From efd2821b8abeccb6b51423002e2a62921481a26e Mon Sep 17 00:00:00 2001 From: Sui Jingfeng Date: Thu, 20 Apr 2023 11:05:00 +0800 Subject: drm/fbdev-generic: prohibit potential out-of-bounds access [ Upstream commit c8687694bb1f5c48134f152f8c5c2e53483eb99d ] The fbdev test of IGT may write after EOF, which lead to out-of-bound access for drm drivers with fbdev-generic. For example, run fbdev test on a x86+ast2400 platform, with 1680x1050 resolution, will cause the linux kernel hang with the following call trace: Oops: 0000 [#1] PREEMPT SMP PTI [IGT] fbdev: starting subtest eof Workqueue: events drm_fb_helper_damage_work [drm_kms_helper] [IGT] fbdev: starting subtest nullptr RIP: 0010:memcpy_erms+0xa/0x20 RSP: 0018:ffffa17d40167d98 EFLAGS: 00010246 RAX: ffffa17d4eb7fa80 RBX: ffffa17d40e0aa80 RCX: 00000000000014c0 RDX: 0000000000001a40 RSI: ffffa17d40e0b000 RDI: ffffa17d4eb80000 RBP: ffffa17d40167e20 R08: 0000000000000000 R09: ffff89522ecff8c0 R10: ffffa17d4e4c5000 R11: 0000000000000000 R12: ffffa17d4eb7fa80 R13: 0000000000001a40 R14: 000000000000041a R15: ffffa17d40167e30 FS: 0000000000000000(0000) GS:ffff895257380000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffa17d40e0b000 CR3: 00000001eaeca006 CR4: 00000000001706e0 Call Trace: ? drm_fbdev_generic_helper_fb_dirty+0x207/0x330 [drm_kms_helper] drm_fb_helper_damage_work+0x8f/0x170 [drm_kms_helper] process_one_work+0x21f/0x430 worker_thread+0x4e/0x3c0 ? __pfx_worker_thread+0x10/0x10 kthread+0xf4/0x120 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x2c/0x50 CR2: ffffa17d40e0b000 ---[ end trace 0000000000000000 ]--- The is because damage rectangles computed by drm_fb_helper_memory_range_to_clip() function is not guaranteed to be bound in the screen's active display area. Possible reasons are: 1) Buffers are allocated in the granularity of page size, for mmap system call support. The shadow screen buffer consumed by fbdev emulation may also choosed be page size aligned. 2) The DIV_ROUND_UP() used in drm_fb_helper_memory_range_to_clip() will introduce off-by-one error. For example, on a 16KB page size system, in order to store a 1920x1080 XRGB framebuffer, we need allocate 507 pages. Unfortunately, the size 1920*1080*4 can not be divided exactly by 16KB. 1920 * 1080 * 4 = 8294400 bytes 506 * 16 * 1024 = 8290304 bytes 507 * 16 * 1024 = 8306688 bytes line_length = 1920*4 = 7680 bytes 507 * 16 * 1024 / 7680 = 1081.6 off / line_length = 507 * 16 * 1024 / 7680 = 1081 DIV_ROUND_UP(507 * 16 * 1024, 7680) will yeild 1082 memcpy_toio() typically issue the copy line by line, when copy the last line, out-of-bound access will be happen. Because: 1082 * line_length = 1082 * 7680 = 8309760, and 8309760 > 8306688 Note that userspace may still write to the invisiable area if a larger buffer than width x stride is exposed. But it is not a big issue as long as there still have memory resolve the access if not drafting so far. - Also limit the y1 (Daniel) - keep fix patch it to minimal (Daniel) - screen_size is page size aligned because of it need mmap (Thomas) - Adding fixes tag (Thomas) Signed-off-by: Sui Jingfeng Fixes: aa15c677cc34 ("drm/fb-helper: Fix vertical damage clipping") Reviewed-by: Thomas Zimmermann Tested-by: Geert Uytterhoeven Link: https://lore.kernel.org/dri-devel/ad44df29-3241-0d9e-e708-b0338bf3c623@189.cn/ Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20230420030500.1578756-1-suijingfeng@loongson.cn Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_fb_helper.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 06cd2f8c2773..76e46713b2f0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -708,19 +708,27 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y, static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len, struct drm_rect *clip) { + u32 line_length = info->fix.line_length; + u32 fb_height = info->var.yres; off_t end = off + len; u32 x1 = 0; - u32 y1 = off / info->fix.line_length; + u32 y1 = off / line_length; u32 x2 = info->var.xres; - u32 y2 = DIV_ROUND_UP(end, info->fix.line_length); + u32 y2 = DIV_ROUND_UP(end, line_length); + + /* Don't allow any of them beyond the bottom bound of display area */ + if (y1 > fb_height) + y1 = fb_height; + if (y2 > fb_height) + y2 = fb_height; if ((y2 - y1) == 1) { /* * We've only written to a single scanline. Try to reduce * the number of horizontal pixels that need an update. */ - off_t bit_off = (off % info->fix.line_length) * 8; - off_t bit_end = (end % info->fix.line_length) * 8; + off_t bit_off = (off % line_length) * 8; + off_t bit_end = (end % line_length) * 8; x1 = bit_off / info->var.bits_per_pixel; x2 = DIV_ROUND_UP(bit_end, info->var.bits_per_pixel); -- cgit 1.4.1 From 3ff962242f5b69176d859c03948fe20cf234d2c3 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Thu, 9 Mar 2023 22:39:09 -0800 Subject: drm/mipi-dsi: Set the fwnode for mipi_dsi_device [ Upstream commit a26cc2934331b57b5a7164bff344f0a2ec245fc0 ] After commit 3fb16866b51d ("driver core: fw_devlink: Make cycle detection more robust"), fw_devlink prints an error when consumer devices don't have their fwnode set. This used to be ignored silently. Set the fwnode mipi_dsi_device so fw_devlink can find them and properly track their dependencies. This fixes errors like this: [ 0.334054] nwl-dsi 30a00000.mipi-dsi: Failed to create device link with regulator-lcd-1v8 [ 0.346964] nwl-dsi 30a00000.mipi-dsi: Failed to create device link with backlight-dsi Reported-by: Martin Kepplinger Link: https://lore.kernel.org/lkml/2a8e407f4f18c9350f8629a2b5fa18673355b2ae.camel@puri.sm/ Fixes: 068a00233969 ("drm: Add MIPI DSI bus support") Signed-off-by: Saravana Kannan Tested-by: Martin Kepplinger Link: https://lore.kernel.org/r/20230310063910.2474472-1-saravanak@google.com Signed-off-by: Maxime Ripard Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_mipi_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index f25ddfe37498..853208e8dd73 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -221,7 +221,7 @@ mipi_dsi_device_register_full(struct mipi_dsi_host *host, return dsi; } - dsi->dev.of_node = info->node; + device_set_node(&dsi->dev, of_fwnode_handle(info->node)); dsi->channel = info->channel; strlcpy(dsi->name, info->type, sizeof(dsi->name)); -- cgit 1.4.1 From 8547757056c4d50ff541cb7be4683fbb4eebfab2 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 23 Apr 2023 06:48:45 +0100 Subject: ARM: 9296/1: HP Jornada 7XX: fix kernel-doc warnings [ Upstream commit 46dd6078dbc7e363a8bb01209da67015a1538929 ] Fix kernel-doc warnings from the kernel test robot: jornada720_ssp.c:24: warning: Function parameter or member 'jornada_ssp_lock' not described in 'DEFINE_SPINLOCK' jornada720_ssp.c:24: warning: expecting prototype for arch/arm/mac(). Prototype was for DEFINE_SPINLOCK() instead jornada720_ssp.c:34: warning: Function parameter or member 'byte' not described in 'jornada_ssp_reverse' jornada720_ssp.c:57: warning: Function parameter or member 'byte' not described in 'jornada_ssp_byte' jornada720_ssp.c:85: warning: Function parameter or member 'byte' not described in 'jornada_ssp_inout' Link: lore.kernel.org/r/202304210535.tWby3jWF-lkp@intel.com Fixes: 69ebb22277a5 ("[ARM] 4506/1: HP Jornada 7XX: Addition of SSP Platform Driver") Signed-off-by: Randy Dunlap Reported-by: kernel test robot Cc: Arnd Bergmann Cc: Kristoffer Ericson Cc: patches@armlinux.org.uk Signed-off-by: Russell King (Oracle) Signed-off-by: Sasha Levin --- arch/arm/mach-sa1100/jornada720_ssp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-sa1100/jornada720_ssp.c b/arch/arm/mach-sa1100/jornada720_ssp.c index 1dbe98948ce3..9627c4cf3e41 100644 --- a/arch/arm/mach-sa1100/jornada720_ssp.c +++ b/arch/arm/mach-sa1100/jornada720_ssp.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * arch/arm/mac-sa1100/jornada720_ssp.c * * Copyright (C) 2006/2007 Kristoffer Ericson @@ -26,6 +26,7 @@ static unsigned long jornada_ssp_flags; /** * jornada_ssp_reverse - reverses input byte + * @byte: input byte to reverse * * we need to reverse all data we receive from the mcu due to its physical location * returns : 01110111 -> 11101110 @@ -46,6 +47,7 @@ EXPORT_SYMBOL(jornada_ssp_reverse); /** * jornada_ssp_byte - waits for ready ssp bus and sends byte + * @byte: input byte to transmit * * waits for fifo buffer to clear and then transmits, if it doesn't then we will * timeout after rounds. Needs mcu running before its called. @@ -77,6 +79,7 @@ EXPORT_SYMBOL(jornada_ssp_byte); /** * jornada_ssp_inout - decide if input is command or trading byte + * @byte: input byte to send (may be %TXDUMMY) * * returns : (jornada_ssp_byte(byte)) on success * : %-ETIMEDOUT on timeout failure -- cgit 1.4.1 From 3e785c8deb046305c61b9fa02265d0cb900c4a45 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 5 May 2023 17:06:18 +0000 Subject: net: skb_partial_csum_set() fix against transport header magic value [ Upstream commit 424f8416bb39936df6365442d651ee729b283460 ] skb->transport_header uses the special 0xFFFF value to mark if the transport header was set or not. We must prevent callers to accidentaly set skb->transport_header to 0xFFFF. Note that only fuzzers can possibly do this today. syzbot reported: WARNING: CPU: 0 PID: 2340 at include/linux/skbuff.h:2847 skb_transport_offset include/linux/skbuff.h:2956 [inline] WARNING: CPU: 0 PID: 2340 at include/linux/skbuff.h:2847 virtio_net_hdr_to_skb+0xbcc/0x10c0 include/linux/virtio_net.h:103 Modules linked in: CPU: 0 PID: 2340 Comm: syz-executor.0 Not tainted 6.3.0-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/14/2023 RIP: 0010:skb_transport_header include/linux/skbuff.h:2847 [inline] RIP: 0010:skb_transport_offset include/linux/skbuff.h:2956 [inline] RIP: 0010:virtio_net_hdr_to_skb+0xbcc/0x10c0 include/linux/virtio_net.h:103 Code: 41 39 df 0f 82 c3 04 00 00 48 8b 7c 24 10 44 89 e6 e8 08 6e 59 ff 48 85 c0 74 54 e8 ce 36 7e fc e9 37 f8 ff ff e8 c4 36 7e fc <0f> 0b e9 93 f8 ff ff 44 89 f7 44 89 e6 e8 32 38 7e fc 45 39 e6 0f RSP: 0018:ffffc90004497880 EFLAGS: 00010293 RAX: ffffffff84fea55c RBX: 000000000000ffff RCX: ffff888120be2100 RDX: 0000000000000000 RSI: 000000000000ffff RDI: 000000000000ffff RBP: ffffc90004497990 R08: ffffffff84fe9de5 R09: 0000000000000034 R10: ffffea00048ebd80 R11: 0000000000000034 R12: ffff88811dc2d9c8 R13: dffffc0000000000 R14: ffff88811dc2d9ae R15: 1ffff11023b85b35 FS: 00007f9211a59700(0000) GS:ffff8881f6c00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000200002c0 CR3: 00000001215a5000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: packet_snd net/packet/af_packet.c:3076 [inline] packet_sendmsg+0x4590/0x61a0 net/packet/af_packet.c:3115 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] __sys_sendto+0x472/0x630 net/socket.c:2144 __do_sys_sendto net/socket.c:2156 [inline] __se_sys_sendto net/socket.c:2152 [inline] __x64_sys_sendto+0xe5/0x100 net/socket.c:2152 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x2f/0x50 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7f9210c8c169 Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 f1 19 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f9211a59168 EFLAGS: 00000246 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 00007f9210dabf80 RCX: 00007f9210c8c169 RDX: 000000000000ffed RSI: 00000000200000c0 RDI: 0000000000000003 RBP: 00007f9210ce7ca1 R08: 0000000020000540 R09: 0000000000000014 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007ffe135d65cf R14: 00007f9211a59300 R15: 0000000000022000 Fixes: 66e4c8d95008 ("net: warn if transport header was not set") Signed-off-by: Eric Dumazet Reported-by: syzbot Cc: Willem de Bruijn Reviewed-by: Willem de Bruijn Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/core/skbuff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ccfd9053754a..47660002cada 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5053,7 +5053,7 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off) u32 csum_end = (u32)start + (u32)off + sizeof(__sum16); u32 csum_start = skb_headroom(skb) + (u32)start; - if (unlikely(csum_start > U16_MAX || csum_end > skb_headlen(skb))) { + if (unlikely(csum_start >= U16_MAX || csum_end > skb_headlen(skb))) { net_warn_ratelimited("bad partial csum: csum=%u/%u headroom=%u headlen=%u\n", start, off, skb_headroom(skb), skb_headlen(skb)); return false; @@ -5061,7 +5061,7 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off) skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = csum_start; skb->csum_offset = off; - skb_set_transport_header(skb, start); + skb->transport_header = csum_start; return true; } EXPORT_SYMBOL_GPL(skb_partial_csum_set); -- cgit 1.4.1 From 27c6b573d1501a457f1e797aa659e820b1c67797 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 5 May 2023 20:39:33 +0200 Subject: net: mdio: mvusb: Fix an error handling path in mvusb_mdio_probe() [ Upstream commit 27c1eaa07283b0c94becf8241f95368267cf558b ] Should of_mdiobus_register() fail, a previous usb_get_dev() call should be undone as in the .disconnect function. Fixes: 04e37d92fbed ("net: phy: add marvell usb to mdio controller") Signed-off-by: Christophe JAILLET Reviewed-by: Simon Horman Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/mdio/mdio-mvusb.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/mdio-mvusb.c b/drivers/net/mdio/mdio-mvusb.c index d5eabddfdf51..11e048136ac2 100644 --- a/drivers/net/mdio/mdio-mvusb.c +++ b/drivers/net/mdio/mdio-mvusb.c @@ -73,6 +73,7 @@ static int mvusb_mdio_probe(struct usb_interface *interface, struct device *dev = &interface->dev; struct mvusb_mdio *mvusb; struct mii_bus *mdio; + int ret; mdio = devm_mdiobus_alloc_size(dev, sizeof(*mvusb)); if (!mdio) @@ -93,7 +94,15 @@ static int mvusb_mdio_probe(struct usb_interface *interface, mdio->write = mvusb_mdio_write; usb_set_intfdata(interface, mvusb); - return of_mdiobus_register(mdio, dev->of_node); + ret = of_mdiobus_register(mdio, dev->of_node); + if (ret) + goto put_dev; + + return 0; + +put_dev: + usb_put_dev(mvusb->udev); + return ret; } static void mvusb_mdio_disconnect(struct usb_interface *interface) -- cgit 1.4.1 From 262841702603e8a0285c95476aa2ee7b27edd80b Mon Sep 17 00:00:00 2001 From: Keoseong Park Date: Tue, 25 Apr 2023 12:17:21 +0900 Subject: scsi: ufs: core: Fix I/O hang that occurs when BKOPS fails in W-LUN suspend [ Upstream commit 1a7edd041f2d252f251523ba3f2eaead076a8f8d ] Even when urgent BKOPS fails, the consumer will get stuck in runtime suspend status. Like commit 1a5665fc8d7a ("scsi: ufs: core: WLUN suspend SSU/enter hibern8 fail recovery"), trigger the error handler and return -EBUSY to break the suspend. Fixes: b294ff3e3449 ("scsi: ufs: core: Enable power management for wlun") Signed-off-by: Keoseong Park Link: https://lore.kernel.org/r/20230425031721epcms2p5d4de65616478c967d466626e20c42a3a@epcms2p5 Reviewed-by: Avri Altman Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/ufs/core/ufshcd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index d89ce7fb6b36..977bd4b9dd0b 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -9058,8 +9058,16 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) * that performance might be impacted. */ ret = ufshcd_urgent_bkops(hba); - if (ret) + if (ret) { + /* + * If return err in suspend flow, IO will hang. + * Trigger error handler and break suspend for + * error recovery. + */ + ufshcd_force_error_recovery(hba); + ret = -EBUSY; goto enable_scaling; + } } else { /* make sure that auto bkops is disabled */ ufshcd_disable_auto_bkops(hba); -- cgit 1.4.1 From a84b08314f441b1433cef6bd216c343e875e0ddc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 6 May 2023 18:40:57 +0200 Subject: tick/broadcast: Make broadcast device replacement work correctly [ Upstream commit f9d36cf445ffff0b913ba187a3eff78028f9b1fb ] When a tick broadcast clockevent device is initialized for one shot mode then tick_broadcast_setup_oneshot() OR's the periodic broadcast mode cpumask into the oneshot broadcast cpumask. This is required when switching from periodic broadcast mode to oneshot broadcast mode to ensure that CPUs which are waiting for periodic broadcast are woken up on the next tick. But it is subtly broken, when an active broadcast device is replaced and the system is already in oneshot (NOHZ/HIGHRES) mode. Victor observed this and debugged the issue. Then the OR of the periodic broadcast CPU mask is wrong as the periodic cpumask bits are sticky after tick_broadcast_enable() set it for a CPU unless explicitly cleared via tick_broadcast_disable(). That means that this sets all other CPUs which have tick broadcasting enabled at that point unconditionally in the oneshot broadcast mask. If the affected CPUs were already idle and had their bits set in the oneshot broadcast mask then this does no harm. But for non idle CPUs which were not set this corrupts their state. On their next invocation of tick_broadcast_enable() they observe the bit set, which indicates that the broadcast for the CPU is already set up. As a consequence they fail to update the broadcast event even if their earliest expiring timer is before the actually programmed broadcast event. If the programmed broadcast event is far in the future, then this can cause stalls or trigger the hung task detector. Avoid this by telling tick_broadcast_setup_oneshot() explicitly whether this is the initial switch over from periodic to oneshot broadcast which must take the periodic broadcast mask into account. In the case of initialization of a replacement device this prevents that the broadcast oneshot mask is modified. There is a second problem with broadcast device replacement in this function. The broadcast device is only armed when the previous state of the device was periodic. That is correct for the switch from periodic broadcast mode to oneshot broadcast mode as the underlying broadcast device could operate in oneshot state already due to lack of periodic state in hardware. In that case it is already armed to expire at the next tick. For the replacement case this is wrong as the device is in shutdown state. That means that any already pending broadcast event will not be armed. This went unnoticed because any CPU which goes idle will observe that the broadcast device has an expiry time of KTIME_MAX and therefore any CPUs next timer event will be earlier and cause a reprogramming of the broadcast device. But that does not guarantee that the events of the CPUs which were already in idle are delivered on time. Fix this by arming the newly installed device for an immediate event which will reevaluate the per CPU expiry times and reprogram the broadcast device accordingly. This is simpler than caching the last expiry time in yet another place or saving it before the device exchange and handing it down to the setup function. Replacement of broadcast devices is not a frequent operation and usually happens once somewhere late in the boot process. Fixes: 9c336c9935cf ("tick/broadcast: Allow late registered device to enter oneshot mode") Reported-by: Victor Hassan Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Link: https://lore.kernel.org/r/87pm7d2z1i.ffs@tglx Signed-off-by: Sasha Levin --- kernel/time/tick-broadcast.c | 120 +++++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 32 deletions(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index f7fe6fe36173..0916cc9adb82 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -35,14 +35,15 @@ static __cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(tick_broadcast_lock); #ifdef CONFIG_TICK_ONESHOT static DEFINE_PER_CPU(struct clock_event_device *, tick_oneshot_wakeup_device); -static void tick_broadcast_setup_oneshot(struct clock_event_device *bc); +static void tick_broadcast_setup_oneshot(struct clock_event_device *bc, bool from_periodic); static void tick_broadcast_clear_oneshot(int cpu); static void tick_resume_broadcast_oneshot(struct clock_event_device *bc); # ifdef CONFIG_HOTPLUG_CPU static void tick_broadcast_oneshot_offline(unsigned int cpu); # endif #else -static inline void tick_broadcast_setup_oneshot(struct clock_event_device *bc) { BUG(); } +static inline void +tick_broadcast_setup_oneshot(struct clock_event_device *bc, bool from_periodic) { BUG(); } static inline void tick_broadcast_clear_oneshot(int cpu) { } static inline void tick_resume_broadcast_oneshot(struct clock_event_device *bc) { } # ifdef CONFIG_HOTPLUG_CPU @@ -264,7 +265,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) tick_broadcast_start_periodic(bc); else - tick_broadcast_setup_oneshot(bc); + tick_broadcast_setup_oneshot(bc, false); ret = 1; } else { /* @@ -500,7 +501,7 @@ void tick_broadcast_control(enum tick_broadcast_mode mode) if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) tick_broadcast_start_periodic(bc); else - tick_broadcast_setup_oneshot(bc); + tick_broadcast_setup_oneshot(bc, false); } } out: @@ -1016,48 +1017,101 @@ static inline ktime_t tick_get_next_period(void) /** * tick_broadcast_setup_oneshot - setup the broadcast device */ -static void tick_broadcast_setup_oneshot(struct clock_event_device *bc) +static void tick_broadcast_setup_oneshot(struct clock_event_device *bc, + bool from_periodic) { int cpu = smp_processor_id(); + ktime_t nexttick = 0; if (!bc) return; - /* Set it up only once ! */ - if (bc->event_handler != tick_handle_oneshot_broadcast) { - int was_periodic = clockevent_state_periodic(bc); - - bc->event_handler = tick_handle_oneshot_broadcast; - + /* + * When the broadcast device was switched to oneshot by the first + * CPU handling the NOHZ change, the other CPUs will reach this + * code via hrtimer_run_queues() -> tick_check_oneshot_change() + * too. Set up the broadcast device only once! + */ + if (bc->event_handler == tick_handle_oneshot_broadcast) { /* - * We must be careful here. There might be other CPUs - * waiting for periodic broadcast. We need to set the - * oneshot_mask bits for those and program the - * broadcast device to fire. + * The CPU which switched from periodic to oneshot mode + * set the broadcast oneshot bit for all other CPUs which + * are in the general (periodic) broadcast mask to ensure + * that CPUs which wait for the periodic broadcast are + * woken up. + * + * Clear the bit for the local CPU as the set bit would + * prevent the first tick_broadcast_enter() after this CPU + * switched to oneshot state to program the broadcast + * device. + * + * This code can also be reached via tick_broadcast_control(), + * but this cannot avoid the tick_broadcast_clear_oneshot() + * as that would break the periodic to oneshot transition of + * secondary CPUs. But that's harmless as the below only + * clears already cleared bits. */ + tick_broadcast_clear_oneshot(cpu); + return; + } + + + bc->event_handler = tick_handle_oneshot_broadcast; + bc->next_event = KTIME_MAX; + + /* + * When the tick mode is switched from periodic to oneshot it must + * be ensured that CPUs which are waiting for periodic broadcast + * get their wake-up at the next tick. This is achieved by ORing + * tick_broadcast_mask into tick_broadcast_oneshot_mask. + * + * For other callers, e.g. broadcast device replacement, + * tick_broadcast_oneshot_mask must not be touched as this would + * set bits for CPUs which are already NOHZ, but not idle. Their + * next tick_broadcast_enter() would observe the bit set and fail + * to update the expiry time and the broadcast event device. + */ + if (from_periodic) { cpumask_copy(tmpmask, tick_broadcast_mask); + /* Remove the local CPU as it is obviously not idle */ cpumask_clear_cpu(cpu, tmpmask); - cpumask_or(tick_broadcast_oneshot_mask, - tick_broadcast_oneshot_mask, tmpmask); + cpumask_or(tick_broadcast_oneshot_mask, tick_broadcast_oneshot_mask, tmpmask); - if (was_periodic && !cpumask_empty(tmpmask)) { - ktime_t nextevt = tick_get_next_period(); + /* + * Ensure that the oneshot broadcast handler will wake the + * CPUs which are still waiting for periodic broadcast. + */ + nexttick = tick_get_next_period(); + tick_broadcast_init_next_event(tmpmask, nexttick); - clockevents_switch_state(bc, CLOCK_EVT_STATE_ONESHOT); - tick_broadcast_init_next_event(tmpmask, nextevt); - tick_broadcast_set_event(bc, cpu, nextevt); - } else - bc->next_event = KTIME_MAX; - } else { /* - * The first cpu which switches to oneshot mode sets - * the bit for all other cpus which are in the general - * (periodic) broadcast mask. So the bit is set and - * would prevent the first broadcast enter after this - * to program the bc device. + * If the underlying broadcast clock event device is + * already in oneshot state, then there is nothing to do. + * The device was already armed for the next tick + * in tick_handle_broadcast_periodic() */ - tick_broadcast_clear_oneshot(cpu); + if (clockevent_state_oneshot(bc)) + return; } + + /* + * When switching from periodic to oneshot mode arm the broadcast + * device for the next tick. + * + * If the broadcast device has been replaced in oneshot mode and + * the oneshot broadcast mask is not empty, then arm it to expire + * immediately in order to reevaluate the next expiring timer. + * @nexttick is 0 and therefore in the past which will cause the + * clockevent code to force an event. + * + * For both cases the programming can be avoided when the oneshot + * broadcast mask is empty. + * + * tick_broadcast_set_event() implicitly switches the broadcast + * device to oneshot state. + */ + if (!cpumask_empty(tick_broadcast_oneshot_mask)) + tick_broadcast_set_event(bc, cpu, nexttick); } /* @@ -1066,14 +1120,16 @@ static void tick_broadcast_setup_oneshot(struct clock_event_device *bc) void tick_broadcast_switch_to_oneshot(void) { struct clock_event_device *bc; + enum tick_device_mode oldmode; unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); + oldmode = tick_broadcast_device.mode; tick_broadcast_device.mode = TICKDEV_MODE_ONESHOT; bc = tick_broadcast_device.evtdev; if (bc) - tick_broadcast_setup_oneshot(bc); + tick_broadcast_setup_oneshot(bc, oldmode == TICKDEV_MODE_PERIODIC); raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); } -- cgit 1.4.1 From 9e916db758a0e1fe4480ba042cde67c86ab4cab5 Mon Sep 17 00:00:00 2001 From: Roy Novich Date: Sun, 7 May 2023 16:57:43 +0300 Subject: linux/dim: Do nothing if no time delta between samples [ Upstream commit 162bd18eb55adf464a0fa2b4144b8d61c75ff7c2 ] Add return value for dim_calc_stats. This is an indication for the caller if curr_stats was assigned by the function. Avoid using curr_stats uninitialized over {rdma/net}_dim, when no time delta between samples. Coverity reported this potential use of an uninitialized variable. Fixes: 4c4dbb4a7363 ("net/mlx5e: Move dynamic interrupt coalescing code to include/linux") Fixes: cb3c7fd4f839 ("net/mlx5e: Support adaptive RX coalescing") Signed-off-by: Roy Novich Reviewed-by: Aya Levin Reviewed-by: Saeed Mahameed Signed-off-by: Tariq Toukan Reviewed-by: Leon Romanovsky Reviewed-by: Michal Kubiak Link: https://lore.kernel.org/r/20230507135743.138993-1-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/linux/dim.h | 3 ++- lib/dim/dim.c | 5 +++-- lib/dim/net_dim.c | 3 ++- lib/dim/rdma_dim.c | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/linux/dim.h b/include/linux/dim.h index 6c5733981563..f343bc9aa2ec 100644 --- a/include/linux/dim.h +++ b/include/linux/dim.h @@ -236,8 +236,9 @@ void dim_park_tired(struct dim *dim); * * Calculate the delta between two samples (in data rates). * Takes into consideration counter wrap-around. + * Returned boolean indicates whether curr_stats are reliable. */ -void dim_calc_stats(struct dim_sample *start, struct dim_sample *end, +bool dim_calc_stats(struct dim_sample *start, struct dim_sample *end, struct dim_stats *curr_stats); /** diff --git a/lib/dim/dim.c b/lib/dim/dim.c index 38045d6d0538..e89aaf07bde5 100644 --- a/lib/dim/dim.c +++ b/lib/dim/dim.c @@ -54,7 +54,7 @@ void dim_park_tired(struct dim *dim) } EXPORT_SYMBOL(dim_park_tired); -void dim_calc_stats(struct dim_sample *start, struct dim_sample *end, +bool dim_calc_stats(struct dim_sample *start, struct dim_sample *end, struct dim_stats *curr_stats) { /* u32 holds up to 71 minutes, should be enough */ @@ -66,7 +66,7 @@ void dim_calc_stats(struct dim_sample *start, struct dim_sample *end, start->comp_ctr); if (!delta_us) - return; + return false; curr_stats->ppms = DIV_ROUND_UP(npkts * USEC_PER_MSEC, delta_us); curr_stats->bpms = DIV_ROUND_UP(nbytes * USEC_PER_MSEC, delta_us); @@ -79,5 +79,6 @@ void dim_calc_stats(struct dim_sample *start, struct dim_sample *end, else curr_stats->cpe_ratio = 0; + return true; } EXPORT_SYMBOL(dim_calc_stats); diff --git a/lib/dim/net_dim.c b/lib/dim/net_dim.c index 53f6b9c6e936..4e32f7aaac86 100644 --- a/lib/dim/net_dim.c +++ b/lib/dim/net_dim.c @@ -227,7 +227,8 @@ void net_dim(struct dim *dim, struct dim_sample end_sample) dim->start_sample.event_ctr); if (nevents < DIM_NEVENTS) break; - dim_calc_stats(&dim->start_sample, &end_sample, &curr_stats); + if (!dim_calc_stats(&dim->start_sample, &end_sample, &curr_stats)) + break; if (net_dim_decision(&curr_stats, dim)) { dim->state = DIM_APPLY_NEW_PROFILE; schedule_work(&dim->work); diff --git a/lib/dim/rdma_dim.c b/lib/dim/rdma_dim.c index 15462d54758d..88f779486707 100644 --- a/lib/dim/rdma_dim.c +++ b/lib/dim/rdma_dim.c @@ -88,7 +88,8 @@ void rdma_dim(struct dim *dim, u64 completions) nevents = curr_sample->event_ctr - dim->start_sample.event_ctr; if (nevents < DIM_NEVENTS) break; - dim_calc_stats(&dim->start_sample, curr_sample, &curr_stats); + if (!dim_calc_stats(&dim->start_sample, curr_sample, &curr_stats)) + break; if (rdma_dim_decision(&curr_stats, dim)) { dim->state = DIM_APPLY_NEW_PROFILE; schedule_work(&dim->work); -- cgit 1.4.1 From edc1f6d89be396aceb4f1ba7f1fcd395031f98c5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 7 May 2023 01:58:45 +0200 Subject: net: stmmac: Initialize MAC_ONEUS_TIC_COUNTER register [ Upstream commit 8efbdbfa99381a017dd2c0f6375a7d80a8118b74 ] Initialize MAC_ONEUS_TIC_COUNTER register with correct value derived from CSR clock, otherwise EEE is unstable on at least NXP i.MX8M Plus and Micrel KSZ9131RNX PHY, to the point where not even ARP request can be sent out. i.MX 8M Plus Applications Processor Reference Manual, Rev. 1, 06/2021 11.7.6.1.34 One-microsecond Reference Timer (MAC_ONEUS_TIC_COUNTER) defines this register as: " This register controls the generation of the Reference time (1 microsecond tic) for all the LPI timers. This timer has to be programmed by the software initially. ... The application must program this counter so that the number of clock cycles of CSR clock is 1us. (Subtract 1 from the value before programming). For example if the CSR clock is 100MHz then this field needs to be programmed to value 100 - 1 = 99 (which is 0x63). This is required to generate the 1US events that are used to update some of the EEE related counters. " The reset value is 0x63 on i.MX8M Plus, which means expected CSR clock are 100 MHz. However, the i.MX8M Plus "enet_qos_root_clk" are 266 MHz instead, which means the LPI timers reach their count much sooner on this platform. This is visible using a scope by monitoring e.g. exit from LPI mode on TX_CTL line from MAC to PHY. This should take 30us per STMMAC_DEFAULT_TWT_LS setting, during which the TX_CTL line transitions from tristate to low, and 30 us later from low to high. On i.MX8M Plus, this transition takes 11 us, which matches the 30us * 100/266 formula for misconfigured MAC_ONEUS_TIC_COUNTER register. Configure MAC_ONEUS_TIC_COUNTER based on CSR clock, so that the LPI timers have correct 1us reference. This then fixes EEE on i.MX8M Plus with Micrel KSZ9131RNX PHY. Fixes: 477286b53f55 ("stmmac: add GMAC4 core support") Signed-off-by: Marek Vasut Tested-by: Harald Seiler Reviewed-by: Francesco Dolcini Tested-by: Francesco Dolcini # Toradex Verdin iMX8MP Reviewed-by: Jesse Brandeburg Link: https://lore.kernel.org/r/20230506235845.246105-1-marex@denx.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 71dad409f78b..12c0e60809f4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -181,6 +181,7 @@ enum power_event { #define GMAC4_LPI_CTRL_STATUS 0xd0 #define GMAC4_LPI_TIMER_CTRL 0xd4 #define GMAC4_LPI_ENTRY_TIMER 0xd8 +#define GMAC4_MAC_ONEUS_TIC_COUNTER 0xdc /* LPI control and status defines */ #define GMAC4_LPI_CTRL_STATUS_LPITCSE BIT(21) /* LPI Tx Clock Stop Enable */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 188a00065f66..84276eb681d7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -25,6 +25,7 @@ static void dwmac4_core_init(struct mac_device_info *hw, struct stmmac_priv *priv = netdev_priv(dev); void __iomem *ioaddr = hw->pcsr; u32 value = readl(ioaddr + GMAC_CONFIG); + u32 clk_rate; value |= GMAC_CORE_INIT; @@ -47,6 +48,10 @@ static void dwmac4_core_init(struct mac_device_info *hw, writel(value, ioaddr + GMAC_CONFIG); + /* Configure LPI 1us counter to number of CSR clock ticks in 1us - 1 */ + clk_rate = clk_get_rate(priv->plat->stmmac_clk); + writel((clk_rate / 1000000) - 1, ioaddr + GMAC4_MAC_ONEUS_TIC_COUNTER); + /* Enable GMAC interrupts */ value = GMAC_INT_DEFAULT_ENABLE; -- cgit 1.4.1 From 7145f2309d649ad6273b9f66448321b9b4c523c8 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 8 May 2023 10:55:43 -0700 Subject: net: Fix load-tearing on sk->sk_stamp in sock_recv_cmsgs(). [ Upstream commit dfd9248c071a3710c24365897459538551cb7167 ] KCSAN found a data race in sock_recv_cmsgs() where the read access to sk->sk_stamp needs READ_ONCE(). BUG: KCSAN: data-race in packet_recvmsg / packet_recvmsg write (marked) to 0xffff88803c81f258 of 8 bytes by task 19171 on cpu 0: sock_write_timestamp include/net/sock.h:2670 [inline] sock_recv_cmsgs include/net/sock.h:2722 [inline] packet_recvmsg+0xb97/0xd00 net/packet/af_packet.c:3489 sock_recvmsg_nosec net/socket.c:1019 [inline] sock_recvmsg+0x11a/0x130 net/socket.c:1040 sock_read_iter+0x176/0x220 net/socket.c:1118 call_read_iter include/linux/fs.h:1845 [inline] new_sync_read fs/read_write.c:389 [inline] vfs_read+0x5e0/0x630 fs/read_write.c:470 ksys_read+0x163/0x1a0 fs/read_write.c:613 __do_sys_read fs/read_write.c:623 [inline] __se_sys_read fs/read_write.c:621 [inline] __x64_sys_read+0x41/0x50 fs/read_write.c:621 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc read to 0xffff88803c81f258 of 8 bytes by task 19183 on cpu 1: sock_recv_cmsgs include/net/sock.h:2721 [inline] packet_recvmsg+0xb64/0xd00 net/packet/af_packet.c:3489 sock_recvmsg_nosec net/socket.c:1019 [inline] sock_recvmsg+0x11a/0x130 net/socket.c:1040 sock_read_iter+0x176/0x220 net/socket.c:1118 call_read_iter include/linux/fs.h:1845 [inline] new_sync_read fs/read_write.c:389 [inline] vfs_read+0x5e0/0x630 fs/read_write.c:470 ksys_read+0x163/0x1a0 fs/read_write.c:613 __do_sys_read fs/read_write.c:623 [inline] __se_sys_read fs/read_write.c:621 [inline] __x64_sys_read+0x41/0x50 fs/read_write.c:621 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc value changed: 0xffffffffc4653600 -> 0x0000000000000000 Reported by Kernel Concurrency Sanitizer on: CPU: 1 PID: 19183 Comm: syz-executor.5 Not tainted 6.3.0-rc7-02330-gca6270c12e20 #2 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 Fixes: 6c7c98bad488 ("sock: avoid dirtying sk_stamp, if possible") Reported-by: syzbot Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20230508175543.55756-1-kuniyu@amazon.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/sock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/sock.h b/include/net/sock.h index 832a4a51de4d..beb1b747fb09 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2744,7 +2744,7 @@ static inline void sock_recv_cmsgs(struct msghdr *msg, struct sock *sk, __sock_recv_cmsgs(msg, sk, skb); else if (unlikely(sock_flag(sk, SOCK_TIMESTAMP))) sock_write_timestamp(sk, skb->tstamp); - else if (unlikely(sk->sk_stamp == SK_DEFAULT_STAMP)) + else if (unlikely(sock_read_timestamp(sk) == SK_DEFAULT_STAMP)) sock_write_timestamp(sk, 0); } -- cgit 1.4.1 From 6fa2e7bb7ba42080343585e22e8b40963613b00c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 8 May 2023 16:17:49 -0700 Subject: net: phy: bcm7xx: Correct read from expansion register [ Upstream commit 582dbb2cc1a0a7427840f5b1e3c65608e511b061 ] Since the driver works in the "legacy" addressing mode, we need to write to the expansion register (0x17) with bits 11:8 set to 0xf to properly select the expansion register passed as argument. Fixes: f68d08c437f9 ("net: phy: bcm7xxx: Add EPHY entry for 72165") Signed-off-by: Florian Fainelli Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230508231749.1681169-1-f.fainelli@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/bcm-phy-lib.h | 5 +++++ drivers/net/phy/bcm7xxx.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index 9902fb182099..729db441797a 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -40,6 +40,11 @@ static inline int bcm_phy_write_exp_sel(struct phy_device *phydev, return bcm_phy_write_exp(phydev, reg | MII_BCM54XX_EXP_SEL_ER, val); } +static inline int bcm_phy_read_exp_sel(struct phy_device *phydev, u16 reg) +{ + return bcm_phy_read_exp(phydev, reg | MII_BCM54XX_EXP_SEL_ER); +} + int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val); int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum); diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 75593e7d1118..6cebf3aaa621 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -487,7 +487,7 @@ static int bcm7xxx_16nm_ephy_afe_config(struct phy_device *phydev) bcm_phy_write_misc(phydev, 0x0038, 0x0002, 0xede0); /* Read CORE_EXPA9 */ - tmp = bcm_phy_read_exp(phydev, 0x00a9); + tmp = bcm_phy_read_exp_sel(phydev, 0x00a9); /* CORE_EXPA9[6:1] is rcalcode[5:0] */ rcalcode = (tmp & 0x7e) / 2; /* Correct RCAL code + 1 is -1% rprogr, LP: +16 */ -- cgit 1.4.1 From 30e4b13b1bfbdf3bf3b27036d8209ea1b9f0d880 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 4 May 2023 14:20:21 +0200 Subject: netfilter: nf_tables: always release netdev hooks from notifier [ Upstream commit dc1c9fd4a8bbe1e06add9053010b652449bfe411 ] This reverts "netfilter: nf_tables: skip netdev events generated on netns removal". The problem is that when a veth device is released, the veth release callback will also queue the peer netns device for removal. Its possible that the peer netns is also slated for removal. In this case, the device memory is already released before the pre_exit hook of the peer netns runs: BUG: KASAN: slab-use-after-free in nf_hook_entry_head+0x1b8/0x1d0 Read of size 8 at addr ffff88812c0124f0 by task kworker/u8:1/45 Workqueue: netns cleanup_net Call Trace: nf_hook_entry_head+0x1b8/0x1d0 __nf_unregister_net_hook+0x76/0x510 nft_netdev_unregister_hooks+0xa0/0x220 __nft_release_hook+0x184/0x490 nf_tables_pre_exit_net+0x12f/0x1b0 .. Order is: 1. First netns is released, veth_dellink() queues peer netns device for removal 2. peer netns is queued for removal 3. peer netns device is released, unreg event is triggered 4. unreg event is ignored because netns is going down 5. pre_exit hook calls nft_netdev_unregister_hooks but device memory might be free'd already. Fixes: 68a3765c659f ("netfilter: nf_tables: skip netdev events generated on netns removal") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_chain_filter.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c index c3563f0be269..680fe557686e 100644 --- a/net/netfilter/nft_chain_filter.c +++ b/net/netfilter/nft_chain_filter.c @@ -344,6 +344,12 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev, return; } + /* UNREGISTER events are also happening on netns exit. + * + * Although nf_tables core releases all tables/chains, only this event + * handler provides guarantee that hook->ops.dev is still accessible, + * so we cannot skip exiting net namespaces. + */ __nft_release_basechain(ctx); } @@ -362,9 +368,6 @@ static int nf_tables_netdev_event(struct notifier_block *this, event != NETDEV_CHANGENAME) return NOTIFY_DONE; - if (!check_net(ctx.net)) - return NOTIFY_DONE; - nft_net = nft_pernet(ctx.net); mutex_lock(&nft_net->commit_mutex); list_for_each_entry(table, &nft_net->tables, list) { -- cgit 1.4.1 From d9176dc690bcb9bd45b1ac088f372d6eaa8aa6c9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 4 May 2023 14:55:02 +0200 Subject: netfilter: conntrack: fix possible bug_on with enable_hooks=1 [ Upstream commit e72eeab542dbf4f544e389e64fa13b82a1b6d003 ] I received a bug report (no reproducer so far) where we trip over 712 rcu_read_lock(); 713 ct_hook = rcu_dereference(nf_ct_hook); 714 BUG_ON(ct_hook == NULL); // here In nf_conntrack_destroy(). First turn this BUG_ON into a WARN. I think it was triggered via enable_hooks=1 flag. When this flag is turned on, the conntrack hooks are registered before nf_ct_hook pointer gets assigned. This opens a short window where packets enter the conntrack machinery, can have skb->_nfct set up and a subsequent kfree_skb might occur before nf_ct_hook is set. Call nf_conntrack_init_end() to set nf_ct_hook before we register the pernet ops. Fixes: ba3fbe663635 ("netfilter: nf_conntrack: provide modparam to always register conntrack hooks") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/core.c | 6 ++++-- net/netfilter/nf_conntrack_standalone.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 6e80f0f6149e..55a7f72d547c 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -699,9 +699,11 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct) rcu_read_lock(); ct_hook = rcu_dereference(nf_ct_hook); - BUG_ON(ct_hook == NULL); - ct_hook->destroy(nfct); + if (ct_hook) + ct_hook->destroy(nfct); rcu_read_unlock(); + + WARN_ON(!ct_hook); } EXPORT_SYMBOL(nf_conntrack_destroy); diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 460294bd4b60..52245dbfae31 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -1222,11 +1222,12 @@ static int __init nf_conntrack_standalone_init(void) nf_conntrack_htable_size_user = nf_conntrack_htable_size; #endif + nf_conntrack_init_end(); + ret = register_pernet_subsys(&nf_conntrack_net_ops); if (ret < 0) goto out_pernet; - nf_conntrack_init_end(); return 0; out_pernet: -- cgit 1.4.1 From 6b4585a3c9f0cfe54a765ffd6da4d95e7d964bd9 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Tue, 9 May 2023 11:11:57 +0800 Subject: bonding: fix send_peer_notif overflow [ Upstream commit 9949e2efb54eb3001cb2f6512ff3166dddbfb75d ] Bonding send_peer_notif was defined as u8. Since commit 07a4ddec3ce9 ("bonding: add an option to specify a delay between peer notifications"). the bond->send_peer_notif will be num_peer_notif multiplied by peer_notif_delay, which is u8 * u32. This would cause the send_peer_notif overflow easily. e.g. ip link add bond0 type bond mode 1 miimon 100 num_grat_arp 30 peer_notify_delay 1000 To fix the overflow, let's set the send_peer_notif to u32 and limit peer_notif_delay to 300s. Reported-by: Liang Li Closes: https://bugzilla.redhat.com/show_bug.cgi?id=2090053 Fixes: 07a4ddec3ce9 ("bonding: add an option to specify a delay between peer notifications") Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_netlink.c | 7 ++++++- drivers/net/bonding/bond_options.c | 8 +++++++- include/net/bonding.h | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index c2d080fc4fc4..27cbe148f0db 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -84,6 +84,11 @@ nla_put_failure: return -EMSGSIZE; } +/* Limit the max delay range to 300s */ +static struct netlink_range_validation delay_range = { + .max = 300000, +}; + static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_MODE] = { .type = NLA_U8 }, [IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 }, @@ -114,7 +119,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_AD_ACTOR_SYSTEM] = { .type = NLA_BINARY, .len = ETH_ALEN }, [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NLA_U8 }, - [IFLA_BOND_PEER_NOTIF_DELAY] = { .type = NLA_U32 }, + [IFLA_BOND_PEER_NOTIF_DELAY] = NLA_POLICY_FULL_RANGE(NLA_U32, &delay_range), [IFLA_BOND_MISSED_MAX] = { .type = NLA_U8 }, [IFLA_BOND_NS_IP6_TARGET] = { .type = NLA_NESTED }, }; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 3498db1c1b3c..5bb2c098bf4d 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -169,6 +169,12 @@ static const struct bond_opt_value bond_num_peer_notif_tbl[] = { { NULL, -1, 0} }; +static const struct bond_opt_value bond_peer_notif_delay_tbl[] = { + { "off", 0, 0}, + { "maxval", 300000, BOND_VALFLAG_MAX}, + { NULL, -1, 0} +}; + static const struct bond_opt_value bond_primary_reselect_tbl[] = { { "always", BOND_PRI_RESELECT_ALWAYS, BOND_VALFLAG_DEFAULT}, { "better", BOND_PRI_RESELECT_BETTER, 0}, @@ -488,7 +494,7 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = { .id = BOND_OPT_PEER_NOTIF_DELAY, .name = "peer_notif_delay", .desc = "Delay between each peer notification on failover event, in milliseconds", - .values = bond_intmax_tbl, + .values = bond_peer_notif_delay_tbl, .set = bond_option_peer_notif_delay_set } }; diff --git a/include/net/bonding.h b/include/net/bonding.h index 768348008d0c..123729c0e1ee 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -235,7 +235,7 @@ struct bonding { */ spinlock_t mode_lock; spinlock_t stats_lock; - u8 send_peer_notif; + u32 send_peer_notif; u8 igmp_retrans; #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_entry; -- cgit 1.4.1 From a115dadf8995b1730c36c474401d97355705cb88 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 9 May 2023 16:56:34 +0000 Subject: netlink: annotate accesses to nlk->cb_running [ Upstream commit a939d14919b799e6fff8a9c80296ca229ba2f8a4 ] Both netlink_recvmsg() and netlink_native_seq_show() read nlk->cb_running locklessly. Use READ_ONCE() there. Add corresponding WRITE_ONCE() to netlink_dump() and __netlink_dump_start() syzbot reported: BUG: KCSAN: data-race in __netlink_dump_start / netlink_recvmsg write to 0xffff88813ea4db59 of 1 bytes by task 28219 on cpu 0: __netlink_dump_start+0x3af/0x4d0 net/netlink/af_netlink.c:2399 netlink_dump_start include/linux/netlink.h:308 [inline] rtnetlink_rcv_msg+0x70f/0x8c0 net/core/rtnetlink.c:6130 netlink_rcv_skb+0x126/0x220 net/netlink/af_netlink.c:2577 rtnetlink_rcv+0x1c/0x20 net/core/rtnetlink.c:6192 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x56f/0x640 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x665/0x770 net/netlink/af_netlink.c:1942 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] sock_write_iter+0x1aa/0x230 net/socket.c:1138 call_write_iter include/linux/fs.h:1851 [inline] new_sync_write fs/read_write.c:491 [inline] vfs_write+0x463/0x760 fs/read_write.c:584 ksys_write+0xeb/0x1a0 fs/read_write.c:637 __do_sys_write fs/read_write.c:649 [inline] __se_sys_write fs/read_write.c:646 [inline] __x64_sys_write+0x42/0x50 fs/read_write.c:646 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd read to 0xffff88813ea4db59 of 1 bytes by task 28222 on cpu 1: netlink_recvmsg+0x3b4/0x730 net/netlink/af_netlink.c:2022 sock_recvmsg_nosec+0x4c/0x80 net/socket.c:1017 ____sys_recvmsg+0x2db/0x310 net/socket.c:2718 ___sys_recvmsg net/socket.c:2762 [inline] do_recvmmsg+0x2e5/0x710 net/socket.c:2856 __sys_recvmmsg net/socket.c:2935 [inline] __do_sys_recvmmsg net/socket.c:2958 [inline] __se_sys_recvmmsg net/socket.c:2951 [inline] __x64_sys_recvmmsg+0xe2/0x160 net/socket.c:2951 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd value changed: 0x00 -> 0x01 Fixes: 16b304f3404f ("netlink: Eliminate kmalloc in netlink dump operation.") Reported-by: syzbot Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/netlink/af_netlink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b1dcc536521b..d1b19618890b 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1979,7 +1979,7 @@ static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, skb_free_datagram(sk, skb); - if (nlk->cb_running && + if (READ_ONCE(nlk->cb_running) && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { ret = netlink_dump(sk); if (ret) { @@ -2293,7 +2293,7 @@ static int netlink_dump(struct sock *sk) if (cb->done) cb->done(cb); - nlk->cb_running = false; + WRITE_ONCE(nlk->cb_running, false); module = cb->module; skb = cb->skb; mutex_unlock(nlk->cb_mutex); @@ -2356,7 +2356,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, goto error_put; } - nlk->cb_running = true; + WRITE_ONCE(nlk->cb_running, true); nlk->dump_done_errno = INT_MAX; mutex_unlock(nlk->cb_mutex); @@ -2681,7 +2681,7 @@ static int netlink_native_seq_show(struct seq_file *seq, void *v) nlk->groups ? (u32)nlk->groups[0] : 0, sk_rmem_alloc_get(s), sk_wmem_alloc_get(s), - nlk->cb_running, + READ_ONCE(nlk->cb_running), refcount_read(&s->sk_refcnt), atomic_read(&s->sk_drops), sock_i_ino(s) -- cgit 1.4.1 From bd0f360ee86494037fbffcac28dbb47c438b33df Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 9 May 2023 16:35:53 +0000 Subject: net: annotate sk->sk_err write from do_recvmmsg() [ Upstream commit e05a5f510f26607616fecdd4ac136310c8bea56b ] do_recvmmsg() can write to sk->sk_err from multiple threads. As said before, many other points reading or writing sk_err need annotations. Fixes: 34b88a68f26a ("net: Fix use after free in the recvmmsg exit path") Signed-off-by: Eric Dumazet Reported-by: syzbot Reviewed-by: Kuniyuki Iwashima Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/socket.c b/net/socket.c index 577079a8935f..c2e0a22f16d9 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2890,7 +2890,7 @@ static int do_recvmmsg(int fd, struct mmsghdr __user *mmsg, * error to return on the next call or if the * app asks about it using getsockopt(SO_ERROR). */ - sock->sk->sk_err = -err; + WRITE_ONCE(sock->sk->sk_err, -err); } out_put: fput_light(sock->file, fput_needed); -- cgit 1.4.1 From 65531f56753625527d2a47923896fc3e100ea497 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 9 May 2023 18:29:48 +0000 Subject: net: deal with most data-races in sk_wait_event() [ Upstream commit d0ac89f6f9879fae316c155de77b5173b3e2c9c9 ] __condition is evaluated twice in sk_wait_event() macro. First invocation is lockless, and reads can race with writes, as spotted by syzbot. BUG: KCSAN: data-race in sk_stream_wait_connect / tcp_disconnect write to 0xffff88812d83d6a0 of 4 bytes by task 9065 on cpu 1: tcp_disconnect+0x2cd/0xdb0 inet_shutdown+0x19e/0x1f0 net/ipv4/af_inet.c:911 __sys_shutdown_sock net/socket.c:2343 [inline] __sys_shutdown net/socket.c:2355 [inline] __do_sys_shutdown net/socket.c:2363 [inline] __se_sys_shutdown+0xf8/0x140 net/socket.c:2361 __x64_sys_shutdown+0x31/0x40 net/socket.c:2361 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd read to 0xffff88812d83d6a0 of 4 bytes by task 9040 on cpu 0: sk_stream_wait_connect+0x1de/0x3a0 net/core/stream.c:75 tcp_sendmsg_locked+0x2e4/0x2120 net/ipv4/tcp.c:1266 tcp_sendmsg+0x30/0x50 net/ipv4/tcp.c:1484 inet6_sendmsg+0x63/0x80 net/ipv6/af_inet6.c:651 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] __sys_sendto+0x246/0x300 net/socket.c:2142 __do_sys_sendto net/socket.c:2154 [inline] __se_sys_sendto net/socket.c:2150 [inline] __x64_sys_sendto+0x78/0x90 net/socket.c:2150 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd value changed: 0x00000000 -> 0x00000068 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/core/stream.c | 12 ++++++------ net/ipv4/tcp_bpf.c | 2 +- net/llc/af_llc.c | 8 +++++--- net/smc/smc_close.c | 4 ++-- net/smc/smc_rx.c | 4 ++-- net/smc/smc_tx.c | 4 ++-- net/tipc/socket.c | 4 ++-- net/tls/tls_main.c | 3 ++- 8 files changed, 22 insertions(+), 19 deletions(-) diff --git a/net/core/stream.c b/net/core/stream.c index cbb268c15251..5b05b889d31a 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -73,8 +73,8 @@ int sk_stream_wait_connect(struct sock *sk, long *timeo_p) add_wait_queue(sk_sleep(sk), &wait); sk->sk_write_pending++; done = sk_wait_event(sk, timeo_p, - !sk->sk_err && - !((1 << sk->sk_state) & + !READ_ONCE(sk->sk_err) && + !((1 << READ_ONCE(sk->sk_state)) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)), &wait); remove_wait_queue(sk_sleep(sk), &wait); sk->sk_write_pending--; @@ -87,9 +87,9 @@ EXPORT_SYMBOL(sk_stream_wait_connect); * sk_stream_closing - Return 1 if we still have things to send in our buffers. * @sk: socket to verify */ -static inline int sk_stream_closing(struct sock *sk) +static int sk_stream_closing(const struct sock *sk) { - return (1 << sk->sk_state) & + return (1 << READ_ONCE(sk->sk_state)) & (TCPF_FIN_WAIT1 | TCPF_CLOSING | TCPF_LAST_ACK); } @@ -142,8 +142,8 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p) set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); sk->sk_write_pending++; - sk_wait_event(sk, ¤t_timeo, sk->sk_err || - (sk->sk_shutdown & SEND_SHUTDOWN) || + sk_wait_event(sk, ¤t_timeo, READ_ONCE(sk->sk_err) || + (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN) || (sk_stream_memory_free(sk) && !vm_wait), &wait); sk->sk_write_pending--; diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index ebf917511937..2e9547467edb 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -168,7 +168,7 @@ static int tcp_msg_wait_data(struct sock *sk, struct sk_psock *psock, sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); ret = sk_wait_event(sk, &timeo, !list_empty(&psock->ingress_msg) || - !skb_queue_empty(&sk->sk_receive_queue), &wait); + !skb_queue_empty_lockless(&sk->sk_receive_queue), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); remove_wait_queue(sk_sleep(sk), &wait); return ret; diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index da7fe94bea2e..9ffbc667be6c 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -583,7 +583,8 @@ static int llc_ui_wait_for_disc(struct sock *sk, long timeout) add_wait_queue(sk_sleep(sk), &wait); while (1) { - if (sk_wait_event(sk, &timeout, sk->sk_state == TCP_CLOSE, &wait)) + if (sk_wait_event(sk, &timeout, + READ_ONCE(sk->sk_state) == TCP_CLOSE, &wait)) break; rc = -ERESTARTSYS; if (signal_pending(current)) @@ -603,7 +604,8 @@ static bool llc_ui_wait_for_conn(struct sock *sk, long timeout) add_wait_queue(sk_sleep(sk), &wait); while (1) { - if (sk_wait_event(sk, &timeout, sk->sk_state != TCP_SYN_SENT, &wait)) + if (sk_wait_event(sk, &timeout, + READ_ONCE(sk->sk_state) != TCP_SYN_SENT, &wait)) break; if (signal_pending(current) || !timeout) break; @@ -622,7 +624,7 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout) while (1) { rc = 0; if (sk_wait_event(sk, &timeout, - (sk->sk_shutdown & RCV_SHUTDOWN) || + (READ_ONCE(sk->sk_shutdown) & RCV_SHUTDOWN) || (!llc_data_accept_state(llc->state) && !llc->remote_busy_flag && !llc->p_flag), &wait)) diff --git a/net/smc/smc_close.c b/net/smc/smc_close.c index 31db7438857c..dbdf03e8aa5b 100644 --- a/net/smc/smc_close.c +++ b/net/smc/smc_close.c @@ -67,8 +67,8 @@ static void smc_close_stream_wait(struct smc_sock *smc, long timeout) rc = sk_wait_event(sk, &timeout, !smc_tx_prepared_sends(&smc->conn) || - sk->sk_err == ECONNABORTED || - sk->sk_err == ECONNRESET || + READ_ONCE(sk->sk_err) == ECONNABORTED || + READ_ONCE(sk->sk_err) == ECONNRESET || smc->conn.killed, &wait); if (rc) diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index 17c5aee7ee4f..ffcc9996a3da 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -263,9 +263,9 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo, sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); add_wait_queue(sk_sleep(sk), &wait); rc = sk_wait_event(sk, timeo, - sk->sk_err || + READ_ONCE(sk->sk_err) || cflags->peer_conn_abort || - sk->sk_shutdown & RCV_SHUTDOWN || + READ_ONCE(sk->sk_shutdown) & RCV_SHUTDOWN || conn->killed || fcrit(conn), &wait); diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index f4b6a71ac488..45128443f1f1 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -113,8 +113,8 @@ static int smc_tx_wait(struct smc_sock *smc, int flags) break; /* at least 1 byte of free & no urgent data */ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); sk_wait_event(sk, &timeo, - sk->sk_err || - (sk->sk_shutdown & SEND_SHUTDOWN) || + READ_ONCE(sk->sk_err) || + (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN) || smc_cdc_rxed_any_close(conn) || (atomic_read(&conn->sndbuf_space) && !conn->urg_tx_pend), diff --git a/net/tipc/socket.c b/net/tipc/socket.c index ff5bb9e4731c..14027a7a7bef 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -313,9 +313,9 @@ static void tsk_rej_rx_queue(struct sock *sk, int error) tipc_sk_respond(sk, skb, error); } -static bool tipc_sk_connected(struct sock *sk) +static bool tipc_sk_connected(const struct sock *sk) { - return sk->sk_state == TIPC_ESTABLISHED; + return READ_ONCE(sk->sk_state) == TIPC_ESTABLISHED; } /* tipc_sk_type_connectionless - check if the socket is datagram socket diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index b32c112984dd..f2e7302a4d96 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -111,7 +111,8 @@ int wait_on_pending_writer(struct sock *sk, long *timeo) break; } - if (sk_wait_event(sk, timeo, !sk->sk_write_pending, &wait)) + if (sk_wait_event(sk, timeo, + !READ_ONCE(sk->sk_write_pending), &wait)) break; } remove_wait_queue(sk_sleep(sk), &wait); -- cgit 1.4.1 From 55caf900e13cd04466def08173a14b41d18c19c3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 9 May 2023 13:18:57 +0000 Subject: net: add vlan_get_protocol_and_depth() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4063384ef762cc5946fc7a3f89879e76c6ec51e2 ] Before blamed commit, pskb_may_pull() was used instead of skb_header_pointer() in __vlan_get_protocol() and friends. Few callers depended on skb->head being populated with MAC header, syzbot caught one of them (skb_mac_gso_segment()) Add vlan_get_protocol_and_depth() to make the intent clearer and use it where sensible. This is a more generic fix than commit e9d3f80935b6 ("net/af_packet: make sure to pull mac header") which was dealing with a similar issue. kernel BUG at include/linux/skbuff.h:2655 ! invalid opcode: 0000 [#1] SMP KASAN CPU: 0 PID: 1441 Comm: syz-executor199 Not tainted 6.1.24-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/14/2023 RIP: 0010:__skb_pull include/linux/skbuff.h:2655 [inline] RIP: 0010:skb_mac_gso_segment+0x68f/0x6a0 net/core/gro.c:136 Code: fd 48 8b 5c 24 10 44 89 6b 70 48 c7 c7 c0 ae 0d 86 44 89 e6 e8 a1 91 d0 00 48 c7 c7 00 af 0d 86 48 89 de 31 d2 e8 d1 4a e9 ff <0f> 0b 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 55 48 89 e5 41 RSP: 0018:ffffc90001bd7520 EFLAGS: 00010286 RAX: ffffffff8469736a RBX: ffff88810f31dac0 RCX: ffff888115a18b00 RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 RBP: ffffc90001bd75e8 R08: ffffffff84697183 R09: fffff5200037adf9 R10: 0000000000000000 R11: dffffc0000000001 R12: 0000000000000012 R13: 000000000000fee5 R14: 0000000000005865 R15: 000000000000fed7 FS: 000055555633f300(0000) GS:ffff8881f6a00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020000000 CR3: 0000000116fea000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: [] __skb_gso_segment+0x32d/0x4c0 net/core/dev.c:3419 [] skb_gso_segment include/linux/netdevice.h:4819 [inline] [] validate_xmit_skb+0x3aa/0xee0 net/core/dev.c:3725 [] __dev_queue_xmit+0x1332/0x3300 net/core/dev.c:4313 [] dev_queue_xmit+0x17/0x20 include/linux/netdevice.h:3029 [] packet_snd net/packet/af_packet.c:3111 [inline] [] packet_sendmsg+0x49d2/0x6470 net/packet/af_packet.c:3142 [] sock_sendmsg_nosec net/socket.c:716 [inline] [] sock_sendmsg net/socket.c:736 [inline] [] __sys_sendto+0x472/0x5f0 net/socket.c:2139 [] __do_sys_sendto net/socket.c:2151 [inline] [] __se_sys_sendto net/socket.c:2147 [inline] [] __x64_sys_sendto+0xe5/0x100 net/socket.c:2147 [] do_syscall_x64 arch/x86/entry/common.c:50 [inline] [] do_syscall_64+0x2f/0x50 arch/x86/entry/common.c:80 [] entry_SYSCALL_64_after_hwframe+0x63/0xcd Fixes: 469aceddfa3e ("vlan: consolidate VLAN parsing code and limit max parsing depth") Reported-by: syzbot Signed-off-by: Eric Dumazet Cc: Toke Høiland-Jørgensen Cc: Willem de Bruijn Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/tap.c | 4 ++-- include/linux/if_vlan.h | 17 +++++++++++++++++ net/bridge/br_forward.c | 2 +- net/core/dev.c | 2 +- net/packet/af_packet.c | 6 ++---- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 760d8d1b6cba..3c468ef8f245 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -739,7 +739,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, /* Move network header to the right position for VLAN tagged packets */ if (eth_type_vlan(skb->protocol) && - __vlan_get_protocol(skb, skb->protocol, &depth) != 0) + vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); /* copy skb_ubuf_info for callback when skb has no error */ @@ -1180,7 +1180,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) /* Move network header to the right position for VLAN tagged packets */ if (eth_type_vlan(skb->protocol) && - __vlan_get_protocol(skb, skb->protocol, &depth) != 0) + vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); rcu_read_lock(); diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index e00c4ee81ff7..68b1c4133298 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -631,6 +631,23 @@ static inline __be16 vlan_get_protocol(const struct sk_buff *skb) return __vlan_get_protocol(skb, skb->protocol, NULL); } +/* This version of __vlan_get_protocol() also pulls mac header in skb->head */ +static inline __be16 vlan_get_protocol_and_depth(struct sk_buff *skb, + __be16 type, int *depth) +{ + int maclen; + + type = __vlan_get_protocol(skb, type, &maclen); + + if (type) { + if (!pskb_may_pull(skb, maclen)) + type = 0; + else if (depth) + *depth = maclen; + } + return type; +} + /* A getter for the SKB protocol field which will handle VLAN tags consistently * whether VLAN acceleration is enabled or not. */ diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 02bb620d3b8d..bd54f17e3c3d 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -42,7 +42,7 @@ int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb eth_type_vlan(skb->protocol)) { int depth; - if (!__vlan_get_protocol(skb, skb->protocol, &depth)) + if (!vlan_get_protocol_and_depth(skb, skb->protocol, &depth)) goto drop; skb_set_network_header(skb, depth); diff --git a/net/core/dev.c b/net/core/dev.c index a25b8741b159..1fb7eef38ebe 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3350,7 +3350,7 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth) type = eth->h_proto; } - return __vlan_get_protocol(skb, type, depth); + return vlan_get_protocol_and_depth(skb, type, depth); } /* openvswitch calls this on rx path, so we need a different check. diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 2af2ab924d64..67771b0f5771 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1936,10 +1936,8 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) /* Move network header to the right position for VLAN tagged packets */ if (likely(skb->dev->type == ARPHRD_ETHER) && eth_type_vlan(skb->protocol) && - __vlan_get_protocol(skb, skb->protocol, &depth) != 0) { - if (pskb_may_pull(skb, depth)) - skb_set_network_header(skb, depth); - } + vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) + skb_set_network_header(skb, depth); skb_probe_transport_header(skb); } -- cgit 1.4.1 From b4c0af8974be010fffe2c8a14a5e85a79de440d3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 9 May 2023 20:36:56 +0000 Subject: tcp: add annotations around sk->sk_shutdown accesses [ Upstream commit e14cadfd80d76f01bfaa1a8d745b1db19b57d6be ] Now sk->sk_shutdown is no longer a bitfield, we can add standard READ_ONCE()/WRITE_ONCE() annotations to silence KCSAN reports like the following: BUG: KCSAN: data-race in tcp_disconnect / tcp_poll write to 0xffff88814588582c of 1 bytes by task 3404 on cpu 1: tcp_disconnect+0x4d6/0xdb0 net/ipv4/tcp.c:3121 __inet_stream_connect+0x5dd/0x6e0 net/ipv4/af_inet.c:715 inet_stream_connect+0x48/0x70 net/ipv4/af_inet.c:727 __sys_connect_file net/socket.c:2001 [inline] __sys_connect+0x19b/0x1b0 net/socket.c:2018 __do_sys_connect net/socket.c:2028 [inline] __se_sys_connect net/socket.c:2025 [inline] __x64_sys_connect+0x41/0x50 net/socket.c:2025 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd read to 0xffff88814588582c of 1 bytes by task 3374 on cpu 0: tcp_poll+0x2e6/0x7d0 net/ipv4/tcp.c:562 sock_poll+0x253/0x270 net/socket.c:1383 vfs_poll include/linux/poll.h:88 [inline] io_poll_check_events io_uring/poll.c:281 [inline] io_poll_task_func+0x15a/0x820 io_uring/poll.c:333 handle_tw_list io_uring/io_uring.c:1184 [inline] tctx_task_work+0x1fe/0x4d0 io_uring/io_uring.c:1246 task_work_run+0x123/0x160 kernel/task_work.c:179 get_signal+0xe64/0xff0 kernel/signal.c:2635 arch_do_signal_or_restart+0x89/0x2a0 arch/x86/kernel/signal.c:306 exit_to_user_mode_loop+0x6f/0xe0 kernel/entry/common.c:168 exit_to_user_mode_prepare+0x6c/0xb0 kernel/entry/common.c:204 __syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline] syscall_exit_to_user_mode+0x26/0x140 kernel/entry/common.c:297 do_syscall_64+0x4d/0xc0 arch/x86/entry/common.c:86 entry_SYSCALL_64_after_hwframe+0x63/0xcd value changed: 0x03 -> 0x00 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/ipv4/af_inet.c | 2 +- net/ipv4/tcp.c | 14 ++++++++------ net/ipv4/tcp_input.c | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 5b19b77d5d75..5fd0ff5734e3 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -897,7 +897,7 @@ int inet_shutdown(struct socket *sock, int how) EPOLLHUP, even on eg. unconnected UDP sockets -- RR */ fallthrough; default: - sk->sk_shutdown |= how; + WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | how); if (sk->sk_prot->shutdown) sk->sk_prot->shutdown(sk, how); break; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6667c3538f2a..1fb67f819de4 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -498,6 +498,7 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait) __poll_t mask; struct sock *sk = sock->sk; const struct tcp_sock *tp = tcp_sk(sk); + u8 shutdown; int state; sock_poll_wait(file, sock, wait); @@ -540,9 +541,10 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait) * NOTE. Check for TCP_CLOSE is added. The goal is to prevent * blocking on fresh not-connected or disconnected socket. --ANK */ - if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE) + shutdown = READ_ONCE(sk->sk_shutdown); + if (shutdown == SHUTDOWN_MASK || state == TCP_CLOSE) mask |= EPOLLHUP; - if (sk->sk_shutdown & RCV_SHUTDOWN) + if (shutdown & RCV_SHUTDOWN) mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; /* Connected or passive Fast Open socket? */ @@ -559,7 +561,7 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait) if (tcp_stream_is_readable(sk, target)) mask |= EPOLLIN | EPOLLRDNORM; - if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { + if (!(shutdown & SEND_SHUTDOWN)) { if (__sk_stream_is_writeable(sk, 1)) { mask |= EPOLLOUT | EPOLLWRNORM; } else { /* send SIGIO later */ @@ -2865,7 +2867,7 @@ void __tcp_close(struct sock *sk, long timeout) int data_was_unread = 0; int state; - sk->sk_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); if (sk->sk_state == TCP_LISTEN) { tcp_set_state(sk, TCP_CLOSE); @@ -3117,7 +3119,7 @@ int tcp_disconnect(struct sock *sk, int flags) inet_bhash2_reset_saddr(sk); - sk->sk_shutdown = 0; + WRITE_ONCE(sk->sk_shutdown, 0); sock_reset_flag(sk, SOCK_DONE); tp->srtt_us = 0; tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); @@ -4645,7 +4647,7 @@ void tcp_done(struct sock *sk) if (req) reqsk_fastopen_remove(sk, req, false); - sk->sk_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); if (!sock_flag(sk, SOCK_DEAD)) sk->sk_state_change(sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 0640453fce54..ac44edd6f52e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4361,7 +4361,7 @@ void tcp_fin(struct sock *sk) inet_csk_schedule_ack(sk); - sk->sk_shutdown |= RCV_SHUTDOWN; + WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | RCV_SHUTDOWN); sock_set_flag(sk, SOCK_DONE); switch (sk->sk_state) { @@ -6585,7 +6585,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) break; tcp_set_state(sk, TCP_FIN_WAIT2); - sk->sk_shutdown |= SEND_SHUTDOWN; + WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | SEND_SHUTDOWN); sk_dst_confirm(sk); -- cgit 1.4.1 From d695dccb74e3e06fde5bfeaf4fd4cb86691d09bd Mon Sep 17 00:00:00 2001 From: Ziwei Xiao Date: Tue, 9 May 2023 15:51:23 -0700 Subject: gve: Remove the code of clearing PBA bit [ Upstream commit f4c2e67c1773d2a2632381ee30e9139c1e744c16 ] Clearing the PBA bit from the driver is race prone and it may lead to dropped interrupt events. This could potentially lead to the traffic being completely halted. Fixes: 5e8c5adf95f8 ("gve: DQO: Add core netdev features") Signed-off-by: Ziwei Xiao Signed-off-by: Bailey Forrest Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/google/gve/gve_main.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index d3e3ac242bfc..2e5e0a887270 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -239,19 +239,6 @@ static int gve_napi_poll_dqo(struct napi_struct *napi, int budget) bool reschedule = false; int work_done = 0; - /* Clear PCI MSI-X Pending Bit Array (PBA) - * - * This bit is set if an interrupt event occurs while the vector is - * masked. If this bit is set and we reenable the interrupt, it will - * fire again. Since we're just about to poll the queue state, we don't - * need it to fire again. - * - * Under high softirq load, it's possible that the interrupt condition - * is triggered twice before we got the chance to process it. - */ - gve_write_irq_doorbell_dqo(priv, block, - GVE_ITR_NO_UPDATE_DQO | GVE_ITR_CLEAR_PBA_BIT_DQO); - if (block->tx) reschedule |= gve_tx_poll_dqo(block, /*do_clean=*/true); -- cgit 1.4.1 From 610a433810b277b3b77389733c07d22e8af68de2 Mon Sep 17 00:00:00 2001 From: "t.feng" Date: Wed, 10 May 2023 11:50:44 +0800 Subject: ipvlan:Fix out-of-bounds caused by unclear skb->cb [ Upstream commit 90cbed5247439a966b645b34eb0a2e037836ea8e ] If skb enqueue the qdisc, fq_skb_cb(skb)->time_to_send is changed which is actually skb->cb, and IPCB(skb_in)->opt will be used in __ip_options_echo. It is possible that memcpy is out of bounds and lead to stack overflow. We should clear skb->cb before ip_local_out or ip6_local_out. v2: 1. clean the stack info 2. use IPCB/IP6CB instead of skb->cb crash on stable-5.10(reproduce in kasan kernel). Stack info: [ 2203.651571] BUG: KASAN: stack-out-of-bounds in __ip_options_echo+0x589/0x800 [ 2203.653327] Write of size 4 at addr ffff88811a388f27 by task swapper/3/0 [ 2203.655460] CPU: 3 PID: 0 Comm: swapper/3 Kdump: loaded Not tainted 5.10.0-60.18.0.50.h856.kasan.eulerosv2r11.x86_64 #1 [ 2203.655466] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.10.2-0-g5f4c7b1-20181220_000000-szxrtosci10000 04/01/2014 [ 2203.655475] Call Trace: [ 2203.655481] [ 2203.655501] dump_stack+0x9c/0xd3 [ 2203.655514] print_address_description.constprop.0+0x19/0x170 [ 2203.655530] __kasan_report.cold+0x6c/0x84 [ 2203.655586] kasan_report+0x3a/0x50 [ 2203.655594] check_memory_region+0xfd/0x1f0 [ 2203.655601] memcpy+0x39/0x60 [ 2203.655608] __ip_options_echo+0x589/0x800 [ 2203.655654] __icmp_send+0x59a/0x960 [ 2203.655755] nf_send_unreach+0x129/0x3d0 [nf_reject_ipv4] [ 2203.655763] reject_tg+0x77/0x1bf [ipt_REJECT] [ 2203.655772] ipt_do_table+0x691/0xa40 [ip_tables] [ 2203.655821] nf_hook_slow+0x69/0x100 [ 2203.655828] __ip_local_out+0x21e/0x2b0 [ 2203.655857] ip_local_out+0x28/0x90 [ 2203.655868] ipvlan_process_v4_outbound+0x21e/0x260 [ipvlan] [ 2203.655931] ipvlan_xmit_mode_l3+0x3bd/0x400 [ipvlan] [ 2203.655967] ipvlan_queue_xmit+0xb3/0x190 [ipvlan] [ 2203.655977] ipvlan_start_xmit+0x2e/0xb0 [ipvlan] [ 2203.655984] xmit_one.constprop.0+0xe1/0x280 [ 2203.655992] dev_hard_start_xmit+0x62/0x100 [ 2203.656000] sch_direct_xmit+0x215/0x640 [ 2203.656028] __qdisc_run+0x153/0x1f0 [ 2203.656069] __dev_queue_xmit+0x77f/0x1030 [ 2203.656173] ip_finish_output2+0x59b/0xc20 [ 2203.656244] __ip_finish_output.part.0+0x318/0x3d0 [ 2203.656312] ip_finish_output+0x168/0x190 [ 2203.656320] ip_output+0x12d/0x220 [ 2203.656357] __ip_queue_xmit+0x392/0x880 [ 2203.656380] __tcp_transmit_skb+0x1088/0x11c0 [ 2203.656436] __tcp_retransmit_skb+0x475/0xa30 [ 2203.656505] tcp_retransmit_skb+0x2d/0x190 [ 2203.656512] tcp_retransmit_timer+0x3af/0x9a0 [ 2203.656519] tcp_write_timer_handler+0x3ba/0x510 [ 2203.656529] tcp_write_timer+0x55/0x180 [ 2203.656542] call_timer_fn+0x3f/0x1d0 [ 2203.656555] expire_timers+0x160/0x200 [ 2203.656562] run_timer_softirq+0x1f4/0x480 [ 2203.656606] __do_softirq+0xfd/0x402 [ 2203.656613] asm_call_irq_on_stack+0x12/0x20 [ 2203.656617] [ 2203.656623] do_softirq_own_stack+0x37/0x50 [ 2203.656631] irq_exit_rcu+0x134/0x1a0 [ 2203.656639] sysvec_apic_timer_interrupt+0x36/0x80 [ 2203.656646] asm_sysvec_apic_timer_interrupt+0x12/0x20 [ 2203.656654] RIP: 0010:default_idle+0x13/0x20 [ 2203.656663] Code: 89 f0 5d 41 5c 41 5d 41 5e c3 cc cc cc cc cc cc cc cc cc cc cc cc cc 0f 1f 44 00 00 0f 1f 44 00 00 0f 00 2d 9f 32 57 00 fb f4 cc cc cc cc 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 41 54 be 08 [ 2203.656668] RSP: 0018:ffff88810036fe78 EFLAGS: 00000256 [ 2203.656676] RAX: ffffffffaf2a87f0 RBX: ffff888100360000 RCX: ffffffffaf290191 [ 2203.656681] RDX: 0000000000098b5e RSI: 0000000000000004 RDI: ffff88811a3c4f60 [ 2203.656686] RBP: 0000000000000000 R08: 0000000000000001 R09: ffff88811a3c4f63 [ 2203.656690] R10: ffffed10234789ec R11: 0000000000000001 R12: 0000000000000003 [ 2203.656695] R13: ffff888100360000 R14: 0000000000000000 R15: 0000000000000000 [ 2203.656729] default_idle_call+0x5a/0x150 [ 2203.656735] cpuidle_idle_call+0x1c6/0x220 [ 2203.656780] do_idle+0xab/0x100 [ 2203.656786] cpu_startup_entry+0x19/0x20 [ 2203.656793] secondary_startup_64_no_verify+0xc2/0xcb [ 2203.657409] The buggy address belongs to the page: [ 2203.658648] page:0000000027a9842f refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x11a388 [ 2203.658665] flags: 0x17ffffc0001000(reserved|node=0|zone=2|lastcpupid=0x1fffff) [ 2203.658675] raw: 0017ffffc0001000 ffffea000468e208 ffffea000468e208 0000000000000000 [ 2203.658682] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 [ 2203.658686] page dumped because: kasan: bad access detected To reproduce(ipvlan with IPVLAN_MODE_L3): Env setting: ======================================================= modprobe ipvlan ipvlan_default_mode=1 sysctl net.ipv4.conf.eth0.forwarding=1 iptables -t nat -A POSTROUTING -s 20.0.0.0/255.255.255.0 -o eth0 -j MASQUERADE ip link add gw link eth0 type ipvlan ip -4 addr add 20.0.0.254/24 dev gw ip netns add net1 ip link add ipv1 link eth0 type ipvlan ip link set ipv1 netns net1 ip netns exec net1 ip link set ipv1 up ip netns exec net1 ip -4 addr add 20.0.0.4/24 dev ipv1 ip netns exec net1 route add default gw 20.0.0.254 ip netns exec net1 tc qdisc add dev ipv1 root netem loss 10% ifconfig gw up iptables -t filter -A OUTPUT -p tcp --dport 8888 -j REJECT --reject-with icmp-port-unreachable ======================================================= And then excute the shell(curl any address of eth0 can reach): for((i=1;i<=100000;i++)) do ip netns exec net1 curl x.x.x.x:8888 done ======================================================= Fixes: 2ad7bf363841 ("ipvlan: Initial check-in of the IPVLAN driver.") Signed-off-by: "t.feng" Suggested-by: Florian Westphal Reviewed-by: Paolo Abeni Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ipvlan/ipvlan_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index bb1c298c1e78..2de3bd3b0c27 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -436,6 +436,9 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) goto err; } skb_dst_set(skb, &rt->dst); + + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + err = ip_local_out(net, skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; @@ -474,6 +477,9 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) goto err; } skb_dst_set(skb, dst); + + memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); + err = ip6_local_out(net, skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; -- cgit 1.4.1 From 9e62a49608387e83bef1b06441a0b45547c6320a Mon Sep 17 00:00:00 2001 From: Colin Foster Date: Tue, 9 May 2023 21:48:51 -0700 Subject: net: mscc: ocelot: fix stat counter register values [ Upstream commit cdc2e28e214fe9315cdd7e069c1c8e2428f93427 ] Commit d4c367650704 ("net: mscc: ocelot: keep ocelot_stat_layout by reg address, not offset") organized the stats counters for Ocelot chips, namely the VSC7512 and VSC7514. A few of the counter offsets were incorrect, and were caught by this warning: WARNING: CPU: 0 PID: 24 at drivers/net/ethernet/mscc/ocelot_stats.c:909 ocelot_stats_init+0x1fc/0x2d8 reg 0x5000078 had address 0x220 but reg 0x5000079 has address 0x214, bulking broken! Fix these register offsets. Fixes: d4c367650704 ("net: mscc: ocelot: keep ocelot_stat_layout by reg address, not offset") Signed-off-by: Colin Foster Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/mscc/vsc7514_regs.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mscc/vsc7514_regs.c b/drivers/net/ethernet/mscc/vsc7514_regs.c index 9d2d3e13cacf..66c428419614 100644 --- a/drivers/net/ethernet/mscc/vsc7514_regs.c +++ b/drivers/net/ethernet/mscc/vsc7514_regs.c @@ -252,15 +252,15 @@ const u32 vsc7514_sys_regmap[] = { REG(SYS_COUNT_DROP_YELLOW_PRIO_4, 0x000218), REG(SYS_COUNT_DROP_YELLOW_PRIO_5, 0x00021c), REG(SYS_COUNT_DROP_YELLOW_PRIO_6, 0x000220), - REG(SYS_COUNT_DROP_YELLOW_PRIO_7, 0x000214), - REG(SYS_COUNT_DROP_GREEN_PRIO_0, 0x000218), - REG(SYS_COUNT_DROP_GREEN_PRIO_1, 0x00021c), - REG(SYS_COUNT_DROP_GREEN_PRIO_2, 0x000220), - REG(SYS_COUNT_DROP_GREEN_PRIO_3, 0x000224), - REG(SYS_COUNT_DROP_GREEN_PRIO_4, 0x000228), - REG(SYS_COUNT_DROP_GREEN_PRIO_5, 0x00022c), - REG(SYS_COUNT_DROP_GREEN_PRIO_6, 0x000230), - REG(SYS_COUNT_DROP_GREEN_PRIO_7, 0x000234), + REG(SYS_COUNT_DROP_YELLOW_PRIO_7, 0x000224), + REG(SYS_COUNT_DROP_GREEN_PRIO_0, 0x000228), + REG(SYS_COUNT_DROP_GREEN_PRIO_1, 0x00022c), + REG(SYS_COUNT_DROP_GREEN_PRIO_2, 0x000230), + REG(SYS_COUNT_DROP_GREEN_PRIO_3, 0x000234), + REG(SYS_COUNT_DROP_GREEN_PRIO_4, 0x000238), + REG(SYS_COUNT_DROP_GREEN_PRIO_5, 0x00023c), + REG(SYS_COUNT_DROP_GREEN_PRIO_6, 0x000240), + REG(SYS_COUNT_DROP_GREEN_PRIO_7, 0x000244), REG(SYS_RESET_CFG, 0x000508), REG(SYS_CMID, 0x00050c), REG(SYS_VLAN_ETYPE_CFG, 0x000510), -- cgit 1.4.1 From 8759c1a361fae09ae2a9ca1173323cc07c328264 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 9 May 2023 17:31:31 +0000 Subject: net: datagram: fix data-races in datagram_poll() [ Upstream commit 5bca1d081f44c9443e61841842ce4e9179d327b6 ] datagram_poll() runs locklessly, we should add READ_ONCE() annotations while reading sk->sk_err, sk->sk_shutdown and sk->sk_state. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://lore.kernel.org/r/20230509173131.3263780-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/datagram.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/core/datagram.c b/net/core/datagram.c index e4ff2db40c98..8dabb9a74cb1 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -799,18 +799,21 @@ __poll_t datagram_poll(struct file *file, struct socket *sock, { struct sock *sk = sock->sk; __poll_t mask; + u8 shutdown; sock_poll_wait(file, sock, wait); mask = 0; /* exceptional events? */ - if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue)) + if (READ_ONCE(sk->sk_err) || + !skb_queue_empty_lockless(&sk->sk_error_queue)) mask |= EPOLLERR | (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0); - if (sk->sk_shutdown & RCV_SHUTDOWN) + shutdown = READ_ONCE(sk->sk_shutdown); + if (shutdown & RCV_SHUTDOWN) mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; - if (sk->sk_shutdown == SHUTDOWN_MASK) + if (shutdown == SHUTDOWN_MASK) mask |= EPOLLHUP; /* readable? */ @@ -819,10 +822,12 @@ __poll_t datagram_poll(struct file *file, struct socket *sock, /* Connection-based need to check for termination and startup */ if (connection_based(sk)) { - if (sk->sk_state == TCP_CLOSE) + int state = READ_ONCE(sk->sk_state); + + if (state == TCP_CLOSE) mask |= EPOLLHUP; /* connection hasn't started yet? */ - if (sk->sk_state == TCP_SYN_SENT) + if (state == TCP_SYN_SENT) return mask; } -- cgit 1.4.1 From 75924fb0f3732be01947032b5bc8258284ad7275 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 9 May 2023 17:34:55 -0700 Subject: af_unix: Fix a data race of sk->sk_receive_queue->qlen. [ Upstream commit 679ed006d416ea0cecfe24a99d365d1dea69c683 ] KCSAN found a data race of sk->sk_receive_queue->qlen where recvmsg() updates qlen under the queue lock and sendmsg() checks qlen under unix_state_sock(), not the queue lock, so the reader side needs READ_ONCE(). BUG: KCSAN: data-race in __skb_try_recv_from_queue / unix_wait_for_peer write (marked) to 0xffff888019fe7c68 of 4 bytes by task 49792 on cpu 0: __skb_unlink include/linux/skbuff.h:2347 [inline] __skb_try_recv_from_queue+0x3de/0x470 net/core/datagram.c:197 __skb_try_recv_datagram+0xf7/0x390 net/core/datagram.c:263 __unix_dgram_recvmsg+0x109/0x8a0 net/unix/af_unix.c:2452 unix_dgram_recvmsg+0x94/0xa0 net/unix/af_unix.c:2549 sock_recvmsg_nosec net/socket.c:1019 [inline] ____sys_recvmsg+0x3a3/0x3b0 net/socket.c:2720 ___sys_recvmsg+0xc8/0x150 net/socket.c:2764 do_recvmmsg+0x182/0x560 net/socket.c:2858 __sys_recvmmsg net/socket.c:2937 [inline] __do_sys_recvmmsg net/socket.c:2960 [inline] __se_sys_recvmmsg net/socket.c:2953 [inline] __x64_sys_recvmmsg+0x153/0x170 net/socket.c:2953 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc read to 0xffff888019fe7c68 of 4 bytes by task 49793 on cpu 1: skb_queue_len include/linux/skbuff.h:2127 [inline] unix_recvq_full net/unix/af_unix.c:229 [inline] unix_wait_for_peer+0x154/0x1a0 net/unix/af_unix.c:1445 unix_dgram_sendmsg+0x13bc/0x14b0 net/unix/af_unix.c:2048 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg+0x148/0x160 net/socket.c:747 ____sys_sendmsg+0x20e/0x620 net/socket.c:2503 ___sys_sendmsg+0xc6/0x140 net/socket.c:2557 __sys_sendmmsg+0x11d/0x370 net/socket.c:2643 __do_sys_sendmmsg net/socket.c:2672 [inline] __se_sys_sendmmsg net/socket.c:2669 [inline] __x64_sys_sendmmsg+0x58/0x70 net/socket.c:2669 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc value changed: 0x0000000b -> 0x00000001 Reported by Kernel Concurrency Sanitizer on: CPU: 1 PID: 49793 Comm: syz-executor.0 Not tainted 6.3.0-rc7-02330-gca6270c12e20 #2 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Reviewed-by: Michal Kubiak Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 7d17601ceee7..3b292a7a1fa5 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1441,7 +1441,7 @@ static long unix_wait_for_peer(struct sock *other, long timeo) sched = !sock_flag(other, SOCK_DEAD) && !(other->sk_shutdown & RCV_SHUTDOWN) && - unix_recvq_full(other); + unix_recvq_full_lockless(other); unix_state_unlock(other); -- cgit 1.4.1 From e410895892f99700ce54347d42c8dbe962eea9f4 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 9 May 2023 17:34:56 -0700 Subject: af_unix: Fix data races around sk->sk_shutdown. [ Upstream commit e1d09c2c2f5793474556b60f83900e088d0d366d ] KCSAN found a data race around sk->sk_shutdown where unix_release_sock() and unix_shutdown() update it under unix_state_lock(), OTOH unix_poll() and unix_dgram_poll() read it locklessly. We need to annotate the writes and reads with WRITE_ONCE() and READ_ONCE(). BUG: KCSAN: data-race in unix_poll / unix_release_sock write to 0xffff88800d0f8aec of 1 bytes by task 264 on cpu 0: unix_release_sock+0x75c/0x910 net/unix/af_unix.c:631 unix_release+0x59/0x80 net/unix/af_unix.c:1042 __sock_release+0x7d/0x170 net/socket.c:653 sock_close+0x19/0x30 net/socket.c:1397 __fput+0x179/0x5e0 fs/file_table.c:321 ____fput+0x15/0x20 fs/file_table.c:349 task_work_run+0x116/0x1a0 kernel/task_work.c:179 resume_user_mode_work include/linux/resume_user_mode.h:49 [inline] exit_to_user_mode_loop kernel/entry/common.c:171 [inline] exit_to_user_mode_prepare+0x174/0x180 kernel/entry/common.c:204 __syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline] syscall_exit_to_user_mode+0x1a/0x30 kernel/entry/common.c:297 do_syscall_64+0x4b/0x90 arch/x86/entry/common.c:86 entry_SYSCALL_64_after_hwframe+0x72/0xdc read to 0xffff88800d0f8aec of 1 bytes by task 222 on cpu 1: unix_poll+0xa3/0x2a0 net/unix/af_unix.c:3170 sock_poll+0xcf/0x2b0 net/socket.c:1385 vfs_poll include/linux/poll.h:88 [inline] ep_item_poll.isra.0+0x78/0xc0 fs/eventpoll.c:855 ep_send_events fs/eventpoll.c:1694 [inline] ep_poll fs/eventpoll.c:1823 [inline] do_epoll_wait+0x6c4/0xea0 fs/eventpoll.c:2258 __do_sys_epoll_wait fs/eventpoll.c:2270 [inline] __se_sys_epoll_wait fs/eventpoll.c:2265 [inline] __x64_sys_epoll_wait+0xcc/0x190 fs/eventpoll.c:2265 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc value changed: 0x00 -> 0x03 Reported by Kernel Concurrency Sanitizer on: CPU: 1 PID: 222 Comm: dbus-broker Not tainted 6.3.0-rc7-02330-gca6270c12e20 #2 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 Fixes: 3c73419c09a5 ("af_unix: fix 'poll for write'/ connected DGRAM sockets") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Reviewed-by: Michal Kubiak Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/unix/af_unix.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 3b292a7a1fa5..70eb3bc67126 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -602,7 +602,7 @@ static void unix_release_sock(struct sock *sk, int embrion) /* Clear state */ unix_state_lock(sk); sock_orphan(sk); - sk->sk_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); path = u->path; u->path.dentry = NULL; u->path.mnt = NULL; @@ -627,7 +627,7 @@ static void unix_release_sock(struct sock *sk, int embrion) if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) { unix_state_lock(skpair); /* No more writes */ - skpair->sk_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(skpair->sk_shutdown, SHUTDOWN_MASK); if (!skb_queue_empty(&sk->sk_receive_queue) || embrion) skpair->sk_err = ECONNRESET; unix_state_unlock(skpair); @@ -3007,7 +3007,7 @@ static int unix_shutdown(struct socket *sock, int mode) ++mode; unix_state_lock(sk); - sk->sk_shutdown |= mode; + WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | mode); other = unix_peer(sk); if (other) sock_hold(other); @@ -3027,7 +3027,7 @@ static int unix_shutdown(struct socket *sock, int mode) if (mode&SEND_SHUTDOWN) peer_mode |= RCV_SHUTDOWN; unix_state_lock(other); - other->sk_shutdown |= peer_mode; + WRITE_ONCE(other->sk_shutdown, other->sk_shutdown | peer_mode); unix_state_unlock(other); other->sk_state_change(other); if (peer_mode == SHUTDOWN_MASK) @@ -3159,16 +3159,18 @@ static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wa { struct sock *sk = sock->sk; __poll_t mask; + u8 shutdown; sock_poll_wait(file, sock, wait); mask = 0; + shutdown = READ_ONCE(sk->sk_shutdown); /* exceptional events? */ if (sk->sk_err) mask |= EPOLLERR; - if (sk->sk_shutdown == SHUTDOWN_MASK) + if (shutdown == SHUTDOWN_MASK) mask |= EPOLLHUP; - if (sk->sk_shutdown & RCV_SHUTDOWN) + if (shutdown & RCV_SHUTDOWN) mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; /* readable? */ @@ -3202,18 +3204,20 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock, struct sock *sk = sock->sk, *other; unsigned int writable; __poll_t mask; + u8 shutdown; sock_poll_wait(file, sock, wait); mask = 0; + shutdown = READ_ONCE(sk->sk_shutdown); /* exceptional events? */ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue)) mask |= EPOLLERR | (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0); - if (sk->sk_shutdown & RCV_SHUTDOWN) + if (shutdown & RCV_SHUTDOWN) mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; - if (sk->sk_shutdown == SHUTDOWN_MASK) + if (shutdown == SHUTDOWN_MASK) mask |= EPOLLHUP; /* readable? */ -- cgit 1.4.1 From 1b485f39acf31e71bf30f32a0642310c1661743c Mon Sep 17 00:00:00 2001 From: John Harrison Date: Fri, 28 Apr 2023 11:56:33 -0700 Subject: drm/i915/guc: Don't capture Gen8 regs on Xe devices [ Upstream commit 275dac1f7f5e9c2a2e806b34d3b10804eec0ac3c ] A pair of pre-Xe registers were being included in the Xe capture list. GuC was rejecting those as being invalid and logging errors about them. So, stop doing it. Signed-off-by: John Harrison Reviewed-by: Alan Previn Fixes: dce2bd542337 ("drm/i915/guc: Add Gen9 registers for GuC error state capture.") Cc: Alan Previn Cc: Umesh Nerlige Ramappa Cc: Lucas De Marchi Cc: John Harrison Cc: Jani Nikula Cc: Matt Roper Cc: Balasubramani Vivekanandan Cc: Daniele Ceraolo Spurio Link: https://patchwork.freedesktop.org/patch/msgid/20230428185636.457407-2-John.C.Harrison@Intel.com (cherry picked from commit b049132d61336f643d8faf2f6574b063667088cf) Signed-off-by: Joonas Lahtinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c index 1e1fa20fb41c..18a8466f8591 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c @@ -30,12 +30,14 @@ { FORCEWAKE_MT, 0, 0, "FORCEWAKE" } #define COMMON_GEN9BASE_GLOBAL \ - { GEN8_FAULT_TLB_DATA0, 0, 0, "GEN8_FAULT_TLB_DATA0" }, \ - { GEN8_FAULT_TLB_DATA1, 0, 0, "GEN8_FAULT_TLB_DATA1" }, \ { ERROR_GEN6, 0, 0, "ERROR_GEN6" }, \ { DONE_REG, 0, 0, "DONE_REG" }, \ { HSW_GTT_CACHE_EN, 0, 0, "HSW_GTT_CACHE_EN" } +#define GEN9_GLOBAL \ + { GEN8_FAULT_TLB_DATA0, 0, 0, "GEN8_FAULT_TLB_DATA0" }, \ + { GEN8_FAULT_TLB_DATA1, 0, 0, "GEN8_FAULT_TLB_DATA1" } + #define COMMON_GEN12BASE_GLOBAL \ { GEN12_FAULT_TLB_DATA0, 0, 0, "GEN12_FAULT_TLB_DATA0" }, \ { GEN12_FAULT_TLB_DATA1, 0, 0, "GEN12_FAULT_TLB_DATA1" }, \ @@ -136,6 +138,7 @@ static const struct __guc_mmio_reg_descr xe_lpd_blt_inst_regs[] = { static const struct __guc_mmio_reg_descr default_global_regs[] = { COMMON_BASE_GLOBAL, COMMON_GEN9BASE_GLOBAL, + GEN9_GLOBAL, }; static const struct __guc_mmio_reg_descr default_rc_class_regs[] = { -- cgit 1.4.1 From dbf25cc21beff4fd2e730573845a266504b21bb2 Mon Sep 17 00:00:00 2001 From: Stanislav Lisovskiy Date: Fri, 5 May 2023 11:22:12 +0300 Subject: drm/i915: Fix NULL ptr deref by checking new_crtc_state [ Upstream commit a41d985902c153c31c616fe183cf2ee331e95ecb ] intel_atomic_get_new_crtc_state can return NULL, unless crtc state wasn't obtained previously with intel_atomic_get_crtc_state, so we must check it for NULLness here, just as in many other places, where we can't guarantee that intel_atomic_get_crtc_state was called. We are currently getting NULL ptr deref because of that, so this fix was confirmed to help. Fixes: 74a75dc90869 ("drm/i915/display: move plane prepare/cleanup to intel_atomic_plane.c") Signed-off-by: Stanislav Lisovskiy Reviewed-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/20230505082212.27089-1-stanislav.lisovskiy@intel.com (cherry picked from commit 1d5b09f8daf859247a1ea65b0d732a24d88980d8) Signed-off-by: Joonas Lahtinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_atomic_plane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c index aaa6708256d5..82826454b5e8 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c @@ -988,7 +988,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane, int ret; if (old_obj) { - const struct intel_crtc_state *crtc_state = + const struct intel_crtc_state *new_crtc_state = intel_atomic_get_new_crtc_state(state, to_intel_crtc(old_plane_state->hw.crtc)); @@ -1003,7 +1003,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane, * This should only fail upon a hung GPU, in which case we * can safely continue. */ - if (intel_crtc_needs_modeset(crtc_state)) { + if (new_crtc_state && intel_crtc_needs_modeset(new_crtc_state)) { ret = i915_sw_fence_await_reservation(&state->commit_ready, old_obj->base.resv, NULL, false, 0, -- cgit 1.4.1 From 86d73b1f98a81f3ecfc19dfe6f8d50daf5707330 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Tue, 18 Apr 2023 07:04:30 -0700 Subject: drm/i915/dp: prevent potential div-by-zero [ Upstream commit 0ff80028e2702c7c3d78b69705dc47c1ccba8c39 ] drm_dp_dsc_sink_max_slice_count() may return 0 if something goes wrong on the part of the DSC sink and its DPCD register. This null value may be later used as a divisor in intel_dsc_compute_params(), which will lead to an error. In the unlikely event that this issue occurs, fix it by testing the return value of drm_dp_dsc_sink_max_slice_count() against zero. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: a4a157777c80 ("drm/i915/dp: Compute DSC pipe config in atomic check") Signed-off-by: Nikita Zhandarovich Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20230418140430.69902-1-n.zhandarovich@fintech.ru (cherry picked from commit 51f7008239de011370c5067bbba07f0207f06b72) Signed-off-by: Joonas Lahtinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_dp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 2e09899f2f92..b1653308f145 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1512,6 +1512,11 @@ static int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, pipe_config->dsc.slice_count = drm_dp_dsc_sink_max_slice_count(intel_dp->dsc_dpcd, true); + if (!pipe_config->dsc.slice_count) { + drm_dbg_kms(&dev_priv->drm, "Unsupported Slice Count %d\n", + pipe_config->dsc.slice_count); + return -EINVAL; + } } else { u16 dsc_max_output_bpp; u8 dsc_dp_slice_count; -- cgit 1.4.1 From 36fa6187753a9b52f2bbf2f3ba628f6bad314510 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Tue, 3 Jan 2023 14:47:01 -0500 Subject: drm/i915: Expand force_probe to block probe of devices as well. [ Upstream commit 157821fb3e9aaa07cf408686b08d117bf27b7de1 ] There are new cases where we want to block i915 probe, such as when experimenting or developing the new Xe driver. But also, with the new hybrid cards, users or developers might want to use i915 only on integrated and fully block the probe of the i915 for the discrete. Or vice versa. There are even older development and validation reasons, like when you use some distro where the modprobe.blacklist is not present. But in any case, let's introduce a more granular control, but without introducing yet another parameter, but using the existent force_probe one. Just by adding a ! in the begin of the id in the force_probe, like in this case where we would block the probe for Alder Lake: $ insmod i915.ko force_probe='!46a6' v2: Take care of '*' and '!*' cases as pointed out by Gustavo and Jani. Cc: Jani Nikula Cc: Gustavo Sousa Signed-off-by: Rodrigo Vivi Reviewed-by: Jani Nikula Acked-by: Gustavo Sousa Link: https://patchwork.freedesktop.org/patch/msgid/20230103194701.1492984-1-rodrigo.vivi@intel.com Stable-dep-of: 79c901c93562 ("drm/i915: taint kernel when force probing unsupported devices") Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/Kconfig | 15 ++++++++++++--- drivers/gpu/drm/i915/i915_params.c | 2 +- drivers/gpu/drm/i915/i915_pci.c | 33 ++++++++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 3a6e176d77aa..e04715fa5bc4 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -54,24 +54,33 @@ config DRM_I915 If "M" is selected, the module will be called i915. config DRM_I915_FORCE_PROBE - string "Force probe driver for selected new Intel hardware" + string "Force probe i915 for selected Intel hardware IDs" depends on DRM_I915 help This is the default value for the i915.force_probe module parameter. Using the module parameter overrides this option. - Force probe the driver for new Intel graphics devices that are + Force probe the i915 for Intel graphics devices that are recognized but not properly supported by this kernel version. It is recommended to upgrade to a kernel version with proper support as soon as it is available. + It can also be used to block the probe of recognized and fully + supported devices. + Use "" to disable force probe. If in doubt, use this. - Use "[,,...]" to force probe the driver for listed + Use "[,,...]" to force probe the i915 for listed devices. For example, "4500" or "4500,4571". Use "*" to force probe the driver for all known devices. + Use "!" right before the ID to block the probe of the device. For + example, "4500,!4571" forces the probe of 4500 and blocks the probe of + 4571. + + Use "!*" to block the probe of the driver for all known devices. + config DRM_I915_CAPTURE_ERROR bool "Enable capturing GPU state following a hang" depends on DRM_I915 diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index d1e4d528cb17..5b24dd50fb6a 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -122,7 +122,7 @@ i915_param_named_unsafe(enable_psr2_sel_fetch, bool, 0400, "Default: 0"); i915_param_named_unsafe(force_probe, charp, 0400, - "Force probe the driver for specified devices. " + "Force probe options for specified supported devices. " "See CONFIG_DRM_I915_FORCE_PROBE for details."); i915_param_named_unsafe(disable_power_well, int, 0400, diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index a2efc0b9d50c..1fa4a5813683 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -1252,7 +1252,7 @@ static void i915_pci_remove(struct pci_dev *pdev) } /* is device_id present in comma separated list of ids */ -static bool force_probe(u16 device_id, const char *devices) +static bool device_id_in_list(u16 device_id, const char *devices, bool negative) { char *s, *p, *tok; bool ret; @@ -1261,7 +1261,9 @@ static bool force_probe(u16 device_id, const char *devices) return false; /* match everything */ - if (strcmp(devices, "*") == 0) + if (negative && strcmp(devices, "!*") == 0) + return true; + if (!negative && strcmp(devices, "*") == 0) return true; s = kstrdup(devices, GFP_KERNEL); @@ -1271,6 +1273,12 @@ static bool force_probe(u16 device_id, const char *devices) for (p = s, ret = false; (tok = strsep(&p, ",")) != NULL; ) { u16 val; + if (negative && tok[0] == '!') + tok++; + else if ((negative && tok[0] != '!') || + (!negative && tok[0] == '!')) + continue; + if (kstrtou16(tok, 16, &val) == 0 && val == device_id) { ret = true; break; @@ -1282,6 +1290,16 @@ static bool force_probe(u16 device_id, const char *devices) return ret; } +static bool id_forced(u16 device_id) +{ + return device_id_in_list(device_id, i915_modparams.force_probe, false); +} + +static bool id_blocked(u16 device_id) +{ + return device_id_in_list(device_id, i915_modparams.force_probe, true); +} + bool i915_pci_resource_valid(struct pci_dev *pdev, int bar) { if (!pci_resource_flags(pdev, bar)) @@ -1309,10 +1327,9 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) (struct intel_device_info *) ent->driver_data; int err; - if (intel_info->require_force_probe && - !force_probe(pdev->device, i915_modparams.force_probe)) { + if (intel_info->require_force_probe && !id_forced(pdev->device)) { dev_info(&pdev->dev, - "Your graphics device %04x is not properly supported by the driver in this\n" + "Your graphics device %04x is not properly supported by i915 in this\n" "kernel version. To force driver probe anyway, use i915.force_probe=%04x\n" "module parameter or CONFIG_DRM_I915_FORCE_PROBE=%04x configuration option,\n" "or (recommended) check for kernel updates.\n", @@ -1320,6 +1337,12 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; } + if (id_blocked(pdev->device)) { + dev_info(&pdev->dev, "I915 probe blocked for Device ID %04x.\n", + pdev->device); + return -ENODEV; + } + /* Only bind to function 0 of the device. Early generations * used function 1 as a placeholder for multi-head. This causes * us confusion instead, especially on the systems where both -- cgit 1.4.1 From dcd289136bccf2bdd519ee1eb355ed71c73cbe75 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 4 May 2023 13:35:08 +0300 Subject: drm/i915: taint kernel when force probing unsupported devices [ Upstream commit 79c901c93562bdf1c84ce6c1b744fbbe4389a6eb ] For development and testing purposes, the i915.force_probe module parameter and DRM_I915_FORCE_PROBE kconfig option allow probing of devices that aren't supported by the driver. The i915.force_probe module parameter is "unsafe" and setting it taints the kernel. However, using the kconfig option does not. Always taint the kernel when force probing a device that is not supported. v2: Drop "depends on EXPERT" to avoid build breakage (kernel test robot) Fixes: 7ef5ef5cdead ("drm/i915: add force_probe module parameter to replace alpha_support") Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: Daniel Vetter Cc: Dave Airlie Acked-by: Daniel Vetter Reviewed-by: Rodrigo Vivi Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20230504103508.1818540-1-jani.nikula@intel.com (cherry picked from commit 3312bb4ad09ca6423bd4a5b15a94588a8962fb8e) Signed-off-by: Joonas Lahtinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/Kconfig | 12 +++++++----- drivers/gpu/drm/i915/i915_pci.c | 6 ++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index e04715fa5bc4..6b10868ec72f 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -60,10 +60,11 @@ config DRM_I915_FORCE_PROBE This is the default value for the i915.force_probe module parameter. Using the module parameter overrides this option. - Force probe the i915 for Intel graphics devices that are - recognized but not properly supported by this kernel version. It is - recommended to upgrade to a kernel version with proper support as soon - as it is available. + Force probe the i915 driver for Intel graphics devices that are + recognized but not properly supported by this kernel version. Force + probing an unsupported device taints the kernel. It is recommended to + upgrade to a kernel version with proper support as soon as it is + available. It can also be used to block the probe of recognized and fully supported devices. @@ -73,7 +74,8 @@ config DRM_I915_FORCE_PROBE Use "[,,...]" to force probe the i915 for listed devices. For example, "4500" or "4500,4571". - Use "*" to force probe the driver for all known devices. + Use "*" to force probe the driver for all known devices. Not + recommended. Use "!" right before the ID to block the probe of the device. For example, "4500,!4571" forces the probe of 4500 and blocks the probe of diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 1fa4a5813683..efa80475fbfe 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -1343,6 +1343,12 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; } + if (intel_info->require_force_probe) { + dev_info(&pdev->dev, "Force probing unsupported Device ID %04x, tainting kernel\n", + pdev->device); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } + /* Only bind to function 0 of the device. Early generations * used function 1 as a placeholder for multi-head. This causes * us confusion instead, especially on the systems where both -- cgit 1.4.1 From 5a08a72da35bf2dd6ba63bd6ee7210a1d25dbefd Mon Sep 17 00:00:00 2001 From: Zongjie Li Date: Tue, 9 May 2023 19:27:26 +0800 Subject: fbdev: arcfb: Fix error handling in arcfb_probe() [ Upstream commit 5a6bef734247c7a8c19511664ff77634ab86f45b ] Smatch complains that: arcfb_probe() warn: 'irq' from request_irq() not released on lines: 587. Fix error handling in the arcfb_probe() function. If IO addresses are not provided or framebuffer registration fails, the code will jump to the err_addr or err_register_fb label to release resources. If IRQ request fails, previously allocated resources will be freed. Fixes: 1154ea7dcd8e ("[PATCH] Framebuffer driver for Arc LCD board") Signed-off-by: Zongjie Li Reviewed-by: Dongliang Mu Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/video/fbdev/arcfb.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/video/fbdev/arcfb.c b/drivers/video/fbdev/arcfb.c index 45e64016db32..024d0ee4f04f 100644 --- a/drivers/video/fbdev/arcfb.c +++ b/drivers/video/fbdev/arcfb.c @@ -523,7 +523,7 @@ static int arcfb_probe(struct platform_device *dev) info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev); if (!info) - goto err; + goto err_fb_alloc; info->screen_base = (char __iomem *)videomemory; info->fbops = &arcfb_ops; @@ -535,7 +535,7 @@ static int arcfb_probe(struct platform_device *dev) if (!dio_addr || !cio_addr || !c2io_addr) { printk(KERN_WARNING "no IO addresses supplied\n"); - goto err1; + goto err_addr; } par->dio_addr = dio_addr; par->cio_addr = cio_addr; @@ -551,12 +551,12 @@ static int arcfb_probe(struct platform_device *dev) printk(KERN_INFO "arcfb: Failed req IRQ %d\n", par->irq); retval = -EBUSY; - goto err1; + goto err_addr; } } retval = register_framebuffer(info); if (retval < 0) - goto err1; + goto err_register_fb; platform_set_drvdata(dev, info); fb_info(info, "Arc frame buffer device, using %dK of video memory\n", videomemorysize >> 10); @@ -580,9 +580,12 @@ static int arcfb_probe(struct platform_device *dev) } return 0; -err1: + +err_register_fb: + free_irq(par->irq, info); +err_addr: framebuffer_release(info); -err: +err_fb_alloc: vfree(videomemory); return retval; } -- cgit 1.4.1 From cc4086759fda39d0b590951fafbc4f12e3159944 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 27 Apr 2023 22:49:34 -0400 Subject: ext4: reflect error codes from ext4_multi_mount_protect() to its callers [ Upstream commit 3b50d5018ed06a647bb26c44bb5ae74e59c903c7 ] This will allow more fine-grained errno codes to be returned by the mount system call. Cc: Andreas Dilger Signed-off-by: Theodore Ts'o Stable-dep-of: a44be64bbecb ("ext4: don't clear SB_RDONLY when remounting r/w until quota is re-enabled") Signed-off-by: Sasha Levin --- fs/ext4/mmp.c | 9 ++++++++- fs/ext4/super.c | 16 +++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c index 23930ed3cbda..24b3beddaf4b 100644 --- a/fs/ext4/mmp.c +++ b/fs/ext4/mmp.c @@ -296,6 +296,7 @@ int ext4_multi_mount_protect(struct super_block *sb, if (mmp_block < le32_to_cpu(es->s_first_data_block) || mmp_block >= ext4_blocks_count(es)) { ext4_warning(sb, "Invalid MMP block in superblock"); + retval = -EINVAL; goto failed; } @@ -321,6 +322,7 @@ int ext4_multi_mount_protect(struct super_block *sb, if (seq == EXT4_MMP_SEQ_FSCK) { dump_mmp_msg(sb, mmp, "fsck is running on the filesystem"); + retval = -EBUSY; goto failed; } @@ -334,6 +336,7 @@ int ext4_multi_mount_protect(struct super_block *sb, if (schedule_timeout_interruptible(HZ * wait_time) != 0) { ext4_warning(sb, "MMP startup interrupted, failing mount\n"); + retval = -ETIMEDOUT; goto failed; } @@ -344,6 +347,7 @@ int ext4_multi_mount_protect(struct super_block *sb, if (seq != le32_to_cpu(mmp->mmp_seq)) { dump_mmp_msg(sb, mmp, "Device is already active on another node."); + retval = -EBUSY; goto failed; } @@ -367,6 +371,7 @@ skip: */ if (schedule_timeout_interruptible(HZ * wait_time) != 0) { ext4_warning(sb, "MMP startup interrupted, failing mount"); + retval = -ETIMEDOUT; goto failed; } @@ -377,6 +382,7 @@ skip: if (seq != le32_to_cpu(mmp->mmp_seq)) { dump_mmp_msg(sb, mmp, "Device is already active on another node."); + retval = -EBUSY; goto failed; } @@ -396,6 +402,7 @@ skip: EXT4_SB(sb)->s_mmp_tsk = NULL; ext4_warning(sb, "Unable to create kmmpd thread for %s.", sb->s_id); + retval = -ENOMEM; goto failed; } @@ -403,5 +410,5 @@ skip: failed: brelse(bh); - return 1; + return retval; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index d542f068ca99..b919f34bb35d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -5281,9 +5281,11 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) ext4_has_feature_orphan_present(sb) || ext4_has_feature_journal_needs_recovery(sb)); - if (ext4_has_feature_mmp(sb) && !sb_rdonly(sb)) - if (ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block))) + if (ext4_has_feature_mmp(sb) && !sb_rdonly(sb)) { + err = ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block)); + if (err) goto failed_mount3a; + } /* * The first inode we look at is the journal inode. Don't try @@ -6524,12 +6526,12 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) goto restore_opts; sb->s_flags &= ~SB_RDONLY; - if (ext4_has_feature_mmp(sb)) - if (ext4_multi_mount_protect(sb, - le64_to_cpu(es->s_mmp_block))) { - err = -EROFS; + if (ext4_has_feature_mmp(sb)) { + err = ext4_multi_mount_protect(sb, + le64_to_cpu(es->s_mmp_block)); + if (err) goto restore_opts; - } + } #ifdef CONFIG_QUOTA enable_quota = 1; #endif -- cgit 1.4.1 From cd2341c26fb6cf45445de70cfd01022cb634746b Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 5 May 2023 21:02:30 -0400 Subject: ext4: don't clear SB_RDONLY when remounting r/w until quota is re-enabled [ Upstream commit a44be64bbecb15a452496f60db6eacfee2b59c79 ] When a file system currently mounted read/only is remounted read/write, if we clear the SB_RDONLY flag too early, before the quota is initialized, and there is another process/thread constantly attempting to create a directory, it's possible to trigger the WARN_ON_ONCE(dquot_initialize_needed(inode)); in ext4_xattr_block_set(), with the following stack trace: WARNING: CPU: 0 PID: 5338 at fs/ext4/xattr.c:2141 ext4_xattr_block_set+0x2ef2/0x3680 RIP: 0010:ext4_xattr_block_set+0x2ef2/0x3680 fs/ext4/xattr.c:2141 Call Trace: ext4_xattr_set_handle+0xcd4/0x15c0 fs/ext4/xattr.c:2458 ext4_initxattrs+0xa3/0x110 fs/ext4/xattr_security.c:44 security_inode_init_security+0x2df/0x3f0 security/security.c:1147 __ext4_new_inode+0x347e/0x43d0 fs/ext4/ialloc.c:1324 ext4_mkdir+0x425/0xce0 fs/ext4/namei.c:2992 vfs_mkdir+0x29d/0x450 fs/namei.c:4038 do_mkdirat+0x264/0x520 fs/namei.c:4061 __do_sys_mkdirat fs/namei.c:4076 [inline] __se_sys_mkdirat fs/namei.c:4074 [inline] __x64_sys_mkdirat+0x89/0xa0 fs/namei.c:4074 Cc: stable@kernel.org Link: https://lore.kernel.org/r/20230506142419.984260-1-tytso@mit.edu Reported-by: syzbot+6385d7d3065524c5ca6d@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?id=6513f6cb5cd6b5fc9f37e3bb70d273b94be9c34c Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/super.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b919f34bb35d..c56192a84bf4 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -6339,6 +6339,7 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) struct ext4_mount_options old_opts; ext4_group_t g; int err = 0; + int enable_rw = 0; #ifdef CONFIG_QUOTA int enable_quota = 0; int i, j; @@ -6525,7 +6526,7 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) if (err) goto restore_opts; - sb->s_flags &= ~SB_RDONLY; + enable_rw = 1; if (ext4_has_feature_mmp(sb)) { err = ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block)); @@ -6584,6 +6585,9 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) if (!test_opt(sb, BLOCK_VALIDITY) && sbi->s_system_blks) ext4_release_system_zone(sb); + if (enable_rw) + sb->s_flags &= ~SB_RDONLY; + if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb)) ext4_stop_mmpd(sbi); -- cgit 1.4.1 From f12aa035e81438b4b005b4916bf68edf540cb4a9 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Sat, 4 Mar 2023 01:21:02 +0800 Subject: ext4: allow to find by goal if EXT4_MB_HINT_GOAL_ONLY is set [ Upstream commit 01e4ca29451760b9ac10b4cdc231c52150842643 ] If EXT4_MB_HINT_GOAL_ONLY is set, ext4_mb_regular_allocator will only allocate blocks from ext4_mb_find_by_goal. Allow to find by goal in ext4_mb_find_by_goal if EXT4_MB_HINT_GOAL_ONLY is set or allocation with EXT4_MB_HINT_GOAL_ONLY set will always fail. EXT4_MB_HINT_GOAL_ONLY is not used at all, so the problem is not found for now. Signed-off-by: Kemeng Shi Reviewed-by: Ojaswin Mujoo Link: https://lore.kernel.org/r/20230303172120.3800725-3-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o Stable-dep-of: 5354b2af3406 ("ext4: allow ext4_get_group_info() to fail") Signed-off-by: Sasha Levin --- fs/ext4/mballoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 912c4a1093fe..b1b63fc84ddb 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2162,7 +2162,7 @@ int ext4_mb_find_by_goal(struct ext4_allocation_context *ac, struct ext4_group_info *grp = ext4_get_group_info(ac->ac_sb, group); struct ext4_free_extent ex; - if (!(ac->ac_flags & EXT4_MB_HINT_TRY_GOAL)) + if (!(ac->ac_flags & (EXT4_MB_HINT_TRY_GOAL | EXT4_MB_HINT_GOAL_ONLY))) return 0; if (grp->bb_free == 0) return 0; -- cgit 1.4.1 From b4319e457d6e3fb33e443efeaf4634fc36e8a9ed Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 29 Apr 2023 00:06:28 -0400 Subject: ext4: allow ext4_get_group_info() to fail [ Upstream commit 5354b2af34064a4579be8bc0e2f15a7b70f14b5f ] Previously, ext4_get_group_info() would treat an invalid group number as BUG(), since in theory it should never happen. However, if a malicious attaker (or fuzzer) modifies the superblock via the block device while it is the file system is mounted, it is possible for s_first_data_block to get set to a very large number. In that case, when calculating the block group of some block number (such as the starting block of a preallocation region), could result in an underflow and very large block group number. Then the BUG_ON check in ext4_get_group_info() would fire, resutling in a denial of service attack that can be triggered by root or someone with write access to the block device. For a quality of implementation perspective, it's best that even if the system administrator does something that they shouldn't, that it will not trigger a BUG. So instead of BUG'ing, ext4_get_group_info() will call ext4_error and return NULL. We also add fallback code in all of the callers of ext4_get_group_info() that it might NULL. Also, since ext4_get_group_info() was already borderline to be an inline function, un-inline it. The results in a next reduction of the compiled text size of ext4 by roughly 2k. Cc: stable@kernel.org Link: https://lore.kernel.org/r/20230430154311.579720-2-tytso@mit.edu Reported-by: syzbot+e2efa3efc15a1c9e95c3@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?id=69b28112e098b070f639efb356393af3ffec4220 Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara Signed-off-by: Sasha Levin --- fs/ext4/balloc.c | 18 +++++++++++++++- fs/ext4/ext4.h | 15 ++----------- fs/ext4/ialloc.c | 12 +++++++---- fs/ext4/mballoc.c | 64 +++++++++++++++++++++++++++++++++++++++++++++---------- fs/ext4/super.c | 2 ++ 5 files changed, 82 insertions(+), 29 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index f2c415f31b75..a38aa33af08e 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -319,6 +319,22 @@ static ext4_fsblk_t ext4_valid_block_bitmap_padding(struct super_block *sb, return (next_zero_bit < bitmap_size ? next_zero_bit : 0); } +struct ext4_group_info *ext4_get_group_info(struct super_block *sb, + ext4_group_t group) +{ + struct ext4_group_info **grp_info; + long indexv, indexh; + + if (unlikely(group >= EXT4_SB(sb)->s_groups_count)) { + ext4_error(sb, "invalid group %u", group); + return NULL; + } + indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb)); + indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1); + grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv); + return grp_info[indexh]; +} + /* * Return the block number which was discovered to be invalid, or 0 if * the block bitmap is valid. @@ -393,7 +409,7 @@ static int ext4_validate_block_bitmap(struct super_block *sb, if (buffer_verified(bh)) return 0; - if (EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) + if (!grp || EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) return -EFSCORRUPTED; ext4_lock_group(sb, block_group); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index a2bc440743ae..2156d2a1a770 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2716,6 +2716,8 @@ extern void ext4_check_blocks_bitmap(struct super_block *); extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb, ext4_group_t block_group, struct buffer_head ** bh); +extern struct ext4_group_info *ext4_get_group_info(struct super_block *sb, + ext4_group_t group); extern int ext4_should_retry_alloc(struct super_block *sb, int *retries); extern struct buffer_head *ext4_read_block_bitmap_nowait(struct super_block *sb, @@ -3322,19 +3324,6 @@ static inline void ext4_isize_set(struct ext4_inode *raw_inode, loff_t i_size) raw_inode->i_size_high = cpu_to_le32(i_size >> 32); } -static inline -struct ext4_group_info *ext4_get_group_info(struct super_block *sb, - ext4_group_t group) -{ - struct ext4_group_info **grp_info; - long indexv, indexh; - BUG_ON(group >= EXT4_SB(sb)->s_groups_count); - indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb)); - indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1); - grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv); - return grp_info[indexh]; -} - /* * Reading s_groups_count requires using smp_rmb() afterwards. See * the locking protocol documented in the comments of ext4_group_add() diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index e9bc46684106..e09c74927a43 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -91,7 +91,7 @@ static int ext4_validate_inode_bitmap(struct super_block *sb, if (buffer_verified(bh)) return 0; - if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) + if (!grp || EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) return -EFSCORRUPTED; ext4_lock_group(sb, block_group); @@ -293,7 +293,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) } if (!(sbi->s_mount_state & EXT4_FC_REPLAY)) { grp = ext4_get_group_info(sb, block_group); - if (unlikely(EXT4_MB_GRP_IBITMAP_CORRUPT(grp))) { + if (!grp || unlikely(EXT4_MB_GRP_IBITMAP_CORRUPT(grp))) { fatal = -EFSCORRUPTED; goto error_return; } @@ -1047,7 +1047,7 @@ got_group: * Skip groups with already-known suspicious inode * tables */ - if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) + if (!grp || EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) goto next_group; } @@ -1185,6 +1185,10 @@ got: if (!(sbi->s_mount_state & EXT4_FC_REPLAY)) { grp = ext4_get_group_info(sb, group); + if (!grp) { + err = -EFSCORRUPTED; + goto out; + } down_read(&grp->alloc_sem); /* * protect vs itable * lazyinit @@ -1528,7 +1532,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group, } gdp = ext4_get_group_desc(sb, group, &group_desc_bh); - if (!gdp) + if (!gdp || !grp) goto out; /* diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index b1b63fc84ddb..09bb1dae40cf 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -745,6 +745,8 @@ static int __mb_check_buddy(struct ext4_buddy *e4b, char *file, MB_CHECK_ASSERT(e4b->bd_info->bb_fragments == fragments); grp = ext4_get_group_info(sb, e4b->bd_group); + if (!grp) + return NULL; list_for_each(cur, &grp->bb_prealloc_list) { ext4_group_t groupnr; struct ext4_prealloc_space *pa; @@ -1060,9 +1062,9 @@ mb_set_largest_free_order(struct super_block *sb, struct ext4_group_info *grp) static noinline_for_stack void ext4_mb_generate_buddy(struct super_block *sb, - void *buddy, void *bitmap, ext4_group_t group) + void *buddy, void *bitmap, ext4_group_t group, + struct ext4_group_info *grp) { - struct ext4_group_info *grp = ext4_get_group_info(sb, group); struct ext4_sb_info *sbi = EXT4_SB(sb); ext4_grpblk_t max = EXT4_CLUSTERS_PER_GROUP(sb); ext4_grpblk_t i = 0; @@ -1183,6 +1185,8 @@ static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp) break; grinfo = ext4_get_group_info(sb, group); + if (!grinfo) + continue; /* * If page is uptodate then we came here after online resize * which added some new uninitialized group info structs, so @@ -1248,6 +1252,10 @@ static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp) group, page->index, i * blocksize); trace_ext4_mb_buddy_bitmap_load(sb, group); grinfo = ext4_get_group_info(sb, group); + if (!grinfo) { + err = -EFSCORRUPTED; + goto out; + } grinfo->bb_fragments = 0; memset(grinfo->bb_counters, 0, sizeof(*grinfo->bb_counters) * @@ -1258,7 +1266,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp) ext4_lock_group(sb, group); /* init the buddy */ memset(data, 0xff, blocksize); - ext4_mb_generate_buddy(sb, data, incore, group); + ext4_mb_generate_buddy(sb, data, incore, group, grinfo); ext4_unlock_group(sb, group); incore = NULL; } else { @@ -1372,6 +1380,9 @@ int ext4_mb_init_group(struct super_block *sb, ext4_group_t group, gfp_t gfp) might_sleep(); mb_debug(sb, "init group %u\n", group); this_grp = ext4_get_group_info(sb, group); + if (!this_grp) + return -EFSCORRUPTED; + /* * This ensures that we don't reinit the buddy cache * page which map to the group from which we are already @@ -1446,6 +1457,8 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group, blocks_per_page = PAGE_SIZE / sb->s_blocksize; grp = ext4_get_group_info(sb, group); + if (!grp) + return -EFSCORRUPTED; e4b->bd_blkbits = sb->s_blocksize_bits; e4b->bd_info = grp; @@ -2162,6 +2175,8 @@ int ext4_mb_find_by_goal(struct ext4_allocation_context *ac, struct ext4_group_info *grp = ext4_get_group_info(ac->ac_sb, group); struct ext4_free_extent ex; + if (!grp) + return -EFSCORRUPTED; if (!(ac->ac_flags & (EXT4_MB_HINT_TRY_GOAL | EXT4_MB_HINT_GOAL_ONLY))) return 0; if (grp->bb_free == 0) @@ -2386,7 +2401,7 @@ static bool ext4_mb_good_group(struct ext4_allocation_context *ac, BUG_ON(cr < 0 || cr >= 4); - if (unlikely(EXT4_MB_GRP_BBITMAP_CORRUPT(grp))) + if (unlikely(EXT4_MB_GRP_BBITMAP_CORRUPT(grp) || !grp)) return false; free = grp->bb_free; @@ -2455,6 +2470,8 @@ static int ext4_mb_good_group_nolock(struct ext4_allocation_context *ac, ext4_grpblk_t free; int ret = 0; + if (!grp) + return -EFSCORRUPTED; if (sbi->s_mb_stats) atomic64_inc(&sbi->s_bal_cX_groups_considered[ac->ac_criteria]); if (should_lock) { @@ -2535,7 +2552,7 @@ ext4_group_t ext4_mb_prefetch(struct super_block *sb, ext4_group_t group, * prefetch once, so we avoid getblk() call, which can * be expensive. */ - if (!EXT4_MB_GRP_TEST_AND_SET_READ(grp) && + if (gdp && grp && !EXT4_MB_GRP_TEST_AND_SET_READ(grp) && EXT4_MB_GRP_NEED_INIT(grp) && ext4_free_group_clusters(sb, gdp) > 0 && !(ext4_has_group_desc_csum(sb) && @@ -2579,7 +2596,7 @@ void ext4_mb_prefetch_fini(struct super_block *sb, ext4_group_t group, group--; grp = ext4_get_group_info(sb, group); - if (EXT4_MB_GRP_NEED_INIT(grp) && + if (grp && gdp && EXT4_MB_GRP_NEED_INIT(grp) && ext4_free_group_clusters(sb, gdp) > 0 && !(ext4_has_group_desc_csum(sb) && (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)))) { @@ -2838,6 +2855,8 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) sizeof(struct ext4_group_info); grinfo = ext4_get_group_info(sb, group); + if (!grinfo) + return 0; /* Load the group info in memory only if not already loaded. */ if (unlikely(EXT4_MB_GRP_NEED_INIT(grinfo))) { err = ext4_mb_load_buddy(sb, group, &e4b); @@ -2848,7 +2867,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) buddy_loaded = 1; } - memcpy(&sg, ext4_get_group_info(sb, group), i); + memcpy(&sg, grinfo, i); if (buddy_loaded) ext4_mb_unload_buddy(&e4b); @@ -3210,8 +3229,12 @@ static int ext4_mb_init_backend(struct super_block *sb) err_freebuddy: cachep = get_groupinfo_cache(sb->s_blocksize_bits); - while (i-- > 0) - kmem_cache_free(cachep, ext4_get_group_info(sb, i)); + while (i-- > 0) { + struct ext4_group_info *grp = ext4_get_group_info(sb, i); + + if (grp) + kmem_cache_free(cachep, grp); + } i = sbi->s_group_info_size; rcu_read_lock(); group_info = rcu_dereference(sbi->s_group_info); @@ -3525,6 +3548,8 @@ int ext4_mb_release(struct super_block *sb) for (i = 0; i < ngroups; i++) { cond_resched(); grinfo = ext4_get_group_info(sb, i); + if (!grinfo) + continue; mb_group_bb_bitmap_free(grinfo); ext4_lock_group(sb, i); count = ext4_mb_cleanup_pa(grinfo); @@ -4454,6 +4479,8 @@ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap, struct ext4_free_data *entry; grp = ext4_get_group_info(sb, group); + if (!grp) + return; n = rb_first(&(grp->bb_free_root)); while (n) { @@ -4481,6 +4508,9 @@ void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap, int preallocated = 0; int len; + if (!grp) + return; + /* all form of preallocation discards first load group, * so the only competing code is preallocation use. * we don't need any locking here @@ -4672,6 +4702,8 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) ei = EXT4_I(ac->ac_inode); grp = ext4_get_group_info(sb, ac->ac_b_ex.fe_group); + if (!grp) + return; pa->pa_obj_lock = &ei->i_prealloc_lock; pa->pa_inode = ac->ac_inode; @@ -4725,6 +4757,8 @@ ext4_mb_new_group_pa(struct ext4_allocation_context *ac) atomic_add(pa->pa_free, &EXT4_SB(sb)->s_mb_preallocated); grp = ext4_get_group_info(sb, ac->ac_b_ex.fe_group); + if (!grp) + return; lg = ac->ac_lg; BUG_ON(lg == NULL); @@ -4853,6 +4887,8 @@ ext4_mb_discard_group_preallocations(struct super_block *sb, int err; int free = 0; + if (!grp) + return 0; mb_debug(sb, "discard preallocation for group %u\n", group); if (list_empty(&grp->bb_prealloc_list)) goto out_dbg; @@ -5090,6 +5126,9 @@ static inline void ext4_mb_show_pa(struct super_block *sb) struct ext4_prealloc_space *pa; ext4_grpblk_t start; struct list_head *cur; + + if (!grp) + continue; ext4_lock_group(sb, i); list_for_each(cur, &grp->bb_prealloc_list) { pa = list_entry(cur, struct ext4_prealloc_space, @@ -5895,6 +5934,7 @@ static void ext4_mb_clear_bb(handle_t *handle, struct inode *inode, struct buffer_head *bitmap_bh = NULL; struct super_block *sb = inode->i_sb; struct ext4_group_desc *gdp; + struct ext4_group_info *grp; unsigned int overflow; ext4_grpblk_t bit; struct buffer_head *gd_bh; @@ -5920,8 +5960,8 @@ do_more: overflow = 0; ext4_get_group_no_and_offset(sb, block, &block_group, &bit); - if (unlikely(EXT4_MB_GRP_BBITMAP_CORRUPT( - ext4_get_group_info(sb, block_group)))) + grp = ext4_get_group_info(sb, block_group); + if (unlikely(!grp || EXT4_MB_GRP_BBITMAP_CORRUPT(grp))) return; /* @@ -6523,6 +6563,8 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) for (group = first_group; group <= last_group; group++) { grp = ext4_get_group_info(sb, group); + if (!grp) + continue; /* We only do this if the grp has never been initialized */ if (unlikely(EXT4_MB_GRP_NEED_INIT(grp))) { ret = ext4_mb_init_group(sb, group, GFP_NOFS); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index c56192a84bf4..bd2e803d653f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1049,6 +1049,8 @@ void ext4_mark_group_bitmap_corrupted(struct super_block *sb, struct ext4_group_desc *gdp = ext4_get_group_desc(sb, group, NULL); int ret; + if (!grp || !gdp) + return; if (flags & EXT4_GROUP_INFO_BBITMAP_CORRUPT) { ret = ext4_test_and_set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state); -- cgit 1.4.1 From 522c441faf82ab88636d66be8e25b8b7dfa2e001 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 31 Jan 2023 16:12:18 -0800 Subject: refscale: Move shutdown from wait_event() to wait_event_idle() [ Upstream commit 6bc6e6b27524304aadb9c04611ddb1c84dd7617a ] The ref_scale_shutdown() kthread/function uses wait_event() to wait for the refscale test to complete. However, although the read-side tests are normally extremely fast, there is no law against specifying a very large value for the refscale.loops module parameter or against having a slow read-side primitive. Either way, this might well trigger the hung-task timeout. This commit therefore replaces those wait_event() calls with calls to wait_event_idle(), which do not trigger the hung-task timeout. Signed-off-by: Paul E. McKenney Signed-off-by: Boqun Feng Signed-off-by: Sasha Levin --- kernel/rcu/refscale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/refscale.c b/kernel/rcu/refscale.c index 435c884c02b5..d49a9d66e000 100644 --- a/kernel/rcu/refscale.c +++ b/kernel/rcu/refscale.c @@ -795,7 +795,7 @@ ref_scale_cleanup(void) static int ref_scale_shutdown(void *arg) { - wait_event(shutdown_wq, shutdown_start); + wait_event_idle(shutdown_wq, shutdown_start); smp_mb(); // Wake before output. ref_scale_cleanup(); -- cgit 1.4.1 From 801593f70be80781134e4d27988b62a399c41c5e Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Sun, 26 Feb 2023 16:16:33 +0300 Subject: selftests: cgroup: Add 'malloc' failures checks in test_memcontrol [ Upstream commit c83f320e55a49abd90629f42a72897afd579e0de ] There are several 'malloc' calls in test_memcontrol, which can be unsuccessful. This patch will add 'malloc' failures checking to give more details about test's fail reasons and avoid possible undefined behavior during the future null dereference (like the one in alloc_anon_50M_check_swap function). Signed-off-by: Ivan Orlov Reviewed-by: Muchun Song Acked-by: Shakeel Butt Acked-by: Roman Gushchin Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- tools/testing/selftests/cgroup/test_memcontrol.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c index 8833359556f3..fe4f9f430282 100644 --- a/tools/testing/selftests/cgroup/test_memcontrol.c +++ b/tools/testing/selftests/cgroup/test_memcontrol.c @@ -98,6 +98,11 @@ static int alloc_anon_50M_check(const char *cgroup, void *arg) int ret = -1; buf = malloc(size); + if (buf == NULL) { + fprintf(stderr, "malloc() failed\n"); + return -1; + } + for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) *ptr = 0; @@ -211,6 +216,11 @@ static int alloc_anon_noexit(const char *cgroup, void *arg) char *buf, *ptr; buf = malloc(size); + if (buf == NULL) { + fprintf(stderr, "malloc() failed\n"); + return -1; + } + for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) *ptr = 0; @@ -759,6 +769,11 @@ static int alloc_anon_50M_check_swap(const char *cgroup, void *arg) int ret = -1; buf = malloc(size); + if (buf == NULL) { + fprintf(stderr, "malloc() failed\n"); + return -1; + } + for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) *ptr = 0; -- cgit 1.4.1 From d0a8c0e31a09ec1efd53079083e2a677956b4d91 Mon Sep 17 00:00:00 2001 From: Zqiang Date: Sat, 24 Dec 2022 13:25:53 +0800 Subject: rcu: Protect rcu_print_task_exp_stall() ->exp_tasks access [ Upstream commit 3c1566bca3f8349f12b75d0a2d5e4a20ad6262ec ] For kernels built with CONFIG_PREEMPT_RCU=y, the following scenario can result in a NULL-pointer dereference: CPU1 CPU2 rcu_preempt_deferred_qs_irqrestore rcu_print_task_exp_stall if (special.b.blocked) READ_ONCE(rnp->exp_tasks) != NULL raw_spin_lock_rcu_node np = rcu_next_node_entry(t, rnp) if (&t->rcu_node_entry == rnp->exp_tasks) WRITE_ONCE(rnp->exp_tasks, np) .... raw_spin_unlock_irqrestore_rcu_node raw_spin_lock_irqsave_rcu_node t = list_entry(rnp->exp_tasks->prev, struct task_struct, rcu_node_entry) (if rnp->exp_tasks is NULL, this will dereference a NULL pointer) The problem is that CPU2 accesses the rcu_node structure's->exp_tasks field without holding the rcu_node structure's ->lock and CPU2 did not observe CPU1's change to rcu_node structure's ->exp_tasks in time. Therefore, if CPU1 sets rcu_node structure's->exp_tasks pointer to NULL, then CPU2 might dereference that NULL pointer. This commit therefore holds the rcu_node structure's ->lock while accessing that structure's->exp_tasks field. [ paulmck: Apply Frederic Weisbecker feedback. ] Acked-by: Joel Fernandes (Google) Signed-off-by: Zqiang Signed-off-by: Paul E. McKenney Signed-off-by: Joel Fernandes (Google) Signed-off-by: Sasha Levin --- kernel/rcu/tree_exp.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index 60732264a7d0..e25321dbb068 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -800,9 +800,11 @@ static int rcu_print_task_exp_stall(struct rcu_node *rnp) int ndetected = 0; struct task_struct *t; - if (!READ_ONCE(rnp->exp_tasks)) - return 0; raw_spin_lock_irqsave_rcu_node(rnp, flags); + if (!rnp->exp_tasks) { + raw_spin_unlock_irqrestore_rcu_node(rnp, flags); + return 0; + } t = list_entry(rnp->exp_tasks->prev, struct task_struct, rcu_node_entry); list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) { -- cgit 1.4.1 From e8c322b76e58378aa5153423c5ed2e941692e204 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 21 Mar 2023 09:18:07 +0100 Subject: open: return EINVAL for O_DIRECTORY | O_CREAT [ Upstream commit 43b450632676fb60e9faeddff285d9fac94a4f58 ] After a couple of years and multiple LTS releases we received a report that the behavior of O_DIRECTORY | O_CREAT changed starting with v5.7. On kernels prior to v5.7 combinations of O_DIRECTORY, O_CREAT, O_EXCL had the following semantics: (1) open("/tmp/d", O_DIRECTORY | O_CREAT) * d doesn't exist: create regular file * d exists and is a regular file: ENOTDIR * d exists and is a directory: EISDIR (2) open("/tmp/d", O_DIRECTORY | O_CREAT | O_EXCL) * d doesn't exist: create regular file * d exists and is a regular file: EEXIST * d exists and is a directory: EEXIST (3) open("/tmp/d", O_DIRECTORY | O_EXCL) * d doesn't exist: ENOENT * d exists and is a regular file: ENOTDIR * d exists and is a directory: open directory On kernels since to v5.7 combinations of O_DIRECTORY, O_CREAT, O_EXCL have the following semantics: (1) open("/tmp/d", O_DIRECTORY | O_CREAT) * d doesn't exist: ENOTDIR (create regular file) * d exists and is a regular file: ENOTDIR * d exists and is a directory: EISDIR (2) open("/tmp/d", O_DIRECTORY | O_CREAT | O_EXCL) * d doesn't exist: ENOTDIR (create regular file) * d exists and is a regular file: EEXIST * d exists and is a directory: EEXIST (3) open("/tmp/d", O_DIRECTORY | O_EXCL) * d doesn't exist: ENOENT * d exists and is a regular file: ENOTDIR * d exists and is a directory: open directory This is a fairly substantial semantic change that userspace didn't notice until Pedro took the time to deliberately figure out corner cases. Since no one noticed this breakage we can somewhat safely assume that O_DIRECTORY | O_CREAT combinations are likely unused. The v5.7 breakage is especially weird because while ENOTDIR is returned indicating failure a regular file is actually created. This doesn't make a lot of sense. Time was spent finding potential users of this combination. Searching on codesearch.debian.net showed that codebases often express semantical expectations about O_DIRECTORY | O_CREAT which are completely contrary to what our code has done and currently does. The expectation often is that this particular combination would create and open a directory. This suggests users who tried to use that combination would stumble upon the counterintuitive behavior no matter if pre-v5.7 or post v5.7 and quickly realize neither semantics give them what they want. For some examples see the code examples in [1] to [3] and the discussion in [4]. There are various ways to address this issue. The lazy/simple option would be to restore the pre-v5.7 behavior and to just live with that bug forever. But since there's a real chance that the O_DIRECTORY | O_CREAT quirk isn't relied upon we should try to get away with murder(ing bad semantics) first. If we need to Frankenstein pre-v5.7 behavior later so be it. So let's simply return EINVAL categorically for O_DIRECTORY | O_CREAT combinations. In addition to cleaning up the old bug this also opens up the possiblity to make that flag combination do something more intuitive in the future. Starting with this commit the following semantics apply: (1) open("/tmp/d", O_DIRECTORY | O_CREAT) * d doesn't exist: EINVAL * d exists and is a regular file: EINVAL * d exists and is a directory: EINVAL (2) open("/tmp/d", O_DIRECTORY | O_CREAT | O_EXCL) * d doesn't exist: EINVAL * d exists and is a regular file: EINVAL * d exists and is a directory: EINVAL (3) open("/tmp/d", O_DIRECTORY | O_EXCL) * d doesn't exist: ENOENT * d exists and is a regular file: ENOTDIR * d exists and is a directory: open directory One additional note, O_TMPFILE is implemented as: #define __O_TMPFILE 020000000 #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) For older kernels it was important to return an explicit error when O_TMPFILE wasn't supported. So O_TMPFILE requires that O_DIRECTORY is raised alongside __O_TMPFILE. It also enforced that O_CREAT wasn't specified. Since O_DIRECTORY | O_CREAT could be used to create a regular allowing that combination together with __O_TMPFILE would've meant that false positives were possible, i.e., that a regular file was created instead of a O_TMPFILE. This could've been used to trick userspace into thinking it operated on a O_TMPFILE when it wasn't. Now that we block O_DIRECTORY | O_CREAT completely the check for O_CREAT in the __O_TMPFILE branch via if ((flags & O_TMPFILE_MASK) != O_TMPFILE) can be dropped. Instead we can simply check verify that O_DIRECTORY is raised via if (!(flags & O_DIRECTORY)) and explain this in two comments. As Aleksa pointed out O_PATH is unaffected by this change since it always returned EINVAL if O_CREAT was specified - with or without O_DIRECTORY. Link: https://lore.kernel.org/lkml/20230320071442.172228-1-pedro.falcato@gmail.com Link: https://sources.debian.org/src/flatpak/1.14.4-1/subprojects/libglnx/glnx-dirfd.c/?hl=324#L324 [1] Link: https://sources.debian.org/src/flatpak-builder/1.2.3-1/subprojects/libglnx/glnx-shutil.c/?hl=251#L251 [2] Link: https://sources.debian.org/src/ostree/2022.7-2/libglnx/glnx-dirfd.c/?hl=324#L324 [3] Link: https://www.openwall.com/lists/oss-security/2014/11/26/14 [4] Reported-by: Pedro Falcato Cc: Aleksa Sarai Signed-off-by: Linus Torvalds Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/open.c | 18 +++++++++++++----- include/uapi/asm-generic/fcntl.h | 1 - tools/include/uapi/asm-generic/fcntl.h | 1 - 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/open.c b/fs/open.c index 20717ec510c0..9541430ec5b3 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1158,13 +1158,21 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) } /* - * In order to ensure programs get explicit errors when trying to use - * O_TMPFILE on old kernels, O_TMPFILE is implemented such that it - * looks like (O_DIRECTORY|O_RDWR & ~O_CREAT) to old kernels. But we - * have to require userspace to explicitly set it. + * Block bugs where O_DIRECTORY | O_CREAT created regular files. + * Note, that blocking O_DIRECTORY | O_CREAT here also protects + * O_TMPFILE below which requires O_DIRECTORY being raised. */ + if ((flags & (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT)) + return -EINVAL; + + /* Now handle the creative implementation of O_TMPFILE. */ if (flags & __O_TMPFILE) { - if ((flags & O_TMPFILE_MASK) != O_TMPFILE) + /* + * In order to ensure programs get explicit errors when trying + * to use O_TMPFILE on old kernels we enforce that O_DIRECTORY + * is raised alongside __O_TMPFILE. + */ + if (!(flags & O_DIRECTORY)) return -EINVAL; if (!(acc_mode & MAY_WRITE)) return -EINVAL; diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 1ecdb911add8..80f37a0d40d7 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -91,7 +91,6 @@ /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) -#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK diff --git a/tools/include/uapi/asm-generic/fcntl.h b/tools/include/uapi/asm-generic/fcntl.h index b02c8e0f4057..1c7a0f6632c0 100644 --- a/tools/include/uapi/asm-generic/fcntl.h +++ b/tools/include/uapi/asm-generic/fcntl.h @@ -91,7 +91,6 @@ /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) -#define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK -- cgit 1.4.1 From 48960a503fcec76d3f72347b7e679dda08ca43be Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 11 Apr 2023 19:57:33 +0900 Subject: fs: hfsplus: remove WARN_ON() from hfsplus_cat_{read,write}_inode() [ Upstream commit 81b21c0f0138ff5a499eafc3eb0578ad2a99622c ] syzbot is hitting WARN_ON() in hfsplus_cat_{read,write}_inode(), for crafted filesystem image can contain bogus length. There conditions are not kernel bugs that can justify kernel to panic. Reported-by: syzbot Link: https://syzkaller.appspot.com/bug?extid=e2787430e752a92b8750 Reported-by: syzbot Link: https://syzkaller.appspot.com/bug?extid=4913dca2ea6e4d43f3f1 Signed-off-by: Tetsuo Handa Reviewed-by: Viacheslav Dubeyko Message-Id: <15308173-5252-d6a3-ae3b-e96d46cb6f41@I-love.SAKURA.ne.jp> Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/hfsplus/inode.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index b675581aa9d0..399a6354f0cc 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -511,7 +511,11 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) if (type == HFSPLUS_FOLDER) { struct hfsplus_cat_folder *folder = &entry.folder; - WARN_ON(fd->entrylength < sizeof(struct hfsplus_cat_folder)); + if (fd->entrylength < sizeof(struct hfsplus_cat_folder)) { + pr_err("bad catalog folder entry\n"); + res = -EIO; + goto out; + } hfs_bnode_read(fd->bnode, &entry, fd->entryoffset, sizeof(struct hfsplus_cat_folder)); hfsplus_get_perms(inode, &folder->permissions, 1); @@ -531,7 +535,11 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) } else if (type == HFSPLUS_FILE) { struct hfsplus_cat_file *file = &entry.file; - WARN_ON(fd->entrylength < sizeof(struct hfsplus_cat_file)); + if (fd->entrylength < sizeof(struct hfsplus_cat_file)) { + pr_err("bad catalog file entry\n"); + res = -EIO; + goto out; + } hfs_bnode_read(fd->bnode, &entry, fd->entryoffset, sizeof(struct hfsplus_cat_file)); @@ -562,6 +570,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) pr_err("bad catalog entry used to create inode\n"); res = -EIO; } +out: return res; } @@ -570,6 +579,7 @@ int hfsplus_cat_write_inode(struct inode *inode) struct inode *main_inode = inode; struct hfs_find_data fd; hfsplus_cat_entry entry; + int res = 0; if (HFSPLUS_IS_RSRC(inode)) main_inode = HFSPLUS_I(inode)->rsrc_inode; @@ -588,7 +598,11 @@ int hfsplus_cat_write_inode(struct inode *inode) if (S_ISDIR(main_inode->i_mode)) { struct hfsplus_cat_folder *folder = &entry.folder; - WARN_ON(fd.entrylength < sizeof(struct hfsplus_cat_folder)); + if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) { + pr_err("bad catalog folder entry\n"); + res = -EIO; + goto out; + } hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(struct hfsplus_cat_folder)); /* simple node checks? */ @@ -613,7 +627,11 @@ int hfsplus_cat_write_inode(struct inode *inode) } else { struct hfsplus_cat_file *file = &entry.file; - WARN_ON(fd.entrylength < sizeof(struct hfsplus_cat_file)); + if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { + pr_err("bad catalog file entry\n"); + res = -EIO; + goto out; + } hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(struct hfsplus_cat_file)); hfsplus_inode_write_fork(inode, &file->data_fork); @@ -634,7 +652,7 @@ int hfsplus_cat_write_inode(struct inode *inode) set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags); out: hfs_find_exit(&fd); - return 0; + return res; } int hfsplus_fileattr_get(struct dentry *dentry, struct fileattr *fa) -- cgit 1.4.1 From 4b17053ba268bf952c19ecb58d66d5d72e782d13 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 16 Feb 2023 22:44:58 +0200 Subject: drm/displayid: add displayid_get_header() and check bounds better [ Upstream commit 5bacecc3c56131c31f18b23d366f2184328fd9cf ] Add a helper to get a pointer to struct displayid_header. To be pedantic, add buffer overflow checks to not touch the base if that itself would overflow. Cc: Iaroslav Boliukin Cc: Dmitry Osipenko Signed-off-by: Jani Nikula Tested-by: Dmitry Osipenko Reviewed-by: Dmitry Osipenko Signed-off-by: Dmitry Osipenko Link: https://patchwork.freedesktop.org/patch/msgid/4a03b3a5132642d3cdb6d4c2641422955a917292.1676580180.git.jani.nikula@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_displayid.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c index 38ea8203df45..7d03159dc146 100644 --- a/drivers/gpu/drm/drm_displayid.c +++ b/drivers/gpu/drm/drm_displayid.c @@ -7,13 +7,28 @@ #include #include +static const struct displayid_header * +displayid_get_header(const u8 *displayid, int length, int index) +{ + const struct displayid_header *base; + + if (sizeof(*base) > length - index) + return ERR_PTR(-EINVAL); + + base = (const struct displayid_header *)&displayid[index]; + + return base; +} + static int validate_displayid(const u8 *displayid, int length, int idx) { int i, dispid_length; u8 csum = 0; const struct displayid_header *base; - base = (const struct displayid_header *)&displayid[idx]; + base = displayid_get_header(displayid, length, idx); + if (IS_ERR(base)) + return PTR_ERR(base); DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", base->rev, base->bytes, base->prod_id, base->ext_count); -- cgit 1.4.1 From 92e6c79acad4b96efeff261d27bdbd8089a7dd24 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Fri, 10 Feb 2023 13:02:09 -0500 Subject: drm/amd/display: populate subvp cmd info only for the top pipe [ Upstream commit 9bb10b7aaec3b6278f9cc410c17dcaa129bbbbf0 ] [Why] System restart observed while changing the display resolution to 8k with extended mode. Sytem restart was caused by a page fault. [How] When the driver populates subvp info it did it for both the pipes using vblank which caused an outof bounds array access causing the page fault. added checks to allow the top pipe only to fix this issue. Co-authored-by: Ayush Gupta Reviewed-by: Alvin Lee Acked-by: Qingqing Zhuo Signed-off-by: Ayush Gupta Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index 0541e87e4f38..a461e9463534 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -764,7 +764,8 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc, !pipe->top_pipe && !pipe->prev_odm_pipe && pipe->stream->mall_stream_config.type == SUBVP_MAIN) { populate_subvp_cmd_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++); - } else if (pipe->plane_state && pipe->stream->mall_stream_config.type == SUBVP_NONE) { + } else if (pipe->plane_state && pipe->stream->mall_stream_config.type == SUBVP_NONE && + !pipe->top_pipe && !pipe->prev_odm_pipe) { // Don't need to check for ActiveDRAMClockChangeMargin < 0, not valid in cases where // we run through DML without calculating "natural" P-state support populate_subvp_cmd_vblank_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++); -- cgit 1.4.1 From 09f7da1301cf7390466847781ef06b0d0c61db4e Mon Sep 17 00:00:00 2001 From: Paul Hsieh Date: Fri, 10 Feb 2023 12:00:16 +0800 Subject: drm/amd/display: Correct DML calculation to align HW formula MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 26a9f53198c955b15161da48cdb51041a38d5325 ] [Why] In 2560x1440@240p eDP panel, some use cases will enable MPC combine with RGB MPO then underflow happened. This case is not allowed from HW formula.  [How] Correct eDP, DP and DP2 output bpp calculation to align HW formula. Reviewed-by: Nicholas Kazlauskas Acked-by: Qingqing Zhuo Signed-off-by: Paul Hsieh Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- .../amd/display/dc/dml/dcn31/display_mode_vba_31.c | 298 ++++++++++++++------- .../display/dc/dml/dcn314/display_mode_vba_314.c | 298 ++++++++++++++------- 2 files changed, 392 insertions(+), 204 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c index b612edb14417..77094035a26a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c @@ -4305,11 +4305,11 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l v->AudioSampleRate[k], v->AudioSampleLayout[k], v->ODMCombineEnablePerState[i][k]); - } else if (v->Output[k] == dm_dp || v->Output[k] == dm_edp) { + } else if (v->Output[k] == dm_dp || v->Output[k] == dm_edp || v->Output[k] == dm_dp2p0) { if (v->DSCEnable[k] == true) { v->RequiresDSC[i][k] = true; v->LinkDSCEnable = true; - if (v->Output[k] == dm_dp) { + if (v->Output[k] == dm_dp || v->Output[k] == dm_dp2p0) { v->RequiresFEC[i][k] = true; } else { v->RequiresFEC[i][k] = false; @@ -4317,107 +4317,201 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l } else { v->RequiresDSC[i][k] = false; v->LinkDSCEnable = false; - v->RequiresFEC[i][k] = false; - } - - v->Outbpp = BPP_INVALID; - if (v->PHYCLKPerState[i] >= 270.0) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 2700, - v->OutputLinkDPLanes[k], - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - // TODO: Need some other way to handle this nonsense - // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR" - } - if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 540.0) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 5400, - v->OutputLinkDPLanes[k], - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - // TODO: Need some other way to handle this nonsense - // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR2" - } - if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 810.0) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 8100, - v->OutputLinkDPLanes[k], - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - // TODO: Need some other way to handle this nonsense - // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3" - } - if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 10000, - 4, - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4"; + if (v->Output[k] == dm_dp2p0) { + v->RequiresFEC[i][k] = true; + } else { + v->RequiresFEC[i][k] = false; + } } - if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) { - v->Outbpp = TruncToValidBPP( - 12000, - 4, - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4"; + if (v->Output[k] == dm_dp2p0) { + v->Outbpp = BPP_INVALID; + if ((v->OutputLinkDPRate[k] == dm_dp_rate_na || v->OutputLinkDPRate[k] == dm_dp_rate_uhbr10) && + v->PHYCLKD18PerState[k] >= 10000.0 / 18.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 10000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[k] < 13500.0 / 18.0 && + v->DSCEnable[k] == true && v->ForcedOutputLinkBPP[k] == 0) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 10000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " UHBR10" + } + if (v->Outbpp == BPP_INVALID && + (v->OutputLinkDPRate[k] == dm_dp_rate_na || v->OutputLinkDPRate[k] == dm_dp_rate_uhbr13p5) && + v->PHYCLKD18PerState[k] >= 13500.0 / 18.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 13500, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[k] < 20000.0 / 18.0 && + v->DSCEnable[k] == true && v->ForcedOutputLinkBPP[k] == 0) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 13500, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " UHBR13p5" + } + if (v->Outbpp == BPP_INVALID && + (v->OutputLinkDPRate[k] == dm_dp_rate_na || v->OutputLinkDPRate[k] == dm_dp_rate_uhbr20) && + v->PHYCLKD18PerState[k] >= 20000.0 / 18.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 20000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->DSCEnable[k] == true && + v->ForcedOutputLinkBPP[k] == 0) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 20000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " UHBR20" + } + } else { + v->Outbpp = BPP_INVALID; + if (v->PHYCLKPerState[i] >= 270.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 2700, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR" + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 540.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 5400, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR2" + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 810.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 8100, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3" + } } } } else { diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c index 3afd3c80e6da..69516cfdbc64 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c @@ -4403,11 +4403,11 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ v->AudioSampleRate[k], v->AudioSampleLayout[k], v->ODMCombineEnablePerState[i][k]); - } else if (v->Output[k] == dm_dp || v->Output[k] == dm_edp) { + } else if (v->Output[k] == dm_dp || v->Output[k] == dm_edp || v->Output[k] == dm_dp2p0) { if (v->DSCEnable[k] == true) { v->RequiresDSC[i][k] = true; v->LinkDSCEnable = true; - if (v->Output[k] == dm_dp) { + if (v->Output[k] == dm_dp || v->Output[k] == dm_dp2p0) { v->RequiresFEC[i][k] = true; } else { v->RequiresFEC[i][k] = false; @@ -4415,107 +4415,201 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ } else { v->RequiresDSC[i][k] = false; v->LinkDSCEnable = false; - v->RequiresFEC[i][k] = false; - } - - v->Outbpp = BPP_INVALID; - if (v->PHYCLKPerState[i] >= 270.0) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 2700, - v->OutputLinkDPLanes[k], - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - // TODO: Need some other way to handle this nonsense - // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR" - } - if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 540.0) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 5400, - v->OutputLinkDPLanes[k], - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - // TODO: Need some other way to handle this nonsense - // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR2" - } - if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 810.0) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 8100, - v->OutputLinkDPLanes[k], - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - // TODO: Need some other way to handle this nonsense - // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3" - } - if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) { - v->Outbpp = TruncToValidBPP( - (1.0 - v->Downspreading / 100.0) * 10000, - 4, - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4"; + if (v->Output[k] == dm_dp2p0) { + v->RequiresFEC[i][k] = true; + } else { + v->RequiresFEC[i][k] = false; + } } - if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) { - v->Outbpp = TruncToValidBPP( - 12000, - 4, - v->HTotal[k], - v->HActive[k], - v->PixelClockBackEnd[k], - v->ForcedOutputLinkBPP[k], - v->LinkDSCEnable, - v->Output[k], - v->OutputFormat[k], - v->DSCInputBitPerComponent[k], - v->NumberOfDSCSlices[k], - v->AudioSampleRate[k], - v->AudioSampleLayout[k], - v->ODMCombineEnablePerState[i][k]); - v->OutputBppPerState[i][k] = v->Outbpp; - //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4"; + if (v->Output[k] == dm_dp2p0) { + v->Outbpp = BPP_INVALID; + if ((v->OutputLinkDPRate[k] == dm_dp_rate_na || v->OutputLinkDPRate[k] == dm_dp_rate_uhbr10) && + v->PHYCLKD18PerState[k] >= 10000.0 / 18.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 10000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[k] < 13500.0 / 18.0 && + v->DSCEnable[k] == true && v->ForcedOutputLinkBPP[k] == 0) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 10000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " UHBR10" + } + if (v->Outbpp == BPP_INVALID && + (v->OutputLinkDPRate[k] == dm_dp_rate_na || v->OutputLinkDPRate[k] == dm_dp_rate_uhbr13p5) && + v->PHYCLKD18PerState[k] >= 13500.0 / 18.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 13500, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[k] < 20000.0 / 18.0 && + v->DSCEnable[k] == true && v->ForcedOutputLinkBPP[k] == 0) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 13500, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " UHBR13p5" + } + if (v->Outbpp == BPP_INVALID && + (v->OutputLinkDPRate[k] == dm_dp_rate_na || v->OutputLinkDPRate[k] == dm_dp_rate_uhbr20) && + v->PHYCLKD18PerState[k] >= 20000.0 / 18.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 20000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->DSCEnable[k] == true && + v->ForcedOutputLinkBPP[k] == 0) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 20000, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " UHBR20" + } + } else { + v->Outbpp = BPP_INVALID; + if (v->PHYCLKPerState[i] >= 270.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 2700, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR" + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 540.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 5400, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR2" + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKPerState[i] >= 810.0) { + v->Outbpp = TruncToValidBPP( + (1.0 - v->Downspreading / 100.0) * 8100, + v->OutputLinkDPLanes[k], + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + // TODO: Need some other way to handle this nonsense + // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3" + } } } } else { -- cgit 1.4.1 From 898b031dc267000ff03eb12d05e531a66320d2b0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 13 Feb 2023 21:35:53 +0100 Subject: platform/x86: x86-android-tablets: Add Acer Iconia One 7 B1-750 data [ Upstream commit 2f0cf1e85ddb5ae17284050dc1adafb89e4f1d8f ] The Acer Iconia One 7 B1-750 is a x86 ACPI tablet which ships with Android x86 as factory OS. Its DSDT contains a bunch of I2C devices which are not actually there, causing various resource conflicts. Enumeration of these is skipped through the acpi_quirk_skip_i2c_client_enumeration(). Add support for manually instantiating the I2C + other devices which are actually present on this tablet by adding the necessary device info to the x86-android-tablets module. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-2-hdegoede@redhat.com Signed-off-by: Sasha Levin --- drivers/platform/x86/x86-android-tablets.c | 101 ++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c index 123a4618db55..9178076d9d7d 100644 --- a/drivers/platform/x86/x86-android-tablets.c +++ b/drivers/platform/x86/x86-android-tablets.c @@ -265,6 +265,88 @@ static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { }, }; +static struct gpiod_lookup_table int3496_reference_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; + +/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ +static const char * const acer_b1_750_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry acer_b1_750_bma250e_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix), + { } +}; + +static const struct software_node acer_b1_750_bma250e_node = { + .properties = acer_b1_750_bma250e_props, +}; + +static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { + { + /* Novatek NVT-ts touchscreen */ + .board_info = { + .type = "NVT-ts", + .addr = 0x34, + .dev_name = "NVT-ts", + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 3, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* BMA250E accelerometer */ + .board_info = { + .type = "bma250e", + .addr = 0x18, + .swnode = &acer_b1_750_bma250e_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 25, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, +}; + +static struct gpiod_lookup_table acer_b1_750_goodix_gpios = { + .dev_id = "i2c-NVT-ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { + &acer_b1_750_goodix_gpios, + &int3496_reference_gpios, + NULL +}; + +static const struct x86_dev_info acer_b1_750_info __initconst = { + .i2c_client_info = acer_b1_750_i2c_clients, + .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), + .pdev_info = int3496_pdevs, + .pdev_count = ARRAY_SIZE(int3496_pdevs), + .gpiod_lookup_tables = acer_b1_750_gpios, +}; + /* * Advantech MICA-071 * This is a standard Windows tablet, but it has an extra "quick launch" button @@ -1298,17 +1380,8 @@ static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst }, }; -static struct gpiod_lookup_table nextbook_ares8_int3496_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { - &nextbook_ares8_int3496_gpios, + &int3496_reference_gpios, NULL }; @@ -1435,6 +1508,14 @@ static const struct x86_dev_info xiaomi_mipad2_info __initconst = { }; static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { + { + /* Acer Iconia One 7 B1-750 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), + }, + .driver_data = (void *)&acer_b1_750_info, + }, { /* Advantech MICA-071 */ .matches = { -- cgit 1.4.1 From d547d499e451f1e38ad22450602122e0e73f8540 Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Thu, 23 Feb 2023 11:30:31 -0500 Subject: drm/amd/display: Enable HostVM based on rIOMMU active [ Upstream commit 97fa4dfa66fdd52ad3d0c9fadeaaa1e87605bac7 ] [Why] There is underflow and flickering occuring. The underflow stops when hostvm is forced to active. According to policy, hostvm should be enabled if riommu is active, but this is not taken into account when deciding whether to enable hostvm. [What] For DCN314, set hostvm to true if riommu is active. Reviewed-by: Nicholas Kazlauskas Acked-by: Qingqing Zhuo Signed-off-by: Gabe Teeger Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c index 4f91e6475423..bf77e56c3f3e 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c @@ -310,6 +310,10 @@ int dcn314_populate_dml_pipes_from_context_fpu(struct dc *dc, struct dc_state *c pipe->plane_state->src_rect.width < pipe->plane_state->dst_rect.width)) upscaled = true; + /* Apply HostVM policy - either based on hypervisor globally enabled, or rIOMMU active */ + if (dc->debug.dml_hostvm_override == DML_HOSTVM_NO_OVERRIDE) + pipes[i].pipe.src.hostvm = dc->vm_pa_config.is_hvm_enabled || dc->res_pool->hubbub->riommu_active; + /* * Immediate flip can be set dynamically after enabling the plane. * We need to require support for immediate flip or underflow can be -- cgit 1.4.1 From 34813f041d0e627905f47ccadc94a7fc566104d0 Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Tue, 1 Nov 2022 10:20:09 -0400 Subject: drm/amd/display: Use DC_LOG_DC in the trasform pixel function [ Upstream commit 7222f5841ff49709ca666b05ff336776e0664a20 ] [Why & How] DC now uses a new commit sequence which is more robust since it addresses cases where we need to reorganize pipes based on planes and other parameters. As a result, this new commit sequence reset the DC state by cleaning plane states and re-creating them accordingly with the need. For this reason, the dce_transform_set_pixel_storage_depth can be invoked after a plane state is destroyed and before its re-creation. In this situation and on DCE devices, DC will hit a condition that will trigger a dmesg log that looks like this: Console: switching to colour frame buffer device 240x67 ------------[ cut here ]------------ [..] Hardware name: System manufacturer System Product Name/PRIME X370-PRO, BIOS 5603 07/28/2020 RIP: 0010:dce_transform_set_pixel_storage_depth+0x3f8/0x480 [amdgpu] [..] RSP: 0018:ffffc9000202b850 EFLAGS: 00010293 RAX: ffffffffa081d100 RBX: ffff888110790000 RCX: 000000000000000c RDX: ffff888100bedbf8 RSI: 0000000000001a50 RDI: ffff88810463c900 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000007 R10: 0000000000000001 R11: 0000000000000f00 R12: ffff88810f500010 R13: ffff888100bedbf8 R14: ffff88810f515688 R15: 0000000000000000 FS: 00007ff0159249c0(0000) GS:ffff88840e940000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff01528e550 CR3: 0000000002a10000 CR4: 00000000003506e0 Call Trace: ? dm_write_reg_func+0x21/0x80 [amdgpu 340dadd3f7c8cf4be11cf0bdc850245e99abe0e8] dc_stream_set_dither_option+0xfb/0x130 [amdgpu 340dadd3f7c8cf4be11cf0bdc850245e99abe0e8] amdgpu_dm_crtc_configure_crc_source+0x10b/0x190 [amdgpu 340dadd3f7c8cf4be11cf0bdc850245e99abe0e8] amdgpu_dm_atomic_commit_tail+0x20a8/0x2a90 [amdgpu 340dadd3f7c8cf4be11cf0bdc850245e99abe0e8] ? free_unref_page_commit+0x98/0x170 ? free_unref_page+0xcc/0x150 commit_tail+0x94/0x120 drm_atomic_helper_commit+0x10f/0x140 drm_atomic_commit+0x94/0xc0 ? drm_plane_get_damage_clips.cold+0x1c/0x1c drm_client_modeset_commit_atomic+0x203/0x250 drm_client_modeset_commit_locked+0x56/0x150 drm_client_modeset_commit+0x21/0x40 drm_fb_helper_lastclose+0x42/0x70 amdgpu_driver_lastclose_kms+0xa/0x10 [amdgpu 340dadd3f7c8cf4be11cf0bdc850245e99abe0e8] drm_release+0xda/0x110 __fput+0x89/0x240 task_work_run+0x5c/0x90 do_exit+0x333/0xae0 do_group_exit+0x2d/0x90 __x64_sys_exit_group+0x14/0x20 do_syscall_64+0x5b/0x80 ? exit_to_user_mode_prepare+0x1e/0x140 entry_SYSCALL_64_after_hwframe+0x44/0xae RIP: 0033:0x7ff016ceaca1 Code: Unable to access opcode bytes at RIP 0x7ff016ceac77. RSP: 002b:00007ffe7a2357e8 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 RAX: ffffffffffffffda RBX: 00007ff016e15a00 RCX: 00007ff016ceaca1 RDX: 000000000000003c RSI: 00000000000000e7 RDI: 0000000000000000 RBP: 0000000000000000 R08: ffffffffffffff78 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007ff016e15a00 R13: 0000000000000000 R14: 00007ff016e1aee8 R15: 00007ff016e1af00 Since this issue only happens in a transition state on DC, this commit replace BREAK_TO_DEBUGGER with DC_LOG_DC. Reviewed-by: Harry Wentland Acked-by: Qingqing Zhuo Signed-off-by: Rodrigo Siqueira Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/dce/dce_transform.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index d9fd4ec60588..670d5ab9d998 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -1009,7 +1009,7 @@ static void dce_transform_set_pixel_storage_depth( color_depth = COLOR_DEPTH_101010; pixel_depth = 0; expan_mode = 1; - BREAK_TO_DEBUGGER(); + DC_LOG_DC("The pixel depth %d is not valid, set COLOR_DEPTH_101010 instead.", depth); break; } @@ -1023,8 +1023,7 @@ static void dce_transform_set_pixel_storage_depth( if (!(xfm_dce->lb_pixel_depth_supported & depth)) { /*we should use unsupported capabilities * unless it is required by w/a*/ - DC_LOG_WARNING("%s: Capability not supported", - __func__); + DC_LOG_DC("%s: Capability not supported", __func__); } } -- cgit 1.4.1 From 10add04ee6083a942d1c6aebb7257ac4eaa35e9e Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 13 Mar 2023 08:18:11 +0100 Subject: regmap: cache: Return error in cache sync operations for REGCACHE_NONE [ Upstream commit fd883d79e4dcd2417c2b80756f22a2ff03b0f6e0 ] There is no sense in doing a cache sync on REGCACHE_NONE regmaps. Instead of panicking the kernel due to missing cache_ops, return an error to client driver. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20230313071812.13577-1-alexander.stein@ew.tq-group.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/base/regmap/regcache.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 362e043e26d8..8031007b4887 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -349,6 +349,9 @@ int regcache_sync(struct regmap *map) const char *name; bool bypass; + if (WARN_ON(map->cache_type == REGCACHE_NONE)) + return -EINVAL; + BUG_ON(!map->cache_ops); map->lock(map->lock_arg); @@ -418,6 +421,9 @@ int regcache_sync_region(struct regmap *map, unsigned int min, const char *name; bool bypass; + if (WARN_ON(map->cache_type == REGCACHE_NONE)) + return -EINVAL; + BUG_ON(!map->cache_ops); map->lock(map->lock_arg); -- cgit 1.4.1 From 44361033a8806aabd0f49b24e5a2fc07232cc5ff Mon Sep 17 00:00:00 2001 From: Iuliana Prodan Date: Tue, 21 Feb 2023 19:03:56 +0200 Subject: remoteproc: imx_dsp_rproc: Add custom memory copy implementation for i.MX DSP Cores [ Upstream commit 408ec1ff0caa340c57eecf4cbd14ef0132036a50 ] The IRAM is part of the HiFi DSP. According to hardware specification only 32-bits write are allowed otherwise we get a Kernel panic. Therefore add a custom memory copy and memset functions to deal with the above restriction. Signed-off-by: Iuliana Prodan Link: https://lore.kernel.org/r/20230221170356.27923-1-iuliana.prodan@oss.nxp.com Signed-off-by: Mathieu Poirier Signed-off-by: Sasha Levin --- drivers/remoteproc/imx_dsp_rproc.c | 187 ++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index 506ec9565716..e8e23f6b8556 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -721,6 +721,191 @@ static void imx_dsp_rproc_kick(struct rproc *rproc, int vqid) dev_err(dev, "%s: failed (%d, err:%d)\n", __func__, vqid, err); } +/* + * Custom memory copy implementation for i.MX DSP Cores + * + * The IRAM is part of the HiFi DSP. + * According to hw specs only 32-bits writes are allowed. + */ +static int imx_dsp_rproc_memcpy(void *dest, const void *src, size_t size) +{ + const u8 *src_byte = src; + const u32 *source = src; + u32 affected_mask; + u32 *dst = dest; + int i, q, r; + u32 tmp; + + /* destination must be 32bit aligned */ + if (!IS_ALIGNED((uintptr_t)dest, 4)) + return -EINVAL; + + q = size / 4; + r = size % 4; + + /* copy data in units of 32 bits at a time */ + for (i = 0; i < q; i++) + writel(source[i], &dst[i]); + + if (r) { + affected_mask = GENMASK(8 * r, 0); + + /* + * first read the 32bit data of dest, then change affected + * bytes, and write back to dest. + * For unaffected bytes, it should not be changed + */ + tmp = readl(dest + q * 4); + tmp &= ~affected_mask; + + /* avoid reading after end of source */ + for (i = 0; i < r; i++) + tmp |= (src_byte[q * 4 + i] << (8 * i)); + + writel(tmp, dest + q * 4); + } + + return 0; +} + +/* + * Custom memset implementation for i.MX DSP Cores + * + * The IRAM is part of the HiFi DSP. + * According to hw specs only 32-bits writes are allowed. + */ +static int imx_dsp_rproc_memset(void *addr, u8 value, size_t size) +{ + u32 tmp_val = value; + u32 *tmp_dst = addr; + u32 affected_mask; + int q, r; + u32 tmp; + + /* destination must be 32bit aligned */ + if (!IS_ALIGNED((uintptr_t)addr, 4)) + return -EINVAL; + + tmp_val |= tmp_val << 8; + tmp_val |= tmp_val << 16; + + q = size / 4; + r = size % 4; + + while (q--) + writel(tmp_val, tmp_dst++); + + if (r) { + affected_mask = GENMASK(8 * r, 0); + + /* + * first read the 32bit data of addr, then change affected + * bytes, and write back to addr. + * For unaffected bytes, it should not be changed + */ + tmp = readl(tmp_dst); + tmp &= ~affected_mask; + + tmp |= (tmp_val & affected_mask); + writel(tmp, tmp_dst); + } + + return 0; +} + +/* + * imx_dsp_rproc_elf_load_segments() - load firmware segments to memory + * @rproc: remote processor which will be booted using these fw segments + * @fw: the ELF firmware image + * + * This function loads the firmware segments to memory, where the remote + * processor expects them. + * + * Return: 0 on success and an appropriate error code otherwise + */ +static int imx_dsp_rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) +{ + struct device *dev = &rproc->dev; + const void *ehdr, *phdr; + int i, ret = 0; + u16 phnum; + const u8 *elf_data = fw->data; + u8 class = fw_elf_get_class(fw); + u32 elf_phdr_get_size = elf_size_of_phdr(class); + + ehdr = elf_data; + phnum = elf_hdr_get_e_phnum(class, ehdr); + phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr); + + /* go through the available ELF segments */ + for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) { + u64 da = elf_phdr_get_p_paddr(class, phdr); + u64 memsz = elf_phdr_get_p_memsz(class, phdr); + u64 filesz = elf_phdr_get_p_filesz(class, phdr); + u64 offset = elf_phdr_get_p_offset(class, phdr); + u32 type = elf_phdr_get_p_type(class, phdr); + void *ptr; + + if (type != PT_LOAD || !memsz) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n", + type, da, memsz, filesz); + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + if (offset + filesz > fw->size) { + dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n", + offset + filesz, fw->size); + ret = -EINVAL; + break; + } + + if (!rproc_u64_fit_in_size_t(memsz)) { + dev_err(dev, "size (%llx) does not fit in size_t type\n", + memsz); + ret = -EOVERFLOW; + break; + } + + /* grab the kernel address for this device address */ + ptr = rproc_da_to_va(rproc, da, memsz, NULL); + if (!ptr) { + dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da, + memsz); + ret = -EINVAL; + break; + } + + /* put the segment where the remote processor expects it */ + if (filesz) { + ret = imx_dsp_rproc_memcpy(ptr, elf_data + offset, filesz); + if (ret) { + dev_err(dev, "memory copy failed for da 0x%llx memsz 0x%llx\n", + da, memsz); + break; + } + } + + /* zero out remaining memory for this segment */ + if (memsz > filesz) { + ret = imx_dsp_rproc_memset(ptr + filesz, 0, memsz - filesz); + if (ret) { + dev_err(dev, "memset failed for da 0x%llx memsz 0x%llx\n", + da, memsz); + break; + } + } + } + + return ret; +} + static int imx_dsp_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { if (rproc_elf_load_rsc_table(rproc, fw)) @@ -735,7 +920,7 @@ static const struct rproc_ops imx_dsp_rproc_ops = { .start = imx_dsp_rproc_start, .stop = imx_dsp_rproc_stop, .kick = imx_dsp_rproc_kick, - .load = rproc_elf_load_segments, + .load = imx_dsp_rproc_elf_load_segments, .parse_fw = imx_dsp_rproc_parse_fw, .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, -- cgit 1.4.1 From 346c975524558da219c2ee5624b49dbef06b61b1 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 2 Mar 2023 02:18:49 +0100 Subject: arm64: dts: qcom: msm8996: Add missing DWC3 quirks [ Upstream commit d0af0537e28f6eace02deed63b585396de939213 ] Add missing dwc3 quirks from msm-3.18. Unfortunately, none of them make `dwc3-qcom 6af8800.usb: HS-PHY not in L2` go away. Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230302011849.1873056-1-konrad.dybcio@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/msm8996.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 67b87915d822..9f8910054201 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -2979,8 +2979,11 @@ interrupts = <0 131 IRQ_TYPE_LEVEL_HIGH>; phys = <&hsusb_phy1>, <&ssusb_phy_0>; phy-names = "usb2-phy", "usb3-phy"; + snps,hird-threshold = /bits/ 8 <0>; snps,dis_u2_susphy_quirk; snps,dis_enblslpm_quirk; + snps,is-utmi-l1-suspend; + tx-fifo-resize; }; }; -- cgit 1.4.1 From 6738841f6fcf23e9fc30e2449f32fc84ee19c6f1 Mon Sep 17 00:00:00 2001 From: harperchen Date: Thu, 2 Mar 2023 13:39:05 +0100 Subject: media: cx23885: Fix a null-ptr-deref bug in buffer_prepare() and buffer_finish() [ Upstream commit 47e8b73bc35d7c54642f78e498697692f6358996 ] When the driver calls cx23885_risc_buffer() to prepare the buffer, the function call dma_alloc_coherent may fail, resulting in a empty buffer risc->cpu. Later when we free the buffer or access the buffer, null ptr deref is triggered. This bug is similar to the following one: https://git.linuxtv.org/media_stage.git/commit/?id=2b064d91440b33fba5b452f2d1b31f13ae911d71. We believe the bug can be also dynamically triggered from user side. Similarly, we fix this by checking the return value of cx23885_risc_buffer() and the value of risc->cpu before buffer free. Signed-off-by: harperchen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/pci/cx23885/cx23885-core.c | 4 +++- drivers/media/pci/cx23885/cx23885-video.c | 13 +++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 9232a966bcab..2ce2914576cf 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1325,7 +1325,9 @@ void cx23885_free_buffer(struct cx23885_dev *dev, struct cx23885_buffer *buf) { struct cx23885_riscmem *risc = &buf->risc; - dma_free_coherent(&dev->pci->dev, risc->size, risc->cpu, risc->dma); + if (risc->cpu) + dma_free_coherent(&dev->pci->dev, risc->size, risc->cpu, risc->dma); + memset(risc, 0, sizeof(*risc)); } static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 3d03f5e95786..671fc0588e43 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -342,6 +342,7 @@ static int queue_setup(struct vb2_queue *q, static int buffer_prepare(struct vb2_buffer *vb) { + int ret; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = @@ -358,12 +359,12 @@ static int buffer_prepare(struct vb2_buffer *vb) switch (dev->field) { case V4L2_FIELD_TOP: - cx23885_risc_buffer(dev->pci, &buf->risc, + ret = cx23885_risc_buffer(dev->pci, &buf->risc, sgt->sgl, 0, UNSET, buf->bpl, 0, dev->height); break; case V4L2_FIELD_BOTTOM: - cx23885_risc_buffer(dev->pci, &buf->risc, + ret = cx23885_risc_buffer(dev->pci, &buf->risc, sgt->sgl, UNSET, 0, buf->bpl, 0, dev->height); break; @@ -391,21 +392,21 @@ static int buffer_prepare(struct vb2_buffer *vb) line0_offset = 0; line1_offset = buf->bpl; } - cx23885_risc_buffer(dev->pci, &buf->risc, + ret = cx23885_risc_buffer(dev->pci, &buf->risc, sgt->sgl, line0_offset, line1_offset, buf->bpl, buf->bpl, dev->height >> 1); break; case V4L2_FIELD_SEQ_TB: - cx23885_risc_buffer(dev->pci, &buf->risc, + ret = cx23885_risc_buffer(dev->pci, &buf->risc, sgt->sgl, 0, buf->bpl * (dev->height >> 1), buf->bpl, 0, dev->height >> 1); break; case V4L2_FIELD_SEQ_BT: - cx23885_risc_buffer(dev->pci, &buf->risc, + ret = cx23885_risc_buffer(dev->pci, &buf->risc, sgt->sgl, buf->bpl * (dev->height >> 1), 0, buf->bpl, 0, @@ -418,7 +419,7 @@ static int buffer_prepare(struct vb2_buffer *vb) buf, buf->vb.vb2_buf.index, dev->width, dev->height, dev->fmt->depth, dev->fmt->fourcc, (unsigned long)buf->risc.dma); - return 0; + return ret; } static void buffer_finish(struct vb2_buffer *vb) -- cgit 1.4.1 From 3c67f49a6643d973e83968ea35806c7b5ae68b56 Mon Sep 17 00:00:00 2001 From: harperchen Date: Fri, 3 Mar 2023 16:30:11 +0100 Subject: media: pci: tw68: Fix null-ptr-deref bug in buf prepare and finish [ Upstream commit 1634b7adcc5bef645b3666fdd564e5952a9e24e0 ] When the driver calls tw68_risc_buffer() to prepare the buffer, the function call dma_alloc_coherent may fail, resulting in a empty buffer buf->cpu. Later when we free the buffer or access the buffer, null ptr deref is triggered. This bug is similar to the following one: https://git.linuxtv.org/media_stage.git/commit/?id=2b064d91440b33fba5b452f2d1b31f13ae911d71. We believe the bug can be also dynamically triggered from user side. Similarly, we fix this by checking the return value of tw68_risc_buffer() and the value of buf->cpu before buffer free. Signed-off-by: harperchen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/pci/tw68/tw68-video.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c index 0cbc5b038073..773a18702d36 100644 --- a/drivers/media/pci/tw68/tw68-video.c +++ b/drivers/media/pci/tw68/tw68-video.c @@ -437,6 +437,7 @@ static void tw68_buf_queue(struct vb2_buffer *vb) */ static int tw68_buf_prepare(struct vb2_buffer *vb) { + int ret; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_queue *vq = vb->vb2_queue; struct tw68_dev *dev = vb2_get_drv_priv(vq); @@ -452,30 +453,30 @@ static int tw68_buf_prepare(struct vb2_buffer *vb) bpl = (dev->width * dev->fmt->depth) >> 3; switch (dev->field) { case V4L2_FIELD_TOP: - tw68_risc_buffer(dev->pci, buf, dma->sgl, + ret = tw68_risc_buffer(dev->pci, buf, dma->sgl, 0, UNSET, bpl, 0, dev->height); break; case V4L2_FIELD_BOTTOM: - tw68_risc_buffer(dev->pci, buf, dma->sgl, + ret = tw68_risc_buffer(dev->pci, buf, dma->sgl, UNSET, 0, bpl, 0, dev->height); break; case V4L2_FIELD_SEQ_TB: - tw68_risc_buffer(dev->pci, buf, dma->sgl, + ret = tw68_risc_buffer(dev->pci, buf, dma->sgl, 0, bpl * (dev->height >> 1), bpl, 0, dev->height >> 1); break; case V4L2_FIELD_SEQ_BT: - tw68_risc_buffer(dev->pci, buf, dma->sgl, + ret = tw68_risc_buffer(dev->pci, buf, dma->sgl, bpl * (dev->height >> 1), 0, bpl, 0, dev->height >> 1); break; case V4L2_FIELD_INTERLACED: default: - tw68_risc_buffer(dev->pci, buf, dma->sgl, + ret = tw68_risc_buffer(dev->pci, buf, dma->sgl, 0, bpl, bpl, bpl, dev->height >> 1); break; } - return 0; + return ret; } static void tw68_buf_finish(struct vb2_buffer *vb) @@ -485,7 +486,8 @@ static void tw68_buf_finish(struct vb2_buffer *vb) struct tw68_dev *dev = vb2_get_drv_priv(vq); struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb); - dma_free_coherent(&dev->pci->dev, buf->size, buf->cpu, buf->dma); + if (buf->cpu) + dma_free_coherent(&dev->pci->dev, buf->size, buf->cpu, buf->dma); } static int tw68_start_streaming(struct vb2_queue *q, unsigned int count) -- cgit 1.4.1 From cc4273233ace863f62344adeda89e06843dff88c Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sat, 11 Mar 2023 14:48:47 +0100 Subject: media: pvrusb2: VIDEO_PVRUSB2 depends on DVB_CORE to use dvb_* symbols [ Upstream commit 1107283b3351bef138cd12dbda1f999891cab7db ] A rand config causes this link error vmlinux.o: In function `pvr2_dvb_create': (.text+0x8af1d2): undefined reference to `dvb_register_adapter' The rand config has CONFIG_VIDEO_PVRUSB2=y CONFIG_VIDEO_DEV=y CONFIG_DVB_CORE=m VIDEO_PVRUSB2 should also depend on DVB_CORE. Signed-off-by: Tom Rix Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/pvrusb2/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/pvrusb2/Kconfig b/drivers/media/usb/pvrusb2/Kconfig index f2b64e49c5a2..9501b10b31aa 100644 --- a/drivers/media/usb/pvrusb2/Kconfig +++ b/drivers/media/usb/pvrusb2/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" - depends on VIDEO_DEV && I2C + depends on VIDEO_DEV && I2C && DVB_CORE select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X -- cgit 1.4.1 From 79ca94bc3e8cc3befa883c7d30b30a27ef0ea386 Mon Sep 17 00:00:00 2001 From: Kang Chen Date: Sun, 26 Feb 2023 13:54:27 +0800 Subject: ACPI: processor: Check for null return of devm_kzalloc() in fch_misc_setup() [ Upstream commit 4dea41775d951ff1f7b472a346a8ca3ae7e74455 ] devm_kzalloc() may fail, clk_data->name might be NULL and will cause a NULL pointer dereference later. Signed-off-by: Kang Chen [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpi_apd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 3bbe2276cac7..80f945cbec8a 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -83,6 +83,8 @@ static int fch_misc_setup(struct apd_private_data *pdata) if (!acpi_dev_get_property(adev, "clk-name", ACPI_TYPE_STRING, &obj)) { clk_data->name = devm_kzalloc(&adev->dev, obj->string.length, GFP_KERNEL); + if (!clk_data->name) + return -ENOMEM; strcpy(clk_data->name, obj->string.pointer); } else { -- cgit 1.4.1 From 110d4202522373d629d14597af9bac97eb58bd67 Mon Sep 17 00:00:00 2001 From: Toby Chen Date: Thu, 16 Mar 2023 17:51:26 -0700 Subject: drm/rockchip: dw_hdmi: cleanup drm encoder during unbind [ Upstream commit b5af48eedcb53491c02ded55d5991e03d6da6dbf ] This fixes a use-after-free crash during rmmod. The DRM encoder is embedded inside the larger rockchip_hdmi, which is allocated with the component. The component memory gets freed before the main drm device is destroyed. Fix it by running encoder cleanup before tearing down its container. Signed-off-by: Toby Chen [moved encoder cleanup above clk_disable, similar to bind-error-path] Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20230317005126.496-1-tobyc@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 2f4b8f64cbad..ae857bf8bd62 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -640,6 +640,7 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); dw_hdmi_unbind(hdmi->hdmi); + drm_encoder_cleanup(&hdmi->encoder.encoder); clk_disable_unprepare(hdmi->ref_clk); regulator_disable(hdmi->avdd_1v8); -- cgit 1.4.1 From 9a342d4eb9fb8e52f7d1afe088a79513f3f9a9a5 Mon Sep 17 00:00:00 2001 From: Zheng Wang Date: Wed, 8 Mar 2023 00:43:38 +0800 Subject: memstick: r592: Fix UAF bug in r592_remove due to race condition [ Upstream commit 63264422785021704c39b38f65a78ab9e4a186d7 ] In r592_probe, dev->detect_timer was bound with r592_detect_timer. In r592_irq function, the timer function will be invoked by mod_timer. If we remove the module which will call hantro_release to make cleanup, there may be a unfinished work. The possible sequence is as follows, which will cause a typical UAF bug. Fix it by canceling the work before cleanup in r592_remove. CPU0 CPU1 |r592_detect_timer r592_remove | memstick_free_host| put_device; | kfree(host); | | | queue_work | &host->media_checker //use Signed-off-by: Zheng Wang Link: https://lore.kernel.org/r/20230307164338.1246287-1-zyytlz.wz@163.com Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/memstick/host/r592.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c index 1d35d147552d..42bfc46842b8 100644 --- a/drivers/memstick/host/r592.c +++ b/drivers/memstick/host/r592.c @@ -829,7 +829,7 @@ static void r592_remove(struct pci_dev *pdev) /* Stop the processing thread. That ensures that we won't take any more requests */ kthread_stop(dev->io_thread); - + del_timer_sync(&dev->detect_timer); r592_enable_device(dev, false); while (!error && dev->req) { -- cgit 1.4.1 From b963e1b7066f0af03e16e4960c0a0aa9a798c191 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Thu, 9 Mar 2023 21:46:05 +0100 Subject: arm64: dts: imx8mq-librem5: Remove dis_u3_susphy_quirk from usb_dwc3_0 [ Upstream commit cfe9de291bd2bbce18c5cd79e1dd582cbbacdb4f ] This reduces power consumption in system suspend by about 10%. Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Martin Kepplinger Signed-off-by: Shawn Guo Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi index ae08556b2ef2..1499d5d8bbc0 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi @@ -1299,7 +1299,6 @@ #address-cells = <1>; #size-cells = <0>; dr_mode = "otg"; - snps,dis_u3_susphy_quirk; usb-role-switch; status = "okay"; -- cgit 1.4.1 From a8267bc8de736cae927165191b52fbc20d101dd1 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Thu, 16 Feb 2023 09:49:19 +0100 Subject: firmware: arm_sdei: Fix sleep from invalid context BUG [ Upstream commit d2c48b2387eb89e0bf2a2e06e30987cf410acad4 ] Running a preempt-rt (v6.2-rc3-rt1) based kernel on an Ampere Altra triggers: BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:46 in_atomic(): 0, irqs_disabled(): 128, non_block: 0, pid: 24, name: cpuhp/0 preempt_count: 0, expected: 0 RCU nest depth: 0, expected: 0 3 locks held by cpuhp/0/24: #0: ffffda30217c70d0 (cpu_hotplug_lock){++++}-{0:0}, at: cpuhp_thread_fun+0x5c/0x248 #1: ffffda30217c7120 (cpuhp_state-up){+.+.}-{0:0}, at: cpuhp_thread_fun+0x5c/0x248 #2: ffffda3021c711f0 (sdei_list_lock){....}-{3:3}, at: sdei_cpuhp_up+0x3c/0x130 irq event stamp: 36 hardirqs last enabled at (35): [] finish_task_switch+0xb4/0x2b0 hardirqs last disabled at (36): [] cpuhp_thread_fun+0x21c/0x248 softirqs last enabled at (0): [] copy_process+0x63c/0x1ac0 softirqs last disabled at (0): [<0000000000000000>] 0x0 CPU: 0 PID: 24 Comm: cpuhp/0 Not tainted 5.19.0-rc3-rt5-[...] Hardware name: WIWYNN Mt.Jade Server [...] Call trace: dump_backtrace+0x114/0x120 show_stack+0x20/0x70 dump_stack_lvl+0x9c/0xd8 dump_stack+0x18/0x34 __might_resched+0x188/0x228 rt_spin_lock+0x70/0x120 sdei_cpuhp_up+0x3c/0x130 cpuhp_invoke_callback+0x250/0xf08 cpuhp_thread_fun+0x120/0x248 smpboot_thread_fn+0x280/0x320 kthread+0x130/0x140 ret_from_fork+0x10/0x20 sdei_cpuhp_up() is called in the STARTING hotplug section, which runs with interrupts disabled. Use a CPUHP_AP_ONLINE_DYN entry instead to execute the cpuhp cb later, with preemption enabled. SDEI originally got its own cpuhp slot to allow interacting with perf. It got superseded by pNMI and this early slot is not relevant anymore. [1] Some SDEI calls (e.g. SDEI_1_0_FN_SDEI_PE_MASK) take actions on the calling CPU. It is checked that preemption is disabled for them. _ONLINE cpuhp cb are executed in the 'per CPU hotplug thread'. Preemption is enabled in those threads, but their cpumask is limited to 1 CPU. Move 'WARN_ON_ONCE(preemptible())' statements so that SDEI cpuhp cb don't trigger them. Also add a check for the SDEI_1_0_FN_SDEI_PRIVATE_RESET SDEI call which acts on the calling CPU. [1]: https://lore.kernel.org/all/5813b8c5-ae3e-87fd-fccc-94c9cd08816d@arm.com/ Suggested-by: James Morse Signed-off-by: Pierre Gondois Reviewed-by: James Morse Link: https://lore.kernel.org/r/20230216084920.144064-1-pierre.gondois@arm.com Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/firmware/arm_sdei.c | 37 ++++++++++++++++++++----------------- include/linux/cpuhotplug.h | 1 - 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 1e1a51510e83..f9040bd61081 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -43,6 +43,8 @@ static asmlinkage void (*sdei_firmware_call)(unsigned long function_id, /* entry point from firmware to arch asm code */ static unsigned long sdei_entry_point; +static int sdei_hp_state; + struct sdei_event { /* These three are protected by the sdei_list_lock */ struct list_head list; @@ -301,8 +303,6 @@ int sdei_mask_local_cpu(void) { int err; - WARN_ON_ONCE(preemptible()); - err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0, NULL); if (err && err != -EIO) { pr_warn_once("failed to mask CPU[%u]: %d\n", @@ -315,6 +315,7 @@ int sdei_mask_local_cpu(void) static void _ipi_mask_cpu(void *ignored) { + WARN_ON_ONCE(preemptible()); sdei_mask_local_cpu(); } @@ -322,8 +323,6 @@ int sdei_unmask_local_cpu(void) { int err; - WARN_ON_ONCE(preemptible()); - err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PE_UNMASK, 0, 0, 0, 0, 0, NULL); if (err && err != -EIO) { pr_warn_once("failed to unmask CPU[%u]: %d\n", @@ -336,6 +335,7 @@ int sdei_unmask_local_cpu(void) static void _ipi_unmask_cpu(void *ignored) { + WARN_ON_ONCE(preemptible()); sdei_unmask_local_cpu(); } @@ -343,6 +343,8 @@ static void _ipi_private_reset(void *ignored) { int err; + WARN_ON_ONCE(preemptible()); + err = invoke_sdei_fn(SDEI_1_0_FN_SDEI_PRIVATE_RESET, 0, 0, 0, 0, 0, NULL); if (err && err != -EIO) @@ -389,8 +391,6 @@ static void _local_event_enable(void *data) int err; struct sdei_crosscall_args *arg = data; - WARN_ON_ONCE(preemptible()); - err = sdei_api_event_enable(arg->event->event_num); sdei_cross_call_return(arg, err); @@ -479,8 +479,6 @@ static void _local_event_unregister(void *data) int err; struct sdei_crosscall_args *arg = data; - WARN_ON_ONCE(preemptible()); - err = sdei_api_event_unregister(arg->event->event_num); sdei_cross_call_return(arg, err); @@ -561,8 +559,6 @@ static void _local_event_register(void *data) struct sdei_registered_event *reg; struct sdei_crosscall_args *arg = data; - WARN_ON(preemptible()); - reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id()); err = sdei_api_event_register(arg->event->event_num, sdei_entry_point, reg, 0, 0); @@ -717,6 +713,8 @@ static int sdei_pm_notifier(struct notifier_block *nb, unsigned long action, { int rv; + WARN_ON_ONCE(preemptible()); + switch (action) { case CPU_PM_ENTER: rv = sdei_mask_local_cpu(); @@ -765,7 +763,7 @@ static int sdei_device_freeze(struct device *dev) int err; /* unregister private events */ - cpuhp_remove_state(CPUHP_AP_ARM_SDEI_STARTING); + cpuhp_remove_state(sdei_entry_point); err = sdei_unregister_shared(); if (err) @@ -786,12 +784,15 @@ static int sdei_device_thaw(struct device *dev) return err; } - err = cpuhp_setup_state(CPUHP_AP_ARM_SDEI_STARTING, "SDEI", + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "SDEI", &sdei_cpuhp_up, &sdei_cpuhp_down); - if (err) + if (err < 0) { pr_warn("Failed to re-register CPU hotplug notifier...\n"); + return err; + } - return err; + sdei_hp_state = err; + return 0; } static int sdei_device_restore(struct device *dev) @@ -823,7 +824,7 @@ static int sdei_reboot_notifier(struct notifier_block *nb, unsigned long action, * We are going to reset the interface, after this there is no point * doing work when we take CPUs offline. */ - cpuhp_remove_state(CPUHP_AP_ARM_SDEI_STARTING); + cpuhp_remove_state(sdei_hp_state); sdei_platform_reset(); @@ -1003,13 +1004,15 @@ static int sdei_probe(struct platform_device *pdev) goto remove_cpupm; } - err = cpuhp_setup_state(CPUHP_AP_ARM_SDEI_STARTING, "SDEI", + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "SDEI", &sdei_cpuhp_up, &sdei_cpuhp_down); - if (err) { + if (err < 0) { pr_warn("Failed to register CPU hotplug notifier...\n"); goto remove_reboot; } + sdei_hp_state = err; + return 0; remove_reboot: diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index f61447913db9..2be2091c2b44 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -161,7 +161,6 @@ enum cpuhp_state { CPUHP_AP_PERF_X86_CSTATE_STARTING, CPUHP_AP_PERF_XTENSA_STARTING, CPUHP_AP_MIPS_OP_LOONGSON3_STARTING, - CPUHP_AP_ARM_SDEI_STARTING, CPUHP_AP_ARM_VFP_STARTING, CPUHP_AP_ARM64_DEBUG_MONITORS_STARTING, CPUHP_AP_PERF_ARM_HW_BREAKPOINT_STARTING, -- cgit 1.4.1 From 86a159fd5bdb01ec34b160cfda1a313b616d9302 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 24 Mar 2023 21:26:27 +0100 Subject: ACPI: EC: Fix oops when removing custom query handlers [ Upstream commit e5b492c6bb900fcf9722e05f4a10924410e170c1 ] When removing custom query handlers, the handler might still be used inside the EC query workqueue, causing a kernel oops if the module holding the callback function was already unloaded. Fix this by flushing the EC query workqueue when removing custom query handlers. Tested on a Acer Travelmate 4002WLMi Signed-off-by: Armin Wolf Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/ec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 9751b84c1b22..ee4c812c8f6c 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1121,6 +1121,7 @@ static void acpi_ec_remove_query_handlers(struct acpi_ec *ec, void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) { acpi_ec_remove_query_handlers(ec, false, query_bit); + flush_workqueue(ec_query_wq); } EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); -- cgit 1.4.1 From 3dc61a19c924632b0bc8ec83593e072739b70645 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Thu, 16 Mar 2023 15:57:30 -0400 Subject: drm/amd/display: fixed dcn30+ underflow issue [ Upstream commit 37403ced9f2873fab7f39ab4ac963bbb33fb0bc0 ] [Why] Observing underflow on dcn30+ system config at 4k144hz [How] We set the UCLK hardmax on AC/DC switch if softmax is enabled and also on boot. While booting up the UCLK Hardmax is set to softmax before the init sequence and the init sequence resets the hardmax to UCLK max which enables P-state switching. Just added a conditional check to avoid setting hardmax on init. Reviewed-by: Alvin Lee Reviewed-by: Martin Leung Acked-by: Qingqing Zhuo Signed-off-by: Ayush Gupta Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c | 3 ++- drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hwseq.c | 2 +- drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c index c20e9f76f021..a1b312483d7f 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c @@ -629,7 +629,8 @@ void dcn30_init_hw(struct dc *dc) if (dc->clk_mgr->funcs->notify_wm_ranges) dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); - if (dc->clk_mgr->funcs->set_hard_max_memclk) + //if softmax is enabled then hardmax will be set by a different call + if (dc->clk_mgr->funcs->set_hard_max_memclk && !dc->clk_mgr->dc_mode_softmax_enabled) dc->clk_mgr->funcs->set_hard_max_memclk(dc->clk_mgr); if (dc->res_pool->hubbub->funcs->force_pstate_change_control) diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hwseq.c index bdf101547484..d4ee533deff3 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hwseq.c @@ -284,7 +284,7 @@ void dcn31_init_hw(struct dc *dc) if (dc->clk_mgr->funcs->notify_wm_ranges) dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); - if (dc->clk_mgr->funcs->set_hard_max_memclk) + if (dc->clk_mgr->funcs->set_hard_max_memclk && !dc->clk_mgr->dc_mode_softmax_enabled) dc->clk_mgr->funcs->set_hard_max_memclk(dc->clk_mgr); if (dc->res_pool->hubbub->funcs->force_pstate_change_control) diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c index e9188bce62e0..2f4afe40f3e6 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c @@ -970,7 +970,7 @@ void dcn32_init_hw(struct dc *dc) if (dc->clk_mgr->funcs->notify_wm_ranges) dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); - if (dc->clk_mgr->funcs->set_hard_max_memclk) + if (dc->clk_mgr->funcs->set_hard_max_memclk && !dc->clk_mgr->dc_mode_softmax_enabled) dc->clk_mgr->funcs->set_hard_max_memclk(dc->clk_mgr); if (dc->res_pool->hubbub->funcs->force_pstate_change_control) -- cgit 1.4.1 From a7f9c14aced359c065f2f3022de6de93e85da92c Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Fri, 31 Mar 2023 18:06:34 +0200 Subject: remoteproc: stm32_rproc: Add mutex protection for workqueue [ Upstream commit 35bdafda40cc343ad2ba2cce105eba03a70241cc ] The workqueue may execute late even after remoteproc is stopped or stopping, some resources (rpmsg device and endpoint) have been released in rproc_stop_subdevices(), then rproc_vq_interrupt() accessing these resources will cause kernel dump. Call trace: virtqueue_add_inbuf virtqueue_add_inbuf rpmsg_recv_single rpmsg_recv_done vring_interrupt stm32_rproc_mb_vq_work process_one_work worker_thread kthread Suggested-by: Mathieu Poirier Signed-off-by: Arnaud Pouliquen Link: https://lore.kernel.org/r/20230331160634.3113031-1-arnaud.pouliquen@foss.st.com Signed-off-by: Mathieu Poirier Signed-off-by: Sasha Levin --- drivers/remoteproc/stm32_rproc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 23c1690b8d73..8746cbb1f168 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -291,8 +291,16 @@ static void stm32_rproc_mb_vq_work(struct work_struct *work) struct stm32_mbox *mb = container_of(work, struct stm32_mbox, vq_work); struct rproc *rproc = dev_get_drvdata(mb->client.dev); + mutex_lock(&rproc->lock); + + if (rproc->state != RPROC_RUNNING) + goto unlock_mutex; + if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) dev_dbg(&rproc->dev, "no message found in vq%d\n", mb->vq_id); + +unlock_mutex: + mutex_unlock(&rproc->lock); } static void stm32_rproc_mb_callback(struct mbox_client *cl, void *data) -- cgit 1.4.1 From a6eb3aa0ec5b5078d02c48aad29f80c0a1a17974 Mon Sep 17 00:00:00 2001 From: Nur Hussein Date: Thu, 6 Apr 2023 04:25:59 +0800 Subject: drm/tegra: Avoid potential 32-bit integer overflow [ Upstream commit 2429b3c529da29d4277d519bd66d034842dcd70c ] In tegra_sor_compute_config(), the 32-bit value mode->clock is multiplied by 1000, and assigned to the u64 variable pclk. We can avoid a potential 32-bit integer overflow by casting mode->clock to u64 before we do the arithmetic and assignment. Signed-off-by: Nur Hussein Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/sor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 8af632740673..77723d5f1d3f 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1153,7 +1153,7 @@ static int tegra_sor_compute_config(struct tegra_sor *sor, struct drm_dp_link *link) { const u64 f = 100000, link_rate = link->rate * 1000; - const u64 pclk = mode->clock * 1000; + const u64 pclk = (u64)mode->clock * 1000; u64 input, output, watermark, num; struct tegra_sor_params params; u32 num_syms_per_line; -- cgit 1.4.1 From b1db73e27f0be198513399b181785caf98a1c4ac Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 26 Jan 2023 17:09:12 -0800 Subject: drm/msm/dp: Clean up handling of DP AUX interrupts [ Upstream commit b20566cdef05cd40d95f10869d2a7646f48b1bbe ] The DP AUX interrupt handling was a bit of a mess. * There were two functions (one for "native" transfers and one for "i2c" transfers) that were quite similar. It was hard to say how many of the differences between the two functions were on purpose and how many of them were just an accident of how they were coded. * Each function sometimes used "else if" to test for error bits and sometimes didn't and again it was hard to say if this was on purpose or just an accident. * The two functions wouldn't notice whether "unknown" bits were set. For instance, there seems to be a bit "DP_INTR_PLL_UNLOCKED" and if it was set there would be no indication. * The two functions wouldn't notice if more than one error was set. Let's fix this by being more consistent / explicit about what we're doing. By design this could cause different handling for AUX transfers, though I'm not actually aware of any bug fixed as a result of this patch (this patch was created because we simply noticed how odd the old code was by code inspection). Specific notes here: 1. In the old native transfer case if we got "done + wrong address" we'd ignore the "wrong address" (because of the "else if"). Now we won't. 2. In the old native transfer case if we got "done + timeout" we'd ignore the "timeout" (because of the "else if"). Now we won't. 3. In the old native transfer case we'd see "nack_defer" and translate it to the error number for "nack". This differed from the i2c transfer case where "nack_defer" was given the error number for "nack_defer". This 100% can't matter because the only user of this error number treats "nack defer" the same as "nack", so it's clear that the difference between the "native" and "i2c" was pointless here. 4. In the old i2c transfer case if we got "done" plus any error besides "nack" or "defer" then we'd ignore the error. Now we don't. 5. If there is more than one error signaled by the hardware it's possible that we'll report a different one than we used to. I don't know if this matters. If someone is aware of a case this matters we should document it and change the code to make it explicit. 6. One quirk we keep (I don't know if this is important) is that in the i2c transfer case if we see "done + defer" we report that as a "nack". That seemed too intentional in the old code to just drop. After this change we will add extra logging, including: * A warning if we see more than one error bit set. * A warning if we see an unexpected interrupt. * A warning if we get an AUX transfer interrupt when shouldn't. It actually turns out that as a result of this change then at boot we sometimes see an error: [drm:dp_aux_isr] *ERROR* Unexpected DP AUX IRQ 0x01000000 when not busy That means that, during init, we are seeing DP_INTR_PLL_UNLOCKED. For now I'm going to say that leaving this error reported in the logs is OK-ish and hopefully it will encourage someone to track down what's going on at init time. One last note here is that this change renames one of the interrupt bits. The bit named "i2c done" clearly was used for native transfers being done too, so I renamed it to indicate this. Signed-off-by: Douglas Anderson Tested-by: Kuogee Hsieh Reviewed-by: Kuogee Hsieh Patchwork: https://patchwork.freedesktop.org/patch/520658/ Link: https://lore.kernel.org/r/20230126170745.v2.1.I90ffed3ddd21e818ae534f820cb4d6d8638859ab@changeid Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_aux.c | 80 ++++++++++++++++--------------------- drivers/gpu/drm/msm/dp/dp_catalog.c | 2 +- drivers/gpu/drm/msm/dp/dp_catalog.h | 2 +- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c index cc3efed593aa..84f9e3e5f964 100644 --- a/drivers/gpu/drm/msm/dp/dp_aux.c +++ b/drivers/gpu/drm/msm/dp/dp_aux.c @@ -162,47 +162,6 @@ static ssize_t dp_aux_cmd_fifo_rx(struct dp_aux_private *aux, return i; } -static void dp_aux_native_handler(struct dp_aux_private *aux, u32 isr) -{ - if (isr & DP_INTR_AUX_I2C_DONE) - aux->aux_error_num = DP_AUX_ERR_NONE; - else if (isr & DP_INTR_WRONG_ADDR) - aux->aux_error_num = DP_AUX_ERR_ADDR; - else if (isr & DP_INTR_TIMEOUT) - aux->aux_error_num = DP_AUX_ERR_TOUT; - if (isr & DP_INTR_NACK_DEFER) - aux->aux_error_num = DP_AUX_ERR_NACK; - if (isr & DP_INTR_AUX_ERROR) { - aux->aux_error_num = DP_AUX_ERR_PHY; - dp_catalog_aux_clear_hw_interrupts(aux->catalog); - } -} - -static void dp_aux_i2c_handler(struct dp_aux_private *aux, u32 isr) -{ - if (isr & DP_INTR_AUX_I2C_DONE) { - if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER)) - aux->aux_error_num = DP_AUX_ERR_NACK; - else - aux->aux_error_num = DP_AUX_ERR_NONE; - } else { - if (isr & DP_INTR_WRONG_ADDR) - aux->aux_error_num = DP_AUX_ERR_ADDR; - else if (isr & DP_INTR_TIMEOUT) - aux->aux_error_num = DP_AUX_ERR_TOUT; - if (isr & DP_INTR_NACK_DEFER) - aux->aux_error_num = DP_AUX_ERR_NACK_DEFER; - if (isr & DP_INTR_I2C_NACK) - aux->aux_error_num = DP_AUX_ERR_NACK; - if (isr & DP_INTR_I2C_DEFER) - aux->aux_error_num = DP_AUX_ERR_DEFER; - if (isr & DP_INTR_AUX_ERROR) { - aux->aux_error_num = DP_AUX_ERR_PHY; - dp_catalog_aux_clear_hw_interrupts(aux->catalog); - } - } -} - static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux, struct drm_dp_aux_msg *input_msg) { @@ -427,13 +386,42 @@ void dp_aux_isr(struct drm_dp_aux *dp_aux) if (!isr) return; - if (!aux->cmd_busy) + if (!aux->cmd_busy) { + DRM_ERROR("Unexpected DP AUX IRQ %#010x when not busy\n", isr); return; + } - if (aux->native) - dp_aux_native_handler(aux, isr); - else - dp_aux_i2c_handler(aux, isr); + /* + * The logic below assumes only one error bit is set (other than "done" + * which can apparently be set at the same time as some of the other + * bits). Warn if more than one get set so we know we need to improve + * the logic. + */ + if (hweight32(isr & ~DP_INTR_AUX_XFER_DONE) > 1) + DRM_WARN("Some DP AUX interrupts unhandled: %#010x\n", isr); + + if (isr & DP_INTR_AUX_ERROR) { + aux->aux_error_num = DP_AUX_ERR_PHY; + dp_catalog_aux_clear_hw_interrupts(aux->catalog); + } else if (isr & DP_INTR_NACK_DEFER) { + aux->aux_error_num = DP_AUX_ERR_NACK_DEFER; + } else if (isr & DP_INTR_WRONG_ADDR) { + aux->aux_error_num = DP_AUX_ERR_ADDR; + } else if (isr & DP_INTR_TIMEOUT) { + aux->aux_error_num = DP_AUX_ERR_TOUT; + } else if (!aux->native && (isr & DP_INTR_I2C_NACK)) { + aux->aux_error_num = DP_AUX_ERR_NACK; + } else if (!aux->native && (isr & DP_INTR_I2C_DEFER)) { + if (isr & DP_INTR_AUX_XFER_DONE) + aux->aux_error_num = DP_AUX_ERR_NACK; + else + aux->aux_error_num = DP_AUX_ERR_DEFER; + } else if (isr & DP_INTR_AUX_XFER_DONE) { + aux->aux_error_num = DP_AUX_ERR_NONE; + } else { + DRM_WARN("Unexpected interrupt: %#010x\n", isr); + return; + } complete(&aux->comp); } diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index 676279d0ca8d..421391755427 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -27,7 +27,7 @@ #define DP_INTF_CONFIG_DATABUS_WIDEN BIT(4) #define DP_INTERRUPT_STATUS1 \ - (DP_INTR_AUX_I2C_DONE| \ + (DP_INTR_AUX_XFER_DONE| \ DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \ DP_INTR_NACK_DEFER | DP_INTR_WRONG_DATA_CNT | \ DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER | \ diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h index 1f717f45c115..f36b7b372a06 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.h +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h @@ -13,7 +13,7 @@ /* interrupts */ #define DP_INTR_HPD BIT(0) -#define DP_INTR_AUX_I2C_DONE BIT(3) +#define DP_INTR_AUX_XFER_DONE BIT(3) #define DP_INTR_WRONG_ADDR BIT(6) #define DP_INTR_TIMEOUT BIT(9) #define DP_INTR_NACK_DEFER BIT(12) -- cgit 1.4.1 From 8c4a7163b7f1495e3cc58bec7a4100de6612cde9 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 5 Apr 2023 15:42:43 +0200 Subject: ACPICA: Avoid undefined behavior: applying zero offset to null pointer [ Upstream commit 05bb0167c80b8f93c6a4e0451b7da9b96db990c2 ] ACPICA commit 770653e3ba67c30a629ca7d12e352d83c2541b1e Before this change we see the following UBSAN stack trace in Fuchsia: #0 0x000021e4213b3302 in acpi_ds_init_aml_walk(struct acpi_walk_state*, union acpi_parse_object*, struct acpi_namespace_node*, u8*, u32, struct acpi_evaluate_info*, u8) ../../third_party/acpica/source/components/dispatcher/dswstate.c:682 +0x233302 #1.2 0x000020d0f660777f in ubsan_get_stack_trace() compiler-rt/lib/ubsan/ubsan_diag.cpp:41 +0x3d77f #1.1 0x000020d0f660777f in maybe_print_stack_trace() compiler-rt/lib/ubsan/ubsan_diag.cpp:51 +0x3d77f #1 0x000020d0f660777f in ~scoped_report() compiler-rt/lib/ubsan/ubsan_diag.cpp:387 +0x3d77f #2 0x000020d0f660b96d in handlepointer_overflow_impl() compiler-rt/lib/ubsan/ubsan_handlers.cpp:809 +0x4196d #3 0x000020d0f660b50d in compiler-rt/lib/ubsan/ubsan_handlers.cpp:815 +0x4150d #4 0x000021e4213b3302 in acpi_ds_init_aml_walk(struct acpi_walk_state*, union acpi_parse_object*, struct acpi_namespace_node*, u8*, u32, struct acpi_evaluate_info*, u8) ../../third_party/acpica/source/components/dispatcher/dswstate.c:682 +0x233302 #5 0x000021e4213e2369 in acpi_ds_call_control_method(struct acpi_thread_state*, struct acpi_walk_state*, union acpi_parse_object*) ../../third_party/acpica/source/components/dispatcher/dsmethod.c:605 +0x262369 #6 0x000021e421437fac in acpi_ps_parse_aml(struct acpi_walk_state*) ../../third_party/acpica/source/components/parser/psparse.c:550 +0x2b7fac #7 0x000021e4214464d2 in acpi_ps_execute_method(struct acpi_evaluate_info*) ../../third_party/acpica/source/components/parser/psxface.c:244 +0x2c64d2 #8 0x000021e4213aa052 in acpi_ns_evaluate(struct acpi_evaluate_info*) ../../third_party/acpica/source/components/namespace/nseval.c:250 +0x22a052 #9 0x000021e421413dd8 in acpi_ns_init_one_device(acpi_handle, u32, void*, void**) ../../third_party/acpica/source/components/namespace/nsinit.c:735 +0x293dd8 #10 0x000021e421429e98 in acpi_ns_walk_namespace(acpi_object_type, acpi_handle, u32, u32, acpi_walk_callback, acpi_walk_callback, void*, void**) ../../third_party/acpica/source/components/namespace/nswalk.c:298 +0x2a9e98 #11 0x000021e4214131ac in acpi_ns_initialize_devices(u32) ../../third_party/acpica/source/components/namespace/nsinit.c:268 +0x2931ac #12 0x000021e42147c40d in acpi_initialize_objects(u32) ../../third_party/acpica/source/components/utilities/utxfinit.c:304 +0x2fc40d #13 0x000021e42126d603 in acpi::acpi_impl::initialize_acpi(acpi::acpi_impl*) ../../src/devices/board/lib/acpi/acpi-impl.cc:224 +0xed603 Add a simple check that avoids incrementing a pointer by zero, but otherwise behaves as before. Note that our findings are against ACPICA 20221020, but the same code exists on master. Link: https://github.com/acpica/acpica/commit/770653e3 Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpica/dswstate.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index 0aa735d3b93c..77076da2029d 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -576,9 +576,14 @@ acpi_ds_init_aml_walk(struct acpi_walk_state *walk_state, ACPI_FUNCTION_TRACE(ds_init_aml_walk); walk_state->parser_state.aml = - walk_state->parser_state.aml_start = aml_start; - walk_state->parser_state.aml_end = - walk_state->parser_state.pkg_end = aml_start + aml_length; + walk_state->parser_state.aml_start = + walk_state->parser_state.aml_end = + walk_state->parser_state.pkg_end = aml_start; + /* Avoid undefined behavior: applying zero offset to null pointer */ + if (aml_length != 0) { + walk_state->parser_state.aml_end += aml_length; + walk_state->parser_state.pkg_end += aml_length; + } /* The next_op of the next_walk will be the beginning of the method */ -- cgit 1.4.1 From fee6133490091492dc66bcf71479bd53bd17a7d2 Mon Sep 17 00:00:00 2001 From: void0red <30990023+void0red@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:57:57 +0200 Subject: ACPICA: ACPICA: check null return of ACPI_ALLOCATE_ZEROED in acpi_db_display_objects [ Upstream commit ae5a0eccc85fc960834dd66e3befc2728284b86c ] ACPICA commit 0d5f467d6a0ba852ea3aad68663cbcbd43300fd4 ACPI_ALLOCATE_ZEROED may fails, object_info might be null and will cause null pointer dereference later. Link: https://github.com/acpica/acpica/commit/0d5f467d Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpica/dbnames.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/acpica/dbnames.c b/drivers/acpi/acpica/dbnames.c index 3615e1a6efd8..b91155ea9c34 100644 --- a/drivers/acpi/acpica/dbnames.c +++ b/drivers/acpi/acpica/dbnames.c @@ -652,6 +652,9 @@ acpi_status acpi_db_display_objects(char *obj_type_arg, char *display_count_arg) object_info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_object_info)); + if (!object_info) + return (AE_NO_MEMORY); + /* Walk the namespace from the root */ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, -- cgit 1.4.1 From 57b5a56cecbe61caed6eb75629de3b262176ee35 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 6 Apr 2023 14:55:45 +0200 Subject: arm64: dts: qcom: sdm845-polaris: Drop inexistent properties [ Upstream commit fbc3a1df2866608ca43e7e6d602f66208a5afd88 ] Drop the qcom,snoc-host-cap-skip-quirk that was never introduced to solve schema warnings. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230406-topic-ath10k_bindings-v3-2-00895afc7764@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts index f98259489679..74c6832e0598 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts @@ -731,8 +731,6 @@ vdd-1.3-rfa-supply = <&vreg_l17a_1p3>; vdd-3.3-ch0-supply = <&vreg_l25a_3p3>; vdd-3.3-ch1-supply = <&vreg_l23a_3p3>; - - qcom,snoc-host-cap-skip-quirk; status = "okay"; }; -- cgit 1.4.1 From 86ba4f7b9f949e4c4bcb425f2a1ce490fea30df0 Mon Sep 17 00:00:00 2001 From: Shanker Donthineni Date: Sat, 18 Mar 2023 21:43:14 -0500 Subject: irqchip/gicv3: Workaround for NVIDIA erratum T241-FABRIC-4 [ Upstream commit 35727af2b15d98a2dd2811d631d3a3886111312e ] The T241 platform suffers from the T241-FABRIC-4 erratum which causes unexpected behavior in the GIC when multiple transactions are received simultaneously from different sources. This hardware issue impacts NVIDIA server platforms that use more than two T241 chips interconnected. Each chip has support for 320 {E}SPIs. This issue occurs when multiple packets from different GICs are incorrectly interleaved at the target chip. The erratum text below specifies exactly what can cause multiple transfer packets susceptible to interleaving and GIC state corruption. GIC state corruption can lead to a range of problems, including kernel panics, and unexpected behavior. >From the erratum text: "In some cases, inter-socket AXI4 Stream packets with multiple transfers, may be interleaved by the fabric when presented to ARM Generic Interrupt Controller. GIC expects all transfers of a packet to be delivered without any interleaving. The following GICv3 commands may result in multiple transfer packets over inter-socket AXI4 Stream interface: - Register reads from GICD_I* and GICD_N* - Register writes to 64-bit GICD registers other than GICD_IROUTERn* - ITS command MOVALL Multiple commands in GICv4+ utilize multiple transfer packets, including VMOVP, VMOVI, VMAPP, and 64-bit register accesses." This issue impacts system configurations with more than 2 sockets, that require multi-transfer packets to be sent over inter-socket AXI4 Stream interface between GIC instances on different sockets. GICv4 cannot be supported. GICv3 SW model can only be supported with the workaround. Single and Dual socket configurations are not impacted by this issue and support GICv3 and GICv4." Link: https://developer.nvidia.com/docs/t241-fabric-4/nvidia-t241-fabric-4-errata.pdf Writing to the chip alias region of the GICD_In{E} registers except GICD_ICENABLERn has an equivalent effect as writing to the global distributor. The SPI interrupt deactivate path is not impacted by the erratum. To fix this problem, implement a workaround that ensures read accesses to the GICD_In{E} registers are directed to the chip that owns the SPI, and disable GICv4.x features. To simplify code changes, the gic_configure_irq() function uses the same alias region for both read and write operations to GICD_ICFGR. Co-developed-by: Vikram Sethi Signed-off-by: Vikram Sethi Signed-off-by: Shanker Donthineni Acked-by: Sudeep Holla (for SMCCC/SOC ID bits) Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230319024314.3540573-2-sdonthineni@nvidia.com Signed-off-by: Sasha Levin --- Documentation/arm64/silicon-errata.rst | 2 + drivers/firmware/smccc/smccc.c | 26 ++++++++ drivers/firmware/smccc/soc_id.c | 28 ++------ drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3.c | 115 +++++++++++++++++++++++++++++---- include/linux/arm-smccc.h | 18 ++++++ 6 files changed, 156 insertions(+), 34 deletions(-) diff --git a/Documentation/arm64/silicon-errata.rst b/Documentation/arm64/silicon-errata.rst index 808ade4cc008..55492fea4427 100644 --- a/Documentation/arm64/silicon-errata.rst +++ b/Documentation/arm64/silicon-errata.rst @@ -170,6 +170,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | NVIDIA | Carmel Core | N/A | NVIDIA_CARMEL_CNP_ERRATUM | +----------------+-----------------+-----------------+-----------------------------+ +| NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A | ++----------------+-----------------+-----------------+-----------------------------+ +----------------+-----------------+-----------------+-----------------------------+ | Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 | +----------------+-----------------+-----------------+-----------------------------+ diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c index 60ccf3e90d7d..db818f9dcb8e 100644 --- a/drivers/firmware/smccc/smccc.c +++ b/drivers/firmware/smccc/smccc.c @@ -17,9 +17,13 @@ static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE; bool __ro_after_init smccc_trng_available = false; u64 __ro_after_init smccc_has_sve_hint = false; +s32 __ro_after_init smccc_soc_id_version = SMCCC_RET_NOT_SUPPORTED; +s32 __ro_after_init smccc_soc_id_revision = SMCCC_RET_NOT_SUPPORTED; void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit) { + struct arm_smccc_res res; + smccc_version = version; smccc_conduit = conduit; @@ -27,6 +31,18 @@ void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit) if (IS_ENABLED(CONFIG_ARM64_SVE) && smccc_version >= ARM_SMCCC_VERSION_1_3) smccc_has_sve_hint = true; + + if ((smccc_version >= ARM_SMCCC_VERSION_1_2) && + (smccc_conduit != SMCCC_CONDUIT_NONE)) { + arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, + ARM_SMCCC_ARCH_SOC_ID, &res); + if ((s32)res.a0 >= 0) { + arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res); + smccc_soc_id_version = (s32)res.a0; + arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res); + smccc_soc_id_revision = (s32)res.a0; + } + } } enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void) @@ -44,6 +60,16 @@ u32 arm_smccc_get_version(void) } EXPORT_SYMBOL_GPL(arm_smccc_get_version); +s32 arm_smccc_get_soc_id_version(void) +{ + return smccc_soc_id_version; +} + +s32 arm_smccc_get_soc_id_revision(void) +{ + return smccc_soc_id_revision; +} + static int __init smccc_devices_init(void) { struct platform_device *pdev; diff --git a/drivers/firmware/smccc/soc_id.c b/drivers/firmware/smccc/soc_id.c index dd7c3d5e8b0b..890eb454599a 100644 --- a/drivers/firmware/smccc/soc_id.c +++ b/drivers/firmware/smccc/soc_id.c @@ -42,41 +42,23 @@ static int __init smccc_soc_init(void) if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2) return 0; - if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) { - pr_err("%s: invalid SMCCC conduit\n", __func__); - return -EOPNOTSUPP; - } - - arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, - ARM_SMCCC_ARCH_SOC_ID, &res); - - if ((int)res.a0 == SMCCC_RET_NOT_SUPPORTED) { + soc_id_version = arm_smccc_get_soc_id_version(); + if (soc_id_version == SMCCC_RET_NOT_SUPPORTED) { pr_info("ARCH_SOC_ID not implemented, skipping ....\n"); return 0; } - if ((int)res.a0 < 0) { - pr_info("ARCH_FEATURES(ARCH_SOC_ID) returned error: %lx\n", - res.a0); - return -EINVAL; - } - - arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res); - if ((int)res.a0 < 0) { + if (soc_id_version < 0) { pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0); return -EINVAL; } - soc_id_version = res.a0; - - arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res); - if ((int)res.a0 < 0) { + soc_id_rev = arm_smccc_get_soc_id_revision(); + if (soc_id_rev < 0) { pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0); return -EINVAL; } - soc_id_rev = res.a0; - soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return -ENOMEM; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 7ef9f5e696d3..a29a426e4eed 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -35,6 +35,7 @@ config ARM_GIC_V3 select IRQ_DOMAIN_HIERARCHY select PARTITION_PERCPU select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP + select HAVE_ARM_SMCCC_DISCOVERY config ARM_GIC_V3_ITS bool diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 34d58567b78d..2182f87d2d12 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #include #include @@ -46,6 +49,7 @@ struct redist_region { struct gic_chip_data { struct fwnode_handle *fwnode; + phys_addr_t dist_phys_base; void __iomem *dist_base; struct redist_region *redist_regions; struct rdists rdists; @@ -58,6 +62,10 @@ struct gic_chip_data { struct partition_desc **ppi_descs; }; +#define T241_CHIPS_MAX 4 +static void __iomem *t241_dist_base_alias[T241_CHIPS_MAX] __read_mostly; +static DEFINE_STATIC_KEY_FALSE(gic_nvidia_t241_erratum); + static struct gic_chip_data gic_data __read_mostly; static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key); @@ -187,6 +195,39 @@ static inline bool gic_irq_in_rdist(struct irq_data *d) } } +static inline void __iomem *gic_dist_base_alias(struct irq_data *d) +{ + if (static_branch_unlikely(&gic_nvidia_t241_erratum)) { + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 chip; + + /* + * For the erratum T241-FABRIC-4, read accesses to GICD_In{E} + * registers are directed to the chip that owns the SPI. The + * the alias region can also be used for writes to the + * GICD_In{E} except GICD_ICENABLERn. Each chip has support + * for 320 {E}SPIs. Mappings for all 4 chips: + * Chip0 = 32-351 + * Chip1 = 352-671 + * Chip2 = 672-991 + * Chip3 = 4096-4415 + */ + switch (__get_intid_range(hwirq)) { + case SPI_RANGE: + chip = (hwirq - 32) / 320; + break; + case ESPI_RANGE: + chip = 3; + break; + default: + unreachable(); + } + return t241_dist_base_alias[chip]; + } + + return gic_data.dist_base; +} + static inline void __iomem *gic_dist_base(struct irq_data *d) { switch (get_intid_range(d)) { @@ -345,7 +386,7 @@ static int gic_peek_irq(struct irq_data *d, u32 offset) if (gic_irq_in_rdist(d)) base = gic_data_rdist_sgi_base(); else - base = gic_data.dist_base; + base = gic_dist_base_alias(d); return !!(readl_relaxed(base + offset + (index / 32) * 4) & mask); } @@ -596,7 +637,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) if (gic_irq_in_rdist(d)) base = gic_data_rdist_sgi_base(); else - base = gic_data.dist_base; + base = gic_dist_base_alias(d); offset = convert_offset_index(d, GICD_ICFGR, &index); @@ -1718,6 +1759,43 @@ static bool gic_enable_quirk_hip06_07(void *data) return false; } +#define T241_CHIPN_MASK GENMASK_ULL(45, 44) +#define T241_CHIP_GICDA_OFFSET 0x1580000 +#define SMCCC_SOC_ID_T241 0x036b0241 + +static bool gic_enable_quirk_nvidia_t241(void *data) +{ + s32 soc_id = arm_smccc_get_soc_id_version(); + unsigned long chip_bmask = 0; + phys_addr_t phys; + u32 i; + + /* Check JEP106 code for NVIDIA T241 chip (036b:0241) */ + if ((soc_id < 0) || (soc_id != SMCCC_SOC_ID_T241)) + return false; + + /* Find the chips based on GICR regions PHYS addr */ + for (i = 0; i < gic_data.nr_redist_regions; i++) { + chip_bmask |= BIT(FIELD_GET(T241_CHIPN_MASK, + (u64)gic_data.redist_regions[i].phys_base)); + } + + if (hweight32(chip_bmask) < 3) + return false; + + /* Setup GICD alias regions */ + for (i = 0; i < ARRAY_SIZE(t241_dist_base_alias); i++) { + if (chip_bmask & BIT(i)) { + phys = gic_data.dist_phys_base + T241_CHIP_GICDA_OFFSET; + phys |= FIELD_PREP(T241_CHIPN_MASK, i); + t241_dist_base_alias[i] = ioremap(phys, SZ_64K); + WARN_ON_ONCE(!t241_dist_base_alias[i]); + } + } + static_branch_enable(&gic_nvidia_t241_erratum); + return true; +} + static const struct gic_quirk gic_quirks[] = { { .desc = "GICv3: Qualcomm MSM8996 broken firmware", @@ -1749,6 +1827,12 @@ static const struct gic_quirk gic_quirks[] = { .mask = 0xe8f00fff, .init = gic_enable_quirk_cavium_38539, }, + { + .desc = "GICv3: NVIDIA erratum T241-FABRIC-4", + .iidr = 0x0402043b, + .mask = 0xffffffff, + .init = gic_enable_quirk_nvidia_t241, + }, { } }; @@ -1816,7 +1900,8 @@ static void gic_enable_nmi_support(void) gic_chip.flags |= IRQCHIP_SUPPORTS_NMI; } -static int __init gic_init_bases(void __iomem *dist_base, +static int __init gic_init_bases(phys_addr_t dist_phys_base, + void __iomem *dist_base, struct redist_region *rdist_regs, u32 nr_redist_regions, u64 redist_stride, @@ -1832,6 +1917,7 @@ static int __init gic_init_bases(void __iomem *dist_base, pr_info("GIC: Using split EOI/Deactivate mode\n"); gic_data.fwnode = handle; + gic_data.dist_phys_base = dist_phys_base; gic_data.dist_base = dist_base; gic_data.redist_regions = rdist_regs; gic_data.nr_redist_regions = nr_redist_regions; @@ -1859,10 +1945,13 @@ static int __init gic_init_bases(void __iomem *dist_base, gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops, &gic_data); gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist)); - gic_data.rdists.has_rvpeid = true; - gic_data.rdists.has_vlpis = true; - gic_data.rdists.has_direct_lpi = true; - gic_data.rdists.has_vpend_valid_dirty = true; + if (!static_branch_unlikely(&gic_nvidia_t241_erratum)) { + /* Disable GICv4.x features for the erratum T241-FABRIC-4 */ + gic_data.rdists.has_rvpeid = true; + gic_data.rdists.has_vlpis = true; + gic_data.rdists.has_direct_lpi = true; + gic_data.rdists.has_vpend_valid_dirty = true; + } if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) { err = -ENOMEM; @@ -2068,6 +2157,7 @@ static void __iomem *gic_of_iomap(struct device_node *node, int idx, static int __init gic_of_init(struct device_node *node, struct device_node *parent) { + phys_addr_t dist_phys_base; void __iomem *dist_base; struct redist_region *rdist_regs; struct resource res; @@ -2081,6 +2171,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare return PTR_ERR(dist_base); } + dist_phys_base = res.start; + err = gic_validate_dist_version(dist_base); if (err) { pr_err("%pOF: no distributor detected, giving up\n", node); @@ -2112,8 +2204,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare gic_enable_of_quirks(node, gic_quirks, &gic_data); - err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions, - redist_stride, &node->fwnode); + err = gic_init_bases(dist_phys_base, dist_base, rdist_regs, + nr_redist_regions, redist_stride, &node->fwnode); if (err) goto out_unmap_rdist; @@ -2429,8 +2521,9 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) goto out_redist_unmap; } - err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs, - acpi_data.nr_redist_regions, 0, gsi_domain_handle); + err = gic_init_bases(dist->base_address, acpi_data.dist_base, + acpi_data.redist_regs, acpi_data.nr_redist_regions, + 0, gsi_domain_handle); if (err) goto out_fwhandle_free; diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 220c8c60e021..f196c19f8e55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -226,6 +226,24 @@ void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit); extern u64 smccc_has_sve_hint; +/** + * arm_smccc_get_soc_id_version() + * + * Returns the SOC ID version. + * + * When ARM_SMCCC_ARCH_SOC_ID is not present, returns SMCCC_RET_NOT_SUPPORTED. + */ +s32 arm_smccc_get_soc_id_version(void); + +/** + * arm_smccc_get_soc_id_revision() + * + * Returns the SOC ID revision. + * + * When ARM_SMCCC_ARCH_SOC_ID is not present, returns SMCCC_RET_NOT_SUPPORTED. + */ +s32 arm_smccc_get_soc_id_revision(void); + /** * struct arm_smccc_res - Result from SMC/HVC call * @a0-a3 result values from registers 0 to 3 -- cgit 1.4.1 From cf180afea303c01a64806705c9fa20422578b8b1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 4 Apr 2023 13:02:51 +0200 Subject: ACPI: video: Remove desktops without backlight DMI quirks [ Upstream commit abe4f5ae5efa6a63c7d5abfa07eb02bb56b4654e ] After the recent backlight changes acpi_video# backlight devices are only registered when explicitly requested from the cmdline, by DMI quirk or by the GPU driver. This means that we no longer get false-positive backlight control support advertised on desktop boards. Remove the 3 DMI quirks for desktop boards where the false-positive issue was fixed through quirks before. Note many more desktop boards were affected but we never build a full quirk list for this. Reviewed-by: Mario Limonciello Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/video_detect.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 0556c4720d3f..b6d429a2bcb6 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -130,12 +130,6 @@ static int video_detect_force_native(const struct dmi_system_id *d) return 0; } -static int video_detect_force_none(const struct dmi_system_id *d) -{ - acpi_backlight_dmi = acpi_backlight_none; - return 0; -} - static const struct dmi_system_id video_detect_dmi_table[] = { /* * Models which should use the vendor backlight interface, @@ -752,35 +746,6 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 15 3535"), }, }, - - /* - * Desktops which falsely report a backlight and which our heuristics - * for this do not catch. - */ - { - .callback = video_detect_force_none, - /* Dell OptiPlex 9020M */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 9020M"), - }, - }, - { - .callback = video_detect_force_none, - /* GIGABYTE GB-BXBT-2807 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "GB-BXBT-2807"), - }, - }, - { - .callback = video_detect_force_none, - /* MSI MS-7721 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "MSI"), - DMI_MATCH(DMI_PRODUCT_NAME, "MS-7721"), - }, - }, { }, }; -- cgit 1.4.1 From ec5f00a59aade7e2575fed8b703e2281da444c56 Mon Sep 17 00:00:00 2001 From: Paul Hsieh Date: Wed, 22 Mar 2023 17:46:31 +0800 Subject: drm/amd/display: Correct DML calculation to follow HW SPEC [ Upstream commit 385c3e4c29e1d4ce8f68687a8c84621e4c0e0416 ] [Why] In 2560x1600@240p eDP panel, driver use lowest voltage level to play 1080p video cause underflow. According to HW SPEC, the senario should use high voltage level. [How] ChromaPre value is zero when bandwidth validation. Correct ChromaPre calculation. Reviewed-by: Nicholas Kazlauskas Reviewed-by: Jun Lei Acked-by: Qingqing Zhuo Signed-off-by: Paul Hsieh Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c | 2 +- drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c | 2 +- drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c | 2 +- drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c index 49da8119b28e..861f32b3248e 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c @@ -4866,7 +4866,7 @@ void dml30_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l v->DETBufferSizeCThisState[k], &v->UrgentBurstFactorCursorPre[k], &v->UrgentBurstFactorLumaPre[k], - &v->UrgentBurstFactorChroma[k], + &v->UrgentBurstFactorChromaPre[k], &v->NoUrgentLatencyHidingPre[k]); } diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c index 77094035a26a..cf8f3d690fa6 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c @@ -5189,7 +5189,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l v->DETBufferSizeCThisState[k], &v->UrgentBurstFactorCursorPre[k], &v->UrgentBurstFactorLumaPre[k], - &v->UrgentBurstFactorChroma[k], + &v->UrgentBurstFactorChromaPre[k], &v->NotUrgentLatencyHidingPre[k]); } diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c index 69516cfdbc64..4998b211ccac 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c @@ -5286,7 +5286,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ v->DETBufferSizeCThisState[k], &v->UrgentBurstFactorCursorPre[k], &v->UrgentBurstFactorLumaPre[k], - &v->UrgentBurstFactorChroma[k], + &v->UrgentBurstFactorChromaPre[k], &v->NotUrgentLatencyHidingPre[k]); } diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c index e02e9d4b04a9..2bb768413c92 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c @@ -3333,7 +3333,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l /* Output */ &mode_lib->vba.UrgentBurstFactorCursorPre[k], &mode_lib->vba.UrgentBurstFactorLumaPre[k], - &mode_lib->vba.UrgentBurstFactorChroma[k], + &mode_lib->vba.UrgentBurstFactorChromaPre[k], &mode_lib->vba.NotUrgentLatencyHidingPre[k]); } -- cgit 1.4.1 From 5675ecd2e0b00a4318ba1db1a1234e7d45b13d6b Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 23 Mar 2023 14:07:06 -0500 Subject: drm/amd: Fix an out of bounds error in BIOS parser [ Upstream commit d116db180decec1b21bba31d2ff495ac4d8e1b83 ] The array is hardcoded to 8 in atomfirmware.h, but firmware provides a bigger one sometimes. Deferencing the larger array causes an out of bounds error. commit 4fc1ba4aa589 ("drm/amd/display: fix array index out of bound error in bios parser") fixed some of this, but there are two other cases not covered by it. Fix those as well. Reported-by: erhard_f@mailbox.org Link: https://bugzilla.kernel.org/show_bug.cgi?id=214853 Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2473 Signed-off-by: Mario Limonciello Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index 074e70a5c458..e507d2e1410b 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -516,11 +516,8 @@ static enum bp_result get_gpio_i2c_info( info->i2c_slave_address = record->i2c_slave_addr; /* TODO: check how to get register offset for en, Y, etc. */ - info->gpio_info.clk_a_register_index = - le16_to_cpu( - header->gpio_pin[table_index].data_a_reg_index); - info->gpio_info.clk_a_shift = - header->gpio_pin[table_index].gpio_bitshift; + info->gpio_info.clk_a_register_index = le16_to_cpu(pin->data_a_reg_index); + info->gpio_info.clk_a_shift = pin->gpio_bitshift; return BP_RESULT_OK; } -- cgit 1.4.1 From 210ef6cd8e634f18fd889421012192b81325b27b Mon Sep 17 00:00:00 2001 From: lyndonli Date: Thu, 6 Apr 2023 15:30:34 +0800 Subject: drm/amdgpu: Fix sdma v4 sw fini error [ Upstream commit 5e08e9c742a00384e5abe74bd40cf4dc15cb3a2e ] Fix sdma v4 sw fini error for sdma 4.2.2 to solve the following general protection fault [ +0.108196] general protection fault, probably for non-canonical address 0xd5e5a4ae79d24a32: 0000 [#1] PREEMPT SMP PTI [ +0.000018] RIP: 0010:free_fw_priv+0xd/0x70 [ +0.000022] Call Trace: [ +0.000012] [ +0.000011] release_firmware+0x55/0x80 [ +0.000021] amdgpu_ucode_release+0x11/0x20 [amdgpu] [ +0.000415] amdgpu_sdma_destroy_inst_ctx+0x4f/0x90 [amdgpu] [ +0.000360] sdma_v4_0_sw_fini+0xce/0x110 [amdgpu] Signed-off-by: lyndonli Reviewed-by: Likun Gao Reviewed-by: Feifei Xu Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index 77d5a6f30409..5b251d009467 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -1908,7 +1908,7 @@ static int sdma_v4_0_sw_fini(void *handle) amdgpu_ring_fini(&adev->sdma.instance[i].page); } - if (adev->ip_versions[SDMA0_HWIP][0] == IP_VERSION(4, 2, 0) || + if (adev->ip_versions[SDMA0_HWIP][0] == IP_VERSION(4, 2, 2) || adev->ip_versions[SDMA0_HWIP][0] == IP_VERSION(4, 4, 0)) amdgpu_sdma_destroy_inst_ctx(adev, true); else -- cgit 1.4.1 From 83c42283bff0eaa633a653834733dfdcddd5c561 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 15 Feb 2023 17:18:39 +0200 Subject: media: Prefer designated initializers over memset for subdev pad ops [ Upstream commit e3a69496a1cde364c74a600d7a370179b58aed29 ] Structures passed to subdev pad operations are all zero-initialized, but not always with the same kind of code constructs. While most drivers used designated initializers, which zero all the fields that are not specified, when declaring variables, some use memset(). Those two methods lead to the same end result, and, depending on compiler optimizations, may even be completely equivalent, but they're not consistent. Improve coding style consistency by using designated initializers instead of calling memset(). Where applicable, also move the variables to inner scopes of for loops to ensure correct initialization in all iterations. Signed-off-by: Laurent Pinchart Reviewed-by: Lad Prabhakar # For am437x Acked-by: Sakari Ailus Reviewed-by: Tomi Valkeinen Reviewed-by: Kieran Bingham Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 18 +++++++++--------- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 11 +++++------ .../media/platform/samsung/exynos4-is/fimc-capture.c | 7 ++++--- drivers/media/platform/ti/am437x/am437x-vpfe.c | 15 ++++++++------- drivers/media/platform/ti/cal/cal-video.c | 8 ++++---- drivers/media/usb/dvb-usb/cxusb-analog.c | 14 +++++++------- drivers/staging/media/imx/imx-media-capture.c | 12 ++++++------ drivers/staging/media/imx/imx-media-utils.c | 8 ++++---- drivers/staging/media/omap4iss/iss_video.c | 6 +++--- 9 files changed, 50 insertions(+), 49 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index c6f25200982c..7fe375b6322c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -66,7 +66,9 @@ static int vsp1_du_insert_uif(struct vsp1_device *vsp1, struct vsp1_entity *prev, unsigned int prev_pad, struct vsp1_entity *next, unsigned int next_pad) { - struct v4l2_subdev_format format; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; if (!uif) { @@ -82,8 +84,6 @@ static int vsp1_du_insert_uif(struct vsp1_device *vsp1, prev->sink = uif; prev->sink_pad = UIF_PAD_SINK; - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; format.pad = prev_pad; ret = v4l2_subdev_call(&prev->subdev, pad, get_fmt, NULL, &format); @@ -118,8 +118,12 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, struct vsp1_entity *uif, unsigned int brx_input) { - struct v4l2_subdev_selection sel; - struct v4l2_subdev_format format; + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; const struct v4l2_rect *crop; int ret; @@ -129,8 +133,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, */ crop = &vsp1->drm->inputs[rpf->entity.index].crop; - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; format.pad = RWPF_PAD_SINK; format.format.width = crop->width + crop->left; format.format.height = crop->height + crop->top; @@ -147,8 +149,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, __func__, format.format.width, format.format.height, format.format.code, rpf->entity.index); - memset(&sel, 0, sizeof(sel)); - sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; sel.pad = RWPF_PAD_SINK; sel.target = V4L2_SEL_TGT_CROP; sel.r = *crop; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 4c3bd2b1ca28..c31f05a80bb5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -184,15 +184,14 @@ vsp1_entity_get_pad_selection(struct vsp1_entity *entity, int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state) { - struct v4l2_subdev_format format; unsigned int pad; for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { - memset(&format, 0, sizeof(format)); - - format.pad = pad; - format.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE; + struct v4l2_subdev_format format = { + .pad = pad, + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); } diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index e3b95a2b7e04..beaee54ee73b 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -763,7 +763,10 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_pipeline *p = to_fimc_pipeline(fimc->vid_cap.ve.pipe); struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR]; - struct v4l2_subdev_format sfmt; + struct v4l2_subdev_format sfmt = { + .which = set ? V4L2_SUBDEV_FORMAT_ACTIVE + : V4L2_SUBDEV_FORMAT_TRY, + }; struct v4l2_mbus_framefmt *mf = &sfmt.format; struct media_entity *me; struct fimc_fmt *ffmt; @@ -774,9 +777,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, if (WARN_ON(!sd || !tfmt)) return -EINVAL; - memset(&sfmt, 0, sizeof(sfmt)); sfmt.format = *tfmt; - sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; me = fimc_pipeline_get_head(&sd->entity); diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index 2dfae9bc0bba..dffac89cbd21 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -1499,7 +1499,9 @@ static int vpfe_enum_size(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { struct vpfe_device *vpfe = video_drvdata(file); - struct v4l2_subdev_frame_size_enum fse; + struct v4l2_subdev_frame_size_enum fse = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *sd = vpfe->current_subdev->sd; struct vpfe_fmt *fmt; int ret; @@ -1514,11 +1516,9 @@ static int vpfe_enum_size(struct file *file, void *priv, memset(fsize->reserved, 0x0, sizeof(fsize->reserved)); - memset(&fse, 0x0, sizeof(fse)); fse.index = fsize->index; fse.pad = 0; fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); if (ret) return ret; @@ -2146,7 +2146,6 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, { struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, struct vpfe_device, v4l2_dev); - struct v4l2_subdev_mbus_code_enum mbus_code; struct vpfe_subdev_info *sdinfo; struct vpfe_fmt *fmt; int ret = 0; @@ -2173,9 +2172,11 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, vpfe->num_active_fmt = 0; for (j = 0, i = 0; (ret != -EINVAL); ++j) { - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + struct v4l2_subdev_mbus_code_enum mbus_code = { + .index = j, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &mbus_code); if (ret) diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 4eade409d5d3..bbfd2719725a 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -811,7 +811,6 @@ static const struct v4l2_file_operations cal_fops = { static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) { - struct v4l2_subdev_mbus_code_enum mbus_code; struct v4l2_mbus_framefmt mbus_fmt; const struct cal_format_info *fmtinfo; unsigned int i, j, k; @@ -826,10 +825,11 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) ctx->num_active_fmt = 0; for (j = 0, i = 0; ; ++j) { + struct v4l2_subdev_mbus_code_enum mbus_code = { + .index = j, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code, NULL, &mbus_code); if (ret == -EINVAL) diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c index e93183ddd797..deba5224cb8d 100644 --- a/drivers/media/usb/dvb-usb/cxusb-analog.c +++ b/drivers/media/usb/dvb-usb/cxusb-analog.c @@ -1014,7 +1014,10 @@ static int cxusb_medion_try_s_fmt_vid_cap(struct file *file, { struct dvb_usb_device *dvbdev = video_drvdata(file); struct cxusb_medion_dev *cxdev = dvbdev->priv; - struct v4l2_subdev_format subfmt; + struct v4l2_subdev_format subfmt = { + .which = isset ? V4L2_SUBDEV_FORMAT_ACTIVE : + V4L2_SUBDEV_FORMAT_TRY, + }; u32 field; int ret; @@ -1024,9 +1027,6 @@ static int cxusb_medion_try_s_fmt_vid_cap(struct file *file, field = vb2_start_streaming_called(&cxdev->videoqueue) ? cxdev->field_order : cxusb_medion_field_order(cxdev); - memset(&subfmt, 0, sizeof(subfmt)); - subfmt.which = isset ? V4L2_SUBDEV_FORMAT_ACTIVE : - V4L2_SUBDEV_FORMAT_TRY; subfmt.format.width = f->fmt.pix.width & ~1; subfmt.format.height = f->fmt.pix.height & ~1; subfmt.format.code = MEDIA_BUS_FMT_FIXED; @@ -1464,7 +1464,9 @@ int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) .buf = tuner_analog_msg_data, .len = sizeof(tuner_analog_msg_data) }; - struct v4l2_subdev_format subfmt; + struct v4l2_subdev_format subfmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; /* switch tuner to analog mode so IF demod will become accessible */ @@ -1507,8 +1509,6 @@ int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) v4l2_subdev_call(cxdev->tuner, video, s_std, cxdev->norm); v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); - memset(&subfmt, 0, sizeof(subfmt)); - subfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; subfmt.format.width = cxdev->width; subfmt.format.height = cxdev->height; subfmt.format.code = MEDIA_BUS_FMT_FIXED; diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 93ba09236010..5cc67786b916 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -501,14 +501,14 @@ static int capture_legacy_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct capture_priv *priv = video_drvdata(file); - struct v4l2_subdev_frame_interval fi; + struct v4l2_subdev_frame_interval fi = { + .pad = priv->src_sd_pad, + }; int ret; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - memset(&fi, 0, sizeof(fi)); - fi.pad = priv->src_sd_pad; ret = v4l2_subdev_call(priv->src_sd, video, g_frame_interval, &fi); if (ret < 0) return ret; @@ -523,14 +523,14 @@ static int capture_legacy_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { struct capture_priv *priv = video_drvdata(file); - struct v4l2_subdev_frame_interval fi; + struct v4l2_subdev_frame_interval fi = { + .pad = priv->src_sd_pad, + }; int ret; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - memset(&fi, 0, sizeof(fi)); - fi.pad = priv->src_sd_pad; fi.interval = a->parm.capture.timeperframe; ret = v4l2_subdev_call(priv->src_sd, video, s_frame_interval, &fi); if (ret < 0) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 3e7462112649..4985f21b4023 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -432,15 +432,15 @@ int imx_media_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf_try; - struct v4l2_subdev_format format; unsigned int pad; int ret; for (pad = 0; pad < sd->entity.num_pads; pad++) { - memset(&format, 0, sizeof(format)); + struct v4l2_subdev_format format = { + .pad = pad, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; - format.pad = pad; - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format); if (ret) continue; diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 60f3d84be828..8cc8b3ebab11 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -244,7 +244,9 @@ static int __iss_video_get_format(struct iss_video *video, struct v4l2_mbus_framefmt *format) { - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; struct v4l2_subdev *subdev; u32 pad; int ret; @@ -253,9 +255,7 @@ __iss_video_get_format(struct iss_video *video, if (!subdev) return -EINVAL; - memset(&fmt, 0, sizeof(fmt)); fmt.pad = pad; - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; mutex_lock(&video->mutex); ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); -- cgit 1.4.1 From 48e4e06e2c5fe1fda283d499f91492eda2248bb9 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Wed, 29 Mar 2023 09:05:13 +0100 Subject: media: mediatek: vcodec: Fix potential array out-of-bounds in decoder queue_setup [ Upstream commit 8fbcf730cb89c3647f3365226fe7014118fa93c7 ] variable *nplanes is provided by user via system call argument. The possible value of q_data->fmt->num_planes is 1-3, while the value of *nplanes can be 1-8. The array access by index i can cause array out-of-bounds. Fix this bug by checking *nplanes against the array size. Signed-off-by: Wei Chen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c index c99705681a03..93fcea821001 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c @@ -735,6 +735,13 @@ int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, } if (*nplanes) { + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (*nplanes != q_data->fmt->num_planes) + return -EINVAL; + } else { + if (*nplanes != 1) + return -EINVAL; + } for (i = 0; i < *nplanes; i++) { if (sizes[i] < q_data->sizeimage[i]) return -EINVAL; -- cgit 1.4.1 From 660ab315619bceab576d65e90df62f28d22deebb Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 15 Feb 2023 20:31:38 +0200 Subject: wifi: ath: Silence memcpy run-time false positive warning [ Upstream commit bfcc8ba45eb87bfaaff900bbad2b87b204899d41 ] The memcpy() in ath_key_config() was attempting to write across neighboring struct members in struct ath_keyval. Introduce a wrapping struct_group, kv_values, to be the addressable target of the memcpy without overflowing an individual member. Silences the false positive run-time warning: memcpy: detected field-spanning write (size 32) of single field "hk.kv_val" at drivers/net/wireless/ath/key.c:506 (size 16) Link: https://bbs.archlinux.org/viewtopic.php?id=282254 Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230210054310.never.554-kees@kernel.org Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath.h | 12 +++++++----- drivers/net/wireless/ath/key.c | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index f083fb9038c3..f02a308a9ffc 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -96,11 +96,13 @@ struct ath_keyval { u8 kv_type; u8 kv_pad; u16 kv_len; - u8 kv_val[16]; /* TK */ - u8 kv_mic[8]; /* Michael MIC key */ - u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware - * supports both MIC keys in the same key cache entry; - * in that case, kv_mic is the RX key) */ + struct_group(kv_values, + u8 kv_val[16]; /* TK */ + u8 kv_mic[8]; /* Michael MIC key */ + u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware + * supports both MIC keys in the same key cache entry; + * in that case, kv_mic is the RX key) */ + ); }; enum ath_cipher { diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c index 61b59a804e30..b7b61d4f02ba 100644 --- a/drivers/net/wireless/ath/key.c +++ b/drivers/net/wireless/ath/key.c @@ -503,7 +503,7 @@ int ath_key_config(struct ath_common *common, hk.kv_len = key->keylen; if (key->keylen) - memcpy(hk.kv_val, key->key, key->keylen); + memcpy(&hk.kv_values, key->key, key->keylen); if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { switch (vif->type) { -- cgit 1.4.1 From 4e7a81b5e7182910ce8d991dbf2a85d59362306a Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 21 Feb 2023 21:06:42 +0100 Subject: bpf: Annotate data races in bpf_local_storage [ Upstream commit 0a09a2f933c73dc76ab0b72da6855f44342a8903 ] There are a few cases where hlist_node is checked to be unhashed without holding the lock protecting its modification. In this case, one must use hlist_unhashed_lockless to avoid load tearing and KCSAN reports. Fix this by using lockless variant in places not protected by the lock. Since this is not prompted by any actual KCSAN reports but only from code review, I have not included a fixes tag. Cc: Martin KaFai Lau Cc: KP Singh Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20230221200646.2500777-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/bpf_local_storage.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index f27fa5ba7d72..d9d88a2cda5e 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -51,11 +51,21 @@ owner_storage(struct bpf_local_storage_map *smap, void *owner) return map->ops->map_owner_storage_ptr(owner); } +static bool selem_linked_to_storage_lockless(const struct bpf_local_storage_elem *selem) +{ + return !hlist_unhashed_lockless(&selem->snode); +} + static bool selem_linked_to_storage(const struct bpf_local_storage_elem *selem) { return !hlist_unhashed(&selem->snode); } +static bool selem_linked_to_map_lockless(const struct bpf_local_storage_elem *selem) +{ + return !hlist_unhashed_lockless(&selem->map_node); +} + static bool selem_linked_to_map(const struct bpf_local_storage_elem *selem) { return !hlist_unhashed(&selem->map_node); @@ -165,7 +175,7 @@ static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem, bool free_local_storage = false; unsigned long flags; - if (unlikely(!selem_linked_to_storage(selem))) + if (unlikely(!selem_linked_to_storage_lockless(selem))) /* selem has already been unlinked from sk */ return; @@ -199,7 +209,7 @@ void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem) struct bpf_local_storage_map_bucket *b; unsigned long flags; - if (unlikely(!selem_linked_to_map(selem))) + if (unlikely(!selem_linked_to_map_lockless(selem))) /* selem has already be unlinked from smap */ return; @@ -410,7 +420,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, err = check_flags(old_sdata, map_flags); if (err) return ERR_PTR(err); - if (old_sdata && selem_linked_to_storage(SELEM(old_sdata))) { + if (old_sdata && selem_linked_to_storage_lockless(SELEM(old_sdata))) { copy_map_value_locked(&smap->map, old_sdata->data, value, false); return old_sdata; -- cgit 1.4.1 From c35105f375b530bc27e03ea9250b1c26dd4cae86 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 17:00:34 +0900 Subject: wifi: brcmfmac: pcie: Provide a buffer of random bytes to the device [ Upstream commit 91918ce88d9fef408bb12c46a27c73d79b604c20 ] Newer Apple firmwares on chipsets without a hardware RNG require the host to provide a buffer of 256 random bytes to the device on initialization. This buffer is present immediately before NVRAM, suffixed by a footer containing a magic number and the buffer length. This won't affect chips/firmwares that do not use this feature, so do it unconditionally for all Apple platforms (those with an Apple OTP). Reviewed-by: Linus Walleij Signed-off-by: Hector Martin Reviewed-by: Julian Calaby Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214080034.3828-3-marcan@marcan.st Signed-off-by: Sasha Levin --- .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 067ea019b110..3b1277a8bd61 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -1631,6 +1632,13 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, return 0; } +struct brcmf_random_seed_footer { + __le32 length; + __le32 magic; +}; + +#define BRCMF_RANDOM_SEED_MAGIC 0xfeedc0de +#define BRCMF_RANDOM_SEED_LENGTH 0x100 static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, const struct firmware *fw, void *nvram, @@ -1667,6 +1675,30 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, nvram_len; memcpy_toio(devinfo->tcm + address, nvram, nvram_len); brcmf_fw_nvram_free(nvram); + + if (devinfo->otp.valid) { + size_t rand_len = BRCMF_RANDOM_SEED_LENGTH; + struct brcmf_random_seed_footer footer = { + .length = cpu_to_le32(rand_len), + .magic = cpu_to_le32(BRCMF_RANDOM_SEED_MAGIC), + }; + void *randbuf; + + /* Some Apple chips/firmwares expect a buffer of random + * data to be present before NVRAM + */ + brcmf_dbg(PCIE, "Download random seed\n"); + + address -= sizeof(footer); + memcpy_toio(devinfo->tcm + address, &footer, + sizeof(footer)); + + address -= rand_len; + randbuf = kzalloc(rand_len, GFP_KERNEL); + get_random_bytes(randbuf, rand_len); + memcpy_toio(devinfo->tcm + address, randbuf, rand_len); + kfree(randbuf); + } } else { brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", devinfo->nvram_name); -- cgit 1.4.1 From f8a6c53ff1d91acd5a20eb627edbffd816eb9a4e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:19 +0900 Subject: wifi: brcmfmac: cfg80211: Pass the PMK in binary instead of hex [ Upstream commit 89b89e52153fda2733562776c7c9d9d3ebf8dd6d ] Apparently the hex passphrase mechanism does not work on newer chips/firmware (e.g. BCM4387). It seems there was a simple way of passing it in binary all along, so use that and avoid the hexification. OpenBSD has been doing it like this from the beginning, so this should work on all chips. Also clear the structure before setting the PMK. This was leaking uninitialized stack contents to the device. Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-6-marcan@marcan.st Signed-off-by: Sasha Levin --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 2cc913acfc2d..ad5a8d61d938 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1351,13 +1351,14 @@ static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len) { struct brcmf_pub *drvr = ifp->drvr; struct brcmf_wsec_pmk_le pmk; - int i, err; + int err; + + memset(&pmk, 0, sizeof(pmk)); - /* convert to firmware key format */ - pmk.key_len = cpu_to_le16(pmk_len << 1); - pmk.flags = cpu_to_le16(BRCMF_WSEC_PASSPHRASE); - for (i = 0; i < pmk_len; i++) - snprintf(&pmk.key[2 * i], 3, "%02x", pmk_data[i]); + /* pass pmk directly */ + pmk.key_len = cpu_to_le16(pmk_len); + pmk.flags = cpu_to_le16(0); + memcpy(pmk.key, pmk_data, pmk_len); /* store psk in firmware */ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK, -- cgit 1.4.1 From e6f4fb28890c1361e0db9eb1adee3fc04e7fe7f5 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Mar 2023 11:59:39 +0100 Subject: ext2: Check block size validity during mount [ Upstream commit 62aeb94433fcec80241754b70d0d1836d5926b0a ] Check that log of block size stored in the superblock has sensible value. Otherwise the shift computing the block size can overflow leading to undefined behavior. Reported-by: syzbot+4fec412f59eba8c01b77@syzkaller.appspotmail.com Signed-off-by: Jan Kara Signed-off-by: Sasha Levin --- fs/ext2/ext2.h | 1 + fs/ext2/super.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 28de11a22e5f..dc5dcb78bc27 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -180,6 +180,7 @@ static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb) #define EXT2_MIN_BLOCK_SIZE 1024 #define EXT2_MAX_BLOCK_SIZE 4096 #define EXT2_MIN_BLOCK_LOG_SIZE 10 +#define EXT2_MAX_BLOCK_LOG_SIZE 16 #define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 03f2af98b1b4..3feea4b31fa7 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -945,6 +945,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } + if (le32_to_cpu(es->s_log_block_size) > + (EXT2_MAX_BLOCK_LOG_SIZE - BLOCK_SIZE_BITS)) { + ext2_msg(sb, KERN_ERR, + "Invalid log block size: %u", + le32_to_cpu(es->s_log_block_size)); + goto failed_mount; + } blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); if (test_opt(sb, DAX)) { -- cgit 1.4.1 From a9df88cb31dcbd72104ec5883f35cbc1fb587e47 Mon Sep 17 00:00:00 2001 From: Justin Tee Date: Wed, 1 Mar 2023 15:16:17 -0800 Subject: scsi: lpfc: Prevent lpfc_debugfs_lockstat_write() buffer overflow [ Upstream commit c6087b82a9146826564a55c5ca0164cac40348f5 ] A static code analysis tool flagged the possibility of buffer overflow when using copy_from_user() for a debugfs entry. Currently, it is possible that copy_from_user() copies more bytes than what would fit in the mybuf char array. Add a min() restriction check between sizeof(mybuf) - 1 and nbytes passed from the userspace buffer to protect against buffer overflow. Link: https://lore.kernel.org/r/20230301231626.9621-2-justintee8345@gmail.com Signed-off-by: Justin Tee Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/lpfc/lpfc_debugfs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index f5252e45a48a..3e365e5e194a 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -2157,10 +2157,13 @@ lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf, char mybuf[64]; char *pbuf; int i; + size_t bsize; memset(mybuf, 0, sizeof(mybuf)); - if (copy_from_user(mybuf, buf, nbytes)) + bsize = min(nbytes, (sizeof(mybuf) - 1)); + + if (copy_from_user(mybuf, buf, bsize)) return -EFAULT; pbuf = &mybuf[0]; @@ -2181,7 +2184,7 @@ lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf, qp->lock_conflict.wq_access = 0; } } - return nbytes; + return bsize; } #endif -- cgit 1.4.1 From 8a7228969431af27e6d6cd86dd3dd97fd1dcb4f7 Mon Sep 17 00:00:00 2001 From: Justin Tee Date: Wed, 1 Mar 2023 15:16:22 -0800 Subject: scsi: lpfc: Correct used_rpi count when devloss tmo fires with no recovery [ Upstream commit db651ec22524eb8f9c854fbb4d9acd5d7e5be9e4 ] A fabric controller can sometimes send an RDP request right before a link down event. Because of this outstanding RDP request, the driver does not remove the last reference count on its ndlp causing a potential leak of RPI resources when devloss tmo fires. In lpfc_cmpl_els_rsp(), modify the NPIV clause to always allow the lpfc_drop_node() routine to execute when not registered with SCSI transport. This relaxes the contraint that an NPIV ndlp must be in a specific state in order to call lpfc_drop node. Logic is revised such that the lpfc_drop_node() routine is always called to ensure the last ndlp decrement occurs. Signed-off-by: Justin Tee Link: https://lore.kernel.org/r/20230301231626.9621-7-justintee8345@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/lpfc/lpfc_els.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 863b2125fed6..ddd5949d8fc0 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -5402,18 +5402,20 @@ out: * these conditions and release the RPI. */ if (phba->sli_rev == LPFC_SLI_REV4 && - (vport && vport->port_type == LPFC_NPIV_PORT) && - !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD) && - ndlp->nlp_flag & NLP_RELEASE_RPI) { - if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE && - ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE) { - lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; - ndlp->nlp_flag &= ~NLP_RELEASE_RPI; - spin_unlock_irq(&ndlp->lock); - lpfc_drop_node(vport, ndlp); + vport && vport->port_type == LPFC_NPIV_PORT && + !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) { + if (ndlp->nlp_flag & NLP_RELEASE_RPI) { + if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE && + ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE) { + lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi); + spin_lock_irq(&ndlp->lock); + ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR; + ndlp->nlp_flag &= ~NLP_RELEASE_RPI; + spin_unlock_irq(&ndlp->lock); + } } + + lpfc_drop_node(vport, ndlp); } /* Release the originating I/O reference. */ -- cgit 1.4.1 From efb1a257513438d43f4335f09b2f684e8167cad2 Mon Sep 17 00:00:00 2001 From: Maxim Korotkov Date: Thu, 9 Mar 2023 20:43:47 +0300 Subject: bnxt: avoid overflow in bnxt_get_nvram_directory() [ Upstream commit 7c6dddc239abe660598c49ec95ea0ed6399a4b2a ] The value of an arithmetic expression is subject of possible overflow due to a failure to cast operands to a larger data type before performing arithmetic. Used macro for multiplication instead operator for avoiding overflow. Found by Security Code and Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Maxim Korotkov Reviewed-by: Pavan Chebbi Link: https://lore.kernel.org/r/20230309174347.3515-1-korotkov.maxim.s@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 01b973bc509f..b2d531e014c5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2822,7 +2822,7 @@ static int bnxt_get_nvram_directory(struct net_device *dev, u32 len, u8 *data) if (rc) return rc; - buflen = dir_entries * entry_length; + buflen = mul_u32_u32(dir_entries, entry_length); buf = hwrm_req_dma_slice(bp, req, buflen, &dma_handle); if (!buf) { hwrm_req_drop(bp, req); -- cgit 1.4.1 From ee5929c1e85ebdc94f938481e90f7e8723083563 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sun, 19 Mar 2023 16:41:08 -0700 Subject: net: pasemi: Fix return type of pasemi_mac_start_tx() [ Upstream commit c8384d4a51e7cb0e6587f3143f29099f202c5de1 ] With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG), indirect call targets are validated against the expected function pointer prototype to make sure the call target is valid to help mitigate ROP attacks. If they are not identical, there is a failure at run time, which manifests as either a kernel panic or thread getting killed. A warning in clang aims to catch these at compile time, which reveals: drivers/net/ethernet/pasemi/pasemi_mac.c:1665:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict] .ndo_start_xmit = pasemi_mac_start_tx, ^~~~~~~~~~~~~~~~~~~ 1 error generated. ->ndo_start_xmit() in 'struct net_device_ops' expects a return type of 'netdev_tx_t', not 'int'. Adjust the return type of pasemi_mac_start_tx() to match the prototype's to resolve the warning. While PowerPC does not currently implement support for kCFI, it could in the future, which means this warning becomes a fatal CFI failure at run time. Link: https://github.com/ClangBuiltLinux/linux/issues/1750 Signed-off-by: Nathan Chancellor Reviewed-by: Horatiu Vultur Link: https://lore.kernel.org/r/20230319-pasemi-incompatible-pointer-types-strict-v1-1-1b9459d8aef0@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/pasemi/pasemi_mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index aaab590ef548..ed7dd0a04235 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -1423,7 +1423,7 @@ static void pasemi_mac_queue_csdesc(const struct sk_buff *skb, write_dma_reg(PAS_DMA_TXCHAN_INCR(txring->chan.chno), 2); } -static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) { struct pasemi_mac * const mac = netdev_priv(dev); struct pasemi_mac_txring * const txring = tx_ring(mac); -- cgit 1.4.1 From cf1fe8ccb5060b60be0ebaa1f861e9ea46101eae Mon Sep 17 00:00:00 2001 From: Nick Child Date: Tue, 21 Mar 2023 10:07:24 -0500 Subject: net: Catch invalid index in XPS mapping [ Upstream commit 5dd0dfd55baec0742ba8f5625a0dd064aca7db16 ] When setting the XPS value of a TX queue, warn the user once if the index of the queue is greater than the number of allocated TX queues. Previously, this scenario went uncaught. In the best case, it resulted in unnecessary allocations. In the worst case, it resulted in out-of-bounds memory references through calls to `netdev_get_tx_queue( dev, index)`. Therefore, it is important to inform the user but not worth returning an error and risk downing the netdevice. Signed-off-by: Nick Child Reviewed-by: Piotr Raczynski Link: https://lore.kernel.org/r/20230321150725.127229-1-nnac123@linux.ibm.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/dev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index 1fb7eef38ebe..93d430693ca0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2549,6 +2549,8 @@ int __netif_set_xps_queue(struct net_device *dev, const unsigned long *mask, struct xps_map *map, *new_map; unsigned int nr_ids; + WARN_ON_ONCE(index >= dev->num_tx_queues); + if (dev->num_tc) { /* Do not allow XPS on subordinate device directly */ num_tc = dev->num_tc; -- cgit 1.4.1 From d957a100bcc2e0ea6b770781a849ba7c3efe1532 Mon Sep 17 00:00:00 2001 From: Nick Child Date: Tue, 21 Mar 2023 10:07:25 -0500 Subject: netdev: Enforce index cap in netdev_get_tx_queue [ Upstream commit 1cc6571f562774f1d928dc8b3cff50829b86e970 ] When requesting a TX queue at a given index, warn on out-of-bounds referencing if the index is greater than the allocated number of queues. Specifically, since this function is used heavily in the networking stack use DEBUG_NET_WARN_ON_ONCE to avoid executing a new branch on every packet. Signed-off-by: Nick Child Link: https://lore.kernel.org/r/20230321150725.127229-2-nnac123@linux.ibm.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/linux/netdevice.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b072449b0f1a..eac51e22a52a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2428,6 +2428,7 @@ static inline struct netdev_queue *netdev_get_tx_queue(const struct net_device *dev, unsigned int index) { + DEBUG_NET_WARN_ON_ONCE(index >= dev->num_tx_queues); return &dev->_tx[index]; } -- cgit 1.4.1 From a7a4def6c7046e090bb10c6d550fdeb487db98ba Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Sat, 18 Mar 2023 20:56:17 -0500 Subject: scsi: target: iscsit: Free cmds before session free [ Upstream commit d8990b5a4d065f38f35d69bcd627ec5a7f8330ca ] Commands from recovery entries are freed after session has been closed. That leads to use-after-free at command free or NPE with such call trace: Time2Retain timer expired for SID: 1, cleaning up iSCSI session. BUG: kernel NULL pointer dereference, address: 0000000000000140 RIP: 0010:sbitmap_queue_clear+0x3a/0xa0 Call Trace: target_release_cmd_kref+0xd1/0x1f0 [target_core_mod] transport_generic_free_cmd+0xd1/0x180 [target_core_mod] iscsit_free_cmd+0x53/0xd0 [iscsi_target_mod] iscsit_free_connection_recovery_entries+0x29d/0x320 [iscsi_target_mod] iscsit_close_session+0x13a/0x140 [iscsi_target_mod] iscsit_check_post_dataout+0x440/0x440 [iscsi_target_mod] call_timer_fn+0x24/0x140 Move cleanup of recovery enrties to before session freeing. Reported-by: Forza Signed-off-by: Dmitry Bogdanov Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230319015620.96006-7-michael.christie@oracle.com Reviewed-by: Maurizio Lombardi Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/iscsi/iscsi_target.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 3f7a9f7f5f4e..07e196b44b91 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -4531,6 +4531,9 @@ int iscsit_close_session(struct iscsit_session *sess, bool can_sleep) iscsit_stop_time2retain_timer(sess); spin_unlock_bh(&se_tpg->session_lock); + if (sess->sess_ops->ErrorRecoveryLevel == 2) + iscsit_free_connection_recovery_entries(sess); + /* * transport_deregister_session_configfs() will clear the * struct se_node_acl->nacl_sess pointer now as a iscsi_np process context @@ -4554,9 +4557,6 @@ int iscsit_close_session(struct iscsit_session *sess, bool can_sleep) transport_deregister_session(sess->se_sess); - if (sess->sess_ops->ErrorRecoveryLevel == 2) - iscsit_free_connection_recovery_entries(sess); - iscsit_free_all_ooo_cmdsns(sess); spin_lock_bh(&se_tpg->session_lock); -- cgit 1.4.1 From c9115f49cf260d24d8b5f2d9a4b63cb31a627bb4 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Wed, 8 Feb 2023 07:51:02 +0200 Subject: lib: cpu_rmap: Avoid use after free on rmap->obj array entries [ Upstream commit 4e0473f1060aa49621d40a113afde24818101d37 ] When calling irq_set_affinity_notifier() with NULL at the notify argument, it will cause freeing of the glue pointer in the corresponding array entry but will leave the pointer in the array. A subsequent call to free_irq_cpu_rmap() will try to free this entry again leading to possible use after free. Fix that by setting NULL to the array entry and checking that we have non-zero at the array entry when iterating over the array in free_irq_cpu_rmap(). The current code does not suffer from this since there are no cases where irq_set_affinity_notifier(irq, NULL) (note the NULL passed for the notify arg) is called, followed by a call to free_irq_cpu_rmap() so we don't hit and issue. Subsequent patches in this series excersize this flow, hence the required fix. Cc: Thomas Gleixner Signed-off-by: Eli Cohen Signed-off-by: Saeed Mahameed Reviewed-by: Jacob Keller Signed-off-by: Sasha Levin --- lib/cpu_rmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/cpu_rmap.c b/lib/cpu_rmap.c index f08d9c56f712..e77f12bb3c77 100644 --- a/lib/cpu_rmap.c +++ b/lib/cpu_rmap.c @@ -232,7 +232,8 @@ void free_irq_cpu_rmap(struct cpu_rmap *rmap) for (index = 0; index < rmap->used; index++) { glue = rmap->obj[index]; - irq_set_affinity_notifier(glue->notify.irq, NULL); + if (glue) + irq_set_affinity_notifier(glue->notify.irq, NULL); } cpu_rmap_put(rmap); @@ -268,6 +269,7 @@ static void irq_cpu_rmap_release(struct kref *ref) container_of(ref, struct irq_glue, notify.kref); cpu_rmap_put(glue->rmap); + glue->rmap->obj[glue->index] = NULL; kfree(glue); } @@ -297,6 +299,7 @@ int irq_cpu_rmap_add(struct cpu_rmap *rmap, int irq) rc = irq_set_affinity_notifier(irq, &glue->notify); if (rc) { cpu_rmap_put(glue->rmap); + rmap->obj[glue->index] = NULL; kfree(glue); } return rc; -- cgit 1.4.1 From 9c6da3b7f12528cd52c458b33496a098b838fcfc Mon Sep 17 00:00:00 2001 From: Zheng Wang Date: Sat, 18 Mar 2023 16:16:35 +0800 Subject: scsi: message: mptlan: Fix use after free bug in mptlan_remove() due to race condition [ Upstream commit f486893288f3e9b171b836f43853a6426515d800 ] mptlan_probe() calls mpt_register_lan_device() which initializes the &priv->post_buckets_task workqueue. A call to mpt_lan_wake_post_buckets_task() will subsequently start the work. During driver unload in mptlan_remove() the following race may occur: CPU0 CPU1 |mpt_lan_post_receive_buckets_work() mptlan_remove() | free_netdev() | kfree(dev); | | | dev->mtu | //use Fix this by finishing the work prior to cleaning up in mptlan_remove(). [mkp: we really should remove mptlan instead of attempting to fix it] Signed-off-by: Zheng Wang Link: https://lore.kernel.org/r/20230318081635.796479-1-zyytlz.wz@163.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/message/fusion/mptlan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index 142eb5d5d9df..de2e7bcf4784 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -1433,7 +1433,9 @@ mptlan_remove(struct pci_dev *pdev) { MPT_ADAPTER *ioc = pci_get_drvdata(pdev); struct net_device *dev = ioc->netdev; + struct mpt_lan_priv *priv = netdev_priv(dev); + cancel_delayed_work_sync(&priv->post_buckets_task); if(dev != NULL) { unregister_netdev(dev); free_netdev(dev); -- cgit 1.4.1 From d485903231868b01f961c09eeab48f73179ce937 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 28 Mar 2023 00:43:16 +0200 Subject: gfs2: Fix inode height consistency check [ Upstream commit cfcdb5bad34f600aed7613c3c1a5e618111f77b7 ] The maximum allowed height of an inode's metadata tree depends on the filesystem block size; it is lower for bigger-block filesystems. When reading in an inode, make sure that the height doesn't exceed the maximum allowed height. Arrays like sd_heightsize are sized to be big enough for any filesystem block size; they will often be slightly bigger than what's needed for a specific filesystem. Reported-by: syzbot+45d4691b1ed3c48eba05@syzkaller.appspotmail.com Signed-off-by: Andreas Gruenbacher Signed-off-by: Sasha Levin --- fs/gfs2/glops.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index d78b61ecc1cd..7762483f5f20 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -393,6 +393,7 @@ static int inode_go_demote_ok(const struct gfs2_glock *gl) static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) { + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); const struct gfs2_dinode *str = buf; struct timespec64 atime; u16 height, depth; @@ -439,7 +440,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) /* i_diskflags and i_eattr must be set before gfs2_set_inode_flags() */ gfs2_set_inode_flags(inode); height = be16_to_cpu(str->di_height); - if (unlikely(height > GFS2_MAX_META_HEIGHT)) + if (unlikely(height > sdp->sd_max_height)) goto corrupt; ip->i_height = (u8)height; -- cgit 1.4.1 From 7739981b9c6acc7d550e46fcf6016d93fe49f94b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 28 Mar 2023 13:58:32 +0300 Subject: scsi: ufs: ufs-pci: Add support for Intel Lunar Lake [ Upstream commit 0a07d3c7a1d205b47d9f3608ff4e9d1065d63b6d ] Add PCI ID to support Intel Lunar Lake, same as MTL. Signed-off-by: Adrian Hunter Link: https://lore.kernel.org/r/20230328105832.3495-1-adrian.hunter@intel.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/ufs/host/ufshcd-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index 1c91f43e15c8..9c911787f84c 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -607,6 +607,7 @@ static const struct pci_device_id ufshcd_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x51FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops }, { PCI_VDEVICE(INTEL, 0x54FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops }, { PCI_VDEVICE(INTEL, 0x7E47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(INTEL, 0xA847), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, { } /* terminate list */ }; -- cgit 1.4.1 From c6bee8970075b256fc1b07bf4873049219380818 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Sat, 4 Mar 2023 01:21:01 +0800 Subject: ext4: set goal start correctly in ext4_mb_normalize_request [ Upstream commit b07ffe6927c75d99af534d685282ea188d9f71a6 ] We need to set ac_g_ex to notify the goal start used in ext4_mb_find_by_goal. Set ac_g_ex instead of ac_f_ex in ext4_mb_normalize_request. Besides we should assure goal start is in range [first_data_block, blocks_count) as ext4_mb_initialize_context does. [ Added a check to make sure size is less than ar->pright; otherwise we could end up passing an underflowed value of ar->pright - size to ext4_get_group_no_and_offset(), which will trigger a BUG_ON later on. - TYT ] Signed-off-by: Kemeng Shi Reviewed-by: Ritesh Harjani (IBM) Link: https://lore.kernel.org/r/20230303172120.3800725-2-shikemeng@huaweicloud.com Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/mballoc.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 09bb1dae40cf..21f09bcffbba 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4018,6 +4018,7 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, struct ext4_allocation_request *ar) { struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); + struct ext4_super_block *es = sbi->s_es; int bsbits, max; ext4_lblk_t end; loff_t size, start_off; @@ -4213,18 +4214,21 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, ac->ac_g_ex.fe_len = EXT4_NUM_B2C(sbi, size); /* define goal start in order to merge */ - if (ar->pright && (ar->lright == (start + size))) { + if (ar->pright && (ar->lright == (start + size)) && + ar->pright >= size && + ar->pright - size >= le32_to_cpu(es->s_first_data_block)) { /* merge to the right */ ext4_get_group_no_and_offset(ac->ac_sb, ar->pright - size, - &ac->ac_f_ex.fe_group, - &ac->ac_f_ex.fe_start); + &ac->ac_g_ex.fe_group, + &ac->ac_g_ex.fe_start); ac->ac_flags |= EXT4_MB_HINT_TRY_GOAL; } - if (ar->pleft && (ar->lleft + 1 == start)) { + if (ar->pleft && (ar->lleft + 1 == start) && + ar->pleft + 1 < ext4_blocks_count(es)) { /* merge to the left */ ext4_get_group_no_and_offset(ac->ac_sb, ar->pleft + 1, - &ac->ac_f_ex.fe_group, - &ac->ac_f_ex.fe_start); + &ac->ac_g_ex.fe_group, + &ac->ac_g_ex.fe_start); ac->ac_flags |= EXT4_MB_HINT_TRY_GOAL; } -- cgit 1.4.1 From 9d4430b7f862ce8835ca4e054b6916d15c8e0862 Mon Sep 17 00:00:00 2001 From: Ojaswin Mujoo Date: Sat, 25 Mar 2023 13:43:39 +0530 Subject: ext4: Fix best extent lstart adjustment logic in ext4_mb_new_inode_pa() [ Upstream commit 93cdf49f6eca5e23f6546b8f28457b2e6a6961d9 ] When the length of best extent found is less than the length of goal extent we need to make sure that the best extent atleast covers the start of the original request. This is done by adjusting the ac_b_ex.fe_logical (logical start) of the extent. While doing so, the current logic sometimes results in the best extent's logical range overflowing the goal extent. Since this best extent is later added to the inode preallocation list, we have a possibility of introducing overlapping preallocations. This is discussed in detail here [1]. As per Jan's suggestion, to fix this, replace the existing logic with the below logic for adjusting best extent as it keeps fragmentation in check while ensuring logical range of best extent doesn't overflow out of goal extent: 1. Check if best extent can be kept at end of goal range and still cover original start. 2. Else, check if best extent can be kept at start of goal range and still cover original start. 3. Else, keep the best extent at start of original request. Also, add a few extra BUG_ONs that might help catch errors faster. [1] https://lore.kernel.org/r/Y+OGkVvzPN0RMv0O@li-bb2b2a4c-3307-11b2-a85c-8fa5c3a69313.ibm.com Suggested-by: Jan Kara Signed-off-by: Ojaswin Mujoo Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Jan Kara Link: https://lore.kernel.org/r/f96aca6d415b36d1f90db86c1a8cd7e2e9d7ab0e.1679731817.git.ojaswin@linux.ibm.com Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/mballoc.c | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 21f09bcffbba..32d88757a780 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4321,6 +4321,7 @@ static void ext4_mb_use_inode_pa(struct ext4_allocation_context *ac, BUG_ON(start < pa->pa_pstart); BUG_ON(end > pa->pa_pstart + EXT4_C2B(sbi, pa->pa_len)); BUG_ON(pa->pa_free < len); + BUG_ON(ac->ac_b_ex.fe_len <= 0); pa->pa_free -= len; mb_debug(ac->ac_sb, "use %llu/%d from inode pa %p\n", start, len, pa); @@ -4650,10 +4651,8 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) pa = ac->ac_pa; if (ac->ac_b_ex.fe_len < ac->ac_g_ex.fe_len) { - int winl; - int wins; - int win; - int offs; + int new_bex_start; + int new_bex_end; /* we can't allocate as much as normalizer wants. * so, found space must get proper lstart @@ -4661,26 +4660,40 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) BUG_ON(ac->ac_g_ex.fe_logical > ac->ac_o_ex.fe_logical); BUG_ON(ac->ac_g_ex.fe_len < ac->ac_o_ex.fe_len); - /* we're limited by original request in that - * logical block must be covered any way - * winl is window we can move our chunk within */ - winl = ac->ac_o_ex.fe_logical - ac->ac_g_ex.fe_logical; + /* + * Use the below logic for adjusting best extent as it keeps + * fragmentation in check while ensuring logical range of best + * extent doesn't overflow out of goal extent: + * + * 1. Check if best ex can be kept at end of goal and still + * cover original start + * 2. Else, check if best ex can be kept at start of goal and + * still cover original start + * 3. Else, keep the best ex at start of original request. + */ + new_bex_end = ac->ac_g_ex.fe_logical + + EXT4_C2B(sbi, ac->ac_g_ex.fe_len); + new_bex_start = new_bex_end - EXT4_C2B(sbi, ac->ac_b_ex.fe_len); + if (ac->ac_o_ex.fe_logical >= new_bex_start) + goto adjust_bex; - /* also, we should cover whole original request */ - wins = EXT4_C2B(sbi, ac->ac_b_ex.fe_len - ac->ac_o_ex.fe_len); + new_bex_start = ac->ac_g_ex.fe_logical; + new_bex_end = + new_bex_start + EXT4_C2B(sbi, ac->ac_b_ex.fe_len); + if (ac->ac_o_ex.fe_logical < new_bex_end) + goto adjust_bex; - /* the smallest one defines real window */ - win = min(winl, wins); + new_bex_start = ac->ac_o_ex.fe_logical; + new_bex_end = + new_bex_start + EXT4_C2B(sbi, ac->ac_b_ex.fe_len); - offs = ac->ac_o_ex.fe_logical % - EXT4_C2B(sbi, ac->ac_b_ex.fe_len); - if (offs && offs < win) - win = offs; +adjust_bex: + ac->ac_b_ex.fe_logical = new_bex_start; - ac->ac_b_ex.fe_logical = ac->ac_o_ex.fe_logical - - EXT4_NUM_B2C(sbi, win); BUG_ON(ac->ac_o_ex.fe_logical < ac->ac_b_ex.fe_logical); BUG_ON(ac->ac_o_ex.fe_len > ac->ac_b_ex.fe_len); + BUG_ON(new_bex_end > (ac->ac_g_ex.fe_logical + + EXT4_C2B(sbi, ac->ac_g_ex.fe_len))); } /* preallocation can change ac_b_ex, thus we store actually -- cgit 1.4.1 From c1b0b32f2dfa3f576bcc427ef64a5ca6405390af Mon Sep 17 00:00:00 2001 From: Stephan Müller Date: Mon, 27 Mar 2023 09:03:52 +0200 Subject: crypto: jitter - permanent and intermittent health errors [ Upstream commit 3fde2fe99aa6dacd4151c87382b07ce7f30f0a52 ] According to SP800-90B, two health failures are allowed: the intermittend and the permanent failure. So far, only the intermittent failure was implemented. The permanent failure was achieved by resetting the entire entropy source including its health test state and waiting for two or more back-to-back health errors. This approach is appropriate for RCT, but not for APT as APT has a non-linear cutoff value. Thus, this patch implements 2 cutoff values for both RCT/APT. This implies that the health state is left untouched when an intermittent failure occurs. The noise source is reset and a new APT powerup-self test is performed. Yet, whith the unchanged health test state, the counting of failures continues until a permanent failure is reached. Any non-failing raw entropy value causes the health tests to reset. The intermittent error has an unchanged significance level of 2^-30. The permanent error has a significance level of 2^-60. Considering that this level also indicates a false-positive rate (see SP800-90B section 4.2) a false-positive must only be incurred with a low probability when considering a fleet of Linux kernels as a whole. Hitting the permanent error may cause a panic(), the following calculation applies: Assuming that a fleet of 10^9 Linux kernels run concurrently with this patch in FIPS mode and on each kernel 2 health tests are performed every minute for one year, the chances of a false positive is about 1:1000 based on the binomial distribution. In addition, any power-up health test errors triggered with jent_entropy_init are treated as permanent errors. A permanent failure causes the entire entropy source to permanently return an error. This implies that a caller can only remedy the situation by re-allocating a new instance of the Jitter RNG. In a subsequent patch, a transparent re-allocation will be provided which also changes the implied heuristic entropy assessment. In addition, when the kernel is booted with fips=1, the Jitter RNG is defined to be part of a FIPS module. The permanent error of the Jitter RNG is translated as a FIPS module error. In this case, the entire FIPS module must cease operation. This is implemented in the kernel by invoking panic(). The patch also fixes an off-by-one in the RCT cutoff value which is now set to 30 instead of 31. This is because the counting of the values starts with 0. Reviewed-by: Vladis Dronov Signed-off-by: Stephan Mueller Reviewed-by: Marcelo Henrique Cerri Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/jitterentropy-kcapi.c | 51 ++++++++------- crypto/jitterentropy.c | 144 ++++++++++++++++--------------------------- crypto/jitterentropy.h | 1 - 3 files changed, 76 insertions(+), 120 deletions(-) diff --git a/crypto/jitterentropy-kcapi.c b/crypto/jitterentropy-kcapi.c index 2d115bec15ae..b9edfaa51b27 100644 --- a/crypto/jitterentropy-kcapi.c +++ b/crypto/jitterentropy-kcapi.c @@ -37,6 +37,7 @@ * DAMAGE. */ +#include #include #include #include @@ -59,11 +60,6 @@ void jent_zfree(void *ptr) kfree_sensitive(ptr); } -void jent_panic(char *s) -{ - panic("%s", s); -} - void jent_memcpy(void *dest, const void *src, unsigned int n) { memcpy(dest, src, n); @@ -102,7 +98,6 @@ void jent_get_nstime(__u64 *out) struct jitterentropy { spinlock_t jent_lock; struct rand_data *entropy_collector; - unsigned int reset_cnt; }; static int jent_kcapi_init(struct crypto_tfm *tfm) @@ -138,32 +133,30 @@ static int jent_kcapi_random(struct crypto_rng *tfm, spin_lock(&rng->jent_lock); - /* Return a permanent error in case we had too many resets in a row. */ - if (rng->reset_cnt > (1<<10)) { - ret = -EFAULT; - goto out; - } - ret = jent_read_entropy(rng->entropy_collector, rdata, dlen); - /* Reset RNG in case of health failures */ - if (ret < -1) { - pr_warn_ratelimited("Reset Jitter RNG due to health test failure: %s failure\n", - (ret == -2) ? "Repetition Count Test" : - "Adaptive Proportion Test"); - - rng->reset_cnt++; - + if (ret == -3) { + /* Handle permanent health test error */ + /* + * If the kernel was booted with fips=1, it implies that + * the entire kernel acts as a FIPS 140 module. In this case + * an SP800-90B permanent health test error is treated as + * a FIPS module error. + */ + if (fips_enabled) + panic("Jitter RNG permanent health test failure\n"); + + pr_err("Jitter RNG permanent health test failure\n"); + ret = -EFAULT; + } else if (ret == -2) { + /* Handle intermittent health test error */ + pr_warn_ratelimited("Reset Jitter RNG due to intermittent health test failure\n"); ret = -EAGAIN; - } else { - rng->reset_cnt = 0; - - /* Convert the Jitter RNG error into a usable error code */ - if (ret == -1) - ret = -EINVAL; + } else if (ret == -1) { + /* Handle other errors */ + ret = -EINVAL; } -out: spin_unlock(&rng->jent_lock); return ret; @@ -197,6 +190,10 @@ static int __init jent_mod_init(void) ret = jent_entropy_init(); if (ret) { + /* Handle permanent health test error */ + if (fips_enabled) + panic("jitterentropy: Initialization failed with host not compliant with requirements: %d\n", ret); + pr_info("jitterentropy: Initialization failed with host not compliant with requirements: %d\n", ret); return -EFAULT; } diff --git a/crypto/jitterentropy.c b/crypto/jitterentropy.c index 93bff3213823..22f48bf4c6f5 100644 --- a/crypto/jitterentropy.c +++ b/crypto/jitterentropy.c @@ -85,10 +85,14 @@ struct rand_data { * bit generation */ /* Repetition Count Test */ - int rct_count; /* Number of stuck values */ + unsigned int rct_count; /* Number of stuck values */ - /* Adaptive Proportion Test for a significance level of 2^-30 */ + /* Intermittent health test failure threshold of 2^-30 */ +#define JENT_RCT_CUTOFF 30 /* Taken from SP800-90B sec 4.4.1 */ #define JENT_APT_CUTOFF 325 /* Taken from SP800-90B sec 4.4.2 */ + /* Permanent health test failure threshold of 2^-60 */ +#define JENT_RCT_CUTOFF_PERMANENT 60 +#define JENT_APT_CUTOFF_PERMANENT 355 #define JENT_APT_WINDOW_SIZE 512 /* Data window size */ /* LSB of time stamp to process */ #define JENT_APT_LSB 16 @@ -97,8 +101,6 @@ struct rand_data { unsigned int apt_count; /* APT counter */ unsigned int apt_base; /* APT base reference */ unsigned int apt_base_set:1; /* APT base reference set? */ - - unsigned int health_failure:1; /* Permanent health failure */ }; /* Flags that can be used to initialize the RNG */ @@ -169,19 +171,26 @@ static void jent_apt_insert(struct rand_data *ec, unsigned int delta_masked) return; } - if (delta_masked == ec->apt_base) { + if (delta_masked == ec->apt_base) ec->apt_count++; - if (ec->apt_count >= JENT_APT_CUTOFF) - ec->health_failure = 1; - } - ec->apt_observations++; if (ec->apt_observations >= JENT_APT_WINDOW_SIZE) jent_apt_reset(ec, delta_masked); } +/* APT health test failure detection */ +static int jent_apt_permanent_failure(struct rand_data *ec) +{ + return (ec->apt_count >= JENT_APT_CUTOFF_PERMANENT) ? 1 : 0; +} + +static int jent_apt_failure(struct rand_data *ec) +{ + return (ec->apt_count >= JENT_APT_CUTOFF) ? 1 : 0; +} + /*************************************************************************** * Stuck Test and its use as Repetition Count Test * @@ -206,55 +215,14 @@ static void jent_apt_insert(struct rand_data *ec, unsigned int delta_masked) */ static void jent_rct_insert(struct rand_data *ec, int stuck) { - /* - * If we have a count less than zero, a previous RCT round identified - * a failure. We will not overwrite it. - */ - if (ec->rct_count < 0) - return; - if (stuck) { ec->rct_count++; - - /* - * The cutoff value is based on the following consideration: - * alpha = 2^-30 as recommended in FIPS 140-2 IG 9.8. - * In addition, we require an entropy value H of 1/OSR as this - * is the minimum entropy required to provide full entropy. - * Note, we collect 64 * OSR deltas for inserting them into - * the entropy pool which should then have (close to) 64 bits - * of entropy. - * - * Note, ec->rct_count (which equals to value B in the pseudo - * code of SP800-90B section 4.4.1) starts with zero. Hence - * we need to subtract one from the cutoff value as calculated - * following SP800-90B. - */ - if ((unsigned int)ec->rct_count >= (31 * ec->osr)) { - ec->rct_count = -1; - ec->health_failure = 1; - } } else { + /* Reset RCT */ ec->rct_count = 0; } } -/* - * Is there an RCT health test failure? - * - * @ec [in] Reference to entropy collector - * - * @return - * 0 No health test failure - * 1 Permanent health test failure - */ -static int jent_rct_failure(struct rand_data *ec) -{ - if (ec->rct_count < 0) - return 1; - return 0; -} - static inline __u64 jent_delta(__u64 prev, __u64 next) { #define JENT_UINT64_MAX (__u64)(~((__u64) 0)) @@ -303,18 +271,26 @@ static int jent_stuck(struct rand_data *ec, __u64 current_delta) return 0; } -/* - * Report any health test failures - * - * @ec [in] Reference to entropy collector - * - * @return - * 0 No health test failure - * 1 Permanent health test failure - */ +/* RCT health test failure detection */ +static int jent_rct_permanent_failure(struct rand_data *ec) +{ + return (ec->rct_count >= JENT_RCT_CUTOFF_PERMANENT) ? 1 : 0; +} + +static int jent_rct_failure(struct rand_data *ec) +{ + return (ec->rct_count >= JENT_RCT_CUTOFF) ? 1 : 0; +} + +/* Report of health test failures */ static int jent_health_failure(struct rand_data *ec) { - return ec->health_failure; + return jent_rct_failure(ec) | jent_apt_failure(ec); +} + +static int jent_permanent_health_failure(struct rand_data *ec) +{ + return jent_rct_permanent_failure(ec) | jent_apt_permanent_failure(ec); } /*************************************************************************** @@ -600,8 +576,8 @@ static void jent_gen_entropy(struct rand_data *ec) * * The following error codes can occur: * -1 entropy_collector is NULL - * -2 RCT failed - * -3 APT test failed + * -2 Intermittent health failure + * -3 Permanent health failure */ int jent_read_entropy(struct rand_data *ec, unsigned char *data, unsigned int len) @@ -616,39 +592,23 @@ int jent_read_entropy(struct rand_data *ec, unsigned char *data, jent_gen_entropy(ec); - if (jent_health_failure(ec)) { - int ret; - - if (jent_rct_failure(ec)) - ret = -2; - else - ret = -3; - + if (jent_permanent_health_failure(ec)) { /* - * Re-initialize the noise source - * - * If the health test fails, the Jitter RNG remains - * in failure state and will return a health failure - * during next invocation. + * At this point, the Jitter RNG instance is considered + * as a failed instance. There is no rerun of the + * startup test any more, because the caller + * is assumed to not further use this instance. */ - if (jent_entropy_init()) - return ret; - - /* Set APT to initial state */ - jent_apt_reset(ec, 0); - ec->apt_base_set = 0; - - /* Set RCT to initial state */ - ec->rct_count = 0; - - /* Re-enable Jitter RNG */ - ec->health_failure = 0; - + return -3; + } else if (jent_health_failure(ec)) { /* - * Return the health test failure status to the - * caller as the generated value is not appropriate. + * Perform startup health tests and return permanent + * error if it fails. */ - return ret; + if (jent_entropy_init()) + return -3; + + return -2; } if ((DATA_SIZE_BITS / 8) < len) diff --git a/crypto/jitterentropy.h b/crypto/jitterentropy.h index b7397b617ef0..5cc583f6bc6b 100644 --- a/crypto/jitterentropy.h +++ b/crypto/jitterentropy.h @@ -2,7 +2,6 @@ extern void *jent_zalloc(unsigned int len); extern void jent_zfree(void *ptr); -extern void jent_panic(char *s); extern void jent_memcpy(void *dest, const void *src, unsigned int n); extern void jent_get_nstime(__u64 *out); -- cgit 1.4.1 From f4631d295ae3fff9e240ab78dc17f4b83d14f7bc Mon Sep 17 00:00:00 2001 From: Yonggil Song Date: Tue, 21 Mar 2023 09:12:51 +0900 Subject: f2fs: Fix system crash due to lack of free space in LFS [ Upstream commit d11cef14f8146f3babd286c2cc8ca09c166295e2 ] When f2fs tries to checkpoint during foreground gc in LFS mode, system crash occurs due to lack of free space if the amount of dirty node and dentry pages generated by data migration exceeds free space. The reproduction sequence is as follows. - 20GiB capacity block device (null_blk) - format and mount with LFS mode - create a file and write 20,000MiB - 4k random write on full range of the file RIP: 0010:new_curseg+0x48a/0x510 [f2fs] Code: 55 e7 f5 89 c0 48 0f af c3 48 8b 5d c0 48 c1 e8 20 83 c0 01 89 43 6c 48 83 c4 28 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc <0f> 0b f0 41 80 4f 48 04 45 85 f6 0f 84 ba fd ff ff e9 ef fe ff ff RSP: 0018:ffff977bc397b218 EFLAGS: 00010246 RAX: 00000000000027b9 RBX: 0000000000000000 RCX: 00000000000027c0 RDX: 0000000000000000 RSI: 00000000000027b9 RDI: ffff8c25ab4e74f8 RBP: ffff977bc397b268 R08: 00000000000027b9 R09: ffff8c29e4a34b40 R10: 0000000000000001 R11: ffff977bc397b0d8 R12: 0000000000000000 R13: ffff8c25b4dd81a0 R14: 0000000000000000 R15: ffff8c2f667f9000 FS: 0000000000000000(0000) GS:ffff8c344ec80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000000c00055d000 CR3: 0000000e30810003 CR4: 00000000003706e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: allocate_segment_by_default+0x9c/0x110 [f2fs] f2fs_allocate_data_block+0x243/0xa30 [f2fs] ? __mod_lruvec_page_state+0xa0/0x150 do_write_page+0x80/0x160 [f2fs] f2fs_do_write_node_page+0x32/0x50 [f2fs] __write_node_page+0x339/0x730 [f2fs] f2fs_sync_node_pages+0x5a6/0x780 [f2fs] block_operations+0x257/0x340 [f2fs] f2fs_write_checkpoint+0x102/0x1050 [f2fs] f2fs_gc+0x27c/0x630 [f2fs] ? folio_mark_dirty+0x36/0x70 f2fs_balance_fs+0x16f/0x180 [f2fs] This patch adds checking whether free sections are enough before checkpoint during gc. Signed-off-by: Yonggil Song [Jaegeuk Kim: code clean-up] Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/gc.c | 10 ++++++++-- fs/f2fs/gc.h | 2 ++ fs/f2fs/segment.h | 39 ++++++++++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 5cd19fdc1059..7e497f5b349c 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1811,6 +1811,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control) .iroot = RADIX_TREE_INIT(gc_list.iroot, GFP_NOFS), }; unsigned int skipped_round = 0, round = 0; + unsigned int upper_secs; trace_f2fs_gc_begin(sbi->sb, gc_type, gc_control->no_bg_gc, gc_control->nr_free_secs, @@ -1896,8 +1897,13 @@ retry: } } - /* Write checkpoint to reclaim prefree segments */ - if (free_sections(sbi) < NR_CURSEG_PERSIST_TYPE && + __get_secs_required(sbi, NULL, &upper_secs, NULL); + + /* + * Write checkpoint to reclaim prefree segments. + * We need more three extra sections for writer's data/node/dentry. + */ + if (free_sections(sbi) <= upper_secs + NR_GC_CHECKPOINT_SECS && prefree_segments(sbi)) { ret = f2fs_write_checkpoint(sbi, &cpc); if (ret) diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index ca84024b9c9e..47357101b03c 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -30,6 +30,8 @@ /* Search max. number of dirty segments to select a victim segment */ #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */ +#define NR_GC_CHECKPOINT_SECS (3) /* data/node/dentry sections */ + struct f2fs_gc_kthread { struct task_struct *f2fs_gc_task; wait_queue_head_t gc_wait_queue_head; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index cd65778fc982..f3951e8ad394 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -608,8 +608,12 @@ static inline bool has_curseg_enough_space(struct f2fs_sb_info *sbi, return true; } -static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, - int freed, int needed) +/* + * calculate needed sections for dirty node/dentry + * and call has_curseg_enough_space + */ +static inline void __get_secs_required(struct f2fs_sb_info *sbi, + unsigned int *lower_p, unsigned int *upper_p, bool *curseg_p) { unsigned int total_node_blocks = get_pages(sbi, F2FS_DIRTY_NODES) + get_pages(sbi, F2FS_DIRTY_DENTS) + @@ -619,20 +623,37 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, unsigned int dent_secs = total_dent_blocks / CAP_BLKS_PER_SEC(sbi); unsigned int node_blocks = total_node_blocks % CAP_BLKS_PER_SEC(sbi); unsigned int dent_blocks = total_dent_blocks % CAP_BLKS_PER_SEC(sbi); - unsigned int free, need_lower, need_upper; + + if (lower_p) + *lower_p = node_secs + dent_secs; + if (upper_p) + *upper_p = node_secs + dent_secs + + (node_blocks ? 1 : 0) + (dent_blocks ? 1 : 0); + if (curseg_p) + *curseg_p = has_curseg_enough_space(sbi, + node_blocks, dent_blocks); +} + +static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, + int freed, int needed) +{ + unsigned int free_secs, lower_secs, upper_secs; + bool curseg_space; if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return false; - free = free_sections(sbi) + freed; - need_lower = node_secs + dent_secs + reserved_sections(sbi) + needed; - need_upper = need_lower + (node_blocks ? 1 : 0) + (dent_blocks ? 1 : 0); + __get_secs_required(sbi, &lower_secs, &upper_secs, &curseg_space); + + free_secs = free_sections(sbi) + freed; + lower_secs += needed + reserved_sections(sbi); + upper_secs += needed + reserved_sections(sbi); - if (free > need_upper) + if (free_secs > upper_secs) return false; - else if (free <= need_lower) + else if (free_secs <= lower_secs) return true; - return !has_curseg_enough_space(sbi, node_blocks, dent_blocks); + return !curseg_space; } static inline bool f2fs_is_checkpoint_ready(struct f2fs_sb_info *sbi) -- cgit 1.4.1 From 7741ddc882a0c806a6508ba8203c55a779db7a21 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 10 Apr 2023 10:12:22 +0800 Subject: f2fs: fix to drop all dirty pages during umount() if cp_error is set [ Upstream commit c9b3649a934d131151111354bcbb638076f03a30 ] xfstest generic/361 reports a bug as below: f2fs_bug_on(sbi, sbi->fsync_node_num); kernel BUG at fs/f2fs/super.c:1627! RIP: 0010:f2fs_put_super+0x3a8/0x3b0 Call Trace: generic_shutdown_super+0x8c/0x1b0 kill_block_super+0x2b/0x60 kill_f2fs_super+0x87/0x110 deactivate_locked_super+0x39/0x80 deactivate_super+0x46/0x50 cleanup_mnt+0x109/0x170 __cleanup_mnt+0x16/0x20 task_work_run+0x65/0xa0 exit_to_user_mode_prepare+0x175/0x190 syscall_exit_to_user_mode+0x25/0x50 do_syscall_64+0x4c/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc During umount(), if cp_error is set, f2fs_wait_on_all_pages() should not stop waiting all F2FS_WB_CP_DATA pages to be writebacked, otherwise, fsync_node_num can be non-zero after f2fs_wait_on_all_pages() causing this bug. In this case, to avoid deadloop in f2fs_wait_on_all_pages(), it needs to drop all dirty pages rather than redirtying them. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/checkpoint.c | 12 ++++++++++-- fs/f2fs/data.c | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 0c82dae082aa..5df04ed010ca 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -322,8 +322,15 @@ static int __f2fs_write_meta_page(struct page *page, trace_f2fs_writepage(page, META); - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) { + if (is_sbi_flag_set(sbi, SBI_IS_CLOSE)) { + ClearPageUptodate(page); + dec_page_count(sbi, F2FS_DIRTY_META); + unlock_page(page); + return 0; + } goto redirty_out; + } if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0)) @@ -1301,7 +1308,8 @@ void f2fs_wait_on_all_pages(struct f2fs_sb_info *sbi, int type) if (!get_pages(sbi, type)) break; - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi) && + !is_sbi_flag_set(sbi, SBI_IS_CLOSE))) break; if (type == F2FS_DIRTY_META) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index de6b056f090b..36db9aab4779 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2788,7 +2788,8 @@ int f2fs_write_single_data_page(struct page *page, int *submitted, * don't drop any dirty dentry pages for keeping lastest * directory structure. */ - if (S_ISDIR(inode->i_mode)) + if (S_ISDIR(inode->i_mode) && + !is_sbi_flag_set(sbi, SBI_IS_CLOSE)) goto redirty_out; goto out; } -- cgit 1.4.1 From e05d63f8b48aad4613bd582c945bee41e2dd7255 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 4 Apr 2023 23:28:07 +0800 Subject: f2fs: fix to check readonly condition correctly [ Upstream commit d78dfefcde9d311284434560d69c0478c55a657e ] With below case, it can mount multi-device image w/ rw option, however one of secondary device is set as ro, later update will cause panic, so let's introduce f2fs_dev_is_readonly(), and check multi-devices rw status in f2fs_remount() w/ it in order to avoid such inconsistent mount status. mkfs.f2fs -c /dev/zram1 /dev/zram0 -f blockdev --setro /dev/zram1 mount -t f2fs dev/zram0 /mnt/f2fs mount: /mnt/f2fs: WARNING: source write-protected, mounted read-only. mount -t f2fs -o remount,rw mnt/f2fs dd if=/dev/zero of=/mnt/f2fs/file bs=1M count=8192 kernel BUG at fs/f2fs/inline.c:258! RIP: 0010:f2fs_write_inline_data+0x23e/0x2d0 [f2fs] Call Trace: f2fs_write_single_data_page+0x26b/0x9f0 [f2fs] f2fs_write_cache_pages+0x389/0xa60 [f2fs] __f2fs_write_data_pages+0x26b/0x2d0 [f2fs] f2fs_write_data_pages+0x2e/0x40 [f2fs] do_writepages+0xd3/0x1b0 __writeback_single_inode+0x5b/0x420 writeback_sb_inodes+0x236/0x5a0 __writeback_inodes_wb+0x56/0xf0 wb_writeback+0x2a3/0x490 wb_do_writeback+0x2b2/0x330 wb_workfn+0x6a/0x260 process_one_work+0x270/0x5e0 worker_thread+0x52/0x3e0 kthread+0xf4/0x120 ret_from_fork+0x29/0x50 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/f2fs.h | 5 +++++ fs/f2fs/super.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a0a232551da9..8d7dc76e6f93 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4435,6 +4435,11 @@ static inline bool f2fs_hw_is_readonly(struct f2fs_sb_info *sbi) return false; } +static inline bool f2fs_dev_is_readonly(struct f2fs_sb_info *sbi) +{ + return f2fs_sb_has_readonly(sbi) || f2fs_hw_is_readonly(sbi); +} + static inline bool f2fs_lfs_mode(struct f2fs_sb_info *sbi) { return F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c46533d65372..b6dad389fa14 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2258,7 +2258,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) if (f2fs_readonly(sb) && (*flags & SB_RDONLY)) goto skip; - if (f2fs_sb_has_readonly(sbi) && !(*flags & SB_RDONLY)) { + if (f2fs_dev_is_readonly(sbi) && !(*flags & SB_RDONLY)) { err = -EROFS; goto restore_opts; } -- cgit 1.4.1 From f2065b8b0a215bc6aa061287a2e3d9eab2446422 Mon Sep 17 00:00:00 2001 From: Hao Zeng Date: Tue, 11 Apr 2023 16:43:49 +0800 Subject: samples/bpf: Fix fout leak in hbm's run_bpf_prog [ Upstream commit 23acb14af1914010dd0aae1bbb7fab28bf518b8e ] Fix fout being fopen'ed but then not subsequently fclose'd. In the affected branch, fout is otherwise going out of scope. Signed-off-by: Hao Zeng Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20230411084349.1999628-1-zenghao@kylinos.cn Signed-off-by: Sasha Levin --- samples/bpf/hbm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/bpf/hbm.c b/samples/bpf/hbm.c index 516fbac28b71..7f89700a17b6 100644 --- a/samples/bpf/hbm.c +++ b/samples/bpf/hbm.c @@ -315,6 +315,7 @@ static int run_bpf_prog(char *prog, int cg_id) fout = fopen(fname, "w"); fprintf(fout, "id:%d\n", cg_id); fprintf(fout, "ERROR: Could not lookup queue_stats\n"); + fclose(fout); } else if (stats_flag && qstats.lastPacketTime > qstats.firstPacketTime) { long long delta_us = (qstats.lastPacketTime - -- cgit 1.4.1 From 60039bf72f81638baa28652a11a68e9b0b7b5b2d Mon Sep 17 00:00:00 2001 From: Yafang Date: Thu, 13 Apr 2023 02:52:48 +0000 Subject: bpf: Add preempt_count_{sub,add} into btf id deny list [ Upstream commit c11bd046485d7bf1ca200db0e7d0bdc4bafdd395 ] The recursion check in __bpf_prog_enter* and __bpf_prog_exit* leave preempt_count_{sub,add} unprotected. When attaching trampoline to them we get panic as follows, [ 867.843050] BUG: TASK stack guard page was hit at 0000000009d325cf (stack is 0000000046a46a15..00000000537e7b28) [ 867.843064] stack guard page: 0000 [#1] PREEMPT SMP NOPTI [ 867.843067] CPU: 8 PID: 11009 Comm: trace Kdump: loaded Not tainted 6.2.0+ #4 [ 867.843100] Call Trace: [ 867.843101] [ 867.843104] asm_exc_int3+0x3a/0x40 [ 867.843108] RIP: 0010:preempt_count_sub+0x1/0xa0 [ 867.843135] __bpf_prog_enter_recur+0x17/0x90 [ 867.843148] bpf_trampoline_6442468108_0+0x2e/0x1000 [ 867.843154] ? preempt_count_sub+0x1/0xa0 [ 867.843157] preempt_count_sub+0x5/0xa0 [ 867.843159] ? migrate_enable+0xac/0xf0 [ 867.843164] __bpf_prog_exit_recur+0x2d/0x40 [ 867.843168] bpf_trampoline_6442468108_0+0x55/0x1000 ... [ 867.843788] preempt_count_sub+0x5/0xa0 [ 867.843793] ? migrate_enable+0xac/0xf0 [ 867.843829] __bpf_prog_exit_recur+0x2d/0x40 [ 867.843837] BUG: IRQ stack guard page was hit at 0000000099bd8228 (stack is 00000000b23e2bc4..000000006d95af35) [ 867.843841] BUG: IRQ stack guard page was hit at 000000005ae07924 (stack is 00000000ffd69623..0000000014eb594c) [ 867.843843] BUG: IRQ stack guard page was hit at 00000000028320f0 (stack is 00000000034b6438..0000000078d1bcec) [ 867.843842] bpf_trampoline_6442468108_0+0x55/0x1000 ... That is because in __bpf_prog_exit_recur, the preempt_count_{sub,add} are called after prog->active is decreased. Fixing this by adding these two functions into btf ids deny list. Suggested-by: Steven Rostedt Signed-off-by: Yafang Cc: Masami Hiramatsu Cc: Steven Rostedt Cc: Jiri Olsa Acked-by: Hao Luo Link: https://lore.kernel.org/r/20230413025248.79764-1-laoar.shao@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 872616107613..322a2ae8f88b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -15133,6 +15133,10 @@ BTF_ID(func, migrate_enable) #if !defined CONFIG_PREEMPT_RCU && !defined CONFIG_TINY_RCU BTF_ID(func, rcu_read_unlock_strict) #endif +#if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_TRACE_PREEMPT_TOGGLE) +BTF_ID(func, preempt_count_add) +BTF_ID(func, preempt_count_sub) +#endif BTF_SET_END(btf_id_deny) static int check_attach_btf_id(struct bpf_verifier_env *env) -- cgit 1.4.1 From b4acb6c3ede88d6b7d33742a09e63cfce5e7fb69 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Fri, 10 Mar 2023 15:38:51 +0800 Subject: md: fix soft lockup in status_resync [ Upstream commit 6efddf1e32e2a264694766ca485a4f5e04ee82a7 ] status_resync() will calculate 'curr_resync - recovery_active' to show user a progress bar like following: [============>........] resync = 61.4% 'curr_resync' and 'recovery_active' is updated in md_do_sync(), and status_resync() can read them concurrently, hence it's possible that 'curr_resync - recovery_active' can overflow to a huge number. In this case status_resync() will be stuck in the loop to print a large amount of '=', which will end up soft lockup. Fix the problem by setting 'resync' to MD_RESYNC_ACTIVE in this case, this way resync in progress will be reported to user. Signed-off-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230310073855.1337560-3-yukuai1@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/md.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index d5c362b1602b..bb73a541bb19 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -8028,16 +8028,16 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev) } else if (resync > max_sectors) { resync = max_sectors; } else { - resync -= atomic_read(&mddev->recovery_active); - if (resync < MD_RESYNC_ACTIVE) { - /* - * Resync has started, but the subtraction has - * yielded one of the special values. Force it - * to active to ensure the status reports an - * active resync. - */ + res = atomic_read(&mddev->recovery_active); + /* + * Resync has started, but the subtraction has overflowed or + * yielded one of the special values. Force it to active to + * ensure the status reports an active resync. + */ + if (resync < res || resync - res < MD_RESYNC_ACTIVE) resync = MD_RESYNC_ACTIVE; - } + else + resync -= res; } if (resync == MD_RESYNC_NONE) { -- cgit 1.4.1 From 0f9a1bcb94016d3a3c455a77b01f6bb06e15f6eb Mon Sep 17 00:00:00 2001 From: Daniel Gabay Date: Thu, 13 Apr 2023 21:40:32 +0300 Subject: wifi: iwlwifi: pcie: fix possible NULL pointer dereference [ Upstream commit b655b9a9f8467684cfa8906713d33b71ea8c8f54 ] It is possible that iwl_pci_probe() will fail and free the trans, then afterwards iwl_pci_remove() will be called and crash by trying to access trans which is already freed, fix it. iwlwifi 0000:01:00.0: Detected crf-id 0xa5a5a5a2, cnv-id 0xa5a5a5a2 wfpm id 0xa5a5a5a2 iwlwifi 0000:01:00.0: Can't find a correct rfid for crf id 0x5a2 ... BUG: kernel NULL pointer dereference, address: 0000000000000028 ... RIP: 0010:iwl_pci_remove+0x12/0x30 [iwlwifi] pci_device_remove+0x3e/0xb0 device_release_driver_internal+0x103/0x1f0 driver_detach+0x4c/0x90 bus_remove_driver+0x5c/0xd0 driver_unregister+0x31/0x50 pci_unregister_driver+0x40/0x90 iwl_pci_unregister_driver+0x15/0x20 [iwlwifi] __exit_compat+0x9/0x98 [iwlwifi] __x64_sys_delete_module+0x147/0x260 Signed-off-by: Daniel Gabay Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230413213309.082f6e21341b.I0db21d7fa9a828d571ca886713bd0b5d0b6e1e5c@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 85fadd1ef1ff..afe6cc15c845 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1685,6 +1685,9 @@ static void iwl_pci_remove(struct pci_dev *pdev) { struct iwl_trans *trans = pci_get_drvdata(pdev); + if (!trans) + return; + iwl_drv_stop(trans->drv); iwl_trans_pcie_free(trans); -- cgit 1.4.1 From 19f063df737b2f9198f7e31860108ab0e6445204 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Fri, 14 Apr 2023 13:11:53 +0300 Subject: wifi: iwlwifi: add a new PCI device ID for BZ device [ Upstream commit c30a2a64788b3d617a9c5d96adb76c68b0862e5f ] Add support for a new PCI device ID 0x272b once registering with PCIe. Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230414130637.56342664110d.I5aa6f2858fdcf69fdea4f1a873115a48bd43764e@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index afe6cc15c845..03e8234d0352 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -504,6 +504,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* Bz devices */ {IWL_PCI_DEVICE(0x2727, PCI_ANY_ID, iwl_bz_trans_cfg)}, + {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_bz_trans_cfg)}, {IWL_PCI_DEVICE(0xA840, PCI_ANY_ID, iwl_bz_trans_cfg)}, {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_trans_cfg)}, #endif /* CONFIG_IWLMVM */ -- cgit 1.4.1 From eb1ef44efac797b384d361a76e33f77027c29a14 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 14 Apr 2023 13:11:59 +0300 Subject: wifi: iwlwifi: pcie: Fix integer overflow in iwl_write_to_user_buf [ Upstream commit 58d1b717879bfeabe09b35e41ad667c79933eb2e ] An integer overflow occurs in the iwl_write_to_user_buf() function, which is called by the iwl_dbgfs_monitor_data_read() function. static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count, void *buf, ssize_t *size, ssize_t *bytes_copied) { int buf_size_left = count - *bytes_copied; buf_size_left = buf_size_left - (buf_size_left % sizeof(u32)); if (*size > buf_size_left) *size = buf_size_left; If the user passes a SIZE_MAX value to the "ssize_t count" parameter, the ssize_t count parameter is assigned to "int buf_size_left". Then compare "*size" with "buf_size_left" . Here, "buf_size_left" is a negative number, so "*size" is assigned "buf_size_left" and goes into the third argument of the copy_to_user function, causing a heap overflow. This is not a security vulnerability because iwl_dbgfs_monitor_data_read() is a debugfs operation with 0400 privileges. Signed-off-by: Hyunwoo Kim Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230414130637.2d80ace81532.Iecfba549e0e0be21bbb0324675392e42e75bd5ad@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 54f11f60f11c..8e95225cdd60 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2852,7 +2852,7 @@ static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count, void *buf, ssize_t *size, ssize_t *bytes_copied) { - int buf_size_left = count - *bytes_copied; + ssize_t buf_size_left = count - *bytes_copied; buf_size_left = buf_size_left - (buf_size_left % sizeof(u32)); if (*size > buf_size_left) -- cgit 1.4.1 From dbebdee3f2e980ea187f1919bcad113c7bf45b2c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Apr 2023 13:12:02 +0300 Subject: wifi: iwlwifi: mvm: fix ptk_pn memory leak [ Upstream commit d066a530af8e1833c7ea2cef7784004700c85f79 ] If adding a key to firmware fails we leak the allocated ptk_pn. This shouldn't happen in practice, but we should still fix it. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230414130637.99446ffd02bc.I82a2ad6ec1395f188e0a1677cc619e3fcb1feac9@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index a841268e0709..801098c5183b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3445,7 +3445,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_sta *mvmsta = NULL; - struct iwl_mvm_key_pn *ptk_pn; + struct iwl_mvm_key_pn *ptk_pn = NULL; int keyidx = key->keyidx; int ret, i; u8 key_offset; @@ -3590,6 +3590,10 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, if (ret) { IWL_WARN(mvm, "set key failed\n"); key->hw_key_idx = STA_KEY_IDX_INVALID; + if (ptk_pn) { + RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL); + kfree(ptk_pn); + } /* * can't add key for RX, but we don't need it * in the device for TX so still return 0, -- cgit 1.4.1 From 1655cfc85250a224b0d9486c8136baeea33b9b5c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 13 Apr 2023 14:30:09 +0100 Subject: block, bfq: Fix division by zero error on zero wsum [ Upstream commit e53413f8deedf738a6782cc14cc00bd5852ccf18 ] When the weighted sum is zero the calculation of limit causes a division by zero error. Fix this by continuing to the next level. This was discovered by running as root: stress-ng --ioprio 0 Fixes divison by error oops: [ 521.450556] divide error: 0000 [#1] SMP NOPTI [ 521.450766] CPU: 2 PID: 2684464 Comm: stress-ng-iopri Not tainted 6.2.1-1280.native #1 [ 521.451117] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.1-0-g3208b098f51a-prebuilt.qemu.org 04/01/2014 [ 521.451627] RIP: 0010:bfqq_request_over_limit+0x207/0x400 [ 521.451875] Code: 01 48 8d 0c c8 74 0b 48 8b 82 98 00 00 00 48 8d 0c c8 8b 85 34 ff ff ff 48 89 ca 41 0f af 41 50 48 d1 ea 48 98 48 01 d0 31 d2 <48> f7 f1 41 39 41 48 89 85 34 ff ff ff 0f 8c 7b 01 00 00 49 8b 44 [ 521.452699] RSP: 0018:ffffb1af84eb3948 EFLAGS: 00010046 [ 521.452938] RAX: 000000000000003c RBX: 0000000000000000 RCX: 0000000000000000 [ 521.453262] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffb1af84eb3978 [ 521.453584] RBP: ffffb1af84eb3a30 R08: 0000000000000001 R09: ffff8f88ab8a4ba0 [ 521.453905] R10: 0000000000000000 R11: 0000000000000001 R12: ffff8f88ab8a4b18 [ 521.454224] R13: ffff8f8699093000 R14: 0000000000000001 R15: ffffb1af84eb3970 [ 521.454549] FS: 00005640b6b0b580(0000) GS:ffff8f88b3880000(0000) knlGS:0000000000000000 [ 521.454912] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 521.455170] CR2: 00007ffcbcae4e38 CR3: 00000002e46de001 CR4: 0000000000770ee0 [ 521.455491] PKRU: 55555554 [ 521.455619] Call Trace: [ 521.455736] [ 521.455837] ? bfq_request_merge+0x3a/0xc0 [ 521.456027] ? elv_merge+0x115/0x140 [ 521.456191] bfq_limit_depth+0xc8/0x240 [ 521.456366] __blk_mq_alloc_requests+0x21a/0x2c0 [ 521.456577] blk_mq_submit_bio+0x23c/0x6c0 [ 521.456766] __submit_bio+0xb8/0x140 [ 521.457236] submit_bio_noacct_nocheck+0x212/0x300 [ 521.457748] submit_bio_noacct+0x1a6/0x580 [ 521.458220] submit_bio+0x43/0x80 [ 521.458660] ext4_io_submit+0x23/0x80 [ 521.459116] ext4_do_writepages+0x40a/0xd00 [ 521.459596] ext4_writepages+0x65/0x100 [ 521.460050] do_writepages+0xb7/0x1c0 [ 521.460492] __filemap_fdatawrite_range+0xa6/0x100 [ 521.460979] file_write_and_wait_range+0xbf/0x140 [ 521.461452] ext4_sync_file+0x105/0x340 [ 521.461882] __x64_sys_fsync+0x67/0x100 [ 521.462305] ? syscall_exit_to_user_mode+0x2c/0x1c0 [ 521.462768] do_syscall_64+0x3b/0xc0 [ 521.463165] entry_SYSCALL_64_after_hwframe+0x5a/0xc4 [ 521.463621] RIP: 0033:0x5640b6c56590 [ 521.464006] Code: 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 80 3d 71 70 0e 00 00 74 17 b8 4a 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 48 c3 0f 1f 80 00 00 00 00 48 83 ec 18 89 7c Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20230413133009.1605335-1-colin.i.king@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/bfq-iosched.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index ff9d23889415..52eb79d60a3f 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -637,6 +637,8 @@ retry: sched_data->service_tree[i].wsum; } } + if (!wsum) + continue; limit = DIV_ROUND_CLOSEST(limit * entity->weight, wsum); if (entity->allocated >= limit) { bfq_log_bfqq(bfqq->bfqd, bfqq, -- cgit 1.4.1 From e78526a06b53718bfc1dfff37864c7760e41f8ec Mon Sep 17 00:00:00 2001 From: Harshitha Prem Date: Tue, 4 Apr 2023 00:11:54 +0530 Subject: wifi: ath11k: Ignore frags from uninitialized peer in dp. [ Upstream commit a06bfb3c9f69f303692cdae87bc0899d2ae8b2a6 ] When max virtual ap interfaces are configured in all the bands with ACS and hostapd restart is done every 60s, a crash is observed at random times. In this certain scenario, a fragmented packet is received for self peer, for which rx_tid and rx_frags are not initialized in datapath. While handling this fragment, crash is observed as the rx_frag list is uninitialised and when we walk in ath11k_dp_rx_h_sort_frags, skb null leads to exception. To address this, before processing received fragments we check dp_setup_done flag is set to ensure that peer has completed its dp peer setup for fragment queue, else ignore processing the fragments. Call trace: ath11k_dp_process_rx_err+0x550/0x1084 [ath11k] ath11k_dp_service_srng+0x70/0x370 [ath11k] 0xffffffc009693a04 __napi_poll+0x30/0xa4 net_rx_action+0x118/0x270 __do_softirq+0x10c/0x244 irq_exit+0x64/0xb4 __handle_domain_irq+0x88/0xac gic_handle_irq+0x74/0xbc el1_irq+0xf0/0x1c0 arch_cpu_idle+0x10/0x18 do_idle+0x104/0x248 cpu_startup_entry+0x20/0x64 rest_init+0xd0/0xdc arch_call_rest_init+0xc/0x14 start_kernel+0x480/0x4b8 Code: f9400281 f94066a2 91405021 b94a0023 (f9406401) Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Harshitha Prem Signed-off-by: Nagarajan Maran Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230403184155.8670-2-quic_nmaran@quicinc.com Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/dp.c | 4 +++- drivers/net/wireless/ath/ath11k/dp_rx.c | 8 ++++++++ drivers/net/wireless/ath/ath11k/peer.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index f5156a7fbdd7..d070bcb3fe24 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -36,6 +36,7 @@ void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr) } ath11k_peer_rx_tid_cleanup(ar, peer); + peer->dp_setup_done = false; crypto_free_shash(peer->tfm_mmic); spin_unlock_bh(&ab->base_lock); } @@ -72,7 +73,8 @@ int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr) ret = ath11k_peer_rx_frag_setup(ar, addr, vdev_id); if (ret) { ath11k_warn(ab, "failed to setup rx defrag context\n"); - return ret; + tid--; + goto peer_clean; } /* TODO: Setup other peer specific resource used in data path */ diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index e964e1b72287..1786d83f8f2e 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -3138,6 +3138,7 @@ int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id } peer->tfm_mmic = tfm; + peer->dp_setup_done = true; spin_unlock_bh(&ab->base_lock); return 0; @@ -3583,6 +3584,13 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, ret = -ENOENT; goto out_unlock; } + if (!peer->dp_setup_done) { + ath11k_warn(ab, "The peer %pM [%d] has uninitialized datapath\n", + peer->addr, peer_id); + ret = -ENOENT; + goto out_unlock; + } + rx_tid = &peer->rx_tid[tid]; if ((!skb_queue_empty(&rx_tid->rx_frags) && seqno != rx_tid->cur_sn) || diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h index 6dd17bafe3a0..9bd385d0a38c 100644 --- a/drivers/net/wireless/ath/ath11k/peer.h +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -35,6 +35,7 @@ struct ath11k_peer { u16 sec_type; u16 sec_type_grp; bool is_authorized; + bool dp_setup_done; }; void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); -- cgit 1.4.1 From 63e2d06adf6b0842132ba89efdf8fada5f7ff1ac Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Apr 2023 11:41:30 +0300 Subject: wifi: iwlwifi: fix iwl_mvm_max_amsdu_size() for MLO [ Upstream commit b2bc600cced23762d4e97db8989b18772145604f ] For MLO, we cannot use vif->bss_conf.chandef.chan->band, since that will lead to a NULL-ptr dereference as bss_conf isn't used. However, in case of real MLO, we also need to take both LMACs into account if they exist, since the station might be active on both LMACs at the same time. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230417113648.3588afc85d79.I11592893bbc191b9548518b8bd782de568a9f848@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 37 ++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index ba944175546d..542cfcad6e0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -788,10 +788,11 @@ unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, struct ieee80211_sta *sta, unsigned int tid) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band; u8 ac = tid_to_mac80211_ac[tid]; + enum nl80211_band band; unsigned int txf; - int lmac = iwl_mvm_get_lmac_id(mvm->fw, band); + unsigned int val; + int lmac; /* For HE redirect to trigger based fifos */ if (sta->deflink.he_cap.has_he && !WARN_ON(!iwl_mvm_has_new_tx_api(mvm))) @@ -805,7 +806,37 @@ unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, * We also want to have the start of the next packet inside the * fifo to be able to send bursts. */ - return min_t(unsigned int, mvmsta->max_amsdu_len, + val = mvmsta->max_amsdu_len; + + if (hweight16(sta->valid_links) <= 1) { + if (sta->valid_links) { + struct ieee80211_bss_conf *link_conf; + unsigned int link = ffs(sta->valid_links) - 1; + + rcu_read_lock(); + link_conf = rcu_dereference(mvmsta->vif->link_conf[link]); + if (WARN_ON(!link_conf)) + band = NL80211_BAND_2GHZ; + else + band = link_conf->chandef.chan->band; + rcu_read_unlock(); + } else { + band = mvmsta->vif->bss_conf.chandef.chan->band; + } + + lmac = iwl_mvm_get_lmac_id(mvm->fw, band); + } else if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CDB_SUPPORT)) { + /* for real MLO restrict to both LMACs if they exist */ + lmac = IWL_LMAC_5G_INDEX; + val = min_t(unsigned int, val, + mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); + lmac = IWL_LMAC_24G_INDEX; + } else { + lmac = IWL_LMAC_24G_INDEX; + } + + return min_t(unsigned int, val, mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); } -- cgit 1.4.1 From fd35b7bb6d5a329c427924886949ab51f210200a Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Sun, 16 Apr 2023 15:03:39 -0700 Subject: null_blk: Always check queue mode setting from configfs [ Upstream commit 63f8793ee60513a09f110ea460a6ff2c33811cdb ] Make sure to check device queue mode in the null_validate_conf() and return error for NULL_Q_RQ as we don't allow legacy I/O path, without this patch we get OOPs when queue mode is set to 1 from configfs, following are repro steps :- modprobe null_blk nr_devices=0 mkdir config/nullb/nullb0 echo 1 > config/nullb/nullb0/memory_backed echo 4096 > config/nullb/nullb0/blocksize echo 20480 > config/nullb/nullb0/size echo 1 > config/nullb/nullb0/queue_mode echo 1 > config/nullb/nullb0/power Entering kdb (current=0xffff88810acdd080, pid 2372) on processor 42 Oops: (null) due to oops @ 0xffffffffc041c329 CPU: 42 PID: 2372 Comm: sh Tainted: G O N 6.3.0-rc5lblk+ #5 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a1990b-prebuilt.qemu.org 04/01/2014 RIP: 0010:null_add_dev.part.0+0xd9/0x720 [null_blk] Code: 01 00 00 85 d2 0f 85 a1 03 00 00 48 83 bb 08 01 00 00 00 0f 85 f7 03 00 00 80 bb 62 01 00 00 00 48 8b 75 20 0f 85 6d 02 00 00 <48> 89 6e 60 48 8b 75 20 bf 06 00 00 00 e8 f5 37 2c c1 48 8b 75 20 RSP: 0018:ffffc900052cbde0 EFLAGS: 00010246 RAX: 0000000000000001 RBX: ffff88811084d800 RCX: 0000000000000001 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888100042e00 RBP: ffff8881053d8200 R08: ffffc900052cbd68 R09: ffff888105db2000 R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000002 R13: ffff888104765200 R14: ffff88810eec1748 R15: ffff88810eec1740 FS: 00007fd445fd1740(0000) GS:ffff8897dfc80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000060 CR3: 0000000166a00000 CR4: 0000000000350ee0 DR0: ffffffff8437a488 DR1: ffffffff8437a489 DR2: ffffffff8437a48a DR3: ffffffff8437a48b DR6: 00000000ffff0ff0 DR7: 0000000000000400 Call Trace: nullb_device_power_store+0xd1/0x120 [null_blk] configfs_write_iter+0xb4/0x120 vfs_write+0x2ba/0x3c0 ksys_write+0x5f/0xe0 do_syscall_64+0x3b/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc RIP: 0033:0x7fd4460c57a7 Code: 0d 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 RSP: 002b:00007ffd3792a4a8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000000000000002 RCX: 00007fd4460c57a7 RDX: 0000000000000002 RSI: 000055b43c02e4c0 RDI: 0000000000000001 RBP: 000055b43c02e4c0 R08: 000000000000000a R09: 00007fd44615b4e0 R10: 00007fd44615b3e0 R11: 0000000000000246 R12: 0000000000000002 R13: 00007fd446198520 R14: 0000000000000002 R15: 00007fd446198700 Signed-off-by: Chaitanya Kulkarni Reviewed-by: Damien Le Moal Reviewed-by: Ming Lei Reviewed-by: Nitesh Shetty Link: https://lore.kernel.org/r/20230416220339.43845-1-kch@nvidia.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/block/null_blk/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index af419af9a0f4..c45d09a9a942 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -1945,6 +1945,11 @@ static int null_init_tag_set(struct nullb *nullb, struct blk_mq_tag_set *set) static int null_validate_conf(struct nullb_device *dev) { + if (dev->queue_mode == NULL_Q_RQ) { + pr_err("legacy IO path is no longer available\n"); + return -EINVAL; + } + dev->blocksize = round_down(dev->blocksize, 512); dev->blocksize = clamp_t(unsigned int, dev->blocksize, 512, 4096); -- cgit 1.4.1 From 57189c885149825be8eb8c3524b5af017fdeb941 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 18 Apr 2023 15:25:46 +0200 Subject: wifi: iwlwifi: dvm: Fix memcpy: detected field-spanning write backtrace [ Upstream commit ef16799640865f937719f0771c93be5dca18adc6 ] A received TKIP key may be up to 32 bytes because it may contain MIC rx/tx keys too. These are not used by iwl and copying these over overflows the iwl_keyinfo.key field. Add a check to not copy more data to iwl_keyinfo.key then will fit. This fixes backtraces like this one: memcpy: detected field-spanning write (size 32) of single field "sta_cmd.key.key" at drivers/net/wireless/intel/iwlwifi/dvm/sta.c:1103 (size 16) WARNING: CPU: 1 PID: 946 at drivers/net/wireless/intel/iwlwifi/dvm/sta.c:1103 iwlagn_send_sta_key+0x375/0x390 [iwldvm] Hardware name: Dell Inc. Latitude E6430/0H3MT5, BIOS A21 05/08/2017 RIP: 0010:iwlagn_send_sta_key+0x375/0x390 [iwldvm] Call Trace: iwl_set_dynamic_key+0x1f0/0x220 [iwldvm] iwlagn_mac_set_key+0x1e4/0x280 [iwldvm] drv_set_key+0xa4/0x1b0 [mac80211] ieee80211_key_enable_hw_accel+0xa8/0x2d0 [mac80211] ieee80211_key_replace+0x22d/0x8e0 [mac80211] Link: https://www.alionet.org/index.php?topic=1469.0 Link: https://lore.kernel.org/linux-wireless/20230218191056.never.374-kees@kernel.org/ Link: https://lore.kernel.org/linux-wireless/68760035-7f75-1b23-e355-bfb758a87d83@redhat.com/ Cc: Kees Cook Suggested-by: Johannes Berg Signed-off-by: Hans de Goede Reviewed-by: Kees Cook Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/dvm/sta.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c index cef43cf80620..8b01ab986cb1 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c @@ -1081,6 +1081,7 @@ static int iwlagn_send_sta_key(struct iwl_priv *priv, { __le16 key_flags; struct iwl_addsta_cmd sta_cmd; + size_t to_copy; int i; spin_lock_bh(&priv->sta_lock); @@ -1100,7 +1101,9 @@ static int iwlagn_send_sta_key(struct iwl_priv *priv, sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; for (i = 0; i < 5; i++) sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); + /* keyconf may contain MIC rx/tx keys which iwl does not use */ + to_copy = min_t(size_t, sizeof(sta_cmd.key.key), keyconf->keylen); + memcpy(sta_cmd.key.key, keyconf->key, to_copy); break; case WLAN_CIPHER_SUITE_WEP104: key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; -- cgit 1.4.1 From 068fd06148fbf0af95bb08dc77cff34ee679fdbc Mon Sep 17 00:00:00 2001 From: Nagarajan Maran Date: Mon, 17 Apr 2023 13:35:02 +0300 Subject: wifi: ath11k: Fix SKB corruption in REO destination ring [ Upstream commit f9fff67d2d7ca6fa8066132003a3deef654c55b1 ] While running traffics for a long time, randomly an RX descriptor filled with value "0" from REO destination ring is received. This descriptor which is invalid causes the wrong SKB (SKB stored in the IDR lookup with buffer id "0") to be fetched which in turn causes SKB memory corruption issue and the same leads to crash after some time. Changed the start id for idr allocation to "1" and the buffer id "0" is reserved for error validation. Introduced Sanity check to validate the descriptor, before processing the SKB. Crash Signature : Unable to handle kernel paging request at virtual address 3f004900 PC points to "b15_dma_inv_range+0x30/0x50" LR points to "dma_cache_maint_page+0x8c/0x128". The Backtrace obtained is as follows: [<8031716c>] (b15_dma_inv_range) from [<80313a4c>] (dma_cache_maint_page+0x8c/0x128) [<80313a4c>] (dma_cache_maint_page) from [<80313b90>] (__dma_page_dev_to_cpu+0x28/0xcc) [<80313b90>] (__dma_page_dev_to_cpu) from [<7fb5dd68>] (ath11k_dp_process_rx+0x1e8/0x4a4 [ath11k]) [<7fb5dd68>] (ath11k_dp_process_rx [ath11k]) from [<7fb53c20>] (ath11k_dp_service_srng+0xb0/0x2ac [ath11k]) [<7fb53c20>] (ath11k_dp_service_srng [ath11k]) from [<7f67bba4>] (ath11k_pci_ext_grp_napi_poll+0x1c/0x78 [ath11k_pci]) [<7f67bba4>] (ath11k_pci_ext_grp_napi_poll [ath11k_pci]) from [<807d5cf4>] (__napi_poll+0x28/0xb8) [<807d5cf4>] (__napi_poll) from [<807d5f28>] (net_rx_action+0xf0/0x280) [<807d5f28>] (net_rx_action) from [<80302148>] (__do_softirq+0xd0/0x280) [<80302148>] (__do_softirq) from [<80320408>] (irq_exit+0x74/0xd4) [<80320408>] (irq_exit) from [<803638a4>] (__handle_domain_irq+0x90/0xb4) [<803638a4>] (__handle_domain_irq) from [<805bedec>] (gic_handle_irq+0x58/0x90) [<805bedec>] (gic_handle_irq) from [<80301a78>] (__irq_svc+0x58/0x8c) Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Nagarajan Maran Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230403191533.28114-1-quic_nmaran@quicinc.com Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/dp_rx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 1786d83f8f2e..38be646bc021 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -389,10 +389,10 @@ int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, goto fail_free_skb; spin_lock_bh(&rx_ring->idr_lock); - buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, - rx_ring->bufs_max * 3, GFP_ATOMIC); + buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 1, + (rx_ring->bufs_max * 3) + 1, GFP_ATOMIC); spin_unlock_bh(&rx_ring->idr_lock); - if (buf_id < 0) + if (buf_id <= 0) goto fail_dma_unmap; desc = ath11k_hal_srng_src_get_next_entry(ab, srng); @@ -2665,6 +2665,9 @@ try_again: cookie); mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, cookie); + if (unlikely(buf_id == 0)) + continue; + ar = ab->pdevs[mac_id].ar; rx_ring = &ar->dp.rx_refill_buf_ring; spin_lock_bh(&rx_ring->idr_lock); -- cgit 1.4.1 From fab766c8a1aff715bce7075aab40e780266f8e1a Mon Sep 17 00:00:00 2001 From: Zhong Jinghua Date: Mon, 6 Feb 2023 22:58:05 +0800 Subject: nbd: fix incomplete validation of ioctl arg [ Upstream commit 55793ea54d77719a071b1ccc05a05056e3b5e009 ] We tested and found an alarm caused by nbd_ioctl arg without verification. The UBSAN warning calltrace like below: UBSAN: Undefined behaviour in fs/buffer.c:1709:35 signed integer overflow: -9223372036854775808 - 1 cannot be represented in type 'long long int' CPU: 3 PID: 2523 Comm: syz-executor.0 Not tainted 4.19.90 #1 Hardware name: linux,dummy-virt (DT) Call trace: dump_backtrace+0x0/0x3f0 arch/arm64/kernel/time.c:78 show_stack+0x28/0x38 arch/arm64/kernel/traps.c:158 __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x170/0x1dc lib/dump_stack.c:118 ubsan_epilogue+0x18/0xb4 lib/ubsan.c:161 handle_overflow+0x188/0x1dc lib/ubsan.c:192 __ubsan_handle_sub_overflow+0x34/0x44 lib/ubsan.c:206 __block_write_full_page+0x94c/0xa20 fs/buffer.c:1709 block_write_full_page+0x1f0/0x280 fs/buffer.c:2934 blkdev_writepage+0x34/0x40 fs/block_dev.c:607 __writepage+0x68/0xe8 mm/page-writeback.c:2305 write_cache_pages+0x44c/0xc70 mm/page-writeback.c:2240 generic_writepages+0xdc/0x148 mm/page-writeback.c:2329 blkdev_writepages+0x2c/0x38 fs/block_dev.c:2114 do_writepages+0xd4/0x250 mm/page-writeback.c:2344 The reason for triggering this warning is __block_write_full_page() -> i_size_read(inode) - 1 overflow. inode->i_size is assigned in __nbd_ioctl() -> nbd_set_size() -> bytesize. We think it is necessary to limit the size of arg to prevent errors. Moreover, __nbd_ioctl() -> nbd_add_socket(), arg will be cast to int. Assuming the value of arg is 0x80000000000000001) (on a 64-bit machine), it will become 1 after the coercion, which will return unexpected results. Fix it by adding checks to prevent passing in too large numbers. Signed-off-by: Zhong Jinghua Reviewed-by: Yu Kuai Reviewed-by: Josef Bacik Link: https://lore.kernel.org/r/20230206145805.2645671-1-zhongjinghua@huawei.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/block/nbd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index e379ccc63c52..888a6abb50f5 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -325,6 +325,9 @@ static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize, if (blk_validate_block_size(blksize)) return -EINVAL; + if (bytesize < 0) + return -EINVAL; + nbd->config->bytesize = bytesize; nbd->config->blksize_bits = __ffs(blksize); @@ -1110,6 +1113,9 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg, struct nbd_sock *nsock; int err; + /* Arg will be cast to int, check it to avoid overflow */ + if (arg > INT_MAX) + return -EINVAL; sock = nbd_get_socket(nbd, arg, &err); if (!sock) return err; -- cgit 1.4.1 From 75481fa7aa5bdcb5223c512cadc6024963b927ae Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 17 Apr 2023 17:10:45 +0200 Subject: ipvs: Update width of source for ip_vs_sync_conn_options [ Upstream commit e3478c68f6704638d08f437cbc552ca5970c151a ] In ip_vs_sync_conn_v0() copy is made to struct ip_vs_sync_conn_options. That structure looks like this: struct ip_vs_sync_conn_options { struct ip_vs_seq in_seq; struct ip_vs_seq out_seq; }; The source of the copy is the in_seq field of struct ip_vs_conn. Whose type is struct ip_vs_seq. Thus we can see that the source - is not as wide as the amount of data copied, which is the width of struct ip_vs_sync_conn_option. The copy is safe because the next field in is another struct ip_vs_seq. Make use of struct_group() to annotate this. Flagged by gcc-13 as: In file included from ./include/linux/string.h:254, from ./include/linux/bitmap.h:11, from ./include/linux/cpumask.h:12, from ./arch/x86/include/asm/paravirt.h:17, from ./arch/x86/include/asm/cpuid.h:62, from ./arch/x86/include/asm/processor.h:19, from ./arch/x86/include/asm/timex.h:5, from ./include/linux/timex.h:67, from ./include/linux/time32.h:13, from ./include/linux/time.h:60, from ./include/linux/stat.h:19, from ./include/linux/module.h:13, from net/netfilter/ipvs/ip_vs_sync.c:38: In function 'fortify_memcpy_chk', inlined from 'ip_vs_sync_conn_v0' at net/netfilter/ipvs/ip_vs_sync.c:606:3: ./include/linux/fortify-string.h:529:25: error: call to '__read_overflow2_field' declared with attribute warning: detected read beyond size of field (2nd parameter); maybe use struct_group()? [-Werror=attribute-warning] 529 | __read_overflow2_field(q_size_field, size); | Compile tested only. Signed-off-by: Simon Horman Reviewed-by: Horatiu Vultur Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/ip_vs.h | 6 ++++-- net/netfilter/ipvs/ip_vs_sync.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 1fca6a88114a..abc46f05762e 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -549,8 +549,10 @@ struct ip_vs_conn { */ struct ip_vs_app *app; /* bound ip_vs_app object */ void *app_data; /* Application private data */ - struct ip_vs_seq in_seq; /* incoming seq. struct */ - struct ip_vs_seq out_seq; /* outgoing seq. struct */ + struct_group(sync_conn_opt, + struct ip_vs_seq in_seq; /* incoming seq. struct */ + struct ip_vs_seq out_seq; /* outgoing seq. struct */ + ); const struct ip_vs_pe *pe; char *pe_data; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 4963fec815da..d4fe7bb4f853 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -603,7 +603,7 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { struct ip_vs_sync_conn_options *opt = (struct ip_vs_sync_conn_options *)&s[1]; - memcpy(opt, &cp->in_seq, sizeof(*opt)); + memcpy(opt, &cp->sync_conn_opt, sizeof(*opt)); } m->nr_conns++; -- cgit 1.4.1 From d9a68e9e89ce86f2df8c1a6132386aaddcc7698c Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Tue, 28 Feb 2023 13:55:17 +0800 Subject: Bluetooth: btusb: Add new PID/VID 04ca:3801 for MT7663 [ Upstream commit 13209415d0e88396d99d346b184864834d70d68a ] This bluetooth device is found in a combo WLAN/BT card for a MediaTek 7663. Tested on Acer Aspire A315-24P Notebook The device information: T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=04ca ProdID=3801 Rev= 1.00 S: Manufacturer=MediaTek Inc. S: Product=Wireless_Device S: SerialNumber=000000000 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms Signed-off-by: Meng Tang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 90b85dcb138d..8ee147ad76b7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -549,6 +549,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x043e, 0x310c), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3801), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, /* Additional MediaTek MT7668 Bluetooth devices */ { USB_DEVICE(0x043e, 0x3109), .driver_info = BTUSB_MEDIATEK | -- cgit 1.4.1 From c97ab504419b41e8bcc2ade6b9b02a32ab626296 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 7 Mar 2023 23:17:30 +0100 Subject: Bluetooth: Add new quirk for broken local ext features page 2 [ Upstream commit 8194f1ef5a815aea815a91daf2c721eab2674f1f ] Some adapters (e.g. RTL8723CS) advertise that they have more than 2 pages for local ext features, but they don't support any features declared in these pages. RTL8723CS reports max_page = 2 and declares support for sync train and secure connection, but it responds with either garbage or with error in status on corresponding commands. Signed-off-by: Vasily Khoruzhick Signed-off-by: Bastian Germann Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- include/net/bluetooth/hci.h | 7 +++++++ net/bluetooth/hci_event.c | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7a381fcef939..f80ae7d23734 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -294,6 +294,13 @@ enum { * during the hdev->setup vendor callback. */ HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG, + + /* When this quirk is set, max_page for local extended features + * is set to 1, even if controller reports higher number. Some + * controllers (e.g. RTL8723CS) report more pages, but they + * don't actually support features declared there. + */ + HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, }; /* HCI device flags */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 42a3a19b111e..21416ccc30ab 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -881,8 +881,13 @@ static u8 hci_cc_read_local_ext_features(struct hci_dev *hdev, void *data, if (rp->status) return rp->status; - if (hdev->max_page < rp->max_page) - hdev->max_page = rp->max_page; + if (hdev->max_page < rp->max_page) { + if (test_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, + &hdev->quirks)) + bt_dev_warn(hdev, "broken local ext features page 2"); + else + hdev->max_page = rp->max_page; + } if (rp->page < HCI_MAX_PAGES) memcpy(hdev->features[rp->page], rp->features, 8); -- cgit 1.4.1 From 88deda7cd8faa2c95768eeb37c731a498b74df09 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 7 Mar 2023 23:17:31 +0100 Subject: Bluetooth: btrtl: add support for the RTL8723CS [ Upstream commit c0123cb6c4c7fc2a42ead6cd7d3e82b8e1c25c6f ] The Realtek RTL8723CS is a SDIO WiFi chip. It also contains a Bluetooth module which is connected via UART to the host. It shares lmp subversion with 8703B, so Realtek's userspace initialization tool (rtk_hciattach) differentiates varieties of RTL8723CS (CG, VF, XX) with RTL8703B using vendor's command to read chip type. Also this chip declares support for some features it doesn't support so add a quirk to indicate that these features are broken. Signed-off-by: Vasily Khoruzhick Signed-off-by: Bastian Germann Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btrtl.c | 120 +++++++++++++++++++++++++++++++++++++++++++-- drivers/bluetooth/btrtl.h | 5 ++ drivers/bluetooth/hci_h5.c | 4 ++ 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index fb52313a1d45..6b3755345427 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -17,7 +17,11 @@ #define VERSION "0.1" +#define RTL_CHIP_8723CS_CG 3 +#define RTL_CHIP_8723CS_VF 4 +#define RTL_CHIP_8723CS_XX 5 #define RTL_EPATCH_SIGNATURE "Realtech" +#define RTL_ROM_LMP_8703B 0x8703 #define RTL_ROM_LMP_8723A 0x1200 #define RTL_ROM_LMP_8723B 0x8723 #define RTL_ROM_LMP_8821A 0x8821 @@ -30,6 +34,7 @@ #define IC_MATCH_FL_HCIREV (1 << 1) #define IC_MATCH_FL_HCIVER (1 << 2) #define IC_MATCH_FL_HCIBUS (1 << 3) +#define IC_MATCH_FL_CHIP_TYPE (1 << 4) #define IC_INFO(lmps, hcir, hciv, bus) \ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \ IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \ @@ -59,6 +64,7 @@ struct id_table { __u16 hci_rev; __u8 hci_ver; __u8 hci_bus; + __u8 chip_type; bool config_needed; bool has_rom_version; bool has_msft_ext; @@ -99,6 +105,39 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8723b_fw.bin", .cfg_name = "rtl_bt/rtl8723b_config" }, + /* 8723CS-CG */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_CG, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_cg_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_cg_config" }, + + /* 8723CS-VF */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_VF, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_vf_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_vf_config" }, + + /* 8723CS-XX */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_XX, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_xx_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_xx_config" }, + /* 8723D */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB), .config_needed = true, @@ -208,7 +247,8 @@ static const struct id_table ic_id_table[] = { }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, - u8 hci_ver, u8 hci_bus) + u8 hci_ver, u8 hci_bus, + u8 chip_type) { int i; @@ -225,6 +265,9 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) && (ic_id_table[i].hci_bus != hci_bus)) continue; + if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) && + (ic_id_table[i].chip_type != chip_type)) + continue; break; } @@ -307,6 +350,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8723B, 1 }, { RTL_ROM_LMP_8821A, 2 }, { RTL_ROM_LMP_8761A, 3 }, + { RTL_ROM_LMP_8703B, 7 }, { RTL_ROM_LMP_8822B, 8 }, { RTL_ROM_LMP_8723B, 9 }, /* 8723D */ { RTL_ROM_LMP_8821A, 10 }, /* 8821C */ @@ -587,6 +631,48 @@ out: return ret; } +static bool rtl_has_chip_type(u16 lmp_subver) +{ + switch (lmp_subver) { + case RTL_ROM_LMP_8703B: + return true; + default: + break; + } + + return false; +} + +static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type) +{ + struct rtl_chip_type_evt *chip_type; + struct sk_buff *skb; + const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0}; + + /* Read RTL chip type command */ + skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + rtl_dev_err(hdev, "Read chip type failed (%ld)", + PTR_ERR(skb)); + return PTR_ERR(skb); + } + + chip_type = skb_pull_data(skb, sizeof(*chip_type)); + if (!chip_type) { + rtl_dev_err(hdev, "RTL chip type event length mismatch"); + kfree_skb(skb); + return -EIO; + } + + rtl_dev_info(hdev, "chip_type status=%x type=%x", + chip_type->status, chip_type->type); + + *type = chip_type->type & 0x0f; + + kfree_skb(skb); + return 0; +} + void btrtl_free(struct btrtl_device_info *btrtl_dev) { kvfree(btrtl_dev->fw_data); @@ -603,7 +689,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, struct hci_rp_read_local_version *resp; char cfg_name[40]; u16 hci_rev, lmp_subver; - u8 hci_ver; + u8 hci_ver, chip_type = 0; int ret; u16 opcode; u8 cmd[2]; @@ -629,8 +715,14 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, hci_rev = le16_to_cpu(resp->hci_rev); lmp_subver = le16_to_cpu(resp->lmp_subver); + if (rtl_has_chip_type(lmp_subver)) { + ret = rtl_read_chip_type(hdev, &chip_type); + if (ret) + goto err_free; + } + btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, - hdev->bus); + hdev->bus, chip_type); if (!btrtl_dev->ic_info) btrtl_dev->drop_fw = true; @@ -673,7 +765,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, lmp_subver = le16_to_cpu(resp->lmp_subver); btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, - hdev->bus); + hdev->bus, chip_type); } out_free: kfree_skb(skb); @@ -755,6 +847,7 @@ int btrtl_download_firmware(struct hci_dev *hdev, case RTL_ROM_LMP_8761A: case RTL_ROM_LMP_8822B: case RTL_ROM_LMP_8852A: + case RTL_ROM_LMP_8703B: return btrtl_setup_rtl8723b(hdev, btrtl_dev); default: rtl_dev_info(hdev, "assuming no firmware upload needed"); @@ -788,6 +881,19 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) rtl_dev_dbg(hdev, "WBS supported not enabled."); break; } + + switch (btrtl_dev->ic_info->lmp_subver) { + case RTL_ROM_LMP_8703B: + /* 8723CS reports two pages for local ext features, + * but it doesn't support any features from page 2 - + * it either responds with garbage or with error status + */ + set_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, + &hdev->quirks); + break; + default: + break; + } } EXPORT_SYMBOL_GPL(btrtl_set_quirks); @@ -946,6 +1052,12 @@ MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin"); diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index 2c441bda390a..1c6282241d2d 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -14,6 +14,11 @@ struct btrtl_device_info; +struct rtl_chip_type_evt { + __u8 status; + __u8 type; +} __packed; + struct rtl_download_cmd { __u8 index; __u8 data[RTL_FRAG_LEN]; diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 6455bc4fb5bb..e90670955df2 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -936,6 +936,8 @@ static int h5_btrtl_setup(struct h5 *h5) err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev); /* Give the device some time before the hci-core sends it a reset */ usleep_range(10000, 20000); + if (err) + goto out_free; btrtl_set_quirks(h5->hu->hdev, btrtl_dev); @@ -1100,6 +1102,8 @@ static const struct of_device_id rtl_bluetooth_of_match[] = { .data = (const void *)&h5_data_rtl8822cs }, { .compatible = "realtek,rtl8723bs-bt", .data = (const void *)&h5_data_rtl8723bs }, + { .compatible = "realtek,rtl8723cs-bt", + .data = (const void *)&h5_data_rtl8723bs }, { .compatible = "realtek,rtl8723ds-bt", .data = (const void *)&h5_data_rtl8723bs }, #endif -- cgit 1.4.1 From f4f3cbdbf2c8e2f39944b2c8a8b03ca87064f9a5 Mon Sep 17 00:00:00 2001 From: Raul Cheleguini Date: Fri, 10 Mar 2023 15:14:10 +0000 Subject: Bluetooth: Improve support for Actions Semi ATS2851 based devices [ Upstream commit 7c2b2d2d0cb658aa543e11e90ae95621d3cb5fe6 ] Add two more quirks to resume the device initialization and basic operation as the device seems not to support "Read Transmit Power" and "Set Extended Scan Parameters". < HCI Command: LE Read Transmit Power (0x08|0x004b) plen 0 > HCI Event: Command Status (0x0f) plen 4 LE Read Transmit Power (0x08|0x004b) ncmd 1 Status: Unknown HCI Command (0x01) < HCI Command: LE Set Extended Scan Parameters (0x08|0x0041) plen 8 Own address type: Random (0x01) Filter policy: Accept all advertisement (0x00) PHYs: 0x01 Entry 0: LE 1M Type: Active (0x01) Interval: 11.250 msec (0x0012) Window: 11.250 msec (0x0012) > HCI Event: Command Status (0x0f) plen 4 LE Set Extended Scan Parameters (0x08|0x0041) ncmd 1 Status: Unknown HCI Command (0x01) Signed-off-by: Raul Cheleguini Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8ee147ad76b7..3991dcd2ebf7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4019,6 +4019,8 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_ACTIONS_SEMI) { /* Support is advertised, but not implemented */ set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); } if (!reset) -- cgit 1.4.1 From ea160ece08668a30ce69f92cc08e87da54a64a9c Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 21 Mar 2023 19:48:26 +0800 Subject: Bluetooth: btrtl: check for NULL in btrtl_set_quirks() [ Upstream commit 253cf30e8d3d001850a95c4729d668f916b037ab ] The btrtl_set_quirks() has accessed btrtl_dev->ic_info->lmp_subver since b8e482d02513. However, if installing a Realtek Bluetooth controller without the driver supported, it will hit the NULL point accessed. Add a check for NULL to avoid the Kernel Oops. Signed-off-by: Max Chou Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btrtl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 6b3755345427..88f8c604d70a 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -882,6 +882,9 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) break; } + if (!btrtl_dev->ic_info) + return; + switch (btrtl_dev->ic_info->lmp_subver) { case RTL_ROM_LMP_8703B: /* 8723CS reports two pages for local ext features, -- cgit 1.4.1 From 803ba6dcc4b6865653f43fb5718eaae87f8beb17 Mon Sep 17 00:00:00 2001 From: Chethan T N Date: Tue, 21 Mar 2023 10:03:10 +0530 Subject: Bluetooth: btintel: Add LE States quirk support [ Upstream commit 77f542b10c535c9a93bf8afdd2665524935807c2 ] Basically all Intel controllers support both Central/Peripheral LE states. This patch enables the LE States quirk by default on all Solar and Magnertor Intel controllers. Signed-off-by: Chethan T N Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btintel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index f6b4b7a1be4c..bbad1207cdfd 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -2553,9 +2553,8 @@ static int btintel_setup_combined(struct hci_dev *hdev) */ set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); - /* Valid LE States quirk for GfP */ - if (INTEL_HW_VARIANT(ver_tlv.cnvi_bt) == 0x18) - set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); + /* Apply LE States quirk from solar onwards */ + set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); /* Setup MSFT Extension support */ btintel_set_msft_opcode(hdev, -- cgit 1.4.1 From 76dd7893bd10d69b1102b37363ed9a6d1cc0e562 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Mar 2023 23:11:21 +0200 Subject: Bluetooth: hci_bcm: Fall back to getting bdaddr from EFI if not set [ Upstream commit 0d218c3642b9ccf71f44987cd03c19320f3bd918 ] On some devices the BCM Bluetooth adapter does not have a valid bdaddr set. btbcm.c currently sets HCI_QUIRK_INVALID_BDADDR to indicate when this is the case. But this requires users to manual setup a btaddr, by doing e.g.: btmgmt -i hci0 public-addr 'B0:F1:EC:82:1D:B3' Which means that Bluetooth will not work out of the box on such devices. To avoid this (where possible) hci_bcm sets: HCI_QUIRK_USE_BDADDR_PROPERTY which tries to get the bdaddr from devicetree. But this only works on devicetree platforms. On UEFI based platforms there is a special Broadcom UEFI variable which when present contains the devices bdaddr, just like how there is another UEFI variable which contains wifi nvram contents including the wifi MAC address. Add support for getting the bdaddr from this Broadcom UEFI variable, so that Bluetooth will work OOTB for users on devices where this UEFI variable is present. This fixes Bluetooth not working on for example Asus T100HA 2-in-1s. Signed-off-by: Hans de Goede Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btbcm.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 43e98a598bd9..de2ea589aa49 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -6,6 +6,7 @@ * Copyright (C) 2015 Intel Corporation */ +#include #include #include #include @@ -34,6 +35,43 @@ /* For kmalloc-ing the fw-name array instead of putting it on the stack */ typedef char bcm_fw_name[BCM_FW_NAME_LEN]; +#ifdef CONFIG_EFI +static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) +{ + efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f, + 0x43, 0x26, 0x81, 0x23, 0xd1, 0x13); + bdaddr_t efi_bdaddr, bdaddr; + efi_status_t status; + unsigned long len; + int ret; + + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) + return -EOPNOTSUPP; + + len = sizeof(efi_bdaddr); + status = efi.get_variable(L"BDADDR", &guid, NULL, &len, &efi_bdaddr); + if (status != EFI_SUCCESS) + return -ENXIO; + + if (len != sizeof(efi_bdaddr)) + return -EIO; + + baswap(&bdaddr, &efi_bdaddr); + + ret = btbcm_set_bdaddr(hdev, &bdaddr); + if (ret) + return ret; + + bt_dev_info(hdev, "BCM: Using EFI device address (%pMR)", &bdaddr); + return 0; +} +#else +static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} +#endif + int btbcm_check_bdaddr(struct hci_dev *hdev) { struct hci_rp_read_bd_addr *bda; @@ -87,9 +125,12 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) !bacmp(&bda->bdaddr, BDADDR_BCM4345C5) || !bacmp(&bda->bdaddr, BDADDR_BCM43430A0) || !bacmp(&bda->bdaddr, BDADDR_BCM43341B)) { - bt_dev_info(hdev, "BCM: Using default device address (%pMR)", - &bda->bdaddr); - set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + /* Try falling back to BDADDR EFI variable */ + if (btbcm_set_bdaddr_from_efi(hdev) != 0) { + bt_dev_info(hdev, "BCM: Using default device address (%pMR)", + &bda->bdaddr); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } } kfree_skb(skb); -- cgit 1.4.1 From 2f4a1b24dad098c4d17544a34df1b24081714b8a Mon Sep 17 00:00:00 2001 From: Raul Cheleguini Date: Thu, 23 Mar 2023 10:45:39 -0300 Subject: Bluetooth: Add new quirk for broken set random RPA timeout for ATS2851 [ Upstream commit 91b6d02ddcd113352bdd895990b252065c596de7 ] The ATS2851 based controller advertises support for command "LE Set Random Private Address Timeout" but does not actually implement it, impeding the controller initialization. Add the quirk HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT to unblock the controller initialization. < HCI Command: LE Set Resolvable Private... (0x08|0x002e) plen 2 Timeout: 900 seconds > HCI Event: Command Status (0x0f) plen 4 LE Set Resolvable Private Address Timeout (0x08|0x002e) ncmd 1 Status: Unknown HCI Command (0x01) Co-developed-by: imoc Signed-off-by: imoc Signed-off-by: Raul Cheleguini Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btusb.c | 1 + include/net/bluetooth/hci.h | 8 ++++++++ net/bluetooth/hci_sync.c | 6 +++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3991dcd2ebf7..faad19b396d5 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4020,6 +4020,7 @@ static int btusb_probe(struct usb_interface *intf, /* Support is advertised, but not implemented */ set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); } diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f80ae7d23734..b350d92136c8 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -301,6 +301,14 @@ enum { * don't actually support features declared there. */ HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, + + /* + * When this quirk is set, the HCI_OP_LE_SET_RPA_TIMEOUT command is + * skipped during initialization. This is required for the Actions + * Semiconductor ATS2851 based controllers, which erroneously claims + * to support it. + */ + HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, }; /* HCI device flags */ diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 9361fb3685cc..e8b78104a407 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4075,7 +4075,8 @@ static int hci_le_set_rpa_timeout_sync(struct hci_dev *hdev) { __le16 timeout = cpu_to_le16(hdev->rpa_timeout); - if (!(hdev->commands[35] & 0x04)) + if (!(hdev->commands[35] & 0x04) || + test_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks)) return 0; return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RPA_TIMEOUT, @@ -4515,6 +4516,9 @@ static const struct { "HCI Set Event Filter command not supported."), HCI_QUIRK_BROKEN(ENHANCED_SETUP_SYNC_CONN, "HCI Enhanced Setup Synchronous Connection command is " + "advertised, but not supported."), + HCI_QUIRK_BROKEN(SET_RPA_TIMEOUT, + "HCI LE Set Random Private Address Timeout command is " "advertised, but not supported.") }; -- cgit 1.4.1 From fd269a0435f8e9943b7a57c5a59688848d42d449 Mon Sep 17 00:00:00 2001 From: Min Li Date: Mon, 17 Apr 2023 10:27:54 +0800 Subject: Bluetooth: L2CAP: fix "bad unlock balance" in l2cap_disconnect_rsp [ Upstream commit 25e97f7b1866e6b8503be349eeea44bb52d661ce ] conn->chan_lock isn't acquired before l2cap_get_chan_by_scid, if l2cap_get_chan_by_scid returns NULL, then 'bad unlock balance' is triggered. Reported-by: syzbot+9519d6b5b79cf7787cf3@syzkaller.appspotmail.com Link: https://lore.kernel.org/all/000000000000894f5f05f95e9f4d@google.com/ Signed-off-by: Min Li Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e62dadad81b3..ee8f806534df 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4694,7 +4694,6 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, chan = l2cap_get_chan_by_scid(conn, scid); if (!chan) { - mutex_unlock(&conn->chan_lock); return 0; } -- cgit 1.4.1 From 022fe9fcac06ae9e1eb0293a63aaddcbbaa13284 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 18 Apr 2023 13:43:54 +0800 Subject: Bluetooth: btrtl: Add the support for RTL8851B [ Upstream commit 7948fe1c92d92313eea5453f83deb7f0141355e8 ] Add the support for RTL8851B BT controller on USB interface. The necessary firmware will be submitted to linux-firmware project. Note that the Bluetooth devices WITH the VID=0x0bda would be set the feature quirk in btrtl_setup_realtek(). It's able to ignore the feature flag set for the specific VID and PID in blacklist_table[] of btusb.c. (check [1]) If Realtek Bluetooth chips WITHOUT the VID=0x0bda, it shall be added the feature flag for the specific VID and PID in blacklist_table[] of btusb.c. (check [2]) [1] '9ab9235fe5cf ("Bluetooth: btrtl: Enable WBS for the specific Realtek devices")' [2] '73280f13c9bb ("Bluetooth: btusb: Add the more support IDs for Realtek RTL8822CE")' The device info from /sys/kernel/debug/usb/devices as below. T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 33 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0bda ProdID=b851 Rev= 0.00 S: Manufacturer=Realtek S: Product=802.11ax WLAN Adapter S: SerialNumber=00E04C885A01 C:* #Ifs= 3 Cfg#= 1 Atr=80 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 8 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=09(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0a(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0b(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0c(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Signed-off-by: Max Chou Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btrtl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 88f8c604d70a..ead632595ce0 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -28,6 +28,7 @@ #define RTL_ROM_LMP_8761A 0x8761 #define RTL_ROM_LMP_8822B 0x8822 #define RTL_ROM_LMP_8852A 0x8852 +#define RTL_ROM_LMP_8851B 0x8851 #define RTL_CONFIG_MAGIC 0x8723ab55 #define IC_MATCH_FL_LMPSUBV (1 << 0) @@ -56,6 +57,7 @@ enum btrtl_chip_id { CHIP_ID_8852A = 18, CHIP_ID_8852B = 20, CHIP_ID_8852C = 25, + CHIP_ID_8851B = 36, }; struct id_table { @@ -244,6 +246,14 @@ static const struct id_table ic_id_table[] = { .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852cu_fw.bin", .cfg_name = "rtl_bt/rtl8852cu_config" }, + + /* 8851B */ + { IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB), + .config_needed = false, + .has_rom_version = true, + .has_msft_ext = false, + .fw_name = "rtl_bt/rtl8851bu_fw.bin", + .cfg_name = "rtl_bt/rtl8851bu_config" }, }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, @@ -359,6 +369,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8852A, 18 }, /* 8852A */ { RTL_ROM_LMP_8852A, 20 }, /* 8852B */ { RTL_ROM_LMP_8852A, 25 }, /* 8852C */ + { RTL_ROM_LMP_8851B, 36 }, /* 8851B */ }; min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3; @@ -848,6 +859,7 @@ int btrtl_download_firmware(struct hci_dev *hdev, case RTL_ROM_LMP_8822B: case RTL_ROM_LMP_8852A: case RTL_ROM_LMP_8703B: + case RTL_ROM_LMP_8851B: return btrtl_setup_rtl8723b(hdev, btrtl_dev); default: rtl_dev_info(hdev, "assuming no firmware upload needed"); @@ -872,6 +884,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) case CHIP_ID_8852A: case CHIP_ID_8852B: case CHIP_ID_8852C: + case CHIP_ID_8851B: set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); hci_set_aosp_capable(hdev); @@ -1075,3 +1088,5 @@ MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8851bu_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8851bu_config.bin"); -- cgit 1.4.1 From ec310591cf839653a5b2c1fcf6b8a110c3f2485c Mon Sep 17 00:00:00 2001 From: Philipp Hortmann Date: Thu, 23 Feb 2023 07:47:21 +0100 Subject: staging: rtl8192e: Replace macro RTL_PCI_DEVICE with PCI_DEVICE [ Upstream commit fda2093860df4812d69052a8cf4997e53853a340 ] Replace macro RTL_PCI_DEVICE with PCI_DEVICE to get rid of rtl819xp_ops which is empty. Signed-off-by: Philipp Hortmann Link: https://lore.kernel.org/r/8b45ee783fa91196b7c9d6fc840a189496afd2f4.1677133271.git.philipp.g.hortmann@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/rtl8192e/rtl8192e/rtl_core.c | 6 +++--- drivers/staging/rtl8192e/rtl8192e/rtl_core.h | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index c1e50084172d..7e11364d718b 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -48,9 +48,9 @@ static const struct rtl819x_ops rtl819xp_ops = { }; static struct pci_device_id rtl8192_pci_id_tbl[] = { - {RTL_PCI_DEVICE(0x10ec, 0x8192, rtl819xp_ops)}, - {RTL_PCI_DEVICE(0x07aa, 0x0044, rtl819xp_ops)}, - {RTL_PCI_DEVICE(0x07aa, 0x0047, rtl819xp_ops)}, + {PCI_DEVICE(0x10ec, 0x8192)}, + {PCI_DEVICE(0x07aa, 0x0044)}, + {PCI_DEVICE(0x07aa, 0x0047)}, {} }; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h index 7021f9c435d9..50f1ec78cc45 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h @@ -55,11 +55,6 @@ #define IS_HARDWARE_TYPE_8192SE(_priv) \ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192SE) -#define RTL_PCI_DEVICE(vend, dev, cfg) \ - .vendor = (vend), .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ - .driver_data = (kernel_ulong_t)&(cfg) - #define TOTAL_CAM_ENTRY 32 #define CAM_CONTENT_COUNT 8 -- cgit 1.4.1 From b268082188e6feb25637b9b350f5f3323d7a0f88 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Sun, 26 Feb 2023 20:06:13 -0700 Subject: HID: apple: Set the tilde quirk flag on the Geyser 4 and later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c3388ddc74a863466c7c3fa24d3a9cea9c9bca53 ] I recently tested several old MacBooks and as far as I can tell, all MacBooks that have an ISO keyboard have the tilde key quirk: Product Model Year System CPU Shape Labels Country Quirky ============================================================================ 05ac:021b A1181 2006 MacBook2,1 T5600 ISO British 13 Yes 05ac:021b A1181 2007 MacBook2,1 T7200 ISO Québécois 13 Yes 05ac:0229 A1181 2007 MacBook4,1 T8300 ANSI Usonian 33 No 05ac:022a A1181 2007 MacBook4,1 T8100 ISO English 13 Yes 05ac:022a A1181 2007 MacBook5,2 P7350 ISO Québécois 13 Yes 05ac:0237 A1278 2008 MacBook5,1 P7350 ISO Dutch 13 Yes 05ac:0237 A1278 2009 MacBook5,5 P7550 ISO British 13 Yes The model number and year are from the laptop case. Since Apple printed the same model and year on many different laptops, the system name (as reported in the SMBIOS tables) and CPU form a more precise identifier. Signed-off-by: Alex Henrie Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-apple.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index c671ce94671c..f21b1c4ca825 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -861,7 +861,8 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, @@ -880,7 +881,8 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, @@ -921,31 +923,31 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO), - .driver_data = APPLE_HAS_FN }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO), - .driver_data = APPLE_HAS_FN }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO), - .driver_data = APPLE_HAS_FN }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ISO), - .driver_data = APPLE_HAS_FN }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), - .driver_data = APPLE_HAS_FN }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), -- cgit 1.4.1 From 6ce24d176ea64cd36c7510e7b6d1849d8e713795 Mon Sep 17 00:00:00 2001 From: Khadija Kamran Date: Fri, 17 Mar 2023 01:09:00 +0500 Subject: staging: axis-fifo: initialize timeouts in init only [ Upstream commit 752cbd8f191678e86aa754f795546b7f06b7f171 ] Initialize the module parameters, read_timeout and write_timeout once in init(). Module parameters can only be set once and cannot be modified later, so we don't need to evaluate them again when passing the parameters to wait_event_interruptible_timeout(). Convert datatype of {read,write}_timeout from 'int' to 'long int' because implicit conversion of 'long int' to 'int' in statement '{read,write}_timeout = MAX_SCHEDULE_TIMEOUT' results in an overflow. Change format specifier for {read,write}_timeout from %i to %li. Reviewed-by: Fabio M. De Francesco Suggested-by: Greg Kroah-Hartman Signed-off-by: Khadija Kamran Link: https://lore.kernel.org/r/ZBN3XAsItCiTk7CV@khadija-virtual-machine Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/axis-fifo/axis-fifo.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/staging/axis-fifo/axis-fifo.c b/drivers/staging/axis-fifo/axis-fifo.c index dfd2b357f484..0a85ea667a1b 100644 --- a/drivers/staging/axis-fifo/axis-fifo.c +++ b/drivers/staging/axis-fifo/axis-fifo.c @@ -103,17 +103,17 @@ * globals * ---------------------------- */ -static int read_timeout = 1000; /* ms to wait before read() times out */ -static int write_timeout = 1000; /* ms to wait before write() times out */ +static long read_timeout = 1000; /* ms to wait before read() times out */ +static long write_timeout = 1000; /* ms to wait before write() times out */ /* ---------------------------- * module command-line arguments * ---------------------------- */ -module_param(read_timeout, int, 0444); +module_param(read_timeout, long, 0444); MODULE_PARM_DESC(read_timeout, "ms to wait before blocking read() timing out; set to -1 for no timeout"); -module_param(write_timeout, int, 0444); +module_param(write_timeout, long, 0444); MODULE_PARM_DESC(write_timeout, "ms to wait before blocking write() timing out; set to -1 for no timeout"); /* ---------------------------- @@ -384,9 +384,7 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf, mutex_lock(&fifo->read_lock); ret = wait_event_interruptible_timeout(fifo->read_queue, ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), - (read_timeout >= 0) ? - msecs_to_jiffies(read_timeout) : - MAX_SCHEDULE_TIMEOUT); + read_timeout); if (ret <= 0) { if (ret == 0) { @@ -528,9 +526,7 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf, ret = wait_event_interruptible_timeout(fifo->write_queue, ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= words_to_write, - (write_timeout >= 0) ? - msecs_to_jiffies(write_timeout) : - MAX_SCHEDULE_TIMEOUT); + write_timeout); if (ret <= 0) { if (ret == 0) { @@ -948,7 +944,17 @@ static struct platform_driver axis_fifo_driver = { static int __init axis_fifo_init(void) { - pr_info("axis-fifo driver loaded with parameters read_timeout = %i, write_timeout = %i\n", + if (read_timeout >= 0) + read_timeout = msecs_to_jiffies(read_timeout); + else + read_timeout = MAX_SCHEDULE_TIMEOUT; + + if (write_timeout >= 0) + write_timeout = msecs_to_jiffies(write_timeout); + else + write_timeout = MAX_SCHEDULE_TIMEOUT; + + pr_info("axis-fifo driver loaded with parameters read_timeout = %li, write_timeout = %li\n", read_timeout, write_timeout); return platform_driver_register(&axis_fifo_driver); } -- cgit 1.4.1 From 2e64faf6553b41d5f83e08b761e3757c6cece9fd Mon Sep 17 00:00:00 2001 From: Prajna Sariputra Date: Sun, 2 Apr 2023 02:21:30 +1100 Subject: ASoC: amd: yc: Add DMI entries to support HP OMEN 16-n0xxx (8A42) [ Upstream commit ee4281de4d60288b9c802bb0906061ec355ecef2 ] This model requires an additional detection quirk to enable the internal microphone. Signed-off-by: Prajna Sariputra Link: https://lore.kernel.org/r/2283110.ElGaqSPkdT@n0067ax-linux62 Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 0acdf0156f07..a428e17f0325 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -262,6 +262,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16z-n000"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8A42"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit 1.4.1 From 16420da84522e4638cb204f0ac06a1c55bb77e71 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Thu, 2 Mar 2023 14:01:16 +0100 Subject: HID: logitech-hidpp: Don't use the USB serial for USB devices [ Upstream commit 7ad1fe0da0fa91bf920b79ab05ae97bfabecc4f4 ] For devices that support the 0x0003 feature (Device Information) version 4, set the serial based on the output of that feature, rather than relying on the usbhid code setting the USB serial. This should allow the serial when connected through USB to (nearly) match the one when connected through a unifying receiver. For example, on the serials on a G903 wired/wireless mouse: - Unifying: 4067-e8-ce-cd-45 - USB before patch: 017C385C3837 - USB after patch: c086-e8-ce-cd-45 Signed-off-by: Bastien Nocera Link: https://lore.kernel.org/r/20230302130117.3975-1-hadess@hadess.net Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-logitech-hidpp.c | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index e906ee375298..c39a4c56678d 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -932,6 +932,55 @@ print_version: return 0; } +/* -------------------------------------------------------------------------- */ +/* 0x0003: Device Information */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_DEVICE_INFORMATION 0x0003 + +#define CMD_GET_DEVICE_INFO 0x00 + +static int hidpp_get_serial(struct hidpp_device *hidpp, u32 *serial) +{ + struct hidpp_report response; + u8 feature_type; + u8 feature_index; + int ret; + + ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_DEVICE_INFORMATION, + &feature_index, + &feature_type); + if (ret) + return ret; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_GET_DEVICE_INFO, + NULL, 0, &response); + if (ret) + return ret; + + /* See hidpp_unifying_get_serial() */ + *serial = *((u32 *)&response.rap.params[1]); + return 0; +} + +static int hidpp_serial_init(struct hidpp_device *hidpp) +{ + struct hid_device *hdev = hidpp->hid_dev; + u32 serial; + int ret; + + ret = hidpp_get_serial(hidpp, &serial); + if (ret) + return ret; + + snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD", + hdev->product, &serial); + dbg_hid("HID++ DeviceInformation: Got serial: %s\n", hdev->uniq); + + return 0; +} + /* -------------------------------------------------------------------------- */ /* 0x0005: GetDeviceNameType */ /* -------------------------------------------------------------------------- */ @@ -4194,6 +4243,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) if (hidpp->quirks & HIDPP_QUIRK_UNIFYING) hidpp_unifying_init(hidpp); + else if (hid_is_usb(hidpp->hid_dev)) + hidpp_serial_init(hidpp); connected = hidpp_root_get_protocol_version(hidpp) == 0; atomic_set(&hidpp->connected, connected); -- cgit 1.4.1 From 1844749dcfc486513d13e88933d4c80fca99a288 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Thu, 2 Mar 2023 14:01:17 +0100 Subject: HID: logitech-hidpp: Reconcile USB and Unifying serials [ Upstream commit 5b3691d15e04b6d5a32c915577b8dbc5cfb56382 ] Now that USB HID++ devices can gather a serial number that matches the one that would be gathered when connected through a Unifying receiver, remove the last difference by dropping the product ID as devices usually have different product IDs when connected through USB or Unifying. For example, on the serials on a G903 wired/wireless mouse: - Unifying before patch: 4067-e8-ce-cd-45 - USB before patch: c086-e8-ce-cd-45 - Unifying and USB after patch: e8-ce-cd-45 Signed-off-by: Bastien Nocera Link: https://lore.kernel.org/r/20230302130117.3975-2-hadess@hadess.net Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-logitech-hidpp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index c39a4c56678d..b2cd7527de19 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -838,8 +838,7 @@ static int hidpp_unifying_init(struct hidpp_device *hidpp) if (ret) return ret; - snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD", - hdev->product, &serial); + snprintf(hdev->uniq, sizeof(hdev->uniq), "%4phD", &serial); dbg_hid("HID++ Unifying: Got serial: %s\n", hdev->uniq); name = hidpp_unifying_get_name(hidpp); @@ -974,8 +973,7 @@ static int hidpp_serial_init(struct hidpp_device *hidpp) if (ret) return ret; - snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD", - hdev->product, &serial); + snprintf(hdev->uniq, sizeof(hdev->uniq), "%4phD", &serial); dbg_hid("HID++ DeviceInformation: Got serial: %s\n", hdev->uniq); return 0; -- cgit 1.4.1 From b484aa214763544a76c183b6fc9548ef7a03bff1 Mon Sep 17 00:00:00 2001 From: Kevin Groeneveld Date: Sat, 18 Mar 2023 18:21:32 -0400 Subject: spi: spi-imx: fix MX51_ECSPI_* macros when cs > 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 87c614175bbf28d3fd076dc2d166bac759e41427 ] When using gpio based chip select the cs value can go outside the range 0 – 3. The various MX51_ECSPI_* macros did not take this into consideration resulting in possible corruption of the configuration. For example for any cs value over 3 the SCLKPHA bits would not be set and other values in the register possibly corrupted. One way to fix this is to just mask the cs bits to 2 bits. This still allows all 4 native chip selects to work as well as gpio chip selects (which can use any of the 4 chip select configurations). Signed-off-by: Kevin Groeneveld Link: https://lore.kernel.org/r/20230318222132.3373-1-kgroeneveld@lenbrook.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-imx.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index fbd7b354dd36..2c660a95c17e 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -253,6 +253,18 @@ static bool spi_imx_can_dma(struct spi_controller *controller, struct spi_device return true; } +/* + * Note the number of natively supported chip selects for MX51 is 4. Some + * devices may have less actual SS pins but the register map supports 4. When + * using gpio chip selects the cs values passed into the macros below can go + * outside the range 0 - 3. We therefore need to limit the cs value to avoid + * corrupting bits outside the allocated locations. + * + * The simplest way to do this is to just mask the cs bits to 2 bits. This + * still allows all 4 native chip selects to work as well as gpio chip selects + * (which can use any of the 4 chip select configurations). + */ + #define MX51_ECSPI_CTRL 0x08 #define MX51_ECSPI_CTRL_ENABLE (1 << 0) #define MX51_ECSPI_CTRL_XCH (1 << 2) @@ -261,16 +273,16 @@ static bool spi_imx_can_dma(struct spi_controller *controller, struct spi_device #define MX51_ECSPI_CTRL_DRCTL(drctl) ((drctl) << 16) #define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8 #define MX51_ECSPI_CTRL_PREDIV_OFFSET 12 -#define MX51_ECSPI_CTRL_CS(cs) ((cs) << 18) +#define MX51_ECSPI_CTRL_CS(cs) ((cs & 3) << 18) #define MX51_ECSPI_CTRL_BL_OFFSET 20 #define MX51_ECSPI_CTRL_BL_MASK (0xfff << 20) #define MX51_ECSPI_CONFIG 0x0c -#define MX51_ECSPI_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0)) -#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4)) -#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8)) -#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12)) -#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs) + 20)) +#define MX51_ECSPI_CONFIG_SCLKPHA(cs) (1 << ((cs & 3) + 0)) +#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs & 3) + 4)) +#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs & 3) + 8)) +#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs & 3) + 12)) +#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs & 3) + 20)) #define MX51_ECSPI_INT 0x10 #define MX51_ECSPI_INT_TEEN (1 << 0) -- cgit 1.4.1 From 907d6b615e793e82b9f7161cba5cde881eafb63e Mon Sep 17 00:00:00 2001 From: Samuel Čavoj Date: Wed, 5 Apr 2023 16:44:56 +0300 Subject: usb: typec: ucsi: acpi: add quirk for ASUS Zenbook UM325 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 326e1c208f3f24d14b93f910b8ae32c94923d22c ] On some ACPI platforms (namely the ASUS Zenbook UM325) the _DSM method must not be called after a notification is received but instead the mailbox should be read immediately from RAM. This is because the ACPI interrupt handler destroys the CCI in ERAM after copying to system memory, and when _DSM is later called to perform a second copy, it retrieves a garbage value. Instead, the _DSM(read) method should only be called when necessary, i.e. for polling the state after reset and for retrieving the version. Other reads should not call _DSM and only peek into the RAM region. This adds a separate read operation for the Zenbook that syncs the ACPI mailbox only with polled commands. Link: https://lore.kernel.org/linux-usb/20210823180626.tb6m7h5tp6adhvt2@fastboi.localdomain/ Signed-off-by: Samuel Čavoj [ heikki : handling everything in ucsi_acpi.c with DMI quirk ] Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230405134456.49607-1-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/typec/ucsi/ucsi_acpi.c | 44 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 62206a6b8ea7..217355f1f9b9 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "ucsi.h" @@ -23,6 +24,7 @@ struct ucsi_acpi { struct completion complete; unsigned long flags; guid_t guid; + u64 cmd; }; static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func) @@ -62,6 +64,7 @@ static int ucsi_acpi_async_write(struct ucsi *ucsi, unsigned int offset, struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); memcpy(ua->base + offset, val, val_len); + ua->cmd = *(u64 *)val; return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE); } @@ -93,13 +96,46 @@ static const struct ucsi_operations ucsi_acpi_ops = { .async_write = ucsi_acpi_async_write }; +static int +ucsi_zenbook_read(struct ucsi *ucsi, unsigned int offset, void *val, size_t val_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + int ret; + + if (offset == UCSI_VERSION || UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) { + ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); + if (ret) + return ret; + } + + memcpy(val, ua->base + offset, val_len); + + return 0; +} + +static const struct ucsi_operations ucsi_zenbook_ops = { + .read = ucsi_zenbook_read, + .sync_write = ucsi_acpi_sync_write, + .async_write = ucsi_acpi_async_write +}; + +static const struct dmi_system_id zenbook_dmi_id[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), + }, + }, + { } +}; + static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) { struct ucsi_acpi *ua = data; u32 cci; int ret; - ret = ucsi_acpi_read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci)); + ret = ua->ucsi->ops->read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci)); if (ret) return; @@ -114,6 +150,7 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) static int ucsi_acpi_probe(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + const struct ucsi_operations *ops = &ucsi_acpi_ops; struct ucsi_acpi *ua; struct resource *res; acpi_status status; @@ -143,7 +180,10 @@ static int ucsi_acpi_probe(struct platform_device *pdev) init_completion(&ua->complete); ua->dev = &pdev->dev; - ua->ucsi = ucsi_create(&pdev->dev, &ucsi_acpi_ops); + if (dmi_check_system(zenbook_dmi_id)) + ops = &ucsi_zenbook_ops; + + ua->ucsi = ucsi_create(&pdev->dev, ops); if (IS_ERR(ua->ucsi)) return PTR_ERR(ua->ucsi); -- cgit 1.4.1 From 26fda373452394c1e583572dbd8172d430425feb Mon Sep 17 00:00:00 2001 From: Fred Oh Date: Thu, 6 Apr 2023 10:25:00 -0500 Subject: ALSA: hda: LNL: add HD Audio PCI ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 714b2f025d767e7df1fe9da18bd70537d64cc157 ] Add HD Audio PCI ID for Intel Lunarlake platform. Signed-off-by: Fred Oh Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20230406152500.15104-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 77a592f21947..881b2f3a1551 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2528,6 +2528,9 @@ static const struct pci_device_id azx_ids[] = { /* Meteorlake-P */ { PCI_DEVICE(0x8086, 0x7e28), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Lunarlake-P */ + { PCI_DEVICE(0x8086, 0xa828), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Broxton-P(Apollolake) */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, -- cgit 1.4.1 From 1a6371c50b79474198593e048cad62456624acb5 Mon Sep 17 00:00:00 2001 From: Cem Kaya Date: Mon, 10 Apr 2023 20:38:15 +0200 Subject: ASoC: amd: Add Dell G15 5525 to quirks list [ Upstream commit faf15233e59052f4d61cad2da6e56daf33124d96 ] Add Dell G15 5525 Ryzen Edition to quirks list for acp6x so that internal mic works. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217155 Signed-off-by: Cem Kaya Link: https://lore.kernel.org/r/20230410183814.260518-1-cemkaya.boun@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index a428e17f0325..1d59163a882c 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -45,6 +45,13 @@ static struct snd_soc_card acp6x_card = { }; static const struct dmi_system_id yc_acp_quirk_table[] = { + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5525"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit 1.4.1 From f3e2f3e0a7af7c463e3e8e00ce280c133591e284 Mon Sep 17 00:00:00 2001 From: Baishan Jiang Date: Wed, 12 Apr 2023 16:40:43 +0800 Subject: ASoC: amd: yc: Add ThinkBook 14 G5+ ARP to quirks list for acp6x [ Upstream commit a8f5da0bf4d85a6ad03810d902aba61c572102a6 ] ThinkBook 14 G5+ ARP uses Ryzen 7735H processor, and has the same microphone problem as ThinkBook 14 G4+ ARA. Adding 21HY to acp6x quirks table enables microphone for ThinkBook 14 G5+ ARP. Signed-off-by: Baishan Jiang Link: https://lore.kernel.org/r/OS3P286MB1711DD6556284B69C79C0C4FE19B9@OS3P286MB1711.JPNP286.PROD.OUTLOOK.COM Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 1d59163a882c..b9958e555367 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -185,6 +185,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "21EN"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21HY"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit 1.4.1 From 10ba1c342451cd045997b04ecbe3d56b27774ebd Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Mon, 3 Apr 2023 20:48:29 -0600 Subject: HID: apple: Set the tilde quirk flag on the Geyser 3 [ Upstream commit 29e1ecc197d410ee59c8877098d54cf417075f7d ] I was finally able to obtain a MacBook1,1 to test and I've now confirmed that it has the tilde key quirk as well: Product Model Year System CPU Shape Labels Country Quirky ============================================================================ 05ac:0218 A1181 2006 MacBook1,1 T2500 ISO British 13 Yes Signed-off-by: Alex Henrie Link: https://lore.kernel.org/r/20230404024829.13982-1-alexhenrie24@gmail.com Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-apple.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index f21b1c4ca825..37b2ce9b50fe 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -854,7 +854,8 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, -- cgit 1.4.1 From 37358a22a334366b514d53d95d8f5f72e56bc80f Mon Sep 17 00:00:00 2001 From: weiliang1503 Date: Thu, 30 Mar 2023 19:56:38 +0800 Subject: HID: Ignore battery for ELAN touchscreen on ROG Flow X13 GV301RA [ Upstream commit 35903009dbde804a1565dc89e431c0f15179f054 ] Ignore the reported battery level of the built-in touchscreen to suppress battery warnings when a stylus is used. The device ID was added and the battery ignore quirk was enabled. Signed-off-by: weiliang1503 Link: https://lore.kernel.org/r/20230330115638.16146-1-weiliang1503@gmail.com Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-input.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 2235d78784b1..53c6692d7771 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -415,6 +415,7 @@ #define I2C_DEVICE_ID_HP_SPECTRE_X360_15 0x2817 #define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF #define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8 +#define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82 #define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544 #define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706 #define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index c3f80b516f39..3acaaca888ac 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -372,6 +372,8 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN), -- cgit 1.4.1 From 1edff076cc41b8106b84f6c0a5011e8a440c489f Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Thu, 13 Apr 2023 11:17:43 -0700 Subject: HID: wacom: generic: Set battery quirk only when we see battery data [ Upstream commit bea407a427baa019758f29f4d31b26f008bb8cc6 ] Some devices will include battery status usages in the HID descriptor but we won't see that battery data for one reason or another. For example, AES sensors won't send battery data unless an AES pen is in proximity. If a user does not have an AES pen but instead only interacts with the AES touchscreen with their fingers then there is no need for us to create a battery object. Similarly, if a family of peripherals shares the same HID descriptor between wired-only and wireless-capable SKUs, users of the former may never see a battery event and will not want a power_supply object created. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217062 Link: https://gitlab.gnome.org/GNOME/gnome-control-center/-/issues/2354 Signed-off-by: Jason Gerecke Tested-by: Mario Limonciello Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/wacom_wac.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 0c6a82c665c1..d2f500242ed4 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1963,18 +1963,7 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, static void wacom_wac_battery_usage_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { - struct wacom *wacom = hid_get_drvdata(hdev); - struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct wacom_features *features = &wacom_wac->features; - unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); - - switch (equivalent_usage) { - case HID_DG_BATTERYSTRENGTH: - case WACOM_HID_WD_BATTERY_LEVEL: - case WACOM_HID_WD_BATTERY_CHARGING: - features->quirks |= WACOM_QUIRK_BATTERY; - break; - } + return; } static void wacom_wac_battery_event(struct hid_device *hdev, struct hid_field *field, @@ -1995,18 +1984,21 @@ static void wacom_wac_battery_event(struct hid_device *hdev, struct hid_field *f wacom_wac->hid_data.bat_connected = 1; wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; } + wacom_wac->features.quirks |= WACOM_QUIRK_BATTERY; break; case WACOM_HID_WD_BATTERY_LEVEL: value = value * 100 / (field->logical_maximum - field->logical_minimum); wacom_wac->hid_data.battery_capacity = value; wacom_wac->hid_data.bat_connected = 1; wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; + wacom_wac->features.quirks |= WACOM_QUIRK_BATTERY; break; case WACOM_HID_WD_BATTERY_CHARGING: wacom_wac->hid_data.bat_charging = value; wacom_wac->hid_data.ps_connected = value; wacom_wac->hid_data.bat_connected = 1; wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO; + wacom_wac->features.quirks |= WACOM_QUIRK_BATTERY; break; } } @@ -2022,18 +2014,15 @@ static void wacom_wac_battery_report(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct wacom_features *features = &wacom_wac->features; - if (features->quirks & WACOM_QUIRK_BATTERY) { - int status = wacom_wac->hid_data.bat_status; - int capacity = wacom_wac->hid_data.battery_capacity; - bool charging = wacom_wac->hid_data.bat_charging; - bool connected = wacom_wac->hid_data.bat_connected; - bool powered = wacom_wac->hid_data.ps_connected; + int status = wacom_wac->hid_data.bat_status; + int capacity = wacom_wac->hid_data.battery_capacity; + bool charging = wacom_wac->hid_data.bat_charging; + bool connected = wacom_wac->hid_data.bat_connected; + bool powered = wacom_wac->hid_data.ps_connected; - wacom_notify_battery(wacom_wac, status, capacity, charging, - connected, powered); - } + wacom_notify_battery(wacom_wac, status, capacity, charging, + connected, powered); } static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, -- cgit 1.4.1 From 6a4cef8244de439f203c5f2216d8ec3d015f8c4b Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Thu, 16 Mar 2023 16:11:49 +0800 Subject: usb: typec: tcpm: fix multiple times discover svids error [ Upstream commit dac3b192107b978198e89ec0f77375738352e0c8 ] PD3.0 Spec 6.4.4.3.2 say that only Responder supports 12 or more SVIDs, the Discover SVIDs Command Shall be executed multiple times until a Discover SVIDs VDO is returned ending either with a SVID value of 0x0000 in the last part of the last VDO or with a VDO containing two SVIDs with values of 0x0000. In the current implementation, if the last VDO does not find that the Discover SVIDs Command would be executed multiple times even if the Responder SVIDs are less than 12, and we found some odd dockers just meet this case. So fix it. Acked-by: Heikki Krogerus Signed-off-by: Frank Wang Link: https://lore.kernel.org/r/20230316081149.24519-1-frank.wang@rock-chips.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/typec/tcpm/tcpm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 032d21a96779..524099634a1d 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1514,7 +1514,21 @@ static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt) pmdata->svids[pmdata->nsvids++] = svid; tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid); } - return true; + + /* + * PD3.0 Spec 6.4.4.3.2: The SVIDs are returned 2 per VDO (see Table + * 6-43), and can be returned maximum 6 VDOs per response (see Figure + * 6-19). If the Respondersupports 12 or more SVID then the Discover + * SVIDs Command Shall be executed multiple times until a Discover + * SVIDs VDO is returned ending either with a SVID value of 0x0000 in + * the last part of the last VDO or with a VDO containing two SVIDs + * with values of 0x0000. + * + * However, some odd dockers support SVIDs less than 12 but without + * 0x0000 in the last VDO, so we need to break the Discover SVIDs + * request and return false here. + */ + return cnt == 7; abort: tcpm_log(port, "SVID_DISCOVERY_MAX(%d) too low!", SVID_DISCOVERY_MAX); return false; -- cgit 1.4.1 From af4d6dbb1a92ea424ad1ba1d0c88c7fa2345d872 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 18 Apr 2023 13:14:06 +0300 Subject: serial: 8250: Reinit port->pm on port specific driver unbind [ Upstream commit 04e82793f068d2f0ffe62fcea03d007a8cdc16a7 ] When we unbind a serial port hardware specific 8250 driver, the generic serial8250 driver takes over the port. After that we see an oops about 10 seconds later. This can produce the following at least on some TI SoCs: Unhandled fault: imprecise external abort (0x1406) Internal error: : 1406 [#1] SMP ARM Turns out that we may still have the serial port hardware specific driver port->pm in use, and serial8250_pm() tries to call it after the port specific driver is gone: serial8250_pm [8250_base] from uart_change_pm+0x54/0x8c [serial_base] uart_change_pm [serial_base] from uart_hangup+0x154/0x198 [serial_base] uart_hangup [serial_base] from __tty_hangup.part.0+0x328/0x37c __tty_hangup.part.0 from disassociate_ctty+0x154/0x20c disassociate_ctty from do_exit+0x744/0xaac do_exit from do_group_exit+0x40/0x8c do_group_exit from __wake_up_parent+0x0/0x1c Let's fix the issue by calling serial8250_set_defaults() in serial8250_unregister_port(). This will set the port back to using the serial8250 default functions, and sets the port->pm to point to serial8250_pm. Signed-off-by: Tony Lindgren Link: https://lore.kernel.org/r/20230418101407.12403-1-tony@atomide.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/8250/8250_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 94fbf0add2ce..81a5dab1a828 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1157,6 +1157,7 @@ void serial8250_unregister_port(int line) uart->port.type = PORT_UNKNOWN; uart->port.dev = &serial8250_isa_devs->dev; uart->capabilities = 0; + serial8250_init_port(uart); serial8250_apply_quirks(uart); uart_add_one_port(&serial8250_reg, &uart->port); } else { -- cgit 1.4.1 From eaa182a6c81cca640cbf2aa5f203d64e5c7fc166 Mon Sep 17 00:00:00 2001 From: "Rodríguez Barbarin, José Javier" Date: Tue, 11 Apr 2023 10:33:28 +0200 Subject: mcb-pci: Reallocate memory region to avoid memory overlapping [ Upstream commit 9be24faadd085c284890c3afcec7a0184642315a ] mcb-pci requests a fixed-size memory region to parse the chameleon table, however, if the chameleon table is smaller that the allocated region, it could overlap with the IP Cores' memory regions. After parsing the chameleon table, drop/reallocate the memory region with the actual chameleon table size. Co-developed-by: Jorge Sanjuan Garcia Signed-off-by: Jorge Sanjuan Garcia Signed-off-by: Javier Rodriguez Signed-off-by: Johannes Thumshirn Link: https://lore.kernel.org/r/20230411083329.4506-3-jth@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/mcb/mcb-pci.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c index dc88232d9af8..53d9202ff9a7 100644 --- a/drivers/mcb/mcb-pci.c +++ b/drivers/mcb/mcb-pci.c @@ -31,7 +31,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct resource *res; struct priv *priv; - int ret; + int ret, table_size; unsigned long flags; priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL); @@ -90,7 +90,30 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret < 0) goto out_mcb_bus; - dev_dbg(&pdev->dev, "Found %d cells\n", ret); + table_size = ret; + + if (table_size < CHAM_HEADER_SIZE) { + /* Release the previous resources */ + devm_iounmap(&pdev->dev, priv->base); + devm_release_mem_region(&pdev->dev, priv->mapbase, CHAM_HEADER_SIZE); + + /* Then, allocate it again with the actual chameleon table size */ + res = devm_request_mem_region(&pdev->dev, priv->mapbase, + table_size, + KBUILD_MODNAME); + if (!res) { + dev_err(&pdev->dev, "Failed to request PCI memory\n"); + ret = -EBUSY; + goto out_mcb_bus; + } + + priv->base = devm_ioremap(&pdev->dev, priv->mapbase, table_size); + if (!priv->base) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + ret = -ENOMEM; + goto out_mcb_bus; + } + } mcb_bus_add_devices(priv->bus); -- cgit 1.4.1 From fa825017fb15e44a3be061e2ed379601dc83ef3d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 12 Apr 2023 10:24:07 -0700 Subject: sched: Fix KCSAN noinstr violation [ Upstream commit e0b081d17a9f4e5c0cbb0e5fbeb1abe3de0f7e4e ] With KCSAN enabled, end_of_stack() can get out-of-lined. Force it inline. Fixes the following warnings: vmlinux.o: warning: objtool: check_stackleak_irqoff+0x2b: call to end_of_stack() leaves .noinstr.text section Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/cc1b4d73d3a428a00d206242a68fdf99a934ca7b.1681320026.git.jpoimboe@kernel.org Signed-off-by: Sasha Levin --- include/linux/sched/task_stack.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h index 5e799a47431e..f158b025c175 100644 --- a/include/linux/sched/task_stack.h +++ b/include/linux/sched/task_stack.h @@ -23,7 +23,7 @@ static __always_inline void *task_stack_page(const struct task_struct *task) #define setup_thread_stack(new,old) do { } while(0) -static inline unsigned long *end_of_stack(const struct task_struct *task) +static __always_inline unsigned long *end_of_stack(const struct task_struct *task) { #ifdef CONFIG_STACK_GROWSUP return (unsigned long *)((unsigned long)task->stack + THREAD_SIZE) - 1; -- cgit 1.4.1 From 4e2df9111887c6147d596549b83801f5b4309112 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 12 Apr 2023 10:24:08 -0700 Subject: lkdtm/stackleak: Fix noinstr violation [ Upstream commit f571da059f86fd9d432aea32c9c7e5aaa53245d8 ] Fixes the following warning: vmlinux.o: warning: objtool: check_stackleak_irqoff+0x2b6: call to _printk() leaves .noinstr.text section Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/ee5209f53aa0a62aea58be18f2b78b17606779a6.1681320026.git.jpoimboe@kernel.org Signed-off-by: Sasha Levin --- drivers/misc/lkdtm/stackleak.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/stackleak.c index 025b133297a6..f1d022160913 100644 --- a/drivers/misc/lkdtm/stackleak.c +++ b/drivers/misc/lkdtm/stackleak.c @@ -43,12 +43,14 @@ static void noinstr check_stackleak_irqoff(void) * STACK_END_MAGIC, and in either casee something is seriously wrong. */ if (current_sp < task_stack_low || current_sp >= task_stack_high) { + instrumentation_begin(); pr_err("FAIL: current_stack_pointer (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n", current_sp, task_stack_low, task_stack_high - 1); test_failed = true; goto out; } if (lowest_sp < task_stack_low || lowest_sp >= task_stack_high) { + instrumentation_begin(); pr_err("FAIL: current->lowest_stack (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n", lowest_sp, task_stack_low, task_stack_high - 1); test_failed = true; @@ -86,11 +88,14 @@ static void noinstr check_stackleak_irqoff(void) if (*(unsigned long *)poison_low == STACKLEAK_POISON) continue; + instrumentation_begin(); pr_err("FAIL: non-poison value %lu bytes below poison boundary: 0x%lx\n", poison_high - poison_low, *(unsigned long *)poison_low); test_failed = true; + goto out; } + instrumentation_begin(); pr_info("stackleak stack usage:\n" " high offset: %lu bytes\n" " current: %lu bytes\n" @@ -113,6 +118,7 @@ out: } else { pr_info("OK: the rest of the thread stack is properly erased\n"); } + instrumentation_end(); } static void lkdtm_STACKLEAK_ERASING(void) -- cgit 1.4.1 From 895130e63c93926f07caf5db286b97bd27b81de9 Mon Sep 17 00:00:00 2001 From: Hao Zeng Date: Wed, 26 Apr 2023 09:05:27 +0800 Subject: recordmcount: Fix memory leaks in the uwrite function [ Upstream commit fa359d068574d29e7d2f0fdd0ebe4c6a12b5cfb9 ] Common realloc mistake: 'file_append' nulled but not freed upon failure Link: https://lkml.kernel.org/r/20230426010527.703093-1-zenghao@kylinos.cn Signed-off-by: Hao Zeng Suggested-by: Steven Rostedt Signed-off-by: Steven Rostedt (Google) Signed-off-by: Sasha Levin --- scripts/recordmcount.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index cce12e1971d8..ec692af8ce9e 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -102,6 +102,7 @@ static ssize_t uwrite(void const *const buf, size_t const count) { size_t cnt = count; off_t idx = 0; + void *p = NULL; file_updated = 1; @@ -109,7 +110,10 @@ static ssize_t uwrite(void const *const buf, size_t const count) off_t aoffset = (file_ptr + count) - file_end; if (aoffset > file_append_size) { - file_append = realloc(file_append, aoffset); + p = realloc(file_append, aoffset); + if (!p) + free(file_append); + file_append = p; file_append_size = aoffset; } if (!file_append) { -- cgit 1.4.1 From 81e8f1abd08f011125e11e9d9c666b482ddc0c3e Mon Sep 17 00:00:00 2001 From: Eugene Huang Date: Tue, 14 Mar 2023 17:06:18 +0800 Subject: soundwire: dmi-quirks: add remapping for Intel 'Rooks County' NUC M15 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 01b33e284ca28cc977bdcfb23be2c719f2139175 ] Same DSDT problem as the HP Omen 16-k0005TX, except rt1316 amp is on link2. Link: https://github.com/thesofproject/linux/issues/4088 Signed-off-by: Eugene Huang Reviewed-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20230314090618.498716-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/dmi-quirks.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 7969881f126d..58ea013fa918 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -73,6 +73,23 @@ static const struct adr_remap hp_omen_16[] = { {} }; +/* + * Intel NUC M15 LAPRC510 and LAPRC710 + */ +static const struct adr_remap intel_rooks_county[] = { + /* rt711-sdca on link0 */ + { + 0x000020025d071100ull, + 0x000030025d071101ull + }, + /* rt1316-sdca on link2 */ + { + 0x000120025d071100ull, + 0x000230025d131601ull + }, + {} +}; + static const struct dmi_system_id adr_remap_quirk_table[] = { /* TGL devices */ { @@ -98,6 +115,14 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)intel_tgl_bios, }, + { + /* quirk used for NUC15 'Rooks County' LAPRC510 and LAPRC710 skews */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"), + DMI_MATCH(DMI_PRODUCT_NAME, "LAPRC"), + }, + .driver_data = (void *)intel_rooks_county, + }, { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), -- cgit 1.4.1 From 3060b08d633a285f1253afb69b139c4a117de508 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Fri, 10 Feb 2023 23:43:08 +0100 Subject: phy: st: miphy28lp: use _poll_timeout functions for waits [ Upstream commit e3be4dd2c8d8aabfd2c3127d0e2e5754d3ae82d6 ] This commit introduces _poll_timeout functions usage instead of wait loops waiting for a status bit. Signed-off-by: Alain Volmat Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20230210224309.98452-1-avolmat@me.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/st/phy-miphy28lp.c | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/drivers/phy/st/phy-miphy28lp.c b/drivers/phy/st/phy-miphy28lp.c index 068160a34f5c..e30305b77f0d 100644 --- a/drivers/phy/st/phy-miphy28lp.c +++ b/drivers/phy/st/phy-miphy28lp.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -484,19 +485,11 @@ static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy) static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy) { - unsigned long finish = jiffies + 5 * HZ; u8 val; /* Waiting for Compensation to complete */ - do { - val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6); - - if (time_after_eq(jiffies, finish)) - return -EBUSY; - cpu_relax(); - } while (!(val & COMP_DONE)); - - return 0; + return readb_relaxed_poll_timeout(miphy_phy->base + MIPHY_COMP_FSM_6, + val, val & COMP_DONE, 1, 5 * USEC_PER_SEC); } @@ -805,7 +798,6 @@ static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy) static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) { - unsigned long finish = jiffies + 5 * HZ; u8 mask = HFC_PLL | HFC_RDY; u8 val; @@ -816,21 +808,14 @@ static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) if (miphy_phy->type == PHY_TYPE_SATA) mask |= PHY_RDY; - do { - val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1); - if ((val & mask) != mask) - cpu_relax(); - else - return 0; - } while (!time_after_eq(jiffies, finish)); - - return -EBUSY; + return readb_relaxed_poll_timeout(miphy_phy->base + MIPHY_STATUS_1, + val, (val & mask) == mask, 1, + 5 * USEC_PER_SEC); } static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) { struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - unsigned long finish = jiffies + 5 * HZ; u32 val; if (!miphy_phy->osc_rdy) @@ -839,17 +824,10 @@ static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) if (!miphy_phy->syscfg_reg[SYSCFG_STATUS]) return -EINVAL; - do { - regmap_read(miphy_dev->regmap, - miphy_phy->syscfg_reg[SYSCFG_STATUS], &val); - - if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) - cpu_relax(); - else - return 0; - } while (!time_after_eq(jiffies, finish)); - - return -EBUSY; + return regmap_read_poll_timeout(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_STATUS], + val, val & MIPHY_OSC_RDY, 1, + 5 * USEC_PER_SEC); } static int miphy28lp_get_resource_byname(struct device_node *child, -- cgit 1.4.1 From 60eb1afb4fb6f79d343844fc3549c8028e03b11e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 22 Feb 2023 15:44:12 +0100 Subject: soundwire: qcom: gracefully handle too many ports in DT [ Upstream commit 2367e0ecb498764e95cfda691ff0828f7d25f9a4 ] There are two issues related to the number of ports coming from Devicetree when exceeding in total QCOM_SDW_MAX_PORTS. Both lead to incorrect memory accesses: 1. With DTS having too big value of input or output ports, the driver, when copying port parameters from local/stack arrays into 'pconfig' array in 'struct qcom_swrm_ctrl', will iterate over their sizes. 2. If DTS also has too many parameters for these ports (e.g. qcom,ports-sinterval-low), the driver will overflow buffers on the stack when reading these properties from DTS. Add a sanity check so incorrect DTS will not cause kernel memory corruption. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230222144412.237832-2-krzysztof.kozlowski@linaro.org Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/qcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 866026185c66..21c50972047f 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1209,6 +1209,9 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) ctrl->num_dout_ports = val; nports = ctrl->num_dout_ports + ctrl->num_din_ports; + if (nports > QCOM_SDW_MAX_PORTS) + return -EINVAL; + /* Valid port numbers are from 1-14, so mask out port 0 explicitly */ set_bit(0, &ctrl->dout_port_mask); set_bit(0, &ctrl->din_port_mask); -- cgit 1.4.1 From 4e5e9da139c007dfc397a159093b4c4187ee67fa Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 6 Apr 2023 14:46:40 +0100 Subject: soundwire: bus: Fix unbalanced pm_runtime_put() causing usage count underflow [ Upstream commit e9537962519e88969f5f69cd0571eb4f6984403c ] This reverts commit 443a98e649b4 ("soundwire: bus: use pm_runtime_resume_and_get()") Change calls to pm_runtime_resume_and_get() back to pm_runtime_get_sync(). This fixes a usage count underrun caused by doing a pm_runtime_put() even though pm_runtime_resume_and_get() returned an error. The three affected functions ignore -EACCES error from trying to get pm_runtime, and carry on, including a put at the end of the function. But pm_runtime_resume_and_get() does not increment the usage count if it returns an error. So in the -EACCES case you must not call pm_runtime_put(). The documentation for pm_runtime_get_sync() says: "Consider using pm_runtime_resume_and_get() ... as this is likely to result in cleaner code." In this case I don't think it results in cleaner code because the pm_runtime_put() at the end of the function would have to be conditional on the return value from pm_runtime_resume_and_get() at the top of the function. pm_runtime_get_sync() doesn't have this problem because it always increments the count, so always needs a put. The code can just flow through and do the pm_runtime_put() unconditionally. Signed-off-by: Richard Fitzgerald Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20230406134640.8582-1-rf@opensource.cirrus.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/bus.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 76515c33e639..4fd221d0cc81 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -571,9 +571,11 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { int ret; - ret = pm_runtime_resume_and_get(&slave->dev); - if (ret < 0 && ret != -EACCES) + ret = pm_runtime_get_sync(&slave->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(&slave->dev); return ret; + } ret = sdw_nread_no_pm(slave, addr, count, val); @@ -595,9 +597,11 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val) { int ret; - ret = pm_runtime_resume_and_get(&slave->dev); - if (ret < 0 && ret != -EACCES) + ret = pm_runtime_get_sync(&slave->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(&slave->dev); return ret; + } ret = sdw_nwrite_no_pm(slave, addr, count, val); @@ -1565,9 +1569,10 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) sdw_modify_slave_status(slave, SDW_SLAVE_ALERT); - ret = pm_runtime_resume_and_get(&slave->dev); + ret = pm_runtime_get_sync(&slave->dev); if (ret < 0 && ret != -EACCES) { dev_err(&slave->dev, "Failed to resume device: %d\n", ret); + pm_runtime_put_noidle(&slave->dev); return ret; } -- cgit 1.4.1 From d3ee2f9e30690989c069f0de438d147b91f5601a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 1 Mar 2023 10:54:02 +0100 Subject: mfd: intel_soc_pmic_chtwc: Add Lenovo Yoga Book X90F to intel_cht_wc_models [ Upstream commit ded99b89d25fd73a1d7bd910378e0339fd9d4c4a ] The Android Lenovo Yoga Book X90F / X90L uses the same charger / fuelgauge setup as the already supported Windows Lenovo Yoga Book X91F/L, add a DMI match for this to intel_cht_wc_models with driver_data set to INTEL_CHT_WC_LENOVO_YOGABOOK1. When the quirk for the X91F/L was initially added it was written to also apply to the X90F/L but this does not work because the Android version of the Yoga Book uses completely different DMI strings. Also adjust the X91F/L quirk to reflect that it only applies to the X91F/L models. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230301095402.28582-1-hdegoede@redhat.com Signed-off-by: Sasha Levin --- drivers/mfd/intel_soc_pmic_chtwc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/intel_soc_pmic_chtwc.c b/drivers/mfd/intel_soc_pmic_chtwc.c index 9216f0d34206..a82b7cb661b7 100644 --- a/drivers/mfd/intel_soc_pmic_chtwc.c +++ b/drivers/mfd/intel_soc_pmic_chtwc.c @@ -159,11 +159,19 @@ static const struct dmi_system_id cht_wc_model_dmi_ids[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), }, }, { - /* Lenovo Yoga Book X90F / X91F / X91L */ + /* Lenovo Yoga Book X90F / X90L */ .driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YOGABOOK1, .matches = { - /* Non exact match to match all versions */ - DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), + }, + }, { + /* Lenovo Yoga Book X91F / X91L */ + .driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YOGABOOK1, + .matches = { + /* Non exact match to match F + L versions */ + DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), }, }, { } -- cgit 1.4.1 From 77f43c014a770c4dcbdeed7cda6884c29382eb0f Mon Sep 17 00:00:00 2001 From: Qiang Ning Date: Thu, 30 Mar 2023 10:43:53 +0800 Subject: mfd: dln2: Fix memory leak in dln2_probe() [ Upstream commit 96da8f148396329ba769246cb8ceaa35f1ddfc48 ] When dln2_setup_rx_urbs() in dln2_probe() fails, error out_free forgets to call usb_put_dev() to decrease the refcount of dln2->usb_dev. Fix this by adding usb_put_dev() in the error handling code of dln2_probe(). Signed-off-by: Qiang Ning Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230330024353.4503-1-qning0106@126.com Signed-off-by: Sasha Levin --- drivers/mfd/dln2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c index 6cd0b0c752d6..c3149729cec2 100644 --- a/drivers/mfd/dln2.c +++ b/drivers/mfd/dln2.c @@ -827,6 +827,7 @@ out_stop_rx: dln2_stop_rx_urbs(dln2); out_free: + usb_put_dev(dln2->usb_dev); dln2_free(dln2); return ret; -- cgit 1.4.1 From e112b2e265690f6549357d511ccb4e7d5692cf4d Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 30 Mar 2023 16:26:18 +0300 Subject: mfd: intel-lpss: Add Intel Meteor Lake PCH-S LPSS PCI IDs [ Upstream commit 72d4a1683741ee578da0e265886e6a7f3d42266c ] Add Intel Meteor Lake PCH-S also called as Meteor Point-S LPSS PCI IDs. Signed-off-by: Jarkko Nikula Acked-by: Andy Shevchenko Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230330132618.4108665-1-jarkko.nikula@linux.intel.com Signed-off-by: Sasha Levin --- drivers/mfd/intel-lpss-pci.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index dde31c50a632..699f44ffff0e 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -447,6 +447,21 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x7e79), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x7e7a), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x7e7b), (kernel_ulong_t)&bxt_i2c_info }, + /* MTP-S */ + { PCI_VDEVICE(INTEL, 0x7f28), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f29), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f2a), (kernel_ulong_t)&tgl_info }, + { PCI_VDEVICE(INTEL, 0x7f2b), (kernel_ulong_t)&tgl_info }, + { PCI_VDEVICE(INTEL, 0x7f4c), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f4d), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f4e), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f4f), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f5c), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f5d), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f5e), (kernel_ulong_t)&tgl_info }, + { PCI_VDEVICE(INTEL, 0x7f5f), (kernel_ulong_t)&tgl_info }, + { PCI_VDEVICE(INTEL, 0x7f7a), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f7b), (kernel_ulong_t)&bxt_i2c_info }, /* LKF */ { PCI_VDEVICE(INTEL, 0x98a8), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x98a9), (kernel_ulong_t)&bxt_uart_info }, -- cgit 1.4.1 From c9888aaed1a25a0d89495e7b525136eb7b8b4faf Mon Sep 17 00:00:00 2001 From: "Guilherme G. Piccoli" Date: Mon, 20 Feb 2023 18:11:05 -0300 Subject: parisc: Replace regular spinlock with spin_trylock on panic path [ Upstream commit 829632dae8321787525ee37dc4828bbe6edafdae ] The panic notifiers' callbacks execute in an atomic context, with interrupts/preemption disabled, and all CPUs not running the panic function are off, so it's very dangerous to wait on a regular spinlock, there's a risk of deadlock. Refactor the panic notifier of parisc/power driver to make use of spin_trylock - for that, we've added a second version of the soft-power function. Also, some comments were reorganized and trailing white spaces, useless header inclusion and blank lines were removed. Cc: "James E.J. Bottomley" Cc: Jeroen Roovers Acked-by: Helge Deller # parisc Signed-off-by: Guilherme G. Piccoli Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- arch/parisc/include/asm/pdc.h | 1 + arch/parisc/kernel/firmware.c | 27 +++++++++++++++++++++++---- drivers/parisc/power.c | 16 ++++++++++------ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h index fcbcf9a96c11..77622558bf65 100644 --- a/arch/parisc/include/asm/pdc.h +++ b/arch/parisc/include/asm/pdc.h @@ -80,6 +80,7 @@ int pdc_do_firm_test_reset(unsigned long ftc_bitmap); int pdc_do_reset(void); int pdc_soft_power_info(unsigned long *power_reg); int pdc_soft_power_button(int sw_control); +int pdc_soft_power_button_panic(int sw_control); void pdc_io_reset(void); void pdc_io_reset_devices(void); int pdc_iodc_getc(void); diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c index bd325f2b5349..3e051a973e9b 100644 --- a/arch/parisc/kernel/firmware.c +++ b/arch/parisc/kernel/firmware.c @@ -1232,15 +1232,18 @@ int __init pdc_soft_power_info(unsigned long *power_reg) } /* - * pdc_soft_power_button - Control the soft power button behaviour - * @sw_control: 0 for hardware control, 1 for software control + * pdc_soft_power_button{_panic} - Control the soft power button behaviour + * @sw_control: 0 for hardware control, 1 for software control * * * This PDC function places the soft power button under software or * hardware control. - * Under software control the OS may control to when to allow to shut - * down the system. Under hardware control pressing the power button + * Under software control the OS may control to when to allow to shut + * down the system. Under hardware control pressing the power button * powers off the system immediately. + * + * The _panic version relies on spin_trylock to prevent deadlock + * on panic path. */ int pdc_soft_power_button(int sw_control) { @@ -1254,6 +1257,22 @@ int pdc_soft_power_button(int sw_control) return retval; } +int pdc_soft_power_button_panic(int sw_control) +{ + int retval; + unsigned long flags; + + if (!spin_trylock_irqsave(&pdc_lock, flags)) { + pr_emerg("Couldn't enable soft power button\n"); + return -EBUSY; /* ignored by the panic notifier */ + } + + retval = mem_pdc_call(PDC_SOFT_POWER, PDC_SOFT_POWER_ENABLE, __pa(pdc_result), sw_control); + spin_unlock_irqrestore(&pdc_lock, flags); + + return retval; +} + /* * pdc_io_reset - Hack to avoid overlapping range registers of Bridges devices. * Primarily a problem on T600 (which parisc-linux doesn't support) but diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c index 456776bd8ee6..6f5e5f0230d3 100644 --- a/drivers/parisc/power.c +++ b/drivers/parisc/power.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -175,16 +174,21 @@ static void powerfail_interrupt(int code, void *x) -/* parisc_panic_event() is called by the panic handler. - * As soon as a panic occurs, our tasklets above will not be - * executed any longer. This function then re-enables the - * soft-power switch and allows the user to switch off the system +/* + * parisc_panic_event() is called by the panic handler. + * + * As soon as a panic occurs, our tasklets above will not + * be executed any longer. This function then re-enables + * the soft-power switch and allows the user to switch off + * the system. We rely in pdc_soft_power_button_panic() + * since this version spin_trylocks (instead of regular + * spinlock), preventing deadlocks on panic path. */ static int parisc_panic_event(struct notifier_block *this, unsigned long event, void *ptr) { /* re-enable the soft-power switch */ - pdc_soft_power_button(0); + pdc_soft_power_button_panic(0); return NOTIFY_DONE; } -- cgit 1.4.1 From 1189b7f4954084a5cfb10ab48f4e41fc9c37b516 Mon Sep 17 00:00:00 2001 From: Jorge Lopez Date: Thu, 20 Oct 2022 15:10:28 -0500 Subject: platform/x86: Move existing HP drivers to a new hp subdir [ Upstream commit 6e9b8992b122cb12688bd259fc99e67d1be234eb ] The purpose of this patch is to provide a central location where all HP related drivers are found. HP drivers will recide under drivers/platform/x86/hp directory. Introduce changes to Kconfig file to list all HP driver under "HP X86 Platform Specific Device Drivers" menu option. Additional changes include update MAINTAINERS file to indicate hp related drivers new path. Signed-off-by: Jorge Lopez Link: https://lore.kernel.org/r/20221020201033.12790-2-jorge.lopez2@hp.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Stable-dep-of: decab2825c3e ("platform/x86: hp-wmi: add micmute to hp_wmi_keymap struct") Signed-off-by: Sasha Levin --- MAINTAINERS | 4 +- drivers/platform/x86/Kconfig | 43 +- drivers/platform/x86/Makefile | 4 +- drivers/platform/x86/hp-wmi.c | 1570 ---------------------------------- drivers/platform/x86/hp/Kconfig | 63 ++ drivers/platform/x86/hp/Makefile | 10 + drivers/platform/x86/hp/hp-wmi.c | 1570 ++++++++++++++++++++++++++++++++++ drivers/platform/x86/hp/hp_accel.c | 387 +++++++++ drivers/platform/x86/hp/tc1100-wmi.c | 265 ++++++ drivers/platform/x86/hp_accel.c | 387 --------- drivers/platform/x86/tc1100-wmi.c | 265 ------ 11 files changed, 2299 insertions(+), 2269 deletions(-) delete mode 100644 drivers/platform/x86/hp-wmi.c create mode 100644 drivers/platform/x86/hp/Kconfig create mode 100644 drivers/platform/x86/hp/Makefile create mode 100644 drivers/platform/x86/hp/hp-wmi.c create mode 100644 drivers/platform/x86/hp/hp_accel.c create mode 100644 drivers/platform/x86/hp/tc1100-wmi.c delete mode 100644 drivers/platform/x86/hp_accel.c delete mode 100644 drivers/platform/x86/tc1100-wmi.c diff --git a/MAINTAINERS b/MAINTAINERS index 350d7e3ba94f..301b9ba6af79 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9362,7 +9362,7 @@ F: drivers/net/wireless/intersil/hostap/ HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan -F: drivers/platform/x86/tc1100-wmi.c +F: drivers/platform/x86/hp/tc1100-wmi.c HPET: High Precision Event Timers driver M: Clemens Ladisch @@ -11858,7 +11858,7 @@ M: Eric Piel S: Maintained F: Documentation/misc-devices/lis3lv02d.rst F: drivers/misc/lis3lv02d/ -F: drivers/platform/x86/hp_accel.c +F: drivers/platform/x86/hp/hp_accel.c LIST KUNIT TEST M: David Gow diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b02a8125bc7d..1396a839dd8a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -424,24 +424,7 @@ config GPD_POCKET_FAN of the CPU temperature. Say Y or M if the kernel may be used on a GPD pocket. -config HP_ACCEL - tristate "HP laptop accelerometer" - depends on INPUT && ACPI - depends on SERIO_I8042 - select SENSORS_LIS3LV02D - select NEW_LEDS - select LEDS_CLASS - help - This driver provides support for the "Mobile Data Protection System 3D" - or "3D DriveGuard" feature of HP laptops. On such systems the driver - should load automatically (via ACPI alias). - - Support for a led indicating disk protection will be provided as - hp::hddprotect. For more information on the feature, refer to - Documentation/misc-devices/lis3lv02d.rst. - - To compile this driver as a module, choose M here: the module will - be called hp_accel. +source "drivers/platform/x86/hp/Kconfig" config WIRELESS_HOTKEY tristate "Wireless hotkey button" @@ -455,30 +438,6 @@ config WIRELESS_HOTKEY To compile this driver as a module, choose M here: the module will be called wireless-hotkey. -config HP_WMI - tristate "HP WMI extras" - depends on ACPI_WMI - depends on INPUT - depends on RFKILL || RFKILL = n - select INPUT_SPARSEKMAP - select ACPI_PLATFORM_PROFILE - select HWMON - help - Say Y here if you want to support WMI-based hotkeys on HP laptops and - to read data from WMI such as docking or ambient light sensor state. - - To compile this driver as a module, choose M here: the module will - be called hp-wmi. - -config TC1100_WMI - tristate "HP Compaq TC1100 Tablet WMI Extras" - depends on !X86_64 - depends on ACPI - depends on ACPI_WMI - help - This is a driver for the WMI extensions (wireless and bluetooth power - control) of the HP Compaq TC1100 tablet. - config IBM_RTL tristate "Device driver to enable PRTL support" depends on PCI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5a428caa654a..1d3d1b02541b 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -55,9 +55,7 @@ obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o obj-$(CONFIG_GPD_POCKET_FAN) += gpd-pocket-fan.o # Hewlett Packard -obj-$(CONFIG_HP_ACCEL) += hp_accel.o -obj-$(CONFIG_HP_WMI) += hp-wmi.o -obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o +obj-$(CONFIG_X86_PLATFORM_DRIVERS_HP) += hp/ # Hewlett Packard Enterprise obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c deleted file mode 100644 index 4a3851332ef2..000000000000 --- a/drivers/platform/x86/hp-wmi.c +++ /dev/null @@ -1,1570 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HP WMI hotkeys - * - * Copyright (C) 2008 Red Hat - * Copyright (C) 2010, 2011 Anssi Hannula - * - * Portions based on wistron_btns.c: - * Copyright (C) 2005 Miloslav Trmac - * Copyright (C) 2005 Bernhard Rosenkraenzer - * Copyright (C) 2005 Dmitry Torokhov - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Matthew Garrett "); -MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); -MODULE_LICENSE("GPL"); - -MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); -MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); - -#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" -#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" -#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 -#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required - -/* DMI board names of devices that should use the omen specific path for - * thermal profiles. - * This was obtained by taking a look in the windows omen command center - * app and parsing a json file that they use to figure out what capabilities - * the device should have. - * A device is considered an omen if the DisplayName in that list contains - * "OMEN", and it can use the thermal profile stuff if the "Feature" array - * contains "PerformanceControl". - */ -static const char * const omen_thermal_profile_boards[] = { - "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", - "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", - "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", - "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", - "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", - "8917", "8918", "8949", "894A", "89EB" -}; - -/* DMI Board names of Omen laptops that are specifically set to be thermal - * profile version 0 by the Omen Command Center app, regardless of what - * the get system design information WMI call returns - */ -static const char *const omen_thermal_profile_force_v0_boards[] = { - "8607", "8746", "8747", "8749", "874A", "8748" -}; - -enum hp_wmi_radio { - HPWMI_WIFI = 0x0, - HPWMI_BLUETOOTH = 0x1, - HPWMI_WWAN = 0x2, - HPWMI_GPS = 0x3, -}; - -enum hp_wmi_event_ids { - HPWMI_DOCK_EVENT = 0x01, - HPWMI_PARK_HDD = 0x02, - HPWMI_SMART_ADAPTER = 0x03, - HPWMI_BEZEL_BUTTON = 0x04, - HPWMI_WIRELESS = 0x05, - HPWMI_CPU_BATTERY_THROTTLE = 0x06, - HPWMI_LOCK_SWITCH = 0x07, - HPWMI_LID_SWITCH = 0x08, - HPWMI_SCREEN_ROTATION = 0x09, - HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, - HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, - HPWMI_PROXIMITY_SENSOR = 0x0C, - HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, - HPWMI_PEAKSHIFT_PERIOD = 0x0F, - HPWMI_BATTERY_CHARGE_PERIOD = 0x10, - HPWMI_SANITIZATION_MODE = 0x17, - HPWMI_OMEN_KEY = 0x1D, - HPWMI_SMART_EXPERIENCE_APP = 0x21, -}; - -/* - * struct bios_args buffer is dynamically allocated. New WMI command types - * were introduced that exceeds 128-byte data size. Changes to handle - * the data size allocation scheme were kept in hp_wmi_perform_qurey function. - */ -struct bios_args { - u32 signature; - u32 command; - u32 commandtype; - u32 datasize; - u8 data[]; -}; - -enum hp_wmi_commandtype { - HPWMI_DISPLAY_QUERY = 0x01, - HPWMI_HDDTEMP_QUERY = 0x02, - HPWMI_ALS_QUERY = 0x03, - HPWMI_HARDWARE_QUERY = 0x04, - HPWMI_WIRELESS_QUERY = 0x05, - HPWMI_BATTERY_QUERY = 0x07, - HPWMI_BIOS_QUERY = 0x09, - HPWMI_FEATURE_QUERY = 0x0b, - HPWMI_HOTKEY_QUERY = 0x0c, - HPWMI_FEATURE2_QUERY = 0x0d, - HPWMI_WIRELESS2_QUERY = 0x1b, - HPWMI_POSTCODEERROR_QUERY = 0x2a, - HPWMI_SYSTEM_DEVICE_MODE = 0x40, - HPWMI_THERMAL_PROFILE_QUERY = 0x4c, -}; - -enum hp_wmi_gm_commandtype { - HPWMI_FAN_SPEED_GET_QUERY = 0x11, - HPWMI_SET_PERFORMANCE_MODE = 0x1A, - HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, - HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, - HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, -}; - -enum hp_wmi_command { - HPWMI_READ = 0x01, - HPWMI_WRITE = 0x02, - HPWMI_ODM = 0x03, - HPWMI_GM = 0x20008, -}; - -enum hp_wmi_hardware_mask { - HPWMI_DOCK_MASK = 0x01, - HPWMI_TABLET_MASK = 0x04, -}; - -struct bios_return { - u32 sigpass; - u32 return_code; -}; - -enum hp_return_value { - HPWMI_RET_WRONG_SIGNATURE = 0x02, - HPWMI_RET_UNKNOWN_COMMAND = 0x03, - HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, - HPWMI_RET_INVALID_PARAMETERS = 0x05, -}; - -enum hp_wireless2_bits { - HPWMI_POWER_STATE = 0x01, - HPWMI_POWER_SOFT = 0x02, - HPWMI_POWER_BIOS = 0x04, - HPWMI_POWER_HARD = 0x08, - HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, -}; - -enum hp_thermal_profile_omen_v0 { - HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00, - HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01, - HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02, -}; - -enum hp_thermal_profile_omen_v1 { - HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30, - HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31, - HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, -}; - -enum hp_thermal_profile { - HP_THERMAL_PROFILE_PERFORMANCE = 0x00, - HP_THERMAL_PROFILE_DEFAULT = 0x01, - HP_THERMAL_PROFILE_COOL = 0x02, - HP_THERMAL_PROFILE_QUIET = 0x03, -}; - -#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW) -#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) - -struct bios_rfkill2_device_state { - u8 radio_type; - u8 bus_type; - u16 vendor_id; - u16 product_id; - u16 subsys_vendor_id; - u16 subsys_product_id; - u8 rfkill_id; - u8 power; - u8 unknown[4]; -}; - -/* 7 devices fit into the 128 byte buffer */ -#define HPWMI_MAX_RFKILL2_DEVICES 7 - -struct bios_rfkill2_state { - u8 unknown[7]; - u8 count; - u8 pad[8]; - struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; -}; - -static const struct key_entry hp_wmi_keymap[] = { - { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, - { KE_KEY, 0x20e6, { KEY_PROG1 } }, - { KE_KEY, 0x20e8, { KEY_MEDIA } }, - { KE_KEY, 0x2142, { KEY_MEDIA } }, - { KE_KEY, 0x213b, { KEY_INFO } }, - { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, - { KE_KEY, 0x216a, { KEY_SETUP } }, - { KE_KEY, 0x21a5, { KEY_PROG2 } }, /* HP Omen Key */ - { KE_KEY, 0x21a7, { KEY_FN_ESC } }, - { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, - { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, - { KE_KEY, 0x231b, { KEY_HELP } }, - { KE_END, 0 } -}; - -static struct input_dev *hp_wmi_input_dev; -static struct platform_device *hp_wmi_platform_dev; -static struct platform_profile_handler platform_profile_handler; -static bool platform_profile_support; -static bool zero_insize_support; - -static struct rfkill *wifi_rfkill; -static struct rfkill *bluetooth_rfkill; -static struct rfkill *wwan_rfkill; - -struct rfkill2_device { - u8 id; - int num; - struct rfkill *rfkill; -}; - -static int rfkill2_count; -static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; - -/* - * Chassis Types values were obtained from SMBIOS reference - * specification version 3.00. A complete list of system enclosures - * and chassis types is available on Table 17. - */ -static const char * const tablet_chassis_types[] = { - "30", /* Tablet*/ - "31", /* Convertible */ - "32" /* Detachable */ -}; - -#define DEVICE_MODE_TABLET 0x06 - -/* map output size to the corresponding WMI method id */ -static inline int encode_outsize_for_pvsz(int outsize) -{ - if (outsize > 4096) - return -EINVAL; - if (outsize > 1024) - return 5; - if (outsize > 128) - return 4; - if (outsize > 4) - return 3; - if (outsize > 0) - return 2; - return 1; -} - -/* - * hp_wmi_perform_query - * - * query: The commandtype (enum hp_wmi_commandtype) - * write: The command (enum hp_wmi_command) - * buffer: Buffer used as input and/or output - * insize: Size of input buffer - * outsize: Size of output buffer - * - * returns zero on success - * an HP WMI query specific error code (which is positive) - * -EINVAL if the query was not successful at all - * -EINVAL if the output buffer size exceeds buffersize - * - * Note: The buffersize must at least be the maximum of the input and output - * size. E.g. Battery info query is defined to have 1 byte input - * and 128 byte output. The caller would do: - * buffer = kzalloc(128, GFP_KERNEL); - * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) - */ -static int hp_wmi_perform_query(int query, enum hp_wmi_command command, - void *buffer, int insize, int outsize) -{ - struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; - struct bios_return *bios_return; - union acpi_object *obj = NULL; - struct bios_args *args = NULL; - int mid, actual_insize, actual_outsize; - size_t bios_args_size; - int ret; - - mid = encode_outsize_for_pvsz(outsize); - if (WARN_ON(mid < 0)) - return mid; - - actual_insize = max(insize, 128); - bios_args_size = struct_size(args, data, actual_insize); - args = kmalloc(bios_args_size, GFP_KERNEL); - if (!args) - return -ENOMEM; - - input.length = bios_args_size; - input.pointer = args; - - args->signature = 0x55434553; - args->command = command; - args->commandtype = query; - args->datasize = insize; - memcpy(args->data, buffer, flex_array_size(args, data, insize)); - - ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); - if (ret) - goto out_free; - - obj = output.pointer; - if (!obj) { - ret = -EINVAL; - goto out_free; - } - - if (obj->type != ACPI_TYPE_BUFFER) { - pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret); - ret = -EINVAL; - goto out_free; - } - - bios_return = (struct bios_return *)obj->buffer.pointer; - ret = bios_return->return_code; - - if (ret) { - if (ret != HPWMI_RET_UNKNOWN_COMMAND && - ret != HPWMI_RET_UNKNOWN_CMDTYPE) - pr_warn("query 0x%x returned error 0x%x\n", query, ret); - goto out_free; - } - - /* Ignore output data of zero size */ - if (!outsize) - goto out_free; - - actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); - memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); - memset(buffer + actual_outsize, 0, outsize - actual_outsize); - -out_free: - kfree(obj); - kfree(args); - return ret; -} - -static int hp_wmi_get_fan_speed(int fan) -{ - u8 fsh, fsl; - char fan_data[4] = { fan, 0, 0, 0 }; - - int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM, - &fan_data, sizeof(char), - sizeof(fan_data)); - - if (ret != 0) - return -EINVAL; - - fsh = fan_data[2]; - fsl = fan_data[3]; - - return (fsh << 8) | fsl; -} - -static int hp_wmi_read_int(int query) -{ - int val = 0, ret; - - ret = hp_wmi_perform_query(query, HPWMI_READ, &val, - zero_if_sup(val), sizeof(val)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return val; -} - -static int hp_wmi_get_dock_state(void) -{ - int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY); - - if (state < 0) - return state; - - return !!(state & HPWMI_DOCK_MASK); -} - -static int hp_wmi_get_tablet_mode(void) -{ - char system_device_mode[4] = { 0 }; - const char *chassis_type; - bool tablet_found; - int ret; - - chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); - if (!chassis_type) - return -ENODEV; - - tablet_found = match_string(tablet_chassis_types, - ARRAY_SIZE(tablet_chassis_types), - chassis_type) >= 0; - if (!tablet_found) - return -ENODEV; - - ret = hp_wmi_perform_query(HPWMI_SYSTEM_DEVICE_MODE, HPWMI_READ, - system_device_mode, zero_if_sup(system_device_mode), - sizeof(system_device_mode)); - if (ret < 0) - return ret; - - return system_device_mode[0] == DEVICE_MODE_TABLET; -} - -static int omen_thermal_profile_set(int mode) -{ - char buffer[2] = {0, mode}; - int ret; - - ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, - &buffer, sizeof(buffer), 0); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return mode; -} - -static bool is_omen_thermal_profile(void) -{ - const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (!board_name) - return false; - - return match_string(omen_thermal_profile_boards, - ARRAY_SIZE(omen_thermal_profile_boards), - board_name) >= 0; -} - -static int omen_get_thermal_policy_version(void) -{ - unsigned char buffer[8] = { 0 }; - int ret; - - const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (board_name) { - int matches = match_string(omen_thermal_profile_force_v0_boards, - ARRAY_SIZE(omen_thermal_profile_force_v0_boards), - board_name); - if (matches >= 0) - return 0; - } - - ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM, - &buffer, sizeof(buffer), sizeof(buffer)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return buffer[3]; -} - -static int omen_thermal_profile_get(void) -{ - u8 data; - - int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data); - - if (ret) - return ret; - - return data; -} - -static int hp_wmi_fan_speed_max_set(int enabled) -{ - int ret; - - ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM, - &enabled, sizeof(enabled), 0); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return enabled; -} - -static int hp_wmi_fan_speed_max_get(void) -{ - int val = 0, ret; - - ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM, - &val, zero_if_sup(val), sizeof(val)); - - if (ret) - return ret < 0 ? ret : -EINVAL; - - return val; -} - -static int __init hp_wmi_bios_2008_later(void) -{ - int state = 0; - int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (!ret) - return 1; - - return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; -} - -static int __init hp_wmi_bios_2009_later(void) -{ - u8 state[128]; - int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (!ret) - return 1; - - return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; -} - -static int __init hp_wmi_enable_hotkeys(void) -{ - int value = 0x6e; - int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, - sizeof(value), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static int hp_wmi_set_block(void *data, bool blocked) -{ - enum hp_wmi_radio r = (enum hp_wmi_radio) data; - int query = BIT(r + 8) | ((!blocked) << r); - int ret; - - ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, - &query, sizeof(query), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static const struct rfkill_ops hp_wmi_rfkill_ops = { - .set_block = hp_wmi_set_block, -}; - -static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) -{ - int mask = 0x200 << (r * 8); - - int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - - /* TBD: Pass error */ - WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); - - return !(wireless & mask); -} - -static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) -{ - int mask = 0x800 << (r * 8); - - int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - - /* TBD: Pass error */ - WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); - - return !(wireless & mask); -} - -static int hp_wmi_rfkill2_set_block(void *data, bool blocked) -{ - int rfkill_id = (int)(long)data; - char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; - int ret; - - ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE, - buffer, sizeof(buffer), 0); - - return ret <= 0 ? ret : -EINVAL; -} - -static const struct rfkill_ops hp_wmi_rfkill2_ops = { - .set_block = hp_wmi_rfkill2_set_block, -}; - -static int hp_wmi_rfkill2_refresh(void) -{ - struct bios_rfkill2_state state; - int err, i; - - err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (err) - return err; - - for (i = 0; i < rfkill2_count; i++) { - int num = rfkill2[i].num; - struct bios_rfkill2_device_state *devstate; - - devstate = &state.device[num]; - - if (num >= state.count || - devstate->rfkill_id != rfkill2[i].id) { - pr_warn("power configuration of the wireless devices unexpectedly changed\n"); - continue; - } - - rfkill_set_states(rfkill2[i].rfkill, - IS_SWBLOCKED(devstate->power), - IS_HWBLOCKED(devstate->power)); - } - - return 0; -} - -static ssize_t display_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t als_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_read_int(HPWMI_ALS_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t dock_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_get_dock_state(); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t tablet_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int value = hp_wmi_get_tablet_mode(); - - if (value < 0) - return value; - return sprintf(buf, "%d\n", value); -} - -static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - /* Get the POST error code of previous boot failure. */ - int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY); - - if (value < 0) - return value; - return sprintf(buf, "0x%x\n", value); -} - -static ssize_t als_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 tmp; - int ret; - - ret = kstrtou32(buf, 10, &tmp); - if (ret) - return ret; - - ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp, - sizeof(tmp), 0); - if (ret) - return ret < 0 ? ret : -EINVAL; - - return count; -} - -static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 tmp = 1; - bool clear; - int ret; - - ret = kstrtobool(buf, &clear); - if (ret) - return ret; - - if (clear == false) - return -EINVAL; - - /* Clear the POST error code. It is kept until cleared. */ - ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, - sizeof(tmp), 0); - if (ret) - return ret < 0 ? ret : -EINVAL; - - return count; -} - -static DEVICE_ATTR_RO(display); -static DEVICE_ATTR_RO(hddtemp); -static DEVICE_ATTR_RW(als); -static DEVICE_ATTR_RO(dock); -static DEVICE_ATTR_RO(tablet); -static DEVICE_ATTR_RW(postcode); - -static struct attribute *hp_wmi_attrs[] = { - &dev_attr_display.attr, - &dev_attr_hddtemp.attr, - &dev_attr_als.attr, - &dev_attr_dock.attr, - &dev_attr_tablet.attr, - &dev_attr_postcode.attr, - NULL, -}; -ATTRIBUTE_GROUPS(hp_wmi); - -static void hp_wmi_notify(u32 value, void *context) -{ - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - u32 event_id, event_data; - union acpi_object *obj; - acpi_status status; - u32 *location; - int key_code; - - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); - return; - } - - obj = (union acpi_object *)response.pointer; - - if (!obj) - return; - if (obj->type != ACPI_TYPE_BUFFER) { - pr_info("Unknown response received %d\n", obj->type); - kfree(obj); - return; - } - - /* - * Depending on ACPI version the concatenation of id and event data - * inside _WED function will result in a 8 or 16 byte buffer. - */ - location = (u32 *)obj->buffer.pointer; - if (obj->buffer.length == 8) { - event_id = *location; - event_data = *(location + 1); - } else if (obj->buffer.length == 16) { - event_id = *location; - event_data = *(location + 2); - } else { - pr_info("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); - return; - } - kfree(obj); - - switch (event_id) { - case HPWMI_DOCK_EVENT: - if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_DOCK, - hp_wmi_get_dock_state()); - if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_get_tablet_mode()); - input_sync(hp_wmi_input_dev); - break; - case HPWMI_PARK_HDD: - break; - case HPWMI_SMART_ADAPTER: - break; - case HPWMI_BEZEL_BUTTON: - case HPWMI_OMEN_KEY: - key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); - if (key_code < 0) - break; - - if (!sparse_keymap_report_event(hp_wmi_input_dev, - key_code, 1, true)) - pr_info("Unknown key code - 0x%x\n", key_code); - break; - case HPWMI_WIRELESS: - if (rfkill2_count) { - hp_wmi_rfkill2_refresh(); - break; - } - - if (wifi_rfkill) - rfkill_set_states(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI), - hp_wmi_get_hw_state(HPWMI_WIFI)); - if (bluetooth_rfkill) - rfkill_set_states(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH), - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - if (wwan_rfkill) - rfkill_set_states(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN), - hp_wmi_get_hw_state(HPWMI_WWAN)); - break; - case HPWMI_CPU_BATTERY_THROTTLE: - pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); - break; - case HPWMI_LOCK_SWITCH: - break; - case HPWMI_LID_SWITCH: - break; - case HPWMI_SCREEN_ROTATION: - break; - case HPWMI_COOLSENSE_SYSTEM_MOBILE: - break; - case HPWMI_COOLSENSE_SYSTEM_HOT: - break; - case HPWMI_PROXIMITY_SENSOR: - break; - case HPWMI_BACKLIT_KB_BRIGHTNESS: - break; - case HPWMI_PEAKSHIFT_PERIOD: - break; - case HPWMI_BATTERY_CHARGE_PERIOD: - break; - case HPWMI_SANITIZATION_MODE: - break; - case HPWMI_SMART_EXPERIENCE_APP: - break; - default: - pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); - break; - } -} - -static int __init hp_wmi_input_setup(void) -{ - acpi_status status; - int err, val; - - hp_wmi_input_dev = input_allocate_device(); - if (!hp_wmi_input_dev) - return -ENOMEM; - - hp_wmi_input_dev->name = "HP WMI hotkeys"; - hp_wmi_input_dev->phys = "wmi/input0"; - hp_wmi_input_dev->id.bustype = BUS_HOST; - - __set_bit(EV_SW, hp_wmi_input_dev->evbit); - - /* Dock */ - val = hp_wmi_get_dock_state(); - if (!(val < 0)) { - __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); - input_report_switch(hp_wmi_input_dev, SW_DOCK, val); - } - - /* Tablet mode */ - val = hp_wmi_get_tablet_mode(); - if (!(val < 0)) { - __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val); - } - - err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); - if (err) - goto err_free_dev; - - /* Set initial hardware state */ - input_sync(hp_wmi_input_dev); - - if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) - hp_wmi_enable_hotkeys(); - - status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); - if (ACPI_FAILURE(status)) { - err = -EIO; - goto err_free_dev; - } - - err = input_register_device(hp_wmi_input_dev); - if (err) - goto err_uninstall_notifier; - - return 0; - - err_uninstall_notifier: - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - err_free_dev: - input_free_device(hp_wmi_input_dev); - return err; -} - -static void hp_wmi_input_destroy(void) -{ - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - input_unregister_device(hp_wmi_input_dev); -} - -static int __init hp_wmi_rfkill_setup(struct platform_device *device) -{ - int err, wireless; - - wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); - if (wireless < 0) - return wireless; - - err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless, - sizeof(wireless), 0); - if (err) - return err; - - if (wireless & 0x1) { - wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, - RFKILL_TYPE_WLAN, - &hp_wmi_rfkill_ops, - (void *) HPWMI_WIFI); - if (!wifi_rfkill) - return -ENOMEM; - rfkill_init_sw_state(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI)); - rfkill_set_hw_state(wifi_rfkill, - hp_wmi_get_hw_state(HPWMI_WIFI)); - err = rfkill_register(wifi_rfkill); - if (err) - goto register_wifi_error; - } - - if (wireless & 0x2) { - bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, - RFKILL_TYPE_BLUETOOTH, - &hp_wmi_rfkill_ops, - (void *) HPWMI_BLUETOOTH); - if (!bluetooth_rfkill) { - err = -ENOMEM; - goto register_bluetooth_error; - } - rfkill_init_sw_state(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); - rfkill_set_hw_state(bluetooth_rfkill, - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - err = rfkill_register(bluetooth_rfkill); - if (err) - goto register_bluetooth_error; - } - - if (wireless & 0x4) { - wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, - RFKILL_TYPE_WWAN, - &hp_wmi_rfkill_ops, - (void *) HPWMI_WWAN); - if (!wwan_rfkill) { - err = -ENOMEM; - goto register_wwan_error; - } - rfkill_init_sw_state(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN)); - rfkill_set_hw_state(wwan_rfkill, - hp_wmi_get_hw_state(HPWMI_WWAN)); - err = rfkill_register(wwan_rfkill); - if (err) - goto register_wwan_error; - } - - return 0; - -register_wwan_error: - rfkill_destroy(wwan_rfkill); - wwan_rfkill = NULL; - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); -register_bluetooth_error: - rfkill_destroy(bluetooth_rfkill); - bluetooth_rfkill = NULL; - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); -register_wifi_error: - rfkill_destroy(wifi_rfkill); - wifi_rfkill = NULL; - return err; -} - -static int __init hp_wmi_rfkill2_setup(struct platform_device *device) -{ - struct bios_rfkill2_state state; - int err, i; - - err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - zero_if_sup(state), sizeof(state)); - if (err) - return err < 0 ? err : -EINVAL; - - if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { - pr_warn("unable to parse 0x1b query output\n"); - return -EINVAL; - } - - for (i = 0; i < state.count; i++) { - struct rfkill *rfkill; - enum rfkill_type type; - char *name; - - switch (state.device[i].radio_type) { - case HPWMI_WIFI: - type = RFKILL_TYPE_WLAN; - name = "hp-wifi"; - break; - case HPWMI_BLUETOOTH: - type = RFKILL_TYPE_BLUETOOTH; - name = "hp-bluetooth"; - break; - case HPWMI_WWAN: - type = RFKILL_TYPE_WWAN; - name = "hp-wwan"; - break; - case HPWMI_GPS: - type = RFKILL_TYPE_GPS; - name = "hp-gps"; - break; - default: - pr_warn("unknown device type 0x%x\n", - state.device[i].radio_type); - continue; - } - - if (!state.device[i].vendor_id) { - pr_warn("zero device %d while %d reported\n", - i, state.count); - continue; - } - - rfkill = rfkill_alloc(name, &device->dev, type, - &hp_wmi_rfkill2_ops, (void *)(long)i); - if (!rfkill) { - err = -ENOMEM; - goto fail; - } - - rfkill2[rfkill2_count].id = state.device[i].rfkill_id; - rfkill2[rfkill2_count].num = i; - rfkill2[rfkill2_count].rfkill = rfkill; - - rfkill_init_sw_state(rfkill, - IS_SWBLOCKED(state.device[i].power)); - rfkill_set_hw_state(rfkill, - IS_HWBLOCKED(state.device[i].power)); - - if (!(state.device[i].power & HPWMI_POWER_BIOS)) - pr_info("device %s blocked by BIOS\n", name); - - err = rfkill_register(rfkill); - if (err) { - rfkill_destroy(rfkill); - goto fail; - } - - rfkill2_count++; - } - - return 0; -fail: - for (; rfkill2_count > 0; rfkill2_count--) { - rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); - rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); - } - return err; -} - -static int platform_profile_omen_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) -{ - int tp; - - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; - - switch (tp) { - case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE: - case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE: - *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT: - case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT: - *profile = PLATFORM_PROFILE_BALANCED; - break; - case HP_OMEN_V0_THERMAL_PROFILE_COOL: - case HP_OMEN_V1_THERMAL_PROFILE_COOL: - *profile = PLATFORM_PROFILE_COOL; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int platform_profile_omen_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) -{ - int err, tp, tp_version; - - tp_version = omen_get_thermal_policy_version(); - - if (tp_version < 0 || tp_version > 1) - return -EOPNOTSUPP; - - switch (profile) { - case PLATFORM_PROFILE_PERFORMANCE: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE; - break; - case PLATFORM_PROFILE_BALANCED: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_DEFAULT; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT; - break; - case PLATFORM_PROFILE_COOL: - if (tp_version == 0) - tp = HP_OMEN_V0_THERMAL_PROFILE_COOL; - else - tp = HP_OMEN_V1_THERMAL_PROFILE_COOL; - break; - default: - return -EOPNOTSUPP; - } - - err = omen_thermal_profile_set(tp); - if (err < 0) - return err; - - return 0; -} - -static int thermal_profile_get(void) -{ - return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); -} - -static int thermal_profile_set(int thermal_profile) -{ - return hp_wmi_perform_query(HPWMI_THERMAL_PROFILE_QUERY, HPWMI_WRITE, &thermal_profile, - sizeof(thermal_profile), 0); -} - -static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) -{ - int tp; - - tp = thermal_profile_get(); - if (tp < 0) - return tp; - - switch (tp) { - case HP_THERMAL_PROFILE_PERFORMANCE: - *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - case HP_THERMAL_PROFILE_DEFAULT: - *profile = PLATFORM_PROFILE_BALANCED; - break; - case HP_THERMAL_PROFILE_COOL: - *profile = PLATFORM_PROFILE_COOL; - break; - case HP_THERMAL_PROFILE_QUIET: - *profile = PLATFORM_PROFILE_QUIET; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) -{ - int err, tp; - - switch (profile) { - case PLATFORM_PROFILE_PERFORMANCE: - tp = HP_THERMAL_PROFILE_PERFORMANCE; - break; - case PLATFORM_PROFILE_BALANCED: - tp = HP_THERMAL_PROFILE_DEFAULT; - break; - case PLATFORM_PROFILE_COOL: - tp = HP_THERMAL_PROFILE_COOL; - break; - case PLATFORM_PROFILE_QUIET: - tp = HP_THERMAL_PROFILE_QUIET; - break; - default: - return -EOPNOTSUPP; - } - - err = thermal_profile_set(tp); - if (err) - return err; - - return 0; -} - -static int thermal_profile_setup(void) -{ - int err, tp; - - if (is_omen_thermal_profile()) { - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; - - /* - * call thermal profile write command to ensure that the - * firmware correctly sets the OEM variables - */ - - err = omen_thermal_profile_set(tp); - if (err < 0) - return err; - - platform_profile_handler.profile_get = platform_profile_omen_get; - platform_profile_handler.profile_set = platform_profile_omen_set; - } else { - tp = thermal_profile_get(); - - if (tp < 0) - return tp; - - /* - * call thermal profile write command to ensure that the - * firmware correctly sets the OEM variables for the DPTF - */ - err = thermal_profile_set(tp); - if (err) - return err; - - platform_profile_handler.profile_get = hp_wmi_platform_profile_get; - platform_profile_handler.profile_set = hp_wmi_platform_profile_set; - - set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); - } - - set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); - - err = platform_profile_register(&platform_profile_handler); - if (err) - return err; - - platform_profile_support = true; - - return 0; -} - -static int hp_wmi_hwmon_init(void); - -static int __init hp_wmi_bios_setup(struct platform_device *device) -{ - int err; - /* clear detected rfkill devices */ - wifi_rfkill = NULL; - bluetooth_rfkill = NULL; - wwan_rfkill = NULL; - rfkill2_count = 0; - - /* - * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that - * BIOS no longer controls the power for the wireless - * devices. All features supported by this command will no - * longer be supported. - */ - if (!hp_wmi_bios_2009_later()) { - if (hp_wmi_rfkill_setup(device)) - hp_wmi_rfkill2_setup(device); - } - - err = hp_wmi_hwmon_init(); - - if (err < 0) - return err; - - thermal_profile_setup(); - - return 0; -} - -static int __exit hp_wmi_bios_remove(struct platform_device *device) -{ - int i; - - for (i = 0; i < rfkill2_count; i++) { - rfkill_unregister(rfkill2[i].rfkill); - rfkill_destroy(rfkill2[i].rfkill); - } - - if (wifi_rfkill) { - rfkill_unregister(wifi_rfkill); - rfkill_destroy(wifi_rfkill); - } - if (bluetooth_rfkill) { - rfkill_unregister(bluetooth_rfkill); - rfkill_destroy(bluetooth_rfkill); - } - if (wwan_rfkill) { - rfkill_unregister(wwan_rfkill); - rfkill_destroy(wwan_rfkill); - } - - if (platform_profile_support) - platform_profile_remove(); - - return 0; -} - -static int hp_wmi_resume_handler(struct device *device) -{ - /* - * Hardware state may have changed while suspended, so trigger - * input events for the current state. As this is a switch, - * the input layer will only actually pass it on if the state - * changed. - */ - if (hp_wmi_input_dev) { - if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_DOCK, - hp_wmi_get_dock_state()); - if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_get_tablet_mode()); - input_sync(hp_wmi_input_dev); - } - - if (rfkill2_count) - hp_wmi_rfkill2_refresh(); - - if (wifi_rfkill) - rfkill_set_states(wifi_rfkill, - hp_wmi_get_sw_state(HPWMI_WIFI), - hp_wmi_get_hw_state(HPWMI_WIFI)); - if (bluetooth_rfkill) - rfkill_set_states(bluetooth_rfkill, - hp_wmi_get_sw_state(HPWMI_BLUETOOTH), - hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); - if (wwan_rfkill) - rfkill_set_states(wwan_rfkill, - hp_wmi_get_sw_state(HPWMI_WWAN), - hp_wmi_get_hw_state(HPWMI_WWAN)); - - return 0; -} - -static const struct dev_pm_ops hp_wmi_pm_ops = { - .resume = hp_wmi_resume_handler, - .restore = hp_wmi_resume_handler, -}; - -static struct platform_driver hp_wmi_driver = { - .driver = { - .name = "hp-wmi", - .pm = &hp_wmi_pm_ops, - .dev_groups = hp_wmi_groups, - }, - .remove = __exit_p(hp_wmi_bios_remove), -}; - -static umode_t hp_wmi_hwmon_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - switch (type) { - case hwmon_pwm: - return 0644; - case hwmon_fan: - if (hp_wmi_get_fan_speed(channel) >= 0) - return 0444; - break; - default: - return 0; - } - - return 0; -} - -static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - int ret; - - switch (type) { - case hwmon_fan: - ret = hp_wmi_get_fan_speed(channel); - - if (ret < 0) - return ret; - *val = ret; - return 0; - case hwmon_pwm: - switch (hp_wmi_fan_speed_max_get()) { - case 0: - /* 0 is automatic fan, which is 2 for hwmon */ - *val = 2; - return 0; - case 1: - /* 1 is max fan, which is 0 - * (no fan speed control) for hwmon - */ - *val = 0; - return 0; - default: - /* shouldn't happen */ - return -ENODATA; - } - default: - return -EINVAL; - } -} - -static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long val) -{ - switch (type) { - case hwmon_pwm: - switch (val) { - case 0: - /* 0 is no fan speed control (max), which is 1 for us */ - return hp_wmi_fan_speed_max_set(1); - case 2: - /* 2 is automatic speed control, which is 0 for us */ - return hp_wmi_fan_speed_max_set(0); - default: - /* we don't support manual fan speed control */ - return -EINVAL; - } - default: - return -EOPNOTSUPP; - } -} - -static const struct hwmon_channel_info *info[] = { - HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), - HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), - NULL -}; - -static const struct hwmon_ops ops = { - .is_visible = hp_wmi_hwmon_is_visible, - .read = hp_wmi_hwmon_read, - .write = hp_wmi_hwmon_write, -}; - -static const struct hwmon_chip_info chip_info = { - .ops = &ops, - .info = info, -}; - -static int hp_wmi_hwmon_init(void) -{ - struct device *dev = &hp_wmi_platform_dev->dev; - struct device *hwmon; - - hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver, - &chip_info, NULL); - - if (IS_ERR(hwmon)) { - dev_err(dev, "Could not register hp hwmon device\n"); - return PTR_ERR(hwmon); - } - - return 0; -} - -static int __init hp_wmi_init(void) -{ - int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); - int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); - int err, tmp = 0; - - if (!bios_capable && !event_capable) - return -ENODEV; - - if (hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &tmp, - sizeof(tmp), sizeof(tmp)) == HPWMI_RET_INVALID_PARAMETERS) - zero_insize_support = true; - - if (event_capable) { - err = hp_wmi_input_setup(); - if (err) - return err; - } - - if (bios_capable) { - hp_wmi_platform_dev = - platform_device_register_simple("hp-wmi", PLATFORM_DEVID_NONE, NULL, 0); - if (IS_ERR(hp_wmi_platform_dev)) { - err = PTR_ERR(hp_wmi_platform_dev); - goto err_destroy_input; - } - - err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); - if (err) - goto err_unregister_device; - } - - return 0; - -err_unregister_device: - platform_device_unregister(hp_wmi_platform_dev); -err_destroy_input: - if (event_capable) - hp_wmi_input_destroy(); - - return err; -} -module_init(hp_wmi_init); - -static void __exit hp_wmi_exit(void) -{ - if (wmi_has_guid(HPWMI_EVENT_GUID)) - hp_wmi_input_destroy(); - - if (hp_wmi_platform_dev) { - platform_device_unregister(hp_wmi_platform_dev); - platform_driver_unregister(&hp_wmi_driver); - } -} -module_exit(hp_wmi_exit); diff --git a/drivers/platform/x86/hp/Kconfig b/drivers/platform/x86/hp/Kconfig new file mode 100644 index 000000000000..ae165955311c --- /dev/null +++ b/drivers/platform/x86/hp/Kconfig @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# X86 Platform Specific Drivers +# +menuconfig X86_PLATFORM_DRIVERS_HP + bool "HP X86 Platform Specific Device Drivers" + depends on X86_PLATFORM_DEVICES + help + Say Y here to get to see options for device drivers for various + HP x86 platforms, including vendor-specific laptop extension drivers. + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if X86_PLATFORM_DRIVERS_HP + +config HP_ACCEL + tristate "HP laptop accelerometer" + default m + depends on INPUT && ACPI + depends on SERIO_I8042 + select SENSORS_LIS3LV02D + select NEW_LEDS + select LEDS_CLASS + help + This driver provides support for the "Mobile Data Protection System 3D" + or "3D DriveGuard" feature of HP laptops. On such systems the driver + should load automatically (via ACPI alias). + + Support for a led indicating disk protection will be provided as + hp::hddprotect. For more information on the feature, refer to + Documentation/misc-devices/lis3lv02d.rst. + + To compile this driver as a module, choose M here: the module will + be called hp_accel. + +config HP_WMI + tristate "HP WMI extras" + default m + depends on ACPI_WMI + depends on INPUT + depends on RFKILL || RFKILL = n + select INPUT_SPARSEKMAP + select ACPI_PLATFORM_PROFILE + select HWMON + help + Say Y here if you want to support WMI-based hotkeys on HP laptops and + to read data from WMI such as docking or ambient light sensor state. + + To compile this driver as a module, choose M here: the module will + be called hp-wmi. + +config TC1100_WMI + tristate "HP Compaq TC1100 Tablet WMI Extras" + default m + depends on !X86_64 + depends on ACPI + depends on ACPI_WMI + help + This is a driver for the WMI extensions (wireless and bluetooth power + control) of the HP Compaq TC1100 tablet. + +endif # X86_PLATFORM_DRIVERS_HP diff --git a/drivers/platform/x86/hp/Makefile b/drivers/platform/x86/hp/Makefile new file mode 100644 index 000000000000..db1eed4cd7c7 --- /dev/null +++ b/drivers/platform/x86/hp/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for linux/drivers/platform/x86/hp +# HP x86 Platform-Specific Drivers +# + +# Hewlett Packard +obj-$(CONFIG_HP_ACCEL) += hp_accel.o +obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c new file mode 100644 index 000000000000..4a3851332ef2 --- /dev/null +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -0,0 +1,1570 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HP WMI hotkeys + * + * Copyright (C) 2008 Red Hat + * Copyright (C) 2010, 2011 Anssi Hannula + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Matthew Garrett "); +MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); +MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); + +#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" +#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" +#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 +#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required + +/* DMI board names of devices that should use the omen specific path for + * thermal profiles. + * This was obtained by taking a look in the windows omen command center + * app and parsing a json file that they use to figure out what capabilities + * the device should have. + * A device is considered an omen if the DisplayName in that list contains + * "OMEN", and it can use the thermal profile stuff if the "Feature" array + * contains "PerformanceControl". + */ +static const char * const omen_thermal_profile_boards[] = { + "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", + "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", + "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", + "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", + "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", + "8917", "8918", "8949", "894A", "89EB" +}; + +/* DMI Board names of Omen laptops that are specifically set to be thermal + * profile version 0 by the Omen Command Center app, regardless of what + * the get system design information WMI call returns + */ +static const char *const omen_thermal_profile_force_v0_boards[] = { + "8607", "8746", "8747", "8749", "874A", "8748" +}; + +enum hp_wmi_radio { + HPWMI_WIFI = 0x0, + HPWMI_BLUETOOTH = 0x1, + HPWMI_WWAN = 0x2, + HPWMI_GPS = 0x3, +}; + +enum hp_wmi_event_ids { + HPWMI_DOCK_EVENT = 0x01, + HPWMI_PARK_HDD = 0x02, + HPWMI_SMART_ADAPTER = 0x03, + HPWMI_BEZEL_BUTTON = 0x04, + HPWMI_WIRELESS = 0x05, + HPWMI_CPU_BATTERY_THROTTLE = 0x06, + HPWMI_LOCK_SWITCH = 0x07, + HPWMI_LID_SWITCH = 0x08, + HPWMI_SCREEN_ROTATION = 0x09, + HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, + HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, + HPWMI_PROXIMITY_SENSOR = 0x0C, + HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, + HPWMI_PEAKSHIFT_PERIOD = 0x0F, + HPWMI_BATTERY_CHARGE_PERIOD = 0x10, + HPWMI_SANITIZATION_MODE = 0x17, + HPWMI_OMEN_KEY = 0x1D, + HPWMI_SMART_EXPERIENCE_APP = 0x21, +}; + +/* + * struct bios_args buffer is dynamically allocated. New WMI command types + * were introduced that exceeds 128-byte data size. Changes to handle + * the data size allocation scheme were kept in hp_wmi_perform_qurey function. + */ +struct bios_args { + u32 signature; + u32 command; + u32 commandtype; + u32 datasize; + u8 data[]; +}; + +enum hp_wmi_commandtype { + HPWMI_DISPLAY_QUERY = 0x01, + HPWMI_HDDTEMP_QUERY = 0x02, + HPWMI_ALS_QUERY = 0x03, + HPWMI_HARDWARE_QUERY = 0x04, + HPWMI_WIRELESS_QUERY = 0x05, + HPWMI_BATTERY_QUERY = 0x07, + HPWMI_BIOS_QUERY = 0x09, + HPWMI_FEATURE_QUERY = 0x0b, + HPWMI_HOTKEY_QUERY = 0x0c, + HPWMI_FEATURE2_QUERY = 0x0d, + HPWMI_WIRELESS2_QUERY = 0x1b, + HPWMI_POSTCODEERROR_QUERY = 0x2a, + HPWMI_SYSTEM_DEVICE_MODE = 0x40, + HPWMI_THERMAL_PROFILE_QUERY = 0x4c, +}; + +enum hp_wmi_gm_commandtype { + HPWMI_FAN_SPEED_GET_QUERY = 0x11, + HPWMI_SET_PERFORMANCE_MODE = 0x1A, + HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, + HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, + HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28, +}; + +enum hp_wmi_command { + HPWMI_READ = 0x01, + HPWMI_WRITE = 0x02, + HPWMI_ODM = 0x03, + HPWMI_GM = 0x20008, +}; + +enum hp_wmi_hardware_mask { + HPWMI_DOCK_MASK = 0x01, + HPWMI_TABLET_MASK = 0x04, +}; + +struct bios_return { + u32 sigpass; + u32 return_code; +}; + +enum hp_return_value { + HPWMI_RET_WRONG_SIGNATURE = 0x02, + HPWMI_RET_UNKNOWN_COMMAND = 0x03, + HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, + HPWMI_RET_INVALID_PARAMETERS = 0x05, +}; + +enum hp_wireless2_bits { + HPWMI_POWER_STATE = 0x01, + HPWMI_POWER_SOFT = 0x02, + HPWMI_POWER_BIOS = 0x04, + HPWMI_POWER_HARD = 0x08, + HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, +}; + +enum hp_thermal_profile_omen_v0 { + HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00, + HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01, + HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02, +}; + +enum hp_thermal_profile_omen_v1 { + HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30, + HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31, + HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, +}; + +enum hp_thermal_profile { + HP_THERMAL_PROFILE_PERFORMANCE = 0x00, + HP_THERMAL_PROFILE_DEFAULT = 0x01, + HP_THERMAL_PROFILE_COOL = 0x02, + HP_THERMAL_PROFILE_QUIET = 0x03, +}; + +#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW) +#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) + +struct bios_rfkill2_device_state { + u8 radio_type; + u8 bus_type; + u16 vendor_id; + u16 product_id; + u16 subsys_vendor_id; + u16 subsys_product_id; + u8 rfkill_id; + u8 power; + u8 unknown[4]; +}; + +/* 7 devices fit into the 128 byte buffer */ +#define HPWMI_MAX_RFKILL2_DEVICES 7 + +struct bios_rfkill2_state { + u8 unknown[7]; + u8 count; + u8 pad[8]; + struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; +}; + +static const struct key_entry hp_wmi_keymap[] = { + { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x20e6, { KEY_PROG1 } }, + { KE_KEY, 0x20e8, { KEY_MEDIA } }, + { KE_KEY, 0x2142, { KEY_MEDIA } }, + { KE_KEY, 0x213b, { KEY_INFO } }, + { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, + { KE_KEY, 0x216a, { KEY_SETUP } }, + { KE_KEY, 0x21a5, { KEY_PROG2 } }, /* HP Omen Key */ + { KE_KEY, 0x21a7, { KEY_FN_ESC } }, + { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, + { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, + { KE_KEY, 0x231b, { KEY_HELP } }, + { KE_END, 0 } +}; + +static struct input_dev *hp_wmi_input_dev; +static struct platform_device *hp_wmi_platform_dev; +static struct platform_profile_handler platform_profile_handler; +static bool platform_profile_support; +static bool zero_insize_support; + +static struct rfkill *wifi_rfkill; +static struct rfkill *bluetooth_rfkill; +static struct rfkill *wwan_rfkill; + +struct rfkill2_device { + u8 id; + int num; + struct rfkill *rfkill; +}; + +static int rfkill2_count; +static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; + +/* + * Chassis Types values were obtained from SMBIOS reference + * specification version 3.00. A complete list of system enclosures + * and chassis types is available on Table 17. + */ +static const char * const tablet_chassis_types[] = { + "30", /* Tablet*/ + "31", /* Convertible */ + "32" /* Detachable */ +}; + +#define DEVICE_MODE_TABLET 0x06 + +/* map output size to the corresponding WMI method id */ +static inline int encode_outsize_for_pvsz(int outsize) +{ + if (outsize > 4096) + return -EINVAL; + if (outsize > 1024) + return 5; + if (outsize > 128) + return 4; + if (outsize > 4) + return 3; + if (outsize > 0) + return 2; + return 1; +} + +/* + * hp_wmi_perform_query + * + * query: The commandtype (enum hp_wmi_commandtype) + * write: The command (enum hp_wmi_command) + * buffer: Buffer used as input and/or output + * insize: Size of input buffer + * outsize: Size of output buffer + * + * returns zero on success + * an HP WMI query specific error code (which is positive) + * -EINVAL if the query was not successful at all + * -EINVAL if the output buffer size exceeds buffersize + * + * Note: The buffersize must at least be the maximum of the input and output + * size. E.g. Battery info query is defined to have 1 byte input + * and 128 byte output. The caller would do: + * buffer = kzalloc(128, GFP_KERNEL); + * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) + */ +static int hp_wmi_perform_query(int query, enum hp_wmi_command command, + void *buffer, int insize, int outsize) +{ + struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct bios_return *bios_return; + union acpi_object *obj = NULL; + struct bios_args *args = NULL; + int mid, actual_insize, actual_outsize; + size_t bios_args_size; + int ret; + + mid = encode_outsize_for_pvsz(outsize); + if (WARN_ON(mid < 0)) + return mid; + + actual_insize = max(insize, 128); + bios_args_size = struct_size(args, data, actual_insize); + args = kmalloc(bios_args_size, GFP_KERNEL); + if (!args) + return -ENOMEM; + + input.length = bios_args_size; + input.pointer = args; + + args->signature = 0x55434553; + args->command = command; + args->commandtype = query; + args->datasize = insize; + memcpy(args->data, buffer, flex_array_size(args, data, insize)); + + ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); + if (ret) + goto out_free; + + obj = output.pointer; + if (!obj) { + ret = -EINVAL; + goto out_free; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret); + ret = -EINVAL; + goto out_free; + } + + bios_return = (struct bios_return *)obj->buffer.pointer; + ret = bios_return->return_code; + + if (ret) { + if (ret != HPWMI_RET_UNKNOWN_COMMAND && + ret != HPWMI_RET_UNKNOWN_CMDTYPE) + pr_warn("query 0x%x returned error 0x%x\n", query, ret); + goto out_free; + } + + /* Ignore output data of zero size */ + if (!outsize) + goto out_free; + + actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); + memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); + memset(buffer + actual_outsize, 0, outsize - actual_outsize); + +out_free: + kfree(obj); + kfree(args); + return ret; +} + +static int hp_wmi_get_fan_speed(int fan) +{ + u8 fsh, fsl; + char fan_data[4] = { fan, 0, 0, 0 }; + + int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM, + &fan_data, sizeof(char), + sizeof(fan_data)); + + if (ret != 0) + return -EINVAL; + + fsh = fan_data[2]; + fsl = fan_data[3]; + + return (fsh << 8) | fsl; +} + +static int hp_wmi_read_int(int query) +{ + int val = 0, ret; + + ret = hp_wmi_perform_query(query, HPWMI_READ, &val, + zero_if_sup(val), sizeof(val)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return val; +} + +static int hp_wmi_get_dock_state(void) +{ + int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY); + + if (state < 0) + return state; + + return !!(state & HPWMI_DOCK_MASK); +} + +static int hp_wmi_get_tablet_mode(void) +{ + char system_device_mode[4] = { 0 }; + const char *chassis_type; + bool tablet_found; + int ret; + + chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); + if (!chassis_type) + return -ENODEV; + + tablet_found = match_string(tablet_chassis_types, + ARRAY_SIZE(tablet_chassis_types), + chassis_type) >= 0; + if (!tablet_found) + return -ENODEV; + + ret = hp_wmi_perform_query(HPWMI_SYSTEM_DEVICE_MODE, HPWMI_READ, + system_device_mode, zero_if_sup(system_device_mode), + sizeof(system_device_mode)); + if (ret < 0) + return ret; + + return system_device_mode[0] == DEVICE_MODE_TABLET; +} + +static int omen_thermal_profile_set(int mode) +{ + char buffer[2] = {0, mode}; + int ret; + + ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, + &buffer, sizeof(buffer), 0); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return mode; +} + +static bool is_omen_thermal_profile(void) +{ + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (!board_name) + return false; + + return match_string(omen_thermal_profile_boards, + ARRAY_SIZE(omen_thermal_profile_boards), + board_name) >= 0; +} + +static int omen_get_thermal_policy_version(void) +{ + unsigned char buffer[8] = { 0 }; + int ret; + + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (board_name) { + int matches = match_string(omen_thermal_profile_force_v0_boards, + ARRAY_SIZE(omen_thermal_profile_force_v0_boards), + board_name); + if (matches >= 0) + return 0; + } + + ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM, + &buffer, sizeof(buffer), sizeof(buffer)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return buffer[3]; +} + +static int omen_thermal_profile_get(void) +{ + u8 data; + + int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data); + + if (ret) + return ret; + + return data; +} + +static int hp_wmi_fan_speed_max_set(int enabled) +{ + int ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM, + &enabled, sizeof(enabled), 0); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return enabled; +} + +static int hp_wmi_fan_speed_max_get(void) +{ + int val = 0, ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM, + &val, zero_if_sup(val), sizeof(val)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return val; +} + +static int __init hp_wmi_bios_2008_later(void) +{ + int state = 0; + int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (!ret) + return 1; + + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; +} + +static int __init hp_wmi_bios_2009_later(void) +{ + u8 state[128]; + int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (!ret) + return 1; + + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; +} + +static int __init hp_wmi_enable_hotkeys(void) +{ + int value = 0x6e; + int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, + sizeof(value), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static int hp_wmi_set_block(void *data, bool blocked) +{ + enum hp_wmi_radio r = (enum hp_wmi_radio) data; + int query = BIT(r + 8) | ((!blocked) << r); + int ret; + + ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, + &query, sizeof(query), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static const struct rfkill_ops hp_wmi_rfkill_ops = { + .set_block = hp_wmi_set_block, +}; + +static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) +{ + int mask = 0x200 << (r * 8); + + int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + + /* TBD: Pass error */ + WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); + + return !(wireless & mask); +} + +static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) +{ + int mask = 0x800 << (r * 8); + + int wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + + /* TBD: Pass error */ + WARN_ONCE(wireless < 0, "error executing HPWMI_WIRELESS_QUERY"); + + return !(wireless & mask); +} + +static int hp_wmi_rfkill2_set_block(void *data, bool blocked) +{ + int rfkill_id = (int)(long)data; + char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; + int ret; + + ret = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_WRITE, + buffer, sizeof(buffer), 0); + + return ret <= 0 ? ret : -EINVAL; +} + +static const struct rfkill_ops hp_wmi_rfkill2_ops = { + .set_block = hp_wmi_rfkill2_set_block, +}; + +static int hp_wmi_rfkill2_refresh(void) +{ + struct bios_rfkill2_state state; + int err, i; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (err) + return err; + + for (i = 0; i < rfkill2_count; i++) { + int num = rfkill2[i].num; + struct bios_rfkill2_device_state *devstate; + + devstate = &state.device[num]; + + if (num >= state.count || + devstate->rfkill_id != rfkill2[i].id) { + pr_warn("power configuration of the wireless devices unexpectedly changed\n"); + continue; + } + + rfkill_set_states(rfkill2[i].rfkill, + IS_SWBLOCKED(devstate->power), + IS_HWBLOCKED(devstate->power)); + } + + return 0; +} + +static ssize_t display_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_DISPLAY_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_HDDTEMP_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t als_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_read_int(HPWMI_ALS_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t dock_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_get_dock_state(); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t tablet_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_get_tablet_mode(); + + if (value < 0) + return value; + return sprintf(buf, "%d\n", value); +} + +static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + /* Get the POST error code of previous boot failure. */ + int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY); + + if (value < 0) + return value; + return sprintf(buf, "0x%x\n", value); +} + +static ssize_t als_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 tmp; + int ret; + + ret = kstrtou32(buf, 10, &tmp); + if (ret) + return ret; + + ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp, + sizeof(tmp), 0); + if (ret) + return ret < 0 ? ret : -EINVAL; + + return count; +} + +static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 tmp = 1; + bool clear; + int ret; + + ret = kstrtobool(buf, &clear); + if (ret) + return ret; + + if (clear == false) + return -EINVAL; + + /* Clear the POST error code. It is kept until cleared. */ + ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, + sizeof(tmp), 0); + if (ret) + return ret < 0 ? ret : -EINVAL; + + return count; +} + +static DEVICE_ATTR_RO(display); +static DEVICE_ATTR_RO(hddtemp); +static DEVICE_ATTR_RW(als); +static DEVICE_ATTR_RO(dock); +static DEVICE_ATTR_RO(tablet); +static DEVICE_ATTR_RW(postcode); + +static struct attribute *hp_wmi_attrs[] = { + &dev_attr_display.attr, + &dev_attr_hddtemp.attr, + &dev_attr_als.attr, + &dev_attr_dock.attr, + &dev_attr_tablet.attr, + &dev_attr_postcode.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hp_wmi); + +static void hp_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 event_id, event_data; + union acpi_object *obj; + acpi_status status; + u32 *location; + int key_code; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + pr_info("bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + + if (!obj) + return; + if (obj->type != ACPI_TYPE_BUFFER) { + pr_info("Unknown response received %d\n", obj->type); + kfree(obj); + return; + } + + /* + * Depending on ACPI version the concatenation of id and event data + * inside _WED function will result in a 8 or 16 byte buffer. + */ + location = (u32 *)obj->buffer.pointer; + if (obj->buffer.length == 8) { + event_id = *location; + event_data = *(location + 1); + } else if (obj->buffer.length == 16) { + event_id = *location; + event_data = *(location + 2); + } else { + pr_info("Unknown buffer length %d\n", obj->buffer.length); + kfree(obj); + return; + } + kfree(obj); + + switch (event_id) { + case HPWMI_DOCK_EVENT: + if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_get_dock_state()); + if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_get_tablet_mode()); + input_sync(hp_wmi_input_dev); + break; + case HPWMI_PARK_HDD: + break; + case HPWMI_SMART_ADAPTER: + break; + case HPWMI_BEZEL_BUTTON: + case HPWMI_OMEN_KEY: + key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); + if (key_code < 0) + break; + + if (!sparse_keymap_report_event(hp_wmi_input_dev, + key_code, 1, true)) + pr_info("Unknown key code - 0x%x\n", key_code); + break; + case HPWMI_WIRELESS: + if (rfkill2_count) { + hp_wmi_rfkill2_refresh(); + break; + } + + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); + break; + case HPWMI_CPU_BATTERY_THROTTLE: + pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); + break; + case HPWMI_LOCK_SWITCH: + break; + case HPWMI_LID_SWITCH: + break; + case HPWMI_SCREEN_ROTATION: + break; + case HPWMI_COOLSENSE_SYSTEM_MOBILE: + break; + case HPWMI_COOLSENSE_SYSTEM_HOT: + break; + case HPWMI_PROXIMITY_SENSOR: + break; + case HPWMI_BACKLIT_KB_BRIGHTNESS: + break; + case HPWMI_PEAKSHIFT_PERIOD: + break; + case HPWMI_BATTERY_CHARGE_PERIOD: + break; + case HPWMI_SANITIZATION_MODE: + break; + case HPWMI_SMART_EXPERIENCE_APP: + break; + default: + pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); + break; + } +} + +static int __init hp_wmi_input_setup(void) +{ + acpi_status status; + int err, val; + + hp_wmi_input_dev = input_allocate_device(); + if (!hp_wmi_input_dev) + return -ENOMEM; + + hp_wmi_input_dev->name = "HP WMI hotkeys"; + hp_wmi_input_dev->phys = "wmi/input0"; + hp_wmi_input_dev->id.bustype = BUS_HOST; + + __set_bit(EV_SW, hp_wmi_input_dev->evbit); + + /* Dock */ + val = hp_wmi_get_dock_state(); + if (!(val < 0)) { + __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); + input_report_switch(hp_wmi_input_dev, SW_DOCK, val); + } + + /* Tablet mode */ + val = hp_wmi_get_tablet_mode(); + if (!(val < 0)) { + __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val); + } + + err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + /* Set initial hardware state */ + input_sync(hp_wmi_input_dev); + + if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) + hp_wmi_enable_hotkeys(); + + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_dev; + } + + err = input_register_device(hp_wmi_input_dev); + if (err) + goto err_uninstall_notifier; + + return 0; + + err_uninstall_notifier: + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + err_free_dev: + input_free_device(hp_wmi_input_dev); + return err; +} + +static void hp_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + input_unregister_device(hp_wmi_input_dev); +} + +static int __init hp_wmi_rfkill_setup(struct platform_device *device) +{ + int err, wireless; + + wireless = hp_wmi_read_int(HPWMI_WIRELESS_QUERY); + if (wireless < 0) + return wireless; + + err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE, &wireless, + sizeof(wireless), 0); + if (err) + return err; + + if (wireless & 0x1) { + wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, + RFKILL_TYPE_WLAN, + &hp_wmi_rfkill_ops, + (void *) HPWMI_WIFI); + if (!wifi_rfkill) + return -ENOMEM; + rfkill_init_sw_state(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI)); + rfkill_set_hw_state(wifi_rfkill, + hp_wmi_get_hw_state(HPWMI_WIFI)); + err = rfkill_register(wifi_rfkill); + if (err) + goto register_wifi_error; + } + + if (wireless & 0x2) { + bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, + RFKILL_TYPE_BLUETOOTH, + &hp_wmi_rfkill_ops, + (void *) HPWMI_BLUETOOTH); + if (!bluetooth_rfkill) { + err = -ENOMEM; + goto register_bluetooth_error; + } + rfkill_init_sw_state(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); + rfkill_set_hw_state(bluetooth_rfkill, + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + err = rfkill_register(bluetooth_rfkill); + if (err) + goto register_bluetooth_error; + } + + if (wireless & 0x4) { + wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, + RFKILL_TYPE_WWAN, + &hp_wmi_rfkill_ops, + (void *) HPWMI_WWAN); + if (!wwan_rfkill) { + err = -ENOMEM; + goto register_wwan_error; + } + rfkill_init_sw_state(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN)); + rfkill_set_hw_state(wwan_rfkill, + hp_wmi_get_hw_state(HPWMI_WWAN)); + err = rfkill_register(wwan_rfkill); + if (err) + goto register_wwan_error; + } + + return 0; + +register_wwan_error: + rfkill_destroy(wwan_rfkill); + wwan_rfkill = NULL; + if (bluetooth_rfkill) + rfkill_unregister(bluetooth_rfkill); +register_bluetooth_error: + rfkill_destroy(bluetooth_rfkill); + bluetooth_rfkill = NULL; + if (wifi_rfkill) + rfkill_unregister(wifi_rfkill); +register_wifi_error: + rfkill_destroy(wifi_rfkill); + wifi_rfkill = NULL; + return err; +} + +static int __init hp_wmi_rfkill2_setup(struct platform_device *device) +{ + struct bios_rfkill2_state state; + int err, i; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, + zero_if_sup(state), sizeof(state)); + if (err) + return err < 0 ? err : -EINVAL; + + if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { + pr_warn("unable to parse 0x1b query output\n"); + return -EINVAL; + } + + for (i = 0; i < state.count; i++) { + struct rfkill *rfkill; + enum rfkill_type type; + char *name; + + switch (state.device[i].radio_type) { + case HPWMI_WIFI: + type = RFKILL_TYPE_WLAN; + name = "hp-wifi"; + break; + case HPWMI_BLUETOOTH: + type = RFKILL_TYPE_BLUETOOTH; + name = "hp-bluetooth"; + break; + case HPWMI_WWAN: + type = RFKILL_TYPE_WWAN; + name = "hp-wwan"; + break; + case HPWMI_GPS: + type = RFKILL_TYPE_GPS; + name = "hp-gps"; + break; + default: + pr_warn("unknown device type 0x%x\n", + state.device[i].radio_type); + continue; + } + + if (!state.device[i].vendor_id) { + pr_warn("zero device %d while %d reported\n", + i, state.count); + continue; + } + + rfkill = rfkill_alloc(name, &device->dev, type, + &hp_wmi_rfkill2_ops, (void *)(long)i); + if (!rfkill) { + err = -ENOMEM; + goto fail; + } + + rfkill2[rfkill2_count].id = state.device[i].rfkill_id; + rfkill2[rfkill2_count].num = i; + rfkill2[rfkill2_count].rfkill = rfkill; + + rfkill_init_sw_state(rfkill, + IS_SWBLOCKED(state.device[i].power)); + rfkill_set_hw_state(rfkill, + IS_HWBLOCKED(state.device[i].power)); + + if (!(state.device[i].power & HPWMI_POWER_BIOS)) + pr_info("device %s blocked by BIOS\n", name); + + err = rfkill_register(rfkill); + if (err) { + rfkill_destroy(rfkill); + goto fail; + } + + rfkill2_count++; + } + + return 0; +fail: + for (; rfkill2_count > 0; rfkill2_count--) { + rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); + rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); + } + return err; +} + +static int platform_profile_omen_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int tp; + + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; + + switch (tp) { + case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE: + case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT: + case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case HP_OMEN_V0_THERMAL_PROFILE_COOL: + case HP_OMEN_V1_THERMAL_PROFILE_COOL: + *profile = PLATFORM_PROFILE_COOL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int platform_profile_omen_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err, tp, tp_version; + + tp_version = omen_get_thermal_policy_version(); + + if (tp_version < 0 || tp_version > 1) + return -EOPNOTSUPP; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_DEFAULT; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT; + break; + case PLATFORM_PROFILE_COOL: + if (tp_version == 0) + tp = HP_OMEN_V0_THERMAL_PROFILE_COOL; + else + tp = HP_OMEN_V1_THERMAL_PROFILE_COOL; + break; + default: + return -EOPNOTSUPP; + } + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; + + return 0; +} + +static int thermal_profile_get(void) +{ + return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); +} + +static int thermal_profile_set(int thermal_profile) +{ + return hp_wmi_perform_query(HPWMI_THERMAL_PROFILE_QUERY, HPWMI_WRITE, &thermal_profile, + sizeof(thermal_profile), 0); +} + +static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int tp; + + tp = thermal_profile_get(); + if (tp < 0) + return tp; + + switch (tp) { + case HP_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case HP_THERMAL_PROFILE_DEFAULT: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case HP_THERMAL_PROFILE_COOL: + *profile = PLATFORM_PROFILE_COOL; + break; + case HP_THERMAL_PROFILE_QUIET: + *profile = PLATFORM_PROFILE_QUIET; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err, tp; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + tp = HP_THERMAL_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + tp = HP_THERMAL_PROFILE_DEFAULT; + break; + case PLATFORM_PROFILE_COOL: + tp = HP_THERMAL_PROFILE_COOL; + break; + case PLATFORM_PROFILE_QUIET: + tp = HP_THERMAL_PROFILE_QUIET; + break; + default: + return -EOPNOTSUPP; + } + + err = thermal_profile_set(tp); + if (err) + return err; + + return 0; +} + +static int thermal_profile_setup(void) +{ + int err, tp; + + if (is_omen_thermal_profile()) { + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; + + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables + */ + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; + + platform_profile_handler.profile_get = platform_profile_omen_get; + platform_profile_handler.profile_set = platform_profile_omen_set; + } else { + tp = thermal_profile_get(); + + if (tp < 0) + return tp; + + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables for the DPTF + */ + err = thermal_profile_set(tp); + if (err) + return err; + + platform_profile_handler.profile_get = hp_wmi_platform_profile_get; + platform_profile_handler.profile_set = hp_wmi_platform_profile_set; + + set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices); + } + + set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices); + + err = platform_profile_register(&platform_profile_handler); + if (err) + return err; + + platform_profile_support = true; + + return 0; +} + +static int hp_wmi_hwmon_init(void); + +static int __init hp_wmi_bios_setup(struct platform_device *device) +{ + int err; + /* clear detected rfkill devices */ + wifi_rfkill = NULL; + bluetooth_rfkill = NULL; + wwan_rfkill = NULL; + rfkill2_count = 0; + + /* + * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that + * BIOS no longer controls the power for the wireless + * devices. All features supported by this command will no + * longer be supported. + */ + if (!hp_wmi_bios_2009_later()) { + if (hp_wmi_rfkill_setup(device)) + hp_wmi_rfkill2_setup(device); + } + + err = hp_wmi_hwmon_init(); + + if (err < 0) + return err; + + thermal_profile_setup(); + + return 0; +} + +static int __exit hp_wmi_bios_remove(struct platform_device *device) +{ + int i; + + for (i = 0; i < rfkill2_count; i++) { + rfkill_unregister(rfkill2[i].rfkill); + rfkill_destroy(rfkill2[i].rfkill); + } + + if (wifi_rfkill) { + rfkill_unregister(wifi_rfkill); + rfkill_destroy(wifi_rfkill); + } + if (bluetooth_rfkill) { + rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); + } + if (wwan_rfkill) { + rfkill_unregister(wwan_rfkill); + rfkill_destroy(wwan_rfkill); + } + + if (platform_profile_support) + platform_profile_remove(); + + return 0; +} + +static int hp_wmi_resume_handler(struct device *device) +{ + /* + * Hardware state may have changed while suspended, so trigger + * input events for the current state. As this is a switch, + * the input layer will only actually pass it on if the state + * changed. + */ + if (hp_wmi_input_dev) { + if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_get_dock_state()); + if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit)) + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_get_tablet_mode()); + input_sync(hp_wmi_input_dev); + } + + if (rfkill2_count) + hp_wmi_rfkill2_refresh(); + + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); + + return 0; +} + +static const struct dev_pm_ops hp_wmi_pm_ops = { + .resume = hp_wmi_resume_handler, + .restore = hp_wmi_resume_handler, +}; + +static struct platform_driver hp_wmi_driver = { + .driver = { + .name = "hp-wmi", + .pm = &hp_wmi_pm_ops, + .dev_groups = hp_wmi_groups, + }, + .remove = __exit_p(hp_wmi_bios_remove), +}; + +static umode_t hp_wmi_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + return 0644; + case hwmon_fan: + if (hp_wmi_get_fan_speed(channel) >= 0) + return 0444; + break; + default: + return 0; + } + + return 0; +} + +static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + + switch (type) { + case hwmon_fan: + ret = hp_wmi_get_fan_speed(channel); + + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_pwm: + switch (hp_wmi_fan_speed_max_get()) { + case 0: + /* 0 is automatic fan, which is 2 for hwmon */ + *val = 2; + return 0; + case 1: + /* 1 is max fan, which is 0 + * (no fan speed control) for hwmon + */ + *val = 0; + return 0; + default: + /* shouldn't happen */ + return -ENODATA; + } + default: + return -EINVAL; + } +} + +static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + switch (val) { + case 0: + /* 0 is no fan speed control (max), which is 1 for us */ + return hp_wmi_fan_speed_max_set(1); + case 2: + /* 2 is automatic speed control, which is 0 for us */ + return hp_wmi_fan_speed_max_set(0); + default: + /* we don't support manual fan speed control */ + return -EINVAL; + } + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_ops ops = { + .is_visible = hp_wmi_hwmon_is_visible, + .read = hp_wmi_hwmon_read, + .write = hp_wmi_hwmon_write, +}; + +static const struct hwmon_chip_info chip_info = { + .ops = &ops, + .info = info, +}; + +static int hp_wmi_hwmon_init(void) +{ + struct device *dev = &hp_wmi_platform_dev->dev; + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver, + &chip_info, NULL); + + if (IS_ERR(hwmon)) { + dev_err(dev, "Could not register hp hwmon device\n"); + return PTR_ERR(hwmon); + } + + return 0; +} + +static int __init hp_wmi_init(void) +{ + int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); + int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); + int err, tmp = 0; + + if (!bios_capable && !event_capable) + return -ENODEV; + + if (hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_READ, &tmp, + sizeof(tmp), sizeof(tmp)) == HPWMI_RET_INVALID_PARAMETERS) + zero_insize_support = true; + + if (event_capable) { + err = hp_wmi_input_setup(); + if (err) + return err; + } + + if (bios_capable) { + hp_wmi_platform_dev = + platform_device_register_simple("hp-wmi", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(hp_wmi_platform_dev)) { + err = PTR_ERR(hp_wmi_platform_dev); + goto err_destroy_input; + } + + err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); + if (err) + goto err_unregister_device; + } + + return 0; + +err_unregister_device: + platform_device_unregister(hp_wmi_platform_dev); +err_destroy_input: + if (event_capable) + hp_wmi_input_destroy(); + + return err; +} +module_init(hp_wmi_init); + +static void __exit hp_wmi_exit(void) +{ + if (wmi_has_guid(HPWMI_EVENT_GUID)) + hp_wmi_input_destroy(); + + if (hp_wmi_platform_dev) { + platform_device_unregister(hp_wmi_platform_dev); + platform_driver_unregister(&hp_wmi_driver); + } +} +module_exit(hp_wmi_exit); diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c new file mode 100644 index 000000000000..6477591747cf --- /dev/null +++ b/drivers/platform/x86/hp/hp_accel.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008-2009 Pavel Machek + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../misc/lis3lv02d/lis3lv02d.h" + +/* Delayed LEDs infrastructure ------------------------------------ */ + +/* Special LED class that can defer work */ +struct delayed_led_classdev { + struct led_classdev led_classdev; + struct work_struct work; + enum led_brightness new_brightness; + + unsigned int led; /* For driver */ + void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); +}; + +static inline void delayed_set_status_worker(struct work_struct *work) +{ + struct delayed_led_classdev *data = + container_of(work, struct delayed_led_classdev, work); + + data->set_brightness(data, data->new_brightness); +} + +static inline void delayed_sysfs_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct delayed_led_classdev *data = container_of(led_cdev, + struct delayed_led_classdev, led_classdev); + data->new_brightness = brightness; + schedule_work(&data->work); +} + +/* HP-specific accelerometer driver ------------------------------------ */ + +/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id + * HPQ6000 sends through the keyboard bus */ +#define ACCEL_1 0x25 +#define ACCEL_2 0x26 +#define ACCEL_3 0x27 +#define ACCEL_4 0x28 + +/* For automatic insertion of the module */ +static const struct acpi_device_id lis3lv02d_device_ids[] = { + {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ + {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */ + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); + +/** + * lis3lv02d_acpi_init - initialize the device for ACPI + * @lis3: pointer to the device struct + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_init(struct lis3lv02d *lis3) +{ + return 0; +} + +/** + * lis3lv02d_acpi_read - ACPI ALRD method: read a register + * @lis3: pointer to the device struct + * @reg: the register to read + * @ret: result of the operation + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) +{ + struct acpi_device *dev = lis3->bus_priv; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long lret; + acpi_status status; + + arg0.integer.value = reg; + + status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); + if (ACPI_FAILURE(status)) + return -EINVAL; + *ret = lret; + return 0; +} + +/** + * lis3lv02d_acpi_write - ACPI ALWR method: write to a register + * @lis3: pointer to the device struct + * @reg: the register to write to + * @val: the value to write + * + * Returns 0 on success. + */ +static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) +{ + struct acpi_device *dev = lis3->bus_priv; + unsigned long long ret; /* Not used when writting */ + union acpi_object in_obj[2]; + struct acpi_object_list args = { 2, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = val; + + if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) + return -EINVAL; + + return 0; +} + +static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) +{ + lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); + pr_info("hardware type %s found\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). + * If the value is negative, the opposite of the hw value is used. */ +#define DEFINE_CONV(name, x, y, z) \ + static union axis_conversion lis3lv02d_axis_##name = \ + { .as_array = { x, y, z } } +DEFINE_CONV(normal, 1, 2, 3); +DEFINE_CONV(y_inverted, 1, -2, 3); +DEFINE_CONV(x_inverted, -1, 2, 3); +DEFINE_CONV(x_inverted_usd, -1, 2, -3); +DEFINE_CONV(z_inverted, 1, 2, -3); +DEFINE_CONV(xy_swap, 2, 1, 3); +DEFINE_CONV(xy_rotated_left, -2, 1, 3); +DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); +DEFINE_CONV(xy_swap_inverted, -2, -1, 3); +DEFINE_CONV(xy_rotated_right, 2, -1, 3); +DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} + +#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ + _class2, _name2, \ + _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_##_class1, _name1), \ + DMI_MATCH(DMI_##_class2, _name2), \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} +static const struct dmi_system_id lis3lv02d_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), + AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), + AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), + AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), + AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), + AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), + AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), + AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), + AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), + /* Intel-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_I", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3603", + x_inverted), + /* AMD-based HP Pavilion dv5 */ + AXIS_DMI_MATCH2("HPDV5_A", + PRODUCT_NAME, "HP Pavilion dv5", + BOARD_NAME, "3600", + y_inverted), + AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), + AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), + AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), + AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), + AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), + AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), + AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), + AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted), + AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), + AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), + AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), + AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), + AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), + AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), + AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), + AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), + AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted), + AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), + { NULL, } +/* Laptop models without axis info (yet): + * "NC6910" "HP Compaq 6910" + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + +static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) +{ + struct acpi_device *dev = lis3_dev.bus_priv; + unsigned long long ret; /* Not used when writing */ + union acpi_object in_obj[1]; + struct acpi_object_list args = { 1, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = !!value; + + acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); +} + +static struct delayed_led_classdev hpled_led = { + .led_classdev = { + .name = "hp::hddprotect", + .default_trigger = "none", + .brightness_set = delayed_sysfs_set, + .flags = LED_CORE_SUSPENDRESUME, + }, + .set_brightness = hpled_set, +}; + +static bool hp_accel_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended; + + if (str & I8042_STR_AUXDATA) + return false; + + if (data == 0xe0) { + extended = true; + return true; + } else if (unlikely(extended)) { + extended = false; + + switch (data) { + case ACCEL_1: + case ACCEL_2: + case ACCEL_3: + case ACCEL_4: + return true; + default: + serio_interrupt(port, 0xe0, 0); + return false; + } + } + + return false; +} + +static int lis3lv02d_probe(struct platform_device *device) +{ + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; + + /* obtain IRQ number of our device from ACPI */ + ret = platform_get_irq_optional(device, 0); + if (ret > 0) + lis3_dev.irq = ret; + + /* If possible use a "standard" axes order */ + if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { + pr_info("Using custom axes %d,%d,%d\n", + lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); + } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + pr_info("laptop model unknown, using default axes configuration\n"); + lis3_dev.ac = lis3lv02d_axis_normal; + } + + /* call the core layer do its init */ + ret = lis3lv02d_init_device(&lis3_dev); + if (ret) + return ret; + + /* filter to remove HPQ6000 accelerometer data + * from keyboard bus stream */ + if (strstr(dev_name(&device->dev), "HPQ6000")) + i8042_install_filter(hp_accel_i8042_filter); + + INIT_WORK(&hpled_led.work, delayed_set_status_worker); + ret = led_classdev_register(NULL, &hpled_led.led_classdev); + if (ret) { + i8042_remove_filter(hp_accel_i8042_filter); + lis3lv02d_joystick_disable(&lis3_dev); + lis3lv02d_poweroff(&lis3_dev); + flush_work(&hpled_led.work); + lis3lv02d_remove_fs(&lis3_dev); + return ret; + } + + return ret; +} + +static int lis3lv02d_remove(struct platform_device *device) +{ + i8042_remove_filter(hp_accel_i8042_filter); + lis3lv02d_joystick_disable(&lis3_dev); + lis3lv02d_poweroff(&lis3_dev); + + led_classdev_unregister(&hpled_led.led_classdev); + flush_work(&hpled_led.work); + + lis3lv02d_remove_fs(&lis3_dev); + return 0; +} + +static int __maybe_unused lis3lv02d_suspend(struct device *dev) +{ + /* make sure the device is off when we suspend */ + lis3lv02d_poweroff(&lis3_dev); + return 0; +} + +static int __maybe_unused lis3lv02d_resume(struct device *dev) +{ + lis3lv02d_poweron(&lis3_dev); + return 0; +} + +static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); + +/* For the HP MDPS aka 3D Driveguard */ +static struct platform_driver lis3lv02d_driver = { + .probe = lis3lv02d_probe, + .remove = lis3lv02d_remove, + .driver = { + .name = "hp_accel", + .pm = &hp_accel_pm, + .acpi_match_table = lis3lv02d_device_ids, + }, +}; +module_platform_driver(lis3lv02d_driver); + +MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/hp/tc1100-wmi.c b/drivers/platform/x86/hp/tc1100-wmi.c new file mode 100644 index 000000000000..ded26213c420 --- /dev/null +++ b/drivers/platform/x86/hp/tc1100-wmi.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HP Compaq TC1100 Tablet WMI Extras Driver + * + * Copyright (C) 2007 Carlos Corbacho + * Copyright (C) 2004 Jamey Hicks + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define GUID "C364AC71-36DB-495A-8494-B439D472A505" + +#define TC1100_INSTANCE_WIRELESS 1 +#define TC1100_INSTANCE_JOGDIAL 2 + +MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); +MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); + +static struct platform_device *tc1100_device; + +struct tc1100_data { + u32 wireless; + u32 jogdial; +}; + +#ifdef CONFIG_PM +static struct tc1100_data suspend_data; +#endif + +/* -------------------------------------------------------------------------- + Device Management + -------------------------------------------------------------------------- */ + +static int get_state(u32 *out, u8 instance) +{ + u32 tmp; + acpi_status status; + struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + if (!out) + return -EINVAL; + + if (instance > 2) + return -ENODEV; + + status = wmi_query_block(GUID, instance, &result); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = (union acpi_object *) result.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) { + tmp = obj->integer.value; + } else { + tmp = 0; + } + + if (result.length > 0) + kfree(result.pointer); + + switch (instance) { + case TC1100_INSTANCE_WIRELESS: + *out = (tmp == 3) ? 1 : 0; + return 0; + case TC1100_INSTANCE_JOGDIAL: + *out = (tmp == 1) ? 0 : 1; + return 0; + default: + return -ENODEV; + } +} + +static int set_state(u32 *in, u8 instance) +{ + u32 value; + acpi_status status; + struct acpi_buffer input; + + if (!in) + return -EINVAL; + + if (instance > 2) + return -ENODEV; + + switch (instance) { + case TC1100_INSTANCE_WIRELESS: + value = (*in) ? 1 : 2; + break; + case TC1100_INSTANCE_JOGDIAL: + value = (*in) ? 0 : 1; + break; + default: + return -ENODEV; + } + + input.length = sizeof(u32); + input.pointer = &value; + + status = wmi_set_block(GUID, instance, &input); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return 0; +} + +/* -------------------------------------------------------------------------- + FS Interface (/sys) + -------------------------------------------------------------------------- */ + +/* + * Read/ write bool sysfs macro + */ +#define show_set_bool(value, instance) \ +static ssize_t \ +show_bool_##value(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + u32 result; \ + acpi_status status = get_state(&result, instance); \ + if (ACPI_SUCCESS(status)) \ + return sprintf(buf, "%d\n", result); \ + return sprintf(buf, "Read error\n"); \ +} \ +\ +static ssize_t \ +set_bool_##value(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + u32 tmp = simple_strtoul(buf, NULL, 10); \ + acpi_status status = set_state(&tmp, instance); \ + if (ACPI_FAILURE(status)) \ + return -EINVAL; \ + return count; \ +} \ +static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ + show_bool_##value, set_bool_##value); + +show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); +show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); + +static struct attribute *tc1100_attributes[] = { + &dev_attr_wireless.attr, + &dev_attr_jogdial.attr, + NULL +}; + +static const struct attribute_group tc1100_attribute_group = { + .attrs = tc1100_attributes, +}; + +/* -------------------------------------------------------------------------- + Driver Model + -------------------------------------------------------------------------- */ + +static int __init tc1100_probe(struct platform_device *device) +{ + return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); +} + + +static int tc1100_remove(struct platform_device *device) +{ + sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); + + return 0; +} + +#ifdef CONFIG_PM +static int tc1100_suspend(struct device *dev) +{ + int ret; + + ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); + if (ret) + return ret; + + ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); + if (ret) + return ret; + + return 0; +} + +static int tc1100_resume(struct device *dev) +{ + int ret; + + ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); + if (ret) + return ret; + + ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); + if (ret) + return ret; + + return 0; +} + +static const struct dev_pm_ops tc1100_pm_ops = { + .suspend = tc1100_suspend, + .resume = tc1100_resume, + .freeze = tc1100_suspend, + .restore = tc1100_resume, +}; +#endif + +static struct platform_driver tc1100_driver = { + .driver = { + .name = "tc1100-wmi", +#ifdef CONFIG_PM + .pm = &tc1100_pm_ops, +#endif + }, + .remove = tc1100_remove, +}; + +static int __init tc1100_init(void) +{ + int error; + + if (!wmi_has_guid(GUID)) + return -ENODEV; + + tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE); + if (!tc1100_device) + return -ENOMEM; + + error = platform_device_add(tc1100_device); + if (error) + goto err_device_put; + + error = platform_driver_probe(&tc1100_driver, tc1100_probe); + if (error) + goto err_device_del; + + pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); + return 0; + + err_device_del: + platform_device_del(tc1100_device); + err_device_put: + platform_device_put(tc1100_device); + return error; +} + +static void __exit tc1100_exit(void) +{ + platform_device_unregister(tc1100_device); + platform_driver_unregister(&tc1100_driver); +} + +module_init(tc1100_init); +module_exit(tc1100_exit); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c deleted file mode 100644 index e9f852f7c27f..000000000000 --- a/drivers/platform/x86/hp_accel.c +++ /dev/null @@ -1,387 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008-2009 Pavel Machek - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../misc/lis3lv02d/lis3lv02d.h" - -/* Delayed LEDs infrastructure ------------------------------------ */ - -/* Special LED class that can defer work */ -struct delayed_led_classdev { - struct led_classdev led_classdev; - struct work_struct work; - enum led_brightness new_brightness; - - unsigned int led; /* For driver */ - void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); -}; - -static inline void delayed_set_status_worker(struct work_struct *work) -{ - struct delayed_led_classdev *data = - container_of(work, struct delayed_led_classdev, work); - - data->set_brightness(data, data->new_brightness); -} - -static inline void delayed_sysfs_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct delayed_led_classdev *data = container_of(led_cdev, - struct delayed_led_classdev, led_classdev); - data->new_brightness = brightness; - schedule_work(&data->work); -} - -/* HP-specific accelerometer driver ------------------------------------ */ - -/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id - * HPQ6000 sends through the keyboard bus */ -#define ACCEL_1 0x25 -#define ACCEL_2 0x26 -#define ACCEL_3 0x27 -#define ACCEL_4 0x28 - -/* For automatic insertion of the module */ -static const struct acpi_device_id lis3lv02d_device_ids[] = { - {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ - {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ - {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */ - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); - -/** - * lis3lv02d_acpi_init - initialize the device for ACPI - * @lis3: pointer to the device struct - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_init(struct lis3lv02d *lis3) -{ - return 0; -} - -/** - * lis3lv02d_acpi_read - ACPI ALRD method: read a register - * @lis3: pointer to the device struct - * @reg: the register to read - * @ret: result of the operation - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) -{ - struct acpi_device *dev = lis3->bus_priv; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - unsigned long long lret; - acpi_status status; - - arg0.integer.value = reg; - - status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); - if (ACPI_FAILURE(status)) - return -EINVAL; - *ret = lret; - return 0; -} - -/** - * lis3lv02d_acpi_write - ACPI ALWR method: write to a register - * @lis3: pointer to the device struct - * @reg: the register to write to - * @val: the value to write - * - * Returns 0 on success. - */ -static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) -{ - struct acpi_device *dev = lis3->bus_priv; - unsigned long long ret; /* Not used when writting */ - union acpi_object in_obj[2]; - struct acpi_object_list args = { 2, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = reg; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = val; - - if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) - return -EINVAL; - - return 0; -} - -static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) -{ - lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); - pr_info("hardware type %s found\n", dmi->ident); - - return 1; -} - -/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). - * If the value is negative, the opposite of the hw value is used. */ -#define DEFINE_CONV(name, x, y, z) \ - static union axis_conversion lis3lv02d_axis_##name = \ - { .as_array = { x, y, z } } -DEFINE_CONV(normal, 1, 2, 3); -DEFINE_CONV(y_inverted, 1, -2, 3); -DEFINE_CONV(x_inverted, -1, 2, 3); -DEFINE_CONV(x_inverted_usd, -1, 2, -3); -DEFINE_CONV(z_inverted, 1, 2, -3); -DEFINE_CONV(xy_swap, 2, 1, 3); -DEFINE_CONV(xy_rotated_left, -2, 1, 3); -DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); -DEFINE_CONV(xy_swap_inverted, -2, -1, 3); -DEFINE_CONV(xy_rotated_right, 2, -1, 3); -DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); - -#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_PRODUCT_NAME, _name) \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} - -#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ - _class2, _name2, \ - _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_##_class1, _name1), \ - DMI_MATCH(DMI_##_class2, _name2), \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} -static const struct dmi_system_id lis3lv02d_dmi_ids[] = { - /* product names are truncated to match all kinds of a same model */ - AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), - AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), - AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), - AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), - AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), - AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), - AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), - AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), - AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), - AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), - AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), - AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), - AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), - AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), - AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), - /* Intel-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_I", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3603", - x_inverted), - /* AMD-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_A", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3600", - y_inverted), - AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), - AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), - AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), - AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), - AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), - AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), - AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), - AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted), - AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), - AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), - AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), - AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), - AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), - AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), - AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), - AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), - AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), - AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), - AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted), - AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), - { NULL, } -/* Laptop models without axis info (yet): - * "NC6910" "HP Compaq 6910" - * "NC2400" "HP Compaq nc2400" - * "NX74x0" "HP Compaq nx74" - * "NX6325" "HP Compaq nx6325" - * "NC4400" "HP Compaq nc4400" - */ -}; - -static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) -{ - struct acpi_device *dev = lis3_dev.bus_priv; - unsigned long long ret; /* Not used when writing */ - union acpi_object in_obj[1]; - struct acpi_object_list args = { 1, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = !!value; - - acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); -} - -static struct delayed_led_classdev hpled_led = { - .led_classdev = { - .name = "hp::hddprotect", - .default_trigger = "none", - .brightness_set = delayed_sysfs_set, - .flags = LED_CORE_SUSPENDRESUME, - }, - .set_brightness = hpled_set, -}; - -static bool hp_accel_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) -{ - static bool extended; - - if (str & I8042_STR_AUXDATA) - return false; - - if (data == 0xe0) { - extended = true; - return true; - } else if (unlikely(extended)) { - extended = false; - - switch (data) { - case ACCEL_1: - case ACCEL_2: - case ACCEL_3: - case ACCEL_4: - return true; - default: - serio_interrupt(port, 0xe0, 0); - return false; - } - } - - return false; -} - -static int lis3lv02d_probe(struct platform_device *device) -{ - int ret; - - lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); - lis3_dev.init = lis3lv02d_acpi_init; - lis3_dev.read = lis3lv02d_acpi_read; - lis3_dev.write = lis3lv02d_acpi_write; - - /* obtain IRQ number of our device from ACPI */ - ret = platform_get_irq_optional(device, 0); - if (ret > 0) - lis3_dev.irq = ret; - - /* If possible use a "standard" axes order */ - if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { - pr_info("Using custom axes %d,%d,%d\n", - lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); - } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { - pr_info("laptop model unknown, using default axes configuration\n"); - lis3_dev.ac = lis3lv02d_axis_normal; - } - - /* call the core layer do its init */ - ret = lis3lv02d_init_device(&lis3_dev); - if (ret) - return ret; - - /* filter to remove HPQ6000 accelerometer data - * from keyboard bus stream */ - if (strstr(dev_name(&device->dev), "HPQ6000")) - i8042_install_filter(hp_accel_i8042_filter); - - INIT_WORK(&hpled_led.work, delayed_set_status_worker); - ret = led_classdev_register(NULL, &hpled_led.led_classdev); - if (ret) { - i8042_remove_filter(hp_accel_i8042_filter); - lis3lv02d_joystick_disable(&lis3_dev); - lis3lv02d_poweroff(&lis3_dev); - flush_work(&hpled_led.work); - lis3lv02d_remove_fs(&lis3_dev); - return ret; - } - - return ret; -} - -static int lis3lv02d_remove(struct platform_device *device) -{ - i8042_remove_filter(hp_accel_i8042_filter); - lis3lv02d_joystick_disable(&lis3_dev); - lis3lv02d_poweroff(&lis3_dev); - - led_classdev_unregister(&hpled_led.led_classdev); - flush_work(&hpled_led.work); - - lis3lv02d_remove_fs(&lis3_dev); - return 0; -} - -static int __maybe_unused lis3lv02d_suspend(struct device *dev) -{ - /* make sure the device is off when we suspend */ - lis3lv02d_poweroff(&lis3_dev); - return 0; -} - -static int __maybe_unused lis3lv02d_resume(struct device *dev) -{ - lis3lv02d_poweron(&lis3_dev); - return 0; -} - -static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); - -/* For the HP MDPS aka 3D Driveguard */ -static struct platform_driver lis3lv02d_driver = { - .probe = lis3lv02d_probe, - .remove = lis3lv02d_remove, - .driver = { - .name = "hp_accel", - .pm = &hp_accel_pm, - .acpi_match_table = lis3lv02d_device_ids, - }, -}; -module_platform_driver(lis3lv02d_driver); - -MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); -MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c deleted file mode 100644 index ded26213c420..000000000000 --- a/drivers/platform/x86/tc1100-wmi.c +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HP Compaq TC1100 Tablet WMI Extras Driver - * - * Copyright (C) 2007 Carlos Corbacho - * Copyright (C) 2004 Jamey Hicks - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#define GUID "C364AC71-36DB-495A-8494-B439D472A505" - -#define TC1100_INSTANCE_WIRELESS 1 -#define TC1100_INSTANCE_JOGDIAL 2 - -MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); -MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); - -static struct platform_device *tc1100_device; - -struct tc1100_data { - u32 wireless; - u32 jogdial; -}; - -#ifdef CONFIG_PM -static struct tc1100_data suspend_data; -#endif - -/* -------------------------------------------------------------------------- - Device Management - -------------------------------------------------------------------------- */ - -static int get_state(u32 *out, u8 instance) -{ - u32 tmp; - acpi_status status; - struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - - if (!out) - return -EINVAL; - - if (instance > 2) - return -ENODEV; - - status = wmi_query_block(GUID, instance, &result); - if (ACPI_FAILURE(status)) - return -ENODEV; - - obj = (union acpi_object *) result.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) { - tmp = obj->integer.value; - } else { - tmp = 0; - } - - if (result.length > 0) - kfree(result.pointer); - - switch (instance) { - case TC1100_INSTANCE_WIRELESS: - *out = (tmp == 3) ? 1 : 0; - return 0; - case TC1100_INSTANCE_JOGDIAL: - *out = (tmp == 1) ? 0 : 1; - return 0; - default: - return -ENODEV; - } -} - -static int set_state(u32 *in, u8 instance) -{ - u32 value; - acpi_status status; - struct acpi_buffer input; - - if (!in) - return -EINVAL; - - if (instance > 2) - return -ENODEV; - - switch (instance) { - case TC1100_INSTANCE_WIRELESS: - value = (*in) ? 1 : 2; - break; - case TC1100_INSTANCE_JOGDIAL: - value = (*in) ? 0 : 1; - break; - default: - return -ENODEV; - } - - input.length = sizeof(u32); - input.pointer = &value; - - status = wmi_set_block(GUID, instance, &input); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return 0; -} - -/* -------------------------------------------------------------------------- - FS Interface (/sys) - -------------------------------------------------------------------------- */ - -/* - * Read/ write bool sysfs macro - */ -#define show_set_bool(value, instance) \ -static ssize_t \ -show_bool_##value(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - u32 result; \ - acpi_status status = get_state(&result, instance); \ - if (ACPI_SUCCESS(status)) \ - return sprintf(buf, "%d\n", result); \ - return sprintf(buf, "Read error\n"); \ -} \ -\ -static ssize_t \ -set_bool_##value(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - u32 tmp = simple_strtoul(buf, NULL, 10); \ - acpi_status status = set_state(&tmp, instance); \ - if (ACPI_FAILURE(status)) \ - return -EINVAL; \ - return count; \ -} \ -static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ - show_bool_##value, set_bool_##value); - -show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); -show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); - -static struct attribute *tc1100_attributes[] = { - &dev_attr_wireless.attr, - &dev_attr_jogdial.attr, - NULL -}; - -static const struct attribute_group tc1100_attribute_group = { - .attrs = tc1100_attributes, -}; - -/* -------------------------------------------------------------------------- - Driver Model - -------------------------------------------------------------------------- */ - -static int __init tc1100_probe(struct platform_device *device) -{ - return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); -} - - -static int tc1100_remove(struct platform_device *device) -{ - sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); - - return 0; -} - -#ifdef CONFIG_PM -static int tc1100_suspend(struct device *dev) -{ - int ret; - - ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); - if (ret) - return ret; - - ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); - if (ret) - return ret; - - return 0; -} - -static int tc1100_resume(struct device *dev) -{ - int ret; - - ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); - if (ret) - return ret; - - ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); - if (ret) - return ret; - - return 0; -} - -static const struct dev_pm_ops tc1100_pm_ops = { - .suspend = tc1100_suspend, - .resume = tc1100_resume, - .freeze = tc1100_suspend, - .restore = tc1100_resume, -}; -#endif - -static struct platform_driver tc1100_driver = { - .driver = { - .name = "tc1100-wmi", -#ifdef CONFIG_PM - .pm = &tc1100_pm_ops, -#endif - }, - .remove = tc1100_remove, -}; - -static int __init tc1100_init(void) -{ - int error; - - if (!wmi_has_guid(GUID)) - return -ENODEV; - - tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE); - if (!tc1100_device) - return -ENOMEM; - - error = platform_device_add(tc1100_device); - if (error) - goto err_device_put; - - error = platform_driver_probe(&tc1100_driver, tc1100_probe); - if (error) - goto err_device_del; - - pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); - return 0; - - err_device_del: - platform_device_del(tc1100_device); - err_device_put: - platform_device_put(tc1100_device); - return error; -} - -static void __exit tc1100_exit(void) -{ - platform_device_unregister(tc1100_device); - platform_driver_unregister(&tc1100_driver); -} - -module_init(tc1100_init); -module_exit(tc1100_exit); -- cgit 1.4.1 From b5f3f923d421f57c0b14c80f93951f87e314433a Mon Sep 17 00:00:00 2001 From: Fae Date: Tue, 25 Apr 2023 01:36:44 -0500 Subject: platform/x86: hp-wmi: add micmute to hp_wmi_keymap struct [ Upstream commit decab2825c3ef9b154c6f76bce40872ffb41c36f ] Fixes micmute key of HP Envy X360 ey0xxx. Signed-off-by: Fae Link: https://lore.kernel.org/r/20230425063644.11828-1-faenkhauser@gmail.com Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/hp/hp-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 4a3851332ef2..94af7d398a1b 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -211,6 +211,7 @@ struct bios_rfkill2_state { static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x270, { KEY_MICMUTE } }, { KE_KEY, 0x20e6, { KEY_PROG1 } }, { KE_KEY, 0x20e8, { KEY_MEDIA } }, { KE_KEY, 0x2142, { KEY_MEDIA } }, -- cgit 1.4.1 From 7b5a8a23acbc3ee50b23602b61db1563561faf84 Mon Sep 17 00:00:00 2001 From: Horatio Zhang Date: Thu, 4 May 2023 01:46:12 -0400 Subject: drm/amdgpu: drop gfx_v11_0_cp_ecc_error_irq_funcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 720b47229a5b24061d1c2e29ddb6043a59178d79 ] The gfx.cp_ecc_error_irq is retired in gfx11. In gfx_v11_0_hw_fini still use amdgpu_irq_put to disable this interrupt, which caused the call trace in this function. [ 102.873958] Call Trace: [ 102.873959] [ 102.873961] gfx_v11_0_hw_fini+0x23/0x1e0 [amdgpu] [ 102.874019] gfx_v11_0_suspend+0xe/0x20 [amdgpu] [ 102.874072] amdgpu_device_ip_suspend_phase2+0x240/0x460 [amdgpu] [ 102.874122] amdgpu_device_ip_suspend+0x3d/0x80 [amdgpu] [ 102.874172] amdgpu_device_pre_asic_reset+0xd9/0x490 [amdgpu] [ 102.874223] amdgpu_device_gpu_recover.cold+0x548/0xce6 [amdgpu] [ 102.874321] amdgpu_debugfs_reset_work+0x4c/0x70 [amdgpu] [ 102.874375] process_one_work+0x21f/0x3f0 [ 102.874377] worker_thread+0x200/0x3e0 [ 102.874378] ? process_one_work+0x3f0/0x3f0 [ 102.874379] kthread+0xfd/0x130 [ 102.874380] ? kthread_complete_and_exit+0x20/0x20 [ 102.874381] ret_from_fork+0x22/0x30 v2: - Handle umc and gfx ras cases in separated patch - Retired the gfx_v11_0_cp_ecc_error_irq_funcs in gfx11 v3: - Improve the subject and code comments - Add judgment on gfx11 in the function of amdgpu_gfx_ras_late_init v4: - Drop the define of CP_ME1_PIPE_INST_ADDR_INTERVAL and SET_ECC_ME_PIPE_STATE which using in gfx_v11_0_set_cp_ecc_error_state - Check cp_ecc_error_irq.funcs rather than ip version for a more sustainable life v5: - Simplify judgment conditions Signed-off-by: Horatio Zhang Reviewed-by: Hawking Zhang Acked-by: Christian König Reviewed-by: Guchun Chen Reviewed-by: Feifei Xu Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 3380daf42da8..b803e785d3af 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -683,9 +683,11 @@ int amdgpu_gfx_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *r if (r) return r; - r = amdgpu_irq_get(adev, &adev->gfx.cp_ecc_error_irq, 0); - if (r) - goto late_fini; + if (adev->gfx.cp_ecc_error_irq.funcs) { + r = amdgpu_irq_get(adev, &adev->gfx.cp_ecc_error_irq, 0); + if (r) + goto late_fini; + } } else { amdgpu_ras_feature_enable_on_boot(adev, ras_block, 0); } -- cgit 1.4.1 From 070d0047c6707f0199315bccd3d7dcb9a81c4fef Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 4 Apr 2023 15:12:16 +0200 Subject: xfrm: don't check the default policy if the policy allows the packet [ Upstream commit 430cac487400494c19a8b85299e979bb07b4671f ] The current code doesn't let a simple "allow" policy counteract a default policy blocking all incoming packets: ip x p setdefault in block ip x p a src 192.168.2.1/32 dst 192.168.2.2/32 dir in action allow At this stage, we have an allow policy (with or without transforms) for this packet. It doesn't matter what the default policy says, since the policy we looked up lets the packet through. The case of a blocking policy is already handled separately, so we can remove this check. Fixes: 2d151d39073a ("xfrm: Add possibility to set the default to block if we have no policy") Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_policy.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 7f49dab3b6b5..bea48a73a731 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3637,12 +3637,6 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } xfrm_nr = ti; - if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK && - !xfrm_nr) { - XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES); - goto reject; - } - if (npols > 1) { xfrm_tmpl_sort(stp, tpp, xfrm_nr, family); tpp = stp; -- cgit 1.4.1 From 6867c4b5dbfe6aaa5ef579254dff5349da0985a9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 25 Apr 2023 09:46:18 +0200 Subject: Revert "Fix XFRM-I support for nested ESP tunnels" [ Upstream commit 5fc46f94219d1d103ffb5f0832be9da674d85a73 ] This reverts commit b0355dbbf13c0052931dd14c38c789efed64d3de. The reverted commit clears the secpath on packets received via xfrm interfaces to support nested IPsec tunnels. This breaks Netfilter policy matching using xt_policy in the FORWARD chain, as the secpath is missing during forwarding. Additionally, Benedict Wong reports that it breaks Transport-in-Tunnel mode. Fix this regression by reverting the commit until we have a better approach for nested IPsec tunnels. Fixes: b0355dbbf13c ("Fix XFRM-I support for nested ESP tunnels") Link: https://lore.kernel.org/netdev/20230412085615.124791-1-martin@strongswan.org/ Signed-off-by: Martin Willi Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_interface.c | 54 ++++------------------------------------------- net/xfrm/xfrm_policy.c | 3 --- 2 files changed, 4 insertions(+), 53 deletions(-) diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c index 94a3609548b1..5a67b120c4db 100644 --- a/net/xfrm/xfrm_interface.c +++ b/net/xfrm/xfrm_interface.c @@ -310,52 +310,6 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) skb->mark = 0; } -static int xfrmi_input(struct sk_buff *skb, int nexthdr, __be32 spi, - int encap_type, unsigned short family) -{ - struct sec_path *sp; - - sp = skb_sec_path(skb); - if (sp && (sp->len || sp->olen) && - !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family)) - goto discard; - - XFRM_SPI_SKB_CB(skb)->family = family; - if (family == AF_INET) { - XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); - XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; - } else { - XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); - XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; - } - - return xfrm_input(skb, nexthdr, spi, encap_type); -discard: - kfree_skb(skb); - return 0; -} - -static int xfrmi4_rcv(struct sk_buff *skb) -{ - return xfrmi_input(skb, ip_hdr(skb)->protocol, 0, 0, AF_INET); -} - -static int xfrmi6_rcv(struct sk_buff *skb) -{ - return xfrmi_input(skb, skb_network_header(skb)[IP6CB(skb)->nhoff], - 0, 0, AF_INET6); -} - -static int xfrmi4_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) -{ - return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET); -} - -static int xfrmi6_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) -{ - return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET6); -} - static int xfrmi_rcv_cb(struct sk_buff *skb, int err) { const struct xfrm_mode *inner_mode; @@ -983,8 +937,8 @@ static struct pernet_operations xfrmi_net_ops = { }; static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = { - .handler = xfrmi6_rcv, - .input_handler = xfrmi6_input, + .handler = xfrm6_rcv, + .input_handler = xfrm_input, .cb_handler = xfrmi_rcv_cb, .err_handler = xfrmi6_err, .priority = 10, @@ -1034,8 +988,8 @@ static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = { #endif static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = { - .handler = xfrmi4_rcv, - .input_handler = xfrmi4_input, + .handler = xfrm4_rcv, + .input_handler = xfrm_input, .cb_handler = xfrmi_rcv_cb, .err_handler = xfrmi4_err, .priority = 10, diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index bea48a73a731..bc04cb83215f 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3664,9 +3664,6 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, goto reject; } - if (if_id) - secpath_reset(skb); - xfrm_pols_put(pols, npols); return 1; } -- cgit 1.4.1 From ccde7016d131750f4043e66ed69b035e475c6011 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 21 Apr 2023 15:56:57 +0100 Subject: drm/msm/dp: unregister audio driver during unbind [ Upstream commit 85c636284cb63b7740b4ae98881ace92158068d3 ] while binding the code always registers a audio driver, however there is no corresponding unregistration done in unbind. This leads to multiple redundant audio platform devices if dp_display_bind and dp_display_unbind happens multiple times during startup. On X13s platform this resulted in 6 to 9 audio codec device instead of just 3 codec devices for 3 dp ports. Fix this by unregistering codecs on unbind. Signed-off-by: Srinivas Kandagatla Fixes: d13e36d7d222 ("drm/msm/dp: add audio support for Display Port on MSM") Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/533324/ Link: https://lore.kernel.org/r/20230421145657.12186-1-srinivas.kandagatla@linaro.org Signed-off-by: Abhinav Kumar Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_audio.c | 12 ++++++++++++ drivers/gpu/drm/msm/dp/dp_audio.h | 2 ++ drivers/gpu/drm/msm/dp/dp_display.c | 1 + 3 files changed, 15 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c index 6666783e1468..1245c7aa49df 100644 --- a/drivers/gpu/drm/msm/dp/dp_audio.c +++ b/drivers/gpu/drm/msm/dp/dp_audio.c @@ -593,6 +593,18 @@ static struct hdmi_codec_pdata codec_data = { .i2s = 1, }; +void dp_unregister_audio_driver(struct device *dev, struct dp_audio *dp_audio) +{ + struct dp_audio_private *audio_priv; + + audio_priv = container_of(dp_audio, struct dp_audio_private, dp_audio); + + if (audio_priv->audio_pdev) { + platform_device_unregister(audio_priv->audio_pdev); + audio_priv->audio_pdev = NULL; + } +} + int dp_register_audio_driver(struct device *dev, struct dp_audio *dp_audio) { diff --git a/drivers/gpu/drm/msm/dp/dp_audio.h b/drivers/gpu/drm/msm/dp/dp_audio.h index 84e5f4a5d26b..4ab78880af82 100644 --- a/drivers/gpu/drm/msm/dp/dp_audio.h +++ b/drivers/gpu/drm/msm/dp/dp_audio.h @@ -53,6 +53,8 @@ struct dp_audio *dp_audio_get(struct platform_device *pdev, int dp_register_audio_driver(struct device *dev, struct dp_audio *dp_audio); +void dp_unregister_audio_driver(struct device *dev, struct dp_audio *dp_audio); + /** * dp_audio_put() * diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index c9d9b384ddd0..57b82e5d0ab1 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -323,6 +323,7 @@ static void dp_display_unbind(struct device *dev, struct device *master, kthread_stop(dp->ev_tsk); dp_power_client_deinit(dp->power); + dp_unregister_audio_driver(dev, dp->audio); dp_aux_unregister(dp->aux); dp->drm_dev = NULL; dp->aux->drm_dev = NULL; -- cgit 1.4.1 From 092f382f65c42b2d6b91ca4e1c8c252b3595dd6f Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 26 Apr 2023 01:11:09 +0200 Subject: drm/msm/dpu: Assign missing writeback log_mask [ Upstream commit a432fc31f03db2546a48bcf5dd69ca28ceb732bf ] The WB debug log mask ended up never being assigned, leading to writes to this block to never be logged even if the mask is enabled in dpu_hw_util_log_mask via debugfs. Fixes: 84a33d0fd921 ("drm/msm/dpu: add dpu_hw_wb abstraction for writeback blocks") Signed-off-by: Marijn Suijten Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/533860/ Link: https://lore.kernel.org/r/20230418-dpu-drop-useless-for-lookup-v3-1-e8d869eea455@somainline.org Signed-off-by: Abhinav Kumar Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c index 2d28afdf860e..a3e413d27717 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c @@ -61,6 +61,7 @@ static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb, for (i = 0; i < m->wb_count; i++) { if (wb == m->wb[i].id) { b->blk_addr = addr + m->wb[i].base; + b->log_mask = DPU_DBG_MASK_WB; return &m->wb[i]; } } -- cgit 1.4.1 From d6d90e140226459a0fca9d7c26ed4bdd6a17703a Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 27 Apr 2023 00:37:17 +0200 Subject: drm/msm/dpu: Move non-MDP_TOP INTF_INTR offsets out of hwio header [ Upstream commit e9d9ce5462fecdeefec87953de71df4d025cbc72 ] These offsets do not fall under the MDP TOP block and do not fit the comment right above. Move them to dpu_hw_interrupts.c next to the repsective MDP_INTF_x_OFF interrupt block offsets. Fixes: 25fdd5933e4c ("drm/msm: Add SDM845 DPU support") Signed-off-by: Marijn Suijten Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/534203/ Link: https://lore.kernel.org/r/20230411-dpu-intf-te-v4-3-27ce1a5ab5c6@somainline.org Signed-off-by: Abhinav Kumar Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c | 5 ++++- drivers/gpu/drm/msm/disp/dpu1/dpu_hwio.h | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c index cf1b6d84c18a..75e1b89c9eac 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c @@ -15,7 +15,7 @@ /* * Register offsets in MDSS register file for the interrupt registers - * w.r.t. to the MDP base + * w.r.t. the MDP base */ #define MDP_SSPP_TOP0_OFF 0x0 #define MDP_INTF_0_OFF 0x6A000 @@ -24,6 +24,9 @@ #define MDP_INTF_3_OFF 0x6B800 #define MDP_INTF_4_OFF 0x6C000 #define MDP_INTF_5_OFF 0x6C800 +#define INTF_INTR_EN 0x1c0 +#define INTF_INTR_STATUS 0x1c4 +#define INTF_INTR_CLEAR 0x1c8 #define MDP_AD4_0_OFF 0x7C000 #define MDP_AD4_1_OFF 0x7D000 #define MDP_AD4_INTR_EN_OFF 0x41c diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hwio.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hwio.h index c8156ed4b7fb..93081e82c6d7 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hwio.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hwio.h @@ -20,9 +20,6 @@ #define HIST_INTR_EN 0x01c #define HIST_INTR_STATUS 0x020 #define HIST_INTR_CLEAR 0x024 -#define INTF_INTR_EN 0x1C0 -#define INTF_INTR_STATUS 0x1C4 -#define INTF_INTR_CLEAR 0x1C8 #define SPLIT_DISPLAY_EN 0x2F4 #define SPLIT_DISPLAY_UPPER_PIPE_CTRL 0x2F8 #define DSPP_IGC_COLOR0_RAM_LUTN 0x300 -- cgit 1.4.1 From 97d6437cbf105fb5fc26ad8b5af1a6986761fd69 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 27 Apr 2023 00:37:22 +0200 Subject: drm/msm/dpu: Remove duplicate register defines from INTF [ Upstream commit 202c044203ac5860e3025169105368d99f9bc6a2 ] The INTF_FRAME_LINE_COUNT_EN, INTF_FRAME_COUNT and INTF_LINE_COUNT registers are already defined higher up, in the right place when sorted numerically. Fixes: 25fdd5933e4c ("drm/msm: Add SDM845 DPU support") Signed-off-by: Marijn Suijten Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/534231/ Link: https://lore.kernel.org/r/20230411-dpu-intf-te-v4-8-27ce1a5ab5c6@somainline.org Signed-off-by: Abhinav Kumar Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index 7ce66bf3f4c8..b2a94b9a3e98 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -56,11 +56,6 @@ #define INTF_TPG_RGB_MAPPING 0x11C #define INTF_PROG_FETCH_START 0x170 #define INTF_PROG_ROT_START 0x174 - -#define INTF_FRAME_LINE_COUNT_EN 0x0A8 -#define INTF_FRAME_COUNT 0x0AC -#define INTF_LINE_COUNT 0x0B0 - #define INTF_MUX 0x25C #define INTF_CFG_ACTIVE_H_EN BIT(29) -- cgit 1.4.1 From 394336e13973562a71f4b9bb3729af6725963564 Mon Sep 17 00:00:00 2001 From: Jianhua Lu Date: Thu, 27 Apr 2023 20:21:32 +0800 Subject: dt-bindings: display/msm: dsi-controller-main: Document qcom, master-dsi and qcom, sync-dual-dsi [ Upstream commit ca29699a57ecee6084a4056f5bfd6f11dd359a71 ] This fixes warning: sm8250-xiaomi-elish-csot.dtb: dsi@ae94000: Unevaluated properties are not allowed ('qcom,master-dsi', 'qcom,sync-dual-dsi' were unexpected) Reviewed-by: Dmitry Baryshkov Acked-by: Rob Herring Signed-off-by: Jianhua Lu Fixes: 4dbe55c97741 ("dt-bindings: msm: dsi: add yaml schemas for DSI bindings") Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/534306/ Link: https://lore.kernel.org/r/20230427122132.24840-1-lujianhua000@gmail.com Signed-off-by: Abhinav Kumar Signed-off-by: Sasha Levin --- .../devicetree/bindings/display/msm/dsi-controller-main.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml index 6c5b4783812a..2fa1759e74d9 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml @@ -65,6 +65,18 @@ properties: Indicates if the DSI controller is driving a panel which needs 2 DSI links. + qcom,master-dsi: + type: boolean + description: | + Indicates if the DSI controller is the master DSI controller when + qcom,dual-dsi-mode enabled. + + qcom,sync-dual-dsi: + type: boolean + description: | + Indicates if the DSI controller needs to sync the other DSI controller + with MIPI DCS commands when qcom,dual-dsi-mode enabled. + assigned-clocks: maxItems: 2 description: | -- cgit 1.4.1 From 9d3ac384cbce2b488609bf807d2ac9e230affcbd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 9 Dec 2022 16:09:14 +0100 Subject: platform: Provide a remove callback that returns no value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5c5a7680e67ba6fbbb5f4d79fa41485450c1985c ] struct platform_driver::remove returning an integer made driver authors expect that returning an error code was proper error handling. However the driver core ignores the error and continues to remove the device because there is nothing the core could do anyhow and reentering the remove callback again is only calling for trouble. So this is an source for errors typically yielding resource leaks in the error path. As there are too many platform drivers to neatly convert them all to return void in a single go, do it in several steps after this patch: a) Convert all drivers to implement .remove_new() returning void instead of .remove() returning int; b) Change struct platform_driver::remove() to return void and so make it identical to .remove_new(); c) Change all drivers back to .remove() now with the better prototype; d) drop struct platform_driver::remove_new(). While this touches all drivers eventually twice, steps a) and c) can be done one driver after another and so reduces coordination efforts immensely and simplifies review. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20221209150914.3557650-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 17955aba7877 ("ASoC: fsl_micfil: Fix error handler with pm_runtime_enable") Signed-off-by: Sasha Levin --- drivers/base/platform.c | 4 +++- include/linux/platform_device.h | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 51bb2289865c..3a06c214ca1c 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1416,7 +1416,9 @@ static void platform_remove(struct device *_dev) struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); - if (drv->remove) { + if (drv->remove_new) { + drv->remove_new(dev); + } else if (drv->remove) { int ret = drv->remove(dev); if (ret) diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index b0d5a253156e..b845fd83f429 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -207,7 +207,18 @@ extern void platform_device_put(struct platform_device *pdev); struct platform_driver { int (*probe)(struct platform_device *); + + /* + * Traditionally the remove callback returned an int which however is + * ignored by the driver core. This led to wrong expectations by driver + * authors who thought returning an error code was a valid error + * handling strategy. To convert to a callback returning void, new + * drivers should implement .remove_new() until the conversion it done + * that eventually makes .remove() return void. + */ int (*remove)(struct platform_device *); + void (*remove_new)(struct platform_device *); + void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); -- cgit 1.4.1 From ce6c7befc2ea73ea0e496b35dbac72363c7c18b0 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 8 May 2023 18:16:36 +0800 Subject: ASoC: fsl_micfil: Fix error handler with pm_runtime_enable [ Upstream commit 17955aba7877a4494d8093ae5498e19469b01d57 ] There is error message when defer probe happens: fsl-micfil-dai 30ca0000.micfil: Unbalanced pm_runtime_enable! Fix the error handler with pm_runtime_enable and add fsl_micfil_remove() for pm_runtime_disable. Fixes: 47a70e6fc9a8 ("ASoC: Add MICFIL SoC Digital Audio Interface driver.") Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_micfil.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 4b8fe9b8be40..3a03f49452fa 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -712,7 +712,7 @@ static int fsl_micfil_probe(struct platform_device *pdev) ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "failed to pcm register\n"); - return ret; + goto err_pm_disable; } fsl_micfil_dai.capture.formats = micfil->soc->formats; @@ -722,9 +722,20 @@ static int fsl_micfil_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to register component %s\n", fsl_micfil_component.name); + goto err_pm_disable; } return ret; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static void fsl_micfil_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); } static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) @@ -785,6 +796,7 @@ static const struct dev_pm_ops fsl_micfil_pm_ops = { static struct platform_driver fsl_micfil_driver = { .probe = fsl_micfil_probe, + .remove_new = fsl_micfil_remove, .driver = { .name = "fsl-micfil-dai", .pm = &fsl_micfil_pm_ops, -- cgit 1.4.1 From 0d778f0cb13140d19e9a313fcca3917657562559 Mon Sep 17 00:00:00 2001 From: Wyes Karny Date: Thu, 4 May 2023 06:25:44 +0000 Subject: cpupower: Make TSC read per CPU for Mperf monitor [ Upstream commit c2adb1877b76fc81ae041e1db1a6ed2078c6746b ] System-wide TSC read could cause a drift in C0 percentage calculation. Because if first TSC is read and then one by one mperf is read for all cpus, this introduces drift between mperf reading of later CPUs and TSC reading. To lower this drift read TSC per CPU and also just after mperf read. This technique improves C0 percentage calculation in Mperf monitor. Before fix: (System 100% busy) | Mperf || RAPL || Idle_Stats PKG|CORE| CPU| C0 | Cx | Freq || pack | core || POLL | C1 | C2 0| 0| 0| 87.15| 12.85| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 0| 256| 84.62| 15.38| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 1| 1| 87.15| 12.85| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 1| 257| 84.08| 15.92| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 2| 2| 86.61| 13.39| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 2| 258| 83.26| 16.74| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 3| 3| 86.61| 13.39| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 3| 259| 83.60| 16.40| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 4| 4| 86.33| 13.67| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 4| 260| 83.33| 16.67| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 5| 5| 86.06| 13.94| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 5| 261| 83.05| 16.95| 2695||168659003|3970468|| 0.00| 0.00| 0.00 0| 6| 6| 85.51| 14.49| 2695||168659003|3970468|| 0.00| 0.00| 0.00 After fix: (System 100% busy) | Mperf || RAPL || Idle_Stats PKG|CORE| CPU| C0 | Cx | Freq || pack | core || POLL | C1 | C2 0| 0| 0| 98.03| 1.97| 2415||163295480|3811189|| 0.00| 0.00| 0.00 0| 0| 256| 98.50| 1.50| 2394||163295480|3811189|| 0.00| 0.00| 0.00 0| 1| 1| 99.99| 0.01| 2401||163295480|3811189|| 0.00| 0.00| 0.00 0| 1| 257| 99.99| 0.01| 2375||163295480|3811189|| 0.00| 0.00| 0.00 0| 2| 2| 99.99| 0.01| 2401||163295480|3811189|| 0.00| 0.00| 0.00 0| 2| 258|100.00| 0.00| 2401||163295480|3811189|| 0.00| 0.00| 0.00 0| 3| 3|100.00| 0.00| 2401||163295480|3811189|| 0.00| 0.00| 0.00 0| 3| 259| 99.99| 0.01| 2435||163295480|3811189|| 0.00| 0.00| 0.00 0| 4| 4|100.00| 0.00| 2401||163295480|3811189|| 0.00| 0.00| 0.00 0| 4| 260|100.00| 0.00| 2435||163295480|3811189|| 0.00| 0.00| 0.00 0| 5| 5| 99.99| 0.01| 2401||163295480|3811189|| 0.00| 0.00| 0.00 0| 5| 261|100.00| 0.00| 2435||163295480|3811189|| 0.00| 0.00| 0.00 0| 6| 6|100.00| 0.00| 2401||163295480|3811189|| 0.00| 0.00| 0.00 0| 6| 262|100.00| 0.00| 2435||163295480|3811189|| 0.00| 0.00| 0.00 Cc: Thomas Renninger Cc: Shuah Khan Cc: Dominik Brodowski Fixes: 7fe2f6399a84 ("cpupowerutils - cpufrequtils extended with quite some features") Signed-off-by: Wyes Karny Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- .../cpupower/utils/idle_monitor/mperf_monitor.c | 31 ++++++++++------------ 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c index e7d48cb563c0..ae6af354a81d 100644 --- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c @@ -70,8 +70,8 @@ static int max_freq_mode; */ static unsigned long max_frequency; -static unsigned long long tsc_at_measure_start; -static unsigned long long tsc_at_measure_end; +static unsigned long long *tsc_at_measure_start; +static unsigned long long *tsc_at_measure_end; static unsigned long long *mperf_previous_count; static unsigned long long *aperf_previous_count; static unsigned long long *mperf_current_count; @@ -169,7 +169,7 @@ static int mperf_get_count_percent(unsigned int id, double *percent, aperf_diff = aperf_current_count[cpu] - aperf_previous_count[cpu]; if (max_freq_mode == MAX_FREQ_TSC_REF) { - tsc_diff = tsc_at_measure_end - tsc_at_measure_start; + tsc_diff = tsc_at_measure_end[cpu] - tsc_at_measure_start[cpu]; *percent = 100.0 * mperf_diff / tsc_diff; dprint("%s: TSC Ref - mperf_diff: %llu, tsc_diff: %llu\n", mperf_cstates[id].name, mperf_diff, tsc_diff); @@ -206,7 +206,7 @@ static int mperf_get_count_freq(unsigned int id, unsigned long long *count, if (max_freq_mode == MAX_FREQ_TSC_REF) { /* Calculate max_freq from TSC count */ - tsc_diff = tsc_at_measure_end - tsc_at_measure_start; + tsc_diff = tsc_at_measure_end[cpu] - tsc_at_measure_start[cpu]; time_diff = timespec_diff_us(time_start, time_end); max_frequency = tsc_diff / time_diff; } @@ -225,33 +225,27 @@ static int mperf_get_count_freq(unsigned int id, unsigned long long *count, static int mperf_start(void) { int cpu; - unsigned long long dbg; clock_gettime(CLOCK_REALTIME, &time_start); - mperf_get_tsc(&tsc_at_measure_start); - for (cpu = 0; cpu < cpu_count; cpu++) + for (cpu = 0; cpu < cpu_count; cpu++) { + mperf_get_tsc(&tsc_at_measure_start[cpu]); mperf_init_stats(cpu); + } - mperf_get_tsc(&dbg); - dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start); return 0; } static int mperf_stop(void) { - unsigned long long dbg; int cpu; - for (cpu = 0; cpu < cpu_count; cpu++) + for (cpu = 0; cpu < cpu_count; cpu++) { mperf_measure_stats(cpu); + mperf_get_tsc(&tsc_at_measure_end[cpu]); + } - mperf_get_tsc(&tsc_at_measure_end); clock_gettime(CLOCK_REALTIME, &time_end); - - mperf_get_tsc(&dbg); - dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); - return 0; } @@ -353,7 +347,8 @@ struct cpuidle_monitor *mperf_register(void) aperf_previous_count = calloc(cpu_count, sizeof(unsigned long long)); mperf_current_count = calloc(cpu_count, sizeof(unsigned long long)); aperf_current_count = calloc(cpu_count, sizeof(unsigned long long)); - + tsc_at_measure_start = calloc(cpu_count, sizeof(unsigned long long)); + tsc_at_measure_end = calloc(cpu_count, sizeof(unsigned long long)); mperf_monitor.name_len = strlen(mperf_monitor.name); return &mperf_monitor; } @@ -364,6 +359,8 @@ void mperf_unregister(void) free(aperf_previous_count); free(mperf_current_count); free(aperf_current_count); + free(tsc_at_measure_start); + free(tsc_at_measure_end); free(is_valid); } -- cgit 1.4.1 From e5a0b280b05fd1d4172f9742215401e3099d5876 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 9 May 2023 10:59:58 +0200 Subject: xfrm: Reject optional tunnel/BEET mode templates in outbound policies [ Upstream commit 3d776e31c841ba2f69895d2255a49320bec7cea6 ] xfrm_state_find() uses `encap_family` of the current template with the passed local and remote addresses to find a matching state. If an optional tunnel or BEET mode template is skipped in a mixed-family scenario, there could be a mismatch causing an out-of-bounds read as the addresses were not replaced to match the family of the next template. While there are theoretical use cases for optional templates in outbound policies, the only practical one is to skip IPComp states in inbound policies if uncompressed packets are received that are handled by an implicitly created IPIP state instead. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Tobias Brunner Acked-by: Herbert Xu Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_user.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 83f35ecacf24..2d68a173b227 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1743,7 +1743,7 @@ static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, } static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family, - struct netlink_ext_ack *extack) + int dir, struct netlink_ext_ack *extack) { u16 prev_family; int i; @@ -1769,6 +1769,10 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family, switch (ut[i].mode) { case XFRM_MODE_TUNNEL: case XFRM_MODE_BEET: + if (ut[i].optional && dir == XFRM_POLICY_OUT) { + NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy"); + return -EINVAL; + } break; default: if (ut[i].family != prev_family) { @@ -1806,7 +1810,7 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family, } static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs, - struct netlink_ext_ack *extack) + int dir, struct netlink_ext_ack *extack) { struct nlattr *rt = attrs[XFRMA_TMPL]; @@ -1817,7 +1821,7 @@ static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs, int nr = nla_len(rt) / sizeof(*utmpl); int err; - err = validate_tmpl(nr, utmpl, pol->family, extack); + err = validate_tmpl(nr, utmpl, pol->family, dir, extack); if (err) return err; @@ -1894,7 +1898,7 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, if (err) goto error; - if (!(err = copy_from_user_tmpl(xp, attrs, extack))) + if (!(err = copy_from_user_tmpl(xp, attrs, p->dir, extack))) err = copy_from_user_sec_ctx(xp, attrs); if (err) goto error; @@ -3443,7 +3447,7 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, return NULL; nr = ((len - sizeof(*p)) / sizeof(*ut)); - if (validate_tmpl(nr, ut, p->sel.family, NULL)) + if (validate_tmpl(nr, ut, p->sel.family, p->dir, NULL)) return NULL; if (p->dir > XFRM_POLICY_OUT) -- cgit 1.4.1 From 474d57adf16ac6322fed2f87cdbc277280742106 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 9 May 2023 11:00:06 +0200 Subject: af_key: Reject optional tunnel/BEET mode templates in outbound policies [ Upstream commit cf3128a7aca55b2eefb68281d44749c683bdc96f ] xfrm_state_find() uses `encap_family` of the current template with the passed local and remote addresses to find a matching state. If an optional tunnel or BEET mode template is skipped in a mixed-family scenario, there could be a mismatch causing an out-of-bounds read as the addresses were not replaced to match the family of the next template. While there are theoretical use cases for optional templates in outbound policies, the only practical one is to skip IPComp states in inbound policies if uncompressed packets are received that are handled by an implicitly created IPIP state instead. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Tobias Brunner Acked-by: Herbert Xu Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/key/af_key.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/key/af_key.c b/net/key/af_key.c index 95edcbedf6ef..8c21de50eadf 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1940,7 +1940,8 @@ static u32 gen_reqid(struct net *net) } static int -parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq) +parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_policy *pol, + struct sadb_x_ipsecrequest *rq) { struct net *net = xp_net(xp); struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr; @@ -1958,9 +1959,12 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq) if ((mode = pfkey_mode_to_xfrm(rq->sadb_x_ipsecrequest_mode)) < 0) return -EINVAL; t->mode = mode; - if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_USE) + if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_USE) { + if ((mode == XFRM_MODE_TUNNEL || mode == XFRM_MODE_BEET) && + pol->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND) + return -EINVAL; t->optional = 1; - else if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_UNIQUE) { + } else if (rq->sadb_x_ipsecrequest_level == IPSEC_LEVEL_UNIQUE) { t->reqid = rq->sadb_x_ipsecrequest_reqid; if (t->reqid > IPSEC_MANUAL_REQID_MAX) t->reqid = 0; @@ -2002,7 +2006,7 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol) rq->sadb_x_ipsecrequest_len < sizeof(*rq)) return -EINVAL; - if ((err = parse_ipsecrequest(xp, rq)) < 0) + if ((err = parse_ipsecrequest(xp, pol, rq)) < 0) return err; len -= rq->sadb_x_ipsecrequest_len; rq = (void*)((u8*)rq + rq->sadb_x_ipsecrequest_len); -- cgit 1.4.1 From c498e5d39294d312b86bdb69a1a18051b3cc0172 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 9 May 2023 13:30:41 -0700 Subject: drm/msm: Fix submit error-path leaks [ Upstream commit 68dc6c2d5eec45515855cce99256162f45651a0b ] For errors after msm_submitqueue_get(), we need to drop the submitqueue reference. Additionally after get_unused_fd() we need to drop the fd. The ordering for dropping the queue lock and put_unused_fd() is not important, so just move this all into out_post_unlock. v2: Only drop queue ref if submit doesn't take it v3: Fix unitialized submit ref in error path v4: IS_ERR_OR_NULL() Reported-by: pinkperfect2021@gmail.com Fixes: f0de40a131d9 drm/msm: ("Reorder lock vs submit alloc") Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/536073/ Link: https://lore.kernel.org/r/20230509203041.440619-1-robdclark@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/msm_gem_submit.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index d8c9d184190b..d6162561141c 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -709,7 +709,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct msm_drm_private *priv = dev->dev_private; struct drm_msm_gem_submit *args = data; struct msm_file_private *ctx = file->driver_priv; - struct msm_gem_submit *submit; + struct msm_gem_submit *submit = NULL; struct msm_gpu *gpu = priv->gpu; struct msm_gpu_submitqueue *queue; struct msm_ringbuffer *ring; @@ -756,13 +756,15 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, out_fence_fd = get_unused_fd_flags(O_CLOEXEC); if (out_fence_fd < 0) { ret = out_fence_fd; - return ret; + goto out_post_unlock; } } submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds); - if (IS_ERR(submit)) - return PTR_ERR(submit); + if (IS_ERR(submit)) { + ret = PTR_ERR(submit); + goto out_post_unlock; + } trace_msm_gpu_submit(pid_nr(submit->pid), ring->id, submit->ident, args->nr_bos, args->nr_cmds); @@ -945,11 +947,20 @@ out: if (has_ww_ticket) ww_acquire_fini(&submit->ticket); out_unlock: - if (ret && (out_fence_fd >= 0)) - put_unused_fd(out_fence_fd); mutex_unlock(&queue->lock); out_post_unlock: - msm_gem_submit_put(submit); + if (ret && (out_fence_fd >= 0)) + put_unused_fd(out_fence_fd); + + if (!IS_ERR_OR_NULL(submit)) { + msm_gem_submit_put(submit); + } else { + /* + * If the submit hasn't yet taken ownership of the queue + * then we need to drop the reference ourself: + */ + msm_submitqueue_put(queue); + } if (!IS_ERR_OR_NULL(post_deps)) { for (i = 0; i < args->nr_out_syncobjs; ++i) { kfree(post_deps[i].chain); -- cgit 1.4.1 From 7099beeec97df38d92e68235239f124efa66691e Mon Sep 17 00:00:00 2001 From: Andrea Mayer Date: Wed, 10 May 2023 13:16:37 +0200 Subject: selftests: seg6: disable DAD on IPv6 router cfg for srv6_end_dt4_l3vpn_test [ Upstream commit 21a933c79a33add3612808f3be4ad65dd4dc026b ] The srv6_end_dt4_l3vpn_test instantiates a virtual network consisting of several routers (rt-1, rt-2) and hosts. When the IPv6 addresses of rt-{1,2} routers are configured, the Deduplicate Address Detection (DAD) kicks in when enabled in the Linux distros running the selftests. DAD is used to check whether an IPv6 address is already assigned in a network. Such a mechanism consists of sending an ICMPv6 Echo Request and waiting for a reply. As the DAD process could take too long to complete, it may cause the failing of some tests carried out by the srv6_end_dt4_l3vpn_test script. To make the srv6_end_dt4_l3vpn_test more robust, we disable DAD on routers since we configure the virtual network manually and do not need any address deduplication mechanism at all. Fixes: 2195444e09b4 ("selftests: add selftest for the SRv6 End.DT4 behavior") Signed-off-by: Andrea Mayer Reviewed-by: David Ahern Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh index 1003119773e5..37f08d582d2f 100755 --- a/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh +++ b/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh @@ -232,10 +232,14 @@ setup_rt_networking() local nsname=rt-${rt} ip netns add ${nsname} + + ip netns exec ${nsname} sysctl -wq net.ipv6.conf.all.accept_dad=0 + ip netns exec ${nsname} sysctl -wq net.ipv6.conf.default.accept_dad=0 + ip link set veth-rt-${rt} netns ${nsname} ip -netns ${nsname} link set veth-rt-${rt} name veth0 - ip -netns ${nsname} addr add ${IPv6_RT_NETWORK}::${rt}/64 dev veth0 + ip -netns ${nsname} addr add ${IPv6_RT_NETWORK}::${rt}/64 dev veth0 nodad ip -netns ${nsname} link set veth0 up ip -netns ${nsname} link set lo up -- cgit 1.4.1 From e412fa5d81f077571bd9fd847feaeb924d1328e6 Mon Sep 17 00:00:00 2001 From: Andrea Mayer Date: Wed, 10 May 2023 13:16:38 +0200 Subject: selftets: seg6: disable rp_filter by default in srv6_end_dt4_l3vpn_test [ Upstream commit f97b8401e0deb46ad1e4245c21f651f64f55aaa6 ] On some distributions, the rp_filter is automatically set (=1) by default on a netdev basis (also on VRFs). In an SRv6 End.DT4 behavior, decapsulated IPv4 packets are routed using the table associated with the VRF bound to that tunnel. During lookup operations, the rp_filter can lead to packet loss when activated on the VRF. Therefore, we chose to make this selftest more robust by explicitly disabling the rp_filter during tests (as it is automatically set by some Linux distributions). Fixes: 2195444e09b4 ("selftests: add selftest for the SRv6 End.DT4 behavior") Reported-by: Hangbin Liu Signed-off-by: Andrea Mayer Tested-by: Hangbin Liu Reviewed-by: David Ahern Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh index 37f08d582d2f..f96282362811 100755 --- a/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh +++ b/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh @@ -258,6 +258,12 @@ setup_hs() # set the networking for the host ip netns add ${hsname} + + # disable the rp_filter otherwise the kernel gets confused about how + # to route decap ipv4 packets. + ip netns exec ${rtname} sysctl -wq net.ipv4.conf.all.rp_filter=0 + ip netns exec ${rtname} sysctl -wq net.ipv4.conf.default.rp_filter=0 + ip -netns ${hsname} link add veth0 type veth peer name ${rtveth} ip -netns ${hsname} link set ${rtveth} netns ${rtname} ip -netns ${hsname} addr add ${IPv4_HS_NETWORK}.${hs}/24 dev veth0 @@ -276,11 +282,6 @@ setup_hs() ip netns exec ${rtname} sysctl -wq net.ipv4.conf.${rtveth}.proxy_arp=1 - # disable the rp_filter otherwise the kernel gets confused about how - # to route decap ipv4 packets. - ip netns exec ${rtname} sysctl -wq net.ipv4.conf.all.rp_filter=0 - ip netns exec ${rtname} sysctl -wq net.ipv4.conf.${rtveth}.rp_filter=0 - ip netns exec ${rtname} sh -c "echo 1 > /proc/sys/net/vrf/strict_mode" } -- cgit 1.4.1 From 9407454a9b18bbeff216e8ecde87ffb2171e9ccf Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 10 May 2023 22:00:20 +0200 Subject: net: fec: Better handle pm_runtime_get() failing in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f816b9829b19394d318e01953aa3b2721bca040d ] In the (unlikely) event that pm_runtime_get() (disguised as pm_runtime_resume_and_get()) fails, the remove callback returned an error early. The problem with this is that the driver core ignores the error value and continues removing the device. This results in a resource leak. Worse the devm allocated resources are freed and so if a callback of the driver is called later the register mapping is already gone which probably results in a crash. Fixes: a31eda65ba21 ("net: fec: fix clock count mis-match") Signed-off-by: Uwe Kleine-König Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20230510200020.1534610-1-u.kleine-koenig@pengutronix.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/fec_main.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 6f914180f479..33226a22d8a4 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -4168,9 +4168,11 @@ fec_drv_remove(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; int ret; - ret = pm_runtime_resume_and_get(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) - return ret; + dev_err(&pdev->dev, + "Failed to resume device in remove callback (%pe)\n", + ERR_PTR(ret)); cancel_work_sync(&fep->tx_timeout_work); fec_ptp_stop(pdev); @@ -4183,8 +4185,13 @@ fec_drv_remove(struct platform_device *pdev) of_phy_deregister_fixed_link(np); of_node_put(fep->phy_node); - clk_disable_unprepare(fep->clk_ahb); - clk_disable_unprepare(fep->clk_ipg); + /* After pm_runtime_get_sync() failed, the clks are still off, so skip + * disabling them again. + */ + if (ret >= 0) { + clk_disable_unprepare(fep->clk_ahb); + clk_disable_unprepare(fep->clk_ipg); + } pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit 1.4.1 From ea9c758184aebe46f3ab3bf318600be2b61f14bb Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 10 May 2023 18:21:39 +0530 Subject: net: phy: dp83867: add w/a for packet errors seen with short cables [ Upstream commit 0b01db274028f5acd207332686ffc92ac77491ac ] Introduce the W/A for packet errors seen with short cables (<1m) between two DP83867 PHYs. The W/A recommended by DM requires FFE Equalizer Configuration tuning by writing value 0x0E81 to DSP_FFE_CFG register (0x012C), surrounded by hard and soft resets as follows: write_reg(0x001F, 0x8000); //hard reset write_reg(DSP_FFE_CFG, 0x0E81); write_reg(0x001F, 0x4000); //soft reset Since DP83867 PHY DM says "Changing this register to 0x0E81, will not affect Long Cable performance.", enable the W/A by default. Fixes: 2a10154abcb7 ("net: phy: dp83867: Add TI dp83867 phy") Signed-off-by: Grygorii Strashko Signed-off-by: Siddharth Vadapalli Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/phy/dp83867.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 7446d5c6c714..14990f8462ae 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -42,6 +42,7 @@ #define DP83867_STRAP_STS1 0x006E #define DP83867_STRAP_STS2 0x006f #define DP83867_RGMIIDCTL 0x0086 +#define DP83867_DSP_FFE_CFG 0x012c #define DP83867_RXFCFG 0x0134 #define DP83867_RXFPMD1 0x0136 #define DP83867_RXFPMD2 0x0137 @@ -910,8 +911,27 @@ static int dp83867_phy_reset(struct phy_device *phydev) usleep_range(10, 20); - return phy_modify(phydev, MII_DP83867_PHYCTRL, + err = phy_modify(phydev, MII_DP83867_PHYCTRL, DP83867_PHYCR_FORCE_LINK_GOOD, 0); + if (err < 0) + return err; + + /* Configure the DSP Feedforward Equalizer Configuration register to + * improve short cable (< 1 meter) performance. This will not affect + * long cable performance. + */ + err = phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_DSP_FFE_CFG, + 0x0e81); + if (err < 0) + return err; + + err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESTART); + if (err < 0) + return err; + + usleep_range(10, 20); + + return 0; } static void dp83867_link_change_notify(struct phy_device *phydev) -- cgit 1.4.1 From ee1a221d947809c0308f27567c07a3ac93406057 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 9 May 2023 12:07:11 +0300 Subject: ALSA: firewire-digi00x: prevent potential use after free [ Upstream commit c0e72058d5e21982e61a29de6b098f7c1f0db498 ] This code was supposed to return an error code if init_stream() failed, but it instead freed dg00x->rx_stream and returned success. This potentially leads to a use after free. Fixes: 9a08067ec318 ("ALSA: firewire-digi00x: support AMDTP domain") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/c224cbd5-d9e2-4cd4-9bcf-2138eb1d35c6@kili.mountain Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/firewire/digi00x/digi00x-stream.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index a15f55b0dce3..295163bb8abb 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -259,8 +259,10 @@ int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) return err; err = init_stream(dg00x, &dg00x->tx_stream); - if (err < 0) + if (err < 0) { destroy_stream(dg00x, &dg00x->rx_stream); + return err; + } err = amdtp_domain_init(&dg00x->domain); if (err < 0) { -- cgit 1.4.1 From a16bf8f9c8b5857aff093f90b10066917ed18c0f Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Mon, 24 Apr 2023 05:39:06 +0800 Subject: wifi: mt76: connac: fix stats->tx_bytes calculation [ Upstream commit c7ab7a29ef5c0779574120d922256ce4651555d3 ] The stats->tx_bytes shall subtract retry byte from tx byte. Fixes: 43eaa3689507 ("wifi: mt76: add PPDU based TxS support for WED device") Signed-off-by: Ryder Lee Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/b3cd45596943cf5a06b2e08e2fe732ab0b51311b.1682285873.git.ryder.lee@mediatek.com Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h | 2 +- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h index f33171bcd343..c3b692eac6f6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h @@ -163,7 +163,7 @@ enum { #define MT_TXS5_MPDU_TX_CNT GENMASK(31, 23) #define MT_TXS6_MPDU_FAIL_CNT GENMASK(31, 23) - +#define MT_TXS7_MPDU_RETRY_BYTE GENMASK(22, 0) #define MT_TXS7_MPDU_RETRY_CNT GENMASK(31, 23) /* RXD DW1 */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 19f02b632a20..68511597599e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -570,7 +570,8 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid, /* PPDU based reporting */ if (FIELD_GET(MT_TXS0_TXS_FORMAT, txs) > 1) { stats->tx_bytes += - le32_get_bits(txs_data[5], MT_TXS5_MPDU_TX_BYTE); + le32_get_bits(txs_data[5], MT_TXS5_MPDU_TX_BYTE) - + le32_get_bits(txs_data[7], MT_TXS7_MPDU_RETRY_BYTE); stats->tx_packets += le32_get_bits(txs_data[5], MT_TXS5_MPDU_TX_CNT); stats->tx_failed += -- cgit 1.4.1 From 1c052acd717a79fcc942fb6ba0e15170d363096b Mon Sep 17 00:00:00 2001 From: "Ryan C. Underwood" Date: Thu, 11 May 2023 12:32:21 -0500 Subject: ALSA: hda/realtek: Apply HP B&O top speaker profile to Pavilion 15 [ Upstream commit 92553ee03166ef8fa978e7683f9f4af30c9c4e6b ] The Pavilion 15 line has B&O top speakers similar to the x360 and applying the same profile produces good sound. Without this, the sound would be tinny and underpowered without either applying model=alc295-hp-x360 or booting another OS first. Signed-off-by: Ryan Underwood Fixes: 563785edfcef ("ALSA: hda/realtek - Add quirk entry for HP Pavilion 15") Link: https://lore.kernel.org/r/ZF0mpcMz3ezP9KQw@icequake.net Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 172ffc2c332b..5d78d4ba1c95 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9363,7 +9363,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x802f, "HP Z240", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x8077, "HP", ALC256_FIXUP_HP_HEADSET_MIC), SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), - SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), -- cgit 1.4.1 From 116cc7670f066bbbb7a2fc26ada8a0df11aefc68 Mon Sep 17 00:00:00 2001 From: Pieter Jansen van Vuuren Date: Thu, 11 May 2023 10:43:33 +0100 Subject: sfc: disable RXFCS and RXALL features by default [ Upstream commit 134120b066044399ef59564ff3ba66ab344cfc5b ] By default we would not want RXFCS and RXALL features enabled as they are mainly intended for debugging purposes. This does not stop users from enabling them later on as needed. Fixes: 8e57daf70671 ("sfc_ef100: RX path for EF100") Signed-off-by: Pieter Jansen van Vuuren Co-developed-by: Edward Cree Signed-off-by: Edward Cree Reviewed-by: Martin Habets Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/sfc/ef100_netdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c index ddcc325ed570..c6b9ba6803c8 100644 --- a/drivers/net/ethernet/sfc/ef100_netdev.c +++ b/drivers/net/ethernet/sfc/ef100_netdev.c @@ -372,7 +372,9 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data) efx->net_dev = net_dev; SET_NETDEV_DEV(net_dev, &efx->pci_dev->dev); - net_dev->features |= efx->type->offload_features; + /* enable all supported features except rx-fcs and rx-all */ + net_dev->features |= efx->type->offload_features & + ~(NETIF_F_RXFCS | NETIF_F_RXALL); net_dev->hw_features |= efx->type->offload_features; net_dev->hw_enc_features |= efx->type->offload_features; net_dev->vlan_features |= NETIF_F_HW_CSUM | NETIF_F_SG | -- cgit 1.4.1 From 9bcf4794f1c633ff2e3d060b1d6c253cb51165d9 Mon Sep 17 00:00:00 2001 From: Zhuang Shengen Date: Thu, 11 May 2023 19:34:30 +0800 Subject: vsock: avoid to close connected socket after the timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6d4486efe9c69626cab423456169e250a5cd3af5 ] When client and server establish a connection through vsock, the client send a request to the server to initiate the connection, then start a timer to wait for the server's response. When the server's RESPONSE message arrives, the timer also times out and exits. The server's RESPONSE message is processed first, and the connection is established. However, the client's timer also times out, the original processing logic of the client is to directly set the state of this vsock to CLOSE and return ETIMEDOUT. It will not notify the server when the port is released, causing the server port remain. when client's vsock_connect timeout,it should check sk state is ESTABLISHED or not. if sk state is ESTABLISHED, it means the connection is established, the client should not set the sk state to CLOSE Note: I encountered this issue on kernel-4.18, which can be fixed by this patch. Then I checked the latest code in the community and found similar issue. Fixes: d021c344051a ("VSOCK: Introduce VM Sockets") Signed-off-by: Zhuang Shengen Reviewed-by: Stefano Garzarella Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/vmw_vsock/af_vsock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 884eca7f6743..8360c790a8a0 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -1427,7 +1427,7 @@ static int vsock_connect(struct socket *sock, struct sockaddr *addr, vsock_transport_cancel_pkt(vsk); vsock_remove_connected(vsk); goto out_wait; - } else if (timeout == 0) { + } else if ((sk->sk_state != TCP_ESTABLISHED) && (timeout == 0)) { err = -ETIMEDOUT; sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; -- cgit 1.4.1 From 820a60a4160f6f1479e6a5258ad0209351d3f5fe Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 11 May 2023 11:47:49 +0000 Subject: tcp: fix possible sk_priority leak in tcp_v4_send_reset() [ Upstream commit 1e306ec49a1f206fd2cc89a42fac6e6f592a8cc1 ] When tcp_v4_send_reset() is called with @sk == NULL, we do not change ctl_sk->sk_priority, which could have been set from a prior invocation. Change tcp_v4_send_reset() to set sk_priority and sk_mark fields before calling ip_send_unicast_reply(). This means tcp_v4_send_reset() and tcp_v4_send_ack() no longer have to clear ctl_sk->sk_mark after their call to ip_send_unicast_reply(). Fixes: f6c0f5d209fa ("tcp: honor SO_PRIORITY in TIME_WAIT state") Signed-off-by: Eric Dumazet Cc: Antoine Tenart Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/ipv4/tcp_ipv4.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ad0a5f185a69..b37c1bcb1509 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -829,6 +829,9 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) inet_twsk(sk)->tw_priority : sk->sk_priority; transmit_time = tcp_transmit_time(sk); xfrm_sk_clone_policy(ctl_sk, sk); + } else { + ctl_sk->sk_mark = 0; + ctl_sk->sk_priority = 0; } ip_send_unicast_reply(ctl_sk, skb, &TCP_SKB_CB(skb)->header.h4.opt, @@ -836,7 +839,6 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) &arg, arg.iov[0].iov_len, transmit_time); - ctl_sk->sk_mark = 0; xfrm_sk_free_policy(ctl_sk); sock_net_set(ctl_sk, &init_net); __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); @@ -935,7 +937,6 @@ static void tcp_v4_send_ack(const struct sock *sk, &arg, arg.iov[0].iov_len, transmit_time); - ctl_sk->sk_mark = 0; sock_net_set(ctl_sk, &init_net); __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); local_bh_enable(); -- cgit 1.4.1 From 081790eee6b47389a0d895262086d64c6a38d6e5 Mon Sep 17 00:00:00 2001 From: Ke Zhang Date: Fri, 28 Apr 2023 11:16:36 +0800 Subject: serial: arc_uart: fix of_iomap leak in `arc_serial_probe` [ Upstream commit 8ab5fc55d7f65d58a3c3aeadf11bdf60267cd2bd ] Smatch reports: drivers/tty/serial/arc_uart.c:631 arc_serial_probe() warn: 'port->membase' from of_iomap() not released on lines: 631. In arc_serial_probe(), if uart_add_one_port() fails, port->membase is not released, which would cause a resource leak. To fix this, I replace of_iomap with devm_platform_ioremap_resource. Fixes: 8dbe1d5e09a7 ("serial/arc: inline the probe helper") Signed-off-by: Ke Zhang Reviewed-by: Dongliang Mu Link: https://lore.kernel.org/r/20230428031636.44642-1-m202171830@hust.edu.cn Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/arc_uart.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 2a65ea2660e1..f3ccc59d8c1f 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -607,10 +607,11 @@ static int arc_serial_probe(struct platform_device *pdev) } uart->baud = val; - port->membase = of_iomap(np, 0); - if (!port->membase) + port->membase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(port->membase)) { /* No point of dev_err since UART itself is hosed here */ - return -ENXIO; + return PTR_ERR(port->membase); + } port->irq = irq_of_parse_and_map(np, 0); -- cgit 1.4.1 From dcf08087c2cdf557a6d6fb70da9bace744827ffd Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Thu, 27 Apr 2023 11:19:15 -0700 Subject: serial: 8250_bcm7271: balance clk_enable calls [ Upstream commit 8a3b5477256a54ae4a470dcebbcf8cdc18e4696d ] The sw_baud clock must be disabled when the device driver is not connected to the device. This now occurs when probe fails and upon remove. Fixes: 41a469482de2 ("serial: 8250: Add new 8250-core based Broadcom STB driver") Reported-by: XuDong Liu Link: https://lore.kernel.org/lkml/20230424125100.4783-1-m202071377@hust.edu.cn/ Signed-off-by: Doug Berger Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20230427181916.2983697-2-opendmb@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/8250/8250_bcm7271.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 36e31b96ef4a..1f0095cf57a7 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -1034,7 +1034,7 @@ static int brcmuart_probe(struct platform_device *pdev) if (clk_rate == 0) { dev_err(dev, "clock-frequency or clk not defined\n"); ret = -EINVAL; - goto release_dma; + goto err_clk_disable; } dev_dbg(dev, "DMA is %senabled\n", priv->dma_enabled ? "" : "not "); @@ -1121,6 +1121,8 @@ err1: serial8250_unregister_port(priv->line); err: brcmuart_free_bufs(dev, priv); +err_clk_disable: + clk_disable_unprepare(baud_mux_clk); release_dma: if (priv->dma_enabled) brcmuart_arbitration(priv, 0); @@ -1135,6 +1137,7 @@ static int brcmuart_remove(struct platform_device *pdev) hrtimer_cancel(&priv->hrt); serial8250_unregister_port(priv->line); brcmuart_free_bufs(&pdev->dev, priv); + clk_disable_unprepare(priv->baud_mux_clk); if (priv->dma_enabled) brcmuart_arbitration(priv, 0); return 0; -- cgit 1.4.1 From 2a3e5f428fc4315be6144524912eaefac16f43a9 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Thu, 27 Apr 2023 11:19:16 -0700 Subject: serial: 8250_bcm7271: fix leak in `brcmuart_probe` [ Upstream commit f264f2f6f4788dc031cef60a0cf2881902736709 ] Smatch reports: drivers/tty/serial/8250/8250_bcm7271.c:1120 brcmuart_probe() warn: 'baud_mux_clk' from clk_prepare_enable() not released on lines: 1032. The issue is fixed by using a managed clock. Fixes: 41a469482de2 ("serial: 8250: Add new 8250-core based Broadcom STB driver") Reported-by: XuDong Liu Link: https://lore.kernel.org/lkml/20230424125100.4783-1-m202071377@hust.edu.cn/ Signed-off-by: Doug Berger Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20230427181916.2983697-3-opendmb@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/8250/8250_bcm7271.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 1f0095cf57a7..ffc7f67e27e3 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -1014,7 +1014,7 @@ static int brcmuart_probe(struct platform_device *pdev) of_property_read_u32(np, "clock-frequency", &clk_rate); /* See if a Baud clock has been specified */ - baud_mux_clk = of_clk_get_by_name(np, "sw_baud"); + baud_mux_clk = devm_clk_get(dev, "sw_baud"); if (IS_ERR(baud_mux_clk)) { if (PTR_ERR(baud_mux_clk) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; -- cgit 1.4.1 From 33a93db90967726882c539cf6dc345828f8ac48a Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 11 May 2023 19:22:11 -0400 Subject: erspan: get the proto with the md version for collect_md [ Upstream commit d80fc101d2eb9b3188c228d61223890aeea480a4 ] In commit 20704bd1633d ("erspan: build the header with the right proto according to erspan_ver"), it gets the proto with t->parms.erspan_ver, but t->parms.erspan_ver is not used by collect_md branch, and instead it should get the proto with md->version for collect_md. Thanks to Kevin for pointing this out. Fixes: 20704bd1633d ("erspan: build the header with the right proto according to erspan_ver") Fixes: 94d7d8f29287 ("ip6_gre: add erspan v2 support") Reported-by: Kevin Traynor Signed-off-by: Xin Long Reviewed-by: Simon Horman Reviewed-by: William Tu Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/ipv6/ip6_gre.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 4d5937af08ee..216b40ccadae 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1037,12 +1037,14 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, ntohl(tun_id), ntohl(md->u.index), truncate, false); + proto = htons(ETH_P_ERSPAN); } else if (md->version == 2) { erspan_build_header_v2(skb, ntohl(tun_id), md->u.md2.dir, get_hwid(&md->u.md2), truncate, false); + proto = htons(ETH_P_ERSPAN2); } else { goto tx_err; } @@ -1065,24 +1067,25 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, break; } - if (t->parms.erspan_ver == 1) + if (t->parms.erspan_ver == 1) { erspan_build_header(skb, ntohl(t->parms.o_key), t->parms.index, truncate, false); - else if (t->parms.erspan_ver == 2) + proto = htons(ETH_P_ERSPAN); + } else if (t->parms.erspan_ver == 2) { erspan_build_header_v2(skb, ntohl(t->parms.o_key), t->parms.dir, t->parms.hwid, truncate, false); - else + proto = htons(ETH_P_ERSPAN2); + } else { goto tx_err; + } fl6.daddr = t->parms.raddr; } /* Push GRE header. */ - proto = (t->parms.erspan_ver == 1) ? htons(ETH_P_ERSPAN) - : htons(ETH_P_ERSPAN2); gre_build_header(skb, 8, TUNNEL_SEQ, proto, 0, htonl(atomic_fetch_inc(&t->o_seqno))); /* TooBig packet may have updated dst->dev's mtu */ -- cgit 1.4.1 From 374c9cf3ad60bf64409b9ddf281041533f38a6a2 Mon Sep 17 00:00:00 2001 From: Clément Léger Date: Fri, 12 May 2023 09:27:10 +0200 Subject: net: dsa: rzn1-a5psw: enable management frames for CPU port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9e4b45f20c5aac786c728619e5ee746bffce1798 ] Currently, management frame were discarded before reaching the CPU port due to a misconfiguration of the MGMT_CONFIG register. Enable them by setting the correct value in this register in order to correctly receive management frame and handle STP. Fixes: 888cdb892b61 ("net: dsa: rzn1-a5psw: add Renesas RZ/N1 advanced 5 port switch driver") Signed-off-by: Clément Léger Signed-off-by: Alexis Lothoré Reviewed-by: Piotr Raczynski Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/rzn1_a5psw.c | 2 +- drivers/net/dsa/rzn1_a5psw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index ed413d555bec..92a3ac78ab1e 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -673,7 +673,7 @@ static int a5psw_setup(struct dsa_switch *ds) } /* Configure management port */ - reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_DISCARD; + reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_ENABLE; a5psw_reg_writel(a5psw, A5PSW_MGMT_CFG, reg); /* Set pattern 0 to forward all frame to mgmt port */ diff --git a/drivers/net/dsa/rzn1_a5psw.h b/drivers/net/dsa/rzn1_a5psw.h index c67abd49c013..b4fbf453ff74 100644 --- a/drivers/net/dsa/rzn1_a5psw.h +++ b/drivers/net/dsa/rzn1_a5psw.h @@ -36,7 +36,7 @@ #define A5PSW_INPUT_LEARN_BLOCK(p) BIT(p) #define A5PSW_MGMT_CFG 0x20 -#define A5PSW_MGMT_CFG_DISCARD BIT(7) +#define A5PSW_MGMT_CFG_ENABLE BIT(6) #define A5PSW_MODE_CFG 0x24 #define A5PSW_MODE_STATS_RESET BIT(31) -- cgit 1.4.1 From 7ceeb5608d16892e08ec1a7c97c5c96d7d804594 Mon Sep 17 00:00:00 2001 From: Alexis Lothoré Date: Fri, 12 May 2023 09:27:11 +0200 Subject: net: dsa: rzn1-a5psw: fix STP states handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ebe9bc50952757b4b25eaf514da7c464196c9606 ] stp_set_state() should actually allow receiving BPDU while in LEARNING mode which is not the case. Additionally, the BLOCKEN bit does not actually forbid sending forwarded frames from that port. To fix this, add a5psw_port_tx_enable() function which allows to disable TX. However, while its name suggest that TX is totally disabled, it is not and can still allow to send BPDUs even if disabled. This can be done by using forced forwarding with the switch tagging mechanism but keeping "filtering" disabled (which is already the case in the rzn1-a5sw tag driver). With these fixes, STP support is now functional. Fixes: 888cdb892b61 ("net: dsa: rzn1-a5psw: add Renesas RZ/N1 advanced 5 port switch driver") Signed-off-by: Clément Léger Signed-off-by: Alexis Lothoré Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/rzn1_a5psw.c | 57 +++++++++++++++++++++++++++++++++++++------- drivers/net/dsa/rzn1_a5psw.h | 1 + 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index 92a3ac78ab1e..2b0463263767 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -120,6 +120,22 @@ static void a5psw_port_mgmtfwd_set(struct a5psw *a5psw, int port, bool enable) a5psw_port_pattern_set(a5psw, port, A5PSW_PATTERN_MGMTFWD, enable); } +static void a5psw_port_tx_enable(struct a5psw *a5psw, int port, bool enable) +{ + u32 mask = A5PSW_PORT_ENA_TX(port); + u32 reg = enable ? mask : 0; + + /* Even though the port TX is disabled through TXENA bit in the + * PORT_ENA register, it can still send BPDUs. This depends on the tag + * configuration added when sending packets from the CPU port to the + * switch port. Indeed, when using forced forwarding without filtering, + * even disabled ports will be able to send packets that are tagged. + * This allows to implement STP support when ports are in a state where + * forwarding traffic should be stopped but BPDUs should still be sent. + */ + a5psw_reg_rmw(a5psw, A5PSW_PORT_ENA, mask, reg); +} + static void a5psw_port_enable_set(struct a5psw *a5psw, int port, bool enable) { u32 port_ena = 0; @@ -292,6 +308,22 @@ static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) return 0; } +static void a5psw_port_learning_set(struct a5psw *a5psw, int port, bool learn) +{ + u32 mask = A5PSW_INPUT_LEARN_DIS(port); + u32 reg = !learn ? mask : 0; + + a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg); +} + +static void a5psw_port_rx_block_set(struct a5psw *a5psw, int port, bool block) +{ + u32 mask = A5PSW_INPUT_LEARN_BLOCK(port); + u32 reg = block ? mask : 0; + + a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg); +} + static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port, bool set) { @@ -344,28 +376,35 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port, static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - u32 mask = A5PSW_INPUT_LEARN_DIS(port) | A5PSW_INPUT_LEARN_BLOCK(port); + bool learning_enabled, rx_enabled, tx_enabled; struct a5psw *a5psw = ds->priv; - u32 reg = 0; switch (state) { case BR_STATE_DISABLED: case BR_STATE_BLOCKING: - reg |= A5PSW_INPUT_LEARN_DIS(port); - reg |= A5PSW_INPUT_LEARN_BLOCK(port); - break; case BR_STATE_LISTENING: - reg |= A5PSW_INPUT_LEARN_DIS(port); + rx_enabled = false; + tx_enabled = false; + learning_enabled = false; break; case BR_STATE_LEARNING: - reg |= A5PSW_INPUT_LEARN_BLOCK(port); + rx_enabled = false; + tx_enabled = false; + learning_enabled = true; break; case BR_STATE_FORWARDING: - default: + rx_enabled = true; + tx_enabled = true; + learning_enabled = true; break; + default: + dev_err(ds->dev, "invalid STP state: %d\n", state); + return; } - a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg); + a5psw_port_learning_set(a5psw, port, learning_enabled); + a5psw_port_rx_block_set(a5psw, port, !rx_enabled); + a5psw_port_tx_enable(a5psw, port, tx_enabled); } static void a5psw_port_fast_age(struct dsa_switch *ds, int port) diff --git a/drivers/net/dsa/rzn1_a5psw.h b/drivers/net/dsa/rzn1_a5psw.h index b4fbf453ff74..b869192eef3f 100644 --- a/drivers/net/dsa/rzn1_a5psw.h +++ b/drivers/net/dsa/rzn1_a5psw.h @@ -19,6 +19,7 @@ #define A5PSW_PORT_OFFSET(port) (0x400 * (port)) #define A5PSW_PORT_ENA 0x8 +#define A5PSW_PORT_ENA_TX(port) BIT(port) #define A5PSW_PORT_ENA_RX_SHIFT 16 #define A5PSW_PORT_ENA_TX_RX(port) (BIT((port) + A5PSW_PORT_ENA_RX_SHIFT) | \ BIT(port)) -- cgit 1.4.1 From 37c1e28967e11caf7500e1234da543ac89d14e65 Mon Sep 17 00:00:00 2001 From: Clément Léger Date: Fri, 12 May 2023 09:27:12 +0200 Subject: net: dsa: rzn1-a5psw: disable learning for standalone ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ec52b69c046a6219011af780aca155a96719637b ] When ports are in standalone mode, they should have learning disabled to avoid adding new entries in the MAC lookup table which might be used by other bridge ports to forward packets. While adding that, also make sure learning is enabled for CPU port. Fixes: 888cdb892b61 ("net: dsa: rzn1-a5psw: add Renesas RZ/N1 advanced 5 port switch driver") Signed-off-by: Clément Léger Signed-off-by: Alexis Lothoré Reviewed-by: Piotr Raczynski Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/rzn1_a5psw.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index 2b0463263767..790e177e2aef 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -340,6 +340,14 @@ static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port, a5psw_reg_writel(a5psw, offsets[i], a5psw->bridged_ports); } +static void a5psw_port_set_standalone(struct a5psw *a5psw, int port, + bool standalone) +{ + a5psw_port_learning_set(a5psw, port, !standalone); + a5psw_flooding_set_resolution(a5psw, port, !standalone); + a5psw_port_mgmtfwd_set(a5psw, port, standalone); +} + static int a5psw_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, bool *tx_fwd_offload, @@ -355,8 +363,7 @@ static int a5psw_port_bridge_join(struct dsa_switch *ds, int port, } a5psw->br_dev = bridge.dev; - a5psw_flooding_set_resolution(a5psw, port, true); - a5psw_port_mgmtfwd_set(a5psw, port, false); + a5psw_port_set_standalone(a5psw, port, false); return 0; } @@ -366,8 +373,7 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port, { struct a5psw *a5psw = ds->priv; - a5psw_flooding_set_resolution(a5psw, port, false); - a5psw_port_mgmtfwd_set(a5psw, port, true); + a5psw_port_set_standalone(a5psw, port, true); /* No more ports bridged */ if (a5psw->bridged_ports == BIT(A5PSW_CPU_PORT)) @@ -761,13 +767,15 @@ static int a5psw_setup(struct dsa_switch *ds) if (dsa_port_is_unused(dp)) continue; - /* Enable egress flooding for CPU port */ - if (dsa_port_is_cpu(dp)) + /* Enable egress flooding and learning for CPU port */ + if (dsa_port_is_cpu(dp)) { a5psw_flooding_set_resolution(a5psw, port, true); + a5psw_port_learning_set(a5psw, port, true); + } - /* Enable management forward only for user ports */ + /* Enable standalone mode for user ports */ if (dsa_port_is_user(dp)) - a5psw_port_mgmtfwd_set(a5psw, port, true); + a5psw_port_set_standalone(a5psw, port, true); } return 0; -- cgit 1.4.1 From 8ee34c90ce5e5267eaa8eb8941b195418ef692a9 Mon Sep 17 00:00:00 2001 From: Jie Wang Date: Fri, 12 May 2023 18:00:11 +0800 Subject: net: hns3: fix output information incomplete for dumping tx queue info with debugfs [ Upstream commit 89f6bfb071182f05d7188c255b0e7251c3806f16 ] In function hns3_dump_tx_queue_info, The print buffer is not enough when the tx BD number is configured to 32760. As a result several BD information wouldn't be displayed. So fix it by increasing the tx queue print buffer length. Fixes: 630a6738da82 ("net: hns3: adjust string spaces of some parameters of tx bd info in debugfs") Signed-off-by: Jie Wang Signed-off-by: Hao Lan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c | 2 +- drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 66feb23f7b7b..bcccd82a2620 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -130,7 +130,7 @@ static struct hns3_dbg_cmd_info hns3_dbg_cmd[] = { .name = "tx_bd_queue", .cmd = HNAE3_DBG_CMD_TX_BD, .dentry = HNS3_DBG_DENTRY_TX_BD, - .buf_len = HNS3_DBG_READ_LEN_4MB, + .buf_len = HNS3_DBG_READ_LEN_5MB, .init = hns3_dbg_bd_file_init, }, { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h index 97578eabb7d8..4a5ef8a90a10 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h @@ -10,6 +10,7 @@ #define HNS3_DBG_READ_LEN_128KB 0x20000 #define HNS3_DBG_READ_LEN_1MB 0x100000 #define HNS3_DBG_READ_LEN_4MB 0x400000 +#define HNS3_DBG_READ_LEN_5MB 0x500000 #define HNS3_DBG_WRITE_LEN 1024 #define HNS3_DBG_DATA_STR_LEN 32 -- cgit 1.4.1 From 2a06c5ab7b92c0622e3f4d43af751bc16e577807 Mon Sep 17 00:00:00 2001 From: Jijie Shao Date: Fri, 12 May 2023 18:00:12 +0800 Subject: net: hns3: fix sending pfc frames after reset issue [ Upstream commit f14db07064727dd3bc0906c77a6d2759c1bbb395 ] To prevent the system from abnormally sending PFC frames after an abnormal reset. The hns3 driver notifies the firmware to disable pfc before reset. Fixes: 35d93a30040c ("net: hns3: adjust the process of PF reset") Signed-off-by: Jijie Shao Signed-off-by: Hao Lan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 15 +++++++++------ drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c | 4 ++-- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h | 5 +++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 07ad5f35219e..50e956d6c3b2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -8053,12 +8053,15 @@ static void hclge_ae_stop(struct hnae3_handle *handle) /* If it is not PF reset or FLR, the firmware will disable the MAC, * so it only need to stop phy here. */ - if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) && - hdev->reset_type != HNAE3_FUNC_RESET && - hdev->reset_type != HNAE3_FLR_RESET) { - hclge_mac_stop_phy(hdev); - hclge_update_link_status(hdev); - return; + if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) { + hclge_pfc_pause_en_cfg(hdev, HCLGE_PFC_TX_RX_DISABLE, + HCLGE_PFC_DISABLE); + if (hdev->reset_type != HNAE3_FUNC_RESET && + hdev->reset_type != HNAE3_FLR_RESET) { + hclge_mac_stop_phy(hdev); + hclge_update_link_status(hdev); + return; + } } hclge_reset_tqp(handle); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index 4a33f65190e2..922c0da3660c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -171,8 +171,8 @@ int hclge_mac_pause_en_cfg(struct hclge_dev *hdev, bool tx, bool rx) return hclge_cmd_send(&hdev->hw, &desc, 1); } -static int hclge_pfc_pause_en_cfg(struct hclge_dev *hdev, u8 tx_rx_bitmap, - u8 pfc_bitmap) +int hclge_pfc_pause_en_cfg(struct hclge_dev *hdev, u8 tx_rx_bitmap, + u8 pfc_bitmap) { struct hclge_desc desc; struct hclge_pfc_en_cmd *pfc = (struct hclge_pfc_en_cmd *)desc.data; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h index 68f28a98e380..dd6f1fd486cf 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h @@ -164,6 +164,9 @@ struct hclge_bp_to_qs_map_cmd { u32 rsvd1; }; +#define HCLGE_PFC_DISABLE 0 +#define HCLGE_PFC_TX_RX_DISABLE 0 + struct hclge_pfc_en_cmd { u8 tx_rx_en_bitmap; u8 pri_en_bitmap; @@ -235,6 +238,8 @@ void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc); void hclge_tm_pfc_info_update(struct hclge_dev *hdev); int hclge_tm_dwrr_cfg(struct hclge_dev *hdev); int hclge_tm_init_hw(struct hclge_dev *hdev, bool init); +int hclge_pfc_pause_en_cfg(struct hclge_dev *hdev, u8 tx_rx_bitmap, + u8 pfc_bitmap); int hclge_mac_pause_en_cfg(struct hclge_dev *hdev, bool tx, bool rx); int hclge_pause_addr_cfg(struct hclge_dev *hdev, const u8 *mac_addr); void hclge_pfc_rx_stats_get(struct hclge_dev *hdev, u64 *stats); -- cgit 1.4.1 From 89982e050110813244301f9def2d5652c2b9568e Mon Sep 17 00:00:00 2001 From: Jie Wang Date: Fri, 12 May 2023 18:00:13 +0800 Subject: net: hns3: fix reset delay time to avoid configuration timeout [ Upstream commit 814d0c786068e858d889ada3153bff82f64223ad ] Currently the hns3 vf function reset delays 5000ms before vf rebuild process. In product applications, this delay is too long for application configurations and causes configuration timeout. According to the tests, 500ms delay is enough for reset process except PF FLR. So this patch modifies delay to 500ms in these scenarios. Fixes: 6988eb2a9b77 ("net: hns3: Add support to reset the enet/ring mgmt layer") Signed-off-by: Jie Wang Signed-off-by: Hao Lan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index e84e5be8e59e..b1b14850e958 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -1436,7 +1436,10 @@ static int hclgevf_reset_wait(struct hclgevf_dev *hdev) * might happen in case reset assertion was made by PF. Yes, this also * means we might end up waiting bit more even for VF reset. */ - msleep(5000); + if (hdev->reset_type == HNAE3_VF_FULL_RESET) + msleep(5000); + else + msleep(500); return 0; } -- cgit 1.4.1 From 4147a0cee15d6a0ee9edf1ea00552c9609e77112 Mon Sep 17 00:00:00 2001 From: Jijie Shao Date: Fri, 12 May 2023 18:00:14 +0800 Subject: net: hns3: fix reset timeout when enable full VF [ Upstream commit 6b45d5ff8c2c61baddd67d7510075ae121c5e704 ] The timeout of the cmdq reset command has been increased to resolve the reset timeout issue in the full VF scenario. The timeout of other cmdq commands remains unchanged. Fixes: 8d307f8e8cf1 ("net: hns3: create new set of unified hclge_comm_cmd_send APIs") Signed-off-by: Jijie Shao Signed-off-by: Hao Lan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- .../hisilicon/hns3/hns3_common/hclge_comm_cmd.c | 25 ++++++++++++++++++---- .../hisilicon/hns3/hns3_common/hclge_comm_cmd.h | 8 ++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c index f671a63cecde..c797d54f98ca 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c @@ -330,9 +330,25 @@ static int hclge_comm_cmd_csq_done(struct hclge_comm_hw *hw) return head == hw->cmq.csq.next_to_use; } -static void hclge_comm_wait_for_resp(struct hclge_comm_hw *hw, +static u32 hclge_get_cmdq_tx_timeout(u16 opcode, u32 tx_timeout) +{ + static const struct hclge_cmdq_tx_timeout_map cmdq_tx_timeout_map[] = { + {HCLGE_OPC_CFG_RST_TRIGGER, HCLGE_COMM_CMDQ_TX_TIMEOUT_500MS}, + }; + u32 i; + + for (i = 0; i < ARRAY_SIZE(cmdq_tx_timeout_map); i++) + if (cmdq_tx_timeout_map[i].opcode == opcode) + return cmdq_tx_timeout_map[i].tx_timeout; + + return tx_timeout; +} + +static void hclge_comm_wait_for_resp(struct hclge_comm_hw *hw, u16 opcode, bool *is_completed) { + u32 cmdq_tx_timeout = hclge_get_cmdq_tx_timeout(opcode, + hw->cmq.tx_timeout); u32 timeout = 0; do { @@ -342,7 +358,7 @@ static void hclge_comm_wait_for_resp(struct hclge_comm_hw *hw, } udelay(1); timeout++; - } while (timeout < hw->cmq.tx_timeout); + } while (timeout < cmdq_tx_timeout); } static int hclge_comm_cmd_convert_err_code(u16 desc_ret) @@ -406,7 +422,8 @@ static int hclge_comm_cmd_check_result(struct hclge_comm_hw *hw, * if multi descriptors to be sent, use the first one to check */ if (HCLGE_COMM_SEND_SYNC(le16_to_cpu(desc->flag))) - hclge_comm_wait_for_resp(hw, &is_completed); + hclge_comm_wait_for_resp(hw, le16_to_cpu(desc->opcode), + &is_completed); if (!is_completed) ret = -EBADE; @@ -528,7 +545,7 @@ int hclge_comm_cmd_queue_init(struct pci_dev *pdev, struct hclge_comm_hw *hw) cmdq->crq.desc_num = HCLGE_COMM_NIC_CMQ_DESC_NUM; /* Setup Tx write back timeout */ - cmdq->tx_timeout = HCLGE_COMM_CMDQ_TX_TIMEOUT; + cmdq->tx_timeout = HCLGE_COMM_CMDQ_TX_TIMEOUT_DEFAULT; /* Setup queue rings */ ret = hclge_comm_alloc_cmd_queue(hw, HCLGE_COMM_TYPE_CSQ); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h index b1f9383b418f..2b2928c6dccf 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h @@ -54,7 +54,8 @@ #define HCLGE_COMM_NIC_SW_RST_RDY BIT(HCLGE_COMM_NIC_SW_RST_RDY_B) #define HCLGE_COMM_NIC_CMQ_DESC_NUM_S 3 #define HCLGE_COMM_NIC_CMQ_DESC_NUM 1024 -#define HCLGE_COMM_CMDQ_TX_TIMEOUT 30000 +#define HCLGE_COMM_CMDQ_TX_TIMEOUT_DEFAULT 30000 +#define HCLGE_COMM_CMDQ_TX_TIMEOUT_500MS 500000 enum hclge_opcode_type { /* Generic commands */ @@ -357,6 +358,11 @@ struct hclge_comm_caps_bit_map { u16 local_bit; }; +struct hclge_cmdq_tx_timeout_map { + u32 opcode; + u32 tx_timeout; +}; + struct hclge_comm_firmware_compat_cmd { __le32 compat; u8 rsv[20]; -- cgit 1.4.1 From 07821524f67bf920342bc84ae8b3dea2a315a89e Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Wed, 8 Mar 2023 12:55:14 +0000 Subject: media: netup_unidvb: fix use-after-free at del_timer() [ Upstream commit 0f5bb36bf9b39a2a96e730bf4455095b50713f63 ] When Universal DVB card is detaching, netup_unidvb_dma_fini() uses del_timer() to stop dma->timeout timer. But when timer handler netup_unidvb_dma_timeout() is running, del_timer() could not stop it. As a result, the use-after-free bug could happen. The process is shown below: (cleanup routine) | (timer routine) | mod_timer(&dev->tx_sim_timer, ..) netup_unidvb_finidev() | (wait a time) netup_unidvb_dma_fini() | netup_unidvb_dma_timeout() del_timer(&dma->timeout); | | ndev->pci_dev->dev //USE Fix by changing del_timer() to del_timer_sync(). Link: https://lore.kernel.org/linux-media/20230308125514.4208-1-duoming@zju.edu.cn Fixes: 52b1eaf4c59a ("[media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver") Signed-off-by: Duoming Zhou Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index 8287851b5ffd..aaa1d2dedebd 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -697,7 +697,7 @@ static void netup_unidvb_dma_fini(struct netup_unidvb_dev *ndev, int num) netup_unidvb_dma_enable(dma, 0); msleep(50); cancel_work_sync(&dma->work); - del_timer(&dma->timeout); + del_timer_sync(&dma->timeout); } static int netup_unidvb_dma_setup(struct netup_unidvb_dev *ndev) -- cgit 1.4.1 From fd86534872f445f54dc01e7db001e25eadf063a8 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 9 May 2023 09:41:49 +1000 Subject: SUNRPC: double free xprt_ctxt while still in use [ Upstream commit eb8d3a2c809abd73ab0a060fe971d6b9019aa3c1 ] When an RPC request is deferred, the rq_xprt_ctxt pointer is moved out of the svc_rqst into the svc_deferred_req. When the deferred request is revisited, the pointer is copied into the new svc_rqst - and also remains in the svc_deferred_req. In the (rare?) case that the request is deferred a second time, the old svc_deferred_req is reused - it still has all the correct content. However in that case the rq_xprt_ctxt pointer is NOT cleared so that when xpo_release_xprt is called, the ctxt is freed (UDP) or possible added to a free list (RDMA). When the deferred request is revisited for a second time, it will reference this ctxt which may be invalid, and the free the object a second time which is likely to oops. So change svc_defer() to *always* clear rq_xprt_ctxt, and assert that the value is now stored in the svc_deferred_req. Fixes: 773f91b2cf3f ("SUNRPC: Fix NFSD's request deferral on RDMA transports") Signed-off-by: NeilBrown Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- net/sunrpc/svc_xprt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index c2ce12538008..b4306cf1b458 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -1228,13 +1228,14 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req) dr->daddr = rqstp->rq_daddr; dr->argslen = rqstp->rq_arg.len >> 2; dr->xprt_ctxt = rqstp->rq_xprt_ctxt; - rqstp->rq_xprt_ctxt = NULL; /* back up head to the start of the buffer and copy */ skip = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len; memcpy(dr->args, rqstp->rq_arg.head[0].iov_base - skip, dr->argslen << 2); } + WARN_ON_ONCE(rqstp->rq_xprt_ctxt != dr->xprt_ctxt); + rqstp->rq_xprt_ctxt = NULL; trace_svc_defer(rqstp); svc_xprt_get(rqstp->rq_xprt); dr->xprt = rqstp->rq_xprt; -- cgit 1.4.1 From 47adb84916ee7a66235d179b98e7702cbd73b54f Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 9 May 2023 09:42:47 +1000 Subject: SUNRPC: always free ctxt when freeing deferred request [ Upstream commit 948f072ada23e0a504c5e4d7d71d4c83bd0785ec ] Since the ->xprt_ctxt pointer was added to svc_deferred_req, it has not been sufficient to use kfree() to free a deferred request. We may need to free the ctxt as well. As freeing the ctxt is all that ->xpo_release_rqst() does, we repurpose it to explicit do that even when the ctxt is not stored in an rqst. So we now have ->xpo_release_ctxt() which is given an xprt and a ctxt, which may have been taken either from an rqst or from a dreq. The caller is now responsible for clearing that pointer after the call to ->xpo_release_ctxt. We also clear dr->xprt_ctxt when the ctxt is moved into a new rqst when revisiting a deferred request. This ensures there is only one pointer to the ctxt, so the risk of double freeing in future is reduced. The new code in svc_xprt_release which releases both the ctxt and any rq_deferred depends on this. Fixes: 773f91b2cf3f ("SUNRPC: Fix NFSD's request deferral on RDMA transports") Signed-off-by: NeilBrown Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- include/linux/sunrpc/svc_rdma.h | 2 +- include/linux/sunrpc/svc_xprt.h | 2 +- net/sunrpc/svc_xprt.c | 23 +++++++++++++++++------ net/sunrpc/svcsock.c | 30 ++++++++++++++++-------------- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 11 +++++------ net/sunrpc/xprtrdma/svc_rdma_transport.c | 2 +- 6 files changed, 41 insertions(+), 29 deletions(-) diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 24aa159d29a7..fbc4bd423b35 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -176,7 +176,7 @@ extern struct svc_rdma_recv_ctxt * extern void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, struct svc_rdma_recv_ctxt *ctxt); extern void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma); -extern void svc_rdma_release_rqst(struct svc_rqst *rqstp); +extern void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *ctxt); extern int svc_rdma_recvfrom(struct svc_rqst *); /* svc_rdma_rw.c */ diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index d42a75b3be10..e882fe16a500 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h @@ -23,7 +23,7 @@ struct svc_xprt_ops { int (*xpo_sendto)(struct svc_rqst *); int (*xpo_result_payload)(struct svc_rqst *, unsigned int, unsigned int); - void (*xpo_release_rqst)(struct svc_rqst *); + void (*xpo_release_ctxt)(struct svc_xprt *xprt, void *ctxt); void (*xpo_detach)(struct svc_xprt *); void (*xpo_free)(struct svc_xprt *); void (*xpo_secure_port)(struct svc_rqst *rqstp); diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index b4306cf1b458..8117d0e08d5a 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -534,13 +534,23 @@ void svc_reserve(struct svc_rqst *rqstp, int space) } EXPORT_SYMBOL_GPL(svc_reserve); +static void free_deferred(struct svc_xprt *xprt, struct svc_deferred_req *dr) +{ + if (!dr) + return; + + xprt->xpt_ops->xpo_release_ctxt(xprt, dr->xprt_ctxt); + kfree(dr); +} + static void svc_xprt_release(struct svc_rqst *rqstp) { struct svc_xprt *xprt = rqstp->rq_xprt; - xprt->xpt_ops->xpo_release_rqst(rqstp); + xprt->xpt_ops->xpo_release_ctxt(xprt, rqstp->rq_xprt_ctxt); + rqstp->rq_xprt_ctxt = NULL; - kfree(rqstp->rq_deferred); + free_deferred(xprt, rqstp->rq_deferred); rqstp->rq_deferred = NULL; pagevec_release(&rqstp->rq_pvec); @@ -1059,7 +1069,7 @@ static void svc_delete_xprt(struct svc_xprt *xprt) spin_unlock_bh(&serv->sv_lock); while ((dr = svc_deferred_dequeue(xprt)) != NULL) - kfree(dr); + free_deferred(xprt, dr); call_xpt_users(xprt); svc_xprt_put(xprt); @@ -1181,8 +1191,8 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many) if (too_many || test_bit(XPT_DEAD, &xprt->xpt_flags)) { spin_unlock(&xprt->xpt_lock); trace_svc_defer_drop(dr); + free_deferred(xprt, dr); svc_xprt_put(xprt); - kfree(dr); return; } dr->xprt = NULL; @@ -1227,14 +1237,13 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req) dr->addrlen = rqstp->rq_addrlen; dr->daddr = rqstp->rq_daddr; dr->argslen = rqstp->rq_arg.len >> 2; - dr->xprt_ctxt = rqstp->rq_xprt_ctxt; /* back up head to the start of the buffer and copy */ skip = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len; memcpy(dr->args, rqstp->rq_arg.head[0].iov_base - skip, dr->argslen << 2); } - WARN_ON_ONCE(rqstp->rq_xprt_ctxt != dr->xprt_ctxt); + dr->xprt_ctxt = rqstp->rq_xprt_ctxt; rqstp->rq_xprt_ctxt = NULL; trace_svc_defer(rqstp); svc_xprt_get(rqstp->rq_xprt); @@ -1268,6 +1277,8 @@ static noinline int svc_deferred_recv(struct svc_rqst *rqstp) rqstp->rq_daddr = dr->daddr; rqstp->rq_respages = rqstp->rq_pages; rqstp->rq_xprt_ctxt = dr->xprt_ctxt; + + dr->xprt_ctxt = NULL; svc_xprt_received(rqstp->rq_xprt); return dr->argslen << 2; } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 815baf308236..7107fbcbff34 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -111,27 +111,27 @@ static void svc_reclassify_socket(struct socket *sock) #endif /** - * svc_tcp_release_rqst - Release transport-related resources - * @rqstp: request structure with resources to be released + * svc_tcp_release_ctxt - Release transport-related resources + * @xprt: the transport which owned the context + * @ctxt: the context from rqstp->rq_xprt_ctxt or dr->xprt_ctxt * */ -static void svc_tcp_release_rqst(struct svc_rqst *rqstp) +static void svc_tcp_release_ctxt(struct svc_xprt *xprt, void *ctxt) { } /** - * svc_udp_release_rqst - Release transport-related resources - * @rqstp: request structure with resources to be released + * svc_udp_release_ctxt - Release transport-related resources + * @xprt: the transport which owned the context + * @ctxt: the context from rqstp->rq_xprt_ctxt or dr->xprt_ctxt * */ -static void svc_udp_release_rqst(struct svc_rqst *rqstp) +static void svc_udp_release_ctxt(struct svc_xprt *xprt, void *ctxt) { - struct sk_buff *skb = rqstp->rq_xprt_ctxt; + struct sk_buff *skb = ctxt; - if (skb) { - rqstp->rq_xprt_ctxt = NULL; + if (skb) consume_skb(skb); - } } union svc_pktinfo_u { @@ -559,7 +559,8 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) unsigned int sent; int err; - svc_udp_release_rqst(rqstp); + svc_udp_release_ctxt(xprt, rqstp->rq_xprt_ctxt); + rqstp->rq_xprt_ctxt = NULL; svc_set_cmsg_data(rqstp, cmh); @@ -631,7 +632,7 @@ static const struct svc_xprt_ops svc_udp_ops = { .xpo_recvfrom = svc_udp_recvfrom, .xpo_sendto = svc_udp_sendto, .xpo_result_payload = svc_sock_result_payload, - .xpo_release_rqst = svc_udp_release_rqst, + .xpo_release_ctxt = svc_udp_release_ctxt, .xpo_detach = svc_sock_detach, .xpo_free = svc_sock_free, .xpo_has_wspace = svc_udp_has_wspace, @@ -1159,7 +1160,8 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp) unsigned int sent; int err; - svc_tcp_release_rqst(rqstp); + svc_tcp_release_ctxt(xprt, rqstp->rq_xprt_ctxt); + rqstp->rq_xprt_ctxt = NULL; atomic_inc(&svsk->sk_sendqlen); mutex_lock(&xprt->xpt_mutex); @@ -1204,7 +1206,7 @@ static const struct svc_xprt_ops svc_tcp_ops = { .xpo_recvfrom = svc_tcp_recvfrom, .xpo_sendto = svc_tcp_sendto, .xpo_result_payload = svc_sock_result_payload, - .xpo_release_rqst = svc_tcp_release_rqst, + .xpo_release_ctxt = svc_tcp_release_ctxt, .xpo_detach = svc_tcp_sock_detach, .xpo_free = svc_sock_free, .xpo_has_wspace = svc_tcp_has_wspace, diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 5242ad121450..53a7cb2f6c07 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -239,21 +239,20 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, } /** - * svc_rdma_release_rqst - Release transport-specific per-rqst resources - * @rqstp: svc_rqst being released + * svc_rdma_release_ctxt - Release transport-specific per-rqst resources + * @xprt: the transport which owned the context + * @vctxt: the context from rqstp->rq_xprt_ctxt or dr->xprt_ctxt * * Ensure that the recv_ctxt is released whether or not a Reply * was sent. For example, the client could close the connection, * or svc_process could drop an RPC, before the Reply is sent. */ -void svc_rdma_release_rqst(struct svc_rqst *rqstp) +void svc_rdma_release_ctxt(struct svc_xprt *xprt, void *vctxt) { - struct svc_rdma_recv_ctxt *ctxt = rqstp->rq_xprt_ctxt; - struct svc_xprt *xprt = rqstp->rq_xprt; + struct svc_rdma_recv_ctxt *ctxt = vctxt; struct svcxprt_rdma *rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); - rqstp->rq_xprt_ctxt = NULL; if (ctxt) svc_rdma_recv_ctxt_put(rdma, ctxt); } diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 94b20fb47135..f776f0cb471f 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -81,7 +81,7 @@ static const struct svc_xprt_ops svc_rdma_ops = { .xpo_recvfrom = svc_rdma_recvfrom, .xpo_sendto = svc_rdma_sendto, .xpo_result_payload = svc_rdma_result_payload, - .xpo_release_rqst = svc_rdma_release_rqst, + .xpo_release_ctxt = svc_rdma_release_ctxt, .xpo_detach = svc_rdma_detach, .xpo_free = svc_rdma_free, .xpo_has_wspace = svc_rdma_has_wspace, -- cgit 1.4.1 From da1b6989764d3cdf69ef82829b4f5428e29c3f87 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 14 May 2023 15:51:48 -0400 Subject: SUNRPC: Fix trace_svc_register() call site [ Upstream commit 07a27305938559fb35f7a46fb90a5e37728bdee6 ] The trace event recorded incorrect values for the registered family, protocol, and port because the arguments are in the wrong order. Fixes: b4af59328c25 ("SUNRPC: Trace server-side rpcbind registration events") Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- net/sunrpc/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 9ee32e06f877..9b0b21cccca9 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1007,7 +1007,7 @@ static int __svc_register(struct net *net, const char *progname, #endif } - trace_svc_register(progname, version, protocol, port, family, error); + trace_svc_register(progname, version, family, protocol, port, error); return error; } -- cgit 1.4.1 From 3e56a1c04882852e3e7d6c59756a16211ebbc457 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 11 May 2023 09:25:12 -0700 Subject: ASoC: mediatek: mt8186: Fix use-after-free in driver remove path [ Upstream commit a93d2afd3f77a7331271a0f25c6a11003db69b3c ] When devm runs function in the "remove" path for a device it runs them in the reverse order. That means that if you have parts of your driver that aren't using devm or are using "roll your own" devm w/ devm_add_action_or_reset() you need to keep that in mind. The mt8186 audio driver didn't quite get this right. Specifically, in mt8186_init_clock() it called mt8186_audsys_clk_register() and then went on to call a bunch of other devm function. The caller of mt8186_init_clock() used devm_add_action_or_reset() to call mt8186_deinit_clock() but, because of the intervening devm functions, the order was wrong. Specifically at probe time, the order was: 1. mt8186_audsys_clk_register() 2. afe_priv->clk = devm_kcalloc(...) 3. afe_priv->clk[i] = devm_clk_get(...) At remove time, the order (which should have been 3, 2, 1) was: 1. mt8186_audsys_clk_unregister() 3. Free all of afe_priv->clk[i] 2. Free afe_priv->clk The above seemed to be causing a use-after-free. Luckily, it's easy to fix this by simply using devm more correctly. Let's move the devm_add_action_or_reset() to the right place. In addition to fixing the use-after-free, code inspection shows that this fixes a leak (missing call to mt8186_audsys_clk_unregister()) that would have happened if any of the syscon_regmap_lookup_by_phandle() calls in mt8186_init_clock() had failed. Fixes: 55b423d5623c ("ASoC: mediatek: mt8186: support audio clock control in platform driver") Signed-off-by: Douglas Anderson --- sound/soc/mediatek/mt8186/mt8186-afe-clk.c | 6 ---- sound/soc/mediatek/mt8186/mt8186-afe-clk.h | 1 - sound/soc/mediatek/mt8186/mt8186-afe-pcm.c | 4 --- sound/soc/mediatek/mt8186/mt8186-audsys-clk.c | 46 ++++++++++++++------------- sound/soc/mediatek/mt8186/mt8186-audsys-clk.h | 1 - 5 files changed, 24 insertions(+), 34 deletions(-) diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c index a6b4f29049bb..539e3a023bc4 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c @@ -644,9 +644,3 @@ int mt8186_init_clock(struct mtk_base_afe *afe) return 0; } - -void mt8186_deinit_clock(void *priv) -{ - struct mtk_base_afe *afe = priv; - mt8186_audsys_clk_unregister(afe); -} diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.h b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h index d5988717d8f2..a9d59e506d9a 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.h +++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h @@ -81,7 +81,6 @@ enum { struct mtk_base_afe; int mt8186_set_audio_int_bus_parent(struct mtk_base_afe *afe, int clk_id); int mt8186_init_clock(struct mtk_base_afe *afe); -void mt8186_deinit_clock(void *priv); int mt8186_afe_enable_cgs(struct mtk_base_afe *afe); void mt8186_afe_disable_cgs(struct mtk_base_afe *afe); int mt8186_afe_enable_clock(struct mtk_base_afe *afe); diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c index d7e94e6a19c7..0e3792ccd49f 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c @@ -2847,10 +2847,6 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev) return ret; } - ret = devm_add_action_or_reset(dev, mt8186_deinit_clock, (void *)afe); - if (ret) - return ret; - /* init memif */ afe->memif_32bit_supported = 0; afe->memif_size = MT8186_MEMIF_NUM; diff --git a/sound/soc/mediatek/mt8186/mt8186-audsys-clk.c b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.c index 578969ca91c8..5666be6b1bd2 100644 --- a/sound/soc/mediatek/mt8186/mt8186-audsys-clk.c +++ b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.c @@ -84,6 +84,29 @@ static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = { GATE_AUD2(CLK_AUD_ETDM_OUT1_BCLK, "aud_etdm_out1_bclk", "top_audio", 24), }; +static void mt8186_audsys_clk_unregister(void *data) +{ + struct mtk_base_afe *afe = data; + struct mt8186_afe_private *afe_priv = afe->platform_priv; + struct clk *clk; + struct clk_lookup *cl; + int i; + + if (!afe_priv) + return; + + for (i = 0; i < CLK_AUD_NR_CLK; i++) { + cl = afe_priv->lookup[i]; + if (!cl) + continue; + + clk = cl->clk; + clk_unregister_gate(clk); + + clkdev_drop(cl); + } +} + int mt8186_audsys_clk_register(struct mtk_base_afe *afe) { struct mt8186_afe_private *afe_priv = afe->platform_priv; @@ -124,27 +147,6 @@ int mt8186_audsys_clk_register(struct mtk_base_afe *afe) afe_priv->lookup[i] = cl; } - return 0; + return devm_add_action_or_reset(afe->dev, mt8186_audsys_clk_unregister, afe); } -void mt8186_audsys_clk_unregister(struct mtk_base_afe *afe) -{ - struct mt8186_afe_private *afe_priv = afe->platform_priv; - struct clk *clk; - struct clk_lookup *cl; - int i; - - if (!afe_priv) - return; - - for (i = 0; i < CLK_AUD_NR_CLK; i++) { - cl = afe_priv->lookup[i]; - if (!cl) - continue; - - clk = cl->clk; - clk_unregister_gate(clk); - - clkdev_drop(cl); - } -} diff --git a/sound/soc/mediatek/mt8186/mt8186-audsys-clk.h b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.h index b8d6a06e11e8..897a2914dc19 100644 --- a/sound/soc/mediatek/mt8186/mt8186-audsys-clk.h +++ b/sound/soc/mediatek/mt8186/mt8186-audsys-clk.h @@ -10,6 +10,5 @@ #define _MT8186_AUDSYS_CLK_H_ int mt8186_audsys_clk_register(struct mtk_base_afe *afe); -void mt8186_audsys_clk_unregister(struct mtk_base_afe *afe); #endif -- cgit 1.4.1 From 866c78a3a99b19d431c2abc6d140d3806f4fe31e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 12 May 2023 14:46:30 +0300 Subject: ASoC: SOF: topology: Fix logic for copying tuples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 41c5305cc3d827d2ea686533777a285176ae01a0 ] Topology could have more instances of the tokens being searched for than the number of sets that need to be copied. Stop copying token after the limit of number of token instances has been reached. This worked before only by chance as we had allocated more size for the tuples array than the number of actual tokens being parsed. Fixes: 7006d20e5e9d ("ASoC: SOF: Introduce IPC3 ops") Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 6a0e7f3b5023..872e44408298 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -545,6 +545,10 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ if (*num_copied_tuples == tuples_size) return 0; } + + /* stop when we've found the required token instances */ + if (found == num_tokens * token_instance_num) + return 0; } /* next array */ -- cgit 1.4.1 From 82ede435440e59064bcd0bf44789fab11ad53bfc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 17 Apr 2023 23:04:11 +0200 Subject: drm/exynos: fix g2d_open/close helper function definitions [ Upstream commit 2ef0785b30bd6549ddbc124979f1b6596e065ae2 ] The empty stub functions are defined as global functions, which causes a warning because of missing prototypes: drivers/gpu/drm/exynos/exynos_drm_g2d.h:37:5: error: no previous prototype for 'g2d_open' drivers/gpu/drm/exynos/exynos_drm_g2d.h:42:5: error: no previous prototype for 'g2d_close' Mark them as 'static inline' to avoid the warning and to make them behave as intended. Fixes: eb4d9796fa34 ("drm/exynos: g2d: Convert to driver component API") Signed-off-by: Arnd Bergmann Reviewed-by: Andi Shyti Signed-off-by: Inki Dae Signed-off-by: Sasha Levin --- drivers/gpu/drm/exynos/exynos_drm_g2d.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.h b/drivers/gpu/drm/exynos/exynos_drm_g2d.h index 74ea3c26dead..1a5ae781b56c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.h +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.h @@ -34,11 +34,11 @@ static inline int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, return -ENODEV; } -int g2d_open(struct drm_device *drm_dev, struct drm_file *file) +static inline int g2d_open(struct drm_device *drm_dev, struct drm_file *file) { return 0; } -void g2d_close(struct drm_device *drm_dev, struct drm_file *file) +static inline void g2d_close(struct drm_device *drm_dev, struct drm_file *file) { } #endif -- cgit 1.4.1 From 6fbedf987b6b8ed54a50e2205d998eb2c8be72f9 Mon Sep 17 00:00:00 2001 From: Dong Chenchen Date: Thu, 11 May 2023 20:54:40 +0800 Subject: net: nsh: Use correct mac_offset to unwind gso skb in nsh_gso_segment() [ Upstream commit c83b49383b595be50647f0c764a48c78b5f3c4f8 ] As the call trace shows, skb_panic was caused by wrong skb->mac_header in nsh_gso_segment(): invalid opcode: 0000 [#1] PREEMPT SMP KASAN PTI CPU: 3 PID: 2737 Comm: syz Not tainted 6.3.0-next-20230505 #1 RIP: 0010:skb_panic+0xda/0xe0 call Trace: skb_push+0x91/0xa0 nsh_gso_segment+0x4f3/0x570 skb_mac_gso_segment+0x19e/0x270 __skb_gso_segment+0x1e8/0x3c0 validate_xmit_skb+0x452/0x890 validate_xmit_skb_list+0x99/0xd0 sch_direct_xmit+0x294/0x7c0 __dev_queue_xmit+0x16f0/0x1d70 packet_xmit+0x185/0x210 packet_snd+0xc15/0x1170 packet_sendmsg+0x7b/0xa0 sock_sendmsg+0x14f/0x160 The root cause is: nsh_gso_segment() use skb->network_header - nhoff to reset mac_header in skb_gso_error_unwind() if inner-layer protocol gso fails. However, skb->network_header may be reset by inner-layer protocol gso function e.g. mpls_gso_segment. skb->mac_header reset by the inaccurate network_header will be larger than skb headroom. nsh_gso_segment nhoff = skb->network_header - skb->mac_header; __skb_pull(skb,nsh_len) skb_mac_gso_segment mpls_gso_segment skb_reset_network_header(skb);//skb->network_header+=nsh_len return -EINVAL; skb_gso_error_unwind skb_push(skb, nsh_len); skb->mac_header = skb->network_header - nhoff; // skb->mac_header > skb->headroom, cause skb_push panic Use correct mac_offset to restore mac_header and get rid of nhoff. Fixes: c411ed854584 ("nsh: add GSO support") Reported-by: syzbot+632b5d9964208bfef8c0@syzkaller.appspotmail.com Suggested-by: Eric Dumazet Signed-off-by: Dong Chenchen Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/nsh/nsh.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/nsh/nsh.c b/net/nsh/nsh.c index e9ca007718b7..0f23e5e8e03e 100644 --- a/net/nsh/nsh.c +++ b/net/nsh/nsh.c @@ -77,13 +77,12 @@ static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); + u16 mac_offset = skb->mac_header; unsigned int nsh_len, mac_len; __be16 proto; - int nhoff; skb_reset_network_header(skb); - nhoff = skb->network_header - skb->mac_header; mac_len = skb->mac_len; if (unlikely(!pskb_may_pull(skb, NSH_BASE_HDR_LEN))) @@ -108,15 +107,14 @@ static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, segs = skb_mac_gso_segment(skb, features); if (IS_ERR_OR_NULL(segs)) { skb_gso_error_unwind(skb, htons(ETH_P_NSH), nsh_len, - skb->network_header - nhoff, - mac_len); + mac_offset, mac_len); goto out; } for (skb = segs; skb; skb = skb->next) { skb->protocol = htons(ETH_P_NSH); __skb_push(skb, nsh_len); - skb_set_mac_header(skb, -nhoff); + skb->mac_header = mac_offset; skb->network_header = skb->mac_header + mac_len; skb->mac_len = mac_len; } -- cgit 1.4.1 From 978a55b0c096c374edfa73881b502052c5fe8e75 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Fri, 3 Feb 2023 15:37:38 +0200 Subject: virtio-net: Maintain reverse cleanup order [ Upstream commit 27369c9c2b722617063d6b80c758ab153f1d95d4 ] To easily audit the code, better to keep the device stop() sequence to be mirror of the device open() sequence. Acked-by: Michael S. Tsirkin Reviewed-by: Jiri Pirko Signed-off-by: Parav Pandit Signed-off-by: David S. Miller Stable-dep-of: 5306623a9826 ("virtio_net: Fix error unwinding of XDP initialization") Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 9a612b13b4e4..08a23ba3d68a 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2158,9 +2158,9 @@ static int virtnet_close(struct net_device *dev) cancel_delayed_work_sync(&vi->refill); for (i = 0; i < vi->max_queue_pairs; i++) { + virtnet_napi_tx_disable(&vi->sq[i].napi); napi_disable(&vi->rq[i].napi); xdp_rxq_info_unreg(&vi->rq[i].xdp_rxq); - virtnet_napi_tx_disable(&vi->sq[i].napi); } return 0; -- cgit 1.4.1 From 73f53bc295727a3cdbd9d6bcdfaa239258970cf4 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Fri, 12 May 2023 11:18:12 -0400 Subject: virtio_net: Fix error unwinding of XDP initialization [ Upstream commit 5306623a9826aa7d63b32c6a3803c798a765474d ] When initializing XDP in virtnet_open(), some rq xdp initialization may hit an error causing net device open failed. However, previous rqs have already initialized XDP and enabled NAPI, which is not the expected behavior. Need to roll back the previous rq initialization to avoid leaks in error unwinding of init code. Also extract helper functions of disable and enable queue pairs. Use newly introduced disable helper function in error unwinding and virtnet_close. Use enable helper function in virtnet_open. Fixes: 754b8a21a96d ("virtio_net: setup xdp_rxq_info") Signed-off-by: Feng Liu Reviewed-by: Jiri Pirko Reviewed-by: William Tu Acked-by: Michael S. Tsirkin Acked-by: Jason Wang Reviewed-by: Xuan Zhuo Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 08a23ba3d68a..47788f093551 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1697,6 +1697,38 @@ static int virtnet_poll(struct napi_struct *napi, int budget) return received; } +static void virtnet_disable_queue_pair(struct virtnet_info *vi, int qp_index) +{ + virtnet_napi_tx_disable(&vi->sq[qp_index].napi); + napi_disable(&vi->rq[qp_index].napi); + xdp_rxq_info_unreg(&vi->rq[qp_index].xdp_rxq); +} + +static int virtnet_enable_queue_pair(struct virtnet_info *vi, int qp_index) +{ + struct net_device *dev = vi->dev; + int err; + + err = xdp_rxq_info_reg(&vi->rq[qp_index].xdp_rxq, dev, qp_index, + vi->rq[qp_index].napi.napi_id); + if (err < 0) + return err; + + err = xdp_rxq_info_reg_mem_model(&vi->rq[qp_index].xdp_rxq, + MEM_TYPE_PAGE_SHARED, NULL); + if (err < 0) + goto err_xdp_reg_mem_model; + + virtnet_napi_enable(vi->rq[qp_index].vq, &vi->rq[qp_index].napi); + virtnet_napi_tx_enable(vi, vi->sq[qp_index].vq, &vi->sq[qp_index].napi); + + return 0; + +err_xdp_reg_mem_model: + xdp_rxq_info_unreg(&vi->rq[qp_index].xdp_rxq); + return err; +} + static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); @@ -1710,22 +1742,20 @@ static int virtnet_open(struct net_device *dev) if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) schedule_delayed_work(&vi->refill, 0); - err = xdp_rxq_info_reg(&vi->rq[i].xdp_rxq, dev, i, vi->rq[i].napi.napi_id); + err = virtnet_enable_queue_pair(vi, i); if (err < 0) - return err; - - err = xdp_rxq_info_reg_mem_model(&vi->rq[i].xdp_rxq, - MEM_TYPE_PAGE_SHARED, NULL); - if (err < 0) { - xdp_rxq_info_unreg(&vi->rq[i].xdp_rxq); - return err; - } - - virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); - virtnet_napi_tx_enable(vi, vi->sq[i].vq, &vi->sq[i].napi); + goto err_enable_qp; } return 0; + +err_enable_qp: + disable_delayed_refill(vi); + cancel_delayed_work_sync(&vi->refill); + + for (i--; i >= 0; i--) + virtnet_disable_queue_pair(vi, i); + return err; } static int virtnet_poll_tx(struct napi_struct *napi, int budget) @@ -2157,11 +2187,8 @@ static int virtnet_close(struct net_device *dev) /* Make sure refill_work doesn't re-enable napi! */ cancel_delayed_work_sync(&vi->refill); - for (i = 0; i < vi->max_queue_pairs; i++) { - virtnet_napi_tx_disable(&vi->sq[i].napi); - napi_disable(&vi->rq[i].napi); - xdp_rxq_info_unreg(&vi->rq[i].xdp_rxq); - } + for (i = 0; i < vi->max_queue_pairs; i++) + virtnet_disable_queue_pair(vi, i); return 0; } -- cgit 1.4.1 From 735c64ea8802633864643e5c6fa1013f93ff211e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 14 May 2023 15:52:27 -0400 Subject: tipc: add tipc_bearer_min_mtu to calculate min mtu [ Upstream commit 3ae6d66b605be604644d4bb5708a7ffd9cf1abe8 ] As different media may requires different min mtu, and even the same media with different net family requires different min mtu, add tipc_bearer_min_mtu() to calculate min mtu accordingly. This API will be used to check the new mtu when doing the link mtu negotiation in the next patch. Signed-off-by: Xin Long Acked-by: Jon Maloy Signed-off-by: David S. Miller Stable-dep-of: 56077b56cd3f ("tipc: do not update mtu if msg_max is too small in mtu negotiation") Signed-off-by: Sasha Levin --- net/tipc/bearer.c | 13 +++++++++++++ net/tipc/bearer.h | 3 +++ net/tipc/udp_media.c | 5 +++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 35cac7733fd3..0e9a29e1536b 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -541,6 +541,19 @@ int tipc_bearer_mtu(struct net *net, u32 bearer_id) return mtu; } +int tipc_bearer_min_mtu(struct net *net, u32 bearer_id) +{ + int mtu = TIPC_MIN_BEARER_MTU; + struct tipc_bearer *b; + + rcu_read_lock(); + b = bearer_get(net, bearer_id); + if (b) + mtu += b->encap_hlen; + rcu_read_unlock(); + return mtu; +} + /* tipc_bearer_xmit_skb - sends buffer to destination over bearer */ void tipc_bearer_xmit_skb(struct net *net, u32 bearer_id, diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 490ad6e5f7a3..bd0cc5c287ef 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -146,6 +146,7 @@ struct tipc_media { * @identity: array index of this bearer within TIPC bearer array * @disc: ptr to link setup request * @net_plane: network plane ('A' through 'H') currently associated with bearer + * @encap_hlen: encap headers length * @up: bearer up flag (bit 0) * @refcnt: tipc_bearer reference counter * @@ -170,6 +171,7 @@ struct tipc_bearer { u32 identity; struct tipc_discoverer *disc; char net_plane; + u16 encap_hlen; unsigned long up; refcount_t refcnt; }; @@ -232,6 +234,7 @@ int tipc_bearer_setup(void); void tipc_bearer_cleanup(void); void tipc_bearer_stop(struct net *net); int tipc_bearer_mtu(struct net *net, u32 bearer_id); +int tipc_bearer_min_mtu(struct net *net, u32 bearer_id); bool tipc_bearer_bcast_support(struct net *net, u32 bearer_id); void tipc_bearer_xmit_skb(struct net *net, u32 bearer_id, struct sk_buff *skb, diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index c2bb818704c8..0a85244fd618 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -738,8 +738,8 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, udp_conf.local_ip.s_addr = local.ipv4.s_addr; udp_conf.use_udp_checksums = false; ub->ifindex = dev->ifindex; - if (tipc_mtu_bad(dev, sizeof(struct iphdr) + - sizeof(struct udphdr))) { + b->encap_hlen = sizeof(struct iphdr) + sizeof(struct udphdr); + if (tipc_mtu_bad(dev, b->encap_hlen)) { err = -EINVAL; goto err; } @@ -760,6 +760,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, else udp_conf.local_ip6 = local.ipv6; ub->ifindex = dev->ifindex; + b->encap_hlen = sizeof(struct ipv6hdr) + sizeof(struct udphdr); b->mtu = 1280; #endif } else { -- cgit 1.4.1 From 259683001d7e879fea4b42084fb6560dd9408a7e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 14 May 2023 15:52:28 -0400 Subject: tipc: do not update mtu if msg_max is too small in mtu negotiation [ Upstream commit 56077b56cd3fb78e1c8619e29581ba25a5c55e86 ] When doing link mtu negotiation, a malicious peer may send Activate msg with a very small mtu, e.g. 4 in Shuang's testing, without checking for the minimum mtu, l->mtu will be set to 4 in tipc_link_proto_rcv(), then n->links[bearer_id].mtu is set to 4294967228, which is a overflow of '4 - INT_H_SIZE - EMSG_OVERHEAD' in tipc_link_mss(). With tipc_link.mtu = 4, tipc_link_xmit() kept printing the warning: tipc: Too large msg, purging xmit list 1 5 0 40 4! tipc: Too large msg, purging xmit list 1 15 0 60 4! And with tipc_link_entry.mtu 4294967228, a huge skb was allocated in named_distribute(), and when purging it in tipc_link_xmit(), a crash was even caused: general protection fault, probably for non-canonical address 0x2100001011000dd: 0000 [#1] PREEMPT SMP PTI CPU: 0 PID: 0 Comm: swapper/0 Kdump: loaded Not tainted 6.3.0.neta #19 RIP: 0010:kfree_skb_list_reason+0x7e/0x1f0 Call Trace: skb_release_data+0xf9/0x1d0 kfree_skb_reason+0x40/0x100 tipc_link_xmit+0x57a/0x740 [tipc] tipc_node_xmit+0x16c/0x5c0 [tipc] tipc_named_node_up+0x27f/0x2c0 [tipc] tipc_node_write_unlock+0x149/0x170 [tipc] tipc_rcv+0x608/0x740 [tipc] tipc_udp_recv+0xdc/0x1f0 [tipc] udp_queue_rcv_one_skb+0x33e/0x620 udp_unicast_rcv_skb.isra.72+0x75/0x90 __udp4_lib_rcv+0x56d/0xc20 ip_protocol_deliver_rcu+0x100/0x2d0 This patch fixes it by checking the new mtu against tipc_bearer_min_mtu(), and not updating mtu if it is too small. Fixes: ed193ece2649 ("tipc: simplify link mtu negotiation") Reported-by: Shuang Li Signed-off-by: Xin Long Acked-by: Jon Maloy Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/tipc/link.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/tipc/link.c b/net/tipc/link.c index b3ce24823f50..2eff1c7949cb 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -2200,7 +2200,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, struct tipc_msg *hdr = buf_msg(skb); struct tipc_gap_ack_blks *ga = NULL; bool reply = msg_probe(hdr), retransmitted = false; - u32 dlen = msg_data_sz(hdr), glen = 0; + u32 dlen = msg_data_sz(hdr), glen = 0, msg_max; u16 peers_snd_nxt = msg_next_sent(hdr); u16 peers_tol = msg_link_tolerance(hdr); u16 peers_prio = msg_linkprio(hdr); @@ -2239,6 +2239,9 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, switch (mtyp) { case RESET_MSG: case ACTIVATE_MSG: + msg_max = msg_max_pkt(hdr); + if (msg_max < tipc_bearer_min_mtu(l->net, l->bearer_id)) + break; /* Complete own link name with peer's interface name */ if_name = strrchr(l->name, ':') + 1; if (sizeof(l->name) - (if_name - l->name) <= TIPC_MAX_IF_NAME) @@ -2283,8 +2286,8 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, l->peer_session = msg_session(hdr); l->in_session = true; l->peer_bearer_id = msg_bearer_id(hdr); - if (l->mtu > msg_max_pkt(hdr)) - l->mtu = msg_max_pkt(hdr); + if (l->mtu > msg_max) + l->mtu = msg_max; break; case STATE_MSG: -- cgit 1.4.1 From f215b62f59be081dd77201273128d44a0c573075 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 14 May 2023 15:52:29 -0400 Subject: tipc: check the bearer min mtu properly when setting it by netlink [ Upstream commit 35a089b5d793d2bfd2cc7cfa6104545184de2ce7 ] Checking the bearer min mtu with tipc_udp_mtu_bad() only works for IPv4 UDP bearer, and IPv6 UDP bearer has a different value for the min mtu. This patch checks with encap_hlen + TIPC_MIN_BEARER_MTU for min mtu, which works for both IPv4 and IPv6 UDP bearer. Note that tipc_udp_mtu_bad() is still used to check media min mtu in __tipc_nl_media_set(), as m->mtu currently is only used by the IPv4 UDP bearer as its default mtu value. Fixes: 682cd3cf946b ("tipc: confgiure and apply UDP bearer MTU on running links") Signed-off-by: Xin Long Acked-by: Jon Maloy Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/tipc/bearer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 0e9a29e1536b..53881406e200 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -1151,8 +1151,8 @@ int __tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } #ifdef CONFIG_TIPC_MEDIA_UDP - if (tipc_udp_mtu_bad(nla_get_u32 - (props[TIPC_NLA_PROP_MTU]))) { + if (nla_get_u32(props[TIPC_NLA_PROP_MTU]) < + b->encap_hlen + TIPC_MIN_BEARER_MTU) { NL_SET_ERR_MSG(info->extack, "MTU value is out-of-range"); return -EINVAL; -- cgit 1.4.1 From c9abef1e07ff38f7ea3dfb62f141e111c54aa7e2 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Tue, 2 May 2023 11:12:42 +0200 Subject: s390/cio: include subchannels without devices also for evaluation [ Upstream commit b1b0d5aec1cf9f9a900a14964f869c68688d923e ] Currently when the new channel-path is enabled, we do evaluation only on the subchannels with a device connected on it. This is because, in the past, if the device in the subchannel is not working or not available, we used to unregister the subchannels. But, from the 'commit 2297791c92d0 ("s390/cio: dont unregister subchannel from child-drivers")' we allow subchannels with or without an active device connected on it. So, when we do the io_subchannel_verify, make sure that, we are evaluating the subchannels without any device too. Fixes: 2297791c92d0 ("s390/cio: dont unregister subchannel from child-drivers") Reported-by: Boris Fiuczynski Signed-off-by: Vineeth Vijayan Reviewed-by: Peter Oberparleiter Signed-off-by: Alexander Gordeev Signed-off-by: Sasha Levin --- drivers/s390/cio/device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 3b1cd0c96a74..ba4c69226c33 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1102,6 +1102,8 @@ static void io_subchannel_verify(struct subchannel *sch) cdev = sch_get_cdev(sch); if (cdev) dev_fsm_event(cdev, DEV_EVENT_VERIFY); + else + css_schedule_eval(sch->schid); } static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) -- cgit 1.4.1 From e90cefcffd731b4d06f1e4ac9499d9023ede6b29 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sat, 6 May 2023 20:45:15 +0200 Subject: can: dev: fix missing CAN XL support in can_put_echo_skb() [ Upstream commit 6bffdc38f9935bae49f980448f3f6be2dada0564 ] can_put_echo_skb() checks for the enabled IFF_ECHO flag and the correct ETH_P type of the given skbuff. When implementing the CAN XL support the new check for ETH_P_CANXL has been forgotten. Fixes: fb08cba12b52 ("can: canxl: update CAN infrastructure for CAN XL frames") Signed-off-by: Oliver Hartkopp Link: https://lore.kernel.org/all/20230506184515.39241-1-socketcan@hartkopp.net Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- drivers/net/can/dev/skb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index 241ec636e91f..f6d05b3ef59a 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -54,7 +54,8 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, /* check flag whether this packet has to be looped back */ if (!(dev->flags & IFF_ECHO) || (skb->protocol != htons(ETH_P_CAN) && - skb->protocol != htons(ETH_P_CANFD))) { + skb->protocol != htons(ETH_P_CANFD) && + skb->protocol != htons(ETH_P_CANXL))) { kfree_skb(skb); return 0; } -- cgit 1.4.1 From 41357a52b83e151ac7a938e4476b66a1a0f1e2f5 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 4 May 2023 16:07:27 -0700 Subject: net: bcmgenet: Remove phy_stop() from bcmgenet_netif_stop() [ Upstream commit 93e0401e0fc0c54b0ac05b687cd135c2ac38187c ] The call to phy_stop() races with the later call to phy_disconnect(), resulting in concurrent phy_suspend() calls being run from different CPUs. The final call to phy_disconnect() ensures that the PHY is stopped and suspended, too. Fixes: c96e731c93ff ("net: bcmgenet: connect and disconnect from the PHY state machine") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index f679ed54b3ef..9860fd66f3bc 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -3460,7 +3460,6 @@ static void bcmgenet_netif_stop(struct net_device *dev) /* Disable MAC transmit. TX DMA disabled must be done before this */ umac_enable_set(priv, CMD_TX_EN, false); - phy_stop(dev->phydev); bcmgenet_disable_rx_napi(priv); bcmgenet_intr_disable(priv); -- cgit 1.4.1 From 36e6c7ada5ed37da3b17a5783028bfaaf39a0c82 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 14 May 2023 19:56:07 -0700 Subject: net: bcmgenet: Restore phy_stop() depending upon suspend/close [ Upstream commit 225c657945c4a6307741cb3cc89467eadcc26e9b ] Removing the phy_stop() from bcmgenet_netif_stop() ended up causing warnings from the PHY library that phy_start() is called from the RUNNING state since we are no longer stopping the PHY state machine during bcmgenet_suspend(). Restore the call to phy_stop() but make it conditional on being called from the close or suspend path. Fixes: c96e731c93ff ("net: bcmgenet: connect and disconnect from the PHY state machine") Fixes: 93e0401e0fc0 ("net: bcmgenet: Remove phy_stop() from bcmgenet_netif_stop()") Signed-off-by: Florian Fainelli Reviewed-by: Pavan Chebbi Link: https://lore.kernel.org/r/20230515025608.2587012-1-f.fainelli@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 9860fd66f3bc..4da2becfa950 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -3445,7 +3445,7 @@ err_clk_disable: return ret; } -static void bcmgenet_netif_stop(struct net_device *dev) +static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy) { struct bcmgenet_priv *priv = netdev_priv(dev); @@ -3460,6 +3460,8 @@ static void bcmgenet_netif_stop(struct net_device *dev) /* Disable MAC transmit. TX DMA disabled must be done before this */ umac_enable_set(priv, CMD_TX_EN, false); + if (stop_phy) + phy_stop(dev->phydev); bcmgenet_disable_rx_napi(priv); bcmgenet_intr_disable(priv); @@ -3480,7 +3482,7 @@ static int bcmgenet_close(struct net_device *dev) netif_dbg(priv, ifdown, dev, "bcmgenet_close\n"); - bcmgenet_netif_stop(dev); + bcmgenet_netif_stop(dev, false); /* Really kill the PHY state machine and disconnect from it */ phy_disconnect(dev->phydev); @@ -4298,7 +4300,7 @@ static int bcmgenet_suspend(struct device *d) netif_device_detach(dev); - bcmgenet_netif_stop(dev); + bcmgenet_netif_stop(dev, true); if (!device_may_wakeup(d)) phy_suspend(dev->phydev); -- cgit 1.4.1 From f181d799fb5336024a4e040ad6fa84b9d0bbd18a Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 18 Jan 2023 17:16:51 -0800 Subject: ice: introduce clear_reset_state operation [ Upstream commit fa4a15c85c849e92257da6dbffeb1e3a6399fd7b ] When hardware is reset, the VF relies on the VFGEN_RSTAT register to detect when the VF is finished resetting. This is a tri-state register where 0 indicates a reset is in progress, 1 indicates the hardware is done resetting, and 2 indicates that the software is done resetting. Currently the PF driver relies on the device hardware resetting VFGEN_RSTAT when a global reset occurs. This works ok, but it does mean that the VF might not immediately notice a reset when the driver first detects that the global reset is occurring. This is also problematic for Scalable IOV, because there is no read/write equivalent VFGEN_RSTAT register for the Scalable VSI type. Instead, the Scalable IOV VFs will need to emulate this register. To support this, introduce a new VF operation, clear_reset_state, which is called when the PF driver first detects a global reset. The Single Root IOV implementation can just write to VFGEN_RSTAT to ensure it's cleared immediately, without waiting for the actual hardware reset to begin. The Scalable IOV implementation will use this as part of its tracking of the reset status to allow properly reporting the emulated VFGEN_RSTAT to the VF driver. Signed-off-by: Jacob Keller Reviewed-by: Paul Menzel Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen Stable-dep-of: 7255355a0636 ("ice: Fix ice VF reset during iavf initialization") Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_main.c | 2 +- drivers/net/ethernet/intel/ice/ice_sriov.c | 16 ++++++++++++++++ drivers/net/ethernet/intel/ice/ice_vf_lib.c | 12 +++++++++++- drivers/net/ethernet/intel/ice/ice_vf_lib.h | 5 +++-- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index cfc57cfc46e4..6a50f8ba3940 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -573,7 +573,7 @@ ice_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type) /* Disable VFs until reset is completed */ mutex_lock(&pf->vfs.table_lock); ice_for_each_vf(pf, bkt, vf) - ice_set_vf_state_qs_dis(vf); + ice_set_vf_state_dis(vf); mutex_unlock(&pf->vfs.table_lock); if (ice_is_eswitch_mode_switchdev(pf)) { diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index b3849bc3d4fc..b719e9a771e3 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -696,6 +696,21 @@ static void ice_sriov_free_vf(struct ice_vf *vf) kfree_rcu(vf, rcu); } +/** + * ice_sriov_clear_reset_state - clears VF Reset status register + * @vf: the vf to configure + */ +static void ice_sriov_clear_reset_state(struct ice_vf *vf) +{ + struct ice_hw *hw = &vf->pf->hw; + + /* Clear the reset status register so that VF immediately sees that + * the device is resetting, even if hardware hasn't yet gotten around + * to clearing VFGEN_RSTAT for us. + */ + wr32(hw, VFGEN_RSTAT(vf->vf_id), VIRTCHNL_VFR_INPROGRESS); +} + /** * ice_sriov_clear_mbx_register - clears SRIOV VF's mailbox registers * @vf: the vf to configure @@ -835,6 +850,7 @@ static void ice_sriov_post_vsi_rebuild(struct ice_vf *vf) static const struct ice_vf_ops ice_sriov_vf_ops = { .reset_type = ICE_VF_RESET, .free = ice_sriov_free_vf, + .clear_reset_state = ice_sriov_clear_reset_state, .clear_mbx_register = ice_sriov_clear_mbx_register, .trigger_reset_register = ice_sriov_trigger_reset_register, .poll_reset_status = ice_sriov_poll_reset_status, diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 1c51778db951..86abbcb480d9 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -673,7 +673,7 @@ out_unlock: * ice_set_vf_state_qs_dis - Set VF queues state to disabled * @vf: pointer to the VF structure */ -void ice_set_vf_state_qs_dis(struct ice_vf *vf) +static void ice_set_vf_state_qs_dis(struct ice_vf *vf) { /* Clear Rx/Tx enabled queues flag */ bitmap_zero(vf->txq_ena, ICE_MAX_RSS_QS_PER_VF); @@ -681,6 +681,16 @@ void ice_set_vf_state_qs_dis(struct ice_vf *vf) clear_bit(ICE_VF_STATE_QS_ENA, vf->vf_states); } +/** + * ice_set_vf_state_dis - Set VF state to disabled + * @vf: pointer to the VF structure + */ +void ice_set_vf_state_dis(struct ice_vf *vf) +{ + ice_set_vf_state_qs_dis(vf); + vf->vf_ops->clear_reset_state(vf); +} + /* Private functions only accessed from other virtualization files */ /** diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h index 52bd9a3816bf..9f7fcd8e5714 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h @@ -56,6 +56,7 @@ struct ice_mdd_vf_events { struct ice_vf_ops { enum ice_disq_rst_src reset_type; void (*free)(struct ice_vf *vf); + void (*clear_reset_state)(struct ice_vf *vf); void (*clear_mbx_register)(struct ice_vf *vf); void (*trigger_reset_register)(struct ice_vf *vf, bool is_vflr); bool (*poll_reset_status)(struct ice_vf *vf); @@ -213,7 +214,7 @@ u16 ice_get_num_vfs(struct ice_pf *pf); struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf); bool ice_is_vf_disabled(struct ice_vf *vf); int ice_check_vf_ready_for_cfg(struct ice_vf *vf); -void ice_set_vf_state_qs_dis(struct ice_vf *vf); +void ice_set_vf_state_dis(struct ice_vf *vf); bool ice_is_any_vf_in_unicast_promisc(struct ice_pf *pf); void ice_vf_get_promisc_masks(struct ice_vf *vf, struct ice_vsi *vsi, @@ -259,7 +260,7 @@ static inline int ice_check_vf_ready_for_cfg(struct ice_vf *vf) return -EOPNOTSUPP; } -static inline void ice_set_vf_state_qs_dis(struct ice_vf *vf) +static inline void ice_set_vf_state_dis(struct ice_vf *vf) { } -- cgit 1.4.1 From dda9c9b117a2eb43239a58beb770ac9c974957c3 Mon Sep 17 00:00:00 2001 From: Dawid Wesierski Date: Tue, 18 Apr 2023 11:52:55 +0200 Subject: ice: Fix ice VF reset during iavf initialization [ Upstream commit 7255355a0636b4eff08d5e8139c77d98f151c4fc ] Fix the current implementation that causes ice_trigger_vf_reset() to start resetting the VF even when the VF-NIC is still initializing. When we reset NIC with ice driver it can interfere with iavf-vf initialization e.g. during consecutive resets induced by ice iavf ice | | |<-----------------| | ice resets vf iavf | reset | start | |<-----------------| | ice resets vf | causing iavf | initialization | error | | iavf reset end This leads to a series of -53 errors (failed to init adminq) from the IAVF. Change the state of the vf_state field to be not active when the IAVF is still initializing. Make sure to wait until receiving the message on the message box to ensure that the vf is ready and initializded. In simple terms we use the ACTIVE flag to make sure that the ice driver knows if the iavf is ready for another reset iavf ice | | | | |<------------- ice resets vf iavf vf_state != ACTIVE reset | start | | | | | iavf | reset-------> vf_state == ACTIVE end ice resets vf | | | | Fixes: c54d209c78b8 ("ice: Wait for VF to be reset/ready before configuration") Signed-off-by: Dawid Wesierski Signed-off-by: Kamil Maziarz Acked-by: Jacob Keller Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_sriov.c | 8 ++++---- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 19 +++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_vf_lib.h | 1 + drivers/net/ethernet/intel/ice/ice_virtchnl.c | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index b719e9a771e3..b8c31bf721ad 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1240,7 +1240,7 @@ int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena) if (!vf) return -EINVAL; - ret = ice_check_vf_ready_for_cfg(vf); + ret = ice_check_vf_ready_for_reset(vf); if (ret) goto out_put_vf; @@ -1355,7 +1355,7 @@ int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) goto out_put_vf; } - ret = ice_check_vf_ready_for_cfg(vf); + ret = ice_check_vf_ready_for_reset(vf); if (ret) goto out_put_vf; @@ -1409,7 +1409,7 @@ int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted) return -EOPNOTSUPP; } - ret = ice_check_vf_ready_for_cfg(vf); + ret = ice_check_vf_ready_for_reset(vf); if (ret) goto out_put_vf; @@ -1722,7 +1722,7 @@ ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, if (!vf) return -EINVAL; - ret = ice_check_vf_ready_for_cfg(vf); + ret = ice_check_vf_ready_for_reset(vf); if (ret) goto out_put_vf; diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 86abbcb480d9..71047fc34139 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -185,6 +185,25 @@ int ice_check_vf_ready_for_cfg(struct ice_vf *vf) return 0; } +/** + * ice_check_vf_ready_for_reset - check if VF is ready to be reset + * @vf: VF to check if it's ready to be reset + * + * The purpose of this function is to ensure that the VF is not in reset, + * disabled, and is both initialized and active, thus enabling us to safely + * initialize another reset. + */ +int ice_check_vf_ready_for_reset(struct ice_vf *vf) +{ + int ret; + + ret = ice_check_vf_ready_for_cfg(vf); + if (!ret && !test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) + ret = -EAGAIN; + + return ret; +} + /** * ice_trigger_vf_reset - Reset a VF on HW * @vf: pointer to the VF structure diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h index 9f7fcd8e5714..e5bed8572462 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h @@ -214,6 +214,7 @@ u16 ice_get_num_vfs(struct ice_pf *pf); struct ice_vsi *ice_get_vf_vsi(struct ice_vf *vf); bool ice_is_vf_disabled(struct ice_vf *vf); int ice_check_vf_ready_for_cfg(struct ice_vf *vf); +int ice_check_vf_ready_for_reset(struct ice_vf *vf); void ice_set_vf_state_dis(struct ice_vf *vf); bool ice_is_any_vf_in_unicast_promisc(struct ice_pf *pf); void diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index 2b4c791b6cba..ef3c709d6a75 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -3722,6 +3722,7 @@ error_handler: ice_vc_notify_vf_link_state(vf); break; case VIRTCHNL_OP_RESET_VF: + clear_bit(ICE_VF_STATE_ACTIVE, vf->vf_states); ops->reset_vf(vf); break; case VIRTCHNL_OP_ADD_ETH_ADDR: -- cgit 1.4.1 From f9a85347e4d167af3972c3e9ba7e096ee12b09c3 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 24 Apr 2023 10:32:24 +0300 Subject: wifi: cfg80211: Drop entries with invalid BSSIDs in RNR [ Upstream commit 1b6b4ed01493b7ea2205ab83c49198f7d13ca9d2 ] Ignore AP information for entries that include an invalid BSSID in the TBTT information field, e.g., all zeros BSSIDs. Fixes: c8cb5b854b40 ("nl80211/cfg80211: support 6 GHz scanning") Signed-off-by: Ilan Peer Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230424103224.5e65d04d1448.Ic10c8577ae4a85272c407106c9d0a2ecb5372743@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/scan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 3d86482e83f5..6c2b73c0d36e 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -5,7 +5,7 @@ * Copyright 2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2023 Intel Corporation */ #include #include @@ -543,6 +543,10 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry, /* skip the TBTT offset */ pos++; + /* ignore entries with invalid BSSID */ + if (!is_valid_ether_addr(pos)) + return -EINVAL; + memcpy(entry->bssid, pos, ETH_ALEN); pos += ETH_ALEN; -- cgit 1.4.1 From c79d794a2cd76eca47b2491c5030be9a6418c5d6 Mon Sep 17 00:00:00 2001 From: Mirsad Goran Todorovac Date: Tue, 25 Apr 2023 18:40:08 +0200 Subject: wifi: mac80211: fortify the spinlock against deadlock by interrupt [ Upstream commit ef6e1997da63ad0ac3fe33153fec9524c9ae56c9 ] In the function ieee80211_tx_dequeue() there is a particular locking sequence: begin: spin_lock(&local->queue_stop_reason_lock); q_stopped = local->queue_stop_reasons[q]; spin_unlock(&local->queue_stop_reason_lock); However small the chance (increased by ftracetest), an asynchronous interrupt can occur in between of spin_lock() and spin_unlock(), and the interrupt routine will attempt to lock the same &local->queue_stop_reason_lock again. This will cause a costly reset of the CPU and the wifi device or an altogether hang in the single CPU and single core scenario. The only remaining spin_lock(&local->queue_stop_reason_lock) that did not disable interrupts was patched, which should prevent any deadlocks on the same CPU/core and the same wifi device. This is the probable trace of the deadlock: kernel: ================================ kernel: WARNING: inconsistent lock state kernel: 6.3.0-rc6-mt-20230401-00001-gf86822a1170f #4 Tainted: G W kernel: -------------------------------- kernel: inconsistent {IN-SOFTIRQ-W} -> {SOFTIRQ-ON-W} usage. kernel: kworker/5:0/25656 [HC0[0]:SC0[0]:HE1:SE1] takes: kernel: ffff9d6190779478 (&local->queue_stop_reason_lock){+.?.}-{2:2}, at: return_to_handler+0x0/0x40 kernel: {IN-SOFTIRQ-W} state was registered at: kernel: lock_acquire+0xc7/0x2d0 kernel: _raw_spin_lock+0x36/0x50 kernel: ieee80211_tx_dequeue+0xb4/0x1330 [mac80211] kernel: iwl_mvm_mac_itxq_xmit+0xae/0x210 [iwlmvm] kernel: iwl_mvm_mac_wake_tx_queue+0x2d/0xd0 [iwlmvm] kernel: ieee80211_queue_skb+0x450/0x730 [mac80211] kernel: __ieee80211_xmit_fast.constprop.66+0x834/0xa50 [mac80211] kernel: __ieee80211_subif_start_xmit+0x217/0x530 [mac80211] kernel: ieee80211_subif_start_xmit+0x60/0x580 [mac80211] kernel: dev_hard_start_xmit+0xb5/0x260 kernel: __dev_queue_xmit+0xdbe/0x1200 kernel: neigh_resolve_output+0x166/0x260 kernel: ip_finish_output2+0x216/0xb80 kernel: __ip_finish_output+0x2a4/0x4d0 kernel: ip_finish_output+0x2d/0xd0 kernel: ip_output+0x82/0x2b0 kernel: ip_local_out+0xec/0x110 kernel: igmpv3_sendpack+0x5c/0x90 kernel: igmp_ifc_timer_expire+0x26e/0x4e0 kernel: call_timer_fn+0xa5/0x230 kernel: run_timer_softirq+0x27f/0x550 kernel: __do_softirq+0xb4/0x3a4 kernel: irq_exit_rcu+0x9b/0xc0 kernel: sysvec_apic_timer_interrupt+0x80/0xa0 kernel: asm_sysvec_apic_timer_interrupt+0x1f/0x30 kernel: _raw_spin_unlock_irqrestore+0x3f/0x70 kernel: free_to_partial_list+0x3d6/0x590 kernel: __slab_free+0x1b7/0x310 kernel: kmem_cache_free+0x52d/0x550 kernel: putname+0x5d/0x70 kernel: do_sys_openat2+0x1d7/0x310 kernel: do_sys_open+0x51/0x80 kernel: __x64_sys_openat+0x24/0x30 kernel: do_syscall_64+0x5c/0x90 kernel: entry_SYSCALL_64_after_hwframe+0x72/0xdc kernel: irq event stamp: 5120729 kernel: hardirqs last enabled at (5120729): [] trace_graph_return+0xd6/0x120 kernel: hardirqs last disabled at (5120728): [] trace_graph_return+0xf0/0x120 kernel: softirqs last enabled at (5069900): [] return_to_handler+0x0/0x40 kernel: softirqs last disabled at (5067555): [] return_to_handler+0x0/0x40 kernel: other info that might help us debug this: kernel: Possible unsafe locking scenario: kernel: CPU0 kernel: ---- kernel: lock(&local->queue_stop_reason_lock); kernel: kernel: lock(&local->queue_stop_reason_lock); kernel: *** DEADLOCK *** kernel: 8 locks held by kworker/5:0/25656: kernel: #0: ffff9d618009d138 ((wq_completion)events_freezable){+.+.}-{0:0}, at: process_one_work+0x1ca/0x530 kernel: #1: ffffb1ef4637fe68 ((work_completion)(&local->restart_work)){+.+.}-{0:0}, at: process_one_work+0x1ce/0x530 kernel: #2: ffffffff9f166548 (rtnl_mutex){+.+.}-{3:3}, at: return_to_handler+0x0/0x40 kernel: #3: ffff9d6190778728 (&rdev->wiphy.mtx){+.+.}-{3:3}, at: return_to_handler+0x0/0x40 kernel: #4: ffff9d619077b480 (&mvm->mutex){+.+.}-{3:3}, at: return_to_handler+0x0/0x40 kernel: #5: ffff9d61907bacd8 (&trans_pcie->mutex){+.+.}-{3:3}, at: return_to_handler+0x0/0x40 kernel: #6: ffffffff9ef9cda0 (rcu_read_lock){....}-{1:2}, at: iwl_mvm_queue_state_change+0x59/0x3a0 [iwlmvm] kernel: #7: ffffffff9ef9cda0 (rcu_read_lock){....}-{1:2}, at: iwl_mvm_mac_itxq_xmit+0x42/0x210 [iwlmvm] kernel: stack backtrace: kernel: CPU: 5 PID: 25656 Comm: kworker/5:0 Tainted: G W 6.3.0-rc6-mt-20230401-00001-gf86822a1170f #4 kernel: Hardware name: LENOVO 82H8/LNVNB161216, BIOS GGCN51WW 11/16/2022 kernel: Workqueue: events_freezable ieee80211_restart_work [mac80211] kernel: Call Trace: kernel: kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: dump_stack_lvl+0x5f/0xa0 kernel: dump_stack+0x14/0x20 kernel: print_usage_bug.part.46+0x208/0x2a0 kernel: mark_lock.part.47+0x605/0x630 kernel: ? sched_clock+0xd/0x20 kernel: ? trace_clock_local+0x14/0x30 kernel: ? __rb_reserve_next+0x5f/0x490 kernel: ? _raw_spin_lock+0x1b/0x50 kernel: __lock_acquire+0x464/0x1990 kernel: ? mark_held_locks+0x4e/0x80 kernel: lock_acquire+0xc7/0x2d0 kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: ? ftrace_return_to_handler+0x8b/0x100 kernel: ? preempt_count_add+0x4/0x70 kernel: _raw_spin_lock+0x36/0x50 kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: ieee80211_tx_dequeue+0xb4/0x1330 [mac80211] kernel: ? prepare_ftrace_return+0xc5/0x190 kernel: ? ftrace_graph_func+0x16/0x20 kernel: ? 0xffffffffc02ab0b1 kernel: ? lock_acquire+0xc7/0x2d0 kernel: ? iwl_mvm_mac_itxq_xmit+0x42/0x210 [iwlmvm] kernel: ? ieee80211_tx_dequeue+0x9/0x1330 [mac80211] kernel: ? __rcu_read_lock+0x4/0x40 kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_mvm_mac_itxq_xmit+0xae/0x210 [iwlmvm] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_mvm_queue_state_change+0x311/0x3a0 [iwlmvm] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_mvm_wake_sw_queue+0x17/0x20 [iwlmvm] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_txq_gen2_unmap+0x1c9/0x1f0 [iwlwifi] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_txq_gen2_free+0x55/0x130 [iwlwifi] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_txq_gen2_tx_free+0x63/0x80 [iwlwifi] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: _iwl_trans_pcie_gen2_stop_device+0x3f3/0x5b0 [iwlwifi] kernel: ? _iwl_trans_pcie_gen2_stop_device+0x9/0x5b0 [iwlwifi] kernel: ? mutex_lock_nested+0x4/0x30 kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_trans_pcie_gen2_stop_device+0x5f/0x90 [iwlwifi] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_mvm_stop_device+0x78/0xd0 [iwlmvm] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: __iwl_mvm_mac_start+0x114/0x210 [iwlmvm] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: iwl_mvm_mac_start+0x76/0x150 [iwlmvm] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: drv_start+0x79/0x180 [mac80211] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: ieee80211_reconfig+0x1523/0x1ce0 [mac80211] kernel: ? synchronize_net+0x4/0x50 kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: ieee80211_restart_work+0x108/0x170 [mac80211] kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: process_one_work+0x250/0x530 kernel: ? ftrace_regs_caller_end+0x66/0x66 kernel: worker_thread+0x48/0x3a0 kernel: ? __pfx_worker_thread+0x10/0x10 kernel: kthread+0x10f/0x140 kernel: ? __pfx_kthread+0x10/0x10 kernel: ret_from_fork+0x29/0x50 kernel: Fixes: 4444bc2116ae ("wifi: mac80211: Proper mark iTXQs for resumption") Link: https://lore.kernel.org/all/1f58a0d1-d2b9-d851-73c3-93fcc607501c@alu.unizg.hr/ Reported-by: Mirsad Goran Todorovac Cc: Gregory Greenman Cc: Johannes Berg Link: https://lore.kernel.org/all/cdc80531-f25f-6f9d-b15f-25e16130b53a@alu.unizg.hr/ Cc: David S. Miller Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: Leon Romanovsky Cc: Alexander Wetzel Signed-off-by: Mirsad Goran Todorovac Reviewed-by: Leon Romanovsky Reviewed-by: tag, or it goes automatically? Link: https://lore.kernel.org/r/20230425164005.25272-1-mirsad.todorovac@alu.unizg.hr Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/tx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 6a1708db652f..763cefd0cc26 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3718,6 +3718,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, ieee80211_tx_result r; struct ieee80211_vif *vif = txq->vif; int q = vif->hw_queue[txq->ac]; + unsigned long flags; bool q_stopped; WARN_ON_ONCE(softirq_count() == 0); @@ -3726,9 +3727,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, return NULL; begin: - spin_lock(&local->queue_stop_reason_lock); + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); q_stopped = local->queue_stop_reasons[q]; - spin_unlock(&local->queue_stop_reason_lock); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); if (unlikely(q_stopped)) { /* mark for waking later */ -- cgit 1.4.1 From 01a4503d9fb8263984cf9442477a3fb5681b6f8f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 4 May 2023 16:45:01 +0300 Subject: wifi: mac80211: fix min center freq offset tracing [ Upstream commit 248e4776514bf70236e6b1a54c65aa5324c8b1eb ] We need to set the correct trace variable, otherwise we're overwriting something else instead and the right one that we print later is not initialized. Fixes: b6011960f392 ("mac80211: handle channel frequency offset") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230504134511.828474-2-gregory.greenman@intel.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/trace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 9f4377566c42..c85367a4757a 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -67,7 +67,7 @@ __entry->min_freq_offset = (c)->chan ? (c)->chan->freq_offset : 0; \ __entry->min_chan_width = (c)->width; \ __entry->min_center_freq1 = (c)->center_freq1; \ - __entry->freq1_offset = (c)->freq1_offset; \ + __entry->min_freq1_offset = (c)->freq1_offset; \ __entry->min_center_freq2 = (c)->center_freq2; #define MIN_CHANDEF_PR_FMT " min_control:%d.%03d MHz min_width:%d min_center: %d.%03d/%d MHz" #define MIN_CHANDEF_PR_ARG __entry->min_control_freq, __entry->min_freq_offset, \ -- cgit 1.4.1 From bc2265643208c1c193bc82456b5073c974caf99e Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Thu, 4 May 2023 16:04:41 +0800 Subject: wifi: mac80211: Abort running color change when stopping the AP [ Upstream commit a23d7f5b2fbda114de60c4b53311e052281d7533 ] When stopping the AP, there might be a color change in progress. It should be deactivated here, or the driver might later finalize a color change on a stopped AP. Fixes: 5f9404abdf2a (mac80211: add support for BSS color change) Signed-off-by: Michael Lee Link: https://lore.kernel.org/r/20230504080441.22958-1-michael-cy.lee@mediatek.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/cfg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e8beec0a0ae1..06b9df2fbcd7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1477,9 +1477,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata); - /* abort any running channel switch */ + /* abort any running channel switch or color change */ mutex_lock(&local->mtx); link_conf->csa_active = false; + link_conf->color_change_active = false; if (link->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); -- cgit 1.4.1 From a20550b3aac3037506c9fbf095c65a6df2aef4c4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 14 May 2023 12:15:46 +0300 Subject: wifi: iwlwifi: mvm: fix cancel_delayed_work_sync() deadlock [ Upstream commit c2d8b7f257b2398f2d866205365895e038beca12 ] Lockdep points out that we can deadlock here by calling cancel_delayed_work_sync() because that might be already running and gotten interrupted by the NAPI soft-IRQ. Even just calling something that can sleep is wrong in this context though. Luckily, it doesn't even really matter since the things we need to do are idempotent, so just drop the _sync(). Fixes: e5d153ec54f0 ("iwlwifi: mvm: fix CSA AP side") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230514120631.b1813c823b4d.I9d20cc06d24fa40b6774d3dd95ea5e2bf8dd015b@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 091225894037..02c2a0630107 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1975,7 +1975,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); /* Unblock BCAST / MCAST station */ iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false); - cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork); + cancel_delayed_work(&mvm->cs_tx_unblock_dwork); } } -- cgit 1.4.1 From 2160e1198191ad87d7c408bb29415945570b892d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 14 May 2023 12:15:48 +0300 Subject: wifi: iwlwifi: fw: fix DBGI dump [ Upstream commit d3ae69180bbd74bcbc03a2b6d10ed7eccbe98c23 ] The DBGI dump is (unsurprisingly) of type DBGI, not SRAM. This leads to bad register accesses because the union is built differently, there's no allocation ID, and thus the allocation ID ends up being 0x8000. Note that this was already wrong for DRAM vs. SMEM since they use different parts of the union, but the allocation ID is at the same place, so it worked. Fix all of this but set the allocation ID in a way that the offset calculation ends up without any offset. Fixes: 34bc27783a31 ("iwlwifi: yoyo: fix DBGI_SRAM ini dump header.") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230514120631.19a302ae4c65.I12272599f7c1930666157b9d5e7f81fe9ec4c421@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 027360e63b92..3ef0b776b772 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1664,14 +1664,10 @@ static __le32 iwl_get_mon_reg(struct iwl_fw_runtime *fwrt, u32 alloc_id, } static void * -iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, - struct iwl_dump_ini_region_data *reg_data, +iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id, struct iwl_fw_ini_monitor_dump *data, const struct iwl_fw_mon_regs *addrs) { - struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; - u32 alloc_id = le32_to_cpu(reg->dram_alloc_id); - if (!iwl_trans_grab_nic_access(fwrt->trans)) { IWL_ERR(fwrt, "Failed to get monitor header\n"); return NULL; @@ -1702,8 +1698,10 @@ iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt, void *data, u32 data_len) { struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + u32 alloc_id = le32_to_cpu(reg->dram_alloc_id); - return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, + return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump, &fwrt->trans->cfg->mon_dram_regs); } @@ -1713,8 +1711,10 @@ iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt, void *data, u32 data_len) { struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + u32 alloc_id = le32_to_cpu(reg->internal_buffer.alloc_id); - return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, + return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump, &fwrt->trans->cfg->mon_smem_regs); } @@ -1725,7 +1725,10 @@ iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt, { struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; - return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, + return iwl_dump_ini_mon_fill_header(fwrt, + /* no offset calculation later */ + IWL_FW_INI_ALLOCATION_ID_DBGC1, + mon_dump, &fwrt->trans->cfg->mon_dbgi_regs); } -- cgit 1.4.1 From bc907fbf48bcdb37be0e4f398d96d69e4a654063 Mon Sep 17 00:00:00 2001 From: Alon Giladi Date: Sun, 14 May 2023 12:15:51 +0300 Subject: wifi: iwlwifi: fix OEM's name in the ppag approved list [ Upstream commit eca7296d9a671e9961834d2ace9cc0ce21fc15b3 ] Fix a spelling mistake. Fixes: e8e10a37c51c ("iwlwifi: acpi: move ppag code from mvm to fw/acpi") Signed-off-by: Alon Giladi Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230514120631.fdd07f36a8bf.I223e5fb16ab5c95d504c3fdaffd0bd70affad1c2@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index a02e5a67b706..585e8cd2d332 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -38,7 +38,7 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = { }, { .ident = "ASUS", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek COMPUTER INC."), + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), }, }, {} -- cgit 1.4.1 From a270c552ced35c83ca346ed7bf35f29b3c65577e Mon Sep 17 00:00:00 2001 From: Alon Giladi Date: Sun, 14 May 2023 12:15:52 +0300 Subject: wifi: iwlwifi: mvm: fix OEM's name in the tas approved list [ Upstream commit d0246a0e49efee0f8649d0e4f2350614cdfe6565 ] Fix a spelling mistake. Fixes: 2856f623ce48 ("iwlwifi: mvm: Add list of OEMs allowed to use TAS") Signed-off-by: Alon Giladi Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230514120631.4090de6d1878.If9391ef6da78f1b2cc5eb6cb8f6965816bb7a7f5@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 5de34edc51fe..887d0789c96c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1055,7 +1055,7 @@ static const struct dmi_system_id dmi_tas_approved_list[] = { }, { .ident = "LENOVO", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Lenovo"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), }, }, { .ident = "DELL", -- cgit 1.4.1 From 557ba100d8cf3661ff8d71c0b4a2cba8db555ec2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 14 May 2023 12:15:53 +0300 Subject: wifi: iwlwifi: mvm: don't trust firmware n_channels [ Upstream commit 682b6dc29d98e857e6ca4bbc077c7dc2899b7473 ] If the firmware sends us a corrupted MCC response with n_channels much larger than the command response can be, we might copy far too much (uninitialized) memory and even crash if the n_channels is large enough to make it run out of the one page allocated for the FW response. Fix that by checking the lengths. Doing a < comparison would be sufficient, but the firmware should be doing it correctly, so check more strictly. Fixes: dcaf9f5ecb6f ("iwlwifi: mvm: add MCC update FW API") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230514120631.d7b233139eb4.I51fd319df8e9d41881fc8450e83d78049518a79a@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 6d18a1fd649b..fdf60afb0f3f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -445,6 +445,11 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, struct iwl_mcc_update_resp *mcc_resp = (void *)pkt->data; n_channels = __le32_to_cpu(mcc_resp->n_channels); + if (iwl_rx_packet_payload_len(pkt) != + struct_size(mcc_resp, channels, n_channels)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels * sizeof(__le32); resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); @@ -456,6 +461,11 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, struct iwl_mcc_update_resp_v3 *mcc_resp_v3 = (void *)pkt->data; n_channels = __le32_to_cpu(mcc_resp_v3->n_channels); + if (iwl_rx_packet_payload_len(pkt) != + struct_size(mcc_resp_v3, channels, n_channels)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels * sizeof(__le32); resp_cp = kzalloc(resp_len, GFP_KERNEL); -- cgit 1.4.1 From ae42c6f79cec7314e608f43e767253bfeab612ee Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Mon, 15 May 2023 10:20:41 -0700 Subject: scsi: storvsc: Don't pass unused PFNs to Hyper-V host [ Upstream commit 4e81a6cba517cb33584308a331f14f5e3fec369b ] In a SCSI request, storvsc pre-allocates space for up to MAX_PAGE_BUFFER_COUNT physical frame numbers to be passed to Hyper-V. If the size of the I/O request requires more PFNs, a separate memory area of exactly the correct size is dynamically allocated. But when the pre-allocated area is used, current code always passes MAX_PAGE_BUFFER_COUNT PFNs to Hyper-V, even if fewer are needed. While this doesn't break anything because the additional PFNs are always zero, more bytes than necessary are copied into the VMBus channel ring buffer. This takes CPU cycles and wastes space in the ring buffer. For a typical 4 Kbyte I/O that requires only a single PFN, 248 unnecessary bytes are copied. Fix this by setting the payload_sz based on the actual number of PFNs required, not the size of the pre-allocated space. Reported-by: John Starks Fixes: 8f43710543ef ("scsi: storvsc: Support PAGE_SIZE larger than 4K") Signed-off-by: Michael Kelley Link: https://lore.kernel.org/r/1684171241-16209-1-git-send-email-mikelley@microsoft.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/storvsc_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index a0665bca54b9..5284f9a0b826 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1780,7 +1780,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) length = scsi_bufflen(scmnd); payload = (struct vmbus_packet_mpb_array *)&cmd_request->mpb; - payload_sz = sizeof(cmd_request->mpb); + payload_sz = 0; if (scsi_sg_count(scmnd)) { unsigned long offset_in_hvpg = offset_in_hvpage(sgl->offset); @@ -1789,10 +1789,10 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) unsigned long hvpfn, hvpfns_to_add; int j, i = 0, sg_count; - if (hvpg_count > MAX_PAGE_BUFFER_COUNT) { + payload_sz = (hvpg_count * sizeof(u64) + + sizeof(struct vmbus_packet_mpb_array)); - payload_sz = (hvpg_count * sizeof(u64) + - sizeof(struct vmbus_packet_mpb_array)); + if (hvpg_count > MAX_PAGE_BUFFER_COUNT) { payload = kzalloc(payload_sz, GFP_ATOMIC); if (!payload) return SCSI_MLQUEUE_DEVICE_BUSY; -- cgit 1.4.1 From e2d59768f851c1726d6d0df63a6ae4c5e39786a0 Mon Sep 17 00:00:00 2001 From: Chuang Wang Date: Thu, 10 Nov 2022 15:31:25 +0800 Subject: net: tun: rebuild error handling in tun_get_user [ Upstream commit ab00af85d2f886a8e4ace1342d9cc2b232eab6a8 ] The error handling in tun_get_user is very scattered. This patch unifies error handling, reduces duplication of code, and makes the logic clearer. Signed-off-by: Chuang Wang Signed-off-by: David S. Miller Stable-dep-of: 82b2bc279467 ("tun: Fix memory leak for detached NAPI queue.") Signed-off-by: Sasha Levin --- drivers/net/tun.c | 65 +++++++++++++++++++++++++------------------------------ 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 91d198aff2f9..65706824eb82 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1748,7 +1748,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, u32 rxhash = 0; int skb_xdp = 1; bool frags = tun_napi_frags_enabled(tfile); - enum skb_drop_reason drop_reason; + enum skb_drop_reason drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; if (!(tun->flags & IFF_NO_PI)) { if (len < sizeof(pi)) @@ -1809,10 +1809,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, * skb was created with generic XDP routine. */ skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp); - if (IS_ERR(skb)) { - dev_core_stats_rx_dropped_inc(tun->dev); - return PTR_ERR(skb); - } + err = PTR_ERR_OR_ZERO(skb); + if (err) + goto drop; if (!skb) return total_len; } else { @@ -1837,13 +1836,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, noblock); } - if (IS_ERR(skb)) { - if (PTR_ERR(skb) != -EAGAIN) - dev_core_stats_rx_dropped_inc(tun->dev); - if (frags) - mutex_unlock(&tfile->napi_mutex); - return PTR_ERR(skb); - } + err = PTR_ERR_OR_ZERO(skb); + if (err) + goto drop; if (zerocopy) err = zerocopy_sg_from_iter(skb, from); @@ -1853,27 +1848,14 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (err) { err = -EFAULT; drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; -drop: - dev_core_stats_rx_dropped_inc(tun->dev); - kfree_skb_reason(skb, drop_reason); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - - return err; + goto drop; } } if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) { atomic_long_inc(&tun->rx_frame_errors); - kfree_skb(skb); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - - return -EINVAL; + err = -EINVAL; + goto free_skb; } switch (tun->flags & TUN_TYPE_MASK) { @@ -1889,9 +1871,8 @@ drop: pi.proto = htons(ETH_P_IPV6); break; default: - dev_core_stats_rx_dropped_inc(tun->dev); - kfree_skb(skb); - return -EINVAL; + err = -EINVAL; + goto drop; } } @@ -1933,11 +1914,7 @@ drop: if (ret != XDP_PASS) { rcu_read_unlock(); local_bh_enable(); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - return total_len; + goto unlock_frags; } } rcu_read_unlock(); @@ -2017,6 +1994,22 @@ napi_busy: tun_flow_update(tun, rxhash, tfile); return total_len; + +drop: + if (err != -EAGAIN) + dev_core_stats_rx_dropped_inc(tun->dev); + +free_skb: + if (!IS_ERR_OR_NULL(skb)) + kfree_skb_reason(skb, drop_reason); + +unlock_frags: + if (frags) { + tfile->napi.skb = NULL; + mutex_unlock(&tfile->napi_mutex); + } + + return err ?: total_len; } static ssize_t tun_chr_write_iter(struct kiocb *iocb, struct iov_iter *from) -- cgit 1.4.1 From 9cae243b9ae25adfe468cd47ceca591f6725b79c Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 15 May 2023 11:42:04 -0700 Subject: tun: Fix memory leak for detached NAPI queue. [ Upstream commit 82b2bc279467c875ec36f8ef820f00997c2a4e8e ] syzkaller reported [0] memory leaks of sk and skb related to the TUN device with no repro, but we can reproduce it easily with: struct ifreq ifr = {} int fd_tun, fd_tmp; char buf[4] = {}; fd_tun = openat(AT_FDCWD, "/dev/net/tun", O_WRONLY, 0); ifr.ifr_flags = IFF_TUN | IFF_NAPI | IFF_MULTI_QUEUE; ioctl(fd_tun, TUNSETIFF, &ifr); ifr.ifr_flags = IFF_DETACH_QUEUE; ioctl(fd_tun, TUNSETQUEUE, &ifr); fd_tmp = socket(AF_PACKET, SOCK_PACKET, 0); ifr.ifr_flags = IFF_UP; ioctl(fd_tmp, SIOCSIFFLAGS, &ifr); write(fd_tun, buf, sizeof(buf)); close(fd_tun); If we enable NAPI and multi-queue on a TUN device, we can put skb into tfile->sk.sk_write_queue after the queue is detached. We should prevent it by checking tfile->detached before queuing skb. Note this must be done under tfile->sk.sk_write_queue.lock because write() and ioctl(IFF_DETACH_QUEUE) can run concurrently. Otherwise, there would be a small race window: write() ioctl(IFF_DETACH_QUEUE) `- tun_get_user `- __tun_detach |- if (tfile->detached) |- tun_disable_queue | `-> false | `- tfile->detached = tun | `- tun_queue_purge |- spin_lock_bh(&queue->lock) `- __skb_queue_tail(queue, skb) Another solution is to call tun_queue_purge() when closing and reattaching the detached queue, but it could paper over another problems. Also, we do the same kind of test for IFF_NAPI_FRAGS. [0]: unreferenced object 0xffff88801edbc800 (size 2048): comm "syz-executor.1", pid 33269, jiffies 4295743834 (age 18.756s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 07 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............ backtrace: [<000000008c16ea3d>] __do_kmalloc_node mm/slab_common.c:965 [inline] [<000000008c16ea3d>] __kmalloc+0x4a/0x130 mm/slab_common.c:979 [<000000003addde56>] kmalloc include/linux/slab.h:563 [inline] [<000000003addde56>] sk_prot_alloc+0xef/0x1b0 net/core/sock.c:2035 [<000000003e20621f>] sk_alloc+0x36/0x2f0 net/core/sock.c:2088 [<0000000028e43843>] tun_chr_open+0x3d/0x190 drivers/net/tun.c:3438 [<000000001b0f1f28>] misc_open+0x1a6/0x1f0 drivers/char/misc.c:165 [<000000004376f706>] chrdev_open+0x111/0x300 fs/char_dev.c:414 [<00000000614d379f>] do_dentry_open+0x2f9/0x750 fs/open.c:920 [<000000008eb24774>] do_open fs/namei.c:3636 [inline] [<000000008eb24774>] path_openat+0x143f/0x1a30 fs/namei.c:3791 [<00000000955077b5>] do_filp_open+0xce/0x1c0 fs/namei.c:3818 [<00000000b78973b0>] do_sys_openat2+0xf0/0x260 fs/open.c:1356 [<00000000057be699>] do_sys_open fs/open.c:1372 [inline] [<00000000057be699>] __do_sys_openat fs/open.c:1388 [inline] [<00000000057be699>] __se_sys_openat fs/open.c:1383 [inline] [<00000000057be699>] __x64_sys_openat+0x83/0xf0 fs/open.c:1383 [<00000000a7d2182d>] do_syscall_x64 arch/x86/entry/common.c:50 [inline] [<00000000a7d2182d>] do_syscall_64+0x3c/0x90 arch/x86/entry/common.c:80 [<000000004cc4e8c4>] entry_SYSCALL_64_after_hwframe+0x72/0xdc unreferenced object 0xffff88802f671700 (size 240): comm "syz-executor.1", pid 33269, jiffies 4295743854 (age 18.736s) hex dump (first 32 bytes): 68 c9 db 1e 80 88 ff ff 68 c9 db 1e 80 88 ff ff h.......h....... 00 c0 7b 2f 80 88 ff ff 00 c8 db 1e 80 88 ff ff ..{/............ backtrace: [<00000000e9d9fdb6>] __alloc_skb+0x223/0x250 net/core/skbuff.c:644 [<000000002c3e4e0b>] alloc_skb include/linux/skbuff.h:1288 [inline] [<000000002c3e4e0b>] alloc_skb_with_frags+0x6f/0x350 net/core/skbuff.c:6378 [<00000000825f98d7>] sock_alloc_send_pskb+0x3ac/0x3e0 net/core/sock.c:2729 [<00000000e9eb3df3>] tun_alloc_skb drivers/net/tun.c:1529 [inline] [<00000000e9eb3df3>] tun_get_user+0x5e1/0x1f90 drivers/net/tun.c:1841 [<0000000053096912>] tun_chr_write_iter+0xac/0x120 drivers/net/tun.c:2035 [<00000000b9282ae0>] call_write_iter include/linux/fs.h:1868 [inline] [<00000000b9282ae0>] new_sync_write fs/read_write.c:491 [inline] [<00000000b9282ae0>] vfs_write+0x40f/0x530 fs/read_write.c:584 [<00000000524566e4>] ksys_write+0xa1/0x170 fs/read_write.c:637 [<00000000a7d2182d>] do_syscall_x64 arch/x86/entry/common.c:50 [inline] [<00000000a7d2182d>] do_syscall_64+0x3c/0x90 arch/x86/entry/common.c:80 [<000000004cc4e8c4>] entry_SYSCALL_64_after_hwframe+0x72/0xdc Fixes: cde8b15f1aab ("tuntap: add ioctl to attach or detach a file form tuntap device") Reported-by: syzkaller Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/tun.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 65706824eb82..7c8db8f6f661 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1971,6 +1971,14 @@ napi_busy: int queue_len; spin_lock_bh(&queue->lock); + + if (unlikely(tfile->detached)) { + spin_unlock_bh(&queue->lock); + rcu_read_unlock(); + err = -EBUSY; + goto free_skb; + } + __skb_queue_tail(queue, skb); queue_len = skb_queue_len(queue); spin_unlock(&queue->lock); @@ -2506,6 +2514,13 @@ build: if (tfile->napi_enabled) { queue = &tfile->sk.sk_write_queue; spin_lock(&queue->lock); + + if (unlikely(tfile->detached)) { + spin_unlock(&queue->lock); + kfree_skb(skb); + return -EBUSY; + } + __skb_queue_tail(queue, skb); spin_unlock(&queue->lock); ret = 1; -- cgit 1.4.1 From 172146c26f0c1b86ab4e9ebffc7e06f04229fa17 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 15 May 2023 21:09:11 +0200 Subject: cassini: Fix a memory leak in the error handling path of cas_init_one() [ Upstream commit 412cd77a2c24b191c65ea53025222418db09817c ] cas_saturn_firmware_init() allocates some memory using vmalloc(). This memory is freed in the .remove() function but not it the error handling path of the probe. Add the missing vfree() to avoid a memory leak, should an error occur. Fixes: fcaa40669cd7 ("cassini: use request_firmware") Signed-off-by: Christophe JAILLET Reviewed-by: Pavan Chebbi Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/sun/cassini.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 0aca193d9550..800956d5464b 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -5095,6 +5095,8 @@ err_out_iounmap: cas_shutdown(cp); mutex_unlock(&cp->pm_mutex); + vfree(cp->fw_data); + pci_iounmap(pdev, cp->regs); -- cgit 1.4.1 From 516114d7fb584af33af26a859f8f51810f961900 Mon Sep 17 00:00:00 2001 From: Marco Migliore Date: Tue, 16 May 2023 09:38:54 +0200 Subject: net: dsa: mv88e6xxx: Fix mv88e6393x EPC write command offset [ Upstream commit 1323e0c6e1d7e103d59384c3ac50f72b17a6936c ] According to datasheet, the command opcode must be specified into bits [14:12] of the Extended Port Control register (EPC). Fixes: de776d0d316f ("net: dsa: mv88e6xxx: add support for mv88e6393x family") Signed-off-by: Marco Migliore Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/mv88e6xxx/port.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index cb04243f37c1..a91e22d9a6cb 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -276,7 +276,7 @@ /* Offset 0x10: Extended Port Control Command */ #define MV88E6393X_PORT_EPC_CMD 0x10 #define MV88E6393X_PORT_EPC_CMD_BUSY 0x8000 -#define MV88E6393X_PORT_EPC_CMD_WRITE 0x0300 +#define MV88E6393X_PORT_EPC_CMD_WRITE 0x3000 #define MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE 0x02 /* Offset 0x11: Extended Port Control Data */ -- cgit 1.4.1 From e02d2b987c0297dd8a2b069a03b6b30616a88b23 Mon Sep 17 00:00:00 2001 From: Aleksandr Loktionov Date: Tue, 16 May 2023 10:41:46 -0700 Subject: igb: fix bit_shift to be in [1..8] range [ Upstream commit 60d758659f1fb49e0d5b6ac2691ede8c0958795b ] In igb_hash_mc_addr() the expression: "mc_addr[4] >> 8 - bit_shift", right shifting "mc_addr[4]" shift by more than 7 bits always yields zero, so hash becomes not so different. Add initialization with bit_shift = 1 and add a loop condition to ensure bit_shift will be always in [1..8] range. Fixes: 9d5c824399de ("igb: PCI-Express 82575 Gigabit Ethernet driver") Signed-off-by: Aleksandr Loktionov Tested-by: Pucha Himasekhar Reddy (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igb/e1000_mac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 205d577bdbba..caf91c6f52b4 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -426,7 +426,7 @@ void igb_mta_set(struct e1000_hw *hw, u32 hash_value) static u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) { u32 hash_value, hash_mask; - u8 bit_shift = 0; + u8 bit_shift = 1; /* Register count multiplied by bits per register */ hash_mask = (hw->mac.mta_reg_count * 32) - 1; @@ -434,7 +434,7 @@ static u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) /* For a mc_filter_type of 0, bit_shift is the number of left-shifts * where 0xFF would still fall within the hash mask. */ - while (hash_mask >> bit_shift != 0xFF) + while (hash_mask >> bit_shift != 0xFF && bit_shift < 4) bit_shift++; /* The portion of the address that is used for the hash table -- cgit 1.4.1 From c3e3e8933faa9de87aa7040d7d7c9a4dd83fc967 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 16 May 2023 14:23:42 +0000 Subject: vlan: fix a potential uninit-value in vlan_dev_hard_start_xmit() [ Upstream commit dacab578c7c6cd06c50c89dfa36b0e0f10decd4e ] syzbot triggered the following splat [1], sending an empty message through pppoe_sendmsg(). When VLAN_FLAG_REORDER_HDR flag is set, vlan_dev_hard_header() does not push extra bytes for the VLAN header, because vlan is offloaded. Unfortunately vlan_dev_hard_start_xmit() first reads veth->h_vlan_proto before testing (vlan->flags & VLAN_FLAG_REORDER_HDR). We need to swap the two conditions. [1] BUG: KMSAN: uninit-value in vlan_dev_hard_start_xmit+0x171/0x7f0 net/8021q/vlan_dev.c:111 vlan_dev_hard_start_xmit+0x171/0x7f0 net/8021q/vlan_dev.c:111 __netdev_start_xmit include/linux/netdevice.h:4883 [inline] netdev_start_xmit include/linux/netdevice.h:4897 [inline] xmit_one net/core/dev.c:3580 [inline] dev_hard_start_xmit+0x253/0xa20 net/core/dev.c:3596 __dev_queue_xmit+0x3c7f/0x5ac0 net/core/dev.c:4246 dev_queue_xmit include/linux/netdevice.h:3053 [inline] pppoe_sendmsg+0xa93/0xb80 drivers/net/ppp/pppoe.c:900 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] ____sys_sendmsg+0xa24/0xe40 net/socket.c:2501 ___sys_sendmsg+0x2a1/0x3f0 net/socket.c:2555 __sys_sendmmsg+0x411/0xa50 net/socket.c:2641 __do_sys_sendmmsg net/socket.c:2670 [inline] __se_sys_sendmmsg net/socket.c:2667 [inline] __x64_sys_sendmmsg+0xbc/0x120 net/socket.c:2667 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd Uninit was created at: slab_post_alloc_hook+0x12d/0xb60 mm/slab.h:774 slab_alloc_node mm/slub.c:3452 [inline] kmem_cache_alloc_node+0x543/0xab0 mm/slub.c:3497 kmalloc_reserve+0x148/0x470 net/core/skbuff.c:520 __alloc_skb+0x3a7/0x850 net/core/skbuff.c:606 alloc_skb include/linux/skbuff.h:1277 [inline] sock_wmalloc+0xfe/0x1a0 net/core/sock.c:2583 pppoe_sendmsg+0x3af/0xb80 drivers/net/ppp/pppoe.c:867 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] ____sys_sendmsg+0xa24/0xe40 net/socket.c:2501 ___sys_sendmsg+0x2a1/0x3f0 net/socket.c:2555 __sys_sendmmsg+0x411/0xa50 net/socket.c:2641 __do_sys_sendmmsg net/socket.c:2670 [inline] __se_sys_sendmmsg net/socket.c:2667 [inline] __x64_sys_sendmmsg+0xbc/0x120 net/socket.c:2667 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd CPU: 0 PID: 29770 Comm: syz-executor.0 Not tainted 6.3.0-rc6-syzkaller-gc478e5b17829 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/30/2023 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/8021q/vlan_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 07e86d03d4ba..d3e511e1eba8 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -108,8 +108,8 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs... */ - if (veth->h_vlan_proto != vlan->vlan_proto || - vlan->flags & VLAN_FLAG_REORDER_HDR) { + if (vlan->flags & VLAN_FLAG_REORDER_HDR || + veth->h_vlan_proto != vlan->vlan_proto) { u16 vlan_tci; vlan_tci = vlan->vlan_id; vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority); -- cgit 1.4.1 From ee44bacf462db3ec6e4f0dcfa7931e768670d77c Mon Sep 17 00:00:00 2001 From: M Chetan Kumar Date: Tue, 16 May 2023 21:09:46 +0530 Subject: net: wwan: iosm: fix NULL pointer dereference when removing device [ Upstream commit 60829145f1e2650b31ebe6a0ec70a9725b38fa2c ] In suspend and resume cycle, the removal and rescan of device ends up in NULL pointer dereference. During driver initialization, if the ipc_imem_wwan_channel_init() fails to get the valid device capabilities it returns an error and further no resource (wwan struct) will be allocated. Now in this situation if driver removal procedure is initiated it would result in NULL pointer exception since unallocated wwan struct is dereferenced inside ipc_wwan_deinit(). ipc_imem_run_state_worker() to handle the called functions return value and to release the resource in failure case. It also reports the link down event in failure cases. The user space application can handle this event to do a device reset for restoring the device communication. Fixes: 3670970dd8c6 ("net: iosm: shared memory IPC interface") Reported-by: Samuel Wein PhD Closes: https://lore.kernel.org/netdev/20230427140819.1310f4bd@kernel.org/T/ Signed-off-by: M Chetan Kumar Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/wwan/iosm/iosm_ipc_imem.c | 27 +++++++++++++++++++++------ drivers/net/wwan/iosm/iosm_ipc_imem_ops.c | 12 ++++++++---- drivers/net/wwan/iosm/iosm_ipc_imem_ops.h | 6 ++++-- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c index 1e6a47976642..8ccd4d26b906 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.c +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c @@ -565,24 +565,32 @@ static void ipc_imem_run_state_worker(struct work_struct *instance) struct ipc_mux_config mux_cfg; struct iosm_imem *ipc_imem; u8 ctrl_chl_idx = 0; + int ret; ipc_imem = container_of(instance, struct iosm_imem, run_state_worker); if (ipc_imem->phase != IPC_P_RUN) { dev_err(ipc_imem->dev, "Modem link down. Exit run state worker."); - return; + goto err_out; } if (test_and_clear_bit(IOSM_DEVLINK_INIT, &ipc_imem->flag)) ipc_devlink_deinit(ipc_imem->ipc_devlink); - if (!ipc_imem_setup_cp_mux_cap_init(ipc_imem, &mux_cfg)) - ipc_imem->mux = ipc_mux_init(&mux_cfg, ipc_imem); + ret = ipc_imem_setup_cp_mux_cap_init(ipc_imem, &mux_cfg); + if (ret < 0) + goto err_out; + + ipc_imem->mux = ipc_mux_init(&mux_cfg, ipc_imem); + if (!ipc_imem->mux) + goto err_out; + + ret = ipc_imem_wwan_channel_init(ipc_imem, mux_cfg.protocol); + if (ret < 0) + goto err_ipc_mux_deinit; - ipc_imem_wwan_channel_init(ipc_imem, mux_cfg.protocol); - if (ipc_imem->mux) - ipc_imem->mux->wwan = ipc_imem->wwan; + ipc_imem->mux->wwan = ipc_imem->wwan; while (ctrl_chl_idx < IPC_MEM_MAX_CHANNELS) { if (!ipc_chnl_cfg_get(&chnl_cfg_port, ctrl_chl_idx)) { @@ -615,6 +623,13 @@ static void ipc_imem_run_state_worker(struct work_struct *instance) /* Complete all memory stores after setting bit */ smp_mb__after_atomic(); + + return; + +err_ipc_mux_deinit: + ipc_mux_deinit(ipc_imem->mux); +err_out: + ipc_uevent_send(ipc_imem->dev, UEVENT_CD_READY_LINK_DOWN); } static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq) diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c index 66b90cc4c346..109cf8930488 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c +++ b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c @@ -77,8 +77,8 @@ out: } /* Initialize wwan channel */ -void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, - enum ipc_mux_protocol mux_type) +int ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, + enum ipc_mux_protocol mux_type) { struct ipc_chnl_cfg chnl_cfg = { 0 }; @@ -87,7 +87,7 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, /* If modem version is invalid (0xffffffff), do not initialize WWAN. */ if (ipc_imem->cp_version == -1) { dev_err(ipc_imem->dev, "invalid CP version"); - return; + return -EIO; } ipc_chnl_cfg_get(&chnl_cfg, ipc_imem->nr_of_channels); @@ -104,9 +104,13 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, /* WWAN registration. */ ipc_imem->wwan = ipc_wwan_init(ipc_imem, ipc_imem->dev); - if (!ipc_imem->wwan) + if (!ipc_imem->wwan) { dev_err(ipc_imem->dev, "failed to register the ipc_wwan interfaces"); + return -ENOMEM; + } + + return 0; } /* Map SKB to DMA for transfer */ diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h index f8afb217d9e2..026c5bd0f999 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h +++ b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.h @@ -91,9 +91,11 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem, int if_id, * MUX. * @ipc_imem: Pointer to iosm_imem struct. * @mux_type: Type of mux protocol. + * + * Return: 0 on success and failure value on error */ -void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, - enum ipc_mux_protocol mux_type); +int ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, + enum ipc_mux_protocol mux_type); /** * ipc_imem_sys_devlink_open - Open a Flash/CD Channel link to CP -- cgit 1.4.1 From fdc5c8fb57d70143acc5cd3bacbbdac11eaf7125 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 16 May 2023 18:44:10 +0300 Subject: net: pcs: xpcs: fix C73 AN not getting enabled [ Upstream commit c46e78ba9a7a09da4f192dc8df15c4e8a07fb9e0 ] The XPCS expects clause 73 (copper backplane) autoneg to follow the ethtool autoneg bit. It actually did that until the blamed commit inaptly replaced state->an_enabled (coming from ethtool) with phylink_autoneg_inband() (coming from the device tree or struct phylink_config), as part of an unrelated phylink_pcs API conversion. Russell King suggests that state->an_enabled from the original code was just a proxy for the ethtool Autoneg bit, and that the correct way of restoring the functionality is to check for this bit in the advertising mask. Fixes: 11059740e616 ("net: pcs: xpcs: convert to phylink_pcs_ops") Link: https://lore.kernel.org/netdev/ZGNt2MFeRolKGFck@shell.armlinux.org.uk/ Suggested-by: Russell King (Oracle) Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/pcs/pcs-xpcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index dd88624593c7..3f882bce37f4 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -881,7 +881,7 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, switch (compat->an_mode) { case DW_AN_C73: - if (phylink_autoneg_inband(mode)) { + if (test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) { ret = xpcs_config_aneg_c73(xpcs, compat); if (ret) return ret; -- cgit 1.4.1 From d862b63605dfabfd17788f1c722e7375d4137c05 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Tue, 16 May 2023 14:49:24 -0400 Subject: net: selftests: Fix optstring [ Upstream commit 9ba9485b87ac97fd159abdb4cbd53099bc9f01c6 ] The cited commit added a stray colon to the 'v' option. That makes the option work incorrectly. ex: tools/testing/selftests/net# ./fib_nexthops.sh -v (should enable verbose mode, instead it shows help text due to missing arg) Fixes: 5feba4727395 ("selftests: fib_nexthops: Make ping timeout configurable") Reviewed-by: Ido Schimmel Signed-off-by: Benjamin Poirier Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- tools/testing/selftests/net/fib_nexthops.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh index a47b26ab48f2..0f5e88c8f4ff 100755 --- a/tools/testing/selftests/net/fib_nexthops.sh +++ b/tools/testing/selftests/net/fib_nexthops.sh @@ -2283,7 +2283,7 @@ EOF ################################################################################ # main -while getopts :t:pP46hv:w: o +while getopts :t:pP46hvw: o do case $o in t) TESTS=$OPTARG;; -- cgit 1.4.1 From 8f58c538573a0cf799e89bb287ee935adc8da2df Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 11 May 2023 14:15:15 +0200 Subject: netfilter: nf_tables: fix nft_trans type confusion [ Upstream commit e3c361b8acd636f5fe80c02849ca175201edf10c ] nft_trans_FOO objects all share a common nft_trans base structure, but trailing fields depend on the real object size. Access is only safe after trans->msg_type check. Check for rule type first. Found by code inspection. Fixes: 1a94e38d254b ("netfilter: nf_tables: add NFTA_RULE_ID attribute") Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index f663262df698..31775d54f4b4 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3692,12 +3692,10 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net, struct nft_trans *trans; list_for_each_entry(trans, &nft_net->commit_list, list) { - struct nft_rule *rule = nft_trans_rule(trans); - if (trans->msg_type == NFT_MSG_NEWRULE && trans->ctx.chain == chain && id == nft_trans_rule_id(trans)) - return rule; + return nft_trans_rule(trans); } return ERR_PTR(-ENOENT); } -- cgit 1.4.1 From a836be60a3aabcedcd9c79f545d409ace1f20ba6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 11 May 2023 22:39:30 +0200 Subject: netfilter: nft_set_rbtree: fix null deref on element insertion [ Upstream commit 61ae320a29b0540c16931816299eb86bf2b66c08 ] There is no guarantee that rb_prev() will not return NULL in nft_rbtree_gc_elem(): general protection fault, probably for non-canonical address 0xdffffc0000000003: 0000 [#1] PREEMPT SMP KASAN KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f] nft_add_set_elem+0x14b0/0x2990 nf_tables_newsetelem+0x528/0xb30 Furthermore, there is a possible use-after-free while iterating, 'node' can be free'd so we need to cache the next value to use. Fixes: c9e6978e2725 ("netfilter: nft_set_rbtree: Switch to node list walk for overlap detection") Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nft_set_rbtree.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 19ea4d3c3553..2f114aa10f1a 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -221,7 +221,7 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, { struct nft_set *set = (struct nft_set *)__set; struct rb_node *prev = rb_prev(&rbe->node); - struct nft_rbtree_elem *rbe_prev; + struct nft_rbtree_elem *rbe_prev = NULL; struct nft_set_gc_batch *gcb; gcb = nft_set_gc_batch_check(set, NULL, GFP_ATOMIC); @@ -229,17 +229,21 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, return -ENOMEM; /* search for expired end interval coming before this element. */ - do { + while (prev) { rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node); if (nft_rbtree_interval_end(rbe_prev)) break; prev = rb_prev(prev); - } while (prev != NULL); + } + + if (rbe_prev) { + rb_erase(&rbe_prev->node, &priv->root); + atomic_dec(&set->nelems); + } - rb_erase(&rbe_prev->node, &priv->root); rb_erase(&rbe->node, &priv->root); - atomic_sub(2, &set->nelems); + atomic_dec(&set->nelems); nft_set_gc_batch_add(gcb, rbe); nft_set_gc_batch_complete(gcb); @@ -268,7 +272,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_set_ext **ext) { struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL; - struct rb_node *node, *parent, **p, *first = NULL; + struct rb_node *node, *next, *parent, **p, *first = NULL; struct nft_rbtree *priv = nft_set_priv(set); u8 genmask = nft_genmask_next(net); int d, err; @@ -307,7 +311,9 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, * Values stored in the tree are in reversed order, starting from * highest to lowest value. */ - for (node = first; node != NULL; node = rb_next(node)) { + for (node = first; node != NULL; node = next) { + next = rb_next(node); + rbe = rb_entry(node, struct nft_rbtree_elem, node); if (!nft_set_elem_active(&rbe->ext, genmask)) -- cgit 1.4.1 From d319fe244e87dd8bb9d829593557797ea4d3dcb9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 21:45:35 +0200 Subject: bridge: always declare tunnel functions [ Upstream commit 89dcd87ce534a3a7f267cfd58505803006f51301 ] When CONFIG_BRIDGE_VLAN_FILTERING is disabled, two functions are still defined but have no prototype or caller. This causes a W=1 warning for the missing prototypes: net/bridge/br_netlink_tunnel.c:29:6: error: no previous prototype for 'vlan_tunid_inrange' [-Werror=missing-prototypes] net/bridge/br_netlink_tunnel.c:199:5: error: no previous prototype for 'br_vlan_tunnel_info' [-Werror=missing-prototypes] The functions are already contitional on CONFIG_BRIDGE_VLAN_FILTERING, and I coulnd't easily figure out the right set of #ifdefs, so just move the declarations out of the #ifdef to avoid the warning, at a small cost in code size over a more elaborate fix. Fixes: 188c67dd1906 ("net: bridge: vlan options: add support for tunnel id dumping") Fixes: 569da0822808 ("net: bridge: vlan options: add support for tunnel mapping set/del") Signed-off-by: Arnd Bergmann Acked-by: Nikolay Aleksandrov Link: https://lore.kernel.org/r/20230516194625.549249-3-arnd@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_private_tunnel.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_private_tunnel.h b/net/bridge/br_private_tunnel.h index 2b053289f016..efb096025151 100644 --- a/net/bridge/br_private_tunnel.h +++ b/net/bridge/br_private_tunnel.h @@ -27,6 +27,10 @@ int br_process_vlan_tunnel_info(const struct net_bridge *br, int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg); int br_fill_vlan_tunnel_info(struct sk_buff *skb, struct net_bridge_vlan_group *vg); +bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr, + const struct net_bridge_vlan *v_last); +int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd, + u16 vid, u32 tun_id, bool *changed); #ifdef CONFIG_BRIDGE_VLAN_FILTERING /* br_vlan_tunnel.c */ @@ -43,10 +47,6 @@ void br_handle_ingress_vlan_tunnel(struct sk_buff *skb, struct net_bridge_vlan_group *vg); int br_handle_egress_vlan_tunnel(struct sk_buff *skb, struct net_bridge_vlan *vlan); -bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *v_last); -int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd, - u16 vid, u32 tun_id, bool *changed); #else static inline int vlan_tunnel_init(struct net_bridge_vlan_group *vg) { -- cgit 1.4.1 From 2cd7d88fcb1ebd20a83bd43e663b1e88be69d80f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 12 May 2023 09:58:58 +0200 Subject: ALSA: usb-audio: Add a sample rate workaround for Line6 Pod Go commit 359b4315471181f108723c61612d96e383e56179 upstream. Line6 Pod Go (0e41:424b) requires the similar workaround for the fixed 48k sample rate like other Line6 models. This patch adds the corresponding entry to line6_parse_audio_format_rate_quirk(). Reported-by: John Humlick Cc: Link: https://lore.kernel.org/r/20230512075858.22813-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/format.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/format.c b/sound/usb/format.c index 4b1c5ba121f3..ab5fed9f55b6 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -423,6 +423,7 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip, case USB_ID(0x0e41, 0x4248): /* Line6 Helix >= fw 2.82 */ case USB_ID(0x0e41, 0x4249): /* Line6 Helix Rack >= fw 2.82 */ case USB_ID(0x0e41, 0x424a): /* Line6 Helix LT >= fw 2.82 */ + case USB_ID(0x0e41, 0x424b): /* Line6 Pod Go */ case USB_ID(0x19f7, 0x0011): /* Rode Rodecaster Pro */ return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000); } -- cgit 1.4.1 From 0ced12bdf624d8d8977ddb16eb130cd479d92bcf Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 1 May 2023 14:22:35 -0400 Subject: USB: usbtmc: Fix direction for 0-length ioctl control messages commit 94d25e9128988c6a1fc9070f6e98215a95795bd8 upstream. The syzbot fuzzer found a problem in the usbtmc driver: When a user submits an ioctl for a 0-length control transfer, the driver does not check that the direction is set to OUT: ------------[ cut here ]------------ usb 3-1: BOGUS control dir, pipe 80000b80 doesn't match bRequestType fd WARNING: CPU: 0 PID: 5100 at drivers/usb/core/urb.c:411 usb_submit_urb+0x14a7/0x1880 drivers/usb/core/urb.c:411 Modules linked in: CPU: 0 PID: 5100 Comm: syz-executor428 Not tainted 6.3.0-syzkaller-12049-g58390c8ce1bd #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/14/2023 RIP: 0010:usb_submit_urb+0x14a7/0x1880 drivers/usb/core/urb.c:411 Code: 7c 24 40 e8 1b 13 5c fb 48 8b 7c 24 40 e8 21 1d f0 fe 45 89 e8 44 89 f1 4c 89 e2 48 89 c6 48 c7 c7 e0 b5 fc 8a e8 19 c8 23 fb <0f> 0b e9 9f ee ff ff e8 ed 12 5c fb 0f b6 1d 12 8a 3c 08 31 ff 41 RSP: 0018:ffffc90003d2fb00 EFLAGS: 00010282 RAX: 0000000000000000 RBX: ffff8880789e9058 RCX: 0000000000000000 RDX: ffff888029593b80 RSI: ffffffff814c1447 RDI: 0000000000000001 RBP: ffff88801ea742f8 R08: 0000000000000001 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000001 R12: ffff88802915e528 R13: 00000000000000fd R14: 0000000080000b80 R15: ffff8880222b3100 FS: 0000555556ca63c0(0000) GS:ffff8880b9800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f9ef4d18150 CR3: 0000000073e5b000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: usb_start_wait_urb+0x101/0x4b0 drivers/usb/core/message.c:58 usb_internal_control_msg drivers/usb/core/message.c:102 [inline] usb_control_msg+0x320/0x4a0 drivers/usb/core/message.c:153 usbtmc_ioctl_request drivers/usb/class/usbtmc.c:1954 [inline] usbtmc_ioctl+0x1b3d/0x2840 drivers/usb/class/usbtmc.c:2097 To fix this, we must override the direction in the bRequestType field of the control request structure when the length is 0. Reported-and-tested-by: syzbot+ce77725b89b7bd52425c@syzkaller.appspotmail.com Signed-off-by: Alan Stern Link: https://lore.kernel.org/linux-usb/000000000000716a3705f9adb8ee@google.com/ CC: Link: https://lore.kernel.org/r/ede1ee02-b718-49e7-a44c-51339fec706b@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 4bb6d304eb4b..311007b1d904 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1928,6 +1928,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, if (request.req.wLength > USBTMC_BUFSIZE) return -EMSGSIZE; + if (request.req.wLength == 0) /* Length-0 requests are never IN */ + request.req.bRequestType &= ~USB_DIR_IN; is_in = request.req.bRequestType & USB_DIR_IN; -- cgit 1.4.1 From 4c3312745ffb92fde037ee874a78b5787adbc32c Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Fri, 5 May 2023 13:47:59 +0200 Subject: usb-storage: fix deadlock when a scsi command timeouts more than once commit a398d5eac6984316e71474e25b975688f282379b upstream. With faulty usb-storage devices, read/write can timeout, in that case the SCSI layer will abort and re-issue the command. USB storage has no internal timeout, it relies on SCSI layer aborting commands via .eh_abort_handler() for non those responsive devices. After two consecutive timeouts of the same command, SCSI layer calls .eh_device_reset_handler(), without calling .eh_abort_handler() first. With usb-storage, this causes a deadlock: -> .eh_device_reset_handler -> device_reset -> mutex_lock(&(us->dev_mutex)); mutex already by usb_stor_control_thread(), which is waiting for command completion: -> usb_stor_control_thread (mutex taken here) -> usb_stor_invoke_transport -> usb_stor_Bulk_transport -> usb_stor_bulk_srb -> usb_stor_bulk_transfer_sglist -> usb_sg_wait Make sure we cancel any pending command in .eh_device_reset_handler() to avoid this. Signed-off-by: Maxime Bizon Cc: linux-usb@vger.kernel.org Cc: stable Link: https://lore.kernel.org/all/ZEllnjMKT8ulZbJh@sakura/ Reviewed-by: Alan Stern Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230505114759.1189741-1-mbizon@freebox.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 8931df5a85fd..c54e9805da53 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -406,22 +406,25 @@ static DEF_SCSI_QCMD(queuecommand) ***********************************************************************/ /* Command timeout and abort */ -static int command_abort(struct scsi_cmnd *srb) +static int command_abort_matching(struct us_data *us, struct scsi_cmnd *srb_match) { - struct us_data *us = host_to_us(srb->device->host); - - usb_stor_dbg(us, "%s called\n", __func__); - /* * us->srb together with the TIMED_OUT, RESETTING, and ABORTING * bits are protected by the host lock. */ scsi_lock(us_to_host(us)); - /* Is this command still active? */ - if (us->srb != srb) { + /* is there any active pending command to abort ? */ + if (!us->srb) { scsi_unlock(us_to_host(us)); usb_stor_dbg(us, "-- nothing to abort\n"); + return SUCCESS; + } + + /* Does the command match the passed srb if any ? */ + if (srb_match && us->srb != srb_match) { + scsi_unlock(us_to_host(us)); + usb_stor_dbg(us, "-- pending command mismatch\n"); return FAILED; } @@ -444,6 +447,14 @@ static int command_abort(struct scsi_cmnd *srb) return SUCCESS; } +static int command_abort(struct scsi_cmnd *srb) +{ + struct us_data *us = host_to_us(srb->device->host); + + usb_stor_dbg(us, "%s called\n", __func__); + return command_abort_matching(us, srb); +} + /* * This invokes the transport reset mechanism to reset the state of the * device @@ -455,6 +466,9 @@ static int device_reset(struct scsi_cmnd *srb) usb_stor_dbg(us, "%s called\n", __func__); + /* abort any pending command before reset */ + command_abort_matching(us, NULL); + /* lock the device pointers and do the reset */ mutex_lock(&(us->dev_mutex)); result = us->transport_reset(us); -- cgit 1.4.1 From 56a0769fa40a0c6d07792e1cb980e22a5da34523 Mon Sep 17 00:00:00 2001 From: Weitao Wang Date: Sun, 23 Apr 2023 18:59:52 +0800 Subject: USB: UHCI: adjust zhaoxin UHCI controllers OverCurrent bit value commit dddb342b5b9e482bb213aecc08cbdb201ea4f8da upstream. OverCurrent condition is not standardized in the UHCI spec. Zhaoxin UHCI controllers report OverCurrent bit active off. In order to handle OverCurrent condition correctly, the uhci-hcd driver needs to be told to expect the active-off behavior. Suggested-by: Alan Stern Cc: stable@vger.kernel.org Signed-off-by: Weitao Wang Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230423105952.4526-1-WeitaoWang-oc@zhaoxin.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-pci.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 3592f757fe05..7bd2fddde770 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -119,11 +119,13 @@ static int uhci_pci_init(struct usb_hcd *hcd) uhci->rh_numports = uhci_count_ports(hcd); - /* Intel controllers report the OverCurrent bit active on. - * VIA controllers report it active off, so we'll adjust the - * bit value. (It's not standardized in the UHCI spec.) + /* + * Intel controllers report the OverCurrent bit active on. VIA + * and ZHAOXIN controllers report it active off, so we'll adjust + * the bit value. (It's not standardized in the UHCI spec.) */ - if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA) + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA || + to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_ZHAOXIN) uhci->oc_low = 1; /* HP's server management chip requires a longer port reset delay. */ -- cgit 1.4.1 From ad43004fd5326bec4466ecc8a07fe0e570b553ca Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 3 May 2023 14:00:48 +0300 Subject: usb: dwc3: gadget: Improve dwc3_gadget_suspend() and dwc3_gadget_resume() commit c8540870af4ce6ddeb27a7bb5498b75fb29b643c upstream. Prevent -ETIMEDOUT error on .suspend(). e.g. If gadget driver is loaded and we are connected to a USB host, all transfers must be stopped before stopping the controller else we will not get a clean stop i.e. dwc3_gadget_run_stop() will take several seconds to complete and will return -ETIMEDOUT. Handle error cases properly in dwc3_gadget_suspend(). Simplify dwc3_gadget_resume() by using the introduced helper function. Fixes: 9f8a67b65a49 ("usb: dwc3: gadget: fix gadget suspend/resume") Cc: stable@vger.kernel.org Suggested-by: Thinh Nguyen Signed-off-by: Roger Quadros Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230503110048.30617-1-rogerq@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 67 ++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index daa767383355..d12cb8f0d1f4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2587,6 +2587,21 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) return ret; } +static int dwc3_gadget_soft_connect(struct dwc3 *dwc) +{ + /* + * In the Synopsys DWC_usb31 1.90a programming guide section + * 4.1.9, it specifies that for a reconnect after a + * device-initiated disconnect requires a core soft reset + * (DCTL.CSftRst) before enabling the run/stop bit. + */ + dwc3_core_soft_reset(dwc); + + dwc3_event_buffers_setup(dwc); + __dwc3_gadget_start(dwc); + return dwc3_gadget_run_stop(dwc, true); +} + static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) { struct dwc3 *dwc = gadget_to_dwc(g); @@ -2625,21 +2640,10 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) synchronize_irq(dwc->irq_gadget); - if (!is_on) { + if (!is_on) ret = dwc3_gadget_soft_disconnect(dwc); - } else { - /* - * In the Synopsys DWC_usb31 1.90a programming guide section - * 4.1.9, it specifies that for a reconnect after a - * device-initiated disconnect requires a core soft reset - * (DCTL.CSftRst) before enabling the run/stop bit. - */ - dwc3_core_soft_reset(dwc); - - dwc3_event_buffers_setup(dwc); - __dwc3_gadget_start(dwc); - ret = dwc3_gadget_run_stop(dwc, true); - } + else + ret = dwc3_gadget_soft_connect(dwc); pm_runtime_put(dwc->dev); @@ -4555,42 +4559,39 @@ void dwc3_gadget_exit(struct dwc3 *dwc) int dwc3_gadget_suspend(struct dwc3 *dwc) { unsigned long flags; + int ret; if (!dwc->gadget_driver) return 0; - dwc3_gadget_run_stop(dwc, false); + ret = dwc3_gadget_soft_disconnect(dwc); + if (ret) + goto err; spin_lock_irqsave(&dwc->lock, flags); dwc3_disconnect_gadget(dwc); - __dwc3_gadget_stop(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return 0; + +err: + /* + * Attempt to reset the controller's state. Likely no + * communication can be established until the host + * performs a port reset. + */ + if (dwc->softconnect) + dwc3_gadget_soft_connect(dwc); + + return ret; } int dwc3_gadget_resume(struct dwc3 *dwc) { - int ret; - if (!dwc->gadget_driver || !dwc->softconnect) return 0; - ret = __dwc3_gadget_start(dwc); - if (ret < 0) - goto err0; - - ret = dwc3_gadget_run_stop(dwc, true); - if (ret < 0) - goto err1; - - return 0; - -err1: - __dwc3_gadget_stop(dwc); - -err0: - return ret; + return dwc3_gadget_soft_connect(dwc); } void dwc3_gadget_process_pending_events(struct dwc3 *dwc) -- cgit 1.4.1 From a9342bd4c29b79de40a9e9af00a02595551bc9f8 Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Tue, 9 May 2023 20:18:36 +0530 Subject: usb: dwc3: debugfs: Resume dwc3 before accessing registers commit 614ce6a2ea50068b45339257891e51e639ac9001 upstream. When the dwc3 device is runtime suspended, various required clocks are in disabled state and it is not guaranteed that access to any registers would work. Depending on the SoC glue, a register read could be as benign as returning 0 or be fatal enough to hang the system. In order to prevent such scenarios of fatal errors, make sure to resume dwc3 then allow the function to proceed. Fixes: 72246da40f37 ("usb: Introduce DesignWare USB3 DRD Driver") Cc: stable@vger.kernel.org #3.2: 30332eeefec8: debugfs: regset32: Add Runtime PM support Signed-off-by: Udipto Goswami Reviewed-by: Johan Hovold Tested-by: Johan Hovold Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230509144836.6803-1-quic_ugoswami@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/debugfs.c | 109 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 850df0e6bcab..f0ffd2e5c642 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -327,6 +327,11 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused) unsigned int current_mode; unsigned long flags; u32 reg; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); reg = dwc3_readl(dwc->regs, DWC3_GSTS); @@ -345,6 +350,8 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused) } spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -390,6 +397,11 @@ static int dwc3_mode_show(struct seq_file *s, void *unused) struct dwc3 *dwc = s->private; unsigned long flags; u32 reg; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); reg = dwc3_readl(dwc->regs, DWC3_GCTL); @@ -409,6 +421,8 @@ static int dwc3_mode_show(struct seq_file *s, void *unused) seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg)); } + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -458,6 +472,11 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused) struct dwc3 *dwc = s->private; unsigned long flags; u32 reg; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); reg = dwc3_readl(dwc->regs, DWC3_DCTL); @@ -488,6 +507,8 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused) seq_printf(s, "UNKNOWN %d\n", reg); } + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -504,6 +525,7 @@ static ssize_t dwc3_testmode_write(struct file *file, unsigned long flags; u32 testmode = 0; char buf[32]; + int ret; if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; @@ -521,10 +543,16 @@ static ssize_t dwc3_testmode_write(struct file *file, else testmode = 0; + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; + spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_set_test_mode(dwc, testmode); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return count; } @@ -543,12 +571,18 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) enum dwc3_link_state state; u32 reg; u8 speed; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); reg = dwc3_readl(dwc->regs, DWC3_GSTS); if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { seq_puts(s, "Not available\n"); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); return 0; } @@ -561,6 +595,8 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) dwc3_gadget_hs_link_string(state)); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -579,6 +615,7 @@ static ssize_t dwc3_link_state_write(struct file *file, char buf[32]; u32 reg; u8 speed; + int ret; if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; @@ -598,10 +635,15 @@ static ssize_t dwc3_link_state_write(struct file *file, else return -EINVAL; + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; + spin_lock_irqsave(&dwc->lock, flags); reg = dwc3_readl(dwc->regs, DWC3_GSTS); if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); return -EINVAL; } @@ -611,12 +653,15 @@ static ssize_t dwc3_link_state_write(struct file *file, if (speed < DWC3_DSTS_SUPERSPEED && state != DWC3_LINK_STATE_RECOV) { spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); return -EINVAL; } dwc3_gadget_set_link_state(dwc, state); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return count; } @@ -640,6 +685,11 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) unsigned long flags; u32 mdwidth; u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_TXFIFO); @@ -652,6 +702,8 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -662,6 +714,11 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) unsigned long flags; u32 mdwidth; u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_RXFIFO); @@ -674,6 +731,8 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -683,12 +742,19 @@ static int dwc3_tx_request_queue_show(struct seq_file *s, void *unused) struct dwc3 *dwc = dep->dwc; unsigned long flags; u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_TXREQQ); seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -698,12 +764,19 @@ static int dwc3_rx_request_queue_show(struct seq_file *s, void *unused) struct dwc3 *dwc = dep->dwc; unsigned long flags; u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_RXREQQ); seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -713,12 +786,19 @@ static int dwc3_rx_info_queue_show(struct seq_file *s, void *unused) struct dwc3 *dwc = dep->dwc; unsigned long flags; u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ); seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -728,12 +808,19 @@ static int dwc3_descriptor_fetch_queue_show(struct seq_file *s, void *unused) struct dwc3 *dwc = dep->dwc; unsigned long flags; u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ); seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -743,12 +830,19 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused) struct dwc3 *dwc = dep->dwc; unsigned long flags; u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_EVENTQ); seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -793,6 +887,11 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused) struct dwc3 *dwc = dep->dwc; unsigned long flags; int i; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); if (dep->number <= 1) { @@ -822,6 +921,8 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused) out: spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -834,6 +935,11 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused) u32 lower_32_bits; u32 upper_32_bits; u32 reg; + int ret; + + ret = pm_runtime_resume_and_get(dwc->dev); + if (ret < 0) + return ret; spin_lock_irqsave(&dwc->lock, flags); reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number); @@ -846,6 +952,8 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused) seq_printf(s, "0x%016llx\n", ep_info); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_sync(dwc->dev); + return 0; } @@ -905,6 +1013,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc) dwc->regset->regs = dwc3_regs; dwc->regset->nregs = ARRAY_SIZE(dwc3_regs); dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START; + dwc->regset->dev = dwc->dev; root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root); dwc->debug_root = root; -- cgit 1.4.1 From f1f810e54163f7fec5df9a123616c18d28295219 Mon Sep 17 00:00:00 2001 From: Konrad Gräfe Date: Fri, 5 May 2023 16:36:40 +0200 Subject: usb: gadget: u_ether: Fix host MAC address case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3c0f4f09c063e143822393d99cb2b19a85451c07 upstream. The CDC-ECM specification [1] requires to send the host MAC address as an uppercase hexadecimal string in chapter "5.4 Ethernet Networking Functional Descriptor": The Unicode character is chosen from the set of values 30h through 39h and 41h through 46h (0-9 and A-F). However, snprintf(.., "%pm", ..) generates a lowercase MAC address string. While most host drivers are tolerant to this, UsbNcm.sys on Windows 10 is not. Instead it uses a different MAC address with all bytes set to zero including and after the first byte containing a lowercase letter. On Windows 11 Microsoft fixed it, but apparently they did not backport the fix. This change fixes the issue by upper-casing the MAC to comply with the specification. [1]: https://www.usb.org/document-library/class-definitions-communication-devices-12, file ECM120.pdf Fixes: bcd4a1c40bee ("usb: gadget: u_ether: construct with default values and add setters/getters") Cc: stable@vger.kernel.org Signed-off-by: Konrad Gräfe Link: https://lore.kernel.org/r/20230505143640.443014-1-k.graefe@gateware.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ether.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index e06022873df1..1f420ff8f423 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "u_ether.h" @@ -978,6 +979,8 @@ int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len) dev = netdev_priv(net); snprintf(host_addr, len, "%pm", dev->host_mac); + string_upper(host_addr, host_addr); + return strlen(host_addr); } EXPORT_SYMBOL_GPL(gether_get_host_addr_cdc); -- cgit 1.4.1 From 08bd1be1c716fd50a7df48f82dcbc59a103082b5 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 8 May 2023 21:44:43 +0000 Subject: usb: typec: altmodes/displayport: fix pin_assignment_show commit d8f28269dd4bf9b55c3fb376ae31512730a96fce upstream. This patch fixes negative indexing of buf array in pin_assignment_show when get_current_pin_assignments returns 0 i.e. no compatible pin assignments are found. BUG: KASAN: use-after-free in pin_assignment_show+0x26c/0x33c ... Call trace: dump_backtrace+0x110/0x204 dump_stack_lvl+0x84/0xbc print_report+0x358/0x974 kasan_report+0x9c/0xfc __do_kernel_fault+0xd4/0x2d4 do_bad_area+0x48/0x168 do_tag_check_fault+0x24/0x38 do_mem_abort+0x6c/0x14c el1_abort+0x44/0x68 el1h_64_sync_handler+0x64/0xa4 el1h_64_sync+0x78/0x7c pin_assignment_show+0x26c/0x33c dev_attr_show+0x50/0xc0 Fixes: 0e3bb7d6894d ("usb: typec: Add driver for DisplayPort alternate mode") Cc: stable@vger.kernel.org Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230508214443.893436-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 4075c0d7e6a2..7cdf83f4c811 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -513,6 +513,10 @@ static ssize_t pin_assignment_show(struct device *dev, mutex_unlock(&dp->lock); + /* get_current_pin_assignments can return 0 when no matching pin assignments are found */ + if (len == 0) + len++; + buf[len - 1] = '\n'; return len; } -- cgit 1.4.1 From 7356d42ceffe4fab6f396511f4b73bcca40f6ee7 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Fri, 12 May 2023 15:14:34 +0200 Subject: Revert "usb: gadget: udc: core: Prevent redundant calls to pullup" commit 5e1617210aede9f1b91bb9819c93097b6da481f9 upstream. This reverts commit a3afbf5cc887fc3401f012fe629810998ed61859. This depends on commit 0db213ea8eed ("usb: gadget: udc: core: Invoke usb_gadget_connect only when started") that introduces a regression, revert it till the issue is fixed. Cc: stable@vger.kernel.org Reported-by: Stephan Gerhold Reported-by: Francesco Dolcini Link: https://lore.kernel.org/all/ZF4BvgsOyoKxdPFF@francesco-nb.int.toradex.com/ Signed-off-by: Francesco Dolcini Link: https://lore.kernel.org/r/20230512131435.205464-2-francesco@dolcini.it Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index e85706812d61..016d03e57a63 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -676,9 +676,6 @@ static int usb_gadget_connect_locked(struct usb_gadget *gadget) goto out; } - if (gadget->connected) - goto out; - if (gadget->deactivated || !gadget->udc->started) { /* * If gadget is deactivated we only save new state. -- cgit 1.4.1 From ea56ede9117808d706318c3817a09dc1fe897a36 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Fri, 12 May 2023 15:14:35 +0200 Subject: Revert "usb: gadget: udc: core: Invoke usb_gadget_connect only when started" commit f22e9b67f19ccc73de1ae04375d4b30684e261f8 upstream. This reverts commit 0db213ea8eed5534a5169e807f28103cbc9d23df. It introduces an issues with configuring the USB gadget hangs forever on multiple Qualcomm and NXP i.MX SoC at least. Cc: stable@vger.kernel.org Fixes: 0db213ea8eed ("usb: gadget: udc: core: Invoke usb_gadget_connect only when started") Reported-by: Stephan Gerhold Reported-by: Francesco Dolcini Link: https://lore.kernel.org/all/ZF4BvgsOyoKxdPFF@francesco-nb.int.toradex.com/ Signed-off-by: Francesco Dolcini Link: https://lore.kernel.org/r/20230512131435.205464-3-francesco@dolcini.it Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 148 +++++++++++++----------------------------- 1 file changed, 44 insertions(+), 104 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 016d03e57a63..bf9878e1a72a 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -37,10 +37,6 @@ static struct bus_type gadget_bus_type; * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true * @started: the UDC's started state. True if the UDC had started. - * @connect_lock: protects udc->vbus, udc->started, gadget->connect, gadget->deactivate related - * functions. usb_gadget_connect_locked, usb_gadget_disconnect_locked, - * usb_udc_connect_control_locked, usb_gadget_udc_start_locked, usb_gadget_udc_stop_locked are - * called with this lock held. * * This represents the internal data structure which is used by the UDC-class * to hold information about udc driver and gadget together. @@ -52,7 +48,6 @@ struct usb_udc { struct list_head list; bool vbus; bool started; - struct mutex connect_lock; }; static struct class *udc_class; @@ -665,9 +660,17 @@ out: } EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); -/* Internal version of usb_gadget_connect needs to be called with connect_lock held. */ -static int usb_gadget_connect_locked(struct usb_gadget *gadget) - __must_hold(&gadget->udc->connect_lock) +/** + * usb_gadget_connect - software-controlled connect to USB host + * @gadget:the peripheral being connected + * + * Enables the D+ (or potentially D-) pullup. The host will start + * enumerating this gadget when the pullup is active and a VBUS session + * is active (the link is powered). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_connect(struct usb_gadget *gadget) { int ret = 0; @@ -676,12 +679,10 @@ static int usb_gadget_connect_locked(struct usb_gadget *gadget) goto out; } - if (gadget->deactivated || !gadget->udc->started) { + if (gadget->deactivated) { /* * If gadget is deactivated we only save new state. * Gadget will be connected automatically after activation. - * - * udc first needs to be started before gadget can be pulled up. */ gadget->connected = true; goto out; @@ -696,32 +697,22 @@ out: return ret; } +EXPORT_SYMBOL_GPL(usb_gadget_connect); /** - * usb_gadget_connect - software-controlled connect to USB host - * @gadget:the peripheral being connected + * usb_gadget_disconnect - software-controlled disconnect from USB host + * @gadget:the peripheral being disconnected * - * Enables the D+ (or potentially D-) pullup. The host will start - * enumerating this gadget when the pullup is active and a VBUS session - * is active (the link is powered). + * Disables the D+ (or potentially D-) pullup, which the host may see + * as a disconnect (when a VBUS session is active). Not all systems + * support software pullup controls. + * + * Following a successful disconnect, invoke the ->disconnect() callback + * for the current gadget driver so that UDC drivers don't need to. * * Returns zero on success, else negative errno. */ -int usb_gadget_connect(struct usb_gadget *gadget) -{ - int ret; - - mutex_lock(&gadget->udc->connect_lock); - ret = usb_gadget_connect_locked(gadget); - mutex_unlock(&gadget->udc->connect_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_connect); - -/* Internal version of usb_gadget_disconnect needs to be called with connect_lock held. */ -static int usb_gadget_disconnect_locked(struct usb_gadget *gadget) - __must_hold(&gadget->udc->connect_lock) +int usb_gadget_disconnect(struct usb_gadget *gadget) { int ret = 0; @@ -733,12 +724,10 @@ static int usb_gadget_disconnect_locked(struct usb_gadget *gadget) if (!gadget->connected) goto out; - if (gadget->deactivated || !gadget->udc->started) { + if (gadget->deactivated) { /* * If gadget is deactivated we only save new state. * Gadget will stay disconnected after activation. - * - * udc should have been started before gadget being pulled down. */ gadget->connected = false; goto out; @@ -758,30 +747,6 @@ out: return ret; } - -/** - * usb_gadget_disconnect - software-controlled disconnect from USB host - * @gadget:the peripheral being disconnected - * - * Disables the D+ (or potentially D-) pullup, which the host may see - * as a disconnect (when a VBUS session is active). Not all systems - * support software pullup controls. - * - * Following a successful disconnect, invoke the ->disconnect() callback - * for the current gadget driver so that UDC drivers don't need to. - * - * Returns zero on success, else negative errno. - */ -int usb_gadget_disconnect(struct usb_gadget *gadget) -{ - int ret; - - mutex_lock(&gadget->udc->connect_lock); - ret = usb_gadget_disconnect_locked(gadget); - mutex_unlock(&gadget->udc->connect_lock); - - return ret; -} EXPORT_SYMBOL_GPL(usb_gadget_disconnect); /** @@ -802,11 +767,10 @@ int usb_gadget_deactivate(struct usb_gadget *gadget) if (gadget->deactivated) goto out; - mutex_lock(&gadget->udc->connect_lock); if (gadget->connected) { - ret = usb_gadget_disconnect_locked(gadget); + ret = usb_gadget_disconnect(gadget); if (ret) - goto unlock; + goto out; /* * If gadget was being connected before deactivation, we want @@ -816,8 +780,6 @@ int usb_gadget_deactivate(struct usb_gadget *gadget) } gadget->deactivated = true; -unlock: - mutex_unlock(&gadget->udc->connect_lock); out: trace_usb_gadget_deactivate(gadget, ret); @@ -841,7 +803,6 @@ int usb_gadget_activate(struct usb_gadget *gadget) if (!gadget->deactivated) goto out; - mutex_lock(&gadget->udc->connect_lock); gadget->deactivated = false; /* @@ -849,8 +810,7 @@ int usb_gadget_activate(struct usb_gadget *gadget) * while it was being deactivated, we call usb_gadget_connect(). */ if (gadget->connected) - ret = usb_gadget_connect_locked(gadget); - mutex_unlock(&gadget->udc->connect_lock); + ret = usb_gadget_connect(gadget); out: trace_usb_gadget_activate(gadget, ret); @@ -1091,13 +1051,12 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); /* ------------------------------------------------------------------------- */ -/* Acquire connect_lock before calling this function. */ -static void usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock) +static void usb_udc_connect_control(struct usb_udc *udc) { - if (udc->vbus && udc->started) - usb_gadget_connect_locked(udc->gadget); + if (udc->vbus) + usb_gadget_connect(udc->gadget); else - usb_gadget_disconnect_locked(udc->gadget); + usb_gadget_disconnect(udc->gadget); } /** @@ -1113,12 +1072,10 @@ void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) { struct usb_udc *udc = gadget->udc; - mutex_lock(&udc->connect_lock); if (udc) { udc->vbus = status; - usb_udc_connect_control_locked(udc); + usb_udc_connect_control(udc); } - mutex_unlock(&udc->connect_lock); } EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); @@ -1140,7 +1097,7 @@ void usb_gadget_udc_reset(struct usb_gadget *gadget, EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); /** - * usb_gadget_udc_start_locked - tells usb device controller to start up + * usb_gadget_udc_start - tells usb device controller to start up * @udc: The UDC to be started * * This call is issued by the UDC Class driver when it's about @@ -1151,11 +1108,8 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); * necessary to have it powered on. * * Returns zero on success, else negative errno. - * - * Caller should acquire connect_lock before invoking this function. */ -static inline int usb_gadget_udc_start_locked(struct usb_udc *udc) - __must_hold(&udc->connect_lock) +static inline int usb_gadget_udc_start(struct usb_udc *udc) { int ret; @@ -1172,7 +1126,7 @@ static inline int usb_gadget_udc_start_locked(struct usb_udc *udc) } /** - * usb_gadget_udc_stop_locked - tells usb device controller we don't need it anymore + * usb_gadget_udc_stop - tells usb device controller we don't need it anymore * @udc: The UDC to be stopped * * This call is issued by the UDC Class driver after calling @@ -1181,11 +1135,8 @@ static inline int usb_gadget_udc_start_locked(struct usb_udc *udc) * The details are implementation specific, but it can go as * far as powering off UDC completely and disable its data * line pullups. - * - * Caller should acquire connect lock before invoking this function. */ -static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc) - __must_hold(&udc->connect_lock) +static inline void usb_gadget_udc_stop(struct usb_udc *udc) { if (!udc->started) { dev_err(&udc->dev, "UDC had already stopped\n"); @@ -1344,7 +1295,6 @@ int usb_add_gadget(struct usb_gadget *gadget) udc->gadget = gadget; gadget->udc = udc; - mutex_init(&udc->connect_lock); udc->started = false; @@ -1546,15 +1496,11 @@ static int gadget_bind_driver(struct device *dev) if (ret) goto err_bind; - mutex_lock(&udc->connect_lock); - ret = usb_gadget_udc_start_locked(udc); - if (ret) { - mutex_unlock(&udc->connect_lock); + ret = usb_gadget_udc_start(udc); + if (ret) goto err_start; - } usb_gadget_enable_async_callbacks(udc); - usb_udc_connect_control_locked(udc); - mutex_unlock(&udc->connect_lock); + usb_udc_connect_control(udc); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; @@ -1585,14 +1531,12 @@ static void gadget_unbind_driver(struct device *dev) kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - mutex_lock(&udc->connect_lock); - usb_gadget_disconnect_locked(gadget); + usb_gadget_disconnect(gadget); usb_gadget_disable_async_callbacks(udc); if (gadget->irq) synchronize_irq(gadget->irq); udc->driver->unbind(gadget); - usb_gadget_udc_stop_locked(udc); - mutex_unlock(&udc->connect_lock); + usb_gadget_udc_stop(udc); mutex_lock(&udc_lock); driver->is_bound = false; @@ -1678,15 +1622,11 @@ static ssize_t soft_connect_store(struct device *dev, } if (sysfs_streq(buf, "connect")) { - mutex_lock(&udc->connect_lock); - usb_gadget_udc_start_locked(udc); - usb_gadget_connect_locked(udc->gadget); - mutex_unlock(&udc->connect_lock); + usb_gadget_udc_start(udc); + usb_gadget_connect(udc->gadget); } else if (sysfs_streq(buf, "disconnect")) { - mutex_lock(&udc->connect_lock); - usb_gadget_disconnect_locked(udc->gadget); - usb_gadget_udc_stop_locked(udc); - mutex_unlock(&udc->connect_lock); + usb_gadget_disconnect(udc->gadget); + usb_gadget_udc_stop(udc); } else { dev_err(dev, "unsupported command '%s'\n", buf); ret = -EINVAL; -- cgit 1.4.1 From 643a45387896566ece928daf31554726b6cc3a27 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 15 May 2023 16:40:58 +0300 Subject: xhci-pci: Only run d3cold avoidance quirk for s2idle commit 2a821fc3136d5d99dcb9de152be8a052ca27d870 upstream. Donghun reports that a notebook that has an AMD Ryzen 5700U but supports S3 has problems with USB after resuming from suspend. The issue was bisected down to commit d1658268e439 ("usb: pci-quirks: disable D3cold on xhci suspend for s2idle on AMD Renoir"). As this issue only happens on S3, narrow the broken D3cold quirk to only run in s2idle. Fixes: d1658268e439 ("usb: pci-quirks: disable D3cold on xhci suspend for s2idle on AMD Renoir") Reported-and-tested-by: Donghun Yoon Cc: stable@vger.kernel.org Signed-off-by: Mario Limonciello Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230515134059.161110-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 12 ++++++++++-- drivers/usb/host/xhci.h | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 232e175e4e96..6e4dac71c409 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-trace.h" @@ -194,7 +195,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == PCI_DEVICE_ID_AMD_RENOIR_XHCI) - xhci->quirks |= XHCI_BROKEN_D3COLD; + xhci->quirks |= XHCI_BROKEN_D3COLD_S2I; if (pdev->vendor == PCI_VENDOR_ID_INTEL) { xhci->quirks |= XHCI_LPM_SUPPORT; @@ -609,9 +610,16 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) * Systems with the TI redriver that loses port status change events * need to have the registers polled during D3, so avoid D3cold. */ - if (xhci->quirks & (XHCI_COMP_MODE_QUIRK | XHCI_BROKEN_D3COLD)) + if (xhci->quirks & XHCI_COMP_MODE_QUIRK) pci_d3cold_disable(pdev); +#ifdef CONFIG_SUSPEND + /* d3cold is broken, but only when s2idle is used */ + if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE && + xhci->quirks & (XHCI_BROKEN_D3COLD_S2I)) + pci_d3cold_disable(pdev); +#endif + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_quirk(hcd); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index dcee7f3207ad..6348cacdc65e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1895,7 +1895,7 @@ struct xhci_hcd { #define XHCI_DISABLE_SPARSE BIT_ULL(38) #define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39) #define XHCI_NO_SOFT_RETRY BIT_ULL(40) -#define XHCI_BROKEN_D3COLD BIT_ULL(41) +#define XHCI_BROKEN_D3COLD_S2I BIT_ULL(41) #define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42) #define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43) #define XHCI_RESET_TO_DEFAULT BIT_ULL(44) -- cgit 1.4.1 From 44f2ed29e16dc9e725948cce8aa03409d2b71a09 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Mon, 15 May 2023 16:40:59 +0300 Subject: xhci: Fix incorrect tracking of free space on transfer rings commit fe82f16aafdaf8002281d3b9524291d4a4a28460 upstream. This incorrect tracking caused unnecessary ring expansion in some usecases which over days of use consume a lot of memory. xhci driver tries to keep track of free transfer blocks (TRBs) on the ring buffer, but failed to add back some cancelled transfers that were turned into no-op operations instead of just moving past them. This can happen if there are several queued pending transfers which then are cancelled in reverse order. Solve this by counting the numer of steps we move the dequeue pointer once we complete a transfer, and add it to the number of free trbs instead of just adding the trb number of the current transfer. This way we ensure we count the no-op trbs on the way as well. Fixes: 55f6153d8cc8 ("xhci: remove extra loop in interrupt context") Cc: stable@vger.kernel.org Reported-by: Miller Hunter Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217242 Tested-by: Miller Hunter Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230515134059.161110-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index dce02d0aad8d..7a7ab525675b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -276,6 +276,26 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, trace_xhci_inc_enq(ring); } +static int xhci_num_trbs_to(struct xhci_segment *start_seg, union xhci_trb *start, + struct xhci_segment *end_seg, union xhci_trb *end, + unsigned int num_segs) +{ + union xhci_trb *last_on_seg; + int num = 0; + int i = 0; + + do { + if (start_seg == end_seg && end >= start) + return num + (end - start); + last_on_seg = &start_seg->trbs[TRBS_PER_SEGMENT - 1]; + num += last_on_seg - start; + start_seg = start_seg->next; + start = start_seg->trbs; + } while (i++ <= num_segs); + + return -EINVAL; +} + /* * Check to see if there's room to enqueue num_trbs on the ring and make sure * enqueue pointer will not advance into dequeue segment. See rules above. @@ -2141,6 +2161,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, u32 trb_comp_code) { struct xhci_ep_ctx *ep_ctx; + int trbs_freed; ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index); @@ -2212,9 +2233,15 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, } /* Update ring dequeue pointer */ + trbs_freed = xhci_num_trbs_to(ep_ring->deq_seg, ep_ring->dequeue, + td->last_trb_seg, td->last_trb, + ep_ring->num_segs); + if (trbs_freed < 0) + xhci_dbg(xhci, "Failed to count freed trbs at TD finish\n"); + else + ep_ring->num_trbs_free += trbs_freed; ep_ring->dequeue = td->last_trb; ep_ring->deq_seg = td->last_trb_seg; - ep_ring->num_trbs_free += td->num_trbs - 1; inc_deq(xhci, ep_ring); return xhci_td_cleanup(xhci, td, ep_ring, td->status); -- cgit 1.4.1 From dc8c569d59f17b17d7bca4f68c36bd571659921e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 16 May 2023 20:44:12 +0200 Subject: ALSA: hda: Fix Oops by 9.1 surround channel names commit 3b44ec8c5c44790a82f07e90db45643c762878c6 upstream. get_line_out_pfx() may trigger an Oops by overflowing the static array with more than 8 channels. This was reported for MacBookPro 12,1 with Cirrus codec. As a workaround, extend for the 9.1 channels and also fix the potential Oops by unifying the code paths accessing the same array with the proper size check. Reported-by: Olliver Schinagl Cc: Link: https://lore.kernel.org/r/64d95eb0-dbdb-cff8-a8b1-988dc22b24cd@schinagl.nl Link: https://lore.kernel.org/r/20230516184412.24078-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_generic.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fc114e522480..dbf7aa88e0e3 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1155,8 +1155,8 @@ static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) return path && path->ctls[ctl_type]; } -static const char * const channel_name[4] = { - "Front", "Surround", "CLFE", "Side" +static const char * const channel_name[] = { + "Front", "Surround", "CLFE", "Side", "Back", }; /* give some appropriate ctl name prefix for the given line out channel */ @@ -1182,7 +1182,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, /* multi-io channels */ if (ch >= cfg->line_outs) - return channel_name[ch]; + goto fixed_name; switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: @@ -1234,6 +1234,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, if (cfg->line_outs == 1 && !spec->multi_ios) return "Line Out"; + fixed_name: if (ch >= ARRAY_SIZE(channel_name)) { snd_BUG(); return "PCM"; -- cgit 1.4.1 From 0e1e6c07795202f937b3791f2f8c71894e3db146 Mon Sep 17 00:00:00 2001 From: Nikhil Mahale Date: Wed, 17 May 2023 14:37:36 +0530 Subject: ALSA: hda: Add NVIDIA codec IDs a3 through a7 to patch table commit dc4f2ccaedddb489a83e7b12ebbdc347272aacc9 upstream. These IDs are for AD102, AD103, AD104, AD106, and AD107 gpus with audio functions that are largely similar to the existing ones. Tested audio using gnome-settings, over HDMI, DP-SST and DP-MST connections on AD106 gpu. Signed-off-by: Nikhil Mahale Cc: Link: https://lore.kernel.org/r/20230517090736.15088-1-nmahale@nvidia.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_hdmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5c6980394dce..be2c6cff7701 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4577,6 +4577,11 @@ HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a3, "GPU a3 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a4, "GPU a4 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a5, "GPU a5 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a6, "GPU a6 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a7, "GPU a7 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), -- cgit 1.4.1 From 30043b0a065c02c208b808dbac26717993c69461 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 5 May 2023 10:36:51 -0600 Subject: ALSA: hda/realtek: Add quirk for Clevo L140AU commit 0a6b36c5dc3dda0196f4fb65bdb34c38b8d060c3 upstream. Fixes headset detection on Clevo L140AU. Signed-off-by: Jeremy Soller Signed-off-by: Tim Crawford Cc: Link: https://lore.kernel.org/r/20230505163651.21257-1-tcrawford@system76.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5d78d4ba1c95..88ca5341782a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9618,6 +9618,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x7716, "Clevo NS50PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x7717, "Clevo NS70PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x7718, "Clevo L140PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x7724, "Clevo L140AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8228, "Clevo NR40BU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8520, "Clevo NH50D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8521, "Clevo NH77D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), -- cgit 1.4.1 From 9328c6569457daa2c5d58b9bce8c86a6f0fb985d Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Sat, 6 May 2023 10:26:53 +0800 Subject: ALSA: hda/realtek: Add a quirk for HP EliteDesk 805 commit 90670ef774a8b6700c38ce1222e6aa263be54d5f upstream. Add a quirk for HP EliteDesk 805 to fixup ALC3867 headset MIC no sound. Signed-off-by: Ai Chao Cc: Link: https://lore.kernel.org/r/20230506022653.2074343-1-aichao@kylinos.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 88ca5341782a..bfbbf588ad60 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11664,6 +11664,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB), SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB), SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), -- cgit 1.4.1 From 4ea7c3388f38814de3fc78758f547045542a4302 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sat, 6 May 2023 11:58:24 +1200 Subject: ALSA: hda/realtek: Add quirk for 2nd ASUS GU603 commit a4671b7fba59775845ee60cfbdfc4ba64300211b upstream. Add quirk for GU603 with 0x1c62 variant of codec. Signed-off-by: Luke D. Jones Cc: Link: https://lore.kernel.org/r/20230505235824.49607-2-luke@ljones.dev Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bfbbf588ad60..a2a3e4fc6167 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9522,6 +9522,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS), SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), -- cgit 1.4.1 From 0cc95fdb67bb90993e8f837225b8fe350b58f20b Mon Sep 17 00:00:00 2001 From: Vitaly Rodionov Date: Wed, 10 May 2023 15:22:27 +0100 Subject: ALSA: hda/realtek: Add quirk for HP EliteBook G10 laptops commit 3e10f6ca76c4d00019badebd235c9d7f0068261e upstream. Add support for HP EliteBook 835/845/845W/865 G10 laptops with CS35L41 amplifiers on I2C/SPI bus connected to Realtek codec. Signed-off-by: Vitaly Rodionov Cc: Link: https://lore.kernel.org/r/20230510142227.32945-1-vitalyr@opensource.cirrus.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a2a3e4fc6167..7a8d97926b1d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9458,7 +9458,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -9469,8 +9469,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b47, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8b77, "HP ElieBook 865 G10", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8b7a, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b7d, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b87, "HP", ALC236_FIXUP_HP_GPIO_LED), @@ -9481,6 +9486,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c26, "HP HP EliteBook 800G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), -- cgit 1.4.1 From 0eee95cbbbf7ecb422329193e5c381fa47c38d65 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 12 May 2023 16:34:16 +0800 Subject: ALSA: hda/realtek: Fix mute and micmute LEDs for yet another HP laptop commit 9dc68a4fe70893b000fb3c92c68b9f72369cf448 upstream. There's yet another laptop that needs the fixup to enable mute and micmute LEDs. So do it accordingly. Signed-off-by: Kai-Heng Feng Cc: Link: https://lore.kernel.org/r/20230512083417.157127-1-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a8d97926b1d..c75760717736 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9485,6 +9485,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c26, "HP HP EliteBook 800G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), -- cgit 1.4.1 From 836641cc41cdce71e9fc957d055426dc4afe2449 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 6 Apr 2023 13:08:45 +0200 Subject: can: j1939: recvmsg(): allow MSG_CMSG_COMPAT flag commit 1db080cbdbab28752bbb1c86d64daf96253a5da1 upstream. The control message provided by J1939 support MSG_CMSG_COMPAT but blocked recvmsg() syscalls that have set this flag, i.e. on 32bit user space on 64 bit kernels. Link: https://github.com/hartkopp/can-isotp/issues/59 Cc: Oleksij Rempel Suggested-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Tested-by: Oleksij Rempel Acked-by: Oleksij Rempel Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Link: https://lore.kernel.org/20230505110308.81087-3-mkl@pengutronix.de Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/j1939/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index b670ba03a675..4fb3a99b5f67 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -798,7 +798,7 @@ static int j1939_sk_recvmsg(struct socket *sock, struct msghdr *msg, struct j1939_sk_buff_cb *skcb; int ret = 0; - if (flags & ~(MSG_DONTWAIT | MSG_ERRQUEUE)) + if (flags & ~(MSG_DONTWAIT | MSG_ERRQUEUE | MSG_CMSG_COMPAT)) return -EINVAL; if (flags & MSG_ERRQUEUE) -- cgit 1.4.1 From 9cd1025b1a46713eb644935b7de6d4751257d43f Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 6 Apr 2023 13:08:45 +0200 Subject: can: isotp: recvmsg(): allow MSG_CMSG_COMPAT flag commit db2773d65b02aed319a93efdfb958087771d4e19 upstream. The control message provided by isotp support MSG_CMSG_COMPAT but blocked recvmsg() syscalls that have set this flag, i.e. on 32bit user space on 64 bit kernels. Link: https://github.com/hartkopp/can-isotp/issues/59 Cc: Oleksij Rempel Suggested-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Fixes: 42bf50a1795a ("can: isotp: support MSG_TRUNC flag when reading from socket") Link: https://lore.kernel.org/20230505110308.81087-2-mkl@pengutronix.de Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 5761d4ab839d..1af623839bff 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1106,7 +1106,7 @@ static int isotp_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, struct isotp_sock *so = isotp_sk(sk); int ret = 0; - if (flags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK)) + if (flags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK | MSG_CMSG_COMPAT)) return -EINVAL; if (!so->bound) -- cgit 1.4.1 From 24bdfcb09947da429bcead9eda475227307c9961 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 16 May 2023 15:43:13 +0200 Subject: can: kvaser_pciefd: Set CAN_STATE_STOPPED in kvaser_pciefd_stop() commit aed0e6ca7dbb8fbea9bc69c9ac663d5533c8c5d8 upstream. Set can.state to CAN_STATE_STOPPED in kvaser_pciefd_stop(). Without this fix, wrong CAN state was repported after the interface was brought down. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/r/20230516134318.104279-2-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/kvaser_pciefd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index bcad11709bc9..589a4b044492 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -719,6 +719,7 @@ static int kvaser_pciefd_stop(struct net_device *netdev) iowrite32(0, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); del_timer(&can->bec_poll_timer); } + can->can.state = CAN_STATE_STOPPED; close_candev(netdev); return ret; -- cgit 1.4.1 From 50bdf44a1bda94059a7f78f2eac27ebe938d05f7 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 16 May 2023 15:43:15 +0200 Subject: can: kvaser_pciefd: Call request_irq() before enabling interrupts commit 84762d8da89d29ba842317eb842973e628c27391 upstream. Make sure the interrupt handler is registered before enabling interrupts. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/r/20230516134318.104279-4-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/kvaser_pciefd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 589a4b044492..99d52a64bbc6 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -1825,6 +1825,11 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, if (err) goto err_teardown_can_ctrls; + err = request_irq(pcie->pci->irq, kvaser_pciefd_irq_handler, + IRQF_SHARED, KVASER_PCIEFD_DRV_NAME, pcie); + if (err) + goto err_teardown_can_ctrls; + iowrite32(KVASER_PCIEFD_SRB_IRQ_DPD0 | KVASER_PCIEFD_SRB_IRQ_DPD1, pcie->reg_base + KVASER_PCIEFD_SRB_IRQ_REG); @@ -1845,11 +1850,6 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, iowrite32(KVASER_PCIEFD_SRB_CMD_RDB1, pcie->reg_base + KVASER_PCIEFD_SRB_CMD_REG); - err = request_irq(pcie->pci->irq, kvaser_pciefd_irq_handler, - IRQF_SHARED, KVASER_PCIEFD_DRV_NAME, pcie); - if (err) - goto err_teardown_can_ctrls; - err = kvaser_pciefd_reg_candev(pcie); if (err) goto err_free_irq; -- cgit 1.4.1 From fcdfc1860f8a1868bfe56e72f5448ecbdf50f557 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 16 May 2023 15:43:16 +0200 Subject: can: kvaser_pciefd: Empty SRB buffer in probe commit c589557dd1426f5adf90c7a919d4fde5a3e4ef64 upstream. Empty the "Shared receive buffer" (SRB) in probe, to assure we start in a known state, and don't process any irrelevant packets. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/r/20230516134318.104279-5-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/kvaser_pciefd.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 99d52a64bbc6..847816d17551 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -71,10 +71,12 @@ MODULE_DESCRIPTION("CAN driver for Kvaser CAN/PCIe devices"); #define KVASER_PCIEFD_SYSID_BUILD_REG (KVASER_PCIEFD_SYSID_BASE + 0x14) /* Shared receive buffer registers */ #define KVASER_PCIEFD_SRB_BASE 0x1f200 +#define KVASER_PCIEFD_SRB_FIFO_LAST_REG (KVASER_PCIEFD_SRB_BASE + 0x1f4) #define KVASER_PCIEFD_SRB_CMD_REG (KVASER_PCIEFD_SRB_BASE + 0x200) #define KVASER_PCIEFD_SRB_IEN_REG (KVASER_PCIEFD_SRB_BASE + 0x204) #define KVASER_PCIEFD_SRB_IRQ_REG (KVASER_PCIEFD_SRB_BASE + 0x20c) #define KVASER_PCIEFD_SRB_STAT_REG (KVASER_PCIEFD_SRB_BASE + 0x210) +#define KVASER_PCIEFD_SRB_RX_NR_PACKETS_REG (KVASER_PCIEFD_SRB_BASE + 0x214) #define KVASER_PCIEFD_SRB_CTRL_REG (KVASER_PCIEFD_SRB_BASE + 0x218) /* EPCS flash controller registers */ #define KVASER_PCIEFD_SPI_BASE 0x1fc00 @@ -111,6 +113,9 @@ MODULE_DESCRIPTION("CAN driver for Kvaser CAN/PCIe devices"); /* DMA support */ #define KVASER_PCIEFD_SRB_STAT_DMA BIT(24) +/* SRB current packet level */ +#define KVASER_PCIEFD_SRB_RX_NR_PACKETS_MASK 0xff + /* DMA Enable */ #define KVASER_PCIEFD_SRB_CTRL_DMA_ENABLE BIT(0) @@ -1059,6 +1064,7 @@ static int kvaser_pciefd_setup_dma(struct kvaser_pciefd *pcie) { int i; u32 srb_status; + u32 srb_packet_count; dma_addr_t dma_addr[KVASER_PCIEFD_DMA_COUNT]; /* Disable the DMA */ @@ -1086,6 +1092,15 @@ static int kvaser_pciefd_setup_dma(struct kvaser_pciefd *pcie) KVASER_PCIEFD_SRB_CMD_RDB1, pcie->reg_base + KVASER_PCIEFD_SRB_CMD_REG); + /* Empty Rx FIFO */ + srb_packet_count = ioread32(pcie->reg_base + KVASER_PCIEFD_SRB_RX_NR_PACKETS_REG) & + KVASER_PCIEFD_SRB_RX_NR_PACKETS_MASK; + while (srb_packet_count) { + /* Drop current packet in FIFO */ + ioread32(pcie->reg_base + KVASER_PCIEFD_SRB_FIFO_LAST_REG); + srb_packet_count--; + } + srb_status = ioread32(pcie->reg_base + KVASER_PCIEFD_SRB_STAT_REG); if (!(srb_status & KVASER_PCIEFD_SRB_STAT_DI)) { dev_err(&pcie->pci->dev, "DMA not idle before enabling\n"); -- cgit 1.4.1 From 45ce3beb02a0b6e8f190f685e6c887ab77c22344 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 16 May 2023 15:43:14 +0200 Subject: can: kvaser_pciefd: Clear listen-only bit if not explicitly requested commit bf7ac55e991ca177f1ac16be51152f1ef291a4df upstream. The listen-only bit was never cleared, causing the controller to always use listen-only mode, if previously set. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/r/20230516134318.104279-3-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/kvaser_pciefd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 847816d17551..4a0894a69b5f 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -559,6 +559,8 @@ static void kvaser_pciefd_setup_controller(struct kvaser_pciefd_can *can) if (can->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) mode |= KVASER_PCIEFD_KCAN_MODE_LOM; + else + mode &= ~KVASER_PCIEFD_KCAN_MODE_LOM; mode |= KVASER_PCIEFD_KCAN_MODE_EEN; mode |= KVASER_PCIEFD_KCAN_MODE_EPEN; -- cgit 1.4.1 From eabb11236a64de9ce057c38cadb9f6dd03209f61 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 16 May 2023 15:43:17 +0200 Subject: can: kvaser_pciefd: Do not send EFLUSH command on TFD interrupt commit 262d7a52ba27525e3c1203230c9f0524e48bbb34 upstream. Under certain circumstances we send two EFLUSH commands, resulting in two EFLUSH ack packets, while only expecting a single EFLUSH ack. This can cause the driver Tx flush completion to get out of sync. To avoid this problem, don't enable the "Transmit buffer flush done" (TFD) interrupt and remove the code handling it. Now we only send EFLUSH command after receiving status packet with "Init detected" (IDET) bit set. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/r/20230516134318.104279-6-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/kvaser_pciefd.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 4a0894a69b5f..d7b1a7e9c7d6 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -531,7 +531,7 @@ static int kvaser_pciefd_set_tx_irq(struct kvaser_pciefd_can *can) KVASER_PCIEFD_KCAN_IRQ_TOF | KVASER_PCIEFD_KCAN_IRQ_ABD | KVASER_PCIEFD_KCAN_IRQ_TAE | KVASER_PCIEFD_KCAN_IRQ_TAL | KVASER_PCIEFD_KCAN_IRQ_FDIC | KVASER_PCIEFD_KCAN_IRQ_BPP | - KVASER_PCIEFD_KCAN_IRQ_TAR | KVASER_PCIEFD_KCAN_IRQ_TFD; + KVASER_PCIEFD_KCAN_IRQ_TAR; iowrite32(msk, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); @@ -579,7 +579,7 @@ static void kvaser_pciefd_start_controller_flush(struct kvaser_pciefd_can *can) spin_lock_irqsave(&can->lock, irq); iowrite32(-1, can->reg_base + KVASER_PCIEFD_KCAN_IRQ_REG); - iowrite32(KVASER_PCIEFD_KCAN_IRQ_ABD | KVASER_PCIEFD_KCAN_IRQ_TFD, + iowrite32(KVASER_PCIEFD_KCAN_IRQ_ABD, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); status = ioread32(can->reg_base + KVASER_PCIEFD_KCAN_STAT_REG); @@ -622,7 +622,7 @@ static int kvaser_pciefd_bus_on(struct kvaser_pciefd_can *can) iowrite32(0, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); iowrite32(-1, can->reg_base + KVASER_PCIEFD_KCAN_IRQ_REG); - iowrite32(KVASER_PCIEFD_KCAN_IRQ_ABD | KVASER_PCIEFD_KCAN_IRQ_TFD, + iowrite32(KVASER_PCIEFD_KCAN_IRQ_ABD, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); mode = ioread32(can->reg_base + KVASER_PCIEFD_KCAN_MODE_REG); @@ -1015,8 +1015,7 @@ static int kvaser_pciefd_setup_can_ctrls(struct kvaser_pciefd *pcie) SET_NETDEV_DEV(netdev, &pcie->pci->dev); iowrite32(-1, can->reg_base + KVASER_PCIEFD_KCAN_IRQ_REG); - iowrite32(KVASER_PCIEFD_KCAN_IRQ_ABD | - KVASER_PCIEFD_KCAN_IRQ_TFD, + iowrite32(KVASER_PCIEFD_KCAN_IRQ_ABD, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); pcie->can[i] = can; @@ -1443,9 +1442,6 @@ static int kvaser_pciefd_handle_status_packet(struct kvaser_pciefd *pcie, cmd = KVASER_PCIEFD_KCAN_CMD_AT; cmd |= ++can->cmd_seq << KVASER_PCIEFD_KCAN_CMD_SEQ_SHIFT; iowrite32(cmd, can->reg_base + KVASER_PCIEFD_KCAN_CMD_REG); - - iowrite32(KVASER_PCIEFD_KCAN_IRQ_TFD, - can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG); } else if (p->header[0] & KVASER_PCIEFD_SPACK_IDET && p->header[0] & KVASER_PCIEFD_SPACK_IRM && cmdseq == (p->header[1] & KVASER_PCIEFD_PACKET_SEQ_MSK) && @@ -1732,15 +1728,6 @@ static int kvaser_pciefd_transmit_irq(struct kvaser_pciefd_can *can) if (irq & KVASER_PCIEFD_KCAN_IRQ_TOF) netdev_err(can->can.dev, "Tx FIFO overflow\n"); - if (irq & KVASER_PCIEFD_KCAN_IRQ_TFD) { - u8 count = ioread32(can->reg_base + - KVASER_PCIEFD_KCAN_TX_NPACKETS_REG) & 0xff; - - if (count == 0) - iowrite32(KVASER_PCIEFD_KCAN_CTRL_EFLUSH, - can->reg_base + KVASER_PCIEFD_KCAN_CTRL_REG); - } - if (irq & KVASER_PCIEFD_KCAN_IRQ_BPP) netdev_err(can->can.dev, "Fail to change bittiming, when not in reset mode\n"); -- cgit 1.4.1 From d61191092d6316daeb0a13da263e0efc86c5ccea Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 16 May 2023 15:43:18 +0200 Subject: can: kvaser_pciefd: Disable interrupts in probe error path commit 11164bc39459335ab93c6e99d53b7e4292fba38b upstream. Disable interrupts in error path of probe function. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Assarsson Link: https://lore.kernel.org/r/20230516134318.104279-7-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/kvaser_pciefd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index d7b1a7e9c7d6..956a4a57396f 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -1861,6 +1861,8 @@ static int kvaser_pciefd_probe(struct pci_dev *pdev, return 0; err_free_irq: + /* Disable PCI interrupts */ + iowrite32(0, pcie->reg_base + KVASER_PCIEFD_IEN_REG); free_irq(pcie->pci->irq, pcie); err_teardown_can_ctrls: -- cgit 1.4.1 From 107677a8f43521e33e4a653e50fdf55ba622a4ce Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 8 May 2023 16:54:29 +0800 Subject: wifi: rtw88: use work to update rate to avoid RCU warning commit bcafcb959a57a6890e900199690c5fc47da1a304 upstream. The ieee80211_ops::sta_rc_update must be atomic, because ieee80211_chan_bw_change() holds rcu_read lock while calling drv_sta_rc_update(), so create a work to do original things. Voluntary context switch within RCU read-side critical section! WARNING: CPU: 0 PID: 4621 at kernel/rcu/tree_plugin.h:318 rcu_note_context_switch+0x571/0x5d0 CPU: 0 PID: 4621 Comm: kworker/u16:2 Tainted: G W OE Workqueue: phy3 ieee80211_chswitch_work [mac80211] RIP: 0010:rcu_note_context_switch+0x571/0x5d0 Call Trace: __schedule+0xb0/0x1460 ? __mod_timer+0x116/0x360 schedule+0x5a/0xc0 schedule_timeout+0x87/0x150 ? trace_raw_output_tick_stop+0x60/0x60 wait_for_completion_timeout+0x7b/0x140 usb_start_wait_urb+0x82/0x160 [usbcore usb_control_msg+0xe3/0x140 [usbcore rtw_usb_read+0x88/0xe0 [rtw_usb rtw_usb_read8+0xf/0x10 [rtw_usb rtw_fw_send_h2c_command+0xa0/0x170 [rtw_core rtw_fw_send_ra_info+0xc9/0xf0 [rtw_core drv_sta_rc_update+0x7c/0x160 [mac80211 ieee80211_chan_bw_change+0xfb/0x110 [mac80211 ieee80211_change_chanctx+0x38/0x130 [mac80211 ieee80211_vif_use_reserved_switch+0x34e/0x900 [mac80211 ieee80211_link_use_reserved_context+0x88/0xe0 [mac80211 ieee80211_chswitch_work+0x95/0x170 [mac80211 process_one_work+0x201/0x410 worker_thread+0x4a/0x3b0 ? process_one_work+0x410/0x410 kthread+0xe1/0x110 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork+0x1f/0x30 Cc: stable@vger.kernel.org Fixes: c1edc86472fc ("rtw88: add ieee80211:sta_rc_update ops") Reported-by: Larry Finger Link: https://lore.kernel.org/linux-wireless/f1e31e8e-f84e-3791-50fb-663a83c5c6e9@lwfinger.net/T/#t Signed-off-by: Ping-Ke Shih Tested-by: Larry Finger Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230508085429.46653-1-pkshih@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw88/mac80211.c | 2 +- drivers/net/wireless/realtek/rtw88/main.c | 15 +++++++++++++++ drivers/net/wireless/realtek/rtw88/main.h | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index 07578ccc4bab..62fb28f14c94 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -891,7 +891,7 @@ static void rtw_ops_sta_rc_update(struct ieee80211_hw *hw, struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; if (changed & IEEE80211_RC_BW_CHANGED) - rtw_update_sta_info(rtwdev, si, true); + ieee80211_queue_work(rtwdev->hw, &si->rc_work); } const struct ieee80211_ops rtw_ops = { diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 67151dbf8384..8080ace5ed51 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -296,6 +296,17 @@ static u8 rtw_acquire_macid(struct rtw_dev *rtwdev) return mac_id; } +static void rtw_sta_rc_work(struct work_struct *work) +{ + struct rtw_sta_info *si = container_of(work, struct rtw_sta_info, + rc_work); + struct rtw_dev *rtwdev = si->rtwdev; + + mutex_lock(&rtwdev->mutex); + rtw_update_sta_info(rtwdev, si, true); + mutex_unlock(&rtwdev->mutex); +} + int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { @@ -306,12 +317,14 @@ int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, if (si->mac_id >= RTW_MAX_MAC_ID_NUM) return -ENOSPC; + si->rtwdev = rtwdev; si->sta = sta; si->vif = vif; si->init_ra_lv = 1; ewma_rssi_init(&si->avg_rssi); for (i = 0; i < ARRAY_SIZE(sta->txq); i++) rtw_txq_init(rtwdev, sta->txq[i]); + INIT_WORK(&si->rc_work, rtw_sta_rc_work); rtw_update_sta_info(rtwdev, si, true); rtw_fw_media_status_report(rtwdev, si->mac_id, true); @@ -330,6 +343,8 @@ void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; int i; + cancel_work_sync(&si->rc_work); + rtw_release_macid(rtwdev, si->mac_id); if (fw_exist) rtw_fw_media_status_report(rtwdev, si->mac_id, false); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index cd9c068ae1a7..f8714f449244 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -734,6 +734,7 @@ struct rtw_txq { DECLARE_EWMA(rssi, 10, 16); struct rtw_sta_info { + struct rtw_dev *rtwdev; struct ieee80211_sta *sta; struct ieee80211_vif *vif; @@ -758,6 +759,8 @@ struct rtw_sta_info { bool use_cfg_mask; struct cfg80211_bitrate_mask *mask; + + struct work_struct rc_work; }; enum rtw_bfee_role { -- cgit 1.4.1 From 3b66d58c89fd3622742c04b0039ff4be7c26984f Mon Sep 17 00:00:00 2001 From: Bharath SM Date: Wed, 3 May 2023 14:38:35 +0000 Subject: SMB3: Close all deferred handles of inode in case of handle lease break commit 47592fa8eb03742048b096b4696ec133384c45eb upstream. Oplock break may occur for different file handle than the deferred handle. Check for inode deferred closes list, if it's not empty then close all the deferred handles of inode because we should not cache handles if we dont have handle lease. Eg: If openfilelist has one deferred file handle and another open file handle from app for a same file, then on a lease break we choose the first handle in openfile list. The first handle in list can be deferred handle or actual open file handle from app. In case if it is actual open handle then today, we don't close deferred handles if we lose handle lease on a file. Problem with this is, later if app decides to close the existing open handle then we still be caching deferred handles until deferred close timeout. Leaving open handle may result in sharing violation when windows client tries to open a file with limited file share access. So we should check for deferred list of inode and walk through the list of deferred files in inode and close all deferred files. Fixes: 9e31678fb403 ("SMB3: fix lease break timeout when multiple deferred close handles for the same file.") Cc: stable@kernel.org Signed-off-by: Bharath SM Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/file.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 158a0a5f4007..6c4d8593eb34 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -5087,8 +5087,6 @@ void cifs_oplock_break(struct work_struct *work) struct TCP_Server_Info *server = tcon->ses->server; int rc = 0; bool purge_cache = false; - struct cifs_deferred_close *dclose; - bool is_deferred = false; wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, TASK_UNINTERRUPTIBLE); @@ -5129,14 +5127,9 @@ oplock_break_ack: * file handles but cached, then schedule deferred close immediately. * So, new open will not use cached handle. */ - spin_lock(&CIFS_I(inode)->deferred_lock); - is_deferred = cifs_is_deferred_close(cfile, &dclose); - spin_unlock(&CIFS_I(inode)->deferred_lock); - if (!CIFS_CACHE_HANDLE(cinode) && is_deferred && - cfile->deferred_close_scheduled && delayed_work_pending(&cfile->deferred)) { + if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes)) cifs_close_deferred_file(cinode); - } /* * releasing stale oplock after recent reconnect of smb session using -- cgit 1.4.1 From 4d25f93e64be7bcb8457f40ec093f5ea43a78748 Mon Sep 17 00:00:00 2001 From: Bharath SM Date: Mon, 15 May 2023 21:25:12 +0000 Subject: SMB3: drop reference to cfile before sending oplock break commit 59a556aebc43dded08535fe97d94ca3f657915e4 upstream. In cifs_oplock_break function we drop reference to a cfile at the end of function, due to which close command goes on wire after lease break acknowledgment even if file is already closed by application but we had deferred the handle close. If other client with limited file shareaccess waiting on lease break ack proceeds operation on that file as soon as first client sends ack, then we may encounter status sharing violation error because of open handle. Solution is to put reference to cfile(send close on wire if last ref) and then send oplock acknowledgment to server. Fixes: 9e31678fb403 ("SMB3: fix lease break timeout when multiple deferred close handles for the same file.") Cc: stable@kernel.org Signed-off-by: Bharath SM Reviewed-by: Shyam Prasad N Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/file.c | 17 ++++++++++++----- fs/cifs/smb1ops.c | 9 ++++----- fs/cifs/smb2ops.c | 7 +++---- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 157d3c0e3cc7..6ec1a34738e2 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -428,8 +428,8 @@ struct smb_version_operations { /* check for STATUS_NETWORK_SESSION_EXPIRED */ bool (*is_session_expired)(char *); /* send oplock break response */ - int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *, - struct cifsInodeInfo *); + int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid, + __u16 net_fid, struct cifsInodeInfo *cifs_inode); /* query remote filesystem */ int (*queryfs)(const unsigned int, struct cifs_tcon *, struct cifs_sb_info *, struct kstatfs *); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 6c4d8593eb34..9a4c33ffb75f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -5086,7 +5086,9 @@ void cifs_oplock_break(struct work_struct *work) struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct TCP_Server_Info *server = tcon->ses->server; int rc = 0; - bool purge_cache = false; + bool purge_cache = false, oplock_break_cancelled; + __u64 persistent_fid, volatile_fid; + __u16 net_fid; wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, TASK_UNINTERRUPTIBLE); @@ -5131,19 +5133,24 @@ oplock_break_ack: if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes)) cifs_close_deferred_file(cinode); + persistent_fid = cfile->fid.persistent_fid; + volatile_fid = cfile->fid.volatile_fid; + net_fid = cfile->fid.netfid; + oplock_break_cancelled = cfile->oplock_break_cancelled; + + _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); /* * releasing stale oplock after recent reconnect of smb session using * a now incorrect file handle is not a data integrity issue but do * not bother sending an oplock release if session to server still is * disconnected since oplock already released by the server */ - if (!cfile->oplock_break_cancelled) { - rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid, - cinode); + if (!oplock_break_cancelled) { + rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, + volatile_fid, net_fid, cinode); cifs_dbg(FYI, "Oplock release rc = %d\n", rc); } - _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); cifs_done_oplock_break(cinode); } diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index abda6148be10..7d1b3fc014d9 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -897,12 +897,11 @@ cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon, } static int -cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, - struct cifsInodeInfo *cinode) +cifs_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) { - return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0, - LOCKING_ANDX_OPLOCK_RELEASE, false, - CIFS_CACHE_READ(cinode) ? 1 : 0); + return CIFSSMBLock(0, tcon, net_fid, current->tgid, 0, 0, 0, 0, + LOCKING_ANDX_OPLOCK_RELEASE, false, CIFS_CACHE_READ(cinode) ? 1 : 0); } static int diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 7468f8baf499..d512440d35b6 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2383,15 +2383,14 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) } static int -smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, - struct cifsInodeInfo *cinode) +smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) { if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) return SMB2_lease_break(0, tcon, cinode->lease_key, smb2_get_lease_state(cinode)); - return SMB2_oplock_break(0, tcon, fid->persistent_fid, - fid->volatile_fid, + return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid, CIFS_CACHE_READ(cinode) ? 1 : 0); } -- cgit 1.4.1 From f1d013b0f0f07264229b54ae64003315837a0fcd Mon Sep 17 00:00:00 2001 From: Gustav Johansson Date: Sat, 6 May 2023 00:05:07 +0900 Subject: ksmbd: smb2: Allow messages padded to 8byte boundary commit e7b8b8ed9960bf699bf4029f482d9e869c094ed6 upstream. clc length is now accepted to <= 8 less than length, rather than < 8. Solve issues on some of Axis's smb clients which send messages where clc length is 8 bytes less than length. The specific client was running kernel 4.19.217 with smb dialect 3.0.2 on armv7l. Cc: stable@vger.kernel.org Signed-off-by: Gustav Johansson Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2misc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index fbdde426dd01..0ffe663b7590 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -416,8 +416,11 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) /* * Allow a message that padded to 8byte boundary. + * Linux 4.19.217 with smb 3.0.2 are sometimes + * sending messages where the cls_len is exactly + * 8 bytes less than len. */ - if (clc_len < len && (len - clc_len) < 8) + if (clc_len < len && (len - clc_len) <= 8) goto validate_credit; pr_err_ratelimited( -- cgit 1.4.1 From af7335a4b946f9f6f9d98398cbcea15cd9850409 Mon Sep 17 00:00:00 2001 From: Chih-Yen Chang Date: Sat, 6 May 2023 00:03:54 +0900 Subject: ksmbd: allocate one more byte for implied bcc[0] commit 443d61d1fa9faa60ef925513d83742902390100f upstream. ksmbd_smb2_check_message allows client to return one byte more, so we need to allocate additional memory in ksmbd_conn_handler_loop to avoid out-of-bound access. Cc: stable@vger.kernel.org Signed-off-by: Chih-Yen Chang Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/connection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index e3312fbf4c09..b371754540f9 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -353,7 +353,8 @@ int ksmbd_conn_handler_loop(void *p) break; /* 4 for rfc1002 length field */ - size = pdu_size + 4; + /* 1 for implied bcc[0] */ + size = pdu_size + 4 + 1; conn->request_buf = kvmalloc(size, GFP_KERNEL); if (!conn->request_buf) break; -- cgit 1.4.1 From 40d90ee0275a1bfcd26fa7690adc4330b4227a69 Mon Sep 17 00:00:00 2001 From: Chih-Yen Chang Date: Sat, 6 May 2023 00:01:54 +0900 Subject: ksmbd: fix wrong UserName check in session_user commit f0a96d1aafd8964e1f9955c830a3e5cb3c60a90f upstream. The offset of UserName is related to the address of security buffer. To ensure the validaty of UserName, we need to compare name_off + name_len with secbuf_len instead of auth_msg_len. [ 27.096243] ================================================================== [ 27.096890] BUG: KASAN: slab-out-of-bounds in smb_strndup_from_utf16+0x188/0x350 [ 27.097609] Read of size 2 at addr ffff888005e3b542 by task kworker/0:0/7 ... [ 27.099950] Call Trace: [ 27.100194] [ 27.100397] dump_stack_lvl+0x33/0x50 [ 27.100752] print_report+0xcc/0x620 [ 27.102305] kasan_report+0xae/0xe0 [ 27.103072] kasan_check_range+0x35/0x1b0 [ 27.103757] smb_strndup_from_utf16+0x188/0x350 [ 27.105474] smb2_sess_setup+0xaf8/0x19c0 [ 27.107935] handle_ksmbd_work+0x274/0x810 [ 27.108315] process_one_work+0x419/0x760 [ 27.108689] worker_thread+0x2a2/0x6f0 [ 27.109385] kthread+0x160/0x190 [ 27.110129] ret_from_fork+0x1f/0x30 [ 27.110454] Cc: stable@vger.kernel.org Signed-off-by: Chih-Yen Chang Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2pdu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 8f96b96dbac1..3accbb2d4165 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1373,7 +1373,7 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, struct authenticate_message *authblob; struct ksmbd_user *user; char *name; - unsigned int auth_msg_len, name_off, name_len, secbuf_len; + unsigned int name_off, name_len, secbuf_len; secbuf_len = le16_to_cpu(req->SecurityBufferLength); if (secbuf_len < sizeof(struct authenticate_message)) { @@ -1383,9 +1383,8 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, authblob = user_authblob(conn, req); name_off = le32_to_cpu(authblob->UserName.BufferOffset); name_len = le16_to_cpu(authblob->UserName.Length); - auth_msg_len = le16_to_cpu(req->SecurityBufferOffset) + secbuf_len; - if (auth_msg_len < (u64)name_off + name_len) + if (secbuf_len < (u64)name_off + name_len) return NULL; name = smb_strndup_from_utf16((const char *)authblob + name_off, -- cgit 1.4.1 From 75378b03a90d75b1349bb03577ac8465194c883e Mon Sep 17 00:00:00 2001 From: Chih-Yen Chang Date: Sun, 14 May 2023 12:05:05 +0900 Subject: ksmbd: fix global-out-of-bounds in smb2_find_context_vals commit 02f76c401d17e409ed45bf7887148fcc22c93c85 upstream. Add tag_len argument in smb2_find_context_vals() to avoid out-of-bound read when create_context's name_len is larger than tag length. [ 7.995411] ================================================================== [ 7.995866] BUG: KASAN: global-out-of-bounds in memcmp+0x83/0xa0 [ 7.996248] Read of size 8 at addr ffffffff8258d940 by task kworker/0:0/7 ... [ 7.998191] Call Trace: [ 7.998358] [ 7.998503] dump_stack_lvl+0x33/0x50 [ 7.998743] print_report+0xcc/0x620 [ 7.999458] kasan_report+0xae/0xe0 [ 7.999895] kasan_check_range+0x35/0x1b0 [ 8.000152] memcmp+0x83/0xa0 [ 8.000347] smb2_find_context_vals+0xf7/0x1e0 [ 8.000635] smb2_open+0x1df2/0x43a0 [ 8.006398] handle_ksmbd_work+0x274/0x810 [ 8.006666] process_one_work+0x419/0x760 [ 8.006922] worker_thread+0x2a2/0x6f0 [ 8.007429] kthread+0x160/0x190 [ 8.007946] ret_from_fork+0x1f/0x30 [ 8.008181] Cc: stable@vger.kernel.org Signed-off-by: Chih-Yen Chang Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/oplock.c | 5 +++-- fs/ksmbd/oplock.h | 2 +- fs/ksmbd/smb2pdu.c | 14 +++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index d7d47b82451d..c7466546e59b 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1449,11 +1449,12 @@ struct lease_ctx_info *parse_lease_state(void *open_req) * smb2_find_context_vals() - find a particular context info in open request * @open_req: buffer containing smb2 file open(create) request * @tag: context name to search for + * @tag_len: the length of tag * * Return: pointer to requested context, NULL if @str context not found * or error pointer if name length is invalid. */ -struct create_context *smb2_find_context_vals(void *open_req, const char *tag) +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len) { struct create_context *cc; unsigned int next = 0; @@ -1492,7 +1493,7 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) return ERR_PTR(-EINVAL); name = (char *)cc + name_off; - if (memcmp(name, tag, name_len) == 0) + if (name_len == tag_len && !memcmp(name, tag, name_len)) return cc; remain_len -= next; diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h index 09753448f779..4b0fe6da7694 100644 --- a/fs/ksmbd/oplock.h +++ b/fs/ksmbd/oplock.h @@ -118,7 +118,7 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); void create_mxac_rsp_buf(char *cc, int maximal_access); void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); -struct create_context *smb2_find_context_vals(void *open_req, const char *str); +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, char *lease_key); int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 3accbb2d4165..7684b31035d9 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2478,7 +2478,7 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, return -ENOENT; /* Parse SD BUFFER create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER); + context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER, 4); if (!context) return -ENOENT; else if (IS_ERR(context)) @@ -2680,7 +2680,7 @@ int smb2_open(struct ksmbd_work *work) if (req->CreateContextsOffset) { /* Parse non-durable handle create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER); + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -2700,7 +2700,7 @@ int smb2_open(struct ksmbd_work *work) } context = smb2_find_context_vals(req, - SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -2711,7 +2711,7 @@ int smb2_open(struct ksmbd_work *work) } context = smb2_find_context_vals(req, - SMB2_CREATE_TIMEWARP_REQUEST); + SMB2_CREATE_TIMEWARP_REQUEST, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -2723,7 +2723,7 @@ int smb2_open(struct ksmbd_work *work) if (tcon->posix_extensions) { context = smb2_find_context_vals(req, - SMB2_CREATE_TAG_POSIX); + SMB2_CREATE_TAG_POSIX, 16); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -3122,7 +3122,7 @@ int smb2_open(struct ksmbd_work *work) struct create_alloc_size_req *az_req; az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, - SMB2_CREATE_ALLOCATION_SIZE); + SMB2_CREATE_ALLOCATION_SIZE, 4); if (IS_ERR(az_req)) { rc = PTR_ERR(az_req); goto err_out; @@ -3149,7 +3149,7 @@ int smb2_open(struct ksmbd_work *work) err); } - context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out; -- cgit 1.4.1 From 154de42fe3f2b4460324edbca332c917fa3ed07d Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Wed, 10 May 2023 16:04:09 +0200 Subject: KVM: Fix vcpu_array[0] races commit afb2acb2e3a32e4d56f7fbd819769b98ed1b7520 upstream. In kvm_vm_ioctl_create_vcpu(), add vcpu to vcpu_array iff it's safe to access vcpu via kvm_get_vcpu() and kvm_for_each_vcpu(), i.e. when there's no failure path requiring vcpu removal and destruction. Such order is important because vcpu_array accessors may end up referencing vcpu at vcpu_array[0] even before online_vcpus is set to 1. When online_vcpus=0, any call to kvm_get_vcpu() goes through array_index_nospec() and ends with an attempt to xa_load(vcpu_array, 0): int num_vcpus = atomic_read(&kvm->online_vcpus); i = array_index_nospec(i, num_vcpus); return xa_load(&kvm->vcpu_array, i); Similarly, when online_vcpus=0, a kvm_for_each_vcpu() does not iterate over an "empty" range, but actually [0, ULONG_MAX]: xa_for_each_range(&kvm->vcpu_array, idx, vcpup, 0, \ (atomic_read(&kvm->online_vcpus) - 1)) In both cases, such online_vcpus=0 edge case, even if leading to unnecessary calls to XArray API, should not be an issue; requesting unpopulated indexes/ranges is handled by xa_load() and xa_for_each_range(). However, this means that when the first vCPU is created and inserted in vcpu_array *and* before online_vcpus is incremented, code calling kvm_get_vcpu()/kvm_for_each_vcpu() already has access to that first vCPU. This should not pose a problem assuming that once a vcpu is stored in vcpu_array, it will remain there, but that's not the case: kvm_vm_ioctl_create_vcpu() first inserts to vcpu_array, then requests a file descriptor. If create_vcpu_fd() fails, newly inserted vcpu is removed from the vcpu_array, then destroyed: vcpu->vcpu_idx = atomic_read(&kvm->online_vcpus); r = xa_insert(&kvm->vcpu_array, vcpu->vcpu_idx, vcpu, GFP_KERNEL_ACCOUNT); kvm_get_kvm(kvm); r = create_vcpu_fd(vcpu); if (r < 0) { xa_erase(&kvm->vcpu_array, vcpu->vcpu_idx); kvm_put_kvm_no_destroy(kvm); goto unlock_vcpu_destroy; } atomic_inc(&kvm->online_vcpus); This results in a possible race condition when a reference to a vcpu is acquired (via kvm_get_vcpu() or kvm_for_each_vcpu()) moments before said vcpu is destroyed. Signed-off-by: Michal Luczaj Message-Id: <20230510140410.1093987-2-mhal@rbox.co> Cc: stable@vger.kernel.org Fixes: c5b077549136 ("KVM: Convert the kvm->vcpus array to a xarray", 2021-12-08) Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- virt/kvm/kvm_main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 3a3c1bc3e303..de8e5eb6af10 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -3947,18 +3947,19 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id) } vcpu->vcpu_idx = atomic_read(&kvm->online_vcpus); - r = xa_insert(&kvm->vcpu_array, vcpu->vcpu_idx, vcpu, GFP_KERNEL_ACCOUNT); - BUG_ON(r == -EBUSY); + r = xa_reserve(&kvm->vcpu_array, vcpu->vcpu_idx, GFP_KERNEL_ACCOUNT); if (r) goto unlock_vcpu_destroy; /* Now it's all set up, let userspace reach it */ kvm_get_kvm(kvm); r = create_vcpu_fd(vcpu); - if (r < 0) { - xa_erase(&kvm->vcpu_array, vcpu->vcpu_idx); - kvm_put_kvm_no_destroy(kvm); - goto unlock_vcpu_destroy; + if (r < 0) + goto kvm_put_xa_release; + + if (KVM_BUG_ON(!!xa_store(&kvm->vcpu_array, vcpu->vcpu_idx, vcpu, 0), kvm)) { + r = -EINVAL; + goto kvm_put_xa_release; } /* @@ -3973,6 +3974,9 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id) kvm_create_vcpu_debugfs(vcpu); return r; +kvm_put_xa_release: + kvm_put_kvm_no_destroy(kvm); + xa_release(&kvm->vcpu_array, vcpu->vcpu_idx); unlock_vcpu_destroy: mutex_unlock(&kvm->lock); kvm_dirty_ring_free(&vcpu->dirty_ring); -- cgit 1.4.1 From 6c4172d44cf971ed1f79d833eaafd8fef1b55cbd Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 4 May 2023 16:40:20 +0200 Subject: statfs: enforce statfs[64] structure initialization commit ed40866ec7d328b3dfb70db7e2011640a16202c3 upstream. s390's struct statfs and struct statfs64 contain padding, which field-by-field copying does not set. Initialize the respective structs with zeros before filling them and copying them to userspace, like it's already done for the compat versions of these structs. Found by KMSAN. [agordeev@linux.ibm.com: fixed typo in patch description] Acked-by: Heiko Carstens Cc: stable@vger.kernel.org # v4.14+ Signed-off-by: Ilya Leoshkevich Reviewed-by: Andrew Morton Link: https://lore.kernel.org/r/20230504144021.808932-2-iii@linux.ibm.com Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- fs/statfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/statfs.c b/fs/statfs.c index 0ba34c135593..96d1c3edf289 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -130,6 +130,7 @@ static int do_statfs_native(struct kstatfs *st, struct statfs __user *p) if (sizeof(buf) == sizeof(*st)) memcpy(&buf, st, sizeof(*st)); else { + memset(&buf, 0, sizeof(buf)); if (sizeof buf.f_blocks == 4) { if ((st->f_blocks | st->f_bfree | st->f_bavail | st->f_bsize | st->f_frsize) & @@ -158,7 +159,6 @@ static int do_statfs_native(struct kstatfs *st, struct statfs __user *p) buf.f_namelen = st->f_namelen; buf.f_frsize = st->f_frsize; buf.f_flags = st->f_flags; - memset(buf.f_spare, 0, sizeof(buf.f_spare)); } if (copy_to_user(p, &buf, sizeof(buf))) return -EFAULT; @@ -171,6 +171,7 @@ static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p) if (sizeof(buf) == sizeof(*st)) memcpy(&buf, st, sizeof(*st)); else { + memset(&buf, 0, sizeof(buf)); buf.f_type = st->f_type; buf.f_bsize = st->f_bsize; buf.f_blocks = st->f_blocks; @@ -182,7 +183,6 @@ static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p) buf.f_namelen = st->f_namelen; buf.f_frsize = st->f_frsize; buf.f_flags = st->f_flags; - memset(buf.f_spare, 0, sizeof(buf.f_spare)); } if (copy_to_user(p, &buf, sizeof(buf))) return -EFAULT; -- cgit 1.4.1 From 254ee530286aeb6d6de93d05b2247153df590af1 Mon Sep 17 00:00:00 2001 From: Peng Zhang Date: Fri, 5 May 2023 22:58:29 +0800 Subject: maple_tree: make maple state reusable after mas_empty_area() commit 0257d9908d38c0b1669af4bb1bc4dbca1f273fe6 upstream. Make mas->min and mas->max point to a node range instead of a leaf entry range. This allows mas to still be usable after mas_empty_area() returns. Users would get unexpected results from other operations on the maple state after calling the affected function. For example, x86 MAP_32BIT mmap() acts as if there is no suitable gap when there should be one. Link: https://lkml.kernel.org/r/20230505145829.74574-1-zhangpeng.00@bytedance.com Fixes: 54a611b60590 ("Maple Tree: add new data structure") Signed-off-by: Peng Zhang Reported-by: "Edgecombe, Rick P" Reported-by: Tad Reported-by: Michael Keyes Link: https://lore.kernel.org/linux-mm/32f156ba80010fd97dbaf0a0cdfc84366608624d.camel@intel.com/ Link: https://lore.kernel.org/linux-mm/e6108286ac025c268964a7ead3aab9899f9bc6e9.camel@spotco.us/ Reviewed-by: Liam R. Howlett Tested-by: Rick Edgecombe Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- lib/maple_tree.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/maple_tree.c b/lib/maple_tree.c index 9fe25ce9937b..b72268848ade 100644 --- a/lib/maple_tree.c +++ b/lib/maple_tree.c @@ -5338,15 +5338,9 @@ int mas_empty_area(struct ma_state *mas, unsigned long min, mt = mte_node_type(mas->node); pivots = ma_pivots(mas_mn(mas), mt); - if (offset) - mas->min = pivots[offset - 1] + 1; - - if (offset < mt_pivots[mt]) - mas->max = pivots[offset]; - - if (mas->index < mas->min) - mas->index = mas->min; - + min = mas_safe_min(mas, pivots, offset); + if (mas->index < min) + mas->index = min; mas->last = mas->index + size - 1; return 0; } -- cgit 1.4.1 From 2cab13f500a6333bd2b853783ac76be9e4956f8a Mon Sep 17 00:00:00 2001 From: Domenico Cerasuolo Date: Wed, 3 May 2023 17:12:00 +0200 Subject: mm: fix zswap writeback race condition commit 04fc7816089c5a32c29a04ec94b998e219dfb946 upstream. The zswap writeback mechanism can cause a race condition resulting in memory corruption, where a swapped out page gets swapped in with data that was written to a different page. The race unfolds like this: 1. a page with data A and swap offset X is stored in zswap 2. page A is removed off the LRU by zpool driver for writeback in zswap-shrink work, data for A is mapped by zpool driver 3. user space program faults and invalidates page entry A, offset X is considered free 4. kswapd stores page B at offset X in zswap (zswap could also be full, if so, page B would then be IOed to X, then skip step 5.) 5. entry A is replaced by B in tree->rbroot, this doesn't affect the local reference held by zswap-shrink work 6. zswap-shrink work writes back A at X, and frees zswap entry A 7. swapin of slot X brings A in memory instead of B The fix: Once the swap page cache has been allocated (case ZSWAP_SWAPCACHE_NEW), zswap-shrink work just checks that the local zswap_entry reference is still the same as the one in the tree. If it's not the same it means that it's either been invalidated or replaced, in both cases the writeback is aborted because the local entry contains stale data. Reproducer: I originally found this by running `stress` overnight to validate my work on the zswap writeback mechanism, it manifested after hours on my test machine. The key to make it happen is having zswap writebacks, so whatever setup pumps /sys/kernel/debug/zswap/written_back_pages should do the trick. In order to reproduce this faster on a vm, I setup a system with ~100M of available memory and a 500M swap file, then running `stress --vm 1 --vm-bytes 300000000 --vm-stride 4000` makes it happen in matter of tens of minutes. One can speed things up even more by swinging /sys/module/zswap/parameters/max_pool_percent up and down between, say, 20 and 1; this makes it reproduce in tens of seconds. It's crucial to set `--vm-stride` to something other than 4096 otherwise `stress` won't realize that memory has been corrupted because all pages would have the same data. Link: https://lkml.kernel.org/r/20230503151200.19707-1-cerasuolodomenico@gmail.com Signed-off-by: Domenico Cerasuolo Acked-by: Johannes Weiner Reviewed-by: Chris Li (Google) Cc: Dan Streetman Cc: Johannes Weiner Cc: Minchan Kim Cc: Nitin Gupta Cc: Seth Jennings Cc: Vitaly Wool Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/zswap.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mm/zswap.c b/mm/zswap.c index 2d48fd59cc7a..708b82dbe8a4 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -1002,6 +1002,22 @@ static int zswap_writeback_entry(struct zpool *pool, unsigned long handle) goto fail; case ZSWAP_SWAPCACHE_NEW: /* page is locked */ + /* + * Having a local reference to the zswap entry doesn't exclude + * swapping from invalidating and recycling the swap slot. Once + * the swapcache is secured against concurrent swapping to and + * from the slot, recheck that the entry is still current before + * writing. + */ + spin_lock(&tree->lock); + if (zswap_rb_search(&tree->rbroot, entry->offset) != entry) { + spin_unlock(&tree->lock); + delete_from_swap_cache(page_folio(page)); + ret = -ENOMEM; + goto fail; + } + spin_unlock(&tree->lock); + /* decompress */ acomp_ctx = raw_cpu_ptr(entry->pool->acomp_ctx); dlen = PAGE_SIZE; -- cgit 1.4.1 From cda8aa19bfdbd45ca2dab6fe131db700a7582924 Mon Sep 17 00:00:00 2001 From: Vitaliy Tomin Date: Sun, 23 Apr 2023 11:45:12 +0800 Subject: serial: Add support for Advantech PCI-1611U card commit d2b00516de0e1d696724247098f6733a6ea53908 upstream. Add support for Advantech PCI-1611U card Advantech provides opensource drivers for this and many others card based on legacy copy of 8250_pci driver called adv950 https://www.advantech.com/emt/support/details/driver?id=1-TDOIMJ It is hard to maintain to run as out of tree module on newer kernels. Just adding PCI ID to kernel 8250_pci works perfect. Signed-off-by: Vitaliy Tomin Cc: stable Link: https://lore.kernel.org/r/20230423034512.2671157-1-tomin@iszf.irk.ru Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 8e9f247590bd..cd27821f54ec 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1940,6 +1940,8 @@ pci_moxa_setup(struct serial_private *priv, #define PCI_SUBDEVICE_ID_SIIG_DUAL_30 0x2530 #define PCI_VENDOR_ID_ADVANTECH 0x13fe #define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 +#define PCI_DEVICE_ID_ADVANTECH_PCI1600 0x1600 +#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1611 0x1611 #define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 #define PCI_DEVICE_ID_ADVANTECH_PCI3618 0x3618 #define PCI_DEVICE_ID_ADVANTECH_PCIf618 0xf618 @@ -4105,6 +4107,9 @@ static SIMPLE_DEV_PM_OPS(pciserial_pm_ops, pciserial_suspend_one, pciserial_resume_one); static const struct pci_device_id serial_pci_tbl[] = { + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600, + PCI_DEVICE_ID_ADVANTECH_PCI1600_1611, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, -- cgit 1.4.1 From 1db5db7a99a02fdc709244c5503245f23379cb5c Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 20 Apr 2023 11:02:09 -0500 Subject: serial: 8250_exar: Add support for USR298x PCI Modems commit 95d698869b404772cc8b72560df71548491c10bc upstream. Possibly the last PCI controller-based (i.e. not a soft/winmodem) dial-up modem one can still buy. Looks to have a stock XR17C154 PCI UART chip for communication, but for some reason when provisioning the PCI IDs they swapped the vendor and subvendor IDs. Otherwise this card would have worked out of the box. Searching online, some folks seem to not have this issue and others do, so it is possible only some batches of cards have this error. Create a new macro to handle the switched IDs and add support here. Signed-off-by: Andrew Davis Cc: stable Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230420160209.28221-1-afd@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 64770c62bbec..b406cba10b0e 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -40,9 +40,13 @@ #define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 + #define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 #define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 +#define PCI_SUBDEVICE_ID_USR_2980 0x0128 +#define PCI_SUBDEVICE_ID_USR_2981 0x0129 + #define PCI_DEVICE_ID_SEALEVEL_710xC 0x1001 #define PCI_DEVICE_ID_SEALEVEL_720xC 0x1002 #define PCI_DEVICE_ID_SEALEVEL_740xC 0x1004 @@ -829,6 +833,15 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { (kernel_ulong_t)&bd \ } +#define USR_DEVICE(devid, sdevid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_USR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_VENDOR_ID_EXAR, \ + PCI_SUBDEVICE_ID_USR_##sdevid), 0, 0, \ + (kernel_ulong_t)&bd \ + } + static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_2S, pbn_exar_XR17C15x), EXAR_DEVICE(ACCESSIO, COM_4S, pbn_exar_XR17C15x), @@ -853,6 +866,10 @@ static const struct pci_device_id exar_pci_tbl[] = { IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), + /* USRobotics USR298x-OEM PCI Modems */ + USR_DEVICE(XR17C152, 2980, pbn_exar_XR17C15x), + USR_DEVICE(XR17C152, 2981, pbn_exar_XR17C15x), + /* Exar Corp. XR17C15[248] Dual/Quad/Octal UART */ EXAR_DEVICE(EXAR, XR17C152, pbn_exar_XR17C15x), EXAR_DEVICE(EXAR, XR17C154, pbn_exar_XR17C15x), -- cgit 1.4.1 From abc7e50e891f0cdfb7d4980b66a8be29dfbdbbfa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 5 May 2023 17:23:01 +0200 Subject: serial: qcom-geni: fix enabling deactivated interrupt commit 5f949f140f73696f64acb89a1f16ff9153d017e0 upstream. The driver have a race, experienced only with PREEMPT_RT patchset: CPU0 | CPU1 ================================================================== qcom_geni_serial_probe | uart_add_one_port | | serdev_drv_probe | qca_serdev_probe | serdev_device_open | uart_open | uart_startup | qcom_geni_serial_startup | enable_irq | __irq_startup | WARN_ON() | IRQ not activated request_threaded_irq | irq_domain_activate_irq | The warning: 894000.serial: ttyHS1 at MMIO 0x894000 (irq = 144, base_baud = 0) is a MSM serial serial0: tty port ttyHS1 registered WARNING: CPU: 7 PID: 107 at kernel/irq/chip.c:241 __irq_startup+0x78/0xd8 ... qcom_geni_serial 894000.serial: serial engine reports 0 RX bytes in! Adding UART port triggers probe of child serial devices - serdev and eventually Qualcomm Bluetooth hci_qca driver. This opens UART port which enables the interrupt before it got activated in request_threaded_irq(). The issue originates in commit f3974413cf02 ("tty: serial: qcom_geni_serial: Wakeup IRQ cleanup") and discussion on mailing list [1]. However the above commit does not explain why the uart_add_one_port() is moved above requesting interrupt. [1] https://lore.kernel.org/all/5d9f3dfa.1c69fb81.84c4b.30bf@mx.google.com/ Fixes: f3974413cf02 ("tty: serial: qcom_geni_serial: Wakeup IRQ cleanup") Cc: Cc: Stephen Boyd Signed-off-by: Krzysztof Kozlowski Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20230505152301.2181270-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 7905935b9f1b..cf9dc2ddfe66 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1483,19 +1483,18 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) platform_set_drvdata(pdev, port); port->handle_rx = console ? handle_rx_console : handle_rx_uart; - ret = uart_add_one_port(drv, uport); - if (ret) - return ret; - irq_set_status_flags(uport->irq, IRQ_NOAUTOEN); ret = devm_request_irq(uport->dev, uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH, port->name, uport); if (ret) { dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret); - uart_remove_one_port(drv, uport); return ret; } + ret = uart_add_one_port(drv, uport); + if (ret) + return ret; + /* * Set pm_runtime status as ACTIVE so that wakeup_irq gets * enabled/disabled from dev_pm_arm_wake_irq during system -- cgit 1.4.1 From e16629c639d429e48c849808e59f1efcce886849 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 24 Apr 2023 14:55:54 -0500 Subject: thunderbolt: Clear registers properly when auto clear isn't in use commit c4af8e3fecd03b0aedcd38145955605cfebe7e3a upstream. When `QUIRK_AUTO_CLEAR_INT` isn't set, interrupt masking should be cleared by writing to Interrupt Mask Clear (IMR) and interrupt status should be cleared properly at shutdown/init. This fixes an error where interrupts are left enabled during resume from hibernation with `CONFIG_USB4=y`. Fixes: 468c49f44759 ("thunderbolt: Disable interrupt auto clear for rings") Cc: stable@vger.kernel.org # v6.3 Reported-by: Takashi Iwai Link: https://bugzilla.kernel.org/show_bug.cgi?id=217343 Signed-off-by: Mario Limonciello Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/nhi.c | 29 ++++++++++++++++++++++++----- drivers/thunderbolt/nhi_regs.h | 2 ++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index cfebec107f3f..0a525f44ea31 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -54,6 +54,21 @@ static int ring_interrupt_index(const struct tb_ring *ring) return bit; } +static void nhi_mask_interrupt(struct tb_nhi *nhi, int mask, int ring) +{ + if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) + return; + iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring); +} + +static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring) +{ + if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) + ioread32(nhi->iobase + REG_RING_NOTIFY_BASE + ring); + else + iowrite32(~0, nhi->iobase + REG_RING_INT_CLEAR + ring); +} + /* * ring_interrupt_active() - activate/deactivate interrupts for a single ring * @@ -61,8 +76,8 @@ static int ring_interrupt_index(const struct tb_ring *ring) */ static void ring_interrupt_active(struct tb_ring *ring, bool active) { - int reg = REG_RING_INTERRUPT_BASE + - ring_interrupt_index(ring) / 32 * 4; + int index = ring_interrupt_index(ring) / 32 * 4; + int reg = REG_RING_INTERRUPT_BASE + index; int interrupt_bit = ring_interrupt_index(ring) & 31; int mask = 1 << interrupt_bit; u32 old, new; @@ -123,7 +138,11 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) "interrupt for %s %d is already %s\n", RING_TYPE(ring), ring->hop, active ? "enabled" : "disabled"); - iowrite32(new, ring->nhi->iobase + reg); + + if (active) + iowrite32(new, ring->nhi->iobase + reg); + else + nhi_mask_interrupt(ring->nhi, mask, index); } /* @@ -136,11 +155,11 @@ static void nhi_disable_interrupts(struct tb_nhi *nhi) int i = 0; /* disable interrupts */ for (i = 0; i < RING_INTERRUPT_REG_COUNT(nhi); i++) - iowrite32(0, nhi->iobase + REG_RING_INTERRUPT_BASE + 4 * i); + nhi_mask_interrupt(nhi, ~0, 4 * i); /* clear interrupt status bits */ for (i = 0; i < RING_NOTIFY_REG_COUNT(nhi); i++) - ioread32(nhi->iobase + REG_RING_NOTIFY_BASE + 4 * i); + nhi_clear_interrupt(nhi, 4 * i); } /* ring helper methods */ diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index faef165a919c..6ba295815477 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -93,6 +93,8 @@ struct ring_desc { #define REG_RING_INTERRUPT_BASE 0x38200 #define RING_INTERRUPT_REG_COUNT(nhi) ((31 + 2 * nhi->hop_count) / 32) +#define REG_RING_INTERRUPT_MASK_CLEAR_BASE 0x38208 + #define REG_INT_THROTTLING_RATE 0x38c00 /* Interrupt Vector Allocation */ -- cgit 1.4.1 From 3338d0b9acde770ee588eead5cac32c25e7048fc Mon Sep 17 00:00:00 2001 From: George Kennedy Date: Fri, 12 May 2023 06:08:48 -0500 Subject: vc_screen: reload load of struct vc_data pointer in vcs_write() to avoid UAF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8fb9ea65c9d1338b0d2bb0a9122dc942cdd32357 upstream. After a call to console_unlock() in vcs_write() the vc_data struct can be freed by vc_port_destruct(). Because of that, the struct vc_data pointer must be reloaded in the while loop in vcs_write() after console_lock() to avoid a UAF when vcs_size() is called. Syzkaller reported a UAF in vcs_size(). BUG: KASAN: slab-use-after-free in vcs_size (drivers/tty/vt/vc_screen.c:215) Read of size 4 at addr ffff8880beab89a8 by task repro_vcs_size/4119 Call Trace: __asan_report_load4_noabort (mm/kasan/report_generic.c:380) vcs_size (drivers/tty/vt/vc_screen.c:215) vcs_write (drivers/tty/vt/vc_screen.c:664) vfs_write (fs/read_write.c:582 fs/read_write.c:564) ... Allocated by task 1213: kmalloc_trace (mm/slab_common.c:1064) vc_allocate (./include/linux/slab.h:559 ./include/linux/slab.h:680 drivers/tty/vt/vt.c:1078 drivers/tty/vt/vt.c:1058) con_install (drivers/tty/vt/vt.c:3334) tty_init_dev (drivers/tty/tty_io.c:1303 drivers/tty/tty_io.c:1415 drivers/tty/tty_io.c:1392) tty_open (drivers/tty/tty_io.c:2082 drivers/tty/tty_io.c:2128) chrdev_open (fs/char_dev.c:415) do_dentry_open (fs/open.c:921) vfs_open (fs/open.c:1052) ... Freed by task 4116: kfree (mm/slab_common.c:1016) vc_port_destruct (drivers/tty/vt/vt.c:1044) tty_port_destructor (drivers/tty/tty_port.c:296) tty_port_put (drivers/tty/tty_port.c:312) vt_disallocate_all (drivers/tty/vt/vt_ioctl.c:662 (discriminator 2)) vt_ioctl (drivers/tty/vt/vt_ioctl.c:903) tty_ioctl (drivers/tty/tty_io.c:2778) ... The buggy address belongs to the object at ffff8880beab8800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 424 bytes inside of freed 1024-byte region [ffff8880beab8800, ffff8880beab8c00) The buggy address belongs to the physical page: page:00000000afc77580 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0xbeab8 head:00000000afc77580 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0xfffffc0010200(slab|head|node=0|zone=1|lastcpupid=0x1fffff) page_type: 0xffffffff() raw: 000fffffc0010200 ffff888100042dc0 ffffea000426de00 dead000000000002 raw: 0000000000000000 0000000000100010 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8880beab8880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8880beab8900: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff8880beab8980: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff8880beab8a00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8880beab8a80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Disabling lock debugging due to kernel taint Fixes: ac751efa6a0d ("console: rename acquire/release_console_sem() to console_lock/unlock()") Cc: stable Reported-by: syzkaller Signed-off-by: George Kennedy Reviewed-by: Thomas Weißschuh Link: https://lore.kernel.org/r/1683889728-10411-1-git-send-email-george.kennedy@oracle.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 1dc07f9214d5..01c96537fa36 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -656,10 +656,17 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) } } - /* The vcs_size might have changed while we slept to grab - * the user buffer, so recheck. + /* The vc might have been freed or vcs_size might have changed + * while we slept to grab the user buffer, so recheck. * Return data written up to now on failure. */ + vc = vcs_vc(inode, &viewed); + if (!vc) { + if (written) + break; + ret = -ENXIO; + goto unlock_out; + } size = vcs_size(vc, attr, false); if (size < 0) { if (written) -- cgit 1.4.1 From 595824a4509094098bb1d317633705588a1a6d76 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 18 May 2023 09:47:23 +0800 Subject: ceph: force updating the msg pointer in non-split case commit 4cafd0400bcb6187c0d4ab4d4b0229a89ac4f8c2 upstream. When the MClientSnap reqeust's op is not CEPH_SNAP_OP_SPLIT the request may still contain a list of 'split_realms', and we need to skip it anyway. Or it will be parsed as a corrupt snaptrace. Cc: stable@vger.kernel.org Link: https://tracker.ceph.com/issues/61200 Reported-by: Frank Schilder Signed-off-by: Xiubo Li Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- fs/ceph/snap.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 87007203f130..0b236ebd989f 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -1111,6 +1111,19 @@ skip_inode: continue; adjust_snap_realm_parent(mdsc, child, realm->ino); } + } else { + /* + * In the non-split case both 'num_split_inos' and + * 'num_split_realms' should be 0, making this a no-op. + * However the MDS happens to populate 'split_realms' list + * in one of the UPDATE op cases by mistake. + * + * Skip both lists just in case to ensure that 'p' is + * positioned at the start of realm info, as expected by + * ceph_update_snap_trace(). + */ + p += sizeof(u64) * num_split_inos; + p += sizeof(u64) * num_split_realms; } /* -- cgit 1.4.1 From 903e942500c64bf3acc95e65336366a9e777d581 Mon Sep 17 00:00:00 2001 From: Evan Quan Date: Thu, 11 May 2023 15:41:27 +0800 Subject: drm/amd/pm: fix possible power mode mismatch between driver and PMFW commit bf4823267a817f7c155876a125b94336d7113e77 upstream. PMFW may boots the ASIC with a different power mode from the system's real one. Notify PMFW explicitly the power mode the system in. This is needed only when ACDC switch via gpio is not supported. Signed-off-by: Evan Quan Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 18 ++++++++++++++++++ drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c | 20 +------------------- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 1 + 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 6d03459de561..91dfc229e34d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -723,6 +723,24 @@ static int smu_late_init(void *handle) return ret; } + /* + * Explicitly notify PMFW the power mode the system in. Since + * the PMFW may boot the ASIC with a different mode. + * For those supporting ACDC switch via gpio, PMFW will + * handle the switch automatically. Driver involvement + * is unnecessary. + */ + if (!smu->dc_controlled_by_gpio) { + ret = smu_set_power_source(smu, + adev->pm.ac_power ? SMU_POWER_SOURCE_AC : + SMU_POWER_SOURCE_DC); + if (ret) { + dev_err(adev->dev, "Failed to switch to %s mode!\n", + adev->pm.ac_power ? "AC" : "DC"); + return ret; + } + } + if ((adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 1)) || (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 3))) return 0; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index 0bcd4fe0ef17..ca278280865f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -3406,26 +3406,8 @@ static int navi10_post_smu_init(struct smu_context *smu) return 0; ret = navi10_run_umc_cdr_workaround(smu); - if (ret) { + if (ret) dev_err(adev->dev, "Failed to apply umc cdr workaround!\n"); - return ret; - } - - if (!smu->dc_controlled_by_gpio) { - /* - * For Navi1X, manually switch it to AC mode as PMFW - * may boot it with DC mode. - */ - ret = smu_v11_0_set_power_source(smu, - adev->pm.ac_power ? - SMU_POWER_SOURCE_AC : - SMU_POWER_SOURCE_DC); - if (ret) { - dev_err(adev->dev, "Failed to switch to %s mode!\n", - adev->pm.ac_power ? "AC" : "DC"); - return ret; - } - } return ret; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index ea4ec937f52e..9dd92bbdcefd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -1767,6 +1767,7 @@ static const struct pptable_funcs smu_v13_0_7_ppt_funcs = { .enable_mgpu_fan_boost = smu_v13_0_7_enable_mgpu_fan_boost, .get_power_limit = smu_v13_0_7_get_power_limit, .set_power_limit = smu_v13_0_set_power_limit, + .set_power_source = smu_v13_0_set_power_source, .get_power_profile_mode = smu_v13_0_7_get_power_profile_mode, .set_power_profile_mode = smu_v13_0_7_set_power_profile_mode, .set_tool_table_location = smu_v13_0_set_tool_table_location, -- cgit 1.4.1 From 9de5a985884acff022375a29bd5166bfabbded07 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 11 May 2023 10:40:03 -0400 Subject: drm/amdgpu/gmc11: implement get_vbios_fb_size() commit 68518294d00da6a2433357af75a63abc6030676e upstream. Implement get_vbios_fb_size() so we can properly reserve the vbios splash screen to avoid potential artifacts on the screen during the transition from the pre-OS console to the OS console. Acked-by: Sunil Khatri Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org # 6.1.x Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 2d11e8e7e123..7124347d2b6c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -31,6 +31,8 @@ #include "umc_v8_10.h" #include "athub/athub_3_0_0_sh_mask.h" #include "athub/athub_3_0_0_offset.h" +#include "dcn/dcn_3_2_0_offset.h" +#include "dcn/dcn_3_2_0_sh_mask.h" #include "oss/osssys_6_0_0_offset.h" #include "ivsrcid/vmc/irqsrcs_vmc_1_0.h" #include "navi10_enum.h" @@ -523,7 +525,24 @@ static void gmc_v11_0_get_vm_pte(struct amdgpu_device *adev, static unsigned gmc_v11_0_get_vbios_fb_size(struct amdgpu_device *adev) { - return 0; + u32 d1vga_control = RREG32_SOC15(DCE, 0, regD1VGA_CONTROL); + unsigned size; + + if (REG_GET_FIELD(d1vga_control, D1VGA_CONTROL, D1VGA_MODE_ENABLE)) { + size = AMDGPU_VBIOS_VGA_ALLOCATION; + } else { + u32 viewport; + u32 pitch; + + viewport = RREG32_SOC15(DCE, 0, regHUBP0_DCSURF_PRI_VIEWPORT_DIMENSION); + pitch = RREG32_SOC15(DCE, 0, regHUBPREQ0_DCSURF_SURFACE_PITCH); + size = (REG_GET_FIELD(viewport, + HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION, PRI_VIEWPORT_HEIGHT) * + REG_GET_FIELD(pitch, HUBPREQ0_DCSURF_SURFACE_PITCH, PITCH) * + 4); + } + + return size; } static const struct amdgpu_gmc_funcs gmc_v11_0_gmc_funcs = { -- cgit 1.4.1 From abfe2ffc004a0e6d23573b52826adf3dafdd9e02 Mon Sep 17 00:00:00 2001 From: Bas Nieuwenhuizen Date: Tue, 9 May 2023 18:49:46 +0200 Subject: drm/amdgpu/gfx10: Disable gfxoff before disabling powergating. commit 8173cab3368a13cdc3cad0bd5cf14e9399b0f501 upstream. Otherwise we get a full system lock (looks like a FW mess). Copied the order from the GFX9 powergating code. Fixes: 366468ff6c34 ("drm/amdgpu: Allow GfxOff on Vangogh as default") Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2545 Signed-off-by: Bas Nieuwenhuizen Tested-by: Guilherme G. Piccoli Cc: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index af94ac580d3e..2127aab74a68 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -8236,8 +8236,14 @@ static int gfx_v10_0_set_powergating_state(void *handle, case IP_VERSION(10, 3, 3): case IP_VERSION(10, 3, 6): case IP_VERSION(10, 3, 7): + if (!enable) + amdgpu_gfx_off_ctrl(adev, false); + gfx_v10_cntl_pg(adev, enable); - amdgpu_gfx_off_ctrl(adev, enable); + + if (enable) + amdgpu_gfx_off_ctrl(adev, true); + break; default: break; -- cgit 1.4.1 From 4e2f9159f9420ff481dc6ae00969d9406aa85e6f Mon Sep 17 00:00:00 2001 From: "Guilherme G. Piccoli" Date: Tue, 9 May 2023 18:49:47 +0200 Subject: drm/amdgpu/gfx11: Adjust gfxoff before powergating on gfx11 as well commit 11fbdda2ab6bf049e2869139c07016022b4e045b upstream. (Bas: speculative change to mirror gfx10/gfx9) Signed-off-by: Guilherme G. Piccoli Signed-off-by: Bas Nieuwenhuizen Cc: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org # 6.1.x Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 0dd2fe4f071e..9f0ec7bf68e1 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -5085,8 +5085,14 @@ static int gfx_v11_0_set_powergating_state(void *handle, break; case IP_VERSION(11, 0, 1): case IP_VERSION(11, 0, 4): + if (!enable) + amdgpu_gfx_off_ctrl(adev, false); + gfx_v11_cntl_pg(adev, enable); - amdgpu_gfx_off_ctrl(adev, enable); + + if (enable) + amdgpu_gfx_off_ctrl(adev, true); + break; default: break; -- cgit 1.4.1 From 055852074c968e3649cdcac5d112af17fd39bac8 Mon Sep 17 00:00:00 2001 From: Tong Liu01 Date: Thu, 6 Apr 2023 15:58:31 +0800 Subject: drm/amdgpu: refine get gpu clock counter method commit 5591a051b86be170a84943698ab140342602ff7b upstream. [why] regGOLDEN_TSC_COUNT_LOWER/regGOLDEN_TSC_COUNT_UPPER are protected and unaccessible under sriov. The clock counter high bit may update during reading process. [How] Replace regGOLDEN_TSC_COUNT_LOWER/regGOLDEN_TSC_COUNT_UPPER with regCP_MES_MTIME_LO/regCP_MES_MTIME_HI to get gpu clock under sriov. Refine get gpu clock counter method to make the result more precise. Signed-off-by: Tong Liu01 Acked-by: Luben Tuikov Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 9f0ec7bf68e1..7e58d59cab11 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -4641,11 +4641,24 @@ static int gfx_v11_0_post_soft_reset(void *handle) static uint64_t gfx_v11_0_get_gpu_clock_counter(struct amdgpu_device *adev) { uint64_t clock; + uint64_t clock_counter_lo, clock_counter_hi_pre, clock_counter_hi_after; amdgpu_gfx_off_ctrl(adev, false); mutex_lock(&adev->gfx.gpu_clock_mutex); - clock = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_LOWER) | - ((uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_UPPER) << 32ULL); + if (amdgpu_sriov_vf(adev)) { + clock_counter_hi_pre = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_HI); + clock_counter_lo = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_LO); + clock_counter_hi_after = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_HI); + if (clock_counter_hi_pre != clock_counter_hi_after) + clock_counter_lo = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_LO); + } else { + clock_counter_hi_pre = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_UPPER); + clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_LOWER); + clock_counter_hi_after = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_UPPER); + if (clock_counter_hi_pre != clock_counter_hi_after) + clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_LOWER); + } + clock = clock_counter_lo | (clock_counter_hi_after << 32ULL); mutex_unlock(&adev->gfx.gpu_clock_mutex); amdgpu_gfx_off_ctrl(adev, true); return clock; -- cgit 1.4.1 From 76313a63f7790d321d425b386e57918fed209545 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 10 Apr 2023 12:02:29 -0400 Subject: drm/amdgpu/gfx11: update gpu_clock_counter logic commit d5aa417808cf14c052ca042920b3c6b9f1dc6aa4 upstream. This code was written prior to previous updates to this logic for other chips. The RSC registers are part of SMUIO which is an always on block so there is no need to disable gfxoff. Additionally add the carryover and preemption checks. v2: rebase Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org # 6.1.y: 5591a051b86b: drm/amdgpu: refine get gpu clock counter method Cc: stable@vger.kernel.org # 6.2.y: 5591a051b86b: drm/amdgpu: refine get gpu clock counter method Cc: stable@vger.kernel.org # 6.3.y: 5591a051b86b: drm/amdgpu: refine get gpu clock counter method Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 7e58d59cab11..bc65fc1350f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -4643,24 +4643,27 @@ static uint64_t gfx_v11_0_get_gpu_clock_counter(struct amdgpu_device *adev) uint64_t clock; uint64_t clock_counter_lo, clock_counter_hi_pre, clock_counter_hi_after; - amdgpu_gfx_off_ctrl(adev, false); - mutex_lock(&adev->gfx.gpu_clock_mutex); if (amdgpu_sriov_vf(adev)) { + amdgpu_gfx_off_ctrl(adev, false); + mutex_lock(&adev->gfx.gpu_clock_mutex); clock_counter_hi_pre = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_HI); clock_counter_lo = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_LO); clock_counter_hi_after = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_HI); if (clock_counter_hi_pre != clock_counter_hi_after) clock_counter_lo = (uint64_t)RREG32_SOC15(GC, 0, regCP_MES_MTIME_LO); + mutex_unlock(&adev->gfx.gpu_clock_mutex); + amdgpu_gfx_off_ctrl(adev, true); } else { + preempt_disable(); clock_counter_hi_pre = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_UPPER); clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_LOWER); clock_counter_hi_after = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_UPPER); if (clock_counter_hi_pre != clock_counter_hi_after) clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_LOWER); + preempt_enable(); } clock = clock_counter_lo | (clock_counter_hi_after << 32ULL); - mutex_unlock(&adev->gfx.gpu_clock_mutex); - amdgpu_gfx_off_ctrl(adev, true); + return clock; } -- cgit 1.4.1 From 6e092fa42ed156e023461fe47558eb72218c9499 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 12 May 2023 13:52:04 +0200 Subject: dt-bindings: ata: ahci-ceva: Cover all 4 iommus entries commit a7844528722619d2f97740ae5ec747afff18c4be upstream. Current only one entry is enabled but IP itself is using 4 different IDs which are already listed in zynqmp.dtsi. sata: ahci@fd0c0000 { compatible = "ceva,ahci-1v84"; ... iommus = <&smmu 0x4c0>, <&smmu 0x4c1>, <&smmu 0x4c2>, <&smmu 0x4c3>; }; Fixes: 8ac47837f0e0 ("arm64: dts: zynqmp: Add missing iommu IDs") Cc: stable@vger.kernel.org # v5.12+ Signed-off-by: Michal Simek Acked-by: Krzysztof Kozlowski Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml b/Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml index 9b31f864e071..71364c6081ff 100644 --- a/Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml +++ b/Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml @@ -32,7 +32,7 @@ properties: maxItems: 1 iommus: - maxItems: 1 + maxItems: 4 power-domains: maxItems: 1 -- cgit 1.4.1 From fc983cf5ddd265aa35e9a84323f82446bfded620 Mon Sep 17 00:00:00 2001 From: Gaurav Batra Date: Thu, 4 May 2023 12:59:13 -0500 Subject: powerpc/iommu: DMA address offset is incorrectly calculated with 2MB TCEs commit 096339ab84f36beae0b1db25e0ce63fb3873e8b2 upstream. When DMA window is backed by 2MB TCEs, the DMA address for the mapped page should be the offset of the page relative to the 2MB TCE. The code was incorrectly setting the DMA address to the beginning of the TCE range. Mellanox driver is reporting timeout trying to ENABLE_HCA for an SR-IOV ethernet port, when DMA window is backed by 2MB TCEs. Fixes: 387273118714 ("powerps/pseries/dma: Add support for 2M IOMMU page size") Cc: stable@vger.kernel.org # v5.16+ Signed-off-by: Gaurav Batra Reviewed-by: Greg Joyce Reviewed-by: Brian King Signed-off-by: Michael Ellerman Link: https://msgid.link/20230504175913.83844-1-gbatra@linux.vnet.ibm.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/iommu.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index ee95937bdaf1..b8b7a189cd3c 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -517,7 +517,7 @@ int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl, /* Convert entry to a dma_addr_t */ entry += tbl->it_offset; dma_addr = entry << tbl->it_page_shift; - dma_addr |= (s->offset & ~IOMMU_PAGE_MASK(tbl)); + dma_addr |= (vaddr & ~IOMMU_PAGE_MASK(tbl)); DBG(" - %lu pages, entry: %lx, dma_addr: %lx\n", npages, entry, dma_addr); @@ -904,6 +904,7 @@ void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl, unsigned int order; unsigned int nio_pages, io_order; struct page *page; + int tcesize = (1 << tbl->it_page_shift); size = PAGE_ALIGN(size); order = get_order(size); @@ -930,7 +931,8 @@ void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl, memset(ret, 0, size); /* Set up tces to cover the allocated range */ - nio_pages = size >> tbl->it_page_shift; + nio_pages = IOMMU_PAGE_ALIGN(size, tbl) >> tbl->it_page_shift; + io_order = get_iommu_order(size, tbl); mapping = iommu_alloc(dev, tbl, ret, nio_pages, DMA_BIDIRECTIONAL, mask >> tbl->it_page_shift, io_order, 0); @@ -938,7 +940,8 @@ void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl, free_pages((unsigned long)ret, order); return NULL; } - *dma_handle = mapping; + + *dma_handle = mapping | ((u64)ret & (tcesize - 1)); return ret; } @@ -949,7 +952,7 @@ void iommu_free_coherent(struct iommu_table *tbl, size_t size, unsigned int nio_pages; size = PAGE_ALIGN(size); - nio_pages = size >> tbl->it_page_shift; + nio_pages = IOMMU_PAGE_ALIGN(size, tbl) >> tbl->it_page_shift; iommu_free(tbl, dma_handle, nio_pages); size = PAGE_ALIGN(size); free_pages((unsigned long)vaddr, get_order(size)); -- cgit 1.4.1 From 9a74146540cfea3918229c86d1a634fe96159ec8 Mon Sep 17 00:00:00 2001 From: Gaurav Batra Date: Fri, 5 May 2023 13:47:01 -0500 Subject: powerpc/iommu: Incorrect DDW Table is referenced for SR-IOV device commit 1f7aacc5eb9ed2cc17be7a90da5cd559effb9d59 upstream. For an SR-IOV device, while enabling DDW, a new table is created and added at index 1 in the group. In the below 2 scenarios, the table is incorrectly referenced at index 0 (which is where the table is for default DMA window). 1. When adding DDW This issue is exposed with "slub_debug". Error thrown out from dma_iommu_dma_supported() Warning: IOMMU offset too big for device mask mask: 0xffffffff, table offset: 0x800000000000000 2. During Dynamic removal of the PCI device. Error is from iommu_tce_table_put() since a NULL table pointer is passed in. Fixes: 381ceda88c4c ("powerpc/pseries/iommu: Make use of DDW for indirect mapping") Cc: stable@vger.kernel.org # v5.15+ Signed-off-by: Gaurav Batra Reviewed-by: Brian King Signed-off-by: Michael Ellerman Link: https://msgid.link/20230505184701.91613-1-gbatra@linux.vnet.ibm.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/dma-iommu.c | 4 +++- arch/powerpc/platforms/pseries/iommu.c | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 038ce8d9061d..8920862ffd79 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -144,7 +144,7 @@ static bool dma_iommu_bypass_supported(struct device *dev, u64 mask) /* We support DMA to/from any memory page via the iommu */ int dma_iommu_dma_supported(struct device *dev, u64 mask) { - struct iommu_table *tbl = get_iommu_table_base(dev); + struct iommu_table *tbl; if (dev_is_pci(dev) && dma_iommu_bypass_supported(dev, mask)) { /* @@ -162,6 +162,8 @@ int dma_iommu_dma_supported(struct device *dev, u64 mask) return 1; } + tbl = get_iommu_table_base(dev); + if (!tbl) { dev_err(dev, "Warning: IOMMU dma not supported: mask 0x%08llx, table unavailable\n", mask); return 0; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 561adac69022..efba867017e2 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -85,19 +85,24 @@ static struct iommu_table_group *iommu_pseries_alloc_group(int node) static void iommu_pseries_free_group(struct iommu_table_group *table_group, const char *node_name) { - struct iommu_table *tbl; - if (!table_group) return; - tbl = table_group->tables[0]; #ifdef CONFIG_IOMMU_API if (table_group->group) { iommu_group_put(table_group->group); BUG_ON(table_group->group); } #endif - iommu_tce_table_put(tbl); + + /* Default DMA window table is at index 0, while DDW at 1. SR-IOV + * adapters only have table on index 1. + */ + if (table_group->tables[0]) + iommu_tce_table_put(table_group->tables[0]); + + if (table_group->tables[1]) + iommu_tce_table_put(table_group->tables[1]); kfree(table_group); } -- cgit 1.4.1 From ae5d148965bcbc1b3359d4e37fd1dc81a8cdc262 Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Wed, 10 May 2023 17:54:03 -0700 Subject: tpm/tpm_tis: Disable interrupts for more Lenovo devices commit e7d3e5c4b1dd50a70b31524c3228c62bb41bbab2 upstream. The P360 Tiny suffers from an irq storm issue like the T490s, so add an entry for it to tpm_tis_dmi_table, and force polling. There also previously was a report from the previous attempt to enable interrupts that involved a ThinkPad L490. So an entry is added for it as well. Cc: stable@vger.kernel.org Reported-by: Peter Zijlstra # P360 Tiny Closes: https://lore.kernel.org/linux-integrity/20230505130731.GO83892@hirez.programming.kicks-ass.net/ Signed-off-by: Jerry Snitselaar Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_tis.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ed5dabd3c72d..4be19d8f3ca9 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -83,6 +83,22 @@ static const struct dmi_system_id tpm_tis_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T490s"), }, }, + { + .callback = tpm_tis_disable_irq, + .ident = "ThinkStation P360 Tiny", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkStation P360 Tiny"), + }, + }, + { + .callback = tpm_tis_disable_irq, + .ident = "ThinkPad L490", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L490"), + }, + }, {} }; -- cgit 1.4.1 From 0fc73f310c05cab229319c4caed7bf4d0a54c2ac Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 11 May 2023 21:42:24 +1000 Subject: powerpc/64s/radix: Fix soft dirty tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 66b2ca086210732954a7790d63d35542936fc664 upstream. It was reported that soft dirty tracking doesn't work when using the Radix MMU. The tracking is supposed to work by clearing the soft dirty bit for a mapping and then write protecting the PTE. If/when the page is written to, a page fault occurs and the soft dirty bit is added back via pte_mkdirty(). For example in wp_page_reuse(): entry = maybe_mkwrite(pte_mkdirty(entry), vma); if (ptep_set_access_flags(vma, vmf->address, vmf->pte, entry, 1)) update_mmu_cache(vma, vmf->address, vmf->pte); Unfortunately on radix _PAGE_SOFTDIRTY is being dropped by radix__ptep_set_access_flags(), called from ptep_set_access_flags(), meaning the soft dirty bit is not set even though the page has been written to. Fix it by adding _PAGE_SOFTDIRTY to the set of bits that are able to be changed in radix__ptep_set_access_flags(). Fixes: b0b5e9b13047 ("powerpc/mm/radix: Add radix pte #defines") Cc: stable@vger.kernel.org # v4.7+ Reported-by: Dan Horák Link: https://lore.kernel.org/r/20230511095558.56663a50f86bdc4cd97700b7@danny.cz Signed-off-by: Michael Ellerman Link: https://msgid.link/20230511114224.977423-1-mpe@ellerman.id.au Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/mm/book3s64/radix_pgtable.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 26245aaf12b8..2297aa764ecd 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -1040,8 +1040,8 @@ void radix__ptep_set_access_flags(struct vm_area_struct *vma, pte_t *ptep, pte_t entry, unsigned long address, int psize) { struct mm_struct *mm = vma->vm_mm; - unsigned long set = pte_val(entry) & (_PAGE_DIRTY | _PAGE_ACCESSED | - _PAGE_RW | _PAGE_EXEC); + unsigned long set = pte_val(entry) & (_PAGE_DIRTY | _PAGE_SOFT_DIRTY | + _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC); unsigned long change = pte_val(entry) ^ pte_val(*ptep); /* -- cgit 1.4.1 From acc2a40e428f12780004e1e9fce4722d88f909fd Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Wed, 10 May 2023 00:29:56 +0900 Subject: nilfs2: fix use-after-free bug of nilfs_root in nilfs_evict_inode() commit 9b5a04ac3ad9898c4745cba46ea26de74ba56a8e upstream. During unmount process of nilfs2, nothing holds nilfs_root structure after nilfs2 detaches its writer in nilfs_detach_log_writer(). However, since nilfs_evict_inode() uses nilfs_root for some cleanup operations, it may cause use-after-free read if inodes are left in "garbage_list" and released by nilfs_dispose_list() at the end of nilfs_detach_log_writer(). Fix this issue by modifying nilfs_evict_inode() to only clear inode without additional metadata changes that use nilfs_root if the file system is degraded to read-only or the writer is detached. Link: https://lkml.kernel.org/r/20230509152956.8313-1-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi Reported-by: syzbot+78d4495558999f55d1da@syzkaller.appspotmail.com Closes: https://lkml.kernel.org/r/00000000000099e5ac05fb1c3b85@google.com Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/inode.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 232dd7b6cca1..f6e44efb58e1 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -917,6 +917,7 @@ void nilfs_evict_inode(struct inode *inode) struct nilfs_transaction_info ti; struct super_block *sb = inode->i_sb; struct nilfs_inode_info *ii = NILFS_I(inode); + struct the_nilfs *nilfs; int ret; if (inode->i_nlink || !ii->i_root || unlikely(is_bad_inode(inode))) { @@ -929,6 +930,23 @@ void nilfs_evict_inode(struct inode *inode) truncate_inode_pages_final(&inode->i_data); + nilfs = sb->s_fs_info; + if (unlikely(sb_rdonly(sb) || !nilfs->ns_writer)) { + /* + * If this inode is about to be disposed after the file system + * has been degraded to read-only due to file system corruption + * or after the writer has been detached, do not make any + * changes that cause writes, just clear it. + * Do this check after read-locking ns_segctor_sem by + * nilfs_transaction_begin() in order to avoid a race with + * the writer detach operation. + */ + clear_inode(inode); + nilfs_clear_inode(inode); + nilfs_transaction_abort(sb); + return; + } + /* TODO: some of the following operations may fail. */ nilfs_truncate_bmap(ii, 0); nilfs_mark_inode_dirty(inode); -- cgit 1.4.1 From eeb63c07ba726137b813d4d2fa120d7830b04d81 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Fri, 19 May 2023 12:23:40 +0200 Subject: s390/dasd: fix command reject error on ESE devices commit c99bff34290f1b994073557b754aff86e4c7b22e upstream. Formatting a thin-provisioned (ESE) device that is part of a PPRC copy relation might fail with the following error: dasd-eckd 0.0.f500: An error occurred in the DASD device driver, reason=09 [...] 24 Byte: 0 MSG 4, no MSGb to SYSOP During format of an ESE disk the Release Allocated Space command is used. A bit in the payload of the command is set that is not allowed to be set for devices in a copy relation. This bit is set to allow the partial release of an extent. Check for the existence of a copy relation before setting the respective bit. Fixes: 91dc4a197569 ("s390/dasd: Add new ioctl to release space") Cc: stable@kernel.org # 5.3+ Signed-off-by: Stefan Haberland Reviewed-by: Jan Hoeppner Link: https://lore.kernel.org/r/20230519102340.3854819-2-sth@linux.ibm.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/s390/block/dasd_eckd.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index b20ce86b97b2..792e5d245bc3 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -127,6 +127,8 @@ static int prepare_itcw(struct itcw *, unsigned int, unsigned int, int, struct dasd_device *, struct dasd_device *, unsigned int, int, unsigned int, unsigned int, unsigned int, unsigned int); +static int dasd_eckd_query_pprc_status(struct dasd_device *, + struct dasd_pprc_data_sc4 *); /* initial attempt at a probe function. this can be simplified once * the other detection code is gone */ @@ -3732,6 +3734,26 @@ static int count_exts(unsigned int from, unsigned int to, int trks_per_ext) return count; } +static int dasd_in_copy_relation(struct dasd_device *device) +{ + struct dasd_pprc_data_sc4 *temp; + int rc; + + if (!dasd_eckd_pprc_enabled(device)) + return 0; + + temp = kzalloc(sizeof(*temp), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + rc = dasd_eckd_query_pprc_status(device, temp); + if (!rc) + rc = temp->dev_info[0].state; + + kfree(temp); + return rc; +} + /* * Release allocated space for a given range or an entire volume. */ @@ -3748,6 +3770,7 @@ dasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block, int cur_to_trk, cur_from_trk; struct dasd_ccw_req *cqr; u32 beg_cyl, end_cyl; + int copy_relation; struct ccw1 *ccw; int trks_per_ext; size_t ras_size; @@ -3759,6 +3782,10 @@ dasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block, if (dasd_eckd_ras_sanity_checks(device, first_trk, last_trk)) return ERR_PTR(-EINVAL); + copy_relation = dasd_in_copy_relation(device); + if (copy_relation < 0) + return ERR_PTR(copy_relation); + rq = req ? blk_mq_rq_to_pdu(req) : NULL; features = &private->features; @@ -3787,9 +3814,11 @@ dasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block, /* * This bit guarantees initialisation of tracks within an extent that is * not fully specified, but is only supported with a certain feature - * subset. + * subset and for devices not in a copy relation. */ - ras_data->op_flags.guarantee_init = !!(features->feature[56] & 0x01); + if (features->feature[56] & 0x01 && !copy_relation) + ras_data->op_flags.guarantee_init = 1; + ras_data->lss = private->conf.ned->ID; ras_data->dev_addr = private->conf.ned->unit_addr; ras_data->nr_exts = nr_exts; -- cgit 1.4.1 From 25e8d30507aa2f251152df1af7809e85b5538f4a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 20 Apr 2023 13:31:29 +0200 Subject: s390/crypto: use vector instructions only if available for ChaCha20 commit 8703dd6b238da0ec6c276e53836f8200983d3d9b upstream. Commit 349d03ffd5f6 ("crypto: s390 - add crypto library interface for ChaCha20") added a library interface to the s390 specific ChaCha20 implementation. However no check was added to verify if the required facilities are installed before branching into the assembler code. If compiled into the kernel, this will lead to the following crash, if vector instructions are not available: data exception: 0007 ilc:3 [#1] SMP Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.3.0-rc7+ #11 Hardware name: IBM 3931 A01 704 (KVM/Linux) Krnl PSW : 0704e00180000000 000000001857277a (chacha20_vx+0x32/0x818) R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3 Krnl GPRS: 0000037f0000000a ffffffffffffff60 000000008184b000 0000000019f5c8e6 0000000000000109 0000037fffb13c58 0000037fffb13c78 0000000019bb1780 0000037fffb13c58 0000000019f5c8e6 000000008184b000 0000000000000109 00000000802d8000 0000000000000109 0000000018571ebc 0000037fffb13718 Krnl Code: 000000001857276a: c07000b1f80b larl %r7,0000000019bb1780 0000000018572770: a708000a lhi %r0,10 #0000000018572774: e78950000c36 vlm %v24,%v25,0(%r5),0 >000000001857277a: e7a060000806 vl %v26,0(%r6),0 0000000018572780: e7bf70004c36 vlm %v27,%v31,0(%r7),4 0000000018572786: e70b00000456 vlr %v0,%v27 000000001857278c: e71800000456 vlr %v1,%v24 0000000018572792: e74b00000456 vlr %v4,%v27 Call Trace: [<000000001857277a>] chacha20_vx+0x32/0x818 Last Breaking-Event-Address: [<0000000018571eb6>] chacha20_crypt_s390.constprop.0+0x6e/0xd8 ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b Fix this by adding a missing MACHINE_HAS_VX check. Fixes: 349d03ffd5f6 ("crypto: s390 - add crypto library interface for ChaCha20") Reported-by: Marc Hartmayer Cc: # 5.19+ Reviewed-by: Harald Freudenberger [agordeev@linux.ibm.com: remove duplicates in commit message] Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- arch/s390/crypto/chacha-glue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/crypto/chacha-glue.c b/arch/s390/crypto/chacha-glue.c index 7752bd314558..5fae187f947a 100644 --- a/arch/s390/crypto/chacha-glue.c +++ b/arch/s390/crypto/chacha-glue.c @@ -82,7 +82,7 @@ void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src, * it cannot handle a block of data or less, but otherwise * it can handle data of arbitrary size */ - if (bytes <= CHACHA_BLOCK_SIZE || nrounds != 20) + if (bytes <= CHACHA_BLOCK_SIZE || nrounds != 20 || !MACHINE_HAS_VX) chacha_crypt_generic(state, dst, src, bytes, nrounds); else chacha20_crypt_s390(state, dst, src, bytes, -- cgit 1.4.1 From 02cf4a336e7ddc96c8b0b0bf85acdbb5d9a626a5 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 11 May 2023 17:04:41 +0200 Subject: s390/qdio: fix do_sqbs() inline assembly constraint commit 2862a2fdfae875888e3c1c3634e3422e01d98147 upstream. Use "a" constraint instead of "d" constraint to pass the state parameter to the do_sqbs() inline assembly. This prevents that general purpose register zero is used for the state parameter. If the compiler would select general purpose register zero this would be problematic for the used instruction in rsy format: the register used for the state parameter is a base register. If the base register is general purpose register zero the contents of the register are unexpectedly ignored when the instruction is executed. This only applies to z/VM guests using QIOASSIST with dedicated (pass through) QDIO-based devices such as FCP [zfcp driver] as well as real OSA or HiperSockets [qeth driver]. A possible symptom for this case using zfcp is the following repeating kernel message pattern: zfcp : A QDIO problem occurred zfcp : A QDIO problem occurred zfcp : qdio: ZFCP on SC using AI:1 QEBSM:1 PRI:1 TDD:1 SIGA: W zfcp : A QDIO problem occurred zfcp : A QDIO problem occurred Each of the qdio problem message can be accompanied by the following entries for the affected subchannel in /sys/kernel/debug/s390dbf/qdio_error/hex_ascii for zfcp or qeth: ccq: 69.... SQBS ERROR. Reviewed-by: Benjamin Block Cc: Steffen Maier Fixes: 8129ee164267 ("[PATCH] s390: qdio V=V pass-through") Cc: Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- drivers/s390/cio/qdio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 5ea6249d8180..641f0dbb65a9 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -95,7 +95,7 @@ static inline int do_sqbs(u64 token, unsigned char state, int queue, " lgr 1,%[token]\n" " .insn rsy,0xeb000000008a,%[qs],%[ccq],0(%[state])" : [ccq] "+&d" (_ccq), [qs] "+&d" (_queuestart) - : [state] "d" ((unsigned long)state), [token] "d" (token) + : [state] "a" ((unsigned long)state), [token] "d" (token) : "memory", "cc", "1"); *count = _ccq & 0xff; *start = _queuestart & 0xff; -- cgit 1.4.1 From 4e38a02b220743444b7dfd77821aeec23b98f841 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Thu, 20 Apr 2023 14:43:27 -0700 Subject: arm64: mte: Do not set PG_mte_tagged if tags were not initialized commit c4c597f1b367433c52c531dccd6859a39b4580fb upstream. The mte_sync_page_tags() function sets PG_mte_tagged if it initializes page tags. Then we return to mte_sync_tags(), which sets PG_mte_tagged again. At best, this is redundant. However, it is possible for mte_sync_page_tags() to return without having initialized tags for the page, i.e. in the case where check_swap is true (non-compound page), is_swap_pte(old_pte) is false and pte_is_tagged is false. So at worst, we set PG_mte_tagged on a page with uninitialized tags. This can happen if, for example, page migration causes a PTE for an untagged page to be replaced. If the userspace program subsequently uses mprotect() to enable PROT_MTE for that page, the uninitialized tags will be exposed to userspace. Fix it by removing the redundant call to set_page_mte_tagged(). Fixes: e059853d14ca ("arm64: mte: Fix/clarify the PG_mte_tagged semantics") Signed-off-by: Peter Collingbourne Cc: # 6.1 Link: https://linux-review.googlesource.com/id/Ib02d004d435b2ed87603b858ef7480f7b1463052 Reviewed-by: Catalin Marinas Reviewed-by: Alexandru Elisei Link: https://lore.kernel.org/r/20230420214327.2357985-1-pcc@google.com Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/mte.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c index 84a085d536f8..e20af03b4cdf 100644 --- a/arch/arm64/kernel/mte.c +++ b/arch/arm64/kernel/mte.c @@ -72,13 +72,10 @@ void mte_sync_tags(pte_t old_pte, pte_t pte) return; /* if PG_mte_tagged is set, tags have already been initialised */ - for (i = 0; i < nr_pages; i++, page++) { - if (!page_mte_tagged(page)) { + for (i = 0; i < nr_pages; i++, page++) + if (!page_mte_tagged(page)) mte_sync_page_tags(page, old_pte, check_swap, pte_is_tagged); - set_page_mte_tagged(page); - } - } /* ensure the tags are visible before the PTE is set */ smp_wmb(); -- cgit 1.4.1 From c46d3efb4d23dbcc325fcb37714bb3127368b2c6 Mon Sep 17 00:00:00 2001 From: Ze Gao Date: Wed, 17 May 2023 11:45:06 +0800 Subject: rethook: use preempt_{disable, enable}_notrace in rethook_trampoline_handler commit be243bacfb25f5219f2396d787408e8cf1301dd1 upstream. This patch replaces preempt_{disable, enable} with its corresponding notrace version in rethook_trampoline_handler so no worries about stack recursion or overflow introduced by preempt_count_{add, sub} under fprobe + rethook context. Link: https://lore.kernel.org/all/20230517034510.15639-2-zegao@tencent.com/ Fixes: 54ecbe6f1ed5 ("rethook: Add a generic return hook") Signed-off-by: Ze Gao Acked-by: Masami Hiramatsu (Google) Cc: Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/rethook.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/rethook.c b/kernel/trace/rethook.c index 32c3dfdb4d6a..60f6cb2b486b 100644 --- a/kernel/trace/rethook.c +++ b/kernel/trace/rethook.c @@ -288,7 +288,7 @@ unsigned long rethook_trampoline_handler(struct pt_regs *regs, * These loops must be protected from rethook_free_rcu() because those * are accessing 'rhn->rethook'. */ - preempt_disable(); + preempt_disable_notrace(); /* * Run the handler on the shadow stack. Do not unlink the list here because @@ -321,7 +321,7 @@ unsigned long rethook_trampoline_handler(struct pt_regs *regs, first = first->next; rethook_recycle(rhn); } - preempt_enable(); + preempt_enable_notrace(); return correct_ret_addr; } -- cgit 1.4.1 From 7099e14f601e7fdca2d278200cfbbb5c2dcf00f1 Mon Sep 17 00:00:00 2001 From: Ze Gao Date: Wed, 17 May 2023 11:45:09 +0800 Subject: rethook, fprobe: do not trace rethook related functions commit 571a2a50a8fc546145ffd3bf673547e9fe128ed2 upstream. These functions are already marked as NOKPROBE to prevent recursion and we have the same reason to blacklist them if rethook is used with fprobe, since they are beyond the recursion-free region ftrace can guard. Link: https://lore.kernel.org/all/20230517034510.15639-5-zegao@tencent.com/ Fixes: f3a112c0c40d ("x86,rethook,kprobes: Replace kretprobe with rethook on x86") Signed-off-by: Ze Gao Reviewed-by: Steven Rostedt (Google) Acked-by: Masami Hiramatsu (Google) Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/probes/Makefile | 2 ++ arch/s390/kernel/Makefile | 1 + arch/x86/kernel/Makefile | 1 + 3 files changed, 4 insertions(+) diff --git a/arch/riscv/kernel/probes/Makefile b/arch/riscv/kernel/probes/Makefile index 7f0840dcc31b..90dea3abdbb2 100644 --- a/arch/riscv/kernel/probes/Makefile +++ b/arch/riscv/kernel/probes/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_KPROBES) += kprobes_trampoline.o obj-$(CONFIG_KPROBES_ON_FTRACE) += ftrace.o obj-$(CONFIG_UPROBES) += uprobes.o decode-insn.o simulate-insn.o CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_rethook.o = $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_rethook_trampoline.o = $(CC_FLAGS_FTRACE) diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 5e6a23299790..7d92aa57715a 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -10,6 +10,7 @@ CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE) # Do not trace early setup code CFLAGS_REMOVE_early.o = $(CC_FLAGS_FTRACE) +CFLAGS_REMOVE_rethook.o = $(CC_FLAGS_FTRACE) endif diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index f901658d9f7c..42b5540dac58 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -17,6 +17,7 @@ CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_early_printk.o = -pg CFLAGS_REMOVE_head64.o = -pg CFLAGS_REMOVE_sev.o = -pg +CFLAGS_REMOVE_rethook.o = -pg endif KASAN_SANITIZE_head$(BITS).o := n -- cgit 1.4.1 From 682679fc953d42e63984ead73cab998b5ba94b7f Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Fri, 7 Apr 2023 10:14:29 -0600 Subject: remoteproc: imx_dsp_rproc: Fix kernel test robot sparse warning [ Upstream commit 3c497f624d40171ebead1a6705793100d92ecb85 ] This patch fixes the kernel test robot warning reported here: https://lore.kernel.org/bpf/642f916b.pPIKZ%2Fl%2F%2Fbw8tvIH%25lkp@intel.com/T/ Fixes: 408ec1ff0caa ("remoteproc: imx_dsp_rproc: Add custom memory copy implementation for i.MX DSP Cores") Link: https://lore.kernel.org/r/20230407161429.3973177-1-mathieu.poirier@linaro.org Tested-by: Iuliana Prodan Reviewed-by: Iuliana Prodan Signed-off-by: Mathieu Poirier Signed-off-by: Sasha Levin --- drivers/remoteproc/imx_dsp_rproc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index e8e23f6b8556..dcd07a6a5e94 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -727,12 +727,12 @@ static void imx_dsp_rproc_kick(struct rproc *rproc, int vqid) * The IRAM is part of the HiFi DSP. * According to hw specs only 32-bits writes are allowed. */ -static int imx_dsp_rproc_memcpy(void *dest, const void *src, size_t size) +static int imx_dsp_rproc_memcpy(void *dst, const void *src, size_t size) { + void __iomem *dest = (void __iomem *)dst; const u8 *src_byte = src; const u32 *source = src; u32 affected_mask; - u32 *dst = dest; int i, q, r; u32 tmp; @@ -745,7 +745,7 @@ static int imx_dsp_rproc_memcpy(void *dest, const void *src, size_t size) /* copy data in units of 32 bits at a time */ for (i = 0; i < q; i++) - writel(source[i], &dst[i]); + writel(source[i], dest + i * 4); if (r) { affected_mask = GENMASK(8 * r, 0); @@ -776,8 +776,8 @@ static int imx_dsp_rproc_memcpy(void *dest, const void *src, size_t size) */ static int imx_dsp_rproc_memset(void *addr, u8 value, size_t size) { + void __iomem *tmp_dst = (void __iomem *)addr; u32 tmp_val = value; - u32 *tmp_dst = addr; u32 affected_mask; int q, r; u32 tmp; -- cgit 1.4.1 From f05ccf6a6ac6a495e6e653236f44ab161ff94432 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 15 May 2023 22:08:50 -0700 Subject: crypto: testmgr - fix RNG performance in fuzz tests commit f900fde28883602b6c5e1027a6c912b673382aaf upstream. The performance of the crypto fuzz tests has greatly regressed since v5.18. When booting a kernel on an arm64 dev board with all software crypto algorithms and CONFIG_CRYPTO_MANAGER_EXTRA_TESTS enabled, the fuzz tests now take about 200 seconds to run, or about 325 seconds with lockdep enabled, compared to about 5 seconds before. The root cause is that the random number generation has become much slower due to commit d4150779e60f ("random32: use real rng for non-deterministic randomness"). On my same arm64 dev board, at the time the fuzz tests are run, get_random_u8() is about 345x slower than prandom_u32_state(), or about 469x if lockdep is enabled. Lockdep makes a big difference, but much of the rest comes from the get_random_*() functions taking a *very* slow path when the CRNG is not yet initialized. Since the crypto self-tests run early during boot, even having a hardware RNG driver enabled (CONFIG_CRYPTO_DEV_QCOM_RNG in my case) doesn't prevent this. x86 systems don't have this issue, but they still see a significant regression if lockdep is enabled. Converting the "Fully random bytes" case in generate_random_bytes() to use get_random_bytes() helps significantly, improving the test time to about 27 seconds. But that's still over 5x slower than before. This is all a bit silly, though, since the fuzz tests don't actually need cryptographically secure random numbers. So let's just make them use a non-cryptographically-secure RNG as they did before. The original prandom_u32() is gone now, so let's use prandom_u32_state() instead, with an explicitly managed state, like various other self-tests in the kernel source tree (rbtree_test.c, test_scanf.c, etc.) already do. This also has the benefit that no locking is required anymore, so performance should be even better than the original version that used prandom_u32(). Fixes: d4150779e60f ("random32: use real rng for non-deterministic randomness") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/testmgr.c | 266 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 169 insertions(+), 97 deletions(-) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 814d2dc87d7e..56c39a0c9495 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -852,12 +852,50 @@ static int prepare_keybuf(const u8 *key, unsigned int ksize, #ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS +/* + * The fuzz tests use prandom instead of the normal Linux RNG since they don't + * need cryptographically secure random numbers. This greatly improves the + * performance of these tests, especially if they are run before the Linux RNG + * has been initialized or if they are run on a lockdep-enabled kernel. + */ + +static inline void init_rnd_state(struct rnd_state *rng) +{ + prandom_seed_state(rng, get_random_u64()); +} + +static inline u8 prandom_u8(struct rnd_state *rng) +{ + return prandom_u32_state(rng); +} + +static inline u32 prandom_u32_below(struct rnd_state *rng, u32 ceil) +{ + /* + * This is slightly biased for non-power-of-2 values of 'ceil', but this + * isn't important here. + */ + return prandom_u32_state(rng) % ceil; +} + +static inline bool prandom_bool(struct rnd_state *rng) +{ + return prandom_u32_below(rng, 2); +} + +static inline u32 prandom_u32_inclusive(struct rnd_state *rng, + u32 floor, u32 ceil) +{ + return floor + prandom_u32_below(rng, ceil - floor + 1); +} + /* Generate a random length in range [0, max_len], but prefer smaller values */ -static unsigned int generate_random_length(unsigned int max_len) +static unsigned int generate_random_length(struct rnd_state *rng, + unsigned int max_len) { - unsigned int len = prandom_u32_max(max_len + 1); + unsigned int len = prandom_u32_below(rng, max_len + 1); - switch (prandom_u32_max(4)) { + switch (prandom_u32_below(rng, 4)) { case 0: return len % 64; case 1: @@ -870,43 +908,44 @@ static unsigned int generate_random_length(unsigned int max_len) } /* Flip a random bit in the given nonempty data buffer */ -static void flip_random_bit(u8 *buf, size_t size) +static void flip_random_bit(struct rnd_state *rng, u8 *buf, size_t size) { size_t bitpos; - bitpos = prandom_u32_max(size * 8); + bitpos = prandom_u32_below(rng, size * 8); buf[bitpos / 8] ^= 1 << (bitpos % 8); } /* Flip a random byte in the given nonempty data buffer */ -static void flip_random_byte(u8 *buf, size_t size) +static void flip_random_byte(struct rnd_state *rng, u8 *buf, size_t size) { - buf[prandom_u32_max(size)] ^= 0xff; + buf[prandom_u32_below(rng, size)] ^= 0xff; } /* Sometimes make some random changes to the given nonempty data buffer */ -static void mutate_buffer(u8 *buf, size_t size) +static void mutate_buffer(struct rnd_state *rng, u8 *buf, size_t size) { size_t num_flips; size_t i; /* Sometimes flip some bits */ - if (prandom_u32_max(4) == 0) { - num_flips = min_t(size_t, 1 << prandom_u32_max(8), size * 8); + if (prandom_u32_below(rng, 4) == 0) { + num_flips = min_t(size_t, 1 << prandom_u32_below(rng, 8), + size * 8); for (i = 0; i < num_flips; i++) - flip_random_bit(buf, size); + flip_random_bit(rng, buf, size); } /* Sometimes flip some bytes */ - if (prandom_u32_max(4) == 0) { - num_flips = min_t(size_t, 1 << prandom_u32_max(8), size); + if (prandom_u32_below(rng, 4) == 0) { + num_flips = min_t(size_t, 1 << prandom_u32_below(rng, 8), size); for (i = 0; i < num_flips; i++) - flip_random_byte(buf, size); + flip_random_byte(rng, buf, size); } } /* Randomly generate 'count' bytes, but sometimes make them "interesting" */ -static void generate_random_bytes(u8 *buf, size_t count) +static void generate_random_bytes(struct rnd_state *rng, u8 *buf, size_t count) { u8 b; u8 increment; @@ -915,11 +954,11 @@ static void generate_random_bytes(u8 *buf, size_t count) if (count == 0) return; - switch (prandom_u32_max(8)) { /* Choose a generation strategy */ + switch (prandom_u32_below(rng, 8)) { /* Choose a generation strategy */ case 0: case 1: /* All the same byte, plus optional mutations */ - switch (prandom_u32_max(4)) { + switch (prandom_u32_below(rng, 4)) { case 0: b = 0x00; break; @@ -927,28 +966,28 @@ static void generate_random_bytes(u8 *buf, size_t count) b = 0xff; break; default: - b = get_random_u8(); + b = prandom_u8(rng); break; } memset(buf, b, count); - mutate_buffer(buf, count); + mutate_buffer(rng, buf, count); break; case 2: /* Ascending or descending bytes, plus optional mutations */ - increment = get_random_u8(); - b = get_random_u8(); + increment = prandom_u8(rng); + b = prandom_u8(rng); for (i = 0; i < count; i++, b += increment) buf[i] = b; - mutate_buffer(buf, count); + mutate_buffer(rng, buf, count); break; default: /* Fully random bytes */ - for (i = 0; i < count; i++) - buf[i] = get_random_u8(); + prandom_bytes_state(rng, buf, count); } } -static char *generate_random_sgl_divisions(struct test_sg_division *divs, +static char *generate_random_sgl_divisions(struct rnd_state *rng, + struct test_sg_division *divs, size_t max_divs, char *p, char *end, bool gen_flushes, u32 req_flags) { @@ -959,24 +998,26 @@ static char *generate_random_sgl_divisions(struct test_sg_division *divs, unsigned int this_len; const char *flushtype_str; - if (div == &divs[max_divs - 1] || prandom_u32_max(2) == 0) + if (div == &divs[max_divs - 1] || prandom_bool(rng)) this_len = remaining; else - this_len = 1 + prandom_u32_max(remaining); + this_len = prandom_u32_inclusive(rng, 1, remaining); div->proportion_of_total = this_len; - if (prandom_u32_max(4) == 0) - div->offset = (PAGE_SIZE - 128) + prandom_u32_max(128); - else if (prandom_u32_max(2) == 0) - div->offset = prandom_u32_max(32); + if (prandom_u32_below(rng, 4) == 0) + div->offset = prandom_u32_inclusive(rng, + PAGE_SIZE - 128, + PAGE_SIZE - 1); + else if (prandom_bool(rng)) + div->offset = prandom_u32_below(rng, 32); else - div->offset = prandom_u32_max(PAGE_SIZE); - if (prandom_u32_max(8) == 0) + div->offset = prandom_u32_below(rng, PAGE_SIZE); + if (prandom_u32_below(rng, 8) == 0) div->offset_relative_to_alignmask = true; div->flush_type = FLUSH_TYPE_NONE; if (gen_flushes) { - switch (prandom_u32_max(4)) { + switch (prandom_u32_below(rng, 4)) { case 0: div->flush_type = FLUSH_TYPE_REIMPORT; break; @@ -988,7 +1029,7 @@ static char *generate_random_sgl_divisions(struct test_sg_division *divs, if (div->flush_type != FLUSH_TYPE_NONE && !(req_flags & CRYPTO_TFM_REQ_MAY_SLEEP) && - prandom_u32_max(2) == 0) + prandom_bool(rng)) div->nosimd = true; switch (div->flush_type) { @@ -1023,7 +1064,8 @@ static char *generate_random_sgl_divisions(struct test_sg_division *divs, } /* Generate a random testvec_config for fuzz testing */ -static void generate_random_testvec_config(struct testvec_config *cfg, +static void generate_random_testvec_config(struct rnd_state *rng, + struct testvec_config *cfg, char *name, size_t max_namelen) { char *p = name; @@ -1035,7 +1077,7 @@ static void generate_random_testvec_config(struct testvec_config *cfg, p += scnprintf(p, end - p, "random:"); - switch (prandom_u32_max(4)) { + switch (prandom_u32_below(rng, 4)) { case 0: case 1: cfg->inplace_mode = OUT_OF_PLACE; @@ -1050,12 +1092,12 @@ static void generate_random_testvec_config(struct testvec_config *cfg, break; } - if (prandom_u32_max(2) == 0) { + if (prandom_bool(rng)) { cfg->req_flags |= CRYPTO_TFM_REQ_MAY_SLEEP; p += scnprintf(p, end - p, " may_sleep"); } - switch (prandom_u32_max(4)) { + switch (prandom_u32_below(rng, 4)) { case 0: cfg->finalization_type = FINALIZATION_TYPE_FINAL; p += scnprintf(p, end - p, " use_final"); @@ -1070,36 +1112,37 @@ static void generate_random_testvec_config(struct testvec_config *cfg, break; } - if (!(cfg->req_flags & CRYPTO_TFM_REQ_MAY_SLEEP) && - prandom_u32_max(2) == 0) { + if (!(cfg->req_flags & CRYPTO_TFM_REQ_MAY_SLEEP) && prandom_bool(rng)) { cfg->nosimd = true; p += scnprintf(p, end - p, " nosimd"); } p += scnprintf(p, end - p, " src_divs=["); - p = generate_random_sgl_divisions(cfg->src_divs, + p = generate_random_sgl_divisions(rng, cfg->src_divs, ARRAY_SIZE(cfg->src_divs), p, end, (cfg->finalization_type != FINALIZATION_TYPE_DIGEST), cfg->req_flags); p += scnprintf(p, end - p, "]"); - if (cfg->inplace_mode == OUT_OF_PLACE && prandom_u32_max(2) == 0) { + if (cfg->inplace_mode == OUT_OF_PLACE && prandom_bool(rng)) { p += scnprintf(p, end - p, " dst_divs=["); - p = generate_random_sgl_divisions(cfg->dst_divs, + p = generate_random_sgl_divisions(rng, cfg->dst_divs, ARRAY_SIZE(cfg->dst_divs), p, end, false, cfg->req_flags); p += scnprintf(p, end - p, "]"); } - if (prandom_u32_max(2) == 0) { - cfg->iv_offset = 1 + prandom_u32_max(MAX_ALGAPI_ALIGNMASK); + if (prandom_bool(rng)) { + cfg->iv_offset = prandom_u32_inclusive(rng, 1, + MAX_ALGAPI_ALIGNMASK); p += scnprintf(p, end - p, " iv_offset=%u", cfg->iv_offset); } - if (prandom_u32_max(2) == 0) { - cfg->key_offset = 1 + prandom_u32_max(MAX_ALGAPI_ALIGNMASK); + if (prandom_bool(rng)) { + cfg->key_offset = prandom_u32_inclusive(rng, 1, + MAX_ALGAPI_ALIGNMASK); p += scnprintf(p, end - p, " key_offset=%u", cfg->key_offset); } @@ -1612,11 +1655,14 @@ static int test_hash_vec(const struct hash_testvec *vec, unsigned int vec_num, #ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS if (!noextratests) { + struct rnd_state rng; struct testvec_config cfg; char cfgname[TESTVEC_CONFIG_NAMELEN]; + init_rnd_state(&rng); + for (i = 0; i < fuzz_iterations; i++) { - generate_random_testvec_config(&cfg, cfgname, + generate_random_testvec_config(&rng, &cfg, cfgname, sizeof(cfgname)); err = test_hash_vec_cfg(vec, vec_name, &cfg, req, desc, tsgl, hashstate); @@ -1634,15 +1680,16 @@ static int test_hash_vec(const struct hash_testvec *vec, unsigned int vec_num, * Generate a hash test vector from the given implementation. * Assumes the buffers in 'vec' were already allocated. */ -static void generate_random_hash_testvec(struct shash_desc *desc, +static void generate_random_hash_testvec(struct rnd_state *rng, + struct shash_desc *desc, struct hash_testvec *vec, unsigned int maxkeysize, unsigned int maxdatasize, char *name, size_t max_namelen) { /* Data */ - vec->psize = generate_random_length(maxdatasize); - generate_random_bytes((u8 *)vec->plaintext, vec->psize); + vec->psize = generate_random_length(rng, maxdatasize); + generate_random_bytes(rng, (u8 *)vec->plaintext, vec->psize); /* * Key: length in range [1, maxkeysize], but usually choose maxkeysize. @@ -1652,9 +1699,9 @@ static void generate_random_hash_testvec(struct shash_desc *desc, vec->ksize = 0; if (maxkeysize) { vec->ksize = maxkeysize; - if (prandom_u32_max(4) == 0) - vec->ksize = 1 + prandom_u32_max(maxkeysize); - generate_random_bytes((u8 *)vec->key, vec->ksize); + if (prandom_u32_below(rng, 4) == 0) + vec->ksize = prandom_u32_inclusive(rng, 1, maxkeysize); + generate_random_bytes(rng, (u8 *)vec->key, vec->ksize); vec->setkey_error = crypto_shash_setkey(desc->tfm, vec->key, vec->ksize); @@ -1688,6 +1735,7 @@ static int test_hash_vs_generic_impl(const char *generic_driver, const unsigned int maxdatasize = (2 * PAGE_SIZE) - TESTMGR_POISON_LEN; const char *algname = crypto_hash_alg_common(tfm)->base.cra_name; const char *driver = crypto_ahash_driver_name(tfm); + struct rnd_state rng; char _generic_driver[CRYPTO_MAX_ALG_NAME]; struct crypto_shash *generic_tfm = NULL; struct shash_desc *generic_desc = NULL; @@ -1701,6 +1749,8 @@ static int test_hash_vs_generic_impl(const char *generic_driver, if (noextratests) return 0; + init_rnd_state(&rng); + if (!generic_driver) { /* Use default naming convention? */ err = build_generic_driver_name(algname, _generic_driver); if (err) @@ -1769,10 +1819,11 @@ static int test_hash_vs_generic_impl(const char *generic_driver, } for (i = 0; i < fuzz_iterations * 8; i++) { - generate_random_hash_testvec(generic_desc, &vec, + generate_random_hash_testvec(&rng, generic_desc, &vec, maxkeysize, maxdatasize, vec_name, sizeof(vec_name)); - generate_random_testvec_config(cfg, cfgname, sizeof(cfgname)); + generate_random_testvec_config(&rng, cfg, cfgname, + sizeof(cfgname)); err = test_hash_vec_cfg(&vec, vec_name, cfg, req, desc, tsgl, hashstate); @@ -2174,11 +2225,14 @@ static int test_aead_vec(int enc, const struct aead_testvec *vec, #ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS if (!noextratests) { + struct rnd_state rng; struct testvec_config cfg; char cfgname[TESTVEC_CONFIG_NAMELEN]; + init_rnd_state(&rng); + for (i = 0; i < fuzz_iterations; i++) { - generate_random_testvec_config(&cfg, cfgname, + generate_random_testvec_config(&rng, &cfg, cfgname, sizeof(cfgname)); err = test_aead_vec_cfg(enc, vec, vec_name, &cfg, req, tsgls); @@ -2194,6 +2248,7 @@ static int test_aead_vec(int enc, const struct aead_testvec *vec, #ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS struct aead_extra_tests_ctx { + struct rnd_state rng; struct aead_request *req; struct crypto_aead *tfm; const struct alg_test_desc *test_desc; @@ -2212,24 +2267,26 @@ struct aead_extra_tests_ctx { * here means the full ciphertext including the authentication tag. The * authentication tag (and hence also the ciphertext) is assumed to be nonempty. */ -static void mutate_aead_message(struct aead_testvec *vec, bool aad_iv, +static void mutate_aead_message(struct rnd_state *rng, + struct aead_testvec *vec, bool aad_iv, unsigned int ivsize) { const unsigned int aad_tail_size = aad_iv ? ivsize : 0; const unsigned int authsize = vec->clen - vec->plen; - if (prandom_u32_max(2) == 0 && vec->alen > aad_tail_size) { + if (prandom_bool(rng) && vec->alen > aad_tail_size) { /* Mutate the AAD */ - flip_random_bit((u8 *)vec->assoc, vec->alen - aad_tail_size); - if (prandom_u32_max(2) == 0) + flip_random_bit(rng, (u8 *)vec->assoc, + vec->alen - aad_tail_size); + if (prandom_bool(rng)) return; } - if (prandom_u32_max(2) == 0) { + if (prandom_bool(rng)) { /* Mutate auth tag (assuming it's at the end of ciphertext) */ - flip_random_bit((u8 *)vec->ctext + vec->plen, authsize); + flip_random_bit(rng, (u8 *)vec->ctext + vec->plen, authsize); } else { /* Mutate any part of the ciphertext */ - flip_random_bit((u8 *)vec->ctext, vec->clen); + flip_random_bit(rng, (u8 *)vec->ctext, vec->clen); } } @@ -2240,7 +2297,8 @@ static void mutate_aead_message(struct aead_testvec *vec, bool aad_iv, */ #define MIN_COLLISION_FREE_AUTHSIZE 8 -static void generate_aead_message(struct aead_request *req, +static void generate_aead_message(struct rnd_state *rng, + struct aead_request *req, const struct aead_test_suite *suite, struct aead_testvec *vec, bool prefer_inauthentic) @@ -2249,17 +2307,18 @@ static void generate_aead_message(struct aead_request *req, const unsigned int ivsize = crypto_aead_ivsize(tfm); const unsigned int authsize = vec->clen - vec->plen; const bool inauthentic = (authsize >= MIN_COLLISION_FREE_AUTHSIZE) && - (prefer_inauthentic || prandom_u32_max(4) == 0); + (prefer_inauthentic || + prandom_u32_below(rng, 4) == 0); /* Generate the AAD. */ - generate_random_bytes((u8 *)vec->assoc, vec->alen); + generate_random_bytes(rng, (u8 *)vec->assoc, vec->alen); if (suite->aad_iv && vec->alen >= ivsize) /* Avoid implementation-defined behavior. */ memcpy((u8 *)vec->assoc + vec->alen - ivsize, vec->iv, ivsize); - if (inauthentic && prandom_u32_max(2) == 0) { + if (inauthentic && prandom_bool(rng)) { /* Generate a random ciphertext. */ - generate_random_bytes((u8 *)vec->ctext, vec->clen); + generate_random_bytes(rng, (u8 *)vec->ctext, vec->clen); } else { int i = 0; struct scatterlist src[2], dst; @@ -2271,7 +2330,7 @@ static void generate_aead_message(struct aead_request *req, if (vec->alen) sg_set_buf(&src[i++], vec->assoc, vec->alen); if (vec->plen) { - generate_random_bytes((u8 *)vec->ptext, vec->plen); + generate_random_bytes(rng, (u8 *)vec->ptext, vec->plen); sg_set_buf(&src[i++], vec->ptext, vec->plen); } sg_init_one(&dst, vec->ctext, vec->alen + vec->clen); @@ -2291,7 +2350,7 @@ static void generate_aead_message(struct aead_request *req, * Mutate the authentic (ciphertext, AAD) pair to get an * inauthentic one. */ - mutate_aead_message(vec, suite->aad_iv, ivsize); + mutate_aead_message(rng, vec, suite->aad_iv, ivsize); } vec->novrfy = 1; if (suite->einval_allowed) @@ -2305,7 +2364,8 @@ static void generate_aead_message(struct aead_request *req, * If 'prefer_inauthentic' is true, then this function will generate inauthentic * test vectors (i.e. vectors with 'vec->novrfy=1') more often. */ -static void generate_random_aead_testvec(struct aead_request *req, +static void generate_random_aead_testvec(struct rnd_state *rng, + struct aead_request *req, struct aead_testvec *vec, const struct aead_test_suite *suite, unsigned int maxkeysize, @@ -2321,18 +2381,18 @@ static void generate_random_aead_testvec(struct aead_request *req, /* Key: length in [0, maxkeysize], but usually choose maxkeysize */ vec->klen = maxkeysize; - if (prandom_u32_max(4) == 0) - vec->klen = prandom_u32_max(maxkeysize + 1); - generate_random_bytes((u8 *)vec->key, vec->klen); + if (prandom_u32_below(rng, 4) == 0) + vec->klen = prandom_u32_below(rng, maxkeysize + 1); + generate_random_bytes(rng, (u8 *)vec->key, vec->klen); vec->setkey_error = crypto_aead_setkey(tfm, vec->key, vec->klen); /* IV */ - generate_random_bytes((u8 *)vec->iv, ivsize); + generate_random_bytes(rng, (u8 *)vec->iv, ivsize); /* Tag length: in [0, maxauthsize], but usually choose maxauthsize */ authsize = maxauthsize; - if (prandom_u32_max(4) == 0) - authsize = prandom_u32_max(maxauthsize + 1); + if (prandom_u32_below(rng, 4) == 0) + authsize = prandom_u32_below(rng, maxauthsize + 1); if (prefer_inauthentic && authsize < MIN_COLLISION_FREE_AUTHSIZE) authsize = MIN_COLLISION_FREE_AUTHSIZE; if (WARN_ON(authsize > maxdatasize)) @@ -2341,11 +2401,11 @@ static void generate_random_aead_testvec(struct aead_request *req, vec->setauthsize_error = crypto_aead_setauthsize(tfm, authsize); /* AAD, plaintext, and ciphertext lengths */ - total_len = generate_random_length(maxdatasize); - if (prandom_u32_max(4) == 0) + total_len = generate_random_length(rng, maxdatasize); + if (prandom_u32_below(rng, 4) == 0) vec->alen = 0; else - vec->alen = generate_random_length(total_len); + vec->alen = generate_random_length(rng, total_len); vec->plen = total_len - vec->alen; vec->clen = vec->plen + authsize; @@ -2356,7 +2416,7 @@ static void generate_random_aead_testvec(struct aead_request *req, vec->novrfy = 0; vec->crypt_error = 0; if (vec->setkey_error == 0 && vec->setauthsize_error == 0) - generate_aead_message(req, suite, vec, prefer_inauthentic); + generate_aead_message(rng, req, suite, vec, prefer_inauthentic); snprintf(name, max_namelen, "\"random: alen=%u plen=%u authsize=%u klen=%u novrfy=%d\"", vec->alen, vec->plen, authsize, vec->klen, vec->novrfy); @@ -2368,7 +2428,7 @@ static void try_to_generate_inauthentic_testvec( int i; for (i = 0; i < 10; i++) { - generate_random_aead_testvec(ctx->req, &ctx->vec, + generate_random_aead_testvec(&ctx->rng, ctx->req, &ctx->vec, &ctx->test_desc->suite.aead, ctx->maxkeysize, ctx->maxdatasize, ctx->vec_name, @@ -2399,7 +2459,8 @@ static int test_aead_inauthentic_inputs(struct aead_extra_tests_ctx *ctx) */ try_to_generate_inauthentic_testvec(ctx); if (ctx->vec.novrfy) { - generate_random_testvec_config(&ctx->cfg, ctx->cfgname, + generate_random_testvec_config(&ctx->rng, &ctx->cfg, + ctx->cfgname, sizeof(ctx->cfgname)); err = test_aead_vec_cfg(DECRYPT, &ctx->vec, ctx->vec_name, &ctx->cfg, @@ -2489,12 +2550,13 @@ static int test_aead_vs_generic_impl(struct aead_extra_tests_ctx *ctx) * the other implementation against them. */ for (i = 0; i < fuzz_iterations * 8; i++) { - generate_random_aead_testvec(generic_req, &ctx->vec, + generate_random_aead_testvec(&ctx->rng, generic_req, &ctx->vec, &ctx->test_desc->suite.aead, ctx->maxkeysize, ctx->maxdatasize, ctx->vec_name, sizeof(ctx->vec_name), false); - generate_random_testvec_config(&ctx->cfg, ctx->cfgname, + generate_random_testvec_config(&ctx->rng, &ctx->cfg, + ctx->cfgname, sizeof(ctx->cfgname)); if (!ctx->vec.novrfy) { err = test_aead_vec_cfg(ENCRYPT, &ctx->vec, @@ -2533,6 +2595,7 @@ static int test_aead_extra(const struct alg_test_desc *test_desc, ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + init_rnd_state(&ctx->rng); ctx->req = req; ctx->tfm = crypto_aead_reqtfm(req); ctx->test_desc = test_desc; @@ -2922,11 +2985,14 @@ static int test_skcipher_vec(int enc, const struct cipher_testvec *vec, #ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS if (!noextratests) { + struct rnd_state rng; struct testvec_config cfg; char cfgname[TESTVEC_CONFIG_NAMELEN]; + init_rnd_state(&rng); + for (i = 0; i < fuzz_iterations; i++) { - generate_random_testvec_config(&cfg, cfgname, + generate_random_testvec_config(&rng, &cfg, cfgname, sizeof(cfgname)); err = test_skcipher_vec_cfg(enc, vec, vec_name, &cfg, req, tsgls); @@ -2944,7 +3010,8 @@ static int test_skcipher_vec(int enc, const struct cipher_testvec *vec, * Generate a symmetric cipher test vector from the given implementation. * Assumes the buffers in 'vec' were already allocated. */ -static void generate_random_cipher_testvec(struct skcipher_request *req, +static void generate_random_cipher_testvec(struct rnd_state *rng, + struct skcipher_request *req, struct cipher_testvec *vec, unsigned int maxdatasize, char *name, size_t max_namelen) @@ -2958,17 +3025,17 @@ static void generate_random_cipher_testvec(struct skcipher_request *req, /* Key: length in [0, maxkeysize], but usually choose maxkeysize */ vec->klen = maxkeysize; - if (prandom_u32_max(4) == 0) - vec->klen = prandom_u32_max(maxkeysize + 1); - generate_random_bytes((u8 *)vec->key, vec->klen); + if (prandom_u32_below(rng, 4) == 0) + vec->klen = prandom_u32_below(rng, maxkeysize + 1); + generate_random_bytes(rng, (u8 *)vec->key, vec->klen); vec->setkey_error = crypto_skcipher_setkey(tfm, vec->key, vec->klen); /* IV */ - generate_random_bytes((u8 *)vec->iv, ivsize); + generate_random_bytes(rng, (u8 *)vec->iv, ivsize); /* Plaintext */ - vec->len = generate_random_length(maxdatasize); - generate_random_bytes((u8 *)vec->ptext, vec->len); + vec->len = generate_random_length(rng, maxdatasize); + generate_random_bytes(rng, (u8 *)vec->ptext, vec->len); /* If the key couldn't be set, no need to continue to encrypt. */ if (vec->setkey_error) @@ -3010,6 +3077,7 @@ static int test_skcipher_vs_generic_impl(const char *generic_driver, const unsigned int maxdatasize = (2 * PAGE_SIZE) - TESTMGR_POISON_LEN; const char *algname = crypto_skcipher_alg(tfm)->base.cra_name; const char *driver = crypto_skcipher_driver_name(tfm); + struct rnd_state rng; char _generic_driver[CRYPTO_MAX_ALG_NAME]; struct crypto_skcipher *generic_tfm = NULL; struct skcipher_request *generic_req = NULL; @@ -3027,6 +3095,8 @@ static int test_skcipher_vs_generic_impl(const char *generic_driver, if (strncmp(algname, "kw(", 3) == 0) return 0; + init_rnd_state(&rng); + if (!generic_driver) { /* Use default naming convention? */ err = build_generic_driver_name(algname, _generic_driver); if (err) @@ -3111,9 +3181,11 @@ static int test_skcipher_vs_generic_impl(const char *generic_driver, } for (i = 0; i < fuzz_iterations * 8; i++) { - generate_random_cipher_testvec(generic_req, &vec, maxdatasize, + generate_random_cipher_testvec(&rng, generic_req, &vec, + maxdatasize, vec_name, sizeof(vec_name)); - generate_random_testvec_config(cfg, cfgname, sizeof(cfgname)); + generate_random_testvec_config(&rng, cfg, cfgname, + sizeof(cfgname)); err = test_skcipher_vec_cfg(ENCRYPT, &vec, vec_name, cfg, req, tsgls); -- cgit 1.4.1 From 09bf14907d8643f3a316d519b2df41ace2db0d7d Mon Sep 17 00:00:00 2001 From: Li Ma Date: Fri, 20 Jan 2023 15:41:22 +0800 Subject: drm/amdgpu: declare firmware for new MES 11.0.4 commit a462ef872fd1e83ebd075cf82d91f111acaa629e upstream. To support new mes ip block Signed-off-by: Li Ma Reviewed-by: Yifan Zhang Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/mes_v11_0.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index e3168149ca8f..9d605945a26a 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -40,6 +40,8 @@ MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes1.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes1.bin"); static int mes_v11_0_hw_fini(void *handle); static int mes_v11_0_kiq_hw_init(struct amdgpu_device *adev); -- cgit 1.4.1 From 616843d5a11bdcad5d65e230a2e372b5701108be Mon Sep 17 00:00:00 2001 From: Jack Xiao Date: Fri, 24 Mar 2023 16:55:15 +0800 Subject: drm/amd/amdgpu: introduce gc_*_mes_2.bin v2 commit 97998b893c3000b27a780a4982e16cfc8f4ea555 upstream. To avoid new mes fw running with old driver, rename mes schq fw to gc_*_mes_2.bin. v2: add MODULE_FIRMWARE declaration v3: squash in fixup patch Signed-off-by: Jack Xiao Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c | 26 ++++++++++++++++++++++---- drivers/gpu/drm/amd/amdgpu/mes_v11_0.c | 10 +++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c index 82e27bd4f038..7e8b7171068d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c @@ -1432,13 +1432,31 @@ int amdgpu_mes_init_microcode(struct amdgpu_device *adev, int pipe) struct amdgpu_firmware_info *info; char ucode_prefix[30]; char fw_name[40]; + bool need_retry = false; int r; - amdgpu_ucode_ip_version_decode(adev, GC_HWIP, ucode_prefix, sizeof(ucode_prefix)); - snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_mes%s.bin", - ucode_prefix, - pipe == AMDGPU_MES_SCHED_PIPE ? "" : "1"); + amdgpu_ucode_ip_version_decode(adev, GC_HWIP, ucode_prefix, + sizeof(ucode_prefix)); + if (adev->ip_versions[GC_HWIP][0] >= IP_VERSION(11, 0, 0)) { + snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_mes%s.bin", + ucode_prefix, + pipe == AMDGPU_MES_SCHED_PIPE ? "_2" : "1"); + need_retry = true; + } else { + snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_mes%s.bin", + ucode_prefix, + pipe == AMDGPU_MES_SCHED_PIPE ? "" : "1"); + } + r = amdgpu_ucode_request(adev, &adev->mes.fw[pipe], fw_name); + if (r && need_retry && pipe == AMDGPU_MES_SCHED_PIPE) { + snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_mes.bin", + ucode_prefix); + DRM_INFO("try to fall back to %s\n", fw_name); + r = amdgpu_ucode_request(adev, &adev->mes.fw[pipe], + fw_name); + } + if (r) goto out; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index 9d605945a26a..804572932579 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -32,15 +32,15 @@ #include "v11_structs.h" #include "mes_v11_api_def.h" -MODULE_FIRMWARE("amdgpu/gc_11_0_0_mes.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_0_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_0_mes1.bin"); -MODULE_FIRMWARE("amdgpu/gc_11_0_1_mes.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_1_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_1_mes1.bin"); -MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes1.bin"); -MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes1.bin"); -MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes1.bin"); static int mes_v11_0_hw_fini(void *handle); -- cgit 1.4.1 From da9a8dc33da2083d762c9d84ad1f2752a5a503f4 Mon Sep 17 00:00:00 2001 From: Li Ma Date: Wed, 12 Apr 2023 22:06:34 +0800 Subject: drm/amdgpu: reserve the old gc_11_0_*_mes.bin commit 8855818ce7554fb7420200187fac9c3b69500da0 upstream. Reserve the MOUDLE_FIRMWARE declaration of gc_11_0_*_mes.bin to fix falling back to old mes bin on failure via autoload. Fixes: 97998b893c30 ("drm/amd/amdgpu: introduce gc_*_mes_2.bin v2") Signed-off-by: Li Ma Reviewed-by: Yifan Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/mes_v11_0.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index 804572932579..08766b678436 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -32,14 +32,19 @@ #include "v11_structs.h" #include "mes_v11_api_def.h" +MODULE_FIRMWARE("amdgpu/gc_11_0_0_mes.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_0_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_0_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_1_mes.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_1_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_1_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes1.bin"); -- cgit 1.4.1 From a343b0dd87b42ba9d508fbf7d0c06f744c2e0954 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 May 2023 17:32:53 +0100 Subject: Linux 6.1.30 Link: https://lore.kernel.org/r/20230522190405.880733338@linuxfoundation.org Tested-by: Chris Paterson (CIP) Tested-by: Florian Fainelli Tested-by: Shuah Khan Tested-by: Theodore Ts'o Tested-by: Bagas Sanjaya Tested-by: Takeshi Ogasawara Tested-by: Jon Hunter Tested-by: Salvatore Bonaccorso Tested-by: Linux Kernel Functional Testing Tested-by: Conor Dooley Tested-by: Markus Reichelt Tested-by: Ron Economos Tested-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f02f6e1ac574..aeb58c7a4baa 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 29 +SUBLEVEL = 30 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From 1ec145277a262fcbdaae2e2edcd6ae5085ef575e Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Fri, 12 May 2023 08:45:24 +0800 Subject: usb: dwc3: fix gadget mode suspend interrupt handler issue [ Upstream commit 4e8ef34e36f2839ef8c8da521ab7035956436818 ] When work in gadget mode, currently driver doesn't update software level link_state correctly as link state change event is not enabled for most devices, in function dwc3_gadget_suspend_interrupt(), it will only pass suspend event to UDC core when software level link state changes, so when interrupt generated in sequences of suspend -> reset -> conndone -> suspend, link state is not updated during reset and conndone, so second suspend interrupt event will not pass to UDC core. Remove link_state compare in dwc3_gadget_suspend_interrupt() and add a suspended flag to replace the compare function. Fixes: 799e9dc82968 ("usb: dwc3: gadget: conditionally disable Link State change events") Cc: stable Acked-by: Thinh Nguyen Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230512004524.31950-1-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 582ebd9cf9c2..bb57bc9bc17c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1110,6 +1110,7 @@ struct dwc3_scratchpad_array { * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. * @dis_split_quirk: set to disable split boundary. + * @suspended: set to track suspend event due to U3/L2. * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. * @max_cfg_eps: current max number of IN eps used across all USB configs. @@ -1327,6 +1328,7 @@ struct dwc3 { unsigned dis_split_quirk:1; unsigned async_callbacks:1; + unsigned suspended:1; u16 imod_interval; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d12cb8f0d1f4..8cd0d919ef63 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3828,6 +3828,8 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { int reg; + dwc->suspended = false; + dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET); reg = dwc3_readl(dwc->regs, DWC3_DCTL); @@ -3859,6 +3861,8 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { u32 reg; + dwc->suspended = false; + /* * Ideally, dwc3_reset_gadget() would trigger the function * drivers to stop any active transfers through ep disable. @@ -4088,6 +4092,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) { + dwc->suspended = false; + /* * TODO take core out of low power mode when that's * implemented. @@ -4203,8 +4209,10 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, { enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; - if (dwc->link_state != next && next == DWC3_LINK_STATE_U3) + if (!dwc->suspended && next == DWC3_LINK_STATE_U3) { + dwc->suspended = true; dwc3_suspend_gadget(dwc); + } dwc->link_state = next; } -- cgit 1.4.1 From 5c4c8075bc8a2163e42c4fa360e19d0c9548f4a8 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:25 +0100 Subject: tpm, tpm_tis: Avoid cache incoherency in test for interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 858e8b792d06f45c427897bd90205a1d90bf430f ] The interrupt handler that sets the boolean variable irq_tested may run on another CPU as the thread that checks irq_tested as part of the irq test in tpm_tis_send(). Since nothing guarantees cache coherency between CPUs for unsynchronized accesses to boolean variables the testing thread might not perceive the value change done in the interrupt handler. Avoid this issue by setting the bit TPM_TIS_IRQ_TESTED in the flags field of the tpm_tis_data struct and by accessing this field with the bit manipulating functions that provide cache coherency. Also convert all other existing sites to use the proper macros when accessing this bitfield. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Stable-dep-of: 1398aa803f19 ("tpm_tis: Use tpm_chip_{start,stop} decoration inside tpm_tis_resume") Signed-off-by: Sasha Levin --- drivers/char/tpm/tpm_tis.c | 2 +- drivers/char/tpm/tpm_tis_core.c | 21 +++++++++++---------- drivers/char/tpm/tpm_tis_core.h | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 4be19d8f3ca9..0d084d6652c4 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -243,7 +243,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info) irq = tpm_info->irq; if (itpm || is_itpm(ACPI_COMPANION(dev))) - phy->priv.flags |= TPM_TIS_ITPM_WORKAROUND; + set_bit(TPM_TIS_ITPM_WORKAROUND, &phy->priv.flags); return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg, ACPI_HANDLE(dev)); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index eecfbd7e9786..6b05a84c3a20 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -376,7 +376,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int rc, status, burstcnt; size_t count = 0; - bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND; + bool itpm = test_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags); status = tpm_tis_status(chip); if ((status & TPM_STS_COMMAND_READY) == 0) { @@ -509,7 +509,8 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) int rc, irq; struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested) + if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || + test_bit(TPM_TIS_IRQ_TESTED, &priv->flags)) return tpm_tis_send_main(chip, buf, len); /* Verify receipt of the expected IRQ */ @@ -519,11 +520,11 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = tpm_tis_send_main(chip, buf, len); priv->irq = irq; chip->flags |= TPM_CHIP_FLAG_IRQ; - if (!priv->irq_tested) + if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags)) tpm_msleep(1); - if (!priv->irq_tested) + if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags)) disable_interrupts(chip); - priv->irq_tested = true; + set_bit(TPM_TIS_IRQ_TESTED, &priv->flags); return rc; } @@ -666,7 +667,7 @@ static int probe_itpm(struct tpm_chip *chip) size_t len = sizeof(cmd_getticks); u16 vendor; - if (priv->flags & TPM_TIS_ITPM_WORKAROUND) + if (test_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags)) return 0; rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor); @@ -686,13 +687,13 @@ static int probe_itpm(struct tpm_chip *chip) tpm_tis_ready(chip); - priv->flags |= TPM_TIS_ITPM_WORKAROUND; + set_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags); rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) dev_info(&chip->dev, "Detected an iTPM.\n"); else { - priv->flags &= ~TPM_TIS_ITPM_WORKAROUND; + clear_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags); rc = -EFAULT; } @@ -736,7 +737,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) if (interrupt == 0) return IRQ_NONE; - priv->irq_tested = true; + set_bit(TPM_TIS_IRQ_TESTED, &priv->flags); if (interrupt & TPM_INTF_DATA_AVAIL_INT) wake_up_interruptible(&priv->read_queue); if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) @@ -819,7 +820,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, if (rc < 0) goto restore_irqs; - priv->irq_tested = false; + clear_bit(TPM_TIS_IRQ_TESTED, &priv->flags); /* Generate an interrupt by having the core call through to * tpm_tis_send diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 1d51d5168fb6..4a58b870b418 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -87,6 +87,7 @@ enum tpm_tis_flags { TPM_TIS_ITPM_WORKAROUND = BIT(0), TPM_TIS_INVALID_STATUS = BIT(1), TPM_TIS_DEFAULT_CANCELLATION = BIT(2), + TPM_TIS_IRQ_TESTED = BIT(3), }; struct tpm_tis_data { @@ -95,7 +96,6 @@ struct tpm_tis_data { unsigned int locality_count; int locality; int irq; - bool irq_tested; unsigned long flags; void __iomem *ilb_base_addr; u16 clkrun_enabled; -- cgit 1.4.1 From c5a5d33886a73f3e22e749da8306370090a94a13 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:30 +0100 Subject: tpm, tpm_tis: Only handle supported interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e87fcf0dc2b47fac5b4824f00f74dfbcd4acd363 ] According to the TPM Interface Specification (TIS) support for "stsValid" and "commandReady" interrupts is only optional. This has to be taken into account when handling the interrupts in functions like wait_for_tpm_stat(). To determine the supported interrupts use the capability query. Also adjust wait_for_tpm_stat() to only wait for interrupt reported status changes. After that process all the remaining status changes by polling the status register. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Stable-dep-of: 1398aa803f19 ("tpm_tis: Use tpm_chip_{start,stop} decoration inside tpm_tis_resume") Signed-off-by: Sasha Levin --- drivers/char/tpm/tpm_tis_core.c | 120 ++++++++++++++++++++++++---------------- drivers/char/tpm/tpm_tis_core.h | 1 + 2 files changed, 73 insertions(+), 48 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 6b05a84c3a20..a35c117ee7c8 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -53,41 +53,63 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, long rc; u8 status; bool canceled = false; + u8 sts_mask = 0; + int ret = 0; /* check current status */ status = chip->ops->status(chip); if ((status & mask) == mask) return 0; - stop = jiffies + timeout; + /* check what status changes can be handled by irqs */ + if (priv->int_mask & TPM_INTF_STS_VALID_INT) + sts_mask |= TPM_STS_VALID; - if (chip->flags & TPM_CHIP_FLAG_IRQ) { + if (priv->int_mask & TPM_INTF_DATA_AVAIL_INT) + sts_mask |= TPM_STS_DATA_AVAIL; + + if (priv->int_mask & TPM_INTF_CMD_READY_INT) + sts_mask |= TPM_STS_COMMAND_READY; + + sts_mask &= mask; + + stop = jiffies + timeout; + /* process status changes with irq support */ + if (sts_mask) { + ret = -ETIME; again: timeout = stop - jiffies; if ((long)timeout <= 0) return -ETIME; rc = wait_event_interruptible_timeout(*queue, - wait_for_tpm_stat_cond(chip, mask, check_cancel, + wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, &canceled), timeout); if (rc > 0) { if (canceled) return -ECANCELED; - return 0; + ret = 0; } if (rc == -ERESTARTSYS && freezing(current)) { clear_thread_flag(TIF_SIGPENDING); goto again; } - } else { - do { - usleep_range(priv->timeout_min, - priv->timeout_max); - status = chip->ops->status(chip); - if ((status & mask) == mask) - return 0; - } while (time_before(jiffies, stop)); } + + if (ret) + return ret; + + mask &= ~sts_mask; + if (!mask) /* all done */ + return 0; + /* process status changes without irq support */ + do { + status = chip->ops->status(chip); + if ((status & mask) == mask) + return 0; + usleep_range(priv->timeout_min, + priv->timeout_max); + } while (time_before(jiffies, stop)); return -ETIME; } @@ -1032,8 +1054,40 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (rc < 0) goto out_err; - intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | - TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; + /* Figure out the capabilities */ + rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps); + if (rc < 0) + goto out_err; + + dev_dbg(dev, "TPM interface capabilities (0x%x):\n", + intfcaps); + if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) + dev_dbg(dev, "\tBurst Count Static\n"); + if (intfcaps & TPM_INTF_CMD_READY_INT) { + intmask |= TPM_INTF_CMD_READY_INT; + dev_dbg(dev, "\tCommand Ready Int Support\n"); + } + if (intfcaps & TPM_INTF_INT_EDGE_FALLING) + dev_dbg(dev, "\tInterrupt Edge Falling\n"); + if (intfcaps & TPM_INTF_INT_EDGE_RISING) + dev_dbg(dev, "\tInterrupt Edge Rising\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_LOW) + dev_dbg(dev, "\tInterrupt Level Low\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) + dev_dbg(dev, "\tInterrupt Level High\n"); + if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) { + intmask |= TPM_INTF_LOCALITY_CHANGE_INT; + dev_dbg(dev, "\tLocality Change Int Support\n"); + } + if (intfcaps & TPM_INTF_STS_VALID_INT) { + intmask |= TPM_INTF_STS_VALID_INT; + dev_dbg(dev, "\tSts Valid Int Support\n"); + } + if (intfcaps & TPM_INTF_DATA_AVAIL_INT) { + intmask |= TPM_INTF_DATA_AVAIL_INT; + dev_dbg(dev, "\tData Avail Int Support\n"); + } + intmask &= ~TPM_GLOBAL_INT_ENABLE; rc = tpm_tis_request_locality(chip, 0); @@ -1067,32 +1121,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, goto out_err; } - /* Figure out the capabilities */ - rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps); - if (rc < 0) - goto out_err; - - dev_dbg(dev, "TPM interface capabilities (0x%x):\n", - intfcaps); - if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) - dev_dbg(dev, "\tBurst Count Static\n"); - if (intfcaps & TPM_INTF_CMD_READY_INT) - dev_dbg(dev, "\tCommand Ready Int Support\n"); - if (intfcaps & TPM_INTF_INT_EDGE_FALLING) - dev_dbg(dev, "\tInterrupt Edge Falling\n"); - if (intfcaps & TPM_INTF_INT_EDGE_RISING) - dev_dbg(dev, "\tInterrupt Edge Rising\n"); - if (intfcaps & TPM_INTF_INT_LEVEL_LOW) - dev_dbg(dev, "\tInterrupt Level Low\n"); - if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) - dev_dbg(dev, "\tInterrupt Level High\n"); - if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) - dev_dbg(dev, "\tLocality Change Int Support\n"); - if (intfcaps & TPM_INTF_STS_VALID_INT) - dev_dbg(dev, "\tSts Valid Int Support\n"); - if (intfcaps & TPM_INTF_DATA_AVAIL_INT) - dev_dbg(dev, "\tData Avail Int Support\n"); - /* INTERRUPT Setup */ init_waitqueue_head(&priv->read_queue); init_waitqueue_head(&priv->int_queue); @@ -1123,7 +1151,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, else tpm_tis_probe_irq(chip, intmask); - if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { + if (chip->flags & TPM_CHIP_FLAG_IRQ) { + priv->int_mask = intmask; + } else { dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); @@ -1170,13 +1200,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) if (rc < 0) goto out; - rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); - if (rc < 0) - goto out; - - intmask |= TPM_INTF_CMD_READY_INT - | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT - | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; + intmask = priv->int_mask | TPM_GLOBAL_INT_ENABLE; tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 4a58b870b418..e978f457fd4d 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -96,6 +96,7 @@ struct tpm_tis_data { unsigned int locality_count; int locality; int irq; + unsigned int int_mask; unsigned long flags; void __iomem *ilb_base_addr; u16 clkrun_enabled; -- cgit 1.4.1 From 9953dbf65f92b7133c74c3ea768c0a70248818a9 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 26 Apr 2023 20:29:27 +0300 Subject: tpm_tis: Use tpm_chip_{start,stop} decoration inside tpm_tis_resume [ Upstream commit 1398aa803f198b7a386fdd8404666043e95f4c16 ] Before sending a TPM command, CLKRUN protocol must be disabled. This is not done in the case of tpm1_do_selftest() call site inside tpm_tis_resume(). Address this by decorating the calls with tpm_chip_{start,stop}, which should be always used to arm and disarm the TPM chip for transmission. Finally, move the call to the main TPM driver callback as the last step because it should arm the chip by itself, if it needs that type of functionality. Cc: stable@vger.kernel.org Reported-by: Jason A. Donenfeld Closes: https://lore.kernel.org/linux-integrity/CS68AWILHXS4.3M36M1EKZLUMS@suppilovahvero/ Fixes: a3fbfae82b4c ("tpm: take TPM chip power gating out of tpm_transmit()") Reviewed-by: Jerry Snitselaar Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- drivers/char/tpm/tpm_tis_core.c | 43 ++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index a35c117ee7c8..a5c22fb4ad42 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1190,25 +1190,20 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) u32 intmask; int rc; - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, true); - - /* reenable interrupts that device may have lost or - * BIOS/firmware may have disabled + /* + * Re-enable interrupts that device may have lost or BIOS/firmware may + * have disabled. */ rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq); - if (rc < 0) - goto out; + if (rc < 0) { + dev_err(&chip->dev, "Setting IRQ failed.\n"); + return; + } intmask = priv->int_mask | TPM_GLOBAL_INT_ENABLE; - - tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); - -out: - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, false); - - return; + rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); + if (rc < 0) + dev_err(&chip->dev, "Enabling interrupts failed.\n"); } int tpm_tis_resume(struct device *dev) @@ -1216,27 +1211,27 @@ int tpm_tis_resume(struct device *dev) struct tpm_chip *chip = dev_get_drvdata(dev); int ret; - ret = tpm_tis_request_locality(chip, 0); - if (ret < 0) + ret = tpm_chip_start(chip); + if (ret) return ret; if (chip->flags & TPM_CHIP_FLAG_IRQ) tpm_tis_reenable_interrupts(chip); - ret = tpm_pm_resume(dev); - if (ret) - goto out; - /* * TPM 1.2 requires self-test on resume. This function actually returns * an error code but for unknown reason it isn't handled. */ if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) tpm1_do_selftest(chip); -out: - tpm_tis_relinquish_locality(chip, 0); - return ret; + tpm_chip_stop(chip); + + ret = tpm_pm_resume(dev); + if (ret) + return ret; + + return 0; } EXPORT_SYMBOL_GPL(tpm_tis_resume); #endif -- cgit 1.4.1 From e76f61a2c523b17ce7f45c2dc9473278f35a7d67 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:37 +0100 Subject: tpm, tpm_tis: startup chip before testing for interrupts [ Upstream commit 548eb516ec0f7a484a23a902835899341164b8ea ] In tpm_tis_gen_interrupt() a request for a property value is sent to the TPM to test if interrupts are generated. However after a power cycle the TPM responds with TPM_RC_INITIALIZE which indicates that the TPM is not yet properly initialized. Fix this by first starting the TPM up before the request is sent. For this the startup implementation is removed from tpm_chip_register() and put into the new function tpm_chip_startup() which is called before the interrupts are tested. Signed-off-by: Lino Sanfilippo Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Stable-dep-of: 99d464506255 ("tpm: Prevent hwrng from activating during resume") Signed-off-by: Sasha Levin --- drivers/char/tpm/tpm-chip.c | 38 ++++++++++++++++++++++++-------------- drivers/char/tpm/tpm.h | 1 + drivers/char/tpm/tpm_tis_core.c | 5 +++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 5165f6d3da22..47c2861af45a 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -601,6 +601,30 @@ static int tpm_get_pcr_allocation(struct tpm_chip *chip) return rc; } +/* + * tpm_chip_startup() - performs auto startup and allocates the PCRs + * @chip: TPM chip to use. + */ +int tpm_chip_startup(struct tpm_chip *chip) +{ + int rc; + + rc = tpm_chip_start(chip); + if (rc) + return rc; + + rc = tpm_auto_startup(chip); + if (rc) + goto stop; + + rc = tpm_get_pcr_allocation(chip); +stop: + tpm_chip_stop(chip); + + return rc; +} +EXPORT_SYMBOL_GPL(tpm_chip_startup); + /* * tpm_chip_register() - create a character device for the TPM chip * @chip: TPM chip to use. @@ -616,20 +640,6 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; - rc = tpm_chip_start(chip); - if (rc) - return rc; - rc = tpm_auto_startup(chip); - if (rc) { - tpm_chip_stop(chip); - return rc; - } - - rc = tpm_get_pcr_allocation(chip); - tpm_chip_stop(chip); - if (rc) - return rc; - tpm_sysfs_add_device(chip); tpm_bios_log_setup(chip); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 830014a26609..88d3bd76e076 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -263,6 +263,7 @@ static inline void tpm_msleep(unsigned int delay_msec) delay_msec * 1000); }; +int tpm_chip_startup(struct tpm_chip *chip); int tpm_chip_start(struct tpm_chip *chip); void tpm_chip_stop(struct tpm_chip *chip); struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index a5c22fb4ad42..9f76c9a5aa42 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1124,6 +1124,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, /* INTERRUPT Setup */ init_waitqueue_head(&priv->read_queue); init_waitqueue_head(&priv->int_queue); + + rc = tpm_chip_startup(chip); + if (rc) + goto out_err; + if (irq != -1) { /* * Before doing irq testing issue a command to the TPM in polling mode -- cgit 1.4.1 From 25d38d5eaa1f7a4d8075d23633f1722b38c8c7a7 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 26 Apr 2023 21:49:37 +0300 Subject: tpm: Re-enable TPM chip boostrapping non-tpm_tis TPM drivers [ Upstream commit 0c8862de05c1a087795ee0a87bf61a6394306cc0 ] TPM chip bootstrapping was removed from tpm_chip_register(), and it was relocated to tpm_tis_core. This breaks all drivers which are not based on tpm_tis because the chip will not get properly initialized. Take the corrective steps: 1. Rename tpm_chip_startup() as tpm_chip_bootstrap() and make it one-shot. 2. Call tpm_chip_bootstrap() in tpm_chip_register(), which reverts the things as tehy used to be. Cc: Lino Sanfilippo Fixes: 548eb516ec0f ("tpm, tpm_tis: startup chip before testing for interrupts") Reported-by: Pengfei Xu Link: https://lore.kernel.org/all/ZEjqhwHWBnxcaRV5@xpf.sh.intel.com/ Tested-by: Pengfei Xu Signed-off-by: Jarkko Sakkinen Stable-dep-of: 99d464506255 ("tpm: Prevent hwrng from activating during resume") Signed-off-by: Sasha Levin --- drivers/char/tpm/tpm-chip.c | 22 +++++++++++++++++++--- drivers/char/tpm/tpm.h | 2 +- drivers/char/tpm/tpm_tis_core.c | 2 +- include/linux/tpm.h | 13 +++++++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 47c2861af45a..31d807482152 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -602,13 +602,19 @@ static int tpm_get_pcr_allocation(struct tpm_chip *chip) } /* - * tpm_chip_startup() - performs auto startup and allocates the PCRs + * tpm_chip_bootstrap() - Boostrap TPM chip after power on * @chip: TPM chip to use. + * + * Initialize TPM chip after power on. This a one-shot function: subsequent + * calls will have no effect. */ -int tpm_chip_startup(struct tpm_chip *chip) +int tpm_chip_bootstrap(struct tpm_chip *chip) { int rc; + if (chip->flags & TPM_CHIP_FLAG_BOOTSTRAPPED) + return 0; + rc = tpm_chip_start(chip); if (rc) return rc; @@ -621,9 +627,15 @@ int tpm_chip_startup(struct tpm_chip *chip) stop: tpm_chip_stop(chip); + /* + * Unconditionally set, as driver initialization should cease, when the + * boostrapping process fails. + */ + chip->flags |= TPM_CHIP_FLAG_BOOTSTRAPPED; + return rc; } -EXPORT_SYMBOL_GPL(tpm_chip_startup); +EXPORT_SYMBOL_GPL(tpm_chip_bootstrap); /* * tpm_chip_register() - create a character device for the TPM chip @@ -640,6 +652,10 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; + rc = tpm_chip_bootstrap(chip); + if (rc) + return rc; + tpm_sysfs_add_device(chip); tpm_bios_log_setup(chip); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 88d3bd76e076..f6c99b3f0045 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -263,7 +263,7 @@ static inline void tpm_msleep(unsigned int delay_msec) delay_msec * 1000); }; -int tpm_chip_startup(struct tpm_chip *chip); +int tpm_chip_bootstrap(struct tpm_chip *chip); int tpm_chip_start(struct tpm_chip *chip); void tpm_chip_stop(struct tpm_chip *chip); struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 9f76c9a5aa42..f02b583005a5 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1125,7 +1125,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, init_waitqueue_head(&priv->read_queue); init_waitqueue_head(&priv->int_queue); - rc = tpm_chip_startup(chip); + rc = tpm_chip_bootstrap(chip); if (rc) goto out_err; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index dfeb25a0362d..cea64d58ef9f 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -273,13 +273,14 @@ enum tpm2_cc_attrs { #define TPM_VID_ATML 0x1114 enum tpm_chip_flags { - TPM_CHIP_FLAG_TPM2 = BIT(1), - TPM_CHIP_FLAG_IRQ = BIT(2), - TPM_CHIP_FLAG_VIRTUAL = BIT(3), - TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), - TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), + TPM_CHIP_FLAG_BOOTSTRAPPED = BIT(0), + TPM_CHIP_FLAG_TPM2 = BIT(1), + TPM_CHIP_FLAG_IRQ = BIT(2), + TPM_CHIP_FLAG_VIRTUAL = BIT(3), + TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), + TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED = BIT(6), - TPM_CHIP_FLAG_FIRMWARE_UPGRADE = BIT(7), + TPM_CHIP_FLAG_FIRMWARE_UPGRADE = BIT(7), }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) -- cgit 1.4.1 From 7cd46930b8bf37b84777410ac21bfdf1179021d5 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 26 Apr 2023 20:29:28 +0300 Subject: tpm: Prevent hwrng from activating during resume [ Upstream commit 99d46450625590d410f86fe4660a5eff7d3b8343 ] Set TPM_CHIP_FLAG_SUSPENDED in tpm_pm_suspend() and reset in tpm_pm_resume(). While the flag is set, tpm_hwrng() gives back zero bytes. This prevents hwrng from racing during resume. Cc: stable@vger.kernel.org Fixes: 6e592a065d51 ("tpm: Move Linux RNG connection to hwrng") Reviewed-by: Jerry Snitselaar Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- drivers/char/tpm/tpm-chip.c | 4 ++++ drivers/char/tpm/tpm-interface.c | 10 ++++++++++ include/linux/tpm.h | 1 + 3 files changed, 15 insertions(+) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 31d807482152..95d847c9de79 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -568,6 +568,10 @@ static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) { struct tpm_chip *chip = container_of(rng, struct tpm_chip, hwrng); + /* Give back zero bytes, as TPM chip has not yet fully resumed: */ + if (chip->flags & TPM_CHIP_FLAG_SUSPENDED) + return 0; + return tpm_get_random(chip, data, max); } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 7e513b771832..0f941cb32eb1 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -412,6 +412,8 @@ int tpm_pm_suspend(struct device *dev) } suspended: + chip->flags |= TPM_CHIP_FLAG_SUSPENDED; + if (rc) dev_err(dev, "Ignoring error %d while suspending\n", rc); return 0; @@ -429,6 +431,14 @@ int tpm_pm_resume(struct device *dev) if (chip == NULL) return -ENODEV; + chip->flags &= ~TPM_CHIP_FLAG_SUSPENDED; + + /* + * Guarantee that SUSPENDED is written last, so that hwrng does not + * activate before the chip has been fully resumed. + */ + wmb(); + return 0; } EXPORT_SYMBOL_GPL(tpm_pm_resume); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index cea64d58ef9f..4e22e4f4cec8 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -281,6 +281,7 @@ enum tpm_chip_flags { TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED = BIT(6), TPM_CHIP_FLAG_FIRMWARE_UPGRADE = BIT(7), + TPM_CHIP_FLAG_SUSPENDED = BIT(8), }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) -- cgit 1.4.1 From ae9e65319f99b9c8211814341121a7189b6c10cc Mon Sep 17 00:00:00 2001 From: Gregory Oakes Date: Thu, 16 Mar 2023 15:13:12 -0500 Subject: watchdog: sp5100_tco: Immediately trigger upon starting. commit 4eda19cc8a29cde3580ed73bf11dc73b4e757697 upstream. The watchdog countdown is supposed to begin when the device file is opened. Instead, it would begin countdown upon the first write to or close of the device file. Now, the ping operation is called within the start operation which ensures the countdown begins. From experimenation, it does not appear possible to do this with a single write including both the start bit and the trigger bit. So, it is done as two distinct writes. Signed-off-by: Gregory Oakes Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230316201312.17538-1-gregory.oakes@amd.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck Cc: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/sp5100_tco.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index fb426b7d81da..14f8d8d90920 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -115,6 +115,10 @@ static int tco_timer_start(struct watchdog_device *wdd) val |= SP5100_WDT_START_STOP_BIT; writel(val, SP5100_WDT_CONTROL(tco->tcobase)); + /* This must be a distinct write. */ + val |= SP5100_WDT_TRIGGER_BIT; + writel(val, SP5100_WDT_CONTROL(tco->tcobase)); + return 0; } -- cgit 1.4.1 From a2fe4534bb38fc5c7c608dac179f205343611003 Mon Sep 17 00:00:00 2001 From: Jack Xiao Date: Tue, 29 Nov 2022 11:12:08 +0800 Subject: drm/amd/amdgpu: update mes11 api def commit 1e7bbdba68baf6af7500dd636f18b6fcce58e945 upstream. Update the api def of mes11. Signed-off-by: Jack Xiao Reviewed-by: Hawking Zhang Tested-and-acked-by: Evan Quan Signed-off-by: Alex Deucher Cc: "Gong, Richard" Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/include/mes_v11_api_def.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/include/mes_v11_api_def.h b/drivers/gpu/drm/amd/include/mes_v11_api_def.h index 7e85cdc5bd34..dc694cb246d9 100644 --- a/drivers/gpu/drm/amd/include/mes_v11_api_def.h +++ b/drivers/gpu/drm/amd/include/mes_v11_api_def.h @@ -222,7 +222,11 @@ union MESAPI_SET_HW_RESOURCES { uint32_t apply_grbm_remote_register_dummy_read_wa : 1; uint32_t second_gfx_pipe_enabled : 1; uint32_t enable_level_process_quantum_check : 1; - uint32_t reserved : 25; + uint32_t legacy_sch_mode : 1; + uint32_t disable_add_queue_wptr_mc_addr : 1; + uint32_t enable_mes_event_int_logging : 1; + uint32_t enable_reg_active_poll : 1; + uint32_t reserved : 21; }; uint32_t uint32_t_all; }; -- cgit 1.4.1 From ac2f5739fdca4f532036d1f2e63c2ab55c288f72 Mon Sep 17 00:00:00 2001 From: Jack Xiao Date: Tue, 29 Nov 2022 11:12:32 +0800 Subject: drm/amdgpu/mes11: enable reg active poll commit a6b3b618c0f7abc3f543dd0c57b2b19a770bffec upstream. Enable reg active poll in mes11. Signed-off-by: Jack Xiao Reviewed-by: Hawking Zhang Tested-and-acked-by: Evan Quan Signed-off-by: Alex Deucher Cc: "Gong, Richard" Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/mes_v11_0.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index 08766b678436..321a5ffebe88 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -390,6 +390,7 @@ static int mes_v11_0_set_hw_resources(struct amdgpu_mes *mes) mes_set_hw_res_pkt.disable_reset = 1; mes_set_hw_res_pkt.disable_mes_log = 1; mes_set_hw_res_pkt.use_different_vmid_compute = 1; + mes_set_hw_res_pkt.enable_reg_active_poll = 1; mes_set_hw_res_pkt.oversubscription_timer = 50; return mes_v11_0_submit_pkt_and_poll_completion(mes, -- cgit 1.4.1 From 0dbc898f5917c5a3bec6be19d9f5469cbc351a7d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 25 Oct 2022 15:39:35 -0700 Subject: skbuff: Proactively round up to kmalloc bucket size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 12d6c1d3a2ad0c199ec57c201cdc71e8e157a232 upstream. Instead of discovering the kmalloc bucket size _after_ allocation, round up proactively so the allocation is explicitly made for the full size, allowing the compiler to correctly reason about the resulting size of the buffer through the existing __alloc_size() hint. This will allow for kernels built with CONFIG_UBSAN_BOUNDS or the coming dynamic bounds checking under CONFIG_FORTIFY_SOURCE to gain back the __alloc_size() hints that were temporarily reverted in commit 93dd04ab0b2b ("slab: remove __alloc_size attribute from __kmalloc_track_caller") Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: netdev@vger.kernel.org Cc: Greg Kroah-Hartman Cc: Nick Desaulniers Cc: David Rientjes Acked-by: Vlastimil Babka Link: https://patchwork.kernel.org/project/netdevbpf/patch/20221021234713.you.031-kees@kernel.org/ Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221025223811.up.360-kees@kernel.org Signed-off-by: Paolo Abeni Cc: Daniel Díaz Signed-off-by: Greg Kroah-Hartman --- net/core/skbuff.c | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 47660002cada..e37830ef2a4b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -506,14 +506,14 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, */ size = SKB_DATA_ALIGN(size); size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc); + osize = kmalloc_size_roundup(size); + data = kmalloc_reserve(osize, gfp_mask, node, &pfmemalloc); if (unlikely(!data)) goto nodata; - /* kmalloc(size) might give us more room than requested. + /* kmalloc_size_roundup() might give us more room than requested. * Put skb_shared_info exactly at the end of allocated zone, * to allow max possible filling before reallocation. */ - osize = ksize(data); size = SKB_WITH_OVERHEAD(osize); prefetchw(data + size); @@ -1822,10 +1822,11 @@ EXPORT_SYMBOL(__pskb_copy_fclone); int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask) { - int i, osize = skb_end_offset(skb); - int size = osize + nhead + ntail; + unsigned int osize = skb_end_offset(skb); + unsigned int size = osize + nhead + ntail; long off; u8 *data; + int i; BUG_ON(nhead < 0); @@ -1833,15 +1834,16 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, skb_zcopy_downgrade_managed(skb); - size = SKB_DATA_ALIGN(size); - if (skb_pfmemalloc(skb)) gfp_mask |= __GFP_MEMALLOC; - data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask, NUMA_NO_NODE, NULL); + + size = SKB_DATA_ALIGN(size); + size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + size = kmalloc_size_roundup(size); + data = kmalloc_reserve(size, gfp_mask, NUMA_NO_NODE, NULL); if (!data) goto nodata; - size = SKB_WITH_OVERHEAD(ksize(data)); + size = SKB_WITH_OVERHEAD(size); /* Copy only real data... and, alas, header. This should be * optimized for the cases when header is void. @@ -6182,21 +6184,20 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, const int headlen, gfp_t gfp_mask) { int i; - int size = skb_end_offset(skb); + unsigned int size = skb_end_offset(skb); int new_hlen = headlen - off; u8 *data; - size = SKB_DATA_ALIGN(size); - if (skb_pfmemalloc(skb)) gfp_mask |= __GFP_MEMALLOC; - data = kmalloc_reserve(size + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask, NUMA_NO_NODE, NULL); + + size = SKB_DATA_ALIGN(size); + size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + size = kmalloc_size_roundup(size); + data = kmalloc_reserve(size, gfp_mask, NUMA_NO_NODE, NULL); if (!data) return -ENOMEM; - - size = SKB_WITH_OVERHEAD(ksize(data)); + size = SKB_WITH_OVERHEAD(size); /* Copy real data, and all frags */ skb_copy_from_linear_data_offset(skb, off, data, new_hlen); @@ -6301,22 +6302,21 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, int pos, gfp_t gfp_mask) { int i, k = 0; - int size = skb_end_offset(skb); + unsigned int size = skb_end_offset(skb); u8 *data; const int nfrags = skb_shinfo(skb)->nr_frags; struct skb_shared_info *shinfo; - size = SKB_DATA_ALIGN(size); - if (skb_pfmemalloc(skb)) gfp_mask |= __GFP_MEMALLOC; - data = kmalloc_reserve(size + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask, NUMA_NO_NODE, NULL); + + size = SKB_DATA_ALIGN(size); + size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + size = kmalloc_size_roundup(size); + data = kmalloc_reserve(size, gfp_mask, NUMA_NO_NODE, NULL); if (!data) return -ENOMEM; - - size = SKB_WITH_OVERHEAD(ksize(data)); + size = SKB_WITH_OVERHEAD(size); memcpy((struct skb_shared_info *)(data + size), skb_shinfo(skb), offsetof(struct skb_shared_info, frags[0])); -- cgit 1.4.1 From 66ede2e4235f842acd0dbeb428b7c9c790c7b265 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 23 Jan 2023 14:28:24 +0100 Subject: platform/x86: hp-wmi: Fix cast to smaller integer type warning commit ce95010ef62d4bf470928969bafc9070ae98cbb1 upstream. Fix the following compiler warning: drivers/platform/x86/hp/hp-wmi.c:551:24: warning: cast to smaller integer type 'enum hp_wmi_radio' from 'void *' [-Wvoid-pointer-to-enum-cast] Reported-by: kernel test robot Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230123132824.660062-1-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/hp/hp-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 94af7d398a1b..3bacee2b8d52 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -552,7 +552,7 @@ static int __init hp_wmi_enable_hotkeys(void) static int hp_wmi_set_block(void *data, bool blocked) { - enum hp_wmi_radio r = (enum hp_wmi_radio) data; + enum hp_wmi_radio r = (long)data; int query = BIT(r + 8) | ((!blocked) << r); int ret; -- cgit 1.4.1 From 7bfd4c0ebcb4fb30cc84fe4bb3f43f3ded0b663c Mon Sep 17 00:00:00 2001 From: Steffen Bätz Date: Fri, 28 Oct 2022 13:31:58 -0300 Subject: net: dsa: mv88e6xxx: Add RGMII delay to 88E6320 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 91e87045a5ef6f7003e9a2cb7dfa435b9b002dbe upstream. Currently, the .port_set_rgmii_delay hook is missing for the 88E6320 family, which causes failure to retrieve an IP address via DHCP. Add mv88e6320_port_set_rgmii_delay() that allows applying the RGMII delay for ports 2, 5, and 6, which are the only ports that can be used in RGMII mode. Tested on a custom i.MX8MN board connected to an 88E6320 switch. This change also applies safely to the 88E6321 variant. The only difference between 88E6320 versus 88E6321 is the temperature grade and pinout. They share exactly the same MDIO register map for ports 2, 5, and 6, which are the only ports that can be used in RGMII mode. Signed-off-by: Steffen Bätz [fabio: Improved commit log and extended it to mv88e6321_ops] Signed-off-by: Fabio Estevam Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20221028163158.198108-1-festevam@gmail.com Signed-off-by: Jakub Kicinski Cc: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/net/dsa/mv88e6xxx/chip.c | 2 ++ drivers/net/dsa/mv88e6xxx/port.c | 9 +++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 12175195d396..a8ff6ee6d58e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -5044,6 +5044,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_sync_link = mv88e6xxx_port_sync_link, + .port_set_rgmii_delay = mv88e6320_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -5088,6 +5089,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_sync_link = mv88e6xxx_port_sync_link, + .port_set_rgmii_delay = mv88e6320_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 5c4195c635b0..f79cf716c541 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -133,6 +133,15 @@ int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); } +int mv88e6320_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port != 2 && port != 5 && port != 6) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) { u16 reg; diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index a91e22d9a6cb..d19b6303b91f 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -332,6 +332,8 @@ int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, int pause); +int mv88e6320_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, -- cgit 1.4.1 From 28ee628fff1e72ad9629168250071a05b7ba1043 Mon Sep 17 00:00:00 2001 From: Robin Chen Date: Fri, 17 Feb 2023 20:47:57 +0800 Subject: drm/amd/display: hpd rx irq not working with eDP interface commit eeefe7c4820b6baa0462a8b723ea0a3b5846ccae upstream. [Why] This is the fix for the defect of commit ab144f0b4ad6 ("drm/amd/display: Allow individual control of eDP hotplug support"). [How] To revise the default eDP hotplug setting and use the enum to git rid of the magic number for different options. Fixes: ab144f0b4ad6 ("drm/amd/display: Allow individual control of eDP hotplug support") Cc: stable@vger.kernel.org Cc: Mario Limonciello Reviewed-by: Wenjing Liu Acked-by: Qingqing Zhuo Signed-off-by: Robin Chen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit eeefe7c4820b6baa0462a8b723ea0a3b5846ccae) Hand modified for missing file rename changes and symbol moves in 6.1.y. Signed-off-by: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/core/dc_link.c | 9 +++++++-- drivers/gpu/drm/amd/display/dc/dc_types.h | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index 6299130663a3..5d53e54ebe90 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -1634,14 +1634,18 @@ static bool dc_link_construct_legacy(struct dc_link *link, link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; switch (link->dc->config.allow_edp_hotplug_detection) { - case 1: // only the 1st eDP handles hotplug + case HPD_EN_FOR_ALL_EDP: + link->irq_source_hpd_rx = + dal_irq_get_rx_source(link->hpd_gpio); + break; + case HPD_EN_FOR_PRIMARY_EDP_ONLY: if (link->link_index == 0) link->irq_source_hpd_rx = dal_irq_get_rx_source(link->hpd_gpio); else link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; break; - case 2: // only the 2nd eDP handles hotplug + case HPD_EN_FOR_SECONDARY_EDP_ONLY: if (link->link_index == 1) link->irq_source_hpd_rx = dal_irq_get_rx_source(link->hpd_gpio); @@ -1649,6 +1653,7 @@ static bool dc_link_construct_legacy(struct dc_link *link, link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; break; default: + link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; break; } } diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index ad9041472cca..6050a3469a57 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -993,4 +993,10 @@ struct display_endpoint_id { enum display_endpoint_type ep_type; }; +enum dc_hpd_enable_select { + HPD_EN_FOR_ALL_EDP = 0, + HPD_EN_FOR_PRIMARY_EDP_ONLY, + HPD_EN_FOR_SECONDARY_EDP_ONLY, +}; + #endif /* DC_TYPES_H_ */ -- cgit 1.4.1 From 8bdf47f9dbeac6b247c8f14fbaebfe020d9a2ffa Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Tue, 14 Mar 2023 09:17:16 +0100 Subject: ocfs2: Switch to security_inode_init_security() commit de3004c874e740304cc4f4a83d6200acb511bbda upstream. In preparation for removing security_old_inode_init_security(), switch to security_inode_init_security(). Extend the existing ocfs2_initxattrs() to take the ocfs2_security_xattr_info structure from fs_info, and populate the name/value/len triple with the first xattr provided by LSMs. As fs_info was not used before, ocfs2_initxattrs() can now handle the case of replicating the behavior of security_old_inode_init_security(), i.e. just obtaining the xattr, in addition to setting all xattrs provided by LSMs. Supporting multiple xattrs is not currently supported where security_old_inode_init_security() was called (mknod, symlink), as it requires non-trivial changes that can be done at a later time. Like for reiserfs, even if EVM is invoked, it will not provide an xattr (if it is not the first to set it, its xattr will be discarded; if it is the first, it does not have xattrs to calculate the HMAC on). Finally, since security_inode_init_security(), unlike security_old_inode_init_security(), returns zero instead of -EOPNOTSUPP if no xattrs were provided by LSMs or if inodes are private, additionally check in ocfs2_init_security_get() if the xattr name is set. If not, act as if security_old_inode_init_security() returned -EOPNOTSUPP, and set si->enable to zero to notify to the functions following ocfs2_init_security_get() that no xattrs are available. Signed-off-by: Roberto Sassu Reviewed-by: Casey Schaufler Acked-by: Joseph Qi Reviewed-by: Mimi Zohar Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/namei.c | 2 ++ fs/ocfs2/xattr.c | 30 ++++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 05f32989bad6..1c7ac433667d 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -242,6 +242,7 @@ static int ocfs2_mknod(struct user_namespace *mnt_userns, int want_meta = 0; int xattr_credits = 0; struct ocfs2_security_xattr_info si = { + .name = NULL, .enable = 1, }; int did_quota_inode = 0; @@ -1805,6 +1806,7 @@ static int ocfs2_symlink(struct user_namespace *mnt_userns, int want_clusters = 0; int xattr_credits = 0; struct ocfs2_security_xattr_info si = { + .name = NULL, .enable = 1, }; int did_quota = 0, did_quota_inode = 0; diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 95d0611c5fc7..55699c573541 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -7259,9 +7259,21 @@ static int ocfs2_xattr_security_set(const struct xattr_handler *handler, static int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array, void *fs_info) { + struct ocfs2_security_xattr_info *si = fs_info; const struct xattr *xattr; int err = 0; + if (si) { + si->value = kmemdup(xattr_array->value, xattr_array->value_len, + GFP_KERNEL); + if (!si->value) + return -ENOMEM; + + si->name = xattr_array->name; + si->value_len = xattr_array->value_len; + return 0; + } + for (xattr = xattr_array; xattr->name != NULL; xattr++) { err = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY, xattr->name, xattr->value, @@ -7277,13 +7289,23 @@ int ocfs2_init_security_get(struct inode *inode, const struct qstr *qstr, struct ocfs2_security_xattr_info *si) { + int ret; + /* check whether ocfs2 support feature xattr */ if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb))) return -EOPNOTSUPP; - if (si) - return security_old_inode_init_security(inode, dir, qstr, - &si->name, &si->value, - &si->value_len); + if (si) { + ret = security_inode_init_security(inode, dir, qstr, + &ocfs2_initxattrs, si); + /* + * security_inode_init_security() does not return -EOPNOTSUPP, + * we have to check the xattr ourselves. + */ + if (!ret && !si->name) + si->enable = 0; + + return ret; + } return security_inode_init_security(inode, dir, qstr, &ocfs2_initxattrs, NULL); -- cgit 1.4.1 From 4eb600f386efdea970f5324f1502f171011ce3b1 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Thu, 20 Apr 2023 14:09:45 -0700 Subject: arm64: Also reset KASAN tag if page is not PG_mte_tagged commit 2efbafb91e12ff5a16cbafb0085e4c10c3fca493 upstream. Consider the following sequence of events: 1) A page in a PROT_READ|PROT_WRITE VMA is faulted. 2) Page migration allocates a page with the KASAN allocator, causing it to receive a non-match-all tag, and uses it to replace the page faulted in 1. 3) The program uses mprotect() to enable PROT_MTE on the page faulted in 1. As a result of step 3, we are left with a non-match-all tag for a page with tags accessible to userspace, which can lead to the same kind of tag check faults that commit e74a68468062 ("arm64: Reset KASAN tag in copy_highpage with HW tags only") intended to fix. The general invariant that we have for pages in a VMA with VM_MTE_ALLOWED is that they cannot have a non-match-all tag. As a result of step 2, the invariant is broken. This means that the fix in the referenced commit was incomplete and we also need to reset the tag for pages without PG_mte_tagged. Fixes: e5b8d9218951 ("arm64: mte: reset the page tag in page->flags") Cc: # 5.15 Link: https://linux-review.googlesource.com/id/I7409cdd41acbcb215c2a7417c1e50d37b875beff Signed-off-by: Peter Collingbourne Reviewed-by: Catalin Marinas Link: https://lore.kernel.org/r/20230420210945.2313627-1-pcc@google.com Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/mm/copypage.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/copypage.c b/arch/arm64/mm/copypage.c index 6dbc822332f2..f2fa8a0776e2 100644 --- a/arch/arm64/mm/copypage.c +++ b/arch/arm64/mm/copypage.c @@ -21,9 +21,10 @@ void copy_highpage(struct page *to, struct page *from) copy_page(kto, kfrom); + if (kasan_hw_tags_enabled()) + page_kasan_tag_reset(to); + if (system_supports_mte() && page_mte_tagged(from)) { - if (kasan_hw_tags_enabled()) - page_kasan_tag_reset(to); mte_copy_page_tags(kto, kfrom); set_page_mte_tagged(to); } -- cgit 1.4.1 From c26b9e193172f48cd0ccc64285337106fb8aa804 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Tue, 16 May 2023 12:24:25 -0700 Subject: x86/mm: Avoid incomplete Global INVLPG flushes commit ce0b15d11ad837fbacc5356941712218e38a0a83 upstream. The INVLPG instruction is used to invalidate TLB entries for a specified virtual address. When PCIDs are enabled, INVLPG is supposed to invalidate TLB entries for the specified address for both the current PCID *and* Global entries. (Note: Only kernel mappings set Global=1.) Unfortunately, some INVLPG implementations can leave Global translations unflushed when PCIDs are enabled. As a workaround, never enable PCIDs on affected processors. I expect there to eventually be microcode mitigations to replace this software workaround. However, the exact version numbers where that will happen are not known today. Once the version numbers are set in stone, the processor list can be tweaked to only disable PCIDs on affected processors with affected microcode. Note: if anyone wants a quick fix that doesn't require patching, just stick 'nopcid' on your kernel command-line. Signed-off-by: Dave Hansen Reviewed-by: Thomas Gleixner Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/x86/mm/init.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 9121bc1b9453..0d5ccea2538f 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -260,6 +261,24 @@ static void __init probe_page_size_mask(void) } } +#define INTEL_MATCH(_model) { .vendor = X86_VENDOR_INTEL, \ + .family = 6, \ + .model = _model, \ + } +/* + * INVLPG may not properly flush Global entries + * on these CPUs when PCIDs are enabled. + */ +static const struct x86_cpu_id invlpg_miss_ids[] = { + INTEL_MATCH(INTEL_FAM6_ALDERLAKE ), + INTEL_MATCH(INTEL_FAM6_ALDERLAKE_L ), + INTEL_MATCH(INTEL_FAM6_ALDERLAKE_N ), + INTEL_MATCH(INTEL_FAM6_RAPTORLAKE ), + INTEL_MATCH(INTEL_FAM6_RAPTORLAKE_P), + INTEL_MATCH(INTEL_FAM6_RAPTORLAKE_S), + {} +}; + static void setup_pcid(void) { if (!IS_ENABLED(CONFIG_X86_64)) @@ -268,6 +287,12 @@ static void setup_pcid(void) if (!boot_cpu_has(X86_FEATURE_PCID)) return; + if (x86_match_cpu(invlpg_miss_ids)) { + pr_info("Incomplete global flushes, disabling PCID"); + setup_clear_cpu_cap(X86_FEATURE_PCID); + return; + } + if (boot_cpu_has(X86_FEATURE_PGE)) { /* * This can't be cr4_set_bits_and_update_boot() -- the -- cgit 1.4.1 From 688c9af6e5fc526d79d3a7d167339d9f5808c524 Mon Sep 17 00:00:00 2001 From: David Arcari Date: Tue, 23 May 2023 06:54:00 -0400 Subject: platform/x86/intel/ifs: Annotate work queue on stack so object debug does not complain commit 3279decb2c3c8d58cb0b70ed5235c480735a36ee upstream. Object Debug results in the following warning while attempting to load ifs firmware: [ 220.007422] ODEBUG: object 000000003bf952db is on stack 00000000e843994b, but NOT annotated. [ 220.007459] ------------[ cut here ]------------ [ 220.007461] WARNING: CPU: 0 PID: 11774 at lib/debugobjects.c:548 __debug_object_init.cold+0x22e/0x2d5 [ 220.137476] RIP: 0010:__debug_object_init.cold+0x22e/0x2d5 [ 220.254774] Call Trace: [ 220.257641] [ 220.265606] scan_chunks_sanity_check+0x368/0x5f0 [intel_ifs] [ 220.288292] ifs_load_firmware+0x2a3/0x400 [intel_ifs] [ 220.332793] current_batch_store+0xea/0x160 [intel_ifs] [ 220.357947] kernfs_fop_write_iter+0x355/0x530 [ 220.363048] new_sync_write+0x28e/0x4a0 [ 220.381226] vfs_write+0x62a/0x920 [ 220.385160] ksys_write+0xf9/0x1d0 [ 220.399421] do_syscall_64+0x59/0x90 [ 220.440635] entry_SYSCALL_64_after_hwframe+0x63/0xcd [ 220.566845] ---[ end trace 3a01b299db142b41 ]--- Correct this by calling INIT_WORK_ONSTACK instead of INIT_WORK. Fixes: 684ec215706d ("platform/x86/intel/ifs: Authenticate and copy to secured memory") Signed-off-by: David Arcari Cc: Jithu Joseph Cc: Ashok Raj Cc: Tony Luck Cc: Hans de Goede Cc: Mark Gross Cc: Greg Kroah-Hartman Cc: Thomas Gleixner Cc: Dan Williams Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230523105400.674152-1-darcari@redhat.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/ifs/load.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index d056617ddc85..3e52b4eb1450 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -154,7 +154,7 @@ static int scan_chunks_sanity_check(struct device *dev) continue; reinit_completion(&ifs_done); local_work.dev = dev; - INIT_WORK(&local_work.w, copy_hashes_authenticate_chunks); + INIT_WORK_ONSTACK(&local_work.w, copy_hashes_authenticate_chunks); schedule_work_on(cpu, &local_work.w); wait_for_completion(&ifs_done); if (ifsd->loading_error) -- cgit 1.4.1 From 5222e81afa2688e79acefcf7e8af1d221f2c201b Mon Sep 17 00:00:00 2001 From: Adam Stylinski Date: Sun, 21 May 2023 10:52:23 -0400 Subject: ALSA: hda/ca0132: add quirk for EVGA X299 DARK commit 7843380d07bbeffd3ce6504e73cf61f840ae76ca upstream. This quirk is necessary for surround and other DSP effects to work with the onboard ca0132 based audio chipset for the EVGA X299 dark mainboard. Signed-off-by: Adam Stylinski Cc: Link: https://bugzilla.kernel.org/show_bug.cgi?id=67071 Link: https://lore.kernel.org/r/ZGopOe19T1QOwizS@eggsbenedict.adamsnet Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_ca0132.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 099722ebaed8..748a3c40966e 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1306,6 +1306,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI), SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI), + SND_PCI_QUIRK(0x3842, 0x104b, "EVGA X299 Dark", QUIRK_R3DI), SND_PCI_QUIRK(0x3842, 0x1055, "EVGA Z390 DARK", QUIRK_R3DI), SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D), -- cgit 1.4.1 From 7d3d306f159e721f48d28c45a18538734ddfca7b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 May 2023 13:35:20 +0200 Subject: ALSA: hda: Fix unhandled register update during auto-suspend period MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 81302b1c7c997e8a56c1c2fc63a296ebeb0cd2d0 upstream. It's reported that the recording started right after the driver probe doesn't work properly, and it turned out that this is related with the codec auto-suspend. Namely, after the probe phase, the usage count goes zero, and the auto-suspend is programmed, but the codec is kept still active until the auto-suspend expiration. When an application (e.g. alsactl) updates the mixer values at this moment, the values are cached but not actually written. Then, starting arecord thereafter also results in the silence because of the missing unmute. The root cause is the handling of "lazy update" mode; when a mixer value is updated *after* the suspend, it should update only the cache and exits. At the resume, the cached value is written to the device, in turn. The problem is that the current code misinterprets the state of auto-suspend as if it were already suspended. Although we can add the check of the actual device state after pm_runtime_get_if_in_use() for catching the missing state, this won't suffice; the second call of regmap_update_bits_check() will skip writing the register because the cache has been already updated by the first call. So we'd need fixes in two different places. OTOH, a simpler fix is to replace pm_runtime_get_if_in_use() with pm_runtime_get_if_active() (with ign_usage_count=true). This change implies that the driver takes the pm refcount if the device is still in ACTIVE state and continues the processing. A small caveat is that this will leave the auto-suspend timer. But, since the timer callback itself checks the device state and aborts gracefully when it's active, this won't be any substantial problem. Long story short: we address the missing register-write problem just by replacing the pm_runtime_*() call in snd_hda_keep_power_up(). Fixes: fc4f000bf8c0 ("ALSA: hda - Fix unexpected resume through regmap code path") Reported-by: Amadeusz Sławiński Closes: https://lore.kernel.org/r/a7478636-af11-92ab-731c-9b13c582a70d@linux.intel.com Suggested-by: Cezary Rojewski Cc: Link: https://lore.kernel.org/r/20230518113520.15213-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/hdac_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index b7e5032b61c9..bfd858577676 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -611,7 +611,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); int snd_hdac_keep_power_up(struct hdac_device *codec) { if (!atomic_inc_not_zero(&codec->in_pm)) { - int ret = pm_runtime_get_if_in_use(&codec->dev); + int ret = pm_runtime_get_if_active(&codec->dev, true); if (!ret) return -1; if (ret < 0) -- cgit 1.4.1 From 40599969ff5859e00d30308da6980600bd64a813 Mon Sep 17 00:00:00 2001 From: Bin Li Date: Wed, 24 May 2023 19:37:55 +0800 Subject: ALSA: hda/realtek: Enable headset onLenovo M70/M90 commit 4ca110cab46561cd74a2acd9b447435acb4bec5f upstream. Lenovo M70/M90 Gen4 are equipped with ALC897, and they need ALC897_FIXUP_HEADSET_MIC_PIN quirk to make its headset mic work. The previous quirk for M70/M90 is for Gen3. Signed-off-by: Bin Li Cc: Link: https://lore.kernel.org/r/20230524113755.1346928-1-bin.li@canonical.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c75760717736..379f216158ab 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11699,6 +11699,8 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), -- cgit 1.4.1 From 0d97634ad49888f1cea7362191cd9e8ee3ddde0c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 10 May 2023 12:28:00 -0400 Subject: SUNRPC: Don't change task->tk_status after the call to rpc_exit_task commit d180891fba995bd54e25b089b1ec98d134873586 upstream. Some calls to rpc_exit_task() may deliberately change the value of task->tk_status, for instance because it gets checked by the RPC call's rpc_release() callback. That makes it wrong to reset the value to task->tk_rpc_status. In particular this causes a bug where the rpc_call_done() callback tries to fail over a set of pNFS/flexfiles writes to a different IP address, but the reset of task->tk_status causes nfs_commit_release_pages() to immediately mark the file as having a fatal error. Fixes: 39494194f93b ("SUNRPC: Fix races with rpc_killall_tasks()") Cc: stable@vger.kernel.org # 6.1.x Signed-off-by: Trond Myklebust Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/sched.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index c8321de341ee..6debf4fd42d4 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -927,11 +927,10 @@ static void __rpc_execute(struct rpc_task *task) */ do_action = task->tk_action; /* Tasks with an RPC error status should exit */ - if (do_action != rpc_exit_task && + if (do_action && do_action != rpc_exit_task && (status = READ_ONCE(task->tk_rpc_status)) != 0) { task->tk_status = status; - if (do_action != NULL) - do_action = rpc_exit_task; + do_action = rpc_exit_task; } /* Callbacks override all actions */ if (task->tk_callback) { -- cgit 1.4.1 From a24aec210aa53e21fc7d2f6a2843882e2d0b17a9 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Thu, 4 May 2023 19:22:22 +0800 Subject: mmc: sdhci-esdhc-imx: make "no-mmc-hs400" works commit 81dce1490e28439c3cd8a8650b862a712f3061ba upstream. After commit 1ed5c3b22fc7 ("mmc: sdhci-esdhc-imx: Propagate ESDHC_FLAG_HS400* only on 8bit bus"), the property "no-mmc-hs400" from device tree file do not work any more. This patch reorder the code, which can avoid the warning message "drop HS400 support since no 8-bit bus" and also make the property "no-mmc-hs400" from dts file works. Fixes: 1ed5c3b22fc7 ("mmc: sdhci-esdhc-imx: Propagate ESDHC_FLAG_HS400* only on 8bit bus") Signed-off-by: Haibo Chen Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230504112222.3599602-1-haibo.chen@nxp.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 8c62c3fba75e..b63cf1f9e8fb 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1585,6 +1585,10 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (ret) return ret; + /* HS400/HS400ES require 8 bit bus */ + if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA)) + host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); + if (mmc_gpio_get_cd(host->mmc) >= 0) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; @@ -1669,10 +1673,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->mmc_host_ops.execute_tuning = usdhc_execute_tuning; } - err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data); - if (err) - goto disable_ahb_clk; - if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) sdhci_esdhc_ops.platform_execute_tuning = esdhc_executing_tuning; @@ -1680,15 +1680,13 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536) host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; - if (host->mmc->caps & MMC_CAP_8_BIT_DATA && - imx_data->socdata->flags & ESDHC_FLAG_HS400) + if (imx_data->socdata->flags & ESDHC_FLAG_HS400) host->mmc->caps2 |= MMC_CAP2_HS400; if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23) host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN; - if (host->mmc->caps & MMC_CAP_8_BIT_DATA && - imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) { + if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) { host->mmc->caps2 |= MMC_CAP2_HS400_ES; host->mmc_host_ops.hs400_enhanced_strobe = esdhc_hs400_enhanced_strobe; @@ -1710,6 +1708,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto disable_ahb_clk; } + err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data); + if (err) + goto disable_ahb_clk; + sdhci_esdhc_imx_hwinit(host); err = sdhci_add_host(host); -- cgit 1.4.1 From 7d5e0150eeec9c9be5073419be11a36251cfa19a Mon Sep 17 00:00:00 2001 From: Christian Loehle Date: Wed, 26 Apr 2023 16:59:39 +0000 Subject: mmc: block: ensure error propagation for non-blk commit 003fb0a51162d940f25fc35e70b0996a12c9e08a upstream. Requests to the mmc layer usually come through a block device IO. The exceptions are the ioctl interface, RPMB chardev ioctl and debugfs, which issue their own blk_mq requests through blk_execute_rq and do not query the BLK_STS error but the mmcblk-internal drv_op_result. This patch ensures that drv_op_result defaults to an error and has to be overwritten by the operation to be considered successful. The behavior leads to a bug where the request never propagates the error, e.g. by directly erroring out at mmc_blk_mq_issue_rq if mmc_blk_part_switch fails. The ioctl caller of the rpmb chardev then can never see an error (BLK_STS_IOERR, but drv_op_result is unchanged) and thus may assume that their call executed successfully when it did not. While always checking the blk_execute_rq return value would be advised, let's eliminate the error by always setting drv_op_result as -EIO to be overwritten on success (or other error) Fixes: 614f0388f580 ("mmc: block: move single ioctl() commands to block requests") Signed-off-by: Christian Loehle Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/59c17ada35664b818b7bd83752119b2d@hyperstone.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/block.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index db6d8a099910..498333b769fd 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -266,6 +266,7 @@ static ssize_t power_ro_lock_store(struct device *dev, goto out_put; } req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_BOOT_WP; + req_to_mmc_queue_req(req)->drv_op_result = -EIO; blk_execute_rq(req, false); ret = req_to_mmc_queue_req(req)->drv_op_result; blk_mq_free_request(req); @@ -657,6 +658,7 @@ static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md, idatas[0] = idata; req_to_mmc_queue_req(req)->drv_op = rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL; + req_to_mmc_queue_req(req)->drv_op_result = -EIO; req_to_mmc_queue_req(req)->drv_op_data = idatas; req_to_mmc_queue_req(req)->ioc_count = 1; blk_execute_rq(req, false); @@ -728,6 +730,7 @@ static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md, } req_to_mmc_queue_req(req)->drv_op = rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL; + req_to_mmc_queue_req(req)->drv_op_result = -EIO; req_to_mmc_queue_req(req)->drv_op_data = idata; req_to_mmc_queue_req(req)->ioc_count = n; blk_execute_rq(req, false); @@ -2812,6 +2815,7 @@ static int mmc_dbg_card_status_get(void *data, u64 *val) if (IS_ERR(req)) return PTR_ERR(req); req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_CARD_STATUS; + req_to_mmc_queue_req(req)->drv_op_result = -EIO; blk_execute_rq(req, false); ret = req_to_mmc_queue_req(req)->drv_op_result; if (ret >= 0) { @@ -2850,6 +2854,7 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) goto out_free; } req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_EXT_CSD; + req_to_mmc_queue_req(req)->drv_op_result = -EIO; req_to_mmc_queue_req(req)->drv_op_data = &ext_csd; blk_execute_rq(req, false); err = req_to_mmc_queue_req(req)->drv_op_result; -- cgit 1.4.1 From 0456b912121e45b3ef54abe3135e5dcb541f956c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 15 Apr 2023 18:07:30 +0200 Subject: power: supply: axp288_fuel_gauge: Fix external_power_changed race commit f8319774d6f1567d6e7d03653174ab0c82c5c66d upstream. fuel_gauge_external_power_changed() dereferences info->bat, which gets sets in axp288_fuel_gauge_probe() like this: info->bat = devm_power_supply_register(dev, &fuel_gauge_desc, &psy_cfg); As soon as devm_power_supply_register() has called device_add() the external_power_changed callback can get called. So there is a window where fuel_gauge_external_power_changed() may get called while info->bat has not been set yet leading to a NULL pointer dereference. Fixing this is easy. The external_power_changed callback gets passed the power_supply which will eventually get stored in info->bat, so fuel_gauge_external_power_changed() can simply directly use the passed in psy argument which is always valid. Fixes: 30abb3d07929 ("power: supply: axp288_fuel_gauge: Take lock before updating the valid flag") Cc: stable@vger.kernel.org Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/axp288_fuel_gauge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 05f413178462..3be6f3b10ea4 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -507,7 +507,7 @@ static void fuel_gauge_external_power_changed(struct power_supply *psy) mutex_lock(&info->lock); info->valid = 0; /* Force updating of the cached registers */ mutex_unlock(&info->lock); - power_supply_changed(info->bat); + power_supply_changed(psy); } static struct power_supply_desc fuel_gauge_desc = { -- cgit 1.4.1 From 72c28207c19c2c46fab8ae994aff25e197fb2949 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 15 Apr 2023 18:07:31 +0200 Subject: power: supply: bq25890: Fix external_power_changed race commit 029a443b9b6424170f00f6dd5b7682e682cce92e upstream. bq25890_charger_external_power_changed() dereferences bq->charger, which gets sets in bq25890_power_supply_init() like this: bq->charger = devm_power_supply_register(bq->dev, &bq->desc, &psy_cfg); As soon as devm_power_supply_register() has called device_add() the external_power_changed callback can get called. So there is a window where bq25890_charger_external_power_changed() may get called while bq->charger has not been set yet leading to a NULL pointer dereference. This race hits during boot sometimes on a Lenovo Yoga Book 1 yb1-x90f when the cht_wcove_pwrsrc (extcon) power_supply is done with detecting the connected charger-type which happens to exactly hit the small window: BUG: kernel NULL pointer dereference, address: 0000000000000018 RIP: 0010:__power_supply_is_supplied_by+0xb/0xb0 Call Trace: __power_supply_get_supplier_property+0x19/0x50 class_for_each_device+0xb1/0xe0 power_supply_get_property_from_supplier+0x2e/0x50 bq25890_charger_external_power_changed+0x38/0x1b0 [bq25890_charger] __power_supply_changed_work+0x30/0x40 class_for_each_device+0xb1/0xe0 power_supply_changed_work+0x5f/0xe0 Fixing this is easy. The external_power_changed callback gets passed the power_supply which will eventually get stored in bq->charger, so bq25890_charger_external_power_changed() can simply directly use the passed in psy argument which is always valid. Fixes: eab25b4f93aa ("power: supply: bq25890: On the bq25892 set the IINLIM based on external charger detection") Cc: stable@vger.kernel.org Cc: Marek Vasut Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 0e15302b8df2..f9ee843c8082 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -650,7 +650,7 @@ static void bq25890_charger_external_power_changed(struct power_supply *psy) if (bq->chip_version != BQ25892) return; - ret = power_supply_get_property_from_supplier(bq->charger, + ret = power_supply_get_property_from_supplier(psy, POWER_SUPPLY_PROP_USB_TYPE, &val); if (ret) -- cgit 1.4.1 From da1e8adab36650c0acc1ea1232e56ba283d69d54 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 16 May 2023 16:46:30 +0000 Subject: ASoC: rt5682: Disable jack detection interrupt during suspend commit 8b271370e963370703819bd9795a54d658071bed upstream. The rt5682 driver switches its regmap to cache-only when the device suspends and back to regular mode on resume. When the jack detect interrupt fires rt5682_irq() schedules the jack detect work. This can result in invalid reads from the regmap in cache-only mode if the work runs before the device has resumed: [ 56.245502] rt5682 9-001a: ASoC: error at soc_component_read_no_lock on rt5682.9-001a for register: [0x000000f0] -16 Disable the jack detection interrupt during suspend and re-enable it on resume. The driver already schedules the jack detection work on resume, so any state change during suspend is still handled. This is essentially the same as commit f7d00a9be147 ("SoC: rt5682s: Disable jack detection interrupt during suspend") for the rt5682s. Cc: stable@kernel.org Signed-off-by: Matthias Kaehlcke --- sound/soc/codecs/rt5682-i2c.c | 4 +++- sound/soc/codecs/rt5682.c | 6 ++++++ sound/soc/codecs/rt5682.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 2935c1bb81f3..5bc46b041786 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -267,7 +267,9 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5682_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5682", rt5682); - if (ret) + if (!ret) + rt5682->irq = i2c->irq; + else dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 2df95e792900..a6636ef69f57 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2957,6 +2957,9 @@ static int rt5682_suspend(struct snd_soc_component *component) if (rt5682->is_sdw) return 0; + if (rt5682->irq) + disable_irq(rt5682->irq); + cancel_delayed_work_sync(&rt5682->jack_detect_work); cancel_delayed_work_sync(&rt5682->jd_check_work); if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) { @@ -3025,6 +3028,9 @@ static int rt5682_resume(struct snd_soc_component *component) mod_delayed_work(system_power_efficient_wq, &rt5682->jack_detect_work, msecs_to_jiffies(0)); + if (rt5682->irq) + enable_irq(rt5682->irq); + return 0; } #else diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 52ff0d9c36c5..5fd798eac40d 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1462,6 +1462,7 @@ struct rt5682_priv { int pll_out[RT5682_PLLS]; int jack_type; + int irq; int irq_work_delay_time; }; -- cgit 1.4.1 From 6147745d43ff4e0d2c542e5b93e398ef0ee4db00 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Wed, 17 May 2023 13:38:08 +0000 Subject: net: cdc_ncm: Deal with too low values of dwNtbOutMaxSize commit 7e01c7f7046efc2c7c192c3619db43292b98e997 upstream. Currently in cdc_ncm_check_tx_max(), if dwNtbOutMaxSize is lower than the calculated "min" value, but greater than zero, the logic sets tx_max to dwNtbOutMaxSize. This is then used to allocate a new SKB in cdc_ncm_fill_tx_frame() where all the data is handled. For small values of dwNtbOutMaxSize the memory allocated during alloc_skb(dwNtbOutMaxSize, GFP_ATOMIC) will have the same size, due to how size is aligned at alloc time: size = SKB_DATA_ALIGN(size); size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); Thus we hit the same bug that we tried to squash with commit 2be6d4d16a084 ("net: cdc_ncm: Allow for dwNtbOutMaxSize to be unset or zero") Low values of dwNtbOutMaxSize do not cause an issue presently because at alloc_skb() time more memory (512b) is allocated than required for the SKB headers alone (320b), leaving some space (512b - 320b = 192b) for CDC data (172b). However, if more elements (for example 3 x u64 = [24b]) were added to one of the SKB header structs, say 'struct skb_shared_info', increasing its original size (320b [320b aligned]) to something larger (344b [384b aligned]), then suddenly the CDC data (172b) no longer fits in the spare SKB data area (512b - 384b = 128b). Consequently the SKB bounds checking semantics fails and panics: skbuff: skb_over_panic: text:ffffffff831f755b len:184 put:172 head:ffff88811f1c6c00 data:ffff88811f1c6c00 tail:0xb8 end:0x80 dev: ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:113! invalid opcode: 0000 [#1] PREEMPT SMP KASAN CPU: 0 PID: 57 Comm: kworker/0:2 Not tainted 5.15.106-syzkaller-00249-g19c0ed55a470 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/14/2023 Workqueue: mld mld_ifc_work RIP: 0010:skb_panic net/core/skbuff.c:113 [inline] RIP: 0010:skb_over_panic+0x14c/0x150 net/core/skbuff.c:118 [snip] Call Trace: skb_put+0x151/0x210 net/core/skbuff.c:2047 skb_put_zero include/linux/skbuff.h:2422 [inline] cdc_ncm_ndp16 drivers/net/usb/cdc_ncm.c:1131 [inline] cdc_ncm_fill_tx_frame+0x11ab/0x3da0 drivers/net/usb/cdc_ncm.c:1308 cdc_ncm_tx_fixup+0xa3/0x100 Deal with too low values of dwNtbOutMaxSize, clamp it in the range [USB_CDC_NCM_NTB_MIN_OUT_SIZE, CDC_NCM_NTB_MAX_SIZE_TX]. We ensure enough data space is allocated to handle CDC data by making sure dwNtbOutMaxSize is not smaller than USB_CDC_NCM_NTB_MIN_OUT_SIZE. Fixes: 289507d3364f ("net: cdc_ncm: use sysfs for rx/tx aggregation tuning") Cc: stable@vger.kernel.org Reported-by: syzbot+9f575a1f15fc0c01ed69@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=b982f1059506db48409d Link: https://lore.kernel.org/all/20211202143437.1411410-1-lee.jones@linaro.org/ Signed-off-by: Tudor Ambarus Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230517133808.1873695-2-tudor.ambarus@linaro.org Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/cdc_ncm.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 0897fdb6254b..789e3647f979 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -180,9 +180,12 @@ static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx) else min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth32); - max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); - if (max == 0) + if (le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) == 0) max = CDC_NCM_NTB_MAX_SIZE_TX; /* dwNtbOutMaxSize not set */ + else + max = clamp_t(u32, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize), + USB_CDC_NCM_NTB_MIN_OUT_SIZE, + CDC_NCM_NTB_MAX_SIZE_TX); /* some devices set dwNtbOutMaxSize too low for the above default */ min = min(min, max); @@ -1243,6 +1246,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) * further. */ if (skb_out == NULL) { + /* If even the smallest allocation fails, abort. */ + if (ctx->tx_curr_size == USB_CDC_NCM_NTB_MIN_OUT_SIZE) + goto alloc_failed; ctx->tx_low_mem_max_cnt = min(ctx->tx_low_mem_max_cnt + 1, (unsigned)CDC_NCM_LOW_MEM_MAX_CNT); ctx->tx_low_mem_val = ctx->tx_low_mem_max_cnt; @@ -1261,13 +1267,8 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC); /* No allocation possible so we will abort */ - if (skb_out == NULL) { - if (skb != NULL) { - dev_kfree_skb_any(skb); - dev->net->stats.tx_dropped++; - } - goto exit_no_skb; - } + if (!skb_out) + goto alloc_failed; ctx->tx_low_mem_val--; } if (ctx->is_ndp16) { @@ -1460,6 +1461,11 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) return skb_out; +alloc_failed: + if (skb) { + dev_kfree_skb_any(skb); + dev->net->stats.tx_dropped++; + } exit_no_skb: /* Start timer, if there is a remaining non-empty skb */ if (ctx->tx_curr_skb != NULL && n > 0) -- cgit 1.4.1 From 08456605089120cc828a8f3d8a46e215844f7af7 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sat, 6 May 2023 19:38:12 +1000 Subject: m68k: Move signal frame following exception on 68020/030 commit b845b574f86dcb6a70dfa698aa87a237b0878d2a upstream. On 68030/020, an instruction such as, moveml %a2-%a3/%a5,%sp@- may cause a stack page fault during instruction execution (i.e. not at an instruction boundary) and produce a format 0xB exception frame. In this situation, the value of USP will be unreliable. If a signal is to be delivered following the exception, this USP value is used to calculate the location for a signal frame. This can result in a corrupted user stack. The corruption was detected in dash (actually in glibc) where it showed up as an intermittent "stack smashing detected" message and crash following signal delivery for SIGCHLD. It was hard to reproduce that failure because delivery of the signal raced with the page fault and because the kernel places an unpredictable gap of up to 7 bytes between the USP and the signal frame. A format 0xB exception frame can be produced by a bus error or an address error. The 68030 Users Manual says that address errors occur immediately upon detection during instruction prefetch. The instruction pipeline allows prefetch to overlap with other instructions, which means an address error can arise during the execution of a different instruction. So it seems likely that this patch may help in the address error case also. Reported-and-tested-by: Stan Johnson Link: https://lore.kernel.org/all/CAMuHMdW3yD22_ApemzW_6me3adq6A458u1_F0v-1EYwK_62jPA@mail.gmail.com/ Cc: Michael Schmitz Cc: Andreas Schwab Cc: stable@vger.kernel.org Co-developed-by: Michael Schmitz Signed-off-by: Michael Schmitz Signed-off-by: Finn Thain Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/9e66262a754fcba50208aa424188896cc52a1dd1.1683365892.git.fthain@linux-m68k.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- arch/m68k/kernel/signal.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index b9f6908a31bc..ba468b5f3f0b 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -858,11 +858,17 @@ static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs * } static inline void __user * -get_sigframe(struct ksignal *ksig, size_t frame_size) +get_sigframe(struct ksignal *ksig, struct pt_regs *tregs, size_t frame_size) { unsigned long usp = sigsp(rdusp(), ksig); + unsigned long gap = 0; - return (void __user *)((usp - frame_size) & -8UL); + if (CPU_IS_020_OR_030 && tregs->format == 0xb) { + /* USP is unreliable so use worst-case value */ + gap = 256; + } + + return (void __user *)((usp - gap - frame_size) & -8UL); } static int setup_frame(struct ksignal *ksig, sigset_t *set, @@ -880,7 +886,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, return -EFAULT; } - frame = get_sigframe(ksig, sizeof(*frame) + fsize); + frame = get_sigframe(ksig, tregs, sizeof(*frame) + fsize); if (fsize) err |= copy_to_user (frame + 1, regs + 1, fsize); @@ -952,7 +958,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, return -EFAULT; } - frame = get_sigframe(ksig, sizeof(*frame)); + frame = get_sigframe(ksig, tregs, sizeof(*frame)); if (fsize) err |= copy_to_user (&frame->uc.uc_extra, regs + 1, fsize); -- cgit 1.4.1 From 522bbbfcb612b11f1c3ec4205c6300d16f878f81 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Tue, 2 May 2023 03:20:47 -0700 Subject: xtensa: fix signal delivery to FDPIC process commit 9c2cc74fb31ec76b8b118c97041a6a154a3ff219 upstream. Fetch function descriptor pointed to by the signal handler pointer from userspace on signal delivery and function pointer pointed to by the sa_restorer on return from the signal handler. Cc: stable@vger.kernel.org Fixes: e3ddb8bbe0f8 ("xtensa: add FDPIC and static PIE support for noMMU") Signed-off-by: Max Filippov Signed-off-by: Greg Kroah-Hartman --- arch/xtensa/kernel/signal.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index 876d5df157ed..5c01d7e70d90 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -343,7 +343,19 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, struct rt_sigframe *frame; int err = 0, sig = ksig->sig; unsigned long sp, ra, tp, ps; + unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler; + unsigned long handler_fdpic_GOT = 0; unsigned int base; + bool fdpic = IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) && + (current->personality & FDPIC_FUNCPTRS); + + if (fdpic) { + unsigned long __user *fdpic_func_desc = + (unsigned long __user *)handler; + if (__get_user(handler, &fdpic_func_desc[0]) || + __get_user(handler_fdpic_GOT, &fdpic_func_desc[1])) + return -EFAULT; + } sp = regs->areg[1]; @@ -373,20 +385,26 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (ksig->ka.sa.sa_flags & SA_RESTORER) { - ra = (unsigned long)ksig->ka.sa.sa_restorer; + if (fdpic) { + unsigned long __user *fdpic_func_desc = + (unsigned long __user *)ksig->ka.sa.sa_restorer; + + err |= __get_user(ra, fdpic_func_desc); + } else { + ra = (unsigned long)ksig->ka.sa.sa_restorer; + } } else { /* Create sys_rt_sigreturn syscall in stack frame */ err |= gen_return_code(frame->retcode); - - if (err) { - return -EFAULT; - } ra = (unsigned long) frame->retcode; } - /* + if (err) + return -EFAULT; + + /* * Create signal handler execution context. * Return context not modified until this point. */ @@ -394,8 +412,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, /* Set up registers for signal handler; preserve the threadptr */ tp = regs->threadptr; ps = regs->ps; - start_thread(regs, (unsigned long) ksig->ka.sa.sa_handler, - (unsigned long) frame); + start_thread(regs, handler, (unsigned long)frame); /* Set up a stack frame for a call4 if userspace uses windowed ABI */ if (ps & PS_WOE_MASK) { @@ -413,6 +430,8 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, regs->areg[base + 4] = (unsigned long) &frame->uc; regs->threadptr = tp; regs->ps = ps; + if (fdpic) + regs->areg[base + 11] = handler_fdpic_GOT; pr_debug("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08lx\n", current->comm, current->pid, sig, frame, regs->pc); -- cgit 1.4.1 From bd90ac0002d1a4e480d6afb0dd84bf0793913c75 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sat, 6 May 2023 17:10:36 -0700 Subject: xtensa: add __bswap{si,di}2 helpers commit 034f4a7877c32a8efd6beee4d71ed14e424499a9 upstream. gcc-13 may generate calls for __bswap{si,di}2. This breaks the kernel build when optimization for size is selected. Add __bswap{si,di}2 helpers to fix that. Cc: stable@vger.kernel.org Fixes: 19c5699f9aff ("xtensa: don't link with libgcc") Signed-off-by: Max Filippov Signed-off-by: Greg Kroah-Hartman --- arch/xtensa/kernel/xtensa_ksyms.c | 4 ++++ arch/xtensa/lib/Makefile | 2 +- arch/xtensa/lib/bswapdi2.S | 21 +++++++++++++++++++++ arch/xtensa/lib/bswapsi2.S | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 arch/xtensa/lib/bswapdi2.S create mode 100644 arch/xtensa/lib/bswapsi2.S diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c index 2a31b1ab0c9f..17a7ef86fd0d 100644 --- a/arch/xtensa/kernel/xtensa_ksyms.c +++ b/arch/xtensa/kernel/xtensa_ksyms.c @@ -56,6 +56,8 @@ EXPORT_SYMBOL(empty_zero_page); */ extern long long __ashrdi3(long long, int); extern long long __ashldi3(long long, int); +extern long long __bswapdi2(long long); +extern int __bswapsi2(int); extern long long __lshrdi3(long long, int); extern int __divsi3(int, int); extern int __modsi3(int, int); @@ -66,6 +68,8 @@ extern unsigned long long __umulsidi3(unsigned int, unsigned int); EXPORT_SYMBOL(__ashldi3); EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__bswapdi2); +EXPORT_SYMBOL(__bswapsi2); EXPORT_SYMBOL(__lshrdi3); EXPORT_SYMBOL(__divsi3); EXPORT_SYMBOL(__modsi3); diff --git a/arch/xtensa/lib/Makefile b/arch/xtensa/lib/Makefile index 7ecef0519a27..c9c2614188f7 100644 --- a/arch/xtensa/lib/Makefile +++ b/arch/xtensa/lib/Makefile @@ -4,7 +4,7 @@ # lib-y += memcopy.o memset.o checksum.o \ - ashldi3.o ashrdi3.o lshrdi3.o \ + ashldi3.o ashrdi3.o bswapdi2.o bswapsi2.o lshrdi3.o \ divsi3.o udivsi3.o modsi3.o umodsi3.o mulsi3.o umulsidi3.o \ usercopy.o strncpy_user.o strnlen_user.o lib-$(CONFIG_PCI) += pci-auto.o diff --git a/arch/xtensa/lib/bswapdi2.S b/arch/xtensa/lib/bswapdi2.S new file mode 100644 index 000000000000..d8e52e05eba6 --- /dev/null +++ b/arch/xtensa/lib/bswapdi2.S @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 */ +#include +#include +#include + +ENTRY(__bswapdi2) + + abi_entry_default + ssai 8 + srli a4, a2, 16 + src a4, a4, a2 + src a4, a4, a4 + src a4, a2, a4 + srli a2, a3, 16 + src a2, a2, a3 + src a2, a2, a2 + src a2, a3, a2 + mov a3, a4 + abi_ret_default + +ENDPROC(__bswapdi2) diff --git a/arch/xtensa/lib/bswapsi2.S b/arch/xtensa/lib/bswapsi2.S new file mode 100644 index 000000000000..9c1de1344f79 --- /dev/null +++ b/arch/xtensa/lib/bswapsi2.S @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 */ +#include +#include +#include + +ENTRY(__bswapsi2) + + abi_entry_default + ssai 8 + srli a3, a2, 16 + src a3, a3, a2 + src a3, a3, a3 + src a2, a2, a3 + abi_ret_default + +ENDPROC(__bswapsi2) -- cgit 1.4.1 From e1f14a4071406fbb0cd41b031027133206762b47 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 19 May 2023 12:12:06 +0200 Subject: parisc: Use num_present_cpus() in alternative patching code commit b6405f0829d7b1dd926ba3ca5f691cab835abfaa upstream. When patching the kernel code some alternatives depend on SMP vs. !SMP. Use the value of num_present_cpus() instead of num_online_cpus() to decide, otherwise we may run into issues if and additional CPU is enabled after having loaded a module while only one CPU was enabled. Signed-off-by: Helge Deller Cc: # v6.1+ Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/alternative.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/kernel/alternative.c b/arch/parisc/kernel/alternative.c index 66f5672c70bd..25c4d6c3375d 100644 --- a/arch/parisc/kernel/alternative.c +++ b/arch/parisc/kernel/alternative.c @@ -25,7 +25,7 @@ void __init_or_module apply_alternatives(struct alt_instr *start, { struct alt_instr *entry; int index = 0, applied = 0; - int num_cpus = num_online_cpus(); + int num_cpus = num_present_cpus(); u16 cond_check; cond_check = ALT_COND_ALWAYS | -- cgit 1.4.1 From c0993b463fe75f2aa7d7e367476757dd1a60fcff Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 24 May 2023 14:34:58 +0200 Subject: parisc: Handle kgdb breakpoints only in kernel context commit 6888ff04e37d01295620a73f3f7efbc79f6ef152 upstream. The kernel kgdb break instructions should only be handled when running in kernel context. Cc: # v5.4+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/traps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index f9696fbf646c..dff447f70042 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -302,8 +302,8 @@ static void handle_break(struct pt_regs *regs) #endif #ifdef CONFIG_KGDB - if (unlikely(iir == PARISC_KGDB_COMPILED_BREAK_INSN || - iir == PARISC_KGDB_BREAK_INSN)) { + if (unlikely((iir == PARISC_KGDB_COMPILED_BREAK_INSN || + iir == PARISC_KGDB_BREAK_INSN)) && !user_mode(regs)) { kgdb_handle_exception(9, SIGTRAP, 0, regs); return; } -- cgit 1.4.1 From c49ffd89b66e8580f3574a6f15967cd96f987ccb Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 24 May 2023 17:07:07 +0200 Subject: parisc: Fix flush_dcache_page() for usage from irq context commit 61e150fb310729c98227a5edf6e4a3619edc3702 upstream. Since at least kernel 6.1, flush_dcache_page() is called with IRQs disabled, e.g. from aio_complete(). But the current implementation for flush_dcache_page() on parisc unintentionally re-enables IRQs, which may lead to deadlocks. Fix it by using xa_lock_irqsave() and xa_unlock_irqrestore() for the flush_dcache_mmap_*lock() macros instead. Cc: linux-parisc@vger.kernel.org Cc: stable@kernel.org # 5.18+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/include/asm/cacheflush.h | 4 ++++ arch/parisc/kernel/cache.c | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h index 0bdee6724132..c8b6928cee1e 100644 --- a/arch/parisc/include/asm/cacheflush.h +++ b/arch/parisc/include/asm/cacheflush.h @@ -48,6 +48,10 @@ void flush_dcache_page(struct page *page); #define flush_dcache_mmap_lock(mapping) xa_lock_irq(&mapping->i_pages) #define flush_dcache_mmap_unlock(mapping) xa_unlock_irq(&mapping->i_pages) +#define flush_dcache_mmap_lock_irqsave(mapping, flags) \ + xa_lock_irqsave(&mapping->i_pages, flags) +#define flush_dcache_mmap_unlock_irqrestore(mapping, flags) \ + xa_unlock_irqrestore(&mapping->i_pages, flags) #define flush_icache_page(vma,page) do { \ flush_kernel_dcache_page_addr(page_address(page)); \ diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index 1d3b8bc8a623..ca4a302d4365 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -399,6 +399,7 @@ void flush_dcache_page(struct page *page) unsigned long offset; unsigned long addr, old_addr = 0; unsigned long count = 0; + unsigned long flags; pgoff_t pgoff; if (mapping && !mapping_mapped(mapping)) { @@ -420,7 +421,7 @@ void flush_dcache_page(struct page *page) * to flush one address here for them all to become coherent * on machines that support equivalent aliasing */ - flush_dcache_mmap_lock(mapping); + flush_dcache_mmap_lock_irqsave(mapping, flags); vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) { offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; addr = mpnt->vm_start + offset; @@ -460,7 +461,7 @@ void flush_dcache_page(struct page *page) } WARN_ON(++count == 4096); } - flush_dcache_mmap_unlock(mapping); + flush_dcache_mmap_unlock_irqrestore(mapping, flags); } EXPORT_SYMBOL(flush_dcache_page); -- cgit 1.4.1 From d935edd510d7873af1824b17f51dffb57c407d33 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 22 May 2023 22:57:30 +0200 Subject: parisc: Allow to reboot machine after system halt commit 2028315cf59bb899a5ac7e87dc48ecb8fac7ac24 upstream. In case a machine can't power-off itself on system shutdown, allow the user to reboot it by pressing the RETURN key. Cc: # v4.14+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/process.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c index c4f8374c7018..b62d60009fac 100644 --- a/arch/parisc/kernel/process.c +++ b/arch/parisc/kernel/process.c @@ -122,13 +122,18 @@ void machine_power_off(void) /* It seems we have no way to power the system off via * software. The user has to press the button himself. */ - printk(KERN_EMERG "System shut down completed.\n" - "Please power this system off now."); + printk("Power off or press RETURN to reboot.\n"); /* prevent soft lockup/stalled CPU messages for endless loop. */ rcu_sysrq_start(); lockup_detector_soft_poweroff(); - for (;;); + while (1) { + /* reboot if user presses RETURN key */ + if (pdc_iodc_getc() == 13) { + printk("Rebooting...\n"); + machine_restart(NULL); + } + } } void (*pm_power_off)(void); -- cgit 1.4.1 From 5596e2ef5f1a4c555e23ccb1d518a06bea21cdec Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 23 May 2023 09:06:40 +0200 Subject: parisc: Enable LOCKDEP support commit adf8e96a7ea670d45b5de7594acc67e8f4787ae6 upstream. Cc: # v6.0+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index a98940e64243..67c26e81e215 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -129,6 +129,10 @@ config PM config STACKTRACE_SUPPORT def_bool y +config LOCKDEP_SUPPORT + bool + default y + config ISA_DMA_API bool -- cgit 1.4.1 From b49706d1799ab8edd3530e19625015fb058e46f5 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 24 May 2023 14:31:14 +0200 Subject: parisc: Handle kprobes breakpoints only in kernel context commit df419492e428b6a2bce98d0f613c58a13da6666c upstream. The kernel kprobes break instructions should only be handled when running in kernel context. Cc: # v5.18+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/traps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index dff447f70042..67b51841dc8b 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -291,11 +291,11 @@ static void handle_break(struct pt_regs *regs) } #ifdef CONFIG_KPROBES - if (unlikely(iir == PARISC_KPROBES_BREAK_INSN)) { + if (unlikely(iir == PARISC_KPROBES_BREAK_INSN && !user_mode(regs))) { parisc_kprobe_break_handler(regs); return; } - if (unlikely(iir == PARISC_KPROBES_BREAK_INSN2)) { + if (unlikely(iir == PARISC_KPROBES_BREAK_INSN2 && !user_mode(regs))) { parisc_kprobe_ss_handler(regs); return; } -- cgit 1.4.1 From 63e12910b7f5efdf8bf2355f1f80bf1f4027d4f5 Mon Sep 17 00:00:00 2001 From: Zev Weiss Date: Tue, 16 May 2023 22:47:56 -0700 Subject: gpio: mockup: Fix mode of debugfs files commit 0a1bb16e0fe6650c3841e611de374bfd5578ad70 upstream. This driver's debugfs files have had a read operation since commit 2a9e27408e12 ("gpio: mockup: rework debugfs interface"), but were still being created with write-only mode bits. Update them to indicate that the files can also be read. Signed-off-by: Zev Weiss Fixes: 2a9e27408e12 ("gpio: mockup: rework debugfs interface") Cc: stable@kernel.org # v5.1+ Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mockup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 523dfd17dd92..72a369cf8207 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -368,7 +368,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev, priv->offset = i; priv->desc = gpiochip_get_desc(gc, i); - debugfs_create_file(name, 0200, chip->dbg_dir, priv, + debugfs_create_file(name, 0600, chip->dbg_dir, priv, &gpio_mockup_debugfs_ops); } } -- cgit 1.4.1 From 937264cd9aab5c636135d061e6d50e26f9beaa1f Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 11 May 2023 12:45:59 -0400 Subject: btrfs: use nofs when cleaning up aborted transactions commit 597441b3436a43011f31ce71dc0a6c0bf5ce958a upstream. Our CI system caught a lockdep splat: ====================================================== WARNING: possible circular locking dependency detected 6.3.0-rc7+ #1167 Not tainted ------------------------------------------------------ kswapd0/46 is trying to acquire lock: ffff8c6543abd650 (sb_internal#2){++++}-{0:0}, at: btrfs_commit_inode_delayed_inode+0x5f/0x120 but task is already holding lock: ffffffffabe61b40 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x4aa/0x7a0 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (fs_reclaim){+.+.}-{0:0}: fs_reclaim_acquire+0xa5/0xe0 kmem_cache_alloc+0x31/0x2c0 alloc_extent_state+0x1d/0xd0 __clear_extent_bit+0x2e0/0x4f0 try_release_extent_mapping+0x216/0x280 btrfs_release_folio+0x2e/0x90 invalidate_inode_pages2_range+0x397/0x470 btrfs_cleanup_dirty_bgs+0x9e/0x210 btrfs_cleanup_one_transaction+0x22/0x760 btrfs_commit_transaction+0x3b7/0x13a0 create_subvol+0x59b/0x970 btrfs_mksubvol+0x435/0x4f0 __btrfs_ioctl_snap_create+0x11e/0x1b0 btrfs_ioctl_snap_create_v2+0xbf/0x140 btrfs_ioctl+0xa45/0x28f0 __x64_sys_ioctl+0x88/0xc0 do_syscall_64+0x38/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc -> #0 (sb_internal#2){++++}-{0:0}: __lock_acquire+0x1435/0x21a0 lock_acquire+0xc2/0x2b0 start_transaction+0x401/0x730 btrfs_commit_inode_delayed_inode+0x5f/0x120 btrfs_evict_inode+0x292/0x3d0 evict+0xcc/0x1d0 inode_lru_isolate+0x14d/0x1e0 __list_lru_walk_one+0xbe/0x1c0 list_lru_walk_one+0x58/0x80 prune_icache_sb+0x39/0x60 super_cache_scan+0x161/0x1f0 do_shrink_slab+0x163/0x340 shrink_slab+0x1d3/0x290 shrink_node+0x300/0x720 balance_pgdat+0x35c/0x7a0 kswapd+0x205/0x410 kthread+0xf0/0x120 ret_from_fork+0x29/0x50 other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(fs_reclaim); lock(sb_internal#2); lock(fs_reclaim); lock(sb_internal#2); *** DEADLOCK *** 3 locks held by kswapd0/46: #0: ffffffffabe61b40 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x4aa/0x7a0 #1: ffffffffabe50270 (shrinker_rwsem){++++}-{3:3}, at: shrink_slab+0x113/0x290 #2: ffff8c6543abd0e0 (&type->s_umount_key#44){++++}-{3:3}, at: super_cache_scan+0x38/0x1f0 stack backtrace: CPU: 0 PID: 46 Comm: kswapd0 Not tainted 6.3.0-rc7+ #1167 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-2.fc32 04/01/2014 Call Trace: dump_stack_lvl+0x58/0x90 check_noncircular+0xd6/0x100 ? save_trace+0x3f/0x310 ? add_lock_to_list+0x97/0x120 __lock_acquire+0x1435/0x21a0 lock_acquire+0xc2/0x2b0 ? btrfs_commit_inode_delayed_inode+0x5f/0x120 start_transaction+0x401/0x730 ? btrfs_commit_inode_delayed_inode+0x5f/0x120 btrfs_commit_inode_delayed_inode+0x5f/0x120 btrfs_evict_inode+0x292/0x3d0 ? lock_release+0x134/0x270 ? __pfx_wake_bit_function+0x10/0x10 evict+0xcc/0x1d0 inode_lru_isolate+0x14d/0x1e0 __list_lru_walk_one+0xbe/0x1c0 ? __pfx_inode_lru_isolate+0x10/0x10 ? __pfx_inode_lru_isolate+0x10/0x10 list_lru_walk_one+0x58/0x80 prune_icache_sb+0x39/0x60 super_cache_scan+0x161/0x1f0 do_shrink_slab+0x163/0x340 shrink_slab+0x1d3/0x290 shrink_node+0x300/0x720 balance_pgdat+0x35c/0x7a0 kswapd+0x205/0x410 ? __pfx_autoremove_wake_function+0x10/0x10 ? __pfx_kswapd+0x10/0x10 kthread+0xf0/0x120 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x29/0x50 This happens because when we abort the transaction in the transaction commit path we call invalidate_inode_pages2_range on our block group cache inodes (if we have space cache v1) and any delalloc inodes we may have. The plain invalidate_inode_pages2_range() call passes through GFP_KERNEL, which makes sense in most cases, but not here. Wrap these two invalidate callees with memalloc_nofs_save/memalloc_nofs_restore to make sure we don't end up with the fs reclaim dependency under the transaction dependency. CC: stable@vger.kernel.org # 4.14+ Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/disk-io.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index acae82a5f8ee..21d8a895e9ad 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -5035,7 +5035,11 @@ static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root) */ inode = igrab(&btrfs_inode->vfs_inode); if (inode) { + unsigned int nofs_flag; + + nofs_flag = memalloc_nofs_save(); invalidate_inode_pages2(inode->i_mapping); + memalloc_nofs_restore(nofs_flag); iput(inode); } spin_lock(&root->delalloc_lock); @@ -5140,7 +5144,12 @@ static void btrfs_cleanup_bg_io(struct btrfs_block_group *cache) inode = cache->io_ctl.inode; if (inode) { + unsigned int nofs_flag; + + nofs_flag = memalloc_nofs_save(); invalidate_inode_pages2(inode->i_mapping); + memalloc_nofs_restore(nofs_flag); + BTRFS_I(inode)->generation = 0; cache->io_ctl.inode = NULL; iput(inode); -- cgit 1.4.1 From 3970ee926e7ef7cb402350c3140fc0a52723b8a6 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 15 May 2023 12:20:52 -0400 Subject: dt-binding: cdns,usb3: Fix cdns,on-chip-buff-size type commit 50a1726b148ff30778cb8a6cf3736130b07c93fd upstream. In cdns3-gadget.c, 'cdns,on-chip-buff-size' was read using device_property_read_u16(). It resulted in 0 if a 32bit value was used in dts. This commit fixes the dt binding doc to declare it as u16. Cc: stable@vger.kernel.org Fixes: 68989fe1c39d ("dt-bindings: usb: Convert cdns-usb3.txt to YAML schema") Signed-off-by: Frank Li Reviewed-by: Krzysztof Kozlowski Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/cdns,usb3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml index dc9d6ed0781d..5d0bfea2c087 100644 --- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml +++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml @@ -64,7 +64,7 @@ properties: description: size of memory intended as internal memory for endpoints buffers expressed in KB - $ref: /schemas/types.yaml#/definitions/uint32 + $ref: /schemas/types.yaml#/definitions/uint16 cdns,phyrst-a-enable: description: Enable resetting of PHY if Rx fail is detected -- cgit 1.4.1 From 3897ac532af081add849a06554949b6bad7653c0 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Wed, 10 May 2023 15:10:34 +0200 Subject: drm/mgag200: Fix gamma lut not initialized. commit ad81e23426a651eb89a4b306e1c4169e6308c124 upstream. When mgag200 switched from simple KMS to regular atomic helpers, the initialization of the gamma settings was lost. This leads to a black screen, if the bios/uefi doesn't use the same pixel color depth. v2: rebase on top of drm-misc-fixes, and add Cc stable tag. Link: https://bugzilla.redhat.com/show_bug.cgi?id=2171155 Fixes: 1baf9127c482 ("drm/mgag200: Replace simple-KMS with regular atomic helpers") Cc: Tested-by: Phil Oester Reviewed-by: Thomas Zimmermann Signed-off-by: Jocelyn Falempe Link: https://patchwork.freedesktop.org/patch/msgid/20230510131034.284078-1-jfalempe@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/mgag200/mgag200_mode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index bbab2549243a..ae90b260312a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -640,6 +640,11 @@ void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_ if (funcs->pixpllc_atomic_update) funcs->pixpllc_atomic_update(crtc, old_state); + if (crtc_state->gamma_lut) + mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); + else + mgag200_crtc_set_gamma_linear(mdev, format); + mgag200_enable_display(mdev); if (funcs->enable_vidrst) -- cgit 1.4.1 From 8756863c7fe0f94b23a2e0f0a0d2a79d58e2a329 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 18 May 2023 12:38:22 -0400 Subject: drm/radeon: reintroduce radeon_dp_work_func content commit a34fc1bcd2c4d8b09dcfc0b95ac65bca1e579bd7 upstream. Put back the radeon_dp_work_func logic. It seems that handling DP RX interrupts is necessary to make some panels work. This was removed with the MST support, but it regresses some systems so add it back. While we are here, add the proper mutex locking. Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2567 Fixes: 01ad1d9c2888 ("drm/radeon: Drop legacy MST support") Reviewed-by: Lyude Paul Signed-off-by: Alex Deucher Cc: Lyude Paul Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_irq_kms.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index da2173435edd..6541c1f6fca6 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -100,6 +100,16 @@ static void radeon_hotplug_work_func(struct work_struct *work) static void radeon_dp_work_func(struct work_struct *work) { + struct radeon_device *rdev = container_of(work, struct radeon_device, + dp_work); + struct drm_device *dev = rdev->ddev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *connector; + + mutex_lock(&mode_config->mutex); + list_for_each_entry(connector, &mode_config->connector_list, head) + radeon_connector_hotplug(connector); + mutex_unlock(&mode_config->mutex); } /** -- cgit 1.4.1 From 6acfbdda4d06d83d434b05b7fe1a9d545d99c027 Mon Sep 17 00:00:00 2001 From: Evan Quan Date: Fri, 19 May 2023 14:20:17 +0800 Subject: drm/amd/pm: add missing NotifyPowerSource message mapping for SMU13.0.7 commit 0d2dd02d74e6377268f56b90261de0fae8f0d2cb upstream. Otherwise, the power source switching will fail due to message unavailable. Fixes: bf4823267a81 ("drm/amd/pm: fix possible power mode mismatch between driver and PMFW") Signed-off-by: Evan Quan Reviewed-by: Guchun Chen Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 9dd92bbdcefd..bd61518bb7b1 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -125,6 +125,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_7_message_map[SMU_MSG_MAX_COUNT] = MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0), MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0), MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0), + MSG_MAP(NotifyPowerSource, PPSMC_MSG_NotifyPowerSource, 0), }; static struct cmn2asic_mapping smu_v13_0_7_clk_map[SMU_CLK_COUNT] = { -- cgit 1.4.1 From 8069bcaa5b392ce3972dd72f184b2cc4b1b8262b Mon Sep 17 00:00:00 2001 From: Jonatas Esteves Date: Sat, 20 May 2023 10:39:52 -0300 Subject: drm/amd/pm: Fix output of pp_od_clk_voltage commit 40baba5693b9af586dc1063af603d05a79e57a6b upstream. Printing the other clock types should not be conditioned on being able to print OD_SCLK. Some GPUs currently have limited capability of only printing a subset of these. Since this condition was introduced in v5.18-rc1, reading from `pp_od_clk_voltage` has been returning empty on the Asus ROG Strix G15 (2021). Fixes: 79c65f3fcbb1 ("drm/amd/pm: do not expose power implementation details to amdgpu_pm.c") Reviewed-by: Evan Quan Signed-off-by: Jonatas Esteves Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 2f3e239e623d..7d613118cb71 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -869,13 +869,11 @@ static ssize_t amdgpu_get_pp_od_clk_voltage(struct device *dev, } if (ret == -ENOENT) { size = amdgpu_dpm_print_clock_levels(adev, OD_SCLK, buf); - if (size > 0) { - size += amdgpu_dpm_print_clock_levels(adev, OD_MCLK, buf + size); - size += amdgpu_dpm_print_clock_levels(adev, OD_VDDC_CURVE, buf + size); - size += amdgpu_dpm_print_clock_levels(adev, OD_VDDGFX_OFFSET, buf + size); - size += amdgpu_dpm_print_clock_levels(adev, OD_RANGE, buf + size); - size += amdgpu_dpm_print_clock_levels(adev, OD_CCLK, buf + size); - } + size += amdgpu_dpm_print_clock_levels(adev, OD_MCLK, buf + size); + size += amdgpu_dpm_print_clock_levels(adev, OD_VDDC_CURVE, buf + size); + size += amdgpu_dpm_print_clock_levels(adev, OD_VDDGFX_OFFSET, buf + size); + size += amdgpu_dpm_print_clock_levels(adev, OD_RANGE, buf + size); + size += amdgpu_dpm_print_clock_levels(adev, OD_CCLK, buf + size); } if (size == 0) -- cgit 1.4.1 From 7e6b8548549e1fa87fbdd6b54f07b8c37a6d4b5e Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Tue, 2 May 2023 20:12:17 +0000 Subject: Revert "binder_alloc: add missing mmap_lock calls when using the VMA" commit b15655b12ddca7ade09807f790bafb6fab61b50a upstream. This reverts commit 44e602b4e52f70f04620bbbf4fe46ecb40170bde. This caused a performance regression particularly when pages are getting reclaimed. We don't need to acquire the mmap_lock to determine when the binder buffer has been fully initialized. A subsequent patch will bring back the lockless approach for this. [cmllamas: resolved trivial conflicts with renaming of alloc->mm] Fixes: 44e602b4e52f ("binder_alloc: add missing mmap_lock calls when using the VMA") Cc: Liam Howlett Cc: Suren Baghdasaryan Cc: stable@vger.kernel.org Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20230502201220.1756319-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 4ad42b0f75cd..35c0cdfa816b 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -380,15 +380,12 @@ static struct binder_buffer *binder_alloc_new_buf_locked( size_t size, data_offsets_size; int ret; - mmap_read_lock(alloc->mm); if (!binder_alloc_get_vma(alloc)) { - mmap_read_unlock(alloc->mm); binder_alloc_debug(BINDER_DEBUG_USER_ERROR, "%d: binder_alloc_buf, no vma\n", alloc->pid); return ERR_PTR(-ESRCH); } - mmap_read_unlock(alloc->mm); data_offsets_size = ALIGN(data_size, sizeof(void *)) + ALIGN(offsets_size, sizeof(void *)); @@ -916,25 +913,17 @@ void binder_alloc_print_pages(struct seq_file *m, * Make sure the binder_alloc is fully initialized, otherwise we might * read inconsistent state. */ - - mmap_read_lock(alloc->mm); - if (binder_alloc_get_vma(alloc) == NULL) { - mmap_read_unlock(alloc->mm); - goto uninitialized; - } - - mmap_read_unlock(alloc->mm); - for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { - page = &alloc->pages[i]; - if (!page->page_ptr) - free++; - else if (list_empty(&page->lru)) - active++; - else - lru++; + if (binder_alloc_get_vma(alloc) != NULL) { + for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { + page = &alloc->pages[i]; + if (!page->page_ptr) + free++; + else if (list_empty(&page->lru)) + active++; + else + lru++; + } } - -uninitialized: mutex_unlock(&alloc->mutex); seq_printf(m, " pages: %d:%d:%d\n", active, lru, free); seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high); -- cgit 1.4.1 From 72a94f8c14a1e537cb50c1dcd48c6f49575478ed Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Tue, 2 May 2023 20:12:18 +0000 Subject: Revert "android: binder: stop saving a pointer to the VMA" commit c0fd2101781ef761b636769b2f445351f71c3626 upstream. This reverts commit a43cfc87caaf46710c8027a8c23b8a55f1078f19. This patch fixed an issue reported by syzkaller in [1]. However, this turned out to be only a band-aid in binder. The root cause, as bisected by syzkaller, was fixed by commit 5789151e48ac ("mm/mmap: undo ->mmap() when mas_preallocate() fails"). We no longer need the patch for binder. Reverting such patch allows us to have a lockless access to alloc->vma in specific cases where the mmap_lock is not required. This approach avoids the contention that caused a performance regression. [1] https://lore.kernel.org/all/0000000000004a0dbe05e1d749e0@google.com [cmllamas: resolved conflicts with rework of alloc->mm and removal of binder_alloc_set_vma() also fixed comment section] Fixes: a43cfc87caaf ("android: binder: stop saving a pointer to the VMA") Cc: Liam Howlett Cc: Suren Baghdasaryan Cc: stable@vger.kernel.org Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20230502201220.1756319-2-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 17 +++++++++-------- drivers/android/binder_alloc.h | 4 ++-- drivers/android/binder_alloc_selftest.c | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 35c0cdfa816b..acf510bb4d16 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -213,7 +213,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, if (mm) { mmap_read_lock(mm); - vma = vma_lookup(mm, alloc->vma_addr); + vma = alloc->vma; } if (!vma && need_mm) { @@ -314,9 +314,11 @@ static inline struct vm_area_struct *binder_alloc_get_vma( { struct vm_area_struct *vma = NULL; - if (alloc->vma_addr) - vma = vma_lookup(alloc->mm, alloc->vma_addr); - + if (alloc->vma) { + /* Look at description in binder_alloc_set_vma */ + smp_rmb(); + vma = alloc->vma; + } return vma; } @@ -775,7 +777,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, buffer->free = 1; binder_insert_free_buffer(alloc, buffer); alloc->free_async_space = alloc->buffer_size / 2; - alloc->vma_addr = vma->vm_start; + alloc->vma = vma; return 0; @@ -805,8 +807,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) buffers = 0; mutex_lock(&alloc->mutex); - BUG_ON(alloc->vma_addr && - vma_lookup(alloc->mm, alloc->vma_addr)); + BUG_ON(alloc->vma); while ((n = rb_first(&alloc->allocated_buffers))) { buffer = rb_entry(n, struct binder_buffer, rb_node); @@ -958,7 +959,7 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) */ void binder_alloc_vma_close(struct binder_alloc *alloc) { - alloc->vma_addr = 0; + alloc->vma = 0; } /** diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index 0f811ac4bcff..138d1d5af9ce 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -75,7 +75,7 @@ struct binder_lru_page { /** * struct binder_alloc - per-binder proc state for binder allocator * @mutex: protects binder_alloc fields - * @vma_addr: vm_area_struct->vm_start passed to mmap_handler + * @vma: vm_area_struct passed to mmap_handler * (invariant after mmap) * @mm: copy of task->mm (invariant after open) * @buffer: base of per-proc address space mapped via mmap @@ -99,7 +99,7 @@ struct binder_lru_page { */ struct binder_alloc { struct mutex mutex; - unsigned long vma_addr; + struct vm_area_struct *vma; struct mm_struct *mm; void __user *buffer; struct list_head buffers; diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c index 43a881073a42..c2b323bc3b3a 100644 --- a/drivers/android/binder_alloc_selftest.c +++ b/drivers/android/binder_alloc_selftest.c @@ -287,7 +287,7 @@ void binder_selftest_alloc(struct binder_alloc *alloc) if (!binder_selftest_run) return; mutex_lock(&binder_selftest_lock); - if (!binder_selftest_run || !alloc->vma_addr) + if (!binder_selftest_run || !alloc->vma) goto done; pr_info("STARTED\n"); binder_selftest_alloc_offset(alloc, end_offset, 0); -- cgit 1.4.1 From d7cee853bcb07e02c6c5a65037eac2ab081c4337 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Tue, 2 May 2023 20:12:19 +0000 Subject: binder: add lockless binder_alloc_(set|get)_vma() commit 0fa53349c3acba0239369ba4cd133740a408d246 upstream. Bring back the original lockless design in binder_alloc to determine whether the buffer setup has been completed by the ->mmap() handler. However, this time use smp_load_acquire() and smp_store_release() to wrap all the ordering in a single macro call. Also, add comments to make it evident that binder uses alloc->vma to determine when the binder_alloc has been fully initialized. In these scenarios acquiring the mmap_lock is not required. Fixes: a43cfc87caaf ("android: binder: stop saving a pointer to the VMA") Cc: Liam Howlett Cc: Suren Baghdasaryan Cc: stable@vger.kernel.org Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20230502201220.1756319-3-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index acf510bb4d16..66e7930705b9 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -309,17 +309,18 @@ err_no_vma: return vma ? -ENOMEM : -ESRCH; } +static inline void binder_alloc_set_vma(struct binder_alloc *alloc, + struct vm_area_struct *vma) +{ + /* pairs with smp_load_acquire in binder_alloc_get_vma() */ + smp_store_release(&alloc->vma, vma); +} + static inline struct vm_area_struct *binder_alloc_get_vma( struct binder_alloc *alloc) { - struct vm_area_struct *vma = NULL; - - if (alloc->vma) { - /* Look at description in binder_alloc_set_vma */ - smp_rmb(); - vma = alloc->vma; - } - return vma; + /* pairs with smp_store_release in binder_alloc_set_vma() */ + return smp_load_acquire(&alloc->vma); } static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid) @@ -382,6 +383,7 @@ static struct binder_buffer *binder_alloc_new_buf_locked( size_t size, data_offsets_size; int ret; + /* Check binder_alloc is fully initialized */ if (!binder_alloc_get_vma(alloc)) { binder_alloc_debug(BINDER_DEBUG_USER_ERROR, "%d: binder_alloc_buf, no vma\n", @@ -777,7 +779,9 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, buffer->free = 1; binder_insert_free_buffer(alloc, buffer); alloc->free_async_space = alloc->buffer_size / 2; - alloc->vma = vma; + + /* Signal binder_alloc is fully initialized */ + binder_alloc_set_vma(alloc, vma); return 0; @@ -959,7 +963,7 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) */ void binder_alloc_vma_close(struct binder_alloc *alloc) { - alloc->vma = 0; + binder_alloc_set_vma(alloc, NULL); } /** -- cgit 1.4.1 From e1e198eff1fbaf56fd8022c4fbbf59c5324ea320 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 5 May 2023 20:30:20 +0000 Subject: binder: fix UAF caused by faulty buffer cleanup commit bdc1c5fac982845a58d28690cdb56db8c88a530d upstream. In binder_transaction_buffer_release() the 'failed_at' offset indicates the number of objects to clean up. However, this function was changed by commit 44d8047f1d87 ("binder: use standard functions to allocate fds"), to release all the objects in the buffer when 'failed_at' is zero. This introduced an issue when a transaction buffer is released without any objects having been processed so far. In this case, 'failed_at' is indeed zero yet it is misinterpreted as releasing the entire buffer. This leads to use-after-free errors where nodes are incorrectly freed and subsequently accessed. Such is the case in the following KASAN report: ================================================================== BUG: KASAN: slab-use-after-free in binder_thread_read+0xc40/0x1f30 Read of size 8 at addr ffff4faf037cfc58 by task poc/474 CPU: 6 PID: 474 Comm: poc Not tainted 6.3.0-12570-g7df047b3f0aa #5 Hardware name: linux,dummy-virt (DT) Call trace: dump_backtrace+0x94/0xec show_stack+0x18/0x24 dump_stack_lvl+0x48/0x60 print_report+0xf8/0x5b8 kasan_report+0xb8/0xfc __asan_load8+0x9c/0xb8 binder_thread_read+0xc40/0x1f30 binder_ioctl+0xd9c/0x1768 __arm64_sys_ioctl+0xd4/0x118 invoke_syscall+0x60/0x188 [...] Allocated by task 474: kasan_save_stack+0x3c/0x64 kasan_set_track+0x2c/0x40 kasan_save_alloc_info+0x24/0x34 __kasan_kmalloc+0xb8/0xbc kmalloc_trace+0x48/0x5c binder_new_node+0x3c/0x3a4 binder_transaction+0x2b58/0x36f0 binder_thread_write+0x8e0/0x1b78 binder_ioctl+0x14a0/0x1768 __arm64_sys_ioctl+0xd4/0x118 invoke_syscall+0x60/0x188 [...] Freed by task 475: kasan_save_stack+0x3c/0x64 kasan_set_track+0x2c/0x40 kasan_save_free_info+0x38/0x5c __kasan_slab_free+0xe8/0x154 __kmem_cache_free+0x128/0x2bc kfree+0x58/0x70 binder_dec_node_tmpref+0x178/0x1fc binder_transaction_buffer_release+0x430/0x628 binder_transaction+0x1954/0x36f0 binder_thread_write+0x8e0/0x1b78 binder_ioctl+0x14a0/0x1768 __arm64_sys_ioctl+0xd4/0x118 invoke_syscall+0x60/0x188 [...] ================================================================== In order to avoid these issues, let's always calculate the intended 'failed_at' offset beforehand. This is renamed and wrapped in a helper function to make it clear and convenient. Fixes: 32e9f56a96d8 ("binder: don't detect sender/target during buffer cleanup") Reported-by: Zi Fan Tan Cc: stable@vger.kernel.org Signed-off-by: Carlos Llamas Acked-by: Todd Kjos Link: https://lore.kernel.org/r/20230505203020.4101154-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 880224ec6abb..e374a8a2da46 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1934,24 +1934,23 @@ static void binder_deferred_fd_close(int fd) static void binder_transaction_buffer_release(struct binder_proc *proc, struct binder_thread *thread, struct binder_buffer *buffer, - binder_size_t failed_at, + binder_size_t off_end_offset, bool is_failure) { int debug_id = buffer->debug_id; - binder_size_t off_start_offset, buffer_offset, off_end_offset; + binder_size_t off_start_offset, buffer_offset; binder_debug(BINDER_DEBUG_TRANSACTION, "%d buffer release %d, size %zd-%zd, failed at %llx\n", proc->pid, buffer->debug_id, buffer->data_size, buffer->offsets_size, - (unsigned long long)failed_at); + (unsigned long long)off_end_offset); if (buffer->target_node) binder_dec_node(buffer->target_node, 1, 0); off_start_offset = ALIGN(buffer->data_size, sizeof(void *)); - off_end_offset = is_failure && failed_at ? failed_at : - off_start_offset + buffer->offsets_size; + for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; buffer_offset += sizeof(binder_size_t)) { struct binder_object_header *hdr; @@ -2111,6 +2110,21 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, } } +/* Clean up all the objects in the buffer */ +static inline void binder_release_entire_buffer(struct binder_proc *proc, + struct binder_thread *thread, + struct binder_buffer *buffer, + bool is_failure) +{ + binder_size_t off_end_offset; + + off_end_offset = ALIGN(buffer->data_size, sizeof(void *)); + off_end_offset += buffer->offsets_size; + + binder_transaction_buffer_release(proc, thread, buffer, + off_end_offset, is_failure); +} + static int binder_translate_binder(struct flat_binder_object *fp, struct binder_transaction *t, struct binder_thread *thread) @@ -2801,7 +2815,7 @@ static int binder_proc_transaction(struct binder_transaction *t, t_outdated->buffer = NULL; buffer->transaction = NULL; trace_binder_transaction_update_buffer_release(buffer); - binder_transaction_buffer_release(proc, NULL, buffer, 0, 0); + binder_release_entire_buffer(proc, NULL, buffer, false); binder_alloc_free_buf(&proc->alloc, buffer); kfree(t_outdated); binder_stats_deleted(BINDER_STAT_TRANSACTION); @@ -3759,7 +3773,7 @@ binder_free_buf(struct binder_proc *proc, binder_node_inner_unlock(buf_node); } trace_binder_transaction_buffer_release(buffer); - binder_transaction_buffer_release(proc, thread, buffer, 0, is_failure); + binder_release_entire_buffer(proc, thread, buffer, is_failure); binder_alloc_free_buf(&proc->alloc, buffer); } -- cgit 1.4.1 From 931ea1ed31be939c1efdbc49bc66d2a45684f9b4 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 May 2023 19:59:49 +0000 Subject: binder: fix UAF of alloc->vma in race with munmap() commit d1d8875c8c13517f6fd1ff8d4d3e1ac366a17e07 upstream. [ cmllamas: clean forward port from commit 015ac18be7de ("binder: fix UAF of alloc->vma in race with munmap()") in 5.10 stable. It is needed in mainline after the revert of commit a43cfc87caaf ("android: binder: stop saving a pointer to the VMA") as pointed out by Liam. The commit log and tags have been tweaked to reflect this. ] In commit 720c24192404 ("ANDROID: binder: change down_write to down_read") binder assumed the mmap read lock is sufficient to protect alloc->vma inside binder_update_page_range(). This used to be accurate until commit dd2283f2605e ("mm: mmap: zap pages with read mmap_sem in munmap"), which now downgrades the mmap_lock after detaching the vma from the rbtree in munmap(). Then it proceeds to teardown and free the vma with only the read lock held. This means that accesses to alloc->vma in binder_update_page_range() now will race with vm_area_free() in munmap() and can cause a UAF as shown in the following KASAN trace: ================================================================== BUG: KASAN: use-after-free in vm_insert_page+0x7c/0x1f0 Read of size 8 at addr ffff16204ad00600 by task server/558 CPU: 3 PID: 558 Comm: server Not tainted 5.10.150-00001-gdc8dcf942daa #1 Hardware name: linux,dummy-virt (DT) Call trace: dump_backtrace+0x0/0x2a0 show_stack+0x18/0x2c dump_stack+0xf8/0x164 print_address_description.constprop.0+0x9c/0x538 kasan_report+0x120/0x200 __asan_load8+0xa0/0xc4 vm_insert_page+0x7c/0x1f0 binder_update_page_range+0x278/0x50c binder_alloc_new_buf+0x3f0/0xba0 binder_transaction+0x64c/0x3040 binder_thread_write+0x924/0x2020 binder_ioctl+0x1610/0x2e5c __arm64_sys_ioctl+0xd4/0x120 el0_svc_common.constprop.0+0xac/0x270 do_el0_svc+0x38/0xa0 el0_svc+0x1c/0x2c el0_sync_handler+0xe8/0x114 el0_sync+0x180/0x1c0 Allocated by task 559: kasan_save_stack+0x38/0x6c __kasan_kmalloc.constprop.0+0xe4/0xf0 kasan_slab_alloc+0x18/0x2c kmem_cache_alloc+0x1b0/0x2d0 vm_area_alloc+0x28/0x94 mmap_region+0x378/0x920 do_mmap+0x3f0/0x600 vm_mmap_pgoff+0x150/0x17c ksys_mmap_pgoff+0x284/0x2dc __arm64_sys_mmap+0x84/0xa4 el0_svc_common.constprop.0+0xac/0x270 do_el0_svc+0x38/0xa0 el0_svc+0x1c/0x2c el0_sync_handler+0xe8/0x114 el0_sync+0x180/0x1c0 Freed by task 560: kasan_save_stack+0x38/0x6c kasan_set_track+0x28/0x40 kasan_set_free_info+0x24/0x4c __kasan_slab_free+0x100/0x164 kasan_slab_free+0x14/0x20 kmem_cache_free+0xc4/0x34c vm_area_free+0x1c/0x2c remove_vma+0x7c/0x94 __do_munmap+0x358/0x710 __vm_munmap+0xbc/0x130 __arm64_sys_munmap+0x4c/0x64 el0_svc_common.constprop.0+0xac/0x270 do_el0_svc+0x38/0xa0 el0_svc+0x1c/0x2c el0_sync_handler+0xe8/0x114 el0_sync+0x180/0x1c0 [...] ================================================================== To prevent the race above, revert back to taking the mmap write lock inside binder_update_page_range(). One might expect an increase of mmap lock contention. However, binder already serializes these calls via top level alloc->mutex. Also, there was no performance impact shown when running the binder benchmark tests. Fixes: c0fd2101781e ("Revert "android: binder: stop saving a pointer to the VMA"") Fixes: dd2283f2605e ("mm: mmap: zap pages with read mmap_sem in munmap") Reported-by: Jann Horn Closes: https://lore.kernel.org/all/20230518144052.xkj6vmddccq4v66b@revolver Cc: Cc: Minchan Kim Cc: Yang Shi Cc: Liam Howlett Signed-off-by: Carlos Llamas Acked-by: Todd Kjos Link: https://lore.kernel.org/r/20230519195950.1775656-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 66e7930705b9..4fb89ef067d5 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -212,7 +212,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, mm = alloc->mm; if (mm) { - mmap_read_lock(mm); + mmap_write_lock(mm); vma = alloc->vma; } @@ -270,7 +270,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, trace_binder_alloc_page_end(alloc, index); } if (mm) { - mmap_read_unlock(mm); + mmap_write_unlock(mm); mmput(mm); } return 0; @@ -303,7 +303,7 @@ err_page_ptr_cleared: } err_no_vma: if (mm) { - mmap_read_unlock(mm); + mmap_write_unlock(mm); mmput(mm); } return vma ? -ENOMEM : -ESRCH; -- cgit 1.4.1 From 34570f85a282eb6e67f833b441be41afa02a749a Mon Sep 17 00:00:00 2001 From: Hardik Garg Date: Fri, 26 May 2023 16:21:36 -0700 Subject: selftests/memfd: Fix unknown type name build failure Partially backport v6.3 commit 11f75a01448f ("selftests/memfd: add tests for MFD_NOEXEC_SEAL MFD_EXEC") to fix an unknown type name build error. In some systems, the __u64 typedef is not present due to differences in system headers, causing compilation errors like this one: fuse_test.c:64:8: error: unknown type name '__u64' 64 | static __u64 mfd_assert_get_seals(int fd) This header includes the __u64 typedef which increases the likelihood of successful compilation on a wider variety of systems. Signed-off-by: Hardik Garg Reviewed-by: Tyler Hicks (Microsoft) Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/memfd/fuse_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/memfd/fuse_test.c b/tools/testing/selftests/memfd/fuse_test.c index be675002f918..93798c8c5d54 100644 --- a/tools/testing/selftests/memfd/fuse_test.c +++ b/tools/testing/selftests/memfd/fuse_test.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include -- cgit 1.4.1 From f3078be2febb77ed484ed59ff86ce15f49c65ddb Mon Sep 17 00:00:00 2001 From: Jack Xiao Date: Wed, 22 Mar 2023 09:31:16 +0800 Subject: drm/amd/amdgpu: limit one queue per gang commit 5ee33d905f89c18d4b33da6e5eefdae6060502df upstream. Limit one queue per gang in mes self test, due to mes schq fw change. Signed-off-by: Jack Xiao Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher Cc: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c index 7e8b7171068d..bebd136ed544 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c @@ -1328,12 +1328,9 @@ int amdgpu_mes_self_test(struct amdgpu_device *adev) struct amdgpu_mes_ctx_data ctx_data = {0}; struct amdgpu_ring *added_rings[AMDGPU_MES_CTX_MAX_RINGS] = { NULL }; int gang_ids[3] = {0}; - int queue_types[][2] = { { AMDGPU_RING_TYPE_GFX, - AMDGPU_MES_CTX_MAX_GFX_RINGS}, - { AMDGPU_RING_TYPE_COMPUTE, - AMDGPU_MES_CTX_MAX_COMPUTE_RINGS}, - { AMDGPU_RING_TYPE_SDMA, - AMDGPU_MES_CTX_MAX_SDMA_RINGS } }; + int queue_types[][2] = { { AMDGPU_RING_TYPE_GFX, 1 }, + { AMDGPU_RING_TYPE_COMPUTE, 1 }, + { AMDGPU_RING_TYPE_SDMA, 1} }; int i, r, pasid, k = 0; pasid = amdgpu_pasid_alloc(16); -- cgit 1.4.1 From ed0ef89508d26f2f1c6a034be49fe4f6747e67e7 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Mon, 8 May 2023 07:02:06 -0700 Subject: perf/x86/uncore: Correct the number of CHAs on SPR commit 38776cc45eb7603df4735a0410f42cffff8e71a1 upstream. The number of CHAs from the discovery table on some SPR variants is incorrect, because of a firmware issue. An accurate number can be read from the MSR UNC_CBO_CONFIG. Fixes: 949b11381f81 ("perf/x86/intel/uncore: Add Sapphire Rapids server CHA support") Reported-by: Stephane Eranian Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Tested-by: Stephane Eranian Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230508140206.283708-1-kan.liang@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/events/intel/uncore_snbep.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c index 8f371f3cbbd2..935081ddf60b 100644 --- a/arch/x86/events/intel/uncore_snbep.c +++ b/arch/x86/events/intel/uncore_snbep.c @@ -5822,6 +5822,7 @@ static struct intel_uncore_type spr_uncore_mdf = { }; #define UNCORE_SPR_NUM_UNCORE_TYPES 12 +#define UNCORE_SPR_CHA 0 #define UNCORE_SPR_IIO 1 #define UNCORE_SPR_IMC 6 @@ -6064,12 +6065,22 @@ static int uncore_type_max_boxes(struct intel_uncore_type **types, return max + 1; } +#define SPR_MSR_UNC_CBO_CONFIG 0x2FFE + void spr_uncore_cpu_init(void) { + struct intel_uncore_type *type; + u64 num_cbo; + uncore_msr_uncores = uncore_get_uncores(UNCORE_ACCESS_MSR, UNCORE_SPR_MSR_EXTRA_UNCORES, spr_msr_uncores); + type = uncore_find_type_by_id(uncore_msr_uncores, UNCORE_SPR_CHA); + if (type) { + rdmsrl(SPR_MSR_UNC_CBO_CONFIG, num_cbo); + type->num_boxes = num_cbo; + } spr_uncore_iio_free_running.num_boxes = uncore_type_max_boxes(uncore_msr_uncores, UNCORE_SPR_IIO); } -- cgit 1.4.1 From 4ca6b06e9be253d2a34676052ed9189c610044f7 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 23 Mar 2023 09:56:40 +0800 Subject: x86/topology: Fix erroneous smp_num_siblings on Intel Hybrid platforms commit edc0a2b5957652f4685ef3516f519f84807087db upstream. Traditionally, all CPUs in a system have identical numbers of SMT siblings. That changes with hybrid processors where some logical CPUs have a sibling and others have none. Today, the CPU boot code sets the global variable smp_num_siblings when every CPU thread is brought up. The last thread to boot will overwrite it with the number of siblings of *that* thread. That last thread to boot will "win". If the thread is a Pcore, smp_num_siblings == 2. If it is an Ecore, smp_num_siblings == 1. smp_num_siblings describes if the *system* supports SMT. It should specify the maximum number of SMT threads among all cores. Ensure that smp_num_siblings represents the system-wide maximum number of siblings by always increasing its value. Never allow it to decrease. On MeteorLake-P platform, this fixes a problem that the Ecore CPUs are not updated in any cpu sibling map because the system is treated as an UP system when probing Ecore CPUs. Below shows part of the CPU topology information before and after the fix, for both Pcore and Ecore CPU (cpu0 is Pcore, cpu 12 is Ecore). ... -/sys/devices/system/cpu/cpu0/topology/package_cpus:000fff -/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-11 +/sys/devices/system/cpu/cpu0/topology/package_cpus:3fffff +/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-21 ... -/sys/devices/system/cpu/cpu12/topology/package_cpus:001000 -/sys/devices/system/cpu/cpu12/topology/package_cpus_list:12 +/sys/devices/system/cpu/cpu12/topology/package_cpus:3fffff +/sys/devices/system/cpu/cpu12/topology/package_cpus_list:0-21 Notice that the "before" 'package_cpus_list' has only one CPU. This means that userspace tools like lscpu will see a little laptop like an 11-socket system: -Core(s) per socket: 1 -Socket(s): 11 +Core(s) per socket: 16 +Socket(s): 1 This is also expected to make the scheduler do rather wonky things too. [ dhansen: remove CPUID detail from changelog, add end user effects ] CC: stable@kernel.org Fixes: bbb65d2d365e ("x86: use cpuid vector 0xb when available for detecting cpu topology") Fixes: 95f3d39ccf7a ("x86/cpu/topology: Provide detect_extended_topology_early()") Suggested-by: Len Brown Signed-off-by: Zhang Rui Signed-off-by: Dave Hansen Acked-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/all/20230323015640.27906-1-rui.zhang%40intel.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/topology.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c index 5e868b62a7c4..0270925fe013 100644 --- a/arch/x86/kernel/cpu/topology.c +++ b/arch/x86/kernel/cpu/topology.c @@ -79,7 +79,7 @@ int detect_extended_topology_early(struct cpuinfo_x86 *c) * initial apic id, which also represents 32-bit extended x2apic id. */ c->initial_apicid = edx; - smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); + smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx)); #endif return 0; } @@ -109,7 +109,8 @@ int detect_extended_topology(struct cpuinfo_x86 *c) */ cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx); c->initial_apicid = edx; - core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); + core_level_siblings = LEVEL_MAX_SIBLINGS(ebx); + smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx)); core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); pkg_mask_width = die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); -- cgit 1.4.1 From dc1b7641a98933356f91de1b5248c87b1b869862 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Mon, 24 Apr 2023 11:31:55 +0100 Subject: irqchip/mips-gic: Don't touch vl_map if a local interrupt is not routable commit 2c6c9c049510163090b979ea5f92a68ae8d93c45 upstream. When a GIC local interrupt is not routable, it's vl_map will be used to control some internal states for core (providing IPTI, IPPCI, IPFDC input signal for core). Overriding it will interfere core's intetrupt controller. Do not touch vl_map if a local interrupt is not routable, we are not going to remap it. Before dd098a0e0319 (" irqchip/mips-gic: Get rid of the reliance on irq_cpu_online()"), if a local interrupt is not routable, then it won't be requested from GIC Local domain, and thus gic_all_vpes_irq_cpu_online won't be called for that particular interrupt. Fixes: dd098a0e0319 (" irqchip/mips-gic: Get rid of the reliance on irq_cpu_online()") Cc: stable@vger.kernel.org Signed-off-by: Jiaxun Yang Reviewed-by: Serge Semin Tested-by: Serge Semin Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230424103156.66753-2-jiaxun.yang@flygoat.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-mips-gic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 1ba0f1555c80..cc991dcce454 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -400,6 +400,8 @@ static void gic_all_vpes_irq_cpu_online(void) unsigned int intr = local_intrs[i]; struct gic_all_vpes_chip_data *cd; + if (!gic_local_irq_is_routable(intr)) + continue; cd = &gic_all_vpes_chip_data[intr]; write_gic_vl_map(mips_gic_vx_map_reg(intr), cd->map); if (cd->mask) -- cgit 1.4.1 From 8694853768e3681402a7e485efcae2c1864e2dc7 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Mon, 24 Apr 2023 11:31:56 +0100 Subject: irqchip/mips-gic: Use raw spinlock for gic_lock commit 3d6a0e4197c04599d75d85a608c8bb16a630a38c upstream. Since we may hold gic_lock in hardirq context, use raw spinlock makes more sense given that it is for low-level interrupt handling routine and the critical section is small. Fixes BUG: [ 0.426106] ============================= [ 0.426257] [ BUG: Invalid wait context ] [ 0.426422] 6.3.0-rc7-next-20230421-dirty #54 Not tainted [ 0.426638] ----------------------------- [ 0.426766] swapper/0/1 is trying to lock: [ 0.426954] ffffffff8104e7b8 (gic_lock){....}-{3:3}, at: gic_set_type+0x30/08 Fixes: 95150ae8b330 ("irqchip: mips-gic: Implement irq_set_type callback") Cc: stable@vger.kernel.org Signed-off-by: Jiaxun Yang Reviewed-by: Serge Semin Tested-by: Serge Semin Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230424103156.66753-3-jiaxun.yang@flygoat.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-mips-gic.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index cc991dcce454..3a33aefec8a4 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -50,7 +50,7 @@ void __iomem *mips_gic_base; static DEFINE_PER_CPU_READ_MOSTLY(unsigned long[GIC_MAX_LONGS], pcpu_masks); -static DEFINE_SPINLOCK(gic_lock); +static DEFINE_RAW_SPINLOCK(gic_lock); static struct irq_domain *gic_irq_domain; static int gic_shared_intrs; static unsigned int gic_cpu_pin; @@ -211,7 +211,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) irq = GIC_HWIRQ_TO_SHARED(d->hwirq); - spin_lock_irqsave(&gic_lock, flags); + raw_spin_lock_irqsave(&gic_lock, flags); switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_FALLING: pol = GIC_POL_FALLING_EDGE; @@ -251,7 +251,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) else irq_set_chip_handler_name_locked(d, &gic_level_irq_controller, handle_level_irq, NULL); - spin_unlock_irqrestore(&gic_lock, flags); + raw_spin_unlock_irqrestore(&gic_lock, flags); return 0; } @@ -269,7 +269,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, return -EINVAL; /* Assumption : cpumask refers to a single CPU */ - spin_lock_irqsave(&gic_lock, flags); + raw_spin_lock_irqsave(&gic_lock, flags); /* Re-route this IRQ */ write_gic_map_vp(irq, BIT(mips_cm_vp_id(cpu))); @@ -280,7 +280,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, set_bit(irq, per_cpu_ptr(pcpu_masks, cpu)); irq_data_update_effective_affinity(d, cpumask_of(cpu)); - spin_unlock_irqrestore(&gic_lock, flags); + raw_spin_unlock_irqrestore(&gic_lock, flags); return IRQ_SET_MASK_OK; } @@ -358,12 +358,12 @@ static void gic_mask_local_irq_all_vpes(struct irq_data *d) cd = irq_data_get_irq_chip_data(d); cd->mask = false; - spin_lock_irqsave(&gic_lock, flags); + raw_spin_lock_irqsave(&gic_lock, flags); for_each_online_cpu(cpu) { write_gic_vl_other(mips_cm_vp_id(cpu)); write_gic_vo_rmask(BIT(intr)); } - spin_unlock_irqrestore(&gic_lock, flags); + raw_spin_unlock_irqrestore(&gic_lock, flags); } static void gic_unmask_local_irq_all_vpes(struct irq_data *d) @@ -376,12 +376,12 @@ static void gic_unmask_local_irq_all_vpes(struct irq_data *d) cd = irq_data_get_irq_chip_data(d); cd->mask = true; - spin_lock_irqsave(&gic_lock, flags); + raw_spin_lock_irqsave(&gic_lock, flags); for_each_online_cpu(cpu) { write_gic_vl_other(mips_cm_vp_id(cpu)); write_gic_vo_smask(BIT(intr)); } - spin_unlock_irqrestore(&gic_lock, flags); + raw_spin_unlock_irqrestore(&gic_lock, flags); } static void gic_all_vpes_irq_cpu_online(void) @@ -394,7 +394,7 @@ static void gic_all_vpes_irq_cpu_online(void) unsigned long flags; int i; - spin_lock_irqsave(&gic_lock, flags); + raw_spin_lock_irqsave(&gic_lock, flags); for (i = 0; i < ARRAY_SIZE(local_intrs); i++) { unsigned int intr = local_intrs[i]; @@ -408,7 +408,7 @@ static void gic_all_vpes_irq_cpu_online(void) write_gic_vl_smask(BIT(intr)); } - spin_unlock_irqrestore(&gic_lock, flags); + raw_spin_unlock_irqrestore(&gic_lock, flags); } static struct irq_chip gic_all_vpes_local_irq_controller = { @@ -438,11 +438,11 @@ static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, data = irq_get_irq_data(virq); - spin_lock_irqsave(&gic_lock, flags); + raw_spin_lock_irqsave(&gic_lock, flags); write_gic_map_pin(intr, GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin); write_gic_map_vp(intr, BIT(mips_cm_vp_id(cpu))); irq_data_update_effective_affinity(data, cpumask_of(cpu)); - spin_unlock_irqrestore(&gic_lock, flags); + raw_spin_unlock_irqrestore(&gic_lock, flags); return 0; } @@ -537,12 +537,12 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, if (!gic_local_irq_is_routable(intr)) return -EPERM; - spin_lock_irqsave(&gic_lock, flags); + raw_spin_lock_irqsave(&gic_lock, flags); for_each_online_cpu(cpu) { write_gic_vl_other(mips_cm_vp_id(cpu)); write_gic_vo_map(mips_gic_vx_map_reg(intr), map); } - spin_unlock_irqrestore(&gic_lock, flags); + raw_spin_unlock_irqrestore(&gic_lock, flags); return 0; } -- cgit 1.4.1 From d7fff52c99d52f180d8bef95d8ed8fec6343889c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 11 May 2023 22:47:32 +0900 Subject: debugobjects: Don't wake up kswapd from fill_pool() commit eb799279fb1f9c63c520fe8c1c41cb9154252db6 upstream. syzbot is reporting a lockdep warning in fill_pool() because the allocation from debugobjects is using GFP_ATOMIC, which is (__GFP_HIGH | __GFP_KSWAPD_RECLAIM) and therefore tries to wake up kswapd, which acquires kswapd_wait::lock. Since fill_pool() might be called with arbitrary locks held, fill_pool() should not assume that acquiring kswapd_wait::lock is safe. Use __GFP_HIGH instead and remove __GFP_NORETRY as it is pointless for !__GFP_DIRECT_RECLAIM allocation. Fixes: 3ac7fe5a4aab ("infrastructure to debug (dynamic) objects") Reported-by: syzbot Signed-off-by: Tetsuo Handa Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/6577e1fa-b6ee-f2be-2414-a2b51b1c5e30@I-love.SAKURA.ne.jp Closes: https://syzkaller.appspot.com/bug?extid=fe0c72f0ccbb93786380 Signed-off-by: Greg Kroah-Hartman --- lib/debugobjects.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/debugobjects.c b/lib/debugobjects.c index bdfd859cccaf..c46736210363 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -126,7 +126,7 @@ static const char *obj_states[ODEBUG_STATE_MAX] = { static void fill_pool(void) { - gfp_t gfp = GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN; + gfp_t gfp = __GFP_HIGH | __GFP_NOWARN; struct debug_obj *obj; unsigned long flags; -- cgit 1.4.1 From c8fdf7feca77cd99e25ef0a1e9e72dfc83add8ef Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 19 May 2023 15:32:30 -0400 Subject: fbdev: udlfb: Fix endpoint check commit ed9de4ed39875706607fb08118a58344ae6c5f42 upstream. The syzbot fuzzer detected a problem in the udlfb driver, caused by an endpoint not having the expected type: usb 1-1: Read EDID byte 0 failed: -71 usb 1-1: Unable to get valid EDID from device/display ------------[ cut here ]------------ usb 1-1: BOGUS urb xfer, pipe 3 != type 1 WARNING: CPU: 0 PID: 9 at drivers/usb/core/urb.c:504 usb_submit_urb+0xed6/0x1880 drivers/usb/core/urb.c:504 Modules linked in: CPU: 0 PID: 9 Comm: kworker/0:1 Not tainted 6.4.0-rc1-syzkaller-00016-ga4422ff22142 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/28/2023 Workqueue: usb_hub_wq hub_event RIP: 0010:usb_submit_urb+0xed6/0x1880 drivers/usb/core/urb.c:504 ... Call Trace: dlfb_submit_urb+0x92/0x180 drivers/video/fbdev/udlfb.c:1980 dlfb_set_video_mode+0x21f0/0x2950 drivers/video/fbdev/udlfb.c:315 dlfb_ops_set_par+0x2a7/0x8d0 drivers/video/fbdev/udlfb.c:1111 dlfb_usb_probe+0x149a/0x2710 drivers/video/fbdev/udlfb.c:1743 The current approach for this issue failed to catch the problem because it only checks for the existence of a bulk-OUT endpoint; it doesn't check whether this endpoint is the one that the driver will actually use. We can fix the problem by instead checking that the endpoint used by the driver does exist and is bulk-OUT. Reported-and-tested-by: syzbot+0e22d63dcebb802b9bc8@syzkaller.appspotmail.com Signed-off-by: Alan Stern CC: Pavel Skripkin Fixes: aaf7dbe07385 ("video: fbdev: udlfb: properly check endpoint type") Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/udlfb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index 216d49c9d47e..256d9b61f4ea 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -27,6 +27,8 @@ #include page_pool_put_defragged_page+0x267/0x320 mlx5e_free_xdpsq_desc+0x99/0xd0 mlx5e_poll_xdpsq_cq+0x138/0x3b0 mlx5e_napi_poll+0xc3/0x8b0 netpoll_poll_dev+0xce/0x150 AFAIU page pool takes a BH lock, releases it and since BH is now enabled tries to run softirqs. Reviewed-by: Tariq Toukan Fixes: 60bbf7eeef10 ("mlx5: use page_pool for xdp_return_frame call") Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 9a458a5d9853..44547b22a536 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -161,20 +161,22 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) } } + /* budget=0 means we may be in IRQ context, do as little as possible */ + if (unlikely(!budget)) + goto out; + busy |= mlx5e_poll_xdpsq_cq(&c->xdpsq.cq); if (c->xdp) busy |= mlx5e_poll_xdpsq_cq(&c->rq_xdpsq.cq); - if (likely(budget)) { /* budget=0 means: don't poll rx rings */ - if (xsk_open) - work_done = mlx5e_poll_rx_cq(&xskrq->cq, budget); + if (xsk_open) + work_done = mlx5e_poll_rx_cq(&xskrq->cq, budget); - if (likely(budget - work_done)) - work_done += mlx5e_poll_rx_cq(&rq->cq, budget - work_done); + if (likely(budget - work_done)) + work_done += mlx5e_poll_rx_cq(&rq->cq, budget - work_done); - busy |= work_done == budget; - } + busy |= work_done == budget; mlx5e_poll_ico_cq(&c->icosq.cq); if (mlx5e_poll_ico_cq(&c->async_icosq.cq)) -- cgit 1.4.1 From e501ab136691c33f50b20de19b22b7ef53b409f5 Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Thu, 9 Mar 2023 16:43:15 +0200 Subject: net/mlx5: DR, Fix crc32 calculation to work on big-endian (BE) CPUs commit 1e5daf5565b61a96e570865091589afc9156e3d3 upstream. When calculating crc for hash index we use the function crc32 that calculates for little-endian (LE) arch. Then we convert it to network endianness using htonl(), but it's wrong to do the conversion in BE archs since the crc32 value is already LE. The solution is to switch the bytes from the crc result for all types of arc. Fixes: 40416d8ede65 ("net/mlx5: DR, Replace CRC32 implementation to use kernel lib") Signed-off-by: Erez Shitrit Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c index 09ebd3088857..7815a629d7e1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c @@ -15,7 +15,8 @@ static u32 dr_ste_crc32_calc(const void *input_data, size_t length) { u32 crc = crc32(0, input_data, length); - return (__force u32)htonl(crc); + return (__force u32)((crc >> 24) & 0xff) | ((crc << 8) & 0xff0000) | + ((crc >> 8) & 0xff00) | ((crc << 24) & 0xff000000); } bool mlx5dr_ste_supp_ttl_cs_recalc(struct mlx5dr_cmd_caps *caps) -- cgit 1.4.1 From b17294e7aa8c39dbb9c3e28e2d1983c88b94b387 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Mon, 20 Mar 2023 13:07:53 +0200 Subject: net/mlx5: Handle pairing of E-switch via uplink un/load APIs commit 2be5bd42a5bba1a05daedc86cf0e248210009669 upstream. In case user switch a device from switchdev mode to legacy mode, mlx5 first unpair the E-switch and afterwards unload the uplink vport. From the other hand, in case user remove or reload a device, mlx5 first unload the uplink vport and afterwards unpair the E-switch. The latter is causing a bug[1], hence, handle pairing of E-switch as part of uplink un/load APIs. [1] In case VF_LAG is used, every tc fdb flow is duplicated to the peer esw. However, the original esw keeps a pointer to this duplicated flow, not the peer esw. e.g.: if user create tc fdb flow over esw0, the flow is duplicated over esw1, in FW/HW, but in SW, esw0 keeps a pointer to the duplicated flow. During module unload while a peer tc fdb flow is still offloaded, in case the first device to be removed is the peer device (esw1 in the example above), the peer net-dev is destroyed, and so the mlx5e_priv is memset to 0. Afterwards, the peer device is trying to unpair himself from the original device (esw0 in the example above). Unpair API invoke the original device to clear peer flow from its eswitch (esw0), but the peer flow, which is stored over the original eswitch (esw0), is trying to use the peer mlx5e_priv, which is memset to 0 and result in bellow kernel-oops. [ 157.964081 ] BUG: unable to handle page fault for address: 000000000002ce60 [ 157.964662 ] #PF: supervisor read access in kernel mode [ 157.965123 ] #PF: error_code(0x0000) - not-present page [ 157.965582 ] PGD 0 P4D 0 [ 157.965866 ] Oops: 0000 [#1] SMP [ 157.967670 ] RIP: 0010:mlx5e_tc_del_fdb_flow+0x48/0x460 [mlx5_core] [ 157.976164 ] Call Trace: [ 157.976437 ] [ 157.976690 ] __mlx5e_tc_del_fdb_peer_flow+0xe6/0x100 [mlx5_core] [ 157.977230 ] mlx5e_tc_clean_fdb_peer_flows+0x67/0x90 [mlx5_core] [ 157.977767 ] mlx5_esw_offloads_unpair+0x2d/0x1e0 [mlx5_core] [ 157.984653 ] mlx5_esw_offloads_devcom_event+0xbf/0x130 [mlx5_core] [ 157.985212 ] mlx5_devcom_send_event+0xa3/0xb0 [mlx5_core] [ 157.985714 ] esw_offloads_disable+0x5a/0x110 [mlx5_core] [ 157.986209 ] mlx5_eswitch_disable_locked+0x152/0x170 [mlx5_core] [ 157.986757 ] mlx5_eswitch_disable+0x51/0x80 [mlx5_core] [ 157.987248 ] mlx5_unload+0x2a/0xb0 [mlx5_core] [ 157.987678 ] mlx5_uninit_one+0x5f/0xd0 [mlx5_core] [ 157.988127 ] remove_one+0x64/0xe0 [mlx5_core] [ 157.988549 ] pci_device_remove+0x31/0xa0 [ 157.988933 ] device_release_driver_internal+0x18f/0x1f0 [ 157.989402 ] driver_detach+0x3f/0x80 [ 157.989754 ] bus_remove_driver+0x70/0xf0 [ 157.990129 ] pci_unregister_driver+0x34/0x90 [ 157.990537 ] mlx5_cleanup+0xc/0x1c [mlx5_core] [ 157.990972 ] __x64_sys_delete_module+0x15a/0x250 [ 157.991398 ] ? exit_to_user_mode_prepare+0xea/0x110 [ 157.991840 ] do_syscall_64+0x3d/0x90 [ 157.992198 ] entry_SYSCALL_64_after_hwframe+0x46/0xb0 Fixes: 04de7dda7394 ("net/mlx5e: Infrastructure for duplicated offloading of TC flows") Fixes: 1418ddd96afd ("net/mlx5e: Duplicate offloaded TC eswitch rules under uplink LAG") Signed-off-by: Shay Drory Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4 +++- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 4 ++++ drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 7 ++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 67abd8eb9117..7883b625634f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -5143,6 +5143,8 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv) goto err_register_fib_notifier; } + mlx5_esw_offloads_devcom_init(esw); + return 0; err_register_fib_notifier: @@ -5169,7 +5171,7 @@ void mlx5e_tc_esw_cleanup(struct mlx5_rep_uplink_priv *uplink_priv) priv = netdev_priv(rpriv->netdev); esw = priv->mdev->priv.eswitch; - mlx5e_tc_clean_fdb_peer_flows(esw); + mlx5_esw_offloads_devcom_cleanup(esw); mlx5e_tc_tun_cleanup(uplink_priv->encap); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 6e6e0864063f..821c78bab373 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -368,6 +368,8 @@ int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int num_vfs); void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf); void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw); void mlx5_eswitch_disable(struct mlx5_eswitch *esw); +void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw); +void mlx5_esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw); int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, u16 vport, const u8 *mac); int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, @@ -757,6 +759,8 @@ static inline void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) {} static inline int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int num_vfs) { return 0; } static inline void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf) {} static inline void mlx5_eswitch_disable(struct mlx5_eswitch *esw) {} +static inline void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw) {} +static inline void mlx5_esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw) {} static inline bool mlx5_eswitch_is_funcs_handler(struct mlx5_core_dev *dev) { return false; } static inline int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, u16 vport, int link_state) { return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 519526a4810e..5235b5a7b963 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -2864,7 +2864,7 @@ err_out: return err; } -static void esw_offloads_devcom_init(struct mlx5_eswitch *esw) +void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw) { struct mlx5_devcom *devcom = esw->dev->priv.devcom; @@ -2887,7 +2887,7 @@ static void esw_offloads_devcom_init(struct mlx5_eswitch *esw) ESW_OFFLOADS_DEVCOM_PAIR, esw); } -static void esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw) +void mlx5_esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw) { struct mlx5_devcom *devcom = esw->dev->priv.devcom; @@ -3357,8 +3357,6 @@ int esw_offloads_enable(struct mlx5_eswitch *esw) if (err) goto err_vports; - esw_offloads_devcom_init(esw); - return 0; err_vports: @@ -3399,7 +3397,6 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw, void esw_offloads_disable(struct mlx5_eswitch *esw) { - esw_offloads_devcom_cleanup(esw); mlx5_eswitch_disable_pf_vf_vports(esw); esw_offloads_unload_rep(esw, MLX5_VPORT_UPLINK); esw_set_passing_vport_metadata(esw, false); -- cgit 1.4.1 From 59dd110ca2413104b1e0d7877e7326ce7a5ee63b Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Sun, 2 Apr 2023 17:14:10 +0300 Subject: net/mlx5: DR, Check force-loopback RC QP capability independently from RoCE commit c7dd225bc224726c22db08e680bf787f60ebdee3 upstream. SW Steering uses RC QP for writing STEs to ICM. This writingis done in LB (loopback), and FL (force-loopback) QP is preferred for performance. FL is available when RoCE is enabled or disabled based on RoCE caps. This patch adds reading of FL capability from HCA caps in addition to the existing reading from RoCE caps, thus fixing the case where we didn't have loopback enabled when RoCE was disabled. Fixes: 7304d603a57a ("net/mlx5: DR, Add support for force-loopback QP") Signed-off-by: Itamar Gozlan Signed-off-by: Yevgeny Kliteynik Signed-off-by: Saeed Mahameed Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c | 4 +++- include/linux/mlx5/mlx5_ifc.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c index 16d65fe4f654..84364691a379 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c @@ -117,6 +117,8 @@ int mlx5dr_cmd_query_device(struct mlx5_core_dev *mdev, caps->gvmi = MLX5_CAP_GEN(mdev, vhca_id); caps->flex_protocols = MLX5_CAP_GEN(mdev, flex_parser_protocols); caps->sw_format_ver = MLX5_CAP_GEN(mdev, steering_format_version); + caps->roce_caps.fl_rc_qp_when_roce_disabled = + MLX5_CAP_GEN(mdev, fl_rc_qp_when_roce_disabled); if (MLX5_CAP_GEN(mdev, roce)) { err = dr_cmd_query_nic_vport_roce_en(mdev, 0, &roce_en); @@ -124,7 +126,7 @@ int mlx5dr_cmd_query_device(struct mlx5_core_dev *mdev, return err; caps->roce_caps.roce_en = roce_en; - caps->roce_caps.fl_rc_qp_when_roce_disabled = + caps->roce_caps.fl_rc_qp_when_roce_disabled |= MLX5_CAP_ROCE(mdev, fl_rc_qp_when_roce_disabled); caps->roce_caps.fl_rc_qp_when_roce_enabled = MLX5_CAP_ROCE(mdev, fl_rc_qp_when_roce_enabled); diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 097cbf84c1e0..b2aee17a34d7 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -1639,7 +1639,9 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 rc[0x1]; u8 uar_4k[0x1]; - u8 reserved_at_241[0x9]; + u8 reserved_at_241[0x7]; + u8 fl_rc_qp_when_roce_disabled[0x1]; + u8 regexp_params[0x1]; u8 uar_sz[0x6]; u8 port_selection_cap[0x1]; u8 reserved_at_248[0x1]; -- cgit 1.4.1 From 390aa5c006b3d1a10b8306f120bd6884f5b384d1 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Mon, 1 May 2023 14:37:56 +0300 Subject: net/mlx5: Fix error message when failing to allocate device memory commit a65735148e0328f80c0f72f9f8d2f609bfcf4aff upstream. Fix spacing for the error and also the correct error code pointer. Fixes: c9b9dcb430b3 ("net/mlx5: Move device memory management to mlx5_core") Signed-off-by: Roi Dayan Signed-off-by: Saeed Mahameed Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index cc8057c4f908..91724c5450a0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1024,7 +1024,7 @@ static int mlx5_init_once(struct mlx5_core_dev *dev) dev->dm = mlx5_dm_create(dev); if (IS_ERR(dev->dm)) - mlx5_core_warn(dev, "Failed to init device memory%d\n", err); + mlx5_core_warn(dev, "Failed to init device memory %ld\n", PTR_ERR(dev->dm)); dev->tracer = mlx5_fw_tracer_create(dev); dev->hv_vhca = mlx5_hv_vhca_create(dev); -- cgit 1.4.1 From 411e4d6caa7f7169192b8dacc8421ac4fd64a354 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 2 May 2023 11:03:53 +0300 Subject: net/mlx5: Collect command failures data only for known commands commit 2a0a935fb64ee8af253b9c6133bb6702fb152ac2 upstream. DEVX can issue a general command, which is not used by mlx5 driver. In case such command is failed, mlx5 is trying to collect the failure data, However, mlx5 doesn't create a storage for this command, since mlx5 doesn't use it. This lead to array-index-out-of-bounds error. Fix it by checking whether the command is known before collecting the failure data. Fixes: 34f46ae0d4b3 ("net/mlx5: Add command failures data to debugfs") Signed-off-by: Shay Drory Signed-off-by: Saeed Mahameed Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 723891eb86ee..b3253e263ebc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1894,9 +1894,10 @@ static void mlx5_cmd_err_trace(struct mlx5_core_dev *dev, u16 opcode, u16 op_mod static void cmd_status_log(struct mlx5_core_dev *dev, u16 opcode, u8 status, u32 syndrome, int err) { + const char *namep = mlx5_command_str(opcode); struct mlx5_cmd_stats *stats; - if (!err) + if (!err || !(strcmp(namep, "unknown command opcode"))) return; stats = &dev->cmd.stats[opcode]; -- cgit 1.4.1 From eaa365c10459052cbe3e44caa4ad760cb93bd435 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 2 May 2023 13:35:11 +0300 Subject: net/mlx5: Devcom, fix error flow in mlx5_devcom_register_device commit af87194352cad882d787d06fb7efa714acd95427 upstream. In case devcom allocation is failed, mlx5 is always freeing the priv. However, this priv might have been allocated by a different thread, and freeing it might lead to use-after-free bugs. Fix it by freeing the priv only in case it was allocated by the running thread. Fixes: fadd59fc50d0 ("net/mlx5: Introduce inter-device communication mechanism") Signed-off-by: Shay Drory Signed-off-by: Saeed Mahameed Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c index 070d55f13419..8f978491dd32 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c @@ -112,7 +112,8 @@ struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev) priv->devs[idx] = dev; devcom = mlx5_devcom_alloc(priv, idx); if (!devcom) { - kfree(priv); + if (new_priv) + kfree(priv); return ERR_PTR(-ENOMEM); } -- cgit 1.4.1 From 3e8a82fb55a640222bd873a2a1a3ff18ff917783 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 2 May 2023 13:36:42 +0300 Subject: net/mlx5: Devcom, serialize devcom registration commit 1f893f57a3bf9fe1f4bcb25b55aea7f7f9712fe7 upstream. From one hand, mlx5 driver is allowing to probe PFs in parallel. From the other hand, devcom, which is a share resource between PFs, is registered without any lock. This might resulted in memory problems. Hence, use the global mlx5_dev_list_lock in order to serialize devcom registration. Fixes: fadd59fc50d0 ("net/mlx5: Introduce inter-device communication mechanism") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c index 8f978491dd32..b7d779d08d83 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c @@ -3,6 +3,7 @@ #include #include "lib/devcom.h" +#include "mlx5_core.h" static LIST_HEAD(devcom_list); @@ -77,6 +78,7 @@ struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev) if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED) return NULL; + mlx5_dev_list_lock(); sguid0 = mlx5_query_nic_system_image_guid(dev); list_for_each_entry(iter, &devcom_list, list) { struct mlx5_core_dev *tmp_dev = NULL; @@ -102,8 +104,10 @@ struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev) if (!priv) { priv = mlx5_devcom_list_alloc(); - if (!priv) - return ERR_PTR(-ENOMEM); + if (!priv) { + devcom = ERR_PTR(-ENOMEM); + goto out; + } idx = 0; new_priv = true; @@ -114,12 +118,14 @@ struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev) if (!devcom) { if (new_priv) kfree(priv); - return ERR_PTR(-ENOMEM); + devcom = ERR_PTR(-ENOMEM); + goto out; } if (new_priv) list_add(&priv->list, &devcom_list); - +out: + mlx5_dev_list_unlock(); return devcom; } @@ -132,6 +138,7 @@ void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom) if (IS_ERR_OR_NULL(devcom)) return; + mlx5_dev_list_lock(); priv = devcom->priv; priv->devs[devcom->idx] = NULL; @@ -142,10 +149,12 @@ void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom) break; if (i != MLX5_DEVCOM_PORTS_SUPPORTED) - return; + goto out; list_del(&priv->list); kfree(priv); +out: + mlx5_dev_list_unlock(); } void mlx5_devcom_register_component(struct mlx5_devcom *devcom, -- cgit 1.4.1 From 1ae70faa86fd0fee482895463471e62251681bab Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Mon, 1 May 2023 13:05:32 -0400 Subject: arm64: dts: imx8mn-var-som: fix PHY detection bug by adding deassert delay commit f161cea5a20f3aeeb637a88ad1705fc2720b4d58 upstream. While testing the ethernet interface on a Variscite symphony carrier board using an imx8mn SOM with an onboard ADIN1300 PHY (EC hardware configuration), the ethernet PHY is not detected. The ADIN1300 datasheet indicate that the "Management interface active (t4)" state is reached at most 5ms after the reset signal is deasserted. The device tree in Variscite custom git repository uses the following property: phy-reset-post-delay = <20>; Add a new MDIO property 'reset-deassert-us' of 20ms to have the same delay inside the ethphy node. Adding this property fixes the problem with the PHY detection. Note that this SOM can also have an Atheros AR8033 PHY. In this case, a 1ms deassert delay is sufficient. Add a comment to that effect. Fixes: ade0176dd8a0 ("arm64: dts: imx8mn-var-som: Add Variscite VAR-SOM-MX8MN System on Module") Signed-off-by: Hugo Villeneuve Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi index 87b5e23c766f..d053ef302fb8 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi @@ -98,11 +98,17 @@ #address-cells = <1>; #size-cells = <0>; - ethphy: ethernet-phy@4 { + ethphy: ethernet-phy@4 { /* AR8033 or ADIN1300 */ compatible = "ethernet-phy-ieee802.3-c22"; reg = <4>; reset-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; + /* + * Deassert delay: + * ADIN1300 requires 5ms. + * AR8033 requires 1ms. + */ + reset-deassert-us = <20000>; }; }; }; -- cgit 1.4.1 From 22157f74458106c5900dbd938eb44623c22d97a0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 3 May 2023 14:12:52 +0100 Subject: firmware: arm_ffa: Set reserved/MBZ fields to zero in the memory descriptors commit 111a833dc5cbef3d05b2a796a7e23cb7f6ff2192 upstream. The transmit buffers allocated by the driver can be used to transmit data by any messages/commands needing the buffer. However, it is not guaranteed to have been zero-ed before every new transmission and hence it will just contain residual value from the previous transmission. There are several reserved fields in the memory descriptors that must be zero(MBZ). The receiver can reject the transmission if any such MBZ fields are non-zero. While we can set the whole page to zero, it is not optimal as most of the fields get initialised to the value required for the current transmission. So, just set the reserved/MBZ fields to zero in the memory descriptors explicitly to honour the requirement and keep the receiver happy. Fixes: cc2195fe536c ("firmware: arm_ffa: Add support for MEM_* interfaces") Reported-by: Marc Bonnici Link: https://lore.kernel.org/r/20230503131252.12585-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/arm_ffa/driver.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index d5e86ef40b89..737f36e7a903 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -501,12 +501,17 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, ep_mem_access->receiver = args->attrs[idx].receiver; ep_mem_access->attrs = args->attrs[idx].attrs; ep_mem_access->composite_off = COMPOSITE_OFFSET(args->nattrs); + ep_mem_access->flag = 0; + ep_mem_access->reserved = 0; } + mem_region->reserved_0 = 0; + mem_region->reserved_1 = 0; mem_region->ep_count = args->nattrs; composite = buffer + COMPOSITE_OFFSET(args->nattrs); composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg); composite->addr_range_cnt = num_entries; + composite->reserved = 0; length = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, num_entries); frag_len = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, 0); @@ -541,6 +546,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, constituents->address = sg_phys(args->sg); constituents->pg_cnt = args->sg->length / FFA_PAGE_SIZE; + constituents->reserved = 0; constituents++; frag_len += sizeof(struct ffa_mem_region_addr_range); } while ((args->sg = sg_next(args->sg))); -- cgit 1.4.1 From be4022669e66ffdae7602240b2b55799e6e7cd0e Mon Sep 17 00:00:00 2001 From: Sen Chu Date: Thu, 18 May 2023 12:06:46 +0800 Subject: regulator: mt6359: add read check for PMIC MT6359 commit a511637502b1caa135046d0f8fdabd55a31af8ef upstream. Add hardware version read check for PMIC MT6359 Signed-off-by: Sen Chu --- drivers/regulator/mt6359-regulator.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/mt6359-regulator.c b/drivers/regulator/mt6359-regulator.c index de3b0462832c..f94f87c5407a 100644 --- a/drivers/regulator/mt6359-regulator.c +++ b/drivers/regulator/mt6359-regulator.c @@ -951,9 +951,12 @@ static int mt6359_regulator_probe(struct platform_device *pdev) struct regulator_config config = {}; struct regulator_dev *rdev; struct mt6359_regulator_info *mt6359_info; - int i, hw_ver; + int i, hw_ver, ret; + + ret = regmap_read(mt6397->regmap, MT6359P_HWCID, &hw_ver); + if (ret) + return ret; - regmap_read(mt6397->regmap, MT6359P_HWCID, &hw_ver); if (hw_ver >= MT6359P_CHIP_VER) mt6359_info = mt6359p_regulators; else -- cgit 1.4.1 From 9540765d1882d15497d880096de99fafabcfa08c Mon Sep 17 00:00:00 2001 From: Wen Gu Date: Thu, 18 May 2023 13:14:55 +0800 Subject: net/smc: Reset connection when trying to use SMCRv2 fails. commit 35112271672ae98f45df7875244a4e33aa215e31 upstream. We found a crash when using SMCRv2 with 2 Mellanox ConnectX-4. It can be reproduced by: - smc_run nginx - smc_run wrk -t 32 -c 500 -d 30 http://: BUG: kernel NULL pointer dereference, address: 0000000000000014 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 8000000108713067 P4D 8000000108713067 PUD 151127067 PMD 0 Oops: 0000 [#1] PREEMPT SMP PTI CPU: 4 PID: 2441 Comm: kworker/4:249 Kdump: loaded Tainted: G W E 6.4.0-rc1+ #42 Workqueue: smc_hs_wq smc_listen_work [smc] RIP: 0010:smc_clc_send_confirm_accept+0x284/0x580 [smc] RSP: 0018:ffffb8294b2d7c78 EFLAGS: 00010a06 RAX: ffff8f1873238880 RBX: ffffb8294b2d7dc8 RCX: 0000000000000000 RDX: 00000000000000b4 RSI: 0000000000000001 RDI: 0000000000b40c00 RBP: ffffb8294b2d7db8 R08: ffff8f1815c5860c R09: 0000000000000000 R10: 0000000000000400 R11: 0000000000000000 R12: ffff8f1846f56180 R13: ffff8f1815c5860c R14: 0000000000000001 R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff8f1aefd00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000014 CR3: 00000001027a0001 CR4: 00000000003706e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: ? mlx5_ib_map_mr_sg+0xa1/0xd0 [mlx5_ib] ? smcr_buf_map_link+0x24b/0x290 [smc] ? __smc_buf_create+0x4ee/0x9b0 [smc] smc_clc_send_accept+0x4c/0xb0 [smc] smc_listen_work+0x346/0x650 [smc] ? __schedule+0x279/0x820 process_one_work+0x1e5/0x3f0 worker_thread+0x4d/0x2f0 ? __pfx_worker_thread+0x10/0x10 kthread+0xe5/0x120 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x2c/0x50 During the CLC handshake, server sequentially tries available SMCRv2 and SMCRv1 devices in smc_listen_work(). If an SMCRv2 device is found. SMCv2 based link group and link will be assigned to the connection. Then assumed that some buffer assignment errors happen later in the CLC handshake, such as RMB registration failure, server will give up SMCRv2 and try SMCRv1 device instead. But the resources assigned to the connection won't be reset. When server tries SMCRv1 device, the connection creation process will be executed again. Since conn->lnk has been assigned when trying SMCRv2, it will not be set to the correct SMCRv1 link in smcr_lgr_conn_assign_link(). So in such situation, conn->lgr points to correct SMCRv1 link group but conn->lnk points to the SMCRv2 link mistakenly. Then in smc_clc_send_confirm_accept(), conn->rmb_desc->mr[link->link_idx] will be accessed. Since the link->link_idx is not correct, the related MR may not have been initialized, so crash happens. | Try SMCRv2 device first | |-> conn->lgr: assign existed SMCRv2 link group; | |-> conn->link: assign existed SMCRv2 link (link_idx may be 1 in SMC_LGR_SYMMETRIC); | |-> sndbuf & RMB creation fails, quit; | | Try SMCRv1 device then | |-> conn->lgr: create SMCRv1 link group and assign; | |-> conn->link: keep SMCRv2 link mistakenly; | |-> sndbuf & RMB creation succeed, only RMB->mr[link_idx = 0] | initialized. | | Then smc_clc_send_confirm_accept() accesses | conn->rmb_desc->mr[conn->link->link_idx, which is 1], then crash. v This patch tries to fix this by cleaning conn->lnk before assigning link. In addition, it is better to reset the connection and clean the resources assigned if trying SMCRv2 failed in buffer creation or registration. Fixes: e49300a6bf62 ("net/smc: add listen processing for SMC-Rv2") Link: https://lore.kernel.org/r/20220523055056.2078994-1-liuyacan@corp.netease.com/ Signed-off-by: Wen Gu Reviewed-by: Tony Lu Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/smc/af_smc.c | 9 +++++++-- net/smc/smc_core.c | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index e8018b0fb767..02d1daae7739 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1987,8 +1987,10 @@ static int smc_listen_rdma_init(struct smc_sock *new_smc, return rc; /* create send buffer and rmb */ - if (smc_buf_create(new_smc, false)) + if (smc_buf_create(new_smc, false)) { + smc_conn_abort(new_smc, ini->first_contact_local); return SMC_CLC_DECL_MEM; + } return 0; } @@ -2204,8 +2206,11 @@ static void smc_find_rdma_v2_device_serv(struct smc_sock *new_smc, smcr_version = ini->smcr_version; ini->smcr_version = SMC_V2; rc = smc_listen_rdma_init(new_smc, ini); - if (!rc) + if (!rc) { rc = smc_listen_rdma_reg(new_smc, ini->first_contact_local); + if (rc) + smc_conn_abort(new_smc, ini->first_contact_local); + } if (!rc) return; ini->smcr_version = smcr_version; diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 0208dfb35345..f82f43573a15 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -127,6 +127,7 @@ static int smcr_lgr_conn_assign_link(struct smc_connection *conn, bool first) int i, j; /* do link balancing */ + conn->lnk = NULL; /* reset conn->lnk first */ for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { struct smc_link *lnk = &conn->lgr->lnk[i]; -- cgit 1.4.1 From 3bcb97e4241b71ae24a78816f9000fab3d8c45d2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 May 2023 11:48:55 +0200 Subject: 3c589_cs: Fix an error handling path in tc589_probe() commit 640bf95b2c7c2981fb471acdafbd3e0458f8390d upstream. Should tc589_config() fail, some resources need to be released as already done in the remove function. Fixes: 15b99ac17295 ("[PATCH] pcmcia: add return value to _config() functions") Signed-off-by: Christophe JAILLET Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/d8593ae867b24c79063646e36f9b18b0790107cb.1684575975.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/3com/3c589_cs.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index 82f94b1635bf..5267e9dcd87e 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -195,6 +195,7 @@ static int tc589_probe(struct pcmcia_device *link) { struct el3_private *lp; struct net_device *dev; + int ret; dev_dbg(&link->dev, "3c589_attach()\n"); @@ -218,7 +219,15 @@ static int tc589_probe(struct pcmcia_device *link) dev->ethtool_ops = &netdev_ethtool_ops; - return tc589_config(link); + ret = tc589_config(link); + if (ret) + goto err_free_netdev; + + return 0; + +err_free_netdev: + free_netdev(dev); + return ret; } static void tc589_detach(struct pcmcia_device *link) -- cgit 1.4.1 From 2f32b89d81208091aedf52fd176d3d8a8eea2fbf Mon Sep 17 00:00:00 2001 From: David Epping Date: Tue, 23 May 2023 17:31:05 +0200 Subject: net: phy: mscc: add VSC8502 to MODULE_DEVICE_TABLE commit 57fb54ab9f6945e204740b696bd4cee61ee04e5e upstream. The mscc driver implements support for VSC8502, so its ID should be in the MODULE_DEVICE_TABLE for automatic loading. Signed-off-by: David Epping Fixes: d3169863310d ("net: phy: mscc: add support for VSC8502") Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/mscc/mscc_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index 8a13b1ad9a33..f778e4f8b508 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -2664,6 +2664,7 @@ static struct phy_driver vsc85xx_driver[] = { module_phy_driver(vsc85xx_driver); static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = { + { PHY_ID_VSC8502, 0xfffffff0, }, { PHY_ID_VSC8504, 0xfffffff0, }, { PHY_ID_VSC8514, 0xfffffff0, }, { PHY_ID_VSC8530, 0xfffffff0, }, -- cgit 1.4.1 From d2869ace6eeb8ea8a6e70e6904524c5a6456d3fb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 30 May 2023 14:03:33 +0100 Subject: Linux 6.1.31 Link: https://lore.kernel.org/r/20230528190835.386670951@linuxfoundation.org Tested-by: Bagas Sanjaya Tested-by: Takeshi Ogasawara Tested-by: Conor Dooley Tested-by: Markus Reichelt Tested-by: Linux Kernel Functional Testing Tested-by: Guenter Roeck Tested-by: Ron Economos Tested-by: Jon Hunter Tested-by: Chris Paterson (CIP) Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index aeb58c7a4baa..902a6b598c73 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 30 +SUBLEVEL = 31 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From 6728486447eea6a58146840b66b43cc791e6a7e4 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 24 Jan 2023 14:36:43 +0100 Subject: inet: Add IP_LOCAL_PORT_RANGE socket option [ Upstream commit 91d0b78c5177f3e42a4d8738af8ac19c3a90d002 ] Users who want to share a single public IP address for outgoing connections between several hosts traditionally reach for SNAT. However, SNAT requires state keeping on the node(s) performing the NAT. A stateless alternative exists, where a single IP address used for egress can be shared between several hosts by partitioning the available ephemeral port range. In such a setup: 1. Each host gets assigned a disjoint range of ephemeral ports. 2. Applications open connections from the host-assigned port range. 3. Return traffic gets routed to the host based on both, the destination IP and the destination port. An application which wants to open an outgoing connection (connect) from a given port range today can choose between two solutions: 1. Manually pick the source port by bind()'ing to it before connect()'ing the socket. This approach has a couple of downsides: a) Search for a free port has to be implemented in the user-space. If the chosen 4-tuple happens to be busy, the application needs to retry from a different local port number. Detecting if 4-tuple is busy can be either easy (TCP) or hard (UDP). In TCP case, the application simply has to check if connect() returned an error (EADDRNOTAVAIL). That is assuming that the local port sharing was enabled (REUSEADDR) by all the sockets. # Assume desired local port range is 60_000-60_511 s = socket(AF_INET, SOCK_STREAM) s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) s.bind(("192.0.2.1", 60_000)) s.connect(("1.1.1.1", 53)) # Fails only if 192.0.2.1:60000 -> 1.1.1.1:53 is busy # Application must retry with another local port In case of UDP, the network stack allows binding more than one socket to the same 4-tuple, when local port sharing is enabled (REUSEADDR). Hence detecting the conflict is much harder and involves querying sock_diag and toggling the REUSEADDR flag [1]. b) For TCP, bind()-ing to a port within the ephemeral port range means that no connecting sockets, that is those which leave it to the network stack to find a free local port at connect() time, can use the this port. IOW, the bind hash bucket tb->fastreuse will be 0 or 1, and the port will be skipped during the free port search at connect() time. 2. Isolate the app in a dedicated netns and use the use the per-netns ip_local_port_range sysctl to adjust the ephemeral port range bounds. The per-netns setting affects all sockets, so this approach can be used only if: - there is just one egress IP address, or - the desired egress port range is the same for all egress IP addresses used by the application. For TCP, this approach avoids the downsides of (1). Free port search and 4-tuple conflict detection is done by the network stack: system("sysctl -w net.ipv4.ip_local_port_range='60000 60511'") s = socket(AF_INET, SOCK_STREAM) s.setsockopt(SOL_IP, IP_BIND_ADDRESS_NO_PORT, 1) s.bind(("192.0.2.1", 0)) s.connect(("1.1.1.1", 53)) # Fails if all 4-tuples 192.0.2.1:60000-60511 -> 1.1.1.1:53 are busy For UDP this approach has limited applicability. Setting the IP_BIND_ADDRESS_NO_PORT socket option does not result in local source port being shared with other connected UDP sockets. Hence relying on the network stack to find a free source port, limits the number of outgoing UDP flows from a single IP address down to the number of available ephemeral ports. To put it another way, partitioning the ephemeral port range between hosts using the existing Linux networking API is cumbersome. To address this use case, add a new socket option at the SOL_IP level, named IP_LOCAL_PORT_RANGE. The new option can be used to clamp down the ephemeral port range for each socket individually. The option can be used only to narrow down the per-netns local port range. If the per-socket range lies outside of the per-netns range, the latter takes precedence. UAPI-wise, the low and high range bounds are passed to the kernel as a pair of u16 values in host byte order packed into a u32. This avoids pointer passing. PORT_LO = 40_000 PORT_HI = 40_511 s = socket(AF_INET, SOCK_STREAM) v = struct.pack("I", PORT_HI << 16 | PORT_LO) s.setsockopt(SOL_IP, IP_LOCAL_PORT_RANGE, v) s.bind(("127.0.0.1", 0)) s.getsockname() # Local address between ("127.0.0.1", 40_000) and ("127.0.0.1", 40_511), # if there is a free port. EADDRINUSE otherwise. [1] https://github.com/cloudflare/cloudflare-blog/blob/232b432c1d57/2022-02-connectx/connectx.py#L116 Reviewed-by: Marek Majkowski Reviewed-by: Kuniyuki Iwashima Signed-off-by: Jakub Sitnicki Reviewed-by: Eric Dumazet Signed-off-by: Jakub Kicinski Stable-dep-of: 3632679d9e4f ("ipv{4,6}/raw: fix output xfrm lookup wrt protocol") Signed-off-by: Sasha Levin --- include/net/inet_sock.h | 4 ++++ include/net/ip.h | 3 ++- include/uapi/linux/in.h | 1 + net/ipv4/inet_connection_sock.c | 25 +++++++++++++++++++++++-- net/ipv4/inet_hashtables.c | 2 +- net/ipv4/ip_sockglue.c | 18 ++++++++++++++++++ net/ipv4/udp.c | 2 +- net/sctp/socket.c | 2 +- 8 files changed, 51 insertions(+), 6 deletions(-) diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index bf5654ce711e..51857117ac09 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -249,6 +249,10 @@ struct inet_sock { __be32 mc_addr; struct ip_mc_socklist __rcu *mc_list; struct inet_cork_full cork; + struct { + __u16 lo; + __u16 hi; + } local_port_range; }; #define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */ diff --git a/include/net/ip.h b/include/net/ip.h index 144bdfbb25af..c3fffaa92d6e 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -340,7 +340,8 @@ static inline u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_o } \ } -void inet_get_local_port_range(struct net *net, int *low, int *high); +void inet_get_local_port_range(const struct net *net, int *low, int *high); +void inet_sk_get_local_port_range(const struct sock *sk, int *low, int *high); #ifdef CONFIG_SYSCTL static inline bool inet_is_local_reserved_port(struct net *net, unsigned short port) diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index 07a4cb149305..4b7f2df66b99 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -162,6 +162,7 @@ struct in_addr { #define MCAST_MSFILTER 48 #define IP_MULTICAST_ALL 49 #define IP_UNICAST_IF 50 +#define IP_LOCAL_PORT_RANGE 51 #define MCAST_EXCLUDE 0 #define MCAST_INCLUDE 1 diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7152ede18f11..916075e00d06 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -117,7 +117,7 @@ bool inet_rcv_saddr_any(const struct sock *sk) return !sk->sk_rcv_saddr; } -void inet_get_local_port_range(struct net *net, int *low, int *high) +void inet_get_local_port_range(const struct net *net, int *low, int *high) { unsigned int seq; @@ -130,6 +130,27 @@ void inet_get_local_port_range(struct net *net, int *low, int *high) } EXPORT_SYMBOL(inet_get_local_port_range); +void inet_sk_get_local_port_range(const struct sock *sk, int *low, int *high) +{ + const struct inet_sock *inet = inet_sk(sk); + const struct net *net = sock_net(sk); + int lo, hi, sk_lo, sk_hi; + + inet_get_local_port_range(net, &lo, &hi); + + sk_lo = inet->local_port_range.lo; + sk_hi = inet->local_port_range.hi; + + if (unlikely(lo <= sk_lo && sk_lo <= hi)) + lo = sk_lo; + if (unlikely(lo <= sk_hi && sk_hi <= hi)) + hi = sk_hi; + + *low = lo; + *high = hi; +} +EXPORT_SYMBOL(inet_sk_get_local_port_range); + static bool inet_use_bhash2_on_bind(const struct sock *sk) { #if IS_ENABLED(CONFIG_IPV6) @@ -316,7 +337,7 @@ inet_csk_find_open_port(const struct sock *sk, struct inet_bind_bucket **tb_ret, ports_exhausted: attempt_half = (sk->sk_reuse == SK_CAN_REUSE) ? 1 : 0; other_half_scan: - inet_get_local_port_range(net, &low, &high); + inet_sk_get_local_port_range(sk, &low, &high); high++; /* [32768, 60999] -> [32768, 61000[ */ if (high - low < 4) attempt_half = 0; diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index f0750c06d5ff..e8734ffca85a 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -1022,7 +1022,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, l3mdev = inet_sk_bound_l3mdev(sk); - inet_get_local_port_range(net, &low, &high); + inet_sk_get_local_port_range(sk, &low, &high); high++; /* [32768, 60999] -> [32768, 61000[ */ remaining = high - low; if (likely(remaining > 1)) diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 6e19cad154f5..d05f631ea640 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -922,6 +922,7 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, case IP_CHECKSUM: case IP_RECVFRAGSIZE: case IP_RECVERR_RFC4884: + case IP_LOCAL_PORT_RANGE: if (optlen >= sizeof(int)) { if (copy_from_sockptr(&val, optval, sizeof(val))) return -EFAULT; @@ -1364,6 +1365,20 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, WRITE_ONCE(inet->min_ttl, val); break; + case IP_LOCAL_PORT_RANGE: + { + const __u16 lo = val; + const __u16 hi = val >> 16; + + if (optlen != sizeof(__u32)) + goto e_inval; + if (lo != 0 && hi != 0 && lo > hi) + goto e_inval; + + inet->local_port_range.lo = lo; + inet->local_port_range.hi = hi; + break; + } default: err = -ENOPROTOOPT; break; @@ -1742,6 +1757,9 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_MINTTL: val = inet->min_ttl; break; + case IP_LOCAL_PORT_RANGE: + val = inet->local_port_range.hi << 16 | inet->local_port_range.lo; + break; default: sockopt_release_sock(sk); return -ENOPROTOOPT; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 2eaf47e23b22..3ffa30c37293 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -243,7 +243,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, int low, high, remaining; unsigned int rand; - inet_get_local_port_range(net, &low, &high); + inet_sk_get_local_port_range(sk, &low, &high); remaining = (high - low) + 1; rand = get_random_u32(); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 17185200079d..bc3d08bd7cef 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -8325,7 +8325,7 @@ static int sctp_get_port_local(struct sock *sk, union sctp_addr *addr) int low, high, remaining, index; unsigned int rover; - inet_get_local_port_range(net, &low, &high); + inet_sk_get_local_port_range(sk, &low, &high); remaining = (high - low) + 1; rover = prandom_u32_max(remaining) + low; -- cgit 1.4.1 From 3f5413c95445f6a2c0e6b1ea5e1409dd653f04ee Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Mon, 22 May 2023 14:08:20 +0200 Subject: ipv{4,6}/raw: fix output xfrm lookup wrt protocol [ Upstream commit 3632679d9e4f879f49949bb5b050e0de553e4739 ] With a raw socket bound to IPPROTO_RAW (ie with hdrincl enabled), the protocol field of the flow structure, build by raw_sendmsg() / rawv6_sendmsg()), is set to IPPROTO_RAW. This breaks the ipsec policy lookup when some policies are defined with a protocol in the selector. For ipv6, the sin6_port field from 'struct sockaddr_in6' could be used to specify the protocol. Just accept all values for IPPROTO_RAW socket. For ipv4, the sin_port field of 'struct sockaddr_in' could not be used without breaking backward compatibility (the value of this field was never checked). Let's add a new kind of control message, so that the userland could specify which protocol is used. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") CC: stable@vger.kernel.org Signed-off-by: Nicolas Dichtel Link: https://lore.kernel.org/r/20230522120820.1319391-1-nicolas.dichtel@6wind.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/ip.h | 2 ++ include/uapi/linux/in.h | 1 + net/ipv4/ip_sockglue.c | 12 +++++++++++- net/ipv4/raw.c | 5 ++++- net/ipv6/raw.c | 3 ++- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/net/ip.h b/include/net/ip.h index c3fffaa92d6e..acec504c469a 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -76,6 +76,7 @@ struct ipcm_cookie { __be32 addr; int oif; struct ip_options_rcu *opt; + __u8 protocol; __u8 ttl; __s16 tos; char priority; @@ -96,6 +97,7 @@ static inline void ipcm_init_sk(struct ipcm_cookie *ipcm, ipcm->sockc.tsflags = inet->sk.sk_tsflags; ipcm->oif = READ_ONCE(inet->sk.sk_bound_dev_if); ipcm->addr = inet->inet_saddr; + ipcm->protocol = inet->inet_num; } #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb)) diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index 4b7f2df66b99..e682ab628dfa 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -163,6 +163,7 @@ struct in_addr { #define IP_MULTICAST_ALL 49 #define IP_UNICAST_IF 50 #define IP_LOCAL_PORT_RANGE 51 +#define IP_PROTOCOL 52 #define MCAST_EXCLUDE 0 #define MCAST_INCLUDE 1 diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index d05f631ea640..a7fd035b5b4f 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -317,7 +317,14 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, ipc->tos = val; ipc->priority = rt_tos2priority(ipc->tos); break; - + case IP_PROTOCOL: + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) + return -EINVAL; + val = *(int *)CMSG_DATA(cmsg); + if (val < 1 || val > 255) + return -EINVAL; + ipc->protocol = val; + break; default: return -EINVAL; } @@ -1760,6 +1767,9 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_LOCAL_PORT_RANGE: val = inet->local_port_range.hi << 16 | inet->local_port_range.lo; break; + case IP_PROTOCOL: + val = inet_sk(sk)->inet_num; + break; default: sockopt_release_sock(sk); return -ENOPROTOOPT; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index af03aa8a8e51..86197634dcf5 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -530,6 +530,9 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } ipcm_init_sk(&ipc, inet); + /* Keep backward compat */ + if (hdrincl) + ipc.protocol = IPPROTO_RAW; if (msg->msg_controllen) { err = ip_cmsg_send(sk, msg, &ipc, false); @@ -597,7 +600,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos, RT_SCOPE_UNIVERSE, - hdrincl ? IPPROTO_RAW : sk->sk_protocol, + hdrincl ? ipc.protocol : sk->sk_protocol, inet_sk_flowi_flags(sk) | (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), daddr, saddr, 0, 0, sk->sk_uid); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index f44b99f7ecdc..33852fc38ad9 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -791,7 +791,8 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) if (!proto) proto = inet->inet_num; - else if (proto != inet->inet_num) + else if (proto != inet->inet_num && + inet->inet_num != IPPROTO_RAW) return -EINVAL; if (proto > 255) -- cgit 1.4.1 From ca39992f104860f60be2ad3d510e031db207089e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 20 Apr 2023 16:06:02 +0100 Subject: firmware: arm_ffa: Fix usage of partition info get count flag [ Upstream commit c6e045361a27ecd4fac6413164e0d091d80eee99 ] Commit bb1be7498500 ("firmware: arm_ffa: Add v1.1 get_partition_info support") adds support to discovery the UUIDs of the partitions or just fetch the partition count using the PARTITION_INFO_GET_RETURN_COUNT_ONLY flag. However the commit doesn't handle the fact that the older version doesn't understand the flag and must be MBZ which results in firmware returning invalid parameter error. That results in the failure of the driver probe which is in correct. Limit the usage of the PARTITION_INFO_GET_RETURN_COUNT_ONLY flag for the versions above v1.0(i.e v1.1 and onwards) which fixes the issue. Fixes: bb1be7498500 ("firmware: arm_ffa: Add v1.1 get_partition_info support") Reported-by: Jens Wiklander Reported-by: Marc Bonnici Tested-by: Jens Wiklander Reviewed-by: Jens Wiklander Link: https://lore.kernel.org/r/20230419-ffa_fixes_6-4-v2-2-d9108e43a176@arm.com Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 737f36e7a903..5904a679d351 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -274,7 +274,8 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, int idx, count, flags = 0, sz, buf_sz; ffa_value_t partition_info; - if (!buffer || !num_partitions) /* Just get the count for now */ + if (drv_info->version > FFA_VERSION_1_0 && + (!buffer || !num_partitions)) /* Just get the count for now */ flags = PARTITION_INFO_GET_RETURN_COUNT_ONLY; mutex_lock(&drv_info->rx_lock); -- cgit 1.4.1 From 8e8c33cc89a0d3ff3dcf2ea5cb7ddee3ea4ae7be Mon Sep 17 00:00:00 2001 From: Jeremy Sowden Date: Wed, 26 Apr 2023 22:50:32 +0100 Subject: selftests/bpf: Fix pkg-config call building sign-file [ Upstream commit 5f5486b620cd43b16a1787ef92b9bc21bd72ef2e ] When building sign-file, the call to get the CFLAGS for libcrypto is missing white-space between `pkg-config` and `--cflags`: $(shell $(HOSTPKG_CONFIG)--cflags libcrypto 2> /dev/null) Removing the redirection of stderr, we see: $ make -C tools/testing/selftests/bpf sign-file make: Entering directory '[...]/tools/testing/selftests/bpf' make: pkg-config--cflags: No such file or directory SIGN-FILE sign-file make: Leaving directory '[...]/tools/testing/selftests/bpf' Add the missing space. Fixes: fc97590668ae ("selftests/bpf: Add test for bpf_verify_pkcs7_signature() kfunc") Signed-off-by: Jeremy Sowden Signed-off-by: Daniel Borkmann Reviewed-by: Roberto Sassu Link: https://lore.kernel.org/bpf/20230426215032.415792-1-jeremy@azazel.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 687249d99b5f..0465ddc81f35 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -193,7 +193,7 @@ $(OUTPUT)/urandom_read: urandom_read.c urandom_read_aux.c $(OUTPUT)/liburandom_r $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c $(call msg,SIGN-FILE,,$@) - $(Q)$(CC) $(shell $(HOSTPKG_CONFIG)--cflags libcrypto 2> /dev/null) \ + $(Q)$(CC) $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null) \ $< -o $@ \ $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto) -- cgit 1.4.1 From b3e54fb3a35d1bc8caa978d49dcae32026c926ee Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 May 2023 20:14:08 -0500 Subject: platform/x86/amd/pmf: Fix CnQF and auto-mode after resume [ Upstream commit b54147fa374dbeadcb01b1762db1a793e06e37de ] After suspend/resume cycle there is an error message and auto-mode or CnQF stops working. [ 5741.447511] amd-pmf AMDI0100:00: SMU cmd failed. err: 0xff [ 5741.447523] amd-pmf AMDI0100:00: AMD_PMF_REGISTER_RESPONSE:ff [ 5741.447527] amd-pmf AMDI0100:00: AMD_PMF_REGISTER_ARGUMENT:7 [ 5741.447531] amd-pmf AMDI0100:00: AMD_PMF_REGISTER_MESSAGE:16 [ 5741.447540] amd-pmf AMDI0100:00: [AUTO_MODE] avg power: 0 mW mode: QUIET This is because the DRAM address used for accessing metrics table needs to be refreshed after a suspend resume cycle. Add a resume callback to reset this again. Fixes: 1a409b35c995 ("platform/x86/amd/pmf: Get performance metrics from PMFW") Signed-off-by: Mario Limonciello Link: https://lore.kernel.org/r/20230513011408.958-1-mario.limonciello@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/amd/pmf/core.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 0acc0b622129..dc9803e1a4b9 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -245,24 +245,29 @@ static const struct pci_device_id pmf_pci_ids[] = { { } }; -int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev) +static void amd_pmf_set_dram_addr(struct amd_pmf_dev *dev) { u64 phys_addr; u32 hi, low; - INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics); + phys_addr = virt_to_phys(dev->buf); + hi = phys_addr >> 32; + low = phys_addr & GENMASK(31, 0); + + amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL); + amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL); +} +int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev) +{ /* Get Metrics Table Address */ dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL); if (!dev->buf) return -ENOMEM; - phys_addr = virt_to_phys(dev->buf); - hi = phys_addr >> 32; - low = phys_addr & GENMASK(31, 0); + INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics); - amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL); - amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL); + amd_pmf_set_dram_addr(dev); /* * Start collecting the metrics data after a small delay @@ -273,6 +278,18 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev) return 0; } +static int amd_pmf_resume_handler(struct device *dev) +{ + struct amd_pmf_dev *pdev = dev_get_drvdata(dev); + + if (pdev->buf) + amd_pmf_set_dram_addr(pdev); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, NULL, amd_pmf_resume_handler); + static void amd_pmf_init_features(struct amd_pmf_dev *dev) { int ret; @@ -414,6 +431,7 @@ static struct platform_driver amd_pmf_driver = { .name = "amd-pmf", .acpi_match_table = amd_pmf_acpi_ids, .dev_groups = amd_pmf_driver_groups, + .pm = pm_sleep_ptr(&amd_pmf_pm), }, .probe = amd_pmf_probe, .remove = amd_pmf_remove, -- cgit 1.4.1 From e734a693a24c735e484eb52d29b002006aa629d5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 16 May 2023 18:50:36 -0700 Subject: tls: rx: device: fix checking decryption status [ Upstream commit b3a03b540e3cf62a255213d084d76d71c02793d5 ] skb->len covers the entire skb, including the frag_list. In fact we're guaranteed that rxm->full_len <= skb->len, so since the change under Fixes we were not checking decrypt status of any skb but the first. Note that the skb_pagelen() added here may feel a bit costly, but it's removed by subsequent fixes, anyway. Reported-by: Tariq Toukan Fixes: 86b259f6f888 ("tls: rx: device: bound the frag walk") Tested-by: Shai Amiram Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/tls/tls_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index a7cc4f9faac2..3b87c7b04ac8 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -1012,7 +1012,7 @@ int tls_device_decrypted(struct sock *sk, struct tls_context *tls_ctx) struct sk_buff *skb_iter; int left; - left = rxm->full_len - skb->len; + left = rxm->full_len + rxm->offset - skb_pagelen(skb); /* Check if all the data is decrypted already */ skb_iter = skb_shinfo(skb)->frag_list; while (skb_iter && left > 0) { -- cgit 1.4.1 From ecd9f6ed9ed2d4c9f11f67245b780bdde3942d81 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 16 May 2023 18:50:37 -0700 Subject: tls: rx: strp: set the skb->len of detached / CoW'ed skbs [ Upstream commit 210620ae44a83f25220450bbfcc22e6fe986b25f ] alloc_skb_with_frags() fills in page frag sizes but does not set skb->len and skb->data_len. Set those correctly otherwise device offload will most likely generate an empty skb and hit the BUG() at the end of __skb_nsg(). Fixes: 84c61fe1a75b ("tls: rx: do not use the standard strparser") Tested-by: Shai Amiram Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/tls/tls_strp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index 955ac3e0bf4d..24016c865e00 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -56,6 +56,8 @@ static struct sk_buff *tls_strp_msg_make_copy(struct tls_strparser *strp) offset += skb_frag_size(frag); } + skb->len = strp->stm.full_len; + skb->data_len = strp->stm.full_len; skb_copy_header(skb, strp->anchor); rxm = strp_msg(skb); rxm->offset = 0; -- cgit 1.4.1 From c48b8399e430860a5b6c0b80c91f09d80c00c1af Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 16 May 2023 18:50:39 -0700 Subject: tls: rx: strp: fix determining record length in copy mode [ Upstream commit 8b0c0dc9fbbd01e58a573a41c38885f9e4c17696 ] We call tls_rx_msg_size(skb) before doing skb->len += chunk. So the tls_rx_msg_size() code will see old skb->len, most likely leading to an over-read. Worst case we will over read an entire record, next iteration will try to trim the skb but may end up turning frag len negative or discarding the subsequent record (since we already told TCP we've read it during previous read but now we'll trim it out of the skb). Fixes: 84c61fe1a75b ("tls: rx: do not use the standard strparser") Tested-by: Shai Amiram Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/tls/tls_strp.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index 24016c865e00..9889df5ce066 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -210,19 +210,28 @@ static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb, skb_frag_size(frag), chunk)); - sz = tls_rx_msg_size(strp, strp->anchor); + skb->len += chunk; + skb->data_len += chunk; + skb_frag_size_add(frag, chunk); + + sz = tls_rx_msg_size(strp, skb); if (sz < 0) { desc->error = sz; return 0; } /* We may have over-read, sz == 0 is guaranteed under-read */ - if (sz > 0) - chunk = min_t(size_t, chunk, sz - skb->len); + if (unlikely(sz && sz < skb->len)) { + int over = skb->len - sz; + + WARN_ON_ONCE(over > chunk); + skb->len -= over; + skb->data_len -= over; + skb_frag_size_add(frag, -over); + + chunk -= over; + } - skb->len += chunk; - skb->data_len += chunk; - skb_frag_size_add(frag, chunk); frag++; len -= chunk; offset += chunk; -- cgit 1.4.1 From 52a89de3e9f2ffe290b2a09cf92fd9bbc26d2606 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 16 May 2023 18:50:38 -0700 Subject: tls: rx: strp: force mixed decrypted records into copy mode [ Upstream commit 14c4be92ebb3e36e392aa9dd8f314038a9f96f3c ] If a record is partially decrypted we'll have to CoW it, anyway, so go into copy mode and allocate a writable skb right away. This will make subsequent fix simpler because we won't have to teach tls_strp_msg_make_copy() how to copy skbs while preserving decrypt status. Tested-by: Shai Amiram Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Stable-dep-of: eca9bfafee3a ("tls: rx: strp: preserve decryption status of skbs when needed") Signed-off-by: Sasha Levin --- include/linux/skbuff.h | 10 ++++++++++ net/tls/tls_strp.c | 16 +++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 20ca1613f2e3..cc5ed2cf25f6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1567,6 +1567,16 @@ static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from) to->l4_hash = from->l4_hash; }; +static inline int skb_cmp_decrypted(const struct sk_buff *skb1, + const struct sk_buff *skb2) +{ +#ifdef CONFIG_TLS_DEVICE + return skb2->decrypted - skb1->decrypted; +#else + return 0; +#endif +} + static inline void skb_copy_decrypted(struct sk_buff *to, const struct sk_buff *from) { diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index 9889df5ce066..e2e48217e7ac 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -326,15 +326,19 @@ static int tls_strp_read_copy(struct tls_strparser *strp, bool qshort) return 0; } -static bool tls_strp_check_no_dup(struct tls_strparser *strp) +static bool tls_strp_check_queue_ok(struct tls_strparser *strp) { unsigned int len = strp->stm.offset + strp->stm.full_len; - struct sk_buff *skb; + struct sk_buff *first, *skb; u32 seq; - skb = skb_shinfo(strp->anchor)->frag_list; - seq = TCP_SKB_CB(skb)->seq; + first = skb_shinfo(strp->anchor)->frag_list; + skb = first; + seq = TCP_SKB_CB(first)->seq; + /* Make sure there's no duplicate data in the queue, + * and the decrypted status matches. + */ while (skb->len < len) { seq += skb->len; len -= skb->len; @@ -342,6 +346,8 @@ static bool tls_strp_check_no_dup(struct tls_strparser *strp) if (TCP_SKB_CB(skb)->seq != seq) return false; + if (skb_cmp_decrypted(first, skb)) + return false; } return true; @@ -422,7 +428,7 @@ static int tls_strp_read_sock(struct tls_strparser *strp) return tls_strp_read_copy(strp, true); } - if (!tls_strp_check_no_dup(strp)) + if (!tls_strp_check_queue_ok(strp)) return tls_strp_read_copy(strp, false); strp->msg_ready = 1; -- cgit 1.4.1 From ba93977437e7c6554563e5d75c9bf4a04b387a60 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 16 May 2023 18:50:40 -0700 Subject: tls: rx: strp: factor out copying skb data [ Upstream commit c1c607b1e5d5477d82ca6a86a05a4f10907b33ee ] We'll need to copy input skbs individually in the next patch. Factor that code out (without assuming we're copying a full record). Tested-by: Shai Amiram Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Stable-dep-of: eca9bfafee3a ("tls: rx: strp: preserve decryption status of skbs when needed") Signed-off-by: Sasha Levin --- net/tls/tls_strp.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index e2e48217e7ac..61fbf84baf9e 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -34,31 +34,44 @@ static void tls_strp_anchor_free(struct tls_strparser *strp) strp->anchor = NULL; } -/* Create a new skb with the contents of input copied to its page frags */ -static struct sk_buff *tls_strp_msg_make_copy(struct tls_strparser *strp) +static struct sk_buff * +tls_strp_skb_copy(struct tls_strparser *strp, struct sk_buff *in_skb, + int offset, int len) { - struct strp_msg *rxm; struct sk_buff *skb; - int i, err, offset; + int i, err; - skb = alloc_skb_with_frags(0, strp->stm.full_len, TLS_PAGE_ORDER, + skb = alloc_skb_with_frags(0, len, TLS_PAGE_ORDER, &err, strp->sk->sk_allocation); if (!skb) return NULL; - offset = strp->stm.offset; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - WARN_ON_ONCE(skb_copy_bits(strp->anchor, offset, + WARN_ON_ONCE(skb_copy_bits(in_skb, offset, skb_frag_address(frag), skb_frag_size(frag))); offset += skb_frag_size(frag); } - skb->len = strp->stm.full_len; - skb->data_len = strp->stm.full_len; - skb_copy_header(skb, strp->anchor); + skb->len = len; + skb->data_len = len; + skb_copy_header(skb, in_skb); + return skb; +} + +/* Create a new skb with the contents of input copied to its page frags */ +static struct sk_buff *tls_strp_msg_make_copy(struct tls_strparser *strp) +{ + struct strp_msg *rxm; + struct sk_buff *skb; + + skb = tls_strp_skb_copy(strp, strp->anchor, strp->stm.offset, + strp->stm.full_len); + if (!skb) + return NULL; + rxm = strp_msg(skb); rxm->offset = 0; return skb; -- cgit 1.4.1 From 3347ac7a81608b30671d49d9be20f8b5563f0957 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 16 May 2023 18:50:41 -0700 Subject: tls: rx: strp: preserve decryption status of skbs when needed [ Upstream commit eca9bfafee3a0487e59c59201ae14c7594ba940a ] When receive buffer is small we try to copy out the data from TCP into a skb maintained by TLS to prevent connection from stalling. Unfortunately if a single record is made up of a mix of decrypted and non-decrypted skbs combining them into a single skb leads to loss of decryption status, resulting in decryption errors or data corruption. Similarly when trying to use TCP receive queue directly we need to make sure that all the skbs within the record have the same status. If we don't the mixed status will be detected correctly but we'll CoW the anchor, again collapsing it into a single paged skb without decrypted status preserved. So the "fixup" code will not know which parts of skb to re-encrypt. Fixes: 84c61fe1a75b ("tls: rx: do not use the standard strparser") Tested-by: Shai Amiram Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/net/tls.h | 1 + net/tls/tls.h | 5 +++ net/tls/tls_device.c | 22 ++++------ net/tls/tls_strp.c | 117 +++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 114 insertions(+), 31 deletions(-) diff --git a/include/net/tls.h b/include/net/tls.h index 154949c7b0c8..c36bf4c50027 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -124,6 +124,7 @@ struct tls_strparser { u32 mark : 8; u32 stopped : 1; u32 copy_mode : 1; + u32 mixed_decrypted : 1; u32 msg_ready : 1; struct strp_msg stm; diff --git a/net/tls/tls.h b/net/tls/tls.h index 0e840a0c3437..17737a65c643 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -165,6 +165,11 @@ static inline bool tls_strp_msg_ready(struct tls_sw_context_rx *ctx) return ctx->strp.msg_ready; } +static inline bool tls_strp_msg_mixed_decrypted(struct tls_sw_context_rx *ctx) +{ + return ctx->strp.mixed_decrypted; +} + #ifdef CONFIG_TLS_DEVICE int tls_device_init(void); void tls_device_cleanup(void); diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 3b87c7b04ac8..bf69c9d6d06c 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -1007,20 +1007,14 @@ int tls_device_decrypted(struct sock *sk, struct tls_context *tls_ctx) struct tls_sw_context_rx *sw_ctx = tls_sw_ctx_rx(tls_ctx); struct sk_buff *skb = tls_strp_msg(sw_ctx); struct strp_msg *rxm = strp_msg(skb); - int is_decrypted = skb->decrypted; - int is_encrypted = !is_decrypted; - struct sk_buff *skb_iter; - int left; - - left = rxm->full_len + rxm->offset - skb_pagelen(skb); - /* Check if all the data is decrypted already */ - skb_iter = skb_shinfo(skb)->frag_list; - while (skb_iter && left > 0) { - is_decrypted &= skb_iter->decrypted; - is_encrypted &= !skb_iter->decrypted; - - left -= skb_iter->len; - skb_iter = skb_iter->next; + int is_decrypted, is_encrypted; + + if (!tls_strp_msg_mixed_decrypted(sw_ctx)) { + is_decrypted = skb->decrypted; + is_encrypted = !is_decrypted; + } else { + is_decrypted = 0; + is_encrypted = 0; } trace_tls_device_decrypted(sk, tcp_sk(sk)->copied_seq - rxm->full_len, diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index 61fbf84baf9e..da95abbb7ea3 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -29,7 +29,8 @@ static void tls_strp_anchor_free(struct tls_strparser *strp) struct skb_shared_info *shinfo = skb_shinfo(strp->anchor); DEBUG_NET_WARN_ON_ONCE(atomic_read(&shinfo->dataref) != 1); - shinfo->frag_list = NULL; + if (!strp->copy_mode) + shinfo->frag_list = NULL; consume_skb(strp->anchor); strp->anchor = NULL; } @@ -195,22 +196,22 @@ static void tls_strp_flush_anchor_copy(struct tls_strparser *strp) for (i = 0; i < shinfo->nr_frags; i++) __skb_frag_unref(&shinfo->frags[i], false); shinfo->nr_frags = 0; + if (strp->copy_mode) { + kfree_skb_list(shinfo->frag_list); + shinfo->frag_list = NULL; + } strp->copy_mode = 0; + strp->mixed_decrypted = 0; } -static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb, - unsigned int offset, size_t in_len) +static int tls_strp_copyin_frag(struct tls_strparser *strp, struct sk_buff *skb, + struct sk_buff *in_skb, unsigned int offset, + size_t in_len) { - struct tls_strparser *strp = (struct tls_strparser *)desc->arg.data; - struct sk_buff *skb; - skb_frag_t *frag; size_t len, chunk; + skb_frag_t *frag; int sz; - if (strp->msg_ready) - return 0; - - skb = strp->anchor; frag = &skb_shinfo(skb)->frags[skb->len / PAGE_SIZE]; len = in_len; @@ -228,10 +229,8 @@ static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb, skb_frag_size_add(frag, chunk); sz = tls_rx_msg_size(strp, skb); - if (sz < 0) { - desc->error = sz; - return 0; - } + if (sz < 0) + return sz; /* We may have over-read, sz == 0 is guaranteed under-read */ if (unlikely(sz && sz < skb->len)) { @@ -271,15 +270,99 @@ static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb, offset += chunk; } - if (strp->stm.full_len == skb->len) { +read_done: + return in_len - len; +} + +static int tls_strp_copyin_skb(struct tls_strparser *strp, struct sk_buff *skb, + struct sk_buff *in_skb, unsigned int offset, + size_t in_len) +{ + struct sk_buff *nskb, *first, *last; + struct skb_shared_info *shinfo; + size_t chunk; + int sz; + + if (strp->stm.full_len) + chunk = strp->stm.full_len - skb->len; + else + chunk = TLS_MAX_PAYLOAD_SIZE + PAGE_SIZE; + chunk = min(chunk, in_len); + + nskb = tls_strp_skb_copy(strp, in_skb, offset, chunk); + if (!nskb) + return -ENOMEM; + + shinfo = skb_shinfo(skb); + if (!shinfo->frag_list) { + shinfo->frag_list = nskb; + nskb->prev = nskb; + } else { + first = shinfo->frag_list; + last = first->prev; + last->next = nskb; + first->prev = nskb; + } + + skb->len += chunk; + skb->data_len += chunk; + + if (!strp->stm.full_len) { + sz = tls_rx_msg_size(strp, skb); + if (sz < 0) + return sz; + + /* We may have over-read, sz == 0 is guaranteed under-read */ + if (unlikely(sz && sz < skb->len)) { + int over = skb->len - sz; + + WARN_ON_ONCE(over > chunk); + skb->len -= over; + skb->data_len -= over; + __pskb_trim(nskb, nskb->len - over); + + chunk -= over; + } + + strp->stm.full_len = sz; + } + + return chunk; +} + +static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb, + unsigned int offset, size_t in_len) +{ + struct tls_strparser *strp = (struct tls_strparser *)desc->arg.data; + struct sk_buff *skb; + int ret; + + if (strp->msg_ready) + return 0; + + skb = strp->anchor; + if (!skb->len) + skb_copy_decrypted(skb, in_skb); + else + strp->mixed_decrypted |= !!skb_cmp_decrypted(skb, in_skb); + + if (IS_ENABLED(CONFIG_TLS_DEVICE) && strp->mixed_decrypted) + ret = tls_strp_copyin_skb(strp, skb, in_skb, offset, in_len); + else + ret = tls_strp_copyin_frag(strp, skb, in_skb, offset, in_len); + if (ret < 0) { + desc->error = ret; + ret = 0; + } + + if (strp->stm.full_len && strp->stm.full_len == skb->len) { desc->count = 0; strp->msg_ready = 1; tls_rx_msg_ready(strp); } -read_done: - return in_len - len; + return ret; } static int tls_strp_read_copyin(struct tls_strparser *strp) -- cgit 1.4.1 From 085f27f48c84c7178123f935de37f4f8e356b064 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Mon, 6 Feb 2023 11:52:02 +0200 Subject: net/mlx5: E-switch, Devcom, sync devcom events and devcom comp register [ Upstream commit 8c253dfc89efde6b5faddf9e7400e5d17884e042 ] devcom events are sent to all registered component. Following the cited patch, it is possible for two components, e.g.: two eswitches, to send devcom events, while both components are registered. This means eswitch layer will do double un/pairing, which is double allocation and free of resources, even though only one un/pairing is needed. flow example: cpu0 cpu1 ---- ---- mlx5_devlink_eswitch_mode_set(dev0) esw_offloads_devcom_init() mlx5_devcom_register_component(esw0) mlx5_devlink_eswitch_mode_set(dev1) esw_offloads_devcom_init() mlx5_devcom_register_component(esw1) mlx5_devcom_send_event() mlx5_devcom_send_event() Hence, check whether the eswitches are already un/paired before free/allocation of resources. Fixes: 09b278462f16 ("net: devlink: enable parallel ops on netlink interface") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 1 + drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 821c78bab373..a3daca44f74b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -340,6 +340,7 @@ struct mlx5_eswitch { } params; struct blocking_notifier_head n_head; struct dentry *dbgfs; + bool paired[MLX5_MAX_PORTS]; }; void esw_offloads_disable(struct mlx5_eswitch *esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 5235b5a7b963..433cdd0a2cf3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -2827,6 +2827,9 @@ static int mlx5_esw_offloads_devcom_event(int event, mlx5_eswitch_vport_match_metadata_enabled(peer_esw)) break; + if (esw->paired[mlx5_get_dev_index(peer_esw->dev)]) + break; + err = mlx5_esw_offloads_set_ns_peer(esw, peer_esw, true); if (err) goto err_out; @@ -2838,14 +2841,18 @@ static int mlx5_esw_offloads_devcom_event(int event, if (err) goto err_pair; + esw->paired[mlx5_get_dev_index(peer_esw->dev)] = true; + peer_esw->paired[mlx5_get_dev_index(esw->dev)] = true; mlx5_devcom_set_paired(devcom, MLX5_DEVCOM_ESW_OFFLOADS, true); break; case ESW_OFFLOADS_DEVCOM_UNPAIR: - if (!mlx5_devcom_is_paired(devcom, MLX5_DEVCOM_ESW_OFFLOADS)) + if (!esw->paired[mlx5_get_dev_index(peer_esw->dev)]) break; mlx5_devcom_set_paired(devcom, MLX5_DEVCOM_ESW_OFFLOADS, false); + esw->paired[mlx5_get_dev_index(peer_esw->dev)] = false; + peer_esw->paired[mlx5_get_dev_index(esw->dev)] = false; mlx5_esw_offloads_unpair(peer_esw); mlx5_esw_offloads_unpair(esw); mlx5_esw_offloads_set_ns_peer(esw, peer_esw, false); -- cgit 1.4.1 From 49b5b5bfeef16a497560b5e35c1230801844c909 Mon Sep 17 00:00:00 2001 From: Henning Schild Date: Thu, 27 Apr 2023 17:20:55 +0200 Subject: gpio-f7188x: fix chip name and pin count on Nuvoton chip [ Upstream commit 3002b8642f016d7fe3ff56240dacea1075f6b877 ] In fact the device with chip id 0xD283 is called NCT6126D, and that is the chip id the Nuvoton code was written for. Correct that name to avoid confusion, because a NCT6116D in fact exists as well but has another chip id, and is currently not supported. The look at the spec also revealed that GPIO group7 in fact has 8 pins, so correct the pin count in that group as well. Fixes: d0918a84aff0 ("gpio-f7188x: Add GPIO support for Nuvoton NCT6116") Reported-by: Xing Tong Wu Signed-off-by: Henning Schild Acked-by: Simon Guinot Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-f7188x.c | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e3af86f06c63..3e8e5f4ffa59 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -882,7 +882,7 @@ config GPIO_F7188X help This option enables support for GPIOs found on Fintek Super-I/O chips F71869, F71869A, F71882FG, F71889F and F81866. - As well as Nuvoton Super-I/O chip NCT6116D. + As well as Nuvoton Super-I/O chip NCT6126D. To compile this driver as a module, choose M here: the module will be called f7188x-gpio. diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 9effa7769bef..f54ca5a1775e 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -48,7 +48,7 @@ /* * Nuvoton devices. */ -#define SIO_NCT6116D_ID 0xD283 /* NCT6116D chipset ID */ +#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */ #define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */ @@ -62,7 +62,7 @@ enum chips { f81866, f81804, f81865, - nct6116d, + nct6126d, }; static const char * const f7188x_names[] = { @@ -74,7 +74,7 @@ static const char * const f7188x_names[] = { "f81866", "f81804", "f81865", - "nct6116d", + "nct6126d", }; struct f7188x_sio { @@ -187,8 +187,8 @@ static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, /* Output mode register (0:open drain 1:push-pull). */ #define f7188x_gpio_out_mode(base) ((base) + 3) -#define f7188x_gpio_dir_invert(type) ((type) == nct6116d) -#define f7188x_gpio_data_single(type) ((type) == nct6116d) +#define f7188x_gpio_dir_invert(type) ((type) == nct6126d) +#define f7188x_gpio_data_single(type) ((type) == nct6126d) static struct f7188x_gpio_bank f71869_gpio_bank[] = { F7188X_GPIO_BANK(0, 6, 0xF0, DRVNAME "-0"), @@ -274,7 +274,7 @@ static struct f7188x_gpio_bank f81865_gpio_bank[] = { F7188X_GPIO_BANK(60, 5, 0x90, DRVNAME "-6"), }; -static struct f7188x_gpio_bank nct6116d_gpio_bank[] = { +static struct f7188x_gpio_bank nct6126d_gpio_bank[] = { F7188X_GPIO_BANK(0, 8, 0xE0, DRVNAME "-0"), F7188X_GPIO_BANK(10, 8, 0xE4, DRVNAME "-1"), F7188X_GPIO_BANK(20, 8, 0xE8, DRVNAME "-2"), @@ -282,7 +282,7 @@ static struct f7188x_gpio_bank nct6116d_gpio_bank[] = { F7188X_GPIO_BANK(40, 8, 0xF0, DRVNAME "-4"), F7188X_GPIO_BANK(50, 8, 0xF4, DRVNAME "-5"), F7188X_GPIO_BANK(60, 8, 0xF8, DRVNAME "-6"), - F7188X_GPIO_BANK(70, 1, 0xFC, DRVNAME "-7"), + F7188X_GPIO_BANK(70, 8, 0xFC, DRVNAME "-7"), }; static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -490,9 +490,9 @@ static int f7188x_gpio_probe(struct platform_device *pdev) data->nr_bank = ARRAY_SIZE(f81865_gpio_bank); data->bank = f81865_gpio_bank; break; - case nct6116d: - data->nr_bank = ARRAY_SIZE(nct6116d_gpio_bank); - data->bank = nct6116d_gpio_bank; + case nct6126d: + data->nr_bank = ARRAY_SIZE(nct6126d_gpio_bank); + data->bank = nct6126d_gpio_bank; break; default: return -ENODEV; @@ -559,9 +559,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81865_ID: sio->type = f81865; break; - case SIO_NCT6116D_ID: + case SIO_NCT6126D_ID: sio->device = SIO_LD_GPIO_NUVOTON; - sio->type = nct6116d; + sio->type = nct6126d; break; default: pr_info("Unsupported Fintek device 0x%04x\n", devid); @@ -569,7 +569,7 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) } /* double check manufacturer where possible */ - if (sio->type != nct6116d) { + if (sio->type != nct6126d) { manid = superio_inw(addr, SIO_FINTEK_MANID); if (manid != SIO_FINTEK_ID) { pr_debug("Not a Fintek device at 0x%08x\n", addr); @@ -581,7 +581,7 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) err = 0; pr_info("Found %s at %#x\n", f7188x_names[sio->type], (unsigned int)addr); - if (sio->type != nct6116d) + if (sio->type != nct6126d) pr_info(" revision %d\n", superio_inb(addr, SIO_FINTEK_DEVREV)); err: -- cgit 1.4.1 From 4ae2af3e59e27f2cda74d1cb5c385cff235ebe00 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:05 -0700 Subject: bpf, sockmap: Pass skb ownership through read_skb [ Upstream commit 78fa0d61d97a728d306b0c23d353c0e340756437 ] The read_skb hook calls consume_skb() now, but this means that if the recv_actor program wants to use the skb it needs to inc the ref cnt so that the consume_skb() doesn't kfree the sk_buff. This is problematic because in some error cases under memory pressure we may need to linearize the sk_buff from sk_psock_skb_ingress_enqueue(). Then we get this, skb_linearize() __pskb_pull_tail() pskb_expand_head() BUG_ON(skb_shared(skb)) Because we incremented users refcnt from sk_psock_verdict_recv() we hit the bug on with refcnt > 1 and trip it. To fix lets simply pass ownership of the sk_buff through the skb_read call. Then we can drop the consume from read_skb handlers and assume the verdict recv does any required kfree. Bug found while testing in our CI which runs in VMs that hit memory constraints rather regularly. William tested TCP read_skb handlers. [ 106.536188] ------------[ cut here ]------------ [ 106.536197] kernel BUG at net/core/skbuff.c:1693! [ 106.536479] invalid opcode: 0000 [#1] PREEMPT SMP PTI [ 106.536726] CPU: 3 PID: 1495 Comm: curl Not tainted 5.19.0-rc5 #1 [ 106.537023] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ArchLinux 1.16.0-1 04/01/2014 [ 106.537467] RIP: 0010:pskb_expand_head+0x269/0x330 [ 106.538585] RSP: 0018:ffffc90000138b68 EFLAGS: 00010202 [ 106.538839] RAX: 000000000000003f RBX: ffff8881048940e8 RCX: 0000000000000a20 [ 106.539186] RDX: 0000000000000002 RSI: 0000000000000000 RDI: ffff8881048940e8 [ 106.539529] RBP: ffffc90000138be8 R08: 00000000e161fd1a R09: 0000000000000000 [ 106.539877] R10: 0000000000000018 R11: 0000000000000000 R12: ffff8881048940e8 [ 106.540222] R13: 0000000000000003 R14: 0000000000000000 R15: ffff8881048940e8 [ 106.540568] FS: 00007f277dde9f00(0000) GS:ffff88813bd80000(0000) knlGS:0000000000000000 [ 106.540954] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 106.541227] CR2: 00007f277eeede64 CR3: 000000000ad3e000 CR4: 00000000000006e0 [ 106.541569] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 106.541915] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 106.542255] Call Trace: [ 106.542383] [ 106.542487] __pskb_pull_tail+0x4b/0x3e0 [ 106.542681] skb_ensure_writable+0x85/0xa0 [ 106.542882] sk_skb_pull_data+0x18/0x20 [ 106.543084] bpf_prog_b517a65a242018b0_bpf_skskb_http_verdict+0x3a9/0x4aa9 [ 106.543536] ? migrate_disable+0x66/0x80 [ 106.543871] sk_psock_verdict_recv+0xe2/0x310 [ 106.544258] ? sk_psock_write_space+0x1f0/0x1f0 [ 106.544561] tcp_read_skb+0x7b/0x120 [ 106.544740] tcp_data_queue+0x904/0xee0 [ 106.544931] tcp_rcv_established+0x212/0x7c0 [ 106.545142] tcp_v4_do_rcv+0x174/0x2a0 [ 106.545326] tcp_v4_rcv+0xe70/0xf60 [ 106.545500] ip_protocol_deliver_rcu+0x48/0x290 [ 106.545744] ip_local_deliver_finish+0xa7/0x150 Fixes: 04919bed948dc ("tcp: Introduce tcp_read_skb()") Reported-by: William Findlay Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Tested-by: William Findlay Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-2-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- net/core/skmsg.c | 2 -- net/ipv4/tcp.c | 1 - net/ipv4/udp.c | 7 ++----- net/unix/af_unix.c | 7 ++----- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 53d0251788aa..2b6d9519ff29 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1180,8 +1180,6 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) int ret = __SK_DROP; int len = skb->len; - skb_get(skb); - rcu_read_lock(); psock = sk_psock(sk); if (unlikely(!psock)) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1fb67f819de4..31156ebb759c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1772,7 +1772,6 @@ int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor) WARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk)); tcp_flags = TCP_SKB_CB(skb)->tcp_flags; used = recv_actor(sk, skb); - consume_skb(skb); if (used < 0) { if (!copied) copied = used; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 3ffa30c37293..956d6797c76f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1806,7 +1806,7 @@ EXPORT_SYMBOL(__skb_recv_udp); int udp_read_skb(struct sock *sk, skb_read_actor_t recv_actor) { struct sk_buff *skb; - int err, copied; + int err; try_again: skb = skb_recv_udp(sk, MSG_DONTWAIT, &err); @@ -1825,10 +1825,7 @@ try_again: } WARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk)); - copied = recv_actor(sk, skb); - kfree_skb(skb); - - return copied; + return recv_actor(sk, skb); } EXPORT_SYMBOL(udp_read_skb); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 70eb3bc67126..5b19b6c53a2c 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2552,7 +2552,7 @@ static int unix_read_skb(struct sock *sk, skb_read_actor_t recv_actor) { struct unix_sock *u = unix_sk(sk); struct sk_buff *skb; - int err, copied; + int err; mutex_lock(&u->iolock); skb = skb_recv_datagram(sk, MSG_DONTWAIT, &err); @@ -2560,10 +2560,7 @@ static int unix_read_skb(struct sock *sk, skb_read_actor_t recv_actor) if (!skb) return err; - copied = recv_actor(sk, skb); - kfree_skb(skb); - - return copied; + return recv_actor(sk, skb); } /* -- cgit 1.4.1 From 9f4d7efb33453a009d1df098a885b012ce93da2d Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:06 -0700 Subject: bpf, sockmap: Convert schedule_work into delayed_work [ Upstream commit 29173d07f79883ac94f5570294f98af3d4287382 ] Sk_buffs are fed into sockmap verdict programs either from a strparser (when the user might want to decide how framing of skb is done by attaching another parser program) or directly through tcp_read_sock. The tcp_read_sock is the preferred method for performance when the BPF logic is a stream parser. The flow for Cilium's common use case with a stream parser is, tcp_read_sock() sk_psock_verdict_recv ret = bpf_prog_run_pin_on_cpu() sk_psock_verdict_apply(sock, skb, ret) // if system is under memory pressure or app is slow we may // need to queue skb. Do this queuing through ingress_skb and // then kick timer to wake up handler skb_queue_tail(ingress_skb, skb) schedule_work(work); The work queue is wired up to sk_psock_backlog(). This will then walk the ingress_skb skb list that holds our sk_buffs that could not be handled, but should be OK to run at some later point. However, its possible that the workqueue doing this work still hits an error when sending the skb. When this happens the skbuff is requeued on a temporary 'state' struct kept with the workqueue. This is necessary because its possible to partially send an skbuff before hitting an error and we need to know how and where to restart when the workqueue runs next. Now for the trouble, we don't rekick the workqueue. This can cause a stall where the skbuff we just cached on the state variable might never be sent. This happens when its the last packet in a flow and no further packets come along that would cause the system to kick the workqueue from that side. To fix we could do simple schedule_work(), but while under memory pressure it makes sense to back off some instead of continue to retry repeatedly. So instead to fix convert schedule_work to schedule_delayed_work and add backoff logic to reschedule from backlog queue on errors. Its not obvious though what a good backoff is so use '1'. To test we observed some flakes whil running NGINX compliance test with sockmap we attributed these failed test to this bug and subsequent issue. >From on list discussion. This commit bec217197b41("skmsg: Schedule psock work if the cached skb exists on the psock") was intended to address similar race, but had a couple cases it missed. Most obvious it only accounted for receiving traffic on the local socket so if redirecting into another socket we could still get an sk_buff stuck here. Next it missed the case where copied=0 in the recv() handler and then we wouldn't kick the scheduler. Also its sub-optimal to require userspace to kick the internal mechanisms of sockmap to wake it up and copy data to user. It results in an extra syscall and requires the app to actual handle the EAGAIN correctly. Fixes: 04919bed948dc ("tcp: Introduce tcp_read_skb()") Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Tested-by: William Findlay Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-3-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- include/linux/skmsg.h | 2 +- net/core/skmsg.c | 21 ++++++++++++++------- net/core/sock_map.c | 3 ++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 84f787416a54..904ff9a32ad6 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -105,7 +105,7 @@ struct sk_psock { struct proto *sk_proto; struct mutex work_mutex; struct sk_psock_work_state work_state; - struct work_struct work; + struct delayed_work work; struct rcu_work rwork; }; diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 2b6d9519ff29..6a9b794861f3 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -481,7 +481,7 @@ int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg, } out: if (psock->work_state.skb && copied > 0) - schedule_work(&psock->work); + schedule_delayed_work(&psock->work, 0); return copied; } EXPORT_SYMBOL_GPL(sk_msg_recvmsg); @@ -639,7 +639,8 @@ static void sk_psock_skb_state(struct sk_psock *psock, static void sk_psock_backlog(struct work_struct *work) { - struct sk_psock *psock = container_of(work, struct sk_psock, work); + struct delayed_work *dwork = to_delayed_work(work); + struct sk_psock *psock = container_of(dwork, struct sk_psock, work); struct sk_psock_work_state *state = &psock->work_state; struct sk_buff *skb = NULL; bool ingress; @@ -679,6 +680,12 @@ start: if (ret == -EAGAIN) { sk_psock_skb_state(psock, state, skb, len, off); + + /* Delay slightly to prioritize any + * other work that might be here. + */ + if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) + schedule_delayed_work(&psock->work, 1); goto end; } /* Hard errors break pipe and stop xmit. */ @@ -733,7 +740,7 @@ struct sk_psock *sk_psock_init(struct sock *sk, int node) INIT_LIST_HEAD(&psock->link); spin_lock_init(&psock->link_lock); - INIT_WORK(&psock->work, sk_psock_backlog); + INIT_DELAYED_WORK(&psock->work, sk_psock_backlog); mutex_init(&psock->work_mutex); INIT_LIST_HEAD(&psock->ingress_msg); spin_lock_init(&psock->ingress_lock); @@ -822,7 +829,7 @@ static void sk_psock_destroy(struct work_struct *work) sk_psock_done_strp(psock); - cancel_work_sync(&psock->work); + cancel_delayed_work_sync(&psock->work); mutex_destroy(&psock->work_mutex); psock_progs_drop(&psock->progs); @@ -937,7 +944,7 @@ static int sk_psock_skb_redirect(struct sk_psock *from, struct sk_buff *skb) } skb_queue_tail(&psock_other->ingress_skb, skb); - schedule_work(&psock_other->work); + schedule_delayed_work(&psock_other->work, 0); spin_unlock_bh(&psock_other->ingress_lock); return 0; } @@ -1017,7 +1024,7 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb, spin_lock_bh(&psock->ingress_lock); if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) { skb_queue_tail(&psock->ingress_skb, skb); - schedule_work(&psock->work); + schedule_delayed_work(&psock->work, 0); err = 0; } spin_unlock_bh(&psock->ingress_lock); @@ -1048,7 +1055,7 @@ static void sk_psock_write_space(struct sock *sk) psock = sk_psock(sk); if (likely(psock)) { if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) - schedule_work(&psock->work); + schedule_delayed_work(&psock->work, 0); write_space = psock->saved_write_space; } rcu_read_unlock(); diff --git a/net/core/sock_map.c b/net/core/sock_map.c index a68a7290a3b2..d38267201892 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -1624,9 +1624,10 @@ void sock_map_close(struct sock *sk, long timeout) rcu_read_unlock(); sk_psock_stop(psock); release_sock(sk); - cancel_work_sync(&psock->work); + cancel_delayed_work_sync(&psock->work); sk_psock_put(sk, psock); } + /* Make sure we do not recurse. This is a bug. * Leak the socket instead of crashing on a stack overflow. */ -- cgit 1.4.1 From 1e4e379ccde8c33d4f60c9221003ffe18ec05592 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:07 -0700 Subject: bpf, sockmap: Reschedule is now done through backlog [ Upstream commit bce22552f92ea7c577f49839b8e8f7d29afaf880 ] Now that the backlog manages the reschedule() logic correctly we can drop the partial fix to reschedule from recvmsg hook. Rescheduling on recvmsg hook was added to address a corner case where we still had data in the backlog state but had nothing to kick it and reschedule the backlog worker to run and finish copying data out of the state. This had a couple limitations, first it required user space to kick it introducing an unnecessary EBUSY and retry. Second it only handled the ingress case and egress redirects would still be hung. With the correct fix, pushing the reschedule logic down to where the enomem error occurs we can drop this fix. Fixes: bec217197b412 ("skmsg: Schedule psock work if the cached skb exists on the psock") Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-4-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- net/core/skmsg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 6a9b794861f3..2dfb6e31e8d0 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -480,8 +480,6 @@ int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg, msg_rx = sk_psock_peek_msg(psock); } out: - if (psock->work_state.skb && copied > 0) - schedule_delayed_work(&psock->work, 0); return copied; } EXPORT_SYMBOL_GPL(sk_msg_recvmsg); -- cgit 1.4.1 From ba4fec5bd6f8d802c1b8f87340c26ed42dda3ed4 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:08 -0700 Subject: bpf, sockmap: Improved check for empty queue [ Upstream commit 405df89dd52cbcd69a3cd7d9a10d64de38f854b2 ] We noticed some rare sk_buffs were stepping past the queue when system was under memory pressure. The general theory is to skip enqueueing sk_buffs when its not necessary which is the normal case with a system that is properly provisioned for the task, no memory pressure and enough cpu assigned. But, if we can't allocate memory due to an ENOMEM error when enqueueing the sk_buff into the sockmap receive queue we push it onto a delayed workqueue to retry later. When a new sk_buff is received we then check if that queue is empty. However, there is a problem with simply checking the queue length. When a sk_buff is being processed from the ingress queue but not yet on the sockmap msg receive queue its possible to also recv a sk_buff through normal path. It will check the ingress queue which is zero and then skip ahead of the pkt being processed. Previously we used sock lock from both contexts which made the problem harder to hit, but not impossible. To fix instead of popping the skb from the queue entirely we peek the skb from the queue and do the copy there. This ensures checks to the queue length are non-zero while skb is being processed. Then finally when the entire skb has been copied to user space queue or another socket we pop it off the queue. This way the queue length check allows bypassing the queue only after the list has been completely processed. To reproduce issue we run NGINX compliance test with sockmap running and observe some flakes in our testing that we attributed to this issue. Fixes: 04919bed948dc ("tcp: Introduce tcp_read_skb()") Suggested-by: Jakub Sitnicki Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Tested-by: William Findlay Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-5-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- include/linux/skmsg.h | 1 - net/core/skmsg.c | 32 ++++++++------------------------ 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 904ff9a32ad6..054d7911bfc9 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -71,7 +71,6 @@ struct sk_psock_link { }; struct sk_psock_work_state { - struct sk_buff *skb; u32 len; u32 off; }; diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 2dfb6e31e8d0..d3ffca1b9646 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -621,16 +621,12 @@ static int sk_psock_handle_skb(struct sk_psock *psock, struct sk_buff *skb, static void sk_psock_skb_state(struct sk_psock *psock, struct sk_psock_work_state *state, - struct sk_buff *skb, int len, int off) { spin_lock_bh(&psock->ingress_lock); if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) { - state->skb = skb; state->len = len; state->off = off; - } else { - sock_drop(psock->sk, skb); } spin_unlock_bh(&psock->ingress_lock); } @@ -641,23 +637,17 @@ static void sk_psock_backlog(struct work_struct *work) struct sk_psock *psock = container_of(dwork, struct sk_psock, work); struct sk_psock_work_state *state = &psock->work_state; struct sk_buff *skb = NULL; + u32 len = 0, off = 0; bool ingress; - u32 len, off; int ret; mutex_lock(&psock->work_mutex); - if (unlikely(state->skb)) { - spin_lock_bh(&psock->ingress_lock); - skb = state->skb; + if (unlikely(state->len)) { len = state->len; off = state->off; - state->skb = NULL; - spin_unlock_bh(&psock->ingress_lock); } - if (skb) - goto start; - while ((skb = skb_dequeue(&psock->ingress_skb))) { + while ((skb = skb_peek(&psock->ingress_skb))) { len = skb->len; off = 0; if (skb_bpf_strparser(skb)) { @@ -666,7 +656,6 @@ static void sk_psock_backlog(struct work_struct *work) off = stm->offset; len = stm->full_len; } -start: ingress = skb_bpf_ingress(skb); skb_bpf_redirect_clear(skb); do { @@ -676,8 +665,7 @@ start: len, ingress); if (ret <= 0) { if (ret == -EAGAIN) { - sk_psock_skb_state(psock, state, skb, - len, off); + sk_psock_skb_state(psock, state, len, off); /* Delay slightly to prioritize any * other work that might be here. @@ -689,15 +677,16 @@ start: /* Hard errors break pipe and stop xmit. */ sk_psock_report_error(psock, ret ? -ret : EPIPE); sk_psock_clear_state(psock, SK_PSOCK_TX_ENABLED); - sock_drop(psock->sk, skb); goto end; } off += ret; len -= ret; } while (len); - if (!ingress) + skb = skb_dequeue(&psock->ingress_skb); + if (!ingress) { kfree_skb(skb); + } } end: mutex_unlock(&psock->work_mutex); @@ -790,11 +779,6 @@ static void __sk_psock_zap_ingress(struct sk_psock *psock) skb_bpf_redirect_clear(skb); sock_drop(psock->sk, skb); } - kfree_skb(psock->work_state.skb); - /* We null the skb here to ensure that calls to sk_psock_backlog - * do not pick up the free'd skb. - */ - psock->work_state.skb = NULL; __sk_psock_purge_ingress_msg(psock); } @@ -813,7 +797,6 @@ void sk_psock_stop(struct sk_psock *psock) spin_lock_bh(&psock->ingress_lock); sk_psock_clear_state(psock, SK_PSOCK_TX_ENABLED); sk_psock_cork_free(psock); - __sk_psock_zap_ingress(psock); spin_unlock_bh(&psock->ingress_lock); } @@ -828,6 +811,7 @@ static void sk_psock_destroy(struct work_struct *work) sk_psock_done_strp(psock); cancel_delayed_work_sync(&psock->work); + __sk_psock_zap_ingress(psock); mutex_destroy(&psock->work_mutex); psock_progs_drop(&psock->progs); -- cgit 1.4.1 From 3a2129ebae359f354e5469916958c77e4871457d Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:09 -0700 Subject: bpf, sockmap: Handle fin correctly [ Upstream commit 901546fd8f9ca4b5c481ce00928ab425ce9aacc0 ] The sockmap code is returning EAGAIN after a FIN packet is received and no more data is on the receive queue. Correct behavior is to return 0 to the user and the user can then close the socket. The EAGAIN causes many apps to retry which masks the problem. Eventually the socket is evicted from the sockmap because its released from sockmap sock free handling. The issue creates a delay and can cause some errors on application side. To fix this check on sk_msg_recvmsg side if length is zero and FIN flag is set then set return to zero. A selftest will be added to check this condition. Fixes: 04919bed948dc ("tcp: Introduce tcp_read_skb()") Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Tested-by: William Findlay Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-6-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- net/ipv4/tcp_bpf.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 2e9547467edb..73c13642d47f 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -174,6 +174,24 @@ static int tcp_msg_wait_data(struct sock *sk, struct sk_psock *psock, return ret; } +static bool is_next_msg_fin(struct sk_psock *psock) +{ + struct scatterlist *sge; + struct sk_msg *msg_rx; + int i; + + msg_rx = sk_psock_peek_msg(psock); + i = msg_rx->sg.start; + sge = sk_msg_elem(msg_rx, i); + if (!sge->length) { + struct sk_buff *skb = msg_rx->skb; + + if (skb && TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) + return true; + } + return false; +} + static int tcp_bpf_recvmsg_parser(struct sock *sk, struct msghdr *msg, size_t len, @@ -196,6 +214,19 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, lock_sock(sk); msg_bytes_ready: copied = sk_msg_recvmsg(sk, psock, msg, len, flags); + /* The typical case for EFAULT is the socket was gracefully + * shutdown with a FIN pkt. So check here the other case is + * some error on copy_page_to_iter which would be unexpected. + * On fin return correct return code to zero. + */ + if (copied == -EFAULT) { + bool is_fin = is_next_msg_fin(psock); + + if (is_fin) { + copied = 0; + goto out; + } + } if (!copied) { long timeo; int data; -- cgit 1.4.1 From ab90b68f650e1b78ec266b701365634b5270fe34 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:10 -0700 Subject: bpf, sockmap: TCP data stall on recv before accept [ Upstream commit ea444185a6bf7da4dd0df1598ee953e4f7174858 ] A common mechanism to put a TCP socket into the sockmap is to hook the BPF_SOCK_OPS_{ACTIVE_PASSIVE}_ESTABLISHED_CB event with a BPF program that can map the socket info to the correct BPF verdict parser. When the user adds the socket to the map the psock is created and the new ops are assigned to ensure the verdict program will 'see' the sk_buffs as they arrive. Part of this process hooks the sk_data_ready op with a BPF specific handler to wake up the BPF verdict program when data is ready to read. The logic is simple enough (posted here for easy reading) static void sk_psock_verdict_data_ready(struct sock *sk) { struct socket *sock = sk->sk_socket; if (unlikely(!sock || !sock->ops || !sock->ops->read_skb)) return; sock->ops->read_skb(sk, sk_psock_verdict_recv); } The oversight here is sk->sk_socket is not assigned until the application accepts() the new socket. However, its entirely ok for the peer application to do a connect() followed immediately by sends. The socket on the receiver is sitting on the backlog queue of the listening socket until its accepted and the data is queued up. If the peer never accepts the socket or is slow it will eventually hit data limits and rate limit the session. But, important for BPF sockmap hooks when this data is received TCP stack does the sk_data_ready() call but the read_skb() for this data is never called because sk_socket is missing. The data sits on the sk_receive_queue. Then once the socket is accepted if we never receive more data from the peer there will be no further sk_data_ready calls and all the data is still on the sk_receive_queue(). Then user calls recvmsg after accept() and for TCP sockets in sockmap we use the tcp_bpf_recvmsg_parser() handler. The handler checks for data in the sk_msg ingress queue expecting that the BPF program has already run from the sk_data_ready hook and enqueued the data as needed. So we are stuck. To fix do an unlikely check in recvmsg handler for data on the sk_receive_queue and if it exists wake up data_ready. We have the sock locked in both read_skb and recvmsg so should avoid having multiple runners. Fixes: 04919bed948dc ("tcp: Introduce tcp_read_skb()") Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-7-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- net/ipv4/tcp_bpf.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 73c13642d47f..01dd76be1a58 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -212,6 +212,26 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, return tcp_recvmsg(sk, msg, len, flags, addr_len); lock_sock(sk); + + /* We may have received data on the sk_receive_queue pre-accept and + * then we can not use read_skb in this context because we haven't + * assigned a sk_socket yet so have no link to the ops. The work-around + * is to check the sk_receive_queue and in these cases read skbs off + * queue again. The read_skb hook is not running at this point because + * of lock_sock so we avoid having multiple runners in read_skb. + */ + if (unlikely(!skb_queue_empty(&sk->sk_receive_queue))) { + tcp_data_ready(sk); + /* This handles the ENOMEM errors if we both receive data + * pre accept and are already under memory pressure. At least + * let user know to retry. + */ + if (unlikely(!skb_queue_empty(&sk->sk_receive_queue))) { + copied = -EAGAIN; + goto out; + } + } + msg_bytes_ready: copied = sk_msg_recvmsg(sk, psock, msg, len, flags); /* The typical case for EFAULT is the socket was gracefully -- cgit 1.4.1 From dd628fc697ee59b76bd3877c4bd13f07ccc3776f Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:11 -0700 Subject: bpf, sockmap: Wake up polling after data copy [ Upstream commit 6df7f764cd3cf5a03a4a47b23be47e57e41fcd85 ] When TCP stack has data ready to read sk_data_ready() is called. Sockmap overwrites this with its own handler to call into BPF verdict program. But, the original TCP socket had sock_def_readable that would additionally wake up any user space waiters with sk_wake_async(). Sockmap saved the callback when the socket was created so call the saved data ready callback and then we can wake up any epoll() logic waiting on the read. Note we call on 'copied >= 0' to account for returning 0 when a FIN is received because we need to wake up user for this as well so they can do the recvmsg() -> 0 and detect the shutdown. Fixes: 04919bed948dc ("tcp: Introduce tcp_read_skb()") Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-8-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- net/core/skmsg.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index d3ffca1b9646..062612ee508c 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1196,10 +1196,19 @@ out: static void sk_psock_verdict_data_ready(struct sock *sk) { struct socket *sock = sk->sk_socket; + int copied; if (unlikely(!sock || !sock->ops || !sock->ops->read_skb)) return; - sock->ops->read_skb(sk, sk_psock_verdict_recv); + copied = sock->ops->read_skb(sk, sk_psock_verdict_recv); + if (copied >= 0) { + struct sk_psock *psock; + + rcu_read_lock(); + psock = sk_psock(sk); + psock->saved_data_ready(sk); + rcu_read_unlock(); + } } void sk_psock_start_verdict(struct sock *sk, struct sk_psock *psock) -- cgit 1.4.1 From fe735073a50eaa71aceb2e990827fe26f33cddd6 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 22 May 2023 19:56:12 -0700 Subject: bpf, sockmap: Incorrectly handling copied_seq [ Upstream commit e5c6de5fa025882babf89cecbed80acf49b987fa ] The read_skb() logic is incrementing the tcp->copied_seq which is used for among other things calculating how many outstanding bytes can be read by the application. This results in application errors, if the application does an ioctl(FIONREAD) we return zero because this is calculated from the copied_seq value. To fix this we move tcp->copied_seq accounting into the recv handler so that we update these when the recvmsg() hook is called and data is in fact copied into user buffers. This gives an accurate FIONREAD value as expected and improves ACK handling. Before we were calling the tcp_rcv_space_adjust() which would update 'number of bytes copied to user in last RTT' which is wrong for programs returning SK_PASS. The bytes are only copied to the user when recvmsg is handled. Doing the fix for recvmsg is straightforward, but fixing redirect and SK_DROP pkts is a bit tricker. Build a tcp_psock_eat() helper and then call this from skmsg handlers. This fixes another issue where a broken socket with a BPF program doing a resubmit could hang the receiver. This happened because although read_skb() consumed the skb through sock_drop() it did not update the copied_seq. Now if a single reccv socket is redirecting to many sockets (for example for lb) the receiver sk will be hung even though we might expect it to continue. The hang comes from not updating the copied_seq numbers and memory pressure resulting from that. We have a slight layer problem of calling tcp_eat_skb even if its not a TCP socket. To fix we could refactor and create per type receiver handlers. I decided this is more work than we want in the fix and we already have some small tweaks depending on caller that use the helper skb_bpf_strparser(). So we extend that a bit and always set the strparser bit when it is in use and then we can gate the seq_copied updates on this. Fixes: 04919bed948dc ("tcp: Introduce tcp_read_skb()") Signed-off-by: John Fastabend Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20230523025618.113937-9-john.fastabend@gmail.com Signed-off-by: Sasha Levin --- include/net/tcp.h | 10 ++++++++++ net/core/skmsg.c | 15 +++++++-------- net/ipv4/tcp.c | 10 +--------- net/ipv4/tcp_bpf.c | 28 +++++++++++++++++++++++++++- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 5b70b241ce71..0744717f5caa 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1467,6 +1467,8 @@ static inline void tcp_adjust_rcv_ssthresh(struct sock *sk) } void tcp_cleanup_rbuf(struct sock *sk, int copied); +void __tcp_cleanup_rbuf(struct sock *sk, int copied); + /* We provision sk_rcvbuf around 200% of sk_rcvlowat. * If 87.5 % (7/8) of the space has been consumed, we want to override @@ -2291,6 +2293,14 @@ int tcp_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool restore); void tcp_bpf_clone(const struct sock *sk, struct sock *newsk); #endif /* CONFIG_BPF_SYSCALL */ +#ifdef CONFIG_INET +void tcp_eat_skb(struct sock *sk, struct sk_buff *skb); +#else +static inline void tcp_eat_skb(struct sock *sk, struct sk_buff *skb) +{ +} +#endif + int tcp_bpf_sendmsg_redir(struct sock *sk, bool ingress, struct sk_msg *msg, u32 bytes, int flags); #endif /* CONFIG_NET_SOCK_MSG */ diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 062612ee508c..9e0f69451563 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -978,10 +978,8 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb, err = -EIO; sk_other = psock->sk; if (sock_flag(sk_other, SOCK_DEAD) || - !sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) { - skb_bpf_redirect_clear(skb); + !sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) goto out_free; - } skb_bpf_set_ingress(skb); @@ -1010,18 +1008,19 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb, err = 0; } spin_unlock_bh(&psock->ingress_lock); - if (err < 0) { - skb_bpf_redirect_clear(skb); + if (err < 0) goto out_free; - } } break; case __SK_REDIRECT: + tcp_eat_skb(psock->sk, skb); err = sk_psock_skb_redirect(psock, skb); break; case __SK_DROP: default: out_free: + skb_bpf_redirect_clear(skb); + tcp_eat_skb(psock->sk, skb); sock_drop(psock->sk, skb); } @@ -1066,8 +1065,7 @@ static void sk_psock_strp_read(struct strparser *strp, struct sk_buff *skb) skb_dst_drop(skb); skb_bpf_redirect_clear(skb); ret = bpf_prog_run_pin_on_cpu(prog, skb); - if (ret == SK_PASS) - skb_bpf_set_strparser(skb); + skb_bpf_set_strparser(skb); ret = sk_psock_map_verd(ret, skb_bpf_redirect_fetch(skb)); skb->sk = NULL; } @@ -1173,6 +1171,7 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) psock = sk_psock(sk); if (unlikely(!psock)) { len = 0; + tcp_eat_skb(sk, skb); sock_drop(sk, skb); goto out; } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 31156ebb759c..021a8bf6a189 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1570,7 +1570,7 @@ static int tcp_peek_sndq(struct sock *sk, struct msghdr *msg, int len) * calculation of whether or not we must ACK for the sake of * a window update. */ -static void __tcp_cleanup_rbuf(struct sock *sk, int copied) +void __tcp_cleanup_rbuf(struct sock *sk, int copied) { struct tcp_sock *tp = tcp_sk(sk); bool time_to_ack = false; @@ -1785,14 +1785,6 @@ int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor) break; } } - WRITE_ONCE(tp->copied_seq, seq); - - tcp_rcv_space_adjust(sk); - - /* Clean up data we have read: This will do ACK frames. */ - if (copied > 0) - __tcp_cleanup_rbuf(sk, copied); - return copied; } EXPORT_SYMBOL(tcp_read_skb); diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 01dd76be1a58..5f93918c063c 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -11,6 +11,24 @@ #include #include +void tcp_eat_skb(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_sock *tcp; + int copied; + + if (!skb || !skb->len || !sk_is_tcp(sk)) + return; + + if (skb_bpf_strparser(skb)) + return; + + tcp = tcp_sk(sk); + copied = tcp->copied_seq + skb->len; + WRITE_ONCE(tcp->copied_seq, copied); + tcp_rcv_space_adjust(sk); + __tcp_cleanup_rbuf(sk, skb->len); +} + static int bpf_tcp_ingress(struct sock *sk, struct sk_psock *psock, struct sk_msg *msg, u32 apply_bytes, int flags) { @@ -198,8 +216,10 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, int flags, int *addr_len) { + struct tcp_sock *tcp = tcp_sk(sk); + u32 seq = tcp->copied_seq; struct sk_psock *psock; - int copied; + int copied = 0; if (unlikely(flags & MSG_ERRQUEUE)) return inet_recv_error(sk, msg, len, addr_len); @@ -244,9 +264,11 @@ msg_bytes_ready: if (is_fin) { copied = 0; + seq++; goto out; } } + seq += copied; if (!copied) { long timeo; int data; @@ -284,6 +306,10 @@ msg_bytes_ready: copied = -EAGAIN; } out: + WRITE_ONCE(tcp->copied_seq, seq); + tcp_rcv_space_adjust(sk); + if (copied > 0) + __tcp_cleanup_rbuf(sk, copied); release_sock(sk); sk_psock_put(sk, psock); return copied; -- cgit 1.4.1 From 6793a3c6326eb3769a99e52908b22df8786776f9 Mon Sep 17 00:00:00 2001 From: Tian Lan Date: Mon, 22 May 2023 17:05:55 -0400 Subject: blk-mq: fix race condition in active queue accounting [ Upstream commit 3e94d54e83cafd2b562bb6d15bb2f72d76200fb5 ] If multiple CPUs are sharing the same hardware queue, it can cause leak in the active queue counter tracking when __blk_mq_tag_busy() is executed simultaneously. Fixes: ee78ec1077d3 ("blk-mq: blk_mq_tag_busy is no need to return a value") Signed-off-by: Tian Lan Reviewed-by: Ming Lei Reviewed-by: Damien Le Moal Reviewed-by: John Garry Link: https://lore.kernel.org/r/20230522210555.794134-1-tilan7663@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/blk-mq-tag.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 9eb968e14d31..a80d7c62bdfe 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -41,16 +41,20 @@ void __blk_mq_tag_busy(struct blk_mq_hw_ctx *hctx) { unsigned int users; + /* + * calling test_bit() prior to test_and_set_bit() is intentional, + * it avoids dirtying the cacheline if the queue is already active. + */ if (blk_mq_is_shared_tags(hctx->flags)) { struct request_queue *q = hctx->queue; - if (test_bit(QUEUE_FLAG_HCTX_ACTIVE, &q->queue_flags)) + if (test_bit(QUEUE_FLAG_HCTX_ACTIVE, &q->queue_flags) || + test_and_set_bit(QUEUE_FLAG_HCTX_ACTIVE, &q->queue_flags)) return; - set_bit(QUEUE_FLAG_HCTX_ACTIVE, &q->queue_flags); } else { - if (test_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state)) + if (test_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state) || + test_and_set_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state)) return; - set_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state); } users = atomic_inc_return(&hctx->tags->active_queues); -- cgit 1.4.1 From cd3c5e4e0d60630c5aae4480204a4a083a3d2503 Mon Sep 17 00:00:00 2001 From: Yan Zhao Date: Fri, 19 May 2023 14:58:43 +0800 Subject: vfio/type1: check pfn valid before converting to struct page [ Upstream commit 4752354af71043e6fd72ef5490ed6da39e6cab4a ] Check physical PFN is valid before converting the PFN to a struct page pointer to be returned to caller of vfio_pin_pages(). vfio_pin_pages() pins user pages with contiguous IOVA. If the IOVA of a user page to be pinned belongs to vma of vm_flags VM_PFNMAP, pin_user_pages_remote() will return -EFAULT without returning struct page address for this PFN. This is because usually this kind of PFN (e.g. MMIO PFN) has no valid struct page address associated. Upon this error, vaddr_get_pfns() will obtain the physical PFN directly. While previously vfio_pin_pages() returns to caller PFN arrays directly, after commit 34a255e67615 ("vfio: Replace phys_pfn with pages for vfio_pin_pages()"), PFNs will be converted to "struct page *" unconditionally and therefore the returned "struct page *" array may contain invalid struct page addresses. Given current in-tree users of vfio_pin_pages() only expect "struct page * returned, check PFN validity and return -EINVAL to let the caller be aware of IOVAs to be pinned containing PFN not able to be returned in "struct page *" array. So that, the caller will not consume the returned pointer (e.g. test PageReserved()) and avoid error like "supervisor read access in kernel mode". Fixes: 34a255e67615 ("vfio: Replace phys_pfn with pages for vfio_pin_pages()") Cc: Sean Christopherson Reviewed-by: Jason Gunthorpe Signed-off-by: Yan Zhao Reviewed-by: Sean Christopherson Link: https://lore.kernel.org/r/20230519065843.10653-1-yan.y.zhao@intel.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- drivers/vfio/vfio_iommu_type1.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 7fa68dc4e938..009ba186652a 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -936,6 +936,11 @@ again: if (ret) goto pin_unwind; + if (!pfn_valid(phys_pfn)) { + ret = -EINVAL; + goto pin_unwind; + } + ret = vfio_add_to_pfn_list(dma, iova, phys_pfn); if (ret) { if (put_pfn(phys_pfn, dma->prot) && do_accounting) -- cgit 1.4.1 From 7dccd5fa7edb9562da61af820bb9d0346447baf1 Mon Sep 17 00:00:00 2001 From: Qingfang DENG Date: Fri, 3 Feb 2023 09:16:11 +0800 Subject: net: page_pool: use in_softirq() instead [ Upstream commit 542bcea4be866b14b3a5c8e90773329066656c43 ] We use BH context only for synchronization, so we don't care if it's actually serving softirq or not. As a side node, in case of threaded NAPI, in_serving_softirq() will return false because it's in process context with BH off, making page_pool_recycle_in_cache() unreachable. Signed-off-by: Qingfang DENG Tested-by: Felix Fietkau Signed-off-by: David S. Miller Stable-dep-of: 368d3cb406cd ("page_pool: fix inconsistency for page_pool_ring_[un]lock()") Signed-off-by: Sasha Levin --- include/net/page_pool.h | 4 ++-- net/core/page_pool.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/net/page_pool.h b/include/net/page_pool.h index 813c93499f20..34bf531ffc8d 100644 --- a/include/net/page_pool.h +++ b/include/net/page_pool.h @@ -386,7 +386,7 @@ static inline void page_pool_nid_changed(struct page_pool *pool, int new_nid) static inline void page_pool_ring_lock(struct page_pool *pool) __acquires(&pool->ring.producer_lock) { - if (in_serving_softirq()) + if (in_softirq()) spin_lock(&pool->ring.producer_lock); else spin_lock_bh(&pool->ring.producer_lock); @@ -395,7 +395,7 @@ static inline void page_pool_ring_lock(struct page_pool *pool) static inline void page_pool_ring_unlock(struct page_pool *pool) __releases(&pool->ring.producer_lock) { - if (in_serving_softirq()) + if (in_softirq()) spin_unlock(&pool->ring.producer_lock); else spin_unlock_bh(&pool->ring.producer_lock); diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 9b203d8660e4..193c18799865 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -511,8 +511,8 @@ static void page_pool_return_page(struct page_pool *pool, struct page *page) static bool page_pool_recycle_in_ring(struct page_pool *pool, struct page *page) { int ret; - /* BH protection not needed if current is serving softirq */ - if (in_serving_softirq()) + /* BH protection not needed if current is softirq */ + if (in_softirq()) ret = ptr_ring_produce(&pool->ring, page); else ret = ptr_ring_produce_bh(&pool->ring, page); @@ -570,7 +570,7 @@ __page_pool_put_page(struct page_pool *pool, struct page *page, page_pool_dma_sync_for_device(pool, page, dma_sync_size); - if (allow_direct && in_serving_softirq() && + if (allow_direct && in_softirq() && page_pool_recycle_in_cache(page, pool)) return NULL; -- cgit 1.4.1 From 7c95f56995c613ad821b7a2cd673af94eb2a8627 Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Mon, 22 May 2023 11:17:14 +0800 Subject: page_pool: fix inconsistency for page_pool_ring_[un]lock() [ Upstream commit 368d3cb406cdd074d1df2ad9ec06d1bfcb664882 ] page_pool_ring_[un]lock() use in_softirq() to decide which spin lock variant to use, and when they are called in the context with in_softirq() being false, spin_lock_bh() is called in page_pool_ring_lock() while spin_unlock() is called in page_pool_ring_unlock(), because spin_lock_bh() has disabled the softirq in page_pool_ring_lock(), which causes inconsistency for spin lock pair calling. This patch fixes it by returning in_softirq state from page_pool_producer_lock(), and use it to decide which spin lock variant to use in page_pool_producer_unlock(). As pool->ring has both producer and consumer lock, so rename it to page_pool_producer_[un]lock() to reflect the actual usage. Also move them to page_pool.c as they are only used there, and remove the 'inline' as the compiler may have better idea to do inlining or not. Fixes: 7886244736a4 ("net: page_pool: Add bulk support for ptr_ring") Signed-off-by: Yunsheng Lin Acked-by: Jesper Dangaard Brouer Acked-by: Ilias Apalodimas Link: https://lore.kernel.org/r/20230522031714.5089-1-linyunsheng@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/page_pool.h | 18 ------------------ net/core/page_pool.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/include/net/page_pool.h b/include/net/page_pool.h index 34bf531ffc8d..ad0bafc877d4 100644 --- a/include/net/page_pool.h +++ b/include/net/page_pool.h @@ -383,22 +383,4 @@ static inline void page_pool_nid_changed(struct page_pool *pool, int new_nid) page_pool_update_nid(pool, new_nid); } -static inline void page_pool_ring_lock(struct page_pool *pool) - __acquires(&pool->ring.producer_lock) -{ - if (in_softirq()) - spin_lock(&pool->ring.producer_lock); - else - spin_lock_bh(&pool->ring.producer_lock); -} - -static inline void page_pool_ring_unlock(struct page_pool *pool) - __releases(&pool->ring.producer_lock) -{ - if (in_softirq()) - spin_unlock(&pool->ring.producer_lock); - else - spin_unlock_bh(&pool->ring.producer_lock); -} - #endif /* _NET_PAGE_POOL_H */ diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 193c18799865..2396c99bedea 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -133,6 +133,29 @@ EXPORT_SYMBOL(page_pool_ethtool_stats_get); #define recycle_stat_add(pool, __stat, val) #endif +static bool page_pool_producer_lock(struct page_pool *pool) + __acquires(&pool->ring.producer_lock) +{ + bool in_softirq = in_softirq(); + + if (in_softirq) + spin_lock(&pool->ring.producer_lock); + else + spin_lock_bh(&pool->ring.producer_lock); + + return in_softirq; +} + +static void page_pool_producer_unlock(struct page_pool *pool, + bool in_softirq) + __releases(&pool->ring.producer_lock) +{ + if (in_softirq) + spin_unlock(&pool->ring.producer_lock); + else + spin_unlock_bh(&pool->ring.producer_lock); +} + static int page_pool_init(struct page_pool *pool, const struct page_pool_params *params) { @@ -615,6 +638,7 @@ void page_pool_put_page_bulk(struct page_pool *pool, void **data, int count) { int i, bulk_len = 0; + bool in_softirq; for (i = 0; i < count; i++) { struct page *page = virt_to_head_page(data[i]); @@ -633,7 +657,7 @@ void page_pool_put_page_bulk(struct page_pool *pool, void **data, return; /* Bulk producer into ptr_ring page_pool cache */ - page_pool_ring_lock(pool); + in_softirq = page_pool_producer_lock(pool); for (i = 0; i < bulk_len; i++) { if (__ptr_ring_produce(&pool->ring, data[i])) { /* ring full */ @@ -642,7 +666,7 @@ void page_pool_put_page_bulk(struct page_pool *pool, void **data, } } recycle_stat_add(pool, ring, i); - page_pool_ring_unlock(pool); + page_pool_producer_unlock(pool, in_softirq); /* Hopefully all pages was return into ptr_ring */ if (likely(i == bulk_len)) -- cgit 1.4.1 From 07c8c1a3cfcfd327ca66f1cb9363d75a286443ef Mon Sep 17 00:00:00 2001 From: David Epping Date: Tue, 23 May 2023 17:31:08 +0200 Subject: net: phy: mscc: enable VSC8501/2 RGMII RX clock [ Upstream commit 71460c9ec5c743e9ffffca3c874d66267c36345e ] By default the VSC8501 and VSC8502 RGMII/GMII/MII RX_CLK output is disabled. To allow packet forwarding towards the MAC it needs to be enabled. For other PHYs supported by this driver the clock output is enabled by default. Fixes: d3169863310d ("net: phy: mscc: add support for VSC8502") Signed-off-by: David Epping Reviewed-by: Russell King (Oracle) Reviewed-by: Vladimir Oltean Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/mscc/mscc.h | 1 + drivers/net/phy/mscc/mscc_main.c | 54 +++++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h index a50235fdf7d9..055e4ca5b3b5 100644 --- a/drivers/net/phy/mscc/mscc.h +++ b/drivers/net/phy/mscc/mscc.h @@ -179,6 +179,7 @@ enum rgmii_clock_delay { #define VSC8502_RGMII_CNTL 20 #define VSC8502_RGMII_RX_DELAY_MASK 0x0070 #define VSC8502_RGMII_TX_DELAY_MASK 0x0007 +#define VSC8502_RGMII_RX_CLK_DISABLE 0x0800 #define MSCC_PHY_WOL_LOWER_MAC_ADDR 21 #define MSCC_PHY_WOL_MID_MAC_ADDR 22 diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index f778e4f8b508..7bd940baec59 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -527,14 +527,27 @@ out_unlock: * * 2.0 ns (which causes the data to be sampled at exactly half way between * clock transitions at 1000 Mbps) if delays should be enabled */ -static int vsc85xx_rgmii_set_skews(struct phy_device *phydev, u32 rgmii_cntl, - u16 rgmii_rx_delay_mask, - u16 rgmii_tx_delay_mask) +static int vsc85xx_update_rgmii_cntl(struct phy_device *phydev, u32 rgmii_cntl, + u16 rgmii_rx_delay_mask, + u16 rgmii_tx_delay_mask) { u16 rgmii_rx_delay_pos = ffs(rgmii_rx_delay_mask) - 1; u16 rgmii_tx_delay_pos = ffs(rgmii_tx_delay_mask) - 1; u16 reg_val = 0; - int rc; + u16 mask = 0; + int rc = 0; + + /* For traffic to pass, the VSC8502 family needs the RX_CLK disable bit + * to be unset for all PHY modes, so do that as part of the paged + * register modification. + * For some family members (like VSC8530/31/40/41) this bit is reserved + * and read-only, and the RX clock is enabled by default. + */ + if (rgmii_cntl == VSC8502_RGMII_CNTL) + mask |= VSC8502_RGMII_RX_CLK_DISABLE; + + if (phy_interface_is_rgmii(phydev)) + mask |= rgmii_rx_delay_mask | rgmii_tx_delay_mask; mutex_lock(&phydev->lock); @@ -545,10 +558,9 @@ static int vsc85xx_rgmii_set_skews(struct phy_device *phydev, u32 rgmii_cntl, phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) reg_val |= RGMII_CLK_DELAY_2_0_NS << rgmii_tx_delay_pos; - rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED_2, - rgmii_cntl, - rgmii_rx_delay_mask | rgmii_tx_delay_mask, - reg_val); + if (mask) + rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED_2, + rgmii_cntl, mask, reg_val); mutex_unlock(&phydev->lock); @@ -557,19 +569,11 @@ static int vsc85xx_rgmii_set_skews(struct phy_device *phydev, u32 rgmii_cntl, static int vsc85xx_default_config(struct phy_device *phydev) { - int rc; - phydev->mdix_ctrl = ETH_TP_MDI_AUTO; - if (phy_interface_mode_is_rgmii(phydev->interface)) { - rc = vsc85xx_rgmii_set_skews(phydev, VSC8502_RGMII_CNTL, - VSC8502_RGMII_RX_DELAY_MASK, - VSC8502_RGMII_TX_DELAY_MASK); - if (rc) - return rc; - } - - return 0; + return vsc85xx_update_rgmii_cntl(phydev, VSC8502_RGMII_CNTL, + VSC8502_RGMII_RX_DELAY_MASK, + VSC8502_RGMII_TX_DELAY_MASK); } static int vsc85xx_get_tunable(struct phy_device *phydev, @@ -1766,13 +1770,11 @@ static int vsc8584_config_init(struct phy_device *phydev) if (ret) return ret; - if (phy_interface_is_rgmii(phydev)) { - ret = vsc85xx_rgmii_set_skews(phydev, VSC8572_RGMII_CNTL, - VSC8572_RGMII_RX_DELAY_MASK, - VSC8572_RGMII_TX_DELAY_MASK); - if (ret) - return ret; - } + ret = vsc85xx_update_rgmii_cntl(phydev, VSC8572_RGMII_CNTL, + VSC8572_RGMII_RX_DELAY_MASK, + VSC8572_RGMII_TX_DELAY_MASK); + if (ret) + return ret; ret = genphy_soft_reset(phydev); if (ret) -- cgit 1.4.1 From b5ceb6aac60788607662077a6a12057f8aec084e Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 6 Apr 2023 15:28:41 +0800 Subject: wifi: rtw89: correct 5 MHz mask setting [ Upstream commit d33fc8d0368c180fe2338bfae4f5367a66a719f4 ] Use primary channel index to determine which 5 MHz mask should be enable. This mask is used to prevent noise from channel edge to effect CCA threshold in wide bandwidth (>= 40 MHZ). Fixes: 1b00e9236a71 ("rtw89: 8852c: add set channel of BB part") Fixes: 6b0698984eb0 ("wifi: rtw89: 8852b: add chip_ops::set_channel") Cc: stable@vger.kernel.org Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230406072841.8308-1-pkshih@realtek.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 67653b3e1a35..3109114cec6f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -1484,18 +1484,19 @@ static void rtw8852c_5m_mask(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { - u8 pri_ch = chan->primary_channel; + u8 pri_ch = chan->pri_ch_idx; bool mask_5m_low; bool mask_5m_en; switch (chan->band_width) { case RTW89_CHANNEL_WIDTH_40: mask_5m_en = true; - mask_5m_low = pri_ch == 2; + mask_5m_low = pri_ch == RTW89_SC_20_LOWER; break; case RTW89_CHANNEL_WIDTH_80: - mask_5m_en = ((pri_ch == 3) || (pri_ch == 4)); - mask_5m_low = pri_ch == 4; + mask_5m_en = pri_ch == RTW89_SC_20_UPMOST || + pri_ch == RTW89_SC_20_LOWEST; + mask_5m_low = pri_ch == RTW89_SC_20_LOWEST; break; default: mask_5m_en = false; -- cgit 1.4.1 From 0c469078bde4dd60aea5111cf8f3b35cb5aa45c2 Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Thu, 13 Apr 2023 21:40:27 +0300 Subject: wifi: iwlwifi: mvm: support wowlan info notification version 2 [ Upstream commit 905d50ddbc83bef0d7f3386e7f3472b0324b405b ] As part of version 2 we don't need to have wake_packet_bufsize and wake_packet_length. The first one is already calculated by the driver, the latter is sent as part of the wake packet notification. Signed-off-by: Haim Dreyfuss Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230413213309.3b53213b10d4.Ibf2f15aca614def2d262dd267d1aad65931b58f1@changeid Signed-off-by: Johannes Berg Stable-dep-of: 457d7fb03e6c ("wifi: iwlwifi: mvm: fix potential memory leak") Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/fw/api/d3.h | 37 ++++++++++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 35 +++++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index df0833890e55..8a613e150a02 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -767,7 +767,7 @@ struct iwl_wowlan_status_v12 { } __packed; /* WOWLAN_STATUSES_RSP_API_S_VER_12 */ /** - * struct iwl_wowlan_info_notif - WoWLAN information notification + * struct iwl_wowlan_info_notif_v1 - WoWLAN information notification * @gtk: GTK data * @igtk: IGTK data * @replay_ctr: GTK rekey replay counter @@ -785,7 +785,7 @@ struct iwl_wowlan_status_v12 { * @station_id: station id * @reserved2: reserved */ -struct iwl_wowlan_info_notif { +struct iwl_wowlan_info_notif_v1 { struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM]; struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM]; __le64 replay_ctr; @@ -803,6 +803,39 @@ struct iwl_wowlan_info_notif { u8 reserved2[2]; } __packed; /* WOWLAN_INFO_NTFY_API_S_VER_1 */ +/** + * struct iwl_wowlan_info_notif - WoWLAN information notification + * @gtk: GTK data + * @igtk: IGTK data + * @replay_ctr: GTK rekey replay counter + * @pattern_number: number of the matched patterns + * @reserved1: reserved + * @qos_seq_ctr: QoS sequence counters to use next + * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason + * @num_of_gtk_rekeys: number of GTK rekeys + * @transmitted_ndps: number of transmitted neighbor discovery packets + * @received_beacons: number of received beacons + * @tid_tear_down: bit mask of tids whose BA sessions were closed + * in suspend state + * @station_id: station id + * @reserved2: reserved + */ +struct iwl_wowlan_info_notif { + struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM]; + struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM]; + __le64 replay_ctr; + __le16 pattern_number; + __le16 reserved1; + __le16 qos_seq_ctr[8]; + __le32 wakeup_reasons; + __le32 num_of_gtk_rekeys; + __le32 transmitted_ndps; + __le32 received_beacons; + u8 tid_tear_down; + u8 station_id; + u8 reserved2[2]; +} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_2 */ + /** * struct iwl_wowlan_wake_pkt_notif - WoWLAN wake packet notification * @wake_packet_length: wakeup packet length diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index bbdda3e1ff3f..0253dedb9b71 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2011,6 +2011,12 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, { u32 i; + if (!data) { + IWL_ERR(mvm, "iwl_wowlan_info_notif data is NULL\n"); + status = NULL; + return; + } + if (len < sizeof(*data)) { IWL_ERR(mvm, "Invalid WoWLAN info notification!\n"); status = NULL; @@ -2698,10 +2704,33 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, struct iwl_d3_data *d3_data = data; u32 len; int ret; + int wowlan_info_ver = iwl_fw_lookup_notif_ver(mvm->fw, + PROT_OFFLOAD_GROUP, + WOWLAN_INFO_NOTIFICATION, + IWL_FW_CMD_VER_UNKNOWN); + switch (WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) { case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION): { - struct iwl_wowlan_info_notif *notif = (void *)pkt->data; + struct iwl_wowlan_info_notif *notif; + + if (wowlan_info_ver < 2) { + struct iwl_wowlan_info_notif_v1 *notif_v1 = (void *)pkt->data; + + notif = kmemdup(notif_v1, + offsetofend(struct iwl_wowlan_info_notif, + received_beacons), + GFP_ATOMIC); + + if (!notif) + return false; + + notif->tid_tear_down = notif_v1->tid_tear_down; + notif->station_id = notif_v1->station_id; + + } else { + notif = (void *)pkt->data; + } if (d3_data->notif_received & IWL_D3_NOTIF_WOWLAN_INFO) { /* We might get two notifications due to dual bss */ @@ -2714,6 +2743,10 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, len = iwl_rx_packet_payload_len(pkt); iwl_mvm_parse_wowlan_info_notif(mvm, notif, d3_data->status, len); + + if (wowlan_info_ver < 2) + kfree(notif); + if (d3_data->status && d3_data->status->wakeup_reasons & IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT) /* We are supposed to get also wake packet notif */ -- cgit 1.4.1 From 592af07ac0c87c103f321d0b69768997cc00ffc4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Apr 2023 12:28:08 +0300 Subject: wifi: iwlwifi: mvm: fix potential memory leak [ Upstream commit 457d7fb03e6c3d73fbb509bd85fc4b02d1ab405e ] If we do get multiple notifications from firmware, then we might have allocated 'notif', but don't free it. Fix that by checking for duplicates before allocation. Fixes: 4da46a06d443 ("wifi: iwlwifi: mvm: Add support for wowlan info notification") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230418122405.116758321cc4.I8bdbcbb38c89ac637eaa20dda58fa9165b25893a@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 0253dedb9b71..c876e81437fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2714,6 +2714,13 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION): { struct iwl_wowlan_info_notif *notif; + if (d3_data->notif_received & IWL_D3_NOTIF_WOWLAN_INFO) { + /* We might get two notifications due to dual bss */ + IWL_DEBUG_WOWLAN(mvm, + "Got additional wowlan info notification\n"); + break; + } + if (wowlan_info_ver < 2) { struct iwl_wowlan_info_notif_v1 *notif_v1 = (void *)pkt->data; @@ -2732,13 +2739,6 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, notif = (void *)pkt->data; } - if (d3_data->notif_received & IWL_D3_NOTIF_WOWLAN_INFO) { - /* We might get two notifications due to dual bss */ - IWL_DEBUG_WOWLAN(mvm, - "Got additional wowlan info notification\n"); - break; - } - d3_data->notif_received |= IWL_D3_NOTIF_WOWLAN_INFO; len = iwl_rx_packet_payload_len(pkt); iwl_mvm_parse_wowlan_info_notif(mvm, notif, d3_data->status, -- cgit 1.4.1 From 3236221bb8e4de8e3d0c8385f634064fb26b8e38 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Thu, 13 Apr 2023 18:11:15 +0800 Subject: RDMA/rxe: Fix the error "trying to register non-static key in rxe_cleanup_task" [ Upstream commit b2b1ddc457458fecd1c6f385baa9fbda5f0c63ad ] In the function rxe_create_qp(), rxe_qp_from_init() is called to initialize qp, internally things like rxe_init_task are not setup until rxe_qp_init_req(). If an error occurred before this point then the unwind will call rxe_cleanup() and eventually to rxe_qp_do_cleanup()/rxe_cleanup_task() which will oops when trying to access the uninitialized spinlock. If rxe_init_task is not executed, rxe_cleanup_task will not be called. Reported-by: syzbot+cfcc1a3c85be15a40cba@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?id=fd85757b74b3eb59f904138486f755f71e090df8 Fixes: 8700e3e7c485 ("Soft RoCE driver") Fixes: 2d4b21e0a291 ("IB/rxe: Prevent from completer to operate on non valid QP") Signed-off-by: Zhu Yanjun Link: https://lore.kernel.org/r/20230413101115.1366068-1-yanjun.zhu@intel.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_qp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index e459fb542b83..1f6e006c51c4 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -792,8 +792,11 @@ static void rxe_qp_do_cleanup(struct work_struct *work) del_timer_sync(&qp->rnr_nak_timer); } - rxe_cleanup_task(&qp->req.task); - rxe_cleanup_task(&qp->comp.task); + if (qp->req.task.func) + rxe_cleanup_task(&qp->req.task); + + if (qp->comp.task.func) + rxe_cleanup_task(&qp->comp.task); /* flush out any receive wr's or pending requests */ if (qp->req.task.func) -- cgit 1.4.1 From 83a7f27c5b94e43f29f8216a32790751139aa61e Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Wed, 3 May 2023 12:39:38 +0530 Subject: octeontx2-af: Add validation for lmac type [ Upstream commit cb5edce271764524b88b1a6866b3e626686d9a33 ] Upon physical link change, firmware reports to the kernel about the change along with the details like speed, lmac_type_id, etc. Kernel derives lmac_type based on lmac_type_id received from firmware. In a few scenarios, firmware returns an invalid lmac_type_id, which is resulting in below kernel panic. This patch adds the missing validation of the lmac_type_id field. Internal error: Oops: 96000005 [#1] PREEMPT SMP [ 35.321595] Modules linked in: [ 35.328982] CPU: 0 PID: 31 Comm: kworker/0:1 Not tainted 5.4.210-g2e3169d8e1bc-dirty #17 [ 35.337014] Hardware name: Marvell CN103XX board (DT) [ 35.344297] Workqueue: events work_for_cpu_fn [ 35.352730] pstate: 40400089 (nZcv daIf +PAN -UAO) [ 35.360267] pc : strncpy+0x10/0x30 [ 35.366595] lr : cgx_link_change_handler+0x90/0x180 Fixes: 61071a871ea6 ("octeontx2-af: Forward CGX link notifications to PFs") Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Kovvuri Goutham Signed-off-by: Sai Krishna Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 8fdd3afe5998..afdddfced7e6 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -1225,6 +1225,14 @@ static inline void link_status_user_format(u64 lstat, linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat); linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat); linfo->lmac_type_id = cgx_get_lmac_type(cgx, lmac_id); + + if (linfo->lmac_type_id >= LMAC_MODE_MAX) { + dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d", + linfo->lmac_type_id, cgx->cgx_id, lmac_id); + strncpy(linfo->lmac_type, "Unknown", LMACTYPE_STR_LEN - 1); + return; + } + lmac_string = cgx_lmactype_string[linfo->lmac_type_id]; strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1); } -- cgit 1.4.1 From 6c1fad655b40b0de6ddad30d9c57e61046df262c Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 13 Feb 2023 15:10:30 -0600 Subject: drm/amd: Don't allow s0ix on APUs older than Raven commit ca47518663973083c513cd6b2801dcda0bfaaa99 upstream. APUs before Raven didn't support s0ix. As we just relieved some of the safety checks for s0ix to improve power consumption on APUs that support it but that are missing BIOS support a new blind spot was introduced that a user could "try" to run s0ix. Plug this hole so that if users try to run s0ix on anything older than Raven it will just skip suspend of the GPU. Fixes: cf488dcd0ab7 ("drm/amd: Allow s0ix without BIOS support") Suggested-by: Alexander Deucher Signed-off-by: Mario Limonciello Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c | 3 +++ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c index a78e80f9f65c..e32bd990800d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c @@ -1083,6 +1083,9 @@ bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev) (pm_suspend_target_state != PM_SUSPEND_TO_IDLE)) return false; + if (adev->asic_type < CHIP_RAVEN) + return false; + /* * If ACPI_FADT_LOW_POWER_S0 is not set in the FADT, it is generally * risky to do any special firmware-related preparations for entering diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 871f481f8432..88a9ece7f464 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2402,8 +2402,10 @@ static int amdgpu_pmops_suspend(struct device *dev) if (amdgpu_acpi_is_s0ix_active(adev)) adev->in_s0ix = true; - else + else if (amdgpu_acpi_is_s3_active(adev)) adev->in_s3 = true; + if (!adev->in_s0ix && !adev->in_s3) + return 0; return amdgpu_device_suspend(drm_dev, true); } @@ -2424,6 +2426,9 @@ static int amdgpu_pmops_resume(struct device *dev) struct amdgpu_device *adev = drm_to_adev(drm_dev); int r; + if (!adev->in_s0ix && !adev->in_s3) + return 0; + /* Avoids registers access if device is physically gone */ if (!pci_device_is_present(adev->pdev)) adev->no_hw_access = true; -- cgit 1.4.1 From 2333dbc88f3840d9255af4e0327e32afb54aea76 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Sun, 16 Apr 2023 16:02:51 +0800 Subject: bluetooth: Add cmd validity checks at the start of hci_sock_ioctl() commit 000c2fa2c144c499c881a101819cf1936a1f7cf2 upstream. Previously, channel open messages were always sent to monitors on the first ioctl() call for unbound HCI sockets, even if the command and arguments were completely invalid. This can leave an exploitable hole with the abuse of invalid ioctl calls. This commit hardens the ioctl processing logic by first checking if the command is valid, and immediately returning with an ENOIOCTLCMD error code if it is not. This ensures that ioctl calls with invalid commands are free of side effects, and increases the difficulty of further exploitation by forcing exploitation to find a way to pass a valid command first. Signed-off-by: Ruihan Li Co-developed-by: Marcel Holtmann Signed-off-by: Marcel Holtmann Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Dragos-Marian Panait Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hci_sock.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index f597fe0db9f8..1d249d839819 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -987,6 +987,34 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, BT_DBG("cmd %x arg %lx", cmd, arg); + /* Make sure the cmd is valid before doing anything */ + switch (cmd) { + case HCIGETDEVLIST: + case HCIGETDEVINFO: + case HCIGETCONNLIST: + case HCIDEVUP: + case HCIDEVDOWN: + case HCIDEVRESET: + case HCIDEVRESTAT: + case HCISETSCAN: + case HCISETAUTH: + case HCISETENCRYPT: + case HCISETPTYPE: + case HCISETLINKPOL: + case HCISETLINKMODE: + case HCISETACLMTU: + case HCISETSCOMTU: + case HCIINQUIRY: + case HCISETRAW: + case HCIGETCONNINFO: + case HCIGETAUTHINFO: + case HCIBLOCKADDR: + case HCIUNBLOCKADDR: + break; + default: + return -ENOIOCTLCMD; + } + lock_sock(sk); if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { -- cgit 1.4.1 From 5d08604754e843bad0eeb6ea2dd03ca4a2278752 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 1 Jun 2023 09:48:03 +0300 Subject: Revert "thermal/drivers/mellanox: Use generic thermal_zone_get_trip() function" This reverts commit a71f388045edbf6788a61f43e2cdc94b392a4ea3. Commit a71f388045ed ("thermal/drivers/mellanox: Use generic thermal_zone_get_trip() function") was backported as a dependency of the fix in upstream commit 6d206b1ea9f4 ("mlxsw: core_thermal: Fix fan speed in maximum cooling state"). However, it is dependent on changes in the thermal core that were merged in v6.3. Without them, the mlxsw driver is unable to register its thermal zone: mlxsw_spectrum 0000:03:00.0: Failed to register thermal zone mlxsw_spectrum 0000:03:00.0: cannot register bus device mlxsw_spectrum: probe of 0000:03:00.0 failed with error -22 Fix this by reverting this commit and instead fix the small conflict with the above mentioned fix. Tested using the test case mentioned in the change log of the fix: # cat /sys/class/thermal/thermal_zone2/cdev0/type mlxsw_fan # echo 10 > /sys/class/thermal/thermal_zone2/cdev0/cur_state # cat /sys/class/hwmon/hwmon1/name mlxsw # cat /sys/class/hwmon/hwmon1/pwm1 255 After setting the fan to its maximum cooling state (10), it operates at 100% duty cycle instead of being stuck at 0 RPM. Fixes: a71f388045ed ("thermal/drivers/mellanox: Use generic thermal_zone_get_trip() function") Reported-by: Joe Botha Tested-by: Joe Botha Signed-off-by: Ido Schimmel Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlxsw/core_thermal.c | 209 ++++++++++++++++----- 1 file changed, 161 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c index 09ed6e5fa6c3..ef5e61708df3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c @@ -36,39 +36,33 @@ enum mlxsw_thermal_trips { MLXSW_THERMAL_TEMP_TRIP_HOT, }; -struct mlxsw_cooling_states { +struct mlxsw_thermal_trip { + int type; + int temp; + int hyst; int min_state; int max_state; }; -static const struct thermal_trip default_thermal_trips[] = { +static const struct mlxsw_thermal_trip default_thermal_trips[] = { { /* In range - 0-40% PWM */ .type = THERMAL_TRIP_ACTIVE, - .temperature = MLXSW_THERMAL_ASIC_TEMP_NORM, - .hysteresis = MLXSW_THERMAL_HYSTERESIS_TEMP, - }, - { - /* In range - 40-100% PWM */ - .type = THERMAL_TRIP_ACTIVE, - .temperature = MLXSW_THERMAL_ASIC_TEMP_HIGH, - .hysteresis = MLXSW_THERMAL_HYSTERESIS_TEMP, - }, - { /* Warning */ - .type = THERMAL_TRIP_HOT, - .temperature = MLXSW_THERMAL_ASIC_TEMP_HOT, - }, -}; - -static const struct mlxsw_cooling_states default_cooling_states[] = { - { + .temp = MLXSW_THERMAL_ASIC_TEMP_NORM, + .hyst = MLXSW_THERMAL_HYSTERESIS_TEMP, .min_state = 0, .max_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10, }, { + /* In range - 40-100% PWM */ + .type = THERMAL_TRIP_ACTIVE, + .temp = MLXSW_THERMAL_ASIC_TEMP_HIGH, + .hyst = MLXSW_THERMAL_HYSTERESIS_TEMP, .min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10, .max_state = MLXSW_THERMAL_MAX_STATE, }, - { + { /* Warning */ + .type = THERMAL_TRIP_HOT, + .temp = MLXSW_THERMAL_ASIC_TEMP_HOT, .min_state = MLXSW_THERMAL_MAX_STATE, .max_state = MLXSW_THERMAL_MAX_STATE, }, @@ -84,8 +78,7 @@ struct mlxsw_thermal; struct mlxsw_thermal_module { struct mlxsw_thermal *parent; struct thermal_zone_device *tzdev; - struct thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; - struct mlxsw_cooling_states cooling_states[MLXSW_THERMAL_NUM_TRIPS]; + struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; int module; /* Module or gearbox number */ u8 slot_index; }; @@ -105,8 +98,7 @@ struct mlxsw_thermal { struct thermal_zone_device *tzdev; int polling_delay; struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX]; - struct thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; - struct mlxsw_cooling_states cooling_states[MLXSW_THERMAL_NUM_TRIPS]; + struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; struct mlxsw_thermal_area line_cards[]; }; @@ -143,9 +135,9 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal, static void mlxsw_thermal_module_trips_reset(struct mlxsw_thermal_module *tz) { - tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temperature = 0; - tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temperature = 0; - tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temperature = 0; + tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = 0; + tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temp = 0; + tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temp = 0; } static int @@ -187,12 +179,12 @@ mlxsw_thermal_module_trips_update(struct device *dev, struct mlxsw_core *core, * by subtracting double hysteresis value. */ if (crit_temp >= MLXSW_THERMAL_MODULE_TEMP_SHIFT) - tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temperature = crit_temp - + tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = crit_temp - MLXSW_THERMAL_MODULE_TEMP_SHIFT; else - tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temperature = crit_temp; - tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temperature = crit_temp; - tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temperature = emerg_temp; + tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = crit_temp; + tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temp = crit_temp; + tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temp = emerg_temp; return 0; } @@ -209,11 +201,11 @@ static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev, return 0; for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { - const struct mlxsw_cooling_states *state = &thermal->cooling_states[i]; + const struct mlxsw_thermal_trip *trip = &thermal->trips[i]; err = thermal_zone_bind_cooling_device(tzdev, i, cdev, - state->max_state, - state->min_state, + trip->max_state, + trip->min_state, THERMAL_WEIGHT_DEFAULT); if (err < 0) { dev_err(dev, "Failed to bind cooling device to trip %d\n", i); @@ -267,6 +259,61 @@ static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev, return 0; } +static int mlxsw_thermal_get_trip_type(struct thermal_zone_device *tzdev, + int trip, + enum thermal_trip_type *p_type) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + *p_type = thermal->trips[trip].type; + return 0; +} + +static int mlxsw_thermal_get_trip_temp(struct thermal_zone_device *tzdev, + int trip, int *p_temp) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + *p_temp = thermal->trips[trip].temp; + return 0; +} + +static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev, + int trip, int temp) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + thermal->trips[trip].temp = temp; + return 0; +} + +static int mlxsw_thermal_get_trip_hyst(struct thermal_zone_device *tzdev, + int trip, int *p_hyst) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + *p_hyst = thermal->trips[trip].hyst; + return 0; +} + +static int mlxsw_thermal_set_trip_hyst(struct thermal_zone_device *tzdev, + int trip, int hyst) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + thermal->trips[trip].hyst = hyst; + return 0; +} + static struct thermal_zone_params mlxsw_thermal_params = { .no_hwmon = true, }; @@ -275,6 +322,11 @@ static struct thermal_zone_device_ops mlxsw_thermal_ops = { .bind = mlxsw_thermal_bind, .unbind = mlxsw_thermal_unbind, .get_temp = mlxsw_thermal_get_temp, + .get_trip_type = mlxsw_thermal_get_trip_type, + .get_trip_temp = mlxsw_thermal_get_trip_temp, + .set_trip_temp = mlxsw_thermal_set_trip_temp, + .get_trip_hyst = mlxsw_thermal_get_trip_hyst, + .set_trip_hyst = mlxsw_thermal_set_trip_hyst, }; static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev, @@ -289,11 +341,11 @@ static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev, return 0; for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { - const struct mlxsw_cooling_states *state = &tz->cooling_states[i]; + const struct mlxsw_thermal_trip *trip = &tz->trips[i]; err = thermal_zone_bind_cooling_device(tzdev, i, cdev, - state->max_state, - state->min_state, + trip->max_state, + trip->min_state, THERMAL_WEIGHT_DEFAULT); if (err < 0) goto err_thermal_zone_bind_cooling_device; @@ -381,10 +433,74 @@ static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev, return 0; } +static int +mlxsw_thermal_module_trip_type_get(struct thermal_zone_device *tzdev, int trip, + enum thermal_trip_type *p_type) +{ + struct mlxsw_thermal_module *tz = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + *p_type = tz->trips[trip].type; + return 0; +} + +static int +mlxsw_thermal_module_trip_temp_get(struct thermal_zone_device *tzdev, + int trip, int *p_temp) +{ + struct mlxsw_thermal_module *tz = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + *p_temp = tz->trips[trip].temp; + return 0; +} + +static int +mlxsw_thermal_module_trip_temp_set(struct thermal_zone_device *tzdev, + int trip, int temp) +{ + struct mlxsw_thermal_module *tz = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + tz->trips[trip].temp = temp; + return 0; +} + +static int +mlxsw_thermal_module_trip_hyst_get(struct thermal_zone_device *tzdev, int trip, + int *p_hyst) +{ + struct mlxsw_thermal_module *tz = tzdev->devdata; + + *p_hyst = tz->trips[trip].hyst; + return 0; +} + +static int +mlxsw_thermal_module_trip_hyst_set(struct thermal_zone_device *tzdev, int trip, + int hyst) +{ + struct mlxsw_thermal_module *tz = tzdev->devdata; + + tz->trips[trip].hyst = hyst; + return 0; +} + static struct thermal_zone_device_ops mlxsw_thermal_module_ops = { .bind = mlxsw_thermal_module_bind, .unbind = mlxsw_thermal_module_unbind, .get_temp = mlxsw_thermal_module_temp_get, + .get_trip_type = mlxsw_thermal_module_trip_type_get, + .get_trip_temp = mlxsw_thermal_module_trip_temp_get, + .set_trip_temp = mlxsw_thermal_module_trip_temp_set, + .get_trip_hyst = mlxsw_thermal_module_trip_hyst_get, + .set_trip_hyst = mlxsw_thermal_module_trip_hyst_set, }; static int mlxsw_thermal_gearbox_temp_get(struct thermal_zone_device *tzdev, @@ -414,6 +530,11 @@ static struct thermal_zone_device_ops mlxsw_thermal_gearbox_ops = { .bind = mlxsw_thermal_module_bind, .unbind = mlxsw_thermal_module_unbind, .get_temp = mlxsw_thermal_gearbox_temp_get, + .get_trip_type = mlxsw_thermal_module_trip_type_get, + .get_trip_temp = mlxsw_thermal_module_trip_temp_get, + .set_trip_temp = mlxsw_thermal_module_trip_temp_set, + .get_trip_hyst = mlxsw_thermal_module_trip_hyst_get, + .set_trip_hyst = mlxsw_thermal_module_trip_hyst_set, }; static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev, @@ -495,8 +616,7 @@ mlxsw_thermal_module_tz_init(struct mlxsw_thermal_module *module_tz) else snprintf(tz_name, sizeof(tz_name), "mlxsw-module%d", module_tz->module + 1); - module_tz->tzdev = thermal_zone_device_register_with_trips(tz_name, - module_tz->trips, + module_tz->tzdev = thermal_zone_device_register(tz_name, MLXSW_THERMAL_NUM_TRIPS, MLXSW_THERMAL_TRIP_MASK, module_tz, @@ -540,8 +660,6 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core, module_tz->parent = thermal; memcpy(module_tz->trips, default_thermal_trips, sizeof(thermal->trips)); - memcpy(module_tz->cooling_states, default_cooling_states, - sizeof(thermal->cooling_states)); /* Initialize all trip point. */ mlxsw_thermal_module_trips_reset(module_tz); /* Read module temperature and thresholds. */ @@ -637,8 +755,7 @@ mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz) else snprintf(tz_name, sizeof(tz_name), "mlxsw-gearbox%d", gearbox_tz->module + 1); - gearbox_tz->tzdev = thermal_zone_device_register_with_trips(tz_name, - gearbox_tz->trips, + gearbox_tz->tzdev = thermal_zone_device_register(tz_name, MLXSW_THERMAL_NUM_TRIPS, MLXSW_THERMAL_TRIP_MASK, gearbox_tz, @@ -695,8 +812,6 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core, gearbox_tz = &area->tz_gearbox_arr[i]; memcpy(gearbox_tz->trips, default_thermal_trips, sizeof(thermal->trips)); - memcpy(gearbox_tz->cooling_states, default_cooling_states, - sizeof(thermal->cooling_states)); gearbox_tz->module = i; gearbox_tz->parent = thermal; gearbox_tz->slot_index = area->slot_index; @@ -812,7 +927,6 @@ int mlxsw_thermal_init(struct mlxsw_core *core, thermal->core = core; thermal->bus_info = bus_info; memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips)); - memcpy(thermal->cooling_states, default_cooling_states, sizeof(thermal->cooling_states)); thermal->line_cards[0].slot_index = 0; err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl); @@ -862,8 +976,7 @@ int mlxsw_thermal_init(struct mlxsw_core *core, MLXSW_THERMAL_SLOW_POLL_INT : MLXSW_THERMAL_POLL_INT; - thermal->tzdev = thermal_zone_device_register_with_trips("mlxsw", - thermal->trips, + thermal->tzdev = thermal_zone_device_register("mlxsw", MLXSW_THERMAL_NUM_TRIPS, MLXSW_THERMAL_TRIP_MASK, thermal, -- cgit 1.4.1 From efc8ec1636fa962acf709741fce1e4d0a974e70a Mon Sep 17 00:00:00 2001 From: Anuj Gupta Date: Tue, 23 May 2023 16:47:09 +0530 Subject: block: fix bio-cache for passthru IO commit 46930b7cc7727271c9c27aac1fdc97a8645e2d00 upstream. commit <8af870aa5b847> ("block: enable bio caching use for passthru IO") introduced bio-cache for passthru IO. In case when nr_vecs are greater than BIO_INLINE_VECS, bio and bvecs are allocated from mempool (instead of percpu cache) and REQ_ALLOC_CACHE is cleared. This causes the side effect of not freeing bio/bvecs into mempool on completion. This patch lets the passthru IO fallback to allocation using bio_kmalloc when nr_vecs are greater than BIO_INLINE_VECS. The corresponding bio is freed during call to blk_mq_map_bio_put during completion. Cc: stable@vger.kernel.org # 6.1 fixes <8af870aa5b847> ("block: enable bio caching use for passthru IO") Signed-off-by: Anuj Gupta Signed-off-by: Kanchan Joshi Link: https://lore.kernel.org/r/20230523111709.145676-1-anuj20.g@samsung.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/blk-map.c b/block/blk-map.c index 34735626b00f..66da9e2b19ab 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -246,7 +246,7 @@ static struct bio *blk_rq_map_bio_alloc(struct request *rq, { struct bio *bio; - if (rq->cmd_flags & REQ_POLLED) { + if (rq->cmd_flags & REQ_POLLED && (nr_vecs <= BIO_INLINE_VECS)) { blk_opf_t opf = rq->cmd_flags | REQ_ALLOC_CACHE; bio = bio_alloc_bioset(NULL, nr_vecs, opf, gfp_mask, -- cgit 1.4.1 From c18f6919b4e7175747f67c9a085549c2c03fff20 Mon Sep 17 00:00:00 2001 From: Wyes Karny Date: Thu, 18 May 2023 05:58:19 +0000 Subject: cpufreq: amd-pstate: Update policy->cur in amd_pstate_adjust_perf() commit 3bf8c6307bad5c0cc09cde982e146d847859b651 upstream. Driver should update policy->cur after updating the frequency. Currently amd_pstate doesn't update policy->cur when `adjust_perf` is used. Which causes /proc/cpuinfo to show wrong cpu frequency. Fix this by updating policy->cur with correct frequency value in adjust_perf function callback. - Before the fix: (setting min freq to 1.5 MHz) [root@amd]# cat /proc/cpuinfo | grep "cpu MHz" | sort | uniq --count 1 cpu MHz : 1777.016 1 cpu MHz : 1797.160 1 cpu MHz : 1797.270 189 cpu MHz : 400.000 - After the fix: (setting min freq to 1.5 MHz) [root@amd]# cat /proc/cpuinfo | grep "cpu MHz" | sort | uniq --count 1 cpu MHz : 1753.353 1 cpu MHz : 1756.838 1 cpu MHz : 1776.466 1 cpu MHz : 1776.873 1 cpu MHz : 1777.308 1 cpu MHz : 1779.900 183 cpu MHz : 1805.231 1 cpu MHz : 1956.815 1 cpu MHz : 2246.203 1 cpu MHz : 2259.984 Fixes: 1d215f0319c2 ("cpufreq: amd-pstate: Add fast switch function for AMD P-State") Signed-off-by: Wyes Karny [ rjw: Subject edits ] Cc: 5.17+ # 5.17+ Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/amd-pstate.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index c17bd845f5fc..cd9d9ab75684 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -284,12 +284,14 @@ static void amd_pstate_adjust_perf(unsigned int cpu, unsigned long capacity) { unsigned long max_perf, min_perf, des_perf, - cap_perf, lowest_nonlinear_perf; + cap_perf, lowest_nonlinear_perf, max_freq; struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); struct amd_cpudata *cpudata = policy->driver_data; + unsigned int target_freq; cap_perf = READ_ONCE(cpudata->highest_perf); lowest_nonlinear_perf = READ_ONCE(cpudata->lowest_nonlinear_perf); + max_freq = READ_ONCE(cpudata->max_freq); des_perf = cap_perf; if (target_perf < capacity) @@ -306,6 +308,10 @@ static void amd_pstate_adjust_perf(unsigned int cpu, if (max_perf < min_perf) max_perf = min_perf; + des_perf = clamp_t(unsigned long, des_perf, min_perf, max_perf); + target_freq = div_u64(des_perf * max_freq, max_perf); + policy->cur = target_freq; + amd_pstate_update(cpudata, min_perf, des_perf, max_perf, true); cpufreq_cpu_put(policy); } -- cgit 1.4.1 From 55ce796e9b0a36926e4ab8c4cb2f03c4533f2a22 Mon Sep 17 00:00:00 2001 From: "Gautham R. Shenoy" Date: Wed, 17 May 2023 16:28:15 +0000 Subject: cpufreq: amd-pstate: Add ->fast_switch() callback commit 4badf2eb1e986bdbf34dd2f5d4c979553a86fe54 upstream. Schedutil normally calls the adjust_perf callback for drivers with adjust_perf callback available and fast_switch_possible flag set. However, when frequency invariance is disabled and schedutil tries to invoke fast_switch. So, there is a chance of kernel crash if this function pointer is not set. To protect against this scenario add fast_switch callback to amd_pstate driver. Fixes: 1d215f0319c2 ("cpufreq: amd-pstate: Add fast switch function for AMD P-State") Signed-off-by: Gautham R. Shenoy Signed-off-by: Wyes Karny Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/amd-pstate.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index cd9d9ab75684..f8d2bba9173d 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -249,9 +249,8 @@ static int amd_pstate_verify(struct cpufreq_policy_data *policy) return 0; } -static int amd_pstate_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) +static int amd_pstate_update_freq(struct cpufreq_policy *policy, + unsigned int target_freq, bool fast_switch) { struct cpufreq_freqs freqs; struct amd_cpudata *cpudata = policy->driver_data; @@ -270,14 +269,36 @@ static int amd_pstate_target(struct cpufreq_policy *policy, des_perf = DIV_ROUND_CLOSEST(target_freq * cap_perf, cpudata->max_freq); - cpufreq_freq_transition_begin(policy, &freqs); - amd_pstate_update(cpudata, min_perf, des_perf, - max_perf, false); - cpufreq_freq_transition_end(policy, &freqs, false); + WARN_ON(fast_switch && !policy->fast_switch_enabled); + /* + * If fast_switch is desired, then there aren't any registered + * transition notifiers. See comment for + * cpufreq_enable_fast_switch(). + */ + if (!fast_switch) + cpufreq_freq_transition_begin(policy, &freqs); + + amd_pstate_update(cpudata, min_perf, des_perf, max_perf, fast_switch); + + if (!fast_switch) + cpufreq_freq_transition_end(policy, &freqs, false); return 0; } +static int amd_pstate_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + return amd_pstate_update_freq(policy, target_freq, false); +} + +static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy, + unsigned int target_freq) +{ + return amd_pstate_update_freq(policy, target_freq, true); +} + static void amd_pstate_adjust_perf(unsigned int cpu, unsigned long _min_perf, unsigned long target_perf, @@ -523,6 +544,7 @@ static int amd_pstate_cpu_exit(struct cpufreq_policy *policy) freq_qos_remove_request(&cpudata->req[1]); freq_qos_remove_request(&cpudata->req[0]); + policy->fast_switch_possible = false; kfree(cpudata); return 0; @@ -614,6 +636,7 @@ static struct cpufreq_driver amd_pstate_driver = { .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS, .verify = amd_pstate_verify, .target = amd_pstate_target, + .fast_switch = amd_pstate_fast_switch, .init = amd_pstate_cpu_init, .exit = amd_pstate_cpu_exit, .suspend = amd_pstate_cpu_suspend, -- cgit 1.4.1 From 2cd02ae656547f0bf47ba2b957de6a923fb41010 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Wed, 22 Mar 2023 09:35:32 +0200 Subject: netfilter: ctnetlink: Support offloaded conntrack entry deletion commit 9b7c68b3911aef84afa4cbfc31bce20f10570d51 upstream. Currently, offloaded conntrack entries (flows) can only be deleted after they are removed from offload, which is either by timeout, tcp state change or tc ct rule deletion. This can cause issues for users wishing to manually delete or flush existing entries. Support deletion of offloaded conntrack entries. Example usage: # Delete all offloaded (and non offloaded) conntrack entries # whose source address is 1.2.3.4 $ conntrack -D -s 1.2.3.4 # Delete all entries $ conntrack -F Signed-off-by: Paul Blakey Reviewed-by: Simon Horman Acked-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Cc: Demi Marie Obenour Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_netlink.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index cb4325b8ebb1..857cddd9d82e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1559,9 +1559,6 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { static int ctnetlink_flush_iterate(struct nf_conn *ct, void *data) { - if (test_bit(IPS_OFFLOAD_BIT, &ct->status)) - return 0; - return ctnetlink_filter_match(ct, data); } @@ -1631,11 +1628,6 @@ static int ctnetlink_del_conntrack(struct sk_buff *skb, ct = nf_ct_tuplehash_to_ctrack(h); - if (test_bit(IPS_OFFLOAD_BIT, &ct->status)) { - nf_ct_put(ct); - return -EBUSY; - } - if (cda[CTA_ID]) { __be32 id = nla_get_be32(cda[CTA_ID]); -- cgit 1.4.1 From cd51ba98aeaaee27c1c89b156647f4505cc66840 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Wed, 10 May 2023 18:21:44 +0800 Subject: tools headers UAPI: Sync the linux/in.h with the kernel sources commit 5d1ac59ff7445e51a0e4958fa39ac8aa23691698 upstream. Picking the changes from: 91d0b78c5177f3e4 ("inet: Add IP_LOCAL_PORT_RANGE socket option") Silencing these perf build warnings: Warning: Kernel ABI header at 'tools/include/uapi/linux/in.h' differs from latest version at 'include/uapi/linux/in.h' diff -u tools/include/uapi/linux/in.h include/uapi/linux/in.h Signed-off-by: Yanteng Si Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: loongson-kernel@lists.loongnix.cn Link: https://lore.kernel.org/r/23aabc69956ac94fbf388b05c8be08a64e8c7ccc.1683712945.git.siyanteng@loongson.cn Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/include/uapi/linux/in.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/uapi/linux/in.h b/tools/include/uapi/linux/in.h index 07a4cb149305..4b7f2df66b99 100644 --- a/tools/include/uapi/linux/in.h +++ b/tools/include/uapi/linux/in.h @@ -162,6 +162,7 @@ struct in_addr { #define MCAST_MSFILTER 48 #define IP_MULTICAST_ALL 49 #define IP_UNICAST_IF 50 +#define IP_LOCAL_PORT_RANGE 51 #define MCAST_EXCLUDE 0 #define MCAST_INCLUDE 1 -- cgit 1.4.1 From 76ba310227d2490018c271f1ecabb6c0a3212eb0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Jun 2023 09:26:22 +0200 Subject: Linux 6.1.32 Link: https://lore.kernel.org/r/20230601131939.051934720@linuxfoundation.org Tested-by: Shuah Khan Link: https://lore.kernel.org/r/20230601143327.479886832@linuxfoundation.org Tested-by: Florian Fainelli Tested-by: Takeshi Ogasawara Tested-by: Conor Dooley Tested-by: Ron Economos Tested-by: Jon Hunter Tested-by: Bagas Sanjaya Tested-by: Linux Kernel Functional Testing Tested-by: Markus Reichelt Tested-by: Florian Fainelli Tested-by: Salvatore Bonaccorso Tested-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 902a6b598c73..a0c3d8809e93 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 31 +SUBLEVEL = 32 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From 836f874d43ed16d2eaa7306f63761017d5a44300 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Sun, 7 May 2023 11:29:29 -0700 Subject: RDMA/bnxt_re: Fix the page_size used during the MR creation [ Upstream commit 08c7f09356e45d093d1867c7a3c6ac6526e2f98b ] Driver populates the list of pages used for Memory region wrongly when page size is more than system page size. This is causing a failure when some of the applications that creates MR with page size as 2M. Since HW can support multiple page sizes, pass the correct page size while creating the MR. Also, driver need not adjust the number of pages when HW Queues are created with user memory. It should work with the number of dma blocks returned by ib_umem_num_dma_blocks. Fix this calculation also. Fixes: 0c4dcd602817 ("RDMA/bnxt_re: Refactor hardware queue memory allocation") Fixes: f6919d56388c ("RDMA/bnxt_re: Code refactor while populating user MRs") Link: https://lore.kernel.org/r/1683484169-9539-1-git-send-email-selvin.xavier@broadcom.com Signed-off-by: Kalesh AP Signed-off-by: Kashyap Desai Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_res.c | 12 ++---------- drivers/infiniband/hw/bnxt_re/qplib_sp.c | 7 +++---- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index 126d4f26f75a..81b0c5e879f9 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -215,17 +215,9 @@ int bnxt_qplib_alloc_init_hwq(struct bnxt_qplib_hwq *hwq, return -EINVAL; hwq_attr->sginfo->npages = npages; } else { - unsigned long sginfo_num_pages = ib_umem_num_dma_blocks( - hwq_attr->sginfo->umem, hwq_attr->sginfo->pgsize); - + npages = ib_umem_num_dma_blocks(hwq_attr->sginfo->umem, + hwq_attr->sginfo->pgsize); hwq->is_user = true; - npages = sginfo_num_pages; - npages = (npages * PAGE_SIZE) / - BIT_ULL(hwq_attr->sginfo->pgshft); - if ((sginfo_num_pages * PAGE_SIZE) % - BIT_ULL(hwq_attr->sginfo->pgshft)) - if (!npages) - npages++; } if (npages == MAX_PBL_LVL_0_PGS && !hwq_attr->sginfo->nopte) { diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c index b802981b7171..bae7d8926143 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c @@ -584,16 +584,15 @@ int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr, /* Free the hwq if it already exist, must be a rereg */ if (mr->hwq.max_elements) bnxt_qplib_free_hwq(res, &mr->hwq); - /* Use system PAGE_SIZE */ hwq_attr.res = res; hwq_attr.depth = pages; - hwq_attr.stride = buf_pg_size; + hwq_attr.stride = sizeof(dma_addr_t); hwq_attr.type = HWQ_TYPE_MR; hwq_attr.sginfo = &sginfo; hwq_attr.sginfo->umem = umem; hwq_attr.sginfo->npages = pages; - hwq_attr.sginfo->pgsize = PAGE_SIZE; - hwq_attr.sginfo->pgshft = PAGE_SHIFT; + hwq_attr.sginfo->pgsize = buf_pg_size; + hwq_attr.sginfo->pgshft = ilog2(buf_pg_size); rc = bnxt_qplib_alloc_init_hwq(&mr->hwq, &hwq_attr); if (rc) { dev_err(&res->pdev->dev, -- cgit 1.4.1 From 21c0eb064890eb852a55dd22bf259dd2e8467ef7 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 12 May 2023 15:11:41 +0200 Subject: phy: amlogic: phy-meson-g12a-mipi-dphy-analog: fix CNTL2_DIF_TX_CTL0 value [ Upstream commit b949193011540bb17cf1da7795ec42af1b875203 ] Use the same CNTL2_DIF_TX_CTL0 value used by the vendor, it was reported fixing timings issues. Fixes: 2a56dc650e54 ("phy: amlogic: Add G12A Analog MIPI D-PHY driver") Signed-off-by: Neil Armstrong Link: https://lore.kernel.org/r/20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-v4-10-2592c29ea263@linaro.org Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c index c14089fa7db4..cabdddbbabfd 100644 --- a/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c +++ b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c @@ -70,7 +70,7 @@ static int phy_g12a_mipi_dphy_analog_power_on(struct phy *phy) HHI_MIPI_CNTL1_BANDGAP); regmap_write(priv->regmap, HHI_MIPI_CNTL2, - FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL0, 0x459) | + FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL0, 0x45a) | FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL1, 0x2680)); reg = DSI_LANE_CLK; -- cgit 1.4.1 From 241de3fec11ff184bc8808ca416f2c6273605d02 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Thu, 11 May 2023 11:51:03 +0000 Subject: RDMA/efa: Fix unsupported page sizes in device [ Upstream commit 866422cdddcdf59d8c68e9472d49ba1be29b5fcf ] Device uses 4KB size blocks for user pages indirect list while the driver creates those blocks with the size of PAGE_SIZE of the kernel. On kernels with PAGE_SIZE different than 4KB (ARM RHEL), this leads to a failure on register MR with indirect list because of the miss communication between driver and device. Fixes: 40909f664d27 ("RDMA/efa: Add EFA verbs implementation") Link: https://lore.kernel.org/r/20230511115103.13876-1-ynachum@amazon.com Reviewed-by: Firas Jahjah Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/efa/efa_verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 31454643f8c5..f9526a4c75b2 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -1397,7 +1397,7 @@ static int pbl_continuous_initialize(struct efa_dev *dev, */ static int pbl_indirect_initialize(struct efa_dev *dev, struct pbl_context *pbl) { - u32 size_in_pages = DIV_ROUND_UP(pbl->pbl_buf_size_in_bytes, PAGE_SIZE); + u32 size_in_pages = DIV_ROUND_UP(pbl->pbl_buf_size_in_bytes, EFA_CHUNK_PAYLOAD_SIZE); struct scatterlist *sgl; int sg_dma_cnt, err; -- cgit 1.4.1 From 38771c0eefa6686a56b59b6ab76bfc83e80239a5 Mon Sep 17 00:00:00 2001 From: Chengchang Tang Date: Fri, 12 May 2023 17:22:43 +0800 Subject: RDMA/hns: Fix timeout attr in query qp for HIP08 [ Upstream commit 58caa2a51ad4fd21763696cc6c4defc9fc1b4b4f ] On HIP08, the queried timeout attr is different from the timeout attr configured by the user. It is found by rdma-core testcase test_rdmacm_async_traffic: ====================================================================== FAIL: test_rdmacm_async_traffic (tests.test_rdmacm.CMTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "./tests/test_rdmacm.py", line 33, in test_rdmacm_async_traffic self.two_nodes_rdmacm_traffic(CMAsyncConnection, self.rdmacm_traffic, File "./tests/base.py", line 382, in two_nodes_rdmacm_traffic raise(res) AssertionError Fixes: 926a01dc000d ("RDMA/hns: Add QP operations support for hip08 SoC") Link: https://lore.kernel.org/r/20230512092245.344442-2-huangjunxian6@hisilicon.com Signed-off-by: Chengchang Tang Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 17 ++++++++++++++--- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index b2421883993b..7a5bfe6a9115 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -5136,7 +5136,6 @@ static int hns_roce_v2_set_abs_fields(struct ib_qp *ibqp, static bool check_qp_timeout_cfg_range(struct hns_roce_dev *hr_dev, u8 *timeout) { #define QP_ACK_TIMEOUT_MAX_HIP08 20 -#define QP_ACK_TIMEOUT_OFFSET 10 #define QP_ACK_TIMEOUT_MAX 31 if (hr_dev->pci_dev->revision == PCI_REVISION_ID_HIP08) { @@ -5145,7 +5144,7 @@ static bool check_qp_timeout_cfg_range(struct hns_roce_dev *hr_dev, u8 *timeout) "local ACK timeout shall be 0 to 20.\n"); return false; } - *timeout += QP_ACK_TIMEOUT_OFFSET; + *timeout += HNS_ROCE_V2_QP_ACK_TIMEOUT_OFS_HIP08; } else if (hr_dev->pci_dev->revision > PCI_REVISION_ID_HIP08) { if (*timeout > QP_ACK_TIMEOUT_MAX) { ibdev_warn(&hr_dev->ib_dev, @@ -5431,6 +5430,18 @@ out: return ret; } +static u8 get_qp_timeout_attr(struct hns_roce_dev *hr_dev, + struct hns_roce_v2_qp_context *context) +{ + u8 timeout; + + timeout = (u8)hr_reg_read(context, QPC_AT); + if (hr_dev->pci_dev->revision == PCI_REVISION_ID_HIP08) + timeout -= HNS_ROCE_V2_QP_ACK_TIMEOUT_OFS_HIP08; + + return timeout; +} + static int hns_roce_v2_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr) @@ -5508,7 +5519,7 @@ static int hns_roce_v2_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, qp_attr->max_dest_rd_atomic = 1 << hr_reg_read(&context, QPC_RR_MAX); qp_attr->min_rnr_timer = (u8)hr_reg_read(&context, QPC_MIN_RNR_TIME); - qp_attr->timeout = (u8)hr_reg_read(&context, QPC_AT); + qp_attr->timeout = get_qp_timeout_attr(hr_dev, &context); qp_attr->retry_cnt = hr_reg_read(&context, QPC_RETRY_NUM_INIT); qp_attr->rnr_retry = hr_reg_read(&context, QPC_RNR_NUM_INIT); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index b1b3e1e0b84e..2b4dbbb06eb5 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -72,6 +72,8 @@ #define HNS_ROCE_V2_IDX_ENTRY_SZ 4 #define HNS_ROCE_V2_SCCC_SZ 32 +#define HNS_ROCE_V2_QP_ACK_TIMEOUT_OFS_HIP08 10 + #define HNS_ROCE_V3_SCCC_SZ 64 #define HNS_ROCE_V3_GMV_ENTRY_SZ 32 -- cgit 1.4.1 From 736e1c4e54e9ca4615a06dde5f802a40eaaf8dd3 Mon Sep 17 00:00:00 2001 From: Chengchang Tang Date: Fri, 12 May 2023 17:22:44 +0800 Subject: RDMA/hns: Fix base address table allocation [ Upstream commit 7f3969b14f356dd65fa95b3528eb05c32e68bc06 ] For hns, the specification of an entry like resource (E.g. WQE/CQE/EQE) depends on BT page size, buf page size and hopnum. For user mode, the buf page size depends on UMEM. Therefore, the actual specification is controlled by BT page size and hopnum. The current BT page size and hopnum are obtained from firmware. This makes the driver inflexible and introduces unnecessary constraints. Resource allocation failures occur in many scenarios. This patch will calculate whether the BT page size set by firmware is sufficient before allocating BT, and increase the BT page size if it is insufficient. Fixes: 1133401412a9 ("RDMA/hns: Optimize base address table config flow for qp buffer") Link: https://lore.kernel.org/r/20230512092245.344442-3-huangjunxian6@hisilicon.com Signed-off-by: Chengchang Tang Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_mr.c | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index 37a5cf62f88b..14376490ac22 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -33,6 +33,7 @@ #include #include +#include #include "hns_roce_device.h" #include "hns_roce_cmd.h" #include "hns_roce_hem.h" @@ -909,6 +910,44 @@ static int mtr_init_buf_cfg(struct hns_roce_dev *hr_dev, return page_cnt; } +static u64 cal_pages_per_l1ba(unsigned int ba_per_bt, unsigned int hopnum) +{ + return int_pow(ba_per_bt, hopnum - 1); +} + +static unsigned int cal_best_bt_pg_sz(struct hns_roce_dev *hr_dev, + struct hns_roce_mtr *mtr, + unsigned int pg_shift) +{ + unsigned long cap = hr_dev->caps.page_size_cap; + struct hns_roce_buf_region *re; + unsigned int pgs_per_l1ba; + unsigned int ba_per_bt; + unsigned int ba_num; + int i; + + for_each_set_bit_from(pg_shift, &cap, sizeof(cap) * BITS_PER_BYTE) { + if (!(BIT(pg_shift) & cap)) + continue; + + ba_per_bt = BIT(pg_shift) / BA_BYTE_LEN; + ba_num = 0; + for (i = 0; i < mtr->hem_cfg.region_count; i++) { + re = &mtr->hem_cfg.region[i]; + if (re->hopnum == 0) + continue; + + pgs_per_l1ba = cal_pages_per_l1ba(ba_per_bt, re->hopnum); + ba_num += DIV_ROUND_UP(re->count, pgs_per_l1ba); + } + + if (ba_num <= ba_per_bt) + return pg_shift; + } + + return 0; +} + static int mtr_alloc_mtt(struct hns_roce_dev *hr_dev, struct hns_roce_mtr *mtr, unsigned int ba_page_shift) { @@ -917,6 +956,10 @@ static int mtr_alloc_mtt(struct hns_roce_dev *hr_dev, struct hns_roce_mtr *mtr, hns_roce_hem_list_init(&mtr->hem_list); if (!cfg->is_direct) { + ba_page_shift = cal_best_bt_pg_sz(hr_dev, mtr, ba_page_shift); + if (!ba_page_shift) + return -ERANGE; + ret = hns_roce_hem_list_request(hr_dev, &mtr->hem_list, cfg->region, cfg->region_count, ba_page_shift); -- cgit 1.4.1 From ef8c7616939dc855c63e6bcfacf45a5c25732f22 Mon Sep 17 00:00:00 2001 From: Yangyang Li Date: Fri, 12 May 2023 17:22:45 +0800 Subject: RDMA/hns: Modify the value of long message loopback slice [ Upstream commit 56518a603fd2bf74762d176ac980572db84a3e14 ] Long message loopback slice is used for achieving traffic balance between QPs. It prevents the problem that QPs with large traffic occupying the hardware pipeline for a long time and QPs with small traffic cannot be scheduled. Currently, its maximum value is set to 16K, which means only after a QP sends 16K will the second QP be scheduled. This value is too large, which will lead to unbalanced traffic scheduling, and thus it needs to be modified. The setting range of the long message loopback slice is modified to be from 1024 (the lower limit supported by hardware) to mtu. Actual testing shows that this value can significantly reduce error in hardware traffic scheduling. This solution is compatible with both HIP08 and HIP09. The modified lp_pktn_ini has a maximum value of 2 (when mtu is 256), so the range checking code for lp_pktn_ini is no longer necessary and needs to be deleted. Fixes: 0e60778efb07 ("RDMA/hns: Modify the value of MAX_LP_MSG_LEN to meet hardware compatibility") Link: https://lore.kernel.org/r/20230512092245.344442-4-huangjunxian6@hisilicon.com Signed-off-by: Yangyang Li Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 7a5bfe6a9115..34a270b6891a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -4728,11 +4728,9 @@ static int modify_qp_init_to_rtr(struct ib_qp *ibqp, mtu = ib_mtu_enum_to_int(ib_mtu); if (WARN_ON(mtu <= 0)) return -EINVAL; -#define MAX_LP_MSG_LEN 16384 - /* MTU * (2 ^ LP_PKTN_INI) shouldn't be bigger than 16KB */ - lp_pktn_ini = ilog2(MAX_LP_MSG_LEN / mtu); - if (WARN_ON(lp_pktn_ini >= 0xF)) - return -EINVAL; +#define MIN_LP_MSG_LEN 1024 + /* mtu * (2 ^ lp_pktn_ini) should be in the range of 1024 to mtu */ + lp_pktn_ini = ilog2(max(mtu, MIN_LP_MSG_LEN) / mtu); if (attr_mask & IB_QP_PATH_MTU) { hr_reg_write(context, QPC_MTU, ib_mtu); -- cgit 1.4.1 From 44fc5eb0e2674b5c934f105d89533e03d064e9c1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 15 May 2023 13:32:10 +0300 Subject: dmaengine: at_xdmac: fix potential Oops in at_xdmac_prep_interleaved() [ Upstream commit 4d43acb145c363626d76f49febb4240c488cd1cf ] There are two place if the at_xdmac_interleaved_queue_desc() fails which could lead to a NULL dereference where "first" is NULL and we call list_add_tail(&first->desc_node, ...). In the first caller, the return is not checked so add a check for that. In the next caller, the return is checked but if it fails on the first iteration through the loop then it will lead to a NULL pointer dereference. Fixes: 4e5385784e69 ("dmaengine: at_xdmac: handle numf > 1") Fixes: 62b5cb757f1d ("dmaengine: at_xdmac: fix memory leak in interleaved mode") Signed-off-by: Dan Carpenter Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/21282b66-9860-410a-83df-39c17fcf2f1b@kili.mountain Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/at_xdmac.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index bfc8ae214395..7919906b02e7 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1026,6 +1026,8 @@ at_xdmac_prep_interleaved(struct dma_chan *chan, NULL, src_addr, dst_addr, xt, xt->sgl); + if (!first) + return NULL; /* Length of the block is (BLEN+1) microblocks. */ for (i = 0; i < xt->numf - 1; i++) @@ -1056,8 +1058,9 @@ at_xdmac_prep_interleaved(struct dma_chan *chan, src_addr, dst_addr, xt, chunk); if (!desc) { - list_splice_tail_init(&first->descs_list, - &atchan->free_descs_list); + if (first) + list_splice_tail_init(&first->descs_list, + &atchan->free_descs_list); return NULL; } -- cgit 1.4.1 From dcaa61b73d9c89364e6f1e0bec44866b1c02c276 Mon Sep 17 00:00:00 2001 From: Kalesh AP Date: Thu, 18 May 2023 01:10:59 -0700 Subject: RDMA/bnxt_re: Fix a possible memory leak [ Upstream commit 349e3c0cf239cc01d58a1e6c749e171de014cd6a ] Inside bnxt_qplib_create_cq(), when the check for NULL DPI fails, driver returns directly without freeing the memory allocated inside bnxt_qplib_alloc_init_hwq() routine. Fixed this by moving the check for NULL DPI before invoking bnxt_qplib_alloc_init_hwq(). Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Link: https://lore.kernel.org/r/1684397461-23082-2-git-send-email-selvin.xavier@broadcom.com Reviewed-by: Kashyap Desai Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 96e581ced50e..ab2cc1c67f70 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -2043,6 +2043,12 @@ int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) u32 pg_sz_lvl; int rc; + if (!cq->dpi) { + dev_err(&rcfw->pdev->dev, + "FP: CREATE_CQ failed due to NULL DPI\n"); + return -EINVAL; + } + hwq_attr.res = res; hwq_attr.depth = cq->max_wqe; hwq_attr.stride = sizeof(struct cq_base); @@ -2054,11 +2060,6 @@ int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) RCFW_CMD_PREP(req, CREATE_CQ, cmd_flags); - if (!cq->dpi) { - dev_err(&rcfw->pdev->dev, - "FP: CREATE_CQ failed due to NULL DPI\n"); - return -EINVAL; - } req.dpi = cpu_to_le32(cq->dpi->dpi); req.cq_handle = cpu_to_le64(cq->cq_handle); req.cq_size = cpu_to_le32(cq->hwq.max_elements); -- cgit 1.4.1 From 0021441cbbf08266b420e60b9eb76b3061cfa85d Mon Sep 17 00:00:00 2001 From: Kalesh AP Date: Thu, 18 May 2023 01:11:00 -0700 Subject: RDMA/bnxt_re: Fix return value of bnxt_re_process_raw_qp_pkt_rx [ Upstream commit 0fa0d520e2a878cb4c94c4dc84395905d3f14f54 ] bnxt_re_process_raw_qp_pkt_rx() always return 0 and ignores the return value of bnxt_re_post_send_shadow_qp(). Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Link: https://lore.kernel.org/r/1684397461-23082-3-git-send-email-selvin.xavier@broadcom.com Reviewed-by: Hongguang Gao Reviewed-by: Ajit Khaparde Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 989edc789633..94222de1d371 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -3241,9 +3241,7 @@ static int bnxt_re_process_raw_qp_pkt_rx(struct bnxt_re_qp *gsi_qp, udwr.remote_qkey = gsi_sqp->qplib_qp.qkey; /* post data received in the send queue */ - rc = bnxt_re_post_send_shadow_qp(rdev, gsi_sqp, swr); - - return 0; + return bnxt_re_post_send_shadow_qp(rdev, gsi_sqp, swr); } static void bnxt_re_process_res_rawqp1_wc(struct ib_wc *wc, -- cgit 1.4.1 From bf1f3f4aa47dda5ee5c5047d4f2311d811fdafb0 Mon Sep 17 00:00:00 2001 From: Chao Wang Date: Mon, 17 Apr 2023 03:04:21 +0000 Subject: iommu/rockchip: Fix unwind goto issue [ Upstream commit ec014683c564fb74fc68e8f5e84691d3b3839d24 ] Smatch complains that drivers/iommu/rockchip-iommu.c:1306 rk_iommu_probe() warn: missing unwind goto? The rk_iommu_probe function, after obtaining the irq value through platform_get_irq, directly returns an error if the returned value is negative, without releasing any resources. Fix this by adding a new error handling label "err_pm_disable" and use a goto statement to redirect to the error handling process. In order to preserve the original semantics, set err to the value of irq. Fixes: 1aa55ca9b14a ("iommu/rockchip: Move irq request past pm_runtime_enable") Signed-off-by: Chao Wang Reviewed-by: Dongliang Mu Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230417030421.2777-1-D202280639@hust.edu.cn Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/rockchip-iommu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index a68eadd64f38..f7e9b56be174 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -1303,20 +1303,22 @@ static int rk_iommu_probe(struct platform_device *pdev) for (i = 0; i < iommu->num_irq; i++) { int irq = platform_get_irq(pdev, i); - if (irq < 0) - return irq; + if (irq < 0) { + err = irq; + goto err_pm_disable; + } err = devm_request_irq(iommu->dev, irq, rk_iommu_irq, IRQF_SHARED, dev_name(dev), iommu); - if (err) { - pm_runtime_disable(dev); - goto err_remove_sysfs; - } + if (err) + goto err_pm_disable; } dma_set_mask_and_coherent(dev, rk_ops->dma_bit_mask); return 0; +err_pm_disable: + pm_runtime_disable(dev); err_remove_sysfs: iommu_device_sysfs_remove(&iommu->iommu); err_put_group: -- cgit 1.4.1 From c3ff24625a426b9ee32cc9003fa70aafe69dcb3b Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Wed, 19 Apr 2023 21:11:53 +0100 Subject: iommu/amd: Don't block updates to GATag if guest mode is on [ Upstream commit ed8a2f4ddef2eaaf864ab1efbbca9788187036ab ] On KVM GSI routing table updates, specially those where they have vIOMMUs with interrupt remapping enabled (to boot >255vcpus setups without relying on KVM_FEATURE_MSI_EXT_DEST_ID), a VMM may update the backing VF MSIs with a new VCPU affinity. On AMD with AVIC enabled, the new vcpu affinity info is updated via: avic_pi_update_irte() irq_set_vcpu_affinity() amd_ir_set_vcpu_affinity() amd_iommu_{de}activate_guest_mode() Where the IRTE[GATag] is updated with the new vcpu affinity. The GATag contains VM ID and VCPU ID, and is used by IOMMU hardware to signal KVM (via GALog) when interrupt cannot be delivered due to vCPU is in blocking state. The issue is that amd_iommu_activate_guest_mode() will essentially only change IRTE fields on transitions from non-guest-mode to guest-mode and otherwise returns *with no changes to IRTE* on already configured guest-mode interrupts. To the guest this means that the VF interrupts remain affined to the first vCPU they were first configured, and guest will be unable to issue VF interrupts and receive messages like this from spurious interrupts (e.g. from waking the wrong vCPU in GALog): [ 167.759472] __common_interrupt: 3.34 No irq handler for vector [ 230.680927] mlx5_core 0000:00:02.0: mlx5_cmd_eq_recover:247:(pid 3122): Recovered 1 EQEs on cmd_eq [ 230.681799] mlx5_core 0000:00:02.0: wait_func_handle_exec_timeout:1113:(pid 3122): cmd[0]: CREATE_CQ(0x400) recovered after timeout [ 230.683266] __common_interrupt: 3.34 No irq handler for vector Given the fact that amd_ir_set_vcpu_affinity() uses amd_iommu_activate_guest_mode() underneath it essentially means that VCPU affinity changes of IRTEs are nops. Fix it by dropping the check for guest-mode at amd_iommu_activate_guest_mode(). Same thing is applicable to amd_iommu_deactivate_guest_mode() although, even if the IRTE doesn't change underlying DestID on the host, the VFIO IRQ handler will still be able to poke at the right guest-vCPU. Fixes: b9c6ff94e43a ("iommu/amd: Re-factor guest virtual APIC (de-)activation code") Signed-off-by: Joao Martins Reviewed-by: Suravee Suthikulpanit Link: https://lore.kernel.org/r/20230419201154.83880-2-joao.m.martins@oracle.com Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/iommu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 26fb78003889..0ccc6b8319fb 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -3504,8 +3504,7 @@ int amd_iommu_activate_guest_mode(void *data) struct irte_ga *entry = (struct irte_ga *) ir_data->entry; u64 valid; - if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) || - !entry || entry->lo.fields_vapic.guest_mode) + if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) || !entry) return 0; valid = entry->lo.fields_vapic.valid; -- cgit 1.4.1 From 817ce9b1d27eed5a17cc202a040b420029de411c Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Wed, 19 Apr 2023 21:11:54 +0100 Subject: iommu/amd: Handle GALog overflows [ Upstream commit af47b0a24058e56e983881993752f88288ca6511 ] GALog exists to propagate interrupts into all vCPUs in the system when interrupts are marked as non running (e.g. when vCPUs aren't running). A GALog overflow happens when there's in no space in the log to record the GATag of the interrupt. So when the GALOverflow condition happens, the GALog queue is processed and the GALog is restarted, as the IOMMU manual indicates in section "2.7.4 Guest Virtual APIC Log Restart Procedure": | * Wait until MMIO Offset 2020h[GALogRun]=0b so that all request | entries are completed as circumstances allow. GALogRun must be 0b to | modify the guest virtual APIC log registers safely. | * Write MMIO Offset 0018h[GALogEn]=0b. | * As necessary, change the following values (e.g., to relocate or | resize the guest virtual APIC event log): | - the Guest Virtual APIC Log Base Address Register | [MMIO Offset 00E0h], | - the Guest Virtual APIC Log Head Pointer Register | [MMIO Offset 2040h][GALogHead], and | - the Guest Virtual APIC Log Tail Pointer Register | [MMIO Offset 2048h][GALogTail]. | * Write MMIO Offset 2020h[GALOverflow] = 1b to clear the bit (W1C). | * Write MMIO Offset 0018h[GALogEn] = 1b, and either set | MMIO Offset 0018h[GAIntEn] to enable the GA log interrupt or clear | the bit to disable it. Failing to handle the GALog overflow means that none of the VFs (in any guest) will work with IOMMU AVIC forcing the user to power cycle the host. When handling the event it resumes the GALog without resizing much like how it is done in the event handler overflow. The [MMIO Offset 2020h][GALOverflow] bit might be set in status register without the [MMIO Offset 2020h][GAInt] bit, so when deciding to poll for GA events (to clear space in the galog), also check the overflow bit. [suravee: Check for GAOverflow without GAInt, toggle CONTROL_GAINT_EN] Co-developed-by: Suravee Suthikulpanit Signed-off-by: Suravee Suthikulpanit Signed-off-by: Joao Martins Reviewed-by: Vasant Hegde Link: https://lore.kernel.org/r/20230419201154.83880-3-joao.m.martins@oracle.com Signed-off-by: Joerg Roedel Stable-dep-of: 8ec4e2befef1 ("iommu/amd: Fix up merge conflict resolution") Signed-off-by: Sasha Levin --- drivers/iommu/amd/amd_iommu.h | 1 + drivers/iommu/amd/init.c | 24 ++++++++++++++++++++++++ drivers/iommu/amd/iommu.c | 9 ++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index c160a332ce33..24c7e6c6c0de 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -15,6 +15,7 @@ extern irqreturn_t amd_iommu_int_thread(int irq, void *data); extern irqreturn_t amd_iommu_int_handler(int irq, void *data); extern void amd_iommu_apply_erratum_63(struct amd_iommu *iommu, u16 devid); extern void amd_iommu_restart_event_logging(struct amd_iommu *iommu); +extern void amd_iommu_restart_ga_log(struct amd_iommu *iommu); extern int amd_iommu_init_devices(void); extern void amd_iommu_uninit_devices(void); extern void amd_iommu_init_notifier(void); diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 7c14b1d32c8d..b0af8b5967e0 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -751,6 +751,30 @@ void amd_iommu_restart_event_logging(struct amd_iommu *iommu) iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); } +/* + * This function restarts event logging in case the IOMMU experienced + * an GA log overflow. + */ +void amd_iommu_restart_ga_log(struct amd_iommu *iommu) +{ + u32 status; + + status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); + if (status & MMIO_STATUS_GALOG_RUN_MASK) + return; + + pr_info_ratelimited("IOMMU GA Log restarting\n"); + + iommu_feature_disable(iommu, CONTROL_GALOG_EN); + iommu_feature_disable(iommu, CONTROL_GAINT_EN); + + writel(MMIO_STATUS_GALOG_OVERFLOW_MASK, + iommu->mmio_base + MMIO_STATUS_OFFSET); + + iommu_feature_enable(iommu, CONTROL_GAINT_EN); + iommu_feature_enable(iommu, CONTROL_GALOG_EN); +} + /* * This function resets the command buffer if the IOMMU stopped fetching * commands from it. diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 0ccc6b8319fb..16c5d1b97b56 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -836,6 +836,7 @@ amd_iommu_set_pci_msi_domain(struct device *dev, struct amd_iommu *iommu) { } (MMIO_STATUS_EVT_OVERFLOW_INT_MASK | \ MMIO_STATUS_EVT_INT_MASK | \ MMIO_STATUS_PPR_INT_MASK | \ + MMIO_STATUS_GALOG_OVERFLOW_MASK | \ MMIO_STATUS_GALOG_INT_MASK) irqreturn_t amd_iommu_int_thread(int irq, void *data) @@ -859,10 +860,16 @@ irqreturn_t amd_iommu_int_thread(int irq, void *data) } #ifdef CONFIG_IRQ_REMAP - if (status & MMIO_STATUS_GALOG_INT_MASK) { + if (status & (MMIO_STATUS_GALOG_INT_MASK | + MMIO_STATUS_GALOG_OVERFLOW_MASK)) { pr_devel("Processing IOMMU GA Log\n"); iommu_poll_ga_log(iommu); } + + if (status & MMIO_STATUS_GALOG_OVERFLOW_MASK) { + pr_info_ratelimited("IOMMU GA Log overflow\n"); + amd_iommu_restart_ga_log(iommu); + } #endif if (status & MMIO_STATUS_EVT_OVERFLOW_INT_MASK) { -- cgit 1.4.1 From dac09fec5b57377d4ac1f735762af939fff6fc06 Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Thu, 20 Apr 2023 12:20:13 -0700 Subject: iommu/amd: Fix up merge conflict resolution [ Upstream commit 8ec4e2befef10c7679cd59251956a428e783c0b5 ] Merge commit e17c6debd4b2 ("Merge branches 'arm/mediatek', 'arm/msm', 'arm/renesas', 'arm/rockchip', 'arm/smmu', 'x86/vt-d' and 'x86/amd' into next") added amd_iommu_init_devices, amd_iommu_uninit_devices, and amd_iommu_init_notifier back to drivers/iommu/amd/amd_iommu.h. The only references to them are here, so clean them up. Fixes: e17c6debd4b2 ("Merge branches 'arm/mediatek', 'arm/msm', 'arm/renesas', 'arm/rockchip', 'arm/smmu', 'x86/vt-d' and 'x86/amd' into next") Cc: Joerg Roedel Cc: Suravee Suthikulpanit Cc: Will Deacon Cc: Robin Murphy Signed-off-by: Jerry Snitselaar Reviewed-by: Vasant Hegde Link: https://lore.kernel.org/r/20230420192013.733331-1-jsnitsel@redhat.com Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/amd_iommu.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index 24c7e6c6c0de..471f40351f4c 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -16,9 +16,6 @@ extern irqreturn_t amd_iommu_int_handler(int irq, void *data); extern void amd_iommu_apply_erratum_63(struct amd_iommu *iommu, u16 devid); extern void amd_iommu_restart_event_logging(struct amd_iommu *iommu); extern void amd_iommu_restart_ga_log(struct amd_iommu *iommu); -extern int amd_iommu_init_devices(void); -extern void amd_iommu_uninit_devices(void); -extern void amd_iommu_init_notifier(void); extern void amd_iommu_set_rlookup_table(struct amd_iommu *iommu, u16 devid); #ifdef CONFIG_AMD_IOMMU_DEBUGFS -- cgit 1.4.1 From bd424277a1f86c41254d1068889908067d1f6aa5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 17 May 2023 12:26:44 -0400 Subject: nfsd: make a copy of struct iattr before calling notify_change [ Upstream commit d53d70084d27f56bcdf5074328f2c9ec861be596 ] notify_change can modify the iattr structure. In particular it can end up setting ATTR_MODE when ATTR_KILL_SUID is already set, causing a BUG() if the same iattr is passed to notify_change more than once. Make a copy of the struct iattr before calling notify_change. Reported-by: Zhi Li Link: https://bugzilla.redhat.com/show_bug.cgi?id=2207969 Tested-by: Zhi Li Fixes: 34b91dda7124 ("NFSD: Make nfsd4_setattr() wait before returning NFS4ERR_DELAY") Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/vfs.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index dc3ba13546dd..155b34c4683c 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -469,7 +469,15 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, inode_lock(inode); for (retries = 1;;) { - host_err = __nfsd_setattr(dentry, iap); + struct iattr attrs; + + /* + * notify_change() can alter its iattr argument, making + * @iap unsuitable for submission multiple times. Make a + * copy for every loop iteration. + */ + attrs = *iap; + host_err = __nfsd_setattr(dentry, &attrs); if (host_err != -EAGAIN || !retries--) break; if (!nfsd_wait_for_delegreturn(rqstp, inode)) -- cgit 1.4.1 From 72fef70abe1848649e3840a5c63de8fdd8133bf1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 23 May 2023 21:53:10 -0700 Subject: dmaengine: pl330: rename _start to prevent build error [ Upstream commit a1a5f2c887252dec161c1e12e04303ca9ba56fa9 ] "_start" is used in several arches and proably should be reserved for ARCH usage. Using it in a driver for a private symbol can cause a build error when it conflicts with ARCH usage of the same symbol. Therefore rename pl330's "_start" to "pl330_start_thread" so that there is no conflict and no build error. drivers/dma/pl330.c:1053:13: error: '_start' redeclared as different kind of symbol 1053 | static bool _start(struct pl330_thread *thrd) | ^~~~~~ In file included from ../include/linux/interrupt.h:21, from ../drivers/dma/pl330.c:18: arch/riscv/include/asm/sections.h:11:13: note: previous declaration of '_start' with type 'char[]' 11 | extern char _start[]; | ^~~~~~ Fixes: b7d861d93945 ("DMA: PL330: Merge PL330 driver into drivers/dma/") Fixes: ae43b3289186 ("ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12") Signed-off-by: Randy Dunlap Cc: Jaswinder Singh Cc: Boojin Kim Cc: Krzysztof Kozlowski Cc: Russell King Cc: Vinod Koul Cc: dmaengine@vger.kernel.org Cc: linux-riscv@lists.infradead.org Link: https://lore.kernel.org/r/20230524045310.27923-1-rdunlap@infradead.org Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/pl330.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 0d9257fbdfb0..b4731fe6bbc1 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1050,7 +1050,7 @@ static bool _trigger(struct pl330_thread *thrd) return true; } -static bool _start(struct pl330_thread *thrd) +static bool pl330_start_thread(struct pl330_thread *thrd) { switch (_state(thrd)) { case PL330_STATE_FAULT_COMPLETING: @@ -1702,7 +1702,7 @@ static int pl330_update(struct pl330_dmac *pl330) thrd->req_running = -1; /* Get going again ASAP */ - _start(thrd); + pl330_start_thread(thrd); /* For now, just make a list of callbacks to be done */ list_add_tail(&descdone->rqd, &pl330->req_done); @@ -2089,7 +2089,7 @@ static void pl330_tasklet(struct tasklet_struct *t) } else { /* Make sure the PL330 Channel thread is active */ spin_lock(&pch->thread->dmac->lock); - _start(pch->thread); + pl330_start_thread(pch->thread); spin_unlock(&pch->thread->dmac->lock); } @@ -2107,7 +2107,7 @@ static void pl330_tasklet(struct tasklet_struct *t) if (power_down) { pch->active = true; spin_lock(&pch->thread->dmac->lock); - _start(pch->thread); + pl330_start_thread(pch->thread); spin_unlock(&pch->thread->dmac->lock); power_down = false; } -- cgit 1.4.1 From ddd8d552a8f8d6e15bcdcdd294fb3ee897d4e221 Mon Sep 17 00:00:00 2001 From: Alexandre Ghiti Date: Fri, 19 May 2023 15:13:11 +0200 Subject: riscv: Fix unused variable warning when BUILTIN_DTB is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 33d418da6f476b15e4510e0a590062583f63cd36 ] commit ef69d2559fe9 ("riscv: Move early dtb mapping into the fixmap region") wrongly moved the #ifndef CONFIG_BUILTIN_DTB surrounding the pa variable definition in create_fdt_early_page_table(), so move it back to its right place to quiet the following warning: ../arch/riscv/mm/init.c: In function ‘create_fdt_early_page_table’: ../arch/riscv/mm/init.c:925:12: warning: unused variable ‘pa’ [-Wunused-variable] 925 | uintptr_t pa = dtb_pa & ~(PMD_SIZE - 1); Fixes: ef69d2559fe9 ("riscv: Move early dtb mapping into the fixmap region") Signed-off-by: Alexandre Ghiti Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230519131311.391960-1-alexghiti@rivosinc.com Signed-off-by: Palmer Dabbelt Signed-off-by: Sasha Levin --- arch/riscv/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 6315a3c94225..2b9906ed2d1d 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -845,9 +845,9 @@ static void __init create_kernel_page_table(pgd_t *pgdir, bool early) static void __init create_fdt_early_page_table(uintptr_t fix_fdt_va, uintptr_t dtb_pa) { +#ifndef CONFIG_BUILTIN_DTB uintptr_t pa = dtb_pa & ~(PMD_SIZE - 1); -#ifndef CONFIG_BUILTIN_DTB /* Make sure the fdt fixmap address is always aligned on PMD size */ BUILD_BUG_ON(FIX_FDT % (PMD_SIZE / PAGE_SIZE)); -- cgit 1.4.1 From 9e49af9766ece106bda4db772669c73d2c52a014 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Mon, 24 Apr 2023 12:31:59 +0300 Subject: net/mlx5: Drain health before unregistering devlink [ Upstream commit 824c8dc4a470040bf0e56ba716543839c2498d49 ] mlx5 health mechanism is using devlink APIs, which are using devlink notify APIs. After the cited patch, using devlink notify APIs after devlink is unregistered triggers a WARN_ON(). Hence, drain health WQ before devlink is unregistered. Fixes: cf530217408e ("devlink: Notify users when objects are accessible") Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 91724c5450a0..1a06493da412 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1753,14 +1753,15 @@ static void remove_one(struct pci_dev *pdev) struct devlink *devlink = priv_to_devlink(dev); set_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state); - /* mlx5_drain_fw_reset() is using devlink APIs. Hence, we must drain - * fw_reset before unregistering the devlink. + /* mlx5_drain_fw_reset() and mlx5_drain_health_wq() are using + * devlink notify APIs. + * Hence, we must drain them before unregistering the devlink. */ mlx5_drain_fw_reset(dev); + mlx5_drain_health_wq(dev); devlink_unregister(devlink); mlx5_sriov_disable(pdev); mlx5_crdump_disable(dev); - mlx5_drain_health_wq(dev); mlx5_uninit_one(dev); mlx5_pci_close(dev); mlx5_mdev_uninit(dev); -- cgit 1.4.1 From e73b7de4bf96fdc87e1f353abf37740f842a4a66 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Mon, 24 Apr 2023 12:46:06 +0300 Subject: net/mlx5: SF, Drain health before removing device [ Upstream commit b4646da0573fae9dfa2b8f1f10936cb6eedd7230 ] There is no point in recovery during device removal. Also, if health work started need to wait for it to avoid races and NULL pointer access. Hence, drain health WQ before removing device. Fixes: 1958fc2f0712 ("net/mlx5: SF, Add auxiliary device driver") Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c index a7377619ba6f..2424cdf9cca9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c @@ -63,6 +63,7 @@ static void mlx5_sf_dev_remove(struct auxiliary_device *adev) struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev); struct devlink *devlink = priv_to_devlink(sf_dev->mdev); + mlx5_drain_health_wq(sf_dev->mdev); devlink_unregister(devlink); mlx5_uninit_one(sf_dev->mdev); iounmap(sf_dev->mdev->iseg); -- cgit 1.4.1 From 9c7ae143a01b6421b2144ab89b20996cd25c9886 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Sat, 29 Apr 2023 20:41:41 +0300 Subject: net/mlx5: fw_tracer, Fix event handling [ Upstream commit 341a80de2468f481b1f771683709b5649cbfe513 ] mlx5 driver needs to parse traces with event_id inside the range of first_string_trace and num_string_trace. However, mlx5 is parsing all events with event_id >= first_string_trace. Fix it by checking for the correct range. Fixes: c71ad41ccb0c ("net/mlx5: FW tracer, events handling") Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c index d7b2ee5de115..c4e40834e3ff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c @@ -483,7 +483,7 @@ static void poll_trace(struct mlx5_fw_tracer *tracer, (u64)timestamp_low; break; default: - if (tracer_event->event_id >= tracer->str_db.first_string_trace || + if (tracer_event->event_id >= tracer->str_db.first_string_trace && tracer_event->event_id <= tracer->str_db.first_string_trace + tracer->str_db.num_string_trace) { tracer_event->type = TRACER_EVENT_TYPE_STRING; -- cgit 1.4.1 From 724aa4fd9e36087b7c8b7caaca0746ce871f7db8 Mon Sep 17 00:00:00 2001 From: Dmytro Linkin Date: Wed, 13 Oct 2021 14:39:24 +0300 Subject: net/mlx5e: Don't attach netdev profile while handling internal error [ Upstream commit bdf274750fca17b289404ef03453c4070725302c ] As part of switchdev mode disablement, driver changes port netdevice profile from uplink to nic. If this process is triggered by health recovery flow (PCI reset, for ex.) profile attach would fail because all fw commands aborted when internal error flag is set. As a result, nic netdevice profile is not attached and driver fails to rollback to uplink profile, which leave driver in broken state and cause crash later. To handle broken state do netdevice profile initialization only instead of full attachment and release mdev resources on driver suspend as expected. Actual netdevice attachment is done during driver load. Fixes: c4d7eb57687f ("net/mxl5e: Add change profile method") Signed-off-by: Dmytro Linkin Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 35 ++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 94d010e2d5ef..4e7daa382bc0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -5745,8 +5745,8 @@ void mlx5e_detach_netdev(struct mlx5e_priv *priv) } static int -mlx5e_netdev_attach_profile(struct net_device *netdev, struct mlx5_core_dev *mdev, - const struct mlx5e_profile *new_profile, void *new_ppriv) +mlx5e_netdev_init_profile(struct net_device *netdev, struct mlx5_core_dev *mdev, + const struct mlx5e_profile *new_profile, void *new_ppriv) { struct mlx5e_priv *priv = netdev_priv(netdev); int err; @@ -5762,6 +5762,25 @@ mlx5e_netdev_attach_profile(struct net_device *netdev, struct mlx5_core_dev *mde err = new_profile->init(priv->mdev, priv->netdev); if (err) goto priv_cleanup; + + return 0; + +priv_cleanup: + mlx5e_priv_cleanup(priv); + return err; +} + +static int +mlx5e_netdev_attach_profile(struct net_device *netdev, struct mlx5_core_dev *mdev, + const struct mlx5e_profile *new_profile, void *new_ppriv) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + int err; + + err = mlx5e_netdev_init_profile(netdev, mdev, new_profile, new_ppriv); + if (err) + return err; + err = mlx5e_attach_netdev(priv); if (err) goto profile_cleanup; @@ -5769,7 +5788,6 @@ mlx5e_netdev_attach_profile(struct net_device *netdev, struct mlx5_core_dev *mde profile_cleanup: new_profile->cleanup(priv); -priv_cleanup: mlx5e_priv_cleanup(priv); return err; } @@ -5788,6 +5806,12 @@ int mlx5e_netdev_change_profile(struct mlx5e_priv *priv, priv->profile->cleanup(priv); mlx5e_priv_cleanup(priv); + if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + mlx5e_netdev_init_profile(netdev, mdev, new_profile, new_ppriv); + set_bit(MLX5E_STATE_DESTROYING, &priv->state); + return -EIO; + } + err = mlx5e_netdev_attach_profile(netdev, mdev, new_profile, new_ppriv); if (err) { /* roll back to original profile */ netdev_warn(netdev, "%s: new profile init failed, %d\n", __func__, err); @@ -5847,8 +5871,11 @@ static int mlx5e_suspend(struct auxiliary_device *adev, pm_message_t state) struct net_device *netdev = priv->netdev; struct mlx5_core_dev *mdev = priv->mdev; - if (!netif_device_present(netdev)) + if (!netif_device_present(netdev)) { + if (test_bit(MLX5E_STATE_DESTROYING, &priv->state)) + mlx5e_destroy_mdev_resources(mdev); return -ENODEV; + } mlx5e_detach_netdev(priv); mlx5e_destroy_mdev_resources(mdev); -- cgit 1.4.1 From ae0ef97f1e2b4b552eb08873ba063c2c5cf1c860 Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Wed, 24 May 2023 21:49:08 +0200 Subject: net: mellanox: mlxbf_gige: Fix skb_panic splat under memory pressure [ Upstream commit d68cb7cf1fd0ef4287bc0ecd1ed0b6ae8e05fc70 ] Do skb_put() after a new skb has been successfully allocated otherwise the reused skb leads to skb_panics or incorrect packet sizes. Fixes: f92e1869d74e ("Add Mellanox BlueField Gigabit Ethernet driver") Signed-off-by: Thomas Bogendoerfer Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230524194908.147145-1-tbogendoerfer@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c index afa3b92a6905..0d5a41a2ae01 100644 --- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c +++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c @@ -245,12 +245,6 @@ static bool mlxbf_gige_rx_packet(struct mlxbf_gige *priv, int *rx_pkts) skb = priv->rx_skb[rx_pi_rem]; - skb_put(skb, datalen); - - skb->ip_summed = CHECKSUM_NONE; /* device did not checksum packet */ - - skb->protocol = eth_type_trans(skb, netdev); - /* Alloc another RX SKB for this same index */ rx_skb = mlxbf_gige_alloc_skb(priv, MLXBF_GIGE_DEFAULT_BUF_SZ, &rx_buf_dma, DMA_FROM_DEVICE); @@ -259,6 +253,13 @@ static bool mlxbf_gige_rx_packet(struct mlxbf_gige *priv, int *rx_pkts) priv->rx_skb[rx_pi_rem] = rx_skb; dma_unmap_single(priv->dev, *rx_wqe_addr, MLXBF_GIGE_DEFAULT_BUF_SZ, DMA_FROM_DEVICE); + + skb_put(skb, datalen); + + skb->ip_summed = CHECKSUM_NONE; /* device did not checksum packet */ + + skb->protocol = eth_type_trans(skb, netdev); + *rx_wqe_addr = rx_buf_dma; } else if (rx_cqe & MLXBF_GIGE_RX_CQE_PKT_STATUS_MAC_ERR) { priv->stats.rx_mac_errors++; -- cgit 1.4.1 From a2c2364e5f5339ff8cf65bc8aeb069b7d0a06e4c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 24 May 2023 14:14:56 +0000 Subject: netrom: fix info-leak in nr_write_internal() [ Upstream commit 31642e7089df8fd3f54ca7843f7ee2952978cad1 ] Simon Kapadia reported the following issue: The Online Amateur Radio Community (OARC) has recently been experimenting with building a nationwide packet network in the UK. As part of our experimentation, we have been testing out packet on 300bps HF, and playing with net/rom. For HF packet at this baud rate you really need to make sure that your MTU is relatively low; AX.25 suggests a PACLEN of 60, and a net/rom PACLEN of 40 to go with that. However the Linux net/rom support didn't work with a low PACLEN; the mkiss module would truncate packets if you set the PACLEN below about 200 or so, e.g.: Apr 19 14:00:51 radio kernel: [12985.747310] mkiss: ax1: truncating oversized transmit packet! This didn't make any sense to me (if the packets are smaller why would they be truncated?) so I started investigating. I looked at the packets using ethereal, and found that many were just huge compared to what I would expect. A simple net/rom connection request packet had the request and then a bunch of what appeared to be random data following it: Simon provided a patch that I slightly revised: Not only we must not use skb_tailroom(), we also do not want to count NR_NETWORK_LEN twice. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Co-Developed-by: Simon Kapadia Signed-off-by: Simon Kapadia Signed-off-by: Eric Dumazet Tested-by: Simon Kapadia Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230524141456.1045467-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/netrom/nr_subr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c index 3f99b432ea70..e2d2af924cff 100644 --- a/net/netrom/nr_subr.c +++ b/net/netrom/nr_subr.c @@ -123,7 +123,7 @@ void nr_write_internal(struct sock *sk, int frametype) unsigned char *dptr; int len, timeout; - len = NR_NETWORK_LEN + NR_TRANSPORT_LEN; + len = NR_TRANSPORT_LEN; switch (frametype & 0x0F) { case NR_CONNREQ: @@ -141,7 +141,8 @@ void nr_write_internal(struct sock *sk, int frametype) return; } - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) + skb = alloc_skb(NR_NETWORK_LEN + len, GFP_ATOMIC); + if (!skb) return; /* @@ -149,7 +150,7 @@ void nr_write_internal(struct sock *sk, int frametype) */ skb_reserve(skb, NR_NETWORK_LEN); - dptr = skb_put(skb, skb_tailroom(skb)); + dptr = skb_put(skb, len); switch (frametype & 0x0F) { case NR_CONNREQ: -- cgit 1.4.1 From 0b64a2bf169965da0273ef29919b2a6b76a9c68b Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 24 May 2023 16:29:34 -0700 Subject: af_packet: Fix data-races of pkt_sk(sk)->num. [ Upstream commit 822b5a1c17df7e338b9f05d1cfe5764e37c7f74f ] syzkaller found a data race of pkt_sk(sk)->num. The value is changed under lock_sock() and po->bind_lock, so we need READ_ONCE() to access pkt_sk(sk)->num without these locks in packet_bind_spkt(), packet_bind(), and sk_diag_fill(). Note that WRITE_ONCE() is already added by commit c7d2ef5dd4b0 ("net/packet: annotate accesses to po->bind"). BUG: KCSAN: data-race in packet_bind / packet_do_bind write (marked) to 0xffff88802ffd1cee of 2 bytes by task 7322 on cpu 0: packet_do_bind+0x446/0x640 net/packet/af_packet.c:3236 packet_bind+0x99/0xe0 net/packet/af_packet.c:3321 __sys_bind+0x19b/0x1e0 net/socket.c:1803 __do_sys_bind net/socket.c:1814 [inline] __se_sys_bind net/socket.c:1812 [inline] __x64_sys_bind+0x40/0x50 net/socket.c:1812 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc read to 0xffff88802ffd1cee of 2 bytes by task 7318 on cpu 1: packet_bind+0xbf/0xe0 net/packet/af_packet.c:3322 __sys_bind+0x19b/0x1e0 net/socket.c:1803 __do_sys_bind net/socket.c:1814 [inline] __se_sys_bind net/socket.c:1812 [inline] __x64_sys_bind+0x40/0x50 net/socket.c:1812 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc value changed: 0x0300 -> 0x0000 Reported by Kernel Concurrency Sanitizer on: CPU: 1 PID: 7318 Comm: syz-executor.4 Not tainted 6.3.0-13380-g7fddb5b5300c #4 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 Fixes: 96ec6327144e ("packet: Diag core and basic socket info dumping") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzkaller Signed-off-by: Kuniyuki Iwashima Reviewed-by: Willem de Bruijn Link: https://lore.kernel.org/r/20230524232934.50950-1-kuniyu@amazon.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/packet/af_packet.c | 4 ++-- net/packet/diag.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 67771b0f5771..c75191b0c82b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3291,7 +3291,7 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data)); name[sizeof(uaddr->sa_data)] = 0; - return packet_do_bind(sk, name, 0, pkt_sk(sk)->num); + return packet_do_bind(sk, name, 0, READ_ONCE(pkt_sk(sk)->num)); } static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) @@ -3309,7 +3309,7 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len return -EINVAL; return packet_do_bind(sk, NULL, sll->sll_ifindex, - sll->sll_protocol ? : pkt_sk(sk)->num); + sll->sll_protocol ? : READ_ONCE(pkt_sk(sk)->num)); } static struct proto packet_proto = { diff --git a/net/packet/diag.c b/net/packet/diag.c index d704c7bf51b2..a68a84574c73 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -143,7 +143,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, rp = nlmsg_data(nlh); rp->pdiag_family = AF_PACKET; rp->pdiag_type = sk->sk_type; - rp->pdiag_num = ntohs(po->num); + rp->pdiag_num = ntohs(READ_ONCE(po->num)); rp->pdiag_ino = sk_ino; sock_diag_save_cookie(sk, rp->pdiag_cookie); -- cgit 1.4.1 From d615070b0eb8cd84f416a066c4f2157ee7632864 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 24 May 2023 22:17:41 -0700 Subject: tls: improve lockless access safety of tls_err_abort() [ Upstream commit 8a0d57df8938e9fd2e99d47a85b7f37d86f91097 ] Most protos' poll() methods insert a memory barrier between writes to sk_err and sk_error_report(). This dates back to commit a4d258036ed9 ("tcp: Fix race in tcp_poll"). I guess we should do the same thing in TLS, tcp_poll() does not hold the socket lock. Fixes: 3c4d7559159b ("tls: kernel TLS support") Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/tls/tls_strp.c | 4 +++- net/tls/tls_sw.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index da95abbb7ea3..f37f4a0fcd3c 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -20,7 +20,9 @@ static void tls_strp_abort_strp(struct tls_strparser *strp, int err) strp->stopped = 1; /* Report an error on the lower socket */ - strp->sk->sk_err = -err; + WRITE_ONCE(strp->sk->sk_err, -err); + /* Paired with smp_rmb() in tcp_poll() */ + smp_wmb(); sk_error_report(strp->sk); } diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 992092aeebad..2e5e7853a610 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -67,7 +67,9 @@ noinline void tls_err_abort(struct sock *sk, int err) { WARN_ON_ONCE(err >= 0); /* sk->sk_err should contain a positive error code. */ - sk->sk_err = -err; + WRITE_ONCE(sk->sk_err, -err); + /* Paired with smp_rmb() in tcp_poll() */ + smp_wmb(); sk_error_report(sk); } -- cgit 1.4.1 From 7164961a9ce054cc22a5cc68d89ff68b8cd342bc Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Thu, 25 May 2023 23:56:12 +0530 Subject: amd-xgbe: fix the false linkup in xgbe_phy_status [ Upstream commit dc362e20cd6ab7a93d1b09669730c406f0910c35 ] In the event of a change in XGBE mode, the current auto-negotiation needs to be reset and the AN cycle needs to be re-triggerred. However, the current code ignores the return value of xgbe_set_mode(), leading to false information as the link is declared without checking the status register. Fix this by propagating the mode switch status information to xgbe_phy_status(). Fixes: e57f7a3feaef ("amd-xgbe: Prepare for working with more than one type of phy") Co-developed-by: Sudheesh Mavila Signed-off-by: Sudheesh Mavila Reviewed-by: Simon Horman Acked-by: Shyam Sundar S K Signed-off-by: Raju Rangoju Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 43fdd111235a..ca7372369b3e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1312,7 +1312,7 @@ static enum xgbe_mode xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) return pdata->phy_if.phy_impl.an_outcome(pdata); } -static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) +static bool xgbe_phy_status_result(struct xgbe_prv_data *pdata) { struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; @@ -1347,8 +1347,13 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) pdata->phy.duplex = DUPLEX_FULL; - if (xgbe_set_mode(pdata, mode) && pdata->an_again) + if (!xgbe_set_mode(pdata, mode)) + return false; + + if (pdata->an_again) xgbe_phy_reconfig_aneg(pdata); + + return true; } static void xgbe_phy_status(struct xgbe_prv_data *pdata) @@ -1378,7 +1383,8 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) return; } - xgbe_phy_status_result(pdata); + if (xgbe_phy_status_result(pdata)) + return; if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) clear_bit(XGBE_LINK_INIT, &pdata->dev_state); -- cgit 1.4.1 From 789394f1df8b2221834db204a3a537e2becdbd20 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 25 May 2023 14:20:38 -0700 Subject: perf ftrace latency: Remove unnecessary "--" from --use-nsec option [ Upstream commit 8d73259ef23f449329294dc187932f7470268126 ] The option name should not have the dashes. Current version shows four dashes for the option. $ perf ftrace latency -h Usage: perf ftrace [] [] or: perf ftrace [] -- [] [] or: perf ftrace {trace|latency} [] [] or: perf ftrace {trace|latency} [] -- [] [] -b, --use-bpf Use BPF to measure function latency -n, ----use-nsec Use nano-second histogram -T, --trace-funcs Show latency of given function Fixes: 84005bb6148618cc ("perf ftrace latency: Add -n/--use-nsec option") Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Changbin Du Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20230525212038.3535851-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-ftrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 4bc5b7cf3e04..1d40f9bcb63b 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -1175,7 +1175,7 @@ int cmd_ftrace(int argc, const char **argv) OPT_BOOLEAN('b', "use-bpf", &ftrace.target.use_bpf, "Use BPF to measure function latency"), #endif - OPT_BOOLEAN('n', "--use-nsec", &ftrace.use_nsec, + OPT_BOOLEAN('n', "use-nsec", &ftrace.use_nsec, "Use nano-second histogram"), OPT_PARENT(common_options), }; -- cgit 1.4.1 From 3cf774741459edd57363973a334bb24a077dd4d9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 22:21:24 +0200 Subject: mtd: rawnand: ingenic: fix empty stub helper definitions [ Upstream commit 650a8884a364ff2568b51cde9009cfd43cdae6ad ] A few functions provide an empty interface definition when CONFIG_MTD_NAND_INGENIC_ECC is disabled, but they are accidentally defined as global functions in the header: drivers/mtd/nand/raw/ingenic/ingenic_ecc.h:39:5: error: no previous prototype for 'ingenic_ecc_calculate' drivers/mtd/nand/raw/ingenic/ingenic_ecc.h:46:5: error: no previous prototype for 'ingenic_ecc_correct' drivers/mtd/nand/raw/ingenic/ingenic_ecc.h:53:6: error: no previous prototype for 'ingenic_ecc_release' drivers/mtd/nand/raw/ingenic/ingenic_ecc.h:57:21: error: no previous prototype for 'of_ingenic_ecc_get' Turn them into 'static inline' definitions instead. Fixes: 15de8c6efd0e ("mtd: rawnand: ingenic: Separate top-level and SoC specific code") Signed-off-by: Arnd Bergmann Reviewed-by: Paul Cercueil Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230516202133.559488-1-arnd@kernel.org Signed-off-by: Sasha Levin --- drivers/mtd/nand/raw/ingenic/ingenic_ecc.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.h b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.h index 2cda439b5e11..017868f59f22 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.h +++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.h @@ -36,25 +36,25 @@ int ingenic_ecc_correct(struct ingenic_ecc *ecc, void ingenic_ecc_release(struct ingenic_ecc *ecc); struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *np); #else /* CONFIG_MTD_NAND_INGENIC_ECC */ -int ingenic_ecc_calculate(struct ingenic_ecc *ecc, +static inline int ingenic_ecc_calculate(struct ingenic_ecc *ecc, struct ingenic_ecc_params *params, const u8 *buf, u8 *ecc_code) { return -ENODEV; } -int ingenic_ecc_correct(struct ingenic_ecc *ecc, +static inline int ingenic_ecc_correct(struct ingenic_ecc *ecc, struct ingenic_ecc_params *params, u8 *buf, u8 *ecc_code) { return -ENODEV; } -void ingenic_ecc_release(struct ingenic_ecc *ecc) +static inline void ingenic_ecc_release(struct ingenic_ecc *ecc) { } -struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *np) +static inline struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *np) { return ERR_PTR(-ENODEV); } -- cgit 1.4.1 From 07322c8a12d6c796450faacb8be9e5e3c278ec84 Mon Sep 17 00:00:00 2001 From: Mustafa Ismail Date: Mon, 22 May 2023 10:56:53 -0500 Subject: RDMA/irdma: Prevent QP use after free [ Upstream commit c8f304d75f6c6cc679a73f89591f9a915da38f09 ] There is a window where the poll cq may use a QP that has been freed. This can happen if a CQE is polled before irdma_clean_cqes() can clear the CQE's related to the QP and the destroy QP races to free the QP memory. then the QP structures are used in irdma_poll_cq. Fix this by moving the clearing of CQE's before the reference is removed and the QP is destroyed. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://lore.kernel.org/r/20230522155654.1309-3-shiraz.saleem@intel.com Signed-off-by: Mustafa Ismail Signed-off-by: Shiraz Saleem Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index f6973ea55eda..1c5a61f51a67 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -522,11 +522,6 @@ static int irdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) if (!iwqp->user_mode) cancel_delayed_work_sync(&iwqp->dwork_flush); - irdma_qp_rem_ref(&iwqp->ibqp); - wait_for_completion(&iwqp->free_qp); - irdma_free_lsmm_rsrc(iwqp); - irdma_cqp_qp_destroy_cmd(&iwdev->rf->sc_dev, &iwqp->sc_qp); - if (!iwqp->user_mode) { if (iwqp->iwscq) { irdma_clean_cqes(iwqp, iwqp->iwscq); @@ -534,6 +529,12 @@ static int irdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) irdma_clean_cqes(iwqp, iwqp->iwrcq); } } + + irdma_qp_rem_ref(&iwqp->ibqp); + wait_for_completion(&iwqp->free_qp); + irdma_free_lsmm_rsrc(iwqp); + irdma_cqp_qp_destroy_cmd(&iwdev->rf->sc_dev, &iwqp->sc_qp); + irdma_remove_push_mmap_entries(iwqp); irdma_free_qp_rsrc(iwqp); -- cgit 1.4.1 From 906134664f33a2d0007003d5e7c699c844f20b28 Mon Sep 17 00:00:00 2001 From: Mustafa Ismail Date: Mon, 22 May 2023 10:56:54 -0500 Subject: RDMA/irdma: Fix Local Invalidate fencing [ Upstream commit 5842d1d9c1b0d17e0c29eae65ae1f245f83682dd ] If the local invalidate fence is indicated in the WR, only the read fence is currently being set in WQE. Fix this to set both the read and local fence in the WQE. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://lore.kernel.org/r/20230522155654.1309-4-shiraz.saleem@intel.com Signed-off-by: Mustafa Ismail Signed-off-by: Shiraz Saleem Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 1c5a61f51a67..6a8bb6ed4bf4 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3191,6 +3191,7 @@ static int irdma_post_send(struct ib_qp *ibqp, break; case IB_WR_LOCAL_INV: info.op_type = IRDMA_OP_TYPE_INV_STAG; + info.local_fence = info.read_fence; info.op.inv_local_stag.target_stag = ib_wr->ex.invalidate_rkey; err = irdma_uk_stag_local_invalidate(ukqp, &info, true); break; -- cgit 1.4.1 From 0dec22a09da1e93126258a1e89812caa80a4b8a0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 26 May 2023 15:43:42 +0000 Subject: af_packet: do not use READ_ONCE() in packet_bind() [ Upstream commit 6ffc57ea004234d9373c57b204fd10370a69f392 ] A recent patch added READ_ONCE() in packet_bind() and packet_bind_spkt() This is better handled by reading pkt_sk(sk)->num later in packet_do_bind() while appropriate lock is held. READ_ONCE() in writers are often an evidence of something being wrong. Fixes: 822b5a1c17df ("af_packet: Fix data-races of pkt_sk(sk)->num.") Signed-off-by: Eric Dumazet Reviewed-by: Willem de Bruijn Reviewed-by: Jiri Pirko Reviewed-by: Kuniyuki Iwashima Link: https://lore.kernel.org/r/20230526154342.2533026-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/packet/af_packet.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index c75191b0c82b..6ab9d5b54338 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3193,6 +3193,9 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, lock_sock(sk); spin_lock(&po->bind_lock); + if (!proto) + proto = po->num; + rcu_read_lock(); if (po->fanout) { @@ -3291,7 +3294,7 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data)); name[sizeof(uaddr->sa_data)] = 0; - return packet_do_bind(sk, name, 0, READ_ONCE(pkt_sk(sk)->num)); + return packet_do_bind(sk, name, 0, 0); } static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) @@ -3308,8 +3311,7 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len if (sll->sll_family != AF_PACKET) return -EINVAL; - return packet_do_bind(sk, NULL, sll->sll_ifindex, - sll->sll_protocol ? : READ_ONCE(pkt_sk(sk)->num)); + return packet_do_bind(sk, NULL, sll->sll_ifindex, sll->sll_protocol); } static struct proto packet_proto = { -- cgit 1.4.1 From c2251ce0483d897383ac44cd3ba4f9733177a8c6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 26 May 2023 16:34:58 +0000 Subject: tcp: deny tcp_disconnect() when threads are waiting [ Upstream commit 4faeee0cf8a5d88d63cdbc3bab124fb0e6aed08c ] Historically connect(AF_UNSPEC) has been abused by syzkaller and other fuzzers to trigger various bugs. A recent one triggers a divide-by-zero [1], and Paolo Abeni was able to diagnose the issue. tcp_recvmsg_locked() has tests about sk_state being not TCP_LISTEN and TCP REPAIR mode being not used. Then later if socket lock is released in sk_wait_data(), another thread can call connect(AF_UNSPEC), then make this socket a TCP listener. When recvmsg() is resumed, it can eventually call tcp_cleanup_rbuf() and attempt a divide by 0 in tcp_rcv_space_adjust() [1] This patch adds a new socket field, counting number of threads blocked in sk_wait_event() and inet_wait_for_connect(). If this counter is not zero, tcp_disconnect() returns an error. This patch adds code in blocking socket system calls, thus should not hurt performance of non blocking ones. Note that we probably could revert commit 499350a5a6e7 ("tcp: initialize rcv_mss to TCP_MIN_MSS instead of 0") to restore original tcpi_rcv_mss meaning (was 0 if no payload was ever received on a socket) [1] divide error: 0000 [#1] PREEMPT SMP KASAN CPU: 0 PID: 13832 Comm: syz-executor.5 Not tainted 6.3.0-rc4-syzkaller-00224-g00c7b5f4ddc5 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/02/2023 RIP: 0010:tcp_rcv_space_adjust+0x36e/0x9d0 net/ipv4/tcp_input.c:740 Code: 00 00 00 00 fc ff df 4c 89 64 24 48 8b 44 24 04 44 89 f9 41 81 c7 80 03 00 00 c1 e1 04 44 29 f0 48 63 c9 48 01 e9 48 0f af c1 <49> f7 f6 48 8d 04 41 48 89 44 24 40 48 8b 44 24 30 48 c1 e8 03 48 RSP: 0018:ffffc900033af660 EFLAGS: 00010206 RAX: 4a66b76cbade2c48 RBX: ffff888076640cc0 RCX: 00000000c334e4ac RDX: 0000000000000000 RSI: dffffc0000000000 RDI: 0000000000000001 RBP: 00000000c324e86c R08: 0000000000000001 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8880766417f8 R13: ffff888028fbb980 R14: 0000000000000000 R15: 0000000000010344 FS: 00007f5bffbfe700(0000) GS:ffff8880b9800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000001b32f25000 CR3: 000000007ced0000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: tcp_recvmsg_locked+0x100e/0x22e0 net/ipv4/tcp.c:2616 tcp_recvmsg+0x117/0x620 net/ipv4/tcp.c:2681 inet6_recvmsg+0x114/0x640 net/ipv6/af_inet6.c:670 sock_recvmsg_nosec net/socket.c:1017 [inline] sock_recvmsg+0xe2/0x160 net/socket.c:1038 ____sys_recvmsg+0x210/0x5a0 net/socket.c:2720 ___sys_recvmsg+0xf2/0x180 net/socket.c:2762 do_recvmmsg+0x25e/0x6e0 net/socket.c:2856 __sys_recvmmsg net/socket.c:2935 [inline] __do_sys_recvmmsg net/socket.c:2958 [inline] __se_sys_recvmmsg net/socket.c:2951 [inline] __x64_sys_recvmmsg+0x20f/0x260 net/socket.c:2951 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x39/0xb0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7f5c0108c0f9 Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 f1 19 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f5bffbfe168 EFLAGS: 00000246 ORIG_RAX: 000000000000012b RAX: ffffffffffffffda RBX: 00007f5c011ac050 RCX: 00007f5c0108c0f9 RDX: 0000000000000001 RSI: 0000000020000bc0 RDI: 0000000000000003 RBP: 00007f5c010e7b39 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000122 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f5c012cfb1f R14: 00007f5bffbfe300 R15: 0000000000022000 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Reported-by: Paolo Abeni Diagnosed-by: Paolo Abeni Signed-off-by: Eric Dumazet Tested-by: Paolo Abeni Link: https://lore.kernel.org/r/20230526163458.2880232-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/sock.h | 4 ++++ net/ipv4/af_inet.c | 2 ++ net/ipv4/inet_connection_sock.c | 1 + net/ipv4/tcp.c | 6 ++++++ 4 files changed, 13 insertions(+) diff --git a/include/net/sock.h b/include/net/sock.h index beb1b747fb09..f11b98bd0244 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -333,6 +333,7 @@ struct sk_filter; * @sk_cgrp_data: cgroup data for this cgroup * @sk_memcg: this socket's memory cgroup association * @sk_write_pending: a write to stream socket waits to start + * @sk_wait_pending: number of threads blocked on this socket * @sk_state_change: callback to indicate change in the state of the sock * @sk_data_ready: callback to indicate there is data to be processed * @sk_write_space: callback to indicate there is bf sending space available @@ -425,6 +426,7 @@ struct sock { unsigned int sk_napi_id; #endif int sk_rcvbuf; + int sk_wait_pending; struct sk_filter __rcu *sk_filter; union { @@ -1170,6 +1172,7 @@ static inline void sock_rps_reset_rxhash(struct sock *sk) #define sk_wait_event(__sk, __timeo, __condition, __wait) \ ({ int __rc; \ + __sk->sk_wait_pending++; \ release_sock(__sk); \ __rc = __condition; \ if (!__rc) { \ @@ -1179,6 +1182,7 @@ static inline void sock_rps_reset_rxhash(struct sock *sk) } \ sched_annotate_sleep(); \ lock_sock(__sk); \ + __sk->sk_wait_pending--; \ __rc = __condition; \ __rc; \ }) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 5fd0ff5734e3..ebb737ac9e89 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -589,6 +589,7 @@ static long inet_wait_for_connect(struct sock *sk, long timeo, int writebias) add_wait_queue(sk_sleep(sk), &wait); sk->sk_write_pending += writebias; + sk->sk_wait_pending++; /* Basic assumption: if someone sets sk->sk_err, he _must_ * change state of the socket from TCP_SYN_*. @@ -604,6 +605,7 @@ static long inet_wait_for_connect(struct sock *sk, long timeo, int writebias) } remove_wait_queue(sk_sleep(sk), &wait); sk->sk_write_pending -= writebias; + sk->sk_wait_pending--; return timeo; } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 916075e00d06..8e35ea66d930 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1143,6 +1143,7 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, if (newsk) { struct inet_connection_sock *newicsk = inet_csk(newsk); + newsk->sk_wait_pending = 0; inet_sk_set_state(newsk, TCP_SYN_RECV); newicsk->icsk_bind_hash = NULL; newicsk->icsk_bind2_hash = NULL; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 021a8bf6a189..c77b57d4a832 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3079,6 +3079,12 @@ int tcp_disconnect(struct sock *sk, int flags) int old_state = sk->sk_state; u32 seq; + /* Deny disconnect if other threads are blocked in sk_wait_event() + * or inet_wait_for_connect(). + */ + if (sk->sk_wait_pending) + return -EBUSY; + if (old_state != TCP_CLOSE) tcp_set_state(sk, TCP_CLOSE); -- cgit 1.4.1 From 752836e1a206ac817d30b833a41c8cbff60756f6 Mon Sep 17 00:00:00 2001 From: Cambda Zhu Date: Sat, 27 May 2023 12:03:17 +0800 Subject: tcp: Return user_mss for TCP_MAXSEG in CLOSE/LISTEN state if user_mss set [ Upstream commit 34dfde4ad87b84d21278a7e19d92b5b2c68e6c4d ] This patch replaces the tp->mss_cache check in getting TCP_MAXSEG with tp->rx_opt.user_mss check for CLOSE/LISTEN sock. Since tp->mss_cache is initialized with TCP_MSS_DEFAULT, checking if it's zero is probably a bug. With this change, getting TCP_MAXSEG before connecting will return default MSS normally, and return user_mss if user_mss is set. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Jack Yang Suggested-by: Eric Dumazet Link: https://lore.kernel.org/netdev/CANn89i+3kL9pYtkxkwxwNMzvC_w3LNUum_2=3u+UyLBmGmifHA@mail.gmail.com/#t Signed-off-by: Cambda Zhu Link: https://lore.kernel.org/netdev/14D45862-36EA-4076-974C-EA67513C92F6@linux.alibaba.com/ Reviewed-by: Jason Xing Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20230527040317.68247-1-cambda@linux.alibaba.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c77b57d4a832..0bd0be3c63d2 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4071,7 +4071,8 @@ int do_tcp_getsockopt(struct sock *sk, int level, switch (optname) { case TCP_MAXSEG: val = tp->mss_cache; - if (!val && ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) + if (tp->rx_opt.user_mss && + ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) val = tp->rx_opt.user_mss; if (tp->repair) val = tp->rx_opt.mss_clamp; -- cgit 1.4.1 From 8c3ec8e7895e8358e4e2b2fa274a91f667038534 Mon Sep 17 00:00:00 2001 From: Wen Gu Date: Fri, 26 May 2023 19:49:00 +0800 Subject: net/smc: Scan from current RMB list when no position specified [ Upstream commit b24aa141c2ff26c919237aee61ea1818fc6780d9 ] When finding the first RMB of link group, it should start from the current RMB list whose index is 0. So fix it. Fixes: b4ba4652b3f8 ("net/smc: extend LLC layer for SMC-Rv2") Signed-off-by: Wen Gu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/smc/smc_llc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index 524649d0ab65..85af5bfa9622 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -578,7 +578,10 @@ static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr, { struct smc_buf_desc *buf_next; - if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) { + if (!buf_pos) + return _smc_llc_get_next_rmb(lgr, buf_lst); + + if (list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) { (*buf_lst)++; return _smc_llc_get_next_rmb(lgr, buf_lst); } -- cgit 1.4.1 From d67a5a587c45f1f330c126fc96a50728caed03f8 Mon Sep 17 00:00:00 2001 From: Wen Gu Date: Fri, 26 May 2023 19:49:01 +0800 Subject: net/smc: Don't use RMBs not mapped to new link in SMCRv2 ADD LINK [ Upstream commit 71c6aa0305e3d2365d3bfd0134b4025d9e7ba388 ] We encountered a crash when using SMCRv2. It is caused by a logical error in smc_llc_fill_ext_v2(). BUG: kernel NULL pointer dereference, address: 0000000000000014 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP PTI CPU: 7 PID: 453 Comm: kworker/7:4 Kdump: loaded Tainted: G W E 6.4.0-rc3+ #44 Workqueue: events smc_llc_add_link_work [smc] RIP: 0010:smc_llc_fill_ext_v2+0x117/0x280 [smc] RSP: 0018:ffffacb5c064bd88 EFLAGS: 00010282 RAX: ffff9a6bc1c3c02c RBX: ffff9a6be3558000 RCX: 0000000000000000 RDX: 0000000000000002 RSI: 0000000000000002 RDI: 000000000000000a RBP: ffffacb5c064bdb8 R08: 0000000000000040 R09: 000000000000000c R10: ffff9a6bc0910300 R11: 0000000000000002 R12: 0000000000000000 R13: 0000000000000002 R14: ffff9a6bc1c3c02c R15: ffff9a6be3558250 FS: 0000000000000000(0000) GS:ffff9a6eefdc0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000014 CR3: 000000010b078003 CR4: 00000000003706e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: smc_llc_send_add_link+0x1ae/0x2f0 [smc] smc_llc_srv_add_link+0x2c9/0x5a0 [smc] ? cc_mkenc+0x40/0x60 smc_llc_add_link_work+0xb8/0x140 [smc] process_one_work+0x1e5/0x3f0 worker_thread+0x4d/0x2f0 ? __pfx_worker_thread+0x10/0x10 kthread+0xe5/0x120 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x2c/0x50 When an alernate RNIC is available in system, SMC will try to add a new link based on the RNIC for resilience. All the RMBs in use will be mapped to the new link. Then the RMBs' MRs corresponding to the new link will be filled into SMCRv2 LLC ADD LINK messages. However, smc_llc_fill_ext_v2() mistakenly accesses to unused RMBs which haven't been mapped to the new link and have no valid MRs, thus causing a crash. So this patch fixes the logic. Fixes: b4ba4652b3f8 ("net/smc: extend LLC layer for SMC-Rv2") Signed-off-by: Wen Gu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/smc/smc_llc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index 85af5bfa9622..3008dfdf7c55 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -617,6 +617,8 @@ static int smc_llc_fill_ext_v2(struct smc_llc_msg_add_link_v2_ext *ext, goto out; buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); for (i = 0; i < ext->num_rkeys; i++) { + while (buf_pos && !(buf_pos)->used) + buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); if (!buf_pos) break; rmb = buf_pos; @@ -626,8 +628,6 @@ static int smc_llc_fill_ext_v2(struct smc_llc_msg_add_link_v2_ext *ext, cpu_to_be64((uintptr_t)rmb->cpu_addr) : cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); - while (buf_pos && !(buf_pos)->used) - buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); } len += i * sizeof(ext->rt[0]); out: -- cgit 1.4.1 From 89a0f4dcae43941d3193bc56ae573f74483fd06d Mon Sep 17 00:00:00 2001 From: Peilin Ye Date: Mon, 29 May 2023 12:52:55 -0700 Subject: net/sched: sch_ingress: Only create under TC_H_INGRESS [ Upstream commit c7cfbd115001f94de9e4053657946a383147e803 ] ingress Qdiscs are only supposed to be created under TC_H_INGRESS. Return -EOPNOTSUPP if 'parent' is not TC_H_INGRESS, similar to mq_init(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot+b53a9c0d1ea4ad62da8b@syzkaller.appspotmail.com Closes: https://lore.kernel.org/r/0000000000006cf87705f79acf1a@google.com/ Tested-by: Pedro Tammela Acked-by: Jamal Hadi Salim Reviewed-by: Jamal Hadi Salim Reviewed-by: Vlad Buslov Signed-off-by: Peilin Ye Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_ingress.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 84838128b9c5..f9ef6deb2770 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -80,6 +80,9 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt, struct net_device *dev = qdisc_dev(sch); int err; + if (sch->parent != TC_H_INGRESS) + return -EOPNOTSUPP; + net_inc_ingress_queue(); mini_qdisc_pair_init(&q->miniqp, sch, &dev->miniq_ingress); @@ -101,6 +104,9 @@ static void ingress_destroy(struct Qdisc *sch) { struct ingress_sched_data *q = qdisc_priv(sch); + if (sch->parent != TC_H_INGRESS) + return; + tcf_block_put_ext(q->block, sch, &q->block_info); net_dec_ingress_queue(); } -- cgit 1.4.1 From 1ec1434630b36f85985c6109bf8fe6ec02f44b28 Mon Sep 17 00:00:00 2001 From: Peilin Ye Date: Mon, 29 May 2023 12:53:21 -0700 Subject: net/sched: sch_clsact: Only create under TC_H_CLSACT [ Upstream commit 5eeebfe6c493192b10d516abfd72742900f2a162 ] clsact Qdiscs are only supposed to be created under TC_H_CLSACT (which equals TC_H_INGRESS). Return -EOPNOTSUPP if 'parent' is not TC_H_CLSACT. Fixes: 1f211a1b929c ("net, sched: add clsact qdisc") Tested-by: Pedro Tammela Acked-by: Jamal Hadi Salim Reviewed-by: Jamal Hadi Salim Reviewed-by: Vlad Buslov Signed-off-by: Peilin Ye Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_ingress.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index f9ef6deb2770..35963929e117 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -225,6 +225,9 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt, struct net_device *dev = qdisc_dev(sch); int err; + if (sch->parent != TC_H_CLSACT) + return -EOPNOTSUPP; + net_inc_ingress_queue(); net_inc_egress_queue(); @@ -254,6 +257,9 @@ static void clsact_destroy(struct Qdisc *sch) { struct clsact_sched_data *q = qdisc_priv(sch); + if (sch->parent != TC_H_CLSACT) + return; + tcf_block_put_ext(q->egress_block, sch, &q->egress_block_info); tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info); -- cgit 1.4.1 From be3e1f71cb6fd460211a87b4a64404d5a69e2e94 Mon Sep 17 00:00:00 2001 From: Peilin Ye Date: Mon, 29 May 2023 12:54:03 -0700 Subject: net/sched: Reserve TC_H_INGRESS (TC_H_CLSACT) for ingress (clsact) Qdiscs [ Upstream commit f85fa45d4a9408d98c46c8fa45ba2e3b2f4bf219 ] Currently it is possible to add e.g. an HTB Qdisc under ffff:fff1 (TC_H_INGRESS, TC_H_CLSACT): $ ip link add name ifb0 type ifb $ tc qdisc add dev ifb0 parent ffff:fff1 htb $ tc qdisc add dev ifb0 clsact Error: Exclusivity flag on, cannot modify. $ drgn ... >>> ifb0 = netdev_get_by_name(prog, "ifb0") >>> qdisc = ifb0.ingress_queue.qdisc_sleeping >>> print(qdisc.ops.id.string_().decode()) htb >>> qdisc.flags.value_() # TCQ_F_INGRESS 2 Only allow ingress and clsact Qdiscs under ffff:fff1. Return -EINVAL for everything else. Make TCQ_F_INGRESS a static flag of ingress and clsact Qdiscs. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 1f211a1b929c ("net, sched: add clsact qdisc") Tested-by: Pedro Tammela Acked-by: Jamal Hadi Salim Reviewed-by: Jamal Hadi Salim Reviewed-by: Vlad Buslov Signed-off-by: Peilin Ye Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_api.c | 7 ++++++- net/sched/sch_ingress.c | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index c82532e20699..2244e00ea9a1 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1241,7 +1241,12 @@ static struct Qdisc *qdisc_create(struct net_device *dev, sch->parent = parent; if (handle == TC_H_INGRESS) { - sch->flags |= TCQ_F_INGRESS; + if (!(sch->flags & TCQ_F_INGRESS)) { + NL_SET_ERR_MSG(extack, + "Specified parent ID is reserved for ingress and clsact Qdiscs"); + err = -EINVAL; + goto err_out3; + } handle = TC_H_MAKE(TC_H_INGRESS, 0); } else { if (handle == 0) { diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 35963929e117..e43a45499372 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -140,7 +140,7 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = { .cl_ops = &ingress_class_ops, .id = "ingress", .priv_size = sizeof(struct ingress_sched_data), - .static_flags = TCQ_F_CPUSTATS, + .static_flags = TCQ_F_INGRESS | TCQ_F_CPUSTATS, .init = ingress_init, .destroy = ingress_destroy, .dump = ingress_dump, @@ -281,7 +281,7 @@ static struct Qdisc_ops clsact_qdisc_ops __read_mostly = { .cl_ops = &clsact_class_ops, .id = "clsact", .priv_size = sizeof(struct clsact_sched_data), - .static_flags = TCQ_F_CPUSTATS, + .static_flags = TCQ_F_INGRESS | TCQ_F_CPUSTATS, .init = clsact_init, .destroy = clsact_destroy, .dump = ingress_dump, -- cgit 1.4.1 From f8884108a2febe9080bbd6ac1ed785d97fc3e692 Mon Sep 17 00:00:00 2001 From: Peilin Ye Date: Mon, 29 May 2023 12:54:26 -0700 Subject: net/sched: Prohibit regrafting ingress or clsact Qdiscs [ Upstream commit 9de95df5d15baa956c2b70b9e794842e790a8a13 ] Currently, after creating an ingress (or clsact) Qdisc and grafting it under TC_H_INGRESS (TC_H_CLSACT), it is possible to graft it again under e.g. a TBF Qdisc: $ ip link add ifb0 type ifb $ tc qdisc add dev ifb0 handle 1: root tbf rate 20kbit buffer 1600 limit 3000 $ tc qdisc add dev ifb0 clsact $ tc qdisc link dev ifb0 handle ffff: parent 1:1 $ tc qdisc show dev ifb0 qdisc tbf 1: root refcnt 2 rate 20Kbit burst 1600b lat 560.0ms qdisc clsact ffff: parent ffff:fff1 refcnt 2 ^^^^^^^^ clsact's refcount has increased: it is now grafted under both TC_H_CLSACT and 1:1. ingress and clsact Qdiscs should only be used under TC_H_INGRESS (TC_H_CLSACT). Prohibit regrafting them. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 1f211a1b929c ("net, sched: add clsact qdisc") Tested-by: Pedro Tammela Acked-by: Jamal Hadi Salim Reviewed-by: Jamal Hadi Salim Reviewed-by: Vlad Buslov Signed-off-by: Peilin Ye Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_api.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2244e00ea9a1..95f38595b0f7 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1591,6 +1591,11 @@ replay: NL_SET_ERR_MSG(extack, "Invalid qdisc name"); return -EINVAL; } + if (q->flags & TCQ_F_INGRESS) { + NL_SET_ERR_MSG(extack, + "Cannot regraft ingress or clsact Qdiscs"); + return -EINVAL; + } if (q == p || (p && check_loop(q, p, 0))) { NL_SET_ERR_MSG(extack, "Qdisc parent/child loop detected"); -- cgit 1.4.1 From 5c05ef3630cace3fe2b7964674faeb040ccb768c Mon Sep 17 00:00:00 2001 From: Zhengchao Shao Date: Sat, 27 May 2023 17:37:47 +0800 Subject: net: sched: fix NULL pointer dereference in mq_attach [ Upstream commit 36eec020fab668719b541f34d97f44e232ffa165 ] When use the following command to test: 1)ip link add bond0 type bond 2)ip link set bond0 up 3)tc qdisc add dev bond0 root handle ffff: mq 4)tc qdisc replace dev bond0 parent ffff:fff1 handle ffff: mq The kernel reports NULL pointer dereference issue. The stack information is as follows: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 Internal error: Oops: 0000000096000006 [#1] SMP Modules linked in: pstate: 20000005 (nzCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : mq_attach+0x44/0xa0 lr : qdisc_graft+0x20c/0x5cc sp : ffff80000e2236a0 x29: ffff80000e2236a0 x28: ffff0000c0e59d80 x27: ffff0000c0be19c0 x26: ffff0000cae3e800 x25: 0000000000000010 x24: 00000000fffffff1 x23: 0000000000000000 x22: ffff0000cae3e800 x21: ffff0000c9df4000 x20: ffff0000c9df4000 x19: 0000000000000000 x18: ffff80000a934000 x17: ffff8000f5b56000 x16: ffff80000bb08000 x15: 0000000000000000 x14: 0000000000000000 x13: 6b6b6b6b6b6b6b6b x12: 6b6b6b6b00000001 x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 x8 : ffff0000c0be0730 x7 : bbbbbbbbbbbbbbbb x6 : 0000000000000008 x5 : ffff0000cae3e864 x4 : 0000000000000000 x3 : 0000000000000001 x2 : 0000000000000001 x1 : ffff8000090bc23c x0 : 0000000000000000 Call trace: mq_attach+0x44/0xa0 qdisc_graft+0x20c/0x5cc tc_modify_qdisc+0x1c4/0x664 rtnetlink_rcv_msg+0x354/0x440 netlink_rcv_skb+0x64/0x144 rtnetlink_rcv+0x28/0x34 netlink_unicast+0x1e8/0x2a4 netlink_sendmsg+0x308/0x4a0 sock_sendmsg+0x64/0xac ____sys_sendmsg+0x29c/0x358 ___sys_sendmsg+0x90/0xd0 __sys_sendmsg+0x7c/0xd0 __arm64_sys_sendmsg+0x2c/0x38 invoke_syscall+0x54/0x114 el0_svc_common.constprop.1+0x90/0x174 do_el0_svc+0x3c/0xb0 el0_svc+0x24/0xec el0t_64_sync_handler+0x90/0xb4 el0t_64_sync+0x174/0x178 This is because when mq is added for the first time, qdiscs in mq is set to NULL in mq_attach(). Therefore, when replacing mq after adding mq, we need to initialize qdiscs in the mq before continuing to graft. Otherwise, it will couse NULL pointer dereference issue in mq_attach(). And the same issue will occur in the attach functions of mqprio, taprio and htb. ffff:fff1 means that the repalce qdisc is ingress. Ingress does not allow any qdisc to be attached. Therefore, ffff:fff1 is incorrectly used, and the command should be dropped. Fixes: 6ec1c69a8f64 ("net_sched: add classful multiqueue dummy scheduler") Signed-off-by: Zhengchao Shao Tested-by: Peilin Ye Acked-by: Jamal Hadi Salim Link: https://lore.kernel.org/r/20230527093747.3583502-1-shaozhengchao@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_api.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 95f38595b0f7..f6a7b876d595 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1601,6 +1601,10 @@ replay: NL_SET_ERR_MSG(extack, "Qdisc parent/child loop detected"); return -ELOOP; } + if (clid == TC_H_INGRESS) { + NL_SET_ERR_MSG(extack, "Ingress cannot graft directly"); + return -EINVAL; + } qdisc_refcount_inc(q); goto graft; } else { -- cgit 1.4.1 From 507182f132a8cb568d33f0fdda0a164f78d8d376 Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Mon, 29 May 2023 12:33:35 -0300 Subject: net/netlink: fix NETLINK_LIST_MEMBERSHIPS length report [ Upstream commit f4e4534850a9d18c250a93f8d7fbb51310828110 ] The current code for the length calculation wrongly truncates the reported length of the groups array, causing an under report of the subscribed groups. To fix this, use 'BITS_TO_BYTES()' which rounds up the division by 8. Fixes: b42be38b2778 ("netlink: add API to retrieve all group memberships") Signed-off-by: Pedro Tammela Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230529153335.389815-1-pctammela@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/netlink/af_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d1b19618890b..6d493a0ccf39 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1768,7 +1768,7 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, break; } } - if (put_user(ALIGN(nlk->ngroups / 8, sizeof(u32)), optlen)) + if (put_user(ALIGN(BITS_TO_BYTES(nlk->ngroups), sizeof(u32)), optlen)) err = -EFAULT; netlink_unlock_table(); return err; -- cgit 1.4.1 From 401a1cf50bfaf8592535924a2e5ef163d2c2d6be Mon Sep 17 00:00:00 2001 From: Vladislav Efanov Date: Tue, 30 May 2023 14:39:41 +0300 Subject: udp6: Fix race condition in udp6_sendmsg & connect [ Upstream commit 448a5ce1120c5bdbce1f1ccdabcd31c7d029f328 ] Syzkaller got the following report: BUG: KASAN: use-after-free in sk_setup_caps+0x621/0x690 net/core/sock.c:2018 Read of size 8 at addr ffff888027f82780 by task syz-executor276/3255 The function sk_setup_caps (called by ip6_sk_dst_store_flow-> ip6_dst_store) referenced already freed memory as this memory was freed by parallel task in udpv6_sendmsg->ip6_sk_dst_lookup_flow-> sk_dst_check. task1 (connect) task2 (udp6_sendmsg) sk_setup_caps->sk_dst_set | | sk_dst_check-> | sk_dst_set | dst_release sk_setup_caps references | to already freed dst_entry| The reason for this race condition is: sk_setup_caps() keeps using the dst after transferring the ownership to the dst cache. Found by Linux Verification Center (linuxtesting.org) with syzkaller. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Vladislav Efanov Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/core/sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/sock.c b/net/core/sock.c index eb0b76acd9df..83f590d8d085 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2370,7 +2370,6 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) { u32 max_segs = 1; - sk_dst_set(sk, dst); sk->sk_route_caps = dst->dev->features; if (sk_is_tcp(sk)) sk->sk_route_caps |= NETIF_F_GSO; @@ -2392,6 +2391,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) } } sk->sk_gso_max_segs = max_segs; + sk_dst_set(sk, dst); } EXPORT_SYMBOL_GPL(sk_setup_caps); -- cgit 1.4.1 From 4e5a5cda3dfd12ab7e0b77f0bd11834e09674227 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 29 May 2023 14:35:55 +0300 Subject: nfsd: fix double fget() bug in __write_ports_addfd() [ Upstream commit c034203b6a9dae6751ef4371c18cb77983e30c28 ] The bug here is that you cannot rely on getting the same socket from multiple calls to fget() because userspace can influence that. This is a kind of double fetch bug. The fix is to delete the svc_alien_sock() function and instead do the checking inside the svc_addsock() function. Fixes: 3064639423c4 ("nfsd: check passed socket's net matches NFSd superblock's one") Signed-off-by: Dan Carpenter Reviewed-by: NeilBrown Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfsctl.c | 7 +------ include/linux/sunrpc/svcsock.h | 7 +++---- net/sunrpc/svcsock.c | 24 ++++++------------------ 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index dc74a947a440..573de0d49e17 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -710,16 +710,11 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred if (err != 0 || fd < 0) return -EINVAL; - if (svc_alien_sock(net, fd)) { - printk(KERN_ERR "%s: socket net is different to NFSd's one\n", __func__); - return -EINVAL; - } - err = nfsd_create_serv(net); if (err != 0) return err; - err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); + err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); if (err >= 0 && !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index bcc555c7ae9c..13aff355d5a1 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -59,10 +59,9 @@ int svc_recv(struct svc_rqst *, long); int svc_send(struct svc_rqst *); void svc_drop(struct svc_rqst *); void svc_sock_update_bufs(struct svc_serv *serv); -bool svc_alien_sock(struct net *net, int fd); -int svc_addsock(struct svc_serv *serv, const int fd, - char *name_return, const size_t len, - const struct cred *cred); +int svc_addsock(struct svc_serv *serv, struct net *net, + const int fd, char *name_return, const size_t len, + const struct cred *cred); void svc_init_xprt_sock(void); void svc_cleanup_xprt_sock(void); struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 7107fbcbff34..d808c00cdbac 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1338,25 +1338,10 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, return svsk; } -bool svc_alien_sock(struct net *net, int fd) -{ - int err; - struct socket *sock = sockfd_lookup(fd, &err); - bool ret = false; - - if (!sock) - goto out; - if (sock_net(sock->sk) != net) - ret = true; - sockfd_put(sock); -out: - return ret; -} -EXPORT_SYMBOL_GPL(svc_alien_sock); - /** * svc_addsock - add a listener socket to an RPC service * @serv: pointer to RPC service to which to add a new listener + * @net: caller's network namespace * @fd: file descriptor of the new listener * @name_return: pointer to buffer to fill in with name of listener * @len: size of the buffer @@ -1366,8 +1351,8 @@ EXPORT_SYMBOL_GPL(svc_alien_sock); * Name is terminated with '\n'. On error, returns a negative errno * value. */ -int svc_addsock(struct svc_serv *serv, const int fd, char *name_return, - const size_t len, const struct cred *cred) +int svc_addsock(struct svc_serv *serv, struct net *net, const int fd, + char *name_return, const size_t len, const struct cred *cred) { int err = 0; struct socket *so = sockfd_lookup(fd, &err); @@ -1378,6 +1363,9 @@ int svc_addsock(struct svc_serv *serv, const int fd, char *name_return, if (!so) return err; + err = -EINVAL; + if (sock_net(so->sk) != net) + goto out; err = -EAFNOSUPPORT; if ((so->sk->sk_family != PF_INET) && (so->sk->sk_family != PF_INET6)) goto out; -- cgit 1.4.1 From 7c3e271626d17e4a6dcdea084127023a3307f91f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:54:54 +0200 Subject: nvme: fix the name of Zone Append for verbose logging [ Upstream commit 856303797724d28f1d65b702f0eadcee1ea7abf5 ] No Management involved in Zone Appened. Fixes: bd83fe6f2cd2 ("nvme: add verbose error logging") Signed-off-by: Christoph Hellwig Reviewed-by: Alan Adamson Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/constants.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/constants.c b/drivers/nvme/host/constants.c index e958d5015585..5766ceba2fec 100644 --- a/drivers/nvme/host/constants.c +++ b/drivers/nvme/host/constants.c @@ -21,7 +21,7 @@ static const char * const nvme_ops[] = { [nvme_cmd_resv_release] = "Reservation Release", [nvme_cmd_zone_mgmt_send] = "Zone Management Send", [nvme_cmd_zone_mgmt_recv] = "Zone Management Receive", - [nvme_cmd_zone_append] = "Zone Management Append", + [nvme_cmd_zone_append] = "Zone Append", }; static const char * const nvme_admin_ops[] = { -- cgit 1.4.1 From 4156c6ff331afe198ee7298d77f58f6b175df1d7 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 27 May 2023 23:07:08 -0700 Subject: net/mlx5e: Fix error handling in mlx5e_refresh_tirs [ Upstream commit b6193d7030e3c59f1d4c75648c9c8fa40cad2bcd ] Allocation failure is outside the critical lock section and should return immediately rather than jumping to the unlock section. Also unlock as soon as required and remove the now redundant jump label. Fixes: 80a2a9026b24 ("net/mlx5e: Add a lock on tir list") Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en_common.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c index 68f19324db93..03a99918a894 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c @@ -139,10 +139,8 @@ int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb, inlen = MLX5_ST_SZ_BYTES(modify_tir_in); in = kvzalloc(inlen, GFP_KERNEL); - if (!in) { - err = -ENOMEM; - goto out; - } + if (!in) + return -ENOMEM; if (enable_uc_lb) lb_flags = MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST; @@ -160,14 +158,13 @@ int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb, tirn = tir->tirn; err = mlx5_core_modify_tir(mdev, tirn, in); if (err) - goto out; + break; } + mutex_unlock(&mdev->mlx5e_res.hw_objs.td.list_lock); -out: kvfree(in); if (err) netdev_err(priv->netdev, "refresh tir(0x%x) failed, %d\n", tirn, err); - mutex_unlock(&mdev->mlx5e_res.hw_objs.td.list_lock); return err; } -- cgit 1.4.1 From 9316fdd57f253acfcef1a8e4940d08d647c8cdfa Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Fri, 28 Apr 2023 13:48:13 +0300 Subject: net/mlx5: Read embedded cpu after init bit cleared [ Upstream commit bbfa4b58997e3d38ba629c9f6fc0bd1c163aaf43 ] During driver load it reads embedded_cpu bit from initialization segment, but the initialization segment is readable only after initialization bit is cleared. Move the call to mlx5_read_embedded_cpu() right after initialization bit cleared. Signed-off-by: Moshe Shemesh Fixes: 591905ba9679 ("net/mlx5: Introduce Mellanox SmartNIC and modify page management logic") Reviewed-by: Shay Drory Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 1a06493da412..077204929fe4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -896,7 +896,6 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct pci_dev *pdev, } mlx5_pci_vsc_init(dev); - dev->caps.embedded_cpu = mlx5_read_embedded_cpu(dev); return 0; err_clr_master: @@ -1130,6 +1129,7 @@ static int mlx5_function_setup(struct mlx5_core_dev *dev, bool boot, u64 timeout goto err_cmd_cleanup; } + dev->caps.embedded_cpu = mlx5_read_embedded_cpu(dev); mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_UP); mlx5_start_health_poll(dev); -- cgit 1.4.1 From 4fc2724f445f46822f7cb8de9b1c362c2f49ec0d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 26 May 2023 16:53:59 +0800 Subject: iommu/mediatek: Flush IOTLB completely only if domain has been attached [ Upstream commit b3fc95709c54ffbe80f16801e0a792a4d2b3d55e ] If an IOMMU domain was never attached, it lacks any linkage to the actual IOMMU hardware. Attempting to do flush_iotlb_all() on it will result in a NULL pointer dereference. This seems to happen after the recent IOMMU core rework in v6.4-rc1. Unable to handle kernel read from unreadable memory at virtual address 0000000000000018 Call trace: mtk_iommu_flush_iotlb_all+0x20/0x80 iommu_create_device_direct_mappings.part.0+0x13c/0x230 iommu_setup_default_domain+0x29c/0x4d0 iommu_probe_device+0x12c/0x190 of_iommu_configure+0x140/0x208 of_dma_configure_id+0x19c/0x3c0 platform_dma_configure+0x38/0x88 really_probe+0x78/0x2c0 Check if the "bank" field has been filled in before actually attempting the IOTLB flush to avoid it. The IOTLB is also flushed when the device comes out of runtime suspend, so it should have a clean initial state. Fixes: 08500c43d4f7 ("iommu/mediatek: Adjust the structure") Signed-off-by: Chen-Yu Tsai Reviewed-by: Yong Wu Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230526085402.394239-1-wenst@chromium.org Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/mtk_iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index e93ca9dc37c8..2ae5a6058a34 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -737,7 +737,8 @@ static void mtk_iommu_flush_iotlb_all(struct iommu_domain *domain) { struct mtk_iommu_domain *dom = to_mtk_domain(domain); - mtk_iommu_tlb_flush_all(dom->bank->parent_data); + if (dom->bank) + mtk_iommu_tlb_flush_all(dom->bank->parent_data); } static void mtk_iommu_iotlb_sync(struct iommu_domain *domain, -- cgit 1.4.1 From eac615ed3c6d91f1196f16f0a0599fff479cb220 Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Wed, 31 May 2023 18:28:04 +0800 Subject: net/sched: flower: fix possible OOB write in fl_set_geneve_opt() [ Upstream commit 4d56304e5827c8cc8cc18c75343d283af7c4825c ] If we send two TCA_FLOWER_KEY_ENC_OPTS_GENEVE packets and their total size is 252 bytes(key->enc_opts.len = 252) then key->enc_opts.len = opt->length = data_len / 4 = 0 when the third TCA_FLOWER_KEY_ENC_OPTS_GENEVE packet enters fl_set_geneve_opt. This bypasses the next bounds check and results in an out-of-bounds. Fixes: 0a6e77784f49 ("net/sched: allow flower to match tunnel options") Signed-off-by: Hangyu Hua Reviewed-by: Simon Horman Reviewed-by: Pieter Jansen van Vuuren Link: https://lore.kernel.org/r/20230531102805.27090-1-hbh25y@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/cls_flower.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 25bc57ee6ea1..3de72e7c1075 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -1147,6 +1147,9 @@ static int fl_set_geneve_opt(const struct nlattr *nla, struct fl_flow_key *key, if (option_len > sizeof(struct geneve_opt)) data_len = option_len - sizeof(struct geneve_opt); + if (key->enc_opts.len > FLOW_DIS_TUN_OPTS_MAX - 4) + return -ERANGE; + opt = (struct geneve_opt *)&key->enc_opts.data[key->enc_opts.len]; memset(opt, 0xff, option_len); opt->length = data_len / 4; -- cgit 1.4.1 From c3fc733798c7bb6befd57c67b18488c4a55ac1f2 Mon Sep 17 00:00:00 2001 From: fuyuanli Date: Wed, 31 May 2023 16:01:50 +0800 Subject: tcp: fix mishandling when the sack compression is deferred. [ Upstream commit 30c6f0bf9579debce27e45fac34fdc97e46acacc ] In this patch, we mainly try to handle sending a compressed ack correctly if it's deferred. Here are more details in the old logic: When sack compression is triggered in the tcp_compressed_ack_kick(), if the sock is owned by user, it will set TCP_DELACK_TIMER_DEFERRED and then defer to the release cb phrase. Later once user releases the sock, tcp_delack_timer_handler() should send a ack as expected, which, however, cannot happen due to lack of ICSK_ACK_TIMER flag. Therefore, the receiver would not sent an ack until the sender's retransmission timeout. It definitely increases unnecessary latency. Fixes: 5d9f4262b7ea ("tcp: add SACK compression") Suggested-by: Eric Dumazet Signed-off-by: fuyuanli Signed-off-by: Jason Xing Link: https://lore.kernel.org/netdev/20230529113804.GA20300@didi-ThinkCentre-M920t-N000/ Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20230531080150.GA20424@didi-ThinkCentre-M920t-N000 Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/tcp.h | 1 + net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_timer.c | 16 +++++++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 0744717f5caa..5eedd476a38d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -632,6 +632,7 @@ void tcp_reset(struct sock *sk, struct sk_buff *skb); void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, struct sk_buff *skb); void tcp_fin(struct sock *sk); void tcp_check_space(struct sock *sk); +void tcp_sack_compress_send_ack(struct sock *sk); /* tcp_timer.c */ void tcp_init_xmit_timers(struct sock *); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ac44edd6f52e..5dabb38b857f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4529,7 +4529,7 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp) } } -static void tcp_sack_compress_send_ack(struct sock *sk) +void tcp_sack_compress_send_ack(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index cb79127f45c3..0b5d0a2867a8 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -290,9 +290,19 @@ static int tcp_write_timeout(struct sock *sk) void tcp_delack_timer_handler(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); - if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) || - !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) + if ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) + return; + + /* Handling the sack compression case */ + if (tp->compressed_ack) { + tcp_mstamp_refresh(tp); + tcp_sack_compress_send_ack(sk); + return; + } + + if (!(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) return; if (time_after(icsk->icsk_ack.timeout, jiffies)) { @@ -312,7 +322,7 @@ void tcp_delack_timer_handler(struct sock *sk) inet_csk_exit_pingpong_mode(sk); icsk->icsk_ack.ato = TCP_ATO_MIN; } - tcp_mstamp_refresh(tcp_sk(sk)); + tcp_mstamp_refresh(tp); tcp_send_ack(sk); __NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKS); } -- cgit 1.4.1 From a0843347a96adb9589ded6af33c18583bf97fc39 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Tue, 30 May 2023 16:52:23 +0200 Subject: net: dsa: mv88e6xxx: Increase wait after reset deactivation [ Upstream commit 3c27f3d53d588618d81d30d6712459a3cc9489b8 ] A switch held in reset by default needs to wait longer until we can reliably detect it. An issue was observed when testing on the Marvell 88E6393X (Link Street). The driver failed to detect the switch on some upstarts. Increasing the wait time after reset deactivation solves this issue. The updated wait time is now also the same as the wait time in the mv88e6xxx_hardware_reset function. Fixes: 7b75e49de424 ("net: dsa: mv88e6xxx: wait after reset deactivation") Signed-off-by: Andreas Svensson Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20230530145223.1223993-1-andreas.svensson@axis.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index a8ff6ee6d58e..4db1652015d1 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -7067,7 +7067,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) goto out; } if (chip->reset) - usleep_range(1000, 2000); + usleep_range(10000, 20000); /* Detect if the device is configured in single chip addressing mode, * otherwise continue with address specific smi init/detection. -- cgit 1.4.1 From 2187cb72b98bfa8fa21a0e4bfcc7084fc916b414 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 25 May 2023 12:31:52 +1200 Subject: mtd: rawnand: marvell: ensure timing values are written [ Upstream commit 8a6f4d346f3bad9c68b4a87701eb3f7978542d57 ] When new timing values are calculated in marvell_nfc_setup_interface() ensure that they will be applied in marvell_nfc_select_target() by clearing the selected_chip pointer. Fixes: b25251414f6e ("mtd: rawnand: marvell: Stop implementing ->select_chip()") Suggested-by: Miquel Raynal Signed-off-by: Chris Packham Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230525003154.2303012-1-chris.packham@alliedtelesis.co.nz Signed-off-by: Sasha Levin --- drivers/mtd/nand/raw/marvell_nand.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index b9d1e96e3334..72069a864356 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2449,6 +2449,12 @@ static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr, NDTR1_WAIT_MODE; } + /* + * Reset nfc->selected_chip so the next command will cause the timing + * registers to be updated in marvell_nfc_select_target(). + */ + nfc->selected_chip = NULL; + return 0; } -- cgit 1.4.1 From 62dcac528be74d62024d38e04524d6c824d61896 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 25 May 2023 12:31:53 +1200 Subject: mtd: rawnand: marvell: don't set the NAND frequency select [ Upstream commit c4d28e30a8d0b979e4029465ab8f312ab6ce2644 ] marvell_nfc_setup_interface() uses the frequency retrieved from the clock associated with the nand interface to determine the timings that will be used. By changing the NAND frequency select without reflecting this in the clock configuration this means that the timings calculated don't correctly meet the requirements of the NAND chip. This hasn't been an issue up to now because of a different bug that was stopping the timings being updated after they were initially set. Fixes: b25251414f6e ("mtd: rawnand: marvell: Stop implementing ->select_chip()") Signed-off-by: Chris Packham Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230525003154.2303012-2-chris.packham@alliedtelesis.co.nz Signed-off-by: Sasha Levin --- drivers/mtd/nand/raw/marvell_nand.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 72069a864356..a57a1501449a 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2891,10 +2891,6 @@ static int marvell_nfc_init(struct marvell_nfc *nfc) regmap_update_bits(sysctrl_base, GENCONF_CLK_GATING_CTRL, GENCONF_CLK_GATING_CTRL_ND_GATE, GENCONF_CLK_GATING_CTRL_ND_GATE); - - regmap_update_bits(sysctrl_base, GENCONF_ND_CLK_CTRL, - GENCONF_ND_CLK_CTRL_EN, - GENCONF_ND_CLK_CTRL_EN); } /* Configure the DMA if appropriate */ -- cgit 1.4.1 From ea9d7382d5d3cbb733b71cad829a4299bc348b2d Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 31 May 2023 12:01:42 -0400 Subject: rtnetlink: call validate_linkmsg in rtnl_create_link commit b0ad3c179059089d809b477a1d445c1183a7b8fe upstream. validate_linkmsg() was introduced by commit 1840bb13c22f5b ("[RTNL]: Validate hardware and broadcast address attribute for RTM_NEWLINK") to validate tb[IFLA_ADDRESS/BROADCAST] for existing links. The same check should also be done for newly created links. This patch adds validate_linkmsg() call in rtnl_create_link(), to avoid the invalid address set when creating some devices like: # ip link add dummy0 type dummy # ip link add link dummy0 name mac0 address 01:02 type macsec Fixes: 0e06877c6fdb ("[RTNETLINK]: rtnl_link: allow specifying initial device address") Signed-off-by: Xin Long Reviewed-by: Simon Horman Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/rtnetlink.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 74864dc46a7e..b192c69f3936 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3212,6 +3212,7 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname, struct net_device *dev; unsigned int num_tx_queues = 1; unsigned int num_rx_queues = 1; + int err; if (tb[IFLA_NUM_TX_QUEUES]) num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]); @@ -3247,13 +3248,18 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname, if (!dev) return ERR_PTR(-ENOMEM); + err = validate_linkmsg(dev, tb, extack); + if (err < 0) { + free_netdev(dev); + return ERR_PTR(err); + } + dev_net_set(dev, net); dev->rtnl_link_ops = ops; dev->rtnl_link_state = RTNL_LINK_INITIALIZING; if (tb[IFLA_MTU]) { u32 mtu = nla_get_u32(tb[IFLA_MTU]); - int err; err = dev_validate_mtu(dev, mtu, extack); if (err) { -- cgit 1.4.1 From c5ebb5cec9fb9528c72ece3140a377bd22e19ccf Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 14 Apr 2023 16:08:01 +0200 Subject: mptcp: avoid unneeded __mptcp_nmpc_socket() usage [ Upstream commit 617612316953093bc859890e405e1b550c27d840 ] In a few spots, the mptcp code invokes the __mptcp_nmpc_socket() helper multiple times under the same socket lock scope. Additionally, in such places, the socket status ensures that there is no MP capable handshake running. Under the above condition we can replace the later __mptcp_nmpc_socket() helper invocation with direct access to the msk->subflow pointer and better document such access is not supposed to fail with WARN(). Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: David S. Miller Stable-dep-of: 5b825727d087 ("mptcp: add annotations around msk->subflow accesses") Signed-off-by: Sasha Levin --- net/mptcp/protocol.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index ea46a5cb1c30..fecee0a850d6 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3172,7 +3172,7 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err, struct socket *listener; struct sock *newsk; - listener = __mptcp_nmpc_socket(msk); + listener = msk->subflow; if (WARN_ON_ONCE(!listener)) { *err = -EINVAL; return NULL; @@ -3398,7 +3398,7 @@ static int mptcp_get_port(struct sock *sk, unsigned short snum) struct mptcp_sock *msk = mptcp_sk(sk); struct socket *ssock; - ssock = __mptcp_nmpc_socket(msk); + ssock = msk->subflow; pr_debug("msk=%p, subflow=%p", msk, ssock); if (WARN_ON_ONCE(!ssock)) return -EINVAL; @@ -3746,7 +3746,10 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock, pr_debug("msk=%p", msk); - ssock = __mptcp_nmpc_socket(msk); + /* buggy applications can call accept on socket states other then LISTEN + * but no need to allocate the first subflow just to error out. + */ + ssock = msk->subflow; if (!ssock) return -EINVAL; -- cgit 1.4.1 From 1b9e3ab669a376d226dbd31ef7be7da81a710754 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 31 May 2023 12:37:04 -0700 Subject: mptcp: add annotations around msk->subflow accesses [ Upstream commit 5b825727d0871b23e8867f6371183e61628b4a26 ] The MPTCP can access the first subflow socket in a few spots outside the socket lock scope. That is actually safe, as MPTCP will delete the socket itself only after the msk sock close(). Still the such accesses causes a few KCSAN splats, as reported by Christoph. Silence the harmless warning adding a few annotation around the relevant accesses. Fixes: 71ba088ce0aa ("mptcp: cleanup accept and poll") Reported-by: Christoph Paasch Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/402 Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/mptcp/protocol.c | 18 ++++++++++-------- net/mptcp/protocol.h | 6 +++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index fecee0a850d6..efe372ff389d 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -112,7 +112,7 @@ static int __mptcp_socket_create(struct mptcp_sock *msk) return err; msk->first = ssock->sk; - msk->subflow = ssock; + WRITE_ONCE(msk->subflow, ssock); subflow = mptcp_subflow_ctx(ssock->sk); list_add(&subflow->node, &msk->conn_list); sock_hold(ssock->sk); @@ -2269,7 +2269,7 @@ static void mptcp_dispose_initial_subflow(struct mptcp_sock *msk) { if (msk->subflow) { iput(SOCK_INODE(msk->subflow)); - msk->subflow = NULL; + WRITE_ONCE(msk->subflow, NULL); } } @@ -3115,7 +3115,7 @@ struct sock *mptcp_sk_clone(const struct sock *sk, msk = mptcp_sk(nsk); msk->local_key = subflow_req->local_key; msk->token = subflow_req->token; - msk->subflow = NULL; + WRITE_ONCE(msk->subflow, NULL); msk->in_accept_queue = 1; WRITE_ONCE(msk->fully_established, false); if (mp_opt->suboptions & OPTION_MPTCP_CSUMREQD) @@ -3172,7 +3172,7 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err, struct socket *listener; struct sock *newsk; - listener = msk->subflow; + listener = READ_ONCE(msk->subflow); if (WARN_ON_ONCE(!listener)) { *err = -EINVAL; return NULL; @@ -3746,10 +3746,10 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock, pr_debug("msk=%p", msk); - /* buggy applications can call accept on socket states other then LISTEN + /* Buggy applications can call accept on socket states other then LISTEN * but no need to allocate the first subflow just to error out. */ - ssock = msk->subflow; + ssock = READ_ONCE(msk->subflow); if (!ssock) return -EINVAL; @@ -3822,10 +3822,12 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock, state = inet_sk_state_load(sk); pr_debug("msk=%p state=%d flags=%lx", msk, state, msk->flags); if (state == TCP_LISTEN) { - if (WARN_ON_ONCE(!msk->subflow || !msk->subflow->sk)) + struct socket *ssock = READ_ONCE(msk->subflow); + + if (WARN_ON_ONCE(!ssock || !ssock->sk)) return 0; - return inet_csk_listen_poll(msk->subflow->sk); + return inet_csk_listen_poll(ssock->sk); } if (state != TCP_SYN_SENT && state != TCP_SYN_RECV) { diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 441feeaeb242..822fd749f569 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -297,7 +297,11 @@ struct mptcp_sock { struct list_head rtx_queue; struct mptcp_data_frag *first_pending; struct list_head join_list; - struct socket *subflow; /* outgoing connect/listener/!mp_capable */ + struct socket *subflow; /* outgoing connect/listener/!mp_capable + * The mptcp ops can safely dereference, using suitable + * ONCE annotation, the subflow outside the socket + * lock as such sock is freed after close(). + */ struct sock *first; struct mptcp_pm_data pm; struct { -- cgit 1.4.1 From 9872e8c632528538f4f0d1ec2a73c413de422b08 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 27 Mar 2023 12:22:21 +0200 Subject: mptcp: avoid unneeded address copy [ Upstream commit 2bb9a37f0e194ed95c70603b0efc7898a5a0d9b4 ] In the syn_recv fallback path, the msk is unused. We can skip setting the socket address. Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: David S. Miller Stable-dep-of: 7e8b88ec35ee ("mptcp: consolidate passive msk socket initialization") Signed-off-by: Sasha Levin --- net/mptcp/subflow.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 67ddbf6f2e4e..4995a6281ea1 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -759,8 +759,6 @@ create_child: goto dispose_child; } - if (new_msk) - mptcp_copy_inaddrs(new_msk, child); mptcp_subflow_drop_ctx(child); goto out; } -- cgit 1.4.1 From fa2cbd1d689a1ab728b7e0ceb0934fe41a5123cf Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 27 Mar 2023 12:22:22 +0200 Subject: mptcp: simplify subflow_syn_recv_sock() [ Upstream commit a88d0092b24b8cddce57fe0e88e60a9e29e0b515 ] Postpone the msk cloning to the child process creation so that we can avoid a bunch of conditionals. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/61 Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: David S. Miller Stable-dep-of: 7e8b88ec35ee ("mptcp: consolidate passive msk socket initialization") Signed-off-by: Sasha Levin --- net/mptcp/subflow.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 4995a6281ea1..276e62003631 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -633,14 +633,6 @@ static bool subflow_hmac_valid(const struct request_sock *req, return !crypto_memneq(hmac, mp_opt->hmac, MPTCPOPT_HMAC_LEN); } -static void mptcp_force_close(struct sock *sk) -{ - /* the msk is not yet exposed to user-space, and refcount is 2 */ - inet_sk_state_store(sk, TCP_CLOSE); - sk_common_release(sk); - sock_put(sk); -} - static void subflow_ulp_fallback(struct sock *sk, struct mptcp_subflow_context *old_ctx) { @@ -693,7 +685,6 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, struct mptcp_subflow_request_sock *subflow_req; struct mptcp_options_received mp_opt; bool fallback, fallback_is_fatal; - struct sock *new_msk = NULL; struct mptcp_sock *owner; struct sock *child; @@ -722,14 +713,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, * options. */ mptcp_get_options(skb, &mp_opt); - if (!(mp_opt.suboptions & OPTIONS_MPTCP_MPC)) { + if (!(mp_opt.suboptions & OPTIONS_MPTCP_MPC)) fallback = true; - goto create_child; - } - new_msk = mptcp_sk_clone(listener->conn, &mp_opt, req); - if (!new_msk) - fallback = true; } else if (subflow_req->mp_join) { mptcp_get_options(skb, &mp_opt); if (!(mp_opt.suboptions & OPTIONS_MPTCP_MPJ) || @@ -758,21 +744,23 @@ create_child: subflow_add_reset_reason(skb, MPTCP_RST_EMPTCP); goto dispose_child; } - - mptcp_subflow_drop_ctx(child); - goto out; + goto fallback; } /* ssk inherits options of listener sk */ ctx->setsockopt_seq = listener->setsockopt_seq; if (ctx->mp_capable) { - owner = mptcp_sk(new_msk); + ctx->conn = mptcp_sk_clone(listener->conn, &mp_opt, req); + if (!ctx->conn) + goto fallback; + + owner = mptcp_sk(ctx->conn); /* this can't race with mptcp_close(), as the msk is * not yet exposted to user-space */ - inet_sk_state_store((void *)new_msk, TCP_ESTABLISHED); + inet_sk_state_store(ctx->conn, TCP_ESTABLISHED); /* record the newly created socket as the first msk * subflow, but don't link it yet into conn_list @@ -782,11 +770,9 @@ create_child: /* new mpc subflow takes ownership of the newly * created mptcp socket */ - mptcp_sk(new_msk)->setsockopt_seq = ctx->setsockopt_seq; + owner->setsockopt_seq = ctx->setsockopt_seq; mptcp_pm_new_connection(owner, child, 1); mptcp_token_accept(subflow_req, owner); - ctx->conn = new_msk; - new_msk = NULL; /* set msk addresses early to ensure mptcp_pm_get_local_id() * uses the correct data @@ -836,11 +822,6 @@ create_child: } } -out: - /* dispose of the left over mptcp master, if any */ - if (unlikely(new_msk)) - mptcp_force_close(new_msk); - /* check for expected invariant - should never trigger, just help * catching eariler subtle bugs */ @@ -858,6 +839,10 @@ dispose_child: /* The last child reference will be released by the caller */ return child; + +fallback: + mptcp_subflow_drop_ctx(child); + return child; } static struct inet_connection_sock_af_ops subflow_specific __ro_after_init; -- cgit 1.4.1 From 519f16d96c2d49fde06aa2817898cc8d01fe4730 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 31 May 2023 12:37:05 -0700 Subject: mptcp: consolidate passive msk socket initialization [ Upstream commit 7e8b88ec35eef363040e08d99536d2bebef83774 ] When the msk socket is cloned at MPC handshake time, a few fields are initialized in a racy way outside mptcp_sk_clone() and the msk socket lock. The above is due historical reasons: before commit a88d0092b24b ("mptcp: simplify subflow_syn_recv_sock()") as the first subflow socket carrying all the needed date was not available yet at msk creation time We can now refactor the code moving the missing initialization bit under the socket lock, removing the init race and avoiding some code duplication. This will also simplify the next patch, as all msk->first write access are now under the msk socket lock. Fixes: 0397c6d85f9c ("mptcp: keep unaccepted MPC subflow into join list") Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/mptcp/protocol.c | 35 ++++++++++++++++++++++++++++------- net/mptcp/protocol.h | 8 ++++---- net/mptcp/subflow.c | 28 +--------------------------- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index efe372ff389d..6f6b65d3eed1 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3017,7 +3017,7 @@ static void mptcp_close(struct sock *sk, long timeout) sock_put(sk); } -void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk) +static void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk) { #if IS_ENABLED(CONFIG_MPTCP_IPV6) const struct ipv6_pinfo *ssk6 = inet6_sk(ssk); @@ -3093,9 +3093,10 @@ static struct ipv6_pinfo *mptcp_inet6_sk(const struct sock *sk) } #endif -struct sock *mptcp_sk_clone(const struct sock *sk, - const struct mptcp_options_received *mp_opt, - struct request_sock *req) +struct sock *mptcp_sk_clone_init(const struct sock *sk, + const struct mptcp_options_received *mp_opt, + struct sock *ssk, + struct request_sock *req) { struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req); struct sock *nsk = sk_clone_lock(sk, GFP_ATOMIC); @@ -3137,10 +3138,30 @@ struct sock *mptcp_sk_clone(const struct sock *sk, } sock_reset_flag(nsk, SOCK_RCU_FREE); - /* will be fully established after successful MPC subflow creation */ - inet_sk_state_store(nsk, TCP_SYN_RECV); - security_inet_csk_clone(nsk, req); + + /* this can't race with mptcp_close(), as the msk is + * not yet exposted to user-space + */ + inet_sk_state_store(nsk, TCP_ESTABLISHED); + + /* The msk maintain a ref to each subflow in the connections list */ + WRITE_ONCE(msk->first, ssk); + list_add(&mptcp_subflow_ctx(ssk)->node, &msk->conn_list); + sock_hold(ssk); + + /* new mpc subflow takes ownership of the newly + * created mptcp socket + */ + mptcp_token_accept(subflow_req, msk); + + /* set msk addresses early to ensure mptcp_pm_get_local_id() + * uses the correct data + */ + mptcp_copy_inaddrs(nsk, ssk); + mptcp_propagate_sndbuf(nsk, ssk); + + mptcp_rcv_space_init(msk, ssk); bh_unlock_sock(nsk); /* note: the newly allocated socket refcount is 2 now */ diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 822fd749f569..4a2f6e29211a 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -606,7 +606,6 @@ int mptcp_is_checksum_enabled(const struct net *net); int mptcp_allow_join_id0(const struct net *net); unsigned int mptcp_stale_loss_cnt(const struct net *net); int mptcp_get_pm_type(const struct net *net); -void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk); void mptcp_subflow_fully_established(struct mptcp_subflow_context *subflow, struct mptcp_options_received *mp_opt); bool __mptcp_retransmit_pending_data(struct sock *sk); @@ -675,9 +674,10 @@ void __init mptcp_proto_init(void); int __init mptcp_proto_v6_init(void); #endif -struct sock *mptcp_sk_clone(const struct sock *sk, - const struct mptcp_options_received *mp_opt, - struct request_sock *req); +struct sock *mptcp_sk_clone_init(const struct sock *sk, + const struct mptcp_options_received *mp_opt, + struct sock *ssk, + struct request_sock *req); void mptcp_get_options(const struct sk_buff *skb, struct mptcp_options_received *mp_opt); diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 276e62003631..336878f8a222 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -751,38 +751,12 @@ create_child: ctx->setsockopt_seq = listener->setsockopt_seq; if (ctx->mp_capable) { - ctx->conn = mptcp_sk_clone(listener->conn, &mp_opt, req); + ctx->conn = mptcp_sk_clone_init(listener->conn, &mp_opt, child, req); if (!ctx->conn) goto fallback; owner = mptcp_sk(ctx->conn); - - /* this can't race with mptcp_close(), as the msk is - * not yet exposted to user-space - */ - inet_sk_state_store(ctx->conn, TCP_ESTABLISHED); - - /* record the newly created socket as the first msk - * subflow, but don't link it yet into conn_list - */ - WRITE_ONCE(owner->first, child); - - /* new mpc subflow takes ownership of the newly - * created mptcp socket - */ - owner->setsockopt_seq = ctx->setsockopt_seq; mptcp_pm_new_connection(owner, child, 1); - mptcp_token_accept(subflow_req, owner); - - /* set msk addresses early to ensure mptcp_pm_get_local_id() - * uses the correct data - */ - mptcp_copy_inaddrs(ctx->conn, child); - mptcp_propagate_sndbuf(ctx->conn, child); - - mptcp_rcv_space_init(owner, child); - list_add(&ctx->node, &owner->conn_list); - sock_hold(child); /* with OoO packets we can reach here without ingress * mpc option -- cgit 1.4.1 From 0b9e6d64cd10081a3b193ceb11d44d2de877f5ff Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 31 May 2023 12:37:06 -0700 Subject: mptcp: fix data race around msk->first access [ Upstream commit 1b1b43ee7a208096ecd79e626f2fc90d4a321111 ] The first subflow socket is accessed outside the msk socket lock by mptcp_subflow_fail(), we need to annotate each write access with WRITE_ONCE, but a few spots still lacks it. Fixes: 76a13b315709 ("mptcp: invoke MP_FAIL response when needed") Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/mptcp/protocol.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 6f6b65d3eed1..f4206001e2fe 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -111,7 +111,7 @@ static int __mptcp_socket_create(struct mptcp_sock *msk) if (err) return err; - msk->first = ssock->sk; + WRITE_ONCE(msk->first, ssock->sk); WRITE_ONCE(msk->subflow, ssock); subflow = mptcp_subflow_ctx(ssock->sk); list_add(&subflow->node, &msk->conn_list); @@ -2405,7 +2405,7 @@ out_release: sock_put(ssk); if (ssk == msk->first) - msk->first = NULL; + WRITE_ONCE(msk->first, NULL); out: if (ssk == msk->last_snd) @@ -2706,7 +2706,7 @@ static int __mptcp_init_sock(struct sock *sk) WRITE_ONCE(msk->rmem_released, 0); msk->timer_ival = TCP_RTO_MIN; - msk->first = NULL; + WRITE_ONCE(msk->first, NULL); inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss; WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk))); WRITE_ONCE(msk->allow_infinite_fallback, true); -- cgit 1.4.1 From e2feb39312a6923146af96ebc8e0e5405d673aad Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 31 May 2023 12:37:07 -0700 Subject: mptcp: add annotations around sk->sk_shutdown accesses [ Upstream commit 6b9831bfd9322b297eb6d44257808cc055fdc586 ] Christoph reported the mptcp variant of a recently addressed plain TCP issue. Similar to commit e14cadfd80d7 ("tcp: add annotations around sk->sk_shutdown accesses") add READ/WRITE ONCE annotations to silence KCSAN reports around lockless sk_shutdown access. Fixes: 71ba088ce0aa ("mptcp: cleanup accept and poll") Reported-by: Christoph Paasch Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/401 Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/mptcp/protocol.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index f4206001e2fe..c25796eacd95 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -599,7 +599,7 @@ static bool mptcp_check_data_fin(struct sock *sk) WRITE_ONCE(msk->ack_seq, msk->ack_seq + 1); WRITE_ONCE(msk->rcv_data_fin, 0); - sk->sk_shutdown |= RCV_SHUTDOWN; + WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | RCV_SHUTDOWN); smp_mb__before_atomic(); /* SHUTDOWN must be visible first */ switch (sk->sk_state) { @@ -906,7 +906,7 @@ static void mptcp_check_for_eof(struct mptcp_sock *msk) /* hopefully temporary hack: propagate shutdown status * to msk, when all subflows agree on it */ - sk->sk_shutdown |= RCV_SHUTDOWN; + WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | RCV_SHUTDOWN); smp_mb__before_atomic(); /* SHUTDOWN must be visible first */ sk->sk_data_ready(sk); @@ -2512,7 +2512,7 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk) } inet_sk_state_store(sk, TCP_CLOSE); - sk->sk_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); smp_mb__before_atomic(); /* SHUTDOWN must be visible first */ set_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags); @@ -2941,7 +2941,7 @@ bool __mptcp_close(struct sock *sk, long timeout) bool do_cancel_work = false; int subflows_alive = 0; - sk->sk_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) { inet_sk_state_store(sk, TCP_CLOSE); @@ -3079,7 +3079,7 @@ static int mptcp_disconnect(struct sock *sk, int flags) mptcp_pm_data_reset(msk); mptcp_ca_reset(sk); - sk->sk_shutdown = 0; + WRITE_ONCE(sk->sk_shutdown, 0); sk_error_report(sk); return 0; } @@ -3815,9 +3815,6 @@ static __poll_t mptcp_check_writeable(struct mptcp_sock *msk) { struct sock *sk = (struct sock *)msk; - if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN)) - return EPOLLOUT | EPOLLWRNORM; - if (sk_stream_is_writeable(sk)) return EPOLLOUT | EPOLLWRNORM; @@ -3835,6 +3832,7 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock, struct sock *sk = sock->sk; struct mptcp_sock *msk; __poll_t mask = 0; + u8 shutdown; int state; msk = mptcp_sk(sk); @@ -3851,17 +3849,22 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock, return inet_csk_listen_poll(ssock->sk); } + shutdown = READ_ONCE(sk->sk_shutdown); + if (shutdown == SHUTDOWN_MASK || state == TCP_CLOSE) + mask |= EPOLLHUP; + if (shutdown & RCV_SHUTDOWN) + mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; + if (state != TCP_SYN_SENT && state != TCP_SYN_RECV) { mask |= mptcp_check_readable(msk); - mask |= mptcp_check_writeable(msk); + if (shutdown & SEND_SHUTDOWN) + mask |= EPOLLOUT | EPOLLWRNORM; + else + mask |= mptcp_check_writeable(msk); } else if (state == TCP_SYN_SENT && inet_sk(sk)->defer_connect) { /* cf tcp_poll() note about TFO */ mask |= EPOLLOUT | EPOLLWRNORM; } - if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE) - mask |= EPOLLHUP; - if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; /* This barrier is coupled with smp_wmb() in __mptcp_error_report() */ smp_rmb(); -- cgit 1.4.1 From 787e74b21394f126bbce355839dcce9b0ad256d2 Mon Sep 17 00:00:00 2001 From: Chong Li Date: Fri, 14 Apr 2023 13:51:19 +0800 Subject: drm/amdgpu: release gpu full access after "amdgpu_device_ip_late_init" [ Upstream commit 38eecbe086a4e52f54b2bbda8feba65d44addbef ] [WHY] Function "amdgpu_irq_update()" called by "amdgpu_device_ip_late_init()" is an atomic context. We shouldn't access registers through KIQ since "msleep()" may be called in "amdgpu_kiq_rreg()". [HOW] Move function "amdgpu_virt_release_full_gpu()" after function "amdgpu_device_ip_late_init()", to ensure that registers be accessed through RLCG instead of KIQ. Call Trace: show_stack+0x52/0x69 dump_stack_lvl+0x49/0x6d dump_stack+0x10/0x18 __schedule_bug.cold+0x4f/0x6b __schedule+0x473/0x5d0 ? __wake_up_klogd.part.0+0x40/0x70 ? vprintk_emit+0xbe/0x1f0 schedule+0x68/0x110 schedule_timeout+0x87/0x160 ? timer_migration_handler+0xa0/0xa0 msleep+0x2d/0x50 amdgpu_kiq_rreg+0x18d/0x1f0 [amdgpu] amdgpu_device_rreg.part.0+0x59/0xd0 [amdgpu] amdgpu_device_rreg+0x3a/0x50 [amdgpu] amdgpu_sriov_rreg+0x3c/0xb0 [amdgpu] gfx_v10_0_set_gfx_eop_interrupt_state.constprop.0+0x16c/0x190 [amdgpu] gfx_v10_0_set_eop_interrupt_state+0xa5/0xb0 [amdgpu] amdgpu_irq_update+0x53/0x80 [amdgpu] amdgpu_irq_get+0x7c/0xb0 [amdgpu] amdgpu_fence_driver_hw_init+0x58/0x90 [amdgpu] amdgpu_device_init.cold+0x16b7/0x2022 [amdgpu] Signed-off-by: Chong Li Reviewed-by: JingWen.Chen2@amd.com Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 32 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 011e4fbe27f1..c83df8b78b7d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2512,8 +2512,6 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev) amdgpu_fru_get_product_info(adev); init_failed: - if (amdgpu_sriov_vf(adev)) - amdgpu_virt_release_full_gpu(adev, true); return r; } @@ -3838,18 +3836,6 @@ fence_driver_init: r = amdgpu_device_ip_init(adev); if (r) { - /* failed in exclusive mode due to timeout */ - if (amdgpu_sriov_vf(adev) && - !amdgpu_sriov_runtime(adev) && - amdgpu_virt_mmio_blocked(adev) && - !amdgpu_virt_wait_reset(adev)) { - dev_err(adev->dev, "VF exclusive mode timeout\n"); - /* Don't send request since VF is inactive. */ - adev->virt.caps &= ~AMDGPU_SRIOV_CAPS_RUNTIME; - adev->virt.ops = NULL; - r = -EAGAIN; - goto release_ras_con; - } dev_err(adev->dev, "amdgpu_device_ip_init failed\n"); amdgpu_vf_error_put(adev, AMDGIM_ERROR_VF_AMDGPU_INIT_FAIL, 0, 0); goto release_ras_con; @@ -3921,8 +3907,10 @@ fence_driver_init: msecs_to_jiffies(AMDGPU_RESUME_MS)); } - if (amdgpu_sriov_vf(adev)) + if (amdgpu_sriov_vf(adev)) { + amdgpu_virt_release_full_gpu(adev, true); flush_delayed_work(&adev->delayed_init_work); + } r = sysfs_create_files(&adev->dev->kobj, amdgpu_dev_attributes); if (r) @@ -3962,6 +3950,20 @@ fence_driver_init: return 0; release_ras_con: + if (amdgpu_sriov_vf(adev)) + amdgpu_virt_release_full_gpu(adev, true); + + /* failed in exclusive mode due to timeout */ + if (amdgpu_sriov_vf(adev) && + !amdgpu_sriov_runtime(adev) && + amdgpu_virt_mmio_blocked(adev) && + !amdgpu_virt_wait_reset(adev)) { + dev_err(adev->dev, "VF exclusive mode timeout\n"); + /* Don't send request since VF is inactive. */ + adev->virt.caps &= ~AMDGPU_SRIOV_CAPS_RUNTIME; + adev->virt.ops = NULL; + r = -EAGAIN; + } amdgpu_release_ras_context(adev); failed: -- cgit 1.4.1 From 867dae55478093824e2ba55ab0f2f5ca4c8a29ba Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Tue, 18 Apr 2023 19:25:30 +0200 Subject: watchdog: menz069_wdt: fix watchdog initialisation [ Upstream commit 87b22656ca6a896d0378e9e60ffccb0c82f48b08 ] Doing a 'cat /dev/watchdog0' with menz069_wdt as watchdog0 will result in a NULL pointer dereference. This happens because we're passing the wrong pointer to watchdog_register_device(). Fix this by getting rid of the static watchdog_device structure and use the one embedded into the driver's per-instance private data. Signed-off-by: Johannes Thumshirn Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230418172531.177349-2-jth@kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck Signed-off-by: Sasha Levin --- drivers/watchdog/menz69_wdt.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/watchdog/menz69_wdt.c b/drivers/watchdog/menz69_wdt.c index 8973f98bc6a5..bca0938f3429 100644 --- a/drivers/watchdog/menz69_wdt.c +++ b/drivers/watchdog/menz69_wdt.c @@ -98,14 +98,6 @@ static const struct watchdog_ops men_z069_ops = { .set_timeout = men_z069_wdt_set_timeout, }; -static struct watchdog_device men_z069_wdt = { - .info = &men_z069_info, - .ops = &men_z069_ops, - .timeout = MEN_Z069_DEFAULT_TIMEOUT, - .min_timeout = 1, - .max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ, -}; - static int men_z069_probe(struct mcb_device *dev, const struct mcb_device_id *id) { @@ -125,15 +117,19 @@ static int men_z069_probe(struct mcb_device *dev, goto release_mem; drv->mem = mem; + drv->wdt.info = &men_z069_info; + drv->wdt.ops = &men_z069_ops; + drv->wdt.timeout = MEN_Z069_DEFAULT_TIMEOUT; + drv->wdt.min_timeout = 1; + drv->wdt.max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ; - drv->wdt = men_z069_wdt; watchdog_init_timeout(&drv->wdt, 0, &dev->dev); watchdog_set_nowayout(&drv->wdt, nowayout); watchdog_set_drvdata(&drv->wdt, drv); drv->wdt.parent = &dev->dev; mcb_set_drvdata(dev, drv); - return watchdog_register_device(&men_z069_wdt); + return watchdog_register_device(&drv->wdt); release_mem: mcb_release_mem(mem); -- cgit 1.4.1 From a7ec48a419fe664e500bc2f8b218ec7eeea24ba0 Mon Sep 17 00:00:00 2001 From: jasontao Date: Wed, 26 Apr 2023 09:30:59 +0800 Subject: ALSA: hda: Glenfly: add HD Audio PCI IDs and HDMI Codec Vendor IDs. [ Upstream commit c51e431052e2eacfb23fbf6b39bc6c8770d9827a ] Add a set of HD Audio PCI IDS, and the HDMI codec vendor IDs for Glenfly Gpus. - In default_bdl_pos_adj, set bdl to 128 as Glenfly Gpus have hardware limitation, need to increase hdac interrupt interval. - In azx_first_init, enable polling mode for Glenfly Gpu. When the codec complete the command, it sends interrupt and writes response entries to memory, howerver, the write requests sometimes are not actually synchronized to memory when driver handle hdac interrupt on Glenfly Gpus. If the RIRB status is not updated in the interrupt handler, azx_rirb_get_response keeps trying to recevie a response from rirb until 1s timeout. Enabling polling mode for Glenfly Gpu can fix the issue. - In patch_gf_hdmi, set Glenlfy Gpu Codec's no_sticky_stream as it need driver to do actual clean-ups for the linked codec when switch from one codec to another. Signed-off-by: jasontao Signed-off-by: Reaper Li Link: https://lore.kernel.org/r/20230426013059.4329-1-reaperlioc@glenfly.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/pci/hda/hda_intel.c | 21 +++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 881b2f3a1551..3226691ac923 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -227,6 +227,7 @@ enum { AZX_DRIVER_ATI, AZX_DRIVER_ATIHDMI, AZX_DRIVER_ATIHDMI_NS, + AZX_DRIVER_GFHDMI, AZX_DRIVER_VIA, AZX_DRIVER_SIS, AZX_DRIVER_ULI, @@ -349,6 +350,7 @@ static const char * const driver_short_names[] = { [AZX_DRIVER_ATI] = "HDA ATI SB", [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI", + [AZX_DRIVER_GFHDMI] = "HDA GF HDMI", [AZX_DRIVER_VIA] = "HDA VIA VT82xx", [AZX_DRIVER_SIS] = "HDA SIS966", [AZX_DRIVER_ULI] = "HDA ULI M5461", @@ -1743,6 +1745,12 @@ static int default_bdl_pos_adj(struct azx *chip) } switch (chip->driver_type) { + /* + * increase the bdl size for Glenfly Gpus for hardware + * limitation on hdac interrupt interval + */ + case AZX_DRIVER_GFHDMI: + return 128; case AZX_DRIVER_ICH: case AZX_DRIVER_PCH: return 1; @@ -1858,6 +1866,12 @@ static int azx_first_init(struct azx *chip) pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0); } #endif + /* + * Fix response write request not synced to memory when handle + * hdac interrupt on Glenfly Gpus + */ + if (chip->driver_type == AZX_DRIVER_GFHDMI) + bus->polling_mode = 1; err = pcim_iomap_regions(pci, 1 << 0, "ICH HD audio"); if (err < 0) @@ -1959,6 +1973,7 @@ static int azx_first_init(struct azx *chip) chip->playback_streams = ATIHDMI_NUM_PLAYBACK; chip->capture_streams = ATIHDMI_NUM_CAPTURE; break; + case AZX_DRIVER_GFHDMI: case AZX_DRIVER_GENERIC: default: chip->playback_streams = ICH6_NUM_PLAYBACK; @@ -2727,6 +2742,12 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x1002, 0xab38), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, + /* GLENFLY */ + { PCI_DEVICE(0x6766, PCI_ANY_ID), + .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, + .class_mask = 0xffffff, + .driver_data = AZX_DRIVER_GFHDMI | AZX_DCAPS_POSFIX_LPIB | + AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, /* VIA VT8251/VT8237A */ { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA }, /* VIA GFX VT7122/VX900 */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index be2c6cff7701..7b5e09070ab9 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4489,6 +4489,22 @@ static int patch_via_hdmi(struct hda_codec *codec) return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID); } +static int patch_gf_hdmi(struct hda_codec *codec) +{ + int err; + + err = patch_generic_hdmi(codec); + if (err) + return err; + + /* + * Glenfly GPUs have two codecs, stream switches from one codec to + * another, need to do actual clean-ups in codec_cleanup_stream + */ + codec->no_sticky_stream = 1; + return 0; +} + /* * patch entries */ @@ -4584,6 +4600,12 @@ HDA_CODEC_ENTRY(0x10de00a6, "GPU a6 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de00a7, "GPU a7 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), +HDA_CODEC_ENTRY(0x67663d82, "Arise 82 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x67663d83, "Arise 83 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x67663d84, "Arise 84 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x67663d85, "Arise 85 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x67663d86, "Arise 86 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x67663d87, "Arise 87 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), -- cgit 1.4.1 From 6a40da600750e604910136082e98b50ec04eea16 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 29 Apr 2023 12:47:21 +0200 Subject: ASoC: Intel: soc-acpi-cht: Add quirk for Nextbook Ares 8A tablet [ Upstream commit ec6f82b4c63cc68f8dc03316e725106d242706be ] The Nextbook Ares 8A tablet which has Android as factory OS, has a buggy DSDT with both ESSX8316 and 10EC5651 ACPI devices. This tablet actually uses an rt5651 codec, but the matching code ends up picking the ESSX8316 device, add a quirk to ignote the ESSX8316 device on this tablet. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Message-Id: <20230429104721.7176-1-hdegoede@redhat.com> Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/common/soc-acpi-intel-cht-match.c | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index 6beb00858c33..cdcbf04b8832 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -50,6 +50,31 @@ static struct snd_soc_acpi_mach *cht_quirk(void *arg) return mach; } +/* + * Some tablets with Android factory OS have buggy DSDTs with an ESSX8316 device + * in the ACPI tables. While they are not using an ESS8316 codec. These DSDTs + * also have an ACPI device for the correct codec, ignore the ESSX8316. + */ +static const struct dmi_system_id cht_ess8316_not_present_table[] = { + { + /* Nextbook Ares 8A */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"), + DMI_MATCH(DMI_BIOS_VERSION, "M882"), + }, + }, + { } +}; + +static struct snd_soc_acpi_mach *cht_ess8316_quirk(void *arg) +{ + if (dmi_check_system(cht_ess8316_not_present_table)) + return NULL; + + return arg; +} + static const struct snd_soc_acpi_codecs rt5640_comp_ids = { .num_codecs = 2, .codecs = { "10EC5640", "10EC3276" }, @@ -113,6 +138,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", + .machine_quirk = cht_ess8316_quirk, .sof_tplg_filename = "sof-cht-es8316.tplg", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ -- cgit 1.4.1 From 88a042d599c3f253ea8a9aa1906833c144525af4 Mon Sep 17 00:00:00 2001 From: lyndonli Date: Sun, 23 Apr 2023 17:05:15 +0800 Subject: drm/amdgpu: Use the default reset when loading or reloading the driver [ Upstream commit 4eea7fb980dc44545a32eec92e2662053b34cd9d ] Below call trace and errors are observed when reloading amdgpu driver with the module parameter reset_method=3. It should do a default reset when loading or reloading the driver, regardless of the module parameter reset_method. v2: add comments inside and modify commit messages. [ +2.180243] [drm] psp gfx command ID_LOAD_TOC(0x20) failed and response status is (0x0) [ +0.000011] [drm:psp_hw_start [amdgpu]] *ERROR* Failed to load toc [ +0.000890] [drm:psp_hw_start [amdgpu]] *ERROR* PSP tmr init failed! [ +0.020683] [drm:amdgpu_fill_buffer [amdgpu]] *ERROR* Trying to clear memory with ring turned off. [ +0.000003] RIP: 0010:amdgpu_bo_release_notify+0x1ef/0x210 [amdgpu] [ +0.000004] Call Trace: [ +0.000003] [ +0.000008] ttm_bo_release+0x2c4/0x330 [amdttm] [ +0.000026] amdttm_bo_put+0x3c/0x70 [amdttm] [ +0.000020] amdgpu_bo_free_kernel+0xe6/0x140 [amdgpu] [ +0.000728] psp_v11_0_ring_destroy+0x34/0x60 [amdgpu] [ +0.000826] psp_hw_init+0xe7/0x2f0 [amdgpu] [ +0.000813] amdgpu_device_fw_loading+0x1ad/0x2d0 [amdgpu] [ +0.000731] amdgpu_device_init.cold+0x108e/0x2002 [amdgpu] [ +0.001071] ? do_pci_enable_device+0xe1/0x110 [ +0.000011] amdgpu_driver_load_kms+0x1a/0x160 [amdgpu] [ +0.000729] amdgpu_pci_probe+0x179/0x3a0 [amdgpu] Signed-off-by: lyndonli Signed-off-by: Yunxiang Li Reviewed-by: Feifei Xu Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index c83df8b78b7d..9556df0dfefa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3555,6 +3555,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, int r, i; bool px = false; u32 max_MBps; + int tmp; adev->shutdown = false; adev->flags = flags; @@ -3776,7 +3777,13 @@ int amdgpu_device_init(struct amdgpu_device *adev, } } } else { + tmp = amdgpu_reset_method; + /* It should do a default reset when loading or reloading the driver, + * regardless of the module parameter reset_method. + */ + amdgpu_reset_method = AMD_RESET_METHOD_NONE; r = amdgpu_asic_reset(adev); + amdgpu_reset_method = tmp; if (r) { dev_err(adev->dev, "asic reset on init failed\n"); goto failed; -- cgit 1.4.1 From cad1abbe488dfd149499e492344c03b87bb0b08c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Apr 2023 08:27:18 +0100 Subject: mailbox: mailbox-test: Fix potential double-free in mbox_test_message_write() [ Upstream commit 2d1e952a2b8e5e92d8d55ac88a7cf7ca5ea591ad ] If a user can make copy_from_user() fail, there is a potential for UAF/DF due to a lack of locking around the allocation, use and freeing of the data buffers. This issue is not theoretical. I managed to author a POC for it: BUG: KASAN: double-free in kfree+0x5c/0xac Free of addr ffff29280be5de00 by task poc/356 CPU: 1 PID: 356 Comm: poc Not tainted 6.1.0-00001-g961aa6552c04-dirty #20 Hardware name: linux,dummy-virt (DT) Call trace: dump_backtrace.part.0+0xe0/0xf0 show_stack+0x18/0x40 dump_stack_lvl+0x64/0x80 print_report+0x188/0x48c kasan_report_invalid_free+0xa0/0xc0 ____kasan_slab_free+0x174/0x1b0 __kasan_slab_free+0x18/0x24 __kmem_cache_free+0x130/0x2e0 kfree+0x5c/0xac mbox_test_message_write+0x208/0x29c full_proxy_write+0x90/0xf0 vfs_write+0x154/0x440 ksys_write+0xcc/0x180 __arm64_sys_write+0x44/0x60 invoke_syscall+0x60/0x190 el0_svc_common.constprop.0+0x7c/0x160 do_el0_svc+0x40/0xf0 el0_svc+0x2c/0x6c el0t_64_sync_handler+0xf4/0x120 el0t_64_sync+0x18c/0x190 Allocated by task 356: kasan_save_stack+0x3c/0x70 kasan_set_track+0x2c/0x40 kasan_save_alloc_info+0x24/0x34 __kasan_kmalloc+0xb8/0xc0 kmalloc_trace+0x58/0x70 mbox_test_message_write+0x6c/0x29c full_proxy_write+0x90/0xf0 vfs_write+0x154/0x440 ksys_write+0xcc/0x180 __arm64_sys_write+0x44/0x60 invoke_syscall+0x60/0x190 el0_svc_common.constprop.0+0x7c/0x160 do_el0_svc+0x40/0xf0 el0_svc+0x2c/0x6c el0t_64_sync_handler+0xf4/0x120 el0t_64_sync+0x18c/0x190 Freed by task 357: kasan_save_stack+0x3c/0x70 kasan_set_track+0x2c/0x40 kasan_save_free_info+0x38/0x5c ____kasan_slab_free+0x13c/0x1b0 __kasan_slab_free+0x18/0x24 __kmem_cache_free+0x130/0x2e0 kfree+0x5c/0xac mbox_test_message_write+0x208/0x29c full_proxy_write+0x90/0xf0 vfs_write+0x154/0x440 ksys_write+0xcc/0x180 __arm64_sys_write+0x44/0x60 invoke_syscall+0x60/0x190 el0_svc_common.constprop.0+0x7c/0x160 do_el0_svc+0x40/0xf0 el0_svc+0x2c/0x6c el0t_64_sync_handler+0xf4/0x120 el0t_64_sync+0x18c/0x190 Signed-off-by: Lee Jones Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/mailbox-test.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 4555d678fadd..6dd5b9614452 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ struct mbox_test_device { char *signal; char *message; spinlock_t lock; + struct mutex mutex; wait_queue_head_t waitq; struct fasync_struct *async_queue; struct dentry *root_debugfs_dir; @@ -110,6 +112,8 @@ static ssize_t mbox_test_message_write(struct file *filp, return -EINVAL; } + mutex_lock(&tdev->mutex); + tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); if (!tdev->message) return -ENOMEM; @@ -144,6 +148,8 @@ out: kfree(tdev->message); tdev->signal = NULL; + mutex_unlock(&tdev->mutex); + return ret < 0 ? ret : count; } @@ -392,6 +398,7 @@ static int mbox_test_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tdev); spin_lock_init(&tdev->lock); + mutex_init(&tdev->mutex); if (tdev->rx_channel) { tdev->rx_buffer = devm_kzalloc(&pdev->dev, -- cgit 1.4.1 From e2d161c539a4e1d48e4575d9fbcbcd09762899d6 Mon Sep 17 00:00:00 2001 From: Jammy Huang Date: Fri, 21 Apr 2023 08:33:54 +0800 Subject: drm/ast: Fix ARM compatibility [ Upstream commit 4327a6137ed43a091d900b1ac833345d60f32228 ] ARM architecture only has 'memory', so all devices are accessed by MMIO if possible. Signed-off-by: Jammy Huang Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20230421003354.27767-1-jammy_huang@aspeedtech.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/ast/ast_main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 067453266897..5df527051177 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -427,11 +427,12 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, return ERR_PTR(-EIO); /* - * If we don't have IO space at all, use MMIO now and - * assume the chip has MMIO enabled by default (rev 0x20 - * and higher). + * After AST2500, MMIO is enabled by default, and it should be adopted + * to be compatible with Arm. */ - if (!(pci_resource_flags(pdev, 2) & IORESOURCE_IO)) { + if (pdev->revision >= 0x40) { + ast->ioregs = ast->regs + AST_IO_MM_OFFSET; + } else if (!(pci_resource_flags(pdev, 2) & IORESOURCE_IO)) { drm_info(dev, "platform has no IO space, trying MMIO\n"); ast->ioregs = ast->regs + AST_IO_MM_OFFSET; } -- cgit 1.4.1 From 0433baa89318728b5e451a67b8d16f1d21f5e2dd Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 26 Apr 2023 11:51:35 +0100 Subject: btrfs: abort transaction when sibling keys check fails for leaves [ Upstream commit 9ae5afd02a03d4e22a17a9609b19400b77c36273 ] If the sibling keys check fails before we move keys from one sibling leaf to another, we are not aborting the transaction - we leave that to some higher level caller of btrfs_search_slot() (or anything else that uses it to insert items into a b+tree). This means that the transaction abort will provide a stack trace that omits the b+tree modification call chain. So change this to immediately abort the transaction and therefore get a more useful stack trace that shows us the call chain in the bt+tree modification code. It's also important to immediately abort the transaction just in case some higher level caller is not doing it, as this indicates a very serious corruption and we should stop the possibility of doing further damage. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/ctree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index dbbae92ac23d..ab9f8d6c4f1b 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -3118,6 +3118,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root if (check_sibling_keys(left, right)) { ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); btrfs_tree_unlock(right); free_extent_buffer(right); return ret; @@ -3348,6 +3349,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root if (check_sibling_keys(left, right)) { ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); goto out; } return __push_leaf_left(path, min_data_size, -- cgit 1.4.1 From 6578e0f196e80bc8d3c30a8a9cf44b3a347223e9 Mon Sep 17 00:00:00 2001 From: Haibo Li Date: Mon, 17 Apr 2023 10:17:07 +0100 Subject: ARM: 9295/1: unwind:fix unwind abort for uleb128 case [ Upstream commit fa3eeb638de0c1a9d2d860e5b48259facdd65176 ] When unwind instruction is 0xb2,the subsequent instructions are uleb128 bytes. For now,it uses only the first uleb128 byte in code. For vsp increments of 0x204~0x400,use one uleb128 byte like below: 0xc06a00e4 : 0x80b27fac Compact model index: 0 0xb2 0x7f vsp = vsp + 1024 0xac pop {r4, r5, r6, r7, r8, r14} For vsp increments larger than 0x400,use two uleb128 bytes like below: 0xc06a00e4 : @0xc0cc9e0c Compact model index: 1 0xb2 0x81 0x01 vsp = vsp + 1032 0xac pop {r4, r5, r6, r7, r8, r14} The unwind works well since the decoded uleb128 byte is also 0x81. For vsp increments larger than 0x600,use two uleb128 bytes like below: 0xc06a00e4 : @0xc0cc9e0c Compact model index: 1 0xb2 0x81 0x02 vsp = vsp + 1544 0xac pop {r4, r5, r6, r7, r8, r14} In this case,the decoded uleb128 result is 0x101(vsp=0x204+(0x101<<2)). While the uleb128 used in code is 0x81(vsp=0x204+(0x81<<2)). The unwind aborts at this frame since it gets incorrect vsp. To fix this,add uleb128 decode to cover all the above case. Signed-off-by: Haibo Li Reviewed-by: Linus Walleij Reviewed-by: Alexandre Mergnat Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Russell King (Oracle) Signed-off-by: Sasha Levin --- arch/arm/kernel/unwind.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c index a37ea6c772cd..2e6aa5dc15bf 100644 --- a/arch/arm/kernel/unwind.c +++ b/arch/arm/kernel/unwind.c @@ -307,6 +307,29 @@ static int unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block *ctrl, return URC_OK; } +static unsigned long unwind_decode_uleb128(struct unwind_ctrl_block *ctrl) +{ + unsigned long bytes = 0; + unsigned long insn; + unsigned long result = 0; + + /* + * unwind_get_byte() will advance `ctrl` one instruction at a time, so + * loop until we get an instruction byte where bit 7 is not set. + * + * Note: This decodes a maximum of 4 bytes to output 28 bits data where + * max is 0xfffffff: that will cover a vsp increment of 1073742336, hence + * it is sufficient for unwinding the stack. + */ + do { + insn = unwind_get_byte(ctrl); + result |= (insn & 0x7f) << (bytes * 7); + bytes++; + } while (!!(insn & 0x80) && (bytes != sizeof(result))); + + return result; +} + /* * Execute the current unwind instruction. */ @@ -360,7 +383,7 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) if (ret) goto error; } else if (insn == 0xb2) { - unsigned long uleb128 = unwind_get_byte(ctrl); + unsigned long uleb128 = unwind_decode_uleb128(ctrl); ctrl->vrs[SP] += 0x204 + (uleb128 << 2); } else { -- cgit 1.4.1 From 705f4dcc41c20af880c7254ad8576a30762967eb Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 27 Apr 2023 00:33:37 -0500 Subject: hwmon: (k10temp) Add PCI ID for family 19, model 78h [ Upstream commit 7d8accfaa0ab65e4282c8e58950f7d688342cd86 ] Enable k10temp on this system. [ bp: Massage. ] Signed-off-by: Mario Limonciello Signed-off-by: Borislav Petkov (AMD) Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20230427053338.16653-3-mario.limonciello@amd.com Signed-off-by: Sasha Levin --- drivers/hwmon/k10temp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index be8bbb1c3a02..823d0ca1d605 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -507,6 +507,7 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M60H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M70H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M78H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, {} }; -- cgit 1.4.1 From 18913fc7c1dd759b68cf5f0ec9867f99ade553e3 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Sat, 11 Feb 2023 21:55:34 +0100 Subject: media: rcar-vin: Select correct interrupt mode for V4L2_FIELD_ALTERNATE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e10707d5865c90d3dfe4ef589ce02ff4287fef85 ] When adding proper support for V4L2_FIELD_ALTERNATE it was missed that this field format should trigger an interrupt for each field, not just for the whole frame. Fix this by marking it as progressive in the capture setup, which will then select the correct interrupt mode. Tested on both Gen2 and Gen3 with the result of a doubling of the frame rate for V4L2_FIELD_ALTERNATE. From a PAL video source the frame rate is now 50, which is expected for alternate field capture. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 3aea96d85165..ef5adffae197 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -651,11 +651,9 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_FIELD_SEQ_TB: case V4L2_FIELD_SEQ_BT: case V4L2_FIELD_NONE: - vnmc = VNMC_IM_ODD_EVEN; - progressive = true; - break; case V4L2_FIELD_ALTERNATE: vnmc = VNMC_IM_ODD_EVEN; + progressive = true; break; default: vnmc = VNMC_IM_ODD; -- cgit 1.4.1 From 809efd7a69a4c45af512038dadfa2e8603396cbe Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Sun, 16 Apr 2023 17:49:32 +0200 Subject: platform/x86: intel_scu_pcidrv: Add back PCI ID for Medfield [ Upstream commit 4a9b6850c794e4394cad99e2b863d75f5bc8e92f ] This id was removed in commit b47018a778c1 ("platform/x86: intel_scu_ipc: Remove Lincroft support"), saying it is only used on Moorestown, but apparently the same id is also used on Medfield. Tested on the Medfield based Motorola RAZR i smartphone. Signed-off-by: Julian Winkler Link: https://lore.kernel.org/r/20230416154932.6579-1-julian.winkler1@web.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/intel_scu_pcidrv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c index 80abc708e4f2..d904fad499aa 100644 --- a/drivers/platform/x86/intel_scu_pcidrv.c +++ b/drivers/platform/x86/intel_scu_pcidrv.c @@ -34,6 +34,7 @@ static int intel_scu_pci_probe(struct pci_dev *pdev, static const struct pci_device_id pci_ids[] = { { PCI_VDEVICE(INTEL, 0x080e) }, + { PCI_VDEVICE(INTEL, 0x082a) }, { PCI_VDEVICE(INTEL, 0x08ea) }, { PCI_VDEVICE(INTEL, 0x0a94) }, { PCI_VDEVICE(INTEL, 0x11a0) }, -- cgit 1.4.1 From 61c3962ab1f9647998bab2aa47fb29bcc214d41a Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Wed, 26 Apr 2023 10:23:44 -0400 Subject: platform/mellanox: fix potential race in mlxbf-tmfifo driver [ Upstream commit 3d43f9f639542fadfb28f40b509bf147a6624d48 ] This commit adds memory barrier for the 'vq' update in function mlxbf_tmfifo_virtio_find_vqs() to avoid potential race due to out-of-order memory write. It also adds barrier for the 'is_ready' flag to make sure the initializations are visible before this flag is checked. Signed-off-by: Liming Sun Reviewed-by: Vadim Pasternak Link: https://lore.kernel.org/r/b98c0ab61d644ba38fa9b3fd1607b138b0dd820b.1682518748.git.limings@nvidia.com Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/mellanox/mlxbf-tmfifo.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index 1ae3c56b66b0..b2e19f30a928 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -765,7 +765,7 @@ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx) fifo = vring->fifo; /* Return if vdev is not ready. */ - if (!fifo->vdev[devid]) + if (!fifo || !fifo->vdev[devid]) return; /* Return if another vring is running. */ @@ -961,9 +961,13 @@ static int mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev, vq->num_max = vring->num; + vq->priv = vring; + + /* Make vq update visible before using it. */ + virtio_mb(false); + vqs[i] = vq; vring->vq = vq; - vq->priv = vring; } return 0; @@ -1260,6 +1264,9 @@ static int mlxbf_tmfifo_probe(struct platform_device *pdev) mod_timer(&fifo->timer, jiffies + MLXBF_TMFIFO_TIMER_INTERVAL); + /* Make all updates visible before setting the 'is_ready' flag. */ + virtio_mb(false); + fifo->is_ready = true; return 0; -- cgit 1.4.1 From 5ae4a618a1558d2b536fdd5d42e53d3e2d73870c Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 28 Apr 2023 12:07:46 -0400 Subject: gfs2: Don't deref jdesc in evict [ Upstream commit 504a10d9e46bc37b23d0a1ae2f28973c8516e636 ] On corrupt gfs2 file systems the evict code can try to reference the journal descriptor structure, jdesc, after it has been freed and set to NULL. The sequence of events is: init_journal() ... fail_jindex: gfs2_jindex_free(sdp); <------frees journals, sets jdesc = NULL if (gfs2_holder_initialized(&ji_gh)) gfs2_glock_dq_uninit(&ji_gh); fail: iput(sdp->sd_jindex); <--references jdesc in evict_linked_inode evict() gfs2_evict_inode() evict_linked_inode() ret = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); <------references the now freed/zeroed sd_jdesc pointer. The call to gfs2_trans_begin is done because the truncate_inode_pages call can cause gfs2 events that require a transaction, such as removing journaled data (jdata) blocks from the journal. This patch fixes the problem by adding a check for sdp->sd_jdesc to function gfs2_evict_inode. In theory, this should only happen to corrupt gfs2 file systems, when gfs2 detects the problem, reports it, then tries to evict all the system inodes it has read in up to that point. Reported-by: Yang Lan Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher Signed-off-by: Sasha Levin --- fs/gfs2/super.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 2015bd05cba1..9d27aa8bd2bc 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1380,6 +1380,14 @@ static void gfs2_evict_inode(struct inode *inode) if (inode->i_nlink || sb_rdonly(sb)) goto out; + /* + * In case of an incomplete mount, gfs2_evict_inode() may be called for + * system files without having an active journal to write to. In that + * case, skip the filesystem evict. + */ + if (!sdp->sd_jdesc) + goto out; + gfs2_holder_mark_uninitialized(&gh); ret = evict_should_delete(inode, &gh); if (ret == SHOULD_DEFER_EVICTION) -- cgit 1.4.1 From 3e336ad6f517d1078b1e6e874e1578827088f269 Mon Sep 17 00:00:00 2001 From: Yifan Zhang Date: Thu, 27 Apr 2023 14:01:05 +0800 Subject: drm/amdgpu: set gfx9 onwards APU atomics support to be true [ Upstream commit af7828fbceed4f9e503034111066a0adef3db383 ] APUs w/ gfx9 onwards doesn't reply on PCIe atomics, rather it is internal path w/ native atomic support. Set have_atomics_support to true. Signed-off-by: Yifan Zhang Reviewed-by: Lang Yu Acked-by: Felix Kuehling Acked-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 9556df0dfefa..ef0a94c70859 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3734,6 +3734,12 @@ int amdgpu_device_init(struct amdgpu_device *adev, adev->have_atomics_support = ((struct amd_sriov_msg_pf2vf_info *) adev->virt.fw_reserve.p_pf2vf)->pcie_atomic_ops_support_flags == (PCI_EXP_DEVCAP2_ATOMIC_COMP32 | PCI_EXP_DEVCAP2_ATOMIC_COMP64); + /* APUs w/ gfx9 onwards doesn't reply on PCIe atomics, rather it is a + * internal path natively support atomics, set have_atomics_support to true. + */ + else if ((adev->flags & AMD_IS_APU) && + (adev->ip_versions[GC_HWIP][0] > IP_VERSION(9, 0, 0))) + adev->have_atomics_support = true; else adev->have_atomics_support = !pci_enable_atomic_ops_to_root(adev->pdev, -- cgit 1.4.1 From c6c0a9f619584be19726ce7f81c31bc555af401a Mon Sep 17 00:00:00 2001 From: Zheng Wang Date: Thu, 27 Apr 2023 11:08:41 +0800 Subject: fbdev: imsttfb: Fix use after free bug in imsttfb_probe [ Upstream commit c75f5a55061091030a13fef71b9995b89bc86213 ] A use-after-free bug may occur if init_imstt invokes framebuffer_release and free the info ptr. The caller, imsttfb_probe didn't notice that and still keep the ptr as private data in pdev. If we remove the driver which will call imsttfb_remove to make cleanup, UAF happens. Fix it by return error code if bad case happens in init_imstt. Signed-off-by: Zheng Wang Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/video/fbdev/imsttfb.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c index d7edb9c5d3a3..e6adb2890ecf 100644 --- a/drivers/video/fbdev/imsttfb.c +++ b/drivers/video/fbdev/imsttfb.c @@ -1347,7 +1347,7 @@ static const struct fb_ops imsttfb_ops = { .fb_ioctl = imsttfb_ioctl, }; -static void init_imstt(struct fb_info *info) +static int init_imstt(struct fb_info *info) { struct imstt_par *par = info->par; __u32 i, tmp, *ip, *end; @@ -1420,7 +1420,7 @@ static void init_imstt(struct fb_info *info) || !(compute_imstt_regvals(par, info->var.xres, info->var.yres))) { printk("imsttfb: %ux%ux%u not supported\n", info->var.xres, info->var.yres, info->var.bits_per_pixel); framebuffer_release(info); - return; + return -ENODEV; } sprintf(info->fix.id, "IMS TT (%s)", par->ramdac == IBM ? "IBM" : "TVP"); @@ -1456,12 +1456,13 @@ static void init_imstt(struct fb_info *info) if (register_framebuffer(info) < 0) { framebuffer_release(info); - return; + return -ENODEV; } tmp = (read_reg_le32(par->dc_regs, SSTATUS) & 0x0f00) >> 8; fb_info(info, "%s frame buffer; %uMB vram; chip version %u\n", info->fix.id, info->fix.smem_len >> 20, tmp); + return 0; } static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -1529,10 +1530,10 @@ static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!par->cmap_regs) goto error; info->pseudo_palette = par->palette; - init_imstt(info); - - pci_set_drvdata(pdev, info); - return 0; + ret = init_imstt(info); + if (!ret) + pci_set_drvdata(pdev, info); + return ret; error: if (par->dc_regs) -- cgit 1.4.1 From be2aefa202a5af820c3adfb7731f68c0941d7702 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 22 Apr 2023 23:24:26 +0200 Subject: fbdev: modedb: Add 1920x1080 at 60 Hz video mode [ Upstream commit c8902258b2b8ecaa1b8d88c312853c5b14c2553d ] Add typical resolution for Full-HD monitors. Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/video/fbdev/core/modedb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 6473e0dfe146..e78ec7f72846 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -257,6 +257,11 @@ static const struct fb_videomode modedb[] = { { NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3, 0, FB_VMODE_DOUBLE }, + /* 1920x1080 @ 60 Hz, 67.3 kHz hsync */ + { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, 0, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED }, + /* 1920x1200 @ 60 Hz, 74.5 Khz hsync */ { NULL, 60, 1920, 1200, 5177, 128, 336, 1, 38, 208, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, -- cgit 1.4.1 From f83c32ed05d428884918eca34a7d87378f0194cd Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 12 May 2023 11:50:33 +0200 Subject: fbdev: stifb: Fix info entry in sti_struct on error path [ Upstream commit 0bdf1ad8d10bd4e50a8b1a2c53d15984165f7fea ] Minor fix to reset the info field to NULL in case of error. Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/video/fbdev/stifb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/stifb.c b/drivers/video/fbdev/stifb.c index ef8a4c5fc687..63f51783352d 100644 --- a/drivers/video/fbdev/stifb.c +++ b/drivers/video/fbdev/stifb.c @@ -1413,6 +1413,7 @@ out_err1: iounmap(info->screen_base); out_err0: kfree(fb); + sti->info = NULL; return -ENXIO; } -- cgit 1.4.1 From 5af920e4d1d120b6a0cca246b242b296f0da96f8 Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Fri, 12 May 2023 17:05:32 +0400 Subject: nbd: Fix debugfs_create_dir error checking [ Upstream commit 4913cfcf014c95f0437db2df1734472fd3e15098 ] The debugfs_create_dir function returns ERR_PTR in case of error, and the only correct way to check if an error occurred is 'IS_ERR' inline function. This patch will replace the null-comparison with IS_ERR. Signed-off-by: Ivan Orlov Link: https://lore.kernel.org/r/20230512130533.98709-1-ivan.orlov0322@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/block/nbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 888a6abb50f5..7718c81e1dba 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1665,7 +1665,7 @@ static int nbd_dev_dbg_init(struct nbd_device *nbd) return -EIO; dir = debugfs_create_dir(nbd_name(nbd), nbd_dbg_dir); - if (!dir) { + if (IS_ERR(dir)) { dev_err(nbd_to_dev(nbd), "Failed to create debugfs dir for '%s'\n", nbd_name(nbd)); return -EIO; @@ -1691,7 +1691,7 @@ static int nbd_dbg_init(void) struct dentry *dbg_dir; dbg_dir = debugfs_create_dir("nbd", NULL); - if (!dbg_dir) + if (IS_ERR(dbg_dir)) return -EIO; nbd_dbg_dir = dbg_dir; -- cgit 1.4.1 From 46193dd43dce8bd0b6c16ded5d4fb8c40a1f4572 Mon Sep 17 00:00:00 2001 From: Guoqing Jiang Date: Fri, 12 May 2023 11:46:31 +0800 Subject: block/rnbd: replace REQ_OP_FLUSH with REQ_OP_WRITE [ Upstream commit 5e6e08087a4acb4ee3574cea32dbff0f63c7f608 ] Since flush bios are implemented as writes with no data and the preflush flag per Christoph's comment [1]. And we need to change it in rnbd accordingly. Otherwise, I got splatting when create fs from rnbd client. [ 464.028545] ------------[ cut here ]------------ [ 464.028553] WARNING: CPU: 0 PID: 65 at block/blk-core.c:751 submit_bio_noacct+0x32c/0x5d0 [ ... ] [ 464.028668] CPU: 0 PID: 65 Comm: kworker/0:1H Tainted: G OE 6.4.0-rc1 #9 [ 464.028671] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.15.0-0-g2dd4b9b-rebuilt.opensuse.org 04/01/2014 [ 464.028673] Workqueue: ib-comp-wq ib_cq_poll_work [ib_core] [ 464.028717] RIP: 0010:submit_bio_noacct+0x32c/0x5d0 [ 464.028720] Code: 03 0f 85 51 fe ff ff 48 8b 43 18 8b 88 04 03 00 00 85 c9 0f 85 3f fe ff ff e9 be fd ff ff 0f b6 d0 3c 0d 74 26 83 fa 01 74 21 <0f> 0b b8 0a 00 00 00 e9 56 fd ff ff 4c 89 e7 e8 70 a1 03 00 84 c0 [ 464.028722] RSP: 0018:ffffaf3680b57c68 EFLAGS: 00010202 [ 464.028724] RAX: 0000000000060802 RBX: ffffa09dcc18bf00 RCX: 0000000000000000 [ 464.028726] RDX: 0000000000000002 RSI: 0000000000000000 RDI: ffffa09dde081d00 [ 464.028727] RBP: ffffaf3680b57c98 R08: ffffa09dde081d00 R09: ffffa09e38327200 [ 464.028729] R10: 0000000000000000 R11: 0000000000000000 R12: ffffa09dde081d00 [ 464.028730] R13: ffffa09dcb06e1e8 R14: 0000000000000000 R15: 0000000000200000 [ 464.028733] FS: 0000000000000000(0000) GS:ffffa09e3bc00000(0000) knlGS:0000000000000000 [ 464.028735] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 464.028736] CR2: 000055a4e8206c40 CR3: 0000000119f06000 CR4: 00000000003506f0 [ 464.028738] Call Trace: [ 464.028740] [ 464.028746] submit_bio+0x1b/0x80 [ 464.028748] rnbd_srv_rdma_ev+0x50d/0x10c0 [rnbd_server] [ 464.028754] ? percpu_ref_get_many.constprop.0+0x55/0x140 [rtrs_server] [ 464.028760] ? __this_cpu_preempt_check+0x13/0x20 [ 464.028769] process_io_req+0x1dc/0x450 [rtrs_server] [ 464.028775] rtrs_srv_inv_rkey_done+0x67/0xb0 [rtrs_server] [ 464.028780] __ib_process_cq+0xbc/0x1f0 [ib_core] [ 464.028793] ib_cq_poll_work+0x2b/0xa0 [ib_core] [ 464.028804] process_one_work+0x2a9/0x580 [1]. https://lore.kernel.org/all/ZFHgefWofVt24tRl@infradead.org/ Signed-off-by: Guoqing Jiang Reviewed-by: Christoph Hellwig Reviewed-by: Chaitanya Kulkarni Link: https://lore.kernel.org/r/20230512034631.28686-1-guoqing.jiang@linux.dev Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/block/rnbd/rnbd-proto.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/rnbd/rnbd-proto.h b/drivers/block/rnbd/rnbd-proto.h index ea7ac8bca63c..da1d0542d7e2 100644 --- a/drivers/block/rnbd/rnbd-proto.h +++ b/drivers/block/rnbd/rnbd-proto.h @@ -241,7 +241,7 @@ static inline blk_opf_t rnbd_to_bio_flags(u32 rnbd_opf) bio_opf = REQ_OP_WRITE; break; case RNBD_OP_FLUSH: - bio_opf = REQ_OP_FLUSH | REQ_PREFLUSH; + bio_opf = REQ_OP_WRITE | REQ_PREFLUSH; break; case RNBD_OP_DISCARD: bio_opf = REQ_OP_DISCARD; -- cgit 1.4.1 From 53786bfadc4371ba163b3292bb39713baf55a54f Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Wed, 3 May 2023 18:57:33 +0300 Subject: nvme-pci: add NVME_QUIRK_BOGUS_NID for HS-SSD-FUTURE 2048G [ Upstream commit 1616d6c3717bae9041a4240d381ec56ccdaafedc ] Add a quirk to fix HS-SSD-FUTURE 2048G SSD drives reporting duplicate nsids. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217384 Reported-by: Andrey God Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig Signed-off-by: Sasha Levin --- drivers/nvme/host/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 581bf94416e6..3347e86b3c55 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3554,6 +3554,8 @@ static const struct pci_device_id nvme_id_table[] = { NVME_QUIRK_IGNORE_DEV_SUBNQN, }, { PCI_DEVICE(0x10ec, 0x5763), /* TEAMGROUP T-FORCE CARDEA ZERO Z330 SSD */ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1e4b, 0x1602), /* HS-SSD-FUTURE 2048G */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(PCI_VENDOR_ID_AMAZON, 0x0061), .driver_data = NVME_QUIRK_DMA_ADDRESS_BITS_48, }, { PCI_DEVICE(PCI_VENDOR_ID_AMAZON, 0x0065), -- cgit 1.4.1 From 7d98a36b101f2d753dcf734208851db0bd3e6242 Mon Sep 17 00:00:00 2001 From: Hristo Venev Date: Tue, 25 Apr 2023 22:58:54 +0300 Subject: nvme-pci: add quirk for missing secondary temperature thresholds [ Upstream commit bd375feeaf3408ed00e08c3bc918d6be15f691ad ] On Kingston KC3000 and Kingston FURY Renegade (both have the same PCI IDs) accessing temp3_{min,max} fails with an invalid field error (note that there is no problem setting the thresholds for temp1). This contradicts the NVM Express Base Specification 2.0b, page 292: The over temperature threshold and under temperature threshold features shall be implemented for all implemented temperature sensors (i.e., all Temperature Sensor fields that report a non-zero value). Define NVME_QUIRK_NO_SECONDARY_TEMP_THRESH that disables the thresholds for all but the composite temperature and set it for this device. Signed-off-by: Hristo Venev Reviewed-by: Guenter Roeck Signed-off-by: Christoph Hellwig Signed-off-by: Sasha Levin --- drivers/nvme/host/hwmon.c | 4 +++- drivers/nvme/host/nvme.h | 5 +++++ drivers/nvme/host/pci.c | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/hwmon.c b/drivers/nvme/host/hwmon.c index 9e6e56c20ec9..316f3e4ca7cc 100644 --- a/drivers/nvme/host/hwmon.c +++ b/drivers/nvme/host/hwmon.c @@ -163,7 +163,9 @@ static umode_t nvme_hwmon_is_visible(const void *_data, case hwmon_temp_max: case hwmon_temp_min: if ((!channel && data->ctrl->wctemp) || - (channel && data->log->temp_sensor[channel - 1])) { + (channel && data->log->temp_sensor[channel - 1] && + !(data->ctrl->quirks & + NVME_QUIRK_NO_SECONDARY_TEMP_THRESH))) { if (data->ctrl->quirks & NVME_QUIRK_NO_TEMP_THRESH_CHANGE) return 0444; diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 01d90424af53..3f82de6060ef 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -149,6 +149,11 @@ enum nvme_quirks { * Reports garbage in the namespace identifiers (eui64, nguid, uuid). */ NVME_QUIRK_BOGUS_NID = (1 << 18), + + /* + * No temperature thresholds for channels other than 0 (Composite). + */ + NVME_QUIRK_NO_SECONDARY_TEMP_THRESH = (1 << 19), }; /* diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 3347e86b3c55..1ec0ca40604a 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3515,6 +3515,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, { PCI_DEVICE(0x2646, 0x2263), /* KINGSTON A2000 NVMe SSD */ .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, + { PCI_DEVICE(0x2646, 0x5013), /* Kingston KC3000, Kingston FURY Renegade */ + .driver_data = NVME_QUIRK_NO_SECONDARY_TEMP_THRESH, }, { PCI_DEVICE(0x2646, 0x5018), /* KINGSTON OM8SFP4xxxxP OS21012 NVMe SSD */ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x2646, 0x5016), /* KINGSTON OM3PGP4xxxxP OS21011 NVMe SSD */ -- cgit 1.4.1 From 0960fb87cdaf9629f14dea8c7dc62f883c93c72d Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 5 May 2023 10:14:58 -0600 Subject: ASoC: amd: yc: Add DMI entry to support System76 Pangolin 12 [ Upstream commit 7b9891ad25246b18b5ccc19518da7abc7763aa0a ] Add pang12 quirk to enable the internal microphone. Signed-off-by: Jeremy Soller --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index b9958e555367..84b401b685f7 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -297,6 +297,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8A22"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "System76"), + DMI_MATCH(DMI_PRODUCT_VERSION, "pang12"), + } + }, {} }; -- cgit 1.4.1 From c03dd939051403cb81bc33f397d47d0bde5e2aee Mon Sep 17 00:00:00 2001 From: Maxim Kochetkov Date: Fri, 5 May 2023 09:28:20 +0300 Subject: ASoC: dwc: limit the number of overrun messages [ Upstream commit ab6ecfbf40fccf74b6ec2ba7ed6dd2fc024c3af2 ] On slow CPU (FPGA/QEMU emulated) printing overrun messages from interrupt handler to uart console may leads to more overrun errors. So use dev_err_ratelimited to limit the number of error messages. Signed-off-by: Maxim Kochetkov --- sound/soc/dwc/dwc-i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 7f7dd07c63b2..3496301582b2 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -132,13 +132,13 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) /* Error Handling: TX */ if (isr[i] & ISR_TXFO) { - dev_err(dev->dev, "TX overrun (ch_id=%d)\n", i); + dev_err_ratelimited(dev->dev, "TX overrun (ch_id=%d)\n", i); irq_valid = true; } /* Error Handling: TX */ if (isr[i] & ISR_RXFO) { - dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i); + dev_err_ratelimited(dev->dev, "RX overrun (ch_id=%d)\n", i); irq_valid = true; } } -- cgit 1.4.1 From 40798c566b567621ab98b3c2a779df8bd6d98aa7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 25 Apr 2023 10:38:37 +0200 Subject: um: harddog: fix modular build [ Upstream commit 73a23d7710331a530e972903318528b75e5a5f58 ] Since we no longer (want to) export any libc symbols the _user portions of any drivers need to be built into image rather than the module. I missed this for the watchdog. Fix the watchdog accordingly. Reported-by: kernel test robot Signed-off-by: Johannes Berg Signed-off-by: Richard Weinberger Signed-off-by: Sasha Levin --- arch/um/drivers/Makefile | 4 +++- arch/um/drivers/harddog.h | 9 +++++++++ arch/um/drivers/harddog_kern.c | 7 +------ arch/um/drivers/harddog_user.c | 1 + arch/um/drivers/harddog_user_exp.c | 9 +++++++++ 5 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 arch/um/drivers/harddog.h create mode 100644 arch/um/drivers/harddog_user_exp.c diff --git a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile index e1dc4292bd22..65b449c992d2 100644 --- a/arch/um/drivers/Makefile +++ b/arch/um/drivers/Makefile @@ -16,7 +16,8 @@ mconsole-objs := mconsole_kern.o mconsole_user.o hostaudio-objs := hostaudio_kern.o ubd-objs := ubd_kern.o ubd_user.o port-objs := port_kern.o port_user.o -harddog-objs := harddog_kern.o harddog_user.o +harddog-objs := harddog_kern.o +harddog-builtin-$(CONFIG_UML_WATCHDOG) := harddog_user.o harddog_user_exp.o rtc-objs := rtc_kern.o rtc_user.o LDFLAGS_pcap.o = $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libpcap.a) @@ -60,6 +61,7 @@ obj-$(CONFIG_PTY_CHAN) += pty.o obj-$(CONFIG_TTY_CHAN) += tty.o obj-$(CONFIG_XTERM_CHAN) += xterm.o xterm_kern.o obj-$(CONFIG_UML_WATCHDOG) += harddog.o +obj-y += $(harddog-builtin-y) $(harddog-builtin-m) obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o obj-$(CONFIG_UML_RANDOM) += random.o obj-$(CONFIG_VIRTIO_UML) += virtio_uml.o diff --git a/arch/um/drivers/harddog.h b/arch/um/drivers/harddog.h new file mode 100644 index 000000000000..6d9ea60e7133 --- /dev/null +++ b/arch/um/drivers/harddog.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef UM_WATCHDOG_H +#define UM_WATCHDOG_H + +int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock); +void stop_watchdog(int in_fd, int out_fd); +int ping_watchdog(int fd); + +#endif /* UM_WATCHDOG_H */ diff --git a/arch/um/drivers/harddog_kern.c b/arch/um/drivers/harddog_kern.c index e6d4f43deba8..60d1c6cab8a9 100644 --- a/arch/um/drivers/harddog_kern.c +++ b/arch/um/drivers/harddog_kern.c @@ -47,6 +47,7 @@ #include #include #include "mconsole.h" +#include "harddog.h" MODULE_LICENSE("GPL"); @@ -60,8 +61,6 @@ static int harddog_out_fd = -1; * Allow only one person to hold it open */ -extern int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock); - static int harddog_open(struct inode *inode, struct file *file) { int err = -EBUSY; @@ -92,8 +91,6 @@ err: return err; } -extern void stop_watchdog(int in_fd, int out_fd); - static int harddog_release(struct inode *inode, struct file *file) { /* @@ -112,8 +109,6 @@ static int harddog_release(struct inode *inode, struct file *file) return 0; } -extern int ping_watchdog(int fd); - static ssize_t harddog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { diff --git a/arch/um/drivers/harddog_user.c b/arch/um/drivers/harddog_user.c index 070468d22e39..9ed89304975e 100644 --- a/arch/um/drivers/harddog_user.c +++ b/arch/um/drivers/harddog_user.c @@ -7,6 +7,7 @@ #include #include #include +#include "harddog.h" struct dog_data { int stdin_fd; diff --git a/arch/um/drivers/harddog_user_exp.c b/arch/um/drivers/harddog_user_exp.c new file mode 100644 index 000000000000..c74d4b815d14 --- /dev/null +++ b/arch/um/drivers/harddog_user_exp.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "harddog.h" + +#if IS_MODULE(CONFIG_UML_WATCHDOG) +EXPORT_SYMBOL(start_watchdog); +EXPORT_SYMBOL(stop_watchdog); +EXPORT_SYMBOL(ping_watchdog); +#endif -- cgit 1.4.1 From f6d2aa322ca78af88d210d0de7ca09d35bc60626 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 10 May 2023 01:14:14 +0000 Subject: xfrm: Check if_id in inbound policy/secpath match [ Upstream commit 8680407b6f8f5fba59e8f1d63c869abc280f04df ] This change ensures that if configured in the policy, the if_id set in the policy and secpath states match during the inbound policy check. Without this, there is potential for ambiguity where entries in the secpath differing by only the if_id could be mismatched. Notably, this is checked in the outbound direction when resolving templates to SAs, but not on the inbound path when matching SAs and policies. Test: Tested against Android kernel unit tests & CTS Signed-off-by: Benedict Wong Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_policy.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index bc04cb83215f..e894c269affb 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3237,7 +3237,7 @@ xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl) static inline int xfrm_state_ok(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, - unsigned short family) + unsigned short family, u32 if_id) { if (xfrm_state_kern(x)) return tmpl->optional && !xfrm_state_addr_cmp(tmpl, x, tmpl->encap_family); @@ -3248,7 +3248,8 @@ xfrm_state_ok(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, (tmpl->allalgs || (tmpl->aalgos & (1<props.aalgo)) || !(xfrm_id_proto_match(tmpl->id.proto, IPSEC_PROTO_ANY))) && !(x->props.mode != XFRM_MODE_TRANSPORT && - xfrm_state_addr_cmp(tmpl, x, family)); + xfrm_state_addr_cmp(tmpl, x, family)) && + (if_id == 0 || if_id == x->if_id); } /* @@ -3260,7 +3261,7 @@ xfrm_state_ok(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, */ static inline int xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int start, - unsigned short family) + unsigned short family, u32 if_id) { int idx = start; @@ -3270,7 +3271,7 @@ xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int star } else start = -1; for (; idx < sp->len; idx++) { - if (xfrm_state_ok(tmpl, sp->xvec[idx], family)) + if (xfrm_state_ok(tmpl, sp->xvec[idx], family, if_id)) return ++idx; if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) { if (start == -1) @@ -3649,7 +3650,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, * are implied between each two transformations. */ for (i = xfrm_nr-1, k = 0; i >= 0; i--) { - k = xfrm_policy_ok(tpp[i], sp, k, family); + k = xfrm_policy_ok(tpp[i], sp, k, family, if_id); if (k < 0) { if (k < -1) /* "-2 - errored_index" returned */ -- cgit 1.4.1 From 603f239216ca0e052fae52b02baa5d48e2335f42 Mon Sep 17 00:00:00 2001 From: Martin Povišer Date: Tue, 9 May 2023 17:34:12 +0200 Subject: ASoC: dt-bindings: Adjust #sound-dai-cells on TI's single-DAI codecs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit efb2bfd7b3d210c479b9361c176d7426e5eb8663 ] A bunch of TI's codecs have binding schemas which force #sound-dai-cells to one despite those codecs only having a single DAI. Allow for bindings with zero DAI cells and deprecate the former non-zero value. Signed-off-by: Martin Povišer --- Documentation/devicetree/bindings/sound/tas2562.yaml | 6 ++++-- Documentation/devicetree/bindings/sound/tas2770.yaml | 6 ++++-- Documentation/devicetree/bindings/sound/tas27xx.yaml | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/tas2562.yaml b/Documentation/devicetree/bindings/sound/tas2562.yaml index 30f6b029ac08..cb519a4b6e71 100644 --- a/Documentation/devicetree/bindings/sound/tas2562.yaml +++ b/Documentation/devicetree/bindings/sound/tas2562.yaml @@ -52,7 +52,9 @@ properties: description: TDM TX current sense time slot. '#sound-dai-cells': - const: 1 + # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward + # compatibility but is deprecated. + enum: [0, 1] required: - compatible @@ -69,7 +71,7 @@ examples: codec: codec@4c { compatible = "ti,tas2562"; reg = <0x4c>; - #sound-dai-cells = <1>; + #sound-dai-cells = <0>; interrupt-parent = <&gpio1>; interrupts = <14>; shutdown-gpios = <&gpio1 15 0>; diff --git a/Documentation/devicetree/bindings/sound/tas2770.yaml b/Documentation/devicetree/bindings/sound/tas2770.yaml index bc90e72bf7cf..1859fbe1cdf1 100644 --- a/Documentation/devicetree/bindings/sound/tas2770.yaml +++ b/Documentation/devicetree/bindings/sound/tas2770.yaml @@ -54,7 +54,9 @@ properties: - 1 # Falling edge '#sound-dai-cells': - const: 1 + # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward + # compatibility but is deprecated. + enum: [0, 1] required: - compatible @@ -71,7 +73,7 @@ examples: codec: codec@41 { compatible = "ti,tas2770"; reg = <0x41>; - #sound-dai-cells = <1>; + #sound-dai-cells = <0>; interrupt-parent = <&gpio1>; interrupts = <14>; reset-gpio = <&gpio1 15 0>; diff --git a/Documentation/devicetree/bindings/sound/tas27xx.yaml b/Documentation/devicetree/bindings/sound/tas27xx.yaml index 66a0df8850ea..079cb6f8d447 100644 --- a/Documentation/devicetree/bindings/sound/tas27xx.yaml +++ b/Documentation/devicetree/bindings/sound/tas27xx.yaml @@ -47,7 +47,9 @@ properties: description: TDM TX voltage sense time slot. '#sound-dai-cells': - const: 1 + # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward + # compatibility but is deprecated. + enum: [0, 1] required: - compatible @@ -64,7 +66,7 @@ examples: codec: codec@38 { compatible = "ti,tas2764"; reg = <0x38>; - #sound-dai-cells = <1>; + #sound-dai-cells = <0>; interrupt-parent = <&gpio1>; interrupts = <14>; reset-gpios = <&gpio1 15 0>; -- cgit 1.4.1 From 7fbdd3bd7bbf56aaee15c63cff5c7cb4d5a3920d Mon Sep 17 00:00:00 2001 From: Alexandru Sorodoc Date: Thu, 11 May 2023 19:15:10 +0300 Subject: ALSA: hda/realtek: Add quirks for ASUS GU604V and GU603V [ Upstream commit 4b963ae1df6426f0e51de64133d379d9bde50c48 ] These models use 2 CS35L41 amplifiers using SPI for down-facing speakers. alc285_fixup_speaker2_to_dac1 is needed to fix volume control of the down-facing speakers. Pin configs are needed to enable headset mic detection. Note that these models lack the ACPI _DSD properties needed to initialize the amplifiers. They can be added during boot to get working sound out of the speakers: https://gist.github.com/lamperez/862763881c0e1c812392b5574727f6ff Signed-off-by: Alexandru Sorodoc Link: https://lore.kernel.org/r/20230511161510.315170-1-ealex95@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/pci/hda/patch_realtek.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 379f216158ab..7b5f194513c7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7063,6 +7063,8 @@ enum { ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_FIXUP_DISABLE_DAC3, ALC285_FIXUP_SPEAKER2_TO_DAC1, + ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, + ALC285_FIXUP_ASUS_HEADSET_MIC, ALC280_FIXUP_HP_HEADSET_MIC, ALC221_FIXUP_HP_FRONT_MIC, ALC292_FIXUP_TPT460, @@ -8033,6 +8035,22 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, + [ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC245_FIXUP_CS35L41_SPI_2 + }, + [ALC285_FIXUP_ASUS_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1 + }, [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -9507,6 +9525,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), + SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604V", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603V", ALC285_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2), -- cgit 1.4.1 From 59dad726de2dd12728680594f0da7eec77cc7022 Mon Sep 17 00:00:00 2001 From: Paweł Anikiel Date: Mon, 8 May 2023 13:30:37 +0200 Subject: ASoC: ssm2602: Add workaround for playback distortions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f63550e2b165208a2f382afcaf5551df9569e1d4 ] Apply a workaround for what appears to be a hardware quirk. The problem seems to happen when enabling "whole chip power" (bit D7 register R6) for the very first time after the chip receives power. If either "output" (D4) or "DAC" (D3) aren't powered on at that time, playback becomes very distorted later on. This happens on the Google Chameleon v3, as well as on a ZYBO Z7-10: https://ez.analog.com/audio/f/q-a/543726/solved-ssm2603-right-output-offset-issue/480229 I suspect this happens only when using an external MCLK signal (which is the case for both of these boards). Here are some experiments run on a Google Chameleon v3. These were run in userspace using a wrapper around the i2cset utility: ssmset() { i2cset -y 0 0x1a $(($1*2)) $2 } For each of the following sequences, we apply power to the ssm2603 chip, set the configuration registers R0-R5 and R7-R8, run the selected sequence, and check for distortions on playback. ssmset 0x09 0x01 # core ssmset 0x06 0x07 # chip, out, dac OK ssmset 0x09 0x01 # core ssmset 0x06 0x87 # out, dac ssmset 0x06 0x07 # chip OK (disable MCLK) ssmset 0x09 0x01 # core ssmset 0x06 0x1f # chip ssmset 0x06 0x07 # out, dac (enable MCLK) OK ssmset 0x09 0x01 # core ssmset 0x06 0x1f # chip ssmset 0x06 0x07 # out, dac NOT OK ssmset 0x06 0x1f # chip ssmset 0x09 0x01 # core ssmset 0x06 0x07 # out, dac NOT OK ssmset 0x09 0x01 # core ssmset 0x06 0x0f # chip, out ssmset 0x06 0x07 # dac NOT OK ssmset 0x09 0x01 # core ssmset 0x06 0x17 # chip, dac ssmset 0x06 0x07 # out NOT OK For each of the following sequences, we apply power to the ssm2603 chip, run the selected sequence, issue a reset with R15, configure R0-R5 and R7-R8, run one of the NOT OK sequences from above, and check for distortions. ssmset 0x09 0x01 # core ssmset 0x06 0x07 # chip, out, dac OK (disable MCLK) ssmset 0x09 0x01 # core ssmset 0x06 0x07 # chip, out, dac (enable MCLK after reset) NOT OK ssmset 0x09 0x01 # core ssmset 0x06 0x17 # chip, dac NOT OK ssmset 0x09 0x01 # core ssmset 0x06 0x0f # chip, out NOT OK ssmset 0x06 0x07 # chip, out, dac NOT OK Signed-off-by: Paweł Anikiel --- sound/soc/codecs/ssm2602.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index cbbe83b85ada..cf7927222be1 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -53,6 +53,18 @@ static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = { { .reg = 0x09, .def = 0x0000 } }; +/* + * ssm2602 register patch + * Workaround for playback distortions after power up: activates digital + * core, and then powers on output, DAC, and whole chip at the same time + */ + +static const struct reg_sequence ssm2602_patch[] = { + { SSM2602_ACTIVE, 0x01 }, + { SSM2602_PWR, 0x07 }, + { SSM2602_RESET, 0x00 }, +}; + /*Appending several "None"s just for OSS mixer use*/ static const char *ssm2602_input_select[] = { @@ -589,6 +601,9 @@ static int ssm260x_component_probe(struct snd_soc_component *component) return ret; } + regmap_register_patch(ssm2602->regmap, ssm2602_patch, + ARRAY_SIZE(ssm2602_patch)); + /* set the update bits */ regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL, LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH); -- cgit 1.4.1 From 8914ae00db45ce3af541ddc3bc5303719ca0ea13 Mon Sep 17 00:00:00 2001 From: YongSu Yoo Date: Sun, 5 Mar 2023 21:25:19 +0000 Subject: media: dvb_demux: fix a bug for the continuity counter [ Upstream commit 7efb10d8dc70ea3000cc70dca53407c52488acd1 ] In dvb_demux.c, some logics exist which compare the expected continuity counter and the real continuity counter. If they are not matched each other, both of the expected continuity counter and the real continuity counter should be printed. But there exists a bug that the expected continuity counter is not correctly printed. The expected continuity counter is replaced with the real countinuity counter + 1 so that the epected continuity counter is not correclty printed. This is wrong. This bug is fixed. Link: https://lore.kernel.org/linux-media/20230305212519.499-1-yongsuyoo0215@gmail.com Signed-off-by: YongSu Yoo Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/dvb-core/dvb_demux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 83cc32ad7e12..eadd579bb4fa 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -115,12 +115,12 @@ static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed, cc = buf[3] & 0x0f; ccok = ((feed->cc + 1) & 0x0f) == cc; - feed->cc = cc; if (!ccok) { set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED); dprintk_sect_loss("missed packet: %d instead of %d!\n", cc, (feed->cc + 1) & 0x0f); } + feed->cc = cc; if (buf[1] & 0x40) // PUSI ? feed->peslen = 0xfffa; @@ -300,7 +300,6 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, cc = buf[3] & 0x0f; ccok = ((feed->cc + 1) & 0x0f) == cc; - feed->cc = cc; if (buf[3] & 0x20) { /* adaption field present, check for discontinuity_indicator */ @@ -336,6 +335,7 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, feed->pusi_seen = false; dvb_dmx_swfilter_section_new(feed); } + feed->cc = cc; if (buf[1] & 0x40) { /* PUSI=1 (is set), section boundary is here */ -- cgit 1.4.1 From 1027c8c068f070ce8773ffbd2b897e2baf4b5b6e Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Fri, 10 Mar 2023 16:56:04 +0000 Subject: media: dvb-usb: az6027: fix three null-ptr-deref in az6027_i2c_xfer() [ Upstream commit 858e97d7956d17a2cb56a9413468704a4d5abfe1 ] In az6027_i2c_xfer, msg is controlled by user. When msg[i].buf is null, commit 0ed554fd769a ("media: dvb-usb: az6027: fix null-ptr-deref in az6027_i2c_xfer()") fix the null-ptr-deref bug when msg[i].addr is 0x99. However, null-ptr-deref also happens when msg[i].addr is 0xd0 and 0xc0. We add check on msg[i].len to prevent null-ptr-deref. Link: https://lore.kernel.org/linux-media/20230310165604.3093483-1-harperchen1110@gmail.com Signed-off-by: Wei Chen Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/dvb-usb/az6027.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c index 7d78ee09be5e..a31c6f82f4e9 100644 --- a/drivers/media/usb/dvb-usb/az6027.c +++ b/drivers/media/usb/dvb-usb/az6027.c @@ -988,6 +988,10 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n /* write/read request */ if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { req = 0xB9; + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); value = msg[i].addr + (msg[i].len << 8); length = msg[i + 1].len + 6; @@ -1001,6 +1005,10 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n /* demod 16bit addr */ req = 0xBD; + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); value = msg[i].addr + (2 << 8); length = msg[i].len - 2; @@ -1026,6 +1034,10 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n } else { req = 0xBD; + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } index = msg[i].buf[0] & 0x00FF; value = msg[i].addr + (1 << 8); length = msg[i].len - 1; -- cgit 1.4.1 From 336ca9b371042681b348a11d66755d781ecf59ad Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Mon, 13 Mar 2023 08:58:53 +0000 Subject: media: dvb-usb-v2: ec168: fix null-ptr-deref in ec168_i2c_xfer() [ Upstream commit a6dcefcc08eca1bf4e3d213c97c3cfb75f377935 ] In ec168_i2c_xfer, msg is controlled by user. When msg[i].buf is null and msg[i].len is zero, former checks on msg[i].buf would be passed. If accessing msg[i].buf[0] without sanity check, null pointer deref would happen. We add check on msg[i].len to prevent crash. Similar commit: commit 0ed554fd769a ("media: dvb-usb: az6027: fix null-ptr-deref in az6027_i2c_xfer()") Link: https://lore.kernel.org/linux-media/20230313085853.3252349-1-harperchen1110@gmail.com Signed-off-by: Wei Chen Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/dvb-usb-v2/ec168.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c index 7ed0ab9e429b..0e4773fc025c 100644 --- a/drivers/media/usb/dvb-usb-v2/ec168.c +++ b/drivers/media/usb/dvb-usb-v2/ec168.c @@ -115,6 +115,10 @@ static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], while (i < num) { if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { if (msg[i].addr == ec168_ec100_config.demod_address) { + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } req.cmd = READ_DEMOD; req.value = 0; req.index = 0xff00 + msg[i].buf[0]; /* reg */ @@ -131,6 +135,10 @@ static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], } } else { if (msg[i].addr == ec168_ec100_config.demod_address) { + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } req.cmd = WRITE_DEMOD; req.value = msg[i].buf[1]; /* val */ req.index = 0xff00 + msg[i].buf[0]; /* reg */ @@ -139,6 +147,10 @@ static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ret = ec168_ctrl_msg(d, &req); i += 1; } else { + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } req.cmd = WRITE_I2C; req.value = msg[i].buf[0]; /* val */ req.index = 0x0100 + msg[i].addr; /* I2C addr */ -- cgit 1.4.1 From 4e896b226311a9627e669d119c16eeb64d5f248a Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Mon, 13 Mar 2023 09:27:51 +0000 Subject: media: dvb-usb-v2: ce6230: fix null-ptr-deref in ce6230_i2c_master_xfer() [ Upstream commit dff919090155fb22679869e8469168f270dcd97f ] In ce6230_i2c_master_xfer, msg is controlled by user. When msg[i].buf is null and msg[i].len is zero, former checks on msg[i].buf would be passed. Malicious data finally reach ce6230_i2c_master_xfer. If accessing msg[i].buf[0] without sanity check, null ptr deref would happen. We add check on msg[i].len to prevent crash. Similar commit: commit 0ed554fd769a ("media: dvb-usb: az6027: fix null-ptr-deref in az6027_i2c_xfer()") Link: https://lore.kernel.org/linux-media/20230313092751.209496-1-harperchen1110@gmail.com Signed-off-by: Wei Chen Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/dvb-usb-v2/ce6230.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.c b/drivers/media/usb/dvb-usb-v2/ce6230.c index 44540de1a206..d3b5cb4a24da 100644 --- a/drivers/media/usb/dvb-usb-v2/ce6230.c +++ b/drivers/media/usb/dvb-usb-v2/ce6230.c @@ -101,6 +101,10 @@ static int ce6230_i2c_master_xfer(struct i2c_adapter *adap, if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { if (msg[i].addr == ce6230_zl10353_config.demod_address) { + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } req.cmd = DEMOD_READ; req.value = msg[i].addr >> 1; req.index = msg[i].buf[0]; @@ -117,6 +121,10 @@ static int ce6230_i2c_master_xfer(struct i2c_adapter *adap, } else { if (msg[i].addr == ce6230_zl10353_config.demod_address) { + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } req.cmd = DEMOD_WRITE; req.value = msg[i].addr >> 1; req.index = msg[i].buf[0]; -- cgit 1.4.1 From 6906e613e6b7db2fd969167e9ff2090a205732d2 Mon Sep 17 00:00:00 2001 From: Zhang Shurong Date: Sun, 7 May 2023 15:52:47 +0100 Subject: media: dvb-usb-v2: rtl28xxu: fix null-ptr-deref in rtl28xxu_i2c_xfer [ Upstream commit aa4a447b81b84f69c1a89ad899df157f386d7636 ] In rtl28xxu_i2c_xfer, msg is controlled by user. When msg[i].buf is null and msg[i].len is zero, former checks on msg[i].buf would be passed. Malicious data finally reach rtl28xxu_i2c_xfer. If accessing msg[i].buf[0] without sanity check, null ptr deref would happen. We add check on msg[i].len to prevent crash. Similar commit: commit 0ed554fd769a ("media: dvb-usb: az6027: fix null-ptr-deref in az6027_i2c_xfer()") Link: https://lore.kernel.org/linux-media/tencent_3623572106754AC2F266B316798B0F6CCA05@qq.com Signed-off-by: Zhang Shurong Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 795a012d4020..f7884bb56fcc 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -176,6 +176,10 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ret = -EOPNOTSUPP; goto err_mutex_unlock; } else if (msg[0].addr == 0x10) { + if (msg[0].len < 1 || msg[1].len < 1) { + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } /* method 1 - integrated demod */ if (msg[0].buf[0] == 0x00) { /* return demod page from driver cache */ @@ -189,6 +193,10 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ret = rtl28xxu_ctrl_msg(d, &req); } } else if (msg[0].len < 2) { + if (msg[0].len < 1) { + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } /* method 2 - old I2C */ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); req.index = CMD_I2C_RD; @@ -217,8 +225,16 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ret = -EOPNOTSUPP; goto err_mutex_unlock; } else if (msg[0].addr == 0x10) { + if (msg[0].len < 1) { + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } /* method 1 - integrated demod */ if (msg[0].buf[0] == 0x00) { + if (msg[0].len < 2) { + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } /* save demod page for later demod access */ dev->page = msg[0].buf[1]; ret = 0; @@ -231,6 +247,10 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ret = rtl28xxu_ctrl_msg(d, &req); } } else if ((msg[0].len < 23) && (!dev->new_i2c_write)) { + if (msg[0].len < 1) { + ret = -EOPNOTSUPP; + goto err_mutex_unlock; + } /* method 2 - old I2C */ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); req.index = CMD_I2C_WR; -- cgit 1.4.1 From 5d2923fb0b72b09ef06888f18a1a17f756a7e7be Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Mon, 13 Mar 2023 09:50:08 +0000 Subject: media: dvb-usb: digitv: fix null-ptr-deref in digitv_i2c_xfer() [ Upstream commit 9ded5bd2a49ce3015b7c936743eec0a0e6e11f0c ] In digitv_i2c_xfer, msg is controlled by user. When msg[i].buf is null and msg[i].len is zero, former checks on msg[i].buf would be passed. Malicious data finally reach digitv_i2c_xfer. If accessing msg[i].buf[0] without sanity check, null ptr deref would happen. We add check on msg[i].len to prevent crash. Similar commit: commit 0ed554fd769a ("media: dvb-usb: az6027: fix null-ptr-deref in az6027_i2c_xfer()") Link: https://lore.kernel.org/linux-media/20230313095008.1039689-1-harperchen1110@gmail.com Signed-off-by: Wei Chen Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/dvb-usb/digitv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/dvb-usb/digitv.c b/drivers/media/usb/dvb-usb/digitv.c index 2756815a780b..32134be16914 100644 --- a/drivers/media/usb/dvb-usb/digitv.c +++ b/drivers/media/usb/dvb-usb/digitv.c @@ -63,6 +63,10 @@ static int digitv_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num warn("more than 2 i2c messages at a time is not handled yet. TODO."); for (i = 0; i < num; i++) { + if (msg[i].len < 1) { + i = -EOPNOTSUPP; + break; + } /* write/read request */ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { if (digitv_ctrl_msg(d, USB_READ_COFDM, msg[i].buf[0], NULL, 0, -- cgit 1.4.1 From b769fbf04a918d3710b2903a0b89869152b69271 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Tue, 28 Mar 2023 13:44:16 +0100 Subject: media: dvb-usb: dw2102: fix uninit-value in su3000_read_mac_address [ Upstream commit a3fd1ef27aa686d871cefe207bd6168c4b0cd29e ] In su3000_read_mac_address, if i2c_transfer fails to execute two messages, array mac address will not be initialized. Without handling such error, later in function dvb_usb_adapter_dvb_init, proposed_mac is accessed before initialization. Fix this error by returning a negative value if message execution fails. Link: https://lore.kernel.org/linux-media/20230328124416.560889-1-harperchen1110@gmail.com Signed-off-by: Wei Chen Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/dvb-usb/dw2102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 0ca764282c76..8747960e6146 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -946,7 +946,7 @@ static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) for (i = 0; i < 6; i++) { obuf[1] = 0xf0 + i; if (i2c_transfer(&d->i2c_adap, msg, 2) != 2) - break; + return -1; else mac[i] = ibuf[0]; } -- cgit 1.4.1 From 058822591b782feafaa17c2c96a9c464f7ca0d7a Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Wed, 15 Mar 2023 13:45:18 +0000 Subject: media: netup_unidvb: fix irq init by register it at the end of probe [ Upstream commit e6ad6233592593079db5c8fa592c298e51bc1356 ] IRQ handler netup_spi_interrupt() takes spinlock spi->lock. The lock is initialized in netup_spi_init(). However, irq handler is registered before initializing the lock. Spinlock dma->lock and i2c->lock suffer from the same problem. Fix this by registering the irq at the end of probe. Link: https://lore.kernel.org/linux-media/20230315134518.1074497-1-harperchen1110@gmail.com Signed-off-by: Wei Chen Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index aaa1d2dedebd..d85bfbb77a25 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -887,12 +887,7 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev, ndev->lmmio0, (u32)pci_resource_len(pci_dev, 0), ndev->lmmio1, (u32)pci_resource_len(pci_dev, 1), pci_dev->irq); - if (request_irq(pci_dev->irq, netup_unidvb_isr, IRQF_SHARED, - "netup_unidvb", pci_dev) < 0) { - dev_err(&pci_dev->dev, - "%s(): can't get IRQ %d\n", __func__, pci_dev->irq); - goto irq_request_err; - } + ndev->dma_size = 2 * 188 * NETUP_DMA_BLOCKS_COUNT * NETUP_DMA_PACKETS_COUNT; ndev->dma_virt = dma_alloc_coherent(&pci_dev->dev, @@ -933,6 +928,14 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev, dev_err(&pci_dev->dev, "netup_unidvb: DMA setup failed\n"); goto dma_setup_err; } + + if (request_irq(pci_dev->irq, netup_unidvb_isr, IRQF_SHARED, + "netup_unidvb", pci_dev) < 0) { + dev_err(&pci_dev->dev, + "%s(): can't get IRQ %d\n", __func__, pci_dev->irq); + goto dma_setup_err; + } + dev_info(&pci_dev->dev, "netup_unidvb: device has been initialized\n"); return 0; @@ -951,8 +954,6 @@ spi_setup_err: dma_free_coherent(&pci_dev->dev, ndev->dma_size, ndev->dma_virt, ndev->dma_phys); dma_alloc_err: - free_irq(pci_dev->irq, pci_dev); -irq_request_err: iounmap(ndev->lmmio1); pci_bar1_error: iounmap(ndev->lmmio0); -- cgit 1.4.1 From dd68399361151aea127d14d03a2951ae19b825f5 Mon Sep 17 00:00:00 2001 From: YongSu Yoo Date: Thu, 18 Aug 2022 13:50:27 +0100 Subject: media: dvb_ca_en50221: fix a size write bug [ Upstream commit a4315e5be7020aac9b24a8151caf4bb85224cd0e ] The function of "dvb_ca_en50221_write_data" at source/drivers/media /dvb-core/dvb_ca_en50221.c is used for two cases. The first case is for writing APDU data in the function of "dvb_ca_en50221_io_write" at source/drivers/media/dvb-core/ dvb_ca_en50221.c. The second case is for writing the host link buf size on the Command Register in the function of "dvb_ca_en50221_link_init" at source/drivers/media/dvb-core/dvb_ca_en50221.c. In the second case, there exists a bug like following. In the function of the "dvb_ca_en50221_link_init", after a TV host calculates the host link buf_size, the TV host writes the calculated host link buf_size on the Size Register. Accroding to the en50221 Spec (the page 60 of https://dvb.org/wp-content/uploads/2020/02/En50221.V1.pdf), before this writing operation, the "SW(CMDREG_SW)" flag in the Command Register should be set. We can see this setting operation in the function of the "dvb_ca_en50221_link_init" like below. ... if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) return ret; ... But, after that, the real writing operation is implemented using the function of the "dvb_ca_en50221_write_data" in the function of "dvb_ca_en50221_link_init", and the "dvb_ca_en50221_write_data" includes the function of "ca->pub->write_cam_control", and the function of the "ca->pub->write_cam_control" in the function of the "dvb_ca_en50221_wrte_data" does not include "CMDREG_SW" flag like below. ... if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_HC)) != 0) ... In the above source code, we can see only the "IRQEN | CMDREG_HC", but we cannot see the "CMDREG_SW". The "CMDREG_SW" flag which was set in the function of the "dvb_ca_en50221_link_init" was rollbacked by the follwoing function of the "dvb_ca_en50221_write_data". This is a bug. and this bug causes that the calculated host link buf_size is not properly written in the CI module. Through this patch, we fix this bug. Link: https://lore.kernel.org/linux-media/20220818125027.1131-1-yongsuyoo0215@gmail.com Signed-off-by: YongSu Yoo Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/dvb-core/dvb_ca_en50221.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index c2d2792227f8..b6ca29dfb184 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -187,7 +187,7 @@ static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 *ebuf, int ecount); static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, - u8 *ebuf, int ecount); + u8 *ebuf, int ecount, int size_write_flag); /** * findstr - Safely find needle in haystack. @@ -370,7 +370,7 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot) ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10); if (ret) return ret; - ret = dvb_ca_en50221_write_data(ca, slot, buf, 2); + ret = dvb_ca_en50221_write_data(ca, slot, buf, 2, CMDREG_SW); if (ret != 2) return -EIO; ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN); @@ -778,11 +778,13 @@ exit: * @buf: The data in this buffer is treated as a complete link-level packet to * be written. * @bytes_write: Size of ebuf. + * @size_write_flag: A flag on Command Register which says whether the link size + * information will be writen or not. * * return: Number of bytes written, or < 0 on error. */ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, - u8 *buf, int bytes_write) + u8 *buf, int bytes_write, int size_write_flag) { struct dvb_ca_slot *sl = &ca->slot_info[slot]; int status; @@ -817,7 +819,7 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, /* OK, set HC bit */ status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, - IRQEN | CMDREG_HC); + IRQEN | CMDREG_HC | size_write_flag); if (status) goto exit; @@ -1508,7 +1510,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, mutex_lock(&sl->slot_lock); status = dvb_ca_en50221_write_data(ca, slot, fragbuf, - fraglen + 2); + fraglen + 2, 0); mutex_unlock(&sl->slot_lock); if (status == (fraglen + 2)) { written = 1; -- cgit 1.4.1 From ea2938c27b0212aaab6702c16b7385e073b35643 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Thu, 17 Nov 2022 04:59:25 +0000 Subject: media: ttusb-dec: fix memory leak in ttusb_dec_exit_dvb() [ Upstream commit 517a281338322ff8293f988771c98aaa7205e457 ] Since dvb_frontend_detach() is not called in ttusb_dec_exit_dvb(), which is called when the device is disconnected, dvb_frontend_free() is not finally called. This causes a memory leak just by repeatedly plugging and unplugging the device. Fix this issue by adding dvb_frontend_detach() to ttusb_dec_exit_dvb(). Link: https://lore.kernel.org/linux-media/20221117045925.14297-5-imv4bel@gmail.com Signed-off-by: Hyunwoo Kim Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/ttusb-dec/ttusb_dec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index 38822cedd93a..c4474d4c44e2 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -1544,8 +1544,7 @@ static void ttusb_dec_exit_dvb(struct ttusb_dec *dec) dvb_dmx_release(&dec->demux); if (dec->fe) { dvb_unregister_frontend(dec->fe); - if (dec->fe->ops.release) - dec->fe->ops.release(dec->fe); + dvb_frontend_detach(dec->fe); } dvb_unregister_adapter(&dec->adapter); } -- cgit 1.4.1 From bf3b6f82fd4cd0873739610aaafea40ffc97321e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 13:13:18 +0000 Subject: media: mn88443x: fix !CONFIG_OF error by drop of_match_ptr from ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ae11c0efaec32fb45130ee9886689f467232eebc ] The driver will match mostly by DT table (even thought there is regular ID table) so there is little benefit in of_match_ptr (this also allows ACPI matching via PRP0001, even though it might not be relevant here). This also fixes !CONFIG_OF error: drivers/media/dvb-frontends/mn88443x.c:782:34: error: ‘mn88443x_of_match’ defined but not used [-Werror=unused-const-variable=] Link: https://lore.kernel.org/linux-media/20230312131318.351173-28-krzysztof.kozlowski@linaro.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/dvb-frontends/mn88443x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c index 452571b380b7..5a024870979d 100644 --- a/drivers/media/dvb-frontends/mn88443x.c +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -798,7 +798,7 @@ MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id); static struct i2c_driver mn88443x_driver = { .driver = { .name = "mn88443x", - .of_match_table = of_match_ptr(mn88443x_of_match), + .of_match_table = mn88443x_of_match, }, .probe = mn88443x_probe, .remove = mn88443x_remove, -- cgit 1.4.1 From 93b5dfebcb1821dde466e29404fcf1fb919f4c72 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Thu, 17 Nov 2022 04:59:23 +0000 Subject: media: dvb-core: Fix use-after-free due on race condition at dvb_net [ Upstream commit 4172385b0c9ac366dcab78eda48c26814b87ed1a ] A race condition may occur between the .disconnect function, which is called when the device is disconnected, and the dvb_device_open() function, which is called when the device node is open()ed. This results in several types of UAFs. The root cause of this is that you use the dvb_device_open() function, which does not implement a conditional statement that checks 'dvbnet->exit'. So, add 'remove_mutex` to protect 'dvbnet->exit' and use locked_dvb_net_open() function to check 'dvbnet->exit'. [mchehab: fix a checkpatch warning] Link: https://lore.kernel.org/linux-media/20221117045925.14297-3-imv4bel@gmail.com Signed-off-by: Hyunwoo Kim Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/dvb-core/dvb_net.c | 38 +++++++++++++++++++++++++++++++++++--- include/media/dvb_net.h | 4 ++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index 8a2febf33ce2..8bb8dd34c223 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1564,15 +1564,43 @@ static long dvb_net_ioctl(struct file *file, return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl); } +static int locked_dvb_net_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_net *dvbnet = dvbdev->priv; + int ret; + + if (mutex_lock_interruptible(&dvbnet->remove_mutex)) + return -ERESTARTSYS; + + if (dvbnet->exit) { + mutex_unlock(&dvbnet->remove_mutex); + return -ENODEV; + } + + ret = dvb_generic_open(inode, file); + + mutex_unlock(&dvbnet->remove_mutex); + + return ret; +} + static int dvb_net_close(struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; struct dvb_net *dvbnet = dvbdev->priv; + mutex_lock(&dvbnet->remove_mutex); + dvb_generic_release(inode, file); - if(dvbdev->users == 1 && dvbnet->exit == 1) + if (dvbdev->users == 1 && dvbnet->exit == 1) { + mutex_unlock(&dvbnet->remove_mutex); wake_up(&dvbdev->wait_queue); + } else { + mutex_unlock(&dvbnet->remove_mutex); + } + return 0; } @@ -1580,7 +1608,7 @@ static int dvb_net_close(struct inode *inode, struct file *file) static const struct file_operations dvb_net_fops = { .owner = THIS_MODULE, .unlocked_ioctl = dvb_net_ioctl, - .open = dvb_generic_open, + .open = locked_dvb_net_open, .release = dvb_net_close, .llseek = noop_llseek, }; @@ -1599,10 +1627,13 @@ void dvb_net_release (struct dvb_net *dvbnet) { int i; + mutex_lock(&dvbnet->remove_mutex); dvbnet->exit = 1; + mutex_unlock(&dvbnet->remove_mutex); + if (dvbnet->dvbdev->users < 1) wait_event(dvbnet->dvbdev->wait_queue, - dvbnet->dvbdev->users==1); + dvbnet->dvbdev->users == 1); dvb_unregister_device(dvbnet->dvbdev); @@ -1621,6 +1652,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, int i; mutex_init(&dvbnet->ioctl_mutex); + mutex_init(&dvbnet->remove_mutex); dvbnet->demux = dmx; for (i=0; i Date: Thu, 17 Nov 2022 04:59:24 +0000 Subject: media: dvb-core: Fix use-after-free due to race at dvb_register_device() [ Upstream commit 627bb528b086b4136315c25d6a447a98ea9448d3 ] dvb_register_device() dynamically allocates fops with kmemdup() to set the fops->owner. And these fops are registered in 'file->f_ops' using replace_fops() in the dvb_device_open() process, and kfree()d in dvb_free_device(). However, it is not common to use dynamically allocated fops instead of 'static const' fops as an argument of replace_fops(), and UAF may occur. These UAFs can occur on any dvb type using dvb_register_device(), such as dvb_dvr, dvb_demux, dvb_frontend, dvb_net, etc. So, instead of kfree() the fops dynamically allocated in dvb_register_device() in dvb_free_device() called during the .disconnect() process, kfree() it collectively in exit_dvbdev() called when the dvbdev.c module is removed. Link: https://lore.kernel.org/linux-media/20221117045925.14297-4-imv4bel@gmail.com Signed-off-by: Hyunwoo Kim Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/dvb-core/dvbdev.c | 84 ++++++++++++++++++++++++++++++----------- include/media/dvbdev.h | 15 ++++++++ 2 files changed, 78 insertions(+), 21 deletions(-) diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index a31d52cb6d62..9f9a97652708 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -27,6 +27,7 @@ #include static DEFINE_MUTEX(dvbdev_mutex); +static LIST_HEAD(dvbdevfops_list); static int dvbdev_debug; module_param(dvbdev_debug, int, 0644); @@ -452,14 +453,15 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, enum dvb_device_type type, int demux_sink_pads) { struct dvb_device *dvbdev; - struct file_operations *dvbdevfops; + struct file_operations *dvbdevfops = NULL; + struct dvbdevfops_node *node = NULL, *new_node = NULL; struct device *clsdev; int minor; int id, ret; mutex_lock(&dvbdev_register_lock); - if ((id = dvbdev_get_free_id (adap, type)) < 0){ + if ((id = dvbdev_get_free_id (adap, type)) < 0) { mutex_unlock(&dvbdev_register_lock); *pdvbdev = NULL; pr_err("%s: couldn't find free device id\n", __func__); @@ -467,18 +469,45 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, } *pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL); - if (!dvbdev){ mutex_unlock(&dvbdev_register_lock); return -ENOMEM; } - dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL); + /* + * When a device of the same type is probe()d more than once, + * the first allocated fops are used. This prevents memory leaks + * that can occur when the same device is probe()d repeatedly. + */ + list_for_each_entry(node, &dvbdevfops_list, list_head) { + if (node->fops->owner == adap->module && + node->type == type && + node->template == template) { + dvbdevfops = node->fops; + break; + } + } - if (!dvbdevfops){ - kfree (dvbdev); - mutex_unlock(&dvbdev_register_lock); - return -ENOMEM; + if (dvbdevfops == NULL) { + dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL); + if (!dvbdevfops) { + kfree(dvbdev); + mutex_unlock(&dvbdev_register_lock); + return -ENOMEM; + } + + new_node = kzalloc(sizeof(struct dvbdevfops_node), GFP_KERNEL); + if (!new_node) { + kfree(dvbdevfops); + kfree(dvbdev); + mutex_unlock(&dvbdev_register_lock); + return -ENOMEM; + } + + new_node->fops = dvbdevfops; + new_node->type = type; + new_node->template = template; + list_add_tail (&new_node->list_head, &dvbdevfops_list); } memcpy(dvbdev, template, sizeof(struct dvb_device)); @@ -489,20 +518,20 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, dvbdev->priv = priv; dvbdev->fops = dvbdevfops; init_waitqueue_head (&dvbdev->wait_queue); - dvbdevfops->owner = adap->module; - list_add_tail (&dvbdev->list_head, &adap->device_list); - down_write(&minor_rwsem); #ifdef CONFIG_DVB_DYNAMIC_MINORS for (minor = 0; minor < MAX_DVB_MINORS; minor++) if (dvb_minors[minor] == NULL) break; - if (minor == MAX_DVB_MINORS) { + if (new_node) { + list_del (&new_node->list_head); + kfree(dvbdevfops); + kfree(new_node); + } list_del (&dvbdev->list_head); - kfree(dvbdevfops); kfree(dvbdev); up_write(&minor_rwsem); mutex_unlock(&dvbdev_register_lock); @@ -511,41 +540,47 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, #else minor = nums2minor(adap->num, type, id); #endif - dvbdev->minor = minor; dvb_minors[minor] = dvb_device_get(dvbdev); up_write(&minor_rwsem); - ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads); if (ret) { pr_err("%s: dvb_register_media_device failed to create the mediagraph\n", __func__); - + if (new_node) { + list_del (&new_node->list_head); + kfree(dvbdevfops); + kfree(new_node); + } dvb_media_device_free(dvbdev); list_del (&dvbdev->list_head); - kfree(dvbdevfops); kfree(dvbdev); mutex_unlock(&dvbdev_register_lock); return ret; } - mutex_unlock(&dvbdev_register_lock); - clsdev = device_create(dvb_class, adap->device, MKDEV(DVB_MAJOR, minor), dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id); if (IS_ERR(clsdev)) { pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n", __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); + if (new_node) { + list_del (&new_node->list_head); + kfree(dvbdevfops); + kfree(new_node); + } dvb_media_device_free(dvbdev); list_del (&dvbdev->list_head); - kfree(dvbdevfops); kfree(dvbdev); + mutex_unlock(&dvbdev_register_lock); return PTR_ERR(clsdev); } + dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", adap->num, dnames[type], id, minor, minor); + mutex_unlock(&dvbdev_register_lock); return 0; } EXPORT_SYMBOL(dvb_register_device); @@ -574,7 +609,6 @@ static void dvb_free_device(struct kref *ref) { struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref); - kfree (dvbdev->fops); kfree (dvbdev); } @@ -1080,9 +1114,17 @@ error: static void __exit exit_dvbdev(void) { + struct dvbdevfops_node *node, *next; + class_destroy(dvb_class); cdev_del(&dvb_device_cdev); unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS); + + list_for_each_entry_safe(node, next, &dvbdevfops_list, list_head) { + list_del (&node->list_head); + kfree(node->fops); + kfree(node); + } } subsys_initcall(init_dvbdev); diff --git a/include/media/dvbdev.h b/include/media/dvbdev.h index ac60c9fcfe9a..34b01ebf3282 100644 --- a/include/media/dvbdev.h +++ b/include/media/dvbdev.h @@ -189,6 +189,21 @@ struct dvb_device { void *priv; }; +/** + * struct dvbdevfops_node - fops nodes registered in dvbdevfops_list + * + * @fops: Dynamically allocated fops for ->owner registration + * @type: type of dvb_device + * @template: dvb_device used for registration + * @list_head: list_head for dvbdevfops_list + */ +struct dvbdevfops_node { + struct file_operations *fops; + enum dvb_device_type type; + const struct dvb_device *template; + struct list_head list_head; +}; + /** * dvb_device_get - Increase dvb_device reference * -- cgit 1.4.1 From d0088ea444e676a0c75551efe183bee4a3d2cfc8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 12 May 2023 16:18:00 +0100 Subject: media: dvb-core: Fix kernel WARNING for blocking operation in wait_event*() [ Upstream commit b8c75e4a1b325ea0a9433fa8834be97b5836b946 ] Using a semaphore in the wait_event*() condition is no good idea. It hits a kernel WARN_ON() at prepare_to_wait_event() like: do not call blocking ops when !TASK_RUNNING; state=1 set at prepare_to_wait_event+0x6d/0x690 For avoiding the potential deadlock, rewrite to an open-coded loop instead. Unlike the loop in wait_event*(), this uses wait_woken() after the condition check, hence the task state stays consistent. CVE-2023-31084 was assigned to this bug. Link: https://lore.kernel.org/r/CA+UBctCu7fXn4q41O_3=id1+OdyQ85tZY1x+TkT-6OVBL6KAUw@mail.gmail.com/ Link: https://lore.kernel.org/linux-media/20230512151800.1874-1-tiwai@suse.de Reported-by: Yu Hao Closes: https://nvd.nist.gov/vuln/detail/CVE-2023-31084 Signed-off-by: Takashi Iwai Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/dvb-core/dvb_frontend.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index c41a7e5c2b92..fce0e2094078 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -293,14 +293,22 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe, } if (events->eventw == events->eventr) { - int ret; + struct wait_queue_entry wait; + int ret = 0; if (flags & O_NONBLOCK) return -EWOULDBLOCK; - ret = wait_event_interruptible(events->wait_queue, - dvb_frontend_test_event(fepriv, events)); - + init_waitqueue_entry(&wait, current); + add_wait_queue(&events->wait_queue, &wait); + while (!dvb_frontend_test_event(fepriv, events)) { + wait_woken(&wait, TASK_INTERRUPTIBLE, 0); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + remove_wait_queue(&events->wait_queue, &wait); if (ret < 0) return ret; } -- cgit 1.4.1 From d5d61f747e3f1ff9042db66896f2f90afa5f3197 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Mon, 21 Nov 2022 06:33:08 +0000 Subject: media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221 [ Upstream commit 280a8ab81733da8bc442253c700a52c4c0886ffd ] If the device node of dvb_ca_en50221 is open() and the device is disconnected, a UAF may occur when calling close() on the device node. The root cause is that wake_up() and wait_event() for dvbdev->wait_queue are not implemented. So implement wait_event() function in dvb_ca_en50221_release() and add 'remove_mutex' which prevents race condition for 'ca->exit'. [mchehab: fix a checkpatch warning] Link: https://lore.kernel.org/linux-media/20221121063308.GA33821@ubuntu Signed-off-by: Hyunwoo Kim Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/dvb-core/dvb_ca_en50221.c | 37 ++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index b6ca29dfb184..baf64540dc00 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -151,6 +151,12 @@ struct dvb_ca_private { /* mutex serializing ioctls */ struct mutex ioctl_mutex; + + /* A mutex used when a device is disconnected */ + struct mutex remove_mutex; + + /* Whether the device is disconnected */ + int exit; }; static void dvb_ca_private_free(struct dvb_ca_private *ca) @@ -1711,12 +1717,22 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) dprintk("%s\n", __func__); - if (!try_module_get(ca->pub->owner)) + mutex_lock(&ca->remove_mutex); + + if (ca->exit) { + mutex_unlock(&ca->remove_mutex); + return -ENODEV; + } + + if (!try_module_get(ca->pub->owner)) { + mutex_unlock(&ca->remove_mutex); return -EIO; + } err = dvb_generic_open(inode, file); if (err < 0) { module_put(ca->pub->owner); + mutex_unlock(&ca->remove_mutex); return err; } @@ -1741,6 +1757,7 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) dvb_ca_private_get(ca); + mutex_unlock(&ca->remove_mutex); return 0; } @@ -1760,6 +1777,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) dprintk("%s\n", __func__); + mutex_lock(&ca->remove_mutex); + /* mark the CA device as closed */ ca->open = 0; dvb_ca_en50221_thread_update_delay(ca); @@ -1770,6 +1789,13 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) dvb_ca_private_put(ca); + if (dvbdev->users == 1 && ca->exit == 1) { + mutex_unlock(&ca->remove_mutex); + wake_up(&dvbdev->wait_queue); + } else { + mutex_unlock(&ca->remove_mutex); + } + return err; } @@ -1893,6 +1919,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, } mutex_init(&ca->ioctl_mutex); + mutex_init(&ca->remove_mutex); if (signal_pending(current)) { ret = -EINTR; @@ -1935,6 +1962,14 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) dprintk("%s\n", __func__); + mutex_lock(&ca->remove_mutex); + ca->exit = 1; + mutex_unlock(&ca->remove_mutex); + + if (ca->dvbdev->users < 1) + wait_event(ca->dvbdev->wait_queue, + ca->dvbdev->users == 1); + /* shutdown the thread if there was one */ kthread_stop(ca->thread); -- cgit 1.4.1 From a6637d5a8f19f5caa81ed5b09384141619295704 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 12 May 2023 13:33:13 +0300 Subject: ASoC: SOF: debug: conditionally bump runtime_pm counter on exceptions [ Upstream commit 3de975862f985f1c9e225a0d13aa3d501373f7c3 ] When a firmware IPC error happens during a pm_runtime suspend, we ignore the error and suspend anyways. However, the code unconditionally increases the runtime_pm counter. This results in a confusing configuration where the code will suspend, resume but never suspend again due to the use of pm_runtime_get_noresume(). The intent of the counter increase was to prevent entry in D3, but if that transition to D3 is already started it cannot be stopped. In addition, there's no point in that case in trying to prevent anything, the firmware error is handled and the next resume will re-initialize the firmware completely. This patch changes the logic to prevent suspend when the device is pm_runtime active and has a use_count > 0. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index ade0507328af..5042312b1b98 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -437,8 +437,8 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg) /* should we prevent DSP entering D3 ? */ if (!sdev->ipc_dump_printed) dev_info(sdev->dev, - "preventing DSP entering D3 state to preserve context\n"); - pm_runtime_get_noresume(sdev->dev); + "Attempting to prevent DSP from entering D3 state to preserve context\n"); + pm_runtime_get_if_in_use(sdev->dev); } /* dump vital information to the logs */ -- cgit 1.4.1 From 1cc6301dfcd2c08559dac50e705f12d4657ff478 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 12 May 2023 13:33:14 +0300 Subject: ASoC: SOF: pcm: fix pm_runtime imbalance in error handling [ Upstream commit da0fe8fd515a471d373acc3682bfb5522cca4d55 ] When an error occurs, we need to make sure the device can pm_runtime suspend instead of keeping it active. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/pcm.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 14571b821eca..be6f38af37b5 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -619,16 +619,17 @@ static int sof_pcm_probe(struct snd_soc_component *component) "%s/%s", plat_data->tplg_filename_prefix, plat_data->tplg_filename); - if (!tplg_filename) - return -ENOMEM; + if (!tplg_filename) { + ret = -ENOMEM; + goto pm_error; + } ret = snd_sof_load_topology(component, tplg_filename); - if (ret < 0) { + if (ret < 0) dev_err(component->dev, "error: failed to load DSP topology %d\n", ret); - return ret; - } +pm_error: pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); -- cgit 1.4.1 From eb708aee4126a1c75b4b6ed7c3935ea6041fb191 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 12 May 2023 13:33:15 +0300 Subject: ASoC: SOF: sof-client-probes: fix pm_runtime imbalance in error handling [ Upstream commit bc424273c74c1565c459c8f2a6ed95caee368d0a ] When an error occurs, we need to make sure the device can pm_runtime suspend instead of keeping it active. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/sof-client-probes.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index ddeabbb5580e..1e521a9e34d2 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -441,12 +441,7 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, ret = sof_probes_points_info(cdev, &desc, &num_desc); if (ret < 0) - goto exit; - - pm_runtime_mark_last_busy(dev); - err = pm_runtime_put_autosuspend(dev); - if (err < 0) - dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err); + goto pm_error; for (i = 0; i < num_desc; i++) { offset = strlen(buf); @@ -464,6 +459,13 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf)); kfree(desc); + +pm_error: + pm_runtime_mark_last_busy(dev); + err = pm_runtime_put_autosuspend(dev); + if (err < 0) + dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err); + exit: kfree(buf); return ret; -- cgit 1.4.1 From 9f2f538cd8c331708292c6deab88dc13448cdd81 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 12 May 2023 13:46:38 +0300 Subject: ASoC: SOF: pm: save io region state in case of errors in resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 171b53be635ac15d4feafeb33946035649b1ca14 ] If there are failures in DSP runtime resume, the device state will not reach active and this makes it impossible e.g. to retrieve a possible DSP panic dump via "exception" debugfs node. If CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE=y is set, the data in cache is stale. If debugfs cache is not used, the region simply cannot be read. To allow debugging these scenarios, update the debugfs cache contents in resume error handler. User-space can then later retrieve DSP panic and other state via debugfs (requires SOF debugfs cache to be enabled in build). Reported-by: Curtis Malainey --- sound/soc/sof/pm.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 26ffcbb6e30f..a1bfa5a37e2a 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -159,7 +159,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) ret = tplg_ops->set_up_all_pipelines(sdev, false); if (ret < 0) { dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret); - return ret; + goto setup_fail; } } @@ -173,6 +173,18 @@ static int sof_resume(struct device *dev, bool runtime_resume) dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret); } +setup_fail: +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) + if (ret < 0) { + /* + * Debugfs cannot be read in runtime suspend, so cache + * the contents upon failure. This allows to capture + * possible DSP coredump information. + */ + sof_cache_debugfs(sdev); + } +#endif + return ret; } -- cgit 1.4.1 From 26cfe2faa82db5cb1cf81a674310a363d9419015 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Thu, 20 Apr 2023 14:34:10 +0200 Subject: s390/pkey: zeroize key blobs [ Upstream commit 844cf829e5f33e00b279230470c8c93b58b8c16f ] Key blobs for the IOCTLs PKEY_KBLOB2PROTK[23] may contain clear key material. Zeroize the copies of these keys in kernel memory after creating the protected key. Reviewed-by: Harald Freudenberger Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Sasha Levin --- drivers/s390/crypto/pkey_api.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 5a05d1cdfec2..a8def50c149b 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -1293,6 +1293,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, return PTR_ERR(kkey); rc = pkey_keyblob2pkey(kkey, ktp.keylen, &ktp.protkey); DEBUG_DBG("%s pkey_keyblob2pkey()=%d\n", __func__, rc); + memzero_explicit(kkey, ktp.keylen); kfree(kkey); if (rc) break; @@ -1426,6 +1427,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, kkey, ktp.keylen, &ktp.protkey); DEBUG_DBG("%s pkey_keyblob2pkey2()=%d\n", __func__, rc); kfree(apqns); + memzero_explicit(kkey, ktp.keylen); kfree(kkey); if (rc) break; @@ -1552,6 +1554,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, protkey, &protkeylen); DEBUG_DBG("%s pkey_keyblob2pkey3()=%d\n", __func__, rc); kfree(apqns); + memzero_explicit(kkey, ktp.keylen); kfree(kkey); if (rc) { kfree(protkey); -- cgit 1.4.1 From 905b247f6e139a7eac70d1f0b28b8823c13ed5c7 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 4 May 2023 16:21:48 +0200 Subject: s390/topology: honour nr_cpu_ids when adding CPUs [ Upstream commit a33239be2d38ff5a44427db1707c08787508d34a ] When SMT thread CPUs are added to CPU masks the nr_cpu_ids limit is not checked and could be exceeded. This leads to a warning for example if CONFIG_DEBUG_PER_CPU_MAPS is set and the command line parameter nr_cpus is set to 1. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Sasha Levin --- arch/s390/kernel/topology.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index c6eecd4a5302..10b20aeb27d3 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -95,7 +95,7 @@ out: static void cpu_thread_map(cpumask_t *dst, unsigned int cpu) { static cpumask_t mask; - int i; + unsigned int max_cpu; cpumask_clear(&mask); if (!cpumask_test_cpu(cpu, &cpu_setup_mask)) @@ -104,9 +104,10 @@ static void cpu_thread_map(cpumask_t *dst, unsigned int cpu) if (topology_mode != TOPOLOGY_MODE_HW) goto out; cpu -= cpu % (smp_cpu_mtid + 1); - for (i = 0; i <= smp_cpu_mtid; i++) { - if (cpumask_test_cpu(cpu + i, &cpu_setup_mask)) - cpumask_set_cpu(cpu + i, &mask); + max_cpu = min(cpu + smp_cpu_mtid, nr_cpu_ids - 1); + for (; cpu <= max_cpu; cpu++) { + if (cpumask_test_cpu(cpu, &cpu_setup_mask)) + cpumask_set_cpu(cpu, &mask); } out: cpumask_copy(dst, &mask); @@ -123,25 +124,26 @@ static void add_cpus_to_mask(struct topology_core *tl_core, unsigned int core; for_each_set_bit(core, &tl_core->mask, TOPOLOGY_CORE_BITS) { - unsigned int rcore; - int lcpu, i; + unsigned int max_cpu, rcore; + int cpu; rcore = TOPOLOGY_CORE_BITS - 1 - core + tl_core->origin; - lcpu = smp_find_processor_id(rcore << smp_cpu_mt_shift); - if (lcpu < 0) + cpu = smp_find_processor_id(rcore << smp_cpu_mt_shift); + if (cpu < 0) continue; - for (i = 0; i <= smp_cpu_mtid; i++) { - topo = &cpu_topology[lcpu + i]; + max_cpu = min(cpu + smp_cpu_mtid, nr_cpu_ids - 1); + for (; cpu <= max_cpu; cpu++) { + topo = &cpu_topology[cpu]; topo->drawer_id = drawer->id; topo->book_id = book->id; topo->socket_id = socket->id; topo->core_id = rcore; - topo->thread_id = lcpu + i; + topo->thread_id = cpu; topo->dedicated = tl_core->d; - cpumask_set_cpu(lcpu + i, &drawer->mask); - cpumask_set_cpu(lcpu + i, &book->mask); - cpumask_set_cpu(lcpu + i, &socket->mask); - smp_cpu_set_polarization(lcpu + i, tl_core->pp); + cpumask_set_cpu(cpu, &drawer->mask); + cpumask_set_cpu(cpu, &book->mask); + cpumask_set_cpu(cpu, &socket->mask); + smp_cpu_set_polarization(cpu, tl_core->pp); } } } -- cgit 1.4.1 From 329da6d07cc918b73f51d3c650747829315fb0d2 Mon Sep 17 00:00:00 2001 From: Rubén Gómez Date: Mon, 8 May 2023 18:03:07 +0000 Subject: ACPI: resource: Add IRQ override quirk for LG UltraPC 17U70P MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 71a485624c4cbb144169852d7bb8ca8c0667d7a3 ] Add an ACPI IRQ override quirk for LG UltraPC 17U70P to address the internal keyboard problem on it. Link: https://bugzilla.kernel.org/show_bug.cgi?id=213031 Link: https://bugzilla.kernel.org/show_bug.cgi?id=216983 Signed-off-by: Rubén Gómez Agudo [ rjw: Subject, changelog, white space damage fixes ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/resource.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index d08818baea88..a7f12bdbc5e2 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -502,6 +502,17 @@ static const struct dmi_system_id maingear_laptop[] = { { } }; +static const struct dmi_system_id lg_laptop[] = { + { + .ident = "LG Electronics 17U70P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), + DMI_MATCH(DMI_BOARD_NAME, "17U70P"), + }, + }, + { } +}; + struct irq_override_cmp { const struct dmi_system_id *system; unsigned char irq; @@ -518,6 +529,7 @@ static const struct irq_override_cmp override_table[] = { { lenovo_laptop, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true }, { tongfang_gm_rg, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, { maingear_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, + { lg_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, }; static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity, -- cgit 1.4.1 From de16dfe7cab5a5b2a64d79e13540f76d3b34c28a Mon Sep 17 00:00:00 2001 From: Yun Lu Date: Fri, 12 May 2023 09:20:55 +0800 Subject: wifi: rtl8xxxu: fix authentication timeout due to incorrect RCR value [ Upstream commit 20429444e653ee8242dfbf815c0c37866beb371b ] When using rtl8192cu with rtl8xxxu driver to connect wifi, there is a probability of failure, which shows "authentication with ... timed out". Through debugging, it was found that the RCR register has been inexplicably modified to an incorrect value, resulting in the nic not being able to receive authenticated frames. To fix this problem, add regrcr in rtl8xxxu_priv struct, and store the RCR value every time the register is written, and use it the next time the register need to be modified. Signed-off-by: Yun Lu Link: https://lore.kernel.org/all/20230427020512.1221062-1-luyun_611@163.com Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230512012055.2990472-1-luyun_611@163.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 1 + drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 1ba66b8f70c9..cda3b802e89d 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1346,6 +1346,7 @@ struct rtl8xxxu_priv { u32 rege9c; u32 regeb4; u32 regebc; + u32 regrcr; int next_mbox; int nr_out_eps; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 95c0150f2356..3a9fa3ff37ac 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4049,6 +4049,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) RCR_ACCEPT_MGMT_FRAME | RCR_HTC_LOC_CTRL | RCR_APPEND_PHYSTAT | RCR_APPEND_ICV | RCR_APPEND_MIC; rtl8xxxu_write32(priv, REG_RCR, val32); + priv->regrcr = val32; /* * Accept all multicast @@ -6058,7 +6059,7 @@ static void rtl8xxxu_configure_filter(struct ieee80211_hw *hw, unsigned int *total_flags, u64 multicast) { struct rtl8xxxu_priv *priv = hw->priv; - u32 rcr = rtl8xxxu_read32(priv, REG_RCR); + u32 rcr = priv->regrcr; dev_dbg(&priv->udev->dev, "%s: changed_flags %08x, total_flags %08x\n", __func__, changed_flags, *total_flags); @@ -6104,6 +6105,7 @@ static void rtl8xxxu_configure_filter(struct ieee80211_hw *hw, */ rtl8xxxu_write32(priv, REG_RCR, rcr); + priv->regrcr = rcr; *total_flags &= (FIF_ALLMULTI | FIF_FCSFAIL | FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL | FIF_OTHER_BSS | FIF_PSPOLL | -- cgit 1.4.1 From e0b5316e2eada7ec583a9db93e279d6f4e2a30a1 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Thu, 27 Apr 2023 22:45:38 +0200 Subject: ARM: dts: stm32: add pin map for CAN controller on stm32f7 [ Upstream commit 011644249686f2675e142519cd59e81e04cfc231 ] Add pin configurations for using CAN controller on stm32f7. Signed-off-by: Dario Binacchi Link: https://lore.kernel.org/all/20230427204540.3126234-4-dario.binacchi@amarulasolutions.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- arch/arm/boot/dts/stm32f7-pinctrl.dtsi | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/arch/arm/boot/dts/stm32f7-pinctrl.dtsi b/arch/arm/boot/dts/stm32f7-pinctrl.dtsi index 8f37aefa7315..000278ec2c58 100644 --- a/arch/arm/boot/dts/stm32f7-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32f7-pinctrl.dtsi @@ -284,6 +284,88 @@ slew-rate = <2>; }; }; + + can1_pins_a: can1-0 { + pins1 { + pinmux = ; /* CAN1_TX */ + }; + pins2 { + pinmux = ; /* CAN1_RX */ + bias-pull-up; + }; + }; + + can1_pins_b: can1-1 { + pins1 { + pinmux = ; /* CAN1_TX */ + }; + pins2 { + pinmux = ; /* CAN1_RX */ + bias-pull-up; + }; + }; + + can1_pins_c: can1-2 { + pins1 { + pinmux = ; /* CAN1_TX */ + }; + pins2 { + pinmux = ; /* CAN1_RX */ + bias-pull-up; + + }; + }; + + can1_pins_d: can1-3 { + pins1 { + pinmux = ; /* CAN1_TX */ + }; + pins2 { + pinmux = ; /* CAN1_RX */ + bias-pull-up; + + }; + }; + + can2_pins_a: can2-0 { + pins1 { + pinmux = ; /* CAN2_TX */ + }; + pins2 { + pinmux = ; /* CAN2_RX */ + bias-pull-up; + }; + }; + + can2_pins_b: can2-1 { + pins1 { + pinmux = ; /* CAN2_TX */ + }; + pins2 { + pinmux = ; /* CAN2_RX */ + bias-pull-up; + }; + }; + + can3_pins_a: can3-0 { + pins1 { + pinmux = ; /* CAN3_TX */ + }; + pins2 { + pinmux = ; /* CAN3_RX */ + bias-pull-up; + }; + }; + + can3_pins_b: can3-1 { + pins1 { + pinmux = ; /* CAN3_TX */ + }; + pins2 { + pinmux = ; /* CAN3_RX */ + bias-pull-up; + }; + }; }; }; }; -- cgit 1.4.1 From 6bf0f6bfcd35afb1a8d2b612bf061edbcb51d6b5 Mon Sep 17 00:00:00 2001 From: Min-Hua Chen Date: Tue, 2 May 2023 23:19:06 +0800 Subject: arm64/mm: mark private VM_FAULT_X defines as vm_fault_t [ Upstream commit d91d580878064b880f3574ac35b98d8b70ee8620 ] This patch fixes several sparse warnings for fault.c: arch/arm64/mm/fault.c:493:24: sparse: warning: incorrect type in return expression (different base types) arch/arm64/mm/fault.c:493:24: sparse: expected restricted vm_fault_t arch/arm64/mm/fault.c:493:24: sparse: got int arch/arm64/mm/fault.c:501:32: sparse: warning: incorrect type in return expression (different base types) arch/arm64/mm/fault.c:501:32: sparse: expected restricted vm_fault_t arch/arm64/mm/fault.c:501:32: sparse: got int arch/arm64/mm/fault.c:503:32: sparse: warning: incorrect type in return expression (different base types) arch/arm64/mm/fault.c:503:32: sparse: expected restricted vm_fault_t arch/arm64/mm/fault.c:503:32: sparse: got int arch/arm64/mm/fault.c:511:24: sparse: warning: incorrect type in return expression (different base types) arch/arm64/mm/fault.c:511:24: sparse: expected restricted vm_fault_t arch/arm64/mm/fault.c:511:24: sparse: got int arch/arm64/mm/fault.c:670:13: sparse: warning: restricted vm_fault_t degrades to integer arch/arm64/mm/fault.c:670:13: sparse: warning: restricted vm_fault_t degrades to integer arch/arm64/mm/fault.c:713:39: sparse: warning: restricted vm_fault_t degrades to integer Reported-by: kernel test robot Signed-off-by: Min-Hua Chen Link: https://lore.kernel.org/r/20230502151909.128810-1-minhuadotchen@gmail.com Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/mm/fault.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 4ee20280133e..2fef1fa93e7b 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -480,8 +480,8 @@ static void do_bad_area(unsigned long far, unsigned long esr, } } -#define VM_FAULT_BADMAP 0x010000 -#define VM_FAULT_BADACCESS 0x020000 +#define VM_FAULT_BADMAP ((__force vm_fault_t)0x010000) +#define VM_FAULT_BADACCESS ((__force vm_fault_t)0x020000) static vm_fault_t __do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int mm_flags, unsigned long vm_flags, -- cgit 1.4.1 From bdd97c99b346db4960c552e5ca45e8f2b747e94b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 10 May 2023 08:48:11 +0200 Subject: arm64: vdso: Pass (void *) to virt_to_page() [ Upstream commit b0abde80620f42d1ceb3de5e4c1a49cdd5628229 ] Like the other calls in this function virt_to_page() expects a pointer, not an integer. However since many architectures implement virt_to_pfn() as a macro, this function becomes polymorphic and accepts both a (unsigned long) and a (void *). Fix this up with an explicit cast. Signed-off-by: Linus Walleij Link: http://lists.infradead.org/pipermail/linux-arm-kernel/2023-May/832583.html Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/kernel/vdso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 99ae81ab91a7..6ebb8dea5f09 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -312,7 +312,7 @@ static int aarch32_alloc_kuser_vdso_page(void) memcpy((void *)(vdso_page + 0x1000 - kuser_sz), __kuser_helper_start, kuser_sz); - aarch32_vectors_page = virt_to_page(vdso_page); + aarch32_vectors_page = virt_to_page((void *)vdso_page); return 0; } -- cgit 1.4.1 From aefa37aa32cb4af9d3d24c469bdbe2b9cc083f61 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 4 May 2023 16:45:02 +0300 Subject: wifi: mac80211: simplify chanctx allocation [ Upstream commit 860e1b43da94551cd1e73adc36b3c64cc3e5dc01 ] There's no need to call ieee80211_recalc_chanctx_min_def() since it cannot and won't call the driver anyway; just use _ieee80211_recalc_chanctx_min_def() instead. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230504134511.828474-3-gregory.greenman@intel.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/chan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index e72cf0749d49..76c6decb0762 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -638,7 +638,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local, ctx->conf.rx_chains_dynamic = 1; ctx->mode = mode; ctx->conf.radar_enabled = false; - ieee80211_recalc_chanctx_min_def(local, ctx); + _ieee80211_recalc_chanctx_min_def(local, ctx); return ctx; } -- cgit 1.4.1 From a0346006116463fd66a482e707756ffb691f14ca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 4 May 2023 16:45:03 +0300 Subject: wifi: mac80211: consider reserved chanctx for mindef [ Upstream commit b72a455a2409fd94d6d9b4eb51d659a88213243b ] When a chanctx is reserved for a new vif and we recalculate the minimal definition for it, we need to consider the new interface it's being reserved for before we assign it, so it can be used directly with the correct min channel width. Fix the code to - optionally - consider that, and use that option just before doing the reassignment. Also, when considering channel context reservations, we should only consider the one link we're currently working with. Change the boolean argument to a link pointer to do that. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230504134511.828474-4-gregory.greenman@intel.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/chan.c | 72 ++++++++++++++++++++++++++++------------------ net/mac80211/ieee80211_i.h | 3 +- net/mac80211/util.c | 2 +- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 76c6decb0762..c5d345e53056 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -258,7 +258,8 @@ ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata, static enum nl80211_chan_width ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, - struct ieee80211_chanctx_conf *conf) + struct ieee80211_chanctx *ctx, + struct ieee80211_link_data *rsvd_for) { enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; struct ieee80211_vif *vif = &sdata->vif; @@ -267,13 +268,14 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; - struct ieee80211_bss_conf *link_conf = - rcu_dereference(sdata->vif.link_conf[link_id]); + struct ieee80211_link_data *link = + rcu_dereference(sdata->link[link_id]); - if (!link_conf) + if (!link) continue; - if (rcu_access_pointer(link_conf->chanctx_conf) != conf) + if (link != rsvd_for && + rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf) continue; switch (vif->type) { @@ -287,7 +289,7 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, * point, so take the width from the chandef, but * account also for TDLS peers */ - width = max(link_conf->chandef.width, + width = max(link->conf->chandef.width, ieee80211_get_max_required_bw(sdata, link_id)); break; case NL80211_IFTYPE_P2P_DEVICE: @@ -296,7 +298,7 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: - width = link_conf->chandef.width; + width = link->conf->chandef.width; break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: @@ -316,7 +318,8 @@ ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, static enum nl80211_chan_width ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, - struct ieee80211_chanctx_conf *conf) + struct ieee80211_chanctx *ctx, + struct ieee80211_link_data *rsvd_for) { struct ieee80211_sub_if_data *sdata; enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; @@ -328,7 +331,8 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, if (!ieee80211_sdata_running(sdata)) continue; - width = ieee80211_get_chanctx_vif_max_required_bw(sdata, conf); + width = ieee80211_get_chanctx_vif_max_required_bw(sdata, ctx, + rsvd_for); max_bw = max(max_bw, width); } @@ -336,8 +340,8 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, /* use the configured bandwidth in case of monitor interface */ sdata = rcu_dereference(local->monitor_sdata); if (sdata && - rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf) - max_bw = max(max_bw, conf->def.width); + rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &ctx->conf) + max_bw = max(max_bw, ctx->conf.def.width); rcu_read_unlock(); @@ -349,8 +353,10 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, * the max of min required widths of all the interfaces bound to this * channel context. */ -static u32 _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx) +static u32 +_ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + struct ieee80211_link_data *rsvd_for) { enum nl80211_chan_width max_bw; struct cfg80211_chan_def min_def; @@ -370,7 +376,7 @@ static u32 _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, return 0; } - max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); + max_bw = ieee80211_get_chanctx_max_required_bw(local, ctx, rsvd_for); /* downgrade chandef up to max_bw */ min_def = ctx->conf.def; @@ -448,9 +454,10 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local, * channel context. */ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx) + struct ieee80211_chanctx *ctx, + struct ieee80211_link_data *rsvd_for) { - u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx); + u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for); if (!changed) return; @@ -464,10 +471,11 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, ieee80211_chan_bw_change(local, ctx, false); } -static void ieee80211_change_chanctx(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx, - struct ieee80211_chanctx *old_ctx, - const struct cfg80211_chan_def *chandef) +static void _ieee80211_change_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + struct ieee80211_chanctx *old_ctx, + const struct cfg80211_chan_def *chandef, + struct ieee80211_link_data *rsvd_for) { u32 changed; @@ -492,7 +500,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, ieee80211_chan_bw_change(local, old_ctx, true); if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) { - ieee80211_recalc_chanctx_min_def(local, ctx); + ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for); return; } @@ -502,7 +510,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, /* check if min chanctx also changed */ changed = IEEE80211_CHANCTX_CHANGE_WIDTH | - _ieee80211_recalc_chanctx_min_def(local, ctx); + _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for); drv_change_chanctx(local, ctx, changed); if (!local->use_chanctx) { @@ -514,6 +522,14 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, ieee80211_chan_bw_change(local, old_ctx, false); } +static void ieee80211_change_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + struct ieee80211_chanctx *old_ctx, + const struct cfg80211_chan_def *chandef) +{ + _ieee80211_change_chanctx(local, ctx, old_ctx, chandef, NULL); +} + static struct ieee80211_chanctx * ieee80211_find_chanctx(struct ieee80211_local *local, const struct cfg80211_chan_def *chandef, @@ -638,7 +654,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local, ctx->conf.rx_chains_dynamic = 1; ctx->mode = mode; ctx->conf.radar_enabled = false; - _ieee80211_recalc_chanctx_min_def(local, ctx); + _ieee80211_recalc_chanctx_min_def(local, ctx, NULL); return ctx; } @@ -873,12 +889,12 @@ out: ieee80211_recalc_chanctx_chantype(local, curr_ctx); ieee80211_recalc_smps_chanctx(local, curr_ctx); ieee80211_recalc_radar_chanctx(local, curr_ctx); - ieee80211_recalc_chanctx_min_def(local, curr_ctx); + ieee80211_recalc_chanctx_min_def(local, curr_ctx, NULL); } if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { ieee80211_recalc_txpower(sdata, false); - ieee80211_recalc_chanctx_min_def(local, new_ctx); + ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL); } if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && @@ -1270,7 +1286,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) ieee80211_link_update_chandef(link, &link->reserved_chandef); - ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef); + _ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef, link); vif_chsw[0].vif = &sdata->vif; vif_chsw[0].old_ctx = &old_ctx->conf; @@ -1300,7 +1316,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) if (ieee80211_chanctx_refcount(local, old_ctx) == 0) ieee80211_free_chanctx(local, old_ctx); - ieee80211_recalc_chanctx_min_def(local, new_ctx); + ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL); ieee80211_recalc_smps_chanctx(local, new_ctx); ieee80211_recalc_radar_chanctx(local, new_ctx); @@ -1665,7 +1681,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) ieee80211_recalc_chanctx_chantype(local, ctx); ieee80211_recalc_smps_chanctx(local, ctx); ieee80211_recalc_radar_chanctx(local, ctx); - ieee80211_recalc_chanctx_min_def(local, ctx); + ieee80211_recalc_chanctx_min_def(local, ctx, NULL); list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links, reserved_chanctx_list) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e57001e00a3d..27479bbb093a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2475,7 +2475,8 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local, void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx); void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx); + struct ieee80211_chanctx *ctx, + struct ieee80211_link_data *rsvd_for); bool ieee80211_is_radar_required(struct ieee80211_local *local); void ieee80211_dfs_cac_timer(unsigned long data); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0785d9393e71..784b9ba61581 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2899,7 +2899,7 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata, chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); - ieee80211_recalc_chanctx_min_def(local, chanctx); + ieee80211_recalc_chanctx_min_def(local, chanctx, NULL); } unlock: mutex_unlock(&local->chanctx_mtx); -- cgit 1.4.1 From 788f129f6370eab75ecf7abbec5f8a81e5b73d2c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 4 May 2023 16:45:04 +0300 Subject: wifi: mac80211: recalc chanctx mindef before assigning [ Upstream commit 04312de4ced4b152749614e8179f3978a20a992f ] When we allocate a new channel context, or find an existing one that is compatible, we currently assign it to a link before its mindef is updated. This leads to strange situations, especially in link switching where you switch to an 80 MHz link and expect it to be active immediately, but the mindef is still configured to 20 MHz while assigning. Also, it's strange that the chandef passed to the assign method's argument is wider than the one in the context. Fix this by calculating the mindef with the new link considered before calling the driver. In particular, this fixes an iwlwifi problem during link switch where the firmware would assert because the (link) station that was added for the AP is configured to transmit at a bandwidth that's wider than the channel context that it's configured on. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230504134511.828474-5-gregory.greenman@intel.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/chan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index c5d345e53056..f07e34bed8f3 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -871,6 +871,9 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link, } if (new_ctx) { + /* recalc considering the link we'll use it for now */ + ieee80211_recalc_chanctx_min_def(local, new_ctx, link); + ret = drv_assign_vif_chanctx(local, sdata, link->conf, new_ctx); if (ret) goto out; -- cgit 1.4.1 From 7402fb48efff67be21392e81df8a6c51bc491c6f Mon Sep 17 00:00:00 2001 From: Ariel Malamud Date: Sun, 14 May 2023 12:15:55 +0300 Subject: wifi: iwlwifi: mvm: Add locking to the rate read flow [ Upstream commit a8938bc881d2a03f9b77f19fae924fe798a01285 ] The rs_drv_get_rate flow reads the lq_sta to return the optimal rate for tx frames. This read flow is not protected thereby leaving a small window, a few instructions wide, open to contention by an asynchronous rate update. Indeed this race condition was hit and the update occurred in the middle of the read. Fix this by locking the lq_sta struct during read. Signed-off-by: Ariel Malamud Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230514120631.b52c9ed5c379.I15290b78e0d966c1b68278263776ca9de841d5fe@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 0b50b816684a..2be6801d48ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -2692,6 +2692,8 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, return; lq_sta = mvm_sta; + + spin_lock(&lq_sta->pers.lock); iwl_mvm_hwrate_to_tx_rate_v1(lq_sta->last_rate_n_flags, info->band, &info->control.rates[0]); info->control.rates[0].count = 1; @@ -2706,6 +2708,7 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, iwl_mvm_hwrate_to_tx_rate_v1(last_ucode_rate, info->band, &txrc->reported_rate); } + spin_unlock(&lq_sta->pers.lock); } static void *rs_drv_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, -- cgit 1.4.1 From c061e13c729797c114f099c29cefcdd472077e2a Mon Sep 17 00:00:00 2001 From: Wenchao Hao Date: Mon, 15 May 2023 15:01:56 +0800 Subject: scsi: core: Decrease scsi_device's iorequest_cnt if dispatch failed [ Upstream commit 09e797c8641f6ad435c33ae24c223351197ea29a ] If scsi_dispatch_cmd() failed, the SCSI command was not sent to the target, scsi_queue_rq() would return BLK_STS_RESOURCE and the related request would be requeued. The timeout of this request would not fire, no one would increase iodone_cnt. The above flow would result the iodone_cnt smaller than iorequest_cnt. So decrease the iorequest_cnt if dispatch failed to workaround the issue. Signed-off-by: Wenchao Hao Reported-by: Ming Lei Closes: https://lore.kernel.org/r/ZF+zB+bB7iqe0wGd@ovpn-8-17.pek2.redhat.com Link: https://lore.kernel.org/r/20230515070156.1790181-3-haowenchao2@huawei.com Reviewed-by: Ming Lei Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/scsi_lib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 8b89fab7c420..fb6e9a7a7f58 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1491,6 +1491,7 @@ static int scsi_dispatch_cmd(struct scsi_cmnd *cmd) */ SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd, "queuecommand : device blocked\n")); + atomic_dec(&cmd->device->iorequest_cnt); return SCSI_MLQUEUE_DEVICE_BUSY; } @@ -1523,6 +1524,7 @@ static int scsi_dispatch_cmd(struct scsi_cmnd *cmd) trace_scsi_dispatch_cmd_start(cmd); rtn = host->hostt->queuecommand(host, cmd); if (rtn) { + atomic_dec(&cmd->device->iorequest_cnt); trace_scsi_dispatch_cmd_error(cmd, rtn); if (rtn != SCSI_MLQUEUE_DEVICE_BUSY && rtn != SCSI_MLQUEUE_TARGET_BUSY) -- cgit 1.4.1 From ae72bd1a4b045595e94ea8a8cc45d60ddd4b6d92 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 20:34:22 +0200 Subject: wifi: b43: fix incorrect __packed annotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 212457ccbd60dba34f965e4ffbe62f0e4f970538 ] clang warns about an unpacked structure inside of a packed one: drivers/net/wireless/broadcom/b43/b43.h:654:4: error: field data within 'struct b43_iv' is less aligned than 'union (unnamed union at /home/arnd/arm-soc/drivers/net/wireless/broadcom/b43/b43.h:651:2)' and is usually due to 'struct b43_iv' being packed, which can lead to unaligned accesses [-Werror,-Wunaligned-access] The problem here is that the anonymous union has the default alignment from its members, apparently because the original author mixed up the placement of the __packed attribute by placing it next to the struct member rather than the union definition. As the struct itself is also marked as __packed, there is no need to mark its members, so just move the annotation to the inner type instead. As Michael noted, the same problem is present in b43legacy, so change both at the same time. Acked-by: Michael Büsch Reported-by: kernel test robot Reviewed-by: Simon Horman Tested-by: Larry Finger Link: https://lore.kernel.org/oe-kbuild-all/202305160749.ay1HAoyP-lkp@intel.com/ Signed-off-by: Arnd Bergmann Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230516183442.536589-1-arnd@kernel.org Signed-off-by: Sasha Levin --- drivers/net/wireless/broadcom/b43/b43.h | 2 +- drivers/net/wireless/broadcom/b43legacy/b43legacy.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/b43/b43.h b/drivers/net/wireless/broadcom/b43/b43.h index 9fc7c088a539..67b4bac048e5 100644 --- a/drivers/net/wireless/broadcom/b43/b43.h +++ b/drivers/net/wireless/broadcom/b43/b43.h @@ -651,7 +651,7 @@ struct b43_iv { union { __be16 d16; __be32 d32; - } data __packed; + } __packed data; } __packed; diff --git a/drivers/net/wireless/broadcom/b43legacy/b43legacy.h b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h index 6b0cec467938..f49365d14619 100644 --- a/drivers/net/wireless/broadcom/b43legacy/b43legacy.h +++ b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h @@ -379,7 +379,7 @@ struct b43legacy_iv { union { __be16 d16; __be32 d32; - } data __packed; + } __packed data; } __packed; #define B43legacy_PHYMODE(phytype) (1 << (phytype)) -- cgit 1.4.1 From def67e27f2b7ae3496236aa080fdc45aa9759512 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Wed, 17 May 2023 13:24:51 +0800 Subject: net: wwan: t7xx: Ensure init is completed before system sleep [ Upstream commit ab87603b251134441a67385ecc9d3371be17b7a7 ] When the system attempts to sleep while mtk_t7xx is not ready, the driver cannot put the device to sleep: [ 12.472918] mtk_t7xx 0000:57:00.0: [PM] Exiting suspend, modem in invalid state [ 12.472936] mtk_t7xx 0000:57:00.0: PM: pci_pm_suspend(): t7xx_pci_pm_suspend+0x0/0x20 [mtk_t7xx] returns -14 [ 12.473678] mtk_t7xx 0000:57:00.0: PM: dpm_run_callback(): pci_pm_suspend+0x0/0x1b0 returns -14 [ 12.473711] mtk_t7xx 0000:57:00.0: PM: failed to suspend async: error -14 [ 12.764776] PM: Some devices failed to suspend, or early wake event detected Mediatek confirmed the device can take a rather long time to complete its initialization, so wait for up to 20 seconds until init is done. Signed-off-by: Kai-Heng Feng Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/wwan/t7xx/t7xx_pci.c | 18 ++++++++++++++++++ drivers/net/wwan/t7xx/t7xx_pci.h | 1 + 2 files changed, 19 insertions(+) diff --git a/drivers/net/wwan/t7xx/t7xx_pci.c b/drivers/net/wwan/t7xx/t7xx_pci.c index 226fc1703e90..91256e005b84 100644 --- a/drivers/net/wwan/t7xx/t7xx_pci.c +++ b/drivers/net/wwan/t7xx/t7xx_pci.c @@ -45,6 +45,7 @@ #define T7XX_PCI_IREG_BASE 0 #define T7XX_PCI_EREG_BASE 2 +#define T7XX_INIT_TIMEOUT 20 #define PM_SLEEP_DIS_TIMEOUT_MS 20 #define PM_ACK_TIMEOUT_MS 1500 #define PM_AUTOSUSPEND_MS 20000 @@ -96,6 +97,7 @@ static int t7xx_pci_pm_init(struct t7xx_pci_dev *t7xx_dev) spin_lock_init(&t7xx_dev->md_pm_lock); init_completion(&t7xx_dev->sleep_lock_acquire); init_completion(&t7xx_dev->pm_sr_ack); + init_completion(&t7xx_dev->init_done); atomic_set(&t7xx_dev->md_pm_state, MTK_PM_INIT); device_init_wakeup(&pdev->dev, true); @@ -124,6 +126,7 @@ void t7xx_pci_pm_init_late(struct t7xx_pci_dev *t7xx_dev) pm_runtime_mark_last_busy(&t7xx_dev->pdev->dev); pm_runtime_allow(&t7xx_dev->pdev->dev); pm_runtime_put_noidle(&t7xx_dev->pdev->dev); + complete_all(&t7xx_dev->init_done); } static int t7xx_pci_pm_reinit(struct t7xx_pci_dev *t7xx_dev) @@ -529,6 +532,20 @@ static void t7xx_pci_shutdown(struct pci_dev *pdev) __t7xx_pci_pm_suspend(pdev); } +static int t7xx_pci_pm_prepare(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct t7xx_pci_dev *t7xx_dev; + + t7xx_dev = pci_get_drvdata(pdev); + if (!wait_for_completion_timeout(&t7xx_dev->init_done, T7XX_INIT_TIMEOUT * HZ)) { + dev_warn(dev, "Not ready for system sleep.\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int t7xx_pci_pm_suspend(struct device *dev) { return __t7xx_pci_pm_suspend(to_pci_dev(dev)); @@ -555,6 +572,7 @@ static int t7xx_pci_pm_runtime_resume(struct device *dev) } static const struct dev_pm_ops t7xx_pci_pm_ops = { + .prepare = t7xx_pci_pm_prepare, .suspend = t7xx_pci_pm_suspend, .resume = t7xx_pci_pm_resume, .resume_noirq = t7xx_pci_pm_resume_noirq, diff --git a/drivers/net/wwan/t7xx/t7xx_pci.h b/drivers/net/wwan/t7xx/t7xx_pci.h index 50b37056ce5a..5dffe24ef37b 100644 --- a/drivers/net/wwan/t7xx/t7xx_pci.h +++ b/drivers/net/wwan/t7xx/t7xx_pci.h @@ -69,6 +69,7 @@ struct t7xx_pci_dev { struct t7xx_modem *md; struct t7xx_ccmni_ctrl *ccmni_ctlb; bool rgu_pci_irq_en; + struct completion init_done; /* Low Power Items */ struct list_head md_pm_entities; -- cgit 1.4.1 From d0013470677f3e907ccdbb10ee4a4dce5156358a Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 14 May 2023 10:00:10 -0400 Subject: netfilter: conntrack: define variables exp_nat_nla_policy and any_addr with CONFIG_NF_NAT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 224a876e37543eee111bf9b6aa4935080e619335 ] gcc with W=1 and ! CONFIG_NF_NAT net/netfilter/nf_conntrack_netlink.c:3463:32: error: ‘exp_nat_nla_policy’ defined but not used [-Werror=unused-const-variable=] 3463 | static const struct nla_policy exp_nat_nla_policy[CTA_EXPECT_NAT_MAX+1] = { | ^~~~~~~~~~~~~~~~~~ net/netfilter/nf_conntrack_netlink.c:2979:33: error: ‘any_addr’ defined but not used [-Werror=unused-const-variable=] 2979 | static const union nf_inet_addr any_addr; | ^~~~~~~~ These variables use is controlled by CONFIG_NF_NAT, so should their definitions. Signed-off-by: Tom Rix Reviewed-by: Simon Horman Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_netlink.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 857cddd9d82e..9ee8abd3e4b1 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2976,7 +2976,9 @@ nla_put_failure: return -1; } +#if IS_ENABLED(CONFIG_NF_NAT) static const union nf_inet_addr any_addr; +#endif static __be32 nf_expect_get_id(const struct nf_conntrack_expect *exp) { @@ -3460,10 +3462,12 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x, return 0; } +#if IS_ENABLED(CONFIG_NF_NAT) static const struct nla_policy exp_nat_nla_policy[CTA_EXPECT_NAT_MAX+1] = { [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 }, [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED }, }; +#endif static int ctnetlink_parse_expect_nat(const struct nlattr *attr, -- cgit 1.4.1 From f481c2af49169d4db27c54350fd8aa007458e3ee Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 17 May 2023 09:53:45 +0200 Subject: nvme-multipath: don't call blk_mark_disk_dead in nvme_mpath_remove_disk [ Upstream commit 1743e5f6000901a11f4e1cd741bfa9136f3ec9b1 ] nvme_mpath_remove_disk is called after del_gendisk, at which point a blk_mark_disk_dead call doesn't make any sense. Signed-off-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/multipath.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index d09ed0070174..f96d330d3964 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -858,7 +858,6 @@ void nvme_mpath_remove_disk(struct nvme_ns_head *head) { if (!head->disk) return; - blk_mark_disk_dead(head->disk); /* make sure all pending bios are cleaned up */ kblockd_schedule_work(&head->requeue_work); flush_work(&head->requeue_work); -- cgit 1.4.1 From e4f1532a9cd9ef366f2eb70b4494fb94f641f117 Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Thu, 11 May 2023 13:07:41 +0200 Subject: nvme: do not let the user delete a ctrl before a complete initialization [ Upstream commit 2eb94dd56a4a4e3fe286def3e2ba207804a37345 ] If a userspace application performes a "delete_controller" command early during the ctrl initialization, the delete operation may race against the init code and the kernel will crash. nvme nvme5: Connect command failed: host path error nvme nvme5: failed to connect queue: 0 ret=880 PF: supervisor write access in kernel mode PF: error_code(0x0002) - not-present page blk_mq_quiesce_queue+0x18/0x90 nvme_tcp_delete_ctrl+0x24/0x40 [nvme_tcp] nvme_do_delete_ctrl+0x7f/0x8b [nvme_core] nvme_sysfs_delete.cold+0x8/0xd [nvme_core] kernfs_fop_write_iter+0x124/0x1b0 new_sync_write+0xff/0x190 vfs_write+0x1ef/0x280 Fix the crash by checking the NVME_CTRL_STARTED_ONCE bit; if it's not set it means that the nvme controller is still in the process of getting initialized and the kernel will return an -EBUSY error to userspace. Set the NVME_CTRL_STARTED_ONCE later in the nvme_start_ctrl() function, after the controller start operation is completed. Signed-off-by: Maurizio Lombardi Reviewed-by: Sagi Grimberg Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index f502e032e7e4..2e22c78991cc 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3540,6 +3540,9 @@ static ssize_t nvme_sysfs_delete(struct device *dev, { struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + if (!test_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags)) + return -EBUSY; + if (device_remove_file_self(dev, attr)) nvme_delete_ctrl_sync(ctrl); return count; @@ -4980,7 +4983,7 @@ void nvme_start_ctrl(struct nvme_ctrl *ctrl) * that were missed. We identify persistent discovery controllers by * checking that they started once before, hence are reconnecting back. */ - if (test_and_set_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags) && + if (test_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags) && nvme_discovery_ctrl(ctrl)) nvme_change_uevent(ctrl, "NVME_EVENT=rediscover"); @@ -4991,6 +4994,7 @@ void nvme_start_ctrl(struct nvme_ctrl *ctrl) } nvme_change_uevent(ctrl, "NVME_EVENT=connected"); + set_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags); } EXPORT_SYMBOL_GPL(nvme_start_ctrl); -- cgit 1.4.1 From c62a9a6bea0c9001ade64fa4cff147db40fd3678 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 21:50:42 +0200 Subject: ALSA: oss: avoid missing-prototype warnings [ Upstream commit 040b5a046a9e18098580d3ccd029e2318fca7859 ] Two functions are defined and used in pcm_oss.c but also optionally used from io.c, with an optional prototype. If CONFIG_SND_PCM_OSS_PLUGINS is disabled, this causes a warning as the functions are not static and have no prototype: sound/core/oss/pcm_oss.c:1235:19: error: no previous prototype for 'snd_pcm_oss_write3' [-Werror=missing-prototypes] sound/core/oss/pcm_oss.c:1266:19: error: no previous prototype for 'snd_pcm_oss_read3' [-Werror=missing-prototypes] Avoid this by making the prototypes unconditional. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230516195046.550584-2-arnd@kernel.org Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/oss/pcm_plugin.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index 46e273bd4a78..50a6b50f5db4 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -141,6 +141,14 @@ int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel, void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size); void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr); +#else + +static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; } +static inline snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size) { return clt_size; } +static inline int snd_pcm_plug_slave_format(int format, const struct snd_mask *format_mask) { return format; } + +#endif + snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel); @@ -151,14 +159,6 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames); -#else - -static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; } -static inline snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size) { return clt_size; } -static inline int snd_pcm_plug_slave_format(int format, const struct snd_mask *format_mask) { return format; } - -#endif - #ifdef PLUGIN_DEBUG #define pdprintf(fmt, args...) printk(KERN_DEBUG "plugin: " fmt, ##args) #else -- cgit 1.4.1 From 47d0f626795f33610fed09b6b80de499b13bf3d5 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 16 May 2023 15:20:37 -0700 Subject: drm/msm: Be more shouty if per-process pgtables aren't working [ Upstream commit 5c054db54c43a5fcb5cc81012361f5e3fac37637 ] Otherwise it is not always obvious if a dt or iommu change is causing us to fall back to global pgtable. Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/537359/ Link: https://lore.kernel.org/r/20230516222039.907690-2-robdclark@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/msm_iommu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 5577cea7c009..d12ba47b37c4 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -227,7 +227,12 @@ struct msm_mmu *msm_iommu_pagetable_create(struct msm_mmu *parent) /* Get the pagetable configuration from the domain */ if (adreno_smmu->cookie) ttbr1_cfg = adreno_smmu->get_ttbr1_cfg(adreno_smmu->cookie); - if (!ttbr1_cfg) + + /* + * If you hit this WARN_ONCE() you are probably missing an entry in + * qcom_smmu_impl_of_match[] in arm-smmu-qcom.c + */ + if (WARN_ONCE(!ttbr1_cfg, "No per-process page tables")) return ERR_PTR(-ENODEV); /* -- cgit 1.4.1 From 56e5d63e4e2df66db3308d96fc40fc7d79c938e7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 21:45:34 +0200 Subject: atm: hide unused procfs functions [ Upstream commit fb1b7be9b16c1f4626969ba4e95a97da2a452b41 ] When CONFIG_PROC_FS is disabled, the function declarations for some procfs functions are hidden, but the definitions are still build, as shown by this compiler warning: net/atm/resources.c:403:7: error: no previous prototype for 'atm_dev_seq_start' [-Werror=missing-prototypes] net/atm/resources.c:409:6: error: no previous prototype for 'atm_dev_seq_stop' [-Werror=missing-prototypes] net/atm/resources.c:414:7: error: no previous prototype for 'atm_dev_seq_next' [-Werror=missing-prototypes] Add another #ifdef to leave these out of the build. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230516194625.549249-2-arnd@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/atm/resources.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/atm/resources.c b/net/atm/resources.c index 2b2d33eeaf20..995d29e7fb13 100644 --- a/net/atm/resources.c +++ b/net/atm/resources.c @@ -400,6 +400,7 @@ done: return error; } +#ifdef CONFIG_PROC_FS void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos) { mutex_lock(&atm_dev_mutex); @@ -415,3 +416,4 @@ void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) { return seq_list_next(v, &atm_devs, pos); } +#endif -- cgit 1.4.1 From baa8901ad70daf6a892c72c9d24b59180e32f234 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 8 May 2023 14:45:01 +0800 Subject: ceph: silence smatch warning in reconnect_caps_cb() [ Upstream commit 9aaa7eb018661b2da221362d9bacb096bd596f52 ] Smatch static checker warning: fs/ceph/mds_client.c:3968 reconnect_caps_cb() warn: missing error code here? '__get_cap_for_mds()' failed. 'err' = '0' [ idryomov: Dan says that Smatch considers it intentional only if the "ret = 0;" assignment is within 4 or 5 lines of the goto. ] Reported-by: Dan Carpenter Signed-off-by: Xiubo Li Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov Signed-off-by: Sasha Levin --- fs/ceph/mds_client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 54e3c2ab21d2..1989c8deea55 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -3938,7 +3938,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) struct dentry *dentry; struct ceph_cap *cap; char *path; - int pathlen = 0, err = 0; + int pathlen = 0, err; u64 pathbase; u64 snap_follows; @@ -3961,6 +3961,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) cap = __get_cap_for_mds(ci, mds); if (!cap) { spin_unlock(&ci->i_ceph_lock); + err = 0; goto out_err; } dout(" adding %p ino %llx.%llx cap %p %lld %s\n", -- cgit 1.4.1 From 68ce1d57e52c6f88ffffb0d81b10650eafc8e540 Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Tue, 9 May 2023 16:15:27 +0800 Subject: drm/amdgpu: skip disabling fence driver src_irqs when device is unplugged [ Upstream commit c1a322a7a4a96cd0a3dde32ce37af437a78bf8cd ] When performing device unbind or halt, we have disabled all irqs at the very begining like amdgpu_pci_remove or amdgpu_device_halt. So amdgpu_irq_put for irqs stored in fence driver should not be called any more, otherwise, below calltrace will arrive. [ 139.114088] WARNING: CPU: 2 PID: 1550 at drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:616 amdgpu_irq_put+0xf6/0x110 [amdgpu] [ 139.114655] Call Trace: [ 139.114655] [ 139.114657] amdgpu_fence_driver_hw_fini+0x93/0x130 [amdgpu] [ 139.114836] amdgpu_device_fini_hw+0xb6/0x350 [amdgpu] [ 139.114955] amdgpu_driver_unload_kms+0x51/0x70 [amdgpu] [ 139.115075] amdgpu_pci_remove+0x63/0x160 [amdgpu] [ 139.115193] ? __pm_runtime_resume+0x64/0x90 [ 139.115195] pci_device_remove+0x3a/0xb0 [ 139.115197] device_remove+0x43/0x70 [ 139.115198] device_release_driver_internal+0xbd/0x140 Signed-off-by: Guchun Chen Acked-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c index 3cc1929285fc..ed6878d5b3ce 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c @@ -528,7 +528,8 @@ void amdgpu_fence_driver_hw_fini(struct amdgpu_device *adev) if (r) amdgpu_fence_driver_force_completion(ring); - if (ring->fence_drv.irq_src) + if (!drm_dev_is_unplugged(adev_to_drm(adev)) && + ring->fence_drv.irq_src) amdgpu_irq_put(adev, ring->fence_drv.irq_src, ring->fence_drv.irq_type); -- cgit 1.4.1 From a731273f3c22d6a22ac99c0d76a931e451d2250d Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 17 May 2023 21:34:08 +0800 Subject: ublk: fix AB-BA lockdep warning [ Upstream commit ac5902f84bb546c64aea02c439c2579cbf40318f ] When handling UBLK_IO_FETCH_REQ, ctx->uring_lock is grabbed first, then ub->mutex is acquired. When handling UBLK_CMD_STOP_DEV or UBLK_CMD_DEL_DEV, ub->mutex is grabbed first, then calling io_uring_cmd_done() for canceling uring command, in which ctx->uring_lock may be required. Real deadlock only happens when all the above commands are issued from same uring context, and in reality different uring contexts are often used for handing control command and IO command. Fix the issue by using io_uring_cmd_complete_in_task() to cancel command in ublk_cancel_dev(ublk_cancel_queue). Reported-by: Shinichiro Kawasaki Closes: https://lore.kernel.org/linux-block/becol2g7sawl4rsjq2dztsbc7mqypfqko6wzsyoyazqydoasml@rcxarzwidrhk Cc: Ziyang Zhang Signed-off-by: Ming Lei Tested-by: Shinichiro Kawasaki Link: https://lore.kernel.org/r/20230517133408.210944-1-ming.lei@redhat.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/block/ublk_drv.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index c0cbc5f3eb26..c56d1c6d8e58 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -1045,6 +1045,11 @@ static inline bool ublk_queue_ready(struct ublk_queue *ubq) return ubq->nr_io_ready == ubq->q_depth; } +static void ublk_cmd_cancel_cb(struct io_uring_cmd *cmd, unsigned issue_flags) +{ + io_uring_cmd_done(cmd, UBLK_IO_RES_ABORT, 0, issue_flags); +} + static void ublk_cancel_queue(struct ublk_queue *ubq) { int i; @@ -1056,8 +1061,8 @@ static void ublk_cancel_queue(struct ublk_queue *ubq) struct ublk_io *io = &ubq->ios[i]; if (io->flags & UBLK_IO_FLAG_ACTIVE) - io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, 0, - IO_URING_F_UNLOCKED); + io_uring_cmd_complete_in_task(io->cmd, + ublk_cmd_cancel_cb); } /* all io commands are canceled */ -- cgit 1.4.1 From 16ddd3bc67410da42311e288360535d01540baca Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Wed, 17 May 2023 14:32:32 -0700 Subject: nvme-pci: Add quirk for Teamgroup MP33 SSD [ Upstream commit 0649728123cf6a5518e154b4e1735fc85ea4f55c ] Add a quirk for Teamgroup MP33 that reports duplicate ids for disk. Signed-off-by: Daniel Smith [kch: patch formatting] Signed-off-by: Chaitanya Kulkarni Tested-by: Daniel Smith Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 1ec0ca40604a..ba4903c86f7f 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3558,6 +3558,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1e4b, 0x1602), /* HS-SSD-FUTURE 2048G */ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x10ec, 0x5765), /* TEAMGROUP MP33 2TB SSD */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(PCI_VENDOR_ID_AMAZON, 0x0061), .driver_data = NVME_QUIRK_DMA_ADDRESS_BITS_48, }, { PCI_DEVICE(PCI_VENDOR_ID_AMAZON, 0x0065), -- cgit 1.4.1 From 7df6008b872693b5aa7da2aa35a3935c175dbff6 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 10 May 2023 09:42:23 +0200 Subject: block: Deny writable memory mapping if block is read-only [ Upstream commit 69baa3a623fd2e58624f24f2f23d46f87b817c93 ] User should not be able to write block device if it is read-only at block level (e.g force_ro attribute). This is ensured in the regular fops write operation (blkdev_write_iter) but not when writing via user mapping (mmap), allowing user to actually write a read-only block device via a PROT_WRITE mapping. Example: This can lead to integrity issue of eMMC boot partition (e.g mmcblk0boot0) which is read-only by default. To fix this issue, simply deny shared writable mapping if the block is readonly. Note: Block remains writable if switch to read-only is performed after the initial mapping, but this is expected behavior according to commit a32e236eb93e ("Partially revert "block: fail op_is_write() requests to read-only partitions"")'. Signed-off-by: Loic Poulain Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230510074223.991297-1-loic.poulain@linaro.org Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/fops.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/block/fops.c b/block/fops.c index e406aa605327..6197d1c41652 100644 --- a/block/fops.c +++ b/block/fops.c @@ -685,6 +685,16 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start, return error; } +static int blkdev_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *bd_inode = bdev_file_inode(file); + + if (bdev_read_only(I_BDEV(bd_inode))) + return generic_file_readonly_mmap(file, vma); + + return generic_file_mmap(file, vma); +} + const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_close, @@ -692,7 +702,7 @@ const struct file_operations def_blk_fops = { .read_iter = blkdev_read_iter, .write_iter = blkdev_write_iter, .iopoll = iocb_bio_iopoll, - .mmap = generic_file_mmap, + .mmap = blkdev_mmap, .fsync = blkdev_fsync, .unlocked_ioctl = blkdev_ioctl, #ifdef CONFIG_COMPAT -- cgit 1.4.1 From 4129d71e5bda3a904933058478104662423b88be Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Thu, 18 May 2023 11:09:15 +0100 Subject: KVM: arm64: vgic: Fix a circular locking issue [ Upstream commit 59112e9c390be595224e427827475a6cd3726021 ] Lockdep reports a circular lock dependency between the srcu and the config_lock: [ 262.179917] -> #1 (&kvm->srcu){.+.+}-{0:0}: [ 262.182010] __synchronize_srcu+0xb0/0x224 [ 262.183422] synchronize_srcu_expedited+0x24/0x34 [ 262.184554] kvm_io_bus_register_dev+0x324/0x50c [ 262.185650] vgic_register_redist_iodev+0x254/0x398 [ 262.186740] vgic_v3_set_redist_base+0x3b0/0x724 [ 262.188087] kvm_vgic_addr+0x364/0x600 [ 262.189189] vgic_set_common_attr+0x90/0x544 [ 262.190278] vgic_v3_set_attr+0x74/0x9c [ 262.191432] kvm_device_ioctl+0x2a0/0x4e4 [ 262.192515] __arm64_sys_ioctl+0x7ac/0x1ba8 [ 262.193612] invoke_syscall.constprop.0+0x70/0x1e0 [ 262.195006] do_el0_svc+0xe4/0x2d4 [ 262.195929] el0_svc+0x44/0x8c [ 262.196917] el0t_64_sync_handler+0xf4/0x120 [ 262.198238] el0t_64_sync+0x190/0x194 [ 262.199224] [ 262.199224] -> #0 (&kvm->arch.config_lock){+.+.}-{3:3}: [ 262.201094] __lock_acquire+0x2b70/0x626c [ 262.202245] lock_acquire+0x454/0x778 [ 262.203132] __mutex_lock+0x190/0x8b4 [ 262.204023] mutex_lock_nested+0x24/0x30 [ 262.205100] vgic_mmio_write_v3_misc+0x5c/0x2a0 [ 262.206178] dispatch_mmio_write+0xd8/0x258 [ 262.207498] __kvm_io_bus_write+0x1e0/0x350 [ 262.208582] kvm_io_bus_write+0xe0/0x1cc [ 262.209653] io_mem_abort+0x2ac/0x6d8 [ 262.210569] kvm_handle_guest_abort+0x9b8/0x1f88 [ 262.211937] handle_exit+0xc4/0x39c [ 262.212971] kvm_arch_vcpu_ioctl_run+0x90c/0x1c04 [ 262.214154] kvm_vcpu_ioctl+0x450/0x12f8 [ 262.215233] __arm64_sys_ioctl+0x7ac/0x1ba8 [ 262.216402] invoke_syscall.constprop.0+0x70/0x1e0 [ 262.217774] do_el0_svc+0xe4/0x2d4 [ 262.218758] el0_svc+0x44/0x8c [ 262.219941] el0t_64_sync_handler+0xf4/0x120 [ 262.221110] el0t_64_sync+0x190/0x194 Note that the current report, which can be triggered by the vgic_irq kselftest, is a triple chain that includes slots_lock, but after inverting the slots_lock/config_lock dependency, the actual problem reported above remains. In several places, the vgic code calls kvm_io_bus_register_dev(), which synchronizes the srcu, while holding config_lock (#1). And the MMIO handler takes the config_lock while holding the srcu read lock (#0). Break dependency #1, by registering the distributor and redistributors without holding config_lock. The ITS also uses kvm_io_bus_register_dev() but already relies on slots_lock to serialize calls. The distributor iodev is created on the first KVM_RUN call. Multiple threads will race for vgic initialization, and only the first one will see !vgic_ready() under the lock. To serialize those threads, rely on slots_lock rather than config_lock. Redistributors are created earlier, through KVM_DEV_ARM_VGIC_GRP_ADDR ioctls and vCPU creation. Similarly, serialize the iodev creation with slots_lock, and the rest with config_lock. Fixes: f00327731131 ("KVM: arm64: Use config_lock to protect vgic state") Signed-off-by: Jean-Philippe Brucker Reviewed-by: Oliver Upton Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230518100914.2837292-2-jean-philippe@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/kvm/vgic/vgic-init.c | 25 ++++++++++++++++++++----- arch/arm64/kvm/vgic/vgic-kvm-device.c | 10 ++++++++-- arch/arm64/kvm/vgic/vgic-mmio-v3.c | 31 +++++++++++++++++++++---------- arch/arm64/kvm/vgic/vgic-mmio.c | 9 ++------- arch/arm64/kvm/vgic/vgic-v2.c | 6 ------ arch/arm64/kvm/vgic/vgic-v3.c | 7 ------- 6 files changed, 51 insertions(+), 37 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 8c1d2d7128db..37e74ca4dad8 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -235,9 +235,9 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) * KVM io device for the redistributor that belongs to this VCPU. */ if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { - mutex_lock(&vcpu->kvm->arch.config_lock); + mutex_lock(&vcpu->kvm->slots_lock); ret = vgic_register_redist_iodev(vcpu); - mutex_unlock(&vcpu->kvm->arch.config_lock); + mutex_unlock(&vcpu->kvm->slots_lock); } return ret; } @@ -446,11 +446,13 @@ int vgic_lazy_init(struct kvm *kvm) int kvm_vgic_map_resources(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; + gpa_t dist_base; int ret = 0; if (likely(vgic_ready(kvm))) return 0; + mutex_lock(&kvm->slots_lock); mutex_lock(&kvm->arch.config_lock); if (vgic_ready(kvm)) goto out; @@ -463,13 +465,26 @@ int kvm_vgic_map_resources(struct kvm *kvm) else ret = vgic_v3_map_resources(kvm); - if (ret) + if (ret) { __kvm_vgic_destroy(kvm); - else - dist->ready = true; + goto out; + } + dist->ready = true; + dist_base = dist->vgic_dist_base; + mutex_unlock(&kvm->arch.config_lock); + + ret = vgic_register_dist_iodev(kvm, dist_base, + kvm_vgic_global_state.type); + if (ret) { + kvm_err("Unable to register VGIC dist MMIO regions\n"); + kvm_vgic_destroy(kvm); + } + mutex_unlock(&kvm->slots_lock); + return ret; out: mutex_unlock(&kvm->arch.config_lock); + mutex_unlock(&kvm->slots_lock); return ret; } diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index 07e727023deb..bf4b3d9631ce 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -102,7 +102,11 @@ static int kvm_vgic_addr(struct kvm *kvm, struct kvm_device_attr *attr, bool wri if (get_user(addr, uaddr)) return -EFAULT; - mutex_lock(&kvm->arch.config_lock); + /* + * Since we can't hold config_lock while registering the redistributor + * iodevs, take the slots_lock immediately. + */ + mutex_lock(&kvm->slots_lock); switch (attr->attr) { case KVM_VGIC_V2_ADDR_TYPE_DIST: r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); @@ -182,6 +186,7 @@ static int kvm_vgic_addr(struct kvm *kvm, struct kvm_device_attr *attr, bool wri if (r) goto out; + mutex_lock(&kvm->arch.config_lock); if (write) { r = vgic_check_iorange(kvm, *addr_ptr, addr, alignment, size); if (!r) @@ -189,9 +194,10 @@ static int kvm_vgic_addr(struct kvm *kvm, struct kvm_device_attr *attr, bool wri } else { addr = *addr_ptr; } + mutex_unlock(&kvm->arch.config_lock); out: - mutex_unlock(&kvm->arch.config_lock); + mutex_unlock(&kvm->slots_lock); if (!r && !write) r = put_user(addr, uaddr); diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 472b18ac92a2..188d2187eede 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -769,10 +769,13 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu) struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev; struct vgic_redist_region *rdreg; gpa_t rd_base; - int ret; + int ret = 0; + + lockdep_assert_held(&kvm->slots_lock); + mutex_lock(&kvm->arch.config_lock); if (!IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) - return 0; + goto out_unlock; /* * We may be creating VCPUs before having set the base address for the @@ -782,10 +785,12 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu) */ rdreg = vgic_v3_rdist_free_slot(&vgic->rd_regions); if (!rdreg) - return 0; + goto out_unlock; - if (!vgic_v3_check_base(kvm)) - return -EINVAL; + if (!vgic_v3_check_base(kvm)) { + ret = -EINVAL; + goto out_unlock; + } vgic_cpu->rdreg = rdreg; vgic_cpu->rdreg_index = rdreg->free_index; @@ -799,16 +804,20 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu) rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rd_registers); rd_dev->redist_vcpu = vcpu; - mutex_lock(&kvm->slots_lock); + mutex_unlock(&kvm->arch.config_lock); + ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, rd_base, 2 * SZ_64K, &rd_dev->dev); - mutex_unlock(&kvm->slots_lock); - if (ret) return ret; + /* Protected by slots_lock */ rdreg->free_index++; return 0; + +out_unlock: + mutex_unlock(&kvm->arch.config_lock); + return ret; } static void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu) @@ -834,12 +843,10 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm) /* The current c failed, so iterate over the previous ones. */ int i; - mutex_lock(&kvm->slots_lock); for (i = 0; i < c; i++) { vcpu = kvm_get_vcpu(kvm, i); vgic_unregister_redist_iodev(vcpu); } - mutex_unlock(&kvm->slots_lock); } return ret; @@ -938,7 +945,9 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count) { int ret; + mutex_lock(&kvm->arch.config_lock); ret = vgic_v3_alloc_redist_region(kvm, index, addr, count); + mutex_unlock(&kvm->arch.config_lock); if (ret) return ret; @@ -950,8 +959,10 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count) if (ret) { struct vgic_redist_region *rdreg; + mutex_lock(&kvm->arch.config_lock); rdreg = vgic_v3_rdist_region_from_index(kvm, index); vgic_v3_free_redist_region(rdreg); + mutex_unlock(&kvm->arch.config_lock); return ret; } diff --git a/arch/arm64/kvm/vgic/vgic-mmio.c b/arch/arm64/kvm/vgic/vgic-mmio.c index a95f99b93dd6..eb5c58d785b9 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio.c +++ b/arch/arm64/kvm/vgic/vgic-mmio.c @@ -1093,7 +1093,6 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address, enum vgic_type type) { struct vgic_io_device *io_device = &kvm->arch.vgic.dist_iodev; - int ret = 0; unsigned int len; switch (type) { @@ -1111,10 +1110,6 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address, io_device->iodev_type = IODEV_DIST; io_device->redist_vcpu = NULL; - mutex_lock(&kvm->slots_lock); - ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist_base_address, - len, &io_device->dev); - mutex_unlock(&kvm->slots_lock); - - return ret; + return kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist_base_address, + len, &io_device->dev); } diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c index 645648349c99..7e9cdb78f7ce 100644 --- a/arch/arm64/kvm/vgic/vgic-v2.c +++ b/arch/arm64/kvm/vgic/vgic-v2.c @@ -312,12 +312,6 @@ int vgic_v2_map_resources(struct kvm *kvm) return ret; } - ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2); - if (ret) { - kvm_err("Unable to register VGIC MMIO regions\n"); - return ret; - } - if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) { ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base, kvm_vgic_global_state.vcpu_base, diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index 5bdada313728..f86c3007a319 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -538,7 +538,6 @@ int vgic_v3_map_resources(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; struct kvm_vcpu *vcpu; - int ret = 0; unsigned long c; kvm_for_each_vcpu(c, vcpu, kvm) { @@ -568,12 +567,6 @@ int vgic_v3_map_resources(struct kvm *kvm) return -EBUSY; } - ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3); - if (ret) { - kvm_err("Unable to register VGICv3 dist MMIO regions\n"); - return ret; - } - if (kvm_vgic_global_state.has_gicv4_1) vgic_v4_configure_vsgis(kvm); -- cgit 1.4.1 From 150a5f74a5973e4ac275b94dcdf18371e0817ac1 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Thu, 18 May 2023 11:09:16 +0100 Subject: KVM: arm64: vgic: Wrap vgic_its_create() with config_lock [ Upstream commit 9cf2f840c439b6b23bd99f584f2917ca425ae406 ] vgic_its_create() changes the vgic state without holding the config_lock, which triggers a lockdep warning in vgic_v4_init(): [ 358.667941] WARNING: CPU: 3 PID: 178 at arch/arm64/kvm/vgic/vgic-v4.c:245 vgic_v4_init+0x15c/0x7a8 ... [ 358.707410] vgic_v4_init+0x15c/0x7a8 [ 358.708550] vgic_its_create+0x37c/0x4a4 [ 358.709640] kvm_vm_ioctl+0x1518/0x2d80 [ 358.710688] __arm64_sys_ioctl+0x7ac/0x1ba8 [ 358.711960] invoke_syscall.constprop.0+0x70/0x1e0 [ 358.713245] do_el0_svc+0xe4/0x2d4 [ 358.714289] el0_svc+0x44/0x8c [ 358.715329] el0t_64_sync_handler+0xf4/0x120 [ 358.716615] el0t_64_sync+0x190/0x194 Wrap the whole of vgic_its_create() with config_lock since, in addition to calling vgic_v4_init(), it also modifies the global kvm->arch.vgic state. Fixes: f00327731131 ("KVM: arm64: Use config_lock to protect vgic state") Signed-off-by: Jean-Philippe Brucker Reviewed-by: Oliver Upton Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230518100914.2837292-3-jean-philippe@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/kvm/vgic/vgic-its.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index c9a03033d507..00ad6587bee9 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -1936,6 +1936,7 @@ void vgic_lpi_translation_cache_destroy(struct kvm *kvm) static int vgic_its_create(struct kvm_device *dev, u32 type) { + int ret; struct vgic_its *its; if (type != KVM_DEV_TYPE_ARM_VGIC_ITS) @@ -1945,9 +1946,12 @@ static int vgic_its_create(struct kvm_device *dev, u32 type) if (!its) return -ENOMEM; + mutex_lock(&dev->kvm->arch.config_lock); + if (vgic_initialized(dev->kvm)) { - int ret = vgic_v4_init(dev->kvm); + ret = vgic_v4_init(dev->kvm); if (ret < 0) { + mutex_unlock(&dev->kvm->arch.config_lock); kfree(its); return ret; } @@ -1960,12 +1964,10 @@ static int vgic_its_create(struct kvm_device *dev, u32 type) /* Yep, even more trickery for lock ordering... */ #ifdef CONFIG_LOCKDEP - mutex_lock(&dev->kvm->arch.config_lock); mutex_lock(&its->cmd_lock); mutex_lock(&its->its_lock); mutex_unlock(&its->its_lock); mutex_unlock(&its->cmd_lock); - mutex_unlock(&dev->kvm->arch.config_lock); #endif its->vgic_its_base = VGIC_ADDR_UNDEF; @@ -1986,7 +1988,11 @@ static int vgic_its_create(struct kvm_device *dev, u32 type) dev->private = its; - return vgic_its_set_abi(its, NR_ITS_ABIS - 1); + ret = vgic_its_set_abi(its, NR_ITS_ABIS - 1); + + mutex_unlock(&dev->kvm->arch.config_lock); + + return ret; } static void vgic_its_destroy(struct kvm_device *kvm_dev) -- cgit 1.4.1 From bafe94ac995004eb113532ff1e74e2012446a03f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Thu, 18 May 2023 11:09:17 +0100 Subject: KVM: arm64: vgic: Fix locking comment [ Upstream commit c38b8400aef99d63be2b1ff131bb993465dcafe1 ] It is now config_lock that must be held, not kvm lock. Replace the comment with a lockdep annotation. Fixes: f00327731131 ("KVM: arm64: Use config_lock to protect vgic state") Signed-off-by: Jean-Philippe Brucker Reviewed-by: Oliver Upton Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230518100914.2837292-4-jean-philippe@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/kvm/vgic/vgic-v4.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c index 3bb003478060..c1c28fe680ba 100644 --- a/arch/arm64/kvm/vgic/vgic-v4.c +++ b/arch/arm64/kvm/vgic/vgic-v4.c @@ -184,13 +184,14 @@ static void vgic_v4_disable_vsgis(struct kvm_vcpu *vcpu) } } -/* Must be called with the kvm lock held */ void vgic_v4_configure_vsgis(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; struct kvm_vcpu *vcpu; unsigned long i; + lockdep_assert_held(&kvm->arch.config_lock); + kvm_arm_halt_guest(kvm); kvm_for_each_vcpu(i, vcpu, kvm) { -- cgit 1.4.1 From 4124000cf4c590bea6d898829c592756ffcb1ceb Mon Sep 17 00:00:00 2001 From: Pin-yen Lin Date: Sat, 22 Apr 2023 11:39:05 +0100 Subject: media: mediatek: vcodec: Only apply 4K frame sizes on decoder formats [ Upstream commit ed17f89e9502f03af493e130620a9bb74c07cf28 ] When VCODEC_CAPABILITY_4K_DISABLED is not set in dec_capability, skip formats that are not MTK_FMT_DEC so only decoder formats is updated in mtk_init_vdec_params. Fixes: e25528e1dbe5 ("media: mediatek: vcodec: Use 4K frame size when supported by stateful decoder") Signed-off-by: Pin-yen Lin Reviewed-by: Chen-Yu Tsai Reviewed-by: Yunfei Dong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c index 29991551cf61..0fbd030026c7 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c @@ -584,6 +584,9 @@ static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) if (!(ctx->dev->dec_capability & VCODEC_CAPABILITY_4K_DISABLED)) { for (i = 0; i < num_supported_formats; i++) { + if (mtk_video_formats[i].type != MTK_FMT_DEC) + continue; + mtk_video_formats[i].frmsize.max_width = VCODEC_DEC_4K_CODED_WIDTH; mtk_video_formats[i].frmsize.max_height = -- cgit 1.4.1 From 7d233f93594f0d9afe44e9409131a8d6ad4f593c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 5 May 2023 12:22:09 +0300 Subject: mailbox: mailbox-test: fix a locking issue in mbox_test_message_write() [ Upstream commit 8fe72b76db79d694858e872370df49676bc3be8c ] There was a bug where this code forgot to unlock the tdev->mutex if the kzalloc() failed. Fix this issue, by moving the allocation outside the lock. Fixes: 2d1e952a2b8e ("mailbox: mailbox-test: Fix potential double-free in mbox_test_message_write()") Signed-off-by: Dan Carpenter Reviewed-by: Lee Jones Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/mailbox-test.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 6dd5b9614452..abcee58e851c 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -97,6 +97,7 @@ static ssize_t mbox_test_message_write(struct file *filp, size_t count, loff_t *ppos) { struct mbox_test_device *tdev = filp->private_data; + char *message; void *data; int ret; @@ -112,12 +113,13 @@ static ssize_t mbox_test_message_write(struct file *filp, return -EINVAL; } - mutex_lock(&tdev->mutex); - - tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); - if (!tdev->message) + message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); + if (!message) return -ENOMEM; + mutex_lock(&tdev->mutex); + + tdev->message = message; ret = copy_from_user(tdev->message, userbuf, count); if (ret) { ret = -EFAULT; -- cgit 1.4.1 From 4d776371127ec55590d49eea7579e77f555548ad Mon Sep 17 00:00:00 2001 From: K Prateek Nayak Date: Mon, 8 May 2023 14:11:14 +0530 Subject: drivers: base: cacheinfo: Fix shared_cpu_map changes in event of CPU hotplug [ Upstream commit 126310c9f669c9a8c875a3e5c2292299ca90225d ] While building the shared_cpu_map, check if the cache level and cache type matches. On certain systems that build the cache topology based on the instance ID, there are cases where the same ID may repeat across multiple cache levels, leading inaccurate topology. In event of CPU offlining, the cache_shared_cpu_map_remove() does not consider if IDs at same level are being compared. As a result, when same IDs repeat across different cache levels, the CPU going offline is not removed from all the shared_cpu_map. Below is the output of cache topology of CPU8 and it's SMT sibling after CPU8 is offlined on a dual socket 3rd Generation AMD EPYC processor (2 x 64C/128T) running kernel release v6.3: # for i in /sys/devices/system/cpu/cpu8/cache/index*/shared_cpu_list; do echo -n "$i: "; cat $i; done /sys/devices/system/cpu/cpu8/cache/index0/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu8/cache/index1/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu8/cache/index2/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu8/cache/index3/shared_cpu_list: 8-15,136-143 # echo 0 > /sys/devices/system/cpu/cpu8/online # for i in /sys/devices/system/cpu/cpu136/cache/index*/shared_cpu_list; do echo -n "$i: "; cat $i; done /sys/devices/system/cpu/cpu136/cache/index0/shared_cpu_list: 136 /sys/devices/system/cpu/cpu136/cache/index1/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu136/cache/index2/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu136/cache/index3/shared_cpu_list: 9-15,136-143 CPU8 is removed from index0 (L1i) but remains in the shared_cpu_list of index1 (L1d) and index2 (L2). Since L1i, L1d, and L2 are shared by the SMT siblings, and they have the same cache instance ID, CPU 2 is only removed from the first index with matching ID which is index1 (L1i) in this case. With this fix, the results are as expected when performing the same experiment on the same system: # for i in /sys/devices/system/cpu/cpu8/cache/index*/shared_cpu_list; do echo -n "$i: "; cat $i; done /sys/devices/system/cpu/cpu8/cache/index0/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu8/cache/index1/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu8/cache/index2/shared_cpu_list: 8,136 /sys/devices/system/cpu/cpu8/cache/index3/shared_cpu_list: 8-15,136-143 # echo 0 > /sys/devices/system/cpu/cpu8/online # for i in /sys/devices/system/cpu/cpu136/cache/index*/shared_cpu_list; do echo -n "$i: "; cat $i; done /sys/devices/system/cpu/cpu136/cache/index0/shared_cpu_list: 136 /sys/devices/system/cpu/cpu136/cache/index1/shared_cpu_list: 136 /sys/devices/system/cpu/cpu136/cache/index2/shared_cpu_list: 136 /sys/devices/system/cpu/cpu136/cache/index3/shared_cpu_list: 9-15,136-143 When rebuilding topology, the same problem appears as cache_shared_cpu_map_setup() implements a similar logic. Consider the same 3rd Generation EPYC processor: CPUs in Core 1, that share the L1 and L2 caches, have L1 and L2 instance ID as 1. For all the CPUs on the second chiplet, the L3 ID is also 1 leading to grouping on CPUs from Core 1 (1, 17) and the entire second chiplet (8-15, 24-31) as CPUs sharing one cache domain. This went undetected since x86 processors depended on arch specific populate_cache_leaves() method to repopulate the shared_cpus_map when CPU came back online until kernel release v6.3-rc5. Fixes: 198102c9103f ("cacheinfo: Fix shared_cpu_map to handle shared caches at different levels") Signed-off-by: K Prateek Nayak Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20230508084115.1157-2-kprateek.nayak@amd.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/cacheinfo.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index c440d1af197a..26e13887aba4 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -280,6 +280,16 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) continue;/* skip if itself or no cacheinfo */ for (sib_index = 0; sib_index < cache_leaves(i); sib_index++) { sib_leaf = per_cpu_cacheinfo_idx(i, sib_index); + + /* + * Comparing cache IDs only makes sense if the leaves + * belong to the same cache level of same type. Skip + * the check if level and type do not match. + */ + if (sib_leaf->level != this_leaf->level || + sib_leaf->type != this_leaf->type) + continue; + if (cache_leaves_are_shared(this_leaf, sib_leaf)) { cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); cpumask_set_cpu(i, &this_leaf->shared_cpu_map); @@ -311,6 +321,16 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) for (sib_index = 0; sib_index < cache_leaves(sibling); sib_index++) { sib_leaf = per_cpu_cacheinfo_idx(sibling, sib_index); + + /* + * Comparing cache IDs only makes sense if the leaves + * belong to the same cache level of same type. Skip + * the check if level and type do not match. + */ + if (sib_leaf->level != this_leaf->level || + sib_leaf->type != this_leaf->type) + continue; + if (cache_leaves_are_shared(this_leaf, sib_leaf)) { cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); -- cgit 1.4.1 From 47cc3cae303c289c51135f90cd5ff961f340b8e0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 20 Apr 2023 10:45:59 +0100 Subject: media: uvcvideo: Don't expose unsupported formats to userspace [ Upstream commit 81f3affa19d6ab0c32aef46b053838219eef7e71 ] When the uvcvideo driver encounters a format descriptor with an unknown format GUID, it creates a corresponding struct uvc_format instance with the fcc field set to 0. Since commit 50459f103edf ("media: uvcvideo: Remove format descriptions"), the driver relies on the V4L2 core to provide the format description string, which the V4L2 core can't do without a valid 4CC. This triggers a WARN_ON. As a format with a zero 4CC can't be selected, it is unusable for applications. Ignore the format completely without creating a uvc_format instance, which fixes the warning. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217252 Link: https://bugzilla.redhat.com/show_bug.cgi?id=2180107 Fixes: 50459f103edf ("media: uvcvideo: Remove format descriptions") Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/usb/uvc/uvc_driver.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index a9cdef07e6b1..191db831d760 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -251,14 +251,17 @@ static int uvc_parse_format(struct uvc_device *dev, /* Find the format descriptor from its GUID. */ fmtdesc = uvc_format_by_guid(&buffer[5]); - if (fmtdesc != NULL) { - format->fcc = fmtdesc->fcc; - } else { + if (!fmtdesc) { + /* + * Unknown video formats are not fatal errors, the + * caller will skip this descriptor. + */ dev_info(&streaming->intf->dev, "Unknown video format %pUl\n", &buffer[5]); - format->fcc = 0; + return 0; } + format->fcc = fmtdesc->fcc; format->bpp = buffer[21]; /* @@ -675,7 +678,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, interval = (u32 *)&frame[nframes]; streaming->format = format; - streaming->nformats = nformats; + streaming->nformats = 0; /* Parse the format descriptors. */ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { @@ -689,7 +692,10 @@ static int uvc_parse_streaming(struct uvc_device *dev, &interval, buffer, buflen); if (ret < 0) goto error; + if (!ret) + break; + streaming->nformats++; frame += format->nframes; format++; -- cgit 1.4.1 From b6867ce5fb35d9dbc2d1d5b435e8d0359cfcce53 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:24:09 +0200 Subject: iio: accel: st_accel: Fix invalid mount_matrix on devices without ACPI _ONT method commit 79b8ded9d9c595db9bd5b2f62f5f738b36de1e22 upstream. When apply_acpi_orientation() fails, st_accel_common_probe() will fall back to iio_read_mount_matrix(), which checks for a mount-matrix device property and if that is not set falls back to the identity matrix. But when a sensor has no ACPI companion fwnode, or when the ACPI fwnode does not have a "_ONT" method apply_acpi_orientation() was returning 0, causing iio_read_mount_matrix() to never get called resulting in an invalid mount_matrix: [root@fedora ~]# cat /sys/bus/iio/devices/iio\:device0/mount_matrix (null), (null), (null); (null), (null), (null); (null), (null), (null) Fix this by making apply_acpi_orientation() always return an error when it did not set the mount_matrix. Fixes: 3d8ad94bb175 ("iio: accel: st_sensors: Support generic mounting matrix") Signed-off-by: Hans de Goede Reviewed-by: Linus Walleij Tested-by: Marius Hoch Link: https://lore.kernel.org/r/20230416212409.310936-1-hdegoede@redhat.com Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/st_accel_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index c8c8eb15c34e..c4143c567874 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -1289,12 +1289,12 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev) adev = ACPI_COMPANION(indio_dev->dev.parent); if (!adev) - return 0; + return -ENXIO; /* Read _ONT data, which should be a package of 6 integers. */ status = acpi_evaluate_object(adev->handle, "_ONT", NULL, &buffer); if (status == AE_NOT_FOUND) { - return 0; + return -ENXIO; } else if (ACPI_FAILURE(status)) { dev_warn(&indio_dev->dev, "failed to execute _ONT: %d\n", status); -- cgit 1.4.1 From 23c241676f32e35fb4b42cd594938f4910880c83 Mon Sep 17 00:00:00 2001 From: Jiakai Luo Date: Sat, 22 Apr 2023 06:34:06 -0700 Subject: iio: adc: mxs-lradc: fix the order of two cleanup operations commit 27b2ed5b6d53cd62fc61c3f259ae52f5cac23b66 upstream. Smatch reports: drivers/iio/adc/mxs-lradc-adc.c:766 mxs_lradc_adc_probe() warn: missing unwind goto? the order of three init operation: 1.mxs_lradc_adc_trigger_init 2.iio_triggered_buffer_setup 3.mxs_lradc_adc_hw_init thus, the order of three cleanup operation should be: 1.mxs_lradc_adc_hw_stop 2.iio_triggered_buffer_cleanup 3.mxs_lradc_adc_trigger_remove we exchange the order of two cleanup operations, introducing the following differences: 1.if mxs_lradc_adc_trigger_init fails, returns directly; 2.if trigger_init succeeds but iio_triggered_buffer_setup fails, goto err_trig and remove the trigger. In addition, we also reorder the unwind that goes on in the remove() callback to match the new ordering. Fixes: 6dd112b9f85e ("iio: adc: mxs-lradc: Add support for ADC driver") Signed-off-by: Jiakai Luo Reviewed-by: Dongliang Mu Link: https://lore.kernel.org/r/20230422133407.72908-1-jkluo@hust.edu.cn Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/mxs-lradc-adc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c index bca79a93cbe4..a50f39143d3e 100644 --- a/drivers/iio/adc/mxs-lradc-adc.c +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -757,13 +757,13 @@ static int mxs_lradc_adc_probe(struct platform_device *pdev) ret = mxs_lradc_adc_trigger_init(iio); if (ret) - goto err_trig; + return ret; ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time, &mxs_lradc_adc_trigger_handler, &mxs_lradc_adc_buffer_ops); if (ret) - return ret; + goto err_trig; adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc]; @@ -801,9 +801,9 @@ static int mxs_lradc_adc_probe(struct platform_device *pdev) err_dev: mxs_lradc_adc_hw_stop(adc); - mxs_lradc_adc_trigger_remove(iio); -err_trig: iio_triggered_buffer_cleanup(iio); +err_trig: + mxs_lradc_adc_trigger_remove(iio); return ret; } @@ -814,8 +814,8 @@ static int mxs_lradc_adc_remove(struct platform_device *pdev) iio_device_unregister(iio); mxs_lradc_adc_hw_stop(adc); - mxs_lradc_adc_trigger_remove(iio); iio_triggered_buffer_cleanup(iio); + mxs_lradc_adc_trigger_remove(iio); return 0; } -- cgit 1.4.1 From 254be1f64819b231faad137ebc2b4e537c8be73d Mon Sep 17 00:00:00 2001 From: Sung-Chi Li Date: Mon, 24 Apr 2023 10:37:36 +0800 Subject: HID: google: add jewel USB id commit ed84c4517a5bc536e8572a01dfa11bc22a280d06 upstream. Add 1 additional hammer-like device. Signed-off-by: Sung-Chi Li Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-google-hammer.c | 2 ++ drivers/hid/hid-ids.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index 7ae5f27df54d..c6bdb9c4ef3e 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -586,6 +586,8 @@ static const struct hid_device_id hammer_devices[] = { USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_EEL) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_JEWEL) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MAGNEMITE) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 53c6692d7771..653db6cdab57 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -523,6 +523,7 @@ #define USB_DEVICE_ID_GOOGLE_MOONBALL 0x5044 #define USB_DEVICE_ID_GOOGLE_DON 0x5050 #define USB_DEVICE_ID_GOOGLE_EEL 0x5057 +#define USB_DEVICE_ID_GOOGLE_JEWEL 0x5061 #define USB_VENDOR_ID_GOTOP 0x08f2 #define USB_DEVICE_ID_SUPER_Q2 0x007f -- cgit 1.4.1 From 3fb021f5c1144073398404bc47c7f4a282311d86 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Mon, 17 Apr 2023 09:01:48 -0700 Subject: HID: wacom: avoid integer overflow in wacom_intuos_inout() commit bd249b91977b768ea02bf84d04625d2690ad2b98 upstream. If high bit is set to 1 in ((data[3] & 0x0f << 28), after all arithmetic operations and integer promotions are done, high bits in wacom->serial[idx] will be filled with 1s as well. Avoid this, albeit unlikely, issue by specifying left operand's __u64 type for the right operand. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 3bea733ab212 ("USB: wacom tablet driver reorganization") Signed-off-by: Nikita Zhandarovich Reviewed-by: Ping Cheng Cc: stable@vger.kernel.org Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_wac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index d2f500242ed4..9c30dd30537a 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -826,7 +826,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) /* Enter report */ if ((data[1] & 0xfc) == 0xc0) { /* serial number of the tool */ - wacom->serial[idx] = ((data[3] & 0x0f) << 28) + + wacom->serial[idx] = ((__u64)(data[3] & 0x0f) << 28) + (data[4] << 20) + (data[5] << 12) + (data[6] << 4) + (data[7] >> 4); -- cgit 1.4.1 From cb2a612c397d3c7d318f6aa0104480dfdd1a808d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Tue, 9 May 2023 15:22:02 +0000 Subject: iio: imu: inv_icm42600: fix timestamp reset commit bbaae0c79ebd49f61ad942a8bf9e12bfc7f821bb upstream. Timestamp reset is not done in the correct place. It must be done before enabling buffer. The reason is that interrupt timestamping is always happening when the chip is on, even if the corresponding sensor is off. When the sensor restarts, timestamp is wrong if you don't do a reset first. Fixes: ec74ae9fd37c ("iio: imu: inv_icm42600: add accurate timestamping") Signed-off-by: Jean-Baptiste Maneyrol Cc: Link: https://lore.kernel.org/r/20230509152202.245444-1-inv.git-commit@tdk.com Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 99576b2c171f..32d7f8364230 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -275,9 +275,14 @@ static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); struct device *dev = regmap_get_device(st->map); + struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); pm_runtime_get_sync(dev); + mutex_lock(&st->lock); + inv_icm42600_timestamp_reset(ts); + mutex_unlock(&st->lock); + return 0; } @@ -375,7 +380,6 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) struct device *dev = regmap_get_device(st->map); unsigned int sensor; unsigned int *watermark; - struct inv_icm42600_timestamp *ts; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; unsigned int sleep_temp = 0; unsigned int sleep_sensor = 0; @@ -385,11 +389,9 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) if (indio_dev == st->indio_gyro) { sensor = INV_ICM42600_SENSOR_GYRO; watermark = &st->fifo.watermark.gyro; - ts = iio_priv(st->indio_gyro); } else if (indio_dev == st->indio_accel) { sensor = INV_ICM42600_SENSOR_ACCEL; watermark = &st->fifo.watermark.accel; - ts = iio_priv(st->indio_accel); } else { return -EINVAL; } @@ -417,8 +419,6 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) if (!st->fifo.on) ret = inv_icm42600_set_temp_conf(st, false, &sleep_temp); - inv_icm42600_timestamp_reset(ts); - out_unlock: mutex_unlock(&st->lock); -- cgit 1.4.1 From 2896a356ed079b7d585f4ae0ab7628a2d7e23190 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 9 May 2023 14:34:22 +0200 Subject: dt-bindings: iio: adc: renesas,rcar-gyroadc: Fix adi,ad7476 compatible value commit 55720d242052e860b9fde445e302e0425722e7f1 upstream. The conversion to json-schema accidentally dropped the "ad" part prefix from the compatible value. Fixes: 8c41245872e2 ("dt-bindings:iio:adc:renesas,rcar-gyroadc: txt to yaml conversion.") Signed-off-by: Geert Uytterhoeven Reviewed-by: Marek Vasut Reviewed-by: Krzysztof Kozlowski Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/6b328a3f52657c20759f3a5bb2fe033d47644ba8.1683635404.git.geert+renesas@glider.be Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml index c115e2e99bd9..4a7b1385fdc7 100644 --- a/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml @@ -86,7 +86,7 @@ patternProperties: of the MAX chips to the GyroADC, while MISO line of each Maxim ADC connects to a shared input pin of the GyroADC. enum: - - adi,7476 + - adi,ad7476 - fujitsu,mb88101a - maxim,max1162 - maxim,max11100 -- cgit 1.4.1 From 54d737d79672c12c6af469ef9a6ff285666e2d0c Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 1 May 2023 10:36:04 -0400 Subject: iio: light: vcnl4035: fixed chip ID check commit a551c26e8e568fad42120843521529241b9bceec upstream. VCNL4035 register(0xE) ID_L and ID_M define as: ID_L: 0x80 ID_H: 7:6 (0:0) 5:4 (0:0) slave address = 0x60 (7-bit) (0:1) slave address = 0x51 (7-bit) (1:0) slave address = 0x40 (7-bit) (1:0) slave address = 0x41 (7-bit) 3:0 Version code default (0:0:0:0) So just check ID_L. Fixes: 55707294c4eb ("iio: light: Add support for vishay vcnl4035") Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20230501143605.1615549-1-Frank.Li@nxp.com Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/vcnl4035.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 3ed37f6057fb..a23c415fcb7a 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -8,6 +8,7 @@ * TODO: Proximity */ #include +#include #include #include #include @@ -42,6 +43,7 @@ #define VCNL4035_ALS_PERS_MASK GENMASK(3, 2) #define VCNL4035_INT_ALS_IF_H_MASK BIT(12) #define VCNL4035_INT_ALS_IF_L_MASK BIT(13) +#define VCNL4035_DEV_ID_MASK GENMASK(7, 0) /* Default values */ #define VCNL4035_MODE_ALS_ENABLE BIT(0) @@ -413,6 +415,7 @@ static int vcnl4035_init(struct vcnl4035_data *data) return ret; } + id = FIELD_GET(VCNL4035_DEV_ID_MASK, id); if (id != VCNL4035_DEV_ID_VAL) { dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n", id, VCNL4035_DEV_ID_VAL); -- cgit 1.4.1 From 2eb269605113326950cf9f6493df720af19f75ee Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Wed, 3 May 2023 18:20:29 +0200 Subject: iio: adc: stm32-adc: skip adc-channels setup if none is present commit 3e27ef0ced49f8ae7883c25fadf76a2086e99025 upstream. If only adc differential channels are defined driver will fail with stm32-adc: probe of 48003000.adc:adc@0 failed with error -22 Fix this by skipping the initialization if no channels are defined. This applies only to the legacy way of initializing adc channels. Fixes: d7705f35448a ("iio: adc: stm32-adc: convert to device properties") Signed-off-by: Sean Nyekjaer Reviewed-by: Olivier Moysan Link: https://lore.kernel.org/r/20230503162029.3654093-2-sean@geanix.com Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/stm32-adc.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 3cda529f081d..b5d7ba2feb07 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1913,6 +1913,7 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; struct device *dev = &indio_dev->dev; u32 num_diff = adc->num_diff; + int num_se = nchans - num_diff; int size = num_diff * sizeof(*diff) / sizeof(u32); int scan_index = 0, ret, i, c; u32 smp = 0, smps[STM32_ADC_CH_MAX], chans[STM32_ADC_CH_MAX]; @@ -1939,29 +1940,32 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, scan_index++; } } - - ret = device_property_read_u32_array(dev, "st,adc-channels", chans, - nchans); - if (ret) - return ret; - - for (c = 0; c < nchans; c++) { - if (chans[c] >= adc_info->max_channels) { - dev_err(&indio_dev->dev, "Invalid channel %d\n", - chans[c]); - return -EINVAL; + if (num_se > 0) { + ret = device_property_read_u32_array(dev, "st,adc-channels", chans, num_se); + if (ret) { + dev_err(&indio_dev->dev, "Failed to get st,adc-channels %d\n", ret); + return ret; } - /* Channel can't be configured both as single-ended & diff */ - for (i = 0; i < num_diff; i++) { - if (chans[c] == diff[i].vinp) { - dev_err(&indio_dev->dev, "channel %d misconfigured\n", chans[c]); + for (c = 0; c < num_se; c++) { + if (chans[c] >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel %d\n", + chans[c]); return -EINVAL; } + + /* Channel can't be configured both as single-ended & diff */ + for (i = 0; i < num_diff; i++) { + if (chans[c] == diff[i].vinp) { + dev_err(&indio_dev->dev, "channel %d misconfigured\n", + chans[c]); + return -EINVAL; + } + } + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + chans[c], 0, scan_index, false); + scan_index++; } - stm32_adc_chan_init_one(indio_dev, &channels[scan_index], - chans[c], 0, scan_index, false); - scan_index++; } if (adc->nsmps > 0) { @@ -2153,7 +2157,7 @@ static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping) if (legacy) ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels, - num_channels); + timestamping ? num_channels - 1 : num_channels); else ret = stm32_adc_generic_chan_init(indio_dev, adc, channels); if (ret < 0) -- cgit 1.4.1 From 89f92d435352578bd940bb39f03f9e64ba1fa180 Mon Sep 17 00:00:00 2001 From: Masahiro Honda Date: Thu, 18 May 2023 20:08:16 +0900 Subject: iio: adc: ad_sigma_delta: Fix IRQ issue by setting IRQ_DISABLE_UNLAZY flag commit 626d312028bec44209d0ecd5beaa9b1aa8945f7d upstream. The Sigma-Delta ADCs supported by this driver can use SDO as an interrupt line to indicate the completion of a conversion. However, some devices cannot properly detect the completion of a conversion by an interrupt. This is for the reason mentioned in the following commit. commit e9849777d0e2 ("genirq: Add flag to force mask in disable_irq[_nosync]()") A read operation is performed by an extra interrupt before the completion of a conversion. At this time, the value read from the ADC data register is the same as the previous conversion result. This patch fixes the issue by setting IRQ_DISABLE_UNLAZY flag. Fixes: 0c6ef985a1fd ("iio: adc: ad7791: fix IRQ flags") Fixes: 1a913270e57a ("iio: adc: ad7793: Fix IRQ flag") Fixes: e081102f3077 ("iio: adc: ad7780: Fix IRQ flag") Fixes: 89a86da5cb8e ("iio: adc: ad7192: Add IRQ flag") Fixes: 79ef91493f54 ("iio: adc: ad7124: Set IRQ type to falling") Signed-off-by: Masahiro Honda Link: https://lore.kernel.org/r/20230518110816.248-1-honda@mechatrax.com Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad_sigma_delta.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index d8570f620785..7e2192870743 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -584,6 +584,10 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de init_completion(&sigma_delta->completion); sigma_delta->irq_dis = true; + + /* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */ + irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY); + ret = devm_request_irq(dev, sigma_delta->spi->irq, ad_sd_data_rdy_trig_poll, sigma_delta->info->irq_flags | IRQF_NO_AUTOEN, -- cgit 1.4.1 From ab0c2dffe80f2ec63b3c43f2930f7a6b536d687b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 11 May 2023 02:43:30 +0200 Subject: iio: dac: mcp4725: Fix i2c_master_send() return value handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 09d3bec7009186bdba77039df01e5834788b3f95 upstream. The i2c_master_send() returns number of sent bytes on success, or negative on error. The suspend/resume callbacks expect zero on success and non-zero on error. Adapt the return value of the i2c_master_send() to the expectation of the suspend and resume callbacks, including proper validation of the return value. Fixes: cf35ad61aca2 ("iio: add mcp4725 I2C DAC driver") Signed-off-by: Marek Vasut Reviewed-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230511004330.206942-1-marex@denx.de Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/mcp4725.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 446d1a8fe4be..2be821d561c4 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -47,12 +47,18 @@ static int mcp4725_suspend(struct device *dev) struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); u8 outbuf[2]; + int ret; outbuf[0] = (data->powerdown_mode + 1) << 4; outbuf[1] = 0; data->powerdown = true; - return i2c_master_send(data->client, outbuf, 2); + ret = i2c_master_send(data->client, outbuf, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + return 0; } static int mcp4725_resume(struct device *dev) @@ -60,13 +66,19 @@ static int mcp4725_resume(struct device *dev) struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); u8 outbuf[2]; + int ret; /* restore previous DAC value */ outbuf[0] = (data->dac_value >> 8) & 0xf; outbuf[1] = data->dac_value & 0xff; data->powerdown = false; - return i2c_master_send(data->client, outbuf, 2); + ret = i2c_master_send(data->client, outbuf, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume); -- cgit 1.4.1 From 84f4d63ae184cfeecce8dda16b5dd4258760d469 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 3 May 2023 11:58:17 +0200 Subject: iio: addac: ad74413: fix resistance input processing commit 24febc99ca725dcf42d57168a2f4e8a75a5ade92 upstream. On success, ad74413r_get_single_adc_result() returns IIO_VAL_INT aka 1. So currently, the IIO_CHAN_INFO_PROCESSED case is effectively equivalent to the IIO_CHAN_INFO_RAW case, and we never call ad74413r_adc_to_resistance_result() to convert the adc measurement to ohms. Check ret for being negative rather than non-zero. Fixes: fea251b6a5dbd (iio: addac: add AD74413R driver) Signed-off-by: Rasmus Villemoes Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230503095817.452551-1-linux@rasmusvillemoes.dk Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/addac/ad74413r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index e0e130ba9d3e..05faf3910bfc 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -973,7 +973,7 @@ static int ad74413r_read_raw(struct iio_dev *indio_dev, ret = ad74413r_get_single_adc_result(indio_dev, chan->channel, val); - if (ret) + if (ret < 0) return ret; ad74413r_adc_to_resistance_result(*val, val); -- cgit 1.4.1 From 735d033bedc338370cbe5f44139cd032b932a677 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 30 Mar 2023 12:21:00 +0200 Subject: iio: adc: ad7192: Change "shorted" channels to differential commit e55245d115bb9054cb72cdd5dda5660f4484873a upstream. The AD7192 provides a specific channel configuration where both negative and positive inputs are connected to AIN2. This was represented in the ad7192 driver as a IIO channel with .channel = 2 and .extended_name set to "shorted". The problem with this approach, is that the driver provided two IIO channels with the identifier .channel = 2; one "shorted" and the other not. This goes against the IIO ABI, as a channel identifier should be unique. Address this issue by changing "shorted" channels to being differential instead, with channel 2 vs. itself, as we're actually measuring AIN2 vs. itself. Note that the fix tag is for the commit that moved the driver out of staging. The bug existed before that, but backporting would become very complex further down and unlikely to happen. Fixes: b581f748cce0 ("staging: iio: adc: ad7192: move out of staging") Signed-off-by: Paul Cercueil Co-developed-by: Alisa Roman Signed-off-by: Alisa Roman Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20230330102100.17590-1-paul@crapouillou.net Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad7192.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index d71977be7d22..a8f6fa48daa8 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -898,10 +898,6 @@ static const struct iio_info ad7195_info = { __AD719x_CHANNEL(_si, _channel1, -1, _address, NULL, IIO_VOLTAGE, \ BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) -#define AD719x_SHORTED_CHANNEL(_si, _channel1, _address) \ - __AD719x_CHANNEL(_si, _channel1, -1, _address, "shorted", IIO_VOLTAGE, \ - BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) - #define AD719x_TEMP_CHANNEL(_si, _address) \ __AD719x_CHANNEL(_si, 0, -1, _address, NULL, IIO_TEMP, 0, NULL) @@ -909,7 +905,7 @@ static const struct iio_chan_spec ad7192_channels[] = { AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M), AD719x_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M), AD719x_TEMP_CHANNEL(2, AD7192_CH_TEMP), - AD719x_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M), + AD719x_DIFF_CHANNEL(3, 2, 2, AD7192_CH_AIN2P_AIN2M), AD719x_CHANNEL(4, 1, AD7192_CH_AIN1), AD719x_CHANNEL(5, 2, AD7192_CH_AIN2), AD719x_CHANNEL(6, 3, AD7192_CH_AIN3), @@ -923,7 +919,7 @@ static const struct iio_chan_spec ad7193_channels[] = { AD719x_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M), AD719x_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M), AD719x_TEMP_CHANNEL(4, AD7193_CH_TEMP), - AD719x_SHORTED_CHANNEL(5, 2, AD7193_CH_AIN2P_AIN2M), + AD719x_DIFF_CHANNEL(5, 2, 2, AD7193_CH_AIN2P_AIN2M), AD719x_CHANNEL(6, 1, AD7193_CH_AIN1), AD719x_CHANNEL(7, 2, AD7193_CH_AIN2), AD719x_CHANNEL(8, 3, AD7193_CH_AIN3), -- cgit 1.4.1 From f453753900e99b26533ffa33588cfb35f59eaa15 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Wed, 3 May 2023 18:20:28 +0200 Subject: iio: adc: stm32-adc: skip adc-diff-channels setup if none is present commit 9c0d6ccd7d6bbd275e390b55a3390b4274291d95 upstream. If no adc differential channels are defined driver will fail with EINVAL: stm32-adc: probe of 48003000.adc:adc@0 failed with error -22 Fix this by skipping the initialization if no channels are defined. This applies only to the legacy way of initializing adc channels. Fixes: d7705f35448a ("iio: adc: stm32-adc: convert to device properties") Signed-off-by: Sean Nyekjaer Link: https://lore.kernel.org/r/20230503162029.3654093-1-sean@geanix.com Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/stm32-adc.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index b5d7ba2feb07..a5d5b7b3823b 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1882,16 +1882,15 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm * to get the *real* number of channels. */ ret = device_property_count_u32(dev, "st,adc-diff-channels"); - if (ret < 0) - return ret; - - ret /= (int)(sizeof(struct stm32_adc_diff_channel) / sizeof(u32)); - if (ret > adc_info->max_channels) { - dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); - return -EINVAL; - } else if (ret > 0) { - adc->num_diff = ret; - num_channels += ret; + if (ret > 0) { + ret /= (int)(sizeof(struct stm32_adc_diff_channel) / sizeof(u32)); + if (ret > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); + return -EINVAL; + } else if (ret > 0) { + adc->num_diff = ret; + num_channels += ret; + } } /* Optional sample time is provided either for each, or all channels */ -- cgit 1.4.1 From 3cfdc3fc1801874d12ea18590d36d54b4a5e030f Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 8 May 2023 06:02:08 +0200 Subject: iio: dac: build ad5758 driver when AD5758 is selected commit a146eccb68be161ae9eab5f3f68bb0ed7c0fbaa8 upstream. Commit 28d1a7ac2a0d ("iio: dac: Add AD5758 support") adds the config AD5758 and the corresponding driver ad5758.c. In the Makefile, the ad5758 driver is however included when AD5755 is selected, not when AD5758 is selected. Probably, this was simply a mistake that happened by copy-and-paste and forgetting to adjust the actual line. Surprisingly, no one has ever noticed that this driver is actually only included when AD5755 is selected and that the config AD5758 has actually no effect on the build. Fixes: 28d1a7ac2a0d ("iio: dac: Add AD5758 support") Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20230508040208.12033-1-lukas.bulwahn@gmail.com Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index ec3e42713f00..e22d9b4bac80 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o obj-$(CONFIG_AD5592R) += ad5592r.o obj-$(CONFIG_AD5593R) += ad5593r.o obj-$(CONFIG_AD5755) += ad5755.o -obj-$(CONFIG_AD5755) += ad5758.o +obj-$(CONFIG_AD5758) += ad5758.o obj-$(CONFIG_AD5761) += ad5761.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5766) += ad5766.o -- cgit 1.4.1 From ef12610ff5fa957f42293e964d0af551b240b0b8 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Fri, 26 May 2023 16:38:11 +0200 Subject: net: usb: qmi_wwan: Set DTR quirk for BroadMobi BM818 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 36936a56e1814f6c526fe71fbf980beab4f5577a upstream. BM818 is based on Qualcomm MDM9607 chipset. Fixes: 9a07406b00cd ("net: usb: qmi_wwan: Add the BroadMobi BM818 card") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Krzyszkowiak Acked-by: Bjørn Mork Link: https://lore.kernel.org/r/20230526-bm818-dtr-v1-1-64bbfa6ba8af@puri.sm Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/qmi_wwan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 2cc28af52ee2..45c258501135 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1324,7 +1324,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x2001, 0x7e3d, 4)}, /* D-Link DWM-222 A2 */ {QMI_FIXED_INTF(0x2020, 0x2031, 4)}, /* Olicard 600 */ {QMI_FIXED_INTF(0x2020, 0x2033, 4)}, /* BroadMobi BM806U */ - {QMI_FIXED_INTF(0x2020, 0x2060, 4)}, /* BroadMobi BM818 */ + {QMI_QUIRK_SET_DTR(0x2020, 0x2060, 4)}, /* BroadMobi BM818 */ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */ {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */ -- cgit 1.4.1 From 08e8ff68a3f677c6559ebebe05f8b7a8f24112a4 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 15 May 2023 19:24:56 +0200 Subject: dt-bindings: usb: snps,dwc3: Fix "snps,hsphy_interface" type commit 7b32040f6d7f885ffc09a6df7c17992d56d2eab8 upstream. The "snps,hsphy_interface" is string, not u8. Fix the type. Fixes: 389d77658801 ("dt-bindings: usb: Convert DWC USB3 bindings to DT schema") Cc: stable Reviewed-by: Krzysztof Kozlowski Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20230515172456.179049-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml index 6d78048c4613..045fc7a28c45 100644 --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml @@ -260,7 +260,7 @@ properties: description: High-Speed PHY interface selection between UTMI+ and ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3. - $ref: /schemas/types.yaml#/definitions/uint8 + $ref: /schemas/types.yaml#/definitions/string enum: [utmi, ulpi] snps,quirk-frame-length-adjustment: -- cgit 1.4.1 From cc8c9864da6986463627a2ba1a1c8a8ac6589988 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 18 May 2023 11:49:45 -0400 Subject: usb: cdns3: fix NCM gadget RX speed 20x slow than expection at iMX8QM commit dbe678f6192f27879ac9ff6bc7a1036aad85aae9 upstream. At iMX8QM platform, enable NCM gadget and run 'iperf3 -s'. At host, run 'iperf3 -V -c fe80::6863:98ff:feef:3e0%enxc6e147509498' [ 5] 0.00-1.00 sec 1.55 MBytes 13.0 Mbits/sec 90 4.18 KBytes [ 5] 1.00-2.00 sec 1.44 MBytes 12.0 Mbits/sec 75 4.18 KBytes [ 5] 2.00-3.00 sec 1.48 MBytes 12.4 Mbits/sec 75 4.18 KBytes Expected speed should be bigger than 300Mbits/sec. The root cause of this performance drop was found to be data corruption happening at 4K borders in some Ethernet packets, leading to TCP checksum errors. This corruption occurs from the position (4K - (address & 0x7F)) to 4K. The u_ether function's allocation of skb_buff reserves 64B, meaning all RX addresses resemble 0xXXXX0040. Force trb_burst_size to 16 can fix this problem. Cc: stable@vger.kernel.org Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20230518154946.3666662-1-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index ccfaebca6faa..1dcadef933e3 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2097,6 +2097,19 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) else priv_ep->trb_burst_size = 16; + /* + * In versions preceding DEV_VER_V2, for example, iMX8QM, there exit the bugs + * in the DMA. These bugs occur when the trb_burst_size exceeds 16 and the + * address is not aligned to 128 Bytes (which is a product of the 64-bit AXI + * and AXI maximum burst length of 16 or 0xF+1, dma_axi_ctrl0[3:0]). This + * results in data corruption when it crosses the 4K border. The corruption + * specifically occurs from the position (4K - (address & 0x7F)) to 4K. + * + * So force trb_burst_size to 16 at such platform. + */ + if (priv_dev->dev_ver < DEV_VER_V2) + priv_ep->trb_burst_size = 16; + mult = min_t(u8, mult, EP_CFG_MULT_MAX); buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX); maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX); -- cgit 1.4.1 From 704842c97aa35e587a287c22c57582a330dd0f57 Mon Sep 17 00:00:00 2001 From: Uttkarsh Aggarwal Date: Thu, 25 May 2023 14:58:54 +0530 Subject: usb: gadget: f_fs: Add unbind event before functionfs_unbind commit efb6b535207395a5c7317993602e2503ca8cb4b3 upstream. While exercising the unbind path, with the current implementation the functionfs_unbind would be calling which waits for the ffs->mutex to be available, however within the same time ffs_ep0_read is invoked & if no setup packets are pending, it will invoke function wait_event_interruptible_exclusive_locked_irq which by definition waits for the ev.count to be increased inside the same mutex for which functionfs_unbind is waiting. This creates deadlock situation because the functionfs_unbind won't get the lock until ev.count is increased which can only happen if the caller ffs_func_unbind can proceed further. Following is the illustration: CPU1 CPU2 ffs_func_unbind() ffs_ep0_read() mutex_lock(ffs->mutex) wait_event(ffs->ev.count) functionfs_unbind() mutex_lock(ffs->mutex) mutex_unlock(ffs->mutex) ffs_event_add() Fix this by moving the event unbind before functionfs_unbind to ensure the ev.count is incrased properly. Fixes: 6a19da111057 ("usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait") Cc: stable Signed-off-by: Uttkarsh Aggarwal Link: https://lore.kernel.org/r/20230525092854.7992-1-quic_uaggarwa@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 8ad354741380..3e59055aa504 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3620,6 +3620,7 @@ static void ffs_func_unbind(struct usb_configuration *c, /* Drain any pending AIO completions */ drain_workqueue(ffs->io_completion_wq); + ffs_event_add(ffs, FUNCTIONFS_UNBIND); if (!--opts->refcnt) functionfs_unbind(ffs); @@ -3644,7 +3645,6 @@ static void ffs_func_unbind(struct usb_configuration *c, func->function.ssp_descriptors = NULL; func->interfaces_nums = NULL; - ffs_event_add(ffs, FUNCTIONFS_UNBIND); } static struct usb_function *ffs_alloc(struct usb_function_instance *fi) -- cgit 1.4.1 From cbfed647fd19131bf5e1a5e7bdabefde2dfebab2 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Wed, 24 May 2023 09:41:18 +0800 Subject: md/raid5: fix miscalculation of 'end_sector' in raid5_read_one_chunk() commit 8557dc27126949c702bd3aafe8a7e0b7e4fcb44c upstream. 'end_sector' is compared to 'rdev->recovery_offset', which is offset to rdev, however, commit e82ed3a4fbb5 ("md/raid6: refactor raid5_read_one_chunk") changes the calculation of 'end_sector' to offset to the array. Fix this miscalculation. Fixes: e82ed3a4fbb5 ("md/raid6: refactor raid5_read_one_chunk") Cc: stable@vger.kernel.org # v5.12+ Signed-off-by: Yu Kuai Reviewed-by: Christoph Hellwig Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230524014118.3172781-1-yukuai1@huaweicloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index f787c9e5b10e..fbef3c9badb6 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5516,7 +5516,7 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio) sector = raid5_compute_sector(conf, raid_bio->bi_iter.bi_sector, 0, &dd_idx, NULL); - end_sector = bio_end_sector(raid_bio); + end_sector = sector + bio_sectors(raid_bio); rcu_read_lock(); if (r5c_big_stripe_cached(conf, sector)) -- cgit 1.4.1 From 93f2aa05af2dd0a0a685b194fbb7cd7c9fa8651f Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Tue, 23 May 2023 16:25:49 +0100 Subject: misc: fastrpc: return -EPIPE to invocations on device removal commit b6a062853ddf6b4f653af2d8b75ba45bb9a036ad upstream. The return value is initialized as -1, or -EPERM. The completion of an invocation implies that the return value is set appropriately, but "Permission denied" does not accurately describe the outcome of the invocation. Set the invocation's return value to a more appropriate "Broken pipe", as the cleanup breaks the driver's connection with rpmsg. Fixes: c68cfb718c8f ("misc: fastrpc: Add support for context Invoke method") Cc: stable Signed-off-by: Richard Acayan Reviewed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230523152550.438363-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 02d26160c64e..b3e5a5d50e67 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2149,8 +2149,10 @@ static void fastrpc_notify_users(struct fastrpc_user *user) struct fastrpc_invoke_ctx *ctx; spin_lock(&user->lock); - list_for_each_entry(ctx, &user->pending, node) + list_for_each_entry(ctx, &user->pending, node) { + ctx->retval = -EPIPE; complete(&ctx->work); + } spin_unlock(&user->lock); } -- cgit 1.4.1 From 2e787e5153b7a43b09d8272999fda6375fed3686 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Tue, 23 May 2023 16:25:50 +0100 Subject: misc: fastrpc: reject new invocations during device removal commit 46248400d81e2aa0b65cd659d6f40188192a58b6 upstream. The channel's rpmsg object allows new invocations to be made. After old invocations are already interrupted, the driver shouldn't try to invoke anymore. Invalidating the rpmsg at the end of the driver removal function makes it easy to cause a race condition in userspace. Even closing a file descriptor before the driver finishes its cleanup can cause an invocation via fastrpc_release_current_dsp_process() and subsequent timeout. Invalidate the channel before the invocations are interrupted to make sure that no invocations can be created to hang after the device closes. Fixes: c68cfb718c8f ("misc: fastrpc: Add support for context Invoke method") Cc: stable Signed-off-by: Richard Acayan Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230523152550.438363-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index b3e5a5d50e67..8b1e8661c3d7 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2162,7 +2162,9 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev) struct fastrpc_user *user; unsigned long flags; + /* No invocations past this point */ spin_lock_irqsave(&cctx->lock, flags); + cctx->rpdev = NULL; list_for_each_entry(user, &cctx->users, user) fastrpc_notify_users(user); spin_unlock_irqrestore(&cctx->lock, flags); @@ -2175,7 +2177,6 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev) of_platform_depopulate(&rpdev->dev); - cctx->rpdev = NULL; fastrpc_channel_ctx_put(cctx); } -- cgit 1.4.1 From 63a44b01df36beb5f2d4ea93f35274a1f359c334 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 29 May 2023 12:50:34 -0700 Subject: scsi: stex: Fix gcc 13 warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6d074ce231772c66e648a61f6bd2245e7129d1f5 upstream. gcc 13 may assign another type to enumeration constants than gcc 12. Split the large enum at the top of source file stex.c such that the type of the constants used in time expressions is changed back to the same type chosen by gcc 12. This patch suppresses compiler warnings like this one: In file included from ./include/linux/bitops.h:7, from ./include/linux/kernel.h:22, from drivers/scsi/stex.c:13: drivers/scsi/stex.c: In function ‘stex_common_handshake’: ./include/linux/typecheck.h:12:25: error: comparison of distinct pointer types lacks a cast [-Werror] 12 | (void)(&__dummy == &__dummy2); \ | ^~ ./include/linux/jiffies.h:106:10: note: in expansion of macro ‘typecheck’ 106 | typecheck(unsigned long, b) && \ | ^~~~~~~~~ drivers/scsi/stex.c:1035:29: note: in expansion of macro ‘time_after’ 1035 | if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) { | ^~~~~~~~~~ See also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107405. Cc: stable@vger.kernel.org Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Bart Van Assche Link: https://lore.kernel.org/r/20230529195034.3077-1-bvanassche@acm.org Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/stex.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 8def242675ef..6b07f367918e 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -109,7 +109,9 @@ enum { TASK_ATTRIBUTE_HEADOFQUEUE = 0x1, TASK_ATTRIBUTE_ORDERED = 0x2, TASK_ATTRIBUTE_ACA = 0x4, +}; +enum { SS_STS_NORMAL = 0x80000000, SS_STS_DONE = 0x40000000, SS_STS_HANDSHAKE = 0x20000000, @@ -121,7 +123,9 @@ enum { SS_I2H_REQUEST_RESET = 0x2000, SS_MU_OPERATIONAL = 0x80000000, +}; +enum { STEX_CDB_LENGTH = 16, STATUS_VAR_LEN = 128, -- cgit 1.4.1 From 009886965e04cce16f810e19c807d89119ecc47d Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 22 May 2023 20:09:57 +0900 Subject: ata: libata-scsi: Use correct device no in ata_find_dev() commit 7f875850f20a42f488840c9df7af91ef7db2d576 upstream. For devices not attached to a port multiplier and managed directly by libata, the device number passed to ata_find_dev() must always be lower than the maximum number of devices returned by ata_link_max_devices(). That is 1 for SATA devices or 2 for an IDE link with master+slave devices. This device number is the SCSI device ID which matches these constraints as the IDs are generated per port and so never exceed the maximum number of devices for the link being used. However, for libsas managed devices, SCSI device IDs are assigned per struct scsi_host, leading to device IDs for SATA devices that can be well in excess of libata per-link maximum number of devices. This results in ata_find_dev() to always return NULL for libsas managed devices except for the first device of the target scsi_host with ID (device number) equal to 0. This issue is visible by executing the hdparm utility, which fails. E.g.: hdparm -i /dev/sdX /dev/sdX: HDIO_GET_IDENTITY failed: No message of desired type Fix this by rewriting ata_find_dev() to ignore the device number for non-PMP attached devices with a link with at most 1 device, that is SATA devices. For these, the device number 0 is always used to return the correct pointer to the struct ata_device of the port link. This change excludes IDE master/slave setups (maximum number of devices per link is 2) and port-multiplier attached devices. Also, to be consistant with the fact that SCSI device IDs and channel numbers used as device numbers are both unsigned int, change the devno argument of ata_find_dev() to unsigned int. Reported-by: Xingui Yang Fixes: 41bda9c98035 ("libata-link: update hotplug to handle PMP links") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Reviewed-by: Jason Yan Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-scsi.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 06a3d95ed8f9..39e1ff9b686b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2699,18 +2699,36 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) return 0; } -static struct ata_device *ata_find_dev(struct ata_port *ap, int devno) +static struct ata_device *ata_find_dev(struct ata_port *ap, unsigned int devno) { - if (!sata_pmp_attached(ap)) { - if (likely(devno >= 0 && - devno < ata_link_max_devices(&ap->link))) + /* + * For the non-PMP case, ata_link_max_devices() returns 1 (SATA case), + * or 2 (IDE master + slave case). However, the former case includes + * libsas hosted devices which are numbered per scsi host, leading + * to devno potentially being larger than 0 but with each struct + * ata_device having its own struct ata_port and struct ata_link. + * To accommodate these, ignore devno and always use device number 0. + */ + if (likely(!sata_pmp_attached(ap))) { + int link_max_devices = ata_link_max_devices(&ap->link); + + if (link_max_devices == 1) + return &ap->link.device[0]; + + if (devno < link_max_devices) return &ap->link.device[devno]; - } else { - if (likely(devno >= 0 && - devno < ap->nr_pmp_links)) - return &ap->pmp_link[devno].device[0]; + + return NULL; } + /* + * For PMP-attached devices, the device number corresponds to C + * (channel) of SCSI [H:C:I:L], indicating the port pmp link + * for the device. + */ + if (devno < ap->nr_pmp_links) + return &ap->pmp_link[devno].device[0]; + return NULL; } -- cgit 1.4.1 From 2f91f92bd870684173e1fa27927ae8ba99fe463c Mon Sep 17 00:00:00 2001 From: Ikshwaku Chauhan Date: Thu, 25 May 2023 10:57:26 +0530 Subject: drm/amdgpu: enable tmz by default for GC 11.0.1 commit 663b930e24842f3d3bb79418bb5cd8d01b40c559 upstream. Add IP GC 11.0.1 in the list of target to have tmz enabled by default. Signed-off-by: Ikshwaku Chauhan Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org # 6.1.x Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 02a4c93673ce..2bc791ed8830 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -526,6 +526,8 @@ void amdgpu_gmc_tmz_set(struct amdgpu_device *adev) case IP_VERSION(9, 3, 0): /* GC 10.3.7 */ case IP_VERSION(10, 3, 7): + /* GC 11.0.1 */ + case IP_VERSION(11, 0, 1): if (amdgpu_tmz == 0) { adev->gmc.tmz_enabled = false; dev_info(adev->dev, @@ -548,7 +550,6 @@ void amdgpu_gmc_tmz_set(struct amdgpu_device *adev) case IP_VERSION(10, 3, 1): /* YELLOW_CARP*/ case IP_VERSION(10, 3, 3): - case IP_VERSION(11, 0, 1): case IP_VERSION(11, 0, 4): /* Don't enable it by default yet. */ -- cgit 1.4.1 From 00abb872ef0f82dde131b9bd12096100bb24d9ba Mon Sep 17 00:00:00 2001 From: Tim Huang Date: Sun, 21 May 2023 09:24:00 +0800 Subject: drm/amd/pm: reverse mclk and fclk clocks levels for SMU v13.0.4 commit 6a07826f2057b5fa1c479ba56460195882464270 upstream. This patch reverses the DPM clocks levels output of pp_dpm_mclk and pp_dpm_fclk. On dGPUs and older APUs we expose the levels from lowest clocks to highest clocks. But for some APUs, the clocks levels that from the DFPstateTable are given the reversed orders by PMFW. Like the memory DPM clocks that are exposed by pp_dpm_mclk. It's not intuitive that they are reversed on these APUs. All tools and software that talks to the driver then has to know different ways to interpret the data depending on the asic. So we need to reverse them to expose the clocks levels from the driver consistently. Signed-off-by: Tim Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c index 8fa9a36c38b6..6d9760eac16d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c @@ -478,7 +478,7 @@ static int smu_v13_0_4_get_dpm_level_count(struct smu_context *smu, static int smu_v13_0_4_print_clk_levels(struct smu_context *smu, enum smu_clk_type clk_type, char *buf) { - int i, size = 0, ret = 0; + int i, idx, size = 0, ret = 0; uint32_t cur_value = 0, value = 0, count = 0; uint32_t min, max; @@ -512,7 +512,8 @@ static int smu_v13_0_4_print_clk_levels(struct smu_context *smu, break; for (i = 0; i < count; i++) { - ret = smu_v13_0_4_get_dpm_freq_by_index(smu, clk_type, i, &value); + idx = (clk_type == SMU_FCLK || clk_type == SMU_MCLK) ? (count - i - 1) : i; + ret = smu_v13_0_4_get_dpm_freq_by_index(smu, clk_type, idx, &value); if (ret) break; -- cgit 1.4.1 From e0a0f5d2ba593bbf155a41890f675390fd312e2c Mon Sep 17 00:00:00 2001 From: Tim Huang Date: Sun, 21 May 2023 11:10:19 +0800 Subject: drm/amd/pm: reverse mclk and fclk clocks levels for vangogh commit bfc03568d9d81332382c73a1985a90c4506bd36c upstream. This patch reverses the DPM clocks levels output of pp_dpm_mclk and pp_dpm_fclk. On dGPUs and older APUs we expose the levels from lowest clocks to highest clocks. But for some APUs, the clocks levels that from the DFPstateTable are given the reversed orders by PMFW. Like the memory DPM clocks that are exposed by pp_dpm_mclk. It's not intuitive that they are reversed on these APUs. All tools and software that talks to the driver then has to know different ways to interpret the data depending on the asic. So we need to reverse them to expose the clocks levels from the driver consistently. Signed-off-by: Tim Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index cb10c7e31264..1b731a9c92d9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -580,7 +580,7 @@ static int vangogh_print_legacy_clk_levels(struct smu_context *smu, DpmClocks_t *clk_table = smu->smu_table.clocks_table; SmuMetrics_legacy_t metrics; struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm); - int i, size = 0, ret = 0; + int i, idx, size = 0, ret = 0; uint32_t cur_value = 0, value = 0, count = 0; bool cur_value_match_level = false; @@ -654,7 +654,8 @@ static int vangogh_print_legacy_clk_levels(struct smu_context *smu, case SMU_MCLK: case SMU_FCLK: for (i = 0; i < count; i++) { - ret = vangogh_get_dpm_clk_limited(smu, clk_type, i, &value); + idx = (clk_type == SMU_FCLK || clk_type == SMU_MCLK) ? (count - i - 1) : i; + ret = vangogh_get_dpm_clk_limited(smu, clk_type, idx, &value); if (ret) return ret; if (!value) @@ -681,7 +682,7 @@ static int vangogh_print_clk_levels(struct smu_context *smu, DpmClocks_t *clk_table = smu->smu_table.clocks_table; SmuMetrics_t metrics; struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm); - int i, size = 0, ret = 0; + int i, idx, size = 0, ret = 0; uint32_t cur_value = 0, value = 0, count = 0; bool cur_value_match_level = false; uint32_t min, max; @@ -763,7 +764,8 @@ static int vangogh_print_clk_levels(struct smu_context *smu, case SMU_MCLK: case SMU_FCLK: for (i = 0; i < count; i++) { - ret = vangogh_get_dpm_clk_limited(smu, clk_type, i, &value); + idx = (clk_type == SMU_FCLK || clk_type == SMU_MCLK) ? (count - i - 1) : i; + ret = vangogh_get_dpm_clk_limited(smu, clk_type, idx, &value); if (ret) return ret; if (!value) -- cgit 1.4.1 From 0f8f233ed76754b0c9262eb2e82f8529da0bef16 Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Wed, 24 May 2023 15:03:02 +0800 Subject: drm/amd/pm: resolve reboot exception for si oland commit e490d60a2f76bff636c68ce4fe34c1b6c34bbd86 upstream. During reboot test on arm64 platform, it may failure on boot. The error message are as follows: [ 1.706570][ 3] [ T273] [drm:si_thermal_enable_alert [amdgpu]] *ERROR* Could not enable thermal interrupts. [ 1.716547][ 3] [ T273] [drm:amdgpu_device_ip_late_init [amdgpu]] *ERROR* late_init of IP block failed -22 [ 1.727064][ 3] [ T273] amdgpu 0000:02:00.0: amdgpu_device_ip_late_init failed [ 1.734367][ 3] [ T273] amdgpu 0000:02:00.0: Fatal error during GPU init v2: squash in built warning fix (Alex) Signed-off-by: Zhenneng Li Reviewed-by: Guchun Chen Signed-off-by: Guchun Chen Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 49c398ec0aaf..c89cfef7cafa 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -6925,23 +6925,6 @@ static int si_dpm_enable(struct amdgpu_device *adev) return 0; } -static int si_set_temperature_range(struct amdgpu_device *adev) -{ - int ret; - - ret = si_thermal_enable_alert(adev, false); - if (ret) - return ret; - ret = si_thermal_set_temperature_range(adev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); - if (ret) - return ret; - ret = si_thermal_enable_alert(adev, true); - if (ret) - return ret; - - return ret; -} - static void si_dpm_disable(struct amdgpu_device *adev) { struct rv7xx_power_info *pi = rv770_get_pi(adev); @@ -7626,18 +7609,6 @@ static int si_dpm_process_interrupt(struct amdgpu_device *adev, static int si_dpm_late_init(void *handle) { - int ret; - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - if (!adev->pm.dpm_enabled) - return 0; - - ret = si_set_temperature_range(adev); - if (ret) - return ret; -#if 0 //TODO ? - si_dpm_powergate_uvd(adev, true); -#endif return 0; } -- cgit 1.4.1 From 1c729bd5b30fe8178c53aeebf6503ea38966f769 Mon Sep 17 00:00:00 2001 From: Tim Huang Date: Sun, 21 May 2023 10:28:05 +0800 Subject: drm/amd/pm: reverse mclk clocks levels for SMU v13.0.5 commit c1d35412b3e826ae8119e3fb5f51dd0fa5b6b567 upstream. This patch reverses the DPM clocks levels output of pp_dpm_mclk. On dGPUs and older APUs we expose the levels from lowest clocks to highest clocks. But for some APUs, the clocks levels that from the DFPstateTable are given the reversed orders by PMFW. Like the memory DPM clocks that are exposed by pp_dpm_mclk. It's not intuitive that they are reversed on these APUs. All tools and software that talks to the driver then has to know different ways to interpret the data depending on the asic. So we need to reverse them to expose the clocks levels from the driver consistently. Signed-off-by: Tim Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c index 66445964efbd..0081fa607e02 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c @@ -866,7 +866,7 @@ out: static int smu_v13_0_5_print_clk_levels(struct smu_context *smu, enum smu_clk_type clk_type, char *buf) { - int i, size = 0, ret = 0; + int i, idx, size = 0, ret = 0; uint32_t cur_value = 0, value = 0, count = 0; uint32_t min = 0, max = 0; @@ -898,7 +898,8 @@ static int smu_v13_0_5_print_clk_levels(struct smu_context *smu, goto print_clk_out; for (i = 0; i < count; i++) { - ret = smu_v13_0_5_get_dpm_freq_by_index(smu, clk_type, i, &value); + idx = (clk_type == SMU_MCLK) ? (count - i - 1) : i; + ret = smu_v13_0_5_get_dpm_freq_by_index(smu, clk_type, idx, &value); if (ret) goto print_clk_out; -- cgit 1.4.1 From f05f3fcc782445b707b998859d9a03410ecbdb73 Mon Sep 17 00:00:00 2001 From: Tim Huang Date: Sun, 21 May 2023 10:35:59 +0800 Subject: drm/amd/pm: reverse mclk and fclk clocks levels for yellow carp commit f1373a97a41f429e0095d4be388092ffa3c1a157 upstream. This patch reverses the DPM clocks levels output of pp_dpm_mclk and pp_dpm_fclk. On dGPUs and older APUs we expose the levels from lowest clocks to highest clocks. But for some APUs, the clocks levels that from the DFPstateTable are given the reversed orders by PMFW. Like the memory DPM clocks that are exposed by pp_dpm_mclk. It's not intuitive that they are reversed on these APUs. All tools and software that talks to the driver then has to know different ways to interpret the data depending on the asic. So we need to reverse them to expose the clocks levels from the driver consistently. Signed-off-by: Tim Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c index 04e56b0b3033..798f36cfcebd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c @@ -1000,7 +1000,7 @@ out: static int yellow_carp_print_clk_levels(struct smu_context *smu, enum smu_clk_type clk_type, char *buf) { - int i, size = 0, ret = 0; + int i, idx, size = 0, ret = 0; uint32_t cur_value = 0, value = 0, count = 0; uint32_t min, max; @@ -1033,7 +1033,8 @@ static int yellow_carp_print_clk_levels(struct smu_context *smu, goto print_clk_out; for (i = 0; i < count; i++) { - ret = yellow_carp_get_dpm_freq_by_index(smu, clk_type, i, &value); + idx = (clk_type == SMU_FCLK || clk_type == SMU_MCLK) ? (count - i - 1) : i; + ret = yellow_carp_get_dpm_freq_by_index(smu, clk_type, idx, &value); if (ret) goto print_clk_out; -- cgit 1.4.1 From 8db2ea7b80d6539c2c3d4e2ca96ca1bc56b982d9 Mon Sep 17 00:00:00 2001 From: Tim Huang Date: Mon, 22 May 2023 23:17:28 +0800 Subject: drm/amd/pm: reverse mclk and fclk clocks levels for renoir commit 55e02c14f9b5fd973ba32a16a715baa42617f9c6 upstream. This patch reverses the DPM clocks levels output of pp_dpm_mclk and pp_dpm_fclk for renoir. On dGPUs and older APUs we expose the levels from lowest clocks to highest clocks. But for some APUs, the clocks levels are given the reversed orders by PMFW. Like the memory DPM clocks that are exposed by pp_dpm_mclk. It's not intuitive that they are reversed on these APUs. All tools and software that talks to the driver then has to know different ways to interpret the data depending on the asic. So we need to reverse them to expose the clocks levels from the driver consistently. Signed-off-by: Tim Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index 5cdc07165480..8a8ba25c9ad7 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -494,7 +494,7 @@ static int renoir_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) static int renoir_print_clk_levels(struct smu_context *smu, enum smu_clk_type clk_type, char *buf) { - int i, size = 0, ret = 0; + int i, idx, size = 0, ret = 0; uint32_t cur_value = 0, value = 0, count = 0, min = 0, max = 0; SmuMetrics_t metrics; struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm); @@ -594,7 +594,8 @@ static int renoir_print_clk_levels(struct smu_context *smu, case SMU_VCLK: case SMU_DCLK: for (i = 0; i < count; i++) { - ret = renoir_get_dpm_clk_limited(smu, clk_type, i, &value); + idx = (clk_type == SMU_FCLK || clk_type == SMU_MCLK) ? (count - i - 1) : i; + ret = renoir_get_dpm_clk_limited(smu, clk_type, idx, &value); if (ret) return ret; if (!value) -- cgit 1.4.1 From 03974abbf2a1dc99d00cadbeb1664c7711e3909a Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 9 Feb 2023 08:22:17 +0100 Subject: x86/mtrr: Revert 90b926e68f50 ("x86/pat: Fix pat_x_mtrr_type() for MTRR disabled case") commit f9f57da2c2d119dbf109e3f6e1ceab7659294046 upstream. Commit 90b926e68f50 ("x86/pat: Fix pat_x_mtrr_type() for MTRR disabled case") broke the use case of running Xen dom0 kernels on machines with an external disk enclosure attached via USB, see Link tag. What this commit was originally fixing - SEV-SNP guests on Hyper-V - is a more specialized situation which has other issues at the moment anyway so reverting this now and addressing the issue properly later is the prudent thing to do. So revert it in time for the 6.2 proper release. [ bp: Rewrite commit message. ] Reported-by: Christian Kujau Tested-by: Christian Kujau Signed-off-by: Juergen Gross Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/r/4fe9541e-4d4c-2b2a-f8c8-2d34a7284930@nerdbynature.de Cc: Jason Andryuk Signed-off-by: Greg Kroah-Hartman --- arch/x86/mm/pat/memtype.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/mm/pat/memtype.c b/arch/x86/mm/pat/memtype.c index 2642bc4c8ec0..66a209f7eb86 100644 --- a/arch/x86/mm/pat/memtype.c +++ b/arch/x86/mm/pat/memtype.c @@ -434,8 +434,7 @@ static unsigned long pat_x_mtrr_type(u64 start, u64 end, u8 mtrr_type, uniform; mtrr_type = mtrr_type_lookup(start, end, &uniform); - if (mtrr_type != MTRR_TYPE_WRBACK && - mtrr_type != MTRR_TYPE_INVALID) + if (mtrr_type != MTRR_TYPE_WRBACK) return _PAGE_CACHE_MODE_UC_MINUS; return _PAGE_CACHE_MODE_WB; -- cgit 1.4.1 From f25568e0801c66fd462e84f33e3b97fc1137b4f9 Mon Sep 17 00:00:00 2001 From: Deren Wu Date: Sat, 13 May 2023 22:48:15 +0800 Subject: mmc: vub300: fix invalid response handling commit a99d21cefd351c8aaa20b83a3c942340e5789d45 upstream. We may get an empty response with zero length at the beginning of the driver start and get following UBSAN error. Since there is no content(SDRT_NONE) for the response, just return and skip the response handling to avoid this problem. Test pass : SDIO wifi throughput test with this patch [ 126.980684] UBSAN: array-index-out-of-bounds in drivers/mmc/host/vub300.c:1719:12 [ 126.980709] index -1 is out of range for type 'u32 [4]' [ 126.980729] CPU: 4 PID: 9 Comm: kworker/u16:0 Tainted: G E 6.3.0-rc4-mtk-local-202304272142 #1 [ 126.980754] Hardware name: Intel(R) Client Systems NUC8i7BEH/NUC8BEB, BIOS BECFL357.86A.0081.2020.0504.1834 05/04/2020 [ 126.980770] Workqueue: kvub300c vub300_cmndwork_thread [vub300] [ 126.980833] Call Trace: [ 126.980845] [ 126.980860] dump_stack_lvl+0x48/0x70 [ 126.980895] dump_stack+0x10/0x20 [ 126.980916] ubsan_epilogue+0x9/0x40 [ 126.980944] __ubsan_handle_out_of_bounds+0x70/0x90 [ 126.980979] vub300_cmndwork_thread+0x58e7/0x5e10 [vub300] [ 126.981018] ? _raw_spin_unlock+0x18/0x40 [ 126.981042] ? finish_task_switch+0x175/0x6f0 [ 126.981070] ? __switch_to+0x42e/0xda0 [ 126.981089] ? __switch_to_asm+0x3a/0x80 [ 126.981129] ? __pfx_vub300_cmndwork_thread+0x10/0x10 [vub300] [ 126.981174] ? __kasan_check_read+0x11/0x20 [ 126.981204] process_one_work+0x7ee/0x13d0 [ 126.981246] worker_thread+0x53c/0x1240 [ 126.981291] kthread+0x2b8/0x370 [ 126.981312] ? __pfx_worker_thread+0x10/0x10 [ 126.981336] ? __pfx_kthread+0x10/0x10 [ 126.981359] ret_from_fork+0x29/0x50 [ 126.981400] Fixes: 88095e7b473a ("mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver") Signed-off-by: Deren Wu Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/048cd6972c50c33c2e8f81d5228fed928519918b.1683987673.git.deren.wu@mediatek.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/vub300.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 72f65f32abbc..7dc0e91dabfc 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -1715,6 +1715,9 @@ static void construct_request_response(struct vub300_mmc_host *vub300, int bytes = 3 & less_cmd; int words = less_cmd >> 2; u8 *r = vub300->resp.response.command_response; + + if (!resp_len) + return; if (bytes == 3) { cmd->resp[words] = (r[1 + (words << 2)] << 24) | (r[2 + (words << 2)] << 16) -- cgit 1.4.1 From 2c8aa1163a21ac28c824a10b5dad90dc3ae3169e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 13 May 2023 21:23:52 +0200 Subject: mmc: pwrseq: sd8787: Fix WILC CHIP_EN and RESETN toggling order commit 0b5d5c436a5c572a45f976cfd34a6741e143e5d9 upstream. Chapter "5.3 Power-Up/Down Sequence" of WILC1000 [1] and WILC3000 [2] states that CHIP_EN must be pulled HIGH first, RESETN second. Fix the order of these signals in the driver. Use the mmc_pwrseq_ops as driver data as the delay between signals is specific to SDIO card type anyway. [1] https://ww1.microchip.com/downloads/aemDocuments/documents/WSG/ProductDocuments/DataSheets/ATWILC1000-MR110XB-IEEE-802.11-b-g-n-Link-Controller-Module-DS70005326E.pdf [2] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/IEEE-802.11-b-g-n-Link-Controller-Module-with-Integrated-Bluetooth-5.0-DS70005327B.pdf Fixes: b2832b96fcf5 ("mmc: pwrseq: sd8787: add support for wilc1000") Signed-off-by: Marek Vasut Reviewed-by: Claudiu Beznea Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230513192352.479627-1-marex@denx.de Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/pwrseq_sd8787.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/pwrseq_sd8787.c b/drivers/mmc/core/pwrseq_sd8787.c index 2e120ad83020..0c5f5e371e1f 100644 --- a/drivers/mmc/core/pwrseq_sd8787.c +++ b/drivers/mmc/core/pwrseq_sd8787.c @@ -28,7 +28,6 @@ struct mmc_pwrseq_sd8787 { struct mmc_pwrseq pwrseq; struct gpio_desc *reset_gpio; struct gpio_desc *pwrdn_gpio; - u32 reset_pwrdwn_delay_ms; }; #define to_pwrseq_sd8787(p) container_of(p, struct mmc_pwrseq_sd8787, pwrseq) @@ -39,7 +38,7 @@ static void mmc_pwrseq_sd8787_pre_power_on(struct mmc_host *host) gpiod_set_value_cansleep(pwrseq->reset_gpio, 1); - msleep(pwrseq->reset_pwrdwn_delay_ms); + msleep(300); gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1); } @@ -51,17 +50,37 @@ static void mmc_pwrseq_sd8787_power_off(struct mmc_host *host) gpiod_set_value_cansleep(pwrseq->reset_gpio, 0); } +static void mmc_pwrseq_wilc1000_pre_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq); + + /* The pwrdn_gpio is really CHIP_EN, reset_gpio is RESETN */ + gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1); + msleep(5); + gpiod_set_value_cansleep(pwrseq->reset_gpio, 1); +} + +static void mmc_pwrseq_wilc1000_power_off(struct mmc_host *host) +{ + struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq); + + gpiod_set_value_cansleep(pwrseq->reset_gpio, 0); + gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 0); +} + static const struct mmc_pwrseq_ops mmc_pwrseq_sd8787_ops = { .pre_power_on = mmc_pwrseq_sd8787_pre_power_on, .power_off = mmc_pwrseq_sd8787_power_off, }; -static const u32 sd8787_delay_ms = 300; -static const u32 wilc1000_delay_ms = 5; +static const struct mmc_pwrseq_ops mmc_pwrseq_wilc1000_ops = { + .pre_power_on = mmc_pwrseq_wilc1000_pre_power_on, + .power_off = mmc_pwrseq_wilc1000_power_off, +}; static const struct of_device_id mmc_pwrseq_sd8787_of_match[] = { - { .compatible = "mmc-pwrseq-sd8787", .data = &sd8787_delay_ms }, - { .compatible = "mmc-pwrseq-wilc1000", .data = &wilc1000_delay_ms }, + { .compatible = "mmc-pwrseq-sd8787", .data = &mmc_pwrseq_sd8787_ops }, + { .compatible = "mmc-pwrseq-wilc1000", .data = &mmc_pwrseq_wilc1000_ops }, {/* sentinel */}, }; MODULE_DEVICE_TABLE(of, mmc_pwrseq_sd8787_of_match); @@ -77,7 +96,6 @@ static int mmc_pwrseq_sd8787_probe(struct platform_device *pdev) return -ENOMEM; match = of_match_node(mmc_pwrseq_sd8787_of_match, pdev->dev.of_node); - pwrseq->reset_pwrdwn_delay_ms = *(u32 *)match->data; pwrseq->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(pwrseq->pwrdn_gpio)) @@ -88,7 +106,7 @@ static int mmc_pwrseq_sd8787_probe(struct platform_device *pdev) return PTR_ERR(pwrseq->reset_gpio); pwrseq->pwrseq.dev = dev; - pwrseq->pwrseq.ops = &mmc_pwrseq_sd8787_ops; + pwrseq->pwrseq.ops = match->data; pwrseq->pwrseq.owner = THIS_MODULE; platform_set_drvdata(pdev, pwrseq); -- cgit 1.4.1 From 380d2da555a817622d63b8e5f7ffe38f7790bd09 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Fri, 19 May 2023 17:47:51 +0800 Subject: tty: serial: fsl_lpuart: use UARTCTRL_TXINV to send break instead of UARTCTRL_SBK commit 2474e05467c00f7d51af3039b664de6886325257 upstream. LPUART IP now has two known bugs, one is that CTS has higher priority than the break signal, which causes the break signal sending through UARTCTRL_SBK may impacted by the CTS input if the HW flow control is enabled. It exists on all platforms we support in this driver. So we add a workaround patch for this issue: commit c4c81db5cf8b ("tty: serial: fsl_lpuart: disable the CTS when send break signal"). Another IP bug is i.MX8QM LPUART may have an additional break character being sent after SBK was cleared. It may need to add some delay between clearing SBK and re-enabling CTS to ensure that the SBK latch are completely cleared. But we found that during the delay period before CTS is enabled, there is still a risk that Bluetooth data in TX FIFO may be sent out during this period because of break off and CTS disabled(even if BT sets CTS line deasserted, data is still sent to BT). Due to this risk, we have to drop the CTS-disabling workaround for SBK bugs, use TXINV seems to be a better way to replace SBK feature and avoid above risk. Also need to disable the transmitter to prevent any data from being sent out during break, then invert the TX line to send break. Then disable the TXINV when turn off break and re-enable transmitter. Fixes: c4c81db5cf8b ("tty: serial: fsl_lpuart: disable the CTS when send break signal") Cc: stable Signed-off-by: Sherry Sun Link: https://lore.kernel.org/r/20230519094751.28948-1-sherry.sun@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 44 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 81467e93c7d5..dc7ac1ddbca5 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1475,34 +1475,36 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state) static void lpuart32_break_ctl(struct uart_port *port, int break_state) { - unsigned long temp, modem; - struct tty_struct *tty; - unsigned int cflag = 0; - - tty = tty_port_tty_get(&port->state->port); - if (tty) { - cflag = tty->termios.c_cflag; - tty_kref_put(tty); - } + unsigned long temp; - temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK; - modem = lpuart32_read(port, UARTMODIR); + temp = lpuart32_read(port, UARTCTRL); + /* + * LPUART IP now has two known bugs, one is CTS has higher priority than the + * break signal, which causes the break signal sending through UARTCTRL_SBK + * may impacted by the CTS input if the HW flow control is enabled. It + * exists on all platforms we support in this driver. + * Another bug is i.MX8QM LPUART may have an additional break character + * being sent after SBK was cleared. + * To avoid above two bugs, we use Transmit Data Inversion function to send + * the break signal instead of UARTCTRL_SBK. + */ if (break_state != 0) { - temp |= UARTCTRL_SBK; /* - * LPUART CTS has higher priority than SBK, need to disable CTS before - * asserting SBK to avoid any interference if flow control is enabled. + * Disable the transmitter to prevent any data from being sent out + * during break, then invert the TX line to send break. */ - if (cflag & CRTSCTS && modem & UARTMODIR_TXCTSE) - lpuart32_write(port, modem & ~UARTMODIR_TXCTSE, UARTMODIR); + temp &= ~UARTCTRL_TE; + lpuart32_write(port, temp, UARTCTRL); + temp |= UARTCTRL_TXINV; + lpuart32_write(port, temp, UARTCTRL); } else { - /* Re-enable the CTS when break off. */ - if (cflag & CRTSCTS && !(modem & UARTMODIR_TXCTSE)) - lpuart32_write(port, modem | UARTMODIR_TXCTSE, UARTMODIR); + /* Disable the TXINV to turn off break and re-enable transmitter. */ + temp &= ~UARTCTRL_TXINV; + lpuart32_write(port, temp, UARTCTRL); + temp |= UARTCTRL_TE; + lpuart32_write(port, temp, UARTCTRL); } - - lpuart32_write(port, temp, UARTCTRL); } static void lpuart_setup_watermark(struct lpuart_port *sport) -- cgit 1.4.1 From 1af8dd54034736b59d34eeb5c47c13e325108126 Mon Sep 17 00:00:00 2001 From: pengfuyuan Date: Tue, 23 May 2023 15:09:55 +0800 Subject: btrfs: fix csum_tree_block page iteration to avoid tripping on -Werror=array-bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5ad9b4719fc9bc4715c7e19875a962095b0577e7 upstream. When compiling on a MIPS 64-bit machine we get these warnings: In file included from ./arch/mips/include/asm/cacheflush.h:13, from ./include/linux/cacheflush.h:5, from ./include/linux/highmem.h:8, from ./include/linux/bvec.h:10, from ./include/linux/blk_types.h:10, from ./include/linux/blkdev.h:9, from fs/btrfs/disk-io.c:7: fs/btrfs/disk-io.c: In function ‘csum_tree_block’: fs/btrfs/disk-io.c:100:34: error: array subscript 1 is above array bounds of ‘struct page *[1]’ [-Werror=array-bounds] 100 | kaddr = page_address(buf->pages[i]); | ~~~~~~~~~~^~~ ./include/linux/mm.h:2135:48: note: in definition of macro ‘page_address’ 2135 | #define page_address(page) lowmem_page_address(page) | ^~~~ cc1: all warnings being treated as errors We can check if i overflows to solve the problem. However, this doesn't make much sense, since i == 1 and num_pages == 1 doesn't execute the body of the loop. In addition, i < num_pages can also ensure that buf->pages[i] will not cross the boundary. Unfortunately, this doesn't help with the problem observed here: gcc still complains. To fix this add a compile-time condition for the extent buffer page array size limit, which would eventually lead to eliminating the whole for loop. CC: stable@vger.kernel.org # 5.10+ Signed-off-by: pengfuyuan Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/disk-io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 21d8a895e9ad..ca69a46f1006 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -104,7 +104,7 @@ static void csum_tree_block(struct extent_buffer *buf, u8 *result) crypto_shash_update(shash, kaddr + BTRFS_CSUM_SIZE, first_page_part - BTRFS_CSUM_SIZE); - for (i = 1; i < num_pages; i++) { + for (i = 1; i < num_pages && INLINE_EXTENT_BUFFER_PAGES > 1; i++) { kaddr = page_address(buf->pages[i]); crypto_shash_update(shash, kaddr, PAGE_SIZE); } -- cgit 1.4.1 From 5daf7a171d27f581d212cdf1bdb97e304facbdba Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 2 May 2023 12:38:09 +0200 Subject: phy: qcom-qmp-combo: fix init-count imbalance commit 9bf03a0cbd80a256bc1e1c4bcc80bc2b06b8b2b9 upstream. The init counter is not decremented on initialisation errors, which prevents retrying initialisation and can lead to the runtime suspend callback attempting to disable resources that have never been enabled. Add the missing decrement on initialisation errors so that the counter reflects the state of the device. Fixes: e78f3d15e115 ("phy: qcom-qmp: new qmp phy driver for qcom-chipsets") Cc: stable@vger.kernel.org # 4.12 Signed-off-by: Johan Hovold Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230502103810.12061-2-johan+linaro@kernel.org Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 816829105135..8c527c4927fd 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -1993,7 +1993,7 @@ static int qmp_combo_com_init(struct qmp_phy *qphy) ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); if (ret) { dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); - goto err_unlock; + goto err_decrement_count; } ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); @@ -2048,7 +2048,8 @@ err_assert_reset: reset_control_bulk_assert(cfg->num_resets, qmp->resets); err_disable_regulators: regulator_bulk_disable(cfg->num_vregs, qmp->vregs); -err_unlock: +err_decrement_count: + qmp->init_count--; mutex_unlock(&qmp->phy_mutex); return ret; -- cgit 1.4.1 From 867ad8cba251b4f8f08be398955727b0fb47455c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 2 May 2023 12:38:10 +0200 Subject: phy: qcom-qmp-pcie-msm8996: fix init-count imbalance commit e42f110700ed7293700c26145e1ed07ea05ac3f6 upstream. The init counter is not decremented on initialisation errors, which prevents retrying initialisation. Add the missing decrement on initialisation errors so that the counter reflects the state of the device. Fixes: e78f3d15e115 ("phy: qcom-qmp: new qmp phy driver for qcom-chipsets") Cc: stable@vger.kernel.org # 4.12 Signed-off-by: Johan Hovold Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230502103810.12061-3-johan+linaro@kernel.org Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c index 461f0b5d464a..31c223a54394 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c @@ -425,7 +425,7 @@ static int qmp_pcie_msm8996_com_init(struct qmp_phy *qphy) ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); if (ret) { dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); - goto err_unlock; + goto err_decrement_count; } ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); @@ -455,7 +455,8 @@ err_assert_reset: reset_control_bulk_assert(cfg->num_resets, qmp->resets); err_disable_regulators: regulator_bulk_disable(cfg->num_vregs, qmp->vregs); -err_unlock: +err_decrement_count: + qmp->init_count--; mutex_unlock(&qmp->phy_mutex); return ret; -- cgit 1.4.1 From 2a72e6814f557bcb85eda68bc2dc6d513b8eda34 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 29 May 2023 16:32:37 +0900 Subject: block: fix revalidate performance regression commit 47fe1c3064c6bc1bfa3c032ff78e603e5dd6e5bc upstream. The scsi driver function sd_read_block_characteristics() always calls disk_set_zoned() to a disk zoned model correctly, in case the device model changed. This is done even for regular disks to set the zoned model to BLK_ZONED_NONE and free any zone related resources if the drive previously was zoned. This behavior significantly impact the time it takes to revalidate disks on a large system as the call to disk_clear_zone_settings() done from disk_set_zoned() for the BLK_ZONED_NONE case results in the device request queued to be frozen, even if there are no zone resources to free. Avoid this overhead for non-zoned devices by not calling disk_clear_zone_settings() in disk_set_zoned() if the device model was already set to BLK_ZONED_NONE, which is always the case for regular devices. Reported by: Brian Bunker Fixes: 508aebb80527 ("block: introduce blk_queue_clear_zone_settings()") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Reviewed-by: Ming Lei Link: https://lore.kernel.org/r/20230529073237.1339862-1-dlemoal@kernel.org Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-settings.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/blk-settings.c b/block/blk-settings.c index 8ac1038d0c79..291cf9df7fc2 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -909,6 +909,7 @@ static bool disk_has_partitions(struct gendisk *disk) void disk_set_zoned(struct gendisk *disk, enum blk_zoned_model model) { struct request_queue *q = disk->queue; + unsigned int old_model = q->limits.zoned; switch (model) { case BLK_ZONED_HM: @@ -946,7 +947,7 @@ void disk_set_zoned(struct gendisk *disk, enum blk_zoned_model model) */ blk_queue_zone_write_granularity(q, queue_logical_block_size(q)); - } else { + } else if (old_model != BLK_ZONED_NONE) { disk_clear_zone_settings(disk); } } -- cgit 1.4.1 From 251cf7fd5a1bb1740843dbf3b00fc157f17bc766 Mon Sep 17 00:00:00 2001 From: Gaurav Batra Date: Thu, 25 May 2023 09:34:54 -0500 Subject: powerpc/iommu: Limit number of TCEs to 512 for H_STUFF_TCE hcall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9d2ccf00bddc268045e3d65a8108d61ada0e4b4e upstream. Currently in tce_freemulti_pSeriesLP() there is no limit on how many TCEs are passed to the H_STUFF_TCE hcall. This has not caused an issue until now, but newer firmware releases have started enforcing a limit of 512 TCEs per call. The limit is correct per the specification (PAPR v2.12 § 14.5.4.2.3). The code has been in it's current form since it was initially merged. Cc: stable@vger.kernel.org Signed-off-by: Gaurav Batra Reviewed-by: Brian King [mpe: Tweak change log wording & add PAPR reference] Signed-off-by: Michael Ellerman Link: https://msgid.link/20230525143454.56878-1-gbatra@linux.vnet.ibm.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/iommu.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index efba867017e2..97b026130c71 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -311,13 +311,22 @@ static void tce_free_pSeriesLP(unsigned long liobn, long tcenum, long tceshift, static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) { u64 rc; + long rpages = npages; + unsigned long limit; if (!firmware_has_feature(FW_FEATURE_STUFF_TCE)) return tce_free_pSeriesLP(tbl->it_index, tcenum, tbl->it_page_shift, npages); - rc = plpar_tce_stuff((u64)tbl->it_index, - (u64)tcenum << tbl->it_page_shift, 0, npages); + do { + limit = min_t(unsigned long, rpages, 512); + + rc = plpar_tce_stuff((u64)tbl->it_index, + (u64)tcenum << tbl->it_page_shift, 0, limit); + + rpages -= limit; + tcenum += limit; + } while (rpages > 0 && !rc); if (rc && printk_ratelimit()) { printk("tce_freemulti_pSeriesLP: plpar_tce_stuff failed\n"); -- cgit 1.4.1 From b0e21c42c1fbda7593df5900ca11ef128e777528 Mon Sep 17 00:00:00 2001 From: Jon Pan-Doh Date: Wed, 26 Apr 2023 13:32:56 -0700 Subject: iommu/amd: Fix domain flush size when syncing iotlb commit 2212fc2acf3f6ee690ea36506fb882a19d1bfcab upstream. When running on an AMD vIOMMU, we observed multiple invalidations (of decreasing power of 2 aligned sizes) when unmapping a single page. Domain flush takes gather bounds (end-start) as size param. However, gather->end is defined as the last inclusive address (start + size - 1). This leads to an off by 1 error. With this patch, verified that 1 invalidation occurs when unmapping a single page. Fixes: a270be1b3fdf ("iommu/amd: Use only natural aligned flushes in a VM") Cc: stable@vger.kernel.org # >= 5.15 Signed-off-by: Jon Pan-Doh Tested-by: Sudheer Dantuluri Suggested-by: Gary Zibrat Reviewed-by: Vasant Hegde Acked-by: Nadav Amit Link: https://lore.kernel.org/r/20230426203256.237116-1-pandoh@google.com Signed-off-by: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/amd/iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 16c5d1b97b56..b4852d79e980 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -2396,7 +2396,7 @@ static void amd_iommu_iotlb_sync(struct iommu_domain *domain, unsigned long flags; spin_lock_irqsave(&dom->lock, flags); - domain_flush_pages(dom, gather->start, gather->end - gather->start, 1); + domain_flush_pages(dom, gather->start, gather->end - gather->start + 1, 1); amd_iommu_domain_flush_complete(dom); spin_unlock_irqrestore(&dom->lock, flags); } -- cgit 1.4.1 From c40dc6e266462d3c684c1cec956000f409b3c490 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Tue, 30 May 2023 18:41:16 +0200 Subject: tpm, tpm_tis: correct tpm_tis_flags enumeration values commit 4ecd704a4c51fd95973fcc3a60444e0e24eb9439 upstream. With commit 858e8b792d06 ("tpm, tpm_tis: Avoid cache incoherency in test for interrupts") bit accessor functions are used to access flags in tpm_tis_data->flags. However these functions expect bit numbers, while the flags are defined as bit masks in enum tpm_tis_flag. Fix this inconsistency by using numbers instead of masks also for the flags in the enum. Reported-by: Pavel Machek Fixes: 858e8b792d06 ("tpm, tpm_tis: Avoid cache incoherency in test for interrupts") Signed-off-by: Lino Sanfilippo Cc: stable@vger.kernel.org Reviewed-by: Pavel Machek Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_tis_core.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index e978f457fd4d..610bfadb6acf 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -84,10 +84,10 @@ enum tis_defaults { #define ILB_REMAP_SIZE 0x100 enum tpm_tis_flags { - TPM_TIS_ITPM_WORKAROUND = BIT(0), - TPM_TIS_INVALID_STATUS = BIT(1), - TPM_TIS_DEFAULT_CANCELLATION = BIT(2), - TPM_TIS_IRQ_TESTED = BIT(3), + TPM_TIS_ITPM_WORKAROUND = 0, + TPM_TIS_INVALID_STATUS = 1, + TPM_TIS_DEFAULT_CANCELLATION = 2, + TPM_TIS_IRQ_TESTED = 3, }; struct tpm_tis_data { -- cgit 1.4.1 From 94f97b8df0058afae7dc00c3dafeffd442bf0048 Mon Sep 17 00:00:00 2001 From: Ism Hong Date: Thu, 1 Jun 2023 17:53:55 +0800 Subject: riscv: perf: Fix callchain parse error with kernel tracepoint events commit 9a7e8ec0d4cc64870ea449b4fce5779b77496cbb upstream. For RISC-V, when tracing with tracepoint events, the IP and status are set to 0, preventing the perf code parsing the callchain and resolving the symbols correctly. ./ply 'tracepoint:kmem/kmem_cache_alloc { @[stack]=count(); }' @: { }: 1 The fix is to implement perf_arch_fetch_caller_regs for riscv, which fills several necessary registers used for callchain unwinding, including epc, sp, s0 and status. It's similar to commit b3eac0265bf6 ("arm: perf: Fix callchain parse error with kernel tracepoint events") and commit 5b09a094f2fb ("arm64: perf: Fix callchain parse error with kernel tracepoint events"). With this patch, callchain can be parsed correctly as: ./ply 'tracepoint:kmem/kmem_cache_alloc { @[stack]=count(); }' @: { __traceiter_kmem_cache_alloc+68 __traceiter_kmem_cache_alloc+68 kmem_cache_alloc+354 __sigqueue_alloc+94 __send_signal_locked+646 send_signal_locked+154 do_send_sig_info+84 __kill_pgrp_info+130 kill_pgrp+60 isig+150 n_tty_receive_signal_char+36 n_tty_receive_buf_standard+2214 n_tty_receive_buf_common+280 n_tty_receive_buf2+26 tty_ldisc_receive_buf+34 tty_port_default_receive_buf+62 flush_to_ldisc+158 process_one_work+458 worker_thread+138 kthread+178 riscv_cpufeature_patch_func+832 }: 1 Signed-off-by: Ism Hong Link: https://lore.kernel.org/r/20230601095355.1168910-1-ism.hong@gmail.com Fixes: 178e9fc47aae ("perf: riscv: preliminary RISC-V support") Cc: stable@vger.kernel.org Signed-off-by: Palmer Dabbelt Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/perf_event.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/include/asm/perf_event.h b/arch/riscv/include/asm/perf_event.h index d42c901f9a97..665bbc9b2f84 100644 --- a/arch/riscv/include/asm/perf_event.h +++ b/arch/riscv/include/asm/perf_event.h @@ -10,4 +10,11 @@ #include #define perf_arch_bpf_user_pt_regs(regs) (struct user_regs_struct *)regs + +#define perf_arch_fetch_caller_regs(regs, __ip) { \ + (regs)->epc = (__ip); \ + (regs)->s0 = (unsigned long) __builtin_frame_address(0); \ + (regs)->sp = current_stack_pointer; \ + (regs)->status = SR_PP; \ +} #endif /* _ASM_RISCV_PERF_EVENT_H */ -- cgit 1.4.1 From 6fb0b098f6905a9e4bcf37516773d04e98de6b17 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 6 May 2023 11:55:02 +0200 Subject: io_uring: undeprecate epoll_ctl support commit 4ea0bf4b98d66a7a790abb285539f395596bae92 upstream. Libuv recently started using it so there is at least one consumer now. Cc: stable@vger.kernel.org Fixes: 61a2732af4b0 ("io_uring: deprecate epoll_ctl support") Link: https://github.com/libuv/libuv/pull/3979 Signed-off-by: Ben Noordhuis Link: https://lore.kernel.org/r/20230506095502.13401-1-info@bnoordhuis.nl Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/epoll.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/io_uring/epoll.c b/io_uring/epoll.c index 9aa74d2c80bc..89bff2068a19 100644 --- a/io_uring/epoll.c +++ b/io_uring/epoll.c @@ -25,10 +25,6 @@ int io_epoll_ctl_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_epoll *epoll = io_kiocb_to_cmd(req, struct io_epoll); - pr_warn_once("%s: epoll_ctl support in io_uring is deprecated and will " - "be removed in a future Linux kernel version.\n", - current->comm); - if (sqe->buf_index || sqe->splice_fd_in) return -EINVAL; -- cgit 1.4.1 From d7c34c8f60051d16ea02a66b43460115eca408d4 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 1 Jun 2023 10:21:21 -0400 Subject: selinux: don't use make's grouped targets feature yet commit 42c4e97e06a839b07d834f640a10911ad84ec8b3 upstream. The Linux Kernel currently only requires make v3.82 while the grouped target functionality requires make v4.3. Removed the grouped target introduced in 4ce1f694eb5d ("selinux: ensure av_permissions.h is built when needed") as well as the multiple header file targets in the make rule. This effectively reverts the problem commit. We will revisit this change when make >= 4.3 is required by the rest of the kernel. Cc: stable@vger.kernel.org Fixes: 4ce1f694eb5d ("selinux: ensure av_permissions.h is built when needed") Reported-by: Erwan Velu Reported-by: Luiz Capitulino Tested-by: Luiz Capitulino Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/security/selinux/Makefile b/security/selinux/Makefile index 0aecf9334ec3..8b21520bd4b9 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -26,5 +26,9 @@ quiet_cmd_flask = GEN $(obj)/flask.h $(obj)/av_permissions.h cmd_flask = $< $(obj)/flask.h $(obj)/av_permissions.h targets += flask.h av_permissions.h -$(obj)/flask.h $(obj)/av_permissions.h &: scripts/selinux/genheaders/genheaders FORCE +# once make >= 4.3 is required, we can use grouped targets in the rule below, +# which basically involves adding both headers and a '&' before the colon, see +# the example below: +# $(obj)/flask.h $(obj)/av_permissions.h &: scripts/selinux/... +$(obj)/flask.h: scripts/selinux/genheaders/genheaders FORCE $(call if_changed,flask) -- cgit 1.4.1 From 007c04225697ffa1ecb56e0bca0643cf8732e03a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 17 Apr 2023 22:56:50 +0200 Subject: mtdchar: mark bits of ioctl handler noinline commit 0ea923f443350c8c5cca6eef5b748d52b903f46c upstream. The addition of the mtdchar_read_ioctl() function caused the stack usage of mtdchar_ioctl() to grow beyond the warning limit on 32-bit architectures with gcc-13: drivers/mtd/mtdchar.c: In function 'mtdchar_ioctl': drivers/mtd/mtdchar.c:1229:1: error: the frame size of 1488 bytes is larger than 1024 bytes [-Werror=frame-larger-than=] Mark both the read and write portions as noinline_for_stack to ensure they don't get inlined and use separate stack slots to reduce the maximum usage, both in the mtdchar_ioctl() and combined with any of its callees. Fixes: 095bb6e44eb1 ("mtdchar: add MEMREAD ioctl") Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Richard Weinberger Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230417205654.1982368-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/mtdchar.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 01f1c6792df9..8dc4f5c493fc 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -590,8 +590,8 @@ static void adjust_oob_length(struct mtd_info *mtd, uint64_t start, (end_page - start_page + 1) * oob_per_page); } -static int mtdchar_write_ioctl(struct mtd_info *mtd, - struct mtd_write_req __user *argp) +static noinline_for_stack int +mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp) { struct mtd_info *master = mtd_get_master(mtd); struct mtd_write_req req; @@ -688,8 +688,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, return ret; } -static int mtdchar_read_ioctl(struct mtd_info *mtd, - struct mtd_read_req __user *argp) +static noinline_for_stack int +mtdchar_read_ioctl(struct mtd_info *mtd, struct mtd_read_req __user *argp) { struct mtd_info *master = mtd_get_master(mtd); struct mtd_read_req req; -- cgit 1.4.1 From 2a1195f0e085742ee91645203ac5f30fb3cad399 Mon Sep 17 00:00:00 2001 From: Daniel Bristot de Oliveira Date: Thu, 11 May 2023 18:32:01 +0200 Subject: tracing/timerlat: Always wakeup the timerlat thread commit 632478a05821bc1c9b55c3a1dd0fb1be7bfa1acc upstream. While testing rtla timerlat auto analysis, I reach a condition where the interface was not receiving tracing data. I was able to manually reproduce the problem with these steps: # echo 0 > tracing_on # disable trace # echo 1 > osnoise/stop_tracing_us # stop trace if timerlat irq > 1 us # echo timerlat > current_tracer # enable timerlat tracer # sleep 1 # wait... that is the time when rtla # apply configs like prio or cgroup # echo 1 > tracing_on # start tracing # cat trace # tracer: timerlat # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / _-=> migrate-disable # |||| / delay # ||||| ACTIVATION # TASK-PID CPU# ||||| TIMESTAMP ID CONTEXT LATENCY # | | | ||||| | | | | NOTHING! Then, trying to enable tracing again with echo 1 > tracing_on resulted in no change: the trace was still not tracing. This problem happens because the timerlat IRQ hits the stop tracing condition while tracing is off, and do not wake up the timerlat thread, so the timerlat threads are kept sleeping forever, resulting in no trace, even after re-enabling the tracer. Avoid this condition by always waking up the threads, even after stopping tracing, allowing the tracer to return to its normal operating after a new tracing on. Link: https://lore.kernel.org/linux-trace-kernel/1ed8f830638b20a39d535d27d908e319a9a3c4e2.1683822622.git.bristot@kernel.org Cc: Juri Lelli Cc: stable@vger.kernel.org Fixes: a955d7eac177 ("trace: Add timerlat tracer") Signed-off-by: Daniel Bristot de Oliveira Signed-off-by: Steven Rostedt (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_osnoise.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c index 689361097bb0..26111b0ca1da 100644 --- a/kernel/trace/trace_osnoise.c +++ b/kernel/trace/trace_osnoise.c @@ -1595,6 +1595,8 @@ static enum hrtimer_restart timerlat_irq(struct hrtimer *timer) osnoise_stop_tracing(); notify_new_max_latency(diff); + wake_up_process(tlat->kthread); + return HRTIMER_NORESTART; } } -- cgit 1.4.1 From 7403630eb94c1d664fb873f967427ef2f6ee3699 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Tue, 23 May 2023 22:11:08 -0400 Subject: tracing/histograms: Allow variables to have some modifiers commit e30fbc618e97b38dbb49f1d44dcd0778d3f23b8c upstream. Modifiers are used to change the behavior of keys. For instance, they can grouped into buckets, converted to syscall names (from the syscall identifier), show task->comm of the current pid, be an array of longs that represent a stacktrace, and more. It was found that nothing stopped a value from taking a modifier. As values are simple counters. If this happened, it would call code that was not expecting a modifier and crash the kernel. This was fixed by having the ___create_val_field() function test if a modifier was present and fail if one was. This fixed the crash. Now there's a problem with variables. Variables are used to pass fields from one event to another. Variables are allowed to have some modifiers, as the processing may need to happen at the time of the event (like stacktraces and comm names of the current pid). The issue is that it too uses __create_val_field(). Now that fails on modifiers, variables can no longer use them (this is a regression). As not all modifiers are for variables, have them use a separate check. Link: https://lore.kernel.org/linux-trace-kernel/20230523221108.064a5d82@rorschach.local.home Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Cc: Tom Zanussi Cc: Mark Rutland Fixes: e0213434fe3e4 ("tracing: Do not let histogram values have some modifiers") Signed-off-by: Steven Rostedt (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_events_hist.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 2b2120ed2460..75244d9e2bf9 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -4193,13 +4193,19 @@ static int __create_val_field(struct hist_trigger_data *hist_data, goto out; } - /* Some types cannot be a value */ - if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT | - HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 | - HIST_FIELD_FL_SYM | HIST_FIELD_FL_SYM_OFFSET | - HIST_FIELD_FL_SYSCALL | HIST_FIELD_FL_STACKTRACE)) { - hist_err(file->tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str)); - ret = -EINVAL; + /* values and variables should not have some modifiers */ + if (hist_field->flags & HIST_FIELD_FL_VAR) { + /* Variable */ + if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT | + HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2)) + goto err; + } else { + /* Value */ + if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT | + HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 | + HIST_FIELD_FL_SYM | HIST_FIELD_FL_SYM_OFFSET | + HIST_FIELD_FL_SYSCALL | HIST_FIELD_FL_STACKTRACE)) + goto err; } hist_data->fields[val_idx] = hist_field; @@ -4211,6 +4217,9 @@ static int __create_val_field(struct hist_trigger_data *hist_data, ret = -EINVAL; out: return ret; + err: + hist_err(file->tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str)); + return -EINVAL; } static int create_val_field(struct hist_trigger_data *hist_data, -- cgit 1.4.1 From 3f731926a1b095459fa03cff8f508720ba51271e Mon Sep 17 00:00:00 2001 From: Pietro Borrello Date: Sat, 28 Jan 2023 16:23:41 +0000 Subject: tracing/probe: trace_probe_primary_from_call(): checked list_first_entry commit 81d0fa4cb4fc0e1a49c2b22f92c43d9fe972ebcf upstream. All callers of trace_probe_primary_from_call() check the return value to be non NULL. However, the function returns list_first_entry(&tpe->probes, ...) which can never be NULL. Additionally, it does not check for the list being possibly empty, possibly causing a type confusion on empty lists. Use list_first_entry_or_null() which solves both problems. Link: https://lore.kernel.org/linux-trace-kernel/20230128-list-entry-null-check-v1-1-8bde6a3da2ef@diag.uniroma1.it/ Fixes: 60d53e2c3b75 ("tracing/probe: Split trace_event related data from trace_probe") Signed-off-by: Pietro Borrello Reviewed-by: Steven Rostedt (Google) Acked-by: Masami Hiramatsu (Google) Acked-by: Mukesh Ojha Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_probe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index de38f1c03776..3d731aac94d4 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -302,7 +302,7 @@ trace_probe_primary_from_call(struct trace_event_call *call) { struct trace_probe_event *tpe = trace_probe_event_from_call(call); - return list_first_entry(&tpe->probes, struct trace_probe, list); + return list_first_entry_or_null(&tpe->probes, struct trace_probe, list); } static inline struct list_head *trace_probe_probe_list(struct trace_probe *tp) -- cgit 1.4.1 From 68ecc09a148ca2dba41385e4f6255b436e76f2d0 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:27 +0200 Subject: selftests: mptcp: connect: skip if MPTCP is not supported commit d83013bdf90a7994a474b0e650a7fc94b0d4ded6 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting MPTCP. A new check is then added to make sure MPTCP is supported. If not, the test stops and is marked as "skipped". Note that this check can also mark the test as failed if 'SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES' env var is set to 1: by doing that, we can make sure a test is not being skipped by mistake. A new shared file is added here to be able to re-used the same check in the different selftests we have. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 048d19d444be ("mptcp: add basic kselftest for mptcp") Cc: stable@vger.kernel.org Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/Makefile | 2 +- tools/testing/selftests/net/mptcp/mptcp_connect.sh | 4 +++ tools/testing/selftests/net/mptcp/mptcp_lib.sh | 40 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/mptcp/mptcp_lib.sh diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile index 43a723626126..7b936a926859 100644 --- a/tools/testing/selftests/net/mptcp/Makefile +++ b/tools/testing/selftests/net/mptcp/Makefile @@ -9,7 +9,7 @@ TEST_PROGS := mptcp_connect.sh pm_netlink.sh mptcp_join.sh diag.sh \ TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq -TEST_FILES := settings +TEST_FILES := mptcp_lib.sh settings EXTRA_CLEAN := *.pcap diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index 621af6895f4d..c923ce5ff6eb 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +. "$(dirname "${0}")/mptcp_lib.sh" + time_start=$(date +%s) optstring="S:R:d:e:l:r:h4cm:f:tC" @@ -141,6 +143,8 @@ cleanup() done } +mptcp_lib_check_mptcp + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Could not run test without ip tool" diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh new file mode 100644 index 000000000000..3286536b79d5 --- /dev/null +++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh @@ -0,0 +1,40 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 + +readonly KSFT_FAIL=1 +readonly KSFT_SKIP=4 + +# SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var can be set when validating all +# features using the last version of the kernel and the selftests to make sure +# a test is not being skipped by mistake. +mptcp_lib_expect_all_features() { + [ "${SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES:-}" = "1" ] +} + +# $1: msg +mptcp_lib_fail_if_expected_feature() { + if mptcp_lib_expect_all_features; then + echo "ERROR: missing feature: ${*}" + exit ${KSFT_FAIL} + fi + + return 1 +} + +# $1: file +mptcp_lib_has_file() { + local f="${1}" + + if [ -f "${f}" ]; then + return 0 + fi + + mptcp_lib_fail_if_expected_feature "${f} file not found" +} + +mptcp_lib_check_mptcp() { + if ! mptcp_lib_has_file "/proc/sys/net/mptcp/enabled"; then + echo "SKIP: MPTCP support is not available" + exit ${KSFT_SKIP} + fi +} -- cgit 1.4.1 From 17ddf2a54eff4dcf8316708de04e0b57de362a92 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:28 +0200 Subject: selftests: mptcp: pm nl: skip if MPTCP is not supported commit 0f4955a40dafe18a1122e3714d8173e4b018e869 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting MPTCP. A new check is then added to make sure MPTCP is supported. If not, the test stops and is marked as "skipped". Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: eedbc685321b ("selftests: add PM netlink functional tests") Cc: stable@vger.kernel.org Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/pm_netlink.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index 89839d1ff9d8..32f7533e0919 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +. "$(dirname "${0}")/mptcp_lib.sh" + ksft_skip=4 ret=0 @@ -34,6 +36,8 @@ cleanup() ip netns del $ns1 } +mptcp_lib_check_mptcp + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Could not run test without ip tool" -- cgit 1.4.1 From 0fea987ccf5f9ad445d41444f1c1d2331d6eebcf Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:29 +0200 Subject: selftests: mptcp: join: skip if MPTCP is not supported commit 715c78a82e00f848f99ef76e6f6b89216ccba268 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting MPTCP. A new check is then added to make sure MPTCP is supported. If not, the test stops and is marked as "skipped". Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: b08fbf241064 ("selftests: add test-cases for MPTCP MP_JOIN") Cc: stable@vger.kernel.org Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 76a197f7b813..504bcfe911ea 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -6,6 +6,8 @@ # address all other issues detected by shellcheck. #shellcheck disable=SC2086 +. "$(dirname "${0}")/mptcp_lib.sh" + ret=0 sin="" sinfail="" @@ -127,6 +129,8 @@ cleanup_partial() check_tools() { + mptcp_lib_check_mptcp + if ! ip -Version &> /dev/null; then echo "SKIP: Could not run test without ip tool" exit $ksft_skip -- cgit 1.4.1 From f324df8de0b53be722c8ad5ffb6ddf4fde35d8f9 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:32 +0200 Subject: selftests: mptcp: sockopt: skip if MPTCP is not supported commit cf6f0fda7af7e8e016070bfee6b189e671a0c776 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting MPTCP. A new check is then added to make sure MPTCP is supported. If not, the test stops and is marked as "skipped". Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: dc65fe82fb07 ("selftests: mptcp: add packet mark test case") Cc: stable@vger.kernel.org Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index 80d36f7cfee8..08d8533c98c4 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +. "$(dirname "${0}")/mptcp_lib.sh" + ret=0 sin="" sout="" @@ -81,6 +83,8 @@ cleanup() rm -f "$sin" "$sout" } +mptcp_lib_check_mptcp + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Could not run test without ip tool" -- cgit 1.4.1 From 97ecfe67f5ca47ee4d0779b772e538cb9eb1f244 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:33 +0200 Subject: selftests: mptcp: userspace pm: skip if MPTCP is not supported commit 63212608a92a1ff10ae56dbb14e9fb685f7e4ffa upstream. Selftests are supposed to run on any kernels, including the old ones not supporting MPTCP. A new check is then added to make sure MPTCP is supported. If not, the test stops and is marked as "skipped". Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 259a834fadda ("selftests: mptcp: functional tests for the userspace PM type") Cc: stable@vger.kernel.org Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/userspace_pm.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index ad6547c79b83..084a2b0a197e 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -1,6 +1,10 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +. "$(dirname "${0}")/mptcp_lib.sh" + +mptcp_lib_check_mptcp + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Cannot not run test without ip tool" -- cgit 1.4.1 From 977a63afd40110e9c04ff3c00e43c61d13fa26b1 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 31 May 2023 12:37:03 -0700 Subject: mptcp: fix connect timeout handling commit 786fc12457268cc9b555dde6c22ae7300d4b40e1 upstream. Ondrej reported a functional issue WRT timeout handling on connect with a nice reproducer. The problem is that the current mptcp connect waits for both the MPTCP socket level timeout, and the first subflow socket timeout. The latter is not influenced/touched by the exposed setsockopt(). Overall the above makes the SO_SNDTIMEO a no-op on connect. Since mptcp_connect is invoked via inet_stream_connect and the latter properly handle the MPTCP level timeout, we can address the issue making the nested subflow level connect always unblocking. This also allow simplifying a bit the code, dropping an ugly hack to handle the fastopen and custom proto_ops connect. The issues predates the blamed commit below, but the current resolution requires the infrastructure introduced there. Fixes: 54f1944ed6d2 ("mptcp: factor out mptcp_connect()") Reported-by: Ondrej Mosnacek Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/399 Cc: stable@vger.kernel.org Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 29 +++++++---------------------- net/mptcp/protocol.h | 1 - 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index c25796eacd95..89c78a71e535 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1683,7 +1683,6 @@ static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msgh lock_sock(ssk); msg->msg_flags |= MSG_DONTWAIT; - msk->connect_flags = O_NONBLOCK; msk->fastopening = 1; ret = tcp_sendmsg_fastopen(ssk, msg, copied_syn, len, NULL); msk->fastopening = 0; @@ -3638,9 +3637,9 @@ static int mptcp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) * acquired the subflow socket lock, too. */ if (msk->fastopening) - err = __inet_stream_connect(ssock, uaddr, addr_len, msk->connect_flags, 1); + err = __inet_stream_connect(ssock, uaddr, addr_len, O_NONBLOCK, 1); else - err = inet_stream_connect(ssock, uaddr, addr_len, msk->connect_flags); + err = inet_stream_connect(ssock, uaddr, addr_len, O_NONBLOCK); inet_sk(sk)->defer_connect = inet_sk(ssock->sk)->defer_connect; /* on successful connect, the msk state will be moved to established by @@ -3653,12 +3652,10 @@ static int mptcp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) mptcp_copy_inaddrs(sk, ssock->sk); - /* unblocking connect, mptcp-level inet_stream_connect will error out - * without changing the socket state, update it here. + /* silence EINPROGRESS and let the caller inet_stream_connect + * handle the connection in progress */ - if (err == -EINPROGRESS) - sk->sk_socket->state = ssock->state; - return err; + return 0; } static struct proto mptcp_prot = { @@ -3717,18 +3714,6 @@ unlock: return err; } -static int mptcp_stream_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags) -{ - int ret; - - lock_sock(sock->sk); - mptcp_sk(sock->sk)->connect_flags = flags; - ret = __inet_stream_connect(sock, uaddr, addr_len, flags, 0); - release_sock(sock->sk); - return ret; -} - static int mptcp_listen(struct socket *sock, int backlog) { struct mptcp_sock *msk = mptcp_sk(sock->sk); @@ -3879,7 +3864,7 @@ static const struct proto_ops mptcp_stream_ops = { .owner = THIS_MODULE, .release = inet_release, .bind = mptcp_bind, - .connect = mptcp_stream_connect, + .connect = inet_stream_connect, .socketpair = sock_no_socketpair, .accept = mptcp_stream_accept, .getname = inet_getname, @@ -3974,7 +3959,7 @@ static const struct proto_ops mptcp_v6_stream_ops = { .owner = THIS_MODULE, .release = inet6_release, .bind = mptcp_bind, - .connect = mptcp_stream_connect, + .connect = inet_stream_connect, .socketpair = sock_no_socketpair, .accept = mptcp_stream_accept, .getname = inet6_getname, diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 4a2f6e29211a..55fc5e42082e 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -288,7 +288,6 @@ struct mptcp_sock { nodelay:1, fastopening:1, in_accept_queue:1; - int connect_flags; struct work_struct work; struct sk_buff *ooo_last_skb; struct rb_root out_of_order_queue; -- cgit 1.4.1 From 9ce0319b0599b41e777562956398e75af52cdec4 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 31 May 2023 12:37:08 -0700 Subject: mptcp: fix active subflow finalization commit 55b47ca7d80814ceb63d64e032e96cd6777811e5 upstream. Active subflow are inserted into the connection list at creation time. When the MPJ handshake completes successfully, a new subflow creation netlink event is generated correctly, but the current code wrongly avoid initializing a couple of subflow data. The above will cause misbehavior on a few exceptional events: unneeded mptcp-level retransmission on msk-level sequence wrap-around and infinite mapping fallback even when a MPJ socket is present. Address the issue factoring out the needed initialization in a new helper and invoking the latter from __mptcp_finish_join() time for passive subflow and from mptcp_finish_join() for active ones. Fixes: 0530020a7c8f ("mptcp: track and update contiguous data status") Cc: stable@vger.kernel.org Reviewed-by: Mat Martineau Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 89c78a71e535..59186997c3e2 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -821,6 +821,13 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk) mptcp_data_unlock(sk); } +static void mptcp_subflow_joined(struct mptcp_sock *msk, struct sock *ssk) +{ + mptcp_subflow_ctx(ssk)->map_seq = READ_ONCE(msk->ack_seq); + WRITE_ONCE(msk->allow_infinite_fallback, false); + mptcp_event(MPTCP_EVENT_SUB_ESTABLISHED, msk, ssk, GFP_ATOMIC); +} + static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk) { struct sock *sk = (struct sock *)msk; @@ -835,6 +842,7 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk) mptcp_sock_graft(ssk, sk->sk_socket); mptcp_sockopt_sync_locked(msk, ssk); + mptcp_subflow_joined(msk, ssk); return true; } @@ -3485,14 +3493,16 @@ bool mptcp_finish_join(struct sock *ssk) return false; } - if (!list_empty(&subflow->node)) - goto out; + /* active subflow, already present inside the conn_list */ + if (!list_empty(&subflow->node)) { + mptcp_subflow_joined(msk, ssk); + return true; + } if (!mptcp_pm_allow_new_subflow(msk)) goto err_prohibited; - /* active connections are already on conn_list. - * If we can't acquire msk socket lock here, let the release callback + /* If we can't acquire msk socket lock here, let the release callback * handle it */ mptcp_data_lock(parent); @@ -3515,11 +3525,6 @@ err_prohibited: return false; } - subflow->map_seq = READ_ONCE(msk->ack_seq); - WRITE_ONCE(msk->allow_infinite_fallback, false); - -out: - mptcp_event(MPTCP_EVENT_SUB_ESTABLISHED, msk, ssk, GFP_ATOMIC); return true; } -- cgit 1.4.1 From b112babc565ea92d54045fe70bd931b546c5dc4e Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 23 May 2023 23:49:48 -0400 Subject: ext4: add EA_INODE checking to ext4_iget() commit b3e6bcb94590dea45396b9481e47b809b1be4afa upstream. Add a new flag, EXT4_IGET_EA_INODE which indicates whether the inode is expected to have the EA_INODE flag or not. If the flag is not set/clear as expected, then fail the iget() operation and mark the file system as corrupted. This commit also makes the ext4_iget() always perform the is_bad_inode() check even when the inode is already inode cache. This allows us to remove the is_bad_inode() check from the callers of ext4_iget() in the ea_inode code. Reported-by: syzbot+cbb68193bdb95af4340a@syzkaller.appspotmail.com Reported-by: syzbot+62120febbd1ee3c3c860@syzkaller.appspotmail.com Reported-by: syzbot+edce54daffee36421b4c@syzkaller.appspotmail.com Cc: stable@kernel.org Signed-off-by: Theodore Ts'o Link: https://lore.kernel.org/r/20230524034951.779531-2-tytso@mit.edu Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/ext4.h | 3 ++- fs/ext4/inode.c | 31 ++++++++++++++++++++++++++----- fs/ext4/xattr.c | 36 +++++++----------------------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 2156d2a1a770..83ea8c2d1038 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2968,7 +2968,8 @@ typedef enum { EXT4_IGET_NORMAL = 0, EXT4_IGET_SPECIAL = 0x0001, /* OK to iget a system inode */ EXT4_IGET_HANDLE = 0x0002, /* Inode # is from a handle */ - EXT4_IGET_BAD = 0x0004 /* Allow to iget a bad inode */ + EXT4_IGET_BAD = 0x0004, /* Allow to iget a bad inode */ + EXT4_IGET_EA_INODE = 0x0008 /* Inode should contain an EA value */ } ext4_iget_flags; extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ffc810436ef2..78d2058c7404 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4763,6 +4763,21 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val) inode_set_iversion_queried(inode, val); } +static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags) + +{ + if (flags & EXT4_IGET_EA_INODE) { + if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) + return "missing EA_INODE flag"; + } else { + if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) + return "unexpected EA_INODE flag"; + } + if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) + return "unexpected bad inode w/o EXT4_IGET_BAD"; + return NULL; +} + struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, ext4_iget_flags flags, const char *function, unsigned int line) @@ -4772,6 +4787,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, struct ext4_inode_info *ei; struct ext4_super_block *es = EXT4_SB(sb)->s_es; struct inode *inode; + const char *err_str; journal_t *journal = EXT4_SB(sb)->s_journal; long ret; loff_t size; @@ -4799,8 +4815,14 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) + if (!(inode->i_state & I_NEW)) { + if ((err_str = check_igot_inode(inode, flags)) != NULL) { + ext4_error_inode(inode, function, line, 0, err_str); + iput(inode); + return ERR_PTR(-EFSCORRUPTED); + } return inode; + } ei = EXT4_I(inode); iloc.bh = NULL; @@ -5066,10 +5088,9 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, if (IS_CASEFOLDED(inode) && !ext4_has_feature_casefold(inode->i_sb)) ext4_error_inode(inode, function, line, 0, "casefold flag without casefold feature"); - if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) { - ext4_error_inode(inode, function, line, 0, - "bad inode without EXT4_IGET_BAD flag"); - ret = -EUCLEAN; + if ((err_str = check_igot_inode(inode, flags)) != NULL) { + ext4_error_inode(inode, function, line, 0, err_str); + ret = -EFSCORRUPTED; goto bad_inode; } diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index b1b8fe86ccdb..b1c8d35ddc77 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -397,7 +397,7 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, return -EFSCORRUPTED; } - inode = ext4_iget(parent->i_sb, ea_ino, EXT4_IGET_NORMAL); + inode = ext4_iget(parent->i_sb, ea_ino, EXT4_IGET_EA_INODE); if (IS_ERR(inode)) { err = PTR_ERR(inode); ext4_error(parent->i_sb, @@ -405,23 +405,6 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, err); return err; } - - if (is_bad_inode(inode)) { - ext4_error(parent->i_sb, - "error while reading EA inode %lu is_bad_inode", - ea_ino); - err = -EIO; - goto error; - } - - if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { - ext4_error(parent->i_sb, - "EA inode %lu does not have EXT4_EA_INODE_FL flag", - ea_ino); - err = -EINVAL; - goto error; - } - ext4_xattr_inode_set_class(inode); /* @@ -442,9 +425,6 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, *ea_inode = inode; return 0; -error: - iput(inode); - return err; } /* Remove entry from mbcache when EA inode is getting evicted */ @@ -1507,11 +1487,10 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value, while (ce) { ea_inode = ext4_iget(inode->i_sb, ce->e_value, - EXT4_IGET_NORMAL); - if (!IS_ERR(ea_inode) && - !is_bad_inode(ea_inode) && - (EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL) && - i_size_read(ea_inode) == value_len && + EXT4_IGET_EA_INODE); + if (IS_ERR(ea_inode)) + goto next_entry; + if (i_size_read(ea_inode) == value_len && !ext4_xattr_inode_read(ea_inode, ea_data, value_len) && !ext4_xattr_inode_verify_hashes(ea_inode, NULL, ea_data, value_len) && @@ -1521,9 +1500,8 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value, kvfree(ea_data); return ea_inode; } - - if (!IS_ERR(ea_inode)) - iput(ea_inode); + iput(ea_inode); + next_entry: ce = mb_cache_entry_find_next(ea_inode_cache, ce); } kvfree(ea_data); -- cgit 1.4.1 From 277cea6f7786eda99b6b8eb9cc08f2f5a9c8de03 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 23 May 2023 23:49:49 -0400 Subject: ext4: set lockdep subclass for the ea_inode in ext4_xattr_inode_cache_find() commit b928dfdcb27d8fa59917b794cfba53052a2f050f upstream. If the ea_inode has been pushed out of the inode cache while there is still a reference in the mb_cache, the lockdep subclass will not be set on the inode, which can lead to some lockdep false positives. Fixes: 33d201e0277b ("ext4: fix lockdep warning about recursive inode locking") Cc: stable@kernel.org Reported-by: syzbot+d4b971e744b1f5439336@syzkaller.appspotmail.com Signed-off-by: Theodore Ts'o Link: https://lore.kernel.org/r/20230524034951.779531-3-tytso@mit.edu Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/xattr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index b1c8d35ddc77..7c09d7784964 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1490,6 +1490,7 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value, EXT4_IGET_EA_INODE); if (IS_ERR(ea_inode)) goto next_entry; + ext4_xattr_inode_set_class(ea_inode); if (i_size_read(ea_inode) == value_len && !ext4_xattr_inode_read(ea_inode, ea_data, value_len) && !ext4_xattr_inode_verify_hashes(ea_inode, NULL, ea_data, -- cgit 1.4.1 From 140aa33f96d6df51e1c8d161fcc603617a1fd0de Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 23 May 2023 23:49:50 -0400 Subject: ext4: disallow ea_inodes with extended attributes commit 2bc7e7c1a3bc9bd0cbf0f71006f6fe7ef24a00c2 upstream. An ea_inode stores the value of an extended attribute; it can not have extended attributes itself, or this will cause recursive nightmares. Add a check in ext4_iget() to make sure this is the case. Cc: stable@kernel.org Reported-by: syzbot+e44749b6ba4d0434cd47@syzkaller.appspotmail.com Signed-off-by: Theodore Ts'o Link: https://lore.kernel.org/r/20230524034951.779531-4-tytso@mit.edu Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/inode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 78d2058c7404..80d5a859ab14 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4769,6 +4769,9 @@ static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags) if (flags & EXT4_IGET_EA_INODE) { if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) return "missing EA_INODE flag"; + if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || + EXT4_I(inode)->i_file_acl) + return "ea_inode with extended attributes"; } else { if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) return "unexpected EA_INODE flag"; -- cgit 1.4.1 From ef8aeffb2c5048fb171a7a5059c22bebdbd7e6dc Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 23 May 2023 23:49:51 -0400 Subject: ext4: add lockdep annotations for i_data_sem for ea_inode's commit aff3bea95388299eec63440389b4545c8041b357 upstream. Treat i_data_sem for ea_inodes as being in their own lockdep class to avoid lockdep complaints about ext4_setattr's use of inode_lock() on normal inodes potentially causing lock ordering with i_data_sem on ea_inodes in ext4_xattr_inode_write(). However, ea_inodes will be operated on by ext4_setattr(), so this isn't a problem. Cc: stable@kernel.org Link: https://syzkaller.appspot.com/bug?extid=298c5d8fb4a128bc27b0 Reported-by: syzbot+298c5d8fb4a128bc27b0@syzkaller.appspotmail.com Signed-off-by: Theodore Ts'o Link: https://lore.kernel.org/r/20230524034951.779531-5-tytso@mit.edu Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/ext4.h | 2 ++ fs/ext4/xattr.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 83ea8c2d1038..0ea3960cb83e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1007,11 +1007,13 @@ do { \ * where the second inode has larger inode number * than the first * I_DATA_SEM_QUOTA - Used for quota inodes only + * I_DATA_SEM_EA - Used for ea_inodes only */ enum { I_DATA_SEM_NORMAL = 0, I_DATA_SEM_OTHER, I_DATA_SEM_QUOTA, + I_DATA_SEM_EA }; diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 7c09d7784964..fddde5045d0c 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -123,7 +123,11 @@ ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array, #ifdef CONFIG_LOCKDEP void ext4_xattr_inode_set_class(struct inode *ea_inode) { + struct ext4_inode_info *ei = EXT4_I(ea_inode); + lockdep_set_subclass(&ea_inode->i_rwsem, 1); + (void) ei; /* shut up clang warning if !CONFIG_LOCKDEP */ + lockdep_set_subclass(&ei->i_data_sem, I_DATA_SEM_EA); } #endif -- cgit 1.4.1 From 3270095f6eb52b50b8c6844594bdd650258aac2b Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 27 May 2023 08:41:09 +0200 Subject: fbcon: Fix null-ptr-deref in soft_cursor commit d78bd6cc68276bd57f766f7cb98bfe32c23ab327 upstream. syzbot repored this bug in the softcursor code: BUG: KASAN: null-ptr-deref in soft_cursor+0x384/0x6b4 drivers/video/fbdev/core/softcursor.c:70 Read of size 16 at addr 0000000000000200 by task kworker/u4:1/12 CPU: 0 PID: 12 Comm: kworker/u4:1 Not tainted 6.4.0-rc3-syzkaller-geb0f1697d729 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/28/2023 Workqueue: events_power_efficient fb_flashcursor Call trace: dump_backtrace+0x1b8/0x1e4 arch/arm64/kernel/stacktrace.c:233 show_stack+0x2c/0x44 arch/arm64/kernel/stacktrace.c:240 __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0xd0/0x124 lib/dump_stack.c:106 print_report+0xe4/0x514 mm/kasan/report.c:465 kasan_report+0xd4/0x130 mm/kasan/report.c:572 kasan_check_range+0x264/0x2a4 mm/kasan/generic.c:187 __asan_memcpy+0x3c/0x84 mm/kasan/shadow.c:105 soft_cursor+0x384/0x6b4 drivers/video/fbdev/core/softcursor.c:70 bit_cursor+0x113c/0x1a64 drivers/video/fbdev/core/bitblit.c:377 fb_flashcursor+0x35c/0x54c drivers/video/fbdev/core/fbcon.c:380 process_one_work+0x788/0x12d4 kernel/workqueue.c:2405 worker_thread+0x8e0/0xfe8 kernel/workqueue.c:2552 kthread+0x288/0x310 kernel/kthread.c:379 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:853 This fix let bit_cursor() bail out early when a font bitmap isn't available yet. Signed-off-by: Helge Deller Reported-by: syzbot+d910bd780e6efac35869@syzkaller.appspotmail.com Acked-by: Sam Ravnborg Cc: stable@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/bitblit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c index f98e8f298bc1..8587c9da0670 100644 --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -247,6 +247,9 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; + if (!vc->vc_font.data) + return; + c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); -- cgit 1.4.1 From 7493392a375cdf920ea45144f471a2943fccbcd0 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 14 May 2023 13:25:42 +0200 Subject: serial: 8250_tegra: Fix an error handling path in tegra_uart_probe() commit 134f49dec0b6aca3259cd8259de4c572048bd207 upstream. If an error occurs after reset_control_deassert(), it must be re-asserted, as already done in the .remove() function. Fixes: c6825c6395b7 ("serial: 8250_tegra: Create Tegra specific 8250 driver") Cc: stable Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/f8130f35339cc80edc6b9aac4bb2a60b60a226bf.1684063511.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_tegra.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_tegra.c b/drivers/tty/serial/8250/8250_tegra.c index e7cddeec9d8e..c424e2ae0e8f 100644 --- a/drivers/tty/serial/8250/8250_tegra.c +++ b/drivers/tty/serial/8250/8250_tegra.c @@ -112,13 +112,15 @@ static int tegra_uart_probe(struct platform_device *pdev) ret = serial8250_register_8250_port(&port8250); if (ret < 0) - goto err_clkdisable; + goto err_ctrl_assert; platform_set_drvdata(pdev, uart); uart->line = ret; return 0; +err_ctrl_assert: + reset_control_assert(uart->rst); err_clkdisable: clk_disable_unprepare(uart->clk); -- cgit 1.4.1 From 97211945ef6800d89050401ea97ddc9c0ed912df Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 23 May 2023 10:59:02 +0200 Subject: serial: cpm_uart: Fix a COMPILE_TEST dependency commit 7183c37fd53eee1e795206e625da12a5d7ec1e1a upstream. In a COMPILE_TEST configuration, the cpm_uart driver uses symbols from the cpm_uart_cpm2.c file. This file is compiled only when CONFIG_CPM2 is set. Without this dependency, the linker fails with some missing symbols for COMPILE_TEST configuration that needs SERIAL_CPM without enabling CPM2. This lead to: depends on CPM2 || CPM1 || (PPC32 && CPM2 && COMPILE_TEST) This dependency does not make sense anymore and can be simplified removing all the COMPILE_TEST part. Signed-off-by: Herve Codina Reported-by: kernel test robot Link: https://lore.kernel.org/oe-kbuild-all/202305160221.9XgweObz-lkp@intel.com/ Fixes: e3e7b13bffae ("serial: allow COMPILE_TEST for some drivers") Cc: stable Link: https://lore.kernel.org/r/20230523085902.75837-3-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/cpm_uart/cpm_uart.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4fce15296f31..12f685168aef 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -769,7 +769,7 @@ config SERIAL_PMACZILOG_CONSOLE config SERIAL_CPM tristate "CPM SCC/SMC serial port support" - depends on CPM2 || CPM1 || (PPC32 && COMPILE_TEST) + depends on CPM2 || CPM1 select SERIAL_CORE help This driver supports the SCC and SMC serial ports on Motorola diff --git a/drivers/tty/serial/cpm_uart/cpm_uart.h b/drivers/tty/serial/cpm_uart/cpm_uart.h index 0577618e78c0..46c03ed71c31 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart.h +++ b/drivers/tty/serial/cpm_uart/cpm_uart.h @@ -19,8 +19,6 @@ struct gpio_desc; #include "cpm_uart_cpm2.h" #elif defined(CONFIG_CPM1) #include "cpm_uart_cpm1.h" -#elif defined(CONFIG_COMPILE_TEST) -#include "cpm_uart_cpm2.h" #endif #define SERIAL_CPM_MAJOR 204 -- cgit 1.4.1 From 33aebb014893ca461363c510cb84f6bb32b4fb13 Mon Sep 17 00:00:00 2001 From: Maninder Singh Date: Mon, 29 May 2023 16:43:37 +0530 Subject: powerpc/xmon: Use KSYM_NAME_LEN in array size commit 719dfd5925e186e09a2a6f23016936ac436f3d78 upstream. kallsyms_lookup() which in turn calls kallsyms_lookup_buildid() writes to index "KSYM_NAME_LEN - 1". Thus the array passed as namebuf to kallsyms_lookup() should be KSYM_NAME_LEN in size. In xmon.c the array was defined to be "128" bytes directly, without using KSYM_NAME_LEN. Commit b8a94bfb3395 ("kallsyms: increase maximum kernel symbol length to 512") changed the value to 512, but missed updating the xmon code. Fixes: b8a94bfb3395 ("kallsyms: increase maximum kernel symbol length to 512") Cc: stable@vger.kernel.org # v6.1+ Co-developed-by: Onkarnath Signed-off-by: Onkarnath Signed-off-by: Maninder Singh [mpe: Tweak change log wording and fix commit reference] Signed-off-by: Michael Ellerman Link: https://msgid.link/20230529111337.352990-2-maninder1.s@samsung.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/xmon/xmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index e34d7809f6c9..bd8e80936f44 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -91,7 +91,7 @@ static unsigned long ndump = 64; static unsigned long nidump = 16; static unsigned long ncsum = 4096; static int termch; -static char tmpstr[128]; +static char tmpstr[KSYM_NAME_LEN]; static int tracing_enabled; static long bus_error_jmp[JMP_BUF_LEN]; -- cgit 1.4.1 From eef67dfdc05008a9c0902ecc50489dd322f1fc87 Mon Sep 17 00:00:00 2001 From: Mirsad Goran Todorovac Date: Tue, 9 May 2023 10:47:47 +0200 Subject: test_firmware: fix a memory leak with reqs buffer commit be37bed754ed90b2655382f93f9724b3c1aae847 upstream. Dan Carpenter spotted that test_fw_config->reqs will be leaked if trigger_batched_requests_store() is called two or more times. The same appears with trigger_batched_requests_async_store(). This bug wasn't trigger by the tests, but observed by Dan's visual inspection of the code. The recommended workaround was to return -EBUSY if test_fw_config->reqs is already allocated. Fixes: 7feebfa487b92 ("test_firmware: add support for request_firmware_into_buf") Cc: Luis Chamberlain Cc: Greg Kroah-Hartman Cc: Russ Weight Cc: Tianfei Zhang Cc: Shuah Khan Cc: Colin Ian King Cc: Randy Dunlap Cc: linux-kselftest@vger.kernel.org Cc: stable@vger.kernel.org # v5.4 Suggested-by: Dan Carpenter Suggested-by: Takashi Iwai Signed-off-by: Mirsad Goran Todorovac Reviewed-by: Dan Carpenter Acked-by: Luis Chamberlain Link: https://lore.kernel.org/r/20230509084746.48259-2-mirsad.todorovac@alu.unizg.hr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- lib/test_firmware.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 1c5a2adb16ef..7556f981ff19 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -894,6 +894,11 @@ static ssize_t trigger_batched_requests_store(struct device *dev, mutex_lock(&test_fw_mutex); + if (test_fw_config->reqs) { + rc = -EBUSY; + goto out_bail; + } + test_fw_config->reqs = vzalloc(array3_size(sizeof(struct test_batched_req), test_fw_config->num_requests, 2)); @@ -992,6 +997,11 @@ ssize_t trigger_batched_requests_async_store(struct device *dev, mutex_lock(&test_fw_mutex); + if (test_fw_config->reqs) { + rc = -EBUSY; + goto out_bail; + } + test_fw_config->reqs = vzalloc(array3_size(sizeof(struct test_batched_req), test_fw_config->num_requests, 2)); -- cgit 1.4.1 From 0659aee089daf1bee14fa8e386e667d04b2e923a Mon Sep 17 00:00:00 2001 From: Mirsad Goran Todorovac Date: Tue, 9 May 2023 10:47:49 +0200 Subject: test_firmware: fix the memory leak of the allocated firmware buffer commit 48e156023059e57a8fc68b498439832f7600ffff upstream. The following kernel memory leak was noticed after running tools/testing/selftests/firmware/fw_run_tests.sh: [root@pc-mtodorov firmware]# cat /sys/kernel/debug/kmemleak . . . unreferenced object 0xffff955389bc3400 (size 1024): comm "test_firmware-0", pid 5451, jiffies 4294944822 (age 65.652s) hex dump (first 32 bytes): 47 48 34 35 36 37 0a 00 00 00 00 00 00 00 00 00 GH4567.......... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] slab_post_alloc_hook+0x8c/0x3c0 [] __kmem_cache_alloc_node+0x184/0x240 [] kmalloc_trace+0x2e/0xc0 [] test_fw_run_batch_request+0x9d/0x180 [] kthread+0x10b/0x140 [] ret_from_fork+0x29/0x50 unreferenced object 0xffff9553c334b400 (size 1024): comm "test_firmware-1", pid 5452, jiffies 4294944822 (age 65.652s) hex dump (first 32 bytes): 47 48 34 35 36 37 0a 00 00 00 00 00 00 00 00 00 GH4567.......... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] slab_post_alloc_hook+0x8c/0x3c0 [] __kmem_cache_alloc_node+0x184/0x240 [] kmalloc_trace+0x2e/0xc0 [] test_fw_run_batch_request+0x9d/0x180 [] kthread+0x10b/0x140 [] ret_from_fork+0x29/0x50 unreferenced object 0xffff9553c334f000 (size 1024): comm "test_firmware-2", pid 5453, jiffies 4294944822 (age 65.652s) hex dump (first 32 bytes): 47 48 34 35 36 37 0a 00 00 00 00 00 00 00 00 00 GH4567.......... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] slab_post_alloc_hook+0x8c/0x3c0 [] __kmem_cache_alloc_node+0x184/0x240 [] kmalloc_trace+0x2e/0xc0 [] test_fw_run_batch_request+0x9d/0x180 [] kthread+0x10b/0x140 [] ret_from_fork+0x29/0x50 unreferenced object 0xffff9553c3348400 (size 1024): comm "test_firmware-3", pid 5454, jiffies 4294944822 (age 65.652s) hex dump (first 32 bytes): 47 48 34 35 36 37 0a 00 00 00 00 00 00 00 00 00 GH4567.......... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] slab_post_alloc_hook+0x8c/0x3c0 [] __kmem_cache_alloc_node+0x184/0x240 [] kmalloc_trace+0x2e/0xc0 [] test_fw_run_batch_request+0x9d/0x180 [] kthread+0x10b/0x140 [] ret_from_fork+0x29/0x50 [root@pc-mtodorov firmware]# Note that the size 1024 corresponds to the size of the test firmware buffer. The actual number of the buffers leaked is around 70-110, depending on the test run. The cause of the leak is the following: request_partial_firmware_into_buf() and request_firmware_into_buf() provided firmware buffer isn't released on release_firmware(), we have allocated it and we are responsible for deallocating it manually. This is introduced in a number of context where previously only release_firmware() was called, which was insufficient. Reported-by: Mirsad Goran Todorovac Fixes: 7feebfa487b92 ("test_firmware: add support for request_firmware_into_buf") Cc: Greg Kroah-Hartman Cc: Dan Carpenter Cc: Takashi Iwai Cc: Luis Chamberlain Cc: Russ Weight Cc: Tianfei zhang Cc: Christophe JAILLET Cc: Zhengchao Shao Cc: Colin Ian King Cc: linux-kernel@vger.kernel.org Cc: Kees Cook Cc: Scott Branden Cc: Luis R. Rodriguez Cc: linux-kselftest@vger.kernel.org Cc: stable@vger.kernel.org # v5.4 Signed-off-by: Mirsad Goran Todorovac Link: https://lore.kernel.org/r/20230509084746.48259-3-mirsad.todorovac@alu.unizg.hr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- lib/test_firmware.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 7556f981ff19..7f165c517338 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -44,6 +44,7 @@ struct test_batched_req { bool sent; const struct firmware *fw; const char *name; + const char *fw_buf; struct completion completion; struct task_struct *task; struct device *dev; @@ -174,8 +175,14 @@ static void __test_release_all_firmware(void) for (i = 0; i < test_fw_config->num_requests; i++) { req = &test_fw_config->reqs[i]; - if (req->fw) + if (req->fw) { + if (req->fw_buf) { + kfree_const(req->fw_buf); + req->fw_buf = NULL; + } release_firmware(req->fw); + req->fw = NULL; + } } vfree(test_fw_config->reqs); @@ -651,6 +658,8 @@ static ssize_t trigger_request_store(struct device *dev, mutex_lock(&test_fw_mutex); release_firmware(test_firmware); + if (test_fw_config->reqs) + __test_release_all_firmware(); test_firmware = NULL; rc = request_firmware(&test_firmware, name, dev); if (rc) { @@ -751,6 +760,8 @@ static ssize_t trigger_async_request_store(struct device *dev, mutex_lock(&test_fw_mutex); release_firmware(test_firmware); test_firmware = NULL; + if (test_fw_config->reqs) + __test_release_all_firmware(); rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, NULL, trigger_async_request_cb); if (rc) { @@ -793,6 +804,8 @@ static ssize_t trigger_custom_fallback_store(struct device *dev, mutex_lock(&test_fw_mutex); release_firmware(test_firmware); + if (test_fw_config->reqs) + __test_release_all_firmware(); test_firmware = NULL; rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT, name, dev, GFP_KERNEL, NULL, @@ -855,6 +868,8 @@ static int test_fw_run_batch_request(void *data) test_fw_config->buf_size); if (!req->fw) kfree(test_buf); + else + req->fw_buf = test_buf; } else { req->rc = test_fw_config->req_firmware(&req->fw, req->name, @@ -915,6 +930,7 @@ static ssize_t trigger_batched_requests_store(struct device *dev, req->fw = NULL; req->idx = i; req->name = test_fw_config->name; + req->fw_buf = NULL; req->dev = dev; init_completion(&req->completion); req->task = kthread_run(test_fw_run_batch_request, req, @@ -1019,6 +1035,7 @@ ssize_t trigger_batched_requests_async_store(struct device *dev, for (i = 0; i < test_fw_config->num_requests; i++) { req = &test_fw_config->reqs[i]; req->name = test_fw_config->name; + req->fw_buf = NULL; req->fw = NULL; req->idx = i; init_completion(&req->completion); -- cgit 1.4.1 From b1d5667afad146cb4109d41ca7b85b0ad451166b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 30 May 2023 11:46:51 +0900 Subject: KVM: arm64: Populate fault info for watchpoint commit 811154e234db72f0a11557a84ba9640f8b3bc823 upstream. When handling ESR_ELx_EC_WATCHPT_LOW, far_el2 member of struct kvm_vcpu_fault_info will be copied to far member of struct kvm_debug_exit_arch and exposed to the userspace. The userspace will see stale values from older faults if the fault info does not get populated. Fixes: 8fb2046180a0 ("KVM: arm64: Move early handlers to per-EC handlers") Suggested-by: Marc Zyngier Signed-off-by: Akihiko Odaki Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230530024651.10014-1-akihiko.odaki@daynix.com Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/include/hyp/switch.h | 8 ++++++-- arch/arm64/kvm/hyp/nvhe/switch.c | 2 ++ arch/arm64/kvm/hyp/vhe/switch.c | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 3330d1b76bdd..2208d79b18de 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -351,17 +351,21 @@ static bool kvm_hyp_handle_cp15_32(struct kvm_vcpu *vcpu, u64 *exit_code) return false; } -static bool kvm_hyp_handle_iabt_low(struct kvm_vcpu *vcpu, u64 *exit_code) +static bool kvm_hyp_handle_memory_fault(struct kvm_vcpu *vcpu, u64 *exit_code) { if (!__populate_fault_info(vcpu)) return true; return false; } +static bool kvm_hyp_handle_iabt_low(struct kvm_vcpu *vcpu, u64 *exit_code) + __alias(kvm_hyp_handle_memory_fault); +static bool kvm_hyp_handle_watchpt_low(struct kvm_vcpu *vcpu, u64 *exit_code) + __alias(kvm_hyp_handle_memory_fault); static bool kvm_hyp_handle_dabt_low(struct kvm_vcpu *vcpu, u64 *exit_code) { - if (!__populate_fault_info(vcpu)) + if (kvm_hyp_handle_memory_fault(vcpu, exit_code)) return true; if (static_branch_unlikely(&vgic_v2_cpuif_trap)) { diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index c2cb46ca4fb6..895fb3200076 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -186,6 +186,7 @@ static const exit_handler_fn hyp_exit_handlers[] = { [ESR_ELx_EC_FP_ASIMD] = kvm_hyp_handle_fpsimd, [ESR_ELx_EC_IABT_LOW] = kvm_hyp_handle_iabt_low, [ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low, + [ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low, [ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth, }; @@ -196,6 +197,7 @@ static const exit_handler_fn pvm_exit_handlers[] = { [ESR_ELx_EC_FP_ASIMD] = kvm_hyp_handle_fpsimd, [ESR_ELx_EC_IABT_LOW] = kvm_hyp_handle_iabt_low, [ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low, + [ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low, [ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth, }; diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 1a97391fedd2..45ac4a59cc2c 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -110,6 +110,7 @@ static const exit_handler_fn hyp_exit_handlers[] = { [ESR_ELx_EC_FP_ASIMD] = kvm_hyp_handle_fpsimd, [ESR_ELx_EC_IABT_LOW] = kvm_hyp_handle_iabt_low, [ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low, + [ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low, [ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth, }; -- cgit 1.4.1 From 5f4d3810ca9c0304850ddfed360a49b01125bc17 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 1 Jun 2023 18:19:19 -0700 Subject: KVM: x86: Account fastpath-only VM-Exits in vCPU stats commit 8b703a49c9df5e74870381ad7ba9c85d8a74ed2c upstream. Increment vcpu->stat.exits when handling a fastpath VM-Exit without going through any part of the "slow" path. Not bumping the exits stat can result in wildly misleading exit counts, e.g. if the primary reason the guest is exiting is to program the TSC deadline timer. Fixes: 404d5d7bff0d ("KVM: X86: Introduce more exit_fastpath_completion enum values") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230602011920.787844-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/x86.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index d7af225b63d8..f48ab047b41d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10829,6 +10829,9 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) exit_fastpath = EXIT_FASTPATH_EXIT_HANDLED; break; } + + /* Note, VM-Exits that go down the "slow" path are accounted below. */ + ++vcpu->stat.exits; } /* -- cgit 1.4.1 From 8072ea6743749b129e9cbd3a62a1b1b5fa6bf5b7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 12 May 2023 23:29:12 +0900 Subject: ksmbd: fix credit count leakage commit 84c5aa47925a1f40d698b6a6a2bf67e99617433d upstream. This patch fix the failure from smb2.credits.single_req_credits_granted test. When client send 8192 credit request, ksmbd return 8191 credit granted. ksmbd should give maximum possible credits that must be granted within the range of not exceeding the max credit to client. Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2pdu.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 7684b31035d9..2683754173ae 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -326,13 +326,9 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) if (hdr->Command == SMB2_NEGOTIATE) aux_max = 1; else - aux_max = conn->vals->max_credits - credit_charge; + aux_max = conn->vals->max_credits - conn->total_credits; credits_granted = min_t(unsigned short, credits_requested, aux_max); - if (conn->vals->max_credits - conn->total_credits < credits_granted) - credits_granted = conn->vals->max_credits - - conn->total_credits; - conn->total_credits += credits_granted; work->credits_granted += credits_granted; -- cgit 1.4.1 From f7add4d1598ac9df930568f37aa072588e938f20 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 19 May 2023 23:09:48 +0900 Subject: ksmbd: fix UAF issue from opinfo->conn commit 36322523dddb11107e9f7f528675a0dec2536103 upstream. If opinfo->conn is another connection and while ksmbd send oplock break request to cient on current connection, The connection for opinfo->conn can be disconnect and conn could be freed. When sending oplock break request, this ksmbd_conn can be used and cause user-after-free issue. When getting opinfo from the list, ksmbd check connection is being released. If it is not released, Increase ->r_count to wait that connection is freed. Cc: stable@vger.kernel.org Reported-by: Per Forlin Tested-by: Per Forlin Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/oplock.c | 72 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index c7466546e59b..cbfed03a1e5c 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -157,13 +157,42 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) rcu_read_lock(); opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, op_entry); - if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) - opinfo = NULL; + if (opinfo) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + else { + atomic_inc(&opinfo->conn->r_count); + if (ksmbd_conn_releasing(opinfo->conn)) { + atomic_dec(&opinfo->conn->r_count); + atomic_dec(&opinfo->refcount); + opinfo = NULL; + } + } + } + rcu_read_unlock(); return opinfo; } +static void opinfo_conn_put(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn; + + if (!opinfo) + return; + + conn = opinfo->conn; + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); + opinfo_put(opinfo); +} + void opinfo_put(struct oplock_info *opinfo) { if (!atomic_dec_and_test(&opinfo->refcount)) @@ -666,13 +695,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) out: ksmbd_free_work_struct(work); - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); } /** @@ -706,7 +728,6 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) work->conn = conn; work->sess = opinfo->sess; - atomic_inc(&conn->r_count); if (opinfo->op_state == OPLOCK_ACK_WAIT) { INIT_WORK(&work->work, __smb2_oplock_break_noti); ksmbd_queue_work(work); @@ -776,13 +797,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk) out: ksmbd_free_work_struct(work); - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); } /** @@ -822,7 +836,6 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) work->conn = conn; work->sess = opinfo->sess; - atomic_inc(&conn->r_count); if (opinfo->op_state == OPLOCK_ACK_WAIT) { list_for_each_safe(tmp, t, &opinfo->interim_list) { struct ksmbd_work *in_work; @@ -1144,8 +1157,10 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, } prev_opinfo = opinfo_get_list(ci); if (!prev_opinfo || - (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) + (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) { + opinfo_conn_put(prev_opinfo); goto set_lev; + } prev_op_has_lease = prev_opinfo->is_lease; if (prev_op_has_lease) prev_op_state = prev_opinfo->o_lease->state; @@ -1153,19 +1168,19 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, if (share_ret < 0 && prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { err = share_ret; - opinfo_put(prev_opinfo); + opinfo_conn_put(prev_opinfo); goto err_out; } if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_put(prev_opinfo); + opinfo_conn_put(prev_opinfo); goto op_break_not_needed; } list_add(&work->interim_entry, &prev_opinfo->interim_list); err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_put(prev_opinfo); + opinfo_conn_put(prev_opinfo); if (err == -ENOENT) goto set_lev; /* Check all oplock was freed by close */ @@ -1228,14 +1243,14 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work, return; if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_put(brk_opinfo); + opinfo_conn_put(brk_opinfo); return; } brk_opinfo->open_trunc = is_trunc; list_add(&work->interim_entry, &brk_opinfo->interim_list); oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_put(brk_opinfo); + opinfo_conn_put(brk_opinfo); } /** @@ -1263,6 +1278,13 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { if (!atomic_inc_not_zero(&brk_op->refcount)) continue; + + atomic_inc(&brk_op->conn->r_count); + if (ksmbd_conn_releasing(brk_op->conn)) { + atomic_dec(&brk_op->conn->r_count); + continue; + } + rcu_read_unlock(); if (brk_op->is_lease && (brk_op->o_lease->state & (~(SMB2_LEASE_READ_CACHING_LE | @@ -1292,7 +1314,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, brk_op->open_trunc = is_trunc; oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); next: - opinfo_put(brk_op); + opinfo_conn_put(brk_op); rcu_read_lock(); } rcu_read_unlock(); -- cgit 1.4.1 From 4c6bdaacb3cbc4d77d4424e6e7d16c79e28ef494 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 19 May 2023 23:11:33 +0900 Subject: ksmbd: fix incorrect AllocationSize set in smb2_get_info commit 6cc2268f5647cbfde3d4fc2e4ee005070ea3a8d2 upstream. If filesystem support sparse file, ksmbd should return allocated size using ->i_blocks instead of stat->size. This fix generic/694 xfstests. Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2pdu.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 2683754173ae..f52a9a9d2db5 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -4367,21 +4367,6 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, return 0; } -static unsigned long long get_allocation_size(struct inode *inode, - struct kstat *stat) -{ - unsigned long long alloc_size = 0; - - if (!S_ISDIR(stat->mode)) { - if ((inode->i_blocks << 9) <= stat->size) - alloc_size = stat->size; - else - alloc_size = inode->i_blocks << 9; - } - - return alloc_size; -} - static void get_file_standard_info(struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, void *rsp_org) { @@ -4396,7 +4381,7 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp, sinfo = (struct smb2_file_standard_info *)rsp->Buffer; delete_pending = ksmbd_inode_pending_delete(fp); - sinfo->AllocationSize = cpu_to_le64(get_allocation_size(inode, &stat)); + sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9); sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); sinfo->DeletePending = delete_pending; @@ -4461,7 +4446,7 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->Attributes = fp->f_ci->m_fattr; file_info->Pad1 = 0; file_info->AllocationSize = - cpu_to_le64(get_allocation_size(inode, &stat)); + cpu_to_le64(inode->i_blocks << 9); file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); file_info->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); @@ -4650,7 +4635,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info->ChangeTime = cpu_to_le64(time); file_info->Attributes = fp->f_ci->m_fattr; file_info->AllocationSize = - cpu_to_le64(get_allocation_size(inode, &stat)); + cpu_to_le64(inode->i_blocks << 9); file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); file_info->Reserved = cpu_to_le32(0); rsp->OutputBufferLength = -- cgit 1.4.1 From 522a9417f64908b5d4938fac3d0f831e65e4f933 Mon Sep 17 00:00:00 2001 From: Kuan-Ting Chen Date: Fri, 19 May 2023 22:59:28 +0900 Subject: ksmbd: fix slab-out-of-bounds read in smb2_handle_negotiate commit d738950f112c8f40f0515fe967db998e8235a175 upstream. Check request_buf length first to avoid out-of-bounds read by req->DialectCount. [ 3350.990282] BUG: KASAN: slab-out-of-bounds in smb2_handle_negotiate+0x35d7/0x3e60 [ 3350.990282] Read of size 2 at addr ffff88810ad61346 by task kworker/5:0/276 [ 3351.000406] Workqueue: ksmbd-io handle_ksmbd_work [ 3351.003499] Call Trace: [ 3351.006473] [ 3351.006473] dump_stack_lvl+0x8d/0xe0 [ 3351.006473] print_report+0xcc/0x620 [ 3351.006473] kasan_report+0x92/0xc0 [ 3351.006473] smb2_handle_negotiate+0x35d7/0x3e60 [ 3351.014760] ksmbd_smb_negotiate_common+0x7a7/0xf00 [ 3351.014760] handle_ksmbd_work+0x3f7/0x12d0 [ 3351.014760] process_one_work+0xa85/0x1780 Cc: stable@vger.kernel.org Signed-off-by: Kuan-Ting Chen Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2pdu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index f52a9a9d2db5..e95ffe232f60 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1069,16 +1069,16 @@ int smb2_handle_negotiate(struct ksmbd_work *work) return rc; } - if (req->DialectCount == 0) { - pr_err("malformed packet\n"); + smb2_buf_len = get_rfc1002_len(work->request_buf); + smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); + if (smb2_neg_size > smb2_buf_len) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; rc = -EINVAL; goto err_out; } - smb2_buf_len = get_rfc1002_len(work->request_buf); - smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); - if (smb2_neg_size > smb2_buf_len) { + if (req->DialectCount == 0) { + pr_err("malformed packet\n"); rsp->hdr.Status = STATUS_INVALID_PARAMETER; rc = -EINVAL; goto err_out; -- cgit 1.4.1 From 4f303c0b9d5dcfefddf4e4e088462519f162c078 Mon Sep 17 00:00:00 2001 From: Kuan-Ting Chen Date: Fri, 19 May 2023 23:00:24 +0900 Subject: ksmbd: fix multiple out-of-bounds read during context decoding commit 0512a5f89e1fae74251fde6893ff634f1c96c6fb upstream. Check the remaining data length before accessing the context structure to ensure that the entire structure is contained within the packet. Additionally, since the context data length `ctxt_len` has already been checked against the total packet length `len_of_ctxts`, update the comparison to use `ctxt_len`. Cc: stable@vger.kernel.org Signed-off-by: Kuan-Ting Chen Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2pdu.c | 53 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index e95ffe232f60..01887570efe8 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -861,13 +861,14 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, struct smb2_preauth_neg_context *pneg_ctxt, - int len_of_ctxts) + int ctxt_len) { /* * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, * which may not be present. Only check for used HashAlgorithms[1]. */ - if (len_of_ctxts < MIN_PREAUTH_CTXT_DATA_LEN) + if (ctxt_len < + sizeof(struct smb2_neg_context) + MIN_PREAUTH_CTXT_DATA_LEN) return STATUS_INVALID_PARAMETER; if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) @@ -879,15 +880,23 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, static void decode_encrypt_ctxt(struct ksmbd_conn *conn, struct smb2_encryption_neg_context *pneg_ctxt, - int len_of_ctxts) + int ctxt_len) { - int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); - int i, cphs_size = cph_cnt * sizeof(__le16); + int cph_cnt; + int i, cphs_size; + + if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { + pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); + return; + } conn->cipher_type = 0; + cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + cphs_size = cph_cnt * sizeof(__le16); + if (sizeof(struct smb2_encryption_neg_context) + cphs_size > - len_of_ctxts) { + ctxt_len) { pr_err("Invalid cipher count(%d)\n", cph_cnt); return; } @@ -935,15 +944,22 @@ static void decode_compress_ctxt(struct ksmbd_conn *conn, static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, struct smb2_signing_capabilities *pneg_ctxt, - int len_of_ctxts) + int ctxt_len) { - int sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); - int i, sign_alos_size = sign_algo_cnt * sizeof(__le16); + int sign_algo_cnt; + int i, sign_alos_size; + + if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { + pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); + return; + } conn->signing_negotiated = false; + sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); + sign_alos_size = sign_algo_cnt * sizeof(__le16); if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > - len_of_ctxts) { + ctxt_len) { pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); return; } @@ -981,18 +997,16 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, len_of_ctxts = len_of_smb - offset; while (i++ < neg_ctxt_cnt) { - int clen; - - /* check that offset is not beyond end of SMB */ - if (len_of_ctxts == 0) - break; + int clen, ctxt_len; if (len_of_ctxts < sizeof(struct smb2_neg_context)) break; pctx = (struct smb2_neg_context *)((char *)pctx + offset); clen = le16_to_cpu(pctx->DataLength); - if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) + ctxt_len = clen + sizeof(struct smb2_neg_context); + + if (ctxt_len > len_of_ctxts) break; if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { @@ -1003,7 +1017,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, status = decode_preauth_ctxt(conn, (struct smb2_preauth_neg_context *)pctx, - len_of_ctxts); + ctxt_len); if (status != STATUS_SUCCESS) break; } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { @@ -1014,7 +1028,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, decode_encrypt_ctxt(conn, (struct smb2_encryption_neg_context *)pctx, - len_of_ctxts); + ctxt_len); } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); @@ -1033,9 +1047,10 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + decode_sign_cap_ctxt(conn, (struct smb2_signing_capabilities *)pctx, - len_of_ctxts); + ctxt_len); } /* offsets must be 8 byte aligned */ -- cgit 1.4.1 From 0b28edf227e30e05ac1069613302b2d561229907 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 8 Dec 2022 10:56:46 +0100 Subject: KEYS: asymmetric: Copy sig and digest in public_key_verify_signature() commit c3d03e8e35e005e1a614e51bb59053eeb5857f76 upstream. Commit ac4e97abce9b8 ("scatterlist: sg_set_buf() argument must be in linear mapping") checks that both the signature and the digest reside in the linear mapping area. However, more recently commit ba14a194a434c ("fork: Add generic vmalloced stack support") made it possible to move the stack in the vmalloc area, which is not contiguous, and thus not suitable for sg_set_buf() which needs adjacent pages. Always make a copy of the signature and digest in the same buffer used to store the key and its parameters, and pass them to sg_init_one(). Prefer it to conditionally doing the copy if necessary, to keep the code simple. The buffer allocated with kmalloc() is in the linear mapping area. Cc: stable@vger.kernel.org # 4.9.x Fixes: ba14a194a434 ("fork: Add generic vmalloced stack support") Link: https://lore.kernel.org/linux-integrity/Y4pIpxbjBdajymBJ@sol.localdomain/ Suggested-by: Eric Biggers Signed-off-by: Roberto Sassu Reviewed-by: Eric Biggers Tested-by: Stefan Berger Signed-off-by: Greg Kroah-Hartman --- crypto/asymmetric_keys/public_key.c | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index eca5671ad3f2..50c933f86b21 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -380,9 +380,10 @@ int public_key_verify_signature(const struct public_key *pkey, struct crypto_wait cwait; struct crypto_akcipher *tfm; struct akcipher_request *req; - struct scatterlist src_sg[2]; + struct scatterlist src_sg; char alg_name[CRYPTO_MAX_ALG_NAME]; - char *key, *ptr; + char *buf, *ptr; + size_t buf_len; int ret; pr_devel("==>%s()\n", __func__); @@ -420,34 +421,37 @@ int public_key_verify_signature(const struct public_key *pkey, if (!req) goto error_free_tfm; - key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen, - GFP_KERNEL); - if (!key) + buf_len = max_t(size_t, pkey->keylen + sizeof(u32) * 2 + pkey->paramlen, + sig->s_size + sig->digest_size); + + buf = kmalloc(buf_len, GFP_KERNEL); + if (!buf) goto error_free_req; - memcpy(key, pkey->key, pkey->keylen); - ptr = key + pkey->keylen; + memcpy(buf, pkey->key, pkey->keylen); + ptr = buf + pkey->keylen; ptr = pkey_pack_u32(ptr, pkey->algo); ptr = pkey_pack_u32(ptr, pkey->paramlen); memcpy(ptr, pkey->params, pkey->paramlen); if (pkey->key_is_private) - ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen); + ret = crypto_akcipher_set_priv_key(tfm, buf, pkey->keylen); else - ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen); + ret = crypto_akcipher_set_pub_key(tfm, buf, pkey->keylen); if (ret) - goto error_free_key; + goto error_free_buf; if (strcmp(pkey->pkey_algo, "sm2") == 0 && sig->data_size) { ret = cert_sig_digest_update(sig, tfm); if (ret) - goto error_free_key; + goto error_free_buf; } - sg_init_table(src_sg, 2); - sg_set_buf(&src_sg[0], sig->s, sig->s_size); - sg_set_buf(&src_sg[1], sig->digest, sig->digest_size); - akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size, + memcpy(buf, sig->s, sig->s_size); + memcpy(buf + sig->s_size, sig->digest, sig->digest_size); + + sg_init_one(&src_sg, buf, sig->s_size + sig->digest_size); + akcipher_request_set_crypt(req, &src_sg, NULL, sig->s_size, sig->digest_size); crypto_init_wait(&cwait); akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | @@ -455,8 +459,8 @@ int public_key_verify_signature(const struct public_key *pkey, crypto_req_done, &cwait); ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait); -error_free_key: - kfree(key); +error_free_buf: + kfree(buf); error_free_req: akcipher_request_free(req); error_free_tfm: -- cgit 1.4.1 From a8eaa9a06addbd9cb0238cb1c729921ecbb6504c Mon Sep 17 00:00:00 2001 From: Edward Lo Date: Sat, 5 Nov 2022 23:39:44 +0800 Subject: fs/ntfs3: Validate MFT flags before replaying logs commit 98bea253aa28ad8be2ce565a9ca21beb4a9419e5 upstream. Log load and replay is part of the metadata handle flow during mount operation. The $MFT record will be loaded and used while replaying logs. However, a malformed $MFT record, say, has RECORD_FLAG_DIR flag set and contains an ATTR_ROOT attribute will misguide kernel to treat it as a directory, and try to free the allocated resources when the corresponding inode is freed, which will cause an invalid kfree because the memory hasn't actually been allocated. [ 101.368647] BUG: KASAN: invalid-free in kvfree+0x2c/0x40 [ 101.369457] [ 101.369986] CPU: 0 PID: 198 Comm: mount Not tainted 6.0.0-rc7+ #5 [ 101.370529] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a1990b-prebuilt.qemu.org 04/01/2014 [ 101.371362] Call Trace: [ 101.371795] [ 101.372157] dump_stack_lvl+0x49/0x63 [ 101.372658] print_report.cold+0xf5/0x689 [ 101.373022] ? ni_write_inode+0x754/0xd90 [ 101.373378] ? kvfree+0x2c/0x40 [ 101.373698] kasan_report_invalid_free+0x77/0xf0 [ 101.374058] ? kvfree+0x2c/0x40 [ 101.374352] ? kvfree+0x2c/0x40 [ 101.374668] __kasan_slab_free+0x189/0x1b0 [ 101.374992] ? kvfree+0x2c/0x40 [ 101.375271] kfree+0x168/0x3b0 [ 101.375717] kvfree+0x2c/0x40 [ 101.376002] indx_clear+0x26/0x60 [ 101.376316] ni_clear+0xc5/0x290 [ 101.376661] ntfs_evict_inode+0x45/0x70 [ 101.377001] evict+0x199/0x280 [ 101.377432] iput.part.0+0x286/0x320 [ 101.377819] iput+0x32/0x50 [ 101.378166] ntfs_loadlog_and_replay+0x143/0x320 [ 101.378656] ? ntfs_bio_fill_1+0x510/0x510 [ 101.378968] ? iput.part.0+0x286/0x320 [ 101.379367] ntfs_fill_super+0xecb/0x1ba0 [ 101.379729] ? put_ntfs+0x1d0/0x1d0 [ 101.380046] ? vsprintf+0x20/0x20 [ 101.380542] ? mutex_unlock+0x81/0xd0 [ 101.380914] ? set_blocksize+0x95/0x150 [ 101.381597] get_tree_bdev+0x232/0x370 [ 101.382254] ? put_ntfs+0x1d0/0x1d0 [ 101.382699] ntfs_fs_get_tree+0x15/0x20 [ 101.383094] vfs_get_tree+0x4c/0x130 [ 101.383675] path_mount+0x654/0xfe0 [ 101.384203] ? putname+0x80/0xa0 [ 101.384540] ? finish_automount+0x2e0/0x2e0 [ 101.384943] ? putname+0x80/0xa0 [ 101.385362] ? kmem_cache_free+0x1c4/0x440 [ 101.385968] ? putname+0x80/0xa0 [ 101.386666] do_mount+0xd6/0xf0 [ 101.387228] ? path_mount+0xfe0/0xfe0 [ 101.387585] ? __kasan_check_write+0x14/0x20 [ 101.387979] __x64_sys_mount+0xca/0x110 [ 101.388436] do_syscall_64+0x3b/0x90 [ 101.388757] entry_SYSCALL_64_after_hwframe+0x63/0xcd [ 101.389289] RIP: 0033:0x7fa0f70e948a [ 101.390048] Code: 48 8b 0d 11 fa 2a 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 49 89 ca b8 a5 00 00 008 [ 101.391297] RSP: 002b:00007ffc24fdecc8 EFLAGS: 00000202 ORIG_RAX: 00000000000000a5 [ 101.391988] RAX: ffffffffffffffda RBX: 000055932c183060 RCX: 00007fa0f70e948a [ 101.392494] RDX: 000055932c183260 RSI: 000055932c1832e0 RDI: 000055932c18bce0 [ 101.393053] RBP: 0000000000000000 R08: 000055932c183280 R09: 0000000000000020 [ 101.393577] R10: 00000000c0ed0000 R11: 0000000000000202 R12: 000055932c18bce0 [ 101.394044] R13: 000055932c183260 R14: 0000000000000000 R15: 00000000ffffffff [ 101.394747] [ 101.395402] [ 101.396047] Allocated by task 198: [ 101.396724] kasan_save_stack+0x26/0x50 [ 101.397400] __kasan_slab_alloc+0x6d/0x90 [ 101.397974] kmem_cache_alloc_lru+0x192/0x5a0 [ 101.398524] ntfs_alloc_inode+0x23/0x70 [ 101.399137] alloc_inode+0x3b/0xf0 [ 101.399534] iget5_locked+0x54/0xa0 [ 101.400026] ntfs_iget5+0xaf/0x1780 [ 101.400414] ntfs_loadlog_and_replay+0xe5/0x320 [ 101.400883] ntfs_fill_super+0xecb/0x1ba0 [ 101.401313] get_tree_bdev+0x232/0x370 [ 101.401774] ntfs_fs_get_tree+0x15/0x20 [ 101.402224] vfs_get_tree+0x4c/0x130 [ 101.402673] path_mount+0x654/0xfe0 [ 101.403160] do_mount+0xd6/0xf0 [ 101.403537] __x64_sys_mount+0xca/0x110 [ 101.404058] do_syscall_64+0x3b/0x90 [ 101.404333] entry_SYSCALL_64_after_hwframe+0x63/0xcd [ 101.404816] [ 101.405067] The buggy address belongs to the object at ffff888008cc9ea0 [ 101.405067] which belongs to the cache ntfs_inode_cache of size 992 [ 101.406171] The buggy address is located 232 bytes inside of [ 101.406171] 992-byte region [ffff888008cc9ea0, ffff888008cca280) [ 101.406995] [ 101.408559] The buggy address belongs to the physical page: [ 101.409320] page:00000000dccf19dd refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x8cc8 [ 101.410654] head:00000000dccf19dd order:2 compound_mapcount:0 compound_pincount:0 [ 101.411533] flags: 0xfffffc0010200(slab|head|node=0|zone=1|lastcpupid=0x1fffff) [ 101.412665] raw: 000fffffc0010200 0000000000000000 dead000000000122 ffff888003695140 [ 101.413209] raw: 0000000000000000 00000000800e000e 00000001ffffffff 0000000000000000 [ 101.413799] page dumped because: kasan: bad access detected [ 101.414213] [ 101.414427] Memory state around the buggy address: [ 101.414991] ffff888008cc9e80: fc fc fc fc 00 00 00 00 00 00 00 00 00 00 00 00 [ 101.415785] ffff888008cc9f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 101.416933] >ffff888008cc9f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 101.417857] ^ [ 101.418566] ffff888008cca000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 101.419704] ffff888008cca080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Signed-off-by: Edward Lo Signed-off-by: Konstantin Komarov Cc: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/inode.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 57988fedd184..e52dfa5c7562 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -98,6 +98,12 @@ static struct inode *ntfs_read_mft(struct inode *inode, /* Record should contain $I30 root. */ is_dir = rec->flags & RECORD_FLAG_DIR; + /* MFT_REC_MFT is not a dir */ + if (is_dir && ino == MFT_REC_MFT) { + err = -EINVAL; + goto out; + } + inode->i_generation = le16_to_cpu(rec->seq); /* Enumerate all struct Attributes MFT. */ -- cgit 1.4.1 From 77ee4f8c02b803b77cc7cf19c7000d7e7de4e849 Mon Sep 17 00:00:00 2001 From: Jim Wylder Date: Wed, 17 May 2023 10:20:11 -0500 Subject: regmap: Account for register length when chunking commit 3981514180c987a79ea98f0ae06a7cbf58a9ac0f upstream. Currently, when regmap_raw_write() splits the data, it uses the max_raw_write value defined for the bus. For any bus that includes the target register address in the max_raw_write value, the chunked transmission will always exceed the maximum transmission length. To avoid this problem, subtract the length of the register and the padding from the maximum transmission. Signed-off-by: Jim Wylder --- drivers/base/regmap/regmap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7de1f27d0323..8359164bff90 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2064,6 +2064,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, size_t val_count = val_len / val_bytes; size_t chunk_count, chunk_bytes; size_t chunk_regs = val_count; + size_t max_data = map->max_raw_write - map->format.reg_bytes - + map->format.pad_bytes; int ret, i; if (!val_count) @@ -2071,8 +2073,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, if (map->use_single_write) chunk_regs = 1; - else if (map->max_raw_write && val_len > map->max_raw_write) - chunk_regs = map->max_raw_write / val_bytes; + else if (map->max_raw_write && val_len > max_data) + chunk_regs = max_data / val_bytes; chunk_count = val_count / chunk_regs; chunk_bytes = chunk_regs * val_bytes; -- cgit 1.4.1 From 3264d875f58a03aa60ddf7df6f05423db735480d Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:34 +0100 Subject: tpm, tpm_tis: Request threaded interrupt handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0c7e66e5fd69bf21034c9a9b081d7de7c3eb2cea upstream. The TIS interrupt handler at least has to read and write the interrupt status register. In case of SPI both operations result in a call to tpm_tis_spi_transfer() which uses the bus_lock_mutex of the spi device and thus must only be called from a sleepable context. To ensure this request a threaded interrupt handler. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_tis_core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index f02b583005a5..e05d2b227de3 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -805,8 +805,11 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int rc; u32 int_status; - if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags, - dev_name(&chip->dev), chip) != 0) { + + rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL, + tis_int_handler, IRQF_ONESHOT | flags, + dev_name(&chip->dev), chip); + if (rc) { dev_info(&chip->dev, "Unable to request irq: %d for probe\n", irq); return -1; -- cgit 1.4.1 From 4a9d63181f8d72d789d799afbb30ddc1173bc4ab Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Thu, 18 May 2023 05:43:51 +0000 Subject: iommu/amd/pgtbl_v2: Fix domain max address commit 11c439a19466e7feaccdbce148a75372fddaf4e9 upstream. IOMMU v2 page table supports 4 level (47 bit) or 5 level (56 bit) virtual address space. Current code assumes it can support 64bit IOVA address space. If IOVA allocator allocates virtual address > 47/56 bit (depending on page table level) then it will do wrong mapping and cause invalid translation. Hence adjust aperture size to use max address supported by the page table. Reported-by: Jerry Snitselaar Fixes: aaac38f61487 ("iommu/amd: Initial support for AMD IOMMU v2 page table") Cc: # v6.0+ Cc: Suravee Suthikulpanit Signed-off-by: Vasant Hegde Reviewed-by: Jerry Snitselaar Link: https://lore.kernel.org/r/20230518054351.9626-1-vasant.hegde@amd.com Signed-off-by: Joerg Roedel [ Modified to work with "V2 with 4 level page table" only - Vasant ] Signed-off-by: Vasant Hegde Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/amd/iommu.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index b4852d79e980..5d34416b3468 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -2101,6 +2101,15 @@ out_err: return NULL; } +static inline u64 dma_max_address(void) +{ + if (amd_iommu_pgtable == AMD_IOMMU_V1) + return ~0ULL; + + /* V2 with 4 level page table */ + return ((1ULL << PM_LEVEL_SHIFT(PAGE_MODE_4_LEVEL)) - 1); +} + static struct iommu_domain *amd_iommu_domain_alloc(unsigned type) { struct protection_domain *domain; @@ -2117,7 +2126,7 @@ static struct iommu_domain *amd_iommu_domain_alloc(unsigned type) return NULL; domain->domain.geometry.aperture_start = 0; - domain->domain.geometry.aperture_end = ~0ULL; + domain->domain.geometry.aperture_end = dma_max_address(); domain->domain.geometry.force_aperture = true; return &domain->domain; -- cgit 1.4.1 From 4042d7ad40f1c3182225461c62926f217a3d0ede Mon Sep 17 00:00:00 2001 From: Fangzhi Zuo Date: Wed, 10 May 2023 16:43:30 -0400 Subject: drm/amd/display: Have Payload Properly Created After Resume commit 482e6ad9adde69d9da08864b4ccf4dfd53edb2f0 upstream. At drm suspend sequence, MST dc_sink is removed. When commit cached MST stream back in drm resume sequence, the MST stream payload is not properly created and added into the payload table. After resume, topology change is reprobed by removing existing streams first. That leads to no payload is found in the existing payload table as below error "[drm] ERROR No payload for [MST PORT:] found in mst state" 1. In encoder .atomic_check routine, remove check existance of dc_sink 2. Bypass MST by checking existence of MST root port. dc_link_type cannot differentiate MST port before topology is rediscovered. Reviewed-by: Wayne Lin Acked-by: Tom Chung Signed-off-by: Fangzhi Zuo Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org [Adjusted for variables that were renamed between 6.1 and 6.3.] Signed-off-by: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index b46732cefe37..8ab0dd799b3c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2763,7 +2763,7 @@ static int dm_resume(void *handle) * this is the case when traversing through already created * MST connectors, should be skipped */ - if (aconnector->dc_link->type == dc_connection_mst_branch) + if (aconnector && aconnector->mst_port) continue; mutex_lock(&aconnector->hpd_lock); @@ -6492,7 +6492,7 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, int clock, bpp = 0; bool is_y420 = false; - if (!aconnector->port || !aconnector->dc_sink) + if (!aconnector->port) return 0; mst_port = aconnector->port; -- cgit 1.4.1 From a2961463d74f5c86a8dda3b41c484c28ccc4c289 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 12 Apr 2023 15:49:23 +1000 Subject: xfs: verify buffer contents when we skip log replay commit 22ed903eee23a5b174e240f1cdfa9acf393a5210 upstream. syzbot detected a crash during log recovery: XFS (loop0): Mounting V5 Filesystem bfdc47fc-10d8-4eed-a562-11a831b3f791 XFS (loop0): Torn write (CRC failure) detected at log block 0x180. Truncating head block from 0x200. XFS (loop0): Starting recovery (logdev: internal) ================================================================== BUG: KASAN: slab-out-of-bounds in xfs_btree_lookup_get_block+0x15c/0x6d0 fs/xfs/libxfs/xfs_btree.c:1813 Read of size 8 at addr ffff88807e89f258 by task syz-executor132/5074 CPU: 0 PID: 5074 Comm: syz-executor132 Not tainted 6.2.0-rc1-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/26/2022 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x1b1/0x290 lib/dump_stack.c:106 print_address_description+0x74/0x340 mm/kasan/report.c:306 print_report+0x107/0x1f0 mm/kasan/report.c:417 kasan_report+0xcd/0x100 mm/kasan/report.c:517 xfs_btree_lookup_get_block+0x15c/0x6d0 fs/xfs/libxfs/xfs_btree.c:1813 xfs_btree_lookup+0x346/0x12c0 fs/xfs/libxfs/xfs_btree.c:1913 xfs_btree_simple_query_range+0xde/0x6a0 fs/xfs/libxfs/xfs_btree.c:4713 xfs_btree_query_range+0x2db/0x380 fs/xfs/libxfs/xfs_btree.c:4953 xfs_refcount_recover_cow_leftovers+0x2d1/0xa60 fs/xfs/libxfs/xfs_refcount.c:1946 xfs_reflink_recover_cow+0xab/0x1b0 fs/xfs/xfs_reflink.c:930 xlog_recover_finish+0x824/0x920 fs/xfs/xfs_log_recover.c:3493 xfs_log_mount_finish+0x1ec/0x3d0 fs/xfs/xfs_log.c:829 xfs_mountfs+0x146a/0x1ef0 fs/xfs/xfs_mount.c:933 xfs_fs_fill_super+0xf95/0x11f0 fs/xfs/xfs_super.c:1666 get_tree_bdev+0x400/0x620 fs/super.c:1282 vfs_get_tree+0x88/0x270 fs/super.c:1489 do_new_mount+0x289/0xad0 fs/namespace.c:3145 do_mount fs/namespace.c:3488 [inline] __do_sys_mount fs/namespace.c:3697 [inline] __se_sys_mount+0x2d3/0x3c0 fs/namespace.c:3674 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3d/0xb0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7f89fa3f4aca Code: 83 c4 08 5b 5d c3 66 2e 0f 1f 84 00 00 00 00 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fffd5fb5ef8 EFLAGS: 00000206 ORIG_RAX: 00000000000000a5 RAX: ffffffffffffffda RBX: 00646975756f6e2c RCX: 00007f89fa3f4aca RDX: 0000000020000100 RSI: 0000000020009640 RDI: 00007fffd5fb5f10 RBP: 00007fffd5fb5f10 R08: 00007fffd5fb5f50 R09: 000000000000970d R10: 0000000000200800 R11: 0000000000000206 R12: 0000000000000004 R13: 0000555556c6b2c0 R14: 0000000000200800 R15: 00007fffd5fb5f50 The fuzzed image contains an AGF with an obviously garbage agf_refcount_level value of 32, and a dirty log with a buffer log item for that AGF. The ondisk AGF has a higher LSN than the recovered log item. xlog_recover_buf_commit_pass2 reads the buffer, compares the LSNs, and decides to skip replay because the ondisk buffer appears to be newer. Unfortunately, the ondisk buffer is corrupt, but recovery just read the buffer with no buffer ops specified: error = xfs_buf_read(mp->m_ddev_targp, buf_f->blf_blkno, buf_f->blf_len, buf_flags, &bp, NULL); Skipping the buffer leaves its contents in memory unverified. This sets us up for a kernel crash because xfs_refcount_recover_cow_leftovers reads the buffer (which is still around in XBF_DONE state, so no read verification) and creates a refcountbt cursor of height 32. This is impossible so we run off the end of the cursor object and crash. Fix this by invoking the verifier on all skipped buffers and aborting log recovery if the ondisk buffer is corrupt. It might be smarter to force replay the log item atop the buffer and then see if it'll pass the write verifier (like ext4 does) but for now let's go with the conservative option where we stop immediately. Link: https://syzkaller.appspot.com/bug?extid=7e9494b8b399902e994e Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner Reported-by: Danila Chernetsov Link: https://lore.kernel.org/linux-xfs/20230601164439.15404-1-listdansp@mail.ru Signed-off-by: Amir Goldstein Acked-by: Darrick J. Wong Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_buf_item_recover.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c index ffa94102094d..43167f543afc 100644 --- a/fs/xfs/xfs_buf_item_recover.c +++ b/fs/xfs/xfs_buf_item_recover.c @@ -943,6 +943,16 @@ xlog_recover_buf_commit_pass2( if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) { trace_xfs_log_recover_buf_skip(log, buf_f); xlog_recover_validate_buf_type(mp, bp, buf_f, NULLCOMMITLSN); + + /* + * We're skipping replay of this buffer log item due to the log + * item LSN being behind the ondisk buffer. Verify the buffer + * contents since we aren't going to run the write verifier. + */ + if (bp->b_ops) { + bp->b_ops->verify_read(bp); + error = bp->b_error; + } goto out_release; } -- cgit 1.4.1 From b026755cc9a8920cc07ceca7d91c0597ba554a1e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 16 May 2023 18:50:42 -0700 Subject: tls: rx: strp: don't use GFP_KERNEL in softirq context commit 74836ec828fe17b63f2006fdbf53311d691396bf upstream. When receive buffer is small, or the TCP rx queue looks too complicated to bother using it directly - we allocate a new skb and copy data into it. We already use sk->sk_allocation... but nothing actually sets it to GFP_ATOMIC on the ->sk_data_ready() path. Users of HW offload are far more likely to experience problems due to scheduling while atomic. "Copy mode" is very rarely triggered with SW crypto. Fixes: 84c61fe1a75b ("tls: rx: do not use the standard strparser") Tested-by: Shai Amiram Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/tls/tls_sw.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 2e5e7853a610..96b4545ea700 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2289,8 +2289,12 @@ static void tls_data_ready(struct sock *sk) struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); struct sk_psock *psock; + gfp_t alloc_save; + alloc_save = sk->sk_allocation; + sk->sk_allocation = GFP_ATOMIC; tls_strp_data_ready(&ctx->strp); + sk->sk_allocation = alloc_save; psock = sk_psock_get(sk); if (psock) { -- cgit 1.4.1 From e8631d84c01ece34670af0d300a6f88d86d12f70 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 28 Feb 2023 17:00:49 +0100 Subject: arm64: efi: Use SMBIOS processor version to key off Ampere quirk commit eb684408f3ea4856639675d6465f0024e498e4b1 upstream. Instead of using the SMBIOS type 1 record 'family' field, which is often modified by OEMs, use the type 4 'processor ID' and 'processor version' fields, which are set to a small set of probe-able values on all known Ampere EFI systems in the field. Fixes: 550b33cfd4452968 ("arm64: efi: Force the use of ...") Tested-by: Andrea Righi Signed-off-by: Ard Biesheuvel Signed-off-by: Jeremi Piotrowski Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/efi/libstub/arm64-stub.c | 39 +++++++++++++++++++++++------ drivers/firmware/efi/libstub/efistub.h | 41 ++++++++++++++++++++++++++++--- drivers/firmware/efi/libstub/smbios.c | 13 ++++++++-- 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 42282c5c3fe6..e2f90566b291 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -17,20 +17,43 @@ static bool system_needs_vamap(void) { - const u8 *type1_family = efi_get_smbios_string(1, family); + const struct efi_smbios_type4_record *record; + const u32 __aligned(1) *socid; + const u8 *version; /* * Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if - * SetVirtualAddressMap() has not been called prior. + * SetVirtualAddressMap() has not been called prior. Most Altra systems + * can be identified by the SMCCC soc ID, which is conveniently exposed + * via the type 4 SMBIOS records. Otherwise, test the processor version + * field. eMAG systems all appear to have the processor version field + * set to "eMAG". */ - if (!type1_family || ( - strcmp(type1_family, "eMAG") && - strcmp(type1_family, "Altra") && - strcmp(type1_family, "Altra Max"))) + record = (struct efi_smbios_type4_record *)efi_get_smbios_record(4); + if (!record) return false; - efi_warn("Working around broken SetVirtualAddressMap()\n"); - return true; + socid = (u32 *)record->processor_id; + switch (*socid & 0xffff000f) { + static char const altra[] = "Ampere(TM) Altra(TM) Processor"; + static char const emag[] = "eMAG"; + + default: + version = efi_get_smbios_string(&record->header, 4, + processor_version); + if (!version || (strncmp(version, altra, sizeof(altra) - 1) && + strncmp(version, emag, sizeof(emag) - 1))) + break; + + fallthrough; + + case 0x0a160001: // Altra + case 0x0a160002: // Altra Max + efi_warn("Working around broken SetVirtualAddressMap()\n"); + return true; + } + + return false; } efi_status_t check_platform_features(void) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 900df67a2078..970e86e3aab0 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -983,6 +983,8 @@ struct efi_smbios_record { u16 handle; }; +const struct efi_smbios_record *efi_get_smbios_record(u8 type); + struct efi_smbios_type1_record { struct efi_smbios_record header; @@ -996,13 +998,46 @@ struct efi_smbios_type1_record { u8 family; }; -#define efi_get_smbios_string(__type, __name) ({ \ +struct efi_smbios_type4_record { + struct efi_smbios_record header; + + u8 socket; + u8 processor_type; + u8 processor_family; + u8 processor_manufacturer; + u8 processor_id[8]; + u8 processor_version; + u8 voltage; + u16 external_clock; + u16 max_speed; + u16 current_speed; + u8 status; + u8 processor_upgrade; + u16 l1_cache_handle; + u16 l2_cache_handle; + u16 l3_cache_handle; + u8 serial_number; + u8 asset_tag; + u8 part_number; + u8 core_count; + u8 enabled_core_count; + u8 thread_count; + u16 processor_characteristics; + u16 processor_family2; + u16 core_count2; + u16 enabled_core_count2; + u16 thread_count2; + u16 thread_enabled; +}; + +#define efi_get_smbios_string(__record, __type, __name) ({ \ int size = sizeof(struct efi_smbios_type ## __type ## _record); \ int off = offsetof(struct efi_smbios_type ## __type ## _record, \ __name); \ - __efi_get_smbios_string(__type, off, size); \ + __efi_get_smbios_string((__record), __type, off, size); \ }) -const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize); +const u8 *__efi_get_smbios_string(const struct efi_smbios_record *record, + u8 type, int offset, int recsize); #endif diff --git a/drivers/firmware/efi/libstub/smbios.c b/drivers/firmware/efi/libstub/smbios.c index aadb422b9637..f9c159c28f46 100644 --- a/drivers/firmware/efi/libstub/smbios.c +++ b/drivers/firmware/efi/libstub/smbios.c @@ -22,19 +22,28 @@ struct efi_smbios_protocol { u8 minor_version; }; -const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize) +const struct efi_smbios_record *efi_get_smbios_record(u8 type) { struct efi_smbios_record *record; efi_smbios_protocol_t *smbios; efi_status_t status; u16 handle = 0xfffe; - const u8 *strtable; status = efi_bs_call(locate_protocol, &EFI_SMBIOS_PROTOCOL_GUID, NULL, (void **)&smbios) ?: efi_call_proto(smbios, get_next, &handle, &type, &record, NULL); if (status != EFI_SUCCESS) return NULL; + return record; +} + +const u8 *__efi_get_smbios_string(const struct efi_smbios_record *record, + u8 type, int offset, int recsize) +{ + const u8 *strtable; + + if (!record) + return NULL; strtable = (u8 *)record + record->length; for (int i = 1; i < ((u8 *)record)[offset]; i++) { -- cgit 1.4.1 From 4bc022b95392b758dfb39dd517b7d7219d4c39f2 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:30 +0200 Subject: selftests: mptcp: diag: skip if MPTCP is not supported commit 46565acdd29facbf418a11e4a3791b3c8967308d upstream. Selftests are supposed to run on any kernels, including the old ones not supporting MPTCP. A new check is then added to make sure MPTCP is supported. If not, the test stops and is marked as "skipped". Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: df62f2ec3df6 ("selftests/mptcp: add diag interface tests") Cc: stable@vger.kernel.org Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/diag.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh index 515859a5168b..dd730a35bd12 100755 --- a/tools/testing/selftests/net/mptcp/diag.sh +++ b/tools/testing/selftests/net/mptcp/diag.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +. "$(dirname "${0}")/mptcp_lib.sh" + rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) ns="ns1-$rndh" ksft_skip=4 @@ -25,6 +27,8 @@ cleanup() ip netns del $ns } +mptcp_lib_check_mptcp + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Could not run test without ip tool" -- cgit 1.4.1 From fbb6db561dd48c7958dc18d1eccbcaaaea126691 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:31 +0200 Subject: selftests: mptcp: simult flows: skip if MPTCP is not supported commit 9161f21c74a1a0e7bb39eb84ea0c86b23c92fc87 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting MPTCP. A new check is then added to make sure MPTCP is supported. If not, the test stops and is marked as "skipped". Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 1a418cb8e888 ("mptcp: simult flow self-tests") Cc: stable@vger.kernel.org Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/simult_flows.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/simult_flows.sh b/tools/testing/selftests/net/mptcp/simult_flows.sh index 40aeb5a71a2a..4a417f9d51d6 100755 --- a/tools/testing/selftests/net/mptcp/simult_flows.sh +++ b/tools/testing/selftests/net/mptcp/simult_flows.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 +. "$(dirname "${0}")/mptcp_lib.sh" + rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) ns1="ns1-$rndh" ns2="ns2-$rndh" @@ -33,6 +35,8 @@ cleanup() done } +mptcp_lib_check_mptcp + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Could not run test without ip tool" -- cgit 1.4.1 From 84683a2cf5ed90897f8d37a15e6866c6453e9b05 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sun, 28 May 2023 19:35:26 +0200 Subject: selftests: mptcp: join: avoid using 'cmp --bytes' commit d328fe87067480cf2bd0b58dab428a98d31dbb7e upstream. BusyBox's 'cmp' command doesn't support the '--bytes' parameter. Some CIs -- i.e. LKFT -- use BusyBox and have the mptcp_join.sh test failing [1] because their 'cmp' command doesn't support this '--bytes' option: cmp: unrecognized option '--bytes=1024' BusyBox v1.35.0 () multi-call binary. Usage: cmp [-ls] [-n NUM] FILE1 [FILE2] Instead, 'head --bytes' can be used as this option is supported by BusyBox. A temporary file is needed for this operation. Because it is apparently quite common to use BusyBox, it is certainly better to backport this fix to impacted kernels. Fixes: 6bf41020b72b ("selftests: mptcp: update and extend fastclose test-cases") Cc: stable@vger.kernel.org Link: https://qa-reports.linaro.org/lkft/linux-mainline-master/build/v6.3-rc5-5-g148341f0a2f5/testrun/16088933/suite/kselftest-net-mptcp/test/net_mptcp_userspace_pm_sh/log [1] Suggested-by: Paolo Abeni Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 504bcfe911ea..c7da2bd7c328 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -15,6 +15,7 @@ sout="" cin="" cinfail="" cinsent="" +tmpfile="" cout="" capout="" ns1="" @@ -168,6 +169,7 @@ cleanup() { rm -f "$cin" "$cout" "$sinfail" rm -f "$sin" "$sout" "$cinsent" "$cinfail" + rm -f "$tmpfile" cleanup_partial } @@ -362,9 +364,16 @@ check_transfer() fail_test return 1 fi - bytes="--bytes=${bytes}" + + # note: BusyBox's "cmp" command doesn't support --bytes + tmpfile=$(mktemp) + head --bytes="$bytes" "$in" > "$tmpfile" + mv "$tmpfile" "$in" + head --bytes="$bytes" "$out" > "$tmpfile" + mv "$tmpfile" "$out" + tmpfile="" fi - cmp -l "$in" "$out" ${bytes} | while read -r i a b; do + cmp -l "$in" "$out" | while read -r i a b; do local sum=$((0${a} + 0${b})) if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then echo "[ FAIL ] $what does not match (in, out):" -- cgit 1.4.1 From c3fcfe8931e1a260b0dbd07f9d22b990b7a45210 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 26 May 2023 23:57:29 -0400 Subject: ext4: enable the lazy init thread when remounting read/write commit eb1f822c76beeaa76ab8b6737ab9dc9f9798408c upstream. In commit a44be64bbecb ("ext4: don't clear SB_RDONLY when remounting r/w until quota is re-enabled") we defer clearing tyhe SB_RDONLY flag in struct super. However, we didn't defer when we checked sb_rdonly() to determine the lazy itable init thread should be enabled, with the next result that the lazy inode table initialization would not be properly started. This can cause generic/231 to fail in ext4's nojournal mode. Fix this by moving when we decide to start or stop the lazy itable init thread to after we clear the SB_RDONLY flag when we are remounting the file system read/write. Fixes a44be64bbecb ("ext4: don't clear SB_RDONLY when remounting r/w until...") Signed-off-by: Theodore Ts'o Link: https://lore.kernel.org/r/20230527035729.1001605-1-tytso@mit.edu Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index bd2e803d653f..5db1060b8684 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -6541,18 +6541,6 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) } } - /* - * Reinitialize lazy itable initialization thread based on - * current settings - */ - if (sb_rdonly(sb) || !test_opt(sb, INIT_INODE_TABLE)) - ext4_unregister_li_request(sb); - else { - ext4_group_t first_not_zeroed; - first_not_zeroed = ext4_has_uninit_itable(sb); - ext4_register_li_request(sb, first_not_zeroed); - } - /* * Handle creation of system zone data early because it can fail. * Releasing of existing data is done when we are sure remount will @@ -6590,6 +6578,18 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) if (enable_rw) sb->s_flags &= ~SB_RDONLY; + /* + * Reinitialize lazy itable initialization thread based on + * current settings + */ + if (sb_rdonly(sb) || !test_opt(sb, INIT_INODE_TABLE)) + ext4_unregister_li_request(sb); + else { + ext4_group_t first_not_zeroed; + first_not_zeroed = ext4_has_uninit_itable(sb); + ext4_register_li_request(sb, first_not_zeroed); + } + if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb)) ext4_stop_mmpd(sbi); -- cgit 1.4.1 From 2f3918bc53fb998fdeed8683ddc61194ceb84edf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 9 Jun 2023 10:34:30 +0200 Subject: Linux 6.1.33 Link: https://lore.kernel.org/r/20230607200913.334991024@linuxfoundation.org Tested-by: Shuah Khan Tested-by: Florian Fainelli Tested-by: Markus Reichelt Tested-by: Chris Paterson (CIP) Tested-by: Conor Dooley Tested-by: Linux Kernel Functional Testing Tested-by: Takeshi Ogasawara Tested-by: Bagas Sanjaya Link: https://lore.kernel.org/r/20230608175726.909746266@linuxfoundation.org Tested-by: Ron Economos Tested-by: Florian Fainelli Tested-by: Linux Kernel Functional Testing Tested-by: Chris Paterson (CIP) Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a0c3d8809e93..6be79c5c934f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 32 +SUBLEVEL = 33 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From ec3ce2c7cf8d68424ddc40ea5480c303b4a32f4f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 5 Jan 2023 21:32:00 -0800 Subject: scsi: megaraid_sas: Add flexible array member for SGLs commit a9a3629592ab7442a2e9d40281420b51c453ea9b upstream. struct MPI2_RAID_SCSI_IO_REQUEST ends with a single SGL, but expects to copy multiple. Add a flexible array member so the compiler can reason about the size of the memcpy(). This will avoid the run-time false positive warning: memcpy: detected field-spanning write (size 128) of single field "&r1_cmd->io_request->SGL" at drivers/scsi/megaraid/megaraid_sas_fusion.c:3326 (size 16) This change results in no binary output differences. Reported-by: Holger Kiehl Link: https://lore.kernel.org/all/88de8faa-56c4-693d-2d3-67152ee72057@diagnostix.dwd.de/ Cc: Kashyap Desai Cc: Sumit Saxena Cc: Shivasharan S Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: megaraidlinux.pdl@broadcom.com Cc: linux-scsi@vger.kernel.org Link: https://lore.kernel.org/r/20230106053153.never.999-kees@kernel.org Signed-off-by: Kees Cook Tested-by: Holger Kiehl Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/megaraid/megaraid_sas_fusion.c | 2 +- drivers/scsi/megaraid/megaraid_sas_fusion.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index af22ffa8f6a2..1475f3e259c1 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -3323,7 +3323,7 @@ static void megasas_prepare_secondRaid1_IO(struct megasas_instance *instance, /* copy the io request frame as well as 8 SGEs data for r1 command*/ memcpy(r1_cmd->io_request, cmd->io_request, (sizeof(struct MPI2_RAID_SCSI_IO_REQUEST))); - memcpy(&r1_cmd->io_request->SGL, &cmd->io_request->SGL, + memcpy(r1_cmd->io_request->SGLs, cmd->io_request->SGLs, (fusion->max_sge_in_main_msg * sizeof(union MPI2_SGE_IO_UNION))); /*sense buffer is different for r1 command*/ r1_cmd->io_request->SenseBufferLowAddress = diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 49e9a9048ee7..b677d80e5874 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -526,7 +526,10 @@ struct MPI2_RAID_SCSI_IO_REQUEST { __le32 Control; /* 0x3C */ union MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */ union RAID_CONTEXT_UNION RaidContext; /* 0x60 */ - union MPI2_SGE_IO_UNION SGL; /* 0x80 */ + union { + union MPI2_SGE_IO_UNION SGL; /* 0x80 */ + DECLARE_FLEX_ARRAY(union MPI2_SGE_IO_UNION, SGLs); + }; }; /* -- cgit 1.4.1 From 097acf0aa622bf10714a9134e2bfc508cd37ac03 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 17 Mar 2023 07:28:00 +0000 Subject: net: sfp: fix state loss when updating state_hw_mask commit 04361b8bb81819efb68bf39c276025e2250ac537 upstream. Andrew reports that the SFF modules on one of the ZII platforms do not indicate link up due to the SFP code believing that LOS indicating that there is no signal being received from the remote end, but in fact the LOS signal is showing that there is signal. What makes SFF modules different from SFPs is they typically have an inverted LOS, which uncovered this issue. When we read the hardware state, we mask it with state_hw_mask so we ignore anything we're not interested in. However, we don't re-read when state_hw_mask changes, leading to sfp->state being stale. Arrange for a software poll of the module state after we have parsed the EEPROM in sfp_sm_mod_probe() and updated state_*_mask. This will generate any necessary events for signal changes for the state machine as well as updating sfp->state. Reported-by: Andrew Lunn Tested-by: Andrew Lunn Fixes: 8475c4b70b04 ("net: sfp: re-implement soft state polling setup") Signed-off-by: Russell King (Oracle) Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/sfp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 766f86bdc4a0..9b1403291d92 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2199,6 +2199,11 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) break; } + /* Force a poll to re-read the hardware signal state after + * sfp_sm_mod_probe() changed state_hw_mask. + */ + mod_delayed_work(system_wq, &sfp->poll, 1); + err = sfp_hwmon_insert(sfp); if (err) dev_warn(sfp->dev, "hwmon probe failed: %pe\n", -- cgit 1.4.1 From 31c3de5f7b705909979f05ac2d9c77230c589d36 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 1 May 2023 19:33:14 +0100 Subject: spi: mt65xx: make sure operations completed before unloading [ Upstream commit 4be47a5d59cbc9396a6ffd327913eb4c8d67a32f ] When unloading the spi-mt65xx kernel module during an ongoing spi-mem operation the kernel will Oops shortly after unloading the module. This is because wait_for_completion_timeout was still running and returning into the no longer loaded module: Internal error: Oops: 0000000096000005 [#1] SMP Modules linked in: [many, but spi-mt65xx is no longer there] CPU: 0 PID: 2578 Comm: block Tainted: G W O 6.3.0-next-20230428+ #0 Hardware name: Bananapi BPI-R3 (DT) pstate: 804000c5 (Nzcv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : __lock_acquire+0x18c/0x20e8 lr : __lock_acquire+0x9b8/0x20e8 sp : ffffffc009ec3400 x29: ffffffc009ec3400 x28: 0000000000000001 x27: 0000000000000004 x26: ffffff80082888c8 x25: 0000000000000000 x24: 0000000000000000 x23: ffffffc009609da8 x22: ffffff8008288000 x21: ffffff8008288968 x20: 00000000000003c2 x19: ffffff8008be7990 x18: 00000000000002af x17: 0000000000000000 x16: 0000000000000000 x15: ffffffc008d78970 x14: 000000000000080d x13: 00000000000002af x12: 00000000ffffffea x11: 00000000ffffefff x10: ffffffc008dd0970 x9 : ffffffc008d78918 x8 : 0000000000017fe8 x7 : 0000000000000001 x6 : 0000000000000000 x5 : ffffff807fb53910 x4 : 0000000000000000 x3 : 0000000000000027 x2 : 0000000000000027 x1 : 0000000000000000 x0 : 00000000000c03c2 Call trace: __lock_acquire+0x18c/0x20e8 lock_acquire+0x100/0x2a4 _raw_spin_lock_irq+0x58/0x74 __wait_for_common+0xe0/0x1b4 wait_for_completion_timeout+0x1c/0x24 0xffffffc000acc8a4 <--- used to be mtk_spi_transfer_wait spi_mem_exec_op+0x390/0x3ec spi_mem_no_dirmap_read+0x6c/0x88 spi_mem_dirmap_read+0xcc/0x12c spinand_read_page+0xf8/0x1dc spinand_mtd_read+0x1b4/0x2fc mtd_read_oob_std+0x58/0x7c mtd_read_oob+0x8c/0x148 mtd_read+0x50/0x6c ... Prevent this by completing in mtk_spi_remove if needed. Fixes: 9f763fd20da7 ("spi: mediatek: add spi memory support for ipm design") Signed-off-by: Daniel Golle Link: https://lore.kernel.org/r/ZFAF6pJxMu1z6k4w@makrotopia.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-mt65xx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 9eab6c20dbc5..6e95efb50acb 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -1275,6 +1275,9 @@ static int mtk_spi_remove(struct platform_device *pdev) struct mtk_spi *mdata = spi_master_get_devdata(master); int ret; + if (mdata->use_spimem && !completion_done(&mdata->spimem_done)) + complete(&mdata->spimem_done); + ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; -- cgit 1.4.1 From 24845da026b8696134aa491339fdc6eecd19d466 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 25 May 2023 23:01:10 +0200 Subject: platform/surface: aggregator: Allow completion work-items to be executed in parallel [ Upstream commit 539e0a7f9105d19c00629c3f4da00330488e8c60 ] Currently, event completion work-items are restricted to be run strictly in non-parallel fashion by the respective workqueue. However, this has lead to some problems: In some instances, the event notifier function called inside this completion workqueue takes a non-negligible amount of time to execute. One such example is the battery event handling code (surface_battery.c), which can result in a full battery information refresh, involving further synchronous communication with the EC inside the event handler. This is made worse if the communication fails spuriously, generally incurring a multi-second timeout. Since the event completions are run strictly non-parallel, this blocks other events from being propagated to the respective subsystems. This becomes especially noticeable for keyboard and touchpad input, which also funnel their events through this system. Here, users have reported occasional multi-second "freezes". Note, however, that the event handling system was never intended to run purely sequentially. Instead, we have one work struct per EC/SAM subsystem, processing the event queue for that subsystem. These work structs were intended to run in parallel, allowing sequential processing of work items for each subsystem but parallel processing of work items across subsystems. The only restriction to this is the way the workqueue is created. Therefore, replace create_workqueue() with alloc_workqueue() and do not restrict the maximum number of parallel work items to be executed on that queue, resolving any cross-subsystem blockage. Fixes: c167b9c7e3d6 ("platform/surface: Add Surface Aggregator subsystem") Link: https://github.com/linux-surface/linux-surface/issues/1026 Signed-off-by: Maximilian Luz Link: https://lore.kernel.org/r/20230525210110.2785470-1-luzmaximilian@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/surface/aggregator/controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c index c6537a1b3a2e..30cea324ff95 100644 --- a/drivers/platform/surface/aggregator/controller.c +++ b/drivers/platform/surface/aggregator/controller.c @@ -825,7 +825,7 @@ static int ssam_cplt_init(struct ssam_cplt *cplt, struct device *dev) cplt->dev = dev; - cplt->wq = create_workqueue(SSAM_CPLT_WQ_NAME); + cplt->wq = alloc_workqueue(SSAM_CPLT_WQ_NAME, WQ_UNBOUND | WQ_MEM_RECLAIM, 0); if (!cplt->wq) return -ENOMEM; -- cgit 1.4.1 From ec2e12b14aa08b13cac1bd4b311a29ca2d3e4da9 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 25 May 2023 23:32:17 +0200 Subject: platform/surface: aggregator_tabletsw: Add support for book mode in KIP subsystem [ Upstream commit 9bed667033e66083d363a11e9414ad401ecc242c ] Devices with a type-cover have an additional "book" mode, deactivating type-cover input and turning off its backlight. This is currently unsupported, leading to the warning surface_aggregator_tablet_mode_switch 01:0e:01:00:01: unknown KIP cover state: 6 Therefore, add support for this state and map it to enable tablet-mode. Fixes: 9f794056db5b ("platform/surface: Add KIP/POS tablet-mode switch driver") Signed-off-by: Maximilian Luz Link: https://lore.kernel.org/r/20230525213218.2797480-2-luzmaximilian@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/surface/surface_aggregator_tabletsw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/surface/surface_aggregator_tabletsw.c b/drivers/platform/surface/surface_aggregator_tabletsw.c index 27d95a6a7851..af8b547cffdc 100644 --- a/drivers/platform/surface/surface_aggregator_tabletsw.c +++ b/drivers/platform/surface/surface_aggregator_tabletsw.c @@ -201,6 +201,7 @@ enum ssam_kip_cover_state { SSAM_KIP_COVER_STATE_LAPTOP = 0x03, SSAM_KIP_COVER_STATE_FOLDED_CANVAS = 0x04, SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05, + SSAM_KIP_COVER_STATE_BOOK = 0x06, }; static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 state) @@ -221,6 +222,9 @@ static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 stat case SSAM_KIP_COVER_STATE_FOLDED_BACK: return "folded-back"; + case SSAM_KIP_COVER_STATE_BOOK: + return "book"; + default: dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state); return ""; @@ -233,6 +237,7 @@ static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 s case SSAM_KIP_COVER_STATE_DISCONNECTED: case SSAM_KIP_COVER_STATE_FOLDED_CANVAS: case SSAM_KIP_COVER_STATE_FOLDED_BACK: + case SSAM_KIP_COVER_STATE_BOOK: return true; case SSAM_KIP_COVER_STATE_CLOSED: -- cgit 1.4.1 From fb7058dd0249e13c3b9654f415908289b62434e3 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 18 May 2023 15:04:25 +0200 Subject: spi: qup: Request DMA before enabling clocks [ Upstream commit 0c331fd1dccfba657129380ee084b95c1cedfbef ] It is usually better to request all necessary resources (clocks, regulators, ...) before starting to make use of them. That way they do not change state in case one of the resources is not available yet and probe deferral (-EPROBE_DEFER) is necessary. This is particularly important for DMA channels and IOMMUs which are not enforced by fw_devlink yet (unless you use fw_devlink.strict=1). spi-qup does this in the wrong order, the clocks are enabled and disabled again when the DMA channels are not available yet. This causes issues in some cases: On most SoCs one of the SPI QUP clocks is shared with the UART controller. When using earlycon UART is actively used during boot but might not have probed yet, usually for the same reason (waiting for the DMA controller). In this case, the brief enable/disable cycle ends up gating the clock and further UART console output will halt the system completely. Avoid this by requesting the DMA channels before changing the clock state. Fixes: 612762e82ae6 ("spi: qup: Add DMA capabilities") Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20230518-spi-qup-clk-defer-v1-1-f49fc9ca4e02@gerhold.net Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-qup.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 205e54f157b4..fb6b7738b4f5 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -1029,23 +1029,8 @@ static int spi_qup_probe(struct platform_device *pdev) return -ENXIO; } - ret = clk_prepare_enable(cclk); - if (ret) { - dev_err(dev, "cannot enable core clock\n"); - return ret; - } - - ret = clk_prepare_enable(iclk); - if (ret) { - clk_disable_unprepare(cclk); - dev_err(dev, "cannot enable iface clock\n"); - return ret; - } - master = spi_alloc_master(dev, sizeof(struct spi_qup)); if (!master) { - clk_disable_unprepare(cclk); - clk_disable_unprepare(iclk); dev_err(dev, "cannot allocate master\n"); return -ENOMEM; } @@ -1093,6 +1078,19 @@ static int spi_qup_probe(struct platform_device *pdev) spin_lock_init(&controller->lock); init_completion(&controller->done); + ret = clk_prepare_enable(cclk); + if (ret) { + dev_err(dev, "cannot enable core clock\n"); + goto error_dma; + } + + ret = clk_prepare_enable(iclk); + if (ret) { + clk_disable_unprepare(cclk); + dev_err(dev, "cannot enable iface clock\n"); + goto error_dma; + } + iomode = readl_relaxed(base + QUP_IO_M_MODES); size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode); @@ -1122,7 +1120,7 @@ static int spi_qup_probe(struct platform_device *pdev) ret = spi_qup_set_state(controller, QUP_STATE_RESET); if (ret) { dev_err(dev, "cannot set RESET state\n"); - goto error_dma; + goto error_clk; } writel_relaxed(0, base + QUP_OPERATIONAL); @@ -1146,7 +1144,7 @@ static int spi_qup_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, spi_qup_qup_irq, IRQF_TRIGGER_HIGH, pdev->name, controller); if (ret) - goto error_dma; + goto error_clk; pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(dev); @@ -1161,11 +1159,12 @@ static int spi_qup_probe(struct platform_device *pdev) disable_pm: pm_runtime_disable(&pdev->dev); +error_clk: + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); error_dma: spi_qup_release_dma(master); error: - clk_disable_unprepare(cclk); - clk_disable_unprepare(iclk); spi_master_put(master); return ret; } -- cgit 1.4.1 From 84c699681a2933ca47adc4aa6d93b3264150afb4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 7 Jun 2023 09:47:13 +0100 Subject: afs: Fix setting of mtime when creating a file/dir/symlink [ Upstream commit a27648c742104a833a01c54becc24429898d85bf ] kafs incorrectly passes a zero mtime (ie. 1st Jan 1970) to the server when creating a file, dir or symlink because the mtime recorded in the afs_operation struct gets passed to the server by the marshalling routines, but the afs_mkdir(), afs_create() and afs_symlink() functions don't set it. This gets masked if a file or directory is subsequently modified. Fix this by filling in op->mtime before calling the create op. Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Signed-off-by: David Howells Reviewed-by: Jeffrey Altman Reviewed-by: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin --- fs/afs/dir.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index f73b2f62afaa..07dc4ec73520 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -1357,6 +1357,7 @@ static int afs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, op->dentry = dentry; op->create.mode = S_IFDIR | mode; op->create.reason = afs_edit_dir_for_mkdir; + op->mtime = current_time(dir); op->ops = &afs_mkdir_operation; return afs_do_sync_operation(op); } @@ -1660,6 +1661,7 @@ static int afs_create(struct user_namespace *mnt_userns, struct inode *dir, op->dentry = dentry; op->create.mode = S_IFREG | mode; op->create.reason = afs_edit_dir_for_create; + op->mtime = current_time(dir); op->ops = &afs_create_operation; return afs_do_sync_operation(op); @@ -1795,6 +1797,7 @@ static int afs_symlink(struct user_namespace *mnt_userns, struct inode *dir, op->ops = &afs_symlink_operation; op->create.reason = afs_edit_dir_for_symlink; op->create.symlink = content; + op->mtime = current_time(dir); return afs_do_sync_operation(op); error: -- cgit 1.4.1 From e783f639b8fb8e380d731409766545c0bb5f76c1 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 24 May 2023 16:39:32 +0200 Subject: wifi: mt76: mt7615: fix possible race in mt7615_mac_sta_poll [ Upstream commit 30bc32c7c1f975cc3c14e1c7dc437266311282cf ] Grab sta_poll_lock spinlock in mt7615_mac_sta_poll routine in order to avoid possible races with mt7615_mac_add_txs() or mt7615_mac_fill_rx() removing msta pointer from sta_poll_list. Fixes: a621372a04ac ("mt76: mt7615: rework mt7615_mac_sta_poll for usb code") Signed-off-by: Lorenzo Bianconi Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/48b23404b759de4f1db2ef85975c72a4aeb1097c.1684938695.git.lorenzo@kernel.org Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index faed43b11ec9..40c80d09d108 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -921,7 +921,10 @@ void mt7615_mac_sta_poll(struct mt7615_dev *dev) msta = list_first_entry(&sta_poll_list, struct mt7615_sta, poll_list); + + spin_lock_bh(&dev->sta_poll_lock); list_del_init(&msta->poll_list); + spin_unlock_bh(&dev->sta_poll_lock); addr = mt7615_mac_wtbl_addr(dev, msta->wcid.idx) + 19 * 4; -- cgit 1.4.1 From 898c9a0ee715e712a8fbb969443926d600e64635 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 30 May 2023 19:51:49 +0000 Subject: bpf, sockmap: Avoid potential NULL dereference in sk_psock_verdict_data_ready() [ Upstream commit b320a45638296b63be8d9a901ca8bc43716b1ae1 ] syzbot found sk_psock(sk) could return NULL when called from sk_psock_verdict_data_ready(). Just make sure to handle this case. [1] general protection fault, probably for non-canonical address 0xdffffc000000005c: 0000 [#1] PREEMPT SMP KASAN KASAN: null-ptr-deref in range [0x00000000000002e0-0x00000000000002e7] CPU: 0 PID: 15 Comm: ksoftirqd/0 Not tainted 6.4.0-rc3-syzkaller-00588-g4781e965e655 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/16/2023 RIP: 0010:sk_psock_verdict_data_ready+0x19f/0x3c0 net/core/skmsg.c:1213 Code: 4c 89 e6 e8 63 70 5e f9 4d 85 e4 75 75 e8 19 74 5e f9 48 8d bb e0 02 00 00 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 07 02 00 00 48 89 ef ff 93 e0 02 00 00 e8 29 fd RSP: 0018:ffffc90000147688 EFLAGS: 00010206 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000100 RDX: 000000000000005c RSI: ffffffff8825ceb7 RDI: 00000000000002e0 RBP: ffff888076518c40 R08: 0000000000000007 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000008000 R15: ffff888076518c40 FS: 0000000000000000(0000) GS:ffff8880b9800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f901375bab0 CR3: 000000004bf26000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: tcp_data_ready+0x10a/0x520 net/ipv4/tcp_input.c:5006 tcp_data_queue+0x25d3/0x4c50 net/ipv4/tcp_input.c:5080 tcp_rcv_established+0x829/0x1f90 net/ipv4/tcp_input.c:6019 tcp_v4_do_rcv+0x65a/0x9c0 net/ipv4/tcp_ipv4.c:1726 tcp_v4_rcv+0x2cbf/0x3340 net/ipv4/tcp_ipv4.c:2148 ip_protocol_deliver_rcu+0x9f/0x480 net/ipv4/ip_input.c:205 ip_local_deliver_finish+0x2ec/0x520 net/ipv4/ip_input.c:233 NF_HOOK include/linux/netfilter.h:303 [inline] NF_HOOK include/linux/netfilter.h:297 [inline] ip_local_deliver+0x1ae/0x200 net/ipv4/ip_input.c:254 dst_input include/net/dst.h:468 [inline] ip_rcv_finish+0x1cf/0x2f0 net/ipv4/ip_input.c:449 NF_HOOK include/linux/netfilter.h:303 [inline] NF_HOOK include/linux/netfilter.h:297 [inline] ip_rcv+0xae/0xd0 net/ipv4/ip_input.c:569 __netif_receive_skb_one_core+0x114/0x180 net/core/dev.c:5491 __netif_receive_skb+0x1f/0x1c0 net/core/dev.c:5605 process_backlog+0x101/0x670 net/core/dev.c:5933 __napi_poll+0xb7/0x6f0 net/core/dev.c:6499 napi_poll net/core/dev.c:6566 [inline] net_rx_action+0x8a9/0xcb0 net/core/dev.c:6699 __do_softirq+0x1d4/0x905 kernel/softirq.c:571 run_ksoftirqd kernel/softirq.c:939 [inline] run_ksoftirqd+0x31/0x60 kernel/softirq.c:931 smpboot_thread_fn+0x659/0x9e0 kernel/smpboot.c:164 kthread+0x344/0x440 kernel/kthread.c:379 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:308 Fixes: 6df7f764cd3c ("bpf, sockmap: Wake up polling after data copy") Reported-by: syzbot Signed-off-by: Eric Dumazet Signed-off-by: Daniel Borkmann Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20230530195149.68145-1-edumazet@google.com Signed-off-by: Sasha Levin --- net/core/skmsg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 9e0f69451563..65fb6f5b21b2 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1205,7 +1205,8 @@ static void sk_psock_verdict_data_ready(struct sock *sk) rcu_read_lock(); psock = sk_psock(sk); - psock->saved_data_ready(sk); + if (psock) + psock->saved_data_ready(sk); rcu_read_unlock(); } } -- cgit 1.4.1 From 8af3119388c432b0133a068185b7664f24da4495 Mon Sep 17 00:00:00 2001 From: Qingfang DENG Date: Thu, 1 Jun 2023 09:54:32 +0800 Subject: neighbour: fix unaligned access to pneigh_entry [ Upstream commit ed779fe4c9b5a20b4ab4fd6f3e19807445bb78c7 ] After the blamed commit, the member key is longer 4-byte aligned. On platforms that do not support unaligned access, e.g., MIPS32R2 with unaligned_action set to 1, this will trigger a crash when accessing an IPv6 pneigh_entry, as the key is cast to an in6_addr pointer. Change the type of the key to u32 to make it aligned. Fixes: 62dd93181aaa ("[IPV6] NDISC: Set per-entry is_router flag in Proxy NA.") Signed-off-by: Qingfang DENG Link: https://lore.kernel.org/r/20230601015432.159066-1-dqfext@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/neighbour.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 2f2a6023fb0e..94a1599824d8 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -180,7 +180,7 @@ struct pneigh_entry { netdevice_tracker dev_tracker; u32 flags; u8 protocol; - u8 key[]; + u32 key[]; }; /* -- cgit 1.4.1 From 332f36a09c4c8035c3365ddc7dd64eee280c483c Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Wed, 31 May 2023 16:38:26 +0200 Subject: net: dsa: lan9303: allow vid != 0 in port_fdb_{add|del} methods [ Upstream commit 5a59a58ec25d44f853c26bdbfda47d73b3067435 ] LAN9303 doesn't associate FDB (ALR) entries with VLANs, it has just one global Address Logic Resolution table [1]. Ignore VID in port_fdb_{add|del} methods, go on with the global table. This is the same semantics as hellcreek or RZ/N1 implement. Visible symptoms: LAN9303_MDIO 5b050000.ethernet-1:00: port 2 failed to delete 00:xx:xx:xx:xx:cf vid 1 from fdb: -2 LAN9303_MDIO 5b050000.ethernet-1:00: port 2 failed to add 00:xx:xx:xx:xx:cf vid 1 to fdb: -95 [1] https://ww1.microchip.com/downloads/en/DeviceDoc/00002308A.pdf Fixes: 0620427ea0d6 ("net: dsa: lan9303: Add fdb/mdb manipulation") Signed-off-by: Alexander Sverdlin Reviewed-by: Vladimir Oltean Link: https://lore.kernel.org/r/20230531143826.477267-1-alexander.sverdlin@siemens.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/lan9303-core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 2e270b479143..a08e7cbb23c9 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1199,8 +1199,6 @@ static int lan9303_port_fdb_add(struct dsa_switch *ds, int port, struct lan9303 *chip = ds->priv; dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid); - if (vid) - return -EOPNOTSUPP; return lan9303_alr_add_port(chip, addr, port, false); } @@ -1212,8 +1210,6 @@ static int lan9303_port_fdb_del(struct dsa_switch *ds, int port, struct lan9303 *chip = ds->priv; dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, addr, vid); - if (vid) - return -EOPNOTSUPP; lan9303_alr_del_port(chip, addr, port); return 0; -- cgit 1.4.1 From 9166225c3b2d04c6a3d93cb8b699c943f26fd570 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 1 Jun 2023 12:13:05 +0900 Subject: net/ipv4: ping_group_range: allow GID from 2147483648 to 4294967294 [ Upstream commit e209fee4118fe9a449d4d805361eb2de6796be39 ] With this commit, all the GIDs ("0 4294967294") can be written to the "net.ipv4.ping_group_range" sysctl. Note that 4294967295 (0xffffffff) is an invalid GID (see gid_valid() in include/linux/uidgid.h), and an attempt to register this number will cause -EINVAL. Prior to this commit, only up to GID 2147483647 could be covered. Documentation/networking/ip-sysctl.rst had "0 4294967295" as an example value, but this example was wrong and causing -EINVAL. Fixes: c319b4d76b9e ("net: ipv4: add IPPROTO_ICMP socket kind") Co-developed-by: Kuniyuki Iwashima Signed-off-by: Kuniyuki Iwashima Signed-off-by: Akihiro Suda Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- Documentation/networking/ip-sysctl.rst | 4 ++-- include/net/ping.h | 6 +----- net/ipv4/sysctl_net_ipv4.c | 8 ++++---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 4ecb549fd052..3301288a7c69 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -1247,8 +1247,8 @@ ping_group_range - 2 INTEGERS Restrict ICMP_PROTO datagram sockets to users in the group range. The default is "1 0", meaning, that nobody (not even root) may create ping sockets. Setting it to "100 100" would grant permissions - to the single group. "0 4294967295" would enable it for the world, "100 - 4294967295" would enable it for the users, but not daemons. + to the single group. "0 4294967294" would enable it for the world, "100 + 4294967294" would enable it for the users, but not daemons. tcp_early_demux - BOOLEAN Enable early demux for established TCP sockets. diff --git a/include/net/ping.h b/include/net/ping.h index 9233ad3de0ad..bc7779262e60 100644 --- a/include/net/ping.h +++ b/include/net/ping.h @@ -16,11 +16,7 @@ #define PING_HTABLE_SIZE 64 #define PING_HTABLE_MASK (PING_HTABLE_SIZE-1) -/* - * gid_t is either uint or ushort. We want to pass it to - * proc_dointvec_minmax(), so it must not be larger than MAX_INT - */ -#define GID_T_MAX (((gid_t)~0U) >> 1) +#define GID_T_MAX (((gid_t)~0U) - 1) /* Compatibility glue so we can support IPv6 when it's compiled as a module */ struct pingv6_ops { diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 39dbeb607196..f68762ce4d8a 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -34,8 +34,8 @@ static int ip_ttl_min = 1; static int ip_ttl_max = 255; static int tcp_syn_retries_min = 1; static int tcp_syn_retries_max = MAX_TCP_SYNCNT; -static int ip_ping_group_range_min[] = { 0, 0 }; -static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; +static unsigned long ip_ping_group_range_min[] = { 0, 0 }; +static unsigned long ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; static u32 u32_max_div_HZ = UINT_MAX / HZ; static int one_day_secs = 24 * 3600; static u32 fib_multipath_hash_fields_all_mask __maybe_unused = @@ -162,7 +162,7 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write, { struct user_namespace *user_ns = current_user_ns(); int ret; - gid_t urange[2]; + unsigned long urange[2]; kgid_t low, high; struct ctl_table tmp = { .data = &urange, @@ -175,7 +175,7 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write, inet_get_ping_group_range_table(table, &low, &high); urange[0] = from_kgid_munged(user_ns, low); urange[1] = from_kgid_munged(user_ns, high); - ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); + ret = proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos); if (write && ret == 0) { low = make_kgid(user_ns, urange[0]); -- cgit 1.4.1 From d7612a922b210635ca9442cb00c6a3cf5aac643e Mon Sep 17 00:00:00 2001 From: KP Singh Date: Fri, 2 Jun 2023 02:26:12 +0200 Subject: bpf: Fix UAF in task local storage [ Upstream commit b0fd1852bcc21accca6260ef245356d5c141ff66 ] When task local storage was generalized for tracing programs, the bpf_task_local_storage callback was moved from a BPF LSM hook callback for security_task_free LSM hook to it's own callback. But a failure case in bad_fork_cleanup_security was missed which, when triggered, led to a dangling task owner pointer and a subsequent use-after-free. Move the bpf_task_storage_free to the very end of free_task to handle all failure cases. This issue was noticed when a BPF LSM program was attached to the task_alloc hook on a kernel with KASAN enabled. The program used bpf_task_storage_get to copy the task local storage from the current task to the new task being created. Fixes: a10787e6d58c ("bpf: Enable task local storage for tracing programs") Reported-by: Kuba Piecuch Signed-off-by: KP Singh Acked-by: Song Liu Link: https://lore.kernel.org/r/20230602002612.1117381-1-kpsingh@kernel.org Signed-off-by: Martin KaFai Lau Signed-off-by: Sasha Levin --- kernel/fork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/fork.c b/kernel/fork.c index ec913b13c5ed..6bb91fbbf73c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -559,6 +559,7 @@ void free_task(struct task_struct *tsk) arch_release_task_struct(tsk); if (tsk->flags & PF_KTHREAD) free_kthread_struct(tsk); + bpf_task_storage_free(tsk); free_task_struct(tsk); } EXPORT_SYMBOL(free_task); @@ -843,7 +844,6 @@ void __put_task_struct(struct task_struct *tsk) cgroup_free(tsk); task_numa_free(tsk, true); security_task_free(tsk); - bpf_task_storage_free(tsk); exit_creds(tsk); delayacct_tsk_free(tsk); put_signal_struct(tsk->signal); -- cgit 1.4.1 From 3849e7fcea20850ede97f3b288674f6451d32846 Mon Sep 17 00:00:00 2001 From: Rhys Rustad-Elliott Date: Fri, 2 Jun 2023 19:02:02 +0000 Subject: bpf: Fix elem_size not being set for inner maps [ Upstream commit cba41bb78d70aad98d8e61e019fd48c561f7f396 ] Commit d937bc3449fa ("bpf: make uniform use of array->elem_size everywhere in arraymap.c") changed array_map_gen_lookup to use array->elem_size instead of round_up(map->value_size, 8) as the element size when generating code to access a value in an array map. array->elem_size, however, is not set by bpf_map_meta_alloc when initializing an BPF_MAP_TYPE_ARRAY_OF_MAPS or BPF_MAP_TYPE_HASH_OF_MAPS. This results in array_map_gen_lookup incorrectly outputting code that always accesses index 0 in the array (as the index will be calculated via a multiplication with the element size, which is incorrectly set to 0). Set elem_size on the bpf_array object when allocating an array or hash of maps to fix this. Fixes: d937bc3449fa ("bpf: make uniform use of array->elem_size everywhere in arraymap.c") Signed-off-by: Rhys Rustad-Elliott Link: https://lore.kernel.org/r/20230602190110.47068-2-me@rhysre.net Signed-off-by: Martin KaFai Lau Signed-off-by: Sasha Levin --- kernel/bpf/map_in_map.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index 135205d0d560..8e87f69aae60 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -61,9 +61,13 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) /* Misc members not needed in bpf_map_meta_equal() check. */ inner_map_meta->ops = inner_map->ops; if (inner_map->ops == &array_map_ops) { + struct bpf_array *inner_array_meta = + container_of(inner_map_meta, struct bpf_array, map); + struct bpf_array *inner_array = container_of(inner_map, struct bpf_array, map); + + inner_array_meta->index_mask = inner_array->index_mask; + inner_array_meta->elem_size = inner_array->elem_size; inner_map_meta->bypass_spec_v1 = inner_map->bypass_spec_v1; - container_of(inner_map_meta, struct bpf_array, map)->index_mask = - container_of(inner_map, struct bpf_array, map)->index_mask; } fdput(f); -- cgit 1.4.1 From 76e38e6e1b35e9ae845a353dbcb3282af768a85a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 1 Jun 2023 16:04:44 +0000 Subject: net/ipv6: fix bool/int mismatch for skip_notify_on_dev_down [ Upstream commit edf2e1d2019b2730d6076dbe4c040d37d7c10bbe ] skip_notify_on_dev_down ctl table expects this field to be an int (4 bytes), not a bool (1 byte). Because proc_dou8vec_minmax() was added in 5.13, this patch converts skip_notify_on_dev_down to an int. Following patch then converts the field to u8 and use proc_dou8vec_minmax(). Fixes: 7c6bb7d2faaf ("net/ipv6: Add knob to skip DELROUTE message on device down") Signed-off-by: Eric Dumazet Reviewed-by: David Ahern Acked-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/netns/ipv6.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index b4af4837d80b..f6e6a3ab9148 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -53,7 +53,7 @@ struct netns_sysctl_ipv6 { int seg6_flowlabel; u32 ioam6_id; u64 ioam6_id_wide; - bool skip_notify_on_dev_down; + int skip_notify_on_dev_down; u8 fib_notify_on_flag_change; }; -- cgit 1.4.1 From 7a5cdd4bc1c3e53b101a0cfa949a30bc7582e6c6 Mon Sep 17 00:00:00 2001 From: Wen Gu Date: Thu, 1 Jun 2023 16:41:52 +0800 Subject: net/smc: Avoid to access invalid RMBs' MRs in SMCRv1 ADD LINK CONT [ Upstream commit c308e9ec004721a656c193243eab61a8be324657 ] SMCRv1 has a similar issue to SMCRv2 (see link below) that may access invalid MRs of RMBs when construct LLC ADD LINK CONT messages. BUG: kernel NULL pointer dereference, address: 0000000000000014 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP PTI CPU: 5 PID: 48 Comm: kworker/5:0 Kdump: loaded Tainted: G W E 6.4.0-rc3+ #49 Workqueue: events smc_llc_add_link_work [smc] RIP: 0010:smc_llc_add_link_cont+0x160/0x270 [smc] RSP: 0018:ffffa737801d3d50 EFLAGS: 00010286 RAX: ffff964f82144000 RBX: ffffa737801d3dd8 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff964f81370c30 RBP: ffffa737801d3dd4 R08: ffff964f81370000 R09: ffffa737801d3db0 R10: 0000000000000001 R11: 0000000000000060 R12: ffff964f82e70000 R13: ffff964f81370c38 R14: ffffa737801d3dd3 R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff9652bfd40000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000014 CR3: 000000008fa20004 CR4: 00000000003706e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: smc_llc_srv_rkey_exchange+0xa7/0x190 [smc] smc_llc_srv_add_link+0x3ae/0x5a0 [smc] smc_llc_add_link_work+0xb8/0x140 [smc] process_one_work+0x1e5/0x3f0 worker_thread+0x4d/0x2f0 ? __pfx_worker_thread+0x10/0x10 kthread+0xe5/0x120 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x2c/0x50 When an alernate RNIC is available in system, SMC will try to add a new link based on the RNIC for resilience. All the RMBs in use will be mapped to the new link. Then the RMBs' MRs corresponding to the new link will be filled into LLC messages. For SMCRv1, they are ADD LINK CONT messages. However smc_llc_add_link_cont() may mistakenly access to unused RMBs which haven't been mapped to the new link and have no valid MRs, thus causing a crash. So this patch fixes it. Fixes: 87f88cda2128 ("net/smc: rkey processing for a new link as SMC client") Link: https://lore.kernel.org/r/1685101741-74826-3-git-send-email-guwen@linux.alibaba.com Signed-off-by: Wen Gu Reviewed-by: Wenjia Zhang Reviewed-by: Tony Lu Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/smc/smc_llc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index 3008dfdf7c55..760f8bbff822 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -851,6 +851,8 @@ static int smc_llc_add_link_cont(struct smc_link *link, addc_llc->num_rkeys = *num_rkeys_todo; n = *num_rkeys_todo; for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) { + while (*buf_pos && !(*buf_pos)->used) + *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); if (!*buf_pos) { addc_llc->num_rkeys = addc_llc->num_rkeys - *num_rkeys_todo; @@ -867,8 +869,6 @@ static int smc_llc_add_link_cont(struct smc_link *link, (*num_rkeys_todo)--; *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); - while (*buf_pos && !(*buf_pos)->used) - *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); } addc_llc->hd.common.llc_type = SMC_LLC_ADD_LINK_CONT; addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont); -- cgit 1.4.1 From b3fc768a746bf5da0bffe9a1d525c61a3208b56c Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 2 Jun 2023 17:46:58 +0800 Subject: net: enetc: correct the statistics of rx bytes [ Upstream commit 7190d0ff0e17690a9b1279d84a06473600ba2060 ] The rx_bytes of struct net_device_stats should count the length of ethernet frames excluding the FCS. However, there are two problems with the rx_bytes statistics of the current enetc driver. one is that the length of VLAN header is not counted if the VLAN extraction feature is enabled. The other is that the length of L2 header is not counted, because eth_type_trans() is invoked before updating rx_bytes which will subtract the length of L2 header from skb->len. BTW, the rx_bytes statistics of XDP path also have similar problem, I will fix it in another patch. Fixes: a800abd3ecb9 ("net: enetc: move skb creation into enetc_build_skb") Signed-off-by: Wei Fang Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/enetc/enetc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index e96449eedfb5..df7747e49bb8 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1209,7 +1209,13 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring, if (!skb) break; - rx_byte_cnt += skb->len; + /* When set, the outer VLAN header is extracted and reported + * in the receive buffer descriptor. So rx_byte_cnt should + * add the length of the extracted VLAN header. + */ + if (bd_status & ENETC_RXBD_FLAG_VLAN) + rx_byte_cnt += VLAN_HLEN; + rx_byte_cnt += skb->len + ETH_HLEN; rx_frm_cnt++; napi_gro_receive(napi, skb); -- cgit 1.4.1 From a22c0a03463075e75464ec627fb6175eaa6a263b Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 2 Jun 2023 17:46:59 +0800 Subject: net: enetc: correct rx_bytes statistics of XDP [ Upstream commit fdebd850cc065495abf1d64756496050bb22db67 ] The rx_bytes statistics of XDP are always zero, because rx_byte_cnt is not updated after it is initialized to 0. So fix it. Fixes: d1b15102dd16 ("net: enetc: add support for XDP_DROP and XDP_PASS") Signed-off-by: Wei Fang Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/enetc/enetc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index df7747e49bb8..25c303406e6b 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1538,6 +1538,14 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring, enetc_build_xdp_buff(rx_ring, bd_status, &rxbd, &i, &cleaned_cnt, &xdp_buff); + /* When set, the outer VLAN header is extracted and reported + * in the receive buffer descriptor. So rx_byte_cnt should + * add the length of the extracted VLAN header. + */ + if (bd_status & ENETC_RXBD_FLAG_VLAN) + rx_byte_cnt += VLAN_HLEN; + rx_byte_cnt += xdp_get_buff_len(&xdp_buff); + xdp_act = bpf_prog_run_xdp(prog, &xdp_buff); switch (xdp_act) { -- cgit 1.4.1 From 1d37434ffc1376306167dc61f37f78da18455b74 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 2 Jun 2023 12:37:47 +0000 Subject: net/sched: fq_pie: ensure reasonable TCA_FQ_PIE_QUANTUM values [ Upstream commit cd2b8113c2e8b9f5a88a942e1eaca61eba401b85 ] We got multiple syzbot reports, all duplicates of the following [1] syzbot managed to install fq_pie with a zero TCA_FQ_PIE_QUANTUM, thus triggering infinite loops. Use limits similar to sch_fq, with commits 3725a269815b ("pkt_sched: fq: avoid hang when quantum 0") and d9e15a273306 ("pkt_sched: fq: do not accept silly TCA_FQ_QUANTUM") [1] watchdog: BUG: soft lockup - CPU#0 stuck for 26s! [swapper/0:0] Modules linked in: irq event stamp: 172817 hardirqs last enabled at (172816): [] __el1_irq arch/arm64/kernel/entry-common.c:476 [inline] hardirqs last enabled at (172816): [] el1_interrupt+0x58/0x68 arch/arm64/kernel/entry-common.c:486 hardirqs last disabled at (172817): [] __el1_irq arch/arm64/kernel/entry-common.c:468 [inline] hardirqs last disabled at (172817): [] el1_interrupt+0x24/0x68 arch/arm64/kernel/entry-common.c:486 softirqs last enabled at (167634): [] softirq_handle_end kernel/softirq.c:414 [inline] softirqs last enabled at (167634): [] __do_softirq+0xac0/0xd54 kernel/softirq.c:600 softirqs last disabled at (167701): [] ____do_softirq+0x14/0x20 arch/arm64/kernel/irq.c:80 CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.4.0-rc3-syzkaller-geb0f1697d729 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/28/2023 pstate: 80400005 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : fq_pie_qdisc_dequeue+0x10c/0x8ac net/sched/sch_fq_pie.c:246 lr : fq_pie_qdisc_dequeue+0xe4/0x8ac net/sched/sch_fq_pie.c:240 sp : ffff800008007210 x29: ffff800008007280 x28: ffff0000c86f7890 x27: ffff0000cb20c2e8 x26: ffff0000cb20c2f0 x25: dfff800000000000 x24: ffff0000cb20c2e0 x23: ffff0000c86f7880 x22: 0000000000000040 x21: 1fffe000190def10 x20: ffff0000cb20c2e0 x19: ffff0000cb20c2e0 x18: ffff800008006e60 x17: 0000000000000000 x16: ffff80000850af6c x15: 0000000000000302 x14: 0000000000000100 x13: 0000000000000000 x12: 0000000000000001 x11: 0000000000000302 x10: 0000000000000100 x9 : 0000000000000000 x8 : 0000000000000000 x7 : ffff80000841c468 x6 : 0000000000000000 x5 : 0000000000000001 x4 : 0000000000000001 x3 : 0000000000000000 x2 : ffff0000cb20c2e0 x1 : ffff0000cb20c2e0 x0 : 0000000000000001 Call trace: fq_pie_qdisc_dequeue+0x10c/0x8ac net/sched/sch_fq_pie.c:246 dequeue_skb net/sched/sch_generic.c:292 [inline] qdisc_restart net/sched/sch_generic.c:397 [inline] __qdisc_run+0x1fc/0x231c net/sched/sch_generic.c:415 __dev_xmit_skb net/core/dev.c:3868 [inline] __dev_queue_xmit+0xc80/0x3318 net/core/dev.c:4210 dev_queue_xmit include/linux/netdevice.h:3085 [inline] neigh_connected_output+0x2f8/0x38c net/core/neighbour.c:1581 neigh_output include/net/neighbour.h:544 [inline] ip6_finish_output2+0xd60/0x1a1c net/ipv6/ip6_output.c:134 __ip6_finish_output net/ipv6/ip6_output.c:195 [inline] ip6_finish_output+0x538/0x8c8 net/ipv6/ip6_output.c:206 NF_HOOK_COND include/linux/netfilter.h:292 [inline] ip6_output+0x270/0x594 net/ipv6/ip6_output.c:227 dst_output include/net/dst.h:458 [inline] NF_HOOK include/linux/netfilter.h:303 [inline] ndisc_send_skb+0xc30/0x1790 net/ipv6/ndisc.c:508 ndisc_send_rs+0x47c/0x5d4 net/ipv6/ndisc.c:718 addrconf_rs_timer+0x300/0x58c net/ipv6/addrconf.c:3936 call_timer_fn+0x19c/0x8cc kernel/time/timer.c:1700 expire_timers kernel/time/timer.c:1751 [inline] __run_timers+0x55c/0x734 kernel/time/timer.c:2022 run_timer_softirq+0x7c/0x114 kernel/time/timer.c:2035 __do_softirq+0x2d0/0xd54 kernel/softirq.c:571 ____do_softirq+0x14/0x20 arch/arm64/kernel/irq.c:80 call_on_irq_stack+0x24/0x4c arch/arm64/kernel/entry.S:882 do_softirq_own_stack+0x20/0x2c arch/arm64/kernel/irq.c:85 invoke_softirq kernel/softirq.c:452 [inline] __irq_exit_rcu+0x28c/0x534 kernel/softirq.c:650 irq_exit_rcu+0x14/0x84 kernel/softirq.c:662 __el1_irq arch/arm64/kernel/entry-common.c:472 [inline] el1_interrupt+0x38/0x68 arch/arm64/kernel/entry-common.c:486 el1h_64_irq_handler+0x18/0x24 arch/arm64/kernel/entry-common.c:491 el1h_64_irq+0x64/0x68 arch/arm64/kernel/entry.S:587 __daif_local_irq_enable arch/arm64/include/asm/irqflags.h:33 [inline] arch_local_irq_enable+0x8/0xc arch/arm64/include/asm/irqflags.h:55 cpuidle_idle_call kernel/sched/idle.c:170 [inline] do_idle+0x1f0/0x4e8 kernel/sched/idle.c:282 cpu_startup_entry+0x24/0x28 kernel/sched/idle.c:379 rest_init+0x2dc/0x2f4 init/main.c:735 start_kernel+0x0/0x55c init/main.c:834 start_kernel+0x3f0/0x55c init/main.c:1088 __primary_switched+0xb8/0xc0 arch/arm64/kernel/head.S:523 Fixes: ec97ecf1ebe4 ("net: sched: add Flow Queue PIE packet scheduler") Reported-by: syzbot Signed-off-by: Eric Dumazet Reviewed-by: Jamal Hadi Salim Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/sch_fq_pie.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 6980796d435d..c699e5095607 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -201,6 +201,11 @@ out: return NET_XMIT_CN; } +static struct netlink_range_validation fq_pie_q_range = { + .min = 1, + .max = 1 << 20, +}; + static const struct nla_policy fq_pie_policy[TCA_FQ_PIE_MAX + 1] = { [TCA_FQ_PIE_LIMIT] = {.type = NLA_U32}, [TCA_FQ_PIE_FLOWS] = {.type = NLA_U32}, @@ -208,7 +213,8 @@ static const struct nla_policy fq_pie_policy[TCA_FQ_PIE_MAX + 1] = { [TCA_FQ_PIE_TUPDATE] = {.type = NLA_U32}, [TCA_FQ_PIE_ALPHA] = {.type = NLA_U32}, [TCA_FQ_PIE_BETA] = {.type = NLA_U32}, - [TCA_FQ_PIE_QUANTUM] = {.type = NLA_U32}, + [TCA_FQ_PIE_QUANTUM] = + NLA_POLICY_FULL_RANGE(NLA_U32, &fq_pie_q_range), [TCA_FQ_PIE_MEMORY_LIMIT] = {.type = NLA_U32}, [TCA_FQ_PIE_ECN_PROB] = {.type = NLA_U32}, [TCA_FQ_PIE_ECN] = {.type = NLA_U32}, -- cgit 1.4.1 From 7bf7bebdc20df3d53a6c7af583d704e811fed85d Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 29 Mar 2023 20:24:34 +0300 Subject: drm/i915: Explain the magic numbers for AUX SYNC/precharge length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 26bfc3f36f2104c174dfc72415547d5c28ef3f1c ] Replace the hardcoded final numbers in the AUX SYNC/precharge setup, and derive those from numbers from the (e)DP specs. The new functions can serve as the single point of truth for the number of SYNC pulses we use. Cc: Jouni Högander Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230329172434.18744-2-ville.syrjala@linux.intel.com Reviewed-by: Jouni Högander Stable-dep-of: 2d6f2f79e065 ("drm/i915: Use 18 fast wake AUX sync len") Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_dp_aux.c | 32 ++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c index 7f3f2d50e6cd..832586877099 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c @@ -119,6 +119,32 @@ static u32 skl_get_aux_clock_divider(struct intel_dp *intel_dp, int index) return index ? 0 : 1; } +static int intel_dp_aux_sync_len(void) +{ + int precharge = 16; /* 10-16 */ + int preamble = 16; + + return precharge + preamble; +} + +static int intel_dp_aux_fw_sync_len(void) +{ + int precharge = 16; /* 10-16 */ + int preamble = 8; + + return precharge + preamble; +} + +static int g4x_dp_aux_precharge_len(void) +{ + int precharge_min = 10; + int preamble = 16; + + /* HW wants the length of the extra precharge in 2us units */ + return (intel_dp_aux_sync_len() - + precharge_min - preamble) / 2; +} + static u32 g4x_get_aux_send_ctl(struct intel_dp *intel_dp, int send_bytes, u32 aux_clock_divider) @@ -141,7 +167,7 @@ static u32 g4x_get_aux_send_ctl(struct intel_dp *intel_dp, timeout | DP_AUX_CH_CTL_RECEIVE_ERROR | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | - (3 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | + (g4x_dp_aux_precharge_len() << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT); } @@ -165,8 +191,8 @@ static u32 skl_get_aux_send_ctl(struct intel_dp *intel_dp, DP_AUX_CH_CTL_TIME_OUT_MAX | DP_AUX_CH_CTL_RECEIVE_ERROR | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | - DP_AUX_CH_CTL_FW_SYNC_PULSE_SKL(24) | - DP_AUX_CH_CTL_SYNC_PULSE_SKL(32); + DP_AUX_CH_CTL_FW_SYNC_PULSE_SKL(intel_dp_aux_fw_sync_len()) | + DP_AUX_CH_CTL_SYNC_PULSE_SKL(intel_dp_aux_sync_len()); if (intel_tc_port_in_tbt_alt_mode(dig_port)) ret |= DP_AUX_CH_CTL_TBT_IO; -- cgit 1.4.1 From 5f285409c91bb97d78b05477d57a921ba1156465 Mon Sep 17 00:00:00 2001 From: Jouni Högander Date: Tue, 30 May 2023 13:16:49 +0300 Subject: drm/i915: Use 18 fast wake AUX sync len MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2d6f2f79e06571d41eb1223abebe9097511c9544 ] HW default for wake sync pulses is 18. 10 precharge and 8 preamble. There is no reason to change this especially as it is causing problems with certain eDP panels. v3: Change "Fixes:" commit v2: Remove "fast wake" repeat from subject Signed-off-by: Jouni Högander Fixes: e1c71f8f9180 ("drm/i915: Fix fast wake AUX sync len") Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/8475 Reviewed-by: Luca Coelho Link: https://patchwork.freedesktop.org/patch/msgid/20230530101649.2549949-1-jouni.hogander@intel.com (cherry picked from commit b29a20f7c4995a059ed764ce42389857426397c7) Signed-off-by: Joonas Lahtinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_dp_aux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c index 832586877099..ab357161ccc3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c @@ -129,7 +129,7 @@ static int intel_dp_aux_sync_len(void) static int intel_dp_aux_fw_sync_len(void) { - int precharge = 16; /* 10-16 */ + int precharge = 10; /* 10-16 */ int preamble = 8; return precharge + preamble; -- cgit 1.4.1 From 17aac1200222e529a936b0bf9fa9827d0fbba21f Mon Sep 17 00:00:00 2001 From: Zhengping Jiang Date: Wed, 24 May 2023 17:11:58 -0700 Subject: Bluetooth: hci_sync: add lock to protect HCI_UNREGISTER [ Upstream commit 1857c19941c87eb36ad47f22a406be5dfe5eff9f ] When the HCI_UNREGISTER flag is set, no jobs should be scheduled. Fix potential race when HCI_UNREGISTER is set after the flag is tested in hci_cmd_sync_queue. Fixes: 0b94f2651f56 ("Bluetooth: hci_sync: Fix queuing commands when HCI_UNREGISTER is set") Signed-off-by: Zhengping Jiang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 2 ++ net/bluetooth/hci_sync.c | 20 ++++++++++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 061fec6fd015..84c5ce57eab6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -513,6 +513,7 @@ struct hci_dev { struct work_struct cmd_sync_work; struct list_head cmd_sync_work_list; struct mutex cmd_sync_work_lock; + struct mutex unregister_lock; struct work_struct cmd_sync_cancel_work; struct work_struct reenable_adv_work; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 334e308451f5..ac36e7ae70b2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2685,7 +2685,9 @@ void hci_unregister_dev(struct hci_dev *hdev) { BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); + mutex_lock(&hdev->unregister_lock); hci_dev_set_flag(hdev, HCI_UNREGISTER); + mutex_unlock(&hdev->unregister_lock); write_lock(&hci_dev_list_lock); list_del(&hdev->list); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index e8b78104a407..40a6cfa2f9a0 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -629,6 +629,7 @@ void hci_cmd_sync_init(struct hci_dev *hdev) INIT_WORK(&hdev->cmd_sync_work, hci_cmd_sync_work); INIT_LIST_HEAD(&hdev->cmd_sync_work_list); mutex_init(&hdev->cmd_sync_work_lock); + mutex_init(&hdev->unregister_lock); INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work); INIT_WORK(&hdev->reenable_adv_work, reenable_adv); @@ -688,14 +689,19 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, void *data, hci_cmd_sync_work_destroy_t destroy) { struct hci_cmd_sync_work_entry *entry; + int err = 0; - if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) - return -ENODEV; + mutex_lock(&hdev->unregister_lock); + if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) { + err = -ENODEV; + goto unlock; + } entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - + if (!entry) { + err = -ENOMEM; + goto unlock; + } entry->func = func; entry->data = data; entry->destroy = destroy; @@ -706,7 +712,9 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, queue_work(hdev->req_workqueue, &hdev->cmd_sync_work); - return 0; +unlock: + mutex_unlock(&hdev->unregister_lock); + return err; } EXPORT_SYMBOL(hci_cmd_sync_queue); -- cgit 1.4.1 From 9c7e51b94709f6aaf24a32bd267e75e89f3a0b2c Mon Sep 17 00:00:00 2001 From: Ying Hsu Date: Wed, 31 May 2023 03:44:56 +0000 Subject: Bluetooth: Fix l2cap_disconnect_req deadlock [ Upstream commit 02c5ea5246a44d6ffde0fddebfc1d56188052976 ] L2CAP assumes that the locks conn->chan_lock and chan->lock are acquired in the order conn->chan_lock, chan->lock to avoid potential deadlock. For example, l2sock_shutdown acquires these locks in the order: mutex_lock(&conn->chan_lock) l2cap_chan_lock(chan) However, l2cap_disconnect_req acquires chan->lock in l2cap_get_chan_by_scid first and then acquires conn->chan_lock before calling l2cap_chan_del. This means that these locks are acquired in unexpected order, which leads to potential deadlock: l2cap_chan_lock(c) mutex_lock(&conn->chan_lock) This patch releases chan->lock before acquiring the conn_chan_lock to avoid the potential deadlock. Fixes: a2a9339e1c9d ("Bluetooth: L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp}") Signed-off-by: Ying Hsu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ee8f806534df..1287de387bc5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4664,7 +4664,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, chan->ops->set_shutdown(chan); + l2cap_chan_unlock(chan); mutex_lock(&conn->chan_lock); + l2cap_chan_lock(chan); l2cap_chan_del(chan, ECONNRESET); mutex_unlock(&conn->chan_lock); @@ -4703,7 +4705,9 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, return 0; } + l2cap_chan_unlock(chan); mutex_lock(&conn->chan_lock); + l2cap_chan_lock(chan); l2cap_chan_del(chan, 0); mutex_unlock(&conn->chan_lock); -- cgit 1.4.1 From 66b3f7425a9c5ebc2174539a55ba4ccf4cf1272c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Thu, 1 Jun 2023 09:34:44 +0300 Subject: Bluetooth: ISO: don't try to remove CIG if there are bound CIS left [ Upstream commit 6c242c64a09e78349fb0a5f0a6f8076a3d7c0bb4 ] Consider existing BOUND & CONNECT state CIS to block CIG removal. Otherwise, under suitable timing conditions we may attempt to remove CIG while Create CIS is pending, which fails. Fixes: 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_conn.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c2c6dea01cc9..ab9f00252dc2 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -966,6 +966,8 @@ static void cis_cleanup(struct hci_conn *conn) /* Check if ISO connection is a CIS and remove CIG if there are * no other connections using it. */ + hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_BOUND, &d); + hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECT, &d); hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d); if (d.count) return; -- cgit 1.4.1 From 3e8a7573ff9f43b76e7056b5ed32ea4b93e6ca10 Mon Sep 17 00:00:00 2001 From: Sungwoo Kim Date: Sat, 3 Jun 2023 08:28:09 -0400 Subject: Bluetooth: L2CAP: Add missing checks for invalid DCID [ Upstream commit 75767213f3d9b97f63694d02260b6a49a2271876 ] When receiving a connect response we should make sure that the DCID is within the valid range and that we don't already have another channel allocated for the same DCID. Missing checks may violate the specification (BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 3, Part A, Page 1046). Fixes: 40624183c202 ("Bluetooth: L2CAP: Add missing checks for invalid LE DCID") Signed-off-by: Sungwoo Kim Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1287de387bc5..02fc9961464c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4307,6 +4307,10 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn, result = __le16_to_cpu(rsp->result); status = __le16_to_cpu(rsp->status); + if (result == L2CAP_CR_SUCCESS && (dcid < L2CAP_CID_DYN_START || + dcid > L2CAP_CID_DYN_END)) + return -EPROTO; + BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status); @@ -4338,6 +4342,11 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn, switch (result) { case L2CAP_CR_SUCCESS: + if (__l2cap_get_chan_by_dcid(conn, dcid)) { + err = -EBADSLT; + break; + } + l2cap_state_change(chan, BT_CONFIG); chan->ident = 0; chan->dcid = dcid; -- cgit 1.4.1 From 434cf4fbee4e3c26f1305865307ae52097e1e342 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Jun 2023 12:11:23 +0300 Subject: wifi: mac80211: use correct iftype HE cap [ Upstream commit c37ab22bb1a43cdca8bf69cc0a22f1ccfc449e68 ] We already check that the right iftype capa exists, but then don't use it. Assign it to a variable so we can actually use it, and then do that. Fixes: bac2fd3d7534 ("mac80211: remove use of ieee80211_get_he_sta_cap()") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230604120651.0e908e5c5fdd.Iac142549a6144ac949ebd116b921a59ae5282735@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/he.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/mac80211/he.c b/net/mac80211/he.c index 729f261520c7..0322abae0825 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -3,7 +3,7 @@ * HE handling * * Copyright(c) 2017 Intel Deutschland GmbH - * Copyright(c) 2019 - 2022 Intel Corporation + * Copyright(c) 2019 - 2023 Intel Corporation */ #include "ieee80211_i.h" @@ -114,6 +114,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, struct link_sta_info *link_sta) { struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; + const struct ieee80211_sta_he_cap *own_he_cap_ptr; struct ieee80211_sta_he_cap own_he_cap; struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie; u8 he_ppe_size; @@ -123,12 +124,16 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, memset(he_cap, 0, sizeof(*he_cap)); - if (!he_cap_ie || - !ieee80211_get_he_iftype_cap(sband, - ieee80211_vif_type_p2p(&sdata->vif))) + if (!he_cap_ie) return; - own_he_cap = sband->iftype_data->he_cap; + own_he_cap_ptr = + ieee80211_get_he_iftype_cap(sband, + ieee80211_vif_type_p2p(&sdata->vif)); + if (!own_he_cap_ptr) + return; + + own_he_cap = *own_he_cap_ptr; /* Make sure size is OK */ mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap_ie_elem); -- cgit 1.4.1 From 8b6ab4bfba6f4391cb5581bc2df77daeb943b318 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Jun 2023 12:11:18 +0300 Subject: wifi: cfg80211: reject bad AP MLD address [ Upstream commit 727073ca5e55ab6a07df316250be8a12606e8677 ] When trying to authenticate, if the AP MLD address isn't a valid address, mac80211 can throw a warning. Avoid that by rejecting such addresses. Fixes: d648c23024bd ("wifi: nl80211: support MLO in auth/assoc") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230604120651.89188912bd1d.I8dbc6c8ee0cb766138803eec59508ef4ce477709@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/nl80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7320d676ce3a..087c0c442e23 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10542,6 +10542,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_MLD_ADDR]) return -EINVAL; req.ap_mld_addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]); + if (!is_valid_ether_addr(req.ap_mld_addr)) + return -EINVAL; } req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, -- cgit 1.4.1 From 4dd40fec5bf2ea0b247611eea2c831d16919a85c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Jun 2023 12:11:16 +0300 Subject: wifi: mac80211: mlme: fix non-inheritence element [ Upstream commit 68c228557d52616cf040651abefda9839de7086a ] There were two bugs when creating the non-inheritence element: 1) 'at_extension' needs to be declared outside the loop, otherwise the value resets every iteration and we can never really switch properly 2) 'added' never got set to true, so we always cut off the extension element again at the end of the function This shows another issue that we might add a list but no extension list, but we need to make the extension list a zero-length one in that case. Fix all these issues. While at it, add a comment explaining the trim. Fixes: 81151ce462e5 ("wifi: mac80211: support MLO authentication/association with one link") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230604120651.3addaa5c4782.If3a78f9305997ad7ef4ba7ffc17a8234c956f613@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/mlme.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0125b3e6175b..dc9e7eb7dd85 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1143,6 +1143,7 @@ static void ieee80211_add_non_inheritance_elem(struct sk_buff *skb, const u16 *inner) { unsigned int skb_len = skb->len; + bool at_extension = false; bool added = false; int i, j; u8 *len, *list_len = NULL; @@ -1154,7 +1155,6 @@ static void ieee80211_add_non_inheritance_elem(struct sk_buff *skb, for (i = 0; i < PRESENT_ELEMS_MAX && outer[i]; i++) { u16 elem = outer[i]; bool have_inner = false; - bool at_extension = false; /* should at least be sorted in the sense of normal -> ext */ WARN_ON(at_extension && elem < PRESENT_ELEM_EXT_OFFS); @@ -1183,8 +1183,14 @@ static void ieee80211_add_non_inheritance_elem(struct sk_buff *skb, } *list_len += 1; skb_put_u8(skb, (u8)elem); + added = true; } + /* if we added a list but no extension list, make a zero-len one */ + if (added && (!at_extension || !list_len)) + skb_put_u8(skb, 0); + + /* if nothing added remove extension element completely */ if (!added) skb_trim(skb, skb_len); else -- cgit 1.4.1 From 79c975514cf198355e9f47fb361c69a435aff3e8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Jun 2023 12:11:15 +0300 Subject: wifi: mac80211: don't translate beacon/presp addrs [ Upstream commit 47c171a426e305f2225b92ed7b5e0a990c95f6d4 ] Don't do link address translation for beacons and probe responses, this leads to reporting multiple scan list entries for the same AP (one with the MLD address) which just breaks things. We might need to extend this in the future for some other (action) frames that aren't MLD addressed. Fixes: 42fb9148c078 ("wifi: mac80211: do link->MLD address translation on RX") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230604120651.62adead1b43a.Ifc25eed26ebf3b269f60b1ec10060156d0e7ec0d@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 44e407e1a14c..0f81492da0b4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -4857,7 +4857,9 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, } if (unlikely(rx->sta && rx->sta->sta.mlo) && - is_unicast_ether_addr(hdr->addr1)) { + is_unicast_ether_addr(hdr->addr1) && + !ieee80211_is_probe_resp(hdr->frame_control) && + !ieee80211_is_beacon(hdr->frame_control)) { /* translate to MLD addresses */ if (ether_addr_equal(link->conf->addr, hdr->addr1)) ether_addr_copy(hdr->addr1, rx->sdata->vif.addr); -- cgit 1.4.1 From 4a64e92846faa2ccbda9d2b8a04a8a4d3de94af0 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Mon, 5 Jun 2023 16:56:00 +0530 Subject: qed/qede: Fix scheduling while atomic [ Upstream commit 42510dffd0e2c27046905f742172ed6662af5557 ] Statistics read through bond interface via sysfs causes below bug and traces as it triggers the bonding module to collect the slave device statistics while holding the spinlock, beneath that qede->qed driver statistics flow gets scheduled out due to usleep_range() used in PTT acquire logic [ 3673.988874] Hardware name: HPE ProLiant DL365 Gen10 Plus/ProLiant DL365 Gen10 Plus, BIOS A42 10/29/2021 [ 3673.988878] Call Trace: [ 3673.988891] dump_stack_lvl+0x34/0x44 [ 3673.988908] __schedule_bug.cold+0x47/0x53 [ 3673.988918] __schedule+0x3fb/0x560 [ 3673.988929] schedule+0x43/0xb0 [ 3673.988932] schedule_hrtimeout_range_clock+0xbf/0x1b0 [ 3673.988937] ? __hrtimer_init+0xc0/0xc0 [ 3673.988950] usleep_range+0x5e/0x80 [ 3673.988955] qed_ptt_acquire+0x2b/0xd0 [qed] [ 3673.988981] _qed_get_vport_stats+0x141/0x240 [qed] [ 3673.989001] qed_get_vport_stats+0x18/0x80 [qed] [ 3673.989016] qede_fill_by_demand_stats+0x37/0x400 [qede] [ 3673.989028] qede_get_stats64+0x19/0xe0 [qede] [ 3673.989034] dev_get_stats+0x5c/0xc0 [ 3673.989045] netstat_show.constprop.0+0x52/0xb0 [ 3673.989055] dev_attr_show+0x19/0x40 [ 3673.989065] sysfs_kf_seq_show+0x9b/0xf0 [ 3673.989076] seq_read_iter+0x120/0x4b0 [ 3673.989087] new_sync_read+0x118/0x1a0 [ 3673.989095] vfs_read+0xf3/0x180 [ 3673.989099] ksys_read+0x5f/0xe0 [ 3673.989102] do_syscall_64+0x3b/0x90 [ 3673.989109] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 3673.989115] RIP: 0033:0x7f8467d0b082 [ 3673.989119] Code: c0 e9 b2 fe ff ff 50 48 8d 3d ca 05 08 00 e8 35 e7 01 00 0f 1f 44 00 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 0f 05 <48> 3d 00 f0 ff ff 77 56 c3 0f 1f 44 00 00 48 83 ec 28 48 89 54 24 [ 3673.989121] RSP: 002b:00007ffffb21fd08 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 [ 3673.989127] RAX: ffffffffffffffda RBX: 000000000100eca0 RCX: 00007f8467d0b082 [ 3673.989128] RDX: 00000000000003ff RSI: 00007ffffb21fdc0 RDI: 0000000000000003 [ 3673.989130] RBP: 00007f8467b96028 R08: 0000000000000010 R09: 00007ffffb21ec00 [ 3673.989132] R10: 00007ffffb27b170 R11: 0000000000000246 R12: 00000000000000f0 [ 3673.989134] R13: 0000000000000003 R14: 00007f8467b92000 R15: 0000000000045a05 [ 3673.989139] CPU: 30 PID: 285188 Comm: read_all Kdump: loaded Tainted: G W OE Fix this by collecting the statistics asynchronously from a periodic delayed work scheduled at default stats coalescing interval and return the recent copy of statisitcs from .ndo_get_stats64(), also add ability to configure/retrieve stats coalescing interval using below commands - ethtool -C ethx stats-block-usecs ethtool -c ethx Fixes: 133fac0eedc3 ("qede: Add basic ethtool support") Cc: Sudarsana Kalluru Cc: David Miller Signed-off-by: Manish Chopra Link: https://lore.kernel.org/r/20230605112600.48238-1-manishc@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/qlogic/qed/qed_l2.c | 2 +- drivers/net/ethernet/qlogic/qede/qede.h | 4 +++ drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 24 +++++++++++++++-- drivers/net/ethernet/qlogic/qede/qede_main.c | 34 ++++++++++++++++++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 2edd6bf64a3c..7776d3bdd459 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1903,7 +1903,7 @@ void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats) { u32 i; - if (!cdev) { + if (!cdev || cdev->recov_in_prog) { memset(stats, 0, sizeof(*stats)); return; } diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index f90dcfe9ee68..8a63f99d499c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -271,6 +271,10 @@ struct qede_dev { #define QEDE_ERR_WARN 3 struct qede_dump_info dump_info; + struct delayed_work periodic_task; + unsigned long stats_coal_ticks; + u32 stats_coal_usecs; + spinlock_t stats_lock; /* lock for vport stats access */ }; enum QEDE_STATE { diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 8034d812d5a0..d0a3395b2bc1 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -430,6 +430,8 @@ static void qede_get_ethtool_stats(struct net_device *dev, } } + spin_lock(&edev->stats_lock); + for (i = 0; i < QEDE_NUM_STATS; i++) { if (qede_is_irrelevant_stat(edev, i)) continue; @@ -439,6 +441,8 @@ static void qede_get_ethtool_stats(struct net_device *dev, buf++; } + spin_unlock(&edev->stats_lock); + __qede_unlock(edev); } @@ -830,6 +834,7 @@ out: coal->rx_coalesce_usecs = rx_coal; coal->tx_coalesce_usecs = tx_coal; + coal->stats_block_coalesce_usecs = edev->stats_coal_usecs; return rc; } @@ -843,6 +848,19 @@ int qede_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal, int i, rc = 0; u16 rxc, txc; + if (edev->stats_coal_usecs != coal->stats_block_coalesce_usecs) { + edev->stats_coal_usecs = coal->stats_block_coalesce_usecs; + if (edev->stats_coal_usecs) { + edev->stats_coal_ticks = usecs_to_jiffies(edev->stats_coal_usecs); + schedule_delayed_work(&edev->periodic_task, 0); + + DP_INFO(edev, "Configured stats coal ticks=%lu jiffies\n", + edev->stats_coal_ticks); + } else { + cancel_delayed_work_sync(&edev->periodic_task); + } + } + if (!netif_running(dev)) { DP_INFO(edev, "Interface is down\n"); return -EINVAL; @@ -2253,7 +2271,8 @@ out: } static const struct ethtool_ops qede_ethtool_ops = { - .supported_coalesce_params = ETHTOOL_COALESCE_USECS, + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_STATS_BLOCK_USECS, .get_link_ksettings = qede_get_link_ksettings, .set_link_ksettings = qede_set_link_ksettings, .get_drvinfo = qede_get_drvinfo, @@ -2304,7 +2323,8 @@ static const struct ethtool_ops qede_ethtool_ops = { }; static const struct ethtool_ops qede_vf_ethtool_ops = { - .supported_coalesce_params = ETHTOOL_COALESCE_USECS, + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_STATS_BLOCK_USECS, .get_link_ksettings = qede_get_link_ksettings, .get_drvinfo = qede_get_drvinfo, .get_msglevel = qede_get_msglevel, diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 89d64a5a4951..e8d427c7d1cf 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -308,6 +308,8 @@ void qede_fill_by_demand_stats(struct qede_dev *edev) edev->ops->get_vport_stats(edev->cdev, &stats); + spin_lock(&edev->stats_lock); + p_common->no_buff_discards = stats.common.no_buff_discards; p_common->packet_too_big_discard = stats.common.packet_too_big_discard; p_common->ttl0_discard = stats.common.ttl0_discard; @@ -405,6 +407,8 @@ void qede_fill_by_demand_stats(struct qede_dev *edev) p_ah->tx_1519_to_max_byte_packets = stats.ah.tx_1519_to_max_byte_packets; } + + spin_unlock(&edev->stats_lock); } static void qede_get_stats64(struct net_device *dev, @@ -413,9 +417,10 @@ static void qede_get_stats64(struct net_device *dev, struct qede_dev *edev = netdev_priv(dev); struct qede_stats_common *p_common; - qede_fill_by_demand_stats(edev); p_common = &edev->stats.common; + spin_lock(&edev->stats_lock); + stats->rx_packets = p_common->rx_ucast_pkts + p_common->rx_mcast_pkts + p_common->rx_bcast_pkts; stats->tx_packets = p_common->tx_ucast_pkts + p_common->tx_mcast_pkts + @@ -435,6 +440,8 @@ static void qede_get_stats64(struct net_device *dev, stats->collisions = edev->stats.bb.tx_total_collisions; stats->rx_crc_errors = p_common->rx_crc_errors; stats->rx_frame_errors = p_common->rx_align_errors; + + spin_unlock(&edev->stats_lock); } #ifdef CONFIG_QED_SRIOV @@ -1061,6 +1068,23 @@ static void qede_unlock(struct qede_dev *edev) rtnl_unlock(); } +static void qede_periodic_task(struct work_struct *work) +{ + struct qede_dev *edev = container_of(work, struct qede_dev, + periodic_task.work); + + qede_fill_by_demand_stats(edev); + schedule_delayed_work(&edev->periodic_task, edev->stats_coal_ticks); +} + +static void qede_init_periodic_task(struct qede_dev *edev) +{ + INIT_DELAYED_WORK(&edev->periodic_task, qede_periodic_task); + spin_lock_init(&edev->stats_lock); + edev->stats_coal_usecs = USEC_PER_SEC; + edev->stats_coal_ticks = usecs_to_jiffies(USEC_PER_SEC); +} + static void qede_sp_task(struct work_struct *work) { struct qede_dev *edev = container_of(work, struct qede_dev, @@ -1080,6 +1104,7 @@ static void qede_sp_task(struct work_struct *work) */ if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { + cancel_delayed_work_sync(&edev->periodic_task); #ifdef CONFIG_QED_SRIOV /* SRIOV must be disabled outside the lock to avoid a deadlock. * The recovery of the active VFs is currently not supported. @@ -1270,6 +1295,7 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, */ INIT_DELAYED_WORK(&edev->sp_task, qede_sp_task); mutex_init(&edev->qede_lock); + qede_init_periodic_task(edev); rc = register_netdev(edev->ndev); if (rc) { @@ -1294,6 +1320,11 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, edev->rx_copybreak = QEDE_RX_HDR_SIZE; qede_log_probe(edev); + + /* retain user config (for example - after recovery) */ + if (edev->stats_coal_usecs) + schedule_delayed_work(&edev->periodic_task, 0); + return 0; err4: @@ -1362,6 +1393,7 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) unregister_netdev(ndev); cancel_delayed_work_sync(&edev->sp_task); + cancel_delayed_work_sync(&edev->periodic_task); edev->ops->common->set_power_state(cdev, PCI_D0); -- cgit 1.4.1 From 0d18f8b90b8a914cb4d4ecebde177d7dadc28552 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Jun 2023 14:34:47 +0200 Subject: wifi: cfg80211: fix locking in sched scan stop work [ Upstream commit 3e54ed8247c94c8bdf370bd872bd9dfe72b1b12b ] This should use wiphy_lock() now instead of acquiring the RTNL, since cfg80211_stop_sched_scan_req() now needs that. Fixes: a05829a7222e ("cfg80211: avoid holding the RTNL when calling the driver") Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 5b0c4d5b80cf..b3ec9eaec36b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -368,12 +368,12 @@ static void cfg80211_sched_scan_stop_wk(struct work_struct *work) rdev = container_of(work, struct cfg80211_registered_device, sched_scan_stop_wk); - rtnl_lock(); + wiphy_lock(&rdev->wiphy); list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) { if (req->nl_owner_dead) cfg80211_stop_sched_scan_req(rdev, req, false); } - rtnl_unlock(); + wiphy_unlock(&rdev->wiphy); } static void cfg80211_propagate_radar_detect_wk(struct work_struct *work) -- cgit 1.4.1 From 1ba03535451f9b64f2fe8901ee80e593736bea92 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 18 Apr 2023 15:53:39 -0700 Subject: selftests/bpf: Verify optval=NULL case [ Upstream commit 833d67ecdc5f35f1ebf59d0fccc1ce771434be9c ] Make sure we get optlen exported instead of getting EFAULT. Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20230418225343.553806-3-sdf@google.com Stable-dep-of: 69844e335d8c ("selftests/bpf: Fix sockopt_sk selftest") Signed-off-by: Sasha Levin --- .../testing/selftests/bpf/prog_tests/sockopt_sk.c | 28 ++++++++++++++++++++++ tools/testing/selftests/bpf/progs/sockopt_sk.c | 12 ++++++++++ 2 files changed, 40 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c index 60d952719d27..4512dd808c33 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c @@ -3,6 +3,7 @@ #include "cgroup_helpers.h" #include +#include #include "sockopt_sk.skel.h" #ifndef SOL_TCP @@ -183,6 +184,33 @@ static int getsetsockopt(void) goto err; } + /* optval=NULL case is handled correctly */ + + close(fd); + fd = socket(AF_NETLINK, SOCK_RAW, 0); + if (fd < 0) { + log_err("Failed to create AF_NETLINK socket"); + return -1; + } + + buf.u32 = 1; + optlen = sizeof(__u32); + err = setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &buf, optlen); + if (err) { + log_err("Unexpected getsockopt(NETLINK_ADD_MEMBERSHIP) err=%d errno=%d", + err, errno); + goto err; + } + + optlen = 0; + err = getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &optlen); + if (err) { + log_err("Unexpected getsockopt(NETLINK_LIST_MEMBERSHIPS) err=%d errno=%d", + err, errno); + goto err; + } + ASSERT_EQ(optlen, 4, "Unexpected NETLINK_LIST_MEMBERSHIPS value"); + free(big_buf); close(fd); return 0; diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c index c8d810010a94..fe1df4cd206e 100644 --- a/tools/testing/selftests/bpf/progs/sockopt_sk.c +++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c @@ -32,6 +32,12 @@ int _getsockopt(struct bpf_sockopt *ctx) __u8 *optval_end = ctx->optval_end; __u8 *optval = ctx->optval; struct sockopt_sk *storage; + struct bpf_sock *sk; + + /* Bypass AF_NETLINK. */ + sk = ctx->sk; + if (sk && sk->family == AF_NETLINK) + return 1; /* Make sure bpf_get_netns_cookie is callable. */ @@ -131,6 +137,12 @@ int _setsockopt(struct bpf_sockopt *ctx) __u8 *optval_end = ctx->optval_end; __u8 *optval = ctx->optval; struct sockopt_sk *storage; + struct bpf_sock *sk; + + /* Bypass AF_NETLINK. */ + sk = ctx->sk; + if (sk && sk->family == AF_NETLINK) + return 1; /* Make sure bpf_get_netns_cookie is callable. */ -- cgit 1.4.1 From 81e11b6c1ad061b780dd16aef1f979463a7b154c Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 6 Jun 2023 10:22:02 -0700 Subject: selftests/bpf: Fix sockopt_sk selftest [ Upstream commit 69844e335d8c22454746c7903776533d8b4ab8fa ] Commit f4e4534850a9 ("net/netlink: fix NETLINK_LIST_MEMBERSHIPS length report") fixed NETLINK_LIST_MEMBERSHIPS length report which caused selftest sockopt_sk failure. The failure log looks like test_sockopt_sk:PASS:join_cgroup /sockopt_sk 0 nsec run_test:PASS:skel_load 0 nsec run_test:PASS:setsockopt_link 0 nsec run_test:PASS:getsockopt_link 0 nsec getsetsockopt:FAIL:Unexpected NETLINK_LIST_MEMBERSHIPS value unexpected Unexpected NETLINK_LIST_MEMBERSHIPS value: actual 8 != expected 4 run_test:PASS:getsetsockopt 0 nsec #201 sockopt_sk:FAIL In net/netlink/af_netlink.c, function netlink_getsockopt(), for NETLINK_LIST_MEMBERSHIPS, nlk->ngroups equals to 36. Before Commit f4e4534850a9, the optlen is calculated as ALIGN(nlk->ngroups / 8, sizeof(u32)) = 4 After that commit, the optlen is ALIGN(BITS_TO_BYTES(nlk->ngroups), sizeof(u32)) = 8 Fix the test by setting the expected optlen to be 8. Fixes: f4e4534850a9 ("net/netlink: fix NETLINK_LIST_MEMBERSHIPS length report") Signed-off-by: Yonghong Song Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20230606172202.1606249-1-yhs@fb.com Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/sockopt_sk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c index 4512dd808c33..05d0e07da394 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c @@ -209,7 +209,7 @@ static int getsetsockopt(void) err, errno); goto err; } - ASSERT_EQ(optlen, 4, "Unexpected NETLINK_LIST_MEMBERSHIPS value"); + ASSERT_EQ(optlen, 8, "Unexpected NETLINK_LIST_MEMBERSHIPS value"); free(big_buf); close(fd); -- cgit 1.4.1 From 1f26ea49a5c0a80659216896b1218efa591402c9 Mon Sep 17 00:00:00 2001 From: Jeremy Sowden Date: Thu, 25 May 2023 15:07:24 +0100 Subject: netfilter: nft_bitwise: fix register tracking [ Upstream commit 14e8b293903785590a0ef168745ac84250cb1f4c ] At the end of `nft_bitwise_reduce`, there is a loop which is intended to update the bitwise expression associated with each tracked destination register. However, currently, it just updates the first register repeatedly. Fix it. Fixes: 34cc9e52884a ("netfilter: nf_tables: cancel tracking for clobbered destination registers") Signed-off-by: Jeremy Sowden Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_bitwise.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index e6e402b247d0..b84312df9aa1 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -322,7 +322,7 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track, dreg = priv->dreg; regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE); for (i = 0; i < regcount; i++, dreg++) - track->regs[priv->dreg].bitwise = expr; + track->regs[dreg].bitwise = expr; return false; } -- cgit 1.4.1 From f057da51c0761e5e3593a0c74b1bfc8b5a2fdfbd Mon Sep 17 00:00:00 2001 From: Tijs Van Buggenhout Date: Thu, 25 May 2023 12:25:26 +0200 Subject: netfilter: conntrack: fix NULL pointer dereference in nf_confirm_cthelper [ Upstream commit e1f543dc660b44618a1bd72ddb4ca0828a95f7ad ] An nf_conntrack_helper from nf_conn_help may become NULL after DNAT. Observed when TCP port 1720 (Q931_PORT), associated with h323 conntrack helper, is DNAT'ed to another destination port (e.g. 1730), while nfqueue is being used for final acceptance (e.g. snort). This happenned after transition from kernel 4.14 to 5.10.161. Workarounds: * keep the same port (1720) in DNAT * disable nfqueue * disable/unload h323 NAT helper $ linux-5.10/scripts/decode_stacktrace.sh vmlinux < /tmp/kernel.log BUG: kernel NULL pointer dereference, address: 0000000000000084 [..] RIP: 0010:nf_conntrack_update (net/netfilter/nf_conntrack_core.c:2080 net/netfilter/nf_conntrack_core.c:2134) nf_conntrack [..] nfqnl_reinject (net/netfilter/nfnetlink_queue.c:237) nfnetlink_queue nfqnl_recv_verdict (net/netfilter/nfnetlink_queue.c:1230) nfnetlink_queue nfnetlink_rcv_msg (net/netfilter/nfnetlink.c:241) nfnetlink [..] Fixes: ee04805ff54a ("netfilter: conntrack: make conntrack userspace helpers work again") Signed-off-by: Tijs Van Buggenhout Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index a0e9c7af0846..796026296609 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -2277,6 +2277,9 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, return 0; helper = rcu_dereference(help->helper); + if (!helper) + return 0; + if (!(helper->flags & NF_CT_HELPER_F_USERSPACE)) return 0; -- cgit 1.4.1 From fea199dbf6c116162deaa979184282a94689923d Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 18 May 2023 10:33:00 -0700 Subject: netfilter: ipset: Add schedule point in call_ad(). [ Upstream commit 24e227896bbf003165e006732dccb3516f87f88e ] syzkaller found a repro that causes Hung Task [0] with ipset. The repro first creates an ipset and then tries to delete a large number of IPs from the ipset concurrently: IPSET_ATTR_IPADDR_IPV4 : 172.20.20.187 IPSET_ATTR_CIDR : 2 The first deleting thread hogs a CPU with nfnl_lock(NFNL_SUBSYS_IPSET) held, and other threads wait for it to be released. Previously, the same issue existed in set->variant->uadt() that could run so long under ip_set_lock(set). Commit 5e29dc36bd5e ("netfilter: ipset: Rework long task execution when adding/deleting entries") tried to fix it, but the issue still exists in the caller with another mutex. While adding/deleting many IPs, we should release the CPU periodically to prevent someone from abusing ipset to hang the system. Note we need to increment the ipset's refcnt to prevent the ipset from being destroyed while rescheduling. [0]: INFO: task syz-executor174:268 blocked for more than 143 seconds. Not tainted 6.4.0-rc1-00145-gba79e9a73284 #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:syz-executor174 state:D stack:0 pid:268 ppid:260 flags:0x0000000d Call trace: __switch_to+0x308/0x714 arch/arm64/kernel/process.c:556 context_switch kernel/sched/core.c:5343 [inline] __schedule+0xd84/0x1648 kernel/sched/core.c:6669 schedule+0xf0/0x214 kernel/sched/core.c:6745 schedule_preempt_disabled+0x58/0xf0 kernel/sched/core.c:6804 __mutex_lock_common kernel/locking/mutex.c:679 [inline] __mutex_lock+0x6fc/0xdb0 kernel/locking/mutex.c:747 __mutex_lock_slowpath+0x14/0x20 kernel/locking/mutex.c:1035 mutex_lock+0x98/0xf0 kernel/locking/mutex.c:286 nfnl_lock net/netfilter/nfnetlink.c:98 [inline] nfnetlink_rcv_msg+0x480/0x70c net/netfilter/nfnetlink.c:295 netlink_rcv_skb+0x1c0/0x350 net/netlink/af_netlink.c:2546 nfnetlink_rcv+0x18c/0x199c net/netfilter/nfnetlink.c:658 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x664/0x8cc net/netlink/af_netlink.c:1365 netlink_sendmsg+0x6d0/0xa4c net/netlink/af_netlink.c:1913 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] ____sys_sendmsg+0x4b8/0x810 net/socket.c:2503 ___sys_sendmsg net/socket.c:2557 [inline] __sys_sendmsg+0x1f8/0x2a4 net/socket.c:2586 __do_sys_sendmsg net/socket.c:2595 [inline] __se_sys_sendmsg net/socket.c:2593 [inline] __arm64_sys_sendmsg+0x80/0x94 net/socket.c:2593 __invoke_syscall arch/arm64/kernel/syscall.c:38 [inline] invoke_syscall+0x84/0x270 arch/arm64/kernel/syscall.c:52 el0_svc_common+0x134/0x24c arch/arm64/kernel/syscall.c:142 do_el0_svc+0x64/0x198 arch/arm64/kernel/syscall.c:193 el0_svc+0x2c/0x7c arch/arm64/kernel/entry-common.c:637 el0t_64_sync_handler+0x84/0xf0 arch/arm64/kernel/entry-common.c:655 el0t_64_sync+0x190/0x194 arch/arm64/kernel/entry.S:591 Reported-by: syzkaller Fixes: a7b4f989a629 ("netfilter: ipset: IP set core support") Signed-off-by: Kuniyuki Iwashima Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 46ebee9400da..9a6b64779e64 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1694,6 +1694,14 @@ call_ad(struct net *net, struct sock *ctnl, struct sk_buff *skb, bool eexist = flags & IPSET_FLAG_EXIST, retried = false; do { + if (retried) { + __ip_set_get(set); + nfnl_unlock(NFNL_SUBSYS_IPSET); + cond_resched(); + nfnl_lock(NFNL_SUBSYS_IPSET); + __ip_set_put(set); + } + ip_set_lock(set); ret = set->variant->uadt(set, tb, adt, &lineno, flags, retried); ip_set_unlock(set); -- cgit 1.4.1 From 65f2def2066255eb9ee6cbfe2961ed09e913ea9b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 6 Jun 2023 16:32:44 +0200 Subject: netfilter: nf_tables: out-of-bound check in chain blob [ Upstream commit 08e42a0d3ad30f276f9597b591f975971a1b0fcf ] Add current size of rule expressions to the boundary check. Fixes: 2c865a8a28a1 ("netfilter: nf_tables: add rule blob layout") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 31775d54f4b4..437891cb8c41 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -8723,7 +8723,7 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha continue; } - if (WARN_ON_ONCE(data + expr->ops->size > data_boundary)) + if (WARN_ON_ONCE(data + size + expr->ops->size > data_boundary)) return -ENOMEM; memcpy(data + size, expr, expr->ops->size); -- cgit 1.4.1 From 251b5d68ac4748f210577d84f1cd5a664c54785f Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 5 Jun 2023 11:06:17 -0700 Subject: ipv6: rpl: Fix Route of Death. [ Upstream commit a2f4c143d76b1a47c91ef9bc46907116b111da0b ] A remote DoS vulnerability of RPL Source Routing is assigned CVE-2023-2156. The Source Routing Header (SRH) has the following format: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next Header | Hdr Ext Len | Routing Type | Segments Left | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | CmprI | CmprE | Pad | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | . . . Addresses[1..n] . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The originator of an SRH places the first hop's IPv6 address in the IPv6 header's IPv6 Destination Address and the second hop's IPv6 address as the first address in Addresses[1..n]. The CmprI and CmprE fields indicate the number of prefix octets that are shared with the IPv6 Destination Address. When CmprI or CmprE is not 0, Addresses[1..n] are compressed as follows: 1..n-1 : (16 - CmprI) bytes n : (16 - CmprE) bytes Segments Left indicates the number of route segments remaining. When the value is not zero, the SRH is forwarded to the next hop. Its address is extracted from Addresses[n - Segment Left + 1] and swapped with IPv6 Destination Address. When Segment Left is greater than or equal to 2, the size of SRH is not changed because Addresses[1..n-1] are decompressed and recompressed with CmprI. OTOH, when Segment Left changes from 1 to 0, the new SRH could have a different size because Addresses[1..n-1] are decompressed with CmprI and recompressed with CmprE. Let's say CmprI is 15 and CmprE is 0. When we receive SRH with Segment Left >= 2, Addresses[1..n-1] have 1 byte for each, and Addresses[n] has 16 bytes. When Segment Left is 1, Addresses[1..n-1] is decompressed to 16 bytes and not recompressed. Finally, the new SRH will need more room in the header, and the size is (16 - 1) * (n - 1) bytes. Here the max value of n is 255 as Segment Left is u8, so in the worst case, we have to allocate 3825 bytes in the skb headroom. However, now we only allocate a small fixed buffer that is IPV6_RPL_SRH_WORST_SWAP_SIZE (16 + 7 bytes). If the decompressed size overflows the room, skb_push() hits BUG() below [0]. Instead of allocating the fixed buffer for every packet, let's allocate enough headroom only when we receive SRH with Segment Left 1. [0]: skbuff: skb_under_panic: text:ffffffff81c9f6e2 len:576 put:576 head:ffff8880070b5180 data:ffff8880070b4fb0 tail:0x70 end:0x140 dev:lo kernel BUG at net/core/skbuff.c:200! invalid opcode: 0000 [#1] PREEMPT SMP PTI CPU: 0 PID: 154 Comm: python3 Not tainted 6.4.0-rc4-00190-gc308e9ec0047 #7 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 RIP: 0010:skb_panic (net/core/skbuff.c:200) Code: 4f 70 50 8b 87 bc 00 00 00 50 8b 87 b8 00 00 00 50 ff b7 c8 00 00 00 4c 8b 8f c0 00 00 00 48 c7 c7 80 6e 77 82 e8 ad 8b 60 ff <0f> 0b 66 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 RSP: 0018:ffffc90000003da0 EFLAGS: 00000246 RAX: 0000000000000085 RBX: ffff8880058a6600 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffff88807dc1c540 RDI: ffff88807dc1c540 RBP: ffffc90000003e48 R08: ffffffff82b392c8 R09: 00000000ffffdfff R10: ffffffff82a592e0 R11: ffffffff82b092e0 R12: ffff888005b1c800 R13: ffff8880070b51b8 R14: ffff888005b1ca18 R15: ffff8880070b5190 FS: 00007f4539f0b740(0000) GS:ffff88807dc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000055670baf3000 CR3: 0000000005b0e000 CR4: 00000000007506f0 PKRU: 55555554 Call Trace: skb_push (net/core/skbuff.c:210) ipv6_rthdr_rcv (./include/linux/skbuff.h:2880 net/ipv6/exthdrs.c:634 net/ipv6/exthdrs.c:718) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:437 (discriminator 5)) ip6_input_finish (./include/linux/rcupdate.h:805 net/ipv6/ip6_input.c:483) __netif_receive_skb_one_core (net/core/dev.c:5494) process_backlog (./include/linux/rcupdate.h:805 net/core/dev.c:5934) __napi_poll (net/core/dev.c:6496) net_rx_action (net/core/dev.c:6565 net/core/dev.c:6696) __do_softirq (./arch/x86/include/asm/jump_label.h:27 ./include/linux/jump_label.h:207 ./include/trace/events/irq.h:142 kernel/softirq.c:572) do_softirq (kernel/softirq.c:472 kernel/softirq.c:459) __local_bh_enable_ip (kernel/softirq.c:396) __dev_queue_xmit (net/core/dev.c:4272) ip6_finish_output2 (./include/net/neighbour.h:544 net/ipv6/ip6_output.c:134) rawv6_sendmsg (./include/net/dst.h:458 ./include/linux/netfilter.h:303 net/ipv6/raw.c:656 net/ipv6/raw.c:914) sock_sendmsg (net/socket.c:724 net/socket.c:747) __sys_sendto (net/socket.c:2144) __x64_sys_sendto (net/socket.c:2156 net/socket.c:2152 net/socket.c:2152) do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:120) RIP: 0033:0x7f453a138aea Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 15 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 7e c3 0f 1f 44 00 00 41 54 48 83 ec 30 44 89 RSP: 002b:00007ffcc212a1c8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 00007ffcc212a288 RCX: 00007f453a138aea RDX: 0000000000000060 RSI: 00007f4539084c20 RDI: 0000000000000003 RBP: 00007f4538308e80 R08: 00007ffcc212a300 R09: 000000000000001c R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: ffffffffc4653600 R14: 0000000000000001 R15: 00007f4539712d1b Modules linked in: Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr") Reported-by: Max VA Closes: https://www.interruptlabs.co.uk/articles/linux-ipv6-route-of-death Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20230605180617.67284-1-kuniyu@amazon.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/rpl.h | 3 --- net/ipv6/exthdrs.c | 29 +++++++++++------------------ 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/include/net/rpl.h b/include/net/rpl.h index 308ef0a05cae..30fe780d1e7c 100644 --- a/include/net/rpl.h +++ b/include/net/rpl.h @@ -23,9 +23,6 @@ static inline int rpl_init(void) static inline void rpl_exit(void) {} #endif -/* Worst decompression memory usage ipv6 address (16) + pad 7 */ -#define IPV6_RPL_SRH_WORST_SWAP_SIZE (sizeof(struct in6_addr) + 7) - size_t ipv6_rpl_srh_size(unsigned char n, unsigned char cmpri, unsigned char cmpre); diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index a8d961d3a477..5fa0e37305d9 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -569,24 +569,6 @@ looped_back: return -1; } - if (skb_cloned(skb)) { - if (pskb_expand_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE, 0, - GFP_ATOMIC)) { - __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_OUTDISCARDS); - kfree_skb(skb); - return -1; - } - } else { - err = skb_cow_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE); - if (unlikely(err)) { - kfree_skb(skb); - return -1; - } - } - - hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); - if (!pskb_may_pull(skb, ipv6_rpl_srh_size(n, hdr->cmpri, hdr->cmpre))) { kfree_skb(skb); @@ -630,6 +612,17 @@ looped_back: skb_pull(skb, ((hdr->hdrlen + 1) << 3)); skb_postpull_rcsum(skb, oldhdr, sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); + if (unlikely(!hdr->segments_left)) { + if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0, + GFP_ATOMIC)) { + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); + kfree(buf); + return -1; + } + + oldhdr = ipv6_hdr(skb); + } skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); skb_reset_network_header(skb); skb_mac_header_rebuild(skb); -- cgit 1.4.1 From f8e6aa0e60568ff3ea422180e6286e504bbc2db9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 5 Jun 2023 16:16:47 +0000 Subject: tcp: gso: really support BIG TCP [ Upstream commit 82a01ab35bd02ba4b0b4e12bc95c5b69240eb7b0 ] We missed that tcp_gso_segment() was assuming skb->len was smaller than 65535 : oldlen = (u16)~skb->len; This part came with commit 0718bcc09b35 ("[NET]: Fix CHECKSUM_HW GSO problems.") This leads to wrong TCP checksum. Adapt the code to accept arbitrary packet length. v2: - use two csum_add() instead of csum_fold() (Alexander Duyck) - Change delta type to __wsum to reduce casts (Alexander Duyck) Fixes: 09f3d1a3a52c ("ipv6/gso: remove temporary HBH/jumbo header") Signed-off-by: Eric Dumazet Reviewed-by: Alexander Duyck Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230605161647.3624428-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/tcp_offload.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index 45dda7889387..4851211aa60d 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c @@ -60,12 +60,12 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, struct tcphdr *th; unsigned int thlen; unsigned int seq; - __be32 delta; unsigned int oldlen; unsigned int mss; struct sk_buff *gso_skb = skb; __sum16 newcheck; bool ooo_okay, copy_destructor; + __wsum delta; th = tcp_hdr(skb); thlen = th->doff * 4; @@ -75,7 +75,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, if (!pskb_may_pull(skb, thlen)) goto out; - oldlen = (u16)~skb->len; + oldlen = ~skb->len; __skb_pull(skb, thlen); mss = skb_shinfo(skb)->gso_size; @@ -110,7 +110,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, if (skb_is_gso(segs)) mss *= skb_shinfo(segs)->gso_segs; - delta = htonl(oldlen + (thlen + mss)); + delta = (__force __wsum)htonl(oldlen + thlen + mss); skb = segs; th = tcp_hdr(skb); @@ -119,8 +119,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, if (unlikely(skb_shinfo(gso_skb)->tx_flags & SKBTX_SW_TSTAMP)) tcp_gso_tstamp(segs, skb_shinfo(gso_skb)->tskey, seq, mss); - newcheck = ~csum_fold((__force __wsum)((__force u32)th->check + - (__force u32)delta)); + newcheck = ~csum_fold(csum_add(csum_unfold(th->check), delta)); while (skb->next) { th->fin = th->psh = 0; @@ -165,11 +164,11 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, WARN_ON_ONCE(refcount_sub_and_test(-delta, &skb->sk->sk_wmem_alloc)); } - delta = htonl(oldlen + (skb_tail_pointer(skb) - - skb_transport_header(skb)) + - skb->data_len); - th->check = ~csum_fold((__force __wsum)((__force u32)th->check + - (__force u32)delta)); + delta = (__force __wsum)htonl(oldlen + + (skb_tail_pointer(skb) - + skb_transport_header(skb)) + + skb->data_len); + th->check = ~csum_fold(csum_add(csum_unfold(th->check), delta)); if (skb->ip_summed == CHECKSUM_PARTIAL) gso_reset_checksum(skb, ~th->check); else -- cgit 1.4.1 From 3d9eface2eee50ec683d4b9523cd913f1d5ac4aa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Jun 2023 07:41:14 +0000 Subject: rfs: annotate lockless accesses to sk->sk_rxhash [ Upstream commit 1e5c647c3f6d4f8497dedcd226204e1880e0ffb3 ] Add READ_ONCE()/WRITE_ONCE() on accesses to sk->sk_rxhash. This also prevents a (smart ?) compiler to remove the condition in: if (sk->sk_rxhash != newval) sk->sk_rxhash = newval; We need the condition to avoid dirtying a shared cache line. Fixes: fec5e652e58f ("rfs: Receive Flow Steering") Signed-off-by: Eric Dumazet Reviewed-by: Simon Horman Reviewed-by: Kuniyuki Iwashima Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/net/sock.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index f11b98bd0244..2f35b82a123f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1148,8 +1148,12 @@ static inline void sock_rps_record_flow(const struct sock *sk) * OR an additional socket flag * [1] : sk_state and sk_prot are in the same cache line. */ - if (sk->sk_state == TCP_ESTABLISHED) - sock_rps_record_flow_hash(sk->sk_rxhash); + if (sk->sk_state == TCP_ESTABLISHED) { + /* This READ_ONCE() is paired with the WRITE_ONCE() + * from sock_rps_save_rxhash() and sock_rps_reset_rxhash(). + */ + sock_rps_record_flow_hash(READ_ONCE(sk->sk_rxhash)); + } } #endif } @@ -1158,15 +1162,19 @@ static inline void sock_rps_save_rxhash(struct sock *sk, const struct sk_buff *skb) { #ifdef CONFIG_RPS - if (unlikely(sk->sk_rxhash != skb->hash)) - sk->sk_rxhash = skb->hash; + /* The following WRITE_ONCE() is paired with the READ_ONCE() + * here, and another one in sock_rps_record_flow(). + */ + if (unlikely(READ_ONCE(sk->sk_rxhash) != skb->hash)) + WRITE_ONCE(sk->sk_rxhash, skb->hash); #endif } static inline void sock_rps_reset_rxhash(struct sock *sk) { #ifdef CONFIG_RPS - sk->sk_rxhash = 0; + /* Paired with READ_ONCE() in sock_rps_record_flow() */ + WRITE_ONCE(sk->sk_rxhash, 0); #endif } -- cgit 1.4.1 From 8a74ea37e1ab8162018a364c8cb419d945609e47 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Jun 2023 07:41:15 +0000 Subject: rfs: annotate lockless accesses to RFS sock flow table [ Upstream commit 5c3b74a92aa285a3df722bf6329ba7ccf70346d6 ] Add READ_ONCE()/WRITE_ONCE() on accesses to the sock flow table. This also prevents a (smart ?) compiler to remove the condition in: if (table->ents[index] != newval) table->ents[index] = newval; We need the condition to avoid dirtying a shared cache line. Fixes: fec5e652e58f ("rfs: Receive Flow Steering") Signed-off-by: Eric Dumazet Reviewed-by: Simon Horman Reviewed-by: Kuniyuki Iwashima Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/linux/netdevice.h | 7 +++++-- net/core/dev.c | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index eac51e22a52a..74e05b82f1bf 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -757,8 +757,11 @@ static inline void rps_record_sock_flow(struct rps_sock_flow_table *table, /* We only give a hint, preemption can change CPU under us */ val |= raw_smp_processor_id(); - if (table->ents[index] != val) - table->ents[index] = val; + /* The following WRITE_ONCE() is paired with the READ_ONCE() + * here, and another one in get_rps_cpu(). + */ + if (READ_ONCE(table->ents[index]) != val) + WRITE_ONCE(table->ents[index], val); } } diff --git a/net/core/dev.c b/net/core/dev.c index 93d430693ca0..ee00d3cfcb56 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4483,8 +4483,10 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, u32 next_cpu; u32 ident; - /* First check into global flow table if there is a match */ - ident = sock_flow_table->ents[hash & sock_flow_table->mask]; + /* First check into global flow table if there is a match. + * This READ_ONCE() pairs with WRITE_ONCE() from rps_record_sock_flow(). + */ + ident = READ_ONCE(sock_flow_table->ents[hash & sock_flow_table->mask]); if ((ident ^ hash) & ~rps_cpu_mask) goto try_rps; -- cgit 1.4.1 From 9d9a38b5639fcefacc1e977567fb4b4e4a74d0b3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Jun 2023 11:19:29 +0000 Subject: net: sched: add rcu annotations around qdisc->qdisc_sleeping [ Upstream commit d636fc5dd692c8f4e00ae6e0359c0eceeb5d9bdb ] syzbot reported a race around qdisc->qdisc_sleeping [1] It is time we add proper annotations to reads and writes to/from qdisc->qdisc_sleeping. [1] BUG: KCSAN: data-race in dev_graft_qdisc / qdisc_lookup_rcu read to 0xffff8881286fc618 of 8 bytes by task 6928 on cpu 1: qdisc_lookup_rcu+0x192/0x2c0 net/sched/sch_api.c:331 __tcf_qdisc_find+0x74/0x3c0 net/sched/cls_api.c:1174 tc_get_tfilter+0x18f/0x990 net/sched/cls_api.c:2547 rtnetlink_rcv_msg+0x7af/0x8c0 net/core/rtnetlink.c:6386 netlink_rcv_skb+0x126/0x220 net/netlink/af_netlink.c:2546 rtnetlink_rcv+0x1c/0x20 net/core/rtnetlink.c:6413 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x56f/0x640 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x665/0x770 net/netlink/af_netlink.c:1913 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] ____sys_sendmsg+0x375/0x4c0 net/socket.c:2503 ___sys_sendmsg net/socket.c:2557 [inline] __sys_sendmsg+0x1e3/0x270 net/socket.c:2586 __do_sys_sendmsg net/socket.c:2595 [inline] __se_sys_sendmsg net/socket.c:2593 [inline] __x64_sys_sendmsg+0x46/0x50 net/socket.c:2593 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd write to 0xffff8881286fc618 of 8 bytes by task 6912 on cpu 0: dev_graft_qdisc+0x4f/0x80 net/sched/sch_generic.c:1115 qdisc_graft+0x7d0/0xb60 net/sched/sch_api.c:1103 tc_modify_qdisc+0x712/0xf10 net/sched/sch_api.c:1693 rtnetlink_rcv_msg+0x807/0x8c0 net/core/rtnetlink.c:6395 netlink_rcv_skb+0x126/0x220 net/netlink/af_netlink.c:2546 rtnetlink_rcv+0x1c/0x20 net/core/rtnetlink.c:6413 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x56f/0x640 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x665/0x770 net/netlink/af_netlink.c:1913 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] ____sys_sendmsg+0x375/0x4c0 net/socket.c:2503 ___sys_sendmsg net/socket.c:2557 [inline] __sys_sendmsg+0x1e3/0x270 net/socket.c:2586 __do_sys_sendmsg net/socket.c:2595 [inline] __se_sys_sendmsg net/socket.c:2593 [inline] __x64_sys_sendmsg+0x46/0x50 net/socket.c:2593 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd Reported by Kernel Concurrency Sanitizer on: CPU: 0 PID: 6912 Comm: syz-executor.5 Not tainted 6.4.0-rc3-syzkaller-00190-g0d85b27b0cc6 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/16/2023 Fixes: 3a7d0d07a386 ("net: sched: extend Qdisc with rcu") Reported-by: syzbot Signed-off-by: Eric Dumazet Cc: Vlad Buslov Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/linux/netdevice.h | 2 +- include/net/sch_generic.h | 6 ++++-- net/core/dev.c | 2 +- net/sched/sch_api.c | 26 ++++++++++++++++---------- net/sched/sch_fq_pie.c | 2 ++ net/sched/sch_generic.c | 30 +++++++++++++++--------------- net/sched/sch_mq.c | 8 ++++---- net/sched/sch_mqprio.c | 8 ++++---- net/sched/sch_pie.c | 5 ++++- net/sched/sch_red.c | 5 ++++- net/sched/sch_sfq.c | 5 ++++- net/sched/sch_taprio.c | 6 +++--- net/sched/sch_teql.c | 2 +- 13 files changed, 63 insertions(+), 44 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 74e05b82f1bf..d5eb3ab8e38f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -609,7 +609,7 @@ struct netdev_queue { netdevice_tracker dev_tracker; struct Qdisc __rcu *qdisc; - struct Qdisc *qdisc_sleeping; + struct Qdisc __rcu *qdisc_sleeping; #ifdef CONFIG_SYSFS struct kobject kobj; #endif diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index af4aa66aaa4e..989eb972fcae 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -543,7 +543,7 @@ static inline struct Qdisc *qdisc_root_bh(const struct Qdisc *qdisc) static inline struct Qdisc *qdisc_root_sleeping(const struct Qdisc *qdisc) { - return qdisc->dev_queue->qdisc_sleeping; + return rcu_dereference_rtnl(qdisc->dev_queue->qdisc_sleeping); } static inline spinlock_t *qdisc_root_sleeping_lock(const struct Qdisc *qdisc) @@ -752,7 +752,9 @@ static inline bool qdisc_tx_changing(const struct net_device *dev) for (i = 0; i < dev->num_tx_queues; i++) { struct netdev_queue *txq = netdev_get_tx_queue(dev, i); - if (rcu_access_pointer(txq->qdisc) != txq->qdisc_sleeping) + + if (rcu_access_pointer(txq->qdisc) != + rcu_access_pointer(txq->qdisc_sleeping)) return true; } return false; diff --git a/net/core/dev.c b/net/core/dev.c index ee00d3cfcb56..a2e3c6470ab3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10518,7 +10518,7 @@ struct netdev_queue *dev_ingress_queue_create(struct net_device *dev) return NULL; netdev_init_one_queue(dev, queue, NULL); RCU_INIT_POINTER(queue->qdisc, &noop_qdisc); - queue->qdisc_sleeping = &noop_qdisc; + RCU_INIT_POINTER(queue->qdisc_sleeping, &noop_qdisc); rcu_assign_pointer(dev->ingress_queue, queue); #endif return queue; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index f6a7b876d595..6fb345ec2264 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -308,7 +308,7 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) if (dev_ingress_queue(dev)) q = qdisc_match_from_root( - dev_ingress_queue(dev)->qdisc_sleeping, + rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping), handle); out: return q; @@ -327,7 +327,8 @@ struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle) nq = dev_ingress_queue_rcu(dev); if (nq) - q = qdisc_match_from_root(nq->qdisc_sleeping, handle); + q = qdisc_match_from_root(rcu_dereference(nq->qdisc_sleeping), + handle); out: return q; } @@ -633,8 +634,13 @@ EXPORT_SYMBOL(qdisc_watchdog_init); void qdisc_watchdog_schedule_range_ns(struct qdisc_watchdog *wd, u64 expires, u64 delta_ns) { - if (test_bit(__QDISC_STATE_DEACTIVATED, - &qdisc_root_sleeping(wd->qdisc)->state)) + bool deactivated; + + rcu_read_lock(); + deactivated = test_bit(__QDISC_STATE_DEACTIVATED, + &qdisc_root_sleeping(wd->qdisc)->state); + rcu_read_unlock(); + if (deactivated) return; if (hrtimer_is_queued(&wd->timer)) { @@ -1473,7 +1479,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, } q = qdisc_leaf(p, clid); } else if (dev_ingress_queue(dev)) { - q = dev_ingress_queue(dev)->qdisc_sleeping; + q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping); } } else { q = rtnl_dereference(dev->qdisc); @@ -1559,7 +1565,7 @@ replay: } q = qdisc_leaf(p, clid); } else if (dev_ingress_queue_create(dev)) { - q = dev_ingress_queue(dev)->qdisc_sleeping; + q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping); } } else { q = rtnl_dereference(dev->qdisc); @@ -1800,8 +1806,8 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) dev_queue = dev_ingress_queue(dev); if (dev_queue && - tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb, - &q_idx, s_q_idx, false, + tc_dump_qdisc_root(rtnl_dereference(dev_queue->qdisc_sleeping), + skb, cb, &q_idx, s_q_idx, false, tca[TCA_DUMP_INVISIBLE]) < 0) goto done; @@ -2239,8 +2245,8 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) dev_queue = dev_ingress_queue(dev); if (dev_queue && - tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb, - &t, s_t, false) < 0) + tc_dump_tclass_root(rtnl_dereference(dev_queue->qdisc_sleeping), + skb, tcm, cb, &t, s_t, false) < 0) goto done; done: diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index c699e5095607..591d87d5e5c0 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -379,6 +379,7 @@ static void fq_pie_timer(struct timer_list *t) spinlock_t *root_lock; /* to lock qdisc for probability calculations */ u32 idx; + rcu_read_lock(); root_lock = qdisc_lock(qdisc_root_sleeping(sch)); spin_lock(root_lock); @@ -391,6 +392,7 @@ static void fq_pie_timer(struct timer_list *t) mod_timer(&q->adapt_timer, jiffies + q->p_params.tupdate); spin_unlock(root_lock); + rcu_read_unlock(); } static int fq_pie_init(struct Qdisc *sch, struct nlattr *opt, diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a9aadc4e6858..ee43e8ac039e 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -648,7 +648,7 @@ struct Qdisc_ops noop_qdisc_ops __read_mostly = { static struct netdev_queue noop_netdev_queue = { RCU_POINTER_INITIALIZER(qdisc, &noop_qdisc), - .qdisc_sleeping = &noop_qdisc, + RCU_POINTER_INITIALIZER(qdisc_sleeping, &noop_qdisc), }; struct Qdisc noop_qdisc = { @@ -1103,7 +1103,7 @@ EXPORT_SYMBOL(qdisc_put_unlocked); struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, struct Qdisc *qdisc) { - struct Qdisc *oqdisc = dev_queue->qdisc_sleeping; + struct Qdisc *oqdisc = rtnl_dereference(dev_queue->qdisc_sleeping); spinlock_t *root_lock; root_lock = qdisc_lock(oqdisc); @@ -1112,7 +1112,7 @@ struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, /* ... and graft new one */ if (qdisc == NULL) qdisc = &noop_qdisc; - dev_queue->qdisc_sleeping = qdisc; + rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc); rcu_assign_pointer(dev_queue->qdisc, &noop_qdisc); spin_unlock_bh(root_lock); @@ -1125,12 +1125,12 @@ static void shutdown_scheduler_queue(struct net_device *dev, struct netdev_queue *dev_queue, void *_qdisc_default) { - struct Qdisc *qdisc = dev_queue->qdisc_sleeping; + struct Qdisc *qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); struct Qdisc *qdisc_default = _qdisc_default; if (qdisc) { rcu_assign_pointer(dev_queue->qdisc, qdisc_default); - dev_queue->qdisc_sleeping = qdisc_default; + rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc_default); qdisc_put(qdisc); } @@ -1154,7 +1154,7 @@ static void attach_one_default_qdisc(struct net_device *dev, if (!netif_is_multiqueue(dev)) qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; - dev_queue->qdisc_sleeping = qdisc; + rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc); } static void attach_default_qdiscs(struct net_device *dev) @@ -1167,7 +1167,7 @@ static void attach_default_qdiscs(struct net_device *dev) if (!netif_is_multiqueue(dev) || dev->priv_flags & IFF_NO_QUEUE) { netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); - qdisc = txq->qdisc_sleeping; + qdisc = rtnl_dereference(txq->qdisc_sleeping); rcu_assign_pointer(dev->qdisc, qdisc); qdisc_refcount_inc(qdisc); } else { @@ -1186,7 +1186,7 @@ static void attach_default_qdiscs(struct net_device *dev) netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); dev->priv_flags |= IFF_NO_QUEUE; netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); - qdisc = txq->qdisc_sleeping; + qdisc = rtnl_dereference(txq->qdisc_sleeping); rcu_assign_pointer(dev->qdisc, qdisc); qdisc_refcount_inc(qdisc); dev->priv_flags ^= IFF_NO_QUEUE; @@ -1202,7 +1202,7 @@ static void transition_one_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, void *_need_watchdog) { - struct Qdisc *new_qdisc = dev_queue->qdisc_sleeping; + struct Qdisc *new_qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); int *need_watchdog_p = _need_watchdog; if (!(new_qdisc->flags & TCQ_F_BUILTIN)) @@ -1272,7 +1272,7 @@ static void dev_reset_queue(struct net_device *dev, struct Qdisc *qdisc; bool nolock; - qdisc = dev_queue->qdisc_sleeping; + qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); if (!qdisc) return; @@ -1303,7 +1303,7 @@ static bool some_qdisc_is_busy(struct net_device *dev) int val; dev_queue = netdev_get_tx_queue(dev, i); - q = dev_queue->qdisc_sleeping; + q = rtnl_dereference(dev_queue->qdisc_sleeping); root_lock = qdisc_lock(q); spin_lock_bh(root_lock); @@ -1379,7 +1379,7 @@ EXPORT_SYMBOL(dev_deactivate); static int qdisc_change_tx_queue_len(struct net_device *dev, struct netdev_queue *dev_queue) { - struct Qdisc *qdisc = dev_queue->qdisc_sleeping; + struct Qdisc *qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); const struct Qdisc_ops *ops = qdisc->ops; if (ops->change_tx_queue_len) @@ -1404,7 +1404,7 @@ void mq_change_real_num_tx(struct Qdisc *sch, unsigned int new_real_tx) unsigned int i; for (i = new_real_tx; i < dev->real_num_tx_queues; i++) { - qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; + qdisc = rtnl_dereference(netdev_get_tx_queue(dev, i)->qdisc_sleeping); /* Only update the default qdiscs we created, * qdiscs with handles are always hashed. */ @@ -1412,7 +1412,7 @@ void mq_change_real_num_tx(struct Qdisc *sch, unsigned int new_real_tx) qdisc_hash_del(qdisc); } for (i = dev->real_num_tx_queues; i < new_real_tx; i++) { - qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; + qdisc = rtnl_dereference(netdev_get_tx_queue(dev, i)->qdisc_sleeping); if (qdisc != &noop_qdisc && !qdisc->handle) qdisc_hash_add(qdisc, false); } @@ -1449,7 +1449,7 @@ static void dev_init_scheduler_queue(struct net_device *dev, struct Qdisc *qdisc = _qdisc; rcu_assign_pointer(dev_queue->qdisc, qdisc); - dev_queue->qdisc_sleeping = qdisc; + rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc); } void dev_init_scheduler(struct net_device *dev) diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index d0bc660d7401..c860119a8f09 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -141,7 +141,7 @@ static int mq_dump(struct Qdisc *sch, struct sk_buff *skb) * qdisc totals are added at end. */ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { - qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping; + qdisc = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); spin_lock_bh(qdisc_lock(qdisc)); gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, @@ -202,7 +202,7 @@ static struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl) { struct netdev_queue *dev_queue = mq_queue_get(sch, cl); - return dev_queue->qdisc_sleeping; + return rtnl_dereference(dev_queue->qdisc_sleeping); } static unsigned long mq_find(struct Qdisc *sch, u32 classid) @@ -221,7 +221,7 @@ static int mq_dump_class(struct Qdisc *sch, unsigned long cl, tcm->tcm_parent = TC_H_ROOT; tcm->tcm_handle |= TC_H_MIN(cl); - tcm->tcm_info = dev_queue->qdisc_sleeping->handle; + tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; return 0; } @@ -230,7 +230,7 @@ static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl, { struct netdev_queue *dev_queue = mq_queue_get(sch, cl); - sch = dev_queue->qdisc_sleeping; + sch = rtnl_dereference(dev_queue->qdisc_sleeping); if (gnet_stats_copy_basic(d, sch->cpu_bstats, &sch->bstats, true) < 0 || qdisc_qstats_copy(d, sch) < 0) return -1; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 4c68abaa289b..9f26fb7d5823 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -399,7 +399,7 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) * qdisc totals are added at end. */ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { - qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping; + qdisc = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); spin_lock_bh(qdisc_lock(qdisc)); gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, @@ -449,7 +449,7 @@ static struct Qdisc *mqprio_leaf(struct Qdisc *sch, unsigned long cl) if (!dev_queue) return NULL; - return dev_queue->qdisc_sleeping; + return rtnl_dereference(dev_queue->qdisc_sleeping); } static unsigned long mqprio_find(struct Qdisc *sch, u32 classid) @@ -482,7 +482,7 @@ static int mqprio_dump_class(struct Qdisc *sch, unsigned long cl, tcm->tcm_parent = (tc < 0) ? 0 : TC_H_MAKE(TC_H_MAJ(sch->handle), TC_H_MIN(tc + TC_H_MIN_PRIORITY)); - tcm->tcm_info = dev_queue->qdisc_sleeping->handle; + tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; } else { tcm->tcm_parent = TC_H_ROOT; tcm->tcm_info = 0; @@ -538,7 +538,7 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, } else { struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); - sch = dev_queue->qdisc_sleeping; + sch = rtnl_dereference(dev_queue->qdisc_sleeping); if (gnet_stats_copy_basic(d, sch->cpu_bstats, &sch->bstats, true) < 0 || qdisc_qstats_copy(d, sch) < 0) diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index 265c238047a4..b60b31ef71cc 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -421,8 +421,10 @@ static void pie_timer(struct timer_list *t) { struct pie_sched_data *q = from_timer(q, t, adapt_timer); struct Qdisc *sch = q->sch; - spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); + spinlock_t *root_lock; + rcu_read_lock(); + root_lock = qdisc_lock(qdisc_root_sleeping(sch)); spin_lock(root_lock); pie_calculate_probability(&q->params, &q->vars, sch->qstats.backlog); @@ -430,6 +432,7 @@ static void pie_timer(struct timer_list *t) if (q->params.tupdate) mod_timer(&q->adapt_timer, jiffies + q->params.tupdate); spin_unlock(root_lock); + rcu_read_unlock(); } static int pie_init(struct Qdisc *sch, struct nlattr *opt, diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 98129324e157..16277b6a0238 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -321,12 +321,15 @@ static inline void red_adaptative_timer(struct timer_list *t) { struct red_sched_data *q = from_timer(q, t, adapt_timer); struct Qdisc *sch = q->sch; - spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); + spinlock_t *root_lock; + rcu_read_lock(); + root_lock = qdisc_lock(qdisc_root_sleeping(sch)); spin_lock(root_lock); red_adaptative_algo(&q->parms, &q->vars); mod_timer(&q->adapt_timer, jiffies + HZ/2); spin_unlock(root_lock); + rcu_read_unlock(); } static int red_init(struct Qdisc *sch, struct nlattr *opt, diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index abd436307d6a..66dcb18638fe 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -606,10 +606,12 @@ static void sfq_perturbation(struct timer_list *t) { struct sfq_sched_data *q = from_timer(q, t, perturb_timer); struct Qdisc *sch = q->sch; - spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); + spinlock_t *root_lock; siphash_key_t nkey; get_random_bytes(&nkey, sizeof(nkey)); + rcu_read_lock(); + root_lock = qdisc_lock(qdisc_root_sleeping(sch)); spin_lock(root_lock); q->perturbation = nkey; if (!q->filter_list && q->tail) @@ -618,6 +620,7 @@ static void sfq_perturbation(struct timer_list *t) if (q->perturb_period) mod_timer(&q->perturb_timer, jiffies + q->perturb_period); + rcu_read_unlock(); } static int sfq_change(struct Qdisc *sch, struct nlattr *opt) diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index c322a61eaeea..a274a9332f33 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -2050,7 +2050,7 @@ static struct Qdisc *taprio_leaf(struct Qdisc *sch, unsigned long cl) if (!dev_queue) return NULL; - return dev_queue->qdisc_sleeping; + return rtnl_dereference(dev_queue->qdisc_sleeping); } static unsigned long taprio_find(struct Qdisc *sch, u32 classid) @@ -2069,7 +2069,7 @@ static int taprio_dump_class(struct Qdisc *sch, unsigned long cl, tcm->tcm_parent = TC_H_ROOT; tcm->tcm_handle |= TC_H_MIN(cl); - tcm->tcm_info = dev_queue->qdisc_sleeping->handle; + tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; return 0; } @@ -2081,7 +2081,7 @@ static int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, { struct netdev_queue *dev_queue = taprio_queue_get(sch, cl); - sch = dev_queue->qdisc_sleeping; + sch = rtnl_dereference(dev_queue->qdisc_sleeping); if (gnet_stats_copy_basic(d, NULL, &sch->bstats, true) < 0 || qdisc_qstats_copy(d, sch) < 0) return -1; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 16f9238aa51d..7721239c185f 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -297,7 +297,7 @@ restart: struct net_device *slave = qdisc_dev(q); struct netdev_queue *slave_txq = netdev_get_tx_queue(slave, 0); - if (slave_txq->qdisc_sleeping != q) + if (rcu_access_pointer(slave_txq->qdisc_sleeping) != q) continue; if (netif_xmit_stopped(netdev_get_tx_queue(slave, subq)) || !netif_running(slave)) { -- cgit 1.4.1 From 4e7f1f6da79e1acda58b738e34ff972821d13072 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Thu, 20 Oct 2022 14:08:41 +0100 Subject: drm/i915/selftests: Stop using kthread_stop() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6407cf533217e09dfd895e64984c3f1ee3802373 ] Since a7c01fa93aeb ("signal: break out of wait loops on kthread_stop()") kthread_stop() started asserting a pending signal which wreaks havoc with a few of our selftests. Mainly because they are not fully expecting to handle signals, but also cutting the intended test runtimes short due signal_pending() now returning true (via __igt_timeout), which therefore breaks both the patterns of: kthread_run() ..sleep for igt_timeout_ms to allow test to exercise stuff.. kthread_stop() And check for errors recorded in the thread. And also: Main thread | Test thread ---------------+------------------------------ kthread_run() | kthread_stop() | do stuff until __igt_timeout | -- exits early due signal -- Where this kthread_stop() was assume would have a "join" semantics, which it would have had if not the new signal assertion issue. To recap, threads are now likely to catch a previously impossible ERESTARTSYS or EINTR, marking the test as failed, or have a pointlessly short run time. To work around this start using kthread_work(er) API which provides an explicit way of waiting for threads to exit. And for cases where parent controls the test duration we add explicit signaling which threads will now use instead of relying on kthread_should_stop(). Signed-off-by: Tvrtko Ursulin Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20221020130841.3845791-1-tvrtko.ursulin@linux.intel.com Stable-dep-of: 79d0150d2d98 ("drm/i915/selftests: Add some missing error propagation") Signed-off-by: Sasha Levin --- .../gpu/drm/i915/gem/selftests/i915_gem_context.c | 118 +++++----- drivers/gpu/drm/i915/gt/selftest_execlists.c | 48 ++-- drivers/gpu/drm/i915/gt/selftest_hangcheck.c | 51 +++-- drivers/gpu/drm/i915/selftests/i915_request.c | 252 +++++++++++++-------- 4 files changed, 281 insertions(+), 188 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c index c6ad67b90e8a..d8864444432b 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c @@ -179,97 +179,108 @@ out_file: } struct parallel_switch { - struct task_struct *tsk; + struct kthread_worker *worker; + struct kthread_work work; struct intel_context *ce[2]; + int result; }; -static int __live_parallel_switch1(void *data) +static void __live_parallel_switch1(struct kthread_work *work) { - struct parallel_switch *arg = data; + struct parallel_switch *arg = + container_of(work, typeof(*arg), work); IGT_TIMEOUT(end_time); unsigned long count; count = 0; + arg->result = 0; do { struct i915_request *rq = NULL; - int err, n; + int n; - err = 0; - for (n = 0; !err && n < ARRAY_SIZE(arg->ce); n++) { + for (n = 0; !arg->result && n < ARRAY_SIZE(arg->ce); n++) { struct i915_request *prev = rq; rq = i915_request_create(arg->ce[n]); if (IS_ERR(rq)) { i915_request_put(prev); - return PTR_ERR(rq); + arg->result = PTR_ERR(rq); + break; } i915_request_get(rq); if (prev) { - err = i915_request_await_dma_fence(rq, &prev->fence); + arg->result = + i915_request_await_dma_fence(rq, + &prev->fence); i915_request_put(prev); } i915_request_add(rq); } + + if (IS_ERR_OR_NULL(rq)) + break; + if (i915_request_wait(rq, 0, HZ) < 0) - err = -ETIME; + arg->result = -ETIME; + i915_request_put(rq); - if (err) - return err; count++; - } while (!__igt_timeout(end_time, NULL)); + } while (!arg->result && !__igt_timeout(end_time, NULL)); - pr_info("%s: %lu switches (sync)\n", arg->ce[0]->engine->name, count); - return 0; + pr_info("%s: %lu switches (sync) <%d>\n", + arg->ce[0]->engine->name, count, arg->result); } -static int __live_parallel_switchN(void *data) +static void __live_parallel_switchN(struct kthread_work *work) { - struct parallel_switch *arg = data; + struct parallel_switch *arg = + container_of(work, typeof(*arg), work); struct i915_request *rq = NULL; IGT_TIMEOUT(end_time); unsigned long count; int n; count = 0; + arg->result = 0; do { - for (n = 0; n < ARRAY_SIZE(arg->ce); n++) { + for (n = 0; !arg->result && n < ARRAY_SIZE(arg->ce); n++) { struct i915_request *prev = rq; - int err = 0; rq = i915_request_create(arg->ce[n]); if (IS_ERR(rq)) { i915_request_put(prev); - return PTR_ERR(rq); + arg->result = PTR_ERR(rq); + break; } i915_request_get(rq); if (prev) { - err = i915_request_await_dma_fence(rq, &prev->fence); + arg->result = + i915_request_await_dma_fence(rq, + &prev->fence); i915_request_put(prev); } i915_request_add(rq); - if (err) { - i915_request_put(rq); - return err; - } } count++; - } while (!__igt_timeout(end_time, NULL)); - i915_request_put(rq); + } while (!arg->result && !__igt_timeout(end_time, NULL)); - pr_info("%s: %lu switches (many)\n", arg->ce[0]->engine->name, count); - return 0; + if (!IS_ERR_OR_NULL(rq)) + i915_request_put(rq); + + pr_info("%s: %lu switches (many) <%d>\n", + arg->ce[0]->engine->name, count, arg->result); } static int live_parallel_switch(void *arg) { struct drm_i915_private *i915 = arg; - static int (* const func[])(void *arg) = { + static void (* const func[])(struct kthread_work *) = { __live_parallel_switch1, __live_parallel_switchN, NULL, @@ -277,7 +288,7 @@ static int live_parallel_switch(void *arg) struct parallel_switch *data = NULL; struct i915_gem_engines *engines; struct i915_gem_engines_iter it; - int (* const *fn)(void *arg); + void (* const *fn)(struct kthread_work *); struct i915_gem_context *ctx; struct intel_context *ce; struct file *file; @@ -348,9 +359,22 @@ static int live_parallel_switch(void *arg) } } + for (n = 0; n < count; n++) { + struct kthread_worker *worker; + + if (!data[n].ce[0]) + continue; + + worker = kthread_create_worker(0, "igt/parallel:%s", + data[n].ce[0]->engine->name); + if (IS_ERR(worker)) + goto out; + + data[n].worker = worker; + } + for (fn = func; !err && *fn; fn++) { struct igt_live_test t; - int n; err = igt_live_test_begin(&t, i915, __func__, ""); if (err) @@ -360,30 +384,17 @@ static int live_parallel_switch(void *arg) if (!data[n].ce[0]) continue; - data[n].tsk = kthread_run(*fn, &data[n], - "igt/parallel:%s", - data[n].ce[0]->engine->name); - if (IS_ERR(data[n].tsk)) { - err = PTR_ERR(data[n].tsk); - break; - } - get_task_struct(data[n].tsk); + data[n].result = 0; + kthread_init_work(&data[n].work, *fn); + kthread_queue_work(data[n].worker, &data[n].work); } - yield(); /* start all threads before we kthread_stop() */ - for (n = 0; n < count; n++) { - int status; - - if (IS_ERR_OR_NULL(data[n].tsk)) - continue; - - status = kthread_stop(data[n].tsk); - if (status && !err) - err = status; - - put_task_struct(data[n].tsk); - data[n].tsk = NULL; + if (data[n].ce[0]) { + kthread_flush_work(&data[n].work); + if (data[n].result && !err) + err = data[n].result; + } } if (igt_live_test_end(&t)) @@ -399,6 +410,9 @@ out: intel_context_unpin(data[n].ce[m]); intel_context_put(data[n].ce[m]); } + + if (data[n].worker) + kthread_destroy_worker(data[n].worker); } kfree(data); out_file: diff --git a/drivers/gpu/drm/i915/gt/selftest_execlists.c b/drivers/gpu/drm/i915/gt/selftest_execlists.c index 1e08b2473b99..6312f42daef4 100644 --- a/drivers/gpu/drm/i915/gt/selftest_execlists.c +++ b/drivers/gpu/drm/i915/gt/selftest_execlists.c @@ -3475,12 +3475,14 @@ static int random_priority(struct rnd_state *rnd) struct preempt_smoke { struct intel_gt *gt; + struct kthread_work work; struct i915_gem_context **contexts; struct intel_engine_cs *engine; struct drm_i915_gem_object *batch; unsigned int ncontext; struct rnd_state prng; unsigned long count; + int result; }; static struct i915_gem_context *smoke_context(struct preempt_smoke *smoke) @@ -3540,34 +3542,31 @@ unpin: return err; } -static int smoke_crescendo_thread(void *arg) +static void smoke_crescendo_work(struct kthread_work *work) { - struct preempt_smoke *smoke = arg; + struct preempt_smoke *smoke = container_of(work, typeof(*smoke), work); IGT_TIMEOUT(end_time); unsigned long count; count = 0; do { struct i915_gem_context *ctx = smoke_context(smoke); - int err; - err = smoke_submit(smoke, - ctx, count % I915_PRIORITY_MAX, - smoke->batch); - if (err) - return err; + smoke->result = smoke_submit(smoke, ctx, + count % I915_PRIORITY_MAX, + smoke->batch); count++; - } while (count < smoke->ncontext && !__igt_timeout(end_time, NULL)); + } while (!smoke->result && count < smoke->ncontext && + !__igt_timeout(end_time, NULL)); smoke->count = count; - return 0; } static int smoke_crescendo(struct preempt_smoke *smoke, unsigned int flags) #define BATCH BIT(0) { - struct task_struct *tsk[I915_NUM_ENGINES] = {}; + struct kthread_worker *worker[I915_NUM_ENGINES] = {}; struct preempt_smoke *arg; struct intel_engine_cs *engine; enum intel_engine_id id; @@ -3578,6 +3577,8 @@ static int smoke_crescendo(struct preempt_smoke *smoke, unsigned int flags) if (!arg) return -ENOMEM; + memset(arg, 0, I915_NUM_ENGINES * sizeof(*arg)); + for_each_engine(engine, smoke->gt, id) { arg[id] = *smoke; arg[id].engine = engine; @@ -3585,31 +3586,28 @@ static int smoke_crescendo(struct preempt_smoke *smoke, unsigned int flags) arg[id].batch = NULL; arg[id].count = 0; - tsk[id] = kthread_run(smoke_crescendo_thread, arg, - "igt/smoke:%d", id); - if (IS_ERR(tsk[id])) { - err = PTR_ERR(tsk[id]); + worker[id] = kthread_create_worker(0, "igt/smoke:%d", id); + if (IS_ERR(worker[id])) { + err = PTR_ERR(worker[id]); break; } - get_task_struct(tsk[id]); - } - yield(); /* start all threads before we kthread_stop() */ + kthread_init_work(&arg[id].work, smoke_crescendo_work); + kthread_queue_work(worker[id], &arg[id].work); + } count = 0; for_each_engine(engine, smoke->gt, id) { - int status; - - if (IS_ERR_OR_NULL(tsk[id])) + if (IS_ERR_OR_NULL(worker[id])) continue; - status = kthread_stop(tsk[id]); - if (status && !err) - err = status; + kthread_flush_work(&arg[id].work); + if (arg[id].result && !err) + err = arg[id].result; count += arg[id].count; - put_task_struct(tsk[id]); + kthread_destroy_worker(worker[id]); } pr_info("Submitted %lu crescendo:%x requests across %d engines and %d contexts\n", diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c index 7f3bb1d34dfb..71263058a7b0 100644 --- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c +++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c @@ -866,10 +866,13 @@ static int igt_reset_active_engine(void *arg) } struct active_engine { - struct task_struct *task; + struct kthread_worker *worker; + struct kthread_work work; struct intel_engine_cs *engine; unsigned long resets; unsigned int flags; + bool stop; + int result; }; #define TEST_ACTIVE BIT(0) @@ -900,10 +903,10 @@ static int active_request_put(struct i915_request *rq) return err; } -static int active_engine(void *data) +static void active_engine(struct kthread_work *work) { I915_RND_STATE(prng); - struct active_engine *arg = data; + struct active_engine *arg = container_of(work, typeof(*arg), work); struct intel_engine_cs *engine = arg->engine; struct i915_request *rq[8] = {}; struct intel_context *ce[ARRAY_SIZE(rq)]; @@ -913,16 +916,17 @@ static int active_engine(void *data) for (count = 0; count < ARRAY_SIZE(ce); count++) { ce[count] = intel_context_create(engine); if (IS_ERR(ce[count])) { - err = PTR_ERR(ce[count]); - pr_err("[%s] Create context #%ld failed: %d!\n", engine->name, count, err); + arg->result = PTR_ERR(ce[count]); + pr_err("[%s] Create context #%ld failed: %d!\n", + engine->name, count, arg->result); while (--count) intel_context_put(ce[count]); - return err; + return; } } count = 0; - while (!kthread_should_stop()) { + while (!READ_ONCE(arg->stop)) { unsigned int idx = count++ & (ARRAY_SIZE(rq) - 1); struct i915_request *old = rq[idx]; struct i915_request *new; @@ -967,7 +971,7 @@ static int active_engine(void *data) intel_context_put(ce[count]); } - return err; + arg->result = err; } static int __igt_reset_engines(struct intel_gt *gt, @@ -1022,7 +1026,7 @@ static int __igt_reset_engines(struct intel_gt *gt, memset(threads, 0, sizeof(*threads) * I915_NUM_ENGINES); for_each_engine(other, gt, tmp) { - struct task_struct *tsk; + struct kthread_worker *worker; threads[tmp].resets = i915_reset_engine_count(global, other); @@ -1036,19 +1040,21 @@ static int __igt_reset_engines(struct intel_gt *gt, threads[tmp].engine = other; threads[tmp].flags = flags; - tsk = kthread_run(active_engine, &threads[tmp], - "igt/%s", other->name); - if (IS_ERR(tsk)) { - err = PTR_ERR(tsk); - pr_err("[%s] Thread spawn failed: %d!\n", engine->name, err); + worker = kthread_create_worker(0, "igt/%s", + other->name); + if (IS_ERR(worker)) { + err = PTR_ERR(worker); + pr_err("[%s] Worker create failed: %d!\n", + engine->name, err); goto unwind; } - threads[tmp].task = tsk; - get_task_struct(tsk); - } + threads[tmp].worker = worker; - yield(); /* start all threads before we begin */ + kthread_init_work(&threads[tmp].work, active_engine); + kthread_queue_work(threads[tmp].worker, + &threads[tmp].work); + } st_engine_heartbeat_disable_no_pm(engine); GEM_BUG_ON(test_and_set_bit(I915_RESET_ENGINE + id, @@ -1197,17 +1203,20 @@ unwind: for_each_engine(other, gt, tmp) { int ret; - if (!threads[tmp].task) + if (!threads[tmp].worker) continue; - ret = kthread_stop(threads[tmp].task); + WRITE_ONCE(threads[tmp].stop, true); + kthread_flush_work(&threads[tmp].work); + ret = READ_ONCE(threads[tmp].result); if (ret) { pr_err("kthread for other engine %s failed, err=%d\n", other->name, ret); if (!err) err = ret; } - put_task_struct(threads[tmp].task); + + kthread_destroy_worker(threads[tmp].worker); /* GuC based resets are not logged per engine */ if (!using_guc) { diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c index 818a4909c1f3..a46350c37e9d 100644 --- a/drivers/gpu/drm/i915/selftests/i915_request.c +++ b/drivers/gpu/drm/i915/selftests/i915_request.c @@ -299,9 +299,18 @@ __live_request_alloc(struct intel_context *ce) return intel_context_create_request(ce); } -static int __igt_breadcrumbs_smoketest(void *arg) +struct smoke_thread { + struct kthread_worker *worker; + struct kthread_work work; + struct smoketest *t; + bool stop; + int result; +}; + +static void __igt_breadcrumbs_smoketest(struct kthread_work *work) { - struct smoketest *t = arg; + struct smoke_thread *thread = container_of(work, typeof(*thread), work); + struct smoketest *t = thread->t; const unsigned int max_batch = min(t->ncontexts, t->max_batch) - 1; const unsigned int total = 4 * t->ncontexts + 1; unsigned int num_waits = 0, num_fences = 0; @@ -320,8 +329,10 @@ static int __igt_breadcrumbs_smoketest(void *arg) */ requests = kcalloc(total, sizeof(*requests), GFP_KERNEL); - if (!requests) - return -ENOMEM; + if (!requests) { + thread->result = -ENOMEM; + return; + } order = i915_random_order(total, &prng); if (!order) { @@ -329,7 +340,7 @@ static int __igt_breadcrumbs_smoketest(void *arg) goto out_requests; } - while (!kthread_should_stop()) { + while (!READ_ONCE(thread->stop)) { struct i915_sw_fence *submit, *wait; unsigned int n, count; @@ -437,7 +448,7 @@ static int __igt_breadcrumbs_smoketest(void *arg) kfree(order); out_requests: kfree(requests); - return err; + thread->result = err; } static int mock_breadcrumbs_smoketest(void *arg) @@ -450,7 +461,7 @@ static int mock_breadcrumbs_smoketest(void *arg) .request_alloc = __mock_request_alloc }; unsigned int ncpus = num_online_cpus(); - struct task_struct **threads; + struct smoke_thread *threads; unsigned int n; int ret = 0; @@ -479,28 +490,37 @@ static int mock_breadcrumbs_smoketest(void *arg) } for (n = 0; n < ncpus; n++) { - threads[n] = kthread_run(__igt_breadcrumbs_smoketest, - &t, "igt/%d", n); - if (IS_ERR(threads[n])) { - ret = PTR_ERR(threads[n]); + struct kthread_worker *worker; + + worker = kthread_create_worker(0, "igt/%d", n); + if (IS_ERR(worker)) { + ret = PTR_ERR(worker); ncpus = n; break; } - get_task_struct(threads[n]); + threads[n].worker = worker; + threads[n].t = &t; + threads[n].stop = false; + threads[n].result = 0; + + kthread_init_work(&threads[n].work, + __igt_breadcrumbs_smoketest); + kthread_queue_work(worker, &threads[n].work); } - yield(); /* start all threads before we begin */ msleep(jiffies_to_msecs(i915_selftest.timeout_jiffies)); for (n = 0; n < ncpus; n++) { int err; - err = kthread_stop(threads[n]); + WRITE_ONCE(threads[n].stop, true); + kthread_flush_work(&threads[n].work); + err = READ_ONCE(threads[n].result); if (err < 0 && !ret) ret = err; - put_task_struct(threads[n]); + kthread_destroy_worker(threads[n].worker); } pr_info("Completed %lu waits for %lu fence across %d cpus\n", atomic_long_read(&t.num_waits), @@ -1419,9 +1439,18 @@ out_free: return err; } -static int __live_parallel_engine1(void *arg) +struct parallel_thread { + struct kthread_worker *worker; + struct kthread_work work; + struct intel_engine_cs *engine; + int result; +}; + +static void __live_parallel_engine1(struct kthread_work *work) { - struct intel_engine_cs *engine = arg; + struct parallel_thread *thread = + container_of(work, typeof(*thread), work); + struct intel_engine_cs *engine = thread->engine; IGT_TIMEOUT(end_time); unsigned long count; int err = 0; @@ -1452,12 +1481,14 @@ static int __live_parallel_engine1(void *arg) intel_engine_pm_put(engine); pr_info("%s: %lu request + sync\n", engine->name, count); - return err; + thread->result = err; } -static int __live_parallel_engineN(void *arg) +static void __live_parallel_engineN(struct kthread_work *work) { - struct intel_engine_cs *engine = arg; + struct parallel_thread *thread = + container_of(work, typeof(*thread), work); + struct intel_engine_cs *engine = thread->engine; IGT_TIMEOUT(end_time); unsigned long count; int err = 0; @@ -1479,7 +1510,7 @@ static int __live_parallel_engineN(void *arg) intel_engine_pm_put(engine); pr_info("%s: %lu requests\n", engine->name, count); - return err; + thread->result = err; } static bool wake_all(struct drm_i915_private *i915) @@ -1505,9 +1536,11 @@ static int wait_for_all(struct drm_i915_private *i915) return -ETIME; } -static int __live_parallel_spin(void *arg) +static void __live_parallel_spin(struct kthread_work *work) { - struct intel_engine_cs *engine = arg; + struct parallel_thread *thread = + container_of(work, typeof(*thread), work); + struct intel_engine_cs *engine = thread->engine; struct igt_spinner spin; struct i915_request *rq; int err = 0; @@ -1520,7 +1553,8 @@ static int __live_parallel_spin(void *arg) if (igt_spinner_init(&spin, engine->gt)) { wake_all(engine->i915); - return -ENOMEM; + thread->result = -ENOMEM; + return; } intel_engine_pm_get(engine); @@ -1553,22 +1587,22 @@ static int __live_parallel_spin(void *arg) out_spin: igt_spinner_fini(&spin); - return err; + thread->result = err; } static int live_parallel_engines(void *arg) { struct drm_i915_private *i915 = arg; - static int (* const func[])(void *arg) = { + static void (* const func[])(struct kthread_work *) = { __live_parallel_engine1, __live_parallel_engineN, __live_parallel_spin, NULL, }; const unsigned int nengines = num_uabi_engines(i915); + struct parallel_thread *threads; struct intel_engine_cs *engine; - int (* const *fn)(void *arg); - struct task_struct **tsk; + void (* const *fn)(struct kthread_work *); int err = 0; /* @@ -1576,8 +1610,8 @@ static int live_parallel_engines(void *arg) * tests that we load up the system maximally. */ - tsk = kcalloc(nengines, sizeof(*tsk), GFP_KERNEL); - if (!tsk) + threads = kcalloc(nengines, sizeof(*threads), GFP_KERNEL); + if (!threads) return -ENOMEM; for (fn = func; !err && *fn; fn++) { @@ -1594,37 +1628,44 @@ static int live_parallel_engines(void *arg) idx = 0; for_each_uabi_engine(engine, i915) { - tsk[idx] = kthread_run(*fn, engine, - "igt/parallel:%s", - engine->name); - if (IS_ERR(tsk[idx])) { - err = PTR_ERR(tsk[idx]); + struct kthread_worker *worker; + + worker = kthread_create_worker(0, "igt/parallel:%s", + engine->name); + if (IS_ERR(worker)) { + err = PTR_ERR(worker); break; } - get_task_struct(tsk[idx++]); - } - yield(); /* start all threads before we kthread_stop() */ + threads[idx].worker = worker; + threads[idx].result = 0; + threads[idx].engine = engine; + + kthread_init_work(&threads[idx].work, *fn); + kthread_queue_work(worker, &threads[idx].work); + idx++; + } idx = 0; for_each_uabi_engine(engine, i915) { int status; - if (IS_ERR(tsk[idx])) + if (!threads[idx].worker) break; - status = kthread_stop(tsk[idx]); + kthread_flush_work(&threads[idx].work); + status = READ_ONCE(threads[idx].result); if (status && !err) err = status; - put_task_struct(tsk[idx++]); + kthread_destroy_worker(threads[idx++].worker); } if (igt_live_test_end(&t)) err = -EIO; } - kfree(tsk); + kfree(threads); return err; } @@ -1672,7 +1713,7 @@ static int live_breadcrumbs_smoketest(void *arg) const unsigned int ncpus = num_online_cpus(); unsigned long num_waits, num_fences; struct intel_engine_cs *engine; - struct task_struct **threads; + struct smoke_thread *threads; struct igt_live_test live; intel_wakeref_t wakeref; struct smoketest *smoke; @@ -1746,23 +1787,26 @@ static int live_breadcrumbs_smoketest(void *arg) smoke[idx].max_batch, engine->name); for (n = 0; n < ncpus; n++) { - struct task_struct *tsk; + unsigned int i = idx * ncpus + n; + struct kthread_worker *worker; - tsk = kthread_run(__igt_breadcrumbs_smoketest, - &smoke[idx], "igt/%d.%d", idx, n); - if (IS_ERR(tsk)) { - ret = PTR_ERR(tsk); + worker = kthread_create_worker(0, "igt/%d.%d", idx, n); + if (IS_ERR(worker)) { + ret = PTR_ERR(worker); goto out_flush; } - get_task_struct(tsk); - threads[idx * ncpus + n] = tsk; + threads[i].worker = worker; + threads[i].t = &smoke[idx]; + + kthread_init_work(&threads[i].work, + __igt_breadcrumbs_smoketest); + kthread_queue_work(worker, &threads[i].work); } idx++; } - yield(); /* start all threads before we begin */ msleep(jiffies_to_msecs(i915_selftest.timeout_jiffies)); out_flush: @@ -1771,17 +1815,19 @@ out_flush: num_fences = 0; for_each_uabi_engine(engine, i915) { for (n = 0; n < ncpus; n++) { - struct task_struct *tsk = threads[idx * ncpus + n]; + unsigned int i = idx * ncpus + n; int err; - if (!tsk) + if (!threads[i].worker) continue; - err = kthread_stop(tsk); + WRITE_ONCE(threads[i].stop, true); + kthread_flush_work(&threads[i].work); + err = READ_ONCE(threads[i].result); if (err < 0 && !ret) ret = err; - put_task_struct(tsk); + kthread_destroy_worker(threads[i].worker); } num_waits += atomic_long_read(&smoke[idx].num_waits); @@ -2891,9 +2937,18 @@ out: return err; } -static int p_sync0(void *arg) +struct p_thread { + struct perf_stats p; + struct kthread_worker *worker; + struct kthread_work work; + struct intel_engine_cs *engine; + int result; +}; + +static void p_sync0(struct kthread_work *work) { - struct perf_stats *p = arg; + struct p_thread *thread = container_of(work, typeof(*thread), work); + struct perf_stats *p = &thread->p; struct intel_engine_cs *engine = p->engine; struct intel_context *ce; IGT_TIMEOUT(end_time); @@ -2902,13 +2957,16 @@ static int p_sync0(void *arg) int err = 0; ce = intel_context_create(engine); - if (IS_ERR(ce)) - return PTR_ERR(ce); + if (IS_ERR(ce)) { + thread->result = PTR_ERR(ce); + return; + } err = intel_context_pin(ce); if (err) { intel_context_put(ce); - return err; + thread->result = err; + return; } if (intel_engine_supports_stats(engine)) { @@ -2958,12 +3016,13 @@ static int p_sync0(void *arg) intel_context_unpin(ce); intel_context_put(ce); - return err; + thread->result = err; } -static int p_sync1(void *arg) +static void p_sync1(struct kthread_work *work) { - struct perf_stats *p = arg; + struct p_thread *thread = container_of(work, typeof(*thread), work); + struct perf_stats *p = &thread->p; struct intel_engine_cs *engine = p->engine; struct i915_request *prev = NULL; struct intel_context *ce; @@ -2973,13 +3032,16 @@ static int p_sync1(void *arg) int err = 0; ce = intel_context_create(engine); - if (IS_ERR(ce)) - return PTR_ERR(ce); + if (IS_ERR(ce)) { + thread->result = PTR_ERR(ce); + return; + } err = intel_context_pin(ce); if (err) { intel_context_put(ce); - return err; + thread->result = err; + return; } if (intel_engine_supports_stats(engine)) { @@ -3031,12 +3093,13 @@ static int p_sync1(void *arg) intel_context_unpin(ce); intel_context_put(ce); - return err; + thread->result = err; } -static int p_many(void *arg) +static void p_many(struct kthread_work *work) { - struct perf_stats *p = arg; + struct p_thread *thread = container_of(work, typeof(*thread), work); + struct perf_stats *p = &thread->p; struct intel_engine_cs *engine = p->engine; struct intel_context *ce; IGT_TIMEOUT(end_time); @@ -3045,13 +3108,16 @@ static int p_many(void *arg) bool busy; ce = intel_context_create(engine); - if (IS_ERR(ce)) - return PTR_ERR(ce); + if (IS_ERR(ce)) { + thread->result = PTR_ERR(ce); + return; + } err = intel_context_pin(ce); if (err) { intel_context_put(ce); - return err; + thread->result = err; + return; } if (intel_engine_supports_stats(engine)) { @@ -3092,26 +3158,23 @@ static int p_many(void *arg) intel_context_unpin(ce); intel_context_put(ce); - return err; + thread->result = err; } static int perf_parallel_engines(void *arg) { struct drm_i915_private *i915 = arg; - static int (* const func[])(void *arg) = { + static void (* const func[])(struct kthread_work *) = { p_sync0, p_sync1, p_many, NULL, }; const unsigned int nengines = num_uabi_engines(i915); + void (* const *fn)(struct kthread_work *); struct intel_engine_cs *engine; - int (* const *fn)(void *arg); struct pm_qos_request qos; - struct { - struct perf_stats p; - struct task_struct *tsk; - } *engines; + struct p_thread *engines; int err = 0; engines = kcalloc(nengines, sizeof(*engines), GFP_KERNEL); @@ -3134,36 +3197,45 @@ static int perf_parallel_engines(void *arg) idx = 0; for_each_uabi_engine(engine, i915) { + struct kthread_worker *worker; + intel_engine_pm_get(engine); memset(&engines[idx].p, 0, sizeof(engines[idx].p)); - engines[idx].p.engine = engine; - engines[idx].tsk = kthread_run(*fn, &engines[idx].p, - "igt:%s", engine->name); - if (IS_ERR(engines[idx].tsk)) { - err = PTR_ERR(engines[idx].tsk); + worker = kthread_create_worker(0, "igt:%s", + engine->name); + if (IS_ERR(worker)) { + err = PTR_ERR(worker); intel_engine_pm_put(engine); break; } - get_task_struct(engines[idx++].tsk); - } + engines[idx].worker = worker; + engines[idx].result = 0; + engines[idx].p.engine = engine; + engines[idx].engine = engine; - yield(); /* start all threads before we kthread_stop() */ + kthread_init_work(&engines[idx].work, *fn); + kthread_queue_work(worker, &engines[idx].work); + idx++; + } idx = 0; for_each_uabi_engine(engine, i915) { int status; - if (IS_ERR(engines[idx].tsk)) + if (!engines[idx].worker) break; - status = kthread_stop(engines[idx].tsk); + kthread_flush_work(&engines[idx].work); + status = READ_ONCE(engines[idx].result); if (status && !err) err = status; intel_engine_pm_put(engine); - put_task_struct(engines[idx++].tsk); + + kthread_destroy_worker(engines[idx].worker); + idx++; } if (igt_live_test_end(&t)) -- cgit 1.4.1 From 76eef453a675794e98400c8889446b1503a9dfa6 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 5 Jun 2023 14:11:35 +0100 Subject: drm/i915/selftests: Add some missing error propagation [ Upstream commit 79d0150d2d983a4f6efee676cea06027f586fcd0 ] Add some missing error propagation in live_parallel_switch. To avoid needlessly burdening the various backport processes, note I am not marking it as a fix against any patches and not copying stable since it is debug/selftests only code. Signed-off-by: Tvrtko Ursulin Reported-by: Dan Carpenter Cc: Andi Shyti Reviewed-by: Andi Shyti Fixes: 50d16d44cce4 ("drm/i915/selftests: Exercise context switching in parallel") Fixes: 6407cf533217 ("drm/i915/selftests: Stop using kthread_stop()") Link: https://patchwork.freedesktop.org/patch/msgid/20230605131135.396854-1-tvrtko.ursulin@linux.intel.com (cherry picked from commit 412fa1f097f48c8c1321806dd25e46618e0da147) Signed-off-by: Joonas Lahtinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c index d8864444432b..a4858be12ee7 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c @@ -346,8 +346,10 @@ static int live_parallel_switch(void *arg) continue; ce = intel_context_create(data[m].ce[0]->engine); - if (IS_ERR(ce)) + if (IS_ERR(ce)) { + err = PTR_ERR(ce); goto out; + } err = intel_context_pin(ce); if (err) { @@ -367,8 +369,10 @@ static int live_parallel_switch(void *arg) worker = kthread_create_worker(0, "igt/parallel:%s", data[n].ce[0]->engine->name); - if (IS_ERR(worker)) + if (IS_ERR(worker)) { + err = PTR_ERR(worker); goto out; + } data[n].worker = worker; } @@ -397,8 +401,10 @@ static int live_parallel_switch(void *arg) } } - if (igt_live_test_end(&t)) - err = -EIO; + if (igt_live_test_end(&t)) { + err = err ?: -EIO; + break; + } } out: -- cgit 1.4.1 From 60f39768efa5f5f2c8baffd4f32ef21f37a8655f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Jun 2023 11:42:33 +0000 Subject: net: sched: move rtm_tca_policy declaration to include file [ Upstream commit 886bc7d6ed3357975c5f1d3c784da96000d4bbb4 ] rtm_tca_policy is used from net/sched/sch_api.c and net/sched/cls_api.c, thus should be declared in an include file. This fixes the following sparse warning: net/sched/sch_api.c:1434:25: warning: symbol 'rtm_tca_policy' was not declared. Should it be static? Fixes: e331473fee3d ("net/sched: cls_api: add missing validation of netlink attributes") Signed-off-by: Eric Dumazet Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/net/pkt_sched.h | 2 ++ net/sched/cls_api.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 38207873eda6..8ab75128512a 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -128,6 +128,8 @@ static inline void qdisc_run(struct Qdisc *q) } } +extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1]; + /* Calculate maximal size of packet seen by hard_start_xmit routine of this device. */ diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 7b2aa04a7cdf..b51d80a2fece 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -41,8 +41,6 @@ #include #include -extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1]; - /* The list of all installed classifier types */ static LIST_HEAD(tcf_proto_base); -- cgit 1.4.1 From e582ceda5a1608c5af9987f1e90233af95b7af3b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Jun 2023 13:13:04 +0000 Subject: net: sched: act_police: fix sparse errors in tcf_police_dump() [ Upstream commit 682881ee45c81daa883dcd4fe613b0b0d988bb22 ] Fixes following sparse errors: net/sched/act_police.c:360:28: warning: dereference of noderef expression net/sched/act_police.c:362:45: warning: dereference of noderef expression net/sched/act_police.c:362:45: warning: dereference of noderef expression net/sched/act_police.c:368:28: warning: dereference of noderef expression net/sched/act_police.c:370:45: warning: dereference of noderef expression net/sched/act_police.c:370:45: warning: dereference of noderef expression net/sched/act_police.c:376:45: warning: dereference of noderef expression net/sched/act_police.c:376:45: warning: dereference of noderef expression Fixes: d1967e495a8d ("net_sched: act_police: add 2 new attributes to support police 64bit rate and peakrate") Signed-off-by: Eric Dumazet Reviewed-by: Simon Horman Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/act_police.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 0adb26e366a7..94be21378e7c 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -355,23 +355,23 @@ static int tcf_police_dump(struct sk_buff *skb, struct tc_action *a, opt.burst = PSCHED_NS2TICKS(p->tcfp_burst); if (p->rate_present) { psched_ratecfg_getrate(&opt.rate, &p->rate); - if ((police->params->rate.rate_bytes_ps >= (1ULL << 32)) && + if ((p->rate.rate_bytes_ps >= (1ULL << 32)) && nla_put_u64_64bit(skb, TCA_POLICE_RATE64, - police->params->rate.rate_bytes_ps, + p->rate.rate_bytes_ps, TCA_POLICE_PAD)) goto nla_put_failure; } if (p->peak_present) { psched_ratecfg_getrate(&opt.peakrate, &p->peak); - if ((police->params->peak.rate_bytes_ps >= (1ULL << 32)) && + if ((p->peak.rate_bytes_ps >= (1ULL << 32)) && nla_put_u64_64bit(skb, TCA_POLICE_PEAKRATE64, - police->params->peak.rate_bytes_ps, + p->peak.rate_bytes_ps, TCA_POLICE_PAD)) goto nla_put_failure; } if (p->pps_present) { if (nla_put_u64_64bit(skb, TCA_POLICE_PKTRATE64, - police->params->ppsrate.rate_pkts_ps, + p->ppsrate.rate_pkts_ps, TCA_POLICE_PAD)) goto nla_put_failure; if (nla_put_u64_64bit(skb, TCA_POLICE_PKTBURST64, -- cgit 1.4.1 From 06177b92902da33ab1a0cb14a3c538277177c2cf Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Wed, 7 Jun 2023 10:23:01 +0800 Subject: net: sched: fix possible refcount leak in tc_chain_tmplt_add() [ Upstream commit 44f8baaf230c655c249467ca415b570deca8df77 ] try_module_get will be called in tcf_proto_lookup_ops. So module_put needs to be called to drop the refcount if ops don't implement the required function. Fixes: 9f407f1768d3 ("net: sched: introduce chain templates") Signed-off-by: Hangyu Hua Reviewed-by: Larysa Zaremba Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/cls_api.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b51d80a2fece..abaf75300497 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -2780,6 +2780,7 @@ static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net, return PTR_ERR(ops); if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) { NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier"); + module_put(ops->owner); return -EOPNOTSUPP; } -- cgit 1.4.1 From dbc880567adf48661ca9cb4506073e846bf64203 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 6 Jun 2023 11:17:14 -0700 Subject: bpf: Add extra path pointer check to d_path helper [ Upstream commit f46fab0e36e611a2389d3843f34658c849b6bd60 ] Anastasios reported crash on stable 5.15 kernel with following BPF attached to lsm hook: SEC("lsm.s/bprm_creds_for_exec") int BPF_PROG(bprm_creds_for_exec, struct linux_binprm *bprm) { struct path *path = &bprm->executable->f_path; char p[128] = { 0 }; bpf_d_path(path, p, 128); return 0; } But bprm->executable can be NULL, so bpf_d_path call will crash: BUG: kernel NULL pointer dereference, address: 0000000000000018 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC NOPTI ... RIP: 0010:d_path+0x22/0x280 ... Call Trace: bpf_d_path+0x21/0x60 bpf_prog_db9cf176e84498d9_bprm_creds_for_exec+0x94/0x99 bpf_trampoline_6442506293_0+0x55/0x1000 bpf_lsm_bprm_creds_for_exec+0x5/0x10 security_bprm_creds_for_exec+0x29/0x40 bprm_execve+0x1c1/0x900 do_execveat_common.isra.0+0x1af/0x260 __x64_sys_execve+0x32/0x40 It's problem for all stable trees with bpf_d_path helper, which was added in 5.9. This issue is fixed in current bpf code, where we identify and mark trusted pointers, so the above code would fail even to load. For the sake of the stable trees and to workaround potentially broken verifier in the future, adding the code that reads the path object from the passed pointer and verifies it's valid in kernel space. Fixes: 6e22ab9da793 ("bpf: Add d_path helper") Reported-by: Anastasios Papagiannis Suggested-by: Alexei Starovoitov Signed-off-by: Jiri Olsa Signed-off-by: Daniel Borkmann Acked-by: Stanislav Fomichev Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20230606181714.532998-1-jolsa@kernel.org Signed-off-by: Sasha Levin --- kernel/trace/bpf_trace.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 9d4163abadf4..1642548892a8 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -903,13 +903,23 @@ static const struct bpf_func_proto bpf_send_signal_thread_proto = { BPF_CALL_3(bpf_d_path, struct path *, path, char *, buf, u32, sz) { + struct path copy; long len; char *p; if (!sz) return 0; - p = d_path(path, buf, sz); + /* + * The path pointer is verified as trusted and safe to use, + * but let's double check it's valid anyway to workaround + * potentially broken verifier. + */ + len = copy_from_kernel_nofault(©, path, sizeof(*path)); + if (len < 0) + return len; + + p = d_path(©, buf, sz); if (IS_ERR(p)) { len = PTR_ERR(p); } else { -- cgit 1.4.1 From c5a17f3247bd7f6c2e22678dbfcd73832f487e3f Mon Sep 17 00:00:00 2001 From: Horatio Zhang Date: Mon, 29 May 2023 14:23:37 -0400 Subject: drm/amdgpu: fix Null pointer dereference error in amdgpu_device_recover_vram [ Upstream commit 2a1eb1a343208ce7d6839b73d62aece343e693ff ] Use the function of amdgpu_bo_vm_destroy to handle the resource release of shadow bo. During the amdgpu_mes_self_test, shadow bo released, but vmbo->shadow_list was not, which caused a null pointer reference error in amdgpu_device_recover_vram when GPU reset. Fixes: 6c032c37ac3e ("drm/amdgpu: Fix vram recover doesn't work after whole GPU reset (v2)") Signed-off-by: xinhui pan Signed-off-by: Horatio Zhang Acked-by: Feifei Xu Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 10 ++++------ drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 4feedf518a19..ad8cb9e6d1ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -79,9 +79,10 @@ static void amdgpu_bo_user_destroy(struct ttm_buffer_object *tbo) static void amdgpu_bo_vm_destroy(struct ttm_buffer_object *tbo) { struct amdgpu_device *adev = amdgpu_ttm_adev(tbo->bdev); - struct amdgpu_bo *bo = ttm_to_amdgpu_bo(tbo); + struct amdgpu_bo *shadow_bo = ttm_to_amdgpu_bo(tbo), *bo; struct amdgpu_bo_vm *vmbo; + bo = shadow_bo->parent; vmbo = to_amdgpu_bo_vm(bo); /* in case amdgpu_device_recover_vram got NULL of bo->parent */ if (!list_empty(&vmbo->shadow_list)) { @@ -691,11 +692,6 @@ int amdgpu_bo_create_vm(struct amdgpu_device *adev, return r; *vmbo_ptr = to_amdgpu_bo_vm(bo_ptr); - INIT_LIST_HEAD(&(*vmbo_ptr)->shadow_list); - /* Set destroy callback to amdgpu_bo_vm_destroy after vmbo->shadow_list - * is initialized. - */ - bo_ptr->tbo.destroy = &amdgpu_bo_vm_destroy; return r; } @@ -712,6 +708,8 @@ void amdgpu_bo_add_to_shadow_list(struct amdgpu_bo_vm *vmbo) mutex_lock(&adev->shadow_list_lock); list_add_tail(&vmbo->shadow_list, &adev->shadow_list); + vmbo->shadow->parent = amdgpu_bo_ref(&vmbo->bo); + vmbo->shadow->tbo.destroy = &amdgpu_bo_vm_destroy; mutex_unlock(&adev->shadow_list_lock); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c index 01e42bdd8e4e..4642cff0e1a4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c @@ -564,7 +564,6 @@ int amdgpu_vm_pt_create(struct amdgpu_device *adev, struct amdgpu_vm *vm, return r; } - (*vmbo)->shadow->parent = amdgpu_bo_ref(bo); amdgpu_bo_add_to_shadow_list(*vmbo); return 0; -- cgit 1.4.1 From d4925800a49c07a05fec99c0523163e7bebc5463 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 2 Jun 2023 20:28:15 +0200 Subject: lib: cpu_rmap: Fix potential use-after-free in irq_cpu_rmap_release() [ Upstream commit 7c5d4801ecf0564c860033d89726b99723c55146 ] irq_cpu_rmap_release() calls cpu_rmap_put(), which may free the rmap. So we need to clear the pointer to our glue structure in rmap before doing that, not after. Fixes: 4e0473f1060a ("lib: cpu_rmap: Avoid use after free on rmap->obj array entries") Signed-off-by: Ben Hutchings Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/ZHo0vwquhOy3FaXc@decadent.org.uk Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- lib/cpu_rmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cpu_rmap.c b/lib/cpu_rmap.c index e77f12bb3c77..1833ad73de6f 100644 --- a/lib/cpu_rmap.c +++ b/lib/cpu_rmap.c @@ -268,8 +268,8 @@ static void irq_cpu_rmap_release(struct kref *ref) struct irq_glue *glue = container_of(ref, struct irq_glue, notify.kref); - cpu_rmap_put(glue->rmap); glue->rmap->obj[glue->index] = NULL; + cpu_rmap_put(glue->rmap); kfree(glue); } -- cgit 1.4.1 From 5df74018d1e6b8fdf4bba9be6e8f9bc8e638e243 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 6 Jun 2023 14:43:47 -0700 Subject: net: bcmgenet: Fix EEE implementation [ Upstream commit a9f31047baca57d47440c879cf259b86f900260c ] We had a number of short comings: - EEE must be re-evaluated whenever the state machine detects a link change as wight be switching from a link partner with EEE enabled/disabled - tx_lpi_enabled controls whether EEE should be enabled/disabled for the transmit path, which applies to the TBUF block - We do not need to forcibly enable EEE upon system resume, as the PHY state machine will trigger a link event that will do that, too Fixes: 6ef398ea60d9 ("net: bcmgenet: add EEE support") Signed-off-by: Florian Fainelli Reviewed-by: Russell King (Oracle) Link: https://lore.kernel.org/r/20230606214348.2408018-1-florian.fainelli@broadcom.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 22 ++++++++-------------- drivers/net/ethernet/broadcom/genet/bcmgenet.h | 3 +++ drivers/net/ethernet/broadcom/genet/bcmmii.c | 5 +++++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 4da2becfa950..1ae082eb9e90 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1290,7 +1290,8 @@ static void bcmgenet_get_ethtool_stats(struct net_device *dev, } } -static void bcmgenet_eee_enable_set(struct net_device *dev, bool enable) +void bcmgenet_eee_enable_set(struct net_device *dev, bool enable, + bool tx_lpi_enabled) { struct bcmgenet_priv *priv = netdev_priv(dev); u32 off = priv->hw_params->tbuf_offset + TBUF_ENERGY_CTRL; @@ -1310,7 +1311,7 @@ static void bcmgenet_eee_enable_set(struct net_device *dev, bool enable) /* Enable EEE and switch to a 27Mhz clock automatically */ reg = bcmgenet_readl(priv->base + off); - if (enable) + if (tx_lpi_enabled) reg |= TBUF_EEE_EN | TBUF_PM_EN; else reg &= ~(TBUF_EEE_EN | TBUF_PM_EN); @@ -1331,6 +1332,7 @@ static void bcmgenet_eee_enable_set(struct net_device *dev, bool enable) priv->eee.eee_enabled = enable; priv->eee.eee_active = enable; + priv->eee.tx_lpi_enabled = tx_lpi_enabled; } static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_eee *e) @@ -1346,6 +1348,7 @@ static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_eee *e) e->eee_enabled = p->eee_enabled; e->eee_active = p->eee_active; + e->tx_lpi_enabled = p->tx_lpi_enabled; e->tx_lpi_timer = bcmgenet_umac_readl(priv, UMAC_EEE_LPI_TIMER); return phy_ethtool_get_eee(dev->phydev, e); @@ -1355,7 +1358,6 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e) { struct bcmgenet_priv *priv = netdev_priv(dev); struct ethtool_eee *p = &priv->eee; - int ret = 0; if (GENET_IS_V1(priv)) return -EOPNOTSUPP; @@ -1366,16 +1368,11 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e) p->eee_enabled = e->eee_enabled; if (!p->eee_enabled) { - bcmgenet_eee_enable_set(dev, false); + bcmgenet_eee_enable_set(dev, false, false); } else { - ret = phy_init_eee(dev->phydev, false); - if (ret) { - netif_err(priv, hw, dev, "EEE initialization failed\n"); - return ret; - } - + p->eee_active = phy_init_eee(dev->phydev, false) >= 0; bcmgenet_umac_writel(priv, e->tx_lpi_timer, UMAC_EEE_LPI_TIMER); - bcmgenet_eee_enable_set(dev, true); + bcmgenet_eee_enable_set(dev, p->eee_active, e->tx_lpi_enabled); } return phy_ethtool_set_eee(dev->phydev, e); @@ -4274,9 +4271,6 @@ static int bcmgenet_resume(struct device *d) if (!device_may_wakeup(d)) phy_resume(dev->phydev); - if (priv->eee.eee_enabled) - bcmgenet_eee_enable_set(dev, true); - bcmgenet_netif_start(dev); netif_device_attach(dev); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 946f6e283c4e..1985c0ec4da2 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -703,4 +703,7 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, enum bcmgenet_power_mode mode); +void bcmgenet_eee_enable_set(struct net_device *dev, bool enable, + bool tx_lpi_enabled); + #endif /* __BCMGENET_H__ */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index ded0e64a9f6a..bf9e246784b6 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -88,6 +88,11 @@ static void bcmgenet_mac_config(struct net_device *dev) reg |= CMD_TX_EN | CMD_RX_EN; } bcmgenet_umac_writel(priv, reg, UMAC_CMD); + + priv->eee.eee_active = phy_init_eee(phydev, 0) >= 0; + bcmgenet_eee_enable_set(dev, + priv->eee.eee_enabled && priv->eee.eee_active, + priv->eee.tx_lpi_enabled); } /* setup netdev link state when PHY link status change and -- cgit 1.4.1 From 53a0c6d5c9294bad7fa8390c73ac96f386b15ce2 Mon Sep 17 00:00:00 2001 From: Sreekanth Reddy Date: Wed, 7 Jun 2023 00:54:05 -0700 Subject: bnxt_en: Don't issue AP reset during ethtool's reset operation [ Upstream commit 1d997801c7cc6a7f542e46d5a6bf16f893ad3fe9 ] Only older NIC controller's firmware uses the PROC AP reset type. Firmware on 5731X/5741X and newer chips does not support this reset type. When bnxt_reset() issues a series of resets, this PROC AP reset may actually fail on these newer chips because the firmware is not ready to accept this unsupported command yet. Avoid this unnecessary error by skipping this reset type on chips that don't support it. Fixes: 7a13240e3718 ("bnxt_en: fix ethtool_reset_flags ABI violations") Reviewed-by: Pavan Chebbi Signed-off-by: Sreekanth Reddy Signed-off-by: Michael Chan Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index b2d531e014c5..89f046ce1373 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -3727,7 +3727,7 @@ static int bnxt_reset(struct net_device *dev, u32 *flags) } } - if (req & BNXT_FW_RESET_AP) { + if (!BNXT_CHIP_P4_PLUS(bp) && (req & BNXT_FW_RESET_AP)) { /* This feature is not supported in older firmware versions */ if (bp->hwrm_spec_code >= 0x10803) { if (!bnxt_firmware_reset_ap(dev)) { -- cgit 1.4.1 From 5fc86a4580da7314dd8451f630021b9626db8c9d Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Wed, 7 Jun 2023 00:54:06 -0700 Subject: bnxt_en: Query default VLAN before VNIC setup on a VF [ Upstream commit 1a9e4f501bc6ff1b6ecb60df54fbf2b54db43bfe ] We need to call bnxt_hwrm_func_qcfg() on a VF to query the default VLAN that may be setup by the PF. If a default VLAN is enabled, the VF cannot support VLAN acceleration on the receive side and the VNIC must be setup to strip out the default VLAN tag. If a default VLAN is not enabled, the VF can support VLAN acceleration on the receive side. The VNIC should be set up to strip or not strip the VLAN based on the RX VLAN acceleration setting. Without this call to determine the default VLAN before calling bnxt_setup_vnic(), the VNIC may not be set up correctly. For example, bnxt_setup_vnic() may set up to strip the VLAN tag based on stale default VLAN information. If RX VLAN acceleration is not enabled, the VLAN tag will be incorrectly stripped and the RX data path will not work correctly. Fixes: cf6645f8ebc6 ("bnxt_en: Add function for VF driver to query default VLAN.") Reviewed-by: Pavan Chebbi Signed-off-by: Somnath Kotur Signed-off-by: Michael Chan Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index e3e5a427222f..93c3b8316c46 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8808,6 +8808,9 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) goto err_out; } + if (BNXT_VF(bp)) + bnxt_hwrm_func_qcfg(bp); + rc = bnxt_setup_vnic(bp, 0); if (rc) goto err_out; -- cgit 1.4.1 From 5ce24936d560f226899e3dbf01987668b39fac09 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Wed, 7 Jun 2023 00:54:07 -0700 Subject: bnxt_en: Skip firmware fatal error recovery if chip is not accessible [ Upstream commit 83474a9b252ab23e6003865c2775024344cb9c09 ] Driver starts firmware fatal error recovery by detecting heartbeat failure or fw reset count register changing. But these checks are not reliable if the device is not accessible. This can happen while DPC (Downstream Port containment) is in progress. Skip firmware fatal recovery if pci_device_is_present() returns false. Fixes: acfb50e4e773 ("bnxt_en: Add FW fatal devlink_health_reporter.") Reviewed-by: Somnath Kotur Reviewed-by: Pavan Chebbi Signed-off-by: Vikas Gupta Signed-off-by: Michael Chan Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 93c3b8316c46..f7b2c4e94e89 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11576,6 +11576,7 @@ static void bnxt_tx_timeout(struct net_device *dev, unsigned int txqueue) static void bnxt_fw_health_check(struct bnxt *bp) { struct bnxt_fw_health *fw_health = bp->fw_health; + struct pci_dev *pdev = bp->pdev; u32 val; if (!fw_health->enabled || test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) @@ -11589,7 +11590,7 @@ static void bnxt_fw_health_check(struct bnxt *bp) } val = bnxt_fw_health_readl(bp, BNXT_FW_HEARTBEAT_REG); - if (val == fw_health->last_fw_heartbeat) { + if (val == fw_health->last_fw_heartbeat && pci_device_is_present(pdev)) { fw_health->arrests++; goto fw_reset; } @@ -11597,7 +11598,7 @@ static void bnxt_fw_health_check(struct bnxt *bp) fw_health->last_fw_heartbeat = val; val = bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG); - if (val != fw_health->last_fw_reset_cnt) { + if (val != fw_health->last_fw_reset_cnt && pci_device_is_present(pdev)) { fw_health->discoveries++; goto fw_reset; } -- cgit 1.4.1 From abc7062313729743132a4dc60248d701a4ddbda2 Mon Sep 17 00:00:00 2001 From: Pavan Chebbi Date: Wed, 7 Jun 2023 00:54:08 -0700 Subject: bnxt_en: Prevent kernel panic when receiving unexpected PHC_UPDATE event [ Upstream commit 319a7827df9784048abe072afe6b4fb4501d8de4 ] The firmware can send PHC_RTC_UPDATE async event on a PF that may not have PTP registered. In such a case, there will be a null pointer deference for bp->ptp_cfg when we try to handle the event. Fix it by not registering for this event with the firmware if !bp->ptp_cfg. Also, check that bp->ptp_cfg is valid before proceeding when we receive the event. Fixes: 8bcf6f04d4a5 ("bnxt_en: Handle async event when the PHC is updated in RTC mode") Signed-off-by: Pavan Chebbi Signed-off-by: Michael Chan Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 6 ++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index f7b2c4e94e89..37a31f684938 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2389,6 +2389,9 @@ static int bnxt_async_event_process(struct bnxt *bp, struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; u64 ns; + if (!ptp) + goto async_event_process_exit; + spin_lock_bh(&ptp->ptp_lock); bnxt_ptp_update_current_time(bp); ns = (((u64)BNXT_EVENT_PHC_RTC_UPDATE(data1) << @@ -4787,6 +4790,9 @@ int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp, unsigned long *bmap, int bmap_size, if (event_id == ASYNC_EVENT_CMPL_EVENT_ID_ERROR_RECOVERY && !(bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY)) continue; + if (event_id == ASYNC_EVENT_CMPL_EVENT_ID_PHC_UPDATE && + !bp->ptp_cfg) + continue; __set_bit(bnxt_async_events_arr[i], async_events_bmap); } if (bmap && bmap_size) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 2132ce63193c..4faaa9a50f4b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -929,6 +929,7 @@ int bnxt_ptp_init(struct bnxt *bp, bool phc_cfg) } else { bnxt_ptp_timecounter_init(bp, true); } + bnxt_hwrm_func_drv_rgtr(bp, NULL, 0, true); ptp->ptp_info = bnxt_ptp_caps; if ((bp->fw_cap & BNXT_FW_CAP_PTP_PPS)) { -- cgit 1.4.1 From f9b9c8469621b3545634d1707c9c96ee4398d46f Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Wed, 7 Jun 2023 00:54:09 -0700 Subject: bnxt_en: Implement .set_port / .unset_port UDP tunnel callbacks [ Upstream commit 1eb4ef12591348c440ac9d6efcf7521e73cf2b10 ] As per the new udp tunnel framework, drivers which need to know the details of a port entry (i.e. port type) when it gets deleted should use the .set_port / .unset_port callbacks. Implementing the current .udp_tunnel_sync callback would mean that the deleted tunnel port entry would be all zeros. This used to work on older firmware because it would not check the input when deleting a tunnel port. With newer firmware, the delete will now fail and subsequent tunnel port allocation will fail as a result. Fixes: 442a35a5a7aa ("bnxt: convert to new udp_tunnel_nic infra") Reviewed-by: Kalesh Anakkur Purayil Signed-off-by: Somnath Kotur Signed-off-by: Michael Chan Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 37a31f684938..6469fb8a42a8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -13008,26 +13008,37 @@ static void bnxt_cfg_ntp_filters(struct bnxt *bp) #endif /* CONFIG_RFS_ACCEL */ -static int bnxt_udp_tunnel_sync(struct net_device *netdev, unsigned int table) +static int bnxt_udp_tunnel_set_port(struct net_device *netdev, unsigned int table, + unsigned int entry, struct udp_tunnel_info *ti) { struct bnxt *bp = netdev_priv(netdev); - struct udp_tunnel_info ti; unsigned int cmd; - udp_tunnel_nic_get_port(netdev, table, 0, &ti); - if (ti.type == UDP_TUNNEL_TYPE_VXLAN) + if (ti->type == UDP_TUNNEL_TYPE_VXLAN) cmd = TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN; else cmd = TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE; - if (ti.port) - return bnxt_hwrm_tunnel_dst_port_alloc(bp, ti.port, cmd); + return bnxt_hwrm_tunnel_dst_port_alloc(bp, ti->port, cmd); +} + +static int bnxt_udp_tunnel_unset_port(struct net_device *netdev, unsigned int table, + unsigned int entry, struct udp_tunnel_info *ti) +{ + struct bnxt *bp = netdev_priv(netdev); + unsigned int cmd; + + if (ti->type == UDP_TUNNEL_TYPE_VXLAN) + cmd = TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN; + else + cmd = TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE; return bnxt_hwrm_tunnel_dst_port_free(bp, cmd); } static const struct udp_tunnel_nic_info bnxt_udp_tunnels = { - .sync_table = bnxt_udp_tunnel_sync, + .set_port = bnxt_udp_tunnel_set_port, + .unset_port = bnxt_udp_tunnel_unset_port, .flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | UDP_TUNNEL_NIC_INFO_OPEN_ONLY, .tables = { -- cgit 1.4.1 From 32c2c234bc2f286ca33b4cb7df8b1b3d9f30046b Mon Sep 17 00:00:00 2001 From: Vladislav Efanov Date: Fri, 26 May 2023 19:16:32 +0300 Subject: batman-adv: Broken sync while rescheduling delayed work commit abac3ac97fe8734b620e7322a116450d7f90aa43 upstream. Syzkaller got a lot of crashes like: KASAN: use-after-free Write in *_timers* All of these crashes point to the same memory area: The buggy address belongs to the object at ffff88801f870000 which belongs to the cache kmalloc-8k of size 8192 The buggy address is located 5320 bytes inside of 8192-byte region [ffff88801f870000, ffff88801f872000) This area belongs to : batadv_priv->batadv_priv_dat->delayed_work->timer_list The reason for these issues is the lack of synchronization. Delayed work (batadv_dat_purge) schedules new timer/work while the device is being deleted. As the result new timer/delayed work is set after cancel_delayed_work_sync() was called. So after the device is freed the timer list contains pointer to already freed memory. Found by Linux Verification Center (linuxtesting.org) with syzkaller. Cc: stable@kernel.org Fixes: 2f1dfbe18507 ("batman-adv: Distributed ARP Table - implement local storage") Signed-off-by: Vladislav Efanov Acked-by: Sven Eckelmann Signed-off-by: Simon Wunderlich Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index fefb51a5f606..a0233252032d 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -101,7 +101,6 @@ static void batadv_dat_purge(struct work_struct *work); */ static void batadv_dat_start_timer(struct batadv_priv *bat_priv) { - INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge); queue_delayed_work(batadv_event_workqueue, &bat_priv->dat.work, msecs_to_jiffies(10000)); } @@ -819,6 +818,7 @@ int batadv_dat_init(struct batadv_priv *bat_priv) if (!bat_priv->dat.hash) return -ENOMEM; + INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge); batadv_dat_start_timer(bat_priv); batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1, -- cgit 1.4.1 From 00b59734f50421cd5e4d4b1fc06ee07961a6dd76 Mon Sep 17 00:00:00 2001 From: Ismael Ferreras Morezuelas Date: Tue, 23 May 2023 14:45:10 -0700 Subject: Input: xpad - delete a Razer DeathAdder mouse VID/PID entry commit feee70f4568650cf44c573488798ffc0a2faeea3 upstream. While doing my research to improve the xpad device names I noticed that the 1532:0037 VID/PID seems to be used by the DeathAdder 2013, so that Razer Sabertooth instance looked wrong and very suspect to me. I didn't see any mention in the official drivers, either. After doing more research, it turns out that the xpad list is used by many other projects (like Steam) as-is [1], this issue was reported [2] and Valve/Sam Lantinga fixed it [3]: [1]: https://github.com/libsdl-org/SDL/blob/dcc5eef0e2395854b254ea2873a4899edab347c6/src/joystick/controller_type.h#L251 [2]: https://steamcommunity.com/app/353380/discussions/0/1743392486228754770/ [3]: https://hg.libsdl.org/SDL/rev/29809f6f0271 (With multiple Internet users reporting similar issues, not linked here) After not being able to find the correct VID/PID combination anywhere on the Internet and not receiving any reply from Razer support I did some additional detective work, it seems like it presents itself as "Razer Sabertooth Gaming Controller (XBOX360)", code 1689:FE00. Leaving us with this: * Razer Sabertooth (1689:fe00) * Razer Sabertooth Elite (24c6:5d04) * Razer DeathAdder 2013 (1532:0037) [note: not a gamepad] So, to sum things up; remove this conflicting/duplicate entry: { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, As the real/correct one is already present there, even if the Internet as a whole insists on presenting it as the Razer Sabertooth Elite, which (by all accounts) is not: { 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 }, Actual change in SDL2 referencing this kernel issue: https://github.com/libsdl-org/SDL/commit/e5e54169754ca5d3e86339d968b20126d9da0a15 For more information of the device, take a look here: https://github.com/xboxdrv/xboxdrv/pull/59 You can see a lsusb dump here: https://github.com/xboxdrv/xboxdrv/files/76581/Qa6dBcrv.txt Fixes: f554f619b70 ("Input: xpad - sync device IDs with xboxdrv") Signed-off-by: Ismael Ferreras Morezuelas Reviewed-by: Cameron Gutman Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/5c12dbdb-5774-fc68-5c58-ca596383663e@gmail.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/xpad.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index cd36cf716542..76cbcca13c9e 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -282,7 +282,6 @@ static const struct xpad_device { { 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 }, { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, { 0x146b, 0x0604, "Bigben Interactive DAIJA Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, - { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE }, { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, -- cgit 1.4.1 From f9172a0bb57d62a42122075a124c071a1c56e455 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 11 May 2023 12:08:37 -0700 Subject: Input: psmouse - fix OOB access in Elantech protocol commit 7b63a88bb62ba2ddf5fcd956be85fe46624628b9 upstream. The kernel only allocate 5 MT slots; check that transmitted slot ID falls within the acceptable range. Link: https://lore.kernel.org/r/ZFnEL91nrT789dbG@google.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/elantech.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index ece97f8c6a3e..2118b2075f43 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -674,10 +674,11 @@ static void process_packet_head_v4(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; unsigned char *packet = psmouse->packet; - int id = ((packet[3] & 0xe0) >> 5) - 1; + int id; int pres, traces; - if (id < 0) + id = ((packet[3] & 0xe0) >> 5) - 1; + if (id < 0 || id >= ETP_MAX_FINGERS) return; etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2]; @@ -707,7 +708,7 @@ static void process_packet_motion_v4(struct psmouse *psmouse) int id, sid; id = ((packet[0] & 0xe0) >> 5) - 1; - if (id < 0) + if (id < 0 || id >= ETP_MAX_FINGERS) return; sid = ((packet[3] & 0xe0) >> 5) - 1; @@ -728,7 +729,7 @@ static void process_packet_motion_v4(struct psmouse *psmouse) input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); - if (sid >= 0) { + if (sid >= 0 && sid < ETP_MAX_FINGERS) { etd->mt[sid].x += delta_x2 * weight; etd->mt[sid].y -= delta_y2 * weight; input_mt_slot(dev, sid); -- cgit 1.4.1 From c8a46f39dd2719c1044a2569a905f018f7a86b93 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 5 May 2023 11:16:07 -0700 Subject: Input: fix open count when closing inhibited device commit 978134c4b192ed04ecf699be3e1b4d23b5d20457 upstream. Because the kernel increments device's open count in input_open_device() even if device is inhibited, the counter should always be decremented in input_close_device() to keep it balanced. Fixes: a181616487db ("Input: Add "inhibited" property") Reviewed-by: Peter Hutterer Link: https://lore.kernel.org/r/ZFFz0xAdPNSL3PT7@google.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/input.c b/drivers/input/input.c index ebb2b7f0f8ff..8b6a922f8470 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -701,7 +701,7 @@ void input_close_device(struct input_handle *handle) __input_release_device(handle); - if (!dev->inhibited && !--dev->users) { + if (!--dev->users && !dev->inhibited) { if (dev->poller) input_dev_poller_stop(dev->poller); if (dev->close) -- cgit 1.4.1 From be0b9b7a6d7757cba0edd68098932725fe722413 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 6 Jun 2023 11:38:55 +0200 Subject: ALSA: hda: Fix kctl->id initialization commit 5c219a340850233aecbb444af964653ecd3d1370 upstream. HD-audio core code replaces the kctl->id.index of SPDIF-related controls after assigning via snd_ctl_add(). This doesn't work any longer with the new Xarray lookup change. The change of the kctl->id content has to be done via snd_ctl_rename_id() helper, instead. Fixes: c27e1efb61c5 ("ALSA: control: Use xarray for faster lookups") Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20230606093855.14685-5-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_codec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 9f79c0ac2bda..bd19f92aeeec 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2458,10 +2458,14 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, type == HDA_PCM_TYPE_HDMI) { /* suppose a single SPDIF device */ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { + struct snd_ctl_elem_id id; + kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0); if (!kctl) break; - kctl->id.index = spdif_index; + id = kctl->id; + id.index = spdif_index; + snd_ctl_rename_id(codec->card, &kctl->id, &id); } bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI; } -- cgit 1.4.1 From 1f6c520932bca5be9e8dec137fccb2fc094a80fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 6 Jun 2023 11:38:52 +0200 Subject: ALSA: ymfpci: Fix kctl->id initialization commit c9b83ae4a1609b1914ba7fc70826a3f3a8b234db upstream. ymfpci driver replaces the kctl->id.device after assigning the kctl via snd_ctl_add(). This doesn't work any longer with the new Xarray lookup change. It has to be set before snd_ctl_add() call instead. Fixes: c27e1efb61c5 ("ALSA: control: Use xarray for faster lookups") Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20230606093855.14685-2-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ymfpci/ymfpci_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index b492c32ce070..f629b3956a69 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1827,20 +1827,20 @@ int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch) if (snd_BUG_ON(!chip->pcm_spdif)) return -ENXIO; kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip); + kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; - kctl->id.device = chip->pcm_spdif->device; kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip); + kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; - kctl->id.device = chip->pcm_spdif->device; kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip); + kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; - kctl->id.device = chip->pcm_spdif->device; chip->spdif_pcm_ctl = kctl; /* direct recording source */ -- cgit 1.4.1 From c35034fd6446afaf408d5ab296068e32c775c965 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 6 Jun 2023 11:38:54 +0200 Subject: ALSA: gus: Fix kctl->id initialization commit c5ae57b1bb99bd6f50b90428fabde397c2aeba0f upstream. GUS driver replaces the kctl->id.index after assigning the kctl via snd_ctl_add(). This doesn't work any longer with the new Xarray lookup change. It has to be set before snd_ctl_add() call instead. Fixes: c27e1efb61c5 ("ALSA: control: Use xarray for faster lookups") Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20230606093855.14685-4-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/isa/gus/gus_pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 230f65a0e4b0..388db5fb65bd 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -892,10 +892,10 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus); else kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus); + kctl->id.index = control_index; err = snd_ctl_add(card, kctl); if (err < 0) return err; - kctl->id.index = control_index; return 0; } -- cgit 1.4.1 From 3454490e0396191f8f9c215fccf5deef76abffb5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 6 Jun 2023 11:38:53 +0200 Subject: ALSA: cmipci: Fix kctl->id initialization commit f2f312ad88c68a7f4a7789b9269ae33af3c7c7e9 upstream. cmipci driver replaces the kctl->id.device after assigning the kctl via snd_ctl_add(). This doesn't work any longer with the new Xarray lookup change. It has to be set before snd_ctl_add() call instead. Fixes: c27e1efb61c5 ("ALSA: control: Use xarray for faster lookups") Cc: Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20230606093855.14685-3-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/cmipci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 727db6d43391..6d25c12d9ef0 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2688,20 +2688,20 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) } if (cm->can_ac3_hw) { kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm); + kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) return err; - kctl->id.device = pcm_spdif_device; kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm); + kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) return err; - kctl->id.device = pcm_spdif_device; kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm); + kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) return err; - kctl->id.device = pcm_spdif_device; } if (cm->chip_version <= 37) { sw = snd_cmipci_old_mixer_switches; -- cgit 1.4.1 From caad8a0a10c348975cf2ab5067ba7ffbd9b737e6 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Mon, 5 Jun 2023 10:38:34 -0600 Subject: ALSA: hda/realtek: Add quirk for Clevo NS50AU commit da209f7a80dd633a32cbcbafe9e9f778933119c1 upstream. Fixes headset detection on Clevo NS50AU. Signed-off-by: Tim Crawford Cc: Link: https://lore.kernel.org/r/20230605163834.24653-1-tcrawford@system76.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7b5f194513c7..d2bd37e447bb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9636,6 +9636,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), -- cgit 1.4.1 From 0df0097ea2d52401c31e550389ac758c90e6a1eb Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 6 Jun 2023 09:31:22 +0200 Subject: ALSA: ice1712,ice1724: fix the kcontrol->id initialization commit b9a4efd61b6b9f62f83752959e75a5dae20624fa upstream. The new xarray lookup code requires to know complete kcontrol->id before snd_ctl_add() call. Reorder the code to make the initialization properly. Cc: stable@kernel.org # v5.19+ Reported-by: Martin Zidek Signed-off-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20230606073122.597491-1-perex@perex.cz Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ice1712/aureon.c | 7 ++++--- sound/pci/ice1712/ice1712.c | 14 +++++++++----- sound/pci/ice1712/ice1724.c | 16 ++++++++++------ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 40a0e0095030..4b59f94ec348 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -1903,11 +1903,12 @@ static int aureon_add_controls(struct snd_ice1712 *ice) else { for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) { struct snd_kcontrol *kctl; - err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice))); - if (err < 0) - return err; + kctl = snd_ctl_new1(&cs8415_controls[i], ice); if (i > 1) kctl->id.device = ice->pcm->device; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; } } } diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index a5241a287851..3b0c3e70987b 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2371,22 +2371,26 @@ int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice) if (snd_BUG_ON(!ice->pcm_pro)) return -EIO; - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice)); + kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice); + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; + kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice); kctl->id.device = ice->pcm_pro->device; - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice)); + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; + kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice); kctl->id.device = ice->pcm_pro->device; - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice)); + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; + kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice); kctl->id.device = ice->pcm_pro->device; - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice)); + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; - kctl->id.device = ice->pcm_pro->device; ice->spdif.stream_ctl = kctl; return 0; } diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 6fab2ad85bbe..1dc776acd637 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2392,23 +2392,27 @@ static int snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice) if (err < 0) return err; - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice)); + kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice); + kctl->id.device = ice->pcm->device; + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; + kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice); kctl->id.device = ice->pcm->device; - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice)); + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; + kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice); kctl->id.device = ice->pcm->device; - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice)); + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; - kctl->id.device = ice->pcm->device; #if 0 /* use default only */ - err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice)); + kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice); + kctl->id.device = ice->pcm->device; + err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; - kctl->id.device = ice->pcm->device; ice->spdif.stream_ctl = kctl; #endif return 0; -- cgit 1.4.1 From 63211350630b5b3dbcf2fe86add704eda40fccf8 Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Fri, 26 May 2023 17:47:04 +0800 Subject: ALSA: hda/realtek: Add a quirk for HP Slim Desktop S01 commit 527c356b51f3ddee02c9ed5277538f85e30a2cdc upstream. Add a quirk for HP Slim Desktop S01 to fixup headset MIC no presence. Signed-off-by: Ai Chao Cc: Link: https://lore.kernel.org/r/20230526094704.14597-1-aichao@kylinos.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d2bd37e447bb..8a1db465270a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11695,6 +11695,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB), SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB), SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), -- cgit 1.4.1 From b1acff11b674a3abc077b9b5e604aed8c00089a2 Mon Sep 17 00:00:00 2001 From: RenHai Date: Fri, 2 Jun 2023 08:36:04 +0800 Subject: ALSA: hda/realtek: Add Lenovo P3 Tower platform commit 7ca4c8d4d3f41c2cd9b4cf22bb829bf03dac0956 upstream. Headset microphone on this platform does not work without ALC897_FIXUP_HEADSET_MIC_PIN fixup. Signed-off-by: RenHai Cc: Link: https://lore.kernel.org/r/20230602003604.975892-1-kean0048@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8a1db465270a..c7e873c8835c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11717,6 +11717,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS), SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x1064, "Lenovo P3 Tower", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), -- cgit 1.4.1 From b40b34913280c957433ba90d2f53e71a9c2bc940 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Mon, 5 Jun 2023 16:33:08 +0100 Subject: ALSA: hda/realtek: Add quirks for Asus ROG 2024 laptops using CS35L41 commit 811dd426a9b16cf61a86fdb12d5f5b983cbfb130 upstream. Add support for Asus ROG 2024 models using CS35L41 SPI with Internal Boost. Signed-off-by: Stefan Binding Cc: Link: https://lore.kernel.org/r/20230605153308.448550-1-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c7e873c8835c..48a0e87136f1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9547,6 +9547,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), @@ -9565,6 +9566,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), + SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), -- cgit 1.4.1 From 8d42c563e4ddbe2d0d71249f137b02daa66972db Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Fri, 26 May 2023 14:41:38 +0200 Subject: drm/i915/gt: Use the correct error value when kernel_context() fails commit 40023959dbab3c6ad56fa7213770e63d197b69fb upstream. kernel_context() returns an error pointer. Use pointer-error conversion functions to evaluate its return value, rather than checking for a '0' return. Fixes: eb5c10cbbc2f ("drm/i915: Remove I915_USER_PRIORITY_SHIFT") Reported-by: Dan Carpenter Signed-off-by: Andi Shyti Cc: Chris Wilson Cc: # v5.13+ Reviewed-by: Andrzej Hajda Acked-by: Tejas Upadhyay Link: https://patchwork.freedesktop.org/patch/msgid/20230526124138.2006110-1-andi.shyti@linux.intel.com (cherry picked from commit edad9ee94f17adc75d3b13ab51bbe3d615ce1e7e) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/gt/selftest_execlists.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/selftest_execlists.c b/drivers/gpu/drm/i915/gt/selftest_execlists.c index 6312f42daef4..b370411d4362 100644 --- a/drivers/gpu/drm/i915/gt/selftest_execlists.c +++ b/drivers/gpu/drm/i915/gt/selftest_execlists.c @@ -1532,8 +1532,8 @@ static int live_busywait_preempt(void *arg) struct drm_i915_gem_object *obj; struct i915_vma *vma; enum intel_engine_id id; - int err = -ENOMEM; u32 *map; + int err; /* * Verify that even without HAS_LOGICAL_RING_PREEMPTION, we can @@ -1541,13 +1541,17 @@ static int live_busywait_preempt(void *arg) */ ctx_hi = kernel_context(gt->i915, NULL); - if (!ctx_hi) - return -ENOMEM; + if (IS_ERR(ctx_hi)) + return PTR_ERR(ctx_hi); + ctx_hi->sched.priority = I915_CONTEXT_MAX_USER_PRIORITY; ctx_lo = kernel_context(gt->i915, NULL); - if (!ctx_lo) + if (IS_ERR(ctx_lo)) { + err = PTR_ERR(ctx_lo); goto err_ctx_hi; + } + ctx_lo->sched.priority = I915_CONTEXT_MIN_USER_PRIORITY; obj = i915_gem_object_create_internal(gt->i915, PAGE_SIZE); -- cgit 1.4.1 From 416ba40ff3c4ecafbed19029c9e9884a7fee3da5 Mon Sep 17 00:00:00 2001 From: Evan Quan Date: Thu, 6 Apr 2023 12:08:21 +0800 Subject: drm/amd/pm: conditionally disable pcie lane switching for some sienna_cichlid SKUs commit 38e4ced804796c5725e2a52ec3601951552c4a97 upstream. Disable the pcie lane switching for some sienna_cichlid SKUs since it might not work well on some platforms. Signed-off-by: Evan Quan Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c | 92 +++++++++++++++++----- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index 75f18681e984..85d53597eb07 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -2067,33 +2067,94 @@ static int sienna_cichlid_display_disable_memory_clock_switch(struct smu_context return ret; } +static void sienna_cichlid_get_override_pcie_settings(struct smu_context *smu, + uint32_t *gen_speed_override, + uint32_t *lane_width_override) +{ + struct amdgpu_device *adev = smu->adev; + + *gen_speed_override = 0xff; + *lane_width_override = 0xff; + + switch (adev->pdev->device) { + case 0x73A0: + case 0x73A1: + case 0x73A2: + case 0x73A3: + case 0x73AB: + case 0x73AE: + /* Bit 7:0: PCIE lane width, 1 to 7 corresponds is x1 to x32 */ + *lane_width_override = 6; + break; + case 0x73E0: + case 0x73E1: + case 0x73E3: + *lane_width_override = 4; + break; + case 0x7420: + case 0x7421: + case 0x7422: + case 0x7423: + case 0x7424: + *lane_width_override = 3; + break; + default: + break; + } +} + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + static int sienna_cichlid_update_pcie_parameters(struct smu_context *smu, uint32_t pcie_gen_cap, uint32_t pcie_width_cap) { struct smu_11_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context; - - uint32_t smu_pcie_arg; + struct smu_11_0_pcie_table *pcie_table = &dpm_context->dpm_tables.pcie_table; + uint32_t gen_speed_override, lane_width_override; uint8_t *table_member1, *table_member2; + uint32_t min_gen_speed, max_gen_speed; + uint32_t min_lane_width, max_lane_width; + uint32_t smu_pcie_arg; int ret, i; GET_PPTABLE_MEMBER(PcieGenSpeed, &table_member1); GET_PPTABLE_MEMBER(PcieLaneCount, &table_member2); - /* lclk dpm table setup */ - for (i = 0; i < MAX_PCIE_CONF; i++) { - dpm_context->dpm_tables.pcie_table.pcie_gen[i] = table_member1[i]; - dpm_context->dpm_tables.pcie_table.pcie_lane[i] = table_member2[i]; + sienna_cichlid_get_override_pcie_settings(smu, + &gen_speed_override, + &lane_width_override); + + /* PCIE gen speed override */ + if (gen_speed_override != 0xff) { + min_gen_speed = MIN(pcie_gen_cap, gen_speed_override); + max_gen_speed = MIN(pcie_gen_cap, gen_speed_override); + } else { + min_gen_speed = MAX(0, table_member1[0]); + max_gen_speed = MIN(pcie_gen_cap, table_member1[1]); + min_gen_speed = min_gen_speed > max_gen_speed ? + max_gen_speed : min_gen_speed; } + pcie_table->pcie_gen[0] = min_gen_speed; + pcie_table->pcie_gen[1] = max_gen_speed; + + /* PCIE lane width override */ + if (lane_width_override != 0xff) { + min_lane_width = MIN(pcie_width_cap, lane_width_override); + max_lane_width = MIN(pcie_width_cap, lane_width_override); + } else { + min_lane_width = MAX(1, table_member2[0]); + max_lane_width = MIN(pcie_width_cap, table_member2[1]); + min_lane_width = min_lane_width > max_lane_width ? + max_lane_width : min_lane_width; + } + pcie_table->pcie_lane[0] = min_lane_width; + pcie_table->pcie_lane[1] = max_lane_width; for (i = 0; i < NUM_LINK_LEVELS; i++) { - smu_pcie_arg = (i << 16) | - ((table_member1[i] <= pcie_gen_cap) ? - (table_member1[i] << 8) : - (pcie_gen_cap << 8)) | - ((table_member2[i] <= pcie_width_cap) ? - table_member2[i] : - pcie_width_cap); + smu_pcie_arg = (i << 16 | + pcie_table->pcie_gen[i] << 8 | + pcie_table->pcie_lane[i]); ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_OverridePcieParameters, @@ -2101,11 +2162,6 @@ static int sienna_cichlid_update_pcie_parameters(struct smu_context *smu, NULL); if (ret) return ret; - - if (table_member1[i] > pcie_gen_cap) - dpm_context->dpm_tables.pcie_table.pcie_gen[i] = pcie_gen_cap; - if (table_member2[i] > pcie_width_cap) - dpm_context->dpm_tables.pcie_table.pcie_lane[i] = pcie_width_cap; } return 0; -- cgit 1.4.1 From 34419aa0b448b0eee941102793893e0e256abda1 Mon Sep 17 00:00:00 2001 From: Chia-I Wu Date: Thu, 1 Jun 2023 14:48:08 -0700 Subject: drm/amdgpu: fix xclk freq on CHIP_STONEY commit b447b079cf3a9971ea4d31301e673f49612ccc18 upstream. According to Alex, most APUs from that time seem to have the same issue (vbios says 48Mhz, actual is 100Mhz). I only have a CHIP_STONEY so I limit the fixup to CHIP_STONEY Signed-off-by: Chia-I Wu Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/vi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index d6c37c90c628..53a3bb7fc9c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -542,8 +542,15 @@ static u32 vi_get_xclk(struct amdgpu_device *adev) u32 reference_clock = adev->clock.spll.reference_freq; u32 tmp; - if (adev->flags & AMD_IS_APU) - return reference_clock; + if (adev->flags & AMD_IS_APU) { + switch (adev->asic_type) { + case CHIP_STONEY: + /* vbios says 48Mhz, but the actual freq is 100Mhz */ + return 10000; + default: + return reference_clock; + } + } tmp = RREG32_SMC(ixCG_CLKPIN_CNTL_2); if (REG_GET_FIELD(tmp, CG_CLKPIN_CNTL_2, MUX_TCLK_TO_XCLK)) -- cgit 1.4.1 From 8e143bae25cdbab69ea3e2e7bb6faf7711ce7583 Mon Sep 17 00:00:00 2001 From: YiPeng Chai Date: Wed, 24 May 2023 17:14:15 +0800 Subject: drm/amdgpu: change reserved vram info print MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit dac652220ba0e5a2ef2da2a47a60b60aea333fdb upstream. The link object of mgr->reserved_pages is the blocks variable in struct amdgpu_vram_reservation, not the link variable in struct drm_buddy_block. Signed-off-by: YiPeng Chai Reviewed-by: Arunpravin Paneer Selvam Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 75c80c557b6e..16594a0a6d18 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -799,7 +799,7 @@ static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man, { struct amdgpu_vram_mgr *mgr = to_vram_mgr(man); struct drm_buddy *mm = &mgr->mm; - struct drm_buddy_block *block; + struct amdgpu_vram_reservation *rsv; drm_printf(printer, " vis usage:%llu\n", amdgpu_vram_mgr_vis_usage(mgr)); @@ -811,8 +811,9 @@ static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man, drm_buddy_print(mm, printer); drm_printf(printer, "reserved:\n"); - list_for_each_entry(block, &mgr->reserved_pages, link) - drm_buddy_block_print(mm, block, printer); + list_for_each_entry(rsv, &mgr->reserved_pages, blocks) + drm_printf(printer, "%#018llx-%#018llx: %llu\n", + rsv->start, rsv->start + rsv->size, rsv->size); mutex_unlock(&mgr->lock); } -- cgit 1.4.1 From 8695a443ad8f38e239b2080f02c4d739d6e33686 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Fri, 31 Mar 2023 16:30:01 +0530 Subject: drm/amd/pm: Fix power context allocation in SMU13 commit 1d13c49cf4e246b218d71873f1bb1bbd376aa10e upstream. Use the right data structure for allocation. Signed-off-by: Lijo Lazar Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c index 479cbf05c331..5143b4df2cc1 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c @@ -582,11 +582,11 @@ int smu_v13_0_init_power(struct smu_context *smu) if (smu_power->power_context || smu_power->power_context_size != 0) return -EINVAL; - smu_power->power_context = kzalloc(sizeof(struct smu_13_0_dpm_context), + smu_power->power_context = kzalloc(sizeof(struct smu_13_0_power_context), GFP_KERNEL); if (!smu_power->power_context) return -ENOMEM; - smu_power->power_context_size = sizeof(struct smu_13_0_dpm_context); + smu_power->power_context_size = sizeof(struct smu_13_0_power_context); return 0; } -- cgit 1.4.1 From 8953be60ec33cba8289a855a886f64508f8c2c0c Mon Sep 17 00:00:00 2001 From: Alvin Lee Date: Fri, 19 May 2023 11:38:15 -0400 Subject: drm/amd/display: Reduce sdp bw after urgent to 90% commit e1a600208286c197c2696e51fc313e49889315bd upstream. [Description] Reduce expected SDP bandwidth due to poor QoS and arbitration issues on high bandwidth configs Cc: Mario Limonciello Cc: Alex Deucher Cc: stable@vger.kernel.org Acked-by: Stylon Wang Signed-off-by: Alvin Lee Reviewed-by: Nevenko Stupar Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c index d2b184fdd7e0..f28caece5f90 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c @@ -137,7 +137,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_2_soc = { .urgent_out_of_order_return_per_channel_pixel_only_bytes = 4096, .urgent_out_of_order_return_per_channel_pixel_and_vm_bytes = 4096, .urgent_out_of_order_return_per_channel_vm_only_bytes = 4096, - .pct_ideal_sdp_bw_after_urgent = 100.0, + .pct_ideal_sdp_bw_after_urgent = 90.0, .pct_ideal_fabric_bw_after_urgent = 67.0, .pct_ideal_dram_sdp_bw_after_urgent_pixel_only = 20.0, .pct_ideal_dram_sdp_bw_after_urgent_pixel_and_vm = 60.0, // N/A, for now keep as is until DML implemented -- cgit 1.4.1 From 727964650a126ff1e7a91ceaac0bd8db93e88a49 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 2 Jun 2023 13:42:47 -0600 Subject: wifi: iwlwifi: mvm: Fix -Warray-bounds bug in iwl_mvm_wait_d3_notif() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7a4615b9a9da5225b22b36a20508555dd133ac24 upstream. kmemdup() at line 2735 is not duplicating enough memory for notif->tid_tear_down and notif->station_id. As it only duplicates 612 bytes: up to offsetofend(struct iwl_wowlan_info_notif, received_beacons), this is the range of [0, 612) bytes. 2735 notif = kmemdup(notif_v1, 2736 offsetofend(struct iwl_wowlan_info_notif, 2737 received_beacons), 2738 GFP_ATOMIC); which evidently does not cover bytes 612 and 613 for members tid_tear_down and station_id in struct iwl_wowlan_info_notif. See below: $ pahole -C iwl_wowlan_info_notif drivers/net/wireless/intel/iwlwifi/mvm/d3.o struct iwl_wowlan_info_notif { struct iwl_wowlan_gtk_status_v3 gtk[2]; /* 0 488 */ /* --- cacheline 7 boundary (448 bytes) was 40 bytes ago --- */ struct iwl_wowlan_igtk_status igtk[2]; /* 488 80 */ /* --- cacheline 8 boundary (512 bytes) was 56 bytes ago --- */ __le64 replay_ctr; /* 568 8 */ /* --- cacheline 9 boundary (576 bytes) --- */ __le16 pattern_number; /* 576 2 */ __le16 reserved1; /* 578 2 */ __le16 qos_seq_ctr[8]; /* 580 16 */ __le32 wakeup_reasons; /* 596 4 */ __le32 num_of_gtk_rekeys; /* 600 4 */ __le32 transmitted_ndps; /* 604 4 */ __le32 received_beacons; /* 608 4 */ u8 tid_tear_down; /* 612 1 */ u8 station_id; /* 613 1 */ u8 reserved2[2]; /* 614 2 */ /* size: 616, cachelines: 10, members: 13 */ /* last cacheline: 40 bytes */ }; Therefore, when the following assignments take place, actually no memory has been allocated for those objects: 2743 notif->tid_tear_down = notif_v1->tid_tear_down; 2744 notif->station_id = notif_v1->station_id; Fix this by allocating space for the whole notif object and zero out the remaining space in memory after member station_id. This also fixes the following -Warray-bounds issues: CC drivers/net/wireless/intel/iwlwifi/mvm/d3.o drivers/net/wireless/intel/iwlwifi/mvm/d3.c: In function ‘iwl_mvm_wait_d3_notif’: drivers/net/wireless/intel/iwlwifi/mvm/d3.c:2743:30: warning: array subscript ‘struct iwl_wowlan_info_notif[0]’ is partly outside array bounds of ‘unsigned char[612]’ [-Warray-bounds=] 2743 | notif->tid_tear_down = notif_v1->tid_tear_down; | from drivers/net/wireless/intel/iwlwifi/mvm/d3.c:7: In function ‘kmemdup’, inlined from ‘iwl_mvm_wait_d3_notif’ at drivers/net/wireless/intel/iwlwifi/mvm/d3.c:2735:12: include/linux/fortify-string.h:765:16: note: object of size 612 allocated by ‘__real_kmemdup’ 765 | return __real_kmemdup(p, size, gfp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/net/wireless/intel/iwlwifi/mvm/d3.c: In function ‘iwl_mvm_wait_d3_notif’: drivers/net/wireless/intel/iwlwifi/mvm/d3.c:2744:30: warning: array subscript ‘struct iwl_wowlan_info_notif[0]’ is partly outside array bounds of ‘unsigned char[612]’ [-Warray-bounds=] 2744 | notif->station_id = notif_v1->station_id; | ^~ In function ‘kmemdup’, inlined from ‘iwl_mvm_wait_d3_notif’ at drivers/net/wireless/intel/iwlwifi/mvm/d3.c:2735:12: include/linux/fortify-string.h:765:16: note: object of size 612 allocated by ‘__real_kmemdup’ 765 | return __real_kmemdup(p, size, gfp); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ Link: https://github.com/KSPP/linux/issues/306 Fixes: 905d50ddbc83 ("wifi: iwlwifi: mvm: support wowlan info notification version 2") Cc: stable@vger.kernel.org Signed-off-by: Gustavo A. R. Silva Acked-by: Gregory Greenman Link: https://lore.kernel.org/r/ZHpGN555FwAKGduH@work Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index c876e81437fe..2748459d1227 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2724,17 +2724,13 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, if (wowlan_info_ver < 2) { struct iwl_wowlan_info_notif_v1 *notif_v1 = (void *)pkt->data; - notif = kmemdup(notif_v1, - offsetofend(struct iwl_wowlan_info_notif, - received_beacons), - GFP_ATOMIC); - + notif = kmemdup(notif_v1, sizeof(*notif), GFP_ATOMIC); if (!notif) return false; notif->tid_tear_down = notif_v1->tid_tear_down; notif->station_id = notif_v1->station_id; - + memset_after(notif, 0, station_id); } else { notif = (void *)pkt->data; } -- cgit 1.4.1 From db15e90a8c3b8a88c1e2dba2640f413d4e55406f Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 26 May 2023 10:19:46 +0200 Subject: can: j1939: j1939_sk_send_loop_abort(): improved error queue handling in J1939 Socket commit 2a84aea80e925ecba6349090559754f8e8eb68ef upstream. This patch addresses an issue within the j1939_sk_send_loop_abort() function in the j1939/socket.c file, specifically in the context of Transport Protocol (TP) sessions. Without this patch, when a TP session is initiated and a Clear To Send (CTS) frame is received from the remote side requesting one data packet, the kernel dispatches the first Data Transport (DT) frame and then waits for the next CTS. If the remote side doesn't respond with another CTS, the kernel aborts due to a timeout. This leads to the user-space receiving an EPOLLERR on the socket, and the socket becomes active. However, when trying to read the error queue from the socket with sock.recvmsg(, , socket.MSG_ERRQUEUE), it returns -EAGAIN, given that the socket is non-blocking. This situation results in an infinite loop: the user-space repeatedly calls epoll(), epoll() returns the socket file descriptor with EPOLLERR, but the socket then blocks on the recv() of ERRQUEUE. This patch introduces an additional check for the J1939_SOCK_ERRQUEUE flag within the j1939_sk_send_loop_abort() function. If the flag is set, it indicates that the application has subscribed to receive error queue messages. In such cases, the kernel can communicate the current transfer state via the error queue. This allows for the function to return early, preventing the unnecessary setting of the socket into an error state, and breaking the infinite loop. It is crucial to note that a socket error is only needed if the application isn't using the error queue, as, without it, the application wouldn't be aware of transfer issues. Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Reported-by: David Jander Tested-by: David Jander Signed-off-by: Oleksij Rempel Link: https://lore.kernel.org/r/20230526081946.715190-1-o.rempel@pengutronix.de Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/j1939/socket.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index 4fb3a99b5f67..9c828067b448 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -1088,6 +1088,11 @@ void j1939_sk_errqueue(struct j1939_session *session, void j1939_sk_send_loop_abort(struct sock *sk, int err) { + struct j1939_sock *jsk = j1939_sk(sk); + + if (jsk->state & J1939_SOCK_ERRQUEUE) + return; + sk->sk_err = err; sk_error_report(sk); -- cgit 1.4.1 From 8a46c4a2bcd7931e6edd7d57b48f0c7498f3f206 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Fri, 26 May 2023 20:19:09 +0300 Subject: can: j1939: change j1939_netdev_lock type to mutex commit cd9c790de2088b0d797dc4d244b4f174f9962554 upstream. It turns out access to j1939_can_rx_register() needs to be serialized, otherwise j1939_priv can be corrupted when parallel threads call j1939_netdev_start() and j1939_can_rx_register() fails. This issue is thoroughly covered in other commit which serializes access to j1939_can_rx_register(). Change j1939_netdev_lock type to mutex so that we do not need to remove GFP_KERNEL from can_rx_register(). j1939_netdev_lock seems to be used in normal contexts where mutex usage is not prohibited. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Suggested-by: Alexey Khoroshilov Signed-off-by: Fedor Pchelkin Tested-by: Oleksij Rempel Acked-by: Oleksij Rempel Link: https://lore.kernel.org/r/20230526171910.227615-2-pchelkin@ispras.ru Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/j1939/main.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c index 821d4ff303b3..6ed79afe19a5 100644 --- a/net/can/j1939/main.c +++ b/net/can/j1939/main.c @@ -126,7 +126,7 @@ static void j1939_can_recv(struct sk_buff *iskb, void *data) #define J1939_CAN_ID CAN_EFF_FLAG #define J1939_CAN_MASK (CAN_EFF_FLAG | CAN_RTR_FLAG) -static DEFINE_SPINLOCK(j1939_netdev_lock); +static DEFINE_MUTEX(j1939_netdev_lock); static struct j1939_priv *j1939_priv_create(struct net_device *ndev) { @@ -220,7 +220,7 @@ static void __j1939_rx_release(struct kref *kref) j1939_can_rx_unregister(priv); j1939_ecu_unmap_all(priv); j1939_priv_set(priv->ndev, NULL); - spin_unlock(&j1939_netdev_lock); + mutex_unlock(&j1939_netdev_lock); } /* get pointer to priv without increasing ref counter */ @@ -248,9 +248,9 @@ static struct j1939_priv *j1939_priv_get_by_ndev(struct net_device *ndev) { struct j1939_priv *priv; - spin_lock(&j1939_netdev_lock); + mutex_lock(&j1939_netdev_lock); priv = j1939_priv_get_by_ndev_locked(ndev); - spin_unlock(&j1939_netdev_lock); + mutex_unlock(&j1939_netdev_lock); return priv; } @@ -260,14 +260,14 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) struct j1939_priv *priv, *priv_new; int ret; - spin_lock(&j1939_netdev_lock); + mutex_lock(&j1939_netdev_lock); priv = j1939_priv_get_by_ndev_locked(ndev); if (priv) { kref_get(&priv->rx_kref); - spin_unlock(&j1939_netdev_lock); + mutex_unlock(&j1939_netdev_lock); return priv; } - spin_unlock(&j1939_netdev_lock); + mutex_unlock(&j1939_netdev_lock); priv = j1939_priv_create(ndev); if (!priv) @@ -277,20 +277,20 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) spin_lock_init(&priv->j1939_socks_lock); INIT_LIST_HEAD(&priv->j1939_socks); - spin_lock(&j1939_netdev_lock); + mutex_lock(&j1939_netdev_lock); priv_new = j1939_priv_get_by_ndev_locked(ndev); if (priv_new) { /* Someone was faster than us, use their priv and roll * back our's. */ kref_get(&priv_new->rx_kref); - spin_unlock(&j1939_netdev_lock); + mutex_unlock(&j1939_netdev_lock); dev_put(ndev); kfree(priv); return priv_new; } j1939_priv_set(ndev, priv); - spin_unlock(&j1939_netdev_lock); + mutex_unlock(&j1939_netdev_lock); ret = j1939_can_rx_register(priv); if (ret < 0) @@ -308,7 +308,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) void j1939_netdev_stop(struct j1939_priv *priv) { - kref_put_lock(&priv->rx_kref, __j1939_rx_release, &j1939_netdev_lock); + kref_put_mutex(&priv->rx_kref, __j1939_rx_release, &j1939_netdev_lock); j1939_priv_put(priv); } -- cgit 1.4.1 From e0b04a9f97dd52b22adaff7988c424c65a7d6d66 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Fri, 26 May 2023 20:19:10 +0300 Subject: can: j1939: avoid possible use-after-free when j1939_can_rx_register fails commit 9f16eb106aa5fce15904625661312623ec783ed3 upstream. Syzkaller reports the following failure: BUG: KASAN: use-after-free in kref_put include/linux/kref.h:64 [inline] BUG: KASAN: use-after-free in j1939_priv_put+0x25/0xa0 net/can/j1939/main.c:172 Write of size 4 at addr ffff888141c15058 by task swapper/3/0 CPU: 3 PID: 0 Comm: swapper/3 Not tainted 5.10.144-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x107/0x167 lib/dump_stack.c:118 print_address_description.constprop.0+0x1c/0x220 mm/kasan/report.c:385 __kasan_report mm/kasan/report.c:545 [inline] kasan_report.cold+0x1f/0x37 mm/kasan/report.c:562 check_memory_region_inline mm/kasan/generic.c:186 [inline] check_memory_region+0x145/0x190 mm/kasan/generic.c:192 instrument_atomic_read_write include/linux/instrumented.h:101 [inline] atomic_fetch_sub_release include/asm-generic/atomic-instrumented.h:220 [inline] __refcount_sub_and_test include/linux/refcount.h:272 [inline] __refcount_dec_and_test include/linux/refcount.h:315 [inline] refcount_dec_and_test include/linux/refcount.h:333 [inline] kref_put include/linux/kref.h:64 [inline] j1939_priv_put+0x25/0xa0 net/can/j1939/main.c:172 j1939_sk_sock_destruct+0x44/0x90 net/can/j1939/socket.c:374 __sk_destruct+0x4e/0x820 net/core/sock.c:1784 rcu_do_batch kernel/rcu/tree.c:2485 [inline] rcu_core+0xb35/0x1a30 kernel/rcu/tree.c:2726 __do_softirq+0x289/0x9a3 kernel/softirq.c:298 asm_call_irq_on_stack+0x12/0x20 __run_on_irqstack arch/x86/include/asm/irq_stack.h:26 [inline] run_on_irqstack_cond arch/x86/include/asm/irq_stack.h:77 [inline] do_softirq_own_stack+0xaa/0xe0 arch/x86/kernel/irq_64.c:77 invoke_softirq kernel/softirq.c:393 [inline] __irq_exit_rcu kernel/softirq.c:423 [inline] irq_exit_rcu+0x136/0x200 kernel/softirq.c:435 sysvec_apic_timer_interrupt+0x4d/0x100 arch/x86/kernel/apic/apic.c:1095 asm_sysvec_apic_timer_interrupt+0x12/0x20 arch/x86/include/asm/idtentry.h:635 Allocated by task 1141: kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48 kasan_set_track mm/kasan/common.c:56 [inline] __kasan_kmalloc.constprop.0+0xc9/0xd0 mm/kasan/common.c:461 kmalloc include/linux/slab.h:552 [inline] kzalloc include/linux/slab.h:664 [inline] j1939_priv_create net/can/j1939/main.c:131 [inline] j1939_netdev_start+0x111/0x860 net/can/j1939/main.c:268 j1939_sk_bind+0x8ea/0xd30 net/can/j1939/socket.c:485 __sys_bind+0x1f2/0x260 net/socket.c:1645 __do_sys_bind net/socket.c:1656 [inline] __se_sys_bind net/socket.c:1654 [inline] __x64_sys_bind+0x6f/0xb0 net/socket.c:1654 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x61/0xc6 Freed by task 1141: kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48 kasan_set_track+0x1c/0x30 mm/kasan/common.c:56 kasan_set_free_info+0x1b/0x30 mm/kasan/generic.c:355 __kasan_slab_free+0x112/0x170 mm/kasan/common.c:422 slab_free_hook mm/slub.c:1542 [inline] slab_free_freelist_hook+0xad/0x190 mm/slub.c:1576 slab_free mm/slub.c:3149 [inline] kfree+0xd9/0x3b0 mm/slub.c:4125 j1939_netdev_start+0x5ee/0x860 net/can/j1939/main.c:300 j1939_sk_bind+0x8ea/0xd30 net/can/j1939/socket.c:485 __sys_bind+0x1f2/0x260 net/socket.c:1645 __do_sys_bind net/socket.c:1656 [inline] __se_sys_bind net/socket.c:1654 [inline] __x64_sys_bind+0x6f/0xb0 net/socket.c:1654 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x61/0xc6 It can be caused by this scenario: CPU0 CPU1 j1939_sk_bind(socket0, ndev0, ...) j1939_netdev_start() j1939_sk_bind(socket1, ndev0, ...) j1939_netdev_start() mutex_lock(&j1939_netdev_lock) j1939_priv_set(ndev0, priv) mutex_unlock(&j1939_netdev_lock) if (priv_new) kref_get(&priv_new->rx_kref) return priv_new; /* inside j1939_sk_bind() */ jsk->priv = priv j1939_can_rx_register(priv) // fails j1939_priv_set(ndev, NULL) kfree(priv) j1939_sk_sock_destruct() j1939_priv_put() // <- uaf To avoid this, call j1939_can_rx_register() under j1939_netdev_lock so that a concurrent thread cannot process j1939_priv before j1939_can_rx_register() returns. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Signed-off-by: Fedor Pchelkin Tested-by: Oleksij Rempel Acked-by: Oleksij Rempel Link: https://lore.kernel.org/r/20230526171910.227615-3-pchelkin@ispras.ru Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/j1939/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c index 6ed79afe19a5..ecff1c947d68 100644 --- a/net/can/j1939/main.c +++ b/net/can/j1939/main.c @@ -290,16 +290,18 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) return priv_new; } j1939_priv_set(ndev, priv); - mutex_unlock(&j1939_netdev_lock); ret = j1939_can_rx_register(priv); if (ret < 0) goto out_priv_put; + mutex_unlock(&j1939_netdev_lock); return priv; out_priv_put: j1939_priv_set(ndev, NULL); + mutex_unlock(&j1939_netdev_lock); + dev_put(ndev); kfree(priv); -- cgit 1.4.1 From d80a36ad400e1bc69a50e25606d22d2025ecee3a Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 4 Jun 2023 20:25:17 -0700 Subject: mptcp: only send RM_ADDR in nl_cmd_remove commit 8b1c94da1e481090f24127b2c420b0c0b0421ce3 upstream. The specifications from [1] about the "REMOVE" command say: Announce that an address has been lost to the peer It was then only supposed to send a RM_ADDR and not trying to delete associated subflows. A new helper mptcp_pm_remove_addrs() is then introduced to do just that, compared to mptcp_pm_remove_addrs_and_subflows() also removing subflows. To delete a subflow, the userspace daemon can use the "SUB_DESTROY" command, see mptcp_nl_cmd_sf_destroy(). Fixes: d9a4594edabf ("mptcp: netlink: Add MPTCP_PM_CMD_REMOVE") Link: https://github.com/multipath-tcp/mptcp/blob/mptcp_v0.96/include/uapi/linux/mptcp.h [1] Cc: stable@vger.kernel.org Reviewed-by: Matthieu Baerts Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/mptcp/pm_netlink.c | 18 ++++++++++++++++++ net/mptcp/pm_userspace.c | 2 +- net/mptcp/protocol.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index 1c69e476f4ad..01d34ee4525e 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -1550,6 +1550,24 @@ static int mptcp_nl_cmd_del_addr(struct sk_buff *skb, struct genl_info *info) return ret; } +void mptcp_pm_remove_addrs(struct mptcp_sock *msk, struct list_head *rm_list) +{ + struct mptcp_rm_list alist = { .nr = 0 }; + struct mptcp_pm_addr_entry *entry; + + list_for_each_entry(entry, rm_list, list) { + remove_anno_list_by_saddr(msk, &entry->addr); + if (alist.nr < MPTCP_RM_IDS_MAX) + alist.ids[alist.nr++] = entry->addr.id; + } + + if (alist.nr) { + spin_lock_bh(&msk->pm.lock); + mptcp_pm_remove_addr(msk, &alist); + spin_unlock_bh(&msk->pm.lock); + } +} + void mptcp_pm_remove_addrs_and_subflows(struct mptcp_sock *msk, struct list_head *rm_list) { diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index c1d6cd5b188c..a0c1e321f2e9 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -231,7 +231,7 @@ int mptcp_nl_cmd_remove(struct sk_buff *skb, struct genl_info *info) list_move(&match->list, &free_list); - mptcp_pm_remove_addrs_and_subflows(msk, &free_list); + mptcp_pm_remove_addrs(msk, &free_list); release_sock((struct sock *)msk); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 55fc5e42082e..fc00dd587a29 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -820,6 +820,7 @@ int mptcp_pm_announce_addr(struct mptcp_sock *msk, bool echo); int mptcp_pm_remove_addr(struct mptcp_sock *msk, const struct mptcp_rm_list *rm_list); int mptcp_pm_remove_subflow(struct mptcp_sock *msk, const struct mptcp_rm_list *rm_list); +void mptcp_pm_remove_addrs(struct mptcp_sock *msk, struct list_head *rm_list); void mptcp_pm_remove_addrs_and_subflows(struct mptcp_sock *msk, struct list_head *rm_list); -- cgit 1.4.1 From 9b7fa33fda0fd29c8c329d9206186bbc9c449d37 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 4 Jun 2023 20:25:19 -0700 Subject: mptcp: add address into userspace pm list commit 24430f8bf51655c5ab7ddc2fafe939dd3cd0dd47 upstream. Add the address into userspace_pm_local_addr_list when the subflow is created. Make sure it can be found in mptcp_nl_cmd_remove(). And delete it in the new helper mptcp_userspace_pm_delete_local_addr(). By doing this, the "REMOVE" command also works with subflows that have been created via the "SUB_CREATE" command instead of restricting to the addresses that have been announced via the "ANNOUNCE" command. Fixes: d9a4594edabf ("mptcp: netlink: Add MPTCP_PM_CMD_REMOVE") Link: https://github.com/multipath-tcp/mptcp_net-next/issues/379 Cc: stable@vger.kernel.org Reviewed-by: Matthieu Baerts Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/mptcp/pm_userspace.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index a0c1e321f2e9..7726e089dfdf 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -78,6 +78,30 @@ int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, return ret; } +/* If the subflow is closed from the other peer (not via a + * subflow destroy command then), we want to keep the entry + * not to assign the same ID to another address and to be + * able to send RM_ADDR after the removal of the subflow. + */ +static int mptcp_userspace_pm_delete_local_addr(struct mptcp_sock *msk, + struct mptcp_pm_addr_entry *addr) +{ + struct mptcp_pm_addr_entry *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &msk->pm.userspace_pm_local_addr_list, list) { + if (mptcp_addresses_equal(&entry->addr, &addr->addr, false)) { + /* TODO: a refcount is needed because the entry can + * be used multiple times (e.g. fullmesh mode). + */ + list_del_rcu(&entry->list); + kfree(entry); + return 0; + } + } + + return -EINVAL; +} + int mptcp_userspace_pm_get_flags_and_ifindex_by_id(struct mptcp_sock *msk, unsigned int id, u8 *flags, int *ifindex) @@ -250,6 +274,7 @@ int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info) struct nlattr *raddr = info->attrs[MPTCP_PM_ATTR_ADDR_REMOTE]; struct nlattr *token = info->attrs[MPTCP_PM_ATTR_TOKEN]; struct nlattr *laddr = info->attrs[MPTCP_PM_ATTR_ADDR]; + struct mptcp_pm_addr_entry local = { 0 }; struct mptcp_addr_info addr_r; struct mptcp_addr_info addr_l; struct mptcp_sock *msk; @@ -301,12 +326,24 @@ int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info) goto create_err; } + local.addr = addr_l; + err = mptcp_userspace_pm_append_new_local_addr(msk, &local); + if (err < 0) { + GENL_SET_ERR_MSG(info, "did not match address and id"); + goto create_err; + } + lock_sock(sk); err = __mptcp_subflow_connect(sk, &addr_l, &addr_r); release_sock(sk); + spin_lock_bh(&msk->pm.lock); + if (err) + mptcp_userspace_pm_delete_local_addr(msk, &local); + spin_unlock_bh(&msk->pm.lock); + create_err: sock_put((struct sock *)msk); return err; @@ -419,7 +456,11 @@ int mptcp_nl_cmd_sf_destroy(struct sk_buff *skb, struct genl_info *info) ssk = mptcp_nl_find_ssk(msk, &addr_l, &addr_r); if (ssk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); + struct mptcp_pm_addr_entry entry = { .addr = addr_l }; + spin_lock_bh(&msk->pm.lock); + mptcp_userspace_pm_delete_local_addr(msk, &entry); + spin_unlock_bh(&msk->pm.lock); mptcp_subflow_shutdown(sk, ssk, RCV_SHUTDOWN | SEND_SHUTDOWN); mptcp_close_ssk(sk, ssk, subflow); MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RMSUBFLOW); -- cgit 1.4.1 From 3fa051b18fc61a23a6675bbb0a6277bcdaa8728b Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 4 Jun 2023 20:25:21 -0700 Subject: mptcp: update userspace pm infos commit 77e4b94a3de692a09b79945ecac5b8e6b77f10c1 upstream. Increase pm subflows counter on both server side and client side when userspace pm creates a new subflow, and decrease the counter when it closes a subflow. Increase add_addr_signaled counter in mptcp_nl_cmd_announce() when the address is announced by userspace PM. This modification is similar to how the in-kernel PM is updating the counter: when additional subflows are created/removed. Fixes: 9ab4807c84a4 ("mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE") Fixes: 702c2f646d42 ("mptcp: netlink: allow userspace-driven subflow establishment") Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/329 Cc: stable@vger.kernel.org Reviewed-by: Matthieu Baerts Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/mptcp/pm.c | 23 +++++++++++++++++++---- net/mptcp/pm_userspace.c | 5 +++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 70f0ced3ca86..10c288a0cb0c 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -87,8 +87,15 @@ bool mptcp_pm_allow_new_subflow(struct mptcp_sock *msk) unsigned int subflows_max; int ret = 0; - if (mptcp_pm_is_userspace(msk)) - return mptcp_userspace_pm_active(msk); + if (mptcp_pm_is_userspace(msk)) { + if (mptcp_userspace_pm_active(msk)) { + spin_lock_bh(&pm->lock); + pm->subflows++; + spin_unlock_bh(&pm->lock); + return true; + } + return false; + } subflows_max = mptcp_pm_get_subflows_max(msk); @@ -181,8 +188,16 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk, const struct sock *ssk, struct mptcp_pm_data *pm = &msk->pm; bool update_subflows; - update_subflows = (subflow->request_join || subflow->mp_join) && - mptcp_pm_is_kernel(msk); + update_subflows = subflow->request_join || subflow->mp_join; + if (mptcp_pm_is_userspace(msk)) { + if (update_subflows) { + spin_lock_bh(&pm->lock); + pm->subflows--; + spin_unlock_bh(&pm->lock); + } + return; + } + if (!READ_ONCE(pm->work_pending) && !update_subflows) return; diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 7726e089dfdf..8a2aa63caa51 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -69,6 +69,7 @@ int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, MPTCP_PM_MAX_ADDR_ID + 1, 1); list_add_tail_rcu(&e->list, &msk->pm.userspace_pm_local_addr_list); + msk->pm.local_addr_used++; ret = e->addr.id; } else if (match) { ret = entry->addr.id; @@ -95,6 +96,7 @@ static int mptcp_userspace_pm_delete_local_addr(struct mptcp_sock *msk, */ list_del_rcu(&entry->list); kfree(entry); + msk->pm.local_addr_used--; return 0; } } @@ -194,6 +196,7 @@ int mptcp_nl_cmd_announce(struct sk_buff *skb, struct genl_info *info) spin_lock_bh(&msk->pm.lock); if (mptcp_pm_alloc_anno_list(msk, &addr_val)) { + msk->pm.add_addr_signaled++; mptcp_pm_announce_addr(msk, &addr_val.addr, false); mptcp_pm_nl_addr_send_ack(msk); } @@ -342,6 +345,8 @@ int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info) spin_lock_bh(&msk->pm.lock); if (err) mptcp_userspace_pm_delete_local_addr(msk, &local); + else + msk->pm.subflows++; spin_unlock_bh(&msk->pm.lock); create_err: -- cgit 1.4.1 From 8f0ba8ec18f5ee25d0600a32eb3f767be3a6e1c3 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 4 Jun 2023 20:25:18 -0700 Subject: selftests: mptcp: update userspace pm addr tests commit 48d73f609dcceeb563b0d960e59bf0362581e39c upstream. This patch is linked to the previous commit ("mptcp: only send RM_ADDR in nl_cmd_remove"). To align with what is done by the in-kernel PM, update userspace pm addr selftests, by sending a remove_subflows command together after the remove_addrs command. Fixes: d9a4594edabf ("mptcp: netlink: Add MPTCP_PM_CMD_REMOVE") Fixes: 97040cf9806e ("selftests: mptcp: userspace pm address tests") Cc: stable@vger.kernel.org Reviewed-by: Matthieu Baerts Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index c7da2bd7c328..08214450f545 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -851,7 +851,15 @@ do_transfer() tk=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evts_ns1") ip netns exec ${listener_ns} ./pm_nl_ctl ann $addr token $tk id $id sleep 1 + sp=$(grep "type:10" "$evts_ns1" | + sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q') + da=$(grep "type:10" "$evts_ns1" | + sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q') + dp=$(grep "type:10" "$evts_ns1" | + sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q') ip netns exec ${listener_ns} ./pm_nl_ctl rem token $tk id $id + ip netns exec ${listener_ns} ./pm_nl_ctl dsf lip "::ffff:$addr" \ + lport $sp rip $da rport $dp token $tk fi counter=$((counter + 1)) -- cgit 1.4.1 From 443cf752f73bffe7b547b83fb75afd992a92d363 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 4 Jun 2023 20:25:20 -0700 Subject: selftests: mptcp: update userspace pm subflow tests commit 6c160b636c91e71e50c39134f78257cc35305ff0 upstream. To align with what is done by the in-kernel PM, update userspace pm subflow selftests, by sending the a remove_addrs command together before the remove_subflows command. This will get a RM_ADDR in chk_rm_nr(). Fixes: d9a4594edabf ("mptcp: netlink: Add MPTCP_PM_CMD_REMOVE") Fixes: 5e986ec46874 ("selftests: mptcp: userspace pm subflow tests") Link: https://github.com/multipath-tcp/mptcp_net-next/issues/379 Cc: stable@vger.kernel.org Reviewed-by: Matthieu Baerts Signed-off-by: Geliang Tang Signed-off-by: Mat Martineau Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 08214450f545..6be19b103824 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -925,6 +925,7 @@ do_transfer() sleep 1 sp=$(grep "type:10" "$evts_ns2" | sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q') + ip netns exec ${connector_ns} ./pm_nl_ctl rem token $tk id $id ip netns exec ${connector_ns} ./pm_nl_ctl dsf lip $addr lport $sp \ rip $da rport $dp token $tk fi @@ -3007,7 +3008,7 @@ userspace_tests() pm_nl_set_limits $ns1 0 1 run_tests $ns1 $ns2 10.0.1.1 0 0 userspace_1 slow chk_join_nr 1 1 1 - chk_rm_nr 0 1 + chk_rm_nr 1 1 fi } -- cgit 1.4.1 From 6f5c0eec89fc6f0c8ee6e8de8a72b7280837534f Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 1 Jun 2023 08:59:31 +0800 Subject: ceph: fix use-after-free bug for inodes when flushing capsnaps commit 409e873ea3c1fd3079909718bbeb06ac1ec7f38b upstream. There is a race between capsnaps flush and removing the inode from 'mdsc->snap_flush_list' list: == Thread A == == Thread B == ceph_queue_cap_snap() -> allocate 'capsnapA' ->ihold('&ci->vfs_inode') ->add 'capsnapA' to 'ci->i_cap_snaps' ->add 'ci' to 'mdsc->snap_flush_list' ... == Thread C == ceph_flush_snaps() ->__ceph_flush_snaps() ->__send_flush_snap() handle_cap_flushsnap_ack() ->iput('&ci->vfs_inode') this also will release 'ci' ... == Thread D == ceph_handle_snap() ->flush_snaps() ->iterate 'mdsc->snap_flush_list' ->get the stale 'ci' ->remove 'ci' from ->ihold(&ci->vfs_inode) this 'mdsc->snap_flush_list' will WARNING To fix this we will increase the inode's i_count ref when adding 'ci' to the 'mdsc->snap_flush_list' list. [ idryomov: need_put int -> bool ] Cc: stable@vger.kernel.org Link: https://bugzilla.redhat.com/show_bug.cgi?id=2209299 Signed-off-by: Xiubo Li Reviewed-by: Milind Changire Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- fs/ceph/caps.c | 6 ++++++ fs/ceph/snap.c | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index faf117802112..cdb26aadae12 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1626,6 +1626,7 @@ void ceph_flush_snaps(struct ceph_inode_info *ci, struct inode *inode = &ci->netfs.inode; struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; struct ceph_mds_session *session = NULL; + bool need_put = false; int mds; dout("ceph_flush_snaps %p\n", inode); @@ -1670,8 +1671,13 @@ out: ceph_put_mds_session(session); /* we flushed them all; remove this inode from the queue */ spin_lock(&mdsc->snap_flush_lock); + if (!list_empty(&ci->i_snap_flush_item)) + need_put = true; list_del_init(&ci->i_snap_flush_item); spin_unlock(&mdsc->snap_flush_lock); + + if (need_put) + iput(inode); } /* diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 0b236ebd989f..2e73ba62bd7a 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -693,8 +693,10 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci, capsnap->size); spin_lock(&mdsc->snap_flush_lock); - if (list_empty(&ci->i_snap_flush_item)) + if (list_empty(&ci->i_snap_flush_item)) { + ihold(inode); list_add_tail(&ci->i_snap_flush_item, &mdsc->snap_flush_list); + } spin_unlock(&mdsc->snap_flush_lock); return 1; /* caller may want to ceph_flush_snaps */ } -- cgit 1.4.1 From 1e1e2ee0cf7fae4d69583a86111e720ff9637ee7 Mon Sep 17 00:00:00 2001 From: Jan Höppner Date: Fri, 9 Jun 2023 17:37:50 +0200 Subject: s390/dasd: Use correct lock while counting channel queue length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ccc45cb4e7271c74dbb27776ae8f73d84557f5c6 upstream. The lock around counting the channel queue length in the BIODASDINFO ioctl was incorrectly changed to the dasd_block->queue_lock with commit 583d6535cb9d ("dasd: remove dead code"). This can lead to endless list iterations and a subsequent crash. The queue_lock is supposed to be used only for queue lists belonging to dasd_block. For dasd_device related queue lists the ccwdev lock must be used. Fix the mentioned issues by correctly using the ccwdev lock instead of the queue lock. Fixes: 583d6535cb9d ("dasd: remove dead code") Cc: stable@vger.kernel.org # v5.0+ Signed-off-by: Jan Höppner Reviewed-by: Stefan Haberland Signed-off-by: Stefan Haberland Link: https://lore.kernel.org/r/20230609153750.1258763-2-sth@linux.ibm.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/s390/block/dasd_ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 9327dcdd6e5e..8fca725b3dae 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -552,10 +552,10 @@ static int __dasd_ioctl_information(struct dasd_block *block, memcpy(dasd_info->type, base->discipline->name, 4); - spin_lock_irqsave(&block->queue_lock, flags); + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); list_for_each(l, &base->ccw_queue) dasd_info->chanq_len++; - spin_unlock_irqrestore(&block->queue_lock, flags); + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); return 0; } -- cgit 1.4.1 From a5490d6a742ca4550ad3f0f2f9e3529109f14625 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 30 May 2023 13:48:44 -0700 Subject: Bluetooth: Fix use-after-free in hci_remove_ltk/hci_remove_irk commit c5d2b6fa26b5b8386a9cc902cdece3a46bef2bd2 upstream. Similar to commit 0f7d9b31ce7a ("netfilter: nf_tables: fix use-after-free in nft_set_catchall_destroy()"). We can not access k after kfree_rcu() call. Cc: stable@vger.kernel.org Signed-off-by: Min Li Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hci_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ac36e7ae70b2..ca42129f8f91 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1416,10 +1416,10 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) { - struct smp_ltk *k; + struct smp_ltk *k, *tmp; int removed = 0; - list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { + list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type) continue; @@ -1435,9 +1435,9 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) { - struct smp_irk *k; + struct smp_irk *k, *tmp; - list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) { + list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) { if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type) continue; -- cgit 1.4.1 From e5ae01fd46a30a77c7e2a6fecff6d3acb9264ced Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 31 May 2023 10:57:58 +0200 Subject: Bluetooth: fix debugfs registration commit fe2ccc6c29d53e14d3c8b3ddf8ad965a92e074ee upstream. Since commit ec6cef9cd98d ("Bluetooth: Fix SMP channel registration for unconfigured controllers") the debugfs interface for unconfigured controllers will be created when the controller is configured. There is however currently nothing preventing a controller from being configured multiple time (e.g. setting the device address using btmgmt) which results in failed attempts to register the already registered debugfs entries: debugfs: File 'features' in directory 'hci0' already present! debugfs: File 'manufacturer' in directory 'hci0' already present! debugfs: File 'hci_version' in directory 'hci0' already present! ... debugfs: File 'quirk_simultaneous_discovery' in directory 'hci0' already present! Add a controller flag to avoid trying to register the debugfs interface more than once. Fixes: ec6cef9cd98d ("Bluetooth: Fix SMP channel registration for unconfigured controllers") Cc: stable@vger.kernel.org # 4.0 Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_sync.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index b350d92136c8..a674221d151d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -350,6 +350,7 @@ enum { enum { HCI_SETUP, HCI_CONFIG, + HCI_DEBUGFS_CREATED, HCI_AUTO_OFF, HCI_RFKILLED, HCI_MGMT, diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 40a6cfa2f9a0..1805ddee0cd0 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4492,6 +4492,9 @@ static int hci_init_sync(struct hci_dev *hdev) !hci_dev_test_flag(hdev, HCI_CONFIG)) return 0; + if (hci_dev_test_and_set_flag(hdev, HCI_DEBUGFS_CREATED)) + return 0; + hci_debugfs_create_common(hdev); if (lmp_bredr_capable(hdev)) -- cgit 1.4.1 From d088bea08ac0c904c90eddc5167a1f3e1e79bdb1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 31 May 2023 10:57:59 +0200 Subject: Bluetooth: hci_qca: fix debugfs registration commit 47c5d829a3e326b7395352a10fc8a6effe7afa15 upstream. Since commit 3e4be65eb82c ("Bluetooth: hci_qca: Add poweroff support during hci down for wcn3990"), the setup callback which registers the debugfs interface can be called multiple times. This specifically leads to the following error when powering on the controller: debugfs: Directory 'ibs' with parent 'hci0' already present! Add a driver flag to avoid trying to register the debugfs interface more than once. Fixes: 3e4be65eb82c ("Bluetooth: hci_qca: Add poweroff support during hci down for wcn3990") Cc: stable@vger.kernel.org # 4.20 Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_qca.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 7b9fd5f10433..45dffd2cbc71 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -78,7 +78,8 @@ enum qca_flags { QCA_HW_ERROR_EVENT, QCA_SSR_TRIGGERED, QCA_BT_OFF, - QCA_ROM_FW + QCA_ROM_FW, + QCA_DEBUGFS_CREATED, }; enum qca_capabilities { @@ -635,6 +636,9 @@ static void qca_debugfs_init(struct hci_dev *hdev) if (!hdev->debugfs) return; + if (test_and_set_bit(QCA_DEBUGFS_CREATED, &qca->flags)) + return; + ibs_dir = debugfs_create_dir("ibs", hdev->debugfs); /* read only */ -- cgit 1.4.1 From 939f00e4825deb9c45cf7b5148f2c9e6e36cf278 Mon Sep 17 00:00:00 2001 From: Rijo Thomas Date: Tue, 9 May 2023 13:02:40 +0530 Subject: tee: amdtee: Add return_origin to 'struct tee_cmd_load_ta' commit 436eeae0411acdfc54521ddea80ee76d4ae8a7ea upstream. After TEE has completed processing of TEE_CMD_ID_LOAD_TA, set proper value in 'return_origin' argument passed by open_session() call. To do so, add 'return_origin' field to the structure tee_cmd_load_ta. The Trusted OS shall update return_origin as part of TEE processing. This change to 'struct tee_cmd_load_ta' interface requires a similar update in AMD-TEE Trusted OS's TEE_CMD_ID_LOAD_TA interface. This patch has been verified on Phoenix Birman setup. On older APUs, return_origin value will be 0. Cc: stable@vger.kernel.org Fixes: 757cc3e9ff1d ("tee: add AMD-TEE driver") Tested-by: Sourabh Das Signed-off-by: Rijo Thomas Acked-by: Sumit Garg Signed-off-by: Jens Wiklander Signed-off-by: Greg Kroah-Hartman --- drivers/tee/amdtee/amdtee_if.h | 10 ++++++---- drivers/tee/amdtee/call.c | 30 +++++++++++++++++------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/drivers/tee/amdtee/amdtee_if.h b/drivers/tee/amdtee/amdtee_if.h index ff48c3e47375..e2014e21530a 100644 --- a/drivers/tee/amdtee/amdtee_if.h +++ b/drivers/tee/amdtee/amdtee_if.h @@ -118,16 +118,18 @@ struct tee_cmd_unmap_shared_mem { /** * struct tee_cmd_load_ta - load Trusted Application (TA) binary into TEE - * @low_addr: [in] bits [31:0] of the physical address of the TA binary - * @hi_addr: [in] bits [63:32] of the physical address of the TA binary - * @size: [in] size of TA binary in bytes - * @ta_handle: [out] return handle of the loaded TA + * @low_addr: [in] bits [31:0] of the physical address of the TA binary + * @hi_addr: [in] bits [63:32] of the physical address of the TA binary + * @size: [in] size of TA binary in bytes + * @ta_handle: [out] return handle of the loaded TA + * @return_origin: [out] origin of return code after TEE processing */ struct tee_cmd_load_ta { u32 low_addr; u32 hi_addr; u32 size; u32 ta_handle; + u32 return_origin; }; /** diff --git a/drivers/tee/amdtee/call.c b/drivers/tee/amdtee/call.c index cec6e70f0ac9..8a02c5fe33a6 100644 --- a/drivers/tee/amdtee/call.c +++ b/drivers/tee/amdtee/call.c @@ -423,19 +423,23 @@ int handle_load_ta(void *data, u32 size, struct tee_ioctl_open_session_arg *arg) if (ret) { arg->ret_origin = TEEC_ORIGIN_COMMS; arg->ret = TEEC_ERROR_COMMUNICATION; - } else if (arg->ret == TEEC_SUCCESS) { - ret = get_ta_refcount(load_cmd.ta_handle); - if (!ret) { - arg->ret_origin = TEEC_ORIGIN_COMMS; - arg->ret = TEEC_ERROR_OUT_OF_MEMORY; - - /* Unload the TA on error */ - unload_cmd.ta_handle = load_cmd.ta_handle; - psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA, - (void *)&unload_cmd, - sizeof(unload_cmd), &ret); - } else { - set_session_id(load_cmd.ta_handle, 0, &arg->session); + } else { + arg->ret_origin = load_cmd.return_origin; + + if (arg->ret == TEEC_SUCCESS) { + ret = get_ta_refcount(load_cmd.ta_handle); + if (!ret) { + arg->ret_origin = TEEC_ORIGIN_COMMS; + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + + /* Unload the TA on error */ + unload_cmd.ta_handle = load_cmd.ta_handle; + psp_tee_process_cmd(TEE_CMD_ID_UNLOAD_TA, + (void *)&unload_cmd, + sizeof(unload_cmd), &ret); + } else { + set_session_id(load_cmd.ta_handle, 0, &arg->session); + } } } mutex_unlock(&ta_refcount_mutex); -- cgit 1.4.1 From d647ee673c4d456b7bd7e0f0e3ef598437cb1489 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 5 Jun 2023 16:33:35 +0200 Subject: rbd: move RBD_OBJ_FLAG_COPYUP_ENABLED flag setting commit 09fe05c57b5aaf23e2c35036c98ea9f282b19a77 upstream. Move RBD_OBJ_FLAG_COPYUP_ENABLED flag setting into the object request state machine to allow for the snapshot context to be captured in the image request state machine rather than in rbd_queue_workfn(). Cc: stable@vger.kernel.org Signed-off-by: Ilya Dryomov Reviewed-by: Dongsheng Yang Signed-off-by: Greg Kroah-Hartman --- drivers/block/rbd.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 60aed196a2e5..6f03845b359e 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1334,14 +1334,28 @@ static bool rbd_obj_is_tail(struct rbd_obj_request *obj_req) /* * Must be called after rbd_obj_calc_img_extents(). */ -static bool rbd_obj_copyup_enabled(struct rbd_obj_request *obj_req) +static void rbd_obj_set_copyup_enabled(struct rbd_obj_request *obj_req) { - if (!obj_req->num_img_extents || - (rbd_obj_is_entire(obj_req) && - !obj_req->img_request->snapc->num_snaps)) - return false; + if (obj_req->img_request->op_type == OBJ_OP_DISCARD) { + dout("%s %p objno %llu discard\n", __func__, obj_req, + obj_req->ex.oe_objno); + return; + } - return true; + if (!obj_req->num_img_extents) { + dout("%s %p objno %llu not overlapping\n", __func__, obj_req, + obj_req->ex.oe_objno); + return; + } + + if (rbd_obj_is_entire(obj_req) && + !obj_req->img_request->snapc->num_snaps) { + dout("%s %p objno %llu entire\n", __func__, obj_req, + obj_req->ex.oe_objno); + return; + } + + obj_req->flags |= RBD_OBJ_FLAG_COPYUP_ENABLED; } static u64 rbd_obj_img_extents_bytes(struct rbd_obj_request *obj_req) @@ -2233,9 +2247,6 @@ static int rbd_obj_init_write(struct rbd_obj_request *obj_req) if (ret) return ret; - if (rbd_obj_copyup_enabled(obj_req)) - obj_req->flags |= RBD_OBJ_FLAG_COPYUP_ENABLED; - obj_req->write_state = RBD_OBJ_WRITE_START; return 0; } @@ -2341,8 +2352,6 @@ static int rbd_obj_init_zeroout(struct rbd_obj_request *obj_req) if (ret) return ret; - if (rbd_obj_copyup_enabled(obj_req)) - obj_req->flags |= RBD_OBJ_FLAG_COPYUP_ENABLED; if (!obj_req->num_img_extents) { obj_req->flags |= RBD_OBJ_FLAG_NOOP_FOR_NONEXISTENT; if (rbd_obj_is_entire(obj_req)) @@ -3287,6 +3296,7 @@ again: case RBD_OBJ_WRITE_START: rbd_assert(!*result); + rbd_obj_set_copyup_enabled(obj_req); if (rbd_obj_write_is_noop(obj_req)) return true; -- cgit 1.4.1 From 222a6bc8a7c60d19eb89cd2750236215791d5079 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 5 Jun 2023 16:33:35 +0200 Subject: rbd: get snapshot context after exclusive lock is ensured to be held commit 870611e4877eff1e8413c3fb92a585e45d5291f6 upstream. Move capturing the snapshot context into the image request state machine, after exclusive lock is ensured to be held for the duration of dealing with the image request. This is needed to ensure correctness of fast-diff states (OBJECT_EXISTS vs OBJECT_EXISTS_CLEAN) and object deltas computed based off of them. Otherwise the object map that is forked for the snapshot isn't guaranteed to accurately reflect the contents of the snapshot when the snapshot is taken under I/O. This breaks differential backup and snapshot-based mirroring use cases with fast-diff enabled: since some object deltas may be incomplete, the destination image may get corrupted. Cc: stable@vger.kernel.org Link: https://tracker.ceph.com/issues/61472 Signed-off-by: Ilya Dryomov Reviewed-by: Dongsheng Yang Signed-off-by: Greg Kroah-Hartman --- drivers/block/rbd.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 6f03845b359e..e009f480675d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1336,6 +1336,8 @@ static bool rbd_obj_is_tail(struct rbd_obj_request *obj_req) */ static void rbd_obj_set_copyup_enabled(struct rbd_obj_request *obj_req) { + rbd_assert(obj_req->img_request->snapc); + if (obj_req->img_request->op_type == OBJ_OP_DISCARD) { dout("%s %p objno %llu discard\n", __func__, obj_req, obj_req->ex.oe_objno); @@ -1456,6 +1458,7 @@ __rbd_obj_add_osd_request(struct rbd_obj_request *obj_req, static struct ceph_osd_request * rbd_obj_add_osd_request(struct rbd_obj_request *obj_req, int num_ops) { + rbd_assert(obj_req->img_request->snapc); return __rbd_obj_add_osd_request(obj_req, obj_req->img_request->snapc, num_ops); } @@ -1592,15 +1595,18 @@ static void rbd_img_request_init(struct rbd_img_request *img_request, mutex_init(&img_request->state_mutex); } +/* + * Only snap_id is captured here, for reads. For writes, snapshot + * context is captured in rbd_img_object_requests() after exclusive + * lock is ensured to be held. + */ static void rbd_img_capture_header(struct rbd_img_request *img_req) { struct rbd_device *rbd_dev = img_req->rbd_dev; lockdep_assert_held(&rbd_dev->header_rwsem); - if (rbd_img_is_write(img_req)) - img_req->snapc = ceph_get_snap_context(rbd_dev->header.snapc); - else + if (!rbd_img_is_write(img_req)) img_req->snap_id = rbd_dev->spec->snap_id; if (rbd_dev_parent_get(rbd_dev)) @@ -3483,9 +3489,19 @@ static int rbd_img_exclusive_lock(struct rbd_img_request *img_req) static void rbd_img_object_requests(struct rbd_img_request *img_req) { + struct rbd_device *rbd_dev = img_req->rbd_dev; struct rbd_obj_request *obj_req; rbd_assert(!img_req->pending.result && !img_req->pending.num_pending); + rbd_assert(!need_exclusive_lock(img_req) || + __rbd_is_lock_owner(rbd_dev)); + + if (rbd_img_is_write(img_req)) { + rbd_assert(!img_req->snapc); + down_read(&rbd_dev->header_rwsem); + img_req->snapc = ceph_get_snap_context(rbd_dev->header.snapc); + up_read(&rbd_dev->header_rwsem); + } for_each_obj_request(img_req, obj_req) { int result = 0; @@ -3503,7 +3519,6 @@ static void rbd_img_object_requests(struct rbd_img_request *img_req) static bool rbd_img_advance(struct rbd_img_request *img_req, int *result) { - struct rbd_device *rbd_dev = img_req->rbd_dev; int ret; again: @@ -3524,9 +3539,6 @@ again: if (*result) return true; - rbd_assert(!need_exclusive_lock(img_req) || - __rbd_is_lock_owner(rbd_dev)); - rbd_img_object_requests(img_req); if (!img_req->pending.num_pending) { *result = img_req->pending.result; @@ -3988,6 +4000,10 @@ static int rbd_post_acquire_action(struct rbd_device *rbd_dev) { int ret; + ret = rbd_dev_refresh(rbd_dev); + if (ret) + return ret; + if (rbd_dev->header.features & RBD_FEATURE_OBJECT_MAP) { ret = rbd_object_map_open(rbd_dev); if (ret) -- cgit 1.4.1 From 30c26b985c3eaee1488395e06983f2d9604f7f27 Mon Sep 17 00:00:00 2001 From: Brett Creeley Date: Mon, 5 Jun 2023 12:59:25 -0700 Subject: virtio_net: use control_buf for coalesce params commit accc1bf23068c1cdc4c2b015320ba856e210dd98 upstream. Commit 699b045a8e43 ("net: virtio_net: notifications coalescing support") added coalescing command support for virtio_net. However, the coalesce commands are using buffers on the stack, which is causing the device to see DMA errors. There should also be a complaint from check_for_stack() in debug_dma_map_xyz(). Fix this by adding and using coalesce params from the control_buf struct, which aligns with other commands. Cc: stable@vger.kernel.org Fixes: 699b045a8e43 ("net: virtio_net: notifications coalescing support") Reviewed-by: Shannon Nelson Signed-off-by: Allen Hubbe Signed-off-by: Brett Creeley Acked-by: Jason Wang Reviewed-by: Xuan Zhuo Acked-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20230605195925.51625-1-brett.creeley@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/virtio_net.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 47788f093551..fae302c5b0a9 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -200,6 +200,8 @@ struct control_buf { __virtio16 vid; __virtio64 offloads; struct virtio_net_ctrl_rss rss; + struct virtio_net_ctrl_coal_tx coal_tx; + struct virtio_net_ctrl_coal_rx coal_rx; }; struct virtnet_info { @@ -2786,12 +2788,10 @@ static int virtnet_send_notf_coal_cmds(struct virtnet_info *vi, struct ethtool_coalesce *ec) { struct scatterlist sgs_tx, sgs_rx; - struct virtio_net_ctrl_coal_tx coal_tx; - struct virtio_net_ctrl_coal_rx coal_rx; - coal_tx.tx_usecs = cpu_to_le32(ec->tx_coalesce_usecs); - coal_tx.tx_max_packets = cpu_to_le32(ec->tx_max_coalesced_frames); - sg_init_one(&sgs_tx, &coal_tx, sizeof(coal_tx)); + vi->ctrl->coal_tx.tx_usecs = cpu_to_le32(ec->tx_coalesce_usecs); + vi->ctrl->coal_tx.tx_max_packets = cpu_to_le32(ec->tx_max_coalesced_frames); + sg_init_one(&sgs_tx, &vi->ctrl->coal_tx, sizeof(vi->ctrl->coal_tx)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, VIRTIO_NET_CTRL_NOTF_COAL_TX_SET, @@ -2802,9 +2802,9 @@ static int virtnet_send_notf_coal_cmds(struct virtnet_info *vi, vi->tx_usecs = ec->tx_coalesce_usecs; vi->tx_max_packets = ec->tx_max_coalesced_frames; - coal_rx.rx_usecs = cpu_to_le32(ec->rx_coalesce_usecs); - coal_rx.rx_max_packets = cpu_to_le32(ec->rx_max_coalesced_frames); - sg_init_one(&sgs_rx, &coal_rx, sizeof(coal_rx)); + vi->ctrl->coal_rx.rx_usecs = cpu_to_le32(ec->rx_coalesce_usecs); + vi->ctrl->coal_rx.rx_max_packets = cpu_to_le32(ec->rx_max_coalesced_frames); + sg_init_one(&sgs_rx, &vi->ctrl->coal_rx, sizeof(vi->ctrl->coal_rx)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, VIRTIO_NET_CTRL_NOTF_COAL_RX_SET, -- cgit 1.4.1 From fdeb7129298bb506834cd914b84cb672da603957 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 13 May 2023 13:17:47 +0200 Subject: soc: qcom: icc-bwmon: fix incorrect error code passed to dev_err_probe() commit 3530167c6fe8001de6c026a3058eaca4c8a5329f upstream. Pass to dev_err_probe() PTR_ERR from actual dev_pm_opp_find_bw_floor() call which failed, instead of previous ret which at this point is 0. Failure of dev_pm_opp_find_bw_floor() would result in prematurely ending the probe with success. Fixes smatch warnings: drivers/soc/qcom/icc-bwmon.c:776 bwmon_probe() warn: passing zero to 'dev_err_probe' drivers/soc/qcom/icc-bwmon.c:781 bwmon_probe() warn: passing zero to 'dev_err_probe' Reported-by: kernel test robot Reported-by: Dan Carpenter Link: https://lore.kernel.org/r/202305131657.76XeHDjF-lkp@intel.com/ Cc: Fixes: b9c2ae6cac40 ("soc: qcom: icc-bwmon: Add bandwidth monitoring driver") Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230513111747.132532-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/soc/qcom/icc-bwmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index d07be3700db6..7829549993c9 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -603,12 +603,12 @@ static int bwmon_probe(struct platform_device *pdev) bwmon->max_bw_kbps = UINT_MAX; opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0); if (IS_ERR(opp)) - return dev_err_probe(dev, ret, "failed to find max peak bandwidth\n"); + return dev_err_probe(dev, PTR_ERR(opp), "failed to find max peak bandwidth\n"); bwmon->min_bw_kbps = 0; opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0); if (IS_ERR(opp)) - return dev_err_probe(dev, ret, "failed to find min peak bandwidth\n"); + return dev_err_probe(dev, PTR_ERR(opp), "failed to find min peak bandwidth\n"); bwmon->dev = dev; -- cgit 1.4.1 From 42a7314f2b84aadb591b5146667dc19c48f195e0 Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Fri, 12 May 2023 08:49:25 +0200 Subject: pinctrl: meson-axg: add missing GPIOA_18 gpio group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5b10ff013e8a57f8845615ac2cc37edf7f6eef05 upstream. Without this, the gpio cannot be explicitly mux'ed to its gpio function. Fixes: 83c566806a68a ("pinctrl: meson-axg: Add new pinctrl driver for Meson AXG SoC") Cc: stable@vger.kernel.org Signed-off-by: Martin Hundebøll Reviewed-by: Neil Armstrong Reviewed-by: Dmitry Rokosov Link: https://lore.kernel.org/r/20230512064925.133516-1-martin@geanix.com Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/meson/pinctrl-meson-axg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/meson/pinctrl-meson-axg.c b/drivers/pinctrl/meson/pinctrl-meson-axg.c index 7bfecdfba177..d249a035c2b9 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-axg.c +++ b/drivers/pinctrl/meson/pinctrl-meson-axg.c @@ -400,6 +400,7 @@ static struct meson_pmx_group meson_axg_periphs_groups[] = { GPIO_GROUP(GPIOA_15), GPIO_GROUP(GPIOA_16), GPIO_GROUP(GPIOA_17), + GPIO_GROUP(GPIOA_18), GPIO_GROUP(GPIOA_19), GPIO_GROUP(GPIOA_20), -- cgit 1.4.1 From 80e29f11be6932274370d66e28e6848ee1120033 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 15 May 2023 21:09:55 +0800 Subject: usb: usbfs: Enforce page requirements for mmap commit 0143d148d1e882fb1538dc9974c94d63961719b9 upstream. The current implementation of usbdev_mmap uses usb_alloc_coherent to allocate memory pages that will later be mapped into the user space. Meanwhile, usb_alloc_coherent employs three different methods to allocate memory, as outlined below: * If hcd->localmem_pool is non-null, it uses gen_pool_dma_alloc to allocate memory; * If DMA is not available, it uses kmalloc to allocate memory; * Otherwise, it uses dma_alloc_coherent. However, it should be noted that gen_pool_dma_alloc does not guarantee that the resulting memory will be page-aligned. Furthermore, trying to map slab pages (i.e., memory allocated by kmalloc) into the user space is not resonable and can lead to problems, such as a type confusion bug when PAGE_TABLE_CHECK=y [1]. To address these issues, this patch introduces hcd_alloc_coherent_pages, which addresses the above two problems. Specifically, hcd_alloc_coherent_pages uses gen_pool_dma_alloc_align instead of gen_pool_dma_alloc to ensure that the memory is page-aligned. To replace kmalloc, hcd_alloc_coherent_pages directly allocates pages by calling __get_free_pages. Reported-by: syzbot+fcf1a817ceb50935ce99@syzkaller.appspotmail.comm Closes: https://lore.kernel.org/lkml/000000000000258e5e05fae79fc1@google.com/ [1] Fixes: f7d34b445abc ("USB: Add support for usbfs zerocopy.") Fixes: ff2437befd8f ("usb: host: Fix excessive alignment restriction for local memory allocations") Cc: stable@vger.kernel.org Signed-off-by: Ruihan Li Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230515130958.32471-2-lrh2000@pku.edu.cn Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/buffer.c | 41 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/core/devio.c | 9 +++++---- include/linux/usb/hcd.h | 5 +++++ 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index fbb087b728dc..268ccbec88f9 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -172,3 +172,44 @@ void hcd_buffer_free( } dma_free_coherent(hcd->self.sysdev, size, addr, dma); } + +void *hcd_buffer_alloc_pages(struct usb_hcd *hcd, + size_t size, gfp_t mem_flags, dma_addr_t *dma) +{ + if (size == 0) + return NULL; + + if (hcd->localmem_pool) + return gen_pool_dma_alloc_align(hcd->localmem_pool, + size, dma, PAGE_SIZE); + + /* some USB hosts just use PIO */ + if (!hcd_uses_dma(hcd)) { + *dma = DMA_MAPPING_ERROR; + return (void *)__get_free_pages(mem_flags, + get_order(size)); + } + + return dma_alloc_coherent(hcd->self.sysdev, + size, dma, mem_flags); +} + +void hcd_buffer_free_pages(struct usb_hcd *hcd, + size_t size, void *addr, dma_addr_t dma) +{ + if (!addr) + return; + + if (hcd->localmem_pool) { + gen_pool_free(hcd->localmem_pool, + (unsigned long)addr, size); + return; + } + + if (!hcd_uses_dma(hcd)) { + free_pages((unsigned long)addr, get_order(size)); + return; + } + + dma_free_coherent(hcd->self.sysdev, size, addr, dma); +} diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 837f3e57f580..e75cf98c660f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -186,6 +186,7 @@ static int connected(struct usb_dev_state *ps) static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count) { struct usb_dev_state *ps = usbm->ps; + struct usb_hcd *hcd = bus_to_hcd(ps->dev->bus); unsigned long flags; spin_lock_irqsave(&ps->lock, flags); @@ -194,8 +195,8 @@ static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count) list_del(&usbm->memlist); spin_unlock_irqrestore(&ps->lock, flags); - usb_free_coherent(ps->dev, usbm->size, usbm->mem, - usbm->dma_handle); + hcd_buffer_free_pages(hcd, usbm->size, + usbm->mem, usbm->dma_handle); usbfs_decrease_memory_usage( usbm->size + sizeof(struct usb_memory)); kfree(usbm); @@ -247,8 +248,8 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) goto error_decrease_mem; } - mem = usb_alloc_coherent(ps->dev, size, GFP_USER | __GFP_NOWARN, - &dma_handle); + mem = hcd_buffer_alloc_pages(hcd, + size, GFP_USER | __GFP_NOWARN, &dma_handle); if (!mem) { ret = -ENOMEM; goto error_free_usbm; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 78cd566ee238..5a89928ea953 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -500,6 +500,11 @@ void *hcd_buffer_alloc(struct usb_bus *bus, size_t size, void hcd_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma); +void *hcd_buffer_alloc_pages(struct usb_hcd *hcd, + size_t size, gfp_t mem_flags, dma_addr_t *dma); +void hcd_buffer_free_pages(struct usb_hcd *hcd, + size_t size, void *addr, dma_addr_t dma); + /* generic bus glue, needed for host controllers that don't use PCI */ extern irqreturn_t usb_hcd_irq(int irq, void *__hcd); -- cgit 1.4.1 From 3901170529a70462c29798005f2e9b38bab211c5 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 15 May 2023 21:09:56 +0800 Subject: usb: usbfs: Use consistent mmap functions commit d0b861653f8c16839c3035875b556afc4472f941 upstream. When hcd->localmem_pool is non-null, localmem_pool is used to allocate DMA memory. In this case, the dma address will be properly returned (in dma_handle), and dma_mmap_coherent should be used to map this memory into the user space. However, the current implementation uses pfn_remap_range, which is supposed to map normal pages. Instead of repeating the logic in the memory allocation function, this patch introduces a more robust solution. Here, the type of allocated memory is checked by testing whether dma_handle is properly set. If dma_handle is properly returned, it means some DMA pages are allocated and dma_mmap_coherent should be used to map them. Otherwise, normal pages are allocated and pfn_remap_range should be called. This ensures that the correct mmap functions are used consistently, independently with logic details that determine which type of memory gets allocated. Fixes: a0e710a7def4 ("USB: usbfs: fix mmap dma mismatch") Cc: stable@vger.kernel.org Signed-off-by: Ruihan Li Link: https://lore.kernel.org/r/20230515130958.32471-3-lrh2000@pku.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e75cf98c660f..984de3f5e8eb 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -235,7 +235,7 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) size_t size = vma->vm_end - vma->vm_start; void *mem; unsigned long flags; - dma_addr_t dma_handle; + dma_addr_t dma_handle = DMA_MAPPING_ERROR; int ret; ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory)); @@ -265,7 +265,14 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) usbm->vma_use_count = 1; INIT_LIST_HEAD(&usbm->memlist); - if (hcd->localmem_pool || !hcd_uses_dma(hcd)) { + /* + * In DMA-unavailable cases, hcd_buffer_alloc_pages allocates + * normal pages and assigns DMA_MAPPING_ERROR to dma_handle. Check + * whether we are in such cases, and then use remap_pfn_range (or + * dma_mmap_coherent) to map normal (or DMA) pages into the user + * space, respectively. + */ + if (dma_handle == DMA_MAPPING_ERROR) { if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(usbm->mem) >> PAGE_SHIFT, size, vma->vm_page_prot) < 0) { -- cgit 1.4.1 From 08378f0314ce30aefe9001a9affbf460dcf6047c Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 15 May 2023 21:09:57 +0800 Subject: mm: page_table_check: Make it dependent on EXCLUSIVE_SYSTEM_RAM commit 81a31a860bb61d54eb688af2568d9332ed9b8942 upstream. Without EXCLUSIVE_SYSTEM_RAM, users are allowed to map arbitrary physical memory regions into the userspace via /dev/mem. At the same time, pages may change their properties (e.g., from anonymous pages to named pages) while they are still being mapped in the userspace, leading to "corruption" detected by the page table check. To avoid these false positives, this patch makes PAGE_TABLE_CHECK depends on EXCLUSIVE_SYSTEM_RAM. This dependency is understandable because PAGE_TABLE_CHECK is a hardening technique but /dev/mem without STRICT_DEVMEM (i.e., !EXCLUSIVE_SYSTEM_RAM) is itself a security problem. Even with EXCLUSIVE_SYSTEM_RAM, I/O pages may be still allowed to be mapped via /dev/mem. However, these pages are always considered as named pages, so they won't break the logic used in the page table check. Cc: # 5.17 Signed-off-by: Ruihan Li Acked-by: David Hildenbrand Acked-by: Pasha Tatashin Link: https://lore.kernel.org/r/20230515130958.32471-4-lrh2000@pku.edu.cn Signed-off-by: Greg Kroah-Hartman --- Documentation/mm/page_table_check.rst | 19 +++++++++++++++++++ mm/Kconfig.debug | 1 + 2 files changed, 20 insertions(+) diff --git a/Documentation/mm/page_table_check.rst b/Documentation/mm/page_table_check.rst index 1a09472f10a3..d19ca356b7bf 100644 --- a/Documentation/mm/page_table_check.rst +++ b/Documentation/mm/page_table_check.rst @@ -54,3 +54,22 @@ Build kernel with: Optionally, build kernel with PAGE_TABLE_CHECK_ENFORCED in order to have page table support without extra kernel parameter. + +Implementation notes +==================== + +We specifically decided not to use VMA information in order to avoid relying on +MM states (except for limited "struct page" info). The page table check is a +separate from Linux-MM state machine that verifies that the user accessible +pages are not falsely shared. + +PAGE_TABLE_CHECK depends on EXCLUSIVE_SYSTEM_RAM. The reason is that without +EXCLUSIVE_SYSTEM_RAM, users are allowed to map arbitrary physical memory +regions into the userspace via /dev/mem. At the same time, pages may change +their properties (e.g., from anonymous pages to named pages) while they are +still being mapped in the userspace, leading to "corruption" detected by the +page table check. + +Even with EXCLUSIVE_SYSTEM_RAM, I/O pages may be still allowed to be mapped via +/dev/mem. However, these pages are always considered as named pages, so they +won't break the logic used in the page table check. diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug index ce8dded36de9..32c2df12a5ec 100644 --- a/mm/Kconfig.debug +++ b/mm/Kconfig.debug @@ -98,6 +98,7 @@ config PAGE_OWNER config PAGE_TABLE_CHECK bool "Check for invalid mappings in user page tables" depends on ARCH_SUPPORTS_PAGE_TABLE_CHECK + depends on EXCLUSIVE_SYSTEM_RAM select PAGE_EXTENSION help Check that anonymous page is not being mapped twice with read write -- cgit 1.4.1 From df9bc25d13c146a3979015f73ab9b5d406ca7ae1 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 15 May 2023 21:09:58 +0800 Subject: mm: page_table_check: Ensure user pages are not slab pages commit 44d0fb387b53e56c8a050bac5c7d460e21eb226f upstream. The current uses of PageAnon in page table check functions can lead to type confusion bugs between struct page and slab [1], if slab pages are accidentally mapped into the user space. This is because slab reuses the bits in struct page to store its internal states, which renders PageAnon ineffective on slab pages. Since slab pages are not expected to be mapped into the user space, this patch adds BUG_ON(PageSlab(page)) checks to make sure that slab pages are not inadvertently mapped. Otherwise, there must be some bugs in the kernel. Reported-by: syzbot+fcf1a817ceb50935ce99@syzkaller.appspotmail.com Closes: https://lore.kernel.org/lkml/000000000000258e5e05fae79fc1@google.com/ [1] Fixes: df4e817b7108 ("mm: page table check") Cc: # 5.17 Signed-off-by: Ruihan Li Acked-by: Pasha Tatashin Link: https://lore.kernel.org/r/20230515130958.32471-5-lrh2000@pku.edu.cn Signed-off-by: Greg Kroah-Hartman --- include/linux/page-flags.h | 6 ++++++ mm/page_table_check.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 0b0ae5084e60..59314d228ec3 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -631,6 +631,12 @@ PAGEFLAG_FALSE(VmemmapSelfHosted, vmemmap_self_hosted) * Please note that, confusingly, "page_mapping" refers to the inode * address_space which maps the page from disk; whereas "page_mapped" * refers to user virtual address space into which the page is mapped. + * + * For slab pages, since slab reuses the bits in struct page to store its + * internal states, the page->mapping does not exist as such, nor do these + * flags below. So in order to avoid testing non-existent bits, please + * make sure that PageSlab(page) actually evaluates to false before calling + * the following functions (e.g., PageAnon). See mm/slab.h. */ #define PAGE_MAPPING_ANON 0x1 #define PAGE_MAPPING_MOVABLE 0x2 diff --git a/mm/page_table_check.c b/mm/page_table_check.c index 433dbce13fe1..4d0506537621 100644 --- a/mm/page_table_check.c +++ b/mm/page_table_check.c @@ -69,6 +69,8 @@ static void page_table_check_clear(struct mm_struct *mm, unsigned long addr, page = pfn_to_page(pfn); page_ext = page_ext_get(page); + + BUG_ON(PageSlab(page)); anon = PageAnon(page); for (i = 0; i < pgcnt; i++) { @@ -105,6 +107,8 @@ static void page_table_check_set(struct mm_struct *mm, unsigned long addr, page = pfn_to_page(pfn); page_ext = page_ext_get(page); + + BUG_ON(PageSlab(page)); anon = PageAnon(page); for (i = 0; i < pgcnt; i++) { @@ -131,6 +135,8 @@ void __page_table_check_zero(struct page *page, unsigned int order) struct page_ext *page_ext; unsigned long i; + BUG_ON(PageSlab(page)); + page_ext = page_ext_get(page); BUG_ON(!page_ext); for (i = 0; i < (1ul << order); i++) { -- cgit 1.4.1 From c97f30d215d8ef6a775fa067627470035a928795 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 12 May 2023 08:04:25 -0700 Subject: arm64: dts: qcom: sc8280xp: Flush RSC sleep & wake votes [ Upstream commit ce7c014937c442be677963848c7db62eccd94eac ] The rpmh driver will cache sleep and wake votes until the cluster power-domain is about to enter idle, to avoid unnecessary writes. So associate the apps_rsc with the cluster pd, so that it can be notified about this event. Without this, only AMC votes are being commited. Signed-off-by: Bjorn Andersson Reviewed-by: Konrad Dybcio Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230512150425.3171122-1-quic_bjorande@quicinc.com Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index ba684d980cf2..1afc960bab5c 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -1727,6 +1727,7 @@ qcom,tcs-config = , , , ; label = "apps_rsc"; + power-domains = <&CLUSTER_PD>; apps_bcm_voter: bcm-voter { compatible = "qcom,bcm-voter"; -- cgit 1.4.1 From 4b8ebe5393ed339f08d2f05d1130645db9de0672 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 18 May 2023 09:25:11 +0300 Subject: ARM: at91: pm: fix imbalanced reference counter for ethernet devices [ Upstream commit ccd4923d18d5698a5910d516646ce125b9155d47 ] The of_find_device_by_node() function is returning a struct platform_device object with the embedded struct device member's reference counter incremented. This needs to be dropped when done with the platform device returned by of_find_device_by_node(). at91_pm_eth_quirk_is_valid() calls of_find_device_by_node() on suspend and resume path. On suspend it calls of_find_device_by_node() and on resume and failure paths it drops the counter of struct platform_device::dev. In case ethernet device may not wakeup there is a put_device() on at91_pm_eth_quirk_is_valid() which is wrong as it colides with put_device() on resume path leading to the reference counter of struct device embedded in struct platform_device to be messed, stack trace to be displayed (after 5 consecutive suspend/resume cycles) and execution to hang. Along with this the error path of at91_pm_config_quirks() had been also adapted to decrement propertly the reference counter of struct device embedded in struct platform_device. Fixes: b7fc72c63399 ("ARM: at91: pm: add quirks for pm") Signed-off-by: Claudiu Beznea Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20230518062511.2988500-1-claudiu.beznea@microchip.com Signed-off-by: Sasha Levin --- arch/arm/mach-at91/pm.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 60dc56d8acfb..437dd0352fd4 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -334,16 +334,14 @@ static bool at91_pm_eth_quirk_is_valid(struct at91_pm_quirk_eth *eth) pdev = of_find_device_by_node(eth->np); if (!pdev) return false; + /* put_device(eth->dev) is called at the end of suspend. */ eth->dev = &pdev->dev; } /* No quirks if device isn't a wakeup source. */ - if (!device_may_wakeup(eth->dev)) { - put_device(eth->dev); + if (!device_may_wakeup(eth->dev)) return false; - } - /* put_device(eth->dev) is called at the end of suspend. */ return true; } @@ -439,14 +437,14 @@ clk_unconfigure: pr_err("AT91: PM: failed to enable %s clocks\n", j == AT91_PM_G_ETH ? "geth" : "eth"); } - } else { - /* - * Release the reference to eth->dev taken in - * at91_pm_eth_quirk_is_valid(). - */ - put_device(eth->dev); - eth->dev = NULL; } + + /* + * Release the reference to eth->dev taken in + * at91_pm_eth_quirk_is_valid(). + */ + put_device(eth->dev); + eth->dev = NULL; } return ret; -- cgit 1.4.1 From 58ab8a0ff81d97b068dafdf1b9002262e71e5295 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 23 May 2023 08:27:50 +0300 Subject: ARM: dts: at91: sama7g5ek: fix debounce delay property for shdwc [ Upstream commit 6b0db163ff9200a55dc77a652dad1d4b0a853f63 ] There is no atmel,shdwc-debouncer property for SHDWC. The right DT property is debounce-delay-us. Use it. Fixes: 16b161bcf5d4 ("ARM: dts: at91: sama7g5: add shdwc node") Signed-off-by: Claudiu Beznea Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20230523052750.184223-1-claudiu.beznea@microchip.com Signed-off-by: Sasha Levin --- arch/arm/boot/dts/at91-sama7g5ek.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/at91-sama7g5ek.dts b/arch/arm/boot/dts/at91-sama7g5ek.dts index 3b25c67795dd..4af8a1c96ed6 100644 --- a/arch/arm/boot/dts/at91-sama7g5ek.dts +++ b/arch/arm/boot/dts/at91-sama7g5ek.dts @@ -789,7 +789,7 @@ }; &shdwc { - atmel,shdwc-debouncer = <976>; + debounce-delay-us = <976>; status = "okay"; input@0 { -- cgit 1.4.1 From 8b13854f26f2ef6ea3563ba12f82a96a69709bf8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 23 May 2023 16:46:04 +0100 Subject: ASoC: codecs: wsa883x: do not set can_multi_write flag [ Upstream commit 40ba0411074485e2cf1bf8ee0f3db27bdff88394 ] regmap-sdw does not support multi register writes, so there is no point in setting this flag. This also leads to incorrect programming of WSA codecs with regmap_multi_reg_write() call. This invalid configuration should have been rejected by regmap-sdw. Fixes: 43b8c7dc85a1 ("ASoC: codecs: add wsa883x amplifier support") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230523154605.4284-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/wsa883x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 6e9a64c5948e..b152f4e5c4f2 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -950,7 +950,6 @@ static struct regmap_config wsa883x_regmap_config = { .writeable_reg = wsa883x_writeable_register, .reg_format_endian = REGMAP_ENDIAN_NATIVE, .val_format_endian = REGMAP_ENDIAN_NATIVE, - .can_multi_write = true, .use_single_read = true, }; -- cgit 1.4.1 From 037449ce1c04c12ccaab26b13e8d63aaed0184f9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 23 May 2023 16:46:05 +0100 Subject: ASoC: codecs: wsa881x: do not set can_multi_write flag [ Upstream commit 6e7a6d4797ef521c0762914610ed682e102b9d36 ] regmap-sdw does not support multi register writes, so there is no point in setting this flag. This also leads to incorrect programming of WSA codecs with regmap_multi_reg_write() call. This invalid configuration should have been rejected by regmap-sdw. Fixes: a0aab9e1404a ("ASoC: codecs: add wsa881x amplifier support") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230523154605.4284-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/wsa881x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 046843b57b03..264ec05a3c67 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -648,7 +648,6 @@ static struct regmap_config wsa881x_regmap_config = { .readable_reg = wsa881x_readable_register, .reg_format_endian = REGMAP_ENDIAN_NATIVE, .val_format_endian = REGMAP_ENDIAN_NATIVE, - .can_multi_write = true, }; enum { -- cgit 1.4.1 From 5a607e53f208ef7c14d0251325abab572c35acc9 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 15 May 2023 17:19:29 -0700 Subject: arm64: dts: qcom: sc7180-lite: Fix SDRAM freq for misidentified sc7180-lite boards [ Upstream commit 3a735530c159b75e1402c08abe1ba4eb99a1f7a3 ] In general, the three SKUs of sc7180 (lite, normal, and pro) are handled dynamically. The cpufreq table in sc7180.dtsi includes the superset of all CPU frequencies. The "qcom-cpufreq-hw" driver in Linux shows that we can dynamically detect which frequencies are actually available on the currently running CPU and then we can just enable those ones. The GPU is similarly dynamic. The nvmem has a fuse in it (see "gpu_speed_bin" in sc7180.dtsi) that the GPU driver can use to figure out which frequencies to enable. There is one part, however, that is not so dynamic. The way SDRAM frequency works in sc7180 is that it's tied to cpufreq. At the busiest cpufreq operating points we'll pick the top supported SDRAM frequency. They ramp down together. For the "pro" SKU of sc7180, we only enable one extra cpufreq step. That extra cpufreq step runs SDRAM at the same speed as the step below. Thus, for normal and pro things are OK. There is no sc7180-pro device tree snippet. For the "lite" SKU if sc7180, however, things aren't so easy. The "lite" SKU drops 3 cpufreq entries but can still run SDRAM at max frequency. That messed things up with the whole scheme. This is why we added the "sc7180-lite" fragment in commit 8fd01e01fd6f ("arm64: dts: qcom: sc7180-lite: Tweak DDR/L3 scaling on SC7180-lite"). When the lite scheme came about, it was agreed that the WiFi SKUs of lazor would _always_ be "lite" and would, in fact, be the only "lite" devices. Unfortunately, this decision changed and folks didn't realize that it would be a problem. Specifically, some later lazor WiFi-only devices were built with "pro" CPUs. Building WiFi-only lazor with "pro" CPUs isn't the end of the world. The SDRAM will ramp up a little sooner than it otherwise would, but aside from a small power hit things work OK. One problem, though, is that the SDRAM scaling becomes a bit quirky. Specifically, with the current tables we'll max out SDRAM frequency at 2.1GHz but then _lower_ it at 2.2GHz / 2.3GHz only to raise it back to max for 2.4GHz and 2.55GHz. Let's at least fix this so that the SDRAM frequency doesn't go down in that quirky way. On true "lite" SKUs this change will be a no-op because the operating points we're touching are disabled. This change is only useful when a board that thinks it has a "lite" CPU actually has a "normal" or "pro" one stuffed. Fixes: 8fd01e01fd6f ("arm64: dts: qcom: sc7180-lite: Tweak DDR/L3 scaling on SC7180-lite") Signed-off-by: Douglas Anderson Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230515171929.1.Ic8dee2cb79ce39ffc04eab2a344dde47b2f9459f@changeid Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc7180-lite.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sc7180-lite.dtsi b/arch/arm64/boot/dts/qcom/sc7180-lite.dtsi index d8ed1d7b4ec7..4b306a59d9be 100644 --- a/arch/arm64/boot/dts/qcom/sc7180-lite.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7180-lite.dtsi @@ -16,3 +16,11 @@ &cpu6_opp12 { opp-peak-kBps = <8532000 23347200>; }; + +&cpu6_opp13 { + opp-peak-kBps = <8532000 23347200>; +}; + +&cpu6_opp14 { + opp-peak-kBps = <8532000 23347200>; +}; -- cgit 1.4.1 From efe115560a29e35317d7b033a2e39b525a4574c3 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Thu, 18 May 2023 08:54:21 -0500 Subject: arm64: dts: imx8qm-mek: correct GPIOs for USDHC2 CD and WP signals [ Upstream commit 2b28fc688cdff225c41cdd22857500e187453ed7 ] The USDHC2 CD and WP sginal should be on LSIO_GPIO5. Fixes: 307fd14d4b14 ("arm64: dts: imx: add imx8qm mek support") Signed-off-by: Shenwei Wang Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8qm-mek.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts index ce9d3f0b98fc..607cd6b4e972 100644 --- a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts +++ b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts @@ -82,8 +82,8 @@ pinctrl-0 = <&pinctrl_usdhc2>; bus-width = <4>; vmmc-supply = <®_usdhc2_vmmc>; - cd-gpios = <&lsio_gpio4 22 GPIO_ACTIVE_LOW>; - wp-gpios = <&lsio_gpio4 21 GPIO_ACTIVE_HIGH>; + cd-gpios = <&lsio_gpio5 22 GPIO_ACTIVE_LOW>; + wp-gpios = <&lsio_gpio5 21 GPIO_ACTIVE_HIGH>; status = "okay"; }; -- cgit 1.4.1 From 370711d7f0c1b6239d52831c1cf5c2b2a89e1e88 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Fri, 26 May 2023 10:38:54 -0500 Subject: arm64: dts: imx8-ss-dma: assign default clock rate for lpuarts [ Upstream commit ca50d7765587fe0a8351a6e8d9742cfd4811d925 ] Add the assigned-clocks and assigned-clock-rates properties for the LPUARTx nodes. Without these properties, the default clock rate used would be 0, which can cause the UART ports to fail when open. Fixes: 35f4e9d7530f ("arm64: dts: imx8: split adma ss into dma and audio ss") Signed-off-by: Shenwei Wang Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi index d7b4229bb4a2..9ad56aaf19b4 100644 --- a/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi @@ -26,6 +26,8 @@ dma_subsys: bus@5a000000 { clocks = <&uart0_lpcg IMX_LPCG_CLK_4>, <&uart0_lpcg IMX_LPCG_CLK_0>; clock-names = "ipg", "baud"; + assigned-clocks = <&clk IMX_SC_R_UART_0 IMX_SC_PM_CLK_PER>; + assigned-clock-rates = <80000000>; power-domains = <&pd IMX_SC_R_UART_0>; status = "disabled"; }; @@ -36,6 +38,8 @@ dma_subsys: bus@5a000000 { clocks = <&uart1_lpcg IMX_LPCG_CLK_4>, <&uart1_lpcg IMX_LPCG_CLK_0>; clock-names = "ipg", "baud"; + assigned-clocks = <&clk IMX_SC_R_UART_1 IMX_SC_PM_CLK_PER>; + assigned-clock-rates = <80000000>; power-domains = <&pd IMX_SC_R_UART_1>; status = "disabled"; }; @@ -46,6 +50,8 @@ dma_subsys: bus@5a000000 { clocks = <&uart2_lpcg IMX_LPCG_CLK_4>, <&uart2_lpcg IMX_LPCG_CLK_0>; clock-names = "ipg", "baud"; + assigned-clocks = <&clk IMX_SC_R_UART_2 IMX_SC_PM_CLK_PER>; + assigned-clock-rates = <80000000>; power-domains = <&pd IMX_SC_R_UART_2>; status = "disabled"; }; @@ -56,6 +62,8 @@ dma_subsys: bus@5a000000 { clocks = <&uart3_lpcg IMX_LPCG_CLK_4>, <&uart3_lpcg IMX_LPCG_CLK_0>; clock-names = "ipg", "baud"; + assigned-clocks = <&clk IMX_SC_R_UART_3 IMX_SC_PM_CLK_PER>; + assigned-clock-rates = <80000000>; power-domains = <&pd IMX_SC_R_UART_3>; status = "disabled"; }; -- cgit 1.4.1 From 50f2160afbb8102cb983dd64c0768ae1f64b6647 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 15 Mar 2023 16:06:45 +0100 Subject: ASoC: mediatek: mt8195-afe-pcm: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6461fee68064ba970e3ba90241fe5f5e038aa9d4 ] The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: AngeloGioacchino Del Regno Acked-by: Takashi Iwai Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20230315150745.67084-114-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown Stable-dep-of: dc93f0dcb436 ("ASoC: mediatek: mt8195: fix use-after-free in driver remove path") Signed-off-by: Sasha Levin --- sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 72b2c6d629b9..9e45efeada55 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -3253,7 +3253,7 @@ err_pm_put: return ret; } -static int mt8195_afe_pcm_dev_remove(struct platform_device *pdev) +static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev) { struct mtk_base_afe *afe = platform_get_drvdata(pdev); @@ -3264,7 +3264,6 @@ static int mt8195_afe_pcm_dev_remove(struct platform_device *pdev) mt8195_afe_runtime_suspend(&pdev->dev); mt8195_afe_deinit_clock(afe); - return 0; } static const struct of_device_id mt8195_afe_pcm_dt_match[] = { @@ -3285,7 +3284,7 @@ static struct platform_driver mt8195_afe_pcm_driver = { .pm = &mt8195_afe_pm_ops, }, .probe = mt8195_afe_pcm_dev_probe, - .remove = mt8195_afe_pcm_dev_remove, + .remove_new = mt8195_afe_pcm_dev_remove, }; module_platform_driver(mt8195_afe_pcm_driver); -- cgit 1.4.1 From c0f9f799bac64381fb2ea322a49073b1a0463bcb Mon Sep 17 00:00:00 2001 From: Trevor Wu Date: Thu, 1 Jun 2023 11:33:18 +0800 Subject: ASoC: mediatek: mt8195: fix use-after-free in driver remove path [ Upstream commit dc93f0dcb436dfd24a06c5b3c0f4c5cd9296e8e5 ] During mt8195_afe_init_clock(), mt8195_audsys_clk_register() was called followed by several other devm functions. At mt8195_afe_deinit_clock() located at mt8195_afe_pcm_dev_remove(), mt8195_audsys_clk_unregister() was called. However, there was an issue with the order in which these functions were called. Specifically, the remove callback of platform_driver was called before devres released the resource, resulting in a use-after-free issue during remove time. At probe time, the order of calls was: 1. mt8195_audsys_clk_register 2. afe_priv->clk = devm_kcalloc 3. afe_priv->clk[i] = devm_clk_get At remove time, the order of calls was: 1. mt8195_audsys_clk_unregister 3. free afe_priv->clk[i] 2. free afe_priv->clk To resolve the problem, we can utilize devm_add_action_or_reset() in mt8195_audsys_clk_register() so that the remove order can be changed to 3->2->1. Fixes: 6746cc858259 ("ASoC: mediatek: mt8195: add platform driver") Signed-off-by: Trevor Wu Reviewed-by: Douglas Anderson Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230601033318.10408-3-trevor.wu@mediatek.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/mediatek/mt8195/mt8195-afe-clk.c | 5 --- sound/soc/mediatek/mt8195/mt8195-afe-clk.h | 1 - sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 4 --- sound/soc/mediatek/mt8195/mt8195-audsys-clk.c | 47 ++++++++++++++------------- sound/soc/mediatek/mt8195/mt8195-audsys-clk.h | 1 - 5 files changed, 24 insertions(+), 34 deletions(-) diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c index 9ca2cb8c8a9c..f35318ae0739 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c @@ -410,11 +410,6 @@ int mt8195_afe_init_clock(struct mtk_base_afe *afe) return 0; } -void mt8195_afe_deinit_clock(struct mtk_base_afe *afe) -{ - mt8195_audsys_clk_unregister(afe); -} - int mt8195_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk) { int ret; diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.h b/sound/soc/mediatek/mt8195/mt8195-afe-clk.h index 40663e31becd..a08c0ee6c860 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.h +++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.h @@ -101,7 +101,6 @@ int mt8195_afe_get_mclk_source_clk_id(int sel); int mt8195_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll); int mt8195_afe_get_default_mclk_source_by_rate(int rate); int mt8195_afe_init_clock(struct mtk_base_afe *afe); -void mt8195_afe_deinit_clock(struct mtk_base_afe *afe); int mt8195_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk); void mt8195_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk); int mt8195_afe_prepare_clk(struct mtk_base_afe *afe, struct clk *clk); diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 9e45efeada55..03dabc056b91 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -3255,15 +3255,11 @@ err_pm_put: static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev) { - struct mtk_base_afe *afe = platform_get_drvdata(pdev); - snd_soc_unregister_component(&pdev->dev); pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) mt8195_afe_runtime_suspend(&pdev->dev); - - mt8195_afe_deinit_clock(afe); } static const struct of_device_id mt8195_afe_pcm_dt_match[] = { diff --git a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c index e0670e0dbd5b..38594bc3f2f7 100644 --- a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c +++ b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c @@ -148,6 +148,29 @@ static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = { GATE_AUD6(CLK_AUD_GASRC19, "aud_gasrc19", "top_asm_h", 19), }; +static void mt8195_audsys_clk_unregister(void *data) +{ + struct mtk_base_afe *afe = data; + struct mt8195_afe_private *afe_priv = afe->platform_priv; + struct clk *clk; + struct clk_lookup *cl; + int i; + + if (!afe_priv) + return; + + for (i = 0; i < CLK_AUD_NR_CLK; i++) { + cl = afe_priv->lookup[i]; + if (!cl) + continue; + + clk = cl->clk; + clk_unregister_gate(clk); + + clkdev_drop(cl); + } +} + int mt8195_audsys_clk_register(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; @@ -188,27 +211,5 @@ int mt8195_audsys_clk_register(struct mtk_base_afe *afe) afe_priv->lookup[i] = cl; } - return 0; -} - -void mt8195_audsys_clk_unregister(struct mtk_base_afe *afe) -{ - struct mt8195_afe_private *afe_priv = afe->platform_priv; - struct clk *clk; - struct clk_lookup *cl; - int i; - - if (!afe_priv) - return; - - for (i = 0; i < CLK_AUD_NR_CLK; i++) { - cl = afe_priv->lookup[i]; - if (!cl) - continue; - - clk = cl->clk; - clk_unregister_gate(clk); - - clkdev_drop(cl); - } + return devm_add_action_or_reset(afe->dev, mt8195_audsys_clk_unregister, afe); } diff --git a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.h b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.h index 239d31016ba7..69db2dd1c9e0 100644 --- a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.h +++ b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.h @@ -10,6 +10,5 @@ #define _MT8195_AUDSYS_CLK_H_ int mt8195_audsys_clk_register(struct mtk_base_afe *afe); -void mt8195_audsys_clk_unregister(struct mtk_base_afe *afe); #endif -- cgit 1.4.1 From c3d87d415f7c45e83cb8327ae4d1f1d9d407549e Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 1 Jun 2023 19:19:35 -0600 Subject: ASoC: simple-card-utils: fix PCM constraint error check [ Upstream commit 635071f5fee31550e921644b2becc42b3ff1036c ] The code in asoc_simple_startup was treating any non-zero return from snd_pcm_hw_constraint_minmax as an error, when this can return 1 in some normal cases and only negative values indicate an error. When this happened, it caused asoc_simple_startup to disable the clocks it just enabled and return 1, which was not treated as an error by the calling code which only checks for negative return values. Then when the PCM is eventually shut down, it causes the clock framework to complain about disabling clocks that were not enabled. Fix the check for snd_pcm_hw_constraint_minmax return value to only treat negative values as an error. Fixes: 5ca2ab459817 ("ASoC: simple-card-utils: Add new system-clock-fixed flag") Signed-off-by: Robert Hancock Link: https://lore.kernel.org/r/20230602011936.231931-1-robert.hancock@calian.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/generic/simple-card-utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index be69bbc47f81..8811321717fb 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -335,7 +335,7 @@ int asoc_simple_startup(struct snd_pcm_substream *substream) } ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, fixed_rate, fixed_rate); - if (ret) + if (ret < 0) goto codec_err; } -- cgit 1.4.1 From b64bbe8b1a45a27b9ea3dccfd82dfa138e9c5277 Mon Sep 17 00:00:00 2001 From: Tian Lan Date: Sat, 13 May 2023 18:12:27 -0400 Subject: blk-mq: fix blk_mq_hw_ctx active request accounting [ Upstream commit ddad59331a4e16088468ca0ad228a9fe32d7955a ] The nr_active counter continues to increase over time which causes the blk_mq_get_tag to hang until the thread is rescheduled to a different core despite there are still tags available. kernel-stack INFO: task inboundIOReacto:3014879 blocked for more than 2 seconds Not tainted 6.1.15-amd64 #1 Debian 6.1.15~debian11 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:inboundIOReacto state:D stack:0 pid:3014879 ppid:4557 flags:0x00000000 Call Trace: __schedule+0x351/0xa20 scheduler+0x5d/0xe0 io_schedule+0x42/0x70 blk_mq_get_tag+0x11a/0x2a0 ? dequeue_task_stop+0x70/0x70 __blk_mq_alloc_requests+0x191/0x2e0 kprobe output showing RQF_MQ_INFLIGHT bit is not cleared before __blk_mq_free_request being called. 320 320 kworker/29:1H __blk_mq_free_request rq_flags 0x220c0 in-flight 1 b'__blk_mq_free_request+0x1 [kernel]' b'bt_iter+0x50 [kernel]' b'blk_mq_queue_tag_busy_iter+0x318 [kernel]' b'blk_mq_timeout_work+0x7c [kernel]' b'process_one_work+0x1c4 [kernel]' b'worker_thread+0x4d [kernel]' b'kthread+0xe6 [kernel]' b'ret_from_fork+0x1f [kernel]' Signed-off-by: Tian Lan Fixes: 2e315dc07df0 ("blk-mq: grab rq->refcount before calling ->fn in blk_mq_tagset_busy_iter") Reviewed-by: Ming Lei Link: https://lore.kernel.org/r/20230513221227.497327-1-tilan7663@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/blk-mq.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index 1ab41fbca094..add013d5bbda 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -685,6 +685,10 @@ static void __blk_mq_free_request(struct request *rq) blk_crypto_free_request(rq); blk_pm_mark_last_busy(rq); rq->mq_hctx = NULL; + + if (rq->rq_flags & RQF_MQ_INFLIGHT) + __blk_mq_dec_active_requests(hctx); + if (rq->tag != BLK_MQ_NO_TAG) blk_mq_put_tag(hctx->tags, ctx, rq->tag); if (sched_tag != BLK_MQ_NO_TAG) @@ -696,15 +700,11 @@ static void __blk_mq_free_request(struct request *rq) void blk_mq_free_request(struct request *rq) { struct request_queue *q = rq->q; - struct blk_mq_hw_ctx *hctx = rq->mq_hctx; if ((rq->rq_flags & RQF_ELVPRIV) && q->elevator->type->ops.finish_request) q->elevator->type->ops.finish_request(rq); - if (rq->rq_flags & RQF_MQ_INFLIGHT) - __blk_mq_dec_active_requests(hctx); - if (unlikely(laptop_mode && !blk_rq_is_passthrough(rq))) laptop_io_completion(q->disk->bdi); -- cgit 1.4.1 From 8e64012c034831b3aad8597217c28086dee62a7b Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Sun, 28 May 2023 06:22:54 -0500 Subject: arm64: dts: imx8mn-beacon: Fix SPI CS pinmux [ Upstream commit 9bf2e534313fcf420367668cc1f30e10469901dc ] The final production baseboard had a different chip select than earlier prototype boards. When the newer board was released, the SPI stopped working because the wrong pin was used in the device tree and conflicted with the UART RTS. Fix the pinmux for production boards. Fixes: 36ca3c8ccb53 ("arm64: dts: imx: Add Beacon i.MX8M Nano development kit") Signed-off-by: Adam Ford Signed-off-by: Shawn Guo Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mn-beacon-baseboard.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mn-beacon-baseboard.dtsi b/arch/arm64/boot/dts/freescale/imx8mn-beacon-baseboard.dtsi index 9e82069c941f..5a1f7c30afe5 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-beacon-baseboard.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn-beacon-baseboard.dtsi @@ -81,7 +81,7 @@ &ecspi2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_espi2>; - cs-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; + cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>; status = "okay"; eeprom@0 { @@ -202,7 +202,7 @@ MX8MN_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK 0x82 MX8MN_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI 0x82 MX8MN_IOMUXC_ECSPI2_MISO_ECSPI2_MISO 0x82 - MX8MN_IOMUXC_ECSPI1_SS0_GPIO5_IO9 0x41 + MX8MN_IOMUXC_ECSPI2_SS0_GPIO5_IO13 0x41 >; }; -- cgit 1.4.1 From f24cb5a04270bb85130a417d157e5860940e95d3 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Sun, 21 May 2023 14:19:40 +0200 Subject: i2c: mv64xxx: Fix reading invalid status value in atomic mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5578d0a79b6430fa1543640dd6f2d397d0886ce7 ] There seems to be a bug within the mv64xxx I2C controller, wherein the status register may not necessarily contain valid value immediately after the IFLG flag is set in the control register. My theory is that the controller: - first sets the IFLG in control register - then updates the status register - then raises an interrupt This may sometime cause weird bugs when in atomic mode, since in this mode we do not wait for an interrupt, but instead we poll the control register for IFLG and read status register immediately after. I encountered -ENXIO from mv64xxx_i2c_fsm() due to this issue when using this driver in atomic mode. Note that I've only seen this issue on Armada 385, I don't know whether other SOCs with this controller are also affected. Also note that this fix has been in U-Boot for over 4 years [1] without anybody complaining, so it should not cause regressions. [1] https://source.denx.de/u-boot/u-boot/-/commit/d50e29662f78 Fixes: 544a8d75f3d6 ("i2c: mv64xxx: Add atomic_xfer method to driver") Signed-off-by: Marek Behún Signed-off-by: Wolfram Sang Signed-off-by: Sasha Levin --- drivers/i2c/busses/i2c-mv64xxx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 047dfef7a657..878c076ebdc6 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -520,6 +520,17 @@ mv64xxx_i2c_intr(int irq, void *dev_id) while (readl(drv_data->reg_base + drv_data->reg_offsets.control) & MV64XXX_I2C_REG_CONTROL_IFLG) { + /* + * It seems that sometime the controller updates the status + * register only after it asserts IFLG in control register. + * This may result in weird bugs when in atomic mode. A delay + * of 100 ns before reading the status register solves this + * issue. This bug does not seem to appear when using + * interrupts. + */ + if (drv_data->atomic) + ndelay(100); + status = readl(drv_data->reg_base + drv_data->reg_offsets.status); mv64xxx_i2c_fsm(drv_data, status); mv64xxx_i2c_do_action(drv_data); -- cgit 1.4.1 From 410689432880e5c6285b58d1d8489d99ee14b5f4 Mon Sep 17 00:00:00 2001 From: Balint Dobszay Date: Thu, 1 Jun 2023 16:07:49 +0200 Subject: firmware: arm_ffa: Set handle field to zero in memory descriptor [ Upstream commit 3aa0519a4780f1b8e11966bd879d4a2934ba455f ] As described in the commit 111a833dc5cb ("firmware: arm_ffa: Set reserved/MBZ fields to zero in the memory descriptors") some fields in the memory descriptor have to be zeroed explicitly. The handle field is one of these, but it was left out from that change, fix this now. Fixes: 111a833dc5cb ("firmware: arm_ffa: Set reserved/MBZ fields to zero in the memory descriptors") Reported-by: Imre Kis Signed-off-by: Balint Dobszay Link: https://lore.kernel.org/r/20230601140749.93812-1-balint.dobszay@arm.com Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 5904a679d351..c37e82359005 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -505,6 +505,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, ep_mem_access->flag = 0; ep_mem_access->reserved = 0; } + mem_region->handle = 0; mem_region->reserved_0 = 0; mem_region->reserved_1 = 0; mem_region->ep_count = args->nattrs; -- cgit 1.4.1 From c53f2e8462f3d60d266d59c476874719a460987b Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 6 Jun 2023 20:00:34 +0800 Subject: gpio: sim: fix memory corruption when adding named lines and unnamed hogs [ Upstream commit 95ae9979bfe3174c2ee8d64409c44532f2881907 ] When constructing the sim, gpio-sim constructs an array of named lines, sized based on the largest offset of any named line, and then initializes that array with the names of all lines, including unnamed hogs with higher offsets. In doing so it writes NULLs beyond the extent of the array. Add a check that only named lines are used to initialize the array. Fixes: cb8c474e79be ("gpio: sim: new testing module") Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-sim.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index cff68f31a09f..803676e307d7 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -717,8 +717,10 @@ static char **gpio_sim_make_line_names(struct gpio_sim_bank *bank, if (!line_names) return ERR_PTR(-ENOMEM); - list_for_each_entry(line, &bank->line_list, siblings) - line_names[line->offset] = line->name; + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->name && (line->offset <= max_offset)) + line_names[line->offset] = line->name; + } return line_names; } -- cgit 1.4.1 From e4b76cd7713c9fc0532bf7ddb997af268d00088f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 9 Mar 2023 10:58:19 +0100 Subject: i2c: sprd: Delete i2c adapter in .remove's error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ca0aa17f2db3468fd017038d23a78e17388e2f67 ] If pm runtime resume fails the .remove callback used to exit early. This resulted in an error message by the driver core but the device gets removed anyhow. This lets the registered i2c adapter stay around with an unbound parent device. So only skip clk disabling if resume failed, but do delete the adapter. Fixes: 8b9ec0719834 ("i2c: Add Spreadtrum I2C controller driver") Signed-off-by: Uwe Kleine-König Reviewed-by: Andi Shyti Signed-off-by: Wolfram Sang Signed-off-by: Sasha Levin --- drivers/i2c/busses/i2c-sprd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index 4fe15cd78907..ffc54fbf814d 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -576,12 +576,14 @@ static int sprd_i2c_remove(struct platform_device *pdev) struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev); int ret; - ret = pm_runtime_resume_and_get(i2c_dev->dev); + ret = pm_runtime_get_sync(i2c_dev->dev); if (ret < 0) - return ret; + dev_err(&pdev->dev, "Failed to resume device (%pe)\n", ERR_PTR(ret)); i2c_del_adapter(&i2c_dev->adap); - clk_disable_unprepare(i2c_dev->clk); + + if (ret >= 0) + clk_disable_unprepare(i2c_dev->clk); pm_runtime_put_noidle(i2c_dev->dev); pm_runtime_disable(i2c_dev->dev); -- cgit 1.4.1 From 67180e079b325de3c6906552619eea65cc596acc Mon Sep 17 00:00:00 2001 From: Hsieh-Tseng Shen Date: Tue, 25 Apr 2023 18:28:28 +0800 Subject: riscv: mm: Ensure prot of VM_WRITE and VM_EXEC must be readable [ Upstream commit 6569fc12e442ea973d96db39e542aa19a7bc3a79 ] Commit 8aeb7b17f04e ("RISC-V: Make mmap() with PROT_WRITE imply PROT_READ") allows riscv to use mmap with PROT_WRITE only, and meanwhile mmap with w+x is also permitted. However, when userspace tries to access this page with PROT_WRITE|PROT_EXEC, which causes infinite loop at load page fault as well as it triggers soft lockup. According to riscv privileged spec, "Writable pages must also be marked readable". The fix to drop the `PAGE_COPY_READ_EXEC` and then `PAGE_COPY_EXEC` would be just used instead. This aligns the other arches (i.e arm64) for protection_map. Fixes: 8aeb7b17f04e ("RISC-V: Make mmap() with PROT_WRITE imply PROT_READ") Signed-off-by: Hsieh-Tseng Shen Reviewed-by: Alexandre Ghiti Link: https://lore.kernel.org/r/20230425102828.1616812-1-woodrow.shen@sifive.com Signed-off-by: Palmer Dabbelt Signed-off-by: Sasha Levin --- arch/riscv/include/asm/pgtable.h | 3 +-- arch/riscv/mm/init.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 2aeaf8e3a4ab..59bb53da473d 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -165,8 +165,7 @@ extern struct pt_alloc_ops pt_ops __initdata; _PAGE_EXEC | _PAGE_WRITE) #define PAGE_COPY PAGE_READ -#define PAGE_COPY_EXEC PAGE_EXEC -#define PAGE_COPY_READ_EXEC PAGE_READ_EXEC +#define PAGE_COPY_EXEC PAGE_READ_EXEC #define PAGE_SHARED PAGE_WRITE #define PAGE_SHARED_EXEC PAGE_WRITE_EXEC diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 2b9906ed2d1d..d8d97df80190 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -286,7 +286,7 @@ static const pgprot_t protection_map[16] = { [VM_EXEC] = PAGE_EXEC, [VM_EXEC | VM_READ] = PAGE_READ_EXEC, [VM_EXEC | VM_WRITE] = PAGE_COPY_EXEC, - [VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY_READ_EXEC, + [VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY_EXEC, [VM_SHARED] = PAGE_NONE, [VM_SHARED | VM_READ] = PAGE_READ, [VM_SHARED | VM_WRITE] = PAGE_SHARED, -- cgit 1.4.1 From e17734900a8810e32369c785d238baa19a0d4c15 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 7 Jun 2023 19:54:24 -0700 Subject: eeprom: at24: also select REGMAP [ Upstream commit 7f3c782b3914e510b646a77aedc3adeac2e4a63b ] Selecting only REGMAP_I2C can leave REGMAP unset, causing build errors, so also select REGMAP to prevent the build errors. ../drivers/misc/eeprom/at24.c:540:42: warning: 'struct regmap_config' declared inside parameter list will not be visible outside of this definition or declaration 540 | struct regmap_config *regmap_config) ../drivers/misc/eeprom/at24.c: In function 'at24_make_dummy_client': ../drivers/misc/eeprom/at24.c:552:18: error: implicit declaration of function 'devm_regmap_init_i2c' [-Werror=implicit-function-declaration] 552 | regmap = devm_regmap_init_i2c(dummy_client, regmap_config); ../drivers/misc/eeprom/at24.c:552:16: warning: assignment to 'struct regmap *' from 'int' makes pointer from integer without a cast [-Wint-conversion] 552 | regmap = devm_regmap_init_i2c(dummy_client, regmap_config); ../drivers/misc/eeprom/at24.c: In function 'at24_probe': ../drivers/misc/eeprom/at24.c:586:16: error: variable 'regmap_config' has initializer but incomplete type 586 | struct regmap_config regmap_config = { }; ../drivers/misc/eeprom/at24.c:586:30: error: storage size of 'regmap_config' isn't known 586 | struct regmap_config regmap_config = { }; ../drivers/misc/eeprom/at24.c:586:30: warning: unused variable 'regmap_config' [-Wunused-variable] Fixes: 5c015258478e ("eeprom: at24: add basic regmap_i2c support") Signed-off-by: Randy Dunlap Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/misc/eeprom/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index f0a7531f354c..2d240bfa819f 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -6,6 +6,7 @@ config EEPROM_AT24 depends on I2C && SYSFS select NVMEM select NVMEM_SYSFS + select REGMAP select REGMAP_I2C help Enable this driver to get read/write support to most I2C EEPROMs -- cgit 1.4.1 From 14e4f37e46e6d0a9997c22a3e73fc8b3de0bc6c1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 2 Jun 2023 11:11:36 +0100 Subject: soundwire: stream: Add missing clear of alloc_slave_rt [ Upstream commit 58d95889f3c2064c6139ee94bb0e4d86e1ad4eab ] The current path that skips allocating the slave runtime does not clear the alloc_slave_rt flag, this is clearly incorrect. Add the missing clear, so the runtime won't be erroneously cleaned up. Fixes: f3016b891c8c ("soundwire: stream: sdw_stream_add_ functions can be called multiple times") Reviewed-by: Pierre-Louis Bossart Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20230602101140.2040141-1-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/stream.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index bd502368339e..b10ea69a638e 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -2019,8 +2019,10 @@ int sdw_stream_add_slave(struct sdw_slave *slave, skip_alloc_master_rt: s_rt = sdw_slave_rt_find(slave, stream); - if (s_rt) + if (s_rt) { + alloc_slave_rt = false; goto skip_alloc_slave_rt; + } s_rt = sdw_slave_rt_alloc(slave, m_rt); if (!s_rt) { -- cgit 1.4.1 From 7e48d635f2745ab3342e421168362b1f4fb5bc61 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Thu, 4 May 2023 15:29:10 +0800 Subject: riscv: fix kprobe __user string arg print fault issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 99a670b2069c725a7b50318aa681d9cae8f89325 ] On riscv qemu platform, when add kprobe event on do_sys_open() to show filename string arg, it just print fault as follow: echo 'p:myprobe do_sys_open dfd=$arg1 filename=+0($arg2):string flags=$arg3 mode=$arg4' > kprobe_events bash-166 [000] ...1. 360.195367: myprobe: (do_sys_open+0x0/0x84) dfd=0xffffffffffffff9c filename=(fault) flags=0x8241 mode=0x1b6 bash-166 [000] ...1. 360.219369: myprobe: (do_sys_open+0x0/0x84) dfd=0xffffffffffffff9c filename=(fault) flags=0x8241 mode=0x1b6 bash-191 [000] ...1. 360.378827: myprobe: (do_sys_open+0x0/0x84) dfd=0xffffffffffffff9c filename=(fault) flags=0x98800 mode=0x0 As riscv do not select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE, the +0($arg2) addr is processed as a kernel address though it is a userspace address, cause the above filename=(fault) print. So select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE to avoid the issue, after that the kprobe trace is ok as below: bash-166 [000] ...1. 96.767641: myprobe: (do_sys_open+0x0/0x84) dfd=0xffffffffffffff9c filename="/dev/null" flags=0x8241 mode=0x1b6 bash-166 [000] ...1. 96.793751: myprobe: (do_sys_open+0x0/0x84) dfd=0xffffffffffffff9c filename="/dev/null" flags=0x8241 mode=0x1b6 bash-177 [000] ...1. 96.962354: myprobe: (do_sys_open+0x0/0x84) dfd=0xffffffffffffff9c filename="/sys/kernel/debug/tracing/events/kprobes/" flags=0x98800 mode=0x0 Signed-off-by: Ruan Jinjie Acked-by: Björn Töpel Fixes: 0ebeea8ca8a4 ("bpf: Restrict bpf_probe_read{, str}() only to archs where they work") Link: https://lore.kernel.org/r/20230504072910.3742842-1-ruanjinjie@huawei.com Signed-off-by: Palmer Dabbelt Signed-off-by: Sasha Levin --- arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 06b9b2f60b9f..a85bbe28dcf4 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -25,6 +25,7 @@ config RISCV select ARCH_HAS_GIGANTIC_PAGE select ARCH_HAS_KCOV select ARCH_HAS_MMIOWB + select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_DIRECT_MAP if MMU select ARCH_HAS_SET_MEMORY if MMU -- cgit 1.4.1 From 6c5a69c5dd53d7624b3eee45702c5c901c4bc785 Mon Sep 17 00:00:00 2001 From: Sheng Zhao Date: Tue, 30 May 2023 11:36:26 +0800 Subject: vduse: avoid empty string for dev name [ Upstream commit a90e8608eb0ed93d31ac0feb055f77ce59512542 ] Syzkaller hits a kernel WARN when the first character of the dev name provided is NULL. Solution is to add a NULL check before calling cdev_device_add() in vduse_create_dev(). kobject: (0000000072042169): attempted to be registered with empty name! WARNING: CPU: 0 PID: 112695 at lib/kobject.c:236 Call Trace: kobject_add_varg linux/src/lib/kobject.c:390 [inline] kobject_add+0xf6/0x150 linux/src/lib/kobject.c:442 device_add+0x28f/0xc20 linux/src/drivers/base/core.c:2167 cdev_device_add+0x83/0xc0 linux/src/fs/char_dev.c:546 vduse_create_dev linux/src/drivers/vdpa/vdpa_user/vduse_dev.c:2254 [inline] vduse_ioctl+0x7b5/0xf30 linux/src/drivers/vdpa/vdpa_user/vduse_dev.c:2316 vfs_ioctl linux/src/fs/ioctl.c:47 [inline] file_ioctl linux/src/fs/ioctl.c:510 [inline] do_vfs_ioctl+0x14b/0xa80 linux/src/fs/ioctl.c:697 ksys_ioctl+0x7c/0xa0 linux/src/fs/ioctl.c:714 __do_sys_ioctl linux/src/fs/ioctl.c:721 [inline] __se_sys_ioctl linux/src/fs/ioctl.c:719 [inline] __x64_sys_ioctl+0x42/0x50 linux/src/fs/ioctl.c:719 do_syscall_64+0x94/0x330 linux/src/arch/x86/entry/common.c:291 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Cc: "Xie Yongji" Reported-by: Xianjun Zeng Signed-off-by: Sheng Zhao Message-Id: <20230530033626.1266794-1-sheng.zhao@bytedance.com> Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang Reviewed-by: Xie Yongji Cc: "Michael S. Tsirkin", "Jason Wang", Reviewed-by: Xie Yongji Signed-off-by: Sasha Levin --- drivers/vdpa/vdpa_user/vduse_dev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 31017ebc4d7c..72f924ec4658 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -1443,6 +1443,9 @@ static bool vduse_validate_config(struct vduse_dev_config *config) if (config->vq_num > 0xffff) return false; + if (!config->name[0]) + return false; + if (!device_is_allowed(config->device_id)) return false; -- cgit 1.4.1 From b839b65456f56958eec6c053e4aff49d64556189 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 24 Apr 2023 15:50:30 -0700 Subject: vhost: support PACKED when setting-getting vring_base [ Upstream commit 55d8122f5cd62d5aaa225d7167dcd14a44c850b9 ] Use the right structs for PACKED or split vqs when setting and getting the vring base. Fixes: 4c8cf31885f6 ("vhost: introduce vDPA-based backend") Signed-off-by: Shannon Nelson Message-Id: <20230424225031.18947-3-shannon.nelson@amd.com> Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang Signed-off-by: Sasha Levin --- drivers/vhost/vhost.c | 18 +++++++++++++----- drivers/vhost/vhost.h | 8 ++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 43c9770b86e5..1a059b028c50 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1633,17 +1633,25 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg r = -EFAULT; break; } - if (s.num > 0xffff) { - r = -EINVAL; - break; + if (vhost_has_feature(vq, VIRTIO_F_RING_PACKED)) { + vq->last_avail_idx = s.num & 0xffff; + vq->last_used_idx = (s.num >> 16) & 0xffff; + } else { + if (s.num > 0xffff) { + r = -EINVAL; + break; + } + vq->last_avail_idx = s.num; } - vq->last_avail_idx = s.num; /* Forget the cached index value. */ vq->avail_idx = vq->last_avail_idx; break; case VHOST_GET_VRING_BASE: s.index = idx; - s.num = vq->last_avail_idx; + if (vhost_has_feature(vq, VIRTIO_F_RING_PACKED)) + s.num = (u32)vq->last_avail_idx | ((u32)vq->last_used_idx << 16); + else + s.num = vq->last_avail_idx; if (copy_to_user(argp, &s, sizeof s)) r = -EFAULT; break; diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 790b296271f1..5e17c4aa7374 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -85,13 +85,17 @@ struct vhost_virtqueue { /* The routine to call when the Guest pings us, or timeout. */ vhost_work_fn_t handle_kick; - /* Last available index we saw. */ + /* Last available index we saw. + * Values are limited to 0x7fff, and the high bit is used as + * a wrap counter when using VIRTIO_F_RING_PACKED. */ u16 last_avail_idx; /* Caches available index value from user. */ u16 avail_idx; - /* Last index we used. */ + /* Last index we used. + * Values are limited to 0x7fff, and the high bit is used as + * a wrap counter when using VIRTIO_F_RING_PACKED. */ u16 last_used_idx; /* Used flags */ -- cgit 1.4.1 From fb322da83ce538bdce0f65029d1f0008ac70cda8 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 24 Apr 2023 15:50:31 -0700 Subject: vhost_vdpa: support PACKED when setting-getting vring_base [ Upstream commit beee7fdb5b56a46415a4992d28dd4c2d06eb52df ] Use the right structs for PACKED or split vqs when setting and getting the vring base. Fixes: 4c8cf31885f6 ("vhost: introduce vDPA-based backend") Signed-off-by: Shannon Nelson Message-Id: <20230424225031.18947-4-shannon.nelson@amd.com> Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang Signed-off-by: Sasha Levin --- drivers/vhost/vdpa.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 6f532da59e08..d591f77961aa 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -549,7 +549,14 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, if (r) return r; - vq->last_avail_idx = vq_state.split.avail_index; + if (vhost_has_feature(vq, VIRTIO_F_RING_PACKED)) { + vq->last_avail_idx = vq_state.packed.last_avail_idx | + (vq_state.packed.last_avail_counter << 15); + vq->last_used_idx = vq_state.packed.last_used_idx | + (vq_state.packed.last_used_counter << 15); + } else { + vq->last_avail_idx = vq_state.split.avail_index; + } break; } @@ -567,9 +574,15 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, break; case VHOST_SET_VRING_BASE: - vq_state.split.avail_index = vq->last_avail_idx; - if (ops->set_vq_state(vdpa, idx, &vq_state)) - r = -EINVAL; + if (vhost_has_feature(vq, VIRTIO_F_RING_PACKED)) { + vq_state.packed.last_avail_idx = vq->last_avail_idx & 0x7fff; + vq_state.packed.last_avail_counter = !!(vq->last_avail_idx & 0x8000); + vq_state.packed.last_used_idx = vq->last_used_idx & 0x7fff; + vq_state.packed.last_used_counter = !!(vq->last_used_idx & 0x8000); + } else { + vq_state.split.avail_index = vq->last_avail_idx; + } + r = ops->set_vq_state(vdpa, idx, &vq_state); break; case VHOST_SET_VRING_CALL: -- cgit 1.4.1 From bf12d7fb63b365fb766655cedcb5d5f292b0c35e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 28 May 2023 00:23:09 +0900 Subject: ksmbd: fix out-of-bound read in deassemble_neg_contexts() commit f1a411873c85b642f13b01f21b534c2bab81fc1b upstream. The check in the beginning is `clen + sizeof(struct smb2_neg_context) <= len_of_ctxts`, but in the end of loop, `len_of_ctxts` will subtract `((clen + 7) & ~0x7) + sizeof(struct smb2_neg_context)`, which causes integer underflow when clen does the 8 alignment. We should use `(clen + 7) & ~0x7` in the check to avoid underflow from happening. Then there are some variables that need to be declared unsigned instead of signed. [ 11.671070] BUG: KASAN: slab-out-of-bounds in smb2_handle_negotiate+0x799/0x1610 [ 11.671533] Read of size 2 at addr ffff888005e86cf2 by task kworker/0:0/7 ... [ 11.673383] Call Trace: [ 11.673541] [ 11.673679] dump_stack_lvl+0x33/0x50 [ 11.673913] print_report+0xcc/0x620 [ 11.674671] kasan_report+0xae/0xe0 [ 11.675171] kasan_check_range+0x35/0x1b0 [ 11.675412] smb2_handle_negotiate+0x799/0x1610 [ 11.676217] ksmbd_smb_negotiate_common+0x526/0x770 [ 11.676795] handle_ksmbd_work+0x274/0x810 ... Cc: stable@vger.kernel.org Signed-off-by: Chih-Yen Chang Tested-by: Chih-Yen Chang Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2pdu.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 01887570efe8..56f661e5ef62 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -979,13 +979,13 @@ static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, struct smb2_negotiate_req *req, - int len_of_smb) + unsigned int len_of_smb) { /* +4 is to account for the RFC1001 len field */ struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; int i = 0, len_of_ctxts; - int offset = le32_to_cpu(req->NegotiateContextOffset); - int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); + unsigned int offset = le32_to_cpu(req->NegotiateContextOffset); + unsigned int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); __le32 status = STATUS_INVALID_PARAMETER; ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); @@ -999,7 +999,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, while (i++ < neg_ctxt_cnt) { int clen, ctxt_len; - if (len_of_ctxts < sizeof(struct smb2_neg_context)) + if (len_of_ctxts < (int)sizeof(struct smb2_neg_context)) break; pctx = (struct smb2_neg_context *)((char *)pctx + offset); @@ -1054,9 +1054,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, } /* offsets must be 8 byte aligned */ - clen = (clen + 7) & ~0x7; - offset = clen + sizeof(struct smb2_neg_context); - len_of_ctxts -= clen + sizeof(struct smb2_neg_context); + offset = (ctxt_len + 7) & ~0x7; + len_of_ctxts -= offset; } return status; } -- cgit 1.4.1 From 8f2984233c87a1d08f4c45f077130590c7a2c991 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 28 May 2023 00:23:41 +0900 Subject: ksmbd: fix out-of-bound read in parse_lease_state() commit fc6c6a3c324c1b3e93a03d0cfa3749c781f23de0 upstream. This bug is in parse_lease_state, and it is caused by the missing check of `struct create_context`. When the ksmbd traverses the create_contexts, it doesn't check if the field of `NameOffset` and `Next` is valid, The KASAN message is following: [ 6.664323] BUG: KASAN: slab-out-of-bounds in parse_lease_state+0x7d/0x280 [ 6.664738] Read of size 2 at addr ffff888005c08988 by task kworker/0:3/103 ... [ 6.666644] Call Trace: [ 6.666796] [ 6.666933] dump_stack_lvl+0x33/0x50 [ 6.667167] print_report+0xcc/0x620 [ 6.667903] kasan_report+0xae/0xe0 [ 6.668374] kasan_check_range+0x35/0x1b0 [ 6.668621] parse_lease_state+0x7d/0x280 [ 6.668868] smb2_open+0xbe8/0x4420 [ 6.675137] handle_ksmbd_work+0x282/0x820 Use smb2_find_context_vals() to find smb2 create request lease context. smb2_find_context_vals validate create context fields. Cc: stable@vger.kernel.org Reported-by: Chih-Yen Chang Tested-by: Chih-Yen Chang Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/oplock.c | 66 ++++++++++++++++++++----------------------------------- 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index cbfed03a1e5c..4b210cdd7556 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1415,56 +1415,38 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) */ struct lease_ctx_info *parse_lease_state(void *open_req) { - char *data_offset; struct create_context *cc; - unsigned int next = 0; - char *name; - bool found = false; struct smb2_create_req *req = (struct smb2_create_req *)open_req; - struct lease_ctx_info *lreq = kzalloc(sizeof(struct lease_ctx_info), - GFP_KERNEL); + struct lease_ctx_info *lreq; + + cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); + if (IS_ERR_OR_NULL(cc)) + return NULL; + + lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); if (!lreq) return NULL; - data_offset = (char *)req + le32_to_cpu(req->CreateContextsOffset); - cc = (struct create_context *)data_offset; - do { - cc = (struct create_context *)((char *)cc + next); - name = le16_to_cpu(cc->NameOffset) + (char *)cc; - if (le16_to_cpu(cc->NameLength) != 4 || - strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { - next = le32_to_cpu(cc->Next); - continue; - } - found = true; - break; - } while (next != 0); + if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; - if (found) { - if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { - struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; - - memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; - memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, - SMB2_LEASE_KEY_SIZE); - lreq->version = 2; - } else { - struct create_lease *lc = (struct create_lease *)cc; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); + lreq->version = 2; + } else { + struct create_lease *lc = (struct create_lease *)cc; - memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; - lreq->version = 1; - } - return lreq; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + lreq->version = 1; } - - kfree(lreq); - return NULL; + return lreq; } /** -- cgit 1.4.1 From 543c12c2644e772caa6880662c2a852cfdc5a10c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 May 2023 23:10:31 +0900 Subject: ksmbd: check the validation of pdu_size in ksmbd_conn_handler_loop commit 368ba06881c395f1c9a7ba22203cf8d78b4addc0 upstream. The length field of netbios header must be greater than the SMB header sizes(smb1 or smb2 header), otherwise the packet is an invalid SMB packet. If `pdu_size` is 0, ksmbd allocates a 4 bytes chunk to `conn->request_buf`. In the function `get_smb2_cmd_val` ksmbd will read cmd from `rcv_hdr->Command`, which is `conn->request_buf + 12`, causing the KASAN detector to print the following error message: [ 7.205018] BUG: KASAN: slab-out-of-bounds in get_smb2_cmd_val+0x45/0x60 [ 7.205423] Read of size 2 at addr ffff8880062d8b50 by task ksmbd:42632/248 ... [ 7.207125] [ 7.209191] get_smb2_cmd_val+0x45/0x60 [ 7.209426] ksmbd_conn_enqueue_request+0x3a/0x100 [ 7.209712] ksmbd_server_process_request+0x72/0x160 [ 7.210295] ksmbd_conn_handler_loop+0x30c/0x550 [ 7.212280] kthread+0x160/0x190 [ 7.212762] ret_from_fork+0x1f/0x30 [ 7.212981] Cc: stable@vger.kernel.org Reported-by: Chih-Yen Chang Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/connection.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index b371754540f9..bf8531b80a18 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -296,6 +296,9 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) return true; } +#define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) +#define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4) + /** * ksmbd_conn_handler_loop() - session thread to listen on new smb requests * @p: connection instance @@ -352,6 +355,9 @@ int ksmbd_conn_handler_loop(void *p) if (pdu_size > MAX_STREAM_PROT_LEN) break; + if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE) + break; + /* 4 for rfc1002 length field */ /* 1 for implied bcc[0] */ size = pdu_size + 4 + 1; @@ -379,6 +385,12 @@ int ksmbd_conn_handler_loop(void *p) continue; } + if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == + SMB2_PROTO_NUMBER) { + if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) + break; + } + if (!default_conn_ops.process_fn) { pr_err("No connection request callback\n"); break; -- cgit 1.4.1 From 77eed67ba24ea194cf8d2ec0168a23155b07d78b Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 8 Jun 2023 09:57:04 -0400 Subject: Revert "ext4: don't clear SB_RDONLY when remounting r/w until quota is re-enabled" commit 1b29243933098cdbc31b579b5616e183b4275e2f upstream. This reverts commit a44be64bbecb15a452496f60db6eacfee2b59c79. Link: https://lore.kernel.org/r/653b3359-2005-21b1-039d-c55ca4cffdcc@gmail.com Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 5db1060b8684..760249d9152d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -6341,7 +6341,6 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) struct ext4_mount_options old_opts; ext4_group_t g; int err = 0; - int enable_rw = 0; #ifdef CONFIG_QUOTA int enable_quota = 0; int i, j; @@ -6528,7 +6527,7 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) if (err) goto restore_opts; - enable_rw = 1; + sb->s_flags &= ~SB_RDONLY; if (ext4_has_feature_mmp(sb)) { err = ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block)); @@ -6575,9 +6574,6 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb) if (!test_opt(sb, BLOCK_VALIDITY) && sbi->s_system_blks) ext4_release_system_zone(sb); - if (enable_rw) - sb->s_flags &= ~SB_RDONLY; - /* * Reinitialize lazy itable initialization thread based on * current settings -- cgit 1.4.1 From 5b2438f0a7e58acc1e47941cec4b353770e7488e Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 8 Jun 2023 10:06:40 -0400 Subject: ext4: only check dquot_initialize_needed() when debugging commit dea9d8f7643fab07bf89a1155f1f94f37d096a5e upstream. ext4_xattr_block_set() relies on its caller to call dquot_initialize() on the inode. To assure that this has happened there are WARN_ON checks. Unfortunately, this is subject to false positives if there is an antagonist thread which is flipping the file system at high rates between r/o and rw. So only do the check if EXT4_XATTR_DEBUG is enabled. Link: https://lore.kernel.org/r/20230608044056.GA1418535@mit.edu Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/xattr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index fddde5045d0c..51d642a95bd2 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -2006,8 +2006,9 @@ inserted: else { u32 ref; +#ifdef EXT4_XATTR_DEBUG WARN_ON_ONCE(dquot_initialize_needed(inode)); - +#endif /* The old block is released after updating the inode. */ error = dquot_alloc_block(inode, @@ -2070,8 +2071,9 @@ inserted: /* We need to allocate a new block */ ext4_fsblk_t goal, block; +#ifdef EXT4_XATTR_DEBUG WARN_ON_ONCE(dquot_initialize_needed(inode)); - +#endif goal = ext4_group_first_block_no(sb, EXT4_I(inode)->i_block_group); block = ext4_new_meta_blocks(handle, inode, goal, 0, -- cgit 1.4.1 From 8fafd871558ff5bd90382206e113aff646d3f1e3 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sat, 27 May 2023 16:29:38 +0800 Subject: wifi: rtw89: correct PS calculation for SUPPORTS_DYNAMIC_PS commit 26a125f550a3bf86ac91d38752f4d446426dfe1c upstream. This driver relies on IEEE80211_CONF_PS of hw->conf.flags to turn off PS or turn on dynamic PS controlled by driver and firmware. Though this would be incorrect, it did work before because the flag is always recalculated until the commit 28977e790b5d ("wifi: mac80211: skip powersave recalc if driver SUPPORTS_DYNAMIC_PS") is introduced by kernel 5.20 to skip to recalculate IEEE80211_CONF_PS of hw->conf.flags if driver sets SUPPORTS_DYNAMIC_PS. Correct this by doing recalculation while BSS_CHANGED_PS is changed and interface is added or removed. For now, it is allowed to enter PS only if single one station vif is working, and it could possible to have PS per vif after firmware can support it. Without this fix, driver doesn't enter PS anymore that causes higher power consumption. Fixes: e3ec7017f6a2 ("rtw89: add Realtek 802.11ax driver") Cc: stable@vger.kernel.org # 6.1+ Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230527082939.11206-3-pkshih@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw89/mac80211.c | 16 +++++++--------- drivers/net/wireless/realtek/rtw89/ps.c | 26 ++++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/ps.h | 1 + 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index a296bfa8188f..a8f478f0cde9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -78,15 +78,6 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) !(hw->conf.flags & IEEE80211_CONF_IDLE)) rtw89_leave_ips(rtwdev); - if (changed & IEEE80211_CONF_CHANGE_PS) { - if (hw->conf.flags & IEEE80211_CONF_PS) { - rtwdev->lps_enabled = true; - } else { - rtw89_leave_lps(rtwdev); - rtwdev->lps_enabled = false; - } - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { rtw89_config_entity_chandef(rtwdev, RTW89_SUB_ENTITY_0, &hw->conf.chandef); @@ -142,6 +133,8 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, rtw89_core_txq_init(rtwdev, vif->txq); rtw89_btc_ntfy_role_info(rtwdev, rtwvif, NULL, BTC_ROLE_START); + + rtw89_recalc_lps(rtwdev); out: mutex_unlock(&rtwdev->mutex); @@ -165,6 +158,8 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw, rtw89_mac_remove_vif(rtwdev, rtwvif); rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif->port); list_del_init(&rtwvif->list); + rtw89_recalc_lps(rtwdev); + mutex_unlock(&rtwdev->mutex); } @@ -411,6 +406,9 @@ static void rtw89_ops_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_P2P_PS) rtw89_process_p2p_ps(rtwdev, vif); + if (changed & BSS_CHANGED_PS) + rtw89_recalc_lps(rtwdev); + mutex_unlock(&rtwdev->mutex); } diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index bf41a1141679..7cf9f80c7a93 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -244,3 +244,29 @@ void rtw89_process_p2p_ps(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif) rtw89_p2p_disable_all_noa(rtwdev, vif); rtw89_p2p_update_noa(rtwdev, vif); } + +void rtw89_recalc_lps(struct rtw89_dev *rtwdev) +{ + struct ieee80211_vif *vif, *found_vif = NULL; + struct rtw89_vif *rtwvif; + int count = 0; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + vif = rtwvif_to_vif(rtwvif); + + if (vif->type != NL80211_IFTYPE_STATION) { + count = 0; + break; + } + + count++; + found_vif = vif; + } + + if (count == 1 && found_vif->cfg.ps) { + rtwdev->lps_enabled = true; + } else { + rtw89_leave_lps(rtwdev); + rtwdev->lps_enabled = false; + } +} diff --git a/drivers/net/wireless/realtek/rtw89/ps.h b/drivers/net/wireless/realtek/rtw89/ps.h index 0feae3991623..49fb9d8b6546 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.h +++ b/drivers/net/wireless/realtek/rtw89/ps.h @@ -13,5 +13,6 @@ void rtw89_enter_ips(struct rtw89_dev *rtwdev); void rtw89_leave_ips(struct rtw89_dev *rtwdev); void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl); void rtw89_process_p2p_ps(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif); +void rtw89_recalc_lps(struct rtw89_dev *rtwdev); #endif -- cgit 1.4.1 From a7e9c2e407080496686f88a707676b84d50ee8ec Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sat, 27 May 2023 16:29:37 +0800 Subject: wifi: rtw88: correct PS calculation for SUPPORTS_DYNAMIC_PS commit 3918dd0177ee08970683a2c22a3388825d82fd79 upstream. This driver relies on IEEE80211_CONF_PS of hw->conf.flags to turn off PS or turn on dynamic PS controlled by driver and firmware. Though this would be incorrect, it did work before because the flag is always recalculated until the commit 28977e790b5d ("wifi: mac80211: skip powersave recalc if driver SUPPORTS_DYNAMIC_PS") is introduced by kernel 5.20 to skip to recalculate IEEE80211_CONF_PS of hw->conf.flags if driver sets SUPPORTS_DYNAMIC_PS. Correct this by doing recalculation while BSS_CHANGED_PS is changed and interface is added or removed. It is allowed to enter PS only if single one station vif is working. Without this fix, driver doesn't enter PS anymore that causes higher power consumption. Fixes: bcde60e599fb ("rtw88: remove misleading module parameter rtw_fw_support_lps") Cc: stable@vger.kernel.org # 6.1+ Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230527082939.11206-2-pkshih@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw88/mac80211.c | 14 ++++----- drivers/net/wireless/realtek/rtw88/main.c | 4 +-- drivers/net/wireless/realtek/rtw88/ps.c | 43 +++++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw88/ps.h | 2 ++ 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index 62fb28f14c94..fabca307867a 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -88,15 +88,6 @@ static int rtw_ops_config(struct ieee80211_hw *hw, u32 changed) } } - if (changed & IEEE80211_CONF_CHANGE_PS) { - if (hw->conf.flags & IEEE80211_CONF_PS) { - rtwdev->ps_enabled = true; - } else { - rtwdev->ps_enabled = false; - rtw_leave_lps(rtwdev); - } - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) rtw_set_channel(rtwdev); @@ -206,6 +197,7 @@ static int rtw_ops_add_interface(struct ieee80211_hw *hw, rtwvif->bcn_ctrl = bcn_ctrl; config |= PORT_SET_BCN_CTRL; rtw_vif_port_config(rtwdev, rtwvif, config); + rtw_recalc_lps(rtwdev, vif); mutex_unlock(&rtwdev->mutex); @@ -236,6 +228,7 @@ static void rtw_ops_remove_interface(struct ieee80211_hw *hw, rtwvif->bcn_ctrl = 0; config |= PORT_SET_BCN_CTRL; rtw_vif_port_config(rtwdev, rtwvif, config); + rtw_recalc_lps(rtwdev, NULL); mutex_unlock(&rtwdev->mutex); } @@ -428,6 +421,9 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ERP_SLOT) rtw_conf_tx(rtwdev, rtwvif); + if (changed & BSS_CHANGED_PS) + rtw_recalc_lps(rtwdev, NULL); + rtw_vif_port_config(rtwdev, rtwvif, config); mutex_unlock(&rtwdev->mutex); diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 8080ace5ed51..4c8164db4a9e 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -248,8 +248,8 @@ static void rtw_watch_dog_work(struct work_struct *work) * more than two stations associated to the AP, then we can not enter * lps, because fw does not handle the overlapped beacon interval * - * mac80211 should iterate vifs and determine if driver can enter - * ps by passing IEEE80211_CONF_PS to us, all we need to do is to + * rtw_recalc_lps() iterate vifs and determine if driver can enter + * ps by vif->type and vif->cfg.ps, all we need to do here is to * get that vif and check if device is having traffic more than the * threshold. */ diff --git a/drivers/net/wireless/realtek/rtw88/ps.c b/drivers/net/wireless/realtek/rtw88/ps.c index dc0d85218245..7ac35c368d7a 100644 --- a/drivers/net/wireless/realtek/rtw88/ps.c +++ b/drivers/net/wireless/realtek/rtw88/ps.c @@ -299,3 +299,46 @@ void rtw_leave_lps_deep(struct rtw_dev *rtwdev) __rtw_leave_lps_deep(rtwdev); } + +struct rtw_vif_recalc_lps_iter_data { + struct rtw_dev *rtwdev; + struct ieee80211_vif *found_vif; + int count; +}; + +static void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data, + struct ieee80211_vif *vif) +{ + if (data->count < 0) + return; + + if (vif->type != NL80211_IFTYPE_STATION) { + data->count = -1; + return; + } + + data->count++; + data->found_vif = vif; +} + +static void rtw_vif_recalc_lps_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + __rtw_vif_recalc_lps(data, vif); +} + +void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif) +{ + struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev }; + + if (new_vif) + __rtw_vif_recalc_lps(&data, new_vif); + rtw_iterate_vifs(rtwdev, rtw_vif_recalc_lps_iter, &data); + + if (data.count == 1 && data.found_vif->cfg.ps) { + rtwdev->ps_enabled = true; + } else { + rtwdev->ps_enabled = false; + rtw_leave_lps(rtwdev); + } +} diff --git a/drivers/net/wireless/realtek/rtw88/ps.h b/drivers/net/wireless/realtek/rtw88/ps.h index c194386f6db5..5ae83d2526cf 100644 --- a/drivers/net/wireless/realtek/rtw88/ps.h +++ b/drivers/net/wireless/realtek/rtw88/ps.h @@ -23,4 +23,6 @@ void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id); void rtw_leave_lps(struct rtw_dev *rtwdev); void rtw_leave_lps_deep(struct rtw_dev *rtwdev); enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev); +void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif); + #endif -- cgit 1.4.1 From 1aaa74177f062f978c33f8547436326d1fd1be1e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 12 Jun 2023 12:06:32 +0200 Subject: Revert "staging: rtl8192e: Replace macro RTL_PCI_DEVICE with PCI_DEVICE" This reverts commit ec310591cf839653a5b2c1fcf6b8a110c3f2485c which is commit fda2093860df4812d69052a8cf4997e53853a340 upstream. Ben reports that this should not have been backported to the older kernels as the rest of the macro is not empty. It was a clean-up patch in 6.4-rc1 only, it did not add new device ids. Reported-by: Ben Hutchings Cc: Philipp Hortmann Cc: Sasha Levin Link: https://lore.kernel.org/r/aa0d401a7f63448cd4c2fe4a2d7e8495d9aa123e.camel@decadent.org.uk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8192e/rtl8192e/rtl_core.c | 6 +++--- drivers/staging/rtl8192e/rtl8192e/rtl_core.h | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index 7e11364d718b..c1e50084172d 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -48,9 +48,9 @@ static const struct rtl819x_ops rtl819xp_ops = { }; static struct pci_device_id rtl8192_pci_id_tbl[] = { - {PCI_DEVICE(0x10ec, 0x8192)}, - {PCI_DEVICE(0x07aa, 0x0044)}, - {PCI_DEVICE(0x07aa, 0x0047)}, + {RTL_PCI_DEVICE(0x10ec, 0x8192, rtl819xp_ops)}, + {RTL_PCI_DEVICE(0x07aa, 0x0044, rtl819xp_ops)}, + {RTL_PCI_DEVICE(0x07aa, 0x0047, rtl819xp_ops)}, {} }; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h index 50f1ec78cc45..7021f9c435d9 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h @@ -55,6 +55,11 @@ #define IS_HARDWARE_TYPE_8192SE(_priv) \ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192SE) +#define RTL_PCI_DEVICE(vend, dev, cfg) \ + .vendor = (vend), .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ + .driver_data = (kernel_ulong_t)&(cfg) + #define TOTAL_CAM_ENTRY 32 #define CAM_CONTENT_COUNT 8 -- cgit 1.4.1 From ca87e77a2ef8b298aa9f69658d5898e72ee450fe Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jun 2023 11:15:34 +0200 Subject: Linux 6.1.34 Link: https://lore.kernel.org/r/20230612101710.279705932@linuxfoundation.org Tested-by: Chris Paterson (CIP) Tested-by: Shuah Khan Tested-by: Bagas Sanjaya Tested-by: Linux Kernel Functional Testing Tested-by: Jon Hunter Tested-by: Conor Dooley Tested-by: Takeshi Ogasawara Tested-by: Sudip Mukherjee Tested-by: Salvatore Bonaccorso Tested-by: Guenter Roeck Tested-by: Ron Economos Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6be79c5c934f..bc7cc17b0e75 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 33 +SUBLEVEL = 34 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From 100cd6d0e563f393e10dee825c1a865f308fc324 Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Wed, 17 May 2023 11:26:41 -0500 Subject: x86/head/64: Switch to KERNEL_CS as soon as new GDT is installed [ Upstream commit a37f2699c36a7f6606ba3300f243227856c5ad6b ] The call to startup_64_setup_env() will install a new GDT but does not actually switch to using the KERNEL_CS entry until returning from the function call. Commit bcce82908333 ("x86/sev: Detect/setup SEV/SME features earlier in boot") moved the call to sme_enable() earlier in the boot process and in between the call to startup_64_setup_env() and the switch to KERNEL_CS. An SEV-ES or an SEV-SNP guest will trigger #VC exceptions during the call to sme_enable() and if the CS pushed on the stack as part of the exception and used by IRETQ is not mapped by the new GDT, then problems occur. Today, the current CS when entering startup_64 is the kernel CS value because it was set up by the decompressor code, so no issue is seen. However, a recent patchset that looked to avoid using the legacy decompressor during an EFI boot exposed this bug. At entry to startup_64, the CS value is that of EFI and is not mapped in the new kernel GDT. So when a #VC exception occurs, the CS value used by IRETQ is not valid and the guest boot crashes. Fix this issue by moving the block that switches to the KERNEL_CS value to be done immediately after returning from startup_64_setup_env(). Fixes: bcce82908333 ("x86/sev: Detect/setup SEV/SME features earlier in boot") Signed-off-by: Tom Lendacky Signed-off-by: Dave Hansen Reviewed-by: Joerg Roedel Link: https://lore.kernel.org/all/6ff1f28af2829cc9aea357ebee285825f90a431f.1684340801.git.thomas.lendacky%40amd.com Signed-off-by: Sasha Levin --- arch/x86/kernel/head_64.S | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index d860d437631b..998cdb112b72 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S @@ -85,6 +85,15 @@ SYM_CODE_START_NOALIGN(startup_64) call startup_64_setup_env popq %rsi + /* Now switch to __KERNEL_CS so IRET works reliably */ + pushq $__KERNEL_CS + leaq .Lon_kernel_cs(%rip), %rax + pushq %rax + lretq + +.Lon_kernel_cs: + UNWIND_HINT_EMPTY + #ifdef CONFIG_AMD_MEM_ENCRYPT /* * Activate SEV/SME memory encryption if supported/enabled. This needs to @@ -98,15 +107,6 @@ SYM_CODE_START_NOALIGN(startup_64) popq %rsi #endif - /* Now switch to __KERNEL_CS so IRET works reliably */ - pushq $__KERNEL_CS - leaq .Lon_kernel_cs(%rip), %rax - pushq %rax - lretq - -.Lon_kernel_cs: - UNWIND_HINT_EMPTY - /* Sanitize CPU configuration */ call verify_cpu -- cgit 1.4.1 From aa2dfdc4ed2594d5cf7bfe1cc914b53bf440a5e4 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 14 Jan 2023 10:22:03 +0100 Subject: test_firmware: Use kstrtobool() instead of strtobool() [ Upstream commit f7d85515bd21902b218370a1a6301f76e4e636ff ] strtobool() is the same as kstrtobool(). However, the latter is more used within the kernel. In order to remove strtobool() and slightly simplify kstrtox.h, switch to the other function name. While at it, include the corresponding header file () Signed-off-by: Christophe JAILLET Acked-by: Luis Chamberlain Link: https://lore.kernel.org/r/34f04735d20e0138695dd4070651bd860a36b81c.1673688120.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 4acfe3dfde68 ("test_firmware: prevent race conditions by a correct implementation of locking") Signed-off-by: Sasha Levin --- lib/test_firmware.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 7f165c517338..6ef3e6926da8 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -365,7 +366,7 @@ static int test_dev_config_update_bool(const char *buf, size_t size, int ret; mutex_lock(&test_fw_mutex); - if (strtobool(buf, cfg) < 0) + if (kstrtobool(buf, cfg) < 0) ret = -EINVAL; else ret = size; -- cgit 1.4.1 From 6111f0add6ffc93612d4abe9fec002319102b1c0 Mon Sep 17 00:00:00 2001 From: Mirsad Goran Todorovac Date: Tue, 9 May 2023 10:47:45 +0200 Subject: test_firmware: prevent race conditions by a correct implementation of locking [ Upstream commit 4acfe3dfde685a5a9eaec5555351918e2d7266a1 ] Dan Carpenter spotted a race condition in a couple of situations like these in the test_firmware driver: static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) { u8 val; int ret; ret = kstrtou8(buf, 10, &val); if (ret) return ret; mutex_lock(&test_fw_mutex); *(u8 *)cfg = val; mutex_unlock(&test_fw_mutex); /* Always return full write size even if we didn't consume all */ return size; } static ssize_t config_num_requests_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc; mutex_lock(&test_fw_mutex); if (test_fw_config->reqs) { pr_err("Must call release_all_firmware prior to changing config\n"); rc = -EINVAL; mutex_unlock(&test_fw_mutex); goto out; } mutex_unlock(&test_fw_mutex); rc = test_dev_config_update_u8(buf, count, &test_fw_config->num_requests); out: return rc; } static ssize_t config_read_fw_idx_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return test_dev_config_update_u8(buf, count, &test_fw_config->read_fw_idx); } The function test_dev_config_update_u8() is called from both the locked and the unlocked context, function config_num_requests_store() and config_read_fw_idx_store() which can both be called asynchronously as they are driver's methods, while test_dev_config_update_u8() and siblings change their argument pointed to by u8 *cfg or similar pointer. To avoid deadlock on test_fw_mutex, the lock is dropped before calling test_dev_config_update_u8() and re-acquired within test_dev_config_update_u8() itself, but alas this creates a race condition. Having two locks wouldn't assure a race-proof mutual exclusion. This situation is best avoided by the introduction of a new, unlocked function __test_dev_config_update_u8() which can be called from the locked context and reducing test_dev_config_update_u8() to: static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) { int ret; mutex_lock(&test_fw_mutex); ret = __test_dev_config_update_u8(buf, size, cfg); mutex_unlock(&test_fw_mutex); return ret; } doing the locking and calling the unlocked primitive, which enables both locked and unlocked versions without duplication of code. The similar approach was applied to all functions called from the locked and the unlocked context, which safely mitigates both deadlocks and race conditions in the driver. __test_dev_config_update_bool(), __test_dev_config_update_u8() and __test_dev_config_update_size_t() unlocked versions of the functions were introduced to be called from the locked contexts as a workaround without releasing the main driver's lock and thereof causing a race condition. The test_dev_config_update_bool(), test_dev_config_update_u8() and test_dev_config_update_size_t() locked versions of the functions are being called from driver methods without the unnecessary multiplying of the locking and unlocking code for each method, and complicating the code with saving of the return value across lock. Fixes: 7feebfa487b92 ("test_firmware: add support for request_firmware_into_buf") Cc: Luis Chamberlain Cc: Greg Kroah-Hartman Cc: Russ Weight Cc: Takashi Iwai Cc: Tianfei Zhang Cc: Shuah Khan Cc: Colin Ian King Cc: Randy Dunlap Cc: linux-kselftest@vger.kernel.org Cc: stable@vger.kernel.org # v5.4 Suggested-by: Dan Carpenter Signed-off-by: Mirsad Goran Todorovac Link: https://lore.kernel.org/r/20230509084746.48259-1-mirsad.todorovac@alu.unizg.hr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- lib/test_firmware.c | 52 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 6ef3e6926da8..13d3fa6aa972 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -360,16 +360,26 @@ static ssize_t config_test_show_str(char *dst, return len; } -static int test_dev_config_update_bool(const char *buf, size_t size, +static inline int __test_dev_config_update_bool(const char *buf, size_t size, bool *cfg) { int ret; - mutex_lock(&test_fw_mutex); if (kstrtobool(buf, cfg) < 0) ret = -EINVAL; else ret = size; + + return ret; +} + +static int test_dev_config_update_bool(const char *buf, size_t size, + bool *cfg) +{ + int ret; + + mutex_lock(&test_fw_mutex); + ret = __test_dev_config_update_bool(buf, size, cfg); mutex_unlock(&test_fw_mutex); return ret; @@ -380,7 +390,8 @@ static ssize_t test_dev_config_show_bool(char *buf, bool val) return snprintf(buf, PAGE_SIZE, "%d\n", val); } -static int test_dev_config_update_size_t(const char *buf, +static int __test_dev_config_update_size_t( + const char *buf, size_t size, size_t *cfg) { @@ -391,9 +402,7 @@ static int test_dev_config_update_size_t(const char *buf, if (ret) return ret; - mutex_lock(&test_fw_mutex); *(size_t *)cfg = new; - mutex_unlock(&test_fw_mutex); /* Always return full write size even if we didn't consume all */ return size; @@ -409,7 +418,7 @@ static ssize_t test_dev_config_show_int(char *buf, int val) return snprintf(buf, PAGE_SIZE, "%d\n", val); } -static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) +static int __test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) { u8 val; int ret; @@ -418,14 +427,23 @@ static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) if (ret) return ret; - mutex_lock(&test_fw_mutex); *(u8 *)cfg = val; - mutex_unlock(&test_fw_mutex); /* Always return full write size even if we didn't consume all */ return size; } +static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) +{ + int ret; + + mutex_lock(&test_fw_mutex); + ret = __test_dev_config_update_u8(buf, size, cfg); + mutex_unlock(&test_fw_mutex); + + return ret; +} + static ssize_t test_dev_config_show_u8(char *buf, u8 val) { return snprintf(buf, PAGE_SIZE, "%u\n", val); @@ -478,10 +496,10 @@ static ssize_t config_num_requests_store(struct device *dev, mutex_unlock(&test_fw_mutex); goto out; } - mutex_unlock(&test_fw_mutex); - rc = test_dev_config_update_u8(buf, count, - &test_fw_config->num_requests); + rc = __test_dev_config_update_u8(buf, count, + &test_fw_config->num_requests); + mutex_unlock(&test_fw_mutex); out: return rc; @@ -525,10 +543,10 @@ static ssize_t config_buf_size_store(struct device *dev, mutex_unlock(&test_fw_mutex); goto out; } - mutex_unlock(&test_fw_mutex); - rc = test_dev_config_update_size_t(buf, count, - &test_fw_config->buf_size); + rc = __test_dev_config_update_size_t(buf, count, + &test_fw_config->buf_size); + mutex_unlock(&test_fw_mutex); out: return rc; @@ -555,10 +573,10 @@ static ssize_t config_file_offset_store(struct device *dev, mutex_unlock(&test_fw_mutex); goto out; } - mutex_unlock(&test_fw_mutex); - rc = test_dev_config_update_size_t(buf, count, - &test_fw_config->file_offset); + rc = __test_dev_config_update_size_t(buf, count, + &test_fw_config->file_offset); + mutex_unlock(&test_fw_mutex); out: return rc; -- cgit 1.4.1 From 7a2e2ca9ad21c770398c1e6b6d63c93e40bc0cfe Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Fri, 3 Mar 2023 15:23:10 +0530 Subject: cgroup: bpf: use cgroup_lock()/cgroup_unlock() wrappers [ Upstream commit 4cdb91b0dea7d7f59fa84a13c7753cd434fdedcf ] Replace mutex_[un]lock() with cgroup_[un]lock() wrappers to stay consistent across cgroup core and other subsystem code, while operating on the cgroup_mutex. Signed-off-by: Kamalesh Babulal Acked-by: Alexei Starovoitov Reviewed-by: Christian Brauner Signed-off-by: Tejun Heo Stable-dep-of: 2bd110339288 ("cgroup: always put cset in cgroup_css_set_put_fork") Signed-off-by: Sasha Levin --- kernel/bpf/cgroup.c | 38 ++++++++++++++--------------- kernel/bpf/cgroup_iter.c | 4 ++-- kernel/bpf/local_storage.c | 4 ++-- kernel/cgroup/cgroup-v1.c | 16 ++++++------- kernel/cgroup/cgroup.c | 60 +++++++++++++++++++++++----------------------- 5 files changed, 61 insertions(+), 61 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 819f011f0a9c..b86b907e566c 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -173,11 +173,11 @@ void bpf_cgroup_atype_put(int cgroup_atype) { int i = cgroup_atype - CGROUP_LSM_START; - mutex_lock(&cgroup_mutex); + cgroup_lock(); if (--cgroup_lsm_atype[i].refcnt <= 0) cgroup_lsm_atype[i].attach_btf_id = 0; WARN_ON_ONCE(cgroup_lsm_atype[i].refcnt < 0); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); } #else static enum cgroup_bpf_attach_type @@ -282,7 +282,7 @@ static void cgroup_bpf_release(struct work_struct *work) unsigned int atype; - mutex_lock(&cgroup_mutex); + cgroup_lock(); for (atype = 0; atype < ARRAY_SIZE(cgrp->bpf.progs); atype++) { struct hlist_head *progs = &cgrp->bpf.progs[atype]; @@ -315,7 +315,7 @@ static void cgroup_bpf_release(struct work_struct *work) bpf_cgroup_storage_free(storage); } - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p)) cgroup_bpf_put(p); @@ -729,9 +729,9 @@ static int cgroup_bpf_attach(struct cgroup *cgrp, { int ret; - mutex_lock(&cgroup_mutex); + cgroup_lock(); ret = __cgroup_bpf_attach(cgrp, prog, replace_prog, link, type, flags); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -831,7 +831,7 @@ static int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *new_prog, cg_link = container_of(link, struct bpf_cgroup_link, link); - mutex_lock(&cgroup_mutex); + cgroup_lock(); /* link might have been auto-released by dying cgroup, so fail */ if (!cg_link->cgroup) { ret = -ENOLINK; @@ -843,7 +843,7 @@ static int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *new_prog, } ret = __cgroup_bpf_replace(cg_link->cgroup, cg_link, new_prog); out_unlock: - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -1009,9 +1009,9 @@ static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, { int ret; - mutex_lock(&cgroup_mutex); + cgroup_lock(); ret = __cgroup_bpf_detach(cgrp, prog, NULL, type); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -1120,9 +1120,9 @@ static int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, { int ret; - mutex_lock(&cgroup_mutex); + cgroup_lock(); ret = __cgroup_bpf_query(cgrp, attr, uattr); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -1189,11 +1189,11 @@ static void bpf_cgroup_link_release(struct bpf_link *link) if (!cg_link->cgroup) return; - mutex_lock(&cgroup_mutex); + cgroup_lock(); /* re-check cgroup under lock again */ if (!cg_link->cgroup) { - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return; } @@ -1205,7 +1205,7 @@ static void bpf_cgroup_link_release(struct bpf_link *link) cg = cg_link->cgroup; cg_link->cgroup = NULL; - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); cgroup_put(cg); } @@ -1232,10 +1232,10 @@ static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link, container_of(link, struct bpf_cgroup_link, link); u64 cg_id = 0; - mutex_lock(&cgroup_mutex); + cgroup_lock(); if (cg_link->cgroup) cg_id = cgroup_id(cg_link->cgroup); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); seq_printf(seq, "cgroup_id:\t%llu\n" @@ -1251,10 +1251,10 @@ static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link, container_of(link, struct bpf_cgroup_link, link); u64 cg_id = 0; - mutex_lock(&cgroup_mutex); + cgroup_lock(); if (cg_link->cgroup) cg_id = cgroup_id(cg_link->cgroup); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); info->cgroup.cgroup_id = cg_id; info->cgroup.attach_type = cg_link->type; diff --git a/kernel/bpf/cgroup_iter.c b/kernel/bpf/cgroup_iter.c index c187a9e62bdb..d57ccb02477f 100644 --- a/kernel/bpf/cgroup_iter.c +++ b/kernel/bpf/cgroup_iter.c @@ -58,7 +58,7 @@ static void *cgroup_iter_seq_start(struct seq_file *seq, loff_t *pos) { struct cgroup_iter_priv *p = seq->private; - mutex_lock(&cgroup_mutex); + cgroup_lock(); /* cgroup_iter doesn't support read across multiple sessions. */ if (*pos > 0) { @@ -89,7 +89,7 @@ static void cgroup_iter_seq_stop(struct seq_file *seq, void *v) { struct cgroup_iter_priv *p = seq->private; - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); /* pass NULL to the prog for post-processing */ if (!v) { diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 098cf336fae6..f01ca6f1ee03 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c @@ -333,14 +333,14 @@ static void cgroup_storage_map_free(struct bpf_map *_map) struct list_head *storages = &map->list; struct bpf_cgroup_storage *storage, *stmp; - mutex_lock(&cgroup_mutex); + cgroup_lock(); list_for_each_entry_safe(storage, stmp, storages, list_map) { bpf_cgroup_storage_unlink(storage); bpf_cgroup_storage_free(storage); } - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); WARN_ON(!RB_EMPTY_ROOT(&map->root)); WARN_ON(!list_empty(&map->list)); diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index 52bb5a74a23b..aeef06c465ef 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -58,7 +58,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) struct cgroup_root *root; int retval = 0; - mutex_lock(&cgroup_mutex); + cgroup_lock(); cgroup_attach_lock(true); for_each_root(root) { struct cgroup *from_cgrp; @@ -72,7 +72,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) break; } cgroup_attach_unlock(true); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return retval; } @@ -106,7 +106,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) if (ret) return ret; - mutex_lock(&cgroup_mutex); + cgroup_lock(); percpu_down_write(&cgroup_threadgroup_rwsem); @@ -145,7 +145,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) out_err: cgroup_migrate_finish(&mgctx); percpu_up_write(&cgroup_threadgroup_rwsem); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -847,13 +847,13 @@ static int cgroup1_rename(struct kernfs_node *kn, struct kernfs_node *new_parent kernfs_break_active_protection(new_parent); kernfs_break_active_protection(kn); - mutex_lock(&cgroup_mutex); + cgroup_lock(); ret = kernfs_rename(kn, new_parent, new_name_str); if (!ret) TRACE_CGROUP_PATH(rename, cgrp); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); kernfs_unbreak_active_protection(kn); kernfs_unbreak_active_protection(new_parent); @@ -1119,7 +1119,7 @@ int cgroup1_reconfigure(struct fs_context *fc) trace_cgroup_remount(root); out_unlock: - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -1246,7 +1246,7 @@ int cgroup1_get_tree(struct fs_context *fc) if (!ret && !percpu_ref_tryget_live(&ctx->root->cgrp.self.refcnt)) ret = 1; /* restart */ - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); if (!ret) ret = cgroup_do_get_tree(fc); diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 2319946715e0..002e563ec2ac 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -1385,7 +1385,7 @@ static void cgroup_destroy_root(struct cgroup_root *root) cgroup_favor_dynmods(root, false); cgroup_exit_root_id(root); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); cgroup_rstat_exit(cgrp); kernfs_destroy_root(root->kf_root); @@ -1619,7 +1619,7 @@ void cgroup_kn_unlock(struct kernfs_node *kn) else cgrp = kn->parent->priv; - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); kernfs_unbreak_active_protection(kn); cgroup_put(cgrp); @@ -1664,7 +1664,7 @@ struct cgroup *cgroup_kn_lock_live(struct kernfs_node *kn, bool drain_offline) if (drain_offline) cgroup_lock_and_drain_offline(cgrp); else - mutex_lock(&cgroup_mutex); + cgroup_lock(); if (!cgroup_is_dead(cgrp)) return cgrp; @@ -2161,13 +2161,13 @@ int cgroup_do_get_tree(struct fs_context *fc) struct super_block *sb = fc->root->d_sb; struct cgroup *cgrp; - mutex_lock(&cgroup_mutex); + cgroup_lock(); spin_lock_irq(&css_set_lock); cgrp = cset_cgroup_from_root(ctx->ns->root_cset, ctx->root); spin_unlock_irq(&css_set_lock); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); nsdentry = kernfs_node_dentry(cgrp->kn, sb); dput(fc->root); @@ -2350,13 +2350,13 @@ int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen, { int ret; - mutex_lock(&cgroup_mutex); + cgroup_lock(); spin_lock_irq(&css_set_lock); ret = cgroup_path_ns_locked(cgrp, buf, buflen, ns); spin_unlock_irq(&css_set_lock); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -2382,7 +2382,7 @@ int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen) int hierarchy_id = 1; int ret; - mutex_lock(&cgroup_mutex); + cgroup_lock(); spin_lock_irq(&css_set_lock); root = idr_get_next(&cgroup_hierarchy_idr, &hierarchy_id); @@ -2396,7 +2396,7 @@ int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen) } spin_unlock_irq(&css_set_lock); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } EXPORT_SYMBOL_GPL(task_cgroup_path); @@ -3107,7 +3107,7 @@ void cgroup_lock_and_drain_offline(struct cgroup *cgrp) int ssid; restart: - mutex_lock(&cgroup_mutex); + cgroup_lock(); cgroup_for_each_live_descendant_post(dsct, d_css, cgrp) { for_each_subsys(ss, ssid) { @@ -3121,7 +3121,7 @@ restart: prepare_to_wait(&dsct->offline_waitq, &wait, TASK_UNINTERRUPTIBLE); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); schedule(); finish_wait(&dsct->offline_waitq, &wait); @@ -4370,9 +4370,9 @@ int cgroup_rm_cftypes(struct cftype *cfts) if (!(cfts[0].flags & __CFTYPE_ADDED)) return -ENOENT; - mutex_lock(&cgroup_mutex); + cgroup_lock(); ret = cgroup_rm_cftypes_locked(cfts); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -4404,14 +4404,14 @@ static int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) if (ret) return ret; - mutex_lock(&cgroup_mutex); + cgroup_lock(); list_add_tail(&cfts->node, &ss->cfts); ret = cgroup_apply_cftypes(cfts, true); if (ret) cgroup_rm_cftypes_locked(cfts); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return ret; } @@ -5380,7 +5380,7 @@ static void css_release_work_fn(struct work_struct *work) struct cgroup_subsys *ss = css->ss; struct cgroup *cgrp = css->cgroup; - mutex_lock(&cgroup_mutex); + cgroup_lock(); css->flags |= CSS_RELEASED; list_del_rcu(&css->sibling); @@ -5421,7 +5421,7 @@ static void css_release_work_fn(struct work_struct *work) NULL); } - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); INIT_RCU_WORK(&css->destroy_rwork, css_free_rwork_fn); queue_rcu_work(cgroup_destroy_wq, &css->destroy_rwork); @@ -5769,7 +5769,7 @@ static void css_killed_work_fn(struct work_struct *work) struct cgroup_subsys_state *css = container_of(work, struct cgroup_subsys_state, destroy_work); - mutex_lock(&cgroup_mutex); + cgroup_lock(); do { offline_css(css); @@ -5778,7 +5778,7 @@ static void css_killed_work_fn(struct work_struct *work) css = css->parent; } while (css && atomic_dec_and_test(&css->online_cnt)); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); } /* css kill confirmation processing requires process context, bounce */ @@ -5962,7 +5962,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) pr_debug("Initializing cgroup subsys %s\n", ss->name); - mutex_lock(&cgroup_mutex); + cgroup_lock(); idr_init(&ss->css_idr); INIT_LIST_HEAD(&ss->cfts); @@ -6006,7 +6006,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) BUG_ON(online_css(css)); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); } /** @@ -6066,7 +6066,7 @@ int __init cgroup_init(void) get_user_ns(init_cgroup_ns.user_ns); - mutex_lock(&cgroup_mutex); + cgroup_lock(); /* * Add init_css_set to the hash table so that dfl_root can link to @@ -6077,7 +6077,7 @@ int __init cgroup_init(void) BUG_ON(cgroup_setup_root(&cgrp_dfl_root, 0)); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); for_each_subsys(ss, ssid) { if (ss->early_init) { @@ -6129,9 +6129,9 @@ int __init cgroup_init(void) if (ss->bind) ss->bind(init_css_set.subsys[ssid]); - mutex_lock(&cgroup_mutex); + cgroup_lock(); css_populate_dir(init_css_set.subsys[ssid]); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); } /* init_css_set.subsys[] has been updated, re-hash */ @@ -6236,7 +6236,7 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns, if (!buf) goto out; - mutex_lock(&cgroup_mutex); + cgroup_lock(); spin_lock_irq(&css_set_lock); for_each_root(root) { @@ -6291,7 +6291,7 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns, retval = 0; out_unlock: spin_unlock_irq(&css_set_lock); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); kfree(buf); out: return retval; @@ -6375,7 +6375,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs) struct file *f; if (kargs->flags & CLONE_INTO_CGROUP) - mutex_lock(&cgroup_mutex); + cgroup_lock(); cgroup_threadgroup_change_begin(current); @@ -6450,7 +6450,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs) err: cgroup_threadgroup_change_end(current); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); if (f) fput(f); if (dst_cgrp) @@ -6477,7 +6477,7 @@ static void cgroup_css_set_put_fork(struct kernel_clone_args *kargs) struct cgroup *cgrp = kargs->cgrp; struct css_set *cset = kargs->cset; - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); if (cset) { put_css_set(cset); -- cgit 1.4.1 From c68b4db581386440dfb760b1e0d79ce10738047c Mon Sep 17 00:00:00 2001 From: John Sperbeck Date: Sun, 21 May 2023 19:29:53 +0000 Subject: cgroup: always put cset in cgroup_css_set_put_fork [ Upstream commit 2bd110339288c18823dcace602b63b0d8627e520 ] A successful call to cgroup_css_set_fork() will always have taken a ref on kargs->cset (regardless of CLONE_INTO_CGROUP), so always do a corresponding put in cgroup_css_set_put_fork(). Without this, a cset and its contained css structures will be leaked for some fork failures. The following script reproduces the leak for a fork failure due to exceeding pids.max in the pids controller. A similar thing can happen if we jump to the bad_fork_cancel_cgroup label in copy_process(). [ -z "$1" ] && echo "Usage $0 pids-root" && exit 1 PID_ROOT=$1 CGROUP=$PID_ROOT/foo [ -e $CGROUP ] && rmdir -f $CGROUP mkdir $CGROUP echo 5 > $CGROUP/pids.max echo $$ > $CGROUP/cgroup.procs fork_bomb() { set -e for i in $(seq 10); do /bin/sleep 3600 & done } (fork_bomb) & wait echo $$ > $PID_ROOT/cgroup.procs kill $(cat $CGROUP/cgroup.procs) rmdir $CGROUP Fixes: ef2c41cf38a7 ("clone3: allow spawning processes into cgroups") Cc: stable@vger.kernel.org # v5.7+ Signed-off-by: John Sperbeck Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- kernel/cgroup/cgroup.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 002e563ec2ac..36c95626afec 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -6471,19 +6471,18 @@ err: static void cgroup_css_set_put_fork(struct kernel_clone_args *kargs) __releases(&cgroup_threadgroup_rwsem) __releases(&cgroup_mutex) { + struct cgroup *cgrp = kargs->cgrp; + struct css_set *cset = kargs->cset; + cgroup_threadgroup_change_end(current); - if (kargs->flags & CLONE_INTO_CGROUP) { - struct cgroup *cgrp = kargs->cgrp; - struct css_set *cset = kargs->cset; + if (cset) { + put_css_set(cset); + kargs->cset = NULL; + } + if (kargs->flags & CLONE_INTO_CGROUP) { cgroup_unlock(); - - if (cset) { - put_css_set(cset); - kargs->cset = NULL; - } - if (cgrp) { cgroup_put(cgrp); kargs->cgrp = NULL; -- cgit 1.4.1 From 314e973f36a501268401079931f75cefa342cb0a Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 May 2023 07:45:45 +0000 Subject: cgroup: fix missing cpus_read_{lock,unlock}() in cgroup_transfer_tasks() [ Upstream commit ab1de7ead871ebe6d12a774c3c25de0388cde082 ] The commit 4f7e7236435c ("cgroup: Fix threadgroup_rwsem <-> cpus_read_lock() deadlock") fixed the deadlock between cgroup_threadgroup_rwsem and cpus_read_lock() by introducing cgroup_attach_{lock,unlock}() and removing cpus_read_{lock,unlock}() from cpuset_attach(). But cgroup_transfer_tasks() was missed and not handled, which will cause th following warning: WARNING: CPU: 0 PID: 589 at kernel/cpu.c:526 lockdep_assert_cpus_held+0x32/0x40 CPU: 0 PID: 589 Comm: kworker/1:4 Not tainted 6.4.0-rc2-next-20230517 #50 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 Workqueue: events cpuset_hotplug_workfn RIP: 0010:lockdep_assert_cpus_held+0x32/0x40 <...> Call Trace: cpuset_attach+0x40/0x240 cgroup_migrate_execute+0x452/0x5e0 ? _raw_spin_unlock_irq+0x28/0x40 cgroup_transfer_tasks+0x1f3/0x360 ? find_held_lock+0x32/0x90 ? cpuset_hotplug_workfn+0xc81/0xed0 cpuset_hotplug_workfn+0xcb1/0xed0 ? process_one_work+0x248/0x5b0 process_one_work+0x2b9/0x5b0 worker_thread+0x56/0x3b0 ? process_one_work+0x5b0/0x5b0 kthread+0xf1/0x120 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork+0x1f/0x30 So just use the cgroup_attach_{lock,unlock}() helper to fix it. Reported-by: Zhao Gongyi Signed-off-by: Qi Zheng Acked-by: Muchun Song Fixes: 05c7b7a92cc8 ("cgroup/cpuset: Fix a race between cpuset_attach() and cpu hotplug") Cc: stable@vger.kernel.org # v5.17+ Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- kernel/cgroup/cgroup-v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index aeef06c465ef..5407241dbb45 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -108,7 +108,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) cgroup_lock(); - percpu_down_write(&cgroup_threadgroup_rwsem); + cgroup_attach_lock(true); /* all tasks in @from are being moved, all csets are source */ spin_lock_irq(&css_set_lock); @@ -144,7 +144,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) } while (task && !ret); out_err: cgroup_migrate_finish(&mgctx); - percpu_up_write(&cgroup_threadgroup_rwsem); + cgroup_attach_unlock(true); cgroup_unlock(); return ret; } -- cgit 1.4.1 From 4b3ec6b6ff249fa24783953523c13efe68e18106 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 14 Mar 2023 13:34:41 +0530 Subject: qcom: llcc/edac: Fix the base address used for accessing LLCC banks [ Upstream commit ee13b5008707948d3052c1b5aab485c6cd53658e ] The Qualcomm LLCC/EDAC drivers were using a fixed register stride for accessing the (Control and Status Registers) CSRs of each LLCC bank. This stride only works for some SoCs like SDM845 for which driver support was initially added. But the later SoCs use different register stride that vary between the banks with holes in-between. So it is not possible to use a single register stride for accessing the CSRs of each bank. By doing so could result in a crash. For fixing this issue, let's obtain the base address of each LLCC bank from devicetree and get rid of the fixed stride. This also means, there is no need to rely on reg-names property and the base addresses can be obtained using the index. First index is LLCC bank 0 and last index is LLCC broadcast. If the SoC supports more than one bank, then those need to be defined in devicetree for index from 1..N-1. Reported-by: Parikshit Pareek Tested-by: Luca Weiss Tested-by: Steev Klimaszewski # Thinkpad X13s Tested-by: Andrew Halaney # sa8540p-ride Reviewed-by: Borislav Petkov (AMD) Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230314080443.64635-13-manivannan.sadhasivam@linaro.org Stable-dep-of: cbd77119b635 ("EDAC/qcom: Get rid of hardcoded register offsets") Signed-off-by: Sasha Levin --- drivers/edac/qcom_edac.c | 14 +++----- drivers/soc/qcom/llcc-qcom.c | 72 ++++++++++++++++++++++---------------- include/linux/soc/qcom/llcc-qcom.h | 6 ++-- 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/drivers/edac/qcom_edac.c b/drivers/edac/qcom_edac.c index 2c91ceff8a9c..b1f5b9a02d6d 100644 --- a/drivers/edac/qcom_edac.c +++ b/drivers/edac/qcom_edac.c @@ -215,7 +215,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) for (i = 0; i < reg_data.reg_cnt; i++) { synd_reg = reg_data.synd_reg + (i * 4); - ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg, + ret = regmap_read(drv->regmaps[bank], synd_reg, &synd_val); if (ret) goto clear; @@ -224,8 +224,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) reg_data.name, i, synd_val); } - ret = regmap_read(drv->regmap, - drv->offsets[bank] + reg_data.count_status_reg, + ret = regmap_read(drv->regmaps[bank], reg_data.count_status_reg, &err_cnt); if (ret) goto clear; @@ -235,8 +234,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n", reg_data.name, err_cnt); - ret = regmap_read(drv->regmap, - drv->offsets[bank] + reg_data.ways_status_reg, + ret = regmap_read(drv->regmaps[bank], reg_data.ways_status_reg, &err_ways); if (ret) goto clear; @@ -297,8 +295,7 @@ static irqreturn_t llcc_ecc_irq_handler(int irq, void *edev_ctl) /* Iterate over the banks and look for Tag RAM or Data RAM errors */ for (i = 0; i < drv->num_banks; i++) { - ret = regmap_read(drv->regmap, - drv->offsets[i] + DRP_INTERRUPT_STATUS, + ret = regmap_read(drv->regmaps[i], DRP_INTERRUPT_STATUS, &drp_error); if (!ret && (drp_error & SB_ECC_ERROR)) { @@ -313,8 +310,7 @@ static irqreturn_t llcc_ecc_irq_handler(int irq, void *edev_ctl) if (!ret) irq_rc = IRQ_HANDLED; - ret = regmap_read(drv->regmap, - drv->offsets[i] + TRP_INTERRUPT_0_STATUS, + ret = regmap_read(drv->regmaps[i], TRP_INTERRUPT_0_STATUS, &trp_error); if (!ret && (trp_error & SB_ECC_ERROR)) { diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index d4cba3b3c56c..85219b5e1f41 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -50,8 +50,6 @@ #define LLCC_TRP_WRSC_EN 0x21f20 #define LLCC_TRP_WRSC_CACHEABLE_EN 0x21f2c -#define BANK_OFFSET_STRIDE 0x80000 - #define LLCC_VERSION_2_0_0_0 0x02000000 #define LLCC_VERSION_2_1_0_0 0x02010000 @@ -749,8 +747,8 @@ static int qcom_llcc_remove(struct platform_device *pdev) return 0; } -static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, - const char *name) +static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, u8 index, + const char *name) { void __iomem *base; struct regmap_config llcc_regmap_config = { @@ -760,7 +758,7 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, .fast_io = true, }; - base = devm_platform_ioremap_resource_byname(pdev, name); + base = devm_platform_ioremap_resource(pdev, index); if (IS_ERR(base)) return ERR_CAST(base); @@ -778,6 +776,7 @@ static int qcom_llcc_probe(struct platform_device *pdev) const struct llcc_slice_config *llcc_cfg; u32 sz; u32 version; + struct regmap *regmap; drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) { @@ -785,21 +784,51 @@ static int qcom_llcc_probe(struct platform_device *pdev) goto err; } - drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base"); - if (IS_ERR(drv_data->regmap)) { - ret = PTR_ERR(drv_data->regmap); + /* Initialize the first LLCC bank regmap */ + regmap = qcom_llcc_init_mmio(pdev, 0, "llcc0_base"); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); goto err; } - drv_data->bcast_regmap = - qcom_llcc_init_mmio(pdev, "llcc_broadcast_base"); + cfg = of_device_get_match_data(&pdev->dev); + + ret = regmap_read(regmap, cfg->reg_offset[LLCC_COMMON_STATUS0], &num_banks); + if (ret) + goto err; + + num_banks &= LLCC_LB_CNT_MASK; + num_banks >>= LLCC_LB_CNT_SHIFT; + drv_data->num_banks = num_banks; + + drv_data->regmaps = devm_kcalloc(dev, num_banks, sizeof(*drv_data->regmaps), GFP_KERNEL); + if (!drv_data->regmaps) { + ret = -ENOMEM; + goto err; + } + + drv_data->regmaps[0] = regmap; + + /* Initialize rest of LLCC bank regmaps */ + for (i = 1; i < num_banks; i++) { + char *base = kasprintf(GFP_KERNEL, "llcc%d_base", i); + + drv_data->regmaps[i] = qcom_llcc_init_mmio(pdev, i, base); + if (IS_ERR(drv_data->regmaps[i])) { + ret = PTR_ERR(drv_data->regmaps[i]); + kfree(base); + goto err; + } + + kfree(base); + } + + drv_data->bcast_regmap = qcom_llcc_init_mmio(pdev, i, "llcc_broadcast_base"); if (IS_ERR(drv_data->bcast_regmap)) { ret = PTR_ERR(drv_data->bcast_regmap); goto err; } - cfg = of_device_get_match_data(&pdev->dev); - /* Extract version of the IP */ ret = regmap_read(drv_data->bcast_regmap, cfg->reg_offset[LLCC_COMMON_HW_INFO], &version); @@ -808,15 +837,6 @@ static int qcom_llcc_probe(struct platform_device *pdev) drv_data->version = version; - ret = regmap_read(drv_data->regmap, cfg->reg_offset[LLCC_COMMON_STATUS0], - &num_banks); - if (ret) - goto err; - - num_banks &= LLCC_LB_CNT_MASK; - num_banks >>= LLCC_LB_CNT_SHIFT; - drv_data->num_banks = num_banks; - llcc_cfg = cfg->sct_data; sz = cfg->size; @@ -824,16 +844,6 @@ static int qcom_llcc_probe(struct platform_device *pdev) if (llcc_cfg[i].slice_id > drv_data->max_slices) drv_data->max_slices = llcc_cfg[i].slice_id; - drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), - GFP_KERNEL); - if (!drv_data->offsets) { - ret = -ENOMEM; - goto err; - } - - for (i = 0; i < num_banks; i++) - drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; - drv_data->bitmap = devm_bitmap_zalloc(dev, drv_data->max_slices, GFP_KERNEL); if (!drv_data->bitmap) { diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index bc2fb8343a94..dfa5706e90a7 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -108,7 +108,7 @@ struct llcc_edac_reg_offset { /** * struct llcc_drv_data - Data associated with the llcc driver - * @regmap: regmap associated with the llcc device + * @regmaps: regmaps associated with the llcc device * @bcast_regmap: regmap associated with llcc broadcast offset * @cfg: pointer to the data structure for slice configuration * @edac_reg_offset: Offset of the LLCC EDAC registers @@ -117,12 +117,11 @@ struct llcc_edac_reg_offset { * @max_slices: max slices as read from device tree * @num_banks: Number of llcc banks * @bitmap: Bit map to track the active slice ids - * @offsets: Pointer to the bank offsets array * @ecc_irq: interrupt for llcc cache error detection and reporting * @version: Indicates the LLCC version */ struct llcc_drv_data { - struct regmap *regmap; + struct regmap **regmaps; struct regmap *bcast_regmap; const struct llcc_slice_config *cfg; const struct llcc_edac_reg_offset *edac_reg_offset; @@ -131,7 +130,6 @@ struct llcc_drv_data { u32 max_slices; u32 num_banks; unsigned long *bitmap; - u32 *offsets; int ecc_irq; u32 version; }; -- cgit 1.4.1 From fec79e4f7df555b703ee4d6fed5ca62a64e2ab1a Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 17 May 2023 17:16:35 +0530 Subject: EDAC/qcom: Get rid of hardcoded register offsets [ Upstream commit cbd77119b6355872cd308a60e99f9ca678435d15 ] The LLCC EDAC register offsets varies between each SoC. Hardcoding the register offsets won't work and will often result in crash due to accessing the wrong locations. Hence, get the register offsets from the LLCC driver matching the individual SoCs. Cc: # 6.0: 5365cea199c7 ("soc: qcom: llcc: Rename reg_offset structs to reflect LLCC version") Cc: # 6.0: c13d7d261e36 ("soc: qcom: llcc: Pass LLCC version based register offsets to EDAC driver") Cc: # 6.0 Fixes: a6e9d7ef252c ("soc: qcom: llcc: Add configuration data for SM8450 SoC") Acked-by: Borislav Petkov (AMD) Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230517114635.76358-3-manivannan.sadhasivam@linaro.org Signed-off-by: Sasha Levin --- drivers/edac/qcom_edac.c | 116 ++++++++++++++++++------------------- include/linux/soc/qcom/llcc-qcom.h | 6 -- 2 files changed, 58 insertions(+), 64 deletions(-) diff --git a/drivers/edac/qcom_edac.c b/drivers/edac/qcom_edac.c index b1f5b9a02d6d..518092d7eaf7 100644 --- a/drivers/edac/qcom_edac.c +++ b/drivers/edac/qcom_edac.c @@ -21,30 +21,9 @@ #define TRP_SYN_REG_CNT 6 #define DRP_SYN_REG_CNT 8 -#define LLCC_COMMON_STATUS0 0x0003000c #define LLCC_LB_CNT_MASK GENMASK(31, 28) #define LLCC_LB_CNT_SHIFT 28 -/* Single & double bit syndrome register offsets */ -#define TRP_ECC_SB_ERR_SYN0 0x0002304c -#define TRP_ECC_DB_ERR_SYN0 0x00020370 -#define DRP_ECC_SB_ERR_SYN0 0x0004204c -#define DRP_ECC_DB_ERR_SYN0 0x00042070 - -/* Error register offsets */ -#define TRP_ECC_ERROR_STATUS1 0x00020348 -#define TRP_ECC_ERROR_STATUS0 0x00020344 -#define DRP_ECC_ERROR_STATUS1 0x00042048 -#define DRP_ECC_ERROR_STATUS0 0x00042044 - -/* TRP, DRP interrupt register offsets */ -#define DRP_INTERRUPT_STATUS 0x00041000 -#define TRP_INTERRUPT_0_STATUS 0x00020480 -#define DRP_INTERRUPT_CLEAR 0x00041008 -#define DRP_ECC_ERROR_CNTR_CLEAR 0x00040004 -#define TRP_INTERRUPT_0_CLEAR 0x00020484 -#define TRP_ECC_ERROR_CNTR_CLEAR 0x00020440 - /* Mask and shift macros */ #define ECC_DB_ERR_COUNT_MASK GENMASK(4, 0) #define ECC_DB_ERR_WAYS_MASK GENMASK(31, 16) @@ -60,15 +39,6 @@ #define DRP_TRP_INT_CLEAR GENMASK(1, 0) #define DRP_TRP_CNT_CLEAR GENMASK(1, 0) -/* Config registers offsets*/ -#define DRP_ECC_ERROR_CFG 0x00040000 - -/* Tag RAM, Data RAM interrupt register offsets */ -#define CMN_INTERRUPT_0_ENABLE 0x0003001c -#define CMN_INTERRUPT_2_ENABLE 0x0003003c -#define TRP_INTERRUPT_0_ENABLE 0x00020488 -#define DRP_INTERRUPT_ENABLE 0x0004100c - #define SB_ERROR_THRESHOLD 0x1 #define SB_ERROR_THRESHOLD_SHIFT 24 #define SB_DB_TRP_INTERRUPT_ENABLE 0x3 @@ -88,9 +58,6 @@ enum { static const struct llcc_edac_reg_data edac_reg_data[] = { [LLCC_DRAM_CE] = { .name = "DRAM Single-bit", - .synd_reg = DRP_ECC_SB_ERR_SYN0, - .count_status_reg = DRP_ECC_ERROR_STATUS1, - .ways_status_reg = DRP_ECC_ERROR_STATUS0, .reg_cnt = DRP_SYN_REG_CNT, .count_mask = ECC_SB_ERR_COUNT_MASK, .ways_mask = ECC_SB_ERR_WAYS_MASK, @@ -98,9 +65,6 @@ static const struct llcc_edac_reg_data edac_reg_data[] = { }, [LLCC_DRAM_UE] = { .name = "DRAM Double-bit", - .synd_reg = DRP_ECC_DB_ERR_SYN0, - .count_status_reg = DRP_ECC_ERROR_STATUS1, - .ways_status_reg = DRP_ECC_ERROR_STATUS0, .reg_cnt = DRP_SYN_REG_CNT, .count_mask = ECC_DB_ERR_COUNT_MASK, .ways_mask = ECC_DB_ERR_WAYS_MASK, @@ -108,9 +72,6 @@ static const struct llcc_edac_reg_data edac_reg_data[] = { }, [LLCC_TRAM_CE] = { .name = "TRAM Single-bit", - .synd_reg = TRP_ECC_SB_ERR_SYN0, - .count_status_reg = TRP_ECC_ERROR_STATUS1, - .ways_status_reg = TRP_ECC_ERROR_STATUS0, .reg_cnt = TRP_SYN_REG_CNT, .count_mask = ECC_SB_ERR_COUNT_MASK, .ways_mask = ECC_SB_ERR_WAYS_MASK, @@ -118,9 +79,6 @@ static const struct llcc_edac_reg_data edac_reg_data[] = { }, [LLCC_TRAM_UE] = { .name = "TRAM Double-bit", - .synd_reg = TRP_ECC_DB_ERR_SYN0, - .count_status_reg = TRP_ECC_ERROR_STATUS1, - .ways_status_reg = TRP_ECC_ERROR_STATUS0, .reg_cnt = TRP_SYN_REG_CNT, .count_mask = ECC_DB_ERR_COUNT_MASK, .ways_mask = ECC_DB_ERR_WAYS_MASK, @@ -128,7 +86,7 @@ static const struct llcc_edac_reg_data edac_reg_data[] = { }, }; -static int qcom_llcc_core_setup(struct regmap *llcc_bcast_regmap) +static int qcom_llcc_core_setup(struct llcc_drv_data *drv, struct regmap *llcc_bcast_regmap) { u32 sb_err_threshold; int ret; @@ -137,31 +95,31 @@ static int qcom_llcc_core_setup(struct regmap *llcc_bcast_regmap) * Configure interrupt enable registers such that Tag, Data RAM related * interrupts are propagated to interrupt controller for servicing */ - ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE, + ret = regmap_update_bits(llcc_bcast_regmap, drv->edac_reg_offset->cmn_interrupt_2_enable, TRP0_INTERRUPT_ENABLE, TRP0_INTERRUPT_ENABLE); if (ret) return ret; - ret = regmap_update_bits(llcc_bcast_regmap, TRP_INTERRUPT_0_ENABLE, + ret = regmap_update_bits(llcc_bcast_regmap, drv->edac_reg_offset->trp_interrupt_0_enable, SB_DB_TRP_INTERRUPT_ENABLE, SB_DB_TRP_INTERRUPT_ENABLE); if (ret) return ret; sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT); - ret = regmap_write(llcc_bcast_regmap, DRP_ECC_ERROR_CFG, + ret = regmap_write(llcc_bcast_regmap, drv->edac_reg_offset->drp_ecc_error_cfg, sb_err_threshold); if (ret) return ret; - ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE, + ret = regmap_update_bits(llcc_bcast_regmap, drv->edac_reg_offset->cmn_interrupt_2_enable, DRP0_INTERRUPT_ENABLE, DRP0_INTERRUPT_ENABLE); if (ret) return ret; - ret = regmap_write(llcc_bcast_regmap, DRP_INTERRUPT_ENABLE, + ret = regmap_write(llcc_bcast_regmap, drv->edac_reg_offset->drp_interrupt_enable, SB_DB_DRP_INTERRUPT_ENABLE); return ret; } @@ -175,24 +133,28 @@ qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv) switch (err_type) { case LLCC_DRAM_CE: case LLCC_DRAM_UE: - ret = regmap_write(drv->bcast_regmap, DRP_INTERRUPT_CLEAR, + ret = regmap_write(drv->bcast_regmap, + drv->edac_reg_offset->drp_interrupt_clear, DRP_TRP_INT_CLEAR); if (ret) return ret; - ret = regmap_write(drv->bcast_regmap, DRP_ECC_ERROR_CNTR_CLEAR, + ret = regmap_write(drv->bcast_regmap, + drv->edac_reg_offset->drp_ecc_error_cntr_clear, DRP_TRP_CNT_CLEAR); if (ret) return ret; break; case LLCC_TRAM_CE: case LLCC_TRAM_UE: - ret = regmap_write(drv->bcast_regmap, TRP_INTERRUPT_0_CLEAR, + ret = regmap_write(drv->bcast_regmap, + drv->edac_reg_offset->trp_interrupt_0_clear, DRP_TRP_INT_CLEAR); if (ret) return ret; - ret = regmap_write(drv->bcast_regmap, TRP_ECC_ERROR_CNTR_CLEAR, + ret = regmap_write(drv->bcast_regmap, + drv->edac_reg_offset->trp_ecc_error_cntr_clear, DRP_TRP_CNT_CLEAR); if (ret) return ret; @@ -205,16 +167,54 @@ qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv) return ret; } +struct qcom_llcc_syn_regs { + u32 synd_reg; + u32 count_status_reg; + u32 ways_status_reg; +}; + +static void get_reg_offsets(struct llcc_drv_data *drv, int err_type, + struct qcom_llcc_syn_regs *syn_regs) +{ + const struct llcc_edac_reg_offset *edac_reg_offset = drv->edac_reg_offset; + + switch (err_type) { + case LLCC_DRAM_CE: + syn_regs->synd_reg = edac_reg_offset->drp_ecc_sb_err_syn0; + syn_regs->count_status_reg = edac_reg_offset->drp_ecc_error_status1; + syn_regs->ways_status_reg = edac_reg_offset->drp_ecc_error_status0; + break; + case LLCC_DRAM_UE: + syn_regs->synd_reg = edac_reg_offset->drp_ecc_db_err_syn0; + syn_regs->count_status_reg = edac_reg_offset->drp_ecc_error_status1; + syn_regs->ways_status_reg = edac_reg_offset->drp_ecc_error_status0; + break; + case LLCC_TRAM_CE: + syn_regs->synd_reg = edac_reg_offset->trp_ecc_sb_err_syn0; + syn_regs->count_status_reg = edac_reg_offset->trp_ecc_error_status1; + syn_regs->ways_status_reg = edac_reg_offset->trp_ecc_error_status0; + break; + case LLCC_TRAM_UE: + syn_regs->synd_reg = edac_reg_offset->trp_ecc_db_err_syn0; + syn_regs->count_status_reg = edac_reg_offset->trp_ecc_error_status1; + syn_regs->ways_status_reg = edac_reg_offset->trp_ecc_error_status0; + break; + } +} + /* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/ static int dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) { struct llcc_edac_reg_data reg_data = edac_reg_data[err_type]; + struct qcom_llcc_syn_regs regs = { }; int err_cnt, err_ways, ret, i; u32 synd_reg, synd_val; + get_reg_offsets(drv, err_type, ®s); + for (i = 0; i < reg_data.reg_cnt; i++) { - synd_reg = reg_data.synd_reg + (i * 4); + synd_reg = regs.synd_reg + (i * 4); ret = regmap_read(drv->regmaps[bank], synd_reg, &synd_val); if (ret) @@ -224,7 +224,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) reg_data.name, i, synd_val); } - ret = regmap_read(drv->regmaps[bank], reg_data.count_status_reg, + ret = regmap_read(drv->regmaps[bank], regs.count_status_reg, &err_cnt); if (ret) goto clear; @@ -234,7 +234,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n", reg_data.name, err_cnt); - ret = regmap_read(drv->regmaps[bank], reg_data.ways_status_reg, + ret = regmap_read(drv->regmaps[bank], regs.ways_status_reg, &err_ways); if (ret) goto clear; @@ -295,7 +295,7 @@ static irqreturn_t llcc_ecc_irq_handler(int irq, void *edev_ctl) /* Iterate over the banks and look for Tag RAM or Data RAM errors */ for (i = 0; i < drv->num_banks; i++) { - ret = regmap_read(drv->regmaps[i], DRP_INTERRUPT_STATUS, + ret = regmap_read(drv->regmaps[i], drv->edac_reg_offset->drp_interrupt_status, &drp_error); if (!ret && (drp_error & SB_ECC_ERROR)) { @@ -310,7 +310,7 @@ static irqreturn_t llcc_ecc_irq_handler(int irq, void *edev_ctl) if (!ret) irq_rc = IRQ_HANDLED; - ret = regmap_read(drv->regmaps[i], TRP_INTERRUPT_0_STATUS, + ret = regmap_read(drv->regmaps[i], drv->edac_reg_offset->trp_interrupt_0_status, &trp_error); if (!ret && (trp_error & SB_ECC_ERROR)) { @@ -342,7 +342,7 @@ static int qcom_llcc_edac_probe(struct platform_device *pdev) int ecc_irq; int rc; - rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap); + rc = qcom_llcc_core_setup(llcc_driv_data, llcc_driv_data->bcast_regmap); if (rc) return rc; diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index dfa5706e90a7..af093281e335 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -57,9 +57,6 @@ struct llcc_slice_desc { /** * struct llcc_edac_reg_data - llcc edac registers data for each error type * @name: Name of the error - * @synd_reg: Syndrome register address - * @count_status_reg: Status register address to read the error count - * @ways_status_reg: Status register address to read the error ways * @reg_cnt: Number of registers * @count_mask: Mask value to get the error count * @ways_mask: Mask value to get the error ways @@ -68,9 +65,6 @@ struct llcc_slice_desc { */ struct llcc_edac_reg_data { char *name; - u64 synd_reg; - u64 count_status_reg; - u64 ways_status_reg; u32 reg_cnt; u32 count_mask; u32 ways_mask; -- cgit 1.4.1 From e01fc7caac9ce9ad76df9f42f7f61ef4bf1d27c9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 31 May 2023 17:59:32 +0900 Subject: ksmbd: validate smb request protocol id [ Upstream commit 1c1bcf2d3ea061613119b534f57507c377df20f9 ] This patch add the validation for smb request protocol id. If it is not one of the four ids(SMB1_PROTO_NUMBER, SMB2_PROTO_NUMBER, SMB2_TRANSFORM_PROTO_NUM, SMB2_COMPRESSION_TRANSFORM_ID), don't allow processing the request. And this will fix the following KASAN warning also. [ 13.905265] BUG: KASAN: slab-out-of-bounds in init_smb2_rsp_hdr+0x1b9/0x1f0 [ 13.905900] Read of size 16 at addr ffff888005fd2f34 by task kworker/0:2/44 ... [ 13.908553] Call Trace: [ 13.908793] [ 13.908995] dump_stack_lvl+0x33/0x50 [ 13.909369] print_report+0xcc/0x620 [ 13.910870] kasan_report+0xae/0xe0 [ 13.911519] kasan_check_range+0x35/0x1b0 [ 13.911796] init_smb2_rsp_hdr+0x1b9/0x1f0 [ 13.912492] handle_ksmbd_work+0xe5/0x820 Cc: stable@vger.kernel.org Reported-by: Chih-Yen Chang Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/ksmbd/connection.c | 5 +++-- fs/ksmbd/smb_common.c | 14 +++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index bf8531b80a18..e1d2be19cddf 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -366,8 +366,6 @@ int ksmbd_conn_handler_loop(void *p) break; memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); - if (!ksmbd_smb_request(conn)) - break; /* * We already read 4 bytes to find out PDU size, now @@ -385,6 +383,9 @@ int ksmbd_conn_handler_loop(void *p) continue; } + if (!ksmbd_smb_request(conn)) + break; + if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == SMB2_PROTO_NUMBER) { if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index 95afb6b23a91..05d7f3e910bf 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -158,7 +158,19 @@ int ksmbd_verify_smb_message(struct ksmbd_work *work) */ bool ksmbd_smb_request(struct ksmbd_conn *conn) { - return conn->request_buf[0] == 0; + __le32 *proto = (__le32 *)smb2_get_msg(conn->request_buf); + + if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { + pr_err_ratelimited("smb2 compression not support yet"); + return false; + } + + if (*proto != SMB1_PROTO_NUMBER && + *proto != SMB2_PROTO_NUMBER && + *proto != SMB2_TRANSFORM_PROTO_NUM) + return false; + + return true; } static bool supported_protocol(int idx) -- cgit 1.4.1 From 628e40a225a015cb87bbbcb6cd55bc48c44f615b Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 2 Jun 2023 11:05:02 +0900 Subject: of: overlay: Fix missing of_node_put() in error case of init_overlay_changeset() [ Upstream commit 39affd1fdf65983904fafc07cf607cff737eaf30 ] In init_overlay_changeset(), the variable "node" is from of_get_child_by_name(), and the "node" should be discarded in error case. Fixes: d1651b03c2df ("of: overlay: add overlay symbols to live device tree") Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20230602020502.11693-1-hayashi.kunihiko@socionext.com Signed-off-by: Rob Herring Signed-off-by: Sasha Levin --- drivers/of/overlay.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index ed4e6c144a68..5289975bad70 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -811,6 +811,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs) if (!fragment->target) { pr_err("symbols in overlay, but not in live tree\n"); ret = -EINVAL; + of_node_put(node); goto err_out; } -- cgit 1.4.1 From e3d2bdca180bef5a8223e43c604fe2d400e473b8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 15 Apr 2023 18:07:29 +0200 Subject: power: supply: ab8500: Fix external_power_changed race [ Upstream commit a5299ce4e96f3e8930e9c051b28d8093ada87b08 ] ab8500_btemp_external_power_changed() dereferences di->btemp_psy, which gets sets in ab8500_btemp_probe() like this: di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc, &psy_cfg); As soon as devm_power_supply_register() has called device_add() the external_power_changed callback can get called. So there is a window where ab8500_btemp_external_power_changed() may get called while di->btemp_psy has not been set yet leading to a NULL pointer dereference. Fixing this is easy. The external_power_changed callback gets passed the power_supply which will eventually get stored in di->btemp_psy, so ab8500_btemp_external_power_changed() can simply directly use the passed in psy argument which is always valid. And the same applies to ab8500_fg_external_power_changed(). Signed-off-by: Hans de Goede Reviewed-by: Linus Walleij Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- drivers/power/supply/ab8500_btemp.c | 6 ++---- drivers/power/supply/ab8500_fg.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 307ee6f71042..6f83e99d2eb7 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -624,10 +624,8 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data) */ static void ab8500_btemp_external_power_changed(struct power_supply *psy) { - struct ab8500_btemp *di = power_supply_get_drvdata(psy); - - class_for_each_device(power_supply_class, NULL, - di->btemp_psy, ab8500_btemp_get_ext_psy_data); + class_for_each_device(power_supply_class, NULL, psy, + ab8500_btemp_get_ext_psy_data); } /* ab8500 btemp driver interrupts and their respective isr */ diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index c6c9804280db..71ce28eed463 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2407,10 +2407,8 @@ out: */ static void ab8500_fg_external_power_changed(struct power_supply *psy) { - struct ab8500_fg *di = power_supply_get_drvdata(psy); - - class_for_each_device(power_supply_class, NULL, - di->fg_psy, ab8500_fg_get_ext_psy_data); + class_for_each_device(power_supply_class, NULL, psy, + ab8500_fg_get_ext_psy_data); } /** -- cgit 1.4.1 From 66a88d04cf5d3a019c520effdad8146e8b855a64 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 15 Apr 2023 18:07:32 +0200 Subject: power: supply: sc27xx: Fix external_power_changed race [ Upstream commit 4d5c129d6c8993fe96e9ae712141eedcb9ca68c2 ] sc27xx_fgu_external_power_changed() dereferences data->battery, which gets sets in ab8500_btemp_probe() like this: data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, &fgu_cfg); As soon as devm_power_supply_register() has called device_add() the external_power_changed callback can get called. So there is a window where sc27xx_fgu_external_power_changed() may get called while data->battery has not been set yet leading to a NULL pointer dereference. Fixing this is easy. The external_power_changed callback gets passed the power_supply which will eventually get stored in data->battery, so sc27xx_fgu_external_power_changed() can simply directly use the passed in psy argument which is always valid. After this change sc27xx_fgu_external_power_changed() is reduced to just "power_supply_changed(psy);" and it has the same prototype. While at it simply replace it with making the external_power_changed callback directly point to power_supply_changed. Cc: Orson Zhai Cc: Chunyan Zhang Signed-off-by: Hans de Goede Reviewed-by: Baolin Wang Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- drivers/power/supply/sc27xx_fuel_gauge.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 632977f84b95..bd23c4d9fed4 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -733,13 +733,6 @@ static int sc27xx_fgu_set_property(struct power_supply *psy, return ret; } -static void sc27xx_fgu_external_power_changed(struct power_supply *psy) -{ - struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); - - power_supply_changed(data->battery); -} - static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { @@ -774,7 +767,7 @@ static const struct power_supply_desc sc27xx_fgu_desc = { .num_properties = ARRAY_SIZE(sc27xx_fgu_props), .get_property = sc27xx_fgu_get_property, .set_property = sc27xx_fgu_set_property, - .external_power_changed = sc27xx_fgu_external_power_changed, + .external_power_changed = power_supply_changed, .property_is_writeable = sc27xx_fgu_property_is_writeable, .no_thermal = true, }; -- cgit 1.4.1 From 398bf0d67bcccac84efc45d413b9bd57f27af0fd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 15 Apr 2023 20:23:39 +0200 Subject: power: supply: bq27xxx: Use mod_delayed_work() instead of cancel() + schedule() [ Upstream commit 59dddea9879713423c7b2ade43c423bb71e0d216 ] Use mod_delayed_work() instead of separate cancel_delayed_work_sync() + schedule_delayed_work() calls. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- drivers/power/supply/bq27xxx_battery.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 17a09d103a59..4a5371a3a531 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -1083,10 +1083,8 @@ static int poll_interval_param_set(const char *val, const struct kernel_param *k return ret; mutex_lock(&bq27xxx_list_lock); - list_for_each_entry(di, &bq27xxx_battery_devices, list) { - cancel_delayed_work_sync(&di->work); - schedule_delayed_work(&di->work, 0); - } + list_for_each_entry(di, &bq27xxx_battery_devices, list) + mod_delayed_work(system_wq, &di->work, 0); mutex_unlock(&bq27xxx_list_lock); return ret; -- cgit 1.4.1 From cc1444a3639838fd1db60ccc84ff024f619b31af Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 23 Apr 2023 17:08:37 +0200 Subject: ARM: dts: vexpress: add missing cache properties [ Upstream commit 328acc5657c6197753238d7ce0a6924ead829347 ] As all level 2 and level 3 caches are unified, add required cache-unified property to fix warnings like: vexpress-v2p-ca5s.dtb: cache-controller@2c0f0000: 'cache-unified' is a required property Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230423150837.118466-1-krzysztof.kozlowski@linaro.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- arch/arm/boot/dts/vexpress-v2p-ca5s.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts index 3b88209bacea..ff1f9a1bcfcf 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts @@ -132,6 +132,7 @@ reg = <0x2c0f0000 0x1000>; interrupts = <0 84 4>; cache-level = <2>; + cache-unified; }; pmu { -- cgit 1.4.1 From 79a0a3695e011cf79a561d419145f07f115ddd64 Mon Sep 17 00:00:00 2001 From: Milo Spadacini Date: Mon, 8 May 2023 15:18:48 +0200 Subject: tools: gpio: fix debounce_period_us output of lsgpio [ Upstream commit eb4b8eca1bad98f4b8574558a74f041f9acb5a54 ] Fix incorrect output that could occur when more attributes are used and GPIO_V2_LINE_ATTR_ID_DEBOUNCE is not the first one. Signed-off-by: Milo Spadacini Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- tools/gpio/lsgpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index c61d061247e1..52a0be45410c 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -94,7 +94,7 @@ static void print_attributes(struct gpio_v2_line_info *info) for (i = 0; i < info->num_attrs; i++) { if (info->attrs[i].id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) fprintf(stdout, ", debounce_period=%dusec", - info->attrs[0].debounce_period_us); + info->attrs[i].debounce_period_us); } } -- cgit 1.4.1 From a7620312a082584b4043de91f4169bc4d0f05268 Mon Sep 17 00:00:00 2001 From: Mirsad Todorovac Date: Tue, 11 Apr 2023 20:15:20 +0300 Subject: selftests: gpio: gpio-sim: Fix BUG: test FAILED due to recent change [ Upstream commit 976d3c6778e99390c6d854d140b746d12ea18a51 ] According to Mirsad the gpio-sim.sh test appears to FAIL in a wrong way due to missing initialisation of shell variables: 4.2. Bias settings work correctly cat: /sys/devices/platform/gpio-sim.0/gpiochip18/sim_gpio0/value: No such file or directory ./gpio-sim.sh: line 393: test: =: unary operator expected bias setting does not work GPIO gpio-sim test FAIL After this change the test passed: 4.2. Bias settings work correctly GPIO gpio-sim test PASS His testing environment is AlmaLinux 8.7 on Lenovo desktop box with the latest Linux kernel based on v6.2: Linux 6.2.0-mglru-kmlk-andy-09238-gd2980d8d8265 x86_64 Suggested-by: Mirsad Todorovac Signed-off-by: Andy Shevchenko Tested-by: Mirsad Goran Todorovac Signed-off-by: Mirsad Goran Todorovac Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- tools/testing/selftests/gpio/gpio-sim.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/gpio/gpio-sim.sh b/tools/testing/selftests/gpio/gpio-sim.sh index 341e3de00896..bf67b23ed29a 100755 --- a/tools/testing/selftests/gpio/gpio-sim.sh +++ b/tools/testing/selftests/gpio/gpio-sim.sh @@ -389,6 +389,9 @@ create_chip chip create_bank chip bank set_num_lines chip bank 8 enable_chip chip +DEVNAME=`configfs_dev_name chip` +CHIPNAME=`configfs_chip_name chip bank` +SYSFS_PATH="/sys/devices/platform/$DEVNAME/$CHIPNAME/sim_gpio0/value" $BASE_DIR/gpio-mockup-cdev -b pull-up /dev/`configfs_chip_name chip bank` 0 test `cat $SYSFS_PATH` = "1" || fail "bias setting does not work" remove_chip chip -- cgit 1.4.1 From 22358b9c418c101cdba1dc3aa819eabb724b43eb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 5 Mar 2023 21:52:26 +0100 Subject: power: supply: Ratelimit no data debug output [ Upstream commit 155c45a25679f571c2ae57d10db843a9dfc63430 ] Reduce the amount of output this dev_dbg() statement emits into logs, otherwise if system software polls the sysfs entry for data and keeps getting -ENODATA, it could end up filling the logs up. This does in fact make systemd journald choke, since during boot the sysfs power supply entries are polled and if journald starts at the same time, the journal is just being repeatedly filled up, and the system stops on trying to start journald without booting any further. Signed-off-by: Marek Vasut Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- drivers/power/supply/power_supply_sysfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 5369abaceb5c..7abd916d005a 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -285,7 +285,8 @@ static ssize_t power_supply_show_property(struct device *dev, if (ret < 0) { if (ret == -ENODATA) - dev_dbg(dev, "driver has no data for `%s' property\n", + dev_dbg_ratelimited(dev, + "driver has no data for `%s' property\n", attr->attr.name); else if (ret != -ENODEV && ret != -EAGAIN) dev_err_ratelimited(dev, -- cgit 1.4.1 From cdf9cfc1bbd7a53691f744b6e9726dc7a9e7e4e6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 11 May 2023 15:19:05 +0300 Subject: PCI/DPC: Quirk PIO log size for Intel Ice Lake Root Ports [ Upstream commit 3b8803494a0612acdeee714cb72aa142b1e05ce5 ] Commit 5459c0b70467 ("PCI/DPC: Quirk PIO log size for certain Intel Root Ports") added quirks for Tiger and Alder Lake Root Ports but missed that the same issue exists also in the previous generation, Ice Lake. Apply the quirk for Ice Lake Root Ports as well. This prevents kernel complaints like: DPC: RP PIO log size 0 is invalid and also enables the DPC driver to dump the RP PIO Log registers when DPC is triggered. [bhelgaas: add dmesg warning and RP PIO Log dump info] Closes: https://bugzilla.kernel.org/show_bug.cgi?id=209943 Link: https://lore.kernel.org/r/20230511121905.73949-1-mika.westerberg@linux.intel.com Reported-by: Mark Blakeney Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin --- drivers/pci/quirks.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 8d32a3834688..ccc90656130a 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5995,8 +5995,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56c1, aspm_l1_acceptable_latency #ifdef CONFIG_PCIE_DPC /* - * Intel Tiger Lake and Alder Lake BIOS has a bug that clears the DPC - * RP PIO Log Size of the integrated Thunderbolt PCIe Root Ports. + * Intel Ice Lake, Tiger Lake and Alder Lake BIOS has a bug that clears + * the DPC RP PIO Log Size of the integrated Thunderbolt PCIe Root + * Ports. */ static void dpc_log_size(struct pci_dev *dev) { @@ -6019,6 +6020,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x461f, dpc_log_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x462f, dpc_log_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x463f, dpc_log_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x466e, dpc_log_size); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x8a1d, dpc_log_size); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x8a1f, dpc_log_size); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x8a21, dpc_log_size); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x8a23, dpc_log_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a23, dpc_log_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a25, dpc_log_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a27, dpc_log_size); -- cgit 1.4.1 From c94be1f039c334f63579f95b9f4338290fe555d4 Mon Sep 17 00:00:00 2001 From: Alexandru Sorodoc Date: Fri, 12 May 2023 13:15:17 +0300 Subject: platform/x86: asus-wmi: Ignore WMI events with codes 0x7B, 0xC0 [ Upstream commit 362c1f2ec82cb65940e1c73e15a395a7a891fc6f ] On ASUS GU604V the key 0x7B is issued when the charger is connected or disconnected, and key 0xC0 is issued when an external display is connected or disconnected. This commit maps them to KE_IGNORE to slience kernel messages about unknown keys, such as: kernel: asus_wmi: Unknown key code 0x7b Signed-off-by: Alexandru Sorodoc Link: https://lore.kernel.org/r/20230512101517.47416-1-ealex95@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/asus-nb-wmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index e2c9a68d12df..fdf7da06af30 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -555,6 +555,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x71, { KEY_F13 } }, /* General-purpose button */ { KE_IGNORE, 0x79, }, /* Charger type dectection notification */ { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ + { KE_IGNORE, 0x7B, }, /* Charger connect/disconnect notification */ { KE_KEY, 0x7c, { KEY_MICMUTE } }, { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ @@ -584,6 +585,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0xAE, { KEY_FN_F5 } }, /* Fn+F5 fan mode on 2020+ */ { KE_KEY, 0xB3, { KEY_PROG4 } }, /* AURA */ { KE_KEY, 0xB5, { KEY_CALC } }, + { KE_IGNORE, 0xC0, }, /* External display connect/disconnect notification */ { KE_KEY, 0xC4, { KEY_KBDILLUMUP } }, { KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } }, { KE_IGNORE, 0xC6, }, /* Ambient Light Sensor notification */ -- cgit 1.4.1 From bf8324676b1c842c100aafcc850342d1492f24a8 Mon Sep 17 00:00:00 2001 From: Osama Muhammad Date: Mon, 15 May 2023 22:29:38 +0500 Subject: regulator: Fix error checking for debugfs_create_dir [ Upstream commit 2bf1c45be3b8f3a3f898d0756c1282f09719debd ] This patch fixes the error checking in core.c in debugfs_create_dir. The correct way to check if an error occurred is 'IS_ERR' inline function. Signed-off-by: Osama Muhammad --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c417eae887b2..e01cade8be0c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5257,7 +5257,7 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) } rdev->debugfs = debugfs_create_dir(rname, debugfs_root); - if (!rdev->debugfs) { + if (IS_ERR(rdev->debugfs)) { rdev_warn(rdev, "Failed to create debugfs directory\n"); return; } @@ -6179,7 +6179,7 @@ static int __init regulator_init(void) ret = class_register(®ulator_class); debugfs_root = debugfs_create_dir("regulator", NULL); - if (!debugfs_root) + if (IS_ERR(debugfs_root)) pr_warn("regulator: Failed to create debugfs directory\n"); #ifdef CONFIG_DEBUG_FS -- cgit 1.4.1 From 0cdb593c2fe94c874fb8a40a4e237211ca18ab7e Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 15 May 2023 13:13:51 -0700 Subject: irqchip/gic-v3: Disable pseudo NMIs on Mediatek devices w/ firmware issues [ Upstream commit 44bd78dd2b8897f59b7e3963f088caadb7e4f047 ] Some Chromebooks with Mediatek SoCs have a problem where the firmware doesn't properly save/restore certain GICR registers. Newer Chromebooks should fix this issue and we may be able to do firmware updates for old Chromebooks. At the moment, the only known issue with these Chromebooks is that we can't enable "pseudo NMIs" since the priority register can be lost. Enabling "pseudo NMIs" on Chromebooks with the problematic firmware causes crashes and freezes. Let's detect devices with this problem and then disable "pseudo NMIs" on them. We'll detect the problem by looking for the presence of the "mediatek,broken-save-restore-fw" property in the GIC device tree node. Any devices with fixed firmware will not have this property. Our detection plan works because we never bake a Chromebook's device tree into firmware. Instead, device trees are always bundled with the kernel. We'll update the device trees of all affected Chromebooks and then we'll never enable "pseudo NMI" on a kernel that is bundled with old device trees. When a firmware update is shipped that fixes this issue it will know to patch the device tree to remove the property. In order to make this work, the quick detection mechanism of the GICv3 code is extended to be able to look for properties in addition to looking at "compatible". Reviewed-by: Julius Werner Signed-off-by: Douglas Anderson Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230515131353.v2.2.I88dc0a0eb1d9d537de61604cd8994ecc55c0cac1@changeid Signed-off-by: Sasha Levin --- drivers/irqchip/irq-gic-common.c | 8 ++++++-- drivers/irqchip/irq-gic-common.h | 1 + drivers/irqchip/irq-gic-v3.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index a610821c8ff2..de47b51cdadb 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -16,7 +16,11 @@ void gic_enable_of_quirks(const struct device_node *np, const struct gic_quirk *quirks, void *data) { for (; quirks->desc; quirks++) { - if (!of_device_is_compatible(np, quirks->compatible)) + if (quirks->compatible && + !of_device_is_compatible(np, quirks->compatible)) + continue; + if (quirks->property && + !of_property_read_bool(np, quirks->property)) continue; if (quirks->init(data)) pr_info("GIC: enabling workaround for %s\n", @@ -28,7 +32,7 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, void *data) { for (; quirks->desc; quirks++) { - if (quirks->compatible) + if (quirks->compatible || quirks->property) continue; if (quirks->iidr != (quirks->mask & iidr)) continue; diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h index 27e3d4ed4f32..3db4592cda1c 100644 --- a/drivers/irqchip/irq-gic-common.h +++ b/drivers/irqchip/irq-gic-common.h @@ -13,6 +13,7 @@ struct gic_quirk { const char *desc; const char *compatible; + const char *property; bool (*init)(void *data); u32 iidr; u32 mask; diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 2182f87d2d12..11f7c53e4b63 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -38,6 +38,7 @@ #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) #define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1) +#define FLAGS_WORKAROUND_MTK_GICR_SAVE (1ULL << 2) #define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1) @@ -1730,6 +1731,15 @@ static bool gic_enable_quirk_msm8996(void *data) return true; } +static bool gic_enable_quirk_mtk_gicr(void *data) +{ + struct gic_chip_data *d = data; + + d->flags |= FLAGS_WORKAROUND_MTK_GICR_SAVE; + + return true; +} + static bool gic_enable_quirk_cavium_38539(void *data) { struct gic_chip_data *d = data; @@ -1802,6 +1812,11 @@ static const struct gic_quirk gic_quirks[] = { .compatible = "qcom,msm8996-gic-v3", .init = gic_enable_quirk_msm8996, }, + { + .desc = "GICv3: Mediatek Chromebook GICR save problem", + .property = "mediatek,broken-save-restore-fw", + .init = gic_enable_quirk_mtk_gicr, + }, { .desc = "GICv3: HIP06 erratum 161010803", .iidr = 0x0204043b, @@ -1844,6 +1859,11 @@ static void gic_enable_nmi_support(void) if (!gic_prio_masking_enabled()) return; + if (gic_data.flags & FLAGS_WORKAROUND_MTK_GICR_SAVE) { + pr_warn("Skipping NMI enable due to firmware issues\n"); + return; + } + ppi_nmi_refs = kcalloc(gic_data.ppi_nr, sizeof(*ppi_nmi_refs), GFP_KERNEL); if (!ppi_nmi_refs) return; -- cgit 1.4.1 From 808e103ebac9db9b66a869f32016b9224cb2ec98 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 12 May 2023 18:45:06 +0200 Subject: irqchip/meson-gpio: Mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 14130211be5366a91ec07c3284c183b75d8fba17 ] The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/irqchip/irq-meson-gpio.c:153:34: error: ‘meson_irq_gpio_matches’ defined but not used [-Werror=unused-const-variable=] Acked-by: Martin Blumenstingl Signed-off-by: Krzysztof Kozlowski Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230512164506.212267-1-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- drivers/irqchip/irq-meson-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index 2aaa9aad3e87..7da18ef95211 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -150,7 +150,7 @@ static const struct meson_gpio_irq_params s4_params = { INIT_MESON_S4_COMMON_DATA(82) }; -static const struct of_device_id meson_irq_gpio_matches[] = { +static const struct of_device_id meson_irq_gpio_matches[] __maybe_unused = { { .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params }, { .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params }, { .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params }, -- cgit 1.4.1 From 2d9144c0ca820565ac25cbaa63f585ddedb741b5 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 16 May 2023 13:25:40 -0500 Subject: power: supply: Fix logic checking if system is running from battery [ Upstream commit 95339f40a8b652b5b1773def31e63fc53c26378a ] The logic used for power_supply_is_system_supplied() counts all power supplies and assumes that the system is running from AC if there is either a non-battery power-supply reporting to be online or if no power-supplies exist at all. The second rule is for desktop systems, that don't have any battery/charger devices. These systems will incorrectly report to be powered from battery once a device scope power-supply is registered (e.g. a HID device), since these power-supplies increase the counter. Apart from HID devices, recent dGPUs provide UCSI power supplies on a desktop systems. The dGPU by default doesn't have anything plugged in so it's 'offline'. This makes power_supply_is_system_supplied() return 0 with a count of 1 meaning all drivers that use this get a wrong judgement. To fix this case adjust the logic to also examine the scope of the power supply. If the power supply is deemed a device power supply, then don't count it. Cc: Evan Quan Suggested-by: Lijo Lazar Signed-off-by: Mario Limonciello Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- drivers/power/supply/power_supply_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 8382be867d27..7871ab5e979c 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -348,6 +348,10 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data) struct power_supply *psy = dev_get_drvdata(dev); unsigned int *count = data; + if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_SCOPE, &ret)) + if (ret.intval == POWER_SUPPLY_SCOPE_DEVICE) + return 0; + (*count)++; if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, @@ -366,8 +370,8 @@ int power_supply_is_system_supplied(void) __power_supply_is_system_supplied); /* - * If no power class device was found at all, most probably we are - * running on a desktop system, so assume we are on mains power. + * If no system scope power class device was found at all, most probably we + * are running on a desktop system, so assume we are on mains power. */ if (count == 0) return 1; -- cgit 1.4.1 From c45aed74318e65b8da938a593c124ad2ee1a1e66 Mon Sep 17 00:00:00 2001 From: Maya Matuszczyk Date: Mon, 15 May 2023 20:48:43 +0200 Subject: drm: panel-orientation-quirks: Change Air's quirk to support Air Plus [ Upstream commit 1aa7f416175619e0286fddc5fc44e968b06bf2aa ] It turned out that Aya Neo Air Plus had a different board name than expected. This patch changes Aya Neo Air's quirk to account for that, as both devices share "Air" in DMI product name. Tested on Air claiming to be an Air Pro, and on Air Plus. Signed-off-by: Maya Matuszczyk Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Link: https://patchwork.freedesktop.org/patch/msgid/20230515184843.1552612-1-maccraft123mc@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_panel_orientation_quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index b1a38e6ce2f8..0cb646cb04ee 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -179,7 +179,7 @@ static const struct dmi_system_id orientation_data[] = { }, { /* AYA NEO AIR */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AYANEO"), - DMI_MATCH(DMI_BOARD_NAME, "AIR"), + DMI_MATCH(DMI_PRODUCT_NAME, "AIR"), }, .driver_data = (void *)&lcd1080x1920_leftside_up, }, { /* AYA NEO NEXT */ -- cgit 1.4.1 From e9a5175d5e83310d92587c67b2583cd9373e6567 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 13 Apr 2023 13:57:17 +0800 Subject: btrfs: scrub: try harder to mark RAID56 block groups read-only [ Upstream commit 7561551e7ba870b9659083b95feb520fb2dacce3 ] Currently we allow a block group not to be marked read-only for scrub. But for RAID56 block groups if we require the block group to be read-only, then we're allowed to use cached content from scrub stripe to reduce unnecessary RAID56 reads. So this patch would: - Make btrfs_inc_block_group_ro() try harder During my tests, for cases like btrfs/061 and btrfs/064, we can hit ENOSPC from btrfs_inc_block_group_ro() calls during scrub. The reason is if we only have one single data chunk, and trying to scrub it, we won't have any space left for any newer data writes. But this check should be done by the caller, especially for scrub cases we only temporarily mark the chunk read-only. And newer data writes would always try to allocate a new data chunk when needed. - Return error for scrub if we failed to mark a RAID56 chunk read-only Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/block-group.c | 14 ++++++++++++-- fs/btrfs/scrub.c | 9 ++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index f33ddd5922b8..74a5c94898b0 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -2621,10 +2621,20 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache, } ret = inc_block_group_ro(cache, 0); - if (!do_chunk_alloc || ret == -ETXTBSY) - goto unlock_out; if (!ret) goto out; + if (ret == -ETXTBSY) + goto unlock_out; + + /* + * Skip chunk alloction if the bg is SYSTEM, this is to avoid system + * chunk allocation storm to exhaust the system chunk array. Otherwise + * we still want to try our best to mark the block group read-only. + */ + if (!do_chunk_alloc && ret == -ENOSPC && + (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM)) + goto unlock_out; + alloc_flags = btrfs_get_alloc_profile(fs_info, cache->space_info->flags); ret = btrfs_chunk_alloc(trans, alloc_flags, CHUNK_ALLOC_FORCE); if (ret < 0) diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index c5d8dc112fd5..1672d4846baa 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -4017,13 +4017,20 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx, if (ret == 0) { ro_set = 1; - } else if (ret == -ENOSPC && !sctx->is_dev_replace) { + } else if (ret == -ENOSPC && !sctx->is_dev_replace && + !(cache->flags & BTRFS_BLOCK_GROUP_RAID56_MASK)) { /* * btrfs_inc_block_group_ro return -ENOSPC when it * failed in creating new chunk for metadata. * It is not a problem for scrub, because * metadata are always cowed, and our scrub paused * commit_transactions. + * + * For RAID56 chunks, we have to mark them read-only + * for scrub, as later we would use our own cache + * out of RAID56 realm. + * Thus we want the RAID56 bg to be marked RO to + * prevent RMW from screwing up out cache. */ ro_set = 0; } else if (ret == -ETXTBSY) { -- cgit 1.4.1 From 0a9b2164b7d8b82b9874f9d54641f5bddd8a2bab Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Thu, 4 May 2023 13:58:13 +0200 Subject: btrfs: handle memory allocation failure in btrfs_csum_one_bio [ Upstream commit 806570c0bb7b4847828c22c4934fcf2dc8fc572f ] Since f8a53bb58ec7 ("btrfs: handle checksum generation in the storage layer") the failures of btrfs_csum_one_bio() are handled via bio_end_io(). This means, we can return BLK_STS_RESOURCE from btrfs_csum_one_bio() in case the allocation of the ordered sums fails. This also fixes a syzkaller report, where injecting a failure into the kvzalloc() call results in a BUG_ON(). Reported-by: syzbot+d8941552e21eac774778@syzkaller.appspotmail.com Reviewed-by: Christoph Hellwig Reviewed-by: Anand Jain Signed-off-by: Johannes Thumshirn Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/file-item.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 4fab7da63259..b14d2da9b26d 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -717,7 +717,9 @@ blk_status_t btrfs_csum_one_bio(struct btrfs_inode *inode, struct bio *bio, sums = kvzalloc(btrfs_ordered_sum_size(fs_info, bytes_left), GFP_KERNEL); memalloc_nofs_restore(nofs_flag); - BUG_ON(!sums); /* -ENOMEM */ + if (!sums) + return BLK_STS_RESOURCE; + sums->len = bytes_left; ordered = btrfs_lookup_ordered_extent(inode, offset); -- cgit 1.4.1 From c33fded7f17f6102a4d7d77f826d0eba5da9b986 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 17 May 2023 13:57:31 -0500 Subject: ASoC: soc-pcm: test if a BE can be prepared [ Upstream commit e123036be377ddf628226a7c6d4f9af5efd113d3 ] In the BE hw_params configuration, the existing code checks if any of the existing FEs are prepared, running, paused or suspended - and skips the configuration in those cases. This allows multiple calls of hw_params which the ALSA state machine supports. This check is not handled for the prepare stage, which can lead to the same BE being prepared multiple times. This patch adds a check similar to that of the hw_params, with the main difference being that the suspended state is allowed: the ALSA state machine allows a transition from suspended to prepared with hw_params skipped. This problem was detected on Intel IPC4/SoundWire devices, where the BE dailink .prepare stage is used to configure the SoundWire stream with a bank switch. Multiple .prepare calls lead to conflicts with the .trigger operation with IPC4 configurations. This problem was not detected earlier on Intel devices, HDaudio BE dailinks detect that the link is already prepared and skip the configuration, and for IPC3 devices there is no BE trigger. Link: https://github.com/thesofproject/sof/issues/7596 Signed-off-by: Ranjani Sridharan --- include/sound/soc-dpcm.h | 4 ++++ sound/soc/soc-pcm.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 5b689c663290..27a5642f07cd 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -125,6 +125,10 @@ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream); +/* can this BE perform prepare */ +int snd_soc_dpcm_can_be_prepared(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream); + /* is the current PCM operation for this FE ? */ int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 7a486ca9633c..f3964060a044 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2403,6 +2403,9 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; + if (!snd_soc_dpcm_can_be_prepared(fe, be, stream)) + continue; + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND) && @@ -3043,3 +3046,20 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); } EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); + +/* + * We can only prepare a BE DAI if any of it's FE are not prepared, + * running or paused for the specified stream direction. + */ +int snd_soc_dpcm_can_be_prepared(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int stream) +{ + const enum snd_soc_dpcm_state state[] = { + SND_SOC_DPCM_STATE_START, + SND_SOC_DPCM_STATE_PAUSED, + SND_SOC_DPCM_STATE_PREPARE, + }; + + return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); +} +EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_prepared); -- cgit 1.4.1 From 5daa27bcb31d731258b39497dbd0a7949eee75b9 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 19 May 2023 22:17:09 +0200 Subject: ASoC: Intel: avs: Account for UID of ACPI device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 836855100b87b4dd7a82546131779dc255c18b67 ] Configurations with multiple codecs attached to the platform are supported but only if each from the set is different. Add new field representing the 'Unique ID' so that codecs that share Vendor and Part IDs can be differentiated and thus enabling support for such configurations. Signed-off-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20230519201711.4073845-6-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- include/sound/soc-acpi.h | 1 + sound/soc/intel/avs/board_selection.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index b38fd25c5729..528279056b3a 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -170,6 +170,7 @@ struct snd_soc_acpi_link_adr { /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { u8 id[ACPI_ID_LEN]; + const char *uid; const struct snd_soc_acpi_codecs *comp_ids; const u32 link_mask; const struct snd_soc_acpi_link_adr *links; diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 87f9c18be238..87353b4b0cd7 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -394,7 +394,7 @@ static int avs_register_i2s_boards(struct avs_dev *adev) } for (mach = boards->machs; mach->id[0]; mach++) { - if (!acpi_dev_present(mach->id, NULL, -1)) + if (!acpi_dev_present(mach->id, mach->uid, -1)) continue; if (mach->machine_quirk) -- cgit 1.4.1 From 0b09b35cae865b383e206d2ab54ce4560859577c Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Fri, 19 May 2023 22:17:11 +0200 Subject: ASoC: Intel: avs: Add missing checks on FE startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 25148f57a2a6d157779bae494852e172952ba980 ] Constraint functions have return values, they should be checked for potential errors. Reviewed-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20230519201711.4073845-8-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/avs/pcm.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 8fe5917b1e26..22f21f3f612d 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -424,21 +424,34 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST); if (!host_stream) { - kfree(data); - return -EBUSY; + ret = -EBUSY; + goto err; } data->host_stream = host_stream; - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto err; + /* avoid wrap-around with wall-clock */ - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); + ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); + if (ret < 0) + goto err; + + ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); + if (ret < 0) + goto err; + snd_pcm_set_sync(substream); dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p", __func__, hdac_stream(host_stream)->stream_tag, substream); return 0; + +err: + kfree(data); + return ret; } static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -- cgit 1.4.1 From e522a12e4892f99b4251dbdd9dcf37f8dc2cb90e Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 17 May 2023 15:52:30 +0200 Subject: parisc: Improve cache flushing for PCXL in arch_sync_dma_for_cpu() [ Upstream commit 59fa12646d9f56c842b4d5b6418ed77af625c588 ] Add comment in arch_sync_dma_for_device() and handle the direction flag in arch_sync_dma_for_cpu(). When receiving data from the device (DMA_FROM_DEVICE) unconditionally purge the data cache in arch_sync_dma_for_cpu(). Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- arch/parisc/kernel/pci-dma.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c index ba87f791323b..71ed5391f29d 100644 --- a/arch/parisc/kernel/pci-dma.c +++ b/arch/parisc/kernel/pci-dma.c @@ -446,11 +446,27 @@ void arch_dma_free(struct device *dev, size_t size, void *vaddr, void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, enum dma_data_direction dir) { + /* + * fdc: The data cache line is written back to memory, if and only if + * it is dirty, and then invalidated from the data cache. + */ flush_kernel_dcache_range((unsigned long)phys_to_virt(paddr), size); } void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, enum dma_data_direction dir) { - flush_kernel_dcache_range((unsigned long)phys_to_virt(paddr), size); + unsigned long addr = (unsigned long) phys_to_virt(paddr); + + switch (dir) { + case DMA_TO_DEVICE: + case DMA_BIDIRECTIONAL: + flush_kernel_dcache_range(addr, size); + return; + case DMA_FROM_DEVICE: + purge_kernel_dcache_range_asm(addr, addr + size); + return; + default: + BUG(); + } } -- cgit 1.4.1 From 3d48ea53c44b7720606cfd1a2467f689c35af86a Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 17 May 2023 15:54:40 +0200 Subject: parisc: Flush gatt writes and adjust gatt mask in parisc_agp_mask_memory() [ Upstream commit d703797380c540bbeac03f104ebcfc364eaf47cc ] Flush caches after changing gatt entries and calculate entry according to SBA requirements. Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/char/agp/parisc-agp.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index d68d05d5d383..514f9f287a78 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -90,6 +90,9 @@ parisc_agp_tlbflush(struct agp_memory *mem) { struct _parisc_agp_info *info = &parisc_agp_info; + /* force fdc ops to be visible to IOMMU */ + asm_io_sync(); + writeq(info->gart_base | ilog2(info->gart_size), info->ioc_regs+IOC_PCOM); readq(info->ioc_regs+IOC_PCOM); /* flush */ } @@ -158,6 +161,7 @@ parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type) info->gatt[j] = parisc_agp_mask_memory(agp_bridge, paddr, type); + asm_io_fdc(&info->gatt[j]); } } @@ -191,7 +195,16 @@ static unsigned long parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, int type) { - return SBA_PDIR_VALID_BIT | addr; + unsigned ci; /* coherent index */ + dma_addr_t pa; + + pa = addr & IOVP_MASK; + asm("lci 0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pa))); + + pa |= (ci >> PAGE_SHIFT) & 0xff;/* move CI (8 bits) into lowest byte */ + pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */ + + return cpu_to_le64(pa); } static void -- cgit 1.4.1 From 048ad52d528ec23b63db3714e40150786722d77f Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Tue, 9 May 2023 20:57:44 +0200 Subject: MIPS: unhide PATA_PLATFORM [ Upstream commit 75b18aac6fa39a1720677970cfcb52ecea1eb44c ] Alchemy DB1200/DB1300 boards can use the pata_platform driver. Unhide the config entry in all of MIPS. Signed-off-by: Manuel Lauss Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index b26b77673c2c..2f5835e300a8 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -82,6 +82,7 @@ config MIPS select HAVE_LD_DEAD_CODE_DATA_ELIMINATION select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI + select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP -- cgit 1.4.1 From 34dd1a90ab640e47d2b5c27689ccaab8ee47084d Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 10 May 2023 12:33:23 +0200 Subject: MIPS: Restore Au1300 support [ Upstream commit f2041708dee30a3425f680265c337acd28293782 ] The Au1300, at least the one I have to test, uses the NetLogic vendor ID, but commit 95b8a5e0111a ("MIPS: Remove NETLOGIC support") also dropped Au1300 detection. Restore Au1300 detection. Tested on DB1300 with Au1380 chip. Signed-off-by: Manuel Lauss Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/kernel/cpu-probe.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 7ddf07f255f3..6f5d82595877 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -1502,6 +1502,10 @@ static inline void cpu_probe_alchemy(struct cpuinfo_mips *c, unsigned int cpu) break; } break; + case PRID_IMP_NETLOGIC_AU13XX: + c->cputype = CPU_ALCHEMY; + __cpu_name[cpu] = "Au1300"; + break; } } @@ -1861,6 +1865,7 @@ void cpu_probe(void) cpu_probe_mips(c, cpu); break; case PRID_COMP_ALCHEMY: + case PRID_COMP_NETLOGIC: cpu_probe_alchemy(c, cpu); break; case PRID_COMP_SIBYTE: -- cgit 1.4.1 From dd035c08ee96eca2e7dc0fdac88bd53e3db00e34 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Thu, 11 May 2023 17:30:10 +0200 Subject: MIPS: Alchemy: fix dbdma2 [ Upstream commit 2d645604f69f3a772d58ead702f9a8e84ab2b342 ] Various fixes for the Au1200/Au1550/Au1300 DBDMA2 code: - skip cache invalidation if chip has working coherency circuitry. - invalidate KSEG0-portion of the (physical) data address. - force the dma channel doorbell write out to bus immediately with a sync. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/alchemy/common/dbdma.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/arch/mips/alchemy/common/dbdma.c b/arch/mips/alchemy/common/dbdma.c index 5ab043000409..6a3c890f7bbf 100644 --- a/arch/mips/alchemy/common/dbdma.c +++ b/arch/mips/alchemy/common/dbdma.c @@ -30,6 +30,7 @@ * */ +#include /* for dma_default_coherent */ #include #include #include @@ -623,17 +624,18 @@ u32 au1xxx_dbdma_put_source(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) dp->dscr_cmd0 &= ~DSCR_CMD0_IE; /* - * There is an errata on the Au1200/Au1550 parts that could result - * in "stale" data being DMA'ed. It has to do with the snoop logic on - * the cache eviction buffer. DMA_NONCOHERENT is on by default for - * these parts. If it is fixed in the future, these dma_cache_inv will - * just be nothing more than empty macros. See io.h. + * There is an erratum on certain Au1200/Au1550 revisions that could + * result in "stale" data being DMA'ed. It has to do with the snoop + * logic on the cache eviction buffer. dma_default_coherent is set + * to false on these parts. */ - dma_cache_wback_inv((unsigned long)buf, nbytes); + if (!dma_default_coherent) + dma_cache_wback_inv(KSEG0ADDR(buf), nbytes); dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ wmb(); /* drain writebuffer */ dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); ctp->chan_ptr->ddma_dbell = 0; + wmb(); /* force doorbell write out to dma engine */ /* Get next descriptor pointer. */ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); @@ -685,17 +687,18 @@ u32 au1xxx_dbdma_put_dest(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); #endif /* - * There is an errata on the Au1200/Au1550 parts that could result in - * "stale" data being DMA'ed. It has to do with the snoop logic on the - * cache eviction buffer. DMA_NONCOHERENT is on by default for these - * parts. If it is fixed in the future, these dma_cache_inv will just - * be nothing more than empty macros. See io.h. + * There is an erratum on certain Au1200/Au1550 revisions that could + * result in "stale" data being DMA'ed. It has to do with the snoop + * logic on the cache eviction buffer. dma_default_coherent is set + * to false on these parts. */ - dma_cache_inv((unsigned long)buf, nbytes); + if (!dma_default_coherent) + dma_cache_inv(KSEG0ADDR(buf), nbytes); dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ wmb(); /* drain writebuffer */ dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); ctp->chan_ptr->ddma_dbell = 0; + wmb(); /* force doorbell write out to dma engine */ /* Get next descriptor pointer. */ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); -- cgit 1.4.1 From 424fc902728c97e3ce2cf3e07fd10a7cbb13ace1 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Tue, 9 May 2023 18:29:21 +0100 Subject: mips: Move initrd_start check after initrd address sanitisation. [ Upstream commit 4897a898a216058dec55e5e5902534e6e224fcdf ] PAGE_OFFSET is technically a virtual address so when checking the value of initrd_start against it we should make sure that it has been sanitised from the values passed by the bootloader. Without this change, even with a bootloader that passes correct addresses for an initrd, we are failing to load it on MT7621 boards, for example. Signed-off-by: Liviu Dudau Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/kernel/setup.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index f1c88f8a1dc5..81dbb4ef5231 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -158,10 +158,6 @@ static unsigned long __init init_initrd(void) pr_err("initrd start must be page aligned\n"); goto disable; } - if (initrd_start < PAGE_OFFSET) { - pr_err("initrd start < PAGE_OFFSET\n"); - goto disable; - } /* * Sanitize initrd addresses. For example firmware @@ -174,6 +170,11 @@ static unsigned long __init init_initrd(void) initrd_end = (unsigned long)__va(end); initrd_start = (unsigned long)__va(__pa(initrd_start)); + if (initrd_start < PAGE_OFFSET) { + pr_err("initrd start < PAGE_OFFSET\n"); + goto disable; + } + ROOT_DEV = Root_RAM0; return PFN_UP(end); disable: -- cgit 1.4.1 From 7e57a56374aab00ca83c3041b31e578073b0c27b Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 14 Apr 2023 16:25:51 +0100 Subject: ASoC: cs35l41: Fix default regmap values for some registers [ Upstream commit e2d035f5a7d597bbabc268e236ec6c0408c4af0e ] Several values do not match the defaults of CS35L41, fix them. Signed-off-by: Stefan Binding Acked-by: Mark Brown Link: https://lore.kernel.org/r/20230414152552.574502-4-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l41-lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 04be71435491..c2c56e560809 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -46,7 +46,7 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_DSP1_RX5_SRC, 0x00000020 }, { CS35L41_DSP1_RX6_SRC, 0x00000021 }, { CS35L41_DSP1_RX7_SRC, 0x0000003A }, - { CS35L41_DSP1_RX8_SRC, 0x00000001 }, + { CS35L41_DSP1_RX8_SRC, 0x0000003B }, { CS35L41_NGATE1_SRC, 0x00000008 }, { CS35L41_NGATE2_SRC, 0x00000009 }, { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, @@ -58,8 +58,8 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_IRQ1_MASK2, 0xFFFFFFFF }, { CS35L41_IRQ1_MASK3, 0xFFFF87FF }, { CS35L41_IRQ1_MASK4, 0xFEFFFFFF }, - { CS35L41_GPIO1_CTRL1, 0xE1000001 }, - { CS35L41_GPIO2_CTRL1, 0xE1000001 }, + { CS35L41_GPIO1_CTRL1, 0x81000001 }, + { CS35L41_GPIO2_CTRL1, 0x81000001 }, { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, -- cgit 1.4.1 From 75955d69863670dbb949be9e9809ed9d2a7014d9 Mon Sep 17 00:00:00 2001 From: Maxim Kochetkov Date: Fri, 12 May 2023 14:03:42 +0300 Subject: ASoC: dwc: move DMA init to snd_soc_dai_driver probe() [ Upstream commit 011a8719d6105dcb48077ea7a6a88ac019d4aa50 ] When using DMA mode we are facing with Oops: [ 396.458157] Unable to handle kernel access to user memory without uaccess routines at virtual address 000000000000000c [ 396.469374] Oops [#1] [ 396.471839] Modules linked in: [ 396.475144] CPU: 0 PID: 114 Comm: arecord Not tainted 6.0.0-00164-g9a8eccdaf2be-dirty #68 [ 396.483619] Hardware name: YMP ELCT FPGA (DT) [ 396.488156] epc : dmaengine_pcm_open+0x1d2/0x342 [ 396.493227] ra : dmaengine_pcm_open+0x1d2/0x342 [ 396.498140] epc : ffffffff807fe346 ra : ffffffff807fe346 sp : ffffffc804e138f0 [ 396.505602] gp : ffffffff817bf730 tp : ffffffd8042c8ac0 t0 : 6500000000000000 [ 396.513045] t1 : 0000000000000064 t2 : 656e69676e65616d s0 : ffffffc804e13990 [ 396.520477] s1 : ffffffd801b86a18 a0 : 0000000000000026 a1 : ffffffff816920f8 [ 396.527897] a2 : 0000000000000010 a3 : fffffffffffffffe a4 : 0000000000000000 [ 396.535319] a5 : 0000000000000000 a6 : ffffffd801b87040 a7 : 0000000000000038 [ 396.542740] s2 : ffffffd801b94a00 s3 : 0000000000000000 s4 : ffffffd80427f5e8 [ 396.550153] s5 : ffffffd80427f5e8 s6 : ffffffd801b44410 s7 : fffffffffffffff5 [ 396.557569] s8 : 0000000000000800 s9 : 0000000000000001 s10: ffffffff8066d254 [ 396.564978] s11: ffffffd8059cf768 t3 : ffffffff817d5577 t4 : ffffffff817d5577 [ 396.572391] t5 : ffffffff817d5578 t6 : ffffffc804e136e8 [ 396.577876] status: 0000000200000120 badaddr: 000000000000000c cause: 000000000000000d [ 396.586007] [] snd_soc_component_open+0x1a/0x68 [ 396.592439] [] __soc_pcm_open+0xf0/0x502 [ 396.598217] [] soc_pcm_open+0x2e/0x4e [ 396.603741] [] snd_pcm_open_substream+0x442/0x68e [ 396.610313] [] snd_pcm_open+0xfa/0x212 [ 396.615868] [] snd_pcm_capture_open+0x3a/0x60 [ 396.622048] [] snd_open+0xa8/0x17a [ 396.627421] [] chrdev_open+0xa0/0x218 [ 396.632893] [] do_dentry_open+0x17c/0x2a6 [ 396.638713] [] vfs_open+0x1e/0x26 [ 396.643850] [] path_openat+0x96e/0xc96 [ 396.649518] [] do_filp_open+0x7c/0xf6 [ 396.655034] [] do_sys_openat2+0x8a/0x11e [ 396.660765] [] sys_openat+0x50/0x7c [ 396.666068] [] ret_from_syscall+0x0/0x2 [ 396.674964] ---[ end trace 0000000000000000 ]--- It happens because of play_dma_data/capture_dma_data pointers are NULL. Current implementation assigns these pointers at snd_soc_dai_driver startup() callback and reset them back to NULL at shutdown(). But soc_pcm_open() sequence uses DMA pointers in dmaengine_pcm_open() before snd_soc_dai_driver startup(). Most generic DMA capable I2S drivers use snd_soc_dai_driver probe() callback to init DMA pointers only once at probe. So move DMA init to dw_i2s_dai_probe and drop shutdown() and startup() callbacks. Signed-off-by: Maxim Kochetkov Link: https://lore.kernel.org/r/20230512110343.66664-1-fido_max@inbox.ru Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/dwc/dwc-i2s.c | 41 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 3496301582b2..f966d39c5c90 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -183,30 +183,6 @@ static void i2s_stop(struct dw_i2s_dev *dev, } } -static int dw_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - union dw_i2s_snd_dma_data *dma_data = NULL; - - if (!(dev->capability & DWC_I2S_RECORD) && - (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) - return -EINVAL; - - if (!(dev->capability & DWC_I2S_PLAY) && - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) - return -EINVAL; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = &dev->play_dma_data; - else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - dma_data = &dev->capture_dma_data; - - snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data); - - return 0; -} - static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) { u32 ch_reg; @@ -305,12 +281,6 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return 0; } -static void dw_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - snd_soc_dai_set_dma_data(dai, substream, NULL); -} - static int dw_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -382,8 +352,6 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) } static const struct snd_soc_dai_ops dw_i2s_dai_ops = { - .startup = dw_i2s_startup, - .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, @@ -625,6 +593,14 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, } +static int dw_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data); + return 0; +} + static int dw_i2s_probe(struct platform_device *pdev) { const struct i2s_platform_data *pdata = pdev->dev.platform_data; @@ -643,6 +619,7 @@ static int dw_i2s_probe(struct platform_device *pdev) return -ENOMEM; dw_i2s_dai->ops = &dw_i2s_dai_ops; + dw_i2s_dai->probe = dw_i2s_dai_probe; dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(dev->i2s_base)) -- cgit 1.4.1 From 41c383c4965717bbdf4d1e9f609925aa12403152 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Wed, 26 Apr 2023 17:40:05 +0100 Subject: xen/blkfront: Only check REQ_FUA for writes [ Upstream commit b6ebaa8100090092aa602530d7e8316816d0c98d ] The existing code silently converts read operations with the REQ_FUA bit set into write-barrier operations. This results in data loss as the backend scribbles zeroes over the data instead of returning it. While the REQ_FUA bit doesn't make sense on a read operation, at least one well-known out-of-tree kernel module does set it and since it results in data loss, let's be safe here and only look at REQ_FUA for writes. Signed-off-by: Ross Lagerwall Acked-by: Juergen Gross Link: https://lore.kernel.org/r/20230426164005.2213139-1-ross.lagerwall@citrix.com Signed-off-by: Juergen Gross Signed-off-by: Sasha Levin --- drivers/block/xen-blkfront.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 35b9bcad9db9..5ddf393aa390 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -780,7 +780,8 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri ring_req->u.rw.handle = info->handle; ring_req->operation = rq_data_dir(req) ? BLKIF_OP_WRITE : BLKIF_OP_READ; - if (req_op(req) == REQ_OP_FLUSH || req->cmd_flags & REQ_FUA) { + if (req_op(req) == REQ_OP_FLUSH || + (req_op(req) == REQ_OP_WRITE && (req->cmd_flags & REQ_FUA))) { /* * Ideally we can do an unordered flush-to-disk. * In case the backend onlysupports barriers, use that. -- cgit 1.4.1 From 8ca9880735b0a33a4ff54328d6c5eb60fe2b0a6d Mon Sep 17 00:00:00 2001 From: Sukrut Bellary Date: Wed, 3 May 2023 16:15:07 -0700 Subject: drm:amd:amdgpu: Fix missing buffer object unlock in failure path [ Upstream commit 60ecaaf54886b0642d5c4744f7fbf1ff0d6b3e42 ] smatch warning - 1) drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c:3615 gfx_v9_0_kiq_resume() warn: inconsistent returns 'ring->mqd_obj->tbo.base.resv'. 2) drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c:6901 gfx_v10_0_kiq_resume() warn: inconsistent returns 'ring->mqd_obj->tbo.base.resv'. Signed-off-by: Sukrut Bellary Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 4 +++- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 2127aab74a68..84a36b50ddd8 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -6969,8 +6969,10 @@ static int gfx_v10_0_kiq_resume(struct amdgpu_device *adev) return r; r = amdgpu_bo_kmap(ring->mqd_obj, (void **)&ring->mqd_ptr); - if (unlikely(r != 0)) + if (unlikely(r != 0)) { + amdgpu_bo_unreserve(ring->mqd_obj); return r; + } gfx_v10_0_kiq_init_queue(ring); amdgpu_bo_kunmap(ring->mqd_obj); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 1f3fdf6cb903..fe371022e510 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -3650,8 +3650,10 @@ static int gfx_v9_0_kiq_resume(struct amdgpu_device *adev) return r; r = amdgpu_bo_kmap(ring->mqd_obj, (void **)&ring->mqd_ptr); - if (unlikely(r != 0)) + if (unlikely(r != 0)) { + amdgpu_bo_unreserve(ring->mqd_obj); return r; + } gfx_v9_0_kiq_init_queue(ring); amdgpu_bo_kunmap(ring->mqd_obj); -- cgit 1.4.1 From c9c205945033a41f3e5e38ffefd24270bb2bc393 Mon Sep 17 00:00:00 2001 From: Wenwen Chen Date: Thu, 25 May 2023 16:26:26 +0800 Subject: io_uring: unlock sqd->lock before sq thread release CPU [ Upstream commit 533ab73f5b5c95dcb4152b52d5482abcc824c690 ] The sq thread actively releases CPU resources by calling the cond_resched() and schedule() interfaces when it is idle. Therefore, more resources are available for other threads to run. There exists a problem in sq thread: it does not unlock sqd->lock before releasing CPU resources every time. This makes other threads pending on sqd->lock for a long time. For example, the following interfaces all require sqd->lock: io_sq_offload_create(), io_register_iowq_max_workers() and io_ring_exit_work(). Before the sq thread releases CPU resources, unlocking sqd->lock will provide the user a better experience because it can respond quickly to user requests. Signed-off-by: Kanchan Joshi Signed-off-by: Wenwen Chen Link: https://lore.kernel.org/r/20230525082626.577862-1-wenwen.chen@samsung.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- io_uring/sqpoll.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c index 559652380672..6ffa5cf1bbb8 100644 --- a/io_uring/sqpoll.c +++ b/io_uring/sqpoll.c @@ -256,9 +256,13 @@ static int io_sq_thread(void *data) sqt_spin = true; if (sqt_spin || !time_after(jiffies, timeout)) { - cond_resched(); if (sqt_spin) timeout = jiffies + sqd->sq_thread_idle; + if (unlikely(need_resched())) { + mutex_unlock(&sqd->lock); + cond_resched(); + mutex_lock(&sqd->lock); + } continue; } -- cgit 1.4.1 From 22efb27a21b6ebe6be0760949d877141077de701 Mon Sep 17 00:00:00 2001 From: Tatsuki Sugiura Date: Sat, 20 May 2023 21:23:50 +0900 Subject: NVMe: Add MAXIO 1602 to bogus nid list. [ Upstream commit a3a9d63dcd15535e7fdf4c7c1b32bfaed762973a ] HIKSEMI FUTURE M.2 SSD uses the same dummy nguid and eui64. I confirmed it with my two devices. This patch marks the controller as NVME_QUIRK_BOGUS_NID. --------------------------------------------------------- sugi@tempest:~% sudo nvme id-ctrl /dev/nvme0 NVME Identify Controller: vid : 0x1e4b ssvid : 0x1e4b sn : 30096022612 mn : HS-SSD-FUTURE 2048G fr : SN10542 rab : 0 ieee : 000000 cmic : 0 mdts : 7 cntlid : 0 ver : 0x10400 rtd3r : 0x7a120 rtd3e : 0x1e8480 oaes : 0x200 ctratt : 0x2 rrls : 0 cntrltype : 1 fguid : 00000000-0000-0000-0000-000000000000 --------------------------------------------------------- --------------------------------------------------------- sugi@tempest:~% sudo nvme id-ns /dev/nvme0n1 NVME Identify Namespace 1: nguid : 00000000000000000000000000000000 eui64 : 0000000000000002 lbaf 0 : ms:0 lbads:9 rp:0 (in use) --------------------------------------------------------- Signed-off-by: Tatsuki Sugiura Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index ba4903c86f7f..145fa7ef3f74 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3537,6 +3537,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1e4B, 0x1202), /* MAXIO MAP1202 */ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1e4B, 0x1602), /* MAXIO MAP1602 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1cc1, 0x5350), /* ADATA XPG GAMMIX S50 */ .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1dbe, 0x5236), /* ADATA XPG GAMMIX S70 */ -- cgit 1.4.1 From 9a9adc42a590208ad3e912c605a7b51b66a6b13f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 30 May 2023 11:01:22 +0100 Subject: irqchip/gic: Correctly validate OF quirk descriptors [ Upstream commit 91539341a3b6e9c868024a4292455dae36e6f58c ] When checking for OF quirks, make sure either 'compatible' or 'property' is set, and give up otherwise. This avoids non-OF quirks being randomly applied as they don't have any of the OF data that need checking. Cc: Douglas Anderson Reported-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Fixes: 44bd78dd2b88 ("irqchip/gic-v3: Disable pseudo NMIs on Mediatek devices w/ firmware issues") Signed-off-by: Marc Zyngier Signed-off-by: Sasha Levin --- drivers/irqchip/irq-gic-common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index de47b51cdadb..afd6a1841715 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -16,6 +16,8 @@ void gic_enable_of_quirks(const struct device_node *np, const struct gic_quirk *quirks, void *data) { for (; quirks->desc; quirks++) { + if (!quirks->compatible && !quirks->property) + continue; if (quirks->compatible && !of_device_is_compatible(np, quirks->compatible)) continue; -- cgit 1.4.1 From b11f953a61e95812af86fb24aad4ea10d309c84c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Jun 2023 14:34:48 +0200 Subject: wifi: cfg80211: fix locking in regulatory disconnect commit f7e60032c6618dfd643c7210d5cba2789e2de2e2 upstream. This should use wiphy_lock() now instead of requiring the RTNL, since __cfg80211_leave() via cfg80211_leave() is now requiring that lock to be held. Fixes: a05829a7222e ("cfg80211: avoid holding the RTNL when calling the driver") Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/reg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 4f3f31244e8b..50b16e643f38 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2478,11 +2478,11 @@ static void reg_leave_invalid_chans(struct wiphy *wiphy) struct wireless_dev *wdev; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - ASSERT_RTNL(); - + wiphy_lock(wiphy); list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) if (!reg_wdev_chan_valid(wiphy, wdev)) cfg80211_leave(rdev, wdev); + wiphy_unlock(wiphy); } static void reg_check_chans_work(struct work_struct *work) -- cgit 1.4.1 From 6d304091e091ede9de77a141c2d04a0ff7dfb54f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 9 Jun 2023 13:48:44 +0300 Subject: wifi: cfg80211: fix double lock bug in reg_wdev_chan_valid() commit 996c3117dae4c02b38a3cb68e5c2aec9d907ec15 upstream. The locking was changed recently so now the caller holds the wiphy_lock() lock. Taking the lock inside the reg_wdev_chan_valid() function will lead to a deadlock. Fixes: f7e60032c661 ("wifi: cfg80211: fix locking in regulatory disconnect") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/40c4114a-6cb4-4abf-b013-300b598aba65@moroto.mountain Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/reg.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 50b16e643f38..522180919a1a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2442,11 +2442,8 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: - wiphy_lock(wiphy); ret = cfg80211_reg_can_beacon_relax(wiphy, &chandef, iftype); - wiphy_unlock(wiphy); - if (!ret) return ret; break; -- cgit 1.4.1 From 3a340c63c0c34d867ced7045d473535687a17491 Mon Sep 17 00:00:00 2001 From: Benjamin Segall Date: Tue, 30 May 2023 11:32:28 -0700 Subject: epoll: ep_autoremove_wake_function should use list_del_init_careful commit 2192bba03d80f829233bfa34506b428f71e531e7 upstream. autoremove_wake_function uses list_del_init_careful, so should epoll's more aggressive variant. It only doesn't because it was copied from an older wait.c rather than the most recent. [bsegall@google.com: add comment] Link: https://lkml.kernel.org/r/xm26bki0ulsr.fsf_-_@google.com Link: https://lkml.kernel.org/r/xm26pm6hvfer.fsf@google.com Fixes: a16ceb139610 ("epoll: autoremove wakers even more aggressively") Signed-off-by: Ben Segall Cc: Al Viro Cc: Christian Brauner Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/eventpoll.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 64659b110973..eccecd3fac90 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1760,7 +1760,11 @@ static int ep_autoremove_wake_function(struct wait_queue_entry *wq_entry, { int ret = default_wake_function(wq_entry, mode, sync, key); - list_del_init(&wq_entry->entry); + /* + * Pairs with list_empty_careful in ep_poll, and ensures future loop + * iterations see the cause of this wakeup. + */ + list_del_init_careful(&wq_entry->entry); return ret; } -- cgit 1.4.1 From 534b4bbc8589bd4618b507a47ca242efcbd248b9 Mon Sep 17 00:00:00 2001 From: Luís Henriques Date: Mon, 22 May 2023 11:21:12 +0100 Subject: ocfs2: fix use-after-free when unmounting read-only filesystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 50d927880e0f90d5cb25e897e9d03e5edacc79a8 upstream. It's trivial to trigger a use-after-free bug in the ocfs2 quotas code using fstest generic/452. After a read-only remount, quotas are suspended and ocfs2_mem_dqinfo is freed through ->ocfs2_local_free_info(). When unmounting the filesystem, an UAF access to the oinfo will eventually cause a crash. BUG: KASAN: slab-use-after-free in timer_delete+0x54/0xc0 Read of size 8 at addr ffff8880389a8208 by task umount/669 ... Call Trace: ... timer_delete+0x54/0xc0 try_to_grab_pending+0x31/0x230 __cancel_work_timer+0x6c/0x270 ocfs2_disable_quotas.isra.0+0x3e/0xf0 [ocfs2] ocfs2_dismount_volume+0xdd/0x450 [ocfs2] generic_shutdown_super+0xaa/0x280 kill_block_super+0x46/0x70 deactivate_locked_super+0x4d/0xb0 cleanup_mnt+0x135/0x1f0 ... Allocated by task 632: kasan_save_stack+0x1c/0x40 kasan_set_track+0x21/0x30 __kasan_kmalloc+0x8b/0x90 ocfs2_local_read_info+0xe3/0x9a0 [ocfs2] dquot_load_quota_sb+0x34b/0x680 dquot_load_quota_inode+0xfe/0x1a0 ocfs2_enable_quotas+0x190/0x2f0 [ocfs2] ocfs2_fill_super+0x14ef/0x2120 [ocfs2] mount_bdev+0x1be/0x200 legacy_get_tree+0x6c/0xb0 vfs_get_tree+0x3e/0x110 path_mount+0xa90/0xe10 __x64_sys_mount+0x16f/0x1a0 do_syscall_64+0x43/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc Freed by task 650: kasan_save_stack+0x1c/0x40 kasan_set_track+0x21/0x30 kasan_save_free_info+0x2a/0x50 __kasan_slab_free+0xf9/0x150 __kmem_cache_free+0x89/0x180 ocfs2_local_free_info+0x2ba/0x3f0 [ocfs2] dquot_disable+0x35f/0xa70 ocfs2_susp_quotas.isra.0+0x159/0x1a0 [ocfs2] ocfs2_remount+0x150/0x580 [ocfs2] reconfigure_super+0x1a5/0x3a0 path_mount+0xc8a/0xe10 __x64_sys_mount+0x16f/0x1a0 do_syscall_64+0x43/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc Link: https://lkml.kernel.org/r/20230522102112.9031-1-lhenriques@suse.de Signed-off-by: Luís Henriques Reviewed-by: Joseph Qi Tested-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Gang He Cc: Jun Piao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/super.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 0b0e6a132101..988d1c076861 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -952,8 +952,10 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb) for (type = 0; type < OCFS2_MAXQUOTAS; type++) { if (!sb_has_quota_loaded(sb, type)) continue; - oinfo = sb_dqinfo(sb, type)->dqi_priv; - cancel_delayed_work_sync(&oinfo->dqi_sync_work); + if (!sb_has_quota_suspended(sb, type)) { + oinfo = sb_dqinfo(sb, type)->dqi_priv; + cancel_delayed_work_sync(&oinfo->dqi_sync_work); + } inode = igrab(sb->s_dquot.files[type]); /* Turn off quotas. This will remove all dquot structures from * memory and so they will be automatically synced to global -- cgit 1.4.1 From 9f17645f85281317564247eb06f2e37cc63a2a96 Mon Sep 17 00:00:00 2001 From: Luís Henriques Date: Mon, 29 May 2023 16:26:45 +0100 Subject: ocfs2: check new file size on fallocate call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 26a6ffff7de5dd369cdb12e38ba11db682f1dec0 upstream. When changing a file size with fallocate() the new size isn't being checked. In particular, the FSIZE ulimit isn't being checked, which makes fstest generic/228 fail. Simply adding a call to inode_newsize_ok() fixes this issue. Link: https://lkml.kernel.org/r/20230529152645.32680-1-lhenriques@suse.de Signed-off-by: Luís Henriques Reviewed-by: Mark Fasheh Reviewed-by: Joseph Qi Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Gang He Cc: Jun Piao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/file.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4d78e0979517..cae410568bb2 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2100,14 +2100,20 @@ static long ocfs2_fallocate(struct file *file, int mode, loff_t offset, struct ocfs2_space_resv sr; int change_size = 1; int cmd = OCFS2_IOC_RESVSP64; + int ret = 0; if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) return -EOPNOTSUPP; if (!ocfs2_writes_unwritten_extents(osb)) return -EOPNOTSUPP; - if (mode & FALLOC_FL_KEEP_SIZE) + if (mode & FALLOC_FL_KEEP_SIZE) { change_size = 0; + } else { + ret = inode_newsize_ok(inode, offset + len); + if (ret) + return ret; + } if (mode & FALLOC_FL_PUNCH_HOLE) cmd = OCFS2_IOC_UNRESVSP64; -- cgit 1.4.1 From 447f325497dcd41fd17cf415c1d1f5c79d192f48 Mon Sep 17 00:00:00 2001 From: Nhat Pham Date: Tue, 30 May 2023 15:24:40 -0700 Subject: zswap: do not shrink if cgroup may not zswap commit 0bdf0efa180a9cb1361cbded4e2260a49306ac89 upstream. Before storing a page, zswap first checks if the number of stored pages exceeds the limit specified by memory.zswap.max, for each cgroup in the hierarchy. If this limit is reached or exceeded, then zswap shrinking is triggered and short-circuits the store attempt. However, since the zswap's LRU is not memcg-aware, this can create the following pathological behavior: the cgroup whose zswap limit is 0 will evict pages from other cgroups continually, without lowering its own zswap usage. This means the shrinking will continue until the need for swap ceases or the pool becomes empty. As a result of this, we observe a disproportionate amount of zswap writeback and a perpetually small zswap pool in our experiments, even though the pool limit is never hit. More generally, a cgroup might unnecessarily evict pages from other cgroups before we drive the memcg back below its limit. This patch fixes the issue by rejecting zswap store attempt without shrinking the pool when obj_cgroup_may_zswap() returns false. [akpm@linux-foundation.org: fix return of unintialized value] [akpm@linux-foundation.org: s/ENOSPC/ENOMEM/] Link: https://lkml.kernel.org/r/20230530222440.2777700-1-nphamcs@gmail.com Link: https://lkml.kernel.org/r/20230530232435.3097106-1-nphamcs@gmail.com Fixes: f4840ccfca25 ("zswap: memcg accounting") Signed-off-by: Nhat Pham Cc: Dan Streetman Cc: Domenico Cerasuolo Cc: Johannes Weiner Cc: Seth Jennings Cc: Vitaly Wool Cc: Yosry Ahmed Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/zswap.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mm/zswap.c b/mm/zswap.c index 708b82dbe8a4..b3829ada4a41 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -1138,9 +1138,16 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset, goto reject; } + /* + * XXX: zswap reclaim does not work with cgroups yet. Without a + * cgroup-aware entry LRU, we will push out entries system-wide based on + * local cgroup limits. + */ objcg = get_obj_cgroup_from_page(page); - if (objcg && !obj_cgroup_may_zswap(objcg)) - goto shrink; + if (objcg && !obj_cgroup_may_zswap(objcg)) { + ret = -ENOMEM; + goto reject; + } /* reclaim space if needed */ if (zswap_is_full()) { -- cgit 1.4.1 From 3d4bc38f716caf534846ae4fceb144043c48973e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Sun, 12 Feb 2023 13:16:32 +0100 Subject: nios2: dts: Fix tse_mac "max-frame-size" property commit 85041e12418fd0c08ff972b7729f7971afb361f8 upstream. The given value of 1518 seems to refer to the layer 2 ethernet frame size without 802.1Q tag. Actual use of the "max-frame-size" including in the consumer of the "altr,tse-1.0" compatible is the MTU. Fixes: 95acd4c7b69c ("nios2: Device tree support") Fixes: 61c610ec61bb ("nios2: Add Max10 device tree") Cc: Signed-off-by: Janne Grunau Signed-off-by: Dinh Nguyen Signed-off-by: Greg Kroah-Hartman --- arch/nios2/boot/dts/10m50_devboard.dts | 2 +- arch/nios2/boot/dts/3c120_devboard.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/nios2/boot/dts/10m50_devboard.dts b/arch/nios2/boot/dts/10m50_devboard.dts index 56339bef3247..0e7e5b0dd685 100644 --- a/arch/nios2/boot/dts/10m50_devboard.dts +++ b/arch/nios2/boot/dts/10m50_devboard.dts @@ -97,7 +97,7 @@ rx-fifo-depth = <8192>; tx-fifo-depth = <8192>; address-bits = <48>; - max-frame-size = <1518>; + max-frame-size = <1500>; local-mac-address = [00 00 00 00 00 00]; altr,has-supplementary-unicast; altr,enable-sup-addr = <1>; diff --git a/arch/nios2/boot/dts/3c120_devboard.dts b/arch/nios2/boot/dts/3c120_devboard.dts index d10fb81686c7..3ee316906379 100644 --- a/arch/nios2/boot/dts/3c120_devboard.dts +++ b/arch/nios2/boot/dts/3c120_devboard.dts @@ -106,7 +106,7 @@ interrupt-names = "rx_irq", "tx_irq"; rx-fifo-depth = <8192>; tx-fifo-depth = <8192>; - max-frame-size = <1518>; + max-frame-size = <1500>; local-mac-address = [ 00 00 00 00 00 00 ]; phy-mode = "rgmii-id"; phy-handle = <&phy0>; -- cgit 1.4.1 From 8f47a9665aee4ebdc095cbd4be9fdb44cef9bdbb Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 13 May 2023 19:24:28 +0900 Subject: nilfs2: fix incomplete buffer cleanup in nilfs_btnode_abort_change_key() commit 2f012f2baca140c488e43d27a374029c1e59098d upstream. A syzbot fault injection test reported that nilfs_btnode_create_block, a helper function that allocates a new node block for b-trees, causes a kernel BUG for disk images where the file system block size is smaller than the page size. This was due to unexpected flags on the newly allocated buffer head, and it turned out to be because the buffer flags were not cleared by nilfs_btnode_abort_change_key() after an error occurred during a b-tree update operation and the buffer was later reused in that state. Fix this issue by using nilfs_btnode_delete() to abandon the unused preallocated buffer in nilfs_btnode_abort_change_key(). Link: https://lkml.kernel.org/r/20230513102428.10223-1-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi Reported-by: syzbot+b0a35a5c1f7e846d3b09@syzkaller.appspotmail.com Closes: https://lkml.kernel.org/r/000000000000d1d6c205ebc4d512@google.com Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/btnode.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c index e74fda212620..ee2cde07264b 100644 --- a/fs/nilfs2/btnode.c +++ b/fs/nilfs2/btnode.c @@ -285,6 +285,14 @@ void nilfs_btnode_abort_change_key(struct address_space *btnc, if (nbh == NULL) { /* blocksize == pagesize */ xa_erase_irq(&btnc->i_pages, newkey); unlock_page(ctxt->bh->b_page); - } else - brelse(nbh); + } else { + /* + * When canceling a buffer that a prepare operation has + * allocated to copy a node block to another location, use + * nilfs_btnode_delete() to initialize and release the buffer + * so that the buffer flags will not be in an inconsistent + * state when it is reallocated. + */ + nilfs_btnode_delete(nbh); + } } -- cgit 1.4.1 From 69caea4eed1cfb9f9e4373e47ba839958061c0ac Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Wed, 24 May 2023 18:43:48 +0900 Subject: nilfs2: fix possible out-of-bounds segment allocation in resize ioctl commit fee5eaecca86afa544355569b831c1f90f334b85 upstream. Syzbot reports that in its stress test for resize ioctl, the log writing function nilfs_segctor_do_construct hits a WARN_ON in nilfs_segctor_truncate_segments(). It turned out that there is a problem with the current implementation of the resize ioctl, which changes the writable range on the device (the range of allocatable segments) at the end of the resize process. This order is necessary for file system expansion to avoid corrupting the superblock at trailing edge. However, in the case of a file system shrink, if log writes occur after truncating out-of-bounds trailing segments and before the resize is complete, segments may be allocated from the truncated space. The userspace resize tool was fine as it limits the range of allocatable segments before performing the resize, but it can run into this issue if the resize ioctl is called alone. Fix this issue by changing nilfs_sufile_resize() to update the range of allocatable segments immediately after successful truncation of segment space in case of file system shrink. Link: https://lkml.kernel.org/r/20230524094348.3784-1-konishi.ryusuke@gmail.com Fixes: 4e33f9eab07e ("nilfs2: implement resize ioctl") Signed-off-by: Ryusuke Konishi Reported-by: syzbot+33494cd0df2ec2931851@syzkaller.appspotmail.com Closes: https://lkml.kernel.org/r/0000000000005434c405fbbafdc5@google.com Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/sufile.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index dc359b56fdfa..2c6078a6b8ec 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c @@ -779,6 +779,15 @@ int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs) goto out_header; sui->ncleansegs -= nsegs - newnsegs; + + /* + * If the sufile is successfully truncated, immediately adjust + * the segment allocation space while locking the semaphore + * "mi_sem" so that nilfs_sufile_alloc() never allocates + * segments in the truncated space. + */ + sui->allocmax = newnsegs - 1; + sui->allocmin = 0; } kaddr = kmap_atomic(header_bh->b_page); -- cgit 1.4.1 From ad64865722b67af392c76bc2f9ddc4555554812c Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 26 May 2023 11:13:32 +0900 Subject: nilfs2: reject devices with insufficient block count commit 92c5d1b860e9581d64baca76779576c0ab0d943d upstream. The current sanity check for nilfs2 geometry information lacks checks for the number of segments stored in superblocks, so even for device images that have been destructively truncated or have an unusually high number of segments, the mount operation may succeed. This causes out-of-bounds block I/O on file system block reads or log writes to the segments, the latter in particular causing "a_ops->writepages" to repeatedly fail, resulting in sync_inodes_sb() to hang. Fix this issue by checking the number of segments stored in the superblock and avoiding mounting devices that can cause out-of-bounds accesses. To eliminate the possibility of overflow when calculating the number of blocks required for the device from the number of segments, this also adds a helper function to calculate the upper bound on the number of segments and inserts a check using it. Link: https://lkml.kernel.org/r/20230526021332.3431-1-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi Reported-by: syzbot+7d50f1e54a12ba3aeae2@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=7d50f1e54a12ba3aeae2 Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/the_nilfs.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 2894152a6b25..0f0667957c81 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -405,6 +405,18 @@ unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs) 100)); } +/** + * nilfs_max_segment_count - calculate the maximum number of segments + * @nilfs: nilfs object + */ +static u64 nilfs_max_segment_count(struct the_nilfs *nilfs) +{ + u64 max_count = U64_MAX; + + do_div(max_count, nilfs->ns_blocks_per_segment); + return min_t(u64, max_count, ULONG_MAX); +} + void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs) { nilfs->ns_nsegments = nsegs; @@ -414,6 +426,8 @@ void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs) static int nilfs_store_disk_layout(struct the_nilfs *nilfs, struct nilfs_super_block *sbp) { + u64 nsegments, nblocks; + if (le32_to_cpu(sbp->s_rev_level) < NILFS_MIN_SUPP_REV) { nilfs_err(nilfs->ns_sb, "unsupported revision (superblock rev.=%d.%d, current rev.=%d.%d). Please check the version of mkfs.nilfs(2).", @@ -457,7 +471,34 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs, return -EINVAL; } - nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments)); + nsegments = le64_to_cpu(sbp->s_nsegments); + if (nsegments > nilfs_max_segment_count(nilfs)) { + nilfs_err(nilfs->ns_sb, + "segment count %llu exceeds upper limit (%llu segments)", + (unsigned long long)nsegments, + (unsigned long long)nilfs_max_segment_count(nilfs)); + return -EINVAL; + } + + nblocks = sb_bdev_nr_blocks(nilfs->ns_sb); + if (nblocks) { + u64 min_block_count = nsegments * nilfs->ns_blocks_per_segment; + /* + * To avoid failing to mount early device images without a + * second superblock, exclude that block count from the + * "min_block_count" calculation. + */ + + if (nblocks < min_block_count) { + nilfs_err(nilfs->ns_sb, + "total number of segment blocks %llu exceeds device size (%llu blocks)", + (unsigned long long)min_block_count, + (unsigned long long)nblocks); + return -EINVAL; + } + } + + nilfs_set_nsegments(nilfs, nsegments); nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed); return 0; } -- cgit 1.4.1 From cffaa97ffb672f41cf27e328479c52a2a32ad683 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 15 Jun 2023 14:35:52 +0800 Subject: LoongArch: Fix perf event id calculation commit 962369120d750cbc9c4dc492f32b4304669ff6aa upstream. LoongArch PMCFG has 10bit event id rather than 8 bit, so fix it. Cc: stable@vger.kernel.org Signed-off-by: Jun Yi Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/perf_event.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/loongarch/kernel/perf_event.c b/arch/loongarch/kernel/perf_event.c index 707bd32e5c4f..3a2edb157b65 100644 --- a/arch/loongarch/kernel/perf_event.c +++ b/arch/loongarch/kernel/perf_event.c @@ -271,7 +271,7 @@ static void loongarch_pmu_enable_event(struct hw_perf_event *evt, int idx) WARN_ON(idx < 0 || idx >= loongarch_pmu.num_counters); /* Make sure interrupt enabled. */ - cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(evt->event_base & 0xff) | + cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(evt->event_base) | (evt->config_base & M_PERFCTL_CONFIG_MASK) | CSR_PERFCTRL_IE; cpu = (event->cpu >= 0) ? event->cpu : smp_processor_id(); @@ -594,7 +594,7 @@ static struct pmu pmu = { static unsigned int loongarch_pmu_perf_event_encode(const struct loongarch_perf_event *pev) { - return (pev->event_id & 0xff); + return M_PERFCTL_EVENT(pev->event_id); } static const struct loongarch_perf_event *loongarch_pmu_map_general_event(int idx) @@ -849,7 +849,7 @@ static void resume_local_counters(void) static const struct loongarch_perf_event *loongarch_pmu_map_raw_event(u64 config) { - raw_event.event_id = config & 0xff; + raw_event.event_id = M_PERFCTL_EVENT(config); return &raw_event; } -- cgit 1.4.1 From c9c3163c7ab901fb1f184a821285851534ba46e2 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 12 Jun 2023 13:51:36 -0600 Subject: io_uring/net: save msghdr->msg_control for retries commit cac9e4418f4cbd548ccb065b3adcafe073f7f7d2 upstream. If the application sets ->msg_control and we have to later retry this command, or if it got queued with IOSQE_ASYNC to begin with, then we need to retain the original msg_control value. This is due to the net stack overwriting this field with an in-kernel pointer, to copy it in. Hitting that path for the second time will now fail the copy from user, as it's attempting to copy from a non-user address. Cc: stable@vger.kernel.org # 5.10+ Link: https://github.com/axboe/liburing/issues/880 Reported-and-tested-by: Marek Majkowski Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/net.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/io_uring/net.c b/io_uring/net.c index f6d8b02387a9..05792a689994 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -65,6 +65,7 @@ struct io_sr_msg { u16 addr_len; u16 buf_group; void __user *addr; + void __user *msg_control; /* used only for send zerocopy */ struct io_kiocb *notif; }; @@ -182,11 +183,15 @@ static int io_sendmsg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg) { struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); + int ret; iomsg->msg.msg_name = &iomsg->addr; iomsg->free_iov = iomsg->fast_iov; - return sendmsg_copy_msghdr(&iomsg->msg, sr->umsg, sr->msg_flags, + ret = sendmsg_copy_msghdr(&iomsg->msg, sr->umsg, sr->msg_flags, &iomsg->free_iov); + /* save msg_control as sys_sendmsg() overwrites it */ + sr->msg_control = iomsg->msg.msg_control; + return ret; } int io_send_prep_async(struct io_kiocb *req) @@ -284,6 +289,7 @@ int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) if (req_has_async_data(req)) { kmsg = req->async_data; + kmsg->msg.msg_control = sr->msg_control; } else { ret = io_sendmsg_copy_hdr(req, &iomsg); if (ret) -- cgit 1.4.1 From 013027918a4efa807409fcb356009c117e4d181a Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 19 May 2023 16:47:36 +0200 Subject: kexec: support purgatories with .text.hot sections commit 8652d44f466ad5772e7d1756e9457046189b0dfc upstream. Patch series "kexec: Fix kexec_file_load for llvm16 with PGO", v7. When upreving llvm I realised that kexec stopped working on my test platform. The reason seems to be that due to PGO there are multiple .text sections on the purgatory, and kexec does not supports that. This patch (of 4): Clang16 links the purgatory text in two sections when PGO is in use: [ 1] .text PROGBITS 0000000000000000 00000040 00000000000011a1 0000000000000000 AX 0 0 16 [ 2] .rela.text RELA 0000000000000000 00003498 0000000000000648 0000000000000018 I 24 1 8 ... [17] .text.hot. PROGBITS 0000000000000000 00003220 000000000000020b 0000000000000000 AX 0 0 1 [18] .rela.text.hot. RELA 0000000000000000 00004428 0000000000000078 0000000000000018 I 24 17 8 And both of them have their range [sh_addr ... sh_addr+sh_size] on the area pointed by `e_entry`. This causes that image->start is calculated twice, once for .text and another time for .text.hot. The second calculation leaves image->start in a random location. Because of this, the system crashes immediately after: kexec_core: Starting new kernel Link: https://lkml.kernel.org/r/20230321-kexec_clang16-v7-0-b05c520b7296@chromium.org Link: https://lkml.kernel.org/r/20230321-kexec_clang16-v7-1-b05c520b7296@chromium.org Fixes: 930457057abe ("kernel/kexec_file.c: split up __kexec_load_puragory") Signed-off-by: Ricardo Ribalda Reviewed-by: Ross Zwisler Reviewed-by: Steven Rostedt (Google) Reviewed-by: Philipp Rudo Cc: Albert Ou Cc: Baoquan He Cc: Borislav Petkov (AMD) Cc: Christophe Leroy Cc: Dave Hansen Cc: Dave Young Cc: Eric W. Biederman Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Michael Ellerman Cc: Nathan Chancellor Cc: Nicholas Piggin Cc: Nick Desaulniers Cc: Palmer Dabbelt Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Simon Horman Cc: Thomas Gleixner Cc: Tom Rix Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- kernel/kexec_file.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 45637511e0de..6d787ae9deca 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -898,10 +898,22 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi, } offset = ALIGN(offset, align); + + /* + * Check if the segment contains the entry point, if so, + * calculate the value of image->start based on it. + * If the compiler has produced more than one .text section + * (Eg: .text.hot), they are generally after the main .text + * section, and they shall not be used to calculate + * image->start. So do not re-calculate image->start if it + * is not set to the initial value, and warn the user so they + * have a chance to fix their purgatory's linker script. + */ if (sechdrs[i].sh_flags & SHF_EXECINSTR && pi->ehdr->e_entry >= sechdrs[i].sh_addr && pi->ehdr->e_entry < (sechdrs[i].sh_addr - + sechdrs[i].sh_size)) { + + sechdrs[i].sh_size) && + !WARN_ON(kbuf->image->start != pi->ehdr->e_entry)) { kbuf->image->start -= sechdrs[i].sh_addr; kbuf->image->start += kbuf->mem + offset; } -- cgit 1.4.1 From 2cf6e32e86ea1976689914070a825150a7658adb Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 19 May 2023 16:47:37 +0200 Subject: x86/purgatory: remove PGO flags commit 97b6b9cbba40a21c1d9a344d5c1991f8cfbf136e upstream. If profile-guided optimization is enabled, the purgatory ends up with multiple .text sections. This is not supported by kexec and crashes the system. Link: https://lkml.kernel.org/r/20230321-kexec_clang16-v7-2-b05c520b7296@chromium.org Fixes: 930457057abe ("kernel/kexec_file.c: split up __kexec_load_puragory") Signed-off-by: Ricardo Ribalda Cc: Cc: Albert Ou Cc: Baoquan He Cc: Borislav Petkov (AMD) Cc: Christophe Leroy Cc: Dave Hansen Cc: Dave Young Cc: Eric W. Biederman Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Michael Ellerman Cc: Nathan Chancellor Cc: Nicholas Piggin Cc: Nick Desaulniers Cc: Palmer Dabbelt Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Philipp Rudo Cc: Ross Zwisler Cc: Simon Horman Cc: Steven Rostedt (Google) Cc: Thomas Gleixner Cc: Tom Rix Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- arch/x86/purgatory/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile index 82fec66d46d2..42abd6af1198 100644 --- a/arch/x86/purgatory/Makefile +++ b/arch/x86/purgatory/Makefile @@ -14,6 +14,11 @@ $(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE CFLAGS_sha256.o := -D__DISABLE_EXPORTS +# When profile-guided optimization is enabled, llvm emits two different +# overlapping text sections, which is not supported by kexec. Remove profile +# optimization flags. +KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS)) + # When linking purgatory.ro with -r unresolved symbols are not checked, # also link a purgatory.chk binary without -r to check for unresolved symbols. PURGATORY_LDFLAGS := -e purgatory_start -z nodefaultlib -- cgit 1.4.1 From 352f62431ad3ac59ef252594948a861c64386226 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 19 May 2023 16:47:39 +0200 Subject: riscv/purgatory: remove PGO flags commit 88ac3bbcf73853880a9b2a65c67e6854390741cc upstream. If profile-guided optimization is enabled, the purgatory ends up with multiple .text sections. This is not supported by kexec and crashes the system. Link: https://lkml.kernel.org/r/20230321-kexec_clang16-v7-4-b05c520b7296@chromium.org Fixes: 930457057abe ("kernel/kexec_file.c: split up __kexec_load_puragory") Signed-off-by: Ricardo Ribalda Acked-by: Palmer Dabbelt Cc: Cc: Albert Ou Cc: Baoquan He Cc: Borislav Petkov (AMD) Cc: Christophe Leroy Cc: Dave Hansen Cc: Dave Young Cc: Eric W. Biederman Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Michael Ellerman Cc: Nathan Chancellor Cc: Nicholas Piggin Cc: Nick Desaulniers Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Philipp Rudo Cc: Ross Zwisler Cc: Simon Horman Cc: Steven Rostedt (Google) Cc: Thomas Gleixner Cc: Tom Rix Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- arch/riscv/purgatory/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile index 659e21862077..6574c91c99b1 100644 --- a/arch/riscv/purgatory/Makefile +++ b/arch/riscv/purgatory/Makefile @@ -25,6 +25,11 @@ CFLAGS_sha256.o := -D__DISABLE_EXPORTS CFLAGS_string.o := -D__DISABLE_EXPORTS CFLAGS_ctype.o := -D__DISABLE_EXPORTS +# When profile-guided optimization is enabled, llvm emits two different +# overlapping text sections, which is not supported by kexec. Remove profile +# optimization flags. +KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS)) + # When linking purgatory.ro with -r unresolved symbols are not checked, # also link a purgatory.chk binary without -r to check for unresolved symbols. PURGATORY_LDFLAGS := -e purgatory_start -z nodefaultlib -- cgit 1.4.1 From 7e23b1ec720aa572a94554ce1444cb756d2e0ed8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 19 May 2023 16:47:38 +0200 Subject: powerpc/purgatory: remove PGO flags commit 20188baceb7a1463dc0bcb0c8678b69c2f447df6 upstream. If profile-guided optimization is enabled, the purgatory ends up with multiple .text sections. This is not supported by kexec and crashes the system. Link: https://lkml.kernel.org/r/20230321-kexec_clang16-v7-3-b05c520b7296@chromium.org Fixes: 930457057abe ("kernel/kexec_file.c: split up __kexec_load_puragory") Signed-off-by: Ricardo Ribalda Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Christophe Leroy Cc: Cc: Albert Ou Cc: Baoquan He Cc: Borislav Petkov (AMD) Cc: Dave Hansen Cc: Dave Young Cc: Eric W. Biederman Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Palmer Dabbelt Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Philipp Rudo Cc: Ross Zwisler Cc: Simon Horman Cc: Steven Rostedt (Google) Cc: Thomas Gleixner Cc: Tom Rix Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/purgatory/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/powerpc/purgatory/Makefile b/arch/powerpc/purgatory/Makefile index a81d155b89ae..df50b155c767 100644 --- a/arch/powerpc/purgatory/Makefile +++ b/arch/powerpc/purgatory/Makefile @@ -4,6 +4,11 @@ KASAN_SANITIZE := n targets += trampoline_$(BITS).o purgatory.ro +# When profile-guided optimization is enabled, llvm emits two different +# overlapping text sections, which is not supported by kexec. Remove profile +# optimization flags. +KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS)) + LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined $(obj)/purgatory.ro: $(obj)/trampoline_$(BITS).o FORCE -- cgit 1.4.1 From 4389fb6b6a9d31704cc576b7671b6dfe9ac9f966 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sun, 11 Jun 2023 08:09:13 +0800 Subject: btrfs: do not ASSERT() on duplicated global roots commit 745806fb4554f334e6406fa82b328562aa48f08f upstream. [BUG] Syzbot reports a reproducible ASSERT() when using rescue=usebackuproot mount option on a corrupted fs. The full report can be found here: https://syzkaller.appspot.com/bug?extid=c4614eae20a166c25bf0 BTRFS error (device loop0: state C): failed to load root csum assertion failed: !tmp, in fs/btrfs/disk-io.c:1103 ------------[ cut here ]------------ kernel BUG at fs/btrfs/ctree.h:3664! invalid opcode: 0000 [#1] PREEMPT SMP KASAN CPU: 1 PID: 3608 Comm: syz-executor356 Not tainted 6.0.0-rc7-syzkaller-00029-g3800a713b607 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/26/2022 RIP: 0010:assertfail+0x1a/0x1c fs/btrfs/ctree.h:3663 RSP: 0018:ffffc90003aaf250 EFLAGS: 00010246 RAX: 0000000000000032 RBX: 0000000000000000 RCX: f21c13f886638400 RDX: 0000000000000000 RSI: 0000000080000000 RDI: 0000000000000000 RBP: ffff888021c640a0 R08: ffffffff816bd38d R09: ffffed10173667f1 R10: ffffed10173667f1 R11: 1ffff110173667f0 R12: dffffc0000000000 R13: ffff8880229c21f7 R14: ffff888021c64060 R15: ffff8880226c0000 FS: 0000555556a73300(0000) GS:ffff8880b9b00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000055a2637d7a00 CR3: 00000000709c4000 CR4: 00000000003506e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: btrfs_global_root_insert+0x1a7/0x1b0 fs/btrfs/disk-io.c:1103 load_global_roots_objectid+0x482/0x8c0 fs/btrfs/disk-io.c:2467 load_global_roots fs/btrfs/disk-io.c:2501 [inline] btrfs_read_roots fs/btrfs/disk-io.c:2528 [inline] init_tree_roots+0xccb/0x203c fs/btrfs/disk-io.c:2939 open_ctree+0x1e53/0x33df fs/btrfs/disk-io.c:3574 btrfs_fill_super+0x1c6/0x2d0 fs/btrfs/super.c:1456 btrfs_mount_root+0x885/0x9a0 fs/btrfs/super.c:1824 legacy_get_tree+0xea/0x180 fs/fs_context.c:610 vfs_get_tree+0x88/0x270 fs/super.c:1530 fc_mount fs/namespace.c:1043 [inline] vfs_kern_mount+0xc9/0x160 fs/namespace.c:1073 btrfs_mount+0x3d3/0xbb0 fs/btrfs/super.c:1884 [CAUSE] Since the introduction of global roots, we handle csum/extent/free-space-tree roots as global roots, even if no extent-tree-v2 feature is enabled. So for regular csum/extent/fst roots, we load them into fs_info::global_root_tree rb tree. And we should not expect any conflicts in that rb tree, thus we have an ASSERT() inside btrfs_global_root_insert(). But rescue=usebackuproot can break the assumption, as we will try to load those trees again and again as long as we have bad roots and have backup roots slot remaining. So in that case we can have conflicting roots in the rb tree, and triggering the ASSERT() crash. [FIX] We can safely remove that ASSERT(), as the caller will properly put the offending root. To make further debugging easier, also add two explicit error messages: - Error message for conflicting global roots - Error message when using backup roots slot Reported-by: syzbot+a694851c6ab28cbcfb9c@syzkaller.appspotmail.com Fixes: abed4aaae4f7 ("btrfs: track the csum, extent, and free space trees in a rb tree") CC: stable@vger.kernel.org # 6.1+ Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/disk-io.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index ca69a46f1006..f22e00dfec6c 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1098,13 +1098,18 @@ int btrfs_global_root_insert(struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; struct rb_node *tmp; + int ret = 0; write_lock(&fs_info->global_root_lock); tmp = rb_find_add(&root->rb_node, &fs_info->global_root_tree, global_root_cmp); write_unlock(&fs_info->global_root_lock); - ASSERT(!tmp); - return tmp ? -EEXIST : 0; + if (tmp) { + ret = -EEXIST; + btrfs_warn(fs_info, "global root %llu %llu already exists", + root->root_key.objectid, root->root_key.offset); + } + return ret; } void btrfs_global_root_delete(struct btrfs_root *root) @@ -2934,6 +2939,7 @@ static int __cold init_tree_roots(struct btrfs_fs_info *fs_info) /* We can't trust the free space cache either */ btrfs_set_opt(fs_info->mount_opt, CLEAR_CACHE); + btrfs_warn(fs_info, "try to load backup roots slot %d", i); ret = read_backup_root(fs_info, i); backup_index = ret; if (ret < 0) -- cgit 1.4.1 From 4e9da0cda14906f91f564f74ef9b8f6be49a2e99 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 8 Jun 2023 11:10:25 +0200 Subject: btrfs: fix iomap_begin length for nocow writes commit 7833b865953c8e62abc76a3261c04132b2fb69de upstream. can_nocow_extent can reduce the len passed in, which needs to be propagated to btrfs_dio_iomap_begin so that iomap does not submit more data then is mapped. This problems exists since the btrfs_get_blocks_direct helper was added in commit c5794e51784a ("btrfs: Factor out write portion of btrfs_get_blocks_direct"), but the ordered_extent splitting added in commit b73a6fd1b1ef ("btrfs: split partial dio bios before submit") added a WARN_ON that made a syzkaller test fail. Reported-by: syzbot+ee90502d5c8fd1d0dd93@syzkaller.appspotmail.com Fixes: c5794e51784a ("btrfs: Factor out write portion of btrfs_get_blocks_direct") CC: stable@vger.kernel.org # 6.1+ Tested-by: syzbot+ee90502d5c8fd1d0dd93@syzkaller.appspotmail.com Reviewed-by: Filipe Manana Signed-off-by: Christoph Hellwig Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/inode.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 56e9efbffd58..8cf1097439a0 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7387,7 +7387,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start, static int btrfs_get_blocks_direct_write(struct extent_map **map, struct inode *inode, struct btrfs_dio_data *dio_data, - u64 start, u64 len, + u64 start, u64 *lenp, unsigned int iomap_flags) { const bool nowait = (iomap_flags & IOMAP_NOWAIT); @@ -7398,6 +7398,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, struct btrfs_block_group *bg; bool can_nocow = false; bool space_reserved = false; + u64 len = *lenp; u64 prev_len; int ret = 0; @@ -7468,15 +7469,19 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, free_extent_map(em); *map = NULL; - if (nowait) - return -EAGAIN; + if (nowait) { + ret = -EAGAIN; + goto out; + } /* * If we could not allocate data space before locking the file * range and we can't do a NOCOW write, then we have to fail. */ - if (!dio_data->data_space_reserved) - return -ENOSPC; + if (!dio_data->data_space_reserved) { + ret = -ENOSPC; + goto out; + } /* * We have to COW and we have already reserved data space before, @@ -7517,6 +7522,7 @@ out: btrfs_delalloc_release_extents(BTRFS_I(inode), len); btrfs_delalloc_release_metadata(BTRFS_I(inode), len, true); } + *lenp = len; return ret; } @@ -7693,7 +7699,7 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start, if (write) { ret = btrfs_get_blocks_direct_write(&em, inode, dio_data, - start, len, flags); + start, &len, flags); if (ret < 0) goto unlock_err; unlock_extents = true; -- cgit 1.4.1 From 9e1c7968a27512168951b7cebd14d814f98747d9 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 9 Jun 2023 10:53:41 -0700 Subject: btrfs: can_nocow_file_extent should pass down args->strict from callers commit deccae40e4b30f98837e44225194d80c8baf2233 upstream. Commit 619104ba453ad0 ("btrfs: move common NOCOW checks against a file extent into a helper") changed our call to btrfs_cross_ref_exist() to always pass false for the 'strict' parameter. We're passing this down through the stack so that we can do a full check for cross references during swapfile activation. With strict always false, this test fails: btrfs subvol create swappy chattr +C swappy fallocate -l1G swappy/swapfile chmod 600 swappy/swapfile mkswap swappy/swapfile btrfs subvol snap swappy swapsnap btrfs subvol del -C swapsnap btrfs fi sync / sync;sync;sync swapon swappy/swapfile The fix is to just use args->strict, and everyone except swapfile activation is passing false. Fixes: 619104ba453ad0 ("btrfs: move common NOCOW checks against a file extent into a helper") CC: stable@vger.kernel.org # 6.1+ Reviewed-by: Filipe Manana Signed-off-by: Chris Mason Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8cf1097439a0..c5583fc2a585 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1849,7 +1849,7 @@ static int can_nocow_file_extent(struct btrfs_path *path, ret = btrfs_cross_ref_exist(root, btrfs_ino(inode), key->offset - args->extent_offset, - args->disk_bytenr, false, path); + args->disk_bytenr, args->strict, path); WARN_ON_ONCE(ret > 0 && is_freespace_inode); if (ret != 0) goto out; -- cgit 1.4.1 From 029e0f1f752165b620fc5d28589523e6b0d6d36d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Jun 2023 15:28:18 +0200 Subject: ALSA: usb-audio: Fix broken resume due to UAC3 power state commit 8ba61c9f6c9bdfbf9d197b0282641d24ae909778 upstream. As reported in the bugzilla below, the PM resume of a UAC3 device may fail due to the incomplete power state change, stuck at D1. The reason is that the driver expects the full D0 power state change only at hw_params, while the normal PCM resume procedure doesn't call hw_params. For fixing the bug, we add the same power state update to D0 at the prepare callback, which is certainly called by the resume procedure. Note that, with this change, the power state change in the hw_params becomes almost redundant, since snd_usb_hw_params() doesn't touch the parameters (at least it tires so). But dropping it is still a bit risky (e.g. we have the media-driver binding), so I leave the D0 power state change in snd_usb_hw_params() as is for now. Fixes: a0a4959eb4e9 ("ALSA: usb-audio: Operate UAC3 Power Domains in PCM callbacks") Cc: Link: https://bugzilla.kernel.org/show_bug.cgi?id=217539 Link: https://lore.kernel.org/r/20230612132818.29486-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 1e1d7458bce1..f2c798c4506b 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -650,6 +650,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) goto unlock; } + ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0); + if (ret < 0) + goto unlock; + again: if (subs->sync_endpoint) { ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint); -- cgit 1.4.1 From 1886db9a4ee1fe3d716647ad10fb9fb647df7673 Mon Sep 17 00:00:00 2001 From: Lukasz Tyl Date: Wed, 14 Jun 2023 14:25:24 +0200 Subject: ALSA: usb-audio: Add quirk flag for HEM devices to enable native DSD playback commit 122e2cb7e1a30438cc0e8bf70d4279db245d7d5b upstream. This commit adds new DEVICE_FLG with QUIRK_FLAG_DSD_RAW and Vendor Id for HEM devices which supports native DSD. Prior to this change Linux kernel was not enabling native DSD playback for HEM devices, and as a result, DSD audio was being converted to PCM "on the fly". HEM devices, when connected to the system, would only play audio in PCM format, even if the source material was in DSD format. With the addition of new VENDOR_FLG in the quircks.c file, the devices are now correctly recognized, and raw DSD data is transmitted to the device, allowing for native DSD playback. Signed-off-by: Lukasz Tyl Cc: Link: https://lore.kernel.org/r/20230614122524.30271-1-ltyl@hem-e.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 3ecd1ba7fd4b..6cf55b7f7a04 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2191,6 +2191,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x2ab6, /* T+A devices */ QUIRK_FLAG_DSD_RAW), + VENDOR_FLG(0x3336, /* HEM devices */ + QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x3353, /* Khadas devices */ QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x3842, /* EVGA */ -- cgit 1.4.1 From 088ad777eeaefb6fda5918f370adda76a65d001a Mon Sep 17 00:00:00 2001 From: Li Lingfeng Date: Tue, 6 Jun 2023 20:20:24 +0800 Subject: dm thin metadata: check fail_io before using data_sm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit cb65b282c9640c27d3129e2e04b711ce1b352838 upstream. Must check pmd->fail_io before using pmd->data_sm since pmd->data_sm may be destroyed by other processes. P1(kworker) P2(message) do_worker process_prepared process_prepared_discard_passdown_pt2 dm_pool_dec_data_range pool_message commit dm_pool_commit_metadata ↓ // commit failed metadata_operation_failed abort_transaction dm_pool_abort_metadata __open_or_format_metadata ↓ dm_sm_disk_open ↓ // open failed // pmd->data_sm is NULL dm_sm_dec_blocks ↓ // try to access pmd->data_sm --> UAF As shown above, if dm_pool_commit_metadata() and dm_pool_abort_metadata() fail in pool_message process, kworker may trigger UAF. Fixes: be500ed721a6 ("dm space maps: improve performance with inc/dec on ranges of blocks") Cc: stable@vger.kernel.org Signed-off-by: Li Lingfeng Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 80545ec54121..59eb1cb7037a 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1750,13 +1750,15 @@ int dm_thin_remove_range(struct dm_thin_device *td, int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *result) { - int r; + int r = -EINVAL; uint32_t ref_count; down_read(&pmd->root_lock); - r = dm_sm_get_count(pmd->data_sm, b, &ref_count); - if (!r) - *result = (ref_count > 1); + if (!pmd->fail_io) { + r = dm_sm_get_count(pmd->data_sm, b, &ref_count); + if (!r) + *result = (ref_count > 1); + } up_read(&pmd->root_lock); return r; @@ -1764,10 +1766,11 @@ int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *re int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e) { - int r = 0; + int r = -EINVAL; pmd_write_lock(pmd); - r = dm_sm_inc_blocks(pmd->data_sm, b, e); + if (!pmd->fail_io) + r = dm_sm_inc_blocks(pmd->data_sm, b, e); pmd_write_unlock(pmd); return r; @@ -1775,10 +1778,11 @@ int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_ int dm_pool_dec_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e) { - int r = 0; + int r = -EINVAL; pmd_write_lock(pmd); - r = dm_sm_dec_blocks(pmd->data_sm, b, e); + if (!pmd->fail_io) + r = dm_sm_dec_blocks(pmd->data_sm, b, e); pmd_write_unlock(pmd); return r; -- cgit 1.4.1 From bfaf388d359922d81e7e7f0551ebe9297d8da98e Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 13 Jun 2023 20:05:34 -0400 Subject: dm thin: fix issue_discard to pass GFP_NOIO to __blkdev_issue_discard commit 722d90822321497e2837cfc9000202e256e6b32f upstream. issue_discard() passes GFP_NOWAIT to __blkdev_issue_discard() despite its code assuming bio_alloc() always succeeds. Commit 3dba53a958a75 ("dm thin: use __blkdev_issue_discard for async discard support") clearly shows where things went bad: Before commit 3dba53a958a75, dm-thin.c's open-coded __blkdev_issue_discard_async() properly handled using GFP_NOWAIT. Unfortunately __blkdev_issue_discard() doesn't and it was missed during review. Cc: stable@vger.kernel.org Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index d12aff50974a..601f9e4e6234 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -398,8 +398,7 @@ static int issue_discard(struct discard_op *op, dm_block_t data_b, dm_block_t da sector_t s = block_to_sectors(tc->pool, data_b); sector_t len = block_to_sectors(tc->pool, data_e - data_b); - return __blkdev_issue_discard(tc->pool_dev->bdev, s, len, GFP_NOWAIT, - &op->bio); + return __blkdev_issue_discard(tc->pool_dev->bdev, s, len, GFP_NOIO, &op->bio); } static void end_discard(struct discard_op *op, int r) -- cgit 1.4.1 From 33965ac3409153d3f1af8039ad671b6b47dd0b2d Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 14 Jun 2023 11:17:14 +0200 Subject: net: ethernet: stmicro: stmmac: fix possible memory leak in __stmmac_open commit 30134b7c47bd28fdb4db4d12aef824e0579cfee4 upstream. Fix a possible memory leak in __stmmac_open when stmmac_init_phy fails. It's also needed to free everything allocated by stmmac_setup_dma_desc and not just the dma_conf struct. Drop free_dma_desc_resources from __stmmac_open and correctly call free_dma_desc_resources on each user of __stmmac_open on error. Reported-by: Jose Abreu Fixes: ba39b344e924 ("net: ethernet: stmicro: stmmac: generate stmmac dma conf before open") Signed-off-by: Christian Marangi Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Reviewed-by: Jose Abreu Link: https://lore.kernel.org/r/20230614091714.15912-1-ansuelsmth@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 490315723062..30ce07305578 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3865,7 +3865,6 @@ irq_error: stmmac_hw_teardown(dev); init_error: - free_dma_desc_resources(priv, &priv->dma_conf); phylink_disconnect_phy(priv->phylink); init_phy_error: pm_runtime_put(priv->device); @@ -3883,6 +3882,9 @@ static int stmmac_open(struct net_device *dev) return PTR_ERR(dma_conf); ret = __stmmac_open(dev, dma_conf); + if (ret) + free_dma_desc_resources(priv, dma_conf); + kfree(dma_conf); return ret; } @@ -5607,12 +5609,15 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) stmmac_release(dev); ret = __stmmac_open(dev, dma_conf); - kfree(dma_conf); if (ret) { + free_dma_desc_resources(priv, dma_conf); + kfree(dma_conf); netdev_err(priv->dev, "failed reopening the interface after MTU change\n"); return ret; } + kfree(dma_conf); + stmmac_set_rx_mode(dev); } -- cgit 1.4.1 From 5a144bad3e75e17d38b3b7609400606dba9d12c8 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 15 Jun 2023 12:22:11 +1000 Subject: nouveau: fix client work fence deletion race commit c8a5d5ea3ba6a18958f8d76430e4cd68eea33943 upstream. This seems to have existed for ever but is now more apparant after commit 9bff18d13473 ("drm/ttm: use per BO cleanup workers") My analysis: two threads are running, one in the irq signalling the fence, in dma_fence_signal_timestamp_locked, it has done the DMA_FENCE_FLAG_SIGNALLED_BIT setting, but hasn't yet reached the callbacks. The second thread in nouveau_cli_work_ready, where it sees the fence is signalled, so then puts the fence, cleanups the object and frees the work item, which contains the callback. Thread one goes again and tries to call the callback and causes the use-after-free. Proposed fix: lock the fence signalled check in nouveau_cli_work_ready, so either the callbacks are done or the memory is freed. Reviewed-by: Karol Herbst Fixes: 11e451e74050 ("drm/nouveau: remove fence wait code from deferred client work handler") Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie Link: https://lore.kernel.org/dri-devel/20230615024008.1600281-1-airlied@gmail.com/ Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/nouveau/nouveau_drm.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index fd99ec0f4257..28062d682f43 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -139,10 +139,16 @@ nouveau_name(struct drm_device *dev) static inline bool nouveau_cli_work_ready(struct dma_fence *fence) { - if (!dma_fence_is_signaled(fence)) - return false; - dma_fence_put(fence); - return true; + bool ret = true; + + spin_lock_irq(fence->lock); + if (!dma_fence_is_signaled_locked(fence)) + ret = false; + spin_unlock_irq(fence->lock); + + if (ret == true) + dma_fence_put(fence); + return ret; } static void -- cgit 1.4.1 From f6d74371ce15e176020ea2441d70c51c7457b680 Mon Sep 17 00:00:00 2001 From: Edward Srouji Date: Mon, 5 Jun 2023 13:33:24 +0300 Subject: RDMA/uverbs: Restrict usage of privileged QKEYs commit 0cadb4db79e1d9eea66711c4031e435c2191907e upstream. According to the IB specification rel-1.6, section 3.5.3: "QKEYs with the most significant bit set are considered controlled QKEYs, and a HCA does not allow a consumer to arbitrarily specify a controlled QKEY." Thus, block non-privileged users from setting such a QKEY. Cc: stable@vger.kernel.org Fixes: bc38a6abdd5a ("[PATCH] IB uverbs: core implementation") Signed-off-by: Edward Srouji Link: https://lore.kernel.org/r/c00c809ddafaaf87d6f6cb827978670989a511b3.1685960567.git.leon@kernel.org Signed-off-by: Leon Romanovsky Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/uverbs_cmd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 4796f6a8828c..e836c9c477f6 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -1850,8 +1850,13 @@ static int modify_qp(struct uverbs_attr_bundle *attrs, attr->path_mtu = cmd->base.path_mtu; if (cmd->base.attr_mask & IB_QP_PATH_MIG_STATE) attr->path_mig_state = cmd->base.path_mig_state; - if (cmd->base.attr_mask & IB_QP_QKEY) + if (cmd->base.attr_mask & IB_QP_QKEY) { + if (cmd->base.qkey & IB_QP_SET_QKEY && !capable(CAP_NET_RAW)) { + ret = -EPERM; + goto release_qp; + } attr->qkey = cmd->base.qkey; + } if (cmd->base.attr_mask & IB_QP_RQ_PSN) attr->rq_psn = cmd->base.rq_psn; if (cmd->base.attr_mask & IB_QP_SQ_PSN) -- cgit 1.4.1 From 5d1fdfb3d185a9726478ba174235eff7c16f1101 Mon Sep 17 00:00:00 2001 From: Sonny Jiang Date: Tue, 6 Jun 2023 17:18:52 -0400 Subject: drm/amdgpu: vcn_4_0 set instance 0 init sched score to 1 commit 9db5ec1ceb5303398ec4f899d691073d531257c3 upstream. Only vcn0 can process AV1 codecx. In order to use both vcn0 and vcn1 in h264/265 transcode to AV1 cases, set vcn0 sched score to 1 at initialization time. Signed-off-by: Sonny Jiang Reviewed-by: Leo Liu Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org # 6.1.x Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index dcc49b01bd59..300bb926dcba 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -117,7 +117,11 @@ static int vcn_v4_0_sw_init(void *handle) if (adev->vcn.harvest_config & (1 << i)) continue; - atomic_set(&adev->vcn.inst[i].sched_score, 0); + /* Init instance 0 sched_score to 1, so it's scheduled after other instances */ + if (i == 0) + atomic_set(&adev->vcn.inst[i].sched_score, 1); + else + atomic_set(&adev->vcn.inst[i].sched_score, 0); /* VCN UNIFIED TRAP */ r = amdgpu_irq_add_id(adev, amdgpu_ih_clientid_vcns[i], -- cgit 1.4.1 From 2cb6026df193dfbbdd89c06e1259326a85da148b Mon Sep 17 00:00:00 2001 From: Wes Huang Date: Thu, 8 Jun 2023 11:01:42 +0800 Subject: net: usb: qmi_wwan: add support for Compal RXM-G1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 863199199713908afaa47ba09332b87621c12496 upstream. Add support for Compal RXM-G1 which is based on Qualcomm SDX55 chip. This patch adds support for two compositions: 0x9091: DIAG + MODEM + QMI_RMNET + ADB 0x90db: DIAG + DUN + RMNET + DPL + QDSS(Trace) + ADB T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=5000 MxCh= 0 D: Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 P: Vendor=05c6 ProdID=9091 Rev= 4.14 S: Manufacturer=QCOM S: Product=SDXPRAIRIE-MTP _SN:719AB680 S: SerialNumber=719ab680 C:* #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=896mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=(none) E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=82(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan E: Ad=84(I) Atr=03(Int.) MxPS= 8 Ivl=32ms E: Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=5000 MxCh= 0 D: Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 P: Vendor=05c6 ProdID=90db Rev= 4.14 S: Manufacturer=QCOM S: Product=SDXPRAIRIE-MTP _SN:719AB680 S: SerialNumber=719ab680 C:* #Ifs= 6 Cfg#= 1 Atr=80 MxPwr=896mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=(none) E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=82(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan E: Ad=84(I) Atr=03(Int.) MxPS= 8 Ivl=32ms E: Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=8f(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=85(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms Cc: stable@vger.kernel.org Signed-off-by: Wes Huang Acked-by: Bjørn Mork Link: https://lore.kernel.org/r/20230608030141.3546-1-wes.huang@moxa.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/qmi_wwan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 45c258501135..68829a5a93d3 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1219,7 +1219,9 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x05c6, 0x9080, 8)}, {QMI_FIXED_INTF(0x05c6, 0x9083, 3)}, {QMI_FIXED_INTF(0x05c6, 0x9084, 4)}, + {QMI_QUIRK_SET_DTR(0x05c6, 0x9091, 2)}, /* Compal RXM-G1 */ {QMI_FIXED_INTF(0x05c6, 0x90b2, 3)}, /* ublox R410M */ + {QMI_QUIRK_SET_DTR(0x05c6, 0x90db, 2)}, /* Compal RXM-G1 */ {QMI_FIXED_INTF(0x05c6, 0x920d, 0)}, {QMI_FIXED_INTF(0x05c6, 0x920d, 5)}, {QMI_QUIRK_SET_DTR(0x05c6, 0x9625, 4)}, /* YUGA CLM920-NC5 */ -- cgit 1.4.1 From ee8c6580c3106b0061c7cdff1804e5a1f1e1d7fe Mon Sep 17 00:00:00 2001 From: Hersen Wu Date: Thu, 25 May 2023 08:37:40 -0400 Subject: drm/amd/display: edp do not add non-edid timings commit e749dd10e5f292061ad63d2b030194bf7d7d452c upstream. [Why] most edp support only timings from edid. applying non-edid timings, especially those timings out of edp bandwidth, may damage edp. [How] do not add non-edid timings for edp. Cc: Mario Limonciello Cc: Alex Deucher Cc: stable@vger.kernel.org Acked-by: Stylon Wang Signed-off-by: Hersen Wu Reviewed-by: Roman Li Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 8ab0dd799b3c..fe179261f327 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -6942,7 +6942,13 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) drm_add_modes_noedid(connector, 640, 480); } else { amdgpu_dm_connector_ddc_get_modes(connector, edid); - amdgpu_dm_connector_add_common_modes(encoder, connector); + /* most eDP supports only timings from its edid, + * usually only detailed timings are available + * from eDP edid. timings which are not from edid + * may damage eDP + */ + if (connector->connector_type != DRM_MODE_CONNECTOR_eDP) + amdgpu_dm_connector_add_common_modes(encoder, connector); amdgpu_dm_connector_add_freesync_modes(connector, edid); } amdgpu_dm_fbc_init(connector); -- cgit 1.4.1 From b2706d862b6577b8218bdef1c238e175624b906d Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 7 Jun 2023 01:41:22 -0500 Subject: drm/amd: Make sure image is written to trigger VBIOS image update flow commit 3eb1a3a04056ba3df3205e169b8acc9da0c65a94 upstream. The VBIOS image update flow requires userspace to: 1) Write the image to `psp_vbflash` 2) Read `psp_vbflash` 3) Poll `psp_vbflash_status` to check for completion If userspace reads `psp_vbflash` before writing an image, it's possible that it causes problems that can put the dGPU into an invalid state. Explicitly check that an image has been written before letting a read succeed. Cc: stable@vger.kernel.org Fixes: 8424f2ccb3c0 ("drm/amdgpu/psp: Add vbflash sysfs interface support") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 3b8825a3e233..2f11c3060782 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -3550,6 +3550,9 @@ static ssize_t amdgpu_psp_vbflash_read(struct file *filp, struct kobject *kobj, void *fw_pri_cpu_addr; int ret; + if (adev->psp.vbflash_image_size == 0) + return -EINVAL; + dev_info(adev->dev, "VBIOS flash to PSP started"); ret = amdgpu_bo_create_kernel(adev, adev->psp.vbflash_image_size, -- cgit 1.4.1 From b69a10df900386871703a4cc3be974485f52ff7c Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 7 Jun 2023 01:45:20 -0500 Subject: drm/amd: Tighten permissions on VBIOS flashing attributes commit 7ab1a4913d0051cf5196ef7987b5fa42c25e13b6 upstream. Non-root users shouldn't be able to try to trigger a VBIOS flash or query the flashing status. This should be reserved for users with the appropriate permissions. Cc: stable@vger.kernel.org Fixes: 8424f2ccb3c0 ("drm/amdgpu/psp: Add vbflash sysfs interface support") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 2f11c3060782..a3cd816f98a1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -3604,13 +3604,13 @@ static ssize_t amdgpu_psp_vbflash_status(struct device *dev, } static const struct bin_attribute psp_vbflash_bin_attr = { - .attr = {.name = "psp_vbflash", .mode = 0664}, + .attr = {.name = "psp_vbflash", .mode = 0660}, .size = 0, .write = amdgpu_psp_vbflash_write, .read = amdgpu_psp_vbflash_read, }; -static DEVICE_ATTR(psp_vbflash_status, 0444, amdgpu_psp_vbflash_status, NULL); +static DEVICE_ATTR(psp_vbflash_status, 0440, amdgpu_psp_vbflash_status, NULL); int amdgpu_psp_sysfs_init(struct amdgpu_device *adev) { -- cgit 1.4.1 From 6f5b5ce9397d91b0a8bc8d15dc8fd72bc0aacaa4 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Thu, 8 Jun 2023 22:07:11 +0800 Subject: drm/amd/pm: workaround for compute workload type on some skus commit 7ca302d488f80cf4529620acc1c545f9022d8bb8 upstream. On smu 13.0.0, the compute workload type cannot be set on all the skus due to some other problems. This workaround is to make sure compute workload type can also run on some specific skus. v2: keep the variable consistent Signed-off-by: Kenneth Feng Acked-by: Lijo Lazar Reviewed-by: Feifei Xu Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org # 6.1.x Signed-off-by: Greg Kroah-Hartman --- .../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 33 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 508e392547d7..54fc42dad775 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -1675,10 +1675,39 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu, } } - /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */ - workload_type = smu_cmn_to_asic_specific_index(smu, + if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_COMPUTE && + (((smu->adev->pdev->device == 0x744C) && (smu->adev->pdev->revision == 0xC8)) || + ((smu->adev->pdev->device == 0x744C) && (smu->adev->pdev->revision == 0xCC)))) { + ret = smu_cmn_update_table(smu, + SMU_TABLE_ACTIVITY_MONITOR_COEFF, + WORKLOAD_PPLIB_COMPUTE_BIT, + (void *)(&activity_monitor_external), + false); + if (ret) { + dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__); + return ret; + } + + ret = smu_cmn_update_table(smu, + SMU_TABLE_ACTIVITY_MONITOR_COEFF, + WORKLOAD_PPLIB_CUSTOM_BIT, + (void *)(&activity_monitor_external), + true); + if (ret) { + dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__); + return ret; + } + + workload_type = smu_cmn_to_asic_specific_index(smu, + CMN2ASIC_MAPPING_WORKLOAD, + PP_SMC_POWER_PROFILE_CUSTOM); + } else { + /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */ + workload_type = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_WORKLOAD, smu->power_profile_mode); + } + if (workload_type < 0) return -EINVAL; -- cgit 1.4.1 From 21863dc45aed8d68a2573cce032ecbc8fe7c5bf5 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 6 Jun 2023 11:14:04 -0400 Subject: drm/amdgpu: add missing radeon secondary PCI ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e61f67749b351c19455ce3085af2ae9af80023bc upstream. 0x5b70 is a missing RV370 secondary id. Add it so we don't try and probe it with amdgpu. Cc: michel@daenzer.net Reviewed-by: Michel Dänzer Tested-by: Michel Dänzer Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 88a9ece7f464..49a023f59b2f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -1605,6 +1605,7 @@ static const u16 amdgpu_unsupported_pciidlist[] = { 0x5874, 0x5940, 0x5941, + 0x5b70, 0x5b72, 0x5b73, 0x5b74, -- cgit 1.4.1 From 1eb0eff7da975426b1072528cb5e6bbddd29195c Mon Sep 17 00:00:00 2001 From: Edson Juliano Drosdeck Date: Fri, 9 Jun 2023 17:10:58 -0300 Subject: ALSA: hda/realtek: Add a quirk for Compaq N14JP6 commit a2a871483161014f1bcc4e9a04354b01aa77cedb upstream. Add a quirk for Compaq N14JP6 to fixup ALC897 headset MIC no sound. Signed-off-by: Edson Juliano Drosdeck Cc: Link: https://lore.kernel.org/r/20230609201058.523499-1-edson.drosdeck@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 48a0e87136f1..920e44ba998a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11738,6 +11738,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), + SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), #if 0 /* Below is a quirk table taken from the old code. -- cgit 1.4.1 From 081b5f1ebc731d74a9b9fe8a0c8d163f7f1aff52 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 24 May 2023 13:47:04 +0300 Subject: thunderbolt: Do not touch CL state configuration during discovery commit 3fe95742af29b8b4eccab2ba94bc521805c6e10c upstream. If the boot firmware has already established tunnels, especially ones that have special requirements from the link such as DisplayPort, we should not blindly enable CL states (nor change the TMU configuration). Otherwise the existing tunnels may not work as expected. For this reason, skip the CL state enabling when we go over the existing topology. This will also keep the TMU settings untouched because we do not change the TMU configuration when CL states are not enabled. Reported-by: Koba Ko Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/7831 Cc: stable@vger.kernel.org # v6.0+ Acked-By: Yehezkel Bernat Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/tb.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 3f1ab30c4fb1..e1eb092ad1d6 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -607,6 +607,7 @@ static void tb_scan_port(struct tb_port *port) { struct tb_cm *tcm = tb_priv(port->sw->tb); struct tb_port *upstream_port; + bool discovery = false; struct tb_switch *sw; int ret; @@ -674,8 +675,10 @@ static void tb_scan_port(struct tb_port *port) * tunnels and know which switches were authorized already by * the boot firmware. */ - if (!tcm->hotplug_active) + if (!tcm->hotplug_active) { dev_set_uevent_suppress(&sw->dev, true); + discovery = true; + } /* * At the moment Thunderbolt 2 and beyond (devices with LC) we @@ -705,10 +708,14 @@ static void tb_scan_port(struct tb_port *port) * CL0s and CL1 are enabled and supported together. * Silently ignore CLx enabling in case CLx is not supported. */ - ret = tb_switch_enable_clx(sw, TB_CL1); - if (ret && ret != -EOPNOTSUPP) - tb_sw_warn(sw, "failed to enable %s on upstream port\n", - tb_switch_clx_name(TB_CL1)); + if (discovery) { + tb_sw_dbg(sw, "discovery, not touching CL states\n"); + } else { + ret = tb_switch_enable_clx(sw, TB_CL1); + if (ret && ret != -EOPNOTSUPP) + tb_sw_warn(sw, "failed to enable %s on upstream port\n", + tb_switch_clx_name(TB_CL1)); + } if (tb_switch_is_clx_enabled(sw, TB_CL1)) /* -- cgit 1.4.1 From d799f73d5d44952fb570077832887046936d4859 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 29 Mar 2023 13:23:04 +0300 Subject: thunderbolt: dma_test: Use correct value for absent rings when creating paths commit 70c2e03e9aaf17496c63f6e42333c012f5ae5307 upstream. Both tb_xdomain_enable_paths() and tb_xdomain_disable_paths() expect -1, not 0, if the corresponding ring is not needed. For this reason change the driver to use correct value for the rings that are not needed. Fixes: 180b0689425c ("thunderbolt: Allow multiple DMA tunnels over a single XDomain connection") Cc: stable@vger.kernel.org Reported-by: Pengfei Xu Tested-by: Pengfei Xu Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/dma_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c index 3bedecb236e0..14bb6dec6c4b 100644 --- a/drivers/thunderbolt/dma_test.c +++ b/drivers/thunderbolt/dma_test.c @@ -192,9 +192,9 @@ static int dma_test_start_rings(struct dma_test *dt) } ret = tb_xdomain_enable_paths(dt->xd, dt->tx_hopid, - dt->tx_ring ? dt->tx_ring->hop : 0, + dt->tx_ring ? dt->tx_ring->hop : -1, dt->rx_hopid, - dt->rx_ring ? dt->rx_ring->hop : 0); + dt->rx_ring ? dt->rx_ring->hop : -1); if (ret) { dma_test_free_rings(dt); return ret; @@ -218,9 +218,9 @@ static void dma_test_stop_rings(struct dma_test *dt) tb_ring_stop(dt->tx_ring); ret = tb_xdomain_disable_paths(dt->xd, dt->tx_hopid, - dt->tx_ring ? dt->tx_ring->hop : 0, + dt->tx_ring ? dt->tx_ring->hop : -1, dt->rx_hopid, - dt->rx_ring ? dt->rx_ring->hop : 0); + dt->rx_ring ? dt->rx_ring->hop : -1); if (ret) dev_warn(&dt->svc->dev, "failed to disable DMA paths\n"); -- cgit 1.4.1 From 5532962c9ed259daf6824041aa923452cfca6bdd Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 30 May 2023 08:48:29 +0300 Subject: thunderbolt: Mask ring interrupt on Intel hardware as well commit 9f9666e65359d5047089aef97ac87c50f624ecb0 upstream. When resuming from system sleep states the driver issues following warning on Intel hardware: thunderbolt 0000:07:00.0: interrupt for TX ring 0 is already enabled The reason for this is that the commit in question did not mask the ring interrupt on Intel hardware leaving the interrupt active. Fix this by masking it also in Intel hardware. Reported-by: beld zhang Tested-by: beld zhang Closes: https://lore.kernel.org/linux-usb/ZHKW5NeabmfhgLbY@debian.me/ Fixes: c4af8e3fecd0 ("thunderbolt: Clear registers properly when auto clear isn't in use") Cc: stable@vger.kernel.org Reviewed-by: Mario Limonciello Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/nhi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 0a525f44ea31..4a6a3802d7e5 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -56,9 +56,14 @@ static int ring_interrupt_index(const struct tb_ring *ring) static void nhi_mask_interrupt(struct tb_nhi *nhi, int mask, int ring) { - if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) - return; - iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring); + if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) { + u32 val; + + val = ioread32(nhi->iobase + REG_RING_INTERRUPT_BASE + ring); + iowrite32(val & ~mask, nhi->iobase + REG_RING_INTERRUPT_BASE + ring); + } else { + iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring); + } } static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring) -- cgit 1.4.1 From 03b5964a2825f809e8b0ee50e92b6303a3f5df0e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 May 2023 12:58:33 +0200 Subject: clk: pxa: fix NULL pointer dereference in pxa3xx_clk_update_accr commit 23200a4c8ac284f8b4263d7cecaefecaa3ad6732 upstream. sparse points out an embarrasing bug in an older patch of mine, which uses the register offset instead of an __iomem pointer: drivers/clk/pxa/clk-pxa3xx.c:167:9: sparse: sparse: Using plain integer as NULL pointer Unlike sparse, gcc and clang ignore this bug and fail to warn because a literal '0' is considered a valid representation of a NULL pointer. Fixes: 3c816d950a49 ("ARM: pxa: move clk register definitions to driver") Cc: stable@vger.kernel.org Reported-by: kernel test robot Link: https://lore.kernel.org/oe-kbuild-all/202305111301.RAHohdob-lkp@intel.com/ Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230511105845.299859-1-arnd@kernel.org Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/clk/pxa/clk-pxa3xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/pxa/clk-pxa3xx.c b/drivers/clk/pxa/clk-pxa3xx.c index 42958a542662..621e298f101a 100644 --- a/drivers/clk/pxa/clk-pxa3xx.c +++ b/drivers/clk/pxa/clk-pxa3xx.c @@ -164,7 +164,7 @@ void pxa3xx_clk_update_accr(u32 disable, u32 enable, u32 xclkcfg, u32 mask) accr &= ~disable; accr |= enable; - writel(accr, ACCR); + writel(accr, clk_regs + ACCR); if (xclkcfg) __asm__("mcr p14, 0, %0, c6, c0, 0\n" : : "r"(xclkcfg)); -- cgit 1.4.1 From 04b3145db2255ac762651c655172dd6928624bd0 Mon Sep 17 00:00:00 2001 From: Jerry Meng Date: Wed, 31 May 2023 11:51:16 +0800 Subject: USB: serial: option: add Quectel EM061KGL series commit f1832e2b5e498e258b090af3b065b85cf8cc5161 upstream. Add support for Quectel EM061KGL series which are based on Qualcomm SDX12 chip: EM061KGL_LTA(0x2c7c / 0x0123): MBIM + GNSS + DIAG + NMEA + AT + QDSS + DPL EM061KGL_LMS(0x2c7c / 0x0124): MBIM + GNSS + DIAG + NMEA + AT + QDSS + DPL EM061KGL_LWW(0x2c7c / 0x6008): MBIM + GNSS + DIAG + NMEA + AT + QDSS + DPL EM061KGL_LCN(0x2c7c / 0x6009): MBIM + GNSS + DIAG + NMEA + AT + QDSS + DPL Above products use the exact same interface layout and option driver is for interfaces DIAG, NMEA and AT. T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 5 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2c7c ProdID=6008 Rev= 5.04 S: Manufacturer=Quectel S: Product=Quectel EM061K-GL S: SerialNumber=f6fa08b6 C:* #Ifs= 8 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=40 Driver=option E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8f(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms Signed-off-by: Jerry Meng Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index f05aea57e2d8..e12fec4c2e2f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -248,6 +248,8 @@ static void option_instat_callback(struct urb *urb); #define QUECTEL_VENDOR_ID 0x2c7c /* These Quectel products use Quectel's vendor ID */ #define QUECTEL_PRODUCT_EC21 0x0121 +#define QUECTEL_PRODUCT_EM061K_LTA 0x0123 +#define QUECTEL_PRODUCT_EM061K_LMS 0x0124 #define QUECTEL_PRODUCT_EC25 0x0125 #define QUECTEL_PRODUCT_EG91 0x0191 #define QUECTEL_PRODUCT_EG95 0x0195 @@ -266,6 +268,8 @@ static void option_instat_callback(struct urb *urb); #define QUECTEL_PRODUCT_RM520N 0x0801 #define QUECTEL_PRODUCT_EC200U 0x0901 #define QUECTEL_PRODUCT_EC200S_CN 0x6002 +#define QUECTEL_PRODUCT_EM061K_LWW 0x6008 +#define QUECTEL_PRODUCT_EM061K_LCN 0x6009 #define QUECTEL_PRODUCT_EC200T 0x6026 #define QUECTEL_PRODUCT_RM500K 0x7001 @@ -1189,6 +1193,18 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0xff, 0xff), .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0, 0) }, -- cgit 1.4.1 From b352f7b6a6469d1d2bb8eeb36ddad3c729ad45b5 Mon Sep 17 00:00:00 2001 From: Bernhard Seibold Date: Fri, 2 Jun 2023 15:30:29 +0200 Subject: serial: lantiq: add missing interrupt ack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 306320034e8fbe7ee1cc4f5269c55658b4612048 upstream. Currently, the error interrupt is never acknowledged, so once active it will stay active indefinitely, causing the handler to be called in an infinite loop. Fixes: 2f0fc4159a6a ("SERIAL: Lantiq: Add driver for MIPS Lantiq SOCs.") Cc: Signed-off-by: Bernhard Seibold Reviewed-by: Ilpo Järvinen Message-ID: <20230602133029.546-1-mail@bernhard-seibold.de> Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/lantiq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index c892f3c7d1ab..112a2f5f6ac3 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -278,6 +278,7 @@ lqasc_err_int(int irq, void *_port) struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); spin_lock_irqsave(<q_port->lock, flags); + __raw_writel(ASC_IRNCR_EIR, port->membase + LTQ_ASC_IRNCR); /* clear any pending interrupts */ asc_update_bits(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE | ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE); -- cgit 1.4.1 From 2bf8ea2e9e39a2a9bd74e98310ef1b96017d6757 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 6 Jun 2023 14:58:02 +0300 Subject: usb: typec: ucsi: Fix command cancellation commit c4a8bfabefed706bb9150867db528ceefd5cb5fe upstream. The Cancel command was passed to the write callback as the offset instead of as the actual command which caused NULL pointer dereference. Reported-by: Stephan Bolten Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217517 Fixes: 094902bc6a3c ("usb: typec: ucsi: Always cancel the command if PPM reports BUSY condition") Cc: stable@vger.kernel.org Signed-off-by: Heikki Krogerus Message-ID: <20230606115802.79339-1-heikki.krogerus@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 086b50968983..47a2c73df342 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -132,10 +132,8 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) if (ret) return ret; - if (cci & UCSI_CCI_BUSY) { - ucsi->ops->async_write(ucsi, UCSI_CANCEL, NULL, 0); - return -EBUSY; - } + if (cmd != UCSI_CANCEL && cci & UCSI_CCI_BUSY) + return ucsi_exec_command(ucsi, UCSI_CANCEL); if (!(cci & UCSI_CCI_COMMAND_COMPLETE)) return -EIO; @@ -149,6 +147,11 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) return ucsi_read_error(ucsi); } + if (cmd == UCSI_CANCEL && cci & UCSI_CCI_CANCEL_COMPLETE) { + ret = ucsi_acknowledge_command(ucsi); + return ret ? ret : -EBUSY; + } + return UCSI_CCI_LENGTH(cci); } -- cgit 1.4.1 From 3a1882841f646ebf01509958deb5e7778c16480b Mon Sep 17 00:00:00 2001 From: Pavan Holla Date: Wed, 7 Jun 2023 19:33:26 +0000 Subject: usb: typec: Fix fast_role_swap_current show function commit 92c9c3baad6b1fd584fbabeaa4756f9b77926cb5 upstream. The current implementation mistakenly performs a & operation on the output of sysfs_emit. This patch performs the & operation before calling sysfs_emit. Fixes: 662a60102c12 ("usb: typec: Separate USB Power Delivery from USB Type-C") Cc: stable Reported-by: Benson Leung Signed-off-by: Pavan Holla Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Message-ID: <20230607193328.3359487-1-pholla@chromium.org> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/pd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index b5ab26422c34..fd2477161e45 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -89,7 +89,7 @@ peak_current_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t fast_role_swap_current_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%u\n", to_pdo(dev)->pdo >> PDO_FIXED_FRS_CURR_SHIFT) & 3; + return sysfs_emit(buf, "%u\n", (to_pdo(dev)->pdo >> PDO_FIXED_FRS_CURR_SHIFT) & 3); } static DEVICE_ATTR_RO(fast_role_swap_current); -- cgit 1.4.1 From 3c048d42c305cba61c2040702778023a7b973b22 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 9 Jun 2023 01:02:26 +0000 Subject: usb: gadget: udc: core: Offload usb_udc_vbus_handler processing commit 50966da807c81c5eb3bdfd392990fe0bba94d1ee upstream. usb_udc_vbus_handler() can be invoked from interrupt context by irq handlers of the gadget drivers, however, usb_udc_connect_control() has to run in non-atomic context due to the following: a. Some of the gadget driver implementations expect the ->pullup callback to be invoked in non-atomic context. b. usb_gadget_disconnect() acquires udc_lock which is a mutex. Hence offload invocation of usb_udc_connect_control() to workqueue. UDC should not be pulled up unless gadget driver is bound. The new flag "allow_connect" is now set by gadget_bind_driver() and cleared by gadget_unbind_driver(). This prevents work item to pull up the gadget even if queued when the gadget driver is already unbound. Cc: stable@vger.kernel.org Fixes: 1016fc0c096c ("USB: gadget: Fix obscure lockdep violation for udc_mutex") Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Alan Stern Message-ID: <20230609010227.978661-1-badhri@google.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index bf9878e1a72a..3c94c1b3c373 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -37,6 +37,9 @@ static struct bus_type gadget_bus_type; * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true * @started: the UDC's started state. True if the UDC had started. + * @allow_connect: Indicates whether UDC is allowed to be pulled up. + * Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or + * unbound. * * This represents the internal data structure which is used by the UDC-class * to hold information about udc driver and gadget together. @@ -48,6 +51,8 @@ struct usb_udc { struct list_head list; bool vbus; bool started; + bool allow_connect; + struct work_struct vbus_work; }; static struct class *udc_class; @@ -679,7 +684,7 @@ int usb_gadget_connect(struct usb_gadget *gadget) goto out; } - if (gadget->deactivated) { + if (gadget->deactivated || !gadget->udc->allow_connect) { /* * If gadget is deactivated we only save new state. * Gadget will be connected automatically after activation. @@ -1059,6 +1064,13 @@ static void usb_udc_connect_control(struct usb_udc *udc) usb_gadget_disconnect(udc->gadget); } +static void vbus_event_work(struct work_struct *work) +{ + struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work); + + usb_udc_connect_control(udc); +} + /** * usb_udc_vbus_handler - updates the udc core vbus status, and try to * connect or disconnect gadget @@ -1067,6 +1079,14 @@ static void usb_udc_connect_control(struct usb_udc *udc) * * The udc driver calls it when it wants to connect or disconnect gadget * according to vbus status. + * + * This function can be invoked from interrupt context by irq handlers of + * the gadget drivers, however, usb_udc_connect_control() has to run in + * non-atomic context due to the following: + * a. Some of the gadget driver implementations expect the ->pullup + * callback to be invoked in non-atomic context. + * b. usb_gadget_disconnect() acquires udc_lock which is a mutex. + * Hence offload invocation of usb_udc_connect_control() to workqueue. */ void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) { @@ -1074,7 +1094,7 @@ void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) if (udc) { udc->vbus = status; - usb_udc_connect_control(udc); + schedule_work(&udc->vbus_work); } } EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); @@ -1301,6 +1321,7 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); mutex_unlock(&udc_lock); + INIT_WORK(&udc->vbus_work, vbus_event_work); ret = device_add(&udc->dev); if (ret) @@ -1432,6 +1453,7 @@ void usb_del_gadget(struct usb_gadget *gadget) flush_work(&gadget->work); device_del(&gadget->dev); ida_free(&gadget_id_numbers, gadget->id_number); + cancel_work_sync(&udc->vbus_work); device_unregister(&udc->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget); @@ -1500,6 +1522,7 @@ static int gadget_bind_driver(struct device *dev) if (ret) goto err_start; usb_gadget_enable_async_callbacks(udc); + udc->allow_connect = true; usb_udc_connect_control(udc); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); @@ -1531,6 +1554,8 @@ static void gadget_unbind_driver(struct device *dev) kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + udc->allow_connect = false; + cancel_work_sync(&udc->vbus_work); usb_gadget_disconnect(gadget); usb_gadget_disable_async_callbacks(udc); if (gadget->irq) -- cgit 1.4.1 From d8195536ce2624e2947d9f56b1a61e7a27874bd3 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 9 Jun 2023 01:02:27 +0000 Subject: usb: gadget: udc: core: Prevent soft_connect_store() race commit 286d9975a838d0a54da049765fa1d1fb96b89682 upstream. usb_udc_connect_control(), soft_connect_store() and usb_gadget_deactivate() can potentially race against each other to invoke usb_gadget_connect()/usb_gadget_disconnect(). To prevent this, guard udc->started, gadget->allow_connect, gadget->deactivate and gadget->connect with connect_lock so that ->pullup() is only invoked when the gadget is bound, started and not deactivated. The routines usb_gadget_connect_locked(), usb_gadget_disconnect_locked(), usb_udc_connect_control_locked(), usb_gadget_udc_start_locked(), usb_gadget_udc_stop_locked() are called with this lock held. An earlier version of this commit was reverted due to the crash reported in https://lore.kernel.org/all/ZF4BvgsOyoKxdPFF@francesco-nb.int.toradex.com/. commit 16737e78d190 ("usb: gadget: udc: core: Offload usb_udc_vbus_handler processing") addresses the crash reported. Cc: stable@vger.kernel.org Fixes: 628ef0d273a6 ("usb: udc: add usb_udc_vbus_handler") Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Alan Stern Message-ID: <20230609010227.978661-2-badhri@google.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 155 +++++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 49 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 3c94c1b3c373..99f40611f459 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -40,6 +40,11 @@ static struct bus_type gadget_bus_type; * @allow_connect: Indicates whether UDC is allowed to be pulled up. * Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or * unbound. + * @connect_lock: protects udc->started, gadget->connect, + * gadget->allow_connect and gadget->deactivate. The routines + * usb_gadget_connect_locked(), usb_gadget_disconnect_locked(), + * usb_udc_connect_control_locked(), usb_gadget_udc_start_locked() and + * usb_gadget_udc_stop_locked() are called with this lock held. * * This represents the internal data structure which is used by the UDC-class * to hold information about udc driver and gadget together. @@ -53,6 +58,7 @@ struct usb_udc { bool started; bool allow_connect; struct work_struct vbus_work; + struct mutex connect_lock; }; static struct class *udc_class; @@ -665,17 +671,8 @@ out: } EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); -/** - * usb_gadget_connect - software-controlled connect to USB host - * @gadget:the peripheral being connected - * - * Enables the D+ (or potentially D-) pullup. The host will start - * enumerating this gadget when the pullup is active and a VBUS session - * is active (the link is powered). - * - * Returns zero on success, else negative errno. - */ -int usb_gadget_connect(struct usb_gadget *gadget) +static int usb_gadget_connect_locked(struct usb_gadget *gadget) + __must_hold(&gadget->udc->connect_lock) { int ret = 0; @@ -684,10 +681,12 @@ int usb_gadget_connect(struct usb_gadget *gadget) goto out; } - if (gadget->deactivated || !gadget->udc->allow_connect) { + if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) { /* - * If gadget is deactivated we only save new state. - * Gadget will be connected automatically after activation. + * If the gadget isn't usable (because it is deactivated, + * unbound, or not yet started), we only save the new state. + * The gadget will be connected automatically when it is + * activated/bound/started. */ gadget->connected = true; goto out; @@ -702,22 +701,31 @@ out: return ret; } -EXPORT_SYMBOL_GPL(usb_gadget_connect); /** - * usb_gadget_disconnect - software-controlled disconnect from USB host - * @gadget:the peripheral being disconnected - * - * Disables the D+ (or potentially D-) pullup, which the host may see - * as a disconnect (when a VBUS session is active). Not all systems - * support software pullup controls. + * usb_gadget_connect - software-controlled connect to USB host + * @gadget:the peripheral being connected * - * Following a successful disconnect, invoke the ->disconnect() callback - * for the current gadget driver so that UDC drivers don't need to. + * Enables the D+ (or potentially D-) pullup. The host will start + * enumerating this gadget when the pullup is active and a VBUS session + * is active (the link is powered). * * Returns zero on success, else negative errno. */ -int usb_gadget_disconnect(struct usb_gadget *gadget) +int usb_gadget_connect(struct usb_gadget *gadget) +{ + int ret; + + mutex_lock(&gadget->udc->connect_lock); + ret = usb_gadget_connect_locked(gadget); + mutex_unlock(&gadget->udc->connect_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_connect); + +static int usb_gadget_disconnect_locked(struct usb_gadget *gadget) + __must_hold(&gadget->udc->connect_lock) { int ret = 0; @@ -729,7 +737,7 @@ int usb_gadget_disconnect(struct usb_gadget *gadget) if (!gadget->connected) goto out; - if (gadget->deactivated) { + if (gadget->deactivated || !gadget->udc->started) { /* * If gadget is deactivated we only save new state. * Gadget will stay disconnected after activation. @@ -752,6 +760,30 @@ out: return ret; } + +/** + * usb_gadget_disconnect - software-controlled disconnect from USB host + * @gadget:the peripheral being disconnected + * + * Disables the D+ (or potentially D-) pullup, which the host may see + * as a disconnect (when a VBUS session is active). Not all systems + * support software pullup controls. + * + * Following a successful disconnect, invoke the ->disconnect() callback + * for the current gadget driver so that UDC drivers don't need to. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_disconnect(struct usb_gadget *gadget) +{ + int ret; + + mutex_lock(&gadget->udc->connect_lock); + ret = usb_gadget_disconnect_locked(gadget); + mutex_unlock(&gadget->udc->connect_lock); + + return ret; +} EXPORT_SYMBOL_GPL(usb_gadget_disconnect); /** @@ -769,13 +801,14 @@ int usb_gadget_deactivate(struct usb_gadget *gadget) { int ret = 0; + mutex_lock(&gadget->udc->connect_lock); if (gadget->deactivated) - goto out; + goto unlock; if (gadget->connected) { - ret = usb_gadget_disconnect(gadget); + ret = usb_gadget_disconnect_locked(gadget); if (ret) - goto out; + goto unlock; /* * If gadget was being connected before deactivation, we want @@ -785,7 +818,8 @@ int usb_gadget_deactivate(struct usb_gadget *gadget) } gadget->deactivated = true; -out: +unlock: + mutex_unlock(&gadget->udc->connect_lock); trace_usb_gadget_deactivate(gadget, ret); return ret; @@ -805,8 +839,9 @@ int usb_gadget_activate(struct usb_gadget *gadget) { int ret = 0; + mutex_lock(&gadget->udc->connect_lock); if (!gadget->deactivated) - goto out; + goto unlock; gadget->deactivated = false; @@ -815,9 +850,11 @@ int usb_gadget_activate(struct usb_gadget *gadget) * while it was being deactivated, we call usb_gadget_connect(). */ if (gadget->connected) - ret = usb_gadget_connect(gadget); + ret = usb_gadget_connect_locked(gadget); + mutex_unlock(&gadget->udc->connect_lock); -out: +unlock: + mutex_unlock(&gadget->udc->connect_lock); trace_usb_gadget_activate(gadget, ret); return ret; @@ -1056,19 +1093,22 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); /* ------------------------------------------------------------------------- */ -static void usb_udc_connect_control(struct usb_udc *udc) +/* Acquire connect_lock before calling this function. */ +static void usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock) { if (udc->vbus) - usb_gadget_connect(udc->gadget); + usb_gadget_connect_locked(udc->gadget); else - usb_gadget_disconnect(udc->gadget); + usb_gadget_disconnect_locked(udc->gadget); } static void vbus_event_work(struct work_struct *work) { struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work); - usb_udc_connect_control(udc); + mutex_lock(&udc->connect_lock); + usb_udc_connect_control_locked(udc); + mutex_unlock(&udc->connect_lock); } /** @@ -1117,7 +1157,7 @@ void usb_gadget_udc_reset(struct usb_gadget *gadget, EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); /** - * usb_gadget_udc_start - tells usb device controller to start up + * usb_gadget_udc_start_locked - tells usb device controller to start up * @udc: The UDC to be started * * This call is issued by the UDC Class driver when it's about @@ -1128,8 +1168,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); * necessary to have it powered on. * * Returns zero on success, else negative errno. + * + * Caller should acquire connect_lock before invoking this function. */ -static inline int usb_gadget_udc_start(struct usb_udc *udc) +static inline int usb_gadget_udc_start_locked(struct usb_udc *udc) + __must_hold(&udc->connect_lock) { int ret; @@ -1146,7 +1189,7 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc) } /** - * usb_gadget_udc_stop - tells usb device controller we don't need it anymore + * usb_gadget_udc_stop_locked - tells usb device controller we don't need it anymore * @udc: The UDC to be stopped * * This call is issued by the UDC Class driver after calling @@ -1155,8 +1198,11 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc) * The details are implementation specific, but it can go as * far as powering off UDC completely and disable its data * line pullups. + * + * Caller should acquire connect lock before invoking this function. */ -static inline void usb_gadget_udc_stop(struct usb_udc *udc) +static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc) + __must_hold(&udc->connect_lock) { if (!udc->started) { dev_err(&udc->dev, "UDC had already stopped\n"); @@ -1315,6 +1361,7 @@ int usb_add_gadget(struct usb_gadget *gadget) udc->gadget = gadget; gadget->udc = udc; + mutex_init(&udc->connect_lock); udc->started = false; @@ -1518,12 +1565,16 @@ static int gadget_bind_driver(struct device *dev) if (ret) goto err_bind; - ret = usb_gadget_udc_start(udc); - if (ret) + mutex_lock(&udc->connect_lock); + ret = usb_gadget_udc_start_locked(udc); + if (ret) { + mutex_unlock(&udc->connect_lock); goto err_start; + } usb_gadget_enable_async_callbacks(udc); udc->allow_connect = true; - usb_udc_connect_control(udc); + usb_udc_connect_control_locked(udc); + mutex_unlock(&udc->connect_lock); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; @@ -1556,12 +1607,14 @@ static void gadget_unbind_driver(struct device *dev) udc->allow_connect = false; cancel_work_sync(&udc->vbus_work); - usb_gadget_disconnect(gadget); + mutex_lock(&udc->connect_lock); + usb_gadget_disconnect_locked(gadget); usb_gadget_disable_async_callbacks(udc); if (gadget->irq) synchronize_irq(gadget->irq); udc->driver->unbind(gadget); - usb_gadget_udc_stop(udc); + usb_gadget_udc_stop_locked(udc); + mutex_unlock(&udc->connect_lock); mutex_lock(&udc_lock); driver->is_bound = false; @@ -1647,11 +1700,15 @@ static ssize_t soft_connect_store(struct device *dev, } if (sysfs_streq(buf, "connect")) { - usb_gadget_udc_start(udc); - usb_gadget_connect(udc->gadget); + mutex_lock(&udc->connect_lock); + usb_gadget_udc_start_locked(udc); + usb_gadget_connect_locked(udc->gadget); + mutex_unlock(&udc->connect_lock); } else if (sysfs_streq(buf, "disconnect")) { - usb_gadget_disconnect(udc->gadget); - usb_gadget_udc_stop(udc); + mutex_lock(&udc->connect_lock); + usb_gadget_disconnect_locked(udc->gadget); + usb_gadget_udc_stop_locked(udc); + mutex_unlock(&udc->connect_lock); } else { dev_err(dev, "unsupported command '%s'\n", buf); ret = -EINVAL; -- cgit 1.4.1 From 307fe59490bde1f027a8f6d0bb9570d2a5faad61 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 7 Jun 2023 12:05:39 +0200 Subject: USB: dwc3: qcom: fix NULL-deref on suspend commit d2d69354226de0b333d4405981f3d9c41ba8430a upstream. The Qualcomm dwc3 glue driver is currently accessing the driver data of the child core device during suspend and on wakeup interrupts. This is clearly a bad idea as the child may not have probed yet or could have been unbound from its driver. The first such layering violation was part of the initial version of the driver, but this was later made worse when the hack that accesses the driver data of the grand child xhci device to configure the wakeup interrupts was added. Fixing this properly is not that easily done, so add a sanity check to make sure that the child driver data is non-NULL before dereferencing it for now. Note that this relies on subtleties like the fact that driver core is making sure that the parent is not suspended while the child is probing. Reported-by: Manivannan Sadhasivam Link: https://lore.kernel.org/all/20230325165217.31069-4-manivannan.sadhasivam@linaro.org/ Fixes: d9152161b4bf ("usb: dwc3: Add Qualcomm DWC3 glue layer driver") Fixes: 6895ea55c385 ("usb: dwc3: qcom: Configure wakeup interrupts during suspend") Cc: stable@vger.kernel.org # 3.18: a872ab303d5d: "usb: dwc3: qcom: fix use-after-free on runtime-PM wakeup" Cc: Sandeep Maheswaram Cc: Krishna Kurapati Signed-off-by: Johan Hovold Acked-by: Thinh Nguyen Reviewed-by: Manivannan Sadhasivam Message-ID: <20230607100540.31045-2-johan+linaro@kernel.org> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 959fc925ca7c..79b22abf9727 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -308,7 +308,16 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom) /* Only usable in contexts where the role can not change. */ static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom) { - struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3); + struct dwc3 *dwc; + + /* + * FIXME: Fix this layering violation. + */ + dwc = platform_get_drvdata(qcom->dwc3); + + /* Core driver may not have probed yet. */ + if (!dwc) + return false; return dwc->xhci; } -- cgit 1.4.1 From 7bee7f13c039ccda1b29141271f415a4441720f3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 7 Jun 2023 12:05:40 +0200 Subject: USB: dwc3: fix use-after-free on core driver unbind commit e3dbb657571509044be15184a13134fa7c1fdca1 upstream. Some dwc3 glue drivers are currently accessing the driver data of the child core device directly, which is clearly a bad idea as the child may not have probed yet or may have been unbound from its driver. As a workaround until the glue drivers have been fixed, clear the driver data pointer before allowing the glue parent device to runtime suspend to prevent its driver from accessing data that has been freed during unbind. Fixes: 6dd2565989b4 ("usb: dwc3: add imx8mp dwc3 glue layer driver") Fixes: 6895ea55c385 ("usb: dwc3: qcom: Configure wakeup interrupts during suspend") Cc: stable@vger.kernel.org # 5.12 Cc: Li Jun Cc: Sandeep Maheswaram Cc: Krishna Kurapati Signed-off-by: Johan Hovold Acked-by: Thinh Nguyen Reviewed-by: Manivannan Sadhasivam Message-ID: <20230607100540.31045-3-johan+linaro@kernel.org> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9f8c988c25cb..e999e6079ae0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1982,6 +1982,11 @@ static int dwc3_remove(struct platform_device *pdev) pm_runtime_allow(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); + /* + * HACK: Clear the driver data, which is currently accessed by parent + * glue drivers, before allowing the parent to suspend. + */ + platform_set_drvdata(pdev, NULL); pm_runtime_set_suspended(&pdev->dev); dwc3_free_event_buffers(dwc); -- cgit 1.4.1 From 03285557deb642e2ce41087aa1caa6a94c414954 Mon Sep 17 00:00:00 2001 From: Elson Roy Serrao Date: Thu, 1 Jun 2023 14:27:30 -0700 Subject: usb: dwc3: gadget: Reset num TRBs before giving back the request commit 00f8205ffcf112dcef14f8151d78075d38d22c08 upstream. Consider a scenario where cable disconnect happens when there is an active usb reqest queued to the UDC. As part of the disconnect we would issue an end transfer with no interrupt-on-completion before giving back this request. Since we are giving back the request without skipping TRBs the num_trbs field of dwc3_request still holds the stale value previously used. Function drivers re-use same request for a given bind-unbind session and hence their dwc3_request context gets preserved across cable disconnect/connect. When such a request gets re-queued after cable connect, we would increase the num_trbs field on top of the previous stale value thus incorrectly representing the number of TRBs used. Fix this by resetting num_trbs field before giving back the request. Fixes: 09fe1f8d7e2f ("usb: dwc3: gadget: track number of TRBs per request") Cc: stable Signed-off-by: Elson Roy Serrao Acked-by: Thinh Nguyen Message-ID: <1685654850-8468-1-git-send-email-quic_eserrao@quicinc.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8cd0d919ef63..7e94d31687e9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -180,6 +180,7 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, list_del(&req->list); req->remaining = 0; req->needs_extra_trb = false; + req->num_trbs = 0; if (req->request.status == -EINPROGRESS) req->request.status = status; -- cgit 1.4.1 From 77226c9785f84c20a58f47852851e210c434eab8 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 25 Apr 2023 01:02:42 +0000 Subject: RDMA/rtrs: Fix the last iu->buf leak in err path [ Upstream commit 3bf3a7c6985c625f64e73baefdaa36f1c2045a29 ] The last iu->buf will leak if ib_dma_mapping_error() fails. Fixes: c0894b3ea69d ("RDMA/rtrs: core: lib functions shared between client and server modules") Link: https://lore.kernel.org/r/1682384563-2-3-git-send-email-lizhijian@fujitsu.com Signed-off-by: Li Zhijian Acked-by: Guoqing Jiang Acked-by: Jack Wang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/rtrs/rtrs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/rtrs/rtrs.c b/drivers/infiniband/ulp/rtrs/rtrs.c index ed324b47d93a..716ec7baddef 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs.c +++ b/drivers/infiniband/ulp/rtrs/rtrs.c @@ -37,8 +37,10 @@ struct rtrs_iu *rtrs_iu_alloc(u32 iu_num, size_t size, gfp_t gfp_mask, goto err; iu->dma_addr = ib_dma_map_single(dma_dev, iu->buf, size, dir); - if (ib_dma_mapping_error(dma_dev, iu->dma_addr)) + if (ib_dma_mapping_error(dma_dev, iu->dma_addr)) { + kfree(iu->buf); goto err; + } iu->cqe.done = done; iu->size = size; -- cgit 1.4.1 From 00b276bc7b621bae995a20a9844ce3b27d71d177 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 25 Apr 2023 01:02:43 +0000 Subject: RDMA/rtrs: Fix rxe_dealloc_pd warning [ Upstream commit 9c29c8c7df0688f358d2df5ddd16c97c2f7292b4 ] In current design: 1. PD and clt_path->s.dev are shared among connections. 2. every con[n]'s cleanup phase will call destroy_con_cq_qp() 3. clt_path->s.dev will be always decreased in destroy_con_cq_qp(), and when clt_path->s.dev become zero, it will destroy PD. 4. when con[1] failed to create, con[1] will not take clt_path->s.dev, but it try to decreased clt_path->s.dev So, in case create_cm(con[0]) succeeds but create_cm(con[1]) fails, destroy_con_cq_qp(con[1]) will be called first which will destroy the PD while this PD is still taken by con[0]. Here, we refactor the error path of create_cm() and init_conns(), so that we do the cleanup in the order they are created. The warning occurs when destroying RXE PD whose reference count is not zero. rnbd_client L597: Mapping device /dev/nvme0n1 on session client, (access_mode: rw, nr_poll_queues: 0) ------------[ cut here ]------------ WARNING: CPU: 0 PID: 26407 at drivers/infiniband/sw/rxe/rxe_pool.c:256 __rxe_cleanup+0x13a/0x170 [rdma_rxe] Modules linked in: rpcrdma rdma_ucm ib_iser rnbd_client libiscsi rtrs_client scsi_transport_iscsi rtrs_core rdma_cm iw_cm ib_cm crc32_generic rdma_rxe udp_tunnel ib_uverbs ib_core kmem device_dax nd_pmem dax_pmem nd_vme crc32c_intel fuse nvme_core nfit libnvdimm dm_multipath scsi_dh_rdac scsi_dh_emc scsi_dh_alua dm_mirror dm_region_hash dm_log dm_mod CPU: 0 PID: 26407 Comm: rnbd-client.sh Kdump: loaded Not tainted 6.2.0-rc6-roce-flush+ #53 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 RIP: 0010:__rxe_cleanup+0x13a/0x170 [rdma_rxe] Code: 45 84 e4 0f 84 5a ff ff ff 48 89 ef e8 5f 18 71 f9 84 c0 75 90 be c8 00 00 00 48 89 ef e8 be 89 1f fa 85 c0 0f 85 7b ff ff ff <0f> 0b 41 bc ea ff ff ff e9 71 ff ff ff e8 84 7f 1f fa e9 d0 fe ff RSP: 0018:ffffb09880b6f5f0 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff99401f15d6a8 RCX: 0000000000000000 RDX: 0000000000000001 RSI: ffffffffbac8234b RDI: 00000000ffffffff RBP: ffff99401f15d6d0 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000002d82 R11: 0000000000000000 R12: 0000000000000001 R13: ffff994101eff208 R14: ffffb09880b6f6a0 R15: 00000000fffffe00 FS: 00007fe113904740(0000) GS:ffff99413bc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff6cde656c8 CR3: 000000001f108004 CR4: 00000000001706f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: rxe_dealloc_pd+0x16/0x20 [rdma_rxe] ib_dealloc_pd_user+0x4b/0x80 [ib_core] rtrs_ib_dev_put+0x79/0xd0 [rtrs_core] destroy_con_cq_qp+0x8a/0xa0 [rtrs_client] init_path+0x1e7/0x9a0 [rtrs_client] ? __pfx_autoremove_wake_function+0x10/0x10 ? lock_is_held_type+0xd7/0x130 ? rcu_read_lock_sched_held+0x43/0x80 ? pcpu_alloc+0x3dd/0x7d0 ? rtrs_clt_init_stats+0x18/0x40 [rtrs_client] rtrs_clt_open+0x24f/0x5a0 [rtrs_client] ? __pfx_rnbd_clt_link_ev+0x10/0x10 [rnbd_client] rnbd_clt_map_device+0x6a5/0xe10 [rnbd_client] Fixes: 6a98d71daea1 ("RDMA/rtrs: client: main functionality") Link: https://lore.kernel.org/r/1682384563-2-4-git-send-email-lizhijian@fujitsu.com Signed-off-by: Li Zhijian Acked-by: Jack Wang Tested-by: Jack Wang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/rtrs/rtrs-clt.c | 55 ++++++++++++++-------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index 730f2f1e09bb..a67f58359de9 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -2042,6 +2042,7 @@ static int rtrs_clt_rdma_cm_handler(struct rdma_cm_id *cm_id, return 0; } +/* The caller should do the cleanup in case of error */ static int create_cm(struct rtrs_clt_con *con) { struct rtrs_path *s = con->c.path; @@ -2064,14 +2065,14 @@ static int create_cm(struct rtrs_clt_con *con) err = rdma_set_reuseaddr(cm_id, 1); if (err != 0) { rtrs_err(s, "Set address reuse failed, err: %d\n", err); - goto destroy_cm; + return err; } err = rdma_resolve_addr(cm_id, (struct sockaddr *)&clt_path->s.src_addr, (struct sockaddr *)&clt_path->s.dst_addr, RTRS_CONNECT_TIMEOUT_MS); if (err) { rtrs_err(s, "Failed to resolve address, err: %d\n", err); - goto destroy_cm; + return err; } /* * Combine connection status and session events. This is needed @@ -2086,29 +2087,15 @@ static int create_cm(struct rtrs_clt_con *con) if (err == 0) err = -ETIMEDOUT; /* Timedout or interrupted */ - goto errr; - } - if (con->cm_err < 0) { - err = con->cm_err; - goto errr; + return err; } - if (READ_ONCE(clt_path->state) != RTRS_CLT_CONNECTING) { + if (con->cm_err < 0) + return con->cm_err; + if (READ_ONCE(clt_path->state) != RTRS_CLT_CONNECTING) /* Device removal */ - err = -ECONNABORTED; - goto errr; - } + return -ECONNABORTED; return 0; - -errr: - stop_cm(con); - mutex_lock(&con->con_mutex); - destroy_con_cq_qp(con); - mutex_unlock(&con->con_mutex); -destroy_cm: - destroy_cm(con); - - return err; } static void rtrs_clt_path_up(struct rtrs_clt_path *clt_path) @@ -2336,7 +2323,7 @@ static void rtrs_clt_close_work(struct work_struct *work) static int init_conns(struct rtrs_clt_path *clt_path) { unsigned int cid; - int err; + int err, i; /* * On every new session connections increase reconnect counter @@ -2352,10 +2339,8 @@ static int init_conns(struct rtrs_clt_path *clt_path) goto destroy; err = create_cm(to_clt_con(clt_path->s.con[cid])); - if (err) { - destroy_con(to_clt_con(clt_path->s.con[cid])); + if (err) goto destroy; - } } err = alloc_path_reqs(clt_path); if (err) @@ -2366,15 +2351,21 @@ static int init_conns(struct rtrs_clt_path *clt_path) return 0; destroy: - while (cid--) { - struct rtrs_clt_con *con = to_clt_con(clt_path->s.con[cid]); + /* Make sure we do the cleanup in the order they are created */ + for (i = 0; i <= cid; i++) { + struct rtrs_clt_con *con; - stop_cm(con); + if (!clt_path->s.con[i]) + break; - mutex_lock(&con->con_mutex); - destroy_con_cq_qp(con); - mutex_unlock(&con->con_mutex); - destroy_cm(con); + con = to_clt_con(clt_path->s.con[i]); + if (con->c.cm_id) { + stop_cm(con); + mutex_lock(&con->con_mutex); + destroy_con_cq_qp(con); + mutex_unlock(&con->con_mutex); + destroy_cm(con); + } destroy_con(con); } /* -- cgit 1.4.1 From 7617a59f00568b931f7fa50756219300cf77e93e Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Wed, 17 May 2023 12:22:42 -0500 Subject: RDMA/rxe: Fix packet length checks [ Upstream commit 9a3763e87379c97a78b7c6c6f40720b1e877174f ] In rxe_net.c a received packet, from udp or loopback, is passed to rxe_rcv() in rxe_recv.c as a udp packet. I.e. skb->data is pointing at the udp header. But rxe_rcv() makes length checks to verify the packet is long enough to hold the roce headers as if it were a roce packet. I.e. skb->data pointing at the bth header. A runt packet would appear to have 8 more bytes than it actually does which may lead to incorrect behavior. This patch calls skb_pull() to adjust the skb to point at the bth header before calling rxe_rcv() which fixes this error. Fixes: 8700e3e7c485 ("Soft RoCE driver") Link: https://lore.kernel.org/r/20230517172242.1806340-1-rpearsonhpe@gmail.com Signed-off-by: Bob Pearson Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_net.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 35f327b9d4b8..65d16024b3bf 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -156,6 +156,9 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb) pkt->mask = RXE_GRH_MASK; pkt->paylen = be16_to_cpu(udph->len) - sizeof(*udph); + /* remove udp header */ + skb_pull(skb, sizeof(struct udphdr)); + rxe_rcv(skb); return 0; @@ -397,6 +400,9 @@ static int rxe_loopback(struct sk_buff *skb, struct rxe_pkt_info *pkt) return -EIO; } + /* remove udp header */ + skb_pull(skb, sizeof(struct udphdr)); + rxe_rcv(skb); return 0; -- cgit 1.4.1 From 2906e0d75b50c987379cd10e0092f167b8145f2e Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Wed, 17 May 2023 16:15:10 -0500 Subject: RDMA/rxe: Fix ref count error in check_rkey() [ Upstream commit b00683422fd79dd07c9b75efdce1660e5e19150e ] There is a reference count error in error path code and a potential race in check_rkey() in rxe_resp.c. When looking up the rkey for a memory window the reference to the mw from rxe_lookup_mw() is dropped before a reference is taken on the mr referenced by the mw. If the mr is destroyed immediately after the call to rxe_put(mw) the mr pointer is unprotected and may end up pointing at freed memory. The rxe_get(mr) call should take place before the rxe_put(mw) call. All errors in check_rkey() call rxe_put(mw) if mw is not NULL but it was already called after the above. The mw pointer should be set to NULL after the rxe_put(mw) call to prevent this from happening. Fixes: cdd0b85675ae ("RDMA/rxe: Implement memory access through MWs") Link: https://lore.kernel.org/r/20230517211509.1819998-1-rpearsonhpe@gmail.com Signed-off-by: Bob Pearson Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_resp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 693081e813ec..9f65c346d843 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -466,8 +466,9 @@ static enum resp_states check_rkey(struct rxe_qp *qp, if (mw->access & IB_ZERO_BASED) qp->resp.offset = mw->addr; - rxe_put(mw); rxe_get(mr); + rxe_put(mw); + mw = NULL; } else { mr = lookup_mr(qp->pd, access, rkey, RXE_LOOKUP_REMOTE); if (!mr) { -- cgit 1.4.1 From 4857924806609cabe283d4d3f5c4c3b2ca7db24a Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 6 Jun 2023 17:38:59 +0800 Subject: spi: cadence-quadspi: Add missing check for dma_set_mask [ Upstream commit 947c70a213769f60e9d5aca2bc88b50a1cfaf5a6 ] Add check for dma_set_mask() and return the error if it fails. Fixes: 1a6f854f7daa ("spi: cadence-quadspi: Add Xilinx Versal external DMA support") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20230606093859.27818-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-cadence-quadspi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 30fd4bc90580..b371e4eb41ec 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1697,8 +1697,11 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->slow_sram = true; if (of_device_is_compatible(pdev->dev.of_node, - "xlnx,versal-ospi-1.0")) - dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + "xlnx,versal-ospi-1.0")) { + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) + goto probe_reset_failed; + } } ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0, -- cgit 1.4.1 From 839d38d3b0aa2ece4ff0758ca4fff98090efeda0 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 30 May 2023 01:34:02 +0300 Subject: spi: fsl-dspi: avoid SCK glitches with continuous transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c5c31fb71f16ba75bad4ade208abbae225305b65 ] The DSPI controller has configurable timing for (a) tCSC: the interval between the assertion of the chip select and the first clock edge (b) tASC: the interval between the last clock edge and the deassertion of the chip select What is a bit surprising, but is documented in the figure "Example of continuous transfer (CPHA=1, CONT=1)" in the datasheet, is that when the chip select stays asserted between multiple TX FIFO writes, the tCSC and tASC times still apply. With CONT=1, chip select remains asserted, but SCK takes a break and goes to the idle state for tASC + tCSC ns. In other words, the default values (of 0 and 0 ns) result in SCK glitches where the SCK transition to the idle state, as well as the SCK transition from the idle state, will have no delay in between, and it may appear that a SCK cycle has simply gone missing. The resulting timing violation might cause data corruption in many peripherals, as their chip select is asserted. The driver has device tree bindings for tCSC ("fsl,spi-cs-sck-delay") and tASC ("fsl,spi-sck-cs-delay"), but these are only specified to apply when the chip select toggles in the first place, and this timing characteristic depends on each peripheral. Many peripherals do not have explicit timing requirements, so many device trees do not have these properties present at all. Nonetheless, the lack of SCK glitches is a common sense requirement, and since the SCK stays in the idle state during transfers for tCSC+tASC ns, and that in itself should look like half a cycle, then let's ensure that tCSC and tASC are at least a quarter of a SCK period, such that their sum is at least half of one. Fixes: 95bf15f38641 ("spi: fsl-dspi: Add ~50ns delay between cs and sck") Reported-by: Lisa Chen (陈敏捷) Debugged-by: Lisa Chen (陈敏捷) Tested-by: Lisa Chen (陈敏捷) Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20230529223402.1199503-1-vladimir.oltean@nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-fsl-dspi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index a33e547b7d39..01930b52c4fb 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -975,7 +975,9 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr, static int dspi_setup(struct spi_device *spi) { struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller); + u32 period_ns = DIV_ROUND_UP(NSEC_PER_SEC, spi->max_speed_hz); unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0; + u32 quarter_period_ns = DIV_ROUND_UP(period_ns, 4); u32 cs_sck_delay = 0, sck_cs_delay = 0; struct fsl_dspi_platform_data *pdata; unsigned char pasc = 0, asc = 0; @@ -1003,6 +1005,19 @@ static int dspi_setup(struct spi_device *spi) sck_cs_delay = pdata->sck_cs_delay; } + /* Since tCSC and tASC apply to continuous transfers too, avoid SCK + * glitches of half a cycle by never allowing tCSC + tASC to go below + * half a SCK period. + */ + if (cs_sck_delay < quarter_period_ns) + cs_sck_delay = quarter_period_ns; + if (sck_cs_delay < quarter_period_ns) + sck_cs_delay = quarter_period_ns; + + dev_dbg(&spi->dev, + "DSPI controller timing params: CS-to-SCK delay %u ns, SCK-to-CS delay %u ns\n", + cs_sck_delay, sck_cs_delay); + clkrate = clk_get_rate(dspi->clk); hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate); -- cgit 1.4.1 From 55b7a00f3422a0291c837ce856068b1997af2d10 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 7 Jun 2023 11:55:42 +0200 Subject: netfilter: nf_tables: integrate pipapo into commit protocol [ Upstream commit 212ed75dc5fb9d1423b3942c8f872a868cda3466 ] The pipapo set backend follows copy-on-update approach, maintaining one clone of the existing datastructure that is being updated. The clone and current datastructures are swapped via rcu from the commit step. The existing integration with the commit protocol is flawed because there is no operation to clean up the clone if the transaction is aborted. Moreover, the datastructure swap happens on set element activation. This patch adds two new operations for sets: commit and abort, these new operations are invoked from the commit and abort steps, after the transactions have been digested, and it updates the pipapo set backend to use it. This patch adds a new ->pending_update field to sets to maintain a list of sets that require this new commit and abort operations. Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_tables.h | 4 ++- net/netfilter/nf_tables_api.c | 56 +++++++++++++++++++++++++++++++++++++++ net/netfilter/nft_set_pipapo.c | 55 +++++++++++++++++++++++++++----------- 3 files changed, 99 insertions(+), 16 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index a1ccf1276f3e..22e96b7e1b44 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -457,7 +457,8 @@ struct nft_set_ops { const struct nft_set *set, const struct nft_set_elem *elem, unsigned int flags); - + void (*commit)(const struct nft_set *set); + void (*abort)(const struct nft_set *set); u64 (*privsize)(const struct nlattr * const nla[], const struct nft_set_desc *desc); bool (*estimate)(const struct nft_set_desc *desc, @@ -552,6 +553,7 @@ struct nft_set { u16 policy; u16 udlen; unsigned char *udata; + struct list_head pending_update; /* runtime data below here */ const struct nft_set_ops *ops ____cacheline_aligned; u16 flags:14, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 437891cb8c41..db84d607a413 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4730,6 +4730,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, set->num_exprs = num_exprs; set->handle = nf_tables_alloc_handle(table); + INIT_LIST_HEAD(&set->pending_update); err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set); if (err < 0) @@ -8992,10 +8993,25 @@ static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation) } } +static void nft_set_commit_update(struct list_head *set_update_list) +{ + struct nft_set *set, *next; + + list_for_each_entry_safe(set, next, set_update_list, pending_update) { + list_del_init(&set->pending_update); + + if (!set->ops->commit) + continue; + + set->ops->commit(set); + } +} + static int nf_tables_commit(struct net *net, struct sk_buff *skb) { struct nftables_pernet *nft_net = nft_pernet(net); struct nft_trans *trans, *next; + LIST_HEAD(set_update_list); struct nft_trans_elem *te; struct nft_chain *chain; struct nft_table *table; @@ -9154,6 +9170,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) nf_tables_setelem_notify(&trans->ctx, te->set, &te->elem, NFT_MSG_NEWSETELEM); + if (te->set->ops->commit && + list_empty(&te->set->pending_update)) { + list_add_tail(&te->set->pending_update, + &set_update_list); + } nft_trans_destroy(trans); break; case NFT_MSG_DELSETELEM: @@ -9167,6 +9188,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) atomic_dec(&te->set->nelems); te->set->ndeact--; } + if (te->set->ops->commit && + list_empty(&te->set->pending_update)) { + list_add_tail(&te->set->pending_update, + &set_update_list); + } break; case NFT_MSG_NEWOBJ: if (nft_trans_obj_update(trans)) { @@ -9227,6 +9253,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) } } + nft_set_commit_update(&set_update_list); + nft_commit_notify(net, NETLINK_CB(skb).portid); nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); nf_tables_commit_audit_log(&adl, nft_net->base_seq); @@ -9283,10 +9311,25 @@ static void nf_tables_abort_release(struct nft_trans *trans) kfree(trans); } +static void nft_set_abort_update(struct list_head *set_update_list) +{ + struct nft_set *set, *next; + + list_for_each_entry_safe(set, next, set_update_list, pending_update) { + list_del_init(&set->pending_update); + + if (!set->ops->abort) + continue; + + set->ops->abort(set); + } +} + static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) { struct nftables_pernet *nft_net = nft_pernet(net); struct nft_trans *trans, *next; + LIST_HEAD(set_update_list); struct nft_trans_elem *te; if (action == NFNL_ABORT_VALIDATE && @@ -9384,6 +9427,12 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) nft_setelem_remove(net, te->set, &te->elem); if (!nft_setelem_is_catchall(te->set, &te->elem)) atomic_dec(&te->set->nelems); + + if (te->set->ops->abort && + list_empty(&te->set->pending_update)) { + list_add_tail(&te->set->pending_update, + &set_update_list); + } break; case NFT_MSG_DELSETELEM: te = (struct nft_trans_elem *)trans->data; @@ -9393,6 +9442,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) if (!nft_setelem_is_catchall(te->set, &te->elem)) te->set->ndeact--; + if (te->set->ops->abort && + list_empty(&te->set->pending_update)) { + list_add_tail(&te->set->pending_update, + &set_update_list); + } nft_trans_destroy(trans); break; case NFT_MSG_NEWOBJ: @@ -9433,6 +9487,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) } } + nft_set_abort_update(&set_update_list); + synchronize_rcu(); list_for_each_entry_safe_reverse(trans, next, diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 06d46d182634..15e451dc3fc4 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -1600,17 +1600,10 @@ static void pipapo_free_fields(struct nft_pipapo_match *m) } } -/** - * pipapo_reclaim_match - RCU callback to free fields from old matching data - * @rcu: RCU head - */ -static void pipapo_reclaim_match(struct rcu_head *rcu) +static void pipapo_free_match(struct nft_pipapo_match *m) { - struct nft_pipapo_match *m; int i; - m = container_of(rcu, struct nft_pipapo_match, rcu); - for_each_possible_cpu(i) kfree(*per_cpu_ptr(m->scratch, i)); @@ -1625,7 +1618,19 @@ static void pipapo_reclaim_match(struct rcu_head *rcu) } /** - * pipapo_commit() - Replace lookup data with current working copy + * pipapo_reclaim_match - RCU callback to free fields from old matching data + * @rcu: RCU head + */ +static void pipapo_reclaim_match(struct rcu_head *rcu) +{ + struct nft_pipapo_match *m; + + m = container_of(rcu, struct nft_pipapo_match, rcu); + pipapo_free_match(m); +} + +/** + * nft_pipapo_commit() - Replace lookup data with current working copy * @set: nftables API set representation * * While at it, check if we should perform garbage collection on the working @@ -1635,7 +1640,7 @@ static void pipapo_reclaim_match(struct rcu_head *rcu) * We also need to create a new working copy for subsequent insertions and * deletions. */ -static void pipapo_commit(const struct nft_set *set) +static void nft_pipapo_commit(const struct nft_set *set) { struct nft_pipapo *priv = nft_set_priv(set); struct nft_pipapo_match *new_clone, *old; @@ -1660,6 +1665,26 @@ static void pipapo_commit(const struct nft_set *set) priv->clone = new_clone; } +static void nft_pipapo_abort(const struct nft_set *set) +{ + struct nft_pipapo *priv = nft_set_priv(set); + struct nft_pipapo_match *new_clone, *m; + + if (!priv->dirty) + return; + + m = rcu_dereference(priv->match); + + new_clone = pipapo_clone(m); + if (IS_ERR(new_clone)) + return; + + priv->dirty = false; + + pipapo_free_match(priv->clone); + priv->clone = new_clone; +} + /** * nft_pipapo_activate() - Mark element reference as active given key, commit * @net: Network namespace @@ -1667,8 +1692,7 @@ static void pipapo_commit(const struct nft_set *set) * @elem: nftables API element representation containing key data * * On insertion, elements are added to a copy of the matching data currently - * in use for lookups, and not directly inserted into current lookup data, so - * we'll take care of that by calling pipapo_commit() here. Both + * in use for lookups, and not directly inserted into current lookup data. Both * nft_pipapo_insert() and nft_pipapo_activate() are called once for each * element, hence we can't purpose either one as a real commit operation. */ @@ -1684,8 +1708,6 @@ static void nft_pipapo_activate(const struct net *net, nft_set_elem_change_active(net, set, &e->ext); nft_set_elem_clear_busy(&e->ext); - - pipapo_commit(set); } /** @@ -1931,7 +1953,6 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set, if (i == m->field_count) { priv->dirty = true; pipapo_drop(m, rulemap); - pipapo_commit(set); return; } @@ -2230,6 +2251,8 @@ const struct nft_set_type nft_set_pipapo_type = { .init = nft_pipapo_init, .destroy = nft_pipapo_destroy, .gc_init = nft_pipapo_gc_init, + .commit = nft_pipapo_commit, + .abort = nft_pipapo_abort, .elemsize = offsetof(struct nft_pipapo_elem, ext), }, }; @@ -2252,6 +2275,8 @@ const struct nft_set_type nft_set_pipapo_avx2_type = { .init = nft_pipapo_init, .destroy = nft_pipapo_destroy, .gc_init = nft_pipapo_gc_init, + .commit = nft_pipapo_commit, + .abort = nft_pipapo_abort, .elemsize = offsetof(struct nft_pipapo_elem, ext), }, }; -- cgit 1.4.1 From 8fddf3f05156538d072c8a3fc7c09fd54f32df87 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 8 Jun 2023 00:19:12 +0200 Subject: netfilter: nfnetlink: skip error delivery on batch in case of ENOMEM [ Upstream commit a1a64a151dae8ac3581c1cbde44b672045cb658b ] If caller reports ENOMEM, then stop iterating over the batch and send a single netlink message to userspace to report OOM. Fixes: cbb8125eb40b ("netfilter: nfnetlink: deliver netlink errors on batch completion") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nfnetlink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index ae7146475d17..c9fbe0f707b5 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -533,7 +533,8 @@ ack: * processed, this avoids that the same error is * reported several times when replaying the batch. */ - if (nfnl_err_add(&err_list, nlh, err, &extack) < 0) { + if (err == -ENOMEM || + nfnl_err_add(&err_list, nlh, err, &extack) < 0) { /* We failed to enqueue an error, reset the * list of errors and send OOM to userspace * pointing to the batch header. -- cgit 1.4.1 From bec51844f9bafa7ddb943aaafd258cdace55f2ec Mon Sep 17 00:00:00 2001 From: Kamil Maziarz Date: Tue, 6 Jun 2023 12:33:58 +0200 Subject: ice: Fix XDP memory leak when NIC is brought up and down [ Upstream commit 78c50d6961fc05491ebbc71c35d87324b1a4f49a ] Fix the buffer leak that occurs while switching the port up and down with traffic and XDP by checking for an active XDP program and freeing all empty TX buffers. Fixes: efc2214b6047 ("ice: Add support for XDP") Signed-off-by: Kamil Maziarz Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Acked-by: Maciej Fijalkowski Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 6a50f8ba3940..4095fe40dfc9 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -6784,6 +6784,10 @@ int ice_down(struct ice_vsi *vsi) ice_for_each_txq(vsi, i) ice_clean_tx_ring(vsi->tx_rings[i]); + if (ice_is_xdp_ena_vsi(vsi)) + ice_for_each_xdp_txq(vsi, i) + ice_clean_tx_ring(vsi->xdp_rings[i]); + ice_for_each_rxq(vsi, i) ice_clean_rx_ring(vsi->rx_rings[i]); -- cgit 1.4.1 From 4aaa3b730d16c13cc3feaa127bfca1af201d969d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 8 Jun 2023 02:32:02 +0200 Subject: netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE [ Upstream commit 1240eb93f0616b21c675416516ff3d74798fdc97 ] In case of error when adding a new rule that refers to an anonymous set, deactivate expressions via NFT_TRANS_PREPARE state, not NFT_TRANS_RELEASE. Thus, the lookup expression marks anonymous sets as inactive in the next generation to ensure it is not reachable in this transaction anymore and decrement the set refcount as introduced by c1592a89942e ("netfilter: nf_tables: deactivate anonymous set from preparation phase"). The abort step takes care of undoing the anonymous set. This is also consistent with rule deletion, where NFT_TRANS_PREPARE is used. Note that this error path is exercised in the preparation step of the commit protocol. This patch replaces nf_tables_rule_release() by the deactivate and destroy calls, this time with NFT_TRANS_PREPARE. Due to this incorrect error handling, it is possible to access a dangling pointer to the anonymous set that remains in the transaction list. [1009.379054] BUG: KASAN: use-after-free in nft_set_lookup_global+0x147/0x1a0 [nf_tables] [1009.379106] Read of size 8 at addr ffff88816c4c8020 by task nft-rule-add/137110 [1009.379116] CPU: 7 PID: 137110 Comm: nft-rule-add Not tainted 6.4.0-rc4+ #256 [1009.379128] Call Trace: [1009.379132] [1009.379135] dump_stack_lvl+0x33/0x50 [1009.379146] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] [1009.379191] print_address_description.constprop.0+0x27/0x300 [1009.379201] kasan_report+0x107/0x120 [1009.379210] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] [1009.379255] nft_set_lookup_global+0x147/0x1a0 [nf_tables] [1009.379302] nft_lookup_init+0xa5/0x270 [nf_tables] [1009.379350] nf_tables_newrule+0x698/0xe50 [nf_tables] [1009.379397] ? nf_tables_rule_release+0xe0/0xe0 [nf_tables] [1009.379441] ? kasan_unpoison+0x23/0x50 [1009.379450] nfnetlink_rcv_batch+0x97c/0xd90 [nfnetlink] [1009.379470] ? nfnetlink_rcv_msg+0x480/0x480 [nfnetlink] [1009.379485] ? __alloc_skb+0xb8/0x1e0 [1009.379493] ? __alloc_skb+0xb8/0x1e0 [1009.379502] ? entry_SYSCALL_64_after_hwframe+0x46/0xb0 [1009.379509] ? unwind_get_return_address+0x2a/0x40 [1009.379517] ? write_profile+0xc0/0xc0 [1009.379524] ? avc_lookup+0x8f/0xc0 [1009.379532] ? __rcu_read_unlock+0x43/0x60 Fixes: 958bee14d071 ("netfilter: nf_tables: use new transaction infrastructure to handle sets") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index db84d607a413..13d4913266b4 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3669,7 +3669,8 @@ err_destroy_flow_rule: if (flow) nft_flow_rule_destroy(flow); err_release_rule: - nf_tables_rule_release(&ctx, rule); + nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); + nf_tables_rule_destroy(&ctx, rule); err_release_expr: for (i = 0; i < n; i++) { if (expr_info[i].ops) { -- cgit 1.4.1 From 381d49ec68ca0dfdf7582d204a4028bf46b8b84f Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Wed, 7 Jun 2023 17:10:48 +0800 Subject: net: enetc: correct the indexes of highest and 2nd highest TCs [ Upstream commit 21225873be1472b7c59ed3650396af0e40578112 ] For ENETC hardware, the TCs are numbered from 0 to N-1, where N is the number of TCs. Numerically higher TC has higher priority. It's obvious that the highest priority TC index should be N-1 and the 2nd highest priority TC index should be N-2. However, the previous logic uses netdev_get_prio_tc_map() to get the indexes of highest priority and 2nd highest priority TCs, it does not make sense and is incorrect to give a "tc" argument to netdev_get_prio_tc_map(). So the driver may get the wrong indexes of the two highest priotiry TCs which would lead to failed to set the CBS for the two highest priotiry TCs. e.g. $ tc qdisc add dev eno0 parent root handle 100: mqprio num_tc 6 \ map 0 0 1 1 2 3 4 5 queues 1@0 1@1 1@2 1@3 2@4 2@6 hw 1 $ tc qdisc replace dev eno0 parent 100:6 cbs idleslope 100000 \ sendslope -900000 hicredit 12 locredit -113 offload 1 $ Error: Specified device failed to setup cbs hardware offload. ^^^^^ In this example, the previous logic deems the indexes of the two highest priotiry TCs should be 3 and 2. Actually, the indexes are 5 and 4, because the number of TCs is 6. So it would be failed to configure the CBS for the two highest priority TCs. Fixes: c431047c4efe ("enetc: add support Credit Based Shaper(CBS) for hardware offload") Signed-off-by: Wei Fang Reviewed-by: Vladimir Oltean Reviewed-by: Maciej Fijalkowski Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/enetc/enetc_qos.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c index a8539a8554a1..762849959cc1 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c @@ -189,8 +189,8 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) int bw_sum = 0; u8 bw; - prio_top = netdev_get_prio_tc_map(ndev, tc_nums - 1); - prio_next = netdev_get_prio_tc_map(ndev, tc_nums - 2); + prio_top = tc_nums - 1; + prio_next = tc_nums - 2; /* Support highest prio and second prio tc in cbs mode */ if (tc != prio_top && tc != prio_next) -- cgit 1.4.1 From a4a912aee14e8fa4f136da849e9c9197789fa347 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Wed, 7 Jun 2023 18:05:02 +0200 Subject: ping6: Fix send to link-local addresses with VRF. [ Upstream commit 91ffd1bae1dafbb9e34b46813f5b058581d9144d ] Ping sockets can't send packets when they're bound to a VRF master device and the output interface is set to a slave device. For example, when net.ipv4.ping_group_range is properly set, so that ping6 can use ping sockets, the following kind of commands fails: $ ip vrf exec red ping6 fe80::854:e7ff:fe88:4bf1%eth1 What happens is that sk->sk_bound_dev_if is set to the VRF master device, but 'oif' is set to the real output device. Since both are set but different, ping_v6_sendmsg() sees their value as inconsistent and fails. Fix this by allowing 'oif' to be a slave device of ->sk_bound_dev_if. This fixes the following kselftest failure: $ ./fcnal-test.sh -t ipv6_ping [...] TEST: ping out, vrf device+address bind - ns-B IPv6 LLA [FAIL] Reported-by: Mirsad Todorovac Closes: https://lore.kernel.org/netdev/b6191f90-ffca-dbca-7d06-88a9788def9c@alu.unizg.hr/ Tested-by: Mirsad Todorovac Fixes: 5e457896986e ("net: ipv6: Fix ping to link-local addresses.") Signed-off-by: Guillaume Nault Reviewed-by: David Ahern Link: https://lore.kernel.org/r/6c8b53108816a8d0d5705ae37bdc5a8322b5e3d9.1686153846.git.gnault@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/ping.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 808983bc2ec9..4651aaf70db4 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -114,7 +114,8 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) addr_type = ipv6_addr_type(daddr); if ((__ipv6_addr_needs_scope_id(addr_type) && !oif) || (addr_type & IPV6_ADDR_MAPPED) || - (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if)) + (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if && + l3mdev_master_ifindex_by_index(sock_net(sk), oif) != sk->sk_bound_dev_if)) return -EINVAL; ipcm6_init_sk(&ipc6, np); -- cgit 1.4.1 From 300be9f1dc4be4052f5f4abb4d308a90b9359fd6 Mon Sep 17 00:00:00 2001 From: Yuezhen Luan Date: Wed, 7 Jun 2023 09:41:16 -0700 Subject: igb: Fix extts capture value format for 82580/i354/i350 [ Upstream commit 6292d7436cf2f0a2ea8800a1d2cbb155d237818a ] 82580/i354/i350 features circle-counter-like timestamp registers that are different with newer i210. The EXTTS capture value in AUXTSMPx should be converted from raw circle counter value to timestamp value in resolution of 1 nanosec by the driver. This issue can be reproduced on i350 nics, connecting an 1PPS signal to a SDP pin, and run 'ts2phc' command to read external 1PPS timestamp value. On i210 this works fine, but on i350 the extts is not correctly converted. The i350/i354/82580's SYSTIM and other timestamp registers are 40bit counters, presenting time range of 2^40 ns, that means these registers overflows every about 1099s. This causes all these regs can't be used directly in contrast to the newer i210/i211s. The igb driver needs to convert these raw register values to valid time stamp format by using kernel timecounter apis for i350s families. Here the igb_extts() just forgot to do the convert. Fixes: 38970eac41db ("igb: support EXTTS on 82580/i354/i350") Signed-off-by: Yuezhen Luan Reviewed-by: Jacob Keller Tested-by: Pucha Himasekhar Reddy (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230607164116.3768175-1-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igb/igb_main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index b3aed4e2ca91..18ffbc892f86 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6893,6 +6893,7 @@ static void igb_extts(struct igb_adapter *adapter, int tsintr_tt) struct e1000_hw *hw = &adapter->hw; struct ptp_clock_event event; struct timespec64 ts; + unsigned long flags; if (pin < 0 || pin >= IGB_N_SDP) return; @@ -6900,9 +6901,12 @@ static void igb_extts(struct igb_adapter *adapter, int tsintr_tt) if (hw->mac.type == e1000_82580 || hw->mac.type == e1000_i354 || hw->mac.type == e1000_i350) { - s64 ns = rd32(auxstmpl); + u64 ns = rd32(auxstmpl); - ns += ((s64)(rd32(auxstmph) & 0xFF)) << 32; + ns += ((u64)(rd32(auxstmph) & 0xFF)) << 32; + spin_lock_irqsave(&adapter->tmreg_lock, flags); + ns = timecounter_cyc2time(&adapter->tc, ns); + spin_unlock_irqrestore(&adapter->tmreg_lock, flags); ts = ns_to_timespec64(ns); } else { ts.tv_nsec = rd32(auxstmpl); -- cgit 1.4.1 From b4e5d0c4cf07caef4b596a7afe0b4ad8e15d8283 Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Tue, 31 Jan 2023 16:05:12 -0300 Subject: net/sched: simplify tcf_pedit_act [ Upstream commit 95b069382351826c0ae37938070aa82dbeaf288d ] Remove the check for a negative number of keys as this cannot ever happen Reviewed-by: Jamal Hadi Salim Reviewed-by: Simon Horman Signed-off-by: Pedro Tammela Signed-off-by: Paolo Abeni Stable-dep-of: 6c02568fd1ae ("net/sched: act_pedit: Parse L3 Header for L4 offset") Signed-off-by: Sasha Levin --- net/sched/act_pedit.c | 137 ++++++++++++++++++++++++-------------------------- 1 file changed, 67 insertions(+), 70 deletions(-) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 238759c3192e..19f6b3fa6a55 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -348,8 +348,12 @@ static int pedit_skb_hdr_offset(struct sk_buff *skb, static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { + enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; + enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET; struct tcf_pedit *p = to_pedit(a); + struct tcf_pedit_key_ex *tkey_ex; struct tcf_pedit_parms *parms; + struct tc_pedit_key *tkey; u32 max_offset; int i; @@ -365,88 +369,81 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, tcf_lastuse_update(&p->tcf_tm); tcf_action_update_bstats(&p->common, skb); - if (parms->tcfp_nkeys > 0) { - struct tc_pedit_key *tkey = parms->tcfp_keys; - struct tcf_pedit_key_ex *tkey_ex = parms->tcfp_keys_ex; - enum pedit_header_type htype = - TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; - enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET; - - for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) { - u32 *ptr, hdata; - int offset = tkey->off; - int hoffset; - u32 val; - int rc; - - if (tkey_ex) { - htype = tkey_ex->htype; - cmd = tkey_ex->cmd; - - tkey_ex++; - } + tkey = parms->tcfp_keys; + tkey_ex = parms->tcfp_keys_ex; - rc = pedit_skb_hdr_offset(skb, htype, &hoffset); - if (rc) { - pr_info("tc action pedit bad header type specified (0x%x)\n", - htype); - goto bad; - } + for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) { + int offset = tkey->off; + u32 *ptr, hdata; + int hoffset; + u32 val; + int rc; - if (tkey->offmask) { - u8 *d, _d; - - if (!offset_valid(skb, hoffset + tkey->at)) { - pr_info("tc action pedit 'at' offset %d out of bounds\n", - hoffset + tkey->at); - goto bad; - } - d = skb_header_pointer(skb, hoffset + tkey->at, - sizeof(_d), &_d); - if (!d) - goto bad; - offset += (*d & tkey->offmask) >> tkey->shift; - } + if (tkey_ex) { + htype = tkey_ex->htype; + cmd = tkey_ex->cmd; - if (offset % 4) { - pr_info("tc action pedit offset must be on 32 bit boundaries\n"); - goto bad; - } + tkey_ex++; + } - if (!offset_valid(skb, hoffset + offset)) { - pr_info("tc action pedit offset %d out of bounds\n", - hoffset + offset); - goto bad; - } + rc = pedit_skb_hdr_offset(skb, htype, &hoffset); + if (rc) { + pr_info("tc action pedit bad header type specified (0x%x)\n", + htype); + goto bad; + } - ptr = skb_header_pointer(skb, hoffset + offset, - sizeof(hdata), &hdata); - if (!ptr) - goto bad; - /* just do it, baby */ - switch (cmd) { - case TCA_PEDIT_KEY_EX_CMD_SET: - val = tkey->val; - break; - case TCA_PEDIT_KEY_EX_CMD_ADD: - val = (*ptr + tkey->val) & ~tkey->mask; - break; - default: - pr_info("tc action pedit bad command (%d)\n", - cmd); + if (tkey->offmask) { + u8 *d, _d; + + if (!offset_valid(skb, hoffset + tkey->at)) { + pr_info("tc action pedit 'at' offset %d out of bounds\n", + hoffset + tkey->at); goto bad; } + d = skb_header_pointer(skb, hoffset + tkey->at, + sizeof(_d), &_d); + if (!d) + goto bad; + offset += (*d & tkey->offmask) >> tkey->shift; + } - *ptr = ((*ptr & tkey->mask) ^ val); - if (ptr == &hdata) - skb_store_bits(skb, hoffset + offset, ptr, 4); + if (offset % 4) { + pr_info("tc action pedit offset must be on 32 bit boundaries\n"); + goto bad; } - goto done; - } else { - WARN(1, "pedit BUG: index %d\n", p->tcf_index); + if (!offset_valid(skb, hoffset + offset)) { + pr_info("tc action pedit offset %d out of bounds\n", + hoffset + offset); + goto bad; + } + + ptr = skb_header_pointer(skb, hoffset + offset, + sizeof(hdata), &hdata); + if (!ptr) + goto bad; + /* just do it, baby */ + switch (cmd) { + case TCA_PEDIT_KEY_EX_CMD_SET: + val = tkey->val; + break; + case TCA_PEDIT_KEY_EX_CMD_ADD: + val = (*ptr + tkey->val) & ~tkey->mask; + break; + default: + pr_info("tc action pedit bad command (%d)\n", + cmd); + goto bad; + } + + *ptr = ((*ptr & tkey->mask) ^ val); + if (ptr == &hdata) + skb_store_bits(skb, hoffset + offset, ptr, 4); } + goto done; + bad: spin_lock(&p->tcf_lock); p->tcf_qstats.overlimits++; -- cgit 1.4.1 From fb25478f66990ef9094ce43f884f29a361ce8ad2 Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Fri, 21 Apr 2023 18:25:16 -0300 Subject: net/sched: act_pedit: remove extra check for key type [ Upstream commit 577140180ba28d0d37bc898c7bd6702c83aa106f ] The netlink parsing already validates the key 'htype'. Remove the datapath check as it's redundant. Reviewed-by: Jamal Hadi Salim Reviewed-by: Simon Horman Signed-off-by: Pedro Tammela Signed-off-by: David S. Miller Stable-dep-of: 6c02568fd1ae ("net/sched: act_pedit: Parse L3 Header for L4 offset") Signed-off-by: Sasha Levin --- net/sched/act_pedit.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 19f6b3fa6a55..48e14cbcd8ff 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -312,37 +312,28 @@ static bool offset_valid(struct sk_buff *skb, int offset) return true; } -static int pedit_skb_hdr_offset(struct sk_buff *skb, - enum pedit_header_type htype, int *hoffset) +static void pedit_skb_hdr_offset(struct sk_buff *skb, + enum pedit_header_type htype, int *hoffset) { - int ret = -EINVAL; - + /* 'htype' is validated in the netlink parsing */ switch (htype) { case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: - if (skb_mac_header_was_set(skb)) { + if (skb_mac_header_was_set(skb)) *hoffset = skb_mac_offset(skb); - ret = 0; - } break; case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: *hoffset = skb_network_offset(skb); - ret = 0; break; case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: - if (skb_transport_header_was_set(skb)) { + if (skb_transport_header_was_set(skb)) *hoffset = skb_transport_offset(skb); - ret = 0; - } break; default: - ret = -EINVAL; break; } - - return ret; } static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, @@ -374,10 +365,9 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) { int offset = tkey->off; + int hoffset = 0; u32 *ptr, hdata; - int hoffset; u32 val; - int rc; if (tkey_ex) { htype = tkey_ex->htype; @@ -386,12 +376,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, tkey_ex++; } - rc = pedit_skb_hdr_offset(skb, htype, &hoffset); - if (rc) { - pr_info("tc action pedit bad header type specified (0x%x)\n", - htype); - goto bad; - } + pedit_skb_hdr_offset(skb, htype, &hoffset); if (tkey->offmask) { u8 *d, _d; -- cgit 1.4.1 From 5cf38fbc82bac41e44368514e00a27c4c2818bd7 Mon Sep 17 00:00:00 2001 From: Max Tottenham Date: Wed, 7 Jun 2023 12:23:54 -0400 Subject: net/sched: act_pedit: Parse L3 Header for L4 offset [ Upstream commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b ] Instead of relying on skb->transport_header being set correctly, opt instead to parse the L3 header length out of the L3 headers for both IPv4/IPv6 when the Extended Layer Op for tcp/udp is used. This fixes a bug if GRO is disabled, when GRO is disabled skb->transport_header is set by __netif_receive_skb_core() to point to the L3 header, it's later fixed by the upper protocol layers, but act_pedit will receive the SKB before the fixups are completed. The existing behavior causes the following to edit the L3 header if GRO is disabled instead of the UDP header: tc filter add dev eth0 ingress protocol ip flower ip_proto udp \ dst_ip 192.168.1.3 action pedit ex munge udp set dport 18053 Also re-introduce a rate-limited warning if we were unable to extract the header offset when using the 'ex' interface. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Signed-off-by: Max Tottenham Reviewed-by: Josh Hunt Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202305261541.N165u9TZ-lkp@intel.com/ Reviewed-by: Pedro Tammela Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/act_pedit.c | 48 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 48e14cbcd8ff..180669aa9d09 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -13,7 +13,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -312,28 +315,58 @@ static bool offset_valid(struct sk_buff *skb, int offset) return true; } -static void pedit_skb_hdr_offset(struct sk_buff *skb, +static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type) +{ + const int noff = skb_network_offset(skb); + int ret = -EINVAL; + struct iphdr _iph; + + switch (skb->protocol) { + case htons(ETH_P_IP): { + const struct iphdr *iph = skb_header_pointer(skb, noff, sizeof(_iph), &_iph); + + if (!iph) + goto out; + *hoffset = noff + iph->ihl * 4; + ret = 0; + break; + } + case htons(ETH_P_IPV6): + ret = ipv6_find_hdr(skb, hoffset, header_type, NULL, NULL) == header_type ? 0 : -EINVAL; + break; + } +out: + return ret; +} + +static int pedit_skb_hdr_offset(struct sk_buff *skb, enum pedit_header_type htype, int *hoffset) { + int ret = -EINVAL; /* 'htype' is validated in the netlink parsing */ switch (htype) { case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: - if (skb_mac_header_was_set(skb)) + if (skb_mac_header_was_set(skb)) { *hoffset = skb_mac_offset(skb); + ret = 0; + } break; case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: *hoffset = skb_network_offset(skb); + ret = 0; break; case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: + ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_TCP); + break; case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: - if (skb_transport_header_was_set(skb)) - *hoffset = skb_transport_offset(skb); + ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_UDP); break; default: break; } + return ret; } static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, @@ -368,6 +401,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, int hoffset = 0; u32 *ptr, hdata; u32 val; + int rc; if (tkey_ex) { htype = tkey_ex->htype; @@ -376,7 +410,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, tkey_ex++; } - pedit_skb_hdr_offset(skb, htype, &hoffset); + rc = pedit_skb_hdr_offset(skb, htype, &hoffset); + if (rc) { + pr_info_ratelimited("tc action pedit unable to extract header offset for header type (0x%x)\n", htype); + goto bad; + } if (tkey->offmask) { u8 *d, _d; -- cgit 1.4.1 From c9411f014e5ccd2e3cb2dc8ab96ea6bc76a978f6 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Thu, 8 Jun 2023 10:46:25 +0530 Subject: octeontx2-af: Fix promiscuous mode [ Upstream commit c0e489372a294044feea650b38f38c888eff57a4 ] CN10KB silicon introduced a new exact match feature, which is used for DMAC filtering. The state of installed DMAC filters in this exact match table is getting corrupted when promiscuous mode is toggled. Fix this by not touching Exact match related config when promiscuous mode is toggled. Fixes: 2dba9459d2c9 ("octeontx2-af: Wrapper functions for MAC addr add/del/update/reset") Signed-off-by: Ratheesh Kannoth Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- .../ethernet/marvell/octeontx2/af/rvu_npc_hash.c | 29 ++-------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index 3182adb7b9a8..3b48b635977f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -1168,10 +1168,8 @@ static u16 __rvu_npc_exact_cmd_rules_cnt_update(struct rvu *rvu, int drop_mcam_i { struct npc_exact_table *table; u16 *cnt, old_cnt; - bool promisc; table = rvu->hw->table; - promisc = table->promisc_mode[drop_mcam_idx]; cnt = &table->cnt_cmd_rules[drop_mcam_idx]; old_cnt = *cnt; @@ -1183,16 +1181,13 @@ static u16 __rvu_npc_exact_cmd_rules_cnt_update(struct rvu *rvu, int drop_mcam_i *enable_or_disable_cam = false; - if (promisc) - goto done; - - /* If all rules are deleted and not already in promisc mode; disable cam */ + /* If all rules are deleted, disable cam */ if (!*cnt && val < 0) { *enable_or_disable_cam = true; goto done; } - /* If rule got added and not already in promisc mode; enable cam */ + /* If rule got added, enable cam */ if (!old_cnt && val > 0) { *enable_or_disable_cam = true; goto done; @@ -1447,7 +1442,6 @@ int rvu_npc_exact_promisc_disable(struct rvu *rvu, u16 pcifunc) u32 drop_mcam_idx; bool *promisc; bool rc; - u32 cnt; table = rvu->hw->table; @@ -1470,17 +1464,8 @@ int rvu_npc_exact_promisc_disable(struct rvu *rvu, u16 pcifunc) return LMAC_AF_ERR_INVALID_PARAM; } *promisc = false; - cnt = __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, 0, NULL); mutex_unlock(&table->lock); - /* If no dmac filter entries configured, disable drop rule */ - if (!cnt) - rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, false); - else - rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, !*promisc); - - dev_dbg(rvu->dev, "%s: disabled promisc mode (cgx=%d lmac=%d, cnt=%d)\n", - __func__, cgx_id, lmac_id, cnt); return 0; } @@ -1498,7 +1483,6 @@ int rvu_npc_exact_promisc_enable(struct rvu *rvu, u16 pcifunc) u32 drop_mcam_idx; bool *promisc; bool rc; - u32 cnt; table = rvu->hw->table; @@ -1521,17 +1505,8 @@ int rvu_npc_exact_promisc_enable(struct rvu *rvu, u16 pcifunc) return LMAC_AF_ERR_INVALID_PARAM; } *promisc = true; - cnt = __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, 0, NULL); mutex_unlock(&table->lock); - /* If no dmac filter entries configured, disable drop rule */ - if (!cnt) - rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, false); - else - rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, !*promisc); - - dev_dbg(rvu->dev, "%s: Enabled promisc mode (cgx=%d lmac=%d cnt=%d)\n", - __func__, cgx_id, lmac_id, cnt); return 0; } -- cgit 1.4.1 From 07f9cc229b44cbcee6385802d390091d915f38c3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 8 Jun 2023 08:29:03 +0100 Subject: net/sched: cls_u32: Fix reference counter leak leading to overflow [ Upstream commit 04c55383fa5689357bcdd2c8036725a55ed632bc ] In the event of a failure in tcf_change_indev(), u32_set_parms() will immediately return without decrementing the recently incremented reference counter. If this happens enough times, the counter will rollover and the reference freed, leading to a double free which can be used to do 'bad things'. In order to prevent this, move the point of possible failure above the point where the reference counter is incremented. Also save any meaningful return values to be applied to the return data at the appropriate point in time. This issue was caught with KASAN. Fixes: 705c7091262d ("net: sched: cls_u32: no need to call tcf_exts_change for newly allocated struct") Suggested-by: Eric Dumazet Signed-off-by: Lee Jones Reviewed-by: Eric Dumazet Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/cls_u32.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 34d25f7a0687..a3477537c102 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -716,13 +716,19 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, struct nlattr *est, u32 flags, u32 fl_flags, struct netlink_ext_ack *extack) { - int err; + int err, ifindex = -1; err = tcf_exts_validate_ex(net, tp, tb, est, &n->exts, flags, fl_flags, extack); if (err < 0) return err; + if (tb[TCA_U32_INDEV]) { + ifindex = tcf_change_indev(net, tb[TCA_U32_INDEV], extack); + if (ifindex < 0) + return -EINVAL; + } + if (tb[TCA_U32_LINK]) { u32 handle = nla_get_u32(tb[TCA_U32_LINK]); struct tc_u_hnode *ht_down = NULL, *ht_old; @@ -757,13 +763,9 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, tcf_bind_filter(tp, &n->res, base); } - if (tb[TCA_U32_INDEV]) { - int ret; - ret = tcf_change_indev(net, tb[TCA_U32_INDEV], extack); - if (ret < 0) - return -EINVAL; - n->ifindex = ret; - } + if (ifindex >= 0) + n->ifindex = ifindex; + return 0; } -- cgit 1.4.1 From 49f3a79f03bdbcb16a133d51e9c57b57cf22d96e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Jun 2023 16:35:59 +0300 Subject: wifi: mac80211: fix link activation settings order [ Upstream commit 01605ad6c3e8608d7e147c9b75d67eb8a3d27d88 ] In the normal MLME code we always call ieee80211_mgd_set_link_qos_params() before ieee80211_link_info_change_notify() and some drivers, notably iwlwifi, rely on that as they don't do anything (but store the data) in their conf_tx. Fix the order here to be the same as in the normal code paths, so this isn't broken. Fixes: 3d9011029227 ("wifi: mac80211: implement link switching") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230608163202.a2a86bba2f80.Iac97e04827966d22161e63bb6e201b4061e9651b@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/link.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index a1b3031fefce..a85b44c1bc99 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -2,7 +2,7 @@ /* * MLO link handling * - * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2022-2023 Intel Corporation */ #include #include @@ -387,6 +387,7 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, IEEE80211_CHANCTX_SHARED); WARN_ON_ONCE(ret); + ieee80211_mgd_set_link_qos_params(link); ieee80211_link_info_change_notify(sdata, link, BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | @@ -401,7 +402,6 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, BSS_CHANGED_TWT | BSS_CHANGED_HE_OBSS_PD | BSS_CHANGED_HE_BSS_COLOR); - ieee80211_mgd_set_link_qos_params(link); } old_active = sdata->vif.active_links; -- cgit 1.4.1 From 170ceadf4a57d2e7d259e85366d37874bff1ea65 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 8 Jun 2023 16:36:01 +0300 Subject: wifi: cfg80211: fix link del callback to call correct handler [ Upstream commit 1ff56684fa8682bdfbbce4e12cf67ab23cb1db05 ] The wrapper function was incorrectly calling the add handler instead of the del handler. This had no negative side effect as the default handlers are essentially identical. Fixes: f2a0290b2df2 ("wifi: cfg80211: add optional link add/remove callbacks") Signed-off-by: Benjamin Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230608163202.ebd00e000459.Iaff7dc8d1cdecf77f53ea47a0e5080caa36ea02a@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/rdev-ops.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 13b209a8db28..ee853a14a02d 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018, 2021-2022 Intel Corporation + * Copyright (C) 2018, 2021-2023 Intel Corporation */ #ifndef __CFG80211_RDEV_OPS #define __CFG80211_RDEV_OPS @@ -1441,8 +1441,8 @@ rdev_del_intf_link(struct cfg80211_registered_device *rdev, unsigned int link_id) { trace_rdev_del_intf_link(&rdev->wiphy, wdev, link_id); - if (rdev->ops->add_intf_link) - rdev->ops->add_intf_link(&rdev->wiphy, wdev, link_id); + if (rdev->ops->del_intf_link) + rdev->ops->del_intf_link(&rdev->wiphy, wdev, link_id); trace_rdev_return_void(&rdev->wiphy); } -- cgit 1.4.1 From e83bc93886276b7689c229d9c9563b3793bb70bd Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 8 Jun 2023 16:36:02 +0300 Subject: wifi: mac80211: take lock before setting vif links [ Upstream commit 15846f95ab01b71fdb1cef8df73680aad41edf70 ] ieee80211_vif_set_links requires the sdata->local->mtx lock to be held. Add the appropriate locking around the calls in both the link add and remove handlers. This causes a warning when e.g. ieee80211_link_release_channel is called via ieee80211_link_stop from ieee80211_vif_update_links. Fixes: 0d8c4a3c8688 ("wifi: mac80211: implement add/del interface link callbacks") Signed-off-by: Benjamin Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230608163202.fa0c6597fdad.I83dd70359f6cda30f86df8418d929c2064cf4995@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/cfg.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 06b9df2fbcd7..23a44edcb11f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4732,11 +4732,16 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy, unsigned int link_id) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + int res; if (wdev->use_4addr) return -EOPNOTSUPP; - return ieee80211_vif_set_links(sdata, wdev->valid_links); + mutex_lock(&sdata->local->mtx); + res = ieee80211_vif_set_links(sdata, wdev->valid_links); + mutex_unlock(&sdata->local->mtx); + + return res; } static void ieee80211_del_intf_link(struct wiphy *wiphy, @@ -4745,7 +4750,9 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + mutex_lock(&sdata->local->mtx); ieee80211_vif_set_links(sdata, wdev->valid_links); + mutex_unlock(&sdata->local->mtx); } static int sta_add_link_station(struct ieee80211_local *local, -- cgit 1.4.1 From b0b3848e0363d5dc7b9293674530d43674b1b224 Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Fri, 21 Oct 2022 15:01:04 -0500 Subject: RDMA/rxe: Removed unused name from rxe_task struct [ Upstream commit de669ae8af49ceed0eed44f5b3d51dc62affc5e4 ] The name field in struct rxe_task is never used. This patch removes it. Link: https://lore.kernel.org/r/20221021200118.2163-4-rpearsonhpe@gmail.com Signed-off-by: Ian Ziemba Signed-off-by: Bob Pearson Signed-off-by: Jason Gunthorpe Stable-dep-of: 2a62b6210ce8 ("RDMA/rxe: Fix the use-before-initialization error of resp_pkts") Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_qp.c | 9 +++------ drivers/infiniband/sw/rxe/rxe_task.c | 4 +--- drivers/infiniband/sw/rxe/rxe_task.h | 4 +--- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index 1f6e006c51c4..f48a624de493 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -242,10 +242,8 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, skb_queue_head_init(&qp->req_pkts); - rxe_init_task(&qp->req.task, qp, - rxe_requester, "req"); - rxe_init_task(&qp->comp.task, qp, - rxe_completer, "comp"); + rxe_init_task(&qp->req.task, qp, rxe_requester); + rxe_init_task(&qp->comp.task, qp, rxe_completer); qp->qp_timeout_jiffies = 0; /* Can't be set for UD/UC in modify_qp */ if (init->qp_type == IB_QPT_RC) { @@ -292,8 +290,7 @@ static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp, skb_queue_head_init(&qp->resp_pkts); - rxe_init_task(&qp->resp.task, qp, - rxe_responder, "resp"); + rxe_init_task(&qp->resp.task, qp, rxe_responder); qp->resp.opcode = OPCODE_NONE; qp->resp.msn = 0; diff --git a/drivers/infiniband/sw/rxe/rxe_task.c b/drivers/infiniband/sw/rxe/rxe_task.c index ec2b7de1c497..182d0532a8ab 100644 --- a/drivers/infiniband/sw/rxe/rxe_task.c +++ b/drivers/infiniband/sw/rxe/rxe_task.c @@ -94,12 +94,10 @@ void rxe_do_task(struct tasklet_struct *t) task->ret = ret; } -int rxe_init_task(struct rxe_task *task, - void *arg, int (*func)(void *), char *name) +int rxe_init_task(struct rxe_task *task, void *arg, int (*func)(void *)) { task->arg = arg; task->func = func; - snprintf(task->name, sizeof(task->name), "%s", name); task->destroyed = false; tasklet_setup(&task->tasklet, rxe_do_task); diff --git a/drivers/infiniband/sw/rxe/rxe_task.h b/drivers/infiniband/sw/rxe/rxe_task.h index 7f612a1c68a7..b3dfd970d1dc 100644 --- a/drivers/infiniband/sw/rxe/rxe_task.h +++ b/drivers/infiniband/sw/rxe/rxe_task.h @@ -25,7 +25,6 @@ struct rxe_task { void *arg; int (*func)(void *arg); int ret; - char name[16]; bool destroyed; }; @@ -34,8 +33,7 @@ struct rxe_task { * arg => parameter to pass to fcn * func => function to call until it returns != 0 */ -int rxe_init_task(struct rxe_task *task, - void *arg, int (*func)(void *), char *name); +int rxe_init_task(struct rxe_task *task, void *arg, int (*func)(void *)); /* cleanup task */ void rxe_cleanup_task(struct rxe_task *task); -- cgit 1.4.1 From 26256aa7edcdd936be6ee63d18a5bbf0b8e32bf4 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Fri, 2 Jun 2023 11:54:08 +0800 Subject: RDMA/rxe: Fix the use-before-initialization error of resp_pkts [ Upstream commit 2a62b6210ce876c596086ab8fd4c8a0c3d10611a ] In the following: Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0xd9/0x150 lib/dump_stack.c:106 assign_lock_key kernel/locking/lockdep.c:982 [inline] register_lock_class+0xdb6/0x1120 kernel/locking/lockdep.c:1295 __lock_acquire+0x10a/0x5df0 kernel/locking/lockdep.c:4951 lock_acquire kernel/locking/lockdep.c:5691 [inline] lock_acquire+0x1b1/0x520 kernel/locking/lockdep.c:5656 __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:110 [inline] _raw_spin_lock_irqsave+0x3d/0x60 kernel/locking/spinlock.c:162 skb_dequeue+0x20/0x180 net/core/skbuff.c:3639 drain_resp_pkts drivers/infiniband/sw/rxe/rxe_comp.c:555 [inline] rxe_completer+0x250d/0x3cc0 drivers/infiniband/sw/rxe/rxe_comp.c:652 rxe_qp_do_cleanup+0x1be/0x820 drivers/infiniband/sw/rxe/rxe_qp.c:761 execute_in_process_context+0x3b/0x150 kernel/workqueue.c:3473 __rxe_cleanup+0x21e/0x370 drivers/infiniband/sw/rxe/rxe_pool.c:233 rxe_create_qp+0x3f6/0x5f0 drivers/infiniband/sw/rxe/rxe_verbs.c:583 This is a use-before-initialization problem. It happens because rxe_qp_do_cleanup is called during error unwind before the struct has been fully initialized. Move the initialization of the skb earlier. Fixes: 8700e3e7c485 ("Soft RoCE driver") Link: https://lore.kernel.org/r/20230602035408.741534-1-yanjun.zhu@intel.com Reported-by: syzbot+eba589d8f49c73d356da@syzkaller.appspotmail.com Signed-off-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_qp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index f48a624de493..59b2024b34ef 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -180,6 +180,9 @@ static void rxe_qp_init_misc(struct rxe_dev *rxe, struct rxe_qp *qp, spin_lock_init(&qp->rq.producer_lock); spin_lock_init(&qp->rq.consumer_lock); + skb_queue_head_init(&qp->req_pkts); + skb_queue_head_init(&qp->resp_pkts); + atomic_set(&qp->ssn, 0); atomic_set(&qp->skb_out, 0); } @@ -240,8 +243,6 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, qp->req.opcode = -1; qp->comp.opcode = -1; - skb_queue_head_init(&qp->req_pkts); - rxe_init_task(&qp->req.task, qp, rxe_requester); rxe_init_task(&qp->comp.task, qp, rxe_completer); @@ -288,8 +289,6 @@ static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp, } } - skb_queue_head_init(&qp->resp_pkts); - rxe_init_task(&qp->resp.task, qp, rxe_responder); qp->resp.opcode = OPCODE_NONE; -- cgit 1.4.1 From b4a3cae58cd86371293a6e504c4a10a31ea51ae2 Mon Sep 17 00:00:00 2001 From: Ahmed Zaki Date: Thu, 8 Jun 2023 13:02:26 -0700 Subject: iavf: remove mask from iavf_irq_enable_queues() [ Upstream commit c37cf54c12cfaa51e7aaf88708167b0d3259e64e ] Enable more than 32 IRQs by removing the u32 bit mask in iavf_irq_enable_queues(). There is no need for the mask as there are no callers that select individual IRQs through the bitmask. Also, if the PF allocates more than 32 IRQs, this mask will prevent us from using all of them. Modify the comment in iavf_register.h to show that the maximum number allowed for the IRQ index is 63 as per the iAVF standard 1.0 [1]. link: [1] https://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ethernet-adaptive-virtual-function-hardware-spec.pdf Fixes: 5eae00c57f5e ("i40evf: main driver core") Signed-off-by: Ahmed Zaki Tested-by: Rafal Romanowski Reviewed-by: Simon Horman Reviewed-by: Maciej Fijalkowski Signed-off-by: Tony Nguyen Link: https://lore.kernel.org/r/20230608200226.451861-1-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/iavf/iavf.h | 2 +- drivers/net/ethernet/intel/iavf/iavf_main.c | 15 ++++++--------- drivers/net/ethernet/intel/iavf/iavf_register.h | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h index 93a998f169de..6625625f91e4 100644 --- a/drivers/net/ethernet/intel/iavf/iavf.h +++ b/drivers/net/ethernet/intel/iavf/iavf.h @@ -525,7 +525,7 @@ void iavf_set_ethtool_ops(struct net_device *netdev); void iavf_update_stats(struct iavf_adapter *adapter); void iavf_reset_interrupt_capability(struct iavf_adapter *adapter); int iavf_init_interrupt_scheme(struct iavf_adapter *adapter); -void iavf_irq_enable_queues(struct iavf_adapter *adapter, u32 mask); +void iavf_irq_enable_queues(struct iavf_adapter *adapter); void iavf_free_all_tx_resources(struct iavf_adapter *adapter); void iavf_free_all_rx_resources(struct iavf_adapter *adapter); diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index 34711a88dbaa..965d02d7ff80 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -359,21 +359,18 @@ static void iavf_irq_disable(struct iavf_adapter *adapter) } /** - * iavf_irq_enable_queues - Enable interrupt for specified queues + * iavf_irq_enable_queues - Enable interrupt for all queues * @adapter: board private structure - * @mask: bitmap of queues to enable **/ -void iavf_irq_enable_queues(struct iavf_adapter *adapter, u32 mask) +void iavf_irq_enable_queues(struct iavf_adapter *adapter) { struct iavf_hw *hw = &adapter->hw; int i; for (i = 1; i < adapter->num_msix_vectors; i++) { - if (mask & BIT(i - 1)) { - wr32(hw, IAVF_VFINT_DYN_CTLN1(i - 1), - IAVF_VFINT_DYN_CTLN1_INTENA_MASK | - IAVF_VFINT_DYN_CTLN1_ITR_INDX_MASK); - } + wr32(hw, IAVF_VFINT_DYN_CTLN1(i - 1), + IAVF_VFINT_DYN_CTLN1_INTENA_MASK | + IAVF_VFINT_DYN_CTLN1_ITR_INDX_MASK); } } @@ -387,7 +384,7 @@ void iavf_irq_enable(struct iavf_adapter *adapter, bool flush) struct iavf_hw *hw = &adapter->hw; iavf_misc_irq_enable(adapter); - iavf_irq_enable_queues(adapter, ~0); + iavf_irq_enable_queues(adapter); if (flush) iavf_flush(hw); diff --git a/drivers/net/ethernet/intel/iavf/iavf_register.h b/drivers/net/ethernet/intel/iavf/iavf_register.h index bf793332fc9d..a19e88898a0b 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_register.h +++ b/drivers/net/ethernet/intel/iavf/iavf_register.h @@ -40,7 +40,7 @@ #define IAVF_VFINT_DYN_CTL01_INTENA_MASK IAVF_MASK(0x1, IAVF_VFINT_DYN_CTL01_INTENA_SHIFT) #define IAVF_VFINT_DYN_CTL01_ITR_INDX_SHIFT 3 #define IAVF_VFINT_DYN_CTL01_ITR_INDX_MASK IAVF_MASK(0x3, IAVF_VFINT_DYN_CTL01_ITR_INDX_SHIFT) -#define IAVF_VFINT_DYN_CTLN1(_INTVF) (0x00003800 + ((_INTVF) * 4)) /* _i=0...15 */ /* Reset: VFR */ +#define IAVF_VFINT_DYN_CTLN1(_INTVF) (0x00003800 + ((_INTVF) * 4)) /* _i=0...63 */ /* Reset: VFR */ #define IAVF_VFINT_DYN_CTLN1_INTENA_SHIFT 0 #define IAVF_VFINT_DYN_CTLN1_INTENA_MASK IAVF_MASK(0x1, IAVF_VFINT_DYN_CTLN1_INTENA_SHIFT) #define IAVF_VFINT_DYN_CTLN1_SWINT_TRIG_SHIFT 2 -- cgit 1.4.1 From 7c6d50414649756508379e873d87ab2726a2fc7d Mon Sep 17 00:00:00 2001 From: Satha Rao Date: Thu, 8 Jun 2023 17:12:00 +0530 Subject: octeontx2-af: fixed resource availability check [ Upstream commit 4e635f9d86165e47f5440196f2ebdb258efb8341 ] txschq_alloc response have two different arrays to store continuous and non-continuous schedulers of each level. Requested count should be checked for each array separately. Fixes: 5d9b976d4480 ("octeontx2-af: Support fixed transmit scheduler topology") Signed-off-by: Satha Rao Signed-off-by: Sunil Kovvuri Goutham Signed-off-by: Naveen Mamindlapalli Reviewed-by: Sridhar Samudrala Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 84f2ba53b8b6..506c67dd6cd4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -1878,7 +1878,8 @@ static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc, free_cnt = rvu_rsrc_free_count(&txsch->schq); } - if (free_cnt < req_schq || req_schq > MAX_TXSCHQ_PER_FUNC) + if (free_cnt < req_schq || req->schq[lvl] > MAX_TXSCHQ_PER_FUNC || + req->schq_contig[lvl] > MAX_TXSCHQ_PER_FUNC) return NIX_AF_ERR_TLX_ALLOC_FAIL; /* If contiguous queues are needed, check for availability */ -- cgit 1.4.1 From c764fed5e544839d3f3dfc7ee248a8a508a9f200 Mon Sep 17 00:00:00 2001 From: Nithin Dabilpuram Date: Thu, 8 Jun 2023 17:12:01 +0530 Subject: octeontx2-af: fix lbk link credits on cn10k [ Upstream commit 87e12a17eef476bbf768dc3a74419ad461f36fbc ] Fix LBK link credits on CN10K to be same as CN9K i.e 16 * MAX_LBK_DATA_RATE instead of current scheme of calculation based on LBK buf length / FIFO size. Fixes: 6e54e1c5399a ("octeontx2-af: cn10K: Add MTU configuration") Signed-off-by: Nithin Dabilpuram Signed-off-by: Naveen Mamindlapalli Reviewed-by: Sridhar Samudrala Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 506c67dd6cd4..8cb2a0181fb9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4070,10 +4070,6 @@ int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req, static u64 rvu_get_lbk_link_credits(struct rvu *rvu, u16 lbk_max_frs) { - /* CN10k supports 72KB FIFO size and max packet size of 64k */ - if (rvu->hw->lbk_bufsize == 0x12000) - return (rvu->hw->lbk_bufsize - lbk_max_frs) / 16; - return 1600; /* 16 * max LBK datarate = 16 * 100Gbps */ } -- cgit 1.4.1 From 3a83145b660cbfd6be7bd7d9ce7e92c05f284cc0 Mon Sep 17 00:00:00 2001 From: Maher Sanalla Date: Mon, 5 Jun 2023 13:33:17 +0300 Subject: RDMA/mlx5: Initiate dropless RQ for RAW Ethernet functions [ Upstream commit ee4d269eccfea6c17b18281bef482700d898e86f ] Delay drop data is initiated for PFs that have the capability of rq_delay_drop and are in roce profile. However, PFs with RAW ethernet profile do not initiate delay drop data on function load, causing kernel panic if delay drop struct members are accessed later on in case a dropless RQ is created. Thus, stage the delay drop initialization as part of RAW ethernet PF loading process. Fixes: b5ca15ad7e61 ("IB/mlx5: Add proper representors support") Signed-off-by: Maher Sanalla Reviewed-by: Maor Gottlieb Link: https://lore.kernel.org/r/2e9d386785043d48c38711826eb910315c1de141.1685960567.git.leon@kernel.org Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index eaa35e1df2a8..3178df55c4d8 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4250,6 +4250,9 @@ const struct mlx5_ib_profile raw_eth_profile = { STAGE_CREATE(MLX5_IB_STAGE_POST_IB_REG_UMR, mlx5_ib_stage_post_ib_reg_umr_init, NULL), + STAGE_CREATE(MLX5_IB_STAGE_DELAY_DROP, + mlx5_ib_stage_delay_drop_init, + mlx5_ib_stage_delay_drop_cleanup), STAGE_CREATE(MLX5_IB_STAGE_RESTRACK, mlx5_ib_restrack_init, NULL), -- cgit 1.4.1 From ec6d49687d2b6056bf8ee35204c91e9eb1e188f1 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Mon, 5 Jun 2023 13:33:18 +0300 Subject: RDMA/mlx5: Create an indirect flow table for steering anchor [ Upstream commit e1f4a52ac171dd863fe89055e749ef5e0a0bc5ce ] A misbehaved user can create a steering anchor that points to a kernel flow table and then destroy the anchor without freeing the associated STC. This creates a problem as the kernel can't destroy the flow table since there is still a reference to it. As a result, this can exhaust all available flow table resources, preventing other users from using the RDMA device. To prevent this problem, a solution is implemented where a special flow table with two steering rules is created when a user creates a steering anchor for the first time. The rules include one that drops all traffic and another that points to the kernel flow table. If the steering anchor is destroyed, only the rule pointing to the kernel's flow table is removed. Any traffic reaching the special flow table after that is dropped. Since the special flow table is not destroyed when the steering anchor is destroyed, any issues are prevented from occurring. The remaining resources are only destroyed when the RDMA device is destroyed, which happens after all DEVX objects are freed, including the STCs, thus mitigating the issue. Fixes: 0c6ab0ca9a66 ("RDMA/mlx5: Expose steering anchor to userspace") Signed-off-by: Mark Bloch Reviewed-by: Maor Gottlieb Link: https://lore.kernel.org/r/b4a88a871d651fa4e8f98d552553c1cfe9ba2cd6.1685960567.git.leon@kernel.org Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/fs.c | 276 ++++++++++++++++++++++++++++++++++- drivers/infiniband/hw/mlx5/fs.h | 16 ++ drivers/infiniband/hw/mlx5/mlx5_ib.h | 11 ++ 3 files changed, 296 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/fs.c b/drivers/infiniband/hw/mlx5/fs.c index 490ec308e309..5a13d902b064 100644 --- a/drivers/infiniband/hw/mlx5/fs.c +++ b/drivers/infiniband/hw/mlx5/fs.c @@ -696,8 +696,6 @@ static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_ib_dev *dev, struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_table *ft; - if (mlx5_ib_shared_ft_allowed(&dev->ib_dev)) - ft_attr.uid = MLX5_SHARED_RESOURCE_UID; ft_attr.prio = priority; ft_attr.max_fte = num_entries; ft_attr.flags = flags; @@ -2026,6 +2024,237 @@ static int flow_matcher_cleanup(struct ib_uobject *uobject, return 0; } +static int steering_anchor_create_ft(struct mlx5_ib_dev *dev, + struct mlx5_ib_flow_prio *ft_prio, + enum mlx5_flow_namespace_type ns_type) +{ + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5_flow_namespace *ns; + struct mlx5_flow_table *ft; + + if (ft_prio->anchor.ft) + return 0; + + ns = mlx5_get_flow_namespace(dev->mdev, ns_type); + if (!ns) + return -EOPNOTSUPP; + + ft_attr.flags = MLX5_FLOW_TABLE_UNMANAGED; + ft_attr.uid = MLX5_SHARED_RESOURCE_UID; + ft_attr.prio = 0; + ft_attr.max_fte = 2; + ft_attr.level = 1; + + ft = mlx5_create_flow_table(ns, &ft_attr); + if (IS_ERR(ft)) + return PTR_ERR(ft); + + ft_prio->anchor.ft = ft; + + return 0; +} + +static void steering_anchor_destroy_ft(struct mlx5_ib_flow_prio *ft_prio) +{ + if (ft_prio->anchor.ft) { + mlx5_destroy_flow_table(ft_prio->anchor.ft); + ft_prio->anchor.ft = NULL; + } +} + +static int +steering_anchor_create_fg_drop(struct mlx5_ib_flow_prio *ft_prio) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *fg; + void *flow_group_in; + int err = 0; + + if (ft_prio->anchor.fg_drop) + return 0; + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + return -ENOMEM; + + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1); + + fg = mlx5_create_flow_group(ft_prio->anchor.ft, flow_group_in); + if (IS_ERR(fg)) { + err = PTR_ERR(fg); + goto out; + } + + ft_prio->anchor.fg_drop = fg; + +out: + kvfree(flow_group_in); + + return err; +} + +static void +steering_anchor_destroy_fg_drop(struct mlx5_ib_flow_prio *ft_prio) +{ + if (ft_prio->anchor.fg_drop) { + mlx5_destroy_flow_group(ft_prio->anchor.fg_drop); + ft_prio->anchor.fg_drop = NULL; + } +} + +static int +steering_anchor_create_fg_goto_table(struct mlx5_ib_flow_prio *ft_prio) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *fg; + void *flow_group_in; + int err = 0; + + if (ft_prio->anchor.fg_goto_table) + return 0; + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + return -ENOMEM; + + fg = mlx5_create_flow_group(ft_prio->anchor.ft, flow_group_in); + if (IS_ERR(fg)) { + err = PTR_ERR(fg); + goto out; + } + ft_prio->anchor.fg_goto_table = fg; + +out: + kvfree(flow_group_in); + + return err; +} + +static void +steering_anchor_destroy_fg_goto_table(struct mlx5_ib_flow_prio *ft_prio) +{ + if (ft_prio->anchor.fg_goto_table) { + mlx5_destroy_flow_group(ft_prio->anchor.fg_goto_table); + ft_prio->anchor.fg_goto_table = NULL; + } +} + +static int +steering_anchor_create_rule_drop(struct mlx5_ib_flow_prio *ft_prio) +{ + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_handle *handle; + + if (ft_prio->anchor.rule_drop) + return 0; + + flow_act.fg = ft_prio->anchor.fg_drop; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP; + + handle = mlx5_add_flow_rules(ft_prio->anchor.ft, NULL, &flow_act, + NULL, 0); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + ft_prio->anchor.rule_drop = handle; + + return 0; +} + +static void steering_anchor_destroy_rule_drop(struct mlx5_ib_flow_prio *ft_prio) +{ + if (ft_prio->anchor.rule_drop) { + mlx5_del_flow_rules(ft_prio->anchor.rule_drop); + ft_prio->anchor.rule_drop = NULL; + } +} + +static int +steering_anchor_create_rule_goto_table(struct mlx5_ib_flow_prio *ft_prio) +{ + struct mlx5_flow_destination dest = {}; + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_handle *handle; + + if (ft_prio->anchor.rule_goto_table) + return 0; + + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + flow_act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL; + flow_act.fg = ft_prio->anchor.fg_goto_table; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = ft_prio->flow_table; + + handle = mlx5_add_flow_rules(ft_prio->anchor.ft, NULL, &flow_act, + &dest, 1); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + ft_prio->anchor.rule_goto_table = handle; + + return 0; +} + +static void +steering_anchor_destroy_rule_goto_table(struct mlx5_ib_flow_prio *ft_prio) +{ + if (ft_prio->anchor.rule_goto_table) { + mlx5_del_flow_rules(ft_prio->anchor.rule_goto_table); + ft_prio->anchor.rule_goto_table = NULL; + } +} + +static int steering_anchor_create_res(struct mlx5_ib_dev *dev, + struct mlx5_ib_flow_prio *ft_prio, + enum mlx5_flow_namespace_type ns_type) +{ + int err; + + err = steering_anchor_create_ft(dev, ft_prio, ns_type); + if (err) + return err; + + err = steering_anchor_create_fg_drop(ft_prio); + if (err) + goto destroy_ft; + + err = steering_anchor_create_fg_goto_table(ft_prio); + if (err) + goto destroy_fg_drop; + + err = steering_anchor_create_rule_drop(ft_prio); + if (err) + goto destroy_fg_goto_table; + + err = steering_anchor_create_rule_goto_table(ft_prio); + if (err) + goto destroy_rule_drop; + + return 0; + +destroy_rule_drop: + steering_anchor_destroy_rule_drop(ft_prio); +destroy_fg_goto_table: + steering_anchor_destroy_fg_goto_table(ft_prio); +destroy_fg_drop: + steering_anchor_destroy_fg_drop(ft_prio); +destroy_ft: + steering_anchor_destroy_ft(ft_prio); + + return err; +} + +static void mlx5_steering_anchor_destroy_res(struct mlx5_ib_flow_prio *ft_prio) +{ + steering_anchor_destroy_rule_goto_table(ft_prio); + steering_anchor_destroy_rule_drop(ft_prio); + steering_anchor_destroy_fg_goto_table(ft_prio); + steering_anchor_destroy_fg_drop(ft_prio); + steering_anchor_destroy_ft(ft_prio); +} + static int steering_anchor_cleanup(struct ib_uobject *uobject, enum rdma_remove_reason why, struct uverbs_attr_bundle *attrs) @@ -2036,6 +2265,9 @@ static int steering_anchor_cleanup(struct ib_uobject *uobject, return -EBUSY; mutex_lock(&obj->dev->flow_db->lock); + if (!--obj->ft_prio->anchor.rule_goto_table_ref) + steering_anchor_destroy_rule_goto_table(obj->ft_prio); + put_flow_table(obj->dev, obj->ft_prio, true); mutex_unlock(&obj->dev->flow_db->lock); @@ -2043,6 +2275,24 @@ static int steering_anchor_cleanup(struct ib_uobject *uobject, return 0; } +static void fs_cleanup_anchor(struct mlx5_ib_flow_prio *prio, + int count) +{ + while (count--) + mlx5_steering_anchor_destroy_res(&prio[count]); +} + +void mlx5_ib_fs_cleanup_anchor(struct mlx5_ib_dev *dev) +{ + fs_cleanup_anchor(dev->flow_db->prios, MLX5_IB_NUM_FLOW_FT); + fs_cleanup_anchor(dev->flow_db->egress_prios, MLX5_IB_NUM_FLOW_FT); + fs_cleanup_anchor(dev->flow_db->sniffer, MLX5_IB_NUM_SNIFFER_FTS); + fs_cleanup_anchor(dev->flow_db->egress, MLX5_IB_NUM_EGRESS_FTS); + fs_cleanup_anchor(dev->flow_db->fdb, MLX5_IB_NUM_FDB_FTS); + fs_cleanup_anchor(dev->flow_db->rdma_rx, MLX5_IB_NUM_FLOW_FT); + fs_cleanup_anchor(dev->flow_db->rdma_tx, MLX5_IB_NUM_FLOW_FT); +} + static int mlx5_ib_matcher_ns(struct uverbs_attr_bundle *attrs, struct mlx5_ib_flow_matcher *obj) { @@ -2183,21 +2433,31 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE)( return -ENOMEM; mutex_lock(&dev->flow_db->lock); + ft_prio = _get_flow_table(dev, priority, ns_type, 0); if (IS_ERR(ft_prio)) { - mutex_unlock(&dev->flow_db->lock); err = PTR_ERR(ft_prio); goto free_obj; } ft_prio->refcount++; - ft_id = mlx5_flow_table_id(ft_prio->flow_table); - mutex_unlock(&dev->flow_db->lock); + + if (!ft_prio->anchor.rule_goto_table_ref) { + err = steering_anchor_create_res(dev, ft_prio, ns_type); + if (err) + goto put_flow_table; + } + + ft_prio->anchor.rule_goto_table_ref++; + + ft_id = mlx5_flow_table_id(ft_prio->anchor.ft); err = uverbs_copy_to(attrs, MLX5_IB_ATTR_STEERING_ANCHOR_FT_ID, &ft_id, sizeof(ft_id)); if (err) - goto put_flow_table; + goto destroy_res; + + mutex_unlock(&dev->flow_db->lock); uobj->object = obj; obj->dev = dev; @@ -2206,8 +2466,10 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE)( return 0; +destroy_res: + --ft_prio->anchor.rule_goto_table_ref; + mlx5_steering_anchor_destroy_res(ft_prio); put_flow_table: - mutex_lock(&dev->flow_db->lock); put_flow_table(dev, ft_prio, true); mutex_unlock(&dev->flow_db->lock); free_obj: diff --git a/drivers/infiniband/hw/mlx5/fs.h b/drivers/infiniband/hw/mlx5/fs.h index ad320adaf321..b9734904f5f0 100644 --- a/drivers/infiniband/hw/mlx5/fs.h +++ b/drivers/infiniband/hw/mlx5/fs.h @@ -10,6 +10,7 @@ #if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS) int mlx5_ib_fs_init(struct mlx5_ib_dev *dev); +void mlx5_ib_fs_cleanup_anchor(struct mlx5_ib_dev *dev); #else static inline int mlx5_ib_fs_init(struct mlx5_ib_dev *dev) { @@ -21,9 +22,24 @@ static inline int mlx5_ib_fs_init(struct mlx5_ib_dev *dev) mutex_init(&dev->flow_db->lock); return 0; } + +inline void mlx5_ib_fs_cleanup_anchor(struct mlx5_ib_dev *dev) {} #endif + static inline void mlx5_ib_fs_cleanup(struct mlx5_ib_dev *dev) { + /* When a steering anchor is created, a special flow table is also + * created for the user to reference. Since the user can reference it, + * the kernel cannot trust that when the user destroys the steering + * anchor, they no longer reference the flow table. + * + * To address this issue, when a user destroys a steering anchor, only + * the flow steering rule in the table is destroyed, but the table + * itself is kept to deal with the above scenario. The remaining + * resources are only removed when the RDMA device is destroyed, which + * is a safe assumption that all references are gone. + */ + mlx5_ib_fs_cleanup_anchor(dev); kfree(dev->flow_db); } #endif /* _MLX5_IB_FS_H */ diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 4a7f7064bd0e..038522bb7113 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -233,8 +233,19 @@ enum { #define MLX5_IB_NUM_SNIFFER_FTS 2 #define MLX5_IB_NUM_EGRESS_FTS 1 #define MLX5_IB_NUM_FDB_FTS MLX5_BY_PASS_NUM_REGULAR_PRIOS + +struct mlx5_ib_anchor { + struct mlx5_flow_table *ft; + struct mlx5_flow_group *fg_goto_table; + struct mlx5_flow_group *fg_drop; + struct mlx5_flow_handle *rule_goto_table; + struct mlx5_flow_handle *rule_drop; + unsigned int rule_goto_table_ref; +}; + struct mlx5_ib_flow_prio { struct mlx5_flow_table *flow_table; + struct mlx5_ib_anchor anchor; unsigned int refcount; }; -- cgit 1.4.1 From 4dc0b367c3908472bf96e088d7eb4f4f8d092da7 Mon Sep 17 00:00:00 2001 From: Mark Zhang Date: Mon, 5 Jun 2023 13:33:23 +0300 Subject: RDMA/cma: Always set static rate to 0 for RoCE [ Upstream commit 58030c76cce473b6cfd630bbecb97215def0dff8 ] Set static rate to 0 as it should be discovered by path query and has no meaning for RoCE. This also avoid of using the rtnl lock and ethtool API, which is a bottleneck when try to setup many rdma-cm connections at the same time, especially with multiple processes. Fixes: 3c86aa70bf67 ("RDMA/cm: Add RDMA CM support for IBoE devices") Signed-off-by: Mark Zhang Link: https://lore.kernel.org/r/f72a4f8b667b803aee9fa794069f61afb5839ce4.1685960567.git.leon@kernel.org Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/core/cma.c | 4 ++-- include/rdma/ib_addr.h | 23 ----------------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index c6a671edba5c..4632b1833381 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -3293,7 +3293,7 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv) route->path_rec->traffic_class = tos; route->path_rec->mtu = iboe_get_mtu(ndev->mtu); route->path_rec->rate_selector = IB_SA_EQ; - route->path_rec->rate = iboe_get_rate(ndev); + route->path_rec->rate = IB_RATE_PORT_CURRENT; dev_put(ndev); route->path_rec->packet_life_time_selector = IB_SA_EQ; /* In case ACK timeout is set, use this value to calculate @@ -4955,7 +4955,7 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv, if (!ndev) return -ENODEV; - ib.rec.rate = iboe_get_rate(ndev); + ib.rec.rate = IB_RATE_PORT_CURRENT; ib.rec.hop_limit = 1; ib.rec.mtu = iboe_get_mtu(ndev->mtu); diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h index d808dc3d239e..811a0f11d0db 100644 --- a/include/rdma/ib_addr.h +++ b/include/rdma/ib_addr.h @@ -194,29 +194,6 @@ static inline enum ib_mtu iboe_get_mtu(int mtu) return 0; } -static inline int iboe_get_rate(struct net_device *dev) -{ - struct ethtool_link_ksettings cmd; - int err; - - rtnl_lock(); - err = __ethtool_get_link_ksettings(dev, &cmd); - rtnl_unlock(); - if (err) - return IB_RATE_PORT_CURRENT; - - if (cmd.base.speed >= 40000) - return IB_RATE_40_GBPS; - else if (cmd.base.speed >= 30000) - return IB_RATE_30_GBPS; - else if (cmd.base.speed >= 20000) - return IB_RATE_20_GBPS; - else if (cmd.base.speed >= 10000) - return IB_RATE_10_GBPS; - else - return IB_RATE_PORT_CURRENT; -} - static inline int rdma_link_local_addr(struct in6_addr *addr) { if (addr->s6_addr32[0] == htonl(0xfe800000) && -- cgit 1.4.1 From 8618f8f72389b46ed976e3fe15badf03c1a0efab Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Mon, 5 Jun 2023 13:33:25 +0300 Subject: IB/uverbs: Fix to consider event queue closing also upon non-blocking mode [ Upstream commit 62fab312fa1683e812e605db20d4f22de3e3fb2f ] Fix ib_uverbs_event_read() to consider event queue closing also upon non-blocking mode. Once the queue is closed (e.g. hot-plug flow) all the existing events are cleaned-up as part of ib_uverbs_free_event_queue(). An application that uses the non-blocking FD mode should get -EIO in that case to let it knows that the device was removed already. Otherwise, it can loose the indication that the device was removed and won't recover. As part of that, refactor the code to have a single flow with regards to 'is_closed' for both blocking and non-blocking modes. Fixes: 14e23bd6d221 ("RDMA/core: Fix locking in ib_uverbs_event_read") Reviewed-by: Maor Gottlieb Signed-off-by: Yishai Hadas Link: https://lore.kernel.org/r/97b00116a1e1e13f8dc4ec38a5ea81cf8c030210.1685960567.git.leon@kernel.org Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/core/uverbs_main.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index d54434088727..fa937cd26821 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -222,8 +222,12 @@ static ssize_t ib_uverbs_event_read(struct ib_uverbs_event_queue *ev_queue, spin_lock_irq(&ev_queue->lock); while (list_empty(&ev_queue->event_list)) { - spin_unlock_irq(&ev_queue->lock); + if (ev_queue->is_closed) { + spin_unlock_irq(&ev_queue->lock); + return -EIO; + } + spin_unlock_irq(&ev_queue->lock); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; @@ -233,12 +237,6 @@ static ssize_t ib_uverbs_event_read(struct ib_uverbs_event_queue *ev_queue, return -ERESTARTSYS; spin_lock_irq(&ev_queue->lock); - - /* If device was disassociated and no event exists set an error */ - if (list_empty(&ev_queue->event_list) && ev_queue->is_closed) { - spin_unlock_irq(&ev_queue->lock); - return -EIO; - } } event = list_entry(ev_queue->event_list.next, struct ib_uverbs_event, list); -- cgit 1.4.1 From 1def2a94f4ee38e8442b4b1e6d8354bf61cb6bb0 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Mon, 5 Jun 2023 13:33:26 +0300 Subject: RDMA/mlx5: Fix affinity assignment [ Upstream commit 617f5db1a626f18d5cbb7c7faf7bf8f9ea12be78 ] The cited commit aimed to ensure that Virtual Functions (VFs) assign a queue affinity to a Queue Pair (QP) to distribute traffic when the LAG master creates a hardware LAG. If the affinity was set while the hardware was not in LAG, the firmware would ignore the affinity value. However, this commit unintentionally assigned an affinity to QPs on the LAG master's VPORT even if the RDMA device was not marked as LAG-enabled. In most cases, this was not an issue because when the hardware entered hardware LAG configuration, the RDMA device of the LAG master would be destroyed and a new one would be created, marked as LAG-enabled. The problem arises when a user configures Equal-Cost Multipath (ECMP). In ECMP mode, traffic can be directed to different physical ports based on the queue affinity, which is intended for use by VPORTS other than the E-Switch manager. ECMP mode is supported only if both E-Switch managers are in switchdev mode and the appropriate route is configured via IP. In this configuration, the RDMA device is not destroyed, and we retain the RDMA device that is not marked as LAG-enabled. To ensure correct behavior, Send Queues (SQs) opened by the E-Switch manager through verbs should be assigned strict affinity. This means they will only be able to communicate through the native physical port associated with the E-Switch manager. This will prevent the firmware from assigning affinity and will not allow the SQs to be remapped in case of failover. Fixes: 802dcc7fc5ec ("RDMA/mlx5: Support TX port affinity for VF drivers in LAG mode") Reviewed-by: Maor Gottlieb Signed-off-by: Mark Bloch Link: https://lore.kernel.org/r/425b05f4da840bc684b0f7e8ebf61aeb5cef09b0.1685960567.git.leon@kernel.org Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 3 +++ drivers/infiniband/hw/mlx5/qp.c | 3 +++ drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h | 12 ------------ include/linux/mlx5/driver.h | 12 ++++++++++++ 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 038522bb7113..8d94e6834e01 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -1564,6 +1564,9 @@ static inline bool mlx5_ib_lag_should_assign_affinity(struct mlx5_ib_dev *dev) MLX5_CAP_PORT_SELECTION(dev->mdev, port_select_flow_table_bypass)) return 0; + if (mlx5_lag_is_lacp_owner(dev->mdev) && !dev->lag_active) + return 0; + return dev->lag_active || (MLX5_CAP_GEN(dev->mdev, num_lag_ports) > 1 && MLX5_CAP_GEN(dev->mdev, lag_tx_port_affinity)); diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index f7d3643b08f5..ac53ed79ca64 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1156,6 +1156,9 @@ static int create_raw_packet_qp_tis(struct mlx5_ib_dev *dev, MLX5_SET(create_tis_in, in, uid, to_mpd(pd)->uid); MLX5_SET(tisc, tisc, transport_domain, tdn); + if (!mlx5_ib_lag_should_assign_affinity(dev) && + mlx5_lag_is_lacp_owner(dev->mdev)) + MLX5_SET(tisc, tisc, strict_lag_tx_port_affinity, 1); if (qp->flags & IB_QP_CREATE_SOURCE_QPN) MLX5_SET(tisc, tisc, underlay_qpn, qp->underlay_qpn); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 1a35b3c2a367..0b560e97a356 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -275,18 +275,6 @@ static inline bool mlx5_sriov_is_enabled(struct mlx5_core_dev *dev) return pci_num_vf(dev->pdev) ? true : false; } -static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev) -{ - /* LACP owner conditions: - * 1) Function is physical. - * 2) LAG is supported by FW. - * 3) LAG is managed by driver (currently the only option). - */ - return MLX5_CAP_GEN(dev, vport_group_manager) && - (MLX5_CAP_GEN(dev, num_lag_ports) > 1) && - MLX5_CAP_GEN(dev, lag_master); -} - int mlx5_rescan_drivers_locked(struct mlx5_core_dev *dev); static inline int mlx5_rescan_drivers(struct mlx5_core_dev *dev) { diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index fff61e6d6d4d..3660ce6a9349 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -1230,6 +1230,18 @@ static inline u16 mlx5_core_max_vfs(const struct mlx5_core_dev *dev) return dev->priv.sriov.max_vfs; } +static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev) +{ + /* LACP owner conditions: + * 1) Function is physical. + * 2) LAG is supported by FW. + * 3) LAG is managed by driver (currently the only option). + */ + return MLX5_CAP_GEN(dev, vport_group_manager) && + (MLX5_CAP_GEN(dev, num_lag_ports) > 1) && + MLX5_CAP_GEN(dev, lag_master); +} + static inline int mlx5_get_gid_table_len(u16 param) { if (param > 4) { -- cgit 1.4.1 From 4e55c9abe94765620a61149a7d18c5e3444b8187 Mon Sep 17 00:00:00 2001 From: Saravanan Vajravel Date: Tue, 6 Jun 2023 03:25:29 -0700 Subject: IB/isert: Fix dead lock in ib_isert [ Upstream commit 691b0480933f0ce88a81ed1d1a0aff340ff6293a ] - When a iSER session is released, ib_isert module is taking a mutex lock and releasing all pending connections. As part of this, ib_isert is destroying rdma cm_id. To destroy cm_id, rdma_cm module is sending CM events to CMA handler of ib_isert. This handler is taking same mutex lock. Hence it leads to deadlock between ib_isert & rdma_cm modules. - For fix, created local list of pending connections and release the connection outside of mutex lock. Calltrace: --------- [ 1229.791410] INFO: task kworker/10:1:642 blocked for more than 120 seconds. [ 1229.791416] Tainted: G OE --------- - - 4.18.0-372.9.1.el8.x86_64 #1 [ 1229.791418] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 1229.791419] task:kworker/10:1 state:D stack: 0 pid: 642 ppid: 2 flags:0x80004000 [ 1229.791424] Workqueue: ib_cm cm_work_handler [ib_cm] [ 1229.791436] Call Trace: [ 1229.791438] __schedule+0x2d1/0x830 [ 1229.791445] ? select_idle_sibling+0x23/0x6f0 [ 1229.791449] schedule+0x35/0xa0 [ 1229.791451] schedule_preempt_disabled+0xa/0x10 [ 1229.791453] __mutex_lock.isra.7+0x310/0x420 [ 1229.791456] ? select_task_rq_fair+0x351/0x990 [ 1229.791459] isert_cma_handler+0x224/0x330 [ib_isert] [ 1229.791463] ? ttwu_queue_wakelist+0x159/0x170 [ 1229.791466] cma_cm_event_handler+0x25/0xd0 [rdma_cm] [ 1229.791474] cma_ib_handler+0xa7/0x2e0 [rdma_cm] [ 1229.791478] cm_process_work+0x22/0xf0 [ib_cm] [ 1229.791483] cm_work_handler+0xf4/0xf30 [ib_cm] [ 1229.791487] ? move_linked_works+0x6e/0xa0 [ 1229.791490] process_one_work+0x1a7/0x360 [ 1229.791491] ? create_worker+0x1a0/0x1a0 [ 1229.791493] worker_thread+0x30/0x390 [ 1229.791494] ? create_worker+0x1a0/0x1a0 [ 1229.791495] kthread+0x10a/0x120 [ 1229.791497] ? set_kthread_struct+0x40/0x40 [ 1229.791499] ret_from_fork+0x1f/0x40 [ 1229.791739] INFO: task targetcli:28666 blocked for more than 120 seconds. [ 1229.791740] Tainted: G OE --------- - - 4.18.0-372.9.1.el8.x86_64 #1 [ 1229.791741] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 1229.791742] task:targetcli state:D stack: 0 pid:28666 ppid: 5510 flags:0x00004080 [ 1229.791743] Call Trace: [ 1229.791744] __schedule+0x2d1/0x830 [ 1229.791746] schedule+0x35/0xa0 [ 1229.791748] schedule_preempt_disabled+0xa/0x10 [ 1229.791749] __mutex_lock.isra.7+0x310/0x420 [ 1229.791751] rdma_destroy_id+0x15/0x20 [rdma_cm] [ 1229.791755] isert_connect_release+0x115/0x130 [ib_isert] [ 1229.791757] isert_free_np+0x87/0x140 [ib_isert] [ 1229.791761] iscsit_del_np+0x74/0x120 [iscsi_target_mod] [ 1229.791776] lio_target_np_driver_store+0xe9/0x140 [iscsi_target_mod] [ 1229.791784] configfs_write_file+0xb2/0x110 [ 1229.791788] vfs_write+0xa5/0x1a0 [ 1229.791792] ksys_write+0x4f/0xb0 [ 1229.791794] do_syscall_64+0x5b/0x1a0 [ 1229.791798] entry_SYSCALL_64_after_hwframe+0x65/0xca Fixes: bd3792205aae ("iser-target: Fix pending connections handling in target stack shutdown sequnce") Reviewed-by: Sagi Grimberg Signed-off-by: Selvin Xavier Signed-off-by: Saravanan Vajravel Link: https://lore.kernel.org/r/20230606102531.162967-2-saravanan.vajravel@broadcom.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/isert/ib_isert.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 7cca171478a2..50d7373b425c 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -2432,6 +2432,7 @@ isert_free_np(struct iscsi_np *np) { struct isert_np *isert_np = np->np_context; struct isert_conn *isert_conn, *n; + LIST_HEAD(drop_conn_list); if (isert_np->cm_id) rdma_destroy_id(isert_np->cm_id); @@ -2451,7 +2452,7 @@ isert_free_np(struct iscsi_np *np) node) { isert_info("cleaning isert_conn %p state (%d)\n", isert_conn, isert_conn->state); - isert_connect_release(isert_conn); + list_move_tail(&isert_conn->node, &drop_conn_list); } } @@ -2462,11 +2463,16 @@ isert_free_np(struct iscsi_np *np) node) { isert_info("cleaning isert_conn %p state (%d)\n", isert_conn, isert_conn->state); - isert_connect_release(isert_conn); + list_move_tail(&isert_conn->node, &drop_conn_list); } } mutex_unlock(&isert_np->mutex); + list_for_each_entry_safe(isert_conn, n, &drop_conn_list, node) { + list_del_init(&isert_conn->node); + isert_connect_release(isert_conn); + } + np->np_context = NULL; kfree(isert_np); } -- cgit 1.4.1 From f77965f48792113ae4f92276bee4cf41ddf2954e Mon Sep 17 00:00:00 2001 From: Saravanan Vajravel Date: Tue, 6 Jun 2023 03:25:30 -0700 Subject: IB/isert: Fix possible list corruption in CMA handler [ Upstream commit 7651e2d6c5b359a28c2d4c904fec6608d1021ca8 ] When ib_isert module receives connection error event, it is releasing the isert session and removes corresponding list node but it doesn't take appropriate mutex lock to remove the list node. This can lead to linked list corruption Fixes: bd3792205aae ("iser-target: Fix pending connections handling in target stack shutdown sequnce") Signed-off-by: Selvin Xavier Signed-off-by: Saravanan Vajravel Link: https://lore.kernel.org/r/20230606102531.162967-3-saravanan.vajravel@broadcom.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/isert/ib_isert.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 50d7373b425c..a02a3caeaa4e 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -657,9 +657,13 @@ static int isert_connect_error(struct rdma_cm_id *cma_id) { struct isert_conn *isert_conn = cma_id->qp->qp_context; + struct isert_np *isert_np = cma_id->context; ib_drain_qp(isert_conn->qp); + + mutex_lock(&isert_np->mutex); list_del_init(&isert_conn->node); + mutex_unlock(&isert_np->mutex); isert_conn->cm_id = NULL; isert_put_conn(isert_conn); -- cgit 1.4.1 From 277fbf63b34a377c800d25c7cfd8231ba19cffe2 Mon Sep 17 00:00:00 2001 From: Saravanan Vajravel Date: Tue, 6 Jun 2023 03:25:31 -0700 Subject: IB/isert: Fix incorrect release of isert connection [ Upstream commit 699826f4e30ab76a62c238c86fbef7e826639c8d ] The ib_isert module is releasing the isert connection both in isert_wait_conn() handler as well as isert_free_conn() handler. In isert_wait_conn() handler, it is expected to wait for iSCSI session logout operation to complete. It should free the isert connection only in isert_free_conn() handler. When a bunch of iSER target is cleared, this issue can lead to use-after-free memory issue as isert conn is twice released Fixes: b02efbfc9a05 ("iser-target: Fix implicit termination of connections") Reviewed-by: Sagi Grimberg Signed-off-by: Saravanan Vajravel Signed-off-by: Selvin Xavier Link: https://lore.kernel.org/r/20230606102531.162967-4-saravanan.vajravel@broadcom.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/isert/ib_isert.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index a02a3caeaa4e..a7fef3ea77fe 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -2571,8 +2571,6 @@ static void isert_wait_conn(struct iscsit_conn *conn) isert_put_unsol_pending_cmds(conn); isert_wait4cmds(conn); isert_wait4logout(isert_conn); - - queue_work(isert_release_wq, &isert_conn->release_work); } static void isert_free_conn(struct iscsit_conn *conn) -- cgit 1.4.1 From 33bd6b76ac772e87713bcce2c6352190762996bf Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 8 Jun 2023 09:23:44 -0700 Subject: net: ethtool: correct MAX attribute value for stats [ Upstream commit 52f79609c0c5b25fddb88e85f25ce08aa7e3fb42 ] When compiling YNL generated code compiler complains about array-initializer-out-of-bounds. Turns out the MAX value for STATS_GRP uses the value for STATS. This may lead to random corruptions in user space (kernel itself doesn't use this value as it never parses stats). Fixes: f09ea6fb1272 ("ethtool: add a new command for reading standard stats") Signed-off-by: Jakub Kicinski Reviewed-by: David Ahern Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/uapi/linux/ethtool_netlink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index bb57084ac524..69f5bec347c2 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -761,7 +761,7 @@ enum { /* add new constants above here */ __ETHTOOL_A_STATS_GRP_CNT, - ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_CNT - 1) + ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_GRP_CNT - 1) }; enum { -- cgit 1.4.1 From 0b8ae7d6e4ad30477d22e7d023c860e90fb71dbb Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Fri, 9 Jun 2023 17:15:02 +0800 Subject: ipvlan: fix bound dev checking for IPv6 l3s mode [ Upstream commit ce57adc222aba32431c42632b396e9213d0eb0b8 ] The commit 59a0b022aa24 ("ipvlan: Make skb->skb_iif track skb->dev for l3s mode") fixed ipvlan bonded dev checking by updating skb skb_iif. This fix works for IPv4, as in raw_v4_input() the dif is from inet_iif(skb), which is skb->skb_iif when there is no route. But for IPv6, the fix is not enough, because in ipv6_raw_deliver() -> raw_v6_match(), the dif is inet6_iif(skb), which is returns IP6CB(skb)->iif instead of skb->skb_iif if it's not a l3_slave. To fix the IPv6 part issue. Let's set IP6CB(skb)->iif to correct ifindex. BTW, ipvlan handles NS/NA specifically. Since it works fine, I will not reset IP6CB(skb)->iif when addr->atype is IPVL_ICMPV6. Fixes: c675e06a98a4 ("ipvlan: decouple l3s mode dependencies from other modes") Link: https://bugzilla.redhat.com/show_bug.cgi?id=2196710 Signed-off-by: Hangbin Liu Reviewed-by: Larysa Zaremba Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ipvlan/ipvlan_l3s.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ipvlan/ipvlan_l3s.c b/drivers/net/ipvlan/ipvlan_l3s.c index 71712ea25403..d5b05e803219 100644 --- a/drivers/net/ipvlan/ipvlan_l3s.c +++ b/drivers/net/ipvlan/ipvlan_l3s.c @@ -102,6 +102,10 @@ static unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb, skb->dev = addr->master->dev; skb->skb_iif = skb->dev->ifindex; +#if IS_ENABLED(CONFIG_IPV6) + if (addr->atype == IPVL_IPV6) + IP6CB(skb)->iif = skb->dev->ifindex; +#endif len = skb->len + ETH_HLEN; ipvlan_count_rx(addr->master, len, true, false); out: -- cgit 1.4.1 From fe289f8fee9a7edd5d148ecb7ad11aa22ae53d2c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 9 Jun 2023 14:05:19 +0300 Subject: sctp: fix an error code in sctp_sf_eat_auth() [ Upstream commit 75e6def3b26736e7ff80639810098c9074229737 ] The sctp_sf_eat_auth() function is supposed to enum sctp_disposition values and returning a kernel error code will cause issues in the caller. Change -ENOMEM to SCTP_DISPOSITION_NOMEM. Fixes: 65b07e5d0d09 ("[SCTP]: API updates to suport SCTP-AUTH extensions.") Signed-off-by: Dan Carpenter Acked-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sctp/sm_statefuns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index f6ee7f4040c1..5383b6a9da61 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -4484,7 +4484,7 @@ enum sctp_disposition sctp_sf_eat_auth(struct net *net, SCTP_AUTH_NEW_KEY, GFP_ATOMIC); if (!ev) - return -ENOMEM; + return SCTP_DISPOSITION_NOMEM; sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); -- cgit 1.4.1 From c6612bf33ebe348c84abc98635e38fe9fbbd4f08 Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Mon, 15 May 2023 23:49:36 +0800 Subject: igc: Clean the TX buffer and TX descriptor ring [ Upstream commit e43516f5978d11d36511ce63d31d1da4db916510 ] There could be a race condition during link down where interrupt being generated and igc_clean_tx_irq() been called to perform the TX completion. Properly clear the TX buffer/descriptor ring and disable the TX Queue ring in igc_free_tx_resources() to avoid that. Kernel trace: [ 108.237177] Hardware name: Intel Corporation Tiger Lake Client Platform/TigerLake U DDR4 SODIMM RVP, BIOS TGLIFUI1.R00.4204.A00.2105270302 05/27/2021 [ 108.237178] RIP: 0010:refcount_warn_saturate+0x55/0x110 [ 108.242143] RSP: 0018:ffff9e7980003db0 EFLAGS: 00010286 [ 108.245555] Code: 84 bc 00 00 00 c3 cc cc cc cc 85 f6 74 46 80 3d 20 8c 4d 01 00 75 ee 48 c7 c7 88 f4 03 ab c6 05 10 8c 4d 01 01 e8 0b 10 96 ff <0f> 0b c3 cc cc cc cc 80 3d fc 8b 4d 01 00 75 cb 48 c7 c7 b0 f4 03 [ 108.250434] [ 108.250434] RSP: 0018:ffff9e798125f910 EFLAGS: 00010286 [ 108.254358] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 [ 108.259325] [ 108.259325] RAX: 0000000000000000 RBX: ffff8ddb935b8000 RCX: 0000000000000027 [ 108.261868] RDX: ffff8de250a28800 RSI: ffff8de250a1c580 RDI: ffff8de250a1c580 [ 108.265538] RDX: 0000000000000027 RSI: 0000000000000002 RDI: ffff8de250a9c588 [ 108.265539] RBP: ffff8ddb935b8000 R08: ffffffffab2655a0 R09: ffff9e798125f898 [ 108.267914] RBP: ffff8ddb8a5b8d80 R08: 0000005648eba354 R09: 0000000000000000 [ 108.270196] R10: 0000000000000001 R11: 000000002d2d2d2d R12: ffff9e798125f948 [ 108.270197] R13: ffff9e798125fa1c R14: ffff8ddb8a5b8d80 R15: 7fffffffffffffff [ 108.273001] R10: 000000002d2d2d2d R11: 000000002d2d2d2d R12: ffff8ddb8a5b8ed4 [ 108.276410] FS: 00007f605851b740(0000) GS:ffff8de250a80000(0000) knlGS:0000000000000000 [ 108.280597] R13: 00000000000002ac R14: 00000000ffffff99 R15: ffff8ddb92561b80 [ 108.282966] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 108.282967] CR2: 00007f053c039248 CR3: 0000000185850003 CR4: 0000000000f70ee0 [ 108.286206] FS: 0000000000000000(0000) GS:ffff8de250a00000(0000) knlGS:0000000000000000 [ 108.289701] PKRU: 55555554 [ 108.289702] Call Trace: [ 108.289704] [ 108.293977] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 108.297562] sock_alloc_send_pskb+0x20c/0x240 [ 108.301494] CR2: 00007f053c03a168 CR3: 0000000184394002 CR4: 0000000000f70ef0 [ 108.301495] PKRU: 55555554 [ 108.306464] __ip_append_data.isra.0+0x96f/0x1040 [ 108.309441] Call Trace: [ 108.309443] ? __pfx_ip_generic_getfrag+0x10/0x10 [ 108.314927] [ 108.314928] sock_wfree+0x1c7/0x1d0 [ 108.318078] ? __pfx_ip_generic_getfrag+0x10/0x10 [ 108.320276] skb_release_head_state+0x32/0x90 [ 108.324812] ip_make_skb+0xf6/0x130 [ 108.327188] skb_release_all+0x16/0x40 [ 108.330775] ? udp_sendmsg+0x9f3/0xcb0 [ 108.332626] napi_consume_skb+0x48/0xf0 [ 108.334134] ? xfrm_lookup_route+0x23/0xb0 [ 108.344285] igc_poll+0x787/0x1620 [igc] [ 108.346659] udp_sendmsg+0x9f3/0xcb0 [ 108.360010] ? ttwu_do_activate+0x40/0x220 [ 108.365237] ? __pfx_ip_generic_getfrag+0x10/0x10 [ 108.366744] ? try_to_wake_up+0x289/0x5e0 [ 108.376987] ? sock_sendmsg+0x81/0x90 [ 108.395698] ? __pfx_process_timeout+0x10/0x10 [ 108.395701] sock_sendmsg+0x81/0x90 [ 108.409052] __napi_poll+0x29/0x1c0 [ 108.414279] ____sys_sendmsg+0x284/0x310 [ 108.419507] net_rx_action+0x257/0x2d0 [ 108.438216] ___sys_sendmsg+0x7c/0xc0 [ 108.439723] __do_softirq+0xc1/0x2a8 [ 108.444950] ? finish_task_switch+0xb4/0x2f0 [ 108.452077] irq_exit_rcu+0xa9/0xd0 [ 108.453584] ? __schedule+0x372/0xd00 [ 108.460713] common_interrupt+0x84/0xa0 [ 108.467840] ? clockevents_program_event+0x95/0x100 [ 108.474968] [ 108.482096] ? do_nanosleep+0x88/0x130 [ 108.489224] [ 108.489225] asm_common_interrupt+0x26/0x40 [ 108.496353] ? __rseq_handle_notify_resume+0xa9/0x4f0 [ 108.503478] RIP: 0010:cpu_idle_poll+0x2c/0x100 [ 108.510607] __sys_sendmsg+0x5d/0xb0 [ 108.518687] Code: 05 e1 d9 c8 00 65 8b 15 de 64 85 55 85 c0 7f 57 e8 b9 ef ff ff fb 65 48 8b 1c 25 00 cc 02 00 48 8b 03 a8 08 74 0b eb 1c f3 90 <48> 8b 03 a8 08 75 13 8b 05 77 63 cd 00 85 c0 75 ed e8 ce ec ff ff [ 108.525817] do_syscall_64+0x44/0xa0 [ 108.531563] RSP: 0018:ffffffffab203e70 EFLAGS: 00000202 [ 108.538693] entry_SYSCALL_64_after_hwframe+0x72/0xdc [ 108.546775] [ 108.546777] RIP: 0033:0x7f605862b7f7 [ 108.549495] RAX: 0000000000000001 RBX: ffffffffab20c940 RCX: 000000000000003b [ 108.551955] Code: 0e 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b9 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 2e 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 89 54 24 1c 48 89 74 24 10 [ 108.554068] RDX: 4000000000000000 RSI: 000000002da97f6a RDI: 00000000002b8ff4 [ 108.559816] RSP: 002b:00007ffc99264058 EFLAGS: 00000246 [ 108.564178] RBP: 0000000000000000 R08: 00000000002b8ff4 R09: ffff8ddb01554c80 [ 108.571302] ORIG_RAX: 000000000000002e [ 108.571303] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f605862b7f7 [ 108.574023] R10: 000000000000015b R11: 000000000000000f R12: ffffffffab20c940 [ 108.574024] R13: 0000000000000000 R14: ffff8de26fbeef40 R15: ffffffffab20c940 [ 108.578727] RDX: 0000000000000000 RSI: 00007ffc992640a0 RDI: 0000000000000003 [ 108.578728] RBP: 00007ffc99264110 R08: 0000000000000000 R09: 175f48ad1c3a9c00 [ 108.581187] do_idle+0x62/0x230 [ 108.585890] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffc992642d8 [ 108.585891] R13: 00005577814ab2ba R14: 00005577814addf0 R15: 00007f605876d000 [ 108.587920] cpu_startup_entry+0x1d/0x20 [ 108.591422] [ 108.596127] rest_init+0xc5/0xd0 [ 108.600490] ---[ end trace 0000000000000000 ]--- Test Setup: DUT: - Change mac address on DUT Side. Ensure NIC not having same MAC Address - Running udp_tai on DUT side. Let udp_tai running throughout the test Example: ./udp_tai -i enp170s0 -P 100000 -p 90 -c 1 -t 0 -u 30004 Host: - Perform link up/down every 5 second. Result: Kernel panic will happen on DUT Side. Fixes: 13b5b7fd6a4a ("igc: Add support for Tx/Rx rings") Signed-off-by: Muhammad Husaini Zulkifli Tested-by: Naama Meir Reviewed-by: Maciej Fijalkowski Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igc/igc_main.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 1d9b70e0ff67..e5cb76cf2c3f 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -255,6 +255,13 @@ static void igc_clean_tx_ring(struct igc_ring *tx_ring) /* reset BQL for queue */ netdev_tx_reset_queue(txring_txq(tx_ring)); + /* Zero out the buffer ring */ + memset(tx_ring->tx_buffer_info, 0, + sizeof(*tx_ring->tx_buffer_info) * tx_ring->count); + + /* Zero out the descriptor ring */ + memset(tx_ring->desc, 0, tx_ring->size); + /* reset next_to_use and next_to_clean */ tx_ring->next_to_use = 0; tx_ring->next_to_clean = 0; @@ -268,7 +275,7 @@ static void igc_clean_tx_ring(struct igc_ring *tx_ring) */ void igc_free_tx_resources(struct igc_ring *tx_ring) { - igc_clean_tx_ring(tx_ring); + igc_disable_tx_ring(tx_ring); vfree(tx_ring->tx_buffer_info); tx_ring->tx_buffer_info = NULL; -- cgit 1.4.1 From 9710e5c30bd4fa6d8ff4ccdc05dd7715890ed276 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 17 Apr 2023 15:18:39 -0700 Subject: igc: Fix possible system crash when loading module [ Upstream commit c080fe262f9e73a00934b70c16b1479cf40cd2bd ] Guarantee that when probe() is run again, PTM and PCI busmaster will be in the same state as it was if the driver was never loaded. Avoid an i225/i226 hardware issue that PTM requests can be made even though PCI bus mastering is not enabled. These unexpected PTM requests can crash some systems. So, "force" disable PTM and busmastering before removing the driver, so they can be re-enabled in the right order during probe(). This is more like a workaround and should be applicable for i225 and i226, in any platform. Fixes: 1b5d73fb8624 ("igc: Enable PCIe PTM") Signed-off-by: Vinicius Costa Gomes Reviewed-by: Muhammad Husaini Zulkifli Tested-by: Naama Meir Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igc/igc_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index e5cb76cf2c3f..3509974c1f8e 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -6685,6 +6685,9 @@ static void igc_remove(struct pci_dev *pdev) igc_ptp_stop(adapter); + pci_disable_ptm(pdev); + pci_clear_master(pdev); + set_bit(__IGC_DOWN, &adapter->state); del_timer_sync(&adapter->watchdog_timer); -- cgit 1.4.1 From df7477a8bdcbc99b4d58759fc75c0ce21e53d0b3 Mon Sep 17 00:00:00 2001 From: Aleksandr Loktionov Date: Tue, 25 Apr 2023 17:44:14 +0200 Subject: igb: fix nvm.ops.read() error handling [ Upstream commit 48a821fd58837800750ec1b3962f0f799630a844 ] Add error handling into igb_set_eeprom() function, in case nvm.ops.read() fails just quit with error code asap. Fixes: 9d5c824399de ("igb: PCI-Express 82575 Gigabit Ethernet driver") Signed-off-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index ff911af16a4b..96fa1c420f91 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -822,6 +822,8 @@ static int igb_set_eeprom(struct net_device *netdev, */ ret_val = hw->nvm.ops.read(hw, last_word, 1, &eeprom_buff[last_word - first_word]); + if (ret_val) + goto out; } /* Device's eeprom is always little-endian, word addressable */ @@ -841,6 +843,7 @@ static int igb_set_eeprom(struct net_device *netdev, hw->nvm.ops.update(hw); igb_set_fw_version(adapter); +out: kfree(eeprom_buff); return ret_val; } -- cgit 1.4.1 From fabf9cb41334705fcce26aee7f96ba4d70b9fd0f Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Fri, 9 Jun 2023 10:03:04 +0200 Subject: net: phylink: report correct max speed for QUSGMII [ Upstream commit b9dc1046edfeb7d9dbc2272c8d9ad5a8c47f3199 ] Q-USGMII is the quad port version of USGMII, and supports a max speed of 1Gbps on each line. Make so that phylink_interface_max_speed() reports this information correctly. Fixes: ae0e4bb2a0e0 ("net: phylink: Adjust link settings based on rate matching") Signed-off-by: Maxime Chevallier Reviewed-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/phylink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 4073e8243df3..acd3405ddc9c 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -188,6 +188,7 @@ static int phylink_interface_max_speed(phy_interface_t interface) case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_GMII: return SPEED_1000; @@ -204,7 +205,6 @@ static int phylink_interface_max_speed(phy_interface_t interface) case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_10GKR: case PHY_INTERFACE_MODE_USXGMII: - case PHY_INTERFACE_MODE_QUSGMII: return SPEED_10000; case PHY_INTERFACE_MODE_25GBASER: -- cgit 1.4.1 From 835457c0d608b388d69d26efbe3b8e4ceeb65e62 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Fri, 9 Jun 2023 10:03:05 +0200 Subject: net: phylink: use a dedicated helper to parse usgmii control word [ Upstream commit 923454c0368b8092e9d05c020f50abca577e7290 ] Q-USGMII is a derivative of USGMII, that uses a specific formatting for the control word. The layout is close to the USXGMII control word, but doesn't support speeds over 1Gbps. Use a dedicated decoding logic for the USGMII control word, re-using USXGMII definitions but only considering 10/100/1000Mbps speeds Fixes: 5e61fe157a27 ("net: phy: Introduce QUSGMII PHY mode") Signed-off-by: Maxime Chevallier Reviewed-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/phylink.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index acd3405ddc9c..5b064a1de92f 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -3263,6 +3263,41 @@ void phylink_decode_usxgmii_word(struct phylink_link_state *state, } EXPORT_SYMBOL_GPL(phylink_decode_usxgmii_word); +/** + * phylink_decode_usgmii_word() - decode the USGMII word from a MAC PCS + * @state: a pointer to a struct phylink_link_state. + * @lpa: a 16 bit value which stores the USGMII auto-negotiation word + * + * Helper for MAC PCS supporting the USGMII protocol and the auto-negotiation + * code word. Decode the USGMII code word and populate the corresponding fields + * (speed, duplex) into the phylink_link_state structure. The structure for this + * word is the same as the USXGMII word, except it only supports speeds up to + * 1Gbps. + */ +static void phylink_decode_usgmii_word(struct phylink_link_state *state, + uint16_t lpa) +{ + switch (lpa & MDIO_USXGMII_SPD_MASK) { + case MDIO_USXGMII_10: + state->speed = SPEED_10; + break; + case MDIO_USXGMII_100: + state->speed = SPEED_100; + break; + case MDIO_USXGMII_1000: + state->speed = SPEED_1000; + break; + default: + state->link = false; + return; + } + + if (lpa & MDIO_USXGMII_FULL_DUPLEX) + state->duplex = DUPLEX_FULL; + else + state->duplex = DUPLEX_HALF; +} + /** * phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers * @state: a pointer to a &struct phylink_link_state. @@ -3299,9 +3334,11 @@ void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: - case PHY_INTERFACE_MODE_QUSGMII: phylink_decode_sgmii_word(state, lpa); break; + case PHY_INTERFACE_MODE_QUSGMII: + phylink_decode_usgmii_word(state, lpa); + break; default: state->link = false; -- cgit 1.4.1 From 90748be0f4f386ad3143cd7538ef37647e1f6260 Mon Sep 17 00:00:00 2001 From: Ratchanan Srirattanamet Date: Wed, 24 May 2023 04:11:56 +0700 Subject: drm/nouveau: don't detect DSM for non-NVIDIA device [ Upstream commit 11d24327c2d7ad7f24fcc44fb00e1fa91ebf6525 ] The call site of nouveau_dsm_pci_probe() uses single set of output variables for all invocations. So, we must not write anything to them unless it's an NVIDIA device. Otherwise, if we are called with another device after the NVIDIA device, we'll clober the result of the NVIDIA device. For example, if the other device doesn't have _PR3 resources, the detection later would miss the presence of power resource support, and the rest of the code will keep using Optimus DSM, breaking power management for that machine. Also, because we're detecting NVIDIA's DSM, it doesn't make sense to run this detection on a non-NVIDIA device anyway. Thus, check at the beginning of the detection code if this is an NVIDIA card, and just return if it isn't. This, together with commit d22915d22ded ("drm/nouveau/devinit/tu102-: wait for GFW_BOOT_PROGRESS == COMPLETED") developed independently and landed earlier, fixes runtime power management of the NVIDIA card in Lenovo Legion 5-15ARH05. Without this patch, the GPU resumption code will "timeout", sometimes hanging userspace. As a bonus, we'll also stop preventing _PR3 usage from the bridge for unrelated devices, which is always nice, I guess. Fixes: ccfc2d5cdb02 ("drm/nouveau: Use generic helper to check _PR3 presence") Signed-off-by: Ratchanan Srirattanamet Closes: https://gitlab.freedesktop.org/drm/nouveau/-/issues/79 Reviewed-by: Karol Herbst Signed-off-by: Karol Herbst Link: https://patchwork.freedesktop.org/patch/msgid/DM6PR19MB2780805D4BE1E3F9B3AC96D0BC409@DM6PR19MB2780.namprd19.prod.outlook.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/nouveau/nouveau_acpi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 8cf096f841a9..a2ae8c21e4dc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -220,6 +220,9 @@ static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out int optimus_funcs; struct pci_dev *parent_pdev; + if (pdev->vendor != PCI_VENDOR_ID_NVIDIA) + return; + *has_pr3 = false; parent_pdev = pci_upstream_bridge(pdev); if (parent_pdev) { -- cgit 1.4.1 From a5acbe4ea5d0847bfce1f12f4cd8780349ba652a Mon Sep 17 00:00:00 2001 From: Su Hui Date: Thu, 8 Jun 2023 09:24:43 +0800 Subject: drm/bridge: ti-sn65dsi86: Avoid possible buffer overflow [ Upstream commit 95011f267c44a4d1f9ca1769e8a29ab2c559e004 ] Smatch error:buffer overflow 'ti_sn_bridge_refclk_lut' 5 <= 5. Fixes: cea86c5bb442 ("drm/bridge: ti-sn65dsi86: Implement the pwm_chip") Signed-off-by: Su Hui Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20230608012443.839372-1-suhui@nfschina.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index aeca9c066bf2..d16775c973c4 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -298,6 +298,10 @@ static void ti_sn_bridge_set_refclk_freq(struct ti_sn65dsi86 *pdata) if (refclk_lut[i] == refclk_rate) break; + /* avoid buffer overflow and "1" is the default rate in the datasheet. */ + if (i >= refclk_lut_size) + i = 1; + regmap_update_bits(pdata->regmap, SN_DPPLL_SRC_REG, REFCLK_FREQ_MASK, REFCLK_FREQ(i)); -- cgit 1.4.1 From fb725beca62d175c02ca619c27037c14f7ab8e7c Mon Sep 17 00:00:00 2001 From: Natalia Petrova Date: Fri, 12 May 2023 14:15:26 +0300 Subject: drm/nouveau/dp: check for NULL nv_connector->native_mode [ Upstream commit 20a2ce87fbaf81e4c3dcb631d738e423959eb320 ] Add checking for NULL before calling nouveau_connector_detect_depth() in nouveau_connector_get_modes() function because nv_connector->native_mode could be dereferenced there since connector pointer passed to nouveau_connector_detect_depth() and the same value of nv_connector->native_mode is used there. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: d4c2c99bdc83 ("drm/nouveau/dp: remove broken display depth function, use the improved one") Signed-off-by: Natalia Petrova Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20230512111526.82408-1-n.petrova@fintech.ru Signed-off-by: Sasha Levin --- drivers/gpu/drm/nouveau/nouveau_connector.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 1991bbb1d05c..fd984733b8e6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -966,7 +966,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) /* Determine display colour depth for everything except LVDS now, * DP requires this before mode_valid() is called. */ - if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode) nouveau_connector_detect_depth(connector); /* Find the native mode if this is a digital panel, if we didn't @@ -987,7 +987,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) * "native" mode as some VBIOS tables require us to use the * pixel clock as part of the lookup... */ - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode) nouveau_connector_detect_depth(connector); if (nv_encoder->dcb->type == DCB_OUTPUT_TV) -- cgit 1.4.1 From 62aecf23f3d12f0a1b170bfd2174fd58d0d1bf50 Mon Sep 17 00:00:00 2001 From: Natalia Petrova Date: Fri, 12 May 2023 13:33:20 +0300 Subject: drm/nouveau: add nv_encoder pointer check for NULL [ Upstream commit 55b94bb8c42464bad3d2217f6874aa1a85664eac ] Pointer nv_encoder could be dereferenced at nouveau_connector.c in case it's equal to NULL by jumping to goto label. This patch adds a NULL-check to avoid it. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 3195c5f9784a ("drm/nouveau: set encoder for lvds") Signed-off-by: Natalia Petrova Reviewed-by: Lyude Paul [Fixed patch title] Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20230512103320.82234-1-n.petrova@fintech.ru Signed-off-by: Sasha Levin --- drivers/gpu/drm/nouveau/nouveau_connector.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index fd984733b8e6..f40310559d13 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -730,7 +730,8 @@ out: #endif nouveau_connector_set_edid(nv_connector, edid); - nouveau_connector_set_encoder(connector, nv_encoder); + if (nv_encoder) + nouveau_connector_set_encoder(connector, nv_encoder); return status; } -- cgit 1.4.1 From 8a086daf20a80437522eff4bc62002df9fae7dfa Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 12 Jun 2023 09:57:09 +0200 Subject: selftests/tc-testing: Fix Error: Specified qdisc kind is unknown. [ Upstream commit aef6e908b54200d04f2d77dab31509fcff2e60ae ] All TEQL tests assume that sch_teql module is loaded. Load module in tdc.sh before running qdisc tests. Fixes following example error when running tests via tdc.sh for all TEQL tests: # $ sudo ./tdc.py -d eth2 -e 84a0 # -- ns/SubPlugin.__init__ # Test 84a0: Create TEQL with default setting # exit: 2 # exit: 0 # Error: Specified qdisc kind is unknown. # # -----> teardown stage *** Could not execute: "$TC qdisc del dev $DUMMY handle 1: root" # # -----> teardown stage *** Error message: "Error: Invalid handle. # " # returncode 2; expected [0] # # -----> teardown stage *** Aborting test run. # # <_io.BufferedReader name=3> *** stdout *** # # <_io.BufferedReader name=5> *** stderr *** # "-----> teardown stage" did not complete successfully # Exception ('teardown', 'Error: Specified qdisc kind is unknown.\n', '"-----> teardown stage" did not complete successfully') (caught in test_runner, running test 2 84a0 Create TEQL with default setting stage teardown) # --------------- # traceback # File "/images/src/linux/tools/testing/selftests/tc-testing/./tdc.py", line 495, in test_runner # res = run_one_test(pm, args, index, tidx) # File "/images/src/linux/tools/testing/selftests/tc-testing/./tdc.py", line 434, in run_one_test # prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout) # File "/images/src/linux/tools/testing/selftests/tc-testing/./tdc.py", line 245, in prepare_env # raise PluginMgrTestFail( # --------------- # accumulated output for this test: # Error: Specified qdisc kind is unknown. # # --------------- # # All test results: # # 1..1 # ok 1 84a0 - Create TEQL with default setting # skipped - "-----> teardown stage" did not complete successfully Fixes: cc62fbe114c9 ("selftests/tc-testing: add selftests for teql qdisc") Signed-off-by: Vlad Buslov Reviewed-by: Victor Nogueira Reviewed-by: Pedro Tammela Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/tc-testing/tdc.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/tc-testing/tdc.sh b/tools/testing/selftests/tc-testing/tdc.sh index afb0cd86fa3d..eb357bd7923c 100755 --- a/tools/testing/selftests/tc-testing/tdc.sh +++ b/tools/testing/selftests/tc-testing/tdc.sh @@ -2,5 +2,6 @@ # SPDX-License-Identifier: GPL-2.0 modprobe netdevsim +modprobe sch_teql ./tdc.py -c actions --nobuildebpf ./tdc.py -c qdisc -- cgit 1.4.1 From 700d7bf300c12f2004c2e071a098db813a4c34b5 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 12 Jun 2023 09:57:10 +0200 Subject: selftests/tc-testing: Fix Error: failed to find target LOG [ Upstream commit b849c566ee9c6ed78288a522278dcaf419f8e239 ] Add missing netfilter config dependency. Fixes following example error when running tests via tdc.sh for all XT tests: # $ sudo ./tdc.py -d eth2 -e 2029 # Test 2029: Add xt action with log-prefix # exit: 255 # exit: 0 # failed to find target LOG # # bad action parsing # parse_action: bad value (7:xt)! # Illegal "action" # # -----> teardown stage *** Could not execute: "$TC actions flush action xt" # # -----> teardown stage *** Error message: "Error: Cannot flush unknown TC action. # We have an error flushing # " # returncode 1; expected [0] # # -----> teardown stage *** Aborting test run. # # <_io.BufferedReader name=3> *** stdout *** # # <_io.BufferedReader name=5> *** stderr *** # "-----> teardown stage" did not complete successfully # Exception ('teardown', ' failed to find target LOG\n\nbad action parsing\nparse_action: bad value (7:xt)!\nIllegal "action"\n', '"-----> teardown stage" did not complete successfully') (caught in test_runner, running test 2 2029 Add xt action with log-prefix stage teardown) # --------------- # traceback # File "/images/src/linux/tools/testing/selftests/tc-testing/./tdc.py", line 495, in test_runner # res = run_one_test(pm, args, index, tidx) # File "/images/src/linux/tools/testing/selftests/tc-testing/./tdc.py", line 434, in run_one_test # prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout) # File "/images/src/linux/tools/testing/selftests/tc-testing/./tdc.py", line 245, in prepare_env # raise PluginMgrTestFail( # --------------- # accumulated output for this test: # failed to find target LOG # # bad action parsing # parse_action: bad value (7:xt)! # Illegal "action" # # --------------- # # All test results: # # 1..1 # ok 1 2029 - Add xt action with log-prefix # skipped - "-----> teardown stage" did not complete successfully Fixes: 910d504bc187 ("selftests/tc-testings: add selftests for xt action") Signed-off-by: Vlad Buslov Reviewed-by: Pedro Tammela Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/tc-testing/config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/tc-testing/config b/tools/testing/selftests/tc-testing/config index 4638c63a339f..aec4de8bea78 100644 --- a/tools/testing/selftests/tc-testing/config +++ b/tools/testing/selftests/tc-testing/config @@ -6,6 +6,7 @@ CONFIG_NF_CONNTRACK_MARK=y CONFIG_NF_CONNTRACK_ZONES=y CONFIG_NF_CONNTRACK_LABELS=y CONFIG_NF_NAT=m +CONFIG_NETFILTER_XT_TARGET_LOG=m CONFIG_NET_SCHED=y -- cgit 1.4.1 From e568e0e1683035b1460978a5584983d59ee526bf Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 12 Jun 2023 09:57:11 +0200 Subject: selftests/tc-testing: Fix SFB db test [ Upstream commit b39d8c41c7a8336ce85c376b5d4906089524a0ae ] Setting very small value of db like 10ms introduces rounding errors when converting to/from jiffies on some kernel configs. For example, on 250hz the actual value will be set to 12ms which causes the test to fail: # $ sudo ./tdc.py -d eth2 -e 3410 # -- ns/SubPlugin.__init__ # Test 3410: Create SFB with db setting # # All test results: # # 1..1 # not ok 1 3410 - Create SFB with db setting # Could not match regex pattern. Verify command output: # qdisc sfb 1: root refcnt 2 rehash 600s db 12ms limit 1000p max 25p target 20p increment 0.000503548 decrement 4.57771e-05 penalty_rate 10pps penalty_burst 20p Set the value to 100ms instead which currently seem to work on 100hz, 250hz, 300hz and 1000hz kernel configs. Fixes: 6ad92dc56fca ("selftests/tc-testing: add selftests for sfb qdisc") Signed-off-by: Vlad Buslov Reviewed-by: Pedro Tammela Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json index ba2f5e79cdbf..e21c7f22c6d4 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json @@ -58,10 +58,10 @@ "setup": [ "$IP link add dev $DUMMY type dummy || /bin/true" ], - "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb db 10", + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb db 100", "expExitCode": "0", "verifyCmd": "$TC qdisc show dev $DUMMY", - "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 600s db 10ms", + "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 600s db 100ms", "matchCount": "1", "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root", -- cgit 1.4.1 From 096c00ea80db541a4ec6c6b701a566399b6a9197 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Fri, 13 Jan 2023 11:43:53 +0800 Subject: sched: add new attr TCA_EXT_WARN_MSG to report tc extact message [ Upstream commit 0349b8779cc949ad9e6aced32672ee48cf79b497 ] We will report extack message if there is an error via netlink_ack(). But if the rule is not to be exclusively executed by the hardware, extack is not passed along and offloading failures don't get logged. In commit 81c7288b170a ("sched: cls: enable verbose logging") Marcelo made cls could log verbose info for offloading failures, which helps improving Open vSwitch debuggability when using flower offloading. It would also be helpful if userspace monitor tools, like "tc monitor", could log this kind of message, as it doesn't require vswitchd log level adjusment. Let's add a new tc attributes to report the extack message so the monitor program could receive the failures. e.g. # tc monitor added chain dev enp3s0f1np1 parent ffff: chain 0 added filter dev enp3s0f1np1 ingress protocol all pref 49152 flower chain 0 handle 0x1 ct_state +trk+new not_in_hw action order 1: gact action drop random type none pass val 0 index 1 ref 1 bind 1 Warning: mlx5_core: matching on ct_state +new isn't supported. In this patch I only report the extack message on add/del operations. It doesn't look like we need to report the extack message on get/dump operations. Note this message not only reporte to multicast groups, it could also be reported unicast, which may affect the current usersapce tool's behaivor. Suggested-by: Marcelo Ricardo Leitner Signed-off-by: Hangbin Liu Acked-by: Jakub Kicinski Acked-by: Jamal Hadi Salim Link: https://lore.kernel.org/r/20230113034353.2766735-1-liuhangbin@gmail.com Signed-off-by: Paolo Abeni Stable-dep-of: 84ad0af0bccd ("net/sched: qdisc_destroy() old ingress and clsact Qdiscs before grafting") Signed-off-by: Sasha Levin --- include/uapi/linux/rtnetlink.h | 1 + net/sched/act_api.c | 15 ++++++---- net/sched/cls_api.c | 62 ++++++++++++++++++++++++++---------------- net/sched/sch_api.c | 55 +++++++++++++++++++++++-------------- 4 files changed, 84 insertions(+), 49 deletions(-) diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index eb2747d58a81..25a0af57dd5e 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -635,6 +635,7 @@ enum { TCA_INGRESS_BLOCK, TCA_EGRESS_BLOCK, TCA_DUMP_FLAGS, + TCA_EXT_WARN_MSG, __TCA_MAX }; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 9b31a10cc639..cc6628a42e83 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1581,7 +1581,7 @@ errout: static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], u32 portid, u32 seq, u16 flags, int event, int bind, - int ref) + int ref, struct netlink_ext_ack *extack) { struct tcamsg *t; struct nlmsghdr *nlh; @@ -1605,7 +1605,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], nla_nest_end(skb, nest); + if (extack && extack->_msg && + nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) + goto out_nlmsg_trim; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; out_nlmsg_trim: @@ -1624,7 +1629,7 @@ tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, if (!skb) return -ENOBUFS; if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, - 0, 1) <= 0) { + 0, 1, NULL) <= 0) { NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action"); kfree_skb(skb); return -EINVAL; @@ -1798,7 +1803,7 @@ tcf_reoffload_del_notify(struct net *net, struct tc_action *action) if (!skb) return -ENOBUFS; - if (tca_get_fill(skb, actions, 0, 0, 0, RTM_DELACTION, 0, 1) <= 0) { + if (tca_get_fill(skb, actions, 0, 0, 0, RTM_DELACTION, 0, 1, NULL) <= 0) { kfree_skb(skb); return -EINVAL; } @@ -1885,7 +1890,7 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[], return -ENOBUFS; if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION, - 0, 2) <= 0) { + 0, 2, extack) <= 0) { NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes"); kfree_skb(skb); return -EINVAL; @@ -1964,7 +1969,7 @@ tcf_add_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[], return -ENOBUFS; if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags, - RTM_NEWACTION, 0, 0) <= 0) { + RTM_NEWACTION, 0, 0, extack) <= 0) { NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action"); kfree_skb(skb); return -EINVAL; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index abaf75300497..0dbfc37d9799 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -485,7 +485,8 @@ static struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block, #endif static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, - u32 seq, u16 flags, int event, bool unicast); + u32 seq, u16 flags, int event, bool unicast, + struct netlink_ext_ack *extack); static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, u32 chain_index, bool create, @@ -518,7 +519,7 @@ static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, */ if (is_first_reference && !by_act) tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, - RTM_NEWCHAIN, false); + RTM_NEWCHAIN, false, NULL); return chain; @@ -1815,7 +1816,8 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb, struct tcf_proto *tp, struct tcf_block *block, struct Qdisc *q, u32 parent, void *fh, u32 portid, u32 seq, u16 flags, int event, - bool terse_dump, bool rtnl_held) + bool terse_dump, bool rtnl_held, + struct netlink_ext_ack *extack) { struct tcmsg *tcm; struct nlmsghdr *nlh; @@ -1855,7 +1857,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb, tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0) goto nla_put_failure; } + + if (extack && extack->_msg && + nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) + goto nla_put_failure; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; out_nlmsg_trim: @@ -1869,7 +1877,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct tcf_proto *tp, struct tcf_block *block, struct Qdisc *q, u32 parent, void *fh, int event, bool unicast, - bool rtnl_held) + bool rtnl_held, struct netlink_ext_ack *extack) { struct sk_buff *skb; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; @@ -1881,7 +1889,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, n->nlmsg_seq, n->nlmsg_flags, event, - false, rtnl_held) <= 0) { + false, rtnl_held, extack) <= 0) { kfree_skb(skb); return -EINVAL; } @@ -1910,7 +1918,7 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER, - false, rtnl_held) <= 0) { + false, rtnl_held, extack) <= 0) { NL_SET_ERR_MSG(extack, "Failed to build del event notification"); kfree_skb(skb); return -EINVAL; @@ -1936,14 +1944,15 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, struct tcf_block *block, struct Qdisc *q, u32 parent, struct nlmsghdr *n, - struct tcf_chain *chain, int event) + struct tcf_chain *chain, int event, + struct netlink_ext_ack *extack) { struct tcf_proto *tp; for (tp = tcf_get_next_proto(chain, NULL); tp; tp = tcf_get_next_proto(chain, tp)) - tfilter_notify(net, oskb, n, tp, block, - q, parent, NULL, event, false, true); + tfilter_notify(net, oskb, n, tp, block, q, parent, NULL, + event, false, true, extack); } static void tfilter_put(struct tcf_proto *tp, void *fh) @@ -2147,7 +2156,7 @@ replay: flags, extack); if (err == 0) { tfilter_notify(net, skb, n, tp, block, q, parent, fh, - RTM_NEWTFILTER, false, rtnl_held); + RTM_NEWTFILTER, false, rtnl_held, extack); tfilter_put(tp, fh); /* q pointer is NULL for shared blocks */ if (q) @@ -2275,7 +2284,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, if (prio == 0) { tfilter_notify_chain(net, skb, block, q, parent, n, - chain, RTM_DELTFILTER); + chain, RTM_DELTFILTER, extack); tcf_chain_flush(chain, rtnl_held); err = 0; goto errout; @@ -2299,7 +2308,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, tcf_proto_put(tp, rtnl_held, NULL); tfilter_notify(net, skb, n, tp, block, q, parent, fh, - RTM_DELTFILTER, false, rtnl_held); + RTM_DELTFILTER, false, rtnl_held, extack); err = 0; goto errout; } @@ -2443,7 +2452,7 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n, err = -ENOENT; } else { err = tfilter_notify(net, skb, n, tp, block, q, parent, - fh, RTM_NEWTFILTER, true, rtnl_held); + fh, RTM_NEWTFILTER, true, rtnl_held, NULL); if (err < 0) NL_SET_ERR_MSG(extack, "Failed to send filter notify message"); } @@ -2481,7 +2490,7 @@ static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent, n, NETLINK_CB(a->cb->skb).portid, a->cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWTFILTER, a->terse_dump, true); + RTM_NEWTFILTER, a->terse_dump, true, NULL); } static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, @@ -2515,7 +2524,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, if (tcf_fill_node(net, skb, tp, block, q, parent, NULL, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWTFILTER, false, true) <= 0) + RTM_NEWTFILTER, false, true, NULL) <= 0) goto errout; cb->args[1] = 1; } @@ -2658,7 +2667,8 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, void *tmplt_priv, u32 chain_index, struct net *net, struct sk_buff *skb, struct tcf_block *block, - u32 portid, u32 seq, u16 flags, int event) + u32 portid, u32 seq, u16 flags, int event, + struct netlink_ext_ack *extack) { unsigned char *b = skb_tail_pointer(skb); const struct tcf_proto_ops *ops; @@ -2695,7 +2705,12 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, goto nla_put_failure; } + if (extack && extack->_msg && + nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) + goto out_nlmsg_trim; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; out_nlmsg_trim: @@ -2705,7 +2720,8 @@ nla_put_failure: } static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, - u32 seq, u16 flags, int event, bool unicast) + u32 seq, u16 flags, int event, bool unicast, + struct netlink_ext_ack *extack) { u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; struct tcf_block *block = chain->block; @@ -2719,7 +2735,7 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, chain->index, net, skb, block, portid, - seq, flags, event) <= 0) { + seq, flags, event, extack) <= 0) { kfree_skb(skb); return -EINVAL; } @@ -2747,7 +2763,7 @@ static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, return -ENOBUFS; if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb, - block, portid, seq, flags, RTM_DELCHAIN) <= 0) { + block, portid, seq, flags, RTM_DELCHAIN, NULL) <= 0) { kfree_skb(skb); return -EINVAL; } @@ -2900,11 +2916,11 @@ replay: } tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, - RTM_NEWCHAIN, false); + RTM_NEWCHAIN, false, extack); break; case RTM_DELCHAIN: tfilter_notify_chain(net, skb, block, q, parent, n, - chain, RTM_DELTFILTER); + chain, RTM_DELTFILTER, extack); /* Flush the chain first as the user requested chain removal. */ tcf_chain_flush(chain, true); /* In case the chain was successfully deleted, put a reference @@ -2914,7 +2930,7 @@ replay: break; case RTM_GETCHAIN: err = tc_chain_notify(chain, skb, n->nlmsg_seq, - n->nlmsg_flags, n->nlmsg_type, true); + n->nlmsg_flags, n->nlmsg_type, true, extack); if (err < 0) NL_SET_ERR_MSG(extack, "Failed to send chain notify message"); break; @@ -3014,7 +3030,7 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) chain->index, net, skb, block, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWCHAIN); + RTM_NEWCHAIN, NULL); if (err <= 0) break; index++; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 6fb345ec2264..3907483dae62 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -907,7 +907,8 @@ static void qdisc_offload_graft_root(struct net_device *dev, } static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, - u32 portid, u32 seq, u16 flags, int event) + u32 portid, u32 seq, u16 flags, int event, + struct netlink_ext_ack *extack) { struct gnet_stats_basic_sync __percpu *cpu_bstats = NULL; struct gnet_stats_queue __percpu *cpu_qstats = NULL; @@ -975,7 +976,12 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, if (gnet_stats_finish_copy(&d) < 0) goto nla_put_failure; + if (extack && extack->_msg && + nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) + goto out_nlmsg_trim; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; out_nlmsg_trim: @@ -996,7 +1002,8 @@ static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible) static int qdisc_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, u32 clid, - struct Qdisc *old, struct Qdisc *new) + struct Qdisc *old, struct Qdisc *new, + struct netlink_ext_ack *extack) { struct sk_buff *skb; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; @@ -1007,12 +1014,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, if (old && !tc_qdisc_dump_ignore(old, false)) { if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, - 0, RTM_DELQDISC) < 0) + 0, RTM_DELQDISC, extack) < 0) goto err_out; } if (new && !tc_qdisc_dump_ignore(new, false)) { if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, - old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) + old ? NLM_F_REPLACE : 0, RTM_NEWQDISC, extack) < 0) goto err_out; } @@ -1027,10 +1034,11 @@ err_out: static void notify_and_destroy(struct net *net, struct sk_buff *skb, struct nlmsghdr *n, u32 clid, - struct Qdisc *old, struct Qdisc *new) + struct Qdisc *old, struct Qdisc *new, + struct netlink_ext_ack *extack) { if (new || old) - qdisc_notify(net, skb, n, clid, old, new); + qdisc_notify(net, skb, n, clid, old, new, extack); if (old) qdisc_put(old); @@ -1110,12 +1118,12 @@ skip: qdisc_refcount_inc(new); rcu_assign_pointer(dev->qdisc, new ? : &noop_qdisc); - notify_and_destroy(net, skb, n, classid, old, new); + notify_and_destroy(net, skb, n, classid, old, new, extack); if (new && new->ops->attach) new->ops->attach(new); } else { - notify_and_destroy(net, skb, n, classid, old, new); + notify_and_destroy(net, skb, n, classid, old, new, extack); } if (dev->flags & IFF_UP) @@ -1146,7 +1154,7 @@ skip: err = cops->graft(parent, cl, new, &old, extack); if (err) return err; - notify_and_destroy(net, skb, n, classid, old, new); + notify_and_destroy(net, skb, n, classid, old, new, extack); } return 0; } @@ -1519,7 +1527,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, if (err != 0) return err; } else { - qdisc_notify(net, skb, n, clid, NULL, q); + qdisc_notify(net, skb, n, clid, NULL, q, NULL); } return 0; } @@ -1667,7 +1675,7 @@ replay: } err = qdisc_change(q, tca, extack); if (err == 0) - qdisc_notify(net, skb, n, clid, NULL, q); + qdisc_notify(net, skb, n, clid, NULL, q, extack); return err; create_n_graft: @@ -1734,7 +1742,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, if (!tc_qdisc_dump_ignore(q, dump_invisible) && tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWQDISC) <= 0) + RTM_NEWQDISC, NULL) <= 0) goto done; q_idx++; } @@ -1756,7 +1764,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, if (!tc_qdisc_dump_ignore(q, dump_invisible) && tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWQDISC) <= 0) + RTM_NEWQDISC, NULL) <= 0) goto done; q_idx++; } @@ -1829,8 +1837,8 @@ done: ************************************************/ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, - unsigned long cl, - u32 portid, u32 seq, u16 flags, int event) + unsigned long cl, u32 portid, u32 seq, u16 flags, + int event, struct netlink_ext_ack *extack) { struct tcmsg *tcm; struct nlmsghdr *nlh; @@ -1865,7 +1873,12 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, if (gnet_stats_finish_copy(&d) < 0) goto nla_put_failure; + if (extack && extack->_msg && + nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) + goto out_nlmsg_trim; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; out_nlmsg_trim: @@ -1876,7 +1889,7 @@ nla_put_failure: static int tclass_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct Qdisc *q, - unsigned long cl, int event) + unsigned long cl, int event, struct netlink_ext_ack *extack) { struct sk_buff *skb; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; @@ -1885,7 +1898,7 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb, if (!skb) return -ENOBUFS; - if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) { + if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event, extack) < 0) { kfree_skb(skb); return -EINVAL; } @@ -1912,7 +1925,7 @@ static int tclass_del_notify(struct net *net, return -ENOBUFS; if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, - RTM_DELTCLASS) < 0) { + RTM_DELTCLASS, extack) < 0) { kfree_skb(skb); return -EINVAL; } @@ -2119,7 +2132,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, tc_bind_tclass(q, portid, clid, 0); goto out; case RTM_GETTCLASS: - err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS); + err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS, extack); goto out; default: err = -EINVAL; @@ -2137,7 +2150,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, if (cops->change) err = cops->change(q, clid, portid, tca, &new_cl, extack); if (err == 0) { - tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS); + tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS, extack); /* We just create a new class, need to do reverse binding. */ if (cl != new_cl) tc_bind_tclass(q, portid, clid, new_cl); @@ -2159,7 +2172,7 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid, a->cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWTCLASS); + RTM_NEWTCLASS, NULL); } static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, -- cgit 1.4.1 From ac57be24dcf1b2cfe4bb19cb2c2153e5ece23403 Mon Sep 17 00:00:00 2001 From: Peilin Ye Date: Sat, 10 Jun 2023 20:30:15 -0700 Subject: net/sched: Refactor qdisc_graft() for ingress and clsact Qdiscs [ Upstream commit 2d5f6a8d7aef7852a9ecc555f88c673a1c91754f ] Grafting ingress and clsact Qdiscs does not need a for-loop in qdisc_graft(). Refactor it. No functional changes intended. Tested-by: Pedro Tammela Acked-by: Jamal Hadi Salim Reviewed-by: Jamal Hadi Salim Reviewed-by: Vlad Buslov Signed-off-by: Peilin Ye Signed-off-by: Paolo Abeni Stable-dep-of: 84ad0af0bccd ("net/sched: qdisc_destroy() old ingress and clsact Qdiscs before grafting") Signed-off-by: Sasha Levin --- net/sched/sch_api.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 3907483dae62..bd9f523b169a 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1076,12 +1076,12 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, if (parent == NULL) { unsigned int i, num_q, ingress; + struct netdev_queue *dev_queue; ingress = 0; num_q = dev->num_tx_queues; if ((q && q->flags & TCQ_F_INGRESS) || (new && new->flags & TCQ_F_INGRESS)) { - num_q = 1; ingress = 1; if (!dev_ingress_queue(dev)) { NL_SET_ERR_MSG(extack, "Device does not have an ingress queue"); @@ -1097,18 +1097,18 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, if (new && new->ops->attach && !ingress) goto skip; - for (i = 0; i < num_q; i++) { - struct netdev_queue *dev_queue = dev_ingress_queue(dev); - - if (!ingress) + if (!ingress) { + for (i = 0; i < num_q; i++) { dev_queue = netdev_get_tx_queue(dev, i); + old = dev_graft_qdisc(dev_queue, new); - old = dev_graft_qdisc(dev_queue, new); - if (new && i > 0) - qdisc_refcount_inc(new); - - if (!ingress) + if (new && i > 0) + qdisc_refcount_inc(new); qdisc_put(old); + } + } else { + dev_queue = dev_ingress_queue(dev); + old = dev_graft_qdisc(dev_queue, new); } skip: -- cgit 1.4.1 From ea3f336f717a8f9236ed50959781c8caa56fa4b8 Mon Sep 17 00:00:00 2001 From: Peilin Ye Date: Sat, 10 Jun 2023 20:30:25 -0700 Subject: net/sched: qdisc_destroy() old ingress and clsact Qdiscs before grafting [ Upstream commit 84ad0af0bccd3691cb951c2974c5cb2c10594d4a ] mini_Qdisc_pair::p_miniq is a double pointer to mini_Qdisc, initialized in ingress_init() to point to net_device::miniq_ingress. ingress Qdiscs access this per-net_device pointer in mini_qdisc_pair_swap(). Similar for clsact Qdiscs and miniq_egress. Unfortunately, after introducing RTNL-unlocked RTM_{NEW,DEL,GET}TFILTER requests (thanks Hillf Danton for the hint), when replacing ingress or clsact Qdiscs, for example, the old Qdisc ("@old") could access the same miniq_{in,e}gress pointer(s) concurrently with the new Qdisc ("@new"), causing race conditions [1] including a use-after-free bug in mini_qdisc_pair_swap() reported by syzbot: BUG: KASAN: slab-use-after-free in mini_qdisc_pair_swap+0x1c2/0x1f0 net/sched/sch_generic.c:1573 Write of size 8 at addr ffff888045b31308 by task syz-executor690/14901 ... Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0xd9/0x150 lib/dump_stack.c:106 print_address_description.constprop.0+0x2c/0x3c0 mm/kasan/report.c:319 print_report mm/kasan/report.c:430 [inline] kasan_report+0x11c/0x130 mm/kasan/report.c:536 mini_qdisc_pair_swap+0x1c2/0x1f0 net/sched/sch_generic.c:1573 tcf_chain_head_change_item net/sched/cls_api.c:495 [inline] tcf_chain0_head_change.isra.0+0xb9/0x120 net/sched/cls_api.c:509 tcf_chain_tp_insert net/sched/cls_api.c:1826 [inline] tcf_chain_tp_insert_unique net/sched/cls_api.c:1875 [inline] tc_new_tfilter+0x1de6/0x2290 net/sched/cls_api.c:2266 ... @old and @new should not affect each other. In other words, @old should never modify miniq_{in,e}gress after @new, and @new should not update @old's RCU state. Fixing without changing sch_api.c turned out to be difficult (please refer to Closes: for discussions). Instead, make sure @new's first call always happen after @old's last call (in {ingress,clsact}_destroy()) has finished: In qdisc_graft(), return -EBUSY if @old has any ongoing filter requests, and call qdisc_destroy() for @old before grafting @new. Introduce qdisc_refcount_dec_if_one() as the counterpart of qdisc_refcount_inc_nz() used for filter requests. Introduce a non-static version of qdisc_destroy() that does a TCQ_F_BUILTIN check, just like qdisc_put() etc. Depends on patch "net/sched: Refactor qdisc_graft() for ingress and clsact Qdiscs". [1] To illustrate, the syzkaller reproducer adds ingress Qdiscs under TC_H_ROOT (no longer possible after commit c7cfbd115001 ("net/sched: sch_ingress: Only create under TC_H_INGRESS")) on eth0 that has 8 transmission queues: Thread 1 creates ingress Qdisc A (containing mini Qdisc a1 and a2), then adds a flower filter X to A. Thread 2 creates another ingress Qdisc B (containing mini Qdisc b1 and b2) to replace A, then adds a flower filter Y to B. Thread 1 A's refcnt Thread 2 RTM_NEWQDISC (A, RTNL-locked) qdisc_create(A) 1 qdisc_graft(A) 9 RTM_NEWTFILTER (X, RTNL-unlocked) __tcf_qdisc_find(A) 10 tcf_chain0_head_change(A) mini_qdisc_pair_swap(A) (1st) | | RTM_NEWQDISC (B, RTNL-locked) RCU sync 2 qdisc_graft(B) | 1 notify_and_destroy(A) | tcf_block_release(A) 0 RTM_NEWTFILTER (Y, RTNL-unlocked) qdisc_destroy(A) tcf_chain0_head_change(B) tcf_chain0_head_change_cb_del(A) mini_qdisc_pair_swap(B) (2nd) mini_qdisc_pair_swap(A) (3rd) | ... ... Here, B calls mini_qdisc_pair_swap(), pointing eth0->miniq_ingress to its mini Qdisc, b1. Then, A calls mini_qdisc_pair_swap() again during ingress_destroy(), setting eth0->miniq_ingress to NULL, so ingress packets on eth0 will not find filter Y in sch_handle_ingress(). This is just one of the possible consequences of concurrently accessing miniq_{in,e}gress pointers. Fixes: 7a096d579e8e ("net: sched: ingress: set 'unlocked' flag for Qdisc ops") Fixes: 87f373921c4e ("net: sched: ingress: set 'unlocked' flag for clsact Qdisc ops") Reported-by: syzbot+b53a9c0d1ea4ad62da8b@syzkaller.appspotmail.com Closes: https://lore.kernel.org/r/0000000000006cf87705f79acf1a@google.com/ Cc: Hillf Danton Cc: Vlad Buslov Signed-off-by: Peilin Ye Acked-by: Jamal Hadi Salim Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/sch_generic.h | 8 ++++++++ net/sched/sch_api.c | 28 +++++++++++++++++++++++----- net/sched/sch_generic.c | 14 +++++++++++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 989eb972fcae..b3e312840296 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -137,6 +137,13 @@ static inline void qdisc_refcount_inc(struct Qdisc *qdisc) refcount_inc(&qdisc->refcnt); } +static inline bool qdisc_refcount_dec_if_one(struct Qdisc *qdisc) +{ + if (qdisc->flags & TCQ_F_BUILTIN) + return true; + return refcount_dec_if_one(&qdisc->refcnt); +} + /* Intended to be used by unlocked users, when concurrent qdisc release is * possible. */ @@ -650,6 +657,7 @@ void dev_deactivate_many(struct list_head *head); struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, struct Qdisc *qdisc); void qdisc_reset(struct Qdisc *qdisc); +void qdisc_destroy(struct Qdisc *qdisc); void qdisc_put(struct Qdisc *qdisc); void qdisc_put_unlocked(struct Qdisc *qdisc); void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, int n, int len); diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index bd9f523b169a..01d07e6a6811 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1083,10 +1083,22 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, if ((q && q->flags & TCQ_F_INGRESS) || (new && new->flags & TCQ_F_INGRESS)) { ingress = 1; - if (!dev_ingress_queue(dev)) { + dev_queue = dev_ingress_queue(dev); + if (!dev_queue) { NL_SET_ERR_MSG(extack, "Device does not have an ingress queue"); return -ENOENT; } + + q = rtnl_dereference(dev_queue->qdisc_sleeping); + + /* This is the counterpart of that qdisc_refcount_inc_nz() call in + * __tcf_qdisc_find() for filter requests. + */ + if (!qdisc_refcount_dec_if_one(q)) { + NL_SET_ERR_MSG(extack, + "Current ingress or clsact Qdisc has ongoing filter requests"); + return -EBUSY; + } } if (dev->flags & IFF_UP) @@ -1107,8 +1119,16 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, qdisc_put(old); } } else { - dev_queue = dev_ingress_queue(dev); - old = dev_graft_qdisc(dev_queue, new); + old = dev_graft_qdisc(dev_queue, NULL); + + /* {ingress,clsact}_destroy() @old before grafting @new to avoid + * unprotected concurrent accesses to net_device::miniq_{in,e}gress + * pointer(s) in mini_qdisc_pair_swap(). + */ + qdisc_notify(net, skb, n, classid, old, new, extack); + qdisc_destroy(old); + + dev_graft_qdisc(dev_queue, new); } skip: @@ -1122,8 +1142,6 @@ skip: if (new && new->ops->attach) new->ops->attach(new); - } else { - notify_and_destroy(net, skb, n, classid, old, new, extack); } if (dev->flags & IFF_UP) diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index ee43e8ac039e..a5693e25b248 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1046,7 +1046,7 @@ static void qdisc_free_cb(struct rcu_head *head) qdisc_free(q); } -static void qdisc_destroy(struct Qdisc *qdisc) +static void __qdisc_destroy(struct Qdisc *qdisc) { const struct Qdisc_ops *ops = qdisc->ops; @@ -1070,6 +1070,14 @@ static void qdisc_destroy(struct Qdisc *qdisc) call_rcu(&qdisc->rcu, qdisc_free_cb); } +void qdisc_destroy(struct Qdisc *qdisc) +{ + if (qdisc->flags & TCQ_F_BUILTIN) + return; + + __qdisc_destroy(qdisc); +} + void qdisc_put(struct Qdisc *qdisc) { if (!qdisc) @@ -1079,7 +1087,7 @@ void qdisc_put(struct Qdisc *qdisc) !refcount_dec_and_test(&qdisc->refcnt)) return; - qdisc_destroy(qdisc); + __qdisc_destroy(qdisc); } EXPORT_SYMBOL(qdisc_put); @@ -1094,7 +1102,7 @@ void qdisc_put_unlocked(struct Qdisc *qdisc) !refcount_dec_and_rtnl_lock(&qdisc->refcnt)) return; - qdisc_destroy(qdisc); + __qdisc_destroy(qdisc); rtnl_unlock(); } EXPORT_SYMBOL(qdisc_put_unlocked); -- cgit 1.4.1 From e8119d4d1611309ae545d292ba4d700f3e883040 Mon Sep 17 00:00:00 2001 From: Danielle Ratson Date: Mon, 12 Jun 2023 16:34:58 +0200 Subject: selftests: forwarding: hw_stats_l3: Set addrgenmode in a separate step [ Upstream commit bef68e201e538eaa3a91f97aae8161eb2d0a8ed7 ] Setting the IPv6 address generation mode of a net device during its creation never worked, but after commit b0ad3c179059 ("rtnetlink: call validate_linkmsg in rtnl_create_link") it explicitly fails [1]. The failure is caused by the fact that validate_linkmsg() is called before the net device is registered, when it still does not have an 'inet6_dev'. Likewise, raising the net device before setting the address generation mode is meaningless, because by the time the mode is set, the address has already been generated. Therefore, fix the test to first create the net device, then set its IPv6 address generation mode and finally bring it up. [1] # ip link add name mydev addrgenmode eui64 type dummy RTNETLINK answers: Address family not supported by protocol Fixes: ba95e7930957 ("selftests: forwarding: hw_stats_l3: Add a new test") Signed-off-by: Danielle Ratson Reviewed-by: Ido Schimmel Signed-off-by: Petr Machata Link: https://lore.kernel.org/r/f3b05d85b2bc0c3d6168fe8f7207c6c8365703db.1686580046.git.petrm@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- tools/testing/selftests/net/forwarding/hw_stats_l3.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/hw_stats_l3.sh b/tools/testing/selftests/net/forwarding/hw_stats_l3.sh index 9c1f76e108af..1a936ffbacee 100755 --- a/tools/testing/selftests/net/forwarding/hw_stats_l3.sh +++ b/tools/testing/selftests/net/forwarding/hw_stats_l3.sh @@ -84,8 +84,9 @@ h2_destroy() router_rp1_200_create() { - ip link add name $rp1.200 up \ - link $rp1 addrgenmode eui64 type vlan id 200 + ip link add name $rp1.200 link $rp1 type vlan id 200 + ip link set dev $rp1.200 addrgenmode eui64 + ip link set dev $rp1.200 up ip address add dev $rp1.200 192.0.2.2/28 ip address add dev $rp1.200 2001:db8:1::2/64 ip stats set dev $rp1.200 l3_stats on @@ -256,9 +257,11 @@ reapply_config() router_rp1_200_destroy - ip link add name $rp1.200 link $rp1 addrgenmode none type vlan id 200 + ip link add name $rp1.200 link $rp1 type vlan id 200 + ip link set dev $rp1.200 addrgenmode none ip stats set dev $rp1.200 l3_stats on - ip link set dev $rp1.200 up addrgenmode eui64 + ip link set dev $rp1.200 addrgenmode eui64 + ip link set dev $rp1.200 up ip address add dev $rp1.200 192.0.2.2/28 ip address add dev $rp1.200 2001:db8:1::2/64 } -- cgit 1.4.1 From d7d6e830cd0ff2c71251ff6c8943a09e35cf0f6b Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 11 Jun 2023 11:23:32 -0500 Subject: cifs: fix lease break oops in xfstest generic/098 [ Upstream commit c774e6779f38bf36f0cce65e30793704bab4b0d7 ] umount can race with lease break so need to check if tcon->ses->server is still valid to send the lease break response. Reviewed-by: Bharath SM Reviewed-by: Shyam Prasad N Fixes: 59a556aebc43 ("SMB3: drop reference to cfile before sending oplock break") Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/cifs/file.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 9a4c33ffb75f..87dcffece762 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -5146,9 +5146,13 @@ oplock_break_ack: * disconnected since oplock already released by the server */ if (!oplock_break_cancelled) { - rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, + /* check for server null since can race with kill_sb calling tree disconnect */ + if (tcon->ses && tcon->ses->server) { + rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, volatile_fid, net_fid, cinode); - cifs_dbg(FYI, "Oplock release rc = %d\n", rc); + cifs_dbg(FYI, "Oplock release rc = %d\n", rc); + } else + pr_warn_once("lease break not sent for unmounted share\n"); } cifs_done_oplock_break(cinode); -- cgit 1.4.1 From fa285d799d1d5e73ad72b22a5edd58d799568c72 Mon Sep 17 00:00:00 2001 From: "Fabio M. De Francesco" Date: Wed, 14 Jun 2023 12:02:55 +0200 Subject: ext4: drop the call to ext4_error() from ext4_get_group_info() [ Upstream commit f451fd97dd2b78f286379203a47d9d295c467255 ] A recent patch added a call to ext4_error() which is problematic since some callers of the ext4_get_group_info() function may be holding a spinlock, whereas ext4_error() must never be called in atomic context. This triggered a report from Syzbot: "BUG: sleeping function called from invalid context in ext4_update_super" (see the link below). Therefore, drop the call to ext4_error() from ext4_get_group_info(). In the meantime use eight characters tabs instead of nine characters ones. Reported-by: syzbot+4acc7d910e617b360859@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/00000000000070575805fdc6cdb2@google.com/ Fixes: 5354b2af3406 ("ext4: allow ext4_get_group_info() to fail") Suggested-by: Theodore Ts'o Signed-off-by: Fabio M. De Francesco Link: https://lore.kernel.org/r/20230614100446.14337-1-fmdefrancesco@gmail.com Signed-off-by: Sasha Levin --- fs/ext4/balloc.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index a38aa33af08e..8e83b51e3c68 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -322,17 +322,15 @@ static ext4_fsblk_t ext4_valid_block_bitmap_padding(struct super_block *sb, struct ext4_group_info *ext4_get_group_info(struct super_block *sb, ext4_group_t group) { - struct ext4_group_info **grp_info; - long indexv, indexh; - - if (unlikely(group >= EXT4_SB(sb)->s_groups_count)) { - ext4_error(sb, "invalid group %u", group); - return NULL; - } - indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb)); - indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1); - grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv); - return grp_info[indexh]; + struct ext4_group_info **grp_info; + long indexv, indexh; + + if (unlikely(group >= EXT4_SB(sb)->s_groups_count)) + return NULL; + indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb)); + indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1); + grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv); + return grp_info[indexh]; } /* -- cgit 1.4.1 From 3626e93cd841d120f741af45b65fa41259c0c1fe Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 12 Jun 2023 11:34:26 +0200 Subject: net/sched: cls_api: Fix lockup on flushing explicitly created chain [ Upstream commit c9a82bec02c339cdda99b37c5e62b3b71fc4209c ] Mingshuai Ren reports: When a new chain is added by using tc, one soft lockup alarm will be generated after delete the prio 0 filter of the chain. To reproduce the problem, perform the following steps: (1) tc qdisc add dev eth0 root handle 1: htb default 1 (2) tc chain add dev eth0 (3) tc filter del dev eth0 chain 0 parent 1: prio 0 (4) tc filter add dev eth0 chain 0 parent 1: Fix the issue by accounting for additional reference to chains that are explicitly created by RTM_NEWCHAIN message as opposed to implicitly by RTM_NEWTFILTER message. Fixes: 726d061286ce ("net: sched: prevent insertion of new classifiers during chain flush") Reported-by: Mingshuai Ren Closes: https://lore.kernel.org/lkml/87legswvi3.fsf@nvidia.com/T/ Signed-off-by: Vlad Buslov Link: https://lore.kernel.org/r/20230612093426.2867183-1-vladbu@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_api.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 0dbfc37d9799..445ab1b0537d 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -552,8 +552,8 @@ static void __tcf_chain_put(struct tcf_chain *chain, bool by_act, { struct tcf_block *block = chain->block; const struct tcf_proto_ops *tmplt_ops; + unsigned int refcnt, non_act_refcnt; bool free_block = false; - unsigned int refcnt; void *tmplt_priv; mutex_lock(&block->lock); @@ -573,13 +573,15 @@ static void __tcf_chain_put(struct tcf_chain *chain, bool by_act, * save these to temporary variables. */ refcnt = --chain->refcnt; + non_act_refcnt = refcnt - chain->action_refcnt; tmplt_ops = chain->tmplt_ops; tmplt_priv = chain->tmplt_priv; - /* The last dropped non-action reference will trigger notification. */ - if (refcnt - chain->action_refcnt == 0 && !by_act) { - tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain->index, - block, NULL, 0, 0, false); + if (non_act_refcnt == chain->explicitly_created && !by_act) { + if (non_act_refcnt == 0) + tc_chain_notify_delete(tmplt_ops, tmplt_priv, + chain->index, block, NULL, 0, 0, + false); /* Last reference to chain, no need to lock. */ chain->flushing = false; } -- cgit 1.4.1 From 59f0c7bec3cacc53a69b28e04fdff65006242c6c Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 13 Jun 2023 20:09:07 +0300 Subject: net: dsa: felix: fix taprio guard band overflow at 10Mbps with jumbo frames [ Upstream commit 6ac7a27a8b07588497ed53dfd885df9c72bc67e0 ] The DEV_MAC_MAXLEN_CFG register contains a 16-bit value - up to 65535. Plus 2 * VLAN_HLEN (4), that is up to 65543. The picos_per_byte variable is the largest when "speed" is lowest - SPEED_10 = 10. In that case it is (1000000L * 8) / 10 = 800000. Their product - 52434400000 - exceeds 32 bits, which is a problem, because apparently, a multiplication between two 32-bit factors is evaluated as 32-bit before being assigned to a 64-bit variable. In fact it's a problem for any MTU value larger than 5368. Cast one of the factors of the multiplication to u64 to force the multiplication to take place on 64 bits. Issue found by Coverity. Fixes: 55a515b1f5a9 ("net: dsa: felix: drop oversized frames with tc-taprio instead of hanging the port") Signed-off-by: Vladimir Oltean Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230613170907.2413559-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/ocelot/felix_vsc9959.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index cc89cff029e1..5f6af0870dfd 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1253,7 +1253,7 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) /* Consider the standard Ethernet overhead of 8 octets preamble+SFD, * 4 octets FCS, 12 octets IFG. */ - needed_bit_time_ps = (maxlen + 24) * picos_per_byte; + needed_bit_time_ps = (u64)(maxlen + 24) * picos_per_byte; dev_dbg(ocelot->dev, "port %d: max frame size %d needs %llu ps at speed %d\n", -- cgit 1.4.1 From 4ea1f3344472943d14842d9ea14af95736122314 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 14 Jun 2023 16:18:02 +0000 Subject: net: lapbether: only support ethernet devices [ Upstream commit 9eed321cde22fc1afd76eac563ce19d899e0d6b2 ] It probbaly makes no sense to support arbitrary network devices for lapbether. syzbot reported: skbuff: skb_under_panic: text:ffff80008934c100 len:44 put:40 head:ffff0000d18dd200 data:ffff0000d18dd1ea tail:0x16 end:0x140 dev:bond1 kernel BUG at net/core/skbuff.c:200 ! Internal error: Oops - BUG: 00000000f2000800 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 5643 Comm: dhcpcd Not tainted 6.4.0-rc5-syzkaller-g4641cff8e810 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/25/2023 pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : skb_panic net/core/skbuff.c:196 [inline] pc : skb_under_panic+0x13c/0x140 net/core/skbuff.c:210 lr : skb_panic net/core/skbuff.c:196 [inline] lr : skb_under_panic+0x13c/0x140 net/core/skbuff.c:210 sp : ffff8000973b7260 x29: ffff8000973b7270 x28: ffff8000973b7360 x27: dfff800000000000 x26: ffff0000d85d8150 x25: 0000000000000016 x24: ffff0000d18dd1ea x23: ffff0000d18dd200 x22: 000000000000002c x21: 0000000000000140 x20: 0000000000000028 x19: ffff80008934c100 x18: ffff8000973b68a0 x17: 0000000000000000 x16: ffff80008a43bfbc x15: 0000000000000202 x14: 0000000000000000 x13: 0000000000000001 x12: 0000000000000001 x11: 0000000000000201 x10: 0000000000000000 x9 : f22f7eb937cced00 x8 : f22f7eb937cced00 x7 : 0000000000000001 x6 : 0000000000000001 x5 : ffff8000973b6b78 x4 : ffff80008df9ee80 x3 : ffff8000805974f4 x2 : 0000000000000001 x1 : 0000000100000201 x0 : 0000000000000086 Call trace: skb_panic net/core/skbuff.c:196 [inline] skb_under_panic+0x13c/0x140 net/core/skbuff.c:210 skb_push+0xf0/0x108 net/core/skbuff.c:2409 ip6gre_header+0xbc/0x738 net/ipv6/ip6_gre.c:1383 dev_hard_header include/linux/netdevice.h:3137 [inline] lapbeth_data_transmit+0x1c4/0x298 drivers/net/wan/lapbether.c:257 lapb_data_transmit+0x8c/0xb0 net/lapb/lapb_iface.c:447 lapb_transmit_buffer+0x178/0x204 net/lapb/lapb_out.c:149 lapb_send_control+0x220/0x320 net/lapb/lapb_subr.c:251 lapb_establish_data_link+0x94/0xec lapb_device_event+0x348/0x4e0 notifier_call_chain+0x1a4/0x510 kernel/notifier.c:93 raw_notifier_call_chain+0x3c/0x50 kernel/notifier.c:461 __dev_notify_flags+0x2bc/0x544 dev_change_flags+0xd0/0x15c net/core/dev.c:8643 devinet_ioctl+0x858/0x17e4 net/ipv4/devinet.c:1150 inet_ioctl+0x2ac/0x4d8 net/ipv4/af_inet.c:979 sock_do_ioctl+0x134/0x2dc net/socket.c:1201 sock_ioctl+0x4ec/0x858 net/socket.c:1318 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:870 [inline] __se_sys_ioctl fs/ioctl.c:856 [inline] __arm64_sys_ioctl+0x14c/0x1c8 fs/ioctl.c:856 __invoke_syscall arch/arm64/kernel/syscall.c:38 [inline] invoke_syscall+0x98/0x2c0 arch/arm64/kernel/syscall.c:52 el0_svc_common+0x138/0x244 arch/arm64/kernel/syscall.c:142 do_el0_svc+0x64/0x198 arch/arm64/kernel/syscall.c:191 el0_svc+0x4c/0x160 arch/arm64/kernel/entry-common.c:647 el0t_64_sync_handler+0x84/0xfc arch/arm64/kernel/entry-common.c:665 el0t_64_sync+0x190/0x194 arch/arm64/kernel/entry.S:591 Code: aa1803e6 aa1903e7 a90023f5 947730f5 (d4210000) Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Signed-off-by: Eric Dumazet Cc: Martin Schiller Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/wan/lapbether.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c index d62a904d2e42..56326f38fe8a 100644 --- a/drivers/net/wan/lapbether.c +++ b/drivers/net/wan/lapbether.c @@ -384,6 +384,9 @@ static int lapbeth_new_device(struct net_device *dev) ASSERT_RTNL(); + if (dev->type != ARPHRD_ETHER) + return -EINVAL; + ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", NET_NAME_UNKNOWN, lapbeth_setup); if (!ndev) -- cgit 1.4.1 From 23efdbfa8eefc39ae2ffb46b6009633626a0ff09 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 13 Jun 2023 22:22:20 +0300 Subject: net: macsec: fix double free of percpu stats [ Upstream commit 0c0cf3db83f8c7c9bb141c2771a34043bcf952ef ] Inside macsec_add_dev() we free percpu macsec->secy.tx_sc.stats and macsec->stats on some of the memory allocation failure paths. However, the net_device is already registered to that moment: in macsec_newlink(), just before calling macsec_add_dev(). This means that during unregister process its priv_destructor - macsec_free_netdev() - will be called and will free the stats again. Remove freeing percpu stats inside macsec_add_dev() because macsec_free_netdev() will correctly free the already allocated ones. The pointers to unallocated stats stay NULL, and free_percpu() treats that correctly. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 0a28bfd4971f ("net/macsec: Add MACsec skb_metadata_dst Tx Data path support") Fixes: c09440f7dcb3 ("macsec: introduce IEEE 802.1AE driver") Signed-off-by: Fedor Pchelkin Reviewed-by: Sabrina Dubroca Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/macsec.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 038a78794392..983cabf9a0f6 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3981,17 +3981,15 @@ static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len) return -ENOMEM; secy->tx_sc.stats = netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats); - if (!secy->tx_sc.stats) { - free_percpu(macsec->stats); + if (!secy->tx_sc.stats) return -ENOMEM; - } secy->tx_sc.md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL); - if (!secy->tx_sc.md_dst) { - free_percpu(secy->tx_sc.stats); - free_percpu(macsec->stats); + if (!secy->tx_sc.md_dst) + /* macsec and secy percpu stats will be freed when unregistering + * net_device in macsec_free_netdev() + */ return -ENOMEM; - } if (sci == MACSEC_UNDEF_SCI) sci = dev_to_sci(dev, MACSEC_PORT_ES); -- cgit 1.4.1 From 010179208c662bfb16f67869dfbb90cf8ec7d55f Mon Sep 17 00:00:00 2001 From: Íñigo Huguet Date: Tue, 13 Jun 2023 15:38:54 +0200 Subject: sfc: fix XDP queues mode with legacy IRQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e84a1e1e683f3558e30f437d7c99df35afb8b52c ] In systems without MSI-X capabilities, xdp_txq_queues_mode is calculated in efx_allocate_msix_channels, but when enabling MSI-X fails, it was not changed to a proper default value. This was leading to the driver thinking that it has dedicated XDP queues, when it didn't. Fix it by setting xdp_txq_queues_mode to the correct value if the driver fallbacks to MSI or legacy IRQ mode. The correct value is EFX_XDP_TX_QUEUES_BORROWED because there are no XDP dedicated queues. The issue can be easily visible if the kernel is started with pci=nomsi, then a call trace is shown. It is not shown only with sfc's modparam interrupt_mode=2. Call trace example: WARNING: CPU: 2 PID: 663 at drivers/net/ethernet/sfc/efx_channels.c:828 efx_set_xdp_channels+0x124/0x260 [sfc] [...skip...] Call Trace: efx_set_channels+0x5c/0xc0 [sfc] efx_probe_nic+0x9b/0x15a [sfc] efx_probe_all+0x10/0x1a2 [sfc] efx_pci_probe_main+0x12/0x156 [sfc] efx_pci_probe_post_io+0x18/0x103 [sfc] efx_pci_probe.cold+0x154/0x257 [sfc] local_pci_probe+0x42/0x80 Fixes: 6215b608a8c4 ("sfc: last resort fallback for lack of xdp tx queues") Reported-by: Yanghang Liu Signed-off-by: Íñigo Huguet Acked-by: Martin Habets Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/sfc/efx_channels.c | 2 ++ drivers/net/ethernet/sfc/siena/efx_channels.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index aaa381743bca..27d00ffac68f 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -301,6 +301,7 @@ int efx_probe_interrupts(struct efx_nic *efx) efx->tx_channel_offset = 0; efx->n_xdp_channels = 0; efx->xdp_channel_offset = efx->n_channels; + efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; rc = pci_enable_msi(efx->pci_dev); if (rc == 0) { efx_get_channel(efx, 0)->irq = efx->pci_dev->irq; @@ -322,6 +323,7 @@ int efx_probe_interrupts(struct efx_nic *efx) efx->tx_channel_offset = efx_separate_tx_channels ? 1 : 0; efx->n_xdp_channels = 0; efx->xdp_channel_offset = efx->n_channels; + efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; efx->legacy_irq = efx->pci_dev->irq; } diff --git a/drivers/net/ethernet/sfc/siena/efx_channels.c b/drivers/net/ethernet/sfc/siena/efx_channels.c index 06ed74994e36..1776f7f8a7a9 100644 --- a/drivers/net/ethernet/sfc/siena/efx_channels.c +++ b/drivers/net/ethernet/sfc/siena/efx_channels.c @@ -302,6 +302,7 @@ int efx_siena_probe_interrupts(struct efx_nic *efx) efx->tx_channel_offset = 0; efx->n_xdp_channels = 0; efx->xdp_channel_offset = efx->n_channels; + efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; rc = pci_enable_msi(efx->pci_dev); if (rc == 0) { efx_get_channel(efx, 0)->irq = efx->pci_dev->irq; @@ -323,6 +324,7 @@ int efx_siena_probe_interrupts(struct efx_nic *efx) efx->tx_channel_offset = efx_siena_separate_tx_channels ? 1 : 0; efx->n_xdp_channels = 0; efx->xdp_channel_offset = efx->n_channels; + efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; efx->legacy_irq = efx->pci_dev->irq; } -- cgit 1.4.1 From d24c9658177587c0e64677691fbc35b2df79b1e8 Mon Sep 17 00:00:00 2001 From: Li Lingfeng Date: Thu, 1 Jun 2023 14:14:23 +0800 Subject: dm: don't lock fs when the map is NULL during suspend or resume [ Upstream commit 2760904d895279f87196f0fa9ec570c79fe6a2e4 ] As described in commit 38d11da522aa ("dm: don't lock fs when the map is NULL in process of resume"), a deadlock may be triggered between do_resume() and do_mount(). This commit preserves the fix from commit 38d11da522aa but moves it to where it also serves to fix a similar deadlock between do_suspend() and do_mount(). It does so, if the active map is NULL, by clearing DM_SUSPEND_LOCKFS_FLAG in dm_suspend() which is called by both do_suspend() and do_resume(). Fixes: 38d11da522aa ("dm: don't lock fs when the map is NULL in process of resume") Signed-off-by: Li Lingfeng Signed-off-by: Mike Snitzer Signed-off-by: Sasha Levin --- drivers/md/dm-ioctl.c | 5 +---- drivers/md/dm.c | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 83aecd9250ba..6ae1c19b8243 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1151,13 +1151,10 @@ static int do_resume(struct dm_ioctl *param) /* Do we need to load a new map ? */ if (new_map) { sector_t old_size, new_size; - int srcu_idx; /* Suspend if it isn't already suspended */ - old_map = dm_get_live_table(md, &srcu_idx); - if ((param->flags & DM_SKIP_LOCKFS_FLAG) || !old_map) + if (param->flags & DM_SKIP_LOCKFS_FLAG) suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; - dm_put_live_table(md, srcu_idx); if (param->flags & DM_NOFLUSH_FLAG) suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; if (!dm_suspended_md(md)) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 24284d22f15b..acf7e7551c94 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2801,6 +2801,10 @@ retry: } map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock)); + if (!map) { + /* avoid deadlock with fs/namespace.c:do_mount() */ + suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; + } r = __dm_suspend(md, map, suspend_flags, TASK_INTERRUPTIBLE, DMF_SUSPENDED); if (r) -- cgit 1.4.1 From 6ab77b3b852d20956f915d67142e1e87f3ca7720 Mon Sep 17 00:00:00 2001 From: Lin Ma Date: Wed, 14 Jun 2023 20:06:04 +0800 Subject: net: tipc: resize nlattr array to correct size [ Upstream commit 44194cb1b6045dea33ae9a0d54fb7e7cd93a2e09 ] According to nla_parse_nested_deprecated(), the tb[] is supposed to the destination array with maxtype+1 elements. In current tipc_nl_media_get() and __tipc_nl_media_set(), a larger array is used which is unnecessary. This patch resize them to a proper size. Fixes: 1e55417d8fc6 ("tipc: add media set to new netlink api") Fixes: 46f15c6794fb ("tipc: add media get/dump to new netlink api") Signed-off-by: Lin Ma Reviewed-by: Florian Westphal Reviewed-by: Tung Nguyen Link: https://lore.kernel.org/r/20230614120604.1196377-1-linma@zju.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/bearer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 53881406e200..cdcd2731860b 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -1258,7 +1258,7 @@ int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info) struct tipc_nl_msg msg; struct tipc_media *media; struct sk_buff *rep; - struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1]; if (!info->attrs[TIPC_NLA_MEDIA]) return -EINVAL; @@ -1307,7 +1307,7 @@ int __tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info) int err; char *name; struct tipc_media *m; - struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1]; if (!info->attrs[TIPC_NLA_MEDIA]) return -EINVAL; -- cgit 1.4.1 From 35d848164fec99faca4a99bf12cd02245e0d6675 Mon Sep 17 00:00:00 2001 From: Alex Maftei Date: Thu, 15 Jun 2023 09:34:04 +0100 Subject: selftests/ptp: Fix timestamp printf format for PTP_SYS_OFFSET [ Upstream commit 76a4c8b82938bc5020b67663db41f451684bf327 ] Previously, timestamps were printed using "%lld.%u" which is incorrect for nanosecond values lower than 100,000,000 as they're fractional digits, therefore leading zeros are meaningful. This patch changes the format strings to "%lld.%09u" in order to add leading zeros to the nanosecond value. Fixes: 568ebc5985f5 ("ptp: add the PTP_SYS_OFFSET ioctl to the testptp program") Fixes: 4ec54f95736f ("ptp: Fix compiler warnings in the testptp utility") Fixes: 6ab0e475f1f3 ("Documentation: fix misc. warnings") Signed-off-by: Alex Maftei Acked-by: Richard Cochran Link: https://lore.kernel.org/r/20230615083404.57112-1-alex.maftei@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/ptp/testptp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c index 198ad5f32187..cfa9562f3cd8 100644 --- a/tools/testing/selftests/ptp/testptp.c +++ b/tools/testing/selftests/ptp/testptp.c @@ -502,11 +502,11 @@ int main(int argc, char *argv[]) interval = t2 - t1; offset = (t2 + t1) / 2 - tp; - printf("system time: %lld.%u\n", + printf("system time: %lld.%09u\n", (pct+2*i)->sec, (pct+2*i)->nsec); - printf("phc time: %lld.%u\n", + printf("phc time: %lld.%09u\n", (pct+2*i+1)->sec, (pct+2*i+1)->nsec); - printf("system time: %lld.%u\n", + printf("system time: %lld.%09u\n", (pct+2*i+2)->sec, (pct+2*i+2)->nsec); printf("system/phc clock time offset is %" PRId64 " ns\n" "system clock time delay is %" PRId64 " ns\n", -- cgit 1.4.1 From 34dc1eed9918b1883de936ca2966130f5e8852ce Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Thu, 15 Jun 2023 11:34:00 +0800 Subject: octeon_ep: Add missing check for ioremap [ Upstream commit 9a36e2d44d122fe73a2a76ba73f1d50a65cf8210 ] Add check for ioremap() and return the error if it fails in order to guarantee the success of ioremap(). Fixes: 862cd659a6fb ("octeon_ep: Add driver framework and device initialization") Signed-off-by: Jiasheng Jiang Reviewed-by: Kalesh AP Link: https://lore.kernel.org/r/20230615033400.2971-1-jiasheng@iscas.ac.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeon_ep/octep_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c index b45dd7f04e21..8979dd05e873 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c @@ -928,6 +928,9 @@ int octep_device_setup(struct octep_device *oct) oct->mmio[i].hw_addr = ioremap(pci_resource_start(oct->pdev, i * 2), pci_resource_len(oct->pdev, i * 2)); + if (!oct->mmio[i].hw_addr) + goto unmap_prev; + oct->mmio[i].mapped = 1; } @@ -966,7 +969,9 @@ int octep_device_setup(struct octep_device *oct) return 0; unsupported_dev: - for (i = 0; i < OCTEP_MMIO_REGIONS; i++) + i = OCTEP_MMIO_REGIONS; +unmap_prev: + while (i--) iounmap(oct->mmio[i].hw_addr); kfree(oct->conf); -- cgit 1.4.1 From 616aba553640d68656e30f6302e0fbc9dd8285c1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 16 Jun 2023 22:39:39 +0100 Subject: afs: Fix vlserver probe RTT handling [ Upstream commit ba00b190670809c1a89326d80de96d714f6004f2 ] In the same spirit as commit ca57f02295f1 ("afs: Fix fileserver probe RTT handling"), don't rule out using a vlserver just because there haven't been enough packets yet to calculate a real rtt. Always set the server's probe rtt from the estimate provided by rxrpc_kernel_get_srtt, which is capped at 1 second. This could lead to EDESTADDRREQ errors when accessing a cell for the first time, even though the vl servers are known and have responded to a probe. Fixes: 1d4adfaf6574 ("rxrpc: Make rxrpc_kernel_get_srtt() indicate validity") Signed-off-by: Marc Dionne Signed-off-by: David Howells cc: linux-afs@lists.infradead.org Link: http://lists.infradead.org/pipermail/linux-afs/2023-June/006746.html Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin --- fs/afs/vl_probe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/afs/vl_probe.c b/fs/afs/vl_probe.c index d1c7068b4346..58452b86e672 100644 --- a/fs/afs/vl_probe.c +++ b/fs/afs/vl_probe.c @@ -115,8 +115,8 @@ responded: } } - if (rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us) && - rtt_us < server->probe.rtt) { + rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us); + if (rtt_us < server->probe.rtt) { server->probe.rtt = rtt_us; server->rtt = rtt_us; alist->preferred = index; -- cgit 1.4.1 From 8d842af30bc25019b60a58eef3fcf60f5db2df7a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 15 Jun 2023 00:00:02 +0200 Subject: parisc: Delete redundant register definitions in commit b5b2a02bcaac7c287694aa0db4837a07bf178626 upstream. We define sp and ipsw in using ".reg", and when using current binutils (snapshot 2.40.50.20230611) the definitions in using "=" conflict with those: arch/parisc/include/asm/assembly.h: Assembler messages: arch/parisc/include/asm/assembly.h:93: Error: symbol `sp' is already defined arch/parisc/include/asm/assembly.h:95: Error: symbol `ipsw' is already defined Delete the duplicate definitions in . Also delete the definition of gp, which isn't used anywhere. Signed-off-by: Ben Hutchings Cc: stable@vger.kernel.org # v6.0+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/include/asm/assembly.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/parisc/include/asm/assembly.h b/arch/parisc/include/asm/assembly.h index 0f0d4a496fef..75677b526b2b 100644 --- a/arch/parisc/include/asm/assembly.h +++ b/arch/parisc/include/asm/assembly.h @@ -90,10 +90,6 @@ #include #include - sp = 30 - gp = 27 - ipsw = 22 - /* * We provide two versions of each macro to convert from physical * to virtual and vice versa. The "_r1" versions take one argument -- cgit 1.4.1 From c6cbb4e1c10c2b1592528fa29d49f9b6c52a7396 Mon Sep 17 00:00:00 2001 From: Ziwei Dai Date: Fri, 31 Mar 2023 20:42:09 +0800 Subject: rcu/kvfree: Avoid freeing new kfree_rcu() memory after old grace period commit 5da7cb193db32da783a3f3e77d8b639989321d48 upstream. Memory passed to kvfree_rcu() that is to be freed is tracked by a per-CPU kfree_rcu_cpu structure, which in turn contains pointers to kvfree_rcu_bulk_data structures that contain pointers to memory that has not yet been handed to RCU, along with an kfree_rcu_cpu_work structure that tracks the memory that has already been handed to RCU. These structures track three categories of memory: (1) Memory for kfree(), (2) Memory for kvfree(), and (3) Memory for both that arrived during an OOM episode. The first two categories are tracked in a cache-friendly manner involving a dynamically allocated page of pointers (the aforementioned kvfree_rcu_bulk_data structures), while the third uses a simple (but decidedly cache-unfriendly) linked list through the rcu_head structures in each block of memory. On a given CPU, these three categories are handled as a unit, with that CPU's kfree_rcu_cpu_work structure having one pointer for each of the three categories. Clearly, new memory for a given category cannot be placed in the corresponding kfree_rcu_cpu_work structure until any old memory has had its grace period elapse and thus has been removed. And the kfree_rcu_monitor() function does in fact check for this. Except that the kfree_rcu_monitor() function checks these pointers one at a time. This means that if the previous kfree_rcu() memory passed to RCU had only category 1 and the current one has only category 2, the kfree_rcu_monitor() function will send that current category-2 memory along immediately. This can result in memory being freed too soon, that is, out from under unsuspecting RCU readers. To see this, consider the following sequence of events, in which: o Task A on CPU 0 calls rcu_read_lock(), then uses "from_cset", then is preempted. o CPU 1 calls kfree_rcu(cset, rcu_head) in order to free "from_cset" after a later grace period. Except that "from_cset" is freed right after the previous grace period ended, so that "from_cset" is immediately freed. Task A resumes and references "from_cset"'s member, after which nothing good happens. In full detail: CPU 0 CPU 1 ---------------------- ---------------------- count_memcg_event_mm() |rcu_read_lock() <--- |mem_cgroup_from_task() |// css_set_ptr is the "from_cset" mentioned on CPU 1 |css_set_ptr = rcu_dereference((task)->cgroups) |// Hard irq comes, current task is scheduled out. cgroup_attach_task() |cgroup_migrate() |cgroup_migrate_execute() |css_set_move_task(task, from_cset, to_cset, true) |cgroup_move_task(task, to_cset) |rcu_assign_pointer(.., to_cset) |... |cgroup_migrate_finish() |put_css_set_locked(from_cset) |from_cset->refcount return 0 |kfree_rcu(cset, rcu_head) // free from_cset after new gp |add_ptr_to_bulk_krc_lock() |schedule_delayed_work(&krcp->monitor_work, ..) kfree_rcu_monitor() |krcp->bulk_head[0]'s work attached to krwp->bulk_head_free[] |queue_rcu_work(system_wq, &krwp->rcu_work) |if rwork->rcu.work is not in WORK_STRUCT_PENDING_BIT state, |call_rcu(&rwork->rcu, rcu_work_rcufn) <--- request new gp // There is a perious call_rcu(.., rcu_work_rcufn) // gp end, rcu_work_rcufn() is called. rcu_work_rcufn() |__queue_work(.., rwork->wq, &rwork->work); |kfree_rcu_work() |krwp->bulk_head_free[0] bulk is freed before new gp end!!! |The "from_cset" is freed before new gp end. // the task resumes some time later. |css_set_ptr->subsys[(subsys_id) <--- Caused kernel crash, because css_set_ptr is freed. This commit therefore causes kfree_rcu_monitor() to refrain from moving kfree_rcu() memory to the kfree_rcu_cpu_work structure until the RCU grace period has completed for all three categories. v2: Use helper function instead of inserted code block at kfree_rcu_monitor(). Fixes: 34c881745549 ("rcu: Support kfree_bulk() interface in kfree_rcu()") Fixes: 5f3c8d620447 ("rcu/tree: Maintain separate array for vmalloc ptrs") Reported-by: Mukesh Ojha Signed-off-by: Ziwei Dai Reviewed-by: Uladzislau Rezki (Sony) Tested-by: Uladzislau Rezki (Sony) Signed-off-by: Paul E. McKenney Signed-off-by: Suren Baghdasaryan Signed-off-by: Greg Kroah-Hartman --- kernel/rcu/tree.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index ce34ca0b5b98..d03122f90cc4 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3099,6 +3099,18 @@ need_offload_krc(struct kfree_rcu_cpu *krcp) return !!krcp->head; } +static bool +need_wait_for_krwp_work(struct kfree_rcu_cpu_work *krwp) +{ + int i; + + for (i = 0; i < FREE_N_CHANNELS; i++) + if (krwp->bkvhead_free[i]) + return true; + + return !!krwp->head_free; +} + static void schedule_delayed_monitor_work(struct kfree_rcu_cpu *krcp) { @@ -3130,14 +3142,13 @@ static void kfree_rcu_monitor(struct work_struct *work) for (i = 0; i < KFREE_N_BATCHES; i++) { struct kfree_rcu_cpu_work *krwp = &(krcp->krw_arr[i]); - // Try to detach bkvhead or head and attach it over any - // available corresponding free channel. It can be that - // a previous RCU batch is in progress, it means that - // immediately to queue another one is not possible so - // in that case the monitor work is rearmed. - if ((krcp->bkvhead[0] && !krwp->bkvhead_free[0]) || - (krcp->bkvhead[1] && !krwp->bkvhead_free[1]) || - (krcp->head && !krwp->head_free)) { + // Try to detach bulk_head or head and attach it, only when + // all channels are free. Any channel is not free means at krwp + // there is on-going rcu work to handle krwp's free business. + if (need_wait_for_krwp_work(krwp)) + continue; + + if (need_offload_krc(krcp)) { // Channel 1 corresponds to the SLAB-pointer bulk path. // Channel 2 corresponds to vmalloc-pointer bulk path. for (j = 0; j < FREE_N_CHANNELS; j++) { -- cgit 1.4.1 From 4b4cae8e4b37c7ad4d9721dbda060a62e49e3c17 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 3 Nov 2022 16:14:27 +0100 Subject: drm/amdgpu: Don't set struct drm_driver.output_poll_changed commit 0e3172bac3f43759719384403fe2d1e4c61f87e0 upstream. Don't set struct drm_driver.output_poll_changed. It's used to restore the fbdev console. But as amdgpu uses generic fbdev emulation, the console is being restored by the DRM client helpers already. See the functions drm_kms_helper_hotplug_event() and drm_kms_helper_connector_hotplug_event() in drm_probe_helper.c. v2: * fix commit description (Christian) Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20221103151446.2638-5-tzimmermann@suse.de Cc: "Gagniuc, Alexandru" Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_display.c | 1 - drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index 1a06b8d724f3..dd6f9ae6fbe9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -1214,7 +1214,6 @@ amdgpu_display_user_framebuffer_create(struct drm_device *dev, const struct drm_mode_config_funcs amdgpu_mode_funcs = { .fb_create = amdgpu_display_user_framebuffer_create, - .output_poll_changed = drm_fb_helper_output_poll_changed, }; static const struct drm_prop_enum_list amdgpu_underscan_enum_list[] = diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index fe179261f327..53687de6c053 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -83,7 +83,6 @@ #include #include #include -#include #include #include #include @@ -2875,7 +2874,6 @@ const struct amdgpu_ip_block_version dm_ip_block = static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = { .fb_create = amdgpu_display_user_framebuffer_create, .get_format_info = amd_get_format_info, - .output_poll_changed = drm_fb_helper_output_poll_changed, .atomic_check = amdgpu_dm_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; -- cgit 1.4.1 From 8f375998110473702d284edd544f6bd096b2338d Mon Sep 17 00:00:00 2001 From: Pedro Tammela Date: Fri, 24 Feb 2023 14:56:01 -0300 Subject: net/sched: act_api: move TCA_EXT_WARN_MSG to the correct hierarchy commit 923b2e30dc9cd05931da0f64e2e23d040865c035 upstream. TCA_EXT_WARN_MSG is currently sitting outside of the expected hierarchy for the tc actions code. It should sit within TCA_ACT_TAB. Fixes: 0349b8779cc9 ("sched: add new attr TCA_EXT_WARN_MSG to report tc extact message") Reviewed-by: Jamal Hadi Salim Signed-off-by: Pedro Tammela Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sched/act_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index cc6628a42e83..2bdcaf4f9285 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1603,12 +1603,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], if (tcf_action_dump(skb, actions, bind, ref, false) < 0) goto out_nlmsg_trim; - nla_nest_end(skb, nest); - if (extack && extack->_msg && nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) goto out_nlmsg_trim; + nla_nest_end(skb, nest); + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit 1.4.1 From ab1bbd79f48ec35fe17c04a657ca53216cc8f7d1 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Thu, 16 Mar 2023 11:37:52 +0800 Subject: Revert "net/sched: act_api: move TCA_EXT_WARN_MSG to the correct hierarchy" commit 8de2bd02439eb839a452a853c1004c2c45ff6fef upstream. This reverts commit 923b2e30dc9cd05931da0f64e2e23d040865c035. This is not a correct fix as TCA_EXT_WARN_MSG is not a hierarchy to TCA_ACT_TAB. I didn't notice the TC actions use different enum when adding TCA_EXT_WARN_MSG. To fix the difference I will add a new WARN enum in TCA_ROOT_MAX as Jamal suggested. Signed-off-by: Hangbin Liu Acked-by: Jamal Hadi Salim Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/act_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 2bdcaf4f9285..cc6628a42e83 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1603,12 +1603,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], if (tcf_action_dump(skb, actions, bind, ref, false) < 0) goto out_nlmsg_trim; + nla_nest_end(skb, nest); + if (extack && extack->_msg && nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) goto out_nlmsg_trim; - nla_nest_end(skb, nest); - nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit 1.4.1 From 26435338f9dd9f085fe8fb605de3efc6c52a1f48 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Thu, 16 Mar 2023 11:37:53 +0800 Subject: net/sched: act_api: add specific EXT_WARN_MSG for tc action commit 2f59823fe696caa844249a90bb3f9aeda69cfe5c upstream. In my previous commit 0349b8779cc9 ("sched: add new attr TCA_EXT_WARN_MSG to report tc extact message") I didn't notice the tc action use different enum with filter. So we can't use TCA_EXT_WARN_MSG directly for tc action. Let's add a TCA_ROOT_EXT_WARN_MSG for tc action specifically and put this param before going to the TCA_ACT_TAB nest. Fixes: 0349b8779cc9 ("sched: add new attr TCA_EXT_WARN_MSG to report tc extact message") Signed-off-by: Hangbin Liu Acked-by: Jamal Hadi Salim Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/rtnetlink.h | 1 + net/sched/act_api.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 25a0af57dd5e..51c13cf9c5ae 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -789,6 +789,7 @@ enum { TCA_ROOT_FLAGS, TCA_ROOT_COUNT, TCA_ROOT_TIME_DELTA, /* in msecs */ + TCA_ROOT_EXT_WARN_MSG, __TCA_ROOT_MAX, #define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) }; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index cc6628a42e83..b33f88e50aa9 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1596,6 +1596,10 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], t->tca__pad1 = 0; t->tca__pad2 = 0; + if (extack && extack->_msg && + nla_put_string(skb, TCA_ROOT_EXT_WARN_MSG, extack->_msg)) + goto out_nlmsg_trim; + nest = nla_nest_start_noflag(skb, TCA_ACT_TAB); if (!nest) goto out_nlmsg_trim; @@ -1605,10 +1609,6 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], nla_nest_end(skb, nest); - if (extack && extack->_msg && - nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) - goto out_nlmsg_trim; - nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; -- cgit 1.4.1 From 741c96715fb752556a81e6c616b7d7aed65ad605 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 8 Mar 2023 11:23:13 +0200 Subject: neighbour: delete neigh_lookup_nodev as not used commit 76b9bf965c98c9b53ef7420b3b11438dbd764f92 upstream. neigh_lookup_nodev isn't used in the kernel after removal of DECnet. So let's remove it. Fixes: 1202cdd66531 ("Remove DECnet support from kernel") Signed-off-by: Leon Romanovsky Reviewed-by: Eric Dumazet Reviewed-by: Nikolay Aleksandrov Link: https://lore.kernel.org/r/eb5656200d7964b2d177a36b77efa3c597d6d72d.1678267343.git.leonro@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/net/neighbour.h | 2 -- net/core/neighbour.c | 31 ------------------------------- 2 files changed, 33 deletions(-) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 94a1599824d8..794e45981891 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -336,8 +336,6 @@ void neigh_table_init(int index, struct neigh_table *tbl); int neigh_table_clear(int index, struct neigh_table *tbl); struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev); -struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, - const void *pkey); struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, struct net_device *dev, bool want_ref); static inline struct neighbour *neigh_create(struct neigh_table *tbl, diff --git a/net/core/neighbour.c b/net/core/neighbour.c index bf081f62ae58..6c0f2149f2c7 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -627,37 +627,6 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, } EXPORT_SYMBOL(neigh_lookup); -struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, - const void *pkey) -{ - struct neighbour *n; - unsigned int key_len = tbl->key_len; - u32 hash_val; - struct neigh_hash_table *nht; - - NEIGH_CACHE_STAT_INC(tbl, lookups); - - rcu_read_lock_bh(); - nht = rcu_dereference_bh(tbl->nht); - hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift); - - for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); - n != NULL; - n = rcu_dereference_bh(n->next)) { - if (!memcmp(n->primary_key, pkey, key_len) && - net_eq(dev_net(n->dev), net)) { - if (!refcount_inc_not_zero(&n->refcnt)) - n = NULL; - NEIGH_CACHE_STAT_INC(tbl, hits); - break; - } - } - - rcu_read_unlock_bh(); - return n; -} -EXPORT_SYMBOL(neigh_lookup_nodev); - static struct neighbour * ___neigh_create(struct neigh_table *tbl, const void *pkey, struct net_device *dev, u32 flags, -- cgit 1.4.1 From bdd22f2aa13176df68cd461af6a1bea6e5f991a3 Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Tue, 13 Jun 2023 09:43:00 -0500 Subject: scsi: target: core: Fix error path in target_setup_session() commit 91271699228bfc66f1bc8abc0327169dc156d854 upstream. In the error exits in target_setup_session(), if a branch is taken to free_sess: transport_free_session() may call to target_free_cmd_counter() and then fall through to call target_free_cmd_counter() a second time. This can, and does, sometimes cause seg faults since the data field in cmd_cnt->refcnt has been freed in the first call. Fix this problem by simply returning after the call to transport_free_session(). The second call is redundant for those cases. Fixes: 4edba7e4a8f3 ("scsi: target: Move cmd counter allocation") Signed-off-by: Bob Pearson Link: https://lore.kernel.org/r/20230613144259.12890-1-rpearsonhpe@gmail.com Reviewed-by: Mike Christie Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_transport.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 86adff2a86ed..687adc9e086c 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -504,6 +504,8 @@ target_setup_session(struct se_portal_group *tpg, free_sess: transport_free_session(sess); + return ERR_PTR(rc); + free_cnt: target_free_cmd_counter(cmd_cnt); return ERR_PTR(rc); -- cgit 1.4.1 From d51d258997e5ec2f3e67972ebb0983cddec1e7b9 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Wed, 14 Jun 2023 11:04:35 -0700 Subject: x86/boot/compressed: prefer cc-option for CFLAGS additions commit 994f5f7816ff963f49269cfc97f63cb2e4edb84f upstream. as-option tests new options using KBUILD_CFLAGS, which causes problems when using as-option to update KBUILD_AFLAGS because many compiler options are not valid assembler options. This will be fixed in a follow up patch. Before doing so, move the assembler test for -Wa,-mrelax-relocations=no from using as-option to cc-option. Link: https://lore.kernel.org/llvm/CAK7LNATcHt7GcXZ=jMszyH=+M_LC9Qr6yeAGRCBbE6xriLxtUQ@mail.gmail.com/ Suggested-by: Masahiro Yamada Reviewed-by: Nathan Chancellor Tested-by: Nathan Chancellor Signed-off-by: Nick Desaulniers Signed-off-by: Nathan Chancellor Tested-by: Linux Kernel Functional Testing Tested-by: Anders Roxell Signed-off-by: Masahiro Yamada Signed-off-by: Nathan Chancellor Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/compressed/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 3a261abb6d15..15b7b403a4bd 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -50,7 +50,7 @@ KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=) KBUILD_CFLAGS += -fno-asynchronous-unwind-tables KBUILD_CFLAGS += -D__DISABLE_EXPORTS # Disable relocation relaxation in case the link is not PIE. -KBUILD_CFLAGS += $(call as-option,-Wa$(comma)-mrelax-relocations=no) +KBUILD_CFLAGS += $(call cc-option,-Wa$(comma)-mrelax-relocations=no) KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h # sev.c indirectly inludes inat-table.h which is generated during -- cgit 1.4.1 From 1d485ddcba85840542f5a4f3b4bbe446dec73309 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 14 Jun 2023 11:04:36 -0700 Subject: MIPS: Move '-Wa,-msoft-float' check from as-option to cc-option This patch is for linux-6.1.y and earlier, it has no direct mainline equivalent. In order to backport commit d5c8d6e0fa61 ("kbuild: Update assembler calls to use proper flags and language target") to resolve a separate issue regarding PowerPC, the problem noticed and fixed by commit 80a20d2f8288 ("MIPS: Always use -Wa,-msoft-float and eliminate GAS_HAS_SET_HARDFLOAT") needs to be addressed. Unfortunately, 6.1 and earlier do not contain commit e4412739472b ("Documentation: raise minimum supported version of binutils to 2.25"), so it cannot be assumed that all supported versions of GNU as have support for -msoft-float. In order to switch from KBUILD_CFLAGS to KBUILD_AFLAGS in as-option without consequence, move the '-Wa,-msoft-float' check to cc-option, including '$(cflags-y)' directly to avoid the issue mentioned in commit 80a20d2f8288 ("MIPS: Always use -Wa,-msoft-float and eliminate GAS_HAS_SET_HARDFLOAT"). Signed-off-by: Nathan Chancellor Signed-off-by: Greg Kroah-Hartman --- arch/mips/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/Makefile b/arch/mips/Makefile index b296e33f8e33..de8d508f27af 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -109,7 +109,7 @@ endif # (specifically newer than 2.24.51.20140728) we then also need to explicitly # set ".set hardfloat" in all files which manipulate floating point registers. # -ifneq ($(call as-option,-Wa$(comma)-msoft-float,),) +ifneq ($(call cc-option,$(cflags-y) -Wa$(comma)-msoft-float,),) cflags-y += -DGAS_HAS_SET_HARDFLOAT -Wa,-msoft-float endif -- cgit 1.4.1 From 5abcd2c18dbbbf676d235ab5ac568f3b836e32e9 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 14 Jun 2023 11:04:37 -0700 Subject: MIPS: Prefer cc-option for additions to cflags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 337ff6bb8960fdc128cabd264aaea3d42ca27a32 upstream. A future change will switch as-option to use KBUILD_AFLAGS instead of KBUILD_CFLAGS to allow clang to drop -Qunused-arguments, which may cause issues if the flag being tested requires a flag previously added to KBUILD_CFLAGS but not KBUILD_AFLAGS. Use cc-option for cflags additions so that the flags are tested properly. Signed-off-by: Nathan Chancellor Acked-by: Thomas Bogendoerfer Reviewed-by: Nick Desaulniers Reviewed-by: Philippe Mathieu-Daudé Tested-by: Linux Kernel Functional Testing Tested-by: Anders Roxell Signed-off-by: Masahiro Yamada Signed-off-by: Nathan Chancellor Signed-off-by: Greg Kroah-Hartman --- arch/mips/Makefile | 2 +- arch/mips/loongson2ef/Platform | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/Makefile b/arch/mips/Makefile index de8d508f27af..85d3c3b4b7bd 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -152,7 +152,7 @@ cflags-y += -fno-stack-check # # Avoid this by explicitly disabling that assembler behaviour. # -cflags-y += $(call as-option,-Wa$(comma)-mno-fix-loongson3-llsc,) +cflags-y += $(call cc-option,-Wa$(comma)-mno-fix-loongson3-llsc,) # # CPU-dependent compiler/assembler options for optimization. diff --git a/arch/mips/loongson2ef/Platform b/arch/mips/loongson2ef/Platform index eebabf9df6ac..c6f7a4b95997 100644 --- a/arch/mips/loongson2ef/Platform +++ b/arch/mips/loongson2ef/Platform @@ -25,7 +25,7 @@ cflags-$(CONFIG_CPU_LOONGSON2F) += -march=loongson2f # binutils does not merge support for the flag then we can revisit & remove # this later - for now it ensures vendor toolchains don't cause problems. # -cflags-$(CONFIG_CPU_LOONGSON2EF) += $(call as-option,-Wa$(comma)-mno-fix-loongson3-llsc,) +cflags-$(CONFIG_CPU_LOONGSON2EF) += $(call cc-option,-Wa$(comma)-mno-fix-loongson3-llsc,) # Enable the workarounds for Loongson2f ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS -- cgit 1.4.1 From a76d4933c38ec352e813e14be1de2558db486f27 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Wed, 14 Jun 2023 11:04:38 -0700 Subject: kbuild: Update assembler calls to use proper flags and language target commit d5c8d6e0fa61401a729e9eb6a9c7077b2d3aebb0 upstream. as-instr uses KBUILD_AFLAGS, but as-option uses KBUILD_CFLAGS. This can cause as-option to fail unexpectedly when CONFIG_WERROR is set, because clang will emit -Werror,-Wunused-command-line-argument for various -m and -f flags in KBUILD_CFLAGS for assembler sources. Callers of as-option and as-instr should be adding flags to KBUILD_AFLAGS / aflags-y, not KBUILD_CFLAGS / cflags-y. Use KBUILD_AFLAGS in all macros to clear up the initial problem. Unfortunately, -Wunused-command-line-argument can still be triggered with clang by the presence of warning flags or macro definitions because '-x assembler' is used, instead of '-x assembler-with-cpp', which will consume these flags. Switch to '-x assembler-with-cpp' in places where '-x assembler' is used, as the compiler is always used as the driver for out of line assembler sources in the kernel. Finally, add -Werror to these macros so that they behave consistently whether or not CONFIG_WERROR is set. [nathan: Reworded and expanded on problems in commit message Use '-x assembler-with-cpp' in a couple more places] Link: https://github.com/ClangBuiltLinux/linux/issues/1699 Suggested-by: Masahiro Yamada Signed-off-by: Nick Desaulniers Signed-off-by: Nathan Chancellor Tested-by: Linux Kernel Functional Testing Tested-by: Anders Roxell Signed-off-by: Masahiro Yamada Signed-off-by: Nathan Chancellor Signed-off-by: Greg Kroah-Hartman --- scripts/Kconfig.include | 2 +- scripts/Makefile.compiler | 8 ++++---- scripts/as-version.sh | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include index 274125307ebd..5a84b6443875 100644 --- a/scripts/Kconfig.include +++ b/scripts/Kconfig.include @@ -33,7 +33,7 @@ ld-option = $(success,$(LD) -v $(1)) # $(as-instr,) # Return y if the assembler supports , n otherwise -as-instr = $(success,printf "%b\n" "$(1)" | $(CC) $(CLANG_FLAGS) -c -x assembler -o /dev/null -) +as-instr = $(success,printf "%b\n" "$(1)" | $(CC) $(CLANG_FLAGS) -c -x assembler-with-cpp -o /dev/null -) # check if $(CC) and $(LD) exist $(error-if,$(failure,command -v $(CC)),C compiler '$(CC)' not found) diff --git a/scripts/Makefile.compiler b/scripts/Makefile.compiler index 20d353dcabfb..158c57f2acfd 100644 --- a/scripts/Makefile.compiler +++ b/scripts/Makefile.compiler @@ -29,16 +29,16 @@ try-run = $(shell set -e; \ fi) # as-option -# Usage: cflags-y += $(call as-option,-Wa$(comma)-isa=foo,) +# Usage: aflags-y += $(call as-option,-Wa$(comma)-isa=foo,) as-option = $(call try-run,\ - $(CC) $(KBUILD_CFLAGS) $(1) -c -x assembler /dev/null -o "$$TMP",$(1),$(2)) + $(CC) -Werror $(KBUILD_AFLAGS) $(1) -c -x assembler-with-cpp /dev/null -o "$$TMP",$(1),$(2)) # as-instr -# Usage: cflags-y += $(call as-instr,instr,option1,option2) +# Usage: aflags-y += $(call as-instr,instr,option1,option2) as-instr = $(call try-run,\ - printf "%b\n" "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -x assembler -o "$$TMP" -,$(2),$(3)) + printf "%b\n" "$(1)" | $(CC) -Werror $(KBUILD_AFLAGS) -c -x assembler-with-cpp -o "$$TMP" -,$(2),$(3)) # __cc-option # Usage: MY_CFLAGS += $(call __cc-option,$(CC),$(MY_CFLAGS),-march=winchip-c6,-march=i586) diff --git a/scripts/as-version.sh b/scripts/as-version.sh index 1a21495e9ff0..af717476152d 100755 --- a/scripts/as-version.sh +++ b/scripts/as-version.sh @@ -45,7 +45,7 @@ orig_args="$@" # Get the first line of the --version output. IFS=' ' -set -- $(LC_ALL=C "$@" -Wa,--version -c -x assembler /dev/null -o /dev/null 2>/dev/null) +set -- $(LC_ALL=C "$@" -Wa,--version -c -x assembler-with-cpp /dev/null -o /dev/null 2>/dev/null) # Split the line on spaces. IFS=' ' -- cgit 1.4.1 From e84a4e368abe42cf359fe237f0238820859d5044 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Jun 2023 16:01:03 +0200 Subject: Linux 6.1.35 Link: https://lore.kernel.org/r/20230619102154.568541872@linuxfoundation.org Tested-by: Florian Fainelli Tested-by: Markus Reichelt Tested-by: Salvatore Bonaccorso Tested-by: Linux Kernel Functional Testing Tested-by: Chris Paterson (CIP) Tested-by: Jon Hunter Tested-by: Ron Economos Tested-by: Conor Dooley Tested-by: Sudip Mukherjee Tested-by: Takeshi Ogasawara Tested-by: Allen Pais Tested-by: Shuah Khan Tested-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bc7cc17b0e75..46c06af912d9 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 34 +SUBLEVEL = 35 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From 8d855bc67630fa2b17855d85de61b9cd4300e3ad Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Thu, 6 Oct 2022 16:40:55 -0400 Subject: drm/amd/display: Use dc_update_planes_and_stream [ Upstream commit f7511289821ffccc07579406d6ab520aa11049f5 ] [Why & How] The old dc_commit_updates_for_stream lacks manipulation for many corner cases where the DC feature requires special attention; as a result, it starts to show its limitation (e.g., the SubVP feature is not supported by it, among other cases). To modernize and unify our internal API, this commit replaces the old dc_commit_updates_for_stream with dc_update_planes_and_stream, which has more features. Reviewed-by: Harry Wentland Acked-by: Qingqing Zhuo Signed-off-by: Rodrigo Siqueira Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Stable-dep-of: ea2062dd1f03 ("drm/amd/display: fix the system hang while disable PSR") Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 53687de6c053..a7bc57529e6c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2632,10 +2632,12 @@ static void dm_gpureset_commit_state(struct dc_state *dc_state, bundle->surface_updates[m].surface->force_full_update = true; } - dc_commit_updates_for_stream( - dm->dc, bundle->surface_updates, + + dc_update_planes_and_stream(dm->dc, + bundle->surface_updates, dc_state->stream_status->plane_count, - dc_state->streams[k], &bundle->stream_update, dc_state); + dc_state->streams[k], + &bundle->stream_update); } cleanup: @@ -7887,12 +7889,11 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, acrtc_state->stream->link->psr_settings.psr_allow_active) amdgpu_dm_psr_disable(acrtc_state->stream); - dc_commit_updates_for_stream(dm->dc, - bundle->surface_updates, - planes_count, - acrtc_state->stream, - &bundle->stream_update, - dc_state); + dc_update_planes_and_stream(dm->dc, + bundle->surface_updates, + planes_count, + acrtc_state->stream, + &bundle->stream_update); /** * Enable or disable the interrupts on the backend. @@ -8334,12 +8335,11 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) mutex_lock(&dm->dc_lock); - dc_commit_updates_for_stream(dm->dc, - dummy_updates, - status->plane_count, - dm_new_crtc_state->stream, - &stream_update, - dc_state); + dc_update_planes_and_stream(dm->dc, + dummy_updates, + status->plane_count, + dm_new_crtc_state->stream, + &stream_update); mutex_unlock(&dm->dc_lock); } -- cgit 1.4.1 From e538342002cbe468224f71b7ae116586e55c1134 Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Thu, 23 Feb 2023 11:36:08 -0700 Subject: drm/amd/display: Add wrapper to call planes and stream update [ Upstream commit 81f743a08f3b214638aa389e252ae5e6c3592e7c ] [Why & How] This commit is part of a sequence of changes that replaces the commit sequence used in the DC with a new one. As a result of this transition, we moved some specific parts from the commit sequence and brought them to amdgpu_dm. This commit adds a wrapper inside DM that enable our drivers to do any necessary preparation or change before we offload the plane/stream update to DC. Reviewed-by: Harry Wentland Acked-by: Qingqing Zhuo Signed-off-by: Rodrigo Siqueira Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Stable-dep-of: ea2062dd1f03 ("drm/amd/display: fix the system hang while disable PSR") Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 51 ++++++++++++++++++----- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index a7bc57529e6c..91f074f4f262 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -348,6 +348,35 @@ static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, return false; } +/** + * update_planes_and_stream_adapter() - Send planes to be updated in DC + * + * DC has a generic way to update planes and stream via + * dc_update_planes_and_stream function; however, DM might need some + * adjustments and preparation before calling it. This function is a wrapper + * for the dc_update_planes_and_stream that does any required configuration + * before passing control to DC. + */ +static inline bool update_planes_and_stream_adapter(struct dc *dc, + int update_type, + int planes_count, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update, + struct dc_surface_update *array_of_surface_update) +{ + /* + * Previous frame finished and HW is ready for optimization. + */ + if (update_type == UPDATE_TYPE_FAST) + dc_post_update_surfaces_to_stream(dc); + + return dc_update_planes_and_stream(dc, + array_of_surface_update, + planes_count, + stream, + stream_update); +} + /** * dm_pflip_high_irq() - Handle pageflip interrupt * @interrupt_params: ignored @@ -2633,11 +2662,12 @@ static void dm_gpureset_commit_state(struct dc_state *dc_state, true; } - dc_update_planes_and_stream(dm->dc, - bundle->surface_updates, - dc_state->stream_status->plane_count, - dc_state->streams[k], - &bundle->stream_update); + update_planes_and_stream_adapter(dm->dc, + UPDATE_TYPE_FULL, + dc_state->stream_status->plane_count, + dc_state->streams[k], + &bundle->stream_update, + bundle->surface_updates); } cleanup: @@ -7889,11 +7919,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, acrtc_state->stream->link->psr_settings.psr_allow_active) amdgpu_dm_psr_disable(acrtc_state->stream); - dc_update_planes_and_stream(dm->dc, - bundle->surface_updates, - planes_count, - acrtc_state->stream, - &bundle->stream_update); + update_planes_and_stream_adapter(dm->dc, + acrtc_state->update_type, + planes_count, + acrtc_state->stream, + &bundle->stream_update, + bundle->surface_updates); /** * Enable or disable the interrupts on the backend. -- cgit 1.4.1 From 9bcac453890bf2c0ab5a7cefb407c0a9d6cbc4cb Mon Sep 17 00:00:00 2001 From: Tom Chung Date: Mon, 29 May 2023 18:00:09 +0800 Subject: drm/amd/display: fix the system hang while disable PSR [ Upstream commit ea2062dd1f0384ae1b136d333ee4ced15bedae38 ] [Why] When the PSR enabled. If you try to adjust the timing parameters, it may cause system hang. Because the timing mismatch with the DMCUB settings. [How] Disable the PSR before adjusting timing parameters. Cc: Mario Limonciello Cc: Alex Deucher Cc: stable@vger.kernel.org Acked-by: Stylon Wang Signed-off-by: Tom Chung Reviewed-by: Wayne Lin Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 91f074f4f262..91c308cf27eb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -7902,6 +7902,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, if (acrtc_state->abm_level != dm_old_crtc_state->abm_level) bundle->stream_update.abm_level = &acrtc_state->abm_level; + mutex_lock(&dm->dc_lock); + if ((acrtc_state->update_type > UPDATE_TYPE_FAST) && + acrtc_state->stream->link->psr_settings.psr_allow_active) + amdgpu_dm_psr_disable(acrtc_state->stream); + mutex_unlock(&dm->dc_lock); + /* * If FreeSync state on the stream has changed then we need to * re-adjust the min/max bounds now that DC doesn't handle this @@ -7915,10 +7921,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); } mutex_lock(&dm->dc_lock); - if ((acrtc_state->update_type > UPDATE_TYPE_FAST) && - acrtc_state->stream->link->psr_settings.psr_allow_active) - amdgpu_dm_psr_disable(acrtc_state->stream); - update_planes_and_stream_adapter(dm->dc, acrtc_state->update_type, planes_count, -- cgit 1.4.1 From 17732fed852a3a50f8ceb92399c45a3f5e8a08a3 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Mon, 30 Jan 2023 14:44:44 +0800 Subject: tty: serial: fsl_lpuart: make rx_watermark configurable for different platforms [ Upstream commit 34ebb26f12a84b744f43c5c4869516f122a2dfaa ] Add rx_watermark parameter for struct lpuart_port to make the receive watermark configurable for different platforms. No function changed. Signed-off-by: Sherry Sun Link: https://lore.kernel.org/r/20230130064449.9564-2-sherry.sun@nxp.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: a82c3df955f8 ("tty: serial: fsl_lpuart: reduce RX watermark to 0 on LS1028A") Signed-off-by: Sasha Levin --- drivers/tty/serial/fsl_lpuart.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index dc7ac1ddbca5..82066f17bdfb 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -257,6 +257,7 @@ struct lpuart_port { unsigned int txfifo_size; unsigned int rxfifo_size; + u8 rx_watermark; bool lpuart_dma_tx_use; bool lpuart_dma_rx_use; struct dma_chan *dma_tx_chan; @@ -283,38 +284,45 @@ struct lpuart_soc_data { enum lpuart_type devtype; char iotype; u8 reg_off; + u8 rx_watermark; }; static const struct lpuart_soc_data vf_data = { .devtype = VF610_LPUART, .iotype = UPIO_MEM, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1021a_data = { .devtype = LS1021A_LPUART, .iotype = UPIO_MEM32BE, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1028a_data = { .devtype = LS1028A_LPUART, .iotype = UPIO_MEM32, + .rx_watermark = 1, }; static struct lpuart_soc_data imx7ulp_data = { .devtype = IMX7ULP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static struct lpuart_soc_data imx8qxp_data = { .devtype = IMX8QXP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static struct lpuart_soc_data imxrt1050_data = { .devtype = IMXRT1050_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static const struct of_device_id lpuart_dt_ids[] = { @@ -1533,7 +1541,7 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) } writeb(0, sport->port.membase + UARTTWFIFO); - writeb(1, sport->port.membase + UARTRWFIFO); + writeb(sport->rx_watermark, sport->port.membase + UARTRWFIFO); /* Restore cr2 */ writeb(cr2_saved, sport->port.membase + UARTCR2); @@ -1568,7 +1576,8 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) lpuart32_write(&sport->port, val, UARTFIFO); /* set the watermark */ - val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); + val = (sport->rx_watermark << UARTWATER_RXWATER_OFF) | + (0x0 << UARTWATER_TXWATER_OFF); lpuart32_write(&sport->port, val, UARTWATER); /* Restore cr2 */ @@ -2714,6 +2723,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; sport->devtype = sdata->devtype; + sport->rx_watermark = sdata->rx_watermark; ret = platform_get_irq(pdev, 0); if (ret < 0) return ret; -- cgit 1.4.1 From c4465bff4d2f19825cbacd5775c32a15a46665fe Mon Sep 17 00:00:00 2001 From: Robert Hodaszi Date: Fri, 9 Jun 2023 14:13:34 +0200 Subject: tty: serial: fsl_lpuart: reduce RX watermark to 0 on LS1028A [ Upstream commit a82c3df955f8c1c726e4976527aa6ae924a67dd9 ] LS1028A is using DMA with LPUART. Having RX watermark set to 1, means DMA transactions are started only after receiving the second character. On other platforms with newer LPUART IP, Receiver Idle Empty function initiates the DMA request after the receiver is idling for 4 characters. But this feature is missing on LS1028A, which is causing a 1-character delay in the RX direction on this platform. Set RX watermark to 0 to initiate RX DMA after each character. Link: https://lore.kernel.org/linux-serial/20230607103459.1222426-1-robert.hodaszi@digi.com/ Fixes: 9ad9df844754 ("tty: serial: fsl_lpuart: Fix the wrong RXWATER setting for rx dma case") Cc: stable Signed-off-by: Robert Hodaszi Message-ID: <20230609121334.1878626-1-robert.hodaszi@digi.com> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/fsl_lpuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 82066f17bdfb..1093c74b5284 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -302,7 +302,7 @@ static const struct lpuart_soc_data ls1021a_data = { static const struct lpuart_soc_data ls1028a_data = { .devtype = LS1028A_LPUART, .iotype = UPIO_MEM32, - .rx_watermark = 1, + .rx_watermark = 0, }; static struct lpuart_soc_data imx7ulp_data = { -- cgit 1.4.1 From 4ed740c6482f84cbb910f9f4e9822a08208ab787 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 15 Jun 2023 17:18:53 +0900 Subject: ata: libata-scsi: Avoid deadlock on rescan after device resume [ Upstream commit 6aa0365a3c8512587fffd42fe438768709ddef8e ] When an ATA port is resumed from sleep, the port is reset and a power management request issued to libata EH to reset the port and rescanning the device(s) attached to the port. Device rescanning is done by scheduling an ata_scsi_dev_rescan() work, which will execute scsi_rescan_device(). However, scsi_rescan_device() takes the generic device lock, which is also taken by dpm_resume() when the SCSI device is resumed as well. If a device rescan execution starts before the completion of the SCSI device resume, the rcu locking used to refresh the cached VPD pages of the device, combined with the generic device locking from scsi_rescan_device() and from dpm_resume() can cause a deadlock. Avoid this situation by changing struct ata_port scsi_rescan_task to be a delayed work instead of a simple work_struct. ata_scsi_dev_rescan() is modified to check if the SCSI device associated with the ATA device that must be rescanned is not suspended. If the SCSI device is still suspended, ata_scsi_dev_rescan() returns early and reschedule itself for execution after an arbitrary delay of 5ms. Reported-by: Kai-Heng Feng Reported-by: Joe Breuer Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217530 Fixes: a19a93e4c6a9 ("scsi: core: pm: Rely on the device driver core for async power management") Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke Tested-by: Kai-Heng Feng Tested-by: Joe Breuer Signed-off-by: Sasha Levin --- drivers/ata/libata-core.c | 3 ++- drivers/ata/libata-eh.c | 2 +- drivers/ata/libata-scsi.c | 22 +++++++++++++++++++++- include/linux/libata.h | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index a5ea144722fa..0ba0c3d1613f 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5313,7 +5313,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) mutex_init(&ap->scsi_scan_mutex); INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug); - INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); + INIT_DELAYED_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); INIT_LIST_HEAD(&ap->eh_done_q); init_waitqueue_head(&ap->eh_wait_q); init_completion(&ap->park_req_pending); @@ -5919,6 +5919,7 @@ static void ata_port_detach(struct ata_port *ap) WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED)); cancel_delayed_work_sync(&ap->hotplug_task); + cancel_delayed_work_sync(&ap->scsi_rescan_task); skip_eh: /* clean up zpodd on port removal */ diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 08e11bc312c2..a3ae5fc2a42f 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2973,7 +2973,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, ehc->i.flags |= ATA_EHI_SETMODE; /* schedule the scsi_rescan_device() here */ - schedule_work(&(ap->scsi_rescan_task)); + schedule_delayed_work(&ap->scsi_rescan_task, 0); } else if (dev->class == ATA_DEV_UNKNOWN && ehc->tries[dev->devno] && ata_class_enabled(ehc->classes[dev->devno])) { diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 39e1ff9b686b..9c0052d28078 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4601,10 +4601,11 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, void ata_scsi_dev_rescan(struct work_struct *work) { struct ata_port *ap = - container_of(work, struct ata_port, scsi_rescan_task); + container_of(work, struct ata_port, scsi_rescan_task.work); struct ata_link *link; struct ata_device *dev; unsigned long flags; + bool delay_rescan = false; mutex_lock(&ap->scsi_scan_mutex); spin_lock_irqsave(ap->lock, flags); @@ -4618,6 +4619,21 @@ void ata_scsi_dev_rescan(struct work_struct *work) if (scsi_device_get(sdev)) continue; + /* + * If the rescan work was scheduled because of a resume + * event, the port is already fully resumed, but the + * SCSI device may not yet be fully resumed. In such + * case, executing scsi_rescan_device() may cause a + * deadlock with the PM code on device_lock(). Prevent + * this by giving up and retrying rescan after a short + * delay. + */ + delay_rescan = sdev->sdev_gendev.power.is_suspended; + if (delay_rescan) { + scsi_device_put(sdev); + break; + } + spin_unlock_irqrestore(ap->lock, flags); scsi_rescan_device(&(sdev->sdev_gendev)); scsi_device_put(sdev); @@ -4627,4 +4643,8 @@ void ata_scsi_dev_rescan(struct work_struct *work) spin_unlock_irqrestore(ap->lock, flags); mutex_unlock(&ap->scsi_scan_mutex); + + if (delay_rescan) + schedule_delayed_work(&ap->scsi_rescan_task, + msecs_to_jiffies(5)); } diff --git a/include/linux/libata.h b/include/linux/libata.h index fe990176e6ee..9713f4d8f15f 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -834,7 +834,7 @@ struct ata_port { struct mutex scsi_scan_mutex; struct delayed_work hotplug_task; - struct work_struct scsi_rescan_task; + struct delayed_work scsi_rescan_task; unsigned int hsm_task_state; -- cgit 1.4.1 From 2e7ad879e1b0256fb9e4703fd6cd2864d707dea7 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Mon, 10 Apr 2023 19:43:44 +0200 Subject: mm: Fix copy_from_user_nofault(). commit d319f344561de23e810515d109c7278919bff7b0 upstream. There are several issues with copy_from_user_nofault(): - access_ok() is designed for user context only and for that reason it has WARN_ON_IN_IRQ() which triggers when bpf, kprobe, eprobe and perf on ppc are calling it from irq. - it's missing nmi_uaccess_okay() which is a nop on all architectures except x86 where it's required. The comment in arch/x86/mm/tlb.c explains the details why it's necessary. Calling copy_from_user_nofault() from bpf, [ke]probe without this check is not safe. - __copy_from_user_inatomic() under CONFIG_HARDENED_USERCOPY is calling check_object_size()->__check_object_size()->check_heap_object()->find_vmap_area()->spin_lock() which is not safe to do from bpf, [ke]probe and perf due to potential deadlock. Fix all three issues. At the end the copy_from_user_nofault() becomes equivalent to copy_from_user_nmi() from safety point of view with a difference in the return value. Reported-by: Hsin-Wei Hung Signed-off-by: Alexei Starovoitov Signed-off-by: Florian Lehner Tested-by: Hsin-Wei Hung Tested-by: Florian Lehner Link: https://lore.kernel.org/r/20230410174345.4376-2-dev@der-flo.net Signed-off-by: Alexei Starovoitov Cc: Javier Honduvilla Coto Cc: Daniel Borkmann Signed-off-by: Greg Kroah-Hartman --- mm/maccess.c | 16 +++++++++++----- mm/usercopy.c | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mm/maccess.c b/mm/maccess.c index 074f6b086671..518a25667323 100644 --- a/mm/maccess.c +++ b/mm/maccess.c @@ -5,6 +5,7 @@ #include #include #include +#include bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size) @@ -113,11 +114,16 @@ Efault: long copy_from_user_nofault(void *dst, const void __user *src, size_t size) { long ret = -EFAULT; - if (access_ok(src, size)) { - pagefault_disable(); - ret = __copy_from_user_inatomic(dst, src, size); - pagefault_enable(); - } + + if (!__access_ok(src, size)) + return ret; + + if (!nmi_uaccess_okay()) + return ret; + + pagefault_disable(); + ret = __copy_from_user_inatomic(dst, src, size); + pagefault_enable(); if (ret) return -EFAULT; diff --git a/mm/usercopy.c b/mm/usercopy.c index c1ee15a98633..434fce112acb 100644 --- a/mm/usercopy.c +++ b/mm/usercopy.c @@ -172,7 +172,7 @@ static inline void check_heap_object(const void *ptr, unsigned long n, return; } - if (is_vmalloc_addr(ptr)) { + if (is_vmalloc_addr(ptr) && !pagefault_disabled()) { struct vmap_area *area = find_vmap_area(addr); if (!area) -- cgit 1.4.1 From a46fa5696613cebd67ebaeca72f6ffef62c280c1 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 24 Nov 2022 14:55:35 +0100 Subject: tpm, tpm_tis: Claim locality in interrupt handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0e069265bce5a40c4eee52e2364bbbd4dabee94a upstream. Writing the TPM_INT_STATUS register in the interrupt handler to clear the interrupts only has effect if a locality is held. Since this is not guaranteed at the time the interrupt is fired, claim the locality explicitly in the handler. Signed-off-by: Lino Sanfilippo Tested-by: Michael Niewöhner Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_tis_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index e05d2b227de3..55f6ff1e05aa 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -772,7 +772,9 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) wake_up_interruptible(&priv->int_queue); /* Clear interrupts handled with TPM_EOI */ + tpm_tis_request_locality(chip, 0); rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt); + tpm_tis_relinquish_locality(chip, 0); if (rc < 0) return IRQ_NONE; -- cgit 1.4.1 From 0fd4ac3773c35f651ee228bfadd477976fa8cd70 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 31 Dec 2022 01:14:32 -0800 Subject: tpm_crb: Add support for CRB devices based on Pluton commit 4d2732882703791ea4b670df433f88fc4b40a5cb upstream. Pluton is an integrated security processor present in some recent Ryzen parts. If it's enabled, it presents two devices - an MSFT0101 ACPI device that's broadly an implementation of a Command Response Buffer TPM2, and an MSFT0200 ACPI device whose functionality I haven't examined in detail yet. This patch only attempts to add support for the TPM device. There's a few things that need to be handled here. The first is that the TPM2 ACPI table uses a previously undefined start method identifier. The table format appears to include 16 bytes of startup data, which corresponds to one 64-bit address for a start message and one 64-bit address for a completion response. The second is that the ACPI tables on the Thinkpad Z13 I'm testing this on don't define any memory windows in _CRS (or, more accurately, there are two empty memory windows). This check doesn't seem strictly necessary, so I've skipped that. Finally, it seems like chip needs to be explicitly asked to transition into ready status on every command. Failing to do this means that if two commands are sent in succession without an idle/ready transition in between, everything will appear to work fine but the response is simply the original command. I'm working without any docs here, so I'm not sure if this is actually the required behaviour or if I'm missing something somewhere else, but doing this results in the chip working reliably. Reviewed-by: Jarkko Sakkinen Signed-off-by: Matthew Garrett Signed-off-by: Jarkko Sakkinen Cc: "Limonciello, Mario" Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_crb.c | 100 ++++++++++++++++++++++++++++++++++++++++----- include/acpi/actbl3.h | 1 + 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 16fc481d6095..916f4ff246c1 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -98,6 +98,8 @@ struct crb_priv { u8 __iomem *rsp; u32 cmd_size; u32 smc_func_id; + u32 __iomem *pluton_start_addr; + u32 __iomem *pluton_reply_addr; }; struct tpm2_crb_smc { @@ -108,6 +110,11 @@ struct tpm2_crb_smc { u32 smc_func_id; }; +struct tpm2_crb_pluton { + u64 start_addr; + u64 reply_addr; +}; + static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, unsigned long timeout) { @@ -127,6 +134,25 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, return ((ioread32(reg) & mask) == value); } +static int crb_try_pluton_doorbell(struct crb_priv *priv, bool wait_for_complete) +{ + if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) + return 0; + + if (!crb_wait_for_reg_32(priv->pluton_reply_addr, ~0, 1, TPM2_TIMEOUT_C)) + return -ETIME; + + iowrite32(1, priv->pluton_start_addr); + if (wait_for_complete == false) + return 0; + + if (!crb_wait_for_reg_32(priv->pluton_start_addr, + 0xffffffff, 0, 200)) + return -ETIME; + + return 0; +} + /** * __crb_go_idle - request tpm crb device to go the idle state * @@ -145,6 +171,8 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, */ static int __crb_go_idle(struct device *dev, struct crb_priv *priv) { + int rc; + if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) @@ -152,6 +180,10 @@ static int __crb_go_idle(struct device *dev, struct crb_priv *priv) iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); + rc = crb_try_pluton_doorbell(priv, true); + if (rc) + return rc; + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, CRB_CTRL_REQ_GO_IDLE/* mask */, 0, /* value */ @@ -188,12 +220,19 @@ static int crb_go_idle(struct tpm_chip *chip) */ static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv) { + int rc; + if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) return 0; iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req); + + rc = crb_try_pluton_doorbell(priv, true); + if (rc) + return rc; + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, CRB_CTRL_REQ_CMD_READY /* mask */, 0, /* value */ @@ -371,6 +410,10 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) return -E2BIG; } + /* Seems to be necessary for every command */ + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) + __crb_cmd_ready(&chip->dev, priv); + memcpy_toio(priv->cmd, buf, len); /* Make sure that cmd is populated before issuing start. */ @@ -394,7 +437,10 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id); } - return rc; + if (rc) + return rc; + + return crb_try_pluton_doorbell(priv, false); } static void crb_cancel(struct tpm_chip *chip) @@ -524,15 +570,18 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, return ret; acpi_dev_free_resource_list(&acpi_resource_list); - if (resource_type(iores_array) != IORESOURCE_MEM) { - dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); - return -EINVAL; - } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) == - IORESOURCE_MEM) { - dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n"); - memset(iores_array + TPM_CRB_MAX_RESOURCES, - 0, sizeof(*iores_array)); - iores_array[TPM_CRB_MAX_RESOURCES].flags = 0; + /* Pluton doesn't appear to define ACPI memory regions */ + if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + if (resource_type(iores_array) != IORESOURCE_MEM) { + dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); + return -EINVAL; + } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) == + IORESOURCE_MEM) { + dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n"); + memset(iores_array + TPM_CRB_MAX_RESOURCES, + 0, sizeof(*iores_array)); + iores_array[TPM_CRB_MAX_RESOURCES].flags = 0; + } } iores = NULL; @@ -656,6 +705,22 @@ out_relinquish_locality: return ret; } +static int crb_map_pluton(struct device *dev, struct crb_priv *priv, + struct acpi_table_tpm2 *buf, struct tpm2_crb_pluton *crb_pluton) +{ + priv->pluton_start_addr = crb_map_res(dev, NULL, NULL, + crb_pluton->start_addr, 4); + if (IS_ERR(priv->pluton_start_addr)) + return PTR_ERR(priv->pluton_start_addr); + + priv->pluton_reply_addr = crb_map_res(dev, NULL, NULL, + crb_pluton->reply_addr, 4); + if (IS_ERR(priv->pluton_reply_addr)) + return PTR_ERR(priv->pluton_reply_addr); + + return 0; +} + static int crb_acpi_add(struct acpi_device *device) { struct acpi_table_tpm2 *buf; @@ -663,6 +728,7 @@ static int crb_acpi_add(struct acpi_device *device) struct tpm_chip *chip; struct device *dev = &device->dev; struct tpm2_crb_smc *crb_smc; + struct tpm2_crb_pluton *crb_pluton; acpi_status status; u32 sm; int rc; @@ -700,6 +766,20 @@ static int crb_acpi_add(struct acpi_device *device) priv->smc_func_id = crb_smc->smc_func_id; } + if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + if (buf->header.length < (sizeof(*buf) + sizeof(*crb_pluton))) { + dev_err(dev, + FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", + buf->header.length, + ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON); + return -EINVAL; + } + crb_pluton = ACPI_ADD_PTR(struct tpm2_crb_pluton, buf, sizeof(*buf)); + rc = crb_map_pluton(dev, priv, buf, crb_pluton); + if (rc) + return rc; + } + priv->sm = sm; priv->hid = acpi_device_hid(device); diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 7b9571e00cc4..832c6464f063 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -443,6 +443,7 @@ struct acpi_tpm2_phy { #define ACPI_TPM2_RESERVED10 10 #define ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC 11 /* V1.2 Rev 8 */ #define ACPI_TPM2_RESERVED 12 +#define ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON 13 /* Optional trailer appears after any start_method subtables */ -- cgit 1.4.1 From 9650cf70ec9d94ff34daa088b643229231723c26 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 5 Jun 2023 01:57:34 +0900 Subject: ksmbd: validate command payload size commit 2b9b8f3b68edb3d67d79962f02e26dbb5ae3808d upstream. ->StructureSize2 indicates command payload size. ksmbd should validate this size with rfc1002 length before accessing it. This patch remove unneeded check and add the validation for this. [ 8.912583] BUG: KASAN: slab-out-of-bounds in ksmbd_smb2_check_message+0x12a/0xc50 [ 8.913051] Read of size 2 at addr ffff88800ac7d92c by task kworker/0:0/7 ... [ 8.914967] Call Trace: [ 8.915126] [ 8.915267] dump_stack_lvl+0x33/0x50 [ 8.915506] print_report+0xcc/0x620 [ 8.916558] kasan_report+0xae/0xe0 [ 8.917080] kasan_check_range+0x35/0x1b0 [ 8.917334] ksmbd_smb2_check_message+0x12a/0xc50 [ 8.917935] ksmbd_verify_smb_message+0xae/0xd0 [ 8.918223] handle_ksmbd_work+0x192/0x820 [ 8.918478] process_one_work+0x419/0x760 [ 8.918727] worker_thread+0x2a2/0x6f0 [ 8.919222] kthread+0x187/0x1d0 [ 8.919723] ret_from_fork+0x1f/0x30 [ 8.919954] Cc: stable@vger.kernel.org Reported-by: Chih-Yen Chang Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2misc.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index 0ffe663b7590..57749f41b991 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -351,6 +351,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) int command; __u32 clc_len; /* calculated length */ __u32 len = get_rfc1002_len(work->request_buf); + __u32 req_struct_size; if (le32_to_cpu(hdr->NextCommand) > 0) len = le32_to_cpu(hdr->NextCommand); @@ -373,17 +374,9 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { - if (command != SMB2_OPLOCK_BREAK_HE && - (hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { - /* error packets have 9 byte structure size */ - ksmbd_debug(SMB, - "Illegal request size %u for command %d\n", - le16_to_cpu(pdu->StructureSize2), command); - return 1; - } else if (command == SMB2_OPLOCK_BREAK_HE && - hdr->Status == 0 && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { + if (command == SMB2_OPLOCK_BREAK_HE && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { /* special case for SMB2.1 lease break message */ ksmbd_debug(SMB, "Illegal request size %d for oplock break\n", @@ -392,6 +385,14 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } } + req_struct_size = le16_to_cpu(pdu->StructureSize2) + + __SMB2_HEADER_STRUCTURE_SIZE; + if (command == SMB2_LOCK_HE) + req_struct_size -= sizeof(struct smb2_lock_element); + + if (req_struct_size > len + 1) + return 1; + if (smb2_calc_size(hdr, &clc_len)) return 1; -- cgit 1.4.1 From c86211159bc3178b891e0d60e586a32c7b6a231b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 15 Jun 2023 22:04:40 +0900 Subject: ksmbd: fix out-of-bound read in smb2_write commit 5fe7f7b78290638806211046a99f031ff26164e1 upstream. ksmbd_smb2_check_message doesn't validate hdr->NextCommand. If ->NextCommand is bigger than Offset + Length of smb2 write, It will allow oversized smb2 write length. It will cause OOB read in smb2_write. Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-21164 Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/smb2misc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index 57749f41b991..33b7e6c4ceff 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -351,10 +351,16 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) int command; __u32 clc_len; /* calculated length */ __u32 len = get_rfc1002_len(work->request_buf); - __u32 req_struct_size; + __u32 req_struct_size, next_cmd = le32_to_cpu(hdr->NextCommand); - if (le32_to_cpu(hdr->NextCommand) > 0) - len = le32_to_cpu(hdr->NextCommand); + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd > len) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return 1; + } + + if (next_cmd > 0) + len = next_cmd; else if (work->next_smb2_rcv_hdr_off) len -= work->next_smb2_rcv_hdr_off; -- cgit 1.4.1 From 854156d12caa9d36de1cf5f084591c7686cc8a9d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 15 Jun 2023 22:05:29 +0900 Subject: ksmbd: validate session id and tree id in the compound request commit 5005bcb4219156f1bf7587b185080ec1da08518e upstream. This patch validate session id and tree id in compound request. If first operation in the compound is SMB2 ECHO request, ksmbd bypass session and tree validation. So work->sess and work->tcon could be NULL. If secound request in the compound access work->sess or tcon, It cause NULL pointer dereferecing error. Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-21165 Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/ksmbd/server.c | 33 ++++++++++++++++++++------------- fs/ksmbd/smb2pdu.c | 44 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index 8a0ad399f245..847ee62afb8a 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -185,24 +185,31 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, goto send; } - if (conn->ops->check_user_session) { - rc = conn->ops->check_user_session(work); - if (rc < 0) { - command = conn->ops->get_cmd_val(work); - conn->ops->set_rsp_status(work, - STATUS_USER_SESSION_DELETED); - goto send; - } else if (rc > 0) { - rc = conn->ops->get_ksmbd_tcon(work); + do { + if (conn->ops->check_user_session) { + rc = conn->ops->check_user_session(work); if (rc < 0) { - conn->ops->set_rsp_status(work, - STATUS_NETWORK_NAME_DELETED); + if (rc == -EINVAL) + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + else + conn->ops->set_rsp_status(work, + STATUS_USER_SESSION_DELETED); goto send; + } else if (rc > 0) { + rc = conn->ops->get_ksmbd_tcon(work); + if (rc < 0) { + if (rc == -EINVAL) + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + else + conn->ops->set_rsp_status(work, + STATUS_NETWORK_NAME_DELETED); + goto send; + } } } - } - do { rc = __process_request(work, conn, &command); if (rc == SERVER_HANDLER_ABORT) break; diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 56f661e5ef62..2b7c0ba6a77d 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -90,7 +90,6 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) unsigned int cmd = le16_to_cpu(req_hdr->Command); int tree_id; - work->tcon = NULL; if (cmd == SMB2_TREE_CONNECT_HE || cmd == SMB2_CANCEL_HE || cmd == SMB2_LOGOFF_HE) { @@ -104,10 +103,28 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) } tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); + + /* + * If request is not the first in Compound request, + * Just validate tree id in header with work->tcon->id. + */ + if (work->next_smb2_rcv_hdr_off) { + if (!work->tcon) { + pr_err("The first operation in the compound does not have tcon\n"); + return -EINVAL; + } + if (work->tcon->id != tree_id) { + pr_err("tree id(%u) is different with id(%u) in first operation\n", + tree_id, work->tcon->id); + return -EINVAL; + } + return 1; + } + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); if (!work->tcon) { pr_err("Invalid tid %d\n", tree_id); - return -EINVAL; + return -ENOENT; } return 1; @@ -553,7 +570,6 @@ int smb2_check_user_session(struct ksmbd_work *work) unsigned int cmd = conn->ops->get_cmd_val(work); unsigned long long sess_id; - work->sess = NULL; /* * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not * require a session id, so no need to validate user session's for @@ -564,15 +580,33 @@ int smb2_check_user_session(struct ksmbd_work *work) return 0; if (!ksmbd_conn_good(conn)) - return -EINVAL; + return -EIO; sess_id = le64_to_cpu(req_hdr->SessionId); + + /* + * If request is not the first in Compound request, + * Just validate session id in header with work->sess->id. + */ + if (work->next_smb2_rcv_hdr_off) { + if (!work->sess) { + pr_err("The first operation in the compound does not have sess\n"); + return -EINVAL; + } + if (work->sess->id != sess_id) { + pr_err("session id(%llu) is different with the first operation(%lld)\n", + sess_id, work->sess->id); + return -EINVAL; + } + return 1; + } + /* Check for validity of user session */ work->sess = ksmbd_session_lookup_all(conn, sess_id); if (work->sess) return 1; ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); - return -EINVAL; + return -ENOENT; } static void destroy_previous_session(struct ksmbd_conn *conn, -- cgit 1.4.1 From 0c6552f83725434669abacdf521f91e504ef6bf0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 15 Jun 2023 11:18:30 +0200 Subject: tick/common: Align tick period during sched_timer setup commit 13bb06f8dd42071cb9a49f6e21099eea05d4b856 upstream. The tick period is aligned very early while the first clock_event_device is registered. At that point the system runs in periodic mode and switches later to one-shot mode if possible. The next wake-up event is programmed based on the aligned value (tick_next_period) but the delta value, that is used to program the clock_event_device, is computed based on ktime_get(). With the subtracted offset, the device fires earlier than the exact time frame. With a large enough offset the system programs the timer for the next wake-up and the remaining time left is too small to make any boot progress. The system hangs. Move the alignment later to the setup of tick_sched timer. At this point the system switches to oneshot mode and a high resolution clocksource is available. At this point it is safe to align tick_next_period because ktime_get() will now return accurate (not jiffies based) time. [bigeasy: Patch description + testing]. Fixes: e9523a0d81899 ("tick/common: Align tick period with the HZ tick.") Reported-by: Mathias Krause Reported-by: "Bhatnagar, Rishabh" Suggested-by: Mathias Krause Signed-off-by: Thomas Gleixner Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Tested-by: Richard W.M. Jones Tested-by: Mathias Krause Acked-by: SeongJae Park Cc: stable@vger.kernel.org Link: https://lore.kernel.org/5a56290d-806e-b9a5-f37c-f21958b5a8c0@grsecurity.net Link: https://lore.kernel.org/12c6f9a3-d087-b824-0d05-0d18c9bc1bf3@amazon.com Link: https://lore.kernel.org/r/20230615091830.RxMV2xf_@linutronix.de Signed-off-by: Greg Kroah-Hartman --- kernel/time/tick-common.c | 13 +------------ kernel/time/tick-sched.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 65b8658da829..e9138cd7a0f5 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -218,19 +218,8 @@ static void tick_setup_device(struct tick_device *td, * this cpu: */ if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) { - ktime_t next_p; - u32 rem; - tick_do_timer_cpu = cpu; - - next_p = ktime_get(); - div_u64_rem(next_p, TICK_NSEC, &rem); - if (rem) { - next_p -= rem; - next_p += TICK_NSEC; - } - - tick_next_period = next_p; + tick_next_period = ktime_get(); #ifdef CONFIG_NO_HZ_FULL /* * The boot CPU may be nohz_full, in which case set diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index a46506f7ec6d..d6fb6a676bbb 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -161,8 +161,19 @@ static ktime_t tick_init_jiffy_update(void) raw_spin_lock(&jiffies_lock); write_seqcount_begin(&jiffies_seq); /* Did we start the jiffies update yet ? */ - if (last_jiffies_update == 0) + if (last_jiffies_update == 0) { + u32 rem; + + /* + * Ensure that the tick is aligned to a multiple of + * TICK_NSEC. + */ + div_u64_rem(tick_next_period, TICK_NSEC, &rem); + if (rem) + tick_next_period += TICK_NSEC - rem; + last_jiffies_update = tick_next_period; + } period = last_jiffies_update; write_seqcount_end(&jiffies_seq); raw_spin_unlock(&jiffies_lock); -- cgit 1.4.1 From a032ccca15e10ef4120753cd375f13287a3e1289 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 14 Apr 2023 17:47:09 +0200 Subject: selftests: mptcp: remove duplicated entries in usage commit 0a85264e48b642d360720589fdb837a3643fb9b0 upstream. mptcp_connect tool was printing some duplicated entries when showing how to use it: -j -l -r While at it, I also: - moved the very few entries that were not sorted, - added -R that was missing since commit 8a4b910d005d ("mptcp: selftests: add rcvbuf set option"), - removed the -u parameter that has been removed in commit f730b65c9d85 ("selftests: mptcp: try to set mptcp ulp mode in different sk states"). No need to backport this, it is just an internal tool used by our selftests. The help menu is mainly useful for MPTCP kernel devs. Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_connect.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index e54653ea2ed4..7df6b9b6f9a8 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -98,8 +98,8 @@ static struct cfg_sockopt_types cfg_sockopt_types; static void die_usage(void) { fprintf(stderr, "Usage: mptcp_connect [-6] [-c cmsg] [-f offset] [-i file] [-I num] [-j] [-l] " - "[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-j] [-l] [-r num] " - "[-s MPTCP|TCP] [-S num] [-r num] [-t num] [-T num] [-u] [-w sec] connect_address\n"); + "[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-r num] [-R num] " + "[-s MPTCP|TCP] [-S num] [-t num] [-T num] [-w sec] connect_address\n"); fprintf(stderr, "\t-6 use ipv6\n"); fprintf(stderr, "\t-c cmsg -- test cmsg type \n"); fprintf(stderr, "\t-f offset -- stop the I/O after receiving and sending the specified amount " @@ -118,13 +118,13 @@ static void die_usage(void) fprintf(stderr, "\t-p num -- use port num\n"); fprintf(stderr, "\t-P [saveWithPeek|saveAfterPeek] -- save data with/after MSG_PEEK form tcp socket\n"); - fprintf(stderr, "\t-t num -- set poll timeout to num\n"); - fprintf(stderr, "\t-T num -- set expected runtime to num ms\n"); fprintf(stderr, "\t-r num -- enable slow mode, limiting each write to num bytes " "-- for remove addr tests\n"); fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n"); fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n"); fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n"); + fprintf(stderr, "\t-t num -- set poll timeout to num\n"); + fprintf(stderr, "\t-T num -- set expected runtime to num ms\n"); fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n"); exit(1); } -- cgit 1.4.1 From 4bed22c6876b5eb40b8ef09c85265007e3960e13 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 14 Apr 2023 17:47:10 +0200 Subject: selftests: mptcp: join: fix ShellCheck warnings commit 0fcd72df8847d3a62eb34a084862157ce0564a94 upstream. Most of the code had an issue according to ShellCheck. That's mainly due to the fact it incorrectly believes most of the code was unreachable because it's invoked by variable name, see how the "tests" array is used. Once SC2317 has been ignored, three small warnings were still visible: - SC2155: Declare and assign separately to avoid masking return values. - SC2046: Quote this to prevent word splitting: can be ignored because "ip netns pids" can display more than one pid. - SC2166: Prefer [ p ] || [ q ] as [ p -o q ] is not well defined. This probably didn't fix any actual issues but it might help spotting new interesting warnings reported by ShellCheck as just before, ShellCheck was reporting issues for most lines making it a bit useless. Signed-off-by: Matthieu Baerts Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 6be19b103824..5a2c56efd0b4 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -8,6 +8,10 @@ . "$(dirname "${0}")/mptcp_lib.sh" +# ShellCheck incorrectly believes that most of the code here is unreachable +# because it's invoked by variable name, see how the "tests" array is used +#shellcheck disable=SC2317 + ret=0 sin="" sinfail="" @@ -357,8 +361,9 @@ check_transfer() local line if [ -n "$bytes" ]; then + local out_size # when truncating we must check the size explicitly - local out_size=$(wc -c $out | awk '{print $1}') + out_size=$(wc -c $out | awk '{print $1}') if [ $out_size -ne $bytes ]; then echo "[ FAIL ] $what output file has wrong size ($out_size, $bytes)" fail_test @@ -487,6 +492,7 @@ kill_wait() kill_tests_wait() { + #shellcheck disable=SC2046 kill -SIGUSR1 $(ip netns pids $ns2) $(ip netns pids $ns1) wait } @@ -1726,7 +1732,7 @@ chk_subflow_nr() cnt1=$(ss -N $ns1 -tOni | grep -c token) cnt2=$(ss -N $ns2 -tOni | grep -c token) - if [ "$cnt1" != "$subflow_nr" -o "$cnt2" != "$subflow_nr" ]; then + if [ "$cnt1" != "$subflow_nr" ] || [ "$cnt2" != "$subflow_nr" ]; then echo "[fail] got $cnt1:$cnt2 subflows expected $subflow_nr" fail_test dump_stats=1 -- cgit 1.4.1 From 9ead68270b29a9a2ee7b84242cd8e5da044fbd43 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:43 +0200 Subject: selftests: mptcp: lib: skip if missing symbol commit 673004821ab98c6645bd21af56a290854e88f533 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. New functions are now available to easily detect if a certain feature is missing by looking at kallsyms. These new helpers are going to be used in the following commits. In order to ease the backport of such future patches, it would be good if this patch is backported up to the introduction of MPTCP selftests, hence the Fixes tag below: this type of check was supposed to be done from the beginning. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 048d19d444be ("mptcp: add basic kselftest for mptcp") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/config | 1 + tools/testing/selftests/net/mptcp/mptcp_lib.sh | 38 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/config b/tools/testing/selftests/net/mptcp/config index 38021a0dd527..6032f9b23c4c 100644 --- a/tools/testing/selftests/net/mptcp/config +++ b/tools/testing/selftests/net/mptcp/config @@ -1,3 +1,4 @@ +CONFIG_KALLSYMS=y CONFIG_MPTCP=y CONFIG_IPV6=y CONFIG_MPTCP_IPV6=y diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh index 3286536b79d5..29b65f4b73b2 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh @@ -38,3 +38,41 @@ mptcp_lib_check_mptcp() { exit ${KSFT_SKIP} fi } + +mptcp_lib_check_kallsyms() { + if ! mptcp_lib_has_file "/proc/kallsyms"; then + echo "SKIP: CONFIG_KALLSYMS is missing" + exit ${KSFT_SKIP} + fi +} + +# Internal: use mptcp_lib_kallsyms_has() instead +__mptcp_lib_kallsyms_has() { + local sym="${1}" + + mptcp_lib_check_kallsyms + + grep -q " ${sym}" /proc/kallsyms +} + +# $1: part of a symbol to look at, add '$' at the end for full name +mptcp_lib_kallsyms_has() { + local sym="${1}" + + if __mptcp_lib_kallsyms_has "${sym}"; then + return 0 + fi + + mptcp_lib_fail_if_expected_feature "${sym} symbol not found" +} + +# $1: part of a symbol to look at, add '$' at the end for full name +mptcp_lib_kallsyms_doesnt_have() { + local sym="${1}" + + if ! __mptcp_lib_kallsyms_has "${sym}"; then + return 0 + fi + + mptcp_lib_fail_if_expected_feature "${sym} symbol has been found" +} -- cgit 1.4.1 From cba0db9c158666efb84ae9e0da8e5965ce556379 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:44 +0200 Subject: selftests: mptcp: connect: skip transp tests if not supported commit 07bf49401909264a38fa3427c3cce43e8304436a upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the support of IP(V6)_TRANSPARENT socket option with MPTCP connections introduced by commit c9406a23c116 ("mptcp: sockopt: add SOL_IP freebind & transparent options"). It is possible to look for "__ip_sock_set_tos" in kallsyms because IP(V6)_TRANSPARENT socket option support has been added after TOS support which came with the required infrastructure in MPTCP sockopt code. To support TOS, the following function has been exported (T). Not great but better than checking for a specific kernel version. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 5fb62e9cd3ad ("selftests: mptcp: add tproxy test case") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_connect.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index c923ce5ff6eb..cc447c8411e1 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -144,6 +144,7 @@ cleanup() } mptcp_lib_check_mptcp +mptcp_lib_check_kallsyms ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then @@ -696,6 +697,15 @@ run_test_transparent() return 0 fi + # IP(V6)_TRANSPARENT has been added after TOS support which came with + # the required infrastructure in MPTCP sockopt code. To support TOS, the + # following function has been exported (T). Not great but better than + # checking for a specific kernel version. + if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then + echo "INFO: ${msg} not supported by the kernel: SKIP" + return + fi + ip netns exec "$listener_ns" nft -f /dev/stdin <<"EOF" flush ruleset table inet mangle { -- cgit 1.4.1 From e79e5e7642ad2e9a174fc21c93e12de7b1248ae1 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:45 +0200 Subject: selftests: mptcp: connect: skip disconnect tests if not supported commit 4ad39a42da2e9770c8e4c37fe632ed8898419129 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the full support of disconnections from the userspace introduced by commit b29fcfb54cd7 ("mptcp: full disconnect implementation"). It is possible to look for "mptcp_pm_data_reset" in kallsyms because a preparation patch added it to ease the introduction of the mentioned feature. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 05be5e273c84 ("selftests: mptcp: add disconnect tests") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_connect.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index cc447c8411e1..36dc2bab7a13 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -782,6 +782,11 @@ run_tests_disconnect() local old_cin=$cin local old_sin=$sin + if ! mptcp_lib_kallsyms_has "mptcp_pm_data_reset$"; then + echo "INFO: Full disconnect not supported: SKIP" + return + fi + cat $cin $cin $cin > "$cin".disconnect # force do_transfer to cope with the multiple tranmissions -- cgit 1.4.1 From 41f7f7f6e43e00b2b479d17c76c0359501ce58d4 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:49 +0200 Subject: selftests: mptcp: pm nl: remove hardcoded default limits commit 2177d0b08e421971e035672b70f3228d9485c650 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the checks of the default limits returned by the MPTCP in-kernel path-manager. The default values have been modified by commit 72bcbc46a5c3 ("mptcp: increase default max additional subflows to 2"). Instead of comparing with hardcoded values, we can get the default one and compare with them. Note that if we expect to have the latest version, we continue to check the hardcoded values to avoid unexpected behaviour changes. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: eedbc685321b ("selftests: add PM netlink functional tests") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/pm_netlink.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index 32f7533e0919..664cafc60705 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -73,8 +73,12 @@ check() } check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "defaults addr list" -check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 + +default_limits="$(ip netns exec $ns1 ./pm_nl_ctl limits)" +if mptcp_lib_expect_all_features; then + check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 subflows 2" "defaults limits" +fi ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1 ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2 flags subflow dev lo @@ -121,12 +125,10 @@ ip netns exec $ns1 ./pm_nl_ctl flush check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "flush addrs" ip netns exec $ns1 ./pm_nl_ctl limits 9 1 -check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 -subflows 2" "rcv addrs above hard limit" +check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "rcv addrs above hard limit" ip netns exec $ns1 ./pm_nl_ctl limits 1 9 -check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 -subflows 2" "subflows above hard limit" +check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "subflows above hard limit" ip netns exec $ns1 ./pm_nl_ctl limits 8 8 check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 8 -- cgit 1.4.1 From 61c1bf0666a969dee1dac3665f12d7052f9f616d Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:50 +0200 Subject: selftests: mptcp: pm nl: skip fullmesh flag checks if not supported commit f3761b50b8e4cb4807b5d41e02144c8c8a0f2512 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the fullmesh flag that can be given to the MPTCP in-kernel path-manager and introduced in commit 2843ff6f36db ("mptcp: remote addresses fullmesh"). If the flag is not visible in the dump after having set it, we don't check the content. Note that if we expect to have this feature and SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var is set to 1, we always check the content to avoid regressions. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 6da1dfdd037e ("selftests: mptcp: add set_flags tests in pm_netlink.sh") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/pm_netlink.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index 664cafc60705..d02e0d63a8f9 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -178,14 +178,19 @@ subflow,backup 10.0.1.1" "set flags (backup)" ip netns exec $ns1 ./pm_nl_ctl set 10.0.1.1 flags nobackup check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow 10.0.1.1" " (nobackup)" + +# fullmesh support has been added later ip netns exec $ns1 ./pm_nl_ctl set id 1 flags fullmesh -check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ +if ip netns exec $ns1 ./pm_nl_ctl dump | grep -q "fullmesh" || + mptcp_lib_expect_all_features; then + check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow,fullmesh 10.0.1.1" " (fullmesh)" -ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh -check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ + ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh + check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow 10.0.1.1" " (nofullmesh)" -ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh -check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ + ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh + check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow,backup,fullmesh 10.0.1.1" " (backup,fullmesh)" +fi exit $ret -- cgit 1.4.1 From 103b4e62de32fcc7a756dd79b94df7c0f4c62148 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:51 +0200 Subject: selftests: mptcp: sockopt: relax expected returned size commit 8dee6ca2ac1e5630a7bb6a98bc0b686916fc2000 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the getsockopt(SOL_MPTCP) to get info about the MPTCP connections introduced by commit 55c42fa7fa33 ("mptcp: add MPTCP_INFO getsockopt") and the following ones. We cannot guess in advance which sizes the kernel will returned: older kernel can returned smaller sizes, e.g. recently the tcp_info structure has been modified in commit 71fc704768f6 ("tcp: add rcv_wnd and plb_rehash to TCP_INFO") where a new field has been added. The userspace can also expect a smaller size if it is compiled with old uAPI kernel headers. So for these sizes, we can only check if they are above a certain threshold, 0 for the moment. We can also only compared sizes with the ones set by the kernel. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: ce9979129a0b ("selftests: mptcp: add mptcp getsockopt test cases") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index ae61f39556ca..b35148edbf02 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -87,6 +87,10 @@ struct so_state { uint64_t tcpi_rcv_delta; }; +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + static void die_perror(const char *msg) { perror(msg); @@ -349,13 +353,14 @@ static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)"); assert(olen <= sizeof(ti)); - assert(ti.d.size_user == ti.d.size_kernel); - assert(ti.d.size_user == sizeof(struct tcp_info)); + assert(ti.d.size_kernel > 0); + assert(ti.d.size_user == + MIN(ti.d.size_kernel, sizeof(struct tcp_info))); assert(ti.d.num_subflows == 1); assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data)); olen -= sizeof(struct mptcp_subflow_data); - assert(olen == sizeof(struct tcp_info)); + assert(olen == ti.d.size_user); if (ti.ti[0].tcpi_bytes_sent == w && ti.ti[0].tcpi_bytes_received == r) @@ -401,13 +406,14 @@ static void do_getsockopt_subflow_addrs(int fd) die_perror("getsockopt MPTCP_SUBFLOW_ADDRS"); assert(olen <= sizeof(addrs)); - assert(addrs.d.size_user == addrs.d.size_kernel); - assert(addrs.d.size_user == sizeof(struct mptcp_subflow_addrs)); + assert(addrs.d.size_kernel > 0); + assert(addrs.d.size_user == + MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs))); assert(addrs.d.num_subflows == 1); assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data)); olen -= sizeof(struct mptcp_subflow_data); - assert(olen == sizeof(struct mptcp_subflow_addrs)); + assert(olen == addrs.d.size_user); llen = sizeof(local); ret = getsockname(fd, (struct sockaddr *)&local, &llen); -- cgit 1.4.1 From bfe225dec643652094d3f6ec70ea7c77e429136d Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:52 +0200 Subject: selftests: mptcp: sockopt: skip getsockopt checks if not supported commit c6f7eccc519837ebde1d099d9610c4f1d5bd975e upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the getsockopt(SOL_MPTCP) to get info about the MPTCP connections introduced by commit 55c42fa7fa33 ("mptcp: add MPTCP_INFO getsockopt") and the following ones. It is possible to look for "mptcp_diag_fill_info" in kallsyms because it is introduced by the mentioned feature. So we can know in advance if the feature is supported and skip the sub-test if not. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: ce9979129a0b ("selftests: mptcp: add mptcp getsockopt test cases") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index 08d8533c98c4..d31be7229cdb 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -84,6 +84,7 @@ cleanup() } mptcp_lib_check_mptcp +mptcp_lib_check_kallsyms ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then @@ -248,6 +249,11 @@ do_mptcp_sockopt_tests() { local lret=0 + if ! mptcp_lib_kallsyms_has "mptcp_diag_fill_info$"; then + echo "INFO: MPTCP sockopt not supported: SKIP" + return + fi + ip netns exec "$ns_sbox" ./mptcp_sockopt lret=$? -- cgit 1.4.1 From 733bf9d80d95ffa9cc83741f4245022782e9dfac Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:54 +0200 Subject: selftests: mptcp: userspace pm: skip if 'ip' tool is unavailable commit 723d6b9b12338c1caf06bf6fe269962ef04e2c71 upstream. When a required tool is missing, the return code 4 (SKIP) should be returned instead of 1 (FAIL). Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 259a834fadda ("selftests: mptcp: functional tests for the userspace PM type") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/userspace_pm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 084a2b0a197e..0ec4c2a1d73d 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -8,7 +8,7 @@ mptcp_lib_check_mptcp ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Cannot not run test without ip tool" - exit 1 + exit ${KSFT_SKIP} fi ANNOUNCED=6 # MPTCP_EVENT_ANNOUNCED -- cgit 1.4.1 From c5bdd8eb8e7dd9ba718b60ce322fbf8c011c349d Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:55 +0200 Subject: selftests: mptcp: userspace pm: skip if not supported commit f90adb033891d418c5dafef34a9aa49f3c860991 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the MPTCP Userspace PM introduced by commit 4638de5aefe5 ("mptcp: handle local addrs announced by userspace PMs"). We can skip all these tests if the feature is not supported simply by looking for the MPTCP pm_type's sysctl knob. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 259a834fadda ("selftests: mptcp: functional tests for the userspace PM type") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/userspace_pm.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 0ec4c2a1d73d..eb0f4f6afebd 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -5,6 +5,11 @@ mptcp_lib_check_mptcp +if ! mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then + echo "userspace pm tests are not supported by the kernel: SKIP" + exit ${KSFT_SKIP} +fi + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Cannot not run test without ip tool" -- cgit 1.4.1 From 44d3366bf4b95ef4a9601158943f70942b57c39d Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:36 +0200 Subject: selftests: mptcp: lib: skip if not below kernel version commit b1a6a38ab8a633546cefae890da842f19e006c74 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. A new function is now available to easily detect if a feature is missing by looking at the kernel version. That's clearly not ideal and this kind of check should be avoided as soon as possible. But sometimes, there are no external sign that a "feature" is available or not: internal behaviours can change without modifying the uAPI and these selftests are verifying the internal behaviours. Sometimes, the only (easy) way to verify if the feature is present is to run the test but then the validation cannot determine if there is a failure with the feature or if the feature is missing. Then it looks better to check the kernel version instead of having tests that can never fail. In any case, we need a solution not to have a whole selftest being marked as failed just because one sub-test has failed. Note that this env var car be set to 1 not to do such check and run the linked sub-test: SELFTESTS_MPTCP_LIB_NO_KVERSION_CHECK. This new helper is going to be used in the following commits. In order to ease the backport of such future patches, it would be good if this patch is backported up to the introduction of MPTCP selftests, hence the Fixes tag below: this type of check was supposed to be done from the beginning. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 048d19d444be ("mptcp: add basic kselftest for mptcp") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_lib.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh index 29b65f4b73b2..f32045b23b89 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh @@ -76,3 +76,29 @@ mptcp_lib_kallsyms_doesnt_have() { mptcp_lib_fail_if_expected_feature "${sym} symbol has been found" } + +# !!!AVOID USING THIS!!! +# Features might not land in the expected version and features can be backported +# +# $1: kernel version, e.g. 6.3 +mptcp_lib_kversion_ge() { + local exp_maj="${1%.*}" + local exp_min="${1#*.}" + local v maj min + + # If the kernel has backported features, set this env var to 1: + if [ "${SELFTESTS_MPTCP_LIB_NO_KVERSION_CHECK:-}" = "1" ]; then + return 0 + fi + + v=$(uname -r | cut -d'.' -f1,2) + maj=${v%.*} + min=${v#*.} + + if [ "${maj}" -gt "${exp_maj}" ] || + { [ "${maj}" -eq "${exp_maj}" ] && [ "${min}" -ge "${exp_min}" ]; }; then + return 0 + fi + + mptcp_lib_fail_if_expected_feature "kernel version ${1} lower than ${v}" +} -- cgit 1.4.1 From 4d65ec947d24f48700efa09ec51480c83dea62ea Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:37 +0200 Subject: selftests: mptcp: join: use 'iptables-legacy' if available commit 0c4cd3f86a40028845ad6f8af5b37165666404cd upstream. IPTables commands using 'iptables-nft' fail on old kernels, at least 5.15 because it doesn't see the default IPTables chains: $ iptables -L iptables/1.8.2 Failed to initialize nft: Protocol not supported As a first step before switching to NFTables, we can use iptables-legacy if available. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 8d014eaa9254 ("selftests: mptcp: add ADD_ADDR timeout test case") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 5a2c56efd0b4..d585c76ee28a 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -25,6 +25,8 @@ capout="" ns1="" ns2="" ksft_skip=4 +iptables="iptables" +ip6tables="ip6tables" timeout_poll=30 timeout_test=$((timeout_poll * 2 + 1)) capture=0 @@ -141,7 +143,11 @@ check_tools() exit $ksft_skip fi - if ! iptables -V &> /dev/null; then + # Use the legacy version if available to support old kernel versions + if iptables-legacy -V &> /dev/null; then + iptables="iptables-legacy" + ip6tables="ip6tables-legacy" + elif ! iptables -V &> /dev/null; then echo "SKIP: Could not run all tests without iptables tool" exit $ksft_skip fi @@ -239,9 +245,9 @@ reset_with_add_addr_timeout() reset "${1}" || return 1 - tables="iptables" + tables="${iptables}" if [ $ip -eq 6 ]; then - tables="ip6tables" + tables="${ip6tables}" fi ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 @@ -306,9 +312,9 @@ reset_with_fail() local ip="${3:-4}" local tables - tables="iptables" + tables="${iptables}" if [ $ip -eq 6 ]; then - tables="ip6tables" + tables="${ip6tables}" fi ip netns exec $ns2 $tables \ @@ -678,7 +684,7 @@ filter_tcp_from() local src="${2}" local target="${3}" - ip netns exec "${ns}" iptables -A INPUT -s "${src}" -p tcp -j "${target}" + ip netns exec "${ns}" ${iptables} -A INPUT -s "${src}" -p tcp -j "${target}" } do_transfer() -- cgit 1.4.1 From e35edb09e53e38edf306efb447c96300f5403f3e Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:38 +0200 Subject: selftests: mptcp: join: helpers to skip tests commit cdb50525345cf5a8359ee391032ef606a7826f08 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. Here are some helpers that will be used to mark subtests as skipped if a feature is not supported. Marking as a fix for the commit introducing this selftest to help with the backports. While at it, also check if kallsyms feature is available as it will also be used in the following commits to check if MPTCP features are available before starting a test. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: b08fbf241064 ("selftests: add test-cases for MPTCP MP_JOIN") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index d585c76ee28a..3525ad3dfc00 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -137,6 +137,7 @@ cleanup_partial() check_tools() { mptcp_lib_check_mptcp + mptcp_lib_check_kallsyms if ! ip -Version &> /dev/null; then echo "SKIP: Could not run test without ip tool" @@ -183,6 +184,32 @@ cleanup() cleanup_partial } +# $1: msg +print_title() +{ + printf "%03u %-36s %s" "${TEST_COUNT}" "${TEST_NAME}" "${1}" +} + +# [ $1: fail msg ] +mark_as_skipped() +{ + local msg="${1:-"Feature not supported"}" + + mptcp_lib_fail_if_expected_feature "${msg}" + + print_title "[ skip ] ${msg}" + printf "\n" +} + +# $@: condition +continue_if() +{ + if ! "${@}"; then + mark_as_skipped + return 1 + fi +} + skip_test() { if [ "${#only_tests_ids[@]}" -eq 0 ] && [ "${#only_tests_names[@]}" -eq 0 ]; then -- cgit 1.4.1 From 1c0d9b4b47459921482652b4269132e2e25d3cc6 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:39 +0200 Subject: selftests: mptcp: join: skip check if MIB counter not supported commit 47867f0a7e831e24e5eab3330667ce9682d50fb1 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the MPTCP MIB counters introduced in commit fc518953bc9c ("mptcp: add and use MIB counter infrastructure") and more later. The MPTCP Join selftest heavily relies on these counters. If a counter is not supported by the kernel, it is not displayed when using 'nstat -z'. We can then detect that and skip the verification. A new helper (get_counter()) has been added to do the required checks and return an error if the counter is not available. Note that if we expect to have these features available and if SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var is set to 1, the tests will be marked as failed instead of skipped. This new helper also makes sure we get the exact counter we want to avoid issues we had in the past, e.g. with MPTcpExtRmAddr and MPTcpExtRmAddrDrop sharing the same prefix. While at it, we uniform the way we fetch a MIB counter. Note for the backports: we rarely change these modified blocks so if there is are conflicts, it is very likely because a counter is not used in the older kernels and we don't need that chunk. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: b08fbf241064 ("selftests: add test-cases for MPTCP MP_JOIN") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 230 +++++++++++++----------- 1 file changed, 130 insertions(+), 100 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 3525ad3dfc00..c14d4dbb08d7 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -480,11 +480,25 @@ wait_local_port_listen() done } -rm_addr_count() +# $1: ns ; $2: counter +get_counter() { - local ns=${1} + local ns="${1}" + local counter="${2}" + local count + + count=$(ip netns exec ${ns} nstat -asz "${counter}" | awk 'NR==1 {next} {print $2}') + if [ -z "${count}" ]; then + mptcp_lib_fail_if_expected_feature "${counter} counter" + return 1 + fi - ip netns exec ${ns} nstat -as | grep MPTcpExtRmAddr | awk '{print $2}' + echo "${count}" +} + +rm_addr_count() +{ + get_counter "${1}" "MPTcpExtRmAddr" } # $1: ns, $2: old rm_addr counter in $ns @@ -507,11 +521,11 @@ wait_mpj() local ns="${1}" local cnt old_cnt - old_cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}') + old_cnt=$(get_counter ${ns} "MPTcpExtMPJoinAckRx") local i for i in $(seq 10); do - cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}') + cnt=$(get_counter ${ns} "MPTcpExtMPJoinAckRx") [ "$cnt" = "${old_cnt}" ] || break sleep 0.1 done @@ -1191,12 +1205,13 @@ chk_csum_nr() fi printf "%-${nr_blank}s %s" " " "sum" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns1} "MPTcpExtDataCsumErr") if [ "$count" != "$csum_ns1" ]; then extra_msg="$extra_msg ns1=$count" fi - if { [ "$count" != $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 0 ]; } || { [ "$count" -lt $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 1 ]; }; then echo "[fail] got $count data checksum error[s] expected $csum_ns1" fail_test @@ -1205,12 +1220,13 @@ chk_csum_nr() echo -n "[ ok ]" fi echo -n " - csum " - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns2} "MPTcpExtDataCsumErr") if [ "$count" != "$csum_ns2" ]; then extra_msg="$extra_msg ns2=$count" fi - if { [ "$count" != $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 0 ]; } || { [ "$count" -lt $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 1 ]; }; then echo "[fail] got $count data checksum error[s] expected $csum_ns2" fail_test @@ -1252,12 +1268,13 @@ chk_fail_nr() fi printf "%-${nr_blank}s %s" " " "ftx" - count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPFailTx | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns_tx} "MPTcpExtMPFailTx") if [ "$count" != "$fail_tx" ]; then extra_msg="$extra_msg,tx=$count" fi - if { [ "$count" != "$fail_tx" ] && [ $allow_tx_lost -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != "$fail_tx" ] && [ $allow_tx_lost -eq 0 ]; } || { [ "$count" -gt "$fail_tx" ] && [ $allow_tx_lost -eq 1 ]; }; then echo "[fail] got $count MP_FAIL[s] TX expected $fail_tx" fail_test @@ -1267,12 +1284,13 @@ chk_fail_nr() fi echo -n " - failrx" - count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPFailRx | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns_rx} "MPTcpExtMPFailRx") if [ "$count" != "$fail_rx" ]; then extra_msg="$extra_msg,rx=$count" fi - if { [ "$count" != "$fail_rx" ] && [ $allow_rx_lost -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != "$fail_rx" ] && [ $allow_rx_lost -eq 0 ]; } || { [ "$count" -gt "$fail_rx" ] && [ $allow_rx_lost -eq 1 ]; }; then echo "[fail] got $count MP_FAIL[s] RX expected $fail_rx" fail_test @@ -1304,10 +1322,11 @@ chk_fclose_nr() fi printf "%-${nr_blank}s %s" " " "ctx" - count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPFastcloseTx | awk '{print $2}') - [ -z "$count" ] && count=0 - [ "$count" != "$fclose_tx" ] && extra_msg="$extra_msg,tx=$count" - if [ "$count" != "$fclose_tx" ]; then + count=$(get_counter ${ns_tx} "MPTcpExtMPFastcloseTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$fclose_tx" ]; then + extra_msg="$extra_msg,tx=$count" echo "[fail] got $count MP_FASTCLOSE[s] TX expected $fclose_tx" fail_test dump_stats=1 @@ -1316,10 +1335,11 @@ chk_fclose_nr() fi echo -n " - fclzrx" - count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPFastcloseRx | awk '{print $2}') - [ -z "$count" ] && count=0 - [ "$count" != "$fclose_rx" ] && extra_msg="$extra_msg,rx=$count" - if [ "$count" != "$fclose_rx" ]; then + count=$(get_counter ${ns_rx} "MPTcpExtMPFastcloseRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$fclose_rx" ]; then + extra_msg="$extra_msg,rx=$count" echo "[fail] got $count MP_FASTCLOSE[s] RX expected $fclose_rx" fail_test dump_stats=1 @@ -1350,9 +1370,10 @@ chk_rst_nr() fi printf "%-${nr_blank}s %s" " " "rtx" - count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPRstTx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ $count -lt $rst_tx ]; then + count=$(get_counter ${ns_tx} "MPTcpExtMPRstTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ $count -lt $rst_tx ]; then echo "[fail] got $count MP_RST[s] TX expected $rst_tx" fail_test dump_stats=1 @@ -1361,9 +1382,10 @@ chk_rst_nr() fi echo -n " - rstrx " - count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPRstRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" -lt "$rst_rx" ]; then + count=$(get_counter ${ns_rx} "MPTcpExtMPRstRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" -lt "$rst_rx" ]; then echo "[fail] got $count MP_RST[s] RX expected $rst_rx" fail_test dump_stats=1 @@ -1384,9 +1406,10 @@ chk_infi_nr() local dump_stats printf "%-${nr_blank}s %s" " " "itx" - count=$(ip netns exec $ns2 nstat -as | grep InfiniteMapTx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$infi_tx" ]; then + count=$(get_counter ${ns2} "MPTcpExtInfiniteMapTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$infi_tx" ]; then echo "[fail] got $count infinite map[s] TX expected $infi_tx" fail_test dump_stats=1 @@ -1395,9 +1418,10 @@ chk_infi_nr() fi echo -n " - infirx" - count=$(ip netns exec $ns1 nstat -as | grep InfiniteMapRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$infi_rx" ]; then + count=$(get_counter ${ns1} "MPTcpExtInfiniteMapRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$infi_rx" ]; then echo "[fail] got $count infinite map[s] RX expected $infi_rx" fail_test dump_stats=1 @@ -1429,9 +1453,10 @@ chk_join_nr() fi printf "%03u %-36s %s" "${TEST_COUNT}" "${title}" "syn" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinSynRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinSynRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_nr" ]; then echo "[fail] got $count JOIN[s] syn expected $syn_nr" fail_test dump_stats=1 @@ -1441,9 +1466,10 @@ chk_join_nr() echo -n " - synack" with_cookie=$(ip netns exec $ns2 sysctl -n net.ipv4.tcp_syncookies) - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinSynAckRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_ack_nr" ]; then + count=$(get_counter ${ns2} "MPTcpExtMPJoinSynAckRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_ack_nr" ]; then # simult connections exceeding the limit with cookie enabled could go up to # synack validation as the conn limit can be enforced reliably only after # the subflow creation @@ -1459,9 +1485,10 @@ chk_join_nr() fi echo -n " - ack" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinAckRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$ack_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinAckRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$ack_nr" ]; then echo "[fail] got $count JOIN[s] ack expected $ack_nr" fail_test dump_stats=1 @@ -1493,12 +1520,12 @@ chk_stale_nr() local recover_nr printf "%-${nr_blank}s %-18s" " " "stale" - stale_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowStale | awk '{print $2}') - [ -z "$stale_nr" ] && stale_nr=0 - recover_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowRecover | awk '{print $2}') - [ -z "$recover_nr" ] && recover_nr=0 - if [ $stale_nr -lt $stale_min ] || + stale_nr=$(get_counter ${ns} "MPTcpExtSubflowStale") + recover_nr=$(get_counter ${ns} "MPTcpExtSubflowRecover") + if [ -z "$stale_nr" ] || [ -z "$recover_nr" ]; then + echo "[skip]" + elif [ $stale_nr -lt $stale_min ] || { [ $stale_max -gt 0 ] && [ $stale_nr -gt $stale_max ]; } || [ $((stale_nr - recover_nr)) -ne $stale_delta ]; then echo "[fail] got $stale_nr stale[s] $recover_nr recover[s], " \ @@ -1534,12 +1561,12 @@ chk_add_nr() timeout=$(ip netns exec $ns1 sysctl -n net.mptcp.add_addr_timeout) printf "%-${nr_blank}s %s" " " "add" - count=$(ip netns exec $ns2 nstat -as MPTcpExtAddAddr | grep MPTcpExtAddAddr | awk '{print $2}') - [ -z "$count" ] && count=0 - + count=$(get_counter ${ns2} "MPTcpExtAddAddr") + if [ -z "$count" ]; then + echo -n "[skip]" # if the test configured a short timeout tolerate greater then expected # add addrs options, due to retransmissions - if [ "$count" != "$add_nr" ] && { [ "$timeout" -gt 1 ] || [ "$count" -lt "$add_nr" ]; }; then + elif [ "$count" != "$add_nr" ] && { [ "$timeout" -gt 1 ] || [ "$count" -lt "$add_nr" ]; }; then echo "[fail] got $count ADD_ADDR[s] expected $add_nr" fail_test dump_stats=1 @@ -1548,9 +1575,10 @@ chk_add_nr() fi echo -n " - echo " - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtEchoAdd | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$echo_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtEchoAdd") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$echo_nr" ]; then echo "[fail] got $count ADD_ADDR echo[s] expected $echo_nr" fail_test dump_stats=1 @@ -1560,9 +1588,10 @@ chk_add_nr() if [ $port_nr -gt 0 ]; then echo -n " - pt " - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtPortAdd | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$port_nr" ]; then + count=$(get_counter ${ns2} "MPTcpExtPortAdd") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$port_nr" ]; then echo "[fail] got $count ADD_ADDR[s] with a port-number expected $port_nr" fail_test dump_stats=1 @@ -1571,10 +1600,10 @@ chk_add_nr() fi printf "%-${nr_blank}s %s" " " "syn" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortSynRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinPortSynRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_nr" ]; then echo "[fail] got $count JOIN[s] syn with a different \ port-number expected $syn_nr" fail_test @@ -1584,10 +1613,10 @@ chk_add_nr() fi echo -n " - synack" - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinPortSynAckRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_ack_nr" ]; then + count=$(get_counter ${ns2} "MPTcpExtMPJoinPortSynAckRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_ack_nr" ]; then echo "[fail] got $count JOIN[s] synack with a different \ port-number expected $syn_ack_nr" fail_test @@ -1597,10 +1626,10 @@ chk_add_nr() fi echo -n " - ack" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortAckRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$ack_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinPortAckRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$ack_nr" ]; then echo "[fail] got $count JOIN[s] ack with a different \ port-number expected $ack_nr" fail_test @@ -1610,10 +1639,10 @@ chk_add_nr() fi printf "%-${nr_blank}s %s" " " "syn" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortSynRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mis_syn_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMismatchPortSynRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$mis_syn_nr" ]; then echo "[fail] got $count JOIN[s] syn with a mismatched \ port-number expected $mis_syn_nr" fail_test @@ -1623,10 +1652,10 @@ chk_add_nr() fi echo -n " - ack " - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortAckRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mis_ack_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMismatchPortAckRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$mis_ack_nr" ]; then echo "[fail] got $count JOIN[s] ack with a mismatched \ port-number expected $mis_ack_nr" fail_test @@ -1670,9 +1699,10 @@ chk_rm_nr() fi printf "%-${nr_blank}s %s" " " "rm " - count=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmAddr | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$rm_addr_nr" ]; then + count=$(get_counter ${addr_ns} "MPTcpExtRmAddr") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$rm_addr_nr" ]; then echo "[fail] got $count RM_ADDR[s] expected $rm_addr_nr" fail_test dump_stats=1 @@ -1681,29 +1711,27 @@ chk_rm_nr() fi echo -n " - rmsf " - count=$(ip netns exec $subflow_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ -n "$simult" ]; then + count=$(get_counter ${subflow_ns} "MPTcpExtRmSubflow") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ -n "$simult" ]; then local cnt suffix - cnt=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}') + cnt=$(get_counter ${addr_ns} "MPTcpExtRmSubflow") # in case of simult flush, the subflow removal count on each side is # unreliable - [ -z "$cnt" ] && cnt=0 count=$((count + cnt)) [ "$count" != "$rm_subflow_nr" ] && suffix="$count in [$rm_subflow_nr:$((rm_subflow_nr*2))]" if [ $count -ge "$rm_subflow_nr" ] && \ [ "$count" -le "$((rm_subflow_nr *2 ))" ]; then - echo "[ ok ] $suffix" + echo -n "[ ok ] $suffix" else echo "[fail] got $count RM_SUBFLOW[s] expected in range [$rm_subflow_nr:$((rm_subflow_nr*2))]" fail_test dump_stats=1 fi - return - fi - if [ "$count" != "$rm_subflow_nr" ]; then + elif [ "$count" != "$rm_subflow_nr" ]; then echo "[fail] got $count RM_SUBFLOW[s] expected $rm_subflow_nr" fail_test dump_stats=1 @@ -1724,9 +1752,10 @@ chk_prio_nr() local dump_stats printf "%-${nr_blank}s %s" " " "ptx" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioTx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mp_prio_nr_tx" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPPrioTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$mp_prio_nr_tx" ]; then echo "[fail] got $count MP_PRIO[s] TX expected $mp_prio_nr_tx" fail_test dump_stats=1 @@ -1735,9 +1764,10 @@ chk_prio_nr() fi echo -n " - prx " - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mp_prio_nr_rx" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPPrioRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$mp_prio_nr_rx" ]; then echo "[fail] got $count MP_PRIO[s] RX expected $mp_prio_nr_rx" fail_test dump_stats=1 @@ -1813,7 +1843,7 @@ wait_attempt_fail() while [ $time -lt $timeout_ms ]; do local cnt - cnt=$(ip netns exec $ns nstat -as TcpAttemptFails | grep TcpAttemptFails | awk '{print $2}') + cnt=$(get_counter ${ns} "TcpAttemptFails") [ "$cnt" = 1 ] && return 1 time=$((time + 100)) -- cgit 1.4.1 From 0381f30735e2b4394be10d57da74f229a69bd4b0 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:41 +0200 Subject: selftests: mptcp: join: support local endpoint being tracked or not commit d4c81bbb8600257fd3076d0196cb08bd2e5bdf24 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. At some points, a new feature caused internal behaviour changes we are verifying in the selftests, see the Fixes tag below. It was not a uAPI change but because in these selftests, we check some internal behaviours, it is normal we have to adapt them from time to time after having added some features. It is possible to look for "mptcp_pm_subflow_check_next" in kallsyms because it was needed to introduce the mentioned feature. So we can know in advance what the behaviour we are expecting here instead of supporting the two behaviours. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 86e39e04482b ("mptcp: keep track of local endpoint still available for each msk") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index c14d4dbb08d7..61cbc1053b3f 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -2064,11 +2064,18 @@ signal_address_tests() # the peer could possibly miss some addr notification, allow retransmission ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow - chk_join_nr 3 3 3 - # the server will not signal the address terminating - # the MPC subflow - chk_add_nr 3 3 + # It is not directly linked to the commit introducing this + # symbol but for the parent one which is linked anyway. + if ! mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then + chk_join_nr 3 3 2 + chk_add_nr 4 4 + else + chk_join_nr 3 3 3 + # the server will not signal the address terminating + # the MPC subflow + chk_add_nr 3 3 + fi fi } -- cgit 1.4.1 From 695cce2f2cf55225be38bb612796d833909dde4b Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:42 +0200 Subject: selftests: mptcp: join: skip Fastclose tests if not supported commit ae947bb2c253ff5f395bb70cb9db8700543bf398 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the support of MP_FASTCLOSE introduced in commit f284c0c77321 ("mptcp: implement fastclose xmit path"). If the MIB counter is not available, the test cannot be verified and the behaviour will not be the expected one. So we can skip the test if the counter is missing. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 01542c9bf9ab ("selftests: mptcp: add fastclose testcase") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 61cbc1053b3f..6fa3bf7f80a3 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -253,6 +253,19 @@ reset() return 0 } +# $1: test name ; $2: counter to check +reset_check_counter() +{ + reset "${1}" || return 1 + + local counter="${2}" + + if ! nstat -asz "${counter}" | grep -wq "${counter}"; then + mark_as_skipped "counter '${counter}' is not available" + return 1 + fi +} + # $1: test name reset_with_cookies() { @@ -2959,14 +2972,14 @@ fullmesh_tests() fastclose_tests() { - if reset "fastclose test"; then + if reset_check_counter "fastclose test" "MPTcpExtMPFastcloseTx"; then run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_client chk_join_nr 0 0 0 chk_fclose_nr 1 1 chk_rst_nr 1 1 invert fi - if reset "fastclose server test"; then + if reset_check_counter "fastclose server test" "MPTcpExtMPFastcloseRx"; then run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_server chk_join_nr 0 0 0 chk_fclose_nr 1 1 invert -- cgit 1.4.1 From dd6c284a343053e666b7bd3dd70f417b4edd8fc0 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:43 +0200 Subject: selftests: mptcp: join: support RM_ADDR for used endpoints or not commit 425ba803124b90cb9124d99f13b372a89dc151d9 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. At some points, a new feature caused internal behaviour changes we are verifying in the selftests, see the Fixes tag below. It was not a UAPI change but because in these selftests, we check some internal behaviours, it is normal we have to adapt them from time to time after having added some features. It looks like there is no external sign we can use to predict the expected behaviour. Instead of accepting different behaviours and thus not really checking for the expected behaviour, we are looking here for a specific kernel version. That's not ideal but it looks better than removing the test because it cannot support older kernel versions. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 6fa0174a7c86 ("mptcp: more careful RM_ADDR generation") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 6fa3bf7f80a3..bbd713e675f7 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -2329,7 +2329,12 @@ remove_tests() pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow chk_join_nr 3 3 3 - chk_rm_nr 0 3 simult + + if mptcp_lib_kversion_ge 5.18; then + chk_rm_nr 0 3 simult + else + chk_rm_nr 3 3 + fi fi # addresses flush -- cgit 1.4.1 From efb4f6c2dd4cda5dc6b47148342fe685e88d674f Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:44 +0200 Subject: selftests: mptcp: join: skip implicit tests if not supported commit 36c4127ae8dd0ebac6d56d8a1b272dd483471c40 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the support of the implicit endpoints introduced by commit d045b9eb95a9 ("mptcp: introduce implicit endpoints"). It is possible to look for "mptcp_subflow_send_ack" in kallsyms because it was needed to introduce the mentioned feature. So we can know in advance if the feature is supported instead of trying and accepting any results. Note that here and in the following commits, we re-do the same check for each sub-test of the same function for a few reasons. The main one is not to break the ID assign to each test in order to be able to easily compare results between different kernel versions. Also, we can still run a specific test even if it is skipped. Another reason is that it makes it clear during the review that a specific subtest will be skipped or not under certain conditions. At the end, it looks OK to call the exact same helper multiple times: it is not a critical path and it is the same code that is executed, not really more cases to maintain. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 69c6ce7b6eca ("selftests: mptcp: add implicit endpoint test case") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index bbd713e675f7..d088e1c18f45 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -3108,8 +3108,10 @@ userspace_tests() endpoint_tests() { + # subflow_rebuild_header is needed to support the implicit flag # userspace pm type prevents add_addr - if reset "implicit EP"; then + if reset "implicit EP" && + mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 2 2 pm_nl_set_limits $ns2 2 2 pm_nl_add_endpoint $ns1 10.0.2.1 flags signal @@ -3129,7 +3131,8 @@ endpoint_tests() kill_tests_wait fi - if reset "delete and re-add"; then + if reset "delete and re-add" && + mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 pm_nl_add_endpoint $ns2 10.0.2.2 id 2 dev ns2eth2 flags subflow -- cgit 1.4.1 From 6313c493e3c9c69d7c1747345cf7c233beec6d4c Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:45 +0200 Subject: selftests: mptcp: join: skip backup if set flag on ID not supported commit 07216a3c5d926bf1b6b360a0073747228a1f9b7f upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. Commit bccefb762439 ("selftests: mptcp: simplify pm_nl_change_endpoint") has simplified the way the backup flag is set on an endpoint. Instead of doing: ./pm_nl_ctl set 10.0.2.1 flags backup Now we do: ./pm_nl_ctl set id 1 flags backup The new way is easier to maintain but it is also incompatible with older kernels not supporting the implicit endpoints putting in place the infrastructure to set flags per ID, hence the second Fixes tag. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: bccefb762439 ("selftests: mptcp: simplify pm_nl_change_endpoint") Cc: stable@vger.kernel.org Fixes: 4cf86ae84c71 ("mptcp: strict local address ID selection") Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index d088e1c18f45..fd5901bd4aa2 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -2573,7 +2573,8 @@ v4mapped_tests() backup_tests() { # single subflow, backup - if reset "single subflow, backup"; then + if reset "single subflow, backup" && + continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 0 1 pm_nl_set_limits $ns2 0 1 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow,backup @@ -2583,7 +2584,8 @@ backup_tests() fi # single address, backup - if reset "single address, backup"; then + if reset "single address, backup" && + continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 0 1 pm_nl_add_endpoint $ns1 10.0.2.1 flags signal pm_nl_set_limits $ns2 1 1 @@ -2594,7 +2596,8 @@ backup_tests() fi # single address with port, backup - if reset "single address with port, backup"; then + if reset "single address with port, backup" && + continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 0 1 pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 pm_nl_set_limits $ns2 1 1 -- cgit 1.4.1 From fe1f28db73f74305b55439a6e980380fe726cab2 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:46 +0200 Subject: selftests: mptcp: join: skip fullmesh flag tests if not supported commit 9db34c4294af9999edc773d96744e2d2d4eb5060 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the support of the fullmesh flag for the in-kernel PM introduced by commit 2843ff6f36db ("mptcp: remote addresses fullmesh") and commit 1a0d6136c5f0 ("mptcp: local addresses fullmesh"). It looks like there is no easy external sign we can use to predict the expected behaviour. We could add the flag and then check if it has been added but for that, and for each fullmesh test, we would need to setup a new environment, do the checks, clean it and then only start the test from yet another clean environment. To keep it simple and avoid introducing new issues, we look for a specific kernel version. That's not ideal but an acceptable solution for this case. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 6a0653b96f5d ("selftests: mptcp: add fullmesh setting tests") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index fd5901bd4aa2..e62b1c9ac262 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -2936,7 +2936,8 @@ fullmesh_tests() fi # set fullmesh flag - if reset "set fullmesh flag test"; then + if reset "set fullmesh flag test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow pm_nl_set_limits $ns2 4 4 @@ -2946,7 +2947,8 @@ fullmesh_tests() fi # set nofullmesh flag - if reset "set nofullmesh flag test"; then + if reset "set nofullmesh flag test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow,fullmesh pm_nl_set_limits $ns2 4 4 @@ -2956,7 +2958,8 @@ fullmesh_tests() fi # set backup,fullmesh flags - if reset "set backup,fullmesh flags test"; then + if reset "set backup,fullmesh flags test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow pm_nl_set_limits $ns2 4 4 @@ -2967,7 +2970,8 @@ fullmesh_tests() fi # set nobackup,nofullmesh flags - if reset "set nobackup,nofullmesh flags test"; then + if reset "set nobackup,nofullmesh flags test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_set_limits $ns2 4 4 pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow,backup,fullmesh -- cgit 1.4.1 From 94851666aff4ff0d5cb3dcc8f1fc4a9e085cb2ca Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Sat, 10 Jun 2023 18:11:49 +0200 Subject: selftests: mptcp: join: skip MPC backups tests if not supported commit 632978f0a961b4591a05ba9e39eab24541d83e84 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the support of sending an MP_PRIO signal for the initial subflow, introduced by commit c157bbe776b7 ("mptcp: allow the in kernel PM to set MPC subflow priority"). It is possible to look for "mptcp_subflow_send_ack" in kallsyms because it was needed to introduce the mentioned feature. So we can know in advance if the feature is supported instead of trying and accepting any results. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 914f6a59b10f ("selftests: mptcp: add MPC backup tests") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index e62b1c9ac262..b6f8eb4110a9 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -2607,14 +2607,16 @@ backup_tests() chk_prio_nr 1 1 fi - if reset "mpc backup"; then + if reset "mpc backup" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow chk_join_nr 0 0 0 chk_prio_nr 0 1 fi - if reset "mpc backup both sides"; then + if reset "mpc backup both sides" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow,backup pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow @@ -2622,14 +2624,16 @@ backup_tests() chk_prio_nr 1 1 fi - if reset "mpc switch to backup"; then + if reset "mpc switch to backup" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup chk_join_nr 0 0 0 chk_prio_nr 0 1 fi - if reset "mpc switch to backup both sides"; then + if reset "mpc switch to backup both sides" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup -- cgit 1.4.1 From 755c8857abde841be7bec551888b963f325651fd Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Mon, 13 Feb 2023 11:20:07 -0700 Subject: selftests/mount_setattr: fix redefine struct mount_attr build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d8e45bf1aed2e5fddd8985b5bb1aaf774a97aba8 upstream. Fix the following build error due to redefining struct mount_attr by removing duplicate define from mount_setattr_test.c gcc -g -isystem .../tools/testing/selftests/../../../usr/include -Wall -O2 -pthread mount_setattr_test.c -o .../tools/testing/selftests/mount_setattr/mount_setattr_test mount_setattr_test.c:107:8: error: redefinition of ‘struct mount_attr’ 107 | struct mount_attr { | ^~~~~~~~~~ In file included from /usr/include/x86_64-linux-gnu/sys/mount.h:32, from mount_setattr_test.c:10: .../usr/include/linux/mount.h:129:8: note: originally defined here 129 | struct mount_attr { | ^~~~~~~~~~ make: *** [../lib.mk:145: .../tools/testing/selftests/mount_setattr/mount_setattr_test] Error 1 Signed-off-by: Shuah Khan Cc: Hardik Garg Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/mount_setattr/mount_setattr_test.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/testing/selftests/mount_setattr/mount_setattr_test.c b/tools/testing/selftests/mount_setattr/mount_setattr_test.c index 969647228817..c6a8c732b802 100644 --- a/tools/testing/selftests/mount_setattr/mount_setattr_test.c +++ b/tools/testing/selftests/mount_setattr/mount_setattr_test.c @@ -104,13 +104,6 @@ #else #define __NR_mount_setattr 442 #endif - -struct mount_attr { - __u64 attr_set; - __u64 attr_clr; - __u64 propagation; - __u64 userns_fd; -}; #endif #ifndef __NR_open_tree -- cgit 1.4.1 From 157dcb20000bd8725f23b8031295d6ff2eb325ba Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 8 Jun 2023 18:38:47 +0200 Subject: selftests: mptcp: diag: skip listen tests if not supported commit dc97251bf0b70549c76ba261516c01b8096771c5 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the listen diag dump support introduced by commit 4fa39b701ce9 ("mptcp: listen diag dump support"). It looks like there is no good pre-check to do here, i.e. dedicated function available in kallsyms. Instead, we try to get info if nothing is returned, the test is marked as skipped. That's not ideal because something could be wrong with the feature and instead of reporting an error, the test could be marked as skipped. If we know in advanced that the feature is supposed to be supported, the tester can set SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var to 1: in this case the test will report an error instead of marking the test as skipped if nothing is returned. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: f2ae0fa68e28 ("selftests/mptcp: add diag listen tests") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/diag.sh | 47 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh index dd730a35bd12..400cf1ce96e3 100755 --- a/tools/testing/selftests/net/mptcp/diag.sh +++ b/tools/testing/selftests/net/mptcp/diag.sh @@ -42,27 +42,39 @@ fi __chk_nr() { - local condition="$1" + local command="$1" local expected=$2 - local msg nr + local msg="$3" + local skip="${4:-SKIP}" + local nr - shift 2 - msg=$* - nr=$(ss -inmHMN $ns | $condition) + nr=$(eval $command) printf "%-50s" "$msg" if [ $nr != $expected ]; then - echo "[ fail ] expected $expected found $nr" - ret=$test_cnt + if [ $nr = "$skip" ] && ! mptcp_lib_expect_all_features; then + echo "[ skip ] Feature probably not supported" + else + echo "[ fail ] expected $expected found $nr" + ret=$test_cnt + fi else echo "[ ok ]" fi test_cnt=$((test_cnt+1)) } +__chk_msk_nr() +{ + local condition=$1 + shift 1 + + __chk_nr "ss -inmHMN $ns | $condition" "$@" +} + chk_msk_nr() { - __chk_nr "grep -c token:" $* + __chk_msk_nr "grep -c token:" "$@" } wait_msk_nr() @@ -100,37 +112,26 @@ wait_msk_nr() chk_msk_fallback_nr() { - __chk_nr "grep -c fallback" $* + __chk_msk_nr "grep -c fallback" "$@" } chk_msk_remote_key_nr() { - __chk_nr "grep -c remote_key" $* + __chk_msk_nr "grep -c remote_key" "$@" } __chk_listen() { local filter="$1" local expected=$2 + local msg="$3" - shift 2 - msg=$* - - nr=$(ss -N $ns -Ml "$filter" | grep -c LISTEN) - printf "%-50s" "$msg" - - if [ $nr != $expected ]; then - echo "[ fail ] expected $expected found $nr" - ret=$test_cnt - else - echo "[ ok ]" - fi + __chk_nr "ss -N $ns -Ml '$filter' | grep -c LISTEN" "$expected" "$msg" 0 } chk_msk_listen() { lport=$1 - local msg="check for listen socket" # destination port search should always return empty list __chk_listen "dport $lport" 0 "listen match for dport $lport" -- cgit 1.4.1 From bce23d12543479712f6bbcd86e59b6221c2f31cc Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 22 Jun 2023 11:17:09 +0200 Subject: selftests: mptcp: sockopt: skip TCP_INQ checks if not supported commit b631e3a4e94c77c9007d60b577a069c203ce9594 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is TCP_INQ cmsg support introduced in commit 2c9e77659a0c ("mptcp: add TCP_INQ cmsg support"). It is possible to look for "mptcp_ioctl" in kallsyms because it was needed to introduce the mentioned feature. We can skip these tests and not set TCPINQ option if the feature is not supported. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 5cbd886ce2a9 ("selftests: mptcp: add TCP_INQ support") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Matthieu Baerts Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index d31be7229cdb..a493eaf8633f 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -182,9 +182,14 @@ do_transfer() local_addr="0.0.0.0" fi + cmsg="TIMESTAMPNS" + if mptcp_lib_kallsyms_has "mptcp_ioctl$"; then + cmsg+=",TCPINQ" + fi + timeout ${timeout_test} \ ip netns exec ${listener_ns} \ - $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS,TCPINQ \ + $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c "${cmsg}" \ ${local_addr} < "$sin" > "$sout" & spid=$! @@ -192,7 +197,7 @@ do_transfer() timeout ${timeout_test} \ ip netns exec ${connector_ns} \ - $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS,TCPINQ \ + $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c "${cmsg}" \ $connect_addr < "$cin" > "$cout" & cpid=$! @@ -311,6 +316,11 @@ do_tcpinq_tests() ip netns exec "$ns1" iptables -F ip netns exec "$ns1" ip6tables -F + if ! mptcp_lib_kallsyms_has "mptcp_ioctl$"; then + echo "INFO: TCP_INQ not supported: SKIP" + return + fi + for args in "-t tcp" "-r tcp"; do do_tcpinq_test $args lret=$? -- cgit 1.4.1 From f40a7ded34c929681e244c6c08d82263ec0deb53 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 22 Jun 2023 12:37:54 +0200 Subject: selftests: mptcp: join: skip test if iptables/tc cmds fail commit 4a0b866a3f7d3c22033f40e93e94befc6fe51bce upstream Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. Some tests are using IPTables and/or TC commands to force some behaviours. If one of these commands fails -- likely because some features are not available due to missing kernel config -- we should intercept the error and skip the tests requiring these features. Note that if we expect to have these features available and if SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var is set to 1, the tests will be marked as failed instead of skipped. This patch also replaces the 'exit 1' by 'return 1' not to stop the selftest in the middle without the conclusion if there is an issue with NF or TC. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 8d014eaa9254 ("selftests: mptcp: add ADD_ADDR timeout test case") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Matthieu Baerts Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 88 ++++++++++++++++--------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index b6f8eb4110a9..68bd7bd221f3 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -291,11 +291,15 @@ reset_with_add_addr_timeout() fi ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 - ip netns exec $ns2 $tables -A OUTPUT -p tcp \ - -m tcp --tcp-option 30 \ - -m bpf --bytecode \ - "$CBPF_MPTCP_SUBOPTION_ADD_ADDR" \ - -j DROP + + if ! ip netns exec $ns2 $tables -A OUTPUT -p tcp \ + -m tcp --tcp-option 30 \ + -m bpf --bytecode \ + "$CBPF_MPTCP_SUBOPTION_ADD_ADDR" \ + -j DROP; then + mark_as_skipped "unable to set the 'add addr' rule" + return 1 + fi } # $1: test name @@ -339,17 +343,12 @@ reset_with_allow_join_id0() # tc action pedit offset 162 out of bounds # # Netfilter is used to mark packets with enough data. -reset_with_fail() +setup_fail_rules() { - reset "${1}" || return 1 - - ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=1 - ip netns exec $ns2 sysctl -q net.mptcp.checksum_enabled=1 - check_invert=1 validate_checksum=1 - local i="$2" - local ip="${3:-4}" + local i="$1" + local ip="${2:-4}" local tables tables="${iptables}" @@ -364,15 +363,51 @@ reset_with_fail() -p tcp \ -m length --length 150:9999 \ -m statistic --mode nth --packet 1 --every 99999 \ - -j MARK --set-mark 42 || exit 1 + -j MARK --set-mark 42 || return ${ksft_skip} - tc -n $ns2 qdisc add dev ns2eth$i clsact || exit 1 + tc -n $ns2 qdisc add dev ns2eth$i clsact || return ${ksft_skip} tc -n $ns2 filter add dev ns2eth$i egress \ protocol ip prio 1000 \ handle 42 fw \ action pedit munge offset 148 u8 invert \ pipe csum tcp \ - index 100 || exit 1 + index 100 || return ${ksft_skip} +} + +reset_with_fail() +{ + reset "${1}" || return 1 + shift + + ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=1 + ip netns exec $ns2 sysctl -q net.mptcp.checksum_enabled=1 + + local rc=0 + setup_fail_rules "${@}" || rc=$? + + if [ ${rc} -eq ${ksft_skip} ]; then + mark_as_skipped "unable to set the 'fail' rules" + return 1 + fi +} + +reset_with_tcp_filter() +{ + reset "${1}" || return 1 + shift + + local ns="${!1}" + local src="${2}" + local target="${3}" + + if ! ip netns exec "${ns}" ${iptables} \ + -A INPUT \ + -s "${src}" \ + -p tcp \ + -j "${target}"; then + mark_as_skipped "unable to set the filter rules" + return 1 + fi } fail_test() @@ -732,15 +767,6 @@ pm_nl_check_endpoint() fi } -filter_tcp_from() -{ - local ns="${1}" - local src="${2}" - local target="${3}" - - ip netns exec "${ns}" ${iptables} -A INPUT -s "${src}" -p tcp -j "${target}" -} - do_transfer() { local listener_ns="$1" @@ -1949,23 +1975,23 @@ subflows_error_tests() fi # multiple subflows, with subflow creation error - if reset "multi subflows, with failing subflow"; then + if reset_with_tcp_filter "multi subflows, with failing subflow" ns1 10.0.3.2 REJECT && + continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow - filter_tcp_from $ns1 10.0.3.2 REJECT run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow chk_join_nr 1 1 1 fi # multiple subflows, with subflow timeout on MPJ - if reset "multi subflows, with subflow timeout"; then + if reset_with_tcp_filter "multi subflows, with subflow timeout" ns1 10.0.3.2 DROP && + continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow - filter_tcp_from $ns1 10.0.3.2 DROP run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow chk_join_nr 1 1 1 fi @@ -1973,11 +1999,11 @@ subflows_error_tests() # multiple subflows, check that the endpoint corresponding to # closed subflow (due to reset) is not reused if additional # subflows are added later - if reset "multi subflows, fair usage on close"; then + if reset_with_tcp_filter "multi subflows, fair usage on close" ns1 10.0.3.2 REJECT && + continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then pm_nl_set_limits $ns1 0 1 pm_nl_set_limits $ns2 0 1 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow - filter_tcp_from $ns1 10.0.3.2 REJECT run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow & # mpj subflow will be in TW after the reset -- cgit 1.4.1 From f17459121c37b899f60890e41b3643e1dfa50550 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 22 Jun 2023 12:39:14 +0200 Subject: selftests: mptcp: join: skip userspace PM tests if not supported commit f2b492b04a167261e1c38eb76f78fb4294473a49 upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the support of the userspace PM introduced by commit 4638de5aefe5 ("mptcp: handle local addrs announced by userspace PMs") and the following ones. It is possible to look for the MPTCP pm_type's sysctl knob to know in advance if the userspace PM is available. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: 5ac1d2d63451 ("selftests: mptcp: Add tests for userspace PM type") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Matthieu Baerts Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 26 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 68bd7bd221f3..1ad7de52e1a9 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -79,7 +79,7 @@ init_partial() ip netns add $netns || exit $ksft_skip ip -net $netns link set lo up ip netns exec $netns sysctl -q net.mptcp.enabled=1 - ip netns exec $netns sysctl -q net.mptcp.pm_type=0 + ip netns exec $netns sysctl -q net.mptcp.pm_type=0 2>/dev/null || true ip netns exec $netns sysctl -q net.ipv4.conf.all.rp_filter=0 ip netns exec $netns sysctl -q net.ipv4.conf.default.rp_filter=0 if [ $checksum -eq 1 ]; then @@ -3059,7 +3059,8 @@ fail_tests() userspace_tests() { # userspace pm type prevents add_addr - if reset "userspace pm type prevents add_addr"; then + if reset "userspace pm type prevents add_addr" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 @@ -3070,7 +3071,8 @@ userspace_tests() fi # userspace pm type does not echo add_addr without daemon - if reset "userspace pm no echo w/o daemon"; then + if reset "userspace pm no echo w/o daemon" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns2 pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 @@ -3081,7 +3083,8 @@ userspace_tests() fi # userspace pm type rejects join - if reset "userspace pm type rejects join"; then + if reset "userspace pm type rejects join" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 @@ -3091,7 +3094,8 @@ userspace_tests() fi # userspace pm type does not send join - if reset "userspace pm type does not send join"; then + if reset "userspace pm type does not send join" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns2 pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 @@ -3101,7 +3105,8 @@ userspace_tests() fi # userspace pm type prevents mp_prio - if reset "userspace pm type prevents mp_prio"; then + if reset "userspace pm type prevents mp_prio" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 @@ -3112,7 +3117,8 @@ userspace_tests() fi # userspace pm type prevents rm_addr - if reset "userspace pm type prevents rm_addr"; then + if reset "userspace pm type prevents rm_addr" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 set_userspace_pm $ns2 pm_nl_set_limits $ns1 0 1 @@ -3124,7 +3130,8 @@ userspace_tests() fi # userspace pm add & remove address - if reset "userspace pm add & remove address"; then + if reset "userspace pm add & remove address" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns2 1 1 run_tests $ns1 $ns2 10.0.1.1 0 userspace_1 0 slow @@ -3134,7 +3141,8 @@ userspace_tests() fi # userspace pm create destroy subflow - if reset "userspace pm create destroy subflow"; then + if reset "userspace pm create destroy subflow" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns2 pm_nl_set_limits $ns1 0 1 run_tests $ns1 $ns2 10.0.1.1 0 0 userspace_1 slow -- cgit 1.4.1 From 53e096bcaeac2425ba84593c4f20a9488f193b2e Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 22 Jun 2023 12:40:01 +0200 Subject: selftests: mptcp: join: skip fail tests if not supported commit ff8897b5189495b47895ca247b860a29dc04b36b upstream. Selftests are supposed to run on any kernels, including the old ones not supporting all MPTCP features. One of them is the support of the MP_FAIL / infinite mapping introduced by commit 1e39e5a32ad7 ("mptcp: infinite mapping sending") and the following ones. It is possible to look for one of the infinite mapping counters to know in advance if the this feature is available. Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368 Fixes: b6e074e171bc ("selftests: mptcp: add infinite map testcase") Cc: stable@vger.kernel.org Fixes: 2ba18161d407 ("selftests: mptcp: add MP_FAIL reset testcase") Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Matthieu Baerts Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 1ad7de52e1a9..2a238ae842d8 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -376,7 +376,7 @@ setup_fail_rules() reset_with_fail() { - reset "${1}" || return 1 + reset_check_counter "${1}" "MPTcpExtInfiniteMapTx" || return 1 shift ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=1 -- cgit 1.4.1 From 1fed1f8513469382a78bb8c019411d0ecf8f8185 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Thu, 22 Jun 2023 17:43:07 +0200 Subject: selftests: mptcp: join: fix "userspace pm add & remove address" It looks like this test was broken in v6.1 after the backport of commit 48d73f609dcc ("selftests: mptcp: update userspace pm addr tests"). It was not working because the commit ad3493746ebe ("selftests: mptcp: add test-cases for mixed v4/v6 subflows") is not in v6.1. This commit changes how the connections are being created in mptcp_join.sh selftest: with IPv6 support always enabled. But then in v6.1, the server still create IPv4 only connections, so without the v4-mapped-v6 format with the "::ffff:" prefix like we have in v6.3. The modification here adds a support for connections created in v4 as well so it fixes the issue in v6.1. This patch is not needed for the selftests in v6.3 because only IPv6 listening sockets are being created. Fixes: 8f0ba8ec18f5 ("selftests: mptcp: update userspace pm addr tests") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 2a238ae842d8..7b65003ee8cf 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -946,11 +946,12 @@ do_transfer() sp=$(grep "type:10" "$evts_ns1" | sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q') da=$(grep "type:10" "$evts_ns1" | - sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q') + sed -n 's/.*\(daddr[46]:\)\([0-9a-f:.]*\).*$/\2/p;q') + echo "$da" | grep -q ":" && addr="::ffff:$addr" dp=$(grep "type:10" "$evts_ns1" | sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q') ip netns exec ${listener_ns} ./pm_nl_ctl rem token $tk id $id - ip netns exec ${listener_ns} ./pm_nl_ctl dsf lip "::ffff:$addr" \ + ip netns exec ${listener_ns} ./pm_nl_ctl dsf lip "$addr" \ lport $sp rip $da rport $dp token $tk fi -- cgit 1.4.1 From 4a89bfb1a142733fb18ed7ccf63952f2e97ea654 Mon Sep 17 00:00:00 2001 From: Rafael Aquini Date: Tue, 6 Jun 2023 19:36:13 -0400 Subject: writeback: fix dereferencing NULL mapping->host on writeback_page_template commit 54abe19e00cfcc5a72773d15cd00ed19ab763439 upstream. When commit 19343b5bdd16 ("mm/page-writeback: introduce tracepoint for wait_on_page_writeback()") repurposed the writeback_dirty_page trace event as a template to create its new wait_on_page_writeback trace event, it ended up opening a window to NULL pointer dereference crashes due to the (infrequent) occurrence of a race where an access to a page in the swap-cache happens concurrently with the moment this page is being written to disk and the tracepoint is enabled: BUG: kernel NULL pointer dereference, address: 0000000000000040 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 800000010ec0a067 P4D 800000010ec0a067 PUD 102353067 PMD 0 Oops: 0000 [#1] PREEMPT SMP PTI CPU: 1 PID: 1320 Comm: shmem-worker Kdump: loaded Not tainted 6.4.0-rc5+ #13 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS edk2-20230301gitf80f052277c8-1.fc37 03/01/2023 RIP: 0010:trace_event_raw_event_writeback_folio_template+0x76/0xf0 Code: 4d 85 e4 74 5c 49 8b 3c 24 e8 06 98 ee ff 48 89 c7 e8 9e 8b ee ff ba 20 00 00 00 48 89 ef 48 89 c6 e8 fe d4 1a 00 49 8b 04 24 <48> 8b 40 40 48 89 43 28 49 8b 45 20 48 89 e7 48 89 43 30 e8 a2 4d RSP: 0000:ffffaad580b6fb60 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff90e38035c01c RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff90e38035c044 RBP: ffff90e38035c024 R08: 0000000000000002 R09: 0000000000000006 R10: ffff90e38035c02e R11: 0000000000000020 R12: ffff90e380bac000 R13: ffffe3a7456d9200 R14: 0000000000001b81 R15: ffffe3a7456d9200 FS: 00007f2e4e8a15c0(0000) GS:ffff90e3fbc80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000040 CR3: 00000001150c6003 CR4: 0000000000170ee0 Call Trace: ? __die+0x20/0x70 ? page_fault_oops+0x76/0x170 ? kernelmode_fixup_or_oops+0x84/0x110 ? exc_page_fault+0x65/0x150 ? asm_exc_page_fault+0x22/0x30 ? trace_event_raw_event_writeback_folio_template+0x76/0xf0 folio_wait_writeback+0x6b/0x80 shmem_swapin_folio+0x24a/0x500 ? filemap_get_entry+0xe3/0x140 shmem_get_folio_gfp+0x36e/0x7c0 ? find_busiest_group+0x43/0x1a0 shmem_fault+0x76/0x2a0 ? __update_load_avg_cfs_rq+0x281/0x2f0 __do_fault+0x33/0x130 do_read_fault+0x118/0x160 do_pte_missing+0x1ed/0x2a0 __handle_mm_fault+0x566/0x630 handle_mm_fault+0x91/0x210 do_user_addr_fault+0x22c/0x740 exc_page_fault+0x65/0x150 asm_exc_page_fault+0x22/0x30 This problem arises from the fact that the repurposed writeback_dirty_page trace event code was written assuming that every pointer to mapping (struct address_space) would come from a file-mapped page-cache object, thus mapping->host would always be populated, and that was a valid case before commit 19343b5bdd16. The swap-cache address space (swapper_spaces), however, doesn't populate its ->host (struct inode) pointer, thus leading to the crashes in the corner-case aforementioned. commit 19343b5bdd16 ended up breaking the assignment of __entry->name and __entry->ino for the wait_on_page_writeback tracepoint -- both dependent on mapping->host carrying a pointer to a valid inode. The assignment of __entry->name was fixed by commit 68f23b89067f ("memcg: fix a crash in wb_workfn when a device disappears"), and this commit fixes the remaining case, for __entry->ino. Link: https://lkml.kernel.org/r/20230606233613.1290819-1-aquini@redhat.com Fixes: 19343b5bdd16 ("mm/page-writeback: introduce tracepoint for wait_on_page_writeback()") Signed-off-by: Rafael Aquini Reviewed-by: Yafang Shao Cc: Aristeu Rozanski Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/trace/events/writeback.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 86b2a82da546..54e353c9f919 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -68,7 +68,7 @@ DECLARE_EVENT_CLASS(writeback_folio_template, strscpy_pad(__entry->name, bdi_dev_name(mapping ? inode_to_bdi(mapping->host) : NULL), 32); - __entry->ino = mapping ? mapping->host->i_ino : 0; + __entry->ino = (mapping && mapping->host) ? mapping->host->i_ino : 0; __entry->index = folio->index; ), -- cgit 1.4.1 From d5d7cde2ad19471d0e88e6322bbafc8b6663ddc5 Mon Sep 17 00:00:00 2001 From: Prathu Baronia Date: Thu, 8 Jun 2023 21:14:49 +0530 Subject: scripts: fix the gfp flags header path in gfp-translate commit 2049a7d0cbc6ac8e370e836ed68597be04a7dc49 upstream. Since gfp flags have been shifted to gfp_types.h so update the path in the gfp-translate script. Link: https://lkml.kernel.org/r/20230608154450.21758-1-prathubaronia2011@gmail.com Fixes: cb5a065b4ea9c ("headers/deps: mm: Split out of ") Signed-off-by: Prathu Baronia Reviewed-by: David Hildenbrand Cc: Masahiro Yamada Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Nicolas Schier Cc: Ingo Molnar Cc: Yury Norov Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- scripts/gfp-translate | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/gfp-translate b/scripts/gfp-translate index b2ce416d944b..6c9aed17cf56 100755 --- a/scripts/gfp-translate +++ b/scripts/gfp-translate @@ -63,11 +63,11 @@ fi # Extract GFP flags from the kernel source TMPFILE=`mktemp -t gfptranslate-XXXXXX` || exit 1 -grep -q ___GFP $SOURCE/include/linux/gfp.h +grep -q ___GFP $SOURCE/include/linux/gfp_types.h if [ $? -eq 0 ]; then - grep "^#define ___GFP" $SOURCE/include/linux/gfp.h | sed -e 's/u$//' | grep -v GFP_BITS > $TMPFILE + grep "^#define ___GFP" $SOURCE/include/linux/gfp_types.h | sed -e 's/u$//' | grep -v GFP_BITS > $TMPFILE else - grep "^#define __GFP" $SOURCE/include/linux/gfp.h | sed -e 's/(__force gfp_t)//' | sed -e 's/u)/)/' | grep -v GFP_BITS | sed -e 's/)\//) \//' > $TMPFILE + grep "^#define __GFP" $SOURCE/include/linux/gfp_types.h | sed -e 's/(__force gfp_t)//' | sed -e 's/u)/)/' | grep -v GFP_BITS | sed -e 's/)\//) \//' > $TMPFILE fi # Parse the flags -- cgit 1.4.1 From 8e63b1fd24a874018495ee52de6a9498cb48c02f Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 9 Jun 2023 12:57:32 +0900 Subject: nilfs2: fix buffer corruption due to concurrent device reads commit 679bd7ebdd315bf457a4740b306ae99f1d0a403d upstream. As a result of analysis of a syzbot report, it turned out that in three cases where nilfs2 allocates block device buffers directly via sb_getblk, concurrent reads to the device can corrupt the allocated buffers. Nilfs2 uses sb_getblk for segment summary blocks, that make up a log header, and the super root block, that is the trailer, and when moving and writing the second super block after fs resize. In any of these, since the uptodate flag is not set when storing metadata to be written in the allocated buffers, the stored metadata will be overwritten if a device read of the same block occurs concurrently before the write. This causes metadata corruption and misbehavior in the log write itself, causing warnings in nilfs_btree_assign() as reported. Fix these issues by setting an uptodate flag on the buffer head on the first or before modifying each buffer obtained with sb_getblk, and clearing the flag on failure. When setting the uptodate flag, the lock_buffer/unlock_buffer pair is used to perform necessary exclusive control, and the buffer is filled to ensure that uninitialized bytes are not mixed into the data read from others. As for buffers for segment summary blocks, they are filled incrementally, so if the uptodate flag was unset on their allocation, set the flag and zero fill the buffer once at that point. Also, regarding the superblock move routine, the starting point of the memset call to zerofill the block is incorrectly specified, which can cause a buffer overflow on file systems with block sizes greater than 4KiB. In addition, if the superblock is moved within a large block, it is necessary to assume the possibility that the data in the superblock will be destroyed by zero-filling before copying. So fix these potential issues as well. Link: https://lkml.kernel.org/r/20230609035732.20426-1-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi Reported-by: syzbot+31837fe952932efc8fb9@syzkaller.appspotmail.com Closes: https://lkml.kernel.org/r/00000000000030000a05e981f475@google.com Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/segbuf.c | 6 ++++++ fs/nilfs2/segment.c | 7 +++++++ fs/nilfs2/super.c | 23 ++++++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 1362ccb64ec7..6e59dc19a732 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -101,6 +101,12 @@ int nilfs_segbuf_extend_segsum(struct nilfs_segment_buffer *segbuf) if (unlikely(!bh)) return -ENOMEM; + lock_buffer(bh); + if (!buffer_uptodate(bh)) { + memset(bh->b_data, 0, bh->b_size); + set_buffer_uptodate(bh); + } + unlock_buffer(bh); nilfs_segbuf_add_segsum_buffer(segbuf, bh); return 0; } diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 209e46431a5e..6cf64023be31 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -979,10 +979,13 @@ static void nilfs_segctor_fill_in_super_root(struct nilfs_sc_info *sci, unsigned int isz, srsz; bh_sr = NILFS_LAST_SEGBUF(&sci->sc_segbufs)->sb_super_root; + + lock_buffer(bh_sr); raw_sr = (struct nilfs_super_root *)bh_sr->b_data; isz = nilfs->ns_inode_size; srsz = NILFS_SR_BYTES(isz); + raw_sr->sr_sum = 0; /* Ensure initialization within this update */ raw_sr->sr_bytes = cpu_to_le16(srsz); raw_sr->sr_nongc_ctime = cpu_to_le64(nilfs_doing_gc() ? @@ -996,6 +999,8 @@ static void nilfs_segctor_fill_in_super_root(struct nilfs_sc_info *sci, nilfs_write_inode_common(nilfs->ns_sufile, (void *)raw_sr + NILFS_SR_SUFILE_OFFSET(isz), 1); memset((void *)raw_sr + srsz, 0, nilfs->ns_blocksize - srsz); + set_buffer_uptodate(bh_sr); + unlock_buffer(bh_sr); } static void nilfs_redirty_inodes(struct list_head *head) @@ -1778,6 +1783,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err) list_for_each_entry(segbuf, logs, sb_list) { list_for_each_entry(bh, &segbuf->sb_segsum_buffers, b_assoc_buffers) { + clear_buffer_uptodate(bh); if (bh->b_page != bd_page) { if (bd_page) end_page_writeback(bd_page); @@ -1789,6 +1795,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err) b_assoc_buffers) { clear_buffer_async_write(bh); if (bh == segbuf->sb_super_root) { + clear_buffer_uptodate(bh); if (bh->b_page != bd_page) { end_page_writeback(bd_page); bd_page = bh->b_page; diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 77f1e5778d1c..9ba4933087af 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -372,10 +372,31 @@ static int nilfs_move_2nd_super(struct super_block *sb, loff_t sb2off) goto out; } nsbp = (void *)nsbh->b_data + offset; - memset(nsbp, 0, nilfs->ns_blocksize); + lock_buffer(nsbh); if (sb2i >= 0) { + /* + * The position of the second superblock only changes by 4KiB, + * which is larger than the maximum superblock data size + * (= 1KiB), so there is no need to use memmove() to allow + * overlap between source and destination. + */ memcpy(nsbp, nilfs->ns_sbp[sb2i], nilfs->ns_sbsize); + + /* + * Zero fill after copy to avoid overwriting in case of move + * within the same block. + */ + memset(nsbh->b_data, 0, offset); + memset((void *)nsbp + nilfs->ns_sbsize, 0, + nsbh->b_size - offset - nilfs->ns_sbsize); + } else { + memset(nsbh->b_data, 0, nsbh->b_size); + } + set_buffer_uptodate(nsbh); + unlock_buffer(nsbh); + + if (sb2i >= 0) { brelse(nilfs->ns_sbh[sb2i]); nilfs->ns_sbh[sb2i] = nsbh; nilfs->ns_sbp[sb2i] = nsbp; -- cgit 1.4.1 From 390aeb5ae7c0479447c12aef6bcfc0f16f2c726d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 14 Jun 2023 17:29:21 +0200 Subject: ACPI: sleep: Avoid breaking S3 wakeup due to might_sleep() commit 22db06337f590d01d79f60f181d8dfe5a9ef9085 upstream. The addition of might_sleep() to down_timeout() caused the latter to enable interrupts unconditionally in some cases, which in turn broke the ACPI S3 wakeup path in acpi_suspend_enter(), where down_timeout() is called by acpi_disable_all_gpes() via acpi_ut_acquire_mutex(). Namely, if CONFIG_DEBUG_ATOMIC_SLEEP is set, might_sleep() causes might_resched() to be used and if CONFIG_PREEMPT_VOLUNTARY is set, this triggers __cond_resched() which may call preempt_schedule_common(), so __schedule() gets invoked and it ends up with enabled interrupts (in the prev == next case). Now, enabling interrupts early in the S3 wakeup path causes the kernel to crash. Address this by modifying acpi_suspend_enter() to disable GPEs without attempting to acquire the sleeping lock which is not needed in that code path anyway. Fixes: 99409b935c9a ("locking/semaphore: Add might_sleep() to down_*() family") Reported-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki Acked-by: Peter Zijlstra (Intel) Cc: 5.15+ # 5.15+ Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/acpica/achware.h | 2 -- drivers/acpi/sleep.c | 16 ++++++++++++---- include/acpi/acpixf.h | 1 + 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 6f2787506b50..c1b6e7bf4fcb 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -101,8 +101,6 @@ acpi_status acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, acpi_event_status *event_status); -acpi_status acpi_hw_disable_all_gpes(void); - acpi_status acpi_hw_enable_all_runtime_gpes(void); acpi_status acpi_hw_enable_all_wakeup_gpes(void); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 4ca667251272..539c12fbd2f1 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -636,11 +636,19 @@ static int acpi_suspend_enter(suspend_state_t pm_state) } /* - * Disable and clear GPE status before interrupt is enabled. Some GPEs - * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. - * acpi_leave_sleep_state will reenable specific GPEs later + * Disable all GPE and clear their status bits before interrupts are + * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can + * prevent them from producing spurious interrups. + * + * acpi_leave_sleep_state() will reenable specific GPEs later. + * + * Because this code runs on one CPU with disabled interrupts (all of + * the other CPUs are offline at this time), it need not acquire any + * sleeping locks which may trigger an implicit preemption point even + * if there is no contention, so avoid doing that by using a low-level + * library routine here. */ - acpi_disable_all_gpes(); + acpi_hw_disable_all_gpes(); /* Allow EC transactions to happen. */ acpi_ec_unblock_transactions(); diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 67c0b9e734b6..911bab2998e2 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -751,6 +751,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_event_status *event_status)) ACPI_HW_DEPENDENT_RETURN_UINT32(u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)) +ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_hw_disable_all_gpes(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void)) -- cgit 1.4.1 From 4f7e702b74f7a44fe6492c8dac78c668297942fe Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 15 Jun 2023 15:42:59 +1000 Subject: KVM: Avoid illegal stage2 mapping on invalid memory slot commit 2230f9e1171a2e9731422a14d1bbc313c0b719d1 upstream. We run into guest hang in edk2 firmware when KSM is kept as running on the host. The edk2 firmware is waiting for status 0x80 from QEMU's pflash device (TYPE_PFLASH_CFI01) during the operation of sector erasing or buffered write. The status is returned by reading the memory region of the pflash device and the read request should have been forwarded to QEMU and emulated by it. Unfortunately, the read request is covered by an illegal stage2 mapping when the guest hang issue occurs. The read request is completed with QEMU bypassed and wrong status is fetched. The edk2 firmware runs into an infinite loop with the wrong status. The illegal stage2 mapping is populated due to same page sharing by KSM at (C) even the associated memory slot has been marked as invalid at (B) when the memory slot is requested to be deleted. It's notable that the active and inactive memory slots can't be swapped when we're in the middle of kvm_mmu_notifier_change_pte() because kvm->mn_active_invalidate_count is elevated, and kvm_swap_active_memslots() will busy loop until it reaches to zero again. Besides, the swapping from the active to the inactive memory slots is also avoided by holding &kvm->srcu in __kvm_handle_hva_range(), corresponding to synchronize_srcu_expedited() in kvm_swap_active_memslots(). CPU-A CPU-B ----- ----- ioctl(kvm_fd, KVM_SET_USER_MEMORY_REGION) kvm_vm_ioctl_set_memory_region kvm_set_memory_region __kvm_set_memory_region kvm_set_memslot(kvm, old, NULL, KVM_MR_DELETE) kvm_invalidate_memslot kvm_copy_memslot kvm_replace_memslot kvm_swap_active_memslots (A) kvm_arch_flush_shadow_memslot (B) same page sharing by KSM kvm_mmu_notifier_invalidate_range_start : kvm_mmu_notifier_change_pte kvm_handle_hva_range __kvm_handle_hva_range kvm_set_spte_gfn (C) : kvm_mmu_notifier_invalidate_range_end Fix the issue by skipping the invalid memory slot at (C) to avoid the illegal stage2 mapping so that the read request for the pflash's status is forwarded to QEMU and emulated by it. In this way, the correct pflash's status can be returned from QEMU to break the infinite loop in the edk2 firmware. We tried a git-bisect and the first problematic commit is cd4c71835228 (" KVM: arm64: Convert to the gfn-based MMU notifier callbacks"). With this, clean_dcache_guest_page() is called after the memory slots are iterated in kvm_mmu_notifier_change_pte(). clean_dcache_guest_page() is called before the iteration on the memory slots before this commit. This change literally enlarges the racy window between kvm_mmu_notifier_change_pte() and memory slot removal so that we're able to reproduce the issue in a practical test case. However, the issue exists since commit d5d8184d35c9 ("KVM: ARM: Memory virtualization setup"). Cc: stable@vger.kernel.org # v3.9+ Fixes: d5d8184d35c9 ("KVM: ARM: Memory virtualization setup") Reported-by: Shuai Hu Reported-by: Zhenyu Zhang Signed-off-by: Gavin Shan Reviewed-by: David Hildenbrand Reviewed-by: Oliver Upton Reviewed-by: Peter Xu Reviewed-by: Sean Christopherson Reviewed-by: Shaoqin Huang Message-Id: <20230615054259.14911-1-gshan@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- virt/kvm/kvm_main.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index de8e5eb6af10..640113f047ef 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -691,6 +691,24 @@ static __always_inline int kvm_handle_hva_range_no_flush(struct mmu_notifier *mn return __kvm_handle_hva_range(kvm, &range); } + +static bool kvm_change_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) +{ + /* + * Skipping invalid memslots is correct if and only change_pte() is + * surrounded by invalidate_range_{start,end}(), which is currently + * guaranteed by the primary MMU. If that ever changes, KVM needs to + * unmap the memslot instead of skipping the memslot to ensure that KVM + * doesn't hold references to the old PFN. + */ + WARN_ON_ONCE(!READ_ONCE(kvm->mn_active_invalidate_count)); + + if (range->slot->flags & KVM_MEMSLOT_INVALID) + return false; + + return kvm_set_spte_gfn(kvm, range); +} + static void kvm_mmu_notifier_change_pte(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long address, @@ -712,7 +730,7 @@ static void kvm_mmu_notifier_change_pte(struct mmu_notifier *mn, if (!READ_ONCE(kvm->mmu_invalidate_in_progress)) return; - kvm_handle_hva_range(mn, address, address + 1, pte, kvm_set_spte_gfn); + kvm_handle_hva_range(mn, address, address + 1, pte, kvm_change_spte_gfn); } void kvm_mmu_invalidate_begin(struct kvm *kvm, unsigned long start, -- cgit 1.4.1 From 191cb913293a6ceb65433c2c6748f5800ebb120d Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Thu, 4 May 2023 15:41:55 -0700 Subject: Drivers: hv: vmbus: Call hv_synic_free() if hv_synic_alloc() fails commit ec97e112985c2581ee61854a4b74f080f6cdfc2c upstream. Commit 572086325ce9 ("Drivers: hv: vmbus: Cleanup synic memory free path") says "Any memory allocations that succeeded will be freed when the caller cleans up by calling hv_synic_free()", but if the get_zeroed_page() in hv_synic_alloc() fails, currently hv_synic_free() is not really called in vmbus_bus_init(), consequently there will be a memory leak, e.g. hv_context.hv_numa_map is not freed in the error path. Fix this by updating the goto labels. Cc: stable@kernel.org Signed-off-by: Dexuan Cui Fixes: 4df4cb9e99f8 ("x86/hyperv: Initialize clockevents earlier in CPU onlining") Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20230504224155.10484-1-decui@microsoft.com Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e592c481f7ae..b03cb7ae7fd3 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1504,7 +1504,7 @@ static int vmbus_bus_init(void) ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vmbus:online", hv_synic_init, hv_synic_cleanup); if (ret < 0) - goto err_cpuhp; + goto err_alloc; hyperv_cpuhp_online = ret; ret = vmbus_connect(); @@ -1555,9 +1555,8 @@ static int vmbus_bus_init(void) err_connect: cpuhp_remove_state(hyperv_cpuhp_online); -err_cpuhp: - hv_synic_free(); err_alloc: + hv_synic_free(); if (vmbus_irq == -1) { hv_remove_vmbus_handler(); } else { -- cgit 1.4.1 From ba803d7ac18aed90ff61026a35bdde598b95a327 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Thu, 18 May 2023 08:13:52 -0700 Subject: Drivers: hv: vmbus: Fix vmbus_wait_for_unload() to scan present CPUs commit 320805ab61e5f1e2a5729ae266e16bec2904050c upstream. vmbus_wait_for_unload() may be called in the panic path after other CPUs are stopped. vmbus_wait_for_unload() currently loops through online CPUs looking for the UNLOAD response message. But the values of CONFIG_KEXEC_CORE and crash_kexec_post_notifiers affect the path used to stop the other CPUs, and in one of the paths the stopped CPUs are removed from cpu_online_mask. This removal happens in both x86/x64 and arm64 architectures. In such a case, vmbus_wait_for_unload() only checks the panic'ing CPU, and misses the UNLOAD response message except when the panic'ing CPU is CPU 0. vmbus_wait_for_unload() eventually times out, but only after waiting 100 seconds. Fix this by looping through *present* CPUs in vmbus_wait_for_unload(). The cpu_present_mask is not modified by stopping the other CPUs in the panic path, nor should it be. Also, in a CoCo VM the synic_message_page is not allocated in hv_synic_alloc(), but is set and cleared in hv_synic_enable_regs() and hv_synic_disable_regs() such that it is set only when the CPU is online. If not all present CPUs are online when vmbus_wait_for_unload() is called, the synic_message_page might be NULL. Add a check for this. Fixes: cd95aad55793 ("Drivers: hv: vmbus: handle various crash scenarios") Cc: stable@vger.kernel.org Reported-by: John Starks Signed-off-by: Michael Kelley Reviewed-by: Vitaly Kuznetsov Link: https://lore.kernel.org/r/1684422832-38476-1-git-send-email-mikelley@microsoft.com Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index cc23b90cae02..d95e567a190d 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -829,11 +829,22 @@ static void vmbus_wait_for_unload(void) if (completion_done(&vmbus_connection.unload_event)) goto completed; - for_each_online_cpu(cpu) { + for_each_present_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); + /* + * In a CoCo VM the synic_message_page is not allocated + * in hv_synic_alloc(). Instead it is set/cleared in + * hv_synic_enable_regs() and hv_synic_disable_regs() + * such that it is set only when the CPU is online. If + * not all present CPUs are online, the message page + * might be NULL, so skip such CPUs. + */ page_addr = hv_cpu->synic_message_page; + if (!page_addr) + continue; + msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; @@ -867,11 +878,14 @@ completed: * maybe-pending messages on all CPUs to be able to receive new * messages after we reconnect. */ - for_each_online_cpu(cpu) { + for_each_present_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); page_addr = hv_cpu->synic_message_page; + if (!page_addr) + continue; + msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; msg->header.message_type = HVMSG_NONE; } -- cgit 1.4.1 From a74a9d9b756a117e861d4cd46d2a81612b97d46d Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 14 Jun 2023 21:44:47 -0700 Subject: PCI: hv: Fix a race condition bug in hv_pci_query_relations() commit 440b5e3663271b0ffbd4908115044a6a51fb938b upstream. Since day 1 of the driver, there has been a race between hv_pci_query_relations() and survey_child_resources(): during fast device hotplug, hv_pci_query_relations() may error out due to device-remove and the stack variable 'comp' is no longer valid; however, pci_devices_present_work() -> survey_child_resources() -> complete() may be running on another CPU and accessing the no-longer-valid 'comp'. Fix the race by flushing the workqueue before we exit from hv_pci_query_relations(). Fixes: 4daace0d8ce8 ("PCI: hv: Add paravirtual PCI front-end for Microsoft Hyper-V VMs") Signed-off-by: Dexuan Cui Reviewed-by: Michael Kelley Acked-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615044451.5580-2-decui@microsoft.com Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-hyperv.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index f1ec8931dfbc..115910e84556 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -3321,6 +3321,24 @@ static int hv_pci_query_relations(struct hv_device *hdev) if (!ret) ret = wait_for_response(hdev, &comp); + /* + * In the case of fast device addition/removal, it's possible that + * vmbus_sendpacket() or wait_for_response() returns -ENODEV but we + * already got a PCI_BUS_RELATIONS* message from the host and the + * channel callback already scheduled a work to hbus->wq, which can be + * running pci_devices_present_work() -> survey_child_resources() -> + * complete(&hbus->survey_event), even after hv_pci_query_relations() + * exits and the stack variable 'comp' is no longer valid; as a result, + * a hang or a page fault may happen when the complete() calls + * raw_spin_lock_irqsave(). Flush hbus->wq before we exit from + * hv_pci_query_relations() to avoid the issues. Note: if 'ret' is + * -ENODEV, there can't be any more work item scheduled to hbus->wq + * after the flush_workqueue(): see vmbus_onoffer_rescind() -> + * vmbus_reset_channel_cb(), vmbus_rescind_cleanup() -> + * channel->rescind = true. + */ + flush_workqueue(hbus->wq); + return ret; } -- cgit 1.4.1 From da2fff20d92db19023b1f700b7f1a6f5c7532c04 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 14 Jun 2023 21:44:50 -0700 Subject: Revert "PCI: hv: Fix a timing issue which causes kdump to fail occasionally" commit a847234e24d03d01a9566d1d9dcce018cc018d67 upstream. This reverts commit d6af2ed29c7c1c311b96dac989dcb991e90ee195. The statement "the hv_pci_bus_exit() call releases structures of all its child devices" in commit d6af2ed29c7c is not true: in the path hv_pci_probe() -> hv_pci_enter_d0() -> hv_pci_bus_exit(hdev, true): the parameter "keep_devs" is true, so hv_pci_bus_exit() does *not* release the child "struct hv_pci_dev *hpdev" that is created earlier in pci_devices_present_work() -> new_pcichild_device(). The commit d6af2ed29c7c was originally made in July 2020 for RHEL 7.7, where the old version of hv_pci_bus_exit() was used; when the commit was rebased and merged into the upstream, people didn't notice that it's not really necessary. The commit itself doesn't cause any issue, but it makes hv_pci_probe() more complicated. Revert it to facilitate some upcoming changes to hv_pci_probe(). Signed-off-by: Dexuan Cui Reviewed-by: Michael Kelley Acked-by: Wei Hu Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615044451.5580-5-decui@microsoft.com Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-hyperv.c | 71 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 115910e84556..3c3c325e165f 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -3251,8 +3251,10 @@ static int hv_pci_enter_d0(struct hv_device *hdev) struct pci_bus_d0_entry *d0_entry; struct hv_pci_compl comp_pkt; struct pci_packet *pkt; + bool retry = true; int ret; +enter_d0_retry: /* * Tell the host that the bus is ready to use, and moved into the * powered-on state. This includes telling the host which region @@ -3279,6 +3281,38 @@ static int hv_pci_enter_d0(struct hv_device *hdev) if (ret) goto exit; + /* + * In certain case (Kdump) the pci device of interest was + * not cleanly shut down and resource is still held on host + * side, the host could return invalid device status. + * We need to explicitly request host to release the resource + * and try to enter D0 again. + */ + if (comp_pkt.completion_status < 0 && retry) { + retry = false; + + dev_err(&hdev->device, "Retrying D0 Entry\n"); + + /* + * Hv_pci_bus_exit() calls hv_send_resource_released() + * to free up resources of its child devices. + * In the kdump kernel we need to set the + * wslot_res_allocated to 255 so it scans all child + * devices to release resources allocated in the + * normal kernel before panic happened. + */ + hbus->wslot_res_allocated = 255; + + ret = hv_pci_bus_exit(hdev, true); + + if (ret == 0) { + kfree(pkt); + goto enter_d0_retry; + } + dev_err(&hdev->device, + "Retrying D0 failed with ret %d\n", ret); + } + if (comp_pkt.completion_status < 0) { dev_err(&hdev->device, "PCI Pass-through VSP failed D0 Entry with status %x\n", @@ -3524,7 +3558,6 @@ static int hv_pci_probe(struct hv_device *hdev, struct hv_pcibus_device *hbus; u16 dom_req, dom; char *name; - bool enter_d0_retry = true; int ret; /* @@ -3664,47 +3697,11 @@ static int hv_pci_probe(struct hv_device *hdev, if (ret) goto free_fwnode; -retry: ret = hv_pci_query_relations(hdev); if (ret) goto free_irq_domain; ret = hv_pci_enter_d0(hdev); - /* - * In certain case (Kdump) the pci device of interest was - * not cleanly shut down and resource is still held on host - * side, the host could return invalid device status. - * We need to explicitly request host to release the resource - * and try to enter D0 again. - * Since the hv_pci_bus_exit() call releases structures - * of all its child devices, we need to start the retry from - * hv_pci_query_relations() call, requesting host to send - * the synchronous child device relations message before this - * information is needed in hv_send_resources_allocated() - * call later. - */ - if (ret == -EPROTO && enter_d0_retry) { - enter_d0_retry = false; - - dev_err(&hdev->device, "Retrying D0 Entry\n"); - - /* - * Hv_pci_bus_exit() calls hv_send_resources_released() - * to free up resources of its child devices. - * In the kdump kernel we need to set the - * wslot_res_allocated to 255 so it scans all child - * devices to release resources allocated in the - * normal kernel before panic happened. - */ - hbus->wslot_res_allocated = 255; - ret = hv_pci_bus_exit(hdev, true); - - if (ret == 0) - goto retry; - - dev_err(&hdev->device, - "Retrying D0 failed with ret %d\n", ret); - } if (ret) goto free_irq_domain; -- cgit 1.4.1 From 5c09925b187931f4106edb7a70d6c23ab0b802c5 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 14 Jun 2023 21:44:49 -0700 Subject: PCI: hv: Remove the useless hv_pcichild_state from struct hv_pci_dev commit add9195e69c94b32e96f78c2f9cea68f0e850b3f upstream. The hpdev->state is never really useful. The only use in hv_pci_eject_device() and hv_eject_device_work() is not really necessary. Signed-off-by: Dexuan Cui Reviewed-by: Michael Kelley Acked-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615044451.5580-4-decui@microsoft.com Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-hyperv.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 3c3c325e165f..ec24a2ddbe57 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -553,19 +553,10 @@ struct hv_dr_state { struct hv_pcidev_description func[]; }; -enum hv_pcichild_state { - hv_pcichild_init = 0, - hv_pcichild_requirements, - hv_pcichild_resourced, - hv_pcichild_ejecting, - hv_pcichild_maximum -}; - struct hv_pci_dev { /* List protected by pci_rescan_remove_lock */ struct list_head list_entry; refcount_t refs; - enum hv_pcichild_state state; struct pci_slot *pci_slot; struct hv_pcidev_description desc; bool reported_missing; @@ -2764,8 +2755,6 @@ static void hv_eject_device_work(struct work_struct *work) hpdev = container_of(work, struct hv_pci_dev, wrk); hbus = hpdev->hbus; - WARN_ON(hpdev->state != hv_pcichild_ejecting); - /* * Ejection can come before or after the PCI bus has been set up, so * attempt to find it and tear down the bus state, if it exists. This @@ -2822,7 +2811,6 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev) return; } - hpdev->state = hv_pcichild_ejecting; get_pcichild(hpdev); INIT_WORK(&hpdev->wrk, hv_eject_device_work); queue_work(hbus->wq, &hpdev->wrk); -- cgit 1.4.1 From 091d03d198597fd0a5e1e5880a85b5d5594710d1 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 14 Jun 2023 21:44:48 -0700 Subject: PCI: hv: Fix a race condition in hv_irq_unmask() that can cause panic commit 2738d5ab7929a845b654cd171a1e275c37eb428e upstream. When the host tries to remove a PCI device, the host first sends a PCI_EJECT message to the guest, and the guest is supposed to gracefully remove the PCI device and send a PCI_EJECTION_COMPLETE message to the host; the host then sends a VMBus message CHANNELMSG_RESCIND_CHANNELOFFER to the guest (when the guest receives this message, the device is already unassigned from the guest) and the guest can do some final cleanup work; if the guest fails to respond to the PCI_EJECT message within one minute, the host sends the VMBus message CHANNELMSG_RESCIND_CHANNELOFFER and removes the PCI device forcibly. In the case of fast device addition/removal, it's possible that the PCI device driver is still configuring MSI-X interrupts when the guest receives the PCI_EJECT message; the channel callback calls hv_pci_eject_device(), which sets hpdev->state to hv_pcichild_ejecting, and schedules a work hv_eject_device_work(); if the PCI device driver is calling pci_alloc_irq_vectors() -> ... -> hv_compose_msi_msg(), we can break the while loop in hv_compose_msi_msg() due to the updated hpdev->state, and leave data->chip_data with its default value of NULL; later, when the PCI device driver calls request_irq() -> ... -> hv_irq_unmask(), the guest crashes in hv_arch_irq_unmask() due to data->chip_data being NULL. Fix the issue by not testing hpdev->state in the while loop: when the guest receives PCI_EJECT, the device is still assigned to the guest, and the guest has one minute to finish the device removal gracefully. We don't really need to (and we should not) test hpdev->state in the loop. Fixes: de0aa7b2f97d ("PCI: hv: Fix 2 hang issues in hv_compose_msi_msg()") Signed-off-by: Dexuan Cui Reviewed-by: Michael Kelley Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615044451.5580-3-decui@microsoft.com Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-hyperv.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index ec24a2ddbe57..6f6b30cd79a0 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -647,6 +647,11 @@ static void hv_arch_irq_unmask(struct irq_data *data) pbus = pdev->bus; hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); int_desc = data->chip_data; + if (!int_desc) { + dev_warn(&hbus->hdev->device, "%s() can not unmask irq %u\n", + __func__, data->irq); + return; + } spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags); @@ -1915,12 +1920,6 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) hv_pci_onchannelcallback(hbus); spin_unlock_irqrestore(&channel->sched_lock, flags); - if (hpdev->state == hv_pcichild_ejecting) { - dev_err_once(&hbus->hdev->device, - "the device is being ejected\n"); - goto enable_tasklet; - } - udelay(100); } -- cgit 1.4.1 From 4db49d59a89ce485fd247dff044686bba2a683b8 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 14 Jun 2023 21:44:51 -0700 Subject: PCI: hv: Add a per-bus mutex state_lock commit 067d6ec7ed5b49380688e06c1e5f883a71bef4fe upstream. In the case of fast device addition/removal, it's possible that hv_eject_device_work() can start to run before create_root_hv_pci_bus() starts to run; as a result, the pci_get_domain_bus_and_slot() in hv_eject_device_work() can return a 'pdev' of NULL, and hv_eject_device_work() can remove the 'hpdev', and immediately send a message PCI_EJECTION_COMPLETE to the host, and the host immediately unassigns the PCI device from the guest; meanwhile, create_root_hv_pci_bus() and the PCI device driver can be probing the dead PCI device and reporting timeout errors. Fix the issue by adding a per-bus mutex 'state_lock' and grabbing the mutex before powering on the PCI bus in hv_pci_enter_d0(): when hv_eject_device_work() starts to run, it's able to find the 'pdev' and call pci_stop_and_remove_bus_device(pdev): if the PCI device driver has loaded, the PCI device driver's probe() function is already called in create_root_hv_pci_bus() -> pci_bus_add_devices(), and now hv_eject_device_work() -> pci_stop_and_remove_bus_device() is able to call the PCI device driver's remove() function and remove the device reliably; if the PCI device driver hasn't loaded yet, the function call hv_eject_device_work() -> pci_stop_and_remove_bus_device() is able to remove the PCI device reliably and the PCI device driver's probe() function won't be called; if the PCI device driver's probe() is already running (e.g., systemd-udev is loading the PCI device driver), it must be holding the per-device lock, and after the probe() finishes and releases the lock, hv_eject_device_work() -> pci_stop_and_remove_bus_device() is able to proceed to remove the device reliably. Fixes: 4daace0d8ce8 ("PCI: hv: Add paravirtual PCI front-end for Microsoft Hyper-V VMs") Signed-off-by: Dexuan Cui Reviewed-by: Michael Kelley Acked-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615044451.5580-6-decui@microsoft.com Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-hyperv.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 6f6b30cd79a0..3351863352d3 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -489,7 +489,10 @@ struct hv_pcibus_device { struct fwnode_handle *fwnode; /* Protocol version negotiated with the host */ enum pci_protocol_version_t protocol_version; + + struct mutex state_lock; enum hv_pcibus_state state; + struct hv_device *hdev; resource_size_t low_mmio_space; resource_size_t high_mmio_space; @@ -2525,6 +2528,8 @@ static void pci_devices_present_work(struct work_struct *work) if (!dr) return; + mutex_lock(&hbus->state_lock); + /* First, mark all existing children as reported missing. */ spin_lock_irqsave(&hbus->device_list_lock, flags); list_for_each_entry(hpdev, &hbus->children, list_entry) { @@ -2606,6 +2611,8 @@ static void pci_devices_present_work(struct work_struct *work) break; } + mutex_unlock(&hbus->state_lock); + kfree(dr); } @@ -2754,6 +2761,8 @@ static void hv_eject_device_work(struct work_struct *work) hpdev = container_of(work, struct hv_pci_dev, wrk); hbus = hpdev->hbus; + mutex_lock(&hbus->state_lock); + /* * Ejection can come before or after the PCI bus has been set up, so * attempt to find it and tear down the bus state, if it exists. This @@ -2790,6 +2799,8 @@ static void hv_eject_device_work(struct work_struct *work) put_pcichild(hpdev); put_pcichild(hpdev); /* hpdev has been freed. Do not use it any more. */ + + mutex_unlock(&hbus->state_lock); } /** @@ -3580,6 +3591,7 @@ static int hv_pci_probe(struct hv_device *hdev, return -ENOMEM; hbus->bridge = bridge; + mutex_init(&hbus->state_lock); hbus->state = hv_pcibus_init; hbus->wslot_res_allocated = -1; @@ -3688,9 +3700,11 @@ static int hv_pci_probe(struct hv_device *hdev, if (ret) goto free_irq_domain; + mutex_lock(&hbus->state_lock); + ret = hv_pci_enter_d0(hdev); if (ret) - goto free_irq_domain; + goto release_state_lock; ret = hv_pci_allocate_bridge_windows(hbus); if (ret) @@ -3708,12 +3722,15 @@ static int hv_pci_probe(struct hv_device *hdev, if (ret) goto free_windows; + mutex_unlock(&hbus->state_lock); return 0; free_windows: hv_pci_free_bridge_windows(hbus); exit_d0: (void) hv_pci_bus_exit(hdev, true); +release_state_lock: + mutex_unlock(&hbus->state_lock); free_irq_domain: irq_domain_remove(hbus->irq_domain); free_fwnode: @@ -3967,20 +3984,26 @@ static int hv_pci_resume(struct hv_device *hdev) if (ret) goto out; + mutex_lock(&hbus->state_lock); + ret = hv_pci_enter_d0(hdev); if (ret) - goto out; + goto release_state_lock; ret = hv_send_resources_allocated(hdev); if (ret) - goto out; + goto release_state_lock; prepopulate_bars(hbus); hv_pci_restore_msi_state(hbus); hbus->state = hv_pcibus_installed; + mutex_unlock(&hbus->state_lock); return 0; + +release_state_lock: + mutex_unlock(&hbus->state_lock); out: vmbus_close(hdev->channel); return ret; -- cgit 1.4.1 From 4d729cc67b05b873055668c1868578f74ce17703 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 19 Jun 2023 09:35:34 -0600 Subject: io_uring/net: clear msg_controllen on partial sendmsg retry commit b1dc492087db0f2e5a45f1072a743d04618dd6be upstream. If we have cmsg attached AND we transferred partial data at least, clear msg_controllen on retry so we don't attempt to send that again. Cc: stable@vger.kernel.org # 5.10+ Fixes: cac9e4418f4c ("io_uring/net: save msghdr->msg_control for retries") Reported-by: Stefan Metzmacher Reviewed-by: Stefan Metzmacher Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io_uring/net.c b/io_uring/net.c index 05792a689994..8ead497b112c 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -313,6 +313,8 @@ int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) if (ret == -EAGAIN && (issue_flags & IO_URING_F_NONBLOCK)) return io_setup_async_msg(req, kmsg, issue_flags); if (ret > 0 && io_net_retry(sock, flags)) { + kmsg->msg.msg_controllen = 0; + kmsg->msg.msg_control = NULL; sr->done_io += ret; req->flags |= REQ_F_PARTIAL_IO; return io_setup_async_msg(req, kmsg, issue_flags); -- cgit 1.4.1 From 1d9dc9bed9996f35bd7b1ceec690e617f760bc58 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 19 Jun 2023 09:41:05 -0600 Subject: io_uring/net: disable partial retries for recvmsg with cmsg commit 78d0d2063bab954d19a1696feae4c7706a626d48 upstream. We cannot sanely handle partial retries for recvmsg if we have cmsg attached. If we don't, then we'd just be overwriting the initial cmsg header on retries. Alternatively we could increment and handle this appropriately, but it doesn't seem worth the complication. Move the MSG_WAITALL check into the non-multishot case while at it, since MSG_WAITALL is explicitly disabled for multishot anyway. Link: https://lore.kernel.org/io-uring/0b0d4411-c8fd-4272-770b-e030af6919a0@kernel.dk/ Cc: stable@vger.kernel.org # 5.10+ Reported-by: Stefan Metzmacher Reviewed-by: Stefan Metzmacher Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/net.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/io_uring/net.c b/io_uring/net.c index 8ead497b112c..41f828d93c89 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -781,16 +781,19 @@ retry_multishot: flags = sr->msg_flags; if (force_nonblock) flags |= MSG_DONTWAIT; - if (flags & MSG_WAITALL) - min_ret = iov_iter_count(&kmsg->msg.msg_iter); kmsg->msg.msg_get_inq = 1; - if (req->flags & REQ_F_APOLL_MULTISHOT) + if (req->flags & REQ_F_APOLL_MULTISHOT) { ret = io_recvmsg_multishot(sock, sr, kmsg, flags, &mshot_finished); - else + } else { + /* disable partial retry for recvmsg with cmsg attached */ + if (flags & MSG_WAITALL && !kmsg->msg.msg_controllen) + min_ret = iov_iter_count(&kmsg->msg.msg_iter); + ret = __sys_recvmsg_sock(sock, &kmsg->msg, sr->umsg, kmsg->uaddr, flags); + } if (ret < min_ret) { if (ret == -EAGAIN && force_nonblock) { -- cgit 1.4.1 From b7bb71dfb541df376c21c24451369fea83c4f327 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 20 Jun 2023 18:24:18 +0200 Subject: mptcp: handle correctly disconnect() failures commit c2b2ae3925b65070adb27d5a31a31c376f26dec7 upstream. Currently the mptcp code has assumes that disconnect() can fail only at mptcp_sendmsg_fastopen() time - to avoid a deadlock scenario - and don't even bother returning an error code. Soon mptcp_disconnect() will handle more error conditions: let's track them explicitly. As a bonus, explicitly annotate TCP-level disconnect as not failing: the mptcp code never blocks for event on the subflows. Fixes: 7d803344fdc3 ("mptcp: fix deadlock in fastopen error path") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Tested-by: Christoph Paasch Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 59186997c3e2..fffcbc881c98 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1708,7 +1708,13 @@ static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msgh if (ret && ret != -EINPROGRESS && ret != -ERESTARTSYS && ret != -EINTR) *copied_syn = 0; } else if (ret && ret != -EINPROGRESS) { - mptcp_disconnect(sk, 0); + /* The disconnect() op called by tcp_sendmsg_fastopen()/ + * __inet_stream_connect() can fail, due to looking check, + * see mptcp_disconnect(). + * Attempt it again outside the problematic scope. + */ + if (!mptcp_disconnect(sk, 0)) + sk->sk_socket->state = SS_UNCONNECTED; } return ret; @@ -2375,7 +2381,10 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, need_push = (flags & MPTCP_CF_PUSH) && __mptcp_retransmit_pending_data(sk); if (!dispose_it) { - tcp_disconnect(ssk, 0); + /* The MPTCP code never wait on the subflow sockets, TCP-level + * disconnect should never fail + */ + WARN_ON_ONCE(tcp_disconnect(ssk, 0)); msk->subflow->state = SS_UNCONNECTED; mptcp_subflow_ctx_reset(subflow); release_sock(ssk); @@ -2799,7 +2808,7 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how) break; fallthrough; case TCP_SYN_SENT: - tcp_disconnect(ssk, O_NONBLOCK); + WARN_ON_ONCE(tcp_disconnect(ssk, O_NONBLOCK)); break; default: if (__mptcp_check_fallback(mptcp_sk(sk))) { @@ -3053,11 +3062,10 @@ static int mptcp_disconnect(struct sock *sk, int flags) /* We are on the fastopen error path. We can't call straight into the * subflows cleanup code due to lock nesting (we are already under - * msk->firstsocket lock). Do nothing and leave the cleanup to the - * caller. + * msk->firstsocket lock). */ if (msk->fastopening) - return 0; + return -EBUSY; inet_sk_state_store(sk, TCP_CLOSE); -- cgit 1.4.1 From b747e755986a43d00a2ac7e2323d1e1a299e8a1e Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 20 Jun 2023 18:24:19 +0200 Subject: mptcp: fix possible divide by zero in recvmsg() commit 0ad529d9fd2bfa3fc619552a8d2fb2f2ef0bce2e upstream. Christoph reported a divide by zero bug in mptcp_recvmsg(): divide error: 0000 [#1] PREEMPT SMP CPU: 1 PID: 19978 Comm: syz-executor.6 Not tainted 6.4.0-rc2-gffcc7899081b #20 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.11.0-2.el7 04/01/2014 RIP: 0010:__tcp_select_window+0x30e/0x420 net/ipv4/tcp_output.c:3018 Code: 11 ff 0f b7 cd c1 e9 0c b8 ff ff ff ff d3 e0 89 c1 f7 d1 01 cb 21 c3 eb 17 e8 2e 83 11 ff 31 db eb 0e e8 25 83 11 ff 89 d8 99 7c 24 04 29 d3 65 48 8b 04 25 28 00 00 00 48 3b 44 24 10 75 60 RSP: 0018:ffffc90000a07a18 EFLAGS: 00010246 RAX: 000000000000ffd7 RBX: 000000000000ffd7 RCX: 0000000000040000 RDX: 0000000000000000 RSI: 000000000003ffff RDI: 0000000000040000 RBP: 000000000000ffd7 R08: ffffffff820cf297 R09: 0000000000000001 R10: 0000000000000000 R11: ffffffff8103d1a0 R12: 0000000000003f00 R13: 0000000000300000 R14: ffff888101cf3540 R15: 0000000000180000 FS: 00007f9af4c09640(0000) GS:ffff88813bd00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000001b33824000 CR3: 000000012f241001 CR4: 0000000000170ee0 Call Trace: __tcp_cleanup_rbuf+0x138/0x1d0 net/ipv4/tcp.c:1611 mptcp_recvmsg+0xcb8/0xdd0 net/mptcp/protocol.c:2034 inet_recvmsg+0x127/0x1f0 net/ipv4/af_inet.c:861 ____sys_recvmsg+0x269/0x2b0 net/socket.c:1019 ___sys_recvmsg+0xe6/0x260 net/socket.c:2764 do_recvmmsg+0x1a5/0x470 net/socket.c:2858 __do_sys_recvmmsg net/socket.c:2937 [inline] __se_sys_recvmmsg net/socket.c:2953 [inline] __x64_sys_recvmmsg+0xa6/0x130 net/socket.c:2953 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x47/0xa0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc RIP: 0033:0x7f9af58fc6a9 Code: 5c c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 4f 37 0d 00 f7 d8 64 89 01 48 RSP: 002b:00007f9af4c08cd8 EFLAGS: 00000246 ORIG_RAX: 000000000000012b RAX: ffffffffffffffda RBX: 00000000006bc050 RCX: 00007f9af58fc6a9 RDX: 0000000000000001 RSI: 0000000020000140 RDI: 0000000000000004 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000f00 R11: 0000000000000246 R12: 00000000006bc05c R13: fffffffffffffea8 R14: 00000000006bc050 R15: 000000000001fe40 mptcp_recvmsg is allowed to release the msk socket lock when blocking, and before re-acquiring it another thread could have switched the sock to TCP_LISTEN status - with a prior connect(AF_UNSPEC) - also clearing icsk_ack.rcv_mss. Address the issue preventing the disconnect if some other process is concurrently performing a blocking syscall on the same socket, alike commit 4faeee0cf8a5 ("tcp: deny tcp_disconnect() when threads are waiting"). Fixes: a6b118febbab ("mptcp: add receive buffer auto-tuning") Cc: stable@vger.kernel.org Reported-by: Christoph Paasch Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/404 Signed-off-by: Paolo Abeni Tested-by: Christoph Paasch Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index fffcbc881c98..f481cb0db8ec 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3060,6 +3060,12 @@ static int mptcp_disconnect(struct sock *sk, int flags) { struct mptcp_sock *msk = mptcp_sk(sk); + /* Deny disconnect if other threads are blocked in sk_wait_event() + * or inet_wait_for_connect(). + */ + if (sk->sk_wait_pending) + return -EBUSY; + /* We are on the fastopen error path. We can't call straight into the * subflows cleanup code due to lock nesting (we are already under * msk->firstsocket lock). @@ -3126,6 +3132,7 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk, inet_sk(nsk)->pinet6 = mptcp_inet6_sk(nsk); #endif + nsk->sk_wait_pending = 0; __mptcp_init_sock(nsk); msk = mptcp_sk(nsk); -- cgit 1.4.1 From 1d3127542665e0a4dee20285766b147bb87402e6 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 20 Jun 2023 18:24:20 +0200 Subject: mptcp: fix possible list corruption on passive MPJ commit 56a666c48b038e91b76471289e2cf60c79d326b9 upstream. At passive MPJ time, if the msk socket lock is held by the user, the new subflow is appended to the msk->join_list under the msk data lock. In mptcp_release_cb()/__mptcp_flush_join_list(), the subflows in that list are moved from the join_list into the conn_list under the msk socket lock. Append and removal could race, possibly corrupting such list. Address the issue splicing the join list into a temporary one while still under the msk data lock. Found by code inspection, the race itself should be almost impossible to trigger in practice. Fixes: 3e5014909b56 ("mptcp: cleanup MPJ subflow list handling") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index f481cb0db8ec..a5b330b2eeb3 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -846,12 +846,12 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk) return true; } -static void __mptcp_flush_join_list(struct sock *sk) +static void __mptcp_flush_join_list(struct sock *sk, struct list_head *join_list) { struct mptcp_subflow_context *tmp, *subflow; struct mptcp_sock *msk = mptcp_sk(sk); - list_for_each_entry_safe(subflow, tmp, &msk->join_list, node) { + list_for_each_entry_safe(subflow, tmp, join_list, node) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); bool slow = lock_sock_fast(ssk); @@ -3335,9 +3335,14 @@ static void mptcp_release_cb(struct sock *sk) for (;;) { unsigned long flags = (msk->cb_flags & MPTCP_FLAGS_PROCESS_CTX_NEED) | msk->push_pending; + struct list_head join_list; + if (!flags) break; + INIT_LIST_HEAD(&join_list); + list_splice_init(&msk->join_list, &join_list); + /* the following actions acquire the subflow socket lock * * 1) can't be invoked in atomic scope @@ -3348,8 +3353,9 @@ static void mptcp_release_cb(struct sock *sk) msk->push_pending = 0; msk->cb_flags &= ~flags; spin_unlock_bh(&sk->sk_lock.slock); + if (flags & BIT(MPTCP_FLUSH_JOIN_LIST)) - __mptcp_flush_join_list(sk); + __mptcp_flush_join_list(sk, &join_list); if (flags & BIT(MPTCP_PUSH_PENDING)) __mptcp_push_pending(sk, 0); if (flags & BIT(MPTCP_RETRANSMIT)) -- cgit 1.4.1 From c2c46a70282f5c34fab89234fb8f016a65d55afd Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 20 Jun 2023 18:24:21 +0200 Subject: mptcp: consolidate fallback and non fallback state machine commit 81c1d029016001f994ce1c46849c5e9900d8eab8 upstream. An orphaned msk releases the used resources via the worker, when the latter first see the msk in CLOSED status. If the msk status transitions to TCP_CLOSE in the release callback invoked by the worker's final release_sock(), such instance of the workqueue will not take any action. Additionally the MPTCP code prevents scheduling the worker once the socket reaches the CLOSE status: such msk resources will be leaked. The only code path that can trigger the above scenario is the __mptcp_check_send_data_fin() in fallback mode. Address the issue removing the special handling of fallback socket in __mptcp_check_send_data_fin(), consolidating the state machine for fallback and non fallback socket. Since non-fallback sockets do not send and do not receive data_fin, the mptcp code can update the msk internal status to match the next step in the SM every time data fin (ack) should be generated or received. As a consequence we can remove a bunch of checks for fallback from the fastpath. Fixes: 6e628cd3a8f7 ("mptcp: use mptcp release_cb for delayed tasks") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 41 +++++++++++++++-------------------------- net/mptcp/subflow.c | 17 ++++++++++------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index a5b330b2eeb3..a2c6ce40e426 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -53,7 +53,7 @@ enum { static struct percpu_counter mptcp_sockets_allocated ____cacheline_aligned_in_smp; static void __mptcp_destroy_sock(struct sock *sk); -static void __mptcp_check_send_data_fin(struct sock *sk); +static void mptcp_check_send_data_fin(struct sock *sk); DEFINE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions); static struct net_device mptcp_napi_dev; @@ -420,8 +420,7 @@ static bool mptcp_pending_data_fin_ack(struct sock *sk) { struct mptcp_sock *msk = mptcp_sk(sk); - return !__mptcp_check_fallback(msk) && - ((1 << sk->sk_state) & + return ((1 << sk->sk_state) & (TCPF_FIN_WAIT1 | TCPF_CLOSING | TCPF_LAST_ACK)) && msk->write_seq == READ_ONCE(msk->snd_una); } @@ -579,9 +578,6 @@ static bool mptcp_check_data_fin(struct sock *sk) u64 rcv_data_fin_seq; bool ret = false; - if (__mptcp_check_fallback(msk)) - return ret; - /* Need to ack a DATA_FIN received from a peer while this side * of the connection is in ESTABLISHED, FIN_WAIT1, or FIN_WAIT2. * msk->rcv_data_fin was set when parsing the incoming options @@ -619,7 +615,8 @@ static bool mptcp_check_data_fin(struct sock *sk) } ret = true; - mptcp_send_ack(msk); + if (!__mptcp_check_fallback(msk)) + mptcp_send_ack(msk); mptcp_close_wake_up(sk); } return ret; @@ -1606,7 +1603,7 @@ out: if (!mptcp_timer_pending(sk)) mptcp_reset_timer(sk); if (do_check_data_fin) - __mptcp_check_send_data_fin(sk); + mptcp_check_send_data_fin(sk); } static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk) @@ -2665,8 +2662,6 @@ static void mptcp_worker(struct work_struct *work) if (unlikely((1 << state) & (TCPF_CLOSE | TCPF_LISTEN))) goto unlock; - mptcp_check_data_fin_ack(sk); - mptcp_check_fastclose(msk); mptcp_pm_nl_work(msk); @@ -2674,7 +2669,8 @@ static void mptcp_worker(struct work_struct *work) if (test_and_clear_bit(MPTCP_WORK_EOF, &msk->flags)) mptcp_check_for_eof(msk); - __mptcp_check_send_data_fin(sk); + mptcp_check_send_data_fin(sk); + mptcp_check_data_fin_ack(sk); mptcp_check_data_fin(sk); if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags)) @@ -2815,6 +2811,12 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how) pr_debug("Fallback"); ssk->sk_shutdown |= how; tcp_shutdown(ssk, how); + + /* simulate the data_fin ack reception to let the state + * machine move forward + */ + WRITE_ONCE(mptcp_sk(sk)->snd_una, mptcp_sk(sk)->snd_nxt); + mptcp_schedule_work(sk); } else { pr_debug("Sending DATA_FIN on subflow %p", ssk); tcp_send_ack(ssk); @@ -2854,7 +2856,7 @@ static int mptcp_close_state(struct sock *sk) return next & TCP_ACTION_FIN; } -static void __mptcp_check_send_data_fin(struct sock *sk) +static void mptcp_check_send_data_fin(struct sock *sk) { struct mptcp_subflow_context *subflow; struct mptcp_sock *msk = mptcp_sk(sk); @@ -2872,19 +2874,6 @@ static void __mptcp_check_send_data_fin(struct sock *sk) WRITE_ONCE(msk->snd_nxt, msk->write_seq); - /* fallback socket will not get data_fin/ack, can move to the next - * state now - */ - if (__mptcp_check_fallback(msk)) { - WRITE_ONCE(msk->snd_una, msk->write_seq); - if ((1 << sk->sk_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) { - inet_sk_state_store(sk, TCP_CLOSE); - mptcp_close_wake_up(sk); - } else if (sk->sk_state == TCP_FIN_WAIT1) { - inet_sk_state_store(sk, TCP_FIN_WAIT2); - } - } - mptcp_for_each_subflow(msk, subflow) { struct sock *tcp_sk = mptcp_subflow_tcp_sock(subflow); @@ -2904,7 +2893,7 @@ static void __mptcp_wr_shutdown(struct sock *sk) WRITE_ONCE(msk->write_seq, msk->write_seq + 1); WRITE_ONCE(msk->snd_data_fin_enable, 1); - __mptcp_check_send_data_fin(sk); + mptcp_check_send_data_fin(sk); } static void __mptcp_destroy_sock(struct sock *sk) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 336878f8a222..047e46dd028d 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1688,14 +1688,16 @@ static void subflow_state_change(struct sock *sk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct sock *parent = subflow->conn; + struct mptcp_sock *msk; __subflow_state_change(sk); + msk = mptcp_sk(parent); if (subflow_simultaneous_connect(sk)) { mptcp_propagate_sndbuf(parent, sk); mptcp_do_fallback(sk); - mptcp_rcv_space_init(mptcp_sk(parent), sk); - pr_fallback(mptcp_sk(parent)); + mptcp_rcv_space_init(msk, sk); + pr_fallback(msk); subflow->conn_finished = 1; mptcp_set_connected(parent); } @@ -1711,11 +1713,12 @@ static void subflow_state_change(struct sock *sk) subflow_sched_work_if_closed(mptcp_sk(parent), sk); - if (__mptcp_check_fallback(mptcp_sk(parent)) && - !subflow->rx_eof && subflow_is_done(sk)) { - subflow->rx_eof = 1; - mptcp_subflow_eof(parent); - } + /* when the fallback subflow closes the rx side, trigger a 'dummy' + * ingress data fin, so that the msk state will follow along + */ + if (__mptcp_check_fallback(msk) && subflow_is_done(sk) && msk->first == sk && + mptcp_update_rcv_data_fin(msk, READ_ONCE(msk->ack_seq), true)) + mptcp_schedule_work(parent); } void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_ssk) -- cgit 1.4.1 From 7b162a18d332bd5ca7ff9170ded674b4cdad31f7 Mon Sep 17 00:00:00 2001 From: Xiu Jianfeng Date: Sat, 10 Jun 2023 17:26:43 +0800 Subject: cgroup: Do not corrupt task iteration when rebinding subsystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6f363f5aa845561f7ea496d8b1175e3204470486 upstream. We found a refcount UAF bug as follows: refcount_t: addition on 0; use-after-free. WARNING: CPU: 1 PID: 342 at lib/refcount.c:25 refcount_warn_saturate+0xa0/0x148 Workqueue: events cpuset_hotplug_workfn Call trace: refcount_warn_saturate+0xa0/0x148 __refcount_add.constprop.0+0x5c/0x80 css_task_iter_advance_css_set+0xd8/0x210 css_task_iter_advance+0xa8/0x120 css_task_iter_next+0x94/0x158 update_tasks_root_domain+0x58/0x98 rebuild_root_domains+0xa0/0x1b0 rebuild_sched_domains_locked+0x144/0x188 cpuset_hotplug_workfn+0x138/0x5a0 process_one_work+0x1e8/0x448 worker_thread+0x228/0x3e0 kthread+0xe0/0xf0 ret_from_fork+0x10/0x20 then a kernel panic will be triggered as below: Unable to handle kernel paging request at virtual address 00000000c0000010 Call trace: cgroup_apply_control_disable+0xa4/0x16c rebind_subsystems+0x224/0x590 cgroup_destroy_root+0x64/0x2e0 css_free_rwork_fn+0x198/0x2a0 process_one_work+0x1d4/0x4bc worker_thread+0x158/0x410 kthread+0x108/0x13c ret_from_fork+0x10/0x18 The race that cause this bug can be shown as below: (hotplug cpu) | (umount cpuset) mutex_lock(&cpuset_mutex) | mutex_lock(&cgroup_mutex) cpuset_hotplug_workfn | rebuild_root_domains | rebind_subsystems update_tasks_root_domain | spin_lock_irq(&css_set_lock) css_task_iter_start | list_move_tail(&cset->e_cset_node[ss->id] while(css_task_iter_next) | &dcgrp->e_csets[ss->id]); css_task_iter_end | spin_unlock_irq(&css_set_lock) mutex_unlock(&cpuset_mutex) | mutex_unlock(&cgroup_mutex) Inside css_task_iter_start/next/end, css_set_lock is hold and then released, so when iterating task(left side), the css_set may be moved to another list(right side), then it->cset_head points to the old list head and it->cset_pos->next points to the head node of new list, which can't be used as struct css_set. To fix this issue, switch from all css_sets to only scgrp's css_sets to patch in-flight iterators to preserve correct iteration, and then update it->cset_head as well. Reported-by: Gaosheng Cui Link: https://www.spinics.net/lists/cgroups/msg37935.html Suggested-by: Michal Koutný Link: https://lore.kernel.org/all/20230526114139.70274-1-xiujianfeng@huaweicloud.com/ Signed-off-by: Xiu Jianfeng Fixes: 2d8f243a5e6e ("cgroup: implement cgroup->e_csets[]") Cc: stable@vger.kernel.org # v3.16+ Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- kernel/cgroup/cgroup.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 36c95626afec..2380c4daef33 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -1782,7 +1782,7 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) { struct cgroup *dcgrp = &dst_root->cgrp; struct cgroup_subsys *ss; - int ssid, i, ret; + int ssid, ret; u16 dfl_disable_ss_mask = 0; lockdep_assert_held(&cgroup_mutex); @@ -1826,7 +1826,8 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) struct cgroup_root *src_root = ss->root; struct cgroup *scgrp = &src_root->cgrp; struct cgroup_subsys_state *css = cgroup_css(scgrp, ss); - struct css_set *cset; + struct css_set *cset, *cset_pos; + struct css_task_iter *it; WARN_ON(!css || cgroup_css(dcgrp, ss)); @@ -1844,9 +1845,22 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) css->cgroup = dcgrp; spin_lock_irq(&css_set_lock); - hash_for_each(css_set_table, i, cset, hlist) + WARN_ON(!list_empty(&dcgrp->e_csets[ss->id])); + list_for_each_entry_safe(cset, cset_pos, &scgrp->e_csets[ss->id], + e_cset_node[ss->id]) { list_move_tail(&cset->e_cset_node[ss->id], &dcgrp->e_csets[ss->id]); + /* + * all css_sets of scgrp together in same order to dcgrp, + * patch in-flight iterators to preserve correct iteration. + * since the iterator is always advanced right away and + * finished when it->cset_pos meets it->cset_head, so only + * update it->cset_head is enough here. + */ + list_for_each_entry(it, &cset->task_iters, iters_node) + if (it->cset_head == &scgrp->e_csets[ss->id]) + it->cset_head = &dcgrp->e_csets[ss->id]; + } spin_unlock_irq(&css_set_lock); if (ss->css_rstat_flush) { -- cgit 1.4.1 From 0d7a4e6589a02fd87d286f7f0713ba6d8fc344e6 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 11 Jun 2023 22:48:12 +0900 Subject: cgroup,freezer: hold cpu_hotplug_lock before freezer_mutex in freezer_css_{online,offline}() commit f0cc749254d12c78e93dae3b27b21dc9546843d0 upstream. syzbot is again reporting circular locking dependency between cpu_hotplug_lock and freezer_mutex. Do like what we did with commit 57dcd64c7e036299 ("cgroup,freezer: hold cpu_hotplug_lock before freezer_mutex"). Reported-by: syzbot Closes: https://syzkaller.appspot.com/bug?extid=2ab700fe1829880a2ec6 Signed-off-by: Tetsuo Handa Tested-by: syzbot Fixes: f5d39b020809 ("freezer,sched: Rewrite core freezer logic") Cc: stable@vger.kernel.org # v6.1+ Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- kernel/cgroup/legacy_freezer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/cgroup/legacy_freezer.c b/kernel/cgroup/legacy_freezer.c index 936473203a6b..122dacb3a443 100644 --- a/kernel/cgroup/legacy_freezer.c +++ b/kernel/cgroup/legacy_freezer.c @@ -108,16 +108,18 @@ static int freezer_css_online(struct cgroup_subsys_state *css) struct freezer *freezer = css_freezer(css); struct freezer *parent = parent_freezer(freezer); + cpus_read_lock(); mutex_lock(&freezer_mutex); freezer->state |= CGROUP_FREEZER_ONLINE; if (parent && (parent->state & CGROUP_FREEZING)) { freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN; - static_branch_inc(&freezer_active); + static_branch_inc_cpuslocked(&freezer_active); } mutex_unlock(&freezer_mutex); + cpus_read_unlock(); return 0; } @@ -132,14 +134,16 @@ static void freezer_css_offline(struct cgroup_subsys_state *css) { struct freezer *freezer = css_freezer(css); + cpus_read_lock(); mutex_lock(&freezer_mutex); if (freezer->state & CGROUP_FREEZING) - static_branch_dec(&freezer_active); + static_branch_dec_cpuslocked(&freezer_active); freezer->state = 0; mutex_unlock(&freezer_mutex); + cpus_read_unlock(); } static void freezer_css_free(struct cgroup_subsys_state *css) -- cgit 1.4.1 From 3dd0041c41da4ce5048a710ece0508ae29691fde Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sat, 17 Jun 2023 16:53:19 +0800 Subject: mmc: litex_mmc: set PROBE_PREFER_ASYNCHRONOUS commit f334ad47683606b682b4166b800d8b372d315436 upstream. mmc host drivers should have enabled the asynchronous probe option, but it seems like we didn't set it for litex_mmc when introducing litex mmc support, so let's set it now. Tested with linux-on-litex-vexriscv on sipeed tang nano 20K fpga. Signed-off-by: Jisheng Zhang Acked-by: Gabriel Somlo Fixes: 92e099104729 ("mmc: Add driver for LiteX's LiteSDCard interface") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230617085319.2139-1-jszhang@kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/litex_mmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 39c6707fdfdb..9af6b0902efe 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -649,6 +649,7 @@ static struct platform_driver litex_mmc_driver = { .driver = { .name = "litex-mmc", .of_match_table = litex_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; module_platform_driver(litex_mmc_driver); -- cgit 1.4.1 From 687d34c578078c21da15f9ed0d46b6b7b8ea6b6a Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 18 May 2023 11:39:36 +0200 Subject: mmc: sdhci-msm: Disable broken 64-bit DMA on MSM8916 commit e6f9e590b72e12bbb86b1b8be7e1981f357392ad upstream. While SDHCI claims to support 64-bit DMA on MSM8916 it does not seem to be properly functional. It is not immediately obvious because SDHCI is usually used with IOMMU bypassed on this SoC, and all physical memory has 32-bit addresses. But when trying to enable the IOMMU it quickly fails with an error such as the following: arm-smmu 1e00000.iommu: Unhandled context fault: fsr=0x402, iova=0xfffff200, fsynr=0xe0000, cbfrsynra=0x140, cb=3 mmc1: ADMA error: 0x02000000 mmc1: sdhci: ============ SDHCI REGISTER DUMP =========== mmc1: sdhci: Sys addr: 0x00000000 | Version: 0x00002e02 mmc1: sdhci: Blk size: 0x00000008 | Blk cnt: 0x00000000 mmc1: sdhci: Argument: 0x00000000 | Trn mode: 0x00000013 mmc1: sdhci: Present: 0x03f80206 | Host ctl: 0x00000019 mmc1: sdhci: Power: 0x0000000f | Blk gap: 0x00000000 mmc1: sdhci: Wake-up: 0x00000000 | Clock: 0x00000007 mmc1: sdhci: Timeout: 0x0000000a | Int stat: 0x00000001 mmc1: sdhci: Int enab: 0x03ff900b | Sig enab: 0x03ff100b mmc1: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000 mmc1: sdhci: Caps: 0x322dc8b2 | Caps_1: 0x00008007 mmc1: sdhci: Cmd: 0x0000333a | Max curr: 0x00000000 mmc1: sdhci: Resp[0]: 0x00000920 | Resp[1]: 0x5b590000 mmc1: sdhci: Resp[2]: 0xe6487f80 | Resp[3]: 0x0a404094 mmc1: sdhci: Host ctl2: 0x00000008 mmc1: sdhci: ADMA Err: 0x00000001 | ADMA Ptr: 0x0000000ffffff224 mmc1: sdhci_msm: ----------- VENDOR REGISTER DUMP ----------- mmc1: sdhci_msm: DLL sts: 0x00000000 | DLL cfg: 0x60006400 | DLL cfg2: 0x00000000 mmc1: sdhci_msm: DLL cfg3: 0x00000000 | DLL usr ctl: 0x00000000 | DDR cfg: 0x00000000 mmc1: sdhci_msm: Vndr func: 0x00018a9c | Vndr func2 : 0xf88018a8 Vndr func3: 0x00000000 mmc1: sdhci: ============================================ mmc1: sdhci: fffffffff200: DMA 0x0000ffffffffe100, LEN 0x0008, Attr=0x21 mmc1: sdhci: fffffffff20c: DMA 0x0000000000000000, LEN 0x0000, Attr=0x03 Looking closely it's obvious that only the 32-bit part of the address (0xfffff200) arrives at the SMMU, the higher 16-bit (0xffff...) get lost somewhere. This might not be a limitation of the SDHCI itself but perhaps the bus/interconnect it is connected to, or even the connection to the SMMU. Work around this by setting SDHCI_QUIRK2_BROKEN_64_BIT_DMA to avoid using 64-bit addresses. Signed-off-by: Stephan Gerhold Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230518-msm8916-64bit-v1-1-5694b0f35211@gerhold.net Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-msm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3a091a387ecb..a5ab2af3e520 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2486,6 +2486,9 @@ static inline void sdhci_msm_get_of_property(struct platform_device *pdev, msm_host->ddr_config = DDR_CONFIG_POR_VAL; of_property_read_u32(node, "qcom,dll-config", &msm_host->dll_config); + + if (of_device_is_compatible(node, "qcom,msm8916-sdhci")) + host->quirks2 |= SDHCI_QUIRK2_BROKEN_64_BIT_DMA; } static int sdhci_msm_gcc_reset(struct device *dev, struct sdhci_host *host) -- cgit 1.4.1 From 6c2af0fd83012db76782c1ad838386122279b8a6 Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Wed, 7 Jun 2023 10:27:12 +0200 Subject: mmc: meson-gx: remove redundant mmc_request_done() call from irq context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3c40eb8145325b0f5b93b8a169146078cb2c49d6 upstream. The call to mmc_request_done() can schedule, so it must not be called from irq context. Wake the irq thread if it needs to be called, and let its existing logic do its work. Fixes the following kernel bug, which appears when running an RT patched kernel on the AmLogic Meson AXG A113X SoC: [ 11.111407] BUG: scheduling while atomic: kworker/0:1H/75/0x00010001 [ 11.111438] Modules linked in: [ 11.111451] CPU: 0 PID: 75 Comm: kworker/0:1H Not tainted 6.4.0-rc3-rt2-rtx-00081-gfd07f41ed6b4-dirty #1 [ 11.111461] Hardware name: RTX AXG A113X Linux Platform Board (DT) [ 11.111469] Workqueue: kblockd blk_mq_run_work_fn [ 11.111492] Call trace: [ 11.111497] dump_backtrace+0xac/0xe8 [ 11.111510] show_stack+0x18/0x28 [ 11.111518] dump_stack_lvl+0x48/0x60 [ 11.111530] dump_stack+0x18/0x24 [ 11.111537] __schedule_bug+0x4c/0x68 [ 11.111548] __schedule+0x80/0x574 [ 11.111558] schedule_loop+0x2c/0x50 [ 11.111567] schedule_rtlock+0x14/0x20 [ 11.111576] rtlock_slowlock_locked+0x468/0x730 [ 11.111587] rt_spin_lock+0x40/0x64 [ 11.111596] __wake_up_common_lock+0x5c/0xc4 [ 11.111610] __wake_up+0x18/0x24 [ 11.111620] mmc_blk_mq_req_done+0x68/0x138 [ 11.111633] mmc_request_done+0x104/0x118 [ 11.111644] meson_mmc_request_done+0x38/0x48 [ 11.111654] meson_mmc_irq+0x128/0x1f0 [ 11.111663] __handle_irq_event_percpu+0x70/0x114 [ 11.111674] handle_irq_event_percpu+0x18/0x4c [ 11.111683] handle_irq_event+0x80/0xb8 [ 11.111691] handle_fasteoi_irq+0xa4/0x120 [ 11.111704] handle_irq_desc+0x20/0x38 [ 11.111712] generic_handle_domain_irq+0x1c/0x28 [ 11.111721] gic_handle_irq+0x8c/0xa8 [ 11.111735] call_on_irq_stack+0x24/0x4c [ 11.111746] do_interrupt_handler+0x88/0x94 [ 11.111757] el1_interrupt+0x34/0x64 [ 11.111769] el1h_64_irq_handler+0x18/0x24 [ 11.111779] el1h_64_irq+0x64/0x68 [ 11.111786] __add_wait_queue+0x0/0x4c [ 11.111795] mmc_blk_rw_wait+0x84/0x118 [ 11.111804] mmc_blk_mq_issue_rq+0x5c4/0x654 [ 11.111814] mmc_mq_queue_rq+0x194/0x214 [ 11.111822] blk_mq_dispatch_rq_list+0x3ac/0x528 [ 11.111834] __blk_mq_sched_dispatch_requests+0x340/0x4d0 [ 11.111847] blk_mq_sched_dispatch_requests+0x38/0x70 [ 11.111858] blk_mq_run_work_fn+0x3c/0x70 [ 11.111865] process_one_work+0x17c/0x1f0 [ 11.111876] worker_thread+0x1d4/0x26c [ 11.111885] kthread+0xe4/0xf4 [ 11.111894] ret_from_fork+0x10/0x20 Fixes: 51c5d8447bd7 ("MMC: meson: initial support for GX platforms") Cc: stable@vger.kernel.org Signed-off-by: Martin Hundebøll Link: https://lore.kernel.org/r/20230607082713.517157-1-martin@geanix.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/meson-gx-mmc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 5c94ad4661ce..5fb18be6f866 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1007,11 +1007,8 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id) if (data && !cmd->error) data->bytes_xfered = data->blksz * data->blocks; - if (meson_mmc_bounce_buf_read(data) || - meson_mmc_get_next_command(cmd)) - ret = IRQ_WAKE_THREAD; - else - ret = IRQ_HANDLED; + + return IRQ_WAKE_THREAD; } out: @@ -1023,9 +1020,6 @@ out: writel(start, host->regs + SD_EMMC_START); } - if (ret == IRQ_HANDLED) - meson_mmc_request_done(host->mmc, cmd->mrq); - return ret; } -- cgit 1.4.1 From f1b17198e45b9eb09464a7524089b9739cb8028d Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Tue, 13 Jun 2023 15:41:46 +0200 Subject: mmc: mmci: stm32: fix max busy timeout calculation commit 47b3ad6b7842f49d374a01b054a4b1461a621bdc upstream. The way that the timeout is currently calculated could lead to a u64 timeout value in mmci_start_command(). This value is then cast in a u32 register that leads to mmc erase failed issue with some SD cards. Fixes: 8266c585f489 ("mmc: mmci: add hardware busy timeout feature") Signed-off-by: Yann Gautier Signed-off-by: Christophe Kerello Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230613134146.418016-1-yann.gautier@foss.st.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/mmci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b9e5dfe74e5c..1c326e4307f4 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1735,7 +1735,8 @@ static void mmci_set_max_busy_timeout(struct mmc_host *mmc) return; if (host->variant->busy_timeout && mmc->actual_clock) - max_busy_timeout = ~0UL / (mmc->actual_clock / MSEC_PER_SEC); + max_busy_timeout = U32_MAX / DIV_ROUND_UP(mmc->actual_clock, + MSEC_PER_SEC); mmc->max_busy_timeout = max_busy_timeout; } -- cgit 1.4.1 From 1db5a39a904e209f76e1bdde071d8e89e46e7170 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:19 +0300 Subject: mmc: sdhci-spear: fix deferred probing commit 8d0caeedcd05a721f3cc2537b0ea212ec4027307 upstream. The driver overrides the error codes and IRQ0 returned by platform_get_irq() to -EINVAL, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Since commit ce753ad1549c ("platform: finally disallow IRQ0 in platform_get_irq() and its ilk") IRQ0 is no longer returned by those APIs, so we now can safely ignore it... Fixes: 682798a596a6 ("mmc: sdhci-spear: Handle return value of platform_get_irq") Cc: stable@vger.kernel.org # v5.19+ Signed-off-by: Sergey Shtylyov Acked-by: Viresh Kumar Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230617203622.6812-10-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-spear.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index d463e2fd5b1a..c79035727b20 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -65,8 +65,8 @@ static int sdhci_probe(struct platform_device *pdev) host->hw_name = "sdhci"; host->ops = &sdhci_pltfm_ops; host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto err_host; } host->quirks = SDHCI_QUIRK_BROKEN_ADMA; -- cgit 1.4.1 From 8b8756324c5bad76c8ad9d51d577a3872f225b6f Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:11 +0300 Subject: mmc: bcm2835: fix deferred probing commit 71150ac12558bcd9d75e6e24cf7c872c2efd80f3 upstream. The driver overrides the error codes and IRQ0 returned by platform_get_irq() to -EINVAL, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Since commit ce753ad1549c ("platform: finally disallow IRQ0 in platform_get_irq() and its ilk") IRQ0 is no longer returned by those APIs, so we now can safely ignore it... Fixes: 660fc733bd74 ("mmc: bcm2835: Add new driver for the sdhost controller.") Cc: stable@vger.kernel.org # v5.19+ Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-2-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/bcm2835.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 641ab4f42125..c15226be4d2a 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1411,8 +1411,8 @@ static int bcm2835_probe(struct platform_device *pdev) host->max_clk = clk_get_rate(clk); host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto err; } -- cgit 1.4.1 From 03b2149d5aca03850de7c89f58593b3019c1e5ce Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:21 +0300 Subject: mmc: sunxi: fix deferred probing commit c2df53c5806cfd746dae08e07bc8c4ad247c3b70 upstream. The driver overrides the error codes and IRQ0 returned by platform_get_irq() to -EINVAL, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Since commit ce753ad1549c ("platform: finally disallow IRQ0 in platform_get_irq() and its ilk") IRQ0 is no longer returned by those APIs, so we now can safely ignore it... Fixes: 2408a08583d2 ("mmc: sunxi-mmc: Handle return value of platform_get_irq") Cc: stable@vger.kernel.org # v5.19+ Signed-off-by: Sergey Shtylyov Reviewed-by: Jernej Skrabec Link: https://lore.kernel.org/r/20230617203622.6812-12-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sunxi-mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 3db9f32d6a7b..69dcb8805e05 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1350,8 +1350,8 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, return ret; host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto error_disable_mmc; } -- cgit 1.4.1 From 1dfca388fc7c456985f4e1cdf3449bb1aa896887 Mon Sep 17 00:00:00 2001 From: Krister Johansen Date: Mon, 12 Jun 2023 17:44:40 -0700 Subject: bpf: ensure main program has an extable commit 0108a4e9f3584a7a2c026d1601b0682ff7335d95 upstream. When subprograms are in use, the main program is not jit'd after the subprograms because jit_subprogs sets a value for prog->bpf_func upon success. Subsequent calls to the JIT are bypassed when this value is non-NULL. This leads to a situation where the main program and its func[0] counterpart are both in the bpf kallsyms tree, but only func[0] has an extable. Extables are only created during JIT. Now there are two nearly identical program ksym entries in the tree, but only one has an extable. Depending upon how the entries are placed, there's a chance that a fault will call search_extable on the aux with the NULL entry. Since jit_subprogs already copies state from func[0] to the main program, include the extable pointer in this state duplication. Additionally, ensure that the copy of the main program in func[0] is not added to the bpf_prog_kallsyms table. Instead, let the main program get added later in bpf_prog_load(). This ensures there is only a single copy of the main program in the kallsyms table, and that its tag matches the tag observed by tooling like bpftool. Cc: stable@vger.kernel.org Fixes: 1c2a088a6626 ("bpf: x64: add JIT support for multi-function programs") Signed-off-by: Krister Johansen Acked-by: Yonghong Song Acked-by: Ilya Leoshkevich Tested-by: Ilya Leoshkevich Link: https://lore.kernel.org/r/6de9b2f4b4724ef56efbb0339daaa66c8b68b1e7.1686616663.git.kjlx@templeofstupid.com Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 280e689517e1..a0e573c08f79 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13819,9 +13819,10 @@ static int jit_subprogs(struct bpf_verifier_env *env) } /* finally lock prog and jit images for all functions and - * populate kallsysm + * populate kallsysm. Begin at the first subprogram, since + * bpf_prog_load will add the kallsyms for the main program. */ - for (i = 0; i < env->subprog_cnt; i++) { + for (i = 1; i < env->subprog_cnt; i++) { bpf_prog_lock_ro(func[i]); bpf_prog_kallsyms_add(func[i]); } @@ -13847,6 +13848,8 @@ static int jit_subprogs(struct bpf_verifier_env *env) prog->jited = 1; prog->bpf_func = func[0]->bpf_func; prog->jited_len = func[0]->jited_len; + prog->aux->extable = func[0]->aux->extable; + prog->aux->num_exentries = func[0]->aux->num_exentries; prog->aux->func = func; prog->aux->func_cnt = env->subprog_cnt; bpf_prog_jit_attempt_done(prog); -- cgit 1.4.1 From 21945b7a868f3abe745d634c9014b75dfebe0a30 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Mon, 19 Jun 2023 17:02:34 +0200 Subject: wifi: iwlwifi: pcie: Handle SO-F device for PCI id 0x7AF0 commit 4e9f0ec38852c18faa9689322e758575af33e5d4 upstream. Add support for AX1690i and AX1690s devices with PCIE id 0x7AF0. Cc: stable@vger.kernel.org # 6.1+ Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20230619150233.461290-2-johannes@sipsolutions.net Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 03e8234d0352..f6872b2a0d9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -547,6 +547,8 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_DEV_INFO(0x54F0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), IWL_DEV_INFO(0x7A70, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), IWL_DEV_INFO(0x7A70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), + IWL_DEV_INFO(0x7AF0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), + IWL_DEV_INFO(0x7AF0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), IWL_DEV_INFO(0x271C, 0x0214, iwl9260_2ac_cfg, iwl9260_1_name), IWL_DEV_INFO(0x7E40, 0x1691, iwl_cfg_ma_a0_gf4_a0, iwl_ax411_killer_1690s_name), -- cgit 1.4.1 From ad5daeaa3d57b27a38083556d67f39dce8651a9e Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 15 Jun 2023 14:51:45 +0200 Subject: spi: spi-geni-qcom: correctly handle -EPROBE_DEFER from dma_request_chan() [ Upstream commit 9d7054fb3ac2e8d252aae1268f20623f244e644f ] Now spi_geni_grab_gpi_chan() errors are correctly reported, the -EPROBE_DEFER error should be returned from probe in case the GPI dma driver is built as module and/or not probed yet. Fixes: b59c122484ec ("spi: spi-geni-qcom: Add support for GPI dma") Fixes: 6532582c353f ("spi: spi-geni-qcom: fix error handling in spi_geni_grab_gpi_chan()") Signed-off-by: Neil Armstrong Link: https://lore.kernel.org/r/20230615-topic-sm8550-upstream-fix-spi-geni-qcom-probe-v2-1-670c3d9e8c9c@linaro.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-geni-qcom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 4e83cc5b445d..dd1581893fe7 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -595,6 +595,8 @@ static int spi_geni_init(struct spi_geni_master *mas) geni_se_select_mode(se, GENI_GPI_DMA); dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n"); break; + } else if (ret == -EPROBE_DEFER) { + goto out_pm; } /* * in case of failure to get dma channel, we can still do the -- cgit 1.4.1 From b385b1d28e4ecfa385007497fece0427eb928909 Mon Sep 17 00:00:00 2001 From: Teresa Remmet Date: Wed, 14 Jun 2023 14:52:40 +0200 Subject: regulator: pca9450: Fix LDO3OUT and LDO4OUT MASK [ Upstream commit 7257d930aadcd62d1c7971ab14f3b1126356abdc ] L3_OUT and L4_OUT Bit fields range from Bit 0:4 and thus the mask should be 0x1F instead of 0x0F. Fixes: 0935ff5f1f0a ("regulator: pca9450: add pca9450 pmic driver") Signed-off-by: Teresa Remmet Reviewed-by: Frieder Schrempf Link: https://lore.kernel.org/r/20230614125240.3946519-1-t.remmet@phytec.de Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- include/linux/regulator/pca9450.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h index 3c01c2bf84f5..505c908dbb81 100644 --- a/include/linux/regulator/pca9450.h +++ b/include/linux/regulator/pca9450.h @@ -196,11 +196,11 @@ enum { /* PCA9450_REG_LDO3_VOLT bits */ #define LDO3_EN_MASK 0xC0 -#define LDO3OUT_MASK 0x0F +#define LDO3OUT_MASK 0x1F /* PCA9450_REG_LDO4_VOLT bits */ #define LDO4_EN_MASK 0xC0 -#define LDO4OUT_MASK 0x0F +#define LDO4OUT_MASK 0x1F /* PCA9450_REG_LDO5_VOLT bits */ #define LDO5L_EN_MASK 0xC0 -- cgit 1.4.1 From 277a7c23b590a16c91f01a0698a607e9e40dfee6 Mon Sep 17 00:00:00 2001 From: Russ Weight Date: Tue, 20 Jun 2023 13:28:24 -0700 Subject: regmap: spi-avmm: Fix regmap_bus max_raw_write [ Upstream commit c8e796895e2310b6130e7577248da1d771431a77 ] The max_raw_write member of the regmap_spi_avmm_bus structure is defined as: .max_raw_write = SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT SPI_AVMM_VAL_SIZE == 4 and MAX_WRITE_CNT == 1 so this results in a maximum write transfer size of 4 bytes which provides only enough space to transfer the address of the target register. It provides no space for the value to be transferred. This bug became an issue (divide-by-zero in _regmap_raw_write()) after the following was accepted into mainline: commit 3981514180c9 ("regmap: Account for register length when chunking") Change max_raw_write to include space (4 additional bytes) for both the register address and value: .max_raw_write = SPI_AVMM_REG_SIZE + SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT Fixes: 7f9fb67358a2 ("regmap: add Intel SPI Slave to AVMM Bus Bridge support") Reviewed-by: Matthew Gerlach Signed-off-by: Russ Weight Link: https://lore.kernel.org/r/20230620202824.380313-1-russell.h.weight@intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/base/regmap/regmap-spi-avmm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-spi-avmm.c b/drivers/base/regmap/regmap-spi-avmm.c index 4c2b94b3e30b..6af692844c19 100644 --- a/drivers/base/regmap/regmap-spi-avmm.c +++ b/drivers/base/regmap/regmap-spi-avmm.c @@ -660,7 +660,7 @@ static const struct regmap_bus regmap_spi_avmm_bus = { .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_NATIVE, .max_raw_read = SPI_AVMM_VAL_SIZE * MAX_READ_CNT, - .max_raw_write = SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT, + .max_raw_write = SPI_AVMM_REG_SIZE + SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT, .free_context = spi_avmm_bridge_ctx_free, }; -- cgit 1.4.1 From 2d80c85fa404ca973e56f1ac98f20d87151e6af1 Mon Sep 17 00:00:00 2001 From: Andrew Powers-Holmes Date: Thu, 1 Jun 2023 15:25:16 +0200 Subject: arm64: dts: rockchip: Fix rk356x PCIe register and range mappings commit 568a67e742dfa90b19a23305317164c5c350b71e upstream. The register and range mappings for the PCIe controller in Rockchip's RK356x SoCs are incorrect. Replace them with corrected values from the vendor BSP sources, updated to match current DT schema. These values are also used in u-boot. Fixes: 66b51ea7d70f ("arm64: dts: rockchip: Add rk3568 PCIe2x1 controller") Cc: stable@vger.kernel.org Signed-off-by: Andrew Powers-Holmes Signed-off-by: Jonas Karlman Signed-off-by: Nicolas Frattaroli Tested-by: Diederik de Haas Link: https://lore.kernel.org/r/20230601132516.153934-1-frattaroli.nicolas@gmail.com Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/rockchip/rk3568.dtsi | 14 ++++++++------ arch/arm64/boot/dts/rockchip/rk356x.dtsi | 7 ++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi index ba67b58f05b7..f1be76a54ceb 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi @@ -94,9 +94,10 @@ power-domains = <&power RK3568_PD_PIPE>; reg = <0x3 0xc0400000 0x0 0x00400000>, <0x0 0xfe270000 0x0 0x00010000>, - <0x3 0x7f000000 0x0 0x01000000>; - ranges = <0x01000000 0x0 0x3ef00000 0x3 0x7ef00000 0x0 0x00100000>, - <0x02000000 0x0 0x00000000 0x3 0x40000000 0x0 0x3ef00000>; + <0x0 0xf2000000 0x0 0x00100000>; + ranges = <0x01000000 0x0 0xf2100000 0x0 0xf2100000 0x0 0x00100000>, + <0x02000000 0x0 0xf2200000 0x0 0xf2200000 0x0 0x01e00000>, + <0x03000000 0x0 0x40000000 0x3 0x40000000 0x0 0x40000000>; reg-names = "dbi", "apb", "config"; resets = <&cru SRST_PCIE30X1_POWERUP>; reset-names = "pipe"; @@ -146,9 +147,10 @@ power-domains = <&power RK3568_PD_PIPE>; reg = <0x3 0xc0800000 0x0 0x00400000>, <0x0 0xfe280000 0x0 0x00010000>, - <0x3 0xbf000000 0x0 0x01000000>; - ranges = <0x01000000 0x0 0x3ef00000 0x3 0xbef00000 0x0 0x00100000>, - <0x02000000 0x0 0x00000000 0x3 0x80000000 0x0 0x3ef00000>; + <0x0 0xf0000000 0x0 0x00100000>; + ranges = <0x01000000 0x0 0xf0100000 0x0 0xf0100000 0x0 0x00100000>, + <0x02000000 0x0 0xf0200000 0x0 0xf0200000 0x0 0x01e00000>, + <0x03000000 0x0 0x40000000 0x3 0x80000000 0x0 0x40000000>; reg-names = "dbi", "apb", "config"; resets = <&cru SRST_PCIE30X2_POWERUP>; reset-names = "pipe"; diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi index 1d423daae971..234b5bbda120 100644 --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi @@ -951,7 +951,7 @@ compatible = "rockchip,rk3568-pcie"; reg = <0x3 0xc0000000 0x0 0x00400000>, <0x0 0xfe260000 0x0 0x00010000>, - <0x3 0x3f000000 0x0 0x01000000>; + <0x0 0xf4000000 0x0 0x00100000>; reg-names = "dbi", "apb", "config"; interrupts = , , @@ -981,8 +981,9 @@ phys = <&combphy2 PHY_TYPE_PCIE>; phy-names = "pcie-phy"; power-domains = <&power RK3568_PD_PIPE>; - ranges = <0x01000000 0x0 0x3ef00000 0x3 0x3ef00000 0x0 0x00100000 - 0x02000000 0x0 0x00000000 0x3 0x00000000 0x0 0x3ef00000>; + ranges = <0x01000000 0x0 0xf4100000 0x0 0xf4100000 0x0 0x00100000>, + <0x02000000 0x0 0xf4200000 0x0 0xf4200000 0x0 0x01e00000>, + <0x03000000 0x0 0x40000000 0x3 0x00000000 0x0 0x40000000>; resets = <&cru SRST_PCIE20_POWERUP>; reset-names = "pipe"; #address-cells = <3>; -- cgit 1.4.1 From 24f473769e7ecf35e2772469a063d5a8bbca6f63 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 17 Jun 2023 19:50:24 -0600 Subject: io_uring/poll: serialize poll linked timer start with poll removal Commit ef7dfac51d8ed961b742218f526bd589f3900a59 upstream. We selectively grab the ctx->uring_lock for poll update/removal, but we really should grab it from the start to fully synchronize with linked timeouts. Normally this is indeed the case, but if requests are forced async by the application, we don't fully cover removal and timer disarm within the uring_lock. Make this simpler by having consistent locking state for poll removal. Cc: stable@vger.kernel.org # 6.1+ Reported-by: Querijn Voet Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/poll.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/io_uring/poll.c b/io_uring/poll.c index 4788073ec45d..869e1d2a4413 100644 --- a/io_uring/poll.c +++ b/io_uring/poll.c @@ -993,8 +993,9 @@ int io_poll_remove(struct io_kiocb *req, unsigned int issue_flags) struct io_hash_bucket *bucket; struct io_kiocb *preq; int ret2, ret = 0; - bool locked; + bool locked = true; + io_ring_submit_lock(ctx, issue_flags); preq = io_poll_find(ctx, true, &cd, &ctx->cancel_table, &bucket); ret2 = io_poll_disarm(preq); if (bucket) @@ -1006,12 +1007,10 @@ int io_poll_remove(struct io_kiocb *req, unsigned int issue_flags) goto out; } - io_ring_submit_lock(ctx, issue_flags); preq = io_poll_find(ctx, true, &cd, &ctx->cancel_table_locked, &bucket); ret2 = io_poll_disarm(preq); if (bucket) spin_unlock(&bucket->lock); - io_ring_submit_unlock(ctx, issue_flags); if (ret2) { ret = ret2; goto out; @@ -1035,7 +1034,7 @@ found: if (poll_update->update_user_data) preq->cqe.user_data = poll_update->new_user_data; - ret2 = io_poll_add(preq, issue_flags); + ret2 = io_poll_add(preq, issue_flags & ~IO_URING_F_UNLOCKED); /* successfully updated, don't complete poll request */ if (!ret2 || ret2 == -EIOCBQUEUED) goto out; @@ -1043,9 +1042,9 @@ found: req_set_fail(preq); io_req_set_res(preq, -ECANCELED, 0); - locked = !(issue_flags & IO_URING_F_UNLOCKED); io_req_task_complete(preq, &locked); out: + io_ring_submit_unlock(ctx, issue_flags); if (ret < 0) { req_set_fail(req); return ret; -- cgit 1.4.1 From cbfee3d9d5c0f7c35f3314667339abe2410b2d90 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 12 Jun 2023 11:14:56 +0900 Subject: nilfs2: prevent general protection fault in nilfs_clear_dirty_page() commit 782e53d0c14420858dbf0f8f797973c150d3b6d7 upstream. In a syzbot stress test that deliberately causes file system errors on nilfs2 with a corrupted disk image, it has been reported that nilfs_clear_dirty_page() called from nilfs_clear_dirty_pages() can cause a general protection fault. In nilfs_clear_dirty_pages(), when looking up dirty pages from the page cache and calling nilfs_clear_dirty_page() for each dirty page/folio retrieved, the back reference from the argument page to "mapping" may have been changed to NULL (and possibly others). It is necessary to check this after locking the page/folio. So, fix this issue by not calling nilfs_clear_dirty_page() on a page/folio after locking it in nilfs_clear_dirty_pages() if the back reference "mapping" from the page/folio is different from the "mapping" that held the page/folio just before. Link: https://lkml.kernel.org/r/20230612021456.3682-1-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi Reported-by: syzbot+53369d11851d8f26735c@syzkaller.appspotmail.com Closes: https://lkml.kernel.org/r/000000000000da4f6b05eb9bf593@google.com Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/page.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 39b7eea2642a..7d31833e68d1 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -369,7 +369,15 @@ void nilfs_clear_dirty_pages(struct address_space *mapping, bool silent) struct page *page = pvec.pages[i]; lock_page(page); - nilfs_clear_dirty_page(page, silent); + + /* + * This page may have been removed from the address + * space by truncation or invalidation when the lock + * was acquired. Skip processing in that case. + */ + if (likely(page->mapping == mapping)) + nilfs_clear_dirty_page(page, silent); + unlock_page(page); } pagevec_release(&pvec); -- cgit 1.4.1 From 364fdcbb035bb910e58a2814708de72481256466 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 14 Jun 2023 17:38:54 +0100 Subject: x86/mm: Avoid using set_pgd() outside of real PGD pages commit d082d48737c75d2b3cc1f972b8c8674c25131534 upstream. KPTI keeps around two PGDs: one for userspace and another for the kernel. Among other things, set_pgd() contains infrastructure to ensure that updates to the kernel PGD are reflected in the user PGD as well. One side-effect of this is that set_pgd() expects to be passed whole pages. Unfortunately, init_trampoline_kaslr() passes in a single entry: 'trampoline_pgd_entry'. When KPTI is on, set_pgd() will update 'trampoline_pgd_entry' (an 8-Byte globally stored [.bss] variable) and will then proceed to replicate that value into the non-existent neighboring user page (located +4k away), leading to the corruption of other global [.bss] stored variables. Fix it by directly assigning 'trampoline_pgd_entry' and avoiding set_pgd(). [ dhansen: tweak subject and changelog ] Fixes: 0925dda5962e ("x86/mm/KASLR: Use only one PUD entry for real mode trampoline") Suggested-by: Dave Hansen Signed-off-by: Lee Jones Signed-off-by: Dave Hansen Cc: Link: https://lore.kernel.org/all/20230614163859.924309-1-lee@kernel.org/g Signed-off-by: Greg Kroah-Hartman --- arch/x86/mm/kaslr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/mm/kaslr.c b/arch/x86/mm/kaslr.c index 557f0fe25dff..37db264866b6 100644 --- a/arch/x86/mm/kaslr.c +++ b/arch/x86/mm/kaslr.c @@ -172,10 +172,10 @@ void __meminit init_trampoline_kaslr(void) set_p4d(p4d_tramp, __p4d(_KERNPG_TABLE | __pa(pud_page_tramp))); - set_pgd(&trampoline_pgd_entry, - __pgd(_KERNPG_TABLE | __pa(p4d_page_tramp))); + trampoline_pgd_entry = + __pgd(_KERNPG_TABLE | __pa(p4d_page_tramp)); } else { - set_pgd(&trampoline_pgd_entry, - __pgd(_KERNPG_TABLE | __pa(pud_page_tramp))); + trampoline_pgd_entry = + __pgd(_KERNPG_TABLE | __pa(pud_page_tramp)); } } -- cgit 1.4.1 From 1a2793a25a6083fa5309d9cebcfd6171a3b2e856 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 7 Jun 2023 15:24:27 +0200 Subject: memfd: check for non-NULL file_seals in memfd_create() syscall [ Upstream commit 935d44acf621aa0688fef8312dec3e5940f38f4e ] Ensure that file_seals is non-NULL before using it in the memfd_create() syscall. One situation in which memfd_file_seals_ptr() could return a NULL pointer when CONFIG_SHMEM=n, oopsing the kernel. Link: https://lkml.kernel.org/r/20230607132427.2867435-1-roberto.sassu@huaweicloud.com Fixes: 47b9012ecdc7 ("shmem: add sealing support to hugetlb-backed memfd") Signed-off-by: Roberto Sassu Cc: Marc-Andr Lureau Cc: Mike Kravetz Cc: Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- mm/memfd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/memfd.c b/mm/memfd.c index 08f5f8304746..b0104b49bf82 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -328,7 +328,8 @@ SYSCALL_DEFINE2(memfd_create, if (flags & MFD_ALLOW_SEALING) { file_seals = memfd_file_seals_ptr(file); - *file_seals &= ~F_SEAL_SEAL; + if (file_seals) + *file_seals &= ~F_SEAL_SEAL; } fd_install(fd, file); -- cgit 1.4.1 From caddcdf2a9993b65314c09bcd0b32fe77d817dd8 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:12 +0300 Subject: mmc: meson-gx: fix deferred probing [ Upstream commit b8ada54fa1b83f3b6480d4cced71354301750153 ] The driver overrides the error codes and IRQ0 returned by platform_get_irq() to -EINVAL, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Since commit ce753ad1549c ("platform: finally disallow IRQ0 in platform_get_irq() and its ilk") IRQ0 is no longer returned by those APIs, so we now can safely ignore it... Fixes: cbcaac6d7dd2 ("mmc: meson-gx-mmc: Fix platform_get_irq's error checking") Cc: stable@vger.kernel.org # v5.19+ Signed-off-by: Sergey Shtylyov Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20230617203622.6812-3-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/meson-gx-mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 5fb18be6f866..0f39f86bd0c2 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1227,8 +1227,8 @@ static int meson_mmc_probe(struct platform_device *pdev) } host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto free_host; } -- cgit 1.4.1 From 43489b2cba5affb602883ae137ebe4c6aa32d331 Mon Sep 17 00:00:00 2001 From: Chen Aotian Date: Sun, 9 Apr 2023 10:20:48 +0800 Subject: ieee802154: hwsim: Fix possible memory leaks [ Upstream commit a61675294735570daca3779bd1dbb3715f7232bd ] After replacing e->info, it is necessary to free the old einfo. Fixes: f25da51fdc38 ("ieee802154: hwsim: add replacement for fakelb") Reviewed-by: Miquel Raynal Reviewed-by: Alexander Aring Signed-off-by: Chen Aotian Link: https://lore.kernel.org/r/20230409022048.61223-1-chenaotian2@163.com Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- drivers/net/ieee802154/mac802154_hwsim.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 2f0544dd7c2a..9b3da61840a8 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -522,7 +522,7 @@ static int hwsim_del_edge_nl(struct sk_buff *msg, struct genl_info *info) static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info) { struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1]; - struct hwsim_edge_info *einfo; + struct hwsim_edge_info *einfo, *einfo_old; struct hwsim_phy *phy_v0; struct hwsim_edge *e; u32 v0, v1; @@ -560,8 +560,10 @@ static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info) list_for_each_entry_rcu(e, &phy_v0->edges, list) { if (e->endpoint->idx == v1) { einfo->lqi = lqi; - rcu_assign_pointer(e->info, einfo); + einfo_old = rcu_replace_pointer(e->info, einfo, + lockdep_is_held(&hwsim_phys_lock)); rcu_read_unlock(); + kfree_rcu(einfo_old, rcu); mutex_unlock(&hwsim_phys_lock); return 0; } -- cgit 1.4.1 From 8ea03341f78ab916c64b26f283ffb12eb6737f2d Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 10 May 2023 01:30:21 +0000 Subject: xfrm: Treat already-verified secpath entries as optional [ Upstream commit 1f8b6df6a997a430b0c48b504638154b520781ad ] This change allows inbound traffic through nested IPsec tunnels to successfully match policies and templates, while retaining the secpath stack trace as necessary for netfilter policies. Specifically, this patch marks secpath entries that have already matched against a relevant policy as having been verified, allowing it to be treated as optional and skipped after a tunnel decapsulation (during which the src/dst/proto/etc may have changed, and the correct policy chain no long be resolvable). This approach is taken as opposed to the iteration in b0355dbbf13c, where the secpath was cleared, since that breaks subsequent validations that rely on the existence of the secpath entries (netfilter policies, or transport-in-tunnel mode, where policies remain resolvable). Fixes: b0355dbbf13c ("Fix XFRM-I support for nested ESP tunnels") Test: Tested against Android Kernel Unit Tests Test: Tested against Android CTS Signed-off-by: Benedict Wong Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- include/net/xfrm.h | 1 + net/xfrm/xfrm_input.c | 1 + net/xfrm/xfrm_policy.c | 12 ++++++++++++ 3 files changed, 14 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index dbc81f5eb553..9ec6f2e92ad3 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1039,6 +1039,7 @@ struct xfrm_offload { struct sec_path { int len; int olen; + int verified_cnt; struct xfrm_state *xvec[XFRM_MAX_DEPTH]; struct xfrm_offload ovec[XFRM_MAX_OFFLOAD_DEPTH]; diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 2defd89da700..ac1a645afa8d 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -131,6 +131,7 @@ struct sec_path *secpath_set(struct sk_buff *skb) memset(sp->ovec, 0, sizeof(sp->ovec)); sp->olen = 0; sp->len = 0; + sp->verified_cnt = 0; return sp; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index e894c269affb..7b1b93584bdb 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3274,6 +3274,13 @@ xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int star if (xfrm_state_ok(tmpl, sp->xvec[idx], family, if_id)) return ++idx; if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) { + if (idx < sp->verified_cnt) { + /* Secpath entry previously verified, consider optional and + * continue searching + */ + continue; + } + if (start == -1) start = -2-idx; break; @@ -3648,6 +3655,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, * Order is _important_. Later we will implement * some barriers, but at the moment barriers * are implied between each two transformations. + * Upon success, marks secpath entries as having been + * verified to allow them to be skipped in future policy + * checks (e.g. nested tunnels). */ for (i = xfrm_nr-1, k = 0; i >= 0; i--) { k = xfrm_policy_ok(tpp[i], sp, k, family, if_id); @@ -3666,6 +3676,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } xfrm_pols_put(pols, npols); + sp->verified_cnt = k; + return 1; } XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK); -- cgit 1.4.1 From 94e81817f080311029bb563959ab2e34b3afaa10 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sat, 3 Dec 2022 10:46:56 +0200 Subject: xfrm: interface: rename xfrm_interface.c to xfrm_interface_core.c [ Upstream commit ee9a113ab63468137802898bcd2c598998c96938 ] This change allows adding additional files to the xfrm_interface module. Signed-off-by: Eyal Birger Link: https://lore.kernel.org/r/20221203084659.1837829-2-eyal.birger@gmail.com Signed-off-by: Martin KaFai Lau Stable-dep-of: a287f5b0cfc6 ("xfrm: Ensure policies always checked on XFRM-I input path") Signed-off-by: Sasha Levin --- net/xfrm/Makefile | 2 + net/xfrm/xfrm_interface.c | 1198 ---------------------------------------- net/xfrm/xfrm_interface_core.c | 1198 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1200 insertions(+), 1198 deletions(-) delete mode 100644 net/xfrm/xfrm_interface.c create mode 100644 net/xfrm/xfrm_interface_core.c diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index 494aa744bfb9..08a2870fdd36 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -3,6 +3,8 @@ # Makefile for the XFRM subsystem. # +xfrm_interface-$(CONFIG_XFRM_INTERFACE) += xfrm_interface_core.o + obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ xfrm_input.o xfrm_output.o \ xfrm_sysctl.o xfrm_replay.o xfrm_device.o diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c deleted file mode 100644 index 5a67b120c4db..000000000000 --- a/net/xfrm/xfrm_interface.c +++ /dev/null @@ -1,1198 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * XFRM virtual interface - * - * Copyright (C) 2018 secunet Security Networks AG - * - * Author: - * Steffen Klassert - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int xfrmi_dev_init(struct net_device *dev); -static void xfrmi_dev_setup(struct net_device *dev); -static struct rtnl_link_ops xfrmi_link_ops __read_mostly; -static unsigned int xfrmi_net_id __read_mostly; -static const struct net_device_ops xfrmi_netdev_ops; - -#define XFRMI_HASH_BITS 8 -#define XFRMI_HASH_SIZE BIT(XFRMI_HASH_BITS) - -struct xfrmi_net { - /* lists for storing interfaces in use */ - struct xfrm_if __rcu *xfrmi[XFRMI_HASH_SIZE]; - struct xfrm_if __rcu *collect_md_xfrmi; -}; - -static const struct nla_policy xfrm_lwt_policy[LWT_XFRM_MAX + 1] = { - [LWT_XFRM_IF_ID] = NLA_POLICY_MIN(NLA_U32, 1), - [LWT_XFRM_LINK] = NLA_POLICY_MIN(NLA_U32, 1), -}; - -static void xfrmi_destroy_state(struct lwtunnel_state *lwt) -{ -} - -static int xfrmi_build_state(struct net *net, struct nlattr *nla, - unsigned int family, const void *cfg, - struct lwtunnel_state **ts, - struct netlink_ext_ack *extack) -{ - struct nlattr *tb[LWT_XFRM_MAX + 1]; - struct lwtunnel_state *new_state; - struct xfrm_md_info *info; - int ret; - - ret = nla_parse_nested(tb, LWT_XFRM_MAX, nla, xfrm_lwt_policy, extack); - if (ret < 0) - return ret; - - if (!tb[LWT_XFRM_IF_ID]) { - NL_SET_ERR_MSG(extack, "if_id must be set"); - return -EINVAL; - } - - new_state = lwtunnel_state_alloc(sizeof(*info)); - if (!new_state) { - NL_SET_ERR_MSG(extack, "failed to create encap info"); - return -ENOMEM; - } - - new_state->type = LWTUNNEL_ENCAP_XFRM; - - info = lwt_xfrm_info(new_state); - - info->if_id = nla_get_u32(tb[LWT_XFRM_IF_ID]); - - if (tb[LWT_XFRM_LINK]) - info->link = nla_get_u32(tb[LWT_XFRM_LINK]); - - *ts = new_state; - return 0; -} - -static int xfrmi_fill_encap_info(struct sk_buff *skb, - struct lwtunnel_state *lwt) -{ - struct xfrm_md_info *info = lwt_xfrm_info(lwt); - - if (nla_put_u32(skb, LWT_XFRM_IF_ID, info->if_id) || - (info->link && nla_put_u32(skb, LWT_XFRM_LINK, info->link))) - return -EMSGSIZE; - - return 0; -} - -static int xfrmi_encap_nlsize(struct lwtunnel_state *lwtstate) -{ - return nla_total_size(sizeof(u32)) + /* LWT_XFRM_IF_ID */ - nla_total_size(sizeof(u32)); /* LWT_XFRM_LINK */ -} - -static int xfrmi_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) -{ - struct xfrm_md_info *a_info = lwt_xfrm_info(a); - struct xfrm_md_info *b_info = lwt_xfrm_info(b); - - return memcmp(a_info, b_info, sizeof(*a_info)); -} - -static const struct lwtunnel_encap_ops xfrmi_encap_ops = { - .build_state = xfrmi_build_state, - .destroy_state = xfrmi_destroy_state, - .fill_encap = xfrmi_fill_encap_info, - .get_encap_size = xfrmi_encap_nlsize, - .cmp_encap = xfrmi_encap_cmp, - .owner = THIS_MODULE, -}; - -#define for_each_xfrmi_rcu(start, xi) \ - for (xi = rcu_dereference(start); xi; xi = rcu_dereference(xi->next)) - -static u32 xfrmi_hash(u32 if_id) -{ - return hash_32(if_id, XFRMI_HASH_BITS); -} - -static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x) -{ - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - struct xfrm_if *xi; - - for_each_xfrmi_rcu(xfrmn->xfrmi[xfrmi_hash(x->if_id)], xi) { - if (x->if_id == xi->p.if_id && - (xi->dev->flags & IFF_UP)) - return xi; - } - - xi = rcu_dereference(xfrmn->collect_md_xfrmi); - if (xi && (xi->dev->flags & IFF_UP)) - return xi; - - return NULL; -} - -static bool xfrmi_decode_session(struct sk_buff *skb, - unsigned short family, - struct xfrm_if_decode_session_result *res) -{ - struct net_device *dev; - struct xfrm_if *xi; - int ifindex = 0; - - if (!secpath_exists(skb) || !skb->dev) - return false; - - switch (family) { - case AF_INET6: - ifindex = inet6_sdif(skb); - break; - case AF_INET: - ifindex = inet_sdif(skb); - break; - } - - if (ifindex) { - struct net *net = xs_net(xfrm_input_state(skb)); - - dev = dev_get_by_index_rcu(net, ifindex); - } else { - dev = skb->dev; - } - - if (!dev || !(dev->flags & IFF_UP)) - return false; - if (dev->netdev_ops != &xfrmi_netdev_ops) - return false; - - xi = netdev_priv(dev); - res->net = xi->net; - - if (xi->p.collect_md) - res->if_id = xfrm_input_state(skb)->if_id; - else - res->if_id = xi->p.if_id; - return true; -} - -static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi) -{ - struct xfrm_if __rcu **xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; - - rcu_assign_pointer(xi->next , rtnl_dereference(*xip)); - rcu_assign_pointer(*xip, xi); -} - -static void xfrmi_unlink(struct xfrmi_net *xfrmn, struct xfrm_if *xi) -{ - struct xfrm_if __rcu **xip; - struct xfrm_if *iter; - - for (xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; - (iter = rtnl_dereference(*xip)) != NULL; - xip = &iter->next) { - if (xi == iter) { - rcu_assign_pointer(*xip, xi->next); - break; - } - } -} - -static void xfrmi_dev_free(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - - gro_cells_destroy(&xi->gro_cells); - free_percpu(dev->tstats); -} - -static int xfrmi_create(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net *net = dev_net(dev); - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - int err; - - dev->rtnl_link_ops = &xfrmi_link_ops; - err = register_netdevice(dev); - if (err < 0) - goto out; - - if (xi->p.collect_md) - rcu_assign_pointer(xfrmn->collect_md_xfrmi, xi); - else - xfrmi_link(xfrmn, xi); - - return 0; - -out: - return err; -} - -static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p) -{ - struct xfrm_if __rcu **xip; - struct xfrm_if *xi; - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - - for (xip = &xfrmn->xfrmi[xfrmi_hash(p->if_id)]; - (xi = rtnl_dereference(*xip)) != NULL; - xip = &xi->next) - if (xi->p.if_id == p->if_id) - return xi; - - return NULL; -} - -static void xfrmi_dev_uninit(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id); - - if (xi->p.collect_md) - RCU_INIT_POINTER(xfrmn->collect_md_xfrmi, NULL); - else - xfrmi_unlink(xfrmn, xi); -} - -static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) -{ - skb_clear_tstamp(skb); - skb->pkt_type = PACKET_HOST; - skb->skb_iif = 0; - skb->ignore_df = 0; - skb_dst_drop(skb); - nf_reset_ct(skb); - nf_reset_trace(skb); - - if (!xnet) - return; - - ipvs_reset(skb); - secpath_reset(skb); - skb_orphan(skb); - skb->mark = 0; -} - -static int xfrmi_rcv_cb(struct sk_buff *skb, int err) -{ - const struct xfrm_mode *inner_mode; - struct net_device *dev; - struct xfrm_state *x; - struct xfrm_if *xi; - bool xnet; - int link; - - if (err && !secpath_exists(skb)) - return 0; - - x = xfrm_input_state(skb); - - xi = xfrmi_lookup(xs_net(x), x); - if (!xi) - return 1; - - link = skb->dev->ifindex; - dev = xi->dev; - skb->dev = dev; - - if (err) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; - - return 0; - } - - xnet = !net_eq(xi->net, dev_net(skb->dev)); - - if (xnet) { - inner_mode = &x->inner_mode; - - if (x->sel.family == AF_UNSPEC) { - inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); - if (inner_mode == NULL) { - XFRM_INC_STATS(dev_net(skb->dev), - LINUX_MIB_XFRMINSTATEMODEERROR); - return -EINVAL; - } - } - - if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, - inner_mode->family)) - return -EPERM; - } - - xfrmi_scrub_packet(skb, xnet); - if (xi->p.collect_md) { - struct metadata_dst *md_dst; - - md_dst = metadata_dst_alloc(0, METADATA_XFRM, GFP_ATOMIC); - if (!md_dst) - return -ENOMEM; - - md_dst->u.xfrm_info.if_id = x->if_id; - md_dst->u.xfrm_info.link = link; - skb_dst_set(skb, (struct dst_entry *)md_dst); - } - dev_sw_netstats_rx_add(dev, skb->len); - - return 0; -} - -static int -xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net_device_stats *stats = &xi->dev->stats; - struct dst_entry *dst = skb_dst(skb); - unsigned int length = skb->len; - struct net_device *tdev; - struct xfrm_state *x; - int err = -1; - u32 if_id; - int mtu; - - if (xi->p.collect_md) { - struct xfrm_md_info *md_info = skb_xfrm_md_info(skb); - - if (unlikely(!md_info)) - return -EINVAL; - - if_id = md_info->if_id; - fl->flowi_oif = md_info->link; - } else { - if_id = xi->p.if_id; - } - - dst_hold(dst); - dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, if_id); - if (IS_ERR(dst)) { - err = PTR_ERR(dst); - dst = NULL; - goto tx_err_link_failure; - } - - x = dst->xfrm; - if (!x) - goto tx_err_link_failure; - - if (x->if_id != if_id) - goto tx_err_link_failure; - - tdev = dst->dev; - - if (tdev == dev) { - stats->collisions++; - net_warn_ratelimited("%s: Local routing loop detected!\n", - dev->name); - goto tx_err_dst_release; - } - - mtu = dst_mtu(dst); - if ((!skb_is_gso(skb) && skb->len > mtu) || - (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) { - skb_dst_update_pmtu_no_confirm(skb, mtu); - - if (skb->protocol == htons(ETH_P_IPV6)) { - if (mtu < IPV6_MIN_MTU) - mtu = IPV6_MIN_MTU; - - if (skb->len > 1280) - icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); - else - goto xmit; - } else { - if (!(ip_hdr(skb)->frag_off & htons(IP_DF))) - goto xmit; - icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, - htonl(mtu)); - } - - dst_release(dst); - return -EMSGSIZE; - } - -xmit: - xfrmi_scrub_packet(skb, !net_eq(xi->net, dev_net(dev))); - skb_dst_set(skb, dst); - skb->dev = tdev; - - err = dst_output(xi->net, skb->sk, skb); - if (net_xmit_eval(err) == 0) { - dev_sw_netstats_tx_add(dev, 1, length); - } else { - stats->tx_errors++; - stats->tx_aborted_errors++; - } - - return 0; -tx_err_link_failure: - stats->tx_carrier_errors++; - dst_link_failure(skb); -tx_err_dst_release: - dst_release(dst); - return err; -} - -static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net_device_stats *stats = &xi->dev->stats; - struct dst_entry *dst = skb_dst(skb); - struct flowi fl; - int ret; - - memset(&fl, 0, sizeof(fl)); - - switch (skb->protocol) { - case htons(ETH_P_IPV6): - xfrm_decode_session(skb, &fl, AF_INET6); - memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); - if (!dst) { - fl.u.ip6.flowi6_oif = dev->ifindex; - fl.u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC; - dst = ip6_route_output(dev_net(dev), NULL, &fl.u.ip6); - if (dst->error) { - dst_release(dst); - stats->tx_carrier_errors++; - goto tx_err; - } - skb_dst_set(skb, dst); - } - break; - case htons(ETH_P_IP): - xfrm_decode_session(skb, &fl, AF_INET); - memset(IPCB(skb), 0, sizeof(*IPCB(skb))); - if (!dst) { - struct rtable *rt; - - fl.u.ip4.flowi4_oif = dev->ifindex; - fl.u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; - rt = __ip_route_output_key(dev_net(dev), &fl.u.ip4); - if (IS_ERR(rt)) { - stats->tx_carrier_errors++; - goto tx_err; - } - skb_dst_set(skb, &rt->dst); - } - break; - default: - goto tx_err; - } - - fl.flowi_oif = xi->p.link; - - ret = xfrmi_xmit2(skb, dev, &fl); - if (ret < 0) - goto tx_err; - - return NETDEV_TX_OK; - -tx_err: - stats->tx_errors++; - stats->tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; -} - -static int xfrmi4_err(struct sk_buff *skb, u32 info) -{ - const struct iphdr *iph = (const struct iphdr *)skb->data; - struct net *net = dev_net(skb->dev); - int protocol = iph->protocol; - struct ip_comp_hdr *ipch; - struct ip_esp_hdr *esph; - struct ip_auth_hdr *ah ; - struct xfrm_state *x; - struct xfrm_if *xi; - __be32 spi; - - switch (protocol) { - case IPPROTO_ESP: - esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); - spi = esph->spi; - break; - case IPPROTO_AH: - ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2)); - spi = ah->spi; - break; - case IPPROTO_COMP: - ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); - spi = htonl(ntohs(ipch->cpi)); - break; - default: - return 0; - } - - switch (icmp_hdr(skb)->type) { - case ICMP_DEST_UNREACH: - if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) - return 0; - break; - case ICMP_REDIRECT: - break; - default: - return 0; - } - - x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, - spi, protocol, AF_INET); - if (!x) - return 0; - - xi = xfrmi_lookup(net, x); - if (!xi) { - xfrm_state_put(x); - return -1; - } - - if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) - ipv4_update_pmtu(skb, net, info, 0, protocol); - else - ipv4_redirect(skb, net, 0, protocol); - xfrm_state_put(x); - - return 0; -} - -static int xfrmi6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - u8 type, u8 code, int offset, __be32 info) -{ - const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; - struct net *net = dev_net(skb->dev); - int protocol = iph->nexthdr; - struct ip_comp_hdr *ipch; - struct ip_esp_hdr *esph; - struct ip_auth_hdr *ah; - struct xfrm_state *x; - struct xfrm_if *xi; - __be32 spi; - - switch (protocol) { - case IPPROTO_ESP: - esph = (struct ip_esp_hdr *)(skb->data + offset); - spi = esph->spi; - break; - case IPPROTO_AH: - ah = (struct ip_auth_hdr *)(skb->data + offset); - spi = ah->spi; - break; - case IPPROTO_COMP: - ipch = (struct ip_comp_hdr *)(skb->data + offset); - spi = htonl(ntohs(ipch->cpi)); - break; - default: - return 0; - } - - if (type != ICMPV6_PKT_TOOBIG && - type != NDISC_REDIRECT) - return 0; - - x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, - spi, protocol, AF_INET6); - if (!x) - return 0; - - xi = xfrmi_lookup(net, x); - if (!xi) { - xfrm_state_put(x); - return -1; - } - - if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0, - sock_net_uid(net, NULL)); - else - ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); - xfrm_state_put(x); - - return 0; -} - -static int xfrmi_change(struct xfrm_if *xi, const struct xfrm_if_parms *p) -{ - if (xi->p.link != p->link) - return -EINVAL; - - xi->p.if_id = p->if_id; - - return 0; -} - -static int xfrmi_update(struct xfrm_if *xi, struct xfrm_if_parms *p) -{ - struct net *net = xi->net; - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - int err; - - xfrmi_unlink(xfrmn, xi); - synchronize_net(); - err = xfrmi_change(xi, p); - xfrmi_link(xfrmn, xi); - netdev_state_change(xi->dev); - return err; -} - -static int xfrmi_get_iflink(const struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - - return xi->p.link; -} - -static const struct net_device_ops xfrmi_netdev_ops = { - .ndo_init = xfrmi_dev_init, - .ndo_uninit = xfrmi_dev_uninit, - .ndo_start_xmit = xfrmi_xmit, - .ndo_get_stats64 = dev_get_tstats64, - .ndo_get_iflink = xfrmi_get_iflink, -}; - -static void xfrmi_dev_setup(struct net_device *dev) -{ - dev->netdev_ops = &xfrmi_netdev_ops; - dev->header_ops = &ip_tunnel_header_ops; - dev->type = ARPHRD_NONE; - dev->mtu = ETH_DATA_LEN; - dev->min_mtu = ETH_MIN_MTU; - dev->max_mtu = IP_MAX_MTU; - dev->flags = IFF_NOARP; - dev->needs_free_netdev = true; - dev->priv_destructor = xfrmi_dev_free; - netif_keep_dst(dev); - - eth_broadcast_addr(dev->broadcast); -} - -#define XFRMI_FEATURES (NETIF_F_SG | \ - NETIF_F_FRAGLIST | \ - NETIF_F_GSO_SOFTWARE | \ - NETIF_F_HW_CSUM) - -static int xfrmi_dev_init(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net_device *phydev = __dev_get_by_index(xi->net, xi->p.link); - int err; - - dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); - if (!dev->tstats) - return -ENOMEM; - - err = gro_cells_init(&xi->gro_cells, dev); - if (err) { - free_percpu(dev->tstats); - return err; - } - - dev->features |= NETIF_F_LLTX; - dev->features |= XFRMI_FEATURES; - dev->hw_features |= XFRMI_FEATURES; - - if (phydev) { - dev->needed_headroom = phydev->needed_headroom; - dev->needed_tailroom = phydev->needed_tailroom; - - if (is_zero_ether_addr(dev->dev_addr)) - eth_hw_addr_inherit(dev, phydev); - if (is_zero_ether_addr(dev->broadcast)) - memcpy(dev->broadcast, phydev->broadcast, - dev->addr_len); - } else { - eth_hw_addr_random(dev); - eth_broadcast_addr(dev->broadcast); - } - - return 0; -} - -static int xfrmi_validate(struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - return 0; -} - -static void xfrmi_netlink_parms(struct nlattr *data[], - struct xfrm_if_parms *parms) -{ - memset(parms, 0, sizeof(*parms)); - - if (!data) - return; - - if (data[IFLA_XFRM_LINK]) - parms->link = nla_get_u32(data[IFLA_XFRM_LINK]); - - if (data[IFLA_XFRM_IF_ID]) - parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]); - - if (data[IFLA_XFRM_COLLECT_METADATA]) - parms->collect_md = true; -} - -static int xfrmi_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct net *net = dev_net(dev); - struct xfrm_if_parms p = {}; - struct xfrm_if *xi; - int err; - - xfrmi_netlink_parms(data, &p); - if (p.collect_md) { - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - - if (p.link || p.if_id) { - NL_SET_ERR_MSG(extack, "link and if_id must be zero"); - return -EINVAL; - } - - if (rtnl_dereference(xfrmn->collect_md_xfrmi)) - return -EEXIST; - - } else { - if (!p.if_id) { - NL_SET_ERR_MSG(extack, "if_id must be non zero"); - return -EINVAL; - } - - xi = xfrmi_locate(net, &p); - if (xi) - return -EEXIST; - } - - xi = netdev_priv(dev); - xi->p = p; - xi->net = net; - xi->dev = dev; - - err = xfrmi_create(dev); - return err; -} - -static void xfrmi_dellink(struct net_device *dev, struct list_head *head) -{ - unregister_netdevice_queue(dev, head); -} - -static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[], - struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net *net = xi->net; - struct xfrm_if_parms p = {}; - - xfrmi_netlink_parms(data, &p); - if (!p.if_id) { - NL_SET_ERR_MSG(extack, "if_id must be non zero"); - return -EINVAL; - } - - if (p.collect_md) { - NL_SET_ERR_MSG(extack, "collect_md can't be changed"); - return -EINVAL; - } - - xi = xfrmi_locate(net, &p); - if (!xi) { - xi = netdev_priv(dev); - } else { - if (xi->dev != dev) - return -EEXIST; - if (xi->p.collect_md) { - NL_SET_ERR_MSG(extack, - "device can't be changed to collect_md"); - return -EINVAL; - } - } - - return xfrmi_update(xi, &p); -} - -static size_t xfrmi_get_size(const struct net_device *dev) -{ - return - /* IFLA_XFRM_LINK */ - nla_total_size(4) + - /* IFLA_XFRM_IF_ID */ - nla_total_size(4) + - /* IFLA_XFRM_COLLECT_METADATA */ - nla_total_size(0) + - 0; -} - -static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct xfrm_if_parms *parm = &xi->p; - - if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) || - nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id) || - (xi->p.collect_md && nla_put_flag(skb, IFLA_XFRM_COLLECT_METADATA))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -EMSGSIZE; -} - -static struct net *xfrmi_get_link_net(const struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - - return xi->net; -} - -static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { - [IFLA_XFRM_UNSPEC] = { .strict_start_type = IFLA_XFRM_COLLECT_METADATA }, - [IFLA_XFRM_LINK] = { .type = NLA_U32 }, - [IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, - [IFLA_XFRM_COLLECT_METADATA] = { .type = NLA_FLAG }, -}; - -static struct rtnl_link_ops xfrmi_link_ops __read_mostly = { - .kind = "xfrm", - .maxtype = IFLA_XFRM_MAX, - .policy = xfrmi_policy, - .priv_size = sizeof(struct xfrm_if), - .setup = xfrmi_dev_setup, - .validate = xfrmi_validate, - .newlink = xfrmi_newlink, - .dellink = xfrmi_dellink, - .changelink = xfrmi_changelink, - .get_size = xfrmi_get_size, - .fill_info = xfrmi_fill_info, - .get_link_net = xfrmi_get_link_net, -}; - -static void __net_exit xfrmi_exit_batch_net(struct list_head *net_exit_list) -{ - struct net *net; - LIST_HEAD(list); - - rtnl_lock(); - list_for_each_entry(net, net_exit_list, exit_list) { - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - struct xfrm_if __rcu **xip; - struct xfrm_if *xi; - int i; - - for (i = 0; i < XFRMI_HASH_SIZE; i++) { - for (xip = &xfrmn->xfrmi[i]; - (xi = rtnl_dereference(*xip)) != NULL; - xip = &xi->next) - unregister_netdevice_queue(xi->dev, &list); - } - xi = rtnl_dereference(xfrmn->collect_md_xfrmi); - if (xi) - unregister_netdevice_queue(xi->dev, &list); - } - unregister_netdevice_many(&list); - rtnl_unlock(); -} - -static struct pernet_operations xfrmi_net_ops = { - .exit_batch = xfrmi_exit_batch_net, - .id = &xfrmi_net_id, - .size = sizeof(struct xfrmi_net), -}; - -static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = { - .handler = xfrm6_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 10, -}; - -static struct xfrm6_protocol xfrmi_ah6_protocol __read_mostly = { - .handler = xfrm6_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 10, -}; - -static struct xfrm6_protocol xfrmi_ipcomp6_protocol __read_mostly = { - .handler = xfrm6_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 10, -}; - -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) -static int xfrmi6_rcv_tunnel(struct sk_buff *skb) -{ - const xfrm_address_t *saddr; - __be32 spi; - - saddr = (const xfrm_address_t *)&ipv6_hdr(skb)->saddr; - spi = xfrm6_tunnel_spi_lookup(dev_net(skb->dev), saddr); - - return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi, NULL); -} - -static struct xfrm6_tunnel xfrmi_ipv6_handler __read_mostly = { - .handler = xfrmi6_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 2, -}; - -static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = { - .handler = xfrmi6_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 2, -}; -#endif - -static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = { - .handler = xfrm4_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 10, -}; - -static struct xfrm4_protocol xfrmi_ah4_protocol __read_mostly = { - .handler = xfrm4_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 10, -}; - -static struct xfrm4_protocol xfrmi_ipcomp4_protocol __read_mostly = { - .handler = xfrm4_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 10, -}; - -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) -static int xfrmi4_rcv_tunnel(struct sk_buff *skb) -{ - return xfrm4_rcv_spi(skb, IPPROTO_IPIP, ip_hdr(skb)->saddr); -} - -static struct xfrm_tunnel xfrmi_ipip_handler __read_mostly = { - .handler = xfrmi4_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 3, -}; - -static struct xfrm_tunnel xfrmi_ipip6_handler __read_mostly = { - .handler = xfrmi4_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 2, -}; -#endif - -static int __init xfrmi4_init(void) -{ - int err; - - err = xfrm4_protocol_register(&xfrmi_esp4_protocol, IPPROTO_ESP); - if (err < 0) - goto xfrm_proto_esp_failed; - err = xfrm4_protocol_register(&xfrmi_ah4_protocol, IPPROTO_AH); - if (err < 0) - goto xfrm_proto_ah_failed; - err = xfrm4_protocol_register(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); - if (err < 0) - goto xfrm_proto_comp_failed; -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) - err = xfrm4_tunnel_register(&xfrmi_ipip_handler, AF_INET); - if (err < 0) - goto xfrm_tunnel_ipip_failed; - err = xfrm4_tunnel_register(&xfrmi_ipip6_handler, AF_INET6); - if (err < 0) - goto xfrm_tunnel_ipip6_failed; -#endif - - return 0; - -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) -xfrm_tunnel_ipip6_failed: - xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); -xfrm_tunnel_ipip_failed: - xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); -#endif -xfrm_proto_comp_failed: - xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); -xfrm_proto_ah_failed: - xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); -xfrm_proto_esp_failed: - return err; -} - -static void xfrmi4_fini(void) -{ -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) - xfrm4_tunnel_deregister(&xfrmi_ipip6_handler, AF_INET6); - xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); -#endif - xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); - xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); - xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); -} - -static int __init xfrmi6_init(void) -{ - int err; - - err = xfrm6_protocol_register(&xfrmi_esp6_protocol, IPPROTO_ESP); - if (err < 0) - goto xfrm_proto_esp_failed; - err = xfrm6_protocol_register(&xfrmi_ah6_protocol, IPPROTO_AH); - if (err < 0) - goto xfrm_proto_ah_failed; - err = xfrm6_protocol_register(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); - if (err < 0) - goto xfrm_proto_comp_failed; -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) - err = xfrm6_tunnel_register(&xfrmi_ipv6_handler, AF_INET6); - if (err < 0) - goto xfrm_tunnel_ipv6_failed; - err = xfrm6_tunnel_register(&xfrmi_ip6ip_handler, AF_INET); - if (err < 0) - goto xfrm_tunnel_ip6ip_failed; -#endif - - return 0; - -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) -xfrm_tunnel_ip6ip_failed: - xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); -xfrm_tunnel_ipv6_failed: - xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); -#endif -xfrm_proto_comp_failed: - xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); -xfrm_proto_ah_failed: - xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); -xfrm_proto_esp_failed: - return err; -} - -static void xfrmi6_fini(void) -{ -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) - xfrm6_tunnel_deregister(&xfrmi_ip6ip_handler, AF_INET); - xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); -#endif - xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); - xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); - xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); -} - -static const struct xfrm_if_cb xfrm_if_cb = { - .decode_session = xfrmi_decode_session, -}; - -static int __init xfrmi_init(void) -{ - const char *msg; - int err; - - pr_info("IPsec XFRM device driver\n"); - - msg = "tunnel device"; - err = register_pernet_device(&xfrmi_net_ops); - if (err < 0) - goto pernet_dev_failed; - - msg = "xfrm4 protocols"; - err = xfrmi4_init(); - if (err < 0) - goto xfrmi4_failed; - - msg = "xfrm6 protocols"; - err = xfrmi6_init(); - if (err < 0) - goto xfrmi6_failed; - - - msg = "netlink interface"; - err = rtnl_link_register(&xfrmi_link_ops); - if (err < 0) - goto rtnl_link_failed; - - lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); - - xfrm_if_register_cb(&xfrm_if_cb); - - return err; - -rtnl_link_failed: - xfrmi6_fini(); -xfrmi6_failed: - xfrmi4_fini(); -xfrmi4_failed: - unregister_pernet_device(&xfrmi_net_ops); -pernet_dev_failed: - pr_err("xfrmi init: failed to register %s\n", msg); - return err; -} - -static void __exit xfrmi_fini(void) -{ - xfrm_if_unregister_cb(); - lwtunnel_encap_del_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); - rtnl_link_unregister(&xfrmi_link_ops); - xfrmi4_fini(); - xfrmi6_fini(); - unregister_pernet_device(&xfrmi_net_ops); -} - -module_init(xfrmi_init); -module_exit(xfrmi_fini); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_RTNL_LINK("xfrm"); -MODULE_ALIAS_NETDEV("xfrm0"); -MODULE_AUTHOR("Steffen Klassert"); -MODULE_DESCRIPTION("XFRM virtual interface"); diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c new file mode 100644 index 000000000000..5a67b120c4db --- /dev/null +++ b/net/xfrm/xfrm_interface_core.c @@ -0,0 +1,1198 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * XFRM virtual interface + * + * Copyright (C) 2018 secunet Security Networks AG + * + * Author: + * Steffen Klassert + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int xfrmi_dev_init(struct net_device *dev); +static void xfrmi_dev_setup(struct net_device *dev); +static struct rtnl_link_ops xfrmi_link_ops __read_mostly; +static unsigned int xfrmi_net_id __read_mostly; +static const struct net_device_ops xfrmi_netdev_ops; + +#define XFRMI_HASH_BITS 8 +#define XFRMI_HASH_SIZE BIT(XFRMI_HASH_BITS) + +struct xfrmi_net { + /* lists for storing interfaces in use */ + struct xfrm_if __rcu *xfrmi[XFRMI_HASH_SIZE]; + struct xfrm_if __rcu *collect_md_xfrmi; +}; + +static const struct nla_policy xfrm_lwt_policy[LWT_XFRM_MAX + 1] = { + [LWT_XFRM_IF_ID] = NLA_POLICY_MIN(NLA_U32, 1), + [LWT_XFRM_LINK] = NLA_POLICY_MIN(NLA_U32, 1), +}; + +static void xfrmi_destroy_state(struct lwtunnel_state *lwt) +{ +} + +static int xfrmi_build_state(struct net *net, struct nlattr *nla, + unsigned int family, const void *cfg, + struct lwtunnel_state **ts, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[LWT_XFRM_MAX + 1]; + struct lwtunnel_state *new_state; + struct xfrm_md_info *info; + int ret; + + ret = nla_parse_nested(tb, LWT_XFRM_MAX, nla, xfrm_lwt_policy, extack); + if (ret < 0) + return ret; + + if (!tb[LWT_XFRM_IF_ID]) { + NL_SET_ERR_MSG(extack, "if_id must be set"); + return -EINVAL; + } + + new_state = lwtunnel_state_alloc(sizeof(*info)); + if (!new_state) { + NL_SET_ERR_MSG(extack, "failed to create encap info"); + return -ENOMEM; + } + + new_state->type = LWTUNNEL_ENCAP_XFRM; + + info = lwt_xfrm_info(new_state); + + info->if_id = nla_get_u32(tb[LWT_XFRM_IF_ID]); + + if (tb[LWT_XFRM_LINK]) + info->link = nla_get_u32(tb[LWT_XFRM_LINK]); + + *ts = new_state; + return 0; +} + +static int xfrmi_fill_encap_info(struct sk_buff *skb, + struct lwtunnel_state *lwt) +{ + struct xfrm_md_info *info = lwt_xfrm_info(lwt); + + if (nla_put_u32(skb, LWT_XFRM_IF_ID, info->if_id) || + (info->link && nla_put_u32(skb, LWT_XFRM_LINK, info->link))) + return -EMSGSIZE; + + return 0; +} + +static int xfrmi_encap_nlsize(struct lwtunnel_state *lwtstate) +{ + return nla_total_size(sizeof(u32)) + /* LWT_XFRM_IF_ID */ + nla_total_size(sizeof(u32)); /* LWT_XFRM_LINK */ +} + +static int xfrmi_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) +{ + struct xfrm_md_info *a_info = lwt_xfrm_info(a); + struct xfrm_md_info *b_info = lwt_xfrm_info(b); + + return memcmp(a_info, b_info, sizeof(*a_info)); +} + +static const struct lwtunnel_encap_ops xfrmi_encap_ops = { + .build_state = xfrmi_build_state, + .destroy_state = xfrmi_destroy_state, + .fill_encap = xfrmi_fill_encap_info, + .get_encap_size = xfrmi_encap_nlsize, + .cmp_encap = xfrmi_encap_cmp, + .owner = THIS_MODULE, +}; + +#define for_each_xfrmi_rcu(start, xi) \ + for (xi = rcu_dereference(start); xi; xi = rcu_dereference(xi->next)) + +static u32 xfrmi_hash(u32 if_id) +{ + return hash_32(if_id, XFRMI_HASH_BITS); +} + +static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x) +{ + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + struct xfrm_if *xi; + + for_each_xfrmi_rcu(xfrmn->xfrmi[xfrmi_hash(x->if_id)], xi) { + if (x->if_id == xi->p.if_id && + (xi->dev->flags & IFF_UP)) + return xi; + } + + xi = rcu_dereference(xfrmn->collect_md_xfrmi); + if (xi && (xi->dev->flags & IFF_UP)) + return xi; + + return NULL; +} + +static bool xfrmi_decode_session(struct sk_buff *skb, + unsigned short family, + struct xfrm_if_decode_session_result *res) +{ + struct net_device *dev; + struct xfrm_if *xi; + int ifindex = 0; + + if (!secpath_exists(skb) || !skb->dev) + return false; + + switch (family) { + case AF_INET6: + ifindex = inet6_sdif(skb); + break; + case AF_INET: + ifindex = inet_sdif(skb); + break; + } + + if (ifindex) { + struct net *net = xs_net(xfrm_input_state(skb)); + + dev = dev_get_by_index_rcu(net, ifindex); + } else { + dev = skb->dev; + } + + if (!dev || !(dev->flags & IFF_UP)) + return false; + if (dev->netdev_ops != &xfrmi_netdev_ops) + return false; + + xi = netdev_priv(dev); + res->net = xi->net; + + if (xi->p.collect_md) + res->if_id = xfrm_input_state(skb)->if_id; + else + res->if_id = xi->p.if_id; + return true; +} + +static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi) +{ + struct xfrm_if __rcu **xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; + + rcu_assign_pointer(xi->next , rtnl_dereference(*xip)); + rcu_assign_pointer(*xip, xi); +} + +static void xfrmi_unlink(struct xfrmi_net *xfrmn, struct xfrm_if *xi) +{ + struct xfrm_if __rcu **xip; + struct xfrm_if *iter; + + for (xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; + (iter = rtnl_dereference(*xip)) != NULL; + xip = &iter->next) { + if (xi == iter) { + rcu_assign_pointer(*xip, xi->next); + break; + } + } +} + +static void xfrmi_dev_free(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + + gro_cells_destroy(&xi->gro_cells); + free_percpu(dev->tstats); +} + +static int xfrmi_create(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net *net = dev_net(dev); + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + int err; + + dev->rtnl_link_ops = &xfrmi_link_ops; + err = register_netdevice(dev); + if (err < 0) + goto out; + + if (xi->p.collect_md) + rcu_assign_pointer(xfrmn->collect_md_xfrmi, xi); + else + xfrmi_link(xfrmn, xi); + + return 0; + +out: + return err; +} + +static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p) +{ + struct xfrm_if __rcu **xip; + struct xfrm_if *xi; + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + + for (xip = &xfrmn->xfrmi[xfrmi_hash(p->if_id)]; + (xi = rtnl_dereference(*xip)) != NULL; + xip = &xi->next) + if (xi->p.if_id == p->if_id) + return xi; + + return NULL; +} + +static void xfrmi_dev_uninit(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id); + + if (xi->p.collect_md) + RCU_INIT_POINTER(xfrmn->collect_md_xfrmi, NULL); + else + xfrmi_unlink(xfrmn, xi); +} + +static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) +{ + skb_clear_tstamp(skb); + skb->pkt_type = PACKET_HOST; + skb->skb_iif = 0; + skb->ignore_df = 0; + skb_dst_drop(skb); + nf_reset_ct(skb); + nf_reset_trace(skb); + + if (!xnet) + return; + + ipvs_reset(skb); + secpath_reset(skb); + skb_orphan(skb); + skb->mark = 0; +} + +static int xfrmi_rcv_cb(struct sk_buff *skb, int err) +{ + const struct xfrm_mode *inner_mode; + struct net_device *dev; + struct xfrm_state *x; + struct xfrm_if *xi; + bool xnet; + int link; + + if (err && !secpath_exists(skb)) + return 0; + + x = xfrm_input_state(skb); + + xi = xfrmi_lookup(xs_net(x), x); + if (!xi) + return 1; + + link = skb->dev->ifindex; + dev = xi->dev; + skb->dev = dev; + + if (err) { + dev->stats.rx_errors++; + dev->stats.rx_dropped++; + + return 0; + } + + xnet = !net_eq(xi->net, dev_net(skb->dev)); + + if (xnet) { + inner_mode = &x->inner_mode; + + if (x->sel.family == AF_UNSPEC) { + inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); + if (inner_mode == NULL) { + XFRM_INC_STATS(dev_net(skb->dev), + LINUX_MIB_XFRMINSTATEMODEERROR); + return -EINVAL; + } + } + + if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, + inner_mode->family)) + return -EPERM; + } + + xfrmi_scrub_packet(skb, xnet); + if (xi->p.collect_md) { + struct metadata_dst *md_dst; + + md_dst = metadata_dst_alloc(0, METADATA_XFRM, GFP_ATOMIC); + if (!md_dst) + return -ENOMEM; + + md_dst->u.xfrm_info.if_id = x->if_id; + md_dst->u.xfrm_info.link = link; + skb_dst_set(skb, (struct dst_entry *)md_dst); + } + dev_sw_netstats_rx_add(dev, skb->len); + + return 0; +} + +static int +xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net_device_stats *stats = &xi->dev->stats; + struct dst_entry *dst = skb_dst(skb); + unsigned int length = skb->len; + struct net_device *tdev; + struct xfrm_state *x; + int err = -1; + u32 if_id; + int mtu; + + if (xi->p.collect_md) { + struct xfrm_md_info *md_info = skb_xfrm_md_info(skb); + + if (unlikely(!md_info)) + return -EINVAL; + + if_id = md_info->if_id; + fl->flowi_oif = md_info->link; + } else { + if_id = xi->p.if_id; + } + + dst_hold(dst); + dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, if_id); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; + goto tx_err_link_failure; + } + + x = dst->xfrm; + if (!x) + goto tx_err_link_failure; + + if (x->if_id != if_id) + goto tx_err_link_failure; + + tdev = dst->dev; + + if (tdev == dev) { + stats->collisions++; + net_warn_ratelimited("%s: Local routing loop detected!\n", + dev->name); + goto tx_err_dst_release; + } + + mtu = dst_mtu(dst); + if ((!skb_is_gso(skb) && skb->len > mtu) || + (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) { + skb_dst_update_pmtu_no_confirm(skb, mtu); + + if (skb->protocol == htons(ETH_P_IPV6)) { + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + + if (skb->len > 1280) + icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + else + goto xmit; + } else { + if (!(ip_hdr(skb)->frag_off & htons(IP_DF))) + goto xmit; + icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(mtu)); + } + + dst_release(dst); + return -EMSGSIZE; + } + +xmit: + xfrmi_scrub_packet(skb, !net_eq(xi->net, dev_net(dev))); + skb_dst_set(skb, dst); + skb->dev = tdev; + + err = dst_output(xi->net, skb->sk, skb); + if (net_xmit_eval(err) == 0) { + dev_sw_netstats_tx_add(dev, 1, length); + } else { + stats->tx_errors++; + stats->tx_aborted_errors++; + } + + return 0; +tx_err_link_failure: + stats->tx_carrier_errors++; + dst_link_failure(skb); +tx_err_dst_release: + dst_release(dst); + return err; +} + +static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net_device_stats *stats = &xi->dev->stats; + struct dst_entry *dst = skb_dst(skb); + struct flowi fl; + int ret; + + memset(&fl, 0, sizeof(fl)); + + switch (skb->protocol) { + case htons(ETH_P_IPV6): + xfrm_decode_session(skb, &fl, AF_INET6); + memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); + if (!dst) { + fl.u.ip6.flowi6_oif = dev->ifindex; + fl.u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC; + dst = ip6_route_output(dev_net(dev), NULL, &fl.u.ip6); + if (dst->error) { + dst_release(dst); + stats->tx_carrier_errors++; + goto tx_err; + } + skb_dst_set(skb, dst); + } + break; + case htons(ETH_P_IP): + xfrm_decode_session(skb, &fl, AF_INET); + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + if (!dst) { + struct rtable *rt; + + fl.u.ip4.flowi4_oif = dev->ifindex; + fl.u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; + rt = __ip_route_output_key(dev_net(dev), &fl.u.ip4); + if (IS_ERR(rt)) { + stats->tx_carrier_errors++; + goto tx_err; + } + skb_dst_set(skb, &rt->dst); + } + break; + default: + goto tx_err; + } + + fl.flowi_oif = xi->p.link; + + ret = xfrmi_xmit2(skb, dev, &fl); + if (ret < 0) + goto tx_err; + + return NETDEV_TX_OK; + +tx_err: + stats->tx_errors++; + stats->tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int xfrmi4_err(struct sk_buff *skb, u32 info) +{ + const struct iphdr *iph = (const struct iphdr *)skb->data; + struct net *net = dev_net(skb->dev); + int protocol = iph->protocol; + struct ip_comp_hdr *ipch; + struct ip_esp_hdr *esph; + struct ip_auth_hdr *ah ; + struct xfrm_state *x; + struct xfrm_if *xi; + __be32 spi; + + switch (protocol) { + case IPPROTO_ESP: + esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); + spi = esph->spi; + break; + case IPPROTO_AH: + ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2)); + spi = ah->spi; + break; + case IPPROTO_COMP: + ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); + spi = htonl(ntohs(ipch->cpi)); + break; + default: + return 0; + } + + switch (icmp_hdr(skb)->type) { + case ICMP_DEST_UNREACH: + if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) + return 0; + break; + case ICMP_REDIRECT: + break; + default: + return 0; + } + + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + spi, protocol, AF_INET); + if (!x) + return 0; + + xi = xfrmi_lookup(net, x); + if (!xi) { + xfrm_state_put(x); + return -1; + } + + if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) + ipv4_update_pmtu(skb, net, info, 0, protocol); + else + ipv4_redirect(skb, net, 0, protocol); + xfrm_state_put(x); + + return 0; +} + +static int xfrmi6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info) +{ + const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; + struct net *net = dev_net(skb->dev); + int protocol = iph->nexthdr; + struct ip_comp_hdr *ipch; + struct ip_esp_hdr *esph; + struct ip_auth_hdr *ah; + struct xfrm_state *x; + struct xfrm_if *xi; + __be32 spi; + + switch (protocol) { + case IPPROTO_ESP: + esph = (struct ip_esp_hdr *)(skb->data + offset); + spi = esph->spi; + break; + case IPPROTO_AH: + ah = (struct ip_auth_hdr *)(skb->data + offset); + spi = ah->spi; + break; + case IPPROTO_COMP: + ipch = (struct ip_comp_hdr *)(skb->data + offset); + spi = htonl(ntohs(ipch->cpi)); + break; + default: + return 0; + } + + if (type != ICMPV6_PKT_TOOBIG && + type != NDISC_REDIRECT) + return 0; + + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + spi, protocol, AF_INET6); + if (!x) + return 0; + + xi = xfrmi_lookup(net, x); + if (!xi) { + xfrm_state_put(x); + return -1; + } + + if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); + else + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); + xfrm_state_put(x); + + return 0; +} + +static int xfrmi_change(struct xfrm_if *xi, const struct xfrm_if_parms *p) +{ + if (xi->p.link != p->link) + return -EINVAL; + + xi->p.if_id = p->if_id; + + return 0; +} + +static int xfrmi_update(struct xfrm_if *xi, struct xfrm_if_parms *p) +{ + struct net *net = xi->net; + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + int err; + + xfrmi_unlink(xfrmn, xi); + synchronize_net(); + err = xfrmi_change(xi, p); + xfrmi_link(xfrmn, xi); + netdev_state_change(xi->dev); + return err; +} + +static int xfrmi_get_iflink(const struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + + return xi->p.link; +} + +static const struct net_device_ops xfrmi_netdev_ops = { + .ndo_init = xfrmi_dev_init, + .ndo_uninit = xfrmi_dev_uninit, + .ndo_start_xmit = xfrmi_xmit, + .ndo_get_stats64 = dev_get_tstats64, + .ndo_get_iflink = xfrmi_get_iflink, +}; + +static void xfrmi_dev_setup(struct net_device *dev) +{ + dev->netdev_ops = &xfrmi_netdev_ops; + dev->header_ops = &ip_tunnel_header_ops; + dev->type = ARPHRD_NONE; + dev->mtu = ETH_DATA_LEN; + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = IP_MAX_MTU; + dev->flags = IFF_NOARP; + dev->needs_free_netdev = true; + dev->priv_destructor = xfrmi_dev_free; + netif_keep_dst(dev); + + eth_broadcast_addr(dev->broadcast); +} + +#define XFRMI_FEATURES (NETIF_F_SG | \ + NETIF_F_FRAGLIST | \ + NETIF_F_GSO_SOFTWARE | \ + NETIF_F_HW_CSUM) + +static int xfrmi_dev_init(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net_device *phydev = __dev_get_by_index(xi->net, xi->p.link); + int err; + + dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + if (!dev->tstats) + return -ENOMEM; + + err = gro_cells_init(&xi->gro_cells, dev); + if (err) { + free_percpu(dev->tstats); + return err; + } + + dev->features |= NETIF_F_LLTX; + dev->features |= XFRMI_FEATURES; + dev->hw_features |= XFRMI_FEATURES; + + if (phydev) { + dev->needed_headroom = phydev->needed_headroom; + dev->needed_tailroom = phydev->needed_tailroom; + + if (is_zero_ether_addr(dev->dev_addr)) + eth_hw_addr_inherit(dev, phydev); + if (is_zero_ether_addr(dev->broadcast)) + memcpy(dev->broadcast, phydev->broadcast, + dev->addr_len); + } else { + eth_hw_addr_random(dev); + eth_broadcast_addr(dev->broadcast); + } + + return 0; +} + +static int xfrmi_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + return 0; +} + +static void xfrmi_netlink_parms(struct nlattr *data[], + struct xfrm_if_parms *parms) +{ + memset(parms, 0, sizeof(*parms)); + + if (!data) + return; + + if (data[IFLA_XFRM_LINK]) + parms->link = nla_get_u32(data[IFLA_XFRM_LINK]); + + if (data[IFLA_XFRM_IF_ID]) + parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]); + + if (data[IFLA_XFRM_COLLECT_METADATA]) + parms->collect_md = true; +} + +static int xfrmi_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct net *net = dev_net(dev); + struct xfrm_if_parms p = {}; + struct xfrm_if *xi; + int err; + + xfrmi_netlink_parms(data, &p); + if (p.collect_md) { + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + + if (p.link || p.if_id) { + NL_SET_ERR_MSG(extack, "link and if_id must be zero"); + return -EINVAL; + } + + if (rtnl_dereference(xfrmn->collect_md_xfrmi)) + return -EEXIST; + + } else { + if (!p.if_id) { + NL_SET_ERR_MSG(extack, "if_id must be non zero"); + return -EINVAL; + } + + xi = xfrmi_locate(net, &p); + if (xi) + return -EEXIST; + } + + xi = netdev_priv(dev); + xi->p = p; + xi->net = net; + xi->dev = dev; + + err = xfrmi_create(dev); + return err; +} + +static void xfrmi_dellink(struct net_device *dev, struct list_head *head) +{ + unregister_netdevice_queue(dev, head); +} + +static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net *net = xi->net; + struct xfrm_if_parms p = {}; + + xfrmi_netlink_parms(data, &p); + if (!p.if_id) { + NL_SET_ERR_MSG(extack, "if_id must be non zero"); + return -EINVAL; + } + + if (p.collect_md) { + NL_SET_ERR_MSG(extack, "collect_md can't be changed"); + return -EINVAL; + } + + xi = xfrmi_locate(net, &p); + if (!xi) { + xi = netdev_priv(dev); + } else { + if (xi->dev != dev) + return -EEXIST; + if (xi->p.collect_md) { + NL_SET_ERR_MSG(extack, + "device can't be changed to collect_md"); + return -EINVAL; + } + } + + return xfrmi_update(xi, &p); +} + +static size_t xfrmi_get_size(const struct net_device *dev) +{ + return + /* IFLA_XFRM_LINK */ + nla_total_size(4) + + /* IFLA_XFRM_IF_ID */ + nla_total_size(4) + + /* IFLA_XFRM_COLLECT_METADATA */ + nla_total_size(0) + + 0; +} + +static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct xfrm_if_parms *parm = &xi->p; + + if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) || + nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id) || + (xi->p.collect_md && nla_put_flag(skb, IFLA_XFRM_COLLECT_METADATA))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static struct net *xfrmi_get_link_net(const struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + + return xi->net; +} + +static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { + [IFLA_XFRM_UNSPEC] = { .strict_start_type = IFLA_XFRM_COLLECT_METADATA }, + [IFLA_XFRM_LINK] = { .type = NLA_U32 }, + [IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, + [IFLA_XFRM_COLLECT_METADATA] = { .type = NLA_FLAG }, +}; + +static struct rtnl_link_ops xfrmi_link_ops __read_mostly = { + .kind = "xfrm", + .maxtype = IFLA_XFRM_MAX, + .policy = xfrmi_policy, + .priv_size = sizeof(struct xfrm_if), + .setup = xfrmi_dev_setup, + .validate = xfrmi_validate, + .newlink = xfrmi_newlink, + .dellink = xfrmi_dellink, + .changelink = xfrmi_changelink, + .get_size = xfrmi_get_size, + .fill_info = xfrmi_fill_info, + .get_link_net = xfrmi_get_link_net, +}; + +static void __net_exit xfrmi_exit_batch_net(struct list_head *net_exit_list) +{ + struct net *net; + LIST_HEAD(list); + + rtnl_lock(); + list_for_each_entry(net, net_exit_list, exit_list) { + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + struct xfrm_if __rcu **xip; + struct xfrm_if *xi; + int i; + + for (i = 0; i < XFRMI_HASH_SIZE; i++) { + for (xip = &xfrmn->xfrmi[i]; + (xi = rtnl_dereference(*xip)) != NULL; + xip = &xi->next) + unregister_netdevice_queue(xi->dev, &list); + } + xi = rtnl_dereference(xfrmn->collect_md_xfrmi); + if (xi) + unregister_netdevice_queue(xi->dev, &list); + } + unregister_netdevice_many(&list); + rtnl_unlock(); +} + +static struct pernet_operations xfrmi_net_ops = { + .exit_batch = xfrmi_exit_batch_net, + .id = &xfrmi_net_id, + .size = sizeof(struct xfrmi_net), +}; + +static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = { + .handler = xfrm6_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 10, +}; + +static struct xfrm6_protocol xfrmi_ah6_protocol __read_mostly = { + .handler = xfrm6_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 10, +}; + +static struct xfrm6_protocol xfrmi_ipcomp6_protocol __read_mostly = { + .handler = xfrm6_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 10, +}; + +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) +static int xfrmi6_rcv_tunnel(struct sk_buff *skb) +{ + const xfrm_address_t *saddr; + __be32 spi; + + saddr = (const xfrm_address_t *)&ipv6_hdr(skb)->saddr; + spi = xfrm6_tunnel_spi_lookup(dev_net(skb->dev), saddr); + + return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi, NULL); +} + +static struct xfrm6_tunnel xfrmi_ipv6_handler __read_mostly = { + .handler = xfrmi6_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 2, +}; + +static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = { + .handler = xfrmi6_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 2, +}; +#endif + +static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = { + .handler = xfrm4_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 10, +}; + +static struct xfrm4_protocol xfrmi_ah4_protocol __read_mostly = { + .handler = xfrm4_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 10, +}; + +static struct xfrm4_protocol xfrmi_ipcomp4_protocol __read_mostly = { + .handler = xfrm4_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 10, +}; + +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) +static int xfrmi4_rcv_tunnel(struct sk_buff *skb) +{ + return xfrm4_rcv_spi(skb, IPPROTO_IPIP, ip_hdr(skb)->saddr); +} + +static struct xfrm_tunnel xfrmi_ipip_handler __read_mostly = { + .handler = xfrmi4_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 3, +}; + +static struct xfrm_tunnel xfrmi_ipip6_handler __read_mostly = { + .handler = xfrmi4_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 2, +}; +#endif + +static int __init xfrmi4_init(void) +{ + int err; + + err = xfrm4_protocol_register(&xfrmi_esp4_protocol, IPPROTO_ESP); + if (err < 0) + goto xfrm_proto_esp_failed; + err = xfrm4_protocol_register(&xfrmi_ah4_protocol, IPPROTO_AH); + if (err < 0) + goto xfrm_proto_ah_failed; + err = xfrm4_protocol_register(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); + if (err < 0) + goto xfrm_proto_comp_failed; +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) + err = xfrm4_tunnel_register(&xfrmi_ipip_handler, AF_INET); + if (err < 0) + goto xfrm_tunnel_ipip_failed; + err = xfrm4_tunnel_register(&xfrmi_ipip6_handler, AF_INET6); + if (err < 0) + goto xfrm_tunnel_ipip6_failed; +#endif + + return 0; + +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) +xfrm_tunnel_ipip6_failed: + xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); +xfrm_tunnel_ipip_failed: + xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); +#endif +xfrm_proto_comp_failed: + xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); +xfrm_proto_ah_failed: + xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); +xfrm_proto_esp_failed: + return err; +} + +static void xfrmi4_fini(void) +{ +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) + xfrm4_tunnel_deregister(&xfrmi_ipip6_handler, AF_INET6); + xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); +#endif + xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); + xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); + xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); +} + +static int __init xfrmi6_init(void) +{ + int err; + + err = xfrm6_protocol_register(&xfrmi_esp6_protocol, IPPROTO_ESP); + if (err < 0) + goto xfrm_proto_esp_failed; + err = xfrm6_protocol_register(&xfrmi_ah6_protocol, IPPROTO_AH); + if (err < 0) + goto xfrm_proto_ah_failed; + err = xfrm6_protocol_register(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); + if (err < 0) + goto xfrm_proto_comp_failed; +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) + err = xfrm6_tunnel_register(&xfrmi_ipv6_handler, AF_INET6); + if (err < 0) + goto xfrm_tunnel_ipv6_failed; + err = xfrm6_tunnel_register(&xfrmi_ip6ip_handler, AF_INET); + if (err < 0) + goto xfrm_tunnel_ip6ip_failed; +#endif + + return 0; + +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) +xfrm_tunnel_ip6ip_failed: + xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); +xfrm_tunnel_ipv6_failed: + xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); +#endif +xfrm_proto_comp_failed: + xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); +xfrm_proto_ah_failed: + xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); +xfrm_proto_esp_failed: + return err; +} + +static void xfrmi6_fini(void) +{ +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) + xfrm6_tunnel_deregister(&xfrmi_ip6ip_handler, AF_INET); + xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); +#endif + xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); + xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); + xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); +} + +static const struct xfrm_if_cb xfrm_if_cb = { + .decode_session = xfrmi_decode_session, +}; + +static int __init xfrmi_init(void) +{ + const char *msg; + int err; + + pr_info("IPsec XFRM device driver\n"); + + msg = "tunnel device"; + err = register_pernet_device(&xfrmi_net_ops); + if (err < 0) + goto pernet_dev_failed; + + msg = "xfrm4 protocols"; + err = xfrmi4_init(); + if (err < 0) + goto xfrmi4_failed; + + msg = "xfrm6 protocols"; + err = xfrmi6_init(); + if (err < 0) + goto xfrmi6_failed; + + + msg = "netlink interface"; + err = rtnl_link_register(&xfrmi_link_ops); + if (err < 0) + goto rtnl_link_failed; + + lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); + + xfrm_if_register_cb(&xfrm_if_cb); + + return err; + +rtnl_link_failed: + xfrmi6_fini(); +xfrmi6_failed: + xfrmi4_fini(); +xfrmi4_failed: + unregister_pernet_device(&xfrmi_net_ops); +pernet_dev_failed: + pr_err("xfrmi init: failed to register %s\n", msg); + return err; +} + +static void __exit xfrmi_fini(void) +{ + xfrm_if_unregister_cb(); + lwtunnel_encap_del_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); + rtnl_link_unregister(&xfrmi_link_ops); + xfrmi4_fini(); + xfrmi6_fini(); + unregister_pernet_device(&xfrmi_net_ops); +} + +module_init(xfrmi_init); +module_exit(xfrmi_fini); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("xfrm"); +MODULE_ALIAS_NETDEV("xfrm0"); +MODULE_AUTHOR("Steffen Klassert"); +MODULE_DESCRIPTION("XFRM virtual interface"); -- cgit 1.4.1 From c803e91600bef2626744734f7ba7d617e1dc19f7 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Wed, 10 May 2023 01:30:22 +0000 Subject: xfrm: Ensure policies always checked on XFRM-I input path [ Upstream commit a287f5b0cfc6804c5b12a4be13c7c9fe27869e90 ] This change adds methods in the XFRM-I input path that ensures that policies are checked prior to processing of the subsequent decapsulated packet, after which the relevant policies may no longer be resolvable (due to changing src/dst/proto/etc). Notably, raw ESP/AH packets did not perform policy checks inherently, whereas all other encapsulated packets (UDP, TCP encapsulated) do policy checks after calling xfrm_input handling in the respective encapsulation layer. Fixes: b0355dbbf13c ("Fix XFRM-I support for nested ESP tunnels") Test: Verified with additional Android Kernel Unit tests Test: Verified against Android CTS Signed-off-by: Benedict Wong Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_interface_core.c | 54 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c index 5a67b120c4db..94a3609548b1 100644 --- a/net/xfrm/xfrm_interface_core.c +++ b/net/xfrm/xfrm_interface_core.c @@ -310,6 +310,52 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) skb->mark = 0; } +static int xfrmi_input(struct sk_buff *skb, int nexthdr, __be32 spi, + int encap_type, unsigned short family) +{ + struct sec_path *sp; + + sp = skb_sec_path(skb); + if (sp && (sp->len || sp->olen) && + !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family)) + goto discard; + + XFRM_SPI_SKB_CB(skb)->family = family; + if (family == AF_INET) { + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; + } else { + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; + } + + return xfrm_input(skb, nexthdr, spi, encap_type); +discard: + kfree_skb(skb); + return 0; +} + +static int xfrmi4_rcv(struct sk_buff *skb) +{ + return xfrmi_input(skb, ip_hdr(skb)->protocol, 0, 0, AF_INET); +} + +static int xfrmi6_rcv(struct sk_buff *skb) +{ + return xfrmi_input(skb, skb_network_header(skb)[IP6CB(skb)->nhoff], + 0, 0, AF_INET6); +} + +static int xfrmi4_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) +{ + return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET); +} + +static int xfrmi6_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) +{ + return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET6); +} + static int xfrmi_rcv_cb(struct sk_buff *skb, int err) { const struct xfrm_mode *inner_mode; @@ -937,8 +983,8 @@ static struct pernet_operations xfrmi_net_ops = { }; static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = { - .handler = xfrm6_rcv, - .input_handler = xfrm_input, + .handler = xfrmi6_rcv, + .input_handler = xfrmi6_input, .cb_handler = xfrmi_rcv_cb, .err_handler = xfrmi6_err, .priority = 10, @@ -988,8 +1034,8 @@ static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = { #endif static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = { - .handler = xfrm4_rcv, - .input_handler = xfrm_input, + .handler = xfrmi4_rcv, + .input_handler = xfrmi4_input, .cb_handler = xfrmi_rcv_cb, .err_handler = xfrmi4_err, .priority = 10, -- cgit 1.4.1 From b36ba84f09a142b2564932b42735e04bb0907395 Mon Sep 17 00:00:00 2001 From: Reiji Watanabe Date: Fri, 2 Jun 2023 19:50:34 -0700 Subject: KVM: arm64: PMU: Restore the host's PMUSERENR_EL0 [ Upstream commit 8681f71759010503892f9e3ddb05f65c0f21b690 ] Restore the host's PMUSERENR_EL0 value instead of clearing it, before returning back to userspace, as the host's EL0 might have a direct access to PMU registers (some bits of PMUSERENR_EL0 for might not be zero for the host EL0). Fixes: 83a7a4d643d3 ("arm64: perf: Enable PMU counter userspace access for perf event") Signed-off-by: Reiji Watanabe Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230603025035.3781797-2-reijiw@google.com Signed-off-by: Sasha Levin --- arch/arm64/kvm/hyp/include/hyp/switch.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 2208d79b18de..081aca8f432e 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -81,7 +81,12 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu) * EL1 instead of being trapped to EL2. */ if (kvm_arm_support_pmu_v3()) { + struct kvm_cpu_context *hctxt; + write_sysreg(0, pmselr_el0); + + hctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; + ctxt_sys_reg(hctxt, PMUSERENR_EL0) = read_sysreg(pmuserenr_el0); write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0); } @@ -105,8 +110,12 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) write_sysreg(vcpu->arch.mdcr_el2_host, mdcr_el2); write_sysreg(0, hstr_el2); - if (kvm_arm_support_pmu_v3()) - write_sysreg(0, pmuserenr_el0); + if (kvm_arm_support_pmu_v3()) { + struct kvm_cpu_context *hctxt; + + hctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; + write_sysreg(ctxt_sys_reg(hctxt, PMUSERENR_EL0), pmuserenr_el0); + } if (cpus_have_final_cap(ARM64_SME)) { sysreg_clear_set_s(SYS_HFGRTR_EL2, 0, -- cgit 1.4.1 From 461fc3391c5263710113d0ae9b89c077d197543d Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 15 Feb 2023 01:20:27 +0200 Subject: bpf: track immediate values written to stack by BPF_ST instruction [ Upstream commit ecdf985d7615356b78241fdb159c091830ed0380 ] For aligned stack writes using BPF_ST instruction track stored values in a same way BPF_STX is handled, e.g. make sure that the following commands produce similar verifier knowledge: fp[-8] = 42; r1 = 42; fp[-8] = r1; This covers two cases: - non-null values written to stack are stored as spill of fake registers; - null values written to stack are stored as STACK_ZERO marks. Previously both cases above used STACK_MISC marks instead. Some verifier test cases relied on the old logic to obtain STACK_MISC marks for some stack values. These test cases are updated in the same commit to avoid failures during bisect. Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20230214232030.1502829-2-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov Stable-dep-of: 713274f1f2c8 ("bpf: Fix verifier id tracking of scalars on spill") Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 18 +++- .../bpf/verifier/bounds_mix_sign_unsign.c | 110 ++++++++++++--------- 2 files changed, 80 insertions(+), 48 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a0e573c08f79..c4ceb4166528 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3061,6 +3061,11 @@ static void save_register_state(struct bpf_func_state *state, scrub_spilled_slot(&state->stack[spi].slot_type[i - 1]); } +static bool is_bpf_st_mem(struct bpf_insn *insn) +{ + return BPF_CLASS(insn->code) == BPF_ST && BPF_MODE(insn->code) == BPF_MEM; +} + /* check_stack_{read,write}_fixed_off functions track spill/fill of registers, * stack boundary and alignment are checked in check_mem_access() */ @@ -3072,8 +3077,9 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, { struct bpf_func_state *cur; /* state of the current function */ int i, slot = -off - 1, spi = slot / BPF_REG_SIZE, err; - u32 dst_reg = env->prog->insnsi[insn_idx].dst_reg; + struct bpf_insn *insn = &env->prog->insnsi[insn_idx]; struct bpf_reg_state *reg = NULL; + u32 dst_reg = insn->dst_reg; err = grow_stack_state(state, round_up(slot + 1, BPF_REG_SIZE)); if (err) @@ -3122,6 +3128,13 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, return err; } save_register_state(state, spi, reg, size); + } else if (!reg && !(off % BPF_REG_SIZE) && is_bpf_st_mem(insn) && + insn->imm != 0 && env->bpf_capable) { + struct bpf_reg_state fake_reg = {}; + + __mark_reg_known(&fake_reg, (u32)insn->imm); + fake_reg.type = SCALAR_VALUE; + save_register_state(state, spi, &fake_reg, size); } else if (reg && is_spillable_regtype(reg->type)) { /* register containing pointer is being spilled into stack */ if (size != BPF_REG_SIZE) { @@ -3156,7 +3169,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN; /* when we zero initialize stack slots mark them as such */ - if (reg && register_is_null(reg)) { + if ((reg && register_is_null(reg)) || + (!reg && is_bpf_st_mem(insn) && insn->imm == 0)) { /* backtracking doesn't work for STACK_ZERO yet. */ err = mark_chain_precision(env, value_regno); if (err) diff --git a/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c b/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c index c2aa6f26738b..bf82b923c5fe 100644 --- a/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c +++ b/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c @@ -1,13 +1,14 @@ { "bounds checks mixing signed and unsigned, positive bounds", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 2), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 3), @@ -17,20 +18,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 3), @@ -40,20 +42,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 2", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 5), @@ -65,20 +68,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 3", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 4), @@ -89,20 +93,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 4", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 1), BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_2), @@ -112,19 +117,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .result = ACCEPT, }, { "bounds checks mixing signed and unsigned, variant 5", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 5), @@ -135,17 +141,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 6", .insns = { + BPF_MOV64_REG(BPF_REG_9, BPF_REG_1), + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), BPF_MOV64_IMM(BPF_REG_2, 0), BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -512), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_6, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_6, 5), @@ -163,13 +172,14 @@ { "bounds checks mixing signed and unsigned, variant 7", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 1024 * 1024 * 1024), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 3), @@ -179,19 +189,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .result = ACCEPT, }, { "bounds checks mixing signed and unsigned, variant 8", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), @@ -203,20 +214,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 9", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_LD_IMM64(BPF_REG_2, -9223372036854775808ULL), BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), @@ -228,19 +240,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .result = ACCEPT, }, { "bounds checks mixing signed and unsigned, variant 10", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 0), BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), @@ -252,20 +265,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 11", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -278,20 +292,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 12", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -6), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -303,20 +318,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 13", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 2), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -331,7 +347,7 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, @@ -340,13 +356,14 @@ .insns = { BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_1, offsetof(struct __sk_buff, mark)), + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_MOV64_IMM(BPF_REG_8, 2), @@ -360,20 +377,21 @@ BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, -3), BPF_JMP_IMM(BPF_JA, 0, 0, -7), }, - .fixup_map_hash_8b = { 4 }, + .fixup_map_hash_8b = { 6 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 15", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -6), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -387,7 +405,7 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, -- cgit 1.4.1 From d9a0b1a53c794046f424f1d94b641e141faaf5f6 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Wed, 7 Jun 2023 15:39:50 +0300 Subject: bpf: Fix verifier id tracking of scalars on spill [ Upstream commit 713274f1f2c896d37017efee333fd44149710119 ] The following scenario describes a bug in the verifier where it incorrectly concludes about equivalent scalar IDs which could lead to verifier bypass in privileged mode: 1. Prepare a 32-bit rogue number. 2. Put the rogue number into the upper half of a 64-bit register, and roll a random (unknown to the verifier) bit in the lower half. The rest of the bits should be zero (although variations are possible). 3. Assign an ID to the register by MOVing it to another arbitrary register. 4. Perform a 32-bit spill of the register, then perform a 32-bit fill to another register. Due to a bug in the verifier, the ID will be preserved, although the new register will contain only the lower 32 bits, i.e. all zeros except one random bit. At this point there are two registers with different values but the same ID, which means the integrity of the verifier state has been corrupted. 5. Compare the new 32-bit register with 0. In the branch where it's equal to 0, the verifier will believe that the original 64-bit register is also 0, because it has the same ID, but its actual value still contains the rogue number in the upper half. Some optimizations of the verifier prevent the actual bypass, so extra care is needed: the comparison must be between two registers, and both branches must be reachable (this is why one random bit is needed). Both branches are still suitable for the bypass. 6. Right shift the original register by 32 bits to pop the rogue number. 7. Use the rogue number as an offset with any pointer. The verifier will believe that the offset is 0, while in reality it's the given number. The fix is similar to the 32-bit BPF_MOV handling in check_alu_op for SCALAR_VALUE. If the spill is narrowing the actual register value, don't keep the ID, make sure it's reset to 0. Fixes: 354e8f1970f8 ("bpf: Support <8-byte scalar spill and refill") Signed-off-by: Maxim Mikityanskiy Signed-off-by: Daniel Borkmann Tested-by: Andrii Nakryiko # Checked veristat delta Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20230607123951.558971-2-maxtram95@gmail.com Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c4ceb4166528..49c6b5e0855c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3128,6 +3128,9 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, return err; } save_register_state(state, spi, reg, size); + /* Break the relation on a narrowing spill. */ + if (fls64(reg->umax_value) > BITS_PER_BYTE * size) + state->stack[spi].spilled_ptr.id = 0; } else if (!reg && !(off % BPF_REG_SIZE) && is_bpf_st_mem(insn) && insn->imm != 0 && env->bpf_capable) { struct bpf_reg_state fake_reg = {}; -- cgit 1.4.1 From 8bb51cdc4fc2382045d246146b0948a6c43ba0d6 Mon Sep 17 00:00:00 2001 From: Maciej Żenczykowski Date: Mon, 5 Jun 2023 04:06:54 -0700 Subject: xfrm: fix inbound ipv4/udp/esp packets to UDPv6 dualstack sockets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1166a530a84758bb9e6b448fc8c195ed413f5ded ] Before Linux v5.8 an AF_INET6 SOCK_DGRAM (udp/udplite) socket with SOL_UDP, UDP_ENCAP, UDP_ENCAP_ESPINUDP{,_NON_IKE} enabled would just unconditionally use xfrm4_udp_encap_rcv(), afterwards such a socket would use the newly added xfrm6_udp_encap_rcv() which only handles IPv6 packets. Cc: Sabrina Dubroca Cc: Steffen Klassert Cc: Jakub Kicinski Cc: Benedict Wong Cc: Yan Yan Fixes: 0146dca70b87 ("xfrm: add support for UDPv6 encapsulation of ESP") Signed-off-by: Maciej Żenczykowski Reviewed-by: Simon Horman Reviewed-by: Sabrina Dubroca Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/ipv4/xfrm4_input.c | 1 + net/ipv6/xfrm6_input.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index ad2afeef4f10..eac206a290d0 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -164,6 +164,7 @@ drop: kfree_skb(skb); return 0; } +EXPORT_SYMBOL(xfrm4_udp_encap_rcv); int xfrm4_rcv(struct sk_buff *skb) { diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 04cbeefd8982..4907ab241d6b 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -86,6 +86,9 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) __be32 *udpdata32; __u16 encap_type = up->encap_type; + if (skb->protocol == htons(ETH_P_IP)) + return xfrm4_udp_encap_rcv(sk, skb); + /* if this is not encapsulated socket, then just return now */ if (!encap_type) return 1; -- cgit 1.4.1 From ac5671d10060176091c38f602181144061f3be47 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Thu, 8 Jun 2023 17:54:39 -0700 Subject: bpf: Fix a bpf_jit_dump issue for x86_64 with sysctl bpf_jit_enable. [ Upstream commit ad96f1c9138e0897bee7f7c5e54b3e24f8b62f57 ] The sysctl net/core/bpf_jit_enable does not work now due to commit 1022a5498f6f ("bpf, x86_64: Use bpf_jit_binary_pack_alloc"). The commit saved the jitted insns into 'rw_image' instead of 'image' which caused bpf_jit_dump not dumping proper content. With 'echo 2 > /proc/sys/net/core/bpf_jit_enable', run './test_progs -t fentry_test'. Without this patch, one of jitted image for one particular prog is: flen=17 proglen=92 pass=4 image=0000000014c64883 from=test_progs pid=1807 00000000: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 00000010: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 00000020: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 00000030: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 00000040: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 00000050: cc cc cc cc cc cc cc cc cc cc cc cc With this patch, the jitte image for the same prog is: flen=17 proglen=92 pass=4 image=00000000b90254b7 from=test_progs pid=1809 00000000: f3 0f 1e fa 0f 1f 44 00 00 66 90 55 48 89 e5 f3 00000010: 0f 1e fa 31 f6 48 8b 57 00 48 83 fa 07 75 2b 48 00000020: 8b 57 10 83 fa 09 75 22 48 8b 57 08 48 81 e2 ff 00000030: 00 00 00 48 83 fa 08 75 11 48 8b 7f 18 be 01 00 00000040: 00 00 48 83 ff 0a 74 02 31 f6 48 bf 18 d0 14 00 00000050: 00 c9 ff ff 48 89 77 00 31 c0 c9 c3 Fixes: 1022a5498f6f ("bpf, x86_64: Use bpf_jit_binary_pack_alloc") Signed-off-by: Yonghong Song Signed-off-by: Daniel Borkmann Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20230609005439.3173569-1-yhs@fb.com Signed-off-by: Sasha Levin --- arch/x86/net/bpf_jit_comp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 99620428ad78..db6053a22e86 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -2478,7 +2478,7 @@ out_image: } if (bpf_jit_enable > 1) - bpf_jit_dump(prog->len, proglen, pass + 1, image); + bpf_jit_dump(prog->len, proglen, pass + 1, rw_image); if (image) { if (!prog->is_func || extra_pass) { -- cgit 1.4.1 From 6919634176cd1c8df18777bbeb1d1b934f891562 Mon Sep 17 00:00:00 2001 From: Magali Lemes Date: Tue, 13 Jun 2023 09:32:20 -0300 Subject: selftests: net: tls: check if FIPS mode is enabled [ Upstream commit d113c395c67b62fc0d3f2004c0afc406aca0a2b7 ] TLS selftests use the ChaCha20-Poly1305 and SM4 algorithms, which are not FIPS compliant. When fips=1, this set of tests fails. Add a check and only run these tests if not in FIPS mode. Fixes: 4f336e88a870 ("selftests/tls: add CHACHA20-POLY1305 to tls selftests") Fixes: e506342a03c7 ("selftests/tls: add SM4 GCM/CCM to tls selftests") Reviewed-by: Jakub Kicinski Signed-off-by: Magali Lemes Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/tls.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 2cbb12736596..c0ad8385441f 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -25,6 +25,8 @@ #define TLS_PAYLOAD_MAX_LEN 16384 #define SOL_TLS 282 +static int fips_enabled; + struct tls_crypto_info_keys { union { struct tls12_crypto_info_aes_gcm_128 aes128; @@ -235,7 +237,7 @@ FIXTURE_VARIANT(tls) { uint16_t tls_version; uint16_t cipher_type; - bool nopad; + bool nopad, fips_non_compliant; }; FIXTURE_VARIANT_ADD(tls, 12_aes_gcm) @@ -254,24 +256,28 @@ FIXTURE_VARIANT_ADD(tls, 12_chacha) { .tls_version = TLS_1_2_VERSION, .cipher_type = TLS_CIPHER_CHACHA20_POLY1305, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 13_chacha) { .tls_version = TLS_1_3_VERSION, .cipher_type = TLS_CIPHER_CHACHA20_POLY1305, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 13_sm4_gcm) { .tls_version = TLS_1_3_VERSION, .cipher_type = TLS_CIPHER_SM4_GCM, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 13_sm4_ccm) { .tls_version = TLS_1_3_VERSION, .cipher_type = TLS_CIPHER_SM4_CCM, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 12_aes_ccm) @@ -311,6 +317,9 @@ FIXTURE_SETUP(tls) int one = 1; int ret; + if (fips_enabled && variant->fips_non_compliant) + SKIP(return, "Unsupported cipher in FIPS mode"); + tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12); @@ -1820,4 +1829,17 @@ TEST(tls_v6ops) { close(sfd); } +static void __attribute__((constructor)) fips_check(void) { + int res; + FILE *f; + + f = fopen("/proc/sys/crypto/fips_enabled", "r"); + if (f) { + res = fscanf(f, "%d", &fips_enabled); + if (res != 1) + ksft_print_msg("ERROR: Couldn't read /proc/sys/crypto/fips_enabled\n"); + fclose(f); + } +} + TEST_HARNESS_MAIN -- cgit 1.4.1 From 0793ead2ff2c11946c6f1671170c8d840c048449 Mon Sep 17 00:00:00 2001 From: Magali Lemes Date: Tue, 13 Jun 2023 09:32:21 -0300 Subject: selftests: net: vrf-xfrm-tests: change authentication and encryption algos [ Upstream commit cb43c60e64ca67fcc9d23bd08f51d2ab8209d9d7 ] The vrf-xfrm-tests tests use the hmac(md5) and cbc(des3_ede) algorithms for performing authentication and encryption, respectively. This causes the tests to fail when fips=1 is set, since these algorithms are not allowed in FIPS mode. Therefore, switch from hmac(md5) and cbc(des3_ede) to hmac(sha1) and cbc(aes), which are FIPS compliant. Fixes: 3f251d741150 ("selftests: Add tests for vrf and xfrms") Reviewed-by: David Ahern Signed-off-by: Magali Lemes Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/vrf-xfrm-tests.sh | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/net/vrf-xfrm-tests.sh b/tools/testing/selftests/net/vrf-xfrm-tests.sh index 184da81f554f..452638ae8aed 100755 --- a/tools/testing/selftests/net/vrf-xfrm-tests.sh +++ b/tools/testing/selftests/net/vrf-xfrm-tests.sh @@ -264,60 +264,60 @@ setup_xfrm() ip -netns host1 xfrm state add src ${HOST1_4} dst ${HOST2_4} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_4} dst ${h2_4} ${devarg} ip -netns host2 xfrm state add src ${HOST1_4} dst ${HOST2_4} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_4} dst ${h2_4} ip -netns host1 xfrm state add src ${HOST2_4} dst ${HOST1_4} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_4} dst ${h1_4} ${devarg} ip -netns host2 xfrm state add src ${HOST2_4} dst ${HOST1_4} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_4} dst ${h1_4} ip -6 -netns host1 xfrm state add src ${HOST1_6} dst ${HOST2_6} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_6} dst ${h2_6} ${devarg} ip -6 -netns host2 xfrm state add src ${HOST1_6} dst ${HOST2_6} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_6} dst ${h2_6} ip -6 -netns host1 xfrm state add src ${HOST2_6} dst ${HOST1_6} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_6} dst ${h1_6} ${devarg} ip -6 -netns host2 xfrm state add src ${HOST2_6} dst ${HOST1_6} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_6} dst ${h1_6} } -- cgit 1.4.1 From fff9a18e01286116991e82d95f89e99ffa764bed Mon Sep 17 00:00:00 2001 From: Magali Lemes Date: Tue, 13 Jun 2023 09:32:22 -0300 Subject: selftests: net: fcnal-test: check if FIPS mode is enabled [ Upstream commit d7a2fc1437f71cb058c7b11bc33dfc19e4bf277a ] There are some MD5 tests which fail when the kernel is in FIPS mode, since MD5 is not FIPS compliant. Add a check and only run those tests if FIPS mode is not enabled. Fixes: f0bee1ebb5594 ("fcnal-test: Add TCP MD5 tests") Fixes: 5cad8bce26e01 ("fcnal-test: Add TCP MD5 tests for VRF") Reviewed-by: David Ahern Signed-off-by: Magali Lemes Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/fcnal-test.sh | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh index 21ca91473c09..ee6880ac3e5e 100755 --- a/tools/testing/selftests/net/fcnal-test.sh +++ b/tools/testing/selftests/net/fcnal-test.sh @@ -92,6 +92,13 @@ NSC_CMD="ip netns exec ${NSC}" which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping) +# Check if FIPS mode is enabled +if [ -f /proc/sys/crypto/fips_enabled ]; then + fips_enabled=`cat /proc/sys/crypto/fips_enabled` +else + fips_enabled=0 +fi + ################################################################################ # utilities @@ -1216,7 +1223,7 @@ ipv4_tcp_novrf() run_cmd nettest -d ${NSA_DEV} -r ${a} log_test_addr ${a} $? 1 "No server, device client, local conn" - ipv4_tcp_md5_novrf + [ "$fips_enabled" = "1" ] || ipv4_tcp_md5_novrf } ipv4_tcp_vrf() @@ -1270,9 +1277,11 @@ ipv4_tcp_vrf() log_test_addr ${a} $? 1 "Global server, local connection" # run MD5 tests - setup_vrf_dup - ipv4_tcp_md5 - cleanup_vrf_dup + if [ "$fips_enabled" = "0" ]; then + setup_vrf_dup + ipv4_tcp_md5 + cleanup_vrf_dup + fi # # enable VRF global server @@ -2772,7 +2781,7 @@ ipv6_tcp_novrf() log_test_addr ${a} $? 1 "No server, device client, local conn" done - ipv6_tcp_md5_novrf + [ "$fips_enabled" = "1" ] || ipv6_tcp_md5_novrf } ipv6_tcp_vrf() @@ -2842,9 +2851,11 @@ ipv6_tcp_vrf() log_test_addr ${a} $? 1 "Global server, local connection" # run MD5 tests - setup_vrf_dup - ipv6_tcp_md5 - cleanup_vrf_dup + if [ "$fips_enabled" = "0" ]; then + setup_vrf_dup + ipv6_tcp_md5 + cleanup_vrf_dup + fi # # enable VRF global server -- cgit 1.4.1 From b062caf4f73b7319a81b4f7e4df4957bdc1c47ca Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 14 Jun 2023 12:02:02 +0200 Subject: xfrm: Linearize the skb after offloading if needed. [ Upstream commit f015b900bc3285322029b4a7d132d6aeb0e51857 ] With offloading enabled, esp_xmit() gets invoked very late, from within validate_xmit_xfrm() which is after validate_xmit_skb() validates and linearizes the skb if the underlying device does not support fragments. esp_output_tail() may add a fragment to the skb while adding the auth tag/ IV. Devices without the proper support will then send skb->data points to with the correct length so the packet will have garbage at the end. A pcap sniffer will claim that the proper data has been sent since it parses the skb properly. It is not affected with INET_ESP_OFFLOAD disabled. Linearize the skb after offloading if the sending hardware requires it. It was tested on v4, v6 has been adopted. Fixes: 7785bba299a8d ("esp: Add a software GRO codepath") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/ipv4/esp4_offload.c | 3 +++ net/ipv6/esp6_offload.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index 3969fa805679..ee848be59e65 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -340,6 +340,9 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ secpath_reset(skb); + if (skb_needs_linearize(skb, skb->dev->features) && + __skb_linearize(skb)) + return -ENOMEM; return 0; } diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 242f4295940e..fc6a5be73263 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -375,6 +375,9 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features secpath_reset(skb); + if (skb_needs_linearize(skb, skb->dev->features) && + __skb_linearize(skb)) + return -ENOMEM; return 0; } -- cgit 1.4.1 From 0bbb8164ed07fdb40e999611e8447dc5b9b8e8c7 Mon Sep 17 00:00:00 2001 From: Yevgeny Kliteynik Date: Sun, 4 Jun 2023 21:07:04 +0300 Subject: net/mlx5: DR, Fix wrong action data allocation in decap action [ Upstream commit ef4c5afc783dc3d47640270a9b94713229c697e8 ] When TUNNEL_L3_TO_L2 decap action was created, a pointer to a local variable was passed as its HW action data, resulting in attempt to free invalid address: BUG: KASAN: invalid-free in mlx5dr_action_destroy+0x318/0x410 [mlx5_core] Fixes: 4781df92f4da ("net/mlx5: DR, Move STEv0 modify header logic") Signed-off-by: Yevgeny Kliteynik Reviewed-by: Alex Vesker Signed-off-by: Saeed Mahameed Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index b1dfad274a39..a3e7602b044e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -1200,9 +1200,13 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, } case DR_ACTION_TYP_TNL_L3_TO_L2: { - u8 hw_actions[ACTION_CACHE_LINE_SIZE] = {}; + u8 *hw_actions; int ret; + hw_actions = kzalloc(ACTION_CACHE_LINE_SIZE, GFP_KERNEL); + if (!hw_actions) + return -ENOMEM; + ret = mlx5dr_ste_set_action_decap_l3_list(dmn->ste_ctx, data, data_sz, hw_actions, @@ -1210,6 +1214,7 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, &action->rewrite->num_of_actions); if (ret) { mlx5dr_dbg(dmn, "Failed creating decap l3 action list\n"); + kfree(hw_actions); return ret; } @@ -1217,6 +1222,7 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, DR_CHUNK_SIZE_8); if (!action->rewrite->chunk) { mlx5dr_dbg(dmn, "Failed allocating modify header chunk\n"); + kfree(hw_actions); return -ENOMEM; } @@ -1230,6 +1236,7 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, if (ret) { mlx5dr_dbg(dmn, "Writing decap l3 actions to ICM failed\n"); mlx5dr_icm_free_chunk(action->rewrite->chunk); + kfree(hw_actions); return ret; } return 0; -- cgit 1.4.1 From 1d4dd09f13a958118ba37f24c6b8fcaec0c6c327 Mon Sep 17 00:00:00 2001 From: Íñigo Huguet Date: Thu, 15 Jun 2023 10:49:29 +0200 Subject: sfc: use budget for TX completions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4aaf2c52834b7f95acdf9fb0211a1b60adbf421b ] When running workloads heavy unbalanced towards TX (high TX, low RX traffic), sfc driver can retain the CPU during too long times. Although in many cases this is not enough to be visible, it can affect performance and system responsiveness. A way to reproduce it is to use a debug kernel and run some parallel netperf TX tests. In some systems, this will lead to this message being logged: kernel:watchdog: BUG: soft lockup - CPU#12 stuck for 22s! The reason is that sfc driver doesn't account any NAPI budget for the TX completion events work. With high-TX/low-RX traffic, this makes that the CPU is held for long time for NAPI poll. Documentations says "drivers can process completions for any number of Tx packets but should only process up to budget number of Rx packets". However, many drivers do limit the amount of TX completions that they process in a single NAPI poll. In the same way, this patch adds a limit for the TX work in sfc. With the patch applied, the watchdog warning never appears. Tested with netperf in different combinations: single process / parallel processes, TCP / UDP and different sizes of UDP messages. Repeated the tests before and after the patch, without any noticeable difference in network or CPU performance. Test hardware: Intel(R) Xeon(R) CPU E5-1620 v4 @ 3.50GHz (4 cores, 2 threads/core) Solarflare Communications XtremeScale X2522-25G Network Adapter Fixes: 5227ecccea2d ("sfc: remove tx and MCDI handling from NAPI budget consideration") Fixes: d19a53721863 ("sfc_ef100: TX path for EF100 NICs") Reported-by: Fei Liu Signed-off-by: Íñigo Huguet Acked-by: Martin Habets Link: https://lore.kernel.org/r/20230615084929.10506-1-ihuguet@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/sfc/ef10.c | 25 ++++++++++++++++++------- drivers/net/ethernet/sfc/ef100_nic.c | 7 ++++++- drivers/net/ethernet/sfc/ef100_tx.c | 4 ++-- drivers/net/ethernet/sfc/ef100_tx.h | 2 +- drivers/net/ethernet/sfc/tx_common.c | 4 +++- drivers/net/ethernet/sfc/tx_common.h | 2 +- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index d30459dbfe8f..b63e47af6365 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2950,7 +2950,7 @@ static u32 efx_ef10_extract_event_ts(efx_qword_t *event) return tstamp; } -static void +static int efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) { struct efx_nic *efx = channel->efx; @@ -2958,13 +2958,14 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) unsigned int tx_ev_desc_ptr; unsigned int tx_ev_q_label; unsigned int tx_ev_type; + int work_done; u64 ts_part; if (unlikely(READ_ONCE(efx->reset_pending))) - return; + return 0; if (unlikely(EFX_QWORD_FIELD(*event, ESF_DZ_TX_DROP_EVENT))) - return; + return 0; /* Get the transmit queue */ tx_ev_q_label = EFX_QWORD_FIELD(*event, ESF_DZ_TX_QLABEL); @@ -2973,8 +2974,7 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) if (!tx_queue->timestamping) { /* Transmit completion */ tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, ESF_DZ_TX_DESCR_INDX); - efx_xmit_done(tx_queue, tx_ev_desc_ptr & tx_queue->ptr_mask); - return; + return efx_xmit_done(tx_queue, tx_ev_desc_ptr & tx_queue->ptr_mask); } /* Transmit timestamps are only available for 8XXX series. They result @@ -3000,6 +3000,7 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) * fields in the event. */ tx_ev_type = EFX_QWORD_FIELD(*event, ESF_EZ_TX_SOFT1); + work_done = 0; switch (tx_ev_type) { case TX_TIMESTAMP_EVENT_TX_EV_COMPLETION: @@ -3016,6 +3017,7 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) tx_queue->completed_timestamp_major = ts_part; efx_xmit_done_single(tx_queue); + work_done = 1; break; default: @@ -3026,6 +3028,8 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) EFX_QWORD_VAL(*event)); break; } + + return work_done; } static void @@ -3081,13 +3085,16 @@ static void efx_ef10_handle_driver_generated_event(struct efx_channel *channel, } } +#define EFX_NAPI_MAX_TX 512 + static int efx_ef10_ev_process(struct efx_channel *channel, int quota) { struct efx_nic *efx = channel->efx; efx_qword_t event, *p_event; unsigned int read_ptr; - int ev_code; + int spent_tx = 0; int spent = 0; + int ev_code; if (quota <= 0) return spent; @@ -3126,7 +3133,11 @@ static int efx_ef10_ev_process(struct efx_channel *channel, int quota) } break; case ESE_DZ_EV_CODE_TX_EV: - efx_ef10_handle_tx_event(channel, &event); + spent_tx += efx_ef10_handle_tx_event(channel, &event); + if (spent_tx >= EFX_NAPI_MAX_TX) { + spent = quota; + goto out; + } break; case ESE_DZ_EV_CODE_DRIVER_EV: efx_ef10_handle_driver_event(channel, &event); diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index ad686c671ab8..fa1f7039a8e2 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -242,6 +242,8 @@ static void ef100_ev_read_ack(struct efx_channel *channel) efx_reg(channel->efx, ER_GZ_EVQ_INT_PRIME)); } +#define EFX_NAPI_MAX_TX 512 + static int ef100_ev_process(struct efx_channel *channel, int quota) { struct efx_nic *efx = channel->efx; @@ -249,6 +251,7 @@ static int ef100_ev_process(struct efx_channel *channel, int quota) bool evq_phase, old_evq_phase; unsigned int read_ptr; efx_qword_t *p_event; + int spent_tx = 0; int spent = 0; bool ev_phase; int ev_type; @@ -284,7 +287,9 @@ static int ef100_ev_process(struct efx_channel *channel, int quota) efx_mcdi_process_event(channel, p_event); break; case ESE_GZ_EF100_EV_TX_COMPLETION: - ef100_ev_tx(channel, p_event); + spent_tx += ef100_ev_tx(channel, p_event); + if (spent_tx >= EFX_NAPI_MAX_TX) + spent = quota; break; case ESE_GZ_EF100_EV_DRIVER: netif_info(efx, drv, efx->net_dev, diff --git a/drivers/net/ethernet/sfc/ef100_tx.c b/drivers/net/ethernet/sfc/ef100_tx.c index 29ffaf35559d..849e5555bd12 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.c +++ b/drivers/net/ethernet/sfc/ef100_tx.c @@ -346,7 +346,7 @@ void ef100_tx_write(struct efx_tx_queue *tx_queue) ef100_tx_push_buffers(tx_queue); } -void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) +int ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) { unsigned int tx_done = EFX_QWORD_FIELD(*p_event, ESF_GZ_EV_TXCMPL_NUM_DESC); @@ -357,7 +357,7 @@ void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) unsigned int tx_index = (tx_queue->read_count + tx_done - 1) & tx_queue->ptr_mask; - efx_xmit_done(tx_queue, tx_index); + return efx_xmit_done(tx_queue, tx_index); } /* Add a socket buffer to a TX queue diff --git a/drivers/net/ethernet/sfc/ef100_tx.h b/drivers/net/ethernet/sfc/ef100_tx.h index e9e11540fcde..d9a0819c5a72 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.h +++ b/drivers/net/ethernet/sfc/ef100_tx.h @@ -20,7 +20,7 @@ void ef100_tx_init(struct efx_tx_queue *tx_queue); void ef100_tx_write(struct efx_tx_queue *tx_queue); unsigned int ef100_tx_max_skb_descs(struct efx_nic *efx); -void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event); +int ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event); netdev_tx_t ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); int __ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb, diff --git a/drivers/net/ethernet/sfc/tx_common.c b/drivers/net/ethernet/sfc/tx_common.c index 67e789b96c43..755aa92bf823 100644 --- a/drivers/net/ethernet/sfc/tx_common.c +++ b/drivers/net/ethernet/sfc/tx_common.c @@ -249,7 +249,7 @@ void efx_xmit_done_check_empty(struct efx_tx_queue *tx_queue) } } -void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) +int efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) { unsigned int fill_level, pkts_compl = 0, bytes_compl = 0; unsigned int efv_pkts_compl = 0; @@ -279,6 +279,8 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) } efx_xmit_done_check_empty(tx_queue); + + return pkts_compl + efv_pkts_compl; } /* Remove buffers put into a tx_queue for the current packet. diff --git a/drivers/net/ethernet/sfc/tx_common.h b/drivers/net/ethernet/sfc/tx_common.h index d87aecbc7bf1..1e9f42938aac 100644 --- a/drivers/net/ethernet/sfc/tx_common.h +++ b/drivers/net/ethernet/sfc/tx_common.h @@ -28,7 +28,7 @@ static inline bool efx_tx_buffer_in_use(struct efx_tx_buffer *buffer) } void efx_xmit_done_check_empty(struct efx_tx_queue *tx_queue); -void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +int efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); void efx_enqueue_unwind(struct efx_tx_queue *tx_queue, unsigned int insert_count); -- cgit 1.4.1 From d28b7a87332f192653984e64a862cdf5a04106b4 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Wed, 14 Jun 2023 23:06:56 +0200 Subject: net: qca_spi: Avoid high load if QCA7000 is not available [ Upstream commit 92717c2356cb62c89e8a3dc37cbbab2502562524 ] In case the QCA7000 is not available via SPI (e.g. in reset), the driver will cause a high load. The reason for this is that the synchronization is never finished and schedule() is never called. Since the synchronization is not timing critical, it's safe to drop this from the scheduling condition. Signed-off-by: Stefan Wahren Fixes: 291ab06ecf67 ("net: qualcomm: new Ethernet over SPI driver for QCA7000") Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/qualcomm/qca_spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index c865a4be05ee..4a1b94e5a8ea 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -582,8 +582,7 @@ qcaspi_spi_thread(void *data) while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); if ((qca->intr_req == qca->intr_svc) && - (qca->txr.skb[qca->txr.head] == NULL) && - (qca->sync == QCASPI_SYNC_READY)) + !qca->txr.skb[qca->txr.head]) schedule(); set_current_state(TASK_RUNNING); -- cgit 1.4.1 From 4806f6b6b7a3bb321b6f5b7b3d748739dc282834 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:13 +0300 Subject: mmc: mtk-sd: fix deferred probing [ Upstream commit 0c4dc0f054891a2cbde0426b0c0fdf232d89f47f ] The driver overrides the error codes returned by platform_get_irq() to -EINVAL, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Fixes: 208489032bdd ("mmc: mediatek: Add Mediatek MMC driver") Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-4-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/mtk-sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 26bc59b5a7cc..425efb3fba04 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -2658,7 +2658,7 @@ static int msdc_drv_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { - ret = -EINVAL; + ret = host->irq; goto host_free; } -- cgit 1.4.1 From f73b380518b26f1925d0944fd6488af13b9df0b2 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:14 +0300 Subject: mmc: mvsdio: fix deferred probing [ Upstream commit 8d84064da0d4672e74f984e8710f27881137472c ] The driver overrides the error codes returned by platform_get_irq() to -ENXIO, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Fixes: 9ec36cafe43b ("of/irq: do irq resolution in platform_get_irq") Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-5-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/mvsdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 629efbe639c4..b4f6a0a2fcb5 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -704,7 +704,7 @@ static int mvsd_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENXIO; + return irq; mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); if (!mmc) { -- cgit 1.4.1 From 0057a905de5c4e1308d7262e27efae502dd3d8c6 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:15 +0300 Subject: mmc: omap: fix deferred probing [ Upstream commit aedf4ba1ad00aaa94c1b66c73ecaae95e2564b95 ] The driver overrides the error codes returned by platform_get_irq() to -ENXIO, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Fixes: 9ec36cafe43b ("of/irq: do irq resolution in platform_get_irq") Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-6-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 57d39283924d..cc2213ea324f 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1343,7 +1343,7 @@ static int mmc_omap_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENXIO; + return irq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->virt_base = devm_ioremap_resource(&pdev->dev, res); -- cgit 1.4.1 From 251101c32a0165872e216607302a0394378c6cfe Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:16 +0300 Subject: mmc: omap_hsmmc: fix deferred probing [ Upstream commit fb51b74a57859b707c3e8055ed0c25a7ca4f6a29 ] The driver overrides the error codes returned by platform_get_irq() to -ENXIO, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Fixes: 9ec36cafe43b ("of/irq: do irq resolution in platform_get_irq") Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-7-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/omap_hsmmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4bd744755205..2db3a16e63c4 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1791,9 +1791,11 @@ static int omap_hsmmc_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (res == NULL || irq < 0) + if (!res) return -ENXIO; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) -- cgit 1.4.1 From 645f89ee3e3eb145fa239153192cb7e1986dff1b Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:17 +0300 Subject: mmc: owl: fix deferred probing [ Upstream commit 3c482e1e830d79b9be8afb900a965135c01f7893 ] The driver overrides the error codes returned by platform_get_irq() to -EINVAL, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Fixes: ff65ffe46d28 ("mmc: Add Actions Semi Owl SoCs SD/MMC driver") Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-8-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/owl-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c index 3dc143b03939..679b8b0b310e 100644 --- a/drivers/mmc/host/owl-mmc.c +++ b/drivers/mmc/host/owl-mmc.c @@ -638,7 +638,7 @@ static int owl_mmc_probe(struct platform_device *pdev) owl_host->irq = platform_get_irq(pdev, 0); if (owl_host->irq < 0) { - ret = -EINVAL; + ret = owl_host->irq; goto err_release_channel; } -- cgit 1.4.1 From 565b8bd2905d1d1ca797ac718bc29d267facd277 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:18 +0300 Subject: mmc: sdhci-acpi: fix deferred probing [ Upstream commit b465dea5e1540c7d7b5211adaf94926980d3014b ] The driver overrides the error codes returned by platform_get_irq() to -EINVAL, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Fixes: 1b7ba57ecc86 ("mmc: sdhci-acpi: Handle return value of platform_get_irq") Signed-off-by: Sergey Shtylyov Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230617203622.6812-9-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/sdhci-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 4cca4c90769b..b917060a258a 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -829,7 +829,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) host->ops = &sdhci_acpi_ops_dflt; host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { - err = -EINVAL; + err = host->irq; goto err_free; } -- cgit 1.4.1 From 7e10fff133530eaa105024ad20cc654e0d0306c2 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:20 +0300 Subject: mmc: sh_mmcif: fix deferred probing [ Upstream commit 5b067d7f855c61df7f8e2e8ccbcee133c282415e ] The driver overrides the error codes returned by platform_get_irq() to -ENXIO, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating the error codes upstream. Fixes: 9ec36cafe43b ("of/irq: do irq resolution in platform_get_irq") Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-11-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 0fd4c9d644dd..5cf53348372a 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1400,7 +1400,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq_optional(pdev, 1); if (irq[0] < 0) - return -ENXIO; + return irq[0]; reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg)) -- cgit 1.4.1 From 6d1eec1f2dd71a218b7a579f862337f4c739ffef Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Jun 2023 23:36:22 +0300 Subject: mmc: usdhi60rol0: fix deferred probing [ Upstream commit 413db499730248431c1005b392e8ed82c4fa19bf ] The driver overrides the error codes returned by platform_get_irq_byname() to -ENODEV, so if it returns -EPROBE_DEFER, the driver will fail the probe permanently instead of the deferred probing. Switch to propagating error codes upstream. Fixes: 9ec36cafe43b ("of/irq: do irq resolution in platform_get_irq") Signed-off-by: Sergey Shtylyov Link: https://lore.kernel.org/r/20230617203622.6812-13-s.shtylyov@omp.ru Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/usdhi6rol0.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 99515be6e5e5..2032e4e1ee68 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1757,8 +1757,10 @@ static int usdhi6_probe(struct platform_device *pdev) irq_cd = platform_get_irq_byname(pdev, "card detect"); irq_sd = platform_get_irq_byname(pdev, "data"); irq_sdio = platform_get_irq_byname(pdev, "SDIO"); - if (irq_sd < 0 || irq_sdio < 0) - return -ENODEV; + if (irq_sd < 0) + return irq_sd; + if (irq_sdio < 0) + return irq_sdio; mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev); if (!mmc) -- cgit 1.4.1 From 7fd2e9a69e83ec7e6ba732519f7ebdf43a4668a9 Mon Sep 17 00:00:00 2001 From: Terin Stock Date: Fri, 9 Jun 2023 22:58:42 +0200 Subject: ipvs: align inner_mac_header for encapsulation [ Upstream commit d7fce52fdf96663ddc2eb21afecff3775588612a ] When using encapsulation the original packet's headers are copied to the inner headers. This preserves the space for an inner mac header, which is not used by the inner payloads for the encapsulation types supported by IPVS. If a packet is using GUE or GRE encapsulation and needs to be segmented, flow can be passed to __skb_udp_tunnel_segment() which calculates a negative tunnel header length. A negative tunnel header length causes pskb_may_pull() to fail, dropping the packet. This can be observed by attaching probes to ip_vs_in_hook(), __dev_queue_xmit(), and __skb_udp_tunnel_segment(): perf probe --add '__dev_queue_xmit skb->inner_mac_header \ skb->inner_network_header skb->mac_header skb->network_header' perf probe --add '__skb_udp_tunnel_segment:7 tnl_hlen' perf probe -m ip_vs --add 'ip_vs_in_hook skb->inner_mac_header \ skb->inner_network_header skb->mac_header skb->network_header' These probes the headers and tunnel header length for packets which traverse the IPVS encapsulation path. A TCP packet can be forced into the segmentation path by being smaller than a calculated clamped MSS, but larger than the advertised MSS. probe:ip_vs_in_hook: inner_mac_header=0x0 inner_network_header=0x0 mac_header=0x44 network_header=0x52 probe:ip_vs_in_hook: inner_mac_header=0x44 inner_network_header=0x52 mac_header=0x44 network_header=0x32 probe:dev_queue_xmit: inner_mac_header=0x44 inner_network_header=0x52 mac_header=0x44 network_header=0x32 probe:__skb_udp_tunnel_segment_L7: tnl_hlen=-2 When using veth-based encapsulation, the interfaces are set to be mac-less, which does not preserve space for an inner mac header. This prevents this issue from occurring. In our real-world testing of sending a 32KB file we observed operation time increasing from ~75ms for veth-based encapsulation to over 1.5s using IPVS encapsulation due to retries from dropped packets. This changeset modifies the packet on the encapsulation path in ip_vs_tunnel_xmit() and ip_vs_tunnel_xmit_v6() to remove the inner mac header offset. This fixes UDP segmentation for both encapsulation types, and corrects the inner headers for any IPIP flows that may use it. Fixes: 84c0d5e96f3a ("ipvs: allow tunneling with gue encapsulation") Signed-off-by: Terin Stock Acked-by: Julian Anastasov Acked-by: Simon Horman Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipvs/ip_vs_xmit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 029171379884..7243079ef354 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1225,6 +1225,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb->transport_header = skb->network_header; skb_set_inner_ipproto(skb, next_protocol); + skb_set_inner_mac_header(skb, skb_inner_network_offset(skb)); if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { bool check = false; @@ -1373,6 +1374,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, skb->transport_header = skb->network_header; skb_set_inner_ipproto(skb, next_protocol); + skb_set_inner_mac_header(skb, skb_inner_network_offset(skb)); if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { bool check = false; -- cgit 1.4.1 From a4e4c7190126de5cc6824f657b434f4b972c431b Mon Sep 17 00:00:00 2001 From: Arınç ÜNAL Date: Sat, 17 Jun 2023 09:26:45 +0300 Subject: net: dsa: mt7530: fix trapping frames on non-MT7621 SoC MT7530 switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4ae90f90e4909e3014e2dc6a0627964617a7b824 ] All MT7530 switch IP variants share the MT7530_MFC register, but the current driver only writes it for the switch variant that is integrated in the MT7621 SoC. Modify the code to include all MT7530 derivatives. Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") Suggested-by: Vladimir Oltean Signed-off-by: Arınç ÜNAL Reviewed-by: Vladimir Oltean Reviewed-by: Russell King (Oracle) Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/mt7530.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 855220c5ce33..886dc7d3d85d 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1020,7 +1020,7 @@ mt753x_cpu_port_enable(struct dsa_switch *ds, int port) UNU_FFP(BIT(port))); /* Set CPU port number */ - if (priv->id == ID_MT7621) + if (priv->id == ID_MT7530 || priv->id == ID_MT7621) mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port)); /* CPU port gets connected to all user ports of -- cgit 1.4.1 From a50f84af215ee26cf9d54b871a156d15ac02df03 Mon Sep 17 00:00:00 2001 From: Arınç ÜNAL Date: Sat, 17 Jun 2023 09:26:46 +0300 Subject: net: dsa: mt7530: fix handling of BPDUs on MT7530 switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d7c66073559386b836bded7cdc8b66ee5c049129 ] BPDUs are link-local frames, therefore they must be trapped to the CPU port. Currently, the MT7530 switch treats BPDUs as regular multicast frames, therefore flooding them to user ports. To fix this, set BPDUs to be trapped to the CPU port. Group this on mt7530_setup() and mt7531_setup_common() into mt753x_trap_frames() and call that. Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") Signed-off-by: Arınç ÜNAL Reviewed-by: Vladimir Oltean Reviewed-by: Russell King (Oracle) Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/mt7530.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 886dc7d3d85d..ec43edcaa7d9 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -998,6 +998,14 @@ unlock_exit: mutex_unlock(&priv->reg_mutex); } +static void +mt753x_trap_frames(struct mt7530_priv *priv) +{ + /* Trap BPDUs to the CPU port(s) */ + mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, + MT753X_BPDU_CPU_ONLY); +} + static int mt753x_cpu_port_enable(struct dsa_switch *ds, int port) { @@ -2219,6 +2227,8 @@ mt7530_setup(struct dsa_switch *ds) priv->p6_interface = PHY_INTERFACE_MODE_NA; + mt753x_trap_frames(priv); + /* Enable and reset MIB counters */ mt7530_mib_reset(ds); @@ -2325,8 +2335,8 @@ mt7531_setup_common(struct dsa_switch *ds) BIT(cpu_dp->index)); break; } - mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, - MT753X_BPDU_CPU_ONLY); + + mt753x_trap_frames(priv); /* Enable and reset MIB counters */ mt7530_mib_reset(ds); -- cgit 1.4.1 From 50f689918db4bbb4f380189e98bbdd64e87416dd Mon Sep 17 00:00:00 2001 From: Arınç ÜNAL Date: Sat, 17 Jun 2023 09:26:47 +0300 Subject: net: dsa: mt7530: fix handling of LLDP frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8332cf6fd7c7087dbc2067115b33979c9851bbc4 ] LLDP frames are link-local frames, therefore they must be trapped to the CPU port. Currently, the MT753X switches treat LLDP frames as regular multicast frames, therefore flooding them to user ports. To fix this, set LLDP frames to be trapped to the CPU port(s). Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") Signed-off-by: Arınç ÜNAL Reviewed-by: Vladimir Oltean Reviewed-by: Russell King (Oracle) Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/mt7530.c | 4 ++++ drivers/net/dsa/mt7530.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index ec43edcaa7d9..51d2ef0dc835 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1004,6 +1004,10 @@ mt753x_trap_frames(struct mt7530_priv *priv) /* Trap BPDUs to the CPU port(s) */ mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, MT753X_BPDU_CPU_ONLY); + + /* Trap LLDP frames with :0E MAC DA to the CPU port(s) */ + mt7530_rmw(priv, MT753X_RGAC2, MT753X_R0E_PORT_FW_MASK, + MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY)); } static int diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index e8d966435350..9a45663d8b4e 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -65,6 +65,11 @@ enum mt753x_id { #define MT753X_BPC 0x24 #define MT753X_BPDU_PORT_FW_MASK GENMASK(2, 0) +/* Register for :03 and :0E MAC DA frame control */ +#define MT753X_RGAC2 0x2c +#define MT753X_R0E_PORT_FW_MASK GENMASK(18, 16) +#define MT753X_R0E_PORT_FW(x) FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) + enum mt753x_bpdu_port_fw { MT753X_BPDU_FOLLOW_MFC, MT753X_BPDU_CPU_EXCLUDE = 4, -- cgit 1.4.1 From f5b6dbec26f1b58d9a27cf5180e1fb2f1219f52a Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Fri, 16 Jun 2023 17:45:49 +0100 Subject: be2net: Extend xmit workaround to BE3 chip [ Upstream commit 7580e0a78eb29e7bb1a772eba4088250bbb70d41 ] We have seen a bug where the NIC incorrectly changes the length in the IP header of a padded packet to include the padding bytes. The driver already has a workaround for this so do the workaround for this NIC too. This resolves the issue. The NIC in question identifies itself as follows: [ 8.828494] be2net 0000:02:00.0: FW version is 10.7.110.31 [ 8.834759] be2net 0000:02:00.0: Emulex OneConnect(be3): PF FLEX10 port 1 02:00.0 Ethernet controller: Emulex Corporation OneConnect 10Gb NIC (be3) (rev 01) Fixes: ca34fe38f06d ("be2net: fix wrong usage of adapter->generation") Signed-off-by: Ross Lagerwall Link: https://lore.kernel.org/r/20230616164549.2863037-1-ross.lagerwall@citrix.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/emulex/benet/be_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a92a74761546..5d39df845265 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1136,8 +1136,8 @@ static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter, eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? VLAN_ETH_HLEN : ETH_HLEN; if (skb->len <= 60 && - (lancer_chip(adapter) || skb_vlan_tag_present(skb)) && - is_ipv4_pkt(skb)) { + (lancer_chip(adapter) || BE3_chip(adapter) || + skb_vlan_tag_present(skb)) && is_ipv4_pkt(skb)) { ip = (struct iphdr *)ip_hdr(skb); pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); } -- cgit 1.4.1 From 891cd2edddc76c58e842706ad27e2ff96000bd5d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 14:45:22 +0200 Subject: netfilter: nf_tables: fix chain binding transaction logic [ Upstream commit 4bedf9eee016286c835e3d8fa981ddece5338795 ] Add bound flag to rule and chain transactions as in 6a0a8d10a366 ("netfilter: nf_tables: use-after-free in failing rule with bound set") to skip them in case that the chain is already bound from the abort path. This patch fixes an imbalance in the chain use refcnt that triggers a WARN_ON on the table and chain destroy path. This patch also disallows nested chain bindings, which is not supported from userspace. The logic to deal with chain binding in nft_data_hold() and nft_data_release() is not correct. The NFT_TRANS_PREPARE state needs a special handling in case a chain is bound but next expressions in the same rule fail to initialize as described by 1240eb93f061 ("netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE"). The chain is left bound if rule construction fails, so the objects stored in this chain (and the chain itself) are released by the transaction records from the abort path, follow up patch ("netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain") completes this error handling. When deleting an existing rule, chain bound flag is set off so the rule expression .destroy path releases the objects. Fixes: d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_tables.h | 21 +++++++++- net/netfilter/nf_tables_api.c | 86 ++++++++++++++++++++++++-------------- net/netfilter/nft_immediate.c | 87 +++++++++++++++++++++++++++++++++++---- 3 files changed, 153 insertions(+), 41 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 22e96b7e1b44..c13a84c0b496 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1002,7 +1002,10 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule) return (void *)&rule->data[rule->dlen]; } -void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule); +void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule); +void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule, + enum nft_trans_phase phase); +void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule); static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext, struct nft_regs *regs, @@ -1085,6 +1088,7 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set, const struct nft_set_iter *iter, struct nft_set_elem *elem); int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set); +int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); enum nft_chain_types { NFT_CHAIN_T_DEFAULT = 0, @@ -1121,11 +1125,17 @@ int nft_chain_validate_dependency(const struct nft_chain *chain, int nft_chain_validate_hooks(const struct nft_chain *chain, unsigned int hook_flags); +static inline bool nft_chain_binding(const struct nft_chain *chain) +{ + return chain->flags & NFT_CHAIN_BINDING; +} + static inline bool nft_chain_is_bound(struct nft_chain *chain) { return (chain->flags & NFT_CHAIN_BINDING) && chain->bound; } +int nft_chain_add(struct nft_table *table, struct nft_chain *chain); void nft_chain_del(struct nft_chain *chain); void nf_tables_chain_destroy(struct nft_ctx *ctx); @@ -1560,6 +1570,7 @@ struct nft_trans_rule { struct nft_rule *rule; struct nft_flow_rule *flow; u32 rule_id; + bool bound; }; #define nft_trans_rule(trans) \ @@ -1568,6 +1579,8 @@ struct nft_trans_rule { (((struct nft_trans_rule *)trans->data)->flow) #define nft_trans_rule_id(trans) \ (((struct nft_trans_rule *)trans->data)->rule_id) +#define nft_trans_rule_bound(trans) \ + (((struct nft_trans_rule *)trans->data)->bound) struct nft_trans_set { struct nft_set *set; @@ -1592,13 +1605,17 @@ struct nft_trans_set { (((struct nft_trans_set *)trans->data)->gc_int) struct nft_trans_chain { + struct nft_chain *chain; bool update; char *name; struct nft_stats __percpu *stats; u8 policy; + bool bound; u32 chain_id; }; +#define nft_trans_chain(trans) \ + (((struct nft_trans_chain *)trans->data)->chain) #define nft_trans_chain_update(trans) \ (((struct nft_trans_chain *)trans->data)->update) #define nft_trans_chain_name(trans) \ @@ -1607,6 +1624,8 @@ struct nft_trans_chain { (((struct nft_trans_chain *)trans->data)->stats) #define nft_trans_chain_policy(trans) \ (((struct nft_trans_chain *)trans->data)->policy) +#define nft_trans_chain_bound(trans) \ + (((struct nft_trans_chain *)trans->data)->bound) #define nft_trans_chain_id(trans) \ (((struct nft_trans_chain *)trans->data)->chain_id) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 13d4913266b4..8f8e315691dd 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -195,6 +195,48 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) } } +static void nft_chain_trans_bind(const struct nft_ctx *ctx, struct nft_chain *chain) +{ + struct nftables_pernet *nft_net; + struct net *net = ctx->net; + struct nft_trans *trans; + + if (!nft_chain_binding(chain)) + return; + + nft_net = nft_pernet(net); + list_for_each_entry_reverse(trans, &nft_net->commit_list, list) { + switch (trans->msg_type) { + case NFT_MSG_NEWCHAIN: + if (nft_trans_chain(trans) == chain) + nft_trans_chain_bound(trans) = true; + break; + case NFT_MSG_NEWRULE: + if (trans->ctx.chain == chain) + nft_trans_rule_bound(trans) = true; + break; + } + } +} + +int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) +{ + if (!nft_chain_binding(chain)) + return 0; + + if (nft_chain_binding(ctx->chain)) + return -EOPNOTSUPP; + + if (chain->bound) + return -EBUSY; + + chain->bound = true; + chain->use++; + nft_chain_trans_bind(ctx, chain); + + return 0; +} + static int nft_netdev_register_hooks(struct net *net, struct list_head *hook_list) { @@ -340,8 +382,9 @@ static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type) ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID])); } } - + nft_trans_chain(trans) = ctx->chain; nft_trans_commit_list_add_tail(ctx->net, trans); + return trans; } @@ -359,8 +402,7 @@ static int nft_delchain(struct nft_ctx *ctx) return 0; } -static void nft_rule_expr_activate(const struct nft_ctx *ctx, - struct nft_rule *rule) +void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule) { struct nft_expr *expr; @@ -373,9 +415,8 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx, } } -static void nft_rule_expr_deactivate(const struct nft_ctx *ctx, - struct nft_rule *rule, - enum nft_trans_phase phase) +void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule, + enum nft_trans_phase phase) { struct nft_expr *expr; @@ -2188,7 +2229,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family, return 0; } -static int nft_chain_add(struct nft_table *table, struct nft_chain *chain) +int nft_chain_add(struct nft_table *table, struct nft_chain *chain) { int err; @@ -3315,8 +3356,7 @@ err_fill_rule_info: return err; } -static void nf_tables_rule_destroy(const struct nft_ctx *ctx, - struct nft_rule *rule) +void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule) { struct nft_expr *expr, *next; @@ -3333,7 +3373,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, kfree(rule); } -void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) +static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) { nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE); nf_tables_rule_destroy(ctx, rule); @@ -6446,7 +6486,6 @@ static int nf_tables_newsetelem(struct sk_buff *skb, void nft_data_hold(const struct nft_data *data, enum nft_data_types type) { struct nft_chain *chain; - struct nft_rule *rule; if (type == NFT_DATA_VERDICT) { switch (data->verdict.code) { @@ -6454,15 +6493,6 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type) case NFT_GOTO: chain = data->verdict.chain; chain->use++; - - if (!nft_chain_is_bound(chain)) - break; - - chain->table->use++; - list_for_each_entry(rule, &chain->rules, list) - chain->use++; - - nft_chain_add(chain->table, chain); break; } } @@ -9368,7 +9398,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) kfree(nft_trans_chain_name(trans)); nft_trans_destroy(trans); } else { - if (nft_chain_is_bound(trans->ctx.chain)) { + if (nft_trans_chain_bound(trans)) { nft_trans_destroy(trans); break; } @@ -9385,6 +9415,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) nft_trans_destroy(trans); break; case NFT_MSG_NEWRULE: + if (nft_trans_rule_bound(trans)) { + nft_trans_destroy(trans); + break; + } trans->ctx.chain->use--; list_del_rcu(&nft_trans_rule(trans)->list); nft_rule_expr_deactivate(&trans->ctx, @@ -9943,22 +9977,12 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, static void nft_verdict_uninit(const struct nft_data *data) { struct nft_chain *chain; - struct nft_rule *rule; switch (data->verdict.code) { case NFT_JUMP: case NFT_GOTO: chain = data->verdict.chain; chain->use--; - - if (!nft_chain_is_bound(chain)) - break; - - chain->table->use--; - list_for_each_entry(rule, &chain->rules, list) - chain->use--; - - nft_chain_del(chain); break; } } diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 5f28b21abc7d..457fc1e21841 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -76,11 +76,9 @@ static int nft_immediate_init(const struct nft_ctx *ctx, switch (priv->data.verdict.code) { case NFT_JUMP: case NFT_GOTO: - if (nft_chain_is_bound(chain)) { - err = -EBUSY; - goto err1; - } - chain->bound = true; + err = nf_tables_bind_chain(ctx, chain); + if (err < 0) + return err; break; default: break; @@ -98,6 +96,31 @@ static void nft_immediate_activate(const struct nft_ctx *ctx, const struct nft_expr *expr) { const struct nft_immediate_expr *priv = nft_expr_priv(expr); + const struct nft_data *data = &priv->data; + struct nft_ctx chain_ctx; + struct nft_chain *chain; + struct nft_rule *rule; + + if (priv->dreg == NFT_REG_VERDICT) { + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + chain = data->verdict.chain; + if (!nft_chain_binding(chain)) + break; + + chain_ctx = *ctx; + chain_ctx.chain = chain; + + list_for_each_entry(rule, &chain->rules, list) + nft_rule_expr_activate(&chain_ctx, rule); + + nft_clear(ctx->net, chain); + break; + default: + break; + } + } return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg)); } @@ -107,6 +130,40 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx, enum nft_trans_phase phase) { const struct nft_immediate_expr *priv = nft_expr_priv(expr); + const struct nft_data *data = &priv->data; + struct nft_ctx chain_ctx; + struct nft_chain *chain; + struct nft_rule *rule; + + if (priv->dreg == NFT_REG_VERDICT) { + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + chain = data->verdict.chain; + if (!nft_chain_binding(chain)) + break; + + chain_ctx = *ctx; + chain_ctx.chain = chain; + + list_for_each_entry(rule, &chain->rules, list) + nft_rule_expr_deactivate(&chain_ctx, rule, phase); + + switch (phase) { + case NFT_TRANS_PREPARE: + nft_deactivate_next(ctx->net, chain); + break; + default: + nft_chain_del(chain); + chain->bound = false; + chain->table->use--; + break; + } + break; + default: + break; + } + } if (phase == NFT_TRANS_COMMIT) return; @@ -131,15 +188,27 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx, case NFT_GOTO: chain = data->verdict.chain; - if (!nft_chain_is_bound(chain)) + if (!nft_chain_binding(chain)) + break; + + /* Rule construction failed, but chain is already bound: + * let the transaction records release this chain and its rules. + */ + if (chain->bound) { + chain->use--; break; + } + /* Rule has been deleted, release chain and its rules. */ chain_ctx = *ctx; chain_ctx.chain = chain; - list_for_each_entry_safe(rule, n, &chain->rules, list) - nf_tables_rule_release(&chain_ctx, rule); - + chain->use--; + list_for_each_entry_safe(rule, n, &chain->rules, list) { + chain->use--; + list_del(&rule->list); + nf_tables_rule_destroy(&chain_ctx, rule); + } nf_tables_chain_destroy(&chain_ctx); break; default: -- cgit 1.4.1 From df27be7c15301ea33ddaafc8d5db9bbe29bd930d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 14:45:26 +0200 Subject: netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain [ Upstream commit 26b5a5712eb85e253724e56a54c17f8519bd8e4e ] Add a new state to deal with rule expressions deactivation from the newrule error path, otherwise the anonymous set remains in the list in inactive state for the next generation. Mark the set/chain transaction as unbound so the abort path releases this object, set it as inactive in the next generation so it is not reachable anymore from this transaction and reference counter is dropped. Fixes: 1240eb93f061 ("netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_tables_api.c | 45 +++++++++++++++++++++++++++++++++------ net/netfilter/nft_immediate.c | 3 +++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index c13a84c0b496..984f7d308773 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -895,6 +895,7 @@ struct nft_expr_type { enum nft_trans_phase { NFT_TRANS_PREPARE, + NFT_TRANS_PREPARE_ERROR, NFT_TRANS_ABORT, NFT_TRANS_COMMIT, NFT_TRANS_RELEASE @@ -1089,6 +1090,7 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_elem *elem); int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set); int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); +void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); enum nft_chain_types { NFT_CHAIN_T_DEFAULT = 0, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 8f8e315691dd..72bb2b397748 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -171,7 +171,8 @@ static void nft_trans_destroy(struct nft_trans *trans) kfree(trans); } -static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) +static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set, + bool bind) { struct nftables_pernet *nft_net; struct net *net = ctx->net; @@ -185,17 +186,28 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) switch (trans->msg_type) { case NFT_MSG_NEWSET: if (nft_trans_set(trans) == set) - nft_trans_set_bound(trans) = true; + nft_trans_set_bound(trans) = bind; break; case NFT_MSG_NEWSETELEM: if (nft_trans_elem_set(trans) == set) - nft_trans_elem_set_bound(trans) = true; + nft_trans_elem_set_bound(trans) = bind; break; } } } -static void nft_chain_trans_bind(const struct nft_ctx *ctx, struct nft_chain *chain) +static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) +{ + return __nft_set_trans_bind(ctx, set, true); +} + +static void nft_set_trans_unbind(const struct nft_ctx *ctx, struct nft_set *set) +{ + return __nft_set_trans_bind(ctx, set, false); +} + +static void __nft_chain_trans_bind(const struct nft_ctx *ctx, + struct nft_chain *chain, bool bind) { struct nftables_pernet *nft_net; struct net *net = ctx->net; @@ -209,16 +221,22 @@ static void nft_chain_trans_bind(const struct nft_ctx *ctx, struct nft_chain *ch switch (trans->msg_type) { case NFT_MSG_NEWCHAIN: if (nft_trans_chain(trans) == chain) - nft_trans_chain_bound(trans) = true; + nft_trans_chain_bound(trans) = bind; break; case NFT_MSG_NEWRULE: if (trans->ctx.chain == chain) - nft_trans_rule_bound(trans) = true; + nft_trans_rule_bound(trans) = bind; break; } } } +static void nft_chain_trans_bind(const struct nft_ctx *ctx, + struct nft_chain *chain) +{ + __nft_chain_trans_bind(ctx, chain, true); +} + int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) { if (!nft_chain_binding(chain)) @@ -237,6 +255,11 @@ int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) return 0; } +void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) +{ + __nft_chain_trans_bind(ctx, chain, false); +} + static int nft_netdev_register_hooks(struct net *net, struct list_head *hook_list) { @@ -3709,7 +3732,7 @@ err_destroy_flow_rule: if (flow) nft_flow_rule_destroy(flow); err_release_rule: - nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); + nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR); nf_tables_rule_destroy(&ctx, rule); err_release_expr: for (i = 0; i < n; i++) { @@ -4990,6 +5013,13 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set, enum nft_trans_phase phase) { switch (phase) { + case NFT_TRANS_PREPARE_ERROR: + nft_set_trans_unbind(ctx, set); + if (nft_set_is_anonymous(set)) + nft_deactivate_next(ctx->net, set); + + set->use--; + break; case NFT_TRANS_PREPARE: if (nft_set_is_anonymous(set)) nft_deactivate_next(ctx->net, set); @@ -7494,6 +7524,7 @@ void nf_tables_deactivate_flowtable(const struct nft_ctx *ctx, enum nft_trans_phase phase) { switch (phase) { + case NFT_TRANS_PREPARE_ERROR: case NFT_TRANS_PREPARE: case NFT_TRANS_ABORT: case NFT_TRANS_RELEASE: diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 457fc1e21841..900e75e8c346 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -150,6 +150,9 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx, nft_rule_expr_deactivate(&chain_ctx, rule, phase); switch (phase) { + case NFT_TRANS_PREPARE_ERROR: + nf_tables_unbind_chain(ctx, chain); + fallthrough; case NFT_TRANS_PREPARE: nft_deactivate_next(ctx->net, chain); break; -- cgit 1.4.1 From d60be2da67d172aecf866302c91ea11533eca4d9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 14:51:49 +0200 Subject: netfilter: nf_tables: drop map element references from preparation phase [ Upstream commit 628bd3e49cba1c066228e23d71a852c23e26da73 ] set .destroy callback releases the references to other objects in maps. This is very late and it results in spurious EBUSY errors. Drop refcount from the preparation phase instead, update set backend not to drop reference counter from set .destroy path. Exceptions: NFT_TRANS_PREPARE_ERROR does not require to drop the reference counter because the transaction abort path releases the map references for each element since the set is unbound. The abort path also deals with releasing reference counter for new elements added to unbound sets. Fixes: 591054469b3e ("netfilter: nf_tables: revisit chain/object refcounting from elements") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_tables.h | 5 +- net/netfilter/nf_tables_api.c | 147 +++++++++++++++++++++++++++++++++----- net/netfilter/nft_set_bitmap.c | 5 +- net/netfilter/nft_set_hash.c | 23 ++++-- net/netfilter/nft_set_pipapo.c | 14 ++-- net/netfilter/nft_set_rbtree.c | 5 +- 6 files changed, 167 insertions(+), 32 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 984f7d308773..bbcd558f1934 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -467,7 +467,8 @@ struct nft_set_ops { int (*init)(const struct nft_set *set, const struct nft_set_desc *desc, const struct nlattr * const nla[]); - void (*destroy)(const struct nft_set *set); + void (*destroy)(const struct nft_ctx *ctx, + const struct nft_set *set); void (*gc_init)(const struct nft_set *set); unsigned int elemsize; @@ -804,6 +805,8 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set, struct nft_expr *expr_array[]); void nft_set_elem_destroy(const struct nft_set *set, void *elem, bool destroy_expr); +void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, void *elem); /** * struct nft_set_gc_batch_head - nf_tables set garbage collection batch diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 72bb2b397748..337bab663540 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -561,6 +561,58 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type, return __nft_trans_set_add(ctx, msg_type, set, NULL); } +static void nft_setelem_data_deactivate(const struct net *net, + const struct nft_set *set, + struct nft_set_elem *elem); + +static int nft_mapelem_deactivate(const struct nft_ctx *ctx, + struct nft_set *set, + const struct nft_set_iter *iter, + struct nft_set_elem *elem) +{ + nft_setelem_data_deactivate(ctx->net, set, elem); + + return 0; +} + +struct nft_set_elem_catchall { + struct list_head list; + struct rcu_head rcu; + void *elem; +}; + +static void nft_map_catchall_deactivate(const struct nft_ctx *ctx, + struct nft_set *set) +{ + u8 genmask = nft_genmask_next(ctx->net); + struct nft_set_elem_catchall *catchall; + struct nft_set_elem elem; + struct nft_set_ext *ext; + + list_for_each_entry(catchall, &set->catchall_list, list) { + ext = nft_set_elem_ext(set, catchall->elem); + if (!nft_set_elem_active(ext, genmask)) + continue; + + elem.priv = catchall->elem; + nft_setelem_data_deactivate(ctx->net, set, &elem); + break; + } +} + +static void nft_map_deactivate(const struct nft_ctx *ctx, struct nft_set *set) +{ + struct nft_set_iter iter = { + .genmask = nft_genmask_next(ctx->net), + .fn = nft_mapelem_deactivate, + }; + + set->ops->walk(ctx, set, &iter); + WARN_ON_ONCE(iter.err); + + nft_map_catchall_deactivate(ctx, set); +} + static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set) { int err; @@ -569,6 +621,9 @@ static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set) if (err < 0) return err; + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(ctx, set); + nft_deactivate_next(ctx->net, set); ctx->table->use--; @@ -3484,12 +3539,6 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set, return 0; } -struct nft_set_elem_catchall { - struct list_head list; - struct rcu_head rcu; - void *elem; -}; - int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set) { u8 genmask = nft_genmask_next(ctx->net); @@ -4808,7 +4857,7 @@ err_set_expr_alloc: for (i = 0; i < set->num_exprs; i++) nft_expr_destroy(&ctx, set->exprs[i]); err_set_destroy: - ops->destroy(set); + ops->destroy(&ctx, set); err_set_init: kfree(set->name); err_set_name: @@ -4823,7 +4872,7 @@ static void nft_set_catchall_destroy(const struct nft_ctx *ctx, list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { list_del_rcu(&catchall->list); - nft_set_elem_destroy(set, catchall->elem, true); + nf_tables_set_elem_destroy(ctx, set, catchall->elem); kfree_rcu(catchall, rcu); } } @@ -4838,7 +4887,7 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) for (i = 0; i < set->num_exprs; i++) nft_expr_destroy(ctx, set->exprs[i]); - set->ops->destroy(set); + set->ops->destroy(ctx, set); nft_set_catchall_destroy(ctx, set); kfree(set->name); kvfree(set); @@ -4999,10 +5048,60 @@ static void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, } } +static void nft_setelem_data_activate(const struct net *net, + const struct nft_set *set, + struct nft_set_elem *elem); + +static int nft_mapelem_activate(const struct nft_ctx *ctx, + struct nft_set *set, + const struct nft_set_iter *iter, + struct nft_set_elem *elem) +{ + nft_setelem_data_activate(ctx->net, set, elem); + + return 0; +} + +static void nft_map_catchall_activate(const struct nft_ctx *ctx, + struct nft_set *set) +{ + u8 genmask = nft_genmask_next(ctx->net); + struct nft_set_elem_catchall *catchall; + struct nft_set_elem elem; + struct nft_set_ext *ext; + + list_for_each_entry(catchall, &set->catchall_list, list) { + ext = nft_set_elem_ext(set, catchall->elem); + if (!nft_set_elem_active(ext, genmask)) + continue; + + elem.priv = catchall->elem; + nft_setelem_data_activate(ctx->net, set, &elem); + break; + } +} + +static void nft_map_activate(const struct nft_ctx *ctx, struct nft_set *set) +{ + struct nft_set_iter iter = { + .genmask = nft_genmask_next(ctx->net), + .fn = nft_mapelem_activate, + }; + + set->ops->walk(ctx, set, &iter); + WARN_ON_ONCE(iter.err); + + nft_map_catchall_activate(ctx, set); +} + void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set) { - if (nft_set_is_anonymous(set)) + if (nft_set_is_anonymous(set)) { + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_activate(ctx, set); + nft_clear(ctx->net, set); + } set->use++; } @@ -5021,13 +5120,20 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set, set->use--; break; case NFT_TRANS_PREPARE: - if (nft_set_is_anonymous(set)) - nft_deactivate_next(ctx->net, set); + if (nft_set_is_anonymous(set)) { + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(ctx, set); + nft_deactivate_next(ctx->net, set); + } set->use--; return; case NFT_TRANS_ABORT: case NFT_TRANS_RELEASE: + if (nft_set_is_anonymous(set) && + set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(ctx, set); + set->use--; fallthrough; default: @@ -5780,6 +5886,7 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, __nft_set_elem_expr_destroy(ctx, expr); } +/* Drop references and destroy. Called from gc, dynset and abort path. */ void nft_set_elem_destroy(const struct nft_set *set, void *elem, bool destroy_expr) { @@ -5801,11 +5908,11 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem, } EXPORT_SYMBOL_GPL(nft_set_elem_destroy); -/* Only called from commit path, nft_setelem_data_deactivate() already deals - * with the refcounting from the preparation phase. +/* Destroy element. References have been already dropped in the preparation + * path via nft_setelem_data_deactivate(). */ -static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, - const struct nft_set *set, void *elem) +void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, void *elem) { struct nft_set_ext *ext = nft_set_elem_ext(set, elem); @@ -6438,7 +6545,7 @@ err_elem_free: if (obj) obj->use--; err_elem_userdata: - nf_tables_set_elem_destroy(ctx, set, elem.priv); + nft_set_elem_destroy(set, elem.priv, true); err_parse_data: if (nla[NFTA_SET_ELEM_DATA] != NULL) nft_data_release(&elem.data.val, desc.type); @@ -9482,6 +9589,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) case NFT_MSG_DELSET: trans->ctx.table->use++; nft_clear(trans->ctx.net, nft_trans_set(trans)); + if (nft_trans_set(trans)->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_activate(&trans->ctx, nft_trans_set(trans)); + nft_trans_destroy(trans); break; case NFT_MSG_NEWSETELEM: @@ -10248,6 +10358,9 @@ static void __nft_release_table(struct net *net, struct nft_table *table) list_for_each_entry_safe(set, ns, &table->sets, list) { list_del(&set->list); table->use--; + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(&ctx, set); + nft_set_destroy(&ctx, set); } list_for_each_entry_safe(obj, ne, &table->objects, list) { diff --git a/net/netfilter/nft_set_bitmap.c b/net/netfilter/nft_set_bitmap.c index 96081ac8d2b4..1e5e7a181e0b 100644 --- a/net/netfilter/nft_set_bitmap.c +++ b/net/netfilter/nft_set_bitmap.c @@ -271,13 +271,14 @@ static int nft_bitmap_init(const struct nft_set *set, return 0; } -static void nft_bitmap_destroy(const struct nft_set *set) +static void nft_bitmap_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_bitmap *priv = nft_set_priv(set); struct nft_bitmap_elem *be, *n; list_for_each_entry_safe(be, n, &priv->list, head) - nft_set_elem_destroy(set, be, true); + nf_tables_set_elem_destroy(ctx, set, be); } static bool nft_bitmap_estimate(const struct nft_set_desc *desc, u32 features, diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 76de6c8d9865..0b73cb0e752f 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -400,19 +400,31 @@ static int nft_rhash_init(const struct nft_set *set, return 0; } +struct nft_rhash_ctx { + const struct nft_ctx ctx; + const struct nft_set *set; +}; + static void nft_rhash_elem_destroy(void *ptr, void *arg) { - nft_set_elem_destroy(arg, ptr, true); + struct nft_rhash_ctx *rhash_ctx = arg; + + nf_tables_set_elem_destroy(&rhash_ctx->ctx, rhash_ctx->set, ptr); } -static void nft_rhash_destroy(const struct nft_set *set) +static void nft_rhash_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_rhash *priv = nft_set_priv(set); + struct nft_rhash_ctx rhash_ctx = { + .ctx = *ctx, + .set = set, + }; cancel_delayed_work_sync(&priv->gc_work); rcu_barrier(); rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy, - (void *)set); + (void *)&rhash_ctx); } /* Number of buckets is stored in u32, so cap our result to 1U<<31 */ @@ -643,7 +655,8 @@ static int nft_hash_init(const struct nft_set *set, return 0; } -static void nft_hash_destroy(const struct nft_set *set) +static void nft_hash_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_hash *priv = nft_set_priv(set); struct nft_hash_elem *he; @@ -653,7 +666,7 @@ static void nft_hash_destroy(const struct nft_set *set) for (i = 0; i < priv->buckets; i++) { hlist_for_each_entry_safe(he, next, &priv->table[i], node) { hlist_del_rcu(&he->node); - nft_set_elem_destroy(set, he, true); + nf_tables_set_elem_destroy(ctx, set, he); } } } diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 15e451dc3fc4..c867b5b772e8 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -2148,10 +2148,12 @@ out_scratch: /** * nft_set_pipapo_match_destroy() - Destroy elements from key mapping array + * @ctx: context * @set: nftables API set representation * @m: matching data pointing to key mapping array */ -static void nft_set_pipapo_match_destroy(const struct nft_set *set, +static void nft_set_pipapo_match_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, struct nft_pipapo_match *m) { struct nft_pipapo_field *f; @@ -2168,15 +2170,17 @@ static void nft_set_pipapo_match_destroy(const struct nft_set *set, e = f->mt[r].e; - nft_set_elem_destroy(set, e, true); + nf_tables_set_elem_destroy(ctx, set, e); } } /** * nft_pipapo_destroy() - Free private data for set and all committed elements + * @ctx: context * @set: nftables API set representation */ -static void nft_pipapo_destroy(const struct nft_set *set) +static void nft_pipapo_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_pipapo *priv = nft_set_priv(set); struct nft_pipapo_match *m; @@ -2186,7 +2190,7 @@ static void nft_pipapo_destroy(const struct nft_set *set) if (m) { rcu_barrier(); - nft_set_pipapo_match_destroy(set, m); + nft_set_pipapo_match_destroy(ctx, set, m); #ifdef NFT_PIPAPO_ALIGN free_percpu(m->scratch_aligned); @@ -2203,7 +2207,7 @@ static void nft_pipapo_destroy(const struct nft_set *set) m = priv->clone; if (priv->dirty) - nft_set_pipapo_match_destroy(set, m); + nft_set_pipapo_match_destroy(ctx, set, m); #ifdef NFT_PIPAPO_ALIGN free_percpu(priv->clone->scratch_aligned); diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 2f114aa10f1a..5c05c9b990fb 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -664,7 +664,8 @@ static int nft_rbtree_init(const struct nft_set *set, return 0; } -static void nft_rbtree_destroy(const struct nft_set *set) +static void nft_rbtree_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree_elem *rbe; @@ -675,7 +676,7 @@ static void nft_rbtree_destroy(const struct nft_set *set) while ((node = priv->root.rb_node) != NULL) { rb_erase(node, &priv->root); rbe = rb_entry(node, struct nft_rbtree_elem, node); - nft_set_elem_destroy(set, rbe, true); + nf_tables_set_elem_destroy(ctx, set, rbe); } } -- cgit 1.4.1 From 0d836f917520300a8725a5dbdad4406438d0cead Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 15:20:04 +0200 Subject: netfilter: nft_set_pipapo: .walk does not deal with generations [ Upstream commit 2b84e215f87443c74ac0aa7f76bb172d43a87033 ] The .walk callback iterates over the current active set, but it might be useful to iterate over the next generation set. Use the generation mask to determine what set view (either current or next generation) is use for the walk iteration. Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_set_pipapo.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index c867b5b772e8..0452ee586c1c 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -1974,12 +1974,16 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_iter *iter) { struct nft_pipapo *priv = nft_set_priv(set); + struct net *net = read_pnet(&set->net); struct nft_pipapo_match *m; struct nft_pipapo_field *f; int i, r; rcu_read_lock(); - m = rcu_dereference(priv->match); + if (iter->genmask == nft_genmask_cur(net)) + m = rcu_dereference(priv->match); + else + m = priv->clone; if (unlikely(!m)) goto out; -- cgit 1.4.1 From b60c0ce0ff3107b8cbabda46769b104890381046 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 15:20:16 +0200 Subject: netfilter: nf_tables: disallow element updates of bound anonymous sets [ Upstream commit c88c535b592d3baeee74009f3eceeeaf0fdd5e1b ] Anonymous sets come with NFT_SET_CONSTANT from userspace. Although API allows to create anonymous sets without NFT_SET_CONSTANT, it makes no sense to allow to add and to delete elements for bound anonymous sets. Fixes: 96518518cc41 ("netfilter: add nftables") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 337bab663540..2cd83d09f419 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -6590,7 +6590,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb, if (IS_ERR(set)) return PTR_ERR(set); - if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) + if (!list_empty(&set->bindings) && + (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS))) return -EBUSY; nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); @@ -6864,7 +6865,9 @@ static int nf_tables_delsetelem(struct sk_buff *skb, set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask); if (IS_ERR(set)) return PTR_ERR(set); - if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) + + if (!list_empty(&set->bindings) && + (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS))) return -EBUSY; nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); -- cgit 1.4.1 From 46f801ab5fb90d8aadeecd00bd2582cb0acc43b6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 15:21:33 +0200 Subject: netfilter: nf_tables: reject unbound anonymous set before commit phase [ Upstream commit 938154b93be8cd611ddfd7bafc1849f3c4355201 ] Add a new list to track set transaction and to check for unbound anonymous sets before entering the commit phase. Bail out at the end of the transaction handling if an anonymous set remains unbound. Fixes: 96518518cc41 ("netfilter: add nftables") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_tables.h | 3 +++ net/netfilter/nf_tables_api.c | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index bbcd558f1934..f3a37cacb32c 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1558,6 +1558,7 @@ static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext) * struct nft_trans - nf_tables object update in transaction * * @list: used internally + * @binding_list: list of objects with possible bindings * @msg_type: message type * @put_net: ctx->net needs to be put * @ctx: transaction context @@ -1565,6 +1566,7 @@ static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext) */ struct nft_trans { struct list_head list; + struct list_head binding_list; int msg_type; bool put_net; struct nft_ctx ctx; @@ -1703,6 +1705,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re struct nftables_pernet { struct list_head tables; struct list_head commit_list; + struct list_head binding_list; struct list_head module_list; struct list_head notify_list; struct mutex commit_mutex; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 2cd83d09f419..c0126aac035f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx, return NULL; INIT_LIST_HEAD(&trans->list); + INIT_LIST_HEAD(&trans->binding_list); trans->msg_type = msg_type; trans->ctx = *ctx; @@ -165,9 +166,15 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx, return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL); } -static void nft_trans_destroy(struct nft_trans *trans) +static void nft_trans_list_del(struct nft_trans *trans) { list_del(&trans->list); + list_del(&trans->binding_list); +} + +static void nft_trans_destroy(struct nft_trans *trans) +{ + nft_trans_list_del(trans); kfree(trans); } @@ -359,6 +366,14 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr { struct nftables_pernet *nft_net = nft_pernet(net); + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (!nft_trans_set_update(trans) && + nft_set_is_anonymous(nft_trans_set(trans))) + list_add_tail(&trans->binding_list, &nft_net->binding_list); + break; + } + list_add_tail(&trans->list, &nft_net->commit_list); } @@ -8829,7 +8844,7 @@ static void nf_tables_trans_destroy_work(struct work_struct *w) synchronize_rcu(); list_for_each_entry_safe(trans, next, &head, list) { - list_del(&trans->list); + nft_trans_list_del(trans); nft_commit_release(trans); } } @@ -9196,6 +9211,19 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) return 0; } + list_for_each_entry(trans, &nft_net->binding_list, binding_list) { + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (!nft_trans_set_update(trans) && + nft_set_is_anonymous(nft_trans_set(trans)) && + !nft_trans_set_bound(trans)) { + pr_warn_once("nftables ruleset with unbound set\n"); + return -EINVAL; + } + break; + } + } + /* 0. Validate ruleset, otherwise roll back for error reporting. */ if (nf_tables_validate(net) < 0) return -EAGAIN; @@ -9672,7 +9700,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list, list) { - list_del(&trans->list); + nft_trans_list_del(trans); nf_tables_abort_release(trans); } @@ -10448,6 +10476,7 @@ static int __net_init nf_tables_init_net(struct net *net) INIT_LIST_HEAD(&nft_net->tables); INIT_LIST_HEAD(&nft_net->commit_list); + INIT_LIST_HEAD(&nft_net->binding_list); INIT_LIST_HEAD(&nft_net->module_list); INIT_LIST_HEAD(&nft_net->notify_list); mutex_init(&nft_net->commit_mutex); -- cgit 1.4.1 From c34b22038543e1cb2000743fee9deba25b8f7372 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 15:21:39 +0200 Subject: netfilter: nf_tables: reject unbound chain set before commit phase [ Upstream commit 62e1e94b246e685d89c3163aaef4b160e42ceb02 ] Use binding list to track set transaction and to check for unbound chains before entering the commit phase. Bail out if chain binding remain unused before entering the commit step. Fixes: d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index c0126aac035f..984720964a49 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -372,6 +372,11 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr nft_set_is_anonymous(nft_trans_set(trans))) list_add_tail(&trans->binding_list, &nft_net->binding_list); break; + case NFT_MSG_NEWCHAIN: + if (!nft_trans_chain_update(trans) && + nft_chain_binding(nft_trans_chain(trans))) + list_add_tail(&trans->binding_list, &nft_net->binding_list); + break; } list_add_tail(&trans->list, &nft_net->commit_list); @@ -9221,6 +9226,14 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) return -EINVAL; } break; + case NFT_MSG_NEWCHAIN: + if (!nft_trans_chain_update(trans) && + nft_chain_binding(nft_trans_chain(trans)) && + !nft_trans_chain_bound(trans)) { + pr_warn_once("nftables ruleset with unbound chain\n"); + return -EINVAL; + } + break; } } -- cgit 1.4.1 From abd3afddbf9ec41278c30486c20676c117498eef Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 16 Jun 2023 15:22:01 +0200 Subject: netfilter: nf_tables: disallow updates of anonymous sets [ Upstream commit b770283c98e0eee9133c47bc03b6cc625dc94723 ] Disallow updates of set timeout and garbage collection parameters for anonymous sets. Fixes: 123b99619cca ("netfilter: nf_tables: honor set timeout and garbage collection updates") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 984720964a49..7f71bdbc8267 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4774,6 +4774,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, if (info->nlh->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; + if (nft_set_is_anonymous(set)) + return -EOPNOTSUPP; + err = nft_set_expr_alloc(&ctx, set, nla, exprs, &num_exprs, flags); if (err < 0) return err; -- cgit 1.4.1 From 3e04743dbacf3e4b9cc8e215854ab050bd2309c5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 15 Jun 2023 10:14:25 +0200 Subject: netfilter: nfnetlink_osf: fix module autoload [ Upstream commit 62f9a68a36d4441a6c412b81faed102594bc6670 ] Move the alias from xt_osf to nfnetlink_osf. Fixes: f9324952088f ("netfilter: nfnetlink_osf: extract nfnetlink_subsystem code from xt_osf.c") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nfnetlink_osf.c | 1 + net/netfilter/xt_osf.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index ee6840bd5933..8f1bfa6ccc2d 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -439,3 +439,4 @@ module_init(nfnl_osf_init); module_exit(nfnl_osf_fini); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF); diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index e1990baf3a3b..dc9485854002 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -71,4 +71,3 @@ MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Passive OS fingerprint matching."); MODULE_ALIAS("ipt_osf"); MODULE_ALIAS("ip6t_osf"); -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF); -- cgit 1.4.1 From 22cc989f2b10327a39943ff16a7d1baf290e6421 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Mon, 19 Jun 2023 17:44:35 +0200 Subject: Revert "net: phy: dp83867: perform soft reset and retain established link" [ Upstream commit a129b41fe0a8b4da828c46b10f5244ca07a3fec3 ] This reverts commit da9ef50f545f86ffe6ff786174d26500c4db737a. This fixes a regression in which the link would come up, but no communication was possible. The reverted commit was also removing a comment about DP83867_PHYCR_FORCE_LINK_GOOD, this is not added back in this commits since it seems that this is unrelated to the original code change. Closes: https://lore.kernel.org/all/ZGuDJos8D7N0J6Z2@francesco-nb.int.toradex.com/ Fixes: da9ef50f545f ("net: phy: dp83867: perform soft reset and retain established link") Signed-off-by: Francesco Dolcini Reviewed-by: Andrew Lunn Reviewed-by: Praneeth Bajjuri Link: https://lore.kernel.org/r/20230619154435.355485-1-francesco@dolcini.it Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/dp83867.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 14990f8462ae..f7436191fa80 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -905,7 +905,7 @@ static int dp83867_phy_reset(struct phy_device *phydev) { int err; - err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESTART); + err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESET); if (err < 0) return err; -- cgit 1.4.1 From fc3afb337814a66792859d9ca67b822977045a2f Mon Sep 17 00:00:00 2001 From: Florent Revest Date: Thu, 15 Jun 2023 16:56:07 +0200 Subject: bpf/btf: Accept function names that contain dots [ Upstream commit 9724160b3942b0a967b91a59f81da5593f28b8ba ] When building a kernel with LLVM=1, LLVM_IAS=0 and CONFIG_KASAN=y, LLVM leaves DWARF tags for the "asan.module_ctor" & co symbols. In turn, pahole creates BTF_KIND_FUNC entries for these and this makes the BTF metadata validation fail because they contain a dot. In a dramatic turn of event, this BTF verification failure can cause the netfilter_bpf initialization to fail, causing netfilter_core to free the netfilter_helper hashmap and netfilter_ftp to trigger a use-after-free. The risk of u-a-f in netfilter will be addressed separately but the existence of "asan.module_ctor" debug info under some build conditions sounds like a good enough reason to accept functions that contain dots in BTF. Although using only LLVM=1 is the recommended way to compile clang-based kernels, users can certainly do LLVM=1, LLVM_IAS=0 as well and we still try to support that combination according to Nick. To clarify: - > v5.10 kernel, LLVM=1 (LLVM_IAS=0 is not the default) is recommended, but user can still have LLVM=1, LLVM_IAS=0 to trigger the issue - <= 5.10 kernel, LLVM=1 (LLVM_IAS=0 is the default) is recommended in which case GNU as will be used Fixes: 1dc92851849c ("bpf: kernel side support for BTF Var and DataSec") Signed-off-by: Florent Revest Signed-off-by: Daniel Borkmann Acked-by: Andrii Nakryiko Cc: Yonghong Song Cc: Nick Desaulniers Link: https://lore.kernel.org/bpf/20230615145607.3469985-1-revest@chromium.org Signed-off-by: Sasha Levin --- kernel/bpf/btf.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index a8838a32f750..8220caa488c5 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -735,13 +735,12 @@ static bool btf_name_offset_valid(const struct btf *btf, u32 offset) return offset < btf->hdr.str_len; } -static bool __btf_name_char_ok(char c, bool first, bool dot_ok) +static bool __btf_name_char_ok(char c, bool first) { if ((first ? !isalpha(c) : !isalnum(c)) && c != '_' && - ((c == '.' && !dot_ok) || - c != '.')) + c != '.') return false; return true; } @@ -758,20 +757,20 @@ static const char *btf_str_by_offset(const struct btf *btf, u32 offset) return NULL; } -static bool __btf_name_valid(const struct btf *btf, u32 offset, bool dot_ok) +static bool __btf_name_valid(const struct btf *btf, u32 offset) { /* offset must be valid */ const char *src = btf_str_by_offset(btf, offset); const char *src_limit; - if (!__btf_name_char_ok(*src, true, dot_ok)) + if (!__btf_name_char_ok(*src, true)) return false; /* set a limit on identifier length */ src_limit = src + KSYM_NAME_LEN; src++; while (*src && src < src_limit) { - if (!__btf_name_char_ok(*src, false, dot_ok)) + if (!__btf_name_char_ok(*src, false)) return false; src++; } @@ -779,17 +778,14 @@ static bool __btf_name_valid(const struct btf *btf, u32 offset, bool dot_ok) return !*src; } -/* Only C-style identifier is permitted. This can be relaxed if - * necessary. - */ static bool btf_name_valid_identifier(const struct btf *btf, u32 offset) { - return __btf_name_valid(btf, offset, false); + return __btf_name_valid(btf, offset); } static bool btf_name_valid_section(const struct btf *btf, u32 offset) { - return __btf_name_valid(btf, offset, true); + return __btf_name_valid(btf, offset); } static const char *__btf_name_by_offset(const struct btf *btf, u32 offset) @@ -4044,7 +4040,7 @@ static s32 btf_var_check_meta(struct btf_verifier_env *env, } if (!t->name_off || - !__btf_name_valid(env->btf, t->name_off, true)) { + !__btf_name_valid(env->btf, t->name_off)) { btf_verifier_log_type(env, t, "Invalid name"); return -EINVAL; } -- cgit 1.4.1 From 1b7b048c228effa3c66ac183811e394fa4d9a28a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 18 Jun 2023 15:14:14 +0200 Subject: bpf: Force kprobe multi expected_attach_type for kprobe_multi link [ Upstream commit db8eae6bc5c702d8e3ab2d0c6bb5976c131576eb ] We currently allow to create perf link for program with expected_attach_type == BPF_TRACE_KPROBE_MULTI. This will cause crash when we call helpers like get_attach_cookie or get_func_ip in such program, because it will call the kprobe_multi's version (current->bpf_ctx context setup) of those helpers while it expects perf_link's current->bpf_ctx context setup. Making sure that we use BPF_TRACE_KPROBE_MULTI expected_attach_type only for programs attaching through kprobe_multi link. Fixes: ca74823c6e16 ("bpf: Add cookie support to programs attached with kprobe multi link") Signed-off-by: Jiri Olsa Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20230618131414.75649-1-jolsa@kernel.org Signed-off-by: Sasha Levin --- kernel/bpf/syscall.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6c61dba26f4d..8633ec4f92df 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3383,6 +3383,11 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, return prog->enforce_expected_attach_type && prog->expected_attach_type != attach_type ? -EINVAL : 0; + case BPF_PROG_TYPE_KPROBE: + if (prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI && + attach_type != BPF_TRACE_KPROBE_MULTI) + return -EINVAL; + return 0; default: return 0; } -- cgit 1.4.1 From eff07bf118411b9d2de8fd2f949d23c8dc24c3dc Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 20 Jun 2023 16:11:51 -0600 Subject: io_uring/net: use the correct msghdr union member in io_sendmsg_copy_hdr [ Upstream commit 26fed83653d0154704cadb7afc418f315c7ac1f0 ] Rather than assign the user pointer to msghdr->msg_control, assign it to msghdr->msg_control_user to make sparse happy. They are in a union so the end result is the same, but let's avoid new sparse warnings and squash this one. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202306210654.mDMcyMuB-lkp@intel.com/ Fixes: cac9e4418f4c ("io_uring/net: save msghdr->msg_control for retries") Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- io_uring/net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_uring/net.c b/io_uring/net.c index 41f828d93c89..2b44126a876e 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -190,7 +190,7 @@ static int io_sendmsg_copy_hdr(struct io_kiocb *req, ret = sendmsg_copy_msghdr(&iomsg->msg, sr->umsg, sr->msg_flags, &iomsg->free_iov); /* save msg_control as sys_sendmsg() overwrites it */ - sr->msg_control = iomsg->msg.msg_control; + sr->msg_control = iomsg->msg.msg_control_user; return ret; } @@ -289,7 +289,7 @@ int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) if (req_has_async_data(req)) { kmsg = req->async_data; - kmsg->msg.msg_control = sr->msg_control; + kmsg->msg.msg_control_user = sr->msg_control; } else { ret = io_sendmsg_copy_hdr(req, &iomsg); if (ret) -- cgit 1.4.1 From 2d580c73afdcd6f1e95ef6f95b3596a266c92bf3 Mon Sep 17 00:00:00 2001 From: Danielle Ratson Date: Tue, 20 Jun 2023 14:45:15 +0200 Subject: selftests: forwarding: Fix race condition in mirror installation [ Upstream commit c7c059fba6fb19c3bc924925c984772e733cb594 ] When mirroring to a gretap in hardware the device expects to be programmed with the egress port and all the encapsulating headers. This requires the driver to resolve the path the packet will take in the software data path and program the device accordingly. If the path cannot be resolved (in this case because of an unresolved neighbor), then mirror installation fails until the path is resolved. This results in a race that causes the test to sometimes fail. Fix this by setting the neighbor's state to permanent in a couple of tests, so that it is always valid. Fixes: 35c31d5c323f ("selftests: forwarding: Test mirror-to-gretap w/ UL 802.1d") Fixes: 239e754af854 ("selftests: forwarding: Test mirror-to-gretap w/ UL 802.1q") Signed-off-by: Danielle Ratson Reviewed-by: Petr Machata Signed-off-by: Petr Machata Link: https://lore.kernel.org/r/268816ac729cb6028c7a34d4dda6f4ec7af55333.1687264607.git.petrm@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh | 4 ++++ tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh index c5095da7f6bf..aec752a22e9e 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh @@ -93,12 +93,16 @@ cleanup() test_gretap() { + ip neigh replace 192.0.2.130 lladdr $(mac_get $h3) \ + nud permanent dev br2 full_test_span_gre_dir gt4 ingress 8 0 "mirror to gretap" full_test_span_gre_dir gt4 egress 0 8 "mirror to gretap" } test_ip6gretap() { + ip neigh replace 2001:db8:2::2 lladdr $(mac_get $h3) \ + nud permanent dev br2 full_test_span_gre_dir gt6 ingress 8 0 "mirror to ip6gretap" full_test_span_gre_dir gt6 egress 0 8 "mirror to ip6gretap" } diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh index 9ff22f28032d..0cf4c47a46f9 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh @@ -90,12 +90,16 @@ cleanup() test_gretap() { + ip neigh replace 192.0.2.130 lladdr $(mac_get $h3) \ + nud permanent dev br1 full_test_span_gre_dir gt4 ingress 8 0 "mirror to gretap" full_test_span_gre_dir gt4 egress 0 8 "mirror to gretap" } test_ip6gretap() { + ip neigh replace 2001:db8:2::2 lladdr $(mac_get $h3) \ + nud permanent dev br1 full_test_span_gre_dir gt6 ingress 8 0 "mirror to ip6gretap" full_test_span_gre_dir gt6 egress 0 8 "mirror to ip6gretap" } -- cgit 1.4.1 From 0434277b72a48d063124af4ff7602bdc96881f3f Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 22 Jun 2023 11:33:09 +0530 Subject: platform/x86/amd/pmf: Register notify handler only if SPS is enabled [ Upstream commit 146b6f6855e7656e8329910606595220c761daac ] Power source notify handler is getting registered even when none of the PMF feature in enabled leading to a crash. ... [ 22.592162] Call Trace: [ 22.592164] [ 22.592164] ? rcu_note_context_switch+0x5e0/0x660 [ 22.592166] ? __warn+0x81/0x130 [ 22.592171] ? rcu_note_context_switch+0x5e0/0x660 [ 22.592172] ? report_bug+0x171/0x1a0 [ 22.592175] ? prb_read_valid+0x1b/0x30 [ 22.592177] ? handle_bug+0x3c/0x80 [ 22.592178] ? exc_invalid_op+0x17/0x70 [ 22.592179] ? asm_exc_invalid_op+0x1a/0x20 [ 22.592182] ? rcu_note_context_switch+0x5e0/0x660 [ 22.592183] ? acpi_ut_delete_object_desc+0x86/0xb0 [ 22.592186] ? acpi_ut_update_ref_count.part.0+0x22d/0x930 [ 22.592187] __schedule+0xc0/0x1410 [ 22.592189] ? ktime_get+0x3c/0xa0 [ 22.592191] ? lapic_next_event+0x1d/0x30 [ 22.592193] ? hrtimer_start_range_ns+0x25b/0x350 [ 22.592196] schedule+0x5e/0xd0 [ 22.592197] schedule_hrtimeout_range_clock+0xbe/0x140 [ 22.592199] ? __pfx_hrtimer_wakeup+0x10/0x10 [ 22.592200] usleep_range_state+0x64/0x90 [ 22.592203] amd_pmf_send_cmd+0x106/0x2a0 [amd_pmf bddfe0fe3712aaa99acce3d5487405c5213c6616] [ 22.592207] amd_pmf_update_slider+0x56/0x1b0 [amd_pmf bddfe0fe3712aaa99acce3d5487405c5213c6616] [ 22.592210] amd_pmf_set_sps_power_limits+0x72/0x80 [amd_pmf bddfe0fe3712aaa99acce3d5487405c5213c6616] [ 22.592213] amd_pmf_pwr_src_notify_call+0x49/0x90 [amd_pmf bddfe0fe3712aaa99acce3d5487405c5213c6616] [ 22.592216] notifier_call_chain+0x5a/0xd0 [ 22.592218] atomic_notifier_call_chain+0x32/0x50 ... Fix this by moving the registration of source change notify handler only when SPS(Static Slider) is advertised as supported. Reported-by: Allen Zhong Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217571 Fixes: 4c71ae414474 ("platform/x86/amd/pmf: Add support SPS PMF feature") Tested-by: Patil Rajesh Reddy Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20230622060309.310001-1-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/amd/pmf/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index dc9803e1a4b9..73d2357e32f8 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -297,6 +297,8 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) /* Enable Static Slider */ if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { amd_pmf_init_sps(dev); + dev->pwr_src_notifier.notifier_call = amd_pmf_pwr_src_notify_call; + power_supply_reg_notifier(&dev->pwr_src_notifier); dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n"); } @@ -315,8 +317,10 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) { - if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) + if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { + power_supply_unreg_notifier(&dev->pwr_src_notifier); amd_pmf_deinit_sps(dev); + } if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { amd_pmf_deinit_auto_mode(dev); @@ -399,9 +403,6 @@ static int amd_pmf_probe(struct platform_device *pdev) apmf_install_handler(dev); amd_pmf_dbgfs_register(dev); - dev->pwr_src_notifier.notifier_call = amd_pmf_pwr_src_notify_call; - power_supply_reg_notifier(&dev->pwr_src_notifier); - dev_info(dev->dev, "registered PMF device successfully\n"); return 0; @@ -411,7 +412,6 @@ static int amd_pmf_remove(struct platform_device *pdev) { struct amd_pmf_dev *dev = platform_get_drvdata(pdev); - power_supply_unreg_notifier(&dev->pwr_src_notifier); amd_pmf_deinit_features(dev); apmf_acpi_deinit(dev); amd_pmf_dbgfs_unregister(dev); -- cgit 1.4.1 From b2e2f9c0939f990dc5daca0838c3a4456f0ba345 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 20 Jun 2023 18:44:25 +0000 Subject: sch_netem: acquire qdisc lock in netem_change() [ Upstream commit 2174a08db80d1efeea382e25ac41c4e7511eb6d6 ] syzbot managed to trigger a divide error [1] in netem. It could happen if q->rate changes while netem_enqueue() is running, since q->rate is read twice. It turns out netem_change() always lacked proper synchronization. [1] divide error: 0000 [#1] SMP KASAN CPU: 1 PID: 7867 Comm: syz-executor.1 Not tainted 6.1.30-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/25/2023 RIP: 0010:div64_u64 include/linux/math64.h:69 [inline] RIP: 0010:packet_time_ns net/sched/sch_netem.c:357 [inline] RIP: 0010:netem_enqueue+0x2067/0x36d0 net/sched/sch_netem.c:576 Code: 89 e2 48 69 da 00 ca 9a 3b 42 80 3c 28 00 4c 8b a4 24 88 00 00 00 74 0d 4c 89 e7 e8 c3 4f 3b fd 48 8b 4c 24 18 48 89 d8 31 d2 <49> f7 34 24 49 01 c7 4c 8b 64 24 48 4d 01 f7 4c 89 e3 48 c1 eb 03 RSP: 0018:ffffc9000dccea60 EFLAGS: 00010246 RAX: 000001a442624200 RBX: 000001a442624200 RCX: ffff888108a4f000 RDX: 0000000000000000 RSI: 000000000000070d RDI: 000000000000070d RBP: ffffc9000dcceb90 R08: ffffffff849c5e26 R09: fffffbfff10e1297 R10: 0000000000000000 R11: dffffc0000000001 R12: ffff888108a4f358 R13: dffffc0000000000 R14: 0000001a8cd9a7ec R15: 0000000000000000 FS: 00007fa73fe18700(0000) GS:ffff8881f6b00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fa73fdf7718 CR3: 000000011d36e000 CR4: 0000000000350ee0 Call Trace: [] __dev_xmit_skb net/core/dev.c:3931 [inline] [] __dev_queue_xmit+0xcf5/0x3370 net/core/dev.c:4290 [] dev_queue_xmit include/linux/netdevice.h:3030 [inline] [] neigh_hh_output include/net/neighbour.h:531 [inline] [] neigh_output include/net/neighbour.h:545 [inline] [] ip_finish_output2+0xb92/0x10d0 net/ipv4/ip_output.c:235 [] __ip_finish_output+0xc3/0x2b0 [] ip_finish_output+0x31/0x2a0 net/ipv4/ip_output.c:323 [] NF_HOOK_COND include/linux/netfilter.h:298 [inline] [] ip_output+0x224/0x2a0 net/ipv4/ip_output.c:437 [] dst_output include/net/dst.h:444 [inline] [] ip_local_out net/ipv4/ip_output.c:127 [inline] [] __ip_queue_xmit+0x1425/0x2000 net/ipv4/ip_output.c:542 [] ip_queue_xmit+0x4c/0x70 net/ipv4/ip_output.c:556 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot Signed-off-by: Eric Dumazet Cc: Stephen Hemminger Cc: Jamal Hadi Salim Cc: Cong Wang Cc: Jiri Pirko Reviewed-by: Jamal Hadi Salim Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230620184425.1179809-1-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/sch_netem.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index fb00ac40ecb7..aa9842158df0 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -966,6 +966,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, if (ret < 0) return ret; + sch_tree_lock(sch); /* backup q->clg and q->loss_model */ old_clg = q->clg; old_loss_model = q->loss_model; @@ -974,7 +975,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]); if (ret) { q->loss_model = old_loss_model; - return ret; + goto unlock; } } else { q->loss_model = CLG_RANDOM; @@ -1041,6 +1042,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, /* capping jitter to the range acceptable by tabledist() */ q->jitter = min_t(s64, abs(q->jitter), INT_MAX); +unlock: + sch_tree_unlock(sch); return ret; get_table_failure: @@ -1050,7 +1053,8 @@ get_table_failure: */ q->clg = old_clg; q->loss_model = old_loss_model; - return ret; + + goto unlock; } static int netem_init(struct Qdisc *sch, struct nlattr *opt, -- cgit 1.4.1 From ec3d0f12e728b1d9abbeb2c176ed2a277b6477d7 Mon Sep 17 00:00:00 2001 From: Maciej Żenczykowski Date: Sun, 18 Jun 2023 03:31:30 -0700 Subject: revert "net: align SO_RCVMARK required privileges with SO_MARK" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a9628e88776eb7d045cf46467f1afdd0f7fe72ea ] This reverts commit 1f86123b9749 ("net: align SO_RCVMARK required privileges with SO_MARK") because the reasoning in the commit message is not really correct: SO_RCVMARK is used for 'reading' incoming skb mark (via cmsg), as such it is more equivalent to 'getsockopt(SO_MARK)' which has no priv check and retrieves the socket mark, rather than 'setsockopt(SO_MARK) which sets the socket mark and does require privs. Additionally incoming skb->mark may already be visible if sysctl_fwmark_reflect and/or sysctl_tcp_fwmark_accept are enabled. Furthermore, it is easier to block the getsockopt via bpf (either cgroup setsockopt hook, or via syscall filters) then to unblock it if it requires CAP_NET_RAW/ADMIN. On Android the socket mark is (among other things) used to store the network identifier a socket is bound to. Setting it is privileged, but retrieving it is not. We'd like unprivileged userspace to be able to read the network id of incoming packets (where mark is set via iptables [to be moved to bpf])... An alternative would be to add another sysctl to control whether setting SO_RCVMARK is privilged or not. (or even a MASK of which bits in the mark can be exposed) But this seems like over-engineering... Note: This is a non-trivial revert, due to later merged commit e42c7beee71d ("bpf: net: Consider has_current_bpf_ctx() when testing capable() in sk_setsockopt()") which changed both 'ns_capable' into 'sockopt_ns_capable' calls. Fixes: 1f86123b9749 ("net: align SO_RCVMARK required privileges with SO_MARK") Cc: Larysa Zaremba Cc: Simon Horman Cc: Paolo Abeni Cc: Eyal Birger Cc: Jakub Kicinski Cc: Eric Dumazet Cc: Patrick Rohr Signed-off-by: Maciej Żenczykowski Reviewed-by: Simon Horman Reviewed-by: Kuniyuki Iwashima Link: https://lore.kernel.org/r/20230618103130.51628-1-maze@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/core/sock.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/core/sock.c b/net/core/sock.c index 83f590d8d085..b021cb9c95ef 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1355,12 +1355,6 @@ set_sndbuf: __sock_set_mark(sk, val); break; case SO_RCVMARK: - if (!sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && - !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - sock_valbool_flag(sk, SOCK_RCVMARK, valbool); break; -- cgit 1.4.1 From e51abd4808f9e91e5b7ee819521ae1b08c4b48b2 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Sat, 12 Nov 2022 17:03:58 +0100 Subject: arm64: dts: rockchip: Enable GPU on SOQuartz CM4 [ Upstream commit e48824e8a03e5bc3666e9f5461f68d440d9acba0 ] This enables the Mali-G52 GPU on the SOQuartz CM4 module. Signed-off-by: Nicolas Frattaroli Link: https://lore.kernel.org/r/20221112160404.70868-2-frattaroli.nicolas@gmail.com Signed-off-by: Heiko Stuebner Stable-dep-of: cf9ae4a00774 ("arm64: dts: rockchip: fix nEXTRST on SOQuartz") Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi index 4d494b53a71a..4ceb9a979f6a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi @@ -143,6 +143,11 @@ status = "disabled"; }; +&gpu { + mali-supply = <&vdd_gpu>; + status = "okay"; +}; + &i2c0 { status = "okay"; -- cgit 1.4.1 From 90714f7ed760bb21a072dfa86795d4864daf9d6d Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Fri, 21 Apr 2023 17:26:10 +0200 Subject: arm64: dts: rockchip: fix nEXTRST on SOQuartz [ Upstream commit cf9ae4a0077496e8224d68fc88e3df13dd7e5f37 ] In pre-production prototypes (of which I only know one person having one, Peter Geis), GPIO0 pin A5 was tied to the SDMMC power enable pin on the CM4 connector. On all production models, this is not the case; instead, this pin is used for the nEXTRST signal, and the SDMMC power enable pin is always pulled high. Since everyone currently using the SOQuartz device trees will want this change, it is made to the tree without splitting the trees into two separate ones of which users will then inevitably choose the wrong one. This fixes USB and PCIe on a wide variety of CM4IO-compatible boards which use the nEXTRST signal. Fixes: 5859b5a9c3ac ("arm64: dts: rockchip: add SoQuartz CM4IO dts") Signed-off-by: Nicolas Frattaroli Link: https://lore.kernel.org/r/20230421152610.21688-1-frattaroli.nicolas@gmail.com Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- .../boot/dts/rockchip/rk3566-soquartz-cm4.dts | 18 ++++++++------ arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi | 29 ++++++++++------------ 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts b/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts index e00568a6be5c..6ba562b922e6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts @@ -28,6 +28,16 @@ regulator-max-microvolt = <5000000>; vin-supply = <&vcc12v_dcin>; }; + + vcc_sd_pwr: vcc-sd-pwr-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_sd_pwr"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc3v3_sys>; + }; }; &gmac1 { @@ -119,13 +129,7 @@ }; &sdmmc0 { - vmmc-supply = <&sdmmc_pwr>; - status = "okay"; -}; - -&sdmmc_pwr { - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; + vmmc-supply = <&vcc_sd_pwr>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi index 4ceb9a979f6a..ba56ca2e66c8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi @@ -92,16 +92,6 @@ regulator-max-microvolt = <3300000>; vin-supply = <&vcc5v0_sys>; }; - - sdmmc_pwr: sdmmc-pwr-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <&gpio0 RK_PA5 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc_pwr_h>; - regulator-name = "sdmmc_pwr"; - status = "disabled"; - }; }; &cpu0 { @@ -143,6 +133,19 @@ status = "disabled"; }; +&gpio0 { + nextrst-hog { + gpio-hog; + /* + * GPIO_ACTIVE_LOW + output-low here means that the pin is set + * to high, because output-low decides the value pre-inversion. + */ + gpios = ; + line-name = "nEXTRST"; + output-low; + }; +}; + &gpu { mali-supply = <&vdd_gpu>; status = "okay"; @@ -485,12 +488,6 @@ rockchip,pins = <2 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; }; }; - - sdmmc-pwr { - sdmmc_pwr_h: sdmmc-pwr-h { - rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; }; &pmu_io_domains { -- cgit 1.4.1 From cb1108e174935ac986c18bf3bb2178b20209c234 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 7 Jun 2023 16:18:03 +0800 Subject: gpiolib: Fix GPIO chip IRQ initialization restriction [ Upstream commit 8c00914e5438e3636f26b4f814b3297ae2a1b9ee ] In case of gpio-regmap, IRQ chip is added by regmap-irq and associated with GPIO chip by gpiochip_irqchip_add_domain(). The initialization flag was not added in gpiochip_irqchip_add_domain(), causing gpiochip_to_irq() to return -EPROBE_DEFER. Fixes: 5467801f1fcb ("gpio: Restrict usage of GPIO chip irq members before initialization") Signed-off-by: Jiawen Wu Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpiolib.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5974cfc61b41..f2cb07093185 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1697,6 +1697,14 @@ int gpiochip_irqchip_add_domain(struct gpio_chip *gc, gc->to_irq = gpiochip_to_irq; gc->irq.domain = domain; + /* + * Using barrier() here to prevent compiler from reordering + * gc->irq.initialized before adding irqdomain. + */ + barrier(); + + gc->irq.initialized = true; + return 0; } EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain); -- cgit 1.4.1 From 8592ada80ea52f7d4ce283da6624fc372a31b455 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 6 Jun 2023 11:11:59 +0800 Subject: gpio: sifive: add missing check for platform_get_irq [ Upstream commit c1bcb976d8feb107ff2c12caaf12ac5e70f44d5f ] Add the missing check for platform_get_irq() and return error code if it fails. The returned error code will be dealed with in builtin_platform_driver(sifive_gpio_driver) and the driver will not be registered. Fixes: f52d6d8b43e5 ("gpio: sifive: To get gpio irq offset from device tree data") Signed-off-by: Jiasheng Jiang Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-sifive.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index bc5660f61c57..0f1e1226ebbe 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -221,8 +221,12 @@ static int sifive_gpio_probe(struct platform_device *pdev) return -ENODEV; } - for (i = 0; i < ngpio; i++) - chip->irq_number[i] = platform_get_irq(pdev, i); + for (i = 0; i < ngpio; i++) { + ret = platform_get_irq(pdev, i); + if (ret < 0) + return ret; + chip->irq_number[i] = ret; + } ret = bgpio_init(&chip->gc, dev, 4, chip->base + SIFIVE_GPIO_INPUT_VAL, -- cgit 1.4.1 From 0357259cb103f1e48b4372bab50be45f2591c49f Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 19 Jun 2023 10:56:07 +0200 Subject: gpiolib: Fix irq_domain resource tracking for gpiochip_irqchip_add_domain() [ Upstream commit ff7a1790fbf92f1bdd0966d3f0da3ea808ede876 ] Up until commit 6a45b0e2589f ("gpiolib: Introduce gpiochip_irqchip_add_domain()") all irq_domains were allocated by gpiolib itself and thus gpiolib also takes care of freeing it. With gpiochip_irqchip_add_domain() a user of gpiolib can associate an irq_domain with the gpio_chip. This irq_domain is not managed by gpiolib and therefore must not be freed by gpiolib. Fixes: 6a45b0e2589f ("gpiolib: Introduce gpiochip_irqchip_add_domain()") Reported-by: Jiawen Wu Signed-off-by: Michael Walle Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpiolib.c | 3 ++- include/linux/gpio/driver.h | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f2cb07093185..6d3e3454a6ed 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1650,7 +1650,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc) } /* Remove all IRQ mappings and delete the domain */ - if (gc->irq.domain) { + if (!gc->irq.domain_is_allocated_externally && gc->irq.domain) { unsigned int irq; for (offset = 0; offset < gc->ngpio; offset++) { @@ -1696,6 +1696,7 @@ int gpiochip_irqchip_add_domain(struct gpio_chip *gc, gc->to_irq = gpiochip_to_irq; gc->irq.domain = domain; + gc->irq.domain_is_allocated_externally = true; /* * Using barrier() here to prevent compiler from reordering diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 6aeea1071b1b..78bcb1639999 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -244,6 +244,14 @@ struct gpio_irq_chip { */ bool initialized; + /** + * @domain_is_allocated_externally: + * + * True it the irq_domain was allocated outside of gpiolib, in which + * case gpiolib won't free the irq_domain itself. + */ + bool domain_is_allocated_externally; + /** * @init_hw: optional routine to initialize hardware before * an IRQ chip will be added. This is quite useful when -- cgit 1.4.1 From aaa50510adb7f7f40e02809148f1d407b72c74f0 Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Mon, 8 May 2023 18:22:19 +0200 Subject: scsi: target: iscsi: Prevent login threads from racing between each other [ Upstream commit 2a737d3b8c792400118d6cf94958f559de9c5e59 ] The tpg->np_login_sem is a semaphore that is used to serialize the login process when multiple login threads run concurrently against the same target portal group. The iscsi_target_locate_portal() function finds the tpg, calls iscsit_access_np() against the np_login_sem semaphore and saves the tpg pointer in conn->tpg; If iscsi_target_locate_portal() fails, the caller will check for the conn->tpg pointer and, if it's not NULL, then it will assume that iscsi_target_locate_portal() called iscsit_access_np() on the semaphore. Make sure that conn->tpg gets initialized only if iscsit_access_np() was successful, otherwise iscsit_deaccess_np() may end up being called against a semaphore we never took, allowing more than one thread to access the same tpg. Signed-off-by: Maurizio Lombardi Link: https://lore.kernel.org/r/20230508162219.1731964-4-mlombard@redhat.com Reviewed-by: Mike Christie Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/iscsi/iscsi_target_nego.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index ff49c8f3fe24..62b2d0dcfda8 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -1128,6 +1128,7 @@ int iscsi_target_locate_portal( iscsi_target_set_sock_callbacks(conn); login->np = np; + conn->tpg = NULL; login_req = (struct iscsi_login_req *) login->req; payload_length = ntoh24(login_req->dlength); @@ -1195,7 +1196,6 @@ int iscsi_target_locate_portal( */ sessiontype = strncmp(s_buf, DISCOVERY, 9); if (!sessiontype) { - conn->tpg = iscsit_global->discovery_tpg; if (!login->leading_connection) goto get_target; @@ -1212,9 +1212,11 @@ int iscsi_target_locate_portal( * Serialize access across the discovery struct iscsi_portal_group to * process login attempt. */ + conn->tpg = iscsit_global->discovery_tpg; if (iscsit_access_np(np, conn->tpg) < 0) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); + conn->tpg = NULL; ret = -1; goto out; } -- cgit 1.4.1 From 8428f4c00d8c36f279d176548b4db73a7021983a Mon Sep 17 00:00:00 2001 From: Denis Arefev Date: Thu, 27 Apr 2023 14:47:45 +0300 Subject: HID: wacom: Add error check to wacom_parse_and_register() [ Upstream commit 16a9c24f24fbe4564284eb575b18cc20586b9270 ] Added a variable check and transition in case of an error Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Denis Arefev Reviewed-by: Ping Cheng Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/wacom_sys.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index fb538a6c4add..aff4a21a46b6 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -2417,8 +2417,13 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) goto fail_quirks; } - if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) + if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) { error = hid_hw_open(hdev); + if (error) { + hid_err(hdev, "hw open failed\n"); + goto fail_quirks; + } + } wacom_set_shared_values(wacom_wac); devres_close_group(&hdev->dev, wacom); -- cgit 1.4.1 From 3e8458c5b2058014b7d056eb0b0ebbf386f48637 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 15 May 2023 21:46:00 +0100 Subject: arm64: Add missing Set/Way CMO encodings [ Upstream commit 8d0f019e4c4f2ee2de81efd9bf1c27e9fb3c0460 ] Add the missing Set/Way CMOs that apply to tagged memory. Signed-off-by: Marc Zyngier Reviewed-by: Cornelia Huck Reviewed-by: Steven Price Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/20230515204601.1270428-2-maz@kernel.org Signed-off-by: Sasha Levin --- arch/arm64/include/asm/sysreg.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 7d301700d1a9..3a448ab0924b 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -111,8 +111,14 @@ #define SB_BARRIER_INSN __SYS_BARRIER_INSN(0, 7, 31) #define SYS_DC_ISW sys_insn(1, 0, 7, 6, 2) +#define SYS_DC_IGSW sys_insn(1, 0, 7, 6, 4) +#define SYS_DC_IGDSW sys_insn(1, 0, 7, 6, 6) #define SYS_DC_CSW sys_insn(1, 0, 7, 10, 2) +#define SYS_DC_CGSW sys_insn(1, 0, 7, 10, 4) +#define SYS_DC_CGDSW sys_insn(1, 0, 7, 10, 6) #define SYS_DC_CISW sys_insn(1, 0, 7, 14, 2) +#define SYS_DC_CIGSW sys_insn(1, 0, 7, 14, 4) +#define SYS_DC_CIGDSW sys_insn(1, 0, 7, 14, 6) /* * Automatically generated definitions for system registers, the -- cgit 1.4.1 From 9d8ac2726cff64a1209b1c027f0e0eb04c92bcaf Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 25 May 2023 18:53:28 -0500 Subject: smb3: missing null check in SMB2_change_notify [ Upstream commit b535cc796a4b4942cd189652588e8d37c1f5925a ] If plen is null when passed in, we only checked for null in one of the two places where it could be used. Although plen is always valid (not null) for current callers of the SMB2_change_notify function, this change makes it more consistent. Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/202305251831.3V1gbbFs-lkp@intel.com/ Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/cifs/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 537e8679900b..3ca593cdda76 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3779,7 +3779,7 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, if (*out_data == NULL) { rc = -ENOMEM; goto cnotify_exit; - } else + } else if (plen) *plen = le32_to_cpu(smb_rsp->OutputBufferLength); } -- cgit 1.4.1 From f37956a140d10efc07a303b5b043b422b93cc47e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 20 Apr 2023 08:26:53 +0100 Subject: media: cec: core: disable adapter in cec_devnode_unregister [ Upstream commit fe4526d99e2e06b08bb80316c3a596ea6a807b75 ] Explicitly disable the CEC adapter in cec_devnode_unregister() Usually this does not really do anything important, but for drivers that use the CEC pin framework this is needed to properly stop the hrtimer. Without this a crash would happen when such a driver is unloaded with rmmod. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/cec/core/cec-adap.c | 5 ++++- drivers/media/cec/core/cec-core.c | 2 ++ drivers/media/cec/core/cec-priv.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 4f5ab3cae8a7..ac18707fddcd 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1582,7 +1582,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) * * This function is called with adap->lock held. */ -static int cec_adap_enable(struct cec_adapter *adap) +int cec_adap_enable(struct cec_adapter *adap) { bool enable; int ret = 0; @@ -1592,6 +1592,9 @@ static int cec_adap_enable(struct cec_adapter *adap) if (adap->needs_hpd) enable = enable && adap->phys_addr != CEC_PHYS_ADDR_INVALID; + if (adap->devnode.unregistered) + enable = false; + if (enable == adap->is_enabled) return 0; diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index af358e901b5f..7e153c5cad04 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -191,6 +191,8 @@ static void cec_devnode_unregister(struct cec_adapter *adap) mutex_lock(&adap->lock); __cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false); __cec_s_log_addrs(adap, NULL, false); + // Disable the adapter (since adap->devnode.unregistered is true) + cec_adap_enable(adap); mutex_unlock(&adap->lock); cdev_device_del(&devnode->cdev, &devnode->dev); diff --git a/drivers/media/cec/core/cec-priv.h b/drivers/media/cec/core/cec-priv.h index b78df931aa74..ed1f8c67626b 100644 --- a/drivers/media/cec/core/cec-priv.h +++ b/drivers/media/cec/core/cec-priv.h @@ -47,6 +47,7 @@ int cec_monitor_pin_cnt_inc(struct cec_adapter *adap); void cec_monitor_pin_cnt_dec(struct cec_adapter *adap); int cec_adap_status(struct seq_file *file, void *priv); int cec_thread_func(void *_adap); +int cec_adap_enable(struct cec_adapter *adap); void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block); int __cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block); -- cgit 1.4.1 From a05df0643120adb7c4ee3cc01e40836fa31b1a9f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Apr 2023 16:07:28 +0100 Subject: media: cec: core: don't set last_initiator if tx in progress [ Upstream commit 73af6c7511038249cad3d5f3b44bf8d78ac0f499 ] When a message was received the last_initiator is set to 0xff. This will force the signal free time for the next transmit to that for a new initiator. However, if a new transmit is already in progress, then don't set last_initiator, since that's the initiator of the current transmit. Overwriting this would cause the signal free time of a following transmit to be that of the new initiator instead of a next transmit. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/cec/core/cec-adap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index ac18707fddcd..b1512f9c5895 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1090,7 +1090,8 @@ void cec_received_msg_ts(struct cec_adapter *adap, mutex_lock(&adap->lock); dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg); - adap->last_initiator = 0xff; + if (!adap->transmit_in_progress) + adap->last_initiator = 0xff; /* Check if this message was for us (directed or broadcast). */ if (!cec_msg_is_broadcast(msg)) -- cgit 1.4.1 From a2c3e9bfc02c01cfe7700741fe1c60af731e2b2e Mon Sep 17 00:00:00 2001 From: Osama Muhammad Date: Thu, 25 May 2023 22:27:46 +0500 Subject: nfcsim.c: Fix error checking for debugfs_create_dir [ Upstream commit 9b9e46aa07273ceb96866b2e812b46f1ee0b8d2f ] This patch fixes the error checking in nfcsim.c. The DebugFS kernel API is developed in a way that the caller can safely ignore the errors that occur during the creation of DebugFS nodes. Signed-off-by: Osama Muhammad Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/nfc/nfcsim.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 85bf8d586c70..0f6befe8be1e 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -336,10 +336,6 @@ static struct dentry *nfcsim_debugfs_root; static void nfcsim_debugfs_init(void) { nfcsim_debugfs_root = debugfs_create_dir("nfcsim", NULL); - - if (!nfcsim_debugfs_root) - pr_err("Could not create debugfs entry\n"); - } static void nfcsim_debugfs_remove(void) -- cgit 1.4.1 From 3f6391062d0b58eceba54b7819658f85f51f3607 Mon Sep 17 00:00:00 2001 From: Shida Zhang Date: Tue, 16 May 2023 09:34:30 +0800 Subject: btrfs: fix an uninitialized variable warning in btrfs_log_inode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8fd9f4232d8152c650fd15127f533a0f6d0a4b2b ] This fixes the following warning reported by gcc 10.2.1 under x86_64: ../fs/btrfs/tree-log.c: In function ‘btrfs_log_inode’: ../fs/btrfs/tree-log.c:6211:9: error: ‘last_range_start’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 6211 | ret = insert_dir_log_key(trans, log, path, key.objectid, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6212 | first_dir_index, last_dir_index); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../fs/btrfs/tree-log.c:6161:6: note: ‘last_range_start’ was declared here 6161 | u64 last_range_start; | ^~~~~~~~~~~~~~~~ This might be a false positive fixed in later compiler versions but we want to have it fixed. Reported-by: k2ci Reviewed-by: Anand Jain Signed-off-by: Shida Zhang Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/tree-log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index e71464c0e466..00be69ce7b90 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -6205,7 +6205,7 @@ static int log_delayed_deletions_incremental(struct btrfs_trans_handle *trans, { struct btrfs_root *log = inode->root->log_root; const struct btrfs_delayed_item *curr; - u64 last_range_start; + u64 last_range_start = 0; u64 last_range_end = 0; struct btrfs_key key; -- cgit 1.4.1 From c8f988c37a6b46eb44e9f64f849313c89a29a52c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 25 May 2023 18:38:37 +0300 Subject: usb: gadget: udc: fix NULL dereference in remove() [ Upstream commit 016da9c65fec9f0e78c4909ed9a0f2d567af6775 ] The "udc" pointer was never set in the probe() function so it will lead to a NULL dereference in udc_pci_remove() when we do: usb_del_gadget_udc(&udc->gadget); Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/ZG+A/dNpFWAlCChk@kili Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/gadget/udc/amd5536udc_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c index c80f9bd51b75..a36913ae31f9 100644 --- a/drivers/usb/gadget/udc/amd5536udc_pci.c +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -170,6 +170,9 @@ static int udc_pci_probe( retval = -ENODEV; goto err_probe; } + + udc = dev; + return 0; err_probe: -- cgit 1.4.1 From 8a72260619ca53a37e7442d44f2f37d0cbc12645 Mon Sep 17 00:00:00 2001 From: Uday Shankar Date: Thu, 25 May 2023 12:22:02 -0600 Subject: nvme: double KA polling frequency to avoid KATO with TBKAS on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ea4d453b9ec9ea279c39744cd0ecb47ef48ede35 ] With TBKAS on, the completion of one command can defer sending a keep alive for up to twice the delay between successive runs of nvme_keep_alive_work. The current delay of KATO / 2 thus makes it possible for one command to defer sending a keep alive for up to KATO, which can result in the controller detecting a KATO. The following trace demonstrates the issue, taking KATO = 8 for simplicity: 1. t = 0: run nvme_keep_alive_work, no keep-alive sent 2. t = ε: I/O completion seen, set comp_seen = true 3. t = 4: run nvme_keep_alive_work, see comp_seen == true, skip sending keep-alive, set comp_seen = false 4. t = 8: run nvme_keep_alive_work, see comp_seen == false, send a keep-alive command. Here, there is a delay of 8 - ε between receiving a command completion and sending the next command. With ε small, the controller is likely to detect a keep alive timeout. Fix this by running nvme_keep_alive_work with a delay of KATO / 4 whenever TBKAS is on. Going through the above trace now gives us a worst-case delay of 4 - ε, which is in line with the recommendation of sending a command every KATO / 2 in the NVMe specification. Reported-by: Costa Sapuntzakis Reported-by: Randy Jennings Signed-off-by: Uday Shankar Reviewed-by: Hannes Reinecke Reviewed-by: Sagi Grimberg Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 2e22c78991cc..a97f2f21c532 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1198,9 +1198,25 @@ EXPORT_SYMBOL_NS_GPL(nvme_execute_passthru_rq, NVME_TARGET_PASSTHRU); * The host should send Keep Alive commands at half of the Keep Alive Timeout * accounting for transport roundtrip times [..]. */ +static unsigned long nvme_keep_alive_work_period(struct nvme_ctrl *ctrl) +{ + unsigned long delay = ctrl->kato * HZ / 2; + + /* + * When using Traffic Based Keep Alive, we need to run + * nvme_keep_alive_work at twice the normal frequency, as one + * command completion can postpone sending a keep alive command + * by up to twice the delay between runs. + */ + if (ctrl->ctratt & NVME_CTRL_ATTR_TBKAS) + delay /= 2; + return delay; +} + static void nvme_queue_keep_alive_work(struct nvme_ctrl *ctrl) { - queue_delayed_work(nvme_wq, &ctrl->ka_work, ctrl->kato * HZ / 2); + queue_delayed_work(nvme_wq, &ctrl->ka_work, + nvme_keep_alive_work_period(ctrl)); } static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, -- cgit 1.4.1 From 06d9ec407f76f895bbeea87501badc601da889c3 Mon Sep 17 00:00:00 2001 From: Uday Shankar Date: Thu, 25 May 2023 12:22:03 -0600 Subject: nvme: check IO start time when deciding to defer KA [ Upstream commit 774a9636514764ddc0d072ae0d1d1c01a47e6ddd ] When a command completes, we set a flag which will skip sending a keep alive at the next run of nvme_keep_alive_work when TBKAS is on. However, if the command was submitted long ago, it's possible that the controller may have also restarted its keep alive timer (as a result of receiving the command) long ago. The following trace demonstrates the issue, assuming TBKAS is on and KATO = 8 for simplicity: 1. t = 0: submit I/O commands A, B, C, D, E 2. t = 0.5: commands A, B, C, D, E reach controller, restart its keep alive timer 3. t = 1: A completes 4. t = 2: run nvme_keep_alive_work, see recent completion, do nothing 5. t = 3: B completes 6. t = 4: run nvme_keep_alive_work, see recent completion, do nothing 7. t = 5: C completes 8. t = 6: run nvme_keep_alive_work, see recent completion, do nothing 9. t = 7: D completes 10. t = 8: run nvme_keep_alive_work, see recent completion, do nothing 11. t = 9: E completes At this point, 8.5 seconds have passed without restarting the controller's keep alive timer, so the controller will detect a keep alive timeout. Fix this by checking the IO start time when deciding to defer sending a keep alive command. Only set comp_seen if the command started after the most recent run of nvme_keep_alive_work. With this change, the completions of B, C, and D will not set comp_seen and the run of nvme_keep_alive_work at t = 4 will send a keep alive. Reported-by: Costa Sapuntzakis Reported-by: Randy Jennings Signed-off-by: Uday Shankar Reviewed-by: Hannes Reinecke Reviewed-by: Sagi Grimberg Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 14 +++++++++++++- drivers/nvme/host/nvme.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index a97f2f21c532..15eb2ee1be66 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -395,7 +395,16 @@ void nvme_complete_rq(struct request *req) trace_nvme_complete_rq(req); nvme_cleanup_cmd(req); - if (ctrl->kas) + /* + * Completions of long-running commands should not be able to + * defer sending of periodic keep alives, since the controller + * may have completed processing such commands a long time ago + * (arbitrarily close to command submission time). + * req->deadline - req->timeout is the command submission time + * in jiffies. + */ + if (ctrl->kas && + req->deadline - req->timeout >= ctrl->ka_last_check_time) ctrl->comp_seen = true; switch (nvme_decide_disposition(req)) { @@ -1235,6 +1244,7 @@ static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, return RQ_END_IO_NONE; } + ctrl->ka_last_check_time = jiffies; ctrl->comp_seen = false; spin_lock_irqsave(&ctrl->lock, flags); if (ctrl->state == NVME_CTRL_LIVE || @@ -1253,6 +1263,8 @@ static void nvme_keep_alive_work(struct work_struct *work) bool comp_seen = ctrl->comp_seen; struct request *rq; + ctrl->ka_last_check_time = jiffies; + if ((ctrl->ctratt & NVME_CTRL_ATTR_TBKAS) && comp_seen) { dev_dbg(ctrl->device, "reschedule traffic based keep-alive timer\n"); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 3f82de6060ef..2aa514c3dfa1 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -323,6 +323,7 @@ struct nvme_ctrl { struct delayed_work ka_work; struct delayed_work failfast_work; struct nvme_command ka_cmd; + unsigned long ka_last_check_time; struct work_struct fw_act_work; unsigned long events; -- cgit 1.4.1 From ba0cc7a2e508d6b8f945afde47689126d9d6b702 Mon Sep 17 00:00:00 2001 From: Uday Shankar Date: Thu, 25 May 2023 12:22:04 -0600 Subject: nvme: improve handling of long keep alives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c7275ce6a5fd32ca9f5a6294ed89cf0523181af9 ] Upon keep alive completion, nvme_keep_alive_work is scheduled with the same delay every time. If keep alive commands are completing slowly, this may cause a keep alive timeout. The following trace illustrates the issue, taking KATO = 8 and TBKAS off for simplicity: 1. t = 0: run nvme_keep_alive_work, send keep alive 2. t = ε: keep alive reaches controller, controller restarts its keep alive timer 3. t = 4: host receives keep alive completion, schedules nvme_keep_alive_work with delay 4 4. t = 8: run nvme_keep_alive_work, send keep alive Here, a keep alive having RTT of 4 causes a delay of at least 8 - ε between the controller receiving successive keep alives. With ε small, the controller is likely to detect a keep alive timeout. Fix this by calculating the RTT of the keep alive command, and adjusting the scheduling delay of the next keep alive work accordingly. Reported-by: Costa Sapuntzakis Reported-by: Randy Jennings Signed-off-by: Uday Shankar Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 15eb2ee1be66..a7d9b5b42b38 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1234,6 +1234,20 @@ static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, struct nvme_ctrl *ctrl = rq->end_io_data; unsigned long flags; bool startka = false; + unsigned long rtt = jiffies - (rq->deadline - rq->timeout); + unsigned long delay = nvme_keep_alive_work_period(ctrl); + + /* + * Subtract off the keepalive RTT so nvme_keep_alive_work runs + * at the desired frequency. + */ + if (rtt <= delay) { + delay -= rtt; + } else { + dev_warn(ctrl->device, "long keepalive RTT (%u ms)\n", + jiffies_to_msecs(rtt)); + delay = 0; + } blk_mq_free_request(rq); @@ -1252,7 +1266,7 @@ static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, startka = true; spin_unlock_irqrestore(&ctrl->lock, flags); if (startka) - nvme_queue_keep_alive_work(ctrl); + queue_delayed_work(nvme_wq, &ctrl->ka_work, delay); return RQ_END_IO_NONE; } -- cgit 1.4.1 From 2e8ebf1a44cbeea239ef209c5c9077bf0ec7f732 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 11 May 2023 11:57:04 -0700 Subject: Input: soc_button_array - add invalid acpi_index DMI quirk handling [ Upstream commit 20a99a291d564a559cc2fd013b4824a3bb3f1db7 ] Some devices have a wrong entry in their button array which points to a GPIO which is required in another driver, so soc_button_array must not claim it. A specific example of this is the Lenovo Yoga Book X90F / X90L, where the PNP0C40 home button entry points to a GPIO which is not a home button and which is required by the lenovo-yogabook driver. Add a DMI quirk table which can specify an ACPI GPIO resource index which should be skipped; and add an entry for the Lenovo Yoga Book X90F / X90L to this new DMI quirk table. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230414072116.4497-1-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin --- drivers/input/misc/soc_button_array.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 09489380afda..e79f5497948b 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -108,6 +108,27 @@ static const struct dmi_system_id dmi_use_low_level_irq[] = { {} /* Terminating entry */ }; +/* + * Some devices have a wrong entry which points to a GPIO which is + * required in another driver, so this driver must not claim it. + */ +static const struct dmi_system_id dmi_invalid_acpi_index[] = { + { + /* + * Lenovo Yoga Book X90F / X90L, the PNP0C40 home button entry + * points to a GPIO which is not a home button and which is + * required by the lenovo-yogabook driver. + */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), + }, + .driver_data = (void *)1l, + }, + {} /* Terminating entry */ +}; + /* * Get the Nth GPIO number from the ACPI object. */ @@ -137,6 +158,8 @@ soc_button_device_create(struct platform_device *pdev, struct platform_device *pd; struct gpio_keys_button *gpio_keys; struct gpio_keys_platform_data *gpio_keys_pdata; + const struct dmi_system_id *dmi_id; + int invalid_acpi_index = -1; int error, gpio, irq; int n_buttons = 0; @@ -154,10 +177,17 @@ soc_button_device_create(struct platform_device *pdev, gpio_keys = (void *)(gpio_keys_pdata + 1); n_buttons = 0; + dmi_id = dmi_first_match(dmi_invalid_acpi_index); + if (dmi_id) + invalid_acpi_index = (long)dmi_id->driver_data; + for (info = button_info; info->name; info++) { if (info->autorepeat != autorepeat) continue; + if (info->acpi_index == invalid_acpi_index) + continue; + error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq); if (error || irq < 0) { /* -- cgit 1.4.1 From 4de58b7c14c059dbfb7fb14b20a498478943ba3f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 20 Feb 2023 10:54:00 +0100 Subject: arm64: dts: qcom: sc7280-idp: drop incorrect dai-cells from WCD938x SDW [ Upstream commit ca8fc6814844d8787e7fec61b2544a871ea8b675 ] The WCD938x audio codec Soundwire interface part is not a DAI and does not allow sound-dai-cells: sc7280-idp.dtb: codec@0,4: '#sound-dai-cells' does not match any of the regexes: 'pinctrl-[0-9]+' Signed-off-by: Krzysztof Kozlowski Reviewed-by: Douglas Anderson Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230220095401.64196-1-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc7280-idp.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi b/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi index ca50f0ba9b81..1c370dcfe60b 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi @@ -488,7 +488,6 @@ wcd_rx: codec@0,4 { compatible = "sdw20217010d00"; reg = <0 4>; - #sound-dai-cells = <1>; qcom,rx-port-mapping = <1 2 3 4 5>; }; }; @@ -499,7 +498,6 @@ wcd_tx: codec@0,3 { compatible = "sdw20217010d00"; reg = <0 3>; - #sound-dai-cells = <1>; qcom,tx-port-mapping = <1 2 3 4>; }; }; -- cgit 1.4.1 From 552a24eb716808298a710be2b00d9d76895842dc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 20 Feb 2023 10:54:01 +0100 Subject: arm64: dts: qcom: sc7280-qcard: drop incorrect dai-cells from WCD938x SDW [ Upstream commit 16bd455d0897d1b8b7a9aee2ed51d75b14a34563 ] The WCD938x audio codec Soundwire interface part is not a DAI and does not allow sound-dai-cells: sc7280-herobrine-crd.dtb: codec@0,4: '#sound-dai-cells' does not match any of the regexes: 'pinctrl-[0-9]+' Signed-off-by: Krzysztof Kozlowski Reviewed-by: Douglas Anderson Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230220095401.64196-2-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi index f7665b379923..c358abc052eb 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi @@ -418,7 +418,6 @@ wcd_rx: codec@0,4 { compatible = "sdw20217010d00"; reg = <0 4>; - #sound-dai-cells = <1>; qcom,rx-port-mapping = <1 2 3 4 5>; }; }; @@ -427,7 +426,6 @@ wcd_tx: codec@0,3 { compatible = "sdw20217010d00"; reg = <0 3>; - #sound-dai-cells = <1>; qcom,tx-port-mapping = <1 2 3 4>; }; }; -- cgit 1.4.1 From 39a77f005f8a7e2fb8ddc678883a34d0d5874437 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Thu, 4 May 2023 20:53:20 +0200 Subject: s390/cio: unregister device when the only path is gone [ Upstream commit 89c0c62e947a01e7a36b54582fd9c9e346170255 ] Currently, if the device is offline and all the channel paths are either configured or varied offline, the associated subchannel gets unregistered. Don't unregister the subchannel, instead unregister offline device. Signed-off-by: Vineeth Vijayan Reviewed-by: Peter Oberparleiter Signed-off-by: Alexander Gordeev Signed-off-by: Sasha Levin --- drivers/s390/cio/device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index ba4c69226c33..02813b63f90f 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1367,6 +1367,7 @@ void ccw_device_set_notoper(struct ccw_device *cdev) enum io_sch_action { IO_SCH_UNREG, IO_SCH_ORPH_UNREG, + IO_SCH_UNREG_CDEV, IO_SCH_ATTACH, IO_SCH_UNREG_ATTACH, IO_SCH_ORPH_ATTACH, @@ -1399,7 +1400,7 @@ static enum io_sch_action sch_get_action(struct subchannel *sch) } if ((sch->schib.pmcw.pam & sch->opm) == 0) { if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK) - return IO_SCH_UNREG; + return IO_SCH_UNREG_CDEV; return IO_SCH_DISC; } if (device_is_disconnected(cdev)) @@ -1461,6 +1462,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) case IO_SCH_ORPH_ATTACH: ccw_device_set_disconnected(cdev); break; + case IO_SCH_UNREG_CDEV: case IO_SCH_UNREG_ATTACH: case IO_SCH_UNREG: if (!cdev) @@ -1494,6 +1496,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) if (rc) goto out; break; + case IO_SCH_UNREG_CDEV: case IO_SCH_UNREG_ATTACH: spin_lock_irqsave(sch->lock, flags); sch_set_cdev(sch, NULL); -- cgit 1.4.1 From 06b9522ca831c505ff2874ef22c0cf0f7314aebb Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Fri, 5 May 2023 14:35:57 +0800 Subject: spi: lpspi: disable lpspi module irq in DMA mode [ Upstream commit 9728fb3ce11729aa8c276825ddf504edeb00611d ] When all bits of IER are set to 0, we still can observe the lpspi irq events when using DMA mode to transfer data. So disable irq to avoid the too much irq events. Signed-off-by: Clark Wang Link: https://lore.kernel.org/r/20230505063557.3962220-1-xiaoning.wang@nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-fsl-lpspi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index e8c1c8a4c6c8..9e324d72596a 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -905,9 +905,14 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller); if (ret == -EPROBE_DEFER) goto out_pm_get; - if (ret < 0) dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret); + else + /* + * disable LPSPI module IRQ when enable DMA mode successfully, + * to prevent the unexpected LPSPI module IRQ events. + */ + disable_irq(irq); ret = devm_spi_register_controller(&pdev->dev, controller); if (ret < 0) { -- cgit 1.4.1 From e701fb0a5d5b409f7390f7009cd9ac49f796b62d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 23 May 2023 17:54:14 +0100 Subject: ASoC: codecs: wcd938x-sdw: do not set can_multi_write flag [ Upstream commit 2d7c2f9272de6347a9cec0fc07708913692c0ae3 ] regmap-sdw does not support multi register writes, so there is no point in setting this flag. This also leads to incorrect programming of WSA codecs with regmap_multi_reg_write() call. This invalid configuration should have been rejected by regmap-sdw. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20230523165414.14560-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/wcd938x-sdw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 402286dfaea4..9c10200ff34b 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1190,7 +1190,6 @@ static const struct regmap_config wcd938x_regmap_config = { .readable_reg = wcd938x_readable_register, .writeable_reg = wcd938x_writeable_register, .volatile_reg = wcd938x_volatile_register, - .can_multi_write = true, }; static const struct sdw_slave_ops wcd9380_slave_ops = { -- cgit 1.4.1 From ee4d36a14d3f9b324f7717b105505d1014baf392 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 23 May 2023 17:12:22 +0200 Subject: ASoC: simple-card: Add missing of_node_put() in case of error [ Upstream commit 8938f75a5e35c597a647c28984a0304da7a33d63 ] In the error path, a of_node_put() for platform is missing. Just add it. Signed-off-by: Herve Codina Acked-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20230523151223.109551-9-herve.codina@bootlin.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/generic/simple-card.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index feb55b66239b..fbb682747f59 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -416,6 +416,7 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv, if (ret < 0) { of_node_put(codec); + of_node_put(plat); of_node_put(np); goto error; } -- cgit 1.4.1 From 2839e0b64e654d135b16886cb244e100de142105 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 15 May 2023 15:48:59 +0800 Subject: soundwire: dmi-quirks: add new mapping for HP Spectre x360 [ Upstream commit 700581ede41d029403feec935df4616309696fd7 ] A BIOS/DMI update seems to have broken some devices, let's add a new mapping. Link: https://github.com/thesofproject/linux/issues/4323 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20230515074859.3097-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/dmi-quirks.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 58ea013fa918..2a1096dab63d 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -99,6 +99,13 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)intel_tgl_bios, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8709"), + }, + .driver_data = (void *)intel_tgl_bios, + }, { /* quirk used for NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */ .matches = { -- cgit 1.4.1 From 5cc506e9b3e528f2afa7184a3b2e799879758cb7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 May 2023 18:37:36 +0200 Subject: soundwire: qcom: add proper error paths in qcom_swrm_startup() [ Upstream commit 99e09b9c0ab43346c52f2787ca4e5c4b1798362e ] Reverse actions in qcom_swrm_startup() error paths to avoid leaking stream memory and keeping runtime PM unbalanced. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20230517163736.997553-1-krzysztof.kozlowski@linaro.org Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/qcom.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 21c50972047f..b2eb3090f4b4 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1090,8 +1090,10 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, } sruntime = sdw_alloc_stream(dai->name); - if (!sruntime) - return -ENOMEM; + if (!sruntime) { + ret = -ENOMEM; + goto err_alloc; + } ctrl->sruntime[dai->id] = sruntime; @@ -1101,12 +1103,19 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, if (ret < 0 && ret != -ENOTSUPP) { dev_err(dai->dev, "Failed to set sdw stream on %s\n", codec_dai->name); - sdw_release_stream(sruntime); - return ret; + goto err_set_stream; } } return 0; + +err_set_stream: + sdw_release_stream(sruntime); +err_alloc: + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + + return ret; } static void qcom_swrm_shutdown(struct snd_pcm_substream *substream, -- cgit 1.4.1 From 1c97025d441f5f0615bb4aaf6dcd967638f7b388 Mon Sep 17 00:00:00 2001 From: Edson Juliano Drosdeck Date: Mon, 29 May 2023 15:19:11 -0300 Subject: ASoC: nau8824: Add quirk to active-high jack-detect [ Upstream commit e384dba03e3294ce7ea69e4da558e9bf8f0e8946 ] Add entries for Positivo laptops: CW14Q01P, K1424G, N14ZP74G to the DMI table, so that active-high jack-detect will work properly on these laptops. Signed-off-by: Edson Juliano Drosdeck Link: https://lore.kernel.org/r/20230529181911.632851-1-edson.drosdeck@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/nau8824.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 4f19fd9b65d1..5a4db8944d06 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -1903,6 +1903,30 @@ static const struct dmi_system_id nau8824_quirk_table[] = { }, .driver_data = (void *)(NAU8824_MONO_SPEAKER), }, + { + /* Positivo CW14Q01P */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, + { + /* Positivo K1424G */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "K1424G"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, + { + /* Positivo N14ZP74G */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "N14ZP74G"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, {} }; -- cgit 1.4.1 From 878dad66b9b5e86a2916858933037c1cb5db275a Mon Sep 17 00:00:00 2001 From: Sicong Jiang Date: Wed, 31 May 2023 21:06:35 +1200 Subject: ASoC: amd: yc: Add Thinkpad Neo14 to quirks list for acp6x [ Upstream commit 57d1e8900495cf1751cec74db16fe1a0fe47efbb ] Thinkpad Neo14 Ryzen Edition uses Ryzen 6800H processor, and adding to quirks list for acp6x will enable internal mic. Signed-off-by: Sicong Jiang Link: https://lore.kernel.org/r/20230531090635.89565-1-kevin.jiangsc@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 84b401b685f7..c1ca3ceac5f2 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -171,6 +171,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "21CL"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21EF"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit 1.4.1 From c2888c460db2a7d83c4d062a22d575b871d98dbc Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 31 May 2023 21:08:26 +0200 Subject: gfs2: Don't get stuck writing page onto itself under direct I/O [ Upstream commit fa58cc888d67e640e354d8b3ceef877ea167b0cf ] When a direct I/O write is performed, iomap_dio_rw() invalidates the part of the page cache which the write is going to before carrying out the write. In the odd case, the direct I/O write will be reading from the same page it is writing to. gfs2 carries out writes with page faults disabled, so it should have been obvious that this page invalidation can cause iomap_dio_rw() to never make any progress. Currently, gfs2 will end up in an endless retry loop in gfs2_file_direct_write() instead, though. Break this endless loop by limiting the number of retries and falling back to buffered I/O after that. Also simplify should_fault_in_pages() sightly and add a comment to make the above case easier to understand. Reported-by: Jan Kara Signed-off-by: Andreas Gruenbacher Signed-off-by: Sasha Levin --- fs/gfs2/file.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 60c6fb91fb58..bc6cd5f4b107 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -783,9 +783,13 @@ static inline bool should_fault_in_pages(struct iov_iter *i, if (!user_backed_iter(i)) return false; + /* + * Try to fault in multiple pages initially. When that doesn't result + * in any progress, fall back to a single page. + */ size = PAGE_SIZE; offs = offset_in_page(iocb->ki_pos); - if (*prev_count != count || !*window_size) { + if (*prev_count != count) { size_t nr_dirtied; nr_dirtied = max(current->nr_dirtied_pause - @@ -869,6 +873,7 @@ static ssize_t gfs2_file_direct_write(struct kiocb *iocb, struct iov_iter *from, struct gfs2_inode *ip = GFS2_I(inode); size_t prev_count = 0, window_size = 0; size_t written = 0; + bool enough_retries; ssize_t ret; /* @@ -912,11 +917,17 @@ retry: if (ret > 0) written = ret; + enough_retries = prev_count == iov_iter_count(from) && + window_size <= PAGE_SIZE; if (should_fault_in_pages(from, iocb, &prev_count, &window_size)) { gfs2_glock_dq(gh); window_size -= fault_in_iov_iter_readable(from, window_size); - if (window_size) - goto retry; + if (window_size) { + if (!enough_retries) + goto retry; + /* fall back to buffered I/O */ + ret = 0; + } } out_unlock: if (gfs2_holder_queued(gh)) -- cgit 1.4.1 From 570583c6251a2683fffe6e0ba4ba896f52f10be2 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 25 May 2023 19:41:45 +0200 Subject: s390/purgatory: disable branch profiling [ Upstream commit 03c5c83b70dca3729a3eb488e668e5044bd9a5ea ] Avoid linker error for randomly generated config file that has CONFIG_BRANCH_PROFILE_NONE enabled and make it similar to riscv, x86 and also to commit 4bf3ec384edf ("s390: disable branch profiling for vdso"). Reviewed-by: Vasily Gorbik Signed-off-by: Alexander Gordeev Signed-off-by: Sasha Levin --- arch/s390/purgatory/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index d237bc6841cb..4cbf306b8181 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile @@ -26,6 +26,7 @@ KBUILD_CFLAGS += -Wno-pointer-sign -Wno-sign-compare KBUILD_CFLAGS += -fno-zero-initialized-in-bss -fno-builtin -ffreestanding KBUILD_CFLAGS += -c -MD -Os -m64 -msoft-float -fno-common KBUILD_CFLAGS += -fno-stack-protector +KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING KBUILD_CFLAGS += $(CLANG_FLAGS) KBUILD_CFLAGS += $(call cc-option,-fno-PIE) KBUILD_AFLAGS := $(filter-out -DCC_USING_EXPOLINE,$(KBUILD_AFLAGS)) -- cgit 1.4.1 From cfa01235b5ed4a740b0cdb5118f58c0a7066ef23 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Tue, 30 May 2023 18:30:12 +0800 Subject: ASoC: fsl_sai: Enable BCI bit if SAI works on synchronous mode with BYP asserted [ Upstream commit 32cf0046a652116d6a216d575f3049a9ff9dd80d ] There's an issue on SAI synchronous mode that TX/RX side can't get BCLK from RX/TX it sync with if BYP bit is asserted. It's a workaround to fix it that enable SION of IOMUX pad control and assert BCI. For example if TX sync with RX which means both TX and RX are using clk form RX and BYP=1. TX can get BCLK only if the following two conditions are valid: 1. SION of RX BCLK IOMUX pad is set to 1 2. BCI of TX is set to 1 Signed-off-by: Chancel Liu Acked-by: Shengjiu Wang Link: https://lore.kernel.org/r/20230530103012.3448838-1-chancel.liu@nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/fsl/fsl_sai.c | 11 +++++++++-- sound/soc/fsl/fsl_sai.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 6d88af5b287f..b33104715c7b 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -491,14 +491,21 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - if (savediv == 1) + if (savediv == 1) { regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP, FSL_SAI_CR2_BYP); - else + if (fsl_sai_dir_is_synced(sai, adir)) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), + FSL_SAI_CR2_BCI, FSL_SAI_CR2_BCI); + else + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), + FSL_SAI_CR2_BCI, 0); + } else { regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP, savediv / 2 - 1); + } if (sai->soc_data->max_register >= FSL_SAI_MCTL) { /* SAI is in master mode at this point, so enable MCLK */ diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 697f6690068c..c5423f81e456 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -116,6 +116,7 @@ /* SAI Transmit and Receive Configuration 2 Register */ #define FSL_SAI_CR2_SYNC BIT(30) +#define FSL_SAI_CR2_BCI BIT(28) #define FSL_SAI_CR2_MSEL_MASK (0x3 << 26) #define FSL_SAI_CR2_MSEL_BUS 0 #define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) -- cgit 1.4.1 From 8e32575994098117e395ce66a9b391850ee9b24c Mon Sep 17 00:00:00 2001 From: "Sayed, Karimuddin" Date: Fri, 2 Jun 2023 14:38:12 -0500 Subject: ALSA: hda/realtek: Add "Intel Reference board" and "NUC 13" SSID in the ALC256 [ Upstream commit 1a93f10c5b12bd766a537b24a50fca5373467303 ] Add "Intel Reference boad" and "Intel NUC 13" SSID in the alc256. Enable jack headset volume buttons Reviewed-by: Kai Vehmanen Signed-off-by: Sayed, Karimuddin Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20230602193812.66768-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 920e44ba998a..eb049014f87a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9594,6 +9594,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC225_FIXUP_HEADSET_JACK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), @@ -9814,6 +9815,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), + SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC225_FIXUP_HEADSET_JACK), SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), #if 0 -- cgit 1.4.1 From 1d1baefacdb48c81b8bf389265a4c3072c9aa08a Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 10 May 2023 14:32:17 +0200 Subject: i2c: mchp-pci1xxxx: Avoid cast to incompatible function type [ Upstream commit 7ebfd881abe9e0ea9557b29dab6aa28d294fabb4 ] Rather than casting pci1xxxx_i2c_shutdown to an incompatible function type, update the type to match that expected by __devm_add_action. Reported by clang-16 with W-1: .../i2c-mchp-pci1xxxx.c:1159:29: error: cast from 'void (*)(struct pci1xxxx_i2c *)' to 'void (*)(void *)' converts to incompatible function type [-Werror,-Wcast-function-type-strict] ret = devm_add_action(dev, (void (*)(void *))pci1xxxx_i2c_shutdown, i2c); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ./include/linux/device.h:251:29: note: expanded from macro 'devm_add_action' __devm_add_action(release, action, data, #action) ^~~~~~ No functional change intended. Compile tested only. Signed-off-by: Simon Horman Reviewed-by: Horatiu Vultur Reviewed-by: Andi Shyti Reviewed-by: Tharun Kumar P Signed-off-by: Wolfram Sang Signed-off-by: Sasha Levin --- drivers/i2c/busses/i2c-mchp-pci1xxxx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-mchp-pci1xxxx.c b/drivers/i2c/busses/i2c-mchp-pci1xxxx.c index b21ffd6df927..5ef136c3ecb1 100644 --- a/drivers/i2c/busses/i2c-mchp-pci1xxxx.c +++ b/drivers/i2c/busses/i2c-mchp-pci1xxxx.c @@ -1118,8 +1118,10 @@ static int pci1xxxx_i2c_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_i2c_pm_ops, pci1xxxx_i2c_suspend, pci1xxxx_i2c_resume); -static void pci1xxxx_i2c_shutdown(struct pci1xxxx_i2c *i2c) +static void pci1xxxx_i2c_shutdown(void *data) { + struct pci1xxxx_i2c *i2c = data; + pci1xxxx_i2c_config_padctrl(i2c, false); pci1xxxx_i2c_configure_core_reg(i2c, false); } @@ -1156,7 +1158,7 @@ static int pci1xxxx_i2c_probe_pci(struct pci_dev *pdev, init_completion(&i2c->i2c_xfer_done); pci1xxxx_i2c_init(i2c); - ret = devm_add_action(dev, (void (*)(void *))pci1xxxx_i2c_shutdown, i2c); + ret = devm_add_action(dev, pci1xxxx_i2c_shutdown, i2c); if (ret) return ret; -- cgit 1.4.1 From f6076a1386c99f06a99266c9e5ac10faede1fb94 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 10 May 2023 12:51:56 +0200 Subject: ARM: dts: Fix erroneous ADS touchscreen polarities [ Upstream commit 4a672d500bfd6bb87092c33d5a2572c3d0a1cf83 ] Several device tree files get the polarity of the pendown-gpios wrong: this signal is active low. Fix up all incorrect flags, so that operating systems can rely on the flag being correctly set. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20230510105156.1134320-1-linus.walleij@linaro.org Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/boot/dts/am57xx-cl-som-am57x.dts | 2 +- arch/arm/boot/dts/at91sam9261ek.dts | 2 +- arch/arm/boot/dts/imx7d-pico-hobbit.dts | 2 +- arch/arm/boot/dts/imx7d-sdb.dts | 2 +- arch/arm/boot/dts/omap3-cm-t3x.dtsi | 2 +- arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi | 2 +- arch/arm/boot/dts/omap3-lilly-a83x.dtsi | 2 +- arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi | 2 +- arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi | 2 +- arch/arm/boot/dts/omap3-pandora-common.dtsi | 2 +- arch/arm/boot/dts/omap5-cm-t54.dts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm/boot/dts/am57xx-cl-som-am57x.dts b/arch/arm/boot/dts/am57xx-cl-som-am57x.dts index 2fc9a5d5e0c0..625b9b311b49 100644 --- a/arch/arm/boot/dts/am57xx-cl-som-am57x.dts +++ b/arch/arm/boot/dts/am57xx-cl-som-am57x.dts @@ -527,7 +527,7 @@ interrupt-parent = <&gpio1>; interrupts = <31 0>; - pendown-gpio = <&gpio1 31 0>; + pendown-gpio = <&gpio1 31 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts index 88869ca874d1..045cb253f23a 100644 --- a/arch/arm/boot/dts/at91sam9261ek.dts +++ b/arch/arm/boot/dts/at91sam9261ek.dts @@ -156,7 +156,7 @@ compatible = "ti,ads7843"; interrupts-extended = <&pioC 2 IRQ_TYPE_EDGE_BOTH>; spi-max-frequency = <3000000>; - pendown-gpio = <&pioC 2 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&pioC 2 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <150>; ti,x-max = /bits/ 16 <3830>; diff --git a/arch/arm/boot/dts/imx7d-pico-hobbit.dts b/arch/arm/boot/dts/imx7d-pico-hobbit.dts index d917dc4f2f22..6ad39dca7009 100644 --- a/arch/arm/boot/dts/imx7d-pico-hobbit.dts +++ b/arch/arm/boot/dts/imx7d-pico-hobbit.dts @@ -64,7 +64,7 @@ interrupt-parent = <&gpio2>; interrupts = <7 0>; spi-max-frequency = <1000000>; - pendown-gpio = <&gpio2 7 0>; + pendown-gpio = <&gpio2 7 GPIO_ACTIVE_LOW>; vcc-supply = <®_3p3v>; ti,x-min = /bits/ 16 <0>; ti,x-max = /bits/ 16 <4095>; diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts index f483bc0afe5e..234e5fc647b2 100644 --- a/arch/arm/boot/dts/imx7d-sdb.dts +++ b/arch/arm/boot/dts/imx7d-sdb.dts @@ -205,7 +205,7 @@ pinctrl-0 = <&pinctrl_tsc2046_pendown>; interrupt-parent = <&gpio2>; interrupts = <29 0>; - pendown-gpio = <&gpio2 29 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio2 29 GPIO_ACTIVE_LOW>; touchscreen-max-pressure = <255>; wakeup-source; }; diff --git a/arch/arm/boot/dts/omap3-cm-t3x.dtsi b/arch/arm/boot/dts/omap3-cm-t3x.dtsi index e61b8a2bfb7d..51baedf1603b 100644 --- a/arch/arm/boot/dts/omap3-cm-t3x.dtsi +++ b/arch/arm/boot/dts/omap3-cm-t3x.dtsi @@ -227,7 +227,7 @@ interrupt-parent = <&gpio2>; interrupts = <25 0>; /* gpio_57 */ - pendown-gpio = <&gpio2 25 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio2 25 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi b/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi index 3decc2d78a6c..a7f99ae0c1fe 100644 --- a/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi +++ b/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi @@ -54,7 +54,7 @@ interrupt-parent = <&gpio1>; interrupts = <27 0>; /* gpio_27 */ - pendown-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio1 27 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-lilly-a83x.dtsi b/arch/arm/boot/dts/omap3-lilly-a83x.dtsi index c595afe4181d..d310b5c7bac3 100644 --- a/arch/arm/boot/dts/omap3-lilly-a83x.dtsi +++ b/arch/arm/boot/dts/omap3-lilly-a83x.dtsi @@ -311,7 +311,7 @@ interrupt-parent = <&gpio1>; interrupts = <8 0>; /* boot6 / gpio_8 */ spi-max-frequency = <1000000>; - pendown-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio1 8 GPIO_ACTIVE_LOW>; vcc-supply = <®_vcc3>; pinctrl-names = "default"; pinctrl-0 = <&tsc2048_pins>; diff --git a/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi b/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi index 1d6e88f99eb3..c3570acc35fa 100644 --- a/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi +++ b/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi @@ -149,7 +149,7 @@ interrupt-parent = <&gpio4>; interrupts = <18 0>; /* gpio_114 */ - pendown-gpio = <&gpio4 18 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio4 18 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi b/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi index 7e30f9d45790..d95a0e130058 100644 --- a/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi +++ b/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi @@ -160,7 +160,7 @@ interrupt-parent = <&gpio4>; interrupts = <18 0>; /* gpio_114 */ - pendown-gpio = <&gpio4 18 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio4 18 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-pandora-common.dtsi b/arch/arm/boot/dts/omap3-pandora-common.dtsi index 559853764487..4c3b6bab179c 100644 --- a/arch/arm/boot/dts/omap3-pandora-common.dtsi +++ b/arch/arm/boot/dts/omap3-pandora-common.dtsi @@ -651,7 +651,7 @@ pinctrl-0 = <&penirq_pins>; interrupt-parent = <&gpio3>; interrupts = <30 IRQ_TYPE_NONE>; /* GPIO_94 */ - pendown-gpio = <&gpio3 30 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio3 30 GPIO_ACTIVE_LOW>; vcc-supply = <&vaux4>; ti,x-min = /bits/ 16 <0>; diff --git a/arch/arm/boot/dts/omap5-cm-t54.dts b/arch/arm/boot/dts/omap5-cm-t54.dts index ca759b7b8a58..e62ea8b6d53f 100644 --- a/arch/arm/boot/dts/omap5-cm-t54.dts +++ b/arch/arm/boot/dts/omap5-cm-t54.dts @@ -354,7 +354,7 @@ interrupt-parent = <&gpio1>; interrupts = <15 0>; /* gpio1_wk15 */ - pendown-gpio = <&gpio1 15 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio1 15 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; -- cgit 1.4.1 From 711f727f7bab5f244ceef99fdb8248ed75c05b22 Mon Sep 17 00:00:00 2001 From: Nitesh Shetty Date: Mon, 5 Jun 2023 11:53:53 +0530 Subject: null_blk: Fix: memory release when memory_backed=1 [ Upstream commit 8cfb98196cceec35416041c6b91212d2b99392e4 ] Memory/pages are not freed, when unloading nullblk driver. Steps to reproduce issue 1.free -h total used free shared buff/cache available Mem: 7.8Gi 260Mi 7.1Gi 3.0Mi 395Mi 7.3Gi Swap: 0B 0B 0B 2.modprobe null_blk memory_backed=1 3.dd if=/dev/urandom of=/dev/nullb0 oflag=direct bs=1M count=1000 4.modprobe -r null_blk 5.free -h total used free shared buff/cache available Mem: 7.8Gi 1.2Gi 6.1Gi 3.0Mi 398Mi 6.3Gi Swap: 0B 0B 0B Signed-off-by: Anuj Gupta Signed-off-by: Nitesh Shetty Link: https://lore.kernel.org/r/20230605062354.24785-1-nj.shetty@samsung.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/block/null_blk/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index c45d09a9a942..e8cb914223cd 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -2194,6 +2194,7 @@ static void null_destroy_dev(struct nullb *nullb) struct nullb_device *dev = nullb->dev; null_del_dev(nullb); + null_free_device_storage(dev, false); null_free_dev(dev); } -- cgit 1.4.1 From aa88042218aaef183f5707ed0274520f19f45bb7 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Fri, 19 May 2023 08:55:05 +0900 Subject: drm/exynos: vidi: fix a wrong error return [ Upstream commit 4a059559809fd1ddbf16f847c4d2237309c08edf ] Fix a wrong error return by dropping an error return. When vidi driver is remvoed, if ctx->raw_edid isn't same as fake_edid_info then only what we have to is to free ctx->raw_edid so that driver removing can work correctly - it's not an error case. Signed-off-by: Inki Dae Reviewed-by: Andi Shyti Signed-off-by: Sasha Levin --- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 4d56c8c799c5..f5e1adfcaa51 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -469,8 +469,6 @@ static int vidi_remove(struct platform_device *pdev) if (ctx->raw_edid != (struct edid *)fake_edid_info) { kfree(ctx->raw_edid); ctx->raw_edid = NULL; - - return -EINVAL; } component_del(&pdev->dev, &vidi_component_ops); -- cgit 1.4.1 From 022f2306d94f87defde366729938c5c54a3d12ff Mon Sep 17 00:00:00 2001 From: Min Li Date: Fri, 26 May 2023 21:01:31 +0800 Subject: drm/exynos: fix race condition UAF in exynos_g2d_exec_ioctl [ Upstream commit 48bfd02569f5db49cc033f259e66d57aa6efc9a3 ] If it is async, runqueue_node is freed in g2d_runqueue_worker on another worker thread. So in extreme cases, if g2d_runqueue_worker runs first, and then executes the following if statement, there will be use-after-free. Signed-off-by: Min Li Reviewed-by: Andi Shyti Signed-off-by: Inki Dae Signed-off-by: Sasha Levin --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 471fd6c8135f..27613abeed96 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -1335,7 +1335,7 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, /* Let the runqueue know that there is work to do. */ queue_work(g2d->g2d_workq, &g2d->runqueue_work); - if (runqueue_node->async) + if (req->async) goto out; wait_for_completion(&runqueue_node->complete); -- cgit 1.4.1 From 1af1cd7be370b08694d8752c97325fe51fdab6aa Mon Sep 17 00:00:00 2001 From: Min Li Date: Sat, 3 Jun 2023 15:43:45 +0800 Subject: drm/radeon: fix race condition UAF in radeon_gem_set_domain_ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 982b173a6c6d9472730c3116051977e05d17c8c5 ] Userspace can race to free the gobj(robj converted from), robj should not be accessed again after drm_gem_object_put, otherwith it will result in use-after-free. Reviewed-by: Christian König Signed-off-by: Min Li Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/radeon/radeon_gem.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index 261fcbae88d7..75d79c311038 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -459,7 +459,6 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct radeon_device *rdev = dev->dev_private; struct drm_radeon_gem_set_domain *args = data; struct drm_gem_object *gobj; - struct radeon_bo *robj; int r; /* for now if someone requests domain CPU - @@ -472,13 +471,12 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, up_read(&rdev->exclusive_lock); return -ENOENT; } - robj = gem_to_radeon_bo(gobj); r = radeon_gem_set_domain(gobj, args->read_domains, args->write_domain); drm_gem_object_put(gobj); up_read(&rdev->exclusive_lock); - r = radeon_gem_handle_lockup(robj->rdev, r); + r = radeon_gem_handle_lockup(rdev, r); return r; } -- cgit 1.4.1 From 454e4f391a144732d327ccd6ee4ee5e33ca59913 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 24 Apr 2023 15:50:29 -0700 Subject: vhost_vdpa: tell vqs about the negotiated [ Upstream commit 376daf317753ccb6b1ecbdece66018f7f6313a7f ] As is done in the net, iscsi, and vsock vhost support, let the vdpa vqs know about the features that have been negotiated. This allows vhost to more safely make decisions based on the features, such as when using PACKED vs split queues. Signed-off-by: Shannon Nelson Acked-by: Jason Wang Message-Id: <20230424225031.18947-2-shannon.nelson@amd.com> Signed-off-by: Michael S. Tsirkin Signed-off-by: Sasha Levin --- drivers/vhost/vdpa.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index d591f77961aa..31a156669a53 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -377,7 +377,10 @@ static long vhost_vdpa_set_features(struct vhost_vdpa *v, u64 __user *featurep) { struct vdpa_device *vdpa = v->vdpa; const struct vdpa_config_ops *ops = vdpa->config; + struct vhost_dev *d = &v->vdev; + u64 actual_features; u64 features; + int i; /* * It's not allowed to change the features after they have @@ -392,6 +395,16 @@ static long vhost_vdpa_set_features(struct vhost_vdpa *v, u64 __user *featurep) if (vdpa_set_features(vdpa, features)) return -EINVAL; + /* let the vqs know what has been configured */ + actual_features = ops->get_driver_features(vdpa); + for (i = 0; i < d->nvqs; ++i) { + struct vhost_virtqueue *vq = d->vqs[i]; + + mutex_lock(&vq->mutex); + vq->acked_features = actual_features; + mutex_unlock(&vq->mutex); + } + return 0; } -- cgit 1.4.1 From 6ab9468d3eeaf243956cd05b21348e0f7a3e979f Mon Sep 17 00:00:00 2001 From: Andrey Smetanin Date: Mon, 24 Apr 2023 23:44:11 +0300 Subject: vhost_net: revert upend_idx only on retriable error [ Upstream commit 1f5d2e3bab16369d5d4b4020a25db4ab1f4f082c ] Fix possible virtqueue used buffers leak and corresponding stuck in case of temporary -EIO from sendmsg() which is produced by tun driver while backend device is not up. In case of no-retriable error and zcopy do not revert upend_idx to pass packet data (that is update used_idx in corresponding vhost_zerocopy_signal_used()) as if packet data has been transferred successfully. v2: set vq->heads[ubuf->desc].len equal to VHOST_DMA_DONE_LEN in case of fake successful transmit. Signed-off-by: Andrey Smetanin Message-Id: <20230424204411.24888-1-asmetanin@yandex-team.ru> Signed-off-by: Michael S. Tsirkin Signed-off-by: Andrey Smetanin Acked-by: Jason Wang Signed-off-by: Sasha Levin --- drivers/vhost/net.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 4c538b30fd76..4418192ab8aa 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -934,13 +934,18 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock) err = sock->ops->sendmsg(sock, &msg, len); if (unlikely(err < 0)) { + bool retry = err == -EAGAIN || err == -ENOMEM || err == -ENOBUFS; + if (zcopy_used) { if (vq->heads[ubuf->desc].len == VHOST_DMA_IN_PROGRESS) vhost_net_ubuf_put(ubufs); - nvq->upend_idx = ((unsigned)nvq->upend_idx - 1) - % UIO_MAXIOV; + if (retry) + nvq->upend_idx = ((unsigned)nvq->upend_idx - 1) + % UIO_MAXIOV; + else + vq->heads[ubuf->desc].len = VHOST_DMA_DONE_LEN; } - if (err == -EAGAIN || err == -ENOMEM || err == -ENOBUFS) { + if (retry) { vhost_discard_vq_desc(vq, 1); vhost_net_enable_vq(net, vq); break; -- cgit 1.4.1 From 679354bea008d548ab668776ea169db202cbd751 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 7 Jun 2023 15:38:44 +0100 Subject: KVM: arm64: Restore GICv2-on-GICv3 functionality commit 1caa71a7a600f7781ce05ef1e84701c459653663 upstream. When reworking the vgic locking, the vgic distributor registration got simplified, which was a very good cleanup. But just a tad too radical, as we now register the *native* vgic only, ignoring the GICv2-on-GICv3 that allows pre-historic VMs (or so I thought) to run. As it turns out, QEMU still defaults to GICv2 in some cases, and this breaks Nathan's setup! Fix it by propagating the *requested* vgic type rather than the host's version. Fixes: 59112e9c390b ("KVM: arm64: vgic: Fix a circular locking issue") Reported-by: Nathan Chancellor Tested-by: Nathan Chancellor Signed-off-by: Marc Zyngier link: https://lore.kernel.org/r/20230606221525.GA2269598@dev-arch.thelio-3990X Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic-init.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 37e74ca4dad8..f2f3bf4a04b0 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -446,6 +446,7 @@ int vgic_lazy_init(struct kvm *kvm) int kvm_vgic_map_resources(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; + enum vgic_type type; gpa_t dist_base; int ret = 0; @@ -460,10 +461,13 @@ int kvm_vgic_map_resources(struct kvm *kvm) if (!irqchip_in_kernel(kvm)) goto out; - if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) + if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) { ret = vgic_v2_map_resources(kvm); - else + type = VGIC_V2; + } else { ret = vgic_v3_map_resources(kvm); + type = VGIC_V3; + } if (ret) { __kvm_vgic_destroy(kvm); @@ -473,8 +477,7 @@ int kvm_vgic_map_resources(struct kvm *kvm) dist_base = dist->vgic_dist_base; mutex_unlock(&kvm->arch.config_lock); - ret = vgic_register_dist_iodev(kvm, dist_base, - kvm_vgic_global_state.type); + ret = vgic_register_dist_iodev(kvm, dist_base, type); if (ret) { kvm_err("Unable to register VGIC dist MMIO regions\n"); kvm_vgic_destroy(kvm); -- cgit 1.4.1 From 81d4078f7a4249a939c977fa8ed574d1004be712 Mon Sep 17 00:00:00 2001 From: Dheeraj Kumar Srivastava Date: Sat, 17 Jun 2023 02:52:36 +0530 Subject: x86/apic: Fix kernel panic when booting with intremap=off and x2apic_phys [ Upstream commit 85d38d5810e285d5aec7fb5283107d1da70c12a9 ] When booting with "intremap=off" and "x2apic_phys" on the kernel command line, the physical x2APIC driver ends up being used even when x2APIC mode is disabled ("intremap=off" disables x2APIC mode). This happens because the first compound condition check in x2apic_phys_probe() is false due to x2apic_mode == 0 and so the following one returns true after default_acpi_madt_oem_check() having already selected the physical x2APIC driver. This results in the following panic: kernel BUG at arch/x86/kernel/apic/io_apic.c:2409! invalid opcode: 0000 [#1] PREEMPT SMP NOPTI CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.4.0-rc2-ver4.1rc2 #2 Hardware name: Dell Inc. PowerEdge R6515/07PXPY, BIOS 2.3.6 07/06/2021 RIP: 0010:setup_IO_APIC+0x9c/0xaf0 Call Trace: ? native_read_msr apic_intr_mode_init x86_late_time_init start_kernel x86_64_start_reservations x86_64_start_kernel secondary_startup_64_no_verify which is: setup_IO_APIC: apic_printk(APIC_VERBOSE, "ENABLING IO-APIC IRQs\n"); for_each_ioapic(ioapic) BUG_ON(mp_irqdomain_create(ioapic)); Return 0 to denote that x2APIC has not been enabled when probing the physical x2APIC driver. [ bp: Massage commit message heavily. ] Fixes: 9ebd680bd029 ("x86, apic: Use probe routines to simplify apic selection") Signed-off-by: Dheeraj Kumar Srivastava Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Vasant Hegde Reviewed-by: Cyrill Gorcunov Reviewed-by: Thomas Gleixner Link: https://lore.kernel.org/r/20230616212236.1389-1-dheerajkumar.srivastava@amd.com Signed-off-by: Sasha Levin --- arch/x86/kernel/apic/x2apic_phys.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/apic/x2apic_phys.c b/arch/x86/kernel/apic/x2apic_phys.c index 6bde05a86b4e..896bc41cb2ba 100644 --- a/arch/x86/kernel/apic/x2apic_phys.c +++ b/arch/x86/kernel/apic/x2apic_phys.c @@ -97,7 +97,10 @@ static void init_x2apic_ldr(void) static int x2apic_phys_probe(void) { - if (x2apic_mode && (x2apic_phys || x2apic_fadt_phys())) + if (!x2apic_mode) + return 0; + + if (x2apic_phys || x2apic_fadt_phys()) return 1; return apic == &apic_x2apic_phys; -- cgit 1.4.1 From 9d3e4bca4b92bb64491ce7b83482494fe70455bf Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Mon, 29 May 2023 16:02:51 +0800 Subject: i2c: imx-lpi2c: fix type char overflow issue when calculating the clock cycle [ Upstream commit e69b9bc170c6d93ee375a5cbfd15f74c0fb59bdd ] Claim clkhi and clklo as integer type to avoid possible calculation errors caused by data overflow. Fixes: a55fa9d0e42e ("i2c: imx-lpi2c: add low power i2c bus driver") Signed-off-by: Clark Wang Signed-off-by: Carlos Song Reviewed-by: Andi Shyti Signed-off-by: Wolfram Sang Signed-off-by: Sasha Levin --- drivers/i2c/busses/i2c-imx-lpi2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index a49b14d52a98..ff12018bc206 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -201,8 +201,8 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx) /* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */ static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx) { - u8 prescale, filt, sethold, clkhi, clklo, datavd; - unsigned int clk_rate, clk_cycle; + u8 prescale, filt, sethold, datavd; + unsigned int clk_rate, clk_cycle, clkhi, clklo; enum lpi2c_imx_pincfg pincfg; unsigned int temp; -- cgit 1.4.1 From 29429a1f5871dbe54ee0da81bb12db8567f15379 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 21 May 2023 20:46:30 -0500 Subject: smb: move client and server files to common directory fs/smb commit 38c8a9a52082579090e34c033d439ed2cd1a462d upstream. Move CIFS/SMB3 related client and server files (cifs.ko and ksmbd.ko and helper modules) to new fs/smb subdirectory: fs/cifs --> fs/smb/client fs/ksmbd --> fs/smb/server fs/smbfs_common --> fs/smb/common Suggested-by: Linus Torvalds Acked-by: Namjae Jeon Signed-off-by: Steve French [ added to stable trees to handle the directory change to handle the future stable patches due to the constant churn in this filesystem at the moment - gregkh ] Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 8 +- fs/Kconfig | 9 +- fs/Makefile | 4 +- fs/cifs/Kconfig | 203 - fs/cifs/Makefile | 34 - fs/cifs/asn1.c | 63 - fs/cifs/cached_dir.c | 606 -- fs/cifs/cached_dir.h | 80 - fs/cifs/cifs_debug.c | 1067 ---- fs/cifs/cifs_debug.h | 160 - fs/cifs/cifs_dfs_ref.c | 374 -- fs/cifs/cifs_fs_sb.h | 76 - fs/cifs/cifs_ioctl.h | 126 - fs/cifs/cifs_spnego.c | 236 - fs/cifs/cifs_spnego.h | 36 - fs/cifs/cifs_spnego_negtokeninit.asn1 | 40 - fs/cifs/cifs_swn.c | 674 -- fs/cifs/cifs_swn.h | 52 - fs/cifs/cifs_unicode.c | 632 -- fs/cifs/cifs_unicode.h | 404 -- fs/cifs/cifs_uniupr.h | 239 - fs/cifs/cifsacl.c | 1672 ----- fs/cifs/cifsacl.h | 199 - fs/cifs/cifsencrypt.c | 733 --- fs/cifs/cifsfs.c | 1857 ------ fs/cifs/cifsfs.h | 161 - fs/cifs/cifsglob.h | 2208 ------- fs/cifs/cifspdu.h | 2730 -------- fs/cifs/cifsproto.h | 696 -- fs/cifs/cifsroot.c | 94 - fs/cifs/cifssmb.c | 5873 ----------------- fs/cifs/connect.c | 4754 -------------- fs/cifs/dfs_cache.c | 1690 ----- fs/cifs/dfs_cache.h | 97 - fs/cifs/dir.c | 867 --- fs/cifs/dns_resolve.c | 89 - fs/cifs/dns_resolve.h | 18 - fs/cifs/export.c | 54 - fs/cifs/file.c | 5290 ---------------- fs/cifs/fs_context.c | 1773 ------ fs/cifs/fs_context.h | 293 - fs/cifs/fscache.c | 253 - fs/cifs/fscache.h | 148 - fs/cifs/inode.c | 3093 --------- fs/cifs/ioctl.c | 526 -- fs/cifs/link.c | 650 -- fs/cifs/misc.c | 1434 ----- fs/cifs/netlink.c | 90 - fs/cifs/netlink.h | 16 - fs/cifs/netmisc.c | 1021 --- fs/cifs/nterr.c | 674 -- fs/cifs/nterr.h | 551 -- fs/cifs/ntlmssp.h | 157 - fs/cifs/readdir.c | 1237 ---- fs/cifs/rfc1002pdu.h | 61 - fs/cifs/sess.c | 1858 ------ fs/cifs/smb1ops.c | 1276 ---- fs/cifs/smb2file.c | 371 -- fs/cifs/smb2glob.h | 44 - fs/cifs/smb2inode.c | 799 --- fs/cifs/smb2maperror.c | 2481 -------- fs/cifs/smb2misc.c | 944 --- fs/cifs/smb2ops.c | 5805 ----------------- fs/cifs/smb2pdu.c | 5722 ----------------- fs/cifs/smb2pdu.h | 434 -- fs/cifs/smb2proto.h | 284 - fs/cifs/smb2status.h | 1769 ------ fs/cifs/smb2transport.c | 934 --- fs/cifs/smbdirect.c | 2494 -------- fs/cifs/smbdirect.h | 320 - fs/cifs/smbencrypt.c | 91 - fs/cifs/smberr.h | 171 - fs/cifs/trace.c | 8 - fs/cifs/trace.h | 1070 ---- fs/cifs/transport.c | 1807 ------ fs/cifs/unc.c | 69 - fs/cifs/winucase.c | 649 -- fs/cifs/xattr.c | 555 -- fs/ksmbd/Kconfig | 68 - fs/ksmbd/Makefile | 20 - fs/ksmbd/asn1.c | 239 - fs/ksmbd/asn1.h | 21 - fs/ksmbd/auth.c | 1206 ---- fs/ksmbd/auth.h | 71 - fs/ksmbd/connection.c | 485 -- fs/ksmbd/connection.h | 231 - fs/ksmbd/crypto_ctx.c | 266 - fs/ksmbd/crypto_ctx.h | 66 - fs/ksmbd/glob.h | 49 - fs/ksmbd/ksmbd_netlink.h | 412 -- fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 | 31 - fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 | 19 - fs/ksmbd/ksmbd_work.c | 79 - fs/ksmbd/ksmbd_work.h | 117 - fs/ksmbd/mgmt/ksmbd_ida.c | 46 - fs/ksmbd/mgmt/ksmbd_ida.h | 34 - fs/ksmbd/mgmt/share_config.c | 234 - fs/ksmbd/mgmt/share_config.h | 82 - fs/ksmbd/mgmt/tree_connect.c | 147 - fs/ksmbd/mgmt/tree_connect.h | 61 - fs/ksmbd/mgmt/user_config.c | 79 - fs/ksmbd/mgmt/user_config.h | 68 - fs/ksmbd/mgmt/user_session.c | 400 -- fs/ksmbd/mgmt/user_session.h | 103 - fs/ksmbd/misc.c | 381 -- fs/ksmbd/misc.h | 37 - fs/ksmbd/ndr.c | 514 -- fs/ksmbd/ndr.h | 22 - fs/ksmbd/nterr.h | 543 -- fs/ksmbd/ntlmssp.h | 169 - fs/ksmbd/oplock.c | 1722 ----- fs/ksmbd/oplock.h | 127 - fs/ksmbd/server.c | 646 -- fs/ksmbd/server.h | 71 - fs/ksmbd/smb2misc.c | 454 -- fs/ksmbd/smb2ops.c | 314 - fs/ksmbd/smb2pdu.c | 8727 -------------------------- fs/ksmbd/smb2pdu.h | 536 -- fs/ksmbd/smb_common.c | 797 --- fs/ksmbd/smb_common.h | 468 -- fs/ksmbd/smbacl.c | 1436 ----- fs/ksmbd/smbacl.h | 238 - fs/ksmbd/smbfsctl.h | 91 - fs/ksmbd/smbstatus.h | 1822 ------ fs/ksmbd/transport_ipc.c | 884 --- fs/ksmbd/transport_ipc.h | 47 - fs/ksmbd/transport_rdma.c | 2273 ------- fs/ksmbd/transport_rdma.h | 69 - fs/ksmbd/transport_tcp.c | 649 -- fs/ksmbd/transport_tcp.h | 13 - fs/ksmbd/unicode.c | 384 -- fs/ksmbd/unicode.h | 358 -- fs/ksmbd/uniupr.h | 268 - fs/ksmbd/vfs.c | 1911 ------ fs/ksmbd/vfs.h | 167 - fs/ksmbd/vfs_cache.c | 708 --- fs/ksmbd/vfs_cache.h | 166 - fs/ksmbd/xattr.h | 122 - fs/smb/Kconfig | 11 + fs/smb/Makefile | 5 + fs/smb/client/Kconfig | 203 + fs/smb/client/Makefile | 34 + fs/smb/client/asn1.c | 63 + fs/smb/client/cached_dir.c | 606 ++ fs/smb/client/cached_dir.h | 80 + fs/smb/client/cifs_debug.c | 1067 ++++ fs/smb/client/cifs_debug.h | 160 + fs/smb/client/cifs_dfs_ref.c | 374 ++ fs/smb/client/cifs_fs_sb.h | 76 + fs/smb/client/cifs_ioctl.h | 126 + fs/smb/client/cifs_spnego.c | 236 + fs/smb/client/cifs_spnego.h | 36 + fs/smb/client/cifs_spnego_negtokeninit.asn1 | 40 + fs/smb/client/cifs_swn.c | 674 ++ fs/smb/client/cifs_swn.h | 52 + fs/smb/client/cifs_unicode.c | 632 ++ fs/smb/client/cifs_unicode.h | 404 ++ fs/smb/client/cifs_uniupr.h | 239 + fs/smb/client/cifsacl.c | 1672 +++++ fs/smb/client/cifsacl.h | 199 + fs/smb/client/cifsencrypt.c | 733 +++ fs/smb/client/cifsfs.c | 1857 ++++++ fs/smb/client/cifsfs.h | 161 + fs/smb/client/cifsglob.h | 2208 +++++++ fs/smb/client/cifspdu.h | 2730 ++++++++ fs/smb/client/cifsproto.h | 696 ++ fs/smb/client/cifsroot.c | 94 + fs/smb/client/cifssmb.c | 5873 +++++++++++++++++ fs/smb/client/connect.c | 4754 ++++++++++++++ fs/smb/client/dfs_cache.c | 1690 +++++ fs/smb/client/dfs_cache.h | 97 + fs/smb/client/dir.c | 867 +++ fs/smb/client/dns_resolve.c | 89 + fs/smb/client/dns_resolve.h | 18 + fs/smb/client/export.c | 54 + fs/smb/client/file.c | 5290 ++++++++++++++++ fs/smb/client/fs_context.c | 1773 ++++++ fs/smb/client/fs_context.h | 293 + fs/smb/client/fscache.c | 253 + fs/smb/client/fscache.h | 148 + fs/smb/client/inode.c | 3093 +++++++++ fs/smb/client/ioctl.c | 526 ++ fs/smb/client/link.c | 650 ++ fs/smb/client/misc.c | 1434 +++++ fs/smb/client/netlink.c | 90 + fs/smb/client/netlink.h | 16 + fs/smb/client/netmisc.c | 1021 +++ fs/smb/client/nterr.c | 674 ++ fs/smb/client/nterr.h | 551 ++ fs/smb/client/ntlmssp.h | 157 + fs/smb/client/readdir.c | 1237 ++++ fs/smb/client/rfc1002pdu.h | 61 + fs/smb/client/sess.c | 1858 ++++++ fs/smb/client/smb1ops.c | 1276 ++++ fs/smb/client/smb2file.c | 371 ++ fs/smb/client/smb2glob.h | 44 + fs/smb/client/smb2inode.c | 799 +++ fs/smb/client/smb2maperror.c | 2481 ++++++++ fs/smb/client/smb2misc.c | 944 +++ fs/smb/client/smb2ops.c | 5805 +++++++++++++++++ fs/smb/client/smb2pdu.c | 5722 +++++++++++++++++ fs/smb/client/smb2pdu.h | 434 ++ fs/smb/client/smb2proto.h | 284 + fs/smb/client/smb2status.h | 1769 ++++++ fs/smb/client/smb2transport.c | 934 +++ fs/smb/client/smbdirect.c | 2494 ++++++++ fs/smb/client/smbdirect.h | 320 + fs/smb/client/smbencrypt.c | 91 + fs/smb/client/smberr.h | 171 + fs/smb/client/trace.c | 8 + fs/smb/client/trace.h | 1070 ++++ fs/smb/client/transport.c | 1807 ++++++ fs/smb/client/unc.c | 69 + fs/smb/client/winucase.c | 649 ++ fs/smb/client/xattr.c | 555 ++ fs/smb/common/Makefile | 7 + fs/smb/common/arc4.h | 23 + fs/smb/common/cifs_arc4.c | 74 + fs/smb/common/cifs_md4.c | 197 + fs/smb/common/md4.h | 27 + fs/smb/common/smb2pdu.h | 1702 +++++ fs/smb/common/smbfsctl.h | 170 + fs/smb/server/Kconfig | 68 + fs/smb/server/Makefile | 20 + fs/smb/server/asn1.c | 239 + fs/smb/server/asn1.h | 21 + fs/smb/server/auth.c | 1206 ++++ fs/smb/server/auth.h | 71 + fs/smb/server/connection.c | 485 ++ fs/smb/server/connection.h | 231 + fs/smb/server/crypto_ctx.c | 266 + fs/smb/server/crypto_ctx.h | 66 + fs/smb/server/glob.h | 49 + fs/smb/server/ksmbd_netlink.h | 412 ++ fs/smb/server/ksmbd_spnego_negtokeninit.asn1 | 31 + fs/smb/server/ksmbd_spnego_negtokentarg.asn1 | 19 + fs/smb/server/ksmbd_work.c | 79 + fs/smb/server/ksmbd_work.h | 117 + fs/smb/server/mgmt/ksmbd_ida.c | 46 + fs/smb/server/mgmt/ksmbd_ida.h | 34 + fs/smb/server/mgmt/share_config.c | 234 + fs/smb/server/mgmt/share_config.h | 82 + fs/smb/server/mgmt/tree_connect.c | 147 + fs/smb/server/mgmt/tree_connect.h | 61 + fs/smb/server/mgmt/user_config.c | 79 + fs/smb/server/mgmt/user_config.h | 68 + fs/smb/server/mgmt/user_session.c | 400 ++ fs/smb/server/mgmt/user_session.h | 103 + fs/smb/server/misc.c | 381 ++ fs/smb/server/misc.h | 37 + fs/smb/server/ndr.c | 514 ++ fs/smb/server/ndr.h | 22 + fs/smb/server/nterr.h | 543 ++ fs/smb/server/ntlmssp.h | 169 + fs/smb/server/oplock.c | 1722 +++++ fs/smb/server/oplock.h | 127 + fs/smb/server/server.c | 646 ++ fs/smb/server/server.h | 71 + fs/smb/server/smb2misc.c | 454 ++ fs/smb/server/smb2ops.c | 314 + fs/smb/server/smb2pdu.c | 8727 ++++++++++++++++++++++++++ fs/smb/server/smb2pdu.h | 536 ++ fs/smb/server/smb_common.c | 797 +++ fs/smb/server/smb_common.h | 468 ++ fs/smb/server/smbacl.c | 1436 +++++ fs/smb/server/smbacl.h | 238 + fs/smb/server/smbfsctl.h | 91 + fs/smb/server/smbstatus.h | 1822 ++++++ fs/smb/server/transport_ipc.c | 884 +++ fs/smb/server/transport_ipc.h | 47 + fs/smb/server/transport_rdma.c | 2273 +++++++ fs/smb/server/transport_rdma.h | 69 + fs/smb/server/transport_tcp.c | 649 ++ fs/smb/server/transport_tcp.h | 13 + fs/smb/server/unicode.c | 384 ++ fs/smb/server/unicode.h | 358 ++ fs/smb/server/uniupr.h | 268 + fs/smb/server/vfs.c | 1911 ++++++ fs/smb/server/vfs.h | 167 + fs/smb/server/vfs_cache.c | 708 +++ fs/smb/server/vfs_cache.h | 166 + fs/smb/server/xattr.h | 122 + fs/smbfs_common/Makefile | 7 - fs/smbfs_common/arc4.h | 23 - fs/smbfs_common/cifs_arc4.c | 74 - fs/smbfs_common/cifs_md4.c | 197 - fs/smbfs_common/md4.h | 27 - fs/smbfs_common/smb2pdu.h | 1702 ----- fs/smbfs_common/smbfsctl.h | 170 - 289 files changed, 110106 insertions(+), 110099 deletions(-) delete mode 100644 fs/cifs/Kconfig delete mode 100644 fs/cifs/Makefile delete mode 100644 fs/cifs/asn1.c delete mode 100644 fs/cifs/cached_dir.c delete mode 100644 fs/cifs/cached_dir.h delete mode 100644 fs/cifs/cifs_debug.c delete mode 100644 fs/cifs/cifs_debug.h delete mode 100644 fs/cifs/cifs_dfs_ref.c delete mode 100644 fs/cifs/cifs_fs_sb.h delete mode 100644 fs/cifs/cifs_ioctl.h delete mode 100644 fs/cifs/cifs_spnego.c delete mode 100644 fs/cifs/cifs_spnego.h delete mode 100644 fs/cifs/cifs_spnego_negtokeninit.asn1 delete mode 100644 fs/cifs/cifs_swn.c delete mode 100644 fs/cifs/cifs_swn.h delete mode 100644 fs/cifs/cifs_unicode.c delete mode 100644 fs/cifs/cifs_unicode.h delete mode 100644 fs/cifs/cifs_uniupr.h delete mode 100644 fs/cifs/cifsacl.c delete mode 100644 fs/cifs/cifsacl.h delete mode 100644 fs/cifs/cifsencrypt.c delete mode 100644 fs/cifs/cifsfs.c delete mode 100644 fs/cifs/cifsfs.h delete mode 100644 fs/cifs/cifsglob.h delete mode 100644 fs/cifs/cifspdu.h delete mode 100644 fs/cifs/cifsproto.h delete mode 100644 fs/cifs/cifsroot.c delete mode 100644 fs/cifs/cifssmb.c delete mode 100644 fs/cifs/connect.c delete mode 100644 fs/cifs/dfs_cache.c delete mode 100644 fs/cifs/dfs_cache.h delete mode 100644 fs/cifs/dir.c delete mode 100644 fs/cifs/dns_resolve.c delete mode 100644 fs/cifs/dns_resolve.h delete mode 100644 fs/cifs/export.c delete mode 100644 fs/cifs/file.c delete mode 100644 fs/cifs/fs_context.c delete mode 100644 fs/cifs/fs_context.h delete mode 100644 fs/cifs/fscache.c delete mode 100644 fs/cifs/fscache.h delete mode 100644 fs/cifs/inode.c delete mode 100644 fs/cifs/ioctl.c delete mode 100644 fs/cifs/link.c delete mode 100644 fs/cifs/misc.c delete mode 100644 fs/cifs/netlink.c delete mode 100644 fs/cifs/netlink.h delete mode 100644 fs/cifs/netmisc.c delete mode 100644 fs/cifs/nterr.c delete mode 100644 fs/cifs/nterr.h delete mode 100644 fs/cifs/ntlmssp.h delete mode 100644 fs/cifs/readdir.c delete mode 100644 fs/cifs/rfc1002pdu.h delete mode 100644 fs/cifs/sess.c delete mode 100644 fs/cifs/smb1ops.c delete mode 100644 fs/cifs/smb2file.c delete mode 100644 fs/cifs/smb2glob.h delete mode 100644 fs/cifs/smb2inode.c delete mode 100644 fs/cifs/smb2maperror.c delete mode 100644 fs/cifs/smb2misc.c delete mode 100644 fs/cifs/smb2ops.c delete mode 100644 fs/cifs/smb2pdu.c delete mode 100644 fs/cifs/smb2pdu.h delete mode 100644 fs/cifs/smb2proto.h delete mode 100644 fs/cifs/smb2status.h delete mode 100644 fs/cifs/smb2transport.c delete mode 100644 fs/cifs/smbdirect.c delete mode 100644 fs/cifs/smbdirect.h delete mode 100644 fs/cifs/smbencrypt.c delete mode 100644 fs/cifs/smberr.h delete mode 100644 fs/cifs/trace.c delete mode 100644 fs/cifs/trace.h delete mode 100644 fs/cifs/transport.c delete mode 100644 fs/cifs/unc.c delete mode 100644 fs/cifs/winucase.c delete mode 100644 fs/cifs/xattr.c delete mode 100644 fs/ksmbd/Kconfig delete mode 100644 fs/ksmbd/Makefile delete mode 100644 fs/ksmbd/asn1.c delete mode 100644 fs/ksmbd/asn1.h delete mode 100644 fs/ksmbd/auth.c delete mode 100644 fs/ksmbd/auth.h delete mode 100644 fs/ksmbd/connection.c delete mode 100644 fs/ksmbd/connection.h delete mode 100644 fs/ksmbd/crypto_ctx.c delete mode 100644 fs/ksmbd/crypto_ctx.h delete mode 100644 fs/ksmbd/glob.h delete mode 100644 fs/ksmbd/ksmbd_netlink.h delete mode 100644 fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 delete mode 100644 fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 delete mode 100644 fs/ksmbd/ksmbd_work.c delete mode 100644 fs/ksmbd/ksmbd_work.h delete mode 100644 fs/ksmbd/mgmt/ksmbd_ida.c delete mode 100644 fs/ksmbd/mgmt/ksmbd_ida.h delete mode 100644 fs/ksmbd/mgmt/share_config.c delete mode 100644 fs/ksmbd/mgmt/share_config.h delete mode 100644 fs/ksmbd/mgmt/tree_connect.c delete mode 100644 fs/ksmbd/mgmt/tree_connect.h delete mode 100644 fs/ksmbd/mgmt/user_config.c delete mode 100644 fs/ksmbd/mgmt/user_config.h delete mode 100644 fs/ksmbd/mgmt/user_session.c delete mode 100644 fs/ksmbd/mgmt/user_session.h delete mode 100644 fs/ksmbd/misc.c delete mode 100644 fs/ksmbd/misc.h delete mode 100644 fs/ksmbd/ndr.c delete mode 100644 fs/ksmbd/ndr.h delete mode 100644 fs/ksmbd/nterr.h delete mode 100644 fs/ksmbd/ntlmssp.h delete mode 100644 fs/ksmbd/oplock.c delete mode 100644 fs/ksmbd/oplock.h delete mode 100644 fs/ksmbd/server.c delete mode 100644 fs/ksmbd/server.h delete mode 100644 fs/ksmbd/smb2misc.c delete mode 100644 fs/ksmbd/smb2ops.c delete mode 100644 fs/ksmbd/smb2pdu.c delete mode 100644 fs/ksmbd/smb2pdu.h delete mode 100644 fs/ksmbd/smb_common.c delete mode 100644 fs/ksmbd/smb_common.h delete mode 100644 fs/ksmbd/smbacl.c delete mode 100644 fs/ksmbd/smbacl.h delete mode 100644 fs/ksmbd/smbfsctl.h delete mode 100644 fs/ksmbd/smbstatus.h delete mode 100644 fs/ksmbd/transport_ipc.c delete mode 100644 fs/ksmbd/transport_ipc.h delete mode 100644 fs/ksmbd/transport_rdma.c delete mode 100644 fs/ksmbd/transport_rdma.h delete mode 100644 fs/ksmbd/transport_tcp.c delete mode 100644 fs/ksmbd/transport_tcp.h delete mode 100644 fs/ksmbd/unicode.c delete mode 100644 fs/ksmbd/unicode.h delete mode 100644 fs/ksmbd/uniupr.h delete mode 100644 fs/ksmbd/vfs.c delete mode 100644 fs/ksmbd/vfs.h delete mode 100644 fs/ksmbd/vfs_cache.c delete mode 100644 fs/ksmbd/vfs_cache.h delete mode 100644 fs/ksmbd/xattr.h create mode 100644 fs/smb/Kconfig create mode 100644 fs/smb/Makefile create mode 100644 fs/smb/client/Kconfig create mode 100644 fs/smb/client/Makefile create mode 100644 fs/smb/client/asn1.c create mode 100644 fs/smb/client/cached_dir.c create mode 100644 fs/smb/client/cached_dir.h create mode 100644 fs/smb/client/cifs_debug.c create mode 100644 fs/smb/client/cifs_debug.h create mode 100644 fs/smb/client/cifs_dfs_ref.c create mode 100644 fs/smb/client/cifs_fs_sb.h create mode 100644 fs/smb/client/cifs_ioctl.h create mode 100644 fs/smb/client/cifs_spnego.c create mode 100644 fs/smb/client/cifs_spnego.h create mode 100644 fs/smb/client/cifs_spnego_negtokeninit.asn1 create mode 100644 fs/smb/client/cifs_swn.c create mode 100644 fs/smb/client/cifs_swn.h create mode 100644 fs/smb/client/cifs_unicode.c create mode 100644 fs/smb/client/cifs_unicode.h create mode 100644 fs/smb/client/cifs_uniupr.h create mode 100644 fs/smb/client/cifsacl.c create mode 100644 fs/smb/client/cifsacl.h create mode 100644 fs/smb/client/cifsencrypt.c create mode 100644 fs/smb/client/cifsfs.c create mode 100644 fs/smb/client/cifsfs.h create mode 100644 fs/smb/client/cifsglob.h create mode 100644 fs/smb/client/cifspdu.h create mode 100644 fs/smb/client/cifsproto.h create mode 100644 fs/smb/client/cifsroot.c create mode 100644 fs/smb/client/cifssmb.c create mode 100644 fs/smb/client/connect.c create mode 100644 fs/smb/client/dfs_cache.c create mode 100644 fs/smb/client/dfs_cache.h create mode 100644 fs/smb/client/dir.c create mode 100644 fs/smb/client/dns_resolve.c create mode 100644 fs/smb/client/dns_resolve.h create mode 100644 fs/smb/client/export.c create mode 100644 fs/smb/client/file.c create mode 100644 fs/smb/client/fs_context.c create mode 100644 fs/smb/client/fs_context.h create mode 100644 fs/smb/client/fscache.c create mode 100644 fs/smb/client/fscache.h create mode 100644 fs/smb/client/inode.c create mode 100644 fs/smb/client/ioctl.c create mode 100644 fs/smb/client/link.c create mode 100644 fs/smb/client/misc.c create mode 100644 fs/smb/client/netlink.c create mode 100644 fs/smb/client/netlink.h create mode 100644 fs/smb/client/netmisc.c create mode 100644 fs/smb/client/nterr.c create mode 100644 fs/smb/client/nterr.h create mode 100644 fs/smb/client/ntlmssp.h create mode 100644 fs/smb/client/readdir.c create mode 100644 fs/smb/client/rfc1002pdu.h create mode 100644 fs/smb/client/sess.c create mode 100644 fs/smb/client/smb1ops.c create mode 100644 fs/smb/client/smb2file.c create mode 100644 fs/smb/client/smb2glob.h create mode 100644 fs/smb/client/smb2inode.c create mode 100644 fs/smb/client/smb2maperror.c create mode 100644 fs/smb/client/smb2misc.c create mode 100644 fs/smb/client/smb2ops.c create mode 100644 fs/smb/client/smb2pdu.c create mode 100644 fs/smb/client/smb2pdu.h create mode 100644 fs/smb/client/smb2proto.h create mode 100644 fs/smb/client/smb2status.h create mode 100644 fs/smb/client/smb2transport.c create mode 100644 fs/smb/client/smbdirect.c create mode 100644 fs/smb/client/smbdirect.h create mode 100644 fs/smb/client/smbencrypt.c create mode 100644 fs/smb/client/smberr.h create mode 100644 fs/smb/client/trace.c create mode 100644 fs/smb/client/trace.h create mode 100644 fs/smb/client/transport.c create mode 100644 fs/smb/client/unc.c create mode 100644 fs/smb/client/winucase.c create mode 100644 fs/smb/client/xattr.c create mode 100644 fs/smb/common/Makefile create mode 100644 fs/smb/common/arc4.h create mode 100644 fs/smb/common/cifs_arc4.c create mode 100644 fs/smb/common/cifs_md4.c create mode 100644 fs/smb/common/md4.h create mode 100644 fs/smb/common/smb2pdu.h create mode 100644 fs/smb/common/smbfsctl.h create mode 100644 fs/smb/server/Kconfig create mode 100644 fs/smb/server/Makefile create mode 100644 fs/smb/server/asn1.c create mode 100644 fs/smb/server/asn1.h create mode 100644 fs/smb/server/auth.c create mode 100644 fs/smb/server/auth.h create mode 100644 fs/smb/server/connection.c create mode 100644 fs/smb/server/connection.h create mode 100644 fs/smb/server/crypto_ctx.c create mode 100644 fs/smb/server/crypto_ctx.h create mode 100644 fs/smb/server/glob.h create mode 100644 fs/smb/server/ksmbd_netlink.h create mode 100644 fs/smb/server/ksmbd_spnego_negtokeninit.asn1 create mode 100644 fs/smb/server/ksmbd_spnego_negtokentarg.asn1 create mode 100644 fs/smb/server/ksmbd_work.c create mode 100644 fs/smb/server/ksmbd_work.h create mode 100644 fs/smb/server/mgmt/ksmbd_ida.c create mode 100644 fs/smb/server/mgmt/ksmbd_ida.h create mode 100644 fs/smb/server/mgmt/share_config.c create mode 100644 fs/smb/server/mgmt/share_config.h create mode 100644 fs/smb/server/mgmt/tree_connect.c create mode 100644 fs/smb/server/mgmt/tree_connect.h create mode 100644 fs/smb/server/mgmt/user_config.c create mode 100644 fs/smb/server/mgmt/user_config.h create mode 100644 fs/smb/server/mgmt/user_session.c create mode 100644 fs/smb/server/mgmt/user_session.h create mode 100644 fs/smb/server/misc.c create mode 100644 fs/smb/server/misc.h create mode 100644 fs/smb/server/ndr.c create mode 100644 fs/smb/server/ndr.h create mode 100644 fs/smb/server/nterr.h create mode 100644 fs/smb/server/ntlmssp.h create mode 100644 fs/smb/server/oplock.c create mode 100644 fs/smb/server/oplock.h create mode 100644 fs/smb/server/server.c create mode 100644 fs/smb/server/server.h create mode 100644 fs/smb/server/smb2misc.c create mode 100644 fs/smb/server/smb2ops.c create mode 100644 fs/smb/server/smb2pdu.c create mode 100644 fs/smb/server/smb2pdu.h create mode 100644 fs/smb/server/smb_common.c create mode 100644 fs/smb/server/smb_common.h create mode 100644 fs/smb/server/smbacl.c create mode 100644 fs/smb/server/smbacl.h create mode 100644 fs/smb/server/smbfsctl.h create mode 100644 fs/smb/server/smbstatus.h create mode 100644 fs/smb/server/transport_ipc.c create mode 100644 fs/smb/server/transport_ipc.h create mode 100644 fs/smb/server/transport_rdma.c create mode 100644 fs/smb/server/transport_rdma.h create mode 100644 fs/smb/server/transport_tcp.c create mode 100644 fs/smb/server/transport_tcp.h create mode 100644 fs/smb/server/unicode.c create mode 100644 fs/smb/server/unicode.h create mode 100644 fs/smb/server/uniupr.h create mode 100644 fs/smb/server/vfs.c create mode 100644 fs/smb/server/vfs.h create mode 100644 fs/smb/server/vfs_cache.c create mode 100644 fs/smb/server/vfs_cache.h create mode 100644 fs/smb/server/xattr.h delete mode 100644 fs/smbfs_common/Makefile delete mode 100644 fs/smbfs_common/arc4.h delete mode 100644 fs/smbfs_common/cifs_arc4.c delete mode 100644 fs/smbfs_common/cifs_md4.c delete mode 100644 fs/smbfs_common/md4.h delete mode 100644 fs/smbfs_common/smb2pdu.h delete mode 100644 fs/smbfs_common/smbfsctl.h diff --git a/MAINTAINERS b/MAINTAINERS index 301b9ba6af79..379387e20a96 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5183,8 +5183,8 @@ S: Supported W: https://wiki.samba.org/index.php/LinuxCIFS T: git git://git.samba.org/sfrench/cifs-2.6.git F: Documentation/admin-guide/cifs/ -F: fs/cifs/ -F: fs/smbfs_common/ +F: fs/smb/client/ +F: fs/smb/common/ F: include/uapi/linux/cifs COMPACTPCI HOTPLUG CORE @@ -11168,8 +11168,8 @@ L: linux-cifs@vger.kernel.org S: Maintained T: git git://git.samba.org/ksmbd.git F: Documentation/filesystems/cifs/ksmbd.rst -F: fs/ksmbd/ -F: fs/smbfs_common/ +F: fs/smb/common/ +F: fs/smb/server/ KERNEL UNIT TESTING FRAMEWORK (KUnit) M: Brendan Higgins diff --git a/fs/Kconfig b/fs/Kconfig index 2685a4d0d353..703a1cea0fc0 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -371,14 +371,7 @@ config NFS_V4_2_SSC_HELPER source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" -source "fs/cifs/Kconfig" -source "fs/ksmbd/Kconfig" - -config SMBFS_COMMON - tristate - default y if CIFS=y || SMB_SERVER=y - default m if CIFS=m || SMB_SERVER=m - +source "fs/smb/Kconfig" source "fs/coda/Kconfig" source "fs/afs/Kconfig" source "fs/9p/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 80ab0154419e..9b7bcf6e54cd 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -94,9 +94,7 @@ obj-$(CONFIG_LOCKD) += lockd/ obj-$(CONFIG_NLS) += nls/ obj-y += unicode/ obj-$(CONFIG_SYSV_FS) += sysv/ -obj-$(CONFIG_SMBFS_COMMON) += smbfs_common/ -obj-$(CONFIG_CIFS) += cifs/ -obj-$(CONFIG_SMB_SERVER) += ksmbd/ +obj-$(CONFIG_SMBFS) += smb/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_NTFS3_FS) += ntfs3/ diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig deleted file mode 100644 index 3b7e3b9e4fd2..000000000000 --- a/fs/cifs/Kconfig +++ /dev/null @@ -1,203 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config CIFS - tristate "SMB3 and CIFS support (advanced network filesystem)" - depends on INET - select NLS - select CRYPTO - select CRYPTO_MD5 - select CRYPTO_SHA256 - select CRYPTO_SHA512 - select CRYPTO_CMAC - select CRYPTO_HMAC - select CRYPTO_AEAD2 - select CRYPTO_CCM - select CRYPTO_GCM - select CRYPTO_ECB - select CRYPTO_AES - select KEYS - select DNS_RESOLVER - select ASN1 - select OID_REGISTRY - help - This is the client VFS module for the SMB3 family of NAS protocols, - (including support for the most recent, most secure dialect SMB3.1.1) - as well as for earlier dialects such as SMB2.1, SMB2 and the older - Common Internet File System (CIFS) protocol. CIFS was the successor - to the original dialect, the Server Message Block (SMB) protocol, the - native file sharing mechanism for most early PC operating systems. - - The SMB3 protocol is supported by most modern operating systems - and NAS appliances (e.g. Samba, Windows 10, Windows Server 2016, - MacOS) and even in the cloud (e.g. Microsoft Azure). - The older CIFS protocol was included in Windows NT4, 2000 and XP (and - later) as well by Samba (which provides excellent CIFS and SMB3 - server support for Linux and many other operating systems). Use of - dialects older than SMB2.1 is often discouraged on public networks. - This module also provides limited support for OS/2 and Windows ME - and similar very old servers. - - This module provides an advanced network file system client - for mounting to SMB3 (and CIFS) compliant servers. It includes - support for DFS (hierarchical name space), secure per-user - session establishment via Kerberos or NTLM or NTLMv2, RDMA - (smbdirect), advanced security features, per-share encryption, - directory leases, safe distributed caching (oplock), optional packet - signing, Unicode and other internationalization improvements. - - In general, the default dialects, SMB3 and later, enable better - performance, security and features, than would be possible with CIFS. - Note that when mounting to Samba, due to the CIFS POSIX extensions, - CIFS mounts can provide slightly better POSIX compatibility - than SMB3 mounts. SMB2/SMB3 mount options are also - slightly simpler (compared to CIFS) due to protocol improvements. - - If you need to mount to Samba, Azure, Macs or Windows from this machine, say Y. - -config CIFS_STATS2 - bool "Extended statistics" - depends on CIFS - default y - help - Enabling this option will allow more detailed statistics on SMB - request timing to be displayed in /proc/fs/cifs/DebugData and also - allow optional logging of slow responses to dmesg (depending on the - value of /proc/fs/cifs/cifsFYI). See Documentation/admin-guide/cifs/usage.rst - for more details. These additional statistics may have a minor effect - on performance and memory utilization. - - If unsure, say Y. - -config CIFS_ALLOW_INSECURE_LEGACY - bool "Support legacy servers which use less secure dialects" - depends on CIFS - default y - help - Modern dialects, SMB2.1 and later (including SMB3 and 3.1.1), have - additional security features, including protection against - man-in-the-middle attacks and stronger crypto hashes, so the use - of legacy dialects (SMB1/CIFS and SMB2.0) is discouraged. - - Disabling this option prevents users from using vers=1.0 or vers=2.0 - on mounts with cifs.ko - - If unsure, say Y. - -config CIFS_UPCALL - bool "Kerberos/SPNEGO advanced session setup" - depends on CIFS - help - Enables an upcall mechanism for CIFS which accesses userspace helper - utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets - which are needed to mount to certain secure servers (for which more - secure Kerberos authentication is required). If unsure, say Y. - -config CIFS_XATTR - bool "CIFS extended attributes" - depends on CIFS - help - Extended attributes are name:value pairs associated with inodes by - the kernel or by users (see the attr(5) manual page for details). - CIFS maps the name of extended attributes beginning with the user - namespace prefix to SMB/CIFS EAs. EAs are stored on Windows - servers without the user namespace prefix, but their names are - seen by Linux cifs clients prefaced by the user namespace prefix. - The system namespace (used by some filesystems to store ACLs) is - not supported at this time. - - If unsure, say Y. - -config CIFS_POSIX - bool "CIFS POSIX Extensions" - depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR - help - Enabling this option will cause the cifs client to attempt to - negotiate a newer dialect with servers, such as Samba 3.0.5 - or later, that optionally can handle more POSIX like (rather - than Windows like) file behavior. It also enables - support for POSIX ACLs (getfacl and setfacl) to servers - (such as Samba 3.10 and later) which can negotiate - CIFS POSIX ACL support. If unsure, say N. - -config CIFS_DEBUG - bool "Enable CIFS debugging routines" - default y - depends on CIFS - help - Enabling this option adds helpful debugging messages to - the cifs code which increases the size of the cifs module. - If unsure, say Y. - -config CIFS_DEBUG2 - bool "Enable additional CIFS debugging routines" - depends on CIFS_DEBUG - help - Enabling this option adds a few more debugging routines - to the cifs code which slightly increases the size of - the cifs module and can cause additional logging of debug - messages in some error paths, slowing performance. This - option can be turned off unless you are debugging - cifs problems. If unsure, say N. - -config CIFS_DEBUG_DUMP_KEYS - bool "Dump encryption keys for offline decryption (Unsafe)" - depends on CIFS_DEBUG - help - Enabling this will dump the encryption and decryption keys - used to communicate on an encrypted share connection on the - console. This allows Wireshark to decrypt and dissect - encrypted network captures. Enable this carefully. - If unsure, say N. - -config CIFS_DFS_UPCALL - bool "DFS feature support" - depends on CIFS - help - Distributed File System (DFS) support is used to access shares - transparently in an enterprise name space, even if the share - moves to a different server. This feature also enables - an upcall mechanism for CIFS which contacts userspace helper - utilities to provide server name resolution (host names to - IP addresses) which is needed in order to reconnect to - servers if their addresses change or for implicit mounts of - DFS junction points. If unsure, say Y. - -config CIFS_SWN_UPCALL - bool "SWN feature support" - depends on CIFS - help - The Service Witness Protocol (SWN) is used to get notifications - from a highly available server of resource state changes. This - feature enables an upcall mechanism for CIFS which contacts a - userspace daemon to establish the DCE/RPC connection to retrieve - the cluster available interfaces and resource change notifications. - If unsure, say Y. - -config CIFS_NFSD_EXPORT - bool "Allow nfsd to export CIFS file system" - depends on CIFS && BROKEN - help - Allows NFS server to export a CIFS mounted share (nfsd over cifs) - -config CIFS_SMB_DIRECT - bool "SMB Direct support" - depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y - help - Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. - SMB Direct allows transferring SMB packets over RDMA. If unsure, - say Y. - -config CIFS_FSCACHE - bool "Provide CIFS client caching support" - depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y - help - Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data - to be cached locally on disk through the general filesystem cache - manager. If unsure, say N. - -config CIFS_ROOT - bool "SMB root file system (Experimental)" - depends on CIFS=y && IP_PNP - help - Enables root file system support over SMB protocol. - - Most people say N here. diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile deleted file mode 100644 index 7c9785973f49..000000000000 --- a/fs/cifs/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for Linux CIFS/SMB2/SMB3 VFS client -# -ccflags-y += -I$(src) # needed for trace events -obj-$(CONFIG_CIFS) += cifs.o - -cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \ - inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \ - cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \ - readdir.o ioctl.o sess.o export.o unc.o winucase.o \ - smb2ops.o smb2maperror.o smb2transport.o \ - smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \ - dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o - -$(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h - -$(obj)/cifs_spnego_negtokeninit.asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.c $(obj)/cifs_spnego_negtokeninit.asn1.h - -cifs-$(CONFIG_CIFS_XATTR) += xattr.o - -cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o - -cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o - -cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o - -cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o - -cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o - -cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o - -cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c deleted file mode 100644 index b5724ef9f182..000000000000 --- a/fs/cifs/asn1.c +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifsproto.h" -#include "cifs_spnego_negtokeninit.asn1.h" - -int -decode_negTokenInit(unsigned char *security_blob, int length, - struct TCP_Server_Info *server) -{ - if (asn1_ber_decoder(&cifs_spnego_negtokeninit_decoder, server, - security_blob, length) == 0) - return 1; - else - return 0; -} - -int cifs_gssapi_this_mech(void *context, size_t hdrlen, - unsigned char tag, const void *value, size_t vlen) -{ - enum OID oid; - - oid = look_up_OID(value, vlen); - if (oid != OID_spnego) { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - cifs_dbg(FYI, "Error decoding negTokenInit header: unexpected OID %s\n", - buf); - return -EBADMSG; - } - return 0; -} - -int cifs_neg_token_init_mech_type(void *context, size_t hdrlen, - unsigned char tag, - const void *value, size_t vlen) -{ - struct TCP_Server_Info *server = context; - enum OID oid; - - oid = look_up_OID(value, vlen); - if (oid == OID_mskrb5) - server->sec_mskerberos = true; - else if (oid == OID_krb5u2u) - server->sec_kerberosu2u = true; - else if (oid == OID_krb5) - server->sec_kerberos = true; - else if (oid == OID_ntlmssp) - server->sec_ntlmssp = true; - else { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - cifs_dbg(FYI, "Decoding negTokenInit: unsupported OID %s\n", - buf); - } - return 0; -} diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c deleted file mode 100644 index bfc964b36c72..000000000000 --- a/fs/cifs/cached_dir.c +++ /dev/null @@ -1,606 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Functions to handle the cached directory entries - * - * Copyright (c) 2022, Ronnie Sahlberg - */ - -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "smb2proto.h" -#include "cached_dir.h" - -static struct cached_fid *init_cached_dir(const char *path); -static void free_cached_dir(struct cached_fid *cfid); -static void smb2_close_cached_fid(struct kref *ref); - -static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, - const char *path, - bool lookup_only) -{ - struct cached_fid *cfid; - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry(cfid, &cfids->entries, entry) { - if (!strcmp(cfid->path, path)) { - /* - * If it doesn't have a lease it is either not yet - * fully cached or it may be in the process of - * being deleted due to a lease break. - */ - if (!cfid->has_lease) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - kref_get(&cfid->refcount); - spin_unlock(&cfids->cfid_list_lock); - return cfid; - } - } - if (lookup_only) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - if (cfids->num_entries >= MAX_CACHED_FIDS) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - cfid = init_cached_dir(path); - if (cfid == NULL) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - cfid->cfids = cfids; - cfids->num_entries++; - list_add(&cfid->entry, &cfids->entries); - cfid->on_list = true; - kref_get(&cfid->refcount); - spin_unlock(&cfids->cfid_list_lock); - return cfid; -} - -static struct dentry * -path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path) -{ - struct dentry *dentry; - const char *s, *p; - char sep; - - sep = CIFS_DIR_SEP(cifs_sb); - dentry = dget(cifs_sb->root); - s = path; - - do { - struct inode *dir = d_inode(dentry); - struct dentry *child; - - if (!S_ISDIR(dir->i_mode)) { - dput(dentry); - dentry = ERR_PTR(-ENOTDIR); - break; - } - - /* skip separators */ - while (*s == sep) - s++; - if (!*s) - break; - p = s++; - /* next separator */ - while (*s && *s != sep) - s++; - - child = lookup_positive_unlocked(p, dentry, s - p); - dput(dentry); - dentry = child; - } while (!IS_ERR(dentry)); - return dentry; -} - -static const char *path_no_prefix(struct cifs_sb_info *cifs_sb, - const char *path) -{ - size_t len = 0; - - if (!*path) - return path; - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && - cifs_sb->prepath) { - len = strlen(cifs_sb->prepath) + 1; - if (unlikely(len > strlen(path))) - return ERR_PTR(-EINVAL); - } - return path + len; -} - -/* - * Open the and cache a directory handle. - * If error then *cfid is not initialized. - */ -int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, - const char *path, - struct cifs_sb_info *cifs_sb, - bool lookup_only, struct cached_fid **ret_cfid) -{ - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct cifs_open_parms oparms; - struct smb2_create_rsp *o_rsp = NULL; - struct smb2_query_info_rsp *qi_rsp = NULL; - int resp_buftype[2]; - struct smb_rqst rqst[2]; - struct kvec rsp_iov[2]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - int rc, flags = 0; - __le16 *utf16_path = NULL; - u8 oplock = SMB2_OPLOCK_LEVEL_II; - struct cifs_fid *pfid; - struct dentry *dentry = NULL; - struct cached_fid *cfid; - struct cached_fids *cfids; - const char *npath; - - if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || - is_smb1_server(tcon->ses->server)) - return -EOPNOTSUPP; - - ses = tcon->ses; - server = ses->server; - cfids = tcon->cfids; - - if (!server->ops->new_lease_key) - return -EIO; - - if (cifs_sb->root == NULL) - return -ENOENT; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - cfid = find_or_create_cached_dir(cfids, path, lookup_only); - if (cfid == NULL) { - kfree(utf16_path); - return -ENOENT; - } - /* - * At this point we either have a lease already and we can just - * return it. If not we are guaranteed to be the only thread accessing - * this cfid. - */ - if (cfid->has_lease) { - *ret_cfid = cfid; - kfree(utf16_path); - return 0; - } - - /* - * Skip any prefix paths in @path as lookup_positive_unlocked() ends up - * calling ->lookup() which already adds those through - * build_path_from_dentry(). Also, do it earlier as we might reconnect - * below when trying to send compounded request and then potentially - * having a different prefix path (e.g. after DFS failover). - */ - npath = path_no_prefix(cifs_sb, path); - if (IS_ERR(npath)) { - rc = PTR_ERR(npath); - kfree(utf16_path); - return rc; - } - - /* - * We do not hold the lock for the open because in case - * SMB2_open needs to reconnect. - * This is safe because no other thread will be able to get a ref - * to the cfid until we have finished opening the file and (possibly) - * acquired a lease. - */ - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - pfid = &cfid->fid; - server->ops->new_lease_key(pfid); - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE), - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .fid = pfid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto oshr_free; - smb2_set_next_command(tcon, &rqst[0]); - - memset(&qi_iov, 0, sizeof(qi_iov)); - rqst[1].rq_iov = qi_iov; - rqst[1].rq_nvec = 1; - - rc = SMB2_query_info_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, FILE_ALL_INFORMATION, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + - PATH_MAX * 2, 0, NULL); - if (rc) - goto oshr_free; - - smb2_set_related(&rqst[1]); - - rc = compound_send_recv(xid, ses, server, - flags, 2, rqst, - resp_buftype, rsp_iov); - if (rc) { - if (rc == -EREMCHG) { - tcon->need_reconnect = true; - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - } - goto oshr_free; - } - cfid->tcon = tcon; - cfid->is_open = true; - - o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; - oparms.fid->persistent_fid = o_rsp->PersistentFileId; - oparms.fid->volatile_fid = o_rsp->VolatileFileId; -#ifdef CONFIG_CIFS_DEBUG2 - oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); -#endif /* CIFS_DEBUG2 */ - - if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) - goto oshr_free; - - smb2_parse_contexts(server, o_rsp, - &oparms.fid->epoch, - oparms.fid->lease_key, &oplock, - NULL, NULL); - if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) - goto oshr_free; - qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; - if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) - goto oshr_free; - if (!smb2_validate_and_copy_iov( - le16_to_cpu(qi_rsp->OutputBufferOffset), - sizeof(struct smb2_file_all_info), - &rsp_iov[1], sizeof(struct smb2_file_all_info), - (char *)&cfid->file_all_info)) - cfid->file_all_info_is_valid = true; - - if (!npath[0]) - dentry = dget(cifs_sb->root); - else { - dentry = path_to_dentry(cifs_sb, npath); - if (IS_ERR(dentry)) { - rc = -ENOENT; - goto oshr_free; - } - } - cfid->dentry = dentry; - cfid->time = jiffies; - cfid->has_lease = true; - -oshr_free: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_query_info_free(&rqst[1]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - spin_lock(&cfids->cfid_list_lock); - if (rc && !cfid->has_lease) { - if (cfid->on_list) { - list_del(&cfid->entry); - cfid->on_list = false; - cfids->num_entries--; - } - rc = -ENOENT; - } - spin_unlock(&cfids->cfid_list_lock); - if (!rc && !cfid->has_lease) { - /* - * We are guaranteed to have two references at this point. - * One for the caller and one for a potential lease. - * Release the Lease-ref so that the directory will be closed - * when the caller closes the cached handle. - */ - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - if (rc) { - if (cfid->is_open) - SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid); - free_cached_dir(cfid); - cfid = NULL; - } - - if (rc == 0) { - *ret_cfid = cfid; - atomic_inc(&tcon->num_remote_opens); - } - - return rc; -} - -int open_cached_dir_by_dentry(struct cifs_tcon *tcon, - struct dentry *dentry, - struct cached_fid **ret_cfid) -{ - struct cached_fid *cfid; - struct cached_fids *cfids = tcon->cfids; - - if (cfids == NULL) - return -ENOENT; - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry(cfid, &cfids->entries, entry) { - if (dentry && cfid->dentry == dentry) { - cifs_dbg(FYI, "found a cached root file handle by dentry\n"); - kref_get(&cfid->refcount); - *ret_cfid = cfid; - spin_unlock(&cfids->cfid_list_lock); - return 0; - } - } - spin_unlock(&cfids->cfid_list_lock); - return -ENOENT; -} - -static void -smb2_close_cached_fid(struct kref *ref) -{ - struct cached_fid *cfid = container_of(ref, struct cached_fid, - refcount); - - spin_lock(&cfid->cfids->cfid_list_lock); - if (cfid->on_list) { - list_del(&cfid->entry); - cfid->on_list = false; - cfid->cfids->num_entries--; - } - spin_unlock(&cfid->cfids->cfid_list_lock); - - dput(cfid->dentry); - cfid->dentry = NULL; - - if (cfid->is_open) { - SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid); - atomic_dec(&cfid->tcon->num_remote_opens); - } - - free_cached_dir(cfid); -} - -void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb) -{ - struct cached_fid *cfid = NULL; - int rc; - - rc = open_cached_dir(xid, tcon, name, cifs_sb, true, &cfid); - if (rc) { - cifs_dbg(FYI, "no cached dir found for rmdir(%s)\n", name); - return; - } - spin_lock(&cfid->cfids->cfid_list_lock); - if (cfid->has_lease) { - cfid->has_lease = false; - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - spin_unlock(&cfid->cfids->cfid_list_lock); - close_cached_dir(cfid); -} - - -void close_cached_dir(struct cached_fid *cfid) -{ - kref_put(&cfid->refcount, smb2_close_cached_fid); -} - -/* - * Called from cifs_kill_sb when we unmount a share - */ -void close_all_cached_dirs(struct cifs_sb_info *cifs_sb) -{ - struct rb_root *root = &cifs_sb->tlink_tree; - struct rb_node *node; - struct cached_fid *cfid; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cached_fids *cfids; - - for (node = rb_first(root); node; node = rb_next(node)) { - tlink = rb_entry(node, struct tcon_link, tl_rbnode); - tcon = tlink_tcon(tlink); - if (IS_ERR(tcon)) - continue; - cfids = tcon->cfids; - if (cfids == NULL) - continue; - list_for_each_entry(cfid, &cfids->entries, entry) { - dput(cfid->dentry); - cfid->dentry = NULL; - } - } -} - -/* - * Invalidate all cached dirs when a TCON has been reset - * due to a session loss. - */ -void invalidate_all_cached_dirs(struct cifs_tcon *tcon) -{ - struct cached_fids *cfids = tcon->cfids; - struct cached_fid *cfid, *q; - LIST_HEAD(entry); - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { - list_move(&cfid->entry, &entry); - cfids->num_entries--; - cfid->is_open = false; - cfid->on_list = false; - /* To prevent race with smb2_cached_lease_break() */ - kref_get(&cfid->refcount); - } - spin_unlock(&cfids->cfid_list_lock); - - list_for_each_entry_safe(cfid, q, &entry, entry) { - list_del(&cfid->entry); - cancel_work_sync(&cfid->lease_break); - if (cfid->has_lease) { - /* - * We lease was never cancelled from the server so we - * need to drop the reference. - */ - spin_lock(&cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - /* Drop the extra reference opened above*/ - kref_put(&cfid->refcount, smb2_close_cached_fid); - } -} - -static void -smb2_cached_lease_break(struct work_struct *work) -{ - struct cached_fid *cfid = container_of(work, - struct cached_fid, lease_break); - - spin_lock(&cfid->cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfid->cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); -} - -int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) -{ - struct cached_fids *cfids = tcon->cfids; - struct cached_fid *cfid; - - if (cfids == NULL) - return false; - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry(cfid, &cfids->entries, entry) { - if (cfid->has_lease && - !memcmp(lease_key, - cfid->fid.lease_key, - SMB2_LEASE_KEY_SIZE)) { - cfid->time = 0; - /* - * We found a lease remove it from the list - * so no threads can access it. - */ - list_del(&cfid->entry); - cfid->on_list = false; - cfids->num_entries--; - - queue_work(cifsiod_wq, - &cfid->lease_break); - spin_unlock(&cfids->cfid_list_lock); - return true; - } - } - spin_unlock(&cfids->cfid_list_lock); - return false; -} - -static struct cached_fid *init_cached_dir(const char *path) -{ - struct cached_fid *cfid; - - cfid = kzalloc(sizeof(*cfid), GFP_ATOMIC); - if (!cfid) - return NULL; - cfid->path = kstrdup(path, GFP_ATOMIC); - if (!cfid->path) { - kfree(cfid); - return NULL; - } - - INIT_WORK(&cfid->lease_break, smb2_cached_lease_break); - INIT_LIST_HEAD(&cfid->entry); - INIT_LIST_HEAD(&cfid->dirents.entries); - mutex_init(&cfid->dirents.de_mutex); - spin_lock_init(&cfid->fid_lock); - kref_init(&cfid->refcount); - return cfid; -} - -static void free_cached_dir(struct cached_fid *cfid) -{ - struct cached_dirent *dirent, *q; - - dput(cfid->dentry); - cfid->dentry = NULL; - - /* - * Delete all cached dirent names - */ - list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) { - list_del(&dirent->entry); - kfree(dirent->name); - kfree(dirent); - } - - kfree(cfid->path); - cfid->path = NULL; - kfree(cfid); -} - -struct cached_fids *init_cached_dirs(void) -{ - struct cached_fids *cfids; - - cfids = kzalloc(sizeof(*cfids), GFP_KERNEL); - if (!cfids) - return NULL; - spin_lock_init(&cfids->cfid_list_lock); - INIT_LIST_HEAD(&cfids->entries); - return cfids; -} - -/* - * Called from tconInfoFree when we are tearing down the tcon. - * There are no active users or open files/directories at this point. - */ -void free_cached_dirs(struct cached_fids *cfids) -{ - struct cached_fid *cfid, *q; - LIST_HEAD(entry); - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { - cfid->on_list = false; - cfid->is_open = false; - list_move(&cfid->entry, &entry); - } - spin_unlock(&cfids->cfid_list_lock); - - list_for_each_entry_safe(cfid, q, &entry, entry) { - list_del(&cfid->entry); - free_cached_dir(cfid); - } - - kfree(cfids); -} diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h deleted file mode 100644 index 2f4e764c9ca9..000000000000 --- a/fs/cifs/cached_dir.h +++ /dev/null @@ -1,80 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Functions to handle the cached directory entries - * - * Copyright (c) 2022, Ronnie Sahlberg - */ - -#ifndef _CACHED_DIR_H -#define _CACHED_DIR_H - - -struct cached_dirent { - struct list_head entry; - char *name; - int namelen; - loff_t pos; - - struct cifs_fattr fattr; -}; - -struct cached_dirents { - bool is_valid:1; - bool is_failed:1; - struct dir_context *ctx; /* - * Only used to make sure we only take entries - * from a single context. Never dereferenced. - */ - struct mutex de_mutex; - int pos; /* Expected ctx->pos */ - struct list_head entries; -}; - -struct cached_fid { - struct list_head entry; - struct cached_fids *cfids; - const char *path; - bool has_lease:1; - bool is_open:1; - bool on_list:1; - bool file_all_info_is_valid:1; - unsigned long time; /* jiffies of when lease was taken */ - struct kref refcount; - struct cifs_fid fid; - spinlock_t fid_lock; - struct cifs_tcon *tcon; - struct dentry *dentry; - struct work_struct lease_break; - struct smb2_file_all_info file_all_info; - struct cached_dirents dirents; -}; - -#define MAX_CACHED_FIDS 16 -struct cached_fids { - /* Must be held when: - * - accessing the cfids->entries list - */ - spinlock_t cfid_list_lock; - int num_entries; - struct list_head entries; -}; - -extern struct cached_fids *init_cached_dirs(void); -extern void free_cached_dirs(struct cached_fids *cfids); -extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, - const char *path, - struct cifs_sb_info *cifs_sb, - bool lookup_only, struct cached_fid **cfid); -extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon, - struct dentry *dentry, - struct cached_fid **cfid); -extern void close_cached_dir(struct cached_fid *cfid); -extern void drop_cached_dir_by_name(const unsigned int xid, - struct cifs_tcon *tcon, - const char *name, - struct cifs_sb_info *cifs_sb); -extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); -extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon); -extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); - -#endif /* _CACHED_DIR_H */ diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c deleted file mode 100644 index e41154ad96af..000000000000 --- a/fs/cifs/cifs_debug.c +++ /dev/null @@ -1,1067 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) International Business Machines Corp., 2000,2005 - * - * Modified by Steve French (sfrench@us.ibm.com) - */ -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifsfs.h" -#include "fs_context.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#ifdef CONFIG_CIFS_SMB_DIRECT -#include "smbdirect.h" -#endif -#include "cifs_swn.h" - -void -cifs_dump_mem(char *label, void *data, int length) -{ - pr_debug("%s: dump of %d bytes of data at 0x%p\n", label, length, data); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4, - data, length, true); -} - -void cifs_dump_detail(void *buf, struct TCP_Server_Info *server) -{ -#ifdef CONFIG_CIFS_DEBUG2 - struct smb_hdr *smb = buf; - - cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n", - smb->Command, smb->Status.CifsError, - smb->Flags, smb->Flags2, smb->Mid, smb->Pid); - cifs_dbg(VFS, "smb buf %p len %u\n", smb, - server->ops->calc_smb_size(smb)); -#endif /* CONFIG_CIFS_DEBUG2 */ -} - -void cifs_dump_mids(struct TCP_Server_Info *server) -{ -#ifdef CONFIG_CIFS_DEBUG2 - struct mid_q_entry *mid_entry; - - if (server == NULL) - return; - - cifs_dbg(VFS, "Dump pending requests:\n"); - spin_lock(&server->mid_lock); - list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { - cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n", - mid_entry->mid_state, - le16_to_cpu(mid_entry->command), - mid_entry->pid, - mid_entry->callback_data, - mid_entry->mid); -#ifdef CONFIG_CIFS_STATS2 - cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n", - mid_entry->large_buf, - mid_entry->resp_buf, - mid_entry->when_received, - jiffies); -#endif /* STATS2 */ - cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n", - mid_entry->multiRsp, mid_entry->multiEnd); - if (mid_entry->resp_buf) { - cifs_dump_detail(mid_entry->resp_buf, server); - cifs_dump_mem("existing buf: ", - mid_entry->resp_buf, 62); - } - } - spin_unlock(&server->mid_lock); -#endif /* CONFIG_CIFS_DEBUG2 */ -} - -#ifdef CONFIG_PROC_FS -static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon) -{ - __u32 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); - - seq_printf(m, "%s Mounts: %d ", tcon->tree_name, tcon->tc_count); - if (tcon->nativeFileSystem) - seq_printf(m, "Type: %s ", tcon->nativeFileSystem); - seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x\n\tPathComponentMax: %d Status: %d", - le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), - le32_to_cpu(tcon->fsAttrInfo.Attributes), - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), - tcon->status); - if (dev_type == FILE_DEVICE_DISK) - seq_puts(m, " type: DISK "); - else if (dev_type == FILE_DEVICE_CD_ROM) - seq_puts(m, " type: CDROM "); - else - seq_printf(m, " type: %d ", dev_type); - - seq_printf(m, "Serial Number: 0x%x", tcon->vol_serial_number); - - if ((tcon->seal) || - (tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || - (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) - seq_printf(m, " Encrypted"); - if (tcon->nocase) - seq_printf(m, " nocase"); - if (tcon->unix_ext) - seq_printf(m, " POSIX Extensions"); - if (tcon->ses->server->ops->dump_share_caps) - tcon->ses->server->ops->dump_share_caps(m, tcon); - if (tcon->use_witness) - seq_puts(m, " Witness"); - if (tcon->broken_sparse_sup) - seq_puts(m, " nosparse"); - if (tcon->need_reconnect) - seq_puts(m, "\tDISCONNECTED "); - seq_putc(m, '\n'); -} - -static void -cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) -{ - struct TCP_Server_Info *server = chan->server; - - seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx" - "\n\t\tNumber of credits: %d Dialect 0x%x" - "\n\t\tTCP status: %d Instance: %d" - "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d" - "\n\t\tIn Send: %d In MaxReq Wait: %d", - i+1, server->conn_id, - server->credits, - server->dialect, - server->tcpStatus, - server->reconnect_instance, - server->srv_count, - server->sec_mode, - in_flight(server), - atomic_read(&server->in_send), - atomic_read(&server->num_waiters)); -} - -static void -cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) -{ - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; - - seq_printf(m, "\tSpeed: %zu bps\n", iface->speed); - seq_puts(m, "\t\tCapabilities: "); - if (iface->rdma_capable) - seq_puts(m, "rdma "); - if (iface->rss_capable) - seq_puts(m, "rss "); - seq_putc(m, '\n'); - if (iface->sockaddr.ss_family == AF_INET) - seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr); - else if (iface->sockaddr.ss_family == AF_INET6) - seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr); - if (!iface->is_active) - seq_puts(m, "\t\t[for-cleanup]\n"); -} - -static int cifs_debug_files_proc_show(struct seq_file *m, void *v) -{ - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - - seq_puts(m, "# Version:1\n"); - seq_puts(m, "# Format:\n"); - seq_puts(m, "# "); -#ifdef CONFIG_CIFS_DEBUG2 - seq_printf(m, " \n"); -#else - seq_printf(m, " \n"); -#endif /* CIFS_DEBUG2 */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - seq_printf(m, - "0x%x 0x%llx 0x%llx 0x%x %d %d %d %pd", - tcon->tid, - ses->Suid, - cfile->fid.persistent_fid, - cfile->f_flags, - cfile->count, - cfile->pid, - from_kuid(&init_user_ns, cfile->uid), - cfile->dentry); -#ifdef CONFIG_CIFS_DEBUG2 - seq_printf(m, " %llu\n", cfile->fid.mid); -#else - seq_printf(m, "\n"); -#endif /* CIFS_DEBUG2 */ - } - spin_unlock(&tcon->open_file_lock); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - seq_putc(m, '\n'); - return 0; -} - -static int cifs_debug_data_proc_show(struct seq_file *m, void *v) -{ - struct mid_q_entry *mid_entry; - struct TCP_Server_Info *server; - struct TCP_Server_Info *chan_server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifs_server_iface *iface; - int c, i, j; - - seq_puts(m, - "Display Internal CIFS Data Structures for Debugging\n" - "---------------------------------------------------\n"); - seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); - seq_printf(m, "Features:"); -#ifdef CONFIG_CIFS_DFS_UPCALL - seq_printf(m, " DFS"); -#endif -#ifdef CONFIG_CIFS_FSCACHE - seq_printf(m, ",FSCACHE"); -#endif -#ifdef CONFIG_CIFS_SMB_DIRECT - seq_printf(m, ",SMB_DIRECT"); -#endif -#ifdef CONFIG_CIFS_STATS2 - seq_printf(m, ",STATS2"); -#else - seq_printf(m, ",STATS"); -#endif -#ifdef CONFIG_CIFS_DEBUG2 - seq_printf(m, ",DEBUG2"); -#elif defined(CONFIG_CIFS_DEBUG) - seq_printf(m, ",DEBUG"); -#endif -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - seq_printf(m, ",ALLOW_INSECURE_LEGACY"); -#endif -#ifdef CONFIG_CIFS_POSIX - seq_printf(m, ",CIFS_POSIX"); -#endif -#ifdef CONFIG_CIFS_UPCALL - seq_printf(m, ",UPCALL(SPNEGO)"); -#endif -#ifdef CONFIG_CIFS_XATTR - seq_printf(m, ",XATTR"); -#endif - seq_printf(m, ",ACL"); -#ifdef CONFIG_CIFS_SWN_UPCALL - seq_puts(m, ",WITNESS"); -#endif - seq_putc(m, '\n'); - seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize); - seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); - - seq_printf(m, "\nServers: "); - - c = 0; - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - /* channel info will be printed as a part of sessions below */ - if (CIFS_SERVER_IS_CHAN(server)) - continue; - - c++; - seq_printf(m, "\n%d) ConnectionId: 0x%llx ", - c, server->conn_id); - - spin_lock(&server->srv_lock); - if (server->hostname) - seq_printf(m, "Hostname: %s ", server->hostname); - spin_unlock(&server->srv_lock); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (!server->rdma) - goto skip_rdma; - - if (!server->smbd_conn) { - seq_printf(m, "\nSMBDirect transport not available"); - goto skip_rdma; - } - - seq_printf(m, "\nSMBDirect (in hex) protocol version: %x " - "transport status: %x", - server->smbd_conn->protocol, - server->smbd_conn->transport_status); - seq_printf(m, "\nConn receive_credit_max: %x " - "send_credit_target: %x max_send_size: %x", - server->smbd_conn->receive_credit_max, - server->smbd_conn->send_credit_target, - server->smbd_conn->max_send_size); - seq_printf(m, "\nConn max_fragmented_recv_size: %x " - "max_fragmented_send_size: %x max_receive_size:%x", - server->smbd_conn->max_fragmented_recv_size, - server->smbd_conn->max_fragmented_send_size, - server->smbd_conn->max_receive_size); - seq_printf(m, "\nConn keep_alive_interval: %x " - "max_readwrite_size: %x rdma_readwrite_threshold: %x", - server->smbd_conn->keep_alive_interval, - server->smbd_conn->max_readwrite_size, - server->smbd_conn->rdma_readwrite_threshold); - seq_printf(m, "\nDebug count_get_receive_buffer: %x " - "count_put_receive_buffer: %x count_send_empty: %x", - server->smbd_conn->count_get_receive_buffer, - server->smbd_conn->count_put_receive_buffer, - server->smbd_conn->count_send_empty); - seq_printf(m, "\nRead Queue count_reassembly_queue: %x " - "count_enqueue_reassembly_queue: %x " - "count_dequeue_reassembly_queue: %x " - "fragment_reassembly_remaining: %x " - "reassembly_data_length: %x " - "reassembly_queue_length: %x", - server->smbd_conn->count_reassembly_queue, - server->smbd_conn->count_enqueue_reassembly_queue, - server->smbd_conn->count_dequeue_reassembly_queue, - server->smbd_conn->fragment_reassembly_remaining, - server->smbd_conn->reassembly_data_length, - server->smbd_conn->reassembly_queue_length); - seq_printf(m, "\nCurrent Credits send_credits: %x " - "receive_credits: %x receive_credit_target: %x", - atomic_read(&server->smbd_conn->send_credits), - atomic_read(&server->smbd_conn->receive_credits), - server->smbd_conn->receive_credit_target); - seq_printf(m, "\nPending send_pending: %x ", - atomic_read(&server->smbd_conn->send_pending)); - seq_printf(m, "\nReceive buffers count_receive_queue: %x " - "count_empty_packet_queue: %x", - server->smbd_conn->count_receive_queue, - server->smbd_conn->count_empty_packet_queue); - seq_printf(m, "\nMR responder_resources: %x " - "max_frmr_depth: %x mr_type: %x", - server->smbd_conn->responder_resources, - server->smbd_conn->max_frmr_depth, - server->smbd_conn->mr_type); - seq_printf(m, "\nMR mr_ready_count: %x mr_used_count: %x", - atomic_read(&server->smbd_conn->mr_ready_count), - atomic_read(&server->smbd_conn->mr_used_count)); -skip_rdma: -#endif - seq_printf(m, "\nNumber of credits: %d Dialect 0x%x", - server->credits, server->dialect); - if (server->compress_algorithm == SMB3_COMPRESS_LZNT1) - seq_printf(m, " COMPRESS_LZNT1"); - else if (server->compress_algorithm == SMB3_COMPRESS_LZ77) - seq_printf(m, " COMPRESS_LZ77"); - else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF) - seq_printf(m, " COMPRESS_LZ77_HUFF"); - if (server->sign) - seq_printf(m, " signed"); - if (server->posix_ext_supported) - seq_printf(m, " posix"); - if (server->nosharesock) - seq_printf(m, " nosharesock"); - - if (server->rdma) - seq_printf(m, "\nRDMA "); - seq_printf(m, "\nTCP status: %d Instance: %d" - "\nLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d", - server->tcpStatus, - server->reconnect_instance, - server->srv_count, - server->sec_mode, in_flight(server)); - - seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d", - atomic_read(&server->in_send), - atomic_read(&server->num_waiters)); - - seq_printf(m, "\n\n\tSessions: "); - i = 0; - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - i++; - if ((ses->serverDomain == NULL) || - (ses->serverOS == NULL) || - (ses->serverNOS == NULL)) { - seq_printf(m, "\n\t%d) Address: %s Uses: %d Capability: 0x%x\tSession Status: %d ", - i, ses->ip_addr, ses->ses_count, - ses->capabilities, ses->ses_status); - if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) - seq_printf(m, "Guest "); - else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) - seq_printf(m, "Anonymous "); - } else { - seq_printf(m, - "\n\t%d) Name: %s Domain: %s Uses: %d OS: %s " - "\n\tNOS: %s\tCapability: 0x%x" - "\n\tSMB session status: %d ", - i, ses->ip_addr, ses->serverDomain, - ses->ses_count, ses->serverOS, ses->serverNOS, - ses->capabilities, ses->ses_status); - } - - seq_printf(m, "\n\tSecurity type: %s ", - get_security_type_str(server->ops->select_sectype(server, ses->sectype))); - - /* dump session id helpful for use with network trace */ - seq_printf(m, " SessionId: 0x%llx", ses->Suid); - if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) - seq_puts(m, " encrypted"); - if (ses->sign) - seq_puts(m, " signed"); - - seq_printf(m, "\n\tUser: %d Cred User: %d", - from_kuid(&init_user_ns, ses->linux_uid), - from_kuid(&init_user_ns, ses->cred_uid)); - - spin_lock(&ses->chan_lock); - if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0)) - seq_puts(m, "\tPrimary channel: DISCONNECTED "); - if (CIFS_CHAN_IN_RECONNECT(ses, 0)) - seq_puts(m, "\t[RECONNECTING] "); - - if (ses->chan_count > 1) { - seq_printf(m, "\n\n\tExtra Channels: %zu ", - ses->chan_count-1); - for (j = 1; j < ses->chan_count; j++) { - cifs_dump_channel(m, j, &ses->chans[j]); - if (CIFS_CHAN_NEEDS_RECONNECT(ses, j)) - seq_puts(m, "\tDISCONNECTED "); - if (CIFS_CHAN_IN_RECONNECT(ses, j)) - seq_puts(m, "\t[RECONNECTING] "); - } - } - spin_unlock(&ses->chan_lock); - - seq_puts(m, "\n\n\tShares: "); - j = 0; - - seq_printf(m, "\n\t%d) IPC: ", j); - if (ses->tcon_ipc) - cifs_debug_tcon(m, ses->tcon_ipc); - else - seq_puts(m, "none\n"); - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - ++j; - seq_printf(m, "\n\t%d) ", j); - cifs_debug_tcon(m, tcon); - } - - spin_lock(&ses->iface_lock); - if (ses->iface_count) - seq_printf(m, "\n\n\tServer interfaces: %zu", - ses->iface_count); - j = 0; - list_for_each_entry(iface, &ses->iface_list, - iface_head) { - seq_printf(m, "\n\t%d)", ++j); - cifs_dump_iface(m, iface); - if (is_ses_using_iface(ses, iface)) - seq_puts(m, "\t\t[CONNECTED]\n"); - } - spin_unlock(&ses->iface_lock); - - seq_puts(m, "\n\n\tMIDs: "); - spin_lock(&ses->chan_lock); - for (j = 0; j < ses->chan_count; j++) { - chan_server = ses->chans[j].server; - if (!chan_server) - continue; - - if (list_empty(&chan_server->pending_mid_q)) - continue; - - seq_printf(m, "\n\tServer ConnectionId: 0x%llx", - chan_server->conn_id); - spin_lock(&chan_server->mid_lock); - list_for_each_entry(mid_entry, &chan_server->pending_mid_q, qhead) { - seq_printf(m, "\n\t\tState: %d com: %d pid: %d cbdata: %p mid %llu", - mid_entry->mid_state, - le16_to_cpu(mid_entry->command), - mid_entry->pid, - mid_entry->callback_data, - mid_entry->mid); - } - spin_unlock(&chan_server->mid_lock); - } - spin_unlock(&ses->chan_lock); - seq_puts(m, "\n--\n"); - } - if (i == 0) - seq_printf(m, "\n\t\t[NONE]"); - } - if (c == 0) - seq_printf(m, "\n\t[NONE]"); - - spin_unlock(&cifs_tcp_ses_lock); - seq_putc(m, '\n'); - cifs_swn_dump(m); - - /* BB add code to dump additional info such as TCP session info now */ - return 0; -} - -static ssize_t cifs_stats_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - bool bv; - int rc; - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - rc = kstrtobool_from_user(buffer, count, &bv); - if (rc == 0) { -#ifdef CONFIG_CIFS_STATS2 - int i; - - atomic_set(&total_buf_alloc_count, 0); - atomic_set(&total_small_buf_alloc_count, 0); -#endif /* CONFIG_CIFS_STATS2 */ - atomic_set(&tcpSesReconnectCount, 0); - atomic_set(&tconInfoReconnectCount, 0); - - spin_lock(&GlobalMid_Lock); - GlobalMaxActiveXid = 0; - GlobalCurrentXid = 0; - spin_unlock(&GlobalMid_Lock); - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - server->max_in_flight = 0; -#ifdef CONFIG_CIFS_STATS2 - for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { - atomic_set(&server->num_cmds[i], 0); - atomic_set(&server->smb2slowcmd[i], 0); - server->time_per_cmd[i] = 0; - server->slowest_cmd[i] = 0; - server->fastest_cmd[0] = 0; - } -#endif /* CONFIG_CIFS_STATS2 */ - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - atomic_set(&tcon->num_smbs_sent, 0); - spin_lock(&tcon->stat_lock); - tcon->bytes_read = 0; - tcon->bytes_written = 0; - spin_unlock(&tcon->stat_lock); - if (server->ops->clear_stats) - server->ops->clear_stats(tcon); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - } else { - return rc; - } - - return count; -} - -static int cifs_stats_proc_show(struct seq_file *m, void *v) -{ - int i; -#ifdef CONFIG_CIFS_STATS2 - int j; -#endif /* STATS2 */ - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - seq_printf(m, "Resources in use\nCIFS Session: %d\n", - sesInfoAllocCount.counter); - seq_printf(m, "Share (unique mount targets): %d\n", - tconInfoAllocCount.counter); - seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n", - buf_alloc_count.counter, - cifs_min_rcv + tcpSesAllocCount.counter); - seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n", - small_buf_alloc_count.counter, cifs_min_small); -#ifdef CONFIG_CIFS_STATS2 - seq_printf(m, "Total Large %d Small %d Allocations\n", - atomic_read(&total_buf_alloc_count), - atomic_read(&total_small_buf_alloc_count)); -#endif /* CONFIG_CIFS_STATS2 */ - - seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&mid_count)); - seq_printf(m, - "\n%d session %d share reconnects\n", - tcpSesReconnectCount.counter, tconInfoReconnectCount.counter); - - seq_printf(m, - "Total vfs operations: %d maximum at one time: %d\n", - GlobalCurrentXid, GlobalMaxActiveXid); - - i = 0; - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - seq_printf(m, "\nMax requests in flight: %d", server->max_in_flight); -#ifdef CONFIG_CIFS_STATS2 - seq_puts(m, "\nTotal time spent processing by command. Time "); - seq_printf(m, "units are jiffies (%d per second)\n", HZ); - seq_puts(m, " SMB3 CMD\tNumber\tTotal Time\tFastest\tSlowest\n"); - seq_puts(m, " --------\t------\t----------\t-------\t-------\n"); - for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) - seq_printf(m, " %d\t\t%d\t%llu\t\t%u\t%u\n", j, - atomic_read(&server->num_cmds[j]), - server->time_per_cmd[j], - server->fastest_cmd[j], - server->slowest_cmd[j]); - for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) - if (atomic_read(&server->smb2slowcmd[j])) { - spin_lock(&server->srv_lock); - seq_printf(m, " %d slow responses from %s for command %d\n", - atomic_read(&server->smb2slowcmd[j]), - server->hostname, j); - spin_unlock(&server->srv_lock); - } -#endif /* STATS2 */ - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - i++; - seq_printf(m, "\n%d) %s", i, tcon->tree_name); - if (tcon->need_reconnect) - seq_puts(m, "\tDISCONNECTED "); - seq_printf(m, "\nSMBs: %d", - atomic_read(&tcon->num_smbs_sent)); - if (server->ops->print_stats) - server->ops->print_stats(m, tcon); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - - seq_putc(m, '\n'); - return 0; -} - -static int cifs_stats_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_stats_proc_show, NULL); -} - -static const struct proc_ops cifs_stats_proc_ops = { - .proc_open = cifs_stats_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_stats_proc_write, -}; - -#ifdef CONFIG_CIFS_SMB_DIRECT -#define PROC_FILE_DEFINE(name) \ -static ssize_t name##_write(struct file *file, const char __user *buffer, \ - size_t count, loff_t *ppos) \ -{ \ - int rc; \ - rc = kstrtoint_from_user(buffer, count, 10, & name); \ - if (rc) \ - return rc; \ - return count; \ -} \ -static int name##_proc_show(struct seq_file *m, void *v) \ -{ \ - seq_printf(m, "%d\n", name ); \ - return 0; \ -} \ -static int name##_open(struct inode *inode, struct file *file) \ -{ \ - return single_open(file, name##_proc_show, NULL); \ -} \ -\ -static const struct proc_ops cifs_##name##_proc_fops = { \ - .proc_open = name##_open, \ - .proc_read = seq_read, \ - .proc_lseek = seq_lseek, \ - .proc_release = single_release, \ - .proc_write = name##_write, \ -} - -PROC_FILE_DEFINE(rdma_readwrite_threshold); -PROC_FILE_DEFINE(smbd_max_frmr_depth); -PROC_FILE_DEFINE(smbd_keep_alive_interval); -PROC_FILE_DEFINE(smbd_max_receive_size); -PROC_FILE_DEFINE(smbd_max_fragmented_recv_size); -PROC_FILE_DEFINE(smbd_max_send_size); -PROC_FILE_DEFINE(smbd_send_credit_target); -PROC_FILE_DEFINE(smbd_receive_credit_max); -#endif - -static struct proc_dir_entry *proc_fs_cifs; -static const struct proc_ops cifsFYI_proc_ops; -static const struct proc_ops cifs_lookup_cache_proc_ops; -static const struct proc_ops traceSMB_proc_ops; -static const struct proc_ops cifs_security_flags_proc_ops; -static const struct proc_ops cifs_linux_ext_proc_ops; -static const struct proc_ops cifs_mount_params_proc_ops; - -void -cifs_proc_init(void) -{ - proc_fs_cifs = proc_mkdir("fs/cifs", NULL); - if (proc_fs_cifs == NULL) - return; - - proc_create_single("DebugData", 0, proc_fs_cifs, - cifs_debug_data_proc_show); - - proc_create_single("open_files", 0400, proc_fs_cifs, - cifs_debug_files_proc_show); - - proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops); - proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops); - proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops); - proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs, - &cifs_linux_ext_proc_ops); - proc_create("SecurityFlags", 0644, proc_fs_cifs, - &cifs_security_flags_proc_ops); - proc_create("LookupCacheEnabled", 0644, proc_fs_cifs, - &cifs_lookup_cache_proc_ops); - - proc_create("mount_params", 0444, proc_fs_cifs, &cifs_mount_params_proc_ops); - -#ifdef CONFIG_CIFS_DFS_UPCALL - proc_create("dfscache", 0644, proc_fs_cifs, &dfscache_proc_ops); -#endif - -#ifdef CONFIG_CIFS_SMB_DIRECT - proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs, - &cifs_rdma_readwrite_threshold_proc_fops); - proc_create("smbd_max_frmr_depth", 0644, proc_fs_cifs, - &cifs_smbd_max_frmr_depth_proc_fops); - proc_create("smbd_keep_alive_interval", 0644, proc_fs_cifs, - &cifs_smbd_keep_alive_interval_proc_fops); - proc_create("smbd_max_receive_size", 0644, proc_fs_cifs, - &cifs_smbd_max_receive_size_proc_fops); - proc_create("smbd_max_fragmented_recv_size", 0644, proc_fs_cifs, - &cifs_smbd_max_fragmented_recv_size_proc_fops); - proc_create("smbd_max_send_size", 0644, proc_fs_cifs, - &cifs_smbd_max_send_size_proc_fops); - proc_create("smbd_send_credit_target", 0644, proc_fs_cifs, - &cifs_smbd_send_credit_target_proc_fops); - proc_create("smbd_receive_credit_max", 0644, proc_fs_cifs, - &cifs_smbd_receive_credit_max_proc_fops); -#endif -} - -void -cifs_proc_clean(void) -{ - if (proc_fs_cifs == NULL) - return; - - remove_proc_entry("DebugData", proc_fs_cifs); - remove_proc_entry("open_files", proc_fs_cifs); - remove_proc_entry("cifsFYI", proc_fs_cifs); - remove_proc_entry("traceSMB", proc_fs_cifs); - remove_proc_entry("Stats", proc_fs_cifs); - remove_proc_entry("SecurityFlags", proc_fs_cifs); - remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); - remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); - remove_proc_entry("mount_params", proc_fs_cifs); - -#ifdef CONFIG_CIFS_DFS_UPCALL - remove_proc_entry("dfscache", proc_fs_cifs); -#endif -#ifdef CONFIG_CIFS_SMB_DIRECT - remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs); - remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs); - remove_proc_entry("smbd_keep_alive_interval", proc_fs_cifs); - remove_proc_entry("smbd_max_receive_size", proc_fs_cifs); - remove_proc_entry("smbd_max_fragmented_recv_size", proc_fs_cifs); - remove_proc_entry("smbd_max_send_size", proc_fs_cifs); - remove_proc_entry("smbd_send_credit_target", proc_fs_cifs); - remove_proc_entry("smbd_receive_credit_max", proc_fs_cifs); -#endif - remove_proc_entry("fs/cifs", NULL); -} - -static int cifsFYI_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", cifsFYI); - return 0; -} - -static int cifsFYI_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifsFYI_proc_show, NULL); -} - -static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - char c[2] = { '\0' }; - bool bv; - int rc; - - rc = get_user(c[0], buffer); - if (rc) - return rc; - if (strtobool(c, &bv) == 0) - cifsFYI = bv; - else if ((c[0] > '1') && (c[0] <= '9')) - cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */ - else - return -EINVAL; - - return count; -} - -static const struct proc_ops cifsFYI_proc_ops = { - .proc_open = cifsFYI_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifsFYI_proc_write, -}; - -static int cifs_linux_ext_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", linuxExtEnabled); - return 0; -} - -static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_linux_ext_proc_show, NULL); -} - -static ssize_t cifs_linux_ext_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - int rc; - - rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled); - if (rc) - return rc; - - return count; -} - -static const struct proc_ops cifs_linux_ext_proc_ops = { - .proc_open = cifs_linux_ext_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_linux_ext_proc_write, -}; - -static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", lookupCacheEnabled); - return 0; -} - -static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_lookup_cache_proc_show, NULL); -} - -static ssize_t cifs_lookup_cache_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - int rc; - - rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled); - if (rc) - return rc; - - return count; -} - -static const struct proc_ops cifs_lookup_cache_proc_ops = { - .proc_open = cifs_lookup_cache_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_lookup_cache_proc_write, -}; - -static int traceSMB_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", traceSMB); - return 0; -} - -static int traceSMB_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, traceSMB_proc_show, NULL); -} - -static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - int rc; - - rc = kstrtobool_from_user(buffer, count, &traceSMB); - if (rc) - return rc; - - return count; -} - -static const struct proc_ops traceSMB_proc_ops = { - .proc_open = traceSMB_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = traceSMB_proc_write, -}; - -static int cifs_security_flags_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "0x%x\n", global_secflags); - return 0; -} - -static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_security_flags_proc_show, NULL); -} - -/* - * Ensure that if someone sets a MUST flag, that we disable all other MAY - * flags except for the ones corresponding to the given MUST flag. If there are - * multiple MUST flags, then try to prefer more secure ones. - */ -static void -cifs_security_flags_handle_must_flags(unsigned int *flags) -{ - unsigned int signflags = *flags & CIFSSEC_MUST_SIGN; - - if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) - *flags = CIFSSEC_MUST_KRB5; - else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) - *flags = CIFSSEC_MUST_NTLMSSP; - else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) - *flags = CIFSSEC_MUST_NTLMV2; - - *flags |= signflags; -} - -static ssize_t cifs_security_flags_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - int rc; - unsigned int flags; - char flags_string[12]; - bool bv; - - if ((count < 1) || (count > 11)) - return -EINVAL; - - memset(flags_string, 0, 12); - - if (copy_from_user(flags_string, buffer, count)) - return -EFAULT; - - if (count < 3) { - /* single char or single char followed by null */ - if (strtobool(flags_string, &bv) == 0) { - global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF; - return count; - } else if (!isdigit(flags_string[0])) { - cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", - flags_string); - return -EINVAL; - } - } - - /* else we have a number */ - rc = kstrtouint(flags_string, 0, &flags); - if (rc) { - cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", - flags_string); - return rc; - } - - cifs_dbg(FYI, "sec flags 0x%x\n", flags); - - if (flags == 0) { - cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string); - return -EINVAL; - } - - if (flags & ~CIFSSEC_MASK) { - cifs_dbg(VFS, "Unsupported security flags: 0x%x\n", - flags & ~CIFSSEC_MASK); - return -EINVAL; - } - - cifs_security_flags_handle_must_flags(&flags); - - /* flags look ok - update the global security flags for cifs module */ - global_secflags = flags; - if (global_secflags & CIFSSEC_MUST_SIGN) { - /* requiring signing implies signing is allowed */ - global_secflags |= CIFSSEC_MAY_SIGN; - cifs_dbg(FYI, "packet signing now required\n"); - } else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) { - cifs_dbg(FYI, "packet signing disabled\n"); - } - /* BB should we turn on MAY flags for other MUST options? */ - return count; -} - -static const struct proc_ops cifs_security_flags_proc_ops = { - .proc_open = cifs_security_flags_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_security_flags_proc_write, -}; - -/* To make it easier to debug, can help to show mount params */ -static int cifs_mount_params_proc_show(struct seq_file *m, void *v) -{ - const struct fs_parameter_spec *p; - const char *type; - - for (p = smb3_fs_parameters; p->name; p++) { - /* cannot use switch with pointers... */ - if (!p->type) { - if (p->flags == fs_param_neg_with_no) - type = "noflag"; - else - type = "flag"; - } else if (p->type == fs_param_is_bool) - type = "bool"; - else if (p->type == fs_param_is_u32) - type = "u32"; - else if (p->type == fs_param_is_u64) - type = "u64"; - else if (p->type == fs_param_is_string) - type = "string"; - else - type = "unknown"; - - seq_printf(m, "%s:%s\n", p->name, type); - } - - return 0; -} - -static int cifs_mount_params_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_mount_params_proc_show, NULL); -} - -static const struct proc_ops cifs_mount_params_proc_ops = { - .proc_open = cifs_mount_params_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - /* No need for write for now */ - /* .proc_write = cifs_mount_params_proc_write, */ -}; - -#else -inline void cifs_proc_init(void) -{ -} - -inline void cifs_proc_clean(void) -{ -} -#endif /* PROC_FS */ diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h deleted file mode 100644 index ce5cfd236fdb..000000000000 --- a/fs/cifs/cifs_debug.h +++ /dev/null @@ -1,160 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * Copyright (c) International Business Machines Corp., 2000,2002 - * Modified by Steve French (sfrench@us.ibm.com) - */ - -#ifndef _H_CIFS_DEBUG -#define _H_CIFS_DEBUG - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#define pr_fmt(fmt) "CIFS: " fmt - -void cifs_dump_mem(char *label, void *data, int length); -void cifs_dump_detail(void *buf, struct TCP_Server_Info *ptcp_info); -void cifs_dump_mids(struct TCP_Server_Info *); -extern bool traceSMB; /* flag which enables the function below */ -void dump_smb(void *, int); -#define CIFS_INFO 0x01 -#define CIFS_RC 0x02 -#define CIFS_TIMER 0x04 - -#define VFS 1 -#define FYI 2 -extern int cifsFYI; -#ifdef CONFIG_CIFS_DEBUG2 -#define NOISY 4 -#else -#define NOISY 0 -#endif -#define ONCE 8 - -/* - * debug ON - * -------- - */ -#ifdef CONFIG_CIFS_DEBUG - - -/* - * When adding tracepoints and debug messages we have various choices. - * Some considerations: - * - * Use cifs_dbg(VFS, ...) for things we always want logged, and the user to see - * cifs_info(...) slightly less important, admin can filter via loglevel > 6 - * cifs_dbg(FYI, ...) minor debugging messages, off by default - * trace_smb3_* ftrace functions are preferred for complex debug messages - * intended for developers or experienced admins, off by default - */ - -/* Information level messages, minor events */ -#define cifs_info_func(ratefunc, fmt, ...) \ - pr_info_ ## ratefunc(fmt, ##__VA_ARGS__) - -#define cifs_info(fmt, ...) \ - cifs_info_func(ratelimited, fmt, ##__VA_ARGS__) - -/* information message: e.g., configuration, major event */ -#define cifs_dbg_func(ratefunc, type, fmt, ...) \ -do { \ - if ((type) & FYI && cifsFYI & CIFS_INFO) { \ - pr_debug_ ## ratefunc("%s: " fmt, \ - __FILE__, ##__VA_ARGS__); \ - } else if ((type) & VFS) { \ - pr_err_ ## ratefunc("VFS: " fmt, ##__VA_ARGS__); \ - } else if ((type) & NOISY && (NOISY != 0)) { \ - pr_debug_ ## ratefunc(fmt, ##__VA_ARGS__); \ - } \ -} while (0) - -#define cifs_dbg(type, fmt, ...) \ -do { \ - if ((type) & ONCE) \ - cifs_dbg_func(once, type, fmt, ##__VA_ARGS__); \ - else \ - cifs_dbg_func(ratelimited, type, fmt, ##__VA_ARGS__); \ -} while (0) - -#define cifs_server_dbg_func(ratefunc, type, fmt, ...) \ -do { \ - spin_lock(&server->srv_lock); \ - if ((type) & FYI && cifsFYI & CIFS_INFO) { \ - pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \ - __FILE__, server->hostname, \ - ##__VA_ARGS__); \ - } else if ((type) & VFS) { \ - pr_err_ ## ratefunc("VFS: \\\\%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ - } else if ((type) & NOISY && (NOISY != 0)) { \ - pr_debug_ ## ratefunc("\\\\%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ - } \ - spin_unlock(&server->srv_lock); \ -} while (0) - -#define cifs_server_dbg(type, fmt, ...) \ -do { \ - if ((type) & ONCE) \ - cifs_server_dbg_func(once, type, fmt, ##__VA_ARGS__); \ - else \ - cifs_server_dbg_func(ratelimited, type, fmt, \ - ##__VA_ARGS__); \ -} while (0) - -#define cifs_tcon_dbg_func(ratefunc, type, fmt, ...) \ -do { \ - const char *tn = ""; \ - if (tcon && tcon->tree_name) \ - tn = tcon->tree_name; \ - if ((type) & FYI && cifsFYI & CIFS_INFO) { \ - pr_debug_ ## ratefunc("%s: %s " fmt, \ - __FILE__, tn, ##__VA_ARGS__); \ - } else if ((type) & VFS) { \ - pr_err_ ## ratefunc("VFS: %s " fmt, tn, ##__VA_ARGS__); \ - } else if ((type) & NOISY && (NOISY != 0)) { \ - pr_debug_ ## ratefunc("%s " fmt, tn, ##__VA_ARGS__); \ - } \ -} while (0) - -#define cifs_tcon_dbg(type, fmt, ...) \ -do { \ - if ((type) & ONCE) \ - cifs_tcon_dbg_func(once, type, fmt, ##__VA_ARGS__); \ - else \ - cifs_tcon_dbg_func(ratelimited, type, fmt, \ - ##__VA_ARGS__); \ -} while (0) - -/* - * debug OFF - * --------- - */ -#else /* _CIFS_DEBUG */ -#define cifs_dbg(type, fmt, ...) \ -do { \ - if (0) \ - pr_debug(fmt, ##__VA_ARGS__); \ -} while (0) - -#define cifs_server_dbg(type, fmt, ...) \ -do { \ - if (0) \ - pr_debug("\\\\%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ -} while (0) - -#define cifs_tcon_dbg(type, fmt, ...) \ -do { \ - if (0) \ - pr_debug("%s " fmt, tcon->tree_name, ##__VA_ARGS__); \ -} while (0) - -#define cifs_info(fmt, ...) \ - pr_info(fmt, ##__VA_ARGS__) -#endif - -#endif /* _H_CIFS_DEBUG */ diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c deleted file mode 100644 index b0864da9ef43..000000000000 --- a/fs/cifs/cifs_dfs_ref.c +++ /dev/null @@ -1,374 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Contains the CIFS DFS referral mounting routines used for handling - * traversal via DFS junction point - * - * Copyright (c) 2007 Igor Mammedov - * Copyright (C) International Business Machines Corp., 2008 - * Author(s): Igor Mammedov (niallain@gmail.com) - * Steve French (sfrench@us.ibm.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifsfs.h" -#include "dns_resolve.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "dfs_cache.h" -#include "fs_context.h" - -static LIST_HEAD(cifs_dfs_automount_list); - -static void cifs_dfs_expire_automounts(struct work_struct *work); -static DECLARE_DELAYED_WORK(cifs_dfs_automount_task, - cifs_dfs_expire_automounts); -static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; - -static void cifs_dfs_expire_automounts(struct work_struct *work) -{ - struct list_head *list = &cifs_dfs_automount_list; - - mark_mounts_for_expiry(list); - if (!list_empty(list)) - schedule_delayed_work(&cifs_dfs_automount_task, - cifs_dfs_mountpoint_expiry_timeout); -} - -void cifs_dfs_release_automount_timer(void) -{ - BUG_ON(!list_empty(&cifs_dfs_automount_list)); - cancel_delayed_work_sync(&cifs_dfs_automount_task); -} - -/** - * cifs_build_devname - build a devicename from a UNC and optional prepath - * @nodename: pointer to UNC string - * @prepath: pointer to prefixpath (or NULL if there isn't one) - * - * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer - * big enough to hold the final thing. Copy the UNC from the nodename, and - * concatenate the prepath onto the end of it if there is one. - * - * Returns pointer to the built string, or a ERR_PTR. Caller is responsible - * for freeing the returned string. - */ -static char * -cifs_build_devname(char *nodename, const char *prepath) -{ - size_t pplen; - size_t unclen; - char *dev; - char *pos; - - /* skip over any preceding delimiters */ - nodename += strspn(nodename, "\\"); - if (!*nodename) - return ERR_PTR(-EINVAL); - - /* get length of UNC and set pos to last char */ - unclen = strlen(nodename); - pos = nodename + unclen - 1; - - /* trim off any trailing delimiters */ - while (*pos == '\\') { - --pos; - --unclen; - } - - /* allocate a buffer: - * +2 for preceding "//" - * +1 for delimiter between UNC and prepath - * +1 for trailing NULL - */ - pplen = prepath ? strlen(prepath) : 0; - dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); - if (!dev) - return ERR_PTR(-ENOMEM); - - pos = dev; - /* add the initial "//" */ - *pos = '/'; - ++pos; - *pos = '/'; - ++pos; - - /* copy in the UNC portion from referral */ - memcpy(pos, nodename, unclen); - pos += unclen; - - /* copy the prefixpath remainder (if there is one) */ - if (pplen) { - *pos = '/'; - ++pos; - memcpy(pos, prepath, pplen); - pos += pplen; - } - - /* NULL terminator */ - *pos = '\0'; - - convert_delimiter(dev, '/'); - return dev; -} - - -/** - * cifs_compose_mount_options - creates mount options for referral - * @sb_mountdata: parent/root DFS mount options (template) - * @fullpath: full path in UNC format - * @ref: optional server's referral - * @devname: return the built cifs device name if passed pointer not NULL - * creates mount options for submount based on template options sb_mountdata - * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. - * - * Returns: pointer to new mount options or ERR_PTR. - * Caller is responsible for freeing returned value if it is not error. - */ -char *cifs_compose_mount_options(const char *sb_mountdata, - const char *fullpath, - const struct dfs_info3_param *ref, - char **devname) -{ - int rc; - char *name; - char *mountdata = NULL; - const char *prepath = NULL; - int md_len; - char *tkn_e; - char *srvIP = NULL; - char sep = ','; - int off, noff; - - if (sb_mountdata == NULL) - return ERR_PTR(-EINVAL); - - if (ref) { - if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) - return ERR_PTR(-EINVAL); - - if (strlen(fullpath) - ref->path_consumed) { - prepath = fullpath + ref->path_consumed; - /* skip initial delimiter */ - if (*prepath == '/' || *prepath == '\\') - prepath++; - } - - name = cifs_build_devname(ref->node_name, prepath); - if (IS_ERR(name)) { - rc = PTR_ERR(name); - name = NULL; - goto compose_mount_options_err; - } - } else { - name = cifs_build_devname((char *)fullpath, NULL); - if (IS_ERR(name)) { - rc = PTR_ERR(name); - name = NULL; - goto compose_mount_options_err; - } - } - - rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL); - if (rc < 0) { - cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", - __func__, name, rc); - goto compose_mount_options_err; - } - - /* - * In most cases, we'll be building a shorter string than the original, - * but we do have to assume that the address in the ip= option may be - * much longer than the original. Add the max length of an address - * string to the length of the original string to allow for worst case. - */ - md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN; - mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL); - if (mountdata == NULL) { - rc = -ENOMEM; - goto compose_mount_options_err; - } - - /* copy all options except of unc,ip,prefixpath */ - off = 0; - if (strncmp(sb_mountdata, "sep=", 4) == 0) { - sep = sb_mountdata[4]; - strncpy(mountdata, sb_mountdata, 5); - off += 5; - } - - do { - tkn_e = strchr(sb_mountdata + off, sep); - if (tkn_e == NULL) - noff = strlen(sb_mountdata + off); - else - noff = tkn_e - (sb_mountdata + off) + 1; - - if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) { - off += noff; - continue; - } - if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) { - off += noff; - continue; - } - if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) { - off += noff; - continue; - } - if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) { - off += noff; - continue; - } - strncat(mountdata, sb_mountdata + off, noff); - off += noff; - } while (tkn_e); - strcat(mountdata, sb_mountdata + off); - mountdata[md_len] = '\0'; - - /* copy new IP and ref share name */ - if (mountdata[strlen(mountdata) - 1] != sep) - strncat(mountdata, &sep, 1); - strcat(mountdata, "ip="); - strcat(mountdata, srvIP); - - if (devname) - *devname = name; - else - kfree(name); - - /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ - /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ - -compose_mount_options_out: - kfree(srvIP); - return mountdata; - -compose_mount_options_err: - kfree(mountdata); - mountdata = ERR_PTR(rc); - kfree(name); - goto compose_mount_options_out; -} - -/** - * cifs_dfs_do_mount - mounts specified path using DFS full path - * - * Always pass down @fullpath to smb3_do_mount() so we can use the root server - * to perform failover in case we failed to connect to the first target in the - * referral. - * - * @mntpt: directory entry for the path we are trying to automount - * @cifs_sb: parent/root superblock - * @fullpath: full path in UNC format - */ -static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, - struct cifs_sb_info *cifs_sb, - const char *fullpath) -{ - struct vfsmount *mnt; - char *mountdata; - char *devname; - - devname = kstrdup(fullpath, GFP_KERNEL); - if (!devname) - return ERR_PTR(-ENOMEM); - - convert_delimiter(devname, '/'); - - /* TODO: change to call fs_context_for_mount(), fill in context directly, call fc_mount */ - - /* See afs_mntpt_do_automount in fs/afs/mntpt.c for an example */ - - /* strip first '\' from fullpath */ - mountdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, - fullpath + 1, NULL, NULL); - if (IS_ERR(mountdata)) { - kfree(devname); - return (struct vfsmount *)mountdata; - } - - mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata); - kfree(mountdata); - kfree(devname); - return mnt; -} - -/* - * Create a vfsmount that we can automount - */ -static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) -{ - struct cifs_sb_info *cifs_sb; - void *page; - char *full_path; - struct vfsmount *mnt; - - cifs_dbg(FYI, "in %s\n", __func__); - BUG_ON(IS_ROOT(mntpt)); - - /* - * The MSDFS spec states that paths in DFS referral requests and - * responses must be prefixed by a single '\' character instead of - * the double backslashes usually used in the UNC. This function - * gives us the latter, so we must adjust the result. - */ - cifs_sb = CIFS_SB(mntpt->d_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { - mnt = ERR_PTR(-EREMOTE); - goto cdda_exit; - } - - page = alloc_dentry_path(); - /* always use tree name prefix */ - full_path = build_path_from_dentry_optional_prefix(mntpt, page, true); - if (IS_ERR(full_path)) { - mnt = ERR_CAST(full_path); - goto free_full_path; - } - - convert_delimiter(full_path, '\\'); - cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); - - mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); - cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt); - -free_full_path: - free_dentry_path(page); -cdda_exit: - cifs_dbg(FYI, "leaving %s\n" , __func__); - return mnt; -} - -/* - * Attempt to automount the referral - */ -struct vfsmount *cifs_dfs_d_automount(struct path *path) -{ - struct vfsmount *newmnt; - - cifs_dbg(FYI, "in %s\n", __func__); - - newmnt = cifs_dfs_do_automount(path->dentry); - if (IS_ERR(newmnt)) { - cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); - return newmnt; - } - - mntget(newmnt); /* prevent immediate expiration */ - mnt_set_expiry(newmnt, &cifs_dfs_automount_list); - schedule_delayed_work(&cifs_dfs_automount_task, - cifs_dfs_mountpoint_expiry_timeout); - cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); - return newmnt; -} - -const struct inode_operations cifs_dfs_referral_inode_operations = { -}; diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h deleted file mode 100644 index 013a4bd65280..000000000000 --- a/fs/cifs/cifs_fs_sb.h +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2004 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include - -#ifndef _CIFS_FS_SB_H -#define _CIFS_FS_SB_H - -#include - -#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ -#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */ -#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */ -#define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */ -#define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */ -#define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ -#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible*/ -#define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */ -#define CIFS_MOUNT_NO_BRL 0x100 /* No sending byte range locks to srv */ -#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */ -#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */ -#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */ -#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */ -#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ -#define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ -#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ -#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ -#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ -#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */ -#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */ -#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of SB_POSIXACL in mnt_cifs_flags */ -#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ -#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */ -#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */ -#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible - * root mountable - */ -#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */ -#define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ -#define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ -#define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ -#define CIFS_MOUNT_RO_CACHE 0x20000000 /* assumes share will not change */ -#define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */ -#define CIFS_MOUNT_SHUTDOWN 0x80000000 - -struct cifs_sb_info { - struct rb_root tlink_tree; - spinlock_t tlink_tree_lock; - struct tcon_link *master_tlink; - struct nls_table *local_nls; - struct smb3_fs_context *ctx; - atomic_t active; - unsigned int mnt_cifs_flags; - struct delayed_work prune_tlinks; - struct rcu_head rcu; - - /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ - char *prepath; - - /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */ - uuid_t dfs_mount_id; - /* - * Indicate whether serverino option was turned off later - * (cifs_autodisable_serverino) in order to match new mounts. - */ - bool mnt_cifs_serverino_autodisabled; - /* - * Available once the mount has completed. - */ - struct dentry *root; -}; -#endif /* _CIFS_FS_SB_H */ diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h deleted file mode 100644 index d86d78d5bfdc..000000000000 --- a/fs/cifs/cifs_ioctl.h +++ /dev/null @@ -1,126 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Structure definitions for io control for cifs/smb3 - * - * Copyright (c) 2015 Steve French - * - */ - -struct smb_mnt_fs_info { - __u32 version; /* 0001 */ - __u16 protocol_id; - __u16 tcon_flags; - __u32 vol_serial_number; - __u32 vol_create_time; - __u32 share_caps; - __u32 share_flags; - __u32 sector_flags; - __u32 optimal_sector_size; - __u32 max_bytes_chunk; - __u32 fs_attributes; - __u32 max_path_component; - __u32 device_type; - __u32 device_characteristics; - __u32 maximal_access; - __u64 cifs_posix_caps; -} __packed; - -struct smb_snapshot_array { - __u32 number_of_snapshots; - __u32 number_of_snapshots_returned; - __u32 snapshot_array_size; - /* snapshots[]; */ -} __packed; - -/* query_info flags */ -#define PASSTHRU_QUERY_INFO 0x00000000 -#define PASSTHRU_FSCTL 0x00000001 -#define PASSTHRU_SET_INFO 0x00000002 -struct smb_query_info { - __u32 info_type; - __u32 file_info_class; - __u32 additional_information; - __u32 flags; - __u32 input_buffer_length; - __u32 output_buffer_length; - /* char buffer[]; */ -} __packed; - -/* - * Dumping the commonly used 16 byte (e.g. CCM and GCM128) keys still supported - * for backlevel compatibility, but is not sufficient for dumping the less - * frequently used GCM256 (32 byte) keys (see the newer "CIFS_DUMP_FULL_KEY" - * ioctl for dumping decryption info for GCM256 mounts) - */ -struct smb3_key_debug_info { - __u64 Suid; - __u16 cipher_type; - __u8 auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */ - __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; - __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; -} __packed; - -/* - * Dump variable-sized keys - */ -struct smb3_full_key_debug_info { - /* INPUT: size of userspace buffer */ - __u32 in_size; - - /* - * INPUT: 0 for current user, otherwise session to dump - * OUTPUT: session id that was dumped - */ - __u64 session_id; - __u16 cipher_type; - __u8 session_key_length; - __u8 server_in_key_length; - __u8 server_out_key_length; - __u8 data[]; - /* - * return this struct with the keys appended at the end: - * __u8 session_key[session_key_length]; - * __u8 server_in_key[server_in_key_length]; - * __u8 server_out_key[server_out_key_length]; - */ -} __packed; - -struct smb3_notify { - __u32 completion_filter; - bool watch_tree; -} __packed; - -struct smb3_notify_info { - __u32 completion_filter; - bool watch_tree; - __u32 data_len; /* size of notify data below */ - __u8 notify_data[]; -} __packed; - -#define CIFS_IOCTL_MAGIC 0xCF -#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) -#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) -#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) -#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) -#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info) -#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info) -#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify) -#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info) -#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info) -#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32) - -/* - * Flags for going down operation - */ -#define CIFS_GOING_FLAGS_DEFAULT 0x0 /* going down */ -#define CIFS_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ -#define CIFS_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ - -static inline bool cifs_forced_shutdown(struct cifs_sb_info *sbi) -{ - if (CIFS_MOUNT_SHUTDOWN & sbi->mnt_cifs_flags) - return true; - else - return false; -} diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c deleted file mode 100644 index 342717bf1dc2..000000000000 --- a/fs/cifs/cifs_spnego.c +++ /dev/null @@ -1,236 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * SPNEGO upcall management for CIFS - * - * Copyright (c) 2007 Red Hat, Inc. - * Author(s): Jeff Layton (jlayton@redhat.com) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifs_spnego.h" -#include "cifs_debug.h" -#include "cifsproto.h" -static const struct cred *spnego_cred; - -/* create a new cifs key */ -static int -cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep) -{ - char *payload; - int ret; - - ret = -ENOMEM; - payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); - if (!payload) - goto error; - - /* attach the data */ - key->payload.data[0] = payload; - ret = 0; - -error: - return ret; -} - -static void -cifs_spnego_key_destroy(struct key *key) -{ - kfree(key->payload.data[0]); -} - - -/* - * keytype for CIFS spnego keys - */ -struct key_type cifs_spnego_key_type = { - .name = "cifs.spnego", - .instantiate = cifs_spnego_key_instantiate, - .destroy = cifs_spnego_key_destroy, - .describe = user_describe, -}; - -/* length of longest version string e.g. strlen("ver=0xFF") */ -#define MAX_VER_STR_LEN 8 - -/* length of longest security mechanism name, eg in future could have - * strlen(";sec=ntlmsspi") */ -#define MAX_MECH_STR_LEN 13 - -/* strlen of "host=" */ -#define HOST_KEY_LEN 5 - -/* strlen of ";ip4=" or ";ip6=" */ -#define IP_KEY_LEN 5 - -/* strlen of ";uid=0x" */ -#define UID_KEY_LEN 7 - -/* strlen of ";creduid=0x" */ -#define CREDUID_KEY_LEN 11 - -/* strlen of ";user=" */ -#define USER_KEY_LEN 6 - -/* strlen of ";pid=0x" */ -#define PID_KEY_LEN 7 - -/* get a key struct with a SPNEGO security blob, suitable for session setup */ -struct key * -cifs_get_spnego_key(struct cifs_ses *sesInfo, - struct TCP_Server_Info *server) -{ - struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; - char *description, *dp; - size_t desc_len; - struct key *spnego_key; - const char *hostname = server->hostname; - const struct cred *saved_cred; - - /* length of fields (with semicolons): ver=0xyz ip4=ipaddress - host=hostname sec=mechanism uid=0xFF user=username */ - desc_len = MAX_VER_STR_LEN + - HOST_KEY_LEN + strlen(hostname) + - IP_KEY_LEN + INET6_ADDRSTRLEN + - MAX_MECH_STR_LEN + - UID_KEY_LEN + (sizeof(uid_t) * 2) + - CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + - PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; - - if (sesInfo->user_name) - desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); - - spnego_key = ERR_PTR(-ENOMEM); - description = kzalloc(desc_len, GFP_KERNEL); - if (description == NULL) - goto out; - - dp = description; - /* start with version and hostname portion of UNC string */ - spnego_key = ERR_PTR(-EINVAL); - sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, - hostname); - dp = description + strlen(description); - - /* add the server address */ - if (server->dstaddr.ss_family == AF_INET) - sprintf(dp, "ip4=%pI4", &sa->sin_addr); - else if (server->dstaddr.ss_family == AF_INET6) - sprintf(dp, "ip6=%pI6", &sa6->sin6_addr); - else - goto out; - - dp = description + strlen(description); - - /* for now, only sec=krb5 and sec=mskrb5 are valid */ - if (server->sec_kerberos) - sprintf(dp, ";sec=krb5"); - else if (server->sec_mskerberos) - sprintf(dp, ";sec=mskrb5"); - else { - cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n"); - sprintf(dp, ";sec=krb5"); - } - - dp = description + strlen(description); - sprintf(dp, ";uid=0x%x", - from_kuid_munged(&init_user_ns, sesInfo->linux_uid)); - - dp = description + strlen(description); - sprintf(dp, ";creduid=0x%x", - from_kuid_munged(&init_user_ns, sesInfo->cred_uid)); - - if (sesInfo->user_name) { - dp = description + strlen(description); - sprintf(dp, ";user=%s", sesInfo->user_name); - } - - dp = description + strlen(description); - sprintf(dp, ";pid=0x%x", current->pid); - - cifs_dbg(FYI, "key description = %s\n", description); - saved_cred = override_creds(spnego_cred); - spnego_key = request_key(&cifs_spnego_key_type, description, ""); - revert_creds(saved_cred); - -#ifdef CONFIG_CIFS_DEBUG2 - if (cifsFYI && !IS_ERR(spnego_key)) { - struct cifs_spnego_msg *msg = spnego_key->payload.data[0]; - cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U, - msg->secblob_len + msg->sesskey_len)); - } -#endif /* CONFIG_CIFS_DEBUG2 */ - -out: - kfree(description); - return spnego_key; -} - -int -init_cifs_spnego(void) -{ - struct cred *cred; - struct key *keyring; - int ret; - - cifs_dbg(FYI, "Registering the %s key type\n", - cifs_spnego_key_type.name); - - /* - * Create an override credential set with special thread keyring for - * spnego upcalls. - */ - - cred = prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; - - keyring = keyring_alloc(".cifs_spnego", - GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) { - ret = PTR_ERR(keyring); - goto failed_put_cred; - } - - ret = register_key_type(&cifs_spnego_key_type); - if (ret < 0) - goto failed_put_key; - - /* - * instruct request_key() to use this special keyring as a cache for - * the results it looks up - */ - set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); - cred->thread_keyring = keyring; - cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; - spnego_cred = cred; - - cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring)); - return 0; - -failed_put_key: - key_put(keyring); -failed_put_cred: - put_cred(cred); - return ret; -} - -void -exit_cifs_spnego(void) -{ - key_revoke(spnego_cred->thread_keyring); - unregister_key_type(&cifs_spnego_key_type); - put_cred(spnego_cred); - cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name); -} diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h deleted file mode 100644 index 7f102ffeb675..000000000000 --- a/fs/cifs/cifs_spnego.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * SPNEGO upcall management for CIFS - * - * Copyright (c) 2007 Red Hat, Inc. - * Author(s): Jeff Layton (jlayton@redhat.com) - * Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFS_SPNEGO_H -#define _CIFS_SPNEGO_H - -#define CIFS_SPNEGO_UPCALL_VERSION 2 - -/* - * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION. - * The flags field is for future use. The request-key callout should set - * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob - * and stuff it in the data field. - */ -struct cifs_spnego_msg { - uint32_t version; - uint32_t flags; - uint32_t sesskey_len; - uint32_t secblob_len; - uint8_t data[1]; -}; - -#ifdef __KERNEL__ -extern struct key_type cifs_spnego_key_type; -extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo, - struct TCP_Server_Info *server); -#endif /* KERNEL */ - -#endif /* _CIFS_SPNEGO_H */ diff --git a/fs/cifs/cifs_spnego_negtokeninit.asn1 b/fs/cifs/cifs_spnego_negtokeninit.asn1 deleted file mode 100644 index 181c083887d5..000000000000 --- a/fs/cifs/cifs_spnego_negtokeninit.asn1 +++ /dev/null @@ -1,40 +0,0 @@ -GSSAPI ::= - [APPLICATION 0] IMPLICIT SEQUENCE { - thisMech - OBJECT IDENTIFIER ({cifs_gssapi_this_mech}), - negotiationToken - NegotiationToken - } - -MechType ::= OBJECT IDENTIFIER ({cifs_neg_token_init_mech_type}) - -MechTypeList ::= SEQUENCE OF MechType - -NegHints ::= SEQUENCE { - hintName - [0] GeneralString OPTIONAL, - hintAddress - [1] OCTET STRING OPTIONAL - } - -NegTokenInit2 ::= - SEQUENCE { - mechTypes - [0] MechTypeList OPTIONAL, - reqFlags - [1] BIT STRING OPTIONAL, - mechToken - [2] OCTET STRING OPTIONAL, - negHints - [3] NegHints OPTIONAL, - mechListMIC - [3] OCTET STRING OPTIONAL - } - -NegotiationToken ::= - CHOICE { - negTokenInit - [0] NegTokenInit2, - negTokenTarg - [1] ANY - } diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c deleted file mode 100644 index 7233c6a7e6d7..000000000000 --- a/fs/cifs/cifs_swn.c +++ /dev/null @@ -1,674 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Witness Service client for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#include -#include -#include - -#include "cifs_swn.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "fscache.h" -#include "cifs_debug.h" -#include "netlink.h" - -static DEFINE_IDR(cifs_swnreg_idr); -static DEFINE_MUTEX(cifs_swnreg_idr_mutex); - -struct cifs_swn_reg { - int id; - struct kref ref_count; - - const char *net_name; - const char *share_name; - bool net_name_notify; - bool share_name_notify; - bool ip_notify; - - struct cifs_tcon *tcon; -}; - -static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) -{ - int ret; - - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH); - if (ret < 0) - return ret; - - return 0; -} - -static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) -{ - int ret; - - if (tcon->ses->user_name != NULL) { - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name); - if (ret < 0) - return ret; - } - - if (tcon->ses->password != NULL) { - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password); - if (ret < 0) - return ret; - } - - if (tcon->ses->domainName != NULL) { - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - * Sends a register message to the userspace daemon based on the registration. - * The authentication information to connect to the witness service is bundled - * into the message. - */ -static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) -{ - struct sk_buff *skb; - struct genlmsghdr *hdr; - enum securityEnum authtype; - struct sockaddr_storage *addr; - int ret; - - skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (skb == NULL) { - ret = -ENOMEM; - goto fail; - } - - hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER); - if (hdr == NULL) { - ret = -ENOMEM; - goto nlmsg_fail; - } - - ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); - if (ret < 0) - goto nlmsg_fail; - - /* - * If there is an address stored use it instead of the server address, because we are - * in the process of reconnecting to it after a share has been moved or we have been - * told to switch to it (client move message). In these cases we unregister from the - * server address and register to the new address when we receive the notification. - */ - if (swnreg->tcon->ses->server->use_swn_dstaddr) - addr = &swnreg->tcon->ses->server->swn_dstaddr; - else - addr = &swnreg->tcon->ses->server->dstaddr; - - ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); - if (ret < 0) - goto nlmsg_fail; - - if (swnreg->net_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->share_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->ip_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); - switch (authtype) { - case Kerberos: - ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); - goto nlmsg_fail; - } - break; - case NTLMv2: - case RawNTLMSSP: - ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); - goto nlmsg_fail; - } - break; - default: - cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype); - ret = -EINVAL; - goto nlmsg_fail; - } - - genlmsg_end(skb, hdr); - genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); - - cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__, - swnreg->net_name, swnreg->id); - - return 0; - -nlmsg_fail: - genlmsg_cancel(skb, hdr); - nlmsg_free(skb); -fail: - return ret; -} - -/* - * Sends an uregister message to the userspace daemon based on the registration - */ -static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) -{ - struct sk_buff *skb; - struct genlmsghdr *hdr; - int ret; - - skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (skb == NULL) - return -ENOMEM; - - hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER); - if (hdr == NULL) { - ret = -ENOMEM; - goto nlmsg_fail; - } - - ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), - &swnreg->tcon->ses->server->dstaddr); - if (ret < 0) - goto nlmsg_fail; - - if (swnreg->net_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->share_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->ip_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - genlmsg_end(skb, hdr); - genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); - - cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__, - swnreg->net_name, swnreg->id); - - return 0; - -nlmsg_fail: - genlmsg_cancel(skb, hdr); - nlmsg_free(skb); - return ret; -} - -/* - * Try to find a matching registration for the tcon's server name and share name. - * Calls to this function must be protected by cifs_swnreg_idr_mutex. - * TODO Try to avoid memory allocations - */ -static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *swnreg; - int id; - const char *share_name; - const char *net_name; - - net_name = extract_hostname(tcon->tree_name); - if (IS_ERR(net_name)) { - int ret; - - ret = PTR_ERR(net_name); - cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n", - __func__, tcon->tree_name, ret); - return ERR_PTR(-EINVAL); - } - - share_name = extract_sharename(tcon->tree_name); - if (IS_ERR(share_name)) { - int ret; - - ret = PTR_ERR(share_name); - cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n", - __func__, tcon->tree_name, ret); - kfree(net_name); - return ERR_PTR(-EINVAL); - } - - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - if (strcasecmp(swnreg->net_name, net_name) != 0 - || strcasecmp(swnreg->share_name, share_name) != 0) { - continue; - } - - cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name, - swnreg->share_name); - - kfree(net_name); - kfree(share_name); - - return swnreg; - } - - kfree(net_name); - kfree(share_name); - - return ERR_PTR(-EEXIST); -} - -/* - * Get a registration for the tcon's server and share name, allocating a new one if it does not - * exists - */ -static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *reg = NULL; - int ret; - - mutex_lock(&cifs_swnreg_idr_mutex); - - /* Check if we are already registered for this network and share names */ - reg = cifs_find_swn_reg(tcon); - if (!IS_ERR(reg)) { - kref_get(®->ref_count); - mutex_unlock(&cifs_swnreg_idr_mutex); - return reg; - } else if (PTR_ERR(reg) != -EEXIST) { - mutex_unlock(&cifs_swnreg_idr_mutex); - return reg; - } - - reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC); - if (reg == NULL) { - mutex_unlock(&cifs_swnreg_idr_mutex); - return ERR_PTR(-ENOMEM); - } - - kref_init(®->ref_count); - - reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); - if (reg->id < 0) { - cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); - ret = reg->id; - goto fail; - } - - reg->net_name = extract_hostname(tcon->tree_name); - if (IS_ERR(reg->net_name)) { - ret = PTR_ERR(reg->net_name); - cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret); - goto fail_idr; - } - - reg->share_name = extract_sharename(tcon->tree_name); - if (IS_ERR(reg->share_name)) { - ret = PTR_ERR(reg->share_name); - cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret); - goto fail_net_name; - } - - reg->net_name_notify = true; - reg->share_name_notify = true; - reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); - - reg->tcon = tcon; - - mutex_unlock(&cifs_swnreg_idr_mutex); - - return reg; - -fail_net_name: - kfree(reg->net_name); -fail_idr: - idr_remove(&cifs_swnreg_idr, reg->id); -fail: - kfree(reg); - mutex_unlock(&cifs_swnreg_idr_mutex); - return ERR_PTR(ret); -} - -static void cifs_swn_reg_release(struct kref *ref) -{ - struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); - int ret; - - ret = cifs_swn_send_unregister_message(swnreg); - if (ret < 0) - cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); - - idr_remove(&cifs_swnreg_idr, swnreg->id); - kfree(swnreg->net_name); - kfree(swnreg->share_name); - kfree(swnreg); -} - -static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) -{ - mutex_lock(&cifs_swnreg_idr_mutex); - kref_put(&swnreg->ref_count, cifs_swn_reg_release); - mutex_unlock(&cifs_swnreg_idr_mutex); -} - -static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) -{ - switch (state) { - case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: - cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); - break; - case CIFS_SWN_RESOURCE_STATE_AVAILABLE: - cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); - break; - case CIFS_SWN_RESOURCE_STATE_UNKNOWN: - cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name); - break; - } - return 0; -} - -static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2) -{ - if (addr1->ss_family != addr2->ss_family) - return false; - - if (addr1->ss_family == AF_INET) { - return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr, - &((const struct sockaddr_in *)addr2)->sin_addr, - sizeof(struct in_addr)) == 0); - } - - if (addr1->ss_family == AF_INET6) { - return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr, - &((const struct sockaddr_in6 *)addr2)->sin6_addr, - sizeof(struct in6_addr)) == 0); - } - - return false; -} - -static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new, - const struct sockaddr_storage *old, - struct sockaddr_storage *dst) -{ - __be16 port = cpu_to_be16(CIFS_PORT); - - if (old->ss_family == AF_INET) { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)old; - - port = ipv4->sin_port; - } else if (old->ss_family == AF_INET6) { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old; - - port = ipv6->sin6_port; - } - - if (new->ss_family == AF_INET) { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)new; - - ipv4->sin_port = port; - } else if (new->ss_family == AF_INET6) { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new; - - ipv6->sin6_port = port; - } - - *dst = *new; - - return 0; -} - -static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr) -{ - int ret = 0; - - /* Store the reconnect address */ - cifs_server_lock(tcon->ses->server); - if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) - goto unlock; - - ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr, - &tcon->ses->server->swn_dstaddr); - if (ret < 0) { - cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret); - goto unlock; - } - tcon->ses->server->use_swn_dstaddr = true; - - /* - * Unregister to stop receiving notifications for the old IP address. - */ - ret = cifs_swn_unregister(tcon); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", - __func__, ret); - goto unlock; - } - - /* - * And register to receive notifications for the new IP address now that we have - * stored the new address. - */ - ret = cifs_swn_register(tcon); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n", - __func__, ret); - goto unlock; - } - - cifs_signal_cifsd_for_reconnect(tcon->ses->server, false); - -unlock: - cifs_server_unlock(tcon->ses->server); - - return ret; -} - -static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) -{ - struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; - - if (addr->ss_family == AF_INET) - cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr); - else if (addr->ss_family == AF_INET6) - cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); - - return cifs_swn_reconnect(swnreg->tcon, addr); -} - -int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) -{ - struct cifs_swn_reg *swnreg; - char name[256]; - int type; - - if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { - int swnreg_id; - - swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); - mutex_lock(&cifs_swnreg_idr_mutex); - swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); - mutex_unlock(&cifs_swnreg_idr_mutex); - if (swnreg == NULL) { - cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); - return -EINVAL; - } - } else { - cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); - return -EINVAL; - } - - if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { - type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); - } else { - cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); - return -EINVAL; - } - - switch (type) { - case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: { - int state; - - if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) { - nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME], - sizeof(name)); - } else { - cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); - return -EINVAL; - } - if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { - state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); - } else { - cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); - return -EINVAL; - } - return cifs_swn_resource_state_changed(swnreg, name, state); - } - case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { - struct sockaddr_storage addr; - - if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) { - nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); - } else { - cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); - return -EINVAL; - } - return cifs_swn_client_move(swnreg, &addr); - } - default: - cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); - break; - } - - return 0; -} - -int cifs_swn_register(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *swnreg; - int ret; - - swnreg = cifs_get_swn_reg(tcon); - if (IS_ERR(swnreg)) - return PTR_ERR(swnreg); - - ret = cifs_swn_send_register_message(swnreg); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); - /* Do not put the swnreg or return error, the echo task will retry */ - } - - return 0; -} - -int cifs_swn_unregister(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *swnreg; - - mutex_lock(&cifs_swnreg_idr_mutex); - - swnreg = cifs_find_swn_reg(tcon); - if (IS_ERR(swnreg)) { - mutex_unlock(&cifs_swnreg_idr_mutex); - return PTR_ERR(swnreg); - } - - mutex_unlock(&cifs_swnreg_idr_mutex); - - cifs_put_swn_reg(swnreg); - - return 0; -} - -void cifs_swn_dump(struct seq_file *m) -{ - struct cifs_swn_reg *swnreg; - struct sockaddr_in *sa; - struct sockaddr_in6 *sa6; - int id; - - seq_puts(m, "Witness registrations:"); - - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", - id, kref_read(&swnreg->ref_count), - swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", - swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); - switch (swnreg->tcon->ses->server->dstaddr.ss_family) { - case AF_INET: - sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; - seq_printf(m, "%pI4", &sa->sin_addr.s_addr); - break; - case AF_INET6: - sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; - seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); - if (sa6->sin6_scope_id) - seq_printf(m, "%%%u", sa6->sin6_scope_id); - break; - default: - seq_puts(m, "(unknown)"); - } - seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); - } - mutex_unlock(&cifs_swnreg_idr_mutex); - seq_puts(m, "\n"); -} - -void cifs_swn_check(void) -{ - struct cifs_swn_reg *swnreg; - int id; - int ret; - - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - ret = cifs_swn_send_register_message(swnreg); - if (ret < 0) - cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); - } - mutex_unlock(&cifs_swnreg_idr_mutex); -} diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h deleted file mode 100644 index 8a9d2a5c9077..000000000000 --- a/fs/cifs/cifs_swn.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Witness Service client for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#ifndef _CIFS_SWN_H -#define _CIFS_SWN_H -#include "cifsglob.h" - -struct cifs_tcon; -struct sk_buff; -struct genl_info; - -#ifdef CONFIG_CIFS_SWN_UPCALL -extern int cifs_swn_register(struct cifs_tcon *tcon); - -extern int cifs_swn_unregister(struct cifs_tcon *tcon); - -extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info); - -extern void cifs_swn_dump(struct seq_file *m); - -extern void cifs_swn_check(void); - -static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) -{ - if (server->use_swn_dstaddr) { - server->dstaddr = server->swn_dstaddr; - return true; - } - return false; -} - -static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) -{ - server->use_swn_dstaddr = false; -} - -#else - -static inline int cifs_swn_register(struct cifs_tcon *tcon) { return 0; } -static inline int cifs_swn_unregister(struct cifs_tcon *tcon) { return 0; } -static inline int cifs_swn_notify(struct sk_buff *s, struct genl_info *i) { return 0; } -static inline void cifs_swn_dump(struct seq_file *m) {} -static inline void cifs_swn_check(void) {} -static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) { return false; } -static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) {} - -#endif /* CONFIG_CIFS_SWN_UPCALL */ -#endif /* _CIFS_SWN_H */ diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c deleted file mode 100644 index e7582dd79179..000000000000 --- a/fs/cifs/cifs_unicode.c +++ /dev/null @@ -1,632 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * Modified by Steve French (sfrench@us.ibm.com) - */ -#include -#include -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "cifs_uniupr.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifs_debug.h" - -int cifs_remap(struct cifs_sb_info *cifs_sb) -{ - int map_type; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) - map_type = SFM_MAP_UNI_RSVD; - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) - map_type = SFU_MAP_UNI_RSVD; - else - map_type = NO_MAP_UNI_RSVD; - - return map_type; -} - -/* Convert character using the SFU - "Services for Unix" remapping range */ -static bool -convert_sfu_char(const __u16 src_char, char *target) -{ - /* - * BB: Cannot handle remapping UNI_SLASH until all the calls to - * build_path_from_dentry are modified, as they use slash as - * separator. - */ - switch (src_char) { - case UNI_COLON: - *target = ':'; - break; - case UNI_ASTERISK: - *target = '*'; - break; - case UNI_QUESTION: - *target = '?'; - break; - case UNI_PIPE: - *target = '|'; - break; - case UNI_GRTRTHAN: - *target = '>'; - break; - case UNI_LESSTHAN: - *target = '<'; - break; - default: - return false; - } - return true; -} - -/* Convert character using the SFM - "Services for Mac" remapping range */ -static bool -convert_sfm_char(const __u16 src_char, char *target) -{ - if (src_char >= 0xF001 && src_char <= 0xF01F) { - *target = src_char - 0xF000; - return true; - } - switch (src_char) { - case SFM_COLON: - *target = ':'; - break; - case SFM_DOUBLEQUOTE: - *target = '"'; - break; - case SFM_ASTERISK: - *target = '*'; - break; - case SFM_QUESTION: - *target = '?'; - break; - case SFM_PIPE: - *target = '|'; - break; - case SFM_GRTRTHAN: - *target = '>'; - break; - case SFM_LESSTHAN: - *target = '<'; - break; - case SFM_SPACE: - *target = ' '; - break; - case SFM_PERIOD: - *target = '.'; - break; - default: - return false; - } - return true; -} - - -/* - * cifs_mapchar - convert a host-endian char to proper char in codepage - * @target - where converted character should be copied - * @src_char - 2 byte host-endian source character - * @cp - codepage to which character should be converted - * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2? - * - * This function handles the conversion of a single character. It is the - * responsibility of the caller to ensure that the target buffer is large - * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). - */ -static int -cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp, - int maptype) -{ - int len = 1; - __u16 src_char; - - src_char = *from; - - if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target)) - return len; - else if ((maptype == SFU_MAP_UNI_RSVD) && - convert_sfu_char(src_char, target)) - return len; - - /* if character not one of seven in special remap set */ - len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); - if (len <= 0) - goto surrogate_pair; - - return len; - -surrogate_pair: - /* convert SURROGATE_PAIR and IVS */ - if (strcmp(cp->charset, "utf8")) - goto unknown; - len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6); - if (len <= 0) - goto unknown; - return len; - -unknown: - *target = '?'; - len = 1; - return len; -} - -/* - * cifs_from_utf16 - convert utf16le string to local charset - * @to - destination buffer - * @from - source buffer - * @tolen - destination buffer size (in bytes) - * @fromlen - source buffer size (in bytes) - * @codepage - codepage to which characters should be converted - * @mapchar - should characters be remapped according to the mapchars option? - * - * Convert a little-endian utf16le string (as sent by the server) to a string - * in the provided codepage. The tolen and fromlen parameters are to ensure - * that the code doesn't walk off of the end of the buffer (which is always - * a danger if the alignment of the source buffer is off). The destination - * string is always properly null terminated and fits in the destination - * buffer. Returns the length of the destination string in bytes (including - * null terminator). - * - * Note that some windows versions actually send multiword UTF-16 characters - * instead of straight UTF16-2. The linux nls routines however aren't able to - * deal with those characters properly. In the event that we get some of - * those characters, they won't be translated properly. - */ -int -cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *codepage, int map_type) -{ - int i, charlen, safelen; - int outlen = 0; - int nullsize = nls_nullsize(codepage); - int fromwords = fromlen / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */ - - /* - * because the chars can be of varying widths, we need to take care - * not to overflow the destination buffer when we get close to the - * end of it. Until we get to this offset, we don't need to check - * for overflow however. - */ - safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); - - for (i = 0; i < fromwords; i++) { - ftmp[0] = get_unaligned_le16(&from[i]); - if (ftmp[0] == 0) - break; - if (i + 1 < fromwords) - ftmp[1] = get_unaligned_le16(&from[i + 1]); - else - ftmp[1] = 0; - if (i + 2 < fromwords) - ftmp[2] = get_unaligned_le16(&from[i + 2]); - else - ftmp[2] = 0; - - /* - * check to see if converting this character might make the - * conversion bleed into the null terminator - */ - if (outlen >= safelen) { - charlen = cifs_mapchar(tmp, ftmp, codepage, map_type); - if ((outlen + charlen) > (tolen - nullsize)) - break; - } - - /* put converted char into 'to' buffer */ - charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type); - outlen += charlen; - - /* charlen (=bytes of UTF-8 for 1 character) - * 4bytes UTF-8(surrogate pair) is charlen=4 - * (4bytes UTF-16 code) - * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4 - * (2 UTF-8 pairs divided to 2 UTF-16 pairs) */ - if (charlen == 4) - i++; - else if (charlen >= 5) - /* 5-6bytes UTF-8 */ - i += 2; - } - - /* properly null-terminate string */ - for (i = 0; i < nullsize; i++) - to[outlen++] = 0; - - return outlen; -} - -/* - * NAME: cifs_strtoUTF16() - * - * FUNCTION: Convert character string to unicode string - * - */ -int -cifs_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage) -{ - int charlen; - int i; - wchar_t wchar_to; /* needed to quiet sparse */ - - /* special case for utf8 to handle no plane0 chars */ - if (!strcmp(codepage->charset, "utf8")) { - /* - * convert utf8 -> utf16, we assume we have enough space - * as caller should have assumed conversion does not overflow - * in destination len is length in wchar_t units (16bits) - */ - i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, - (wchar_t *) to, len); - - /* if success terminate and exit */ - if (i >= 0) - goto success; - /* - * if fails fall back to UCS encoding as this - * function should not return negative values - * currently can fail only if source contains - * invalid encoded characters - */ - } - - for (i = 0; len && *from; i++, from += charlen, len -= charlen) { - charlen = codepage->char2uni(from, len, &wchar_to); - if (charlen < 1) { - cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n", - *from, charlen); - /* A question mark */ - wchar_to = 0x003f; - charlen = 1; - } - put_unaligned_le16(wchar_to, &to[i]); - } - -success: - put_unaligned_le16(0, &to[i]); - return i; -} - -/* - * cifs_utf16_bytes - how long will a string be after conversion? - * @utf16 - pointer to input string - * @maxbytes - don't go past this many bytes of input string - * @codepage - destination codepage - * - * Walk a utf16le string and return the number of bytes that the string will - * be after being converted to the given charset, not including any null - * termination required. Don't walk past maxbytes in the source buffer. - */ -int -cifs_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage) -{ - int i; - int charlen, outlen = 0; - int maxwords = maxbytes / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp[3]; - - for (i = 0; i < maxwords; i++) { - ftmp[0] = get_unaligned_le16(&from[i]); - if (ftmp[0] == 0) - break; - if (i + 1 < maxwords) - ftmp[1] = get_unaligned_le16(&from[i + 1]); - else - ftmp[1] = 0; - if (i + 2 < maxwords) - ftmp[2] = get_unaligned_le16(&from[i + 2]); - else - ftmp[2] = 0; - - charlen = cifs_mapchar(tmp, ftmp, codepage, NO_MAP_UNI_RSVD); - outlen += charlen; - } - - return outlen; -} - -/* - * cifs_strndup_from_utf16 - copy a string from wire format to the local - * codepage - * @src - source string - * @maxlen - don't walk past this many bytes in the source string - * @is_unicode - is this a unicode string? - * @codepage - destination codepage - * - * Take a string given by the server, convert it to the local codepage and - * put it in a new buffer. Returns a pointer to the new string or NULL on - * error. - */ -char * -cifs_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *codepage) -{ - int len; - char *dst; - - if (is_unicode) { - len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage); - len += nls_nullsize(codepage); - dst = kmalloc(len, GFP_KERNEL); - if (!dst) - return NULL; - cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, - NO_MAP_UNI_RSVD); - } else { - dst = kstrndup(src, maxlen, GFP_KERNEL); - } - - return dst; -} - -static __le16 convert_to_sfu_char(char src_char) -{ - __le16 dest_char; - - switch (src_char) { - case ':': - dest_char = cpu_to_le16(UNI_COLON); - break; - case '*': - dest_char = cpu_to_le16(UNI_ASTERISK); - break; - case '?': - dest_char = cpu_to_le16(UNI_QUESTION); - break; - case '<': - dest_char = cpu_to_le16(UNI_LESSTHAN); - break; - case '>': - dest_char = cpu_to_le16(UNI_GRTRTHAN); - break; - case '|': - dest_char = cpu_to_le16(UNI_PIPE); - break; - default: - dest_char = 0; - } - - return dest_char; -} - -static __le16 convert_to_sfm_char(char src_char, bool end_of_string) -{ - __le16 dest_char; - - if (src_char >= 0x01 && src_char <= 0x1F) { - dest_char = cpu_to_le16(src_char + 0xF000); - return dest_char; - } - switch (src_char) { - case ':': - dest_char = cpu_to_le16(SFM_COLON); - break; - case '"': - dest_char = cpu_to_le16(SFM_DOUBLEQUOTE); - break; - case '*': - dest_char = cpu_to_le16(SFM_ASTERISK); - break; - case '?': - dest_char = cpu_to_le16(SFM_QUESTION); - break; - case '<': - dest_char = cpu_to_le16(SFM_LESSTHAN); - break; - case '>': - dest_char = cpu_to_le16(SFM_GRTRTHAN); - break; - case '|': - dest_char = cpu_to_le16(SFM_PIPE); - break; - case '.': - if (end_of_string) - dest_char = cpu_to_le16(SFM_PERIOD); - else - dest_char = 0; - break; - case ' ': - if (end_of_string) - dest_char = cpu_to_le16(SFM_SPACE); - else - dest_char = 0; - break; - default: - dest_char = 0; - } - - return dest_char; -} - -/* - * Convert 16 bit Unicode pathname to wire format from string in current code - * page. Conversion may involve remapping up the six characters that are - * only legal in POSIX-like OS (if they are present in the string). Path - * names are little endian 16 bit Unicode on the wire - */ -int -cifsConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int map_chars) -{ - int i, charlen; - int j = 0; - char src_char; - __le16 dst_char; - wchar_t tmp; - wchar_t *wchar_to; /* UTF-16 */ - int ret; - unicode_t u; - - if (map_chars == NO_MAP_UNI_RSVD) - return cifs_strtoUTF16(target, source, PATH_MAX, cp); - - wchar_to = kzalloc(6, GFP_KERNEL); - - for (i = 0; i < srclen; j++) { - src_char = source[i]; - charlen = 1; - - /* check if end of string */ - if (src_char == 0) - goto ctoUTF16_out; - - /* see if we must remap this char */ - if (map_chars == SFU_MAP_UNI_RSVD) - dst_char = convert_to_sfu_char(src_char); - else if (map_chars == SFM_MAP_UNI_RSVD) { - bool end_of_string; - - /** - * Remap spaces and periods found at the end of every - * component of the path. The special cases of '.' and - * '..' do not need to be dealt with explicitly because - * they are addressed in namei.c:link_path_walk(). - **/ - if ((i == srclen - 1) || (source[i+1] == '\\')) - end_of_string = true; - else - end_of_string = false; - - dst_char = convert_to_sfm_char(src_char, end_of_string); - } else - dst_char = 0; - /* - * FIXME: We can not handle remapping backslash (UNI_SLASH) - * until all the calls to build_path_from_dentry are modified, - * as they use backslash as separator. - */ - if (dst_char == 0) { - charlen = cp->char2uni(source + i, srclen - i, &tmp); - dst_char = cpu_to_le16(tmp); - - /* - * if no match, use question mark, which at least in - * some cases serves as wild card - */ - if (charlen > 0) - goto ctoUTF16; - - /* convert SURROGATE_PAIR */ - if (strcmp(cp->charset, "utf8") || !wchar_to) - goto unknown; - if (*(source + i) & 0x80) { - charlen = utf8_to_utf32(source + i, 6, &u); - if (charlen < 0) - goto unknown; - } else - goto unknown; - ret = utf8s_to_utf16s(source + i, charlen, - UTF16_LITTLE_ENDIAN, - wchar_to, 6); - if (ret < 0) - goto unknown; - - i += charlen; - dst_char = cpu_to_le16(*wchar_to); - if (charlen <= 3) - /* 1-3bytes UTF-8 to 2bytes UTF-16 */ - put_unaligned(dst_char, &target[j]); - else if (charlen == 4) { - /* 4bytes UTF-8(surrogate pair) to 4bytes UTF-16 - * 7-8bytes UTF-8(IVS) divided to 2 UTF-16 - * (charlen=3+4 or 4+4) */ - put_unaligned(dst_char, &target[j]); - dst_char = cpu_to_le16(*(wchar_to + 1)); - j++; - put_unaligned(dst_char, &target[j]); - } else if (charlen >= 5) { - /* 5-6bytes UTF-8 to 6bytes UTF-16 */ - put_unaligned(dst_char, &target[j]); - dst_char = cpu_to_le16(*(wchar_to + 1)); - j++; - put_unaligned(dst_char, &target[j]); - dst_char = cpu_to_le16(*(wchar_to + 2)); - j++; - put_unaligned(dst_char, &target[j]); - } - continue; - -unknown: - dst_char = cpu_to_le16(0x003f); - charlen = 1; - } - -ctoUTF16: - /* - * character may take more than one byte in the source string, - * but will take exactly two bytes in the target string - */ - i += charlen; - put_unaligned(dst_char, &target[j]); - } - -ctoUTF16_out: - put_unaligned(0, &target[j]); /* Null terminate target unicode string */ - kfree(wchar_to); - return j; -} - -/* - * cifs_local_to_utf16_bytes - how long will a string be after conversion? - * @from - pointer to input string - * @maxbytes - don't go past this many bytes of input string - * @codepage - source codepage - * - * Walk a string and return the number of bytes that the string will - * be after being converted to the given charset, not including any null - * termination required. Don't walk past maxbytes in the source buffer. - */ - -static int -cifs_local_to_utf16_bytes(const char *from, int len, - const struct nls_table *codepage) -{ - int charlen; - int i; - wchar_t wchar_to; - - for (i = 0; len && *from; i++, from += charlen, len -= charlen) { - charlen = codepage->char2uni(from, len, &wchar_to); - /* Failed conversion defaults to a question mark */ - if (charlen < 1) - charlen = 1; - } - return 2 * i; /* UTF16 characters are two bytes */ -} - -/* - * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage - * @src - source string - * @maxlen - don't walk past this many bytes in the source string - * @utf16_len - the length of the allocated string in bytes (including null) - * @cp - source codepage - * @remap - map special chars - * - * Take a string convert it from the local codepage to UTF16 and - * put it in a new buffer. Returns a pointer to the new string or NULL on - * error. - */ -__le16 * -cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, - const struct nls_table *cp, int remap) -{ - int len; - __le16 *dst; - - len = cifs_local_to_utf16_bytes(src, maxlen, cp); - len += 2; /* NULL */ - dst = kmalloc(len, GFP_KERNEL); - if (!dst) { - *utf16_len = 0; - return NULL; - } - cifsConvertToUTF16(dst, src, strlen(src), cp, remap); - *utf16_len = len; - return dst; -} diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h deleted file mode 100644 index 80b3d845419f..000000000000 --- a/fs/cifs/cifs_unicode.h +++ /dev/null @@ -1,404 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * cifs_unicode: Unicode kernel case support - * - * Function: - * Convert a unicode character to upper or lower case using - * compressed tables. - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * - * Notes: - * These APIs are based on the C library functions. The semantics - * should match the C functions but with expanded size operands. - * - * The upper/lower functions are based on a table created by mkupr. - * This is a compressed table of upper and lower case conversion. - */ -#ifndef _CIFS_UNICODE_H -#define _CIFS_UNICODE_H - -#include -#include -#include - -#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ - -/* - * Windows maps these to the user defined 16 bit Unicode range since they are - * reserved symbols (along with \ and /), otherwise illegal to store - * in filenames in NTFS - */ -#define UNI_ASTERISK (__u16) ('*' + 0xF000) -#define UNI_QUESTION (__u16) ('?' + 0xF000) -#define UNI_COLON (__u16) (':' + 0xF000) -#define UNI_GRTRTHAN (__u16) ('>' + 0xF000) -#define UNI_LESSTHAN (__u16) ('<' + 0xF000) -#define UNI_PIPE (__u16) ('|' + 0xF000) -#define UNI_SLASH (__u16) ('\\' + 0xF000) - -/* - * Macs use an older "SFM" mapping of the symbols above. Fortunately it does - * not conflict (although almost does) with the mapping above. - */ - -#define SFM_DOUBLEQUOTE ((__u16) 0xF020) -#define SFM_ASTERISK ((__u16) 0xF021) -#define SFM_QUESTION ((__u16) 0xF025) -#define SFM_COLON ((__u16) 0xF022) -#define SFM_GRTRTHAN ((__u16) 0xF024) -#define SFM_LESSTHAN ((__u16) 0xF023) -#define SFM_PIPE ((__u16) 0xF027) -#define SFM_SLASH ((__u16) 0xF026) -#define SFM_SPACE ((__u16) 0xF028) -#define SFM_PERIOD ((__u16) 0xF029) - -/* - * Mapping mechanism to use when one of the seven reserved characters is - * encountered. We can only map using one of the mechanisms at a time - * since otherwise readdir could return directory entries which we would - * not be able to open - * - * NO_MAP_UNI_RSVD = do not perform any remapping of the character - * SFM_MAP_UNI_RSVD = map reserved characters using SFM scheme (MAC compatible) - * SFU_MAP_UNI_RSVD = map reserved characters ala SFU ("mapchars" option) - * - */ -#define NO_MAP_UNI_RSVD 0 -#define SFM_MAP_UNI_RSVD 1 -#define SFU_MAP_UNI_RSVD 2 - -/* Just define what we want from uniupr.h. We don't want to define the tables - * in each source file. - */ -#ifndef UNICASERANGE_DEFINED -struct UniCaseRange { - wchar_t start; - wchar_t end; - signed char *table; -}; -#endif /* UNICASERANGE_DEFINED */ - -#ifndef UNIUPR_NOUPPER -extern signed char CifsUniUpperTable[512]; -extern const struct UniCaseRange CifsUniUpperRange[]; -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -extern signed char CifsUniLowerTable[512]; -extern const struct UniCaseRange CifsUniLowerRange[]; -#endif /* UNIUPR_NOLOWER */ - -#ifdef __KERNEL__ -int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *cp, int map_type); -int cifs_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage); -int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); -char *cifs_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, - const struct nls_table *codepage); -extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, - const struct nls_table *cp, int mapChars); -extern int cifs_remap(struct cifs_sb_info *cifs_sb); -extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, - int *utf16_len, const struct nls_table *cp, - int remap); -#endif - -wchar_t cifs_toupper(wchar_t in); - -/* - * UniStrcat: Concatenate the second string to the first - * - * Returns: - * Address of the first string - */ -static inline __le16 * -UniStrcat(__le16 *ucs1, const __le16 *ucs2) -{ - __le16 *anchor = ucs1; /* save a pointer to start of ucs1 */ - - while (*ucs1++) ; /* To end of first string */ - ucs1--; /* Return to the null */ - while ((*ucs1++ = *ucs2++)) ; /* copy string 2 over */ - return anchor; -} - -/* - * UniStrchr: Find a character in a string - * - * Returns: - * Address of first occurrence of character in string - * or NULL if the character is not in the string - */ -static inline wchar_t * -UniStrchr(const wchar_t *ucs, wchar_t uc) -{ - while ((*ucs != uc) && *ucs) - ucs++; - - if (*ucs == uc) - return (wchar_t *) ucs; - return NULL; -} - -/* - * UniStrcmp: Compare two strings - * - * Returns: - * < 0: First string is less than second - * = 0: Strings are equal - * > 0: First string is greater than second - */ -static inline int -UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) -{ - while ((*ucs1 == *ucs2) && *ucs1) { - ucs1++; - ucs2++; - } - return (int) *ucs1 - (int) *ucs2; -} - -/* - * UniStrcpy: Copy a string - */ -static inline wchar_t * -UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save the start of result string */ - - while ((*ucs1++ = *ucs2++)) ; - return anchor; -} - -/* - * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) - */ -static inline size_t -UniStrlen(const wchar_t *ucs1) -{ - int i = 0; - - while (*ucs1++) - i++; - return i; -} - -/* - * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a - * string (length limited) - */ -static inline size_t -UniStrnlen(const wchar_t *ucs1, int maxlen) -{ - int i = 0; - - while (*ucs1++) { - i++; - if (i >= maxlen) - break; - } - return i; -} - -/* - * UniStrncat: Concatenate length limited string - */ -static inline wchar_t * -UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; /* save pointer to string 1 */ - - while (*ucs1++) ; - ucs1--; /* point to null terminator of s1 */ - while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ - ucs1++; - ucs2++; - } - *ucs1 = 0; /* Null terminate the result */ - return (anchor); -} - -/* - * UniStrncmp: Compare length limited string - */ -static inline int -UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == *ucs2) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int) *ucs1 - (int) *ucs2; -} - -/* - * UniStrncmp_le: Compare length limited string - native to little-endian - */ -static inline int -UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int) *ucs1 - (int) __le16_to_cpu(*ucs2); -} - -/* - * UniStrncpy: Copy length limited string with pad - */ -static inline wchar_t * -UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = *ucs2++; - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrncpy_le: Copy length limited string with pad to little-endian - */ -static inline wchar_t * -UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = __le16_to_cpu(*ucs2++); - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrstr: Find a string in a string - * - * Returns: - * Address of first match found - * NULL if no matching string is found - */ -static inline wchar_t * -UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) -{ - const wchar_t *anchor1 = ucs1; - const wchar_t *anchor2 = ucs2; - - while (*ucs1) { - if (*ucs1 == *ucs2) { - /* Partial match found */ - ucs1++; - ucs2++; - } else { - if (!*ucs2) /* Match found */ - return (wchar_t *) anchor1; - ucs1 = ++anchor1; /* No match */ - ucs2 = anchor2; - } - } - - if (!*ucs2) /* Both end together */ - return (wchar_t *) anchor1; /* Match found */ - return NULL; /* No match */ -} - -#ifndef UNIUPR_NOUPPER -/* - * UniToupper: Convert a unicode character to upper case - */ -static inline wchar_t -UniToupper(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(CifsUniUpperTable)) { - /* Latin characters */ - return uc + CifsUniUpperTable[uc]; /* Use base tables */ - } else { - rp = CifsUniUpperRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - } - return uc; /* Past last range */ -} - -/* - * UniStrupr: Upper case a unicode string - */ -static inline __le16 * -UniStrupr(register __le16 *upin) -{ - register __le16 *up; - - up = upin; - while (*up) { /* For all characters */ - *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); - up++; - } - return upin; /* Return input pointer */ -} -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -/* - * UniTolower: Convert a unicode character to lower case - */ -static inline wchar_t -UniTolower(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(CifsUniLowerTable)) { - /* Latin characters */ - return uc + CifsUniLowerTable[uc]; /* Use base tables */ - } else { - rp = CifsUniLowerRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - } - return uc; /* Past last range */ -} - -/* - * UniStrlwr: Lower case a unicode string - */ -static inline wchar_t * -UniStrlwr(register wchar_t *upin) -{ - register wchar_t *up; - - up = upin; - while (*up) { /* For all characters */ - *up = UniTolower(*up); - up++; - } - return upin; /* Return input pointer */ -} - -#endif - -#endif /* _CIFS_UNICODE_H */ diff --git a/fs/cifs/cifs_uniupr.h b/fs/cifs/cifs_uniupr.h deleted file mode 100644 index 7b272fcdf0d3..000000000000 --- a/fs/cifs/cifs_uniupr.h +++ /dev/null @@ -1,239 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) International Business Machines Corp., 2000,2002 - * - * uniupr.h - Unicode compressed case ranges -*/ - -#ifndef UNIUPR_NOUPPER -/* - * Latin upper case - */ -signed char CifsUniUpperTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 060-06f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ - -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ - 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ - 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ - 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ - -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ - 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ -}; - -/* Upper case range - Greek */ -static signed char UniCaseRangeU03a0[47] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 3b0-3bf */ - -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, - -63, -63, -}; - -/* Upper case range - Cyrillic */ -static signed char UniCaseRangeU0430[48] = { - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 430-43f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 440-44f */ - 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, 0, -80, -80, /* 450-45f */ -}; - -/* Upper case range - Extended cyrillic */ -static signed char UniCaseRangeU0490[61] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ - 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -}; - -/* Upper case range - Extended latin and greek */ -static signed char UniCaseRangeU1e00[509] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ - 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ - 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ - 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, 126, 126, 0, 0, /* 1f70-1f7f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ - 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ - 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ - 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Upper case range - Wide latin */ -static signed char UniCaseRangeUff40[27] = { - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* ff40-ff4f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -}; - -/* - * Upper Case Range - */ -const struct UniCaseRange CifsUniUpperRange[] = { - {0x03a0, 0x03ce, UniCaseRangeU03a0}, - {0x0430, 0x045f, UniCaseRangeU0430}, - {0x0490, 0x04cc, UniCaseRangeU0490}, - {0x1e00, 0x1ffc, UniCaseRangeU1e00}, - {0xff40, 0xff5a, UniCaseRangeUff40}, - {0} -}; -#endif - -#ifndef UNIUPR_NOLOWER -/* - * Latin lower case - */ -signed char CifsUniLowerTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 040-04f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, /* 050-05f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 0c0-0cf */ - 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 0, /* 0d0-0df */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ - 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ - 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, 0, /* 170-17f */ - 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, 0, /* 180-18f */ - 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ - 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ - 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ -}; - -/* Lower case range - Greek */ -static signed char UniCaseRangeL0380[44] = { - 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 390-39f */ - 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* Lower case range - Cyrillic */ -static signed char UniCaseRangeL0400[48] = { - 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 80, 80, /* 400-40f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 410-41f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 420-42f */ -}; - -/* Lower case range - Extended cyrillic */ -static signed char UniCaseRangeL0490[60] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ - 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, -}; - -/* Lower case range - Extended latin and greek */ -static signed char UniCaseRangeL1e00[504] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ - 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, 0, 0, /* 1fc0-1fcf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Lower case range - Wide latin */ -static signed char UniCaseRangeLff20[27] = { - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* ff20-ff2f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* - * Lower Case Range - */ -const struct UniCaseRange CifsUniLowerRange[] = { - {0x0380, 0x03ab, UniCaseRangeL0380}, - {0x0400, 0x042f, UniCaseRangeL0400}, - {0x0490, 0x04cb, UniCaseRangeL0490}, - {0x1e00, 0x1ff7, UniCaseRangeL1e00}, - {0xff20, 0xff3a, UniCaseRangeLff20}, - {0} -}; -#endif diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c deleted file mode 100644 index a6c7566a0182..000000000000 --- a/fs/cifs/cifsacl.c +++ /dev/null @@ -1,1672 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2007,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Contains the routines for mapping CIFS/NTFS ACLs - * - */ - -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsacl.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "fs_context.h" - -/* security id for everyone/world system group */ -static const struct cifs_sid sid_everyone = { - 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; -/* security id for Authenticated Users system group */ -static const struct cifs_sid sid_authusers = { - 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; - -/* S-1-22-1 Unmapped Unix users */ -static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-22-2 Unmapped Unix groups */ -static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* - * See https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ - -/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ - -/* S-1-5-88-1 Unix uid */ -static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-2 Unix gid */ -static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-3 Unix mode */ -static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -static const struct cred *root_cred; - -static int -cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) -{ - char *payload; - - /* - * If the payload is less than or equal to the size of a pointer, then - * an allocation here is wasteful. Just copy the data directly to the - * payload.value union member instead. - * - * With this however, you must check the datalen before trying to - * dereference payload.data! - */ - if (prep->datalen <= sizeof(key->payload)) { - key->payload.data[0] = NULL; - memcpy(&key->payload, prep->data, prep->datalen); - } else { - payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); - if (!payload) - return -ENOMEM; - key->payload.data[0] = payload; - } - - key->datalen = prep->datalen; - return 0; -} - -static inline void -cifs_idmap_key_destroy(struct key *key) -{ - if (key->datalen > sizeof(key->payload)) - kfree(key->payload.data[0]); -} - -static struct key_type cifs_idmap_key_type = { - .name = "cifs.idmap", - .instantiate = cifs_idmap_key_instantiate, - .destroy = cifs_idmap_key_destroy, - .describe = user_describe, -}; - -static char * -sid_to_key_str(struct cifs_sid *sidptr, unsigned int type) -{ - int i, len; - unsigned int saval; - char *sidstr, *strptr; - unsigned long long id_auth_val; - - /* 3 bytes for prefix */ - sidstr = kmalloc(3 + SID_STRING_BASE_SIZE + - (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth), - GFP_KERNEL); - if (!sidstr) - return sidstr; - - strptr = sidstr; - len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g', - sidptr->revision); - strptr += len; - - /* The authority field is a single 48-bit number */ - id_auth_val = (unsigned long long)sidptr->authority[5]; - id_auth_val |= (unsigned long long)sidptr->authority[4] << 8; - id_auth_val |= (unsigned long long)sidptr->authority[3] << 16; - id_auth_val |= (unsigned long long)sidptr->authority[2] << 24; - id_auth_val |= (unsigned long long)sidptr->authority[1] << 32; - id_auth_val |= (unsigned long long)sidptr->authority[0] << 48; - - /* - * MS-DTYP states that if the authority is >= 2^32, then it should be - * expressed as a hex value. - */ - if (id_auth_val <= UINT_MAX) - len = sprintf(strptr, "-%llu", id_auth_val); - else - len = sprintf(strptr, "-0x%llx", id_auth_val); - - strptr += len; - - for (i = 0; i < sidptr->num_subauth; ++i) { - saval = le32_to_cpu(sidptr->sub_auth[i]); - len = sprintf(strptr, "-%u", saval); - strptr += len; - } - - return sidstr; -} - -/* - * if the two SIDs (roughly equivalent to a UUID for a user or group) are - * the same returns zero, if they do not match returns non-zero. - */ -static int -compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) -{ - int i; - int num_subauth, num_sat, num_saw; - - if ((!ctsid) || (!cwsid)) - return 1; - - /* compare the revision */ - if (ctsid->revision != cwsid->revision) { - if (ctsid->revision > cwsid->revision) - return 1; - else - return -1; - } - - /* compare all of the six auth values */ - for (i = 0; i < NUM_AUTHS; ++i) { - if (ctsid->authority[i] != cwsid->authority[i]) { - if (ctsid->authority[i] > cwsid->authority[i]) - return 1; - else - return -1; - } - } - - /* compare all of the subauth values if any */ - num_sat = ctsid->num_subauth; - num_saw = cwsid->num_subauth; - num_subauth = num_sat < num_saw ? num_sat : num_saw; - if (num_subauth) { - for (i = 0; i < num_subauth; ++i) { - if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { - if (le32_to_cpu(ctsid->sub_auth[i]) > - le32_to_cpu(cwsid->sub_auth[i])) - return 1; - else - return -1; - } - } - } - - return 0; /* sids compare/match */ -} - -static bool -is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group) -{ - int i; - int num_subauth; - const struct cifs_sid *pwell_known_sid; - - if (!psid || (puid == NULL)) - return false; - - num_subauth = psid->num_subauth; - - /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */ - if (num_subauth == 2) { - if (is_group) - pwell_known_sid = &sid_unix_groups; - else - pwell_known_sid = &sid_unix_users; - } else if (num_subauth == 3) { - if (is_group) - pwell_known_sid = &sid_unix_NFS_groups; - else - pwell_known_sid = &sid_unix_NFS_users; - } else - return false; - - /* compare the revision */ - if (psid->revision != pwell_known_sid->revision) - return false; - - /* compare all of the six auth values */ - for (i = 0; i < NUM_AUTHS; ++i) { - if (psid->authority[i] != pwell_known_sid->authority[i]) { - cifs_dbg(FYI, "auth %d did not match\n", i); - return false; - } - } - - if (num_subauth == 2) { - if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) - return false; - - *puid = le32_to_cpu(psid->sub_auth[1]); - } else /* 3 subauths, ie Windows/Mac style */ { - *puid = le32_to_cpu(psid->sub_auth[0]); - if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) || - (psid->sub_auth[1] != pwell_known_sid->sub_auth[1])) - return false; - - *puid = le32_to_cpu(psid->sub_auth[2]); - } - - cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid); - return true; /* well known sid found, uid returned */ -} - -static __u16 -cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) -{ - int i; - __u16 size = 1 + 1 + 6; - - dst->revision = src->revision; - dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); - for (i = 0; i < NUM_AUTHS; ++i) - dst->authority[i] = src->authority[i]; - for (i = 0; i < dst->num_subauth; ++i) - dst->sub_auth[i] = src->sub_auth[i]; - size += (dst->num_subauth * 4); - - return size; -} - -static int -id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid) -{ - int rc; - struct key *sidkey; - struct cifs_sid *ksid; - unsigned int ksid_size; - char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */ - const struct cred *saved_cred; - - rc = snprintf(desc, sizeof(desc), "%ci:%u", - sidtype == SIDOWNER ? 'o' : 'g', cid); - if (rc >= sizeof(desc)) - return -EINVAL; - - rc = 0; - saved_cred = override_creds(root_cred); - sidkey = request_key(&cifs_idmap_key_type, desc, ""); - if (IS_ERR(sidkey)) { - rc = -EINVAL; - cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n", - __func__, sidtype == SIDOWNER ? 'u' : 'g', cid); - goto out_revert_creds; - } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) { - rc = -EIO; - cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", - __func__, sidkey->datalen); - goto invalidate_key; - } - - /* - * A sid is usually too large to be embedded in payload.value, but if - * there are no subauthorities and the host has 8-byte pointers, then - * it could be. - */ - ksid = sidkey->datalen <= sizeof(sidkey->payload) ? - (struct cifs_sid *)&sidkey->payload : - (struct cifs_sid *)sidkey->payload.data[0]; - - ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32)); - if (ksid_size > sidkey->datalen) { - rc = -EIO; - cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n", - __func__, sidkey->datalen, ksid_size); - goto invalidate_key; - } - - cifs_copy_sid(ssid, ksid); -out_key_put: - key_put(sidkey); -out_revert_creds: - revert_creds(saved_cred); - return rc; - -invalidate_key: - key_invalidate(sidkey); - goto out_key_put; -} - -int -sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, - struct cifs_fattr *fattr, uint sidtype) -{ - int rc = 0; - struct key *sidkey; - char *sidstr; - const struct cred *saved_cred; - kuid_t fuid = cifs_sb->ctx->linux_uid; - kgid_t fgid = cifs_sb->ctx->linux_gid; - - /* - * If we have too many subauthorities, then something is really wrong. - * Just return an error. - */ - if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { - cifs_dbg(FYI, "%s: %u subauthorities is too many!\n", - __func__, psid->num_subauth); - return -EIO; - } - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) || - (cifs_sb_master_tcon(cifs_sb)->posix_extensions)) { - uint32_t unix_id; - bool is_group; - - if (sidtype != SIDOWNER) - is_group = true; - else - is_group = false; - - if (is_well_known_sid(psid, &unix_id, is_group) == false) - goto try_upcall_to_get_id; - - if (is_group) { - kgid_t gid; - gid_t id; - - id = (gid_t)unix_id; - gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid)) { - fgid = gid; - goto got_valid_id; - } - } else { - kuid_t uid; - uid_t id; - - id = (uid_t)unix_id; - uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid)) { - fuid = uid; - goto got_valid_id; - } - } - /* If unable to find uid/gid easily from SID try via upcall */ - } - -try_upcall_to_get_id: - sidstr = sid_to_key_str(psid, sidtype); - if (!sidstr) - return -ENOMEM; - - saved_cred = override_creds(root_cred); - sidkey = request_key(&cifs_idmap_key_type, sidstr, ""); - if (IS_ERR(sidkey)) { - cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n", - __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g'); - goto out_revert_creds; - } - - /* - * FIXME: Here we assume that uid_t and gid_t are same size. It's - * probably a safe assumption but might be better to check based on - * sidtype. - */ - BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); - if (sidkey->datalen != sizeof(uid_t)) { - cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", - __func__, sidkey->datalen); - key_invalidate(sidkey); - goto out_key_put; - } - - if (sidtype == SIDOWNER) { - kuid_t uid; - uid_t id; - memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t)); - uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid)) - fuid = uid; - } else { - kgid_t gid; - gid_t id; - memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t)); - gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid)) - fgid = gid; - } - -out_key_put: - key_put(sidkey); -out_revert_creds: - revert_creds(saved_cred); - kfree(sidstr); - - /* - * Note that we return 0 here unconditionally. If the mapping - * fails then we just fall back to using the ctx->linux_uid/linux_gid. - */ -got_valid_id: - rc = 0; - if (sidtype == SIDOWNER) - fattr->cf_uid = fuid; - else - fattr->cf_gid = fgid; - return rc; -} - -int -init_cifs_idmap(void) -{ - struct cred *cred; - struct key *keyring; - int ret; - - cifs_dbg(FYI, "Registering the %s key type\n", - cifs_idmap_key_type.name); - - /* create an override credential set with a special thread keyring in - * which requests are cached - * - * this is used to prevent malicious redirections from being installed - * with add_key(). - */ - cred = prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; - - keyring = keyring_alloc(".cifs_idmap", - GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) { - ret = PTR_ERR(keyring); - goto failed_put_cred; - } - - ret = register_key_type(&cifs_idmap_key_type); - if (ret < 0) - goto failed_put_key; - - /* instruct request_key() to use this special keyring as a cache for - * the results it looks up */ - set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); - cred->thread_keyring = keyring; - cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; - root_cred = cred; - - cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring)); - return 0; - -failed_put_key: - key_put(keyring); -failed_put_cred: - put_cred(cred); - return ret; -} - -void -exit_cifs_idmap(void) -{ - key_revoke(root_cred->thread_keyring); - unregister_key_type(&cifs_idmap_key_type); - put_cred(root_cred); - cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name); -} - -/* copy ntsd, owner sid, and group sid from a security descriptor to another */ -static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd, - struct cifs_ntsd *pnntsd, - __u32 sidsoffset, - struct cifs_sid *pownersid, - struct cifs_sid *pgrpsid) -{ - struct cifs_sid *owner_sid_ptr, *group_sid_ptr; - struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; - - /* copy security descriptor control portion */ - pnntsd->revision = pntsd->revision; - pnntsd->type = pntsd->type; - pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd)); - pnntsd->sacloffset = 0; - pnntsd->osidoffset = cpu_to_le32(sidsoffset); - pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid)); - - /* copy owner sid */ - if (pownersid) - owner_sid_ptr = pownersid; - else - owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset); - cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr); - - /* copy group sid */ - if (pgrpsid) - group_sid_ptr = pgrpsid; - else - group_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset + - sizeof(struct cifs_sid)); - cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr); - - return sidsoffset + (2 * sizeof(struct cifs_sid)); -} - - -/* - change posix mode to reflect permissions - pmode is the existing mode (we only want to overwrite part of this - bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 -*/ -static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode, - umode_t *pdenied, umode_t mask) -{ - __u32 flags = le32_to_cpu(ace_flags); - /* - * Do not assume "preferred" or "canonical" order. - * The first DENY or ALLOW ACE which matches perfectly is - * the permission to be used. Once allowed or denied, same - * permission in later ACEs do not matter. - */ - - /* If not already allowed, deny these bits */ - if (type == ACCESS_DENIED) { - if (flags & GENERIC_ALL && - !(*pmode & mask & 0777)) - *pdenied |= mask & 0777; - - if (((flags & GENERIC_WRITE) || - ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && - !(*pmode & mask & 0222)) - *pdenied |= mask & 0222; - - if (((flags & GENERIC_READ) || - ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && - !(*pmode & mask & 0444)) - *pdenied |= mask & 0444; - - if (((flags & GENERIC_EXECUTE) || - ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && - !(*pmode & mask & 0111)) - *pdenied |= mask & 0111; - - return; - } else if (type != ACCESS_ALLOWED) { - cifs_dbg(VFS, "unknown access control type %d\n", type); - return; - } - /* else ACCESS_ALLOWED type */ - - if ((flags & GENERIC_ALL) && - !(*pdenied & mask & 0777)) { - *pmode |= mask & 0777; - cifs_dbg(NOISY, "all perms\n"); - return; - } - - if (((flags & GENERIC_WRITE) || - ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && - !(*pdenied & mask & 0222)) - *pmode |= mask & 0222; - - if (((flags & GENERIC_READ) || - ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && - !(*pdenied & mask & 0444)) - *pmode |= mask & 0444; - - if (((flags & GENERIC_EXECUTE) || - ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && - !(*pdenied & mask & 0111)) - *pmode |= mask & 0111; - - /* If DELETE_CHILD is set only on an owner ACE, set sticky bit */ - if (flags & FILE_DELETE_CHILD) { - if (mask == ACL_OWNER_MASK) { - if (!(*pdenied & 01000)) - *pmode |= 01000; - } else if (!(*pdenied & 01000)) { - *pmode &= ~01000; - *pdenied |= 01000; - } - } - - cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode); - return; -} - -/* - Generate access flags to reflect permissions mode is the existing mode. - This function is called for every ACE in the DACL whose SID matches - with either owner or group or everyone. -*/ - -static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, - __u32 *pace_flags) -{ - /* reset access mask */ - *pace_flags = 0x0; - - /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ - mode &= bits_to_use; - - /* check for R/W/X UGO since we do not know whose flags - is this but we have cleared all the bits sans RWX for - either user or group or other as per bits_to_use */ - if (mode & S_IRUGO) - *pace_flags |= SET_FILE_READ_RIGHTS; - if (mode & S_IWUGO) - *pace_flags |= SET_FILE_WRITE_RIGHTS; - if (mode & S_IXUGO) - *pace_flags |= SET_FILE_EXEC_RIGHTS; - - cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n", - mode, *pace_flags); - return; -} - -static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src, struct cifs_sid *psid) -{ - __u16 size = 1 + 1 + 2 + 4; - - dst->type = src->type; - dst->flags = src->flags; - dst->access_req = src->access_req; - - /* Check if there's a replacement sid specified */ - if (psid) - size += cifs_copy_sid(&dst->sid, psid); - else - size += cifs_copy_sid(&dst->sid, &src->sid); - - dst->size = cpu_to_le16(size); - - return size; -} - -static __u16 fill_ace_for_sid(struct cifs_ace *pntace, - const struct cifs_sid *psid, __u64 nmode, - umode_t bits, __u8 access_type, - bool allow_delete_child) -{ - int i; - __u16 size = 0; - __u32 access_req = 0; - - pntace->type = access_type; - pntace->flags = 0x0; - mode_to_access_flags(nmode, bits, &access_req); - - if (access_type == ACCESS_ALLOWED && allow_delete_child) - access_req |= FILE_DELETE_CHILD; - - if (access_type == ACCESS_ALLOWED && !access_req) - access_req = SET_MINIMUM_RIGHTS; - else if (access_type == ACCESS_DENIED) - access_req &= ~SET_MINIMUM_RIGHTS; - - pntace->access_req = cpu_to_le32(access_req); - - pntace->sid.revision = psid->revision; - pntace->sid.num_subauth = psid->num_subauth; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = psid->authority[i]; - for (i = 0; i < psid->num_subauth; i++) - pntace->sid.sub_auth[i] = psid->sub_auth[i]; - - size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); - pntace->size = cpu_to_le16(size); - - return size; -} - - -#ifdef CONFIG_CIFS_DEBUG2 -static void dump_ace(struct cifs_ace *pace, char *end_of_acl) -{ - int num_subauth; - - /* validate that we do not go past end of acl */ - - if (le16_to_cpu(pace->size) < 16) { - cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size)); - return; - } - - if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { - cifs_dbg(VFS, "ACL too small to parse ACE\n"); - return; - } - - num_subauth = pace->sid.num_subauth; - if (num_subauth) { - int i; - cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n", - pace->sid.revision, pace->sid.num_subauth, pace->type, - pace->flags, le16_to_cpu(pace->size)); - for (i = 0; i < num_subauth; ++i) { - cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n", - i, le32_to_cpu(pace->sid.sub_auth[i])); - } - - /* BB add length check to make sure that we do not have huge - num auths and therefore go off the end */ - } - - return; -} -#endif - -static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, - struct cifs_fattr *fattr, bool mode_from_special_sid) -{ - int i; - int num_aces = 0; - int acl_size; - char *acl_base; - struct cifs_ace **ppace; - - /* BB need to add parm so we can store the SID BB */ - - if (!pdacl) { - /* no DACL in the security descriptor, set - all the permissions for user/group/other */ - fattr->cf_mode |= 0777; - return; - } - - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { - cifs_dbg(VFS, "ACL too small to parse DACL\n"); - return; - } - - cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", - le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), - le32_to_cpu(pdacl->num_aces)); - - /* reset rwx permissions for user/group/other. - Also, if num_aces is 0 i.e. DACL has no ACEs, - user/group/other have no permissions */ - fattr->cf_mode &= ~(0777); - - acl_base = (char *)pdacl; - acl_size = sizeof(struct cifs_acl); - - num_aces = le32_to_cpu(pdacl->num_aces); - if (num_aces > 0) { - umode_t denied_mode = 0; - - if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *)) - return; - ppace = kmalloc_array(num_aces, sizeof(struct cifs_ace *), - GFP_KERNEL); - if (!ppace) - return; - - for (i = 0; i < num_aces; ++i) { - ppace[i] = (struct cifs_ace *) (acl_base + acl_size); -#ifdef CONFIG_CIFS_DEBUG2 - dump_ace(ppace[i], end_of_acl); -#endif - if (mode_from_special_sid && - (compare_sids(&(ppace[i]->sid), - &sid_unix_NFS_mode) == 0)) { - /* - * Full permissions are: - * 07777 = S_ISUID | S_ISGID | S_ISVTX | - * S_IRWXU | S_IRWXG | S_IRWXO - */ - fattr->cf_mode &= ~07777; - fattr->cf_mode |= - le32_to_cpu(ppace[i]->sid.sub_auth[2]); - break; - } else { - if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { - access_flags_to_mode(ppace[i]->access_req, - ppace[i]->type, - &fattr->cf_mode, - &denied_mode, - ACL_OWNER_MASK); - } else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) { - access_flags_to_mode(ppace[i]->access_req, - ppace[i]->type, - &fattr->cf_mode, - &denied_mode, - ACL_GROUP_MASK); - } else if ((compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) || - (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)) { - access_flags_to_mode(ppace[i]->access_req, - ppace[i]->type, - &fattr->cf_mode, - &denied_mode, - ACL_EVERYONE_MASK); - } - } - - -/* memcpy((void *)(&(cifscred->aces[i])), - (void *)ppace[i], - sizeof(struct cifs_ace)); */ - - acl_base = (char *)ppace[i]; - acl_size = le16_to_cpu(ppace[i]->size); - } - - kfree(ppace); - } - - return; -} - -unsigned int setup_authusers_ACE(struct cifs_ace *pntace) -{ - int i; - unsigned int ace_size = 20; - - pntace->type = ACCESS_ALLOWED_ACE_TYPE; - pntace->flags = 0x0; - pntace->access_req = cpu_to_le32(GENERIC_ALL); - pntace->sid.num_subauth = 1; - pntace->sid.revision = 1; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = sid_authusers.authority[i]; - - pntace->sid.sub_auth[0] = sid_authusers.sub_auth[0]; - - /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ - pntace->size = cpu_to_le16(ace_size); - return ace_size; -} - -/* - * Fill in the special SID based on the mode. See - * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ -unsigned int setup_special_mode_ACE(struct cifs_ace *pntace, __u64 nmode) -{ - int i; - unsigned int ace_size = 28; - - pntace->type = ACCESS_DENIED_ACE_TYPE; - pntace->flags = 0x0; - pntace->access_req = 0; - pntace->sid.num_subauth = 3; - pntace->sid.revision = 1; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = sid_unix_NFS_mode.authority[i]; - - pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0]; - pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; - pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); - - /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ - pntace->size = cpu_to_le16(ace_size); - return ace_size; -} - -unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace) -{ - int i; - unsigned int ace_size = 28; - - pntace->type = ACCESS_ALLOWED_ACE_TYPE; - pntace->flags = 0x0; - pntace->access_req = cpu_to_le32(GENERIC_ALL); - pntace->sid.num_subauth = 3; - pntace->sid.revision = 1; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = sid_unix_NFS_users.authority[i]; - - pntace->sid.sub_auth[0] = sid_unix_NFS_users.sub_auth[0]; - pntace->sid.sub_auth[1] = sid_unix_NFS_users.sub_auth[1]; - pntace->sid.sub_auth[2] = cpu_to_le32(current_fsgid().val); - - /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ - pntace->size = cpu_to_le16(ace_size); - return ace_size; -} - -static void populate_new_aces(char *nacl_base, - struct cifs_sid *pownersid, - struct cifs_sid *pgrpsid, - __u64 *pnmode, u32 *pnum_aces, u16 *pnsize, - bool modefromsid) -{ - __u64 nmode; - u32 num_aces = 0; - u16 nsize = 0; - __u64 user_mode; - __u64 group_mode; - __u64 other_mode; - __u64 deny_user_mode = 0; - __u64 deny_group_mode = 0; - bool sticky_set = false; - struct cifs_ace *pnntace = NULL; - - nmode = *pnmode; - num_aces = *pnum_aces; - nsize = *pnsize; - - if (modefromsid) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += setup_special_mode_ACE(pnntace, nmode); - num_aces++; - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += setup_authusers_ACE(pnntace); - num_aces++; - goto set_size; - } - - /* - * We'll try to keep the mode as requested by the user. - * But in cases where we cannot meaningfully convert that - * into ACL, return back the updated mode, so that it is - * updated in the inode. - */ - - if (!memcmp(pownersid, pgrpsid, sizeof(struct cifs_sid))) { - /* - * Case when owner and group SIDs are the same. - * Set the more restrictive of the two modes. - */ - user_mode = nmode & (nmode << 3) & 0700; - group_mode = nmode & (nmode >> 3) & 0070; - } else { - user_mode = nmode & 0700; - group_mode = nmode & 0070; - } - - other_mode = nmode & 0007; - - /* We need DENY ACE when the perm is more restrictive than the next sets. */ - deny_user_mode = ~(user_mode) & ((group_mode << 3) | (other_mode << 6)) & 0700; - deny_group_mode = ~(group_mode) & (other_mode << 3) & 0070; - - *pnmode = user_mode | group_mode | other_mode | (nmode & ~0777); - - /* This tells if we should allow delete child for group and everyone. */ - if (nmode & 01000) - sticky_set = true; - - if (deny_user_mode) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pownersid, deny_user_mode, - 0700, ACCESS_DENIED, false); - num_aces++; - } - - /* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/ - if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, - 0070, ACCESS_DENIED, false); - num_aces++; - } - - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pownersid, user_mode, - 0700, ACCESS_ALLOWED, true); - num_aces++; - - /* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */ - if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, - 0070, ACCESS_DENIED, false); - num_aces++; - } - - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pgrpsid, group_mode, - 0070, ACCESS_ALLOWED, !sticky_set); - num_aces++; - - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, &sid_everyone, other_mode, - 0007, ACCESS_ALLOWED, !sticky_set); - num_aces++; - -set_size: - *pnum_aces = num_aces; - *pnsize = nsize; -} - -static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, - struct cifs_sid *pnownersid, struct cifs_sid *pngrpsid) -{ - int i; - u16 size = 0; - struct cifs_ace *pntace = NULL; - char *acl_base = NULL; - u32 src_num_aces = 0; - u16 nsize = 0; - struct cifs_ace *pnntace = NULL; - char *nacl_base = NULL; - u16 ace_size = 0; - - acl_base = (char *)pdacl; - size = sizeof(struct cifs_acl); - src_num_aces = le32_to_cpu(pdacl->num_aces); - - nacl_base = (char *)pndacl; - nsize = sizeof(struct cifs_acl); - - /* Go through all the ACEs */ - for (i = 0; i < src_num_aces; ++i) { - pntace = (struct cifs_ace *) (acl_base + size); - pnntace = (struct cifs_ace *) (nacl_base + nsize); - - if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) - ace_size = cifs_copy_ace(pnntace, pntace, pnownersid); - else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) - ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid); - else - ace_size = cifs_copy_ace(pnntace, pntace, NULL); - - size += le16_to_cpu(pntace->size); - nsize += ace_size; - } - - return nsize; -} - -static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, - __u64 *pnmode, bool mode_from_sid) -{ - int i; - u16 size = 0; - struct cifs_ace *pntace = NULL; - char *acl_base = NULL; - u32 src_num_aces = 0; - u16 nsize = 0; - struct cifs_ace *pnntace = NULL; - char *nacl_base = NULL; - u32 num_aces = 0; - bool new_aces_set = false; - - /* Assuming that pndacl and pnmode are never NULL */ - nacl_base = (char *)pndacl; - nsize = sizeof(struct cifs_acl); - - /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */ - if (!pdacl) { - populate_new_aces(nacl_base, - pownersid, pgrpsid, - pnmode, &num_aces, &nsize, - mode_from_sid); - goto finalize_dacl; - } - - acl_base = (char *)pdacl; - size = sizeof(struct cifs_acl); - src_num_aces = le32_to_cpu(pdacl->num_aces); - - /* Retain old ACEs which we can retain */ - for (i = 0; i < src_num_aces; ++i) { - pntace = (struct cifs_ace *) (acl_base + size); - - if (!new_aces_set && (pntace->flags & INHERITED_ACE)) { - /* Place the new ACEs in between existing explicit and inherited */ - populate_new_aces(nacl_base, - pownersid, pgrpsid, - pnmode, &num_aces, &nsize, - mode_from_sid); - - new_aces_set = true; - } - - /* If it's any one of the ACE we're replacing, skip! */ - if (((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) || - (compare_sids(&pntace->sid, pownersid) == 0) || - (compare_sids(&pntace->sid, pgrpsid) == 0) || - (compare_sids(&pntace->sid, &sid_everyone) == 0) || - (compare_sids(&pntace->sid, &sid_authusers) == 0))) { - goto next_ace; - } - - /* update the pointer to the next ACE to populate*/ - pnntace = (struct cifs_ace *) (nacl_base + nsize); - - nsize += cifs_copy_ace(pnntace, pntace, NULL); - num_aces++; - -next_ace: - size += le16_to_cpu(pntace->size); - } - - /* If inherited ACEs are not present, place the new ones at the tail */ - if (!new_aces_set) { - populate_new_aces(nacl_base, - pownersid, pgrpsid, - pnmode, &num_aces, &nsize, - mode_from_sid); - - new_aces_set = true; - } - -finalize_dacl: - pndacl->num_aces = cpu_to_le32(num_aces); - pndacl->size = cpu_to_le16(nsize); - - return 0; -} - -static int parse_sid(struct cifs_sid *psid, char *end_of_acl) -{ - /* BB need to add parm so we can store the SID BB */ - - /* validate that we do not go past end of ACL - sid must be at least 8 - bytes long (assuming no sub-auths - e.g. the null SID */ - if (end_of_acl < (char *)psid + 8) { - cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid); - return -EINVAL; - } - -#ifdef CONFIG_CIFS_DEBUG2 - if (psid->num_subauth) { - int i; - cifs_dbg(FYI, "SID revision %d num_auth %d\n", - psid->revision, psid->num_subauth); - - for (i = 0; i < psid->num_subauth; i++) { - cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n", - i, le32_to_cpu(psid->sub_auth[i])); - } - - /* BB add length check to make sure that we do not have huge - num auths and therefore go off the end */ - cifs_dbg(FYI, "RID 0x%x\n", - le32_to_cpu(psid->sub_auth[psid->num_subauth-1])); - } -#endif - - return 0; -} - - -/* Convert CIFS ACL to POSIX form */ -static int parse_sec_desc(struct cifs_sb_info *cifs_sb, - struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr, - bool get_mode_from_special_sid) -{ - int rc = 0; - struct cifs_sid *owner_sid_ptr, *group_sid_ptr; - struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ - char *end_of_acl = ((char *)pntsd) + acl_len; - __u32 dacloffset; - - if (pntsd == NULL) - return -EIO; - - owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - group_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - dacloffset = le32_to_cpu(pntsd->dacloffset); - dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); - cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", - pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), - le32_to_cpu(pntsd->gsidoffset), - le32_to_cpu(pntsd->sacloffset), dacloffset); -/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ - rc = parse_sid(owner_sid_ptr, end_of_acl); - if (rc) { - cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc); - return rc; - } - rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", - __func__, rc); - return rc; - } - - rc = parse_sid(group_sid_ptr, end_of_acl); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n", - __func__, rc); - return rc; - } - rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", - __func__, rc); - return rc; - } - - if (dacloffset) - parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, - group_sid_ptr, fattr, get_mode_from_special_sid); - else - cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ - - return rc; -} - -/* Convert permission bits from mode to equivalent CIFS ACL */ -static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, - __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid, - bool mode_from_sid, bool id_from_sid, int *aclflag) -{ - int rc = 0; - __u32 dacloffset; - __u32 ndacloffset; - __u32 sidsoffset; - struct cifs_sid *owner_sid_ptr, *group_sid_ptr; - struct cifs_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL; - struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */ - struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */ - char *end_of_acl = ((char *)pntsd) + secdesclen; - u16 size = 0; - - dacloffset = le32_to_cpu(pntsd->dacloffset); - if (dacloffset) { - dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); - if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { - cifs_dbg(VFS, "Server returned illegal ACL size\n"); - return -EINVAL; - } - } - - owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - group_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - - if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ - ndacloffset = sizeof(struct cifs_ntsd); - ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); - ndacl_ptr->revision = - dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); - - ndacl_ptr->size = cpu_to_le16(0); - ndacl_ptr->num_aces = cpu_to_le32(0); - - rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, - pnmode, mode_from_sid); - - sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); - /* copy the non-dacl portion of secdesc */ - *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, - NULL, NULL); - - *aclflag |= CIFS_ACL_DACL; - } else { - ndacloffset = sizeof(struct cifs_ntsd); - ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); - ndacl_ptr->revision = - dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); - ndacl_ptr->num_aces = dacl_ptr ? dacl_ptr->num_aces : 0; - - if (uid_valid(uid)) { /* chown */ - uid_t id; - nowner_sid_ptr = kzalloc(sizeof(struct cifs_sid), - GFP_KERNEL); - if (!nowner_sid_ptr) { - rc = -ENOMEM; - goto chown_chgrp_exit; - } - id = from_kuid(&init_user_ns, uid); - if (id_from_sid) { - struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr; - /* Populate the user ownership fields S-1-5-88-1 */ - osid->Revision = 1; - osid->NumAuth = 3; - osid->Authority[5] = 5; - osid->SubAuthorities[0] = cpu_to_le32(88); - osid->SubAuthorities[1] = cpu_to_le32(1); - osid->SubAuthorities[2] = cpu_to_le32(id); - - } else { /* lookup sid with upcall */ - rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); - if (rc) { - cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", - __func__, rc, id); - goto chown_chgrp_exit; - } - } - *aclflag |= CIFS_ACL_OWNER; - } - if (gid_valid(gid)) { /* chgrp */ - gid_t id; - ngroup_sid_ptr = kzalloc(sizeof(struct cifs_sid), - GFP_KERNEL); - if (!ngroup_sid_ptr) { - rc = -ENOMEM; - goto chown_chgrp_exit; - } - id = from_kgid(&init_user_ns, gid); - if (id_from_sid) { - struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr; - /* Populate the group ownership fields S-1-5-88-2 */ - gsid->Revision = 1; - gsid->NumAuth = 3; - gsid->Authority[5] = 5; - gsid->SubAuthorities[0] = cpu_to_le32(88); - gsid->SubAuthorities[1] = cpu_to_le32(2); - gsid->SubAuthorities[2] = cpu_to_le32(id); - - } else { /* lookup sid with upcall */ - rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); - if (rc) { - cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", - __func__, rc, id); - goto chown_chgrp_exit; - } - } - *aclflag |= CIFS_ACL_GROUP; - } - - if (dacloffset) { - /* Replace ACEs for old owner with new one */ - size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, - owner_sid_ptr, group_sid_ptr, - nowner_sid_ptr, ngroup_sid_ptr); - ndacl_ptr->size = cpu_to_le16(size); - } - - sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); - /* copy the non-dacl portion of secdesc */ - *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, - nowner_sid_ptr, ngroup_sid_ptr); - -chown_chgrp_exit: - /* errors could jump here. So make sure we return soon after this */ - kfree(nowner_sid_ptr); - kfree(ngroup_sid_ptr); - } - - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, - const struct cifs_fid *cifsfid, u32 *pacllen, - u32 __maybe_unused unused) -{ - struct cifs_ntsd *pntsd = NULL; - unsigned int xid; - int rc; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - xid = get_xid(); - rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd, - pacllen); - free_xid(xid); - - cifs_put_tlink(tlink); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; -} - -static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, - const char *path, u32 *pacllen) -{ - struct cifs_ntsd *pntsd = NULL; - int oplock = 0; - unsigned int xid; - int rc; - struct cifs_tcon *tcon; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = READ_CONTROL, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (!rc) { - rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen); - CIFSSMBClose(xid, tcon, fid.netfid); - } - - cifs_put_tlink(tlink); - free_xid(xid); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; -} - -/* Retrieve an ACL from the server */ -struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, - struct inode *inode, const char *path, - u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - struct cifsFileInfo *open_file = NULL; - - if (inode) - open_file = find_readable_file(CIFS_I(inode), true); - if (!open_file) - return get_cifs_acl_by_path(cifs_sb, path, pacllen); - - pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); - cifsFileInfo_put(open_file); - return pntsd; -} - - /* Set an ACL on the server */ -int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, - struct inode *inode, const char *path, int aclflag) -{ - int oplock = 0; - unsigned int xid; - int rc, access_flags; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP) - access_flags = WRITE_OWNER; - else - access_flags = WRITE_DAC; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = access_flags, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) { - cifs_dbg(VFS, "Unable to open file to set ACL\n"); - goto out; - } - - rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag); - cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc); - - CIFSSMBClose(xid, tcon, fid.netfid); -out: - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */ -int -cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, - struct inode *inode, bool mode_from_special_sid, - const char *path, const struct cifs_fid *pfid) -{ - struct cifs_ntsd *pntsd = NULL; - u32 acllen = 0; - int rc = 0; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct smb_version_operations *ops; - const u32 info = 0; - - cifs_dbg(NOISY, "converting ACL to mode for %s\n", path); - - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - ops = tlink_tcon(tlink)->ses->server->ops; - - if (pfid && (ops->get_acl_by_fid)) - pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen, info); - else if (ops->get_acl) - pntsd = ops->get_acl(cifs_sb, inode, path, &acllen, info); - else { - cifs_put_tlink(tlink); - return -EOPNOTSUPP; - } - /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ - if (IS_ERR(pntsd)) { - rc = PTR_ERR(pntsd); - cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); - } else if (mode_from_special_sid) { - rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true); - kfree(pntsd); - } else { - /* get approximated mode from ACL */ - rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false); - kfree(pntsd); - if (rc) - cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); - } - - cifs_put_tlink(tlink); - - return rc; -} - -/* Convert mode bits to an ACL so we can update the ACL on the server */ -int -id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, - kuid_t uid, kgid_t gid) -{ - int rc = 0; - int aclflag = CIFS_ACL_DACL; /* default flag to set */ - __u32 secdesclen = 0; - __u32 nsecdesclen = 0; - __u32 dacloffset = 0; - struct cifs_acl *dacl_ptr = NULL; - struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */ - struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct smb_version_operations *ops; - bool mode_from_sid, id_from_sid; - const u32 info = 0; - - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - ops = tlink_tcon(tlink)->ses->server->ops; - - cifs_dbg(NOISY, "set ACL from mode for %s\n", path); - - /* Get the security descriptor */ - - if (ops->get_acl == NULL) { - cifs_put_tlink(tlink); - return -EOPNOTSUPP; - } - - pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen, info); - if (IS_ERR(pntsd)) { - rc = PTR_ERR(pntsd); - cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); - cifs_put_tlink(tlink); - return rc; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) - mode_from_sid = true; - else - mode_from_sid = false; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) - id_from_sid = true; - else - id_from_sid = false; - - /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */ - nsecdesclen = secdesclen; - if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ - if (mode_from_sid) - nsecdesclen += 2 * sizeof(struct cifs_ace); - else /* cifsacl */ - nsecdesclen += 5 * sizeof(struct cifs_ace); - } else { /* chown */ - /* When ownership changes, changes new owner sid length could be different */ - nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2); - dacloffset = le32_to_cpu(pntsd->dacloffset); - if (dacloffset) { - dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); - if (mode_from_sid) - nsecdesclen += - le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace); - else /* cifsacl */ - nsecdesclen += le16_to_cpu(dacl_ptr->size); - } - } - - /* - * Add three ACEs for owner, group, everyone getting rid of other ACEs - * as chmod disables ACEs and set the security descriptor. Allocate - * memory for the smb header, set security descriptor request security - * descriptor parameters, and security descriptor itself - */ - nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN); - pnntsd = kmalloc(nsecdesclen, GFP_KERNEL); - if (!pnntsd) { - kfree(pntsd); - cifs_put_tlink(tlink); - return -ENOMEM; - } - - rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid, - mode_from_sid, id_from_sid, &aclflag); - - cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); - - if (ops->set_acl == NULL) - rc = -EOPNOTSUPP; - - if (!rc) { - /* Set the security descriptor */ - rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); - cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); - } - cifs_put_tlink(tlink); - - kfree(pnntsd); - kfree(pntsd); - return rc; -} diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h deleted file mode 100644 index ccbfc754bd3c..000000000000 --- a/fs/cifs/cifsacl.h +++ /dev/null @@ -1,199 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFSACL_H -#define _CIFSACL_H - -#define NUM_AUTHS (6) /* number of authority fields */ -#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ - -#define READ_BIT 0x4 -#define WRITE_BIT 0x2 -#define EXEC_BIT 0x1 - -#define ACL_OWNER_MASK 0700 -#define ACL_GROUP_MASK 0070 -#define ACL_EVERYONE_MASK 0007 - -#define UBITSHIFT 6 -#define GBITSHIFT 3 - -#define ACCESS_ALLOWED 0 -#define ACCESS_DENIED 1 - -#define SIDOWNER 1 -#define SIDGROUP 2 - -/* - * Security Descriptor length containing DACL with 3 ACEs (one each for - * owner, group and world). - */ -#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \ - sizeof(struct cifs_acl) + \ - (sizeof(struct cifs_ace) * 4)) - -/* - * Maximum size of a string representation of a SID: - * - * The fields are unsigned values in decimal. So: - * - * u8: max 3 bytes in decimal - * u32: max 10 bytes in decimal - * - * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator - * - * For authority field, max is when all 6 values are non-zero and it must be - * represented in hex. So "-0x" + 12 hex digits. - * - * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') - */ -#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) -#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ - -struct cifs_ntsd { - __le16 revision; /* revision level */ - __le16 type; - __le32 osidoffset; - __le32 gsidoffset; - __le32 sacloffset; - __le32 dacloffset; -} __attribute__((packed)); - -struct cifs_sid { - __u8 revision; /* revision level */ - __u8 num_subauth; - __u8 authority[NUM_AUTHS]; - __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ -} __attribute__((packed)); - -/* size of a struct cifs_sid, sans sub_auth array */ -#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) - -struct cifs_acl { - __le16 revision; /* revision level */ - __le16 size; - __le32 num_aces; -} __attribute__((packed)); - -/* ACE types - see MS-DTYP 2.4.4.1 */ -#define ACCESS_ALLOWED_ACE_TYPE 0x00 -#define ACCESS_DENIED_ACE_TYPE 0x01 -#define SYSTEM_AUDIT_ACE_TYPE 0x02 -#define SYSTEM_ALARM_ACE_TYPE 0x03 -#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 -#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 -#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 -#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 -#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 -#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 -#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A -#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B -#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C -#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D -#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ -#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F -#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ -#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 -#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 -#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 - -/* ACE flags */ -#define OBJECT_INHERIT_ACE 0x01 -#define CONTAINER_INHERIT_ACE 0x02 -#define NO_PROPAGATE_INHERIT_ACE 0x04 -#define INHERIT_ONLY_ACE 0x08 -#define INHERITED_ACE 0x10 -#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 -#define FAILED_ACCESS_ACE_FLAG 0x80 - -struct cifs_ace { - __u8 type; /* see above and MS-DTYP 2.4.4.1 */ - __u8 flags; - __le16 size; - __le32 access_req; - struct cifs_sid sid; /* ie UUID of user or group who gets these perms */ -} __attribute__((packed)); - -/* - * The current SMB3 form of security descriptor is similar to what was used for - * cifs (see above) but some fields are split, and fields in the struct below - * matches names of fields to the spec, MS-DTYP (see sections 2.4.5 and - * 2.4.6). Note that "CamelCase" fields are used in this struct in order to - * match the MS-DTYP and MS-SMB2 specs which define the wire format. - */ -struct smb3_sd { - __u8 Revision; /* revision level, MUST be one */ - __u8 Sbz1; /* only meaningful if 'RM' flag set below */ - __le16 Control; - __le32 OffsetOwner; - __le32 OffsetGroup; - __le32 OffsetSacl; - __le32 OffsetDacl; -} __packed; - -/* Meaning of 'Control' field flags */ -#define ACL_CONTROL_SR 0x8000 /* Self relative */ -#define ACL_CONTROL_RM 0x4000 /* Resource manager control bits */ -#define ACL_CONTROL_PS 0x2000 /* SACL protected from inherits */ -#define ACL_CONTROL_PD 0x1000 /* DACL protected from inherits */ -#define ACL_CONTROL_SI 0x0800 /* SACL Auto-Inherited */ -#define ACL_CONTROL_DI 0x0400 /* DACL Auto-Inherited */ -#define ACL_CONTROL_SC 0x0200 /* SACL computed through inheritance */ -#define ACL_CONTROL_DC 0x0100 /* DACL computed through inheritence */ -#define ACL_CONTROL_SS 0x0080 /* Create server ACL */ -#define ACL_CONTROL_DT 0x0040 /* DACL provided by trusted source */ -#define ACL_CONTROL_SD 0x0020 /* SACL defaulted */ -#define ACL_CONTROL_SP 0x0010 /* SACL is present on object */ -#define ACL_CONTROL_DD 0x0008 /* DACL defaulted */ -#define ACL_CONTROL_DP 0x0004 /* DACL is present on object */ -#define ACL_CONTROL_GD 0x0002 /* Group was defaulted */ -#define ACL_CONTROL_OD 0x0001 /* User was defaulted */ - -/* Meaning of AclRevision flags */ -#define ACL_REVISION 0x02 /* See section 2.4.4.1 of MS-DTYP */ -#define ACL_REVISION_DS 0x04 /* Additional AceTypes allowed */ - -struct smb3_acl { - u8 AclRevision; /* revision level */ - u8 Sbz1; /* MBZ */ - __le16 AclSize; - __le16 AceCount; - __le16 Sbz2; /* MBZ */ -} __packed; - -/* - * Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid - * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ -struct owner_sid { - u8 Revision; - u8 NumAuth; - u8 Authority[6]; - __le32 SubAuthorities[3]; -} __packed; - -struct owner_group_sids { - struct owner_sid owner; - struct owner_sid group; -} __packed; - -/* - * Minimum security identifier can be one for system defined Users - * and Groups such as NULL SID and World or Built-in accounts such - * as Administrator and Guest and consists of - * Revision + Num (Sub)Auths + Authority + Domain (one Subauthority) - */ -#define MIN_SID_LEN (1 + 1 + 6 + 4) /* in bytes */ - -/* - * Minimum security descriptor can be one without any SACL and DACL and can - * consist of revision, type, and two sids of minimum size for owner and group - */ -#define MIN_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + (2 * MIN_SID_LEN)) - -#endif /* _CIFSACL_H */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c deleted file mode 100644 index cbc18b4a9cb2..000000000000 --- a/fs/cifs/cifsencrypt.c +++ /dev/null @@ -1,733 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP - * for more detailed information - * - * Copyright (C) International Business Machines Corp., 2005,2013 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "cifsproto.h" -#include "ntlmssp.h" -#include -#include -#include -#include -#include "../smbfs_common/arc4.h" -#include - -int __cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature, - struct shash_desc *shash) -{ - int i; - int rc; - struct kvec *iov = rqst->rq_iov; - int n_vec = rqst->rq_nvec; - - /* iov[0] is actual data and not the rfc1002 length for SMB2+ */ - if (!is_smb1(server)) { - if (iov[0].iov_len <= 4) - return -EIO; - i = 0; - } else { - if (n_vec < 2 || iov[0].iov_len != 4) - return -EIO; - i = 1; /* skip rfc1002 length */ - } - - for (; i < n_vec; i++) { - if (iov[i].iov_len == 0) - continue; - if (iov[i].iov_base == NULL) { - cifs_dbg(VFS, "null iovec entry\n"); - return -EIO; - } - - rc = crypto_shash_update(shash, - iov[i].iov_base, iov[i].iov_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with payload\n", - __func__); - return rc; - } - } - - /* now hash over the rq_pages array */ - for (i = 0; i < rqst->rq_npages; i++) { - void *kaddr; - unsigned int len, offset; - - rqst_page_get_length(rqst, i, &len, &offset); - - kaddr = (char *) kmap(rqst->rq_pages[i]) + offset; - - rc = crypto_shash_update(shash, kaddr, len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with payload\n", - __func__); - kunmap(rqst->rq_pages[i]); - return rc; - } - - kunmap(rqst->rq_pages[i]); - } - - rc = crypto_shash_final(shash, signature); - if (rc) - cifs_dbg(VFS, "%s: Could not generate hash\n", __func__); - - return rc; -} - -/* - * Calculate and return the CIFS signature based on the mac key and SMB PDU. - * The 16 byte signature must be allocated by the caller. Note we only use the - * 1st eight bytes and that the smb header signature field on input contains - * the sequence number before this function is called. Also, this function - * should be called with the server->srv_mutex held. - */ -static int cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature) -{ - int rc; - - if (!rqst->rq_iov || !signature || !server) - return -EINVAL; - - rc = cifs_alloc_hash("md5", &server->secmech.md5); - if (rc) - return -1; - - rc = crypto_shash_init(server->secmech.md5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init md5\n", __func__); - return rc; - } - - rc = crypto_shash_update(server->secmech.md5, - server->session_key.response, server->session_key.len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); - return rc; - } - - return __cifs_calc_signature(rqst, server, signature, server->secmech.md5); -} - -/* must be called with server->srv_mutex held */ -int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number) -{ - int rc = 0; - char smb_signature[20]; - struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return -EIO; - - if ((cifs_pdu == NULL) || (server == NULL)) - return -EINVAL; - - spin_lock(&server->srv_lock); - if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || - server->tcpStatus == CifsNeedNegotiate) { - spin_unlock(&server->srv_lock); - return rc; - } - spin_unlock(&server->srv_lock); - - if (!server->session_estab) { - memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); - return rc; - } - - cifs_pdu->Signature.Sequence.SequenceNumber = - cpu_to_le32(server->sequence_number); - cifs_pdu->Signature.Sequence.Reserved = 0; - - *pexpected_response_sequence_number = ++server->sequence_number; - ++server->sequence_number; - - rc = cifs_calc_signature(rqst, server, smb_signature); - if (rc) - memset(cifs_pdu->Signature.SecuritySignature, 0, 8); - else - memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); - - return rc; -} - -int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence) -{ - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = n_vec }; - - return cifs_sign_rqst(&rqst, server, pexpected_response_sequence); -} - -/* must be called with server->srv_mutex held */ -int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number) -{ - struct kvec iov[2]; - - iov[0].iov_base = cifs_pdu; - iov[0].iov_len = 4; - iov[1].iov_base = (char *)cifs_pdu + 4; - iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length); - - return cifs_sign_smbv(iov, 2, server, - pexpected_response_sequence_number); -} - -int cifs_verify_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - __u32 expected_sequence_number) -{ - unsigned int rc; - char server_response_sig[8]; - char what_we_think_sig_should_be[20]; - struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return -EIO; - - if (cifs_pdu == NULL || server == NULL) - return -EINVAL; - - if (!server->session_estab) - return 0; - - if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { - struct smb_com_lock_req *pSMB = - (struct smb_com_lock_req *)cifs_pdu; - if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) - return 0; - } - - /* BB what if signatures are supposed to be on for session but - server does not send one? BB */ - - /* Do not need to verify session setups with signature "BSRSPYL " */ - if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0) - cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", - cifs_pdu->Command); - - /* save off the origiginal signature so we can modify the smb and check - its signature against what the server sent */ - memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8); - - cifs_pdu->Signature.Sequence.SequenceNumber = - cpu_to_le32(expected_sequence_number); - cifs_pdu->Signature.Sequence.Reserved = 0; - - cifs_server_lock(server); - rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be); - cifs_server_unlock(server); - - if (rc) - return rc; - -/* cifs_dump_mem("what we think it should be: ", - what_we_think_sig_should_be, 16); */ - - if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) - return -EACCES; - else - return 0; - -} - -/* Build a proper attribute value/target info pairs blob. - * Fill in netbios and dns domain name and workstation name - * and client time (total five av pairs and + one end of fields indicator. - * Allocate domain name which gets freed when session struct is deallocated. - */ -static int -build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp) -{ - unsigned int dlen; - unsigned int size = 2 * sizeof(struct ntlmssp2_name); - char *defdmname = "WORKGROUP"; - unsigned char *blobptr; - struct ntlmssp2_name *attrptr; - - if (!ses->domainName) { - ses->domainName = kstrdup(defdmname, GFP_KERNEL); - if (!ses->domainName) - return -ENOMEM; - } - - dlen = strlen(ses->domainName); - - /* - * The length of this blob is two times the size of a - * structure (av pair) which holds name/size - * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) + - * unicode length of a netbios domain name - */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.len = size + 2 * dlen; - ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); - if (!ses->auth_key.response) { - ses->auth_key.len = 0; - return -ENOMEM; - } - - blobptr = ses->auth_key.response; - attrptr = (struct ntlmssp2_name *) blobptr; - - /* - * As defined in MS-NTLM 3.3.2, just this av pair field - * is sufficient as part of the temp - */ - attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); - attrptr->length = cpu_to_le16(2 * dlen); - blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); - cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp); - - return 0; -} - -/* Server has provided av pairs/target info in the type 2 challenge - * packet and we have plucked it and stored within smb session. - * We parse that blob here to find netbios domain name to be used - * as part of ntlmv2 authentication (in Target String), if not already - * specified on the command line. - * If this function returns without any error but without fetching - * domain name, authentication may fail against some server but - * may not fail against other (those who are not very particular - * about target string i.e. for some, just user name might suffice. - */ -static int -find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp) -{ - unsigned int attrsize; - unsigned int type; - unsigned int onesize = sizeof(struct ntlmssp2_name); - unsigned char *blobptr; - unsigned char *blobend; - struct ntlmssp2_name *attrptr; - - if (!ses->auth_key.len || !ses->auth_key.response) - return 0; - - blobptr = ses->auth_key.response; - blobend = blobptr + ses->auth_key.len; - - while (blobptr + onesize < blobend) { - attrptr = (struct ntlmssp2_name *) blobptr; - type = le16_to_cpu(attrptr->type); - if (type == NTLMSSP_AV_EOL) - break; - blobptr += 2; /* advance attr type */ - attrsize = le16_to_cpu(attrptr->length); - blobptr += 2; /* advance attr size */ - if (blobptr + attrsize > blobend) - break; - if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { - if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN) - break; - if (!ses->domainName) { - ses->domainName = - kmalloc(attrsize + 1, GFP_KERNEL); - if (!ses->domainName) - return -ENOMEM; - cifs_from_utf16(ses->domainName, - (__le16 *)blobptr, attrsize, attrsize, - nls_cp, NO_MAP_UNI_RSVD); - break; - } - } - blobptr += attrsize; /* advance attr value */ - } - - return 0; -} - -/* Server has provided av pairs/target info in the type 2 challenge - * packet and we have plucked it and stored within smb session. - * We parse that blob here to find the server given timestamp - * as part of ntlmv2 authentication (or local current time as - * default in case of failure) - */ -static __le64 -find_timestamp(struct cifs_ses *ses) -{ - unsigned int attrsize; - unsigned int type; - unsigned int onesize = sizeof(struct ntlmssp2_name); - unsigned char *blobptr; - unsigned char *blobend; - struct ntlmssp2_name *attrptr; - struct timespec64 ts; - - if (!ses->auth_key.len || !ses->auth_key.response) - return 0; - - blobptr = ses->auth_key.response; - blobend = blobptr + ses->auth_key.len; - - while (blobptr + onesize < blobend) { - attrptr = (struct ntlmssp2_name *) blobptr; - type = le16_to_cpu(attrptr->type); - if (type == NTLMSSP_AV_EOL) - break; - blobptr += 2; /* advance attr type */ - attrsize = le16_to_cpu(attrptr->length); - blobptr += 2; /* advance attr size */ - if (blobptr + attrsize > blobend) - break; - if (type == NTLMSSP_AV_TIMESTAMP) { - if (attrsize == sizeof(u64)) - return *((__le64 *)blobptr); - } - blobptr += attrsize; /* advance attr value */ - } - - ktime_get_real_ts64(&ts); - return cpu_to_le64(cifs_UnixTimeToNT(ts)); -} - -static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, - const struct nls_table *nls_cp) -{ - int rc = 0; - int len; - char nt_hash[CIFS_NTHASH_SIZE]; - __le16 *user; - wchar_t *domain; - wchar_t *server; - - if (!ses->server->secmech.hmacmd5) { - cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); - return -1; - } - - /* calculate md4 hash of password */ - E_md4hash(ses->password, nt_hash, nls_cp); - - rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, nt_hash, - CIFS_NTHASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not set NT Hash as a key\n", __func__); - return rc; - } - - rc = crypto_shash_init(ses->server->secmech.hmacmd5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); - return rc; - } - - /* convert ses->user_name to unicode */ - len = ses->user_name ? strlen(ses->user_name) : 0; - user = kmalloc(2 + (len * 2), GFP_KERNEL); - if (user == NULL) { - rc = -ENOMEM; - return rc; - } - - if (len) { - len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp); - UniStrupr(user); - } else { - memset(user, '\0', 2); - } - - rc = crypto_shash_update(ses->server->secmech.hmacmd5, - (char *)user, 2 * len); - kfree(user); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with user\n", __func__); - return rc; - } - - /* convert ses->domainName to unicode and uppercase */ - if (ses->domainName) { - len = strlen(ses->domainName); - - domain = kmalloc(2 + (len * 2), GFP_KERNEL); - if (domain == NULL) { - rc = -ENOMEM; - return rc; - } - len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len, - nls_cp); - rc = - crypto_shash_update(ses->server->secmech.hmacmd5, - (char *)domain, 2 * len); - kfree(domain); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with domain\n", - __func__); - return rc; - } - } else { - /* We use ses->ip_addr if no domain name available */ - len = strlen(ses->ip_addr); - - server = kmalloc(2 + (len * 2), GFP_KERNEL); - if (server == NULL) { - rc = -ENOMEM; - return rc; - } - len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, - nls_cp); - rc = - crypto_shash_update(ses->server->secmech.hmacmd5, - (char *)server, 2 * len); - kfree(server); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with server\n", - __func__); - return rc; - } - } - - rc = crypto_shash_final(ses->server->secmech.hmacmd5, - ntlmv2_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - - return rc; -} - -static int -CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) -{ - int rc; - struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *) - (ses->auth_key.response + CIFS_SESS_KEY_SIZE); - unsigned int hash_len; - - /* The MD5 hash starts at challenge_key.key */ - hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE + - offsetof(struct ntlmv2_resp, challenge.key[0])); - - if (!ses->server->secmech.hmacmd5) { - cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); - return -1; - } - - rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, - ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", - __func__); - return rc; - } - - rc = crypto_shash_init(ses->server->secmech.hmacmd5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); - return rc; - } - - if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) - memcpy(ntlmv2->challenge.key, - ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); - else - memcpy(ntlmv2->challenge.key, - ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); - rc = crypto_shash_update(ses->server->secmech.hmacmd5, - ntlmv2->challenge.key, hash_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); - return rc; - } - - /* Note that the MD5 digest over writes anon.challenge_key.key */ - rc = crypto_shash_final(ses->server->secmech.hmacmd5, - ntlmv2->ntlmv2_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - - return rc; -} - -int -setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) -{ - int rc; - int baselen; - unsigned int tilen; - struct ntlmv2_resp *ntlmv2; - char ntlmv2_hash[16]; - unsigned char *tiblob = NULL; /* target info blob */ - __le64 rsp_timestamp; - - if (nls_cp == NULL) { - cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__); - return -EINVAL; - } - - if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { - if (!ses->domainName) { - if (ses->domainAuto) { - rc = find_domain_name(ses, nls_cp); - if (rc) { - cifs_dbg(VFS, "error %d finding domain name\n", - rc); - goto setup_ntlmv2_rsp_ret; - } - } else { - ses->domainName = kstrdup("", GFP_KERNEL); - } - } - } else { - rc = build_avpair_blob(ses, nls_cp); - if (rc) { - cifs_dbg(VFS, "error %d building av pair blob\n", rc); - goto setup_ntlmv2_rsp_ret; - } - } - - /* Must be within 5 minutes of the server (or in range +/-2h - * in case of Mac OS X), so simply carry over server timestamp - * (as Windows 7 does) - */ - rsp_timestamp = find_timestamp(ses); - - baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp); - tilen = ses->auth_key.len; - tiblob = ses->auth_key.response; - - ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL); - if (!ses->auth_key.response) { - rc = -ENOMEM; - ses->auth_key.len = 0; - goto setup_ntlmv2_rsp_ret; - } - ses->auth_key.len += baselen; - - ntlmv2 = (struct ntlmv2_resp *) - (ses->auth_key.response + CIFS_SESS_KEY_SIZE); - ntlmv2->blob_signature = cpu_to_le32(0x00000101); - ntlmv2->reserved = 0; - ntlmv2->time = rsp_timestamp; - - get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); - ntlmv2->reserved2 = 0; - - memcpy(ses->auth_key.response + baselen, tiblob, tilen); - - cifs_server_lock(ses->server); - - rc = cifs_alloc_hash("hmac(md5)", &ses->server->secmech.hmacmd5); - if (rc) { - goto unlock; - } - - /* calculate ntlmv2_hash */ - rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); - if (rc) { - cifs_dbg(VFS, "Could not get v2 hash rc %d\n", rc); - goto unlock; - } - - /* calculate first part of the client response (CR1) */ - rc = CalcNTLMv2_response(ses, ntlmv2_hash); - if (rc) { - cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc); - goto unlock; - } - - /* now calculate the session key for NTLMv2 */ - rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, - ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", - __func__); - goto unlock; - } - - rc = crypto_shash_init(ses->server->secmech.hmacmd5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); - goto unlock; - } - - rc = crypto_shash_update(ses->server->secmech.hmacmd5, - ntlmv2->ntlmv2_hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); - goto unlock; - } - - rc = crypto_shash_final(ses->server->secmech.hmacmd5, - ses->auth_key.response); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - -unlock: - cifs_server_unlock(ses->server); -setup_ntlmv2_rsp_ret: - kfree_sensitive(tiblob); - - return rc; -} - -int -calc_seckey(struct cifs_ses *ses) -{ - unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ - struct arc4_ctx *ctx_arc4; - - if (fips_enabled) - return -ENODEV; - - get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); - - ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); - if (!ctx_arc4) { - cifs_dbg(VFS, "Could not allocate arc4 context\n"); - return -ENOMEM; - } - - cifs_arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE); - cifs_arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key, - CIFS_CPHTXT_SIZE); - - /* make secondary_key/nonce as session key */ - memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); - /* and make len as that of session key only */ - ses->auth_key.len = CIFS_SESS_KEY_SIZE; - - memzero_explicit(sec_key, CIFS_SESS_KEY_SIZE); - kfree_sensitive(ctx_arc4); - return 0; -} - -void -cifs_crypto_secmech_release(struct TCP_Server_Info *server) -{ - cifs_free_hash(&server->secmech.aes_cmac); - cifs_free_hash(&server->secmech.hmacsha256); - cifs_free_hash(&server->secmech.md5); - cifs_free_hash(&server->secmech.sha512); - cifs_free_hash(&server->secmech.hmacmd5); - - if (server->secmech.enc) { - crypto_free_aead(server->secmech.enc); - server->secmech.enc = NULL; - } - - if (server->secmech.dec) { - crypto_free_aead(server->secmech.dec); - server->secmech.dec = NULL; - } -} diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c deleted file mode 100644 index 078df1e2dd18..000000000000 --- a/fs/cifs/cifsfs.c +++ /dev/null @@ -1,1857 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Common Internet FileSystem (CIFS) client - * - */ - -/* Note that BB means BUGBUG (ie something to fix eventually) */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#define DECLARE_GLOBALS_HERE -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include -#include -#include "cifs_spnego.h" -#include "fscache.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#ifdef CONFIG_CIFS_SWN_UPCALL -#include "netlink.h" -#endif -#include "fs_context.h" -#include "cached_dir.h" - -/* - * DOS dates from 1980/1/1 through 2107/12/31 - * Protocol specifications indicate the range should be to 119, which - * limits maximum year to 2099. But this range has not been checked. - */ -#define SMB_DATE_MAX (127<<9 | 12<<5 | 31) -#define SMB_DATE_MIN (0<<9 | 1<<5 | 1) -#define SMB_TIME_MAX (23<<11 | 59<<5 | 29) - -int cifsFYI = 0; -bool traceSMB; -bool enable_oplocks = true; -bool linuxExtEnabled = true; -bool lookupCacheEnabled = true; -bool disable_legacy_dialects; /* false by default */ -bool enable_gcm_256 = true; -bool require_gcm_256; /* false by default */ -bool enable_negotiate_signing; /* false by default */ -unsigned int global_secflags = CIFSSEC_DEF; -/* unsigned int ntlmv2_support = 0; */ -unsigned int sign_CIFS_PDUs = 1; - -/* - * Global transaction id (XID) information - */ -unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ -unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ -unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ -spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ - -/* - * Global counters, updated atomically - */ -atomic_t sesInfoAllocCount; -atomic_t tconInfoAllocCount; -atomic_t tcpSesNextId; -atomic_t tcpSesAllocCount; -atomic_t tcpSesReconnectCount; -atomic_t tconInfoReconnectCount; - -atomic_t mid_count; -atomic_t buf_alloc_count; -atomic_t small_buf_alloc_count; -#ifdef CONFIG_CIFS_STATS2 -atomic_t total_buf_alloc_count; -atomic_t total_small_buf_alloc_count; -#endif/* STATS2 */ -struct list_head cifs_tcp_ses_list; -spinlock_t cifs_tcp_ses_lock; -static const struct super_operations cifs_super_ops; -unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; -module_param(CIFSMaxBufSize, uint, 0444); -MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header) " - "for CIFS requests. " - "Default: 16384 Range: 8192 to 130048"); -unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; -module_param(cifs_min_rcv, uint, 0444); -MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: " - "1 to 64"); -unsigned int cifs_min_small = 30; -module_param(cifs_min_small, uint, 0444); -MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 " - "Range: 2 to 256"); -unsigned int cifs_max_pending = CIFS_MAX_REQ; -module_param(cifs_max_pending, uint, 0444); -MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server for " - "CIFS/SMB1 dialect (N/A for SMB3) " - "Default: 32767 Range: 2 to 32767."); -#ifdef CONFIG_CIFS_STATS2 -unsigned int slow_rsp_threshold = 1; -module_param(slow_rsp_threshold, uint, 0644); -MODULE_PARM_DESC(slow_rsp_threshold, "Amount of time (in seconds) to wait " - "before logging that a response is delayed. " - "Default: 1 (if set to 0 disables msg)."); -#endif /* STATS2 */ - -module_param(enable_oplocks, bool, 0644); -MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1"); - -module_param(enable_gcm_256, bool, 0644); -MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: n/N/0"); - -module_param(require_gcm_256, bool, 0644); -MODULE_PARM_DESC(require_gcm_256, "Require strongest (256 bit) GCM encryption. Default: n/N/0"); - -module_param(enable_negotiate_signing, bool, 0644); -MODULE_PARM_DESC(enable_negotiate_signing, "Enable negotiating packet signing algorithm with server. Default: n/N/0"); - -module_param(disable_legacy_dialects, bool, 0644); -MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be " - "helpful to restrict the ability to " - "override the default dialects (SMB2.1, " - "SMB3 and SMB3.02) on mount with old " - "dialects (CIFS/SMB1 and SMB2) since " - "vers=1.0 (CIFS/SMB1) and vers=2.0 are weaker" - " and less secure. Default: n/N/0"); - -extern mempool_t *cifs_sm_req_poolp; -extern mempool_t *cifs_req_poolp; -extern mempool_t *cifs_mid_poolp; - -struct workqueue_struct *cifsiod_wq; -struct workqueue_struct *decrypt_wq; -struct workqueue_struct *fileinfo_put_wq; -struct workqueue_struct *cifsoplockd_wq; -struct workqueue_struct *deferredclose_wq; -__u32 cifs_lock_secret; - -/* - * Bumps refcount for cifs super block. - * Note that it should be only called if a referece to VFS super block is - * already held, e.g. in open-type syscalls context. Otherwise it can race with - * atomic_dec_and_test in deactivate_locked_super. - */ -void -cifs_sb_active(struct super_block *sb) -{ - struct cifs_sb_info *server = CIFS_SB(sb); - - if (atomic_inc_return(&server->active) == 1) - atomic_inc(&sb->s_active); -} - -void -cifs_sb_deactive(struct super_block *sb) -{ - struct cifs_sb_info *server = CIFS_SB(sb); - - if (atomic_dec_and_test(&server->active)) - deactivate_super(sb); -} - -static int -cifs_read_super(struct super_block *sb) -{ - struct inode *inode; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct timespec64 ts; - int rc = 0; - - cifs_sb = CIFS_SB(sb); - tcon = cifs_sb_master_tcon(cifs_sb); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL) - sb->s_flags |= SB_POSIXACL; - - if (tcon->snapshot_time) - sb->s_flags |= SB_RDONLY; - - if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files) - sb->s_maxbytes = MAX_LFS_FILESIZE; - else - sb->s_maxbytes = MAX_NON_LFS; - - /* - * Some very old servers like DOS and OS/2 used 2 second granularity - * (while all current servers use 100ns granularity - see MS-DTYP) - * but 1 second is the maximum allowed granularity for the VFS - * so for old servers set time granularity to 1 second while for - * everything else (current servers) set it to 100ns. - */ - if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) && - ((tcon->ses->capabilities & - tcon->ses->server->vals->cap_nt_find) == 0) && - !tcon->unix_ext) { - sb->s_time_gran = 1000000000; /* 1 second is max allowed gran */ - ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MIN), 0, 0); - sb->s_time_min = ts.tv_sec; - ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MAX), - cpu_to_le16(SMB_TIME_MAX), 0); - sb->s_time_max = ts.tv_sec; - } else { - /* - * Almost every server, including all SMB2+, uses DCE TIME - * ie 100 nanosecond units, since 1601. See MS-DTYP and MS-FSCC - */ - sb->s_time_gran = 100; - ts = cifs_NTtimeToUnix(0); - sb->s_time_min = ts.tv_sec; - ts = cifs_NTtimeToUnix(cpu_to_le64(S64_MAX)); - sb->s_time_max = ts.tv_sec; - } - - sb->s_magic = CIFS_SUPER_MAGIC; - sb->s_op = &cifs_super_ops; - sb->s_xattr = cifs_xattr_handlers; - rc = super_setup_bdi(sb); - if (rc) - goto out_no_root; - /* tune readahead according to rsize if readahead size not set on mount */ - if (cifs_sb->ctx->rsize == 0) - cifs_sb->ctx->rsize = - tcon->ses->server->ops->negotiate_rsize(tcon, cifs_sb->ctx); - if (cifs_sb->ctx->rasize) - sb->s_bdi->ra_pages = cifs_sb->ctx->rasize / PAGE_SIZE; - else - sb->s_bdi->ra_pages = cifs_sb->ctx->rsize / PAGE_SIZE; - - sb->s_blocksize = CIFS_MAX_MSGSIZE; - sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ - inode = cifs_root_iget(sb); - - if (IS_ERR(inode)) { - rc = PTR_ERR(inode); - goto out_no_root; - } - - if (tcon->nocase) - sb->s_d_op = &cifs_ci_dentry_ops; - else - sb->s_d_op = &cifs_dentry_ops; - - sb->s_root = d_make_root(inode); - if (!sb->s_root) { - rc = -ENOMEM; - goto out_no_root; - } - -#ifdef CONFIG_CIFS_NFSD_EXPORT - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { - cifs_dbg(FYI, "export ops supported\n"); - sb->s_export_op = &cifs_export_ops; - } -#endif /* CONFIG_CIFS_NFSD_EXPORT */ - - return 0; - -out_no_root: - cifs_dbg(VFS, "%s: get root inode failed\n", __func__); - return rc; -} - -static void cifs_kill_sb(struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - /* - * We ned to release all dentries for the cached directories - * before we kill the sb. - */ - if (cifs_sb->root) { - close_all_cached_dirs(cifs_sb); - - /* finally release root dentry */ - dput(cifs_sb->root); - cifs_sb->root = NULL; - } - - kill_anon_super(sb); - cifs_umount(cifs_sb); -} - -static int -cifs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int xid; - int rc = 0; - - xid = get_xid(); - - if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) > 0) - buf->f_namelen = - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); - else - buf->f_namelen = PATH_MAX; - - buf->f_fsid.val[0] = tcon->vol_serial_number; - /* are using part of create time for more randomness, see man statfs */ - buf->f_fsid.val[1] = (int)le64_to_cpu(tcon->vol_create_time); - - buf->f_files = 0; /* undefined */ - buf->f_ffree = 0; /* unlimited */ - - if (server->ops->queryfs) - rc = server->ops->queryfs(xid, tcon, cifs_sb, buf); - - free_xid(xid); - return rc; -} - -static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len) -{ - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct TCP_Server_Info *server = tcon->ses->server; - - if (server->ops->fallocate) - return server->ops->fallocate(file, tcon, mode, off, len); - - return -EOPNOTSUPP; -} - -static int cifs_permission(struct user_namespace *mnt_userns, - struct inode *inode, int mask) -{ - struct cifs_sb_info *cifs_sb; - - cifs_sb = CIFS_SB(inode->i_sb); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { - if ((mask & MAY_EXEC) && !execute_ok(inode)) - return -EACCES; - else - return 0; - } else /* file mode might have been restricted at mount time - on the client (above and beyond ACL on servers) for - servers which do not support setting and viewing mode bits, - so allowing client to check permissions is useful */ - return generic_permission(&init_user_ns, inode, mask); -} - -static struct kmem_cache *cifs_inode_cachep; -static struct kmem_cache *cifs_req_cachep; -static struct kmem_cache *cifs_mid_cachep; -static struct kmem_cache *cifs_sm_req_cachep; -mempool_t *cifs_sm_req_poolp; -mempool_t *cifs_req_poolp; -mempool_t *cifs_mid_poolp; - -static struct inode * -cifs_alloc_inode(struct super_block *sb) -{ - struct cifsInodeInfo *cifs_inode; - cifs_inode = alloc_inode_sb(sb, cifs_inode_cachep, GFP_KERNEL); - if (!cifs_inode) - return NULL; - cifs_inode->cifsAttrs = 0x20; /* default */ - cifs_inode->time = 0; - /* - * Until the file is open and we have gotten oplock info back from the - * server, can not assume caching of file data or metadata. - */ - cifs_set_oplock_level(cifs_inode, 0); - cifs_inode->flags = 0; - spin_lock_init(&cifs_inode->writers_lock); - cifs_inode->writers = 0; - cifs_inode->netfs.inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ - cifs_inode->server_eof = 0; - cifs_inode->uniqueid = 0; - cifs_inode->createtime = 0; - cifs_inode->epoch = 0; - spin_lock_init(&cifs_inode->open_file_lock); - generate_random_uuid(cifs_inode->lease_key); - cifs_inode->symlink_target = NULL; - - /* - * Can not set i_flags here - they get immediately overwritten to zero - * by the VFS. - */ - /* cifs_inode->netfs.inode.i_flags = S_NOATIME | S_NOCMTIME; */ - INIT_LIST_HEAD(&cifs_inode->openFileList); - INIT_LIST_HEAD(&cifs_inode->llist); - INIT_LIST_HEAD(&cifs_inode->deferred_closes); - spin_lock_init(&cifs_inode->deferred_lock); - return &cifs_inode->netfs.inode; -} - -static void -cifs_free_inode(struct inode *inode) -{ - struct cifsInodeInfo *cinode = CIFS_I(inode); - - if (S_ISLNK(inode->i_mode)) - kfree(cinode->symlink_target); - kmem_cache_free(cifs_inode_cachep, cinode); -} - -static void -cifs_evict_inode(struct inode *inode) -{ - truncate_inode_pages_final(&inode->i_data); - if (inode->i_state & I_PINNING_FSCACHE_WB) - cifs_fscache_unuse_inode_cookie(inode, true); - cifs_fscache_release_inode_cookie(inode); - clear_inode(inode); -} - -static void -cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server) -{ - struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; - - seq_puts(s, ",addr="); - - switch (server->dstaddr.ss_family) { - case AF_INET: - seq_printf(s, "%pI4", &sa->sin_addr.s_addr); - break; - case AF_INET6: - seq_printf(s, "%pI6", &sa6->sin6_addr.s6_addr); - if (sa6->sin6_scope_id) - seq_printf(s, "%%%u", sa6->sin6_scope_id); - break; - default: - seq_puts(s, "(unknown)"); - } - if (server->rdma) - seq_puts(s, ",rdma"); -} - -static void -cifs_show_security(struct seq_file *s, struct cifs_ses *ses) -{ - if (ses->sectype == Unspecified) { - if (ses->user_name == NULL) - seq_puts(s, ",sec=none"); - return; - } - - seq_puts(s, ",sec="); - - switch (ses->sectype) { - case NTLMv2: - seq_puts(s, "ntlmv2"); - break; - case Kerberos: - seq_puts(s, "krb5"); - break; - case RawNTLMSSP: - seq_puts(s, "ntlmssp"); - break; - default: - /* shouldn't ever happen */ - seq_puts(s, "unknown"); - break; - } - - if (ses->sign) - seq_puts(s, "i"); - - if (ses->sectype == Kerberos) - seq_printf(s, ",cruid=%u", - from_kuid_munged(&init_user_ns, ses->cred_uid)); -} - -static void -cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) -{ - seq_puts(s, ",cache="); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) - seq_puts(s, "strict"); - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) - seq_puts(s, "none"); - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) - seq_puts(s, "singleclient"); /* assume only one client access */ - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) - seq_puts(s, "ro"); /* read only caching assumed */ - else - seq_puts(s, "loose"); -} - -/* - * cifs_show_devname() is used so we show the mount device name with correct - * format (e.g. forward slashes vs. back slashes) in /proc/mounts - */ -static int cifs_show_devname(struct seq_file *m, struct dentry *root) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); - char *devname = kstrdup(cifs_sb->ctx->source, GFP_KERNEL); - - if (devname == NULL) - seq_puts(m, "none"); - else { - convert_delimiter(devname, '/'); - /* escape all spaces in share names */ - seq_escape(m, devname, " \t"); - kfree(devname); - } - return 0; -} - -/* - * cifs_show_options() is for displaying mount options in /proc/mounts. - * Not all settable options are displayed but most of the important - * ones are. - */ -static int -cifs_show_options(struct seq_file *s, struct dentry *root) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct sockaddr *srcaddr; - srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; - - seq_show_option(s, "vers", tcon->ses->server->vals->version_string); - cifs_show_security(s, tcon->ses); - cifs_show_cache_flavor(s, cifs_sb); - - if (tcon->no_lease) - seq_puts(s, ",nolease"); - if (cifs_sb->ctx->multiuser) - seq_puts(s, ",multiuser"); - else if (tcon->ses->user_name) - seq_show_option(s, "username", tcon->ses->user_name); - - if (tcon->ses->domainName && tcon->ses->domainName[0] != 0) - seq_show_option(s, "domain", tcon->ses->domainName); - - if (srcaddr->sa_family != AF_UNSPEC) { - struct sockaddr_in *saddr4; - struct sockaddr_in6 *saddr6; - saddr4 = (struct sockaddr_in *)srcaddr; - saddr6 = (struct sockaddr_in6 *)srcaddr; - if (srcaddr->sa_family == AF_INET6) - seq_printf(s, ",srcaddr=%pI6c", - &saddr6->sin6_addr); - else if (srcaddr->sa_family == AF_INET) - seq_printf(s, ",srcaddr=%pI4", - &saddr4->sin_addr.s_addr); - else - seq_printf(s, ",srcaddr=BAD-AF:%i", - (int)(srcaddr->sa_family)); - } - - seq_printf(s, ",uid=%u", - from_kuid_munged(&init_user_ns, cifs_sb->ctx->linux_uid)); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) - seq_puts(s, ",forceuid"); - else - seq_puts(s, ",noforceuid"); - - seq_printf(s, ",gid=%u", - from_kgid_munged(&init_user_ns, cifs_sb->ctx->linux_gid)); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) - seq_puts(s, ",forcegid"); - else - seq_puts(s, ",noforcegid"); - - cifs_show_address(s, tcon->ses->server); - - if (!tcon->unix_ext) - seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho", - cifs_sb->ctx->file_mode, - cifs_sb->ctx->dir_mode); - if (cifs_sb->ctx->iocharset) - seq_printf(s, ",iocharset=%s", cifs_sb->ctx->iocharset); - if (tcon->seal) - seq_puts(s, ",seal"); - else if (tcon->ses->server->ignore_signature) - seq_puts(s, ",signloosely"); - if (tcon->nocase) - seq_puts(s, ",nocase"); - if (tcon->nodelete) - seq_puts(s, ",nodelete"); - if (cifs_sb->ctx->no_sparse) - seq_puts(s, ",nosparse"); - if (tcon->local_lease) - seq_puts(s, ",locallease"); - if (tcon->retry) - seq_puts(s, ",hard"); - else - seq_puts(s, ",soft"); - if (tcon->use_persistent) - seq_puts(s, ",persistenthandles"); - else if (tcon->use_resilient) - seq_puts(s, ",resilienthandles"); - if (tcon->posix_extensions) - seq_puts(s, ",posix"); - else if (tcon->unix_ext) - seq_puts(s, ",unix"); - else - seq_puts(s, ",nounix"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) - seq_puts(s, ",nodfs"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) - seq_puts(s, ",posixpaths"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) - seq_puts(s, ",setuids"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) - seq_puts(s, ",idsfromsid"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - seq_puts(s, ",serverino"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - seq_puts(s, ",rwpidforward"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) - seq_puts(s, ",forcemand"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - seq_puts(s, ",nouser_xattr"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) - seq_puts(s, ",mapchars"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) - seq_puts(s, ",mapposix"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) - seq_puts(s, ",sfu"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - seq_puts(s, ",nobrl"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_HANDLE_CACHE) - seq_puts(s, ",nohandlecache"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) - seq_puts(s, ",modefromsid"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) - seq_puts(s, ",cifsacl"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) - seq_puts(s, ",dynperm"); - if (root->d_sb->s_flags & SB_POSIXACL) - seq_puts(s, ",acl"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) - seq_puts(s, ",mfsymlinks"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) - seq_puts(s, ",fsc"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC) - seq_puts(s, ",nostrictsync"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) - seq_puts(s, ",noperm"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) - seq_printf(s, ",backupuid=%u", - from_kuid_munged(&init_user_ns, - cifs_sb->ctx->backupuid)); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) - seq_printf(s, ",backupgid=%u", - from_kgid_munged(&init_user_ns, - cifs_sb->ctx->backupgid)); - - seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize); - seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize); - seq_printf(s, ",bsize=%u", cifs_sb->ctx->bsize); - if (cifs_sb->ctx->rasize) - seq_printf(s, ",rasize=%u", cifs_sb->ctx->rasize); - if (tcon->ses->server->min_offload) - seq_printf(s, ",esize=%u", tcon->ses->server->min_offload); - seq_printf(s, ",echo_interval=%lu", - tcon->ses->server->echo_interval / HZ); - - /* Only display the following if overridden on mount */ - if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE) - seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits); - if (tcon->ses->server->tcp_nodelay) - seq_puts(s, ",tcpnodelay"); - if (tcon->ses->server->noautotune) - seq_puts(s, ",noautotune"); - if (tcon->ses->server->noblocksnd) - seq_puts(s, ",noblocksend"); - - if (tcon->snapshot_time) - seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); - if (tcon->handle_timeout) - seq_printf(s, ",handletimeout=%u", tcon->handle_timeout); - - /* - * Display file and directory attribute timeout in seconds. - * If file and directory attribute timeout the same then actimeo - * was likely specified on mount - */ - if (cifs_sb->ctx->acdirmax == cifs_sb->ctx->acregmax) - seq_printf(s, ",actimeo=%lu", cifs_sb->ctx->acregmax / HZ); - else { - seq_printf(s, ",acdirmax=%lu", cifs_sb->ctx->acdirmax / HZ); - seq_printf(s, ",acregmax=%lu", cifs_sb->ctx->acregmax / HZ); - } - seq_printf(s, ",closetimeo=%lu", cifs_sb->ctx->closetimeo / HZ); - - if (tcon->ses->chan_max > 1) - seq_printf(s, ",multichannel,max_channels=%zu", - tcon->ses->chan_max); - - if (tcon->use_witness) - seq_puts(s, ",witness"); - - return 0; -} - -static void cifs_umount_begin(struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon; - - if (cifs_sb == NULL) - return; - - tcon = cifs_sb_master_tcon(cifs_sb); - - spin_lock(&cifs_tcp_ses_lock); - spin_lock(&tcon->tc_lock); - if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) { - /* we have other mounts to same share or we have - already tried to umount this and woken up - all waiting network requests, nothing to do */ - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - return; - } - /* - * can not set tcon->status to TID_EXITING yet since we don't know if umount -f will - * fail later (e.g. due to open files). TID_EXITING will be set just before tdis req sent - */ - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - - cifs_close_all_deferred_files(tcon); - /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ - /* cancel_notify_requests(tcon); */ - if (tcon->ses && tcon->ses->server) { - cifs_dbg(FYI, "wake up tasks now - umount begin not complete\n"); - wake_up_all(&tcon->ses->server->request_q); - wake_up_all(&tcon->ses->server->response_q); - msleep(1); /* yield */ - /* we have to kick the requests once more */ - wake_up_all(&tcon->ses->server->response_q); - msleep(1); - } - - return; -} - -static int cifs_freeze(struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon; - - if (cifs_sb == NULL) - return 0; - - tcon = cifs_sb_master_tcon(cifs_sb); - - cifs_close_all_deferred_files(tcon); - return 0; -} - -#ifdef CONFIG_CIFS_STATS2 -static int cifs_show_stats(struct seq_file *s, struct dentry *root) -{ - /* BB FIXME */ - return 0; -} -#endif - -static int cifs_write_inode(struct inode *inode, struct writeback_control *wbc) -{ - fscache_unpin_writeback(wbc, cifs_inode_cookie(inode)); - return 0; -} - -static int cifs_drop_inode(struct inode *inode) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - /* no serverino => unconditional eviction */ - return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) || - generic_drop_inode(inode); -} - -static const struct super_operations cifs_super_ops = { - .statfs = cifs_statfs, - .alloc_inode = cifs_alloc_inode, - .write_inode = cifs_write_inode, - .free_inode = cifs_free_inode, - .drop_inode = cifs_drop_inode, - .evict_inode = cifs_evict_inode, -/* .show_path = cifs_show_path, */ /* Would we ever need show path? */ - .show_devname = cifs_show_devname, -/* .delete_inode = cifs_delete_inode, */ /* Do not need above - function unless later we add lazy close of inodes or unless the - kernel forgets to call us with the same number of releases (closes) - as opens */ - .show_options = cifs_show_options, - .umount_begin = cifs_umount_begin, - .freeze_fs = cifs_freeze, -#ifdef CONFIG_CIFS_STATS2 - .show_stats = cifs_show_stats, -#endif -}; - -/* - * Get root dentry from superblock according to prefix path mount option. - * Return dentry with refcount + 1 on success and NULL otherwise. - */ -static struct dentry * -cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb) -{ - struct dentry *dentry; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - char *full_path = NULL; - char *s, *p; - char sep; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) - return dget(sb->s_root); - - full_path = cifs_build_path_to_root(ctx, cifs_sb, - cifs_sb_master_tcon(cifs_sb), 0); - if (full_path == NULL) - return ERR_PTR(-ENOMEM); - - cifs_dbg(FYI, "Get root dentry for %s\n", full_path); - - sep = CIFS_DIR_SEP(cifs_sb); - dentry = dget(sb->s_root); - s = full_path; - - do { - struct inode *dir = d_inode(dentry); - struct dentry *child; - - if (!S_ISDIR(dir->i_mode)) { - dput(dentry); - dentry = ERR_PTR(-ENOTDIR); - break; - } - - /* skip separators */ - while (*s == sep) - s++; - if (!*s) - break; - p = s++; - /* next separator */ - while (*s && *s != sep) - s++; - - child = lookup_positive_unlocked(p, dentry, s - p); - dput(dentry); - dentry = child; - } while (!IS_ERR(dentry)); - kfree(full_path); - return dentry; -} - -static int cifs_set_super(struct super_block *sb, void *data) -{ - struct cifs_mnt_data *mnt_data = data; - sb->s_fs_info = mnt_data->cifs_sb; - return set_anon_super(sb, NULL); -} - -struct dentry * -cifs_smb3_do_mount(struct file_system_type *fs_type, - int flags, struct smb3_fs_context *old_ctx) -{ - int rc; - struct super_block *sb = NULL; - struct cifs_sb_info *cifs_sb = NULL; - struct cifs_mnt_data mnt_data; - struct dentry *root; - - /* - * Prints in Kernel / CIFS log the attempted mount operation - * If CIFS_DEBUG && cifs_FYI - */ - if (cifsFYI) - cifs_dbg(FYI, "Devname: %s flags: %d\n", old_ctx->UNC, flags); - else - cifs_info("Attempting to mount %s\n", old_ctx->UNC); - - cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); - if (cifs_sb == NULL) { - root = ERR_PTR(-ENOMEM); - goto out; - } - - cifs_sb->ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); - if (!cifs_sb->ctx) { - root = ERR_PTR(-ENOMEM); - goto out; - } - rc = smb3_fs_context_dup(cifs_sb->ctx, old_ctx); - if (rc) { - root = ERR_PTR(rc); - goto out; - } - - rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL); - if (rc) { - root = ERR_PTR(rc); - goto out; - } - - rc = cifs_setup_cifs_sb(cifs_sb); - if (rc) { - root = ERR_PTR(rc); - goto out; - } - - rc = cifs_mount(cifs_sb, cifs_sb->ctx); - if (rc) { - if (!(flags & SB_SILENT)) - cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n", - rc); - root = ERR_PTR(rc); - goto out; - } - - mnt_data.ctx = cifs_sb->ctx; - mnt_data.cifs_sb = cifs_sb; - mnt_data.flags = flags; - - /* BB should we make this contingent on mount parm? */ - flags |= SB_NODIRATIME | SB_NOATIME; - - sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data); - if (IS_ERR(sb)) { - root = ERR_CAST(sb); - cifs_umount(cifs_sb); - cifs_sb = NULL; - goto out; - } - - if (sb->s_root) { - cifs_dbg(FYI, "Use existing superblock\n"); - cifs_umount(cifs_sb); - cifs_sb = NULL; - } else { - rc = cifs_read_super(sb); - if (rc) { - root = ERR_PTR(rc); - goto out_super; - } - - sb->s_flags |= SB_ACTIVE; - } - - root = cifs_get_root(cifs_sb ? cifs_sb->ctx : old_ctx, sb); - if (IS_ERR(root)) - goto out_super; - - if (cifs_sb) - cifs_sb->root = dget(root); - - cifs_dbg(FYI, "dentry root is: %p\n", root); - return root; - -out_super: - deactivate_locked_super(sb); - return root; -out: - if (cifs_sb) { - if (!sb || IS_ERR(sb)) { /* otherwise kill_sb will handle */ - kfree(cifs_sb->prepath); - smb3_cleanup_fs_context(cifs_sb->ctx); - kfree(cifs_sb); - } - } - return root; -} - - -static ssize_t -cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) -{ - ssize_t rc; - struct inode *inode = file_inode(iocb->ki_filp); - - if (iocb->ki_flags & IOCB_DIRECT) - return cifs_user_readv(iocb, iter); - - rc = cifs_revalidate_mapping(inode); - if (rc) - return rc; - - return generic_file_read_iter(iocb, iter); -} - -static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) -{ - struct inode *inode = file_inode(iocb->ki_filp); - struct cifsInodeInfo *cinode = CIFS_I(inode); - ssize_t written; - int rc; - - if (iocb->ki_filp->f_flags & O_DIRECT) { - written = cifs_user_writev(iocb, from); - if (written > 0 && CIFS_CACHE_READ(cinode)) { - cifs_zap_mapping(inode); - cifs_dbg(FYI, - "Set no oplock for inode=%p after a write operation\n", - inode); - cinode->oplock = 0; - } - return written; - } - - written = cifs_get_writer(cinode); - if (written) - return written; - - written = generic_file_write_iter(iocb, from); - - if (CIFS_CACHE_WRITE(CIFS_I(inode))) - goto out; - - rc = filemap_fdatawrite(inode->i_mapping); - if (rc) - cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n", - rc, inode); - -out: - cifs_put_writer(cinode); - return written; -} - -static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) -{ - struct cifsFileInfo *cfile = file->private_data; - struct cifs_tcon *tcon; - - /* - * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate - * the cached file length - */ - if (whence != SEEK_SET && whence != SEEK_CUR) { - int rc; - struct inode *inode = file_inode(file); - - /* - * We need to be sure that all dirty pages are written and the - * server has the newest file length. - */ - if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && - inode->i_mapping->nrpages != 0) { - rc = filemap_fdatawait(inode->i_mapping); - if (rc) { - mapping_set_error(inode->i_mapping, rc); - return rc; - } - } - /* - * Some applications poll for the file length in this strange - * way so we must seek to end on non-oplocked files by - * setting the revalidate time to zero. - */ - CIFS_I(inode)->time = 0; - - rc = cifs_revalidate_file_attr(file); - if (rc < 0) - return (loff_t)rc; - } - if (cfile && cfile->tlink) { - tcon = tlink_tcon(cfile->tlink); - if (tcon->ses->server->ops->llseek) - return tcon->ses->server->ops->llseek(file, tcon, - offset, whence); - } - return generic_file_llseek(file, offset, whence); -} - -static int -cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv) -{ - /* - * Note that this is called by vfs setlease with i_lock held to - * protect *lease from going away. - */ - struct inode *inode = file_inode(file); - struct cifsFileInfo *cfile = file->private_data; - - if (!(S_ISREG(inode->i_mode))) - return -EINVAL; - - /* Check if file is oplocked if this is request for new lease */ - if (arg == F_UNLCK || - ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || - ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode)))) - return generic_setlease(file, arg, lease, priv); - else if (tlink_tcon(cfile->tlink)->local_lease && - !CIFS_CACHE_READ(CIFS_I(inode))) - /* - * If the server claims to support oplock on this file, then we - * still need to check oplock even if the local_lease mount - * option is set, but there are servers which do not support - * oplock for which this mount option may be useful if the user - * knows that the file won't be changed on the server by anyone - * else. - */ - return generic_setlease(file, arg, lease, priv); - else - return -EAGAIN; -} - -struct file_system_type cifs_fs_type = { - .owner = THIS_MODULE, - .name = "cifs", - .init_fs_context = smb3_init_fs_context, - .parameters = smb3_fs_parameters, - .kill_sb = cifs_kill_sb, - .fs_flags = FS_RENAME_DOES_D_MOVE, -}; -MODULE_ALIAS_FS("cifs"); - -struct file_system_type smb3_fs_type = { - .owner = THIS_MODULE, - .name = "smb3", - .init_fs_context = smb3_init_fs_context, - .parameters = smb3_fs_parameters, - .kill_sb = cifs_kill_sb, - .fs_flags = FS_RENAME_DOES_D_MOVE, -}; -MODULE_ALIAS_FS("smb3"); -MODULE_ALIAS("smb3"); - -const struct inode_operations cifs_dir_inode_ops = { - .create = cifs_create, - .atomic_open = cifs_atomic_open, - .lookup = cifs_lookup, - .getattr = cifs_getattr, - .unlink = cifs_unlink, - .link = cifs_hardlink, - .mkdir = cifs_mkdir, - .rmdir = cifs_rmdir, - .rename = cifs_rename2, - .permission = cifs_permission, - .setattr = cifs_setattr, - .symlink = cifs_symlink, - .mknod = cifs_mknod, - .listxattr = cifs_listxattr, -}; - -const struct inode_operations cifs_file_inode_ops = { - .setattr = cifs_setattr, - .getattr = cifs_getattr, - .permission = cifs_permission, - .listxattr = cifs_listxattr, - .fiemap = cifs_fiemap, -}; - -const char *cifs_get_link(struct dentry *dentry, struct inode *inode, - struct delayed_call *done) -{ - char *target_path; - - target_path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!target_path) - return ERR_PTR(-ENOMEM); - - spin_lock(&inode->i_lock); - if (likely(CIFS_I(inode)->symlink_target)) { - strscpy(target_path, CIFS_I(inode)->symlink_target, PATH_MAX); - } else { - kfree(target_path); - target_path = ERR_PTR(-EOPNOTSUPP); - } - spin_unlock(&inode->i_lock); - - if (!IS_ERR(target_path)) - set_delayed_call(done, kfree_link, target_path); - - return target_path; -} - -const struct inode_operations cifs_symlink_inode_ops = { - .get_link = cifs_get_link, - .permission = cifs_permission, - .listxattr = cifs_listxattr, -}; - -static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, loff_t len, - unsigned int remap_flags) -{ - struct inode *src_inode = file_inode(src_file); - struct inode *target_inode = file_inode(dst_file); - struct cifsFileInfo *smb_file_src = src_file->private_data; - struct cifsFileInfo *smb_file_target; - struct cifs_tcon *target_tcon; - unsigned int xid; - int rc; - - if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY)) - return -EINVAL; - - cifs_dbg(FYI, "clone range\n"); - - xid = get_xid(); - - if (!src_file->private_data || !dst_file->private_data) { - rc = -EBADF; - cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); - goto out; - } - - smb_file_target = dst_file->private_data; - target_tcon = tlink_tcon(smb_file_target->tlink); - - /* - * Note: cifs case is easier than btrfs since server responsible for - * checks for proper open modes and file type and if it wants - * server could even support copy of range where source = target - */ - lock_two_nondirectories(target_inode, src_inode); - - if (len == 0) - len = src_inode->i_size - off; - - cifs_dbg(FYI, "about to flush pages\n"); - /* should we flush first and last page first */ - truncate_inode_pages_range(&target_inode->i_data, destoff, - PAGE_ALIGN(destoff + len)-1); - - if (target_tcon->ses->server->ops->duplicate_extents) - rc = target_tcon->ses->server->ops->duplicate_extents(xid, - smb_file_src, smb_file_target, off, len, destoff); - else - rc = -EOPNOTSUPP; - - /* force revalidate of size and timestamps of target file now - that target is updated on the server */ - CIFS_I(target_inode)->time = 0; - /* although unlocking in the reverse order from locking is not - strictly necessary here it is a little cleaner to be consistent */ - unlock_two_nondirectories(src_inode, target_inode); -out: - free_xid(xid); - return rc < 0 ? rc : len; -} - -ssize_t cifs_file_copychunk_range(unsigned int xid, - struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, - size_t len, unsigned int flags) -{ - struct inode *src_inode = file_inode(src_file); - struct inode *target_inode = file_inode(dst_file); - struct cifsFileInfo *smb_file_src; - struct cifsFileInfo *smb_file_target; - struct cifs_tcon *src_tcon; - struct cifs_tcon *target_tcon; - ssize_t rc; - - cifs_dbg(FYI, "copychunk range\n"); - - if (!src_file->private_data || !dst_file->private_data) { - rc = -EBADF; - cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); - goto out; - } - - rc = -EXDEV; - smb_file_target = dst_file->private_data; - smb_file_src = src_file->private_data; - src_tcon = tlink_tcon(smb_file_src->tlink); - target_tcon = tlink_tcon(smb_file_target->tlink); - - if (src_tcon->ses != target_tcon->ses) { - cifs_dbg(VFS, "source and target of copy not on same server\n"); - goto out; - } - - rc = -EOPNOTSUPP; - if (!target_tcon->ses->server->ops->copychunk_range) - goto out; - - /* - * Note: cifs case is easier than btrfs since server responsible for - * checks for proper open modes and file type and if it wants - * server could even support copy of range where source = target - */ - lock_two_nondirectories(target_inode, src_inode); - - cifs_dbg(FYI, "about to flush pages\n"); - - rc = filemap_write_and_wait_range(src_inode->i_mapping, off, - off + len - 1); - if (rc) - goto unlock; - - /* should we flush first and last page first */ - truncate_inode_pages(&target_inode->i_data, 0); - - rc = file_modified(dst_file); - if (!rc) - rc = target_tcon->ses->server->ops->copychunk_range(xid, - smb_file_src, smb_file_target, off, len, destoff); - - file_accessed(src_file); - - /* force revalidate of size and timestamps of target file now - * that target is updated on the server - */ - CIFS_I(target_inode)->time = 0; - -unlock: - /* although unlocking in the reverse order from locking is not - * strictly necessary here it is a little cleaner to be consistent - */ - unlock_two_nondirectories(src_inode, target_inode); - -out: - return rc; -} - -/* - * Directory operations under CIFS/SMB2/SMB3 are synchronous, so fsync() - * is a dummy operation. - */ -static int cifs_dir_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - cifs_dbg(FYI, "Sync directory - name: %pD datasync: 0x%x\n", - file, datasync); - - return 0; -} - -static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, - size_t len, unsigned int flags) -{ - unsigned int xid = get_xid(); - ssize_t rc; - struct cifsFileInfo *cfile = dst_file->private_data; - - if (cfile->swapfile) { - rc = -EOPNOTSUPP; - free_xid(xid); - return rc; - } - - rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff, - len, flags); - free_xid(xid); - - if (rc == -EOPNOTSUPP || rc == -EXDEV) - rc = generic_copy_file_range(src_file, off, dst_file, - destoff, len, flags); - return rc; -} - -const struct file_operations cifs_file_ops = { - .read_iter = cifs_loose_read_iter, - .write_iter = cifs_file_write_iter, - .open = cifs_open, - .release = cifs_close, - .lock = cifs_lock, - .flock = cifs_flock, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_strict_ops = { - .read_iter = cifs_strict_readv, - .write_iter = cifs_strict_writev, - .open = cifs_open, - .release = cifs_close, - .lock = cifs_lock, - .flock = cifs_flock, - .fsync = cifs_strict_fsync, - .flush = cifs_flush, - .mmap = cifs_file_strict_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_direct_ops = { - .read_iter = cifs_direct_readv, - .write_iter = cifs_direct_writev, - .open = cifs_open, - .release = cifs_close, - .lock = cifs_lock, - .flock = cifs_flock, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .llseek = cifs_llseek, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_nobrl_ops = { - .read_iter = cifs_loose_read_iter, - .write_iter = cifs_file_write_iter, - .open = cifs_open, - .release = cifs_close, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_strict_nobrl_ops = { - .read_iter = cifs_strict_readv, - .write_iter = cifs_strict_writev, - .open = cifs_open, - .release = cifs_close, - .fsync = cifs_strict_fsync, - .flush = cifs_flush, - .mmap = cifs_file_strict_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_direct_nobrl_ops = { - .read_iter = cifs_direct_readv, - .write_iter = cifs_direct_writev, - .open = cifs_open, - .release = cifs_close, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .llseek = cifs_llseek, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_dir_ops = { - .iterate_shared = cifs_readdir, - .release = cifs_closedir, - .read = generic_read_dir, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .llseek = generic_file_llseek, - .fsync = cifs_dir_fsync, -}; - -static void -cifs_init_once(void *inode) -{ - struct cifsInodeInfo *cifsi = inode; - - inode_init_once(&cifsi->netfs.inode); - init_rwsem(&cifsi->lock_sem); -} - -static int __init -cifs_init_inodecache(void) -{ - cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", - sizeof(struct cifsInodeInfo), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD|SLAB_ACCOUNT), - cifs_init_once); - if (cifs_inode_cachep == NULL) - return -ENOMEM; - - return 0; -} - -static void -cifs_destroy_inodecache(void) -{ - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ - rcu_barrier(); - kmem_cache_destroy(cifs_inode_cachep); -} - -static int -cifs_init_request_bufs(void) -{ - /* - * SMB2 maximum header size is bigger than CIFS one - no problems to - * allocate some more bytes for CIFS. - */ - size_t max_hdr_size = MAX_SMB2_HDR_SIZE; - - if (CIFSMaxBufSize < 8192) { - /* Buffer size can not be smaller than 2 * PATH_MAX since maximum - Unicode path name has to fit in any SMB/CIFS path based frames */ - CIFSMaxBufSize = 8192; - } else if (CIFSMaxBufSize > 1024*127) { - CIFSMaxBufSize = 1024 * 127; - } else { - CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/ - } -/* - cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n", - CIFSMaxBufSize, CIFSMaxBufSize); -*/ - cifs_req_cachep = kmem_cache_create_usercopy("cifs_request", - CIFSMaxBufSize + max_hdr_size, 0, - SLAB_HWCACHE_ALIGN, 0, - CIFSMaxBufSize + max_hdr_size, - NULL); - if (cifs_req_cachep == NULL) - return -ENOMEM; - - if (cifs_min_rcv < 1) - cifs_min_rcv = 1; - else if (cifs_min_rcv > 64) { - cifs_min_rcv = 64; - cifs_dbg(VFS, "cifs_min_rcv set to maximum (64)\n"); - } - - cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv, - cifs_req_cachep); - - if (cifs_req_poolp == NULL) { - kmem_cache_destroy(cifs_req_cachep); - return -ENOMEM; - } - /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and - almost all handle based requests (but not write response, nor is it - sufficient for path based requests). A smaller size would have - been more efficient (compacting multiple slab items on one 4k page) - for the case in which debug was on, but this larger size allows - more SMBs to use small buffer alloc and is still much more - efficient to alloc 1 per page off the slab compared to 17K (5page) - alloc of large cifs buffers even when page debugging is on */ - cifs_sm_req_cachep = kmem_cache_create_usercopy("cifs_small_rq", - MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, - 0, MAX_CIFS_SMALL_BUFFER_SIZE, NULL); - if (cifs_sm_req_cachep == NULL) { - mempool_destroy(cifs_req_poolp); - kmem_cache_destroy(cifs_req_cachep); - return -ENOMEM; - } - - if (cifs_min_small < 2) - cifs_min_small = 2; - else if (cifs_min_small > 256) { - cifs_min_small = 256; - cifs_dbg(FYI, "cifs_min_small set to maximum (256)\n"); - } - - cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small, - cifs_sm_req_cachep); - - if (cifs_sm_req_poolp == NULL) { - mempool_destroy(cifs_req_poolp); - kmem_cache_destroy(cifs_req_cachep); - kmem_cache_destroy(cifs_sm_req_cachep); - return -ENOMEM; - } - - return 0; -} - -static void -cifs_destroy_request_bufs(void) -{ - mempool_destroy(cifs_req_poolp); - kmem_cache_destroy(cifs_req_cachep); - mempool_destroy(cifs_sm_req_poolp); - kmem_cache_destroy(cifs_sm_req_cachep); -} - -static int init_mids(void) -{ - cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids", - sizeof(struct mid_q_entry), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (cifs_mid_cachep == NULL) - return -ENOMEM; - - /* 3 is a reasonable minimum number of simultaneous operations */ - cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep); - if (cifs_mid_poolp == NULL) { - kmem_cache_destroy(cifs_mid_cachep); - return -ENOMEM; - } - - return 0; -} - -static void destroy_mids(void) -{ - mempool_destroy(cifs_mid_poolp); - kmem_cache_destroy(cifs_mid_cachep); -} - -static int __init -init_cifs(void) -{ - int rc = 0; - cifs_proc_init(); - INIT_LIST_HEAD(&cifs_tcp_ses_list); -/* - * Initialize Global counters - */ - atomic_set(&sesInfoAllocCount, 0); - atomic_set(&tconInfoAllocCount, 0); - atomic_set(&tcpSesNextId, 0); - atomic_set(&tcpSesAllocCount, 0); - atomic_set(&tcpSesReconnectCount, 0); - atomic_set(&tconInfoReconnectCount, 0); - - atomic_set(&buf_alloc_count, 0); - atomic_set(&small_buf_alloc_count, 0); -#ifdef CONFIG_CIFS_STATS2 - atomic_set(&total_buf_alloc_count, 0); - atomic_set(&total_small_buf_alloc_count, 0); - if (slow_rsp_threshold < 1) - cifs_dbg(FYI, "slow_response_threshold msgs disabled\n"); - else if (slow_rsp_threshold > 32767) - cifs_dbg(VFS, - "slow response threshold set higher than recommended (0 to 32767)\n"); -#endif /* CONFIG_CIFS_STATS2 */ - - atomic_set(&mid_count, 0); - GlobalCurrentXid = 0; - GlobalTotalActiveXid = 0; - GlobalMaxActiveXid = 0; - spin_lock_init(&cifs_tcp_ses_lock); - spin_lock_init(&GlobalMid_Lock); - - cifs_lock_secret = get_random_u32(); - - if (cifs_max_pending < 2) { - cifs_max_pending = 2; - cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); - } else if (cifs_max_pending > CIFS_MAX_REQ) { - cifs_max_pending = CIFS_MAX_REQ; - cifs_dbg(FYI, "cifs_max_pending set to max of %u\n", - CIFS_MAX_REQ); - } - - cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!cifsiod_wq) { - rc = -ENOMEM; - goto out_clean_proc; - } - - /* - * Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3) - * so that we don't launch too many worker threads but - * Documentation/core-api/workqueue.rst recommends setting it to 0 - */ - - /* WQ_UNBOUND allows decrypt tasks to run on any CPU */ - decrypt_wq = alloc_workqueue("smb3decryptd", - WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!decrypt_wq) { - rc = -ENOMEM; - goto out_destroy_cifsiod_wq; - } - - fileinfo_put_wq = alloc_workqueue("cifsfileinfoput", - WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!fileinfo_put_wq) { - rc = -ENOMEM; - goto out_destroy_decrypt_wq; - } - - cifsoplockd_wq = alloc_workqueue("cifsoplockd", - WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!cifsoplockd_wq) { - rc = -ENOMEM; - goto out_destroy_fileinfo_put_wq; - } - - deferredclose_wq = alloc_workqueue("deferredclose", - WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!deferredclose_wq) { - rc = -ENOMEM; - goto out_destroy_cifsoplockd_wq; - } - - rc = cifs_init_inodecache(); - if (rc) - goto out_destroy_deferredclose_wq; - - rc = init_mids(); - if (rc) - goto out_destroy_inodecache; - - rc = cifs_init_request_bufs(); - if (rc) - goto out_destroy_mids; - -#ifdef CONFIG_CIFS_DFS_UPCALL - rc = dfs_cache_init(); - if (rc) - goto out_destroy_request_bufs; -#endif /* CONFIG_CIFS_DFS_UPCALL */ -#ifdef CONFIG_CIFS_UPCALL - rc = init_cifs_spnego(); - if (rc) - goto out_destroy_dfs_cache; -#endif /* CONFIG_CIFS_UPCALL */ -#ifdef CONFIG_CIFS_SWN_UPCALL - rc = cifs_genl_init(); - if (rc) - goto out_register_key_type; -#endif /* CONFIG_CIFS_SWN_UPCALL */ - - rc = init_cifs_idmap(); - if (rc) - goto out_cifs_swn_init; - - rc = register_filesystem(&cifs_fs_type); - if (rc) - goto out_init_cifs_idmap; - - rc = register_filesystem(&smb3_fs_type); - if (rc) { - unregister_filesystem(&cifs_fs_type); - goto out_init_cifs_idmap; - } - - return 0; - -out_init_cifs_idmap: - exit_cifs_idmap(); -out_cifs_swn_init: -#ifdef CONFIG_CIFS_SWN_UPCALL - cifs_genl_exit(); -out_register_key_type: -#endif -#ifdef CONFIG_CIFS_UPCALL - exit_cifs_spnego(); -out_destroy_dfs_cache: -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL - dfs_cache_destroy(); -out_destroy_request_bufs: -#endif - cifs_destroy_request_bufs(); -out_destroy_mids: - destroy_mids(); -out_destroy_inodecache: - cifs_destroy_inodecache(); -out_destroy_deferredclose_wq: - destroy_workqueue(deferredclose_wq); -out_destroy_cifsoplockd_wq: - destroy_workqueue(cifsoplockd_wq); -out_destroy_fileinfo_put_wq: - destroy_workqueue(fileinfo_put_wq); -out_destroy_decrypt_wq: - destroy_workqueue(decrypt_wq); -out_destroy_cifsiod_wq: - destroy_workqueue(cifsiod_wq); -out_clean_proc: - cifs_proc_clean(); - return rc; -} - -static void __exit -exit_cifs(void) -{ - cifs_dbg(NOISY, "exit_smb3\n"); - unregister_filesystem(&cifs_fs_type); - unregister_filesystem(&smb3_fs_type); - cifs_dfs_release_automount_timer(); - exit_cifs_idmap(); -#ifdef CONFIG_CIFS_SWN_UPCALL - cifs_genl_exit(); -#endif -#ifdef CONFIG_CIFS_UPCALL - exit_cifs_spnego(); -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL - dfs_cache_destroy(); -#endif - cifs_destroy_request_bufs(); - destroy_mids(); - cifs_destroy_inodecache(); - destroy_workqueue(deferredclose_wq); - destroy_workqueue(cifsoplockd_wq); - destroy_workqueue(decrypt_wq); - destroy_workqueue(fileinfo_put_wq); - destroy_workqueue(cifsiod_wq); - cifs_proc_clean(); -} - -MODULE_AUTHOR("Steve French"); -MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */ -MODULE_DESCRIPTION - ("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and " - "also older servers complying with the SNIA CIFS Specification)"); -MODULE_VERSION(CIFS_VERSION); -MODULE_SOFTDEP("ecb"); -MODULE_SOFTDEP("hmac"); -MODULE_SOFTDEP("md5"); -MODULE_SOFTDEP("nls"); -MODULE_SOFTDEP("aes"); -MODULE_SOFTDEP("cmac"); -MODULE_SOFTDEP("sha256"); -MODULE_SOFTDEP("sha512"); -MODULE_SOFTDEP("aead2"); -MODULE_SOFTDEP("ccm"); -MODULE_SOFTDEP("gcm"); -module_init(init_cifs) -module_exit(exit_cifs) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h deleted file mode 100644 index b6c38896fb2d..000000000000 --- a/fs/cifs/cifsfs.h +++ /dev/null @@ -1,161 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002, 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFSFS_H -#define _CIFSFS_H - -#include - -#define ROOT_I 2 - -/* - * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down - * so that it will fit. We use hash_64 to convert the value to 31 bits, and - * then add 1, to ensure that we don't end up with a 0 as the value. - */ -static inline ino_t -cifs_uniqueid_to_ino_t(u64 fileid) -{ - if ((sizeof(ino_t)) < (sizeof(u64))) - return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1; - - return (ino_t)fileid; - -} - -static inline void cifs_set_time(struct dentry *dentry, unsigned long time) -{ - dentry->d_fsdata = (void *) time; -} - -static inline unsigned long cifs_get_time(struct dentry *dentry) -{ - return (unsigned long) dentry->d_fsdata; -} - -extern struct file_system_type cifs_fs_type, smb3_fs_type; -extern const struct address_space_operations cifs_addr_ops; -extern const struct address_space_operations cifs_addr_ops_smallbuf; - -/* Functions related to super block operations */ -extern void cifs_sb_active(struct super_block *sb); -extern void cifs_sb_deactive(struct super_block *sb); - -/* Functions related to inodes */ -extern const struct inode_operations cifs_dir_inode_ops; -extern struct inode *cifs_root_iget(struct super_block *); -extern int cifs_create(struct user_namespace *, struct inode *, - struct dentry *, umode_t, bool excl); -extern int cifs_atomic_open(struct inode *, struct dentry *, - struct file *, unsigned, umode_t); -extern struct dentry *cifs_lookup(struct inode *, struct dentry *, - unsigned int); -extern int cifs_unlink(struct inode *dir, struct dentry *dentry); -extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); -extern int cifs_mknod(struct user_namespace *, struct inode *, struct dentry *, - umode_t, dev_t); -extern int cifs_mkdir(struct user_namespace *, struct inode *, struct dentry *, - umode_t); -extern int cifs_rmdir(struct inode *, struct dentry *); -extern int cifs_rename2(struct user_namespace *, struct inode *, - struct dentry *, struct inode *, struct dentry *, - unsigned int); -extern int cifs_revalidate_file_attr(struct file *filp); -extern int cifs_revalidate_dentry_attr(struct dentry *); -extern int cifs_revalidate_file(struct file *filp); -extern int cifs_revalidate_dentry(struct dentry *); -extern int cifs_invalidate_mapping(struct inode *inode); -extern int cifs_revalidate_mapping(struct inode *inode); -extern int cifs_zap_mapping(struct inode *inode); -extern int cifs_getattr(struct user_namespace *, const struct path *, - struct kstat *, u32, unsigned int); -extern int cifs_setattr(struct user_namespace *, struct dentry *, - struct iattr *); -extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start, - u64 len); - -extern const struct inode_operations cifs_file_inode_ops; -extern const struct inode_operations cifs_symlink_inode_ops; -extern const struct inode_operations cifs_dfs_referral_inode_operations; - - -/* Functions related to files and directories */ -extern const struct file_operations cifs_file_ops; -extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ -extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */ -extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */ -extern const struct file_operations cifs_file_direct_nobrl_ops; -extern const struct file_operations cifs_file_strict_nobrl_ops; -extern int cifs_open(struct inode *inode, struct file *file); -extern int cifs_close(struct inode *inode, struct file *file); -extern int cifs_closedir(struct inode *inode, struct file *file); -extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to); -extern ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to); -extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); -extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from); -extern ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from); -extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); -extern int cifs_flock(struct file *pfile, int cmd, struct file_lock *plock); -extern int cifs_lock(struct file *, int, struct file_lock *); -extern int cifs_fsync(struct file *, loff_t, loff_t, int); -extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int); -extern int cifs_flush(struct file *, fl_owner_t id); -extern int cifs_file_mmap(struct file * , struct vm_area_struct *); -extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *); -extern const struct file_operations cifs_dir_ops; -extern int cifs_dir_open(struct inode *inode, struct file *file); -extern int cifs_readdir(struct file *file, struct dir_context *ctx); - -/* Functions related to dir entries */ -extern const struct dentry_operations cifs_dentry_ops; -extern const struct dentry_operations cifs_ci_dentry_ops; - -#ifdef CONFIG_CIFS_DFS_UPCALL -extern struct vfsmount *cifs_dfs_d_automount(struct path *path); -#else -static inline struct vfsmount *cifs_dfs_d_automount(struct path *path) -{ - return ERR_PTR(-EREMOTE); -} -#endif - -/* Functions related to symlinks */ -extern const char *cifs_get_link(struct dentry *, struct inode *, - struct delayed_call *); -extern int cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, const char *symname); - -#ifdef CONFIG_CIFS_XATTR -extern const struct xattr_handler *cifs_xattr_handlers[]; -extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); -#else -# define cifs_xattr_handlers NULL -# define cifs_listxattr NULL -#endif - -extern ssize_t cifs_file_copychunk_range(unsigned int xid, - struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, - size_t len, unsigned int flags); - -extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -extern void cifs_setsize(struct inode *inode, loff_t offset); -extern int cifs_truncate_page(struct address_space *mapping, loff_t from); - -struct smb3_fs_context; -extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, - int flags, struct smb3_fs_context *ctx); - -#ifdef CONFIG_CIFS_NFSD_EXPORT -extern const struct export_operations cifs_export_ops; -#endif /* CONFIG_CIFS_NFSD_EXPORT */ - -/* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 40 -#define CIFS_VERSION "2.40" -#endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h deleted file mode 100644 index 6ec1a34738e2..000000000000 --- a/fs/cifs/cifsglob.h +++ /dev/null @@ -1,2208 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) - * - */ -#ifndef _CIFS_GLOB_H -#define _CIFS_GLOB_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifs_fs_sb.h" -#include "cifsacl.h" -#include -#include -#include -#include "../smbfs_common/smb2pdu.h" -#include "smb2pdu.h" - -#define SMB_PATH_MAX 260 -#define CIFS_PORT 445 -#define RFC1001_PORT 139 - -/* - * The sizes of various internal tables and strings - */ -#define MAX_UID_INFO 16 -#define MAX_SES_INFO 2 -#define MAX_TCON_INFO 4 - -#define MAX_TREE_SIZE (2 + CIFS_NI_MAXHOST + 1 + CIFS_MAX_SHARE_LEN + 1) - -#define CIFS_MIN_RCV_POOL 4 - -#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */ -/* - * default attribute cache timeout (jiffies) - */ -#define CIFS_DEF_ACTIMEO (1 * HZ) - -/* - * max attribute cache timeout (jiffies) - 2^30 - */ -#define CIFS_MAX_ACTIMEO (1 << 30) - -/* - * Max persistent and resilient handle timeout (milliseconds). - * Windows durable max was 960000 (16 minutes) - */ -#define SMB3_MAX_HANDLE_TIMEOUT 960000 - -/* - * MAX_REQ is the maximum number of requests that WE will send - * on one socket concurrently. - */ -#define CIFS_MAX_REQ 32767 - -#define RFC1001_NAME_LEN 15 -#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1) - -/* maximum length of ip addr as a string (including ipv6 and sctp) */ -#define SERVER_NAME_LENGTH 80 -#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) - -/* echo interval in seconds */ -#define SMB_ECHO_INTERVAL_MIN 1 -#define SMB_ECHO_INTERVAL_MAX 600 -#define SMB_ECHO_INTERVAL_DEFAULT 60 - -/* dns resolution intervals in seconds */ -#define SMB_DNS_RESOLVE_INTERVAL_MIN 120 -#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600 - -/* smb multichannel query server interfaces interval in seconds */ -#define SMB_INTERFACE_POLL_INTERVAL 600 - -/* maximum number of PDUs in one compound */ -#define MAX_COMPOUND 5 - -/* - * Default number of credits to keep available for SMB3. - * This value is chosen somewhat arbitrarily. The Windows client - * defaults to 128 credits, the Windows server allows clients up to - * 512 credits (or 8K for later versions), and the NetApp server - * does not limit clients at all. Choose a high enough default value - * such that the client shouldn't limit performance, but allow mount - * to override (until you approach 64K, where we limit credits to 65000 - * to reduce possibility of seeing more server credit overflow bugs. - */ -#define SMB2_MAX_CREDITS_AVAILABLE 32000 - -#include "cifspdu.h" - -#ifndef XATTR_DOS_ATTRIB -#define XATTR_DOS_ATTRIB "user.DOSATTRIB" -#endif - -#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */ - -/* - * CIFS vfs client Status information (based on what we know.) - */ - -/* associated with each connection */ -enum statusEnum { - CifsNew = 0, - CifsGood, - CifsExiting, - CifsNeedReconnect, - CifsNeedNegotiate, - CifsInNegotiate, -}; - -/* associated with each smb session */ -enum ses_status_enum { - SES_NEW = 0, - SES_GOOD, - SES_EXITING, - SES_NEED_RECON, - SES_IN_SETUP -}; - -/* associated with each tree connection to the server */ -enum tid_status_enum { - TID_NEW = 0, - TID_GOOD, - TID_EXITING, - TID_NEED_RECON, - TID_NEED_TCON, - TID_IN_TCON, - TID_NEED_FILES_INVALIDATE, /* currently unused */ - TID_IN_FILES_INVALIDATE -}; - -enum securityEnum { - Unspecified = 0, /* not specified */ - NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ - RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ - Kerberos, /* Kerberos via SPNEGO */ -}; - -struct session_key { - unsigned int len; - char *response; -}; - -/* crypto hashing related structure/fields, not specific to a sec mech */ -struct cifs_secmech { - struct shash_desc *hmacmd5; /* hmacmd5 hash function, for NTLMv2/CR1 hashes */ - struct shash_desc *md5; /* md5 hash function, for CIFS/SMB1 signatures */ - struct shash_desc *hmacsha256; /* hmac-sha256 hash function, for SMB2 signatures */ - struct shash_desc *sha512; /* sha512 hash function, for SMB3.1.1 preauth hash */ - struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */ - - struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */ - struct crypto_aead *dec; /* smb3 decryption AEAD TFM (AES-CCM and AES-GCM) */ -}; - -/* per smb session structure/fields */ -struct ntlmssp_auth { - bool sesskey_per_smbsess; /* whether session key is per smb session */ - __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */ - __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */ - unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ - char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlmssp */ -}; - -struct cifs_cred { - int uid; - int gid; - int mode; - int cecount; - struct cifs_sid osid; - struct cifs_sid gsid; - struct cifs_ntace *ntaces; - struct cifs_ace *aces; -}; - -struct cifs_open_info_data { - char *symlink_target; - union { - struct smb2_file_all_info fi; - struct smb311_posix_qinfo posix_fi; - }; -}; - -static inline void cifs_free_open_info(struct cifs_open_info_data *data) -{ - kfree(data->symlink_target); -} - -/* - ***************************************************************** - * Except the CIFS PDUs themselves all the - * globally interesting structs should go here - ***************************************************************** - */ - -/* - * A smb_rqst represents a complete request to be issued to a server. It's - * formed by a kvec array, followed by an array of pages. Page data is assumed - * to start at the beginning of the first page. - */ -struct smb_rqst { - struct kvec *rq_iov; /* array of kvecs */ - unsigned int rq_nvec; /* number of kvecs in array */ - struct page **rq_pages; /* pointer to array of page ptrs */ - unsigned int rq_offset; /* the offset to the 1st page */ - unsigned int rq_npages; /* number pages in array */ - unsigned int rq_pagesz; /* page size to use */ - unsigned int rq_tailsz; /* length of last page */ -}; - -struct mid_q_entry; -struct TCP_Server_Info; -struct cifsFileInfo; -struct cifs_ses; -struct cifs_tcon; -struct dfs_info3_param; -struct cifs_fattr; -struct smb3_fs_context; -struct cifs_fid; -struct cifs_readdata; -struct cifs_writedata; -struct cifs_io_parms; -struct cifs_search_info; -struct cifsInodeInfo; -struct cifs_open_parms; -struct cifs_credits; - -struct smb_version_operations { - int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *, - struct mid_q_entry *); - bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); - /* setup request: allocate mid, sign message */ - struct mid_q_entry *(*setup_request)(struct cifs_ses *, - struct TCP_Server_Info *, - struct smb_rqst *); - /* setup async request: allocate mid, sign message */ - struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *, - struct smb_rqst *); - /* check response: verify signature, map error */ - int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, - bool); - void (*add_credits)(struct TCP_Server_Info *server, - const struct cifs_credits *credits, - const int optype); - void (*set_credits)(struct TCP_Server_Info *, const int); - int * (*get_credits_field)(struct TCP_Server_Info *, const int); - unsigned int (*get_credits)(struct mid_q_entry *); - __u64 (*get_next_mid)(struct TCP_Server_Info *); - void (*revert_current_mid)(struct TCP_Server_Info *server, - const unsigned int val); - /* data offset from read response message */ - unsigned int (*read_data_offset)(char *); - /* - * Data length from read response message - * When in_remaining is true, the returned data length is in - * message field DataRemaining for out-of-band data read (e.g through - * Memory Registration RDMA write in SMBD). - * Otherwise, the returned data length is in message field DataLength. - */ - unsigned int (*read_data_length)(char *, bool in_remaining); - /* map smb to linux error */ - int (*map_error)(char *, bool); - /* find mid corresponding to the response message */ - struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *); - void (*dump_detail)(void *buf, struct TCP_Server_Info *ptcp_info); - void (*clear_stats)(struct cifs_tcon *); - void (*print_stats)(struct seq_file *m, struct cifs_tcon *); - void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); - /* verify the message */ - int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); - bool (*is_oplock_break)(char *, struct TCP_Server_Info *); - int (*handle_cancelled_mid)(struct mid_q_entry *, struct TCP_Server_Info *); - void (*downgrade_oplock)(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache); - /* process transaction2 response */ - bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, - char *, int); - /* check if we need to negotiate */ - bool (*need_neg)(struct TCP_Server_Info *); - /* negotiate to the server */ - int (*negotiate)(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); - /* set negotiated write size */ - unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); - /* set negotiated read size */ - unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); - /* setup smb sessionn */ - int (*sess_setup)(const unsigned int, struct cifs_ses *, - struct TCP_Server_Info *server, - const struct nls_table *); - /* close smb session */ - int (*logoff)(const unsigned int, struct cifs_ses *); - /* connect to a server share */ - int (*tree_connect)(const unsigned int, struct cifs_ses *, const char *, - struct cifs_tcon *, const struct nls_table *); - /* close tree connecion */ - int (*tree_disconnect)(const unsigned int, struct cifs_tcon *); - /* get DFS referrals */ - int (*get_dfs_refer)(const unsigned int, struct cifs_ses *, - const char *, struct dfs_info3_param **, - unsigned int *, const struct nls_table *, int); - /* informational QFS call */ - void (*qfs_tcon)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *); - /* check if a path is accessible or not */ - int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const char *); - /* query path data from the server */ - int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); - /* query file data from the server */ - int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct cifs_open_info_data *data); - /* query reparse tag from srv to determine which type of special file */ - int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *path, - __u32 *reparse_tag); - /* get server index number */ - int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid, - struct cifs_open_info_data *data); - /* set size by path */ - int (*set_path_size)(const unsigned int, struct cifs_tcon *, - const char *, __u64, struct cifs_sb_info *, bool); - /* set size by file handle */ - int (*set_file_size)(const unsigned int, struct cifs_tcon *, - struct cifsFileInfo *, __u64, bool); - /* set attributes */ - int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *, - const unsigned int); - int (*set_compression)(const unsigned int, struct cifs_tcon *, - struct cifsFileInfo *); - /* check if we can send an echo or nor */ - bool (*can_echo)(struct TCP_Server_Info *); - /* send echo request */ - int (*echo)(struct TCP_Server_Info *); - /* create directory */ - int (*posix_mkdir)(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *full_path, - struct cifs_sb_info *cifs_sb); - int (*mkdir)(const unsigned int xid, struct inode *inode, umode_t mode, - struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *sb); - /* set info on created directory */ - void (*mkdir_setinfo)(struct inode *, const char *, - struct cifs_sb_info *, struct cifs_tcon *, - const unsigned int); - /* remove directory */ - int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *, - struct cifs_sb_info *); - /* unlink file */ - int (*unlink)(const unsigned int, struct cifs_tcon *, const char *, - struct cifs_sb_info *); - /* open, rename and delete file */ - int (*rename_pending_delete)(const char *, struct dentry *, - const unsigned int); - /* send rename request */ - int (*rename)(const unsigned int, struct cifs_tcon *, const char *, - const char *, struct cifs_sb_info *); - /* send create hardlink request */ - int (*create_hardlink)(const unsigned int, struct cifs_tcon *, - const char *, const char *, - struct cifs_sb_info *); - /* query symlink target */ - int (*query_symlink)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const char *, - char **, bool); - /* open a file for non-posix mounts */ - int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, - void *buf); - /* set fid protocol-specific info */ - void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); - /* close a file */ - void (*close)(const unsigned int, struct cifs_tcon *, - struct cifs_fid *); - /* close a file, returning file attributes and timestamps */ - void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *pfile_info); - /* send a flush request to the server */ - int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *); - /* async read from the server */ - int (*async_readv)(struct cifs_readdata *); - /* async write to the server */ - int (*async_writev)(struct cifs_writedata *, - void (*release)(struct kref *)); - /* sync read from the server */ - int (*sync_read)(const unsigned int, struct cifs_fid *, - struct cifs_io_parms *, unsigned int *, char **, - int *); - /* sync write to the server */ - int (*sync_write)(const unsigned int, struct cifs_fid *, - struct cifs_io_parms *, unsigned int *, struct kvec *, - unsigned long); - /* open dir, start readdir */ - int (*query_dir_first)(const unsigned int, struct cifs_tcon *, - const char *, struct cifs_sb_info *, - struct cifs_fid *, __u16, - struct cifs_search_info *); - /* continue readdir */ - int (*query_dir_next)(const unsigned int, struct cifs_tcon *, - struct cifs_fid *, - __u16, struct cifs_search_info *srch_inf); - /* close dir */ - int (*close_dir)(const unsigned int, struct cifs_tcon *, - struct cifs_fid *); - /* calculate a size of SMB message */ - unsigned int (*calc_smb_size)(void *buf); - /* check for STATUS_PENDING and process the response if yes */ - bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server); - /* check for STATUS_NETWORK_SESSION_EXPIRED */ - bool (*is_session_expired)(char *); - /* send oplock break response */ - int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid, - __u16 net_fid, struct cifsInodeInfo *cifs_inode); - /* query remote filesystem */ - int (*queryfs)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, struct kstatfs *); - /* send mandatory brlock to the server */ - int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64, - __u64, __u32, int, int, bool); - /* unlock range of mandatory locks */ - int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *, - const unsigned int); - /* push brlocks from the cache to the server */ - int (*push_mand_locks)(struct cifsFileInfo *); - /* get lease key of the inode */ - void (*get_lease_key)(struct inode *, struct cifs_fid *); - /* set lease key of the inode */ - void (*set_lease_key)(struct inode *, struct cifs_fid *); - /* generate new lease key */ - void (*new_lease_key)(struct cifs_fid *); - int (*generate_signingkey)(struct cifs_ses *ses, - struct TCP_Server_Info *server); - int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *, - bool allocate_crypto); - int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, - struct cifsFileInfo *src_file); - int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *src_file, void __user *); - int (*notify)(const unsigned int xid, struct file *pfile, - void __user *pbuf, bool return_changes); - int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const unsigned char *, - char *, unsigned int *); - int (*create_mf_symlink)(unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const unsigned char *, - char *, unsigned int *); - /* if we can do cache read operations */ - bool (*is_read_op)(__u32); - /* set oplock level for the inode */ - void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int, - bool *); - /* create lease context buffer for CREATE request */ - char * (*create_lease_buf)(u8 *lease_key, u8 oplock); - /* parse lease context buffer and return oplock/epoch info */ - __u8 (*parse_lease_buf)(void *buf, unsigned int *epoch, char *lkey); - ssize_t (*copychunk_range)(const unsigned int, - struct cifsFileInfo *src_file, - struct cifsFileInfo *target_file, - u64 src_off, u64 len, u64 dest_off); - int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src, - struct cifsFileInfo *target_file, u64 src_off, u64 len, - u64 dest_off); - int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); - ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *, - const unsigned char *, const unsigned char *, char *, - size_t, struct cifs_sb_info *); - int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *, - const char *, const void *, const __u16, - const struct nls_table *, struct cifs_sb_info *); - struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *, - const char *, u32 *, u32); - struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *, - const struct cifs_fid *, u32 *, u32); - int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, - int); - /* writepages retry size */ - unsigned int (*wp_retry_size)(struct inode *); - /* get mtu credits */ - int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, - unsigned int *, struct cifs_credits *); - /* adjust previously taken mtu credits to request size */ - int (*adjust_credits)(struct TCP_Server_Info *server, - struct cifs_credits *credits, - const unsigned int payload_size); - /* check if we need to issue closedir */ - bool (*dir_needs_close)(struct cifsFileInfo *); - long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, - loff_t); - /* init transform request - used for encryption for now */ - int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst, - struct smb_rqst *, struct smb_rqst *); - int (*is_transform_hdr)(void *buf); - int (*receive_transform)(struct TCP_Server_Info *, - struct mid_q_entry **, char **, int *); - enum securityEnum (*select_sectype)(struct TCP_Server_Info *, - enum securityEnum); - int (*next_header)(char *); - /* ioctl passthrough for query_info */ - int (*ioctl_query_info)(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - __le16 *path, int is_dir, - unsigned long p); - /* make unix special files (block, char, fifo, socket) */ - int (*make_node)(unsigned int xid, - struct inode *inode, - struct dentry *dentry, - struct cifs_tcon *tcon, - const char *full_path, - umode_t mode, - dev_t device_number); - /* version specific fiemap implementation */ - int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *, - struct fiemap_extent_info *, u64, u64); - /* version specific llseek implementation */ - loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int); - /* Check for STATUS_IO_TIMEOUT */ - bool (*is_status_io_timeout)(char *buf); - /* Check for STATUS_NETWORK_NAME_DELETED */ - void (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); -}; - -struct smb_version_values { - char *version_string; - __u16 protocol_id; - __u32 req_capabilities; - __u32 large_lock_type; - __u32 exclusive_lock_type; - __u32 shared_lock_type; - __u32 unlock_lock_type; - size_t header_preamble_size; - size_t header_size; - size_t max_header_size; - size_t read_rsp_size; - __le16 lock_cmd; - unsigned int cap_unix; - unsigned int cap_nt_find; - unsigned int cap_large_files; - __u16 signing_enabled; - __u16 signing_required; - size_t create_lease_size; -}; - -#define HEADER_SIZE(server) (server->vals->header_size) -#define MAX_HEADER_SIZE(server) (server->vals->max_header_size) -#define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size) -#define MID_HEADER_SIZE(server) (HEADER_SIZE(server) - 1 - HEADER_PREAMBLE_SIZE(server)) - -/** - * CIFS superblock mount flags (mnt_cifs_flags) to consider when - * trying to reuse existing superblock for a new mount - */ -#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ - CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \ - CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \ - CIFS_MOUNT_MAP_SFM_CHR | \ - CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \ - CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \ - CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \ - CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \ - CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \ - CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ - CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ - CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ - CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \ - CIFS_MOUNT_RO_CACHE | CIFS_MOUNT_RW_CACHE) - -/** - * Generic VFS superblock mount flags (s_flags) to consider when - * trying to reuse existing superblock for a new mount - */ -#define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \ - SB_NODEV | SB_SYNCHRONOUS) - -struct cifs_mnt_data { - struct cifs_sb_info *cifs_sb; - struct smb3_fs_context *ctx; - int flags; -}; - -static inline unsigned int -get_rfc1002_length(void *buf) -{ - return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; -} - -static inline void -inc_rfc1001_len(void *buf, int count) -{ - be32_add_cpu((__be32 *)buf, count); -} - -struct TCP_Server_Info { - struct list_head tcp_ses_list; - struct list_head smb_ses_list; - spinlock_t srv_lock; /* protect anything here that is not protected */ - __u64 conn_id; /* connection identifier (useful for debugging) */ - int srv_count; /* reference counter */ - /* 15 character server name + 0x20 16th byte indicating type = srv */ - char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; - struct smb_version_operations *ops; - struct smb_version_values *vals; - /* updates to tcpStatus protected by cifs_tcp_ses_lock */ - enum statusEnum tcpStatus; /* what we think the status is */ - char *hostname; /* hostname portion of UNC string */ - struct socket *ssocket; - struct sockaddr_storage dstaddr; - struct sockaddr_storage srcaddr; /* locally bind to this IP */ -#ifdef CONFIG_NET_NS - struct net *net; -#endif - wait_queue_head_t response_q; - wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ - spinlock_t mid_lock; /* protect mid queue and it's entries */ - struct list_head pending_mid_q; - bool noblocksnd; /* use blocking sendmsg */ - bool noautotune; /* do not autotune send buf sizes */ - bool nosharesock; - bool tcp_nodelay; - unsigned int credits; /* send no more requests at once */ - unsigned int max_credits; /* can override large 32000 default at mnt */ - unsigned int in_flight; /* number of requests on the wire to server */ - unsigned int max_in_flight; /* max number of requests that were on wire */ - spinlock_t req_lock; /* protect the two values above */ - struct mutex _srv_mutex; - unsigned int nofs_flag; - struct task_struct *tsk; - char server_GUID[16]; - __u16 sec_mode; - bool sign; /* is signing enabled on this connection? */ - bool ignore_signature:1; /* skip validation of signatures in SMB2/3 rsp */ - bool session_estab; /* mark when very first sess is established */ - int echo_credits; /* echo reserved slots */ - int oplock_credits; /* oplock break reserved slots */ - bool echoes:1; /* enable echoes */ - __u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */ - u16 dialect; /* dialect index that server chose */ - bool oplocks:1; /* enable oplocks */ - unsigned int maxReq; /* Clients should submit no more */ - /* than maxReq distinct unanswered SMBs to the server when using */ - /* multiplexed reads or writes (for SMB1/CIFS only, not SMB2/SMB3) */ - unsigned int maxBuf; /* maxBuf specifies the maximum */ - /* message size the server can send or receive for non-raw SMBs */ - /* maxBuf is returned by SMB NegotiateProtocol so maxBuf is only 0 */ - /* when socket is setup (and during reconnect) before NegProt sent */ - unsigned int max_rw; /* maxRw specifies the maximum */ - /* message size the server can send or receive for */ - /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ - unsigned int capabilities; /* selective disabling of caps by smb sess */ - int timeAdj; /* Adjust for difference in server time zone in sec */ - __u64 CurrentMid; /* multiplex id - rotating counter, protected by GlobalMid_Lock */ - char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ - /* 16th byte of RFC1001 workstation name is always null */ - char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; - __u32 sequence_number; /* for signing, protected by srv_mutex */ - __u32 reconnect_instance; /* incremented on each reconnect */ - struct session_key session_key; - unsigned long lstrp; /* when we got last response from this server */ - struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ -#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */ -#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */ - char negflavor; /* NEGOTIATE response flavor */ - /* extended security flavors that server supports */ - bool sec_ntlmssp; /* supports NTLMSSP */ - bool sec_kerberosu2u; /* supports U2U Kerberos */ - bool sec_kerberos; /* supports plain Kerberos */ - bool sec_mskerberos; /* supports legacy MS Kerberos */ - bool large_buf; /* is current buffer large? */ - /* use SMBD connection instead of socket */ - bool rdma; - /* point to the SMBD connection if RDMA is used instead of socket */ - struct smbd_connection *smbd_conn; - struct delayed_work echo; /* echo ping workqueue job */ - struct delayed_work resolve; /* dns resolution workqueue job */ - char *smallbuf; /* pointer to current "small" buffer */ - char *bigbuf; /* pointer to current "big" buffer */ - /* Total size of this PDU. Only valid from cifs_demultiplex_thread */ - unsigned int pdu_size; - unsigned int total_read; /* total amount of data read in this pass */ - atomic_t in_send; /* requests trying to send */ - atomic_t num_waiters; /* blocked waiting to get in sendrecv */ -#ifdef CONFIG_CIFS_STATS2 - atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */ - atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */ - __u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */ - __u32 slowest_cmd[NUMBER_OF_SMB2_COMMANDS]; - __u32 fastest_cmd[NUMBER_OF_SMB2_COMMANDS]; -#endif /* STATS2 */ - unsigned int max_read; - unsigned int max_write; - unsigned int min_offload; - __le16 compress_algorithm; - __u16 signing_algorithm; - __le16 cipher_type; - /* save initital negprot hash */ - __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; - bool signing_negotiated; /* true if valid signing context rcvd from server */ - bool posix_ext_supported; - struct delayed_work reconnect; /* reconnect workqueue job */ - struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ - unsigned long echo_interval; - - /* - * Number of targets available for reconnect. The more targets - * the more tasks have to wait to let the demultiplex thread - * reconnect. - */ - int nr_targets; - bool noblockcnt; /* use non-blocking connect() */ - - /* - * If this is a session channel, - * primary_server holds the ref-counted - * pointer to primary channel connection for the session. - */ -#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) - struct TCP_Server_Info *primary_server; - -#ifdef CONFIG_CIFS_SWN_UPCALL - bool use_swn_dstaddr; - struct sockaddr_storage swn_dstaddr; -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL - bool is_dfs_conn; /* if a dfs connection */ - struct mutex refpath_lock; /* protects leaf_fullpath */ - /* - * Canonical DFS full paths that were used to chase referrals in mount and reconnect. - * - * origin_fullpath: first or original referral path - * leaf_fullpath: last referral path (might be changed due to nested links in reconnect) - * - * current_fullpath: pointer to either origin_fullpath or leaf_fullpath - * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect() - * - * format: \\HOST\SHARE\[OPTIONAL PATH] - */ - char *origin_fullpath, *leaf_fullpath, *current_fullpath; -#endif -}; - -static inline bool is_smb1(struct TCP_Server_Info *server) -{ - return HEADER_PREAMBLE_SIZE(server) != 0; -} - -static inline void cifs_server_lock(struct TCP_Server_Info *server) -{ - unsigned int nofs_flag = memalloc_nofs_save(); - - mutex_lock(&server->_srv_mutex); - server->nofs_flag = nofs_flag; -} - -static inline void cifs_server_unlock(struct TCP_Server_Info *server) -{ - unsigned int nofs_flag = server->nofs_flag; - - mutex_unlock(&server->_srv_mutex); - memalloc_nofs_restore(nofs_flag); -} - -struct cifs_credits { - unsigned int value; - unsigned int instance; -}; - -static inline unsigned int -in_flight(struct TCP_Server_Info *server) -{ - unsigned int num; - spin_lock(&server->req_lock); - num = server->in_flight; - spin_unlock(&server->req_lock); - return num; -} - -static inline bool -has_credits(struct TCP_Server_Info *server, int *credits, int num_credits) -{ - int num; - spin_lock(&server->req_lock); - num = *credits; - spin_unlock(&server->req_lock); - return num >= num_credits; -} - -static inline void -add_credits(struct TCP_Server_Info *server, const struct cifs_credits *credits, - const int optype) -{ - server->ops->add_credits(server, credits, optype); -} - -static inline void -add_credits_and_wake_if(struct TCP_Server_Info *server, - const struct cifs_credits *credits, const int optype) -{ - if (credits->value) { - server->ops->add_credits(server, credits, optype); - wake_up(&server->request_q); - } -} - -static inline void -set_credits(struct TCP_Server_Info *server, const int val) -{ - server->ops->set_credits(server, val); -} - -static inline int -adjust_credits(struct TCP_Server_Info *server, struct cifs_credits *credits, - const unsigned int payload_size) -{ - return server->ops->adjust_credits ? - server->ops->adjust_credits(server, credits, payload_size) : 0; -} - -static inline __le64 -get_next_mid64(struct TCP_Server_Info *server) -{ - return cpu_to_le64(server->ops->get_next_mid(server)); -} - -static inline __le16 -get_next_mid(struct TCP_Server_Info *server) -{ - __u16 mid = server->ops->get_next_mid(server); - /* - * The value in the SMB header should be little endian for easy - * on-the-wire decoding. - */ - return cpu_to_le16(mid); -} - -static inline void -revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) -{ - if (server->ops->revert_current_mid) - server->ops->revert_current_mid(server, val); -} - -static inline void -revert_current_mid_from_hdr(struct TCP_Server_Info *server, - const struct smb2_hdr *shdr) -{ - unsigned int num = le16_to_cpu(shdr->CreditCharge); - - return revert_current_mid(server, num > 0 ? num : 1); -} - -static inline __u16 -get_mid(const struct smb_hdr *smb) -{ - return le16_to_cpu(smb->Mid); -} - -static inline bool -compare_mid(__u16 mid, const struct smb_hdr *smb) -{ - return mid == le16_to_cpu(smb->Mid); -} - -/* - * When the server supports very large reads and writes via POSIX extensions, - * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not - * including the RFC1001 length. - * - * Note that this might make for "interesting" allocation problems during - * writeback however as we have to allocate an array of pointers for the - * pages. A 16M write means ~32kb page array with PAGE_SIZE == 4096. - * - * For reads, there is a similar problem as we need to allocate an array - * of kvecs to handle the receive, though that should only need to be done - * once. - */ -#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4) -#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4) - -/* - * When the server doesn't allow large posix writes, only allow a rsize/wsize - * of 2^17-1 minus the size of the call header. That allows for a read or - * write up to the maximum size described by RFC1002. - */ -#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4) -#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4) - -#define CIFS_DEFAULT_IOSIZE (1024 * 1024) - -/* - * Windows only supports a max of 60kb reads and 65535 byte writes. Default to - * those values when posix extensions aren't in force. In actuality here, we - * use 65536 to allow for a write that is a multiple of 4k. Most servers seem - * to be ok with the extra byte even though Windows doesn't send writes that - * are that large. - * - * Citation: - * - * https://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx - */ -#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) -#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) - -/* - * Macros to allow the TCP_Server_Info->net field and related code to drop out - * when CONFIG_NET_NS isn't set. - */ - -#ifdef CONFIG_NET_NS - -static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) -{ - return srv->net; -} - -static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) -{ - srv->net = net; -} - -#else - -static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) -{ - return &init_net; -} - -static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) -{ -} - -#endif - -struct cifs_server_iface { - struct list_head iface_head; - struct kref refcount; - size_t speed; - unsigned int rdma_capable : 1; - unsigned int rss_capable : 1; - unsigned int is_active : 1; /* unset if non existent */ - struct sockaddr_storage sockaddr; -}; - -/* release iface when last ref is dropped */ -static inline void -release_iface(struct kref *ref) -{ - struct cifs_server_iface *iface = container_of(ref, - struct cifs_server_iface, - refcount); - list_del_init(&iface->iface_head); - kfree(iface); -} - -/* - * compare two interfaces a and b - * return 0 if everything matches. - * return 1 if a has higher link speed, or rdma capable, or rss capable - * return -1 otherwise. - */ -static inline int -iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b) -{ - int cmp_ret = 0; - - WARN_ON(!a || !b); - if (a->speed == b->speed) { - if (a->rdma_capable == b->rdma_capable) { - if (a->rss_capable == b->rss_capable) { - cmp_ret = memcmp(&a->sockaddr, &b->sockaddr, - sizeof(a->sockaddr)); - if (!cmp_ret) - return 0; - else if (cmp_ret > 0) - return 1; - else - return -1; - } else if (a->rss_capable > b->rss_capable) - return 1; - else - return -1; - } else if (a->rdma_capable > b->rdma_capable) - return 1; - else - return -1; - } else if (a->speed > b->speed) - return 1; - else - return -1; -} - -struct cifs_chan { - unsigned int in_reconnect : 1; /* if session setup in progress for this channel */ - struct TCP_Server_Info *server; - struct cifs_server_iface *iface; /* interface in use */ - __u8 signkey[SMB3_SIGN_KEY_SIZE]; -}; - -/* - * Session structure. One of these for each uid session with a particular host - */ -struct cifs_ses { - struct list_head smb_ses_list; - struct list_head rlist; /* reconnect list */ - struct list_head tcon_list; - struct cifs_tcon *tcon_ipc; - spinlock_t ses_lock; /* protect anything here that is not protected */ - struct mutex session_mutex; - struct TCP_Server_Info *server; /* pointer to server info */ - int ses_count; /* reference counter */ - enum ses_status_enum ses_status; /* updates protected by cifs_tcp_ses_lock */ - unsigned overrideSecFlg; /* if non-zero override global sec flags */ - char *serverOS; /* name of operating system underlying server */ - char *serverNOS; /* name of network operating system of server */ - char *serverDomain; /* security realm of server */ - __u64 Suid; /* remote smb uid */ - kuid_t linux_uid; /* overriding owner of files on the mount */ - kuid_t cred_uid; /* owner of credentials */ - unsigned int capabilities; - char ip_addr[INET6_ADDRSTRLEN + 1]; /* Max ipv6 (or v4) addr string len */ - char *user_name; /* must not be null except during init of sess - and after mount option parsing we fill it */ - char *domainName; - char *password; - char workstation_name[CIFS_MAX_WORKSTATION_LEN]; - struct session_key auth_key; - struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ - enum securityEnum sectype; /* what security flavor was specified? */ - bool sign; /* is signing required? */ - bool domainAuto:1; - __u16 session_flags; - __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; - __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; - - /* - * Network interfaces available on the server this session is - * connected to. - * - * Other channels can be opened by connecting and binding this - * session to interfaces from this list. - * - * iface_lock should be taken when accessing any of these fields - */ - spinlock_t iface_lock; - /* ========= begin: protected by iface_lock ======== */ - struct list_head iface_list; - size_t iface_count; - unsigned long iface_last_update; /* jiffies */ - /* ========= end: protected by iface_lock ======== */ - - spinlock_t chan_lock; - /* ========= begin: protected by chan_lock ======== */ -#define CIFS_MAX_CHANNELS 16 -#define CIFS_ALL_CHANNELS_SET(ses) \ - ((1UL << (ses)->chan_count) - 1) -#define CIFS_ALL_CHANS_GOOD(ses) \ - (!(ses)->chans_need_reconnect) -#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \ - ((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses)) -#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \ - ((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses)) -#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \ - test_bit((index), &(ses)->chans_need_reconnect) -#define CIFS_CHAN_IN_RECONNECT(ses, index) \ - ((ses)->chans[(index)].in_reconnect) - - struct cifs_chan chans[CIFS_MAX_CHANNELS]; - size_t chan_count; - size_t chan_max; - atomic_t chan_seq; /* round robin state */ - - /* - * chans_need_reconnect is a bitmap indicating which of the channels - * under this smb session needs to be reconnected. - * If not multichannel session, only one bit will be used. - * - * We will ask for sess and tcon reconnection only if all the - * channels are marked for needing reconnection. This will - * enable the sessions on top to continue to live till any - * of the channels below are active. - */ - unsigned long chans_need_reconnect; - /* ========= end: protected by chan_lock ======== */ -}; - -static inline bool -cap_unix(struct cifs_ses *ses) -{ - return ses->server->vals->cap_unix & ses->capabilities; -} - -/* - * common struct for holding inode info when searching for or updating an - * inode with new info - */ - -#define CIFS_FATTR_DFS_REFERRAL 0x1 -#define CIFS_FATTR_DELETE_PENDING 0x2 -#define CIFS_FATTR_NEED_REVAL 0x4 -#define CIFS_FATTR_INO_COLLISION 0x8 -#define CIFS_FATTR_UNKNOWN_NLINK 0x10 -#define CIFS_FATTR_FAKE_ROOT_INO 0x20 - -struct cifs_fattr { - u32 cf_flags; - u32 cf_cifsattrs; - u64 cf_uniqueid; - u64 cf_eof; - u64 cf_bytes; - u64 cf_createtime; - kuid_t cf_uid; - kgid_t cf_gid; - umode_t cf_mode; - dev_t cf_rdev; - unsigned int cf_nlink; - unsigned int cf_dtype; - struct timespec64 cf_atime; - struct timespec64 cf_mtime; - struct timespec64 cf_ctime; - u32 cf_cifstag; - char *cf_symlink_target; -}; - -/* - * there is one of these for each connection to a resource on a particular - * session - */ -struct cifs_tcon { - struct list_head tcon_list; - int tc_count; - struct list_head rlist; /* reconnect list */ - spinlock_t tc_lock; /* protect anything here that is not protected */ - atomic_t num_local_opens; /* num of all opens including disconnected */ - atomic_t num_remote_opens; /* num of all network opens on server */ - struct list_head openFileList; - spinlock_t open_file_lock; /* protects list above */ - struct cifs_ses *ses; /* pointer to session associated with */ - char tree_name[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ - char *nativeFileSystem; - char *password; /* for share-level security */ - __u32 tid; /* The 4 byte tree id */ - __u16 Flags; /* optional support bits */ - enum tid_status_enum status; - atomic_t num_smbs_sent; - union { - struct { - atomic_t num_writes; - atomic_t num_reads; - atomic_t num_flushes; - atomic_t num_oplock_brks; - atomic_t num_opens; - atomic_t num_closes; - atomic_t num_deletes; - atomic_t num_mkdirs; - atomic_t num_posixopens; - atomic_t num_posixmkdirs; - atomic_t num_rmdirs; - atomic_t num_renames; - atomic_t num_t2renames; - atomic_t num_ffirst; - atomic_t num_fnext; - atomic_t num_fclose; - atomic_t num_hardlinks; - atomic_t num_symlinks; - atomic_t num_locks; - atomic_t num_acl_get; - atomic_t num_acl_set; - } cifs_stats; - struct { - atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS]; - atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; - } smb2_stats; - } stats; - __u64 bytes_read; - __u64 bytes_written; - spinlock_t stat_lock; /* protects the two fields above */ - FILE_SYSTEM_DEVICE_INFO fsDevInfo; - FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ - FILE_SYSTEM_UNIX_INFO fsUnixInfo; - bool ipc:1; /* set if connection to IPC$ share (always also pipe) */ - bool pipe:1; /* set if connection to pipe share */ - bool print:1; /* set if connection to printer share */ - bool retry:1; - bool nocase:1; - bool nohandlecache:1; /* if strange server resource prob can turn off */ - bool nodelete:1; - bool seal:1; /* transport encryption for this mounted share */ - bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol - for this mount even if server would support */ - bool posix_extensions; /* if true SMB3.11 posix extensions enabled */ - bool local_lease:1; /* check leases (only) on local system not remote */ - bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ - bool broken_sparse_sup; /* if server or share does not support sparse */ - bool need_reconnect:1; /* connection reset, tid now invalid */ - bool need_reopen_files:1; /* need to reopen tcon file handles */ - bool use_resilient:1; /* use resilient instead of durable handles */ - bool use_persistent:1; /* use persistent instead of durable handles */ - bool no_lease:1; /* Do not request leases on files or directories */ - bool use_witness:1; /* use witness protocol */ - __le32 capabilities; - __u32 share_flags; - __u32 maximal_access; - __u32 vol_serial_number; - __le64 vol_create_time; - __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */ - __u32 handle_timeout; /* persistent and durable handle timeout in ms */ - __u32 ss_flags; /* sector size flags */ - __u32 perf_sector_size; /* best sector size for perf */ - __u32 max_chunks; - __u32 max_bytes_chunk; - __u32 max_bytes_copy; -#ifdef CONFIG_CIFS_FSCACHE - u64 resource_id; /* server resource id */ - struct fscache_volume *fscache; /* cookie for share */ -#endif - struct list_head pending_opens; /* list of incomplete opens */ - struct cached_fids *cfids; - /* BB add field for back pointer to sb struct(s)? */ -#ifdef CONFIG_CIFS_DFS_UPCALL - struct list_head ulist; /* cache update list */ -#endif - struct delayed_work query_interfaces; /* query interfaces workqueue job */ -}; - -/* - * This is a refcounted and timestamped container for a tcon pointer. The - * container holds a tcon reference. It is considered safe to free one of - * these when the tl_count goes to 0. The tl_time is the time of the last - * "get" on the container. - */ -struct tcon_link { - struct rb_node tl_rbnode; - kuid_t tl_uid; - unsigned long tl_flags; -#define TCON_LINK_MASTER 0 -#define TCON_LINK_PENDING 1 -#define TCON_LINK_IN_TREE 2 - unsigned long tl_time; - atomic_t tl_count; - struct cifs_tcon *tl_tcon; -}; - -extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); -extern void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst); - -static inline struct cifs_tcon * -tlink_tcon(struct tcon_link *tlink) -{ - return tlink->tl_tcon; -} - -static inline struct tcon_link * -cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) -{ - return cifs_sb->master_tlink; -} - -extern void cifs_put_tlink(struct tcon_link *tlink); - -static inline struct tcon_link * -cifs_get_tlink(struct tcon_link *tlink) -{ - if (tlink && !IS_ERR(tlink)) - atomic_inc(&tlink->tl_count); - return tlink; -} - -/* This function is always expected to succeed */ -extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); - -#define CIFS_OPLOCK_NO_CHANGE 0xfe - -struct cifs_pending_open { - struct list_head olist; - struct tcon_link *tlink; - __u8 lease_key[16]; - __u32 oplock; -}; - -struct cifs_deferred_close { - struct list_head dlist; - struct tcon_link *tlink; - __u16 netfid; - __u64 persistent_fid; - __u64 volatile_fid; -}; - -/* - * This info hangs off the cifsFileInfo structure, pointed to by llist. - * This is used to track byte stream locks on the file - */ -struct cifsLockInfo { - struct list_head llist; /* pointer to next cifsLockInfo */ - struct list_head blist; /* pointer to locks blocked on this */ - wait_queue_head_t block_q; - __u64 offset; - __u64 length; - __u32 pid; - __u16 type; - __u16 flags; -}; - -/* - * One of these for each open instance of a file - */ -struct cifs_search_info { - loff_t index_of_last_entry; - __u16 entries_in_buffer; - __u16 info_level; - __u32 resume_key; - char *ntwrk_buf_start; - char *srch_entries_start; - char *last_entry; - const char *presume_name; - unsigned int resume_name_len; - bool endOfSearch:1; - bool emptyDir:1; - bool unicode:1; - bool smallBuf:1; /* so we know which buf_release function to call */ -}; - -#define ACL_NO_MODE ((umode_t)(-1)) -struct cifs_open_parms { - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb; - int disposition; - int desired_access; - int create_options; - const char *path; - struct cifs_fid *fid; - umode_t mode; - bool reconnect:1; -}; - -struct cifs_fid { - __u16 netfid; - __u64 persistent_fid; /* persist file id for smb2 */ - __u64 volatile_fid; /* volatile file id for smb2 */ - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ - __u8 create_guid[16]; - __u32 access; - struct cifs_pending_open *pending_open; - unsigned int epoch; -#ifdef CONFIG_CIFS_DEBUG2 - __u64 mid; -#endif /* CIFS_DEBUG2 */ - bool purge_cache; -}; - -struct cifs_fid_locks { - struct list_head llist; - struct cifsFileInfo *cfile; /* fid that owns locks */ - struct list_head locks; /* locks held by fid above */ -}; - -struct cifsFileInfo { - /* following two lists are protected by tcon->open_file_lock */ - struct list_head tlist; /* pointer to next fid owned by tcon */ - struct list_head flist; /* next fid (file instance) for this inode */ - /* lock list below protected by cifsi->lock_sem */ - struct cifs_fid_locks *llist; /* brlocks held by this fid */ - kuid_t uid; /* allows finding which FileInfo structure */ - __u32 pid; /* process id who opened file */ - struct cifs_fid fid; /* file id from remote */ - struct list_head rlist; /* reconnect list */ - /* BB add lock scope info here if needed */ ; - /* lock scope id (0 if none) */ - struct dentry *dentry; - struct tcon_link *tlink; - unsigned int f_flags; - bool invalidHandle:1; /* file closed via session abend */ - bool swapfile:1; - bool oplock_break_cancelled:1; - unsigned int oplock_epoch; /* epoch from the lease break */ - __u32 oplock_level; /* oplock/lease level from the lease break */ - int count; - spinlock_t file_info_lock; /* protects four flag/count fields above */ - struct mutex fh_mutex; /* prevents reopen race after dead ses*/ - struct cifs_search_info srch_inf; - struct work_struct oplock_break; /* work for oplock breaks */ - struct work_struct put; /* work for the final part of _put */ - struct delayed_work deferred; - bool deferred_close_scheduled; /* Flag to indicate close is scheduled */ - char *symlink_target; -}; - -struct cifs_io_parms { - __u16 netfid; - __u64 persistent_fid; /* persist file id for smb2 */ - __u64 volatile_fid; /* volatile file id for smb2 */ - __u32 pid; - __u64 offset; - unsigned int length; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; -}; - -struct cifs_aio_ctx { - struct kref refcount; - struct list_head list; - struct mutex aio_mutex; - struct completion done; - struct iov_iter iter; - struct kiocb *iocb; - struct cifsFileInfo *cfile; - struct bio_vec *bv; - loff_t pos; - unsigned int npages; - ssize_t rc; - unsigned int len; - unsigned int total_len; - bool should_dirty; - /* - * Indicates if this aio_ctx is for direct_io, - * If yes, iter is a copy of the user passed iov_iter - */ - bool direct_io; -}; - -/* asynchronous read support */ -struct cifs_readdata { - struct kref refcount; - struct list_head list; - struct completion done; - struct cifsFileInfo *cfile; - struct address_space *mapping; - struct cifs_aio_ctx *ctx; - __u64 offset; - unsigned int bytes; - unsigned int got_bytes; - pid_t pid; - int result; - struct work_struct work; - int (*read_into_pages)(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - unsigned int len); - int (*copy_into_pages)(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - struct iov_iter *iter); - struct kvec iov[2]; - struct TCP_Server_Info *server; -#ifdef CONFIG_CIFS_SMB_DIRECT - struct smbd_mr *mr; -#endif - unsigned int pagesz; - unsigned int page_offset; - unsigned int tailsz; - struct cifs_credits credits; - unsigned int nr_pages; - struct page **pages; -}; - -/* asynchronous write support */ -struct cifs_writedata { - struct kref refcount; - struct list_head list; - struct completion done; - enum writeback_sync_modes sync_mode; - struct work_struct work; - struct cifsFileInfo *cfile; - struct cifs_aio_ctx *ctx; - __u64 offset; - pid_t pid; - unsigned int bytes; - int result; - struct TCP_Server_Info *server; -#ifdef CONFIG_CIFS_SMB_DIRECT - struct smbd_mr *mr; -#endif - unsigned int pagesz; - unsigned int page_offset; - unsigned int tailsz; - struct cifs_credits credits; - unsigned int nr_pages; - struct page **pages; -}; - -/* - * Take a reference on the file private data. Must be called with - * cfile->file_info_lock held. - */ -static inline void -cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) -{ - ++cifs_file->count; -} - -struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); -void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr, - bool offload); -void cifsFileInfo_put(struct cifsFileInfo *cifs_file); - -#define CIFS_CACHE_READ_FLG 1 -#define CIFS_CACHE_HANDLE_FLG 2 -#define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG) -#define CIFS_CACHE_WRITE_FLG 4 -#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) -#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) - -#define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE)) -#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) -#define CIFS_CACHE_WRITE(cinode) ((cinode->oplock & CIFS_CACHE_WRITE_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE)) - -/* - * One of these for each file inode - */ - -struct cifsInodeInfo { - struct netfs_inode netfs; /* Netfslib context and vfs inode */ - bool can_cache_brlcks; - struct list_head llist; /* locks helb by this inode */ - /* - * NOTE: Some code paths call down_read(lock_sem) twice, so - * we must always use cifs_down_write() instead of down_write() - * for this semaphore to avoid deadlocks. - */ - struct rw_semaphore lock_sem; /* protect the fields above */ - /* BB add in lists for dirty pages i.e. write caching info for oplock */ - struct list_head openFileList; - spinlock_t open_file_lock; /* protects openFileList */ - __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ - unsigned int oplock; /* oplock/lease level we have */ - unsigned int epoch; /* used to track lease state changes */ -#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */ -#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */ -#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */ -#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */ -#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */ -#define CIFS_INO_LOCK (5) /* lock bit for synchronization */ -#define CIFS_INO_MODIFIED_ATTR (6) /* Indicate change in mtime/ctime */ -#define CIFS_INO_CLOSE_ON_LOCK (7) /* Not to defer the close when lock is set */ - unsigned long flags; - spinlock_t writers_lock; - unsigned int writers; /* Number of writers on this inode */ - unsigned long time; /* jiffies of last update of inode */ - u64 server_eof; /* current file size on server -- protected by i_lock */ - u64 uniqueid; /* server inode number */ - u64 createtime; /* creation time on server */ - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */ - struct list_head deferred_closes; /* list of deferred closes */ - spinlock_t deferred_lock; /* protection on deferred list */ - bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */ - char *symlink_target; -}; - -static inline struct cifsInodeInfo * -CIFS_I(struct inode *inode) -{ - return container_of(inode, struct cifsInodeInfo, netfs.inode); -} - -static inline struct cifs_sb_info * -CIFS_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct cifs_sb_info * -CIFS_FILE_SB(struct file *file) -{ - return CIFS_SB(file_inode(file)->i_sb); -} - -static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) -{ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) - return '/'; - else - return '\\'; -} - -static inline void -convert_delimiter(char *path, char delim) -{ - char old_delim, *pos; - - if (delim == '/') - old_delim = '\\'; - else - old_delim = '/'; - - pos = path; - while ((pos = strchr(pos, old_delim))) - *pos = delim; -} - -#define cifs_stats_inc atomic_inc - -static inline void cifs_stats_bytes_written(struct cifs_tcon *tcon, - unsigned int bytes) -{ - if (bytes) { - spin_lock(&tcon->stat_lock); - tcon->bytes_written += bytes; - spin_unlock(&tcon->stat_lock); - } -} - -static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon, - unsigned int bytes) -{ - spin_lock(&tcon->stat_lock); - tcon->bytes_read += bytes; - spin_unlock(&tcon->stat_lock); -} - - -/* - * This is the prototype for the mid receive function. This function is for - * receiving the rest of the SMB frame, starting with the WordCount (which is - * just after the MID in struct smb_hdr). Note: - * - * - This will be called by cifsd, with no locks held. - * - The mid will still be on the pending_mid_q. - * - mid->resp_buf will point to the current buffer. - * - * Returns zero on a successful receive, or an error. The receive state in - * the TCP_Server_Info will also be updated. - */ -typedef int (mid_receive_t)(struct TCP_Server_Info *server, - struct mid_q_entry *mid); - -/* - * This is the prototype for the mid callback function. This is called once the - * mid has been received off of the socket. When creating one, take special - * care to avoid deadlocks. Things to bear in mind: - * - * - it will be called by cifsd, with no locks held - * - the mid will be removed from any lists - */ -typedef void (mid_callback_t)(struct mid_q_entry *mid); - -/* - * This is the protopyte for mid handle function. This is called once the mid - * has been recognized after decryption of the message. - */ -typedef int (mid_handle_t)(struct TCP_Server_Info *server, - struct mid_q_entry *mid); - -/* one of these for every pending CIFS request to the server */ -struct mid_q_entry { - struct list_head qhead; /* mids waiting on reply from this server */ - struct kref refcount; - struct TCP_Server_Info *server; /* server corresponding to this mid */ - __u64 mid; /* multiplex id */ - __u16 credits; /* number of credits consumed by this mid */ - __u16 credits_received; /* number of credits from the response */ - __u32 pid; /* process id */ - __u32 sequence_number; /* for CIFS signing */ - unsigned long when_alloc; /* when mid was created */ -#ifdef CONFIG_CIFS_STATS2 - unsigned long when_sent; /* time when smb send finished */ - unsigned long when_received; /* when demux complete (taken off wire) */ -#endif - mid_receive_t *receive; /* call receive callback */ - mid_callback_t *callback; /* call completion callback */ - mid_handle_t *handle; /* call handle mid callback */ - void *callback_data; /* general purpose pointer for callback */ - struct task_struct *creator; - void *resp_buf; /* pointer to received SMB header */ - unsigned int resp_buf_size; - int mid_state; /* wish this were enum but can not pass to wait_event */ - unsigned int mid_flags; - __le16 command; /* smb command code */ - unsigned int optype; /* operation type */ - bool large_buf:1; /* if valid response, is pointer to large buf */ - bool multiRsp:1; /* multiple trans2 responses for one request */ - bool multiEnd:1; /* both received */ - bool decrypted:1; /* decrypted entry */ -}; - -struct close_cancelled_open { - struct cifs_fid fid; - struct cifs_tcon *tcon; - struct work_struct work; - __u64 mid; - __u16 cmd; -}; - -/* Make code in transport.c a little cleaner by moving - update of optional stats into function below */ -static inline void cifs_in_send_inc(struct TCP_Server_Info *server) -{ - atomic_inc(&server->in_send); -} - -static inline void cifs_in_send_dec(struct TCP_Server_Info *server) -{ - atomic_dec(&server->in_send); -} - -static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server) -{ - atomic_inc(&server->num_waiters); -} - -static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server) -{ - atomic_dec(&server->num_waiters); -} - -#ifdef CONFIG_CIFS_STATS2 -static inline void cifs_save_when_sent(struct mid_q_entry *mid) -{ - mid->when_sent = jiffies; -} -#else -static inline void cifs_save_when_sent(struct mid_q_entry *mid) -{ -} -#endif - -/* for pending dnotify requests */ -struct dir_notify_req { - struct list_head lhead; - __le16 Pid; - __le16 PidHigh; - __u16 Mid; - __u16 Tid; - __u16 Uid; - __u16 netfid; - __u32 filter; /* CompletionFilter (for multishot) */ - int multishot; - struct file *pfile; -}; - -struct dfs_info3_param { - int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/ - int path_consumed; - int server_type; - int ref_flag; - char *path_name; - char *node_name; - int ttl; -}; - -struct file_list { - struct list_head list; - struct cifsFileInfo *cfile; -}; - -static inline void free_dfs_info_param(struct dfs_info3_param *param) -{ - if (param) { - kfree(param->path_name); - kfree(param->node_name); - } -} - -static inline void free_dfs_info_array(struct dfs_info3_param *param, - int number_of_items) -{ - int i; - if ((number_of_items == 0) || (param == NULL)) - return; - for (i = 0; i < number_of_items; i++) { - kfree(param[i].path_name); - kfree(param[i].node_name); - } - kfree(param); -} - -static inline bool is_interrupt_error(int error) -{ - switch (error) { - case -EINTR: - case -ERESTARTSYS: - case -ERESTARTNOHAND: - case -ERESTARTNOINTR: - return true; - } - return false; -} - -static inline bool is_retryable_error(int error) -{ - if (is_interrupt_error(error) || error == -EAGAIN) - return true; - return false; -} - - -/* cifs_get_writable_file() flags */ -#define FIND_WR_ANY 0 -#define FIND_WR_FSUID_ONLY 1 -#define FIND_WR_WITH_DELETE 2 - -#define MID_FREE 0 -#define MID_REQUEST_ALLOCATED 1 -#define MID_REQUEST_SUBMITTED 2 -#define MID_RESPONSE_RECEIVED 4 -#define MID_RETRY_NEEDED 8 /* session closed while this request out */ -#define MID_RESPONSE_MALFORMED 0x10 -#define MID_SHUTDOWN 0x20 - -/* Flags */ -#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */ -#define MID_DELETED 2 /* Mid has been dequeued/deleted */ - -/* Types of response buffer returned from SendReceive2 */ -#define CIFS_NO_BUFFER 0 /* Response buffer not returned */ -#define CIFS_SMALL_BUFFER 1 -#define CIFS_LARGE_BUFFER 2 -#define CIFS_IOVEC 4 /* array of response buffers */ - -/* Type of Request to SendReceive2 */ -#define CIFS_BLOCKING_OP 1 /* operation can block */ -#define CIFS_NON_BLOCKING 2 /* do not block waiting for credits */ -#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */ -#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */ -#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */ -#define CIFS_NO_RSP_BUF 0x040 /* no response buffer required */ - -/* Type of request operation */ -#define CIFS_ECHO_OP 0x080 /* echo request */ -#define CIFS_OBREAK_OP 0x0100 /* oplock break request */ -#define CIFS_NEG_OP 0x0200 /* negotiate request */ -#define CIFS_CP_CREATE_CLOSE_OP 0x0400 /* compound create+close request */ -/* Lower bitmask values are reserved by others below. */ -#define CIFS_SESS_OP 0x2000 /* session setup request */ -#define CIFS_OP_MASK 0x2780 /* mask request type */ - -#define CIFS_HAS_CREDITS 0x0400 /* already has credits */ -#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */ -#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */ - -/* Security Flags: indicate type of session setup needed */ -#define CIFSSEC_MAY_SIGN 0x00001 -#define CIFSSEC_MAY_NTLMV2 0x00004 -#define CIFSSEC_MAY_KRB5 0x00008 -#define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */ -#define CIFSSEC_MAY_NTLMSSP 0x00080 /* raw ntlmssp with ntlmv2 */ - -#define CIFSSEC_MUST_SIGN 0x01001 -/* note that only one of the following can be set so the -result of setting MUST flags more than once will be to -require use of the stronger protocol */ -#define CIFSSEC_MUST_NTLMV2 0x04004 -#define CIFSSEC_MUST_KRB5 0x08008 -#ifdef CONFIG_CIFS_UPCALL -#define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */ -#else -#define CIFSSEC_MASK 0x87087 /* flags supported if no weak allowed */ -#endif /* UPCALL */ -#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ -#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ - -#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP) -#define CIFSSEC_MAX (CIFSSEC_MUST_NTLMV2) -#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) -/* - ***************************************************************** - * All constants go here - ***************************************************************** - */ - -#define UID_HASH (16) - -/* - * Note that ONE module should define _DECLARE_GLOBALS_HERE to cause the - * following to be declared. - */ - -/**************************************************************************** - * Here are all the locks (spinlock, mutex, semaphore) in cifs.ko, arranged according - * to the locking order. i.e. if two locks are to be held together, the lock that - * appears higher in this list needs to be taken before the other. - * - * If you hold a lock that is lower in this list, and you need to take a higher lock - * (or if you think that one of the functions that you're calling may need to), first - * drop the lock you hold, pick up the higher lock, then the lower one. This will - * ensure that locks are picked up only in one direction in the below table - * (top to bottom). - * - * Also, if you expect a function to be called with a lock held, explicitly document - * this in the comments on top of your function definition. - * - * And also, try to keep the critical sections (lock hold time) to be as minimal as - * possible. Blocking / calling other functions with a lock held always increase - * the risk of a possible deadlock. - * - * Following this rule will avoid unnecessary deadlocks, which can get really hard to - * debug. Also, any new lock that you introduce, please add to this list in the correct - * order. - * - * Please populate this list whenever you introduce new locks in your changes. Or in - * case I've missed some existing locks. Please ensure that it's added in the list - * based on the locking order expected. - * - * ===================================================================================== - * Lock Protects Initialization fn - * ===================================================================================== - * vol_list_lock - * vol_info->ctx_lock vol_info->ctx - * cifs_sb_info->tlink_tree_lock cifs_sb_info->tlink_tree cifs_setup_cifs_sb - * TCP_Server_Info-> TCP_Server_Info cifs_get_tcp_session - * reconnect_mutex - * TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session - * cifs_ses->session_mutex cifs_ses sesInfoAlloc - * cifs_tcon - * cifs_tcon->open_file_lock cifs_tcon->openFileList tconInfoAlloc - * cifs_tcon->pending_opens - * cifs_tcon->stat_lock cifs_tcon->bytes_read tconInfoAlloc - * cifs_tcon->bytes_written - * cifs_tcp_ses_lock cifs_tcp_ses_list sesInfoAlloc - * GlobalMid_Lock GlobalMaxActiveXid init_cifs - * GlobalCurrentXid - * GlobalTotalActiveXid - * TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change) - * TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session - * ->CurrentMid - * (any changes in mid_q_entry fields) - * TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session - * ->credits - * ->echo_credits - * ->oplock_credits - * ->reconnect_instance - * cifs_ses->ses_lock (anything that is not protected by another lock and can change) - * cifs_ses->iface_lock cifs_ses->iface_list sesInfoAlloc - * ->iface_count - * ->iface_last_update - * cifs_ses->chan_lock cifs_ses->chans - * ->chans_need_reconnect - * ->chans_in_reconnect - * cifs_tcon->tc_lock (anything that is not protected by another lock and can change) - * cifsInodeInfo->open_file_lock cifsInodeInfo->openFileList cifs_alloc_inode - * cifsInodeInfo->writers_lock cifsInodeInfo->writers cifsInodeInfo_alloc - * cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once - * ->can_cache_brlcks - * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc - * cached_fid->fid_mutex cifs_tcon->crfid tconInfoAlloc - * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo - * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo - * ->invalidHandle initiate_cifs_search - * ->oplock_break_cancelled - * cifs_aio_ctx->aio_mutex cifs_aio_ctx cifs_aio_ctx_alloc - ****************************************************************************/ - -#ifdef DECLARE_GLOBALS_HERE -#define GLOBAL_EXTERN -#else -#define GLOBAL_EXTERN extern -#endif - -/* - * the list of TCP_Server_Info structures, ie each of the sockets - * connecting our client to a distinct server (ip address), is - * chained together by cifs_tcp_ses_list. The list of all our SMB - * sessions (and from that the tree connections) can be found - * by iterating over cifs_tcp_ses_list - */ -extern struct list_head cifs_tcp_ses_list; - -/* - * This lock protects the cifs_tcp_ses_list, the list of smb sessions per - * tcp session, and the list of tcon's per smb session. It also protects - * the reference counters for the server, smb session, and tcon. - * generally the locks should be taken in order tcp_ses_lock before - * tcon->open_file_lock and that before file->file_info_lock since the - * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file - */ -extern spinlock_t cifs_tcp_ses_lock; - -/* - * Global transaction id (XID) information - */ -extern unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ -extern unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ -extern unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ -extern spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ - -/* - * Global counters, updated atomically - */ -extern atomic_t sesInfoAllocCount; -extern atomic_t tconInfoAllocCount; -extern atomic_t tcpSesNextId; -extern atomic_t tcpSesAllocCount; -extern atomic_t tcpSesReconnectCount; -extern atomic_t tconInfoReconnectCount; - -/* Various Debug counters */ -extern atomic_t buf_alloc_count; /* current number allocated */ -extern atomic_t small_buf_alloc_count; -#ifdef CONFIG_CIFS_STATS2 -extern atomic_t total_buf_alloc_count; /* total allocated over all time */ -extern atomic_t total_small_buf_alloc_count; -extern unsigned int slow_rsp_threshold; /* number of secs before logging */ -#endif - -/* Misc globals */ -extern bool enable_oplocks; /* enable or disable oplocks */ -extern bool lookupCacheEnabled; -extern unsigned int global_secflags; /* if on, session setup sent - with more secure ntlmssp2 challenge/resp */ -extern unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ -extern bool enable_gcm_256; /* allow optional negotiate of strongest signing (aes-gcm-256) */ -extern bool require_gcm_256; /* require use of strongest signing (aes-gcm-256) */ -extern bool enable_negotiate_signing; /* request use of faster (GMAC) signing if available */ -extern bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/ -extern unsigned int CIFSMaxBufSize; /* max size not including hdr */ -extern unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ -extern unsigned int cifs_min_small; /* min size of small buf pool */ -extern unsigned int cifs_max_pending; /* MAX requests at once to server*/ -extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */ -extern atomic_t mid_count; - -void cifs_oplock_break(struct work_struct *work); -void cifs_queue_oplock_break(struct cifsFileInfo *cfile); -void smb2_deferred_work_close(struct work_struct *work); - -extern const struct slow_work_ops cifs_oplock_break_ops; -extern struct workqueue_struct *cifsiod_wq; -extern struct workqueue_struct *decrypt_wq; -extern struct workqueue_struct *fileinfo_put_wq; -extern struct workqueue_struct *cifsoplockd_wq; -extern struct workqueue_struct *deferredclose_wq; -extern __u32 cifs_lock_secret; - -extern mempool_t *cifs_mid_poolp; - -/* Operations for different SMB versions */ -#define SMB1_VERSION_STRING "1.0" -#define SMB20_VERSION_STRING "2.0" -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern struct smb_version_operations smb1_operations; -extern struct smb_version_values smb1_values; -extern struct smb_version_operations smb20_operations; -extern struct smb_version_values smb20_values; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ -#define SMB21_VERSION_STRING "2.1" -extern struct smb_version_operations smb21_operations; -extern struct smb_version_values smb21_values; -#define SMBDEFAULT_VERSION_STRING "default" -extern struct smb_version_values smbdefault_values; -#define SMB3ANY_VERSION_STRING "3" -extern struct smb_version_values smb3any_values; -#define SMB30_VERSION_STRING "3.0" -extern struct smb_version_operations smb30_operations; -extern struct smb_version_values smb30_values; -#define SMB302_VERSION_STRING "3.02" -#define ALT_SMB302_VERSION_STRING "3.0.2" -/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ -extern struct smb_version_values smb302_values; -#define SMB311_VERSION_STRING "3.1.1" -#define ALT_SMB311_VERSION_STRING "3.11" -extern struct smb_version_operations smb311_operations; -extern struct smb_version_values smb311_values; - -static inline char *get_security_type_str(enum securityEnum sectype) -{ - switch (sectype) { - case RawNTLMSSP: - return "RawNTLMSSP"; - case Kerberos: - return "Kerberos"; - case NTLMv2: - return "NTLMv2"; - default: - return "Unknown"; - } -} - -static inline bool is_smb1_server(struct TCP_Server_Info *server) -{ - return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0; -} - -static inline bool is_tcon_dfs(struct cifs_tcon *tcon) -{ - /* - * For SMB1, see MS-CIFS 2.4.55 SMB_COM_TREE_CONNECT_ANDX (0x75) and MS-CIFS 3.3.4.4 DFS - * Subsystem Notifies That a Share Is a DFS Share. - * - * For SMB2+, see MS-SMB2 2.2.10 SMB2 TREE_CONNECT Response and MS-SMB2 3.3.4.14 Server - * Application Updates a Share. - */ - if (!tcon || !tcon->ses || !tcon->ses->server) - return false; - return is_smb1_server(tcon->ses->server) ? tcon->Flags & SMB_SHARE_IS_IN_DFS : - tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT); -} - -static inline bool cifs_is_referral_server(struct cifs_tcon *tcon, - const struct dfs_info3_param *ref) -{ - /* - * Check if all targets are capable of handling DFS referrals as per - * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. - */ - return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER)); -} - -static inline u64 cifs_flock_len(const struct file_lock *fl) -{ - return (u64)fl->fl_end - fl->fl_start + 1; -} - -static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses) -{ - if (WARN_ON_ONCE(!ses || !ses->server)) - return 0; - /* - * Make workstation name no more than 15 chars when using insecure dialects as some legacy - * servers do require it during NTLMSSP. - */ - if (ses->server->dialect <= SMB20_PROT_ID) - return min_t(size_t, sizeof(ses->workstation_name), RFC1001_NAME_LEN_WITH_NULL); - return sizeof(ses->workstation_name); -} - -static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src) -{ - memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src)); - dst->AccessFlags = src->AccessFlags; - dst->CurrentByteOffset = src->CurrentByteOffset; - dst->Mode = src->Mode; - dst->AlignmentRequirement = src->AlignmentRequirement; - dst->FileNameLength = src->FileNameLength; -} - -static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, - int num_rqst, - const u8 *sig) -{ - unsigned int len, skip; - unsigned int nents = 0; - unsigned long addr; - int i, j; - - /* Assumes the first rqst has a transform header as the first iov. - * I.e. - * rqst[0].rq_iov[0] is transform header - * rqst[0].rq_iov[1+] data to be encrypted/decrypted - * rqst[1+].rq_iov[0+] data to be encrypted/decrypted - */ - for (i = 0; i < num_rqst; i++) { - /* - * The first rqst has a transform header where the - * first 20 bytes are not part of the encrypted blob. - */ - for (j = 0; j < rqst[i].rq_nvec; j++) { - struct kvec *iov = &rqst[i].rq_iov[j]; - - skip = (i == 0) && (j == 0) ? 20 : 0; - addr = (unsigned long)iov->iov_base + skip; - if (unlikely(is_vmalloc_addr((void *)addr))) { - len = iov->iov_len - skip; - nents += DIV_ROUND_UP(offset_in_page(addr) + len, - PAGE_SIZE); - } else { - nents++; - } - } - nents += rqst[i].rq_npages; - } - nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE); - return nents; -} - -/* We can not use the normal sg_set_buf() as we will sometimes pass a - * stack object as buf. - */ -static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg, - const void *buf, - unsigned int buflen) -{ - unsigned long addr = (unsigned long)buf; - unsigned int off = offset_in_page(addr); - - addr &= PAGE_MASK; - if (unlikely(is_vmalloc_addr((void *)addr))) { - do { - unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off); - - sg_set_page(sg++, vmalloc_to_page((void *)addr), len, off); - - off = 0; - addr += PAGE_SIZE; - buflen -= len; - } while (buflen); - } else { - sg_set_page(sg++, virt_to_page(addr), buflen, off); - } - return sg; -} - -#endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h deleted file mode 100644 index d1abaeea974a..000000000000 --- a/fs/cifs/cifspdu.h +++ /dev/null @@ -1,2730 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFSPDU_H -#define _CIFSPDU_H - -#include -#include -#include "../smbfs_common/smbfsctl.h" - -#define CIFS_PROT 0 -#define POSIX_PROT (CIFS_PROT+1) -#define BAD_PROT 0xFFFF - -/* SMB command codes: - * Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses - * (ie which include no useful data other than the SMB error code itself). - * This can allow us to avoid response buffer allocations and copy in some cases - */ -#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ -#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ -#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ -#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ -#define SMB_COM_DELETE 0x06 /* trivial response */ -#define SMB_COM_RENAME 0x07 /* trivial response */ -#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ -#define SMB_COM_SETATTR 0x09 /* trivial response */ -#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ -#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ -#define SMB_COM_ECHO 0x2B /* echo request */ -#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ -#define SMB_COM_READ_ANDX 0x2E -#define SMB_COM_WRITE_ANDX 0x2F -#define SMB_COM_TRANSACTION2 0x32 -#define SMB_COM_TRANSACTION2_SECONDARY 0x33 -#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ -#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ -#define SMB_COM_NEGOTIATE 0x72 -#define SMB_COM_SESSION_SETUP_ANDX 0x73 -#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ -#define SMB_COM_TREE_CONNECT_ANDX 0x75 -#define SMB_COM_NT_TRANSACT 0xA0 -#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 -#define SMB_COM_NT_CREATE_ANDX 0xA2 -#define SMB_COM_NT_CANCEL 0xA4 /* no response */ -#define SMB_COM_NT_RENAME 0xA5 /* trivial response */ - -/* Transact2 subcommand codes */ -#define TRANS2_OPEN 0x00 -#define TRANS2_FIND_FIRST 0x01 -#define TRANS2_FIND_NEXT 0x02 -#define TRANS2_QUERY_FS_INFORMATION 0x03 -#define TRANS2_SET_FS_INFORMATION 0x04 -#define TRANS2_QUERY_PATH_INFORMATION 0x05 -#define TRANS2_SET_PATH_INFORMATION 0x06 -#define TRANS2_QUERY_FILE_INFORMATION 0x07 -#define TRANS2_SET_FILE_INFORMATION 0x08 -#define TRANS2_GET_DFS_REFERRAL 0x10 -#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 - -/* SMB Transact (Named Pipe) subcommand codes */ -#define TRANS_SET_NMPIPE_STATE 0x0001 -#define TRANS_RAW_READ_NMPIPE 0x0011 -#define TRANS_QUERY_NMPIPE_STATE 0x0021 -#define TRANS_QUERY_NMPIPE_INFO 0x0022 -#define TRANS_PEEK_NMPIPE 0x0023 -#define TRANS_TRANSACT_NMPIPE 0x0026 -#define TRANS_RAW_WRITE_NMPIPE 0x0031 -#define TRANS_READ_NMPIPE 0x0036 -#define TRANS_WRITE_NMPIPE 0x0037 -#define TRANS_WAIT_NMPIPE 0x0053 -#define TRANS_CALL_NMPIPE 0x0054 - -/* NT Transact subcommand codes */ -#define NT_TRANSACT_CREATE 0x01 -#define NT_TRANSACT_IOCTL 0x02 -#define NT_TRANSACT_SET_SECURITY_DESC 0x03 -#define NT_TRANSACT_NOTIFY_CHANGE 0x04 -#define NT_TRANSACT_RENAME 0x05 -#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 -#define NT_TRANSACT_GET_USER_QUOTA 0x07 -#define NT_TRANSACT_SET_USER_QUOTA 0x08 - -#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ -/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */ -/* among the requests (NTCreateX response is bigger with wct of 34) */ -#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */ -#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */ - -/* internal cifs vfs structures */ -/***************************************************************** - * All constants go here - ***************************************************************** - */ - -/* - * Starting value for maximum SMB size negotiation - */ -#define CIFS_MAX_MSGSIZE (4*4096) - -/* - * Size of encrypted user password in bytes - */ -#define CIFS_ENCPWD_SIZE (16) - -/* - * Size of the crypto key returned on the negotiate SMB in bytes - */ -#define CIFS_CRYPTO_KEY_SIZE (8) - -/* - * Size of the ntlm client response - */ -#define CIFS_AUTH_RESP_SIZE (24) - -/* - * Size of the session key (crypto key encrypted with the password - */ -#define CIFS_SESS_KEY_SIZE (16) - -#define CIFS_SERVER_CHALLENGE_SIZE (8) -#define CIFS_HMAC_MD5_HASH_SIZE (16) -#define CIFS_CPHTXT_SIZE (16) -#define CIFS_NTHASH_SIZE (16) - -/* - * Maximum user name length - */ -#define CIFS_UNLEN (20) - -/* - * Flags on SMB open - */ -#define SMBOPEN_WRITE_THROUGH 0x4000 -#define SMBOPEN_DENY_ALL 0x0010 -#define SMBOPEN_DENY_WRITE 0x0020 -#define SMBOPEN_DENY_READ 0x0030 -#define SMBOPEN_DENY_NONE 0x0040 -#define SMBOPEN_READ 0x0000 -#define SMBOPEN_WRITE 0x0001 -#define SMBOPEN_READWRITE 0x0002 -#define SMBOPEN_EXECUTE 0x0003 - -#define SMBOPEN_OCREATE 0x0010 -#define SMBOPEN_OTRUNC 0x0002 -#define SMBOPEN_OAPPEND 0x0001 - -/* - * SMB flag definitions - */ -#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */ -#define SMBFLG_RCV_POSTED 0x02 /* obsolete */ -#define SMBFLG_RSVD 0x04 -#define SMBFLG_CASELESS 0x08 /* all pathnames treated as caseless (off - implies case sensitive file handling request) */ -#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ -#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ -#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ -#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ - -/* - * SMB flag2 definitions - */ -#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3) - path names in response */ -#define SMBFLG2_KNOWS_EAS cpu_to_le16(2) -#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) -#define SMBFLG2_COMPRESSED (8) -#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) -#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) -#define SMBFLG2_REPARSE_PATH (0x400) -#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) -#define SMBFLG2_DFS cpu_to_le16(0x1000) -#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) -#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) -#define SMBFLG2_UNICODE cpu_to_le16(0x8000) - -/* - * These are the file access permission bits defined in CIFS for the - * NTCreateAndX as well as the level 0x107 - * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO - * responds with the AccessFlags. - * The AccessFlags specifies the access permissions a caller has to the - * file and can have any suitable combination of the following values: - */ - -#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ -#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ -#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ -#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ - /* with the file can be read */ -#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ - /* with the file can be written */ -#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ - /* the file using system paging I/O */ -#define FILE_DELETE_CHILD 0x00000040 -#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ - /* file can be read */ -#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ - /* file can be written */ -#define DELETE 0x00010000 /* The file can be deleted */ -#define READ_CONTROL 0x00020000 /* The access control list and */ - /* ownership associated with the */ - /* file can be read */ -#define WRITE_DAC 0x00040000 /* The access control list and */ - /* ownership associated with the */ - /* file can be written. */ -#define WRITE_OWNER 0x00080000 /* Ownership information associated */ - /* with the file can be written */ -#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ - /* synchronize with the completion */ - /* of an input/output request */ -#define SYSTEM_SECURITY 0x01000000 /* The system access control list */ - /* can be read and changed */ -#define GENERIC_ALL 0x10000000 -#define GENERIC_EXECUTE 0x20000000 -#define GENERIC_WRITE 0x40000000 -#define GENERIC_READ 0x80000000 - /* In summary - Relevant file */ - /* access flags from CIFS are */ - /* file_read_data, file_write_data */ - /* file_execute, file_read_attributes*/ - /* write_dac, and delete. */ - -#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) -#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) -#define FILE_EXEC_RIGHTS (FILE_EXECUTE) - -#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_READ_EA | FILE_WRITE_EA \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) - -#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ - | READ_CONTROL | SYNCHRONIZE) - - -/* - * Invalid readdir handle - */ -#define CIFS_NO_HANDLE 0xFFFF - -#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL - -/* IPC$ in ASCII */ -#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" - -/* IPC$ in Unicode */ -#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00" - -/* Unicode Null terminate 2 bytes of 0 */ -#define UNICODE_NULL "\x00\x00" -#define ASCII_NULL 0x00 - -/* - * Server type values (returned on EnumServer API - */ -#define CIFS_SV_TYPE_DC 0x00000008 -#define CIFS_SV_TYPE_BACKDC 0x00000010 - -/* - * Alias type flags (From EnumAlias API call - */ -#define CIFS_ALIAS_TYPE_FILE 0x0001 -#define CIFS_SHARE_TYPE_FILE 0x0000 - -/* - * File Attribute flags - */ -#define ATTR_READONLY 0x0001 -#define ATTR_HIDDEN 0x0002 -#define ATTR_SYSTEM 0x0004 -#define ATTR_VOLUME 0x0008 -#define ATTR_DIRECTORY 0x0010 -#define ATTR_ARCHIVE 0x0020 -#define ATTR_DEVICE 0x0040 -#define ATTR_NORMAL 0x0080 -#define ATTR_TEMPORARY 0x0100 -#define ATTR_SPARSE 0x0200 -#define ATTR_REPARSE 0x0400 -#define ATTR_COMPRESSED 0x0800 -#define ATTR_OFFLINE 0x1000 /* ie file not immediately available - - on offline storage */ -#define ATTR_NOT_CONTENT_INDEXED 0x2000 -#define ATTR_ENCRYPTED 0x4000 -#define ATTR_POSIX_SEMANTICS 0x01000000 -#define ATTR_BACKUP_SEMANTICS 0x02000000 -#define ATTR_DELETE_ON_CLOSE 0x04000000 -#define ATTR_SEQUENTIAL_SCAN 0x08000000 -#define ATTR_RANDOM_ACCESS 0x10000000 -#define ATTR_NO_BUFFERING 0x20000000 -#define ATTR_WRITE_THROUGH 0x80000000 - -/* ShareAccess flags */ -#define FILE_NO_SHARE 0x00000000 -#define FILE_SHARE_READ 0x00000001 -#define FILE_SHARE_WRITE 0x00000002 -#define FILE_SHARE_DELETE 0x00000004 -#define FILE_SHARE_ALL 0x00000007 - -/* CreateDisposition flags, similar to CreateAction as well */ -#define FILE_SUPERSEDE 0x00000000 -#define FILE_OPEN 0x00000001 -#define FILE_CREATE 0x00000002 -#define FILE_OPEN_IF 0x00000003 -#define FILE_OVERWRITE 0x00000004 -#define FILE_OVERWRITE_IF 0x00000005 - -/* CreateOptions */ -#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ -#define CREATE_WRITE_THROUGH 0x00000002 -#define CREATE_SEQUENTIAL 0x00000004 -#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */ -#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */ -#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */ -#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ -#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */ -#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */ -#define CREATE_NO_EA_KNOWLEDGE 0x00000200 -#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete - "open for recovery" flag should - be zero in any case */ -#define CREATE_OPEN_FOR_RECOVERY 0x00000400 -#define CREATE_RANDOM_ACCESS 0x00000800 -#define CREATE_DELETE_ON_CLOSE 0x00001000 -#define CREATE_OPEN_BY_ID 0x00002000 -#define CREATE_OPEN_BACKUP_INTENT 0x00004000 -#define CREATE_NO_COMPRESSION 0x00008000 -#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */ -#define OPEN_REPARSE_POINT 0x00200000 -#define OPEN_NO_RECALL 0x00400000 -#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */ -#define CREATE_OPTIONS_MASK 0x007FFFFF -#define CREATE_OPTION_READONLY 0x10000000 -#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ - -/* ImpersonationLevel flags */ -#define SECURITY_ANONYMOUS 0 -#define SECURITY_IDENTIFICATION 1 -#define SECURITY_IMPERSONATION 2 -#define SECURITY_DELEGATION 3 - -/* SecurityFlags */ -#define SECURITY_CONTEXT_TRACKING 0x01 -#define SECURITY_EFFECTIVE_ONLY 0x02 - -/* - * Default PID value, used in all SMBs where the PID is not important - */ -#define CIFS_DFT_PID 0x1234 - -/* - * We use the same routine for Copy and Move SMBs. This flag is used to - * distinguish - */ -#define CIFS_COPY_OP 1 -#define CIFS_RENAME_OP 2 - -#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */ -#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */ - -struct smb_hdr { - __be32 smb_buf_length; /* BB length is only two (rarely three) bytes, - with one or two byte "type" preceding it that will be - zero - we could mask the type byte off */ - __u8 Protocol[4]; - __u8 Command; - union { - struct { - __u8 ErrorClass; - __u8 Reserved; - __le16 Error; - } __attribute__((packed)) DosError; - __le32 CifsError; - } __attribute__((packed)) Status; - __u8 Flags; - __le16 Flags2; /* note: le */ - __le16 PidHigh; - union { - struct { - __le32 SequenceNumber; /* le */ - __u32 Reserved; /* zero */ - } __attribute__((packed)) Sequence; - __u8 SecuritySignature[8]; /* le */ - } __attribute__((packed)) Signature; - __u8 pad[2]; - __u16 Tid; - __le16 Pid; - __u16 Uid; - __le16 Mid; - __u8 WordCount; -} __attribute__((packed)); - -/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */ -static inline void * -BCC(struct smb_hdr *smb) -{ - return (void *)smb + sizeof(*smb) + 2 * smb->WordCount; -} - -/* given a pointer to an smb_hdr retrieve the pointer to the byte area */ -#define pByteArea(smb_var) (BCC(smb_var) + 2) - -/* get the unconverted ByteCount for a SMB packet and return it */ -static inline __u16 -get_bcc(struct smb_hdr *hdr) -{ - __le16 *bc_ptr = (__le16 *)BCC(hdr); - - return get_unaligned_le16(bc_ptr); -} - -/* set the ByteCount for a SMB packet in little-endian */ -static inline void -put_bcc(__u16 count, struct smb_hdr *hdr) -{ - __le16 *bc_ptr = (__le16 *)BCC(hdr); - - put_unaligned_le16(count, bc_ptr); -} - -/* - * Computer Name Length (since Netbios name was length 16 with last byte 0x20) - * No longer as important, now that TCP names are more commonly used to - * resolve hosts. - */ -#define CNLEN 15 - -/* - * Share Name Length (SNLEN) - * Note: This length was limited by the SMB used to get - * the Share info. NetShareEnum only returned 13 - * chars, including the null termination. - * This was removed because it no longer is limiting. - */ - -/* - * Comment Length - */ -#define MAXCOMMENTLEN 40 - -/* - * The OS/2 maximum path name - */ -#define MAX_PATHCONF 256 - -/* - * SMB frame definitions (following must be packed structs) - * See the SNIA CIFS Specification for details. - * - * The Naming convention is the lower case version of the - * smb command code name for the struct and this is typedef to the - * uppercase version of the same name with the prefix SMB_ removed - * for brevity. Although typedefs are not commonly used for - * structure definitions in the Linux kernel, their use in the - * CIFS standards document, which this code is based on, may - * make this one of the cases where typedefs for structures make - * sense to improve readability for readers of the standards doc. - * Typedefs can always be removed later if they are too distracting - * and they are only used for the CIFSs PDUs themselves, not - * internal cifs vfs structures - * - */ - -typedef struct negotiate_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - unsigned char DialectsArray[]; -} __attribute__((packed)) NEGOTIATE_REQ; - -#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */ - -#define READ_RAW_ENABLE 1 -#define WRITE_RAW_ENABLE 2 -#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) -#define SMB1_CLIENT_GUID_SIZE (16) -typedef struct negotiate_rsp { - struct smb_hdr hdr; /* wct = 17 */ - __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ - __u8 SecurityMode; - __le16 MaxMpxCount; - __le16 MaxNumberVcs; - __le32 MaxBufferSize; - __le32 MaxRawSize; - __le32 SessionKey; - __le32 Capabilities; /* see below */ - __le32 SystemTimeLow; - __le32 SystemTimeHigh; - __le16 ServerTimeZone; - __u8 EncryptionKeyLength; - __u16 ByteCount; - union { - /* cap extended security off */ - DECLARE_FLEX_ARRAY(unsigned char, EncryptionKey); - /* followed by Domain name - if extended security is off */ - /* followed by 16 bytes of server GUID */ - /* then security blob if cap_extended_security negotiated */ - struct { - unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; - unsigned char SecurityBlob[]; - } __attribute__((packed)) extended_response; - } __attribute__((packed)) u; -} __attribute__((packed)) NEGOTIATE_RSP; - -/* SecurityMode bits */ -#define SECMODE_USER 0x01 /* off indicates share level security */ -#define SECMODE_PW_ENCRYPT 0x02 -#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ -#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ - -/* Negotiate response Capabilities */ -#define CAP_RAW_MODE 0x00000001 -#define CAP_MPX_MODE 0x00000002 -#define CAP_UNICODE 0x00000004 -#define CAP_LARGE_FILES 0x00000008 -#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ -#define CAP_RPC_REMOTE_APIS 0x00000020 -#define CAP_STATUS32 0x00000040 -#define CAP_LEVEL_II_OPLOCKS 0x00000080 -#define CAP_LOCK_AND_READ 0x00000100 -#define CAP_NT_FIND 0x00000200 -#define CAP_DFS 0x00001000 -#define CAP_INFOLEVEL_PASSTHRU 0x00002000 -#define CAP_LARGE_READ_X 0x00004000 -#define CAP_LARGE_WRITE_X 0x00008000 -#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ -#define CAP_UNIX 0x00800000 -#define CAP_COMPRESSED_DATA 0x02000000 -#define CAP_DYNAMIC_REAUTH 0x20000000 -#define CAP_PERSISTENT_HANDLES 0x40000000 -#define CAP_EXTENDED_SECURITY 0x80000000 - -typedef union smb_com_session_setup_andx { - struct { /* request format */ - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __u32 SessionKey; - __le16 SecurityBlobLength; - __u32 Reserved; - __le32 Capabilities; /* see below */ - __le16 ByteCount; - unsigned char SecurityBlob[1]; /* followed by */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __attribute__((packed)) req; /* NTLM request format (with - extended security */ - - struct { /* request format */ - struct smb_hdr hdr; /* wct = 13 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __u32 SessionKey; - __le16 CaseInsensitivePasswordLength; /* ASCII password len */ - __le16 CaseSensitivePasswordLength; /* Unicode password length*/ - __u32 Reserved; /* see below */ - __le32 Capabilities; - __le16 ByteCount; - unsigned char CaseInsensitivePassword[1]; /* followed by: */ - /* unsigned char * CaseSensitivePassword; */ - /* STRING AccountName */ - /* STRING PrimaryDomain */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __attribute__((packed)) req_no_secext; /* NTLM request format (without - extended security */ - - struct { /* default (NTLM) response format */ - struct smb_hdr hdr; /* wct = 4 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Action; /* see below */ - __le16 SecurityBlobLength; - __u16 ByteCount; - unsigned char SecurityBlob[1]; /* followed by */ -/* unsigned char * NativeOS; */ -/* unsigned char * NativeLanMan; */ -/* unsigned char * PrimaryDomain; */ - } __attribute__((packed)) resp; /* NTLM response - (with or without extended sec) */ - - struct { /* request format */ - struct smb_hdr hdr; /* wct = 10 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __u32 SessionKey; - __le16 PasswordLength; - __u32 Reserved; /* encrypt key len and offset */ - __le16 ByteCount; - unsigned char AccountPassword[1]; /* followed by */ - /* STRING AccountName */ - /* STRING PrimaryDomain */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */ - - struct { /* default (NTLM) response format */ - struct smb_hdr hdr; /* wct = 3 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Action; /* see below */ - __u16 ByteCount; - unsigned char NativeOS[1]; /* followed by */ -/* unsigned char * NativeLanMan; */ -/* unsigned char * PrimaryDomain; */ - } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */ -} __attribute__((packed)) SESSION_SETUP_ANDX; - -/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ - -#define NTLMSSP_SERVER_TYPE 1 -#define NTLMSSP_DOMAIN_TYPE 2 -#define NTLMSSP_FQ_DOMAIN_TYPE 3 -#define NTLMSSP_DNS_DOMAIN_TYPE 4 -#define NTLMSSP_DNS_PARENT_TYPE 5 - -struct ntlmssp2_name { - __le16 type; - __le16 length; -/* char name[length]; */ -} __attribute__((packed)); - -struct ntlmv2_resp { - union { - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - struct { - __u8 reserved[8]; - __u8 key[CIFS_SERVER_CHALLENGE_SIZE]; - } __attribute__((packed)) challenge; - } __attribute__((packed)); - __le32 blob_signature; - __u32 reserved; - __le64 time; - __u64 client_chal; /* random */ - __u32 reserved2; - /* array of name entries could follow ending in minimum 4 byte struct */ -} __attribute__((packed)); - - -#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux" - -/* Capabilities bits (for NTLM SessSetup request) */ -#define CAP_UNICODE 0x00000004 -#define CAP_LARGE_FILES 0x00000008 -#define CAP_NT_SMBS 0x00000010 -#define CAP_STATUS32 0x00000040 -#define CAP_LEVEL_II_OPLOCKS 0x00000080 -#define CAP_NT_FIND 0x00000200 /* reserved should be zero - (because NT_SMBs implies the same thing?) */ -#define CAP_BULK_TRANSFER 0x20000000 -#define CAP_EXTENDED_SECURITY 0x80000000 - -/* Action bits */ -#define GUEST_LOGIN 1 - -typedef struct smb_com_tconx_req { - struct smb_hdr hdr; /* wct = 4 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Flags; /* see below */ - __le16 PasswordLength; - __le16 ByteCount; - unsigned char Password[1]; /* followed by */ -/* STRING Path *//* \\server\share name */ - /* STRING Service */ -} __attribute__((packed)) TCONX_REQ; - -typedef struct smb_com_tconx_rsp { - struct smb_hdr hdr; /* wct = 3 , not extended response */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OptionalSupport; /* see below */ - __u16 ByteCount; - unsigned char Service[1]; /* always ASCII, not Unicode */ - /* STRING NativeFileSystem */ -} __attribute__((packed)) TCONX_RSP; - -typedef struct smb_com_tconx_rsp_ext { - struct smb_hdr hdr; /* wct = 7, extended response */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OptionalSupport; /* see below */ - __le32 MaximalShareAccessRights; - __le32 GuestMaximalShareAccessRights; - __u16 ByteCount; - unsigned char Service[1]; /* always ASCII, not Unicode */ - /* STRING NativeFileSystem */ -} __attribute__((packed)) TCONX_RSP_EXT; - - -/* tree connect Flags */ -#define DISCONNECT_TID 0x0001 -#define TCON_EXTENDED_SIGNATURES 0x0004 -#define TCON_EXTENDED_SECINFO 0x0008 - -/* OptionalSupport bits */ -#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits - (exclusive searches supported) */ -#define SMB_SHARE_IS_IN_DFS 0x0002 -#define SMB_CSC_MASK 0x000C -/* CSC flags defined as follows */ -#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 -#define SMB_CSC_CACHE_AUTO_REINT 0x0004 -#define SMB_CSC_CACHE_VDO 0x0008 -#define SMB_CSC_NO_CACHING 0x000C -#define SMB_UNIQUE_FILE_NAME 0x0010 -#define SMB_EXTENDED_SIGNATURES 0x0020 - -/* services - * - * A: ie disk - * LPT1: ie printer - * IPC ie named pipe - * COMM - * ????? ie any type - * - */ - -typedef struct smb_com_echo_req { - struct smb_hdr hdr; - __le16 EchoCount; - __le16 ByteCount; - char Data[1]; -} __attribute__((packed)) ECHO_REQ; - -typedef struct smb_com_echo_rsp { - struct smb_hdr hdr; - __le16 SequenceNumber; - __le16 ByteCount; - char Data[1]; -} __attribute__((packed)) ECHO_RSP; - -typedef struct smb_com_logoff_andx_req { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __u16 AndXOffset; - __u16 ByteCount; -} __attribute__((packed)) LOGOFF_ANDX_REQ; - -typedef struct smb_com_logoff_andx_rsp { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __u16 AndXOffset; - __u16 ByteCount; -} __attribute__((packed)) LOGOFF_ANDX_RSP; - -typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on - tree_connect PDU to effect disconnect */ - /* tdis is probably simplest SMB PDU */ - struct { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bcc = 0 */ - } __attribute__((packed)) req; - struct { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bcc = 0 */ - } __attribute__((packed)) resp; -} __attribute__((packed)) TREE_DISCONNECT; - -typedef struct smb_com_close_req { - struct smb_hdr hdr; /* wct = 3 */ - __u16 FileID; - __u32 LastWriteTime; /* should be zero or -1 */ - __u16 ByteCount; /* 0 */ -} __attribute__((packed)) CLOSE_REQ; - -typedef struct smb_com_close_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) CLOSE_RSP; - -typedef struct smb_com_flush_req { - struct smb_hdr hdr; /* wct = 1 */ - __u16 FileID; - __u16 ByteCount; /* 0 */ -} __attribute__((packed)) FLUSH_REQ; - -typedef struct smb_com_findclose_req { - struct smb_hdr hdr; /* wct = 1 */ - __u16 FileID; - __u16 ByteCount; /* 0 */ -} __attribute__((packed)) FINDCLOSE_REQ; - -/* OpenFlags */ -#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ -#define REQ_OPLOCK 0x00000002 -#define REQ_BATCHOPLOCK 0x00000004 -#define REQ_OPENDIRONLY 0x00000008 -#define REQ_EXTENDED_INFO 0x00000010 - -/* File type */ -#define DISK_TYPE 0x0000 -#define BYTE_PIPE_TYPE 0x0001 -#define MESSAGE_PIPE_TYPE 0x0002 -#define PRINTER_TYPE 0x0003 -#define COMM_DEV_TYPE 0x0004 -#define UNKNOWN_TYPE 0xFFFF - -/* Device Type or File Status Flags */ -#define NO_EAS 0x0001 -#define NO_SUBSTREAMS 0x0002 -#define NO_REPARSETAG 0x0004 -/* following flags can apply if pipe */ -#define ICOUNT_MASK 0x00FF -#define PIPE_READ_MODE 0x0100 -#define NAMED_PIPE_TYPE 0x0400 -#define PIPE_END_POINT 0x4000 -#define BLOCKING_NAMED_PIPE 0x8000 - -typedef struct smb_com_open_req { /* also handles create */ - struct smb_hdr hdr; /* wct = 24 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 Reserved; /* Must Be Zero */ - __le16 NameLength; - __le32 OpenFlags; - __u32 RootDirectoryFid; - __le32 DesiredAccess; - __le64 AllocationSize; - __le32 FileAttributes; - __le32 ShareAccess; - __le32 CreateDisposition; - __le32 CreateOptions; - __le32 ImpersonationLevel; - __u8 SecurityFlags; - __le16 ByteCount; - char fileName[1]; -} __attribute__((packed)) OPEN_REQ; - -/* open response: oplock levels */ -#define OPLOCK_NONE 0 -#define OPLOCK_EXCLUSIVE 1 -#define OPLOCK_BATCH 2 -#define OPLOCK_READ 3 /* level 2 oplock */ - -/* open response for CreateAction shifted left */ -#define CIFS_CREATE_ACTION 0x20000 /* file created */ - -typedef struct smb_com_open_rsp { - struct smb_hdr hdr; /* wct = 34 BB */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 OplockLevel; - __u16 Fid; - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 FileAttributes; - __le64 AllocationSize; - __le64 EndOfFile; - __le16 FileType; - __le16 DeviceState; - __u8 DirectoryFlag; - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) OPEN_RSP; - -typedef struct smb_com_open_rsp_ext { - struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 OplockLevel; - __u16 Fid; - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 FileAttributes; - __le64 AllocationSize; - __le64 EndOfFile; - __le16 FileType; - __le16 DeviceState; - __u8 DirectoryFlag; - __u8 VolumeGUID[16]; - __u64 FileId; /* note no endian conversion - is opaque UniqueID */ - __le32 MaximalAccessRights; - __le32 GuestMaximalAccessRights; - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) OPEN_RSP_EXT; - - -/* format of legacy open request */ -typedef struct smb_com_openx_req { - struct smb_hdr hdr; /* wct = 15 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OpenFlags; - __le16 Mode; - __le16 Sattr; /* search attributes */ - __le16 FileAttributes; /* dos attrs */ - __le32 CreateTime; /* os2 format */ - __le16 OpenFunction; - __le32 EndOfFile; - __le32 Timeout; - __le32 Reserved; - __le16 ByteCount; /* file name follows */ - char fileName[1]; -} __attribute__((packed)) OPENX_REQ; - -typedef struct smb_com_openx_rsp { - struct smb_hdr hdr; /* wct = 15 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le16 FileAttributes; - __le32 LastWriteTime; /* os2 format */ - __le32 EndOfFile; - __le16 Access; - __le16 FileType; - __le16 IPCState; - __le16 Action; - __u32 FileId; - __u16 Reserved; - __u16 ByteCount; -} __attribute__((packed)) OPENX_RSP; - -/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ - -/* Legacy write request for older servers */ -typedef struct smb_com_writex_req { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __u32 Reserved; /* Timeout */ - __le16 WriteMode; /* 1 = write through */ - __le16 Remaining; - __le16 Reserved2; - __le16 DataLengthLow; - __le16 DataOffset; - __le16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD - boundary and optimum performance here */ - char Data[]; -} __attribute__((packed)) WRITEX_REQ; - -typedef struct smb_com_write_req { - struct smb_hdr hdr; /* wct = 14 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __u32 Reserved; - __le16 WriteMode; - __le16 Remaining; - __le16 DataLengthHigh; - __le16 DataLengthLow; - __le16 DataOffset; - __le32 OffsetHigh; - __le16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD - boundary and optimum performance here */ - char Data[]; -} __attribute__((packed)) WRITE_REQ; - -typedef struct smb_com_write_rsp { - struct smb_hdr hdr; /* wct = 6 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Count; - __le16 Remaining; - __le16 CountHigh; - __u16 Reserved; - __u16 ByteCount; -} __attribute__((packed)) WRITE_RSP; - -/* legacy read request for older servers */ -typedef struct smb_com_readx_req { - struct smb_hdr hdr; /* wct = 10 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __le16 MaxCount; - __le16 MinCount; /* obsolete */ - __le32 Reserved; - __le16 Remaining; - __le16 ByteCount; -} __attribute__((packed)) READX_REQ; - -typedef struct smb_com_read_req { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __le16 MaxCount; - __le16 MinCount; /* obsolete */ - __le32 MaxCountHigh; - __le16 Remaining; - __le32 OffsetHigh; - __le16 ByteCount; -} __attribute__((packed)) READ_REQ; - -typedef struct smb_com_read_rsp { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Remaining; - __le16 DataCompactionMode; - __le16 Reserved; - __le16 DataLength; - __le16 DataOffset; - __le16 DataLengthHigh; - __u64 Reserved2; - __u16 ByteCount; - /* read response data immediately follows */ -} __attribute__((packed)) READ_RSP; - -typedef struct locking_andx_range { - __le16 Pid; - __le16 Pad; - __le32 OffsetHigh; - __le32 OffsetLow; - __le32 LengthHigh; - __le32 LengthLow; -} __attribute__((packed)) LOCKING_ANDX_RANGE; - -#define LOCKING_ANDX_SHARED_LOCK 0x01 -#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 -#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 -#define LOCKING_ANDX_CANCEL_LOCK 0x08 -#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ - -typedef struct smb_com_lock_req { - struct smb_hdr hdr; /* wct = 8 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __u8 LockType; - __u8 OplockLevel; - __le32 Timeout; - __le16 NumberOfUnlocks; - __le16 NumberOfLocks; - __le16 ByteCount; - LOCKING_ANDX_RANGE Locks[1]; -} __attribute__((packed)) LOCK_REQ; - -/* lock type */ -#define CIFS_RDLCK 0 -#define CIFS_WRLCK 1 -#define CIFS_UNLCK 2 -typedef struct cifs_posix_lock { - __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */ - __le16 lock_flags; /* 1 = Wait (only valid for setlock) */ - __le32 pid; - __le64 start; - __le64 length; - /* BB what about additional owner info to identify network client */ -} __attribute__((packed)) CIFS_POSIX_LOCK; - -typedef struct smb_com_lock_rsp { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 ByteCount; -} __attribute__((packed)) LOCK_RSP; - -typedef struct smb_com_rename_req { - struct smb_hdr hdr; /* wct = 1 */ - __le16 SearchAttributes; /* target file attributes */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[1]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName */ -} __attribute__((packed)) RENAME_REQ; - - /* copy request flags */ -#define COPY_MUST_BE_FILE 0x0001 -#define COPY_MUST_BE_DIR 0x0002 -#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */ -#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */ -#define COPY_VERIFY_WRITES 0x0010 -#define COPY_TREE 0x0020 - -typedef struct smb_com_copy_req { - struct smb_hdr hdr; /* wct = 3 */ - __u16 Tid2; - __le16 OpenFunction; - __le16 Flags; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[1]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName string */ -} __attribute__((packed)) COPY_REQ; - -typedef struct smb_com_copy_rsp { - struct smb_hdr hdr; /* wct = 1 */ - __le16 CopyCount; /* number of files copied */ - __u16 ByteCount; /* may be zero */ - __u8 BufferFormat; /* 0x04 - only present if errored file follows */ - unsigned char ErrorFileName[1]; /* only present if error in copy */ -} __attribute__((packed)) COPY_RSP; - -#define CREATE_HARD_LINK 0x103 -#define MOVEFILE_COPY_ALLOWED 0x0002 -#define MOVEFILE_REPLACE_EXISTING 0x0001 - -typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ - struct smb_hdr hdr; /* wct = 4 */ - __le16 SearchAttributes; /* target file attributes */ - __le16 Flags; /* spec says Information Level */ - __le32 ClusterCount; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[1]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName */ -} __attribute__((packed)) NT_RENAME_REQ; - -typedef struct smb_com_rename_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) RENAME_RSP; - -typedef struct smb_com_delete_file_req { - struct smb_hdr hdr; /* wct = 1 */ - __le16 SearchAttributes; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char fileName[1]; -} __attribute__((packed)) DELETE_FILE_REQ; - -typedef struct smb_com_delete_file_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) DELETE_FILE_RSP; - -typedef struct smb_com_delete_directory_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char DirName[1]; -} __attribute__((packed)) DELETE_DIRECTORY_REQ; - -typedef struct smb_com_delete_directory_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) DELETE_DIRECTORY_RSP; - -typedef struct smb_com_create_directory_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char DirName[1]; -} __attribute__((packed)) CREATE_DIRECTORY_REQ; - -typedef struct smb_com_create_directory_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) CREATE_DIRECTORY_RSP; - -typedef struct smb_com_query_information_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; /* 1 + namelen + 1 */ - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char FileName[1]; -} __attribute__((packed)) QUERY_INFORMATION_REQ; - -typedef struct smb_com_query_information_rsp { - struct smb_hdr hdr; /* wct = 10 */ - __le16 attr; - __le32 last_write_time; - __le32 size; - __u16 reserved[5]; - __le16 ByteCount; /* bcc = 0 */ -} __attribute__((packed)) QUERY_INFORMATION_RSP; - -typedef struct smb_com_setattr_req { - struct smb_hdr hdr; /* wct = 8 */ - __le16 attr; - __le16 time_low; - __le16 time_high; - __le16 reserved[5]; /* must be zero */ - __u16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char fileName[1]; -} __attribute__((packed)) SETATTR_REQ; - -typedef struct smb_com_setattr_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) SETATTR_RSP; - -/* empty wct response to setattr */ - -/*******************************************************/ -/* NT Transact structure definitions follow */ -/* Currently only ioctl, acl (get security descriptor) */ -/* and notify are implemented */ -/*******************************************************/ -typedef struct smb_com_ntransact_req { - struct smb_hdr hdr; /* wct >= 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - /* SetupCount words follow then */ - __le16 ByteCount; - __u8 Pad[3]; - __u8 Parms[]; -} __attribute__((packed)) NTRANSACT_REQ; - -typedef struct smb_com_ntransact_rsp { - struct smb_hdr hdr; /* wct = 18 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 0 */ - __u16 ByteCount; - /* __u8 Pad[3]; */ - /* parms and data follow */ -} __attribute__((packed)) NTRANSACT_RSP; - -/* See MS-SMB 2.2.7.2.1.1 */ -struct srv_copychunk { - __le64 SourceOffset; - __le64 DestinationOffset; - __le32 CopyLength; - __u32 Reserved; -} __packed; - -typedef struct smb_com_transaction_ioctl_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - __le32 FunctionCode; - __u16 Fid; - __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ - __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ - __le16 ByteCount; - __u8 Pad[3]; - __u8 Data[1]; -} __attribute__((packed)) TRANSACT_IOCTL_REQ; - -typedef struct smb_com_transaction_compr_ioctl_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - __le32 FunctionCode; - __u16 Fid; - __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ - __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ - __le16 ByteCount; - __u8 Pad[3]; - __le16 compression_state; /* See below for valid flags */ -} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ; - -/* compression state flags */ -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_DEFAULT 0x0001 -#define COMPRESSION_FORMAT_LZNT1 0x0002 - -typedef struct smb_com_transaction_ioctl_rsp { - struct smb_hdr hdr; /* wct = 19 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 1 */ - __le16 ReturnedDataLen; - __u16 ByteCount; -} __attribute__((packed)) TRANSACT_IOCTL_RSP; - -#define CIFS_ACL_OWNER 1 -#define CIFS_ACL_GROUP 2 -#define CIFS_ACL_DACL 4 -#define CIFS_ACL_SACL 8 - -typedef struct smb_com_transaction_qsec_req { - struct smb_hdr hdr; /* wct = 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* no setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */ - __le16 ByteCount; /* bcc = 3 + 8 */ - __u8 Pad[3]; - __u16 Fid; - __u16 Reserved2; - __le32 AclFlags; -} __attribute__((packed)) QUERY_SEC_DESC_REQ; - - -typedef struct smb_com_transaction_ssec_req { - struct smb_hdr hdr; /* wct = 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* no setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ - __le16 ByteCount; /* bcc = 3 + 8 */ - __u8 Pad[3]; - __u16 Fid; - __u16 Reserved2; - __le32 AclFlags; -} __attribute__((packed)) SET_SEC_DESC_REQ; - -typedef struct smb_com_transaction_change_notify_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand;/* 4 = Change Notify */ - __le32 CompletionFilter; /* operation to monitor */ - __u16 Fid; - __u8 WatchTree; /* 1 = Monitor subdirectories */ - __u8 Reserved2; - __le16 ByteCount; -/* __u8 Pad[3];*/ -/* __u8 Data[1];*/ -} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ; - -/* BB eventually change to use generic ntransact rsp struct - and validation routine */ -typedef struct smb_com_transaction_change_notify_rsp { - struct smb_hdr hdr; /* wct = 18 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 0 */ - __u16 ByteCount; - /* __u8 Pad[3]; */ -} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_RSP; -/* Completion Filter flags for Notify */ -#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 -#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 -#define FILE_NOTIFY_CHANGE_NAME 0x00000003 -#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 -#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 -#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 -#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 -#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 -#define FILE_NOTIFY_CHANGE_EA 0x00000080 -#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 -#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 -#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 -#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 - -#define FILE_ACTION_ADDED 0x00000001 -#define FILE_ACTION_REMOVED 0x00000002 -#define FILE_ACTION_MODIFIED 0x00000003 -#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 -#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 -#define FILE_ACTION_ADDED_STREAM 0x00000006 -#define FILE_ACTION_REMOVED_STREAM 0x00000007 -#define FILE_ACTION_MODIFIED_STREAM 0x00000008 - -/* response contains array of the following structures */ -struct file_notify_information { - __le32 NextEntryOffset; - __le32 Action; - __le32 FileNameLength; - __u8 FileName[]; -} __attribute__((packed)); - -/* For IO_REPARSE_TAG_SYMLINK */ -struct reparse_symlink_data { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __le32 Flags; - char PathBuffer[]; -} __attribute__((packed)); - -/* Flag above */ -#define SYMLINK_FLAG_RELATIVE 0x00000001 - -/* For IO_REPARSE_TAG_NFS */ -#define NFS_SPECFILE_LNK 0x00000000014B4E4C -#define NFS_SPECFILE_CHR 0x0000000000524843 -#define NFS_SPECFILE_BLK 0x00000000004B4C42 -#define NFS_SPECFILE_FIFO 0x000000004F464946 -#define NFS_SPECFILE_SOCK 0x000000004B434F53 -struct reparse_posix_data { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le64 InodeType; /* LNK, FIFO, CHR etc. */ - char PathBuffer[]; -} __attribute__((packed)); - -struct cifs_quota_data { - __u32 rsrvd1; /* 0 */ - __u32 sid_size; - __u64 rsrvd2; /* 0 */ - __u64 space_used; - __u64 soft_limit; - __u64 hard_limit; - char sid[1]; /* variable size? */ -} __attribute__((packed)); - -/* quota sub commands */ -#define QUOTA_LIST_CONTINUE 0 -#define QUOTA_LIST_START 0x100 -#define QUOTA_FOR_SID 0x101 - -struct trans2_req { - /* struct smb_hdr hdr precedes. Set wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* 1st setup word - SetupCount words follow */ - __le16 ByteCount; -} __attribute__((packed)); - -struct smb_t2_req { - struct smb_hdr hdr; - struct trans2_req t2_req; -} __attribute__((packed)); - -struct trans2_resp { - /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; - /* SetupWords[SetupCount]; - __u16 ByteCount; - __u16 Reserved2;*/ - /* data area follows */ -} __attribute__((packed)); - -struct smb_t2_rsp { - struct smb_hdr hdr; - struct trans2_resp t2_rsp; -} __attribute__((packed)); - -/* PathInfo/FileInfo infolevels */ -#define SMB_INFO_STANDARD 1 -#define SMB_SET_FILE_EA 2 -#define SMB_QUERY_FILE_EA_SIZE 2 -#define SMB_INFO_QUERY_EAS_FROM_LIST 3 -#define SMB_INFO_QUERY_ALL_EAS 4 -#define SMB_INFO_IS_NAME_VALID 6 -#define SMB_QUERY_FILE_BASIC_INFO 0x101 -#define SMB_QUERY_FILE_STANDARD_INFO 0x102 -#define SMB_QUERY_FILE_EA_INFO 0x103 -#define SMB_QUERY_FILE_NAME_INFO 0x104 -#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 -#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 -#define SMB_QUERY_FILE_ALL_INFO 0x107 -#define SMB_QUERY_ALT_NAME_INFO 0x108 -#define SMB_QUERY_FILE_STREAM_INFO 0x109 -#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B -#define SMB_QUERY_FILE_UNIX_BASIC 0x200 -#define SMB_QUERY_FILE_UNIX_LINK 0x201 -#define SMB_QUERY_POSIX_ACL 0x204 -#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ -#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ -#define SMB_QUERY_POSIX_PERMISSION 0x207 -#define SMB_QUERY_POSIX_LOCK 0x208 -/* #define SMB_POSIX_OPEN 0x209 */ -/* #define SMB_POSIX_UNLINK 0x20a */ -#define SMB_QUERY_FILE__UNIX_INFO2 0x20b -#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee -#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 -#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ -#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 -#define SMB_QUERY_FILE_MODE_INFO 0x3f8 -#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 - - -#define SMB_SET_FILE_BASIC_INFO 0x101 -#define SMB_SET_FILE_DISPOSITION_INFO 0x102 -#define SMB_SET_FILE_ALLOCATION_INFO 0x103 -#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 -#define SMB_SET_FILE_UNIX_BASIC 0x200 -#define SMB_SET_FILE_UNIX_LINK 0x201 -#define SMB_SET_FILE_UNIX_HLINK 0x203 -#define SMB_SET_POSIX_ACL 0x204 -#define SMB_SET_XATTR 0x205 -#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ -#define SMB_SET_POSIX_LOCK 0x208 -#define SMB_POSIX_OPEN 0x209 -#define SMB_POSIX_UNLINK 0x20a -#define SMB_SET_FILE_UNIX_INFO2 0x20b -#define SMB_SET_FILE_BASIC_INFO2 0x3ec -#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ -#define SMB_FILE_ALL_INFO2 0x3fa -#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb -#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc -#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 -#define SMB_FILE_QUOTA_INFO 0x408 -#define SMB_FILE_REPARSEPOINT_INFO 0x409 -#define SMB_FILE_MAXIMUM_INFO 0x40d - -/* Find File infolevels */ -#define SMB_FIND_FILE_INFO_STANDARD 0x001 -#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 -#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 -#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 -#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 -#define SMB_FIND_FILE_NAMES_INFO 0x103 -#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 -#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 -#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 -#define SMB_FIND_FILE_UNIX 0x202 -/* #define SMB_FIND_FILE_POSIX_INFO 0x064 */ - -typedef struct smb_com_transaction2_qpi_req { - struct smb_hdr hdr; /* wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __le16 InformationLevel; - __u32 Reserved4; - char FileName[1]; -} __attribute__((packed)) TRANSACTION2_QPI_REQ; - -typedef struct smb_com_transaction2_qpi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word is present for infolevels > 100 */ -} __attribute__((packed)) TRANSACTION2_QPI_RSP; - -typedef struct smb_com_transaction2_spi_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __u16 Pad1; - __le16 InformationLevel; - __u32 Reserved4; - char FileName[1]; -} __attribute__((packed)) TRANSACTION2_SPI_REQ; - -typedef struct smb_com_transaction2_spi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word is present for infolevels > 100 */ -} __attribute__((packed)) TRANSACTION2_SPI_RSP; - -struct set_file_rename { - __le32 overwrite; /* 1 = overwrite dest */ - __u32 root_fid; /* zero */ - __le32 target_name_len; - char target_name[]; /* Must be unicode */ -} __attribute__((packed)); - -struct smb_com_transaction2_sfi_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __u16 Pad1; - __u16 Fid; - __le16 InformationLevel; - __u16 Reserved4; - __u8 payload[]; -} __attribute__((packed)); - -struct smb_com_transaction2_sfi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - - present for infolevels > 100 */ -} __attribute__((packed)); - -struct smb_t2_qfi_req { - struct smb_hdr hdr; - struct trans2_req t2; - __u8 Pad; - __u16 Fid; - __le16 InformationLevel; -} __attribute__((packed)); - -struct smb_t2_qfi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - - present for infolevels > 100 */ -} __attribute__((packed)); - -/* - * Flags on T2 FINDFIRST and FINDNEXT - */ -#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 -#define CIFS_SEARCH_CLOSE_AT_END 0x0002 -#define CIFS_SEARCH_RETURN_RESUME 0x0004 -#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 -#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 - -/* - * Size of the resume key on FINDFIRST and FINDNEXT calls - */ -#define CIFS_SMB_RESUME_KEY_SIZE 4 - -typedef struct smb_com_transaction2_ffirst_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_FIND_FIRST */ - __le16 ByteCount; - __u8 Pad; - __le16 SearchAttributes; - __le16 SearchCount; - __le16 SearchFlags; - __le16 InformationLevel; - __le32 SearchStorageType; - char FileName[1]; -} __attribute__((packed)) TRANSACTION2_FFIRST_REQ; - -typedef struct smb_com_transaction2_ffirst_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __attribute__((packed)) TRANSACTION2_FFIRST_RSP; - -typedef struct smb_com_transaction2_ffirst_rsp_parms { - __u16 SearchHandle; - __le16 SearchCount; - __le16 EndofSearch; - __le16 EAErrorOffset; - __le16 LastNameOffset; -} __attribute__((packed)) T2_FFIRST_RSP_PARMS; - -typedef struct smb_com_transaction2_fnext_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_FIND_NEXT */ - __le16 ByteCount; - __u8 Pad; - __u16 SearchHandle; - __le16 SearchCount; - __le16 InformationLevel; - __u32 ResumeKey; - __le16 SearchFlags; - char ResumeFileName[]; -} __attribute__((packed)) TRANSACTION2_FNEXT_REQ; - -typedef struct smb_com_transaction2_fnext_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __attribute__((packed)) TRANSACTION2_FNEXT_RSP; - -typedef struct smb_com_transaction2_fnext_rsp_parms { - __le16 SearchCount; - __le16 EndofSearch; - __le16 EAErrorOffset; - __le16 LastNameOffset; -} __attribute__((packed)) T2_FNEXT_RSP_PARMS; - -/* QFSInfo Levels */ -#define SMB_INFO_ALLOCATION 1 -#define SMB_INFO_VOLUME 2 -#define SMB_QUERY_FS_VOLUME_INFO 0x102 -#define SMB_QUERY_FS_SIZE_INFO 0x103 -#define SMB_QUERY_FS_DEVICE_INFO 0x104 -#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 -#define SMB_QUERY_CIFS_UNIX_INFO 0x200 -#define SMB_QUERY_POSIX_FS_INFO 0x201 -#define SMB_QUERY_POSIX_WHO_AM_I 0x202 -#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 -#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure - FILE_SYSTEM__UNIX_INFO to tell - whether new NTIOCTL available - (0xACE) for WAN friendly SMB - operations to be carried */ -#define SMB_QUERY_LABEL_INFO 0x3ea -#define SMB_QUERY_FS_QUOTA_INFO 0x3ee -#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef -#define SMB_QUERY_OBJECTID_INFO 0x3f0 - -typedef struct smb_com_transaction2_qfsi_req { - struct smb_hdr hdr; /* wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __le16 InformationLevel; -} __attribute__((packed)) TRANSACTION2_QFSI_REQ; - -typedef struct smb_com_transaction_qfsi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u8 Pad; /* may be three bytes? *//* followed by data area */ -} __attribute__((packed)) TRANSACTION2_QFSI_RSP; - -typedef struct whoami_rsp_data { /* Query level 0x202 */ - __u32 flags; /* 0 = Authenticated user 1 = GUEST */ - __u32 mask; /* which flags bits server understands ie 0x0001 */ - __u64 unix_user_id; - __u64 unix_user_gid; - __u32 number_of_supplementary_gids; /* may be zero */ - __u32 number_of_sids; /* may be zero */ - __u32 length_of_sid_array; /* in bytes - may be zero */ - __u32 pad; /* reserved - MBZ */ - /* __u64 gid_array[0]; */ /* may be empty */ - /* __u8 * psid_list */ /* may be empty */ -} __attribute__((packed)) WHOAMI_RSP_DATA; - -/* SETFSInfo Levels */ -#define SMB_SET_CIFS_UNIX_INFO 0x200 -/* level 0x203 is defined above in list of QFS info levels */ -/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */ - -/* Level 0x200 request structure follows */ -typedef struct smb_com_transaction2_setfsi_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; /* 4 */ - __le16 ParameterOffset; - __le16 DataCount; /* 12 */ - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ - __le16 ByteCount; - __u8 Pad; - __u16 FileNum; /* Parameters start. */ - __le16 InformationLevel;/* Parameters end. */ - __le16 ClientUnixMajor; /* Data start. */ - __le16 ClientUnixMinor; - __le64 ClientUnixCap; /* Data end */ -} __attribute__((packed)) TRANSACTION2_SETFSI_REQ; - -/* level 0x203 request structure follows */ -typedef struct smb_com_transaction2_setfs_enc_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; /* 4 */ - __le16 ParameterOffset; - __le16 DataCount; /* 12 */ - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ - __le16 ByteCount; - __u8 Pad; - __u16 Reserved4; /* Parameters start. */ - __le16 InformationLevel;/* Parameters end. */ - /* NTLMSSP Blob, Data start. */ -} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ; - -/* response for setfsinfo levels 0x200 and 0x203 */ -typedef struct smb_com_transaction2_setfsi_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __attribute__((packed)) TRANSACTION2_SETFSI_RSP; - -typedef struct smb_com_transaction2_get_dfs_refer_req { - struct smb_hdr hdr; /* wct = 15 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length - perhaps?) followed by one byte pad - doesn't - seem to matter though */ - __le16 MaxReferralLevel; - char RequestFileName[1]; -} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ; - -#define DFS_VERSION cpu_to_le16(0x0003) - -/* DFS server target type */ -#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */ -#define DFS_TYPE_ROOT 0x0001 - -/* Referral Entry Flags */ -#define DFS_NAME_LIST_REF 0x0200 /* set for domain or DC referral responses */ -#define DFS_TARGET_SET_BOUNDARY 0x0400 /* only valid with version 4 dfs req */ - -typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */ - __le16 VersionNumber; /* must be 3 or 4 */ - __le16 Size; - __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ - __le16 ReferralEntryFlags; - __le32 TimeToLive; - __le16 DfsPathOffset; - __le16 DfsAlternatePathOffset; - __le16 NetworkAddressOffset; /* offset of the link target */ - __u8 ServiceSiteGuid[16]; /* MBZ, ignored */ -} __attribute__((packed)) REFERRAL3; - -struct get_dfs_referral_rsp { - __le16 PathConsumed; - __le16 NumberOfReferrals; - __le32 DFSFlags; - REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */ - /* followed by the strings pointed to by the referral structures */ -} __packed; - -typedef struct smb_com_transaction_get_dfs_refer_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; - __u8 Pad; - struct get_dfs_referral_rsp dfs_data; -} __packed TRANSACTION2_GET_DFS_REFER_RSP; - -/* DFS Flags */ -#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */ -#define DFSREF_STORAGE_SERVER 0x00000002 /* no further ref requests needed */ -#define DFSREF_TARGET_FAILBACK 0x00000004 /* only for DFS referral version 4 */ - -/* - ************************************************************************ - * All structs for everything above the SMB PDUs themselves - * (such as the T2 level specific data) go here - ************************************************************************ - */ - -/* - * Information on a server - */ - -struct serverInfo { - char name[16]; - unsigned char versionMajor; - unsigned char versionMinor; - unsigned long type; - unsigned int commentOffset; -} __attribute__((packed)); - -/* - * The following structure is the format of the data returned on a NetShareEnum - * with level "90" (x5A) - */ - -struct shareInfo { - char shareName[13]; - char pad; - unsigned short type; - unsigned int commentOffset; -} __attribute__((packed)); - -struct aliasInfo { - char aliasName[9]; - char pad; - unsigned int commentOffset; - unsigned char type[2]; -} __attribute__((packed)); - -struct aliasInfo92 { - int aliasNameOffset; - int serverNameOffset; - int shareNameOffset; -} __attribute__((packed)); - -typedef struct { - __le64 TotalAllocationUnits; - __le64 FreeAllocationUnits; - __le32 SectorsPerAllocationUnit; - __le32 BytesPerSector; -} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */ - -typedef struct { - __le32 fsid; - __le32 SectorsPerAllocationUnit; - __le32 TotalAllocationUnits; - __le32 FreeAllocationUnits; - __le16 BytesPerSector; -} __attribute__((packed)) FILE_SYSTEM_ALLOC_INFO; - -typedef struct { - __le16 MajorVersionNumber; - __le16 MinorVersionNumber; - __le64 Capability; -} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/ - -/* Version numbers for CIFS UNIX major and minor. */ -#define CIFS_UNIX_MAJOR_VERSION 1 -#define CIFS_UNIX_MINOR_VERSION 0 - -/* Linux/Unix extensions capability flags */ -#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ -#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ -#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ -#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ -#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */ -#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based - calls including posix open - and posix unlink */ -#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up - to 0xFFFF00 */ -#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 -#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ -#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ -#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and - QFS PROXY call */ -#ifdef CONFIG_CIFS_POSIX -/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send - LockingX instead of posix locking call on unix sess (and we do not expect - LockingX to use different (ie Windows) semantics than posix locking on - the same session (if WINE needs to do this later, we can add this cap - back in later */ -/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ -#define CIFS_UNIX_CAP_MASK 0x000003db -#else -#define CIFS_UNIX_CAP_MASK 0x00000013 -#endif /* CONFIG_CIFS_POSIX */ - - -#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ - -typedef struct { - /* For undefined recommended transfer size return -1 in that field */ - __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ - __le32 BlockSize; - /* The next three fields are in terms of the block size. - (above). If block size is unknown, 4096 would be a - reasonable block size for a server to report. - Note that returning the blocks/blocksavail removes need - to make a second call (to QFSInfo level 0x103 to get this info. - UserBlockAvail is typically less than or equal to BlocksAvail, - if no distinction is made return the same value in each */ - __le64 TotalBlocks; - __le64 BlocksAvail; /* bfree */ - __le64 UserBlocksAvail; /* bavail */ - /* For undefined Node fields or FSID return -1 */ - __le64 TotalFileNodes; - __le64 FreeFileNodes; - __le64 FileSysIdentifier; /* fsid */ - /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ - /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ -} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO; - -/* DeviceType Flags */ -#define FILE_DEVICE_CD_ROM 0x00000002 -#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 -#define FILE_DEVICE_DFS 0x00000006 -#define FILE_DEVICE_DISK 0x00000007 -#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 -#define FILE_DEVICE_FILE_SYSTEM 0x00000009 -#define FILE_DEVICE_NAMED_PIPE 0x00000011 -#define FILE_DEVICE_NETWORK 0x00000012 -#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 -#define FILE_DEVICE_NULL 0x00000015 -#define FILE_DEVICE_PARALLEL_PORT 0x00000016 -#define FILE_DEVICE_PRINTER 0x00000018 -#define FILE_DEVICE_SERIAL_PORT 0x0000001b -#define FILE_DEVICE_STREAMS 0x0000001e -#define FILE_DEVICE_TAPE 0x0000001f -#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 -#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 -#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 - -/* Device Characteristics */ -#define FILE_REMOVABLE_MEDIA 0x00000001 -#define FILE_READ_ONLY_DEVICE 0x00000002 -#define FILE_FLOPPY_DISKETTE 0x00000004 -#define FILE_WRITE_ONCE_MEDIA 0x00000008 -#define FILE_REMOTE_DEVICE 0x00000010 -#define FILE_DEVICE_IS_MOUNTED 0x00000020 -#define FILE_VIRTUAL_VOLUME 0x00000040 -#define FILE_DEVICE_SECURE_OPEN 0x00000100 -#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000 -#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000 -#define FILE_PORTABLE_DEVICE 0x00004000 -#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 - -typedef struct { - __le32 DeviceType; - __le32 DeviceCharacteristics; -} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ - -/* minimum includes first three fields, and empty FS Name */ -#define MIN_FS_ATTR_INFO_SIZE 12 - - -/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ -#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ -#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ -#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 -#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 -#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 -#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 -#define FILE_SUPPORTS_HARD_LINKS 0x00400000 -#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 -#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 -#define FILE_READ_ONLY_VOLUME 0x00080000 -#define FILE_NAMED_STREAMS 0x00040000 -#define FILE_SUPPORTS_ENCRYPTION 0x00020000 -#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 -#define FILE_VOLUME_IS_COMPRESSED 0x00008000 -#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 -#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 -#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 -#define FILE_VOLUME_QUOTAS 0x00000020 -#define FILE_FILE_COMPRESSION 0x00000010 -#define FILE_PERSISTENT_ACLS 0x00000008 -#define FILE_UNICODE_ON_DISK 0x00000004 -#define FILE_CASE_PRESERVED_NAMES 0x00000002 -#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 -typedef struct { - __le32 Attributes; - __le32 MaxPathNameComponentLength; - __le32 FileSystemNameLen; - char FileSystemName[52]; /* do not have to save this - get subset? */ -} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO; - -/******************************************************************************/ -/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ -/******************************************************************************/ -typedef struct { /* data block encoding of response to level 263 QPathInfo */ - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; - __le64 AllocationSize; - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad2; - __le64 IndexNumber; - __le32 EASize; - __le32 AccessFlags; - __u64 IndexNumber1; - __le64 CurrentByteOffset; - __le32 Mode; - __le32 AlignmentRequirement; - __le32 FileNameLength; - char FileName[1]; -} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ - -typedef struct { - __le64 AllocationSize; - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad; -} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ - - -/* defines for enumerating possible values of the Unix type field below */ -#define UNIX_FILE 0 -#define UNIX_DIR 1 -#define UNIX_SYMLINK 2 -#define UNIX_CHARDEV 3 -#define UNIX_BLOCKDEV 4 -#define UNIX_FIFO 5 -#define UNIX_SOCKET 6 -typedef struct { - __le64 EndOfFile; - __le64 NumOfBytes; - __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ - __le64 LastAccessTime; - __le64 LastModificationTime; - __le64 Uid; - __le64 Gid; - __le32 Type; - __le64 DevMajor; - __le64 DevMinor; - __le64 UniqueId; - __le64 Permissions; - __le64 Nlinks; -} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ - -typedef struct { - char LinkDest[1]; -} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */ - -/* The following three structures are needed only for - setting time to NT4 and some older servers via - the primitive DOS time format */ -typedef struct { - __u16 Day:5; - __u16 Month:4; - __u16 Year:7; -} __attribute__((packed)) SMB_DATE; - -typedef struct { - __u16 TwoSeconds:5; - __u16 Minutes:6; - __u16 Hours:5; -} __attribute__((packed)) SMB_TIME; - -typedef struct { - __le16 CreationDate; /* SMB Date see above */ - __le16 CreationTime; /* SMB Time */ - __le16 LastAccessDate; - __le16 LastAccessTime; - __le16 LastWriteDate; - __le16 LastWriteTime; - __le32 DataSize; /* File Size (EOF) */ - __le32 AllocationSize; - __le16 Attributes; /* verify not u32 */ - __le32 EASize; -} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ - -typedef struct { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad; -} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */ - -struct file_allocation_info { - __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ -} __attribute__((packed)); /* size used on disk, for level 0x103 for set, - 0x105 for query */ - -struct file_end_of_file_info { - __le64 FileSize; /* offset to end of file */ -} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */ - -struct file_alt_name_info { - __u8 alt_name[1]; -} __attribute__((packed)); /* level 0x0108 */ - -struct file_stream_info { - __le32 number_of_streams; /* BB check sizes and verify location */ - /* followed by info on streams themselves - u64 size; - u64 allocation_size - stream info */ -}; /* level 0x109 */ - -struct file_compression_info { - __le64 compressed_size; - __le16 format; - __u8 unit_shift; - __u8 ch_shift; - __u8 cl_shift; - __u8 pad[3]; -} __attribute__((packed)); /* level 0x10b */ - -/* POSIX ACL set/query path info structures */ -#define CIFS_ACL_VERSION 1 -struct cifs_posix_ace { /* access control entry (ACE) */ - __u8 cifs_e_tag; - __u8 cifs_e_perm; - __le64 cifs_uid; /* or gid */ -} __attribute__((packed)); - -struct cifs_posix_acl { /* access conrol list (ACL) */ - __le16 version; - __le16 access_entry_count; /* access ACL - count of entries */ - __le16 default_entry_count; /* default ACL - count of entries */ - struct cifs_posix_ace ace_array[]; - /* followed by - struct cifs_posix_ace default_ace_arraay[] */ -} __attribute__((packed)); /* level 0x204 */ - -/* types of access control entries already defined in posix_acl.h */ -/* #define CIFS_POSIX_ACL_USER_OBJ 0x01 -#define CIFS_POSIX_ACL_USER 0x02 -#define CIFS_POSIX_ACL_GROUP_OBJ 0x04 -#define CIFS_POSIX_ACL_GROUP 0x08 -#define CIFS_POSIX_ACL_MASK 0x10 -#define CIFS_POSIX_ACL_OTHER 0x20 */ - -/* types of perms */ -/* #define CIFS_POSIX_ACL_EXECUTE 0x01 -#define CIFS_POSIX_ACL_WRITE 0x02 -#define CIFS_POSIX_ACL_READ 0x04 */ - -/* end of POSIX ACL definitions */ - -/* POSIX Open Flags */ -#define SMB_O_RDONLY 0x1 -#define SMB_O_WRONLY 0x2 -#define SMB_O_RDWR 0x4 -#define SMB_O_CREAT 0x10 -#define SMB_O_EXCL 0x20 -#define SMB_O_TRUNC 0x40 -#define SMB_O_APPEND 0x80 -#define SMB_O_SYNC 0x100 -#define SMB_O_DIRECTORY 0x200 -#define SMB_O_NOFOLLOW 0x400 -#define SMB_O_DIRECT 0x800 - -typedef struct { - __le32 OpenFlags; /* same as NT CreateX */ - __le32 PosixOpenFlags; - __le64 Permissions; - __le16 Level; /* reply level requested (see QPathInfo levels) */ -} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */ - -typedef struct { - __le16 OplockFlags; - __u16 Fid; - __le32 CreateAction; - __le16 ReturnedLevel; - __le16 Pad; - /* struct following varies based on requested level */ -} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ - -#define SMB_POSIX_UNLINK_FILE_TARGET 0 -#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 - -struct unlink_psx_rq { /* level 0x20a SetPathInfo */ - __le16 type; -} __attribute__((packed)); - -struct file_internal_info { - __le64 UniqueId; /* inode number */ -} __attribute__((packed)); /* level 0x3ee */ - -struct file_mode_info { - __le32 Mode; -} __attribute__((packed)); /* level 0x3f8 */ - -struct file_attrib_tag { - __le32 Attribute; - __le32 ReparseTag; -} __attribute__((packed)); /* level 0x40b */ - - -/********************************************************/ -/* FindFirst/FindNext transact2 data buffer formats */ -/********************************************************/ - -typedef struct { - __le32 NextEntryOffset; - __u32 ResumeKey; /* as with FileIndex - no need to convert */ - FILE_UNIX_BASIC_INFO basic; - char FileName[1]; -} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - char FileName[1]; -} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - char FileName[1]; -} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* EA size */ - __le32 Reserved; - __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ - char FileName[1]; -} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - __u8 ShortNameLength; - __u8 Reserved; - __u8 ShortName[24]; - char FileName[1]; -} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */ - -typedef struct { - __u32 ResumeKey; - __le16 CreationDate; /* SMB Date */ - __le16 CreationTime; /* SMB Time */ - __le16 LastAccessDate; - __le16 LastAccessTime; - __le16 LastWriteDate; - __le16 LastWriteTime; - __le32 DataSize; /* File Size (EOF) */ - __le32 AllocationSize; - __le16 Attributes; /* verify not u32 */ - __u8 FileNameLength; - char FileName[1]; -} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */ - - -struct win_dev { - unsigned char type[8]; /* IntxCHR or IntxBLK */ - __le64 major; - __le64 minor; -} __attribute__((packed)); - -struct gea { - unsigned char name_len; - char name[1]; -} __attribute__((packed)); - -struct gealist { - unsigned long list_len; - struct gea list[1]; -} __attribute__((packed)); - -struct fea { - unsigned char EA_flags; - __u8 name_len; - __le16 value_len; - char name[1]; - /* optionally followed by value */ -} __attribute__((packed)); -/* flags for _FEA.fEA */ -#define FEA_NEEDEA 0x80 /* need EA bit */ - -struct fealist { - __le32 list_len; - struct fea list[1]; -} __attribute__((packed)); - -/* used to hold an arbitrary blob of data */ -struct data_blob { - __u8 *data; - size_t length; - void (*free) (struct data_blob *data_blob); -} __attribute__((packed)); - - -#ifdef CONFIG_CIFS_POSIX -/* - For better POSIX semantics from Linux client, (even better - than the existing CIFS Unix Extensions) we need updated PDUs for: - - 1) PosixCreateX - to set and return the mode, inode#, device info and - perhaps add a CreateDevice - to create Pipes and other special .inodes - Also note POSIX open flags - 2) Close - to return the last write time to do cache across close - more safely - 3) FindFirst return unique inode number - what about resume key, two - forms short (matches readdir) and full (enough info to cache inodes) - 4) Mkdir - set mode - - And under consideration: - 5) FindClose2 (return nanosecond timestamp ??) - 6) Use nanosecond timestamps throughout all time fields if - corresponding attribute flag is set - 7) sendfile - handle based copy - - what about fixing 64 bit alignment - - There are also various legacy SMB/CIFS requests used as is - - From existing Lanman and NTLM dialects: - -------------------------------------- - NEGOTIATE - SESSION_SETUP_ANDX (BB which?) - TREE_CONNECT_ANDX (BB which wct?) - TREE_DISCONNECT (BB add volume timestamp on response) - LOGOFF_ANDX - DELETE (note delete open file behavior) - DELETE_DIRECTORY - READ_AND_X - WRITE_AND_X - LOCKING_AND_X (note posix lock semantics) - RENAME (note rename across dirs and open file rename posix behaviors) - NT_RENAME (for hardlinks) Is this good enough for all features? - FIND_CLOSE2 - TRANSACTION2 (18 cases) - SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2 - (BB verify that never need to set allocation size) - SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via - Unix ext?) - - COPY (note support for copy across directories) - FUTURE, OPTIONAL - setting/getting OS/2 EAs - FUTURE (BB can this handle - setting Linux xattrs perfectly) - OPTIONAL - dnotify - FUTURE, OPTIONAL - quota - FUTURE, OPTIONAL - - Note that various requests implemented for NT interop such as - NT_TRANSACT (IOCTL) QueryReparseInfo - are unneeded to servers compliant with the CIFS POSIX extensions - - From CIFS Unix Extensions: - ------------------------- - T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks - T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2) - T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK) - T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing - inode fields - Actually a need QUERY_FILE_UNIX_INFO - since has inode num - BB what about a) blksize/blkbits/blocks - b) i_version - c) i_rdev - d) notify mask? - e) generation - f) size_seqcount - T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX - TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended - T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL - */ - -/* xsymlink is a symlink format (used by MacOS) that can be used - to save symlink info in a regular file when - mounted to operating systems that do not - support the cifs Unix extensions or EAs (for xattr - based symlinks). For such a file to be recognized - as containing symlink data: - - 1) file size must be 1067, - 2) signature must begin file data, - 3) length field must be set to ASCII representation - of a number which is less than or equal to 1024, - 4) md5 must match that of the path data */ - -struct xsymlink { - /* 1067 bytes */ - char signature[4]; /* XSym */ /* not null terminated */ - char cr0; /* \n */ -/* ASCII representation of length (4 bytes decimal) terminated by \n not null */ - char length[4]; - char cr1; /* \n */ -/* md5 of valid subset of path ie path[0] through path[length-1] */ - __u8 md5[32]; - char cr2; /* \n */ -/* if room left, then end with \n then 0x20s by convention but not required */ - char path[1024]; -} __attribute__((packed)); - -typedef struct file_xattr_info { - /* BB do we need another field for flags? BB */ - __u32 xattr_name_len; - __u32 xattr_value_len; - char xattr_name[]; - /* followed by xattr_value[xattr_value_len], no pad */ -} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info - level 0x205 */ - -/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */ - -typedef struct file_chattr_info { - __le64 mask; /* list of all possible attribute bits */ - __le64 mode; /* list of actual attribute bits on this inode */ -} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes - (chattr, chflags) level 0x206 */ -#endif /* POSIX */ -#endif /* _CIFSPDU_H */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h deleted file mode 100644 index 98513f5af3f9..000000000000 --- a/fs/cifs/cifsproto.h +++ /dev/null @@ -1,696 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#ifndef _CIFSPROTO_H -#define _CIFSPROTO_H -#include -#include "trace.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif - -struct statfs; -struct smb_rqst; -struct smb3_fs_context; - -/* - ***************************************************************** - * All Prototypes - ***************************************************************** - */ - -extern struct smb_hdr *cifs_buf_get(void); -extern void cifs_buf_release(void *); -extern struct smb_hdr *cifs_small_buf_get(void); -extern void cifs_small_buf_release(void *); -extern void free_rsp_buf(int, void *); -extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *, - unsigned int /* length */); -extern unsigned int _get_xid(void); -extern void _free_xid(unsigned int); -#define get_xid() \ -({ \ - unsigned int __xid = _get_xid(); \ - cifs_dbg(FYI, "VFS: in %s as Xid: %u with uid: %d\n", \ - __func__, __xid, \ - from_kuid(&init_user_ns, current_fsuid())); \ - trace_smb3_enter(__xid, __func__); \ - __xid; \ -}) - -#define free_xid(curr_xid) \ -do { \ - _free_xid(curr_xid); \ - cifs_dbg(FYI, "VFS: leaving %s (xid = %u) rc = %d\n", \ - __func__, curr_xid, (int)rc); \ - if (rc) \ - trace_smb3_exit_err(curr_xid, __func__, (int)rc); \ - else \ - trace_smb3_exit_done(curr_xid, __func__); \ -} while (0) -extern int init_cifs_idmap(void); -extern void exit_cifs_idmap(void); -extern int init_cifs_spnego(void); -extern void exit_cifs_spnego(void); -extern const char *build_path_from_dentry(struct dentry *, void *); -extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry, - void *page, bool prefix); -static inline void *alloc_dentry_path(void) -{ - return __getname(); -} - -static inline void free_dentry_path(void *page) -{ - if (page) - __putname(page); -} - -extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx, - struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, - int add_treename); -extern char *build_wildcard_path_from_dentry(struct dentry *direntry); -extern char *cifs_compose_mount_options(const char *sb_mountdata, - const char *fullpath, const struct dfs_info3_param *ref, - char **devname); -extern void delete_mid(struct mid_q_entry *mid); -extern void release_mid(struct mid_q_entry *mid); -extern void cifs_wake_up_task(struct mid_q_entry *mid); -extern int cifs_handle_standard(struct TCP_Server_Info *server, - struct mid_q_entry *mid); -extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); -extern int smb3_parse_opt(const char *options, const char *key, char **val); -extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); -extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); -extern int cifs_call_async(struct TCP_Server_Info *server, - struct smb_rqst *rqst, - mid_receive_t *receive, mid_callback_t *callback, - mid_handle_t *handle, void *cbdata, const int flags, - const struct cifs_credits *exist_credits); -extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses); -extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, int *resp_buf_type, - const int flags, struct kvec *resp_iov); -extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const int flags, const int num_rqst, - struct smb_rqst *rqst, int *resp_buf_type, - struct kvec *resp_iov); -extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, - struct smb_hdr * /* input */ , - struct smb_hdr * /* out */ , - int * /* bytes returned */ , const int); -extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - char *in_buf, int flags); -extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *, - struct TCP_Server_Info *, - struct smb_rqst *); -extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, - struct smb_rqst *); -extern int cifs_check_receive(struct mid_q_entry *mid, - struct TCP_Server_Info *server, bool log_error); -extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, - unsigned int size, unsigned int *num, - struct cifs_credits *credits); -extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, - struct kvec *, int /* nvec to send */, - int * /* type of buf returned */, const int flags, - struct kvec * /* resp vec */); -extern int SendReceiveBlockingLock(const unsigned int xid, - struct cifs_tcon *ptcon, - struct smb_hdr *in_buf , - struct smb_hdr *out_buf, - int *bytes_returned); -void -cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, - bool all_channels); -void -cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session); -extern int cifs_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session); -extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr); -extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); -extern bool backup_cred(struct cifs_sb_info *); -extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); -extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, - unsigned int bytes_written); -extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int); -extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, - int flags, - struct cifsFileInfo **ret_file); -extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, - int flags, - struct cifsFileInfo **ret_file); -extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); -extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, - struct cifsFileInfo **ret_file); -extern unsigned int smbCalcSize(void *buf); -extern int decode_negTokenInit(unsigned char *security_blob, int length, - struct TCP_Server_Info *server); -extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); -extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port); -extern int map_smb_to_linux_error(char *buf, bool logErr); -extern int map_and_check_smb_error(struct mid_q_entry *mid, bool logErr); -extern void header_assemble(struct smb_hdr *, char /* command */ , - const struct cifs_tcon *, int /* length of - fixed section (word count) in two byte units */); -extern int small_smb_init_no_tc(const int smb_cmd, const int wct, - struct cifs_ses *ses, - void **request_buf); -extern enum securityEnum select_sectype(struct TCP_Server_Info *server, - enum securityEnum requested); -extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); -extern u64 cifs_UnixTimeToNT(struct timespec64); -extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, - int offset); -extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); -extern int cifs_get_writer(struct cifsInodeInfo *cinode); -extern void cifs_put_writer(struct cifsInodeInfo *cinode); -extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode); -extern int cifs_unlock_range(struct cifsFileInfo *cfile, - struct file_lock *flock, const unsigned int xid); -extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); - -extern void cifs_down_write(struct rw_semaphore *sem); -struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, - struct tcon_link *tlink, __u32 oplock, - const char *symlink_target); -extern int cifs_posix_open(const char *full_path, struct inode **inode, - struct super_block *sb, int mode, - unsigned int f_flags, __u32 *oplock, __u16 *netfid, - unsigned int xid); -void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr); -extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, - FILE_UNIX_BASIC_INFO *info, - struct cifs_sb_info *cifs_sb); -extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *, - struct cifs_sb_info *); -extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr); -extern struct inode *cifs_iget(struct super_block *sb, - struct cifs_fattr *fattr); - -int cifs_get_inode_info(struct inode **inode, const char *full_path, - struct cifs_open_info_data *data, struct super_block *sb, int xid, - const struct cifs_fid *fid); -extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, - struct super_block *sb, unsigned int xid); -extern int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *search_path, - struct super_block *sb, unsigned int xid); -extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs, - unsigned int xid, const char *full_path, __u32 dosattr); -extern int cifs_rename_pending_delete(const char *full_path, - struct dentry *dentry, - const unsigned int xid); -extern int sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, - struct cifs_fattr *fattr, uint sidtype); -extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, - struct cifs_fattr *fattr, struct inode *inode, - bool get_mode_from_special_sid, - const char *path, const struct cifs_fid *pfid); -extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, - kuid_t uid, kgid_t gid); -extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *, - const char *, u32 *, u32); -extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *, - const struct cifs_fid *, u32 *, u32); -extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, - const char *, int); -extern unsigned int setup_authusers_ACE(struct cifs_ace *pace); -extern unsigned int setup_special_mode_ACE(struct cifs_ace *pace, __u64 nmode); -extern unsigned int setup_special_user_owner_ACE(struct cifs_ace *pace); - -extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); -extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, - unsigned int to_read); -extern ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server, - size_t to_read); -extern int cifs_read_page_from_socket(struct TCP_Server_Info *server, - struct page *page, - unsigned int page_offset, - unsigned int to_read); -extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb); -extern int cifs_match_super(struct super_block *, void *); -extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx); -extern void cifs_umount(struct cifs_sb_info *); -extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); -extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); - -extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u8 type, __u16 flags, - struct cifsLockInfo **conf_lock, - int rw_check); -extern void cifs_add_pending_open(struct cifs_fid *fid, - struct tcon_link *tlink, - struct cifs_pending_open *open); -extern void cifs_add_pending_open_locked(struct cifs_fid *fid, - struct tcon_link *tlink, - struct cifs_pending_open *open); -extern void cifs_del_pending_open(struct cifs_pending_open *open); - -extern bool cifs_is_deferred_close(struct cifsFileInfo *cfile, - struct cifs_deferred_close **dclose); - -extern void cifs_add_deferred_close(struct cifsFileInfo *cfile, - struct cifs_deferred_close *dclose); - -extern void cifs_del_deferred_close(struct cifsFileInfo *cfile); - -extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); - -extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); - -extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, - const char *path); -extern struct TCP_Server_Info * -cifs_get_tcp_session(struct smb3_fs_context *ctx, - struct TCP_Server_Info *primary_server); -extern void cifs_put_tcp_session(struct TCP_Server_Info *server, - int from_reconnect); -extern void cifs_put_tcon(struct cifs_tcon *tcon); - -#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) -extern void cifs_dfs_release_automount_timer(void); -#else /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ -#define cifs_dfs_release_automount_timer() do { } while (0) -#endif /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ - -void cifs_proc_init(void); -void cifs_proc_clean(void); - -extern void cifs_move_llist(struct list_head *source, struct list_head *dest); -extern void cifs_free_llist(struct list_head *llist); -extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); - -extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, - const struct nls_table *nlsc); - -extern int cifs_negotiate_protocol(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct nls_table *nls_info); -extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); -extern int CIFSSMBNegotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); - -extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *); - -extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, - const char *searchName, struct cifs_sb_info *cifs_sb, - __u16 *searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf, - bool msearch); - -extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, - __u16 searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf); - -extern int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 search_handle); - -extern int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_ALL_INFO *pFindData); -extern int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_Name, FILE_ALL_INFO *data, - int legacy /* whether to use old info level */, - const struct nls_table *nls_codepage, int remap); -extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - const struct nls_table *nls_codepage, int remap); - -extern int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); -extern int CIFSSMBUnixQPathInfo(const unsigned int xid, - struct cifs_tcon *tcon, - const unsigned char *searchName, - FILE_UNIX_BASIC_INFO *pFindData, - const struct nls_table *nls_codepage, int remap); - -extern int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, - const char *search_name, - struct dfs_info3_param **target_nodes, - unsigned int *num_of_nodes, - const struct nls_table *nls_codepage, int remap); - -extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, - unsigned int *num_of_nodes, - struct dfs_info3_param **target_nodes, - const struct nls_table *nls_codepage, int remap, - const char *searchName, bool is_unicode); -extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - struct smb3_fs_context *ctx); -extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); -extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); -extern int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, - __u64 cap); - -extern int CIFSSMBQFSAttributeInfo(const unsigned int xid, - struct cifs_tcon *tcon); -extern int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); - -extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - const FILE_BASIC_INFO *data, __u16 fid, - __u32 pid_of_opener); -extern int CIFSSMBSetFileDisposition(const unsigned int xid, - struct cifs_tcon *tcon, - bool delete_file, __u16 fid, - __u32 pid_of_opener); -extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, - const char *file_name, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_allocation); -extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, __u64 size, - bool set_allocation); - -struct cifs_unix_set_info_args { - __u64 ctime; - __u64 atime; - __u64 mtime; - __u64 mode; - kuid_t uid; - kgid_t gid; - dev_t device; -}; - -extern int CIFSSMBUnixSetFileInfo(const unsigned int xid, - struct cifs_tcon *tcon, - const struct cifs_unix_set_info_args *args, - u16 fid, u32 pid_of_opener); - -extern int CIFSSMBUnixSetPathInfo(const unsigned int xid, - struct cifs_tcon *tcon, const char *file_name, - const struct cifs_unix_set_info_args *args, - const struct nls_table *nls_codepage, - int remap); - -extern int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, __u16 type, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon, - int netfid, const char *target_name, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int CIFSUnixCreateHardLink(const unsigned int xid, - struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSUnixCreateSymLink(const unsigned int xid, - struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, - struct cifs_tcon *tcon, - const unsigned char *searchName, char **syminfo, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, char **symlinkinfo, - const struct nls_table *nls_codepage); -extern int CIFSSMB_set_compression(const unsigned int xid, - struct cifs_tcon *tcon, __u16 fid); -extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, - int *oplock, FILE_ALL_INFO *buf); -extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const int disposition, - const int access_flags, const int omode, - __u16 *netfid, int *pOplock, FILE_ALL_INFO *, - const struct nls_table *nls_codepage, int remap); -extern int CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, - u32 posix_flags, __u64 mode, __u16 *netfid, - FILE_UNIX_BASIC_INFO *pRetData, - __u32 *pOplock, const char *name, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, - const int smb_file_id); - -extern int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, - const int smb_file_id); - -extern int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, - int *return_buf_type); -extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, const char *buf); -extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, const int nvec); -extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, __u64 *inode_number, - const struct nls_table *nls_codepage, - int remap); - -extern int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u8 lock_type, - const __u32 num_unlock, const __u32 num_lock, - LOCKING_ANDX_RANGE *buf); -extern int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u32 netpid, const __u64 len, - const __u64 offset, const __u32 numUnlock, - const __u32 numLock, const __u8 lockType, - const bool waitFlag, const __u8 oplock_level); -extern int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 smb_file_id, const __u32 netpid, - const loff_t start_offset, const __u64 len, - struct file_lock *, const __u16 lock_type, - const bool waitFlag); -extern int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBEcho(struct TCP_Server_Info *server); -extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); - -extern struct cifs_ses *sesInfoAlloc(void); -extern void sesInfoFree(struct cifs_ses *); -extern struct cifs_tcon *tconInfoAlloc(void); -extern void tconInfoFree(struct cifs_tcon *); - -extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number); -extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *, - __u32 *); -extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); -extern int cifs_verify_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - __u32 expected_sequence_number); -extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); -extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); -extern int calc_seckey(struct cifs_ses *); -extern int generate_smb30signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int generate_smb311signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server); - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern int CIFSSMBCopy(unsigned int xid, - struct cifs_tcon *source_tcon, - const char *fromName, - const __u16 target_tid, - const char *toName, const int flags, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - const unsigned char *ea_name, char *EAData, - size_t bufsize, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const char *ea_name, - const void *ea_value, const __u16 ea_value_len, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); -extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16, - struct cifs_ntsd *, __u32, int); -extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - char *acl_inf, const int buflen, const int acl_type, - const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *fileName, - const char *local_acl, const int buflen, const int acl_type, - const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, - const int netfid, __u64 *pExtAttrBits, __u64 *pMask); -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ -extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); -extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr); -extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - struct cifs_fattr *fattr, - const unsigned char *path); -extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, - const struct nls_table *codepage); - -extern int -cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname); - -extern struct TCP_Server_Info * -cifs_find_tcp_session(struct smb3_fs_context *ctx); - -extern void cifs_put_smb_ses(struct cifs_ses *ses); - -extern struct cifs_ses * -cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx); - -void cifs_readdata_release(struct kref *refcount); -int cifs_async_readv(struct cifs_readdata *rdata); -int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); - -int cifs_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)); -void cifs_writev_complete(struct work_struct *work); -struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, - work_func_t complete); -struct cifs_writedata *cifs_writedata_direct_alloc(struct page **pages, - work_func_t complete); -void cifs_writedata_release(struct kref *refcount); -int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const unsigned char *path, char *pbuf, - unsigned int *pbytes_read); -int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const unsigned char *path, char *pbuf, - unsigned int *pbytes_written); -int __cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature, - struct shash_desc *shash); -enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, - enum securityEnum); -struct cifs_aio_ctx *cifs_aio_ctx_alloc(void); -void cifs_aio_ctx_release(struct kref *refcount); -int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw); - -int cifs_alloc_hash(const char *name, struct shash_desc **sdesc); -void cifs_free_hash(struct shash_desc **sdesc); - -void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, - unsigned int *len, unsigned int *offset); -struct cifs_chan * -cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server); -int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses); -bool is_server_using_iface(struct TCP_Server_Info *server, - struct cifs_server_iface *iface); -bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); -void cifs_ses_mark_for_reconnect(struct cifs_ses *ses); - -unsigned int -cifs_ses_get_chan_index(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_set_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_clear_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -bool -cifs_chan_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_set_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_clear_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -bool -cifs_chan_needs_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -bool -cifs_chan_is_iface_active(struct cifs_ses *ses, - struct TCP_Server_Info *server); -int -cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server); -int -SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount); - -void extract_unc_hostname(const char *unc, const char **h, size_t *len); -int copy_path_name(char *dst, const char *src); -int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, - int resp_buftype, - struct cifs_search_info *srch_inf); - -struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); -void cifs_put_tcp_super(struct super_block *sb); -int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); -char *extract_hostname(const char *unc); -char *extract_sharename(const char *unc); - -#ifdef CONFIG_CIFS_DFS_UPCALL -static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, - const char *old_path, - const struct nls_table *nls_codepage, - struct dfs_info3_param *referral, int remap) -{ - return dfs_cache_find(xid, ses, nls_codepage, remap, old_path, - referral, NULL); -} - -int match_target_ip(struct TCP_Server_Info *server, - const char *share, size_t share_len, - bool *result); -int cifs_inval_name_dfs_link_error(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - bool *islink); -#else -static inline int cifs_inval_name_dfs_link_error(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - bool *islink) -{ - *islink = false; - return 0; -} -#endif - -static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) -{ - if (cifs_sb && (backup_cred(cifs_sb))) - return options | CREATE_OPEN_BACKUP_INTENT; - else - return options; -} - -struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); -void cifs_put_tcon_super(struct super_block *sb); -int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); - -#endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifsroot.c b/fs/cifs/cifsroot.c deleted file mode 100644 index 56ec1b233f52..000000000000 --- a/fs/cifs/cifsroot.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * SMB root file system support - * - * Copyright (c) 2019 Paulo Alcantara - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_MNT_OPTS \ - "vers=1.0,cifsacl,mfsymlinks,rsize=1048576,wsize=65536,uid=0,gid=0," \ - "hard,rootfs" - -static char root_dev[2048] __initdata = ""; -static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS; - -static __be32 __init parse_srvaddr(char *start, char *end) -{ - /* TODO: ipv6 support */ - char addr[sizeof("aaa.bbb.ccc.ddd")]; - int i = 0; - - while (start < end && i < sizeof(addr) - 1) { - if (isdigit(*start) || *start == '.') - addr[i++] = *start; - start++; - } - addr[i] = '\0'; - return in_aton(addr); -} - -/* cifsroot=///[,options] */ -static int __init cifs_root_setup(char *line) -{ - char *s; - int len; - __be32 srvaddr = htonl(INADDR_NONE); - - ROOT_DEV = Root_CIFS; - - if (strlen(line) > 3 && line[0] == '/' && line[1] == '/') { - s = strchr(&line[2], '/'); - if (!s || s[1] == '\0') - return 1; - - /* make s point to ',' or '\0' at end of line */ - s = strchrnul(s, ','); - /* len is strlen(unc) + '\0' */ - len = s - line + 1; - if (len > sizeof(root_dev)) { - pr_err("Root-CIFS: UNC path too long\n"); - return 1; - } - strscpy(root_dev, line, len); - srvaddr = parse_srvaddr(&line[2], s); - if (*s) { - int n = snprintf(root_opts, - sizeof(root_opts), "%s,%s", - DEFAULT_MNT_OPTS, s + 1); - if (n >= sizeof(root_opts)) { - pr_err("Root-CIFS: mount options string too long\n"); - root_opts[sizeof(root_opts)-1] = '\0'; - return 1; - } - } - } - - root_server_addr = srvaddr; - - return 1; -} - -__setup("cifsroot=", cifs_root_setup); - -int __init cifs_root_data(char **dev, char **opts) -{ - if (!root_dev[0] || root_server_addr == htonl(INADDR_NONE)) { - pr_err("Root-CIFS: no SMB server address\n"); - return -1; - } - - *dev = root_dev; - *opts = root_opts; - - return 0; -} diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c deleted file mode 100644 index c90d4ec9292c..000000000000 --- a/fs/cifs/cifssmb.c +++ /dev/null @@ -1,5873 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2010 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Contains the routines for constructing the SMB PDUs themselves - * - */ - - /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ - /* These are mostly routines that operate on a pathname, or on a tree id */ - /* (mounted volume), but there are eight handle based routines which must be */ - /* treated slightly differently for reconnection purposes since we never */ - /* want to reuse a stale file handle and only the caller knows the file info */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsacl.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "fscache.h" -#include "smbdirect.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif - -#ifdef CONFIG_CIFS_POSIX -static struct { - int index; - char *name; -} protocols[] = { - {CIFS_PROT, "\2NT LM 0.12"}, - {POSIX_PROT, "\2POSIX 2"}, - {BAD_PROT, "\2"} -}; -#else -static struct { - int index; - char *name; -} protocols[] = { - {CIFS_PROT, "\2NT LM 0.12"}, - {BAD_PROT, "\2"} -}; -#endif - -/* define the number of elements in the cifs dialect array */ -#ifdef CONFIG_CIFS_POSIX -#define CIFS_NUM_PROT 2 -#else /* not posix */ -#define CIFS_NUM_PROT 1 -#endif /* CIFS_POSIX */ - - -/* reconnect the socket, tcon, and smb session if needed */ -static int -cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) -{ - int rc; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct nls_table *nls_codepage; - - /* - * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for - * tcp and smb session status done differently for those three - in the - * calling routine - */ - if (!tcon) - return 0; - - ses = tcon->ses; - server = ses->server; - - /* - * only tree disconnect, open, and write, (and ulogoff which does not - * have tcon) are allowed as we start umount - */ - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_EXITING) { - if (smb_command != SMB_COM_TREE_DISCONNECT) { - spin_unlock(&tcon->tc_lock); - cifs_dbg(FYI, "can not send cmd %d while umounting\n", - smb_command); - return -ENODEV; - } - } - spin_unlock(&tcon->tc_lock); - - rc = cifs_wait_for_server_reconnect(server, tcon->retry); - if (rc) - return rc; - - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { - spin_unlock(&ses->chan_lock); - return 0; - } - spin_unlock(&ses->chan_lock); - - nls_codepage = load_nls_default(); - - /* - * Recheck after acquire mutex. If another thread is negotiating - * and the server never sends an answer the socket will be closed - * and tcpStatus set to reconnect. - */ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - rc = -EHOSTDOWN; - goto out; - } - spin_unlock(&server->srv_lock); - - /* - * need to prevent multiple threads trying to simultaneously - * reconnect the same SMB session - */ - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server)) { - spin_unlock(&ses->chan_lock); - - /* this means that we only need to tree connect */ - if (tcon->need_reconnect) - goto skip_sess_setup; - - rc = -EHOSTDOWN; - goto out; - } - spin_unlock(&ses->chan_lock); - - mutex_lock(&ses->session_mutex); - rc = cifs_negotiate_protocol(0, ses, server); - if (!rc) - rc = cifs_setup_session(0, ses, server, nls_codepage); - - /* do we need to reconnect tcon? */ - if (rc || !tcon->need_reconnect) { - mutex_unlock(&ses->session_mutex); - goto out; - } - -skip_sess_setup: - cifs_mark_open_files_invalid(tcon); - rc = cifs_tree_connect(0, tcon, nls_codepage); - mutex_unlock(&ses->session_mutex); - cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); - - if (rc) { - pr_warn_once("reconnect tcon failed rc = %d\n", rc); - goto out; - } - - atomic_inc(&tconInfoReconnectCount); - - /* tell server Unix caps we support */ - if (cap_unix(ses)) - reset_cifs_unix_caps(0, tcon, NULL, NULL); - - /* - * Removed call to reopen open files here. It is safer (and faster) to - * reopen files one at a time as needed in read and write. - * - * FIXME: what about file locks? don't we need to reclaim them ASAP? - */ - -out: - /* - * Check if handle based operation so we know whether we can continue - * or not without returning to caller to reset file handle - */ - switch (smb_command) { - case SMB_COM_READ_ANDX: - case SMB_COM_WRITE_ANDX: - case SMB_COM_CLOSE: - case SMB_COM_FIND_CLOSE2: - case SMB_COM_LOCKING_ANDX: - rc = -EAGAIN; - } - - unload_nls(nls_codepage); - return rc; -} - -/* Allocate and return pointer to an SMB request buffer, and set basic - SMB information in the SMB header. If the return code is zero, this - function must have filled in request_buf pointer */ -static int -small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf) -{ - int rc; - - rc = cifs_reconnect_tcon(tcon, smb_command); - if (rc) - return rc; - - *request_buf = cifs_small_buf_get(); - if (*request_buf == NULL) { - /* BB should we add a retry in here if not a writepage? */ - return -ENOMEM; - } - - header_assemble((struct smb_hdr *) *request_buf, smb_command, - tcon, wct); - - if (tcon != NULL) - cifs_stats_inc(&tcon->num_smbs_sent); - - return 0; -} - -int -small_smb_init_no_tc(const int smb_command, const int wct, - struct cifs_ses *ses, void **request_buf) -{ - int rc; - struct smb_hdr *buffer; - - rc = small_smb_init(smb_command, wct, NULL, request_buf); - if (rc) - return rc; - - buffer = (struct smb_hdr *)*request_buf; - buffer->Mid = get_next_mid(ses->server); - if (ses->capabilities & CAP_UNICODE) - buffer->Flags2 |= SMBFLG2_UNICODE; - if (ses->capabilities & CAP_STATUS32) - buffer->Flags2 |= SMBFLG2_ERR_STATUS; - - /* uid, tid can stay at zero as set in header assemble */ - - /* BB add support for turning on the signing when - this function is used after 1st of session setup requests */ - - return rc; -} - -/* If the return code is zero, this function must fill in request_buf pointer */ -static int -__smb_init(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf, void **response_buf) -{ - *request_buf = cifs_buf_get(); - if (*request_buf == NULL) { - /* BB should we add a retry in here if not a writepage? */ - return -ENOMEM; - } - /* Although the original thought was we needed the response buf for */ - /* potential retries of smb operations it turns out we can determine */ - /* from the mid flags when the request buffer can be resent without */ - /* having to use a second distinct buffer for the response */ - if (response_buf) - *response_buf = *request_buf; - - header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, - wct); - - if (tcon != NULL) - cifs_stats_inc(&tcon->num_smbs_sent); - - return 0; -} - -/* If the return code is zero, this function must fill in request_buf pointer */ -static int -smb_init(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf, void **response_buf) -{ - int rc; - - rc = cifs_reconnect_tcon(tcon, smb_command); - if (rc) - return rc; - - return __smb_init(smb_command, wct, tcon, request_buf, response_buf); -} - -static int -smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf, void **response_buf) -{ - spin_lock(&tcon->ses->chan_lock); - if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) || - tcon->need_reconnect) { - spin_unlock(&tcon->ses->chan_lock); - return -EHOSTDOWN; - } - spin_unlock(&tcon->ses->chan_lock); - - return __smb_init(smb_command, wct, tcon, request_buf, response_buf); -} - -static int validate_t2(struct smb_t2_rsp *pSMB) -{ - unsigned int total_size; - - /* check for plausible wct */ - if (pSMB->hdr.WordCount < 10) - goto vt2_err; - - /* check for parm and data offset going beyond end of smb */ - if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 || - get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024) - goto vt2_err; - - total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount); - if (total_size >= 512) - goto vt2_err; - - /* check that bcc is at least as big as parms + data, and that it is - * less than negotiated smb buffer - */ - total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount); - if (total_size > get_bcc(&pSMB->hdr) || - total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) - goto vt2_err; - - return 0; -vt2_err: - cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, - sizeof(struct smb_t2_rsp) + 16); - return -EINVAL; -} - -static int -decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) -{ - int rc = 0; - u16 count; - char *guid = pSMBr->u.extended_response.GUID; - struct TCP_Server_Info *server = ses->server; - - count = get_bcc(&pSMBr->hdr); - if (count < SMB1_CLIENT_GUID_SIZE) - return -EIO; - - spin_lock(&cifs_tcp_ses_lock); - if (server->srv_count > 1) { - spin_unlock(&cifs_tcp_ses_lock); - if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) { - cifs_dbg(FYI, "server UID changed\n"); - memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); - } - } else { - spin_unlock(&cifs_tcp_ses_lock); - memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); - } - - if (count == SMB1_CLIENT_GUID_SIZE) { - server->sec_ntlmssp = true; - } else { - count -= SMB1_CLIENT_GUID_SIZE; - rc = decode_negTokenInit( - pSMBr->u.extended_response.SecurityBlob, count, server); - if (rc != 1) - return -EINVAL; - } - - return 0; -} - -static bool -should_set_ext_sec_flag(enum securityEnum sectype) -{ - switch (sectype) { - case RawNTLMSSP: - case Kerberos: - return true; - case Unspecified: - if (global_secflags & - (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)) - return true; - fallthrough; - default: - return false; - } -} - -int -CIFSSMBNegotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - NEGOTIATE_REQ *pSMB; - NEGOTIATE_RSP *pSMBr; - int rc = 0; - int bytes_returned; - int i; - u16 count; - - if (!server) { - WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; - } - - rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , - (void **) &pSMB, (void **) &pSMBr); - if (rc) - return rc; - - pSMB->hdr.Mid = get_next_mid(server); - pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); - - if (should_set_ext_sec_flag(ses->sectype)) { - cifs_dbg(FYI, "Requesting extended security\n"); - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - } - - count = 0; - /* - * We know that all the name entries in the protocols array - * are short (< 16 bytes anyway) and are NUL terminated. - */ - for (i = 0; i < CIFS_NUM_PROT; i++) { - size_t len = strlen(protocols[i].name) + 1; - - memcpy(&pSMB->DialectsArray[count], protocols[i].name, len); - count += len; - } - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc != 0) - goto neg_err_exit; - - server->dialect = le16_to_cpu(pSMBr->DialectIndex); - cifs_dbg(FYI, "Dialect: %d\n", server->dialect); - /* Check wct = 1 error case */ - if ((pSMBr->hdr.WordCount <= 13) || (server->dialect == BAD_PROT)) { - /* core returns wct = 1, but we do not ask for core - otherwise - small wct just comes when dialect index is -1 indicating we - could not negotiate a common dialect */ - rc = -EOPNOTSUPP; - goto neg_err_exit; - } else if (pSMBr->hdr.WordCount != 17) { - /* unknown wct */ - rc = -EOPNOTSUPP; - goto neg_err_exit; - } - /* else wct == 17, NTLM or better */ - - server->sec_mode = pSMBr->SecurityMode; - if ((server->sec_mode & SECMODE_USER) == 0) - cifs_dbg(FYI, "share mode security\n"); - - /* one byte, so no need to convert this or EncryptionKeyLen from - little endian */ - server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), - cifs_max_pending); - set_credits(server, server->maxReq); - /* probably no need to store and check maxvcs */ - server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); - /* set up max_read for readahead check */ - server->max_read = server->maxBuf; - server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); - cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf); - server->capabilities = le32_to_cpu(pSMBr->Capabilities); - server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); - server->timeAdj *= 60; - - if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { - server->negflavor = CIFS_NEGFLAVOR_UNENCAP; - memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, - CIFS_CRYPTO_KEY_SIZE); - } else if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || - server->capabilities & CAP_EXTENDED_SECURITY) { - server->negflavor = CIFS_NEGFLAVOR_EXTENDED; - rc = decode_ext_sec_blob(ses, pSMBr); - } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { - rc = -EIO; /* no crypt key only if plain text pwd */ - } else { - server->negflavor = CIFS_NEGFLAVOR_UNENCAP; - server->capabilities &= ~CAP_EXTENDED_SECURITY; - } - - if (!rc) - rc = cifs_enable_signing(server, ses->sign); -neg_err_exit: - cifs_buf_release(pSMB); - - cifs_dbg(FYI, "negprot rc %d\n", rc); - return rc; -} - -int -CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) -{ - struct smb_hdr *smb_buffer; - int rc = 0; - - cifs_dbg(FYI, "In tree disconnect\n"); - - /* BB: do we need to check this? These should never be NULL. */ - if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) - return -EIO; - - /* - * No need to return error on this operation if tid invalidated and - * closed on server already e.g. due to tcp session crashing. Also, - * the tcon is no longer on the list, so no need to take lock before - * checking this. - */ - spin_lock(&tcon->ses->chan_lock); - if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) { - spin_unlock(&tcon->ses->chan_lock); - return -EIO; - } - spin_unlock(&tcon->ses->chan_lock); - - rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, - (void **)&smb_buffer); - if (rc) - return rc; - - rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0); - cifs_small_buf_release(smb_buffer); - if (rc) - cifs_dbg(FYI, "Tree disconnect failed %d\n", rc); - - /* No need to return error on this operation if tid invalidated and - closed on server already e.g. due to tcp session crashing */ - if (rc == -EAGAIN) - rc = 0; - - return rc; -} - -/* - * This is a no-op for now. We're not really interested in the reply, but - * rather in the fact that the server sent one and that server->lstrp - * gets updated. - * - * FIXME: maybe we should consider checking that the reply matches request? - */ -static void -cifs_echo_callback(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->callback_data; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - - release_mid(mid); - add_credits(server, &credits, CIFS_ECHO_OP); -} - -int -CIFSSMBEcho(struct TCP_Server_Info *server) -{ - ECHO_REQ *smb; - int rc = 0; - struct kvec iov[2]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 2 }; - - cifs_dbg(FYI, "In echo request\n"); - - rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb); - if (rc) - return rc; - - if (server->capabilities & CAP_UNICODE) - smb->hdr.Flags2 |= SMBFLG2_UNICODE; - - /* set up echo request */ - smb->hdr.Tid = 0xffff; - smb->hdr.WordCount = 1; - put_unaligned_le16(1, &smb->EchoCount); - put_bcc(1, &smb->hdr); - smb->Data[0] = 'a'; - inc_rfc1001_len(smb, 3); - - iov[0].iov_len = 4; - iov[0].iov_base = smb; - iov[1].iov_len = get_rfc1002_length(smb); - iov[1].iov_base = (char *)smb + 4; - - rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL, - server, CIFS_NON_BLOCKING | CIFS_ECHO_OP, NULL); - if (rc) - cifs_dbg(FYI, "Echo request failed: %d\n", rc); - - cifs_small_buf_release(smb); - - return rc; -} - -int -CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) -{ - LOGOFF_ANDX_REQ *pSMB; - int rc = 0; - - cifs_dbg(FYI, "In SMBLogoff for session disconnect\n"); - - /* - * BB: do we need to check validity of ses and server? They should - * always be valid since we have an active reference. If not, that - * should probably be a BUG() - */ - if (!ses || !ses->server) - return -EIO; - - mutex_lock(&ses->session_mutex); - spin_lock(&ses->chan_lock); - if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { - spin_unlock(&ses->chan_lock); - goto session_already_dead; /* no need to send SMBlogoff if uid - already closed due to reconnect */ - } - spin_unlock(&ses->chan_lock); - - rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); - if (rc) { - mutex_unlock(&ses->session_mutex); - return rc; - } - - pSMB->hdr.Mid = get_next_mid(ses->server); - - if (ses->server->sign) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - pSMB->hdr.Uid = ses->Suid; - - pSMB->AndXCommand = 0xFF; - rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); -session_already_dead: - mutex_unlock(&ses->session_mutex); - - /* if session dead then we do not need to do ulogoff, - since server closed smb session, no sense reporting - error */ - if (rc == -EAGAIN) - rc = 0; - return rc; -} - -int -CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, __u16 type, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - struct unlink_psx_rq *pRqD; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count; - - cifs_dbg(FYI, "In POSIX delete\n"); -PsxDelete: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - - params = 6 + name_len; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = 0; /* BB double check this with jra */ - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - - /* Setup pointer to Request Data (inode type). - * Note that SMB offsets are from the beginning of SMB which is 4 bytes - * in, after RFC1001 field - */ - pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset + 4); - pRqD->type = cpu_to_le16(type); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq); - - pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); - pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "Posix delete returned %d\n", rc); - cifs_buf_release(pSMB); - - cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes); - - if (rc == -EAGAIN) - goto PsxDelete; - - return rc; -} - -int -CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - DELETE_FILE_REQ *pSMB = NULL; - DELETE_FILE_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - int remap = cifs_remap(cifs_sb); - -DelFileRetry: - rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name, - PATH_MAX, cifs_sb->local_nls, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->fileName, name); - } - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM); - pSMB->BufferFormat = 0x04; - inc_rfc1001_len(pSMB, name_len + 1); - pSMB->ByteCount = cpu_to_le16(name_len + 1); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes); - if (rc) - cifs_dbg(FYI, "Error in RMFile = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto DelFileRetry; - - return rc; -} - -int -CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - DELETE_DIRECTORY_REQ *pSMB = NULL; - DELETE_DIRECTORY_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSSMBRmDir\n"); -RmDirRetry: - rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, - PATH_MAX, cifs_sb->local_nls, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->DirName, name); - } - - pSMB->BufferFormat = 0x04; - inc_rfc1001_len(pSMB, name_len + 1); - pSMB->ByteCount = cpu_to_le16(name_len + 1); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_rmdirs); - if (rc) - cifs_dbg(FYI, "Error in RMDir = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto RmDirRetry; - return rc; -} - -int -CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode, - struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - int rc = 0; - CREATE_DIRECTORY_REQ *pSMB = NULL; - CREATE_DIRECTORY_RSP *pSMBr = NULL; - int bytes_returned; - int name_len; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSSMBMkDir\n"); -MkDirRetry: - rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, - PATH_MAX, cifs_sb->local_nls, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->DirName, name); - } - - pSMB->BufferFormat = 0x04; - inc_rfc1001_len(pSMB, name_len + 1); - pSMB->ByteCount = cpu_to_le16(name_len + 1); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_mkdirs); - if (rc) - cifs_dbg(FYI, "Error in Mkdir = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto MkDirRetry; - return rc; -} - -int -CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, - __u32 posix_flags, __u64 mode, __u16 *netfid, - FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, - const char *name, const struct nls_table *nls_codepage, - int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count, count; - OPEN_PSX_REQ *pdata; - OPEN_PSX_RSP *psx_rsp; - - cifs_dbg(FYI, "In POSIX Create\n"); -PsxCreat: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, name, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, name); - } - - params = 6 + name_len; - count = sizeof(OPEN_PSX_REQ); - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */ - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset + 4); - pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); - pdata->Permissions = cpu_to_le64(mode); - pdata->PosixOpenFlags = cpu_to_le32(posix_flags); - pdata->OpenFlags = cpu_to_le32(*pOplock); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Posix create returned %d\n", rc); - goto psx_create_err; - } - - cifs_dbg(FYI, "copying inode info\n"); - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) { - rc = -EIO; /* bad smb */ - goto psx_create_err; - } - - /* copy return information to pRetData */ - psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol - + le16_to_cpu(pSMBr->t2.DataOffset)); - - *pOplock = le16_to_cpu(psx_rsp->OplockFlags); - if (netfid) - *netfid = psx_rsp->Fid; /* cifs fid stays in le */ - /* Let caller know file was created so we can set the mode. */ - /* Do we care about the CreateAction in any other cases? */ - if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) - *pOplock |= CIFS_CREATE_ACTION; - /* check to make sure response data is there */ - if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) { - pRetData->Type = cpu_to_le32(-1); /* unknown */ - cifs_dbg(NOISY, "unknown type\n"); - } else { - if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP) - + sizeof(FILE_UNIX_BASIC_INFO)) { - cifs_dbg(VFS, "Open response data too small\n"); - pRetData->Type = cpu_to_le32(-1); - goto psx_create_err; - } - memcpy((char *) pRetData, - (char *)psx_rsp + sizeof(OPEN_PSX_RSP), - sizeof(FILE_UNIX_BASIC_INFO)); - } - -psx_create_err: - cifs_buf_release(pSMB); - - if (posix_flags & SMB_O_DIRECTORY) - cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs); - else - cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens); - - if (rc == -EAGAIN) - goto PsxCreat; - - return rc; -} - -static __u16 convert_disposition(int disposition) -{ - __u16 ofun = 0; - - switch (disposition) { - case FILE_SUPERSEDE: - ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; - break; - case FILE_OPEN: - ofun = SMBOPEN_OAPPEND; - break; - case FILE_CREATE: - ofun = SMBOPEN_OCREATE; - break; - case FILE_OPEN_IF: - ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND; - break; - case FILE_OVERWRITE: - ofun = SMBOPEN_OTRUNC; - break; - case FILE_OVERWRITE_IF: - ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; - break; - default: - cifs_dbg(FYI, "unknown disposition %d\n", disposition); - ofun = SMBOPEN_OAPPEND; /* regular open */ - } - return ofun; -} - -static int -access_flags_to_smbopen_mode(const int access_flags) -{ - int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE); - - if (masked_flags == GENERIC_READ) - return SMBOPEN_READ; - else if (masked_flags == GENERIC_WRITE) - return SMBOPEN_WRITE; - - /* just go for read/write */ - return SMBOPEN_READWRITE; -} - -int -SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const int openDisposition, - const int access_flags, const int create_options, __u16 *netfid, - int *pOplock, FILE_ALL_INFO *pfile_info, - const struct nls_table *nls_codepage, int remap) -{ - int rc; - OPENX_REQ *pSMB = NULL; - OPENX_RSP *pSMBr = NULL; - int bytes_returned; - int name_len; - __u16 count; - -OldOpenRetry: - rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->AndXCommand = 0xFF; /* none */ - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - count = 1; /* account for one byte pad to word boundary */ - name_len = - cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), - fileName, PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - count = 0; /* no pad */ - name_len = copy_path_name(pSMB->fileName, fileName); - } - if (*pOplock & REQ_OPLOCK) - pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK); - else if (*pOplock & REQ_BATCHOPLOCK) - pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK); - - pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO); - pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags)); - pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ - /* set file as system file if special file such - as fifo and server expecting SFU style and - no Unix extensions */ - - if (create_options & CREATE_OPTION_SPECIAL) - pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); - else /* BB FIXME BB */ - pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); - - if (create_options & CREATE_OPTION_READONLY) - pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY); - - /* BB FIXME BB */ -/* pSMB->CreateOptions = cpu_to_le32(create_options & - CREATE_OPTIONS_MASK); */ - /* BB FIXME END BB */ - - pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); - pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition)); - count += name_len; - inc_rfc1001_len(pSMB, count); - - pSMB->ByteCount = cpu_to_le16(count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *)pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); - if (rc) { - cifs_dbg(FYI, "Error in Open = %d\n", rc); - } else { - /* BB verify if wct == 15 */ - -/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/ - - *netfid = pSMBr->Fid; /* cifs fid stays in le */ - /* Let caller know file was created so we can set the mode. */ - /* Do we care about the CreateAction in any other cases? */ - /* BB FIXME BB */ -/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) - *pOplock |= CIFS_CREATE_ACTION; */ - /* BB FIXME END */ - - if (pfile_info) { - pfile_info->CreationTime = 0; /* BB convert CreateTime*/ - pfile_info->LastAccessTime = 0; /* BB fixme */ - pfile_info->LastWriteTime = 0; /* BB fixme */ - pfile_info->ChangeTime = 0; /* BB fixme */ - pfile_info->Attributes = - cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); - /* the file_info buf is endian converted by caller */ - pfile_info->AllocationSize = - cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile)); - pfile_info->EndOfFile = pfile_info->AllocationSize; - pfile_info->NumberOfLinks = cpu_to_le32(1); - pfile_info->DeletePending = 0; - } - } - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto OldOpenRetry; - return rc; -} - -int -CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, - FILE_ALL_INFO *buf) -{ - int rc; - OPEN_REQ *req = NULL; - OPEN_RSP *rsp = NULL; - int bytes_returned; - int name_len; - __u16 count; - struct cifs_sb_info *cifs_sb = oparms->cifs_sb; - struct cifs_tcon *tcon = oparms->tcon; - int remap = cifs_remap(cifs_sb); - const struct nls_table *nls = cifs_sb->local_nls; - int create_options = oparms->create_options; - int desired_access = oparms->desired_access; - int disposition = oparms->disposition; - const char *path = oparms->path; - -openRetry: - rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req, - (void **)&rsp); - if (rc) - return rc; - - /* no commands go after this */ - req->AndXCommand = 0xFF; - - if (req->hdr.Flags2 & SMBFLG2_UNICODE) { - /* account for one byte pad to word boundary */ - count = 1; - name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1), - path, PATH_MAX, nls, remap); - /* trailing null */ - name_len++; - name_len *= 2; - req->NameLength = cpu_to_le16(name_len); - } else { - /* BB improve check for buffer overruns BB */ - /* no pad */ - count = 0; - name_len = copy_path_name(req->fileName, path); - req->NameLength = cpu_to_le16(name_len); - } - - if (*oplock & REQ_OPLOCK) - req->OpenFlags = cpu_to_le32(REQ_OPLOCK); - else if (*oplock & REQ_BATCHOPLOCK) - req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); - - req->DesiredAccess = cpu_to_le32(desired_access); - req->AllocationSize = 0; - - /* - * Set file as system file if special file such as fifo and server - * expecting SFU style and no Unix extensions. - */ - if (create_options & CREATE_OPTION_SPECIAL) - req->FileAttributes = cpu_to_le32(ATTR_SYSTEM); - else - req->FileAttributes = cpu_to_le32(ATTR_NORMAL); - - /* - * XP does not handle ATTR_POSIX_SEMANTICS but it helps speed up case - * sensitive checks for other servers such as Samba. - */ - if (tcon->ses->capabilities & CAP_UNIX) - req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); - - if (create_options & CREATE_OPTION_READONLY) - req->FileAttributes |= cpu_to_le32(ATTR_READONLY); - - req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); - req->CreateDisposition = cpu_to_le32(disposition); - req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); - - /* BB Expirement with various impersonation levels and verify */ - req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); - req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY; - - count += name_len; - inc_rfc1001_len(req, count); - - req->ByteCount = cpu_to_le16(count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, - (struct smb_hdr *)rsp, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); - if (rc) { - cifs_dbg(FYI, "Error in Open = %d\n", rc); - cifs_buf_release(req); - if (rc == -EAGAIN) - goto openRetry; - return rc; - } - - /* 1 byte no need to le_to_cpu */ - *oplock = rsp->OplockLevel; - /* cifs fid stays in le */ - oparms->fid->netfid = rsp->Fid; - oparms->fid->access = desired_access; - - /* Let caller know file was created so we can set the mode. */ - /* Do we care about the CreateAction in any other cases? */ - if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction) - *oplock |= CIFS_CREATE_ACTION; - - if (buf) { - /* copy from CreationTime to Attributes */ - memcpy((char *)buf, (char *)&rsp->CreationTime, 36); - /* the file_info buf is endian converted by caller */ - buf->AllocationSize = rsp->AllocationSize; - buf->EndOfFile = rsp->EndOfFile; - buf->NumberOfLinks = cpu_to_le32(1); - buf->DeletePending = 0; - } - - cifs_buf_release(req); - return rc; -} - -static void -cifs_readv_callback(struct mid_q_entry *mid) -{ - struct cifs_readdata *rdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2, - .rq_pages = rdata->pages, - .rq_offset = rdata->page_offset, - .rq_npages = rdata->nr_pages, - .rq_pagesz = rdata->pagesz, - .rq_tailsz = rdata->tailsz }; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - - cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", - __func__, mid->mid, mid->mid_state, rdata->result, - rdata->bytes); - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - /* result already set, check signature */ - if (server->sign) { - int rc = 0; - - rc = cifs_verify_signature(&rqst, server, - mid->sequence_number); - if (rc) - cifs_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - rdata->result = -EAGAIN; - if (server->sign && rdata->got_bytes) - /* reset bytes number since we can not check a sign */ - rdata->got_bytes = 0; - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - default: - rdata->result = -EIO; - } - - queue_work(cifsiod_wq, &rdata->work); - release_mid(mid); - add_credits(server, &credits, 0); -} - -/* cifs_async_readv - send an async write, and set up mid to handle result */ -int -cifs_async_readv(struct cifs_readdata *rdata) -{ - int rc; - READ_REQ *smb = NULL; - int wct; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2 }; - - cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", - __func__, rdata->offset, rdata->bytes); - - if (tcon->ses->capabilities & CAP_LARGE_FILES) - wct = 12; - else { - wct = 10; /* old style read */ - if ((rdata->offset >> 32) > 0) { - /* can not handle this big offset for old */ - return -EIO; - } - } - - rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb); - if (rc) - return rc; - - smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid); - smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16)); - - smb->AndXCommand = 0xFF; /* none */ - smb->Fid = rdata->cfile->fid.netfid; - smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF); - if (wct == 12) - smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32); - smb->Remaining = 0; - smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF); - smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16); - if (wct == 12) - smb->ByteCount = 0; - else { - /* old style read */ - struct smb_com_readx_req *smbr = - (struct smb_com_readx_req *)smb; - smbr->ByteCount = 0; - } - - /* 4 for RFC1001 length + 1 for BCC */ - rdata->iov[0].iov_base = smb; - rdata->iov[0].iov_len = 4; - rdata->iov[1].iov_base = (char *)smb + 4; - rdata->iov[1].iov_len = get_rfc1002_length(smb); - - kref_get(&rdata->refcount); - rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, - cifs_readv_callback, NULL, rdata, 0, NULL); - - if (rc == 0) - cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); - else - kref_put(&rdata->refcount, cifs_readdata_release); - - cifs_small_buf_release(smb); - return rc; -} - -int -CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, int *pbuf_type) -{ - int rc = -EACCES; - READ_REQ *pSMB = NULL; - READ_RSP *pSMBr = NULL; - char *pReadData = NULL; - int wct; - int resp_buf_type = 0; - struct kvec iov[1]; - struct kvec rsp_iov; - __u32 pid = io_parms->pid; - __u16 netfid = io_parms->netfid; - __u64 offset = io_parms->offset; - struct cifs_tcon *tcon = io_parms->tcon; - unsigned int count = io_parms->length; - - cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid); - if (tcon->ses->capabilities & CAP_LARGE_FILES) - wct = 12; - else { - wct = 10; /* old style read */ - if ((offset >> 32) > 0) { - /* can not handle this big offset for old */ - return -EIO; - } - } - - *nbytes = 0; - rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); - - /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) - return -ECONNABORTED; - - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; - pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if (wct == 12) - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - - pSMB->Remaining = 0; - pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); - pSMB->MaxCountHigh = cpu_to_le32(count >> 16); - if (wct == 12) - pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ - else { - /* old style read */ - struct smb_com_readx_req *pSMBW = - (struct smb_com_readx_req *)pSMB; - pSMBW->ByteCount = 0; - } - - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; - rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type, - CIFS_LOG_ERROR, &rsp_iov); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); - pSMBr = (READ_RSP *)rsp_iov.iov_base; - if (rc) { - cifs_dbg(VFS, "Send error in read = %d\n", rc); - } else { - int data_length = le16_to_cpu(pSMBr->DataLengthHigh); - data_length = data_length << 16; - data_length += le16_to_cpu(pSMBr->DataLength); - *nbytes = data_length; - - /*check that DataLength would not go beyond end of SMB */ - if ((data_length > CIFSMaxBufSize) - || (data_length > count)) { - cifs_dbg(FYI, "bad length %d for count %d\n", - data_length, count); - rc = -EIO; - *nbytes = 0; - } else { - pReadData = (char *) (&pSMBr->hdr.Protocol) + - le16_to_cpu(pSMBr->DataOffset); -/* if (rc = copy_to_user(buf, pReadData, data_length)) { - cifs_dbg(VFS, "Faulting on read rc = %d\n",rc); - rc = -EFAULT; - }*/ /* can not use copy_to_user when using page cache*/ - if (*buf) - memcpy(*buf, pReadData, data_length); - } - } - - if (*buf) { - free_rsp_buf(resp_buf_type, rsp_iov.iov_base); - } else if (resp_buf_type != CIFS_NO_BUFFER) { - /* return buffer to caller to free */ - *buf = rsp_iov.iov_base; - if (resp_buf_type == CIFS_SMALL_BUFFER) - *pbuf_type = CIFS_SMALL_BUFFER; - else if (resp_buf_type == CIFS_LARGE_BUFFER) - *pbuf_type = CIFS_LARGE_BUFFER; - } /* else no valid buffer on return - leave as null */ - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - return rc; -} - - -int -CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, const char *buf) -{ - int rc = -EACCES; - WRITE_REQ *pSMB = NULL; - WRITE_RSP *pSMBr = NULL; - int bytes_returned, wct; - __u32 bytes_sent; - __u16 byte_count; - __u32 pid = io_parms->pid; - __u16 netfid = io_parms->netfid; - __u64 offset = io_parms->offset; - struct cifs_tcon *tcon = io_parms->tcon; - unsigned int count = io_parms->length; - - *nbytes = 0; - - /* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/ - if (tcon->ses == NULL) - return -ECONNABORTED; - - if (tcon->ses->capabilities & CAP_LARGE_FILES) - wct = 14; - else { - wct = 12; - if ((offset >> 32) > 0) { - /* can not handle big offset for old srv */ - return -EIO; - } - } - - rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); - - /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) - return -ECONNABORTED; - - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; - pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if (wct == 14) - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - - pSMB->Reserved = 0xFFFFFFFF; - pSMB->WriteMode = 0; - pSMB->Remaining = 0; - - /* Can increase buffer size if buffer is big enough in some cases ie we - can send more if LARGE_WRITE_X capability returned by the server and if - our buffer is big enough or if we convert to iovecs on socket writes - and eliminate the copy to the CIFS buffer */ - if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) { - bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); - } else { - bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) - & ~0xFF; - } - - if (bytes_sent > count) - bytes_sent = count; - pSMB->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); - if (buf) - memcpy(pSMB->Data, buf, bytes_sent); - else if (count != 0) { - /* No buffer */ - cifs_buf_release(pSMB); - return -EINVAL; - } /* else setting file size with write of zero bytes */ - if (wct == 14) - byte_count = bytes_sent + 1; /* pad */ - else /* wct == 12 */ - byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */ - - pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); - pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); - inc_rfc1001_len(pSMB, byte_count); - - if (wct == 14) - pSMB->ByteCount = cpu_to_le16(byte_count); - else { /* old style write has byte count 4 bytes earlier - so 4 bytes pad */ - struct smb_com_writex_req *pSMBW = - (struct smb_com_writex_req *)pSMB; - pSMBW->ByteCount = cpu_to_le16(byte_count); - } - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); - if (rc) { - cifs_dbg(FYI, "Send error in write = %d\n", rc); - } else { - *nbytes = le16_to_cpu(pSMBr->CountHigh); - *nbytes = (*nbytes) << 16; - *nbytes += le16_to_cpu(pSMBr->Count); - - /* - * Mask off high 16 bits when bytes written as returned by the - * server is greater than bytes requested by the client. Some - * OS/2 servers are known to set incorrect CountHigh values. - */ - if (*nbytes > count) - *nbytes &= 0xFFFF; - } - - cifs_buf_release(pSMB); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -/* - * Check the mid_state and signature on received buffer (if any), and queue the - * workqueue completion task. - */ -static void -cifs_writev_callback(struct mid_q_entry *mid) -{ - struct cifs_writedata *wdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - unsigned int written; - WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - wdata->result = cifs_check_receive(mid, tcon->ses->server, 0); - if (wdata->result != 0) - break; - - written = le16_to_cpu(smb->CountHigh); - written <<= 16; - written += le16_to_cpu(smb->Count); - /* - * Mask off high 16 bits when bytes written as returned - * by the server is greater than bytes requested by the - * client. OS/2 servers are known to set incorrect - * CountHigh values. - */ - if (written > wdata->bytes) - written &= 0xFFFF; - - if (written < wdata->bytes) - wdata->result = -ENOSPC; - else - wdata->bytes = written; - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - wdata->result = -EAGAIN; - break; - default: - wdata->result = -EIO; - break; - } - - queue_work(cifsiod_wq, &wdata->work); - release_mid(mid); - add_credits(tcon->ses->server, &credits, 0); -} - -/* cifs_async_writev - send an async write, and set up mid to handle result */ -int -cifs_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)) -{ - int rc = -EACCES; - WRITE_REQ *smb = NULL; - int wct; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - struct kvec iov[2]; - struct smb_rqst rqst = { }; - - if (tcon->ses->capabilities & CAP_LARGE_FILES) { - wct = 14; - } else { - wct = 12; - if (wdata->offset >> 32 > 0) { - /* can not handle big offset for old srv */ - return -EIO; - } - } - - rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb); - if (rc) - goto async_writev_out; - - smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid); - smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16)); - - smb->AndXCommand = 0xFF; /* none */ - smb->Fid = wdata->cfile->fid.netfid; - smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF); - if (wct == 14) - smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32); - smb->Reserved = 0xFFFFFFFF; - smb->WriteMode = 0; - smb->Remaining = 0; - - smb->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); - - /* 4 for RFC1001 length + 1 for BCC */ - iov[0].iov_len = 4; - iov[0].iov_base = smb; - iov[1].iov_len = get_rfc1002_length(smb) + 1; - iov[1].iov_base = (char *)smb + 4; - - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - rqst.rq_pages = wdata->pages; - rqst.rq_offset = wdata->page_offset; - rqst.rq_npages = wdata->nr_pages; - rqst.rq_pagesz = wdata->pagesz; - rqst.rq_tailsz = wdata->tailsz; - - cifs_dbg(FYI, "async write at %llu %u bytes\n", - wdata->offset, wdata->bytes); - - smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF); - smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16); - - if (wct == 14) { - inc_rfc1001_len(&smb->hdr, wdata->bytes + 1); - put_bcc(wdata->bytes + 1, &smb->hdr); - } else { - /* wct == 12 */ - struct smb_com_writex_req *smbw = - (struct smb_com_writex_req *)smb; - inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); - put_bcc(wdata->bytes + 5, &smbw->hdr); - iov[1].iov_len += 4; /* pad bigger by four bytes */ - } - - kref_get(&wdata->refcount); - rc = cifs_call_async(tcon->ses->server, &rqst, NULL, - cifs_writev_callback, NULL, wdata, 0, NULL); - - if (rc == 0) - cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); - else - kref_put(&wdata->refcount, release); - -async_writev_out: - cifs_small_buf_release(smb); - return rc; -} - -int -CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, int n_vec) -{ - int rc; - WRITE_REQ *pSMB = NULL; - int wct; - int smb_hdr_len; - int resp_buf_type = 0; - __u32 pid = io_parms->pid; - __u16 netfid = io_parms->netfid; - __u64 offset = io_parms->offset; - struct cifs_tcon *tcon = io_parms->tcon; - unsigned int count = io_parms->length; - struct kvec rsp_iov; - - *nbytes = 0; - - cifs_dbg(FYI, "write2 at %lld %d bytes\n", (long long)offset, count); - - if (tcon->ses->capabilities & CAP_LARGE_FILES) { - wct = 14; - } else { - wct = 12; - if ((offset >> 32) > 0) { - /* can not handle big offset for old srv */ - return -EIO; - } - } - rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); - - /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) - return -ECONNABORTED; - - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; - pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if (wct == 14) - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - pSMB->Reserved = 0xFFFFFFFF; - pSMB->WriteMode = 0; - pSMB->Remaining = 0; - - pSMB->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); - - pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); - pSMB->DataLengthHigh = cpu_to_le16(count >> 16); - /* header + 1 byte pad */ - smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1; - if (wct == 14) - inc_rfc1001_len(pSMB, count + 1); - else /* wct == 12 */ - inc_rfc1001_len(pSMB, count + 5); /* smb data starts later */ - if (wct == 14) - pSMB->ByteCount = cpu_to_le16(count + 1); - else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ { - struct smb_com_writex_req *pSMBW = - (struct smb_com_writex_req *)pSMB; - pSMBW->ByteCount = cpu_to_le16(count + 5); - } - iov[0].iov_base = pSMB; - if (wct == 14) - iov[0].iov_len = smb_hdr_len + 4; - else /* wct == 12 pad bigger by four bytes */ - iov[0].iov_len = smb_hdr_len + 8; - - rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0, - &rsp_iov); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); - if (rc) { - cifs_dbg(FYI, "Send error Write2 = %d\n", rc); - } else if (resp_buf_type == 0) { - /* presumably this can not happen, but best to be safe */ - rc = -EIO; - } else { - WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base; - *nbytes = le16_to_cpu(pSMBr->CountHigh); - *nbytes = (*nbytes) << 16; - *nbytes += le16_to_cpu(pSMBr->Count); - - /* - * Mask off high 16 bits when bytes written as returned by the - * server is greater than bytes requested by the client. OS/2 - * servers are known to set incorrect CountHigh values. - */ - if (*nbytes > count) - *nbytes &= 0xFFFF; - } - - free_rsp_buf(resp_buf_type, rsp_iov.iov_base); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u8 lock_type, const __u32 num_unlock, - const __u32 num_lock, LOCKING_ANDX_RANGE *buf) -{ - int rc = 0; - LOCK_REQ *pSMB = NULL; - struct kvec iov[2]; - struct kvec rsp_iov; - int resp_buf_type; - __u16 count; - - cifs_dbg(FYI, "cifs_lockv num lock %d num unlock %d\n", - num_lock, num_unlock); - - rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->Timeout = 0; - pSMB->NumberOfLocks = cpu_to_le16(num_lock); - pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock); - pSMB->LockType = lock_type; - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; /* netfid stays le */ - - count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 - - (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); - iov[1].iov_base = (char *)buf; - iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); - - cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, - CIFS_NO_RSP_BUF, &rsp_iov); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc); - - return rc; -} - -int -CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 smb_file_id, const __u32 netpid, const __u64 len, - const __u64 offset, const __u32 numUnlock, - const __u32 numLock, const __u8 lockType, - const bool waitFlag, const __u8 oplock_level) -{ - int rc = 0; - LOCK_REQ *pSMB = NULL; -/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */ - int bytes_returned; - int flags = 0; - __u16 count; - - cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n", - (int)waitFlag, numLock); - rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); - - if (rc) - return rc; - - if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { - /* no response expected */ - flags = CIFS_NO_SRV_RSP | CIFS_NON_BLOCKING | CIFS_OBREAK_OP; - pSMB->Timeout = 0; - } else if (waitFlag) { - flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ - pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */ - } else { - pSMB->Timeout = 0; - } - - pSMB->NumberOfLocks = cpu_to_le16(numLock); - pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock); - pSMB->LockType = lockType; - pSMB->OplockLevel = oplock_level; - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = smb_file_id; /* netfid stays le */ - - if ((numLock != 0) || (numUnlock != 0)) { - pSMB->Locks[0].Pid = cpu_to_le16(netpid); - /* BB where to store pid high? */ - pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len); - pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32)); - pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset); - pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32)); - count = sizeof(LOCKING_ANDX_RANGE); - } else { - /* oplock break */ - count = 0; - } - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - if (waitFlag) - rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMB, &bytes_returned); - else - rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - if (rc) - cifs_dbg(FYI, "Send error in Lock = %d\n", rc); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - return rc; -} - -int -CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 smb_file_id, const __u32 netpid, - const loff_t start_offset, const __u64 len, - struct file_lock *pLockData, const __u16 lock_type, - const bool waitFlag) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; - struct cifs_posix_lock *parm_data; - int rc = 0; - int timeout = 0; - int bytes_returned = 0; - int resp_buf_type = 0; - __u16 params, param_offset, offset, byte_count, count; - struct kvec iov[1]; - struct kvec rsp_iov; - - cifs_dbg(FYI, "Posix Lock\n"); - - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - count = sizeof(struct cifs_posix_lock); - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - if (pLockData) - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - else - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - parm_data = (struct cifs_posix_lock *) - (((char *)pSMB) + offset + 4); - - parm_data->lock_type = cpu_to_le16(lock_type); - if (waitFlag) { - timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ - parm_data->lock_flags = cpu_to_le16(1); - pSMB->Timeout = cpu_to_le32(-1); - } else - pSMB->Timeout = 0; - - parm_data->pid = cpu_to_le32(netpid); - parm_data->start = cpu_to_le64(start_offset); - parm_data->length = cpu_to_le64(len); /* normalize negative numbers */ - - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->Fid = smb_file_id; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - if (waitFlag) { - rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned); - } else { - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; - rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, - &resp_buf_type, timeout, &rsp_iov); - pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base; - } - cifs_small_buf_release(pSMB); - - if (rc) { - cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc); - } else if (pLockData) { - /* lock structure can be returned on get */ - __u16 data_offset; - __u16 data_count; - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) { - rc = -EIO; /* bad smb */ - goto plk_err_exit; - } - data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - data_count = le16_to_cpu(pSMBr->t2.DataCount); - if (data_count < sizeof(struct cifs_posix_lock)) { - rc = -EIO; - goto plk_err_exit; - } - parm_data = (struct cifs_posix_lock *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) - pLockData->fl_type = F_UNLCK; - else { - if (parm_data->lock_type == - cpu_to_le16(CIFS_RDLCK)) - pLockData->fl_type = F_RDLCK; - else if (parm_data->lock_type == - cpu_to_le16(CIFS_WRLCK)) - pLockData->fl_type = F_WRLCK; - - pLockData->fl_start = le64_to_cpu(parm_data->start); - pLockData->fl_end = pLockData->fl_start + - (le64_to_cpu(parm_data->length) ? - le64_to_cpu(parm_data->length) - 1 : 0); - pLockData->fl_pid = -le32_to_cpu(parm_data->pid); - } - } - -plk_err_exit: - free_rsp_buf(resp_buf_type, rsp_iov.iov_base); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - - -int -CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) -{ - int rc = 0; - CLOSE_REQ *pSMB = NULL; - cifs_dbg(FYI, "In CIFSSMBClose\n"); - -/* do not retry on dead session on close */ - rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); - if (rc == -EAGAIN) - return 0; - if (rc) - return rc; - - pSMB->FileID = (__u16) smb_file_id; - pSMB->LastWriteTime = 0xFFFFFFFF; - pSMB->ByteCount = 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_closes); - if (rc) { - if (rc != -EINTR) { - /* EINTR is expected when user ctl-c to kill app */ - cifs_dbg(VFS, "Send error in Close = %d\n", rc); - } - } - - /* Since session is dead, file will be closed on server already */ - if (rc == -EAGAIN) - rc = 0; - - return rc; -} - -int -CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) -{ - int rc = 0; - FLUSH_REQ *pSMB = NULL; - cifs_dbg(FYI, "In CIFSSMBFlush\n"); - - rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->FileID = (__u16) smb_file_id; - pSMB->ByteCount = 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes); - if (rc) - cifs_dbg(VFS, "Send error in Flush = %d\n", rc); - - return rc; -} - -int -CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - int rc = 0; - RENAME_REQ *pSMB = NULL; - RENAME_RSP *pSMBr = NULL; - int bytes_returned; - int name_len, name_len2; - __u16 count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSSMBRename\n"); -renameRetry: - rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->BufferFormat = 0x04; - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | - ATTR_DIRECTORY); - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, - from_name, PATH_MAX, - cifs_sb->local_nls, remap); - name_len++; /* trailing null */ - name_len *= 2; - pSMB->OldFileName[name_len] = 0x04; /* pad */ - /* protocol requires ASCII signature byte on Unicode string */ - pSMB->OldFileName[name_len + 1] = 0x00; - name_len2 = - cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], - to_name, PATH_MAX, cifs_sb->local_nls, - remap); - name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; - name_len2 *= 2; /* convert to bytes */ - } else { - name_len = copy_path_name(pSMB->OldFileName, from_name); - name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); - pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ - name_len2++; /* signature byte */ - } - - count = 1 /* 1st signature byte */ + name_len + name_len2; - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_renames); - if (rc) - cifs_dbg(FYI, "Send error in rename = %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto renameRetry; - - return rc; -} - -int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon, - int netfid, const char *target_name, - const struct nls_table *nls_codepage, int remap) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; - struct set_file_rename *rename_info; - char *data_offset; - char dummy_string[30]; - int rc = 0; - int bytes_returned = 0; - int len_of_str; - __u16 params, param_offset, offset, count, byte_count; - - cifs_dbg(FYI, "Rename to File by handle\n"); - rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)(pSMB) + offset + 4; - rename_info = (struct set_file_rename *) data_offset; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - /* construct random name ".cifs_tmp" */ - rename_info->overwrite = cpu_to_le32(1); - rename_info->root_fid = 0; - /* unicode only call */ - if (target_name == NULL) { - sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); - len_of_str = - cifsConvertToUTF16((__le16 *)rename_info->target_name, - dummy_string, 24, nls_codepage, remap); - } else { - len_of_str = - cifsConvertToUTF16((__le16 *)rename_info->target_name, - target_name, PATH_MAX, nls_codepage, - remap); - } - rename_info->target_name_len = cpu_to_le32(2 * len_of_str); - count = sizeof(struct set_file_rename) + (2 * len_of_str); - byte_count += count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->Fid = netfid; - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&pTcon->stats.cifs_stats.num_t2renames); - if (rc) - cifs_dbg(FYI, "Send error in Rename (by file handle) = %d\n", - rc); - - cifs_buf_release(pSMB); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int -CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon, - const char *fromName, const __u16 target_tid, const char *toName, - const int flags, const struct nls_table *nls_codepage, int remap) -{ - int rc = 0; - COPY_REQ *pSMB = NULL; - COPY_RSP *pSMBr = NULL; - int bytes_returned; - int name_len, name_len2; - __u16 count; - - cifs_dbg(FYI, "In CIFSSMBCopy\n"); -copyRetry: - rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->BufferFormat = 0x04; - pSMB->Tid2 = target_tid; - - pSMB->Flags = cpu_to_le16(flags & COPY_TREE); - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, - fromName, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - pSMB->OldFileName[name_len] = 0x04; /* pad */ - /* protocol requires ASCII signature byte on Unicode string */ - pSMB->OldFileName[name_len + 1] = 0x00; - name_len2 = - cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], - toName, PATH_MAX, nls_codepage, remap); - name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; - name_len2 *= 2; /* convert to bytes */ - } else { - name_len = copy_path_name(pSMB->OldFileName, fromName); - pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ - name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, toName); - name_len2++; /* signature byte */ - } - - count = 1 /* 1st signature byte */ + name_len + name_len2; - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n", - rc, le16_to_cpu(pSMBr->CopyCount)); - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto copyRetry; - - return rc; -} - -int -CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - char *data_offset; - int name_len; - int name_len_target; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count; - - cifs_dbg(FYI, "In Symlink Unix style\n"); -createSymLinkRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fromName, - /* find define for this maxpathcomponent */ - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - - } else { - name_len = copy_path_name(pSMB->FileName, fromName); - } - params = 6 + name_len; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)pSMB + offset + 4; - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len_target = - cifsConvertToUTF16((__le16 *) data_offset, toName, - /* find define for this maxpathcomponent */ - PATH_MAX, nls_codepage, remap); - name_len_target++; /* trailing null */ - name_len_target *= 2; - } else { - name_len_target = copy_path_name(data_offset, toName); - } - - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max on data count below from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + name_len_target; - pSMB->DataCount = cpu_to_le16(name_len_target); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks); - if (rc) - cifs_dbg(FYI, "Send error in SetPathInfo create symlink = %d\n", - rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto createSymLinkRetry; - - return rc; -} - -int -CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - char *data_offset; - int name_len; - int name_len_target; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count; - - cifs_dbg(FYI, "In Create Hard link Unix style\n"); -createHardLinkRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - - } else { - name_len = copy_path_name(pSMB->FileName, toName); - } - params = 6 + name_len; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)pSMB + offset + 4; - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len_target = - cifsConvertToUTF16((__le16 *) data_offset, fromName, - PATH_MAX, nls_codepage, remap); - name_len_target++; /* trailing null */ - name_len_target *= 2; - } else { - name_len_target = copy_path_name(data_offset, fromName); - } - - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max on data count below from sess*/ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + name_len_target; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->DataCount = cpu_to_le16(name_len_target); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); - if (rc) - cifs_dbg(FYI, "Send error in SetPathInfo (hard link) = %d\n", - rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto createHardLinkRetry; - - return rc; -} - -int -CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - int rc = 0; - NT_RENAME_REQ *pSMB = NULL; - RENAME_RSP *pSMBr = NULL; - int bytes_returned; - int name_len, name_len2; - __u16 count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSCreateHardLink\n"); -winCreateHardLinkRetry: - - rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | - ATTR_DIRECTORY); - pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK); - pSMB->ClusterCount = 0; - - pSMB->BufferFormat = 0x04; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name, - PATH_MAX, cifs_sb->local_nls, remap); - name_len++; /* trailing null */ - name_len *= 2; - - /* protocol specifies ASCII buffer format (0x04) for unicode */ - pSMB->OldFileName[name_len] = 0x04; - pSMB->OldFileName[name_len + 1] = 0x00; /* pad */ - name_len2 = - cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], - to_name, PATH_MAX, cifs_sb->local_nls, - remap); - name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; - name_len2 *= 2; /* convert to bytes */ - } else { - name_len = copy_path_name(pSMB->OldFileName, from_name); - pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ - name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); - name_len2++; /* signature byte */ - } - - count = 1 /* string type byte */ + name_len + name_len2; - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); - if (rc) - cifs_dbg(FYI, "Send error in hard link (NT rename) = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto winCreateHardLinkRetry; - - return rc; -} - -int -CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, char **symlinkinfo, - const struct nls_table *nls_codepage, int remap) -{ -/* SMB_QUERY_FILE_UNIX_LINK */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - char *data_start; - - cifs_dbg(FYI, "In QPathSymLinkInfo (Unix) for path %s\n", searchName); - -querySymLinkRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - searchName, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc); - } else { - /* decode response */ - - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - rc = -EIO; - else { - bool is_unicode; - u16 count = le16_to_cpu(pSMBr->t2.DataCount); - - data_start = ((char *) &pSMBr->hdr.Protocol) + - le16_to_cpu(pSMBr->t2.DataOffset); - - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - /* BB FIXME investigate remapping reserved chars here */ - *symlinkinfo = cifs_strndup_from_utf16(data_start, - count, is_unicode, nls_codepage); - if (!*symlinkinfo) - rc = -ENOMEM; - } - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto querySymLinkRetry; - return rc; -} - -/* - * Recent Windows versions now create symlinks more frequently - * and they use the "reparse point" mechanism below. We can of course - * do symlinks nicely to Samba and other servers which support the - * CIFS Unix Extensions and we can also do SFU symlinks and "client only" - * "MF" symlinks optionally, but for recent Windows we really need to - * reenable the code below and fix the cifs_symlink callers to handle this. - * In the interim this code has been moved to its own config option so - * it is not compiled in by default until callers fixed up and more tested. - */ -int -CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, char **symlinkinfo, - const struct nls_table *nls_codepage) -{ - int rc = 0; - int bytes_returned; - struct smb_com_transaction_ioctl_req *pSMB; - struct smb_com_transaction_ioctl_rsp *pSMBr; - bool is_unicode; - unsigned int sub_len; - char *sub_start; - struct reparse_symlink_data *reparse_buf; - struct reparse_posix_data *posix_buf; - __u32 data_offset, data_count; - char *end_of_smb; - - cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); - rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->TotalParameterCount = 0 ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le32(2); - /* BB find exact data count max from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->MaxSetupCount = 4; - pSMB->Reserved = 0; - pSMB->ParameterOffset = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 4; - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT); - pSMB->IsFsctl = 1; /* FSCTL */ - pSMB->IsRootFlag = 0; - pSMB->Fid = fid; /* file handle always le */ - pSMB->ByteCount = 0; - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); - goto qreparse_out; - } - - data_offset = le32_to_cpu(pSMBr->DataOffset); - data_count = le32_to_cpu(pSMBr->DataCount); - if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { - /* BB also check enough total bytes returned */ - rc = -EIO; /* bad smb */ - goto qreparse_out; - } - if (!data_count || (data_count > 2048)) { - rc = -EIO; - cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); - goto qreparse_out; - } - end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; - reparse_buf = (struct reparse_symlink_data *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - if ((char *)reparse_buf >= end_of_smb) { - rc = -EIO; - goto qreparse_out; - } - if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { - cifs_dbg(FYI, "NFS style reparse tag\n"); - posix_buf = (struct reparse_posix_data *)reparse_buf; - - if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { - cifs_dbg(FYI, "unsupported file type 0x%llx\n", - le64_to_cpu(posix_buf->InodeType)); - rc = -EOPNOTSUPP; - goto qreparse_out; - } - is_unicode = true; - sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); - if (posix_buf->PathBuffer + sub_len > end_of_smb) { - cifs_dbg(FYI, "reparse buf beyond SMB\n"); - rc = -EIO; - goto qreparse_out; - } - *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, - sub_len, is_unicode, nls_codepage); - goto qreparse_out; - } else if (reparse_buf->ReparseTag != - cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { - rc = -EOPNOTSUPP; - goto qreparse_out; - } - - /* Reparse tag is NTFS symlink */ - sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + - reparse_buf->PathBuffer; - sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); - if (sub_start + sub_len > end_of_smb) { - cifs_dbg(FYI, "reparse buf beyond SMB\n"); - rc = -EIO; - goto qreparse_out; - } - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - /* BB FIXME investigate remapping reserved chars here */ - *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, - nls_codepage); - if (!*symlinkinfo) - rc = -ENOMEM; -qreparse_out: - cifs_buf_release(pSMB); - - /* - * Note: On -EAGAIN error only caller can retry on handle based calls - * since file handle passed in no longer valid. - */ - return rc; -} - -int -CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid) -{ - int rc = 0; - int bytes_returned; - struct smb_com_transaction_compr_ioctl_req *pSMB; - struct smb_com_transaction_ioctl_rsp *pSMBr; - - cifs_dbg(FYI, "Set compression for %u\n", fid); - rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); - - pSMB->TotalParameterCount = 0; - pSMB->TotalDataCount = cpu_to_le32(2); - pSMB->MaxParameterCount = 0; - pSMB->MaxDataCount = 0; - pSMB->MaxSetupCount = 4; - pSMB->Reserved = 0; - pSMB->ParameterOffset = 0; - pSMB->DataCount = cpu_to_le32(2); - pSMB->DataOffset = - cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, - compression_state) - 4); /* 84 */ - pSMB->SetupCount = 4; - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); - pSMB->ParameterCount = 0; - pSMB->FunctionCode = cpu_to_le32(FSCTL_SET_COMPRESSION); - pSMB->IsFsctl = 1; /* FSCTL */ - pSMB->IsRootFlag = 0; - pSMB->Fid = fid; /* file handle always le */ - /* 3 byte pad, followed by 2 byte compress state */ - pSMB->ByteCount = cpu_to_le16(5); - inc_rfc1001_len(pSMB, 5); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); - - cifs_buf_release(pSMB); - - /* - * Note: On -EAGAIN error only caller can retry on handle based calls - * since file handle passed in no longer valid. - */ - return rc; -} - - -#ifdef CONFIG_CIFS_POSIX - -/*Convert an Access Control Entry from wire format to local POSIX xattr format*/ -static void cifs_convert_ace(struct posix_acl_xattr_entry *ace, - struct cifs_posix_ace *cifs_ace) -{ - /* u8 cifs fields do not need le conversion */ - ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); - ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag); - ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid)); -/* - cifs_dbg(FYI, "perm %d tag %d id %d\n", - ace->e_perm, ace->e_tag, ace->e_id); -*/ - - return; -} - -/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ -static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, - const int acl_type, const int size_of_data_area) -{ - int size = 0; - int i; - __u16 count; - struct cifs_posix_ace *pACE; - struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; - struct posix_acl_xattr_header *local_acl = (void *)trgt; - - if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) - return -EOPNOTSUPP; - - if (acl_type == ACL_TYPE_ACCESS) { - count = le16_to_cpu(cifs_acl->access_entry_count); - pACE = &cifs_acl->ace_array[0]; - size = sizeof(struct cifs_posix_acl); - size += sizeof(struct cifs_posix_ace) * count; - /* check if we would go beyond end of SMB */ - if (size_of_data_area < size) { - cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n", - size_of_data_area, size); - return -EINVAL; - } - } else if (acl_type == ACL_TYPE_DEFAULT) { - count = le16_to_cpu(cifs_acl->access_entry_count); - size = sizeof(struct cifs_posix_acl); - size += sizeof(struct cifs_posix_ace) * count; -/* skip past access ACEs to get to default ACEs */ - pACE = &cifs_acl->ace_array[count]; - count = le16_to_cpu(cifs_acl->default_entry_count); - size += sizeof(struct cifs_posix_ace) * count; - /* check if we would go beyond end of SMB */ - if (size_of_data_area < size) - return -EINVAL; - } else { - /* illegal type */ - return -EINVAL; - } - - size = posix_acl_xattr_size(count); - if ((buflen == 0) || (local_acl == NULL)) { - /* used to query ACL EA size */ - } else if (size > buflen) { - return -ERANGE; - } else /* buffer big enough */ { - struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); - - local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); - for (i = 0; i < count ; i++) { - cifs_convert_ace(&ace[i], pACE); - pACE++; - } - } - return size; -} - -static void convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, - const struct posix_acl_xattr_entry *local_ace) -{ - cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); - cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); - /* BB is there a better way to handle the large uid? */ - if (local_ace->e_id == cpu_to_le32(-1)) { - /* Probably no need to le convert -1 on any arch but can not hurt */ - cifs_ace->cifs_uid = cpu_to_le64(-1); - } else - cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); -/* - cifs_dbg(FYI, "perm %d tag %d id %d\n", - ace->e_perm, ace->e_tag, ace->e_id); -*/ -} - -/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ -static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, - const int buflen, const int acl_type) -{ - __u16 rc = 0; - struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; - struct posix_acl_xattr_header *local_acl = (void *)pACL; - struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); - int count; - int i; - - if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) - return 0; - - count = posix_acl_xattr_count((size_t)buflen); - cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n", - count, buflen, le32_to_cpu(local_acl->a_version)); - if (le32_to_cpu(local_acl->a_version) != 2) { - cifs_dbg(FYI, "unknown POSIX ACL version %d\n", - le32_to_cpu(local_acl->a_version)); - return 0; - } - cifs_acl->version = cpu_to_le16(1); - if (acl_type == ACL_TYPE_ACCESS) { - cifs_acl->access_entry_count = cpu_to_le16(count); - cifs_acl->default_entry_count = cpu_to_le16(0xFFFF); - } else if (acl_type == ACL_TYPE_DEFAULT) { - cifs_acl->default_entry_count = cpu_to_le16(count); - cifs_acl->access_entry_count = cpu_to_le16(0xFFFF); - } else { - cifs_dbg(FYI, "unknown ACL type %d\n", acl_type); - return 0; - } - for (i = 0; i < count; i++) - convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]); - if (rc == 0) { - rc = (__u16)(count * sizeof(struct cifs_posix_ace)); - rc += sizeof(struct cifs_posix_acl); - /* BB add check to make sure ACL does not overflow SMB */ - } - return rc; -} - -int -CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - char *acl_inf, const int buflen, const int acl_type, - const struct nls_table *nls_codepage, int remap) -{ -/* SMB_QUERY_POSIX_ACL */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - - cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName); - -queryAclRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - searchName, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - pSMB->FileName[name_len] = 0; - pSMB->FileName[name_len+1] = 0; - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max data count below from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_qpi_req, - InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); - if (rc) { - cifs_dbg(FYI, "Send error in Query POSIX ACL = %d\n", rc); - } else { - /* decode response */ - - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - rc = cifs_copy_posix_acl(acl_inf, - (char *)&pSMBr->hdr.Protocol+data_offset, - buflen, acl_type, count); - } - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto queryAclRetry; - return rc; -} - -int -CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *fileName, - const char *local_acl, const int buflen, - const int acl_type, - const struct nls_table *nls_codepage, int remap) -{ - struct smb_com_transaction2_spi_req *pSMB = NULL; - struct smb_com_transaction2_spi_rsp *pSMBr = NULL; - char *parm_data; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count, data_count, param_offset, offset; - - cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName); -setAclRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - params = 6 + name_len; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB size from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - parm_data = ((char *) &pSMB->hdr.Protocol) + offset; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - - /* convert to on the wire format for POSIX ACL */ - data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type); - - if (data_count == 0) { - rc = -EOPNOTSUPP; - goto setACLerrorExit; - } - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL); - byte_count = 3 /* pad */ + params + data_count; - pSMB->DataCount = cpu_to_le16(data_count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc); - -setACLerrorExit: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto setAclRetry; - return rc; -} - -int -CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, - const int netfid, __u64 *pExtAttrBits, __u64 *pMask) -{ - int rc = 0; - struct smb_t2_qfi_req *pSMB = NULL; - struct smb_t2_qfi_rsp *pSMBr = NULL; - int bytes_returned; - __u16 params, byte_count; - - cifs_dbg(FYI, "In GetExtAttr\n"); - if (tcon == NULL) - return -ENODEV; - -GetExtAttrRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2 /* level */ + 2 /* fid */; - pSMB->t2.TotalDataCount = 0; - pSMB->t2.MaxParameterCount = cpu_to_le16(4); - /* BB find exact max data count below from sess structure BB */ - pSMB->t2.MaxDataCount = cpu_to_le16(4000); - pSMB->t2.MaxSetupCount = 0; - pSMB->t2.Reserved = 0; - pSMB->t2.Flags = 0; - pSMB->t2.Timeout = 0; - pSMB->t2.Reserved2 = 0; - pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, - Fid) - 4); - pSMB->t2.DataCount = 0; - pSMB->t2.DataOffset = 0; - pSMB->t2.SetupCount = 1; - pSMB->t2.Reserved3 = 0; - pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->t2.TotalParameterCount = cpu_to_le16(params); - pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); - pSMB->Pad = 0; - pSMB->Fid = netfid; - inc_rfc1001_len(pSMB, byte_count); - pSMB->t2.ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "error %d in GetExtAttr\n", rc); - } else { - /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - /* If rc should we check for EOPNOSUPP and - disable the srvino flag? or in caller? */ - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - struct file_chattr_info *pfinfo; - - if (count != 16) { - cifs_dbg(FYI, "Invalid size ret in GetExtAttr\n"); - rc = -EIO; - goto GetExtAttrOut; - } - pfinfo = (struct file_chattr_info *) - (data_offset + (char *) &pSMBr->hdr.Protocol); - *pExtAttrBits = le64_to_cpu(pfinfo->mode); - *pMask = le64_to_cpu(pfinfo->mask); - } - } -GetExtAttrOut: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto GetExtAttrRetry; - return rc; -} - -#endif /* CONFIG_POSIX */ - -/* - * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that - * all NT TRANSACTS that we init here have total parm and data under about 400 - * bytes (to fit in small cifs buffer size), which is the case so far, it - * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of - * returned setup area) and MaxParameterCount (returned parms size) must be set - * by caller - */ -static int -smb_init_nttransact(const __u16 sub_command, const int setup_count, - const int parm_len, struct cifs_tcon *tcon, - void **ret_buf) -{ - int rc; - __u32 temp_offset; - struct smb_com_ntransact_req *pSMB; - - rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, - (void **)&pSMB); - if (rc) - return rc; - *ret_buf = (void *)pSMB; - pSMB->Reserved = 0; - pSMB->TotalParameterCount = cpu_to_le32(parm_len); - pSMB->TotalDataCount = 0; - pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->DataCount = pSMB->TotalDataCount; - temp_offset = offsetof(struct smb_com_ntransact_req, Parms) + - (setup_count * 2) - 4 /* for rfc1001 length itself */; - pSMB->ParameterOffset = cpu_to_le32(temp_offset); - pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len); - pSMB->SetupCount = setup_count; /* no need to le convert byte fields */ - pSMB->SubCommand = cpu_to_le16(sub_command); - return 0; -} - -static int -validate_ntransact(char *buf, char **ppparm, char **ppdata, - __u32 *pparmlen, __u32 *pdatalen) -{ - char *end_of_smb; - __u32 data_count, data_offset, parm_count, parm_offset; - struct smb_com_ntransact_rsp *pSMBr; - u16 bcc; - - *pdatalen = 0; - *pparmlen = 0; - - if (buf == NULL) - return -EINVAL; - - pSMBr = (struct smb_com_ntransact_rsp *)buf; - - bcc = get_bcc(&pSMBr->hdr); - end_of_smb = 2 /* sizeof byte count */ + bcc + - (char *)&pSMBr->ByteCount; - - data_offset = le32_to_cpu(pSMBr->DataOffset); - data_count = le32_to_cpu(pSMBr->DataCount); - parm_offset = le32_to_cpu(pSMBr->ParameterOffset); - parm_count = le32_to_cpu(pSMBr->ParameterCount); - - *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; - *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; - - /* should we also check that parm and data areas do not overlap? */ - if (*ppparm > end_of_smb) { - cifs_dbg(FYI, "parms start after end of smb\n"); - return -EINVAL; - } else if (parm_count + *ppparm > end_of_smb) { - cifs_dbg(FYI, "parm end after end of smb\n"); - return -EINVAL; - } else if (*ppdata > end_of_smb) { - cifs_dbg(FYI, "data starts after end of smb\n"); - return -EINVAL; - } else if (data_count + *ppdata > end_of_smb) { - cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n", - *ppdata, data_count, (data_count + *ppdata), - end_of_smb, pSMBr); - return -EINVAL; - } else if (parm_count + data_count > bcc) { - cifs_dbg(FYI, "parm count and data count larger than SMB\n"); - return -EINVAL; - } - *pdatalen = data_count; - *pparmlen = parm_count; - return 0; -} - -/* Get Security Descriptor (by handle) from remote server for a file or dir */ -int -CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, - struct cifs_ntsd **acl_inf, __u32 *pbuflen) -{ - int rc = 0; - int buf_type = 0; - QUERY_SEC_DESC_REQ *pSMB; - struct kvec iov[1]; - struct kvec rsp_iov; - - cifs_dbg(FYI, "GetCifsACL\n"); - - *pbuflen = 0; - *acl_inf = NULL; - - rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, - 8 /* parm len */, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->MaxParameterCount = cpu_to_le32(4); - /* BB TEST with big acls that might need to be e.g. larger than 16K */ - pSMB->MaxSetupCount = 0; - pSMB->Fid = fid; /* file handle always le */ - pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP | - CIFS_ACL_DACL); - pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */ - inc_rfc1001_len(pSMB, 11); - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; - - rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, - 0, &rsp_iov); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); - if (rc) { - cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc); - } else { /* decode response */ - __le32 *parm; - __u32 parm_len; - __u32 acl_len; - struct smb_com_ntransact_rsp *pSMBr; - char *pdata; - -/* validate_nttransact */ - rc = validate_ntransact(rsp_iov.iov_base, (char **)&parm, - &pdata, &parm_len, pbuflen); - if (rc) - goto qsec_out; - pSMBr = (struct smb_com_ntransact_rsp *)rsp_iov.iov_base; - - cifs_dbg(FYI, "smb %p parm %p data %p\n", - pSMBr, parm, *acl_inf); - - if (le32_to_cpu(pSMBr->ParameterCount) != 4) { - rc = -EIO; /* bad smb */ - *pbuflen = 0; - goto qsec_out; - } - -/* BB check that data area is minimum length and as big as acl_len */ - - acl_len = le32_to_cpu(*parm); - if (acl_len != *pbuflen) { - cifs_dbg(VFS, "acl length %d does not match %d\n", - acl_len, *pbuflen); - if (*pbuflen > acl_len) - *pbuflen = acl_len; - } - - /* check if buffer is big enough for the acl - header followed by the smallest SID */ - if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) || - (*pbuflen >= 64 * 1024)) { - cifs_dbg(VFS, "bad acl length %d\n", *pbuflen); - rc = -EINVAL; - *pbuflen = 0; - } else { - *acl_inf = kmemdup(pdata, *pbuflen, GFP_KERNEL); - if (*acl_inf == NULL) { - *pbuflen = 0; - rc = -ENOMEM; - } - } - } -qsec_out: - free_rsp_buf(buf_type, rsp_iov.iov_base); - return rc; -} - -int -CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, - struct cifs_ntsd *pntsd, __u32 acllen, int aclflag) -{ - __u16 byte_count, param_count, data_count, param_offset, data_offset; - int rc = 0; - int bytes_returned = 0; - SET_SEC_DESC_REQ *pSMB = NULL; - void *pSMBr; - -setCifsAclRetry: - rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr); - if (rc) - return rc; - - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - - param_count = 8; - param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4; - data_count = acllen; - data_offset = param_offset + param_count; - byte_count = 3 /* pad */ + param_count; - - pSMB->DataCount = cpu_to_le32(data_count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->MaxParameterCount = cpu_to_le32(4); - pSMB->MaxDataCount = cpu_to_le32(16384); - pSMB->ParameterCount = cpu_to_le32(param_count); - pSMB->ParameterOffset = cpu_to_le32(param_offset); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->DataOffset = cpu_to_le32(data_offset); - pSMB->SetupCount = 0; - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC); - pSMB->ByteCount = cpu_to_le16(byte_count+data_count); - - pSMB->Fid = fid; /* file handle always le */ - pSMB->Reserved2 = 0; - pSMB->AclFlags = cpu_to_le32(aclflag); - - if (pntsd && acllen) { - memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) + - data_offset, pntsd, acllen); - inc_rfc1001_len(pSMB, byte_count + data_count); - } else - inc_rfc1001_len(pSMB, byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - - cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n", - bytes_returned, rc); - if (rc) - cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc); - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto setCifsAclRetry; - - return (rc); -} - - -/* Legacy Query Path Information call for lookup to old servers such - as Win9x/WinME */ -int -SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - const struct nls_table *nls_codepage, int remap) -{ - QUERY_INFORMATION_REQ *pSMB; - QUERY_INFORMATION_RSP *pSMBr; - int rc = 0; - int bytes_returned; - int name_len; - - cifs_dbg(FYI, "In SMBQPath path %s\n", search_name); -QInfRetry: - rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - search_name, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, search_name); - } - pSMB->BufferFormat = 0x04; - name_len++; /* account for buffer type byte */ - inc_rfc1001_len(pSMB, (__u16)name_len); - pSMB->ByteCount = cpu_to_le16(name_len); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc); - } else if (data) { - struct timespec64 ts; - __u32 time = le32_to_cpu(pSMBr->last_write_time); - - /* decode response */ - /* BB FIXME - add time zone adjustment BB */ - memset(data, 0, sizeof(FILE_ALL_INFO)); - ts.tv_nsec = 0; - ts.tv_sec = time; - /* decode time fields */ - data->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts)); - data->LastWriteTime = data->ChangeTime; - data->LastAccessTime = 0; - data->AllocationSize = - cpu_to_le64(le32_to_cpu(pSMBr->size)); - data->EndOfFile = data->AllocationSize; - data->Attributes = - cpu_to_le32(le16_to_cpu(pSMBr->attr)); - } else - rc = -EIO; /* bad buffer passed in */ - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QInfRetry; - - return rc; -} - -int -CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_ALL_INFO *pFindData) -{ - struct smb_t2_qfi_req *pSMB = NULL; - struct smb_t2_qfi_rsp *pSMBr = NULL; - int rc = 0; - int bytes_returned; - __u16 params, byte_count; - -QFileInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2 /* level */ + 2 /* fid */; - pSMB->t2.TotalDataCount = 0; - pSMB->t2.MaxParameterCount = cpu_to_le16(4); - /* BB find exact max data count below from sess structure BB */ - pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->t2.MaxSetupCount = 0; - pSMB->t2.Reserved = 0; - pSMB->t2.Flags = 0; - pSMB->t2.Timeout = 0; - pSMB->t2.Reserved2 = 0; - pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, - Fid) - 4); - pSMB->t2.DataCount = 0; - pSMB->t2.DataOffset = 0; - pSMB->t2.SetupCount = 1; - pSMB->t2.Reserved3 = 0; - pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->t2.TotalParameterCount = cpu_to_le16(params); - pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); - pSMB->Pad = 0; - pSMB->Fid = netfid; - inc_rfc1001_len(pSMB, byte_count); - pSMB->t2.ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QFileInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc) /* BB add auto retry on EOPNOTSUPP? */ - rc = -EIO; - else if (get_bcc(&pSMBr->hdr) < 40) - rc = -EIO; /* bad smb */ - else if (pFindData) { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - memcpy((char *) pFindData, - (char *) &pSMBr->hdr.Protocol + - data_offset, sizeof(FILE_ALL_INFO)); - } else - rc = -ENOMEM; - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto QFileInfoRetry; - - return rc; -} - -int -CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - int legacy /* old style infolevel */, - const struct nls_table *nls_codepage, int remap) -{ - /* level 263 SMB_QUERY_FILE_ALL_INFO */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - - /* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */ -QPathInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, search_name, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, search_name); - } - - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - if (legacy) - pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); - else - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc) /* BB add auto retry on EOPNOTSUPP? */ - rc = -EIO; - else if (!legacy && get_bcc(&pSMBr->hdr) < 40) - rc = -EIO; /* bad smb */ - else if (legacy && get_bcc(&pSMBr->hdr) < 24) - rc = -EIO; /* 24 or 26 expected but we do not read - last field */ - else if (data) { - int size; - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - - /* - * On legacy responses we do not read the last field, - * EAsize, fortunately since it varies by subdialect and - * also note it differs on Set vs Get, ie two bytes or 4 - * bytes depending but we don't care here. - */ - if (legacy) - size = sizeof(FILE_INFO_STANDARD); - else - size = sizeof(FILE_ALL_INFO); - memcpy((char *) data, (char *) &pSMBr->hdr.Protocol + - data_offset, size); - } else - rc = -ENOMEM; - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto QPathInfoRetry; - - return rc; -} - -int -CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_UNIX_BASIC_INFO *pFindData) -{ - struct smb_t2_qfi_req *pSMB = NULL; - struct smb_t2_qfi_rsp *pSMBr = NULL; - int rc = 0; - int bytes_returned; - __u16 params, byte_count; - -UnixQFileInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2 /* level */ + 2 /* fid */; - pSMB->t2.TotalDataCount = 0; - pSMB->t2.MaxParameterCount = cpu_to_le16(4); - /* BB find exact max data count below from sess structure BB */ - pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->t2.MaxSetupCount = 0; - pSMB->t2.Reserved = 0; - pSMB->t2.Flags = 0; - pSMB->t2.Timeout = 0; - pSMB->t2.Reserved2 = 0; - pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, - Fid) - 4); - pSMB->t2.DataCount = 0; - pSMB->t2.DataOffset = 0; - pSMB->t2.SetupCount = 1; - pSMB->t2.Reserved3 = 0; - pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->t2.TotalParameterCount = cpu_to_le16(params); - pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); - pSMB->Pad = 0; - pSMB->Fid = netfid; - inc_rfc1001_len(pSMB, byte_count); - pSMB->t2.ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in UnixQFileInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { - cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - memcpy((char *) pFindData, - (char *) &pSMBr->hdr.Protocol + - data_offset, - sizeof(FILE_UNIX_BASIC_INFO)); - } - } - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto UnixQFileInfoRetry; - - return rc; -} - -int -CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - FILE_UNIX_BASIC_INFO *pFindData, - const struct nls_table *nls_codepage, int remap) -{ -/* SMB_QUERY_FILE_UNIX_BASIC */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned = 0; - int name_len; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QPathInfo (Unix) the path %s\n", searchName); -UnixQPathInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in UnixQPathInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { - cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - memcpy((char *) pFindData, - (char *) &pSMBr->hdr.Protocol + - data_offset, - sizeof(FILE_UNIX_BASIC_INFO)); - } - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto UnixQPathInfoRetry; - - return rc; -} - -/* xid, tcon, searchName and codepage are input parms, rest are returned */ -int -CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, - const char *searchName, struct cifs_sb_info *cifs_sb, - __u16 *pnetfid, __u16 search_flags, - struct cifs_search_info *psrch_inf, bool msearch) -{ -/* level 257 SMB_ */ - TRANSACTION2_FFIRST_REQ *pSMB = NULL; - TRANSACTION2_FFIRST_RSP *pSMBr = NULL; - T2_FFIRST_RSP_PARMS *parms; - int rc = 0; - int bytes_returned = 0; - int name_len, remap; - __u16 params, byte_count; - struct nls_table *nls_codepage; - - cifs_dbg(FYI, "In FindFirst for %s\n", searchName); - -findFirstRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - nls_codepage = cifs_sb->local_nls; - remap = cifs_remap(cifs_sb); - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); - /* We can not add the asterik earlier in case - it got remapped to 0xF03A as if it were part of the - directory name instead of a wildcard */ - name_len *= 2; - if (msearch) { - pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb); - pSMB->FileName[name_len+1] = 0; - pSMB->FileName[name_len+2] = '*'; - pSMB->FileName[name_len+3] = 0; - name_len += 4; /* now the trailing null */ - /* null terminate just in case */ - pSMB->FileName[name_len] = 0; - pSMB->FileName[name_len+1] = 0; - name_len += 2; - } - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - if (msearch) { - if (WARN_ON_ONCE(name_len > PATH_MAX-2)) - name_len = PATH_MAX-2; - /* overwrite nul byte */ - pSMB->FileName[name_len-1] = CIFS_DIR_SEP(cifs_sb); - pSMB->FileName[name_len] = '*'; - pSMB->FileName[name_len+1] = 0; - name_len += 2; - } - } - - params = 12 + name_len /* includes null */ ; - pSMB->TotalDataCount = 0; /* no EAs */ - pSMB->MaxParameterCount = cpu_to_le16(10); - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes) - - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */ - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST); - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | - ATTR_DIRECTORY); - pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); - pSMB->SearchFlags = cpu_to_le16(search_flags); - pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); - - /* BB what should we set StorageType to? Does it matter? BB */ - pSMB->SearchStorageType = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_ffirst); - - if (rc) {/* BB add logic to retry regular search if Unix search - rejected unexpectedly by server */ - /* BB Add code to handle unsupported level rc */ - cifs_dbg(FYI, "Error in FindFirst = %d\n", rc); - - cifs_buf_release(pSMB); - - /* BB eventually could optimize out free and realloc of buf */ - /* for this case */ - if (rc == -EAGAIN) - goto findFirstRetry; - } else { /* decode response */ - /* BB remember to free buffer if error BB */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc == 0) { - unsigned int lnoff; - - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - psrch_inf->unicode = true; - else - psrch_inf->unicode = false; - - psrch_inf->ntwrk_buf_start = (char *)pSMBr; - psrch_inf->smallBuf = false; - psrch_inf->srch_entries_start = - (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.DataOffset); - parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.ParameterOffset)); - - if (parms->EndofSearch) - psrch_inf->endOfSearch = true; - else - psrch_inf->endOfSearch = false; - - psrch_inf->entries_in_buffer = - le16_to_cpu(parms->SearchCount); - psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + - psrch_inf->entries_in_buffer; - lnoff = le16_to_cpu(parms->LastNameOffset); - if (CIFSMaxBufSize < lnoff) { - cifs_dbg(VFS, "ignoring corrupt resume name\n"); - psrch_inf->last_entry = NULL; - return rc; - } - - psrch_inf->last_entry = psrch_inf->srch_entries_start + - lnoff; - - if (pnetfid) - *pnetfid = parms->SearchHandle; - } else { - cifs_buf_release(pSMB); - } - } - - return rc; -} - -int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, - __u16 searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf) -{ - TRANSACTION2_FNEXT_REQ *pSMB = NULL; - TRANSACTION2_FNEXT_RSP *pSMBr = NULL; - T2_FNEXT_RSP_PARMS *parms; - char *response_data; - int rc = 0; - int bytes_returned; - unsigned int name_len; - __u16 params, byte_count; - - cifs_dbg(FYI, "In FindNext\n"); - - if (psrch_inf->endOfSearch) - return -ENOENT; - - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 14; /* includes 2 bytes of null string, converted to LE below*/ - byte_count = 0; - pSMB->TotalDataCount = 0; /* no EAs */ - pSMB->MaxParameterCount = cpu_to_le16(8); - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); - pSMB->SearchHandle = searchHandle; /* always kept as le */ - pSMB->SearchCount = - cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); - pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); - pSMB->ResumeKey = psrch_inf->resume_key; - pSMB->SearchFlags = cpu_to_le16(search_flags); - - name_len = psrch_inf->resume_name_len; - params += name_len; - if (name_len < PATH_MAX) { - memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); - byte_count += name_len; - /* 14 byte parm len above enough for 2 byte null terminator */ - pSMB->ResumeFileName[name_len] = 0; - pSMB->ResumeFileName[name_len+1] = 0; - } else { - rc = -EINVAL; - goto FNext2_err_exit; - } - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext); - if (rc) { - if (rc == -EBADF) { - psrch_inf->endOfSearch = true; - cifs_buf_release(pSMB); - rc = 0; /* search probably was closed at end of search*/ - } else - cifs_dbg(FYI, "FindNext returned = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc == 0) { - unsigned int lnoff; - - /* BB fixme add lock for file (srch_info) struct here */ - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - psrch_inf->unicode = true; - else - psrch_inf->unicode = false; - response_data = (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.ParameterOffset); - parms = (T2_FNEXT_RSP_PARMS *)response_data; - response_data = (char *)&pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.DataOffset); - if (psrch_inf->smallBuf) - cifs_small_buf_release( - psrch_inf->ntwrk_buf_start); - else - cifs_buf_release(psrch_inf->ntwrk_buf_start); - psrch_inf->srch_entries_start = response_data; - psrch_inf->ntwrk_buf_start = (char *)pSMB; - psrch_inf->smallBuf = false; - if (parms->EndofSearch) - psrch_inf->endOfSearch = true; - else - psrch_inf->endOfSearch = false; - psrch_inf->entries_in_buffer = - le16_to_cpu(parms->SearchCount); - psrch_inf->index_of_last_entry += - psrch_inf->entries_in_buffer; - lnoff = le16_to_cpu(parms->LastNameOffset); - if (CIFSMaxBufSize < lnoff) { - cifs_dbg(VFS, "ignoring corrupt resume name\n"); - psrch_inf->last_entry = NULL; - return rc; - } else - psrch_inf->last_entry = - psrch_inf->srch_entries_start + lnoff; - -/* cifs_dbg(FYI, "fnxt2 entries in buf %d index_of_last %d\n", - psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */ - - /* BB fixme add unlock here */ - } - - } - - /* BB On error, should we leave previous search buf (and count and - last entry fields) intact or free the previous one? */ - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ -FNext2_err_exit: - if (rc != 0) - cifs_buf_release(pSMB); - return rc; -} - -int -CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 searchHandle) -{ - int rc = 0; - FINDCLOSE_REQ *pSMB = NULL; - - cifs_dbg(FYI, "In CIFSSMBFindClose\n"); - rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB); - - /* no sense returning error if session restarted - as file handle has been closed */ - if (rc == -EAGAIN) - return 0; - if (rc) - return rc; - - pSMB->FileID = searchHandle; - pSMB->ByteCount = 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(VFS, "Send error in FindClose = %d\n", rc); - - cifs_stats_inc(&tcon->stats.cifs_stats.num_fclose); - - /* Since session is dead, search handle closed on server already */ - if (rc == -EAGAIN) - rc = 0; - - return rc; -} - -int -CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, __u64 *inode_number, - const struct nls_table *nls_codepage, int remap) -{ - int rc = 0; - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int name_len, bytes_returned; - __u16 params, byte_count; - - cifs_dbg(FYI, "In GetSrvInodeNum for %s\n", search_name); - if (tcon == NULL) - return -ENODEV; - -GetInodeNumberRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - search_name, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, search_name); - } - - params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max data count below from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc); - } else { - /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - /* If rc should we check for EOPNOSUPP and - disable the srvino flag? or in caller? */ - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - struct file_internal_info *pfinfo; - /* BB Do we need a cast or hash here ? */ - if (count < 8) { - cifs_dbg(FYI, "Invalid size ret in QryIntrnlInf\n"); - rc = -EIO; - goto GetInodeNumOut; - } - pfinfo = (struct file_internal_info *) - (data_offset + (char *) &pSMBr->hdr.Protocol); - *inode_number = le64_to_cpu(pfinfo->UniqueId); - } - } -GetInodeNumOut: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto GetInodeNumberRetry; - return rc; -} - -int -CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, - const char *search_name, struct dfs_info3_param **target_nodes, - unsigned int *num_of_nodes, - const struct nls_table *nls_codepage, int remap) -{ -/* TRANS2_GET_DFS_REFERRAL */ - TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; - TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - *num_of_nodes = 0; - *target_nodes = NULL; - - cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name); - if (ses == NULL || ses->tcon_ipc == NULL) - return -ENODEV; - -getDFSRetry: - /* - * Use smb_init_no_reconnect() instead of smb_init() as - * CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus - * causing an infinite recursion. - */ - rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, - (void **)&pSMB, (void **)&pSMBr); - if (rc) - return rc; - - /* server pointer checked in called function, - but should never be null here anyway */ - pSMB->hdr.Mid = get_next_mid(ses->server); - pSMB->hdr.Tid = ses->tcon_ipc->tid; - pSMB->hdr.Uid = ses->Suid; - if (ses->capabilities & CAP_STATUS32) - pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS; - if (ses->capabilities & CAP_DFS) - pSMB->hdr.Flags2 |= SMBFLG2_DFS; - - if (ses->capabilities & CAP_UNICODE) { - pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; - name_len = - cifsConvertToUTF16((__le16 *) pSMB->RequestFileName, - search_name, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ - name_len = copy_path_name(pSMB->RequestFileName, search_name); - } - - if (ses->server->sign) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - pSMB->hdr.Uid = ses->Suid; - - params = 2 /* level */ + name_len /*includes null */ ; - pSMB->TotalDataCount = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->MaxParameterCount = 0; - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL); - byte_count = params + 3 /* pad */ ; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->MaxReferralLevel = cpu_to_le16(3); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc); - goto GetDFSRefExit; - } - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - /* BB Also check if enough total bytes returned? */ - if (rc || get_bcc(&pSMBr->hdr) < 17) { - rc = -EIO; /* bad smb */ - goto GetDFSRefExit; - } - - cifs_dbg(FYI, "Decoding GetDFSRefer response BCC: %d Offset %d\n", - get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset)); - - /* parse returned result into more usable form */ - rc = parse_dfs_referrals(&pSMBr->dfs_data, - le16_to_cpu(pSMBr->t2.DataCount), - num_of_nodes, target_nodes, nls_codepage, - remap, search_name, - (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0); - -GetDFSRefExit: - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto getDFSRetry; - - return rc; -} - -/* Query File System Info such as free space to old servers such as Win 9x */ -int -SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData) -{ -/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_ALLOC_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "OldQFSInfo\n"); -oldQFSInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 18) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - cifs_dbg(FYI, "qfsinf resp BCC: %d Offset %d\n", - get_bcc(&pSMBr->hdr), data_offset); - - response_data = (FILE_SYSTEM_ALLOC_INFO *) - (((char *) &pSMBr->hdr.Protocol) + data_offset); - FSData->f_bsize = - le16_to_cpu(response_data->BytesPerSector) * - le32_to_cpu(response_data-> - SectorsPerAllocationUnit); - /* - * much prefer larger but if server doesn't report - * a valid size than 4K is a reasonable minimum - */ - if (FSData->f_bsize < 512) - FSData->f_bsize = 4096; - - FSData->f_blocks = - le32_to_cpu(response_data->TotalAllocationUnits); - FSData->f_bfree = FSData->f_bavail = - le32_to_cpu(response_data->FreeAllocationUnits); - cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", - (unsigned long long)FSData->f_blocks, - (unsigned long long)FSData->f_bfree, - FSData->f_bsize); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto oldQFSInfoRetry; - - return rc; -} - -int -CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData) -{ -/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSInfo\n"); -QFSInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 24) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - - response_data = - (FILE_SYSTEM_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - FSData->f_bsize = - le32_to_cpu(response_data->BytesPerSector) * - le32_to_cpu(response_data-> - SectorsPerAllocationUnit); - /* - * much prefer larger but if server doesn't report - * a valid size than 4K is a reasonable minimum - */ - if (FSData->f_bsize < 512) - FSData->f_bsize = 4096; - - FSData->f_blocks = - le64_to_cpu(response_data->TotalAllocationUnits); - FSData->f_bfree = FSData->f_bavail = - le64_to_cpu(response_data->FreeAllocationUnits); - cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", - (unsigned long long)FSData->f_blocks, - (unsigned long long)FSData->f_bfree, - FSData->f_bsize); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSInfoRetry; - - return rc; -} - -int -CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon) -{ -/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_ATTRIBUTE_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSAttributeInfo\n"); -QFSAttributeRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 13) { - /* BB also check if enough bytes returned */ - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_ATTRIBUTE_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - memcpy(&tcon->fsAttrInfo, response_data, - sizeof(FILE_SYSTEM_ATTRIBUTE_INFO)); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSAttributeRetry; - - return rc; -} - -int -CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon) -{ -/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_DEVICE_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSDeviceInfo\n"); -QFSDeviceRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); - - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < - sizeof(FILE_SYSTEM_DEVICE_INFO)) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_DEVICE_INFO *) - (((char *) &pSMBr->hdr.Protocol) + - data_offset); - memcpy(&tcon->fsDevInfo, response_data, - sizeof(FILE_SYSTEM_DEVICE_INFO)); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSDeviceRetry; - - return rc; -} - -int -CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon) -{ -/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_UNIX_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSUnixInfo\n"); -QFSUnixRetry: - rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, - (void **) &pSMB, (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(100); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof(struct - smb_com_transaction2_qfsi_req, InformationLevel) - 4); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 13) { - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_UNIX_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - memcpy(&tcon->fsUnixInfo, response_data, - sizeof(FILE_SYSTEM_UNIX_INFO)); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSUnixRetry; - - - return rc; -} - -int -CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, __u64 cap) -{ -/* level 0x200 SMB_SET_CIFS_UNIX_INFO */ - TRANSACTION2_SETFSI_REQ *pSMB = NULL; - TRANSACTION2_SETFSI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count; - - cifs_dbg(FYI, "In SETFSUnixInfo\n"); -SETFSUnixRetry: - /* BB switch to small buf init to save memory */ - rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, - (void **) &pSMB, (void **) &pSMBr); - if (rc) - return rc; - - params = 4; /* 2 bytes zero followed by info level. */ - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) - - 4; - offset = param_offset + params; - - pSMB->MaxParameterCount = cpu_to_le16(4); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(100); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION); - byte_count = 1 /* pad */ + params + 12; - - pSMB->DataCount = cpu_to_le16(12); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - - /* Params. */ - pSMB->FileNum = 0; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO); - - /* Data. */ - pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION); - pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION); - pSMB->ClientUnixCap = cpu_to_le64(cap); - - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc) - rc = -EIO; /* bad smb */ - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto SETFSUnixRetry; - - return rc; -} - - - -int -CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData) -{ -/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_POSIX_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSPosixInfo\n"); -QFSPosixRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(100); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof(struct - smb_com_transaction2_qfsi_req, InformationLevel) - 4); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 13) { - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_POSIX_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - FSData->f_bsize = - le32_to_cpu(response_data->BlockSize); - /* - * much prefer larger but if server doesn't report - * a valid size than 4K is a reasonable minimum - */ - if (FSData->f_bsize < 512) - FSData->f_bsize = 4096; - - FSData->f_blocks = - le64_to_cpu(response_data->TotalBlocks); - FSData->f_bfree = - le64_to_cpu(response_data->BlocksAvail); - if (response_data->UserBlocksAvail == cpu_to_le64(-1)) { - FSData->f_bavail = FSData->f_bfree; - } else { - FSData->f_bavail = - le64_to_cpu(response_data->UserBlocksAvail); - } - if (response_data->TotalFileNodes != cpu_to_le64(-1)) - FSData->f_files = - le64_to_cpu(response_data->TotalFileNodes); - if (response_data->FreeFileNodes != cpu_to_le64(-1)) - FSData->f_ffree = - le64_to_cpu(response_data->FreeFileNodes); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSPosixRetry; - - return rc; -} - - -/* - * We can not use write of zero bytes trick to set file size due to need for - * large file support. Also note that this SetPathInfo is preferred to - * SetFileInfo based method in next routine which is only needed to work around - * a sharing violation bugin Samba which this routine can run into. - */ -int -CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, - const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb, - bool set_allocation) -{ - struct smb_com_transaction2_spi_req *pSMB = NULL; - struct smb_com_transaction2_spi_rsp *pSMBr = NULL; - struct file_end_of_file_info *parm_data; - int name_len; - int rc = 0; - int bytes_returned = 0; - int remap = cifs_remap(cifs_sb); - - __u16 params, byte_count, data_count, param_offset, offset; - - cifs_dbg(FYI, "In SetEOF\n"); -SetEOFRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, - PATH_MAX, cifs_sb->local_nls, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, file_name); - } - params = 6 + name_len; - data_count = sizeof(struct file_end_of_file_info); - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(4100); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - if (set_allocation) { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); - } - - parm_data = - (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + - offset); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + data_count; - pSMB->DataCount = cpu_to_le16(data_count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - parm_data->FileSize = cpu_to_le64(size); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto SetEOFRetry; - - return rc; -} - -int -CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, __u64 size, bool set_allocation) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct file_end_of_file_info *parm_data; - int rc = 0; - __u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n", - (long long)size); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - count = sizeof(struct file_end_of_file_info); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - parm_data = - (struct file_end_of_file_info *)(((char *)pSMB) + offset + 4); - pSMB->DataOffset = cpu_to_le16(offset); - parm_data->FileSize = cpu_to_le64(size); - pSMB->Fid = cfile->fid.netfid; - if (set_allocation) { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); - } - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) { - cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n", - rc); - } - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -/* Some legacy servers such as NT4 require that the file times be set on - an open handle, rather than by pathname - this is awkward due to - potential access conflicts on the open, but it is unavoidable for these - old servers since the only other choice is to go from 100 nanosecond DCE - time and resort to the original setpathinfo level which takes the ancient - DOS time format with 2 second granularity */ -int -CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - char *data_offset; - int rc = 0; - __u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "Set Times (via SetFileInfo)\n"); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - data_offset = (char *)pSMB + - offsetof(struct smb_hdr, Protocol) + offset; - - count = sizeof(FILE_BASIC_INFO); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->Fid = fid; - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); - else - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", - rc); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int -CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, - bool delete_file, __u16 fid, __u32 pid_of_opener) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - char *data_offset; - int rc = 0; - __u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n"); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)(pSMB) + offset + 4; - - count = 1; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->Fid = fid; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - *data_offset = delete_file ? 1 : 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc); - - return rc; -} - -static int -CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - int oplock = 0; - struct cifs_open_parms oparms; - struct cifs_fid fid; - int rc; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = fileName, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - goto out; - - rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid); - CIFSSMBClose(xid, tcon, fid.netfid); -out: - - return rc; -} - -int -CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - int name_len; - int rc = 0; - int bytes_returned = 0; - char *data_offset; - __u16 params, param_offset, offset, byte_count, count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In SetTimes\n"); - -SetTimesRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - - params = 6 + name_len; - count = sizeof(FILE_BASIC_INFO); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); - else - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto SetTimesRetry; - - if (rc == -EOPNOTSUPP) - return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data, - nls_codepage, cifs_sb); - - return rc; -} - -static void -cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, - const struct cifs_unix_set_info_args *args) -{ - u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64; - u64 mode = args->mode; - - if (uid_valid(args->uid)) - uid = from_kuid(&init_user_ns, args->uid); - if (gid_valid(args->gid)) - gid = from_kgid(&init_user_ns, args->gid); - - /* - * Samba server ignores set of file size to zero due to bugs in some - * older clients, but we should be precise - we use SetFileSize to - * set file size and do not want to truncate file size to zero - * accidentally as happened on one Samba server beta by putting - * zero instead of -1 here - */ - data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64); - data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64); - data_offset->LastStatusChange = cpu_to_le64(args->ctime); - data_offset->LastAccessTime = cpu_to_le64(args->atime); - data_offset->LastModificationTime = cpu_to_le64(args->mtime); - data_offset->Uid = cpu_to_le64(uid); - data_offset->Gid = cpu_to_le64(gid); - /* better to leave device as zero when it is */ - data_offset->DevMajor = cpu_to_le64(MAJOR(args->device)); - data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); - data_offset->Permissions = cpu_to_le64(mode); - - if (S_ISREG(mode)) - data_offset->Type = cpu_to_le32(UNIX_FILE); - else if (S_ISDIR(mode)) - data_offset->Type = cpu_to_le32(UNIX_DIR); - else if (S_ISLNK(mode)) - data_offset->Type = cpu_to_le32(UNIX_SYMLINK); - else if (S_ISCHR(mode)) - data_offset->Type = cpu_to_le32(UNIX_CHARDEV); - else if (S_ISBLK(mode)) - data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV); - else if (S_ISFIFO(mode)) - data_offset->Type = cpu_to_le32(UNIX_FIFO); - else if (S_ISSOCK(mode)) - data_offset->Type = cpu_to_le32(UNIX_SOCKET); -} - -int -CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - const struct cifs_unix_set_info_args *args, - u16 fid, u32 pid_of_opener) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - char *data_offset; - int rc = 0; - u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n"); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - data_offset = (char *)pSMB + - offsetof(struct smb_hdr, Protocol) + offset; - - count = sizeof(FILE_UNIX_BASIC_INFO); - - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->Fid = fid; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); - - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", - rc); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int -CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *file_name, - const struct cifs_unix_set_info_args *args, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - int name_len; - int rc = 0; - int bytes_returned = 0; - FILE_UNIX_BASIC_INFO *data_offset; - __u16 params, param_offset, offset, count, byte_count; - - cifs_dbg(FYI, "In SetUID/GID/Mode\n"); -setPermsRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, file_name); - } - - params = 6 + name_len; - count = sizeof(FILE_UNIX_BASIC_INFO); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (FILE_UNIX_BASIC_INFO *)((char *) pSMB + offset + 4); - memset(data_offset, 0, count); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->DataCount = cpu_to_le16(count); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - - cifs_fill_unix_set_info(data_offset, args); - - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto setPermsRetry; - return rc; -} - -#ifdef CONFIG_CIFS_XATTR -/* - * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common - * function used by listxattr and getxattr type calls. When ea_name is set, - * it looks for that attribute name and stuffs that value into the EAData - * buffer. When ea_name is NULL, it stuffs a list of attribute names into the - * buffer. In both cases, the return value is either the length of the - * resulting data or a negative error code. If EAData is a NULL pointer then - * the data isn't copied to it, but the length is returned. - */ -ssize_t -CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, const unsigned char *ea_name, - char *EAData, size_t buf_size, - struct cifs_sb_info *cifs_sb) -{ - /* BB assumes one setup word */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int remap = cifs_remap(cifs_sb); - struct nls_table *nls_codepage = cifs_sb->local_nls; - int rc = 0; - int bytes_returned; - int list_len; - struct fealist *ea_response_data; - struct fea *temp_fea; - char *temp_ptr; - char *end_of_smb; - __u16 params, byte_count, data_offset; - unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; - - cifs_dbg(FYI, "In Query All EAs path %s\n", searchName); -QAllEAsRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - list_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); - list_len++; /* trailing null */ - list_len *= 2; - } else { - list_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc); - goto QAllEAsOut; - } - - - /* BB also check enough total bytes returned */ - /* BB we need to improve the validity checking - of these trans2 responses */ - - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || get_bcc(&pSMBr->hdr) < 4) { - rc = -EIO; /* bad smb */ - goto QAllEAsOut; - } - - /* check that length of list is not more than bcc */ - /* check that each entry does not go beyond length - of list */ - /* check that each element of each entry does not - go beyond end of list */ - /* validate_trans2_offsets() */ - /* BB check if start of smb + data_offset > &bcc+ bcc */ - - data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - ea_response_data = (struct fealist *) - (((char *) &pSMBr->hdr.Protocol) + data_offset); - - list_len = le32_to_cpu(ea_response_data->list_len); - cifs_dbg(FYI, "ea length %d\n", list_len); - if (list_len <= 8) { - cifs_dbg(FYI, "empty EA list returned from server\n"); - /* didn't find the named attribute */ - if (ea_name) - rc = -ENODATA; - goto QAllEAsOut; - } - - /* make sure list_len doesn't go past end of SMB */ - end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr); - if ((char *)ea_response_data + list_len > end_of_smb) { - cifs_dbg(FYI, "EA list appears to go beyond SMB\n"); - rc = -EIO; - goto QAllEAsOut; - } - - /* account for ea list len */ - list_len -= 4; - temp_fea = ea_response_data->list; - temp_ptr = (char *)temp_fea; - while (list_len > 0) { - unsigned int name_len; - __u16 value_len; - - list_len -= 4; - temp_ptr += 4; - /* make sure we can read name_len and value_len */ - if (list_len < 0) { - cifs_dbg(FYI, "EA entry goes beyond length of list\n"); - rc = -EIO; - goto QAllEAsOut; - } - - name_len = temp_fea->name_len; - value_len = le16_to_cpu(temp_fea->value_len); - list_len -= name_len + 1 + value_len; - if (list_len < 0) { - cifs_dbg(FYI, "EA entry goes beyond length of list\n"); - rc = -EIO; - goto QAllEAsOut; - } - - if (ea_name) { - if (ea_name_len == name_len && - memcmp(ea_name, temp_ptr, name_len) == 0) { - temp_ptr += name_len + 1; - rc = value_len; - if (buf_size == 0) - goto QAllEAsOut; - if ((size_t)value_len > buf_size) { - rc = -ERANGE; - goto QAllEAsOut; - } - memcpy(EAData, temp_ptr, value_len); - goto QAllEAsOut; - } - } else { - /* account for prefix user. and trailing null */ - rc += (5 + 1 + name_len); - if (rc < (int) buf_size) { - memcpy(EAData, "user.", 5); - EAData += 5; - memcpy(EAData, temp_ptr, name_len); - EAData += name_len; - /* null terminate name */ - *EAData = 0; - ++EAData; - } else if (buf_size == 0) { - /* skip copy - calc size only */ - } else { - /* stop before overrun buffer */ - rc = -ERANGE; - break; - } - } - temp_ptr += name_len + 1 + value_len; - temp_fea = (struct fea *)temp_ptr; - } - - /* didn't find the named attribute */ - if (ea_name) - rc = -ENODATA; - -QAllEAsOut: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto QAllEAsRetry; - - return (ssize_t)rc; -} - -int -CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const char *ea_name, const void *ea_value, - const __u16 ea_value_len, const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - struct smb_com_transaction2_spi_req *pSMB = NULL; - struct smb_com_transaction2_spi_rsp *pSMBr = NULL; - struct fealist *parm_data; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, byte_count, offset, count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In SetEA\n"); -SetEARetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - - params = 6 + name_len; - - /* done calculating parms using name_len of file name, - now use name_len to calculate length of ea name - we are going to create in the inode xattrs */ - if (ea_name == NULL) - name_len = 0; - else - name_len = strnlen(ea_name, 255); - - count = sizeof(*parm_data) + ea_value_len + name_len; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_EA); - - parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - parm_data->list_len = cpu_to_le32(count); - parm_data->list[0].EA_flags = 0; - /* we checked above that name len is less than 255 */ - parm_data->list[0].name_len = (__u8)name_len; - /* EA names are always ASCII */ - if (ea_name) - strncpy(parm_data->list[0].name, ea_name, name_len); - parm_data->list[0].name[name_len] = 0; - parm_data->list[0].value_len = cpu_to_le16(ea_value_len); - /* caller ensures that ea_value_len is less than 64K but - we need to ensure that it fits within the smb */ - - /*BB add length check to see if it would fit in - negotiated SMB buffer size BB */ - /* if (ea_value_len > buffer_size - 512 (enough for header)) */ - if (ea_value_len) - memcpy(parm_data->list[0].name+name_len+1, - ea_value, ea_value_len); - - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto SetEARetry; - - return rc; -} -#endif diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c deleted file mode 100644 index 935fe198a4ba..000000000000 --- a/fs/cifs/connect.c +++ /dev/null @@ -1,4754 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2011 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "ntlmssp.h" -#include "nterr.h" -#include "rfc1002pdu.h" -#include "fscache.h" -#include "smb2proto.h" -#include "smbdirect.h" -#include "dns_resolve.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#include "fs_context.h" -#include "cifs_swn.h" - -extern mempool_t *cifs_req_poolp; -extern bool disable_legacy_dialects; - -/* FIXME: should these be tunable? */ -#define TLINK_ERROR_EXPIRE (1 * HZ) -#define TLINK_IDLE_EXPIRE (600 * HZ) - -/* Drop the connection to not overload the server */ -#define NUM_STATUS_IO_TIMEOUT 5 - -struct mount_ctx { - struct cifs_sb_info *cifs_sb; - struct smb3_fs_context *fs_ctx; - unsigned int xid; - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; -#ifdef CONFIG_CIFS_DFS_UPCALL - struct cifs_ses *root_ses; - uuid_t mount_id; - char *origin_fullpath, *leaf_fullpath; -#endif -}; - -static int ip_connect(struct TCP_Server_Info *server); -static int generic_ip_connect(struct TCP_Server_Info *server); -static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); -static void cifs_prune_tlinks(struct work_struct *work); - -/* - * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may - * get their ip addresses changed at some point. - * - * This should be called with server->srv_mutex held. - */ -static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) -{ - int rc; - int len; - char *unc, *ipaddr = NULL; - time64_t expiry, now; - unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT; - - if (!server->hostname) - return -EINVAL; - - /* if server hostname isn't populated, there's nothing to do here */ - if (server->hostname[0] == '\0') - return 0; - - len = strlen(server->hostname) + 3; - - unc = kmalloc(len, GFP_KERNEL); - if (!unc) { - cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__); - return -ENOMEM; - } - scnprintf(unc, len, "\\\\%s", server->hostname); - - rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry); - kfree(unc); - - if (rc < 0) { - cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n", - __func__, server->hostname, rc); - goto requeue_resolve; - } - - spin_lock(&server->srv_lock); - rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr, - strlen(ipaddr)); - spin_unlock(&server->srv_lock); - kfree(ipaddr); - - /* rc == 1 means success here */ - if (rc) { - now = ktime_get_real_seconds(); - if (expiry && expiry > now) - /* - * To make sure we don't use the cached entry, retry 1s - * after expiry. - */ - ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1; - } - rc = !rc ? -1 : 0; - -requeue_resolve: - cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n", - __func__, ttl); - mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ)); - - return rc; -} - -static void smb2_query_server_interfaces(struct work_struct *work) -{ - int rc; - struct cifs_tcon *tcon = container_of(work, - struct cifs_tcon, - query_interfaces.work); - - /* - * query server network interfaces, in case they change - */ - rc = SMB3_request_interfaces(0, tcon, false); - if (rc) { - cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", - __func__, rc); - } - - queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, - (SMB_INTERFACE_POLL_INTERVAL * HZ)); -} - -static void cifs_resolve_server(struct work_struct *work) -{ - int rc; - struct TCP_Server_Info *server = container_of(work, - struct TCP_Server_Info, resolve.work); - - cifs_server_lock(server); - - /* - * Resolve the hostname again to make sure that IP address is up-to-date. - */ - rc = reconn_set_ipaddr_from_hostname(server); - if (rc) { - cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", - __func__, rc); - } - - cifs_server_unlock(server); -} - -/* - * Update the tcpStatus for the server. - * This is used to signal the cifsd thread to call cifs_reconnect - * ONLY cifsd thread should call cifs_reconnect. For any other - * thread, use this function - * - * @server: the tcp ses for which reconnect is needed - * @all_channels: if this needs to be done for all channels - */ -void -cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, - bool all_channels) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - int i; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - spin_lock(&pserver->srv_lock); - if (!all_channels) { - pserver->tcpStatus = CifsNeedReconnect; - spin_unlock(&pserver->srv_lock); - return; - } - spin_unlock(&pserver->srv_lock); - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - spin_lock(&ses->chan_lock); - for (i = 0; i < ses->chan_count; i++) { - spin_lock(&ses->chans[i].server->srv_lock); - ses->chans[i].server->tcpStatus = CifsNeedReconnect; - spin_unlock(&ses->chans[i].server->srv_lock); - } - spin_unlock(&ses->chan_lock); - } - spin_unlock(&cifs_tcp_ses_lock); -} - -/* - * Mark all sessions and tcons for reconnect. - * IMPORTANT: make sure that this gets called only from - * cifsd thread. For any other thread, use - * cifs_signal_cifsd_for_reconnect - * - * @server: the tcp ses for which reconnect is needed - * @server needs to be previously set to CifsNeedReconnect. - * @mark_smb_session: whether even sessions need to be marked - */ -void -cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses, *nses; - struct cifs_tcon *tcon; - - /* - * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they - * are not used until reconnected. - */ - cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { - /* check if iface is still active */ - if (!cifs_chan_is_iface_active(ses, server)) - cifs_chan_update_iface(ses, server); - - spin_lock(&ses->chan_lock); - if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) { - spin_unlock(&ses->chan_lock); - continue; - } - - if (mark_smb_session) - CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses); - else - cifs_chan_set_need_reconnect(ses, server); - - cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", - __func__, ses->chans_need_reconnect); - - /* If all channels need reconnect, then tcon needs reconnect */ - if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { - spin_unlock(&ses->chan_lock); - continue; - } - spin_unlock(&ses->chan_lock); - - spin_lock(&ses->ses_lock); - ses->ses_status = SES_NEED_RECON; - spin_unlock(&ses->ses_lock); - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - tcon->need_reconnect = true; - spin_lock(&tcon->tc_lock); - tcon->status = TID_NEED_RECON; - spin_unlock(&tcon->tc_lock); - } - if (ses->tcon_ipc) { - ses->tcon_ipc->need_reconnect = true; - spin_lock(&ses->tcon_ipc->tc_lock); - ses->tcon_ipc->status = TID_NEED_RECON; - spin_unlock(&ses->tcon_ipc->tc_lock); - } - } - spin_unlock(&cifs_tcp_ses_lock); -} - -static void -cifs_abort_connection(struct TCP_Server_Info *server) -{ - struct mid_q_entry *mid, *nmid; - struct list_head retry_list; - - server->maxBuf = 0; - server->max_read = 0; - - /* do not want to be sending data on a socket we are freeing */ - cifs_dbg(FYI, "%s: tearing down socket\n", __func__); - cifs_server_lock(server); - if (server->ssocket) { - cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, - server->ssocket->flags); - kernel_sock_shutdown(server->ssocket, SHUT_WR); - cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state, - server->ssocket->flags); - sock_release(server->ssocket); - server->ssocket = NULL; - } - server->sequence_number = 0; - server->session_estab = false; - kfree_sensitive(server->session_key.response); - server->session_key.response = NULL; - server->session_key.len = 0; - server->lstrp = jiffies; - - /* mark submitted MIDs for retry and issue callback */ - INIT_LIST_HEAD(&retry_list); - cifs_dbg(FYI, "%s: moving mids to private list\n", __func__); - spin_lock(&server->mid_lock); - list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) { - kref_get(&mid->refcount); - if (mid->mid_state == MID_REQUEST_SUBMITTED) - mid->mid_state = MID_RETRY_NEEDED; - list_move(&mid->qhead, &retry_list); - mid->mid_flags |= MID_DELETED; - } - spin_unlock(&server->mid_lock); - cifs_server_unlock(server); - - cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); - list_for_each_entry_safe(mid, nmid, &retry_list, qhead) { - list_del_init(&mid->qhead); - mid->callback(mid); - release_mid(mid); - } - - if (cifs_rdma_enabled(server)) { - cifs_server_lock(server); - smbd_destroy(server); - cifs_server_unlock(server); - } -} - -static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets) -{ - spin_lock(&server->srv_lock); - server->nr_targets = num_targets; - if (server->tcpStatus == CifsExiting) { - /* the demux thread will exit normally next time through the loop */ - spin_unlock(&server->srv_lock); - wake_up(&server->response_q); - return false; - } - - cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); - trace_smb3_reconnect(server->CurrentMid, server->conn_id, - server->hostname); - server->tcpStatus = CifsNeedReconnect; - - spin_unlock(&server->srv_lock); - return true; -} - -/* - * cifs tcp session reconnection - * - * mark tcp session as reconnecting so temporarily locked - * mark all smb sessions as reconnecting for tcp session - * reconnect tcp session - * wake up waiters on reconnection? - (not needed currently) - * - * if mark_smb_session is passed as true, unconditionally mark - * the smb session (and tcon) for reconnect as well. This value - * doesn't really matter for non-multichannel scenario. - * - */ -static int __cifs_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session) -{ - int rc = 0; - - if (!cifs_tcp_ses_needs_reconnect(server, 1)) - return 0; - - cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session); - - cifs_abort_connection(server); - - do { - try_to_freeze(); - cifs_server_lock(server); - - if (!cifs_swn_set_server_dstaddr(server)) { - /* resolve the hostname again to make sure that IP address is up-to-date */ - rc = reconn_set_ipaddr_from_hostname(server); - cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); - } - - if (cifs_rdma_enabled(server)) - rc = smbd_reconnect(server); - else - rc = generic_ip_connect(server); - if (rc) { - cifs_server_unlock(server); - cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); - msleep(3000); - } else { - atomic_inc(&tcpSesReconnectCount); - set_credits(server, 1); - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsExiting) - server->tcpStatus = CifsNeedNegotiate; - spin_unlock(&server->srv_lock); - cifs_swn_reset_server_dstaddr(server); - cifs_server_unlock(server); - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - } - } while (server->tcpStatus == CifsNeedReconnect); - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedNegotiate) - mod_delayed_work(cifsiod_wq, &server->echo, 0); - spin_unlock(&server->srv_lock); - - wake_up(&server->response_q); - return rc; -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target) -{ - int rc; - char *hostname; - - if (!cifs_swn_set_server_dstaddr(server)) { - if (server->hostname != target) { - hostname = extract_hostname(target); - if (!IS_ERR(hostname)) { - spin_lock(&server->srv_lock); - kfree(server->hostname); - server->hostname = hostname; - spin_unlock(&server->srv_lock); - } else { - cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", - __func__, PTR_ERR(hostname)); - cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__, - server->hostname); - } - } - /* resolve the hostname again to make sure that IP address is up-to-date. */ - rc = reconn_set_ipaddr_from_hostname(server); - cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); - } - /* Reconnect the socket */ - if (cifs_rdma_enabled(server)) - rc = smbd_reconnect(server); - else - rc = generic_ip_connect(server); - - return rc; -} - -static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl, - struct dfs_cache_tgt_iterator **target_hint) -{ - int rc; - struct dfs_cache_tgt_iterator *tit; - - *target_hint = NULL; - - /* If dfs target list is empty, then reconnect to last server */ - tit = dfs_cache_get_tgt_iterator(tl); - if (!tit) - return __reconnect_target_unlocked(server, server->hostname); - - /* Otherwise, try every dfs target in @tl */ - for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { - rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit)); - if (!rc) { - *target_hint = tit; - break; - } - } - return rc; -} - -static int reconnect_dfs_server(struct TCP_Server_Info *server) -{ - int rc = 0; - const char *refpath = server->current_fullpath + 1; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - struct dfs_cache_tgt_iterator *target_hint = NULL; - int num_targets = 0; - - /* - * Determine the number of dfs targets the referral path in @cifs_sb resolves to. - * - * smb2_reconnect() needs to know how long it should wait based upon the number of dfs - * targets (server->nr_targets). It's also possible that the cached referral was cleared - * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after - * refreshing the referral, so, in this case, default it to 1. - */ - if (!dfs_cache_noreq_find(refpath, NULL, &tl)) - num_targets = dfs_cache_get_nr_tgts(&tl); - if (!num_targets) - num_targets = 1; - - if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) - return 0; - - /* - * Unconditionally mark all sessions & tcons for reconnect as we might be connecting to a - * different server or share during failover. It could be improved by adding some logic to - * only do that in case it connects to a different server or share, though. - */ - cifs_mark_tcp_ses_conns_for_reconnect(server, true); - - cifs_abort_connection(server); - - do { - try_to_freeze(); - cifs_server_lock(server); - - rc = reconnect_target_unlocked(server, &tl, &target_hint); - if (rc) { - /* Failed to reconnect socket */ - cifs_server_unlock(server); - cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); - msleep(3000); - continue; - } - /* - * Socket was created. Update tcp session status to CifsNeedNegotiate so that a - * process waiting for reconnect will know it needs to re-establish session and tcon - * through the reconnected target server. - */ - atomic_inc(&tcpSesReconnectCount); - set_credits(server, 1); - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsExiting) - server->tcpStatus = CifsNeedNegotiate; - spin_unlock(&server->srv_lock); - cifs_swn_reset_server_dstaddr(server); - cifs_server_unlock(server); - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - } while (server->tcpStatus == CifsNeedReconnect); - - if (target_hint) - dfs_cache_noreq_update_tgthint(refpath, target_hint); - - dfs_cache_free_tgts(&tl); - - /* Need to set up echo worker again once connection has been established */ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedNegotiate) - mod_delayed_work(cifsiod_wq, &server->echo, 0); - spin_unlock(&server->srv_lock); - - wake_up(&server->response_q); - return rc; -} - -int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) -{ - /* If tcp session is not an dfs connection, then reconnect to last target server */ - spin_lock(&server->srv_lock); - if (!server->is_dfs_conn) { - spin_unlock(&server->srv_lock); - return __cifs_reconnect(server, mark_smb_session); - } - spin_unlock(&server->srv_lock); - - mutex_lock(&server->refpath_lock); - if (!server->origin_fullpath || !server->leaf_fullpath) { - mutex_unlock(&server->refpath_lock); - return __cifs_reconnect(server, mark_smb_session); - } - mutex_unlock(&server->refpath_lock); - - return reconnect_dfs_server(server); -} -#else -int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) -{ - return __cifs_reconnect(server, mark_smb_session); -} -#endif - -static void -cifs_echo_request(struct work_struct *work) -{ - int rc; - struct TCP_Server_Info *server = container_of(work, - struct TCP_Server_Info, echo.work); - - /* - * We cannot send an echo if it is disabled. - * Also, no need to ping if we got a response recently. - */ - - if (server->tcpStatus == CifsNeedReconnect || - server->tcpStatus == CifsExiting || - server->tcpStatus == CifsNew || - (server->ops->can_echo && !server->ops->can_echo(server)) || - time_before(jiffies, server->lstrp + server->echo_interval - HZ)) - goto requeue_echo; - - rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS; - cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc); - - /* Check witness registrations */ - cifs_swn_check(); - -requeue_echo: - queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval); -} - -static bool -allocate_buffers(struct TCP_Server_Info *server) -{ - if (!server->bigbuf) { - server->bigbuf = (char *)cifs_buf_get(); - if (!server->bigbuf) { - cifs_server_dbg(VFS, "No memory for large SMB response\n"); - msleep(3000); - /* retry will check if exiting */ - return false; - } - } else if (server->large_buf) { - /* we are reusing a dirty large buf, clear its start */ - memset(server->bigbuf, 0, HEADER_SIZE(server)); - } - - if (!server->smallbuf) { - server->smallbuf = (char *)cifs_small_buf_get(); - if (!server->smallbuf) { - cifs_server_dbg(VFS, "No memory for SMB response\n"); - msleep(1000); - /* retry will check if exiting */ - return false; - } - /* beginning of smb buffer is cleared in our buf_get */ - } else { - /* if existing small buf clear beginning */ - memset(server->smallbuf, 0, HEADER_SIZE(server)); - } - - return true; -} - -static bool -server_unresponsive(struct TCP_Server_Info *server) -{ - /* - * We need to wait 3 echo intervals to make sure we handle such - * situations right: - * 1s client sends a normal SMB request - * 2s client gets a response - * 30s echo workqueue job pops, and decides we got a response recently - * and don't need to send another - * ... - * 65s kernel_recvmsg times out, and we see that we haven't gotten - * a response in >60s. - */ - spin_lock(&server->srv_lock); - if ((server->tcpStatus == CifsGood || - server->tcpStatus == CifsNeedNegotiate) && - (!server->ops->can_echo || server->ops->can_echo(server)) && - time_after(jiffies, server->lstrp + 3 * server->echo_interval)) { - spin_unlock(&server->srv_lock); - cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n", - (3 * server->echo_interval) / HZ); - cifs_reconnect(server, false); - return true; - } - spin_unlock(&server->srv_lock); - - return false; -} - -static inline bool -zero_credits(struct TCP_Server_Info *server) -{ - int val; - - spin_lock(&server->req_lock); - val = server->credits + server->echo_credits + server->oplock_credits; - if (server->in_flight == 0 && val == 0) { - spin_unlock(&server->req_lock); - return true; - } - spin_unlock(&server->req_lock); - return false; -} - -static int -cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) -{ - int length = 0; - int total_read; - - for (total_read = 0; msg_data_left(smb_msg); total_read += length) { - try_to_freeze(); - - /* reconnect if no credits and no requests in flight */ - if (zero_credits(server)) { - cifs_reconnect(server, false); - return -ECONNABORTED; - } - - if (server_unresponsive(server)) - return -ECONNABORTED; - if (cifs_rdma_enabled(server) && server->smbd_conn) - length = smbd_recv(server->smbd_conn, smb_msg); - else - length = sock_recvmsg(server->ssocket, smb_msg, 0); - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ESHUTDOWN; - } - - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - cifs_reconnect(server, false); - return -ECONNABORTED; - } - spin_unlock(&server->srv_lock); - - if (length == -ERESTARTSYS || - length == -EAGAIN || - length == -EINTR) { - /* - * Minimum sleep to prevent looping, allowing socket - * to clear and app threads to set tcpStatus - * CifsNeedReconnect if server hung. - */ - usleep_range(1000, 2000); - length = 0; - continue; - } - - if (length <= 0) { - cifs_dbg(FYI, "Received no data or error: %d\n", length); - cifs_reconnect(server, false); - return -ECONNABORTED; - } - } - return total_read; -} - -int -cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, - unsigned int to_read) -{ - struct msghdr smb_msg = {}; - struct kvec iov = {.iov_base = buf, .iov_len = to_read}; - iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read); - - return cifs_readv_from_socket(server, &smb_msg); -} - -ssize_t -cifs_discard_from_socket(struct TCP_Server_Info *server, size_t to_read) -{ - struct msghdr smb_msg = {}; - - /* - * iov_iter_discard already sets smb_msg.type and count and iov_offset - * and cifs_readv_from_socket sets msg_control and msg_controllen - * so little to initialize in struct msghdr - */ - iov_iter_discard(&smb_msg.msg_iter, ITER_DEST, to_read); - - return cifs_readv_from_socket(server, &smb_msg); -} - -int -cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page, - unsigned int page_offset, unsigned int to_read) -{ - struct msghdr smb_msg = {}; - struct bio_vec bv = { - .bv_page = page, .bv_len = to_read, .bv_offset = page_offset}; - iov_iter_bvec(&smb_msg.msg_iter, ITER_DEST, &bv, 1, to_read); - return cifs_readv_from_socket(server, &smb_msg); -} - -static bool -is_smb_response(struct TCP_Server_Info *server, unsigned char type) -{ - /* - * The first byte big endian of the length field, - * is actually not part of the length but the type - * with the most common, zero, as regular data. - */ - switch (type) { - case RFC1002_SESSION_MESSAGE: - /* Regular SMB response */ - return true; - case RFC1002_SESSION_KEEP_ALIVE: - cifs_dbg(FYI, "RFC 1002 session keep alive\n"); - break; - case RFC1002_POSITIVE_SESSION_RESPONSE: - cifs_dbg(FYI, "RFC 1002 positive session response\n"); - break; - case RFC1002_NEGATIVE_SESSION_RESPONSE: - /* - * We get this from Windows 98 instead of an error on - * SMB negprot response. - */ - cifs_dbg(FYI, "RFC 1002 negative session response\n"); - /* give server a second to clean up */ - msleep(1000); - /* - * Always try 445 first on reconnect since we get NACK - * on some if we ever connected to port 139 (the NACK - * is since we do not begin with RFC1001 session - * initialize frame). - */ - cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT); - cifs_reconnect(server, true); - break; - default: - cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); - cifs_reconnect(server, true); - } - - return false; -} - -void -dequeue_mid(struct mid_q_entry *mid, bool malformed) -{ -#ifdef CONFIG_CIFS_STATS2 - mid->when_received = jiffies; -#endif - spin_lock(&mid->server->mid_lock); - if (!malformed) - mid->mid_state = MID_RESPONSE_RECEIVED; - else - mid->mid_state = MID_RESPONSE_MALFORMED; - /* - * Trying to handle/dequeue a mid after the send_recv() - * function has finished processing it is a bug. - */ - if (mid->mid_flags & MID_DELETED) { - spin_unlock(&mid->server->mid_lock); - pr_warn_once("trying to dequeue a deleted mid\n"); - } else { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - spin_unlock(&mid->server->mid_lock); - } -} - -static unsigned int -smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; - - /* - * SMB1 does not use credits. - */ - if (is_smb1(server)) - return 0; - - return le16_to_cpu(shdr->CreditRequest); -} - -static void -handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, - char *buf, int malformed) -{ - if (server->ops->check_trans2 && - server->ops->check_trans2(mid, server, buf, malformed)) - return; - mid->credits_received = smb2_get_credits_from_hdr(buf, server); - mid->resp_buf = buf; - mid->large_buf = server->large_buf; - /* Was previous buf put in mpx struct for multi-rsp? */ - if (!mid->multiRsp) { - /* smb buffer will be freed by user thread */ - if (server->large_buf) - server->bigbuf = NULL; - else - server->smallbuf = NULL; - } - dequeue_mid(mid, malformed); -} - -int -cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) -{ - bool srv_sign_required = server->sec_mode & server->vals->signing_required; - bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled; - bool mnt_sign_enabled; - - /* - * Is signing required by mnt options? If not then check - * global_secflags to see if it is there. - */ - if (!mnt_sign_required) - mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) == - CIFSSEC_MUST_SIGN); - - /* - * If signing is required then it's automatically enabled too, - * otherwise, check to see if the secflags allow it. - */ - mnt_sign_enabled = mnt_sign_required ? mnt_sign_required : - (global_secflags & CIFSSEC_MAY_SIGN); - - /* If server requires signing, does client allow it? */ - if (srv_sign_required) { - if (!mnt_sign_enabled) { - cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!\n"); - return -EOPNOTSUPP; - } - server->sign = true; - } - - /* If client requires signing, does server allow it? */ - if (mnt_sign_required) { - if (!srv_sign_enabled) { - cifs_dbg(VFS, "Server does not support signing!\n"); - return -EOPNOTSUPP; - } - server->sign = true; - } - - if (cifs_rdma_enabled(server) && server->sign) - cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled\n"); - - return 0; -} - - -static void clean_demultiplex_info(struct TCP_Server_Info *server) -{ - int length; - - /* take it off the list, if it's not already */ - spin_lock(&server->srv_lock); - list_del_init(&server->tcp_ses_list); - spin_unlock(&server->srv_lock); - - cancel_delayed_work_sync(&server->echo); - cancel_delayed_work_sync(&server->resolve); - - spin_lock(&server->srv_lock); - server->tcpStatus = CifsExiting; - spin_unlock(&server->srv_lock); - wake_up_all(&server->response_q); - - /* check if we have blocked requests that need to free */ - spin_lock(&server->req_lock); - if (server->credits <= 0) - server->credits = 1; - spin_unlock(&server->req_lock); - /* - * Although there should not be any requests blocked on this queue it - * can not hurt to be paranoid and try to wake up requests that may - * haven been blocked when more than 50 at time were on the wire to the - * same server - they now will see the session is in exit state and get - * out of SendReceive. - */ - wake_up_all(&server->request_q); - /* give those requests time to exit */ - msleep(125); - if (cifs_rdma_enabled(server)) - smbd_destroy(server); - if (server->ssocket) { - sock_release(server->ssocket); - server->ssocket = NULL; - } - - if (!list_empty(&server->pending_mid_q)) { - struct list_head dispose_list; - struct mid_q_entry *mid_entry; - struct list_head *tmp, *tmp2; - - INIT_LIST_HEAD(&dispose_list); - spin_lock(&server->mid_lock); - list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { - mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid); - kref_get(&mid_entry->refcount); - mid_entry->mid_state = MID_SHUTDOWN; - list_move(&mid_entry->qhead, &dispose_list); - mid_entry->mid_flags |= MID_DELETED; - } - spin_unlock(&server->mid_lock); - - /* now walk dispose list and issue callbacks */ - list_for_each_safe(tmp, tmp2, &dispose_list) { - mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid); - list_del_init(&mid_entry->qhead); - mid_entry->callback(mid_entry); - release_mid(mid_entry); - } - /* 1/8th of sec is more than enough time for them to exit */ - msleep(125); - } - - if (!list_empty(&server->pending_mid_q)) { - /* - * mpx threads have not exited yet give them at least the smb - * send timeout time for long ops. - * - * Due to delays on oplock break requests, we need to wait at - * least 45 seconds before giving up on a request getting a - * response and going ahead and killing cifsd. - */ - cifs_dbg(FYI, "Wait for exit from demultiplex thread\n"); - msleep(46000); - /* - * If threads still have not exited they are probably never - * coming home not much else we can do but free the memory. - */ - } - -#ifdef CONFIG_CIFS_DFS_UPCALL - kfree(server->origin_fullpath); - kfree(server->leaf_fullpath); -#endif - kfree(server); - - length = atomic_dec_return(&tcpSesAllocCount); - if (length > 0) - mempool_resize(cifs_req_poolp, length + cifs_min_rcv); -} - -static int -standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - int length; - char *buf = server->smallbuf; - unsigned int pdu_length = server->pdu_size; - - /* make sure this will fit in a large buffer */ - if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - - HEADER_PREAMBLE_SIZE(server)) { - cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); - cifs_reconnect(server, true); - return -ECONNABORTED; - } - - /* switch to large buffer if too big for a small one */ - if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { - server->large_buf = true; - memcpy(server->bigbuf, buf, server->total_read); - buf = server->bigbuf; - } - - /* now read the rest */ - length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, - pdu_length - MID_HEADER_SIZE(server)); - - if (length < 0) - return length; - server->total_read += length; - - dump_smb(buf, server->total_read); - - return cifs_handle_standard(server, mid); -} - -int -cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - char *buf = server->large_buf ? server->bigbuf : server->smallbuf; - int rc; - - /* - * We know that we received enough to get to the MID as we - * checked the pdu_length earlier. Now check to see - * if the rest of the header is OK. - * - * 48 bytes is enough to display the header and a little bit - * into the payload for debugging purposes. - */ - rc = server->ops->check_message(buf, server->total_read, server); - if (rc) - cifs_dump_mem("Bad SMB: ", buf, - min_t(unsigned int, server->total_read, 48)); - - if (server->ops->is_session_expired && - server->ops->is_session_expired(buf)) { - cifs_reconnect(server, true); - return -1; - } - - if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server)) - return -1; - - if (!mid) - return rc; - - handle_mid(mid, server, buf, rc); - return 0; -} - -static void -smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; - int scredits, in_flight; - - /* - * SMB1 does not use credits. - */ - if (is_smb1(server)) - return; - - if (shdr->CreditRequest) { - spin_lock(&server->req_lock); - server->credits += le16_to_cpu(shdr->CreditRequest); - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - trace_smb3_hdr_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - le16_to_cpu(shdr->CreditRequest), in_flight); - cifs_server_dbg(FYI, "%s: added %u credits total=%d\n", - __func__, le16_to_cpu(shdr->CreditRequest), - scredits); - } -} - - -static int -cifs_demultiplex_thread(void *p) -{ - int i, num_mids, length; - struct TCP_Server_Info *server = p; - unsigned int pdu_length; - unsigned int next_offset; - char *buf = NULL; - struct task_struct *task_to_wake = NULL; - struct mid_q_entry *mids[MAX_COMPOUND]; - char *bufs[MAX_COMPOUND]; - unsigned int noreclaim_flag, num_io_timeout = 0; - - noreclaim_flag = memalloc_noreclaim_save(); - cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); - - length = atomic_inc_return(&tcpSesAllocCount); - if (length > 1) - mempool_resize(cifs_req_poolp, length + cifs_min_rcv); - - set_freezable(); - allow_kernel_signal(SIGKILL); - while (server->tcpStatus != CifsExiting) { - if (try_to_freeze()) - continue; - - if (!allocate_buffers(server)) - continue; - - server->large_buf = false; - buf = server->smallbuf; - pdu_length = 4; /* enough to get RFC1001 header */ - - length = cifs_read_from_socket(server, buf, pdu_length); - if (length < 0) - continue; - - if (is_smb1(server)) - server->total_read = length; - else - server->total_read = 0; - - /* - * The right amount was read from socket - 4 bytes, - * so we can now interpret the length field. - */ - pdu_length = get_rfc1002_length(buf); - - cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); - if (!is_smb_response(server, buf[0])) - continue; -next_pdu: - server->pdu_size = pdu_length; - - /* make sure we have enough to get to the MID */ - if (server->pdu_size < MID_HEADER_SIZE(server)) { - cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", - server->pdu_size); - cifs_reconnect(server, true); - continue; - } - - /* read down to the MID */ - length = cifs_read_from_socket(server, - buf + HEADER_PREAMBLE_SIZE(server), - MID_HEADER_SIZE(server)); - if (length < 0) - continue; - server->total_read += length; - - if (server->ops->next_header) { - next_offset = server->ops->next_header(buf); - if (next_offset) - server->pdu_size = next_offset; - } - - memset(mids, 0, sizeof(mids)); - memset(bufs, 0, sizeof(bufs)); - num_mids = 0; - - if (server->ops->is_transform_hdr && - server->ops->receive_transform && - server->ops->is_transform_hdr(buf)) { - length = server->ops->receive_transform(server, - mids, - bufs, - &num_mids); - } else { - mids[0] = server->ops->find_mid(server, buf); - bufs[0] = buf; - num_mids = 1; - - if (!mids[0] || !mids[0]->receive) - length = standard_receive3(server, mids[0]); - else - length = mids[0]->receive(server, mids[0]); - } - - if (length < 0) { - for (i = 0; i < num_mids; i++) - if (mids[i]) - release_mid(mids[i]); - continue; - } - - if (server->ops->is_status_io_timeout && - server->ops->is_status_io_timeout(buf)) { - num_io_timeout++; - if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) { - cifs_reconnect(server, false); - num_io_timeout = 0; - continue; - } - } - - server->lstrp = jiffies; - - for (i = 0; i < num_mids; i++) { - if (mids[i] != NULL) { - mids[i]->resp_buf_size = server->pdu_size; - - if (bufs[i] && server->ops->is_network_name_deleted) - server->ops->is_network_name_deleted(bufs[i], - server); - - if (!mids[i]->multiRsp || mids[i]->multiEnd) - mids[i]->callback(mids[i]); - - release_mid(mids[i]); - } else if (server->ops->is_oplock_break && - server->ops->is_oplock_break(bufs[i], - server)) { - smb2_add_credits_from_hdr(bufs[i], server); - cifs_dbg(FYI, "Received oplock break\n"); - } else { - cifs_server_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", - atomic_read(&mid_count)); - cifs_dump_mem("Received Data is: ", bufs[i], - HEADER_SIZE(server)); - smb2_add_credits_from_hdr(bufs[i], server); -#ifdef CONFIG_CIFS_DEBUG2 - if (server->ops->dump_detail) - server->ops->dump_detail(bufs[i], - server); - cifs_dump_mids(server); -#endif /* CIFS_DEBUG2 */ - } - } - - if (pdu_length > server->pdu_size) { - if (!allocate_buffers(server)) - continue; - pdu_length -= server->pdu_size; - server->total_read = 0; - server->large_buf = false; - buf = server->smallbuf; - goto next_pdu; - } - } /* end while !EXITING */ - - /* buffer usually freed in free_mid - need to free it here on exit */ - cifs_buf_release(server->bigbuf); - if (server->smallbuf) /* no sense logging a debug message if NULL */ - cifs_small_buf_release(server->smallbuf); - - task_to_wake = xchg(&server->tsk, NULL); - clean_demultiplex_info(server); - - /* if server->tsk was NULL then wait for a signal before exiting */ - if (!task_to_wake) { - set_current_state(TASK_INTERRUPTIBLE); - while (!signal_pending(current)) { - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - } - - memalloc_noreclaim_restore(noreclaim_flag); - module_put_and_kthread_exit(0); -} - -/* - * Returns true if srcaddr isn't specified and rhs isn't specified, or - * if srcaddr is specified and matches the IP address of the rhs argument - */ -bool -cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs) -{ - switch (srcaddr->sa_family) { - case AF_UNSPEC: - return (rhs->sa_family == AF_UNSPEC); - case AF_INET: { - struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; - struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; - return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); - } - case AF_INET6: { - struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; - struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs; - return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); - } - default: - WARN_ON(1); - return false; /* don't expect to be here */ - } -} - -/* - * If no port is specified in addr structure, we try to match with 445 port - * and if it fails - with 139 ports. It should be called only if address - * families of server and addr are equal. - */ -static bool -match_port(struct TCP_Server_Info *server, struct sockaddr *addr) -{ - __be16 port, *sport; - - /* SMBDirect manages its own ports, don't match it here */ - if (server->rdma) - return true; - - switch (addr->sa_family) { - case AF_INET: - sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port; - port = ((struct sockaddr_in *) addr)->sin_port; - break; - case AF_INET6: - sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port; - port = ((struct sockaddr_in6 *) addr)->sin6_port; - break; - default: - WARN_ON(1); - return false; - } - - if (!port) { - port = htons(CIFS_PORT); - if (port == *sport) - return true; - - port = htons(RFC1001_PORT); - } - - return port == *sport; -} - -static bool -match_address(struct TCP_Server_Info *server, struct sockaddr *addr, - struct sockaddr *srcaddr) -{ - switch (addr->sa_family) { - case AF_INET: { - struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; - struct sockaddr_in *srv_addr4 = - (struct sockaddr_in *)&server->dstaddr; - - if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr) - return false; - break; - } - case AF_INET6: { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; - struct sockaddr_in6 *srv_addr6 = - (struct sockaddr_in6 *)&server->dstaddr; - - if (!ipv6_addr_equal(&addr6->sin6_addr, - &srv_addr6->sin6_addr)) - return false; - if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id) - return false; - break; - } - default: - WARN_ON(1); - return false; /* don't expect to be here */ - } - - if (!cifs_match_ipaddr(srcaddr, (struct sockaddr *)&server->srcaddr)) - return false; - - return true; -} - -static bool -match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - /* - * The select_sectype function should either return the ctx->sectype - * that was specified, or "Unspecified" if that sectype was not - * compatible with the given NEGOTIATE request. - */ - if (server->ops->select_sectype(server, ctx->sectype) - == Unspecified) - return false; - - /* - * Now check if signing mode is acceptable. No need to check - * global_secflags at this point since if MUST_SIGN is set then - * the server->sign had better be too. - */ - if (ctx->sign && !server->sign) - return false; - - return true; -} - -/* this function must be called with srv_lock held */ -static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; - - lockdep_assert_held(&server->srv_lock); - - if (ctx->nosharesock) - return 0; - - /* this server does not share socket */ - if (server->nosharesock) - return 0; - - /* If multidialect negotiation see if existing sessions match one */ - if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { - if (server->vals->protocol_id < SMB30_PROT_ID) - return 0; - } else if (strcmp(ctx->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - if (server->vals->protocol_id < SMB21_PROT_ID) - return 0; - } else if ((server->vals != ctx->vals) || (server->ops != ctx->ops)) - return 0; - - if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) - return 0; - - if (strcasecmp(server->hostname, ctx->server_hostname)) - return 0; - - if (!match_address(server, addr, - (struct sockaddr *)&ctx->srcaddr)) - return 0; - - if (!match_port(server, addr)) - return 0; - - if (!match_security(server, ctx)) - return 0; - - if (server->echo_interval != ctx->echo_interval * HZ) - return 0; - - if (server->rdma != ctx->rdma) - return 0; - - if (server->ignore_signature != ctx->ignore_signature) - return 0; - - if (server->min_offload != ctx->min_offload) - return 0; - - return 1; -} - -struct TCP_Server_Info * -cifs_find_tcp_session(struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - spin_lock(&server->srv_lock); -#ifdef CONFIG_CIFS_DFS_UPCALL - /* - * DFS failover implementation in cifs_reconnect() requires unique tcp sessions for - * DFS connections to do failover properly, so avoid sharing them with regular - * shares or even links that may connect to same server but having completely - * different failover targets. - */ - if (server->is_dfs_conn) { - spin_unlock(&server->srv_lock); - continue; - } -#endif - /* - * Skip ses channels since they're only handled in lower layers - * (e.g. cifs_send_recv). - */ - if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) { - spin_unlock(&server->srv_lock); - continue; - } - spin_unlock(&server->srv_lock); - - ++server->srv_count; - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "Existing tcp session with server found\n"); - return server; - } - spin_unlock(&cifs_tcp_ses_lock); - return NULL; -} - -void -cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) -{ - struct task_struct *task; - - spin_lock(&cifs_tcp_ses_lock); - if (--server->srv_count > 0) { - spin_unlock(&cifs_tcp_ses_lock); - return; - } - - /* srv_count can never go negative */ - WARN_ON(server->srv_count < 0); - - put_net(cifs_net_ns(server)); - - list_del_init(&server->tcp_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - /* For secondary channels, we pick up ref-count on the primary server */ - if (CIFS_SERVER_IS_CHAN(server)) - cifs_put_tcp_session(server->primary_server, from_reconnect); - - cancel_delayed_work_sync(&server->echo); - cancel_delayed_work_sync(&server->resolve); - - if (from_reconnect) - /* - * Avoid deadlock here: reconnect work calls - * cifs_put_tcp_session() at its end. Need to be sure - * that reconnect work does nothing with server pointer after - * that step. - */ - cancel_delayed_work(&server->reconnect); - else - cancel_delayed_work_sync(&server->reconnect); - - spin_lock(&server->srv_lock); - server->tcpStatus = CifsExiting; - spin_unlock(&server->srv_lock); - - cifs_crypto_secmech_release(server); - - kfree_sensitive(server->session_key.response); - server->session_key.response = NULL; - server->session_key.len = 0; - kfree(server->hostname); - server->hostname = NULL; - - task = xchg(&server->tsk, NULL); - if (task) - send_sig(SIGKILL, task, 1); -} - -struct TCP_Server_Info * -cifs_get_tcp_session(struct smb3_fs_context *ctx, - struct TCP_Server_Info *primary_server) -{ - struct TCP_Server_Info *tcp_ses = NULL; - int rc; - - cifs_dbg(FYI, "UNC: %s\n", ctx->UNC); - - /* see if we already have a matching tcp_ses */ - tcp_ses = cifs_find_tcp_session(ctx); - if (tcp_ses) - return tcp_ses; - - tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); - if (!tcp_ses) { - rc = -ENOMEM; - goto out_err; - } - - tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL); - if (!tcp_ses->hostname) { - rc = -ENOMEM; - goto out_err; - } - - if (ctx->nosharesock) - tcp_ses->nosharesock = true; - - tcp_ses->ops = ctx->ops; - tcp_ses->vals = ctx->vals; - cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns)); - - tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId); - tcp_ses->noblockcnt = ctx->rootfs; - tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs; - tcp_ses->noautotune = ctx->noautotune; - tcp_ses->tcp_nodelay = ctx->sockopt_tcp_nodelay; - tcp_ses->rdma = ctx->rdma; - tcp_ses->in_flight = 0; - tcp_ses->max_in_flight = 0; - tcp_ses->credits = 1; - if (primary_server) { - spin_lock(&cifs_tcp_ses_lock); - ++primary_server->srv_count; - spin_unlock(&cifs_tcp_ses_lock); - tcp_ses->primary_server = primary_server; - } - init_waitqueue_head(&tcp_ses->response_q); - init_waitqueue_head(&tcp_ses->request_q); - INIT_LIST_HEAD(&tcp_ses->pending_mid_q); - mutex_init(&tcp_ses->_srv_mutex); - memcpy(tcp_ses->workstation_RFC1001_name, - ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); - memcpy(tcp_ses->server_RFC1001_name, - ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); - tcp_ses->session_estab = false; - tcp_ses->sequence_number = 0; - tcp_ses->reconnect_instance = 1; - tcp_ses->lstrp = jiffies; - tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); - spin_lock_init(&tcp_ses->req_lock); - spin_lock_init(&tcp_ses->srv_lock); - spin_lock_init(&tcp_ses->mid_lock); - INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); - INIT_LIST_HEAD(&tcp_ses->smb_ses_list); - INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); - INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server); - INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); - mutex_init(&tcp_ses->reconnect_mutex); -#ifdef CONFIG_CIFS_DFS_UPCALL - mutex_init(&tcp_ses->refpath_lock); -#endif - memcpy(&tcp_ses->srcaddr, &ctx->srcaddr, - sizeof(tcp_ses->srcaddr)); - memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, - sizeof(tcp_ses->dstaddr)); - if (ctx->use_client_guid) - memcpy(tcp_ses->client_guid, ctx->client_guid, - SMB2_CLIENT_GUID_SIZE); - else - generate_random_uuid(tcp_ses->client_guid); - /* - * at this point we are the only ones with the pointer - * to the struct since the kernel thread not created yet - * no need to spinlock this init of tcpStatus or srv_count - */ - tcp_ses->tcpStatus = CifsNew; - ++tcp_ses->srv_count; - - if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN && - ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX) - tcp_ses->echo_interval = ctx->echo_interval * HZ; - else - tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ; - if (tcp_ses->rdma) { -#ifndef CONFIG_CIFS_SMB_DIRECT - cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n"); - rc = -ENOENT; - goto out_err_crypto_release; -#endif - tcp_ses->smbd_conn = smbd_get_connection( - tcp_ses, (struct sockaddr *)&ctx->dstaddr); - if (tcp_ses->smbd_conn) { - cifs_dbg(VFS, "RDMA transport established\n"); - rc = 0; - goto smbd_connected; - } else { - rc = -ENOENT; - goto out_err_crypto_release; - } - } - rc = ip_connect(tcp_ses); - if (rc < 0) { - cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n"); - goto out_err_crypto_release; - } -smbd_connected: - /* - * since we're in a cifs function already, we know that - * this will succeed. No need for try_module_get(). - */ - __module_get(THIS_MODULE); - tcp_ses->tsk = kthread_run(cifs_demultiplex_thread, - tcp_ses, "cifsd"); - if (IS_ERR(tcp_ses->tsk)) { - rc = PTR_ERR(tcp_ses->tsk); - cifs_dbg(VFS, "error %d create cifsd thread\n", rc); - module_put(THIS_MODULE); - goto out_err_crypto_release; - } - tcp_ses->min_offload = ctx->min_offload; - /* - * at this point we are the only ones with the pointer - * to the struct since the kernel thread not created yet - * no need to spinlock this update of tcpStatus - */ - spin_lock(&tcp_ses->srv_lock); - tcp_ses->tcpStatus = CifsNeedNegotiate; - spin_unlock(&tcp_ses->srv_lock); - - if ((ctx->max_credits < 20) || (ctx->max_credits > 60000)) - tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE; - else - tcp_ses->max_credits = ctx->max_credits; - - tcp_ses->nr_targets = 1; - tcp_ses->ignore_signature = ctx->ignore_signature; - /* thread spawned, put it on the list */ - spin_lock(&cifs_tcp_ses_lock); - list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - /* queue echo request delayed work */ - queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval); - - /* queue dns resolution delayed work */ - cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n", - __func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT); - - queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ)); - - return tcp_ses; - -out_err_crypto_release: - cifs_crypto_secmech_release(tcp_ses); - - put_net(cifs_net_ns(tcp_ses)); - -out_err: - if (tcp_ses) { - if (CIFS_SERVER_IS_CHAN(tcp_ses)) - cifs_put_tcp_session(tcp_ses->primary_server, false); - kfree(tcp_ses->hostname); - if (tcp_ses->ssocket) - sock_release(tcp_ses->ssocket); - kfree(tcp_ses); - } - return ERR_PTR(rc); -} - -/* this function must be called with ses_lock and chan_lock held */ -static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - if (ctx->sectype != Unspecified && - ctx->sectype != ses->sectype) - return 0; - - /* - * If an existing session is limited to less channels than - * requested, it should not be reused - */ - if (ses->chan_max < ctx->max_channels) - return 0; - - switch (ses->sectype) { - case Kerberos: - if (!uid_eq(ctx->cred_uid, ses->cred_uid)) - return 0; - break; - default: - /* NULL username means anonymous session */ - if (ses->user_name == NULL) { - if (!ctx->nullauth) - return 0; - break; - } - - /* anything else takes username/password */ - if (strncmp(ses->user_name, - ctx->username ? ctx->username : "", - CIFS_MAX_USERNAME_LEN)) - return 0; - if ((ctx->username && strlen(ctx->username) != 0) && - ses->password != NULL && - strncmp(ses->password, - ctx->password ? ctx->password : "", - CIFS_MAX_PASSWORD_LEN)) - return 0; - } - return 1; -} - -/** - * cifs_setup_ipc - helper to setup the IPC tcon for the session - * @ses: smb session to issue the request on - * @ctx: the superblock configuration context to use for building the - * new tree connection for the IPC (interprocess communication RPC) - * - * A new IPC connection is made and stored in the session - * tcon_ipc. The IPC tcon has the same lifetime as the session. - */ -static int -cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - int rc = 0, xid; - struct cifs_tcon *tcon; - char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; - bool seal = false; - struct TCP_Server_Info *server = ses->server; - - /* - * If the mount request that resulted in the creation of the - * session requires encryption, force IPC to be encrypted too. - */ - if (ctx->seal) { - if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) - seal = true; - else { - cifs_server_dbg(VFS, - "IPC: server doesn't support encryption\n"); - return -EOPNOTSUPP; - } - } - - tcon = tconInfoAlloc(); - if (tcon == NULL) - return -ENOMEM; - - spin_lock(&server->srv_lock); - scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname); - spin_unlock(&server->srv_lock); - - xid = get_xid(); - tcon->ses = ses; - tcon->ipc = true; - tcon->seal = seal; - rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls); - free_xid(xid); - - if (rc) { - cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); - tconInfoFree(tcon); - goto out; - } - - cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid); - - spin_lock(&tcon->tc_lock); - tcon->status = TID_GOOD; - spin_unlock(&tcon->tc_lock); - ses->tcon_ipc = tcon; -out: - return rc; -} - -/** - * cifs_free_ipc - helper to release the session IPC tcon - * @ses: smb session to unmount the IPC from - * - * Needs to be called everytime a session is destroyed. - * - * On session close, the IPC is closed and the server must release all tcons of the session. - * No need to send a tree disconnect here. - * - * Besides, it will make the server to not close durable and resilient files on session close, as - * specified in MS-SMB2 3.3.5.6 Receiving an SMB2 LOGOFF Request. - */ -static int -cifs_free_ipc(struct cifs_ses *ses) -{ - struct cifs_tcon *tcon = ses->tcon_ipc; - - if (tcon == NULL) - return 0; - - tconInfoFree(tcon); - ses->tcon_ipc = NULL; - return 0; -} - -static struct cifs_ses * -cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - struct cifs_ses *ses; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_EXITING) { - spin_unlock(&ses->ses_lock); - continue; - } - spin_lock(&ses->chan_lock); - if (!match_session(ses, ctx)) { - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - continue; - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - ++ses->ses_count; - spin_unlock(&cifs_tcp_ses_lock); - return ses; - } - spin_unlock(&cifs_tcp_ses_lock); - return NULL; -} - -void cifs_put_smb_ses(struct cifs_ses *ses) -{ - unsigned int rc, xid; - unsigned int chan_count; - struct TCP_Server_Info *server = ses->server; - - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_EXITING) { - spin_unlock(&ses->ses_lock); - return; - } - spin_unlock(&ses->ses_lock); - - cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); - cifs_dbg(FYI, - "%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->tree_name : "NONE"); - - spin_lock(&cifs_tcp_ses_lock); - if (--ses->ses_count > 0) { - spin_unlock(&cifs_tcp_ses_lock); - return; - } - spin_unlock(&cifs_tcp_ses_lock); - - /* ses_count can never go negative */ - WARN_ON(ses->ses_count < 0); - - if (ses->ses_status == SES_GOOD) - ses->ses_status = SES_EXITING; - - cifs_free_ipc(ses); - - if (ses->ses_status == SES_EXITING && server->ops->logoff) { - xid = get_xid(); - rc = server->ops->logoff(xid, ses); - if (rc) - cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n", - __func__, rc); - _free_xid(xid); - } - - spin_lock(&cifs_tcp_ses_lock); - list_del_init(&ses->smb_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - chan_count = ses->chan_count; - - /* close any extra channels */ - if (chan_count > 1) { - int i; - - for (i = 1; i < chan_count; i++) { - if (ses->chans[i].iface) { - kref_put(&ses->chans[i].iface->refcount, release_iface); - ses->chans[i].iface = NULL; - } - cifs_put_tcp_session(ses->chans[i].server, 0); - ses->chans[i].server = NULL; - } - } - - sesInfoFree(ses); - cifs_put_tcp_session(server, 0); -} - -#ifdef CONFIG_KEYS - -/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */ -#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1) - -/* Populate username and pw fields from keyring if possible */ -static int -cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses) -{ - int rc = 0; - int is_domain = 0; - const char *delim, *payload; - char *desc; - ssize_t len; - struct key *key; - struct TCP_Server_Info *server = ses->server; - struct sockaddr_in *sa; - struct sockaddr_in6 *sa6; - const struct user_key_payload *upayload; - - desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); - if (!desc) - return -ENOMEM; - - /* try to find an address key first */ - switch (server->dstaddr.ss_family) { - case AF_INET: - sa = (struct sockaddr_in *)&server->dstaddr; - sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr); - break; - case AF_INET6: - sa6 = (struct sockaddr_in6 *)&server->dstaddr; - sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr); - break; - default: - cifs_dbg(FYI, "Bad ss_family (%hu)\n", - server->dstaddr.ss_family); - rc = -EINVAL; - goto out_err; - } - - cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); - key = request_key(&key_type_logon, desc, ""); - if (IS_ERR(key)) { - if (!ses->domainName) { - cifs_dbg(FYI, "domainName is NULL\n"); - rc = PTR_ERR(key); - goto out_err; - } - - /* didn't work, try to find a domain key */ - sprintf(desc, "cifs:d:%s", ses->domainName); - cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); - key = request_key(&key_type_logon, desc, ""); - if (IS_ERR(key)) { - rc = PTR_ERR(key); - goto out_err; - } - is_domain = 1; - } - - down_read(&key->sem); - upayload = user_key_payload_locked(key); - if (IS_ERR_OR_NULL(upayload)) { - rc = upayload ? PTR_ERR(upayload) : -EINVAL; - goto out_key_put; - } - - /* find first : in payload */ - payload = upayload->data; - delim = strnchr(payload, upayload->datalen, ':'); - cifs_dbg(FYI, "payload=%s\n", payload); - if (!delim) { - cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n", - upayload->datalen); - rc = -EINVAL; - goto out_key_put; - } - - len = delim - payload; - if (len > CIFS_MAX_USERNAME_LEN || len <= 0) { - cifs_dbg(FYI, "Bad value from username search (len=%zd)\n", - len); - rc = -EINVAL; - goto out_key_put; - } - - ctx->username = kstrndup(payload, len, GFP_KERNEL); - if (!ctx->username) { - cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n", - len); - rc = -ENOMEM; - goto out_key_put; - } - cifs_dbg(FYI, "%s: username=%s\n", __func__, ctx->username); - - len = key->datalen - (len + 1); - if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) { - cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len); - rc = -EINVAL; - kfree(ctx->username); - ctx->username = NULL; - goto out_key_put; - } - - ++delim; - ctx->password = kstrndup(delim, len, GFP_KERNEL); - if (!ctx->password) { - cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n", - len); - rc = -ENOMEM; - kfree(ctx->username); - ctx->username = NULL; - goto out_key_put; - } - - /* - * If we have a domain key then we must set the domainName in the - * for the request. - */ - if (is_domain && ses->domainName) { - ctx->domainname = kstrdup(ses->domainName, GFP_KERNEL); - if (!ctx->domainname) { - cifs_dbg(FYI, "Unable to allocate %zd bytes for domain\n", - len); - rc = -ENOMEM; - kfree(ctx->username); - ctx->username = NULL; - kfree_sensitive(ctx->password); - ctx->password = NULL; - goto out_key_put; - } - } - - strscpy(ctx->workstation_name, ses->workstation_name, sizeof(ctx->workstation_name)); - -out_key_put: - up_read(&key->sem); - key_put(key); -out_err: - kfree(desc); - cifs_dbg(FYI, "%s: returning %d\n", __func__, rc); - return rc; -} -#else /* ! CONFIG_KEYS */ -static inline int -cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)), - struct cifs_ses *ses __attribute__((unused))) -{ - return -ENOSYS; -} -#endif /* CONFIG_KEYS */ - -/** - * cifs_get_smb_ses - get a session matching @ctx data from @server - * @server: server to setup the session to - * @ctx: superblock configuration context to use to setup the session - * - * This function assumes it is being called from cifs_mount() where we - * already got a server reference (server refcount +1). See - * cifs_get_tcon() for refcount explanations. - */ -struct cifs_ses * -cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - int rc = 0; - unsigned int xid; - struct cifs_ses *ses; - struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; - - xid = get_xid(); - - ses = cifs_find_smb_ses(server, ctx); - if (ses) { - cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", - ses->ses_status); - - spin_lock(&ses->chan_lock); - if (cifs_chan_needs_reconnect(ses, server)) { - spin_unlock(&ses->chan_lock); - cifs_dbg(FYI, "Session needs reconnect\n"); - - mutex_lock(&ses->session_mutex); - rc = cifs_negotiate_protocol(xid, ses, server); - if (rc) { - mutex_unlock(&ses->session_mutex); - /* problem -- put our ses reference */ - cifs_put_smb_ses(ses); - free_xid(xid); - return ERR_PTR(rc); - } - - rc = cifs_setup_session(xid, ses, server, - ctx->local_nls); - if (rc) { - mutex_unlock(&ses->session_mutex); - /* problem -- put our reference */ - cifs_put_smb_ses(ses); - free_xid(xid); - return ERR_PTR(rc); - } - mutex_unlock(&ses->session_mutex); - - spin_lock(&ses->chan_lock); - } - spin_unlock(&ses->chan_lock); - - /* existing SMB ses has a server reference already */ - cifs_put_tcp_session(server, 0); - free_xid(xid); - return ses; - } - - rc = -ENOMEM; - - cifs_dbg(FYI, "Existing smb sess not found\n"); - ses = sesInfoAlloc(); - if (ses == NULL) - goto get_ses_fail; - - /* new SMB session uses our server ref */ - ses->server = server; - if (server->dstaddr.ss_family == AF_INET6) - sprintf(ses->ip_addr, "%pI6", &addr6->sin6_addr); - else - sprintf(ses->ip_addr, "%pI4", &addr->sin_addr); - - if (ctx->username) { - ses->user_name = kstrdup(ctx->username, GFP_KERNEL); - if (!ses->user_name) - goto get_ses_fail; - } - - /* ctx->password freed at unmount */ - if (ctx->password) { - ses->password = kstrdup(ctx->password, GFP_KERNEL); - if (!ses->password) - goto get_ses_fail; - } - if (ctx->domainname) { - ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL); - if (!ses->domainName) - goto get_ses_fail; - } - - strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name)); - - if (ctx->domainauto) - ses->domainAuto = ctx->domainauto; - ses->cred_uid = ctx->cred_uid; - ses->linux_uid = ctx->linux_uid; - - ses->sectype = ctx->sectype; - ses->sign = ctx->sign; - - /* add server as first channel */ - spin_lock(&ses->chan_lock); - ses->chans[0].server = server; - ses->chan_count = 1; - ses->chan_max = ctx->multichannel ? ctx->max_channels:1; - ses->chans_need_reconnect = 1; - spin_unlock(&ses->chan_lock); - - mutex_lock(&ses->session_mutex); - rc = cifs_negotiate_protocol(xid, ses, server); - if (!rc) - rc = cifs_setup_session(xid, ses, server, ctx->local_nls); - mutex_unlock(&ses->session_mutex); - - /* each channel uses a different signing key */ - spin_lock(&ses->chan_lock); - memcpy(ses->chans[0].signkey, ses->smb3signingkey, - sizeof(ses->smb3signingkey)); - spin_unlock(&ses->chan_lock); - - if (rc) - goto get_ses_fail; - - /* - * success, put it on the list and add it as first channel - * note: the session becomes active soon after this. So you'll - * need to lock before changing something in the session. - */ - spin_lock(&cifs_tcp_ses_lock); - list_add(&ses->smb_ses_list, &server->smb_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - cifs_setup_ipc(ses, ctx); - - free_xid(xid); - - return ses; - -get_ses_fail: - sesInfoFree(ses); - free_xid(xid); - return ERR_PTR(rc); -} - -/* this function must be called with tc_lock held */ -static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - if (tcon->status == TID_EXITING) - return 0; - if (strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) - return 0; - if (tcon->seal != ctx->seal) - return 0; - if (tcon->snapshot_time != ctx->snapshot_time) - return 0; - if (tcon->handle_timeout != ctx->handle_timeout) - return 0; - if (tcon->no_lease != ctx->no_lease) - return 0; - if (tcon->nodelete != ctx->nodelete) - return 0; - return 1; -} - -static struct cifs_tcon * -cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - struct cifs_tcon *tcon; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->tc_lock); - if (!match_tcon(tcon, ctx)) { - spin_unlock(&tcon->tc_lock); - continue; - } - ++tcon->tc_count; - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - return tcon; - } - spin_unlock(&cifs_tcp_ses_lock); - return NULL; -} - -void -cifs_put_tcon(struct cifs_tcon *tcon) -{ - unsigned int xid; - struct cifs_ses *ses; - - /* - * IPC tcon share the lifetime of their session and are - * destroyed in the session put function - */ - if (tcon == NULL || tcon->ipc) - return; - - ses = tcon->ses; - cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); - spin_lock(&cifs_tcp_ses_lock); - spin_lock(&tcon->tc_lock); - if (--tcon->tc_count > 0) { - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - return; - } - - /* tc_count can never go negative */ - WARN_ON(tcon->tc_count < 0); - - list_del_init(&tcon->tcon_list); - tcon->status = TID_EXITING; - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - - /* cancel polling of interfaces */ - cancel_delayed_work_sync(&tcon->query_interfaces); - - if (tcon->use_witness) { - int rc; - - rc = cifs_swn_unregister(tcon); - if (rc < 0) { - cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", - __func__, rc); - } - } - - xid = get_xid(); - if (ses->server->ops->tree_disconnect) - ses->server->ops->tree_disconnect(xid, tcon); - _free_xid(xid); - - cifs_fscache_release_super_cookie(tcon); - tconInfoFree(tcon); - cifs_put_smb_ses(ses); -} - -/** - * cifs_get_tcon - get a tcon matching @ctx data from @ses - * @ses: smb session to issue the request on - * @ctx: the superblock configuration context to use for building the - * - * - tcon refcount is the number of mount points using the tcon. - * - ses refcount is the number of tcon using the session. - * - * 1. This function assumes it is being called from cifs_mount() where - * we already got a session reference (ses refcount +1). - * - * 2. Since we're in the context of adding a mount point, the end - * result should be either: - * - * a) a new tcon already allocated with refcount=1 (1 mount point) and - * its session refcount incremented (1 new tcon). This +1 was - * already done in (1). - * - * b) an existing tcon with refcount+1 (add a mount point to it) and - * identical ses refcount (no new tcon). Because of (1) we need to - * decrement the ses refcount. - */ -static struct cifs_tcon * -cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - int rc, xid; - struct cifs_tcon *tcon; - - tcon = cifs_find_tcon(ses, ctx); - if (tcon) { - /* - * tcon has refcount already incremented but we need to - * decrement extra ses reference gotten by caller (case b) - */ - cifs_dbg(FYI, "Found match on UNC path\n"); - cifs_put_smb_ses(ses); - return tcon; - } - - if (!ses->server->ops->tree_connect) { - rc = -ENOSYS; - goto out_fail; - } - - tcon = tconInfoAlloc(); - if (tcon == NULL) { - rc = -ENOMEM; - goto out_fail; - } - - if (ctx->snapshot_time) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "Use SMB2 or later for snapshot mount option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else - tcon->snapshot_time = ctx->snapshot_time; - } - - if (ctx->handle_timeout) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "Use SMB2.1 or later for handle timeout option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else - tcon->handle_timeout = ctx->handle_timeout; - } - - tcon->ses = ses; - if (ctx->password) { - tcon->password = kstrdup(ctx->password, GFP_KERNEL); - if (!tcon->password) { - rc = -ENOMEM; - goto out_fail; - } - } - - if (ctx->seal) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB3 or later required for encryption\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else if (tcon->ses->server->capabilities & - SMB2_GLOBAL_CAP_ENCRYPTION) - tcon->seal = true; - else { - cifs_dbg(VFS, "Encryption is not supported on share\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } - - if (ctx->linux_ext) { - if (ses->server->posix_ext_supported) { - tcon->posix_extensions = true; - pr_warn_once("SMB3.11 POSIX Extensions are experimental\n"); - } else if ((ses->server->vals->protocol_id == SMB311_PROT_ID) || - (strcmp(ses->server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) || - (strcmp(ses->server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0)) { - cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else { - cifs_dbg(VFS, "Check vers= mount option. SMB3.11 " - "disabled but required for POSIX extensions\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } - - xid = get_xid(); - rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon, - ctx->local_nls); - free_xid(xid); - cifs_dbg(FYI, "Tcon rc = %d\n", rc); - if (rc) - goto out_fail; - - tcon->use_persistent = false; - /* check if SMB2 or later, CIFS does not support persistent handles */ - if (ctx->persistent) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB3 or later required for persistent handles\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else if (ses->server->capabilities & - SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) - tcon->use_persistent = true; - else /* persistent handles requested but not supported */ { - cifs_dbg(VFS, - "Persistent handles not supported on share\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) - && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) - && (ctx->nopersistent == false)) { - cifs_dbg(FYI, "enabling persistent handles\n"); - tcon->use_persistent = true; - } else if (ctx->resilient) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB2.1 or later required for resilient handles\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - tcon->use_resilient = true; - } - - tcon->use_witness = false; - if (IS_ENABLED(CONFIG_CIFS_SWN_UPCALL) && ctx->witness) { - if (ses->server->vals->protocol_id >= SMB30_PROT_ID) { - if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) { - /* - * Set witness in use flag in first place - * to retry registration in the echo task - */ - tcon->use_witness = true; - /* And try to register immediately */ - rc = cifs_swn_register(tcon); - if (rc < 0) { - cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc); - goto out_fail; - } - } else { - /* TODO: try to extend for non-cluster uses (eg multichannel) */ - cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } else { - cifs_dbg(VFS, "SMB3 or later required for witness option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } - - /* If the user really knows what they are doing they can override */ - if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) { - if (ctx->cache_ro) - cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n"); - else if (ctx->cache_rw) - cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n"); - } - - if (ctx->no_lease) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB2 or later required for nolease option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else - tcon->no_lease = ctx->no_lease; - } - - /* - * We can have only one retry value for a connection to a share so for - * resources mounted more than once to the same server share the last - * value passed in for the retry flag is used. - */ - tcon->retry = ctx->retry; - tcon->nocase = ctx->nocase; - tcon->broken_sparse_sup = ctx->no_sparse; - if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) - tcon->nohandlecache = ctx->nohandlecache; - else - tcon->nohandlecache = true; - tcon->nodelete = ctx->nodelete; - tcon->local_lease = ctx->local_lease; - INIT_LIST_HEAD(&tcon->pending_opens); - tcon->status = TID_GOOD; - - INIT_DELAYED_WORK(&tcon->query_interfaces, - smb2_query_server_interfaces); - if (ses->server->dialect >= SMB30_PROT_ID && - (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { - /* schedule query interfaces poll */ - queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, - (SMB_INTERFACE_POLL_INTERVAL * HZ)); - } - - spin_lock(&cifs_tcp_ses_lock); - list_add(&tcon->tcon_list, &ses->tcon_list); - spin_unlock(&cifs_tcp_ses_lock); - - return tcon; - -out_fail: - tconInfoFree(tcon); - return ERR_PTR(rc); -} - -void -cifs_put_tlink(struct tcon_link *tlink) -{ - if (!tlink || IS_ERR(tlink)) - return; - - if (!atomic_dec_and_test(&tlink->tl_count) || - test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { - tlink->tl_time = jiffies; - return; - } - - if (!IS_ERR(tlink_tcon(tlink))) - cifs_put_tcon(tlink_tcon(tlink)); - kfree(tlink); - return; -} - -static int -compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) -{ - struct cifs_sb_info *old = CIFS_SB(sb); - struct cifs_sb_info *new = mnt_data->cifs_sb; - unsigned int oldflags = old->mnt_cifs_flags & CIFS_MOUNT_MASK; - unsigned int newflags = new->mnt_cifs_flags & CIFS_MOUNT_MASK; - - if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK)) - return 0; - - if (old->mnt_cifs_serverino_autodisabled) - newflags &= ~CIFS_MOUNT_SERVER_INUM; - - if (oldflags != newflags) - return 0; - - /* - * We want to share sb only if we don't specify an r/wsize or - * specified r/wsize is greater than or equal to existing one. - */ - if (new->ctx->wsize && new->ctx->wsize < old->ctx->wsize) - return 0; - - if (new->ctx->rsize && new->ctx->rsize < old->ctx->rsize) - return 0; - - if (!uid_eq(old->ctx->linux_uid, new->ctx->linux_uid) || - !gid_eq(old->ctx->linux_gid, new->ctx->linux_gid)) - return 0; - - if (old->ctx->file_mode != new->ctx->file_mode || - old->ctx->dir_mode != new->ctx->dir_mode) - return 0; - - if (strcmp(old->local_nls->charset, new->local_nls->charset)) - return 0; - - if (old->ctx->acregmax != new->ctx->acregmax) - return 0; - if (old->ctx->acdirmax != new->ctx->acdirmax) - return 0; - if (old->ctx->closetimeo != new->ctx->closetimeo) - return 0; - - return 1; -} - -static int -match_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data) -{ - struct cifs_sb_info *old = CIFS_SB(sb); - struct cifs_sb_info *new = mnt_data->cifs_sb; - bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && - old->prepath; - bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && - new->prepath; - - if (old_set && new_set && !strcmp(new->prepath, old->prepath)) - return 1; - else if (!old_set && !new_set) - return 1; - - return 0; -} - -int -cifs_match_super(struct super_block *sb, void *data) -{ - struct cifs_mnt_data *mnt_data = data; - struct smb3_fs_context *ctx; - struct cifs_sb_info *cifs_sb; - struct TCP_Server_Info *tcp_srv; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - int rc = 0; - - spin_lock(&cifs_tcp_ses_lock); - cifs_sb = CIFS_SB(sb); - - /* We do not want to use a superblock that has been shutdown */ - if (CIFS_MOUNT_SHUTDOWN & cifs_sb->mnt_cifs_flags) { - spin_unlock(&cifs_tcp_ses_lock); - return 0; - } - - tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); - if (tlink == NULL) { - /* can not match superblock if tlink were ever null */ - spin_unlock(&cifs_tcp_ses_lock); - return 0; - } - tcon = tlink_tcon(tlink); - ses = tcon->ses; - tcp_srv = ses->server; - - ctx = mnt_data->ctx; - - spin_lock(&tcp_srv->srv_lock); - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - spin_lock(&tcon->tc_lock); - if (!match_server(tcp_srv, ctx) || - !match_session(ses, ctx) || - !match_tcon(tcon, ctx) || - !match_prepath(sb, mnt_data)) { - rc = 0; - goto out; - } - - rc = compare_mount_options(sb, mnt_data); -out: - spin_unlock(&tcon->tc_lock); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - spin_unlock(&tcp_srv->srv_lock); - - spin_unlock(&cifs_tcp_ses_lock); - cifs_put_tlink(tlink); - return rc; -} - -#ifdef CONFIG_DEBUG_LOCK_ALLOC -static struct lock_class_key cifs_key[2]; -static struct lock_class_key cifs_slock_key[2]; - -static inline void -cifs_reclassify_socket4(struct socket *sock) -{ - struct sock *sk = sock->sk; - BUG_ON(!sock_allow_reclassification(sk)); - sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS", - &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]); -} - -static inline void -cifs_reclassify_socket6(struct socket *sock) -{ - struct sock *sk = sock->sk; - BUG_ON(!sock_allow_reclassification(sk)); - sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS", - &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]); -} -#else -static inline void -cifs_reclassify_socket4(struct socket *sock) -{ -} - -static inline void -cifs_reclassify_socket6(struct socket *sock) -{ -} -#endif - -/* See RFC1001 section 14 on representation of Netbios names */ -static void rfc1002mangle(char *target, char *source, unsigned int length) -{ - unsigned int i, j; - - for (i = 0, j = 0; i < (length); i++) { - /* mask a nibble at a time and encode */ - target[j] = 'A' + (0x0F & (source[i] >> 4)); - target[j+1] = 'A' + (0x0F & source[i]); - j += 2; - } - -} - -static int -bind_socket(struct TCP_Server_Info *server) -{ - int rc = 0; - if (server->srcaddr.ss_family != AF_UNSPEC) { - /* Bind to the specified local IP address */ - struct socket *socket = server->ssocket; - rc = socket->ops->bind(socket, - (struct sockaddr *) &server->srcaddr, - sizeof(server->srcaddr)); - if (rc < 0) { - struct sockaddr_in *saddr4; - struct sockaddr_in6 *saddr6; - saddr4 = (struct sockaddr_in *)&server->srcaddr; - saddr6 = (struct sockaddr_in6 *)&server->srcaddr; - if (saddr6->sin6_family == AF_INET6) - cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", - &saddr6->sin6_addr, rc); - else - cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", - &saddr4->sin_addr.s_addr, rc); - } - } - return rc; -} - -static int -ip_rfc1001_connect(struct TCP_Server_Info *server) -{ - int rc = 0; - /* - * some servers require RFC1001 sessinit before sending - * negprot - BB check reconnection in case where second - * sessinit is sent but no second negprot - */ - struct rfc1002_session_packet req = {}; - struct smb_hdr *smb_buf = (struct smb_hdr *)&req; - unsigned int len; - - req.trailer.session_req.called_len = sizeof(req.trailer.session_req.called_name); - - if (server->server_RFC1001_name[0] != 0) - rfc1002mangle(req.trailer.session_req.called_name, - server->server_RFC1001_name, - RFC1001_NAME_LEN_WITH_NULL); - else - rfc1002mangle(req.trailer.session_req.called_name, - DEFAULT_CIFS_CALLED_NAME, - RFC1001_NAME_LEN_WITH_NULL); - - req.trailer.session_req.calling_len = sizeof(req.trailer.session_req.calling_name); - - /* calling name ends in null (byte 16) from old smb convention */ - if (server->workstation_RFC1001_name[0] != 0) - rfc1002mangle(req.trailer.session_req.calling_name, - server->workstation_RFC1001_name, - RFC1001_NAME_LEN_WITH_NULL); - else - rfc1002mangle(req.trailer.session_req.calling_name, - "LINUX_CIFS_CLNT", - RFC1001_NAME_LEN_WITH_NULL); - - /* - * As per rfc1002, @len must be the number of bytes that follows the - * length field of a rfc1002 session request payload. - */ - len = sizeof(req) - offsetof(struct rfc1002_session_packet, trailer.session_req); - - smb_buf->smb_buf_length = cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | len); - rc = smb_send(server, smb_buf, len); - /* - * RFC1001 layer in at least one server requires very short break before - * negprot presumably because not expecting negprot to follow so fast. - * This is a simple solution that works without complicating the code - * and causes no significant slowing down on mount for everyone else - */ - usleep_range(1000, 2000); - - return rc; -} - -static int -generic_ip_connect(struct TCP_Server_Info *server) -{ - int rc = 0; - __be16 sport; - int slen, sfamily; - struct socket *socket = server->ssocket; - struct sockaddr *saddr; - - saddr = (struct sockaddr *) &server->dstaddr; - - if (server->dstaddr.ss_family == AF_INET6) { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&server->dstaddr; - - sport = ipv6->sin6_port; - slen = sizeof(struct sockaddr_in6); - sfamily = AF_INET6; - cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr, - ntohs(sport)); - } else { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&server->dstaddr; - - sport = ipv4->sin_port; - slen = sizeof(struct sockaddr_in); - sfamily = AF_INET; - cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr, - ntohs(sport)); - } - - if (socket == NULL) { - rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM, - IPPROTO_TCP, &socket, 1); - if (rc < 0) { - cifs_server_dbg(VFS, "Error %d creating socket\n", rc); - server->ssocket = NULL; - return rc; - } - - /* BB other socket options to set KEEPALIVE, NODELAY? */ - cifs_dbg(FYI, "Socket created\n"); - server->ssocket = socket; - socket->sk->sk_allocation = GFP_NOFS; - if (sfamily == AF_INET6) - cifs_reclassify_socket6(socket); - else - cifs_reclassify_socket4(socket); - } - - rc = bind_socket(server); - if (rc < 0) - return rc; - - /* - * Eventually check for other socket options to change from - * the default. sock_setsockopt not used because it expects - * user space buffer - */ - socket->sk->sk_rcvtimeo = 7 * HZ; - socket->sk->sk_sndtimeo = 5 * HZ; - - /* make the bufsizes depend on wsize/rsize and max requests */ - if (server->noautotune) { - if (socket->sk->sk_sndbuf < (200 * 1024)) - socket->sk->sk_sndbuf = 200 * 1024; - if (socket->sk->sk_rcvbuf < (140 * 1024)) - socket->sk->sk_rcvbuf = 140 * 1024; - } - - if (server->tcp_nodelay) - tcp_sock_set_nodelay(socket->sk); - - cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n", - socket->sk->sk_sndbuf, - socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo); - - rc = socket->ops->connect(socket, saddr, slen, - server->noblockcnt ? O_NONBLOCK : 0); - /* - * When mounting SMB root file systems, we do not want to block in - * connect. Otherwise bail out and then let cifs_reconnect() perform - * reconnect failover - if possible. - */ - if (server->noblockcnt && rc == -EINPROGRESS) - rc = 0; - if (rc < 0) { - cifs_dbg(FYI, "Error %d connecting to server\n", rc); - trace_smb3_connect_err(server->hostname, server->conn_id, &server->dstaddr, rc); - sock_release(socket); - server->ssocket = NULL; - return rc; - } - trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr); - if (sport == htons(RFC1001_PORT)) - rc = ip_rfc1001_connect(server); - - return rc; -} - -static int -ip_connect(struct TCP_Server_Info *server) -{ - __be16 *sport; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; - struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; - - if (server->dstaddr.ss_family == AF_INET6) - sport = &addr6->sin6_port; - else - sport = &addr->sin_port; - - if (*sport == 0) { - int rc; - - /* try with 445 port at first */ - *sport = htons(CIFS_PORT); - - rc = generic_ip_connect(server); - if (rc >= 0) - return rc; - - /* if it failed, try with 139 port */ - *sport = htons(RFC1001_PORT); - } - - return generic_ip_connect(server); -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - /* - * If we are reconnecting then should we check to see if - * any requested capabilities changed locally e.g. via - * remount but we can not do much about it here - * if they have (even if we could detect it by the following) - * Perhaps we could add a backpointer to array of sb from tcon - * or if we change to make all sb to same share the same - * sb as NFS - then we only have one backpointer to sb. - * What if we wanted to mount the server share twice once with - * and once without posixacls or posix paths? - */ - __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - - if (ctx && ctx->no_linux_ext) { - tcon->fsUnixInfo.Capability = 0; - tcon->unix_ext = 0; /* Unix Extensions disabled */ - cifs_dbg(FYI, "Linux protocol extensions disabled\n"); - return; - } else if (ctx) - tcon->unix_ext = 1; /* Unix Extensions supported */ - - if (!tcon->unix_ext) { - cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n"); - return; - } - - if (!CIFSSMBQFSUnixInfo(xid, tcon)) { - __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); - /* - * check for reconnect case in which we do not - * want to change the mount behavior if we can avoid it - */ - if (ctx == NULL) { - /* - * turn off POSIX ACL and PATHNAMES if not set - * originally at mount time - */ - if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) - cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { - if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cifs_dbg(VFS, "POSIXPATH support change\n"); - cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { - cifs_dbg(VFS, "possible reconnect error\n"); - cifs_dbg(VFS, "server disabled POSIX path support\n"); - } - } - - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) - cifs_dbg(VFS, "per-share encryption not supported yet\n"); - - cap &= CIFS_UNIX_CAP_MASK; - if (ctx && ctx->no_psx_acl) - cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { - cifs_dbg(FYI, "negotiated posix acl support\n"); - if (cifs_sb) - cifs_sb->mnt_cifs_flags |= - CIFS_MOUNT_POSIXACL; - } - - if (ctx && ctx->posix_paths == 0) - cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { - cifs_dbg(FYI, "negotiate posix pathnames\n"); - if (cifs_sb) - cifs_sb->mnt_cifs_flags |= - CIFS_MOUNT_POSIX_PATHS; - } - - cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap); -#ifdef CONFIG_CIFS_DEBUG2 - if (cap & CIFS_UNIX_FCNTL_CAP) - cifs_dbg(FYI, "FCNTL cap\n"); - if (cap & CIFS_UNIX_EXTATTR_CAP) - cifs_dbg(FYI, "EXTATTR cap\n"); - if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cifs_dbg(FYI, "POSIX path cap\n"); - if (cap & CIFS_UNIX_XATTR_CAP) - cifs_dbg(FYI, "XATTR cap\n"); - if (cap & CIFS_UNIX_POSIX_ACL_CAP) - cifs_dbg(FYI, "POSIX ACL cap\n"); - if (cap & CIFS_UNIX_LARGE_READ_CAP) - cifs_dbg(FYI, "very large read cap\n"); - if (cap & CIFS_UNIX_LARGE_WRITE_CAP) - cifs_dbg(FYI, "very large write cap\n"); - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) - cifs_dbg(FYI, "transport encryption cap\n"); - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) - cifs_dbg(FYI, "mandatory transport encryption cap\n"); -#endif /* CIFS_DEBUG2 */ - if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { - if (ctx == NULL) - cifs_dbg(FYI, "resetting capabilities failed\n"); - else - cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n"); - - } - } -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) -{ - struct smb3_fs_context *ctx = cifs_sb->ctx; - - INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); - - spin_lock_init(&cifs_sb->tlink_tree_lock); - cifs_sb->tlink_tree = RB_ROOT; - - cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", - ctx->file_mode, ctx->dir_mode); - - /* this is needed for ASCII cp to Unicode converts */ - if (ctx->iocharset == NULL) { - /* load_nls_default cannot return null */ - cifs_sb->local_nls = load_nls_default(); - } else { - cifs_sb->local_nls = load_nls(ctx->iocharset); - if (cifs_sb->local_nls == NULL) { - cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n", - ctx->iocharset); - return -ELIBACC; - } - } - ctx->local_nls = cifs_sb->local_nls; - - smb3_update_mnt_flags(cifs_sb); - - if (ctx->direct_io) - cifs_dbg(FYI, "mounting share using direct i/o\n"); - if (ctx->cache_ro) { - cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n"); - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE; - } else if (ctx->cache_rw) { - cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n"); - cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE | - CIFS_MOUNT_RW_CACHE); - } - - if ((ctx->cifs_acl) && (ctx->dynperm)) - cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n"); - - if (ctx->prepath) { - cifs_sb->prepath = kstrdup(ctx->prepath, GFP_KERNEL); - if (cifs_sb->prepath == NULL) - return -ENOMEM; - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - } - - return 0; -} - -/* Release all succeed connections */ -static inline void mount_put_conns(struct mount_ctx *mnt_ctx) -{ - int rc = 0; - - if (mnt_ctx->tcon) - cifs_put_tcon(mnt_ctx->tcon); - else if (mnt_ctx->ses) - cifs_put_smb_ses(mnt_ctx->ses); - else if (mnt_ctx->server) - cifs_put_tcp_session(mnt_ctx->server, 0); - mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; - free_xid(mnt_ctx->xid); -} - -/* Get connections for tcp, ses and tcon */ -static int mount_get_conns(struct mount_ctx *mnt_ctx) -{ - int rc = 0; - struct TCP_Server_Info *server = NULL; - struct cifs_ses *ses = NULL; - struct cifs_tcon *tcon = NULL; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - unsigned int xid; - - xid = get_xid(); - - /* get a reference to a tcp session */ - server = cifs_get_tcp_session(ctx, NULL); - if (IS_ERR(server)) { - rc = PTR_ERR(server); - server = NULL; - goto out; - } - - /* get a reference to a SMB session */ - ses = cifs_get_smb_ses(server, ctx); - if (IS_ERR(ses)) { - rc = PTR_ERR(ses); - ses = NULL; - goto out; - } - - if ((ctx->persistent == true) && (!(ses->server->capabilities & - SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { - cifs_server_dbg(VFS, "persistent handles not supported by server\n"); - rc = -EOPNOTSUPP; - goto out; - } - - /* search for existing tcon to this server share */ - tcon = cifs_get_tcon(ses, ctx); - if (IS_ERR(tcon)) { - rc = PTR_ERR(tcon); - tcon = NULL; - goto out; - } - - /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ - if (tcon->posix_extensions) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* tell server which Unix caps we support */ - if (cap_unix(tcon->ses)) { - /* - * reset of caps checks mount to see if unix extensions disabled - * for just this mount. - */ - reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx); - spin_lock(&tcon->ses->server->srv_lock); - if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && - (le64_to_cpu(tcon->fsUnixInfo.Capability) & - CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { - spin_unlock(&tcon->ses->server->srv_lock); - rc = -EACCES; - goto out; - } - spin_unlock(&tcon->ses->server->srv_lock); - } else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - tcon->unix_ext = 0; /* server does not support them */ - - /* do not care if a following call succeed - informational */ - if (!tcon->pipe && server->ops->qfs_tcon) { - server->ops->qfs_tcon(xid, tcon, cifs_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { - if (tcon->fsDevInfo.DeviceCharacteristics & - cpu_to_le32(FILE_READ_ONLY_DEVICE)) - cifs_dbg(VFS, "mounted to read only share\n"); - else if ((cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_RW_CACHE) == 0) - cifs_dbg(VFS, "read only mount of RW share\n"); - /* no need to log a RW mount of a typical RW share */ - } - } - - /* - * Clamp the rsize/wsize mount arguments if they are too big for the server - * and set the rsize/wsize to the negotiated values if not passed in by - * the user on mount - */ - if ((cifs_sb->ctx->wsize == 0) || - (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) - cifs_sb->ctx->wsize = server->ops->negotiate_wsize(tcon, ctx); - if ((cifs_sb->ctx->rsize == 0) || - (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) - cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); - - /* - * The cookie is initialized from volume info returned above. - * Inside cifs_fscache_get_super_cookie it checks - * that we do not get super cookie twice. - */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) - cifs_fscache_get_super_cookie(tcon); - -out: - mnt_ctx->server = server; - mnt_ctx->ses = ses; - mnt_ctx->tcon = tcon; - mnt_ctx->xid = xid; - - return rc; -} - -static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, - struct cifs_tcon *tcon) -{ - struct tcon_link *tlink; - - /* hang the tcon off of the superblock */ - tlink = kzalloc(sizeof(*tlink), GFP_KERNEL); - if (tlink == NULL) - return -ENOMEM; - - tlink->tl_uid = ses->linux_uid; - tlink->tl_tcon = tcon; - tlink->tl_time = jiffies; - set_bit(TCON_LINK_MASTER, &tlink->tl_flags); - set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); - - cifs_sb->master_tlink = tlink; - spin_lock(&cifs_sb->tlink_tree_lock); - tlink_rb_insert(&cifs_sb->tlink_tree, tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - - queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, - TLINK_IDLE_EXPIRE); - return 0; -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -/* Get unique dfs connections */ -static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx) -{ - int rc; - - mnt_ctx->fs_ctx->nosharesock = true; - rc = mount_get_conns(mnt_ctx); - if (mnt_ctx->server) { - cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__); - spin_lock(&mnt_ctx->server->srv_lock); - mnt_ctx->server->is_dfs_conn = true; - spin_unlock(&mnt_ctx->server->srv_lock); - } - return rc; -} - -/* - * cifs_build_path_to_root returns full path to root when we do not have an - * existing connection (tcon) - */ -static char * -build_unc_path_to_root(const struct smb3_fs_context *ctx, - const struct cifs_sb_info *cifs_sb, bool useppath) -{ - char *full_path, *pos; - unsigned int pplen = useppath && ctx->prepath ? - strlen(ctx->prepath) + 1 : 0; - unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); - - if (unc_len > MAX_TREE_SIZE) - return ERR_PTR(-EINVAL); - - full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); - if (full_path == NULL) - return ERR_PTR(-ENOMEM); - - memcpy(full_path, ctx->UNC, unc_len); - pos = full_path + unc_len; - - if (pplen) { - *pos = CIFS_DIR_SEP(cifs_sb); - memcpy(pos + 1, ctx->prepath, pplen); - pos += pplen; - } - - *pos = '\0'; /* add trailing null */ - convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); - cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); - return full_path; -} - -/* - * expand_dfs_referral - Update cifs_sb from dfs referral path - * - * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the - * submount. Otherwise it will be left untouched. - */ -static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path, - struct dfs_info3_param *referral) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *fake_devname = NULL, *mdata = NULL; - - mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral, - &fake_devname); - if (IS_ERR(mdata)) { - rc = PTR_ERR(mdata); - mdata = NULL; - } else { - /* - * We can not clear out the whole structure since we no longer have an explicit - * function to parse a mount-string. Instead we need to clear out the individual - * fields that are no longer valid. - */ - kfree(ctx->prepath); - ctx->prepath = NULL; - rc = cifs_setup_volume_info(ctx, mdata, fake_devname); - } - kfree(fake_devname); - kfree(cifs_sb->ctx->mount_options); - cifs_sb->ctx->mount_options = mdata; - - return rc; -} -#endif - -/* TODO: all callers to this are broken. We are not parsing mount_options here - * we should pass a clone of the original context? - */ -int -cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname) -{ - int rc; - - if (devname) { - cifs_dbg(FYI, "%s: devname=%s\n", __func__, devname); - rc = smb3_parse_devname(devname, ctx); - if (rc) { - cifs_dbg(VFS, "%s: failed to parse %s: %d\n", __func__, devname, rc); - return rc; - } - } - - if (mntopts) { - char *ip; - - rc = smb3_parse_opt(mntopts, "ip", &ip); - if (rc) { - cifs_dbg(VFS, "%s: failed to parse ip options: %d\n", __func__, rc); - return rc; - } - - rc = cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip)); - kfree(ip); - if (!rc) { - cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__); - return -EINVAL; - } - } - - if (ctx->nullauth) { - cifs_dbg(FYI, "Anonymous login\n"); - kfree(ctx->username); - ctx->username = NULL; - } else if (ctx->username) { - /* BB fixme parse for domain name here */ - cifs_dbg(FYI, "Username: %s\n", ctx->username); - } else { - cifs_dbg(VFS, "No username specified\n"); - /* In userspace mount helper we can get user name from alternate - locations such as env variables and files on disk */ - return -EINVAL; - } - - return 0; -} - -static int -cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, - unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - char *full_path, - int added_treename) -{ - int rc; - char *s; - char sep, tmp; - int skip = added_treename ? 1 : 0; - - sep = CIFS_DIR_SEP(cifs_sb); - s = full_path; - - rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, ""); - while (rc == 0) { - /* skip separators */ - while (*s == sep) - s++; - if (!*s) - break; - /* next separator */ - while (*s && *s != sep) - s++; - /* - * if the treename is added, we then have to skip the first - * part within the separators - */ - if (skip) { - skip = 0; - continue; - } - /* - * temporarily null-terminate the path at the end of - * the current component - */ - tmp = *s; - *s = 0; - rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, - full_path); - *s = tmp; - } - return rc; -} - -/* - * Check if path is remote (i.e. a DFS share). - * - * Return -EREMOTE if it is, otherwise 0 or -errno. - */ -static int is_path_remote(struct mount_ctx *mnt_ctx) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct TCP_Server_Info *server = mnt_ctx->server; - unsigned int xid = mnt_ctx->xid; - struct cifs_tcon *tcon = mnt_ctx->tcon; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *full_path; - - if (!server->ops->is_path_accessible) - return -EOPNOTSUPP; - - /* - * cifs_build_path_to_root works only when we have a valid tcon - */ - full_path = cifs_build_path_to_root(ctx, cifs_sb, tcon, - tcon->Flags & SMB_SHARE_IS_IN_DFS); - if (full_path == NULL) - return -ENOMEM; - - cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); - - rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, - full_path); - if (rc != 0 && rc != -EREMOTE) - goto out; - - if (rc != -EREMOTE) { - rc = cifs_are_all_path_components_accessible(server, xid, tcon, - cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS); - if (rc != 0) { - cifs_server_dbg(VFS, "cannot query dirs between root and final path, enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - rc = 0; - } - } - -out: - kfree(full_path); - return rc; -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -static void set_root_ses(struct mount_ctx *mnt_ctx) -{ - if (mnt_ctx->ses) { - spin_lock(&cifs_tcp_ses_lock); - mnt_ctx->ses->ses_count++; - spin_unlock(&cifs_tcp_ses_lock); - dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); - } - mnt_ctx->root_ses = mnt_ctx->ses; -} - -static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - - *isdfs = true; - - rc = mount_get_conns(mnt_ctx); - /* - * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally - * try to get an DFS referral (even cached) to determine whether it is an DFS mount. - * - * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem - * to respond with PATH_NOT_COVERED to requests that include the prefix. - */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || - dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), - ctx->UNC + 1, NULL, root_tl)) { - if (rc) - return rc; - /* Check if it is fully accessible and then mount it */ - rc = is_path_remote(mnt_ctx); - if (!rc) - *isdfs = false; - else if (rc != -EREMOTE) - return rc; - } - return 0; -} - -static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path, - const char *ref_path, struct dfs_cache_tgt_iterator *tit) -{ - int rc; - struct dfs_info3_param ref = {}; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - char *oldmnt = cifs_sb->ctx->mount_options; - - cifs_dbg(FYI, "%s: full_path=%s ref_path=%s target=%s\n", __func__, full_path, ref_path, - dfs_cache_get_tgt_name(tit)); - - rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref); - if (rc) - goto out; - - rc = expand_dfs_referral(mnt_ctx, full_path, &ref); - if (rc) - goto out; - - /* Connect to new target only if we were redirected (e.g. mount options changed) */ - if (oldmnt != cifs_sb->ctx->mount_options) { - mount_put_conns(mnt_ctx); - rc = mount_get_dfs_conns(mnt_ctx); - } - if (!rc) { - if (cifs_is_referral_server(mnt_ctx->tcon, &ref)) - set_root_ses(mnt_ctx); - rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, - cifs_remap(cifs_sb), ref_path, tit); - } - -out: - free_dfs_info_param(&ref); - return rc; -} - -static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl) -{ - int rc; - char *full_path; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - struct dfs_cache_tgt_iterator *tit; - - /* Put initial connections as they might be shared with other mounts. We need unique dfs - * connections per mount to properly failover, so mount_get_dfs_conns() must be used from - * now on. - */ - mount_put_conns(mnt_ctx); - mount_get_dfs_conns(mnt_ctx); - set_root_ses(mnt_ctx); - - full_path = build_unc_path_to_root(ctx, cifs_sb, true); - if (IS_ERR(full_path)) - return PTR_ERR(full_path); - - mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (IS_ERR(mnt_ctx->origin_fullpath)) { - rc = PTR_ERR(mnt_ctx->origin_fullpath); - mnt_ctx->origin_fullpath = NULL; - goto out; - } - - /* Try all dfs root targets */ - for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl); - tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) { - rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit); - if (!rc) { - mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL); - if (!mnt_ctx->leaf_fullpath) - rc = -ENOMEM; - break; - } - } - -out: - kfree(full_path); - return rc; -} - -static int __follow_dfs_link(struct mount_ctx *mnt_ctx) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *full_path; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - struct dfs_cache_tgt_iterator *tit; - - full_path = build_unc_path_to_root(ctx, cifs_sb, true); - if (IS_ERR(full_path)) - return PTR_ERR(full_path); - - kfree(mnt_ctx->leaf_fullpath); - mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (IS_ERR(mnt_ctx->leaf_fullpath)) { - rc = PTR_ERR(mnt_ctx->leaf_fullpath); - mnt_ctx->leaf_fullpath = NULL; - goto out; - } - - /* Get referral from dfs link */ - rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, - cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl); - if (rc) - goto out; - - /* Try all dfs link targets. If an I/O fails from currently connected DFS target with an - * error other than STATUS_PATH_NOT_COVERED (-EREMOTE), then retry it from other targets as - * specified in MS-DFSC "3.1.5.2 I/O Operation to Target Fails with an Error Other Than - * STATUS_PATH_NOT_COVERED." - */ - for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl); - tit; tit = dfs_cache_get_next_tgt(&tl, tit)) { - rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit); - if (!rc) { - rc = is_path_remote(mnt_ctx); - if (!rc || rc == -EREMOTE) - break; - } - } - -out: - kfree(full_path); - dfs_cache_free_tgts(&tl); - return rc; -} - -static int follow_dfs_link(struct mount_ctx *mnt_ctx) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *full_path; - int num_links = 0; - - full_path = build_unc_path_to_root(ctx, cifs_sb, true); - if (IS_ERR(full_path)) - return PTR_ERR(full_path); - - kfree(mnt_ctx->origin_fullpath); - mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - kfree(full_path); - - if (IS_ERR(mnt_ctx->origin_fullpath)) { - rc = PTR_ERR(mnt_ctx->origin_fullpath); - mnt_ctx->origin_fullpath = NULL; - return rc; - } - - do { - rc = __follow_dfs_link(mnt_ctx); - if (!rc || rc != -EREMOTE) - break; - } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); - - return rc; -} - -/* Set up DFS referral paths for failover */ -static void setup_server_referral_paths(struct mount_ctx *mnt_ctx) -{ - struct TCP_Server_Info *server = mnt_ctx->server; - - mutex_lock(&server->refpath_lock); - server->origin_fullpath = mnt_ctx->origin_fullpath; - server->leaf_fullpath = mnt_ctx->leaf_fullpath; - server->current_fullpath = mnt_ctx->leaf_fullpath; - mutex_unlock(&server->refpath_lock); - mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL; -} - -int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - int rc; - struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - bool isdfs; - - rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl); - if (rc) - goto error; - if (!isdfs) - goto out; - - /* proceed as DFS mount */ - uuid_gen(&mnt_ctx.mount_id); - rc = connect_dfs_root(&mnt_ctx, &tl); - dfs_cache_free_tgts(&tl); - - if (rc) - goto error; - - rc = is_path_remote(&mnt_ctx); - if (rc) - rc = follow_dfs_link(&mnt_ctx); - if (rc) - goto error; - - setup_server_referral_paths(&mnt_ctx); - /* - * After reconnecting to a different server, unique ids won't match anymore, so we disable - * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). - */ - cifs_autodisable_serverino(cifs_sb); - /* - * Force the use of prefix path to support failover on DFS paths that resolve to targets - * that have different prefix paths. - */ - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - kfree(cifs_sb->prepath); - cifs_sb->prepath = ctx->prepath; - ctx->prepath = NULL; - uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id); - -out: - cifs_try_adding_channels(cifs_sb, mnt_ctx.ses); - rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); - if (rc) - goto error; - - free_xid(mnt_ctx.xid); - return rc; - -error: - dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); - kfree(mnt_ctx.origin_fullpath); - kfree(mnt_ctx.leaf_fullpath); - mount_put_conns(&mnt_ctx); - return rc; -} -#else -int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - int rc = 0; - struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; - - rc = mount_get_conns(&mnt_ctx); - if (rc) - goto error; - - if (mnt_ctx.tcon) { - rc = is_path_remote(&mnt_ctx); - if (rc == -EREMOTE) - rc = -EOPNOTSUPP; - if (rc) - goto error; - } - - rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); - if (rc) - goto error; - - free_xid(mnt_ctx.xid); - return rc; - -error: - mount_put_conns(&mnt_ctx); - return rc; -} -#endif - -/* - * Issue a TREE_CONNECT request. - */ -int -CIFSTCon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *nls_codepage) -{ - struct smb_hdr *smb_buffer; - struct smb_hdr *smb_buffer_response; - TCONX_REQ *pSMB; - TCONX_RSP *pSMBr; - unsigned char *bcc_ptr; - int rc = 0; - int length; - __u16 bytes_left, count; - - if (ses == NULL) - return -EIO; - - smb_buffer = cifs_buf_get(); - if (smb_buffer == NULL) - return -ENOMEM; - - smb_buffer_response = smb_buffer; - - header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, - NULL /*no tid */ , 4 /*wct */ ); - - smb_buffer->Mid = get_next_mid(ses->server); - smb_buffer->Uid = ses->Suid; - pSMB = (TCONX_REQ *) smb_buffer; - pSMBr = (TCONX_RSP *) smb_buffer_response; - - pSMB->AndXCommand = 0xFF; - pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); - bcc_ptr = &pSMB->Password[0]; - - pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ - *bcc_ptr = 0; /* password is null byte */ - bcc_ptr++; /* skip password */ - /* already aligned so no need to do it below */ - - if (ses->server->sign) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - if (ses->capabilities & CAP_STATUS32) { - smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - } - if (ses->capabilities & CAP_DFS) { - smb_buffer->Flags2 |= SMBFLG2_DFS; - } - if (ses->capabilities & CAP_UNICODE) { - smb_buffer->Flags2 |= SMBFLG2_UNICODE; - length = - cifs_strtoUTF16((__le16 *) bcc_ptr, tree, - 6 /* max utf8 char length in bytes */ * - (/* server len*/ + 256 /* share len */), nls_codepage); - bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ - bcc_ptr += 2; /* skip trailing null */ - } else { /* ASCII */ - strcpy(bcc_ptr, tree); - bcc_ptr += strlen(tree) + 1; - } - strcpy(bcc_ptr, "?????"); - bcc_ptr += strlen("?????"); - bcc_ptr += 1; - count = bcc_ptr - &pSMB->Password[0]; - be32_add_cpu(&pSMB->hdr.smb_buf_length, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, - 0); - - /* above now done in SendReceive */ - if (rc == 0) { - bool is_unicode; - - tcon->tid = smb_buffer_response->Tid; - bcc_ptr = pByteArea(smb_buffer_response); - bytes_left = get_bcc(smb_buffer_response); - length = strnlen(bcc_ptr, bytes_left - 2); - if (smb_buffer->Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - - /* skip service field (NB: this field is always ASCII) */ - if (length == 3) { - if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && - (bcc_ptr[2] == 'C')) { - cifs_dbg(FYI, "IPC connection\n"); - tcon->ipc = true; - tcon->pipe = true; - } - } else if (length == 2) { - if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) { - /* the most common case */ - cifs_dbg(FYI, "disk share connection\n"); - } - } - bcc_ptr += length + 1; - bytes_left -= (length + 1); - strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); - - /* mostly informational -- no need to fail on error here */ - kfree(tcon->nativeFileSystem); - tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr, - bytes_left, is_unicode, - nls_codepage); - - cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem); - - if ((smb_buffer_response->WordCount == 3) || - (smb_buffer_response->WordCount == 7)) - /* field is in same location */ - tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); - else - tcon->Flags = 0; - cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags); - } - - cifs_buf_release(smb_buffer); - return rc; -} - -static void delayed_free(struct rcu_head *p) -{ - struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, rcu); - - unload_nls(cifs_sb->local_nls); - smb3_cleanup_fs_context(cifs_sb->ctx); - kfree(cifs_sb); -} - -void -cifs_umount(struct cifs_sb_info *cifs_sb) -{ - struct rb_root *root = &cifs_sb->tlink_tree; - struct rb_node *node; - struct tcon_link *tlink; - - cancel_delayed_work_sync(&cifs_sb->prune_tlinks); - - spin_lock(&cifs_sb->tlink_tree_lock); - while ((node = rb_first(root))) { - tlink = rb_entry(node, struct tcon_link, tl_rbnode); - cifs_get_tlink(tlink); - clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); - rb_erase(node, root); - - spin_unlock(&cifs_sb->tlink_tree_lock); - cifs_put_tlink(tlink); - spin_lock(&cifs_sb->tlink_tree_lock); - } - spin_unlock(&cifs_sb->tlink_tree_lock); - - kfree(cifs_sb->prepath); -#ifdef CONFIG_CIFS_DFS_UPCALL - dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id); -#endif - call_rcu(&cifs_sb->rcu, delayed_free); -} - -int -cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - int rc = 0; - - if (!server->ops->need_neg || !server->ops->negotiate) - return -ENOSYS; - - /* only send once per connect */ - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsGood && - server->tcpStatus != CifsNew && - server->tcpStatus != CifsNeedNegotiate) { - spin_unlock(&server->srv_lock); - return -EHOSTDOWN; - } - - if (!server->ops->need_neg(server) && - server->tcpStatus == CifsGood) { - spin_unlock(&server->srv_lock); - return 0; - } - - server->tcpStatus = CifsInNegotiate; - spin_unlock(&server->srv_lock); - - rc = server->ops->negotiate(xid, ses, server); - if (rc == 0) { - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsInNegotiate) - server->tcpStatus = CifsGood; - else - rc = -EHOSTDOWN; - spin_unlock(&server->srv_lock); - } else { - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsInNegotiate) - server->tcpStatus = CifsNeedNegotiate; - spin_unlock(&server->srv_lock); - } - - return rc; -} - -int -cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct nls_table *nls_info) -{ - int rc = -ENOSYS; - struct TCP_Server_Info *pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr; - struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr; - bool is_binding = false; - - spin_lock(&ses->ses_lock); - cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", - __func__, ses->chans_need_reconnect); - - if (ses->ses_status != SES_GOOD && - ses->ses_status != SES_NEW && - ses->ses_status != SES_NEED_RECON) { - spin_unlock(&ses->ses_lock); - return -EHOSTDOWN; - } - - /* only send once per connect */ - spin_lock(&ses->chan_lock); - if (CIFS_ALL_CHANS_GOOD(ses)) { - if (ses->ses_status == SES_NEED_RECON) - ses->ses_status = SES_GOOD; - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - return 0; - } - - cifs_chan_set_in_reconnect(ses, server); - is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); - spin_unlock(&ses->chan_lock); - - if (!is_binding) - ses->ses_status = SES_IN_SETUP; - spin_unlock(&ses->ses_lock); - - /* update ses ip_addr only for primary chan */ - if (server == pserver) { - if (server->dstaddr.ss_family == AF_INET6) - scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr); - else - scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr); - } - - if (!is_binding) { - ses->capabilities = server->capabilities; - if (!linuxExtEnabled) - ses->capabilities &= (~server->vals->cap_unix); - - if (ses->auth_key.response) { - cifs_dbg(FYI, "Free previous auth_key.response = %p\n", - ses->auth_key.response); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - ses->auth_key.len = 0; - } - } - - cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", - server->sec_mode, server->capabilities, server->timeAdj); - - if (server->ops->sess_setup) - rc = server->ops->sess_setup(xid, ses, server, nls_info); - - if (rc) { - cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc); - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_IN_SETUP) - ses->ses_status = SES_NEED_RECON; - spin_lock(&ses->chan_lock); - cifs_chan_clear_in_reconnect(ses, server); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - } else { - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_IN_SETUP) - ses->ses_status = SES_GOOD; - spin_lock(&ses->chan_lock); - cifs_chan_clear_in_reconnect(ses, server); - cifs_chan_clear_need_reconnect(ses, server); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - } - - return rc; -} - -static int -cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) -{ - ctx->sectype = ses->sectype; - - /* krb5 is special, since we don't need username or pw */ - if (ctx->sectype == Kerberos) - return 0; - - return cifs_set_cifscreds(ctx, ses); -} - -static struct cifs_tcon * -cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) -{ - int rc; - struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); - struct cifs_ses *ses; - struct cifs_tcon *tcon = NULL; - struct smb3_fs_context *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (ctx == NULL) - return ERR_PTR(-ENOMEM); - - ctx->local_nls = cifs_sb->local_nls; - ctx->linux_uid = fsuid; - ctx->cred_uid = fsuid; - ctx->UNC = master_tcon->tree_name; - ctx->retry = master_tcon->retry; - ctx->nocase = master_tcon->nocase; - ctx->nohandlecache = master_tcon->nohandlecache; - ctx->local_lease = master_tcon->local_lease; - ctx->no_lease = master_tcon->no_lease; - ctx->resilient = master_tcon->use_resilient; - ctx->persistent = master_tcon->use_persistent; - ctx->handle_timeout = master_tcon->handle_timeout; - ctx->no_linux_ext = !master_tcon->unix_ext; - ctx->linux_ext = master_tcon->posix_extensions; - ctx->sectype = master_tcon->ses->sectype; - ctx->sign = master_tcon->ses->sign; - ctx->seal = master_tcon->seal; - ctx->witness = master_tcon->use_witness; - - rc = cifs_set_vol_auth(ctx, master_tcon->ses); - if (rc) { - tcon = ERR_PTR(rc); - goto out; - } - - /* get a reference for the same TCP session */ - spin_lock(&cifs_tcp_ses_lock); - ++master_tcon->ses->server->srv_count; - spin_unlock(&cifs_tcp_ses_lock); - - ses = cifs_get_smb_ses(master_tcon->ses->server, ctx); - if (IS_ERR(ses)) { - tcon = (struct cifs_tcon *)ses; - cifs_put_tcp_session(master_tcon->ses->server, 0); - goto out; - } - - tcon = cifs_get_tcon(ses, ctx); - if (IS_ERR(tcon)) { - cifs_put_smb_ses(ses); - goto out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(ses)) - reset_cifs_unix_caps(0, tcon, NULL, ctx); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -out: - kfree(ctx->username); - kfree_sensitive(ctx->password); - kfree(ctx); - - return tcon; -} - -struct cifs_tcon * -cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) -{ - return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); -} - -/* find and return a tlink with given uid */ -static struct tcon_link * -tlink_rb_search(struct rb_root *root, kuid_t uid) -{ - struct rb_node *node = root->rb_node; - struct tcon_link *tlink; - - while (node) { - tlink = rb_entry(node, struct tcon_link, tl_rbnode); - - if (uid_gt(tlink->tl_uid, uid)) - node = node->rb_left; - else if (uid_lt(tlink->tl_uid, uid)) - node = node->rb_right; - else - return tlink; - } - return NULL; -} - -/* insert a tcon_link into the tree */ -static void -tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) -{ - struct rb_node **new = &(root->rb_node), *parent = NULL; - struct tcon_link *tlink; - - while (*new) { - tlink = rb_entry(*new, struct tcon_link, tl_rbnode); - parent = *new; - - if (uid_gt(tlink->tl_uid, new_tlink->tl_uid)) - new = &((*new)->rb_left); - else - new = &((*new)->rb_right); - } - - rb_link_node(&new_tlink->tl_rbnode, parent, new); - rb_insert_color(&new_tlink->tl_rbnode, root); -} - -/* - * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the - * current task. - * - * If the superblock doesn't refer to a multiuser mount, then just return - * the master tcon for the mount. - * - * First, search the rbtree for an existing tcon for this fsuid. If one - * exists, then check to see if it's pending construction. If it is then wait - * for construction to complete. Once it's no longer pending, check to see if - * it failed and either return an error or retry construction, depending on - * the timeout. - * - * If one doesn't exist then insert a new tcon_link struct into the tree and - * try to construct a new one. - */ -struct tcon_link * -cifs_sb_tlink(struct cifs_sb_info *cifs_sb) -{ - int ret; - kuid_t fsuid = current_fsuid(); - struct tcon_link *tlink, *newtlink; - - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) - return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); - - spin_lock(&cifs_sb->tlink_tree_lock); - tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); - if (tlink) - cifs_get_tlink(tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - - if (tlink == NULL) { - newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); - if (newtlink == NULL) - return ERR_PTR(-ENOMEM); - newtlink->tl_uid = fsuid; - newtlink->tl_tcon = ERR_PTR(-EACCES); - set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); - set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); - cifs_get_tlink(newtlink); - - spin_lock(&cifs_sb->tlink_tree_lock); - /* was one inserted after previous search? */ - tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); - if (tlink) { - cifs_get_tlink(tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - kfree(newtlink); - goto wait_for_construction; - } - tlink = newtlink; - tlink_rb_insert(&cifs_sb->tlink_tree, tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - } else { -wait_for_construction: - ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, - TASK_INTERRUPTIBLE); - if (ret) { - cifs_put_tlink(tlink); - return ERR_PTR(-ERESTARTSYS); - } - - /* if it's good, return it */ - if (!IS_ERR(tlink->tl_tcon)) - return tlink; - - /* return error if we tried this already recently */ - if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { - cifs_put_tlink(tlink); - return ERR_PTR(-EACCES); - } - - if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) - goto wait_for_construction; - } - - tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); - clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); - wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); - - if (IS_ERR(tlink->tl_tcon)) { - cifs_put_tlink(tlink); - return ERR_PTR(-EACCES); - } - - return tlink; -} - -/* - * periodic workqueue job that scans tcon_tree for a superblock and closes - * out tcons. - */ -static void -cifs_prune_tlinks(struct work_struct *work) -{ - struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, - prune_tlinks.work); - struct rb_root *root = &cifs_sb->tlink_tree; - struct rb_node *node; - struct rb_node *tmp; - struct tcon_link *tlink; - - /* - * Because we drop the spinlock in the loop in order to put the tlink - * it's not guarded against removal of links from the tree. The only - * places that remove entries from the tree are this function and - * umounts. Because this function is non-reentrant and is canceled - * before umount can proceed, this is safe. - */ - spin_lock(&cifs_sb->tlink_tree_lock); - node = rb_first(root); - while (node != NULL) { - tmp = node; - node = rb_next(tmp); - tlink = rb_entry(tmp, struct tcon_link, tl_rbnode); - - if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) || - atomic_read(&tlink->tl_count) != 0 || - time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies)) - continue; - - cifs_get_tlink(tlink); - clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); - rb_erase(tmp, root); - - spin_unlock(&cifs_sb->tlink_tree_lock); - cifs_put_tlink(tlink); - spin_lock(&cifs_sb->tlink_tree_lock); - } - spin_unlock(&cifs_sb->tlink_tree_lock); - - queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, - TLINK_IDLE_EXPIRE); -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -/* Update dfs referral path of superblock */ -static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, - const char *target) -{ - int rc = 0; - size_t len = strlen(target); - char *refpath, *npath; - - if (unlikely(len < 2 || *target != '\\')) - return -EINVAL; - - if (target[1] == '\\') { - len += 1; - refpath = kmalloc(len, GFP_KERNEL); - if (!refpath) - return -ENOMEM; - - scnprintf(refpath, len, "%s", target); - } else { - len += sizeof("\\"); - refpath = kmalloc(len, GFP_KERNEL); - if (!refpath) - return -ENOMEM; - - scnprintf(refpath, len, "\\%s", target); - } - - npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); - kfree(refpath); - - if (IS_ERR(npath)) { - rc = PTR_ERR(npath); - } else { - mutex_lock(&server->refpath_lock); - kfree(server->leaf_fullpath); - server->leaf_fullpath = npath; - mutex_unlock(&server->refpath_lock); - server->current_fullpath = server->leaf_fullpath; - } - return rc; -} - -static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host, - size_t tcp_host_len, char *share, bool *target_match) -{ - int rc = 0; - const char *dfs_host; - size_t dfs_host_len; - - *target_match = true; - extract_unc_hostname(share, &dfs_host, &dfs_host_len); - - /* Check if hostnames or addresses match */ - if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { - cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, - dfs_host, (int)tcp_host_len, tcp_host); - rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); - if (rc) - cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); - } - return rc; -} - -static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, char *tree, bool islink, - struct dfs_cache_tgt_list *tl) -{ - int rc; - struct TCP_Server_Info *server = tcon->ses->server; - const struct smb_version_operations *ops = server->ops; - struct cifs_tcon *ipc = tcon->ses->tcon_ipc; - char *share = NULL, *prefix = NULL; - const char *tcp_host; - size_t tcp_host_len; - struct dfs_cache_tgt_iterator *tit; - bool target_match; - - extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); - - tit = dfs_cache_get_tgt_iterator(tl); - if (!tit) { - rc = -ENOENT; - goto out; - } - - /* Try to tree connect to all dfs targets */ - for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { - const char *target = dfs_cache_get_tgt_name(tit); - struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); - - kfree(share); - kfree(prefix); - share = prefix = NULL; - - /* Check if share matches with tcp ses */ - rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix); - if (rc) { - cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); - break; - } - - rc = target_share_matches_server(server, tcp_host, tcp_host_len, share, - &target_match); - if (rc) - break; - if (!target_match) { - rc = -EHOSTUNREACH; - continue; - } - - if (ipc->need_reconnect) { - scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); - rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls); - if (rc) - break; - } - - scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); - if (!islink) { - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); - break; - } - /* - * If no dfs referrals were returned from link target, then just do a TREE_CONNECT - * to it. Otherwise, cache the dfs referral and then mark current tcp ses for - * reconnect so either the demultiplex thread or the echo worker will reconnect to - * newly resolved target. - */ - if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, - NULL, &ntl)) { - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); - if (rc) - continue; - rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit); - if (!rc) - rc = cifs_update_super_prepath(cifs_sb, prefix); - } else { - /* Target is another dfs share */ - rc = update_server_fullpath(server, cifs_sb, target); - dfs_cache_free_tgts(tl); - - if (!rc) { - rc = -EREMOTE; - list_replace_init(&ntl.tl_list, &tl->tl_list); - } else - dfs_cache_free_tgts(&ntl); - } - break; - } - -out: - kfree(share); - kfree(prefix); - - return rc; -} - -static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, char *tree, bool islink, - struct dfs_cache_tgt_list *tl) -{ - int rc; - int num_links = 0; - struct TCP_Server_Info *server = tcon->ses->server; - - do { - rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); - if (!rc || rc != -EREMOTE) - break; - } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); - /* - * If we couldn't tree connect to any targets from last referral path, then retry from - * original referral path. - */ - if (rc && server->current_fullpath != server->origin_fullpath) { - server->current_fullpath = server->origin_fullpath; - cifs_signal_cifsd_for_reconnect(server, true); - } - - dfs_cache_free_tgts(tl); - return rc; -} - -int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) -{ - int rc; - struct TCP_Server_Info *server = tcon->ses->server; - const struct smb_version_operations *ops = server->ops; - struct super_block *sb = NULL; - struct cifs_sb_info *cifs_sb; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - char *tree; - struct dfs_info3_param ref = {0}; - - /* only send once per connect */ - spin_lock(&tcon->tc_lock); - if (tcon->ses->ses_status != SES_GOOD || - (tcon->status != TID_NEW && - tcon->status != TID_NEED_TCON)) { - spin_unlock(&tcon->tc_lock); - return 0; - } - tcon->status = TID_IN_TCON; - spin_unlock(&tcon->tc_lock); - - tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); - if (!tree) { - rc = -ENOMEM; - goto out; - } - - if (tcon->ipc) { - scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); - goto out; - } - - sb = cifs_get_tcp_super(server); - if (IS_ERR(sb)) { - rc = PTR_ERR(sb); - cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); - goto out; - } - - cifs_sb = CIFS_SB(sb); - - /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ - if (!server->current_fullpath || - dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) { - rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls); - goto out; - } - - rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, - &tl); - free_dfs_info_param(&ref); - -out: - kfree(tree); - cifs_put_tcp_super(sb); - - if (rc) { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_NEED_TCON; - spin_unlock(&tcon->tc_lock); - } else { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_GOOD; - spin_unlock(&tcon->tc_lock); - tcon->need_reconnect = false; - } - - return rc; -} -#else -int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) -{ - int rc; - const struct smb_version_operations *ops = tcon->ses->server->ops; - - /* only send once per connect */ - spin_lock(&tcon->tc_lock); - if (tcon->ses->ses_status != SES_GOOD || - (tcon->status != TID_NEW && - tcon->status != TID_NEED_TCON)) { - spin_unlock(&tcon->tc_lock); - return 0; - } - tcon->status = TID_IN_TCON; - spin_unlock(&tcon->tc_lock); - - rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc); - if (rc) { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_NEED_TCON; - spin_unlock(&tcon->tc_lock); - } else { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_GOOD; - tcon->need_reconnect = false; - spin_unlock(&tcon->tc_lock); - } - - return rc; -} -#endif diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c deleted file mode 100644 index 3bc1d3494be3..000000000000 --- a/fs/cifs/dfs_cache.c +++ /dev/null @@ -1,1690 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * DFS referral cache routines - * - * Copyright (c) 2018-2019 Paulo Alcantara - */ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "smb2glob.h" -#include "dns_resolve.h" - -#include "dfs_cache.h" - -#define CACHE_HTABLE_SIZE 32 -#define CACHE_MAX_ENTRIES 64 -#define CACHE_MIN_TTL 120 /* 2 minutes */ - -#define IS_DFS_INTERLINK(v) (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER)) - -struct cache_dfs_tgt { - char *name; - int path_consumed; - struct list_head list; -}; - -struct cache_entry { - struct hlist_node hlist; - const char *path; - int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */ - int ttl; /* DFS_REREFERRAL_V3.TimeToLive */ - int srvtype; /* DFS_REREFERRAL_V3.ServerType */ - int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */ - struct timespec64 etime; - int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */ - int numtgts; - struct list_head tlist; - struct cache_dfs_tgt *tgthint; -}; - -/* List of referral server sessions per dfs mount */ -struct mount_group { - struct list_head list; - uuid_t id; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES]; - int num_sessions; - spinlock_t lock; - struct list_head refresh_list; - struct kref refcount; -}; - -static struct kmem_cache *cache_slab __read_mostly; -static struct workqueue_struct *dfscache_wq __read_mostly; - -static int cache_ttl; -static DEFINE_SPINLOCK(cache_ttl_lock); - -static struct nls_table *cache_cp; - -/* - * Number of entries in the cache - */ -static atomic_t cache_count; - -static struct hlist_head cache_htable[CACHE_HTABLE_SIZE]; -static DECLARE_RWSEM(htable_rw_lock); - -static LIST_HEAD(mount_group_list); -static DEFINE_MUTEX(mount_group_list_lock); - -static void refresh_cache_worker(struct work_struct *work); - -static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker); - -static void get_ipc_unc(const char *ref_path, char *ipc, size_t ipclen) -{ - const char *host; - size_t len; - - extract_unc_hostname(ref_path, &host, &len); - scnprintf(ipc, ipclen, "\\\\%.*s\\IPC$", (int)len, host); -} - -static struct cifs_ses *find_ipc_from_server_path(struct cifs_ses **ses, const char *path) -{ - char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; - - get_ipc_unc(path, unc, sizeof(unc)); - for (; *ses; ses++) { - if (!strcasecmp(unc, (*ses)->tcon_ipc->tree_name)) - return *ses; - } - return ERR_PTR(-ENOENT); -} - -static void __mount_group_release(struct mount_group *mg) -{ - int i; - - for (i = 0; i < mg->num_sessions; i++) - cifs_put_smb_ses(mg->sessions[i]); - kfree(mg); -} - -static void mount_group_release(struct kref *kref) -{ - struct mount_group *mg = container_of(kref, struct mount_group, refcount); - - mutex_lock(&mount_group_list_lock); - list_del(&mg->list); - mutex_unlock(&mount_group_list_lock); - __mount_group_release(mg); -} - -static struct mount_group *find_mount_group_locked(const uuid_t *id) -{ - struct mount_group *mg; - - list_for_each_entry(mg, &mount_group_list, list) { - if (uuid_equal(&mg->id, id)) - return mg; - } - return ERR_PTR(-ENOENT); -} - -static struct mount_group *__get_mount_group_locked(const uuid_t *id) -{ - struct mount_group *mg; - - mg = find_mount_group_locked(id); - if (!IS_ERR(mg)) - return mg; - - mg = kmalloc(sizeof(*mg), GFP_KERNEL); - if (!mg) - return ERR_PTR(-ENOMEM); - kref_init(&mg->refcount); - uuid_copy(&mg->id, id); - mg->num_sessions = 0; - spin_lock_init(&mg->lock); - list_add(&mg->list, &mount_group_list); - return mg; -} - -static struct mount_group *get_mount_group(const uuid_t *id) -{ - struct mount_group *mg; - - mutex_lock(&mount_group_list_lock); - mg = __get_mount_group_locked(id); - if (!IS_ERR(mg)) - kref_get(&mg->refcount); - mutex_unlock(&mount_group_list_lock); - - return mg; -} - -static void free_mount_group_list(void) -{ - struct mount_group *mg, *tmp_mg; - - list_for_each_entry_safe(mg, tmp_mg, &mount_group_list, list) { - list_del_init(&mg->list); - __mount_group_release(mg); - } -} - -/** - * dfs_cache_canonical_path - get a canonical DFS path - * - * @path: DFS path - * @cp: codepage - * @remap: mapping type - * - * Return canonical path if success, otherwise error. - */ -char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap) -{ - char *tmp; - int plen = 0; - char *npath; - - if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/')) - return ERR_PTR(-EINVAL); - - if (unlikely(strcmp(cp->charset, cache_cp->charset))) { - tmp = (char *)cifs_strndup_to_utf16(path, strlen(path), &plen, cp, remap); - if (!tmp) { - cifs_dbg(VFS, "%s: failed to convert path to utf16\n", __func__); - return ERR_PTR(-EINVAL); - } - - npath = cifs_strndup_from_utf16(tmp, plen, true, cache_cp); - kfree(tmp); - - if (!npath) { - cifs_dbg(VFS, "%s: failed to convert path from utf16\n", __func__); - return ERR_PTR(-EINVAL); - } - } else { - npath = kstrdup(path, GFP_KERNEL); - if (!npath) - return ERR_PTR(-ENOMEM); - } - convert_delimiter(npath, '\\'); - return npath; -} - -static inline bool cache_entry_expired(const struct cache_entry *ce) -{ - struct timespec64 ts; - - ktime_get_coarse_real_ts64(&ts); - return timespec64_compare(&ts, &ce->etime) >= 0; -} - -static inline void free_tgts(struct cache_entry *ce) -{ - struct cache_dfs_tgt *t, *n; - - list_for_each_entry_safe(t, n, &ce->tlist, list) { - list_del(&t->list); - kfree(t->name); - kfree(t); - } -} - -static inline void flush_cache_ent(struct cache_entry *ce) -{ - hlist_del_init(&ce->hlist); - kfree(ce->path); - free_tgts(ce); - atomic_dec(&cache_count); - kmem_cache_free(cache_slab, ce); -} - -static void flush_cache_ents(void) -{ - int i; - - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - struct hlist_node *n; - struct cache_entry *ce; - - hlist_for_each_entry_safe(ce, n, l, hlist) { - if (!hlist_unhashed(&ce->hlist)) - flush_cache_ent(ce); - } - } -} - -/* - * dfs cache /proc file - */ -static int dfscache_proc_show(struct seq_file *m, void *v) -{ - int i; - struct cache_entry *ce; - struct cache_dfs_tgt *t; - - seq_puts(m, "DFS cache\n---------\n"); - - down_read(&htable_rw_lock); - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - - hlist_for_each_entry(ce, l, hlist) { - if (hlist_unhashed(&ce->hlist)) - continue; - - seq_printf(m, - "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", - ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", - ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, - IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", - ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); - - list_for_each_entry(t, &ce->tlist, list) { - seq_printf(m, " %s%s\n", - t->name, - ce->tgthint == t ? " (target hint)" : ""); - } - } - } - up_read(&htable_rw_lock); - - return 0; -} - -static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - char c; - int rc; - - rc = get_user(c, buffer); - if (rc) - return rc; - - if (c != '0') - return -EINVAL; - - cifs_dbg(FYI, "clearing dfs cache\n"); - - down_write(&htable_rw_lock); - flush_cache_ents(); - up_write(&htable_rw_lock); - - return count; -} - -static int dfscache_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, dfscache_proc_show, NULL); -} - -const struct proc_ops dfscache_proc_ops = { - .proc_open = dfscache_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = dfscache_proc_write, -}; - -#ifdef CONFIG_CIFS_DEBUG2 -static inline void dump_tgts(const struct cache_entry *ce) -{ - struct cache_dfs_tgt *t; - - cifs_dbg(FYI, "target list:\n"); - list_for_each_entry(t, &ce->tlist, list) { - cifs_dbg(FYI, " %s%s\n", t->name, - ce->tgthint == t ? " (target hint)" : ""); - } -} - -static inline void dump_ce(const struct cache_entry *ce) -{ - cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", - ce->path, - ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, - ce->etime.tv_nsec, - ce->hdr_flags, ce->ref_flags, - IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", - ce->path_consumed, - cache_entry_expired(ce) ? "yes" : "no"); - dump_tgts(ce); -} - -static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs) -{ - int i; - - cifs_dbg(FYI, "DFS referrals returned by the server:\n"); - for (i = 0; i < numrefs; i++) { - const struct dfs_info3_param *ref = &refs[i]; - - cifs_dbg(FYI, - "\n" - "flags: 0x%x\n" - "path_consumed: %d\n" - "server_type: 0x%x\n" - "ref_flag: 0x%x\n" - "path_name: %s\n" - "node_name: %s\n" - "ttl: %d (%dm)\n", - ref->flags, ref->path_consumed, ref->server_type, - ref->ref_flag, ref->path_name, ref->node_name, - ref->ttl, ref->ttl / 60); - } -} -#else -#define dump_tgts(e) -#define dump_ce(e) -#define dump_refs(r, n) -#endif - -/** - * dfs_cache_init - Initialize DFS referral cache. - * - * Return zero if initialized successfully, otherwise non-zero. - */ -int dfs_cache_init(void) -{ - int rc; - int i; - - dfscache_wq = alloc_workqueue("cifs-dfscache", WQ_FREEZABLE | WQ_UNBOUND, 1); - if (!dfscache_wq) - return -ENOMEM; - - cache_slab = kmem_cache_create("cifs_dfs_cache", - sizeof(struct cache_entry), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!cache_slab) { - rc = -ENOMEM; - goto out_destroy_wq; - } - - for (i = 0; i < CACHE_HTABLE_SIZE; i++) - INIT_HLIST_HEAD(&cache_htable[i]); - - atomic_set(&cache_count, 0); - cache_cp = load_nls("utf8"); - if (!cache_cp) - cache_cp = load_nls_default(); - - cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__); - return 0; - -out_destroy_wq: - destroy_workqueue(dfscache_wq); - return rc; -} - -static int cache_entry_hash(const void *data, int size, unsigned int *hash) -{ - int i, clen; - const unsigned char *s = data; - wchar_t c; - unsigned int h = 0; - - for (i = 0; i < size; i += clen) { - clen = cache_cp->char2uni(&s[i], size - i, &c); - if (unlikely(clen < 0)) { - cifs_dbg(VFS, "%s: can't convert char\n", __func__); - return clen; - } - c = cifs_toupper(c); - h = jhash(&c, sizeof(c), h); - } - *hash = h % CACHE_HTABLE_SIZE; - return 0; -} - -/* Return target hint of a DFS cache entry */ -static inline char *get_tgt_name(const struct cache_entry *ce) -{ - struct cache_dfs_tgt *t = ce->tgthint; - - return t ? t->name : ERR_PTR(-ENOENT); -} - -/* Return expire time out of a new entry's TTL */ -static inline struct timespec64 get_expire_time(int ttl) -{ - struct timespec64 ts = { - .tv_sec = ttl, - .tv_nsec = 0, - }; - struct timespec64 now; - - ktime_get_coarse_real_ts64(&now); - return timespec64_add(now, ts); -} - -/* Allocate a new DFS target */ -static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) -{ - struct cache_dfs_tgt *t; - - t = kmalloc(sizeof(*t), GFP_ATOMIC); - if (!t) - return ERR_PTR(-ENOMEM); - t->name = kstrdup(name, GFP_ATOMIC); - if (!t->name) { - kfree(t); - return ERR_PTR(-ENOMEM); - } - t->path_consumed = path_consumed; - INIT_LIST_HEAD(&t->list); - return t; -} - -/* - * Copy DFS referral information to a cache entry and conditionally update - * target hint. - */ -static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, - struct cache_entry *ce, const char *tgthint) -{ - int i; - - ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL); - ce->etime = get_expire_time(ce->ttl); - ce->srvtype = refs[0].server_type; - ce->hdr_flags = refs[0].flags; - ce->ref_flags = refs[0].ref_flag; - ce->path_consumed = refs[0].path_consumed; - - for (i = 0; i < numrefs; i++) { - struct cache_dfs_tgt *t; - - t = alloc_target(refs[i].node_name, refs[i].path_consumed); - if (IS_ERR(t)) { - free_tgts(ce); - return PTR_ERR(t); - } - if (tgthint && !strcasecmp(t->name, tgthint)) { - list_add(&t->list, &ce->tlist); - tgthint = NULL; - } else { - list_add_tail(&t->list, &ce->tlist); - } - ce->numtgts++; - } - - ce->tgthint = list_first_entry_or_null(&ce->tlist, - struct cache_dfs_tgt, list); - - return 0; -} - -/* Allocate a new cache entry */ -static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int numrefs) -{ - struct cache_entry *ce; - int rc; - - ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL); - if (!ce) - return ERR_PTR(-ENOMEM); - - ce->path = refs[0].path_name; - refs[0].path_name = NULL; - - INIT_HLIST_NODE(&ce->hlist); - INIT_LIST_HEAD(&ce->tlist); - - rc = copy_ref_data(refs, numrefs, ce, NULL); - if (rc) { - kfree(ce->path); - kmem_cache_free(cache_slab, ce); - ce = ERR_PTR(rc); - } - return ce; -} - -static void remove_oldest_entry_locked(void) -{ - int i; - struct cache_entry *ce; - struct cache_entry *to_del = NULL; - - WARN_ON(!rwsem_is_locked(&htable_rw_lock)); - - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - - hlist_for_each_entry(ce, l, hlist) { - if (hlist_unhashed(&ce->hlist)) - continue; - if (!to_del || timespec64_compare(&ce->etime, - &to_del->etime) < 0) - to_del = ce; - } - } - - if (!to_del) { - cifs_dbg(FYI, "%s: no entry to remove\n", __func__); - return; - } - - cifs_dbg(FYI, "%s: removing entry\n", __func__); - dump_ce(to_del); - flush_cache_ent(to_del); -} - -/* Add a new DFS cache entry */ -static int add_cache_entry_locked(struct dfs_info3_param *refs, int numrefs) -{ - int rc; - struct cache_entry *ce; - unsigned int hash; - - WARN_ON(!rwsem_is_locked(&htable_rw_lock)); - - if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { - cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES); - remove_oldest_entry_locked(); - } - - rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash); - if (rc) - return rc; - - ce = alloc_cache_entry(refs, numrefs); - if (IS_ERR(ce)) - return PTR_ERR(ce); - - spin_lock(&cache_ttl_lock); - if (!cache_ttl) { - cache_ttl = ce->ttl; - queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); - } else { - cache_ttl = min_t(int, cache_ttl, ce->ttl); - mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); - } - spin_unlock(&cache_ttl_lock); - - hlist_add_head(&ce->hlist, &cache_htable[hash]); - dump_ce(ce); - - atomic_inc(&cache_count); - - return 0; -} - -/* Check if two DFS paths are equal. @s1 and @s2 are expected to be in @cache_cp's charset */ -static bool dfs_path_equal(const char *s1, int len1, const char *s2, int len2) -{ - int i, l1, l2; - wchar_t c1, c2; - - if (len1 != len2) - return false; - - for (i = 0; i < len1; i += l1) { - l1 = cache_cp->char2uni(&s1[i], len1 - i, &c1); - l2 = cache_cp->char2uni(&s2[i], len2 - i, &c2); - if (unlikely(l1 < 0 && l2 < 0)) { - if (s1[i] != s2[i]) - return false; - l1 = 1; - continue; - } - if (l1 != l2) - return false; - if (cifs_toupper(c1) != cifs_toupper(c2)) - return false; - } - return true; -} - -static struct cache_entry *__lookup_cache_entry(const char *path, unsigned int hash, int len) -{ - struct cache_entry *ce; - - hlist_for_each_entry(ce, &cache_htable[hash], hlist) { - if (dfs_path_equal(ce->path, strlen(ce->path), path, len)) { - dump_ce(ce); - return ce; - } - } - return ERR_PTR(-ENOENT); -} - -/* - * Find a DFS cache entry in hash table and optionally check prefix path against normalized @path. - * - * Use whole path components in the match. Must be called with htable_rw_lock held. - * - * Return ERR_PTR(-ENOENT) if the entry is not found. - */ -static struct cache_entry *lookup_cache_entry(const char *path) -{ - struct cache_entry *ce; - int cnt = 0; - const char *s = path, *e; - char sep = *s; - unsigned int hash; - int rc; - - while ((s = strchr(s, sep)) && ++cnt < 3) - s++; - - if (cnt < 3) { - rc = cache_entry_hash(path, strlen(path), &hash); - if (rc) - return ERR_PTR(rc); - return __lookup_cache_entry(path, hash, strlen(path)); - } - /* - * Handle paths that have more than two path components and are a complete prefix of the DFS - * referral request path (@path). - * - * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request". - */ - e = path + strlen(path) - 1; - while (e > s) { - int len; - - /* skip separators */ - while (e > s && *e == sep) - e--; - if (e == s) - break; - - len = e + 1 - path; - rc = cache_entry_hash(path, len, &hash); - if (rc) - return ERR_PTR(rc); - ce = __lookup_cache_entry(path, hash, len); - if (!IS_ERR(ce)) - return ce; - - /* backward until separator */ - while (e > s && *e != sep) - e--; - } - return ERR_PTR(-ENOENT); -} - -/** - * dfs_cache_destroy - destroy DFS referral cache - */ -void dfs_cache_destroy(void) -{ - cancel_delayed_work_sync(&refresh_task); - unload_nls(cache_cp); - free_mount_group_list(); - flush_cache_ents(); - kmem_cache_destroy(cache_slab); - destroy_workqueue(dfscache_wq); - - cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__); -} - -/* Update a cache entry with the new referral in @refs */ -static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_info3_param *refs, - int numrefs) -{ - int rc; - char *s, *th = NULL; - - WARN_ON(!rwsem_is_locked(&htable_rw_lock)); - - if (ce->tgthint) { - s = ce->tgthint->name; - th = kstrdup(s, GFP_ATOMIC); - if (!th) - return -ENOMEM; - } - - free_tgts(ce); - ce->numtgts = 0; - - rc = copy_ref_data(refs, numrefs, ce, th); - - kfree(th); - - return rc; -} - -static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const char *path, - struct dfs_info3_param **refs, int *numrefs) -{ - int rc; - int i; - - cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path); - - *refs = NULL; - *numrefs = 0; - - if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) - return -EOPNOTSUPP; - if (unlikely(!cache_cp)) - return -EINVAL; - - rc = ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp, - NO_MAP_UNI_RSVD); - if (!rc) { - struct dfs_info3_param *ref = *refs; - - for (i = 0; i < *numrefs; i++) - convert_delimiter(ref[i].path_name, '\\'); - } - return rc; -} - -/* - * Find, create or update a DFS cache entry. - * - * If the entry wasn't found, it will create a new one. Or if it was found but - * expired, then it will update the entry accordingly. - * - * For interlinks, cifs_mount() and expand_dfs_referral() are supposed to - * handle them properly. - */ -static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses, const char *path) -{ - struct dfs_info3_param *refs = NULL; - struct cache_entry *ce; - int numrefs = 0; - int rc; - - cifs_dbg(FYI, "%s: search path: %s\n", __func__, path); - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (!IS_ERR(ce) && !cache_entry_expired(ce)) { - up_read(&htable_rw_lock); - return 0; - } - /* - * Unlock shared access as we don't want to hold any locks while getting - * a new referral. The @ses used for performing the I/O could be - * reconnecting and it acquires @htable_rw_lock to look up the dfs cache - * in order to failover -- if necessary. - */ - up_read(&htable_rw_lock); - - /* - * Either the entry was not found, or it is expired. - * Request a new DFS referral in order to create or update a cache entry. - */ - rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); - if (rc) - goto out; - - dump_refs(refs, numrefs); - - down_write(&htable_rw_lock); - /* Re-check as another task might have it added or refreshed already */ - ce = lookup_cache_entry(path); - if (!IS_ERR(ce)) { - if (cache_entry_expired(ce)) - rc = update_cache_entry_locked(ce, refs, numrefs); - } else { - rc = add_cache_entry_locked(refs, numrefs); - } - - up_write(&htable_rw_lock); -out: - free_dfs_info_array(refs, numrefs); - return rc; -} - -/* - * Set up a DFS referral from a given cache entry. - * - * Must be called with htable_rw_lock held. - */ -static int setup_referral(const char *path, struct cache_entry *ce, - struct dfs_info3_param *ref, const char *target) -{ - int rc; - - cifs_dbg(FYI, "%s: set up new ref\n", __func__); - - memset(ref, 0, sizeof(*ref)); - - ref->path_name = kstrdup(path, GFP_ATOMIC); - if (!ref->path_name) - return -ENOMEM; - - ref->node_name = kstrdup(target, GFP_ATOMIC); - if (!ref->node_name) { - rc = -ENOMEM; - goto err_free_path; - } - - ref->path_consumed = ce->path_consumed; - ref->ttl = ce->ttl; - ref->server_type = ce->srvtype; - ref->ref_flag = ce->ref_flags; - ref->flags = ce->hdr_flags; - - return 0; - -err_free_path: - kfree(ref->path_name); - ref->path_name = NULL; - return rc; -} - -/* Return target list of a DFS cache entry */ -static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) -{ - int rc; - struct list_head *head = &tl->tl_list; - struct cache_dfs_tgt *t; - struct dfs_cache_tgt_iterator *it, *nit; - - memset(tl, 0, sizeof(*tl)); - INIT_LIST_HEAD(head); - - list_for_each_entry(t, &ce->tlist, list) { - it = kzalloc(sizeof(*it), GFP_ATOMIC); - if (!it) { - rc = -ENOMEM; - goto err_free_it; - } - - it->it_name = kstrdup(t->name, GFP_ATOMIC); - if (!it->it_name) { - kfree(it); - rc = -ENOMEM; - goto err_free_it; - } - it->it_path_consumed = t->path_consumed; - - if (ce->tgthint == t) - list_add(&it->it_list, head); - else - list_add_tail(&it->it_list, head); - } - - tl->tl_numtgts = ce->numtgts; - - return 0; - -err_free_it: - list_for_each_entry_safe(it, nit, head, it_list) { - list_del(&it->it_list); - kfree(it->it_name); - kfree(it); - } - return rc; -} - -/** - * dfs_cache_find - find a DFS cache entry - * - * If it doesn't find the cache entry, then it will get a DFS referral - * for @path and create a new entry. - * - * In case the cache entry exists but expired, it will get a DFS referral - * for @path and then update the respective cache entry. - * - * These parameters are passed down to the get_dfs_refer() call if it - * needs to be issued: - * @xid: syscall xid - * @ses: smb session to issue the request on - * @cp: codepage - * @remap: path character remapping type - * @path: path to lookup in DFS referral cache. - * - * @ref: when non-NULL, store single DFS referral result in it. - * @tgt_list: when non-NULL, store complete DFS target list in it. - * - * Return zero if the target was found, otherwise non-zero. - */ -int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, - int remap, const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list) -{ - int rc; - const char *npath; - struct cache_entry *ce; - - npath = dfs_cache_canonical_path(path, cp, remap); - if (IS_ERR(npath)) - return PTR_ERR(npath); - - rc = cache_refresh_path(xid, ses, npath); - if (rc) - goto out_free_path; - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(npath); - if (IS_ERR(ce)) { - up_read(&htable_rw_lock); - rc = PTR_ERR(ce); - goto out_free_path; - } - - if (ref) - rc = setup_referral(path, ce, ref, get_tgt_name(ce)); - else - rc = 0; - if (!rc && tgt_list) - rc = get_targets(ce, tgt_list); - - up_read(&htable_rw_lock); - -out_free_path: - kfree(npath); - return rc; -} - -/** - * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to - * the currently connected server. - * - * NOTE: This function will neither update a cache entry in case it was - * expired, nor create a new cache entry if @path hasn't been found. It heavily - * relies on an existing cache entry. - * - * @path: canonical DFS path to lookup in the DFS referral cache. - * @ref: when non-NULL, store single DFS referral result in it. - * @tgt_list: when non-NULL, store complete DFS target list in it. - * - * Return 0 if successful. - * Return -ENOENT if the entry was not found. - * Return non-zero for other errors. - */ -int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list) -{ - int rc; - struct cache_entry *ce; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - if (ref) - rc = setup_referral(path, ce, ref, get_tgt_name(ce)); - else - rc = 0; - if (!rc && tgt_list) - rc = get_targets(ce, tgt_list); - -out_unlock: - up_read(&htable_rw_lock); - return rc; -} - -/** - * dfs_cache_update_tgthint - update target hint of a DFS cache entry - * - * If it doesn't find the cache entry, then it will get a DFS referral for @path - * and create a new entry. - * - * In case the cache entry exists but expired, it will get a DFS referral - * for @path and then update the respective cache entry. - * - * @xid: syscall id - * @ses: smb session - * @cp: codepage - * @remap: type of character remapping for paths - * @path: path to lookup in DFS referral cache - * @it: DFS target iterator - * - * Return zero if the target hint was updated successfully, otherwise non-zero. - */ -int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, - const struct nls_table *cp, int remap, const char *path, - const struct dfs_cache_tgt_iterator *it) -{ - struct cache_dfs_tgt *t; - struct cache_entry *ce; - const char *npath; - int rc = 0; - - npath = dfs_cache_canonical_path(path, cp, remap); - if (IS_ERR(npath)) - return PTR_ERR(npath); - - cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath); - - rc = cache_refresh_path(xid, ses, npath); - if (rc) - goto out_free_path; - - down_write(&htable_rw_lock); - - ce = lookup_cache_entry(npath); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - t = ce->tgthint; - - if (likely(!strcasecmp(it->it_name, t->name))) - goto out_unlock; - - list_for_each_entry(t, &ce->tlist, list) { - if (!strcasecmp(t->name, it->it_name)) { - ce->tgthint = t; - cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, - it->it_name); - break; - } - } - -out_unlock: - up_write(&htable_rw_lock); -out_free_path: - kfree(npath); - return rc; -} - -/** - * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry - * without sending any requests to the currently connected server. - * - * NOTE: This function will neither update a cache entry in case it was - * expired, nor create a new cache entry if @path hasn't been found. It heavily - * relies on an existing cache entry. - * - * @path: canonical DFS path to lookup in DFS referral cache. - * @it: target iterator which contains the target hint to update the cache - * entry with. - * - * Return zero if the target hint was updated successfully, otherwise non-zero. - */ -int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it) -{ - int rc; - struct cache_entry *ce; - struct cache_dfs_tgt *t; - - if (!it) - return -EINVAL; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - down_write(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - rc = 0; - t = ce->tgthint; - - if (unlikely(!strcasecmp(it->it_name, t->name))) - goto out_unlock; - - list_for_each_entry(t, &ce->tlist, list) { - if (!strcasecmp(t->name, it->it_name)) { - ce->tgthint = t; - cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, - it->it_name); - break; - } - } - -out_unlock: - up_write(&htable_rw_lock); - return rc; -} - -/** - * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given - * target iterator (@it). - * - * @path: canonical DFS path to lookup in DFS referral cache. - * @it: DFS target iterator. - * @ref: DFS referral pointer to set up the gathered information. - * - * Return zero if the DFS referral was set up correctly, otherwise non-zero. - */ -int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, - struct dfs_info3_param *ref) -{ - int rc; - struct cache_entry *ce; - - if (!it || !ref) - return -EINVAL; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name); - - rc = setup_referral(path, ce, ref, it->it_name); - -out_unlock: - up_read(&htable_rw_lock); - return rc; -} - -/** - * dfs_cache_add_refsrv_session - add SMB session of referral server - * - * @mount_id: mount group uuid to lookup. - * @ses: reference counted SMB session of referral server. - */ -void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses) -{ - struct mount_group *mg; - - if (WARN_ON_ONCE(!mount_id || uuid_is_null(mount_id) || !ses)) - return; - - mg = get_mount_group(mount_id); - if (WARN_ON_ONCE(IS_ERR(mg))) - return; - - spin_lock(&mg->lock); - if (mg->num_sessions < ARRAY_SIZE(mg->sessions)) - mg->sessions[mg->num_sessions++] = ses; - spin_unlock(&mg->lock); - kref_put(&mg->refcount, mount_group_release); -} - -/** - * dfs_cache_put_refsrv_sessions - put all referral server sessions - * - * Put all SMB sessions from the given mount group id. - * - * @mount_id: mount group uuid to lookup. - */ -void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id) -{ - struct mount_group *mg; - - if (!mount_id || uuid_is_null(mount_id)) - return; - - mutex_lock(&mount_group_list_lock); - mg = find_mount_group_locked(mount_id); - if (IS_ERR(mg)) { - mutex_unlock(&mount_group_list_lock); - return; - } - mutex_unlock(&mount_group_list_lock); - kref_put(&mg->refcount, mount_group_release); -} - -/* Extract share from DFS target and return a pointer to prefix path or NULL */ -static const char *parse_target_share(const char *target, char **share) -{ - const char *s, *seps = "/\\"; - size_t len; - - s = strpbrk(target + 1, seps); - if (!s) - return ERR_PTR(-EINVAL); - - len = strcspn(s + 1, seps); - if (!len) - return ERR_PTR(-EINVAL); - s += len; - - len = s - target + 1; - *share = kstrndup(target, len, GFP_KERNEL); - if (!*share) - return ERR_PTR(-ENOMEM); - - s = target + len; - return s + strspn(s, seps); -} - -/** - * dfs_cache_get_tgt_share - parse a DFS target - * - * @path: DFS full path - * @it: DFS target iterator. - * @share: tree name. - * @prefix: prefix path. - * - * Return zero if target was parsed correctly, otherwise non-zero. - */ -int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, - char **prefix) -{ - char sep; - char *target_share; - char *ppath = NULL; - const char *target_ppath, *dfsref_ppath; - size_t target_pplen, dfsref_pplen; - size_t len, c; - - if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed) - return -EINVAL; - - sep = it->it_name[0]; - if (sep != '\\' && sep != '/') - return -EINVAL; - - target_ppath = parse_target_share(it->it_name, &target_share); - if (IS_ERR(target_ppath)) - return PTR_ERR(target_ppath); - - /* point to prefix in DFS referral path */ - dfsref_ppath = path + it->it_path_consumed; - dfsref_ppath += strspn(dfsref_ppath, "/\\"); - - target_pplen = strlen(target_ppath); - dfsref_pplen = strlen(dfsref_ppath); - - /* merge prefix paths from DFS referral path and target node */ - if (target_pplen || dfsref_pplen) { - len = target_pplen + dfsref_pplen + 2; - ppath = kzalloc(len, GFP_KERNEL); - if (!ppath) { - kfree(target_share); - return -ENOMEM; - } - c = strscpy(ppath, target_ppath, len); - if (c && dfsref_pplen) - ppath[c] = sep; - strlcat(ppath, dfsref_ppath, len); - } - *share = target_share; - *prefix = ppath; - return 0; -} - -static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) -{ - char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; - const char *host; - size_t hostlen; - char *ip = NULL; - struct sockaddr sa; - bool match; - int rc; - - if (strcasecmp(s1, s2)) - return false; - - /* - * Resolve share's hostname and check if server address matches. Otherwise just ignore it - * as we could not have upcall to resolve hostname or failed to convert ip address. - */ - match = true; - extract_unc_hostname(s1, &host, &hostlen); - scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host); - - rc = dns_resolve_server_name_to_ip(unc, &ip, NULL); - if (rc < 0) { - cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n", - __func__, (int)hostlen, host); - return true; - } - - if (!cifs_convert_address(&sa, ip, strlen(ip))) { - cifs_dbg(VFS, "%s: failed to convert address \'%s\'. skip address matching.\n", - __func__, ip); - } else { - cifs_server_lock(server); - match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, &sa); - cifs_server_unlock(server); - } - - kfree(ip); - return match; -} - -/* - * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new - * target shares in @refs. - */ -static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cache_tgt_list *tl, - const struct dfs_info3_param *refs, int numrefs) -{ - struct dfs_cache_tgt_iterator *it; - int i; - - for (it = dfs_cache_get_tgt_iterator(tl); it; it = dfs_cache_get_next_tgt(tl, it)) { - for (i = 0; i < numrefs; i++) { - if (target_share_equal(tcon->ses->server, dfs_cache_get_tgt_name(it), - refs[i].node_name)) - return; - } - } - - cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__); - cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); -} - -/* Refresh dfs referral of tcon and mark it for reconnect if needed */ -static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon, - bool force_refresh) -{ - struct cifs_ses *ses; - struct cache_entry *ce; - struct dfs_info3_param *refs = NULL; - int numrefs = 0; - bool needs_refresh = false; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - int rc = 0; - unsigned int xid; - - ses = find_ipc_from_server_path(sessions, path); - if (IS_ERR(ses)) { - cifs_dbg(FYI, "%s: could not find ipc session\n", __func__); - return PTR_ERR(ses); - } - - down_read(&htable_rw_lock); - ce = lookup_cache_entry(path); - needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); - if (!IS_ERR(ce)) { - rc = get_targets(ce, &tl); - if (rc) - cifs_dbg(FYI, "%s: could not get dfs targets: %d\n", __func__, rc); - } - up_read(&htable_rw_lock); - - if (!needs_refresh) { - rc = 0; - goto out; - } - - xid = get_xid(); - rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); - free_xid(xid); - - /* Create or update a cache entry with the new referral */ - if (!rc) { - dump_refs(refs, numrefs); - - down_write(&htable_rw_lock); - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) - add_cache_entry_locked(refs, numrefs); - else if (force_refresh || cache_entry_expired(ce)) - update_cache_entry_locked(ce, refs, numrefs); - up_write(&htable_rw_lock); - - mark_for_reconnect_if_needed(tcon, &tl, refs, numrefs); - } - -out: - dfs_cache_free_tgts(&tl); - free_dfs_info_array(refs, numrefs); - return rc; -} - -static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) -{ - struct TCP_Server_Info *server = tcon->ses->server; - - mutex_lock(&server->refpath_lock); - if (server->origin_fullpath) { - if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, - server->origin_fullpath)) - __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh); - __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh); - } - mutex_unlock(&server->refpath_lock); - - return 0; -} - -/** - * dfs_cache_remount_fs - remount a DFS share - * - * Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not - * match any of the new targets, mark it for reconnect. - * - * @cifs_sb: cifs superblock. - * - * Return zero if remounted, otherwise non-zero. - */ -int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) -{ - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct mount_group *mg; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; - int rc; - - if (!cifs_sb || !cifs_sb->master_tlink) - return -EINVAL; - - tcon = cifs_sb_master_tcon(cifs_sb); - server = tcon->ses->server; - - if (!server->origin_fullpath) { - cifs_dbg(FYI, "%s: not a dfs mount\n", __func__); - return 0; - } - - if (uuid_is_null(&cifs_sb->dfs_mount_id)) { - cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__); - return -EINVAL; - } - - mutex_lock(&mount_group_list_lock); - mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); - if (IS_ERR(mg)) { - mutex_unlock(&mount_group_list_lock); - cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__); - return PTR_ERR(mg); - } - kref_get(&mg->refcount); - mutex_unlock(&mount_group_list_lock); - - spin_lock(&mg->lock); - memcpy(&sessions, mg->sessions, mg->num_sessions * sizeof(mg->sessions[0])); - spin_unlock(&mg->lock); - - /* - * After reconnecting to a different server, unique ids won't match anymore, so we disable - * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). - */ - cifs_autodisable_serverino(cifs_sb); - /* - * Force the use of prefix path to support failover on DFS paths that resolve to targets - * that have different prefix paths. - */ - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - rc = refresh_tcon(sessions, tcon, true); - - kref_put(&mg->refcount, mount_group_release); - return rc; -} - -/* - * Refresh all active dfs mounts regardless of whether they are in cache or not. - * (cache can be cleared) - */ -static void refresh_mounts(struct cifs_ses **sessions) -{ - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon, *ntcon; - struct list_head tcons; - - INIT_LIST_HEAD(&tcons); - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - spin_lock(&server->srv_lock); - if (!server->is_dfs_conn) { - spin_unlock(&server->srv_lock); - continue; - } - spin_unlock(&server->srv_lock); - - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->tc_lock); - if (!tcon->ipc && !tcon->need_reconnect) { - tcon->tc_count++; - list_add_tail(&tcon->ulist, &tcons); - } - spin_unlock(&tcon->tc_lock); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - - list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { - struct TCP_Server_Info *server = tcon->ses->server; - - list_del_init(&tcon->ulist); - - mutex_lock(&server->refpath_lock); - if (server->origin_fullpath) { - if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, - server->origin_fullpath)) - __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); - __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); - } - mutex_unlock(&server->refpath_lock); - - cifs_put_tcon(tcon); - } -} - -static void refresh_cache(struct cifs_ses **sessions) -{ - int i; - struct cifs_ses *ses; - unsigned int xid; - char *ref_paths[CACHE_MAX_ENTRIES]; - int count = 0; - struct cache_entry *ce; - - /* - * Refresh all cached entries. Get all new referrals outside critical section to avoid - * starvation while performing SMB2 IOCTL on broken or slow connections. - - * The cache entries may cover more paths than the active mounts - * (e.g. domain-based DFS referrals or multi tier DFS setups). - */ - down_read(&htable_rw_lock); - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - - hlist_for_each_entry(ce, l, hlist) { - if (count == ARRAY_SIZE(ref_paths)) - goto out_unlock; - if (hlist_unhashed(&ce->hlist) || !cache_entry_expired(ce) || - IS_ERR(find_ipc_from_server_path(sessions, ce->path))) - continue; - ref_paths[count++] = kstrdup(ce->path, GFP_ATOMIC); - } - } - -out_unlock: - up_read(&htable_rw_lock); - - for (i = 0; i < count; i++) { - char *path = ref_paths[i]; - struct dfs_info3_param *refs = NULL; - int numrefs = 0; - int rc = 0; - - if (!path) - continue; - - ses = find_ipc_from_server_path(sessions, path); - if (IS_ERR(ses)) - goto next_referral; - - xid = get_xid(); - rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); - free_xid(xid); - - if (!rc) { - down_write(&htable_rw_lock); - ce = lookup_cache_entry(path); - /* - * We need to re-check it because other tasks might have it deleted or - * updated. - */ - if (!IS_ERR(ce) && cache_entry_expired(ce)) - update_cache_entry_locked(ce, refs, numrefs); - up_write(&htable_rw_lock); - } - -next_referral: - kfree(path); - free_dfs_info_array(refs, numrefs); - } -} - -/* - * Worker that will refresh DFS cache and active mounts based on lowest TTL value from a DFS - * referral. - */ -static void refresh_cache_worker(struct work_struct *work) -{ - struct list_head mglist; - struct mount_group *mg, *tmp_mg; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; - int max_sessions = ARRAY_SIZE(sessions) - 1; - int i = 0, count; - - INIT_LIST_HEAD(&mglist); - - /* Get refereces of mount groups */ - mutex_lock(&mount_group_list_lock); - list_for_each_entry(mg, &mount_group_list, list) { - kref_get(&mg->refcount); - list_add(&mg->refresh_list, &mglist); - } - mutex_unlock(&mount_group_list_lock); - - /* Fill in local array with an NULL-terminated list of all referral server sessions */ - list_for_each_entry(mg, &mglist, refresh_list) { - if (i >= max_sessions) - break; - - spin_lock(&mg->lock); - if (i + mg->num_sessions > max_sessions) - count = max_sessions - i; - else - count = mg->num_sessions; - memcpy(&sessions[i], mg->sessions, count * sizeof(mg->sessions[0])); - spin_unlock(&mg->lock); - i += count; - } - - if (sessions[0]) { - /* Refresh all active mounts and cached entries */ - refresh_mounts(sessions); - refresh_cache(sessions); - } - - list_for_each_entry_safe(mg, tmp_mg, &mglist, refresh_list) { - list_del_init(&mg->refresh_list); - kref_put(&mg->refcount, mount_group_release); - } - - spin_lock(&cache_ttl_lock); - queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); - spin_unlock(&cache_ttl_lock); -} diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h deleted file mode 100644 index 52070d1df189..000000000000 --- a/fs/cifs/dfs_cache.h +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * DFS referral cache routines - * - * Copyright (c) 2018-2019 Paulo Alcantara - */ - -#ifndef _CIFS_DFS_CACHE_H -#define _CIFS_DFS_CACHE_H - -#include -#include -#include -#include "cifsglob.h" - -#define DFS_CACHE_TGT_LIST_INIT(var) { .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), } - -struct dfs_cache_tgt_list { - int tl_numtgts; - struct list_head tl_list; -}; - -struct dfs_cache_tgt_iterator { - char *it_name; - int it_path_consumed; - struct list_head it_list; -}; - -int dfs_cache_init(void); -void dfs_cache_destroy(void); -extern const struct proc_ops dfscache_proc_ops; - -int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, - int remap, const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list); -int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list); -int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, - const struct nls_table *cp, int remap, const char *path, - const struct dfs_cache_tgt_iterator *it); -int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it); -int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, - struct dfs_info3_param *ref); -int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, - char **prefix); -void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id); -void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses); -char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap); -int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb); - -static inline struct dfs_cache_tgt_iterator * -dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, - struct dfs_cache_tgt_iterator *it) -{ - if (!tl || list_empty(&tl->tl_list) || !it || - list_is_last(&it->it_list, &tl->tl_list)) - return NULL; - return list_next_entry(it, it_list); -} - -static inline struct dfs_cache_tgt_iterator * -dfs_cache_get_tgt_iterator(struct dfs_cache_tgt_list *tl) -{ - if (!tl) - return NULL; - return list_first_entry_or_null(&tl->tl_list, - struct dfs_cache_tgt_iterator, - it_list); -} - -static inline void dfs_cache_free_tgts(struct dfs_cache_tgt_list *tl) -{ - struct dfs_cache_tgt_iterator *it, *nit; - - if (!tl || list_empty(&tl->tl_list)) - return; - list_for_each_entry_safe(it, nit, &tl->tl_list, it_list) { - list_del(&it->it_list); - kfree(it->it_name); - kfree(it); - } - tl->tl_numtgts = 0; -} - -static inline const char * -dfs_cache_get_tgt_name(const struct dfs_cache_tgt_iterator *it) -{ - return it ? it->it_name : NULL; -} - -static inline int -dfs_cache_get_nr_tgts(const struct dfs_cache_tgt_list *tl) -{ - return tl ? tl->tl_numtgts : 0; -} - -#endif /* _CIFS_DFS_CACHE_H */ diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c deleted file mode 100644 index e382b794acbe..000000000000 --- a/fs/cifs/dir.c +++ /dev/null @@ -1,867 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * vfs operations that deal with dentries - * - * Copyright (C) International Business Machines Corp., 2002,2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fs_context.h" -#include "cifs_ioctl.h" -#include "fscache.h" - -static void -renew_parental_timestamps(struct dentry *direntry) -{ - /* BB check if there is a way to get the kernel to do this or if we - really need this */ - do { - cifs_set_time(direntry, jiffies); - direntry = direntry->d_parent; - } while (!IS_ROOT(direntry)); -} - -char * -cifs_build_path_to_root(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, int add_treename) -{ - int pplen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0; - int dfsplen; - char *full_path = NULL; - - /* if no prefix path, simply set path to the root of share to "" */ - if (pplen == 0) { - full_path = kzalloc(1, GFP_KERNEL); - return full_path; - } - - if (add_treename) - dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); - else - dfsplen = 0; - - full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); - if (full_path == NULL) - return full_path; - - if (dfsplen) - memcpy(full_path, tcon->tree_name, dfsplen); - full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb); - memcpy(full_path + dfsplen + 1, ctx->prepath, pplen); - convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); - return full_path; -} - -/* Note: caller must free return buffer */ -const char * -build_path_from_dentry(struct dentry *direntry, void *page) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS; - - return build_path_from_dentry_optional_prefix(direntry, page, - prefix); -} - -char * -build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page, - bool prefix) -{ - int dfsplen; - int pplen = 0; - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - char dirsep = CIFS_DIR_SEP(cifs_sb); - char *s; - - if (unlikely(!page)) - return ERR_PTR(-ENOMEM); - - if (prefix) - dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); - else - dfsplen = 0; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) - pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0; - - s = dentry_path_raw(direntry, page, PATH_MAX); - if (IS_ERR(s)) - return s; - if (!s[1]) // for root we want "", not "/" - s++; - if (s < (char *)page + pplen + dfsplen) - return ERR_PTR(-ENAMETOOLONG); - if (pplen) { - cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath); - s -= pplen; - memcpy(s + 1, cifs_sb->prepath, pplen - 1); - *s = '/'; - } - if (dirsep != '/') { - /* BB test paths to Windows with '/' in the midst of prepath */ - char *p; - - for (p = s; *p; p++) - if (*p == '/') - *p = dirsep; - } - if (dfsplen) { - s -= dfsplen; - memcpy(s, tcon->tree_name, dfsplen); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { - int i; - for (i = 0; i < dfsplen; i++) { - if (s[i] == '\\') - s[i] = '/'; - } - } - } - return s; -} - -/* - * Don't allow path components longer than the server max. - * Don't allow the separator character in a path component. - * The VFS will not allow "/", but "\" is allowed by posix. - */ -static int -check_name(struct dentry *direntry, struct cifs_tcon *tcon) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - int i; - - if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength && - direntry->d_name.len > - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength))) - return -ENAMETOOLONG; - - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) { - for (i = 0; i < direntry->d_name.len; i++) { - if (direntry->d_name.name[i] == '\\') { - cifs_dbg(FYI, "Invalid file name\n"); - return -EINVAL; - } - } - } - return 0; -} - - -/* Inode operations in similar order to how they appear in Linux file fs.h */ - -static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, - struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock, - struct cifs_fid *fid, struct cifs_open_info_data *buf) -{ - int rc = -ENOENT; - int create_options = CREATE_NOT_DIR; - int desired_access; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = tlink_tcon(tlink); - const char *full_path; - void *page = alloc_dentry_path(); - struct inode *newinode = NULL; - int disposition; - struct TCP_Server_Info *server = tcon->ses->server; - struct cifs_open_parms oparms; - - *oplock = 0; - if (tcon->ses->server->oplocks) - *oplock = REQ_OPLOCK; - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - free_dentry_path(page); - return PTR_ERR(full_path); - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, - oflags, oplock, &fid->netfid, xid); - switch (rc) { - case 0: - if (newinode == NULL) { - /* query inode info */ - goto cifs_create_get_file_info; - } - - if (S_ISDIR(newinode->i_mode)) { - CIFSSMBClose(xid, tcon, fid->netfid); - iput(newinode); - rc = -EISDIR; - goto out; - } - - if (!S_ISREG(newinode->i_mode)) { - /* - * The server may allow us to open things like - * FIFOs, but the client isn't set up to deal - * with that. If it's not a regular file, just - * close it and proceed as if it were a normal - * lookup. - */ - CIFSSMBClose(xid, tcon, fid->netfid); - goto cifs_create_get_file_info; - } - /* success, no need to query */ - goto cifs_create_set_dentry; - - case -ENOENT: - goto cifs_create_get_file_info; - - case -EIO: - case -EINVAL: - /* - * EIO could indicate that (posix open) operation is not - * supported, despite what server claimed in capability - * negotiation. - * - * POSIX open in samba versions 3.3.1 and earlier could - * incorrectly fail with invalid parameter. - */ - tcon->broken_posix_open = true; - break; - - case -EREMOTE: - case -EOPNOTSUPP: - /* - * EREMOTE indicates DFS junction, which is not handled - * in posix open. If either that or op not supported - * returned, follow the normal lookup. - */ - break; - - default: - goto out; - } - /* - * fallthrough to retry, using older open call, this is case - * where server does not support this SMB level, and falsely - * claims capability (also get here for DFS case which should be - * rare for path not covered on files) - */ - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - desired_access = 0; - if (OPEN_FMODE(oflags) & FMODE_READ) - desired_access |= GENERIC_READ; /* is this too little? */ - if (OPEN_FMODE(oflags) & FMODE_WRITE) - desired_access |= GENERIC_WRITE; - - disposition = FILE_OVERWRITE_IF; - if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - disposition = FILE_CREATE; - else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) - disposition = FILE_OVERWRITE_IF; - else if ((oflags & O_CREAT) == O_CREAT) - disposition = FILE_OPEN_IF; - else - cifs_dbg(FYI, "Create flag not set in create function\n"); - - /* - * BB add processing to set equivalent of mode - e.g. via CreateX with - * ACLs - */ - - if (!server->ops->open) { - rc = -ENOSYS; - goto out; - } - - /* - * if we're not using unix extensions, see if we need to set - * ATTR_READONLY on the create call - */ - if (!tcon->unix_ext && (mode & S_IWUGO) == 0) - create_options |= CREATE_OPTION_READONLY; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = desired_access, - .create_options = cifs_create_options(cifs_sb, create_options), - .disposition = disposition, - .path = full_path, - .fid = fid, - .mode = mode, - }; - rc = server->ops->open(xid, &oparms, oplock, buf); - if (rc) { - cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); - goto out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* - * If Open reported that we actually created a file then we now have to - * set the mode if possible. - */ - if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) { - struct cifs_unix_set_info_args args = { - .mode = mode, - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = 0, - }; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = current_fsuid(); - if (inode->i_mode & S_ISGID) - args.gid = inode->i_gid; - else - args.gid = current_fsgid(); - } else { - args.uid = INVALID_UID; /* no change */ - args.gid = INVALID_GID; /* no change */ - } - CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, - current->tgid); - } else { - /* - * BB implement mode setting via Windows security - * descriptors e.g. - */ - /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ - - /* Could set r/o dos attribute if mode & 0222 == 0 */ - } - -cifs_create_get_file_info: - /* server might mask mode so we have to query for it */ - if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, - xid); - else { -#else - { -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - /* TODO: Add support for calling POSIX query info here, but passing in fid */ - rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid); - if (newinode) { - if (server->ops->set_lease_key) - server->ops->set_lease_key(newinode, fid); - if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) - newinode->i_mode = mode; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - newinode->i_uid = current_fsuid(); - if (inode->i_mode & S_ISGID) - newinode->i_gid = inode->i_gid; - else - newinode->i_gid = current_fsgid(); - } - } - } - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -cifs_create_set_dentry: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - if (rc != 0) { - cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", - rc); - goto out_err; - } - - if (newinode) - if (S_ISDIR(newinode->i_mode)) { - rc = -EISDIR; - goto out_err; - } - - d_drop(direntry); - d_add(direntry, newinode); - -out: - free_dentry_path(page); - return rc; - -out_err: - if (server->ops->close) - server->ops->close(xid, tcon, fid); - if (newinode) - iput(newinode); - goto out; -} - -int -cifs_atomic_open(struct inode *inode, struct dentry *direntry, - struct file *file, unsigned oflags, umode_t mode) -{ - int rc; - unsigned int xid; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifs_fid fid = {}; - struct cifs_pending_open open; - __u32 oplock; - struct cifsFileInfo *file_info; - struct cifs_open_info_data buf = {}; - - if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) - return -EIO; - - /* - * Posix open is only called (at lookup time) for file create now. For - * opens (rather than creates), because we do not know if it is a file - * or directory yet, and current Samba no longer allows us to do posix - * open on dirs, we could end up wasting an open call on what turns out - * to be a dir. For file opens, we wait to call posix open till - * cifs_open. It could be added to atomic_open in the future but the - * performance tradeoff of the extra network request when EISDIR or - * EACCES is returned would have to be weighed against the 50% reduction - * in network traffic in the other paths. - */ - if (!(oflags & O_CREAT)) { - struct dentry *res; - - /* - * Check for hashed negative dentry. We have already revalidated - * the dentry and it is fine. No need to perform another lookup. - */ - if (!d_in_lookup(direntry)) - return -ENOENT; - - res = cifs_lookup(inode, direntry, 0); - if (IS_ERR(res)) - return PTR_ERR(res); - - return finish_no_open(file, res); - } - - xid = get_xid(); - - cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", - inode, direntry, direntry); - - tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto out_free_xid; - } - - tcon = tlink_tcon(tlink); - - rc = check_name(direntry, tcon); - if (rc) - goto out; - - server = tcon->ses->server; - - if (server->ops->new_lease_key) - server->ops->new_lease_key(&fid); - - cifs_add_pending_open(&fid, tlink, &open); - - rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, - &oplock, &fid, &buf); - if (rc) { - cifs_del_pending_open(&open); - goto out; - } - - if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - file->f_mode |= FMODE_CREATED; - - rc = finish_open(file, direntry, generic_file_open); - if (rc) { - if (server->ops->close) - server->ops->close(xid, tcon, &fid); - cifs_del_pending_open(&open); - goto out; - } - - if (file->f_flags & O_DIRECT && - CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { - if (CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - file->f_op = &cifs_file_direct_nobrl_ops; - else - file->f_op = &cifs_file_direct_ops; - } - - file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target); - if (file_info == NULL) { - if (server->ops->close) - server->ops->close(xid, tcon, &fid); - cifs_del_pending_open(&open); - rc = -ENOMEM; - goto out; - } - - fscache_use_cookie(cifs_inode_cookie(file_inode(file)), - file->f_mode & FMODE_WRITE); - -out: - cifs_put_tlink(tlink); -out_free_xid: - free_xid(xid); - cifs_free_open_info(&buf); - return rc; -} - -int cifs_create(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, umode_t mode, bool excl) -{ - int rc; - unsigned int xid = get_xid(); - /* - * BB below access is probably too much for mknod to request - * but we have to do query and setpathinfo so requesting - * less could fail (unless we want to request getatr and setatr - * permissions (only). At least for POSIX we do not have to - * request so much. - */ - unsigned oflags = O_EXCL | O_CREAT | O_RDWR; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifs_fid fid; - __u32 oplock; - struct cifs_open_info_data buf = {}; - - cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n", - inode, direntry, direntry); - - if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) { - rc = -EIO; - goto out_free_xid; - } - - tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); - rc = PTR_ERR(tlink); - if (IS_ERR(tlink)) - goto out_free_xid; - - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - if (server->ops->new_lease_key) - server->ops->new_lease_key(&fid); - - rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf); - if (!rc && server->ops->close) - server->ops->close(xid, tcon, &fid); - - cifs_free_open_info(&buf); - cifs_put_tlink(tlink); -out_free_xid: - free_xid(xid); - return rc; -} - -int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, umode_t mode, dev_t device_number) -{ - int rc = -EPERM; - unsigned int xid; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - const char *full_path; - void *page; - - if (!old_valid_dev(device_number)) - return -EINVAL; - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - page = alloc_dentry_path(); - tcon = tlink_tcon(tlink); - xid = get_xid(); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto mknod_out; - } - - rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon, - full_path, mode, - device_number); - -mknod_out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -struct dentry * -cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, - unsigned int flags) -{ - unsigned int xid; - int rc = 0; /* to get around spurious gcc warning, set to zero here */ - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - struct inode *newInode = NULL; - const char *full_path; - void *page; - int retry_count = 0; - - xid = get_xid(); - - cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", - parent_dir_inode, direntry, direntry); - - /* check whether path exists */ - - cifs_sb = CIFS_SB(parent_dir_inode->i_sb); - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - free_xid(xid); - return ERR_CAST(tlink); - } - pTcon = tlink_tcon(tlink); - - rc = check_name(direntry, pTcon); - if (unlikely(rc)) { - cifs_put_tlink(tlink); - free_xid(xid); - return ERR_PTR(rc); - } - - /* can not grab the rename sem here since it would - deadlock in the cases (beginning of sys_rename itself) - in which we already have the sb rename sem */ - page = alloc_dentry_path(); - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - cifs_put_tlink(tlink); - free_xid(xid); - free_dentry_path(page); - return ERR_CAST(full_path); - } - - if (d_really_is_positive(direntry)) { - cifs_dbg(FYI, "non-NULL inode in lookup\n"); - } else { - cifs_dbg(FYI, "NULL inode in lookup\n"); - } - cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", - full_path, d_inode(direntry)); - -again: - if (pTcon->posix_extensions) - rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid); - else if (pTcon->unix_ext) { - rc = cifs_get_inode_info_unix(&newInode, full_path, - parent_dir_inode->i_sb, xid); - } else { - rc = cifs_get_inode_info(&newInode, full_path, NULL, - parent_dir_inode->i_sb, xid, NULL); - } - - if (rc == 0) { - /* since paths are not looked up by component - the parent - directories are presumed to be good here */ - renew_parental_timestamps(direntry); - } else if (rc == -EAGAIN && retry_count++ < 10) { - goto again; - } else if (rc == -ENOENT) { - cifs_set_time(direntry, jiffies); - newInode = NULL; - } else { - if (rc != -EACCES) { - cifs_dbg(FYI, "Unexpected lookup error %d\n", rc); - /* We special case check for Access Denied - since that - is a common return code */ - } - newInode = ERR_PTR(rc); - } - free_dentry_path(page); - cifs_put_tlink(tlink); - free_xid(xid); - return d_splice_alias(newInode, direntry); -} - -static int -cifs_d_revalidate(struct dentry *direntry, unsigned int flags) -{ - struct inode *inode; - int rc; - - if (flags & LOOKUP_RCU) - return -ECHILD; - - if (d_really_is_positive(direntry)) { - inode = d_inode(direntry); - if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode))) - CIFS_I(inode)->time = 0; /* force reval */ - - rc = cifs_revalidate_dentry(direntry); - if (rc) { - cifs_dbg(FYI, "cifs_revalidate_dentry failed with rc=%d", rc); - switch (rc) { - case -ENOENT: - case -ESTALE: - /* - * Those errors mean the dentry is invalid - * (file was deleted or recreated) - */ - return 0; - default: - /* - * Otherwise some unexpected error happened - * report it as-is to VFS layer - */ - return rc; - } - } - else { - /* - * If the inode wasn't known to be a dfs entry when - * the dentry was instantiated, such as when created - * via ->readdir(), it needs to be set now since the - * attributes will have been updated by - * cifs_revalidate_dentry(). - */ - if (IS_AUTOMOUNT(inode) && - !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { - spin_lock(&direntry->d_lock); - direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; - spin_unlock(&direntry->d_lock); - } - - return 1; - } - } - - /* - * This may be nfsd (or something), anyway, we can't see the - * intent of this. So, since this can be for creation, drop it. - */ - if (!flags) - return 0; - - /* - * Drop the negative dentry, in order to make sure to use the - * case sensitive name which is specified by user if this is - * for creation. - */ - if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) - return 0; - - if (time_after(jiffies, cifs_get_time(direntry) + HZ) || !lookupCacheEnabled) - return 0; - - return 1; -} - -/* static int cifs_d_delete(struct dentry *direntry) -{ - int rc = 0; - - cifs_dbg(FYI, "In cifs d_delete, name = %pd\n", direntry); - - return rc; -} */ - -const struct dentry_operations cifs_dentry_ops = { - .d_revalidate = cifs_d_revalidate, - .d_automount = cifs_dfs_d_automount, -/* d_delete: cifs_d_delete, */ /* not needed except for debugging */ -}; - -static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) -{ - struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; - unsigned long hash; - wchar_t c; - int i, charlen; - - hash = init_name_hash(dentry); - for (i = 0; i < q->len; i += charlen) { - charlen = codepage->char2uni(&q->name[i], q->len - i, &c); - /* error out if we can't convert the character */ - if (unlikely(charlen < 0)) - return charlen; - hash = partial_name_hash(cifs_toupper(c), hash); - } - q->hash = end_name_hash(hash); - - return 0; -} - -static int cifs_ci_compare(const struct dentry *dentry, - unsigned int len, const char *str, const struct qstr *name) -{ - struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; - wchar_t c1, c2; - int i, l1, l2; - - /* - * We make the assumption here that uppercase characters in the local - * codepage are always the same length as their lowercase counterparts. - * - * If that's ever not the case, then this will fail to match it. - */ - if (name->len != len) - return 1; - - for (i = 0; i < len; i += l1) { - /* Convert characters in both strings to UTF-16. */ - l1 = codepage->char2uni(&str[i], len - i, &c1); - l2 = codepage->char2uni(&name->name[i], name->len - i, &c2); - - /* - * If we can't convert either character, just declare it to - * be 1 byte long and compare the original byte. - */ - if (unlikely(l1 < 0 && l2 < 0)) { - if (str[i] != name->name[i]) - return 1; - l1 = 1; - continue; - } - - /* - * Here, we again ass|u|me that upper/lowercase versions of - * a character are the same length in the local NLS. - */ - if (l1 != l2) - return 1; - - /* Now compare uppercase versions of these characters */ - if (cifs_toupper(c1) != cifs_toupper(c2)) - return 1; - } - - return 0; -} - -const struct dentry_operations cifs_ci_dentry_ops = { - .d_revalidate = cifs_d_revalidate, - .d_hash = cifs_ci_hash, - .d_compare = cifs_ci_compare, - .d_automount = cifs_dfs_d_automount, -}; diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c deleted file mode 100644 index 0458d28d71aa..000000000000 --- a/fs/cifs/dns_resolve.c +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (c) 2007 Igor Mammedov - * Author(s): Igor Mammedov (niallain@gmail.com) - * Steve French (sfrench@us.ibm.com) - * Wang Lei (wang840925@gmail.com) - * David Howells (dhowells@redhat.com) - * - * Contains the CIFS DFS upcall routines used for hostname to - * IP address translation. - * - */ - -#include -#include -#include "dns_resolve.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" - -/** - * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. - * @unc: UNC path specifying the server (with '/' as delimiter) - * @ip_addr: Where to return the IP address. - * @expiry: Where to return the expiry time for the dns record. - * - * The IP address will be returned in string form, and the caller is - * responsible for freeing it. - * - * Returns length of result on success, -ve on error. - */ -int -dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry) -{ - struct sockaddr_storage ss; - const char *hostname, *sep; - char *name; - int len, rc; - - if (!ip_addr || !unc) - return -EINVAL; - - len = strlen(unc); - if (len < 3) { - cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc); - return -EINVAL; - } - - /* Discount leading slashes for cifs */ - len -= 2; - hostname = unc + 2; - - /* Search for server name delimiter */ - sep = memchr(hostname, '/', len); - if (sep) - len = sep - hostname; - else - cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n", - __func__, unc); - - /* Try to interpret hostname as an IPv4 or IPv6 address */ - rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len); - if (rc > 0) - goto name_is_IP_address; - - /* Perform the upcall */ - rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len, - NULL, ip_addr, expiry, false); - if (rc < 0) - cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n", - __func__, len, len, hostname); - else - cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n", - __func__, len, len, hostname, *ip_addr, - expiry ? (*expiry) : 0); - return rc; - -name_is_IP_address: - name = kmalloc(len + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - memcpy(name, hostname, len); - name[len] = 0; - cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n", - __func__, name); - *ip_addr = name; - return 0; -} diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h deleted file mode 100644 index afc0df381246..000000000000 --- a/fs/cifs/dns_resolve.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * DNS Resolver upcall management for CIFS DFS - * Handles host name to IP address resolution - * - * Copyright (c) International Business Machines Corp., 2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _DNS_RESOLVE_H -#define _DNS_RESOLVE_H - -#ifdef __KERNEL__ -extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry); -#endif /* KERNEL */ - -#endif /* _DNS_RESOLVE_H */ diff --git a/fs/cifs/export.c b/fs/cifs/export.c deleted file mode 100644 index 37c28415df1e..000000000000 --- a/fs/cifs/export.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Common Internet FileSystem (CIFS) client - * - * Operations related to support for exporting files via NFSD - * - */ - - /* - * See Documentation/filesystems/nfs/exporting.rst - * and examples in fs/exportfs - * - * Since cifs is a network file system, an "fsid" must be included for - * any nfs exports file entries which refer to cifs paths. In addition - * the cifs mount must be mounted with the "serverino" option (ie use stable - * server inode numbers instead of locally generated temporary ones). - * Although cifs inodes do not use generation numbers (have generation number - * of zero) - the inode number alone should be good enough for simple cases - * in which users want to export cifs shares with NFS. The decode and encode - * could be improved by using a new routine which expects 64 bit inode numbers - * instead of the default 32 bit routines in fs/exportfs - * - */ - -#include -#include -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifsfs.h" - -#ifdef CONFIG_CIFS_NFSD_EXPORT -static struct dentry *cifs_get_parent(struct dentry *dentry) -{ - /* BB need to add code here eventually to enable export via NFSD */ - cifs_dbg(FYI, "get parent for %p\n", dentry); - return ERR_PTR(-EACCES); -} - -const struct export_operations cifs_export_ops = { - .get_parent = cifs_get_parent, -/* Following five export operations are unneeded so far and can default: - .get_dentry = - .get_name = - .find_exported_dentry = - .decode_fh = - .encode_fs = */ -}; - -#endif /* CONFIG_CIFS_NFSD_EXPORT */ - diff --git a/fs/cifs/file.c b/fs/cifs/file.c deleted file mode 100644 index 87dcffece762..000000000000 --- a/fs/cifs/file.c +++ /dev/null @@ -1,5290 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * vfs operations that deal with files - * - * Copyright (C) International Business Machines Corp., 2002,2010 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "fscache.h" -#include "smbdirect.h" -#include "fs_context.h" -#include "cifs_ioctl.h" -#include "cached_dir.h" - -/* - * Mark as invalid, all open files on tree connections since they - * were closed when session to server was lost. - */ -void -cifs_mark_open_files_invalid(struct cifs_tcon *tcon) -{ - struct cifsFileInfo *open_file = NULL; - struct list_head *tmp; - struct list_head *tmp1; - - /* only send once per connect */ - spin_lock(&tcon->ses->ses_lock); - if ((tcon->ses->ses_status != SES_GOOD) || (tcon->status != TID_NEED_RECON)) { - spin_unlock(&tcon->ses->ses_lock); - return; - } - tcon->status = TID_IN_FILES_INVALIDATE; - spin_unlock(&tcon->ses->ses_lock); - - /* list all files open on tree connection and mark them invalid */ - spin_lock(&tcon->open_file_lock); - list_for_each_safe(tmp, tmp1, &tcon->openFileList) { - open_file = list_entry(tmp, struct cifsFileInfo, tlist); - open_file->invalidHandle = true; - open_file->oplock_break_cancelled = true; - } - spin_unlock(&tcon->open_file_lock); - - invalidate_all_cached_dirs(tcon); - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_FILES_INVALIDATE) - tcon->status = TID_NEED_TCON; - spin_unlock(&tcon->tc_lock); - - /* - * BB Add call to invalidate_inodes(sb) for all superblocks mounted - * to this tcon. - */ -} - -static inline int cifs_convert_flags(unsigned int flags) -{ - if ((flags & O_ACCMODE) == O_RDONLY) - return GENERIC_READ; - else if ((flags & O_ACCMODE) == O_WRONLY) - return GENERIC_WRITE; - else if ((flags & O_ACCMODE) == O_RDWR) { - /* GENERIC_ALL is too much permission to request - can cause unnecessary access denied on create */ - /* return GENERIC_ALL; */ - return (GENERIC_READ | GENERIC_WRITE); - } - - return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | - FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | - FILE_READ_DATA); -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static u32 cifs_posix_convert_flags(unsigned int flags) -{ - u32 posix_flags = 0; - - if ((flags & O_ACCMODE) == O_RDONLY) - posix_flags = SMB_O_RDONLY; - else if ((flags & O_ACCMODE) == O_WRONLY) - posix_flags = SMB_O_WRONLY; - else if ((flags & O_ACCMODE) == O_RDWR) - posix_flags = SMB_O_RDWR; - - if (flags & O_CREAT) { - posix_flags |= SMB_O_CREAT; - if (flags & O_EXCL) - posix_flags |= SMB_O_EXCL; - } else if (flags & O_EXCL) - cifs_dbg(FYI, "Application %s pid %d has incorrectly set O_EXCL flag but not O_CREAT on file open. Ignoring O_EXCL\n", - current->comm, current->tgid); - - if (flags & O_TRUNC) - posix_flags |= SMB_O_TRUNC; - /* be safe and imply O_SYNC for O_DSYNC */ - if (flags & O_DSYNC) - posix_flags |= SMB_O_SYNC; - if (flags & O_DIRECTORY) - posix_flags |= SMB_O_DIRECTORY; - if (flags & O_NOFOLLOW) - posix_flags |= SMB_O_NOFOLLOW; - if (flags & O_DIRECT) - posix_flags |= SMB_O_DIRECT; - - return posix_flags; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static inline int cifs_get_disposition(unsigned int flags) -{ - if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - return FILE_CREATE; - else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) - return FILE_OVERWRITE_IF; - else if ((flags & O_CREAT) == O_CREAT) - return FILE_OPEN_IF; - else if ((flags & O_TRUNC) == O_TRUNC) - return FILE_OVERWRITE; - else - return FILE_OPEN; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -int cifs_posix_open(const char *full_path, struct inode **pinode, - struct super_block *sb, int mode, unsigned int f_flags, - __u32 *poplock, __u16 *pnetfid, unsigned int xid) -{ - int rc; - FILE_UNIX_BASIC_INFO *presp_data; - __u32 posix_flags = 0; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_fattr fattr; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - - cifs_dbg(FYI, "posix open %s\n", full_path); - - presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); - if (presp_data == NULL) - return -ENOMEM; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto posix_open_ret; - } - - tcon = tlink_tcon(tlink); - mode &= ~current_umask(); - - posix_flags = cifs_posix_convert_flags(f_flags); - rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, - poplock, full_path, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_put_tlink(tlink); - - if (rc) - goto posix_open_ret; - - if (presp_data->Type == cpu_to_le32(-1)) - goto posix_open_ret; /* open ok, caller does qpathinfo */ - - if (!pinode) - goto posix_open_ret; /* caller does not need info */ - - cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb); - - /* get new inode and set it up */ - if (*pinode == NULL) { - cifs_fill_uniqueid(sb, &fattr); - *pinode = cifs_iget(sb, &fattr); - if (!*pinode) { - rc = -ENOMEM; - goto posix_open_ret; - } - } else { - cifs_revalidate_mapping(*pinode); - rc = cifs_fattr_to_inode(*pinode, &fattr); - } - -posix_open_ret: - kfree(presp_data); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock, - struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf) -{ - int rc; - int desired_access; - int disposition; - int create_options = CREATE_NOT_DIR; - struct TCP_Server_Info *server = tcon->ses->server; - struct cifs_open_parms oparms; - - if (!server->ops->open) - return -ENOSYS; - - desired_access = cifs_convert_flags(f_flags); - -/********************************************************************* - * open flag mapping table: - * - * POSIX Flag CIFS Disposition - * ---------- ---------------- - * O_CREAT FILE_OPEN_IF - * O_CREAT | O_EXCL FILE_CREATE - * O_CREAT | O_TRUNC FILE_OVERWRITE_IF - * O_TRUNC FILE_OVERWRITE - * none of the above FILE_OPEN - * - * Note that there is not a direct match between disposition - * FILE_SUPERSEDE (ie create whether or not file exists although - * O_CREAT | O_TRUNC is similar but truncates the existing - * file rather than creating a new file as FILE_SUPERSEDE does - * (which uses the attributes / metadata passed in on open call) - *? - *? O_SYNC is a reasonable match to CIFS writethrough flag - *? and the read write flags match reasonably. O_LARGEFILE - *? is irrelevant because largefile support is always used - *? by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY, - * O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation - *********************************************************************/ - - disposition = cifs_get_disposition(f_flags); - - /* BB pass O_SYNC flag through on file attributes .. BB */ - - /* O_SYNC also has bit for O_DSYNC so following check picks up either */ - if (f_flags & O_SYNC) - create_options |= CREATE_WRITE_THROUGH; - - if (f_flags & O_DIRECT) - create_options |= CREATE_NO_BUFFER; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = desired_access, - .create_options = cifs_create_options(cifs_sb, create_options), - .disposition = disposition, - .path = full_path, - .fid = fid, - }; - - rc = server->ops->open(xid, &oparms, oplock, buf); - if (rc) - return rc; - - /* TODO: Add support for calling posix query info but with passing in fid */ - if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, - xid); - else - rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, - xid, fid); - - if (rc) { - server->ops->close(xid, tcon, fid); - if (rc == -ESTALE) - rc = -EOPENSTALE; - } - - return rc; -} - -static bool -cifs_has_mand_locks(struct cifsInodeInfo *cinode) -{ - struct cifs_fid_locks *cur; - bool has_locks = false; - - down_read(&cinode->lock_sem); - list_for_each_entry(cur, &cinode->llist, llist) { - if (!list_empty(&cur->locks)) { - has_locks = true; - break; - } - } - up_read(&cinode->lock_sem); - return has_locks; -} - -void -cifs_down_write(struct rw_semaphore *sem) -{ - while (!down_write_trylock(sem)) - msleep(10); -} - -static void cifsFileInfo_put_work(struct work_struct *work); - -struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, - struct tcon_link *tlink, __u32 oplock, - const char *symlink_target) -{ - struct dentry *dentry = file_dentry(file); - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifsFileInfo *cfile; - struct cifs_fid_locks *fdlocks; - struct cifs_tcon *tcon = tlink_tcon(tlink); - struct TCP_Server_Info *server = tcon->ses->server; - - cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); - if (cfile == NULL) - return cfile; - - fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL); - if (!fdlocks) { - kfree(cfile); - return NULL; - } - - if (symlink_target) { - cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL); - if (!cfile->symlink_target) { - kfree(fdlocks); - kfree(cfile); - return NULL; - } - } - - INIT_LIST_HEAD(&fdlocks->locks); - fdlocks->cfile = cfile; - cfile->llist = fdlocks; - - cfile->count = 1; - cfile->pid = current->tgid; - cfile->uid = current_fsuid(); - cfile->dentry = dget(dentry); - cfile->f_flags = file->f_flags; - cfile->invalidHandle = false; - cfile->deferred_close_scheduled = false; - cfile->tlink = cifs_get_tlink(tlink); - INIT_WORK(&cfile->oplock_break, cifs_oplock_break); - INIT_WORK(&cfile->put, cifsFileInfo_put_work); - INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close); - mutex_init(&cfile->fh_mutex); - spin_lock_init(&cfile->file_info_lock); - - cifs_sb_active(inode->i_sb); - - /* - * If the server returned a read oplock and we have mandatory brlocks, - * set oplock level to None. - */ - if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { - cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); - oplock = 0; - } - - cifs_down_write(&cinode->lock_sem); - list_add(&fdlocks->llist, &cinode->llist); - up_write(&cinode->lock_sem); - - spin_lock(&tcon->open_file_lock); - if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) - oplock = fid->pending_open->oplock; - list_del(&fid->pending_open->olist); - - fid->purge_cache = false; - server->ops->set_fid(cfile, fid, oplock); - - list_add(&cfile->tlist, &tcon->openFileList); - atomic_inc(&tcon->num_local_opens); - - /* if readable file instance put first in list*/ - spin_lock(&cinode->open_file_lock); - if (file->f_mode & FMODE_READ) - list_add(&cfile->flist, &cinode->openFileList); - else - list_add_tail(&cfile->flist, &cinode->openFileList); - spin_unlock(&cinode->open_file_lock); - spin_unlock(&tcon->open_file_lock); - - if (fid->purge_cache) - cifs_zap_mapping(inode); - - file->private_data = cfile; - return cfile; -} - -struct cifsFileInfo * -cifsFileInfo_get(struct cifsFileInfo *cifs_file) -{ - spin_lock(&cifs_file->file_info_lock); - cifsFileInfo_get_locked(cifs_file); - spin_unlock(&cifs_file->file_info_lock); - return cifs_file; -} - -static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) -{ - struct inode *inode = d_inode(cifs_file->dentry); - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct cifsLockInfo *li, *tmp; - struct super_block *sb = inode->i_sb; - - /* - * Delete any outstanding lock records. We'll lose them when the file - * is closed anyway. - */ - cifs_down_write(&cifsi->lock_sem); - list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { - list_del(&li->llist); - cifs_del_lock_waiters(li); - kfree(li); - } - list_del(&cifs_file->llist->llist); - kfree(cifs_file->llist); - up_write(&cifsi->lock_sem); - - cifs_put_tlink(cifs_file->tlink); - dput(cifs_file->dentry); - cifs_sb_deactive(sb); - kfree(cifs_file->symlink_target); - kfree(cifs_file); -} - -static void cifsFileInfo_put_work(struct work_struct *work) -{ - struct cifsFileInfo *cifs_file = container_of(work, - struct cifsFileInfo, put); - - cifsFileInfo_put_final(cifs_file); -} - -/** - * cifsFileInfo_put - release a reference of file priv data - * - * Always potentially wait for oplock handler. See _cifsFileInfo_put(). - * - * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file - */ -void cifsFileInfo_put(struct cifsFileInfo *cifs_file) -{ - _cifsFileInfo_put(cifs_file, true, true); -} - -/** - * _cifsFileInfo_put - release a reference of file priv data - * - * This may involve closing the filehandle @cifs_file out on the - * server. Must be called without holding tcon->open_file_lock, - * cinode->open_file_lock and cifs_file->file_info_lock. - * - * If @wait_for_oplock_handler is true and we are releasing the last - * reference, wait for any running oplock break handler of the file - * and cancel any pending one. - * - * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file - * @wait_oplock_handler: must be false if called from oplock_break_handler - * @offload: not offloaded on close and oplock breaks - * - */ -void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, - bool wait_oplock_handler, bool offload) -{ - struct inode *inode = d_inode(cifs_file->dentry); - struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct super_block *sb = inode->i_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_fid fid = {}; - struct cifs_pending_open open; - bool oplock_break_cancelled; - - spin_lock(&tcon->open_file_lock); - spin_lock(&cifsi->open_file_lock); - spin_lock(&cifs_file->file_info_lock); - if (--cifs_file->count > 0) { - spin_unlock(&cifs_file->file_info_lock); - spin_unlock(&cifsi->open_file_lock); - spin_unlock(&tcon->open_file_lock); - return; - } - spin_unlock(&cifs_file->file_info_lock); - - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &fid); - - /* store open in pending opens to make sure we don't miss lease break */ - cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open); - - /* remove it from the lists */ - list_del(&cifs_file->flist); - list_del(&cifs_file->tlist); - atomic_dec(&tcon->num_local_opens); - - if (list_empty(&cifsi->openFileList)) { - cifs_dbg(FYI, "closing last open instance for inode %p\n", - d_inode(cifs_file->dentry)); - /* - * In strict cache mode we need invalidate mapping on the last - * close because it may cause a error when we open this file - * again and get at least level II oplock. - */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) - set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); - cifs_set_oplock_level(cifsi, 0); - } - - spin_unlock(&cifsi->open_file_lock); - spin_unlock(&tcon->open_file_lock); - - oplock_break_cancelled = wait_oplock_handler ? - cancel_work_sync(&cifs_file->oplock_break) : false; - - if (!tcon->need_reconnect && !cifs_file->invalidHandle) { - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int xid; - - xid = get_xid(); - if (server->ops->close_getattr) - server->ops->close_getattr(xid, tcon, cifs_file); - else if (server->ops->close) - server->ops->close(xid, tcon, &cifs_file->fid); - _free_xid(xid); - } - - if (oplock_break_cancelled) - cifs_done_oplock_break(cifsi); - - cifs_del_pending_open(&open); - - if (offload) - queue_work(fileinfo_put_wq, &cifs_file->put); - else - cifsFileInfo_put_final(cifs_file); -} - -int cifs_open(struct inode *inode, struct file *file) - -{ - int rc = -EACCES; - unsigned int xid; - __u32 oplock; - struct cifs_sb_info *cifs_sb; - struct TCP_Server_Info *server; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cifsFileInfo *cfile = NULL; - void *page; - const char *full_path; - bool posix_open_ok = false; - struct cifs_fid fid = {}; - struct cifs_pending_open open; - struct cifs_open_info_data data = {}; - - xid = get_xid(); - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) { - free_xid(xid); - return -EIO; - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - free_xid(xid); - return PTR_ERR(tlink); - } - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - page = alloc_dentry_path(); - full_path = build_path_from_dentry(file_dentry(file), page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - cifs_dbg(FYI, "inode = 0x%p file flags are 0x%x for %s\n", - inode, file->f_flags, full_path); - - if (file->f_flags & O_DIRECT && - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - file->f_op = &cifs_file_direct_nobrl_ops; - else - file->f_op = &cifs_file_direct_ops; - } - - /* Get the cached handle as SMB2 close is deferred */ - rc = cifs_get_readable_path(tcon, full_path, &cfile); - if (rc == 0) { - if (file->f_flags == cfile->f_flags) { - file->private_data = cfile; - spin_lock(&CIFS_I(inode)->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&CIFS_I(inode)->deferred_lock); - goto use_cache; - } else { - _cifsFileInfo_put(cfile, true, false); - } - } - - if (server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (!tcon->broken_posix_open && tcon->unix_ext && - cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - /* can not refresh inode info since size could be stale */ - rc = cifs_posix_open(full_path, &inode, inode->i_sb, - cifs_sb->ctx->file_mode /* ignored */, - file->f_flags, &oplock, &fid.netfid, xid); - if (rc == 0) { - cifs_dbg(FYI, "posix open succeeded\n"); - posix_open_ok = true; - } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { - if (tcon->ses->serverNOS) - cifs_dbg(VFS, "server %s of type %s returned unexpected error on SMB posix open, disabling posix open support. Check if server update available.\n", - tcon->ses->ip_addr, - tcon->ses->serverNOS); - tcon->broken_posix_open = true; - } else if ((rc != -EIO) && (rc != -EREMOTE) && - (rc != -EOPNOTSUPP)) /* path not found or net err */ - goto out; - /* - * Else fallthrough to retry open the old way on network i/o - * or DFS errors. - */ - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &fid); - - cifs_add_pending_open(&fid, tlink, &open); - - if (!posix_open_ok) { - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &fid); - - rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid, - xid, &data); - if (rc) { - cifs_del_pending_open(&open); - goto out; - } - } - - cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target); - if (cfile == NULL) { - if (server->ops->close) - server->ops->close(xid, tcon, &fid); - cifs_del_pending_open(&open); - rc = -ENOMEM; - goto out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) { - /* - * Time to set mode which we can not set earlier due to - * problems creating new read-only files. - */ - struct cifs_unix_set_info_args args = { - .mode = inode->i_mode, - .uid = INVALID_UID, /* no change */ - .gid = INVALID_GID, /* no change */ - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = 0, - }; - CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid, - cfile->pid); - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -use_cache: - fscache_use_cookie(cifs_inode_cookie(file_inode(file)), - file->f_mode & FMODE_WRITE); - if (file->f_flags & O_DIRECT && - (!((file->f_flags & O_ACCMODE) != O_RDONLY) || - file->f_flags & O_APPEND)) - cifs_invalidate_cache(file_inode(file), - FSCACHE_INVAL_DIO_WRITE); - -out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - cifs_free_open_info(&data); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int cifs_push_posix_locks(struct cifsFileInfo *cfile); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* - * Try to reacquire byte range locks that were released when session - * to server was lost. - */ -static int -cifs_relock_file(struct cifsFileInfo *cfile) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - int rc = 0; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - down_read_nested(&cinode->lock_sem, SINGLE_DEPTH_NESTING); - if (cinode->can_cache_brlcks) { - /* can cache locks - no need to relock */ - up_read(&cinode->lock_sem); - return rc; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - rc = cifs_push_posix_locks(cfile); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = tcon->ses->server->ops->push_mand_locks(cfile); - - up_read(&cinode->lock_sem); - return rc; -} - -static int -cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) -{ - int rc = -EACCES; - unsigned int xid; - __u32 oplock; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsInodeInfo *cinode; - struct inode *inode; - void *page; - const char *full_path; - int desired_access; - int disposition = FILE_OPEN; - int create_options = CREATE_NOT_DIR; - struct cifs_open_parms oparms; - - xid = get_xid(); - mutex_lock(&cfile->fh_mutex); - if (!cfile->invalidHandle) { - mutex_unlock(&cfile->fh_mutex); - free_xid(xid); - return 0; - } - - inode = d_inode(cfile->dentry); - cifs_sb = CIFS_SB(inode->i_sb); - tcon = tlink_tcon(cfile->tlink); - server = tcon->ses->server; - - /* - * Can not grab rename sem here because various ops, including those - * that already have the rename sem can end up causing writepage to get - * called and if the server was down that means we end up here, and we - * can never tell if the caller already has the rename_sem. - */ - page = alloc_dentry_path(); - full_path = build_path_from_dentry(cfile->dentry, page); - if (IS_ERR(full_path)) { - mutex_unlock(&cfile->fh_mutex); - free_dentry_path(page); - free_xid(xid); - return PTR_ERR(full_path); - } - - cifs_dbg(FYI, "inode = 0x%p file flags 0x%x for %s\n", - inode, cfile->f_flags, full_path); - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext && cap_unix(tcon->ses) && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - /* - * O_CREAT, O_EXCL and O_TRUNC already had their effect on the - * original open. Must mask them off for a reopen. - */ - unsigned int oflags = cfile->f_flags & - ~(O_CREAT | O_EXCL | O_TRUNC); - - rc = cifs_posix_open(full_path, NULL, inode->i_sb, - cifs_sb->ctx->file_mode /* ignored */, - oflags, &oplock, &cfile->fid.netfid, xid); - if (rc == 0) { - cifs_dbg(FYI, "posix reopen succeeded\n"); - oparms.reconnect = true; - goto reopen_success; - } - /* - * fallthrough to retry open the old way on errors, especially - * in the reconnect path it is important to retry hard - */ - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - desired_access = cifs_convert_flags(cfile->f_flags); - - /* O_SYNC also has bit for O_DSYNC so following check picks up either */ - if (cfile->f_flags & O_SYNC) - create_options |= CREATE_WRITE_THROUGH; - - if (cfile->f_flags & O_DIRECT) - create_options |= CREATE_NO_BUFFER; - - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &cfile->fid); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = desired_access, - .create_options = cifs_create_options(cifs_sb, create_options), - .disposition = disposition, - .path = full_path, - .fid = &cfile->fid, - .reconnect = true, - }; - - /* - * Can not refresh inode by passing in file_info buf to be returned by - * ops->open and then calling get_inode_info with returned buf since - * file might have write behind data that needs to be flushed and server - * version of file size can be stale. If we knew for sure that inode was - * not dirty locally we could do this. - */ - rc = server->ops->open(xid, &oparms, &oplock, NULL); - if (rc == -ENOENT && oparms.reconnect == false) { - /* durable handle timeout is expired - open the file again */ - rc = server->ops->open(xid, &oparms, &oplock, NULL); - /* indicate that we need to relock the file */ - oparms.reconnect = true; - } - - if (rc) { - mutex_unlock(&cfile->fh_mutex); - cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc); - cifs_dbg(FYI, "oplock: %d\n", oplock); - goto reopen_error_exit; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -reopen_success: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - cfile->invalidHandle = false; - mutex_unlock(&cfile->fh_mutex); - cinode = CIFS_I(inode); - - if (can_flush) { - rc = filemap_write_and_wait(inode->i_mapping); - if (!is_interrupt_error(rc)) - mapping_set_error(inode->i_mapping, rc); - - if (tcon->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid); - else if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, - inode->i_sb, xid); - else - rc = cifs_get_inode_info(&inode, full_path, NULL, - inode->i_sb, xid, NULL); - } - /* - * Else we are writing out data to server already and could deadlock if - * we tried to flush data, and since we do not know if we have data that - * would invalidate the current end of file on the server we can not go - * to the server to get the new inode info. - */ - - /* - * If the server returned a read oplock and we have mandatory brlocks, - * set oplock level to None. - */ - if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { - cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); - oplock = 0; - } - - server->ops->set_fid(cfile, &cfile->fid, oplock); - if (oparms.reconnect) - cifs_relock_file(cfile); - -reopen_error_exit: - free_dentry_path(page); - free_xid(xid); - return rc; -} - -void smb2_deferred_work_close(struct work_struct *work) -{ - struct cifsFileInfo *cfile = container_of(work, - struct cifsFileInfo, deferred.work); - - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - cifs_del_deferred_close(cfile); - cfile->deferred_close_scheduled = false; - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - _cifsFileInfo_put(cfile, true, false); -} - -int cifs_close(struct inode *inode, struct file *file) -{ - struct cifsFileInfo *cfile; - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_deferred_close *dclose; - - cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE); - - if (file->private_data != NULL) { - cfile = file->private_data; - file->private_data = NULL; - dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL); - if ((cinode->oplock == CIFS_CACHE_RHW_FLG) && - cinode->lease_granted && - !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) && - dclose) { - if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { - inode->i_ctime = inode->i_mtime = current_time(inode); - } - spin_lock(&cinode->deferred_lock); - cifs_add_deferred_close(cfile, dclose); - if (cfile->deferred_close_scheduled && - delayed_work_pending(&cfile->deferred)) { - /* - * If there is no pending work, mod_delayed_work queues new work. - * So, Increase the ref count to avoid use-after-free. - */ - if (!mod_delayed_work(deferredclose_wq, - &cfile->deferred, cifs_sb->ctx->closetimeo)) - cifsFileInfo_get(cfile); - } else { - /* Deferred close for files */ - queue_delayed_work(deferredclose_wq, - &cfile->deferred, cifs_sb->ctx->closetimeo); - cfile->deferred_close_scheduled = true; - spin_unlock(&cinode->deferred_lock); - return 0; - } - spin_unlock(&cinode->deferred_lock); - _cifsFileInfo_put(cfile, true, false); - } else { - _cifsFileInfo_put(cfile, true, false); - kfree(dclose); - } - } - - /* return code from the ->release op is always ignored */ - return 0; -} - -void -cifs_reopen_persistent_handles(struct cifs_tcon *tcon) -{ - struct cifsFileInfo *open_file, *tmp; - struct list_head tmp_list; - - if (!tcon->use_persistent || !tcon->need_reopen_files) - return; - - tcon->need_reopen_files = false; - - cifs_dbg(FYI, "Reopen persistent handles\n"); - INIT_LIST_HEAD(&tmp_list); - - /* list all files open on tree connection, reopen resilient handles */ - spin_lock(&tcon->open_file_lock); - list_for_each_entry(open_file, &tcon->openFileList, tlist) { - if (!open_file->invalidHandle) - continue; - cifsFileInfo_get(open_file); - list_add_tail(&open_file->rlist, &tmp_list); - } - spin_unlock(&tcon->open_file_lock); - - list_for_each_entry_safe(open_file, tmp, &tmp_list, rlist) { - if (cifs_reopen_file(open_file, false /* do not flush */)) - tcon->need_reopen_files = true; - list_del_init(&open_file->rlist); - cifsFileInfo_put(open_file); - } -} - -int cifs_closedir(struct inode *inode, struct file *file) -{ - int rc = 0; - unsigned int xid; - struct cifsFileInfo *cfile = file->private_data; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - char *buf; - - cifs_dbg(FYI, "Closedir inode = 0x%p\n", inode); - - if (cfile == NULL) - return rc; - - xid = get_xid(); - tcon = tlink_tcon(cfile->tlink); - server = tcon->ses->server; - - cifs_dbg(FYI, "Freeing private data in close dir\n"); - spin_lock(&cfile->file_info_lock); - if (server->ops->dir_needs_close(cfile)) { - cfile->invalidHandle = true; - spin_unlock(&cfile->file_info_lock); - if (server->ops->close_dir) - rc = server->ops->close_dir(xid, tcon, &cfile->fid); - else - rc = -ENOSYS; - cifs_dbg(FYI, "Closing uncompleted readdir with rc %d\n", rc); - /* not much we can do if it fails anyway, ignore rc */ - rc = 0; - } else - spin_unlock(&cfile->file_info_lock); - - buf = cfile->srch_inf.ntwrk_buf_start; - if (buf) { - cifs_dbg(FYI, "closedir free smb buf in srch struct\n"); - cfile->srch_inf.ntwrk_buf_start = NULL; - if (cfile->srch_inf.smallBuf) - cifs_small_buf_release(buf); - else - cifs_buf_release(buf); - } - - cifs_put_tlink(cfile->tlink); - kfree(file->private_data); - file->private_data = NULL; - /* BB can we lock the filestruct while this is going on? */ - free_xid(xid); - return rc; -} - -static struct cifsLockInfo * -cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 flags) -{ - struct cifsLockInfo *lock = - kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); - if (!lock) - return lock; - lock->offset = offset; - lock->length = length; - lock->type = type; - lock->pid = current->tgid; - lock->flags = flags; - INIT_LIST_HEAD(&lock->blist); - init_waitqueue_head(&lock->block_q); - return lock; -} - -void -cifs_del_lock_waiters(struct cifsLockInfo *lock) -{ - struct cifsLockInfo *li, *tmp; - list_for_each_entry_safe(li, tmp, &lock->blist, blist) { - list_del_init(&li->blist); - wake_up(&li->block_q); - } -} - -#define CIFS_LOCK_OP 0 -#define CIFS_READ_OP 1 -#define CIFS_WRITE_OP 2 - -/* @rw_check : 0 - no op, 1 - read, 2 - write */ -static bool -cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, - __u64 length, __u8 type, __u16 flags, - struct cifsFileInfo *cfile, - struct cifsLockInfo **conf_lock, int rw_check) -{ - struct cifsLockInfo *li; - struct cifsFileInfo *cur_cfile = fdlocks->cfile; - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - - list_for_each_entry(li, &fdlocks->locks, llist) { - if (offset + length <= li->offset || - offset >= li->offset + li->length) - continue; - if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid && - server->ops->compare_fids(cfile, cur_cfile)) { - /* shared lock prevents write op through the same fid */ - if (!(li->type & server->vals->shared_lock_type) || - rw_check != CIFS_WRITE_OP) - continue; - } - if ((type & server->vals->shared_lock_type) && - ((server->ops->compare_fids(cfile, cur_cfile) && - current->tgid == li->pid) || type == li->type)) - continue; - if (rw_check == CIFS_LOCK_OP && - (flags & FL_OFDLCK) && (li->flags & FL_OFDLCK) && - server->ops->compare_fids(cfile, cur_cfile)) - continue; - if (conf_lock) - *conf_lock = li; - return true; - } - return false; -} - -bool -cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, - __u8 type, __u16 flags, - struct cifsLockInfo **conf_lock, int rw_check) -{ - bool rc = false; - struct cifs_fid_locks *cur; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - - list_for_each_entry(cur, &cinode->llist, llist) { - rc = cifs_find_fid_lock_conflict(cur, offset, length, type, - flags, cfile, conf_lock, - rw_check); - if (rc) - break; - } - - return rc; -} - -/* - * Check if there is another lock that prevents us to set the lock (mandatory - * style). If such a lock exists, update the flock structure with its - * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks - * or leave it the same if we can't. Returns 0 if we don't need to request to - * the server or 1 otherwise. - */ -static int -cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, - __u8 type, struct file_lock *flock) -{ - int rc = 0; - struct cifsLockInfo *conf_lock; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - bool exist; - - down_read(&cinode->lock_sem); - - exist = cifs_find_lock_conflict(cfile, offset, length, type, - flock->fl_flags, &conf_lock, - CIFS_LOCK_OP); - if (exist) { - flock->fl_start = conf_lock->offset; - flock->fl_end = conf_lock->offset + conf_lock->length - 1; - flock->fl_pid = conf_lock->pid; - if (conf_lock->type & server->vals->shared_lock_type) - flock->fl_type = F_RDLCK; - else - flock->fl_type = F_WRLCK; - } else if (!cinode->can_cache_brlcks) - rc = 1; - else - flock->fl_type = F_UNLCK; - - up_read(&cinode->lock_sem); - return rc; -} - -static void -cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - cifs_down_write(&cinode->lock_sem); - list_add_tail(&lock->llist, &cfile->llist->locks); - up_write(&cinode->lock_sem); -} - -/* - * Set the byte-range lock (mandatory style). Returns: - * 1) 0, if we set the lock and don't need to request to the server; - * 2) 1, if no locks prevent us but we need to request to the server; - * 3) -EACCES, if there is a lock that prevents us and wait is false. - */ -static int -cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, - bool wait) -{ - struct cifsLockInfo *conf_lock; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - bool exist; - int rc = 0; - -try_again: - exist = false; - cifs_down_write(&cinode->lock_sem); - - exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, - lock->type, lock->flags, &conf_lock, - CIFS_LOCK_OP); - if (!exist && cinode->can_cache_brlcks) { - list_add_tail(&lock->llist, &cfile->llist->locks); - up_write(&cinode->lock_sem); - return rc; - } - - if (!exist) - rc = 1; - else if (!wait) - rc = -EACCES; - else { - list_add_tail(&lock->blist, &conf_lock->blist); - up_write(&cinode->lock_sem); - rc = wait_event_interruptible(lock->block_q, - (lock->blist.prev == &lock->blist) && - (lock->blist.next == &lock->blist)); - if (!rc) - goto try_again; - cifs_down_write(&cinode->lock_sem); - list_del_init(&lock->blist); - } - - up_write(&cinode->lock_sem); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/* - * Check if there is another lock that prevents us to set the lock (posix - * style). If such a lock exists, update the flock structure with its - * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks - * or leave it the same if we can't. Returns 0 if we don't need to request to - * the server or 1 otherwise. - */ -static int -cifs_posix_lock_test(struct file *file, struct file_lock *flock) -{ - int rc = 0; - struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); - unsigned char saved_type = flock->fl_type; - - if ((flock->fl_flags & FL_POSIX) == 0) - return 1; - - down_read(&cinode->lock_sem); - posix_test_lock(file, flock); - - if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) { - flock->fl_type = saved_type; - rc = 1; - } - - up_read(&cinode->lock_sem); - return rc; -} - -/* - * Set the byte-range lock (posix style). Returns: - * 1) <0, if the error occurs while setting the lock; - * 2) 0, if we set the lock and don't need to request to the server; - * 3) FILE_LOCK_DEFERRED, if we will wait for some other file_lock; - * 4) FILE_LOCK_DEFERRED + 1, if we need to request to the server. - */ -static int -cifs_posix_lock_set(struct file *file, struct file_lock *flock) -{ - struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); - int rc = FILE_LOCK_DEFERRED + 1; - - if ((flock->fl_flags & FL_POSIX) == 0) - return rc; - - cifs_down_write(&cinode->lock_sem); - if (!cinode->can_cache_brlcks) { - up_write(&cinode->lock_sem); - return rc; - } - - rc = posix_lock_file(file, flock, NULL); - up_write(&cinode->lock_sem); - return rc; -} - -int -cifs_push_mandatory_locks(struct cifsFileInfo *cfile) -{ - unsigned int xid; - int rc = 0, stored_rc; - struct cifsLockInfo *li, *tmp; - struct cifs_tcon *tcon; - unsigned int num, max_num, max_buf; - LOCKING_ANDX_RANGE *buf, *cur; - static const int types[] = { - LOCKING_ANDX_LARGE_FILES, - LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES - }; - int i; - - xid = get_xid(); - tcon = tlink_tcon(cfile->tlink); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it before using. - */ - max_buf = tcon->ses->server->maxBuf; - if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) { - free_xid(xid); - return -EINVAL; - } - - BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > - PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), - PAGE_SIZE); - max_num = (max_buf - sizeof(struct smb_hdr)) / - sizeof(LOCKING_ANDX_RANGE); - buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); - if (!buf) { - free_xid(xid); - return -ENOMEM; - } - - for (i = 0; i < 2; i++) { - cur = buf; - num = 0; - list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { - if (li->type != types[i]) - continue; - cur->Pid = cpu_to_le16(li->pid); - cur->LengthLow = cpu_to_le32((u32)li->length); - cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); - cur->OffsetLow = cpu_to_le32((u32)li->offset); - cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); - if (++num == max_num) { - stored_rc = cifs_lockv(xid, tcon, - cfile->fid.netfid, - (__u8)li->type, 0, num, - buf); - if (stored_rc) - rc = stored_rc; - cur = buf; - num = 0; - } else - cur++; - } - - if (num) { - stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, - (__u8)types[i], 0, num, buf); - if (stored_rc) - rc = stored_rc; - } - } - - kfree(buf); - free_xid(xid); - return rc; -} - -static __u32 -hash_lockowner(fl_owner_t owner) -{ - return cifs_lock_secret ^ hash32_ptr((const void *)owner); -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -struct lock_to_push { - struct list_head llist; - __u64 offset; - __u64 length; - __u32 pid; - __u16 netfid; - __u8 type; -}; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_push_posix_locks(struct cifsFileInfo *cfile) -{ - struct inode *inode = d_inode(cfile->dentry); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct file_lock *flock; - struct file_lock_context *flctx = inode->i_flctx; - unsigned int count = 0, i; - int rc = 0, xid, type; - struct list_head locks_to_send, *el; - struct lock_to_push *lck, *tmp; - __u64 length; - - xid = get_xid(); - - if (!flctx) - goto out; - - spin_lock(&flctx->flc_lock); - list_for_each(el, &flctx->flc_posix) { - count++; - } - spin_unlock(&flctx->flc_lock); - - INIT_LIST_HEAD(&locks_to_send); - - /* - * Allocating count locks is enough because no FL_POSIX locks can be - * added to the list while we are holding cinode->lock_sem that - * protects locking operations of this inode. - */ - for (i = 0; i < count; i++) { - lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL); - if (!lck) { - rc = -ENOMEM; - goto err_out; - } - list_add_tail(&lck->llist, &locks_to_send); - } - - el = locks_to_send.next; - spin_lock(&flctx->flc_lock); - list_for_each_entry(flock, &flctx->flc_posix, fl_list) { - if (el == &locks_to_send) { - /* - * The list ended. We don't have enough allocated - * structures - something is really wrong. - */ - cifs_dbg(VFS, "Can't push all brlocks!\n"); - break; - } - length = cifs_flock_len(flock); - if (flock->fl_type == F_RDLCK || flock->fl_type == F_SHLCK) - type = CIFS_RDLCK; - else - type = CIFS_WRLCK; - lck = list_entry(el, struct lock_to_push, llist); - lck->pid = hash_lockowner(flock->fl_owner); - lck->netfid = cfile->fid.netfid; - lck->length = length; - lck->type = type; - lck->offset = flock->fl_start; - } - spin_unlock(&flctx->flc_lock); - - list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { - int stored_rc; - - stored_rc = CIFSSMBPosixLock(xid, tcon, lck->netfid, lck->pid, - lck->offset, lck->length, NULL, - lck->type, 0); - if (stored_rc) - rc = stored_rc; - list_del(&lck->llist); - kfree(lck); - } - -out: - free_xid(xid); - return rc; -err_out: - list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { - list_del(&lck->llist); - kfree(lck); - } - goto out; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_push_locks(struct cifsFileInfo *cfile) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - int rc = 0; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - /* we are going to update can_cache_brlcks here - need a write access */ - cifs_down_write(&cinode->lock_sem); - if (!cinode->can_cache_brlcks) { - up_write(&cinode->lock_sem); - return rc; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - rc = cifs_push_posix_locks(cfile); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = tcon->ses->server->ops->push_mand_locks(cfile); - - cinode->can_cache_brlcks = false; - up_write(&cinode->lock_sem); - return rc; -} - -static void -cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, - bool *wait_flag, struct TCP_Server_Info *server) -{ - if (flock->fl_flags & FL_POSIX) - cifs_dbg(FYI, "Posix\n"); - if (flock->fl_flags & FL_FLOCK) - cifs_dbg(FYI, "Flock\n"); - if (flock->fl_flags & FL_SLEEP) { - cifs_dbg(FYI, "Blocking lock\n"); - *wait_flag = true; - } - if (flock->fl_flags & FL_ACCESS) - cifs_dbg(FYI, "Process suspended by mandatory locking - not implemented yet\n"); - if (flock->fl_flags & FL_LEASE) - cifs_dbg(FYI, "Lease on file - not implemented yet\n"); - if (flock->fl_flags & - (~(FL_POSIX | FL_FLOCK | FL_SLEEP | - FL_ACCESS | FL_LEASE | FL_CLOSE | FL_OFDLCK))) - cifs_dbg(FYI, "Unknown lock flags 0x%x\n", flock->fl_flags); - - *type = server->vals->large_lock_type; - if (flock->fl_type == F_WRLCK) { - cifs_dbg(FYI, "F_WRLCK\n"); - *type |= server->vals->exclusive_lock_type; - *lock = 1; - } else if (flock->fl_type == F_UNLCK) { - cifs_dbg(FYI, "F_UNLCK\n"); - *type |= server->vals->unlock_lock_type; - *unlock = 1; - /* Check if unlock includes more than one lock range */ - } else if (flock->fl_type == F_RDLCK) { - cifs_dbg(FYI, "F_RDLCK\n"); - *type |= server->vals->shared_lock_type; - *lock = 1; - } else if (flock->fl_type == F_EXLCK) { - cifs_dbg(FYI, "F_EXLCK\n"); - *type |= server->vals->exclusive_lock_type; - *lock = 1; - } else if (flock->fl_type == F_SHLCK) { - cifs_dbg(FYI, "F_SHLCK\n"); - *type |= server->vals->shared_lock_type; - *lock = 1; - } else - cifs_dbg(FYI, "Unknown type of lock\n"); -} - -static int -cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, - bool wait_flag, bool posix_lck, unsigned int xid) -{ - int rc = 0; - __u64 length = cifs_flock_len(flock); - struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - __u16 netfid = cfile->fid.netfid; - - if (posix_lck) { - int posix_lock_type; - - rc = cifs_posix_lock_test(file, flock); - if (!rc) - return rc; - - if (type & server->vals->shared_lock_type) - posix_lock_type = CIFS_RDLCK; - else - posix_lock_type = CIFS_WRLCK; - rc = CIFSSMBPosixLock(xid, tcon, netfid, - hash_lockowner(flock->fl_owner), - flock->fl_start, length, flock, - posix_lock_type, wait_flag); - return rc; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock); - if (!rc) - return rc; - - /* BB we could chain these into one lock request BB */ - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type, - 1, 0, false); - if (rc == 0) { - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type, 0, 1, false); - flock->fl_type = F_UNLCK; - if (rc != 0) - cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", - rc); - return 0; - } - - if (type & server->vals->shared_lock_type) { - flock->fl_type = F_WRLCK; - return 0; - } - - type &= ~server->vals->exclusive_lock_type; - - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type | server->vals->shared_lock_type, - 1, 0, false); - if (rc == 0) { - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type | server->vals->shared_lock_type, 0, 1, false); - flock->fl_type = F_RDLCK; - if (rc != 0) - cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", - rc); - } else - flock->fl_type = F_WRLCK; - - return 0; -} - -void -cifs_move_llist(struct list_head *source, struct list_head *dest) -{ - struct list_head *li, *tmp; - list_for_each_safe(li, tmp, source) - list_move(li, dest); -} - -void -cifs_free_llist(struct list_head *llist) -{ - struct cifsLockInfo *li, *tmp; - list_for_each_entry_safe(li, tmp, llist, llist) { - cifs_del_lock_waiters(li); - list_del(&li->llist); - kfree(li); - } -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -int -cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, - unsigned int xid) -{ - int rc = 0, stored_rc; - static const int types[] = { - LOCKING_ANDX_LARGE_FILES, - LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES - }; - unsigned int i; - unsigned int max_num, num, max_buf; - LOCKING_ANDX_RANGE *buf, *cur; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifsLockInfo *li, *tmp; - __u64 length = cifs_flock_len(flock); - struct list_head tmp_llist; - - INIT_LIST_HEAD(&tmp_llist); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it before using. - */ - max_buf = tcon->ses->server->maxBuf; - if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) - return -EINVAL; - - BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > - PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), - PAGE_SIZE); - max_num = (max_buf - sizeof(struct smb_hdr)) / - sizeof(LOCKING_ANDX_RANGE); - buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - cifs_down_write(&cinode->lock_sem); - for (i = 0; i < 2; i++) { - cur = buf; - num = 0; - list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { - if (flock->fl_start > li->offset || - (flock->fl_start + length) < - (li->offset + li->length)) - continue; - if (current->tgid != li->pid) - continue; - if (types[i] != li->type) - continue; - if (cinode->can_cache_brlcks) { - /* - * We can cache brlock requests - simply remove - * a lock from the file's list. - */ - list_del(&li->llist); - cifs_del_lock_waiters(li); - kfree(li); - continue; - } - cur->Pid = cpu_to_le16(li->pid); - cur->LengthLow = cpu_to_le32((u32)li->length); - cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); - cur->OffsetLow = cpu_to_le32((u32)li->offset); - cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); - /* - * We need to save a lock here to let us add it again to - * the file's list if the unlock range request fails on - * the server. - */ - list_move(&li->llist, &tmp_llist); - if (++num == max_num) { - stored_rc = cifs_lockv(xid, tcon, - cfile->fid.netfid, - li->type, num, 0, buf); - if (stored_rc) { - /* - * We failed on the unlock range - * request - add all locks from the tmp - * list to the head of the file's list. - */ - cifs_move_llist(&tmp_llist, - &cfile->llist->locks); - rc = stored_rc; - } else - /* - * The unlock range request succeed - - * free the tmp list. - */ - cifs_free_llist(&tmp_llist); - cur = buf; - num = 0; - } else - cur++; - } - if (num) { - stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, - types[i], num, 0, buf); - if (stored_rc) { - cifs_move_llist(&tmp_llist, - &cfile->llist->locks); - rc = stored_rc; - } else - cifs_free_llist(&tmp_llist); - } - } - - up_write(&cinode->lock_sem); - kfree(buf); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, - bool wait_flag, bool posix_lck, int lock, int unlock, - unsigned int xid) -{ - int rc = 0; - __u64 length = cifs_flock_len(flock); - struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - struct inode *inode = d_inode(cfile->dentry); - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (posix_lck) { - int posix_lock_type; - - rc = cifs_posix_lock_set(file, flock); - if (rc <= FILE_LOCK_DEFERRED) - return rc; - - if (type & server->vals->shared_lock_type) - posix_lock_type = CIFS_RDLCK; - else - posix_lock_type = CIFS_WRLCK; - - if (unlock == 1) - posix_lock_type = CIFS_UNLCK; - - rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid, - hash_lockowner(flock->fl_owner), - flock->fl_start, length, - NULL, posix_lock_type, wait_flag); - goto out; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - if (lock) { - struct cifsLockInfo *lock; - - lock = cifs_lock_init(flock->fl_start, length, type, - flock->fl_flags); - if (!lock) - return -ENOMEM; - - rc = cifs_lock_add_if(cfile, lock, wait_flag); - if (rc < 0) { - kfree(lock); - return rc; - } - if (!rc) - goto out; - - /* - * Windows 7 server can delay breaking lease from read to None - * if we set a byte-range lock on a file - break it explicitly - * before sending the lock to the server to be sure the next - * read won't conflict with non-overlapted locks due to - * pagereading. - */ - if (!CIFS_CACHE_WRITE(CIFS_I(inode)) && - CIFS_CACHE_READ(CIFS_I(inode))) { - cifs_zap_mapping(inode); - cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n", - inode); - CIFS_I(inode)->oplock = 0; - } - - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type, 1, 0, wait_flag); - if (rc) { - kfree(lock); - return rc; - } - - cifs_lock_add(cfile, lock); - } else if (unlock) - rc = server->ops->mand_unlock_range(cfile, flock, xid); - -out: - if ((flock->fl_flags & FL_POSIX) || (flock->fl_flags & FL_FLOCK)) { - /* - * If this is a request to remove all locks because we - * are closing the file, it doesn't matter if the - * unlocking failed as both cifs.ko and the SMB server - * remove the lock on file close - */ - if (rc) { - cifs_dbg(VFS, "%s failed rc=%d\n", __func__, rc); - if (!(flock->fl_flags & FL_CLOSE)) - return rc; - } - rc = locks_lock_file_wait(file, flock); - } - return rc; -} - -int cifs_flock(struct file *file, int cmd, struct file_lock *fl) -{ - int rc, xid; - int lock = 0, unlock = 0; - bool wait_flag = false; - bool posix_lck = false; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - __u32 type; - - xid = get_xid(); - - if (!(fl->fl_flags & FL_FLOCK)) { - rc = -ENOLCK; - free_xid(xid); - return rc; - } - - cfile = (struct cifsFileInfo *)file->private_data; - tcon = tlink_tcon(cfile->tlink); - - cifs_read_flock(fl, &type, &lock, &unlock, &wait_flag, - tcon->ses->server); - cifs_sb = CIFS_FILE_SB(file); - - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - posix_lck = true; - - if (!lock && !unlock) { - /* - * if no lock or unlock then nothing to do since we do not - * know what it is - */ - rc = -EOPNOTSUPP; - free_xid(xid); - return rc; - } - - rc = cifs_setlk(file, fl, type, wait_flag, posix_lck, lock, unlock, - xid); - free_xid(xid); - return rc; - - -} - -int cifs_lock(struct file *file, int cmd, struct file_lock *flock) -{ - int rc, xid; - int lock = 0, unlock = 0; - bool wait_flag = false; - bool posix_lck = false; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - __u32 type; - - rc = -EACCES; - xid = get_xid(); - - cifs_dbg(FYI, "%s: %pD2 cmd=0x%x type=0x%x flags=0x%x r=%lld:%lld\n", __func__, file, cmd, - flock->fl_flags, flock->fl_type, (long long)flock->fl_start, - (long long)flock->fl_end); - - cfile = (struct cifsFileInfo *)file->private_data; - tcon = tlink_tcon(cfile->tlink); - - cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, - tcon->ses->server); - cifs_sb = CIFS_FILE_SB(file); - set_bit(CIFS_INO_CLOSE_ON_LOCK, &CIFS_I(d_inode(cfile->dentry))->flags); - - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - posix_lck = true; - /* - * BB add code here to normalize offset and length to account for - * negative length which we can not accept over the wire. - */ - if (IS_GETLK(cmd)) { - rc = cifs_getlk(file, flock, type, wait_flag, posix_lck, xid); - free_xid(xid); - return rc; - } - - if (!lock && !unlock) { - /* - * if no lock or unlock then nothing to do since we do not - * know what it is - */ - free_xid(xid); - return -EOPNOTSUPP; - } - - rc = cifs_setlk(file, flock, type, wait_flag, posix_lck, lock, unlock, - xid); - free_xid(xid); - return rc; -} - -/* - * update the file size (if needed) after a write. Should be called with - * the inode->i_lock held - */ -void -cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, - unsigned int bytes_written) -{ - loff_t end_of_write = offset + bytes_written; - - if (end_of_write > cifsi->server_eof) - cifsi->server_eof = end_of_write; -} - -static ssize_t -cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, - size_t write_size, loff_t *offset) -{ - int rc = 0; - unsigned int bytes_written = 0; - unsigned int total_written; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - unsigned int xid; - struct dentry *dentry = open_file->dentry; - struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry)); - struct cifs_io_parms io_parms = {0}; - - cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n", - write_size, *offset, dentry); - - tcon = tlink_tcon(open_file->tlink); - server = tcon->ses->server; - - if (!server->ops->sync_write) - return -ENOSYS; - - xid = get_xid(); - - for (total_written = 0; write_size > total_written; - total_written += bytes_written) { - rc = -EAGAIN; - while (rc == -EAGAIN) { - struct kvec iov[2]; - unsigned int len; - - if (open_file->invalidHandle) { - /* we could deadlock if we called - filemap_fdatawait from here so tell - reopen_file not to flush data to - server now */ - rc = cifs_reopen_file(open_file, false); - if (rc != 0) - break; - } - - len = min(server->ops->wp_retry_size(d_inode(dentry)), - (unsigned int)write_size - total_written); - /* iov[0] is reserved for smb header */ - iov[1].iov_base = (char *)write_data + total_written; - iov[1].iov_len = len; - io_parms.pid = pid; - io_parms.tcon = tcon; - io_parms.offset = *offset; - io_parms.length = len; - rc = server->ops->sync_write(xid, &open_file->fid, - &io_parms, &bytes_written, iov, 1); - } - if (rc || (bytes_written == 0)) { - if (total_written) - break; - else { - free_xid(xid); - return rc; - } - } else { - spin_lock(&d_inode(dentry)->i_lock); - cifs_update_eof(cifsi, *offset, bytes_written); - spin_unlock(&d_inode(dentry)->i_lock); - *offset += bytes_written; - } - } - - cifs_stats_bytes_written(tcon, total_written); - - if (total_written > 0) { - spin_lock(&d_inode(dentry)->i_lock); - if (*offset > d_inode(dentry)->i_size) { - i_size_write(d_inode(dentry), *offset); - d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9; - } - spin_unlock(&d_inode(dentry)->i_lock); - } - mark_inode_dirty_sync(d_inode(dentry)); - free_xid(xid); - return total_written; -} - -struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, - bool fsuid_only) -{ - struct cifsFileInfo *open_file = NULL; - struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); - - /* only filter by fsuid on multiuser mounts */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) - fsuid_only = false; - - spin_lock(&cifs_inode->open_file_lock); - /* we could simply get the first_list_entry since write-only entries - are always at the end of the list but since the first entry might - have a close pending, we go through the whole list */ - list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) - continue; - if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { - if ((!open_file->invalidHandle)) { - /* found a good file */ - /* lock it so it will not be closed on us */ - cifsFileInfo_get(open_file); - spin_unlock(&cifs_inode->open_file_lock); - return open_file; - } /* else might as well continue, and look for - another, or simply have the caller reopen it - again rather than trying to fix this handle */ - } else /* write only file */ - break; /* write only files are last so must be done */ - } - spin_unlock(&cifs_inode->open_file_lock); - return NULL; -} - -/* Return -EBADF if no handle is found and general rc otherwise */ -int -cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags, - struct cifsFileInfo **ret_file) -{ - struct cifsFileInfo *open_file, *inv_file = NULL; - struct cifs_sb_info *cifs_sb; - bool any_available = false; - int rc = -EBADF; - unsigned int refind = 0; - bool fsuid_only = flags & FIND_WR_FSUID_ONLY; - bool with_delete = flags & FIND_WR_WITH_DELETE; - *ret_file = NULL; - - /* - * Having a null inode here (because mapping->host was set to zero by - * the VFS or MM) should not happen but we had reports of on oops (due - * to it being zero) during stress testcases so we need to check for it - */ - - if (cifs_inode == NULL) { - cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n"); - dump_stack(); - return rc; - } - - cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); - - /* only filter by fsuid on multiuser mounts */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) - fsuid_only = false; - - spin_lock(&cifs_inode->open_file_lock); -refind_writable: - if (refind > MAX_REOPEN_ATT) { - spin_unlock(&cifs_inode->open_file_lock); - return rc; - } - list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (!any_available && open_file->pid != current->tgid) - continue; - if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) - continue; - if (with_delete && !(open_file->fid.access & DELETE)) - continue; - if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { - if (!open_file->invalidHandle) { - /* found a good writable file */ - cifsFileInfo_get(open_file); - spin_unlock(&cifs_inode->open_file_lock); - *ret_file = open_file; - return 0; - } else { - if (!inv_file) - inv_file = open_file; - } - } - } - /* couldn't find useable FH with same pid, try any available */ - if (!any_available) { - any_available = true; - goto refind_writable; - } - - if (inv_file) { - any_available = false; - cifsFileInfo_get(inv_file); - } - - spin_unlock(&cifs_inode->open_file_lock); - - if (inv_file) { - rc = cifs_reopen_file(inv_file, false); - if (!rc) { - *ret_file = inv_file; - return 0; - } - - spin_lock(&cifs_inode->open_file_lock); - list_move_tail(&inv_file->flist, &cifs_inode->openFileList); - spin_unlock(&cifs_inode->open_file_lock); - cifsFileInfo_put(inv_file); - ++refind; - inv_file = NULL; - spin_lock(&cifs_inode->open_file_lock); - goto refind_writable; - } - - return rc; -} - -struct cifsFileInfo * -find_writable_file(struct cifsInodeInfo *cifs_inode, int flags) -{ - struct cifsFileInfo *cfile; - int rc; - - rc = cifs_get_writable_file(cifs_inode, flags, &cfile); - if (rc) - cifs_dbg(FYI, "Couldn't find writable handle rc=%d\n", rc); - - return cfile; -} - -int -cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, - int flags, - struct cifsFileInfo **ret_file) -{ - struct cifsFileInfo *cfile; - void *page = alloc_dentry_path(); - - *ret_file = NULL; - - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - struct cifsInodeInfo *cinode; - const char *full_path = build_path_from_dentry(cfile->dentry, page); - if (IS_ERR(full_path)) { - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return PTR_ERR(full_path); - } - if (strcmp(full_path, name)) - continue; - - cinode = CIFS_I(d_inode(cfile->dentry)); - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return cifs_get_writable_file(cinode, flags, ret_file); - } - - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return -ENOENT; -} - -int -cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, - struct cifsFileInfo **ret_file) -{ - struct cifsFileInfo *cfile; - void *page = alloc_dentry_path(); - - *ret_file = NULL; - - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - struct cifsInodeInfo *cinode; - const char *full_path = build_path_from_dentry(cfile->dentry, page); - if (IS_ERR(full_path)) { - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return PTR_ERR(full_path); - } - if (strcmp(full_path, name)) - continue; - - cinode = CIFS_I(d_inode(cfile->dentry)); - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - *ret_file = find_readable_file(cinode, 0); - return *ret_file ? 0 : -ENOENT; - } - - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return -ENOENT; -} - -void -cifs_writedata_release(struct kref *refcount) -{ - struct cifs_writedata *wdata = container_of(refcount, - struct cifs_writedata, refcount); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (wdata->mr) { - smbd_deregister_mr(wdata->mr); - wdata->mr = NULL; - } -#endif - - if (wdata->cfile) - cifsFileInfo_put(wdata->cfile); - - kvfree(wdata->pages); - kfree(wdata); -} - -/* - * Write failed with a retryable error. Resend the write request. It's also - * possible that the page was redirtied so re-clean the page. - */ -static void -cifs_writev_requeue(struct cifs_writedata *wdata) -{ - int i, rc = 0; - struct inode *inode = d_inode(wdata->cfile->dentry); - struct TCP_Server_Info *server; - unsigned int rest_len; - - server = tlink_tcon(wdata->cfile->tlink)->ses->server; - i = 0; - rest_len = wdata->bytes; - do { - struct cifs_writedata *wdata2; - unsigned int j, nr_pages, wsize, tailsz, cur_len; - - wsize = server->ops->wp_retry_size(inode); - if (wsize < rest_len) { - nr_pages = wsize / PAGE_SIZE; - if (!nr_pages) { - rc = -EOPNOTSUPP; - break; - } - cur_len = nr_pages * PAGE_SIZE; - tailsz = PAGE_SIZE; - } else { - nr_pages = DIV_ROUND_UP(rest_len, PAGE_SIZE); - cur_len = rest_len; - tailsz = rest_len - (nr_pages - 1) * PAGE_SIZE; - } - - wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete); - if (!wdata2) { - rc = -ENOMEM; - break; - } - - for (j = 0; j < nr_pages; j++) { - wdata2->pages[j] = wdata->pages[i + j]; - lock_page(wdata2->pages[j]); - clear_page_dirty_for_io(wdata2->pages[j]); - } - - wdata2->sync_mode = wdata->sync_mode; - wdata2->nr_pages = nr_pages; - wdata2->offset = page_offset(wdata2->pages[0]); - wdata2->pagesz = PAGE_SIZE; - wdata2->tailsz = tailsz; - wdata2->bytes = cur_len; - - rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, - &wdata2->cfile); - if (!wdata2->cfile) { - cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n", - rc); - if (!is_retryable_error(rc)) - rc = -EBADF; - } else { - wdata2->pid = wdata2->cfile->pid; - rc = server->ops->async_writev(wdata2, - cifs_writedata_release); - } - - for (j = 0; j < nr_pages; j++) { - unlock_page(wdata2->pages[j]); - if (rc != 0 && !is_retryable_error(rc)) { - SetPageError(wdata2->pages[j]); - end_page_writeback(wdata2->pages[j]); - put_page(wdata2->pages[j]); - } - } - - kref_put(&wdata2->refcount, cifs_writedata_release); - if (rc) { - if (is_retryable_error(rc)) - continue; - i += nr_pages; - break; - } - - rest_len -= cur_len; - i += nr_pages; - } while (i < wdata->nr_pages); - - /* cleanup remaining pages from the original wdata */ - for (; i < wdata->nr_pages; i++) { - SetPageError(wdata->pages[i]); - end_page_writeback(wdata->pages[i]); - put_page(wdata->pages[i]); - } - - if (rc != 0 && !is_retryable_error(rc)) - mapping_set_error(inode->i_mapping, rc); - kref_put(&wdata->refcount, cifs_writedata_release); -} - -void -cifs_writev_complete(struct work_struct *work) -{ - struct cifs_writedata *wdata = container_of(work, - struct cifs_writedata, work); - struct inode *inode = d_inode(wdata->cfile->dentry); - int i = 0; - - if (wdata->result == 0) { - spin_lock(&inode->i_lock); - cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes); - spin_unlock(&inode->i_lock); - cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink), - wdata->bytes); - } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN) - return cifs_writev_requeue(wdata); - - for (i = 0; i < wdata->nr_pages; i++) { - struct page *page = wdata->pages[i]; - - if (wdata->result == -EAGAIN) - __set_page_dirty_nobuffers(page); - else if (wdata->result < 0) - SetPageError(page); - end_page_writeback(page); - cifs_readpage_to_fscache(inode, page); - put_page(page); - } - if (wdata->result != -EAGAIN) - mapping_set_error(inode->i_mapping, wdata->result); - kref_put(&wdata->refcount, cifs_writedata_release); -} - -struct cifs_writedata * -cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) -{ - struct cifs_writedata *writedata = NULL; - struct page **pages = - kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS); - if (pages) { - writedata = cifs_writedata_direct_alloc(pages, complete); - if (!writedata) - kvfree(pages); - } - - return writedata; -} - -struct cifs_writedata * -cifs_writedata_direct_alloc(struct page **pages, work_func_t complete) -{ - struct cifs_writedata *wdata; - - wdata = kzalloc(sizeof(*wdata), GFP_NOFS); - if (wdata != NULL) { - wdata->pages = pages; - kref_init(&wdata->refcount); - INIT_LIST_HEAD(&wdata->list); - init_completion(&wdata->done); - INIT_WORK(&wdata->work, complete); - } - return wdata; -} - - -static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) -{ - struct address_space *mapping = page->mapping; - loff_t offset = (loff_t)page->index << PAGE_SHIFT; - char *write_data; - int rc = -EFAULT; - int bytes_written = 0; - struct inode *inode; - struct cifsFileInfo *open_file; - - if (!mapping || !mapping->host) - return -EFAULT; - - inode = page->mapping->host; - - offset += (loff_t)from; - write_data = kmap(page); - write_data += from; - - if ((to > PAGE_SIZE) || (from > to)) { - kunmap(page); - return -EIO; - } - - /* racing with truncate? */ - if (offset > mapping->host->i_size) { - kunmap(page); - return 0; /* don't care */ - } - - /* check to make sure that we are not extending the file */ - if (mapping->host->i_size - offset < (loff_t)to) - to = (unsigned)(mapping->host->i_size - offset); - - rc = cifs_get_writable_file(CIFS_I(mapping->host), FIND_WR_ANY, - &open_file); - if (!rc) { - bytes_written = cifs_write(open_file, open_file->pid, - write_data, to - from, &offset); - cifsFileInfo_put(open_file); - /* Does mm or vfs already set times? */ - inode->i_atime = inode->i_mtime = current_time(inode); - if ((bytes_written > 0) && (offset)) - rc = 0; - else if (bytes_written < 0) - rc = bytes_written; - else - rc = -EFAULT; - } else { - cifs_dbg(FYI, "No writable handle for write page rc=%d\n", rc); - if (!is_retryable_error(rc)) - rc = -EIO; - } - - kunmap(page); - return rc; -} - -static struct cifs_writedata * -wdata_alloc_and_fillpages(pgoff_t tofind, struct address_space *mapping, - pgoff_t end, pgoff_t *index, - unsigned int *found_pages) -{ - struct cifs_writedata *wdata; - - wdata = cifs_writedata_alloc((unsigned int)tofind, - cifs_writev_complete); - if (!wdata) - return NULL; - - *found_pages = find_get_pages_range_tag(mapping, index, end, - PAGECACHE_TAG_DIRTY, tofind, wdata->pages); - return wdata; -} - -static unsigned int -wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages, - struct address_space *mapping, - struct writeback_control *wbc, - pgoff_t end, pgoff_t *index, pgoff_t *next, bool *done) -{ - unsigned int nr_pages = 0, i; - struct page *page; - - for (i = 0; i < found_pages; i++) { - page = wdata->pages[i]; - /* - * At this point we hold neither the i_pages lock nor the - * page lock: the page may be truncated or invalidated - * (changing page->mapping to NULL), or even swizzled - * back from swapper_space to tmpfs file mapping - */ - - if (nr_pages == 0) - lock_page(page); - else if (!trylock_page(page)) - break; - - if (unlikely(page->mapping != mapping)) { - unlock_page(page); - break; - } - - if (!wbc->range_cyclic && page->index > end) { - *done = true; - unlock_page(page); - break; - } - - if (*next && (page->index != *next)) { - /* Not next consecutive page */ - unlock_page(page); - break; - } - - if (wbc->sync_mode != WB_SYNC_NONE) - wait_on_page_writeback(page); - - if (PageWriteback(page) || - !clear_page_dirty_for_io(page)) { - unlock_page(page); - break; - } - - /* - * This actually clears the dirty bit in the radix tree. - * See cifs_writepage() for more commentary. - */ - set_page_writeback(page); - if (page_offset(page) >= i_size_read(mapping->host)) { - *done = true; - unlock_page(page); - end_page_writeback(page); - break; - } - - wdata->pages[i] = page; - *next = page->index + 1; - ++nr_pages; - } - - /* reset index to refind any pages skipped */ - if (nr_pages == 0) - *index = wdata->pages[0]->index + 1; - - /* put any pages we aren't going to use */ - for (i = nr_pages; i < found_pages; i++) { - put_page(wdata->pages[i]); - wdata->pages[i] = NULL; - } - - return nr_pages; -} - -static int -wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages, - struct address_space *mapping, struct writeback_control *wbc) -{ - int rc; - - wdata->sync_mode = wbc->sync_mode; - wdata->nr_pages = nr_pages; - wdata->offset = page_offset(wdata->pages[0]); - wdata->pagesz = PAGE_SIZE; - wdata->tailsz = min(i_size_read(mapping->host) - - page_offset(wdata->pages[nr_pages - 1]), - (loff_t)PAGE_SIZE); - wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz; - wdata->pid = wdata->cfile->pid; - - rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes); - if (rc) - return rc; - - if (wdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = wdata->server->ops->async_writev(wdata, - cifs_writedata_release); - - return rc; -} - -static int cifs_writepages(struct address_space *mapping, - struct writeback_control *wbc) -{ - struct inode *inode = mapping->host; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct TCP_Server_Info *server; - bool done = false, scanned = false, range_whole = false; - pgoff_t end, index; - struct cifs_writedata *wdata; - struct cifsFileInfo *cfile = NULL; - int rc = 0; - int saved_rc = 0; - unsigned int xid; - - /* - * If wsize is smaller than the page cache size, default to writing - * one page at a time via cifs_writepage - */ - if (cifs_sb->ctx->wsize < PAGE_SIZE) - return generic_writepages(mapping, wbc); - - xid = get_xid(); - if (wbc->range_cyclic) { - index = mapping->writeback_index; /* Start from prev offset */ - end = -1; - } else { - index = wbc->range_start >> PAGE_SHIFT; - end = wbc->range_end >> PAGE_SHIFT; - if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) - range_whole = true; - scanned = true; - } - server = cifs_pick_channel(cifs_sb_master_tcon(cifs_sb)->ses); - -retry: - while (!done && index <= end) { - unsigned int i, nr_pages, found_pages, wsize; - pgoff_t next = 0, tofind, saved_index = index; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - int get_file_rc = 0; - - if (cfile) - cifsFileInfo_put(cfile); - - rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile); - - /* in case of an error store it to return later */ - if (rc) - get_file_rc = rc; - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, - &wsize, credits); - if (rc != 0) { - done = true; - break; - } - - tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1; - - wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index, - &found_pages); - if (!wdata) { - rc = -ENOMEM; - done = true; - add_credits_and_wake_if(server, credits, 0); - break; - } - - if (found_pages == 0) { - kref_put(&wdata->refcount, cifs_writedata_release); - add_credits_and_wake_if(server, credits, 0); - break; - } - - nr_pages = wdata_prepare_pages(wdata, found_pages, mapping, wbc, - end, &index, &next, &done); - - /* nothing to write? */ - if (nr_pages == 0) { - kref_put(&wdata->refcount, cifs_writedata_release); - add_credits_and_wake_if(server, credits, 0); - continue; - } - - wdata->credits = credits_on_stack; - wdata->cfile = cfile; - wdata->server = server; - cfile = NULL; - - if (!wdata->cfile) { - cifs_dbg(VFS, "No writable handle in writepages rc=%d\n", - get_file_rc); - if (is_retryable_error(get_file_rc)) - rc = get_file_rc; - else - rc = -EBADF; - } else - rc = wdata_send_pages(wdata, nr_pages, mapping, wbc); - - for (i = 0; i < nr_pages; ++i) - unlock_page(wdata->pages[i]); - - /* send failure -- clean up the mess */ - if (rc != 0) { - add_credits_and_wake_if(server, &wdata->credits, 0); - for (i = 0; i < nr_pages; ++i) { - if (is_retryable_error(rc)) - redirty_page_for_writepage(wbc, - wdata->pages[i]); - else - SetPageError(wdata->pages[i]); - end_page_writeback(wdata->pages[i]); - put_page(wdata->pages[i]); - } - if (!is_retryable_error(rc)) - mapping_set_error(mapping, rc); - } - kref_put(&wdata->refcount, cifs_writedata_release); - - if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) { - index = saved_index; - continue; - } - - /* Return immediately if we received a signal during writing */ - if (is_interrupt_error(rc)) { - done = true; - break; - } - - if (rc != 0 && saved_rc == 0) - saved_rc = rc; - - wbc->nr_to_write -= nr_pages; - if (wbc->nr_to_write <= 0) - done = true; - - index = next; - } - - if (!scanned && !done) { - /* - * We hit the last page and there is more work to be done: wrap - * back to the start of the file - */ - scanned = true; - index = 0; - goto retry; - } - - if (saved_rc != 0) - rc = saved_rc; - - if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) - mapping->writeback_index = index; - - if (cfile) - cifsFileInfo_put(cfile); - free_xid(xid); - /* Indication to update ctime and mtime as close is deferred */ - set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); - return rc; -} - -static int -cifs_writepage_locked(struct page *page, struct writeback_control *wbc) -{ - int rc; - unsigned int xid; - - xid = get_xid(); -/* BB add check for wbc flags */ - get_page(page); - if (!PageUptodate(page)) - cifs_dbg(FYI, "ppw - page not up to date\n"); - - /* - * Set the "writeback" flag, and clear "dirty" in the radix tree. - * - * A writepage() implementation always needs to do either this, - * or re-dirty the page with "redirty_page_for_writepage()" in - * the case of a failure. - * - * Just unlocking the page will cause the radix tree tag-bits - * to fail to update with the state of the page correctly. - */ - set_page_writeback(page); -retry_write: - rc = cifs_partialpagewrite(page, 0, PAGE_SIZE); - if (is_retryable_error(rc)) { - if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) - goto retry_write; - redirty_page_for_writepage(wbc, page); - } else if (rc != 0) { - SetPageError(page); - mapping_set_error(page->mapping, rc); - } else { - SetPageUptodate(page); - } - end_page_writeback(page); - put_page(page); - free_xid(xid); - return rc; -} - -static int cifs_writepage(struct page *page, struct writeback_control *wbc) -{ - int rc = cifs_writepage_locked(page, wbc); - unlock_page(page); - return rc; -} - -static int cifs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - int rc; - struct inode *inode = mapping->host; - struct cifsFileInfo *cfile = file->private_data; - struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); - __u32 pid; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = cfile->pid; - else - pid = current->tgid; - - cifs_dbg(FYI, "write_end for page %p from pos %lld with %d bytes\n", - page, pos, copied); - - if (PageChecked(page)) { - if (copied == len) - SetPageUptodate(page); - ClearPageChecked(page); - } else if (!PageUptodate(page) && copied == PAGE_SIZE) - SetPageUptodate(page); - - if (!PageUptodate(page)) { - char *page_data; - unsigned offset = pos & (PAGE_SIZE - 1); - unsigned int xid; - - xid = get_xid(); - /* this is probably better than directly calling - partialpage_write since in this function the file handle is - known which we might as well leverage */ - /* BB check if anything else missing out of ppw - such as updating last write time */ - page_data = kmap(page); - rc = cifs_write(cfile, pid, page_data + offset, copied, &pos); - /* if (rc < 0) should we set writebehind rc? */ - kunmap(page); - - free_xid(xid); - } else { - rc = copied; - pos += copied; - set_page_dirty(page); - } - - if (rc > 0) { - spin_lock(&inode->i_lock); - if (pos > inode->i_size) { - i_size_write(inode, pos); - inode->i_blocks = (512 - 1 + pos) >> 9; - } - spin_unlock(&inode->i_lock); - } - - unlock_page(page); - put_page(page); - /* Indication to update ctime and mtime as close is deferred */ - set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); - - return rc; -} - -int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, - int datasync) -{ - unsigned int xid; - int rc = 0; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsFileInfo *smbfile = file->private_data; - struct inode *inode = file_inode(file); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - rc = file_write_and_wait_range(file, start, end); - if (rc) { - trace_cifs_fsync_err(inode->i_ino, rc); - return rc; - } - - xid = get_xid(); - - cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", - file, datasync); - - if (!CIFS_CACHE_READ(CIFS_I(inode))) { - rc = cifs_zap_mapping(inode); - if (rc) { - cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc); - rc = 0; /* don't care about it in fsync */ - } - } - - tcon = tlink_tcon(smbfile->tlink); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { - server = tcon->ses->server; - if (server->ops->flush == NULL) { - rc = -ENOSYS; - goto strict_fsync_exit; - } - - if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { - smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); - if (smbfile) { - rc = server->ops->flush(xid, tcon, &smbfile->fid); - cifsFileInfo_put(smbfile); - } else - cifs_dbg(FYI, "ignore fsync for file not open for write\n"); - } else - rc = server->ops->flush(xid, tcon, &smbfile->fid); - } - -strict_fsync_exit: - free_xid(xid); - return rc; -} - -int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - unsigned int xid; - int rc = 0; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsFileInfo *smbfile = file->private_data; - struct inode *inode = file_inode(file); - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - - rc = file_write_and_wait_range(file, start, end); - if (rc) { - trace_cifs_fsync_err(file_inode(file)->i_ino, rc); - return rc; - } - - xid = get_xid(); - - cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", - file, datasync); - - tcon = tlink_tcon(smbfile->tlink); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { - server = tcon->ses->server; - if (server->ops->flush == NULL) { - rc = -ENOSYS; - goto fsync_exit; - } - - if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { - smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); - if (smbfile) { - rc = server->ops->flush(xid, tcon, &smbfile->fid); - cifsFileInfo_put(smbfile); - } else - cifs_dbg(FYI, "ignore fsync for file not open for write\n"); - } else - rc = server->ops->flush(xid, tcon, &smbfile->fid); - } - -fsync_exit: - free_xid(xid); - return rc; -} - -/* - * As file closes, flush all cached write data for this inode checking - * for write behind errors. - */ -int cifs_flush(struct file *file, fl_owner_t id) -{ - struct inode *inode = file_inode(file); - int rc = 0; - - if (file->f_mode & FMODE_WRITE) - rc = filemap_write_and_wait(inode->i_mapping); - - cifs_dbg(FYI, "Flush inode %p file %p rc %d\n", inode, file, rc); - if (rc) { - /* get more nuanced writeback errors */ - rc = filemap_check_wb_err(file->f_mapping, 0); - trace_cifs_flush_err(inode->i_ino, rc); - } - return rc; -} - -static int -cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) -{ - int rc = 0; - unsigned long i; - - for (i = 0; i < num_pages; i++) { - pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!pages[i]) { - /* - * save number of pages we have already allocated and - * return with ENOMEM error - */ - num_pages = i; - rc = -ENOMEM; - break; - } - } - - if (rc) { - for (i = 0; i < num_pages; i++) - put_page(pages[i]); - } - return rc; -} - -static inline -size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) -{ - size_t num_pages; - size_t clen; - - clen = min_t(const size_t, len, wsize); - num_pages = DIV_ROUND_UP(clen, PAGE_SIZE); - - if (cur_len) - *cur_len = clen; - - return num_pages; -} - -static void -cifs_uncached_writedata_release(struct kref *refcount) -{ - int i; - struct cifs_writedata *wdata = container_of(refcount, - struct cifs_writedata, refcount); - - kref_put(&wdata->ctx->refcount, cifs_aio_ctx_release); - for (i = 0; i < wdata->nr_pages; i++) - put_page(wdata->pages[i]); - cifs_writedata_release(refcount); -} - -static void collect_uncached_write_data(struct cifs_aio_ctx *ctx); - -static void -cifs_uncached_writev_complete(struct work_struct *work) -{ - struct cifs_writedata *wdata = container_of(work, - struct cifs_writedata, work); - struct inode *inode = d_inode(wdata->cfile->dentry); - struct cifsInodeInfo *cifsi = CIFS_I(inode); - - spin_lock(&inode->i_lock); - cifs_update_eof(cifsi, wdata->offset, wdata->bytes); - if (cifsi->server_eof > inode->i_size) - i_size_write(inode, cifsi->server_eof); - spin_unlock(&inode->i_lock); - - complete(&wdata->done); - collect_uncached_write_data(wdata->ctx); - /* the below call can possibly free the last ref to aio ctx */ - kref_put(&wdata->refcount, cifs_uncached_writedata_release); -} - -static int -wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from, - size_t *len, unsigned long *num_pages) -{ - size_t save_len, copied, bytes, cur_len = *len; - unsigned long i, nr_pages = *num_pages; - - save_len = cur_len; - for (i = 0; i < nr_pages; i++) { - bytes = min_t(const size_t, cur_len, PAGE_SIZE); - copied = copy_page_from_iter(wdata->pages[i], 0, bytes, from); - cur_len -= copied; - /* - * If we didn't copy as much as we expected, then that - * may mean we trod into an unmapped area. Stop copying - * at that point. On the next pass through the big - * loop, we'll likely end up getting a zero-length - * write and bailing out of it. - */ - if (copied < bytes) - break; - } - cur_len = save_len - cur_len; - *len = cur_len; - - /* - * If we have no data to send, then that probably means that - * the copy above failed altogether. That's most likely because - * the address in the iovec was bogus. Return -EFAULT and let - * the caller free anything we allocated and bail out. - */ - if (!cur_len) - return -EFAULT; - - /* - * i + 1 now represents the number of pages we actually used in - * the copy phase above. - */ - *num_pages = i + 1; - return 0; -} - -static int -cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, - struct cifs_aio_ctx *ctx) -{ - unsigned int wsize; - struct cifs_credits credits; - int rc; - struct TCP_Server_Info *server = wdata->server; - - do { - if (wdata->cfile->invalidHandle) { - rc = cifs_reopen_file(wdata->cfile, false); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - - /* - * Wait for credits to resend this wdata. - * Note: we are attempting to resend the whole wdata not in - * segments - */ - do { - rc = server->ops->wait_mtu_credits(server, wdata->bytes, - &wsize, &credits); - if (rc) - goto fail; - - if (wsize < wdata->bytes) { - add_credits_and_wake_if(server, &credits, 0); - msleep(1000); - } - } while (wsize < wdata->bytes); - wdata->credits = credits; - - rc = adjust_credits(server, &wdata->credits, wdata->bytes); - - if (!rc) { - if (wdata->cfile->invalidHandle) - rc = -EAGAIN; - else { -#ifdef CONFIG_CIFS_SMB_DIRECT - if (wdata->mr) { - wdata->mr->need_invalidate = true; - smbd_deregister_mr(wdata->mr); - wdata->mr = NULL; - } -#endif - rc = server->ops->async_writev(wdata, - cifs_uncached_writedata_release); - } - } - - /* If the write was successfully sent, we are done */ - if (!rc) { - list_add_tail(&wdata->list, wdata_list); - return 0; - } - - /* Roll back credits and retry if needed */ - add_credits_and_wake_if(server, &wdata->credits, 0); - } while (rc == -EAGAIN); - -fail: - kref_put(&wdata->refcount, cifs_uncached_writedata_release); - return rc; -} - -static int -cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, - struct cifsFileInfo *open_file, - struct cifs_sb_info *cifs_sb, struct list_head *wdata_list, - struct cifs_aio_ctx *ctx) -{ - int rc = 0; - size_t cur_len; - unsigned long nr_pages, num_pages, i; - struct cifs_writedata *wdata; - struct iov_iter saved_from = *from; - loff_t saved_offset = offset; - pid_t pid; - struct TCP_Server_Info *server; - struct page **pagevec; - size_t start; - unsigned int xid; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); - xid = get_xid(); - - do { - unsigned int wsize; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, false); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, - &wsize, credits); - if (rc) - break; - - cur_len = min_t(const size_t, len, wsize); - - if (ctx->direct_io) { - ssize_t result; - - result = iov_iter_get_pages_alloc2( - from, &pagevec, cur_len, &start); - if (result < 0) { - cifs_dbg(VFS, - "direct_writev couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", - result, iov_iter_type(from), - from->iov_offset, from->count); - dump_stack(); - - rc = result; - add_credits_and_wake_if(server, credits, 0); - break; - } - cur_len = (size_t)result; - - nr_pages = - (cur_len + start + PAGE_SIZE - 1) / PAGE_SIZE; - - wdata = cifs_writedata_direct_alloc(pagevec, - cifs_uncached_writev_complete); - if (!wdata) { - rc = -ENOMEM; - for (i = 0; i < nr_pages; i++) - put_page(pagevec[i]); - kvfree(pagevec); - add_credits_and_wake_if(server, credits, 0); - break; - } - - - wdata->page_offset = start; - wdata->tailsz = - nr_pages > 1 ? - cur_len - (PAGE_SIZE - start) - - (nr_pages - 2) * PAGE_SIZE : - cur_len; - } else { - nr_pages = get_numpages(wsize, len, &cur_len); - wdata = cifs_writedata_alloc(nr_pages, - cifs_uncached_writev_complete); - if (!wdata) { - rc = -ENOMEM; - add_credits_and_wake_if(server, credits, 0); - break; - } - - rc = cifs_write_allocate_pages(wdata->pages, nr_pages); - if (rc) { - kvfree(wdata->pages); - kfree(wdata); - add_credits_and_wake_if(server, credits, 0); - break; - } - - num_pages = nr_pages; - rc = wdata_fill_from_iovec( - wdata, from, &cur_len, &num_pages); - if (rc) { - for (i = 0; i < nr_pages; i++) - put_page(wdata->pages[i]); - kvfree(wdata->pages); - kfree(wdata); - add_credits_and_wake_if(server, credits, 0); - break; - } - - /* - * Bring nr_pages down to the number of pages we - * actually used, and free any pages that we didn't use. - */ - for ( ; nr_pages > num_pages; nr_pages--) - put_page(wdata->pages[nr_pages - 1]); - - wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); - } - - wdata->sync_mode = WB_SYNC_ALL; - wdata->nr_pages = nr_pages; - wdata->offset = (__u64)offset; - wdata->cfile = cifsFileInfo_get(open_file); - wdata->server = server; - wdata->pid = pid; - wdata->bytes = cur_len; - wdata->pagesz = PAGE_SIZE; - wdata->credits = credits_on_stack; - wdata->ctx = ctx; - kref_get(&ctx->refcount); - - rc = adjust_credits(server, &wdata->credits, wdata->bytes); - - if (!rc) { - if (wdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = server->ops->async_writev(wdata, - cifs_uncached_writedata_release); - } - - if (rc) { - add_credits_and_wake_if(server, &wdata->credits, 0); - kref_put(&wdata->refcount, - cifs_uncached_writedata_release); - if (rc == -EAGAIN) { - *from = saved_from; - iov_iter_advance(from, offset - saved_offset); - continue; - } - break; - } - - list_add_tail(&wdata->list, wdata_list); - offset += cur_len; - len -= cur_len; - } while (len > 0); - - free_xid(xid); - return rc; -} - -static void collect_uncached_write_data(struct cifs_aio_ctx *ctx) -{ - struct cifs_writedata *wdata, *tmp; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb; - struct dentry *dentry = ctx->cfile->dentry; - ssize_t rc; - - tcon = tlink_tcon(ctx->cfile->tlink); - cifs_sb = CIFS_SB(dentry->d_sb); - - mutex_lock(&ctx->aio_mutex); - - if (list_empty(&ctx->list)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - rc = ctx->rc; - /* - * Wait for and collect replies for any successful sends in order of - * increasing offset. Once an error is hit, then return without waiting - * for any more replies. - */ -restart_loop: - list_for_each_entry_safe(wdata, tmp, &ctx->list, list) { - if (!rc) { - if (!try_wait_for_completion(&wdata->done)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - if (wdata->result) - rc = wdata->result; - else - ctx->total_len += wdata->bytes; - - /* resend call if it's a retryable error */ - if (rc == -EAGAIN) { - struct list_head tmp_list; - struct iov_iter tmp_from = ctx->iter; - - INIT_LIST_HEAD(&tmp_list); - list_del_init(&wdata->list); - - if (ctx->direct_io) - rc = cifs_resend_wdata( - wdata, &tmp_list, ctx); - else { - iov_iter_advance(&tmp_from, - wdata->offset - ctx->pos); - - rc = cifs_write_from_iter(wdata->offset, - wdata->bytes, &tmp_from, - ctx->cfile, cifs_sb, &tmp_list, - ctx); - - kref_put(&wdata->refcount, - cifs_uncached_writedata_release); - } - - list_splice(&tmp_list, &ctx->list); - goto restart_loop; - } - } - list_del_init(&wdata->list); - kref_put(&wdata->refcount, cifs_uncached_writedata_release); - } - - cifs_stats_bytes_written(tcon, ctx->total_len); - set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(dentry->d_inode)->flags); - - ctx->rc = (rc == 0) ? ctx->total_len : rc; - - mutex_unlock(&ctx->aio_mutex); - - if (ctx->iocb && ctx->iocb->ki_complete) - ctx->iocb->ki_complete(ctx->iocb, ctx->rc); - else - complete(&ctx->done); -} - -static ssize_t __cifs_writev( - struct kiocb *iocb, struct iov_iter *from, bool direct) -{ - struct file *file = iocb->ki_filp; - ssize_t total_written = 0; - struct cifsFileInfo *cfile; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb; - struct cifs_aio_ctx *ctx; - struct iov_iter saved_from = *from; - size_t len = iov_iter_count(from); - int rc; - - /* - * iov_iter_get_pages_alloc doesn't work with ITER_KVEC. - * In this case, fall back to non-direct write function. - * this could be improved by getting pages directly in ITER_KVEC - */ - if (direct && iov_iter_is_kvec(from)) { - cifs_dbg(FYI, "use non-direct cifs_writev for kvec I/O\n"); - direct = false; - } - - rc = generic_write_checks(iocb, from); - if (rc <= 0) - return rc; - - cifs_sb = CIFS_FILE_SB(file); - cfile = file->private_data; - tcon = tlink_tcon(cfile->tlink); - - if (!tcon->ses->server->ops->async_writev) - return -ENOSYS; - - ctx = cifs_aio_ctx_alloc(); - if (!ctx) - return -ENOMEM; - - ctx->cfile = cifsFileInfo_get(cfile); - - if (!is_sync_kiocb(iocb)) - ctx->iocb = iocb; - - ctx->pos = iocb->ki_pos; - - if (direct) { - ctx->direct_io = true; - ctx->iter = *from; - ctx->len = len; - } else { - rc = setup_aio_ctx_iter(ctx, from, ITER_SOURCE); - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - } - - /* grab a lock here due to read response handlers can access ctx */ - mutex_lock(&ctx->aio_mutex); - - rc = cifs_write_from_iter(iocb->ki_pos, ctx->len, &saved_from, - cfile, cifs_sb, &ctx->list, ctx); - - /* - * If at least one write was successfully sent, then discard any rc - * value from the later writes. If the other write succeeds, then - * we'll end up returning whatever was written. If it fails, then - * we'll get a new rc value from that. - */ - if (!list_empty(&ctx->list)) - rc = 0; - - mutex_unlock(&ctx->aio_mutex); - - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - - if (!is_sync_kiocb(iocb)) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return -EIOCBQUEUED; - } - - rc = wait_for_completion_killable(&ctx->done); - if (rc) { - mutex_lock(&ctx->aio_mutex); - ctx->rc = rc = -EINTR; - total_written = ctx->total_len; - mutex_unlock(&ctx->aio_mutex); - } else { - rc = ctx->rc; - total_written = ctx->total_len; - } - - kref_put(&ctx->refcount, cifs_aio_ctx_release); - - if (unlikely(!total_written)) - return rc; - - iocb->ki_pos += total_written; - return total_written; -} - -ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from) -{ - struct file *file = iocb->ki_filp; - - cifs_revalidate_mapping(file->f_inode); - return __cifs_writev(iocb, from, true); -} - -ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) -{ - return __cifs_writev(iocb, from, false); -} - -static ssize_t -cifs_writev(struct kiocb *iocb, struct iov_iter *from) -{ - struct file *file = iocb->ki_filp; - struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; - struct inode *inode = file->f_mapping->host; - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - ssize_t rc; - - inode_lock(inode); - /* - * We need to hold the sem to be sure nobody modifies lock list - * with a brlock that prevents writing. - */ - down_read(&cinode->lock_sem); - - rc = generic_write_checks(iocb, from); - if (rc <= 0) - goto out; - - if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from), - server->vals->exclusive_lock_type, 0, - NULL, CIFS_WRITE_OP)) - rc = __generic_file_write_iter(iocb, from); - else - rc = -EACCES; -out: - up_read(&cinode->lock_sem); - inode_unlock(inode); - - if (rc > 0) - rc = generic_write_sync(iocb, rc); - return rc; -} - -ssize_t -cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) -{ - struct inode *inode = file_inode(iocb->ki_filp); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsFileInfo *cfile = (struct cifsFileInfo *) - iocb->ki_filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - ssize_t written; - - written = cifs_get_writer(cinode); - if (written) - return written; - - if (CIFS_CACHE_WRITE(cinode)) { - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) - && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) { - written = generic_file_write_iter(iocb, from); - goto out; - } - written = cifs_writev(iocb, from); - goto out; - } - /* - * For non-oplocked files in strict cache mode we need to write the data - * to the server exactly from the pos to pos+len-1 rather than flush all - * affected pages because it may cause a error with mandatory locks on - * these pages but not on the region from pos to ppos+len-1. - */ - written = cifs_user_writev(iocb, from); - if (CIFS_CACHE_READ(cinode)) { - /* - * We have read level caching and we have just sent a write - * request to the server thus making data in the cache stale. - * Zap the cache and set oplock/lease level to NONE to avoid - * reading stale data from the cache. All subsequent read - * operations will read new data from the server. - */ - cifs_zap_mapping(inode); - cifs_dbg(FYI, "Set Oplock/Lease to NONE for inode=%p after write\n", - inode); - cinode->oplock = 0; - } -out: - cifs_put_writer(cinode); - return written; -} - -static struct cifs_readdata * -cifs_readdata_direct_alloc(struct page **pages, work_func_t complete) -{ - struct cifs_readdata *rdata; - - rdata = kzalloc(sizeof(*rdata), GFP_KERNEL); - if (rdata != NULL) { - rdata->pages = pages; - kref_init(&rdata->refcount); - INIT_LIST_HEAD(&rdata->list); - init_completion(&rdata->done); - INIT_WORK(&rdata->work, complete); - } - - return rdata; -} - -static struct cifs_readdata * -cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete) -{ - struct page **pages = - kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); - struct cifs_readdata *ret = NULL; - - if (pages) { - ret = cifs_readdata_direct_alloc(pages, complete); - if (!ret) - kfree(pages); - } - - return ret; -} - -void -cifs_readdata_release(struct kref *refcount) -{ - struct cifs_readdata *rdata = container_of(refcount, - struct cifs_readdata, refcount); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (rdata->mr) { - smbd_deregister_mr(rdata->mr); - rdata->mr = NULL; - } -#endif - if (rdata->cfile) - cifsFileInfo_put(rdata->cfile); - - kvfree(rdata->pages); - kfree(rdata); -} - -static int -cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages) -{ - int rc = 0; - struct page *page; - unsigned int i; - - for (i = 0; i < nr_pages; i++) { - page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!page) { - rc = -ENOMEM; - break; - } - rdata->pages[i] = page; - } - - if (rc) { - unsigned int nr_page_failed = i; - - for (i = 0; i < nr_page_failed; i++) { - put_page(rdata->pages[i]); - rdata->pages[i] = NULL; - } - } - return rc; -} - -static void -cifs_uncached_readdata_release(struct kref *refcount) -{ - struct cifs_readdata *rdata = container_of(refcount, - struct cifs_readdata, refcount); - unsigned int i; - - kref_put(&rdata->ctx->refcount, cifs_aio_ctx_release); - for (i = 0; i < rdata->nr_pages; i++) { - put_page(rdata->pages[i]); - } - cifs_readdata_release(refcount); -} - -/** - * cifs_readdata_to_iov - copy data from pages in response to an iovec - * @rdata: the readdata response with list of pages holding data - * @iter: destination for our data - * - * This function copies data from a list of pages in a readdata response into - * an array of iovecs. It will first calculate where the data should go - * based on the info in the readdata and then copy the data into that spot. - */ -static int -cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter) -{ - size_t remaining = rdata->got_bytes; - unsigned int i; - - for (i = 0; i < rdata->nr_pages; i++) { - struct page *page = rdata->pages[i]; - size_t copy = min_t(size_t, remaining, PAGE_SIZE); - size_t written; - - if (unlikely(iov_iter_is_pipe(iter))) { - void *addr = kmap_atomic(page); - - written = copy_to_iter(addr, copy, iter); - kunmap_atomic(addr); - } else - written = copy_page_to_iter(page, 0, copy, iter); - remaining -= written; - if (written < copy && iov_iter_count(iter) > 0) - break; - } - return remaining ? -EFAULT : 0; -} - -static void collect_uncached_read_data(struct cifs_aio_ctx *ctx); - -static void -cifs_uncached_readv_complete(struct work_struct *work) -{ - struct cifs_readdata *rdata = container_of(work, - struct cifs_readdata, work); - - complete(&rdata->done); - collect_uncached_read_data(rdata->ctx); - /* the below call can possibly free the last ref to aio ctx */ - kref_put(&rdata->refcount, cifs_uncached_readdata_release); -} - -static int -uncached_fill_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, struct iov_iter *iter, - unsigned int len) -{ - int result = 0; - unsigned int i; - unsigned int nr_pages = rdata->nr_pages; - unsigned int page_offset = rdata->page_offset; - - rdata->got_bytes = 0; - rdata->tailsz = PAGE_SIZE; - for (i = 0; i < nr_pages; i++) { - struct page *page = rdata->pages[i]; - size_t n; - unsigned int segment_size = rdata->pagesz; - - if (i == 0) - segment_size -= page_offset; - else - page_offset = 0; - - - if (len <= 0) { - /* no need to hold page hostage */ - rdata->pages[i] = NULL; - rdata->nr_pages--; - put_page(page); - continue; - } - - n = len; - if (len >= segment_size) - /* enough data to fill the page */ - n = segment_size; - else - rdata->tailsz = len; - len -= n; - - if (iter) - result = copy_page_from_iter( - page, page_offset, n, iter); -#ifdef CONFIG_CIFS_SMB_DIRECT - else if (rdata->mr) - result = n; -#endif - else - result = cifs_read_page_from_socket( - server, page, page_offset, n); - if (result < 0) - break; - - rdata->got_bytes += result; - } - - return result != -ECONNABORTED && rdata->got_bytes > 0 ? - rdata->got_bytes : result; -} - -static int -cifs_uncached_read_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, unsigned int len) -{ - return uncached_fill_pages(server, rdata, NULL, len); -} - -static int -cifs_uncached_copy_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - struct iov_iter *iter) -{ - return uncached_fill_pages(server, rdata, iter, iter->count); -} - -static int cifs_resend_rdata(struct cifs_readdata *rdata, - struct list_head *rdata_list, - struct cifs_aio_ctx *ctx) -{ - unsigned int rsize; - struct cifs_credits credits; - int rc; - struct TCP_Server_Info *server; - - /* XXX: should we pick a new channel here? */ - server = rdata->server; - - do { - if (rdata->cfile->invalidHandle) { - rc = cifs_reopen_file(rdata->cfile, true); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - /* - * Wait for credits to resend this rdata. - * Note: we are attempting to resend the whole rdata not in - * segments - */ - do { - rc = server->ops->wait_mtu_credits(server, rdata->bytes, - &rsize, &credits); - - if (rc) - goto fail; - - if (rsize < rdata->bytes) { - add_credits_and_wake_if(server, &credits, 0); - msleep(1000); - } - } while (rsize < rdata->bytes); - rdata->credits = credits; - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - if (!rc) { - if (rdata->cfile->invalidHandle) - rc = -EAGAIN; - else { -#ifdef CONFIG_CIFS_SMB_DIRECT - if (rdata->mr) { - rdata->mr->need_invalidate = true; - smbd_deregister_mr(rdata->mr); - rdata->mr = NULL; - } -#endif - rc = server->ops->async_readv(rdata); - } - } - - /* If the read was successfully sent, we are done */ - if (!rc) { - /* Add to aio pending list */ - list_add_tail(&rdata->list, rdata_list); - return 0; - } - - /* Roll back credits and retry if needed */ - add_credits_and_wake_if(server, &rdata->credits, 0); - } while (rc == -EAGAIN); - -fail: - kref_put(&rdata->refcount, cifs_uncached_readdata_release); - return rc; -} - -static int -cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, - struct cifs_sb_info *cifs_sb, struct list_head *rdata_list, - struct cifs_aio_ctx *ctx) -{ - struct cifs_readdata *rdata; - unsigned int npages, rsize; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - size_t cur_len; - int rc; - pid_t pid; - struct TCP_Server_Info *server; - struct page **pagevec; - size_t start; - struct iov_iter direct_iov = ctx->iter; - - server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - if (ctx->direct_io) - iov_iter_advance(&direct_iov, offset - ctx->pos); - - do { - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - if (cifs_sb->ctx->rsize == 0) - cifs_sb->ctx->rsize = - server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), - cifs_sb->ctx); - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, - &rsize, credits); - if (rc) - break; - - cur_len = min_t(const size_t, len, rsize); - - if (ctx->direct_io) { - ssize_t result; - - result = iov_iter_get_pages_alloc2( - &direct_iov, &pagevec, - cur_len, &start); - if (result < 0) { - cifs_dbg(VFS, - "Couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", - result, iov_iter_type(&direct_iov), - direct_iov.iov_offset, - direct_iov.count); - dump_stack(); - - rc = result; - add_credits_and_wake_if(server, credits, 0); - break; - } - cur_len = (size_t)result; - - rdata = cifs_readdata_direct_alloc( - pagevec, cifs_uncached_readv_complete); - if (!rdata) { - add_credits_and_wake_if(server, credits, 0); - rc = -ENOMEM; - break; - } - - npages = (cur_len + start + PAGE_SIZE-1) / PAGE_SIZE; - rdata->page_offset = start; - rdata->tailsz = npages > 1 ? - cur_len-(PAGE_SIZE-start)-(npages-2)*PAGE_SIZE : - cur_len; - - } else { - - npages = DIV_ROUND_UP(cur_len, PAGE_SIZE); - /* allocate a readdata struct */ - rdata = cifs_readdata_alloc(npages, - cifs_uncached_readv_complete); - if (!rdata) { - add_credits_and_wake_if(server, credits, 0); - rc = -ENOMEM; - break; - } - - rc = cifs_read_allocate_pages(rdata, npages); - if (rc) { - kvfree(rdata->pages); - kfree(rdata); - add_credits_and_wake_if(server, credits, 0); - break; - } - - rdata->tailsz = PAGE_SIZE; - } - - rdata->server = server; - rdata->cfile = cifsFileInfo_get(open_file); - rdata->nr_pages = npages; - rdata->offset = offset; - rdata->bytes = cur_len; - rdata->pid = pid; - rdata->pagesz = PAGE_SIZE; - rdata->read_into_pages = cifs_uncached_read_into_pages; - rdata->copy_into_pages = cifs_uncached_copy_into_pages; - rdata->credits = credits_on_stack; - rdata->ctx = ctx; - kref_get(&ctx->refcount); - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - - if (!rc) { - if (rdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = server->ops->async_readv(rdata); - } - - if (rc) { - add_credits_and_wake_if(server, &rdata->credits, 0); - kref_put(&rdata->refcount, - cifs_uncached_readdata_release); - if (rc == -EAGAIN) { - iov_iter_revert(&direct_iov, cur_len); - continue; - } - break; - } - - list_add_tail(&rdata->list, rdata_list); - offset += cur_len; - len -= cur_len; - } while (len > 0); - - return rc; -} - -static void -collect_uncached_read_data(struct cifs_aio_ctx *ctx) -{ - struct cifs_readdata *rdata, *tmp; - struct iov_iter *to = &ctx->iter; - struct cifs_sb_info *cifs_sb; - int rc; - - cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb); - - mutex_lock(&ctx->aio_mutex); - - if (list_empty(&ctx->list)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - rc = ctx->rc; - /* the loop below should proceed in the order of increasing offsets */ -again: - list_for_each_entry_safe(rdata, tmp, &ctx->list, list) { - if (!rc) { - if (!try_wait_for_completion(&rdata->done)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - if (rdata->result == -EAGAIN) { - /* resend call if it's a retryable error */ - struct list_head tmp_list; - unsigned int got_bytes = rdata->got_bytes; - - list_del_init(&rdata->list); - INIT_LIST_HEAD(&tmp_list); - - /* - * Got a part of data and then reconnect has - * happened -- fill the buffer and continue - * reading. - */ - if (got_bytes && got_bytes < rdata->bytes) { - rc = 0; - if (!ctx->direct_io) - rc = cifs_readdata_to_iov(rdata, to); - if (rc) { - kref_put(&rdata->refcount, - cifs_uncached_readdata_release); - continue; - } - } - - if (ctx->direct_io) { - /* - * Re-use rdata as this is a - * direct I/O - */ - rc = cifs_resend_rdata( - rdata, - &tmp_list, ctx); - } else { - rc = cifs_send_async_read( - rdata->offset + got_bytes, - rdata->bytes - got_bytes, - rdata->cfile, cifs_sb, - &tmp_list, ctx); - - kref_put(&rdata->refcount, - cifs_uncached_readdata_release); - } - - list_splice(&tmp_list, &ctx->list); - - goto again; - } else if (rdata->result) - rc = rdata->result; - else if (!ctx->direct_io) - rc = cifs_readdata_to_iov(rdata, to); - - /* if there was a short read -- discard anything left */ - if (rdata->got_bytes && rdata->got_bytes < rdata->bytes) - rc = -ENODATA; - - ctx->total_len += rdata->got_bytes; - } - list_del_init(&rdata->list); - kref_put(&rdata->refcount, cifs_uncached_readdata_release); - } - - if (!ctx->direct_io) - ctx->total_len = ctx->len - iov_iter_count(to); - - /* mask nodata case */ - if (rc == -ENODATA) - rc = 0; - - ctx->rc = (rc == 0) ? (ssize_t)ctx->total_len : rc; - - mutex_unlock(&ctx->aio_mutex); - - if (ctx->iocb && ctx->iocb->ki_complete) - ctx->iocb->ki_complete(ctx->iocb, ctx->rc); - else - complete(&ctx->done); -} - -static ssize_t __cifs_readv( - struct kiocb *iocb, struct iov_iter *to, bool direct) -{ - size_t len; - struct file *file = iocb->ki_filp; - struct cifs_sb_info *cifs_sb; - struct cifsFileInfo *cfile; - struct cifs_tcon *tcon; - ssize_t rc, total_read = 0; - loff_t offset = iocb->ki_pos; - struct cifs_aio_ctx *ctx; - - /* - * iov_iter_get_pages_alloc() doesn't work with ITER_KVEC, - * fall back to data copy read path - * this could be improved by getting pages directly in ITER_KVEC - */ - if (direct && iov_iter_is_kvec(to)) { - cifs_dbg(FYI, "use non-direct cifs_user_readv for kvec I/O\n"); - direct = false; - } - - len = iov_iter_count(to); - if (!len) - return 0; - - cifs_sb = CIFS_FILE_SB(file); - cfile = file->private_data; - tcon = tlink_tcon(cfile->tlink); - - if (!tcon->ses->server->ops->async_readv) - return -ENOSYS; - - if ((file->f_flags & O_ACCMODE) == O_WRONLY) - cifs_dbg(FYI, "attempting read on write only file instance\n"); - - ctx = cifs_aio_ctx_alloc(); - if (!ctx) - return -ENOMEM; - - ctx->cfile = cifsFileInfo_get(cfile); - - if (!is_sync_kiocb(iocb)) - ctx->iocb = iocb; - - if (user_backed_iter(to)) - ctx->should_dirty = true; - - if (direct) { - ctx->pos = offset; - ctx->direct_io = true; - ctx->iter = *to; - ctx->len = len; - } else { - rc = setup_aio_ctx_iter(ctx, to, ITER_DEST); - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - len = ctx->len; - } - - if (direct) { - rc = filemap_write_and_wait_range(file->f_inode->i_mapping, - offset, offset + len - 1); - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return -EAGAIN; - } - } - - /* grab a lock here due to read response handlers can access ctx */ - mutex_lock(&ctx->aio_mutex); - - rc = cifs_send_async_read(offset, len, cfile, cifs_sb, &ctx->list, ctx); - - /* if at least one read request send succeeded, then reset rc */ - if (!list_empty(&ctx->list)) - rc = 0; - - mutex_unlock(&ctx->aio_mutex); - - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - - if (!is_sync_kiocb(iocb)) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return -EIOCBQUEUED; - } - - rc = wait_for_completion_killable(&ctx->done); - if (rc) { - mutex_lock(&ctx->aio_mutex); - ctx->rc = rc = -EINTR; - total_read = ctx->total_len; - mutex_unlock(&ctx->aio_mutex); - } else { - rc = ctx->rc; - total_read = ctx->total_len; - } - - kref_put(&ctx->refcount, cifs_aio_ctx_release); - - if (total_read) { - iocb->ki_pos += total_read; - return total_read; - } - return rc; -} - -ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to) -{ - return __cifs_readv(iocb, to, true); -} - -ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to) -{ - return __cifs_readv(iocb, to, false); -} - -ssize_t -cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to) -{ - struct inode *inode = file_inode(iocb->ki_filp); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsFileInfo *cfile = (struct cifsFileInfo *) - iocb->ki_filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - int rc = -EACCES; - - /* - * In strict cache mode we need to read from the server all the time - * if we don't have level II oplock because the server can delay mtime - * change - so we can't make a decision about inode invalidating. - * And we can also fail with pagereading if there are mandatory locks - * on pages affected by this read but not on the region from pos to - * pos+len-1. - */ - if (!CIFS_CACHE_READ(cinode)) - return cifs_user_readv(iocb, to); - - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - return generic_file_read_iter(iocb, to); - - /* - * We need to hold the sem to be sure nobody modifies lock list - * with a brlock that prevents reading. - */ - down_read(&cinode->lock_sem); - if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to), - tcon->ses->server->vals->shared_lock_type, - 0, NULL, CIFS_READ_OP)) - rc = generic_file_read_iter(iocb, to); - up_read(&cinode->lock_sem); - return rc; -} - -static ssize_t -cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset) -{ - int rc = -EACCES; - unsigned int bytes_read = 0; - unsigned int total_read; - unsigned int current_read_size; - unsigned int rsize; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - unsigned int xid; - char *cur_offset; - struct cifsFileInfo *open_file; - struct cifs_io_parms io_parms = {0}; - int buf_type = CIFS_NO_BUFFER; - __u32 pid; - - xid = get_xid(); - cifs_sb = CIFS_FILE_SB(file); - - /* FIXME: set up handlers for larger reads and/or convert to async */ - rsize = min_t(unsigned int, cifs_sb->ctx->rsize, CIFSMaxBufSize); - - if (file->private_data == NULL) { - rc = -EBADF; - free_xid(xid); - return rc; - } - open_file = file->private_data; - tcon = tlink_tcon(open_file->tlink); - server = cifs_pick_channel(tcon->ses); - - if (!server->ops->sync_read) { - free_xid(xid); - return -ENOSYS; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - if ((file->f_flags & O_ACCMODE) == O_WRONLY) - cifs_dbg(FYI, "attempting read on write only file instance\n"); - - for (total_read = 0, cur_offset = read_data; read_size > total_read; - total_read += bytes_read, cur_offset += bytes_read) { - do { - current_read_size = min_t(uint, read_size - total_read, - rsize); - /* - * For windows me and 9x we do not want to request more - * than it negotiated since it will refuse the read - * then. - */ - if (!(tcon->ses->capabilities & - tcon->ses->server->vals->cap_large_files)) { - current_read_size = min_t(uint, - current_read_size, CIFSMaxBufSize); - } - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc != 0) - break; - } - io_parms.pid = pid; - io_parms.tcon = tcon; - io_parms.offset = *offset; - io_parms.length = current_read_size; - io_parms.server = server; - rc = server->ops->sync_read(xid, &open_file->fid, &io_parms, - &bytes_read, &cur_offset, - &buf_type); - } while (rc == -EAGAIN); - - if (rc || (bytes_read == 0)) { - if (total_read) { - break; - } else { - free_xid(xid); - return rc; - } - } else { - cifs_stats_bytes_read(tcon, total_read); - *offset += bytes_read; - } - } - free_xid(xid); - return total_read; -} - -/* - * If the page is mmap'ed into a process' page tables, then we need to make - * sure that it doesn't change while being written back. - */ -static vm_fault_t -cifs_page_mkwrite(struct vm_fault *vmf) -{ - struct page *page = vmf->page; - - /* Wait for the page to be written to the cache before we allow it to - * be modified. We then assume the entire page will need writing back. - */ -#ifdef CONFIG_CIFS_FSCACHE - if (PageFsCache(page) && - wait_on_page_fscache_killable(page) < 0) - return VM_FAULT_RETRY; -#endif - - wait_on_page_writeback(page); - - if (lock_page_killable(page) < 0) - return VM_FAULT_RETRY; - return VM_FAULT_LOCKED; -} - -static const struct vm_operations_struct cifs_file_vm_ops = { - .fault = filemap_fault, - .map_pages = filemap_map_pages, - .page_mkwrite = cifs_page_mkwrite, -}; - -int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) -{ - int xid, rc = 0; - struct inode *inode = file_inode(file); - - xid = get_xid(); - - if (!CIFS_CACHE_READ(CIFS_I(inode))) - rc = cifs_zap_mapping(inode); - if (!rc) - rc = generic_file_mmap(file, vma); - if (!rc) - vma->vm_ops = &cifs_file_vm_ops; - - free_xid(xid); - return rc; -} - -int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - int rc, xid; - - xid = get_xid(); - - rc = cifs_revalidate_file(file); - if (rc) - cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n", - rc); - if (!rc) - rc = generic_file_mmap(file, vma); - if (!rc) - vma->vm_ops = &cifs_file_vm_ops; - - free_xid(xid); - return rc; -} - -static void -cifs_readv_complete(struct work_struct *work) -{ - unsigned int i, got_bytes; - struct cifs_readdata *rdata = container_of(work, - struct cifs_readdata, work); - - got_bytes = rdata->got_bytes; - for (i = 0; i < rdata->nr_pages; i++) { - struct page *page = rdata->pages[i]; - - if (rdata->result == 0 || - (rdata->result == -EAGAIN && got_bytes)) { - flush_dcache_page(page); - SetPageUptodate(page); - } else - SetPageError(page); - - if (rdata->result == 0 || - (rdata->result == -EAGAIN && got_bytes)) - cifs_readpage_to_fscache(rdata->mapping->host, page); - - unlock_page(page); - - got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes); - - put_page(page); - rdata->pages[i] = NULL; - } - kref_put(&rdata->refcount, cifs_readdata_release); -} - -static int -readpages_fill_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, struct iov_iter *iter, - unsigned int len) -{ - int result = 0; - unsigned int i; - u64 eof; - pgoff_t eof_index; - unsigned int nr_pages = rdata->nr_pages; - unsigned int page_offset = rdata->page_offset; - - /* determine the eof that the server (probably) has */ - eof = CIFS_I(rdata->mapping->host)->server_eof; - eof_index = eof ? (eof - 1) >> PAGE_SHIFT : 0; - cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index); - - rdata->got_bytes = 0; - rdata->tailsz = PAGE_SIZE; - for (i = 0; i < nr_pages; i++) { - struct page *page = rdata->pages[i]; - unsigned int to_read = rdata->pagesz; - size_t n; - - if (i == 0) - to_read -= page_offset; - else - page_offset = 0; - - n = to_read; - - if (len >= to_read) { - len -= to_read; - } else if (len > 0) { - /* enough for partial page, fill and zero the rest */ - zero_user(page, len + page_offset, to_read - len); - n = rdata->tailsz = len; - len = 0; - } else if (page->index > eof_index) { - /* - * The VFS will not try to do readahead past the - * i_size, but it's possible that we have outstanding - * writes with gaps in the middle and the i_size hasn't - * caught up yet. Populate those with zeroed out pages - * to prevent the VFS from repeatedly attempting to - * fill them until the writes are flushed. - */ - zero_user(page, 0, PAGE_SIZE); - flush_dcache_page(page); - SetPageUptodate(page); - unlock_page(page); - put_page(page); - rdata->pages[i] = NULL; - rdata->nr_pages--; - continue; - } else { - /* no need to hold page hostage */ - unlock_page(page); - put_page(page); - rdata->pages[i] = NULL; - rdata->nr_pages--; - continue; - } - - if (iter) - result = copy_page_from_iter( - page, page_offset, n, iter); -#ifdef CONFIG_CIFS_SMB_DIRECT - else if (rdata->mr) - result = n; -#endif - else - result = cifs_read_page_from_socket( - server, page, page_offset, n); - if (result < 0) - break; - - rdata->got_bytes += result; - } - - return result != -ECONNABORTED && rdata->got_bytes > 0 ? - rdata->got_bytes : result; -} - -static int -cifs_readpages_read_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, unsigned int len) -{ - return readpages_fill_pages(server, rdata, NULL, len); -} - -static int -cifs_readpages_copy_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - struct iov_iter *iter) -{ - return readpages_fill_pages(server, rdata, iter, iter->count); -} - -static void cifs_readahead(struct readahead_control *ractl) -{ - int rc; - struct cifsFileInfo *open_file = ractl->file->private_data; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(ractl->file); - struct TCP_Server_Info *server; - pid_t pid; - unsigned int xid, nr_pages, last_batch_size = 0, cache_nr_pages = 0; - pgoff_t next_cached = ULONG_MAX; - bool caching = fscache_cookie_enabled(cifs_inode_cookie(ractl->mapping->host)) && - cifs_inode_cookie(ractl->mapping->host)->cache_priv; - bool check_cache = caching; - - xid = get_xid(); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - rc = 0; - server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); - - cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n", - __func__, ractl->file, ractl->mapping, readahead_count(ractl)); - - /* - * Chop the readahead request up into rsize-sized read requests. - */ - while ((nr_pages = readahead_count(ractl) - last_batch_size)) { - unsigned int i, got, rsize; - struct page *page; - struct cifs_readdata *rdata; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - pgoff_t index = readahead_index(ractl) + last_batch_size; - - /* - * Find out if we have anything cached in the range of - * interest, and if so, where the next chunk of cached data is. - */ - if (caching) { - if (check_cache) { - rc = cifs_fscache_query_occupancy( - ractl->mapping->host, index, nr_pages, - &next_cached, &cache_nr_pages); - if (rc < 0) - caching = false; - check_cache = false; - } - - if (index == next_cached) { - /* - * TODO: Send a whole batch of pages to be read - * by the cache. - */ - struct folio *folio = readahead_folio(ractl); - - last_batch_size = folio_nr_pages(folio); - if (cifs_readpage_from_fscache(ractl->mapping->host, - &folio->page) < 0) { - /* - * TODO: Deal with cache read failure - * here, but for the moment, delegate - * that to readpage. - */ - caching = false; - } - folio_unlock(folio); - next_cached++; - cache_nr_pages--; - if (cache_nr_pages == 0) - check_cache = true; - continue; - } - } - - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc) { - if (rc == -EAGAIN) - continue; - break; - } - } - - if (cifs_sb->ctx->rsize == 0) - cifs_sb->ctx->rsize = - server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), - cifs_sb->ctx); - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, - &rsize, credits); - if (rc) - break; - nr_pages = min_t(size_t, rsize / PAGE_SIZE, readahead_count(ractl)); - nr_pages = min_t(size_t, nr_pages, next_cached - index); - - /* - * Give up immediately if rsize is too small to read an entire - * page. The VFS will fall back to readpage. We should never - * reach this point however since we set ra_pages to 0 when the - * rsize is smaller than a cache page. - */ - if (unlikely(!nr_pages)) { - add_credits_and_wake_if(server, credits, 0); - break; - } - - rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete); - if (!rdata) { - /* best to give up if we're out of mem */ - add_credits_and_wake_if(server, credits, 0); - break; - } - - got = __readahead_batch(ractl, rdata->pages, nr_pages); - if (got != nr_pages) { - pr_warn("__readahead_batch() returned %u/%u\n", - got, nr_pages); - nr_pages = got; - } - - rdata->nr_pages = nr_pages; - rdata->bytes = readahead_batch_length(ractl); - rdata->cfile = cifsFileInfo_get(open_file); - rdata->server = server; - rdata->mapping = ractl->mapping; - rdata->offset = readahead_pos(ractl); - rdata->pid = pid; - rdata->pagesz = PAGE_SIZE; - rdata->tailsz = PAGE_SIZE; - rdata->read_into_pages = cifs_readpages_read_into_pages; - rdata->copy_into_pages = cifs_readpages_copy_into_pages; - rdata->credits = credits_on_stack; - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - if (!rc) { - if (rdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = server->ops->async_readv(rdata); - } - - if (rc) { - add_credits_and_wake_if(server, &rdata->credits, 0); - for (i = 0; i < rdata->nr_pages; i++) { - page = rdata->pages[i]; - unlock_page(page); - put_page(page); - } - /* Fallback to the readpage in error/reconnect cases */ - kref_put(&rdata->refcount, cifs_readdata_release); - break; - } - - kref_put(&rdata->refcount, cifs_readdata_release); - last_batch_size = nr_pages; - } - - free_xid(xid); -} - -/* - * cifs_readpage_worker must be called with the page pinned - */ -static int cifs_readpage_worker(struct file *file, struct page *page, - loff_t *poffset) -{ - char *read_data; - int rc; - - /* Is the page cached? */ - rc = cifs_readpage_from_fscache(file_inode(file), page); - if (rc == 0) - goto read_complete; - - read_data = kmap(page); - /* for reads over a certain size could initiate async read ahead */ - - rc = cifs_read(file, read_data, PAGE_SIZE, poffset); - - if (rc < 0) - goto io_error; - else - cifs_dbg(FYI, "Bytes read %d\n", rc); - - /* we do not want atime to be less than mtime, it broke some apps */ - file_inode(file)->i_atime = current_time(file_inode(file)); - if (timespec64_compare(&(file_inode(file)->i_atime), &(file_inode(file)->i_mtime))) - file_inode(file)->i_atime = file_inode(file)->i_mtime; - else - file_inode(file)->i_atime = current_time(file_inode(file)); - - if (PAGE_SIZE > rc) - memset(read_data + rc, 0, PAGE_SIZE - rc); - - flush_dcache_page(page); - SetPageUptodate(page); - - /* send this page to the cache */ - cifs_readpage_to_fscache(file_inode(file), page); - - rc = 0; - -io_error: - kunmap(page); - unlock_page(page); - -read_complete: - return rc; -} - -static int cifs_read_folio(struct file *file, struct folio *folio) -{ - struct page *page = &folio->page; - loff_t offset = page_file_offset(page); - int rc = -EACCES; - unsigned int xid; - - xid = get_xid(); - - if (file->private_data == NULL) { - rc = -EBADF; - free_xid(xid); - return rc; - } - - cifs_dbg(FYI, "read_folio %p at offset %d 0x%x\n", - page, (int)offset, (int)offset); - - rc = cifs_readpage_worker(file, page, &offset); - - free_xid(xid); - return rc; -} - -static int is_inode_writable(struct cifsInodeInfo *cifs_inode) -{ - struct cifsFileInfo *open_file; - - spin_lock(&cifs_inode->open_file_lock); - list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { - spin_unlock(&cifs_inode->open_file_lock); - return 1; - } - } - spin_unlock(&cifs_inode->open_file_lock); - return 0; -} - -/* We do not want to update the file size from server for inodes - open for write - to avoid races with writepage extending - the file - in the future we could consider allowing - refreshing the inode only on increases in the file size - but this is tricky to do without racing with writebehind - page caching in the current Linux kernel design */ -bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) -{ - if (!cifsInode) - return true; - - if (is_inode_writable(cifsInode)) { - /* This inode is open for write at least once */ - struct cifs_sb_info *cifs_sb; - - cifs_sb = CIFS_SB(cifsInode->netfs.inode.i_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { - /* since no page cache to corrupt on directio - we can change size safely */ - return true; - } - - if (i_size_read(&cifsInode->netfs.inode) < end_of_file) - return true; - - return false; - } else - return true; -} - -static int cifs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, - struct page **pagep, void **fsdata) -{ - int oncethru = 0; - pgoff_t index = pos >> PAGE_SHIFT; - loff_t offset = pos & (PAGE_SIZE - 1); - loff_t page_start = pos & PAGE_MASK; - loff_t i_size; - struct page *page; - int rc = 0; - - cifs_dbg(FYI, "write_begin from %lld len %d\n", (long long)pos, len); - -start: - page = grab_cache_page_write_begin(mapping, index); - if (!page) { - rc = -ENOMEM; - goto out; - } - - if (PageUptodate(page)) - goto out; - - /* - * If we write a full page it will be up to date, no need to read from - * the server. If the write is short, we'll end up doing a sync write - * instead. - */ - if (len == PAGE_SIZE) - goto out; - - /* - * optimize away the read when we have an oplock, and we're not - * expecting to use any of the data we'd be reading in. That - * is, when the page lies beyond the EOF, or straddles the EOF - * and the write will cover all of the existing data. - */ - if (CIFS_CACHE_READ(CIFS_I(mapping->host))) { - i_size = i_size_read(mapping->host); - if (page_start >= i_size || - (offset == 0 && (pos + len) >= i_size)) { - zero_user_segments(page, 0, offset, - offset + len, - PAGE_SIZE); - /* - * PageChecked means that the parts of the page - * to which we're not writing are considered up - * to date. Once the data is copied to the - * page, it can be set uptodate. - */ - SetPageChecked(page); - goto out; - } - } - - if ((file->f_flags & O_ACCMODE) != O_WRONLY && !oncethru) { - /* - * might as well read a page, it is fast enough. If we get - * an error, we don't need to return it. cifs_write_end will - * do a sync write instead since PG_uptodate isn't set. - */ - cifs_readpage_worker(file, page, &page_start); - put_page(page); - oncethru = 1; - goto start; - } else { - /* we could try using another file handle if there is one - - but how would we lock it to prevent close of that handle - racing with this read? In any case - this will be written out by write_end so is fine */ - } -out: - *pagep = page; - return rc; -} - -static bool cifs_release_folio(struct folio *folio, gfp_t gfp) -{ - if (folio_test_private(folio)) - return 0; - if (folio_test_fscache(folio)) { - if (current_is_kswapd() || !(gfp & __GFP_FS)) - return false; - folio_wait_fscache(folio); - } - fscache_note_page_release(cifs_inode_cookie(folio->mapping->host)); - return true; -} - -static void cifs_invalidate_folio(struct folio *folio, size_t offset, - size_t length) -{ - folio_wait_fscache(folio); -} - -static int cifs_launder_folio(struct folio *folio) -{ - int rc = 0; - loff_t range_start = folio_pos(folio); - loff_t range_end = range_start + folio_size(folio); - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 0, - .range_start = range_start, - .range_end = range_end, - }; - - cifs_dbg(FYI, "Launder page: %lu\n", folio->index); - - if (folio_clear_dirty_for_io(folio)) - rc = cifs_writepage_locked(&folio->page, &wbc); - - folio_wait_fscache(folio); - return rc; -} - -void cifs_oplock_break(struct work_struct *work) -{ - struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, - oplock_break); - struct inode *inode = d_inode(cfile->dentry); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - int rc = 0; - bool purge_cache = false, oplock_break_cancelled; - __u64 persistent_fid, volatile_fid; - __u16 net_fid; - - wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, - TASK_UNINTERRUPTIBLE); - - server->ops->downgrade_oplock(server, cinode, cfile->oplock_level, - cfile->oplock_epoch, &purge_cache); - - if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) && - cifs_has_mand_locks(cinode)) { - cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", - inode); - cinode->oplock = 0; - } - - if (inode && S_ISREG(inode->i_mode)) { - if (CIFS_CACHE_READ(cinode)) - break_lease(inode, O_RDONLY); - else - break_lease(inode, O_WRONLY); - rc = filemap_fdatawrite(inode->i_mapping); - if (!CIFS_CACHE_READ(cinode) || purge_cache) { - rc = filemap_fdatawait(inode->i_mapping); - mapping_set_error(inode->i_mapping, rc); - cifs_zap_mapping(inode); - } - cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc); - if (CIFS_CACHE_WRITE(cinode)) - goto oplock_break_ack; - } - - rc = cifs_push_locks(cfile); - if (rc) - cifs_dbg(VFS, "Push locks rc = %d\n", rc); - -oplock_break_ack: - /* - * When oplock break is received and there are no active - * file handles but cached, then schedule deferred close immediately. - * So, new open will not use cached handle. - */ - - if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes)) - cifs_close_deferred_file(cinode); - - persistent_fid = cfile->fid.persistent_fid; - volatile_fid = cfile->fid.volatile_fid; - net_fid = cfile->fid.netfid; - oplock_break_cancelled = cfile->oplock_break_cancelled; - - _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); - /* - * releasing stale oplock after recent reconnect of smb session using - * a now incorrect file handle is not a data integrity issue but do - * not bother sending an oplock release if session to server still is - * disconnected since oplock already released by the server - */ - if (!oplock_break_cancelled) { - /* check for server null since can race with kill_sb calling tree disconnect */ - if (tcon->ses && tcon->ses->server) { - rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, - volatile_fid, net_fid, cinode); - cifs_dbg(FYI, "Oplock release rc = %d\n", rc); - } else - pr_warn_once("lease break not sent for unmounted share\n"); - } - - cifs_done_oplock_break(cinode); -} - -/* - * The presence of cifs_direct_io() in the address space ops vector - * allowes open() O_DIRECT flags which would have failed otherwise. - * - * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests - * so this method should never be called. - * - * Direct IO is not yet supported in the cached mode. - */ -static ssize_t -cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter) -{ - /* - * FIXME - * Eventually need to support direct IO for non forcedirectio mounts - */ - return -EINVAL; -} - -static int cifs_swap_activate(struct swap_info_struct *sis, - struct file *swap_file, sector_t *span) -{ - struct cifsFileInfo *cfile = swap_file->private_data; - struct inode *inode = swap_file->f_mapping->host; - unsigned long blocks; - long long isize; - - cifs_dbg(FYI, "swap activate\n"); - - if (!swap_file->f_mapping->a_ops->swap_rw) - /* Cannot support swap */ - return -EINVAL; - - spin_lock(&inode->i_lock); - blocks = inode->i_blocks; - isize = inode->i_size; - spin_unlock(&inode->i_lock); - if (blocks*512 < isize) { - pr_warn("swap activate: swapfile has holes\n"); - return -EINVAL; - } - *span = sis->pages; - - pr_warn_once("Swap support over SMB3 is experimental\n"); - - /* - * TODO: consider adding ACL (or documenting how) to prevent other - * users (on this or other systems) from reading it - */ - - - /* TODO: add sk_set_memalloc(inet) or similar */ - - if (cfile) - cfile->swapfile = true; - /* - * TODO: Since file already open, we can't open with DENY_ALL here - * but we could add call to grab a byte range lock to prevent others - * from reading or writing the file - */ - - sis->flags |= SWP_FS_OPS; - return add_swap_extent(sis, 0, sis->max, 0); -} - -static void cifs_swap_deactivate(struct file *file) -{ - struct cifsFileInfo *cfile = file->private_data; - - cifs_dbg(FYI, "swap deactivate\n"); - - /* TODO: undo sk_set_memalloc(inet) will eventually be needed */ - - if (cfile) - cfile->swapfile = false; - - /* do we need to unpin (or unlock) the file */ -} - -/* - * Mark a page as having been made dirty and thus needing writeback. We also - * need to pin the cache object to write back to. - */ -#ifdef CONFIG_CIFS_FSCACHE -static bool cifs_dirty_folio(struct address_space *mapping, struct folio *folio) -{ - return fscache_dirty_folio(mapping, folio, - cifs_inode_cookie(mapping->host)); -} -#else -#define cifs_dirty_folio filemap_dirty_folio -#endif - -const struct address_space_operations cifs_addr_ops = { - .read_folio = cifs_read_folio, - .readahead = cifs_readahead, - .writepage = cifs_writepage, - .writepages = cifs_writepages, - .write_begin = cifs_write_begin, - .write_end = cifs_write_end, - .dirty_folio = cifs_dirty_folio, - .release_folio = cifs_release_folio, - .direct_IO = cifs_direct_io, - .invalidate_folio = cifs_invalidate_folio, - .launder_folio = cifs_launder_folio, - /* - * TODO: investigate and if useful we could add an cifs_migratePage - * helper (under an CONFIG_MIGRATION) in the future, and also - * investigate and add an is_dirty_writeback helper if needed - */ - .swap_activate = cifs_swap_activate, - .swap_deactivate = cifs_swap_deactivate, -}; - -/* - * cifs_readahead requires the server to support a buffer large enough to - * contain the header plus one complete page of data. Otherwise, we need - * to leave cifs_readahead out of the address space operations. - */ -const struct address_space_operations cifs_addr_ops_smallbuf = { - .read_folio = cifs_read_folio, - .writepage = cifs_writepage, - .writepages = cifs_writepages, - .write_begin = cifs_write_begin, - .write_end = cifs_write_end, - .dirty_folio = cifs_dirty_folio, - .release_folio = cifs_release_folio, - .invalidate_folio = cifs_invalidate_folio, - .launder_folio = cifs_launder_folio, -}; diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c deleted file mode 100644 index e2e2ef0fa9a0..000000000000 --- a/fs/cifs/fs_context.c +++ /dev/null @@ -1,1773 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2020, Microsoft Corporation. - * - * Author(s): Steve French - * David Howells - */ - -/* -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "ntlmssp.h" -#include "nterr.h" -#include "rfc1002pdu.h" -#include "fs_context.h" - -static DEFINE_MUTEX(cifs_mount_mutex); - -static const match_table_t cifs_smb_version_tokens = { - { Smb_1, SMB1_VERSION_STRING }, - { Smb_20, SMB20_VERSION_STRING}, - { Smb_21, SMB21_VERSION_STRING }, - { Smb_30, SMB30_VERSION_STRING }, - { Smb_302, SMB302_VERSION_STRING }, - { Smb_302, ALT_SMB302_VERSION_STRING }, - { Smb_311, SMB311_VERSION_STRING }, - { Smb_311, ALT_SMB311_VERSION_STRING }, - { Smb_3any, SMB3ANY_VERSION_STRING }, - { Smb_default, SMBDEFAULT_VERSION_STRING }, - { Smb_version_err, NULL } -}; - -static const match_table_t cifs_secflavor_tokens = { - { Opt_sec_krb5, "krb5" }, - { Opt_sec_krb5i, "krb5i" }, - { Opt_sec_krb5p, "krb5p" }, - { Opt_sec_ntlmsspi, "ntlmsspi" }, - { Opt_sec_ntlmssp, "ntlmssp" }, - { Opt_sec_ntlmv2, "nontlm" }, - { Opt_sec_ntlmv2, "ntlmv2" }, - { Opt_sec_ntlmv2i, "ntlmv2i" }, - { Opt_sec_none, "none" }, - - { Opt_sec_err, NULL } -}; - -const struct fs_parameter_spec smb3_fs_parameters[] = { - /* Mount options that take no arguments */ - fsparam_flag_no("user_xattr", Opt_user_xattr), - fsparam_flag_no("forceuid", Opt_forceuid), - fsparam_flag_no("multichannel", Opt_multichannel), - fsparam_flag_no("forcegid", Opt_forcegid), - fsparam_flag("noblocksend", Opt_noblocksend), - fsparam_flag("noautotune", Opt_noautotune), - fsparam_flag("nolease", Opt_nolease), - fsparam_flag_no("hard", Opt_hard), - fsparam_flag_no("soft", Opt_soft), - fsparam_flag_no("perm", Opt_perm), - fsparam_flag("nodelete", Opt_nodelete), - fsparam_flag_no("mapposix", Opt_mapposix), - fsparam_flag("mapchars", Opt_mapchars), - fsparam_flag("nomapchars", Opt_nomapchars), - fsparam_flag_no("sfu", Opt_sfu), - fsparam_flag("nodfs", Opt_nodfs), - fsparam_flag_no("posixpaths", Opt_posixpaths), - fsparam_flag_no("unix", Opt_unix), - fsparam_flag_no("linux", Opt_unix), - fsparam_flag_no("posix", Opt_unix), - fsparam_flag("nocase", Opt_nocase), - fsparam_flag("ignorecase", Opt_nocase), - fsparam_flag_no("brl", Opt_brl), - fsparam_flag_no("handlecache", Opt_handlecache), - fsparam_flag("forcemandatorylock", Opt_forcemandatorylock), - fsparam_flag("forcemand", Opt_forcemandatorylock), - fsparam_flag("setuidfromacl", Opt_setuidfromacl), - fsparam_flag("idsfromsid", Opt_setuidfromacl), - fsparam_flag_no("setuids", Opt_setuids), - fsparam_flag_no("dynperm", Opt_dynperm), - fsparam_flag_no("intr", Opt_intr), - fsparam_flag_no("strictsync", Opt_strictsync), - fsparam_flag_no("serverino", Opt_serverino), - fsparam_flag("rwpidforward", Opt_rwpidforward), - fsparam_flag("cifsacl", Opt_cifsacl), - fsparam_flag_no("acl", Opt_acl), - fsparam_flag("locallease", Opt_locallease), - fsparam_flag("sign", Opt_sign), - fsparam_flag("ignore_signature", Opt_ignore_signature), - fsparam_flag("signloosely", Opt_ignore_signature), - fsparam_flag("seal", Opt_seal), - fsparam_flag("noac", Opt_noac), - fsparam_flag("fsc", Opt_fsc), - fsparam_flag("mfsymlinks", Opt_mfsymlinks), - fsparam_flag("multiuser", Opt_multiuser), - fsparam_flag("sloppy", Opt_sloppy), - fsparam_flag("nosharesock", Opt_nosharesock), - fsparam_flag_no("persistenthandles", Opt_persistent), - fsparam_flag_no("resilienthandles", Opt_resilient), - fsparam_flag_no("tcpnodelay", Opt_tcp_nodelay), - fsparam_flag("nosparse", Opt_nosparse), - fsparam_flag("domainauto", Opt_domainauto), - fsparam_flag("rdma", Opt_rdma), - fsparam_flag("modesid", Opt_modesid), - fsparam_flag("modefromsid", Opt_modesid), - fsparam_flag("rootfs", Opt_rootfs), - fsparam_flag("compress", Opt_compress), - fsparam_flag("witness", Opt_witness), - - /* Mount options which take numeric value */ - fsparam_u32("backupuid", Opt_backupuid), - fsparam_u32("backupgid", Opt_backupgid), - fsparam_u32("uid", Opt_uid), - fsparam_u32("cruid", Opt_cruid), - fsparam_u32("gid", Opt_gid), - fsparam_u32("file_mode", Opt_file_mode), - fsparam_u32("dirmode", Opt_dirmode), - fsparam_u32("dir_mode", Opt_dirmode), - fsparam_u32("port", Opt_port), - fsparam_u32("min_enc_offload", Opt_min_enc_offload), - fsparam_u32("esize", Opt_min_enc_offload), - fsparam_u32("bsize", Opt_blocksize), - fsparam_u32("rasize", Opt_rasize), - fsparam_u32("rsize", Opt_rsize), - fsparam_u32("wsize", Opt_wsize), - fsparam_u32("actimeo", Opt_actimeo), - fsparam_u32("acdirmax", Opt_acdirmax), - fsparam_u32("acregmax", Opt_acregmax), - fsparam_u32("closetimeo", Opt_closetimeo), - fsparam_u32("echo_interval", Opt_echo_interval), - fsparam_u32("max_credits", Opt_max_credits), - fsparam_u32("handletimeout", Opt_handletimeout), - fsparam_u64("snapshot", Opt_snapshot), - fsparam_u32("max_channels", Opt_max_channels), - - /* Mount options which take string value */ - fsparam_string("source", Opt_source), - fsparam_string("user", Opt_user), - fsparam_string("username", Opt_user), - fsparam_string("pass", Opt_pass), - fsparam_string("password", Opt_pass), - fsparam_string("ip", Opt_ip), - fsparam_string("addr", Opt_ip), - fsparam_string("domain", Opt_domain), - fsparam_string("dom", Opt_domain), - fsparam_string("srcaddr", Opt_srcaddr), - fsparam_string("iocharset", Opt_iocharset), - fsparam_string("netbiosname", Opt_netbiosname), - fsparam_string("servern", Opt_servern), - fsparam_string("ver", Opt_ver), - fsparam_string("vers", Opt_vers), - fsparam_string("sec", Opt_sec), - fsparam_string("cache", Opt_cache), - - /* Arguments that should be ignored */ - fsparam_flag("guest", Opt_ignore), - fsparam_flag("noatime", Opt_ignore), - fsparam_flag("relatime", Opt_ignore), - fsparam_flag("_netdev", Opt_ignore), - fsparam_flag_no("suid", Opt_ignore), - fsparam_flag_no("exec", Opt_ignore), - fsparam_flag_no("dev", Opt_ignore), - fsparam_flag_no("mand", Opt_ignore), - fsparam_flag_no("auto", Opt_ignore), - fsparam_string("cred", Opt_ignore), - fsparam_string("credentials", Opt_ignore), - /* - * UNC and prefixpath is now extracted from Opt_source - * in the new mount API so we can just ignore them going forward. - */ - fsparam_string("unc", Opt_ignore), - fsparam_string("prefixpath", Opt_ignore), - {} -}; - -static int -cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) -{ - - substring_t args[MAX_OPT_ARGS]; - - /* - * With mount options, the last one should win. Reset any existing - * settings back to default. - */ - ctx->sectype = Unspecified; - ctx->sign = false; - - switch (match_token(value, cifs_secflavor_tokens, args)) { - case Opt_sec_krb5p: - cifs_errorf(fc, "sec=krb5p is not supported!\n"); - return 1; - case Opt_sec_krb5i: - ctx->sign = true; - fallthrough; - case Opt_sec_krb5: - ctx->sectype = Kerberos; - break; - case Opt_sec_ntlmsspi: - ctx->sign = true; - fallthrough; - case Opt_sec_ntlmssp: - ctx->sectype = RawNTLMSSP; - break; - case Opt_sec_ntlmv2i: - ctx->sign = true; - fallthrough; - case Opt_sec_ntlmv2: - ctx->sectype = NTLMv2; - break; - case Opt_sec_none: - ctx->nullauth = 1; - break; - default: - cifs_errorf(fc, "bad security option: %s\n", value); - return 1; - } - - return 0; -} - -static const match_table_t cifs_cacheflavor_tokens = { - { Opt_cache_loose, "loose" }, - { Opt_cache_strict, "strict" }, - { Opt_cache_none, "none" }, - { Opt_cache_ro, "ro" }, - { Opt_cache_rw, "singleclient" }, - { Opt_cache_err, NULL } -}; - -static int -cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) -{ - substring_t args[MAX_OPT_ARGS]; - - switch (match_token(value, cifs_cacheflavor_tokens, args)) { - case Opt_cache_loose: - ctx->direct_io = false; - ctx->strict_io = false; - ctx->cache_ro = false; - ctx->cache_rw = false; - break; - case Opt_cache_strict: - ctx->direct_io = false; - ctx->strict_io = true; - ctx->cache_ro = false; - ctx->cache_rw = false; - break; - case Opt_cache_none: - ctx->direct_io = true; - ctx->strict_io = false; - ctx->cache_ro = false; - ctx->cache_rw = false; - break; - case Opt_cache_ro: - ctx->direct_io = false; - ctx->strict_io = false; - ctx->cache_ro = true; - ctx->cache_rw = false; - break; - case Opt_cache_rw: - ctx->direct_io = false; - ctx->strict_io = false; - ctx->cache_ro = false; - ctx->cache_rw = true; - break; - default: - cifs_errorf(fc, "bad cache= option: %s\n", value); - return 1; - } - return 0; -} - -#define DUP_CTX_STR(field) \ -do { \ - if (ctx->field) { \ - new_ctx->field = kstrdup(ctx->field, GFP_ATOMIC); \ - if (new_ctx->field == NULL) { \ - smb3_cleanup_fs_context_contents(new_ctx); \ - return -ENOMEM; \ - } \ - } \ -} while (0) - -int -smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx) -{ - memcpy(new_ctx, ctx, sizeof(*ctx)); - new_ctx->prepath = NULL; - new_ctx->mount_options = NULL; - new_ctx->nodename = NULL; - new_ctx->username = NULL; - new_ctx->password = NULL; - new_ctx->server_hostname = NULL; - new_ctx->domainname = NULL; - new_ctx->UNC = NULL; - new_ctx->source = NULL; - new_ctx->iocharset = NULL; - /* - * Make sure to stay in sync with smb3_cleanup_fs_context_contents() - */ - DUP_CTX_STR(prepath); - DUP_CTX_STR(mount_options); - DUP_CTX_STR(username); - DUP_CTX_STR(password); - DUP_CTX_STR(server_hostname); - DUP_CTX_STR(UNC); - DUP_CTX_STR(source); - DUP_CTX_STR(domainname); - DUP_CTX_STR(nodename); - DUP_CTX_STR(iocharset); - - return 0; -} - -static int -cifs_parse_smb_version(struct fs_context *fc, char *value, struct smb3_fs_context *ctx, bool is_smb3) -{ - substring_t args[MAX_OPT_ARGS]; - - switch (match_token(value, cifs_smb_version_tokens, args)) { -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - case Smb_1: - if (disable_legacy_dialects) { - cifs_errorf(fc, "mount with legacy dialect disabled\n"); - return 1; - } - if (is_smb3) { - cifs_errorf(fc, "vers=1.0 (cifs) not permitted when mounting with smb3\n"); - return 1; - } - cifs_errorf(fc, "Use of the less secure dialect vers=1.0 is not recommended unless required for access to very old servers\n"); - ctx->ops = &smb1_operations; - ctx->vals = &smb1_values; - break; - case Smb_20: - if (disable_legacy_dialects) { - cifs_errorf(fc, "mount with legacy dialect disabled\n"); - return 1; - } - if (is_smb3) { - cifs_errorf(fc, "vers=2.0 not permitted when mounting with smb3\n"); - return 1; - } - ctx->ops = &smb20_operations; - ctx->vals = &smb20_values; - break; -#else - case Smb_1: - cifs_errorf(fc, "vers=1.0 (cifs) mount not permitted when legacy dialects disabled\n"); - return 1; - case Smb_20: - cifs_errorf(fc, "vers=2.0 mount not permitted when legacy dialects disabled\n"); - return 1; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ - case Smb_21: - ctx->ops = &smb21_operations; - ctx->vals = &smb21_values; - break; - case Smb_30: - ctx->ops = &smb30_operations; - ctx->vals = &smb30_values; - break; - case Smb_302: - ctx->ops = &smb30_operations; /* currently identical with 3.0 */ - ctx->vals = &smb302_values; - break; - case Smb_311: - ctx->ops = &smb311_operations; - ctx->vals = &smb311_values; - break; - case Smb_3any: - ctx->ops = &smb30_operations; /* currently identical with 3.0 */ - ctx->vals = &smb3any_values; - break; - case Smb_default: - ctx->ops = &smb30_operations; - ctx->vals = &smbdefault_values; - break; - default: - cifs_errorf(fc, "Unknown vers= option specified: %s\n", value); - return 1; - } - return 0; -} - -int smb3_parse_opt(const char *options, const char *key, char **val) -{ - int rc = -ENOENT; - char *opts, *orig, *p; - - orig = opts = kstrdup(options, GFP_KERNEL); - if (!opts) - return -ENOMEM; - - while ((p = strsep(&opts, ","))) { - char *nval; - - if (!*p) - continue; - if (strncasecmp(p, key, strlen(key))) - continue; - nval = strchr(p, '='); - if (nval) { - if (nval == p) - continue; - *nval++ = 0; - *val = kstrdup(nval, GFP_KERNEL); - rc = !*val ? -ENOMEM : 0; - goto out; - } - } -out: - kfree(orig); - return rc; -} - -/* - * Remove duplicate path delimiters. Windows is supposed to do that - * but there are some bugs that prevent rename from working if there are - * multiple delimiters. - * - * Returns a sanitized duplicate of @path. @gfp indicates the GFP_* flags - * for kstrdup. - * The caller is responsible for freeing the original. - */ -#define IS_DELIM(c) ((c) == '/' || (c) == '\\') -char *cifs_sanitize_prepath(char *prepath, gfp_t gfp) -{ - char *cursor1 = prepath, *cursor2 = prepath; - - /* skip all prepended delimiters */ - while (IS_DELIM(*cursor1)) - cursor1++; - - /* copy the first letter */ - *cursor2 = *cursor1; - - /* copy the remainder... */ - while (*(cursor1++)) { - /* ... skipping all duplicated delimiters */ - if (IS_DELIM(*cursor1) && IS_DELIM(*cursor2)) - continue; - *(++cursor2) = *cursor1; - } - - /* if the last character is a delimiter, skip it */ - if (IS_DELIM(*(cursor2 - 1))) - cursor2--; - - *(cursor2) = '\0'; - return kstrdup(prepath, gfp); -} - -/* - * Parse a devname into substrings and populate the ctx->UNC and ctx->prepath - * fields with the result. Returns 0 on success and an error otherwise - * (e.g. ENOMEM or EINVAL) - */ -int -smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx) -{ - char *pos; - const char *delims = "/\\"; - size_t len; - - if (unlikely(!devname || !*devname)) { - cifs_dbg(VFS, "Device name not specified\n"); - return -EINVAL; - } - - /* make sure we have a valid UNC double delimiter prefix */ - len = strspn(devname, delims); - if (len != 2) - return -EINVAL; - - /* find delimiter between host and sharename */ - pos = strpbrk(devname + 2, delims); - if (!pos) - return -EINVAL; - - /* record the server hostname */ - kfree(ctx->server_hostname); - ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL); - if (!ctx->server_hostname) - return -ENOMEM; - - /* skip past delimiter */ - ++pos; - - /* now go until next delimiter or end of string */ - len = strcspn(pos, delims); - - /* move "pos" up to delimiter or NULL */ - pos += len; - kfree(ctx->UNC); - ctx->UNC = kstrndup(devname, pos - devname, GFP_KERNEL); - if (!ctx->UNC) - return -ENOMEM; - - convert_delimiter(ctx->UNC, '\\'); - - /* skip any delimiter */ - if (*pos == '/' || *pos == '\\') - pos++; - - kfree(ctx->prepath); - ctx->prepath = NULL; - - /* If pos is NULL then no prepath */ - if (!*pos) - return 0; - - ctx->prepath = cifs_sanitize_prepath(pos, GFP_KERNEL); - if (!ctx->prepath) - return -ENOMEM; - - return 0; -} - -static void smb3_fs_context_free(struct fs_context *fc); -static int smb3_fs_context_parse_param(struct fs_context *fc, - struct fs_parameter *param); -static int smb3_fs_context_parse_monolithic(struct fs_context *fc, - void *data); -static int smb3_get_tree(struct fs_context *fc); -static int smb3_reconfigure(struct fs_context *fc); - -static const struct fs_context_operations smb3_fs_context_ops = { - .free = smb3_fs_context_free, - .parse_param = smb3_fs_context_parse_param, - .parse_monolithic = smb3_fs_context_parse_monolithic, - .get_tree = smb3_get_tree, - .reconfigure = smb3_reconfigure, -}; - -/* - * Parse a monolithic block of data from sys_mount(). - * smb3_fs_context_parse_monolithic - Parse key[=val][,key[=val]]* mount data - * @ctx: The superblock configuration to fill in. - * @data: The data to parse - * - * Parse a blob of data that's in key[=val][,key[=val]]* form. This can be - * called from the ->monolithic_mount_data() fs_context operation. - * - * Returns 0 on success or the error returned by the ->parse_option() fs_context - * operation on failure. - */ -static int smb3_fs_context_parse_monolithic(struct fs_context *fc, - void *data) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - char *options = data, *key; - int ret = 0; - - if (!options) - return 0; - - ctx->mount_options = kstrdup(data, GFP_KERNEL); - if (ctx->mount_options == NULL) - return -ENOMEM; - - ret = security_sb_eat_lsm_opts(options, &fc->security); - if (ret) - return ret; - - /* BB Need to add support for sep= here TBD */ - while ((key = strsep(&options, ",")) != NULL) { - size_t len; - char *value; - - if (*key == 0) - break; - - /* Check if following character is the deliminator If yes, - * we have encountered a double deliminator reset the NULL - * character to the deliminator - */ - while (options && options[0] == ',') { - len = strlen(key); - strcpy(key + len, options); - options = strchr(options, ','); - if (options) - *options++ = 0; - } - - - len = 0; - value = strchr(key, '='); - if (value) { - if (value == key) - continue; - *value++ = 0; - len = strlen(value); - } - - ret = vfs_parse_fs_string(fc, key, value, len); - if (ret < 0) - break; - } - - return ret; -} - -/* - * Validate the preparsed information in the config. - */ -static int smb3_fs_context_validate(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - - if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) { - cifs_errorf(fc, "SMB Direct requires Version >=3.0\n"); - return -EOPNOTSUPP; - } - -#ifndef CONFIG_KEYS - /* Muliuser mounts require CONFIG_KEYS support */ - if (ctx->multiuser) { - cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); - return -1; - } -#endif - - if (ctx->got_version == false) - pr_warn_once("No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.\n"); - - - if (!ctx->UNC) { - cifs_errorf(fc, "CIFS mount error: No usable UNC path provided in device string!\n"); - return -1; - } - - /* make sure UNC has a share name */ - if (strlen(ctx->UNC) < 3 || !strchr(ctx->UNC + 3, '\\')) { - cifs_errorf(fc, "Malformed UNC. Unable to find share name.\n"); - return -ENOENT; - } - - if (!ctx->got_ip) { - int len; - const char *slash; - - /* No ip= option specified? Try to get it from UNC */ - /* Use the address part of the UNC. */ - slash = strchr(&ctx->UNC[2], '\\'); - len = slash - &ctx->UNC[2]; - if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, - &ctx->UNC[2], len)) { - pr_err("Unable to determine destination address\n"); - return -EHOSTUNREACH; - } - } - - /* set the port that we got earlier */ - cifs_set_port((struct sockaddr *)&ctx->dstaddr, ctx->port); - - if (ctx->override_uid && !ctx->uid_specified) { - ctx->override_uid = 0; - pr_notice("ignoring forceuid mount option specified with no uid= option\n"); - } - - if (ctx->override_gid && !ctx->gid_specified) { - ctx->override_gid = 0; - pr_notice("ignoring forcegid mount option specified with no gid= option\n"); - } - - return 0; -} - -static int smb3_get_tree_common(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - struct dentry *root; - int rc = 0; - - root = cifs_smb3_do_mount(fc->fs_type, 0, ctx); - if (IS_ERR(root)) - return PTR_ERR(root); - - fc->root = root; - - return rc; -} - -/* - * Create an SMB3 superblock from the parameters passed. - */ -static int smb3_get_tree(struct fs_context *fc) -{ - int err = smb3_fs_context_validate(fc); - int ret; - - if (err) - return err; - mutex_lock(&cifs_mount_mutex); - ret = smb3_get_tree_common(fc); - mutex_unlock(&cifs_mount_mutex); - return ret; -} - -static void smb3_fs_context_free(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - - smb3_cleanup_fs_context(ctx); -} - -/* - * Compare the old and new proposed context during reconfigure - * and check if the changes are compatible. - */ -static int smb3_verify_reconfigure_ctx(struct fs_context *fc, - struct smb3_fs_context *new_ctx, - struct smb3_fs_context *old_ctx) -{ - if (new_ctx->posix_paths != old_ctx->posix_paths) { - cifs_errorf(fc, "can not change posixpaths during remount\n"); - return -EINVAL; - } - if (new_ctx->sectype != old_ctx->sectype) { - cifs_errorf(fc, "can not change sec during remount\n"); - return -EINVAL; - } - if (new_ctx->multiuser != old_ctx->multiuser) { - cifs_errorf(fc, "can not change multiuser during remount\n"); - return -EINVAL; - } - if (new_ctx->UNC && - (!old_ctx->UNC || strcmp(new_ctx->UNC, old_ctx->UNC))) { - cifs_errorf(fc, "can not change UNC during remount\n"); - return -EINVAL; - } - if (new_ctx->username && - (!old_ctx->username || strcmp(new_ctx->username, old_ctx->username))) { - cifs_errorf(fc, "can not change username during remount\n"); - return -EINVAL; - } - if (new_ctx->password && - (!old_ctx->password || strcmp(new_ctx->password, old_ctx->password))) { - cifs_errorf(fc, "can not change password during remount\n"); - return -EINVAL; - } - if (new_ctx->domainname && - (!old_ctx->domainname || strcmp(new_ctx->domainname, old_ctx->domainname))) { - cifs_errorf(fc, "can not change domainname during remount\n"); - return -EINVAL; - } - if (strcmp(new_ctx->workstation_name, old_ctx->workstation_name)) { - cifs_errorf(fc, "can not change workstation_name during remount\n"); - return -EINVAL; - } - if (new_ctx->nodename && - (!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) { - cifs_errorf(fc, "can not change nodename during remount\n"); - return -EINVAL; - } - if (new_ctx->iocharset && - (!old_ctx->iocharset || strcmp(new_ctx->iocharset, old_ctx->iocharset))) { - cifs_errorf(fc, "can not change iocharset during remount\n"); - return -EINVAL; - } - - return 0; -} - -#define STEAL_STRING(cifs_sb, ctx, field) \ -do { \ - kfree(ctx->field); \ - ctx->field = cifs_sb->ctx->field; \ - cifs_sb->ctx->field = NULL; \ -} while (0) - -#define STEAL_STRING_SENSITIVE(cifs_sb, ctx, field) \ -do { \ - kfree_sensitive(ctx->field); \ - ctx->field = cifs_sb->ctx->field; \ - cifs_sb->ctx->field = NULL; \ -} while (0) - -static int smb3_reconfigure(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - struct dentry *root = fc->root; - struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); - int rc; - - rc = smb3_verify_reconfigure_ctx(fc, ctx, cifs_sb->ctx); - if (rc) - return rc; - - /* - * We can not change UNC/username/password/domainname/ - * workstation_name/nodename/iocharset - * during reconnect so ignore what we have in the new context and - * just use what we already have in cifs_sb->ctx. - */ - STEAL_STRING(cifs_sb, ctx, UNC); - STEAL_STRING(cifs_sb, ctx, source); - STEAL_STRING(cifs_sb, ctx, username); - STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); - STEAL_STRING(cifs_sb, ctx, domainname); - STEAL_STRING(cifs_sb, ctx, nodename); - STEAL_STRING(cifs_sb, ctx, iocharset); - - /* if rsize or wsize not passed in on remount, use previous values */ - if (ctx->rsize == 0) - ctx->rsize = cifs_sb->ctx->rsize; - if (ctx->wsize == 0) - ctx->wsize = cifs_sb->ctx->wsize; - - - smb3_cleanup_fs_context_contents(cifs_sb->ctx); - rc = smb3_fs_context_dup(cifs_sb->ctx, ctx); - smb3_update_mnt_flags(cifs_sb); -#ifdef CONFIG_CIFS_DFS_UPCALL - if (!rc) - rc = dfs_cache_remount_fs(cifs_sb); -#endif - - return rc; -} - -static int smb3_fs_context_parse_param(struct fs_context *fc, - struct fs_parameter *param) -{ - struct fs_parse_result result; - struct smb3_fs_context *ctx = smb3_fc2context(fc); - int i, opt; - bool is_smb3 = !strcmp(fc->fs_type->name, "smb3"); - bool skip_parsing = false; - kuid_t uid; - kgid_t gid; - - cifs_dbg(FYI, "CIFS: parsing cifs mount option '%s'\n", param->key); - - /* - * fs_parse can not handle string options with an empty value so - * we will need special handling of them. - */ - if (param->type == fs_value_is_string && param->string[0] == 0) { - if (!strcmp("pass", param->key) || !strcmp("password", param->key)) { - skip_parsing = true; - opt = Opt_pass; - } else if (!strcmp("user", param->key) || !strcmp("username", param->key)) { - skip_parsing = true; - opt = Opt_user; - } - } - - if (!skip_parsing) { - opt = fs_parse(fc, smb3_fs_parameters, param, &result); - if (opt < 0) - return ctx->sloppy ? 1 : opt; - } - - switch (opt) { - case Opt_compress: - ctx->compression = UNKNOWN_TYPE; - cifs_dbg(VFS, - "SMB3 compression support is experimental\n"); - break; - case Opt_nodfs: - ctx->nodfs = 1; - break; - case Opt_hard: - if (result.negated) - ctx->retry = 0; - else - ctx->retry = 1; - break; - case Opt_soft: - if (result.negated) - ctx->retry = 1; - else - ctx->retry = 0; - break; - case Opt_mapposix: - if (result.negated) - ctx->remap = false; - else { - ctx->remap = true; - ctx->sfu_remap = false; /* disable SFU mapping */ - } - break; - case Opt_mapchars: - if (result.negated) - ctx->sfu_remap = false; - else { - ctx->sfu_remap = true; - ctx->remap = false; /* disable SFM (mapposix) mapping */ - } - break; - case Opt_user_xattr: - if (result.negated) - ctx->no_xattr = 1; - else - ctx->no_xattr = 0; - break; - case Opt_forceuid: - if (result.negated) - ctx->override_uid = 0; - else - ctx->override_uid = 1; - break; - case Opt_forcegid: - if (result.negated) - ctx->override_gid = 0; - else - ctx->override_gid = 1; - break; - case Opt_perm: - if (result.negated) - ctx->noperm = 1; - else - ctx->noperm = 0; - break; - case Opt_dynperm: - if (result.negated) - ctx->dynperm = 0; - else - ctx->dynperm = 1; - break; - case Opt_sfu: - if (result.negated) - ctx->sfu_emul = 0; - else - ctx->sfu_emul = 1; - break; - case Opt_noblocksend: - ctx->noblocksnd = 1; - break; - case Opt_noautotune: - ctx->noautotune = 1; - break; - case Opt_nolease: - ctx->no_lease = 1; - break; - case Opt_nosparse: - ctx->no_sparse = 1; - break; - case Opt_nodelete: - ctx->nodelete = 1; - break; - case Opt_multichannel: - if (result.negated) { - ctx->multichannel = false; - ctx->max_channels = 1; - } else { - ctx->multichannel = true; - /* if number of channels not specified, default to 2 */ - if (ctx->max_channels < 2) - ctx->max_channels = 2; - } - break; - case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->linux_uid = uid; - ctx->uid_specified = true; - break; - case Opt_cruid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->cred_uid = uid; - ctx->cruid_specified = true; - break; - case Opt_backupuid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->backupuid = uid; - ctx->backupuid_specified = true; - break; - case Opt_backupgid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - goto cifs_parse_mount_err; - ctx->backupgid = gid; - ctx->backupgid_specified = true; - break; - case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - goto cifs_parse_mount_err; - ctx->linux_gid = gid; - ctx->gid_specified = true; - break; - case Opt_port: - ctx->port = result.uint_32; - break; - case Opt_file_mode: - ctx->file_mode = result.uint_32; - break; - case Opt_dirmode: - ctx->dir_mode = result.uint_32; - break; - case Opt_min_enc_offload: - ctx->min_offload = result.uint_32; - break; - case Opt_blocksize: - /* - * inode blocksize realistically should never need to be - * less than 16K or greater than 16M and default is 1MB. - * Note that small inode block sizes (e.g. 64K) can lead - * to very poor performance of common tools like cp and scp - */ - if ((result.uint_32 < CIFS_MAX_MSGSIZE) || - (result.uint_32 > (4 * SMB3_DEFAULT_IOSIZE))) { - cifs_errorf(fc, "%s: Invalid blocksize\n", - __func__); - goto cifs_parse_mount_err; - } - ctx->bsize = result.uint_32; - ctx->got_bsize = true; - break; - case Opt_rasize: - /* - * readahead size realistically should never need to be - * less than 1M (CIFS_DEFAULT_IOSIZE) or greater than 32M - * (perhaps an exception should be considered in the - * for the case of a large number of channels - * when multichannel is negotiated) since that would lead - * to plenty of parallel I/O in flight to the server. - * Note that smaller read ahead sizes would - * hurt performance of common tools like cp and scp - * which often trigger sequential i/o with read ahead - */ - if ((result.uint_32 > (8 * SMB3_DEFAULT_IOSIZE)) || - (result.uint_32 < CIFS_DEFAULT_IOSIZE)) { - cifs_errorf(fc, "%s: Invalid rasize %d vs. %d\n", - __func__, result.uint_32, SMB3_DEFAULT_IOSIZE); - goto cifs_parse_mount_err; - } - ctx->rasize = result.uint_32; - break; - case Opt_rsize: - ctx->rsize = result.uint_32; - ctx->got_rsize = true; - break; - case Opt_wsize: - ctx->wsize = result.uint_32; - ctx->got_wsize = true; - break; - case Opt_acregmax: - ctx->acregmax = HZ * result.uint_32; - if (ctx->acregmax > CIFS_MAX_ACTIMEO) { - cifs_errorf(fc, "acregmax too large\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_acdirmax: - ctx->acdirmax = HZ * result.uint_32; - if (ctx->acdirmax > CIFS_MAX_ACTIMEO) { - cifs_errorf(fc, "acdirmax too large\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_actimeo: - if (HZ * result.uint_32 > CIFS_MAX_ACTIMEO) { - cifs_errorf(fc, "timeout too large\n"); - goto cifs_parse_mount_err; - } - if ((ctx->acdirmax != CIFS_DEF_ACTIMEO) || - (ctx->acregmax != CIFS_DEF_ACTIMEO)) { - cifs_errorf(fc, "actimeo ignored since acregmax or acdirmax specified\n"); - break; - } - ctx->acdirmax = ctx->acregmax = HZ * result.uint_32; - break; - case Opt_closetimeo: - ctx->closetimeo = HZ * result.uint_32; - if (ctx->closetimeo > SMB3_MAX_DCLOSETIMEO) { - cifs_errorf(fc, "closetimeo too large\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_echo_interval: - ctx->echo_interval = result.uint_32; - break; - case Opt_snapshot: - ctx->snapshot_time = result.uint_64; - break; - case Opt_max_credits: - if (result.uint_32 < 20 || result.uint_32 > 60000) { - cifs_errorf(fc, "%s: Invalid max_credits value\n", - __func__); - goto cifs_parse_mount_err; - } - ctx->max_credits = result.uint_32; - break; - case Opt_max_channels: - if (result.uint_32 < 1 || result.uint_32 > CIFS_MAX_CHANNELS) { - cifs_errorf(fc, "%s: Invalid max_channels value, needs to be 1-%d\n", - __func__, CIFS_MAX_CHANNELS); - goto cifs_parse_mount_err; - } - ctx->max_channels = result.uint_32; - /* If more than one channel requested ... they want multichan */ - if (result.uint_32 > 1) - ctx->multichannel = true; - break; - case Opt_handletimeout: - ctx->handle_timeout = result.uint_32; - if (ctx->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) { - cifs_errorf(fc, "Invalid handle cache timeout, longer than 16 minutes\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_source: - kfree(ctx->UNC); - ctx->UNC = NULL; - switch (smb3_parse_devname(param->string, ctx)) { - case 0: - break; - case -ENOMEM: - cifs_errorf(fc, "Unable to allocate memory for devname\n"); - goto cifs_parse_mount_err; - case -EINVAL: - cifs_errorf(fc, "Malformed UNC in devname\n"); - goto cifs_parse_mount_err; - default: - cifs_errorf(fc, "Unknown error parsing devname\n"); - goto cifs_parse_mount_err; - } - ctx->source = kstrdup(param->string, GFP_KERNEL); - if (ctx->source == NULL) { - cifs_errorf(fc, "OOM when copying UNC string\n"); - goto cifs_parse_mount_err; - } - fc->source = kstrdup(param->string, GFP_KERNEL); - if (fc->source == NULL) { - cifs_errorf(fc, "OOM when copying UNC string\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_user: - kfree(ctx->username); - ctx->username = NULL; - if (strlen(param->string) == 0) { - /* null user, ie. anonymous authentication */ - ctx->nullauth = 1; - break; - } - - if (strnlen(param->string, CIFS_MAX_USERNAME_LEN) > - CIFS_MAX_USERNAME_LEN) { - pr_warn("username too long\n"); - goto cifs_parse_mount_err; - } - ctx->username = kstrdup(param->string, GFP_KERNEL); - if (ctx->username == NULL) { - cifs_errorf(fc, "OOM when copying username string\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_pass: - kfree_sensitive(ctx->password); - ctx->password = NULL; - if (strlen(param->string) == 0) - break; - - ctx->password = kstrdup(param->string, GFP_KERNEL); - if (ctx->password == NULL) { - cifs_errorf(fc, "OOM when copying password string\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_ip: - if (strlen(param->string) == 0) { - ctx->got_ip = false; - break; - } - if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, - param->string, - strlen(param->string))) { - pr_err("bad ip= option (%s)\n", param->string); - goto cifs_parse_mount_err; - } - ctx->got_ip = true; - break; - case Opt_domain: - if (strnlen(param->string, CIFS_MAX_DOMAINNAME_LEN) - == CIFS_MAX_DOMAINNAME_LEN) { - pr_warn("domain name too long\n"); - goto cifs_parse_mount_err; - } - - kfree(ctx->domainname); - ctx->domainname = kstrdup(param->string, GFP_KERNEL); - if (ctx->domainname == NULL) { - cifs_errorf(fc, "OOM when copying domainname string\n"); - goto cifs_parse_mount_err; - } - cifs_dbg(FYI, "Domain name set\n"); - break; - case Opt_srcaddr: - if (!cifs_convert_address( - (struct sockaddr *)&ctx->srcaddr, - param->string, strlen(param->string))) { - pr_warn("Could not parse srcaddr: %s\n", - param->string); - goto cifs_parse_mount_err; - } - break; - case Opt_iocharset: - if (strnlen(param->string, 1024) >= 65) { - pr_warn("iocharset name too long\n"); - goto cifs_parse_mount_err; - } - - if (strncasecmp(param->string, "default", 7) != 0) { - kfree(ctx->iocharset); - ctx->iocharset = kstrdup(param->string, GFP_KERNEL); - if (ctx->iocharset == NULL) { - cifs_errorf(fc, "OOM when copying iocharset string\n"); - goto cifs_parse_mount_err; - } - } - /* if iocharset not set then load_nls_default - * is used by caller - */ - cifs_dbg(FYI, "iocharset set to %s\n", ctx->iocharset); - break; - case Opt_netbiosname: - memset(ctx->source_rfc1001_name, 0x20, - RFC1001_NAME_LEN); - /* - * FIXME: are there cases in which a comma can - * be valid in workstation netbios name (and - * need special handling)? - */ - for (i = 0; i < RFC1001_NAME_LEN; i++) { - /* don't ucase netbiosname for user */ - if (param->string[i] == 0) - break; - ctx->source_rfc1001_name[i] = param->string[i]; - } - /* The string has 16th byte zero still from - * set at top of the function - */ - if (i == RFC1001_NAME_LEN && param->string[i] != 0) - pr_warn("netbiosname longer than 15 truncated\n"); - break; - case Opt_servern: - /* last byte, type, is 0x20 for servr type */ - memset(ctx->target_rfc1001_name, 0x20, - RFC1001_NAME_LEN_WITH_NULL); - /* - * BB are there cases in which a comma can be valid in this - * workstation netbios name (and need special handling)? - */ - - /* user or mount helper must uppercase the netbios name */ - for (i = 0; i < 15; i++) { - if (param->string[i] == 0) - break; - ctx->target_rfc1001_name[i] = param->string[i]; - } - - /* The string has 16th byte zero still from set at top of function */ - if (i == RFC1001_NAME_LEN && param->string[i] != 0) - pr_warn("server netbiosname longer than 15 truncated\n"); - break; - case Opt_ver: - /* version of mount userspace tools, not dialect */ - /* If interface changes in mount.cifs bump to new ver */ - if (strncasecmp(param->string, "1", 1) == 0) { - if (strlen(param->string) > 1) { - pr_warn("Bad mount helper ver=%s. Did you want SMB1 (CIFS) dialect and mean to type vers=1.0 instead?\n", - param->string); - goto cifs_parse_mount_err; - } - /* This is the default */ - break; - } - /* For all other value, error */ - pr_warn("Invalid mount helper version specified\n"); - goto cifs_parse_mount_err; - case Opt_vers: - /* protocol version (dialect) */ - if (cifs_parse_smb_version(fc, param->string, ctx, is_smb3) != 0) - goto cifs_parse_mount_err; - ctx->got_version = true; - break; - case Opt_sec: - if (cifs_parse_security_flavors(fc, param->string, ctx) != 0) - goto cifs_parse_mount_err; - break; - case Opt_cache: - if (cifs_parse_cache_flavor(fc, param->string, ctx) != 0) - goto cifs_parse_mount_err; - break; - case Opt_witness: -#ifndef CONFIG_CIFS_SWN_UPCALL - cifs_errorf(fc, "Witness support needs CONFIG_CIFS_SWN_UPCALL config option\n"); - goto cifs_parse_mount_err; -#endif - ctx->witness = true; - pr_warn_once("Witness protocol support is experimental\n"); - break; - case Opt_rootfs: -#ifndef CONFIG_CIFS_ROOT - cifs_dbg(VFS, "rootfs support requires CONFIG_CIFS_ROOT config option\n"); - goto cifs_parse_mount_err; -#endif - ctx->rootfs = true; - break; - case Opt_posixpaths: - if (result.negated) - ctx->posix_paths = 0; - else - ctx->posix_paths = 1; - break; - case Opt_unix: - if (result.negated) { - if (ctx->linux_ext == 1) - pr_warn_once("conflicting posix mount options specified\n"); - ctx->linux_ext = 0; - ctx->no_linux_ext = 1; - } else { - if (ctx->no_linux_ext == 1) - pr_warn_once("conflicting posix mount options specified\n"); - ctx->linux_ext = 1; - ctx->no_linux_ext = 0; - } - break; - case Opt_nocase: - ctx->nocase = 1; - break; - case Opt_brl: - if (result.negated) { - /* - * turn off mandatory locking in mode - * if remote locking is turned off since the - * local vfs will do advisory - */ - if (ctx->file_mode == - (S_IALLUGO & ~(S_ISUID | S_IXGRP))) - ctx->file_mode = S_IALLUGO; - ctx->nobrl = 1; - } else - ctx->nobrl = 0; - break; - case Opt_handlecache: - if (result.negated) - ctx->nohandlecache = 1; - else - ctx->nohandlecache = 0; - break; - case Opt_forcemandatorylock: - ctx->mand_lock = 1; - break; - case Opt_setuids: - ctx->setuids = result.negated; - break; - case Opt_intr: - ctx->intr = !result.negated; - break; - case Opt_setuidfromacl: - ctx->setuidfromacl = 1; - break; - case Opt_strictsync: - ctx->nostrictsync = result.negated; - break; - case Opt_serverino: - ctx->server_ino = !result.negated; - break; - case Opt_rwpidforward: - ctx->rwpidforward = 1; - break; - case Opt_modesid: - ctx->mode_ace = 1; - break; - case Opt_cifsacl: - ctx->cifs_acl = !result.negated; - break; - case Opt_acl: - ctx->no_psx_acl = result.negated; - break; - case Opt_locallease: - ctx->local_lease = 1; - break; - case Opt_sign: - ctx->sign = true; - break; - case Opt_ignore_signature: - ctx->sign = true; - ctx->ignore_signature = true; - break; - case Opt_seal: - /* we do not do the following in secFlags because seal - * is a per tree connection (mount) not a per socket - * or per-smb connection option in the protocol - * vol->secFlg |= CIFSSEC_MUST_SEAL; - */ - ctx->seal = 1; - break; - case Opt_noac: - pr_warn("Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n"); - break; - case Opt_fsc: -#ifndef CONFIG_CIFS_FSCACHE - cifs_errorf(fc, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n"); - goto cifs_parse_mount_err; -#endif - ctx->fsc = true; - break; - case Opt_mfsymlinks: - ctx->mfsymlinks = true; - break; - case Opt_multiuser: - ctx->multiuser = true; - break; - case Opt_sloppy: - ctx->sloppy = true; - break; - case Opt_nosharesock: - ctx->nosharesock = true; - break; - case Opt_persistent: - if (result.negated) { - ctx->nopersistent = true; - if (ctx->persistent) { - cifs_errorf(fc, "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - } else { - ctx->persistent = true; - if ((ctx->nopersistent) || (ctx->resilient)) { - cifs_errorf(fc, "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - } - break; - case Opt_resilient: - if (result.negated) { - ctx->resilient = false; /* already the default */ - } else { - ctx->resilient = true; - if (ctx->persistent) { - cifs_errorf(fc, "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - } - break; - case Opt_tcp_nodelay: - /* tcp nodelay should not usually be needed since we CORK/UNCORK the socket */ - if (result.negated) - ctx->sockopt_tcp_nodelay = false; - else - ctx->sockopt_tcp_nodelay = true; - break; - case Opt_domainauto: - ctx->domainauto = true; - break; - case Opt_rdma: - ctx->rdma = true; - break; - } - /* case Opt_ignore: - is ignored as expected ... */ - - return 0; - - cifs_parse_mount_err: - kfree_sensitive(ctx->password); - return -EINVAL; -} - -int smb3_init_fs_context(struct fs_context *fc) -{ - struct smb3_fs_context *ctx; - char *nodename = utsname()->nodename; - int i; - - ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); - if (unlikely(!ctx)) - return -ENOMEM; - - strscpy(ctx->workstation_name, nodename, sizeof(ctx->workstation_name)); - - /* - * does not have to be perfect mapping since field is - * informational, only used for servers that do not support - * port 445 and it can be overridden at mount time - */ - memset(ctx->source_rfc1001_name, 0x20, RFC1001_NAME_LEN); - for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++) - ctx->source_rfc1001_name[i] = toupper(nodename[i]); - - ctx->source_rfc1001_name[RFC1001_NAME_LEN] = 0; - /* - * null target name indicates to use *SMBSERVR default called name - * if we end up sending RFC1001 session initialize - */ - ctx->target_rfc1001_name[0] = 0; - ctx->cred_uid = current_uid(); - ctx->linux_uid = current_uid(); - ctx->linux_gid = current_gid(); - /* By default 4MB read ahead size, 1MB block size */ - ctx->bsize = CIFS_DEFAULT_IOSIZE; /* can improve cp performance significantly */ - ctx->rasize = 0; /* 0 = use default (ie negotiated rsize) for read ahead pages */ - - /* - * default to SFM style remapping of seven reserved characters - * unless user overrides it or we negotiate CIFS POSIX where - * it is unnecessary. Can not simultaneously use more than one mapping - * since then readdir could list files that open could not open - */ - ctx->remap = true; - - /* default to only allowing write access to owner of the mount */ - ctx->dir_mode = ctx->file_mode = S_IRUGO | S_IXUGO | S_IWUSR; - - /* ctx->retry default is 0 (i.e. "soft" limited retry not hard retry) */ - /* default is always to request posix paths. */ - ctx->posix_paths = 1; - /* default to using server inode numbers where available */ - ctx->server_ino = 1; - - /* default is to use strict cifs caching semantics */ - ctx->strict_io = true; - - ctx->acregmax = CIFS_DEF_ACTIMEO; - ctx->acdirmax = CIFS_DEF_ACTIMEO; - ctx->closetimeo = SMB3_DEF_DCLOSETIMEO; - - /* Most clients set timeout to 0, allows server to use its default */ - ctx->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */ - - /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */ - ctx->ops = &smb30_operations; - ctx->vals = &smbdefault_values; - - ctx->echo_interval = SMB_ECHO_INTERVAL_DEFAULT; - - /* default to no multichannel (single server connection) */ - ctx->multichannel = false; - ctx->max_channels = 1; - - ctx->backupuid_specified = false; /* no backup intent for a user */ - ctx->backupgid_specified = false; /* no backup intent for a group */ - -/* - * short int override_uid = -1; - * short int override_gid = -1; - * char *nodename = strdup(utsname()->nodename); - * struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr; - */ - - fc->fs_private = ctx; - fc->ops = &smb3_fs_context_ops; - return 0; -} - -void -smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx) -{ - if (ctx == NULL) - return; - - /* - * Make sure this stays in sync with smb3_fs_context_dup() - */ - kfree(ctx->mount_options); - ctx->mount_options = NULL; - kfree(ctx->username); - ctx->username = NULL; - kfree_sensitive(ctx->password); - ctx->password = NULL; - kfree(ctx->server_hostname); - ctx->server_hostname = NULL; - kfree(ctx->UNC); - ctx->UNC = NULL; - kfree(ctx->source); - ctx->source = NULL; - kfree(ctx->domainname); - ctx->domainname = NULL; - kfree(ctx->nodename); - ctx->nodename = NULL; - kfree(ctx->iocharset); - ctx->iocharset = NULL; - kfree(ctx->prepath); - ctx->prepath = NULL; -} - -void -smb3_cleanup_fs_context(struct smb3_fs_context *ctx) -{ - if (!ctx) - return; - smb3_cleanup_fs_context_contents(ctx); - kfree(ctx); -} - -void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb) -{ - struct smb3_fs_context *ctx = cifs_sb->ctx; - - if (ctx->nodfs) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_DFS; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_DFS; - - if (ctx->noperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_PERM; - - if (ctx->setuids) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SET_UID; - - if (ctx->setuidfromacl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UID_FROM_ACL; - - if (ctx->server_ino) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; - - if (ctx->remap) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SFM_CHR; - - if (ctx->sfu_remap) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SPECIAL_CHR; - - if (ctx->no_xattr) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_XATTR; - - if (ctx->sfu_emul) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UNX_EMUL; - - if (ctx->nobrl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_BRL; - - if (ctx->nohandlecache) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_HANDLE_CACHE; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_HANDLE_CACHE; - - if (ctx->nostrictsync) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOSSYNC; - - if (ctx->mand_lock) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOPOSIXBRL; - - if (ctx->rwpidforward) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_RWPIDFORWARD; - - if (ctx->mode_ace) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MODE_FROM_SID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MODE_FROM_SID; - - if (ctx->cifs_acl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_ACL; - - if (ctx->backupuid_specified) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPUID; - - if (ctx->backupgid_specified) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPGID; - - if (ctx->override_uid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_UID; - - if (ctx->override_gid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_GID; - - if (ctx->dynperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DYNPERM; - - if (ctx->fsc) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_FSCACHE; - - if (ctx->multiuser) - cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | - CIFS_MOUNT_NO_PERM); - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MULTIUSER; - - - if (ctx->strict_io) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_STRICT_IO; - - if (ctx->direct_io) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DIRECT_IO; - - if (ctx->mfsymlinks) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MF_SYMLINKS; - if (ctx->mfsymlinks) { - if (ctx->sfu_emul) { - /* - * Our SFU ("Services for Unix" emulation does not allow - * creating symlinks but does allow reading existing SFU - * symlinks (it does allow both creating and reading SFU - * style mknod and FIFOs though). When "mfsymlinks" and - * "sfu" are both enabled at the same time, it allows - * reading both types of symlinks, but will only create - * them with mfsymlinks format. This allows better - * Apple compatibility (probably better for Samba too) - * while still recognizing old Windows style symlinks. - */ - cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n"); - } - } - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SHUTDOWN; - - return; -} diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h deleted file mode 100644 index 26093f54d3e6..000000000000 --- a/fs/cifs/fs_context.h +++ /dev/null @@ -1,293 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020, Microsoft Corporation. - * - * Author(s): Steve French - * David Howells - */ - -#ifndef _FS_CONTEXT_H -#define _FS_CONTEXT_H - -#include "cifsglob.h" -#include -#include - -/* Log errors in fs_context (new mount api) but also in dmesg (old style) */ -#define cifs_errorf(fc, fmt, ...) \ - do { \ - errorf(fc, fmt, ## __VA_ARGS__); \ - cifs_dbg(VFS, fmt, ## __VA_ARGS__); \ - } while (0) - -enum smb_version { - Smb_1 = 1, - Smb_20, - Smb_21, - Smb_30, - Smb_302, - Smb_311, - Smb_3any, - Smb_default, - Smb_version_err -}; - -enum { - Opt_cache_loose, - Opt_cache_strict, - Opt_cache_none, - Opt_cache_ro, - Opt_cache_rw, - Opt_cache_err -}; - -enum cifs_sec_param { - Opt_sec_krb5, - Opt_sec_krb5i, - Opt_sec_krb5p, - Opt_sec_ntlmsspi, - Opt_sec_ntlmssp, - Opt_sec_ntlmv2, - Opt_sec_ntlmv2i, - Opt_sec_none, - - Opt_sec_err -}; - -enum cifs_param { - /* Mount options that take no arguments */ - Opt_user_xattr, - Opt_forceuid, - Opt_forcegid, - Opt_noblocksend, - Opt_noautotune, - Opt_nolease, - Opt_nosparse, - Opt_hard, - Opt_soft, - Opt_perm, - Opt_nodelete, - Opt_mapposix, - Opt_mapchars, - Opt_nomapchars, - Opt_sfu, - Opt_nodfs, - Opt_posixpaths, - Opt_unix, - Opt_nocase, - Opt_brl, - Opt_handlecache, - Opt_forcemandatorylock, - Opt_setuidfromacl, - Opt_setuids, - Opt_dynperm, - Opt_intr, - Opt_strictsync, - Opt_serverino, - Opt_rwpidforward, - Opt_cifsacl, - Opt_acl, - Opt_locallease, - Opt_sign, - Opt_ignore_signature, - Opt_seal, - Opt_noac, - Opt_fsc, - Opt_mfsymlinks, - Opt_multiuser, - Opt_sloppy, - Opt_nosharesock, - Opt_persistent, - Opt_resilient, - Opt_tcp_nodelay, - Opt_domainauto, - Opt_rdma, - Opt_modesid, - Opt_rootfs, - Opt_multichannel, - Opt_compress, - Opt_witness, - - /* Mount options which take numeric value */ - Opt_backupuid, - Opt_backupgid, - Opt_uid, - Opt_cruid, - Opt_gid, - Opt_port, - Opt_file_mode, - Opt_dirmode, - Opt_min_enc_offload, - Opt_blocksize, - Opt_rasize, - Opt_rsize, - Opt_wsize, - Opt_actimeo, - Opt_acdirmax, - Opt_acregmax, - Opt_closetimeo, - Opt_echo_interval, - Opt_max_credits, - Opt_snapshot, - Opt_max_channels, - Opt_handletimeout, - - /* Mount options which take string value */ - Opt_source, - Opt_user, - Opt_pass, - Opt_ip, - Opt_domain, - Opt_srcaddr, - Opt_iocharset, - Opt_netbiosname, - Opt_servern, - Opt_ver, - Opt_vers, - Opt_sec, - Opt_cache, - - /* Mount options to be ignored */ - Opt_ignore, - - Opt_err -}; - -struct smb3_fs_context { - bool uid_specified; - bool cruid_specified; - bool gid_specified; - bool sloppy; - bool got_ip; - bool got_version; - bool got_rsize; - bool got_wsize; - bool got_bsize; - unsigned short port; - - char *username; - char *password; - char *domainname; - char *source; - char *server_hostname; - char *UNC; - char *nodename; - char workstation_name[CIFS_MAX_WORKSTATION_LEN]; - char *iocharset; /* local code page for mapping to and from Unicode */ - char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ - char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ - kuid_t cred_uid; - kuid_t linux_uid; - kgid_t linux_gid; - kuid_t backupuid; - kgid_t backupgid; - umode_t file_mode; - umode_t dir_mode; - enum securityEnum sectype; /* sectype requested via mnt opts */ - bool sign; /* was signing requested via mnt opts? */ - bool ignore_signature:1; - bool retry:1; - bool intr:1; - bool setuids:1; - bool setuidfromacl:1; - bool override_uid:1; - bool override_gid:1; - bool dynperm:1; - bool noperm:1; - bool nodelete:1; - bool mode_ace:1; - bool no_psx_acl:1; /* set if posix acl support should be disabled */ - bool cifs_acl:1; - bool backupuid_specified; /* mount option backupuid is specified */ - bool backupgid_specified; /* mount option backupgid is specified */ - bool no_xattr:1; /* set if xattr (EA) support should be disabled*/ - bool server_ino:1; /* use inode numbers from server ie UniqueId */ - bool direct_io:1; - bool strict_io:1; /* strict cache behavior */ - bool cache_ro:1; - bool cache_rw:1; - bool remap:1; /* set to remap seven reserved chars in filenames */ - bool sfu_remap:1; /* remap seven reserved chars ala SFU */ - bool posix_paths:1; /* unset to not ask for posix pathnames. */ - bool no_linux_ext:1; - bool linux_ext:1; - bool sfu_emul:1; - bool nullauth:1; /* attempt to authenticate with null user */ - bool nocase:1; /* request case insensitive filenames */ - bool nobrl:1; /* disable sending byte range locks to srv */ - bool nohandlecache:1; /* disable caching dir handles if srvr probs */ - bool mand_lock:1; /* send mandatory not posix byte range lock reqs */ - bool seal:1; /* request transport encryption on share */ - bool nodfs:1; /* Do not request DFS, even if available */ - bool local_lease:1; /* check leases only on local system, not remote */ - bool noblocksnd:1; - bool noautotune:1; - bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ - bool no_lease:1; /* disable requesting leases */ - bool no_sparse:1; /* do not attempt to set files sparse */ - bool fsc:1; /* enable fscache */ - bool mfsymlinks:1; /* use Minshall+French Symlinks */ - bool multiuser:1; - bool rwpidforward:1; /* pid forward for read/write operations */ - bool nosharesock:1; - bool persistent:1; - bool nopersistent:1; - bool resilient:1; /* noresilient not required since not fored for CA */ - bool domainauto:1; - bool rdma:1; - bool multichannel:1; - bool use_client_guid:1; - /* reuse existing guid for multichannel */ - u8 client_guid[SMB2_CLIENT_GUID_SIZE]; - unsigned int bsize; - unsigned int rasize; - unsigned int rsize; - unsigned int wsize; - unsigned int min_offload; - bool sockopt_tcp_nodelay:1; - /* attribute cache timemout for files and directories in jiffies */ - unsigned long acregmax; - unsigned long acdirmax; - /* timeout for deferred close of files in jiffies */ - unsigned long closetimeo; - struct smb_version_operations *ops; - struct smb_version_values *vals; - char *prepath; - struct sockaddr_storage dstaddr; /* destination address */ - struct sockaddr_storage srcaddr; /* allow binding to a local IP */ - struct nls_table *local_nls; /* This is a copy of the pointer in cifs_sb */ - unsigned int echo_interval; /* echo interval in secs */ - __u64 snapshot_time; /* needed for timewarp tokens */ - __u32 handle_timeout; /* persistent and durable handle timeout in ms */ - unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ - unsigned int max_channels; - __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ - bool rootfs:1; /* if it's a SMB root file system */ - bool witness:1; /* use witness protocol */ - - char *mount_options; -}; - -extern const struct fs_parameter_spec smb3_fs_parameters[]; - -extern int smb3_init_fs_context(struct fs_context *fc); -extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx); -extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx); - -static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *fc) -{ - return fc->fs_private; -} - -extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx); -extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); - -/* - * max deferred close timeout (jiffies) - 2^30 - */ -#define SMB3_MAX_DCLOSETIMEO (1 << 30) -#define SMB3_DEF_DCLOSETIMEO (1 * HZ) /* even 1 sec enough to help eg open/write/close/open/read */ - -extern char *cifs_sanitize_prepath(char *prepath, gfp_t gfp); - -#endif diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c deleted file mode 100644 index f6f3a6b75601..000000000000 --- a/fs/cifs/fscache.c +++ /dev/null @@ -1,253 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * CIFS filesystem cache interface - * - * Copyright (c) 2010 Novell, Inc. - * Author(s): Suresh Jayaraman - * - */ -#include "fscache.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifsproto.h" - -static void cifs_fscache_fill_volume_coherency( - struct cifs_tcon *tcon, - struct cifs_fscache_volume_coherency_data *cd) -{ - memset(cd, 0, sizeof(*cd)); - cd->resource_id = cpu_to_le64(tcon->resource_id); - cd->vol_create_time = tcon->vol_create_time; - cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); -} - -int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) -{ - struct cifs_fscache_volume_coherency_data cd; - struct TCP_Server_Info *server = tcon->ses->server; - struct fscache_volume *vcookie; - const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; - size_t slen, i; - char *sharename; - char *key; - int ret = -ENOMEM; - - tcon->fscache = NULL; - switch (sa->sa_family) { - case AF_INET: - case AF_INET6: - break; - default: - cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); - return -EINVAL; - } - - memset(&key, 0, sizeof(key)); - - sharename = extract_sharename(tcon->tree_name); - if (IS_ERR(sharename)) { - cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); - return -EINVAL; - } - - slen = strlen(sharename); - for (i = 0; i < slen; i++) - if (sharename[i] == '/') - sharename[i] = ';'; - - key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); - if (!key) - goto out; - - cifs_fscache_fill_volume_coherency(tcon, &cd); - vcookie = fscache_acquire_volume(key, - NULL, /* preferred_cache */ - &cd, sizeof(cd)); - cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); - if (IS_ERR(vcookie)) { - if (vcookie != ERR_PTR(-EBUSY)) { - ret = PTR_ERR(vcookie); - goto out_2; - } - pr_err("Cache volume key already in use (%s)\n", key); - vcookie = NULL; - } - - tcon->fscache = vcookie; - ret = 0; -out_2: - kfree(key); -out: - kfree(sharename); - return ret; -} - -void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) -{ - struct cifs_fscache_volume_coherency_data cd; - - cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); - - cifs_fscache_fill_volume_coherency(tcon, &cd); - fscache_relinquish_volume(tcon->fscache, &cd, false); - tcon->fscache = NULL; -} - -void cifs_fscache_get_inode_cookie(struct inode *inode) -{ - struct cifs_fscache_inode_coherency_data cd; - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - - cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); - - cifsi->netfs.cache = - fscache_acquire_cookie(tcon->fscache, 0, - &cifsi->uniqueid, sizeof(cifsi->uniqueid), - &cd, sizeof(cd), - i_size_read(&cifsi->netfs.inode)); -} - -void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) -{ - if (update) { - struct cifs_fscache_inode_coherency_data cd; - loff_t i_size = i_size_read(inode); - - cifs_fscache_fill_coherency(inode, &cd); - fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); - } else { - fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); - } -} - -void cifs_fscache_release_inode_cookie(struct inode *inode) -{ - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - - if (cookie) { - cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); - fscache_relinquish_cookie(cookie, false); - cifsi->netfs.cache = NULL; - } -} - -/* - * Fallback page reading interface. - */ -static int fscache_fallback_read_page(struct inode *inode, struct page *page) -{ - struct netfs_cache_resources cres; - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - struct iov_iter iter; - struct bio_vec bvec[1]; - int ret; - - memset(&cres, 0, sizeof(cres)); - bvec[0].bv_page = page; - bvec[0].bv_offset = 0; - bvec[0].bv_len = PAGE_SIZE; - iov_iter_bvec(&iter, ITER_DEST, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); - - ret = fscache_begin_read_operation(&cres, cookie); - if (ret < 0) - return ret; - - ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, - NULL, NULL); - fscache_end_operation(&cres); - return ret; -} - -/* - * Fallback page writing interface. - */ -static int fscache_fallback_write_page(struct inode *inode, struct page *page, - bool no_space_allocated_yet) -{ - struct netfs_cache_resources cres; - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - struct iov_iter iter; - struct bio_vec bvec[1]; - loff_t start = page_offset(page); - size_t len = PAGE_SIZE; - int ret; - - memset(&cres, 0, sizeof(cres)); - bvec[0].bv_page = page; - bvec[0].bv_offset = 0; - bvec[0].bv_len = PAGE_SIZE; - iov_iter_bvec(&iter, ITER_SOURCE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); - - ret = fscache_begin_write_operation(&cres, cookie); - if (ret < 0) - return ret; - - ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), - no_space_allocated_yet); - if (ret == 0) - ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL); - fscache_end_operation(&cres); - return ret; -} - -/* - * Retrieve a page from FS-Cache - */ -int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) -{ - int ret; - - cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", - __func__, cifs_inode_cookie(inode), page, inode); - - ret = fscache_fallback_read_page(inode, page); - if (ret < 0) - return ret; - - /* Read completed synchronously */ - SetPageUptodate(page); - return 0; -} - -void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) -{ - cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", - __func__, cifs_inode_cookie(inode), page, inode); - - fscache_fallback_write_page(inode, page, true); -} - -/* - * Query the cache occupancy. - */ -int __cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages) -{ - struct netfs_cache_resources cres; - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - loff_t start, data_start; - size_t len, data_len; - int ret; - - ret = fscache_begin_read_operation(&cres, cookie); - if (ret < 0) - return ret; - - start = first * PAGE_SIZE; - len = nr_pages * PAGE_SIZE; - ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE, - &data_start, &data_len); - if (ret == 0) { - *_data_first = data_start / PAGE_SIZE; - *_data_nr_pages = len / PAGE_SIZE; - } - - fscache_end_operation(&cres); - return ret; -} diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h deleted file mode 100644 index 67b601041f0a..000000000000 --- a/fs/cifs/fscache.h +++ /dev/null @@ -1,148 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * CIFS filesystem cache interface definitions - * - * Copyright (c) 2010 Novell, Inc. - * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> - * - */ -#ifndef _CIFS_FSCACHE_H -#define _CIFS_FSCACHE_H - -#include -#include - -#include "cifsglob.h" - -/* - * Coherency data attached to CIFS volume within the cache - */ -struct cifs_fscache_volume_coherency_data { - __le64 resource_id; /* unique server resource id */ - __le64 vol_create_time; - __le32 vol_serial_number; -} __packed; - -/* - * Coherency data attached to CIFS inode within the cache. - */ -struct cifs_fscache_inode_coherency_data { - __le64 last_write_time_sec; - __le64 last_change_time_sec; - __le32 last_write_time_nsec; - __le32 last_change_time_nsec; -}; - -#ifdef CONFIG_CIFS_FSCACHE - -/* - * fscache.c - */ -extern int cifs_fscache_get_super_cookie(struct cifs_tcon *); -extern void cifs_fscache_release_super_cookie(struct cifs_tcon *); - -extern void cifs_fscache_get_inode_cookie(struct inode *inode); -extern void cifs_fscache_release_inode_cookie(struct inode *); -extern void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update); - -static inline -void cifs_fscache_fill_coherency(struct inode *inode, - struct cifs_fscache_inode_coherency_data *cd) -{ - struct cifsInodeInfo *cifsi = CIFS_I(inode); - - memset(cd, 0, sizeof(*cd)); - cd->last_write_time_sec = cpu_to_le64(cifsi->netfs.inode.i_mtime.tv_sec); - cd->last_write_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_mtime.tv_nsec); - cd->last_change_time_sec = cpu_to_le64(cifsi->netfs.inode.i_ctime.tv_sec); - cd->last_change_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_ctime.tv_nsec); -} - - -static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) -{ - return netfs_i_cookie(&CIFS_I(inode)->netfs); -} - -static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) -{ - struct cifs_fscache_inode_coherency_data cd; - - cifs_fscache_fill_coherency(inode, &cd); - fscache_invalidate(cifs_inode_cookie(inode), &cd, - i_size_read(inode), flags); -} - -extern int __cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages); - -static inline int cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages) -{ - if (!cifs_inode_cookie(inode)) - return -ENOBUFS; - return __cifs_fscache_query_occupancy(inode, first, nr_pages, - _data_first, _data_nr_pages); -} - -extern int __cifs_readpage_from_fscache(struct inode *pinode, struct page *ppage); -extern void __cifs_readpage_to_fscache(struct inode *pinode, struct page *ppage); - - -static inline int cifs_readpage_from_fscache(struct inode *inode, - struct page *page) -{ - if (cifs_inode_cookie(inode)) - return __cifs_readpage_from_fscache(inode, page); - return -ENOBUFS; -} - -static inline void cifs_readpage_to_fscache(struct inode *inode, - struct page *page) -{ - if (cifs_inode_cookie(inode)) - __cifs_readpage_to_fscache(inode, page); -} - -#else /* CONFIG_CIFS_FSCACHE */ -static inline -void cifs_fscache_fill_coherency(struct inode *inode, - struct cifs_fscache_inode_coherency_data *cd) -{ -} - -static inline int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { return 0; } -static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {} - -static inline void cifs_fscache_get_inode_cookie(struct inode *inode) {} -static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} -static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {} -static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; } -static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {} - -static inline int cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages) -{ - *_data_first = ULONG_MAX; - *_data_nr_pages = 0; - return -ENOBUFS; -} - -static inline int -cifs_readpage_from_fscache(struct inode *inode, struct page *page) -{ - return -ENOBUFS; -} - -static inline -void cifs_readpage_to_fscache(struct inode *inode, struct page *page) {} - -#endif /* CONFIG_CIFS_FSCACHE */ - -#endif /* _CIFS_FSCACHE_H */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c deleted file mode 100644 index 8901d884f5b9..000000000000 --- a/fs/cifs/inode.c +++ /dev/null @@ -1,3093 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2010 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fscache.h" -#include "fs_context.h" -#include "cifs_ioctl.h" -#include "cached_dir.h" - -static void cifs_set_ops(struct inode *inode) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - switch (inode->i_mode & S_IFMT) { - case S_IFREG: - inode->i_op = &cifs_file_inode_ops; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - inode->i_fop = &cifs_file_direct_nobrl_ops; - else - inode->i_fop = &cifs_file_direct_ops; - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - inode->i_fop = &cifs_file_strict_nobrl_ops; - else - inode->i_fop = &cifs_file_strict_ops; - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - inode->i_fop = &cifs_file_nobrl_ops; - else { /* not direct, send byte range locks */ - inode->i_fop = &cifs_file_ops; - } - - /* check if server can support readahead */ - if (cifs_sb_master_tcon(cifs_sb)->ses->server->max_read < - PAGE_SIZE + MAX_CIFS_HDR_SIZE) - inode->i_data.a_ops = &cifs_addr_ops_smallbuf; - else - inode->i_data.a_ops = &cifs_addr_ops; - break; - case S_IFDIR: -#ifdef CONFIG_CIFS_DFS_UPCALL - if (IS_AUTOMOUNT(inode)) { - inode->i_op = &cifs_dfs_referral_inode_operations; - } else { -#else /* NO DFS support, treat as a directory */ - { -#endif - inode->i_op = &cifs_dir_inode_ops; - inode->i_fop = &cifs_dir_ops; - } - break; - case S_IFLNK: - inode->i_op = &cifs_symlink_inode_ops; - break; - default: - init_special_inode(inode, inode->i_mode, inode->i_rdev); - break; - } -} - -/* check inode attributes against fattr. If they don't match, tag the - * inode for cache invalidation - */ -static void -cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) -{ - struct cifs_fscache_inode_coherency_data cd; - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - - cifs_dbg(FYI, "%s: revalidating inode %llu\n", - __func__, cifs_i->uniqueid); - - if (inode->i_state & I_NEW) { - cifs_dbg(FYI, "%s: inode %llu is new\n", - __func__, cifs_i->uniqueid); - return; - } - - /* don't bother with revalidation if we have an oplock */ - if (CIFS_CACHE_READ(cifs_i)) { - cifs_dbg(FYI, "%s: inode %llu is oplocked\n", - __func__, cifs_i->uniqueid); - return; - } - - /* revalidate if mtime or size have changed */ - fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); - if (timespec64_equal(&inode->i_mtime, &fattr->cf_mtime) && - cifs_i->server_eof == fattr->cf_eof) { - cifs_dbg(FYI, "%s: inode %llu is unchanged\n", - __func__, cifs_i->uniqueid); - return; - } - - cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n", - __func__, cifs_i->uniqueid); - set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); - /* Invalidate fscache cookie */ - cifs_fscache_fill_coherency(&cifs_i->netfs.inode, &cd); - fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0); -} - -/* - * copy nlink to the inode, unless it wasn't provided. Provide - * sane values if we don't have an existing one and none was provided - */ -static void -cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) -{ - /* - * if we're in a situation where we can't trust what we - * got from the server (readdir, some non-unix cases) - * fake reasonable values - */ - if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { - /* only provide fake values on a new inode */ - if (inode->i_state & I_NEW) { - if (fattr->cf_cifsattrs & ATTR_DIRECTORY) - set_nlink(inode, 2); - else - set_nlink(inode, 1); - } - return; - } - - /* we trust the server, so update it */ - set_nlink(inode, fattr->cf_nlink); -} - -/* populate an inode with info from a cifs_fattr struct */ -int -cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) -{ - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - if (!(inode->i_state & I_NEW) && - unlikely(inode_wrong_type(inode, fattr->cf_mode))) { - CIFS_I(inode)->time = 0; /* force reval */ - return -ESTALE; - } - - cifs_revalidate_cache(inode, fattr); - - spin_lock(&inode->i_lock); - fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); - fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode); - fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode); - /* we do not want atime to be less than mtime, it broke some apps */ - if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime) < 0) - inode->i_atime = fattr->cf_mtime; - else - inode->i_atime = fattr->cf_atime; - inode->i_mtime = fattr->cf_mtime; - inode->i_ctime = fattr->cf_ctime; - inode->i_rdev = fattr->cf_rdev; - cifs_nlink_fattr_to_inode(inode, fattr); - inode->i_uid = fattr->cf_uid; - inode->i_gid = fattr->cf_gid; - - /* if dynperm is set, don't clobber existing mode */ - if (inode->i_state & I_NEW || - !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) - inode->i_mode = fattr->cf_mode; - - cifs_i->cifsAttrs = fattr->cf_cifsattrs; - - if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) - cifs_i->time = 0; - else - cifs_i->time = jiffies; - - if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) - set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); - else - clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); - - cifs_i->server_eof = fattr->cf_eof; - /* - * Can't safely change the file size here if the client is writing to - * it due to potential races. - */ - if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) { - i_size_write(inode, fattr->cf_eof); - - /* - * i_blocks is not related to (i_size / i_blksize), - * but instead 512 byte (2**9) size is required for - * calculating num blocks. - */ - inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9; - } - - if (S_ISLNK(fattr->cf_mode)) { - kfree(cifs_i->symlink_target); - cifs_i->symlink_target = fattr->cf_symlink_target; - fattr->cf_symlink_target = NULL; - } - spin_unlock(&inode->i_lock); - - if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) - inode->i_flags |= S_AUTOMOUNT; - if (inode->i_state & I_NEW) - cifs_set_ops(inode); - return 0; -} - -void -cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - return; - - fattr->cf_uniqueid = iunique(sb, ROOT_I); -} - -/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */ -void -cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_uniqueid = le64_to_cpu(info->UniqueId); - fattr->cf_bytes = le64_to_cpu(info->NumOfBytes); - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime); - fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange); - /* old POSIX extensions don't get create time */ - - fattr->cf_mode = le64_to_cpu(info->Permissions); - - /* - * Since we set the inode type below we need to mask off - * to avoid strange results if bits set above. - */ - fattr->cf_mode &= ~S_IFMT; - switch (le32_to_cpu(info->Type)) { - case UNIX_FILE: - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - break; - case UNIX_SYMLINK: - fattr->cf_mode |= S_IFLNK; - fattr->cf_dtype = DT_LNK; - break; - case UNIX_DIR: - fattr->cf_mode |= S_IFDIR; - fattr->cf_dtype = DT_DIR; - break; - case UNIX_CHARDEV: - fattr->cf_mode |= S_IFCHR; - fattr->cf_dtype = DT_CHR; - fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), - le64_to_cpu(info->DevMinor) & MINORMASK); - break; - case UNIX_BLOCKDEV: - fattr->cf_mode |= S_IFBLK; - fattr->cf_dtype = DT_BLK; - fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), - le64_to_cpu(info->DevMinor) & MINORMASK); - break; - case UNIX_FIFO: - fattr->cf_mode |= S_IFIFO; - fattr->cf_dtype = DT_FIFO; - break; - case UNIX_SOCKET: - fattr->cf_mode |= S_IFSOCK; - fattr->cf_dtype = DT_SOCK; - break; - default: - /* safest to call it a file if we do not know */ - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type)); - break; - } - - fattr->cf_uid = cifs_sb->ctx->linux_uid; - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { - u64 id = le64_to_cpu(info->Uid); - if (id < ((uid_t)-1)) { - kuid_t uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid)) - fattr->cf_uid = uid; - } - } - - fattr->cf_gid = cifs_sb->ctx->linux_gid; - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { - u64 id = le64_to_cpu(info->Gid); - if (id < ((gid_t)-1)) { - kgid_t gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid)) - fattr->cf_gid = gid; - } - } - - fattr->cf_nlink = le64_to_cpu(info->Nlinks); -} - -/* - * Fill a cifs_fattr struct with fake inode info. - * - * Needed to setup cifs_fattr data for the directory which is the - * junction to the new submount (ie to setup the fake directory - * which represents a DFS referral). - */ -static void -cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - cifs_dbg(FYI, "creating fake fattr for DFS referral\n"); - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; - ktime_get_coarse_real_ts64(&fattr->cf_mtime); - fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; - fattr->cf_nlink = 2; - fattr->cf_flags = CIFS_FATTR_DFS_REFERRAL; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_get_file_info_unix(struct file *filp) -{ - int rc; - unsigned int xid; - FILE_UNIX_BASIC_INFO find_data; - struct cifs_fattr fattr = {}; - struct inode *inode = file_inode(filp); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsFileInfo *cfile = filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - - xid = get_xid(); - - if (cfile->symlink_target) { - fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!fattr.cf_symlink_target) { - rc = -ENOMEM; - goto cifs_gfiunix_out; - } - } - - rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data); - if (!rc) { - cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); - } else if (rc == -EREMOTE) { - cifs_create_dfs_fattr(&fattr, inode->i_sb); - rc = 0; - } else - goto cifs_gfiunix_out; - - rc = cifs_fattr_to_inode(inode, &fattr); - -cifs_gfiunix_out: - free_xid(xid); - return rc; -} - -int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *full_path, - struct super_block *sb, unsigned int xid) -{ - int rc; - FILE_UNIX_BASIC_INFO find_data; - struct cifs_fattr fattr; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - cifs_dbg(FYI, "Getting info on %s\n", full_path); - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - /* could have done a find first instead but this returns more info */ - rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc); - cifs_put_tlink(tlink); - - if (!rc) { - cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); - } else if (rc == -EREMOTE) { - cifs_create_dfs_fattr(&fattr, sb); - rc = 0; - } else { - return rc; - } - - /* check for Minshall+French symlinks */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { - int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, - full_path); - if (tmprc) - cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); - } - - if (S_ISLNK(fattr.cf_mode) && !fattr.cf_symlink_target) { - if (!server->ops->query_symlink) - return -EOPNOTSUPP; - rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, - &fattr.cf_symlink_target, false); - if (rc) { - cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); - goto cgiiu_exit; - } - } - - if (*pinode == NULL) { - /* get new inode */ - cifs_fill_uniqueid(sb, &fattr); - *pinode = cifs_iget(sb, &fattr); - if (!*pinode) - rc = -ENOMEM; - } else { - /* we already have inode, update it */ - - /* if uniqueid is different, return error */ - if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && - CIFS_I(*pinode)->uniqueid != fattr.cf_uniqueid)) { - CIFS_I(*pinode)->time = 0; /* force reval */ - rc = -ESTALE; - goto cgiiu_exit; - } - - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(*pinode, &fattr); - } - -cgiiu_exit: - kfree(fattr.cf_symlink_target); - return rc; -} -#else -int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *full_path, - struct super_block *sb, unsigned int xid) -{ - return -EOPNOTSUPP; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_sfu_type(struct cifs_fattr *fattr, const char *path, - struct cifs_sb_info *cifs_sb, unsigned int xid) -{ - int rc; - __u32 oplock; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - char buf[24]; - unsigned int bytes_read; - char *pbuf; - int buf_type = CIFS_NO_BUFFER; - - pbuf = buf; - - fattr->cf_mode &= ~S_IFMT; - - if (fattr->cf_eof == 0) { - fattr->cf_mode |= S_IFIFO; - fattr->cf_dtype = DT_FIFO; - return 0; - } else if (fattr->cf_eof < 8) { - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - return -EINVAL; /* EOPNOTSUPP? */ - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_READ, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL); - if (rc) { - cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc); - cifs_put_tlink(tlink); - return rc; - } - - /* Read header */ - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = 24; - - rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms, - &bytes_read, &pbuf, &buf_type); - if ((rc == 0) && (bytes_read >= 8)) { - if (memcmp("IntxBLK", pbuf, 8) == 0) { - cifs_dbg(FYI, "Block device\n"); - fattr->cf_mode |= S_IFBLK; - fattr->cf_dtype = DT_BLK; - if (bytes_read == 24) { - /* we have enough to decode dev num */ - __u64 mjr; /* major */ - __u64 mnr; /* minor */ - mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); - mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); - fattr->cf_rdev = MKDEV(mjr, mnr); - } - } else if (memcmp("IntxCHR", pbuf, 8) == 0) { - cifs_dbg(FYI, "Char device\n"); - fattr->cf_mode |= S_IFCHR; - fattr->cf_dtype = DT_CHR; - if (bytes_read == 24) { - /* we have enough to decode dev num */ - __u64 mjr; /* major */ - __u64 mnr; /* minor */ - mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); - mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); - fattr->cf_rdev = MKDEV(mjr, mnr); - } - } else if (memcmp("IntxLNK", pbuf, 7) == 0) { - cifs_dbg(FYI, "Symlink\n"); - fattr->cf_mode |= S_IFLNK; - fattr->cf_dtype = DT_LNK; - } else { - fattr->cf_mode |= S_IFREG; /* file? */ - fattr->cf_dtype = DT_REG; - rc = -EOPNOTSUPP; - } - } else { - fattr->cf_mode |= S_IFREG; /* then it is a file */ - fattr->cf_dtype = DT_REG; - rc = -EOPNOTSUPP; /* or some unknown SFU type */ - } - - tcon->ses->server->ops->close(xid, tcon, &fid); - cifs_put_tlink(tlink); - return rc; -} - -#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ - -/* - * Fetch mode bits as provided by SFU. - * - * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ? - */ -static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, - struct cifs_sb_info *cifs_sb, unsigned int xid) -{ -#ifdef CONFIG_CIFS_XATTR - ssize_t rc; - char ea_value[4]; - __u32 mode; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - if (tcon->ses->server->ops->query_all_EAs == NULL) { - cifs_put_tlink(tlink); - return -EOPNOTSUPP; - } - - rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path, - "SETFILEBITS", ea_value, 4 /* size of buf */, - cifs_sb); - cifs_put_tlink(tlink); - if (rc < 0) - return (int)rc; - else if (rc > 3) { - mode = le32_to_cpu(*((__le32 *)ea_value)); - fattr->cf_mode &= ~SFBITS_MASK; - cifs_dbg(FYI, "special bits 0%o org mode 0%o\n", - mode, fattr->cf_mode); - fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode; - cifs_dbg(FYI, "special mode bits 0%o\n", mode); - } - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -/* Fill a cifs_fattr struct with info from POSIX info struct */ -static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, - struct super_block *sb, bool adjust_tz, bool symlink) -{ - struct smb311_posix_qinfo *info = &data->posix_fi; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - - memset(fattr, 0, sizeof(*fattr)); - - /* no fattr->flags to set */ - fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); - fattr->cf_uniqueid = le64_to_cpu(info->Inode); - - if (info->LastAccessTime) - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - else - ktime_get_coarse_real_ts64(&fattr->cf_atime); - - fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); - - if (adjust_tz) { - fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; - fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; - } - - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); - fattr->cf_createtime = le64_to_cpu(info->CreationTime); - - fattr->cf_nlink = le32_to_cpu(info->HardLinks); - fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); - /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ - /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ - - if (symlink) { - fattr->cf_mode |= S_IFLNK; - fattr->cf_dtype = DT_LNK; - fattr->cf_symlink_target = data->symlink_target; - data->symlink_target = NULL; - } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode |= S_IFDIR; - fattr->cf_dtype = DT_DIR; - } else { /* file */ - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - } - /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */ - - fattr->cf_uid = cifs_sb->ctx->linux_uid; /* TODO: map uid and gid from SID */ - fattr->cf_gid = cifs_sb->ctx->linux_gid; - - cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", - fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); -} - -static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, - struct super_block *sb, bool adjust_tz, bool symlink, - u32 reparse_tag) -{ - struct smb2_file_all_info *info = &data->fi; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); - if (info->DeletePending) - fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; - - if (info->LastAccessTime) - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - else - ktime_get_coarse_real_ts64(&fattr->cf_atime); - - fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); - - if (adjust_tz) { - fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; - fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; - } - - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); - fattr->cf_createtime = le64_to_cpu(info->CreationTime); - - fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); - if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) { - fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_LNK; - } else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) { - fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_FIFO; - } else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) { - fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_SOCK; - } else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) { - fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_CHR; - } else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) { - fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_BLK; - } else if (symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK || - reparse_tag == IO_REPARSE_TAG_NFS) { - fattr->cf_mode = S_IFLNK; - fattr->cf_dtype = DT_LNK; - } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; - fattr->cf_dtype = DT_DIR; - /* - * Server can return wrong NumberOfLinks value for directories - * when Unix extensions are disabled - fake it. - */ - if (!tcon->unix_ext) - fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; - } else { - fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_REG; - - /* clear write bits if ATTR_READONLY is set */ - if (fattr->cf_cifsattrs & ATTR_READONLY) - fattr->cf_mode &= ~(S_IWUGO); - - /* - * Don't accept zero nlink from non-unix servers unless - * delete is pending. Instead mark it as unknown. - */ - if ((fattr->cf_nlink < 1) && !tcon->unix_ext && - !info->DeletePending) { - cifs_dbg(VFS, "bogus file nlink value %u\n", - fattr->cf_nlink); - fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; - } - } - - if (S_ISLNK(fattr->cf_mode)) { - fattr->cf_symlink_target = data->symlink_target; - data->symlink_target = NULL; - } - - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; -} - -static int -cifs_get_file_info(struct file *filp) -{ - int rc; - unsigned int xid; - struct cifs_open_info_data data = {}; - struct cifs_fattr fattr; - struct inode *inode = file_inode(filp); - struct cifsFileInfo *cfile = filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - bool symlink = false; - u32 tag = 0; - - if (!server->ops->query_file_info) - return -ENOSYS; - - xid = get_xid(); - rc = server->ops->query_file_info(xid, tcon, cfile, &data); - switch (rc) { - case 0: - /* TODO: add support to query reparse tag */ - if (data.symlink_target) { - symlink = true; - tag = IO_REPARSE_TAG_SYMLINK; - } - cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag); - break; - case -EREMOTE: - cifs_create_dfs_fattr(&fattr, inode->i_sb); - rc = 0; - break; - case -EOPNOTSUPP: - case -EINVAL: - /* - * FIXME: legacy server -- fall back to path-based call? - * for now, just skip revalidating and mark inode for - * immediate reval. - */ - rc = 0; - CIFS_I(inode)->time = 0; - goto cgfi_exit; - default: - goto cgfi_exit; - } - - /* - * don't bother with SFU junk here -- just mark inode as needing - * revalidation. - */ - fattr.cf_uniqueid = CIFS_I(inode)->uniqueid; - fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(inode, &fattr); -cgfi_exit: - cifs_free_open_info(&data); - free_xid(xid); - return rc; -} - -/* Simple function to return a 64 bit hash of string. Rarely called */ -static __u64 simple_hashstr(const char *str) -{ - const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */ - __u64 hash = 0; - - while (*str) - hash = (hash + (__u64) *str++) * hash_mult; - - return hash; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/** - * cifs_backup_query_path_info - SMB1 fallback code to get ino - * - * Fallback code to get file metadata when we don't have access to - * full_path (EACCES) and have backup creds. - * - * @xid: transaction id used to identify original request in logs - * @tcon: information about the server share we have mounted - * @sb: the superblock stores info such as disk space available - * @full_path: name of the file we are getting the metadata for - * @resp_buf: will be set to cifs resp buf and needs to be freed with - * cifs_buf_release() when done with @data - * @data: will be set to search info result buffer - */ -static int -cifs_backup_query_path_info(int xid, - struct cifs_tcon *tcon, - struct super_block *sb, - const char *full_path, - void **resp_buf, - FILE_ALL_INFO **data) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_search_info info = {0}; - u16 flags; - int rc; - - *resp_buf = NULL; - info.endOfSearch = false; - if (tcon->unix_ext) - info.info_level = SMB_FIND_FILE_UNIX; - else if ((tcon->ses->capabilities & - tcon->ses->server->vals->cap_nt_find) == 0) - info.info_level = SMB_FIND_FILE_INFO_STANDARD; - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; - else /* no srvino useful for fallback to some netapp */ - info.info_level = SMB_FIND_FILE_DIRECTORY_INFO; - - flags = CIFS_SEARCH_CLOSE_ALWAYS | - CIFS_SEARCH_CLOSE_AT_END | - CIFS_SEARCH_BACKUP_SEARCH; - - rc = CIFSFindFirst(xid, tcon, full_path, - cifs_sb, NULL, flags, &info, false); - if (rc) - return rc; - - *resp_buf = (void *)info.ntwrk_buf_start; - *data = (FILE_ALL_INFO *)info.srch_entries_start; - return 0; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb, - struct inode **inode, const char *full_path, - struct cifs_open_info_data *data, struct cifs_fattr *fattr) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct TCP_Server_Info *server = tcon->ses->server; - int rc; - - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { - if (*inode) - fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; - else - fattr->cf_uniqueid = iunique(sb, ROOT_I); - return; - } - - /* - * If we have an inode pass a NULL tcon to ensure we don't - * make a round trip to the server. This only works for SMB2+. - */ - rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path, - &fattr->cf_uniqueid, data); - if (rc) { - /* - * If that fails reuse existing ino or generate one - * and disable server ones - */ - if (*inode) - fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; - else { - fattr->cf_uniqueid = iunique(sb, ROOT_I); - cifs_autodisable_serverino(cifs_sb); - } - return; - } - - /* If no errors, check for zero root inode (invalid) */ - if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) { - cifs_dbg(FYI, "Invalid (0) inodenum\n"); - if (*inode) { - /* reuse */ - fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; - } else { - /* make an ino by hashing the UNC */ - fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO; - fattr->cf_uniqueid = simple_hashstr(tcon->tree_name); - } - } -} - -static inline bool is_inode_cache_good(struct inode *ino) -{ - return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; -} - -int cifs_get_inode_info(struct inode **inode, const char *full_path, - struct cifs_open_info_data *data, struct super_block *sb, int xid, - const struct cifs_fid *fid) -{ - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - bool adjust_tz = false; - struct cifs_fattr fattr = {0}; - bool is_reparse_point = false; - struct cifs_open_info_data tmp_data = {}; - void *smb1_backup_rsp_buf = NULL; - int rc = 0; - int tmprc = 0; - __u32 reparse_tag = 0; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - /* - * 1. Fetch file metadata if not provided (data) - */ - - if (!data) { - if (is_inode_cache_good(*inode)) { - cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); - goto out; - } - rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data, - &adjust_tz, &is_reparse_point); - data = &tmp_data; - } - - /* - * 2. Convert it to internal cifs metadata (fattr) - */ - - switch (rc) { - case 0: - /* - * If the file is a reparse point, it is more complicated - * since we have to check if its reparse tag matches a known - * special file type e.g. symlink or fifo or char etc. - */ - if (is_reparse_point && data->symlink_target) { - reparse_tag = IO_REPARSE_TAG_SYMLINK; - } else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) && - server->ops->query_reparse_tag) { - tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path, - &reparse_tag); - if (tmprc) - cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc); - if (server->ops->query_symlink) { - tmprc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, - &data->symlink_target, - is_reparse_point); - if (tmprc) - cifs_dbg(FYI, "%s: query_symlink: rc = %d\n", __func__, - tmprc); - } - } - cifs_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag); - break; - case -EREMOTE: - /* DFS link, no metadata available on this server */ - cifs_create_dfs_fattr(&fattr, sb); - rc = 0; - break; - case -EACCES: -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* - * perm errors, try again with backup flags if possible - * - * For SMB2 and later the backup intent flag - * is already sent if needed on open and there - * is no path based FindFirst operation to use - * to retry with - */ - if (backup_cred(cifs_sb) && is_smb1_server(server)) { - /* for easier reading */ - FILE_ALL_INFO *fi; - FILE_DIRECTORY_INFO *fdi; - SEARCH_ID_FULL_DIR_INFO *si; - - rc = cifs_backup_query_path_info(xid, tcon, sb, - full_path, - &smb1_backup_rsp_buf, - &fi); - if (rc) - goto out; - - move_cifs_info_to_smb2(&data->fi, fi); - fdi = (FILE_DIRECTORY_INFO *)fi; - si = (SEARCH_ID_FULL_DIR_INFO *)fi; - - cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb); - fattr.cf_uniqueid = le64_to_cpu(si->UniqueId); - /* uniqueid set, skip get inum step */ - goto handle_mnt_opt; - } else { - /* nothing we can do, bail out */ - goto out; - } -#else - goto out; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - break; - default: - cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); - goto out; - } - - /* - * 3. Get or update inode number (fattr.cf_uniqueid) - */ - - cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr); - - /* - * 4. Tweak fattr based on mount options - */ -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -handle_mnt_opt: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - /* query for SFU type info if supported and needed */ - if (fattr.cf_cifsattrs & ATTR_SYSTEM && - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { - tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid); - if (tmprc) - cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc); - } - - /* fill in 0777 bits from ACL */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) { - rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, true, - full_path, fid); - if (rc == -EREMOTE) - rc = 0; - if (rc) { - cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n", - __func__, rc); - goto out; - } - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { - rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false, - full_path, fid); - if (rc == -EREMOTE) - rc = 0; - if (rc) { - cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", - __func__, rc); - goto out; - } - } - - /* fill in remaining high mode bits e.g. SUID, VTX */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) - cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); - - /* check for Minshall+French symlinks */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { - tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, - full_path); - if (tmprc) - cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); - } - - /* - * 5. Update inode with final fattr data - */ - - if (!*inode) { - *inode = cifs_iget(sb, &fattr); - if (!*inode) - rc = -ENOMEM; - } else { - /* we already have inode, update it */ - - /* if uniqueid is different, return error */ - if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && - CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { - CIFS_I(*inode)->time = 0; /* force reval */ - rc = -ESTALE; - goto out; - } - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(*inode, &fattr); - } -out: - cifs_buf_release(smb1_backup_rsp_buf); - cifs_put_tlink(tlink); - cifs_free_open_info(&tmp_data); - kfree(fattr.cf_symlink_target); - return rc; -} - -int -smb311_posix_get_inode_info(struct inode **inode, - const char *full_path, - struct super_block *sb, unsigned int xid) -{ - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - bool adjust_tz = false; - struct cifs_fattr fattr = {0}; - bool symlink = false; - struct cifs_open_info_data data = {}; - int rc = 0; - int tmprc = 0; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - /* - * 1. Fetch file metadata - */ - - if (is_inode_cache_good(*inode)) { - cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); - goto out; - } - - rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data, &adjust_tz, - &symlink); - - /* - * 2. Convert it to internal cifs metadata (fattr) - */ - - switch (rc) { - case 0: - smb311_posix_info_to_fattr(&fattr, &data, sb, adjust_tz, symlink); - break; - case -EREMOTE: - /* DFS link, no metadata available on this server */ - cifs_create_dfs_fattr(&fattr, sb); - rc = 0; - break; - case -EACCES: - /* - * For SMB2 and later the backup intent flag - * is already sent if needed on open and there - * is no path based FindFirst operation to use - * to retry with so nothing we can do, bail out - */ - goto out; - default: - cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); - goto out; - } - - - /* - * 3. Tweak fattr based on mount options - */ - - /* check for Minshall+French symlinks */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { - tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, - full_path); - if (tmprc) - cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); - } - - /* - * 4. Update inode with final fattr data - */ - - if (!*inode) { - *inode = cifs_iget(sb, &fattr); - if (!*inode) - rc = -ENOMEM; - } else { - /* we already have inode, update it */ - - /* if uniqueid is different, return error */ - if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && - CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { - CIFS_I(*inode)->time = 0; /* force reval */ - rc = -ESTALE; - goto out; - } - - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(*inode, &fattr); - } -out: - cifs_put_tlink(tlink); - cifs_free_open_info(&data); - kfree(fattr.cf_symlink_target); - return rc; -} - - -static const struct inode_operations cifs_ipc_inode_ops = { - .lookup = cifs_lookup, -}; - -static int -cifs_find_inode(struct inode *inode, void *opaque) -{ - struct cifs_fattr *fattr = opaque; - - /* don't match inode with different uniqueid */ - if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) - return 0; - - /* use createtime like an i_generation field */ - if (CIFS_I(inode)->createtime != fattr->cf_createtime) - return 0; - - /* don't match inode of different type */ - if (inode_wrong_type(inode, fattr->cf_mode)) - return 0; - - /* if it's not a directory or has no dentries, then flag it */ - if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) - fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; - - return 1; -} - -static int -cifs_init_inode(struct inode *inode, void *opaque) -{ - struct cifs_fattr *fattr = opaque; - - CIFS_I(inode)->uniqueid = fattr->cf_uniqueid; - CIFS_I(inode)->createtime = fattr->cf_createtime; - return 0; -} - -/* - * walk dentry list for an inode and report whether it has aliases that - * are hashed. We use this to determine if a directory inode can actually - * be used. - */ -static bool -inode_has_hashed_dentries(struct inode *inode) -{ - struct dentry *dentry; - - spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { - if (!d_unhashed(dentry) || IS_ROOT(dentry)) { - spin_unlock(&inode->i_lock); - return true; - } - } - spin_unlock(&inode->i_lock); - return false; -} - -/* Given fattrs, get a corresponding inode */ -struct inode * -cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) -{ - unsigned long hash; - struct inode *inode; - -retry_iget5_locked: - cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid); - - /* hash down to 32-bits on 32-bit arch */ - hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); - - inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); - if (inode) { - /* was there a potentially problematic inode collision? */ - if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { - fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; - - if (inode_has_hashed_dentries(inode)) { - cifs_autodisable_serverino(CIFS_SB(sb)); - iput(inode); - fattr->cf_uniqueid = iunique(sb, ROOT_I); - goto retry_iget5_locked; - } - } - - /* can't fail - see cifs_find_inode() */ - cifs_fattr_to_inode(inode, fattr); - if (sb->s_flags & SB_NOATIME) - inode->i_flags |= S_NOATIME | S_NOCMTIME; - if (inode->i_state & I_NEW) { - inode->i_ino = hash; - cifs_fscache_get_inode_cookie(inode); - unlock_new_inode(inode); - } - } - - return inode; -} - -/* gets root inode */ -struct inode *cifs_root_iget(struct super_block *sb) -{ - unsigned int xid; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct inode *inode = NULL; - long rc; - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - char *path = NULL; - int len; - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) - && cifs_sb->prepath) { - len = strlen(cifs_sb->prepath); - path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL); - if (path == NULL) - return ERR_PTR(-ENOMEM); - path[0] = '/'; - memcpy(path+1, cifs_sb->prepath, len); - } else { - path = kstrdup("", GFP_KERNEL); - if (path == NULL) - return ERR_PTR(-ENOMEM); - } - - xid = get_xid(); - if (tcon->unix_ext) { - rc = cifs_get_inode_info_unix(&inode, path, sb, xid); - /* some servers mistakenly claim POSIX support */ - if (rc != -EOPNOTSUPP) - goto iget_no_retry; - cifs_dbg(VFS, "server does not support POSIX extensions\n"); - tcon->unix_ext = false; - } - - convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); - if (tcon->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, path, sb, xid); - else - rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL); - -iget_no_retry: - if (!inode) { - inode = ERR_PTR(rc); - goto out; - } - - if (rc && tcon->pipe) { - cifs_dbg(FYI, "ipc connection - fake read inode\n"); - spin_lock(&inode->i_lock); - inode->i_mode |= S_IFDIR; - set_nlink(inode, 2); - inode->i_op = &cifs_ipc_inode_ops; - inode->i_fop = &simple_dir_operations; - inode->i_uid = cifs_sb->ctx->linux_uid; - inode->i_gid = cifs_sb->ctx->linux_gid; - spin_unlock(&inode->i_lock); - } else if (rc) { - iget_failed(inode); - inode = ERR_PTR(rc); - } - -out: - kfree(path); - free_xid(xid); - return inode; -} - -int -cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid, - const char *full_path, __u32 dosattr) -{ - bool set_time = false; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct TCP_Server_Info *server; - FILE_BASIC_INFO info_buf; - - if (attrs == NULL) - return -EINVAL; - - server = cifs_sb_master_tcon(cifs_sb)->ses->server; - if (!server->ops->set_file_info) - return -ENOSYS; - - info_buf.Pad = 0; - - if (attrs->ia_valid & ATTR_ATIME) { - set_time = true; - info_buf.LastAccessTime = - cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); - } else - info_buf.LastAccessTime = 0; - - if (attrs->ia_valid & ATTR_MTIME) { - set_time = true; - info_buf.LastWriteTime = - cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); - } else - info_buf.LastWriteTime = 0; - - /* - * Samba throws this field away, but windows may actually use it. - * Do not set ctime unless other time stamps are changed explicitly - * (i.e. by utimes()) since we would then have a mix of client and - * server times. - */ - if (set_time && (attrs->ia_valid & ATTR_CTIME)) { - cifs_dbg(FYI, "CIFS - CTIME changed\n"); - info_buf.ChangeTime = - cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); - } else - info_buf.ChangeTime = 0; - - info_buf.CreationTime = 0; /* don't change */ - info_buf.Attributes = cpu_to_le32(dosattr); - - return server->ops->set_file_info(inode, full_path, &info_buf, xid); -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/* - * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit - * and rename it to a random name that hopefully won't conflict with - * anything else. - */ -int -cifs_rename_pending_delete(const char *full_path, struct dentry *dentry, - const unsigned int xid) -{ - int oplock = 0; - int rc; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - __u32 dosattr, origattr; - FILE_BASIC_INFO *info_buf = NULL; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - /* - * We cannot rename the file if the server doesn't support - * CAP_INFOLEVEL_PASSTHRU - */ - if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) { - rc = -EBUSY; - goto out; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = DELETE | FILE_WRITE_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc != 0) - goto out; - - origattr = cifsInode->cifsAttrs; - if (origattr == 0) - origattr |= ATTR_NORMAL; - - dosattr = origattr & ~ATTR_READONLY; - if (dosattr == 0) - dosattr |= ATTR_NORMAL; - dosattr |= ATTR_HIDDEN; - - /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */ - if (dosattr != origattr) { - info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); - if (info_buf == NULL) { - rc = -ENOMEM; - goto out_close; - } - info_buf->Attributes = cpu_to_le32(dosattr); - rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, - current->tgid); - /* although we would like to mark the file hidden - if that fails we will still try to rename it */ - if (!rc) - cifsInode->cifsAttrs = dosattr; - else - dosattr = origattr; /* since not able to change them */ - } - - /* rename the file */ - rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (rc != 0) { - rc = -EBUSY; - goto undo_setattr; - } - - /* try to set DELETE_ON_CLOSE */ - if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) { - rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid, - current->tgid); - /* - * some samba versions return -ENOENT when we try to set the - * file disposition here. Likely a samba bug, but work around - * it for now. This means that some cifsXXX files may hang - * around after they shouldn't. - * - * BB: remove this hack after more servers have the fix - */ - if (rc == -ENOENT) - rc = 0; - else if (rc != 0) { - rc = -EBUSY; - goto undo_rename; - } - set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags); - } - -out_close: - CIFSSMBClose(xid, tcon, fid.netfid); -out: - kfree(info_buf); - cifs_put_tlink(tlink); - return rc; - - /* - * reset everything back to the original state. Don't bother - * dealing with errors here since we can't do anything about - * them anyway. - */ -undo_rename: - CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name, - cifs_sb->local_nls, cifs_remap(cifs_sb)); -undo_setattr: - if (dosattr != origattr) { - info_buf->Attributes = cpu_to_le32(origattr); - if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, - current->tgid)) - cifsInode->cifsAttrs = origattr; - } - - goto out_close; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* copied from fs/nfs/dir.c with small changes */ -static void -cifs_drop_nlink(struct inode *inode) -{ - spin_lock(&inode->i_lock); - if (inode->i_nlink > 0) - drop_nlink(inode); - spin_unlock(&inode->i_lock); -} - -/* - * If d_inode(dentry) is null (usually meaning the cached dentry - * is a negative dentry) then we would attempt a standard SMB delete, but - * if that fails we can not attempt the fall back mechanisms on EACCES - * but will return the EACCES to the caller. Note that the VFS does not call - * unlink on negative dentries currently. - */ -int cifs_unlink(struct inode *dir, struct dentry *dentry) -{ - int rc = 0; - unsigned int xid; - const char *full_path; - void *page; - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cifs_inode; - struct super_block *sb = dir->i_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct iattr *attrs = NULL; - __u32 dosattr = 0, origattr = 0; - - cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry); - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - xid = get_xid(); - page = alloc_dentry_path(); - - if (tcon->nodelete) { - rc = -EACCES; - goto unlink_out; - } - - /* Unlink can be called from rename so we can not take the - * sb->s_vfs_rename_mutex here */ - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto unlink_out; - } - - cifs_close_deferred_file_under_dentry(tcon, full_path); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - rc = CIFSPOSIXDelFile(xid, tcon, full_path, - SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_dbg(FYI, "posix del rc %d\n", rc); - if ((rc == 0) || (rc == -ENOENT)) - goto psx_del_no_retry; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -retry_std_delete: - if (!server->ops->unlink) { - rc = -ENOSYS; - goto psx_del_no_retry; - } - - rc = server->ops->unlink(xid, tcon, full_path, cifs_sb); - -psx_del_no_retry: - if (!rc) { - if (inode) - cifs_drop_nlink(inode); - } else if (rc == -ENOENT) { - d_drop(dentry); - } else if (rc == -EBUSY) { - if (server->ops->rename_pending_delete) { - rc = server->ops->rename_pending_delete(full_path, - dentry, xid); - if (rc == 0) - cifs_drop_nlink(inode); - } - } else if ((rc == -EACCES) && (dosattr == 0) && inode) { - attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); - if (attrs == NULL) { - rc = -ENOMEM; - goto out_reval; - } - - /* try to reset dos attributes */ - cifs_inode = CIFS_I(inode); - origattr = cifs_inode->cifsAttrs; - if (origattr == 0) - origattr |= ATTR_NORMAL; - dosattr = origattr & ~ATTR_READONLY; - if (dosattr == 0) - dosattr |= ATTR_NORMAL; - dosattr |= ATTR_HIDDEN; - - rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); - if (rc != 0) - goto out_reval; - - goto retry_std_delete; - } - - /* undo the setattr if we errored out and it's needed */ - if (rc != 0 && dosattr != 0) - cifs_set_file_info(inode, attrs, xid, full_path, origattr); - -out_reval: - if (inode) { - cifs_inode = CIFS_I(inode); - cifs_inode->time = 0; /* will force revalidate to get info - when needed */ - inode->i_ctime = current_time(inode); - } - dir->i_ctime = dir->i_mtime = current_time(dir); - cifs_inode = CIFS_I(dir); - CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ -unlink_out: - free_dentry_path(page); - kfree(attrs); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static int -cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, - const char *full_path, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, const unsigned int xid) -{ - int rc = 0; - struct inode *inode = NULL; - - if (tcon->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - else if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, - xid); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - else - rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb, - xid, NULL); - - if (rc) - return rc; - - if (!S_ISDIR(inode->i_mode)) { - /* - * mkdir succeeded, but another client has managed to remove the - * sucker and replace it with non-directory. Return success, - * but don't leave the child in dcache. - */ - iput(inode); - d_drop(dentry); - return 0; - } - /* - * setting nlink not necessary except in cases where we failed to get it - * from the server or was set bogus. Also, since this is a brand new - * inode, no need to grab the i_lock before setting the i_nlink. - */ - if (inode->i_nlink < 2) - set_nlink(inode, 2); - mode &= ~current_umask(); - /* must turn on setgid bit if parent dir has it */ - if (parent->i_mode & S_ISGID) - mode |= S_ISGID; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext) { - struct cifs_unix_set_info_args args = { - .mode = mode, - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = 0, - }; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = current_fsuid(); - if (parent->i_mode & S_ISGID) - args.gid = parent->i_gid; - else - args.gid = current_fsgid(); - } else { - args.uid = INVALID_UID; /* no change */ - args.gid = INVALID_GID; /* no change */ - } - CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - } else { -#else - { -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - struct TCP_Server_Info *server = tcon->ses->server; - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && - (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo) - server->ops->mkdir_setinfo(inode, full_path, cifs_sb, - tcon, xid); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) - inode->i_mode = (mode | S_IFDIR); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - inode->i_uid = current_fsuid(); - if (inode->i_mode & S_ISGID) - inode->i_gid = parent->i_gid; - else - inode->i_gid = current_fsgid(); - } - } - d_instantiate(dentry, inode); - return 0; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode, - const char *full_path, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, const unsigned int xid) -{ - int rc = 0; - u32 oplock = 0; - FILE_UNIX_BASIC_INFO *info = NULL; - struct inode *newinode = NULL; - struct cifs_fattr fattr; - - info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); - if (info == NULL) { - rc = -ENOMEM; - goto posix_mkdir_out; - } - - mode &= ~current_umask(); - rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, - NULL /* netfid */, info, &oplock, full_path, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - if (rc == -EOPNOTSUPP) - goto posix_mkdir_out; - else if (rc) { - cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc); - d_drop(dentry); - goto posix_mkdir_out; - } - - if (info->Type == cpu_to_le32(-1)) - /* no return info, go query for it */ - goto posix_mkdir_get_info; - /* - * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if - * need to set uid/gid. - */ - - cifs_unix_basic_to_fattr(&fattr, info, cifs_sb); - cifs_fill_uniqueid(inode->i_sb, &fattr); - newinode = cifs_iget(inode->i_sb, &fattr); - if (!newinode) - goto posix_mkdir_get_info; - - d_instantiate(dentry, newinode); - -#ifdef CONFIG_CIFS_DEBUG2 - cifs_dbg(FYI, "instantiated dentry %p %pd to inode %p\n", - dentry, dentry, newinode); - - if (newinode->i_nlink != 2) - cifs_dbg(FYI, "unexpected number of links %d\n", - newinode->i_nlink); -#endif - -posix_mkdir_out: - kfree(info); - return rc; -posix_mkdir_get_info: - rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon, - xid); - goto posix_mkdir_out; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, umode_t mode) -{ - int rc = 0; - unsigned int xid; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - const char *full_path; - void *page; - - cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", - mode, inode); - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - xid = get_xid(); - - page = alloc_dentry_path(); - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto mkdir_out; - } - - server = tcon->ses->server; - - if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { - rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, - cifs_sb); - d_drop(direntry); /* for time being always refresh inode info */ - goto mkdir_out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, - tcon, xid); - if (rc != -EOPNOTSUPP) - goto mkdir_out; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (!server->ops->mkdir) { - rc = -ENOSYS; - goto mkdir_out; - } - - /* BB add setting the equivalent of mode via CreateX w/ACLs */ - rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb); - if (rc) { - cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc); - d_drop(direntry); - goto mkdir_out; - } - - /* TODO: skip this for smb2/smb3 */ - rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, - xid); -mkdir_out: - /* - * Force revalidate to get parent dir info when needed since cached - * attributes are invalid now. - */ - CIFS_I(inode)->time = 0; - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -int cifs_rmdir(struct inode *inode, struct dentry *direntry) -{ - int rc = 0; - unsigned int xid; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - const char *full_path; - void *page = alloc_dentry_path(); - struct cifsInodeInfo *cifsInode; - - cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode); - - xid = get_xid(); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto rmdir_exit; - } - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) { - rc = -EIO; - goto rmdir_exit; - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto rmdir_exit; - } - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - if (!server->ops->rmdir) { - rc = -ENOSYS; - cifs_put_tlink(tlink); - goto rmdir_exit; - } - - if (tcon->nodelete) { - rc = -EACCES; - cifs_put_tlink(tlink); - goto rmdir_exit; - } - - rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb); - cifs_put_tlink(tlink); - - if (!rc) { - spin_lock(&d_inode(direntry)->i_lock); - i_size_write(d_inode(direntry), 0); - clear_nlink(d_inode(direntry)); - spin_unlock(&d_inode(direntry)->i_lock); - } - - cifsInode = CIFS_I(d_inode(direntry)); - /* force revalidate to go get info when needed */ - cifsInode->time = 0; - - cifsInode = CIFS_I(inode); - /* - * Force revalidate to get parent dir info when needed since cached - * attributes are invalid now. - */ - cifsInode->time = 0; - - d_inode(direntry)->i_ctime = inode->i_ctime = inode->i_mtime = - current_time(inode); - -rmdir_exit: - free_dentry_path(page); - free_xid(xid); - return rc; -} - -static int -cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, - const char *from_path, struct dentry *to_dentry, - const char *to_path) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_fid fid; - struct cifs_open_parms oparms; - int oplock; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - int rc; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - if (!server->ops->rename) - return -ENOSYS; - - /* try path-based rename first */ - rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb); - - /* - * Don't bother with rename by filehandle unless file is busy and - * source. Note that cross directory moves do not work with - * rename by filehandle to various Windows servers. - */ - if (rc == 0 || rc != -EBUSY) - goto do_rename_exit; - - /* Don't fall back to using SMB on SMB 2+ mount */ - if (server->vals->protocol_id != 0) - goto do_rename_exit; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* open-file renames don't work across directories */ - if (to_dentry->d_parent != from_dentry->d_parent) - goto do_rename_exit; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - /* open the file to be renamed -- we need DELETE perms */ - .desired_access = DELETE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = from_path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc == 0) { - rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, - (const char *) to_dentry->d_name.name, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - CIFSSMBClose(xid, tcon, fid.netfid); - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ -do_rename_exit: - if (rc == 0) - d_move(from_dentry, to_dentry); - cifs_put_tlink(tlink); - return rc; -} - -int -cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, - struct dentry *source_dentry, struct inode *target_dir, - struct dentry *target_dentry, unsigned int flags) -{ - const char *from_name, *to_name; - void *page1, *page2; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - unsigned int xid; - int rc, tmprc; - int retry_count = 0; - FILE_UNIX_BASIC_INFO *info_buf_source = NULL; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - FILE_UNIX_BASIC_INFO *info_buf_target; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (flags & ~RENAME_NOREPLACE) - return -EINVAL; - - cifs_sb = CIFS_SB(source_dir->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - page1 = alloc_dentry_path(); - page2 = alloc_dentry_path(); - xid = get_xid(); - - from_name = build_path_from_dentry(source_dentry, page1); - if (IS_ERR(from_name)) { - rc = PTR_ERR(from_name); - goto cifs_rename_exit; - } - - to_name = build_path_from_dentry(target_dentry, page2); - if (IS_ERR(to_name)) { - rc = PTR_ERR(to_name); - goto cifs_rename_exit; - } - - cifs_close_deferred_file_under_dentry(tcon, from_name); - if (d_inode(target_dentry) != NULL) - cifs_close_deferred_file_under_dentry(tcon, to_name); - - rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, - to_name); - - if (rc == -EACCES) { - while (retry_count < 3) { - cifs_close_all_deferred_files(tcon); - rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, - to_name); - if (rc != -EACCES) - break; - retry_count++; - } - } - - /* - * No-replace is the natural behavior for CIFS, so skip unlink hacks. - */ - if (flags & RENAME_NOREPLACE) - goto cifs_rename_exit; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (rc == -EEXIST && tcon->unix_ext) { - /* - * Are src and dst hardlinks of same inode? We can only tell - * with unix extensions enabled. - */ - info_buf_source = - kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO), - GFP_KERNEL); - if (info_buf_source == NULL) { - rc = -ENOMEM; - goto cifs_rename_exit; - } - - info_buf_target = info_buf_source + 1; - tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name, - info_buf_source, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (tmprc != 0) - goto unlink_target; - - tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name, - info_buf_target, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - - if (tmprc == 0 && (info_buf_source->UniqueId == - info_buf_target->UniqueId)) { - /* same file, POSIX says that this is a noop */ - rc = 0; - goto cifs_rename_exit; - } - } - /* - * else ... BB we could add the same check for Windows by - * checking the UniqueId via FILE_INTERNAL_INFO - */ - -unlink_target: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - /* Try unlinking the target dentry if it's not negative */ - if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) { - if (d_is_dir(target_dentry)) - tmprc = cifs_rmdir(target_dir, target_dentry); - else - tmprc = cifs_unlink(target_dir, target_dentry); - if (tmprc) - goto cifs_rename_exit; - rc = cifs_do_rename(xid, source_dentry, from_name, - target_dentry, to_name); - } - - /* force revalidate to go get info when needed */ - CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; - - source_dir->i_ctime = source_dir->i_mtime = target_dir->i_ctime = - target_dir->i_mtime = current_time(source_dir); - -cifs_rename_exit: - kfree(info_buf_source); - free_dentry_path(page2); - free_dentry_path(page1); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static bool -cifs_dentry_needs_reval(struct dentry *dentry) -{ - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct cached_fid *cfid = NULL; - - if (cifs_i->time == 0) - return true; - - if (CIFS_CACHE_READ(cifs_i)) - return false; - - if (!lookupCacheEnabled) - return true; - - if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { - spin_lock(&cfid->fid_lock); - if (cfid->time && cifs_i->time > cfid->time) { - spin_unlock(&cfid->fid_lock); - close_cached_dir(cfid); - return false; - } - spin_unlock(&cfid->fid_lock); - close_cached_dir(cfid); - } - /* - * depending on inode type, check if attribute caching disabled for - * files or directories - */ - if (S_ISDIR(inode->i_mode)) { - if (!cifs_sb->ctx->acdirmax) - return true; - if (!time_in_range(jiffies, cifs_i->time, - cifs_i->time + cifs_sb->ctx->acdirmax)) - return true; - } else { /* file */ - if (!cifs_sb->ctx->acregmax) - return true; - if (!time_in_range(jiffies, cifs_i->time, - cifs_i->time + cifs_sb->ctx->acregmax)) - return true; - } - - /* hardlinked files w/ noserverino get "special" treatment */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && - S_ISREG(inode->i_mode) && inode->i_nlink != 1) - return true; - - return false; -} - -/* - * Zap the cache. Called when invalid_mapping flag is set. - */ -int -cifs_invalidate_mapping(struct inode *inode) -{ - int rc = 0; - - if (inode->i_mapping && inode->i_mapping->nrpages != 0) { - rc = invalidate_inode_pages2(inode->i_mapping); - if (rc) - cifs_dbg(VFS, "%s: Could not invalidate inode %p\n", - __func__, inode); - } - - return rc; -} - -/** - * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks - * - * @key: currently unused - * @mode: the task state to sleep in - */ -static int -cifs_wait_bit_killable(struct wait_bit_key *key, int mode) -{ - schedule(); - if (signal_pending_state(mode, current)) - return -ERESTARTSYS; - return 0; -} - -int -cifs_revalidate_mapping(struct inode *inode) -{ - int rc; - unsigned long *flags = &CIFS_I(inode)->flags; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - /* swapfiles are not supposed to be shared */ - if (IS_SWAPFILE(inode)) - return 0; - - rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, - TASK_KILLABLE|TASK_FREEZABLE_UNSAFE); - if (rc) - return rc; - - if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { - /* for cache=singleclient, do not invalidate */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) - goto skip_invalidate; - - rc = cifs_invalidate_mapping(inode); - if (rc) - set_bit(CIFS_INO_INVALID_MAPPING, flags); - } - -skip_invalidate: - clear_bit_unlock(CIFS_INO_LOCK, flags); - smp_mb__after_atomic(); - wake_up_bit(flags, CIFS_INO_LOCK); - - return rc; -} - -int -cifs_zap_mapping(struct inode *inode) -{ - set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); - return cifs_revalidate_mapping(inode); -} - -int cifs_revalidate_file_attr(struct file *filp) -{ - int rc = 0; - struct dentry *dentry = file_dentry(filp); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (!cifs_dentry_needs_reval(dentry)) - return rc; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tlink_tcon(cfile->tlink)->unix_ext) - rc = cifs_get_file_info_unix(filp); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = cifs_get_file_info(filp); - - return rc; -} - -int cifs_revalidate_dentry_attr(struct dentry *dentry) -{ - unsigned int xid; - int rc = 0; - struct inode *inode = d_inode(dentry); - struct super_block *sb = dentry->d_sb; - const char *full_path; - void *page; - int count = 0; - - if (inode == NULL) - return -ENOENT; - - if (!cifs_dentry_needs_reval(dentry)) - return rc; - - xid = get_xid(); - - page = alloc_dentry_path(); - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n", - full_path, inode, inode->i_count.counter, - dentry, cifs_get_time(dentry), jiffies); - -again: - if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid); - else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); - else - rc = cifs_get_inode_info(&inode, full_path, NULL, sb, - xid, NULL); - if (rc == -EAGAIN && count++ < 10) - goto again; -out: - free_dentry_path(page); - free_xid(xid); - - return rc; -} - -int cifs_revalidate_file(struct file *filp) -{ - int rc; - struct inode *inode = file_inode(filp); - - rc = cifs_revalidate_file_attr(filp); - if (rc) - return rc; - - return cifs_revalidate_mapping(inode); -} - -/* revalidate a dentry's inode attributes */ -int cifs_revalidate_dentry(struct dentry *dentry) -{ - int rc; - struct inode *inode = d_inode(dentry); - - rc = cifs_revalidate_dentry_attr(dentry); - if (rc) - return rc; - - return cifs_revalidate_mapping(inode); -} - -int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path, - struct kstat *stat, u32 request_mask, unsigned int flags) -{ - struct dentry *dentry = path->dentry; - struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct inode *inode = d_inode(dentry); - int rc; - - if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) - return -EIO; - - /* - * We need to be sure that all dirty pages are written and the server - * has actual ctime, mtime and file length. - */ - if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE | STATX_BLOCKS)) && - !CIFS_CACHE_READ(CIFS_I(inode)) && - inode->i_mapping && inode->i_mapping->nrpages != 0) { - rc = filemap_fdatawait(inode->i_mapping); - if (rc) { - mapping_set_error(inode->i_mapping, rc); - return rc; - } - } - - if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC) - CIFS_I(inode)->time = 0; /* force revalidate */ - - /* - * If the caller doesn't require syncing, only sync if - * necessary (e.g. due to earlier truncate or setattr - * invalidating the cached metadata) - */ - if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) || - (CIFS_I(inode)->time == 0)) { - rc = cifs_revalidate_dentry_attr(dentry); - if (rc) - return rc; - } - - generic_fillattr(&init_user_ns, inode, stat); - stat->blksize = cifs_sb->ctx->bsize; - stat->ino = CIFS_I(inode)->uniqueid; - - /* old CIFS Unix Extensions doesn't return create time */ - if (CIFS_I(inode)->createtime) { - stat->result_mask |= STATX_BTIME; - stat->btime = - cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime)); - } - - stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED); - if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED) - stat->attributes |= STATX_ATTR_COMPRESSED; - if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED) - stat->attributes |= STATX_ATTR_ENCRYPTED; - - /* - * If on a multiuser mount without unix extensions or cifsacl being - * enabled, and the admin hasn't overridden them, set the ownership - * to the fsuid/fsgid of the current process. - */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && - !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && - !tcon->unix_ext) { - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) - stat->uid = current_fsuid(); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) - stat->gid = current_fsgid(); - } - return 0; -} - -int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, - u64 len) -{ - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->netfs.inode.i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct TCP_Server_Info *server = tcon->ses->server; - struct cifsFileInfo *cfile; - int rc; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - /* - * We need to be sure that all dirty pages are written as they - * might fill holes on the server. - */ - if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && - inode->i_mapping->nrpages != 0) { - rc = filemap_fdatawait(inode->i_mapping); - if (rc) { - mapping_set_error(inode->i_mapping, rc); - return rc; - } - } - - cfile = find_readable_file(cifs_i, false); - if (cfile == NULL) - return -EINVAL; - - if (server->ops->fiemap) { - rc = server->ops->fiemap(tcon, cfile, fei, start, len); - cifsFileInfo_put(cfile); - return rc; - } - - cifsFileInfo_put(cfile); - return -ENOTSUPP; -} - -int cifs_truncate_page(struct address_space *mapping, loff_t from) -{ - pgoff_t index = from >> PAGE_SHIFT; - unsigned offset = from & (PAGE_SIZE - 1); - struct page *page; - int rc = 0; - - page = grab_cache_page(mapping, index); - if (!page) - return -ENOMEM; - - zero_user_segment(page, offset, PAGE_SIZE); - unlock_page(page); - put_page(page); - return rc; -} - -void cifs_setsize(struct inode *inode, loff_t offset) -{ - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - - spin_lock(&inode->i_lock); - i_size_write(inode, offset); - spin_unlock(&inode->i_lock); - - /* Cached inode must be refreshed on truncate */ - cifs_i->time = 0; - truncate_pagecache(inode, offset); -} - -static int -cifs_set_file_size(struct inode *inode, struct iattr *attrs, - unsigned int xid, const char *full_path) -{ - int rc; - struct cifsFileInfo *open_file; - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon = NULL; - struct TCP_Server_Info *server; - - /* - * To avoid spurious oplock breaks from server, in the case of - * inodes that we already have open, avoid doing path based - * setting of file size if we can do it by handle. - * This keeps our caching token (oplock) and avoids timeouts - * when the local oplock break takes longer to flush - * writebehind data than the SMB timeout for the SetPathInfo - * request would allow - */ - open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); - if (open_file) { - tcon = tlink_tcon(open_file->tlink); - server = tcon->ses->server; - if (server->ops->set_file_size) - rc = server->ops->set_file_size(xid, tcon, open_file, - attrs->ia_size, false); - else - rc = -ENOSYS; - cifsFileInfo_put(open_file); - cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc); - } else - rc = -EINVAL; - - if (!rc) - goto set_size_out; - - if (tcon == NULL) { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - } - - /* - * Set file size by pathname rather than by handle either because no - * valid, writeable file handle for it was found or because there was - * an error setting it by handle. - */ - if (server->ops->set_path_size) - rc = server->ops->set_path_size(xid, tcon, full_path, - attrs->ia_size, cifs_sb, false); - else - rc = -ENOSYS; - cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); - - if (tlink) - cifs_put_tlink(tlink); - -set_size_out: - if (rc == 0) { - cifsInode->server_eof = attrs->ia_size; - cifs_setsize(inode, attrs->ia_size); - /* - * i_blocks is not related to (i_size / i_blksize), but instead - * 512 byte (2**9) size is required for calculating num blocks. - * Until we can query the server for actual allocation size, - * this is best estimate we have for blocks allocated for a file - * Number of blocks must be rounded up so size 1 is not 0 blocks - */ - inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9; - - /* - * The man page of truncate says if the size changed, - * then the st_ctime and st_mtime fields for the file - * are updated. - */ - attrs->ia_ctime = attrs->ia_mtime = current_time(inode); - attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME; - - cifs_truncate_page(inode->i_mapping, inode->i_size); - } - - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) -{ - int rc; - unsigned int xid; - const char *full_path; - void *page = alloc_dentry_path(); - struct inode *inode = d_inode(direntry); - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - struct cifs_unix_set_info_args *args = NULL; - struct cifsFileInfo *open_file; - - cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n", - direntry, attrs->ia_valid); - - xid = get_xid(); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) - attrs->ia_valid |= ATTR_FORCE; - - rc = setattr_prepare(&init_user_ns, direntry, attrs); - if (rc < 0) - goto out; - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - /* - * Attempt to flush data before changing attributes. We need to do - * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the - * ownership or mode then we may also need to do this. Here, we take - * the safe way out and just do the flush on all setattr requests. If - * the flush returns error, store it to report later and continue. - * - * BB: This should be smarter. Why bother flushing pages that - * will be truncated anyway? Also, should we error out here if - * the flush returns error? - */ - rc = filemap_write_and_wait(inode->i_mapping); - if (is_interrupt_error(rc)) { - rc = -ERESTARTSYS; - goto out; - } - - mapping_set_error(inode->i_mapping, rc); - rc = 0; - - if (attrs->ia_valid & ATTR_SIZE) { - rc = cifs_set_file_size(inode, attrs, xid, full_path); - if (rc != 0) - goto out; - } - - /* skip mode change if it's just for clearing setuid/setgid */ - if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) - attrs->ia_valid &= ~ATTR_MODE; - - args = kmalloc(sizeof(*args), GFP_KERNEL); - if (args == NULL) { - rc = -ENOMEM; - goto out; - } - - /* set up the struct */ - if (attrs->ia_valid & ATTR_MODE) - args->mode = attrs->ia_mode; - else - args->mode = NO_CHANGE_64; - - if (attrs->ia_valid & ATTR_UID) - args->uid = attrs->ia_uid; - else - args->uid = INVALID_UID; /* no change */ - - if (attrs->ia_valid & ATTR_GID) - args->gid = attrs->ia_gid; - else - args->gid = INVALID_GID; /* no change */ - - if (attrs->ia_valid & ATTR_ATIME) - args->atime = cifs_UnixTimeToNT(attrs->ia_atime); - else - args->atime = NO_CHANGE_64; - - if (attrs->ia_valid & ATTR_MTIME) - args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime); - else - args->mtime = NO_CHANGE_64; - - if (attrs->ia_valid & ATTR_CTIME) - args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime); - else - args->ctime = NO_CHANGE_64; - - args->device = 0; - open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); - if (open_file) { - u16 nfid = open_file->fid.netfid; - u32 npid = open_file->pid; - pTcon = tlink_tcon(open_file->tlink); - rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); - cifsFileInfo_put(open_file); - } else { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto out; - } - pTcon = tlink_tcon(tlink); - rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_put_tlink(tlink); - } - - if (rc) - goto out; - - if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size != i_size_read(inode)) { - truncate_setsize(inode, attrs->ia_size); - fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); - } - - setattr_copy(&init_user_ns, inode, attrs); - mark_inode_dirty(inode); - - /* force revalidate when any of these times are set since some - of the fs types (eg ext3, fat) do not have fine enough - time granularity to match protocol, and we do not have a - a way (yet) to query the server fs's time granularity (and - whether it rounds times down). - */ - if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME)) - cifsInode->time = 0; -out: - kfree(args); - free_dentry_path(page); - free_xid(xid); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) -{ - unsigned int xid; - kuid_t uid = INVALID_UID; - kgid_t gid = INVALID_GID; - struct inode *inode = d_inode(direntry); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifsFileInfo *wfile; - struct cifs_tcon *tcon; - const char *full_path; - void *page = alloc_dentry_path(); - int rc = -EACCES; - __u32 dosattr = 0; - __u64 mode = NO_CHANGE_64; - - xid = get_xid(); - - cifs_dbg(FYI, "setattr on file %pd attrs->ia_valid 0x%x\n", - direntry, attrs->ia_valid); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) - attrs->ia_valid |= ATTR_FORCE; - - rc = setattr_prepare(&init_user_ns, direntry, attrs); - if (rc < 0) - goto cifs_setattr_exit; - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto cifs_setattr_exit; - } - - /* - * Attempt to flush data before changing attributes. We need to do - * this for ATTR_SIZE and ATTR_MTIME. If the flush of the data - * returns error, store it to report later and continue. - * - * BB: This should be smarter. Why bother flushing pages that - * will be truncated anyway? Also, should we error out here if - * the flush returns error? Do we need to check for ATTR_MTIME_SET flag? - */ - if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) { - rc = filemap_write_and_wait(inode->i_mapping); - if (is_interrupt_error(rc)) { - rc = -ERESTARTSYS; - goto cifs_setattr_exit; - } - mapping_set_error(inode->i_mapping, rc); - } - - rc = 0; - - if ((attrs->ia_valid & ATTR_MTIME) && - !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { - rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile); - if (!rc) { - tcon = tlink_tcon(wfile->tlink); - rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid); - cifsFileInfo_put(wfile); - if (rc) - goto cifs_setattr_exit; - } else if (rc != -EBADF) - goto cifs_setattr_exit; - else - rc = 0; - } - - if (attrs->ia_valid & ATTR_SIZE) { - rc = cifs_set_file_size(inode, attrs, xid, full_path); - if (rc != 0) - goto cifs_setattr_exit; - } - - if (attrs->ia_valid & ATTR_UID) - uid = attrs->ia_uid; - - if (attrs->ia_valid & ATTR_GID) - gid = attrs->ia_gid; - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { - if (uid_valid(uid) || gid_valid(gid)) { - mode = NO_CHANGE_64; - rc = id_mode_to_cifs_acl(inode, full_path, &mode, - uid, gid); - if (rc) { - cifs_dbg(FYI, "%s: Setting id failed with error: %d\n", - __func__, rc); - goto cifs_setattr_exit; - } - } - } else - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) - attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); - - /* skip mode change if it's just for clearing setuid/setgid */ - if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) - attrs->ia_valid &= ~ATTR_MODE; - - if (attrs->ia_valid & ATTR_MODE) { - mode = attrs->ia_mode; - rc = 0; - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { - rc = id_mode_to_cifs_acl(inode, full_path, &mode, - INVALID_UID, INVALID_GID); - if (rc) { - cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n", - __func__, rc); - goto cifs_setattr_exit; - } - - /* - * In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes. - * Pick up the actual mode bits that were set. - */ - if (mode != attrs->ia_mode) - attrs->ia_mode = mode; - } else - if (((mode & S_IWUGO) == 0) && - (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { - - dosattr = cifsInode->cifsAttrs | ATTR_READONLY; - - /* fix up mode if we're not using dynperm */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) - attrs->ia_mode = inode->i_mode & ~S_IWUGO; - } else if ((mode & S_IWUGO) && - (cifsInode->cifsAttrs & ATTR_READONLY)) { - - dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; - /* Attributes of 0 are ignored */ - if (dosattr == 0) - dosattr |= ATTR_NORMAL; - - /* reset local inode permissions to normal */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { - attrs->ia_mode &= ~(S_IALLUGO); - if (S_ISDIR(inode->i_mode)) - attrs->ia_mode |= - cifs_sb->ctx->dir_mode; - else - attrs->ia_mode |= - cifs_sb->ctx->file_mode; - } - } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { - /* ignore mode change - ATTR_READONLY hasn't changed */ - attrs->ia_valid &= ~ATTR_MODE; - } - } - - if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) || - ((attrs->ia_valid & ATTR_MODE) && dosattr)) { - rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); - /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */ - - /* Even if error on time set, no sense failing the call if - the server would set the time to a reasonable value anyway, - and this check ensures that we are not being called from - sys_utimes in which case we ought to fail the call back to - the user when the server rejects the call */ - if ((rc) && (attrs->ia_valid & - (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) - rc = 0; - } - - /* do not need local check to inode_check_ok since the server does - that */ - if (rc) - goto cifs_setattr_exit; - - if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size != i_size_read(inode)) { - truncate_setsize(inode, attrs->ia_size); - fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); - } - - setattr_copy(&init_user_ns, inode, attrs); - mark_inode_dirty(inode); - -cifs_setattr_exit: - free_xid(xid); - free_dentry_path(page); - return rc; -} - -int -cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry, - struct iattr *attrs) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - int rc, retries = 0; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - do { -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (pTcon->unix_ext) - rc = cifs_setattr_unix(direntry, attrs); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = cifs_setattr_nounix(direntry, attrs); - retries++; - } while (is_retryable_error(rc) && retries < 2); - - /* BB: add cifs_setattr_legacy for really old servers */ - return rc; -} diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c deleted file mode 100644 index 6419ec47c2a8..000000000000 --- a/fs/cifs/ioctl.c +++ /dev/null @@ -1,526 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * vfs operations that deal with io control - * - * Copyright (C) International Business Machines Corp., 2005,2013 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifsfs.h" -#include "cifs_ioctl.h" -#include "smb2proto.h" -#include "smb2glob.h" -#include - -static long cifs_ioctl_query_info(unsigned int xid, struct file *filep, - unsigned long p) -{ - struct inode *inode = file_inode(filep); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct dentry *dentry = filep->f_path.dentry; - const unsigned char *path; - void *page = alloc_dentry_path(); - __le16 *utf16_path = NULL, root_path; - int rc = 0; - - path = build_path_from_dentry(dentry, page); - if (IS_ERR(path)) { - free_dentry_path(page); - return PTR_ERR(path); - } - - cifs_dbg(FYI, "%s %s\n", __func__, path); - - if (!path[0]) { - root_path = 0; - utf16_path = &root_path; - } else { - utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - goto ici_exit; - } - } - - if (tcon->ses->server->ops->ioctl_query_info) - rc = tcon->ses->server->ops->ioctl_query_info( - xid, tcon, cifs_sb, utf16_path, - filep->private_data ? 0 : 1, p); - else - rc = -EOPNOTSUPP; - - ici_exit: - if (utf16_path != &root_path) - kfree(utf16_path); - free_dentry_path(page); - return rc; -} - -static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, - unsigned long srcfd) -{ - int rc; - struct fd src_file; - struct inode *src_inode; - - cifs_dbg(FYI, "ioctl copychunk range\n"); - /* the destination must be opened for writing */ - if (!(dst_file->f_mode & FMODE_WRITE)) { - cifs_dbg(FYI, "file target not open for write\n"); - return -EINVAL; - } - - /* check if target volume is readonly and take reference */ - rc = mnt_want_write_file(dst_file); - if (rc) { - cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); - return rc; - } - - src_file = fdget(srcfd); - if (!src_file.file) { - rc = -EBADF; - goto out_drop_write; - } - - if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) { - rc = -EBADF; - cifs_dbg(VFS, "src file seems to be from a different filesystem type\n"); - goto out_fput; - } - - src_inode = file_inode(src_file.file); - rc = -EINVAL; - if (S_ISDIR(src_inode->i_mode)) - goto out_fput; - - rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0, - src_inode->i_size, 0); - if (rc > 0) - rc = 0; -out_fput: - fdput(src_file); -out_drop_write: - mnt_drop_write_file(dst_file); - return rc; -} - -static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, - void __user *arg) -{ - int rc = 0; - struct smb_mnt_fs_info *fsinf; - - fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL); - if (fsinf == NULL) - return -ENOMEM; - - fsinf->version = 1; - fsinf->protocol_id = tcon->ses->server->vals->protocol_id; - fsinf->device_characteristics = - le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics); - fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); - fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes); - fsinf->max_path_component = - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); - fsinf->vol_serial_number = tcon->vol_serial_number; - fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time); - fsinf->share_flags = tcon->share_flags; - fsinf->share_caps = le32_to_cpu(tcon->capabilities); - fsinf->sector_flags = tcon->ss_flags; - fsinf->optimal_sector_size = tcon->perf_sector_size; - fsinf->max_bytes_chunk = tcon->max_bytes_chunk; - fsinf->maximal_access = tcon->maximal_access; - fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability); - - if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info))) - rc = -EFAULT; - - kfree(fsinf); - return rc; -} - -static int cifs_shutdown(struct super_block *sb, unsigned long arg) -{ - struct cifs_sb_info *sbi = CIFS_SB(sb); - __u32 flags; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (get_user(flags, (__u32 __user *)arg)) - return -EFAULT; - - if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH) - return -EINVAL; - - if (cifs_forced_shutdown(sbi)) - return 0; - - cifs_dbg(VFS, "shut down requested (%d)", flags); -/* trace_cifs_shutdown(sb, flags);*/ - - /* - * see: - * https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html - * for more information and description of original intent of the flags - */ - switch (flags) { - /* - * We could add support later for default flag which requires: - * "Flush all dirty data and metadata to disk" - * would need to call syncfs or equivalent to flush page cache for - * the mount and then issue fsync to server (if nostrictsync not set) - */ - case CIFS_GOING_FLAGS_DEFAULT: - cifs_dbg(FYI, "shutdown with default flag not supported\n"); - return -EINVAL; - /* - * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not - * data) but metadata writes are not cached on the client, so can treat - * it similarly to NOLOGFLUSH - */ - case CIFS_GOING_FLAGS_LOGFLUSH: - case CIFS_GOING_FLAGS_NOLOGFLUSH: - sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN; - return 0; - default: - return -EINVAL; - } - return 0; -} - -static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in) -{ - struct smb3_full_key_debug_info out; - struct cifs_ses *ses; - int rc = 0; - bool found = false; - u8 __user *end; - - if (!smb3_encryption_required(tcon)) { - rc = -EOPNOTSUPP; - goto out; - } - - /* copy user input into our output buffer */ - if (copy_from_user(&out, in, sizeof(out))) { - rc = -EINVAL; - goto out; - } - - if (!out.session_id) { - /* if ses id is 0, use current user session */ - ses = tcon->ses; - } else { - /* otherwise if a session id is given, look for it in all our sessions */ - struct cifs_ses *ses_it = NULL; - struct TCP_Server_Info *server_it = NULL; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) { - list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) { - if (ses_it->Suid == out.session_id) { - ses = ses_it; - /* - * since we are using the session outside the crit - * section, we need to make sure it won't be released - * so increment its refcount - */ - ses->ses_count++; - found = true; - goto search_end; - } - } - } -search_end: - spin_unlock(&cifs_tcp_ses_lock); - if (!found) { - rc = -ENOENT; - goto out; - } - } - - switch (ses->server->cipher_type) { - case SMB2_ENCRYPTION_AES128_CCM: - case SMB2_ENCRYPTION_AES128_GCM: - out.session_key_length = CIFS_SESS_KEY_SIZE; - out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE; - break; - case SMB2_ENCRYPTION_AES256_CCM: - case SMB2_ENCRYPTION_AES256_GCM: - out.session_key_length = CIFS_SESS_KEY_SIZE; - out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE; - break; - default: - rc = -EOPNOTSUPP; - goto out; - } - - /* check if user buffer is big enough to store all the keys */ - if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length - + out.server_out_key_length) { - rc = -ENOBUFS; - goto out; - } - - out.session_id = ses->Suid; - out.cipher_type = le16_to_cpu(ses->server->cipher_type); - - /* overwrite user input with our output */ - if (copy_to_user(in, &out, sizeof(out))) { - rc = -EINVAL; - goto out; - } - - /* append all the keys at the end of the user buffer */ - end = in->data; - if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) { - rc = -EINVAL; - goto out; - } - end += out.session_key_length; - - if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) { - rc = -EINVAL; - goto out; - } - end += out.server_in_key_length; - - if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) { - rc = -EINVAL; - goto out; - } - -out: - if (found) - cifs_put_smb_ses(ses); - return rc; -} - -long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) -{ - struct inode *inode = file_inode(filep); - struct smb3_key_debug_info pkey_inf; - int rc = -ENOTTY; /* strange error - but the precedent */ - unsigned int xid; - struct cifsFileInfo *pSMBFile = filep->private_data; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb; - __u64 ExtAttrBits = 0; - __u64 caps; - - xid = get_xid(); - - cifs_dbg(FYI, "cifs ioctl 0x%x\n", command); - switch (command) { - case FS_IOC_GETFLAGS: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - caps = le64_to_cpu(tcon->fsUnixInfo.Capability); -#ifdef CONFIG_CIFS_POSIX -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (CIFS_UNIX_EXTATTR_CAP & caps) { - __u64 ExtAttrMask = 0; - rc = CIFSGetExtAttr(xid, tcon, - pSMBFile->fid.netfid, - &ExtAttrBits, &ExtAttrMask); - if (rc == 0) - rc = put_user(ExtAttrBits & - FS_FL_USER_VISIBLE, - (int __user *)arg); - if (rc != -EOPNOTSUPP) - break; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ -#endif /* CONFIG_CIFS_POSIX */ - rc = 0; - if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { - /* add in the compressed bit */ - ExtAttrBits = FS_COMPR_FL; - rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, - (int __user *)arg); - } - break; - case FS_IOC_SETFLAGS: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - /* caps = le64_to_cpu(tcon->fsUnixInfo.Capability); */ - - if (get_user(ExtAttrBits, (int __user *)arg)) { - rc = -EFAULT; - break; - } - - /* - * if (CIFS_UNIX_EXTATTR_CAP & caps) - * rc = CIFSSetExtAttr(xid, tcon, - * pSMBFile->fid.netfid, - * extAttrBits, - * &ExtAttrMask); - * if (rc != -EOPNOTSUPP) - * break; - */ - - /* Currently only flag we can set is compressed flag */ - if ((ExtAttrBits & FS_COMPR_FL) == 0) - break; - - /* Try to set compress flag */ - if (tcon->ses->server->ops->set_compression) { - rc = tcon->ses->server->ops->set_compression( - xid, tcon, pSMBFile); - cifs_dbg(FYI, "set compress flag rc %d\n", rc); - } - break; - case CIFS_IOC_COPYCHUNK_FILE: - rc = cifs_ioctl_copychunk(xid, filep, arg); - break; - case CIFS_QUERY_INFO: - rc = cifs_ioctl_query_info(xid, filep, arg); - break; - case CIFS_IOC_SET_INTEGRITY: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - if (tcon->ses->server->ops->set_integrity) - rc = tcon->ses->server->ops->set_integrity(xid, - tcon, pSMBFile); - else - rc = -EOPNOTSUPP; - break; - case CIFS_IOC_GET_MNT_INFO: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); - break; - case CIFS_ENUMERATE_SNAPSHOTS: - if (pSMBFile == NULL) - break; - if (arg == 0) { - rc = -EINVAL; - goto cifs_ioc_exit; - } - tcon = tlink_tcon(pSMBFile->tlink); - if (tcon->ses->server->ops->enum_snapshots) - rc = tcon->ses->server->ops->enum_snapshots(xid, tcon, - pSMBFile, (void __user *)arg); - else - rc = -EOPNOTSUPP; - break; - case CIFS_DUMP_KEY: - /* - * Dump encryption keys. This is an old ioctl that only - * handles AES-128-{CCM,GCM}. - */ - if (pSMBFile == NULL) - break; - if (!capable(CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - - tcon = tlink_tcon(pSMBFile->tlink); - if (!smb3_encryption_required(tcon)) { - rc = -EOPNOTSUPP; - break; - } - pkey_inf.cipher_type = - le16_to_cpu(tcon->ses->server->cipher_type); - pkey_inf.Suid = tcon->ses->Suid; - memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response, - 16 /* SMB2_NTLMV2_SESSKEY_SIZE */); - memcpy(pkey_inf.smb3decryptionkey, - tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); - memcpy(pkey_inf.smb3encryptionkey, - tcon->ses->smb3encryptionkey, SMB3_SIGN_KEY_SIZE); - if (copy_to_user((void __user *)arg, &pkey_inf, - sizeof(struct smb3_key_debug_info))) - rc = -EFAULT; - else - rc = 0; - break; - case CIFS_DUMP_FULL_KEY: - /* - * Dump encryption keys (handles any key sizes) - */ - if (pSMBFile == NULL) - break; - if (!capable(CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - tcon = tlink_tcon(pSMBFile->tlink); - rc = cifs_dump_full_key(tcon, (void __user *)arg); - break; - case CIFS_IOC_NOTIFY: - if (!S_ISDIR(inode->i_mode)) { - /* Notify can only be done on directories */ - rc = -EOPNOTSUPP; - break; - } - cifs_sb = CIFS_SB(inode->i_sb); - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - break; - } - tcon = tlink_tcon(tlink); - if (tcon && tcon->ses->server->ops->notify) { - rc = tcon->ses->server->ops->notify(xid, - filep, (void __user *)arg, - false /* no ret data */); - cifs_dbg(FYI, "ioctl notify rc %d\n", rc); - } else - rc = -EOPNOTSUPP; - cifs_put_tlink(tlink); - break; - case CIFS_IOC_NOTIFY_INFO: - if (!S_ISDIR(inode->i_mode)) { - /* Notify can only be done on directories */ - rc = -EOPNOTSUPP; - break; - } - cifs_sb = CIFS_SB(inode->i_sb); - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - break; - } - tcon = tlink_tcon(tlink); - if (tcon && tcon->ses->server->ops->notify) { - rc = tcon->ses->server->ops->notify(xid, - filep, (void __user *)arg, - true /* return details */); - cifs_dbg(FYI, "ioctl notify info rc %d\n", rc); - } else - rc = -EOPNOTSUPP; - cifs_put_tlink(tlink); - break; - case CIFS_IOC_SHUTDOWN: - rc = cifs_shutdown(inode->i_sb, arg); - break; - default: - cifs_dbg(FYI, "unsupported ioctl\n"); - break; - } -cifs_ioc_exit: - free_xid(xid); - return rc; -} diff --git a/fs/cifs/link.c b/fs/cifs/link.c deleted file mode 100644 index c0f101fc1e5d..000000000000 --- a/fs/cifs/link.c +++ /dev/null @@ -1,650 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "smb2proto.h" -#include "cifs_ioctl.h" - -/* - * M-F Symlink Functions - Begin - */ - -#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1) -#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) -#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1)) -#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) -#define CIFS_MF_SYMLINK_FILE_SIZE \ - (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN) - -#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n" -#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n" -#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash - -static int -symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash) -{ - int rc; - struct shash_desc *md5 = NULL; - - rc = cifs_alloc_hash("md5", &md5); - if (rc) - goto symlink_hash_err; - - rc = crypto_shash_init(md5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__); - goto symlink_hash_err; - } - rc = crypto_shash_update(md5, link_str, link_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); - goto symlink_hash_err; - } - rc = crypto_shash_final(md5, md5_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - -symlink_hash_err: - cifs_free_hash(&md5); - return rc; -} - -static int -parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len, - char **_link_str) -{ - int rc; - unsigned int link_len; - const char *md5_str1; - const char *link_str; - u8 md5_hash[16]; - char md5_str2[34]; - - if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) - return -EINVAL; - - md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; - link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; - - rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); - if (rc != 1) - return -EINVAL; - - if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) - return -EINVAL; - - rc = symlink_hash(link_len, link_str, md5_hash); - if (rc) { - cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); - return rc; - } - - scnprintf(md5_str2, sizeof(md5_str2), - CIFS_MF_SYMLINK_MD5_FORMAT, - CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); - - if (strncmp(md5_str1, md5_str2, 17) != 0) - return -EINVAL; - - if (_link_str) { - *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); - if (!*_link_str) - return -ENOMEM; - } - - *_link_len = link_len; - return 0; -} - -static int -format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str) -{ - int rc; - unsigned int link_len; - unsigned int ofs; - u8 md5_hash[16]; - - if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) - return -EINVAL; - - link_len = strlen(link_str); - - if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) - return -ENAMETOOLONG; - - rc = symlink_hash(link_len, link_str, md5_hash); - if (rc) { - cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); - return rc; - } - - scnprintf(buf, buf_len, - CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, - link_len, - CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); - - ofs = CIFS_MF_SYMLINK_LINK_OFFSET; - memcpy(buf + ofs, link_str, link_len); - - ofs += link_len; - if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { - buf[ofs] = '\n'; - ofs++; - } - - while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { - buf[ofs] = ' '; - ofs++; - } - - return 0; -} - -bool -couldbe_mf_symlink(const struct cifs_fattr *fattr) -{ - if (!S_ISREG(fattr->cf_mode)) - /* it's not a symlink */ - return false; - - if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) - /* it's not a symlink */ - return false; - - return true; -} - -static int -create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *fromName, - const char *toName) -{ - int rc; - u8 *buf; - unsigned int bytes_written = 0; - - buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); - if (rc) - goto out; - - if (tcon->ses->server->ops->create_mf_symlink) - rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon, - cifs_sb, fromName, buf, &bytes_written); - else - rc = -EOPNOTSUPP; - - if (rc) - goto out; - - if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) - rc = -EIO; -out: - kfree(buf); - return rc; -} - -int -check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, - const unsigned char *path) -{ - int rc; - u8 *buf = NULL; - unsigned int link_len = 0; - unsigned int bytes_read = 0; - char *symlink = NULL; - - if (!couldbe_mf_symlink(fattr)) - /* it's not a symlink */ - return 0; - - buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (tcon->ses->server->ops->query_mf_symlink) - rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, - cifs_sb, path, buf, &bytes_read); - else - rc = -ENOSYS; - - if (rc) - goto out; - - if (bytes_read == 0) /* not a symlink */ - goto out; - - rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink); - if (rc == -EINVAL) { - /* it's not a symlink */ - rc = 0; - goto out; - } - - if (rc != 0) - goto out; - - /* it is a symlink */ - fattr->cf_eof = link_len; - fattr->cf_mode &= ~S_IFMT; - fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; - fattr->cf_dtype = DT_LNK; - fattr->cf_symlink_target = symlink; -out: - kfree(buf); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/* - * SMB 1.0 Protocol specific functions - */ - -int -cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_read) -{ - int rc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - int buf_type = CIFS_NO_BUFFER; - FILE_ALL_INFO file_info; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_READ, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, &file_info); - if (rc) - return rc; - - if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { - rc = -ENOENT; - /* it's not a symlink */ - goto out; - } - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - - rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); -out: - CIFSSMBClose(xid, tcon, fid.netfid); - return rc; -} - -int -cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_written) -{ - int rc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_CREATE, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - return rc; - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - - rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf); - CIFSSMBClose(xid, tcon, fid.netfid); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* - * SMB 2.1/SMB3 Protocol specific functions - */ -int -smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_read) -{ - int rc; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - int buf_type = CIFS_NO_BUFFER; - __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct smb2_file_all_info *pfile_info = NULL; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .path = path, - .desired_access = GENERIC_READ, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .fid = &fid, - }; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (utf16_path == NULL) - return -ENOMEM; - - pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, - GFP_KERNEL); - - if (pfile_info == NULL) { - kfree(utf16_path); - return -ENOMEM; - } - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL, - NULL, NULL); - if (rc) - goto qmf_out_open_fail; - - if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { - /* it's not a symlink */ - rc = -ENOENT; /* Is there a better rc to return? */ - goto qmf_out; - } - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - io_parms.persistent_fid = fid.persistent_fid; - io_parms.volatile_fid = fid.volatile_fid; - rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type); -qmf_out: - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); -qmf_out_open_fail: - kfree(utf16_path); - kfree(pfile_info); - return rc; -} - -int -smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_written) -{ - int rc; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct kvec iov[2]; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .path = path, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_CREATE, - .fid = &fid, - .mode = 0644, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) { - kfree(utf16_path); - return rc; - } - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - io_parms.persistent_fid = fid.persistent_fid; - io_parms.volatile_fid = fid.volatile_fid; - - /* iov[0] is reserved for smb header */ - iov[1].iov_base = pbuf; - iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE; - - rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1); - - /* Make sure we wrote all of the symlink data */ - if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE)) - rc = -EIO; - - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - - kfree(utf16_path); - return rc; -} - -/* - * M-F Symlink Functions - End - */ - -int -cifs_hardlink(struct dentry *old_file, struct inode *inode, - struct dentry *direntry) -{ - int rc = -EACCES; - unsigned int xid; - const char *from_name, *to_name; - void *page1, *page2; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsInodeInfo *cifsInode; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - xid = get_xid(); - page1 = alloc_dentry_path(); - page2 = alloc_dentry_path(); - - from_name = build_path_from_dentry(old_file, page1); - if (IS_ERR(from_name)) { - rc = PTR_ERR(from_name); - goto cifs_hl_exit; - } - to_name = build_path_from_dentry(direntry, page2); - if (IS_ERR(to_name)) { - rc = PTR_ERR(to_name); - goto cifs_hl_exit; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext) - rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - else { -#else - { -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - server = tcon->ses->server; - if (!server->ops->create_hardlink) { - rc = -ENOSYS; - goto cifs_hl_exit; - } - rc = server->ops->create_hardlink(xid, tcon, from_name, to_name, - cifs_sb); - if ((rc == -EIO) || (rc == -EINVAL)) - rc = -EOPNOTSUPP; - } - - d_drop(direntry); /* force new lookup from server of target */ - - /* - * if source file is cached (oplocked) revalidate will not go to server - * until the file is closed or oplock broken so update nlinks locally - */ - if (d_really_is_positive(old_file)) { - cifsInode = CIFS_I(d_inode(old_file)); - if (rc == 0) { - spin_lock(&d_inode(old_file)->i_lock); - inc_nlink(d_inode(old_file)); - spin_unlock(&d_inode(old_file)->i_lock); - - /* - * parent dir timestamps will update from srv within a - * second, would it really be worth it to set the parent - * dir cifs inode time to zero to force revalidate - * (faster) for it too? - */ - } - /* - * if not oplocked will force revalidate to get info on source - * file from srv. Note Samba server prior to 4.2 has bug - - * not updating src file ctime on hardlinks but Windows servers - * handle it properly - */ - cifsInode->time = 0; - - /* - * Will update parent dir timestamps from srv within a second. - * Would it really be worth it to set the parent dir (cifs - * inode) time field to zero to force revalidate on parent - * directory faster ie - * - * CIFS_I(inode)->time = 0; - */ - } - -cifs_hl_exit: - free_dentry_path(page1); - free_dentry_path(page2); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -int -cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, const char *symname) -{ - int rc = -EOPNOTSUPP; - unsigned int xid; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - struct inode *newinode = NULL; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - page = alloc_dentry_path(); - if (!page) - return -ENOMEM; - - xid = get_xid(); - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto symlink_exit; - } - pTcon = tlink_tcon(tlink); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto symlink_exit; - } - - cifs_dbg(FYI, "Full path: %s\n", full_path); - cifs_dbg(FYI, "symname is %s\n", symname); - - /* BB what if DFS and this volume is on different share? BB */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) - rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - else if (pTcon->unix_ext) - rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - /* else - rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls); */ - - if (rc == 0) { - if (pTcon->posix_extensions) - rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid); - else if (pTcon->unix_ext) - rc = cifs_get_inode_info_unix(&newinode, full_path, - inode->i_sb, xid); - else - rc = cifs_get_inode_info(&newinode, full_path, NULL, - inode->i_sb, xid, NULL); - - if (rc != 0) { - cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n", - rc); - } else { - d_instantiate(direntry, newinode); - } - } -symlink_exit: - free_dentry_path(page); - cifs_put_tlink(tlink); - free_xid(xid); - return rc; -} diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c deleted file mode 100644 index 31e06133acc3..000000000000 --- a/fs/cifs/misc.c +++ /dev/null @@ -1,1434 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "smberr.h" -#include "nterr.h" -#include "cifs_unicode.h" -#include "smb2pdu.h" -#include "cifsfs.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dns_resolve.h" -#include "dfs_cache.h" -#endif -#include "fs_context.h" -#include "cached_dir.h" - -extern mempool_t *cifs_sm_req_poolp; -extern mempool_t *cifs_req_poolp; - -/* The xid serves as a useful identifier for each incoming vfs request, - in a similar way to the mid which is useful to track each sent smb, - and CurrentXid can also provide a running counter (although it - will eventually wrap past zero) of the total vfs operations handled - since the cifs fs was mounted */ - -unsigned int -_get_xid(void) -{ - unsigned int xid; - - spin_lock(&GlobalMid_Lock); - GlobalTotalActiveXid++; - - /* keep high water mark for number of simultaneous ops in filesystem */ - if (GlobalTotalActiveXid > GlobalMaxActiveXid) - GlobalMaxActiveXid = GlobalTotalActiveXid; - if (GlobalTotalActiveXid > 65000) - cifs_dbg(FYI, "warning: more than 65000 requests active\n"); - xid = GlobalCurrentXid++; - spin_unlock(&GlobalMid_Lock); - return xid; -} - -void -_free_xid(unsigned int xid) -{ - spin_lock(&GlobalMid_Lock); - /* if (GlobalTotalActiveXid == 0) - BUG(); */ - GlobalTotalActiveXid--; - spin_unlock(&GlobalMid_Lock); -} - -struct cifs_ses * -sesInfoAlloc(void) -{ - struct cifs_ses *ret_buf; - - ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL); - if (ret_buf) { - atomic_inc(&sesInfoAllocCount); - spin_lock_init(&ret_buf->ses_lock); - ret_buf->ses_status = SES_NEW; - ++ret_buf->ses_count; - INIT_LIST_HEAD(&ret_buf->smb_ses_list); - INIT_LIST_HEAD(&ret_buf->tcon_list); - mutex_init(&ret_buf->session_mutex); - spin_lock_init(&ret_buf->iface_lock); - INIT_LIST_HEAD(&ret_buf->iface_list); - spin_lock_init(&ret_buf->chan_lock); - } - return ret_buf; -} - -void -sesInfoFree(struct cifs_ses *buf_to_free) -{ - struct cifs_server_iface *iface = NULL, *niface = NULL; - - if (buf_to_free == NULL) { - cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n"); - return; - } - - atomic_dec(&sesInfoAllocCount); - kfree(buf_to_free->serverOS); - kfree(buf_to_free->serverDomain); - kfree(buf_to_free->serverNOS); - kfree_sensitive(buf_to_free->password); - kfree(buf_to_free->user_name); - kfree(buf_to_free->domainName); - kfree_sensitive(buf_to_free->auth_key.response); - spin_lock(&buf_to_free->iface_lock); - list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list, - iface_head) - kref_put(&iface->refcount, release_iface); - spin_unlock(&buf_to_free->iface_lock); - kfree_sensitive(buf_to_free); -} - -struct cifs_tcon * -tconInfoAlloc(void) -{ - struct cifs_tcon *ret_buf; - - ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); - if (!ret_buf) - return NULL; - ret_buf->cfids = init_cached_dirs(); - if (!ret_buf->cfids) { - kfree(ret_buf); - return NULL; - } - - atomic_inc(&tconInfoAllocCount); - ret_buf->status = TID_NEW; - ++ret_buf->tc_count; - spin_lock_init(&ret_buf->tc_lock); - INIT_LIST_HEAD(&ret_buf->openFileList); - INIT_LIST_HEAD(&ret_buf->tcon_list); - spin_lock_init(&ret_buf->open_file_lock); - spin_lock_init(&ret_buf->stat_lock); - atomic_set(&ret_buf->num_local_opens, 0); - atomic_set(&ret_buf->num_remote_opens, 0); - - return ret_buf; -} - -void -tconInfoFree(struct cifs_tcon *tcon) -{ - if (tcon == NULL) { - cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n"); - return; - } - free_cached_dirs(tcon->cfids); - atomic_dec(&tconInfoAllocCount); - kfree(tcon->nativeFileSystem); - kfree_sensitive(tcon->password); - kfree(tcon); -} - -struct smb_hdr * -cifs_buf_get(void) -{ - struct smb_hdr *ret_buf = NULL; - /* - * SMB2 header is bigger than CIFS one - no problems to clean some - * more bytes for CIFS. - */ - size_t buf_size = sizeof(struct smb2_hdr); - - /* - * We could use negotiated size instead of max_msgsize - - * but it may be more efficient to always alloc same size - * albeit slightly larger than necessary and maxbuffersize - * defaults to this and can not be bigger. - */ - ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); - - /* clear the first few header bytes */ - /* for most paths, more is cleared in header_assemble */ - memset(ret_buf, 0, buf_size + 3); - atomic_inc(&buf_alloc_count); -#ifdef CONFIG_CIFS_STATS2 - atomic_inc(&total_buf_alloc_count); -#endif /* CONFIG_CIFS_STATS2 */ - - return ret_buf; -} - -void -cifs_buf_release(void *buf_to_free) -{ - if (buf_to_free == NULL) { - /* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/ - return; - } - mempool_free(buf_to_free, cifs_req_poolp); - - atomic_dec(&buf_alloc_count); - return; -} - -struct smb_hdr * -cifs_small_buf_get(void) -{ - struct smb_hdr *ret_buf = NULL; - -/* We could use negotiated size instead of max_msgsize - - but it may be more efficient to always alloc same size - albeit slightly larger than necessary and maxbuffersize - defaults to this and can not be bigger */ - ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS); - /* No need to clear memory here, cleared in header assemble */ - /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ - atomic_inc(&small_buf_alloc_count); -#ifdef CONFIG_CIFS_STATS2 - atomic_inc(&total_small_buf_alloc_count); -#endif /* CONFIG_CIFS_STATS2 */ - - return ret_buf; -} - -void -cifs_small_buf_release(void *buf_to_free) -{ - - if (buf_to_free == NULL) { - cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n"); - return; - } - mempool_free(buf_to_free, cifs_sm_req_poolp); - - atomic_dec(&small_buf_alloc_count); - return; -} - -void -free_rsp_buf(int resp_buftype, void *rsp) -{ - if (resp_buftype == CIFS_SMALL_BUFFER) - cifs_small_buf_release(rsp); - else if (resp_buftype == CIFS_LARGE_BUFFER) - cifs_buf_release(rsp); -} - -/* NB: MID can not be set if treeCon not passed in, in that - case it is responsbility of caller to set the mid */ -void -header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , - const struct cifs_tcon *treeCon, int word_count - /* length of fixed section (word count) in two byte units */) -{ - char *temp = (char *) buffer; - - memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ - - buffer->smb_buf_length = cpu_to_be32( - (2 * word_count) + sizeof(struct smb_hdr) - - 4 /* RFC 1001 length field does not count */ + - 2 /* for bcc field itself */) ; - - buffer->Protocol[0] = 0xFF; - buffer->Protocol[1] = 'S'; - buffer->Protocol[2] = 'M'; - buffer->Protocol[3] = 'B'; - buffer->Command = smb_command; - buffer->Flags = 0x00; /* case sensitive */ - buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; - buffer->Pid = cpu_to_le16((__u16)current->tgid); - buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); - if (treeCon) { - buffer->Tid = treeCon->tid; - if (treeCon->ses) { - if (treeCon->ses->capabilities & CAP_UNICODE) - buffer->Flags2 |= SMBFLG2_UNICODE; - if (treeCon->ses->capabilities & CAP_STATUS32) - buffer->Flags2 |= SMBFLG2_ERR_STATUS; - - /* Uid is not converted */ - buffer->Uid = treeCon->ses->Suid; - if (treeCon->ses->server) - buffer->Mid = get_next_mid(treeCon->ses->server); - } - if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) - buffer->Flags2 |= SMBFLG2_DFS; - if (treeCon->nocase) - buffer->Flags |= SMBFLG_CASELESS; - if ((treeCon->ses) && (treeCon->ses->server)) - if (treeCon->ses->server->sign) - buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - } - -/* endian conversion of flags is now done just before sending */ - buffer->WordCount = (char) word_count; - return; -} - -static int -check_smb_hdr(struct smb_hdr *smb) -{ - /* does it have the right SMB "signature" ? */ - if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { - cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", - *(unsigned int *)smb->Protocol); - return 1; - } - - /* if it's a response then accept */ - if (smb->Flags & SMBFLG_RESPONSE) - return 0; - - /* only one valid case where server sends us request */ - if (smb->Command == SMB_COM_LOCKING_ANDX) - return 0; - - cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", - get_mid(smb)); - return 1; -} - -int -checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server) -{ - struct smb_hdr *smb = (struct smb_hdr *)buf; - __u32 rfclen = be32_to_cpu(smb->smb_buf_length); - __u32 clc_len; /* calculated length */ - cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", - total_read, rfclen); - - /* is this frame too small to even get to a BCC? */ - if (total_read < 2 + sizeof(struct smb_hdr)) { - if ((total_read >= sizeof(struct smb_hdr) - 1) - && (smb->Status.CifsError != 0)) { - /* it's an error return */ - smb->WordCount = 0; - /* some error cases do not return wct and bcc */ - return 0; - } else if ((total_read == sizeof(struct smb_hdr) + 1) && - (smb->WordCount == 0)) { - char *tmp = (char *)smb; - /* Need to work around a bug in two servers here */ - /* First, check if the part of bcc they sent was zero */ - if (tmp[sizeof(struct smb_hdr)] == 0) { - /* some servers return only half of bcc - * on simple responses (wct, bcc both zero) - * in particular have seen this on - * ulogoffX and FindClose. This leaves - * one byte of bcc potentially unitialized - */ - /* zero rest of bcc */ - tmp[sizeof(struct smb_hdr)+1] = 0; - return 0; - } - cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n"); - } else { - cifs_dbg(VFS, "Length less than smb header size\n"); - } - return -EIO; - } - - /* otherwise, there is enough to get to the BCC */ - if (check_smb_hdr(smb)) - return -EIO; - clc_len = smbCalcSize(smb); - - if (4 + rfclen != total_read) { - cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n", - rfclen); - return -EIO; - } - - if (4 + rfclen != clc_len) { - __u16 mid = get_mid(smb); - /* check if bcc wrapped around for large read responses */ - if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { - /* check if lengths match mod 64K */ - if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) - return 0; /* bcc wrapped */ - } - cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", - clc_len, 4 + rfclen, mid); - - if (4 + rfclen < clc_len) { - cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", - rfclen, mid); - return -EIO; - } else if (rfclen > clc_len + 512) { - /* - * Some servers (Windows XP in particular) send more - * data than the lengths in the SMB packet would - * indicate on certain calls (byte range locks and - * trans2 find first calls in particular). While the - * client can handle such a frame by ignoring the - * trailing data, we choose limit the amount of extra - * data to 512 bytes. - */ - cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", - rfclen, mid); - return -EIO; - } - } - return 0; -} - -bool -is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) -{ - struct smb_hdr *buf = (struct smb_hdr *)buffer; - struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifsInodeInfo *pCifsInode; - struct cifsFileInfo *netfile; - - cifs_dbg(FYI, "Checking for oplock break or dnotify response\n"); - if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && - (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { - struct smb_com_transaction_change_notify_rsp *pSMBr = - (struct smb_com_transaction_change_notify_rsp *)buf; - struct file_notify_information *pnotify; - __u32 data_offset = 0; - size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length); - - if (get_bcc(buf) > sizeof(struct file_notify_information)) { - data_offset = le32_to_cpu(pSMBr->DataOffset); - - if (data_offset > - len - sizeof(struct file_notify_information)) { - cifs_dbg(FYI, "Invalid data_offset %u\n", - data_offset); - return true; - } - pnotify = (struct file_notify_information *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n", - pnotify->FileName, pnotify->Action); - /* cifs_dump_mem("Rcvd notify Data: ",buf, - sizeof(struct smb_hdr)+60); */ - return true; - } - if (pSMBr->hdr.Status.CifsError) { - cifs_dbg(FYI, "notify err 0x%x\n", - pSMBr->hdr.Status.CifsError); - return true; - } - return false; - } - if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) - return false; - if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { - /* no sense logging error on invalid handle on oplock - break - harmless race between close request and oplock - break response is expected from time to time writing out - large dirty files cached on the client */ - if ((NT_STATUS_INVALID_HANDLE) == - le32_to_cpu(pSMB->hdr.Status.CifsError)) { - cifs_dbg(FYI, "Invalid handle on oplock break\n"); - return true; - } else if (ERRbadfid == - le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { - return true; - } else { - return false; /* on valid oplock brk we get "request" */ - } - } - if (pSMB->hdr.WordCount != 8) - return false; - - cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n", - pSMB->LockType, pSMB->OplockLevel); - if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) - return false; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(srv) ? srv->primary_server : srv; - - /* look up tcon based on tid & uid */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid != buf->Tid) - continue; - - cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); - spin_lock(&tcon->open_file_lock); - list_for_each_entry(netfile, &tcon->openFileList, tlist) { - if (pSMB->Fid != netfile->fid.netfid) - continue; - - cifs_dbg(FYI, "file id match, oplock break\n"); - pCifsInode = CIFS_I(d_inode(netfile->dentry)); - - set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, - &pCifsInode->flags); - - netfile->oplock_epoch = 0; - netfile->oplock_level = pSMB->OplockLevel; - netfile->oplock_break_cancelled = false; - cifs_queue_oplock_break(netfile); - - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "No matching file for oplock break\n"); - return true; - } - } - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); - return true; -} - -void -dump_smb(void *buf, int smb_buf_length) -{ - if (traceSMB == 0) - return; - - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 8, 2, buf, - smb_buf_length, true); -} - -void -cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) -{ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { - struct cifs_tcon *tcon = NULL; - - if (cifs_sb->master_tlink) - tcon = cifs_sb_master_tcon(cifs_sb); - - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; - cifs_sb->mnt_cifs_serverino_autodisabled = true; - cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n", - tcon ? tcon->tree_name : "new server"); - cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n"); - cifs_dbg(VFS, "Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n"); - - } -} - -void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) -{ - oplock &= 0xF; - - if (oplock == OPLOCK_EXCLUSIVE) { - cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG; - cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else if (oplock == OPLOCK_READ) { - cinode->oplock = CIFS_CACHE_READ_FLG; - cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else - cinode->oplock = 0; -} - -/* - * We wait for oplock breaks to be processed before we attempt to perform - * writes. - */ -int cifs_get_writer(struct cifsInodeInfo *cinode) -{ - int rc; - -start: - rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, - TASK_KILLABLE); - if (rc) - return rc; - - spin_lock(&cinode->writers_lock); - if (!cinode->writers) - set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); - cinode->writers++; - /* Check to see if we have started servicing an oplock break */ - if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { - cinode->writers--; - if (cinode->writers == 0) { - clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); - wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); - } - spin_unlock(&cinode->writers_lock); - goto start; - } - spin_unlock(&cinode->writers_lock); - return 0; -} - -void cifs_put_writer(struct cifsInodeInfo *cinode) -{ - spin_lock(&cinode->writers_lock); - cinode->writers--; - if (cinode->writers == 0) { - clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); - wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); - } - spin_unlock(&cinode->writers_lock); -} - -/** - * cifs_queue_oplock_break - queue the oplock break handler for cfile - * @cfile: The file to break the oplock on - * - * This function is called from the demultiplex thread when it - * receives an oplock break for @cfile. - * - * Assumes the tcon->open_file_lock is held. - * Assumes cfile->file_info_lock is NOT held. - */ -void cifs_queue_oplock_break(struct cifsFileInfo *cfile) -{ - /* - * Bump the handle refcount now while we hold the - * open_file_lock to enforce the validity of it for the oplock - * break handler. The matching put is done at the end of the - * handler. - */ - cifsFileInfo_get(cfile); - - queue_work(cifsoplockd_wq, &cfile->oplock_break); -} - -void cifs_done_oplock_break(struct cifsInodeInfo *cinode) -{ - clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); - wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); -} - -bool -backup_cred(struct cifs_sb_info *cifs_sb) -{ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { - if (uid_eq(cifs_sb->ctx->backupuid, current_fsuid())) - return true; - } - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { - if (in_group_p(cifs_sb->ctx->backupgid)) - return true; - } - - return false; -} - -void -cifs_del_pending_open(struct cifs_pending_open *open) -{ - spin_lock(&tlink_tcon(open->tlink)->open_file_lock); - list_del(&open->olist); - spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); -} - -void -cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, - struct cifs_pending_open *open) -{ - memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); - open->oplock = CIFS_OPLOCK_NO_CHANGE; - open->tlink = tlink; - fid->pending_open = open; - list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens); -} - -void -cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, - struct cifs_pending_open *open) -{ - spin_lock(&tlink_tcon(tlink)->open_file_lock); - cifs_add_pending_open_locked(fid, tlink, open); - spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); -} - -/* - * Critical section which runs after acquiring deferred_lock. - * As there is no reference count on cifs_deferred_close, pdclose - * should not be used outside deferred_lock. - */ -bool -cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose) -{ - struct cifs_deferred_close *dclose; - - list_for_each_entry(dclose, &CIFS_I(d_inode(cfile->dentry))->deferred_closes, dlist) { - if ((dclose->netfid == cfile->fid.netfid) && - (dclose->persistent_fid == cfile->fid.persistent_fid) && - (dclose->volatile_fid == cfile->fid.volatile_fid)) { - *pdclose = dclose; - return true; - } - } - return false; -} - -/* - * Critical section which runs after acquiring deferred_lock. - */ -void -cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose) -{ - bool is_deferred = false; - struct cifs_deferred_close *pdclose; - - is_deferred = cifs_is_deferred_close(cfile, &pdclose); - if (is_deferred) { - kfree(dclose); - return; - } - - dclose->tlink = cfile->tlink; - dclose->netfid = cfile->fid.netfid; - dclose->persistent_fid = cfile->fid.persistent_fid; - dclose->volatile_fid = cfile->fid.volatile_fid; - list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes); -} - -/* - * Critical section which runs after acquiring deferred_lock. - */ -void -cifs_del_deferred_close(struct cifsFileInfo *cfile) -{ - bool is_deferred = false; - struct cifs_deferred_close *dclose; - - is_deferred = cifs_is_deferred_close(cfile, &dclose); - if (!is_deferred) - return; - list_del(&dclose->dlist); - kfree(dclose); -} - -void -cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) -{ - struct cifsFileInfo *cfile = NULL; - struct file_list *tmp_list, *tmp_next_list; - struct list_head file_head; - - if (cifs_inode == NULL) - return; - - INIT_LIST_HEAD(&file_head); - spin_lock(&cifs_inode->open_file_lock); - list_for_each_entry(cfile, &cifs_inode->openFileList, flist) { - if (delayed_work_pending(&cfile->deferred)) { - if (cancel_delayed_work(&cfile->deferred)) { - spin_lock(&cifs_inode->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&cifs_inode->deferred_lock); - - tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); - if (tmp_list == NULL) - break; - tmp_list->cfile = cfile; - list_add_tail(&tmp_list->list, &file_head); - } - } - } - spin_unlock(&cifs_inode->open_file_lock); - - list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, false, false); - list_del(&tmp_list->list); - kfree(tmp_list); - } -} - -void -cifs_close_all_deferred_files(struct cifs_tcon *tcon) -{ - struct cifsFileInfo *cfile; - struct file_list *tmp_list, *tmp_next_list; - struct list_head file_head; - - INIT_LIST_HEAD(&file_head); - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - if (delayed_work_pending(&cfile->deferred)) { - if (cancel_delayed_work(&cfile->deferred)) { - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - - tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); - if (tmp_list == NULL) - break; - tmp_list->cfile = cfile; - list_add_tail(&tmp_list->list, &file_head); - } - } - } - spin_unlock(&tcon->open_file_lock); - - list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); - list_del(&tmp_list->list); - kfree(tmp_list); - } -} -void -cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path) -{ - struct cifsFileInfo *cfile; - struct file_list *tmp_list, *tmp_next_list; - struct list_head file_head; - void *page; - const char *full_path; - - INIT_LIST_HEAD(&file_head); - page = alloc_dentry_path(); - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - full_path = build_path_from_dentry(cfile->dentry, page); - if (strstr(full_path, path)) { - if (delayed_work_pending(&cfile->deferred)) { - if (cancel_delayed_work(&cfile->deferred)) { - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - - tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); - if (tmp_list == NULL) - break; - tmp_list->cfile = cfile; - list_add_tail(&tmp_list->list, &file_head); - } - } - } - } - spin_unlock(&tcon->open_file_lock); - - list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); - list_del(&tmp_list->list); - kfree(tmp_list); - } - free_dentry_path(page); -} - -/* parses DFS referral V3 structure - * caller is responsible for freeing target_nodes - * returns: - * - on success - 0 - * - on failure - errno - */ -int -parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, - unsigned int *num_of_nodes, - struct dfs_info3_param **target_nodes, - const struct nls_table *nls_codepage, int remap, - const char *searchName, bool is_unicode) -{ - int i, rc = 0; - char *data_end; - struct dfs_referral_level_3 *ref; - - *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals); - - if (*num_of_nodes < 1) { - cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n", - *num_of_nodes); - rc = -EINVAL; - goto parse_DFS_referrals_exit; - } - - ref = (struct dfs_referral_level_3 *) &(rsp->referrals); - if (ref->VersionNumber != cpu_to_le16(3)) { - cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n", - le16_to_cpu(ref->VersionNumber)); - rc = -EINVAL; - goto parse_DFS_referrals_exit; - } - - /* get the upper boundary of the resp buffer */ - data_end = (char *)rsp + rsp_size; - - cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", - *num_of_nodes, le32_to_cpu(rsp->DFSFlags)); - - *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param), - GFP_KERNEL); - if (*target_nodes == NULL) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - - /* collect necessary data from referrals */ - for (i = 0; i < *num_of_nodes; i++) { - char *temp; - int max_len; - struct dfs_info3_param *node = (*target_nodes)+i; - - node->flags = le32_to_cpu(rsp->DFSFlags); - if (is_unicode) { - __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, - GFP_KERNEL); - if (tmp == NULL) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - cifsConvertToUTF16((__le16 *) tmp, searchName, - PATH_MAX, nls_codepage, remap); - node->path_consumed = cifs_utf16_bytes(tmp, - le16_to_cpu(rsp->PathConsumed), - nls_codepage); - kfree(tmp); - } else - node->path_consumed = le16_to_cpu(rsp->PathConsumed); - - node->server_type = le16_to_cpu(ref->ServerType); - node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); - - /* copy DfsPath */ - temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); - max_len = data_end - temp; - node->path_name = cifs_strndup_from_utf16(temp, max_len, - is_unicode, nls_codepage); - if (!node->path_name) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - - /* copy link target UNC */ - temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); - max_len = data_end - temp; - node->node_name = cifs_strndup_from_utf16(temp, max_len, - is_unicode, nls_codepage); - if (!node->node_name) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - - node->ttl = le32_to_cpu(ref->TimeToLive); - - ref++; - } - -parse_DFS_referrals_exit: - if (rc) { - free_dfs_info_array(*target_nodes, *num_of_nodes); - *target_nodes = NULL; - *num_of_nodes = 0; - } - return rc; -} - -struct cifs_aio_ctx * -cifs_aio_ctx_alloc(void) -{ - struct cifs_aio_ctx *ctx; - - /* - * Must use kzalloc to initialize ctx->bv to NULL and ctx->direct_io - * to false so that we know when we have to unreference pages within - * cifs_aio_ctx_release() - */ - ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL); - if (!ctx) - return NULL; - - INIT_LIST_HEAD(&ctx->list); - mutex_init(&ctx->aio_mutex); - init_completion(&ctx->done); - kref_init(&ctx->refcount); - return ctx; -} - -void -cifs_aio_ctx_release(struct kref *refcount) -{ - struct cifs_aio_ctx *ctx = container_of(refcount, - struct cifs_aio_ctx, refcount); - - cifsFileInfo_put(ctx->cfile); - - /* - * ctx->bv is only set if setup_aio_ctx_iter() was call successfuly - * which means that iov_iter_get_pages() was a success and thus that - * we have taken reference on pages. - */ - if (ctx->bv) { - unsigned i; - - for (i = 0; i < ctx->npages; i++) { - if (ctx->should_dirty) - set_page_dirty(ctx->bv[i].bv_page); - put_page(ctx->bv[i].bv_page); - } - kvfree(ctx->bv); - } - - kfree(ctx); -} - -#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024) - -int -setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw) -{ - ssize_t rc; - unsigned int cur_npages; - unsigned int npages = 0; - unsigned int i; - size_t len; - size_t count = iov_iter_count(iter); - unsigned int saved_len; - size_t start; - unsigned int max_pages = iov_iter_npages(iter, INT_MAX); - struct page **pages = NULL; - struct bio_vec *bv = NULL; - - if (iov_iter_is_kvec(iter)) { - memcpy(&ctx->iter, iter, sizeof(*iter)); - ctx->len = count; - iov_iter_advance(iter, count); - return 0; - } - - if (array_size(max_pages, sizeof(*bv)) <= CIFS_AIO_KMALLOC_LIMIT) - bv = kmalloc_array(max_pages, sizeof(*bv), GFP_KERNEL); - - if (!bv) { - bv = vmalloc(array_size(max_pages, sizeof(*bv))); - if (!bv) - return -ENOMEM; - } - - if (array_size(max_pages, sizeof(*pages)) <= CIFS_AIO_KMALLOC_LIMIT) - pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL); - - if (!pages) { - pages = vmalloc(array_size(max_pages, sizeof(*pages))); - if (!pages) { - kvfree(bv); - return -ENOMEM; - } - } - - saved_len = count; - - while (count && npages < max_pages) { - rc = iov_iter_get_pages2(iter, pages, count, max_pages, &start); - if (rc < 0) { - cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc); - break; - } - - if (rc > count) { - cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc, - count); - break; - } - - count -= rc; - rc += start; - cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE); - - if (npages + cur_npages > max_pages) { - cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n", - npages + cur_npages, max_pages); - break; - } - - for (i = 0; i < cur_npages; i++) { - len = rc > PAGE_SIZE ? PAGE_SIZE : rc; - bv[npages + i].bv_page = pages[i]; - bv[npages + i].bv_offset = start; - bv[npages + i].bv_len = len - start; - rc -= len; - start = 0; - } - - npages += cur_npages; - } - - kvfree(pages); - ctx->bv = bv; - ctx->len = saved_len - count; - ctx->npages = npages; - iov_iter_bvec(&ctx->iter, rw, ctx->bv, npages, ctx->len); - return 0; -} - -/** - * cifs_alloc_hash - allocate hash and hash context together - * @name: The name of the crypto hash algo - * @sdesc: SHASH descriptor where to put the pointer to the hash TFM - * - * The caller has to make sure @sdesc is initialized to either NULL or - * a valid context. It can be freed via cifs_free_hash(). - */ -int -cifs_alloc_hash(const char *name, struct shash_desc **sdesc) -{ - int rc = 0; - struct crypto_shash *alg = NULL; - - if (*sdesc) - return 0; - - alg = crypto_alloc_shash(name, 0, 0); - if (IS_ERR(alg)) { - cifs_dbg(VFS, "Could not allocate shash TFM '%s'\n", name); - rc = PTR_ERR(alg); - *sdesc = NULL; - return rc; - } - - *sdesc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(alg), GFP_KERNEL); - if (*sdesc == NULL) { - cifs_dbg(VFS, "no memory left to allocate shash TFM '%s'\n", name); - crypto_free_shash(alg); - return -ENOMEM; - } - - (*sdesc)->tfm = alg; - return 0; -} - -/** - * cifs_free_hash - free hash and hash context together - * @sdesc: Where to find the pointer to the hash TFM - * - * Freeing a NULL descriptor is safe. - */ -void -cifs_free_hash(struct shash_desc **sdesc) -{ - if (unlikely(!sdesc) || !*sdesc) - return; - - if ((*sdesc)->tfm) { - crypto_free_shash((*sdesc)->tfm); - (*sdesc)->tfm = NULL; - } - - kfree_sensitive(*sdesc); - *sdesc = NULL; -} - -/** - * rqst_page_get_length - obtain the length and offset for a page in smb_rqst - * @rqst: The request descriptor - * @page: The index of the page to query - * @len: Where to store the length for this page: - * @offset: Where to store the offset for this page - */ -void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, - unsigned int *len, unsigned int *offset) -{ - *len = rqst->rq_pagesz; - *offset = (page == 0) ? rqst->rq_offset : 0; - - if (rqst->rq_npages == 1 || page == rqst->rq_npages-1) - *len = rqst->rq_tailsz; - else if (page == 0) - *len = rqst->rq_pagesz - rqst->rq_offset; -} - -void extract_unc_hostname(const char *unc, const char **h, size_t *len) -{ - const char *end; - - /* skip initial slashes */ - while (*unc && (*unc == '\\' || *unc == '/')) - unc++; - - end = unc; - - while (*end && !(*end == '\\' || *end == '/')) - end++; - - *h = unc; - *len = end - unc; -} - -/** - * copy_path_name - copy src path to dst, possibly truncating - * @dst: The destination buffer - * @src: The source name - * - * returns number of bytes written (including trailing nul) - */ -int copy_path_name(char *dst, const char *src) -{ - int name_len; - - /* - * PATH_MAX includes nul, so if strlen(src) >= PATH_MAX it - * will truncate and strlen(dst) will be PATH_MAX-1 - */ - name_len = strscpy(dst, src, PATH_MAX); - if (WARN_ON_ONCE(name_len < 0)) - name_len = PATH_MAX-1; - - /* we count the trailing nul */ - name_len++; - return name_len; -} - -struct super_cb_data { - void *data; - struct super_block *sb; -}; - -static void tcp_super_cb(struct super_block *sb, void *arg) -{ - struct super_cb_data *sd = arg; - struct TCP_Server_Info *server = sd->data; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - - if (sd->sb) - return; - - cifs_sb = CIFS_SB(sb); - tcon = cifs_sb_master_tcon(cifs_sb); - if (tcon->ses->server == server) - sd->sb = sb; -} - -static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *), - void *data) -{ - struct super_cb_data sd = { - .data = data, - .sb = NULL, - }; - struct file_system_type **fs_type = (struct file_system_type *[]) { - &cifs_fs_type, &smb3_fs_type, NULL, - }; - - for (; *fs_type; fs_type++) { - iterate_supers_type(*fs_type, f, &sd); - if (sd.sb) { - /* - * Grab an active reference in order to prevent automounts (DFS links) - * of expiring and then freeing up our cifs superblock pointer while - * we're doing failover. - */ - cifs_sb_active(sd.sb); - return sd.sb; - } - } - return ERR_PTR(-EINVAL); -} - -static void __cifs_put_super(struct super_block *sb) -{ - if (!IS_ERR_OR_NULL(sb)) - cifs_sb_deactive(sb); -} - -struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) -{ - return __cifs_get_super(tcp_super_cb, server); -} - -void cifs_put_tcp_super(struct super_block *sb) -{ - __cifs_put_super(sb); -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -int match_target_ip(struct TCP_Server_Info *server, - const char *share, size_t share_len, - bool *result) -{ - int rc; - char *target, *tip = NULL; - struct sockaddr tipaddr; - - *result = false; - - target = kzalloc(share_len + 3, GFP_KERNEL); - if (!target) { - rc = -ENOMEM; - goto out; - } - - scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share); - - cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2); - - rc = dns_resolve_server_name_to_ip(target, &tip, NULL); - if (rc < 0) - goto out; - - cifs_dbg(FYI, "%s: target ip: %s\n", __func__, tip); - - if (!cifs_convert_address(&tipaddr, tip, strlen(tip))) { - cifs_dbg(VFS, "%s: failed to convert target ip address\n", - __func__); - rc = -EINVAL; - goto out; - } - - *result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, - &tipaddr); - cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result); - rc = 0; - -out: - kfree(target); - kfree(tip); - - return rc; -} - -int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix) -{ - kfree(cifs_sb->prepath); - - if (prefix && *prefix) { - cifs_sb->prepath = cifs_sanitize_prepath(prefix, GFP_ATOMIC); - if (!cifs_sb->prepath) - return -ENOMEM; - - convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); - } else - cifs_sb->prepath = NULL; - - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - return 0; -} - -/* - * Handle weird Windows SMB server behaviour. It responds with - * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for - * "\\\" DFS reference, where contains - * non-ASCII unicode symbols. - */ -int cifs_inval_name_dfs_link_error(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - bool *islink) -{ - struct cifs_ses *ses = tcon->ses; - size_t len; - char *path; - char *ref_path; - - *islink = false; - - /* - * Fast path - skip check when @full_path doesn't have a prefix path to - * look up or tcon is not DFS. - */ - if (strlen(full_path) < 2 || !cifs_sb || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || - !is_tcon_dfs(tcon) || !ses->server->origin_fullpath) - return 0; - - /* - * Slow path - tcon is DFS and @full_path has prefix path, so attempt - * to get a referral to figure out whether it is an DFS link. - */ - len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1; - path = kmalloc(len, GFP_KERNEL); - if (!path) - return -ENOMEM; - - scnprintf(path, len, "%s%s", tcon->tree_name, full_path); - ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - kfree(path); - - if (IS_ERR(ref_path)) { - if (PTR_ERR(ref_path) != -EINVAL) - return PTR_ERR(ref_path); - } else { - struct dfs_info3_param *refs = NULL; - int num_refs = 0; - - /* - * XXX: we are not using dfs_cache_find() here because we might - * end filling all the DFS cache and thus potentially - * removing cached DFS targets that the client would eventually - * need during failover. - */ - if (ses->server->ops->get_dfs_refer && - !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs, - &num_refs, cifs_sb->local_nls, - cifs_remap(cifs_sb))) - *islink = refs[0].server_type == DFS_TYPE_LINK; - free_dfs_info_array(refs, num_refs); - kfree(ref_path); - } - return 0; -} -#endif - -int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry) -{ - int timeout = 10; - int rc; - - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - return 0; - } - timeout *= server->nr_targets; - spin_unlock(&server->srv_lock); - - /* - * Give demultiplex thread up to 10 seconds to each target available for - * reconnect -- should be greater than cifs socket timeout which is 7 - * seconds. - * - * On "soft" mounts we wait once. Hard mounts keep retrying until - * process is killed or server comes back on-line. - */ - do { - rc = wait_event_interruptible_timeout(server->response_q, - (server->tcpStatus != CifsNeedReconnect), - timeout * HZ); - if (rc < 0) { - cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", - __func__); - return -ERESTARTSYS; - } - - /* are we still trying to reconnect? */ - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - return 0; - } - spin_unlock(&server->srv_lock); - } while (retry); - - cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); - return -EHOSTDOWN; -} diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c deleted file mode 100644 index 147d9409252c..000000000000 --- a/fs/cifs/netlink.c +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Netlink routines for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#include -#include - -#include "netlink.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifs_swn.h" - -static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = { - [CIFS_GENL_ATTR_SWN_REGISTRATION_ID] = { .type = NLA_U32 }, - [CIFS_GENL_ATTR_SWN_NET_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_SHARE_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_IP] = { .len = sizeof(struct sockaddr_storage) }, - [CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_IP_NOTIFY] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_KRB_AUTH] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_USER_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_PASSWORD] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_DOMAIN_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE] = { .type = NLA_U32 }, - [CIFS_GENL_ATTR_SWN_RESOURCE_STATE] = { .type = NLA_U32 }, - [CIFS_GENL_ATTR_SWN_RESOURCE_NAME] = { .type = NLA_STRING}, -}; - -static const struct genl_ops cifs_genl_ops[] = { - { - .cmd = CIFS_GENL_CMD_SWN_NOTIFY, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = cifs_swn_notify, - }, -}; - -static const struct genl_multicast_group cifs_genl_mcgrps[] = { - [CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME }, -}; - -struct genl_family cifs_genl_family = { - .name = CIFS_GENL_NAME, - .version = CIFS_GENL_VERSION, - .hdrsize = 0, - .maxattr = CIFS_GENL_ATTR_MAX, - .module = THIS_MODULE, - .policy = cifs_genl_policy, - .ops = cifs_genl_ops, - .n_ops = ARRAY_SIZE(cifs_genl_ops), - .resv_start_op = CIFS_GENL_CMD_SWN_NOTIFY + 1, - .mcgrps = cifs_genl_mcgrps, - .n_mcgrps = ARRAY_SIZE(cifs_genl_mcgrps), -}; - -/** - * cifs_genl_init - Register generic netlink family - * - * Return zero if initialized successfully, otherwise non-zero. - */ -int cifs_genl_init(void) -{ - int ret; - - ret = genl_register_family(&cifs_genl_family); - if (ret < 0) { - cifs_dbg(VFS, "%s: failed to register netlink family\n", - __func__); - return ret; - } - - return 0; -} - -/** - * cifs_genl_exit - Unregister generic netlink family - */ -void cifs_genl_exit(void) -{ - int ret; - - ret = genl_unregister_family(&cifs_genl_family); - if (ret < 0) { - cifs_dbg(VFS, "%s: failed to unregister netlink family\n", - __func__); - } -} diff --git a/fs/cifs/netlink.h b/fs/cifs/netlink.h deleted file mode 100644 index e2fa8ed24c54..000000000000 --- a/fs/cifs/netlink.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Netlink routines for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#ifndef _CIFS_NETLINK_H -#define _CIFS_NETLINK_H - -extern struct genl_family cifs_genl_family; - -extern int cifs_genl_init(void); -extern void cifs_genl_exit(void); - -#endif /* _CIFS_NETLINK_H */ diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c deleted file mode 100644 index 1b52e6ac431c..000000000000 --- a/fs/cifs/netmisc.c +++ /dev/null @@ -1,1021 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Error mapping routines from Samba libsmb/errormap.c - * Copyright (C) Andrew Tridgell 2001 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "smberr.h" -#include "cifs_debug.h" -#include "nterr.h" - -struct smb_to_posix_error { - __u16 smb_err; - int posix_code; -}; - -static const struct smb_to_posix_error mapping_table_ERRDOS[] = { - {ERRbadfunc, -EINVAL}, - {ERRbadfile, -ENOENT}, - {ERRbadpath, -ENOTDIR}, - {ERRnofids, -EMFILE}, - {ERRnoaccess, -EACCES}, - {ERRbadfid, -EBADF}, - {ERRbadmcb, -EIO}, - {ERRnomem, -EREMOTEIO}, - {ERRbadmem, -EFAULT}, - {ERRbadenv, -EFAULT}, - {ERRbadformat, -EINVAL}, - {ERRbadaccess, -EACCES}, - {ERRbaddata, -EIO}, - {ERRbaddrive, -ENXIO}, - {ERRremcd, -EACCES}, - {ERRdiffdevice, -EXDEV}, - {ERRnofiles, -ENOENT}, - {ERRwriteprot, -EROFS}, - {ERRbadshare, -EBUSY}, - {ERRlock, -EACCES}, - {ERRunsup, -EINVAL}, - {ERRnosuchshare, -ENXIO}, - {ERRfilexists, -EEXIST}, - {ERRinvparm, -EINVAL}, - {ERRdiskfull, -ENOSPC}, - {ERRinvname, -ENOENT}, - {ERRinvlevel, -EOPNOTSUPP}, - {ERRdirnotempty, -ENOTEMPTY}, - {ERRnotlocked, -ENOLCK}, - {ERRcancelviolation, -ENOLCK}, - {ERRalreadyexists, -EEXIST}, - {ERRmoredata, -EOVERFLOW}, - {ERReasnotsupported, -EOPNOTSUPP}, - {ErrQuota, -EDQUOT}, - {ErrNotALink, -ENOLINK}, - {ERRnetlogonNotStarted, -ENOPROTOOPT}, - {ERRsymlink, -EOPNOTSUPP}, - {ErrTooManyLinks, -EMLINK}, - {0, 0} -}; - -static const struct smb_to_posix_error mapping_table_ERRSRV[] = { - {ERRerror, -EIO}, - {ERRbadpw, -EACCES}, /* was EPERM */ - {ERRbadtype, -EREMOTE}, - {ERRaccess, -EACCES}, - {ERRinvtid, -ENXIO}, - {ERRinvnetname, -ENXIO}, - {ERRinvdevice, -ENXIO}, - {ERRqfull, -ENOSPC}, - {ERRqtoobig, -ENOSPC}, - {ERRqeof, -EIO}, - {ERRinvpfid, -EBADF}, - {ERRsmbcmd, -EBADRQC}, - {ERRsrverror, -EIO}, - {ERRbadBID, -EIO}, - {ERRfilespecs, -EINVAL}, - {ERRbadLink, -EIO}, - {ERRbadpermits, -EINVAL}, - {ERRbadPID, -ESRCH}, - {ERRsetattrmode, -EINVAL}, - {ERRpaused, -EHOSTDOWN}, - {ERRmsgoff, -EHOSTDOWN}, - {ERRnoroom, -ENOSPC}, - {ERRrmuns, -EUSERS}, - {ERRtimeout, -ETIME}, - {ERRnoresource, -EREMOTEIO}, - {ERRtoomanyuids, -EUSERS}, - {ERRbaduid, -EACCES}, - {ERRusempx, -EIO}, - {ERRusestd, -EIO}, - {ERR_NOTIFY_ENUM_DIR, -ENOBUFS}, - {ERRnoSuchUser, -EACCES}, -/* {ERRaccountexpired, -EACCES}, - {ERRbadclient, -EACCES}, - {ERRbadLogonTime, -EACCES}, - {ERRpasswordExpired, -EACCES},*/ - {ERRaccountexpired, -EKEYEXPIRED}, - {ERRbadclient, -EACCES}, - {ERRbadLogonTime, -EACCES}, - {ERRpasswordExpired, -EKEYEXPIRED}, - - {ERRnosupport, -EINVAL}, - {0, 0} -}; - -/* - * Convert a string containing text IPv4 or IPv6 address to binary form. - * - * Returns 0 on failure. - */ -static int -cifs_inet_pton(const int address_family, const char *cp, int len, void *dst) -{ - int ret = 0; - - /* calculate length by finding first slash or NULL */ - if (address_family == AF_INET) - ret = in4_pton(cp, len, dst, '\\', NULL); - else if (address_family == AF_INET6) - ret = in6_pton(cp, len, dst , '\\', NULL); - - cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n", - ret, len, len, cp); - if (ret > 0) - ret = 1; - return ret; -} - -/* - * Try to convert a string to an IPv4 address and then attempt to convert - * it to an IPv6 address if that fails. Set the family field if either - * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to - * treat the part following it as a numeric sin6_scope_id. - * - * Returns 0 on failure. - */ -int -cifs_convert_address(struct sockaddr *dst, const char *src, int len) -{ - int rc, alen, slen; - const char *pct; - char scope_id[13]; - struct sockaddr_in *s4 = (struct sockaddr_in *) dst; - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; - - /* IPv4 address */ - if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) { - s4->sin_family = AF_INET; - return 1; - } - - /* attempt to exclude the scope ID from the address part */ - pct = memchr(src, '%', len); - alen = pct ? pct - src : len; - - rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr); - if (!rc) - return rc; - - s6->sin6_family = AF_INET6; - if (pct) { - /* grab the scope ID */ - slen = len - (alen + 1); - if (slen <= 0 || slen > 12) - return 0; - memcpy(scope_id, pct + 1, slen); - scope_id[slen] = '\0'; - - rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id); - rc = (rc == 0) ? 1 : 0; - } - - return rc; -} - -void -cifs_set_port(struct sockaddr *addr, const unsigned short int port) -{ - switch (addr->sa_family) { - case AF_INET: - ((struct sockaddr_in *)addr)->sin_port = htons(port); - break; - case AF_INET6: - ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); - break; - } -} - -/***************************************************************************** -convert a NT status code to a dos class/code - *****************************************************************************/ -/* NT status -> dos error map */ -static const struct { - __u8 dos_class; - __u16 dos_code; - __u32 ntstatus; -} ntstatus_to_dos_map[] = { - { - ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { - ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { - ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, { - ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { - ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { - ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { - ERRDOS, 87, NT_STATUS_INVALID_CID}, { - ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { - ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { - ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { - ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { - ERRDOS, 38, NT_STATUS_END_OF_FILE}, { - ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { - ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { - ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, -/* { This NT error code was 'sqashed' - from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK - during the session setup } */ - { - ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { - ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { - ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { - ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { - ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { - ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { - ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { - ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, -/* { This NT error code was 'sqashed' - from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { - ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { - ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { - ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { - ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { - ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { - ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { - ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { - ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { - /* mapping changed since shell does lookup on * expects FileNotFound */ - ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { - ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { - ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { - ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { - ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { - ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { - ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { - ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { - ERRDOS, 23, NT_STATUS_DATA_ERROR}, { - ERRDOS, 23, NT_STATUS_CRC_ERROR}, { - ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { - ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { - ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { - ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { - ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { - ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { - ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { - ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { - ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { - ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { - ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { - ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { - ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { - ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { - ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { - ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { - ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { - ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { - ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { - ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { - ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { - ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { - ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { - ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { - ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, -/* { This NT error code was 'sqashed' - from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ - ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, -/* { This NT error code was 'sqashed' - from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { - ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { - ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { - ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { - ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { - ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { - ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { - ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { - ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { - ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { - ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { - ERRDOS, 112, NT_STATUS_DISK_FULL}, { - ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { - ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { - ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { - ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { - ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { - ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { - ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { - ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { - ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { - ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, -/* { This NT error code was 'sqashed' - from NT_STATUS_INSUFFICIENT_RESOURCES to - NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */ - { - ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { - ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { - ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { - ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { - ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { - ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { - ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { - ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { - ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { - ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { - ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { - ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { - ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { - ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { - ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { - ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { - ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { - ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { - ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { - ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { - ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { - ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { - ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { - ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { - ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { - ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { - ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { - ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { - ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { - ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { - ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { - ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { - ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { - ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { - ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { - ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { - ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { - ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { - ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { - ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { - ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { - ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { - ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { - ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { - ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { - ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { - ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { - ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { - ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { - ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { - ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { - ERRDOS, 203, 0xc0000100}, { - ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { - ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { - ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { - ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { - ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { - ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { - ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { - ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { - ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { - ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { - ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { - ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { - ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { - ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { - ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { - ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { - ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { - ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { - ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { - ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { - ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { - ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { - ERRDOS, 59, NT_STATUS_LINK_FAILED}, { - ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { - ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { - ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { - ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { - ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { - ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { - ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { - ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { - ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { - ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { - ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { - ERRHRD, ERRgeneral, 0xc000016e}, { - ERRHRD, ERRgeneral, 0xc000016f}, { - ERRHRD, ERRgeneral, 0xc0000170}, { - ERRHRD, ERRgeneral, 0xc0000171}, { - ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { - ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { - ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { - ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { - ERRHRD, ERRgeneral, 0xc0000179}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { - ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { - ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { - ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { - ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { - ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { - ERRDOS, 19, NT_STATUS_TOO_LATE}, { - ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, -/* { This NT error code was 'sqashed' - from NT_STATUS_NO_TRUST_SAM_ACCOUNT to - NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { - ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { - ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { - ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { - ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, -/* { This NT error code was 'sqashed' - from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { - ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { - ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { - ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { - ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { - ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { - ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { - ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { - ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { - ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { - ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { - ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { - ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { - ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { - ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { - ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { - ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { - ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { - ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { - ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { - ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { - ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { - ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { - ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { - ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { - ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { - ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { - ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { - ERRHRD, ERRgeneral, 0xc000024a}, { - ERRHRD, ERRgeneral, 0xc000024b}, { - ERRHRD, ERRgeneral, 0xc000024c}, { - ERRHRD, ERRgeneral, 0xc000024d}, { - ERRHRD, ERRgeneral, 0xc000024e}, { - ERRHRD, ERRgeneral, 0xc000024f}, { - ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { - ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { - ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { - ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { - ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { - ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { - ERRHRD, ERRgeneral, 0xc000025d}, { - ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { - ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { - ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { - ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { - ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { - ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { - ERRDOS, 21, 0xc000026e}, { - ERRDOS, 161, 0xc0000281}, { - ERRDOS, ERRnoaccess, 0xc000028a}, { - ERRDOS, ERRnoaccess, 0xc000028b}, { - ERRHRD, ERRgeneral, 0xc000028c}, { - ERRDOS, ERRnoaccess, 0xc000028d}, { - ERRDOS, ERRnoaccess, 0xc000028e}, { - ERRDOS, ERRnoaccess, 0xc000028f}, { - ERRDOS, ERRnoaccess, 0xc0000290}, { - ERRDOS, ERRbadfunc, 0xc000029c}, { - ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { - ERRDOS, ERRinvlevel, 0x007c0001}, { - 0, 0, 0 } -}; - -/***************************************************************************** - Print an error message from the status code - *****************************************************************************/ -static void -cifs_print_status(__u32 status_code) -{ - int idx = 0; - - while (nt_errs[idx].nt_errstr != NULL) { - if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) == - (status_code & 0xFFFFFF)) { - pr_notice("Status code returned 0x%08x %s\n", - status_code, nt_errs[idx].nt_errstr); - } - idx++; - } - return; -} - - -static void -ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) -{ - int i; - if (ntstatus == 0) { - *eclass = 0; - *ecode = 0; - return; - } - for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { - if (ntstatus == ntstatus_to_dos_map[i].ntstatus) { - *eclass = ntstatus_to_dos_map[i].dos_class; - *ecode = ntstatus_to_dos_map[i].dos_code; - return; - } - } - *eclass = ERRHRD; - *ecode = ERRgeneral; -} - -int -map_smb_to_linux_error(char *buf, bool logErr) -{ - struct smb_hdr *smb = (struct smb_hdr *)buf; - unsigned int i; - int rc = -EIO; /* if transport error smb error may not be set */ - __u8 smberrclass; - __u16 smberrcode; - - /* BB if NT Status codes - map NT BB */ - - /* old style smb error codes */ - if (smb->Status.CifsError == 0) - return 0; - - if (smb->Flags2 & SMBFLG2_ERR_STATUS) { - /* translate the newer STATUS codes to old style SMB errors - * and then to POSIX errors */ - __u32 err = le32_to_cpu(smb->Status.CifsError); - if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED))) - cifs_print_status(err); - else if (cifsFYI & CIFS_RC) - cifs_print_status(err); - ntstatus_to_dos(err, &smberrclass, &smberrcode); - } else { - smberrclass = smb->Status.DosError.ErrorClass; - smberrcode = le16_to_cpu(smb->Status.DosError.Error); - } - - /* old style errors */ - - /* DOS class smb error codes - map DOS */ - if (smberrclass == ERRDOS) { - /* 1 byte field no need to byte reverse */ - for (i = 0; - i < - sizeof(mapping_table_ERRDOS) / - sizeof(struct smb_to_posix_error); i++) { - if (mapping_table_ERRDOS[i].smb_err == 0) - break; - else if (mapping_table_ERRDOS[i].smb_err == - smberrcode) { - rc = mapping_table_ERRDOS[i].posix_code; - break; - } - /* else try next error mapping one to see if match */ - } - } else if (smberrclass == ERRSRV) { - /* server class of error codes */ - for (i = 0; - i < - sizeof(mapping_table_ERRSRV) / - sizeof(struct smb_to_posix_error); i++) { - if (mapping_table_ERRSRV[i].smb_err == 0) - break; - else if (mapping_table_ERRSRV[i].smb_err == - smberrcode) { - rc = mapping_table_ERRSRV[i].posix_code; - break; - } - /* else try next error mapping to see if match */ - } - } - /* else ERRHRD class errors or junk - return EIO */ - - cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", - le32_to_cpu(smb->Status.CifsError), rc); - - /* generic corrective action e.g. reconnect SMB session on - * ERRbaduid could be added */ - - return rc; -} - -int -map_and_check_smb_error(struct mid_q_entry *mid, bool logErr) -{ - int rc; - struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; - - rc = map_smb_to_linux_error((char *)smb, logErr); - if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { - /* possible ERRBaduid */ - __u8 class = smb->Status.DosError.ErrorClass; - __u16 code = le16_to_cpu(smb->Status.DosError.Error); - - /* switch can be used to handle different errors */ - if (class == ERRSRV && code == ERRbaduid) { - cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", - code); - cifs_signal_cifsd_for_reconnect(mid->server, false); - } - } - - return rc; -} - - -/* - * calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message - */ -unsigned int -smbCalcSize(void *buf) -{ - struct smb_hdr *ptr = buf; - return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + - 2 /* size of the bcc field */ + get_bcc(ptr)); -} - -/* The following are taken from fs/ntfs/util.c */ - -#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) - -/* - * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) - * into Unix UTC (based 1970-01-01, in seconds). - */ -struct timespec64 -cifs_NTtimeToUnix(__le64 ntutc) -{ - struct timespec64 ts; - /* BB what about the timezone? BB */ - - /* Subtract the NTFS time offset, then convert to 1s intervals. */ - s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; - u64 abs_t; - - /* - * Unfortunately can not use normal 64 bit division on 32 bit arch, but - * the alternative, do_div, does not work with negative numbers so have - * to special case them - */ - if (t < 0) { - abs_t = -t; - ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100); - ts.tv_nsec = -ts.tv_nsec; - ts.tv_sec = -abs_t; - } else { - abs_t = t; - ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100; - ts.tv_sec = abs_t; - } - - return ts; -} - -/* Convert the Unix UTC into NT UTC. */ -u64 -cifs_UnixTimeToNT(struct timespec64 t) -{ - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET; -} - -static const int total_days_of_prev_months[] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - -struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset) -{ - struct timespec64 ts; - time64_t sec, days; - int min, day, month, year; - u16 date = le16_to_cpu(le_date); - u16 time = le16_to_cpu(le_time); - SMB_TIME *st = (SMB_TIME *)&time; - SMB_DATE *sd = (SMB_DATE *)&date; - - cifs_dbg(FYI, "date %d time %d\n", date, time); - - sec = 2 * st->TwoSeconds; - min = st->Minutes; - if ((sec > 59) || (min > 59)) - cifs_dbg(VFS, "Invalid time min %d sec %lld\n", min, sec); - sec += (min * 60); - sec += 60 * 60 * st->Hours; - if (st->Hours > 24) - cifs_dbg(VFS, "Invalid hours %d\n", st->Hours); - day = sd->Day; - month = sd->Month; - if (day < 1 || day > 31 || month < 1 || month > 12) { - cifs_dbg(VFS, "Invalid date, month %d day: %d\n", month, day); - day = clamp(day, 1, 31); - month = clamp(month, 1, 12); - } - month -= 1; - days = day + total_days_of_prev_months[month]; - days += 3652; /* account for difference in days between 1980 and 1970 */ - year = sd->Year; - days += year * 365; - days += (year/4); /* leap year */ - /* generalized leap year calculation is more complex, ie no leap year - for years/100 except for years/400, but since the maximum number for DOS - year is 2**7, the last year is 1980+127, which means we need only - consider 2 special case years, ie the years 2000 and 2100, and only - adjust for the lack of leap year for the year 2100, as 2000 was a - leap year (divisable by 400) */ - if (year >= 120) /* the year 2100 */ - days = days - 1; /* do not count leap year for the year 2100 */ - - /* adjust for leap year where we are still before leap day */ - if (year != 120) - days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0); - sec += 24 * 60 * 60 * days; - - ts.tv_sec = sec + offset; - - /* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */ - - ts.tv_nsec = 0; - return ts; -} diff --git a/fs/cifs/nterr.c b/fs/cifs/nterr.c deleted file mode 100644 index 358a766375b4..000000000000 --- a/fs/cifs/nterr.c +++ /dev/null @@ -1,674 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Unix SMB/Netbios implementation. - * Version 1.9. - * RPC Pipe client / server routines - * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. - */ - -/* NT error codes - see nterr.h */ -#include -#include -#include "nterr.h" - -const struct nt_err_code_struct nt_errs[] = { - {"NT_STATUS_OK", NT_STATUS_OK}, - {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL}, - {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED}, - {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS}, - {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH}, - {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION}, - {"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW}, - {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR}, - {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA}, - {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE}, - {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK}, - {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC}, - {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID}, - {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED}, - {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER}, - {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE}, - {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE}, - {"NT_STATUS_INVALID_DEVICE_REQUEST", - NT_STATUS_INVALID_DEVICE_REQUEST}, - {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE}, - {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME}, - {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE}, - {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA}, - {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR}, - {"NT_STATUS_MORE_PROCESSING_REQUIRED", - NT_STATUS_MORE_PROCESSING_REQUIRED}, - {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY}, - {"NT_STATUS_CONFLICTING_ADDRESSES", - NT_STATUS_CONFLICTING_ADDRESSES}, - {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW}, - {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM}, - {"NT_STATUS_UNABLE_TO_DELETE_SECTION", - NT_STATUS_UNABLE_TO_DELETE_SECTION}, - {"NT_STATUS_INVALID_SYSTEM_SERVICE", - NT_STATUS_INVALID_SYSTEM_SERVICE}, - {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION}, - {"NT_STATUS_INVALID_LOCK_SEQUENCE", - NT_STATUS_INVALID_LOCK_SEQUENCE}, - {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE}, - {"NT_STATUS_INVALID_FILE_FOR_SECTION", - NT_STATUS_INVALID_FILE_FOR_SECTION}, - {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED}, - {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED}, - {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL}, - {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH}, - {"NT_STATUS_NONCONTINUABLE_EXCEPTION", - NT_STATUS_NONCONTINUABLE_EXCEPTION}, - {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION}, - {"NT_STATUS_UNWIND", NT_STATUS_UNWIND}, - {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK}, - {"NT_STATUS_INVALID_UNWIND_TARGET", - NT_STATUS_INVALID_UNWIND_TARGET}, - {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED}, - {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR}, - {"NT_STATUS_UNABLE_TO_DECOMMIT_VM", - NT_STATUS_UNABLE_TO_DECOMMIT_VM}, - {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED}, - {"NT_STATUS_INVALID_PORT_ATTRIBUTES", - NT_STATUS_INVALID_PORT_ATTRIBUTES}, - {"NT_STATUS_PORT_MESSAGE_TOO_LONG", - NT_STATUS_PORT_MESSAGE_TOO_LONG}, - {"NT_STATUS_INVALID_PARAMETER_MIX", - NT_STATUS_INVALID_PARAMETER_MIX}, - {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER}, - {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR}, - {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID}, - {"NT_STATUS_OBJECT_NAME_NOT_FOUND", - NT_STATUS_OBJECT_NAME_NOT_FOUND}, - {"NT_STATUS_OBJECT_NAME_COLLISION", - NT_STATUS_OBJECT_NAME_COLLISION}, - {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE}, - {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED}, - {"NT_STATUS_DEVICE_ALREADY_ATTACHED", - NT_STATUS_DEVICE_ALREADY_ATTACHED}, - {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID}, - {"NT_STATUS_OBJECT_PATH_NOT_FOUND", - NT_STATUS_OBJECT_PATH_NOT_FOUND}, - {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD", - NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, - {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN}, - {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR}, - {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR}, - {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR}, - {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG}, - {"NT_STATUS_PORT_CONNECTION_REFUSED", - NT_STATUS_PORT_CONNECTION_REFUSED}, - {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE}, - {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION}, - {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED}, - {"NT_STATUS_INVALID_PAGE_PROTECTION", - NT_STATUS_INVALID_PAGE_PROTECTION}, - {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED}, - {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", - NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, - {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET}, - {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE}, - {"NT_STATUS_SUSPEND_COUNT_EXCEEDED", - NT_STATUS_SUSPEND_COUNT_EXCEEDED}, - {"NT_STATUS_THREAD_IS_TERMINATING", - NT_STATUS_THREAD_IS_TERMINATING}, - {"NT_STATUS_BAD_WORKING_SET_LIMIT", - NT_STATUS_BAD_WORKING_SET_LIMIT}, - {"NT_STATUS_INCOMPATIBLE_FILE_MAP", - NT_STATUS_INCOMPATIBLE_FILE_MAP}, - {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION}, - {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED}, - {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE}, - {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY}, - {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE}, - {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR}, - {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT}, - {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED}, - {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING}, - {"NT_STATUS_CTL_FILE_NOT_SUPPORTED", - NT_STATUS_CTL_FILE_NOT_SUPPORTED}, - {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION}, - {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH}, - {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER}, - {"NT_STATUS_INVALID_PRIMARY_GROUP", - NT_STATUS_INVALID_PRIMARY_GROUP}, - {"NT_STATUS_NO_IMPERSONATION_TOKEN", - NT_STATUS_NO_IMPERSONATION_TOKEN}, - {"NT_STATUS_CANT_DISABLE_MANDATORY", - NT_STATUS_CANT_DISABLE_MANDATORY}, - {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS}, - {"NT_STATUS_NO_SUCH_LOGON_SESSION", - NT_STATUS_NO_SUCH_LOGON_SESSION}, - {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE}, - {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD}, - {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME}, - {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS}, - {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER}, - {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS}, - {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP}, - {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP}, - {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP}, - {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN}, - {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD}, - {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD}, - {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION}, - {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE}, - {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION}, - {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS}, - {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION}, - {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED}, - {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED}, - {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED}, - {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED", - NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, - {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED}, - {"NT_STATUS_INVALID_SUB_AUTHORITY", - NT_STATUS_INVALID_SUB_AUTHORITY}, - {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL}, - {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID}, - {"NT_STATUS_INVALID_SECURITY_DESCR", - NT_STATUS_INVALID_SECURITY_DESCR}, - {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND}, - {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT}, - {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN}, - {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL}, - {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED}, - {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL}, - {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED}, - {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED}, - {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED", - NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, - {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED}, - {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY}, - {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED}, - {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL}, - {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED}, - {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA}, - {"NT_STATUS_RESOURCE_DATA_NOT_FOUND", - NT_STATUS_RESOURCE_DATA_NOT_FOUND}, - {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND", - NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, - {"NT_STATUS_RESOURCE_NAME_NOT_FOUND", - NT_STATUS_RESOURCE_NAME_NOT_FOUND}, - {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED", - NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, - {"NT_STATUS_FLOAT_DENORMAL_OPERAND", - NT_STATUS_FLOAT_DENORMAL_OPERAND}, - {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, - {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT}, - {"NT_STATUS_FLOAT_INVALID_OPERATION", - NT_STATUS_FLOAT_INVALID_OPERATION}, - {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW}, - {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK}, - {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW}, - {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO", - NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, - {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW}, - {"NT_STATUS_PRIVILEGED_INSTRUCTION", - NT_STATUS_PRIVILEGED_INSTRUCTION}, - {"NT_STATUS_TOO_MANY_PAGING_FILES", - NT_STATUS_TOO_MANY_PAGING_FILES}, - {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID}, - {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED", - NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, - {"NT_STATUS_INSUFFICIENT_RESOURCES", - NT_STATUS_INSUFFICIENT_RESOURCES}, - {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND}, - {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR}, - {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED}, - {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE}, - {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE}, - {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED}, - {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA}, - {"NT_STATUS_MEDIA_WRITE_PROTECTED", - NT_STATUS_MEDIA_WRITE_PROTECTED}, - {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY}, - {"NT_STATUS_INVALID_GROUP_ATTRIBUTES", - NT_STATUS_INVALID_GROUP_ATTRIBUTES}, - {"NT_STATUS_BAD_IMPERSONATION_LEVEL", - NT_STATUS_BAD_IMPERSONATION_LEVEL}, - {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS}, - {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS}, - {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE}, - {"NT_STATUS_BAD_MASTER_BOOT_RECORD", - NT_STATUS_BAD_MASTER_BOOT_RECORD}, - {"NT_STATUS_INSTRUCTION_MISALIGNMENT", - NT_STATUS_INSTRUCTION_MISALIGNMENT}, - {"NT_STATUS_INSTANCE_NOT_AVAILABLE", - NT_STATUS_INSTANCE_NOT_AVAILABLE}, - {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE}, - {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE}, - {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY}, - {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION}, - {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED}, - {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING}, - {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED}, - {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING}, - {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE}, - {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT}, - {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED}, - {"NT_STATUS_PROFILING_NOT_STARTED", - NT_STATUS_PROFILING_NOT_STARTED}, - {"NT_STATUS_PROFILING_NOT_STOPPED", - NT_STATUS_PROFILING_NOT_STOPPED}, - {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET}, - {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY}, - {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED}, - {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING}, - {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME}, - {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH}, - {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY}, - {"NT_STATUS_DEVICE_DOES_NOT_EXIST", - NT_STATUS_DEVICE_DOES_NOT_EXIST}, - {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS}, - {"NT_STATUS_ADAPTER_HARDWARE_ERROR", - NT_STATUS_ADAPTER_HARDWARE_ERROR}, - {"NT_STATUS_INVALID_NETWORK_RESPONSE", - NT_STATUS_INVALID_NETWORK_RESPONSE}, - {"NT_STATUS_UNEXPECTED_NETWORK_ERROR", - NT_STATUS_UNEXPECTED_NETWORK_ERROR}, - {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER}, - {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL}, - {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE}, - {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED}, - {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED}, - {"NT_STATUS_NETWORK_ACCESS_DENIED", - NT_STATUS_NETWORK_ACCESS_DENIED}, - {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE}, - {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME}, - {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES}, - {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS}, - {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED}, - {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED}, - {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED}, - {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT}, - {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT}, - {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE}, - {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED}, - {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", - NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, - {"NT_STATUS_NO_SECURITY_ON_OBJECT", - NT_STATUS_NO_SECURITY_ON_OBJECT}, - {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT}, - {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY}, - {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO", - NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, - {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF}, - {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE}, - {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE}, - {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE}, - {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN}, - {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS}, - {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED", - NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, - {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED}, - {"NT_STATUS_INVALID_OPLOCK_PROTOCOL", - NT_STATUS_INVALID_OPLOCK_PROTOCOL}, - {"NT_STATUS_INTERNAL_DB_CORRUPTION", - NT_STATUS_INTERNAL_DB_CORRUPTION}, - {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR}, - {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED}, - {"NT_STATUS_BAD_DESCRIPTOR_FORMAT", - NT_STATUS_BAD_DESCRIPTOR_FORMAT}, - {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER}, - {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR}, - {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR", - NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, - {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR", - NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, - {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", - NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, - {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS}, - {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS}, - {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1}, - {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2}, - {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3}, - {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4}, - {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5}, - {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6}, - {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7}, - {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8}, - {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9}, - {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10}, - {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11}, - {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12}, - {"NT_STATUS_REDIRECTOR_NOT_STARTED", - NT_STATUS_REDIRECTOR_NOT_STARTED}, - {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED}, - {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW}, - {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE}, - {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE}, - {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY}, - {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR}, - {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY}, - {"NT_STATUS_BAD_LOGON_SESSION_STATE", - NT_STATUS_BAD_LOGON_SESSION_STATE}, - {"NT_STATUS_LOGON_SESSION_COLLISION", - NT_STATUS_LOGON_SESSION_COLLISION}, - {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG}, - {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN}, - {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE}, - {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND}, - {"NT_STATUS_PROCESS_IS_TERMINATING", - NT_STATUS_PROCESS_IS_TERMINATING}, - {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE}, - {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION}, - {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE}, - {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED}, - {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT}, - {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST}, - {"NT_STATUS_ABIOS_LID_ALREADY_OWNED", - NT_STATUS_ABIOS_LID_ALREADY_OWNED}, - {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER}, - {"NT_STATUS_ABIOS_INVALID_COMMAND", - NT_STATUS_ABIOS_INVALID_COMMAND}, - {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID}, - {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", - NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, - {"NT_STATUS_ABIOS_INVALID_SELECTOR", - NT_STATUS_ABIOS_INVALID_SELECTOR}, - {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT}, - {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE}, - {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET}, - {"NT_STATUS_INVALID_LDT_DESCRIPTOR", - NT_STATUS_INVALID_LDT_DESCRIPTOR}, - {"NT_STATUS_INVALID_IMAGE_NE_FORMAT", - NT_STATUS_INVALID_IMAGE_NE_FORMAT}, - {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE}, - {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE}, - {"NT_STATUS_MAPPED_FILE_SIZE_ZERO", - NT_STATUS_MAPPED_FILE_SIZE_ZERO}, - {"NT_STATUS_TOO_MANY_OPENED_FILES", - NT_STATUS_TOO_MANY_OPENED_FILES}, - {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED}, - {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE}, - {"NT_STATUS_INVALID_COMPUTER_NAME", - NT_STATUS_INVALID_COMPUTER_NAME}, - {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED}, - {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT}, - {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP}, - {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER}, - {"NT_STATUS_MEMBERS_PRIMARY_GROUP", - NT_STATUS_MEMBERS_PRIMARY_GROUP}, - {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED}, - {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS}, - {"NT_STATUS_THREAD_NOT_IN_PROCESS", - NT_STATUS_THREAD_NOT_IN_PROCESS}, - {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE}, - {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", - NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, - {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT}, - {"NT_STATUS_INVALID_IMAGE_LE_FORMAT", - NT_STATUS_INVALID_IMAGE_LE_FORMAT}, - {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ}, - {"NT_STATUS_INVALID_IMAGE_PROTECT", - NT_STATUS_INVALID_IMAGE_PROTECT}, - {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16}, - {"NT_STATUS_LOGON_SERVER_CONFLICT", - NT_STATUS_LOGON_SERVER_CONFLICT}, - {"NT_STATUS_TIME_DIFFERENCE_AT_DC", - NT_STATUS_TIME_DIFFERENCE_AT_DC}, - {"NT_STATUS_SYNCHRONIZATION_REQUIRED", - NT_STATUS_SYNCHRONIZATION_REQUIRED}, - {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND}, - {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED}, - {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED}, - {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND}, - {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND}, - {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT}, - {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT}, - {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT}, - {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES}, - {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED}, - {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT}, - {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION}, - {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS}, - {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED}, - {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE}, - {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION}, - {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE}, - {"NT_STATUS_PAGEFILE_CREATE_FAILED", - NT_STATUS_PAGEFILE_CREATE_FAILED}, - {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE}, - {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL}, - {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE}, - {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT", - NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, - {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN}, - {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT}, - {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED}, - {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR}, - {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME}, - {"NT_STATUS_SERIAL_NO_DEVICE_INITED", - NT_STATUS_SERIAL_NO_DEVICE_INITED}, - {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS}, - {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS}, - {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS}, - {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS}, - {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED}, - {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS}, - {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG}, - {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR}, - {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE}, - {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS}, - {"NT_STATUS_LOGON_TYPE_NOT_GRANTED", - NT_STATUS_LOGON_TYPE_NOT_GRANTED}, - {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE}, - {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", - NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, - {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", - NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, - {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER}, - {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY", - NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, - {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER}, - {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER}, - {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER}, - {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME}, - {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", - NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, - {"NT_STATUS_FLOPPY_WRONG_CYLINDER", - NT_STATUS_FLOPPY_WRONG_CYLINDER}, - {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR}, - {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS}, - {"NT_STATUS_DISK_RECALIBRATE_FAILED", - NT_STATUS_DISK_RECALIBRATE_FAILED}, - {"NT_STATUS_DISK_OPERATION_FAILED", - NT_STATUS_DISK_OPERATION_FAILED}, - {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED}, - {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY}, - {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING}, - {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE}, - {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH}, - {"NT_STATUS_DEVICE_NOT_PARTITIONED", - NT_STATUS_DEVICE_NOT_PARTITIONED}, - {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA}, - {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", - NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, - {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW}, - {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA}, - {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER}, - {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER}, - {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED}, - {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE}, - {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS}, - {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", - NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, - {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN}, - {"NT_STATUS_CHILD_MUST_BE_VOLATILE", - NT_STATUS_CHILD_MUST_BE_VOLATILE}, - {"NT_STATUS_DEVICE_CONFIGURATION_ERROR", - NT_STATUS_DEVICE_CONFIGURATION_ERROR}, - {"NT_STATUS_DRIVER_INTERNAL_ERROR", - NT_STATUS_DRIVER_INTERNAL_ERROR}, - {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE}, - {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR}, - {"NT_STATUS_DEVICE_PROTOCOL_ERROR", - NT_STATUS_DEVICE_PROTOCOL_ERROR}, - {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER}, - {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL}, - {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE}, - {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET}, - {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT}, - {"NT_STATUS_TRUSTED_DOMAIN_FAILURE", - NT_STATUS_TRUSTED_DOMAIN_FAILURE}, - {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", - NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, - {"NT_STATUS_EVENTLOG_FILE_CORRUPT", - NT_STATUS_EVENTLOG_FILE_CORRUPT}, - {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START}, - {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE}, - {"NT_STATUS_MUTANT_LIMIT_EXCEEDED", - NT_STATUS_MUTANT_LIMIT_EXCEEDED}, - {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED}, - {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED}, - {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK}, - {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", - NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, - {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT}, - {"NT_STATUS_EVENTLOG_FILE_CHANGED", - NT_STATUS_EVENTLOG_FILE_CHANGED}, - {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, - {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, - {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, - {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT", - NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, - {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED}, - {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY}, - {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED}, - {"NT_STATUS_RESOURCE_LANG_NOT_FOUND", - NT_STATUS_RESOURCE_LANG_NOT_FOUND}, - {"NT_STATUS_INSUFF_SERVER_RESOURCES", - NT_STATUS_INSUFF_SERVER_RESOURCES}, - {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE}, - {"NT_STATUS_INVALID_ADDRESS_COMPONENT", - NT_STATUS_INVALID_ADDRESS_COMPONENT}, - {"NT_STATUS_INVALID_ADDRESS_WILDCARD", - NT_STATUS_INVALID_ADDRESS_WILDCARD}, - {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES}, - {"NT_STATUS_ADDRESS_ALREADY_EXISTS", - NT_STATUS_ADDRESS_ALREADY_EXISTS}, - {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED}, - {"NT_STATUS_CONNECTION_DISCONNECTED", - NT_STATUS_CONNECTION_DISCONNECTED}, - {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET}, - {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES}, - {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED}, - {"NT_STATUS_TRANSACTION_TIMED_OUT", - NT_STATUS_TRANSACTION_TIMED_OUT}, - {"NT_STATUS_TRANSACTION_NO_RELEASE", - NT_STATUS_TRANSACTION_NO_RELEASE}, - {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH}, - {"NT_STATUS_TRANSACTION_RESPONDED", - NT_STATUS_TRANSACTION_RESPONDED}, - {"NT_STATUS_TRANSACTION_INVALID_ID", - NT_STATUS_TRANSACTION_INVALID_ID}, - {"NT_STATUS_TRANSACTION_INVALID_TYPE", - NT_STATUS_TRANSACTION_INVALID_TYPE}, - {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION}, - {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION}, - {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", - NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, - {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED}, - {"NT_STATUS_SYSTEM_PROCESS_TERMINATED", - NT_STATUS_SYSTEM_PROCESS_TERMINATED}, - {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED}, - {"NT_STATUS_NO_BROWSER_SERVERS_FOUND", - NT_STATUS_NO_BROWSER_SERVERS_FOUND}, - {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR}, - {"NT_STATUS_DRIVER_CANCEL_TIMEOUT", - NT_STATUS_DRIVER_CANCEL_TIMEOUT}, - {"NT_STATUS_REPLY_MESSAGE_MISMATCH", - NT_STATUS_REPLY_MESSAGE_MISMATCH}, - {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT}, - {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH", - NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, - {"NT_STATUS_LOST_WRITEBEHIND_DATA", - NT_STATUS_LOST_WRITEBEHIND_DATA}, - {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", - NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, - {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE}, - {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND}, - {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM}, - {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE}, - {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ}, - {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK}, - {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID}, - {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS}, - {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE}, - {"NT_STATUS_RETRY", NT_STATUS_RETRY}, - {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE}, - {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET}, - {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND}, - {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW}, - {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT}, - {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", - NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, - {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT}, - {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE}, - {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED}, - {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT}, - {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", - NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, - {"NT_STATUS_ADDRESS_NOT_ASSOCIATED", - NT_STATUS_ADDRESS_NOT_ASSOCIATED}, - {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID}, - {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE}, - {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE}, - {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE}, - {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE}, - {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE}, - {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED}, - {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED}, - {"NT_STATUS_BAD_COMPRESSION_BUFFER", - NT_STATUS_BAD_COMPRESSION_BUFFER}, - {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE}, - {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED}, - {"NT_STATUS_TIMER_RESOLUTION_NOT_SET", - NT_STATUS_TIMER_RESOLUTION_NOT_SET}, - {"NT_STATUS_CONNECTION_COUNT_LIMIT", - NT_STATUS_CONNECTION_COUNT_LIMIT}, - {"NT_STATUS_LOGIN_TIME_RESTRICTION", - NT_STATUS_LOGIN_TIME_RESTRICTION}, - {"NT_STATUS_LOGIN_WKSTA_RESTRICTION", - NT_STATUS_LOGIN_WKSTA_RESTRICTION}, - {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH}, - {"NT_STATUS_INSUFFICIENT_LOGON_INFO", - NT_STATUS_INSUFFICIENT_LOGON_INFO}, - {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT}, - {"NT_STATUS_BAD_SERVICE_ENTRYPOINT", - NT_STATUS_BAD_SERVICE_ENTRYPOINT}, - {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST}, - {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1}, - {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2}, - {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT}, - {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED}, - {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE}, - {"NT_STATUS_LICENSE_QUOTA_EXCEEDED", - NT_STATUS_LICENSE_QUOTA_EXCEEDED}, - {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT}, - {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT}, - {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT}, - {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE}, - {"NT_STATUS_UNSUPPORTED_COMPRESSION", - NT_STATUS_UNSUPPORTED_COMPRESSION}, - {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE}, - {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", - NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, - {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", - NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, - {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", - NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, - {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED}, - {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS}, - {"NT_STATUS_QUOTA_LIST_INCONSISTENT", - NT_STATUS_QUOTA_LIST_INCONSISTENT}, - {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE}, - {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES}, - {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, - {"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED}, - {NULL, 0} -}; diff --git a/fs/cifs/nterr.h b/fs/cifs/nterr.h deleted file mode 100644 index edd4741cab0a..000000000000 --- a/fs/cifs/nterr.h +++ /dev/null @@ -1,551 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NT error code constants - Copyright (C) Andrew Tridgell 1992-2000 - Copyright (C) John H Terpstra 1996-2000 - Copyright (C) Luke Kenneth Casson Leighton 1996-2000 - Copyright (C) Paul Ashton 1998-2000 - -*/ - - - -#ifndef _NTERR_H -#define _NTERR_H - -struct nt_err_code_struct { - char *nt_errstr; - __u32 nt_errcode; -}; - -extern const struct nt_err_code_struct nt_errs[]; - -/* Win32 Status codes. */ -#define NT_STATUS_MORE_ENTRIES 0x0105 -#define NT_ERROR_INVALID_PARAMETER 0x0057 -#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a -#define NT_STATUS_1804 0x070c -#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c - -/* - * Win32 Error codes extracted using a loop in smbclient then printing a netmon - * sniff to a file. - */ - -#define NT_STATUS_OK 0x0000 -#define NT_STATUS_SOME_UNMAPPED 0x0107 -#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 -#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a -#define NT_STATUS_MEDIA_CHANGED 0x8000001c -#define NT_STATUS_END_OF_MEDIA 0x8000001e -#define NT_STATUS_MEDIA_CHECK 0x80000020 -#define NT_STATUS_NO_DATA_DETECTED 0x8000001c -#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d -#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 -#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 -#define NT_STATUS_UNSUCCESSFUL 0xC0000000 | 0x0001 -#define NT_STATUS_NOT_IMPLEMENTED 0xC0000000 | 0x0002 -#define NT_STATUS_INVALID_INFO_CLASS 0xC0000000 | 0x0003 -#define NT_STATUS_INFO_LENGTH_MISMATCH 0xC0000000 | 0x0004 -#define NT_STATUS_ACCESS_VIOLATION 0xC0000000 | 0x0005 -#define NT_STATUS_IN_PAGE_ERROR 0xC0000000 | 0x0006 -#define NT_STATUS_PAGEFILE_QUOTA 0xC0000000 | 0x0007 -#define NT_STATUS_INVALID_HANDLE 0xC0000000 | 0x0008 -#define NT_STATUS_BAD_INITIAL_STACK 0xC0000000 | 0x0009 -#define NT_STATUS_BAD_INITIAL_PC 0xC0000000 | 0x000a -#define NT_STATUS_INVALID_CID 0xC0000000 | 0x000b -#define NT_STATUS_TIMER_NOT_CANCELED 0xC0000000 | 0x000c -#define NT_STATUS_INVALID_PARAMETER 0xC0000000 | 0x000d -#define NT_STATUS_NO_SUCH_DEVICE 0xC0000000 | 0x000e -#define NT_STATUS_NO_SUCH_FILE 0xC0000000 | 0x000f -#define NT_STATUS_INVALID_DEVICE_REQUEST 0xC0000000 | 0x0010 -#define NT_STATUS_END_OF_FILE 0xC0000000 | 0x0011 -#define NT_STATUS_WRONG_VOLUME 0xC0000000 | 0x0012 -#define NT_STATUS_NO_MEDIA_IN_DEVICE 0xC0000000 | 0x0013 -#define NT_STATUS_UNRECOGNIZED_MEDIA 0xC0000000 | 0x0014 -#define NT_STATUS_NONEXISTENT_SECTOR 0xC0000000 | 0x0015 -#define NT_STATUS_MORE_PROCESSING_REQUIRED 0xC0000000 | 0x0016 -#define NT_STATUS_NO_MEMORY 0xC0000000 | 0x0017 -#define NT_STATUS_CONFLICTING_ADDRESSES 0xC0000000 | 0x0018 -#define NT_STATUS_NOT_MAPPED_VIEW 0xC0000000 | 0x0019 -#define NT_STATUS_UNABLE_TO_FREE_VM 0x80000000 | 0x001a -#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0xC0000000 | 0x001b -#define NT_STATUS_INVALID_SYSTEM_SERVICE 0xC0000000 | 0x001c -#define NT_STATUS_ILLEGAL_INSTRUCTION 0xC0000000 | 0x001d -#define NT_STATUS_INVALID_LOCK_SEQUENCE 0xC0000000 | 0x001e -#define NT_STATUS_INVALID_VIEW_SIZE 0xC0000000 | 0x001f -#define NT_STATUS_INVALID_FILE_FOR_SECTION 0xC0000000 | 0x0020 -#define NT_STATUS_ALREADY_COMMITTED 0xC0000000 | 0x0021 -#define NT_STATUS_ACCESS_DENIED 0xC0000000 | 0x0022 -#define NT_STATUS_BUFFER_TOO_SMALL 0xC0000000 | 0x0023 -#define NT_STATUS_OBJECT_TYPE_MISMATCH 0xC0000000 | 0x0024 -#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0xC0000000 | 0x0025 -#define NT_STATUS_INVALID_DISPOSITION 0xC0000000 | 0x0026 -#define NT_STATUS_UNWIND 0xC0000000 | 0x0027 -#define NT_STATUS_BAD_STACK 0xC0000000 | 0x0028 -#define NT_STATUS_INVALID_UNWIND_TARGET 0xC0000000 | 0x0029 -#define NT_STATUS_NOT_LOCKED 0xC0000000 | 0x002a -#define NT_STATUS_PARITY_ERROR 0xC0000000 | 0x002b -#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0xC0000000 | 0x002c -#define NT_STATUS_NOT_COMMITTED 0xC0000000 | 0x002d -#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0xC0000000 | 0x002e -#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0xC0000000 | 0x002f -#define NT_STATUS_INVALID_PARAMETER_MIX 0xC0000000 | 0x0030 -#define NT_STATUS_INVALID_QUOTA_LOWER 0xC0000000 | 0x0031 -#define NT_STATUS_DISK_CORRUPT_ERROR 0xC0000000 | 0x0032 -#define NT_STATUS_OBJECT_NAME_INVALID 0xC0000000 | 0x0033 -#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000000 | 0x0034 -#define NT_STATUS_OBJECT_NAME_COLLISION 0xC0000000 | 0x0035 -#define NT_STATUS_HANDLE_NOT_WAITABLE 0xC0000000 | 0x0036 -#define NT_STATUS_PORT_DISCONNECTED 0xC0000000 | 0x0037 -#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0xC0000000 | 0x0038 -#define NT_STATUS_OBJECT_PATH_INVALID 0xC0000000 | 0x0039 -#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0xC0000000 | 0x003a -#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0xC0000000 | 0x003b -#define NT_STATUS_DATA_OVERRUN 0xC0000000 | 0x003c -#define NT_STATUS_DATA_LATE_ERROR 0xC0000000 | 0x003d -#define NT_STATUS_DATA_ERROR 0xC0000000 | 0x003e -#define NT_STATUS_CRC_ERROR 0xC0000000 | 0x003f -#define NT_STATUS_SECTION_TOO_BIG 0xC0000000 | 0x0040 -#define NT_STATUS_PORT_CONNECTION_REFUSED 0xC0000000 | 0x0041 -#define NT_STATUS_INVALID_PORT_HANDLE 0xC0000000 | 0x0042 -#define NT_STATUS_SHARING_VIOLATION 0xC0000000 | 0x0043 -#define NT_STATUS_QUOTA_EXCEEDED 0xC0000000 | 0x0044 -#define NT_STATUS_INVALID_PAGE_PROTECTION 0xC0000000 | 0x0045 -#define NT_STATUS_MUTANT_NOT_OWNED 0xC0000000 | 0x0046 -#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0xC0000000 | 0x0047 -#define NT_STATUS_PORT_ALREADY_SET 0xC0000000 | 0x0048 -#define NT_STATUS_SECTION_NOT_IMAGE 0xC0000000 | 0x0049 -#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0xC0000000 | 0x004a -#define NT_STATUS_THREAD_IS_TERMINATING 0xC0000000 | 0x004b -#define NT_STATUS_BAD_WORKING_SET_LIMIT 0xC0000000 | 0x004c -#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0xC0000000 | 0x004d -#define NT_STATUS_SECTION_PROTECTION 0xC0000000 | 0x004e -#define NT_STATUS_EAS_NOT_SUPPORTED 0xC0000000 | 0x004f -#define NT_STATUS_EA_TOO_LARGE 0xC0000000 | 0x0050 -#define NT_STATUS_NONEXISTENT_EA_ENTRY 0xC0000000 | 0x0051 -#define NT_STATUS_NO_EAS_ON_FILE 0xC0000000 | 0x0052 -#define NT_STATUS_EA_CORRUPT_ERROR 0xC0000000 | 0x0053 -#define NT_STATUS_FILE_LOCK_CONFLICT 0xC0000000 | 0x0054 -#define NT_STATUS_LOCK_NOT_GRANTED 0xC0000000 | 0x0055 -#define NT_STATUS_DELETE_PENDING 0xC0000000 | 0x0056 -#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0xC0000000 | 0x0057 -#define NT_STATUS_UNKNOWN_REVISION 0xC0000000 | 0x0058 -#define NT_STATUS_REVISION_MISMATCH 0xC0000000 | 0x0059 -#define NT_STATUS_INVALID_OWNER 0xC0000000 | 0x005a -#define NT_STATUS_INVALID_PRIMARY_GROUP 0xC0000000 | 0x005b -#define NT_STATUS_NO_IMPERSONATION_TOKEN 0xC0000000 | 0x005c -#define NT_STATUS_CANT_DISABLE_MANDATORY 0xC0000000 | 0x005d -#define NT_STATUS_NO_LOGON_SERVERS 0xC0000000 | 0x005e -#define NT_STATUS_NO_SUCH_LOGON_SESSION 0xC0000000 | 0x005f -#define NT_STATUS_NO_SUCH_PRIVILEGE 0xC0000000 | 0x0060 -#define NT_STATUS_PRIVILEGE_NOT_HELD 0xC0000000 | 0x0061 -#define NT_STATUS_INVALID_ACCOUNT_NAME 0xC0000000 | 0x0062 -#define NT_STATUS_USER_EXISTS 0xC0000000 | 0x0063 -#define NT_STATUS_NO_SUCH_USER 0xC0000000 | 0x0064 -#define NT_STATUS_GROUP_EXISTS 0xC0000000 | 0x0065 -#define NT_STATUS_NO_SUCH_GROUP 0xC0000000 | 0x0066 -#define NT_STATUS_MEMBER_IN_GROUP 0xC0000000 | 0x0067 -#define NT_STATUS_MEMBER_NOT_IN_GROUP 0xC0000000 | 0x0068 -#define NT_STATUS_LAST_ADMIN 0xC0000000 | 0x0069 -#define NT_STATUS_WRONG_PASSWORD 0xC0000000 | 0x006a -#define NT_STATUS_ILL_FORMED_PASSWORD 0xC0000000 | 0x006b -#define NT_STATUS_PASSWORD_RESTRICTION 0xC0000000 | 0x006c -#define NT_STATUS_LOGON_FAILURE 0xC0000000 | 0x006d -#define NT_STATUS_ACCOUNT_RESTRICTION 0xC0000000 | 0x006e -#define NT_STATUS_INVALID_LOGON_HOURS 0xC0000000 | 0x006f -#define NT_STATUS_INVALID_WORKSTATION 0xC0000000 | 0x0070 -#define NT_STATUS_PASSWORD_EXPIRED 0xC0000000 | 0x0071 -#define NT_STATUS_ACCOUNT_DISABLED 0xC0000000 | 0x0072 -#define NT_STATUS_NONE_MAPPED 0xC0000000 | 0x0073 -#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0xC0000000 | 0x0074 -#define NT_STATUS_LUIDS_EXHAUSTED 0xC0000000 | 0x0075 -#define NT_STATUS_INVALID_SUB_AUTHORITY 0xC0000000 | 0x0076 -#define NT_STATUS_INVALID_ACL 0xC0000000 | 0x0077 -#define NT_STATUS_INVALID_SID 0xC0000000 | 0x0078 -#define NT_STATUS_INVALID_SECURITY_DESCR 0xC0000000 | 0x0079 -#define NT_STATUS_PROCEDURE_NOT_FOUND 0xC0000000 | 0x007a -#define NT_STATUS_INVALID_IMAGE_FORMAT 0xC0000000 | 0x007b -#define NT_STATUS_NO_TOKEN 0xC0000000 | 0x007c -#define NT_STATUS_BAD_INHERITANCE_ACL 0xC0000000 | 0x007d -#define NT_STATUS_RANGE_NOT_LOCKED 0xC0000000 | 0x007e -#define NT_STATUS_DISK_FULL 0xC0000000 | 0x007f -#define NT_STATUS_SERVER_DISABLED 0xC0000000 | 0x0080 -#define NT_STATUS_SERVER_NOT_DISABLED 0xC0000000 | 0x0081 -#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0xC0000000 | 0x0082 -#define NT_STATUS_GUIDS_EXHAUSTED 0xC0000000 | 0x0083 -#define NT_STATUS_INVALID_ID_AUTHORITY 0xC0000000 | 0x0084 -#define NT_STATUS_AGENTS_EXHAUSTED 0xC0000000 | 0x0085 -#define NT_STATUS_INVALID_VOLUME_LABEL 0xC0000000 | 0x0086 -#define NT_STATUS_SECTION_NOT_EXTENDED 0xC0000000 | 0x0087 -#define NT_STATUS_NOT_MAPPED_DATA 0xC0000000 | 0x0088 -#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0xC0000000 | 0x0089 -#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0xC0000000 | 0x008a -#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0xC0000000 | 0x008b -#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0xC0000000 | 0x008c -#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0xC0000000 | 0x008d -#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0xC0000000 | 0x008e -#define NT_STATUS_FLOAT_INEXACT_RESULT 0xC0000000 | 0x008f -#define NT_STATUS_FLOAT_INVALID_OPERATION 0xC0000000 | 0x0090 -#define NT_STATUS_FLOAT_OVERFLOW 0xC0000000 | 0x0091 -#define NT_STATUS_FLOAT_STACK_CHECK 0xC0000000 | 0x0092 -#define NT_STATUS_FLOAT_UNDERFLOW 0xC0000000 | 0x0093 -#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000000 | 0x0094 -#define NT_STATUS_INTEGER_OVERFLOW 0xC0000000 | 0x0095 -#define NT_STATUS_PRIVILEGED_INSTRUCTION 0xC0000000 | 0x0096 -#define NT_STATUS_TOO_MANY_PAGING_FILES 0xC0000000 | 0x0097 -#define NT_STATUS_FILE_INVALID 0xC0000000 | 0x0098 -#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0xC0000000 | 0x0099 -#define NT_STATUS_INSUFFICIENT_RESOURCES 0xC0000000 | 0x009a -#define NT_STATUS_DFS_EXIT_PATH_FOUND 0xC0000000 | 0x009b -#define NT_STATUS_DEVICE_DATA_ERROR 0xC0000000 | 0x009c -#define NT_STATUS_DEVICE_NOT_CONNECTED 0xC0000000 | 0x009d -#define NT_STATUS_DEVICE_POWER_FAILURE 0xC0000000 | 0x009e -#define NT_STATUS_FREE_VM_NOT_AT_BASE 0xC0000000 | 0x009f -#define NT_STATUS_MEMORY_NOT_ALLOCATED 0xC0000000 | 0x00a0 -#define NT_STATUS_WORKING_SET_QUOTA 0xC0000000 | 0x00a1 -#define NT_STATUS_MEDIA_WRITE_PROTECTED 0xC0000000 | 0x00a2 -#define NT_STATUS_DEVICE_NOT_READY 0xC0000000 | 0x00a3 -#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0xC0000000 | 0x00a4 -#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0xC0000000 | 0x00a5 -#define NT_STATUS_CANT_OPEN_ANONYMOUS 0xC0000000 | 0x00a6 -#define NT_STATUS_BAD_VALIDATION_CLASS 0xC0000000 | 0x00a7 -#define NT_STATUS_BAD_TOKEN_TYPE 0xC0000000 | 0x00a8 -#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0xC0000000 | 0x00a9 -#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0xC0000000 | 0x00aa -#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0xC0000000 | 0x00ab -#define NT_STATUS_PIPE_NOT_AVAILABLE 0xC0000000 | 0x00ac -#define NT_STATUS_INVALID_PIPE_STATE 0xC0000000 | 0x00ad -#define NT_STATUS_PIPE_BUSY 0xC0000000 | 0x00ae -#define NT_STATUS_ILLEGAL_FUNCTION 0xC0000000 | 0x00af -#define NT_STATUS_PIPE_DISCONNECTED 0xC0000000 | 0x00b0 -#define NT_STATUS_PIPE_CLOSING 0xC0000000 | 0x00b1 -#define NT_STATUS_PIPE_CONNECTED 0xC0000000 | 0x00b2 -#define NT_STATUS_PIPE_LISTENING 0xC0000000 | 0x00b3 -#define NT_STATUS_INVALID_READ_MODE 0xC0000000 | 0x00b4 -#define NT_STATUS_IO_TIMEOUT 0xC0000000 | 0x00b5 -#define NT_STATUS_FILE_FORCED_CLOSED 0xC0000000 | 0x00b6 -#define NT_STATUS_PROFILING_NOT_STARTED 0xC0000000 | 0x00b7 -#define NT_STATUS_PROFILING_NOT_STOPPED 0xC0000000 | 0x00b8 -#define NT_STATUS_COULD_NOT_INTERPRET 0xC0000000 | 0x00b9 -#define NT_STATUS_FILE_IS_A_DIRECTORY 0xC0000000 | 0x00ba -#define NT_STATUS_NOT_SUPPORTED 0xC0000000 | 0x00bb -#define NT_STATUS_REMOTE_NOT_LISTENING 0xC0000000 | 0x00bc -#define NT_STATUS_DUPLICATE_NAME 0xC0000000 | 0x00bd -#define NT_STATUS_BAD_NETWORK_PATH 0xC0000000 | 0x00be -#define NT_STATUS_NETWORK_BUSY 0xC0000000 | 0x00bf -#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0xC0000000 | 0x00c0 -#define NT_STATUS_TOO_MANY_COMMANDS 0xC0000000 | 0x00c1 -#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0xC0000000 | 0x00c2 -#define NT_STATUS_INVALID_NETWORK_RESPONSE 0xC0000000 | 0x00c3 -#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0xC0000000 | 0x00c4 -#define NT_STATUS_BAD_REMOTE_ADAPTER 0xC0000000 | 0x00c5 -#define NT_STATUS_PRINT_QUEUE_FULL 0xC0000000 | 0x00c6 -#define NT_STATUS_NO_SPOOL_SPACE 0xC0000000 | 0x00c7 -#define NT_STATUS_PRINT_CANCELLED 0xC0000000 | 0x00c8 -#define NT_STATUS_NETWORK_NAME_DELETED 0xC0000000 | 0x00c9 -#define NT_STATUS_NETWORK_ACCESS_DENIED 0xC0000000 | 0x00ca -#define NT_STATUS_BAD_DEVICE_TYPE 0xC0000000 | 0x00cb -#define NT_STATUS_BAD_NETWORK_NAME 0xC0000000 | 0x00cc -#define NT_STATUS_TOO_MANY_NAMES 0xC0000000 | 0x00cd -#define NT_STATUS_TOO_MANY_SESSIONS 0xC0000000 | 0x00ce -#define NT_STATUS_SHARING_PAUSED 0xC0000000 | 0x00cf -#define NT_STATUS_REQUEST_NOT_ACCEPTED 0xC0000000 | 0x00d0 -#define NT_STATUS_REDIRECTOR_PAUSED 0xC0000000 | 0x00d1 -#define NT_STATUS_NET_WRITE_FAULT 0xC0000000 | 0x00d2 -#define NT_STATUS_PROFILING_AT_LIMIT 0xC0000000 | 0x00d3 -#define NT_STATUS_NOT_SAME_DEVICE 0xC0000000 | 0x00d4 -#define NT_STATUS_FILE_RENAMED 0xC0000000 | 0x00d5 -#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0xC0000000 | 0x00d6 -#define NT_STATUS_NO_SECURITY_ON_OBJECT 0xC0000000 | 0x00d7 -#define NT_STATUS_CANT_WAIT 0xC0000000 | 0x00d8 -#define NT_STATUS_PIPE_EMPTY 0xC0000000 | 0x00d9 -#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0xC0000000 | 0x00da -#define NT_STATUS_CANT_TERMINATE_SELF 0xC0000000 | 0x00db -#define NT_STATUS_INVALID_SERVER_STATE 0xC0000000 | 0x00dc -#define NT_STATUS_INVALID_DOMAIN_STATE 0xC0000000 | 0x00dd -#define NT_STATUS_INVALID_DOMAIN_ROLE 0xC0000000 | 0x00de -#define NT_STATUS_NO_SUCH_DOMAIN 0xC0000000 | 0x00df -#define NT_STATUS_DOMAIN_EXISTS 0xC0000000 | 0x00e0 -#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0xC0000000 | 0x00e1 -#define NT_STATUS_OPLOCK_NOT_GRANTED 0xC0000000 | 0x00e2 -#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0xC0000000 | 0x00e3 -#define NT_STATUS_INTERNAL_DB_CORRUPTION 0xC0000000 | 0x00e4 -#define NT_STATUS_INTERNAL_ERROR 0xC0000000 | 0x00e5 -#define NT_STATUS_GENERIC_NOT_MAPPED 0xC0000000 | 0x00e6 -#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0xC0000000 | 0x00e7 -#define NT_STATUS_INVALID_USER_BUFFER 0xC0000000 | 0x00e8 -#define NT_STATUS_UNEXPECTED_IO_ERROR 0xC0000000 | 0x00e9 -#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0xC0000000 | 0x00ea -#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0xC0000000 | 0x00eb -#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0xC0000000 | 0x00ec -#define NT_STATUS_NOT_LOGON_PROCESS 0xC0000000 | 0x00ed -#define NT_STATUS_LOGON_SESSION_EXISTS 0xC0000000 | 0x00ee -#define NT_STATUS_INVALID_PARAMETER_1 0xC0000000 | 0x00ef -#define NT_STATUS_INVALID_PARAMETER_2 0xC0000000 | 0x00f0 -#define NT_STATUS_INVALID_PARAMETER_3 0xC0000000 | 0x00f1 -#define NT_STATUS_INVALID_PARAMETER_4 0xC0000000 | 0x00f2 -#define NT_STATUS_INVALID_PARAMETER_5 0xC0000000 | 0x00f3 -#define NT_STATUS_INVALID_PARAMETER_6 0xC0000000 | 0x00f4 -#define NT_STATUS_INVALID_PARAMETER_7 0xC0000000 | 0x00f5 -#define NT_STATUS_INVALID_PARAMETER_8 0xC0000000 | 0x00f6 -#define NT_STATUS_INVALID_PARAMETER_9 0xC0000000 | 0x00f7 -#define NT_STATUS_INVALID_PARAMETER_10 0xC0000000 | 0x00f8 -#define NT_STATUS_INVALID_PARAMETER_11 0xC0000000 | 0x00f9 -#define NT_STATUS_INVALID_PARAMETER_12 0xC0000000 | 0x00fa -#define NT_STATUS_REDIRECTOR_NOT_STARTED 0xC0000000 | 0x00fb -#define NT_STATUS_REDIRECTOR_STARTED 0xC0000000 | 0x00fc -#define NT_STATUS_STACK_OVERFLOW 0xC0000000 | 0x00fd -#define NT_STATUS_NO_SUCH_PACKAGE 0xC0000000 | 0x00fe -#define NT_STATUS_BAD_FUNCTION_TABLE 0xC0000000 | 0x00ff -#define NT_STATUS_DIRECTORY_NOT_EMPTY 0xC0000000 | 0x0101 -#define NT_STATUS_FILE_CORRUPT_ERROR 0xC0000000 | 0x0102 -#define NT_STATUS_NOT_A_DIRECTORY 0xC0000000 | 0x0103 -#define NT_STATUS_BAD_LOGON_SESSION_STATE 0xC0000000 | 0x0104 -#define NT_STATUS_LOGON_SESSION_COLLISION 0xC0000000 | 0x0105 -#define NT_STATUS_NAME_TOO_LONG 0xC0000000 | 0x0106 -#define NT_STATUS_FILES_OPEN 0xC0000000 | 0x0107 -#define NT_STATUS_CONNECTION_IN_USE 0xC0000000 | 0x0108 -#define NT_STATUS_MESSAGE_NOT_FOUND 0xC0000000 | 0x0109 -#define NT_STATUS_PROCESS_IS_TERMINATING 0xC0000000 | 0x010a -#define NT_STATUS_INVALID_LOGON_TYPE 0xC0000000 | 0x010b -#define NT_STATUS_NO_GUID_TRANSLATION 0xC0000000 | 0x010c -#define NT_STATUS_CANNOT_IMPERSONATE 0xC0000000 | 0x010d -#define NT_STATUS_IMAGE_ALREADY_LOADED 0xC0000000 | 0x010e -#define NT_STATUS_ABIOS_NOT_PRESENT 0xC0000000 | 0x010f -#define NT_STATUS_ABIOS_LID_NOT_EXIST 0xC0000000 | 0x0110 -#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0xC0000000 | 0x0111 -#define NT_STATUS_ABIOS_NOT_LID_OWNER 0xC0000000 | 0x0112 -#define NT_STATUS_ABIOS_INVALID_COMMAND 0xC0000000 | 0x0113 -#define NT_STATUS_ABIOS_INVALID_LID 0xC0000000 | 0x0114 -#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0xC0000000 | 0x0115 -#define NT_STATUS_ABIOS_INVALID_SELECTOR 0xC0000000 | 0x0116 -#define NT_STATUS_NO_LDT 0xC0000000 | 0x0117 -#define NT_STATUS_INVALID_LDT_SIZE 0xC0000000 | 0x0118 -#define NT_STATUS_INVALID_LDT_OFFSET 0xC0000000 | 0x0119 -#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0xC0000000 | 0x011a -#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0xC0000000 | 0x011b -#define NT_STATUS_RXACT_INVALID_STATE 0xC0000000 | 0x011c -#define NT_STATUS_RXACT_COMMIT_FAILURE 0xC0000000 | 0x011d -#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0xC0000000 | 0x011e -#define NT_STATUS_TOO_MANY_OPENED_FILES 0xC0000000 | 0x011f -#define NT_STATUS_CANCELLED 0xC0000000 | 0x0120 -#define NT_STATUS_CANNOT_DELETE 0xC0000000 | 0x0121 -#define NT_STATUS_INVALID_COMPUTER_NAME 0xC0000000 | 0x0122 -#define NT_STATUS_FILE_DELETED 0xC0000000 | 0x0123 -#define NT_STATUS_SPECIAL_ACCOUNT 0xC0000000 | 0x0124 -#define NT_STATUS_SPECIAL_GROUP 0xC0000000 | 0x0125 -#define NT_STATUS_SPECIAL_USER 0xC0000000 | 0x0126 -#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0xC0000000 | 0x0127 -#define NT_STATUS_FILE_CLOSED 0xC0000000 | 0x0128 -#define NT_STATUS_TOO_MANY_THREADS 0xC0000000 | 0x0129 -#define NT_STATUS_THREAD_NOT_IN_PROCESS 0xC0000000 | 0x012a -#define NT_STATUS_TOKEN_ALREADY_IN_USE 0xC0000000 | 0x012b -#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0xC0000000 | 0x012c -#define NT_STATUS_COMMITMENT_LIMIT 0xC0000000 | 0x012d -#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0xC0000000 | 0x012e -#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0xC0000000 | 0x012f -#define NT_STATUS_INVALID_IMAGE_PROTECT 0xC0000000 | 0x0130 -#define NT_STATUS_INVALID_IMAGE_WIN_16 0xC0000000 | 0x0131 -#define NT_STATUS_LOGON_SERVER_CONFLICT 0xC0000000 | 0x0132 -#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0xC0000000 | 0x0133 -#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0xC0000000 | 0x0134 -#define NT_STATUS_DLL_NOT_FOUND 0xC0000000 | 0x0135 -#define NT_STATUS_OPEN_FAILED 0xC0000000 | 0x0136 -#define NT_STATUS_IO_PRIVILEGE_FAILED 0xC0000000 | 0x0137 -#define NT_STATUS_ORDINAL_NOT_FOUND 0xC0000000 | 0x0138 -#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0139 -#define NT_STATUS_CONTROL_C_EXIT 0xC0000000 | 0x013a -#define NT_STATUS_LOCAL_DISCONNECT 0xC0000000 | 0x013b -#define NT_STATUS_REMOTE_DISCONNECT 0xC0000000 | 0x013c -#define NT_STATUS_REMOTE_RESOURCES 0xC0000000 | 0x013d -#define NT_STATUS_LINK_FAILED 0xC0000000 | 0x013e -#define NT_STATUS_LINK_TIMEOUT 0xC0000000 | 0x013f -#define NT_STATUS_INVALID_CONNECTION 0xC0000000 | 0x0140 -#define NT_STATUS_INVALID_ADDRESS 0xC0000000 | 0x0141 -#define NT_STATUS_DLL_INIT_FAILED 0xC0000000 | 0x0142 -#define NT_STATUS_MISSING_SYSTEMFILE 0xC0000000 | 0x0143 -#define NT_STATUS_UNHANDLED_EXCEPTION 0xC0000000 | 0x0144 -#define NT_STATUS_APP_INIT_FAILURE 0xC0000000 | 0x0145 -#define NT_STATUS_PAGEFILE_CREATE_FAILED 0xC0000000 | 0x0146 -#define NT_STATUS_NO_PAGEFILE 0xC0000000 | 0x0147 -#define NT_STATUS_INVALID_LEVEL 0xC0000000 | 0x0148 -#define NT_STATUS_WRONG_PASSWORD_CORE 0xC0000000 | 0x0149 -#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0xC0000000 | 0x014a -#define NT_STATUS_PIPE_BROKEN 0xC0000000 | 0x014b -#define NT_STATUS_REGISTRY_CORRUPT 0xC0000000 | 0x014c -#define NT_STATUS_REGISTRY_IO_FAILED 0xC0000000 | 0x014d -#define NT_STATUS_NO_EVENT_PAIR 0xC0000000 | 0x014e -#define NT_STATUS_UNRECOGNIZED_VOLUME 0xC0000000 | 0x014f -#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0xC0000000 | 0x0150 -#define NT_STATUS_NO_SUCH_ALIAS 0xC0000000 | 0x0151 -#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0xC0000000 | 0x0152 -#define NT_STATUS_MEMBER_IN_ALIAS 0xC0000000 | 0x0153 -#define NT_STATUS_ALIAS_EXISTS 0xC0000000 | 0x0154 -#define NT_STATUS_LOGON_NOT_GRANTED 0xC0000000 | 0x0155 -#define NT_STATUS_TOO_MANY_SECRETS 0xC0000000 | 0x0156 -#define NT_STATUS_SECRET_TOO_LONG 0xC0000000 | 0x0157 -#define NT_STATUS_INTERNAL_DB_ERROR 0xC0000000 | 0x0158 -#define NT_STATUS_FULLSCREEN_MODE 0xC0000000 | 0x0159 -#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0xC0000000 | 0x015a -#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0xC0000000 | 0x015b -#define NT_STATUS_NOT_REGISTRY_FILE 0xC0000000 | 0x015c -#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x015d -#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0xC0000000 | 0x015e -#define NT_STATUS_FT_MISSING_MEMBER 0xC0000000 | 0x015f -#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0xC0000000 | 0x0160 -#define NT_STATUS_ILLEGAL_CHARACTER 0xC0000000 | 0x0161 -#define NT_STATUS_UNMAPPABLE_CHARACTER 0xC0000000 | 0x0162 -#define NT_STATUS_UNDEFINED_CHARACTER 0xC0000000 | 0x0163 -#define NT_STATUS_FLOPPY_VOLUME 0xC0000000 | 0x0164 -#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0xC0000000 | 0x0165 -#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0xC0000000 | 0x0166 -#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0xC0000000 | 0x0167 -#define NT_STATUS_FLOPPY_BAD_REGISTERS 0xC0000000 | 0x0168 -#define NT_STATUS_DISK_RECALIBRATE_FAILED 0xC0000000 | 0x0169 -#define NT_STATUS_DISK_OPERATION_FAILED 0xC0000000 | 0x016a -#define NT_STATUS_DISK_RESET_FAILED 0xC0000000 | 0x016b -#define NT_STATUS_SHARED_IRQ_BUSY 0xC0000000 | 0x016c -#define NT_STATUS_FT_ORPHANING 0xC0000000 | 0x016d -#define NT_STATUS_PARTITION_FAILURE 0xC0000000 | 0x0172 -#define NT_STATUS_INVALID_BLOCK_LENGTH 0xC0000000 | 0x0173 -#define NT_STATUS_DEVICE_NOT_PARTITIONED 0xC0000000 | 0x0174 -#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0xC0000000 | 0x0175 -#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0xC0000000 | 0x0176 -#define NT_STATUS_EOM_OVERFLOW 0xC0000000 | 0x0177 -#define NT_STATUS_NO_MEDIA 0xC0000000 | 0x0178 -#define NT_STATUS_NO_SUCH_MEMBER 0xC0000000 | 0x017a -#define NT_STATUS_INVALID_MEMBER 0xC0000000 | 0x017b -#define NT_STATUS_KEY_DELETED 0xC0000000 | 0x017c -#define NT_STATUS_NO_LOG_SPACE 0xC0000000 | 0x017d -#define NT_STATUS_TOO_MANY_SIDS 0xC0000000 | 0x017e -#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x017f -#define NT_STATUS_KEY_HAS_CHILDREN 0xC0000000 | 0x0180 -#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0xC0000000 | 0x0181 -#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0xC0000000 | 0x0182 -#define NT_STATUS_DRIVER_INTERNAL_ERROR 0xC0000000 | 0x0183 -#define NT_STATUS_INVALID_DEVICE_STATE 0xC0000000 | 0x0184 -#define NT_STATUS_IO_DEVICE_ERROR 0xC0000000 | 0x0185 -#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0xC0000000 | 0x0186 -#define NT_STATUS_BACKUP_CONTROLLER 0xC0000000 | 0x0187 -#define NT_STATUS_LOG_FILE_FULL 0xC0000000 | 0x0188 -#define NT_STATUS_TOO_LATE 0xC0000000 | 0x0189 -#define NT_STATUS_NO_TRUST_LSA_SECRET 0xC0000000 | 0x018a -#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0xC0000000 | 0x018b -#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0xC0000000 | 0x018c -#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0xC0000000 | 0x018d -#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0xC0000000 | 0x018e -#define NT_STATUS_EVENTLOG_CANT_START 0xC0000000 | 0x018f -#define NT_STATUS_TRUST_FAILURE 0xC0000000 | 0x0190 -#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0xC0000000 | 0x0191 -#define NT_STATUS_NETLOGON_NOT_STARTED 0xC0000000 | 0x0192 -#define NT_STATUS_ACCOUNT_EXPIRED 0xC0000000 | 0x0193 -#define NT_STATUS_POSSIBLE_DEADLOCK 0xC0000000 | 0x0194 -#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0xC0000000 | 0x0195 -#define NT_STATUS_REMOTE_SESSION_LIMIT 0xC0000000 | 0x0196 -#define NT_STATUS_EVENTLOG_FILE_CHANGED 0xC0000000 | 0x0197 -#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0xC0000000 | 0x0198 -#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0xC0000000 | 0x0199 -#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0xC0000000 | 0x019a -#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0xC0000000 | 0x019b -#define NT_STATUS_FS_DRIVER_REQUIRED 0xC0000000 | 0x019c -#define NT_STATUS_NO_USER_SESSION_KEY 0xC0000000 | 0x0202 -#define NT_STATUS_USER_SESSION_DELETED 0xC0000000 | 0x0203 -#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0xC0000000 | 0x0204 -#define NT_STATUS_INSUFF_SERVER_RESOURCES 0xC0000000 | 0x0205 -#define NT_STATUS_INVALID_BUFFER_SIZE 0xC0000000 | 0x0206 -#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0xC0000000 | 0x0207 -#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0xC0000000 | 0x0208 -#define NT_STATUS_TOO_MANY_ADDRESSES 0xC0000000 | 0x0209 -#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0xC0000000 | 0x020a -#define NT_STATUS_ADDRESS_CLOSED 0xC0000000 | 0x020b -#define NT_STATUS_CONNECTION_DISCONNECTED 0xC0000000 | 0x020c -#define NT_STATUS_CONNECTION_RESET 0xC0000000 | 0x020d -#define NT_STATUS_TOO_MANY_NODES 0xC0000000 | 0x020e -#define NT_STATUS_TRANSACTION_ABORTED 0xC0000000 | 0x020f -#define NT_STATUS_TRANSACTION_TIMED_OUT 0xC0000000 | 0x0210 -#define NT_STATUS_TRANSACTION_NO_RELEASE 0xC0000000 | 0x0211 -#define NT_STATUS_TRANSACTION_NO_MATCH 0xC0000000 | 0x0212 -#define NT_STATUS_TRANSACTION_RESPONDED 0xC0000000 | 0x0213 -#define NT_STATUS_TRANSACTION_INVALID_ID 0xC0000000 | 0x0214 -#define NT_STATUS_TRANSACTION_INVALID_TYPE 0xC0000000 | 0x0215 -#define NT_STATUS_NOT_SERVER_SESSION 0xC0000000 | 0x0216 -#define NT_STATUS_NOT_CLIENT_SESSION 0xC0000000 | 0x0217 -#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0xC0000000 | 0x0218 -#define NT_STATUS_DEBUG_ATTACH_FAILED 0xC0000000 | 0x0219 -#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0xC0000000 | 0x021a -#define NT_STATUS_DATA_NOT_ACCEPTED 0xC0000000 | 0x021b -#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0xC0000000 | 0x021c -#define NT_STATUS_VDM_HARD_ERROR 0xC0000000 | 0x021d -#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0xC0000000 | 0x021e -#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0xC0000000 | 0x021f -#define NT_STATUS_MAPPED_ALIGNMENT 0xC0000000 | 0x0220 -#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0xC0000000 | 0x0221 -#define NT_STATUS_LOST_WRITEBEHIND_DATA 0xC0000000 | 0x0222 -#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0xC0000000 | 0x0223 -#define NT_STATUS_PASSWORD_MUST_CHANGE 0xC0000000 | 0x0224 -#define NT_STATUS_NOT_FOUND 0xC0000000 | 0x0225 -#define NT_STATUS_NOT_TINY_STREAM 0xC0000000 | 0x0226 -#define NT_STATUS_RECOVERY_FAILURE 0xC0000000 | 0x0227 -#define NT_STATUS_STACK_OVERFLOW_READ 0xC0000000 | 0x0228 -#define NT_STATUS_FAIL_CHECK 0xC0000000 | 0x0229 -#define NT_STATUS_DUPLICATE_OBJECTID 0xC0000000 | 0x022a -#define NT_STATUS_OBJECTID_EXISTS 0xC0000000 | 0x022b -#define NT_STATUS_CONVERT_TO_LARGE 0xC0000000 | 0x022c -#define NT_STATUS_RETRY 0xC0000000 | 0x022d -#define NT_STATUS_FOUND_OUT_OF_SCOPE 0xC0000000 | 0x022e -#define NT_STATUS_ALLOCATE_BUCKET 0xC0000000 | 0x022f -#define NT_STATUS_PROPSET_NOT_FOUND 0xC0000000 | 0x0230 -#define NT_STATUS_MARSHALL_OVERFLOW 0xC0000000 | 0x0231 -#define NT_STATUS_INVALID_VARIANT 0xC0000000 | 0x0232 -#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0xC0000000 | 0x0233 -#define NT_STATUS_ACCOUNT_LOCKED_OUT 0xC0000000 | 0x0234 -#define NT_STATUS_HANDLE_NOT_CLOSABLE 0xC0000000 | 0x0235 -#define NT_STATUS_CONNECTION_REFUSED 0xC0000000 | 0x0236 -#define NT_STATUS_GRACEFUL_DISCONNECT 0xC0000000 | 0x0237 -#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0xC0000000 | 0x0238 -#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0xC0000000 | 0x0239 -#define NT_STATUS_CONNECTION_INVALID 0xC0000000 | 0x023a -#define NT_STATUS_CONNECTION_ACTIVE 0xC0000000 | 0x023b -#define NT_STATUS_NETWORK_UNREACHABLE 0xC0000000 | 0x023c -#define NT_STATUS_HOST_UNREACHABLE 0xC0000000 | 0x023d -#define NT_STATUS_PROTOCOL_UNREACHABLE 0xC0000000 | 0x023e -#define NT_STATUS_PORT_UNREACHABLE 0xC0000000 | 0x023f -#define NT_STATUS_REQUEST_ABORTED 0xC0000000 | 0x0240 -#define NT_STATUS_CONNECTION_ABORTED 0xC0000000 | 0x0241 -#define NT_STATUS_BAD_COMPRESSION_BUFFER 0xC0000000 | 0x0242 -#define NT_STATUS_USER_MAPPED_FILE 0xC0000000 | 0x0243 -#define NT_STATUS_AUDIT_FAILED 0xC0000000 | 0x0244 -#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000000 | 0x0245 -#define NT_STATUS_CONNECTION_COUNT_LIMIT 0xC0000000 | 0x0246 -#define NT_STATUS_LOGIN_TIME_RESTRICTION 0xC0000000 | 0x0247 -#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0xC0000000 | 0x0248 -#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0xC0000000 | 0x0249 -#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0xC0000000 | 0x0250 -#define NT_STATUS_BAD_DLL_ENTRYPOINT 0xC0000000 | 0x0251 -#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0xC0000000 | 0x0252 -#define NT_STATUS_LPC_REPLY_LOST 0xC0000000 | 0x0253 -#define NT_STATUS_IP_ADDRESS_CONFLICT1 0xC0000000 | 0x0254 -#define NT_STATUS_IP_ADDRESS_CONFLICT2 0xC0000000 | 0x0255 -#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0xC0000000 | 0x0256 -#define NT_STATUS_PATH_NOT_COVERED 0xC0000000 | 0x0257 -#define NT_STATUS_NO_CALLBACK_ACTIVE 0xC0000000 | 0x0258 -#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0xC0000000 | 0x0259 -#define NT_STATUS_PWD_TOO_SHORT 0xC0000000 | 0x025a -#define NT_STATUS_PWD_TOO_RECENT 0xC0000000 | 0x025b -#define NT_STATUS_PWD_HISTORY_CONFLICT 0xC0000000 | 0x025c -#define NT_STATUS_PLUGPLAY_NO_DEVICE 0xC0000000 | 0x025e -#define NT_STATUS_UNSUPPORTED_COMPRESSION 0xC0000000 | 0x025f -#define NT_STATUS_INVALID_HW_PROFILE 0xC0000000 | 0x0260 -#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0xC0000000 | 0x0261 -#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0xC0000000 | 0x0262 -#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0263 -#define NT_STATUS_RESOURCE_NOT_OWNED 0xC0000000 | 0x0264 -#define NT_STATUS_TOO_MANY_LINKS 0xC0000000 | 0x0265 -#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0xC0000000 | 0x0266 -#define NT_STATUS_FILE_IS_OFFLINE 0xC0000000 | 0x0267 -#define NT_STATUS_NO_SUCH_JOB 0xC0000000 | 0xEDE /* scheduler */ - -#endif /* _NTERR_H */ diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h deleted file mode 100644 index 55758b9ec877..000000000000 --- a/fs/cifs/ntlmssp.h +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#define NTLMSSP_SIGNATURE "NTLMSSP" -/* Message Types */ -#define NtLmNegotiate cpu_to_le32(1) -#define NtLmChallenge cpu_to_le32(2) -#define NtLmAuthenticate cpu_to_le32(3) -#define UnknownMessage cpu_to_le32(8) - -/* Negotiate Flags */ -#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ -#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ -#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ -/* define reserved9 0x08 */ -#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ -#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ -#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 -#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ -/* defined reserved 8 0x0100 */ -#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ -#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ -#define NTLMSSP_ANONYMOUS 0x0800 -#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ -#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 -#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ -#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ -#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 -#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 -#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 -#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ -/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ -#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 -#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ -#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 -#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 -/* #define reserved4 0x1000000 */ -#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we only set for SMB2+ */ -/* #define reserved3 0x4000000 */ -/* #define reserved2 0x8000000 */ -/* #define reserved1 0x10000000 */ -#define NTLMSSP_NEGOTIATE_128 0x20000000 -#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 -#define NTLMSSP_NEGOTIATE_56 0x80000000 - -/* Define AV Pair Field IDs */ -enum av_field_type { - NTLMSSP_AV_EOL = 0, - NTLMSSP_AV_NB_COMPUTER_NAME, - NTLMSSP_AV_NB_DOMAIN_NAME, - NTLMSSP_AV_DNS_COMPUTER_NAME, - NTLMSSP_AV_DNS_DOMAIN_NAME, - NTLMSSP_AV_DNS_TREE_NAME, - NTLMSSP_AV_FLAGS, - NTLMSSP_AV_TIMESTAMP, - NTLMSSP_AV_RESTRICTION, - NTLMSSP_AV_TARGET_NAME, - NTLMSSP_AV_CHANNEL_BINDINGS -}; - -/* Although typedefs are not commonly used for structure definitions */ -/* in the Linux kernel, in this particular case they are useful */ -/* to more closely match the standards document for NTLMSSP from */ -/* OpenGroup and to make the code more closely match the standard in */ -/* appearance */ - -typedef struct _SECURITY_BUFFER { - __le16 Length; - __le16 MaximumLength; - __le32 BufferOffset; /* offset to buffer */ -} __attribute__((packed)) SECURITY_BUFFER; - -typedef struct _NEGOTIATE_MESSAGE { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmNegotiate = 1 */ - __le32 NegotiateFlags; - SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ - SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ - /* SECURITY_BUFFER for version info not present since we - do not set the version is present flag */ - char DomainString[0]; - /* followed by WorkstationString */ -} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE; - -#define NTLMSSP_REVISION_W2K3 0x0F - -/* See MS-NLMP section 2.2.2.10 */ -struct ntlmssp_version { - __u8 ProductMajorVersion; - __u8 ProductMinorVersion; - __le16 ProductBuild; /* we send the cifs.ko module version here */ - __u8 Reserved[3]; - __u8 NTLMRevisionCurrent; /* currently 0x0F */ -} __packed; - -/* see MS-NLMP section 2.2.1.1 */ -struct negotiate_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmNegotiate = 1 */ - __le32 NegotiateFlags; - SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ - SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ - struct ntlmssp_version Version; - /* SECURITY_BUFFER */ - char DomainString[]; - /* followed by WorkstationString */ -} __packed; - -typedef struct _CHALLENGE_MESSAGE { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmChallenge = 2 */ - SECURITY_BUFFER TargetName; - __le32 NegotiateFlags; - __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; - __u8 Reserved[8]; - SECURITY_BUFFER TargetInfoArray; - /* SECURITY_BUFFER for version info not present since we - do not set the version is present flag */ -} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE; - -typedef struct _AUTHENTICATE_MESSAGE { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmsAuthenticate = 3 */ - SECURITY_BUFFER LmChallengeResponse; - SECURITY_BUFFER NtChallengeResponse; - SECURITY_BUFFER DomainName; - SECURITY_BUFFER UserName; - SECURITY_BUFFER WorkstationName; - SECURITY_BUFFER SessionKey; - __le32 NegotiateFlags; - /* SECURITY_BUFFER for version info not present since we - do not set the version is present flag */ - char UserString[0]; -} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; - -/* - * Size of the session key (crypto key encrypted with the password - */ - -int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); -int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c deleted file mode 100644 index 2d75ba5aaa8a..000000000000 --- a/fs/cifs/readdir.c +++ /dev/null @@ -1,1237 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Directory search handling - * - * Copyright (C) International Business Machines Corp., 2004, 2008 - * Copyright (C) Red Hat, Inc., 2011 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifsfs.h" -#include "smb2proto.h" -#include "fs_context.h" -#include "cached_dir.h" - -/* - * To be safe - for UCS to UTF-8 with strings loaded with the rare long - * characters alloc more to account for such multibyte target UTF-8 - * characters. - */ -#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2) - -#ifdef CONFIG_CIFS_DEBUG2 -static void dump_cifs_file_struct(struct file *file, char *label) -{ - struct cifsFileInfo *cf; - - if (file) { - cf = file->private_data; - if (cf == NULL) { - cifs_dbg(FYI, "empty cifs private file data\n"); - return; - } - if (cf->invalidHandle) - cifs_dbg(FYI, "Invalid handle\n"); - if (cf->srch_inf.endOfSearch) - cifs_dbg(FYI, "end of search\n"); - if (cf->srch_inf.emptyDir) - cifs_dbg(FYI, "empty dir\n"); - } -} -#else -static inline void dump_cifs_file_struct(struct file *file, char *label) -{ -} -#endif /* DEBUG2 */ - -/* - * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT - * - * Find the dentry that matches "name". If there isn't one, create one. If it's - * a negative dentry or the uniqueid or filetype(mode) changed, - * then drop it and recreate it. - */ -static void -cifs_prime_dcache(struct dentry *parent, struct qstr *name, - struct cifs_fattr *fattr) -{ - struct dentry *dentry, *alias; - struct inode *inode; - struct super_block *sb = parent->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); - - cifs_dbg(FYI, "%s: for %s\n", __func__, name->name); - - dentry = d_hash_and_lookup(parent, name); - if (!dentry) { - /* - * If we know that the inode will need to be revalidated - * immediately, then don't create a new dentry for it. - * We'll end up doing an on the wire call either way and - * this spares us an invalidation. - */ - if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) - return; -retry: - dentry = d_alloc_parallel(parent, name, &wq); - } - if (IS_ERR(dentry)) - return; - if (!d_in_lookup(dentry)) { - inode = d_inode(dentry); - if (inode) { - if (d_mountpoint(dentry)) { - dput(dentry); - return; - } - /* - * If we're generating inode numbers, then we don't - * want to clobber the existing one with the one that - * the readdir code created. - */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) - fattr->cf_uniqueid = CIFS_I(inode)->uniqueid; - - /* update inode in place - * if both i_ino and i_mode didn't change */ - if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid && - cifs_fattr_to_inode(inode, fattr) == 0) { - dput(dentry); - return; - } - } - d_invalidate(dentry); - dput(dentry); - goto retry; - } else { - inode = cifs_iget(sb, fattr); - if (!inode) - inode = ERR_PTR(-ENOMEM); - alias = d_splice_alias(inode, dentry); - d_lookup_done(dentry); - if (alias && !IS_ERR(alias)) - dput(alias); - } - dput(dentry); -} - -static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) -{ - if (!(fattr->cf_cifsattrs & ATTR_REPARSE)) - return false; - /* - * The DFS tags should be only intepreted by server side as per - * MS-FSCC 2.1.2.1, but let's include them anyway. - * - * Besides, if cf_cifstag is unset (0), then we still need it to be - * revalidated to know exactly what reparse point it is. - */ - switch (fattr->cf_cifstag) { - case IO_REPARSE_TAG_DFS: - case IO_REPARSE_TAG_DFSR: - case IO_REPARSE_TAG_SYMLINK: - case IO_REPARSE_TAG_NFS: - case 0: - return true; - } - return false; -} - -static void -cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) -{ - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; - - /* - * The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they - * are preferred by the Linux client in some cases since, unlike - * the NFS reparse tag (or EAs), they don't require an extra query - * to determine which type of special file they represent. - * TODO: go through all documented reparse tags to see if we can - * reasonably map some of them to directories vs. files vs. symlinks - */ - if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; - fattr->cf_dtype = DT_DIR; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) { - fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_LNK; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) { - fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_FIFO; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) { - fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_SOCK; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) { - fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_CHR; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) { - fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_BLK; - } else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */ - fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_REG; - } - - /* - * We need to revalidate it further to make a decision about whether it - * is a symbolic link, DFS referral or a reparse point with a direct - * access like junctions, deduplicated files, NFS symlinks. - */ - if (reparse_file_needs_reval(fattr)) - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - - /* non-unix readdir doesn't provide nlink */ - fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; - - if (fattr->cf_cifsattrs & ATTR_READONLY) - fattr->cf_mode &= ~S_IWUGO; - - /* - * We of course don't get ACL info in FIND_FIRST/NEXT results, so - * mark it for revalidation so that "ls -l" will look right. It might - * be super-slow, but if we don't do this then the ownership of files - * may look wrong since the inodes may not have timed out by the time - * "ls" does a stat() call on them. - */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL && - fattr->cf_cifsattrs & ATTR_SYSTEM) { - if (fattr->cf_eof == 0) { - fattr->cf_mode &= ~S_IFMT; - fattr->cf_mode |= S_IFIFO; - fattr->cf_dtype = DT_FIFO; - } else { - /* - * trying to get the type and mode via SFU can be slow, - * so just call those regular files for now, and mark - * for reval - */ - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - } - } -} - -/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */ -static void -cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, - struct cifs_sb_info *cifs_sb) -{ - struct smb2_posix_info_parsed parsed; - - posix_info_parse(info, NULL, &parsed); - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_uniqueid = le64_to_cpu(info->Inode); - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); - fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime); - - fattr->cf_nlink = le32_to_cpu(info->HardLinks); - fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); - - /* - * Since we set the inode type below we need to mask off - * to avoid strange results if bits set above. - * XXX: why not make server&client use the type bits? - */ - fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; - - cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n", - le32_to_cpu(info->DeviceId), - le32_to_cpu(info->ReparseTag), - le32_to_cpu(info->Mode)); - - if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode |= S_IFDIR; - fattr->cf_dtype = DT_DIR; - } else { - /* - * mark anything that is not a dir as regular - * file. special files should have the REPARSE - * attribute and will be marked as needing revaluation - */ - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - } - - if (reparse_file_needs_reval(fattr)) - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - - sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); - sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); -} - -static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) -{ - const FILE_DIRECTORY_INFO *fi = info; - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes); - fattr->cf_eof = le64_to_cpu(fi->EndOfFile); - fattr->cf_bytes = le64_to_cpu(fi->AllocationSize); - fattr->cf_createtime = le64_to_cpu(fi->CreationTime); - fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime); - fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime); - fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime); -} - -void -cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - __dir_info_to_fattr(fattr, info); - cifs_fill_common_info(fattr, cifs_sb); -} - -static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr, - SEARCH_ID_FULL_DIR_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - __dir_info_to_fattr(fattr, info); - - /* See MS-FSCC 2.4.19 FileIdFullDirectoryInformation */ - if (fattr->cf_cifsattrs & ATTR_REPARSE) - fattr->cf_cifstag = le32_to_cpu(info->EaSize); - cifs_fill_common_info(fattr, cifs_sb); -} - -static void -cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj; - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, - info->LastAccessTime, offset); - fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate, - info->LastWriteTime, offset); - fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate, - info->LastWriteTime, offset); - - fattr->cf_cifsattrs = le16_to_cpu(info->Attributes); - fattr->cf_bytes = le32_to_cpu(info->AllocationSize); - fattr->cf_eof = le32_to_cpu(info->DataSize); - - cifs_fill_common_info(fattr, cifs_sb); -} - -/* BB eventually need to add the following helper function to - resolve NT_STATUS_STOPPED_ON_SYMLINK return code when - we try to do FindFirst on (NTFS) directory symlinks */ -/* -int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, - unsigned int xid) -{ - __u16 fid; - int len; - int oplock = 0; - int rc; - struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb); - char *tmpbuffer; - - rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, - OPEN_REPARSE_POINT, &fid, &oplock, NULL, - cifs_sb->local_nls, - cifs_remap(cifs_sb); - if (!rc) { - tmpbuffer = kmalloc(maxpath); - rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path, - tmpbuffer, - maxpath -1, - fid, - cifs_sb->local_nls); - if (CIFSSMBClose(xid, ptcon, fid)) { - cifs_dbg(FYI, "Error closing temporary reparsepoint open\n"); - } - } -} - */ - -static int -_initiate_cifs_search(const unsigned int xid, struct file *file, - const char *full_path) -{ - __u16 search_flags; - int rc = 0; - struct cifsFileInfo *cifsFile; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - - if (file->private_data == NULL) { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); - if (cifsFile == NULL) { - rc = -ENOMEM; - goto error_exit; - } - spin_lock_init(&cifsFile->file_info_lock); - file->private_data = cifsFile; - cifsFile->tlink = cifs_get_tlink(tlink); - tcon = tlink_tcon(tlink); - } else { - cifsFile = file->private_data; - tcon = tlink_tcon(cifsFile->tlink); - } - - server = tcon->ses->server; - - if (!server->ops->query_dir_first) { - rc = -ENOSYS; - goto error_exit; - } - - cifsFile->invalidHandle = true; - cifsFile->srch_inf.endOfSearch = false; - - cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos); - -ffirst_retry: - /* test for Unix extensions */ - /* but now check for them on the share/mount not on the SMB session */ - /* if (cap_unix(tcon->ses) { */ - if (tcon->unix_ext) - cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; - else if (tcon->posix_extensions) - cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO; - else if ((tcon->ses->capabilities & - tcon->ses->server->vals->cap_nt_find) == 0) { - cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { - cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; - } else /* not srvinos - BB fixme add check for backlevel? */ { - cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; - } - - search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; - if (backup_cred(cifs_sb)) - search_flags |= CIFS_SEARCH_BACKUP_SEARCH; - - rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb, - &cifsFile->fid, search_flags, - &cifsFile->srch_inf); - - if (rc == 0) - cifsFile->invalidHandle = false; - /* BB add following call to handle readdir on new NTFS symlink errors - else if STATUS_STOPPED_ON_SYMLINK - call get_symlink_reparse_path and retry with new path */ - else if ((rc == -EOPNOTSUPP) && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; - goto ffirst_retry; - } -error_exit: - cifs_put_tlink(tlink); - return rc; -} - -static int -initiate_cifs_search(const unsigned int xid, struct file *file, - const char *full_path) -{ - int rc, retry_count = 0; - - do { - rc = _initiate_cifs_search(xid, file, full_path); - /* - * If we don't have enough credits to start reading the - * directory just try again after short wait. - */ - if (rc != -EDEADLK) - break; - - usleep_range(512, 2048); - } while (retry_count++ < 5); - - return rc; -} - -/* return length of unicode string in bytes */ -static int cifs_unicode_bytelen(const char *str) -{ - int len; - const __le16 *ustr = (const __le16 *)str; - - for (len = 0; len <= PATH_MAX; len++) { - if (ustr[len] == 0) - return len << 1; - } - cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n"); - return len << 1; -} - -static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) -{ - char *new_entry; - FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; - - if (level == SMB_FIND_FILE_INFO_STANDARD) { - FIND_FILE_STANDARD_INFO *pfData; - pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo; - - new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + - pfData->FileNameLength; - } else { - u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset); - - if (old_entry + next_offset < old_entry) { - cifs_dbg(VFS, "Invalid offset %u\n", next_offset); - return NULL; - } - new_entry = old_entry + next_offset; - } - cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry); - /* validate that new_entry is not past end of SMB */ - if (new_entry >= end_of_smb) { - cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n", - new_entry, end_of_smb, old_entry); - return NULL; - } else if (((level == SMB_FIND_FILE_INFO_STANDARD) && - (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) - || ((level != SMB_FIND_FILE_INFO_STANDARD) && - (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) { - cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n", - new_entry, end_of_smb); - return NULL; - } else - return new_entry; - -} - -struct cifs_dirent { - const char *name; - size_t namelen; - u32 resume_key; - u64 ino; -}; - -static void cifs_fill_dirent_posix(struct cifs_dirent *de, - const struct smb2_posix_info *info) -{ - struct smb2_posix_info_parsed parsed; - - /* payload should have already been checked at this point */ - if (posix_info_parse(info, NULL, &parsed) < 0) { - cifs_dbg(VFS, "Invalid POSIX info payload\n"); - return; - } - - de->name = parsed.name; - de->namelen = parsed.name_len; - de->resume_key = info->Ignored; - de->ino = le64_to_cpu(info->Inode); -} - -static void cifs_fill_dirent_unix(struct cifs_dirent *de, - const FILE_UNIX_INFO *info, bool is_unicode) -{ - de->name = &info->FileName[0]; - if (is_unicode) - de->namelen = cifs_unicode_bytelen(de->name); - else - de->namelen = strnlen(de->name, PATH_MAX); - de->resume_key = info->ResumeKey; - de->ino = le64_to_cpu(info->basic.UniqueId); -} - -static void cifs_fill_dirent_dir(struct cifs_dirent *de, - const FILE_DIRECTORY_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; -} - -static void cifs_fill_dirent_full(struct cifs_dirent *de, - const FILE_FULL_DIRECTORY_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; -} - -static void cifs_fill_dirent_search(struct cifs_dirent *de, - const SEARCH_ID_FULL_DIR_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; - de->ino = le64_to_cpu(info->UniqueId); -} - -static void cifs_fill_dirent_both(struct cifs_dirent *de, - const FILE_BOTH_DIRECTORY_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; -} - -static void cifs_fill_dirent_std(struct cifs_dirent *de, - const FIND_FILE_STANDARD_INFO *info) -{ - de->name = &info->FileName[0]; - /* one byte length, no endianess conversion */ - de->namelen = info->FileNameLength; - de->resume_key = info->ResumeKey; -} - -static int cifs_fill_dirent(struct cifs_dirent *de, const void *info, - u16 level, bool is_unicode) -{ - memset(de, 0, sizeof(*de)); - - switch (level) { - case SMB_FIND_FILE_POSIX_INFO: - cifs_fill_dirent_posix(de, info); - break; - case SMB_FIND_FILE_UNIX: - cifs_fill_dirent_unix(de, info, is_unicode); - break; - case SMB_FIND_FILE_DIRECTORY_INFO: - cifs_fill_dirent_dir(de, info); - break; - case SMB_FIND_FILE_FULL_DIRECTORY_INFO: - cifs_fill_dirent_full(de, info); - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - cifs_fill_dirent_search(de, info); - break; - case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: - cifs_fill_dirent_both(de, info); - break; - case SMB_FIND_FILE_INFO_STANDARD: - cifs_fill_dirent_std(de, info); - break; - default: - cifs_dbg(FYI, "Unknown findfirst level %d\n", level); - return -EINVAL; - } - - return 0; -} - -#define UNICODE_DOT cpu_to_le16(0x2e) - -/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ -static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode) -{ - int rc = 0; - - if (!de->name) - return 0; - - if (is_unicode) { - __le16 *ufilename = (__le16 *)de->name; - if (de->namelen == 2) { - /* check for . */ - if (ufilename[0] == UNICODE_DOT) - rc = 1; - } else if (de->namelen == 4) { - /* check for .. */ - if (ufilename[0] == UNICODE_DOT && - ufilename[1] == UNICODE_DOT) - rc = 2; - } - } else /* ASCII */ { - if (de->namelen == 1) { - if (de->name[0] == '.') - rc = 1; - } else if (de->namelen == 2) { - if (de->name[0] == '.' && de->name[1] == '.') - rc = 2; - } - } - - return rc; -} - -/* Check if directory that we are searching has changed so we can decide - whether we can use the cached search results from the previous search */ -static int is_dir_changed(struct file *file) -{ - struct inode *inode = file_inode(file); - struct cifsInodeInfo *cifsInfo = CIFS_I(inode); - - if (cifsInfo->time == 0) - return 1; /* directory was changed, perhaps due to unlink */ - else - return 0; - -} - -static int cifs_save_resume_key(const char *current_entry, - struct cifsFileInfo *file_info) -{ - struct cifs_dirent de; - int rc; - - rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level, - file_info->srch_inf.unicode); - if (!rc) { - file_info->srch_inf.presume_name = de.name; - file_info->srch_inf.resume_name_len = de.namelen; - file_info->srch_inf.resume_key = de.resume_key; - } - return rc; -} - -/* - * Find the corresponding entry in the search. Note that the SMB server returns - * search entries for . and .. which complicates logic here if we choose to - * parse for them and we do not assume that they are located in the findfirst - * return buffer. We start counting in the buffer with entry 2 and increment for - * every entry (do not increment for . or .. entry). - */ -static int -find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, - struct file *file, const char *full_path, - char **current_entry, int *num_to_ret) -{ - __u16 search_flags; - int rc = 0; - int pos_in_buf = 0; - loff_t first_entry_in_buffer; - loff_t index_to_find = pos; - struct cifsFileInfo *cfile = file->private_data; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - struct TCP_Server_Info *server = tcon->ses->server; - /* check if index in the buffer */ - - if (!server->ops->query_dir_first || !server->ops->query_dir_next) - return -ENOSYS; - - if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL)) - return -ENOENT; - - *current_entry = NULL; - first_entry_in_buffer = cfile->srch_inf.index_of_last_entry - - cfile->srch_inf.entries_in_buffer; - - /* - * If first entry in buf is zero then is first buffer - * in search response data which means it is likely . and .. - * will be in this buffer, although some servers do not return - * . and .. for the root of a drive and for those we need - * to start two entries earlier. - */ - - dump_cifs_file_struct(file, "In fce "); - if (((index_to_find < cfile->srch_inf.index_of_last_entry) && - is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { - /* close and restart search */ - cifs_dbg(FYI, "search backing up - close and restart search\n"); - spin_lock(&cfile->file_info_lock); - if (server->ops->dir_needs_close(cfile)) { - cfile->invalidHandle = true; - spin_unlock(&cfile->file_info_lock); - if (server->ops->close_dir) - server->ops->close_dir(xid, tcon, &cfile->fid); - } else - spin_unlock(&cfile->file_info_lock); - if (cfile->srch_inf.ntwrk_buf_start) { - cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); - if (cfile->srch_inf.smallBuf) - cifs_small_buf_release(cfile->srch_inf. - ntwrk_buf_start); - else - cifs_buf_release(cfile->srch_inf. - ntwrk_buf_start); - cfile->srch_inf.ntwrk_buf_start = NULL; - } - rc = initiate_cifs_search(xid, file, full_path); - if (rc) { - cifs_dbg(FYI, "error %d reinitiating a search on rewind\n", - rc); - return rc; - } - /* FindFirst/Next set last_entry to NULL on malformed reply */ - if (cfile->srch_inf.last_entry) - cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); - } - - search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; - if (backup_cred(cifs_sb)) - search_flags |= CIFS_SEARCH_BACKUP_SEARCH; - - while ((index_to_find >= cfile->srch_inf.index_of_last_entry) && - (rc == 0) && !cfile->srch_inf.endOfSearch) { - cifs_dbg(FYI, "calling findnext2\n"); - rc = server->ops->query_dir_next(xid, tcon, &cfile->fid, - search_flags, - &cfile->srch_inf); - /* FindFirst/Next set last_entry to NULL on malformed reply */ - if (cfile->srch_inf.last_entry) - cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); - if (rc) - return -ENOENT; - } - if (index_to_find < cfile->srch_inf.index_of_last_entry) { - /* we found the buffer that contains the entry */ - /* scan and find it */ - int i; - char *cur_ent; - char *end_of_smb; - - if (cfile->srch_inf.ntwrk_buf_start == NULL) { - cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n"); - return -EIO; - } - - end_of_smb = cfile->srch_inf.ntwrk_buf_start + - server->ops->calc_smb_size( - cfile->srch_inf.ntwrk_buf_start); - - cur_ent = cfile->srch_inf.srch_entries_start; - first_entry_in_buffer = cfile->srch_inf.index_of_last_entry - - cfile->srch_inf.entries_in_buffer; - pos_in_buf = index_to_find - first_entry_in_buffer; - cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf); - - for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) { - /* go entry by entry figuring out which is first */ - cur_ent = nxt_dir_entry(cur_ent, end_of_smb, - cfile->srch_inf.info_level); - } - if ((cur_ent == NULL) && (i < pos_in_buf)) { - /* BB fixme - check if we should flag this error */ - cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n", - pos_in_buf, index_to_find, rc); - } - rc = 0; - *current_entry = cur_ent; - } else { - cifs_dbg(FYI, "index not in buffer - could not findnext into it\n"); - return 0; - } - - if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) { - cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n"); - *num_to_ret = 0; - } else - *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf; - - return rc; -} - -static bool emit_cached_dirents(struct cached_dirents *cde, - struct dir_context *ctx) -{ - struct cached_dirent *dirent; - bool rc; - - list_for_each_entry(dirent, &cde->entries, entry) { - /* - * Skip all early entries prior to the current lseek() - * position. - */ - if (ctx->pos > dirent->pos) - continue; - /* - * We recorded the current ->pos value for the dirent - * when we stored it in the cache. - * However, this sequence of ->pos values may have holes - * in it, for example dot-dirs returned from the server - * are suppressed. - * Handle this bu forcing ctx->pos to be the same as the - * ->pos of the current dirent we emit from the cache. - * This means that when we emit these entries from the cache - * we now emit them with the same ->pos value as in the - * initial scan. - */ - ctx->pos = dirent->pos; - rc = dir_emit(ctx, dirent->name, dirent->namelen, - dirent->fattr.cf_uniqueid, - dirent->fattr.cf_dtype); - if (!rc) - return rc; - ctx->pos++; - } - return true; -} - -static void update_cached_dirents_count(struct cached_dirents *cde, - struct dir_context *ctx) -{ - if (cde->ctx != ctx) - return; - if (cde->is_valid || cde->is_failed) - return; - - cde->pos++; -} - -static void finished_cached_dirents_count(struct cached_dirents *cde, - struct dir_context *ctx) -{ - if (cde->ctx != ctx) - return; - if (cde->is_valid || cde->is_failed) - return; - if (ctx->pos != cde->pos) - return; - - cde->is_valid = 1; -} - -static void add_cached_dirent(struct cached_dirents *cde, - struct dir_context *ctx, - const char *name, int namelen, - struct cifs_fattr *fattr) -{ - struct cached_dirent *de; - - if (cde->ctx != ctx) - return; - if (cde->is_valid || cde->is_failed) - return; - if (ctx->pos != cde->pos) { - cde->is_failed = 1; - return; - } - de = kzalloc(sizeof(*de), GFP_ATOMIC); - if (de == NULL) { - cde->is_failed = 1; - return; - } - de->namelen = namelen; - de->name = kstrndup(name, namelen, GFP_ATOMIC); - if (de->name == NULL) { - kfree(de); - cde->is_failed = 1; - return; - } - de->pos = ctx->pos; - - memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr)); - - list_add_tail(&de->entry, &cde->entries); -} - -static bool cifs_dir_emit(struct dir_context *ctx, - const char *name, int namelen, - struct cifs_fattr *fattr, - struct cached_fid *cfid) -{ - bool rc; - ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); - - rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype); - if (!rc) - return rc; - - if (cfid) { - mutex_lock(&cfid->dirents.de_mutex); - add_cached_dirent(&cfid->dirents, ctx, name, namelen, - fattr); - mutex_unlock(&cfid->dirents.de_mutex); - } - - return rc; -} - -static int cifs_filldir(char *find_entry, struct file *file, - struct dir_context *ctx, - char *scratch_buf, unsigned int max_len, - struct cached_fid *cfid) -{ - struct cifsFileInfo *file_info = file->private_data; - struct super_block *sb = file_inode(file)->i_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_dirent de = { NULL, }; - struct cifs_fattr fattr; - struct qstr name; - int rc = 0; - - rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level, - file_info->srch_inf.unicode); - if (rc) - return rc; - - if (de.namelen > max_len) { - cifs_dbg(VFS, "bad search response length %zd past smb end\n", - de.namelen); - return -EINVAL; - } - - /* skip . and .. since we added them first */ - if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode)) - return 0; - - if (file_info->srch_inf.unicode) { - struct nls_table *nlt = cifs_sb->local_nls; - int map_type; - - map_type = cifs_remap(cifs_sb); - name.name = scratch_buf; - name.len = - cifs_from_utf16((char *)name.name, (__le16 *)de.name, - UNICODE_NAME_MAX, - min_t(size_t, de.namelen, - (size_t)max_len), nlt, map_type); - name.len -= nls_nullsize(nlt); - } else { - name.name = de.name; - name.len = de.namelen; - } - - switch (file_info->srch_inf.info_level) { - case SMB_FIND_FILE_POSIX_INFO: - cifs_posix_to_fattr(&fattr, - (struct smb2_posix_info *)find_entry, - cifs_sb); - break; - case SMB_FIND_FILE_UNIX: - cifs_unix_basic_to_fattr(&fattr, - &((FILE_UNIX_INFO *)find_entry)->basic, - cifs_sb); - if (S_ISLNK(fattr.cf_mode)) - fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; - break; - case SMB_FIND_FILE_INFO_STANDARD: - cifs_std_info_to_fattr(&fattr, - (FIND_FILE_STANDARD_INFO *)find_entry, - cifs_sb); - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - cifs_fulldir_info_to_fattr(&fattr, - (SEARCH_ID_FULL_DIR_INFO *)find_entry, - cifs_sb); - break; - default: - cifs_dir_info_to_fattr(&fattr, - (FILE_DIRECTORY_INFO *)find_entry, - cifs_sb); - break; - } - - if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { - fattr.cf_uniqueid = de.ino; - } else { - fattr.cf_uniqueid = iunique(sb, ROOT_I); - cifs_autodisable_serverino(cifs_sb); - } - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && - couldbe_mf_symlink(&fattr)) - /* - * trying to get the type and mode can be slow, - * so just call those regular files for now, and mark - * for reval - */ - fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; - - cifs_prime_dcache(file_dentry(file), &name, &fattr); - - return !cifs_dir_emit(ctx, name.name, name.len, - &fattr, cfid); -} - - -int cifs_readdir(struct file *file, struct dir_context *ctx) -{ - int rc = 0; - unsigned int xid; - int i; - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon; - struct cifsFileInfo *cifsFile; - char *current_entry; - int num_to_fill = 0; - char *tmp_buf = NULL; - char *end_of_smb; - unsigned int max_len; - const char *full_path; - void *page = alloc_dentry_path(); - struct cached_fid *cfid = NULL; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - - xid = get_xid(); - - full_path = build_path_from_dentry(file_dentry(file), page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto rddir2_exit; - } - - if (file->private_data == NULL) { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - goto cache_not_found; - tcon = tlink_tcon(tlink); - } else { - cifsFile = file->private_data; - tcon = tlink_tcon(cifsFile->tlink); - } - - rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); - cifs_put_tlink(tlink); - if (rc) - goto cache_not_found; - - mutex_lock(&cfid->dirents.de_mutex); - /* - * If this was reading from the start of the directory - * we need to initialize scanning and storing the - * directory content. - */ - if (ctx->pos == 0 && cfid->dirents.ctx == NULL) { - cfid->dirents.ctx = ctx; - cfid->dirents.pos = 2; - } - /* - * If we already have the entire directory cached then - * we can just serve the cache. - */ - if (cfid->dirents.is_valid) { - if (!dir_emit_dots(file, ctx)) { - mutex_unlock(&cfid->dirents.de_mutex); - goto rddir2_exit; - } - emit_cached_dirents(&cfid->dirents, ctx); - mutex_unlock(&cfid->dirents.de_mutex); - goto rddir2_exit; - } - mutex_unlock(&cfid->dirents.de_mutex); - - /* Drop the cache while calling initiate_cifs_search and - * find_cifs_entry in case there will be reconnects during - * query_directory. - */ - close_cached_dir(cfid); - cfid = NULL; - - cache_not_found: - /* - * Ensure FindFirst doesn't fail before doing filldir() for '.' and - * '..'. Otherwise we won't be able to notify VFS in case of failure. - */ - if (file->private_data == NULL) { - rc = initiate_cifs_search(xid, file, full_path); - cifs_dbg(FYI, "initiate cifs search rc %d\n", rc); - if (rc) - goto rddir2_exit; - } - - if (!dir_emit_dots(file, ctx)) - goto rddir2_exit; - - /* 1) If search is active, - is in current search buffer? - if it before then restart search - if after then keep searching till find it */ - cifsFile = file->private_data; - if (cifsFile->srch_inf.endOfSearch) { - if (cifsFile->srch_inf.emptyDir) { - cifs_dbg(FYI, "End of search, empty dir\n"); - rc = 0; - goto rddir2_exit; - } - } /* else { - cifsFile->invalidHandle = true; - tcon->ses->server->close(xid, tcon, &cifsFile->fid); - } */ - - tcon = tlink_tcon(cifsFile->tlink); - rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path, - ¤t_entry, &num_to_fill); - open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); - if (rc) { - cifs_dbg(FYI, "fce error %d\n", rc); - goto rddir2_exit; - } else if (current_entry != NULL) { - cifs_dbg(FYI, "entry %lld found\n", ctx->pos); - } else { - if (cfid) { - mutex_lock(&cfid->dirents.de_mutex); - finished_cached_dirents_count(&cfid->dirents, ctx); - mutex_unlock(&cfid->dirents.de_mutex); - } - cifs_dbg(FYI, "Could not find entry\n"); - goto rddir2_exit; - } - cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", - num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); - max_len = tcon->ses->server->ops->calc_smb_size( - cifsFile->srch_inf.ntwrk_buf_start); - end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; - - tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); - if (tmp_buf == NULL) { - rc = -ENOMEM; - goto rddir2_exit; - } - - for (i = 0; i < num_to_fill; i++) { - if (current_entry == NULL) { - /* evaluate whether this case is an error */ - cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n", - num_to_fill, i); - break; - } - /* - * if buggy server returns . and .. late do we want to - * check for that here? - */ - *tmp_buf = 0; - rc = cifs_filldir(current_entry, file, ctx, - tmp_buf, max_len, cfid); - if (rc) { - if (rc > 0) - rc = 0; - break; - } - - ctx->pos++; - if (cfid) { - mutex_lock(&cfid->dirents.de_mutex); - update_cached_dirents_count(&cfid->dirents, ctx); - mutex_unlock(&cfid->dirents.de_mutex); - } - - if (ctx->pos == - cifsFile->srch_inf.index_of_last_entry) { - cifs_dbg(FYI, "last entry in buf at pos %lld %s\n", - ctx->pos, tmp_buf); - cifs_save_resume_key(current_entry, cifsFile); - break; - } - current_entry = - nxt_dir_entry(current_entry, end_of_smb, - cifsFile->srch_inf.info_level); - } - kfree(tmp_buf); - -rddir2_exit: - if (cfid) - close_cached_dir(cfid); - free_dentry_path(page); - free_xid(xid); - return rc; -} diff --git a/fs/cifs/rfc1002pdu.h b/fs/cifs/rfc1002pdu.h deleted file mode 100644 index ae1d025da294..000000000000 --- a/fs/cifs/rfc1002pdu.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Protocol Data Unit definitions for RFC 1001/1002 support - * - * Copyright (c) International Business Machines Corp., 2004 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -/* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */ - - /* RFC 1002 session packet types */ -#define RFC1002_SESSION_MESSAGE 0x00 -#define RFC1002_SESSION_REQUEST 0x81 -#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 -#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 -#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 -#define RFC1002_SESSION_KEEP_ALIVE 0x85 - - /* RFC 1002 flags (only one defined */ -#define RFC1002_LENGTH_EXTEND 0x80 /* high order bit of length (ie +64K) */ - -struct rfc1002_session_packet { - __u8 type; - __u8 flags; - __u16 length; - union { - struct { - __u8 called_len; - __u8 called_name[32]; - __u8 scope1; /* null */ - __u8 calling_len; - __u8 calling_name[32]; - __u8 scope2; /* null */ - } __attribute__((packed)) session_req; - struct { - __u32 retarget_ip_addr; - __u16 port; - } __attribute__((packed)) retarget_resp; - __u8 neg_ses_resp_error_code; - /* POSITIVE_SESSION_RESPONSE packet does not include trailer. - SESSION_KEEP_ALIVE packet also does not include a trailer. - Trailer for the SESSION_MESSAGE packet is SMB/CIFS header */ - } __attribute__((packed)) trailer; -} __attribute__((packed)); - -/* Negative Session Response error codes */ -#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */ -#define RFC1002_NOT_LISTENING_CALLING 0x81 /* not listening on calling name */ -#define RFC1002_NOT_PRESENT 0x82 /* called name not present */ -#define RFC1002_INSUFFICIENT_RESOURCE 0x83 -#define RFC1002_UNSPECIFIED_ERROR 0x8F - -/* RFC 1002 Datagram service packets are not defined here as they -are not needed for the network filesystem client unless we plan on -implementing broadcast resolution of the server ip address (from -server netbios name). Currently server names are resolved only via DNS -(tcp name) or ip address or an /etc/hosts equivalent mapping to ip address.*/ - -#define DEFAULT_CIFS_CALLED_NAME "*SMBSERVER " diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c deleted file mode 100644 index 81be17845072..000000000000 --- a/fs/cifs/sess.c +++ /dev/null @@ -1,1858 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * SMB/CIFS session setup handling routines - * - * Copyright (c) International Business Machines Corp., 2006, 2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "ntlmssp.h" -#include "nterr.h" -#include -#include -#include -#include "cifsfs.h" -#include "cifs_spnego.h" -#include "smb2proto.h" -#include "fs_context.h" - -static int -cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, - struct cifs_server_iface *iface); - -bool -is_server_using_iface(struct TCP_Server_Info *server, - struct cifs_server_iface *iface) -{ - struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr; - struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr; - struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr; - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr; - - if (server->dstaddr.ss_family != iface->sockaddr.ss_family) - return false; - if (server->dstaddr.ss_family == AF_INET) { - if (s4->sin_addr.s_addr != i4->sin_addr.s_addr) - return false; - } else if (server->dstaddr.ss_family == AF_INET6) { - if (memcmp(&s6->sin6_addr, &i6->sin6_addr, - sizeof(i6->sin6_addr)) != 0) - return false; - } else { - /* unknown family.. */ - return false; - } - return true; -} - -bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) -{ - int i; - - spin_lock(&ses->chan_lock); - for (i = 0; i < ses->chan_count; i++) { - if (ses->chans[i].iface == iface) { - spin_unlock(&ses->chan_lock); - return true; - } - } - spin_unlock(&ses->chan_lock); - return false; -} - -/* channel helper functions. assumed that chan_lock is held by caller. */ - -unsigned int -cifs_ses_get_chan_index(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int i; - - for (i = 0; i < ses->chan_count; i++) { - if (ses->chans[i].server == server) - return i; - } - - /* If we didn't find the channel, it is likely a bug */ - if (server) - cifs_dbg(VFS, "unable to get chan index for server: 0x%llx", - server->conn_id); - WARN_ON(1); - return 0; -} - -void -cifs_chan_set_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - ses->chans[chan_index].in_reconnect = true; -} - -void -cifs_chan_clear_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - ses->chans[chan_index].in_reconnect = false; -} - -bool -cifs_chan_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - return CIFS_CHAN_IN_RECONNECT(ses, chan_index); -} - -void -cifs_chan_set_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - set_bit(chan_index, &ses->chans_need_reconnect); - cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n", - chan_index, ses->chans_need_reconnect); -} - -void -cifs_chan_clear_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - clear_bit(chan_index, &ses->chans_need_reconnect); - cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n", - chan_index, ses->chans_need_reconnect); -} - -bool -cifs_chan_needs_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index); -} - -bool -cifs_chan_is_iface_active(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - return ses->chans[chan_index].iface && - ses->chans[chan_index].iface->is_active; -} - -/* returns number of channels added */ -int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) -{ - struct TCP_Server_Info *server = ses->server; - int old_chan_count, new_chan_count; - int left; - int rc = 0; - int tries = 0; - struct cifs_server_iface *iface = NULL, *niface = NULL; - - spin_lock(&ses->chan_lock); - - new_chan_count = old_chan_count = ses->chan_count; - left = ses->chan_max - ses->chan_count; - - if (left <= 0) { - spin_unlock(&ses->chan_lock); - cifs_dbg(FYI, - "ses already at max_channels (%zu), nothing to open\n", - ses->chan_max); - return 0; - } - - if (server->dialect < SMB30_PROT_ID) { - spin_unlock(&ses->chan_lock); - cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n"); - return 0; - } - - if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { - ses->chan_max = 1; - spin_unlock(&ses->chan_lock); - cifs_server_dbg(VFS, "no multichannel support\n"); - return 0; - } - spin_unlock(&ses->chan_lock); - - /* - * Keep connecting to same, fastest, iface for all channels as - * long as its RSS. Try next fastest one if not RSS or channel - * creation fails. - */ - spin_lock(&ses->iface_lock); - iface = list_first_entry(&ses->iface_list, struct cifs_server_iface, - iface_head); - spin_unlock(&ses->iface_lock); - - while (left > 0) { - - tries++; - if (tries > 3*ses->chan_max) { - cifs_dbg(FYI, "too many channel open attempts (%d channels left to open)\n", - left); - break; - } - - spin_lock(&ses->iface_lock); - if (!ses->iface_count) { - spin_unlock(&ses->iface_lock); - break; - } - - list_for_each_entry_safe_from(iface, niface, &ses->iface_list, - iface_head) { - /* skip ifaces that are unusable */ - if (!iface->is_active || - (is_ses_using_iface(ses, iface) && - !iface->rss_capable)) { - continue; - } - - /* take ref before unlock */ - kref_get(&iface->refcount); - - spin_unlock(&ses->iface_lock); - rc = cifs_ses_add_channel(cifs_sb, ses, iface); - spin_lock(&ses->iface_lock); - - if (rc) { - cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n", - &iface->sockaddr, - rc); - kref_put(&iface->refcount, release_iface); - continue; - } - - cifs_dbg(FYI, "successfully opened new channel on iface:%pIS\n", - &iface->sockaddr); - break; - } - spin_unlock(&ses->iface_lock); - - left--; - new_chan_count++; - } - - return new_chan_count - old_chan_count; -} - -/* - * update the iface for the channel if necessary. - * will return 0 when iface is updated, 1 if removed, 2 otherwise - * Must be called with chan_lock held. - */ -int -cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) -{ - unsigned int chan_index; - struct cifs_server_iface *iface = NULL; - struct cifs_server_iface *old_iface = NULL; - int rc = 0; - - spin_lock(&ses->chan_lock); - chan_index = cifs_ses_get_chan_index(ses, server); - if (!chan_index) { - spin_unlock(&ses->chan_lock); - return 0; - } - - if (ses->chans[chan_index].iface) { - old_iface = ses->chans[chan_index].iface; - if (old_iface->is_active) { - spin_unlock(&ses->chan_lock); - return 1; - } - } - spin_unlock(&ses->chan_lock); - - spin_lock(&ses->iface_lock); - /* then look for a new one */ - list_for_each_entry(iface, &ses->iface_list, iface_head) { - if (!iface->is_active || - (is_ses_using_iface(ses, iface) && - !iface->rss_capable)) { - continue; - } - kref_get(&iface->refcount); - break; - } - - if (list_entry_is_head(iface, &ses->iface_list, iface_head)) { - rc = 1; - iface = NULL; - cifs_dbg(FYI, "unable to find a suitable iface\n"); - } - - /* now drop the ref to the current iface */ - if (old_iface && iface) { - cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n", - &old_iface->sockaddr, - &iface->sockaddr); - kref_put(&old_iface->refcount, release_iface); - } else if (old_iface) { - cifs_dbg(FYI, "releasing ref to iface: %pIS\n", - &old_iface->sockaddr); - kref_put(&old_iface->refcount, release_iface); - } else { - WARN_ON(!iface); - cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr); - } - spin_unlock(&ses->iface_lock); - - spin_lock(&ses->chan_lock); - chan_index = cifs_ses_get_chan_index(ses, server); - ses->chans[chan_index].iface = iface; - - /* No iface is found. if secondary chan, drop connection */ - if (!iface && CIFS_SERVER_IS_CHAN(server)) - ses->chans[chan_index].server = NULL; - - spin_unlock(&ses->chan_lock); - - if (!iface && CIFS_SERVER_IS_CHAN(server)) - cifs_put_tcp_session(server, false); - - return rc; -} - -/* - * If server is a channel of ses, return the corresponding enclosing - * cifs_chan otherwise return NULL. - */ -struct cifs_chan * -cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server) -{ - int i; - - spin_lock(&ses->chan_lock); - for (i = 0; i < ses->chan_count; i++) { - if (ses->chans[i].server == server) { - spin_unlock(&ses->chan_lock); - return &ses->chans[i]; - } - } - spin_unlock(&ses->chan_lock); - return NULL; -} - -static int -cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, - struct cifs_server_iface *iface) -{ - struct TCP_Server_Info *chan_server; - struct cifs_chan *chan; - struct smb3_fs_context ctx = {NULL}; - static const char unc_fmt[] = "\\%s\\foo"; - char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0}; - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; - int rc; - unsigned int xid = get_xid(); - - if (iface->sockaddr.ss_family == AF_INET) - cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI4)\n", - ses, iface->speed, iface->rdma_capable ? "yes" : "no", - &ipv4->sin_addr); - else - cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI6)\n", - ses, iface->speed, iface->rdma_capable ? "yes" : "no", - &ipv6->sin6_addr); - - /* - * Setup a ctx with mostly the same info as the existing - * session and overwrite it with the requested iface data. - * - * We need to setup at least the fields used for negprot and - * sesssetup. - * - * We only need the ctx here, so we can reuse memory from - * the session and server without caring about memory - * management. - */ - - /* Always make new connection for now (TODO?) */ - ctx.nosharesock = true; - - /* Auth */ - ctx.domainauto = ses->domainAuto; - ctx.domainname = ses->domainName; - - /* no hostname for extra channels */ - ctx.server_hostname = ""; - - ctx.username = ses->user_name; - ctx.password = ses->password; - ctx.sectype = ses->sectype; - ctx.sign = ses->sign; - - /* UNC and paths */ - /* XXX: Use ses->server->hostname? */ - sprintf(unc, unc_fmt, ses->ip_addr); - ctx.UNC = unc; - ctx.prepath = ""; - - /* Reuse same version as master connection */ - ctx.vals = ses->server->vals; - ctx.ops = ses->server->ops; - - ctx.noblocksnd = ses->server->noblocksnd; - ctx.noautotune = ses->server->noautotune; - ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay; - ctx.echo_interval = ses->server->echo_interval / HZ; - ctx.max_credits = ses->server->max_credits; - - /* - * This will be used for encoding/decoding user/domain/pw - * during sess setup auth. - */ - ctx.local_nls = cifs_sb->local_nls; - - /* Use RDMA if possible */ - ctx.rdma = iface->rdma_capable; - memcpy(&ctx.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage)); - - /* reuse master con client guid */ - memcpy(&ctx.client_guid, ses->server->client_guid, - SMB2_CLIENT_GUID_SIZE); - ctx.use_client_guid = true; - - chan_server = cifs_get_tcp_session(&ctx, ses->server); - - spin_lock(&ses->chan_lock); - chan = &ses->chans[ses->chan_count]; - chan->server = chan_server; - if (IS_ERR(chan->server)) { - rc = PTR_ERR(chan->server); - chan->server = NULL; - spin_unlock(&ses->chan_lock); - goto out; - } - chan->iface = iface; - ses->chan_count++; - atomic_set(&ses->chan_seq, 0); - - /* Mark this channel as needing connect/setup */ - cifs_chan_set_need_reconnect(ses, chan->server); - - spin_unlock(&ses->chan_lock); - - mutex_lock(&ses->session_mutex); - /* - * We need to allocate the server crypto now as we will need - * to sign packets before we generate the channel signing key - * (we sign with the session key) - */ - rc = smb311_crypto_shash_allocate(chan->server); - if (rc) { - cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); - mutex_unlock(&ses->session_mutex); - goto out; - } - - rc = cifs_negotiate_protocol(xid, ses, chan->server); - if (!rc) - rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls); - - mutex_unlock(&ses->session_mutex); - -out: - if (rc && chan->server) { - /* - * we should avoid race with these delayed works before we - * remove this channel - */ - cancel_delayed_work_sync(&chan->server->echo); - cancel_delayed_work_sync(&chan->server->resolve); - cancel_delayed_work_sync(&chan->server->reconnect); - - spin_lock(&ses->chan_lock); - /* we rely on all bits beyond chan_count to be clear */ - cifs_chan_clear_need_reconnect(ses, chan->server); - ses->chan_count--; - /* - * chan_count should never reach 0 as at least the primary - * channel is always allocated - */ - WARN_ON(ses->chan_count < 1); - spin_unlock(&ses->chan_lock); - - cifs_put_tcp_session(chan->server, 0); - } - - free_xid(xid); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, - struct TCP_Server_Info *server, - SESSION_SETUP_ANDX *pSMB) -{ - __u32 capabilities = 0; - - /* init fields common to all four types of SessSetup */ - /* Note that offsets for first seven fields in req struct are same */ - /* in CIFS Specs so does not matter which of 3 forms of struct */ - /* that we use in next few lines */ - /* Note that header is initialized to zero in header_assemble */ - pSMB->req.AndXCommand = 0xFF; - pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, - CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, - USHRT_MAX)); - pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq); - pSMB->req.VcNumber = cpu_to_le16(1); - - /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ - - /* BB verify whether signing required on neg or just on auth frame - (and NTLM case) */ - - capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | - CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; - - if (server->sign) - pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - if (ses->capabilities & CAP_UNICODE) { - pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE; - capabilities |= CAP_UNICODE; - } - if (ses->capabilities & CAP_STATUS32) { - pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS; - capabilities |= CAP_STATUS32; - } - if (ses->capabilities & CAP_DFS) { - pSMB->req.hdr.Flags2 |= SMBFLG2_DFS; - capabilities |= CAP_DFS; - } - if (ses->capabilities & CAP_UNIX) - capabilities |= CAP_UNIX; - - return capabilities; -} - -static void -unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* Copy OS version */ - bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32, - nls_cp); - bcc_ptr += 2 * bytes_ret; - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ - - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ - - *pbcc_area = bcc_ptr; -} - -static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* copy domain */ - if (ses->domainName == NULL) { - /* Sending null domain better than using a bogus domain name (as - we did briefly in 2.6.18) since server will use its default */ - *bcc_ptr = 0; - *(bcc_ptr+1) = 0; - bytes_ret = 0; - } else - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, - CIFS_MAX_DOMAINNAME_LEN, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* account for null terminator */ - - *pbcc_area = bcc_ptr; -} - -static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* BB FIXME add check that strings total less - than 335 or will need to send them as arrays */ - - /* copy user */ - if (ses->user_name == NULL) { - /* null user mount */ - *bcc_ptr = 0; - *(bcc_ptr+1) = 0; - } else { - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, - CIFS_MAX_USERNAME_LEN, nls_cp); - } - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* account for null termination */ - - unicode_domain_string(&bcc_ptr, ses, nls_cp); - unicode_oslm_strings(&bcc_ptr, nls_cp); - - *pbcc_area = bcc_ptr; -} - -static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int len; - - /* copy user */ - /* BB what about null user mounts - check that we do this BB */ - /* copy user */ - if (ses->user_name != NULL) { - len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN); - if (WARN_ON_ONCE(len < 0)) - len = CIFS_MAX_USERNAME_LEN - 1; - bcc_ptr += len; - } - /* else null user mount */ - *bcc_ptr = 0; - bcc_ptr++; /* account for null termination */ - - /* copy domain */ - if (ses->domainName != NULL) { - len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); - if (WARN_ON_ONCE(len < 0)) - len = CIFS_MAX_DOMAINNAME_LEN - 1; - bcc_ptr += len; - } /* else we will send a null domain name - so the server will default to its own domain */ - *bcc_ptr = 0; - bcc_ptr++; - - /* BB check for overflow here */ - - strcpy(bcc_ptr, "Linux version "); - bcc_ptr += strlen("Linux version "); - strcpy(bcc_ptr, init_utsname()->release); - bcc_ptr += strlen(init_utsname()->release) + 1; - - strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); - bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; - - *pbcc_area = bcc_ptr; -} - -static void -decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - int len; - char *data = *pbcc_area; - - cifs_dbg(FYI, "bleft %d\n", bleft); - - kfree(ses->serverOS); - ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS); - len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; - data += len; - bleft -= len; - if (bleft <= 0) - return; - - kfree(ses->serverNOS); - ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS); - len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; - data += len; - bleft -= len; - if (bleft <= 0) - return; - - kfree(ses->serverDomain); - ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain); - - return; -} - -static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, - struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - int len; - char *bcc_ptr = *pbcc_area; - - cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft); - - len = strnlen(bcc_ptr, bleft); - if (len >= bleft) - return; - - kfree(ses->serverOS); - - ses->serverOS = kmalloc(len + 1, GFP_KERNEL); - if (ses->serverOS) { - memcpy(ses->serverOS, bcc_ptr, len); - ses->serverOS[len] = 0; - if (strncmp(ses->serverOS, "OS/2", 4) == 0) - cifs_dbg(FYI, "OS/2 server\n"); - } - - bcc_ptr += len + 1; - bleft -= len + 1; - - len = strnlen(bcc_ptr, bleft); - if (len >= bleft) - return; - - kfree(ses->serverNOS); - - ses->serverNOS = kmalloc(len + 1, GFP_KERNEL); - if (ses->serverNOS) { - memcpy(ses->serverNOS, bcc_ptr, len); - ses->serverNOS[len] = 0; - } - - bcc_ptr += len + 1; - bleft -= len + 1; - - len = strnlen(bcc_ptr, bleft); - if (len > bleft) - return; - - /* No domain field in LANMAN case. Domain is - returned by old servers in the SMB negprot response */ - /* BB For newer servers which do not support Unicode, - but thus do return domain here we could add parsing - for it later, but it is not very important */ - cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, - struct cifs_ses *ses) -{ - unsigned int tioffset; /* challenge message target info area */ - unsigned int tilen; /* challenge message target info area length */ - CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; - __u32 server_flags; - - if (blob_len < sizeof(CHALLENGE_MESSAGE)) { - cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len); - return -EINVAL; - } - - if (memcmp(pblob->Signature, "NTLMSSP", 8)) { - cifs_dbg(VFS, "blob signature incorrect %s\n", - pblob->Signature); - return -EINVAL; - } - if (pblob->MessageType != NtLmChallenge) { - cifs_dbg(VFS, "Incorrect message type %d\n", - pblob->MessageType); - return -EINVAL; - } - - server_flags = le32_to_cpu(pblob->NegotiateFlags); - cifs_dbg(FYI, "%s: negotiate=0x%08x challenge=0x%08x\n", __func__, - ses->ntlmssp->client_flags, server_flags); - - if ((ses->ntlmssp->client_flags & (NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN)) && - (!(server_flags & NTLMSSP_NEGOTIATE_56) && !(server_flags & NTLMSSP_NEGOTIATE_128))) { - cifs_dbg(VFS, "%s: requested signing/encryption but server did not return either 56-bit or 128-bit session key size\n", - __func__); - return -EINVAL; - } - if (!(server_flags & NTLMSSP_NEGOTIATE_NTLM) && !(server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) { - cifs_dbg(VFS, "%s: server does not seem to support either NTLMv1 or NTLMv2\n", __func__); - return -EINVAL; - } - if (ses->server->sign && !(server_flags & NTLMSSP_NEGOTIATE_SIGN)) { - cifs_dbg(VFS, "%s: forced packet signing but server does not seem to support it\n", - __func__); - return -EOPNOTSUPP; - } - if ((ses->ntlmssp->client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && - !(server_flags & NTLMSSP_NEGOTIATE_KEY_XCH)) - pr_warn_once("%s: authentication has been weakened as server does not support key exchange\n", - __func__); - - ses->ntlmssp->server_flags = server_flags; - - memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); - /* In particular we can examine sign flags */ - /* BB spec says that if AvId field of MsvAvTimestamp is populated then - we must set the MIC field of the AUTHENTICATE_MESSAGE */ - - tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); - tilen = le16_to_cpu(pblob->TargetInfoArray.Length); - if (tioffset > blob_len || tioffset + tilen > blob_len) { - cifs_dbg(VFS, "tioffset + tilen too high %u + %u\n", - tioffset, tilen); - return -EINVAL; - } - if (tilen) { - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, "Challenge target info alloc failure\n"); - return -ENOMEM; - } - ses->auth_key.len = tilen; - } - - return 0; -} - -static int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size) -{ - int sz = base_size + ses->auth_key.len - - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; - - if (ses->domainName) - sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); - else - sz += sizeof(__le16); - - if (ses->user_name) - sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); - else - sz += sizeof(__le16); - - if (ses->workstation_name[0]) - sz += sizeof(__le16) * strnlen(ses->workstation_name, - ntlmssp_workstation_name_size(ses)); - else - sz += sizeof(__le16); - - return sz; -} - -static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf, - char *str_value, - int str_length, - unsigned char *pstart, - unsigned char **pcur, - const struct nls_table *nls_cp) -{ - unsigned char *tmp = pstart; - int len; - - if (!pbuf) - return; - - if (!pcur) - pcur = &tmp; - - if (!str_value) { - pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); - pbuf->Length = 0; - pbuf->MaximumLength = 0; - *pcur += sizeof(__le16); - } else { - len = cifs_strtoUTF16((__le16 *)*pcur, - str_value, - str_length, - nls_cp); - len *= sizeof(__le16); - pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); - pbuf->Length = cpu_to_le16(len); - pbuf->MaximumLength = cpu_to_le16(len); - *pcur += len; - } -} - -/* BB Move to ntlmssp.c eventually */ - -int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, - u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - NEGOTIATE_MESSAGE *sec_blob; - __u32 flags; - unsigned char *tmp; - int len; - - len = size_of_ntlmssp_blob(ses, sizeof(NEGOTIATE_MESSAGE)); - *pbuffer = kmalloc(len, GFP_KERNEL); - if (!*pbuffer) { - rc = -ENOMEM; - cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); - *buflen = 0; - goto setup_ntlm_neg_ret; - } - sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer; - - memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE)); - memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); - sec_blob->MessageType = NtLmNegotiate; - - /* BB is NTLMV2 session security format easier to use here? */ - flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | - NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | - NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | - NTLMSSP_NEGOTIATE_SIGN; - if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) - flags |= NTLMSSP_NEGOTIATE_KEY_XCH; - - tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE); - ses->ntlmssp->client_flags = flags; - sec_blob->NegotiateFlags = cpu_to_le32(flags); - - /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ - cifs_security_buffer_from_str(&sec_blob->DomainName, - NULL, - CIFS_MAX_DOMAINNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->WorkstationName, - NULL, - CIFS_MAX_WORKSTATION_LEN, - *pbuffer, &tmp, - nls_cp); - - *buflen = tmp - *pbuffer; -setup_ntlm_neg_ret: - return rc; -} - -/* - * Build ntlmssp blob with additional fields, such as version, - * supported by modern servers. For safety limit to SMB3 or later - * See notes in MS-NLMP Section 2.2.2.1 e.g. - */ -int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, - u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - struct negotiate_message *sec_blob; - __u32 flags; - unsigned char *tmp; - int len; - - len = size_of_ntlmssp_blob(ses, sizeof(struct negotiate_message)); - *pbuffer = kmalloc(len, GFP_KERNEL); - if (!*pbuffer) { - rc = -ENOMEM; - cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); - *buflen = 0; - goto setup_ntlm_smb3_neg_ret; - } - sec_blob = (struct negotiate_message *)*pbuffer; - - memset(*pbuffer, 0, sizeof(struct negotiate_message)); - memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); - sec_blob->MessageType = NtLmNegotiate; - - /* BB is NTLMV2 session security format easier to use here? */ - flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | - NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | - NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | - NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_VERSION; - if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) - flags |= NTLMSSP_NEGOTIATE_KEY_XCH; - - sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR; - sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL; - sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD); - sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3; - - tmp = *pbuffer + sizeof(struct negotiate_message); - ses->ntlmssp->client_flags = flags; - sec_blob->NegotiateFlags = cpu_to_le32(flags); - - /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ - cifs_security_buffer_from_str(&sec_blob->DomainName, - NULL, - CIFS_MAX_DOMAINNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->WorkstationName, - NULL, - CIFS_MAX_WORKSTATION_LEN, - *pbuffer, &tmp, - nls_cp); - - *buflen = tmp - *pbuffer; -setup_ntlm_smb3_neg_ret: - return rc; -} - - -int build_ntlmssp_auth_blob(unsigned char **pbuffer, - u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc; - AUTHENTICATE_MESSAGE *sec_blob; - __u32 flags; - unsigned char *tmp; - int len; - - rc = setup_ntlmv2_rsp(ses, nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); - *buflen = 0; - goto setup_ntlmv2_ret; - } - - len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE)); - *pbuffer = kmalloc(len, GFP_KERNEL); - if (!*pbuffer) { - rc = -ENOMEM; - cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); - *buflen = 0; - goto setup_ntlmv2_ret; - } - sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer; - - memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); - sec_blob->MessageType = NtLmAuthenticate; - - flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET | - NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; - - tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE); - sec_blob->NegotiateFlags = cpu_to_le32(flags); - - sec_blob->LmChallengeResponse.BufferOffset = - cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE)); - sec_blob->LmChallengeResponse.Length = 0; - sec_blob->LmChallengeResponse.MaximumLength = 0; - - sec_blob->NtChallengeResponse.BufferOffset = - cpu_to_le32(tmp - *pbuffer); - if (ses->user_name != NULL) { - memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - ses->auth_key.len - CIFS_SESS_KEY_SIZE); - tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - - sec_blob->NtChallengeResponse.Length = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - sec_blob->NtChallengeResponse.MaximumLength = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - } else { - /* - * don't send an NT Response for anonymous access - */ - sec_blob->NtChallengeResponse.Length = 0; - sec_blob->NtChallengeResponse.MaximumLength = 0; - } - - cifs_security_buffer_from_str(&sec_blob->DomainName, - ses->domainName, - CIFS_MAX_DOMAINNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->UserName, - ses->user_name, - CIFS_MAX_USERNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->WorkstationName, - ses->workstation_name, - ntlmssp_workstation_name_size(ses), - *pbuffer, &tmp, - nls_cp); - - if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && - (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) && - !calc_seckey(ses)) { - memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); - sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); - sec_blob->SessionKey.MaximumLength = - cpu_to_le16(CIFS_CPHTXT_SIZE); - tmp += CIFS_CPHTXT_SIZE; - } else { - sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->SessionKey.Length = 0; - sec_blob->SessionKey.MaximumLength = 0; - } - - *buflen = tmp - *pbuffer; -setup_ntlmv2_ret: - return rc; -} - -enum securityEnum -cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) -{ - switch (server->negflavor) { - case CIFS_NEGFLAVOR_EXTENDED: - switch (requested) { - case Kerberos: - case RawNTLMSSP: - return requested; - case Unspecified: - if (server->sec_ntlmssp && - (global_secflags & CIFSSEC_MAY_NTLMSSP)) - return RawNTLMSSP; - if ((server->sec_kerberos || server->sec_mskerberos) && - (global_secflags & CIFSSEC_MAY_KRB5)) - return Kerberos; - fallthrough; - default: - return Unspecified; - } - case CIFS_NEGFLAVOR_UNENCAP: - switch (requested) { - case NTLMv2: - return requested; - case Unspecified: - if (global_secflags & CIFSSEC_MAY_NTLMV2) - return NTLMv2; - break; - default: - break; - } - fallthrough; - default: - return Unspecified; - } -} - -struct sess_data { - unsigned int xid; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct nls_table *nls_cp; - void (*func)(struct sess_data *); - int result; - - /* we will send the SMB in three pieces: - * a fixed length beginning part, an optional - * SPNEGO blob (which can be zero length), and a - * last part which will include the strings - * and rest of bcc area. This allows us to avoid - * a large buffer 17K allocation - */ - int buf0_type; - struct kvec iov[3]; -}; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -sess_alloc_buffer(struct sess_data *sess_data, int wct) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct smb_hdr *smb_buf; - - rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, - (void **)&smb_buf); - - if (rc) - return rc; - - sess_data->iov[0].iov_base = (char *)smb_buf; - sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4; - /* - * This variable will be used to clear the buffer - * allocated above in case of any error in the calling function. - */ - sess_data->buf0_type = CIFS_SMALL_BUFFER; - - /* 2000 big enough to fit max user, domain, NOS name etc. */ - sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL); - if (!sess_data->iov[2].iov_base) { - rc = -ENOMEM; - goto out_free_smb_buf; - } - - return 0; - -out_free_smb_buf: - cifs_small_buf_release(smb_buf); - sess_data->iov[0].iov_base = NULL; - sess_data->iov[0].iov_len = 0; - sess_data->buf0_type = CIFS_NO_BUFFER; - return rc; -} - -static void -sess_free_buffer(struct sess_data *sess_data) -{ - struct kvec *iov = sess_data->iov; - - /* - * Zero the session data before freeing, as it might contain sensitive info (keys, etc). - * Note that iov[1] is already freed by caller. - */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) - memzero_explicit(iov[0].iov_base, iov[0].iov_len); - - free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); - sess_data->buf0_type = CIFS_NO_BUFFER; - kfree_sensitive(iov[2].iov_base); -} - -static int -sess_establish_session(struct sess_data *sess_data) -{ - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - cifs_server_lock(server); - if (!server->session_estab) { - if (server->sign) { - server->session_key.response = - kmemdup(ses->auth_key.response, - ses->auth_key.len, GFP_KERNEL); - if (!server->session_key.response) { - cifs_server_unlock(server); - return -ENOMEM; - } - server->session_key.len = - ses->auth_key.len; - } - server->sequence_number = 0x2; - server->session_estab = true; - } - cifs_server_unlock(server); - - cifs_dbg(FYI, "CIFS session established successfully\n"); - return 0; -} - -static int -sess_sendreceive(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base; - __u16 count; - struct kvec rsp_iov = { NULL, 0 }; - - count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; - be32_add_cpu(&smb_buf->smb_buf_length, count); - put_bcc(count, smb_buf); - - rc = SendReceive2(sess_data->xid, sess_data->ses, - sess_data->iov, 3 /* num_iovecs */, - &sess_data->buf0_type, - CIFS_LOG_ERROR, &rsp_iov); - cifs_small_buf_release(sess_data->iov[0].iov_base); - memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); - - return rc; -} - -static void -sess_auth_ntlmv2(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - __u16 bytes_remaining; - - /* old style NTLM sessionsetup */ - /* wct = 13 */ - rc = sess_alloc_buffer(sess_data, 13); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - - pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - - /* LM2 password would be here if we supported it */ - pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; - - if (ses->user_name != NULL) { - /* calculate nlmv2 response and session key */ - rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); - goto out; - } - - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - ses->auth_key.len - CIFS_SESS_KEY_SIZE); - bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - - /* set case sensitive password length after tilen may get - * assigned, tilen is 0 otherwise. - */ - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - } else { - pSMB->req_no_secext.CaseSensitivePasswordLength = 0; - } - - if (ses->capabilities & CAP_UNICODE) { - if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } else { - ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } - - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - if (smb_buf->WordCount != 3) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; -} - -#ifdef CONFIG_CIFS_UPCALL -static void -sess_auth_kerberos(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - __u16 bytes_remaining; - struct key *spnego_key = NULL; - struct cifs_spnego_msg *msg; - u16 blob_len; - - /* extended security */ - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - - spnego_key = cifs_get_spnego_key(ses, server); - if (IS_ERR(spnego_key)) { - rc = PTR_ERR(spnego_key); - spnego_key = NULL; - goto out; - } - - msg = spnego_key->payload.data[0]; - /* - * check version field to make sure that cifs.upcall is - * sending us a response in an expected form - */ - if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { - cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n", - CIFS_SPNEGO_UPCALL_VERSION, msg->version); - rc = -EKEYREJECTED; - goto out_put_spnego_key; - } - - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", - msg->sesskey_len); - rc = -ENOMEM; - goto out_put_spnego_key; - } - ses->auth_key.len = msg->sesskey_len; - - pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; - capabilities |= CAP_EXTENDED_SECURITY; - pSMB->req.Capabilities = cpu_to_le32(capabilities); - sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; - sess_data->iov[1].iov_len = msg->secblob_len; - pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len); - - if (ses->capabilities & CAP_UNICODE) { - /* unicode strings must be word aligned */ - if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); - unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp); - } else { - /* BB: is this right? */ - ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out_put_spnego_key; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - if (smb_buf->WordCount != 4) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_put_spnego_key; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_put_spnego_key; - } - bcc_ptr += blob_len; - bytes_remaining -= blob_len; - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out_put_spnego_key: - key_invalidate(spnego_key); - key_put(spnego_key); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; -} - -#endif /* ! CONFIG_CIFS_UPCALL */ - -/* - * The required kvec buffers have to be allocated before calling this - * function. - */ -static int -_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) -{ - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - char *bcc_ptr; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { - cifs_dbg(VFS, "NTLMSSP requires Unicode support\n"); - return -ENOSYS; - } - - pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; - capabilities |= CAP_EXTENDED_SECURITY; - pSMB->req.Capabilities |= cpu_to_le32(capabilities); - - bcc_ptr = sess_data->iov[2].iov_base; - /* unicode strings must be word aligned */ - if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - return 0; -} - -static void -sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data); - -static void -sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u16 bytes_remaining; - char *bcc_ptr; - unsigned char *ntlmsspblob = NULL; - u16 blob_len; - - cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n"); - - /* - * if memory allocation is successful, caller of this function - * frees it. - */ - ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); - if (!ses->ntlmssp) { - rc = -ENOMEM; - goto out; - } - ses->ntlmssp->sesskey_per_smbsess = false; - - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - - /* Build security blob before we assemble the request */ - rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, - &blob_len, ses, server, - sess_data->nls_cp); - if (rc) - goto out_free_ntlmsspblob; - - sess_data->iov[1].iov_len = blob_len; - sess_data->iov[1].iov_base = ntlmsspblob; - pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); - - rc = _sess_auth_rawntlmssp_assemble_req(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - rc = sess_sendreceive(sess_data); - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - /* If true, rc here is expected and not an error */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && - smb_buf->Status.CifsError == - cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) - rc = 0; - - if (rc) - goto out_free_ntlmsspblob; - - cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); - - if (smb_buf->WordCount != 4) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_free_ntlmsspblob; - } - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_free_ntlmsspblob; - } - - rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); - -out_free_ntlmsspblob: - kfree_sensitive(ntlmsspblob); -out: - sess_free_buffer(sess_data); - - if (!rc) { - sess_data->func = sess_auth_rawntlmssp_authenticate; - return; - } - - /* Else error. Cleanup */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - - sess_data->func = NULL; - sess_data->result = rc; -} - -static void -sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u16 bytes_remaining; - char *bcc_ptr; - unsigned char *ntlmsspblob = NULL; - u16 blob_len; - - cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); - - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - /* Build security blob before we assemble the request */ - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)pSMB; - rc = build_ntlmssp_auth_blob(&ntlmsspblob, - &blob_len, ses, server, - sess_data->nls_cp); - if (rc) - goto out_free_ntlmsspblob; - sess_data->iov[1].iov_len = blob_len; - sess_data->iov[1].iov_base = ntlmsspblob; - pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); - /* - * Make sure that we tell the server that we are using - * the uid that it just gave us back on the response - * (challenge) - */ - smb_buf->Uid = ses->Suid; - - rc = _sess_auth_rawntlmssp_assemble_req(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - if (smb_buf->WordCount != 4) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_free_ntlmsspblob; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - if (ses->Suid != smb_buf->Uid) { - ses->Suid = smb_buf->Uid; - cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid); - } - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_free_ntlmsspblob; - } - bcc_ptr += blob_len; - bytes_remaining -= blob_len; - - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - -out_free_ntlmsspblob: - kfree_sensitive(ntlmsspblob); -out: - sess_free_buffer(sess_data); - - if (!rc) - rc = sess_establish_session(sess_data); - - /* Cleanup */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - - sess_data->func = NULL; - sess_data->result = rc; -} - -static int select_sec(struct sess_data *sess_data) -{ - int type; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - type = cifs_select_sectype(server, ses->sectype); - cifs_dbg(FYI, "sess setup type %d\n", type); - if (type == Unspecified) { - cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); - return -EINVAL; - } - - switch (type) { - case NTLMv2: - sess_data->func = sess_auth_ntlmv2; - break; - case Kerberos: -#ifdef CONFIG_CIFS_UPCALL - sess_data->func = sess_auth_kerberos; - break; -#else - cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); - return -ENOSYS; -#endif /* CONFIG_CIFS_UPCALL */ - case RawNTLMSSP: - sess_data->func = sess_auth_rawntlmssp_negotiate; - break; - default: - cifs_dbg(VFS, "secType %d not supported!\n", type); - return -ENOSYS; - } - - return 0; -} - -int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - struct sess_data *sess_data; - - if (ses == NULL) { - WARN(1, "%s: ses == NULL!", __func__); - return -EINVAL; - } - - sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); - if (!sess_data) - return -ENOMEM; - - sess_data->xid = xid; - sess_data->ses = ses; - sess_data->server = server; - sess_data->buf0_type = CIFS_NO_BUFFER; - sess_data->nls_cp = (struct nls_table *) nls_cp; - - rc = select_sec(sess_data); - if (rc) - goto out; - - while (sess_data->func) - sess_data->func(sess_data); - - /* Store result before we free sess_data */ - rc = sess_data->result; - -out: - kfree_sensitive(sess_data); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c deleted file mode 100644 index 7d1b3fc014d9..000000000000 --- a/fs/cifs/smb1ops.c +++ /dev/null @@ -1,1276 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * SMB1 (CIFS) version specific operations - * - * Copyright (c) 2012, Jeff Layton - */ - -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifspdu.h" -#include "cifs_unicode.h" -#include "fs_context.h" - -/* - * An NT cancel request header looks just like the original request except: - * - * The Command is SMB_COM_NT_CANCEL - * The WordCount is zeroed out - * The ByteCount is zeroed out - * - * This function mangles an existing request buffer into a - * SMB_COM_NT_CANCEL request and then sends it. - */ -static int -send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, - struct mid_q_entry *mid) -{ - int rc = 0; - struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - /* -4 for RFC1001 length and +2 for BCC field */ - in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); - in_buf->Command = SMB_COM_NT_CANCEL; - in_buf->WordCount = 0; - put_bcc(0, in_buf); - - cifs_server_lock(server); - rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); - if (rc) { - cifs_server_unlock(server); - return rc; - } - - /* - * The response to this call was already factored into the sequence - * number when the call went out, so we must adjust it back downward - * after signing here. - */ - --server->sequence_number; - rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); - if (rc < 0) - server->sequence_number--; - - cifs_server_unlock(server); - - cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n", - get_mid(in_buf), rc); - - return rc; -} - -static bool -cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) -{ - return ob1->fid.netfid == ob2->fid.netfid; -} - -static unsigned int -cifs_read_data_offset(char *buf) -{ - READ_RSP *rsp = (READ_RSP *)buf; - return le16_to_cpu(rsp->DataOffset); -} - -static unsigned int -cifs_read_data_length(char *buf, bool in_remaining) -{ - READ_RSP *rsp = (READ_RSP *)buf; - /* It's a bug reading remaining data for SMB1 packets */ - WARN_ON(in_remaining); - return (le16_to_cpu(rsp->DataLengthHigh) << 16) + - le16_to_cpu(rsp->DataLength); -} - -static struct mid_q_entry * -cifs_find_mid(struct TCP_Server_Info *server, char *buffer) -{ - struct smb_hdr *buf = (struct smb_hdr *)buffer; - struct mid_q_entry *mid; - - spin_lock(&server->mid_lock); - list_for_each_entry(mid, &server->pending_mid_q, qhead) { - if (compare_mid(mid->mid, buf) && - mid->mid_state == MID_REQUEST_SUBMITTED && - le16_to_cpu(mid->command) == buf->Command) { - kref_get(&mid->refcount); - spin_unlock(&server->mid_lock); - return mid; - } - } - spin_unlock(&server->mid_lock); - return NULL; -} - -static void -cifs_add_credits(struct TCP_Server_Info *server, - const struct cifs_credits *credits, const int optype) -{ - spin_lock(&server->req_lock); - server->credits += credits->value; - server->in_flight--; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); -} - -static void -cifs_set_credits(struct TCP_Server_Info *server, const int val) -{ - spin_lock(&server->req_lock); - server->credits = val; - server->oplocks = val > 1 ? enable_oplocks : false; - spin_unlock(&server->req_lock); -} - -static int * -cifs_get_credits_field(struct TCP_Server_Info *server, const int optype) -{ - return &server->credits; -} - -static unsigned int -cifs_get_credits(struct mid_q_entry *mid) -{ - return 1; -} - -/* - * Find a free multiplex id (SMB mid). Otherwise there could be - * mid collisions which might cause problems, demultiplexing the - * wrong response to this request. Multiplex ids could collide if - * one of a series requests takes much longer than the others, or - * if a very large number of long lived requests (byte range - * locks or FindNotify requests) are pending. No more than - * 64K-1 requests can be outstanding at one time. If no - * mids are available, return zero. A future optimization - * could make the combination of mids and uid the key we use - * to demultiplex on (rather than mid alone). - * In addition to the above check, the cifs demultiplex - * code already used the command code as a secondary - * check of the frame and if signing is negotiated the - * response would be discarded if the mid were the same - * but the signature was wrong. Since the mid is not put in the - * pending queue until later (when it is about to be dispatched) - * we do have to limit the number of outstanding requests - * to somewhat less than 64K-1 although it is hard to imagine - * so many threads being in the vfs at one time. - */ -static __u64 -cifs_get_next_mid(struct TCP_Server_Info *server) -{ - __u64 mid = 0; - __u16 last_mid, cur_mid; - bool collision, reconnect = false; - - spin_lock(&server->mid_lock); - - /* mid is 16 bit only for CIFS/SMB */ - cur_mid = (__u16)((server->CurrentMid) & 0xffff); - /* we do not want to loop forever */ - last_mid = cur_mid; - cur_mid++; - /* avoid 0xFFFF MID */ - if (cur_mid == 0xffff) - cur_mid++; - - /* - * This nested loop looks more expensive than it is. - * In practice the list of pending requests is short, - * fewer than 50, and the mids are likely to be unique - * on the first pass through the loop unless some request - * takes longer than the 64 thousand requests before it - * (and it would also have to have been a request that - * did not time out). - */ - while (cur_mid != last_mid) { - struct mid_q_entry *mid_entry; - unsigned int num_mids; - - collision = false; - if (cur_mid == 0) - cur_mid++; - - num_mids = 0; - list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { - ++num_mids; - if (mid_entry->mid == cur_mid && - mid_entry->mid_state == MID_REQUEST_SUBMITTED) { - /* This mid is in use, try a different one */ - collision = true; - break; - } - } - - /* - * if we have more than 32k mids in the list, then something - * is very wrong. Possibly a local user is trying to DoS the - * box by issuing long-running calls and SIGKILL'ing them. If - * we get to 2^16 mids then we're in big trouble as this - * function could loop forever. - * - * Go ahead and assign out the mid in this situation, but force - * an eventual reconnect to clean out the pending_mid_q. - */ - if (num_mids > 32768) - reconnect = true; - - if (!collision) { - mid = (__u64)cur_mid; - server->CurrentMid = mid; - break; - } - cur_mid++; - } - spin_unlock(&server->mid_lock); - - if (reconnect) { - cifs_signal_cifsd_for_reconnect(server, false); - } - - return mid; -} - -/* - return codes: - 0 not a transact2, or all data present - >0 transact2 with that much data missing - -EINVAL invalid transact2 - */ -static int -check2ndT2(char *buf) -{ - struct smb_hdr *pSMB = (struct smb_hdr *)buf; - struct smb_t2_rsp *pSMBt; - int remaining; - __u16 total_data_size, data_in_this_rsp; - - if (pSMB->Command != SMB_COM_TRANSACTION2) - return 0; - - /* check for plausible wct, bcc and t2 data and parm sizes */ - /* check for parm and data offset going beyond end of smb */ - if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ - cifs_dbg(FYI, "Invalid transact2 word count\n"); - return -EINVAL; - } - - pSMBt = (struct smb_t2_rsp *)pSMB; - - total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); - - if (total_data_size == data_in_this_rsp) - return 0; - else if (total_data_size < data_in_this_rsp) { - cifs_dbg(FYI, "total data %d smaller than data in frame %d\n", - total_data_size, data_in_this_rsp); - return -EINVAL; - } - - remaining = total_data_size - data_in_this_rsp; - - cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n", - remaining); - if (total_data_size > CIFSMaxBufSize) { - cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n", - total_data_size, CIFSMaxBufSize); - return -EINVAL; - } - return remaining; -} - -static int -coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) -{ - struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; - struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; - char *data_area_of_tgt; - char *data_area_of_src; - int remaining; - unsigned int byte_count, total_in_tgt; - __u16 tgt_total_cnt, src_total_cnt, total_in_src; - - src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); - tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - - if (tgt_total_cnt != src_total_cnt) - cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n", - src_total_cnt, tgt_total_cnt); - - total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); - - remaining = tgt_total_cnt - total_in_tgt; - - if (remaining < 0) { - cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n", - tgt_total_cnt, total_in_tgt); - return -EPROTO; - } - - if (remaining == 0) { - /* nothing to do, ignore */ - cifs_dbg(FYI, "no more data remains\n"); - return 0; - } - - total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); - if (remaining < total_in_src) - cifs_dbg(FYI, "transact2 2nd response contains too much data\n"); - - /* find end of first SMB data area */ - data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + - get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); - - /* validate target area */ - data_area_of_src = (char *)&pSMBs->hdr.Protocol + - get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); - - data_area_of_tgt += total_in_tgt; - - total_in_tgt += total_in_src; - /* is the result too big for the field? */ - if (total_in_tgt > USHRT_MAX) { - cifs_dbg(FYI, "coalesced DataCount too large (%u)\n", - total_in_tgt); - return -EPROTO; - } - put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); - - /* fix up the BCC */ - byte_count = get_bcc(target_hdr); - byte_count += total_in_src; - /* is the result too big for the field? */ - if (byte_count > USHRT_MAX) { - cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); - return -EPROTO; - } - put_bcc(byte_count, target_hdr); - - byte_count = be32_to_cpu(target_hdr->smb_buf_length); - byte_count += total_in_src; - /* don't allow buffer to overflow */ - if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", - byte_count); - return -ENOBUFS; - } - target_hdr->smb_buf_length = cpu_to_be32(byte_count); - - /* copy second buffer into end of first buffer */ - memcpy(data_area_of_tgt, data_area_of_src, total_in_src); - - if (remaining != total_in_src) { - /* more responses to go */ - cifs_dbg(FYI, "waiting for more secondary responses\n"); - return 1; - } - - /* we are done */ - cifs_dbg(FYI, "found the last secondary response\n"); - return 0; -} - -static void -cifs_downgrade_oplock(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - cifs_set_oplock_level(cinode, oplock); -} - -static bool -cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, - char *buf, int malformed) -{ - if (malformed) - return false; - if (check2ndT2(buf) <= 0) - return false; - mid->multiRsp = true; - if (mid->resp_buf) { - /* merge response - fix up 1st*/ - malformed = coalesce_t2(buf, mid->resp_buf); - if (malformed > 0) - return true; - /* All parts received or packet is malformed. */ - mid->multiEnd = true; - dequeue_mid(mid, malformed); - return true; - } - if (!server->large_buf) { - /*FIXME: switch to already allocated largebuf?*/ - cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n"); - } else { - /* Have first buffer */ - mid->resp_buf = buf; - mid->large_buf = true; - server->bigbuf = NULL; - } - return true; -} - -static bool -cifs_need_neg(struct TCP_Server_Info *server) -{ - return server->maxBuf == 0; -} - -static int -cifs_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - int rc; - rc = CIFSSMBNegotiate(xid, ses, server); - if (rc == -EAGAIN) { - /* retry only once on 1st time connection */ - set_credits(server, 1); - rc = CIFSSMBNegotiate(xid, ses, server); - if (rc == -EAGAIN) - rc = -EHOSTDOWN; - } - return rc; -} - -static unsigned int -cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int wsize; - - /* start with specified wsize, or default */ - if (ctx->wsize) - wsize = ctx->wsize; - else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) - wsize = CIFS_DEFAULT_IOSIZE; - else - wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; - - /* can server support 24-bit write sizes? (via UNIX extensions) */ - if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) - wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE); - - /* - * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set? - * Limit it to max buffer offered by the server, minus the size of the - * WRITEX header, not including the 4 byte RFC1001 length. - */ - if (!(server->capabilities & CAP_LARGE_WRITE_X) || - (!(server->capabilities & CAP_UNIX) && server->sign)) - wsize = min_t(unsigned int, wsize, - server->maxBuf - sizeof(WRITE_REQ) + 4); - - /* hard limit of CIFS_MAX_WSIZE */ - wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE); - - return wsize; -} - -static unsigned int -cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int rsize, defsize; - - /* - * Set default value... - * - * HACK alert! Ancient servers have very small buffers. Even though - * MS-CIFS indicates that servers are only limited by the client's - * bufsize for reads, testing against win98se shows that it throws - * INVALID_PARAMETER errors if you try to request too large a read. - * OS/2 just sends back short reads. - * - * If the server doesn't advertise CAP_LARGE_READ_X, then assume that - * it can't handle a read request larger than its MaxBufferSize either. - */ - if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP)) - defsize = CIFS_DEFAULT_IOSIZE; - else if (server->capabilities & CAP_LARGE_READ_X) - defsize = CIFS_DEFAULT_NON_POSIX_RSIZE; - else - defsize = server->maxBuf - sizeof(READ_RSP); - - rsize = ctx->rsize ? ctx->rsize : defsize; - - /* - * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to - * the client's MaxBufferSize. - */ - if (!(server->capabilities & CAP_LARGE_READ_X)) - rsize = min_t(unsigned int, CIFSMaxBufSize, rsize); - - /* hard limit of CIFS_MAX_RSIZE */ - rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE); - - return rsize; -} - -static void -cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb) -{ - CIFSSMBQFSDeviceInfo(xid, tcon); - CIFSSMBQFSAttributeInfo(xid, tcon); -} - -static int -cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path) -{ - int rc; - FILE_ALL_INFO *file_info; - - file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); - if (file_info == NULL) - return -ENOMEM; - - rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, - 0 /* not legacy */, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - - if (rc == -EOPNOTSUPP || rc == -EINVAL) - rc = SMBQueryInformation(xid, tcon, full_path, file_info, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - kfree(file_info); - return rc; -} - -static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink) -{ - int rc; - FILE_ALL_INFO fi = {}; - - *symlink = false; - - /* could do find first instead but this returns more info */ - rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - /* - * BB optimize code so we do not make the above call when server claims - * no NT SMB support and the above call failed at least once - set flag - * in tcon or mount. - */ - if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { - rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - *adjustTZ = true; - } - - if (!rc) { - int tmprc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - - move_cifs_info_to_smb2(&data->fi, &fi); - - if (!(le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) - return 0; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = FILE_READ_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - /* Need to check if this is a symbolic link or not */ - tmprc = CIFS_open(xid, &oparms, &oplock, NULL); - if (tmprc == -EOPNOTSUPP) - *symlink = true; - else if (tmprc == 0) - CIFSSMBClose(xid, tcon, fid.netfid); - } - - return rc; -} - -static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - u64 *uniqueid, struct cifs_open_info_data *unused) -{ - /* - * We can not use the IndexNumber field by default from Windows or - * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA - * CIFS spec claims that this value is unique within the scope of a - * share, and the windows docs hint that it's actually unique - * per-machine. - * - * There may be higher info levels that work but are there Windows - * server or network appliances for which IndexNumber field is not - * guaranteed unique? - */ - return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -} - -static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct cifs_open_info_data *data) -{ - int rc; - FILE_ALL_INFO fi = {}; - - if (cfile->symlink_target) { - data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!data->symlink_target) - return -ENOMEM; - } - - rc = CIFSSMBQFileInfo(xid, tcon, cfile->fid.netfid, &fi); - if (!rc) - move_cifs_info_to_smb2(&data->fi, &fi); - return rc; -} - -static void -cifs_clear_stats(struct cifs_tcon *tcon) -{ - atomic_set(&tcon->stats.cifs_stats.num_writes, 0); - atomic_set(&tcon->stats.cifs_stats.num_reads, 0); - atomic_set(&tcon->stats.cifs_stats.num_flushes, 0); - atomic_set(&tcon->stats.cifs_stats.num_oplock_brks, 0); - atomic_set(&tcon->stats.cifs_stats.num_opens, 0); - atomic_set(&tcon->stats.cifs_stats.num_posixopens, 0); - atomic_set(&tcon->stats.cifs_stats.num_posixmkdirs, 0); - atomic_set(&tcon->stats.cifs_stats.num_closes, 0); - atomic_set(&tcon->stats.cifs_stats.num_deletes, 0); - atomic_set(&tcon->stats.cifs_stats.num_mkdirs, 0); - atomic_set(&tcon->stats.cifs_stats.num_rmdirs, 0); - atomic_set(&tcon->stats.cifs_stats.num_renames, 0); - atomic_set(&tcon->stats.cifs_stats.num_t2renames, 0); - atomic_set(&tcon->stats.cifs_stats.num_ffirst, 0); - atomic_set(&tcon->stats.cifs_stats.num_fnext, 0); - atomic_set(&tcon->stats.cifs_stats.num_fclose, 0); - atomic_set(&tcon->stats.cifs_stats.num_hardlinks, 0); - atomic_set(&tcon->stats.cifs_stats.num_symlinks, 0); - atomic_set(&tcon->stats.cifs_stats.num_locks, 0); - atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0); - atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0); -} - -static void -cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon) -{ - seq_printf(m, " Oplocks breaks: %d", - atomic_read(&tcon->stats.cifs_stats.num_oplock_brks)); - seq_printf(m, "\nReads: %d Bytes: %llu", - atomic_read(&tcon->stats.cifs_stats.num_reads), - (long long)(tcon->bytes_read)); - seq_printf(m, "\nWrites: %d Bytes: %llu", - atomic_read(&tcon->stats.cifs_stats.num_writes), - (long long)(tcon->bytes_written)); - seq_printf(m, "\nFlushes: %d", - atomic_read(&tcon->stats.cifs_stats.num_flushes)); - seq_printf(m, "\nLocks: %d HardLinks: %d Symlinks: %d", - atomic_read(&tcon->stats.cifs_stats.num_locks), - atomic_read(&tcon->stats.cifs_stats.num_hardlinks), - atomic_read(&tcon->stats.cifs_stats.num_symlinks)); - seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", - atomic_read(&tcon->stats.cifs_stats.num_opens), - atomic_read(&tcon->stats.cifs_stats.num_closes), - atomic_read(&tcon->stats.cifs_stats.num_deletes)); - seq_printf(m, "\nPosix Opens: %d Posix Mkdirs: %d", - atomic_read(&tcon->stats.cifs_stats.num_posixopens), - atomic_read(&tcon->stats.cifs_stats.num_posixmkdirs)); - seq_printf(m, "\nMkdirs: %d Rmdirs: %d", - atomic_read(&tcon->stats.cifs_stats.num_mkdirs), - atomic_read(&tcon->stats.cifs_stats.num_rmdirs)); - seq_printf(m, "\nRenames: %d T2 Renames %d", - atomic_read(&tcon->stats.cifs_stats.num_renames), - atomic_read(&tcon->stats.cifs_stats.num_t2renames)); - seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", - atomic_read(&tcon->stats.cifs_stats.num_ffirst), - atomic_read(&tcon->stats.cifs_stats.num_fnext), - atomic_read(&tcon->stats.cifs_stats.num_fclose)); -} - -static void -cifs_mkdir_setinfo(struct inode *inode, const char *full_path, - struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, - const unsigned int xid) -{ - FILE_BASIC_INFO info; - struct cifsInodeInfo *cifsInode; - u32 dosattrs; - int rc; - - memset(&info, 0, sizeof(info)); - cifsInode = CIFS_I(inode); - dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; - info.Attributes = cpu_to_le32(dosattrs); - rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls, - cifs_sb); - if (rc == 0) - cifsInode->cifsAttrs = dosattrs; -} - -static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, - void *buf) -{ - struct cifs_open_info_data *data = buf; - FILE_ALL_INFO fi = {}; - int rc; - - if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS)) - rc = SMBLegacyOpen(xid, oparms->tcon, oparms->path, - oparms->disposition, - oparms->desired_access, - oparms->create_options, - &oparms->fid->netfid, oplock, &fi, - oparms->cifs_sb->local_nls, - cifs_remap(oparms->cifs_sb)); - else - rc = CIFS_open(xid, oparms, oplock, &fi); - - if (!rc && data) - move_cifs_info_to_smb2(&data->fi, &fi); - - return rc; -} - -static void -cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - cfile->fid.netfid = fid->netfid; - cifs_set_oplock_level(cinode, oplock); - cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); -} - -static void -cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - CIFSSMBClose(xid, tcon, fid->netfid); -} - -static int -cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return CIFSSMBFlush(xid, tcon, fid->netfid); -} - -static int -cifs_sync_read(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *bytes_read, - char **buf, int *buf_type) -{ - parms->netfid = pfid->netfid; - return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type); -} - -static int -cifs_sync_write(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *written, - struct kvec *iov, unsigned long nr_segs) -{ - - parms->netfid = pfid->netfid; - return CIFSSMBWrite2(xid, parms, written, iov, nr_segs); -} - -static int -smb_set_file_info(struct inode *inode, const char *full_path, - FILE_BASIC_INFO *buf, const unsigned int xid) -{ - int oplock = 0; - int rc; - __u32 netpid; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifsFileInfo *open_file; - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon; - - /* if the file is already open for write, just use that fileid */ - open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY); - if (open_file) { - fid.netfid = open_file->fid.netfid; - netpid = open_file->pid; - tcon = tlink_tcon(open_file->tlink); - goto set_via_filehandle; - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - tlink = NULL; - goto out; - } - tcon = tlink_tcon(tlink); - - rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, - cifs_sb); - if (rc == 0) { - cinode->cifsAttrs = le32_to_cpu(buf->Attributes); - goto out; - } else if (rc != -EOPNOTSUPP && rc != -EINVAL) { - goto out; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc != 0) { - if (rc == -EIO) - rc = -EINVAL; - goto out; - } - - netpid = current->tgid; - -set_via_filehandle: - rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid); - if (!rc) - cinode->cifsAttrs = le32_to_cpu(buf->Attributes); - - if (open_file == NULL) - CIFSSMBClose(xid, tcon, fid.netfid); - else - cifsFileInfo_put(open_file); -out: - if (tlink != NULL) - cifs_put_tlink(tlink); - return rc; -} - -static int -cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid); -} - -static int -cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, struct cifs_sb_info *cifs_sb, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - int rc; - - rc = CIFSFindFirst(xid, tcon, path, cifs_sb, - &fid->netfid, search_flags, srch_inf, true); - if (rc) - cifs_dbg(FYI, "find first failed=%d\n", rc); - return rc; -} - -static int -cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf); -} - -static int -cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return CIFSFindClose(xid, tcon, fid->netfid); -} - -static int -cifs_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, - __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) -{ - return CIFSSMBLock(0, tcon, net_fid, current->tgid, 0, 0, 0, 0, - LOCKING_ANDX_OPLOCK_RELEASE, false, CIFS_CACHE_READ(cinode) ? 1 : 0); -} - -static int -cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct kstatfs *buf) -{ - int rc = -EOPNOTSUPP; - - buf->f_type = CIFS_SUPER_MAGIC; - - /* - * We could add a second check for a QFS Unix capability bit - */ - if ((tcon->ses->capabilities & CAP_UNIX) && - (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability))) - rc = CIFSSMBQFSPosixInfo(xid, tcon, buf); - - /* - * Only need to call the old QFSInfo if failed on newer one, - * e.g. by OS/2. - **/ - if (rc && (tcon->ses->capabilities & CAP_NT_SMBS)) - rc = CIFSSMBQFSInfo(xid, tcon, buf); - - /* - * Some old Windows servers also do not support level 103, retry with - * older level one if old server failed the previous call or we - * bypassed it because we detected that this was an older LANMAN sess - */ - if (rc) - rc = SMBOldQFSInfo(xid, tcon, buf); - return rc; -} - -static int -cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u32 type, int lock, int unlock, bool wait) -{ - return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid, - current->tgid, length, offset, unlock, lock, - (__u8)type, wait, 0); -} - -static int -cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, char **symlinkinfo, - const struct nls_table *nls_codepage) -{ -#ifdef CONFIG_CIFS_DFS_UPCALL - int rc; - struct dfs_info3_param referral = {0}; - - rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, &referral, - 0); - - if (!rc) { - *symlinkinfo = kstrdup(referral.node_name, GFP_KERNEL); - free_dfs_info_param(&referral); - if (!*symlinkinfo) - rc = -ENOMEM; - } - return rc; -#else /* No DFS support */ - return -EREMOTE; -#endif -} - -static int -cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - char **target_path, bool is_reparse_point) -{ - int rc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); - - if (is_reparse_point) { - cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n"); - return -EOPNOTSUPP; - } - - /* Check for unix extensions */ - if (cap_unix(tcon->ses)) { - rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (rc == -EREMOTE) - rc = cifs_unix_dfs_readlink(xid, tcon, full_path, - target_path, - cifs_sb->local_nls); - - goto out; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = FILE_READ_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, - OPEN_REPARSE_POINT), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - goto out; - - rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path, - cifs_sb->local_nls); - if (rc) - goto out_close; - - convert_delimiter(*target_path, '/'); -out_close: - CIFSSMBClose(xid, tcon, fid.netfid); -out: - if (!rc) - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); - return rc; -} - -static bool -cifs_is_read_op(__u32 oplock) -{ - return oplock == OPLOCK_READ; -} - -static unsigned int -cifs_wp_retry_size(struct inode *inode) -{ - return CIFS_SB(inode->i_sb)->ctx->wsize; -} - -static bool -cifs_dir_needs_close(struct cifsFileInfo *cfile) -{ - return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; -} - -static bool -cifs_can_echo(struct TCP_Server_Info *server) -{ - if (server->tcpStatus == CifsGood) - return true; - - return false; -} - -static int -cifs_make_node(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct inode *newinode = NULL; - int rc = -EPERM; - struct cifs_open_info_data buf = {}; - struct cifs_io_parms io_parms; - __u32 oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - unsigned int bytes_written; - struct win_dev *pdev; - struct kvec iov[2]; - - if (tcon->unix_ext) { - /* - * SMB1 Unix Extensions: requires server support but - * works with all special files - */ - struct cifs_unix_set_info_args args = { - .mode = mode & ~current_umask(), - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = dev, - }; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = current_fsuid(); - args.gid = current_fsgid(); - } else { - args.uid = INVALID_UID; /* no change */ - args.gid = INVALID_GID; /* no change */ - } - rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (rc) - return rc; - - rc = cifs_get_inode_info_unix(&newinode, full_path, - inode->i_sb, xid); - - if (rc == 0) - d_instantiate(dentry, newinode); - return rc; - } - - /* - * SMB1 SFU emulation: should work with all servers, but only - * support block and char device (no socket & fifo) - */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) - return rc; - - if (!S_ISCHR(mode) && !S_ISBLK(mode)) - return rc; - - cifs_dbg(FYI, "sfu compat create special file\n"); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | - CREATE_OPTION_SPECIAL), - .disposition = FILE_CREATE, - .path = full_path, - .fid = &fid, - }; - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); - if (rc) - return rc; - - /* - * BB Do not bother to decode buf since no local inode yet to put - * timestamps in, but we can reuse it safely. - */ - - pdev = (struct win_dev *)&buf.fi; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = sizeof(struct win_dev); - iov[1].iov_base = &buf.fi; - iov[1].iov_len = sizeof(struct win_dev); - if (S_ISCHR(mode)) { - memcpy(pdev->type, "IntxCHR", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } else if (S_ISBLK(mode)) { - memcpy(pdev->type, "IntxBLK", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } - tcon->ses->server->ops->close(xid, tcon, &fid); - d_drop(dentry); - - /* FIXME: add code here to set EAs */ - - cifs_free_open_info(&buf); - return rc; -} - - - -struct smb_version_operations smb1_operations = { - .send_cancel = send_nt_cancel, - .compare_fids = cifs_compare_fids, - .setup_request = cifs_setup_request, - .setup_async_request = cifs_setup_async_request, - .check_receive = cifs_check_receive, - .add_credits = cifs_add_credits, - .set_credits = cifs_set_credits, - .get_credits_field = cifs_get_credits_field, - .get_credits = cifs_get_credits, - .wait_mtu_credits = cifs_wait_mtu_credits, - .get_next_mid = cifs_get_next_mid, - .read_data_offset = cifs_read_data_offset, - .read_data_length = cifs_read_data_length, - .map_error = map_smb_to_linux_error, - .find_mid = cifs_find_mid, - .check_message = checkSMB, - .dump_detail = cifs_dump_detail, - .clear_stats = cifs_clear_stats, - .print_stats = cifs_print_stats, - .is_oplock_break = is_valid_oplock_break, - .downgrade_oplock = cifs_downgrade_oplock, - .check_trans2 = cifs_check_trans2, - .need_neg = cifs_need_neg, - .negotiate = cifs_negotiate, - .negotiate_wsize = cifs_negotiate_wsize, - .negotiate_rsize = cifs_negotiate_rsize, - .sess_setup = CIFS_SessSetup, - .logoff = CIFSSMBLogoff, - .tree_connect = CIFSTCon, - .tree_disconnect = CIFSSMBTDis, - .get_dfs_refer = CIFSGetDFSRefer, - .qfs_tcon = cifs_qfs_tcon, - .is_path_accessible = cifs_is_path_accessible, - .can_echo = cifs_can_echo, - .query_path_info = cifs_query_path_info, - .query_file_info = cifs_query_file_info, - .get_srv_inum = cifs_get_srv_inum, - .set_path_size = CIFSSMBSetEOF, - .set_file_size = CIFSSMBSetFileSize, - .set_file_info = smb_set_file_info, - .set_compression = cifs_set_compression, - .echo = CIFSSMBEcho, - .mkdir = CIFSSMBMkDir, - .mkdir_setinfo = cifs_mkdir_setinfo, - .rmdir = CIFSSMBRmDir, - .unlink = CIFSSMBDelFile, - .rename_pending_delete = cifs_rename_pending_delete, - .rename = CIFSSMBRename, - .create_hardlink = CIFSCreateHardLink, - .query_symlink = cifs_query_symlink, - .open = cifs_open_file, - .set_fid = cifs_set_fid, - .close = cifs_close_file, - .flush = cifs_flush_file, - .async_readv = cifs_async_readv, - .async_writev = cifs_async_writev, - .sync_read = cifs_sync_read, - .sync_write = cifs_sync_write, - .query_dir_first = cifs_query_dir_first, - .query_dir_next = cifs_query_dir_next, - .close_dir = cifs_close_dir, - .calc_smb_size = smbCalcSize, - .oplock_response = cifs_oplock_response, - .queryfs = cifs_queryfs, - .mand_lock = cifs_mand_lock, - .mand_unlock_range = cifs_unlock_range, - .push_mand_locks = cifs_push_mandatory_locks, - .query_mf_symlink = cifs_query_mf_symlink, - .create_mf_symlink = cifs_create_mf_symlink, - .is_read_op = cifs_is_read_op, - .wp_retry_size = cifs_wp_retry_size, - .dir_needs_close = cifs_dir_needs_close, - .select_sectype = cifs_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = CIFSSMBQAllEAs, - .set_EA = CIFSSMBSetEA, -#endif /* CIFS_XATTR */ - .get_acl = get_cifs_acl, - .get_acl_by_fid = get_cifs_acl_by_fid, - .set_acl = set_cifs_acl, - .make_node = cifs_make_node, -}; - -struct smb_version_values smb1_values = { - .version_string = SMB1_VERSION_STRING, - .protocol_id = SMB10_PROT_ID, - .large_lock_type = LOCKING_ANDX_LARGE_FILES, - .exclusive_lock_type = 0, - .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, - .unlock_lock_type = 0, - .header_preamble_size = 4, - .header_size = sizeof(struct smb_hdr), - .max_header_size = MAX_CIFS_HDR_SIZE, - .read_rsp_size = sizeof(READ_RSP), - .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), - .cap_unix = CAP_UNIX, - .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, - .cap_large_files = CAP_LARGE_FILES, - .signing_enabled = SECMODE_SIGN_ENABLED, - .signing_required = SECMODE_SIGN_REQUIRED, -}; diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c deleted file mode 100644 index ba6cc50af390..000000000000 --- a/fs/cifs/smb2file.c +++ /dev/null @@ -1,371 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Author(s): Steve French (sfrench@us.ibm.com), - * Pavel Shilovsky ((pshilovsky@samba.org) 2012 - * - */ -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fscache.h" -#include "smb2proto.h" -#include "smb2status.h" - -static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) -{ - struct smb2_err_rsp *err = iov->iov_base; - struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL); - u32 len; - - if (err->ErrorContextCount) { - struct smb2_error_context_rsp *p, *end; - - len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp, - ErrorContextData) + - sizeof(struct smb2_symlink_err_rsp)); - if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err)) - return ERR_PTR(-EINVAL); - - p = (struct smb2_error_context_rsp *)err->ErrorData; - end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len); - do { - if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) { - sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData; - break; - } - cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n", - __func__, le32_to_cpu(p->ErrorId)); - - len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8); - p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len); - } while (p < end); - } else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) && - iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) { - sym = (struct smb2_symlink_err_rsp *)err->ErrorData; - } - - if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG || - le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK)) - sym = ERR_PTR(-EINVAL); - - return sym; -} - -int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path) -{ - struct smb2_symlink_err_rsp *sym; - unsigned int sub_offs, sub_len; - unsigned int print_offs, print_len; - char *s; - - if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path) - return -EINVAL; - - sym = symlink_data(iov); - if (IS_ERR(sym)) - return PTR_ERR(sym); - - sub_len = le16_to_cpu(sym->SubstituteNameLength); - sub_offs = le16_to_cpu(sym->SubstituteNameOffset); - print_len = le16_to_cpu(sym->PrintNameLength); - print_offs = le16_to_cpu(sym->PrintNameOffset); - - if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len || - iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len) - return -EINVAL; - - s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true, - cifs_sb->local_nls); - if (!s) - return -ENOMEM; - convert_delimiter(s, '/'); - cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s); - - *path = s; - return 0; -} - -int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf) -{ - int rc; - __le16 *smb2_path; - __u8 smb2_oplock; - struct cifs_open_info_data *data = buf; - struct smb2_file_all_info file_info = {}; - struct smb2_file_all_info *smb2_data = data ? &file_info : NULL; - struct kvec err_iov = {}; - int err_buftype = CIFS_NO_BUFFER; - struct cifs_fid *fid = oparms->fid; - struct network_resiliency_req nr_ioctl_req; - - smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); - if (smb2_path == NULL) - return -ENOMEM; - - oparms->desired_access |= FILE_READ_ATTRIBUTES; - smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; - - rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, - &err_buftype); - if (rc && data) { - struct smb2_hdr *hdr = err_iov.iov_base; - - if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER)) - goto out; - if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) { - rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, - &data->symlink_target); - if (!rc) { - memset(smb2_data, 0, sizeof(*smb2_data)); - oparms->create_options |= OPEN_REPARSE_POINT; - rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, - NULL, NULL, NULL); - oparms->create_options &= ~OPEN_REPARSE_POINT; - } - } - } - - if (rc) - goto out; - - if (oparms->tcon->use_resilient) { - /* default timeout is 0, servers pick default (120 seconds) */ - nr_ioctl_req.Timeout = - cpu_to_le32(oparms->tcon->handle_timeout); - nr_ioctl_req.Reserved = 0; - rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, - fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, - (char *)&nr_ioctl_req, sizeof(nr_ioctl_req), - CIFSMaxBufSize, NULL, NULL /* no return info */); - if (rc == -EOPNOTSUPP) { - cifs_dbg(VFS, - "resiliency not supported by server, disabling\n"); - oparms->tcon->use_resilient = false; - } else if (rc) - cifs_dbg(FYI, "error %d setting resiliency\n", rc); - - rc = 0; - } - - if (smb2_data) { - /* if open response does not have IndexNumber field - get it */ - if (smb2_data->IndexNumber == 0) { - rc = SMB2_get_srv_num(xid, oparms->tcon, - fid->persistent_fid, - fid->volatile_fid, - &smb2_data->IndexNumber); - if (rc) { - /* - * let get_inode_info disable server inode - * numbers - */ - smb2_data->IndexNumber = 0; - rc = 0; - } - } - memcpy(&data->fi, smb2_data, sizeof(data->fi)); - } - - *oplock = smb2_oplock; -out: - free_rsp_buf(err_buftype, err_iov.iov_base); - kfree(smb2_path); - return rc; -} - -int -smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, - const unsigned int xid) -{ - int rc = 0, stored_rc; - unsigned int max_num, num = 0, max_buf; - struct smb2_lock_element *buf, *cur; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifsLockInfo *li, *tmp; - __u64 length = 1 + flock->fl_end - flock->fl_start; - struct list_head tmp_llist; - - INIT_LIST_HEAD(&tmp_llist); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it before using. - */ - max_buf = tcon->ses->server->maxBuf; - if (max_buf < sizeof(struct smb2_lock_element)) - return -EINVAL; - - BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); - max_num = max_buf / sizeof(struct smb2_lock_element); - buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - cur = buf; - - cifs_down_write(&cinode->lock_sem); - list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { - if (flock->fl_start > li->offset || - (flock->fl_start + length) < - (li->offset + li->length)) - continue; - if (current->tgid != li->pid) - /* - * flock and OFD lock are associated with an open - * file description, not the process. - */ - if (!(flock->fl_flags & (FL_FLOCK | FL_OFDLCK))) - continue; - if (cinode->can_cache_brlcks) { - /* - * We can cache brlock requests - simply remove a lock - * from the file's list. - */ - list_del(&li->llist); - cifs_del_lock_waiters(li); - kfree(li); - continue; - } - cur->Length = cpu_to_le64(li->length); - cur->Offset = cpu_to_le64(li->offset); - cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); - /* - * We need to save a lock here to let us add it again to the - * file's list if the unlock range request fails on the server. - */ - list_move(&li->llist, &tmp_llist); - if (++num == max_num) { - stored_rc = smb2_lockv(xid, tcon, - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, num, buf); - if (stored_rc) { - /* - * We failed on the unlock range request - add - * all locks from the tmp list to the head of - * the file's list. - */ - cifs_move_llist(&tmp_llist, - &cfile->llist->locks); - rc = stored_rc; - } else - /* - * The unlock range request succeed - free the - * tmp list. - */ - cifs_free_llist(&tmp_llist); - cur = buf; - num = 0; - } else - cur++; - } - if (num) { - stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, current->tgid, - num, buf); - if (stored_rc) { - cifs_move_llist(&tmp_llist, &cfile->llist->locks); - rc = stored_rc; - } else - cifs_free_llist(&tmp_llist); - } - up_write(&cinode->lock_sem); - - kfree(buf); - return rc; -} - -static int -smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid, - struct smb2_lock_element *buf, unsigned int max_num) -{ - int rc = 0, stored_rc; - struct cifsFileInfo *cfile = fdlocks->cfile; - struct cifsLockInfo *li; - unsigned int num = 0; - struct smb2_lock_element *cur = buf; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - - list_for_each_entry(li, &fdlocks->locks, llist) { - cur->Length = cpu_to_le64(li->length); - cur->Offset = cpu_to_le64(li->offset); - cur->Flags = cpu_to_le32(li->type | - SMB2_LOCKFLAG_FAIL_IMMEDIATELY); - if (++num == max_num) { - stored_rc = smb2_lockv(xid, tcon, - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, num, buf); - if (stored_rc) - rc = stored_rc; - cur = buf; - num = 0; - } else - cur++; - } - if (num) { - stored_rc = smb2_lockv(xid, tcon, - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, num, buf); - if (stored_rc) - rc = stored_rc; - } - - return rc; -} - -int -smb2_push_mandatory_locks(struct cifsFileInfo *cfile) -{ - int rc = 0, stored_rc; - unsigned int xid; - unsigned int max_num, max_buf; - struct smb2_lock_element *buf; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifs_fid_locks *fdlocks; - - xid = get_xid(); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it for zero before using. - */ - max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; - if (max_buf < sizeof(struct smb2_lock_element)) { - free_xid(xid); - return -EINVAL; - } - - BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); - max_num = max_buf / sizeof(struct smb2_lock_element); - buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); - if (!buf) { - free_xid(xid); - return -ENOMEM; - } - - list_for_each_entry(fdlocks, &cinode->llist, llist) { - stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num); - if (stored_rc) - rc = stored_rc; - } - - kfree(buf); - free_xid(xid); - return rc; -} diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h deleted file mode 100644 index 82e916ad167c..000000000000 --- a/fs/cifs/smb2glob.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Definitions for various global variables and structures - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ -#ifndef _SMB2_GLOB_H -#define _SMB2_GLOB_H - -/* - ***************************************************************** - * Constants go here - ***************************************************************** - */ - -/* - * Identifiers for functions that use the open, operation, close pattern - * in smb2inode.c:smb2_compound_op() - */ -#define SMB2_OP_SET_DELETE 1 -#define SMB2_OP_SET_INFO 2 -#define SMB2_OP_QUERY_INFO 3 -#define SMB2_OP_QUERY_DIR 4 -#define SMB2_OP_MKDIR 5 -#define SMB2_OP_RENAME 6 -#define SMB2_OP_DELETE 7 -#define SMB2_OP_HARDLINK 8 -#define SMB2_OP_SET_EOF 9 -#define SMB2_OP_RMDIR 10 -#define SMB2_OP_POSIX_QUERY_INFO 11 - -/* Used when constructing chained read requests. */ -#define CHAINED_REQUEST 1 -#define START_OF_CHAIN 2 -#define END_OF_CHAIN 4 -#define RELATED_REQUEST 8 - -#endif /* _SMB2_GLOB_H */ diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c deleted file mode 100644 index c97e049e29dd..000000000000 --- a/fs/cifs/smb2inode.c +++ /dev/null @@ -1,799 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Pavel Shilovsky (pshilovsky@samba.org), - * Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fscache.h" -#include "smb2glob.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "cached_dir.h" -#include "smb2status.h" - -static void -free_set_inf_compound(struct smb_rqst *rqst) -{ - if (rqst[1].rq_iov) - SMB2_set_info_free(&rqst[1]); - if (rqst[2].rq_iov) - SMB2_close_free(&rqst[2]); -} - - -struct cop_vars { - struct cifs_open_parms oparms; - struct kvec rsp_iov[3]; - struct smb_rqst rqst[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; - struct kvec close_iov[1]; - struct smb2_file_rename_info rename_info; - struct smb2_file_link_info link_info; -}; - -/* - * note: If cfile is passed, the reference to it is dropped here. - * So make sure that you do not reuse cfile after return from this func. - * - * If passing @err_iov and @err_buftype, ensure to make them both large enough (>= 3) to hold all - * error responses. Caller is also responsible for freeing them up. - */ -static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - __u32 desired_access, __u32 create_disposition, __u32 create_options, - umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile, - struct kvec *err_iov, int *err_buftype) -{ - struct cop_vars *vars = NULL; - struct kvec *rsp_iov; - struct smb_rqst *rqst; - int rc; - __le16 *utf16_path = NULL; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_fid fid; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server; - int num_rqst = 0; - int resp_buftype[3]; - struct smb2_query_info_rsp *qi_rsp = NULL; - struct cifs_open_info_data *idata; - int flags = 0; - __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; - unsigned int size[2]; - void *data[2]; - int len; - - vars = kzalloc(sizeof(*vars), GFP_ATOMIC); - if (vars == NULL) - return -ENOMEM; - rqst = &vars->rqst[0]; - rsp_iov = &vars->rsp_iov[0]; - - server = cifs_pick_channel(ses); - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - - /* We already have a handle so we can skip the open */ - if (cfile) - goto after_open; - - /* Open */ - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - goto finished; - } - - vars->oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = desired_access, - .disposition = create_disposition, - .create_options = cifs_create_options(cifs_sb, create_options), - .fid = &fid, - .mode = mode, - .cifs_sb = cifs_sb, - }; - - rqst[num_rqst].rq_iov = &vars->open_iov[0]; - rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; - rc = SMB2_open_init(tcon, server, - &rqst[num_rqst], &oplock, &vars->oparms, - utf16_path); - kfree(utf16_path); - if (rc) - goto finished; - - smb2_set_next_command(tcon, &rqst[num_rqst]); - after_open: - num_rqst++; - rc = 0; - - /* Operation */ - switch (command) { - case SMB2_OP_QUERY_INFO: - rqst[num_rqst].rq_iov = &vars->qi_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - if (cfile) - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FILE_ALL_INFORMATION, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + - PATH_MAX * 2, 0, NULL); - else { - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, - FILE_ALL_INFORMATION, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + - PATH_MAX * 2, 0, NULL); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - - if (rc) - goto finished; - num_rqst++; - trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, - full_path); - break; - case SMB2_OP_POSIX_QUERY_INFO: - rqst[num_rqst].rq_iov = &vars->qi_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - if (cfile) - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - SMB_FIND_FILE_POSIX_INFO, - SMB2_O_INFO_FILE, 0, - /* TBD: fix following to allow for longer SIDs */ - sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + - (sizeof(struct cifs_sid) * 2), 0, NULL); - else { - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, - SMB_FIND_FILE_POSIX_INFO, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + - (sizeof(struct cifs_sid) * 2), 0, NULL); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - - if (rc) - goto finished; - num_rqst++; - trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_DELETE: - trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_MKDIR: - /* - * Directories are created through parameters in the - * SMB2_open() call. - */ - trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_RMDIR: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ - data[0] = &delete_pending[0]; - - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_DISPOSITION_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (rc) - goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst++]); - trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_SET_EOF: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - size[0] = 8; /* sizeof __le64 */ - data[0] = ptr; - - if (cfile) { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, - FILE_END_OF_FILE_INFORMATION, - SMB2_O_INFO_FILE, 0, - data, size); - } else { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, - current->tgid, - FILE_END_OF_FILE_INFORMATION, - SMB2_O_INFO_FILE, 0, - data, size); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - if (rc) - goto finished; - num_rqst++; - trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_SET_INFO: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - - size[0] = sizeof(FILE_BASIC_INFO); - data[0] = ptr; - - if (cfile) - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, current->tgid, - FILE_BASIC_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - else { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_BASIC_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - - if (rc) - goto finished; - num_rqst++; - trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, - full_path); - break; - case SMB2_OP_RENAME: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 2; - - len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); - - vars->rename_info.ReplaceIfExists = 1; - vars->rename_info.RootDirectory = 0; - vars->rename_info.FileNameLength = cpu_to_le32(len); - - size[0] = sizeof(struct smb2_file_rename_info); - data[0] = &vars->rename_info; - - size[1] = len + 2 /* null */; - data[1] = (__le16 *)ptr; - - if (cfile) - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, FILE_RENAME_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - else { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, COMPOUND_FID, - current->tgid, FILE_RENAME_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - if (rc) - goto finished; - num_rqst++; - trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_HARDLINK: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 2; - - len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); - - vars->link_info.ReplaceIfExists = 0; - vars->link_info.RootDirectory = 0; - vars->link_info.FileNameLength = cpu_to_le32(len); - - size[0] = sizeof(struct smb2_file_link_info); - data[0] = &vars->link_info; - - size[1] = len + 2 /* null */; - data[1] = (__le16 *)ptr; - - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_LINK_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (rc) - goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst++]); - trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); - break; - default: - cifs_dbg(VFS, "Invalid command\n"); - rc = -EINVAL; - } - if (rc) - goto finished; - - /* We already have a handle so we can skip the close */ - if (cfile) - goto after_close; - /* Close */ - flags |= CIFS_CP_CREATE_CLOSE_OP; - rqst[num_rqst].rq_iov = &vars->close_iov[0]; - rqst[num_rqst].rq_nvec = 1; - rc = SMB2_close_init(tcon, server, - &rqst[num_rqst], COMPOUND_FID, - COMPOUND_FID, false); - smb2_set_related(&rqst[num_rqst]); - if (rc) - goto finished; - after_close: - num_rqst++; - - if (cfile) { - rc = compound_send_recv(xid, ses, server, - flags, num_rqst - 2, - &rqst[1], &resp_buftype[1], - &rsp_iov[1]); - } else - rc = compound_send_recv(xid, ses, server, - flags, num_rqst, - rqst, resp_buftype, - rsp_iov); - - finished: - if (cfile) - cifsFileInfo_put(cfile); - - SMB2_open_free(&rqst[0]); - if (rc == -EREMCHG) { - pr_warn_once("server share %s deleted\n", tcon->tree_name); - tcon->need_reconnect = true; - } - - switch (command) { - case SMB2_OP_QUERY_INFO: - idata = ptr; - if (rc == 0 && cfile && cfile->symlink_target) { - idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!idata->symlink_target) - rc = -ENOMEM; - } - if (rc == 0) { - qi_rsp = (struct smb2_query_info_rsp *) - rsp_iov[1].iov_base; - rc = smb2_validate_and_copy_iov( - le16_to_cpu(qi_rsp->OutputBufferOffset), - le32_to_cpu(qi_rsp->OutputBufferLength), - &rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi); - } - if (rqst[1].rq_iov) - SMB2_query_info_free(&rqst[1]); - if (rqst[2].rq_iov) - SMB2_close_free(&rqst[2]); - if (rc) - trace_smb3_query_info_compound_err(xid, ses->Suid, - tcon->tid, rc); - else - trace_smb3_query_info_compound_done(xid, ses->Suid, - tcon->tid); - break; - case SMB2_OP_POSIX_QUERY_INFO: - idata = ptr; - if (rc == 0 && cfile && cfile->symlink_target) { - idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!idata->symlink_target) - rc = -ENOMEM; - } - if (rc == 0) { - qi_rsp = (struct smb2_query_info_rsp *) - rsp_iov[1].iov_base; - rc = smb2_validate_and_copy_iov( - le16_to_cpu(qi_rsp->OutputBufferOffset), - le32_to_cpu(qi_rsp->OutputBufferLength), - &rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */, - (char *)&idata->posix_fi); - } - if (rqst[1].rq_iov) - SMB2_query_info_free(&rqst[1]); - if (rqst[2].rq_iov) - SMB2_close_free(&rqst[2]); - if (rc) - trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid); - break; - case SMB2_OP_DELETE: - if (rc) - trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_delete_done(xid, ses->Suid, tcon->tid); - if (rqst[1].rq_iov) - SMB2_close_free(&rqst[1]); - break; - case SMB2_OP_MKDIR: - if (rc) - trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); - if (rqst[1].rq_iov) - SMB2_close_free(&rqst[1]); - break; - case SMB2_OP_HARDLINK: - if (rc) - trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_RENAME: - if (rc) - trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_rename_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_RMDIR: - if (rc) - trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_SET_EOF: - if (rc) - trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_SET_INFO: - if (rc) - trace_smb3_set_info_compound_err(xid, ses->Suid, - tcon->tid, rc); - else - trace_smb3_set_info_compound_done(xid, ses->Suid, - tcon->tid); - free_set_inf_compound(rqst); - break; - } - - if (rc && err_iov && err_buftype) { - memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov)); - memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype)); - } else { - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - } - kfree(vars); - return rc; -} - -int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) -{ - __u32 create_options = 0; - struct cifsFileInfo *cfile; - struct cached_fid *cfid = NULL; - struct kvec err_iov[3] = {}; - int err_buftype[3] = {}; - bool islink; - int rc, rc2; - - *adjust_tz = false; - *reparse = false; - - if (strcmp(full_path, "")) - rc = -ENOENT; - else - rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); - /* If it is a root and its handle is cached then use it */ - if (!rc) { - if (cfid->file_all_info_is_valid) { - memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi)); - } else { - rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid, &data->fi); - } - close_cached_dir(cfid); - return rc; - } - - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, - create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile, - err_iov, err_buftype); - if (rc) { - struct smb2_hdr *hdr = err_iov[0].iov_base; - - if (unlikely(!hdr || err_buftype[0] == CIFS_NO_BUFFER)) - goto out; - if (rc == -EOPNOTSUPP && hdr->Command == SMB2_CREATE && - hdr->Status == STATUS_STOPPED_ON_SYMLINK) { - rc = smb2_parse_symlink_response(cifs_sb, err_iov, - &data->symlink_target); - if (rc) - goto out; - - *reparse = true; - create_options |= OPEN_REPARSE_POINT; - - /* Failed on a symbolic link - query a reparse point info */ - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, - FILE_READ_ATTRIBUTES, FILE_OPEN, - create_options, ACL_NO_MODE, data, - SMB2_OP_QUERY_INFO, cfile, NULL, NULL); - goto out; - } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { - rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, - full_path, &islink); - if (rc2) { - rc = rc2; - goto out; - } - if (islink) - rc = -EREMOTE; - } - if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) - rc = -EOPNOTSUPP; - } - -out: - free_rsp_buf(err_buftype[0], err_iov[0].iov_base); - free_rsp_buf(err_buftype[1], err_iov[1].iov_base); - free_rsp_buf(err_buftype[2], err_iov[2].iov_base); - return rc; -} - - -int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) -{ - int rc; - __u32 create_options = 0; - struct cifsFileInfo *cfile; - struct kvec err_iov[3] = {}; - int err_buftype[3] = {}; - - *adjust_tz = false; - *reparse = false; - - /* - * BB TODO: Add support for using the cached root handle. - * Create SMB2_query_posix_info worker function to do non-compounded query - * when we already have an open file handle for this. For now this is fast enough - * (always using the compounded version). - */ - - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, - create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile, - err_iov, err_buftype); - if (rc == -EOPNOTSUPP) { - /* BB TODO: When support for special files added to Samba re-verify this path */ - if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER && - ((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE && - ((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) { - rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target); - if (rc) - goto out; - } - *reparse = true; - create_options |= OPEN_REPARSE_POINT; - - /* Failed on a symbolic link - query a reparse point info */ - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, - FILE_OPEN, create_options, ACL_NO_MODE, data, - SMB2_OP_POSIX_QUERY_INFO, cfile, NULL, NULL); - } - -out: - free_rsp_buf(err_buftype[0], err_iov[0].iov_base); - free_rsp_buf(err_buftype[1], err_iov[1].iov_base); - free_rsp_buf(err_buftype[2], err_iov[2].iov_base); - return rc; -} - -int -smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, - struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - return smb2_compound_op(xid, tcon, cifs_sb, name, - FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR, - NULL, NULL, NULL); -} - -void -smb2_mkdir_setinfo(struct inode *inode, const char *name, - struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, - const unsigned int xid) -{ - FILE_BASIC_INFO data; - struct cifsInodeInfo *cifs_i; - struct cifsFileInfo *cfile; - u32 dosattrs; - int tmprc; - - memset(&data, 0, sizeof(data)); - cifs_i = CIFS_I(inode); - dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; - data.Attributes = cpu_to_le32(dosattrs); - cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); - tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, - FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, ACL_NO_MODE, - &data, SMB2_OP_SET_INFO, cfile, NULL, NULL); - if (tmprc == 0) - cifs_i->cifsAttrs = dosattrs; -} - -int -smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - drop_cached_dir_by_name(xid, tcon, name, cifs_sb); - return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, - CREATE_NOT_FILE, ACL_NO_MODE, - NULL, SMB2_OP_RMDIR, NULL, NULL, NULL); -} - -int -smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, - CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, - ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL); -} - -static int -smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb, __u32 access, int command, - struct cifsFileInfo *cfile) -{ - __le16 *smb2_to_name = NULL; - int rc; - - smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb); - if (smb2_to_name == NULL) { - rc = -ENOMEM; - goto smb2_rename_path; - } - rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, - FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name, - command, cfile, NULL, NULL); -smb2_rename_path: - kfree(smb2_to_name); - return rc; -} - -int -smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - struct cifsFileInfo *cfile; - - drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb); - cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); - - return smb2_set_path_attr(xid, tcon, from_name, to_name, - cifs_sb, DELETE, SMB2_OP_RENAME, cfile); -} - -int -smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, - FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK, - NULL); -} - -int -smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, - const char *full_path, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_alloc) -{ - __le64 eof = cpu_to_le64(size); - struct cifsFileInfo *cfile; - - cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); - return smb2_compound_op(xid, tcon, cifs_sb, full_path, - FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, - &eof, SMB2_OP_SET_EOF, cfile, NULL, NULL); -} - -int -smb2_set_file_info(struct inode *inode, const char *full_path, - FILE_BASIC_INFO *buf, const unsigned int xid) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - int rc; - - if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && - (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) && - (buf->Attributes == 0)) - return 0; /* would be a no op, no sense sending this */ - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, - FILE_WRITE_ATTRIBUTES, FILE_OPEN, - 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile, - NULL, NULL); - cifs_put_tlink(tlink); - return rc; -} diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c deleted file mode 100644 index 194799ddd382..000000000000 --- a/fs/cifs/smb2maperror.c +++ /dev/null @@ -1,2481 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Functions which do error mapping of SMB2 status codes to POSIX errors - * - * Copyright (C) International Business Machines Corp., 2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include "cifsglob.h" -#include "cifs_debug.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "trace.h" - -struct status_to_posix_error { - __le32 smb2_status; - int posix_error; - char *status_string; -}; - -static const struct status_to_posix_error smb2_error_map_table[] = { - {STATUS_SUCCESS, 0, "STATUS_SUCCESS"}, - {STATUS_WAIT_0, 0, "STATUS_WAIT_0"}, - {STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"}, - {STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"}, - {STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"}, - {STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"}, - {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED"}, - {STATUS_ABANDONED_WAIT_0, -EIO, "STATUS_ABANDONED_WAIT_0"}, - {STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"}, - {STATUS_USER_APC, -EIO, "STATUS_USER_APC"}, - {STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"}, - {STATUS_ALERTED, -EIO, "STATUS_ALERTED"}, - {STATUS_TIMEOUT, -ETIMEDOUT, "STATUS_TIMEOUT"}, - {STATUS_PENDING, -EIO, "STATUS_PENDING"}, - {STATUS_REPARSE, -EIO, "STATUS_REPARSE"}, - {STATUS_MORE_ENTRIES, -EIO, "STATUS_MORE_ENTRIES"}, - {STATUS_NOT_ALL_ASSIGNED, -EIO, "STATUS_NOT_ALL_ASSIGNED"}, - {STATUS_SOME_NOT_MAPPED, -EIO, "STATUS_SOME_NOT_MAPPED"}, - {STATUS_OPLOCK_BREAK_IN_PROGRESS, -EIO, - "STATUS_OPLOCK_BREAK_IN_PROGRESS"}, - {STATUS_VOLUME_MOUNTED, -EIO, "STATUS_VOLUME_MOUNTED"}, - {STATUS_RXACT_COMMITTED, -EIO, "STATUS_RXACT_COMMITTED"}, - {STATUS_NOTIFY_CLEANUP, -EIO, "STATUS_NOTIFY_CLEANUP"}, - {STATUS_NOTIFY_ENUM_DIR, -EIO, "STATUS_NOTIFY_ENUM_DIR"}, - {STATUS_NO_QUOTAS_FOR_ACCOUNT, -EIO, "STATUS_NO_QUOTAS_FOR_ACCOUNT"}, - {STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED, -EIO, - "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED"}, - {STATUS_PAGE_FAULT_TRANSITION, -EIO, "STATUS_PAGE_FAULT_TRANSITION"}, - {STATUS_PAGE_FAULT_DEMAND_ZERO, -EIO, "STATUS_PAGE_FAULT_DEMAND_ZERO"}, - {STATUS_PAGE_FAULT_COPY_ON_WRITE, -EIO, - "STATUS_PAGE_FAULT_COPY_ON_WRITE"}, - {STATUS_PAGE_FAULT_GUARD_PAGE, -EIO, "STATUS_PAGE_FAULT_GUARD_PAGE"}, - {STATUS_PAGE_FAULT_PAGING_FILE, -EIO, "STATUS_PAGE_FAULT_PAGING_FILE"}, - {STATUS_CACHE_PAGE_LOCKED, -EIO, "STATUS_CACHE_PAGE_LOCKED"}, - {STATUS_CRASH_DUMP, -EIO, "STATUS_CRASH_DUMP"}, - {STATUS_BUFFER_ALL_ZEROS, -EIO, "STATUS_BUFFER_ALL_ZEROS"}, - {STATUS_REPARSE_OBJECT, -EIO, "STATUS_REPARSE_OBJECT"}, - {STATUS_RESOURCE_REQUIREMENTS_CHANGED, -EIO, - "STATUS_RESOURCE_REQUIREMENTS_CHANGED"}, - {STATUS_TRANSLATION_COMPLETE, -EIO, "STATUS_TRANSLATION_COMPLETE"}, - {STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, -EIO, - "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY"}, - {STATUS_NOTHING_TO_TERMINATE, -EIO, "STATUS_NOTHING_TO_TERMINATE"}, - {STATUS_PROCESS_NOT_IN_JOB, -EIO, "STATUS_PROCESS_NOT_IN_JOB"}, - {STATUS_PROCESS_IN_JOB, -EIO, "STATUS_PROCESS_IN_JOB"}, - {STATUS_VOLSNAP_HIBERNATE_READY, -EIO, - "STATUS_VOLSNAP_HIBERNATE_READY"}, - {STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, -EIO, - "STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY"}, - {STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, -EIO, - "STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED"}, - {STATUS_INTERRUPT_STILL_CONNECTED, -EIO, - "STATUS_INTERRUPT_STILL_CONNECTED"}, - {STATUS_PROCESS_CLONED, -EIO, "STATUS_PROCESS_CLONED"}, - {STATUS_FILE_LOCKED_WITH_ONLY_READERS, -EIO, - "STATUS_FILE_LOCKED_WITH_ONLY_READERS"}, - {STATUS_FILE_LOCKED_WITH_WRITERS, -EIO, - "STATUS_FILE_LOCKED_WITH_WRITERS"}, - {STATUS_RESOURCEMANAGER_READ_ONLY, -EROFS, - "STATUS_RESOURCEMANAGER_READ_ONLY"}, - {STATUS_WAIT_FOR_OPLOCK, -EIO, "STATUS_WAIT_FOR_OPLOCK"}, - {DBG_EXCEPTION_HANDLED, -EIO, "DBG_EXCEPTION_HANDLED"}, - {DBG_CONTINUE, -EIO, "DBG_CONTINUE"}, - {STATUS_FLT_IO_COMPLETE, -EIO, "STATUS_FLT_IO_COMPLETE"}, - {STATUS_OBJECT_NAME_EXISTS, -EIO, "STATUS_OBJECT_NAME_EXISTS"}, - {STATUS_THREAD_WAS_SUSPENDED, -EIO, "STATUS_THREAD_WAS_SUSPENDED"}, - {STATUS_WORKING_SET_LIMIT_RANGE, -EIO, - "STATUS_WORKING_SET_LIMIT_RANGE"}, - {STATUS_IMAGE_NOT_AT_BASE, -EIO, "STATUS_IMAGE_NOT_AT_BASE"}, - {STATUS_RXACT_STATE_CREATED, -EIO, "STATUS_RXACT_STATE_CREATED"}, - {STATUS_SEGMENT_NOTIFICATION, -EIO, "STATUS_SEGMENT_NOTIFICATION"}, - {STATUS_LOCAL_USER_SESSION_KEY, -EIO, "STATUS_LOCAL_USER_SESSION_KEY"}, - {STATUS_BAD_CURRENT_DIRECTORY, -EIO, "STATUS_BAD_CURRENT_DIRECTORY"}, - {STATUS_SERIAL_MORE_WRITES, -EIO, "STATUS_SERIAL_MORE_WRITES"}, - {STATUS_REGISTRY_RECOVERED, -EIO, "STATUS_REGISTRY_RECOVERED"}, - {STATUS_FT_READ_RECOVERY_FROM_BACKUP, -EIO, - "STATUS_FT_READ_RECOVERY_FROM_BACKUP"}, - {STATUS_FT_WRITE_RECOVERY, -EIO, "STATUS_FT_WRITE_RECOVERY"}, - {STATUS_SERIAL_COUNTER_TIMEOUT, -ETIMEDOUT, - "STATUS_SERIAL_COUNTER_TIMEOUT"}, - {STATUS_NULL_LM_PASSWORD, -EIO, "STATUS_NULL_LM_PASSWORD"}, - {STATUS_IMAGE_MACHINE_TYPE_MISMATCH, -EIO, - "STATUS_IMAGE_MACHINE_TYPE_MISMATCH"}, - {STATUS_RECEIVE_PARTIAL, -EIO, "STATUS_RECEIVE_PARTIAL"}, - {STATUS_RECEIVE_EXPEDITED, -EIO, "STATUS_RECEIVE_EXPEDITED"}, - {STATUS_RECEIVE_PARTIAL_EXPEDITED, -EIO, - "STATUS_RECEIVE_PARTIAL_EXPEDITED"}, - {STATUS_EVENT_DONE, -EIO, "STATUS_EVENT_DONE"}, - {STATUS_EVENT_PENDING, -EIO, "STATUS_EVENT_PENDING"}, - {STATUS_CHECKING_FILE_SYSTEM, -EIO, "STATUS_CHECKING_FILE_SYSTEM"}, - {STATUS_FATAL_APP_EXIT, -EIO, "STATUS_FATAL_APP_EXIT"}, - {STATUS_PREDEFINED_HANDLE, -EIO, "STATUS_PREDEFINED_HANDLE"}, - {STATUS_WAS_UNLOCKED, -EIO, "STATUS_WAS_UNLOCKED"}, - {STATUS_SERVICE_NOTIFICATION, -EIO, "STATUS_SERVICE_NOTIFICATION"}, - {STATUS_WAS_LOCKED, -EIO, "STATUS_WAS_LOCKED"}, - {STATUS_LOG_HARD_ERROR, -EIO, "STATUS_LOG_HARD_ERROR"}, - {STATUS_ALREADY_WIN32, -EIO, "STATUS_ALREADY_WIN32"}, - {STATUS_WX86_UNSIMULATE, -EIO, "STATUS_WX86_UNSIMULATE"}, - {STATUS_WX86_CONTINUE, -EIO, "STATUS_WX86_CONTINUE"}, - {STATUS_WX86_SINGLE_STEP, -EIO, "STATUS_WX86_SINGLE_STEP"}, - {STATUS_WX86_BREAKPOINT, -EIO, "STATUS_WX86_BREAKPOINT"}, - {STATUS_WX86_EXCEPTION_CONTINUE, -EIO, - "STATUS_WX86_EXCEPTION_CONTINUE"}, - {STATUS_WX86_EXCEPTION_LASTCHANCE, -EIO, - "STATUS_WX86_EXCEPTION_LASTCHANCE"}, - {STATUS_WX86_EXCEPTION_CHAIN, -EIO, "STATUS_WX86_EXCEPTION_CHAIN"}, - {STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, -EIO, - "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE"}, - {STATUS_NO_YIELD_PERFORMED, -EIO, "STATUS_NO_YIELD_PERFORMED"}, - {STATUS_TIMER_RESUME_IGNORED, -EIO, "STATUS_TIMER_RESUME_IGNORED"}, - {STATUS_ARBITRATION_UNHANDLED, -EIO, "STATUS_ARBITRATION_UNHANDLED"}, - {STATUS_CARDBUS_NOT_SUPPORTED, -ENOSYS, "STATUS_CARDBUS_NOT_SUPPORTED"}, - {STATUS_WX86_CREATEWX86TIB, -EIO, "STATUS_WX86_CREATEWX86TIB"}, - {STATUS_MP_PROCESSOR_MISMATCH, -EIO, "STATUS_MP_PROCESSOR_MISMATCH"}, - {STATUS_HIBERNATED, -EIO, "STATUS_HIBERNATED"}, - {STATUS_RESUME_HIBERNATION, -EIO, "STATUS_RESUME_HIBERNATION"}, - {STATUS_FIRMWARE_UPDATED, -EIO, "STATUS_FIRMWARE_UPDATED"}, - {STATUS_DRIVERS_LEAKING_LOCKED_PAGES, -EIO, - "STATUS_DRIVERS_LEAKING_LOCKED_PAGES"}, - {STATUS_MESSAGE_RETRIEVED, -EIO, "STATUS_MESSAGE_RETRIEVED"}, - {STATUS_SYSTEM_POWERSTATE_TRANSITION, -EIO, - "STATUS_SYSTEM_POWERSTATE_TRANSITION"}, - {STATUS_ALPC_CHECK_COMPLETION_LIST, -EIO, - "STATUS_ALPC_CHECK_COMPLETION_LIST"}, - {STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, -EIO, - "STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION"}, - {STATUS_ACCESS_AUDIT_BY_POLICY, -EIO, "STATUS_ACCESS_AUDIT_BY_POLICY"}, - {STATUS_ABANDON_HIBERFILE, -EIO, "STATUS_ABANDON_HIBERFILE"}, - {STATUS_BIZRULES_NOT_ENABLED, -EIO, "STATUS_BIZRULES_NOT_ENABLED"}, - {STATUS_WAKE_SYSTEM, -EIO, "STATUS_WAKE_SYSTEM"}, - {STATUS_DS_SHUTTING_DOWN, -EIO, "STATUS_DS_SHUTTING_DOWN"}, - {DBG_REPLY_LATER, -EIO, "DBG_REPLY_LATER"}, - {DBG_UNABLE_TO_PROVIDE_HANDLE, -EIO, "DBG_UNABLE_TO_PROVIDE_HANDLE"}, - {DBG_TERMINATE_THREAD, -EIO, "DBG_TERMINATE_THREAD"}, - {DBG_TERMINATE_PROCESS, -EIO, "DBG_TERMINATE_PROCESS"}, - {DBG_CONTROL_C, -EIO, "DBG_CONTROL_C"}, - {DBG_PRINTEXCEPTION_C, -EIO, "DBG_PRINTEXCEPTION_C"}, - {DBG_RIPEXCEPTION, -EIO, "DBG_RIPEXCEPTION"}, - {DBG_CONTROL_BREAK, -EIO, "DBG_CONTROL_BREAK"}, - {DBG_COMMAND_EXCEPTION, -EIO, "DBG_COMMAND_EXCEPTION"}, - {RPC_NT_UUID_LOCAL_ONLY, -EIO, "RPC_NT_UUID_LOCAL_ONLY"}, - {RPC_NT_SEND_INCOMPLETE, -EIO, "RPC_NT_SEND_INCOMPLETE"}, - {STATUS_CTX_CDM_CONNECT, -EIO, "STATUS_CTX_CDM_CONNECT"}, - {STATUS_CTX_CDM_DISCONNECT, -EIO, "STATUS_CTX_CDM_DISCONNECT"}, - {STATUS_SXS_RELEASE_ACTIVATION_CONTEXT, -EIO, - "STATUS_SXS_RELEASE_ACTIVATION_CONTEXT"}, - {STATUS_RECOVERY_NOT_NEEDED, -EIO, "STATUS_RECOVERY_NOT_NEEDED"}, - {STATUS_RM_ALREADY_STARTED, -EIO, "STATUS_RM_ALREADY_STARTED"}, - {STATUS_LOG_NO_RESTART, -EIO, "STATUS_LOG_NO_RESTART"}, - {STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST, -EIO, - "STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST"}, - {STATUS_GRAPHICS_PARTIAL_DATA_POPULATED, -EIO, - "STATUS_GRAPHICS_PARTIAL_DATA_POPULATED"}, - {STATUS_GRAPHICS_DRIVER_MISMATCH, -EIO, - "STATUS_GRAPHICS_DRIVER_MISMATCH"}, - {STATUS_GRAPHICS_MODE_NOT_PINNED, -EIO, - "STATUS_GRAPHICS_MODE_NOT_PINNED"}, - {STATUS_GRAPHICS_NO_PREFERRED_MODE, -EIO, - "STATUS_GRAPHICS_NO_PREFERRED_MODE"}, - {STATUS_GRAPHICS_DATASET_IS_EMPTY, -EIO, - "STATUS_GRAPHICS_DATASET_IS_EMPTY"}, - {STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET, -EIO, - "STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET"}, - {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED, -EIO, - "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED"}, - {STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS, -EIO, - "STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS"}, - {STATUS_GRAPHICS_LEADLINK_START_DEFERRED, -EIO, - "STATUS_GRAPHICS_LEADLINK_START_DEFERRED"}, - {STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY, -EIO, - "STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY"}, - {STATUS_GRAPHICS_START_DEFERRED, -EIO, - "STATUS_GRAPHICS_START_DEFERRED"}, - {STATUS_NDIS_INDICATION_REQUIRED, -EIO, - "STATUS_NDIS_INDICATION_REQUIRED"}, - {STATUS_GUARD_PAGE_VIOLATION, -EIO, "STATUS_GUARD_PAGE_VIOLATION"}, - {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, - {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, - {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, - {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"}, - {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, - {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, - {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, - {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, - {STATUS_GUID_SUBSTITUTION_MADE, -EIO, "STATUS_GUID_SUBSTITUTION_MADE"}, - {STATUS_PARTIAL_COPY, -EIO, "STATUS_PARTIAL_COPY"}, - {STATUS_DEVICE_PAPER_EMPTY, -EIO, "STATUS_DEVICE_PAPER_EMPTY"}, - {STATUS_DEVICE_POWERED_OFF, -EIO, "STATUS_DEVICE_POWERED_OFF"}, - {STATUS_DEVICE_OFF_LINE, -EIO, "STATUS_DEVICE_OFF_LINE"}, - {STATUS_DEVICE_BUSY, -EBUSY, "STATUS_DEVICE_BUSY"}, - {STATUS_NO_MORE_EAS, -EIO, "STATUS_NO_MORE_EAS"}, - {STATUS_INVALID_EA_NAME, -EINVAL, "STATUS_INVALID_EA_NAME"}, - {STATUS_EA_LIST_INCONSISTENT, -EIO, "STATUS_EA_LIST_INCONSISTENT"}, - {STATUS_INVALID_EA_FLAG, -EINVAL, "STATUS_INVALID_EA_FLAG"}, - {STATUS_VERIFY_REQUIRED, -EIO, "STATUS_VERIFY_REQUIRED"}, - {STATUS_EXTRANEOUS_INFORMATION, -EIO, "STATUS_EXTRANEOUS_INFORMATION"}, - {STATUS_RXACT_COMMIT_NECESSARY, -EIO, "STATUS_RXACT_COMMIT_NECESSARY"}, - {STATUS_NO_MORE_ENTRIES, -EIO, "STATUS_NO_MORE_ENTRIES"}, - {STATUS_FILEMARK_DETECTED, -EIO, "STATUS_FILEMARK_DETECTED"}, - {STATUS_MEDIA_CHANGED, -EIO, "STATUS_MEDIA_CHANGED"}, - {STATUS_BUS_RESET, -EIO, "STATUS_BUS_RESET"}, - {STATUS_END_OF_MEDIA, -EIO, "STATUS_END_OF_MEDIA"}, - {STATUS_BEGINNING_OF_MEDIA, -EIO, "STATUS_BEGINNING_OF_MEDIA"}, - {STATUS_MEDIA_CHECK, -EIO, "STATUS_MEDIA_CHECK"}, - {STATUS_SETMARK_DETECTED, -EIO, "STATUS_SETMARK_DETECTED"}, - {STATUS_NO_DATA_DETECTED, -EIO, "STATUS_NO_DATA_DETECTED"}, - {STATUS_REDIRECTOR_HAS_OPEN_HANDLES, -EIO, - "STATUS_REDIRECTOR_HAS_OPEN_HANDLES"}, - {STATUS_SERVER_HAS_OPEN_HANDLES, -EIO, - "STATUS_SERVER_HAS_OPEN_HANDLES"}, - {STATUS_ALREADY_DISCONNECTED, -EIO, "STATUS_ALREADY_DISCONNECTED"}, - {STATUS_LONGJUMP, -EIO, "STATUS_LONGJUMP"}, - {STATUS_CLEANER_CARTRIDGE_INSTALLED, -EIO, - "STATUS_CLEANER_CARTRIDGE_INSTALLED"}, - {STATUS_PLUGPLAY_QUERY_VETOED, -EIO, "STATUS_PLUGPLAY_QUERY_VETOED"}, - {STATUS_UNWIND_CONSOLIDATE, -EIO, "STATUS_UNWIND_CONSOLIDATE"}, - {STATUS_REGISTRY_HIVE_RECOVERED, -EIO, - "STATUS_REGISTRY_HIVE_RECOVERED"}, - {STATUS_DLL_MIGHT_BE_INSECURE, -EIO, "STATUS_DLL_MIGHT_BE_INSECURE"}, - {STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO, - "STATUS_DLL_MIGHT_BE_INCOMPATIBLE"}, - {STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"}, - {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EOPNOTSUPP, - "STATUS_REPARSE_NOT_HANDLED"}, - {STATUS_DEVICE_REQUIRES_CLEANING, -EIO, - "STATUS_DEVICE_REQUIRES_CLEANING"}, - {STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"}, - {STATUS_DATA_LOST_REPAIR, -EIO, "STATUS_DATA_LOST_REPAIR"}, - {DBG_EXCEPTION_NOT_HANDLED, -EIO, "DBG_EXCEPTION_NOT_HANDLED"}, - {STATUS_CLUSTER_NODE_ALREADY_UP, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_UP"}, - {STATUS_CLUSTER_NODE_ALREADY_DOWN, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_DOWN"}, - {STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, -EIO, - "STATUS_CLUSTER_NETWORK_ALREADY_ONLINE"}, - {STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, -EIO, - "STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE"}, - {STATUS_CLUSTER_NODE_ALREADY_MEMBER, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_MEMBER"}, - {STATUS_COULD_NOT_RESIZE_LOG, -EIO, "STATUS_COULD_NOT_RESIZE_LOG"}, - {STATUS_NO_TXF_METADATA, -EIO, "STATUS_NO_TXF_METADATA"}, - {STATUS_CANT_RECOVER_WITH_HANDLE_OPEN, -EIO, - "STATUS_CANT_RECOVER_WITH_HANDLE_OPEN"}, - {STATUS_TXF_METADATA_ALREADY_PRESENT, -EIO, - "STATUS_TXF_METADATA_ALREADY_PRESENT"}, - {STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET, -EIO, - "STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET"}, - {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED, -EIO, - "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED"}, - {STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"}, - {STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"}, - {STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"}, - {STATUS_NOT_IMPLEMENTED, -EOPNOTSUPP, "STATUS_NOT_IMPLEMENTED"}, - {STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"}, - {STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"}, - {STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"}, - {STATUS_IN_PAGE_ERROR, -EFAULT, "STATUS_IN_PAGE_ERROR"}, - {STATUS_PAGEFILE_QUOTA, -EDQUOT, "STATUS_PAGEFILE_QUOTA"}, - {STATUS_INVALID_HANDLE, -EBADF, "STATUS_INVALID_HANDLE"}, - {STATUS_BAD_INITIAL_STACK, -EIO, "STATUS_BAD_INITIAL_STACK"}, - {STATUS_BAD_INITIAL_PC, -EIO, "STATUS_BAD_INITIAL_PC"}, - {STATUS_INVALID_CID, -EIO, "STATUS_INVALID_CID"}, - {STATUS_TIMER_NOT_CANCELED, -EIO, "STATUS_TIMER_NOT_CANCELED"}, - {STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"}, - {STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"}, - {STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"}, - {STATUS_INVALID_DEVICE_REQUEST, -EOPNOTSUPP, "STATUS_INVALID_DEVICE_REQUEST"}, - {STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"}, - {STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"}, - {STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"}, - {STATUS_UNRECOGNIZED_MEDIA, -EIO, "STATUS_UNRECOGNIZED_MEDIA"}, - {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, - {STATUS_MORE_PROCESSING_REQUIRED, -EIO, - "STATUS_MORE_PROCESSING_REQUIRED"}, - {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"}, - {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, - "STATUS_CONFLICTING_ADDRESSES"}, - {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, - {STATUS_UNABLE_TO_FREE_VM, -EIO, "STATUS_UNABLE_TO_FREE_VM"}, - {STATUS_UNABLE_TO_DELETE_SECTION, -EIO, - "STATUS_UNABLE_TO_DELETE_SECTION"}, - {STATUS_INVALID_SYSTEM_SERVICE, -EIO, "STATUS_INVALID_SYSTEM_SERVICE"}, - {STATUS_ILLEGAL_INSTRUCTION, -EIO, "STATUS_ILLEGAL_INSTRUCTION"}, - {STATUS_INVALID_LOCK_SEQUENCE, -EIO, "STATUS_INVALID_LOCK_SEQUENCE"}, - {STATUS_INVALID_VIEW_SIZE, -EIO, "STATUS_INVALID_VIEW_SIZE"}, - {STATUS_INVALID_FILE_FOR_SECTION, -EIO, - "STATUS_INVALID_FILE_FOR_SECTION"}, - {STATUS_ALREADY_COMMITTED, -EIO, "STATUS_ALREADY_COMMITTED"}, - {STATUS_ACCESS_DENIED, -EACCES, "STATUS_ACCESS_DENIED"}, - {STATUS_BUFFER_TOO_SMALL, -EIO, "STATUS_BUFFER_TOO_SMALL"}, - {STATUS_OBJECT_TYPE_MISMATCH, -EIO, "STATUS_OBJECT_TYPE_MISMATCH"}, - {STATUS_NONCONTINUABLE_EXCEPTION, -EIO, - "STATUS_NONCONTINUABLE_EXCEPTION"}, - {STATUS_INVALID_DISPOSITION, -EIO, "STATUS_INVALID_DISPOSITION"}, - {STATUS_UNWIND, -EIO, "STATUS_UNWIND"}, - {STATUS_BAD_STACK, -EIO, "STATUS_BAD_STACK"}, - {STATUS_INVALID_UNWIND_TARGET, -EIO, "STATUS_INVALID_UNWIND_TARGET"}, - {STATUS_NOT_LOCKED, -EIO, "STATUS_NOT_LOCKED"}, - {STATUS_PARITY_ERROR, -EIO, "STATUS_PARITY_ERROR"}, - {STATUS_UNABLE_TO_DECOMMIT_VM, -EIO, "STATUS_UNABLE_TO_DECOMMIT_VM"}, - {STATUS_NOT_COMMITTED, -EIO, "STATUS_NOT_COMMITTED"}, - {STATUS_INVALID_PORT_ATTRIBUTES, -EIO, - "STATUS_INVALID_PORT_ATTRIBUTES"}, - {STATUS_PORT_MESSAGE_TOO_LONG, -EIO, "STATUS_PORT_MESSAGE_TOO_LONG"}, - {STATUS_INVALID_PARAMETER_MIX, -EINVAL, "STATUS_INVALID_PARAMETER_MIX"}, - {STATUS_INVALID_QUOTA_LOWER, -EIO, "STATUS_INVALID_QUOTA_LOWER"}, - {STATUS_DISK_CORRUPT_ERROR, -EIO, "STATUS_DISK_CORRUPT_ERROR"}, - {STATUS_OBJECT_NAME_INVALID, -ENOENT, "STATUS_OBJECT_NAME_INVALID"}, - {STATUS_OBJECT_NAME_NOT_FOUND, -ENOENT, "STATUS_OBJECT_NAME_NOT_FOUND"}, - {STATUS_OBJECT_NAME_COLLISION, -EEXIST, "STATUS_OBJECT_NAME_COLLISION"}, - {STATUS_PORT_DISCONNECTED, -EIO, "STATUS_PORT_DISCONNECTED"}, - {STATUS_DEVICE_ALREADY_ATTACHED, -EIO, - "STATUS_DEVICE_ALREADY_ATTACHED"}, - {STATUS_OBJECT_PATH_INVALID, -ENOTDIR, "STATUS_OBJECT_PATH_INVALID"}, - {STATUS_OBJECT_PATH_NOT_FOUND, -ENOENT, "STATUS_OBJECT_PATH_NOT_FOUND"}, - {STATUS_OBJECT_PATH_SYNTAX_BAD, -EIO, "STATUS_OBJECT_PATH_SYNTAX_BAD"}, - {STATUS_DATA_OVERRUN, -EIO, "STATUS_DATA_OVERRUN"}, - {STATUS_DATA_LATE_ERROR, -EIO, "STATUS_DATA_LATE_ERROR"}, - {STATUS_DATA_ERROR, -EIO, "STATUS_DATA_ERROR"}, - {STATUS_CRC_ERROR, -EIO, "STATUS_CRC_ERROR"}, - {STATUS_SECTION_TOO_BIG, -EIO, "STATUS_SECTION_TOO_BIG"}, - {STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED, - "STATUS_PORT_CONNECTION_REFUSED"}, - {STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"}, - {STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"}, - {STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"}, - {STATUS_INVALID_PAGE_PROTECTION, -EIO, - "STATUS_INVALID_PAGE_PROTECTION"}, - {STATUS_MUTANT_NOT_OWNED, -EIO, "STATUS_MUTANT_NOT_OWNED"}, - {STATUS_SEMAPHORE_LIMIT_EXCEEDED, -EIO, - "STATUS_SEMAPHORE_LIMIT_EXCEEDED"}, - {STATUS_PORT_ALREADY_SET, -EIO, "STATUS_PORT_ALREADY_SET"}, - {STATUS_SECTION_NOT_IMAGE, -EIO, "STATUS_SECTION_NOT_IMAGE"}, - {STATUS_SUSPEND_COUNT_EXCEEDED, -EIO, "STATUS_SUSPEND_COUNT_EXCEEDED"}, - {STATUS_THREAD_IS_TERMINATING, -EIO, "STATUS_THREAD_IS_TERMINATING"}, - {STATUS_BAD_WORKING_SET_LIMIT, -EIO, "STATUS_BAD_WORKING_SET_LIMIT"}, - {STATUS_INCOMPATIBLE_FILE_MAP, -EIO, "STATUS_INCOMPATIBLE_FILE_MAP"}, - {STATUS_SECTION_PROTECTION, -EIO, "STATUS_SECTION_PROTECTION"}, - {STATUS_EAS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_EAS_NOT_SUPPORTED"}, - {STATUS_EA_TOO_LARGE, -EIO, "STATUS_EA_TOO_LARGE"}, - {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, - {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, - {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, - {STATUS_FILE_LOCK_CONFLICT, -EACCES, "STATUS_FILE_LOCK_CONFLICT"}, - {STATUS_LOCK_NOT_GRANTED, -EACCES, "STATUS_LOCK_NOT_GRANTED"}, - {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, - {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, - "STATUS_CTL_FILE_NOT_SUPPORTED"}, - {STATUS_UNKNOWN_REVISION, -EIO, "STATUS_UNKNOWN_REVISION"}, - {STATUS_REVISION_MISMATCH, -EIO, "STATUS_REVISION_MISMATCH"}, - {STATUS_INVALID_OWNER, -EIO, "STATUS_INVALID_OWNER"}, - {STATUS_INVALID_PRIMARY_GROUP, -EIO, "STATUS_INVALID_PRIMARY_GROUP"}, - {STATUS_NO_IMPERSONATION_TOKEN, -EIO, "STATUS_NO_IMPERSONATION_TOKEN"}, - {STATUS_CANT_DISABLE_MANDATORY, -EIO, "STATUS_CANT_DISABLE_MANDATORY"}, - {STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"}, - {STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"}, - {STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"}, - {STATUS_PRIVILEGE_NOT_HELD, -EIO, "STATUS_PRIVILEGE_NOT_HELD"}, - {STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"}, - {STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"}, - {STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"}, - {STATUS_GROUP_EXISTS, -EIO, "STATUS_GROUP_EXISTS"}, - {STATUS_NO_SUCH_GROUP, -EIO, "STATUS_NO_SUCH_GROUP"}, - {STATUS_MEMBER_IN_GROUP, -EIO, "STATUS_MEMBER_IN_GROUP"}, - {STATUS_MEMBER_NOT_IN_GROUP, -EIO, "STATUS_MEMBER_NOT_IN_GROUP"}, - {STATUS_LAST_ADMIN, -EIO, "STATUS_LAST_ADMIN"}, - {STATUS_WRONG_PASSWORD, -EACCES, "STATUS_WRONG_PASSWORD"}, - {STATUS_ILL_FORMED_PASSWORD, -EINVAL, "STATUS_ILL_FORMED_PASSWORD"}, - {STATUS_PASSWORD_RESTRICTION, -EACCES, "STATUS_PASSWORD_RESTRICTION"}, - {STATUS_LOGON_FAILURE, -EACCES, "STATUS_LOGON_FAILURE"}, - {STATUS_ACCOUNT_RESTRICTION, -EACCES, "STATUS_ACCOUNT_RESTRICTION"}, - {STATUS_INVALID_LOGON_HOURS, -EACCES, "STATUS_INVALID_LOGON_HOURS"}, - {STATUS_INVALID_WORKSTATION, -EACCES, "STATUS_INVALID_WORKSTATION"}, - {STATUS_PASSWORD_EXPIRED, -EKEYEXPIRED, "STATUS_PASSWORD_EXPIRED"}, - {STATUS_ACCOUNT_DISABLED, -EKEYREVOKED, "STATUS_ACCOUNT_DISABLED"}, - {STATUS_NONE_MAPPED, -EIO, "STATUS_NONE_MAPPED"}, - {STATUS_TOO_MANY_LUIDS_REQUESTED, -EIO, - "STATUS_TOO_MANY_LUIDS_REQUESTED"}, - {STATUS_LUIDS_EXHAUSTED, -EIO, "STATUS_LUIDS_EXHAUSTED"}, - {STATUS_INVALID_SUB_AUTHORITY, -EIO, "STATUS_INVALID_SUB_AUTHORITY"}, - {STATUS_INVALID_ACL, -EIO, "STATUS_INVALID_ACL"}, - {STATUS_INVALID_SID, -EIO, "STATUS_INVALID_SID"}, - {STATUS_INVALID_SECURITY_DESCR, -EIO, "STATUS_INVALID_SECURITY_DESCR"}, - {STATUS_PROCEDURE_NOT_FOUND, -EIO, "STATUS_PROCEDURE_NOT_FOUND"}, - {STATUS_INVALID_IMAGE_FORMAT, -EIO, "STATUS_INVALID_IMAGE_FORMAT"}, - {STATUS_NO_TOKEN, -EIO, "STATUS_NO_TOKEN"}, - {STATUS_BAD_INHERITANCE_ACL, -EIO, "STATUS_BAD_INHERITANCE_ACL"}, - {STATUS_RANGE_NOT_LOCKED, -EIO, "STATUS_RANGE_NOT_LOCKED"}, - {STATUS_DISK_FULL, -ENOSPC, "STATUS_DISK_FULL"}, - {STATUS_SERVER_DISABLED, -EIO, "STATUS_SERVER_DISABLED"}, - {STATUS_SERVER_NOT_DISABLED, -EIO, "STATUS_SERVER_NOT_DISABLED"}, - {STATUS_TOO_MANY_GUIDS_REQUESTED, -EIO, - "STATUS_TOO_MANY_GUIDS_REQUESTED"}, - {STATUS_GUIDS_EXHAUSTED, -EIO, "STATUS_GUIDS_EXHAUSTED"}, - {STATUS_INVALID_ID_AUTHORITY, -EIO, "STATUS_INVALID_ID_AUTHORITY"}, - {STATUS_AGENTS_EXHAUSTED, -EIO, "STATUS_AGENTS_EXHAUSTED"}, - {STATUS_INVALID_VOLUME_LABEL, -EIO, "STATUS_INVALID_VOLUME_LABEL"}, - {STATUS_SECTION_NOT_EXTENDED, -EIO, "STATUS_SECTION_NOT_EXTENDED"}, - {STATUS_NOT_MAPPED_DATA, -EIO, "STATUS_NOT_MAPPED_DATA"}, - {STATUS_RESOURCE_DATA_NOT_FOUND, -EIO, - "STATUS_RESOURCE_DATA_NOT_FOUND"}, - {STATUS_RESOURCE_TYPE_NOT_FOUND, -EIO, - "STATUS_RESOURCE_TYPE_NOT_FOUND"}, - {STATUS_RESOURCE_NAME_NOT_FOUND, -EIO, - "STATUS_RESOURCE_NAME_NOT_FOUND"}, - {STATUS_ARRAY_BOUNDS_EXCEEDED, -EIO, "STATUS_ARRAY_BOUNDS_EXCEEDED"}, - {STATUS_FLOAT_DENORMAL_OPERAND, -EIO, "STATUS_FLOAT_DENORMAL_OPERAND"}, - {STATUS_FLOAT_DIVIDE_BY_ZERO, -EIO, "STATUS_FLOAT_DIVIDE_BY_ZERO"}, - {STATUS_FLOAT_INEXACT_RESULT, -EIO, "STATUS_FLOAT_INEXACT_RESULT"}, - {STATUS_FLOAT_INVALID_OPERATION, -EIO, - "STATUS_FLOAT_INVALID_OPERATION"}, - {STATUS_FLOAT_OVERFLOW, -EIO, "STATUS_FLOAT_OVERFLOW"}, - {STATUS_FLOAT_STACK_CHECK, -EIO, "STATUS_FLOAT_STACK_CHECK"}, - {STATUS_FLOAT_UNDERFLOW, -EIO, "STATUS_FLOAT_UNDERFLOW"}, - {STATUS_INTEGER_DIVIDE_BY_ZERO, -EIO, "STATUS_INTEGER_DIVIDE_BY_ZERO"}, - {STATUS_INTEGER_OVERFLOW, -EIO, "STATUS_INTEGER_OVERFLOW"}, - {STATUS_PRIVILEGED_INSTRUCTION, -EIO, "STATUS_PRIVILEGED_INSTRUCTION"}, - {STATUS_TOO_MANY_PAGING_FILES, -EIO, "STATUS_TOO_MANY_PAGING_FILES"}, - {STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"}, - {STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO, - "STATUS_ALLOTTED_SPACE_EXCEEDED"}, - {STATUS_INSUFFICIENT_RESOURCES, -EAGAIN, - "STATUS_INSUFFICIENT_RESOURCES"}, - {STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"}, - {STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"}, - {STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"}, - {STATUS_DEVICE_POWER_FAILURE, -EIO, "STATUS_DEVICE_POWER_FAILURE"}, - {STATUS_FREE_VM_NOT_AT_BASE, -EIO, "STATUS_FREE_VM_NOT_AT_BASE"}, - {STATUS_MEMORY_NOT_ALLOCATED, -EFAULT, "STATUS_MEMORY_NOT_ALLOCATED"}, - {STATUS_WORKING_SET_QUOTA, -EIO, "STATUS_WORKING_SET_QUOTA"}, - {STATUS_MEDIA_WRITE_PROTECTED, -EROFS, "STATUS_MEDIA_WRITE_PROTECTED"}, - {STATUS_DEVICE_NOT_READY, -EIO, "STATUS_DEVICE_NOT_READY"}, - {STATUS_INVALID_GROUP_ATTRIBUTES, -EIO, - "STATUS_INVALID_GROUP_ATTRIBUTES"}, - {STATUS_BAD_IMPERSONATION_LEVEL, -EIO, - "STATUS_BAD_IMPERSONATION_LEVEL"}, - {STATUS_CANT_OPEN_ANONYMOUS, -EIO, "STATUS_CANT_OPEN_ANONYMOUS"}, - {STATUS_BAD_VALIDATION_CLASS, -EIO, "STATUS_BAD_VALIDATION_CLASS"}, - {STATUS_BAD_TOKEN_TYPE, -EIO, "STATUS_BAD_TOKEN_TYPE"}, - {STATUS_BAD_MASTER_BOOT_RECORD, -EIO, "STATUS_BAD_MASTER_BOOT_RECORD"}, - {STATUS_INSTRUCTION_MISALIGNMENT, -EIO, - "STATUS_INSTRUCTION_MISALIGNMENT"}, - {STATUS_INSTANCE_NOT_AVAILABLE, -EIO, "STATUS_INSTANCE_NOT_AVAILABLE"}, - {STATUS_PIPE_NOT_AVAILABLE, -EIO, "STATUS_PIPE_NOT_AVAILABLE"}, - {STATUS_INVALID_PIPE_STATE, -EIO, "STATUS_INVALID_PIPE_STATE"}, - {STATUS_PIPE_BUSY, -EBUSY, "STATUS_PIPE_BUSY"}, - {STATUS_ILLEGAL_FUNCTION, -EIO, "STATUS_ILLEGAL_FUNCTION"}, - {STATUS_PIPE_DISCONNECTED, -EPIPE, "STATUS_PIPE_DISCONNECTED"}, - {STATUS_PIPE_CLOSING, -EIO, "STATUS_PIPE_CLOSING"}, - {STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"}, - {STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"}, - {STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"}, - {STATUS_IO_TIMEOUT, -EAGAIN, "STATUS_IO_TIMEOUT"}, - {STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"}, - {STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"}, - {STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"}, - {STATUS_COULD_NOT_INTERPRET, -EIO, "STATUS_COULD_NOT_INTERPRET"}, - {STATUS_FILE_IS_A_DIRECTORY, -EISDIR, "STATUS_FILE_IS_A_DIRECTORY"}, - {STATUS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_NOT_SUPPORTED"}, - {STATUS_REMOTE_NOT_LISTENING, -EHOSTDOWN, - "STATUS_REMOTE_NOT_LISTENING"}, - {STATUS_DUPLICATE_NAME, -ENOTUNIQ, "STATUS_DUPLICATE_NAME"}, - {STATUS_BAD_NETWORK_PATH, -EINVAL, "STATUS_BAD_NETWORK_PATH"}, - {STATUS_NETWORK_BUSY, -EBUSY, "STATUS_NETWORK_BUSY"}, - {STATUS_DEVICE_DOES_NOT_EXIST, -ENODEV, "STATUS_DEVICE_DOES_NOT_EXIST"}, - {STATUS_TOO_MANY_COMMANDS, -EIO, "STATUS_TOO_MANY_COMMANDS"}, - {STATUS_ADAPTER_HARDWARE_ERROR, -EIO, "STATUS_ADAPTER_HARDWARE_ERROR"}, - {STATUS_INVALID_NETWORK_RESPONSE, -EIO, - "STATUS_INVALID_NETWORK_RESPONSE"}, - {STATUS_UNEXPECTED_NETWORK_ERROR, -EIO, - "STATUS_UNEXPECTED_NETWORK_ERROR"}, - {STATUS_BAD_REMOTE_ADAPTER, -EIO, "STATUS_BAD_REMOTE_ADAPTER"}, - {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, - {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, - {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, - {STATUS_NETWORK_NAME_DELETED, -EREMCHG, "STATUS_NETWORK_NAME_DELETED"}, - {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, - {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, - {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, - {STATUS_TOO_MANY_NAMES, -EIO, "STATUS_TOO_MANY_NAMES"}, - {STATUS_TOO_MANY_SESSIONS, -EIO, "STATUS_TOO_MANY_SESSIONS"}, - {STATUS_SHARING_PAUSED, -EIO, "STATUS_SHARING_PAUSED"}, - {STATUS_REQUEST_NOT_ACCEPTED, -EIO, "STATUS_REQUEST_NOT_ACCEPTED"}, - {STATUS_REDIRECTOR_PAUSED, -EIO, "STATUS_REDIRECTOR_PAUSED"}, - {STATUS_NET_WRITE_FAULT, -EIO, "STATUS_NET_WRITE_FAULT"}, - {STATUS_PROFILING_AT_LIMIT, -EIO, "STATUS_PROFILING_AT_LIMIT"}, - {STATUS_NOT_SAME_DEVICE, -EXDEV, "STATUS_NOT_SAME_DEVICE"}, - {STATUS_FILE_RENAMED, -EIO, "STATUS_FILE_RENAMED"}, - {STATUS_VIRTUAL_CIRCUIT_CLOSED, -EIO, "STATUS_VIRTUAL_CIRCUIT_CLOSED"}, - {STATUS_NO_SECURITY_ON_OBJECT, -EIO, "STATUS_NO_SECURITY_ON_OBJECT"}, - {STATUS_CANT_WAIT, -EIO, "STATUS_CANT_WAIT"}, - {STATUS_PIPE_EMPTY, -EIO, "STATUS_PIPE_EMPTY"}, - {STATUS_CANT_ACCESS_DOMAIN_INFO, -EIO, - "STATUS_CANT_ACCESS_DOMAIN_INFO"}, - {STATUS_CANT_TERMINATE_SELF, -EIO, "STATUS_CANT_TERMINATE_SELF"}, - {STATUS_INVALID_SERVER_STATE, -EIO, "STATUS_INVALID_SERVER_STATE"}, - {STATUS_INVALID_DOMAIN_STATE, -EIO, "STATUS_INVALID_DOMAIN_STATE"}, - {STATUS_INVALID_DOMAIN_ROLE, -EIO, "STATUS_INVALID_DOMAIN_ROLE"}, - {STATUS_NO_SUCH_DOMAIN, -EIO, "STATUS_NO_SUCH_DOMAIN"}, - {STATUS_DOMAIN_EXISTS, -EIO, "STATUS_DOMAIN_EXISTS"}, - {STATUS_DOMAIN_LIMIT_EXCEEDED, -EIO, "STATUS_DOMAIN_LIMIT_EXCEEDED"}, - {STATUS_OPLOCK_NOT_GRANTED, -EIO, "STATUS_OPLOCK_NOT_GRANTED"}, - {STATUS_INVALID_OPLOCK_PROTOCOL, -EIO, - "STATUS_INVALID_OPLOCK_PROTOCOL"}, - {STATUS_INTERNAL_DB_CORRUPTION, -EIO, "STATUS_INTERNAL_DB_CORRUPTION"}, - {STATUS_INTERNAL_ERROR, -EIO, "STATUS_INTERNAL_ERROR"}, - {STATUS_GENERIC_NOT_MAPPED, -EIO, "STATUS_GENERIC_NOT_MAPPED"}, - {STATUS_BAD_DESCRIPTOR_FORMAT, -EIO, "STATUS_BAD_DESCRIPTOR_FORMAT"}, - {STATUS_INVALID_USER_BUFFER, -EIO, "STATUS_INVALID_USER_BUFFER"}, - {STATUS_UNEXPECTED_IO_ERROR, -EIO, "STATUS_UNEXPECTED_IO_ERROR"}, - {STATUS_UNEXPECTED_MM_CREATE_ERR, -EIO, - "STATUS_UNEXPECTED_MM_CREATE_ERR"}, - {STATUS_UNEXPECTED_MM_MAP_ERROR, -EIO, - "STATUS_UNEXPECTED_MM_MAP_ERROR"}, - {STATUS_UNEXPECTED_MM_EXTEND_ERR, -EIO, - "STATUS_UNEXPECTED_MM_EXTEND_ERR"}, - {STATUS_NOT_LOGON_PROCESS, -EIO, "STATUS_NOT_LOGON_PROCESS"}, - {STATUS_LOGON_SESSION_EXISTS, -EIO, "STATUS_LOGON_SESSION_EXISTS"}, - {STATUS_INVALID_PARAMETER_1, -EINVAL, "STATUS_INVALID_PARAMETER_1"}, - {STATUS_INVALID_PARAMETER_2, -EINVAL, "STATUS_INVALID_PARAMETER_2"}, - {STATUS_INVALID_PARAMETER_3, -EINVAL, "STATUS_INVALID_PARAMETER_3"}, - {STATUS_INVALID_PARAMETER_4, -EINVAL, "STATUS_INVALID_PARAMETER_4"}, - {STATUS_INVALID_PARAMETER_5, -EINVAL, "STATUS_INVALID_PARAMETER_5"}, - {STATUS_INVALID_PARAMETER_6, -EINVAL, "STATUS_INVALID_PARAMETER_6"}, - {STATUS_INVALID_PARAMETER_7, -EINVAL, "STATUS_INVALID_PARAMETER_7"}, - {STATUS_INVALID_PARAMETER_8, -EINVAL, "STATUS_INVALID_PARAMETER_8"}, - {STATUS_INVALID_PARAMETER_9, -EINVAL, "STATUS_INVALID_PARAMETER_9"}, - {STATUS_INVALID_PARAMETER_10, -EINVAL, "STATUS_INVALID_PARAMETER_10"}, - {STATUS_INVALID_PARAMETER_11, -EINVAL, "STATUS_INVALID_PARAMETER_11"}, - {STATUS_INVALID_PARAMETER_12, -EINVAL, "STATUS_INVALID_PARAMETER_12"}, - {STATUS_REDIRECTOR_NOT_STARTED, -EIO, "STATUS_REDIRECTOR_NOT_STARTED"}, - {STATUS_REDIRECTOR_STARTED, -EIO, "STATUS_REDIRECTOR_STARTED"}, - {STATUS_STACK_OVERFLOW, -EIO, "STATUS_STACK_OVERFLOW"}, - {STATUS_NO_SUCH_PACKAGE, -EIO, "STATUS_NO_SUCH_PACKAGE"}, - {STATUS_BAD_FUNCTION_TABLE, -EIO, "STATUS_BAD_FUNCTION_TABLE"}, - {STATUS_VARIABLE_NOT_FOUND, -EIO, "STATUS_VARIABLE_NOT_FOUND"}, - {STATUS_DIRECTORY_NOT_EMPTY, -ENOTEMPTY, "STATUS_DIRECTORY_NOT_EMPTY"}, - {STATUS_FILE_CORRUPT_ERROR, -EIO, "STATUS_FILE_CORRUPT_ERROR"}, - {STATUS_NOT_A_DIRECTORY, -ENOTDIR, "STATUS_NOT_A_DIRECTORY"}, - {STATUS_BAD_LOGON_SESSION_STATE, -EIO, - "STATUS_BAD_LOGON_SESSION_STATE"}, - {STATUS_LOGON_SESSION_COLLISION, -EIO, - "STATUS_LOGON_SESSION_COLLISION"}, - {STATUS_NAME_TOO_LONG, -ENAMETOOLONG, "STATUS_NAME_TOO_LONG"}, - {STATUS_FILES_OPEN, -EIO, "STATUS_FILES_OPEN"}, - {STATUS_CONNECTION_IN_USE, -EIO, "STATUS_CONNECTION_IN_USE"}, - {STATUS_MESSAGE_NOT_FOUND, -EIO, "STATUS_MESSAGE_NOT_FOUND"}, - {STATUS_PROCESS_IS_TERMINATING, -EIO, "STATUS_PROCESS_IS_TERMINATING"}, - {STATUS_INVALID_LOGON_TYPE, -EIO, "STATUS_INVALID_LOGON_TYPE"}, - {STATUS_NO_GUID_TRANSLATION, -EIO, "STATUS_NO_GUID_TRANSLATION"}, - {STATUS_CANNOT_IMPERSONATE, -EIO, "STATUS_CANNOT_IMPERSONATE"}, - {STATUS_IMAGE_ALREADY_LOADED, -EIO, "STATUS_IMAGE_ALREADY_LOADED"}, - {STATUS_ABIOS_NOT_PRESENT, -EIO, "STATUS_ABIOS_NOT_PRESENT"}, - {STATUS_ABIOS_LID_NOT_EXIST, -EIO, "STATUS_ABIOS_LID_NOT_EXIST"}, - {STATUS_ABIOS_LID_ALREADY_OWNED, -EIO, - "STATUS_ABIOS_LID_ALREADY_OWNED"}, - {STATUS_ABIOS_NOT_LID_OWNER, -EIO, "STATUS_ABIOS_NOT_LID_OWNER"}, - {STATUS_ABIOS_INVALID_COMMAND, -EIO, "STATUS_ABIOS_INVALID_COMMAND"}, - {STATUS_ABIOS_INVALID_LID, -EIO, "STATUS_ABIOS_INVALID_LID"}, - {STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, -EIO, - "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE"}, - {STATUS_ABIOS_INVALID_SELECTOR, -EIO, "STATUS_ABIOS_INVALID_SELECTOR"}, - {STATUS_NO_LDT, -EIO, "STATUS_NO_LDT"}, - {STATUS_INVALID_LDT_SIZE, -EIO, "STATUS_INVALID_LDT_SIZE"}, - {STATUS_INVALID_LDT_OFFSET, -EIO, "STATUS_INVALID_LDT_OFFSET"}, - {STATUS_INVALID_LDT_DESCRIPTOR, -EIO, "STATUS_INVALID_LDT_DESCRIPTOR"}, - {STATUS_INVALID_IMAGE_NE_FORMAT, -EIO, - "STATUS_INVALID_IMAGE_NE_FORMAT"}, - {STATUS_RXACT_INVALID_STATE, -EIO, "STATUS_RXACT_INVALID_STATE"}, - {STATUS_RXACT_COMMIT_FAILURE, -EIO, "STATUS_RXACT_COMMIT_FAILURE"}, - {STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"}, - {STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"}, - {STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"}, - {STATUS_CANNOT_DELETE, -EACCES, "STATUS_CANNOT_DELETE"}, - {STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"}, - {STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"}, - {STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"}, - {STATUS_SPECIAL_GROUP, -EIO, "STATUS_SPECIAL_GROUP"}, - {STATUS_SPECIAL_USER, -EIO, "STATUS_SPECIAL_USER"}, - {STATUS_MEMBERS_PRIMARY_GROUP, -EIO, "STATUS_MEMBERS_PRIMARY_GROUP"}, - {STATUS_FILE_CLOSED, -EBADF, "STATUS_FILE_CLOSED"}, - {STATUS_TOO_MANY_THREADS, -EIO, "STATUS_TOO_MANY_THREADS"}, - {STATUS_THREAD_NOT_IN_PROCESS, -EIO, "STATUS_THREAD_NOT_IN_PROCESS"}, - {STATUS_TOKEN_ALREADY_IN_USE, -EIO, "STATUS_TOKEN_ALREADY_IN_USE"}, - {STATUS_PAGEFILE_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PAGEFILE_QUOTA_EXCEEDED"}, - {STATUS_COMMITMENT_LIMIT, -EIO, "STATUS_COMMITMENT_LIMIT"}, - {STATUS_INVALID_IMAGE_LE_FORMAT, -EIO, - "STATUS_INVALID_IMAGE_LE_FORMAT"}, - {STATUS_INVALID_IMAGE_NOT_MZ, -EIO, "STATUS_INVALID_IMAGE_NOT_MZ"}, - {STATUS_INVALID_IMAGE_PROTECT, -EIO, "STATUS_INVALID_IMAGE_PROTECT"}, - {STATUS_INVALID_IMAGE_WIN_16, -EIO, "STATUS_INVALID_IMAGE_WIN_16"}, - {STATUS_LOGON_SERVER_CONFLICT, -EIO, "STATUS_LOGON_SERVER_CONFLICT"}, - {STATUS_TIME_DIFFERENCE_AT_DC, -EIO, "STATUS_TIME_DIFFERENCE_AT_DC"}, - {STATUS_SYNCHRONIZATION_REQUIRED, -EIO, - "STATUS_SYNCHRONIZATION_REQUIRED"}, - {STATUS_DLL_NOT_FOUND, -ENOENT, "STATUS_DLL_NOT_FOUND"}, - {STATUS_OPEN_FAILED, -EIO, "STATUS_OPEN_FAILED"}, - {STATUS_IO_PRIVILEGE_FAILED, -EIO, "STATUS_IO_PRIVILEGE_FAILED"}, - {STATUS_ORDINAL_NOT_FOUND, -EIO, "STATUS_ORDINAL_NOT_FOUND"}, - {STATUS_ENTRYPOINT_NOT_FOUND, -EIO, "STATUS_ENTRYPOINT_NOT_FOUND"}, - {STATUS_CONTROL_C_EXIT, -EIO, "STATUS_CONTROL_C_EXIT"}, - {STATUS_LOCAL_DISCONNECT, -EIO, "STATUS_LOCAL_DISCONNECT"}, - {STATUS_REMOTE_DISCONNECT, -ESHUTDOWN, "STATUS_REMOTE_DISCONNECT"}, - {STATUS_REMOTE_RESOURCES, -EIO, "STATUS_REMOTE_RESOURCES"}, - {STATUS_LINK_FAILED, -EXDEV, "STATUS_LINK_FAILED"}, - {STATUS_LINK_TIMEOUT, -ETIMEDOUT, "STATUS_LINK_TIMEOUT"}, - {STATUS_INVALID_CONNECTION, -EIO, "STATUS_INVALID_CONNECTION"}, - {STATUS_INVALID_ADDRESS, -EIO, "STATUS_INVALID_ADDRESS"}, - {STATUS_DLL_INIT_FAILED, -EIO, "STATUS_DLL_INIT_FAILED"}, - {STATUS_MISSING_SYSTEMFILE, -EIO, "STATUS_MISSING_SYSTEMFILE"}, - {STATUS_UNHANDLED_EXCEPTION, -EIO, "STATUS_UNHANDLED_EXCEPTION"}, - {STATUS_APP_INIT_FAILURE, -EIO, "STATUS_APP_INIT_FAILURE"}, - {STATUS_PAGEFILE_CREATE_FAILED, -EIO, "STATUS_PAGEFILE_CREATE_FAILED"}, - {STATUS_NO_PAGEFILE, -EIO, "STATUS_NO_PAGEFILE"}, - {STATUS_INVALID_LEVEL, -EIO, "STATUS_INVALID_LEVEL"}, - {STATUS_WRONG_PASSWORD_CORE, -EIO, "STATUS_WRONG_PASSWORD_CORE"}, - {STATUS_ILLEGAL_FLOAT_CONTEXT, -EIO, "STATUS_ILLEGAL_FLOAT_CONTEXT"}, - {STATUS_PIPE_BROKEN, -EPIPE, "STATUS_PIPE_BROKEN"}, - {STATUS_REGISTRY_CORRUPT, -EIO, "STATUS_REGISTRY_CORRUPT"}, - {STATUS_REGISTRY_IO_FAILED, -EIO, "STATUS_REGISTRY_IO_FAILED"}, - {STATUS_NO_EVENT_PAIR, -EIO, "STATUS_NO_EVENT_PAIR"}, - {STATUS_UNRECOGNIZED_VOLUME, -EIO, "STATUS_UNRECOGNIZED_VOLUME"}, - {STATUS_SERIAL_NO_DEVICE_INITED, -EIO, - "STATUS_SERIAL_NO_DEVICE_INITED"}, - {STATUS_NO_SUCH_ALIAS, -EIO, "STATUS_NO_SUCH_ALIAS"}, - {STATUS_MEMBER_NOT_IN_ALIAS, -EIO, "STATUS_MEMBER_NOT_IN_ALIAS"}, - {STATUS_MEMBER_IN_ALIAS, -EIO, "STATUS_MEMBER_IN_ALIAS"}, - {STATUS_ALIAS_EXISTS, -EIO, "STATUS_ALIAS_EXISTS"}, - {STATUS_LOGON_NOT_GRANTED, -EIO, "STATUS_LOGON_NOT_GRANTED"}, - {STATUS_TOO_MANY_SECRETS, -EIO, "STATUS_TOO_MANY_SECRETS"}, - {STATUS_SECRET_TOO_LONG, -EIO, "STATUS_SECRET_TOO_LONG"}, - {STATUS_INTERNAL_DB_ERROR, -EIO, "STATUS_INTERNAL_DB_ERROR"}, - {STATUS_FULLSCREEN_MODE, -EIO, "STATUS_FULLSCREEN_MODE"}, - {STATUS_TOO_MANY_CONTEXT_IDS, -EIO, "STATUS_TOO_MANY_CONTEXT_IDS"}, - {STATUS_LOGON_TYPE_NOT_GRANTED, -EIO, "STATUS_LOGON_TYPE_NOT_GRANTED"}, - {STATUS_NOT_REGISTRY_FILE, -EIO, "STATUS_NOT_REGISTRY_FILE"}, - {STATUS_NT_CROSS_ENCRYPTION_REQUIRED, -EIO, - "STATUS_NT_CROSS_ENCRYPTION_REQUIRED"}, - {STATUS_DOMAIN_CTRLR_CONFIG_ERROR, -EIO, - "STATUS_DOMAIN_CTRLR_CONFIG_ERROR"}, - {STATUS_FT_MISSING_MEMBER, -EIO, "STATUS_FT_MISSING_MEMBER"}, - {STATUS_ILL_FORMED_SERVICE_ENTRY, -EIO, - "STATUS_ILL_FORMED_SERVICE_ENTRY"}, - {STATUS_ILLEGAL_CHARACTER, -EIO, "STATUS_ILLEGAL_CHARACTER"}, - {STATUS_UNMAPPABLE_CHARACTER, -EIO, "STATUS_UNMAPPABLE_CHARACTER"}, - {STATUS_UNDEFINED_CHARACTER, -EIO, "STATUS_UNDEFINED_CHARACTER"}, - {STATUS_FLOPPY_VOLUME, -EIO, "STATUS_FLOPPY_VOLUME"}, - {STATUS_FLOPPY_ID_MARK_NOT_FOUND, -EIO, - "STATUS_FLOPPY_ID_MARK_NOT_FOUND"}, - {STATUS_FLOPPY_WRONG_CYLINDER, -EIO, "STATUS_FLOPPY_WRONG_CYLINDER"}, - {STATUS_FLOPPY_UNKNOWN_ERROR, -EIO, "STATUS_FLOPPY_UNKNOWN_ERROR"}, - {STATUS_FLOPPY_BAD_REGISTERS, -EIO, "STATUS_FLOPPY_BAD_REGISTERS"}, - {STATUS_DISK_RECALIBRATE_FAILED, -EIO, - "STATUS_DISK_RECALIBRATE_FAILED"}, - {STATUS_DISK_OPERATION_FAILED, -EIO, "STATUS_DISK_OPERATION_FAILED"}, - {STATUS_DISK_RESET_FAILED, -EIO, "STATUS_DISK_RESET_FAILED"}, - {STATUS_SHARED_IRQ_BUSY, -EBUSY, "STATUS_SHARED_IRQ_BUSY"}, - {STATUS_FT_ORPHANING, -EIO, "STATUS_FT_ORPHANING"}, - {STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT, -EIO, - "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT"}, - {STATUS_PARTITION_FAILURE, -EIO, "STATUS_PARTITION_FAILURE"}, - {STATUS_INVALID_BLOCK_LENGTH, -EIO, "STATUS_INVALID_BLOCK_LENGTH"}, - {STATUS_DEVICE_NOT_PARTITIONED, -EIO, "STATUS_DEVICE_NOT_PARTITIONED"}, - {STATUS_UNABLE_TO_LOCK_MEDIA, -EIO, "STATUS_UNABLE_TO_LOCK_MEDIA"}, - {STATUS_UNABLE_TO_UNLOAD_MEDIA, -EIO, "STATUS_UNABLE_TO_UNLOAD_MEDIA"}, - {STATUS_EOM_OVERFLOW, -EIO, "STATUS_EOM_OVERFLOW"}, - {STATUS_NO_MEDIA, -EIO, "STATUS_NO_MEDIA"}, - {STATUS_NO_SUCH_MEMBER, -EIO, "STATUS_NO_SUCH_MEMBER"}, - {STATUS_INVALID_MEMBER, -EIO, "STATUS_INVALID_MEMBER"}, - {STATUS_KEY_DELETED, -EIO, "STATUS_KEY_DELETED"}, - {STATUS_NO_LOG_SPACE, -EIO, "STATUS_NO_LOG_SPACE"}, - {STATUS_TOO_MANY_SIDS, -EIO, "STATUS_TOO_MANY_SIDS"}, - {STATUS_LM_CROSS_ENCRYPTION_REQUIRED, -EIO, - "STATUS_LM_CROSS_ENCRYPTION_REQUIRED"}, - {STATUS_KEY_HAS_CHILDREN, -EIO, "STATUS_KEY_HAS_CHILDREN"}, - {STATUS_CHILD_MUST_BE_VOLATILE, -EIO, "STATUS_CHILD_MUST_BE_VOLATILE"}, - {STATUS_DEVICE_CONFIGURATION_ERROR, -EIO, - "STATUS_DEVICE_CONFIGURATION_ERROR"}, - {STATUS_DRIVER_INTERNAL_ERROR, -EIO, "STATUS_DRIVER_INTERNAL_ERROR"}, - {STATUS_INVALID_DEVICE_STATE, -EIO, "STATUS_INVALID_DEVICE_STATE"}, - {STATUS_IO_DEVICE_ERROR, -EIO, "STATUS_IO_DEVICE_ERROR"}, - {STATUS_DEVICE_PROTOCOL_ERROR, -EIO, "STATUS_DEVICE_PROTOCOL_ERROR"}, - {STATUS_BACKUP_CONTROLLER, -EIO, "STATUS_BACKUP_CONTROLLER"}, - {STATUS_LOG_FILE_FULL, -EIO, "STATUS_LOG_FILE_FULL"}, - {STATUS_TOO_LATE, -EIO, "STATUS_TOO_LATE"}, - {STATUS_NO_TRUST_LSA_SECRET, -EIO, "STATUS_NO_TRUST_LSA_SECRET"}, - {STATUS_NO_TRUST_SAM_ACCOUNT, -EIO, "STATUS_NO_TRUST_SAM_ACCOUNT"}, - {STATUS_TRUSTED_DOMAIN_FAILURE, -EIO, "STATUS_TRUSTED_DOMAIN_FAILURE"}, - {STATUS_TRUSTED_RELATIONSHIP_FAILURE, -EIO, - "STATUS_TRUSTED_RELATIONSHIP_FAILURE"}, - {STATUS_EVENTLOG_FILE_CORRUPT, -EIO, "STATUS_EVENTLOG_FILE_CORRUPT"}, - {STATUS_EVENTLOG_CANT_START, -EIO, "STATUS_EVENTLOG_CANT_START"}, - {STATUS_TRUST_FAILURE, -EIO, "STATUS_TRUST_FAILURE"}, - {STATUS_MUTANT_LIMIT_EXCEEDED, -EIO, "STATUS_MUTANT_LIMIT_EXCEEDED"}, - {STATUS_NETLOGON_NOT_STARTED, -EIO, "STATUS_NETLOGON_NOT_STARTED"}, - {STATUS_ACCOUNT_EXPIRED, -EKEYEXPIRED, "STATUS_ACCOUNT_EXPIRED"}, - {STATUS_POSSIBLE_DEADLOCK, -EIO, "STATUS_POSSIBLE_DEADLOCK"}, - {STATUS_NETWORK_CREDENTIAL_CONFLICT, -EIO, - "STATUS_NETWORK_CREDENTIAL_CONFLICT"}, - {STATUS_REMOTE_SESSION_LIMIT, -EIO, "STATUS_REMOTE_SESSION_LIMIT"}, - {STATUS_EVENTLOG_FILE_CHANGED, -EIO, "STATUS_EVENTLOG_FILE_CHANGED"}, - {STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"}, - {STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"}, - {STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"}, - {STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO, - "STATUS_DOMAIN_TRUST_INCONSISTENT"}, - {STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"}, - {STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO, - "STATUS_IMAGE_ALREADY_LOADED_AS_DLL"}, - {STATUS_NETWORK_OPEN_RESTRICTION, -EIO, - "STATUS_NETWORK_OPEN_RESTRICTION"}, - {STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"}, - {STATUS_USER_SESSION_DELETED, -EIO, "STATUS_USER_SESSION_DELETED"}, - {STATUS_RESOURCE_LANG_NOT_FOUND, -EIO, - "STATUS_RESOURCE_LANG_NOT_FOUND"}, - {STATUS_INSUFF_SERVER_RESOURCES, -EIO, - "STATUS_INSUFF_SERVER_RESOURCES"}, - {STATUS_INVALID_BUFFER_SIZE, -EIO, "STATUS_INVALID_BUFFER_SIZE"}, - {STATUS_INVALID_ADDRESS_COMPONENT, -EIO, - "STATUS_INVALID_ADDRESS_COMPONENT"}, - {STATUS_INVALID_ADDRESS_WILDCARD, -EIO, - "STATUS_INVALID_ADDRESS_WILDCARD"}, - {STATUS_TOO_MANY_ADDRESSES, -EIO, "STATUS_TOO_MANY_ADDRESSES"}, - {STATUS_ADDRESS_ALREADY_EXISTS, -EADDRINUSE, - "STATUS_ADDRESS_ALREADY_EXISTS"}, - {STATUS_ADDRESS_CLOSED, -EIO, "STATUS_ADDRESS_CLOSED"}, - {STATUS_CONNECTION_DISCONNECTED, -ECONNABORTED, - "STATUS_CONNECTION_DISCONNECTED"}, - {STATUS_CONNECTION_RESET, -ENETRESET, "STATUS_CONNECTION_RESET"}, - {STATUS_TOO_MANY_NODES, -EIO, "STATUS_TOO_MANY_NODES"}, - {STATUS_TRANSACTION_ABORTED, -EIO, "STATUS_TRANSACTION_ABORTED"}, - {STATUS_TRANSACTION_TIMED_OUT, -EIO, "STATUS_TRANSACTION_TIMED_OUT"}, - {STATUS_TRANSACTION_NO_RELEASE, -EIO, "STATUS_TRANSACTION_NO_RELEASE"}, - {STATUS_TRANSACTION_NO_MATCH, -EIO, "STATUS_TRANSACTION_NO_MATCH"}, - {STATUS_TRANSACTION_RESPONDED, -EIO, "STATUS_TRANSACTION_RESPONDED"}, - {STATUS_TRANSACTION_INVALID_ID, -EIO, "STATUS_TRANSACTION_INVALID_ID"}, - {STATUS_TRANSACTION_INVALID_TYPE, -EIO, - "STATUS_TRANSACTION_INVALID_TYPE"}, - {STATUS_NOT_SERVER_SESSION, -EIO, "STATUS_NOT_SERVER_SESSION"}, - {STATUS_NOT_CLIENT_SESSION, -EIO, "STATUS_NOT_CLIENT_SESSION"}, - {STATUS_CANNOT_LOAD_REGISTRY_FILE, -EIO, - "STATUS_CANNOT_LOAD_REGISTRY_FILE"}, - {STATUS_DEBUG_ATTACH_FAILED, -EIO, "STATUS_DEBUG_ATTACH_FAILED"}, - {STATUS_SYSTEM_PROCESS_TERMINATED, -EIO, - "STATUS_SYSTEM_PROCESS_TERMINATED"}, - {STATUS_DATA_NOT_ACCEPTED, -EIO, "STATUS_DATA_NOT_ACCEPTED"}, - {STATUS_NO_BROWSER_SERVERS_FOUND, -EIO, - "STATUS_NO_BROWSER_SERVERS_FOUND"}, - {STATUS_VDM_HARD_ERROR, -EIO, "STATUS_VDM_HARD_ERROR"}, - {STATUS_DRIVER_CANCEL_TIMEOUT, -EIO, "STATUS_DRIVER_CANCEL_TIMEOUT"}, - {STATUS_REPLY_MESSAGE_MISMATCH, -EIO, "STATUS_REPLY_MESSAGE_MISMATCH"}, - {STATUS_MAPPED_ALIGNMENT, -EIO, "STATUS_MAPPED_ALIGNMENT"}, - {STATUS_IMAGE_CHECKSUM_MISMATCH, -EIO, - "STATUS_IMAGE_CHECKSUM_MISMATCH"}, - {STATUS_LOST_WRITEBEHIND_DATA, -EIO, "STATUS_LOST_WRITEBEHIND_DATA"}, - {STATUS_CLIENT_SERVER_PARAMETERS_INVALID, -EIO, - "STATUS_CLIENT_SERVER_PARAMETERS_INVALID"}, - {STATUS_PASSWORD_MUST_CHANGE, -EIO, "STATUS_PASSWORD_MUST_CHANGE"}, - {STATUS_NOT_FOUND, -ENOENT, "STATUS_NOT_FOUND"}, - {STATUS_NOT_TINY_STREAM, -EIO, "STATUS_NOT_TINY_STREAM"}, - {STATUS_RECOVERY_FAILURE, -EIO, "STATUS_RECOVERY_FAILURE"}, - {STATUS_STACK_OVERFLOW_READ, -EIO, "STATUS_STACK_OVERFLOW_READ"}, - {STATUS_FAIL_CHECK, -EIO, "STATUS_FAIL_CHECK"}, - {STATUS_DUPLICATE_OBJECTID, -EIO, "STATUS_DUPLICATE_OBJECTID"}, - {STATUS_OBJECTID_EXISTS, -EIO, "STATUS_OBJECTID_EXISTS"}, - {STATUS_CONVERT_TO_LARGE, -EIO, "STATUS_CONVERT_TO_LARGE"}, - {STATUS_RETRY, -EAGAIN, "STATUS_RETRY"}, - {STATUS_FOUND_OUT_OF_SCOPE, -EIO, "STATUS_FOUND_OUT_OF_SCOPE"}, - {STATUS_ALLOCATE_BUCKET, -EIO, "STATUS_ALLOCATE_BUCKET"}, - {STATUS_PROPSET_NOT_FOUND, -EIO, "STATUS_PROPSET_NOT_FOUND"}, - {STATUS_MARSHALL_OVERFLOW, -EIO, "STATUS_MARSHALL_OVERFLOW"}, - {STATUS_INVALID_VARIANT, -EIO, "STATUS_INVALID_VARIANT"}, - {STATUS_DOMAIN_CONTROLLER_NOT_FOUND, -EIO, - "STATUS_DOMAIN_CONTROLLER_NOT_FOUND"}, - {STATUS_ACCOUNT_LOCKED_OUT, -EACCES, "STATUS_ACCOUNT_LOCKED_OUT"}, - {STATUS_HANDLE_NOT_CLOSABLE, -EIO, "STATUS_HANDLE_NOT_CLOSABLE"}, - {STATUS_CONNECTION_REFUSED, -EIO, "STATUS_CONNECTION_REFUSED"}, - {STATUS_GRACEFUL_DISCONNECT, -EIO, "STATUS_GRACEFUL_DISCONNECT"}, - {STATUS_ADDRESS_ALREADY_ASSOCIATED, -EIO, - "STATUS_ADDRESS_ALREADY_ASSOCIATED"}, - {STATUS_ADDRESS_NOT_ASSOCIATED, -EIO, "STATUS_ADDRESS_NOT_ASSOCIATED"}, - {STATUS_CONNECTION_INVALID, -EIO, "STATUS_CONNECTION_INVALID"}, - {STATUS_CONNECTION_ACTIVE, -EIO, "STATUS_CONNECTION_ACTIVE"}, - {STATUS_NETWORK_UNREACHABLE, -ENETUNREACH, - "STATUS_NETWORK_UNREACHABLE"}, - {STATUS_HOST_UNREACHABLE, -EHOSTDOWN, "STATUS_HOST_UNREACHABLE"}, - {STATUS_PROTOCOL_UNREACHABLE, -ENETUNREACH, - "STATUS_PROTOCOL_UNREACHABLE"}, - {STATUS_PORT_UNREACHABLE, -ENETUNREACH, "STATUS_PORT_UNREACHABLE"}, - {STATUS_REQUEST_ABORTED, -EIO, "STATUS_REQUEST_ABORTED"}, - {STATUS_CONNECTION_ABORTED, -ECONNABORTED, "STATUS_CONNECTION_ABORTED"}, - {STATUS_BAD_COMPRESSION_BUFFER, -EIO, "STATUS_BAD_COMPRESSION_BUFFER"}, - {STATUS_USER_MAPPED_FILE, -EIO, "STATUS_USER_MAPPED_FILE"}, - {STATUS_AUDIT_FAILED, -EIO, "STATUS_AUDIT_FAILED"}, - {STATUS_TIMER_RESOLUTION_NOT_SET, -EIO, - "STATUS_TIMER_RESOLUTION_NOT_SET"}, - {STATUS_CONNECTION_COUNT_LIMIT, -EIO, "STATUS_CONNECTION_COUNT_LIMIT"}, - {STATUS_LOGIN_TIME_RESTRICTION, -EACCES, - "STATUS_LOGIN_TIME_RESTRICTION"}, - {STATUS_LOGIN_WKSTA_RESTRICTION, -EACCES, - "STATUS_LOGIN_WKSTA_RESTRICTION"}, - {STATUS_IMAGE_MP_UP_MISMATCH, -EIO, "STATUS_IMAGE_MP_UP_MISMATCH"}, - {STATUS_INSUFFICIENT_LOGON_INFO, -EIO, - "STATUS_INSUFFICIENT_LOGON_INFO"}, - {STATUS_BAD_DLL_ENTRYPOINT, -EIO, "STATUS_BAD_DLL_ENTRYPOINT"}, - {STATUS_BAD_SERVICE_ENTRYPOINT, -EIO, "STATUS_BAD_SERVICE_ENTRYPOINT"}, - {STATUS_LPC_REPLY_LOST, -EIO, "STATUS_LPC_REPLY_LOST"}, - {STATUS_IP_ADDRESS_CONFLICT1, -EIO, "STATUS_IP_ADDRESS_CONFLICT1"}, - {STATUS_IP_ADDRESS_CONFLICT2, -EIO, "STATUS_IP_ADDRESS_CONFLICT2"}, - {STATUS_REGISTRY_QUOTA_LIMIT, -EDQUOT, "STATUS_REGISTRY_QUOTA_LIMIT"}, - {STATUS_PATH_NOT_COVERED, -EREMOTE, "STATUS_PATH_NOT_COVERED"}, - {STATUS_NO_CALLBACK_ACTIVE, -EIO, "STATUS_NO_CALLBACK_ACTIVE"}, - {STATUS_LICENSE_QUOTA_EXCEEDED, -EACCES, - "STATUS_LICENSE_QUOTA_EXCEEDED"}, - {STATUS_PWD_TOO_SHORT, -EIO, "STATUS_PWD_TOO_SHORT"}, - {STATUS_PWD_TOO_RECENT, -EIO, "STATUS_PWD_TOO_RECENT"}, - {STATUS_PWD_HISTORY_CONFLICT, -EIO, "STATUS_PWD_HISTORY_CONFLICT"}, - {STATUS_PLUGPLAY_NO_DEVICE, -EIO, "STATUS_PLUGPLAY_NO_DEVICE"}, - {STATUS_UNSUPPORTED_COMPRESSION, -EIO, - "STATUS_UNSUPPORTED_COMPRESSION"}, - {STATUS_INVALID_HW_PROFILE, -EIO, "STATUS_INVALID_HW_PROFILE"}, - {STATUS_INVALID_PLUGPLAY_DEVICE_PATH, -EIO, - "STATUS_INVALID_PLUGPLAY_DEVICE_PATH"}, - {STATUS_DRIVER_ORDINAL_NOT_FOUND, -EIO, - "STATUS_DRIVER_ORDINAL_NOT_FOUND"}, - {STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, -EIO, - "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND"}, - {STATUS_RESOURCE_NOT_OWNED, -EIO, "STATUS_RESOURCE_NOT_OWNED"}, - {STATUS_TOO_MANY_LINKS, -EMLINK, "STATUS_TOO_MANY_LINKS"}, - {STATUS_QUOTA_LIST_INCONSISTENT, -EIO, - "STATUS_QUOTA_LIST_INCONSISTENT"}, - {STATUS_FILE_IS_OFFLINE, -EIO, "STATUS_FILE_IS_OFFLINE"}, - {STATUS_EVALUATION_EXPIRATION, -EIO, "STATUS_EVALUATION_EXPIRATION"}, - {STATUS_ILLEGAL_DLL_RELOCATION, -EIO, "STATUS_ILLEGAL_DLL_RELOCATION"}, - {STATUS_LICENSE_VIOLATION, -EIO, "STATUS_LICENSE_VIOLATION"}, - {STATUS_DLL_INIT_FAILED_LOGOFF, -EIO, "STATUS_DLL_INIT_FAILED_LOGOFF"}, - {STATUS_DRIVER_UNABLE_TO_LOAD, -EIO, "STATUS_DRIVER_UNABLE_TO_LOAD"}, - {STATUS_DFS_UNAVAILABLE, -EIO, "STATUS_DFS_UNAVAILABLE"}, - {STATUS_VOLUME_DISMOUNTED, -EIO, "STATUS_VOLUME_DISMOUNTED"}, - {STATUS_WX86_INTERNAL_ERROR, -EIO, "STATUS_WX86_INTERNAL_ERROR"}, - {STATUS_WX86_FLOAT_STACK_CHECK, -EIO, "STATUS_WX86_FLOAT_STACK_CHECK"}, - {STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"}, - {STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"}, - {STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"}, - {STATUS_NOT_A_REPARSE_POINT, -EIO, "STATUS_NOT_A_REPARSE_POINT"}, - {STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"}, - {STATUS_IO_REPARSE_TAG_MISMATCH, -EIO, - "STATUS_IO_REPARSE_TAG_MISMATCH"}, - {STATUS_IO_REPARSE_DATA_INVALID, -EIO, - "STATUS_IO_REPARSE_DATA_INVALID"}, - {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EIO, - "STATUS_IO_REPARSE_TAG_NOT_HANDLED"}, - {STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO, - "STATUS_REPARSE_POINT_NOT_RESOLVED"}, - {STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO, - "STATUS_DIRECTORY_IS_A_REPARSE_POINT"}, - {STATUS_RANGE_LIST_CONFLICT, -EIO, "STATUS_RANGE_LIST_CONFLICT"}, - {STATUS_SOURCE_ELEMENT_EMPTY, -EIO, "STATUS_SOURCE_ELEMENT_EMPTY"}, - {STATUS_DESTINATION_ELEMENT_FULL, -EIO, - "STATUS_DESTINATION_ELEMENT_FULL"}, - {STATUS_ILLEGAL_ELEMENT_ADDRESS, -EIO, - "STATUS_ILLEGAL_ELEMENT_ADDRESS"}, - {STATUS_MAGAZINE_NOT_PRESENT, -EIO, "STATUS_MAGAZINE_NOT_PRESENT"}, - {STATUS_REINITIALIZATION_NEEDED, -EIO, - "STATUS_REINITIALIZATION_NEEDED"}, - {STATUS_ENCRYPTION_FAILED, -EIO, "STATUS_ENCRYPTION_FAILED"}, - {STATUS_DECRYPTION_FAILED, -EIO, "STATUS_DECRYPTION_FAILED"}, - {STATUS_RANGE_NOT_FOUND, -EIO, "STATUS_RANGE_NOT_FOUND"}, - {STATUS_NO_RECOVERY_POLICY, -EIO, "STATUS_NO_RECOVERY_POLICY"}, - {STATUS_NO_EFS, -EIO, "STATUS_NO_EFS"}, - {STATUS_WRONG_EFS, -EIO, "STATUS_WRONG_EFS"}, - {STATUS_NO_USER_KEYS, -EIO, "STATUS_NO_USER_KEYS"}, - {STATUS_FILE_NOT_ENCRYPTED, -EIO, "STATUS_FILE_NOT_ENCRYPTED"}, - {STATUS_NOT_EXPORT_FORMAT, -EIO, "STATUS_NOT_EXPORT_FORMAT"}, - {STATUS_FILE_ENCRYPTED, -EIO, "STATUS_FILE_ENCRYPTED"}, - {STATUS_WMI_GUID_NOT_FOUND, -EIO, "STATUS_WMI_GUID_NOT_FOUND"}, - {STATUS_WMI_INSTANCE_NOT_FOUND, -EIO, "STATUS_WMI_INSTANCE_NOT_FOUND"}, - {STATUS_WMI_ITEMID_NOT_FOUND, -EIO, "STATUS_WMI_ITEMID_NOT_FOUND"}, - {STATUS_WMI_TRY_AGAIN, -EIO, "STATUS_WMI_TRY_AGAIN"}, - {STATUS_SHARED_POLICY, -EIO, "STATUS_SHARED_POLICY"}, - {STATUS_POLICY_OBJECT_NOT_FOUND, -EIO, - "STATUS_POLICY_OBJECT_NOT_FOUND"}, - {STATUS_POLICY_ONLY_IN_DS, -EIO, "STATUS_POLICY_ONLY_IN_DS"}, - {STATUS_VOLUME_NOT_UPGRADED, -EIO, "STATUS_VOLUME_NOT_UPGRADED"}, - {STATUS_REMOTE_STORAGE_NOT_ACTIVE, -EIO, - "STATUS_REMOTE_STORAGE_NOT_ACTIVE"}, - {STATUS_REMOTE_STORAGE_MEDIA_ERROR, -EIO, - "STATUS_REMOTE_STORAGE_MEDIA_ERROR"}, - {STATUS_NO_TRACKING_SERVICE, -EIO, "STATUS_NO_TRACKING_SERVICE"}, - {STATUS_SERVER_SID_MISMATCH, -EIO, "STATUS_SERVER_SID_MISMATCH"}, - {STATUS_DS_NO_ATTRIBUTE_OR_VALUE, -EIO, - "STATUS_DS_NO_ATTRIBUTE_OR_VALUE"}, - {STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, -EIO, - "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX"}, - {STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, -EIO, - "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED"}, - {STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, -EIO, - "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS"}, - {STATUS_DS_BUSY, -EBUSY, "STATUS_DS_BUSY"}, - {STATUS_DS_UNAVAILABLE, -EIO, "STATUS_DS_UNAVAILABLE"}, - {STATUS_DS_NO_RIDS_ALLOCATED, -EIO, "STATUS_DS_NO_RIDS_ALLOCATED"}, - {STATUS_DS_NO_MORE_RIDS, -EIO, "STATUS_DS_NO_MORE_RIDS"}, - {STATUS_DS_INCORRECT_ROLE_OWNER, -EIO, - "STATUS_DS_INCORRECT_ROLE_OWNER"}, - {STATUS_DS_RIDMGR_INIT_ERROR, -EIO, "STATUS_DS_RIDMGR_INIT_ERROR"}, - {STATUS_DS_OBJ_CLASS_VIOLATION, -EIO, "STATUS_DS_OBJ_CLASS_VIOLATION"}, - {STATUS_DS_CANT_ON_NON_LEAF, -EIO, "STATUS_DS_CANT_ON_NON_LEAF"}, - {STATUS_DS_CANT_ON_RDN, -EIO, "STATUS_DS_CANT_ON_RDN"}, - {STATUS_DS_CANT_MOD_OBJ_CLASS, -EIO, "STATUS_DS_CANT_MOD_OBJ_CLASS"}, - {STATUS_DS_CROSS_DOM_MOVE_FAILED, -EIO, - "STATUS_DS_CROSS_DOM_MOVE_FAILED"}, - {STATUS_DS_GC_NOT_AVAILABLE, -EIO, "STATUS_DS_GC_NOT_AVAILABLE"}, - {STATUS_DIRECTORY_SERVICE_REQUIRED, -EIO, - "STATUS_DIRECTORY_SERVICE_REQUIRED"}, - {STATUS_REPARSE_ATTRIBUTE_CONFLICT, -EIO, - "STATUS_REPARSE_ATTRIBUTE_CONFLICT"}, - {STATUS_CANT_ENABLE_DENY_ONLY, -EIO, "STATUS_CANT_ENABLE_DENY_ONLY"}, - {STATUS_FLOAT_MULTIPLE_FAULTS, -EIO, "STATUS_FLOAT_MULTIPLE_FAULTS"}, - {STATUS_FLOAT_MULTIPLE_TRAPS, -EIO, "STATUS_FLOAT_MULTIPLE_TRAPS"}, - {STATUS_DEVICE_REMOVED, -EIO, "STATUS_DEVICE_REMOVED"}, - {STATUS_JOURNAL_DELETE_IN_PROGRESS, -EIO, - "STATUS_JOURNAL_DELETE_IN_PROGRESS"}, - {STATUS_JOURNAL_NOT_ACTIVE, -EIO, "STATUS_JOURNAL_NOT_ACTIVE"}, - {STATUS_NOINTERFACE, -EIO, "STATUS_NOINTERFACE"}, - {STATUS_DS_ADMIN_LIMIT_EXCEEDED, -EIO, - "STATUS_DS_ADMIN_LIMIT_EXCEEDED"}, - {STATUS_DRIVER_FAILED_SLEEP, -EIO, "STATUS_DRIVER_FAILED_SLEEP"}, - {STATUS_MUTUAL_AUTHENTICATION_FAILED, -EIO, - "STATUS_MUTUAL_AUTHENTICATION_FAILED"}, - {STATUS_CORRUPT_SYSTEM_FILE, -EIO, "STATUS_CORRUPT_SYSTEM_FILE"}, - {STATUS_DATATYPE_MISALIGNMENT_ERROR, -EIO, - "STATUS_DATATYPE_MISALIGNMENT_ERROR"}, - {STATUS_WMI_READ_ONLY, -EROFS, "STATUS_WMI_READ_ONLY"}, - {STATUS_WMI_SET_FAILURE, -EIO, "STATUS_WMI_SET_FAILURE"}, - {STATUS_COMMITMENT_MINIMUM, -EIO, "STATUS_COMMITMENT_MINIMUM"}, - {STATUS_REG_NAT_CONSUMPTION, -EIO, "STATUS_REG_NAT_CONSUMPTION"}, - {STATUS_TRANSPORT_FULL, -EIO, "STATUS_TRANSPORT_FULL"}, - {STATUS_DS_SAM_INIT_FAILURE, -EIO, "STATUS_DS_SAM_INIT_FAILURE"}, - {STATUS_ONLY_IF_CONNECTED, -EIO, "STATUS_ONLY_IF_CONNECTED"}, - {STATUS_DS_SENSITIVE_GROUP_VIOLATION, -EIO, - "STATUS_DS_SENSITIVE_GROUP_VIOLATION"}, - {STATUS_PNP_RESTART_ENUMERATION, -EIO, - "STATUS_PNP_RESTART_ENUMERATION"}, - {STATUS_JOURNAL_ENTRY_DELETED, -EIO, "STATUS_JOURNAL_ENTRY_DELETED"}, - {STATUS_DS_CANT_MOD_PRIMARYGROUPID, -EIO, - "STATUS_DS_CANT_MOD_PRIMARYGROUPID"}, - {STATUS_SYSTEM_IMAGE_BAD_SIGNATURE, -EIO, - "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE"}, - {STATUS_PNP_REBOOT_REQUIRED, -EIO, "STATUS_PNP_REBOOT_REQUIRED"}, - {STATUS_POWER_STATE_INVALID, -EIO, "STATUS_POWER_STATE_INVALID"}, - {STATUS_DS_INVALID_GROUP_TYPE, -EIO, "STATUS_DS_INVALID_GROUP_TYPE"}, - {STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, -EIO, - "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN"}, - {STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, -EIO, - "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN"}, - {STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER"}, - {STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER"}, - {STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, -EIO, - "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER"}, - {STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER"}, - {STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, -EIO, - "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER"}, - {STATUS_DS_HAVE_PRIMARY_MEMBERS, -EIO, - "STATUS_DS_HAVE_PRIMARY_MEMBERS"}, - {STATUS_WMI_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_WMI_NOT_SUPPORTED"}, - {STATUS_INSUFFICIENT_POWER, -EIO, "STATUS_INSUFFICIENT_POWER"}, - {STATUS_SAM_NEED_BOOTKEY_PASSWORD, -EIO, - "STATUS_SAM_NEED_BOOTKEY_PASSWORD"}, - {STATUS_SAM_NEED_BOOTKEY_FLOPPY, -EIO, - "STATUS_SAM_NEED_BOOTKEY_FLOPPY"}, - {STATUS_DS_CANT_START, -EIO, "STATUS_DS_CANT_START"}, - {STATUS_DS_INIT_FAILURE, -EIO, "STATUS_DS_INIT_FAILURE"}, - {STATUS_SAM_INIT_FAILURE, -EIO, "STATUS_SAM_INIT_FAILURE"}, - {STATUS_DS_GC_REQUIRED, -EIO, "STATUS_DS_GC_REQUIRED"}, - {STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, -EIO, - "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY"}, - {STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, -EIO, - "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS"}, - {STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED"}, - {STATUS_MULTIPLE_FAULT_VIOLATION, -EIO, - "STATUS_MULTIPLE_FAULT_VIOLATION"}, - {STATUS_CURRENT_DOMAIN_NOT_ALLOWED, -EIO, - "STATUS_CURRENT_DOMAIN_NOT_ALLOWED"}, - {STATUS_CANNOT_MAKE, -EIO, "STATUS_CANNOT_MAKE"}, - {STATUS_SYSTEM_SHUTDOWN, -EIO, "STATUS_SYSTEM_SHUTDOWN"}, - {STATUS_DS_INIT_FAILURE_CONSOLE, -EIO, - "STATUS_DS_INIT_FAILURE_CONSOLE"}, - {STATUS_DS_SAM_INIT_FAILURE_CONSOLE, -EIO, - "STATUS_DS_SAM_INIT_FAILURE_CONSOLE"}, - {STATUS_UNFINISHED_CONTEXT_DELETED, -EIO, - "STATUS_UNFINISHED_CONTEXT_DELETED"}, - {STATUS_NO_TGT_REPLY, -EIO, "STATUS_NO_TGT_REPLY"}, - /* Note that ENOATTTR and ENODATA are the same errno */ - {STATUS_OBJECTID_NOT_FOUND, -ENODATA, "STATUS_OBJECTID_NOT_FOUND"}, - {STATUS_NO_IP_ADDRESSES, -EIO, "STATUS_NO_IP_ADDRESSES"}, - {STATUS_WRONG_CREDENTIAL_HANDLE, -EIO, - "STATUS_WRONG_CREDENTIAL_HANDLE"}, - {STATUS_CRYPTO_SYSTEM_INVALID, -EIO, "STATUS_CRYPTO_SYSTEM_INVALID"}, - {STATUS_MAX_REFERRALS_EXCEEDED, -EIO, "STATUS_MAX_REFERRALS_EXCEEDED"}, - {STATUS_MUST_BE_KDC, -EIO, "STATUS_MUST_BE_KDC"}, - {STATUS_STRONG_CRYPTO_NOT_SUPPORTED, -EIO, - "STATUS_STRONG_CRYPTO_NOT_SUPPORTED"}, - {STATUS_TOO_MANY_PRINCIPALS, -EIO, "STATUS_TOO_MANY_PRINCIPALS"}, - {STATUS_NO_PA_DATA, -EIO, "STATUS_NO_PA_DATA"}, - {STATUS_PKINIT_NAME_MISMATCH, -EIO, "STATUS_PKINIT_NAME_MISMATCH"}, - {STATUS_SMARTCARD_LOGON_REQUIRED, -EIO, - "STATUS_SMARTCARD_LOGON_REQUIRED"}, - {STATUS_KDC_INVALID_REQUEST, -EIO, "STATUS_KDC_INVALID_REQUEST"}, - {STATUS_KDC_UNABLE_TO_REFER, -EIO, "STATUS_KDC_UNABLE_TO_REFER"}, - {STATUS_KDC_UNKNOWN_ETYPE, -EIO, "STATUS_KDC_UNKNOWN_ETYPE"}, - {STATUS_SHUTDOWN_IN_PROGRESS, -EIO, "STATUS_SHUTDOWN_IN_PROGRESS"}, - {STATUS_SERVER_SHUTDOWN_IN_PROGRESS, -EIO, - "STATUS_SERVER_SHUTDOWN_IN_PROGRESS"}, - {STATUS_NOT_SUPPORTED_ON_SBS, -EOPNOTSUPP, - "STATUS_NOT_SUPPORTED_ON_SBS"}, - {STATUS_WMI_GUID_DISCONNECTED, -EIO, "STATUS_WMI_GUID_DISCONNECTED"}, - {STATUS_WMI_ALREADY_DISABLED, -EIO, "STATUS_WMI_ALREADY_DISABLED"}, - {STATUS_WMI_ALREADY_ENABLED, -EIO, "STATUS_WMI_ALREADY_ENABLED"}, - {STATUS_MFT_TOO_FRAGMENTED, -EIO, "STATUS_MFT_TOO_FRAGMENTED"}, - {STATUS_COPY_PROTECTION_FAILURE, -EIO, - "STATUS_COPY_PROTECTION_FAILURE"}, - {STATUS_CSS_AUTHENTICATION_FAILURE, -EIO, - "STATUS_CSS_AUTHENTICATION_FAILURE"}, - {STATUS_CSS_KEY_NOT_PRESENT, -EIO, "STATUS_CSS_KEY_NOT_PRESENT"}, - {STATUS_CSS_KEY_NOT_ESTABLISHED, -EIO, - "STATUS_CSS_KEY_NOT_ESTABLISHED"}, - {STATUS_CSS_SCRAMBLED_SECTOR, -EIO, "STATUS_CSS_SCRAMBLED_SECTOR"}, - {STATUS_CSS_REGION_MISMATCH, -EIO, "STATUS_CSS_REGION_MISMATCH"}, - {STATUS_CSS_RESETS_EXHAUSTED, -EIO, "STATUS_CSS_RESETS_EXHAUSTED"}, - {STATUS_PKINIT_FAILURE, -EIO, "STATUS_PKINIT_FAILURE"}, - {STATUS_SMARTCARD_SUBSYSTEM_FAILURE, -EIO, - "STATUS_SMARTCARD_SUBSYSTEM_FAILURE"}, - {STATUS_NO_KERB_KEY, -EIO, "STATUS_NO_KERB_KEY"}, - {STATUS_HOST_DOWN, -EIO, "STATUS_HOST_DOWN"}, - {STATUS_UNSUPPORTED_PREAUTH, -EIO, "STATUS_UNSUPPORTED_PREAUTH"}, - {STATUS_EFS_ALG_BLOB_TOO_BIG, -EIO, "STATUS_EFS_ALG_BLOB_TOO_BIG"}, - {STATUS_PORT_NOT_SET, -EIO, "STATUS_PORT_NOT_SET"}, - {STATUS_DEBUGGER_INACTIVE, -EIO, "STATUS_DEBUGGER_INACTIVE"}, - {STATUS_DS_VERSION_CHECK_FAILURE, -EIO, - "STATUS_DS_VERSION_CHECK_FAILURE"}, - {STATUS_AUDITING_DISABLED, -EIO, "STATUS_AUDITING_DISABLED"}, - {STATUS_PRENT4_MACHINE_ACCOUNT, -EIO, "STATUS_PRENT4_MACHINE_ACCOUNT"}, - {STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, - "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER"}, - {STATUS_INVALID_IMAGE_WIN_32, -EIO, "STATUS_INVALID_IMAGE_WIN_32"}, - {STATUS_INVALID_IMAGE_WIN_64, -EIO, "STATUS_INVALID_IMAGE_WIN_64"}, - {STATUS_BAD_BINDINGS, -EIO, "STATUS_BAD_BINDINGS"}, - {STATUS_NETWORK_SESSION_EXPIRED, -EIO, - "STATUS_NETWORK_SESSION_EXPIRED"}, - {STATUS_APPHELP_BLOCK, -EIO, "STATUS_APPHELP_BLOCK"}, - {STATUS_ALL_SIDS_FILTERED, -EIO, "STATUS_ALL_SIDS_FILTERED"}, - {STATUS_NOT_SAFE_MODE_DRIVER, -EIO, "STATUS_NOT_SAFE_MODE_DRIVER"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_PATH, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_PATH"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER"}, - {STATUS_FAILED_DRIVER_ENTRY, -EIO, "STATUS_FAILED_DRIVER_ENTRY"}, - {STATUS_DEVICE_ENUMERATION_ERROR, -EIO, - "STATUS_DEVICE_ENUMERATION_ERROR"}, - {STATUS_MOUNT_POINT_NOT_RESOLVED, -EIO, - "STATUS_MOUNT_POINT_NOT_RESOLVED"}, - {STATUS_INVALID_DEVICE_OBJECT_PARAMETER, -EIO, - "STATUS_INVALID_DEVICE_OBJECT_PARAMETER"}, - {STATUS_MCA_OCCURED, -EIO, "STATUS_MCA_OCCURED"}, - {STATUS_DRIVER_BLOCKED_CRITICAL, -EIO, - "STATUS_DRIVER_BLOCKED_CRITICAL"}, - {STATUS_DRIVER_BLOCKED, -EIO, "STATUS_DRIVER_BLOCKED"}, - {STATUS_DRIVER_DATABASE_ERROR, -EIO, "STATUS_DRIVER_DATABASE_ERROR"}, - {STATUS_SYSTEM_HIVE_TOO_LARGE, -EIO, "STATUS_SYSTEM_HIVE_TOO_LARGE"}, - {STATUS_INVALID_IMPORT_OF_NON_DLL, -EIO, - "STATUS_INVALID_IMPORT_OF_NON_DLL"}, - {STATUS_NO_SECRETS, -EIO, "STATUS_NO_SECRETS"}, - {STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, -EACCES, - "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"}, - {STATUS_FAILED_STACK_SWITCH, -EIO, "STATUS_FAILED_STACK_SWITCH"}, - {STATUS_HEAP_CORRUPTION, -EIO, "STATUS_HEAP_CORRUPTION"}, - {STATUS_SMARTCARD_WRONG_PIN, -EIO, "STATUS_SMARTCARD_WRONG_PIN"}, - {STATUS_SMARTCARD_CARD_BLOCKED, -EIO, "STATUS_SMARTCARD_CARD_BLOCKED"}, - {STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED, -EIO, - "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED"}, - {STATUS_SMARTCARD_NO_CARD, -EIO, "STATUS_SMARTCARD_NO_CARD"}, - {STATUS_SMARTCARD_NO_KEY_CONTAINER, -EIO, - "STATUS_SMARTCARD_NO_KEY_CONTAINER"}, - {STATUS_SMARTCARD_NO_CERTIFICATE, -EIO, - "STATUS_SMARTCARD_NO_CERTIFICATE"}, - {STATUS_SMARTCARD_NO_KEYSET, -EIO, "STATUS_SMARTCARD_NO_KEYSET"}, - {STATUS_SMARTCARD_IO_ERROR, -EIO, "STATUS_SMARTCARD_IO_ERROR"}, - {STATUS_DOWNGRADE_DETECTED, -EIO, "STATUS_DOWNGRADE_DETECTED"}, - {STATUS_SMARTCARD_CERT_REVOKED, -EIO, "STATUS_SMARTCARD_CERT_REVOKED"}, - {STATUS_ISSUING_CA_UNTRUSTED, -EIO, "STATUS_ISSUING_CA_UNTRUSTED"}, - {STATUS_REVOCATION_OFFLINE_C, -EIO, "STATUS_REVOCATION_OFFLINE_C"}, - {STATUS_PKINIT_CLIENT_FAILURE, -EIO, "STATUS_PKINIT_CLIENT_FAILURE"}, - {STATUS_SMARTCARD_CERT_EXPIRED, -EIO, "STATUS_SMARTCARD_CERT_EXPIRED"}, - {STATUS_DRIVER_FAILED_PRIOR_UNLOAD, -EIO, - "STATUS_DRIVER_FAILED_PRIOR_UNLOAD"}, - {STATUS_SMARTCARD_SILENT_CONTEXT, -EIO, - "STATUS_SMARTCARD_SILENT_CONTEXT"}, - {STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED"}, - {STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED"}, - {STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED"}, - {STATUS_DS_NAME_NOT_UNIQUE, -EIO, "STATUS_DS_NAME_NOT_UNIQUE"}, - {STATUS_DS_DUPLICATE_ID_FOUND, -EIO, "STATUS_DS_DUPLICATE_ID_FOUND"}, - {STATUS_DS_GROUP_CONVERSION_ERROR, -EIO, - "STATUS_DS_GROUP_CONVERSION_ERROR"}, - {STATUS_VOLSNAP_PREPARE_HIBERNATE, -EIO, - "STATUS_VOLSNAP_PREPARE_HIBERNATE"}, - {STATUS_USER2USER_REQUIRED, -EIO, "STATUS_USER2USER_REQUIRED"}, - {STATUS_STACK_BUFFER_OVERRUN, -EIO, "STATUS_STACK_BUFFER_OVERRUN"}, - {STATUS_NO_S4U_PROT_SUPPORT, -EIO, "STATUS_NO_S4U_PROT_SUPPORT"}, - {STATUS_CROSSREALM_DELEGATION_FAILURE, -EIO, - "STATUS_CROSSREALM_DELEGATION_FAILURE"}, - {STATUS_REVOCATION_OFFLINE_KDC, -EIO, "STATUS_REVOCATION_OFFLINE_KDC"}, - {STATUS_ISSUING_CA_UNTRUSTED_KDC, -EIO, - "STATUS_ISSUING_CA_UNTRUSTED_KDC"}, - {STATUS_KDC_CERT_EXPIRED, -EIO, "STATUS_KDC_CERT_EXPIRED"}, - {STATUS_KDC_CERT_REVOKED, -EIO, "STATUS_KDC_CERT_REVOKED"}, - {STATUS_PARAMETER_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PARAMETER_QUOTA_EXCEEDED"}, - {STATUS_HIBERNATION_FAILURE, -EIO, "STATUS_HIBERNATION_FAILURE"}, - {STATUS_DELAY_LOAD_FAILED, -EIO, "STATUS_DELAY_LOAD_FAILED"}, - {STATUS_AUTHENTICATION_FIREWALL_FAILED, -EIO, - "STATUS_AUTHENTICATION_FIREWALL_FAILED"}, - {STATUS_VDM_DISALLOWED, -EIO, "STATUS_VDM_DISALLOWED"}, - {STATUS_HUNG_DISPLAY_DRIVER_THREAD, -EIO, - "STATUS_HUNG_DISPLAY_DRIVER_THREAD"}, - {STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, -EIO, - "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE"}, - {STATUS_INVALID_CRUNTIME_PARAMETER, -EIO, - "STATUS_INVALID_CRUNTIME_PARAMETER"}, - {STATUS_NTLM_BLOCKED, -EIO, "STATUS_NTLM_BLOCKED"}, - {STATUS_ASSERTION_FAILURE, -EIO, "STATUS_ASSERTION_FAILURE"}, - {STATUS_VERIFIER_STOP, -EIO, "STATUS_VERIFIER_STOP"}, - {STATUS_CALLBACK_POP_STACK, -EIO, "STATUS_CALLBACK_POP_STACK"}, - {STATUS_INCOMPATIBLE_DRIVER_BLOCKED, -EIO, - "STATUS_INCOMPATIBLE_DRIVER_BLOCKED"}, - {STATUS_HIVE_UNLOADED, -EIO, "STATUS_HIVE_UNLOADED"}, - {STATUS_COMPRESSION_DISABLED, -EIO, "STATUS_COMPRESSION_DISABLED"}, - {STATUS_FILE_SYSTEM_LIMITATION, -EIO, "STATUS_FILE_SYSTEM_LIMITATION"}, - {STATUS_INVALID_IMAGE_HASH, -EIO, "STATUS_INVALID_IMAGE_HASH"}, - {STATUS_NOT_CAPABLE, -EIO, "STATUS_NOT_CAPABLE"}, - {STATUS_REQUEST_OUT_OF_SEQUENCE, -EIO, - "STATUS_REQUEST_OUT_OF_SEQUENCE"}, - {STATUS_IMPLEMENTATION_LIMIT, -EIO, "STATUS_IMPLEMENTATION_LIMIT"}, - {STATUS_ELEVATION_REQUIRED, -EIO, "STATUS_ELEVATION_REQUIRED"}, - {STATUS_BEYOND_VDL, -EIO, "STATUS_BEYOND_VDL"}, - {STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, -EIO, - "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS"}, - {STATUS_PTE_CHANGED, -EIO, "STATUS_PTE_CHANGED"}, - {STATUS_PURGE_FAILED, -EIO, "STATUS_PURGE_FAILED"}, - {STATUS_CRED_REQUIRES_CONFIRMATION, -EIO, - "STATUS_CRED_REQUIRES_CONFIRMATION"}, - {STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, -EIO, - "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE"}, - {STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, -EIO, - "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER"}, - {STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, -EIO, - "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE"}, - {STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, -EIO, - "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE"}, - {STATUS_CS_ENCRYPTION_FILE_NOT_CSE, -EIO, - "STATUS_CS_ENCRYPTION_FILE_NOT_CSE"}, - {STATUS_INVALID_LABEL, -EIO, "STATUS_INVALID_LABEL"}, - {STATUS_DRIVER_PROCESS_TERMINATED, -EIO, - "STATUS_DRIVER_PROCESS_TERMINATED"}, - {STATUS_AMBIGUOUS_SYSTEM_DEVICE, -EIO, - "STATUS_AMBIGUOUS_SYSTEM_DEVICE"}, - {STATUS_SYSTEM_DEVICE_NOT_FOUND, -EIO, - "STATUS_SYSTEM_DEVICE_NOT_FOUND"}, - {STATUS_RESTART_BOOT_APPLICATION, -EIO, - "STATUS_RESTART_BOOT_APPLICATION"}, - {STATUS_INVALID_TASK_NAME, -EIO, "STATUS_INVALID_TASK_NAME"}, - {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"}, - {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"}, - {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"}, - {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"}, - {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"}, - {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"}, - {STATUS_REQUEST_CANCELED, -EIO, "STATUS_REQUEST_CANCELED"}, - {STATUS_RECURSIVE_DISPATCH, -EIO, "STATUS_RECURSIVE_DISPATCH"}, - {STATUS_LPC_RECEIVE_BUFFER_EXPECTED, -EIO, - "STATUS_LPC_RECEIVE_BUFFER_EXPECTED"}, - {STATUS_LPC_INVALID_CONNECTION_USAGE, -EIO, - "STATUS_LPC_INVALID_CONNECTION_USAGE"}, - {STATUS_LPC_REQUESTS_NOT_ALLOWED, -EIO, - "STATUS_LPC_REQUESTS_NOT_ALLOWED"}, - {STATUS_RESOURCE_IN_USE, -EIO, "STATUS_RESOURCE_IN_USE"}, - {STATUS_HARDWARE_MEMORY_ERROR, -EIO, "STATUS_HARDWARE_MEMORY_ERROR"}, - {STATUS_THREADPOOL_HANDLE_EXCEPTION, -EIO, - "STATUS_THREADPOOL_HANDLE_EXCEPTION"}, - {STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASED_DURING_OPERATION, -EIO, - "STATUS_THREADPOOL_RELEASED_DURING_OPERATION"}, - {STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, -EIO, - "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING"}, - {STATUS_APC_RETURNED_WHILE_IMPERSONATING, -EIO, - "STATUS_APC_RETURNED_WHILE_IMPERSONATING"}, - {STATUS_PROCESS_IS_PROTECTED, -EIO, "STATUS_PROCESS_IS_PROTECTED"}, - {STATUS_MCA_EXCEPTION, -EIO, "STATUS_MCA_EXCEPTION"}, - {STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, -EIO, - "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE"}, - {STATUS_SYMLINK_CLASS_DISABLED, -EIO, "STATUS_SYMLINK_CLASS_DISABLED"}, - {STATUS_INVALID_IDN_NORMALIZATION, -EIO, - "STATUS_INVALID_IDN_NORMALIZATION"}, - {STATUS_NO_UNICODE_TRANSLATION, -EIO, "STATUS_NO_UNICODE_TRANSLATION"}, - {STATUS_ALREADY_REGISTERED, -EIO, "STATUS_ALREADY_REGISTERED"}, - {STATUS_CONTEXT_MISMATCH, -EIO, "STATUS_CONTEXT_MISMATCH"}, - {STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, -EIO, - "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST"}, - {STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, -EIO, - "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY"}, - {STATUS_INVALID_THREAD, -EIO, "STATUS_INVALID_THREAD"}, - {STATUS_CALLBACK_RETURNED_TRANSACTION, -EIO, - "STATUS_CALLBACK_RETURNED_TRANSACTION"}, - {STATUS_CALLBACK_RETURNED_LDR_LOCK, -EIO, - "STATUS_CALLBACK_RETURNED_LDR_LOCK"}, - {STATUS_CALLBACK_RETURNED_LANG, -EIO, "STATUS_CALLBACK_RETURNED_LANG"}, - {STATUS_CALLBACK_RETURNED_PRI_BACK, -EIO, - "STATUS_CALLBACK_RETURNED_PRI_BACK"}, - {STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, -EIO, - "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY"}, - {STATUS_DISK_REPAIR_DISABLED, -EIO, "STATUS_DISK_REPAIR_DISABLED"}, - {STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, -EIO, - "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS"}, - {STATUS_DISK_QUOTA_EXCEEDED, -EDQUOT, "STATUS_DISK_QUOTA_EXCEEDED"}, - {STATUS_CONTENT_BLOCKED, -EIO, "STATUS_CONTENT_BLOCKED"}, - {STATUS_BAD_CLUSTERS, -EIO, "STATUS_BAD_CLUSTERS"}, - {STATUS_VOLUME_DIRTY, -EIO, "STATUS_VOLUME_DIRTY"}, - {STATUS_FILE_CHECKED_OUT, -EIO, "STATUS_FILE_CHECKED_OUT"}, - {STATUS_CHECKOUT_REQUIRED, -EIO, "STATUS_CHECKOUT_REQUIRED"}, - {STATUS_BAD_FILE_TYPE, -EIO, "STATUS_BAD_FILE_TYPE"}, - {STATUS_FILE_TOO_LARGE, -EIO, "STATUS_FILE_TOO_LARGE"}, - {STATUS_FORMS_AUTH_REQUIRED, -EIO, "STATUS_FORMS_AUTH_REQUIRED"}, - {STATUS_VIRUS_INFECTED, -EIO, "STATUS_VIRUS_INFECTED"}, - {STATUS_VIRUS_DELETED, -EIO, "STATUS_VIRUS_DELETED"}, - {STATUS_BAD_MCFG_TABLE, -EIO, "STATUS_BAD_MCFG_TABLE"}, - {STATUS_WOW_ASSERTION, -EIO, "STATUS_WOW_ASSERTION"}, - {STATUS_INVALID_SIGNATURE, -EIO, "STATUS_INVALID_SIGNATURE"}, - {STATUS_HMAC_NOT_SUPPORTED, -EIO, "STATUS_HMAC_NOT_SUPPORTED"}, - {STATUS_IPSEC_QUEUE_OVERFLOW, -EIO, "STATUS_IPSEC_QUEUE_OVERFLOW"}, - {STATUS_ND_QUEUE_OVERFLOW, -EIO, "STATUS_ND_QUEUE_OVERFLOW"}, - {STATUS_HOPLIMIT_EXCEEDED, -EIO, "STATUS_HOPLIMIT_EXCEEDED"}, - {STATUS_PROTOCOL_NOT_SUPPORTED, -EOPNOTSUPP, - "STATUS_PROTOCOL_NOT_SUPPORTED"}, - {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED"}, - {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR"}, - {STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR"}, - {STATUS_XML_PARSE_ERROR, -EIO, "STATUS_XML_PARSE_ERROR"}, - {STATUS_XMLDSIG_ERROR, -EIO, "STATUS_XMLDSIG_ERROR"}, - {STATUS_WRONG_COMPARTMENT, -EIO, "STATUS_WRONG_COMPARTMENT"}, - {STATUS_AUTHIP_FAILURE, -EIO, "STATUS_AUTHIP_FAILURE"}, - {DBG_NO_STATE_CHANGE, -EIO, "DBG_NO_STATE_CHANGE"}, - {DBG_APP_NOT_IDLE, -EIO, "DBG_APP_NOT_IDLE"}, - {RPC_NT_INVALID_STRING_BINDING, -EIO, "RPC_NT_INVALID_STRING_BINDING"}, - {RPC_NT_WRONG_KIND_OF_BINDING, -EIO, "RPC_NT_WRONG_KIND_OF_BINDING"}, - {RPC_NT_INVALID_BINDING, -EIO, "RPC_NT_INVALID_BINDING"}, - {RPC_NT_PROTSEQ_NOT_SUPPORTED, -EOPNOTSUPP, - "RPC_NT_PROTSEQ_NOT_SUPPORTED"}, - {RPC_NT_INVALID_RPC_PROTSEQ, -EIO, "RPC_NT_INVALID_RPC_PROTSEQ"}, - {RPC_NT_INVALID_STRING_UUID, -EIO, "RPC_NT_INVALID_STRING_UUID"}, - {RPC_NT_INVALID_ENDPOINT_FORMAT, -EIO, - "RPC_NT_INVALID_ENDPOINT_FORMAT"}, - {RPC_NT_INVALID_NET_ADDR, -EIO, "RPC_NT_INVALID_NET_ADDR"}, - {RPC_NT_NO_ENDPOINT_FOUND, -EIO, "RPC_NT_NO_ENDPOINT_FOUND"}, - {RPC_NT_INVALID_TIMEOUT, -EINVAL, "RPC_NT_INVALID_TIMEOUT"}, - {RPC_NT_OBJECT_NOT_FOUND, -ENOENT, "RPC_NT_OBJECT_NOT_FOUND"}, - {RPC_NT_ALREADY_REGISTERED, -EIO, "RPC_NT_ALREADY_REGISTERED"}, - {RPC_NT_TYPE_ALREADY_REGISTERED, -EIO, - "RPC_NT_TYPE_ALREADY_REGISTERED"}, - {RPC_NT_ALREADY_LISTENING, -EIO, "RPC_NT_ALREADY_LISTENING"}, - {RPC_NT_NO_PROTSEQS_REGISTERED, -EIO, "RPC_NT_NO_PROTSEQS_REGISTERED"}, - {RPC_NT_NOT_LISTENING, -EIO, "RPC_NT_NOT_LISTENING"}, - {RPC_NT_UNKNOWN_MGR_TYPE, -EIO, "RPC_NT_UNKNOWN_MGR_TYPE"}, - {RPC_NT_UNKNOWN_IF, -EIO, "RPC_NT_UNKNOWN_IF"}, - {RPC_NT_NO_BINDINGS, -EIO, "RPC_NT_NO_BINDINGS"}, - {RPC_NT_NO_PROTSEQS, -EIO, "RPC_NT_NO_PROTSEQS"}, - {RPC_NT_CANT_CREATE_ENDPOINT, -EIO, "RPC_NT_CANT_CREATE_ENDPOINT"}, - {RPC_NT_OUT_OF_RESOURCES, -EIO, "RPC_NT_OUT_OF_RESOURCES"}, - {RPC_NT_SERVER_UNAVAILABLE, -EIO, "RPC_NT_SERVER_UNAVAILABLE"}, - {RPC_NT_SERVER_TOO_BUSY, -EBUSY, "RPC_NT_SERVER_TOO_BUSY"}, - {RPC_NT_INVALID_NETWORK_OPTIONS, -EIO, - "RPC_NT_INVALID_NETWORK_OPTIONS"}, - {RPC_NT_NO_CALL_ACTIVE, -EIO, "RPC_NT_NO_CALL_ACTIVE"}, - {RPC_NT_CALL_FAILED, -EIO, "RPC_NT_CALL_FAILED"}, - {RPC_NT_CALL_FAILED_DNE, -EIO, "RPC_NT_CALL_FAILED_DNE"}, - {RPC_NT_PROTOCOL_ERROR, -EIO, "RPC_NT_PROTOCOL_ERROR"}, - {RPC_NT_UNSUPPORTED_TRANS_SYN, -EIO, "RPC_NT_UNSUPPORTED_TRANS_SYN"}, - {RPC_NT_UNSUPPORTED_TYPE, -EIO, "RPC_NT_UNSUPPORTED_TYPE"}, - {RPC_NT_INVALID_TAG, -EIO, "RPC_NT_INVALID_TAG"}, - {RPC_NT_INVALID_BOUND, -EIO, "RPC_NT_INVALID_BOUND"}, - {RPC_NT_NO_ENTRY_NAME, -EIO, "RPC_NT_NO_ENTRY_NAME"}, - {RPC_NT_INVALID_NAME_SYNTAX, -EIO, "RPC_NT_INVALID_NAME_SYNTAX"}, - {RPC_NT_UNSUPPORTED_NAME_SYNTAX, -EIO, - "RPC_NT_UNSUPPORTED_NAME_SYNTAX"}, - {RPC_NT_UUID_NO_ADDRESS, -EIO, "RPC_NT_UUID_NO_ADDRESS"}, - {RPC_NT_DUPLICATE_ENDPOINT, -ENOTUNIQ, "RPC_NT_DUPLICATE_ENDPOINT"}, - {RPC_NT_UNKNOWN_AUTHN_TYPE, -EIO, "RPC_NT_UNKNOWN_AUTHN_TYPE"}, - {RPC_NT_MAX_CALLS_TOO_SMALL, -EIO, "RPC_NT_MAX_CALLS_TOO_SMALL"}, - {RPC_NT_STRING_TOO_LONG, -EIO, "RPC_NT_STRING_TOO_LONG"}, - {RPC_NT_PROTSEQ_NOT_FOUND, -EIO, "RPC_NT_PROTSEQ_NOT_FOUND"}, - {RPC_NT_PROCNUM_OUT_OF_RANGE, -EIO, "RPC_NT_PROCNUM_OUT_OF_RANGE"}, - {RPC_NT_BINDING_HAS_NO_AUTH, -EIO, "RPC_NT_BINDING_HAS_NO_AUTH"}, - {RPC_NT_UNKNOWN_AUTHN_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHN_SERVICE"}, - {RPC_NT_UNKNOWN_AUTHN_LEVEL, -EIO, "RPC_NT_UNKNOWN_AUTHN_LEVEL"}, - {RPC_NT_INVALID_AUTH_IDENTITY, -EIO, "RPC_NT_INVALID_AUTH_IDENTITY"}, - {RPC_NT_UNKNOWN_AUTHZ_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHZ_SERVICE"}, - {EPT_NT_INVALID_ENTRY, -EIO, "EPT_NT_INVALID_ENTRY"}, - {EPT_NT_CANT_PERFORM_OP, -EIO, "EPT_NT_CANT_PERFORM_OP"}, - {EPT_NT_NOT_REGISTERED, -EIO, "EPT_NT_NOT_REGISTERED"}, - {RPC_NT_NOTHING_TO_EXPORT, -EIO, "RPC_NT_NOTHING_TO_EXPORT"}, - {RPC_NT_INCOMPLETE_NAME, -EIO, "RPC_NT_INCOMPLETE_NAME"}, - {RPC_NT_INVALID_VERS_OPTION, -EIO, "RPC_NT_INVALID_VERS_OPTION"}, - {RPC_NT_NO_MORE_MEMBERS, -EIO, "RPC_NT_NO_MORE_MEMBERS"}, - {RPC_NT_NOT_ALL_OBJS_UNEXPORTED, -EIO, - "RPC_NT_NOT_ALL_OBJS_UNEXPORTED"}, - {RPC_NT_INTERFACE_NOT_FOUND, -EIO, "RPC_NT_INTERFACE_NOT_FOUND"}, - {RPC_NT_ENTRY_ALREADY_EXISTS, -EIO, "RPC_NT_ENTRY_ALREADY_EXISTS"}, - {RPC_NT_ENTRY_NOT_FOUND, -EIO, "RPC_NT_ENTRY_NOT_FOUND"}, - {RPC_NT_NAME_SERVICE_UNAVAILABLE, -EIO, - "RPC_NT_NAME_SERVICE_UNAVAILABLE"}, - {RPC_NT_INVALID_NAF_ID, -EIO, "RPC_NT_INVALID_NAF_ID"}, - {RPC_NT_CANNOT_SUPPORT, -EOPNOTSUPP, "RPC_NT_CANNOT_SUPPORT"}, - {RPC_NT_NO_CONTEXT_AVAILABLE, -EIO, "RPC_NT_NO_CONTEXT_AVAILABLE"}, - {RPC_NT_INTERNAL_ERROR, -EIO, "RPC_NT_INTERNAL_ERROR"}, - {RPC_NT_ZERO_DIVIDE, -EIO, "RPC_NT_ZERO_DIVIDE"}, - {RPC_NT_ADDRESS_ERROR, -EIO, "RPC_NT_ADDRESS_ERROR"}, - {RPC_NT_FP_DIV_ZERO, -EIO, "RPC_NT_FP_DIV_ZERO"}, - {RPC_NT_FP_UNDERFLOW, -EIO, "RPC_NT_FP_UNDERFLOW"}, - {RPC_NT_FP_OVERFLOW, -EIO, "RPC_NT_FP_OVERFLOW"}, - {RPC_NT_CALL_IN_PROGRESS, -EIO, "RPC_NT_CALL_IN_PROGRESS"}, - {RPC_NT_NO_MORE_BINDINGS, -EIO, "RPC_NT_NO_MORE_BINDINGS"}, - {RPC_NT_GROUP_MEMBER_NOT_FOUND, -EIO, "RPC_NT_GROUP_MEMBER_NOT_FOUND"}, - {EPT_NT_CANT_CREATE, -EIO, "EPT_NT_CANT_CREATE"}, - {RPC_NT_INVALID_OBJECT, -EIO, "RPC_NT_INVALID_OBJECT"}, - {RPC_NT_NO_INTERFACES, -EIO, "RPC_NT_NO_INTERFACES"}, - {RPC_NT_CALL_CANCELLED, -EIO, "RPC_NT_CALL_CANCELLED"}, - {RPC_NT_BINDING_INCOMPLETE, -EIO, "RPC_NT_BINDING_INCOMPLETE"}, - {RPC_NT_COMM_FAILURE, -EIO, "RPC_NT_COMM_FAILURE"}, - {RPC_NT_UNSUPPORTED_AUTHN_LEVEL, -EIO, - "RPC_NT_UNSUPPORTED_AUTHN_LEVEL"}, - {RPC_NT_NO_PRINC_NAME, -EIO, "RPC_NT_NO_PRINC_NAME"}, - {RPC_NT_NOT_RPC_ERROR, -EIO, "RPC_NT_NOT_RPC_ERROR"}, - {RPC_NT_SEC_PKG_ERROR, -EIO, "RPC_NT_SEC_PKG_ERROR"}, - {RPC_NT_NOT_CANCELLED, -EIO, "RPC_NT_NOT_CANCELLED"}, - {RPC_NT_INVALID_ASYNC_HANDLE, -EIO, "RPC_NT_INVALID_ASYNC_HANDLE"}, - {RPC_NT_INVALID_ASYNC_CALL, -EIO, "RPC_NT_INVALID_ASYNC_CALL"}, - {RPC_NT_PROXY_ACCESS_DENIED, -EACCES, "RPC_NT_PROXY_ACCESS_DENIED"}, - {RPC_NT_NO_MORE_ENTRIES, -EIO, "RPC_NT_NO_MORE_ENTRIES"}, - {RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, -EIO, - "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL"}, - {RPC_NT_SS_CHAR_TRANS_SHORT_FILE, -EIO, - "RPC_NT_SS_CHAR_TRANS_SHORT_FILE"}, - {RPC_NT_SS_IN_NULL_CONTEXT, -EIO, "RPC_NT_SS_IN_NULL_CONTEXT"}, - {RPC_NT_SS_CONTEXT_MISMATCH, -EIO, "RPC_NT_SS_CONTEXT_MISMATCH"}, - {RPC_NT_SS_CONTEXT_DAMAGED, -EIO, "RPC_NT_SS_CONTEXT_DAMAGED"}, - {RPC_NT_SS_HANDLES_MISMATCH, -EIO, "RPC_NT_SS_HANDLES_MISMATCH"}, - {RPC_NT_SS_CANNOT_GET_CALL_HANDLE, -EIO, - "RPC_NT_SS_CANNOT_GET_CALL_HANDLE"}, - {RPC_NT_NULL_REF_POINTER, -EIO, "RPC_NT_NULL_REF_POINTER"}, - {RPC_NT_ENUM_VALUE_OUT_OF_RANGE, -EIO, - "RPC_NT_ENUM_VALUE_OUT_OF_RANGE"}, - {RPC_NT_BYTE_COUNT_TOO_SMALL, -EIO, "RPC_NT_BYTE_COUNT_TOO_SMALL"}, - {RPC_NT_BAD_STUB_DATA, -EIO, "RPC_NT_BAD_STUB_DATA"}, - {RPC_NT_INVALID_ES_ACTION, -EIO, "RPC_NT_INVALID_ES_ACTION"}, - {RPC_NT_WRONG_ES_VERSION, -EIO, "RPC_NT_WRONG_ES_VERSION"}, - {RPC_NT_WRONG_STUB_VERSION, -EIO, "RPC_NT_WRONG_STUB_VERSION"}, - {RPC_NT_INVALID_PIPE_OBJECT, -EIO, "RPC_NT_INVALID_PIPE_OBJECT"}, - {RPC_NT_INVALID_PIPE_OPERATION, -EIO, "RPC_NT_INVALID_PIPE_OPERATION"}, - {RPC_NT_WRONG_PIPE_VERSION, -EIO, "RPC_NT_WRONG_PIPE_VERSION"}, - {RPC_NT_PIPE_CLOSED, -EIO, "RPC_NT_PIPE_CLOSED"}, - {RPC_NT_PIPE_DISCIPLINE_ERROR, -EIO, "RPC_NT_PIPE_DISCIPLINE_ERROR"}, - {RPC_NT_PIPE_EMPTY, -EIO, "RPC_NT_PIPE_EMPTY"}, - {STATUS_PNP_BAD_MPS_TABLE, -EIO, "STATUS_PNP_BAD_MPS_TABLE"}, - {STATUS_PNP_TRANSLATION_FAILED, -EIO, "STATUS_PNP_TRANSLATION_FAILED"}, - {STATUS_PNP_IRQ_TRANSLATION_FAILED, -EIO, - "STATUS_PNP_IRQ_TRANSLATION_FAILED"}, - {STATUS_PNP_INVALID_ID, -EIO, "STATUS_PNP_INVALID_ID"}, - {STATUS_IO_REISSUE_AS_CACHED, -EIO, "STATUS_IO_REISSUE_AS_CACHED"}, - {STATUS_CTX_WINSTATION_NAME_INVALID, -EIO, - "STATUS_CTX_WINSTATION_NAME_INVALID"}, - {STATUS_CTX_INVALID_PD, -EIO, "STATUS_CTX_INVALID_PD"}, - {STATUS_CTX_PD_NOT_FOUND, -EIO, "STATUS_CTX_PD_NOT_FOUND"}, - {STATUS_CTX_CLOSE_PENDING, -EIO, "STATUS_CTX_CLOSE_PENDING"}, - {STATUS_CTX_NO_OUTBUF, -EIO, "STATUS_CTX_NO_OUTBUF"}, - {STATUS_CTX_MODEM_INF_NOT_FOUND, -EIO, - "STATUS_CTX_MODEM_INF_NOT_FOUND"}, - {STATUS_CTX_INVALID_MODEMNAME, -EIO, "STATUS_CTX_INVALID_MODEMNAME"}, - {STATUS_CTX_RESPONSE_ERROR, -EIO, "STATUS_CTX_RESPONSE_ERROR"}, - {STATUS_CTX_MODEM_RESPONSE_TIMEOUT, -ETIMEDOUT, - "STATUS_CTX_MODEM_RESPONSE_TIMEOUT"}, - {STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, -EIO, - "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER"}, - {STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, -EIO, - "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE"}, - {STATUS_CTX_MODEM_RESPONSE_BUSY, -EBUSY, - "STATUS_CTX_MODEM_RESPONSE_BUSY"}, - {STATUS_CTX_MODEM_RESPONSE_VOICE, -EIO, - "STATUS_CTX_MODEM_RESPONSE_VOICE"}, - {STATUS_CTX_TD_ERROR, -EIO, "STATUS_CTX_TD_ERROR"}, - {STATUS_CTX_LICENSE_CLIENT_INVALID, -EIO, - "STATUS_CTX_LICENSE_CLIENT_INVALID"}, - {STATUS_CTX_LICENSE_NOT_AVAILABLE, -EIO, - "STATUS_CTX_LICENSE_NOT_AVAILABLE"}, - {STATUS_CTX_LICENSE_EXPIRED, -EIO, "STATUS_CTX_LICENSE_EXPIRED"}, - {STATUS_CTX_WINSTATION_NOT_FOUND, -EIO, - "STATUS_CTX_WINSTATION_NOT_FOUND"}, - {STATUS_CTX_WINSTATION_NAME_COLLISION, -EIO, - "STATUS_CTX_WINSTATION_NAME_COLLISION"}, - {STATUS_CTX_WINSTATION_BUSY, -EBUSY, "STATUS_CTX_WINSTATION_BUSY"}, - {STATUS_CTX_BAD_VIDEO_MODE, -EIO, "STATUS_CTX_BAD_VIDEO_MODE"}, - {STATUS_CTX_GRAPHICS_INVALID, -EIO, "STATUS_CTX_GRAPHICS_INVALID"}, - {STATUS_CTX_NOT_CONSOLE, -EIO, "STATUS_CTX_NOT_CONSOLE"}, - {STATUS_CTX_CLIENT_QUERY_TIMEOUT, -EIO, - "STATUS_CTX_CLIENT_QUERY_TIMEOUT"}, - {STATUS_CTX_CONSOLE_DISCONNECT, -EIO, "STATUS_CTX_CONSOLE_DISCONNECT"}, - {STATUS_CTX_CONSOLE_CONNECT, -EIO, "STATUS_CTX_CONSOLE_CONNECT"}, - {STATUS_CTX_SHADOW_DENIED, -EIO, "STATUS_CTX_SHADOW_DENIED"}, - {STATUS_CTX_WINSTATION_ACCESS_DENIED, -EACCES, - "STATUS_CTX_WINSTATION_ACCESS_DENIED"}, - {STATUS_CTX_INVALID_WD, -EIO, "STATUS_CTX_INVALID_WD"}, - {STATUS_CTX_WD_NOT_FOUND, -EIO, "STATUS_CTX_WD_NOT_FOUND"}, - {STATUS_CTX_SHADOW_INVALID, -EIO, "STATUS_CTX_SHADOW_INVALID"}, - {STATUS_CTX_SHADOW_DISABLED, -EIO, "STATUS_CTX_SHADOW_DISABLED"}, - {STATUS_RDP_PROTOCOL_ERROR, -EIO, "STATUS_RDP_PROTOCOL_ERROR"}, - {STATUS_CTX_CLIENT_LICENSE_NOT_SET, -EIO, - "STATUS_CTX_CLIENT_LICENSE_NOT_SET"}, - {STATUS_CTX_CLIENT_LICENSE_IN_USE, -EIO, - "STATUS_CTX_CLIENT_LICENSE_IN_USE"}, - {STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, -EIO, - "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE"}, - {STATUS_CTX_SHADOW_NOT_RUNNING, -EIO, "STATUS_CTX_SHADOW_NOT_RUNNING"}, - {STATUS_CTX_LOGON_DISABLED, -EIO, "STATUS_CTX_LOGON_DISABLED"}, - {STATUS_CTX_SECURITY_LAYER_ERROR, -EIO, - "STATUS_CTX_SECURITY_LAYER_ERROR"}, - {STATUS_TS_INCOMPATIBLE_SESSIONS, -EIO, - "STATUS_TS_INCOMPATIBLE_SESSIONS"}, - {STATUS_MUI_FILE_NOT_FOUND, -EIO, "STATUS_MUI_FILE_NOT_FOUND"}, - {STATUS_MUI_INVALID_FILE, -EIO, "STATUS_MUI_INVALID_FILE"}, - {STATUS_MUI_INVALID_RC_CONFIG, -EIO, "STATUS_MUI_INVALID_RC_CONFIG"}, - {STATUS_MUI_INVALID_LOCALE_NAME, -EIO, - "STATUS_MUI_INVALID_LOCALE_NAME"}, - {STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, -EIO, - "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME"}, - {STATUS_MUI_FILE_NOT_LOADED, -EIO, "STATUS_MUI_FILE_NOT_LOADED"}, - {STATUS_RESOURCE_ENUM_USER_STOP, -EIO, - "STATUS_RESOURCE_ENUM_USER_STOP"}, - {STATUS_CLUSTER_INVALID_NODE, -EIO, "STATUS_CLUSTER_INVALID_NODE"}, - {STATUS_CLUSTER_NODE_EXISTS, -EIO, "STATUS_CLUSTER_NODE_EXISTS"}, - {STATUS_CLUSTER_JOIN_IN_PROGRESS, -EIO, - "STATUS_CLUSTER_JOIN_IN_PROGRESS"}, - {STATUS_CLUSTER_NODE_NOT_FOUND, -EIO, "STATUS_CLUSTER_NODE_NOT_FOUND"}, - {STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, -EIO, - "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND"}, - {STATUS_CLUSTER_NETWORK_EXISTS, -EIO, "STATUS_CLUSTER_NETWORK_EXISTS"}, - {STATUS_CLUSTER_NETWORK_NOT_FOUND, -EIO, - "STATUS_CLUSTER_NETWORK_NOT_FOUND"}, - {STATUS_CLUSTER_NETINTERFACE_EXISTS, -EIO, - "STATUS_CLUSTER_NETINTERFACE_EXISTS"}, - {STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, -EIO, - "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND"}, - {STATUS_CLUSTER_INVALID_REQUEST, -EIO, - "STATUS_CLUSTER_INVALID_REQUEST"}, - {STATUS_CLUSTER_INVALID_NETWORK_PROVIDER, -EIO, - "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER"}, - {STATUS_CLUSTER_NODE_DOWN, -EIO, "STATUS_CLUSTER_NODE_DOWN"}, - {STATUS_CLUSTER_NODE_UNREACHABLE, -EIO, - "STATUS_CLUSTER_NODE_UNREACHABLE"}, - {STATUS_CLUSTER_NODE_NOT_MEMBER, -EIO, - "STATUS_CLUSTER_NODE_NOT_MEMBER"}, - {STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, -EIO, - "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS"}, - {STATUS_CLUSTER_INVALID_NETWORK, -EIO, - "STATUS_CLUSTER_INVALID_NETWORK"}, - {STATUS_CLUSTER_NO_NET_ADAPTERS, -EIO, - "STATUS_CLUSTER_NO_NET_ADAPTERS"}, - {STATUS_CLUSTER_NODE_UP, -EIO, "STATUS_CLUSTER_NODE_UP"}, - {STATUS_CLUSTER_NODE_PAUSED, -EIO, "STATUS_CLUSTER_NODE_PAUSED"}, - {STATUS_CLUSTER_NODE_NOT_PAUSED, -EIO, - "STATUS_CLUSTER_NODE_NOT_PAUSED"}, - {STATUS_CLUSTER_NO_SECURITY_CONTEXT, -EIO, - "STATUS_CLUSTER_NO_SECURITY_CONTEXT"}, - {STATUS_CLUSTER_NETWORK_NOT_INTERNAL, -EIO, - "STATUS_CLUSTER_NETWORK_NOT_INTERNAL"}, - {STATUS_CLUSTER_POISONED, -EIO, "STATUS_CLUSTER_POISONED"}, - {STATUS_ACPI_INVALID_OPCODE, -EIO, "STATUS_ACPI_INVALID_OPCODE"}, - {STATUS_ACPI_STACK_OVERFLOW, -EIO, "STATUS_ACPI_STACK_OVERFLOW"}, - {STATUS_ACPI_ASSERT_FAILED, -EIO, "STATUS_ACPI_ASSERT_FAILED"}, - {STATUS_ACPI_INVALID_INDEX, -EIO, "STATUS_ACPI_INVALID_INDEX"}, - {STATUS_ACPI_INVALID_ARGUMENT, -EIO, "STATUS_ACPI_INVALID_ARGUMENT"}, - {STATUS_ACPI_FATAL, -EIO, "STATUS_ACPI_FATAL"}, - {STATUS_ACPI_INVALID_SUPERNAME, -EIO, "STATUS_ACPI_INVALID_SUPERNAME"}, - {STATUS_ACPI_INVALID_ARGTYPE, -EIO, "STATUS_ACPI_INVALID_ARGTYPE"}, - {STATUS_ACPI_INVALID_OBJTYPE, -EIO, "STATUS_ACPI_INVALID_OBJTYPE"}, - {STATUS_ACPI_INVALID_TARGETTYPE, -EIO, - "STATUS_ACPI_INVALID_TARGETTYPE"}, - {STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, -EIO, - "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT"}, - {STATUS_ACPI_ADDRESS_NOT_MAPPED, -EIO, - "STATUS_ACPI_ADDRESS_NOT_MAPPED"}, - {STATUS_ACPI_INVALID_EVENTTYPE, -EIO, "STATUS_ACPI_INVALID_EVENTTYPE"}, - {STATUS_ACPI_HANDLER_COLLISION, -EIO, "STATUS_ACPI_HANDLER_COLLISION"}, - {STATUS_ACPI_INVALID_DATA, -EIO, "STATUS_ACPI_INVALID_DATA"}, - {STATUS_ACPI_INVALID_REGION, -EIO, "STATUS_ACPI_INVALID_REGION"}, - {STATUS_ACPI_INVALID_ACCESS_SIZE, -EIO, - "STATUS_ACPI_INVALID_ACCESS_SIZE"}, - {STATUS_ACPI_ACQUIRE_GLOBAL_LOCK, -EIO, - "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK"}, - {STATUS_ACPI_ALREADY_INITIALIZED, -EIO, - "STATUS_ACPI_ALREADY_INITIALIZED"}, - {STATUS_ACPI_NOT_INITIALIZED, -EIO, "STATUS_ACPI_NOT_INITIALIZED"}, - {STATUS_ACPI_INVALID_MUTEX_LEVEL, -EIO, - "STATUS_ACPI_INVALID_MUTEX_LEVEL"}, - {STATUS_ACPI_MUTEX_NOT_OWNED, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNED"}, - {STATUS_ACPI_MUTEX_NOT_OWNER, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNER"}, - {STATUS_ACPI_RS_ACCESS, -EIO, "STATUS_ACPI_RS_ACCESS"}, - {STATUS_ACPI_INVALID_TABLE, -EIO, "STATUS_ACPI_INVALID_TABLE"}, - {STATUS_ACPI_REG_HANDLER_FAILED, -EIO, - "STATUS_ACPI_REG_HANDLER_FAILED"}, - {STATUS_ACPI_POWER_REQUEST_FAILED, -EIO, - "STATUS_ACPI_POWER_REQUEST_FAILED"}, - {STATUS_SXS_SECTION_NOT_FOUND, -EIO, "STATUS_SXS_SECTION_NOT_FOUND"}, - {STATUS_SXS_CANT_GEN_ACTCTX, -EIO, "STATUS_SXS_CANT_GEN_ACTCTX"}, - {STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, -EIO, - "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT"}, - {STATUS_SXS_ASSEMBLY_NOT_FOUND, -EIO, "STATUS_SXS_ASSEMBLY_NOT_FOUND"}, - {STATUS_SXS_MANIFEST_FORMAT_ERROR, -EIO, - "STATUS_SXS_MANIFEST_FORMAT_ERROR"}, - {STATUS_SXS_MANIFEST_PARSE_ERROR, -EIO, - "STATUS_SXS_MANIFEST_PARSE_ERROR"}, - {STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, -EIO, - "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED"}, - {STATUS_SXS_KEY_NOT_FOUND, -EIO, "STATUS_SXS_KEY_NOT_FOUND"}, - {STATUS_SXS_VERSION_CONFLICT, -EIO, "STATUS_SXS_VERSION_CONFLICT"}, - {STATUS_SXS_WRONG_SECTION_TYPE, -EIO, "STATUS_SXS_WRONG_SECTION_TYPE"}, - {STATUS_SXS_THREAD_QUERIES_DISABLED, -EIO, - "STATUS_SXS_THREAD_QUERIES_DISABLED"}, - {STATUS_SXS_ASSEMBLY_MISSING, -EIO, "STATUS_SXS_ASSEMBLY_MISSING"}, - {STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, -EIO, - "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET"}, - {STATUS_SXS_EARLY_DEACTIVATION, -EIO, "STATUS_SXS_EARLY_DEACTIVATION"}, - {STATUS_SXS_INVALID_DEACTIVATION, -EIO, - "STATUS_SXS_INVALID_DEACTIVATION"}, - {STATUS_SXS_MULTIPLE_DEACTIVATION, -EIO, - "STATUS_SXS_MULTIPLE_DEACTIVATION"}, - {STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY, -EIO, - "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY"}, - {STATUS_SXS_PROCESS_TERMINATION_REQUESTED, -EIO, - "STATUS_SXS_PROCESS_TERMINATION_REQUESTED"}, - {STATUS_SXS_CORRUPT_ACTIVATION_STACK, -EIO, - "STATUS_SXS_CORRUPT_ACTIVATION_STACK"}, - {STATUS_SXS_CORRUPTION, -EIO, "STATUS_SXS_CORRUPTION"}, - {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE, -EIO, - "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE"}, - {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME, -EIO, - "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME"}, - {STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE, -EIO, - "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE"}, - {STATUS_SXS_IDENTITY_PARSE_ERROR, -EIO, - "STATUS_SXS_IDENTITY_PARSE_ERROR"}, - {STATUS_SXS_COMPONENT_STORE_CORRUPT, -EIO, - "STATUS_SXS_COMPONENT_STORE_CORRUPT"}, - {STATUS_SXS_FILE_HASH_MISMATCH, -EIO, "STATUS_SXS_FILE_HASH_MISMATCH"}, - {STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT, -EIO, - "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT"}, - {STATUS_SXS_IDENTITIES_DIFFERENT, -EIO, - "STATUS_SXS_IDENTITIES_DIFFERENT"}, - {STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT, -EIO, - "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT"}, - {STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY, -EIO, - "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY"}, - {STATUS_ADVANCED_INSTALLER_FAILED, -EIO, - "STATUS_ADVANCED_INSTALLER_FAILED"}, - {STATUS_XML_ENCODING_MISMATCH, -EIO, "STATUS_XML_ENCODING_MISMATCH"}, - {STATUS_SXS_MANIFEST_TOO_BIG, -EIO, "STATUS_SXS_MANIFEST_TOO_BIG"}, - {STATUS_SXS_SETTING_NOT_REGISTERED, -EIO, - "STATUS_SXS_SETTING_NOT_REGISTERED"}, - {STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE, -EIO, - "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE"}, - {STATUS_SMI_PRIMITIVE_INSTALLER_FAILED, -EIO, - "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED"}, - {STATUS_GENERIC_COMMAND_FAILED, -EIO, "STATUS_GENERIC_COMMAND_FAILED"}, - {STATUS_SXS_FILE_HASH_MISSING, -EIO, "STATUS_SXS_FILE_HASH_MISSING"}, - {STATUS_TRANSACTIONAL_CONFLICT, -EIO, "STATUS_TRANSACTIONAL_CONFLICT"}, - {STATUS_INVALID_TRANSACTION, -EIO, "STATUS_INVALID_TRANSACTION"}, - {STATUS_TRANSACTION_NOT_ACTIVE, -EIO, "STATUS_TRANSACTION_NOT_ACTIVE"}, - {STATUS_TM_INITIALIZATION_FAILED, -EIO, - "STATUS_TM_INITIALIZATION_FAILED"}, - {STATUS_RM_NOT_ACTIVE, -EIO, "STATUS_RM_NOT_ACTIVE"}, - {STATUS_RM_METADATA_CORRUPT, -EIO, "STATUS_RM_METADATA_CORRUPT"}, - {STATUS_TRANSACTION_NOT_JOINED, -EIO, "STATUS_TRANSACTION_NOT_JOINED"}, - {STATUS_DIRECTORY_NOT_RM, -EIO, "STATUS_DIRECTORY_NOT_RM"}, - {STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE, -EIO, - "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE"}, - {STATUS_LOG_RESIZE_INVALID_SIZE, -EIO, - "STATUS_LOG_RESIZE_INVALID_SIZE"}, - {STATUS_REMOTE_FILE_VERSION_MISMATCH, -EIO, - "STATUS_REMOTE_FILE_VERSION_MISMATCH"}, - {STATUS_CRM_PROTOCOL_ALREADY_EXISTS, -EIO, - "STATUS_CRM_PROTOCOL_ALREADY_EXISTS"}, - {STATUS_TRANSACTION_PROPAGATION_FAILED, -EIO, - "STATUS_TRANSACTION_PROPAGATION_FAILED"}, - {STATUS_CRM_PROTOCOL_NOT_FOUND, -EIO, "STATUS_CRM_PROTOCOL_NOT_FOUND"}, - {STATUS_TRANSACTION_SUPERIOR_EXISTS, -EIO, - "STATUS_TRANSACTION_SUPERIOR_EXISTS"}, - {STATUS_TRANSACTION_REQUEST_NOT_VALID, -EIO, - "STATUS_TRANSACTION_REQUEST_NOT_VALID"}, - {STATUS_TRANSACTION_NOT_REQUESTED, -EIO, - "STATUS_TRANSACTION_NOT_REQUESTED"}, - {STATUS_TRANSACTION_ALREADY_ABORTED, -EIO, - "STATUS_TRANSACTION_ALREADY_ABORTED"}, - {STATUS_TRANSACTION_ALREADY_COMMITTED, -EIO, - "STATUS_TRANSACTION_ALREADY_COMMITTED"}, - {STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER, -EIO, - "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER"}, - {STATUS_CURRENT_TRANSACTION_NOT_VALID, -EIO, - "STATUS_CURRENT_TRANSACTION_NOT_VALID"}, - {STATUS_LOG_GROWTH_FAILED, -EIO, "STATUS_LOG_GROWTH_FAILED"}, - {STATUS_OBJECT_NO_LONGER_EXISTS, -EIO, - "STATUS_OBJECT_NO_LONGER_EXISTS"}, - {STATUS_STREAM_MINIVERSION_NOT_FOUND, -EIO, - "STATUS_STREAM_MINIVERSION_NOT_FOUND"}, - {STATUS_STREAM_MINIVERSION_NOT_VALID, -EIO, - "STATUS_STREAM_MINIVERSION_NOT_VALID"}, - {STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION, -EIO, - "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION"}, - {STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT, -EIO, - "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT"}, - {STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS, -EIO, - "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS"}, - {STATUS_HANDLE_NO_LONGER_VALID, -EIO, "STATUS_HANDLE_NO_LONGER_VALID"}, - {STATUS_LOG_CORRUPTION_DETECTED, -EIO, - "STATUS_LOG_CORRUPTION_DETECTED"}, - {STATUS_RM_DISCONNECTED, -EIO, "STATUS_RM_DISCONNECTED"}, - {STATUS_ENLISTMENT_NOT_SUPERIOR, -EIO, - "STATUS_ENLISTMENT_NOT_SUPERIOR"}, - {STATUS_FILE_IDENTITY_NOT_PERSISTENT, -EIO, - "STATUS_FILE_IDENTITY_NOT_PERSISTENT"}, - {STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY, -EIO, - "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY"}, - {STATUS_CANT_CROSS_RM_BOUNDARY, -EIO, "STATUS_CANT_CROSS_RM_BOUNDARY"}, - {STATUS_TXF_DIR_NOT_EMPTY, -EIO, "STATUS_TXF_DIR_NOT_EMPTY"}, - {STATUS_INDOUBT_TRANSACTIONS_EXIST, -EIO, - "STATUS_INDOUBT_TRANSACTIONS_EXIST"}, - {STATUS_TM_VOLATILE, -EIO, "STATUS_TM_VOLATILE"}, - {STATUS_ROLLBACK_TIMER_EXPIRED, -EIO, "STATUS_ROLLBACK_TIMER_EXPIRED"}, - {STATUS_TXF_ATTRIBUTE_CORRUPT, -EIO, "STATUS_TXF_ATTRIBUTE_CORRUPT"}, - {STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED, -EIO, - "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED"}, - {STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE, -EIO, - "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE"}, - {STATUS_TRANSACTION_REQUIRED_PROMOTION, -EIO, - "STATUS_TRANSACTION_REQUIRED_PROMOTION"}, - {STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION, -EIO, - "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION"}, - {STATUS_TRANSACTIONS_NOT_FROZEN, -EIO, - "STATUS_TRANSACTIONS_NOT_FROZEN"}, - {STATUS_TRANSACTION_FREEZE_IN_PROGRESS, -EIO, - "STATUS_TRANSACTION_FREEZE_IN_PROGRESS"}, - {STATUS_NOT_SNAPSHOT_VOLUME, -EIO, "STATUS_NOT_SNAPSHOT_VOLUME"}, - {STATUS_NO_SAVEPOINT_WITH_OPEN_FILES, -EIO, - "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES"}, - {STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TM_IDENTITY_MISMATCH, -EIO, "STATUS_TM_IDENTITY_MISMATCH"}, - {STATUS_FLOATED_SECTION, -EIO, "STATUS_FLOATED_SECTION"}, - {STATUS_CANNOT_ACCEPT_TRANSACTED_WORK, -EIO, - "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK"}, - {STATUS_CANNOT_ABORT_TRANSACTIONS, -EIO, - "STATUS_CANNOT_ABORT_TRANSACTIONS"}, - {STATUS_TRANSACTION_NOT_FOUND, -EIO, "STATUS_TRANSACTION_NOT_FOUND"}, - {STATUS_RESOURCEMANAGER_NOT_FOUND, -EIO, - "STATUS_RESOURCEMANAGER_NOT_FOUND"}, - {STATUS_ENLISTMENT_NOT_FOUND, -EIO, "STATUS_ENLISTMENT_NOT_FOUND"}, - {STATUS_TRANSACTIONMANAGER_NOT_FOUND, -EIO, - "STATUS_TRANSACTIONMANAGER_NOT_FOUND"}, - {STATUS_TRANSACTIONMANAGER_NOT_ONLINE, -EIO, - "STATUS_TRANSACTIONMANAGER_NOT_ONLINE"}, - {STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION, -EIO, - "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION"}, - {STATUS_TRANSACTION_NOT_ROOT, -EIO, "STATUS_TRANSACTION_NOT_ROOT"}, - {STATUS_TRANSACTION_OBJECT_EXPIRED, -EIO, - "STATUS_TRANSACTION_OBJECT_EXPIRED"}, - {STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED, -EIO, - "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED"}, - {STATUS_TRANSACTION_RECORD_TOO_LONG, -EIO, - "STATUS_TRANSACTION_RECORD_TOO_LONG"}, - {STATUS_NO_LINK_TRACKING_IN_TRANSACTION, -EIO, - "STATUS_NO_LINK_TRACKING_IN_TRANSACTION"}, - {STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION, -EOPNOTSUPP, - "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION"}, - {STATUS_TRANSACTION_INTEGRITY_VIOLATED, -EIO, - "STATUS_TRANSACTION_INTEGRITY_VIOLATED"}, - {STATUS_LOG_SECTOR_INVALID, -EIO, "STATUS_LOG_SECTOR_INVALID"}, - {STATUS_LOG_SECTOR_PARITY_INVALID, -EIO, - "STATUS_LOG_SECTOR_PARITY_INVALID"}, - {STATUS_LOG_SECTOR_REMAPPED, -EIO, "STATUS_LOG_SECTOR_REMAPPED"}, - {STATUS_LOG_BLOCK_INCOMPLETE, -EIO, "STATUS_LOG_BLOCK_INCOMPLETE"}, - {STATUS_LOG_INVALID_RANGE, -EIO, "STATUS_LOG_INVALID_RANGE"}, - {STATUS_LOG_BLOCKS_EXHAUSTED, -EIO, "STATUS_LOG_BLOCKS_EXHAUSTED"}, - {STATUS_LOG_READ_CONTEXT_INVALID, -EIO, - "STATUS_LOG_READ_CONTEXT_INVALID"}, - {STATUS_LOG_RESTART_INVALID, -EIO, "STATUS_LOG_RESTART_INVALID"}, - {STATUS_LOG_BLOCK_VERSION, -EIO, "STATUS_LOG_BLOCK_VERSION"}, - {STATUS_LOG_BLOCK_INVALID, -EIO, "STATUS_LOG_BLOCK_INVALID"}, - {STATUS_LOG_READ_MODE_INVALID, -EIO, "STATUS_LOG_READ_MODE_INVALID"}, - {STATUS_LOG_METADATA_CORRUPT, -EIO, "STATUS_LOG_METADATA_CORRUPT"}, - {STATUS_LOG_METADATA_INVALID, -EIO, "STATUS_LOG_METADATA_INVALID"}, - {STATUS_LOG_METADATA_INCONSISTENT, -EIO, - "STATUS_LOG_METADATA_INCONSISTENT"}, - {STATUS_LOG_RESERVATION_INVALID, -EIO, - "STATUS_LOG_RESERVATION_INVALID"}, - {STATUS_LOG_CANT_DELETE, -EIO, "STATUS_LOG_CANT_DELETE"}, - {STATUS_LOG_CONTAINER_LIMIT_EXCEEDED, -EIO, - "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED"}, - {STATUS_LOG_START_OF_LOG, -EIO, "STATUS_LOG_START_OF_LOG"}, - {STATUS_LOG_POLICY_ALREADY_INSTALLED, -EIO, - "STATUS_LOG_POLICY_ALREADY_INSTALLED"}, - {STATUS_LOG_POLICY_NOT_INSTALLED, -EIO, - "STATUS_LOG_POLICY_NOT_INSTALLED"}, - {STATUS_LOG_POLICY_INVALID, -EIO, "STATUS_LOG_POLICY_INVALID"}, - {STATUS_LOG_POLICY_CONFLICT, -EIO, "STATUS_LOG_POLICY_CONFLICT"}, - {STATUS_LOG_PINNED_ARCHIVE_TAIL, -EIO, - "STATUS_LOG_PINNED_ARCHIVE_TAIL"}, - {STATUS_LOG_RECORD_NONEXISTENT, -EIO, "STATUS_LOG_RECORD_NONEXISTENT"}, - {STATUS_LOG_RECORDS_RESERVED_INVALID, -EIO, - "STATUS_LOG_RECORDS_RESERVED_INVALID"}, - {STATUS_LOG_SPACE_RESERVED_INVALID, -EIO, - "STATUS_LOG_SPACE_RESERVED_INVALID"}, - {STATUS_LOG_TAIL_INVALID, -EIO, "STATUS_LOG_TAIL_INVALID"}, - {STATUS_LOG_FULL, -EIO, "STATUS_LOG_FULL"}, - {STATUS_LOG_MULTIPLEXED, -EIO, "STATUS_LOG_MULTIPLEXED"}, - {STATUS_LOG_DEDICATED, -EIO, "STATUS_LOG_DEDICATED"}, - {STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS, -EIO, - "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS"}, - {STATUS_LOG_ARCHIVE_IN_PROGRESS, -EIO, - "STATUS_LOG_ARCHIVE_IN_PROGRESS"}, - {STATUS_LOG_EPHEMERAL, -EIO, "STATUS_LOG_EPHEMERAL"}, - {STATUS_LOG_NOT_ENOUGH_CONTAINERS, -EIO, - "STATUS_LOG_NOT_ENOUGH_CONTAINERS"}, - {STATUS_LOG_CLIENT_ALREADY_REGISTERED, -EIO, - "STATUS_LOG_CLIENT_ALREADY_REGISTERED"}, - {STATUS_LOG_CLIENT_NOT_REGISTERED, -EIO, - "STATUS_LOG_CLIENT_NOT_REGISTERED"}, - {STATUS_LOG_FULL_HANDLER_IN_PROGRESS, -EIO, - "STATUS_LOG_FULL_HANDLER_IN_PROGRESS"}, - {STATUS_LOG_CONTAINER_READ_FAILED, -EIO, - "STATUS_LOG_CONTAINER_READ_FAILED"}, - {STATUS_LOG_CONTAINER_WRITE_FAILED, -EIO, - "STATUS_LOG_CONTAINER_WRITE_FAILED"}, - {STATUS_LOG_CONTAINER_OPEN_FAILED, -EIO, - "STATUS_LOG_CONTAINER_OPEN_FAILED"}, - {STATUS_LOG_CONTAINER_STATE_INVALID, -EIO, - "STATUS_LOG_CONTAINER_STATE_INVALID"}, - {STATUS_LOG_STATE_INVALID, -EIO, "STATUS_LOG_STATE_INVALID"}, - {STATUS_LOG_PINNED, -EIO, "STATUS_LOG_PINNED"}, - {STATUS_LOG_METADATA_FLUSH_FAILED, -EIO, - "STATUS_LOG_METADATA_FLUSH_FAILED"}, - {STATUS_LOG_INCONSISTENT_SECURITY, -EIO, - "STATUS_LOG_INCONSISTENT_SECURITY"}, - {STATUS_LOG_APPENDED_FLUSH_FAILED, -EIO, - "STATUS_LOG_APPENDED_FLUSH_FAILED"}, - {STATUS_LOG_PINNED_RESERVATION, -EIO, "STATUS_LOG_PINNED_RESERVATION"}, - {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD, -EIO, - "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD"}, - {STATUS_FLT_NO_HANDLER_DEFINED, -EIO, "STATUS_FLT_NO_HANDLER_DEFINED"}, - {STATUS_FLT_CONTEXT_ALREADY_DEFINED, -EIO, - "STATUS_FLT_CONTEXT_ALREADY_DEFINED"}, - {STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST, -EIO, - "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST"}, - {STATUS_FLT_DISALLOW_FAST_IO, -EIO, "STATUS_FLT_DISALLOW_FAST_IO"}, - {STATUS_FLT_INVALID_NAME_REQUEST, -EIO, - "STATUS_FLT_INVALID_NAME_REQUEST"}, - {STATUS_FLT_NOT_SAFE_TO_POST_OPERATION, -EIO, - "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION"}, - {STATUS_FLT_NOT_INITIALIZED, -EIO, "STATUS_FLT_NOT_INITIALIZED"}, - {STATUS_FLT_FILTER_NOT_READY, -EIO, "STATUS_FLT_FILTER_NOT_READY"}, - {STATUS_FLT_POST_OPERATION_CLEANUP, -EIO, - "STATUS_FLT_POST_OPERATION_CLEANUP"}, - {STATUS_FLT_INTERNAL_ERROR, -EIO, "STATUS_FLT_INTERNAL_ERROR"}, - {STATUS_FLT_DELETING_OBJECT, -EIO, "STATUS_FLT_DELETING_OBJECT"}, - {STATUS_FLT_MUST_BE_NONPAGED_POOL, -EIO, - "STATUS_FLT_MUST_BE_NONPAGED_POOL"}, - {STATUS_FLT_DUPLICATE_ENTRY, -EIO, "STATUS_FLT_DUPLICATE_ENTRY"}, - {STATUS_FLT_CBDQ_DISABLED, -EIO, "STATUS_FLT_CBDQ_DISABLED"}, - {STATUS_FLT_DO_NOT_ATTACH, -EIO, "STATUS_FLT_DO_NOT_ATTACH"}, - {STATUS_FLT_DO_NOT_DETACH, -EIO, "STATUS_FLT_DO_NOT_DETACH"}, - {STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, -EIO, - "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION"}, - {STATUS_FLT_INSTANCE_NAME_COLLISION, -EIO, - "STATUS_FLT_INSTANCE_NAME_COLLISION"}, - {STATUS_FLT_FILTER_NOT_FOUND, -EIO, "STATUS_FLT_FILTER_NOT_FOUND"}, - {STATUS_FLT_VOLUME_NOT_FOUND, -EIO, "STATUS_FLT_VOLUME_NOT_FOUND"}, - {STATUS_FLT_INSTANCE_NOT_FOUND, -EIO, "STATUS_FLT_INSTANCE_NOT_FOUND"}, - {STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND, -EIO, - "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND"}, - {STATUS_FLT_INVALID_CONTEXT_REGISTRATION, -EIO, - "STATUS_FLT_INVALID_CONTEXT_REGISTRATION"}, - {STATUS_FLT_NAME_CACHE_MISS, -EIO, "STATUS_FLT_NAME_CACHE_MISS"}, - {STATUS_FLT_NO_DEVICE_OBJECT, -EIO, "STATUS_FLT_NO_DEVICE_OBJECT"}, - {STATUS_FLT_VOLUME_ALREADY_MOUNTED, -EIO, - "STATUS_FLT_VOLUME_ALREADY_MOUNTED"}, - {STATUS_FLT_ALREADY_ENLISTED, -EIO, "STATUS_FLT_ALREADY_ENLISTED"}, - {STATUS_FLT_CONTEXT_ALREADY_LINKED, -EIO, - "STATUS_FLT_CONTEXT_ALREADY_LINKED"}, - {STATUS_FLT_NO_WAITER_FOR_REPLY, -EIO, - "STATUS_FLT_NO_WAITER_FOR_REPLY"}, - {STATUS_MONITOR_NO_DESCRIPTOR, -EIO, "STATUS_MONITOR_NO_DESCRIPTOR"}, - {STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT, -EIO, - "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT"}, - {STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM, -EIO, - "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM"}, - {STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK"}, - {STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED, -EIO, - "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED"}, - {STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK"}, - {STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK"}, - {STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA, -EIO, - "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA"}, - {STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK"}, - {STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER, -EIO, - "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER"}, - {STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, -EIO, - "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER"}, - {STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER, -EIO, - "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER"}, - {STATUS_GRAPHICS_ADAPTER_WAS_RESET, -EIO, - "STATUS_GRAPHICS_ADAPTER_WAS_RESET"}, - {STATUS_GRAPHICS_INVALID_DRIVER_MODEL, -EIO, - "STATUS_GRAPHICS_INVALID_DRIVER_MODEL"}, - {STATUS_GRAPHICS_PRESENT_MODE_CHANGED, -EIO, - "STATUS_GRAPHICS_PRESENT_MODE_CHANGED"}, - {STATUS_GRAPHICS_PRESENT_OCCLUDED, -EIO, - "STATUS_GRAPHICS_PRESENT_OCCLUDED"}, - {STATUS_GRAPHICS_PRESENT_DENIED, -EIO, - "STATUS_GRAPHICS_PRESENT_DENIED"}, - {STATUS_GRAPHICS_CANNOTCOLORCONVERT, -EIO, - "STATUS_GRAPHICS_CANNOTCOLORCONVERT"}, - {STATUS_GRAPHICS_NO_VIDEO_MEMORY, -EIO, - "STATUS_GRAPHICS_NO_VIDEO_MEMORY"}, - {STATUS_GRAPHICS_CANT_LOCK_MEMORY, -EIO, - "STATUS_GRAPHICS_CANT_LOCK_MEMORY"}, - {STATUS_GRAPHICS_ALLOCATION_BUSY, -EBUSY, - "STATUS_GRAPHICS_ALLOCATION_BUSY"}, - {STATUS_GRAPHICS_TOO_MANY_REFERENCES, -EIO, - "STATUS_GRAPHICS_TOO_MANY_REFERENCES"}, - {STATUS_GRAPHICS_TRY_AGAIN_LATER, -EIO, - "STATUS_GRAPHICS_TRY_AGAIN_LATER"}, - {STATUS_GRAPHICS_TRY_AGAIN_NOW, -EIO, "STATUS_GRAPHICS_TRY_AGAIN_NOW"}, - {STATUS_GRAPHICS_ALLOCATION_INVALID, -EIO, - "STATUS_GRAPHICS_ALLOCATION_INVALID"}, - {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE, -EIO, - "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE"}, - {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED, -EIO, - "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED"}, - {STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION, -EIO, - "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE"}, - {STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION, -EIO, - "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION"}, - {STATUS_GRAPHICS_ALLOCATION_CLOSED, -EIO, - "STATUS_GRAPHICS_ALLOCATION_CLOSED"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE"}, - {STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE, -EIO, - "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE"}, - {STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST, -EIO, - "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST"}, - {STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE, -EIO, - "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_VIDPN, -EIO, "STATUS_GRAPHICS_INVALID_VIDPN"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET"}, - {STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET"}, - {STATUS_GRAPHICS_INVALID_FREQUENCY, -EIO, - "STATUS_GRAPHICS_INVALID_FREQUENCY"}, - {STATUS_GRAPHICS_INVALID_ACTIVE_REGION, -EIO, - "STATUS_GRAPHICS_INVALID_ACTIVE_REGION"}, - {STATUS_GRAPHICS_INVALID_TOTAL_REGION, -EIO, - "STATUS_GRAPHICS_INVALID_TOTAL_REGION"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE"}, - {STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET, -EIO, - "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET"}, - {STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET, -EIO, - "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET"}, - {STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET"}, - {STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET"}, - {STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_TARGET_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH"}, - {STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE"}, - {STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET, -EIO, - "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET"}, - {STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_STALE_MODESET, -EIO, "STATUS_GRAPHICS_STALE_MODESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE"}, - {STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN, -EIO, - "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN"}, - {STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION, -EIO, - "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION"}, - {STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES, -EIO, - "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES"}, - {STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE, -EIO, - "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE"}, - {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET, -EIO, - "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET"}, - {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET"}, - {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR, -EIO, - "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE"}, - {STATUS_GRAPHICS_RESOURCES_NOT_RELATED, -EIO, - "STATUS_GRAPHICS_RESOURCES_NOT_RELATED"}, - {STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET, -EIO, - "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET"}, - {STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER, -EIO, - "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER"}, - {STATUS_GRAPHICS_NO_VIDPNMGR, -EIO, "STATUS_GRAPHICS_NO_VIDPNMGR"}, - {STATUS_GRAPHICS_NO_ACTIVE_VIDPN, -EIO, - "STATUS_GRAPHICS_NO_ACTIVE_VIDPN"}, - {STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_MONITOR_NOT_CONNECTED, -EIO, - "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED"}, - {STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE, -EIO, - "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE"}, - {STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE, -EIO, - "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE"}, - {STATUS_GRAPHICS_INVALID_STRIDE, -EIO, - "STATUS_GRAPHICS_INVALID_STRIDE"}, - {STATUS_GRAPHICS_INVALID_PIXELFORMAT, -EIO, - "STATUS_GRAPHICS_INVALID_PIXELFORMAT"}, - {STATUS_GRAPHICS_INVALID_COLORBASIS, -EIO, - "STATUS_GRAPHICS_INVALID_COLORBASIS"}, - {STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE, -EIO, - "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE"}, - {STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT, -EIO, - "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT"}, - {STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE, -EIO, - "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE"}, - {STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN, -EIO, - "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN"}, - {STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL"}, - {STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION"}, - {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED, - -EIO, - "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_GAMMA_RAMP, -EIO, - "STATUS_GRAPHICS_INVALID_GAMMA_RAMP"}, - {STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_MODE_NOT_IN_MODESET, -EIO, - "STATUS_GRAPHICS_MODE_NOT_IN_MODESET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON"}, - {STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE"}, - {STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE"}, - {STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS, -EIO, - "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS"}, - {STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING, -EIO, - "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING"}, - {STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED, -EIO, - "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED"}, - {STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS, -EIO, - "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS"}, - {STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT, -EIO, - "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT"}, - {STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM, -EIO, - "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM"}, - {STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT"}, - {STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED, -EIO, - "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED"}, - {STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION, -EIO, - "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION"}, - {STATUS_GRAPHICS_INVALID_CLIENT_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_CLIENT_TYPE"}, - {STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET, -EIO, - "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET"}, - {STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED, -EIO, - "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED"}, - {STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER, -EIO, - "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER"}, - {STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED, -EIO, - "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED"}, - {STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY, -EIO, - "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON"}, - {STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE, -EIO, - "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE"}, - {STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER, -EIO, - "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER"}, - {STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED, -EIO, - "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS, - -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS"}, - {STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST"}, - {STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS, -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS"}, - {STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST"}, - {STATUS_GRAPHICS_OPM_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_OPM_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_COPP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_COPP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_UAB_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_UAB_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS"}, - {STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL, -EIO, - "STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL"}, - {STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST, -EIO, - "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST"}, - {STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, - "STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, - {STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, - "STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, - {STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_POINTER, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_POINTER"}, - {STATUS_GRAPHICS_OPM_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_OPM_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_OPM_INVALID_HANDLE, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_HANDLE"}, - {STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, - "STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, - {STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH, -EIO, - "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH"}, - {STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED, -EIO, - "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED"}, - {STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED, -EIO, - "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED"}, - {STATUS_GRAPHICS_PVP_HFS_FAILED, -EIO, - "STATUS_GRAPHICS_PVP_HFS_FAILED"}, - {STATUS_GRAPHICS_OPM_INVALID_SRM, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_SRM"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA"}, - {STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET, -EIO, - "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET"}, - {STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH, -EIO, - "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH"}, - {STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE, -EIO, - "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS, -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS"}, - {STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, - "STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS"}, - {STATUS_GRAPHICS_I2C_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_I2C_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST, -EIO, - "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST"}, - {STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA, -EIO, - "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA"}, - {STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA, -EIO, - "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA"}, - {STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_DDCCI_INVALID_DATA, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_DATA"}, - {STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE, - -EIO, - "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE"}, - {STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING"}, - {STATUS_GRAPHICS_MCA_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_MCA_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM"}, - {STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE, -EIO, - "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE"}, - {STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS, -EIO, - "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS"}, - {STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED, -EIO, - "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED"}, - {STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, - "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, - {STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, - "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, - {STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_POINTER, -EIO, - "STATUS_GRAPHICS_INVALID_POINTER"}, - {STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, - "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, - {STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL, -EIO, - "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL"}, - {STATUS_GRAPHICS_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, - "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS"}, - {STATUS_FVE_LOCKED_VOLUME, -EIO, "STATUS_FVE_LOCKED_VOLUME"}, - {STATUS_FVE_NOT_ENCRYPTED, -EIO, "STATUS_FVE_NOT_ENCRYPTED"}, - {STATUS_FVE_BAD_INFORMATION, -EIO, "STATUS_FVE_BAD_INFORMATION"}, - {STATUS_FVE_TOO_SMALL, -EIO, "STATUS_FVE_TOO_SMALL"}, - {STATUS_FVE_FAILED_WRONG_FS, -EIO, "STATUS_FVE_FAILED_WRONG_FS"}, - {STATUS_FVE_FAILED_BAD_FS, -EIO, "STATUS_FVE_FAILED_BAD_FS"}, - {STATUS_FVE_FS_NOT_EXTENDED, -EIO, "STATUS_FVE_FS_NOT_EXTENDED"}, - {STATUS_FVE_FS_MOUNTED, -EIO, "STATUS_FVE_FS_MOUNTED"}, - {STATUS_FVE_NO_LICENSE, -EIO, "STATUS_FVE_NO_LICENSE"}, - {STATUS_FVE_ACTION_NOT_ALLOWED, -EIO, "STATUS_FVE_ACTION_NOT_ALLOWED"}, - {STATUS_FVE_BAD_DATA, -EIO, "STATUS_FVE_BAD_DATA"}, - {STATUS_FVE_VOLUME_NOT_BOUND, -EIO, "STATUS_FVE_VOLUME_NOT_BOUND"}, - {STATUS_FVE_NOT_DATA_VOLUME, -EIO, "STATUS_FVE_NOT_DATA_VOLUME"}, - {STATUS_FVE_CONV_READ_ERROR, -EIO, "STATUS_FVE_CONV_READ_ERROR"}, - {STATUS_FVE_CONV_WRITE_ERROR, -EIO, "STATUS_FVE_CONV_WRITE_ERROR"}, - {STATUS_FVE_OVERLAPPED_UPDATE, -EIO, "STATUS_FVE_OVERLAPPED_UPDATE"}, - {STATUS_FVE_FAILED_SECTOR_SIZE, -EIO, "STATUS_FVE_FAILED_SECTOR_SIZE"}, - {STATUS_FVE_FAILED_AUTHENTICATION, -EIO, - "STATUS_FVE_FAILED_AUTHENTICATION"}, - {STATUS_FVE_NOT_OS_VOLUME, -EIO, "STATUS_FVE_NOT_OS_VOLUME"}, - {STATUS_FVE_KEYFILE_NOT_FOUND, -EIO, "STATUS_FVE_KEYFILE_NOT_FOUND"}, - {STATUS_FVE_KEYFILE_INVALID, -EIO, "STATUS_FVE_KEYFILE_INVALID"}, - {STATUS_FVE_KEYFILE_NO_VMK, -EIO, "STATUS_FVE_KEYFILE_NO_VMK"}, - {STATUS_FVE_TPM_DISABLED, -EIO, "STATUS_FVE_TPM_DISABLED"}, - {STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO, -EIO, - "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO"}, - {STATUS_FVE_TPM_INVALID_PCR, -EIO, "STATUS_FVE_TPM_INVALID_PCR"}, - {STATUS_FVE_TPM_NO_VMK, -EIO, "STATUS_FVE_TPM_NO_VMK"}, - {STATUS_FVE_PIN_INVALID, -EIO, "STATUS_FVE_PIN_INVALID"}, - {STATUS_FVE_AUTH_INVALID_APPLICATION, -EIO, - "STATUS_FVE_AUTH_INVALID_APPLICATION"}, - {STATUS_FVE_AUTH_INVALID_CONFIG, -EIO, - "STATUS_FVE_AUTH_INVALID_CONFIG"}, - {STATUS_FVE_DEBUGGER_ENABLED, -EIO, "STATUS_FVE_DEBUGGER_ENABLED"}, - {STATUS_FVE_DRY_RUN_FAILED, -EIO, "STATUS_FVE_DRY_RUN_FAILED"}, - {STATUS_FVE_BAD_METADATA_POINTER, -EIO, - "STATUS_FVE_BAD_METADATA_POINTER"}, - {STATUS_FVE_OLD_METADATA_COPY, -EIO, "STATUS_FVE_OLD_METADATA_COPY"}, - {STATUS_FVE_REBOOT_REQUIRED, -EIO, "STATUS_FVE_REBOOT_REQUIRED"}, - {STATUS_FVE_RAW_ACCESS, -EIO, "STATUS_FVE_RAW_ACCESS"}, - {STATUS_FVE_RAW_BLOCKED, -EIO, "STATUS_FVE_RAW_BLOCKED"}, - {STATUS_FWP_CALLOUT_NOT_FOUND, -EIO, "STATUS_FWP_CALLOUT_NOT_FOUND"}, - {STATUS_FWP_CONDITION_NOT_FOUND, -EIO, - "STATUS_FWP_CONDITION_NOT_FOUND"}, - {STATUS_FWP_FILTER_NOT_FOUND, -EIO, "STATUS_FWP_FILTER_NOT_FOUND"}, - {STATUS_FWP_LAYER_NOT_FOUND, -EIO, "STATUS_FWP_LAYER_NOT_FOUND"}, - {STATUS_FWP_PROVIDER_NOT_FOUND, -EIO, "STATUS_FWP_PROVIDER_NOT_FOUND"}, - {STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND, -EIO, - "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND"}, - {STATUS_FWP_SUBLAYER_NOT_FOUND, -EIO, "STATUS_FWP_SUBLAYER_NOT_FOUND"}, - {STATUS_FWP_NOT_FOUND, -EIO, "STATUS_FWP_NOT_FOUND"}, - {STATUS_FWP_ALREADY_EXISTS, -EIO, "STATUS_FWP_ALREADY_EXISTS"}, - {STATUS_FWP_IN_USE, -EIO, "STATUS_FWP_IN_USE"}, - {STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS, -EIO, - "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS"}, - {STATUS_FWP_WRONG_SESSION, -EIO, "STATUS_FWP_WRONG_SESSION"}, - {STATUS_FWP_NO_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_NO_TXN_IN_PROGRESS"}, - {STATUS_FWP_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_TXN_IN_PROGRESS"}, - {STATUS_FWP_TXN_ABORTED, -EIO, "STATUS_FWP_TXN_ABORTED"}, - {STATUS_FWP_SESSION_ABORTED, -EIO, "STATUS_FWP_SESSION_ABORTED"}, - {STATUS_FWP_INCOMPATIBLE_TXN, -EIO, "STATUS_FWP_INCOMPATIBLE_TXN"}, - {STATUS_FWP_TIMEOUT, -ETIMEDOUT, "STATUS_FWP_TIMEOUT"}, - {STATUS_FWP_NET_EVENTS_DISABLED, -EIO, - "STATUS_FWP_NET_EVENTS_DISABLED"}, - {STATUS_FWP_INCOMPATIBLE_LAYER, -EIO, "STATUS_FWP_INCOMPATIBLE_LAYER"}, - {STATUS_FWP_KM_CLIENTS_ONLY, -EIO, "STATUS_FWP_KM_CLIENTS_ONLY"}, - {STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"}, - {STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"}, - {STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO, - "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS"}, - {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, "STATUS_FWP_TOO_MANY_CALLOUTS"}, - {STATUS_FWP_NOTIFICATION_DROPPED, -EIO, - "STATUS_FWP_NOTIFICATION_DROPPED"}, - {STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"}, - {STATUS_FWP_INCOMPATIBLE_SA_STATE, -EIO, - "STATUS_FWP_INCOMPATIBLE_SA_STATE"}, - {STATUS_FWP_NULL_POINTER, -EIO, "STATUS_FWP_NULL_POINTER"}, - {STATUS_FWP_INVALID_ENUMERATOR, -EIO, "STATUS_FWP_INVALID_ENUMERATOR"}, - {STATUS_FWP_INVALID_FLAGS, -EIO, "STATUS_FWP_INVALID_FLAGS"}, - {STATUS_FWP_INVALID_NET_MASK, -EIO, "STATUS_FWP_INVALID_NET_MASK"}, - {STATUS_FWP_INVALID_RANGE, -EIO, "STATUS_FWP_INVALID_RANGE"}, - {STATUS_FWP_INVALID_INTERVAL, -EIO, "STATUS_FWP_INVALID_INTERVAL"}, - {STATUS_FWP_ZERO_LENGTH_ARRAY, -EIO, "STATUS_FWP_ZERO_LENGTH_ARRAY"}, - {STATUS_FWP_NULL_DISPLAY_NAME, -EIO, "STATUS_FWP_NULL_DISPLAY_NAME"}, - {STATUS_FWP_INVALID_ACTION_TYPE, -EIO, - "STATUS_FWP_INVALID_ACTION_TYPE"}, - {STATUS_FWP_INVALID_WEIGHT, -EIO, "STATUS_FWP_INVALID_WEIGHT"}, - {STATUS_FWP_MATCH_TYPE_MISMATCH, -EIO, - "STATUS_FWP_MATCH_TYPE_MISMATCH"}, - {STATUS_FWP_TYPE_MISMATCH, -EIO, "STATUS_FWP_TYPE_MISMATCH"}, - {STATUS_FWP_OUT_OF_BOUNDS, -EIO, "STATUS_FWP_OUT_OF_BOUNDS"}, - {STATUS_FWP_RESERVED, -EIO, "STATUS_FWP_RESERVED"}, - {STATUS_FWP_DUPLICATE_CONDITION, -EIO, - "STATUS_FWP_DUPLICATE_CONDITION"}, - {STATUS_FWP_DUPLICATE_KEYMOD, -EIO, "STATUS_FWP_DUPLICATE_KEYMOD"}, - {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER, -EIO, - "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER"}, - {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER, -EIO, - "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER"}, - {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER, -EIO, - "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER"}, - {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT, -EIO, - "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT"}, - {STATUS_FWP_INCOMPATIBLE_AUTH_METHOD, -EIO, - "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD"}, - {STATUS_FWP_INCOMPATIBLE_DH_GROUP, -EIO, - "STATUS_FWP_INCOMPATIBLE_DH_GROUP"}, - {STATUS_FWP_EM_NOT_SUPPORTED, -EOPNOTSUPP, - "STATUS_FWP_EM_NOT_SUPPORTED"}, - {STATUS_FWP_NEVER_MATCH, -EIO, "STATUS_FWP_NEVER_MATCH"}, - {STATUS_FWP_PROVIDER_CONTEXT_MISMATCH, -EIO, - "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH"}, - {STATUS_FWP_INVALID_PARAMETER, -EIO, "STATUS_FWP_INVALID_PARAMETER"}, - {STATUS_FWP_TOO_MANY_SUBLAYERS, -EIO, "STATUS_FWP_TOO_MANY_SUBLAYERS"}, - {STATUS_FWP_CALLOUT_NOTIFICATION_FAILED, -EIO, - "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED"}, - {STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG, -EIO, - "STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG"}, - {STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG, -EIO, - "STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG"}, - {STATUS_FWP_TCPIP_NOT_READY, -EIO, "STATUS_FWP_TCPIP_NOT_READY"}, - {STATUS_FWP_INJECT_HANDLE_CLOSING, -EIO, - "STATUS_FWP_INJECT_HANDLE_CLOSING"}, - {STATUS_FWP_INJECT_HANDLE_STALE, -EIO, - "STATUS_FWP_INJECT_HANDLE_STALE"}, - {STATUS_FWP_CANNOT_PEND, -EIO, "STATUS_FWP_CANNOT_PEND"}, - {STATUS_NDIS_CLOSING, -EIO, "STATUS_NDIS_CLOSING"}, - {STATUS_NDIS_BAD_VERSION, -EIO, "STATUS_NDIS_BAD_VERSION"}, - {STATUS_NDIS_BAD_CHARACTERISTICS, -EIO, - "STATUS_NDIS_BAD_CHARACTERISTICS"}, - {STATUS_NDIS_ADAPTER_NOT_FOUND, -EIO, "STATUS_NDIS_ADAPTER_NOT_FOUND"}, - {STATUS_NDIS_OPEN_FAILED, -EIO, "STATUS_NDIS_OPEN_FAILED"}, - {STATUS_NDIS_DEVICE_FAILED, -EIO, "STATUS_NDIS_DEVICE_FAILED"}, - {STATUS_NDIS_MULTICAST_FULL, -EIO, "STATUS_NDIS_MULTICAST_FULL"}, - {STATUS_NDIS_MULTICAST_EXISTS, -EIO, "STATUS_NDIS_MULTICAST_EXISTS"}, - {STATUS_NDIS_MULTICAST_NOT_FOUND, -EIO, - "STATUS_NDIS_MULTICAST_NOT_FOUND"}, - {STATUS_NDIS_REQUEST_ABORTED, -EIO, "STATUS_NDIS_REQUEST_ABORTED"}, - {STATUS_NDIS_RESET_IN_PROGRESS, -EIO, "STATUS_NDIS_RESET_IN_PROGRESS"}, - {STATUS_NDIS_INVALID_PACKET, -EIO, "STATUS_NDIS_INVALID_PACKET"}, - {STATUS_NDIS_INVALID_DEVICE_REQUEST, -EIO, - "STATUS_NDIS_INVALID_DEVICE_REQUEST"}, - {STATUS_NDIS_ADAPTER_NOT_READY, -EIO, "STATUS_NDIS_ADAPTER_NOT_READY"}, - {STATUS_NDIS_INVALID_LENGTH, -EIO, "STATUS_NDIS_INVALID_LENGTH"}, - {STATUS_NDIS_INVALID_DATA, -EIO, "STATUS_NDIS_INVALID_DATA"}, - {STATUS_NDIS_BUFFER_TOO_SHORT, -ENOBUFS, - "STATUS_NDIS_BUFFER_TOO_SHORT"}, - {STATUS_NDIS_INVALID_OID, -EIO, "STATUS_NDIS_INVALID_OID"}, - {STATUS_NDIS_ADAPTER_REMOVED, -EIO, "STATUS_NDIS_ADAPTER_REMOVED"}, - {STATUS_NDIS_UNSUPPORTED_MEDIA, -EIO, "STATUS_NDIS_UNSUPPORTED_MEDIA"}, - {STATUS_NDIS_GROUP_ADDRESS_IN_USE, -EIO, - "STATUS_NDIS_GROUP_ADDRESS_IN_USE"}, - {STATUS_NDIS_FILE_NOT_FOUND, -EIO, "STATUS_NDIS_FILE_NOT_FOUND"}, - {STATUS_NDIS_ERROR_READING_FILE, -EIO, - "STATUS_NDIS_ERROR_READING_FILE"}, - {STATUS_NDIS_ALREADY_MAPPED, -EIO, "STATUS_NDIS_ALREADY_MAPPED"}, - {STATUS_NDIS_RESOURCE_CONFLICT, -EIO, "STATUS_NDIS_RESOURCE_CONFLICT"}, - {STATUS_NDIS_MEDIA_DISCONNECTED, -EIO, - "STATUS_NDIS_MEDIA_DISCONNECTED"}, - {STATUS_NDIS_INVALID_ADDRESS, -EIO, "STATUS_NDIS_INVALID_ADDRESS"}, - {STATUS_NDIS_PAUSED, -EIO, "STATUS_NDIS_PAUSED"}, - {STATUS_NDIS_INTERFACE_NOT_FOUND, -EIO, - "STATUS_NDIS_INTERFACE_NOT_FOUND"}, - {STATUS_NDIS_UNSUPPORTED_REVISION, -EIO, - "STATUS_NDIS_UNSUPPORTED_REVISION"}, - {STATUS_NDIS_INVALID_PORT, -EIO, "STATUS_NDIS_INVALID_PORT"}, - {STATUS_NDIS_INVALID_PORT_STATE, -EIO, - "STATUS_NDIS_INVALID_PORT_STATE"}, - {STATUS_NDIS_LOW_POWER_STATE, -EIO, "STATUS_NDIS_LOW_POWER_STATE"}, - {STATUS_NDIS_NOT_SUPPORTED, -ENOSYS, "STATUS_NDIS_NOT_SUPPORTED"}, - {STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED, -EIO, - "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED"}, - {STATUS_NDIS_DOT11_MEDIA_IN_USE, -EIO, - "STATUS_NDIS_DOT11_MEDIA_IN_USE"}, - {STATUS_NDIS_DOT11_POWER_STATE_INVALID, -EIO, - "STATUS_NDIS_DOT11_POWER_STATE_INVALID"}, - {STATUS_IPSEC_BAD_SPI, -EIO, "STATUS_IPSEC_BAD_SPI"}, - {STATUS_IPSEC_SA_LIFETIME_EXPIRED, -EIO, - "STATUS_IPSEC_SA_LIFETIME_EXPIRED"}, - {STATUS_IPSEC_WRONG_SA, -EIO, "STATUS_IPSEC_WRONG_SA"}, - {STATUS_IPSEC_REPLAY_CHECK_FAILED, -EIO, - "STATUS_IPSEC_REPLAY_CHECK_FAILED"}, - {STATUS_IPSEC_INVALID_PACKET, -EIO, "STATUS_IPSEC_INVALID_PACKET"}, - {STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO, - "STATUS_IPSEC_INTEGRITY_CHECK_FAILED"}, - {STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"}, - {0, 0, NULL} -}; - -/***************************************************************************** - Print an error message from the status code - *****************************************************************************/ -static void -smb2_print_status(__le32 status) -{ - int idx = 0; - - while (smb2_error_map_table[idx].status_string != NULL) { - if ((smb2_error_map_table[idx].smb2_status) == status) { - pr_notice("Status code returned 0x%08x %s\n", status, - smb2_error_map_table[idx].status_string); - } - idx++; - } - return; -} - -int -map_smb2_to_linux_error(char *buf, bool log_err) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - unsigned int i; - int rc = -EIO; - __le32 smb2err = shdr->Status; - - if (smb2err == 0) { - trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), - le64_to_cpu(shdr->MessageId)); - return 0; - } - - /* mask facility */ - if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) && - (smb2err != STATUS_END_OF_FILE)) - smb2_print_status(smb2err); - else if (cifsFYI & CIFS_RC) - smb2_print_status(smb2err); - - for (i = 0; i < sizeof(smb2_error_map_table) / - sizeof(struct status_to_posix_error); i++) { - if (smb2_error_map_table[i].smb2_status == smb2err) { - rc = smb2_error_map_table[i].posix_error; - break; - } - } - - /* on error mapping not found - return EIO */ - - cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n", - __le32_to_cpu(smb2err), rc); - - trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), - le64_to_cpu(shdr->MessageId), - le32_to_cpu(smb2err), rc); - return rc; -} diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c deleted file mode 100644 index 572293c18e16..000000000000 --- a/fs/cifs/smb2misc.c +++ /dev/null @@ -1,944 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "nterr.h" -#include "cached_dir.h" - -static int -check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) -{ - __u64 wire_mid = le64_to_cpu(shdr->MessageId); - - /* - * Make sure that this really is an SMB, that it is a response, - * and that the message ids match. - */ - if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) && - (mid == wire_mid)) { - if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - return 0; - else { - /* only one valid case where server sends us request */ - if (shdr->Command == SMB2_OPLOCK_BREAK) - return 0; - else - cifs_dbg(VFS, "Received Request not response\n"); - } - } else { /* bad signature or mid */ - if (shdr->ProtocolId != SMB2_PROTO_NUMBER) - cifs_dbg(VFS, "Bad protocol string signature header %x\n", - le32_to_cpu(shdr->ProtocolId)); - if (mid != wire_mid) - cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", - mid, wire_mid); - } - cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", wire_mid); - return 1; -} - -/* - * The following table defines the expected "StructureSize" of SMB2 responses - * in order by SMB2 command. This is similar to "wct" in SMB/CIFS responses. - * - * Note that commands are defined in smb2pdu.h in le16 but the array below is - * indexed by command in host byte order - */ -static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ cpu_to_le16(65), - /* SMB2_SESSION_SETUP */ cpu_to_le16(9), - /* SMB2_LOGOFF */ cpu_to_le16(4), - /* SMB2_TREE_CONNECT */ cpu_to_le16(16), - /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), - /* SMB2_CREATE */ cpu_to_le16(89), - /* SMB2_CLOSE */ cpu_to_le16(60), - /* SMB2_FLUSH */ cpu_to_le16(4), - /* SMB2_READ */ cpu_to_le16(17), - /* SMB2_WRITE */ cpu_to_le16(17), - /* SMB2_LOCK */ cpu_to_le16(4), - /* SMB2_IOCTL */ cpu_to_le16(49), - /* BB CHECK this ... not listed in documentation */ - /* SMB2_CANCEL */ cpu_to_le16(0), - /* SMB2_ECHO */ cpu_to_le16(4), - /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(9), - /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(9), - /* SMB2_QUERY_INFO */ cpu_to_le16(9), - /* SMB2_SET_INFO */ cpu_to_le16(2), - /* BB FIXME can also be 44 for lease break */ - /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) -}; - -#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp)) - -static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, - __u32 non_ctxlen) -{ - __u16 neg_count; - __u32 nc_offset, size_of_pad_before_neg_ctxts; - struct smb2_negotiate_rsp *pneg_rsp = (struct smb2_negotiate_rsp *)hdr; - - /* Negotiate contexts are only valid for latest dialect SMB3.11 */ - neg_count = le16_to_cpu(pneg_rsp->NegotiateContextCount); - if ((neg_count == 0) || - (pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID))) - return 0; - - /* - * if SPNEGO blob present (ie the RFC2478 GSS info which indicates - * which security mechanisms the server supports) make sure that - * the negotiate contexts start after it - */ - nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset); - /* - * non_ctxlen is at least shdr->StructureSize + pdu->StructureSize2 - * and the latter is 1 byte bigger than the fix-sized area of the - * NEGOTIATE response - */ - if (nc_offset + 1 < non_ctxlen) { - pr_warn_once("Invalid negotiate context offset %d\n", nc_offset); - return 0; - } else if (nc_offset + 1 == non_ctxlen) { - cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n"); - size_of_pad_before_neg_ctxts = 0; - } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE) - /* has padding, but no SPNEGO blob */ - size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1; - else - size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen; - - /* Verify that at least minimal negotiate contexts fit within frame */ - if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) { - pr_warn_once("negotiate context goes beyond end\n"); - return 0; - } - - cifs_dbg(FYI, "length of negcontexts %d pad %d\n", - len - nc_offset, size_of_pad_before_neg_ctxts); - - /* length of negcontexts including pad from end of sec blob to them */ - return (len - nc_offset) + size_of_pad_before_neg_ctxts; -} - -int -smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) -{ - struct TCP_Server_Info *pserver; - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; - int hdr_size = sizeof(struct smb2_hdr); - int pdu_size = sizeof(struct smb2_pdu); - int command; - __u32 calc_len; /* calculated length */ - __u64 mid; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* - * Add function to do table lookup of StructureSize by command - * ie Validate the wct via smb2_struct_sizes table above - */ - if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { - struct smb2_transform_hdr *thdr = - (struct smb2_transform_hdr *)buf; - struct cifs_ses *ses = NULL; - struct cifs_ses *iter; - - /* decrypt frame now that it is completely read in */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(iter, &pserver->smb_ses_list, smb_ses_list) { - if (iter->Suid == le64_to_cpu(thdr->SessionId)) { - ses = iter; - break; - } - } - spin_unlock(&cifs_tcp_ses_lock); - if (!ses) { - cifs_dbg(VFS, "no decryption - session id not found\n"); - return 1; - } - } - - mid = le64_to_cpu(shdr->MessageId); - if (len < pdu_size) { - if ((len >= hdr_size) - && (shdr->Status != 0)) { - pdu->StructureSize2 = 0; - /* - * As with SMB/CIFS, on some error cases servers may - * not return wct properly - */ - return 0; - } else { - cifs_dbg(VFS, "Length less than SMB header size\n"); - } - return 1; - } - if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) { - cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n", - mid); - return 1; - } - - if (check_smb2_hdr(shdr, mid)) - return 1; - - if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { - cifs_dbg(VFS, "Invalid structure size %u\n", - le16_to_cpu(shdr->StructureSize)); - return 1; - } - - command = le16_to_cpu(shdr->Command); - if (command >= NUMBER_OF_SMB2_COMMANDS) { - cifs_dbg(VFS, "Invalid SMB2 command %d\n", command); - return 1; - } - - if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { - if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 || - pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { - /* error packets have 9 byte structure size */ - cifs_dbg(VFS, "Invalid response size %u for command %d\n", - le16_to_cpu(pdu->StructureSize2), command); - return 1; - } else if (command == SMB2_OPLOCK_BREAK_HE - && (shdr->Status == 0) - && (le16_to_cpu(pdu->StructureSize2) != 44) - && (le16_to_cpu(pdu->StructureSize2) != 36)) { - /* special case for SMB2.1 lease break message */ - cifs_dbg(VFS, "Invalid response size %d for oplock break\n", - le16_to_cpu(pdu->StructureSize2)); - return 1; - } - } - - calc_len = smb2_calc_size(buf); - - /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might - * be 0, and not a real miscalculation */ - if (command == SMB2_IOCTL_HE && calc_len == 0) - return 0; - - if (command == SMB2_NEGOTIATE_HE) - calc_len += get_neg_ctxt_len(shdr, len, calc_len); - - if (len != calc_len) { - /* create failed on symlink */ - if (command == SMB2_CREATE_HE && - shdr->Status == STATUS_STOPPED_ON_SYMLINK) - return 0; - /* Windows 7 server returns 24 bytes more */ - if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) - return 0; - /* server can return one byte more due to implied bcc[0] */ - if (calc_len == len + 1) - return 0; - - /* - * Some windows servers (win2016) will pad also the final - * PDU in a compound to 8 bytes. - */ - if (ALIGN(calc_len, 8) == len) - return 0; - - /* - * MacOS server pads after SMB2.1 write response with 3 bytes - * of junk. Other servers match RFC1001 len to actual - * SMB2/SMB3 frame length (header + smb2 response specific data) - * Some windows servers also pad up to 8 bytes when compounding. - */ - if (calc_len < len) - return 0; - - /* Only log a message if len was really miscalculated */ - if (unlikely(cifsFYI)) - cifs_dbg(FYI, "Server response too short: calculated " - "length %u doesn't match read length %u (cmd=%d, mid=%llu)\n", - calc_len, len, command, mid); - else - pr_warn("Server response too short: calculated length " - "%u doesn't match read length %u (cmd=%d, mid=%llu)\n", - calc_len, len, command, mid); - - return 1; - } - return 0; -} - -/* - * The size of the variable area depends on the offset and length fields - * located in different fields for various SMB2 responses. SMB2 responses - * with no variable length info, show an offset of zero for the offset field. - */ -static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ true, - /* SMB2_SESSION_SETUP */ true, - /* SMB2_LOGOFF */ false, - /* SMB2_TREE_CONNECT */ false, - /* SMB2_TREE_DISCONNECT */ false, - /* SMB2_CREATE */ true, - /* SMB2_CLOSE */ false, - /* SMB2_FLUSH */ false, - /* SMB2_READ */ true, - /* SMB2_WRITE */ false, - /* SMB2_LOCK */ false, - /* SMB2_IOCTL */ true, - /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ - /* SMB2_ECHO */ false, - /* SMB2_QUERY_DIRECTORY */ true, - /* SMB2_CHANGE_NOTIFY */ true, - /* SMB2_QUERY_INFO */ true, - /* SMB2_SET_INFO */ false, - /* SMB2_OPLOCK_BREAK */ false -}; - -/* - * Returns the pointer to the beginning of the data area. Length of the data - * area and the offset to it (from the beginning of the smb are also returned. - */ -char * -smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) -{ - *off = 0; - *len = 0; - - /* error responses do not have data area */ - if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED && - (((struct smb2_err_rsp *)shdr)->StructureSize) == - SMB2_ERROR_STRUCTURE_SIZE2_LE) - return NULL; - - /* - * Following commands have data areas so we have to get the location - * of the data buffer offset and data buffer length for the particular - * command. - */ - switch (shdr->Command) { - case SMB2_NEGOTIATE: - *off = le16_to_cpu( - ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferOffset); - *len = le16_to_cpu( - ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferLength); - break; - case SMB2_SESSION_SETUP: - *off = le16_to_cpu( - ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferOffset); - *len = le16_to_cpu( - ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferLength); - break; - case SMB2_CREATE: - *off = le32_to_cpu( - ((struct smb2_create_rsp *)shdr)->CreateContextsOffset); - *len = le32_to_cpu( - ((struct smb2_create_rsp *)shdr)->CreateContextsLength); - break; - case SMB2_QUERY_INFO: - *off = le16_to_cpu( - ((struct smb2_query_info_rsp *)shdr)->OutputBufferOffset); - *len = le32_to_cpu( - ((struct smb2_query_info_rsp *)shdr)->OutputBufferLength); - break; - case SMB2_READ: - /* TODO: is this a bug ? */ - *off = ((struct smb2_read_rsp *)shdr)->DataOffset; - *len = le32_to_cpu(((struct smb2_read_rsp *)shdr)->DataLength); - break; - case SMB2_QUERY_DIRECTORY: - *off = le16_to_cpu( - ((struct smb2_query_directory_rsp *)shdr)->OutputBufferOffset); - *len = le32_to_cpu( - ((struct smb2_query_directory_rsp *)shdr)->OutputBufferLength); - break; - case SMB2_IOCTL: - *off = le32_to_cpu( - ((struct smb2_ioctl_rsp *)shdr)->OutputOffset); - *len = le32_to_cpu( - ((struct smb2_ioctl_rsp *)shdr)->OutputCount); - break; - case SMB2_CHANGE_NOTIFY: - *off = le16_to_cpu( - ((struct smb2_change_notify_rsp *)shdr)->OutputBufferOffset); - *len = le32_to_cpu( - ((struct smb2_change_notify_rsp *)shdr)->OutputBufferLength); - break; - default: - cifs_dbg(VFS, "no length check for command %d\n", le16_to_cpu(shdr->Command)); - break; - } - - /* - * Invalid length or offset probably means data area is invalid, but - * we have little choice but to ignore the data area in this case. - */ - if (*off > 4096) { - cifs_dbg(VFS, "offset %d too large, data area ignored\n", *off); - *len = 0; - *off = 0; - } else if (*off < 0) { - cifs_dbg(VFS, "negative offset %d to data invalid ignore data area\n", - *off); - *off = 0; - *len = 0; - } else if (*len < 0) { - cifs_dbg(VFS, "negative data length %d invalid, data area ignored\n", - *len); - *len = 0; - } else if (*len > 128 * 1024) { - cifs_dbg(VFS, "data area larger than 128K: %d\n", *len); - *len = 0; - } - - /* return pointer to beginning of data area, ie offset from SMB start */ - if ((*off != 0) && (*len != 0)) - return (char *)shdr + *off; - else - return NULL; -} - -/* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - */ -unsigned int -smb2_calc_size(void *buf) -{ - struct smb2_pdu *pdu = buf; - struct smb2_hdr *shdr = &pdu->hdr; - int offset; /* the offset from the beginning of SMB to data area */ - int data_length; /* the length of the variable length data area */ - /* Structure Size has already been checked to make sure it is 64 */ - int len = le16_to_cpu(shdr->StructureSize); - - /* - * StructureSize2, ie length of fixed parameter area has already - * been checked to make sure it is the correct length. - */ - len += le16_to_cpu(pdu->StructureSize2); - - if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false) - goto calc_size_exit; - - smb2_get_data_area_len(&offset, &data_length, shdr); - cifs_dbg(FYI, "SMB2 data length %d offset %d\n", data_length, offset); - - if (data_length > 0) { - /* - * Check to make sure that data area begins after fixed area, - * Note that last byte of the fixed area is part of data area - * for some commands, typically those with odd StructureSize, - * so we must add one to the calculation. - */ - if (offset + 1 < len) { - cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", - offset + 1, len); - data_length = 0; - } else { - len = offset + data_length; - } - } -calc_size_exit: - cifs_dbg(FYI, "SMB2 len %d\n", len); - return len; -} - -/* Note: caller must free return buffer */ -__le16 * -cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) -{ - int len; - const char *start_of_path; - __le16 *to; - int map_type; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) - map_type = SFM_MAP_UNI_RSVD; - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) - map_type = SFU_MAP_UNI_RSVD; - else - map_type = NO_MAP_UNI_RSVD; - - /* Windows doesn't allow paths beginning with \ */ - if (from[0] == '\\') - start_of_path = from + 1; - - /* SMB311 POSIX extensions paths do not include leading slash */ - else if (cifs_sb_master_tlink(cifs_sb) && - cifs_sb_master_tcon(cifs_sb)->posix_extensions && - (from[0] == '/')) { - start_of_path = from + 1; - } else - start_of_path = from; - - to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, - cifs_sb->local_nls, map_type); - return to; -} - -__le32 -smb2_get_lease_state(struct cifsInodeInfo *cinode) -{ - __le32 lease = 0; - - if (CIFS_CACHE_WRITE(cinode)) - lease |= SMB2_LEASE_WRITE_CACHING_LE; - if (CIFS_CACHE_HANDLE(cinode)) - lease |= SMB2_LEASE_HANDLE_CACHING_LE; - if (CIFS_CACHE_READ(cinode)) - lease |= SMB2_LEASE_READ_CACHING_LE; - return lease; -} - -struct smb2_lease_break_work { - struct work_struct lease_break; - struct tcon_link *tlink; - __u8 lease_key[16]; - __le32 lease_state; -}; - -static void -cifs_ses_oplock_break(struct work_struct *work) -{ - struct smb2_lease_break_work *lw = container_of(work, - struct smb2_lease_break_work, lease_break); - int rc = 0; - - rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, - lw->lease_state); - - cifs_dbg(FYI, "Lease release rc %d\n", rc); - cifs_put_tlink(lw->tlink); - kfree(lw); -} - -static void -smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key, - __le32 new_lease_state) -{ - struct smb2_lease_break_work *lw; - - lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); - if (!lw) { - cifs_put_tlink(tlink); - return; - } - - INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); - lw->tlink = tlink; - lw->lease_state = new_lease_state; - memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE); - queue_work(cifsiod_wq, &lw->lease_break); -} - -static bool -smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp) -{ - __u8 lease_state; - struct cifsFileInfo *cfile; - struct cifsInodeInfo *cinode; - int ack_req = le32_to_cpu(rsp->Flags & - SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); - - lease_state = le32_to_cpu(rsp->NewLeaseState); - - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - cinode = CIFS_I(d_inode(cfile->dentry)); - - if (memcmp(cinode->lease_key, rsp->LeaseKey, - SMB2_LEASE_KEY_SIZE)) - continue; - - cifs_dbg(FYI, "found in the open list\n"); - cifs_dbg(FYI, "lease key match, lease break 0x%x\n", - lease_state); - - if (ack_req) - cfile->oplock_break_cancelled = false; - else - cfile->oplock_break_cancelled = true; - - set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); - - cfile->oplock_epoch = le16_to_cpu(rsp->Epoch); - cfile->oplock_level = lease_state; - - cifs_queue_oplock_break(cfile); - return true; - } - - return false; -} - -static struct cifs_pending_open * -smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, - struct smb2_lease_break *rsp) -{ - __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); - int ack_req = le32_to_cpu(rsp->Flags & - SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); - struct cifs_pending_open *open; - struct cifs_pending_open *found = NULL; - - list_for_each_entry(open, &tcon->pending_opens, olist) { - if (memcmp(open->lease_key, rsp->LeaseKey, - SMB2_LEASE_KEY_SIZE)) - continue; - - if (!found && ack_req) { - found = open; - } - - cifs_dbg(FYI, "found in the pending open list\n"); - cifs_dbg(FYI, "lease key match, lease break 0x%x\n", - lease_state); - - open->oplock = lease_state; - } - - return found; -} - -static bool -smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifs_pending_open *open; - - cifs_dbg(FYI, "Checking for lease break\n"); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* look up tcon based on tid & uid */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->open_file_lock); - cifs_stats_inc( - &tcon->stats.cifs_stats.num_oplock_brks); - if (smb2_tcon_has_lease(tcon, rsp)) { - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - open = smb2_tcon_find_pending_open_lease(tcon, - rsp); - if (open) { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - struct tcon_link *tlink; - - tlink = cifs_get_tlink(open->tlink); - memcpy(lease_key, open->lease_key, - SMB2_LEASE_KEY_SIZE); - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - smb2_queue_pending_open_break(tlink, - lease_key, - rsp->NewLeaseState); - return true; - } - spin_unlock(&tcon->open_file_lock); - - if (cached_dir_lease_break(tcon, rsp->LeaseKey)) { - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); - trace_smb3_lease_not_found(le32_to_cpu(rsp->CurrentLeaseState), - le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), - le64_to_cpu(rsp->hdr.SessionId), - *((u64 *)rsp->LeaseKey), - *((u64 *)&rsp->LeaseKey[8])); - - return false; -} - -bool -smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifsInodeInfo *cinode; - struct cifsFileInfo *cfile; - - cifs_dbg(FYI, "Checking for oplock break\n"); - - if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) - return false; - - if (rsp->StructureSize != - smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { - if (le16_to_cpu(rsp->StructureSize) == 44) - return smb2_is_valid_lease_break(buffer, server); - else - return false; - } - - cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* look up tcon based on tid & uid */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - if (rsp->PersistentFid != - cfile->fid.persistent_fid || - rsp->VolatileFid != - cfile->fid.volatile_fid) - continue; - - cifs_dbg(FYI, "file id match, oplock break\n"); - cifs_stats_inc( - &tcon->stats.cifs_stats.num_oplock_brks); - cinode = CIFS_I(d_inode(cfile->dentry)); - spin_lock(&cfile->file_info_lock); - if (!CIFS_CACHE_WRITE(cinode) && - rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) - cfile->oplock_break_cancelled = true; - else - cfile->oplock_break_cancelled = false; - - set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, - &cinode->flags); - - cfile->oplock_epoch = 0; - cfile->oplock_level = rsp->OplockLevel; - - spin_unlock(&cfile->file_info_lock); - - cifs_queue_oplock_break(cfile); - - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - spin_unlock(&tcon->open_file_lock); - } - } - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "No file id matched, oplock break ignored\n"); - trace_smb3_oplock_not_found(0 /* no xid */, rsp->PersistentFid, - le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), - le64_to_cpu(rsp->hdr.SessionId)); - - return true; -} - -void -smb2_cancelled_close_fid(struct work_struct *work) -{ - struct close_cancelled_open *cancelled = container_of(work, - struct close_cancelled_open, work); - struct cifs_tcon *tcon = cancelled->tcon; - int rc; - - if (cancelled->mid) - cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llu\n", - cancelled->mid); - else - cifs_tcon_dbg(VFS, "Close interrupted close\n"); - - rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid, - cancelled->fid.volatile_fid); - if (rc) - cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); - - cifs_put_tcon(tcon); - kfree(cancelled); -} - -/* - * Caller should already has an extra reference to @tcon - * This function is used to queue work to close a handle to prevent leaks - * on the server. - * We handle two cases. If an open was interrupted after we sent the - * SMB2_CREATE to the server but before we processed the reply, and second - * if a close was interrupted before we sent the SMB2_CLOSE to the server. - */ -static int -__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, - __u64 persistent_fid, __u64 volatile_fid) -{ - struct close_cancelled_open *cancelled; - - cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC); - if (!cancelled) - return -ENOMEM; - - cancelled->fid.persistent_fid = persistent_fid; - cancelled->fid.volatile_fid = volatile_fid; - cancelled->tcon = tcon; - cancelled->cmd = cmd; - cancelled->mid = mid; - INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); - WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); - - return 0; -} - -int -smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, - __u64 volatile_fid) -{ - int rc; - - cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); - spin_lock(&cifs_tcp_ses_lock); - if (tcon->tc_count <= 0) { - struct TCP_Server_Info *server = NULL; - - WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); - spin_unlock(&cifs_tcp_ses_lock); - - if (tcon->ses) - server = tcon->ses->server; - - cifs_server_dbg(FYI, "tid=0x%x: tcon is closing, skipping async close retry of fid %llu %llu\n", - tcon->tid, persistent_fid, volatile_fid); - - return 0; - } - tcon->tc_count++; - spin_unlock(&cifs_tcp_ses_lock); - - rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, - persistent_fid, volatile_fid); - if (rc) - cifs_put_tcon(tcon); - - return rc; -} - -int -smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server) -{ - struct smb2_hdr *hdr = mid->resp_buf; - struct smb2_create_rsp *rsp = mid->resp_buf; - struct cifs_tcon *tcon; - int rc; - - if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || hdr->Command != SMB2_CREATE || - hdr->Status != STATUS_SUCCESS) - return 0; - - tcon = smb2_find_smb_tcon(server, le64_to_cpu(hdr->SessionId), - le32_to_cpu(hdr->Id.SyncId.TreeId)); - if (!tcon) - return -ENOENT; - - rc = __smb2_handle_cancelled_cmd(tcon, - le16_to_cpu(hdr->Command), - le64_to_cpu(hdr->MessageId), - rsp->PersistentFileId, - rsp->VolatileFileId); - if (rc) - cifs_put_tcon(tcon); - - return rc; -} - -/** - * smb311_update_preauth_hash - update @ses hash with the packet data in @iov - * - * Assumes @iov does not contain the rfc1002 length and iov[0] has the - * SMB2 header. - * - * @ses: server session structure - * @server: pointer to server info - * @iov: array containing the SMB request we will send to the server - * @nvec: number of array entries for the iov - */ -int -smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct kvec *iov, int nvec) -{ - int i, rc; - struct smb2_hdr *hdr; - struct shash_desc *sha512 = NULL; - - hdr = (struct smb2_hdr *)iov[0].iov_base; - /* neg prot are always taken */ - if (hdr->Command == SMB2_NEGOTIATE) - goto ok; - - /* - * If we process a command which wasn't a negprot it means the - * neg prot was already done, so the server dialect was set - * and we can test it. Preauth requires 3.1.1 for now. - */ - if (server->dialect != SMB311_PROT_ID) - return 0; - - if (hdr->Command != SMB2_SESSION_SETUP) - return 0; - - /* skip last sess setup response */ - if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - && (hdr->Status == NT_STATUS_OK - || (hdr->Status != - cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)))) - return 0; - -ok: - rc = smb311_crypto_shash_allocate(server); - if (rc) - return rc; - - sha512 = server->secmech.sha512; - rc = crypto_shash_init(sha512); - if (rc) { - cifs_dbg(VFS, "%s: Could not init sha512 shash\n", __func__); - return rc; - } - - rc = crypto_shash_update(sha512, ses->preauth_sha_hash, - SMB2_PREAUTH_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__); - return rc; - } - - for (i = 0; i < nvec; i++) { - rc = crypto_shash_update(sha512, iov[i].iov_base, iov[i].iov_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update sha512 shash\n", - __func__); - return rc; - } - } - - rc = crypto_shash_final(sha512, ses->preauth_sha_hash); - if (rc) { - cifs_dbg(VFS, "%s: Could not finalize sha512 shash\n", - __func__); - return rc; - } - - return 0; -} diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c deleted file mode 100644 index d512440d35b6..000000000000 --- a/fs/cifs/smb2ops.c +++ /dev/null @@ -1,5805 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * SMB2 version specific operations - * - * Copyright (c) 2012, Jeff Layton - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifsglob.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "cifs_ioctl.h" -#include "smbdirect.h" -#include "fscache.h" -#include "fs_context.h" -#include "cached_dir.h" - -/* Change credits for different ops and return the total number of credits */ -static int -change_conf(struct TCP_Server_Info *server) -{ - server->credits += server->echo_credits + server->oplock_credits; - server->oplock_credits = server->echo_credits = 0; - switch (server->credits) { - case 0: - return 0; - case 1: - server->echoes = false; - server->oplocks = false; - break; - case 2: - server->echoes = true; - server->oplocks = false; - server->echo_credits = 1; - break; - default: - server->echoes = true; - if (enable_oplocks) { - server->oplocks = true; - server->oplock_credits = 1; - } else - server->oplocks = false; - - server->echo_credits = 1; - } - server->credits -= server->echo_credits + server->oplock_credits; - return server->credits + server->echo_credits + server->oplock_credits; -} - -static void -smb2_add_credits(struct TCP_Server_Info *server, - const struct cifs_credits *credits, const int optype) -{ - int *val, rc = -1; - int scredits, in_flight; - unsigned int add = credits->value; - unsigned int instance = credits->instance; - bool reconnect_detected = false; - bool reconnect_with_invalid_credits = false; - - spin_lock(&server->req_lock); - val = server->ops->get_credits_field(server, optype); - - /* eg found case where write overlapping reconnect messed up credits */ - if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) - reconnect_with_invalid_credits = true; - - if ((instance == 0) || (instance == server->reconnect_instance)) - *val += add; - else - reconnect_detected = true; - - if (*val > 65000) { - *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ - pr_warn_once("server overflowed SMB3 credits\n"); - trace_smb3_overflow_credits(server->CurrentMid, - server->conn_id, server->hostname, *val, - add, server->in_flight); - } - server->in_flight--; - if (server->in_flight == 0 && - ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) && - ((optype & CIFS_OP_MASK) != CIFS_SESS_OP)) - rc = change_conf(server); - /* - * Sometimes server returns 0 credits on oplock break ack - we need to - * rebalance credits in this case. - */ - else if (server->in_flight > 0 && server->oplock_credits == 0 && - server->oplocks) { - if (server->credits > 1) { - server->credits--; - server->oplock_credits++; - } - } - scredits = *val; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - if (reconnect_detected) { - trace_smb3_reconnect_detected(server->CurrentMid, - server->conn_id, server->hostname, scredits, add, in_flight); - - cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", - add, instance); - } - - if (reconnect_with_invalid_credits) { - trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, add, in_flight); - cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n", - optype, scredits, add); - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect - || server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return; - } - spin_unlock(&server->srv_lock); - - switch (rc) { - case -1: - /* change_conf hasn't been executed */ - break; - case 0: - cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n"); - break; - case 1: - cifs_server_dbg(VFS, "disabling echoes and oplocks\n"); - break; - case 2: - cifs_dbg(FYI, "disabling oplocks\n"); - break; - default: - /* change_conf rebalanced credits for different types */ - break; - } - - trace_smb3_add_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, add, in_flight); - cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits); -} - -static void -smb2_set_credits(struct TCP_Server_Info *server, const int val) -{ - int scredits, in_flight; - - spin_lock(&server->req_lock); - server->credits = val; - if (val == 1) - server->reconnect_instance++; - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_set_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, val, in_flight); - cifs_dbg(FYI, "%s: set %u credits\n", __func__, val); - - /* don't log while holding the lock */ - if (val == 1) - cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n"); -} - -static int * -smb2_get_credits_field(struct TCP_Server_Info *server, const int optype) -{ - switch (optype) { - case CIFS_ECHO_OP: - return &server->echo_credits; - case CIFS_OBREAK_OP: - return &server->oplock_credits; - default: - return &server->credits; - } -} - -static unsigned int -smb2_get_credits(struct mid_q_entry *mid) -{ - return mid->credits_received; -} - -static int -smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, - unsigned int *num, struct cifs_credits *credits) -{ - int rc = 0; - unsigned int scredits, in_flight; - - spin_lock(&server->req_lock); - while (1) { - if (server->credits <= 0) { - spin_unlock(&server->req_lock); - cifs_num_waiters_inc(server); - rc = wait_event_killable(server->request_q, - has_credits(server, &server->credits, 1)); - cifs_num_waiters_dec(server); - if (rc) - return rc; - spin_lock(&server->req_lock); - } else { - spin_unlock(&server->req_lock); - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - spin_lock(&server->req_lock); - scredits = server->credits; - /* can deadlock with reopen */ - if (scredits <= 8) { - *num = SMB2_MAX_BUFFER_SIZE; - credits->value = 0; - credits->instance = 0; - break; - } - - /* leave some credits for reopen and other ops */ - scredits -= 8; - *num = min_t(unsigned int, size, - scredits * SMB2_MAX_BUFFER_SIZE); - - credits->value = - DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); - credits->instance = server->reconnect_instance; - server->credits -= credits->value; - server->in_flight++; - if (server->in_flight > server->max_in_flight) - server->max_in_flight = server->in_flight; - break; - } - } - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_wait_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, -(credits->value), in_flight); - cifs_dbg(FYI, "%s: removed %u credits total=%d\n", - __func__, credits->value, scredits); - - return rc; -} - -static int -smb2_adjust_credits(struct TCP_Server_Info *server, - struct cifs_credits *credits, - const unsigned int payload_size) -{ - int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE); - int scredits, in_flight; - - if (!credits->value || credits->value == new_val) - return 0; - - if (credits->value < new_val) { - trace_smb3_too_many_credits(server->CurrentMid, - server->conn_id, server->hostname, 0, credits->value - new_val, 0); - cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)", - credits->value, new_val); - - return -ENOTSUPP; - } - - spin_lock(&server->req_lock); - - if (server->reconnect_instance != credits->instance) { - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_reconnect_detected(server->CurrentMid, - server->conn_id, server->hostname, scredits, - credits->value - new_val, in_flight); - cifs_server_dbg(VFS, "trying to return %d credits to old session\n", - credits->value - new_val); - return -EAGAIN; - } - - server->credits += credits->value - new_val; - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - trace_smb3_adj_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - credits->value - new_val, in_flight); - cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n", - __func__, credits->value - new_val, scredits); - - credits->value = new_val; - - return 0; -} - -static __u64 -smb2_get_next_mid(struct TCP_Server_Info *server) -{ - __u64 mid; - /* for SMB2 we need the current value */ - spin_lock(&server->mid_lock); - mid = server->CurrentMid++; - spin_unlock(&server->mid_lock); - return mid; -} - -static void -smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) -{ - spin_lock(&server->mid_lock); - if (server->CurrentMid >= val) - server->CurrentMid -= val; - spin_unlock(&server->mid_lock); -} - -static struct mid_q_entry * -__smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue) -{ - struct mid_q_entry *mid; - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - __u64 wire_mid = le64_to_cpu(shdr->MessageId); - - if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { - cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n"); - return NULL; - } - - spin_lock(&server->mid_lock); - list_for_each_entry(mid, &server->pending_mid_q, qhead) { - if ((mid->mid == wire_mid) && - (mid->mid_state == MID_REQUEST_SUBMITTED) && - (mid->command == shdr->Command)) { - kref_get(&mid->refcount); - if (dequeue) { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - } - spin_unlock(&server->mid_lock); - return mid; - } - } - spin_unlock(&server->mid_lock); - return NULL; -} - -static struct mid_q_entry * -smb2_find_mid(struct TCP_Server_Info *server, char *buf) -{ - return __smb2_find_mid(server, buf, false); -} - -static struct mid_q_entry * -smb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf) -{ - return __smb2_find_mid(server, buf, true); -} - -static void -smb2_dump_detail(void *buf, struct TCP_Server_Info *server) -{ -#ifdef CONFIG_CIFS_DEBUG2 - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - - cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", - shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, - shdr->Id.SyncId.ProcessId); - cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, - server->ops->calc_smb_size(buf)); -#endif -} - -static bool -smb2_need_neg(struct TCP_Server_Info *server) -{ - return server->max_read == 0; -} - -static int -smb2_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - int rc; - - spin_lock(&server->mid_lock); - server->CurrentMid = 0; - spin_unlock(&server->mid_lock); - rc = SMB2_negotiate(xid, ses, server); - /* BB we probably don't need to retry with modern servers */ - if (rc == -EAGAIN) - rc = -EHOSTDOWN; - return rc; -} - -static unsigned int -smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int wsize; - - /* start with specified wsize, or default */ - wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE; - wsize = min_t(unsigned int, wsize, server->max_write); - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - - return wsize; -} - -static unsigned int -smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int wsize; - - /* start with specified wsize, or default */ - wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE; - wsize = min_t(unsigned int, wsize, server->max_write); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (server->rdma) { - if (server->sign) - /* - * Account for SMB2 data transfer packet header and - * possible encryption header - */ - wsize = min_t(unsigned int, - wsize, - server->smbd_conn->max_fragmented_send_size - - SMB2_READWRITE_PDU_HEADER_SIZE - - sizeof(struct smb2_transform_hdr)); - else - wsize = min_t(unsigned int, - wsize, server->smbd_conn->max_readwrite_size); - } -#endif - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - - return wsize; -} - -static unsigned int -smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int rsize; - - /* start with specified rsize, or default */ - rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE; - rsize = min_t(unsigned int, rsize, server->max_read); - - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - - return rsize; -} - -static unsigned int -smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int rsize; - - /* start with specified rsize, or default */ - rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE; - rsize = min_t(unsigned int, rsize, server->max_read); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (server->rdma) { - if (server->sign) - /* - * Account for SMB2 data transfer packet header and - * possible encryption header - */ - rsize = min_t(unsigned int, - rsize, - server->smbd_conn->max_fragmented_recv_size - - SMB2_READWRITE_PDU_HEADER_SIZE - - sizeof(struct smb2_transform_hdr)); - else - rsize = min_t(unsigned int, - rsize, server->smbd_conn->max_readwrite_size); - } -#endif - - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - - return rsize; -} - -static int -parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, - size_t buf_len, struct cifs_ses *ses, bool in_mount) -{ - struct network_interface_info_ioctl_rsp *p; - struct sockaddr_in *addr4; - struct sockaddr_in6 *addr6; - struct iface_info_ipv4 *p4; - struct iface_info_ipv6 *p6; - struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL; - struct cifs_server_iface tmp_iface; - ssize_t bytes_left; - size_t next = 0; - int nb_iface = 0; - int rc = 0, ret = 0; - - bytes_left = buf_len; - p = buf; - - spin_lock(&ses->iface_lock); - /* do not query too frequently, this time with lock held */ - if (ses->iface_last_update && - time_before(jiffies, ses->iface_last_update + - (SMB_INTERFACE_POLL_INTERVAL * HZ))) { - spin_unlock(&ses->iface_lock); - return 0; - } - - /* - * Go through iface_list and do kref_put to remove - * any unused ifaces. ifaces in use will be removed - * when the last user calls a kref_put on it - */ - list_for_each_entry_safe(iface, niface, &ses->iface_list, - iface_head) { - iface->is_active = 0; - kref_put(&iface->refcount, release_iface); - ses->iface_count--; - } - spin_unlock(&ses->iface_lock); - - /* - * Samba server e.g. can return an empty interface list in some cases, - * which would only be a problem if we were requesting multichannel - */ - if (bytes_left == 0) { - /* avoid spamming logs every 10 minutes, so log only in mount */ - if ((ses->chan_max > 1) && in_mount) - cifs_dbg(VFS, - "multichannel not available\n" - "Empty network interface list returned by server %s\n", - ses->server->hostname); - rc = -EINVAL; - goto out; - } - - while (bytes_left >= sizeof(*p)) { - memset(&tmp_iface, 0, sizeof(tmp_iface)); - tmp_iface.speed = le64_to_cpu(p->LinkSpeed); - tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0; - tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0; - - switch (p->Family) { - /* - * The kernel and wire socket structures have the same - * layout and use network byte order but make the - * conversion explicit in case either one changes. - */ - case INTERNETWORK: - addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr; - p4 = (struct iface_info_ipv4 *)p->Buffer; - addr4->sin_family = AF_INET; - memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); - - /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ - addr4->sin_port = cpu_to_be16(CIFS_PORT); - - cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, - &addr4->sin_addr); - break; - case INTERNETWORKV6: - addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr; - p6 = (struct iface_info_ipv6 *)p->Buffer; - addr6->sin6_family = AF_INET6; - memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); - - /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ - addr6->sin6_flowinfo = 0; - addr6->sin6_scope_id = 0; - addr6->sin6_port = cpu_to_be16(CIFS_PORT); - - cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, - &addr6->sin6_addr); - break; - default: - cifs_dbg(VFS, - "%s: skipping unsupported socket family\n", - __func__); - goto next_iface; - } - - /* - * The iface_list is assumed to be sorted by speed. - * Check if the new interface exists in that list. - * NEVER change iface. it could be in use. - * Add a new one instead - */ - spin_lock(&ses->iface_lock); - iface = niface = NULL; - list_for_each_entry_safe(iface, niface, &ses->iface_list, - iface_head) { - ret = iface_cmp(iface, &tmp_iface); - if (!ret) { - /* just get a ref so that it doesn't get picked/freed */ - iface->is_active = 1; - kref_get(&iface->refcount); - ses->iface_count++; - spin_unlock(&ses->iface_lock); - goto next_iface; - } else if (ret < 0) { - /* all remaining ifaces are slower */ - kref_get(&iface->refcount); - break; - } - } - spin_unlock(&ses->iface_lock); - - /* no match. insert the entry in the list */ - info = kmalloc(sizeof(struct cifs_server_iface), - GFP_KERNEL); - if (!info) { - rc = -ENOMEM; - goto out; - } - memcpy(info, &tmp_iface, sizeof(tmp_iface)); - - /* add this new entry to the list */ - kref_init(&info->refcount); - info->is_active = 1; - - cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, ses->iface_count); - cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); - cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, - le32_to_cpu(p->Capability)); - - spin_lock(&ses->iface_lock); - if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) { - list_add_tail(&info->iface_head, &iface->iface_head); - kref_put(&iface->refcount, release_iface); - } else - list_add_tail(&info->iface_head, &ses->iface_list); - - ses->iface_count++; - spin_unlock(&ses->iface_lock); - ses->iface_last_update = jiffies; -next_iface: - nb_iface++; - next = le32_to_cpu(p->Next); - if (!next) { - bytes_left -= sizeof(*p); - break; - } - p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); - bytes_left -= next; - } - - if (!nb_iface) { - cifs_dbg(VFS, "%s: malformed interface info\n", __func__); - rc = -EINVAL; - goto out; - } - - /* Azure rounds the buffer size up 8, to a 16 byte boundary */ - if ((bytes_left > 8) || p->Next) - cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); - - - if (!ses->iface_count) { - rc = -EINVAL; - goto out; - } - -out: - return rc; -} - -int -SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount) -{ - int rc; - unsigned int ret_data_len = 0; - struct network_interface_info_ioctl_rsp *out_buf = NULL; - struct cifs_ses *ses = tcon->ses; - - /* do not query too frequently */ - if (ses->iface_last_update && - time_before(jiffies, ses->iface_last_update + - (SMB_INTERFACE_POLL_INTERVAL * HZ))) - return 0; - - rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, - FSCTL_QUERY_NETWORK_INTERFACE_INFO, - NULL /* no data input */, 0 /* no data input */, - CIFSMaxBufSize, (char **)&out_buf, &ret_data_len); - if (rc == -EOPNOTSUPP) { - cifs_dbg(FYI, - "server does not support query network interfaces\n"); - ret_data_len = 0; - } else if (rc != 0) { - cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc); - goto out; - } - - rc = parse_server_interfaces(out_buf, ret_data_len, ses, in_mount); - if (rc) - goto out; - -out: - kfree(out_buf); - return rc; -} - -static void -smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb) -{ - int rc; - __le16 srch_path = 0; /* Null - open root of share */ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct cached_fid *cfid = NULL; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = "", - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid); - if (rc == 0) - memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid)); - else - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) - return; - - SMB3_request_interfaces(xid, tcon, true /* called during mount */); - - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_ATTRIBUTE_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_DEVICE_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_VOLUME_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ - if (cfid == NULL) - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - else - close_cached_dir(cfid); -} - -static void -smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb) -{ - int rc; - __le16 srch_path = 0; /* Null - open root of share */ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = "", - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) - return; - - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_ATTRIBUTE_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_DEVICE_INFORMATION); - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); -} - -static int -smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path) -{ - __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - int err_buftype = CIFS_NO_BUFFER; - struct cifs_open_parms oparms; - struct kvec err_iov = {}; - struct cifs_fid fid; - struct cached_fid *cfid; - bool islink; - int rc, rc2; - - rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid); - if (!rc) { - if (cfid->has_lease) { - close_cached_dir(cfid); - return 0; - } - close_cached_dir(cfid); - } - - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - &err_iov, &err_buftype); - if (rc) { - struct smb2_hdr *hdr = err_iov.iov_base; - - if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER)) - goto out; - - if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { - rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, - full_path, &islink); - if (rc2) { - rc = rc2; - goto out; - } - if (islink) - rc = -EREMOTE; - } - if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) - rc = -EOPNOTSUPP; - goto out; - } - - rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - -out: - free_rsp_buf(err_buftype, err_iov.iov_base); - kfree(utf16_path); - return rc; -} - -static int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - u64 *uniqueid, struct cifs_open_info_data *data) -{ - *uniqueid = le64_to_cpu(data->fi.IndexNumber); - return 0; -} - -static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct cifs_open_info_data *data) -{ - struct cifs_fid *fid = &cfile->fid; - - if (cfile->symlink_target) { - data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!data->symlink_target) - return -ENOMEM; - } - return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi); -} - -#ifdef CONFIG_CIFS_XATTR -static ssize_t -move_smb2_ea_to_cifs(char *dst, size_t dst_size, - struct smb2_file_full_ea_info *src, size_t src_size, - const unsigned char *ea_name) -{ - int rc = 0; - unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; - char *name, *value; - size_t buf_size = dst_size; - size_t name_len, value_len, user_name_len; - - while (src_size > 0) { - name_len = (size_t)src->ea_name_length; - value_len = (size_t)le16_to_cpu(src->ea_value_length); - - if (name_len == 0) - break; - - if (src_size < 8 + name_len + 1 + value_len) { - cifs_dbg(FYI, "EA entry goes beyond length of list\n"); - rc = -EIO; - goto out; - } - - name = &src->ea_data[0]; - value = &src->ea_data[src->ea_name_length + 1]; - - if (ea_name) { - if (ea_name_len == name_len && - memcmp(ea_name, name, name_len) == 0) { - rc = value_len; - if (dst_size == 0) - goto out; - if (dst_size < value_len) { - rc = -ERANGE; - goto out; - } - memcpy(dst, value, value_len); - goto out; - } - } else { - /* 'user.' plus a terminating null */ - user_name_len = 5 + 1 + name_len; - - if (buf_size == 0) { - /* skip copy - calc size only */ - rc += user_name_len; - } else if (dst_size >= user_name_len) { - dst_size -= user_name_len; - memcpy(dst, "user.", 5); - dst += 5; - memcpy(dst, src->ea_data, name_len); - dst += name_len; - *dst = 0; - ++dst; - rc += user_name_len; - } else { - /* stop before overrun buffer */ - rc = -ERANGE; - break; - } - } - - if (!src->next_entry_offset) - break; - - if (src_size < le32_to_cpu(src->next_entry_offset)) { - /* stop before overrun buffer */ - rc = -ERANGE; - break; - } - src_size -= le32_to_cpu(src->next_entry_offset); - src = (void *)((char *)src + - le32_to_cpu(src->next_entry_offset)); - } - - /* didn't find the named attribute */ - if (ea_name) - rc = -ENODATA; - -out: - return (ssize_t)rc; -} - -static ssize_t -smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *path, const unsigned char *ea_name, - char *ea_data, size_t buf_size, - struct cifs_sb_info *cifs_sb) -{ - int rc; - struct kvec rsp_iov = {NULL, 0}; - int buftype = CIFS_NO_BUFFER; - struct smb2_query_info_rsp *rsp; - struct smb2_file_full_ea_info *info = NULL; - - rc = smb2_query_info_compound(xid, tcon, path, - FILE_READ_EA, - FILE_FULL_EA_INFORMATION, - SMB2_O_INFO_FILE, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE, - &rsp_iov, &buftype, cifs_sb); - if (rc) { - /* - * If ea_name is NULL (listxattr) and there are no EAs, - * return 0 as it's not an error. Otherwise, the specified - * ea_name was not found. - */ - if (!ea_name && rc == -ENODATA) - rc = 0; - goto qeas_exit; - } - - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), - &rsp_iov, - sizeof(struct smb2_file_full_ea_info)); - if (rc) - goto qeas_exit; - - info = (struct smb2_file_full_ea_info *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, - le32_to_cpu(rsp->OutputBufferLength), ea_name); - - qeas_exit: - free_rsp_buf(buftype, rsp_iov.iov_base); - return rc; -} - - -static int -smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, const char *ea_name, const void *ea_value, - const __u16 ea_value_len, const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - __le16 *utf16_path = NULL; - int ea_name_len = strlen(ea_name); - int flags = CIFS_CP_CREATE_CLOSE_OP; - int len; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct cifs_open_parms oparms; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_fid fid; - struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; - unsigned int size[1]; - void *data[1]; - struct smb2_file_full_ea_info *ea = NULL; - struct kvec close_iov[1]; - struct smb2_query_info_rsp *rsp; - int rc, used_len = 0; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - if (ea_name_len > 255) - return -EINVAL; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - if (ses->server->ops->query_all_EAs) { - if (!ea_value) { - rc = ses->server->ops->query_all_EAs(xid, tcon, path, - ea_name, NULL, 0, - cifs_sb); - if (rc == -ENODATA) - goto sea_exit; - } else { - /* If we are adding a attribute we should first check - * if there will be enough space available to store - * the new EA. If not we should not add it since we - * would not be able to even read the EAs back. - */ - rc = smb2_query_info_compound(xid, tcon, path, - FILE_READ_EA, - FILE_FULL_EA_INFORMATION, - SMB2_O_INFO_FILE, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE, - &rsp_iov[1], &resp_buftype[1], cifs_sb); - if (rc == 0) { - rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; - used_len = le32_to_cpu(rsp->OutputBufferLength); - } - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - resp_buftype[1] = CIFS_NO_BUFFER; - memset(&rsp_iov[1], 0, sizeof(rsp_iov[1])); - rc = 0; - - /* Use a fudge factor of 256 bytes in case we collide - * with a different set_EAs command. - */ - if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < - used_len + ea_name_len + ea_value_len + 1) { - rc = -ENOSPC; - goto sea_exit; - } - } - } - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = FILE_WRITE_EA, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto sea_exit; - smb2_set_next_command(tcon, &rqst[0]); - - - /* Set Info */ - memset(&si_iov, 0, sizeof(si_iov)); - rqst[1].rq_iov = si_iov; - rqst[1].rq_nvec = 1; - - len = sizeof(*ea) + ea_name_len + ea_value_len + 1; - ea = kzalloc(len, GFP_KERNEL); - if (ea == NULL) { - rc = -ENOMEM; - goto sea_exit; - } - - ea->ea_name_length = ea_name_len; - ea->ea_value_length = cpu_to_le16(ea_value_len); - memcpy(ea->ea_data, ea_name, ea_name_len + 1); - memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); - - size[0] = len; - data[0] = ea; - - rc = SMB2_set_info_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_FULL_EA_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (rc) - goto sea_exit; - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - - /* Close */ - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto sea_exit; - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - /* no need to bump num_remote_opens because handle immediately closed */ - - sea_exit: - kfree(ea); - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_set_info_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - return rc; -} -#endif - -static bool -smb2_can_echo(struct TCP_Server_Info *server) -{ - return server->echoes; -} - -static void -smb2_clear_stats(struct cifs_tcon *tcon) -{ - int i; - - for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { - atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); - atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); - } -} - -static void -smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) -{ - seq_puts(m, "\n\tShare Capabilities:"); - if (tcon->capabilities & SMB2_SHARE_CAP_DFS) - seq_puts(m, " DFS,"); - if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) - seq_puts(m, " CONTINUOUS AVAILABILITY,"); - if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) - seq_puts(m, " SCALEOUT,"); - if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) - seq_puts(m, " CLUSTER,"); - if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) - seq_puts(m, " ASYMMETRIC,"); - if (tcon->capabilities == 0) - seq_puts(m, " None"); - if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) - seq_puts(m, " Aligned,"); - if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) - seq_puts(m, " Partition Aligned,"); - if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) - seq_puts(m, " SSD,"); - if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) - seq_puts(m, " TRIM-support,"); - - seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); - seq_printf(m, "\n\ttid: 0x%x", tcon->tid); - if (tcon->perf_sector_size) - seq_printf(m, "\tOptimal sector size: 0x%x", - tcon->perf_sector_size); - seq_printf(m, "\tMaximal Access: 0x%x", tcon->maximal_access); -} - -static void -smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) -{ - atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; - atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; - - /* - * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO - * totals (requests sent) since those SMBs are per-session not per tcon - */ - seq_printf(m, "\nBytes read: %llu Bytes written: %llu", - (long long)(tcon->bytes_read), - (long long)(tcon->bytes_written)); - seq_printf(m, "\nOpen files: %d total (local), %d open on server", - atomic_read(&tcon->num_local_opens), - atomic_read(&tcon->num_remote_opens)); - seq_printf(m, "\nTreeConnects: %d total %d failed", - atomic_read(&sent[SMB2_TREE_CONNECT_HE]), - atomic_read(&failed[SMB2_TREE_CONNECT_HE])); - seq_printf(m, "\nTreeDisconnects: %d total %d failed", - atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), - atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); - seq_printf(m, "\nCreates: %d total %d failed", - atomic_read(&sent[SMB2_CREATE_HE]), - atomic_read(&failed[SMB2_CREATE_HE])); - seq_printf(m, "\nCloses: %d total %d failed", - atomic_read(&sent[SMB2_CLOSE_HE]), - atomic_read(&failed[SMB2_CLOSE_HE])); - seq_printf(m, "\nFlushes: %d total %d failed", - atomic_read(&sent[SMB2_FLUSH_HE]), - atomic_read(&failed[SMB2_FLUSH_HE])); - seq_printf(m, "\nReads: %d total %d failed", - atomic_read(&sent[SMB2_READ_HE]), - atomic_read(&failed[SMB2_READ_HE])); - seq_printf(m, "\nWrites: %d total %d failed", - atomic_read(&sent[SMB2_WRITE_HE]), - atomic_read(&failed[SMB2_WRITE_HE])); - seq_printf(m, "\nLocks: %d total %d failed", - atomic_read(&sent[SMB2_LOCK_HE]), - atomic_read(&failed[SMB2_LOCK_HE])); - seq_printf(m, "\nIOCTLs: %d total %d failed", - atomic_read(&sent[SMB2_IOCTL_HE]), - atomic_read(&failed[SMB2_IOCTL_HE])); - seq_printf(m, "\nQueryDirectories: %d total %d failed", - atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), - atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); - seq_printf(m, "\nChangeNotifies: %d total %d failed", - atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), - atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); - seq_printf(m, "\nQueryInfos: %d total %d failed", - atomic_read(&sent[SMB2_QUERY_INFO_HE]), - atomic_read(&failed[SMB2_QUERY_INFO_HE])); - seq_printf(m, "\nSetInfos: %d total %d failed", - atomic_read(&sent[SMB2_SET_INFO_HE]), - atomic_read(&failed[SMB2_SET_INFO_HE])); - seq_printf(m, "\nOplockBreaks: %d sent %d failed", - atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), - atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); -} - -static void -smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - - cfile->fid.persistent_fid = fid->persistent_fid; - cfile->fid.volatile_fid = fid->volatile_fid; - cfile->fid.access = fid->access; -#ifdef CONFIG_CIFS_DEBUG2 - cfile->fid.mid = fid->mid; -#endif /* CIFS_DEBUG2 */ - server->ops->set_oplock_level(cinode, oplock, fid->epoch, - &fid->purge_cache); - cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); - memcpy(cfile->fid.create_guid, fid->create_guid, 16); -} - -static void -smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); -} - -static void -smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - struct smb2_file_network_open_info file_inf; - struct inode *inode; - int rc; - - rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, &file_inf); - if (rc) - return; - - inode = d_inode(cfile->dentry); - - spin_lock(&inode->i_lock); - CIFS_I(inode)->time = jiffies; - - /* Creation time should not need to be updated on close */ - if (file_inf.LastWriteTime) - inode->i_mtime = cifs_NTtimeToUnix(file_inf.LastWriteTime); - if (file_inf.ChangeTime) - inode->i_ctime = cifs_NTtimeToUnix(file_inf.ChangeTime); - if (file_inf.LastAccessTime) - inode->i_atime = cifs_NTtimeToUnix(file_inf.LastAccessTime); - - /* - * i_blocks is not related to (i_size / i_blksize), - * but instead 512 byte (2**9) size is required for - * calculating num blocks. - */ - if (le64_to_cpu(file_inf.AllocationSize) > 4096) - inode->i_blocks = - (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9; - - /* End of file and Attributes should not have to be updated on close */ - spin_unlock(&inode->i_lock); -} - -static int -SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct copychunk_ioctl *pcchunk) -{ - int rc; - unsigned int ret_data_len; - struct resume_key_req *res_key; - - rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, - FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */, - CIFSMaxBufSize, (char **)&res_key, &ret_data_len); - - if (rc == -EOPNOTSUPP) { - pr_warn_once("Server share %s does not support copy range\n", tcon->tree_name); - goto req_res_key_exit; - } else if (rc) { - cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); - goto req_res_key_exit; - } - if (ret_data_len < sizeof(struct resume_key_req)) { - cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n"); - rc = -EINVAL; - goto req_res_key_exit; - } - memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); - -req_res_key_exit: - kfree(res_key); - return rc; -} - -struct iqi_vars { - struct smb_rqst rqst[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; - struct kvec close_iov[1]; -}; - -static int -smb2_ioctl_query_info(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - __le16 *path, int is_dir, - unsigned long p) -{ - struct iqi_vars *vars; - struct smb_rqst *rqst; - struct kvec *rsp_iov; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - char __user *arg = (char __user *)p; - struct smb_query_info qi; - struct smb_query_info __user *pqi; - int rc = 0; - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb2_query_info_rsp *qi_rsp = NULL; - struct smb2_ioctl_rsp *io_rsp = NULL; - void *buffer = NULL; - int resp_buftype[3]; - struct cifs_open_parms oparms; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_fid fid; - unsigned int size[2]; - void *data[2]; - int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; - void (*free_req1_func)(struct smb_rqst *r); - - vars = kzalloc(sizeof(*vars), GFP_ATOMIC); - if (vars == NULL) - return -ENOMEM; - rqst = &vars->rqst[0]; - rsp_iov = &vars->rsp_iov[0]; - - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - - if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) { - rc = -EFAULT; - goto free_vars; - } - if (qi.output_buffer_length > 1024) { - rc = -EINVAL; - goto free_vars; - } - - if (!ses || !server) { - rc = -EIO; - goto free_vars; - } - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - if (qi.output_buffer_length) { - buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length); - if (IS_ERR(buffer)) { - rc = PTR_ERR(buffer); - goto free_vars; - } - } - - /* Open */ - rqst[0].rq_iov = &vars->open_iov[0]; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, create_options), - .fid = &fid, - }; - - if (qi.flags & PASSTHRU_FSCTL) { - switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { - case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: - oparms.desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE; - break; - case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS: - oparms.desired_access = GENERIC_ALL; - break; - case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS: - oparms.desired_access = GENERIC_READ; - break; - case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS: - oparms.desired_access = GENERIC_WRITE; - break; - } - } else if (qi.flags & PASSTHRU_SET_INFO) { - oparms.desired_access = GENERIC_WRITE; - } else { - oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; - } - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, path); - if (rc) - goto free_output_buffer; - smb2_set_next_command(tcon, &rqst[0]); - - /* Query */ - if (qi.flags & PASSTHRU_FSCTL) { - /* Can eventually relax perm check since server enforces too */ - if (!capable(CAP_SYS_ADMIN)) { - rc = -EPERM; - goto free_open_req; - } - rqst[1].rq_iov = &vars->io_iov[0]; - rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, - qi.info_type, buffer, qi.output_buffer_length, - CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE); - free_req1_func = SMB2_ioctl_free; - } else if (qi.flags == PASSTHRU_SET_INFO) { - /* Can eventually relax perm check since server enforces too */ - if (!capable(CAP_SYS_ADMIN)) { - rc = -EPERM; - goto free_open_req; - } - if (qi.output_buffer_length < 8) { - rc = -EINVAL; - goto free_open_req; - } - rqst[1].rq_iov = &vars->si_iov[0]; - rqst[1].rq_nvec = 1; - - /* MS-FSCC 2.4.13 FileEndOfFileInformation */ - size[0] = 8; - data[0] = buffer; - - rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, - current->tgid, FILE_END_OF_FILE_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - free_req1_func = SMB2_set_info_free; - } else if (qi.flags == PASSTHRU_QUERY_INFO) { - rqst[1].rq_iov = &vars->qi_iov[0]; - rqst[1].rq_nvec = 1; - - rc = SMB2_query_info_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, qi.file_info_class, - qi.info_type, qi.additional_information, - qi.input_buffer_length, - qi.output_buffer_length, buffer); - free_req1_func = SMB2_query_info_free; - } else { /* unknown flags */ - cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n", - qi.flags); - rc = -EINVAL; - } - - if (rc) - goto free_open_req; - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - /* Close */ - rqst[2].rq_iov = &vars->close_iov[0]; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto free_req_1; - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - if (rc) - goto out; - - /* No need to bump num_remote_opens since handle immediately closed */ - if (qi.flags & PASSTHRU_FSCTL) { - pqi = (struct smb_query_info __user *)arg; - io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base; - if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) - qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); - if (qi.input_buffer_length > 0 && - le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length - > rsp_iov[1].iov_len) { - rc = -EFAULT; - goto out; - } - - if (copy_to_user(&pqi->input_buffer_length, - &qi.input_buffer_length, - sizeof(qi.input_buffer_length))) { - rc = -EFAULT; - goto out; - } - - if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info), - (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset), - qi.input_buffer_length)) - rc = -EFAULT; - } else { - pqi = (struct smb_query_info __user *)arg; - qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; - if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) - qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); - if (copy_to_user(&pqi->input_buffer_length, - &qi.input_buffer_length, - sizeof(qi.input_buffer_length))) { - rc = -EFAULT; - goto out; - } - - if (copy_to_user(pqi + 1, qi_rsp->Buffer, - qi.input_buffer_length)) - rc = -EFAULT; - } - -out: - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - SMB2_close_free(&rqst[2]); -free_req_1: - free_req1_func(&rqst[1]); -free_open_req: - SMB2_open_free(&rqst[0]); -free_output_buffer: - kfree(buffer); -free_vars: - kfree(vars); - return rc; -} - -static ssize_t -smb2_copychunk_range(const unsigned int xid, - struct cifsFileInfo *srcfile, - struct cifsFileInfo *trgtfile, u64 src_off, - u64 len, u64 dest_off) -{ - int rc; - unsigned int ret_data_len; - struct copychunk_ioctl *pcchunk; - struct copychunk_ioctl_rsp *retbuf = NULL; - struct cifs_tcon *tcon; - int chunks_copied = 0; - bool chunk_sizes_updated = false; - ssize_t bytes_written, total_bytes_written = 0; - - pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); - if (pcchunk == NULL) - return -ENOMEM; - - cifs_dbg(FYI, "%s: about to call request res key\n", __func__); - /* Request a key from the server to identify the source of the copy */ - rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), - srcfile->fid.persistent_fid, - srcfile->fid.volatile_fid, pcchunk); - - /* Note: request_res_key sets res_key null only if rc !=0 */ - if (rc) - goto cchunk_out; - - /* For now array only one chunk long, will make more flexible later */ - pcchunk->ChunkCount = cpu_to_le32(1); - pcchunk->Reserved = 0; - pcchunk->Reserved2 = 0; - - tcon = tlink_tcon(trgtfile->tlink); - - while (len > 0) { - pcchunk->SourceOffset = cpu_to_le64(src_off); - pcchunk->TargetOffset = cpu_to_le64(dest_off); - pcchunk->Length = - cpu_to_le32(min_t(u64, len, tcon->max_bytes_chunk)); - - /* Request server copy to target from src identified by key */ - kfree(retbuf); - retbuf = NULL; - rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, - trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, - (char *)pcchunk, sizeof(struct copychunk_ioctl), - CIFSMaxBufSize, (char **)&retbuf, &ret_data_len); - if (rc == 0) { - if (ret_data_len != - sizeof(struct copychunk_ioctl_rsp)) { - cifs_tcon_dbg(VFS, "Invalid cchunk response size\n"); - rc = -EIO; - goto cchunk_out; - } - if (retbuf->TotalBytesWritten == 0) { - cifs_dbg(FYI, "no bytes copied\n"); - rc = -EIO; - goto cchunk_out; - } - /* - * Check if server claimed to write more than we asked - */ - if (le32_to_cpu(retbuf->TotalBytesWritten) > - le32_to_cpu(pcchunk->Length)) { - cifs_tcon_dbg(VFS, "Invalid copy chunk response\n"); - rc = -EIO; - goto cchunk_out; - } - if (le32_to_cpu(retbuf->ChunksWritten) != 1) { - cifs_tcon_dbg(VFS, "Invalid num chunks written\n"); - rc = -EIO; - goto cchunk_out; - } - chunks_copied++; - - bytes_written = le32_to_cpu(retbuf->TotalBytesWritten); - src_off += bytes_written; - dest_off += bytes_written; - len -= bytes_written; - total_bytes_written += bytes_written; - - cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n", - le32_to_cpu(retbuf->ChunksWritten), - le32_to_cpu(retbuf->ChunkBytesWritten), - bytes_written); - } else if (rc == -EINVAL) { - if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) - goto cchunk_out; - - cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", - le32_to_cpu(retbuf->ChunksWritten), - le32_to_cpu(retbuf->ChunkBytesWritten), - le32_to_cpu(retbuf->TotalBytesWritten)); - - /* - * Check if this is the first request using these sizes, - * (ie check if copy succeed once with original sizes - * and check if the server gave us different sizes after - * we already updated max sizes on previous request). - * if not then why is the server returning an error now - */ - if ((chunks_copied != 0) || chunk_sizes_updated) - goto cchunk_out; - - /* Check that server is not asking us to grow size */ - if (le32_to_cpu(retbuf->ChunkBytesWritten) < - tcon->max_bytes_chunk) - tcon->max_bytes_chunk = - le32_to_cpu(retbuf->ChunkBytesWritten); - else - goto cchunk_out; /* server gave us bogus size */ - - /* No need to change MaxChunks since already set to 1 */ - chunk_sizes_updated = true; - } else - goto cchunk_out; - } - -cchunk_out: - kfree(pcchunk); - kfree(retbuf); - if (rc) - return rc; - else - return total_bytes_written; -} - -static int -smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); -} - -static unsigned int -smb2_read_data_offset(char *buf) -{ - struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; - - return rsp->DataOffset; -} - -static unsigned int -smb2_read_data_length(char *buf, bool in_remaining) -{ - struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; - - if (in_remaining) - return le32_to_cpu(rsp->DataRemaining); - - return le32_to_cpu(rsp->DataLength); -} - - -static int -smb2_sync_read(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *bytes_read, - char **buf, int *buf_type) -{ - parms->persistent_fid = pfid->persistent_fid; - parms->volatile_fid = pfid->volatile_fid; - return SMB2_read(xid, parms, bytes_read, buf, buf_type); -} - -static int -smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *written, - struct kvec *iov, unsigned long nr_segs) -{ - - parms->persistent_fid = pfid->persistent_fid; - parms->volatile_fid = pfid->volatile_fid; - return SMB2_write(xid, parms, written, iov, nr_segs); -} - -/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ -static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) -{ - struct cifsInodeInfo *cifsi; - int rc; - - cifsi = CIFS_I(inode); - - /* if file already sparse don't bother setting sparse again */ - if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) - return true; /* already sparse */ - - if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) - return true; /* already not sparse */ - - /* - * Can't check for sparse support on share the usual way via the - * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share - * since Samba server doesn't set the flag on the share, yet - * supports the set sparse FSCTL and returns sparse correctly - * in the file attributes. If we fail setting sparse though we - * mark that server does not support sparse files for this share - * to avoid repeatedly sending the unsupported fsctl to server - * if the file is repeatedly extended. - */ - if (tcon->broken_sparse_sup) - return false; - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, FSCTL_SET_SPARSE, - &setsparse, 1, CIFSMaxBufSize, NULL, NULL); - if (rc) { - tcon->broken_sparse_sup = true; - cifs_dbg(FYI, "set sparse rc = %d\n", rc); - return false; - } - - if (setsparse) - cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; - else - cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); - - return true; -} - -static int -smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, __u64 size, bool set_alloc) -{ - __le64 eof = cpu_to_le64(size); - struct inode *inode; - - /* - * If extending file more than one page make sparse. Many Linux fs - * make files sparse by default when extending via ftruncate - */ - inode = d_inode(cfile->dentry); - - if (!set_alloc && (size > inode->i_size + 8192)) { - __u8 set_sparse = 1; - - /* whether set sparse succeeds or not, extend the file */ - smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); - } - - return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); -} - -static int -smb2_duplicate_extents(const unsigned int xid, - struct cifsFileInfo *srcfile, - struct cifsFileInfo *trgtfile, u64 src_off, - u64 len, u64 dest_off) -{ - int rc; - unsigned int ret_data_len; - struct inode *inode; - struct duplicate_extents_to_file dup_ext_buf; - struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); - - /* server fileays advertise duplicate extent support with this flag */ - if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & - FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) - return -EOPNOTSUPP; - - dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid; - dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid; - dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); - dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); - dup_ext_buf.ByteCount = cpu_to_le64(len); - cifs_dbg(FYI, "Duplicate extents: src off %lld dst off %lld len %lld\n", - src_off, dest_off, len); - - inode = d_inode(trgtfile->dentry); - if (inode->i_size < dest_off + len) { - rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); - if (rc) - goto duplicate_extents_out; - - /* - * Although also could set plausible allocation size (i_blocks) - * here in addition to setting the file size, in reflink - * it is likely that the target file is sparse. Its allocation - * size will be queried on next revalidate, but it is important - * to make sure that file's cached size is updated immediately - */ - cifs_setsize(inode, dest_off + len); - } - rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, - trgtfile->fid.volatile_fid, - FSCTL_DUPLICATE_EXTENTS_TO_FILE, - (char *)&dup_ext_buf, - sizeof(struct duplicate_extents_to_file), - CIFSMaxBufSize, NULL, - &ret_data_len); - - if (ret_data_len > 0) - cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); - -duplicate_extents_out: - return rc; -} - -static int -smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid); -} - -static int -smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - struct fsctl_set_integrity_information_req integr_info; - unsigned int ret_data_len; - - integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); - integr_info.Flags = 0; - integr_info.Reserved = 0; - - return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_SET_INTEGRITY_INFORMATION, - (char *)&integr_info, - sizeof(struct fsctl_set_integrity_information_req), - CIFSMaxBufSize, NULL, - &ret_data_len); - -} - -/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */ -#define GMT_TOKEN_SIZE 50 - -#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */ - -/* - * Input buffer contains (empty) struct smb_snapshot array with size filled in - * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2 - */ -static int -smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, void __user *ioc_buf) -{ - char *retbuf = NULL; - unsigned int ret_data_len = 0; - int rc; - u32 max_response_size; - struct smb_snapshot_array snapshot_in; - - /* - * On the first query to enumerate the list of snapshots available - * for this volume the buffer begins with 0 (number of snapshots - * which can be returned is zero since at that point we do not know - * how big the buffer needs to be). On the second query, - * it (ret_data_len) is set to number of snapshots so we can - * know to set the maximum response size larger (see below). - */ - if (get_user(ret_data_len, (unsigned int __user *)ioc_buf)) - return -EFAULT; - - /* - * Note that for snapshot queries that servers like Azure expect that - * the first query be minimal size (and just used to get the number/size - * of previous versions) so response size must be specified as EXACTLY - * sizeof(struct snapshot_array) which is 16 when rounded up to multiple - * of eight bytes. - */ - if (ret_data_len == 0) - max_response_size = MIN_SNAPSHOT_ARRAY_SIZE; - else - max_response_size = CIFSMaxBufSize; - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_SRV_ENUMERATE_SNAPSHOTS, - NULL, 0 /* no input data */, max_response_size, - (char **)&retbuf, - &ret_data_len); - cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", - rc, ret_data_len); - if (rc) - return rc; - - if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) { - /* Fixup buffer */ - if (copy_from_user(&snapshot_in, ioc_buf, - sizeof(struct smb_snapshot_array))) { - rc = -EFAULT; - kfree(retbuf); - return rc; - } - - /* - * Check for min size, ie not large enough to fit even one GMT - * token (snapshot). On the first ioctl some users may pass in - * smaller size (or zero) to simply get the size of the array - * so the user space caller can allocate sufficient memory - * and retry the ioctl again with larger array size sufficient - * to hold all of the snapshot GMT tokens on the second try. - */ - if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) - ret_data_len = sizeof(struct smb_snapshot_array); - - /* - * We return struct SRV_SNAPSHOT_ARRAY, followed by - * the snapshot array (of 50 byte GMT tokens) each - * representing an available previous version of the data - */ - if (ret_data_len > (snapshot_in.snapshot_array_size + - sizeof(struct smb_snapshot_array))) - ret_data_len = snapshot_in.snapshot_array_size + - sizeof(struct smb_snapshot_array); - - if (copy_to_user(ioc_buf, retbuf, ret_data_len)) - rc = -EFAULT; - } - - kfree(retbuf); - return rc; -} - - - -static int -smb3_notify(const unsigned int xid, struct file *pfile, - void __user *ioc_buf, bool return_changes) -{ - struct smb3_notify_info notify; - struct smb3_notify_info __user *pnotify_buf; - struct dentry *dentry = pfile->f_path.dentry; - struct inode *inode = file_inode(pfile); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct cifs_tcon *tcon; - const unsigned char *path; - char *returned_ioctl_info = NULL; - void *page = alloc_dentry_path(); - __le16 *utf16_path = NULL; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - int rc = 0; - __u32 ret_len = 0; - - path = build_path_from_dentry(dentry, page); - if (IS_ERR(path)) { - rc = PTR_ERR(path); - goto notify_exit; - } - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (utf16_path == NULL) { - rc = -ENOMEM; - goto notify_exit; - } - - if (return_changes) { - if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify_info))) { - rc = -EFAULT; - goto notify_exit; - } - } else { - if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) { - rc = -EFAULT; - goto notify_exit; - } - notify.data_len = 0; - } - - tcon = cifs_sb_master_tcon(cifs_sb); - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, - NULL); - if (rc) - goto notify_exit; - - rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid, - notify.watch_tree, notify.completion_filter, - notify.data_len, &returned_ioctl_info, &ret_len); - - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - - cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc); - if (return_changes && (ret_len > 0) && (notify.data_len > 0)) { - if (ret_len > notify.data_len) - ret_len = notify.data_len; - pnotify_buf = (struct smb3_notify_info __user *)ioc_buf; - if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len)) - rc = -EFAULT; - else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len))) - rc = -EFAULT; - } - kfree(returned_ioctl_info); -notify_exit: - free_dentry_path(page); - kfree(utf16_path); - return rc; -} - -static int -smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, struct cifs_sb_info *cifs_sb, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - __le16 *utf16_path; - struct smb_rqst rqst[2]; - struct kvec rsp_iov[2]; - int resp_buftype[2]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qd_iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; - int rc, flags = 0; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct smb2_query_directory_rsp *qd_rsp = NULL; - struct smb2_create_rsp *op_rsp = NULL; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - int retry_count = 0; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto qdf_free; - smb2_set_next_command(tcon, &rqst[0]); - - /* Query directory */ - srch_inf->entries_in_buffer = 0; - srch_inf->index_of_last_entry = 2; - - memset(&qd_iov, 0, sizeof(qd_iov)); - rqst[1].rq_iov = qd_iov; - rqst[1].rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; - - rc = SMB2_query_directory_init(xid, tcon, server, - &rqst[1], - COMPOUND_FID, COMPOUND_FID, - 0, srch_inf->info_level); - if (rc) - goto qdf_free; - - smb2_set_related(&rqst[1]); - -again: - rc = compound_send_recv(xid, tcon->ses, server, - flags, 2, rqst, - resp_buftype, rsp_iov); - - if (rc == -EAGAIN && retry_count++ < 10) - goto again; - - /* If the open failed there is nothing to do */ - op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; - if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) { - cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc); - goto qdf_free; - } - fid->persistent_fid = op_rsp->PersistentFileId; - fid->volatile_fid = op_rsp->VolatileFileId; - - /* Anything else than ENODATA means a genuine error */ - if (rc && rc != -ENODATA) { - SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); - cifs_dbg(FYI, "query_dir_first: query directory failed rc=%d\n", rc); - trace_smb3_query_dir_err(xid, fid->persistent_fid, - tcon->tid, tcon->ses->Suid, 0, 0, rc); - goto qdf_free; - } - - atomic_inc(&tcon->num_remote_opens); - - qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; - if (qd_rsp->hdr.Status == STATUS_NO_MORE_FILES) { - trace_smb3_query_dir_done(xid, fid->persistent_fid, - tcon->tid, tcon->ses->Suid, 0, 0); - srch_inf->endOfSearch = true; - rc = 0; - goto qdf_free; - } - - rc = smb2_parse_query_directory(tcon, &rsp_iov[1], resp_buftype[1], - srch_inf); - if (rc) { - trace_smb3_query_dir_err(xid, fid->persistent_fid, tcon->tid, - tcon->ses->Suid, 0, 0, rc); - goto qdf_free; - } - resp_buftype[1] = CIFS_NO_BUFFER; - - trace_smb3_query_dir_done(xid, fid->persistent_fid, tcon->tid, - tcon->ses->Suid, 0, srch_inf->entries_in_buffer); - - qdf_free: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_query_directory_free(&rqst[1]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - return rc; -} - -static int -smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - return SMB2_query_directory(xid, tcon, fid->persistent_fid, - fid->volatile_fid, 0, srch_inf); -} - -static int -smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); -} - -/* - * If we negotiate SMB2 protocol and get STATUS_PENDING - update - * the number of credits and return true. Otherwise - return false. - */ -static bool -smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - int scredits, in_flight; - - if (shdr->Status != STATUS_PENDING) - return false; - - if (shdr->CreditRequest) { - spin_lock(&server->req_lock); - server->credits += le16_to_cpu(shdr->CreditRequest); - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - trace_smb3_pend_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - le16_to_cpu(shdr->CreditRequest), in_flight); - cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n", - __func__, le16_to_cpu(shdr->CreditRequest), scredits); - } - - return true; -} - -static bool -smb2_is_session_expired(char *buf) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - - if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED && - shdr->Status != STATUS_USER_SESSION_DELETED) - return false; - - trace_smb3_ses_expired(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), - le64_to_cpu(shdr->MessageId)); - cifs_dbg(FYI, "Session expired or deleted\n"); - - return true; -} - -static bool -smb2_is_status_io_timeout(char *buf) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - - if (shdr->Status == STATUS_IO_TIMEOUT) - return true; - else - return false; -} - -static void -smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - if (shdr->Status != STATUS_NETWORK_NAME_DELETED) - return; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) { - spin_lock(&tcon->tc_lock); - tcon->need_reconnect = true; - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - pr_warn_once("Server share %s deleted.\n", - tcon->tree_name); - return; - } - } - } - spin_unlock(&cifs_tcp_ses_lock); -} - -static int -smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, - __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) -{ - if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) - return SMB2_lease_break(0, tcon, cinode->lease_key, - smb2_get_lease_state(cinode)); - - return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid, - CIFS_CACHE_READ(cinode) ? 1 : 0); -} - -void -smb2_set_related(struct smb_rqst *rqst) -{ - struct smb2_hdr *shdr; - - shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); - if (shdr == NULL) { - cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); - return; - } - shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; -} - -char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; - -void -smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) -{ - struct smb2_hdr *shdr; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = ses->server; - unsigned long len = smb_rqst_len(server, rqst); - int i, num_padding; - - shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); - if (shdr == NULL) { - cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n"); - return; - } - - /* SMB headers in a compound are 8 byte aligned. */ - - /* No padding needed */ - if (!(len & 7)) - goto finished; - - num_padding = 8 - (len & 7); - if (!smb3_encryption_required(tcon)) { - /* - * If we do not have encryption then we can just add an extra - * iov for the padding. - */ - rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; - rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; - rqst->rq_nvec++; - len += num_padding; - } else { - /* - * We can not add a small padding iov for the encryption case - * because the encryption framework can not handle the padding - * iovs. - * We have to flatten this into a single buffer and add - * the padding to it. - */ - for (i = 1; i < rqst->rq_nvec; i++) { - memcpy(rqst->rq_iov[0].iov_base + - rqst->rq_iov[0].iov_len, - rqst->rq_iov[i].iov_base, - rqst->rq_iov[i].iov_len); - rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; - } - memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, - 0, num_padding); - rqst->rq_iov[0].iov_len += num_padding; - len += num_padding; - rqst->rq_nvec = 1; - } - - finished: - shdr->NextCommand = cpu_to_le32(len); -} - -/* - * Passes the query info response back to the caller on success. - * Caller need to free this with free_rsp_buf(). - */ -int -smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, u32 desired_access, - u32 class, u32 type, u32 output_len, - struct kvec *rsp, int *buftype, - struct cifs_sb_info *cifs_sb) -{ - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - struct kvec close_iov[1]; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - int rc; - __le16 *utf16_path; - struct cached_fid *cfid = NULL; - - if (!path) - path = ""; - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - /* - * We can only call this for things we know are directories. - */ - if (!strcmp(path, "")) - open_cached_dir(xid, tcon, path, cifs_sb, false, - &cfid); /* cfid null if open dir failed */ - - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = desired_access, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto qic_exit; - smb2_set_next_command(tcon, &rqst[0]); - - memset(&qi_iov, 0, sizeof(qi_iov)); - rqst[1].rq_iov = qi_iov; - rqst[1].rq_nvec = 1; - - if (cfid) { - rc = SMB2_query_info_init(tcon, server, - &rqst[1], - cfid->fid.persistent_fid, - cfid->fid.volatile_fid, - class, type, 0, - output_len, 0, - NULL); - } else { - rc = SMB2_query_info_init(tcon, server, - &rqst[1], - COMPOUND_FID, - COMPOUND_FID, - class, type, 0, - output_len, 0, - NULL); - } - if (rc) - goto qic_exit; - if (!cfid) { - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - } - - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto qic_exit; - smb2_set_related(&rqst[2]); - - if (cfid) { - rc = compound_send_recv(xid, ses, server, - flags, 1, &rqst[1], - &resp_buftype[1], &rsp_iov[1]); - } else { - rc = compound_send_recv(xid, ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - } - if (rc) { - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - if (rc == -EREMCHG) { - tcon->need_reconnect = true; - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - } - goto qic_exit; - } - *rsp = rsp_iov[1]; - *buftype = resp_buftype[1]; - - qic_exit: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_query_info_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - if (cfid) - close_cached_dir(cfid); - return rc; -} - -static int -smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct kstatfs *buf) -{ - struct smb2_query_info_rsp *rsp; - struct smb2_fs_full_size_info *info = NULL; - struct kvec rsp_iov = {NULL, 0}; - int buftype = CIFS_NO_BUFFER; - int rc; - - - rc = smb2_query_info_compound(xid, tcon, "", - FILE_READ_ATTRIBUTES, - FS_FULL_SIZE_INFORMATION, - SMB2_O_INFO_FILESYSTEM, - sizeof(struct smb2_fs_full_size_info), - &rsp_iov, &buftype, cifs_sb); - if (rc) - goto qfs_exit; - - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - buf->f_type = SMB2_SUPER_MAGIC; - info = (struct smb2_fs_full_size_info *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), - &rsp_iov, - sizeof(struct smb2_fs_full_size_info)); - if (!rc) - smb2_copy_fs_info_to_kstatfs(info, buf); - -qfs_exit: - free_rsp_buf(buftype, rsp_iov.iov_base); - return rc; -} - -static int -smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct kstatfs *buf) -{ - int rc; - __le16 srch_path = 0; /* Null - open root of share */ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - - if (!tcon->posix_extensions) - return smb2_queryfs(xid, tcon, cifs_sb, buf); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = "", - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) - return rc; - - rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, - fid.volatile_fid, buf); - buf->f_type = SMB2_SUPER_MAGIC; - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - return rc; -} - -static bool -smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) -{ - return ob1->fid.persistent_fid == ob2->fid.persistent_fid && - ob1->fid.volatile_fid == ob2->fid.volatile_fid; -} - -static int -smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u32 type, int lock, int unlock, bool wait) -{ - if (unlock && !lock) - type = SMB2_LOCKFLAG_UNLOCK; - return SMB2_lock(xid, tlink_tcon(cfile->tlink), - cfile->fid.persistent_fid, cfile->fid.volatile_fid, - current->tgid, length, offset, type, wait); -} - -static void -smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) -{ - memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); -} - -static void -smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) -{ - memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); -} - -static void -smb2_new_lease_key(struct cifs_fid *fid) -{ - generate_random_uuid(fid->lease_key); -} - -static int -smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, - const char *search_name, - struct dfs_info3_param **target_nodes, - unsigned int *num_of_nodes, - const struct nls_table *nls_codepage, int remap) -{ - int rc; - __le16 *utf16_path = NULL; - int utf16_path_len = 0; - struct cifs_tcon *tcon; - struct fsctl_get_dfs_referral_req *dfs_req = NULL; - struct get_dfs_referral_rsp *dfs_rsp = NULL; - u32 dfs_req_size = 0, dfs_rsp_size = 0; - int retry_count = 0; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); - - /* - * Try to use the IPC tcon, otherwise just use any - */ - tcon = ses->tcon_ipc; - if (tcon == NULL) { - spin_lock(&cifs_tcp_ses_lock); - tcon = list_first_entry_or_null(&ses->tcon_list, - struct cifs_tcon, - tcon_list); - if (tcon) - tcon->tc_count++; - spin_unlock(&cifs_tcp_ses_lock); - } - - if (tcon == NULL) { - cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n", - ses); - rc = -ENOTCONN; - goto out; - } - - utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX, - &utf16_path_len, - nls_codepage, remap); - if (!utf16_path) { - rc = -ENOMEM; - goto out; - } - - dfs_req_size = sizeof(*dfs_req) + utf16_path_len; - dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); - if (!dfs_req) { - rc = -ENOMEM; - goto out; - } - - /* Highest DFS referral version understood */ - dfs_req->MaxReferralLevel = DFS_VERSION; - - /* Path to resolve in an UTF-16 null-terminated string */ - memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); - - do { - rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, - FSCTL_DFS_GET_REFERRALS, - (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, - (char **)&dfs_rsp, &dfs_rsp_size); - if (!is_retryable_error(rc)) - break; - usleep_range(512, 2048); - } while (++retry_count < 5); - - if (rc) { - if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) - cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); - goto out; - } - - rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size, - num_of_nodes, target_nodes, - nls_codepage, remap, search_name, - true /* is_unicode */); - if (rc) { - cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); - goto out; - } - - out: - if (tcon && !tcon->ipc) { - /* ipc tcons are not refcounted */ - spin_lock(&cifs_tcp_ses_lock); - tcon->tc_count--; - /* tc_count can never go negative */ - WARN_ON(tcon->tc_count < 0); - spin_unlock(&cifs_tcp_ses_lock); - } - kfree(utf16_path); - kfree(dfs_req); - kfree(dfs_rsp); - return rc; -} - -static int -parse_reparse_posix(struct reparse_posix_data *symlink_buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) -{ - unsigned int len; - - /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ - len = le16_to_cpu(symlink_buf->ReparseDataLength); - - if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) { - cifs_dbg(VFS, "%lld not a supported symlink type\n", - le64_to_cpu(symlink_buf->InodeType)); - return -EOPNOTSUPP; - } - - *target_path = cifs_strndup_from_utf16( - symlink_buf->PathBuffer, - len, true, cifs_sb->local_nls); - if (!(*target_path)) - return -ENOMEM; - - convert_delimiter(*target_path, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); - - return 0; -} - -static int -parse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) -{ - unsigned int sub_len; - unsigned int sub_offset; - - /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ - - sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset); - sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength); - if (sub_offset + 20 > plen || - sub_offset + sub_len + 20 > plen) { - cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); - return -EIO; - } - - *target_path = cifs_strndup_from_utf16( - symlink_buf->PathBuffer + sub_offset, - sub_len, true, cifs_sb->local_nls); - if (!(*target_path)) - return -ENOMEM; - - convert_delimiter(*target_path, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); - - return 0; -} - -static int -parse_reparse_point(struct reparse_data_buffer *buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) -{ - if (plen < sizeof(struct reparse_data_buffer)) { - cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n", - plen); - return -EIO; - } - - if (plen < le16_to_cpu(buf->ReparseDataLength) + - sizeof(struct reparse_data_buffer)) { - cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n", - plen); - return -EIO; - } - - /* See MS-FSCC 2.1.2 */ - switch (le32_to_cpu(buf->ReparseTag)) { - case IO_REPARSE_TAG_NFS: - return parse_reparse_posix( - (struct reparse_posix_data *)buf, - plen, target_path, cifs_sb); - case IO_REPARSE_TAG_SYMLINK: - return parse_reparse_symlink( - (struct reparse_symlink_data_buffer *)buf, - plen, target_path, cifs_sb); - default: - cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n", - le32_to_cpu(buf->ReparseTag)); - return -EOPNOTSUPP; - } -} - -static int -smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - char **target_path, bool is_reparse_point) -{ - int rc; - __le16 *utf16_path = NULL; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct kvec err_iov = {NULL, 0}; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec close_iov[1]; - struct smb2_create_rsp *create_rsp; - struct smb2_ioctl_rsp *ioctl_rsp; - struct reparse_data_buffer *reparse_buf; - int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0; - u32 plen; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); - - *target_path = NULL; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, create_options), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto querty_exit; - smb2_set_next_command(tcon, &rqst[0]); - - - /* IOCTL */ - memset(&io_iov, 0, sizeof(io_iov)); - rqst[1].rq_iov = io_iov; - rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, - &rqst[1], fid.persistent_fid, - fid.volatile_fid, FSCTL_GET_REPARSE_POINT, NULL, 0, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE); - if (rc) - goto querty_exit; - - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - - /* Close */ - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto querty_exit; - - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, tcon->ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - - create_rsp = rsp_iov[0].iov_base; - if (create_rsp && create_rsp->hdr.Status) - err_iov = rsp_iov[0]; - ioctl_rsp = rsp_iov[1].iov_base; - - /* - * Open was successful and we got an ioctl response. - */ - if ((rc == 0) && (is_reparse_point)) { - /* See MS-FSCC 2.3.23 */ - - reparse_buf = (struct reparse_data_buffer *) - ((char *)ioctl_rsp + - le32_to_cpu(ioctl_rsp->OutputOffset)); - plen = le32_to_cpu(ioctl_rsp->OutputCount); - - if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > - rsp_iov[1].iov_len) { - cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n", - plen); - rc = -EIO; - goto querty_exit; - } - - rc = parse_reparse_point(reparse_buf, plen, target_path, - cifs_sb); - goto querty_exit; - } - - if (!rc || !err_iov.iov_base) { - rc = -ENOENT; - goto querty_exit; - } - - rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path); - - querty_exit: - cifs_dbg(FYI, "query symlink rc %d\n", rc); - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_ioctl_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - return rc; -} - -int -smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - __u32 *tag) -{ - int rc; - __le16 *utf16_path = NULL; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec close_iov[1]; - struct smb2_ioctl_rsp *ioctl_rsp; - struct reparse_data_buffer *reparse_buf; - u32 plen; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - /* - * setup smb2open - TODO add optimization to call cifs_get_readable_path - * to see if there is a handle already open that we can use - */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto query_rp_exit; - smb2_set_next_command(tcon, &rqst[0]); - - - /* IOCTL */ - memset(&io_iov, 0, sizeof(io_iov)); - rqst[1].rq_iov = io_iov; - rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE); - if (rc) - goto query_rp_exit; - - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - - /* Close */ - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto query_rp_exit; - - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, tcon->ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - - ioctl_rsp = rsp_iov[1].iov_base; - - /* - * Open was successful and we got an ioctl response. - */ - if (rc == 0) { - /* See MS-FSCC 2.3.23 */ - - reparse_buf = (struct reparse_data_buffer *) - ((char *)ioctl_rsp + - le32_to_cpu(ioctl_rsp->OutputOffset)); - plen = le32_to_cpu(ioctl_rsp->OutputCount); - - if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > - rsp_iov[1].iov_len) { - cifs_tcon_dbg(FYI, "srv returned invalid ioctl len: %d\n", - plen); - rc = -EIO; - goto query_rp_exit; - } - *tag = le32_to_cpu(reparse_buf->ReparseTag); - } - - query_rp_exit: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_ioctl_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - return rc; -} - -static struct cifs_ntsd * -get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, - const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - unsigned int xid; - int rc = -EOPNOTSUPP; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - xid = get_xid(); - cifs_dbg(FYI, "trying to get acl\n"); - - rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid, - cifsfid->volatile_fid, (void **)&pntsd, pacllen, - info); - free_xid(xid); - - cifs_put_tlink(tlink); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; - -} - -static struct cifs_ntsd * -get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, - const char *path, u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - unsigned int xid; - int rc; - struct cifs_tcon *tcon; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - __le16 *utf16_path; - - cifs_dbg(FYI, "get smb3 acl for path %s\n", path); - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - free_xid(xid); - return ERR_PTR(rc); - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = READ_CONTROL, - .disposition = FILE_OPEN, - /* - * When querying an ACL, even if the file is a symlink - * we want to open the source not the target, and so - * the protocol requires that the client specify this - * flag when opening a reparse point - */ - .create_options = cifs_create_options(cifs_sb, 0) | - OPEN_REPARSE_POINT, - .fid = &fid, - }; - - if (info & SACL_SECINFO) - oparms.desired_access |= SYSTEM_SECURITY; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, - NULL); - kfree(utf16_path); - if (!rc) { - rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, - fid.volatile_fid, (void **)&pntsd, pacllen, - info); - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - } - - cifs_put_tlink(tlink); - free_xid(xid); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; -} - -static int -set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, - struct inode *inode, const char *path, int aclflag) -{ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - unsigned int xid; - int rc, access_flags = 0; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - __le16 *utf16_path; - - cifs_dbg(FYI, "set smb3 acl for path %s\n", path); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP) - access_flags |= WRITE_OWNER; - if (aclflag & CIFS_ACL_SACL) - access_flags |= SYSTEM_SECURITY; - if (aclflag & CIFS_ACL_DACL) - access_flags |= WRITE_DAC; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - free_xid(xid); - return rc; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .desired_access = access_flags, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - NULL, NULL); - kfree(utf16_path); - if (!rc) { - rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, - fid.volatile_fid, pnntsd, acllen, aclflag); - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - } - - cifs_put_tlink(tlink); - free_xid(xid); - return rc; -} - -/* Retrieve an ACL from the server */ -static struct cifs_ntsd * -get_smb2_acl(struct cifs_sb_info *cifs_sb, - struct inode *inode, const char *path, - u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - struct cifsFileInfo *open_file = NULL; - - if (inode && !(info & SACL_SECINFO)) - open_file = find_readable_file(CIFS_I(inode), true); - if (!open_file || (info & SACL_SECINFO)) - return get_smb2_acl_by_path(cifs_sb, path, pacllen, info); - - pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); - cifsFileInfo_put(open_file); - return pntsd; -} - -static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon, - loff_t offset, loff_t len, unsigned int xid) -{ - struct cifsFileInfo *cfile = file->private_data; - struct file_zero_data_information fsctl_buf; - - cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); - - fsctl_buf.FileOffset = cpu_to_le64(offset); - fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); - - return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, - (char *)&fsctl_buf, - sizeof(struct file_zero_data_information), - 0, NULL, NULL); -} - -static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, - loff_t offset, loff_t len, bool keep_size) -{ - struct cifs_ses *ses = tcon->ses; - struct inode *inode = file_inode(file); - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct cifsFileInfo *cfile = file->private_data; - long rc; - unsigned int xid; - __le64 eof; - - xid = get_xid(); - - trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, - ses->Suid, offset, len); - - inode_lock(inode); - filemap_invalidate_lock(inode->i_mapping); - - /* - * We zero the range through ioctl, so we need remove the page caches - * first, otherwise the data may be inconsistent with the server. - */ - truncate_pagecache_range(inode, offset, offset + len - 1); - - /* if file not oplocked can't be sure whether asking to extend size */ - rc = -EOPNOTSUPP; - if (keep_size == false && !CIFS_CACHE_READ(cifsi)) - goto zero_range_exit; - - rc = smb3_zero_data(file, tcon, offset, len, xid); - if (rc < 0) - goto zero_range_exit; - - /* - * do we also need to change the size of the file? - */ - if (keep_size == false && i_size_read(inode) < offset + len) { - eof = cpu_to_le64(offset + len); - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - } - - zero_range_exit: - filemap_invalidate_unlock(inode->i_mapping); - inode_unlock(inode); - free_xid(xid); - if (rc) - trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, - ses->Suid, offset, len, rc); - else - trace_smb3_zero_done(xid, cfile->fid.persistent_fid, tcon->tid, - ses->Suid, offset, len); - return rc; -} - -static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, - loff_t offset, loff_t len) -{ - struct inode *inode = file_inode(file); - struct cifsFileInfo *cfile = file->private_data; - struct file_zero_data_information fsctl_buf; - long rc; - unsigned int xid; - __u8 set_sparse = 1; - - xid = get_xid(); - - inode_lock(inode); - /* Need to make file sparse, if not already, before freeing range. */ - /* Consider adding equivalent for compressed since it could also work */ - if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { - rc = -EOPNOTSUPP; - goto out; - } - - filemap_invalidate_lock(inode->i_mapping); - /* - * We implement the punch hole through ioctl, so we need remove the page - * caches first, otherwise the data may be inconsistent with the server. - */ - truncate_pagecache_range(inode, offset, offset + len - 1); - - cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); - - fsctl_buf.FileOffset = cpu_to_le64(offset); - fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, - (char *)&fsctl_buf, - sizeof(struct file_zero_data_information), - CIFSMaxBufSize, NULL, NULL); - filemap_invalidate_unlock(inode->i_mapping); -out: - inode_unlock(inode); - free_xid(xid); - return rc; -} - -static int smb3_simple_fallocate_write_range(unsigned int xid, - struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, - loff_t off, loff_t len, - char *buf) -{ - struct cifs_io_parms io_parms = {0}; - int nbytes; - int rc = 0; - struct kvec iov[2]; - - io_parms.netfid = cfile->fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.persistent_fid = cfile->fid.persistent_fid; - io_parms.volatile_fid = cfile->fid.volatile_fid; - - while (len) { - io_parms.offset = off; - io_parms.length = len; - if (io_parms.length > SMB2_MAX_BUFFER_SIZE) - io_parms.length = SMB2_MAX_BUFFER_SIZE; - /* iov[0] is reserved for smb header */ - iov[1].iov_base = buf; - iov[1].iov_len = io_parms.length; - rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1); - if (rc) - break; - if (nbytes > len) - return -EINVAL; - buf += nbytes; - off += nbytes; - len -= nbytes; - } - return rc; -} - -static int smb3_simple_fallocate_range(unsigned int xid, - struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, - loff_t off, loff_t len) -{ - struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; - u32 out_data_len; - char *buf = NULL; - loff_t l; - int rc; - - in_data.file_offset = cpu_to_le64(off); - in_data.length = cpu_to_le64(len); - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_QUERY_ALLOCATED_RANGES, - (char *)&in_data, sizeof(in_data), - 1024 * sizeof(struct file_allocated_range_buffer), - (char **)&out_data, &out_data_len); - if (rc) - goto out; - - buf = kzalloc(1024 * 1024, GFP_KERNEL); - if (buf == NULL) { - rc = -ENOMEM; - goto out; - } - - tmp_data = out_data; - while (len) { - /* - * The rest of the region is unmapped so write it all. - */ - if (out_data_len == 0) { - rc = smb3_simple_fallocate_write_range(xid, tcon, - cfile, off, len, buf); - goto out; - } - - if (out_data_len < sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto out; - } - - if (off < le64_to_cpu(tmp_data->file_offset)) { - /* - * We are at a hole. Write until the end of the region - * or until the next allocated data, - * whichever comes next. - */ - l = le64_to_cpu(tmp_data->file_offset) - off; - if (len < l) - l = len; - rc = smb3_simple_fallocate_write_range(xid, tcon, - cfile, off, l, buf); - if (rc) - goto out; - off = off + l; - len = len - l; - if (len == 0) - goto out; - } - /* - * We are at a section of allocated data, just skip forward - * until the end of the data or the end of the region - * we are supposed to fallocate, whichever comes first. - */ - l = le64_to_cpu(tmp_data->length); - if (len < l) - l = len; - off += l; - len -= l; - - tmp_data = &tmp_data[1]; - out_data_len -= sizeof(struct file_allocated_range_buffer); - } - - out: - kfree(out_data); - kfree(buf); - return rc; -} - - -static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, - loff_t off, loff_t len, bool keep_size) -{ - struct inode *inode; - struct cifsInodeInfo *cifsi; - struct cifsFileInfo *cfile = file->private_data; - long rc = -EOPNOTSUPP; - unsigned int xid; - __le64 eof; - - xid = get_xid(); - - inode = d_inode(cfile->dentry); - cifsi = CIFS_I(inode); - - trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, - tcon->ses->Suid, off, len); - /* if file not oplocked can't be sure whether asking to extend size */ - if (!CIFS_CACHE_READ(cifsi)) - if (keep_size == false) { - trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, off, len, rc); - free_xid(xid); - return rc; - } - - /* - * Extending the file - */ - if ((keep_size == false) && i_size_read(inode) < off + len) { - rc = inode_newsize_ok(inode, off + len); - if (rc) - goto out; - - if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) - smb2_set_sparse(xid, tcon, cfile, inode, false); - - eof = cpu_to_le64(off + len); - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - if (rc == 0) { - cifsi->server_eof = off + len; - cifs_setsize(inode, off + len); - cifs_truncate_page(inode->i_mapping, inode->i_size); - truncate_setsize(inode, off + len); - } - goto out; - } - - /* - * Files are non-sparse by default so falloc may be a no-op - * Must check if file sparse. If not sparse, and since we are not - * extending then no need to do anything since file already allocated - */ - if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { - rc = 0; - goto out; - } - - if (keep_size == true) { - /* - * We can not preallocate pages beyond the end of the file - * in SMB2 - */ - if (off >= i_size_read(inode)) { - rc = 0; - goto out; - } - /* - * For fallocates that are partially beyond the end of file, - * clamp len so we only fallocate up to the end of file. - */ - if (off + len > i_size_read(inode)) { - len = i_size_read(inode) - off; - } - } - - if ((keep_size == true) || (i_size_read(inode) >= off + len)) { - /* - * At this point, we are trying to fallocate an internal - * regions of a sparse file. Since smb2 does not have a - * fallocate command we have two otions on how to emulate this. - * We can either turn the entire file to become non-sparse - * which we only do if the fallocate is for virtually - * the whole file, or we can overwrite the region with zeroes - * using SMB2_write, which could be prohibitevly expensive - * if len is large. - */ - /* - * We are only trying to fallocate a small region so - * just write it with zero. - */ - if (len <= 1024 * 1024) { - rc = smb3_simple_fallocate_range(xid, tcon, cfile, - off, len); - goto out; - } - - /* - * Check if falloc starts within first few pages of file - * and ends within a few pages of the end of file to - * ensure that most of file is being forced to be - * fallocated now. If so then setting whole file sparse - * ie potentially making a few extra pages at the beginning - * or end of the file non-sparse via set_sparse is harmless. - */ - if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { - rc = -EOPNOTSUPP; - goto out; - } - } - - smb2_set_sparse(xid, tcon, cfile, inode, false); - rc = 0; - -out: - if (rc) - trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, - tcon->ses->Suid, off, len, rc); - else - trace_smb3_falloc_done(xid, cfile->fid.persistent_fid, tcon->tid, - tcon->ses->Suid, off, len); - - free_xid(xid); - return rc; -} - -static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, - loff_t off, loff_t len) -{ - int rc; - unsigned int xid; - struct inode *inode = file_inode(file); - struct cifsFileInfo *cfile = file->private_data; - struct cifsInodeInfo *cifsi = CIFS_I(inode); - __le64 eof; - loff_t old_eof; - - xid = get_xid(); - - inode_lock(inode); - - old_eof = i_size_read(inode); - if ((off >= old_eof) || - off + len >= old_eof) { - rc = -EINVAL; - goto out; - } - - filemap_invalidate_lock(inode->i_mapping); - rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1); - if (rc < 0) - goto out_2; - - truncate_pagecache_range(inode, off, old_eof); - - rc = smb2_copychunk_range(xid, cfile, cfile, off + len, - old_eof - off - len, off); - if (rc < 0) - goto out_2; - - eof = cpu_to_le64(old_eof - len); - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - if (rc < 0) - goto out_2; - - rc = 0; - - cifsi->server_eof = i_size_read(inode) - len; - truncate_setsize(inode, cifsi->server_eof); - fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof); -out_2: - filemap_invalidate_unlock(inode->i_mapping); - out: - inode_unlock(inode); - free_xid(xid); - return rc; -} - -static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, - loff_t off, loff_t len) -{ - int rc; - unsigned int xid; - struct cifsFileInfo *cfile = file->private_data; - struct inode *inode = file_inode(file); - __le64 eof; - __u64 count, old_eof; - - xid = get_xid(); - - inode_lock(inode); - - old_eof = i_size_read(inode); - if (off >= old_eof) { - rc = -EINVAL; - goto out; - } - - count = old_eof - off; - eof = cpu_to_le64(old_eof + len); - - filemap_invalidate_lock(inode->i_mapping); - rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1); - if (rc < 0) - goto out_2; - truncate_pagecache_range(inode, off, old_eof); - - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - if (rc < 0) - goto out_2; - - rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); - if (rc < 0) - goto out_2; - - rc = smb3_zero_data(file, tcon, off, len, xid); - if (rc < 0) - goto out_2; - - rc = 0; -out_2: - filemap_invalidate_unlock(inode->i_mapping); - out: - inode_unlock(inode); - free_xid(xid); - return rc; -} - -static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence) -{ - struct cifsFileInfo *wrcfile, *cfile = file->private_data; - struct cifsInodeInfo *cifsi; - struct inode *inode; - int rc = 0; - struct file_allocated_range_buffer in_data, *out_data = NULL; - u32 out_data_len; - unsigned int xid; - - if (whence != SEEK_HOLE && whence != SEEK_DATA) - return generic_file_llseek(file, offset, whence); - - inode = d_inode(cfile->dentry); - cifsi = CIFS_I(inode); - - if (offset < 0 || offset >= i_size_read(inode)) - return -ENXIO; - - xid = get_xid(); - /* - * We need to be sure that all dirty pages are written as they - * might fill holes on the server. - * Note that we also MUST flush any written pages since at least - * some servers (Windows2016) will not reflect recent writes in - * QUERY_ALLOCATED_RANGES until SMB2_flush is called. - */ - wrcfile = find_writable_file(cifsi, FIND_WR_ANY); - if (wrcfile) { - filemap_write_and_wait(inode->i_mapping); - smb2_flush_file(xid, tcon, &wrcfile->fid); - cifsFileInfo_put(wrcfile); - } - - if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) { - if (whence == SEEK_HOLE) - offset = i_size_read(inode); - goto lseek_exit; - } - - in_data.file_offset = cpu_to_le64(offset); - in_data.length = cpu_to_le64(i_size_read(inode)); - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_QUERY_ALLOCATED_RANGES, - (char *)&in_data, sizeof(in_data), - sizeof(struct file_allocated_range_buffer), - (char **)&out_data, &out_data_len); - if (rc == -E2BIG) - rc = 0; - if (rc) - goto lseek_exit; - - if (whence == SEEK_HOLE && out_data_len == 0) - goto lseek_exit; - - if (whence == SEEK_DATA && out_data_len == 0) { - rc = -ENXIO; - goto lseek_exit; - } - - if (out_data_len < sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto lseek_exit; - } - if (whence == SEEK_DATA) { - offset = le64_to_cpu(out_data->file_offset); - goto lseek_exit; - } - if (offset < le64_to_cpu(out_data->file_offset)) - goto lseek_exit; - - offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length); - - lseek_exit: - free_xid(xid); - kfree(out_data); - if (!rc) - return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); - else - return rc; -} - -static int smb3_fiemap(struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, - struct fiemap_extent_info *fei, u64 start, u64 len) -{ - unsigned int xid; - struct file_allocated_range_buffer in_data, *out_data; - u32 out_data_len; - int i, num, rc, flags, last_blob; - u64 next; - - rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len, 0); - if (rc) - return rc; - - xid = get_xid(); - again: - in_data.file_offset = cpu_to_le64(start); - in_data.length = cpu_to_le64(len); - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_QUERY_ALLOCATED_RANGES, - (char *)&in_data, sizeof(in_data), - 1024 * sizeof(struct file_allocated_range_buffer), - (char **)&out_data, &out_data_len); - if (rc == -E2BIG) { - last_blob = 0; - rc = 0; - } else - last_blob = 1; - if (rc) - goto out; - - if (out_data_len && out_data_len < sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto out; - } - if (out_data_len % sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto out; - } - - num = out_data_len / sizeof(struct file_allocated_range_buffer); - for (i = 0; i < num; i++) { - flags = 0; - if (i == num - 1 && last_blob) - flags |= FIEMAP_EXTENT_LAST; - - rc = fiemap_fill_next_extent(fei, - le64_to_cpu(out_data[i].file_offset), - le64_to_cpu(out_data[i].file_offset), - le64_to_cpu(out_data[i].length), - flags); - if (rc < 0) - goto out; - if (rc == 1) { - rc = 0; - goto out; - } - } - - if (!last_blob) { - next = le64_to_cpu(out_data[num - 1].file_offset) + - le64_to_cpu(out_data[num - 1].length); - len = len - (next - start); - start = next; - goto again; - } - - out: - free_xid(xid); - kfree(out_data); - return rc; -} - -static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, - loff_t off, loff_t len) -{ - /* KEEP_SIZE already checked for by do_fallocate */ - if (mode & FALLOC_FL_PUNCH_HOLE) - return smb3_punch_hole(file, tcon, off, len); - else if (mode & FALLOC_FL_ZERO_RANGE) { - if (mode & FALLOC_FL_KEEP_SIZE) - return smb3_zero_range(file, tcon, off, len, true); - return smb3_zero_range(file, tcon, off, len, false); - } else if (mode == FALLOC_FL_KEEP_SIZE) - return smb3_simple_falloc(file, tcon, off, len, true); - else if (mode == FALLOC_FL_COLLAPSE_RANGE) - return smb3_collapse_range(file, tcon, off, len); - else if (mode == FALLOC_FL_INSERT_RANGE) - return smb3_insert_range(file, tcon, off, len); - else if (mode == 0) - return smb3_simple_falloc(file, tcon, off, len, false); - - return -EOPNOTSUPP; -} - -static void -smb2_downgrade_oplock(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - server->ops->set_oplock_level(cinode, oplock, 0, NULL); -} - -static void -smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache); - -static void -smb3_downgrade_oplock(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - unsigned int old_state = cinode->oplock; - unsigned int old_epoch = cinode->epoch; - unsigned int new_state; - - if (epoch > old_epoch) { - smb21_set_oplock_level(cinode, oplock, 0, NULL); - cinode->epoch = epoch; - } - - new_state = cinode->oplock; - *purge_cache = false; - - if ((old_state & CIFS_CACHE_READ_FLG) != 0 && - (new_state & CIFS_CACHE_READ_FLG) == 0) - *purge_cache = true; - else if (old_state == new_state && (epoch - old_epoch > 1)) - *purge_cache = true; -} - -static void -smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - oplock &= 0xFF; - cinode->lease_granted = false; - if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) - return; - if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { - cinode->oplock = CIFS_CACHE_RHW_FLG; - cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - cinode->oplock = CIFS_CACHE_RW_FLG; - cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else if (oplock == SMB2_OPLOCK_LEVEL_II) { - cinode->oplock = CIFS_CACHE_READ_FLG; - cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else - cinode->oplock = 0; -} - -static void -smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - char message[5] = {0}; - unsigned int new_oplock = 0; - - oplock &= 0xFF; - cinode->lease_granted = true; - if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) - return; - - /* Check if the server granted an oplock rather than a lease */ - if (oplock & SMB2_OPLOCK_LEVEL_EXCLUSIVE) - return smb2_set_oplock_level(cinode, oplock, epoch, - purge_cache); - - if (oplock & SMB2_LEASE_READ_CACHING_HE) { - new_oplock |= CIFS_CACHE_READ_FLG; - strcat(message, "R"); - } - if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { - new_oplock |= CIFS_CACHE_HANDLE_FLG; - strcat(message, "H"); - } - if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { - new_oplock |= CIFS_CACHE_WRITE_FLG; - strcat(message, "W"); - } - if (!new_oplock) - strncpy(message, "None", sizeof(message)); - - cinode->oplock = new_oplock; - cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, - &cinode->netfs.inode); -} - -static void -smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - unsigned int old_oplock = cinode->oplock; - - smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); - - if (purge_cache) { - *purge_cache = false; - if (old_oplock == CIFS_CACHE_READ_FLG) { - if (cinode->oplock == CIFS_CACHE_READ_FLG && - (epoch - cinode->epoch > 0)) - *purge_cache = true; - else if (cinode->oplock == CIFS_CACHE_RH_FLG && - (epoch - cinode->epoch > 1)) - *purge_cache = true; - else if (cinode->oplock == CIFS_CACHE_RHW_FLG && - (epoch - cinode->epoch > 1)) - *purge_cache = true; - else if (cinode->oplock == 0 && - (epoch - cinode->epoch > 0)) - *purge_cache = true; - } else if (old_oplock == CIFS_CACHE_RH_FLG) { - if (cinode->oplock == CIFS_CACHE_RH_FLG && - (epoch - cinode->epoch > 0)) - *purge_cache = true; - else if (cinode->oplock == CIFS_CACHE_RHW_FLG && - (epoch - cinode->epoch > 1)) - *purge_cache = true; - } - cinode->epoch = epoch; - } -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static bool -smb2_is_read_op(__u32 oplock) -{ - return oplock == SMB2_OPLOCK_LEVEL_II; -} -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ - -static bool -smb21_is_read_op(__u32 oplock) -{ - return (oplock & SMB2_LEASE_READ_CACHING_HE) && - !(oplock & SMB2_LEASE_WRITE_CACHING_HE); -} - -static __le32 -map_oplock_to_lease(u8 oplock) -{ - if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) - return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; - else if (oplock == SMB2_OPLOCK_LEVEL_II) - return SMB2_LEASE_READ_CACHING_LE; - else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) - return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE; - return 0; -} - -static char * -smb2_create_lease_buf(u8 *lease_key, u8 oplock) -{ - struct create_lease *buf; - - buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); - if (!buf) - return NULL; - - memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseState = map_oplock_to_lease(oplock); - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - return (char *)buf; -} - -static char * -smb3_create_lease_buf(u8 *lease_key, u8 oplock) -{ - struct create_lease_v2 *buf; - - buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); - if (!buf) - return NULL; - - memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseState = map_oplock_to_lease(oplock); - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease_v2, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease_v2, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - return (char *)buf; -} - -static __u8 -smb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) -{ - struct create_lease *lc = (struct create_lease *)buf; - - *epoch = 0; /* not used */ - if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) - return SMB2_OPLOCK_LEVEL_NOCHANGE; - return le32_to_cpu(lc->lcontext.LeaseState); -} - -static __u8 -smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) -{ - struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; - - *epoch = le16_to_cpu(lc->lcontext.Epoch); - if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) - return SMB2_OPLOCK_LEVEL_NOCHANGE; - if (lease_key) - memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - return le32_to_cpu(lc->lcontext.LeaseState); -} - -static unsigned int -smb2_wp_retry_size(struct inode *inode) -{ - return min_t(unsigned int, CIFS_SB(inode->i_sb)->ctx->wsize, - SMB2_MAX_BUFFER_SIZE); -} - -static bool -smb2_dir_needs_close(struct cifsFileInfo *cfile) -{ - return !cfile->invalidHandle; -} - -static void -fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, - struct smb_rqst *old_rq, __le16 cipher_type) -{ - struct smb2_hdr *shdr = - (struct smb2_hdr *)old_rq->rq_iov[0].iov_base; - - memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); - tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; - tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); - tr_hdr->Flags = cpu_to_le16(0x01); - if ((cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - else - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8); -} - -static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst, - int num_rqst, const u8 *sig, u8 **iv, - struct aead_request **req, struct scatterlist **sgl, - unsigned int *num_sgs) -{ - unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); - unsigned int iv_size = crypto_aead_ivsize(tfm); - unsigned int len; - u8 *p; - - *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); - - len = iv_size; - len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); - len = ALIGN(len, crypto_tfm_ctx_alignment()); - len += req_size; - len = ALIGN(len, __alignof__(struct scatterlist)); - len += *num_sgs * sizeof(**sgl); - - p = kmalloc(len, GFP_ATOMIC); - if (!p) - return NULL; - - *iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1); - *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size, - crypto_tfm_ctx_alignment()); - *sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size, - __alignof__(struct scatterlist)); - return p; -} - -static void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst, - int num_rqst, const u8 *sig, u8 **iv, - struct aead_request **req, struct scatterlist **sgl) -{ - unsigned int off, len, skip; - struct scatterlist *sg; - unsigned int num_sgs; - unsigned long addr; - int i, j; - void *p; - - p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs); - if (!p) - return NULL; - - sg_init_table(*sgl, num_sgs); - sg = *sgl; - - /* Assumes the first rqst has a transform header as the first iov. - * I.e. - * rqst[0].rq_iov[0] is transform header - * rqst[0].rq_iov[1+] data to be encrypted/decrypted - * rqst[1+].rq_iov[0+] data to be encrypted/decrypted - */ - for (i = 0; i < num_rqst; i++) { - /* - * The first rqst has a transform header where the - * first 20 bytes are not part of the encrypted blob. - */ - for (j = 0; j < rqst[i].rq_nvec; j++) { - struct kvec *iov = &rqst[i].rq_iov[j]; - - skip = (i == 0) && (j == 0) ? 20 : 0; - addr = (unsigned long)iov->iov_base + skip; - len = iov->iov_len - skip; - sg = cifs_sg_set_buf(sg, (void *)addr, len); - } - for (j = 0; j < rqst[i].rq_npages; j++) { - rqst_page_get_length(&rqst[i], j, &len, &off); - sg_set_page(sg++, rqst[i].rq_pages[j], len, off); - } - } - cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE); - - return p; -} - -static int -smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - u8 *ses_enc_key; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid == ses_id) { - spin_lock(&ses->ses_lock); - ses_enc_key = enc ? ses->smb3encryptionkey : - ses->smb3decryptionkey; - memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); - spin_unlock(&ses->ses_lock); - spin_unlock(&cifs_tcp_ses_lock); - return 0; - } - } - spin_unlock(&cifs_tcp_ses_lock); - - return -EAGAIN; -} -/* - * Encrypt or decrypt @rqst message. @rqst[0] has the following format: - * iov[0] - transform header (associate data), - * iov[1-N] - SMB2 header and pages - data to encrypt. - * On success return encrypted data in iov[1-N] and pages, leave iov[0] - * untouched. - */ -static int -crypt_message(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *rqst, int enc) -{ - struct smb2_transform_hdr *tr_hdr = - (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int rc = 0; - struct scatterlist *sg; - u8 sign[SMB2_SIGNATURE_SIZE] = {}; - u8 key[SMB3_ENC_DEC_KEY_SIZE]; - struct aead_request *req; - u8 *iv; - DECLARE_CRYPTO_WAIT(wait); - struct crypto_aead *tfm; - unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); - void *creq; - - rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__, - enc ? "en" : "de"); - return rc; - } - - rc = smb3_crypto_aead_allocate(server); - if (rc) { - cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); - return rc; - } - - tfm = enc ? server->secmech.enc : server->secmech.dec; - - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); - else - rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); - - if (rc) { - cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); - return rc; - } - - rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); - return rc; - } - - creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg); - if (unlikely(!creq)) - return -ENOMEM; - - if (!enc) { - memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); - crypt_len += SMB2_SIGNATURE_SIZE; - } - - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - else { - iv[0] = 3; - memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - } - - aead_request_set_tfm(req, tfm); - aead_request_set_crypt(req, sg, sg, crypt_len, iv); - aead_request_set_ad(req, assoc_data_len); - - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - crypto_req_done, &wait); - - rc = crypto_wait_req(enc ? crypto_aead_encrypt(req) - : crypto_aead_decrypt(req), &wait); - - if (!rc && enc) - memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); - - kfree_sensitive(creq); - return rc; -} - -void -smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst) -{ - int i, j; - - for (i = 0; i < num_rqst; i++) { - if (rqst[i].rq_pages) { - for (j = rqst[i].rq_npages - 1; j >= 0; j--) - put_page(rqst[i].rq_pages[j]); - kfree(rqst[i].rq_pages); - } - } -} - -/* - * This function will initialize new_rq and encrypt the content. - * The first entry, new_rq[0], only contains a single iov which contains - * a smb2_transform_hdr and is pre-allocated by the caller. - * This function then populates new_rq[1+] with the content from olq_rq[0+]. - * - * The end result is an array of smb_rqst structures where the first structure - * only contains a single iov for the transform header which we then can pass - * to crypt_message(). - * - * new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller - * new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests - */ -static int -smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *new_rq, struct smb_rqst *old_rq) -{ - struct page **pages; - struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base; - unsigned int npages; - unsigned int orig_len = 0; - int i, j; - int rc = -ENOMEM; - - for (i = 1; i < num_rqst; i++) { - npages = old_rq[i - 1].rq_npages; - pages = kmalloc_array(npages, sizeof(struct page *), - GFP_KERNEL); - if (!pages) - goto err_free; - - new_rq[i].rq_pages = pages; - new_rq[i].rq_npages = npages; - new_rq[i].rq_offset = old_rq[i - 1].rq_offset; - new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz; - new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz; - new_rq[i].rq_iov = old_rq[i - 1].rq_iov; - new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec; - - orig_len += smb_rqst_len(server, &old_rq[i - 1]); - - for (j = 0; j < npages; j++) { - pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!pages[j]) - goto err_free; - } - - /* copy pages form the old */ - for (j = 0; j < npages; j++) { - char *dst, *src; - unsigned int offset, len; - - rqst_page_get_length(&new_rq[i], j, &len, &offset); - - dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset; - src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset; - - memcpy(dst, src, len); - kunmap(new_rq[i].rq_pages[j]); - kunmap(old_rq[i - 1].rq_pages[j]); - } - } - - /* fill the 1st iov with a transform header */ - fill_transform_hdr(tr_hdr, orig_len, old_rq, server->cipher_type); - - rc = crypt_message(server, num_rqst, new_rq, 1); - cifs_dbg(FYI, "Encrypt message returned %d\n", rc); - if (rc) - goto err_free; - - return rc; - -err_free: - smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]); - return rc; -} - -static int -smb3_is_transform_hdr(void *buf) -{ - struct smb2_transform_hdr *trhdr = buf; - - return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; -} - -static int -decrypt_raw_data(struct TCP_Server_Info *server, char *buf, - unsigned int buf_data_size, struct page **pages, - unsigned int npages, unsigned int page_data_size, - bool is_offloaded) -{ - struct kvec iov[2]; - struct smb_rqst rqst = {NULL}; - int rc; - - iov[0].iov_base = buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr); - iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); - iov[1].iov_len = buf_data_size; - - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - rqst.rq_pages = pages; - rqst.rq_npages = npages; - rqst.rq_pagesz = PAGE_SIZE; - rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE; - - rc = crypt_message(server, 1, &rqst, 0); - cifs_dbg(FYI, "Decrypt message returned %d\n", rc); - - if (rc) - return rc; - - memmove(buf, iov[1].iov_base, buf_data_size); - - if (!is_offloaded) - server->total_read = buf_data_size + page_data_size; - - return rc; -} - -static int -read_data_into_pages(struct TCP_Server_Info *server, struct page **pages, - unsigned int npages, unsigned int len) -{ - int i; - int length; - - for (i = 0; i < npages; i++) { - struct page *page = pages[i]; - size_t n; - - n = len; - if (len >= PAGE_SIZE) { - /* enough data to fill the page */ - n = PAGE_SIZE; - len -= n; - } else { - zero_user(page, len, PAGE_SIZE - len); - len = 0; - } - length = cifs_read_page_from_socket(server, page, 0, n); - if (length < 0) - return length; - server->total_read += length; - } - - return 0; -} - -static int -init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size, - unsigned int cur_off, struct bio_vec **page_vec) -{ - struct bio_vec *bvec; - int i; - - bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL); - if (!bvec) - return -ENOMEM; - - for (i = 0; i < npages; i++) { - bvec[i].bv_page = pages[i]; - bvec[i].bv_offset = (i == 0) ? cur_off : 0; - bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size); - data_size -= bvec[i].bv_len; - } - - if (data_size != 0) { - cifs_dbg(VFS, "%s: something went wrong\n", __func__); - kfree(bvec); - return -EIO; - } - - *page_vec = bvec; - return 0; -} - -static int -handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, - char *buf, unsigned int buf_len, struct page **pages, - unsigned int npages, unsigned int page_data_size, - bool is_offloaded) -{ - unsigned int data_offset; - unsigned int data_len; - unsigned int cur_off; - unsigned int cur_page_idx; - unsigned int pad_len; - struct cifs_readdata *rdata = mid->callback_data; - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - struct bio_vec *bvec = NULL; - struct iov_iter iter; - struct kvec iov; - int length; - bool use_rdma_mr = false; - - if (shdr->Command != SMB2_READ) { - cifs_server_dbg(VFS, "only big read responses are supported\n"); - return -ENOTSUPP; - } - - if (server->ops->is_session_expired && - server->ops->is_session_expired(buf)) { - if (!is_offloaded) - cifs_reconnect(server, true); - return -1; - } - - if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server)) - return -1; - - /* set up first two iov to get credits */ - rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = 0; - rdata->iov[1].iov_base = buf; - rdata->iov[1].iov_len = - min_t(unsigned int, buf_len, server->vals->read_rsp_size); - cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", - rdata->iov[0].iov_base, rdata->iov[0].iov_len); - cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", - rdata->iov[1].iov_base, rdata->iov[1].iov_len); - - rdata->result = server->ops->map_error(buf, true); - if (rdata->result != 0) { - cifs_dbg(FYI, "%s: server returned error %d\n", - __func__, rdata->result); - /* normal error on read response */ - if (is_offloaded) - mid->mid_state = MID_RESPONSE_RECEIVED; - else - dequeue_mid(mid, false); - return 0; - } - - data_offset = server->ops->read_data_offset(buf); -#ifdef CONFIG_CIFS_SMB_DIRECT - use_rdma_mr = rdata->mr; -#endif - data_len = server->ops->read_data_length(buf, use_rdma_mr); - - if (data_offset < server->vals->read_rsp_size) { - /* - * win2k8 sometimes sends an offset of 0 when the read - * is beyond the EOF. Treat it as if the data starts just after - * the header. - */ - cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", - __func__, data_offset); - data_offset = server->vals->read_rsp_size; - } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { - /* data_offset is beyond the end of smallbuf */ - cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", - __func__, data_offset); - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - pad_len = data_offset - server->vals->read_rsp_size; - - if (buf_len <= data_offset) { - /* read response payload is in pages */ - cur_page_idx = pad_len / PAGE_SIZE; - cur_off = pad_len % PAGE_SIZE; - - if (cur_page_idx != 0) { - /* data offset is beyond the 1st page of response */ - cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n", - __func__, data_offset); - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - if (data_len > page_data_size - pad_len) { - /* data_len is corrupt -- discard frame */ - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - rdata->result = init_read_bvec(pages, npages, page_data_size, - cur_off, &bvec); - if (rdata->result != 0) { - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - iov_iter_bvec(&iter, ITER_SOURCE, bvec, npages, data_len); - } else if (buf_len >= data_offset + data_len) { - /* read response payload is in buf */ - WARN_ONCE(npages > 0, "read data can be either in buf or in pages"); - iov.iov_base = buf + data_offset; - iov.iov_len = data_len; - iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, data_len); - } else { - /* read response payload cannot be in both buf and pages */ - WARN_ONCE(1, "buf can not contain only a part of read data"); - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - length = rdata->copy_into_pages(server, rdata, &iter); - - kfree(bvec); - - if (length < 0) - return length; - - if (is_offloaded) - mid->mid_state = MID_RESPONSE_RECEIVED; - else - dequeue_mid(mid, false); - return length; -} - -struct smb2_decrypt_work { - struct work_struct decrypt; - struct TCP_Server_Info *server; - struct page **ppages; - char *buf; - unsigned int npages; - unsigned int len; -}; - - -static void smb2_decrypt_offload(struct work_struct *work) -{ - struct smb2_decrypt_work *dw = container_of(work, - struct smb2_decrypt_work, decrypt); - int i, rc; - struct mid_q_entry *mid; - - rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, - dw->ppages, dw->npages, dw->len, true); - if (rc) { - cifs_dbg(VFS, "error decrypting rc=%d\n", rc); - goto free_pages; - } - - dw->server->lstrp = jiffies; - mid = smb2_find_dequeue_mid(dw->server, dw->buf); - if (mid == NULL) - cifs_dbg(FYI, "mid not found\n"); - else { - mid->decrypted = true; - rc = handle_read_data(dw->server, mid, dw->buf, - dw->server->vals->read_rsp_size, - dw->ppages, dw->npages, dw->len, - true); - if (rc >= 0) { -#ifdef CONFIG_CIFS_STATS2 - mid->when_received = jiffies; -#endif - if (dw->server->ops->is_network_name_deleted) - dw->server->ops->is_network_name_deleted(dw->buf, - dw->server); - - mid->callback(mid); - } else { - spin_lock(&dw->server->srv_lock); - if (dw->server->tcpStatus == CifsNeedReconnect) { - spin_lock(&dw->server->mid_lock); - mid->mid_state = MID_RETRY_NEEDED; - spin_unlock(&dw->server->mid_lock); - spin_unlock(&dw->server->srv_lock); - mid->callback(mid); - } else { - spin_lock(&dw->server->mid_lock); - mid->mid_state = MID_REQUEST_SUBMITTED; - mid->mid_flags &= ~(MID_DELETED); - list_add_tail(&mid->qhead, - &dw->server->pending_mid_q); - spin_unlock(&dw->server->mid_lock); - spin_unlock(&dw->server->srv_lock); - } - } - release_mid(mid); - } - -free_pages: - for (i = dw->npages-1; i >= 0; i--) - put_page(dw->ppages[i]); - - kfree(dw->ppages); - cifs_small_buf_release(dw->buf); - kfree(dw); -} - - -static int -receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, - int *num_mids) -{ - char *buf = server->smallbuf; - struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; - unsigned int npages; - struct page **pages; - unsigned int len; - unsigned int buflen = server->pdu_size; - int rc; - int i = 0; - struct smb2_decrypt_work *dw; - - *num_mids = 1; - len = min_t(unsigned int, buflen, server->vals->read_rsp_size + - sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; - - rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len); - if (rc < 0) - return rc; - server->total_read += rc; - - len = le32_to_cpu(tr_hdr->OriginalMessageSize) - - server->vals->read_rsp_size; - npages = DIV_ROUND_UP(len, PAGE_SIZE); - - pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); - if (!pages) { - rc = -ENOMEM; - goto discard_data; - } - - for (; i < npages; i++) { - pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!pages[i]) { - rc = -ENOMEM; - goto discard_data; - } - } - - /* read read data into pages */ - rc = read_data_into_pages(server, pages, npages, len); - if (rc) - goto free_pages; - - rc = cifs_discard_remaining_data(server); - if (rc) - goto free_pages; - - /* - * For large reads, offload to different thread for better performance, - * use more cores decrypting which can be expensive - */ - - if ((server->min_offload) && (server->in_flight > 1) && - (server->pdu_size >= server->min_offload)) { - dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); - if (dw == NULL) - goto non_offloaded_decrypt; - - dw->buf = server->smallbuf; - server->smallbuf = (char *)cifs_small_buf_get(); - - INIT_WORK(&dw->decrypt, smb2_decrypt_offload); - - dw->npages = npages; - dw->server = server; - dw->ppages = pages; - dw->len = len; - queue_work(decrypt_wq, &dw->decrypt); - *num_mids = 0; /* worker thread takes care of finding mid */ - return -1; - } - -non_offloaded_decrypt: - rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, - pages, npages, len, false); - if (rc) - goto free_pages; - - *mid = smb2_find_mid(server, buf); - if (*mid == NULL) - cifs_dbg(FYI, "mid not found\n"); - else { - cifs_dbg(FYI, "mid found\n"); - (*mid)->decrypted = true; - rc = handle_read_data(server, *mid, buf, - server->vals->read_rsp_size, - pages, npages, len, false); - if (rc >= 0) { - if (server->ops->is_network_name_deleted) { - server->ops->is_network_name_deleted(buf, - server); - } - } - } - -free_pages: - for (i = i - 1; i >= 0; i--) - put_page(pages[i]); - kfree(pages); - return rc; -discard_data: - cifs_discard_remaining_data(server); - goto free_pages; -} - -static int -receive_encrypted_standard(struct TCP_Server_Info *server, - struct mid_q_entry **mids, char **bufs, - int *num_mids) -{ - int ret, length; - char *buf = server->smallbuf; - struct smb2_hdr *shdr; - unsigned int pdu_length = server->pdu_size; - unsigned int buf_size; - struct mid_q_entry *mid_entry; - int next_is_large; - char *next_buffer = NULL; - - *num_mids = 0; - - /* switch to large buffer if too big for a small one */ - if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { - server->large_buf = true; - memcpy(server->bigbuf, buf, server->total_read); - buf = server->bigbuf; - } - - /* now read the rest */ - length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, - pdu_length - HEADER_SIZE(server) + 1); - if (length < 0) - return length; - server->total_read += length; - - buf_size = pdu_length - sizeof(struct smb2_transform_hdr); - length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0, false); - if (length) - return length; - - next_is_large = server->large_buf; -one_more: - shdr = (struct smb2_hdr *)buf; - if (shdr->NextCommand) { - if (next_is_large) - next_buffer = (char *)cifs_buf_get(); - else - next_buffer = (char *)cifs_small_buf_get(); - memcpy(next_buffer, - buf + le32_to_cpu(shdr->NextCommand), - pdu_length - le32_to_cpu(shdr->NextCommand)); - } - - mid_entry = smb2_find_mid(server, buf); - if (mid_entry == NULL) - cifs_dbg(FYI, "mid not found\n"); - else { - cifs_dbg(FYI, "mid found\n"); - mid_entry->decrypted = true; - mid_entry->resp_buf_size = server->pdu_size; - } - - if (*num_mids >= MAX_COMPOUND) { - cifs_server_dbg(VFS, "too many PDUs in compound\n"); - return -1; - } - bufs[*num_mids] = buf; - mids[(*num_mids)++] = mid_entry; - - if (mid_entry && mid_entry->handle) - ret = mid_entry->handle(server, mid_entry); - else - ret = cifs_handle_standard(server, mid_entry); - - if (ret == 0 && shdr->NextCommand) { - pdu_length -= le32_to_cpu(shdr->NextCommand); - server->large_buf = next_is_large; - if (next_is_large) - server->bigbuf = buf = next_buffer; - else - server->smallbuf = buf = next_buffer; - goto one_more; - } else if (ret != 0) { - /* - * ret != 0 here means that we didn't get to handle_mid() thus - * server->smallbuf and server->bigbuf are still valid. We need - * to free next_buffer because it is not going to be used - * anywhere. - */ - if (next_is_large) - free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer); - else - free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer); - } - - return ret; -} - -static int -smb3_receive_transform(struct TCP_Server_Info *server, - struct mid_q_entry **mids, char **bufs, int *num_mids) -{ - char *buf = server->smallbuf; - unsigned int pdu_length = server->pdu_size; - struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; - unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); - - if (pdu_length < sizeof(struct smb2_transform_hdr) + - sizeof(struct smb2_hdr)) { - cifs_server_dbg(VFS, "Transform message is too small (%u)\n", - pdu_length); - cifs_reconnect(server, true); - return -ECONNABORTED; - } - - if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { - cifs_server_dbg(VFS, "Transform message is broken\n"); - cifs_reconnect(server, true); - return -ECONNABORTED; - } - - /* TODO: add support for compounds containing READ. */ - if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { - return receive_encrypted_read(server, &mids[0], num_mids); - } - - return receive_encrypted_standard(server, mids, bufs, num_mids); -} - -int -smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - char *buf = server->large_buf ? server->bigbuf : server->smallbuf; - - return handle_read_data(server, mid, buf, server->pdu_size, - NULL, 0, 0, false); -} - -static int -smb2_next_header(char *buf) -{ - struct smb2_hdr *hdr = (struct smb2_hdr *)buf; - struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; - - if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) - return sizeof(struct smb2_transform_hdr) + - le32_to_cpu(t_hdr->OriginalMessageSize); - - return le32_to_cpu(hdr->NextCommand); -} - -static int -smb2_make_node(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - int rc = -EPERM; - struct cifs_open_info_data buf = {}; - struct cifs_io_parms io_parms = {0}; - __u32 oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - unsigned int bytes_written; - struct win_dev *pdev; - struct kvec iov[2]; - - /* - * Check if mounted with mount parm 'sfu' mount parm. - * SFU emulation should work with all servers, but only - * supports block and char device (no socket & fifo), - * and was used by default in earlier versions of Windows - */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) - return rc; - - /* - * TODO: Add ability to create instead via reparse point. Windows (e.g. - * their current NFS server) uses this approach to expose special files - * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions - */ - - if (!S_ISCHR(mode) && !S_ISBLK(mode)) - return rc; - - cifs_dbg(FYI, "sfu compat create special file\n"); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | - CREATE_OPTION_SPECIAL), - .disposition = FILE_CREATE, - .path = full_path, - .fid = &fid, - }; - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); - if (rc) - return rc; - - /* - * BB Do not bother to decode buf since no local inode yet to put - * timestamps in, but we can reuse it safely. - */ - - pdev = (struct win_dev *)&buf.fi; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = sizeof(struct win_dev); - iov[1].iov_base = &buf.fi; - iov[1].iov_len = sizeof(struct win_dev); - if (S_ISCHR(mode)) { - memcpy(pdev->type, "IntxCHR", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } else if (S_ISBLK(mode)) { - memcpy(pdev->type, "IntxBLK", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } - tcon->ses->server->ops->close(xid, tcon, &fid); - d_drop(dentry); - - /* FIXME: add code here to set EAs */ - - cifs_free_open_info(&buf); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -struct smb_version_operations smb20_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = cifs_wait_mtu_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb2_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb2_negotiate_wsize, - .negotiate_rsize = smb2_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb2_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb2_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .calc_signature = smb2_calc_signature, - .is_read_op = smb2_is_read_op, - .set_oplock_level = smb2_set_oplock_level, - .create_lease_buf = smb2_create_lease_buf, - .parse_lease_buf = smb2_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ - -struct smb_version_operations smb21_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = smb2_wait_mtu_credits, - .adjust_credits = smb2_adjust_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb2_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb2_negotiate_wsize, - .negotiate_rsize = smb2_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb2_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb2_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .calc_signature = smb2_calc_signature, - .is_read_op = smb21_is_read_op, - .set_oplock_level = smb21_set_oplock_level, - .create_lease_buf = smb2_create_lease_buf, - .parse_lease_buf = smb2_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .enum_snapshots = smb3_enum_snapshots, - .notify = smb3_notify, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; - -struct smb_version_operations smb30_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = smb2_wait_mtu_credits, - .adjust_credits = smb2_adjust_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .dump_share_caps = smb2_dump_share_caps, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb3_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb3_negotiate_wsize, - .negotiate_rsize = smb3_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb3_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - /* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */ - .query_reparse_tag = smb2_query_reparse_tag, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .close_getattr = smb2_close_getattr, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb2_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .generate_signingkey = generate_smb30signingkey, - .calc_signature = smb3_calc_signature, - .set_integrity = smb3_set_integrity, - .is_read_op = smb21_is_read_op, - .set_oplock_level = smb3_set_oplock_level, - .create_lease_buf = smb3_create_lease_buf, - .parse_lease_buf = smb3_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .duplicate_extents = smb2_duplicate_extents, - .validate_negotiate = smb3_validate_negotiate, - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .fallocate = smb3_fallocate, - .enum_snapshots = smb3_enum_snapshots, - .notify = smb3_notify, - .init_transform_rq = smb3_init_transform_rq, - .is_transform_hdr = smb3_is_transform_hdr, - .receive_transform = smb3_receive_transform, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; - -struct smb_version_operations smb311_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = smb2_wait_mtu_credits, - .adjust_credits = smb2_adjust_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .dump_share_caps = smb2_dump_share_caps, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb3_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb3_negotiate_wsize, - .negotiate_rsize = smb3_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb3_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - .query_reparse_tag = smb2_query_reparse_tag, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .posix_mkdir = smb311_posix_mkdir, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .close_getattr = smb2_close_getattr, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb311_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .generate_signingkey = generate_smb311signingkey, - .calc_signature = smb3_calc_signature, - .set_integrity = smb3_set_integrity, - .is_read_op = smb21_is_read_op, - .set_oplock_level = smb3_set_oplock_level, - .create_lease_buf = smb3_create_lease_buf, - .parse_lease_buf = smb3_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .duplicate_extents = smb2_duplicate_extents, -/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */ - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .fallocate = smb3_fallocate, - .enum_snapshots = smb3_enum_snapshots, - .notify = smb3_notify, - .init_transform_rq = smb3_init_transform_rq, - .is_transform_hdr = smb3_is_transform_hdr, - .receive_transform = smb3_receive_transform, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -struct smb_version_values smb20_values = { - .version_string = SMB20_VERSION_STRING, - .protocol_id = SMB20_PROT_ID, - .req_capabilities = 0, /* MBZ */ - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease), -}; -#endif /* ALLOW_INSECURE_LEGACY */ - -struct smb_version_values smb21_values = { - .version_string = SMB21_VERSION_STRING, - .protocol_id = SMB21_PROT_ID, - .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */ - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease), -}; - -struct smb_version_values smb3any_values = { - .version_string = SMB3ANY_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smbdefault_values = { - .version_string = SMBDEFAULT_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smb30_values = { - .version_string = SMB30_VERSION_STRING, - .protocol_id = SMB30_PROT_ID, - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smb302_values = { - .version_string = SMB302_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smb311_values = { - .version_string = SMB311_VERSION_STRING, - .protocol_id = SMB311_PROT_ID, - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c deleted file mode 100644 index 3ca593cdda76..000000000000 --- a/fs/cifs/smb2pdu.c +++ /dev/null @@ -1,5722 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2009, 2013 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - * Contains the routines for constructing the SMB2 PDUs themselves - * - */ - - /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ - /* Note that there are handle based routines which must be */ - /* treated slightly differently for reconnection purposes since we never */ - /* want to reuse a stale file handle and only the caller knows the file info */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsacl.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "ntlmssp.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "cifspdu.h" -#include "cifs_spnego.h" -#include "smbdirect.h" -#include "trace.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#include "cached_dir.h" - -/* - * The following table defines the expected "StructureSize" of SMB2 requests - * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. - * - * Note that commands are defined in smb2pdu.h in le16 but the array below is - * indexed by command in host byte order. - */ -static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ 36, - /* SMB2_SESSION_SETUP */ 25, - /* SMB2_LOGOFF */ 4, - /* SMB2_TREE_CONNECT */ 9, - /* SMB2_TREE_DISCONNECT */ 4, - /* SMB2_CREATE */ 57, - /* SMB2_CLOSE */ 24, - /* SMB2_FLUSH */ 24, - /* SMB2_READ */ 49, - /* SMB2_WRITE */ 49, - /* SMB2_LOCK */ 48, - /* SMB2_IOCTL */ 57, - /* SMB2_CANCEL */ 4, - /* SMB2_ECHO */ 4, - /* SMB2_QUERY_DIRECTORY */ 33, - /* SMB2_CHANGE_NOTIFY */ 32, - /* SMB2_QUERY_INFO */ 41, - /* SMB2_SET_INFO */ 33, - /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ -}; - -int smb3_encryption_required(const struct cifs_tcon *tcon) -{ - if (!tcon || !tcon->ses) - return 0; - if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || - (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) - return 1; - if (tcon->seal && - (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - return 1; - return 0; -} - -static void -smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, - const struct cifs_tcon *tcon, - struct TCP_Server_Info *server) -{ - shdr->ProtocolId = SMB2_PROTO_NUMBER; - shdr->StructureSize = cpu_to_le16(64); - shdr->Command = smb2_cmd; - if (server) { - spin_lock(&server->req_lock); - /* Request up to 10 credits but don't go over the limit. */ - if (server->credits >= server->max_credits) - shdr->CreditRequest = cpu_to_le16(0); - else - shdr->CreditRequest = cpu_to_le16( - min_t(int, server->max_credits - - server->credits, 10)); - spin_unlock(&server->req_lock); - } else { - shdr->CreditRequest = cpu_to_le16(2); - } - shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid); - - if (!tcon) - goto out; - - /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ - /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ - if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - shdr->CreditCharge = cpu_to_le16(1); - /* else CreditCharge MBZ */ - - shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); - /* Uid is not converted */ - if (tcon->ses) - shdr->SessionId = cpu_to_le64(tcon->ses->Suid); - - /* - * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have - * to pass the path on the Open SMB prefixed by \\server\share. - * Not sure when we would need to do the augmented path (if ever) and - * setting this flag breaks the SMB2 open operation since it is - * illegal to send an empty path name (without \\server\share prefix) - * when the DFS flag is set in the SMB open header. We could - * consider setting the flag on all operations other than open - * but it is safer to net set it for now. - */ -/* if (tcon->share_flags & SHI1005_FLAGS_DFS) - shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ - - if (server && server->sign && !smb3_encryption_required(tcon)) - shdr->Flags |= SMB2_FLAGS_SIGNED; -out: - return; -} - -static int -smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server) -{ - int rc = 0; - struct nls_table *nls_codepage = NULL; - struct cifs_ses *ses; - - /* - * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so - * check for tcp and smb session status done differently - * for those three - in the calling routine. - */ - if (tcon == NULL) - return 0; - - /* - * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in - * cifs_tree_connect(). - */ - if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) - return 0; - - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_EXITING) { - /* - * only tree disconnect allowed when disconnecting ... - */ - if (smb2_command != SMB2_TREE_DISCONNECT) { - spin_unlock(&tcon->tc_lock); - cifs_dbg(FYI, "can not send cmd %d while umounting\n", - smb2_command); - return -ENODEV; - } - } - spin_unlock(&tcon->tc_lock); - - ses = tcon->ses; - if (!ses) - return -EIO; - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_EXITING) { - spin_unlock(&ses->ses_lock); - return -EIO; - } - spin_unlock(&ses->ses_lock); - if (!ses->server || !server) - return -EIO; - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect) { - /* - * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE - * here since they are implicitly done when session drops. - */ - switch (smb2_command) { - /* - * BB Should we keep oplock break and add flush to exceptions? - */ - case SMB2_TREE_DISCONNECT: - case SMB2_CANCEL: - case SMB2_CLOSE: - case SMB2_OPLOCK_BREAK: - spin_unlock(&server->srv_lock); - return -EAGAIN; - } - } - spin_unlock(&server->srv_lock); - -again: - rc = cifs_wait_for_server_reconnect(server, tcon->retry); - if (rc) - return rc; - - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { - spin_unlock(&ses->chan_lock); - return 0; - } - spin_unlock(&ses->chan_lock); - cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", - tcon->ses->chans_need_reconnect, - tcon->need_reconnect); - - mutex_lock(&ses->session_mutex); - /* - * Recheck after acquire mutex. If another thread is negotiating - * and the server never sends an answer the socket will be closed - * and tcpStatus set to reconnect. - */ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - mutex_unlock(&ses->session_mutex); - - if (tcon->retry) - goto again; - - rc = -EHOSTDOWN; - goto out; - } - spin_unlock(&server->srv_lock); - - nls_codepage = load_nls_default(); - - /* - * need to prevent multiple threads trying to simultaneously - * reconnect the same SMB session - */ - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD) { - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - /* this means that we only need to tree connect */ - if (tcon->need_reconnect) - goto skip_sess_setup; - - mutex_unlock(&ses->session_mutex); - goto out; - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - rc = cifs_negotiate_protocol(0, ses, server); - if (!rc) { - rc = cifs_setup_session(0, ses, server, nls_codepage); - if ((rc == -EACCES) && !tcon->retry) { - mutex_unlock(&ses->session_mutex); - rc = -EHOSTDOWN; - goto failed; - } else if (rc) { - mutex_unlock(&ses->session_mutex); - goto out; - } - } else { - mutex_unlock(&ses->session_mutex); - goto out; - } - -skip_sess_setup: - if (!tcon->need_reconnect) { - mutex_unlock(&ses->session_mutex); - goto out; - } - cifs_mark_open_files_invalid(tcon); - if (tcon->use_persistent) - tcon->need_reopen_files = true; - - rc = cifs_tree_connect(0, tcon, nls_codepage); - mutex_unlock(&ses->session_mutex); - - cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); - if (rc) { - /* If sess reconnected but tcon didn't, something strange ... */ - cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); - goto out; - } - - if (smb2_command != SMB2_INTERNAL_CMD) - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - - atomic_inc(&tconInfoReconnectCount); -out: - /* - * Check if handle based operation so we know whether we can continue - * or not without returning to caller to reset file handle. - */ - /* - * BB Is flush done by server on drop of tcp session? Should we special - * case it and skip above? - */ - switch (smb2_command) { - case SMB2_FLUSH: - case SMB2_READ: - case SMB2_WRITE: - case SMB2_LOCK: - case SMB2_IOCTL: - case SMB2_QUERY_DIRECTORY: - case SMB2_CHANGE_NOTIFY: - case SMB2_QUERY_INFO: - case SMB2_SET_INFO: - rc = -EAGAIN; - } -failed: - unload_nls(nls_codepage); - return rc; -} - -static void -fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void *buf, - unsigned int *total_len) -{ - struct smb2_pdu *spdu = buf; - /* lookup word count ie StructureSize from table */ - __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; - - /* - * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of - * largest operations (Create) - */ - memset(buf, 0, 256); - - smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server); - spdu->StructureSize2 = cpu_to_le16(parmsize); - - *total_len = parmsize + sizeof(struct smb2_hdr); -} - -/* - * Allocate and return pointer to an SMB request hdr, and set basic - * SMB information in the SMB header. If the return code is zero, this - * function must have filled in request_buf pointer. - */ -static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void **request_buf, unsigned int *total_len) -{ - /* BB eventually switch this to SMB2 specific small buf size */ - if (smb2_command == SMB2_SET_INFO) - *request_buf = cifs_buf_get(); - else - *request_buf = cifs_small_buf_get(); - if (*request_buf == NULL) { - /* BB should we add a retry in here if not a writepage? */ - return -ENOMEM; - } - - fill_small_buf(smb2_command, tcon, server, - (struct smb2_hdr *)(*request_buf), - total_len); - - if (tcon != NULL) { - uint16_t com_code = le16_to_cpu(smb2_command); - cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); - cifs_stats_inc(&tcon->num_smbs_sent); - } - - return 0; -} - -static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void **request_buf, unsigned int *total_len) -{ - int rc; - - rc = smb2_reconnect(smb2_command, tcon, server); - if (rc) - return rc; - - return __smb2_plain_req_init(smb2_command, tcon, server, request_buf, - total_len); -} - -static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void **request_buf, unsigned int *total_len) -{ - /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ - if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { - return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, - request_buf, total_len); - } - return smb2_plain_req_init(SMB2_IOCTL, tcon, server, - request_buf, total_len); -} - -/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ - -static void -build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; - pneg_ctxt->DataLength = cpu_to_le16(38); - pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); - get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); - pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; -} - -static void -build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; - pneg_ctxt->DataLength = - cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - - sizeof(struct smb2_neg_context)); - pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); - pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; - pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; - pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; -} - -static unsigned int -build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) -{ - unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities); - unsigned short num_algs = 1; /* number of signing algorithms sent */ - - pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; - /* - * Context Data length must be rounded to multiple of 8 for some servers - */ - pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) - - sizeof(struct smb2_neg_context) + - (num_algs * sizeof(u16)), 8)); - pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); - pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); - - ctxt_len += sizeof(__le16) * num_algs; - ctxt_len = ALIGN(ctxt_len, 8); - return ctxt_len; - /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ -} - -static void -build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; - if (require_gcm_256) { - pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */ - pneg_ctxt->CipherCount = cpu_to_le16(1); - pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM; - } else if (enable_gcm_256) { - pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */ - pneg_ctxt->CipherCount = cpu_to_le16(3); - pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; - pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM; - pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM; - } else { - pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */ - pneg_ctxt->CipherCount = cpu_to_le16(2); - pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; - pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; - } -} - -static unsigned int -build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) -{ - struct nls_table *cp = load_nls_default(); - - pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; - - /* copy up to max of first 100 bytes of server name to NetName field */ - pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); - /* context size is DataLength + minimal smb2_neg_context */ - return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8); -} - -static void -build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; - pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - pneg_ctxt->Name[0] = 0x93; - pneg_ctxt->Name[1] = 0xAD; - pneg_ctxt->Name[2] = 0x25; - pneg_ctxt->Name[3] = 0x50; - pneg_ctxt->Name[4] = 0x9C; - pneg_ctxt->Name[5] = 0xB4; - pneg_ctxt->Name[6] = 0x11; - pneg_ctxt->Name[7] = 0xE7; - pneg_ctxt->Name[8] = 0xB4; - pneg_ctxt->Name[9] = 0x23; - pneg_ctxt->Name[10] = 0x83; - pneg_ctxt->Name[11] = 0xDE; - pneg_ctxt->Name[12] = 0x96; - pneg_ctxt->Name[13] = 0x8B; - pneg_ctxt->Name[14] = 0xCD; - pneg_ctxt->Name[15] = 0x7C; -} - -static void -assemble_neg_contexts(struct smb2_negotiate_req *req, - struct TCP_Server_Info *server, unsigned int *total_len) -{ - unsigned int ctxt_len, neg_context_count; - struct TCP_Server_Info *pserver; - char *pneg_ctxt; - char *hostname; - - if (*total_len > 200) { - /* In case length corrupted don't want to overrun smb buffer */ - cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); - return; - } - - /* - * round up total_len of fixed part of SMB3 negotiate request to 8 - * byte boundary before adding negotiate contexts - */ - *total_len = ALIGN(*total_len, 8); - - pneg_ctxt = (*total_len) + (char *)req; - req->NegotiateContextOffset = cpu_to_le32(*total_len); - - build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); - ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - - build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); - ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - - /* - * secondary channels don't have the hostname field populated - * use the hostname field in the primary channel instead - */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - cifs_server_lock(pserver); - hostname = pserver->hostname; - if (hostname && (hostname[0] != 0)) { - ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt, - hostname); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - neg_context_count = 3; - } else - neg_context_count = 2; - cifs_server_unlock(pserver); - - build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); - *total_len += sizeof(struct smb2_posix_neg_context); - pneg_ctxt += sizeof(struct smb2_posix_neg_context); - neg_context_count++; - - if (server->compress_algorithm) { - build_compression_ctxt((struct smb2_compression_capabilities_context *) - pneg_ctxt); - ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - neg_context_count++; - } - - if (enable_negotiate_signing) { - ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *) - pneg_ctxt); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - neg_context_count++; - } - - /* check for and add transport_capabilities and signing capabilities */ - req->NegotiateContextCount = cpu_to_le16(neg_context_count); - -} - -/* If invalid preauth context warn but use what we requested, SHA-512 */ -static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt) -{ - unsigned int len = le16_to_cpu(ctxt->DataLength); - - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one HashAlgorithms member is accounted for. - */ - if (len < MIN_PREAUTH_CTXT_DATA_LEN) { - pr_warn_once("server sent bad preauth context\n"); - return; - } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) { - pr_warn_once("server sent invalid SaltLength\n"); - return; - } - if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1) - pr_warn_once("Invalid SMB3 hash algorithm count\n"); - if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) - pr_warn_once("unknown SMB3 hash algorithm\n"); -} - -static void decode_compress_ctx(struct TCP_Server_Info *server, - struct smb2_compression_capabilities_context *ctxt) -{ - unsigned int len = le16_to_cpu(ctxt->DataLength); - - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one CompressionAlgorithms member is accounted - * for. - */ - if (len < 10) { - pr_warn_once("server sent bad compression cntxt\n"); - return; - } - if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { - pr_warn_once("Invalid SMB3 compress algorithm count\n"); - return; - } - if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) { - pr_warn_once("unknown compression algorithm\n"); - return; - } - server->compress_algorithm = ctxt->CompressionAlgorithms[0]; -} - -static int decode_encrypt_ctx(struct TCP_Server_Info *server, - struct smb2_encryption_neg_context *ctxt) -{ - unsigned int len = le16_to_cpu(ctxt->DataLength); - - cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one Cipher flexible array member is accounted - * for. - */ - if (len < MIN_ENCRYPT_CTXT_DATA_LEN) { - pr_warn_once("server sent bad crypto ctxt len\n"); - return -EINVAL; - } - - if (le16_to_cpu(ctxt->CipherCount) != 1) { - pr_warn_once("Invalid SMB3.11 cipher count\n"); - return -EINVAL; - } - cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); - if (require_gcm_256) { - if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) { - cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); - return -EOPNOTSUPP; - } - } else if (ctxt->Ciphers[0] == 0) { - /* - * e.g. if server only supported AES256_CCM (very unlikely) - * or server supported no encryption types or had all disabled. - * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case - * in which mount requested encryption ("seal") checks later - * on during tree connection will return proper rc, but if - * seal not requested by client, since server is allowed to - * return 0 to indicate no supported cipher, we can't fail here - */ - server->cipher_type = 0; - server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION; - pr_warn_once("Server does not support requested encryption types\n"); - return 0; - } else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) && - (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) && - (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { - /* server returned a cipher we didn't ask for */ - pr_warn_once("Invalid SMB3.11 cipher returned\n"); - return -EINVAL; - } - server->cipher_type = ctxt->Ciphers[0]; - server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; - return 0; -} - -static void decode_signing_ctx(struct TCP_Server_Info *server, - struct smb2_signing_capabilities *pctxt) -{ - unsigned int len = le16_to_cpu(pctxt->DataLength); - - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one SigningAlgorithms flexible array member is - * accounted for. - */ - if ((len < 4) || (len > 16)) { - pr_warn_once("server sent bad signing negcontext\n"); - return; - } - if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) { - pr_warn_once("Invalid signing algorithm count\n"); - return; - } - if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) { - pr_warn_once("unknown signing algorithm\n"); - return; - } - - server->signing_negotiated = true; - server->signing_algorithm = le16_to_cpu(pctxt->SigningAlgorithms[0]); - cifs_dbg(FYI, "signing algorithm %d chosen\n", - server->signing_algorithm); -} - - -static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, - struct TCP_Server_Info *server, - unsigned int len_of_smb) -{ - struct smb2_neg_context *pctx; - unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); - unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); - unsigned int len_of_ctxts, i; - int rc = 0; - - cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); - if (len_of_smb <= offset) { - cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); - return -EINVAL; - } - - len_of_ctxts = len_of_smb - offset; - - for (i = 0; i < ctxt_cnt; i++) { - int clen; - /* check that offset is not beyond end of SMB */ - if (len_of_ctxts < sizeof(struct smb2_neg_context)) - break; - - pctx = (struct smb2_neg_context *)(offset + (char *)rsp); - clen = sizeof(struct smb2_neg_context) - + le16_to_cpu(pctx->DataLength); - /* - * 2.2.4 SMB2 NEGOTIATE Response - * Subsequent negotiate contexts MUST appear at the first 8-byte - * aligned offset following the previous negotiate context. - */ - if (i + 1 != ctxt_cnt) - clen = ALIGN(clen, 8); - if (clen > len_of_ctxts) - break; - - if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) - decode_preauth_context( - (struct smb2_preauth_neg_context *)pctx); - else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) - rc = decode_encrypt_ctx(server, - (struct smb2_encryption_neg_context *)pctx); - else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) - decode_compress_ctx(server, - (struct smb2_compression_capabilities_context *)pctx); - else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) - server->posix_ext_supported = true; - else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) - decode_signing_ctx(server, - (struct smb2_signing_capabilities *)pctx); - else - cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", - le16_to_cpu(pctx->ContextType)); - if (rc) - break; - - offset += clen; - len_of_ctxts -= clen; - } - return rc; -} - -static struct create_posix * -create_posix_buf(umode_t mode) -{ - struct create_posix *buf; - - buf = kzalloc(sizeof(struct create_posix), - GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = - cpu_to_le16(offsetof(struct create_posix, Mode)); - buf->ccontext.DataLength = cpu_to_le32(4); - buf->ccontext.NameOffset = - cpu_to_le16(offsetof(struct create_posix, Name)); - buf->ccontext.NameLength = cpu_to_le16(16); - - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - buf->Name[0] = 0x93; - buf->Name[1] = 0xAD; - buf->Name[2] = 0x25; - buf->Name[3] = 0x50; - buf->Name[4] = 0x9C; - buf->Name[5] = 0xB4; - buf->Name[6] = 0x11; - buf->Name[7] = 0xE7; - buf->Name[8] = 0xB4; - buf->Name[9] = 0x23; - buf->Name[10] = 0x83; - buf->Name[11] = 0xDE; - buf->Name[12] = 0x96; - buf->Name[13] = 0x8B; - buf->Name[14] = 0xCD; - buf->Name[15] = 0x7C; - buf->Mode = cpu_to_le32(mode); - cifs_dbg(FYI, "mode on posix create 0%o\n", mode); - return buf; -} - -static int -add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_posix_buf(mode); - if (mode == ACL_NO_MODE) - cifs_dbg(FYI, "Invalid mode\n"); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_posix); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_posix)); - *num_iovec = num + 1; - return 0; -} - - -/* - * - * SMB2 Worker functions follow: - * - * The general structure of the worker functions is: - * 1) Call smb2_init (assembles SMB2 header) - * 2) Initialize SMB2 command specific fields in fixed length area of SMB - * 3) Call smb_sendrcv2 (sends request on socket and waits for response) - * 4) Decode SMB2 command specific fields in the fixed length area - * 5) Decode variable length data area (if any for this SMB2 command type) - * 6) Call free smb buffer - * 7) return - * - */ - -int -SMB2_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - struct smb_rqst rqst; - struct smb2_negotiate_req *req; - struct smb2_negotiate_rsp *rsp; - struct kvec iov[1]; - struct kvec rsp_iov; - int rc; - int resp_buftype; - int blob_offset, blob_length; - char *security_blob; - int flags = CIFS_NEG_OP; - unsigned int total_len; - - cifs_dbg(FYI, "Negotiate protocol\n"); - - if (!server) { - WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; - } - - rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->hdr.SessionId = 0; - - memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); - memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); - - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) { - req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); - req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); - req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); - req->DialectCount = cpu_to_le16(3); - total_len += 6; - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); - req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); - req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); - req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); - req->DialectCount = cpu_to_le16(4); - total_len += 8; - } else { - /* otherwise send specific dialect */ - req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); - req->DialectCount = cpu_to_le16(1); - total_len += 2; - } - - /* only one of SMB2 signing flags may be set in SMB2 request */ - if (ses->sign) - req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); - else if (global_secflags & CIFSSEC_MAY_SIGN) - req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); - else - req->SecurityMode = 0; - - req->Capabilities = cpu_to_le32(server->vals->req_capabilities); - if (ses->chan_max > 1) - req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); - - /* ClientGUID must be zero for SMB2.02 dialect */ - if (server->vals->protocol_id == SMB20_PROT_ID) - memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); - else { - memcpy(req->ClientGUID, server->client_guid, - SMB2_CLIENT_GUID_SIZE); - if ((server->vals->protocol_id == SMB311_PROT_ID) || - (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) || - (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0)) - assemble_neg_contexts(req, server, &total_len); - } - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); - rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; - /* - * No tcon so can't do - * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); - */ - if (rc == -EOPNOTSUPP) { - cifs_server_dbg(VFS, "Dialect not supported by server. Consider specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n"); - goto neg_exit; - } else if (rc != 0) - goto neg_exit; - - rc = -EIO; - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) { - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_server_dbg(VFS, - "SMB2 dialect returned but not requested\n"); - goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { - cifs_server_dbg(VFS, - "SMB2.1 dialect returned but not requested\n"); - goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - /* ops set to 3.0 by default for default so update */ - server->ops = &smb311_operations; - server->vals = &smb311_values; - } - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_server_dbg(VFS, - "SMB2 dialect returned but not requested\n"); - goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { - /* ops set to 3.0 by default for default so update */ - server->ops = &smb21_operations; - server->vals = &smb21_values; - } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - server->ops = &smb311_operations; - server->vals = &smb311_values; - } - } else if (le16_to_cpu(rsp->DialectRevision) != - server->vals->protocol_id) { - /* if requested single dialect ensure returned dialect matched */ - cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", - le16_to_cpu(rsp->DialectRevision)); - goto neg_exit; - } - - cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); - - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) - cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) - cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) - cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) - cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) - cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); - else { - cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", - le16_to_cpu(rsp->DialectRevision)); - goto neg_exit; - } - - rc = 0; - server->dialect = le16_to_cpu(rsp->DialectRevision); - - /* - * Keep a copy of the hash after negprot. This hash will be - * the starting hash value for all sessions made from this - * server. - */ - memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, - SMB2_PREAUTH_HASH_SIZE); - - /* SMB2 only has an extended negflavor */ - server->negflavor = CIFS_NEGFLAVOR_EXTENDED; - /* set it to the maximum buffer size value we can send with 1 credit */ - server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), - SMB2_MAX_BUFFER_SIZE); - server->max_read = le32_to_cpu(rsp->MaxReadSize); - server->max_write = le32_to_cpu(rsp->MaxWriteSize); - server->sec_mode = le16_to_cpu(rsp->SecurityMode); - if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode) - cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n", - server->sec_mode); - server->capabilities = le32_to_cpu(rsp->Capabilities); - /* Internal types */ - server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; - - /* - * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context - * Set the cipher type manually. - */ - if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; - - security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, - (struct smb2_hdr *)rsp); - /* - * See MS-SMB2 section 2.2.4: if no blob, client picks default which - * for us will be - * ses->sectype = RawNTLMSSP; - * but for time being this is our only auth choice so doesn't matter. - * We just found a server which sets blob length to zero expecting raw. - */ - if (blob_length == 0) { - cifs_dbg(FYI, "missing security blob on negprot\n"); - server->sec_ntlmssp = true; - } - - rc = cifs_enable_signing(server, ses->sign); - if (rc) - goto neg_exit; - if (blob_length) { - rc = decode_negTokenInit(security_blob, blob_length, server); - if (rc == 1) - rc = 0; - else if (rc == 0) - rc = -EIO; - } - - if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - if (rsp->NegotiateContextCount) - rc = smb311_decode_neg_context(rsp, server, - rsp_iov.iov_len); - else - cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); - } -neg_exit: - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) -{ - int rc; - struct validate_negotiate_info_req *pneg_inbuf; - struct validate_negotiate_info_rsp *pneg_rsp = NULL; - u32 rsplen; - u32 inbuflen; /* max of 4 dialects */ - struct TCP_Server_Info *server = tcon->ses->server; - - cifs_dbg(FYI, "validate negotiate\n"); - - /* In SMB3.11 preauth integrity supersedes validate negotiate */ - if (server->dialect == SMB311_PROT_ID) - return 0; - - /* - * validation ioctl must be signed, so no point sending this if we - * can not sign it (ie are not known user). Even if signing is not - * required (enabled but not negotiated), in those cases we selectively - * sign just this, the first and only signed request on a connection. - * Having validation of negotiate info helps reduce attack vectors. - */ - if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) - return 0; /* validation requires signing */ - - if (tcon->ses->user_name == NULL) { - cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); - return 0; /* validation requires signing */ - } - - if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) - cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); - - pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); - if (!pneg_inbuf) - return -ENOMEM; - - pneg_inbuf->Capabilities = - cpu_to_le32(server->vals->req_capabilities); - if (tcon->ses->chan_max > 1) - pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); - - memcpy(pneg_inbuf->Guid, server->client_guid, - SMB2_CLIENT_GUID_SIZE); - - if (tcon->ses->sign) - pneg_inbuf->SecurityMode = - cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); - else if (global_secflags & CIFSSEC_MAY_SIGN) - pneg_inbuf->SecurityMode = - cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); - else - pneg_inbuf->SecurityMode = 0; - - - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) { - pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); - pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); - pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); - pneg_inbuf->DialectCount = cpu_to_le16(3); - /* SMB 2.1 not included so subtract one dialect from len */ - inbuflen = sizeof(*pneg_inbuf) - - (sizeof(pneg_inbuf->Dialects[0])); - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); - pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); - pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); - pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); - pneg_inbuf->DialectCount = cpu_to_le16(4); - /* structure is big enough for 4 dialects */ - inbuflen = sizeof(*pneg_inbuf); - } else { - /* otherwise specific dialect was requested */ - pneg_inbuf->Dialects[0] = - cpu_to_le16(server->vals->protocol_id); - pneg_inbuf->DialectCount = cpu_to_le16(1); - /* structure is big enough for 4 dialects, sending only 1 */ - inbuflen = sizeof(*pneg_inbuf) - - sizeof(pneg_inbuf->Dialects[0]) * 3; - } - - rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, - FSCTL_VALIDATE_NEGOTIATE_INFO, - (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize, - (char **)&pneg_rsp, &rsplen); - if (rc == -EOPNOTSUPP) { - /* - * Old Windows versions or Netapp SMB server can return - * not supported error. Client should accept it. - */ - cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); - rc = 0; - goto out_free_inbuf; - } else if (rc != 0) { - cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", - rc); - rc = -EIO; - goto out_free_inbuf; - } - - rc = -EIO; - if (rsplen != sizeof(*pneg_rsp)) { - cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", - rsplen); - - /* relax check since Mac returns max bufsize allowed on ioctl */ - if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) - goto out_free_rsp; - } - - /* check validate negotiate info response matches what we got earlier */ - if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) - goto vneg_out; - - if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) - goto vneg_out; - - /* do not validate server guid because not saved at negprot time yet */ - - if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | - SMB2_LARGE_FILES) != server->capabilities) - goto vneg_out; - - /* validate negotiate successful */ - rc = 0; - cifs_dbg(FYI, "validate negotiate info successful\n"); - goto out_free_rsp; - -vneg_out: - cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); -out_free_rsp: - kfree(pneg_rsp); -out_free_inbuf: - kfree(pneg_inbuf); - return rc; -} - -enum securityEnum -smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) -{ - switch (requested) { - case Kerberos: - case RawNTLMSSP: - return requested; - case NTLMv2: - return RawNTLMSSP; - case Unspecified: - if (server->sec_ntlmssp && - (global_secflags & CIFSSEC_MAY_NTLMSSP)) - return RawNTLMSSP; - if ((server->sec_kerberos || server->sec_mskerberos) && - (global_secflags & CIFSSEC_MAY_KRB5)) - return Kerberos; - fallthrough; - default: - return Unspecified; - } -} - -struct SMB2_sess_data { - unsigned int xid; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct nls_table *nls_cp; - void (*func)(struct SMB2_sess_data *); - int result; - u64 previous_session; - - /* we will send the SMB in three pieces: - * a fixed length beginning part, an optional - * SPNEGO blob (which can be zero length), and a - * last part which will include the strings - * and rest of bcc area. This allows us to avoid - * a large buffer 17K allocation - */ - int buf0_type; - struct kvec iov[2]; -}; - -static int -SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct smb2_sess_setup_req *req; - unsigned int total_len; - bool is_binding = false; - - rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, - (void **) &req, - &total_len); - if (rc) - return rc; - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - if (is_binding) { - req->hdr.SessionId = cpu_to_le64(ses->Suid); - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - req->PreviousSessionId = 0; - req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; - cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid); - } else { - /* First session, not a reauthenticate */ - req->hdr.SessionId = 0; - /* - * if reconnect, we need to send previous sess id - * otherwise it is 0 - */ - req->PreviousSessionId = cpu_to_le64(sess_data->previous_session); - req->Flags = 0; /* MBZ */ - cifs_dbg(FYI, "Fresh session. Previous: %llx\n", - sess_data->previous_session); - } - - /* enough to enable echos and oplocks and one max size write */ - req->hdr.CreditRequest = cpu_to_le16(130); - - /* only one of SMB2 signing flags may be set in SMB2 request */ - if (server->sign) - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; - else - req->SecurityMode = 0; - -#ifdef CONFIG_CIFS_DFS_UPCALL - req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); -#else - req->Capabilities = 0; -#endif /* DFS_UPCALL */ - - req->Channel = 0; /* MBZ */ - - sess_data->iov[0].iov_base = (char *)req; - /* 1 for pad */ - sess_data->iov[0].iov_len = total_len - 1; - /* - * This variable will be used to clear the buffer - * allocated above in case of any error in the calling function. - */ - sess_data->buf0_type = CIFS_SMALL_BUFFER; - - return 0; -} - -static void -SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) -{ - struct kvec *iov = sess_data->iov; - - /* iov[1] is already freed by caller */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) - memzero_explicit(iov[0].iov_base, iov[0].iov_len); - - free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); - sess_data->buf0_type = CIFS_NO_BUFFER; -} - -static int -SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) -{ - int rc; - struct smb_rqst rqst; - struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; - struct kvec rsp_iov = { NULL, 0 }; - - /* Testing shows that buffer offset must be at location of Buffer[0] */ - req->SecurityBufferOffset = - cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */); - req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = sess_data->iov; - rqst.rq_nvec = 2; - - /* BB add code to build os and lm fields */ - rc = cifs_send_recv(sess_data->xid, sess_data->ses, - sess_data->server, - &rqst, - &sess_data->buf0_type, - CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov); - cifs_small_buf_release(sess_data->iov[0].iov_base); - memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); - - return rc; -} - -static int -SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) -{ - int rc = 0; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - cifs_server_lock(server); - if (server->ops->generate_signingkey) { - rc = server->ops->generate_signingkey(ses, server); - if (rc) { - cifs_dbg(FYI, - "SMB3 session key generation failed\n"); - cifs_server_unlock(server); - return rc; - } - } - if (!server->session_estab) { - server->sequence_number = 0x2; - server->session_estab = true; - } - cifs_server_unlock(server); - - cifs_dbg(FYI, "SMB2/3 session established successfully\n"); - return rc; -} - -#ifdef CONFIG_CIFS_UPCALL -static void -SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct cifs_spnego_msg *msg; - struct key *spnego_key = NULL; - struct smb2_sess_setup_rsp *rsp = NULL; - bool is_binding = false; - - rc = SMB2_sess_alloc_buffer(sess_data); - if (rc) - goto out; - - spnego_key = cifs_get_spnego_key(ses, server); - if (IS_ERR(spnego_key)) { - rc = PTR_ERR(spnego_key); - if (rc == -ENOKEY) - cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); - spnego_key = NULL; - goto out; - } - - msg = spnego_key->payload.data[0]; - /* - * check version field to make sure that cifs.upcall is - * sending us a response in an expected form - */ - if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { - cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n", - CIFS_SPNEGO_UPCALL_VERSION, msg->version); - rc = -EKEYREJECTED; - goto out_put_spnego_key; - } - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - /* keep session key if binding */ - if (!is_binding) { - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", - msg->sesskey_len); - rc = -ENOMEM; - goto out_put_spnego_key; - } - ses->auth_key.len = msg->sesskey_len; - } - - sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; - sess_data->iov[1].iov_len = msg->secblob_len; - - rc = SMB2_sess_sendreceive(sess_data); - if (rc) - goto out_put_spnego_key; - - rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - /* keep session id and flags if binding */ - if (!is_binding) { - ses->Suid = le64_to_cpu(rsp->hdr.SessionId); - ses->session_flags = le16_to_cpu(rsp->SessionFlags); - } - - rc = SMB2_sess_establish_session(sess_data); -out_put_spnego_key: - key_invalidate(spnego_key); - key_put(spnego_key); - if (rc) { - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - ses->auth_key.len = 0; - } -out: - sess_data->result = rc; - sess_data->func = NULL; - SMB2_sess_free_buffer(sess_data); -} -#else -static void -SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) -{ - cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); - sess_data->result = -EOPNOTSUPP; - sess_data->func = NULL; -} -#endif - -static void -SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); - -static void -SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct smb2_sess_setup_rsp *rsp = NULL; - unsigned char *ntlmssp_blob = NULL; - bool use_spnego = false; /* else use raw ntlmssp */ - u16 blob_length = 0; - bool is_binding = false; - - /* - * If memory allocation is successful, caller of this function - * frees it. - */ - ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); - if (!ses->ntlmssp) { - rc = -ENOMEM; - goto out_err; - } - ses->ntlmssp->sesskey_per_smbsess = true; - - rc = SMB2_sess_alloc_buffer(sess_data); - if (rc) - goto out_err; - - rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob, - &blob_length, ses, server, - sess_data->nls_cp); - if (rc) - goto out; - - if (use_spnego) { - /* BB eventually need to add this */ - cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); - rc = -EOPNOTSUPP; - goto out; - } - sess_data->iov[1].iov_base = ntlmssp_blob; - sess_data->iov[1].iov_len = blob_length; - - rc = SMB2_sess_sendreceive(sess_data); - rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - - /* If true, rc here is expected and not an error */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && - rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) - rc = 0; - - if (rc) - goto out; - - if (offsetof(struct smb2_sess_setup_rsp, Buffer) != - le16_to_cpu(rsp->SecurityBufferOffset)) { - cifs_dbg(VFS, "Invalid security buffer offset %d\n", - le16_to_cpu(rsp->SecurityBufferOffset)); - rc = -EIO; - goto out; - } - rc = decode_ntlmssp_challenge(rsp->Buffer, - le16_to_cpu(rsp->SecurityBufferLength), ses); - if (rc) - goto out; - - cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - /* keep existing ses id and flags if binding */ - if (!is_binding) { - ses->Suid = le64_to_cpu(rsp->hdr.SessionId); - ses->session_flags = le16_to_cpu(rsp->SessionFlags); - } - -out: - kfree_sensitive(ntlmssp_blob); - SMB2_sess_free_buffer(sess_data); - if (!rc) { - sess_data->result = 0; - sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; - return; - } -out_err: - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - sess_data->result = rc; - sess_data->func = NULL; -} - -static void -SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct smb2_sess_setup_req *req; - struct smb2_sess_setup_rsp *rsp = NULL; - unsigned char *ntlmssp_blob = NULL; - bool use_spnego = false; /* else use raw ntlmssp */ - u16 blob_length = 0; - bool is_binding = false; - - rc = SMB2_sess_alloc_buffer(sess_data); - if (rc) - goto out; - - req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; - req->hdr.SessionId = cpu_to_le64(ses->Suid); - - rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, - ses, server, - sess_data->nls_cp); - if (rc) { - cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); - goto out; - } - - if (use_spnego) { - /* BB eventually need to add this */ - cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); - rc = -EOPNOTSUPP; - goto out; - } - sess_data->iov[1].iov_base = ntlmssp_blob; - sess_data->iov[1].iov_len = blob_length; - - rc = SMB2_sess_sendreceive(sess_data); - if (rc) - goto out; - - rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - /* keep existing ses id and flags if binding */ - if (!is_binding) { - ses->Suid = le64_to_cpu(rsp->hdr.SessionId); - ses->session_flags = le16_to_cpu(rsp->SessionFlags); - } - - rc = SMB2_sess_establish_session(sess_data); -#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS - if (ses->server->dialect < SMB30_PROT_ID) { - cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); - /* - * The session id is opaque in terms of endianness, so we can't - * print it as a long long. we dump it as we got it on the wire - */ - cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), - &ses->Suid); - cifs_dbg(VFS, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); - cifs_dbg(VFS, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, ses->auth_key.response); - } -#endif -out: - kfree_sensitive(ntlmssp_blob); - SMB2_sess_free_buffer(sess_data); - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - sess_data->result = rc; - sess_data->func = NULL; -} - -static int -SMB2_select_sec(struct SMB2_sess_data *sess_data) -{ - int type; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - type = smb2_select_sectype(server, ses->sectype); - cifs_dbg(FYI, "sess setup type %d\n", type); - if (type == Unspecified) { - cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); - return -EINVAL; - } - - switch (type) { - case Kerberos: - sess_data->func = SMB2_auth_kerberos; - break; - case RawNTLMSSP: - sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; - break; - default: - cifs_dbg(VFS, "secType %d not supported!\n", type); - return -EOPNOTSUPP; - } - - return 0; -} - -int -SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - struct SMB2_sess_data *sess_data; - - cifs_dbg(FYI, "Session Setup\n"); - - if (!server) { - WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; - } - - sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); - if (!sess_data) - return -ENOMEM; - - sess_data->xid = xid; - sess_data->ses = ses; - sess_data->server = server; - sess_data->buf0_type = CIFS_NO_BUFFER; - sess_data->nls_cp = (struct nls_table *) nls_cp; - sess_data->previous_session = ses->Suid; - - rc = SMB2_select_sec(sess_data); - if (rc) - goto out; - - /* - * Initialize the session hash with the server one. - */ - memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, - SMB2_PREAUTH_HASH_SIZE); - - while (sess_data->func) - sess_data->func(sess_data); - - if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) - cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); - rc = sess_data->result; -out: - kfree_sensitive(sess_data); - return rc; -} - -int -SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) -{ - struct smb_rqst rqst; - struct smb2_logoff_req *req; /* response is also trivial struct */ - int rc = 0; - struct TCP_Server_Info *server; - int flags = 0; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - cifs_dbg(FYI, "disconnect session %p\n", ses); - - if (ses && (ses->server)) - server = ses->server; - else - return -EIO; - - /* no need to send SMB logoff if uid already closed due to reconnect */ - spin_lock(&ses->chan_lock); - if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { - spin_unlock(&ses->chan_lock); - goto smb2_session_already_dead; - } - spin_unlock(&ses->chan_lock); - - rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, - (void **) &req, &total_len); - if (rc) - return rc; - - /* since no tcon, smb2_init can not do this, so do here */ - req->hdr.SessionId = cpu_to_le64(ses->Suid); - - if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) - flags |= CIFS_TRANSFORM_REQ; - else if (server->sign) - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, ses->server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - /* - * No tcon so can't do - * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); - */ - -smb2_session_already_dead: - return rc; -} - -static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) -{ - cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); -} - -#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) - -/* These are similar values to what Windows uses */ -static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) -{ - tcon->max_chunks = 256; - tcon->max_bytes_chunk = 1048576; - tcon->max_bytes_copy = 16777216; -} - -int -SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, - struct cifs_tcon *tcon, const struct nls_table *cp) -{ - struct smb_rqst rqst; - struct smb2_tree_connect_req *req; - struct smb2_tree_connect_rsp *rsp = NULL; - struct kvec iov[2]; - struct kvec rsp_iov = { NULL, 0 }; - int rc = 0; - int resp_buftype; - int unc_path_len; - __le16 *unc_path = NULL; - int flags = 0; - unsigned int total_len; - struct TCP_Server_Info *server; - - /* always use master channel */ - server = ses->server; - - cifs_dbg(FYI, "TCON\n"); - - if (!server || !tree) - return -EIO; - - unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); - if (unc_path == NULL) - return -ENOMEM; - - unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; - unc_path_len *= 2; - if (unc_path_len < 2) { - kfree(unc_path); - return -EINVAL; - } - - /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ - tcon->tid = 0; - atomic_set(&tcon->num_remote_opens, 0); - rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server, - (void **) &req, &total_len); - if (rc) { - kfree(unc_path); - return rc; - } - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - iov[0].iov_base = (char *)req; - /* 1 for pad */ - iov[0].iov_len = total_len - 1; - - /* Testing shows that buffer offset must be at location of Buffer[0] */ - req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) - - 1 /* pad */); - req->PathLength = cpu_to_le16(unc_path_len - 2); - iov[1].iov_base = unc_path; - iov[1].iov_len = unc_path_len; - - /* - * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 - * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 - * (Samba servers don't always set the flag so also check if null user) - */ - if ((server->dialect == SMB311_PROT_ID) && - !smb3_encryption_required(tcon) && - !(ses->session_flags & - (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && - ((ses->user_name != NULL) || (ses->sectype == Kerberos))) - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - - /* Need 64 for max size write so ask for more in case not there yet */ - req->hdr.CreditRequest = cpu_to_le16(64); - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); - rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; - trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); - if ((rc != 0) || (rsp == NULL)) { - cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); - tcon->need_reconnect = true; - goto tcon_error_exit; - } - - switch (rsp->ShareType) { - case SMB2_SHARE_TYPE_DISK: - cifs_dbg(FYI, "connection to disk share\n"); - break; - case SMB2_SHARE_TYPE_PIPE: - tcon->pipe = true; - cifs_dbg(FYI, "connection to pipe share\n"); - break; - case SMB2_SHARE_TYPE_PRINT: - tcon->print = true; - cifs_dbg(FYI, "connection to printer\n"); - break; - default: - cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); - rc = -EOPNOTSUPP; - goto tcon_error_exit; - } - - tcon->share_flags = le32_to_cpu(rsp->ShareFlags); - tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ - tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); - tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); - strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); - - if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && - ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) - cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); - - if (tcon->seal && - !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); - - init_copy_chunk_defaults(tcon); - if (server->ops->validate_negotiate) - rc = server->ops->validate_negotiate(xid, tcon); -tcon_exit: - - free_rsp_buf(resp_buftype, rsp); - kfree(unc_path); - return rc; - -tcon_error_exit: - if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) - cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); - goto tcon_exit; -} - -int -SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) -{ - struct smb_rqst rqst; - struct smb2_tree_disconnect_req *req; /* response is trivial */ - int rc = 0; - struct cifs_ses *ses = tcon->ses; - int flags = 0; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - cifs_dbg(FYI, "Tree Disconnect\n"); - - if (!ses || !(ses->server)) - return -EIO; - - trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name); - spin_lock(&ses->chan_lock); - if ((tcon->need_reconnect) || - (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) { - spin_unlock(&ses->chan_lock); - return 0; - } - spin_unlock(&ses->chan_lock); - - invalidate_all_cached_dirs(tcon); - - rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server, - (void **) &req, - &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, ses->server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); - trace_smb3_tdis_err(xid, tcon->tid, ses->Suid, rc); - } - trace_smb3_tdis_done(xid, tcon->tid, ses->Suid); - - return rc; -} - - -static struct create_durable * -create_durable_buf(void) -{ - struct create_durable *buf; - - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); - buf->ccontext.DataLength = cpu_to_le32(16); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'Q'; - return buf; -} - -static struct create_durable * -create_reconnect_durable_buf(struct cifs_fid *fid) -{ - struct create_durable *buf; - - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); - buf->ccontext.DataLength = cpu_to_le32(16); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Data.Fid.PersistentFileId = fid->persistent_fid; - buf->Data.Fid.VolatileFileId = fid->volatile_fid; - /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'C'; - return buf; -} - -static void -parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) -{ - struct create_on_disk_id *pdisk_id = (struct create_on_disk_id *)cc; - - cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", - pdisk_id->DiskFileId, pdisk_id->VolumeId); - buf->IndexNumber = pdisk_id->DiskFileId; -} - -static void -parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, - struct create_posix_rsp *posix) -{ - int sid_len; - u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); - u8 *end = beg + le32_to_cpu(cc->DataLength); - u8 *sid; - - memset(posix, 0, sizeof(*posix)); - - posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); - posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); - posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); - - sid = beg + 12; - sid_len = posix_info_sid_size(sid, end); - if (sid_len < 0) { - cifs_dbg(VFS, "bad owner sid in posix create response\n"); - return; - } - memcpy(&posix->owner, sid, sid_len); - - sid = sid + sid_len; - sid_len = posix_info_sid_size(sid, end); - if (sid_len < 0) { - cifs_dbg(VFS, "bad group sid in posix create response\n"); - return; - } - memcpy(&posix->group, sid, sid_len); - - cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", - posix->nlink, posix->mode, posix->reparse_tag); -} - -void -smb2_parse_contexts(struct TCP_Server_Info *server, - struct smb2_create_rsp *rsp, - unsigned int *epoch, char *lease_key, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix) -{ - char *data_offset; - struct create_context *cc; - unsigned int next; - unsigned int remaining; - char *name; - static const char smb3_create_tag_posix[] = { - 0x93, 0xAD, 0x25, 0x50, 0x9C, - 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, - 0xDE, 0x96, 0x8B, 0xCD, 0x7C - }; - - *oplock = 0; - data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); - remaining = le32_to_cpu(rsp->CreateContextsLength); - cc = (struct create_context *)data_offset; - - /* Initialize inode number to 0 in case no valid data in qfid context */ - if (buf) - buf->IndexNumber = 0; - - while (remaining >= sizeof(struct create_context)) { - name = le16_to_cpu(cc->NameOffset) + (char *)cc; - if (le16_to_cpu(cc->NameLength) == 4 && - strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4) == 0) - *oplock = server->ops->parse_lease_buf(cc, epoch, - lease_key); - else if (buf && (le16_to_cpu(cc->NameLength) == 4) && - strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) - parse_query_id_ctxt(cc, buf); - else if ((le16_to_cpu(cc->NameLength) == 16)) { - if (posix && - memcmp(name, smb3_create_tag_posix, 16) == 0) - parse_posix_ctxt(cc, buf, posix); - } - /* else { - cifs_dbg(FYI, "Context not matched with len %d\n", - le16_to_cpu(cc->NameLength)); - cifs_dump_mem("Cctxt name: ", name, 4); - } */ - - next = le32_to_cpu(cc->Next); - if (!next) - break; - remaining -= next; - cc = (struct create_context *)((char *)cc + next); - } - - if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) - *oplock = rsp->OplockLevel; - - return; -} - -static int -add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, - unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = server->vals->create_lease_size; - req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, - server->vals->create_lease_size); - *num_iovec = num + 1; - return 0; -} - -static struct create_durable_v2 * -create_durable_v2_buf(struct cifs_open_parms *oparms) -{ - struct cifs_fid *pfid = oparms->fid; - struct create_durable_v2 *buf; - - buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_v2, dcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_v2, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - - /* - * NB: Handle timeout defaults to 0, which allows server to choose - * (most servers default to 120 seconds) and most clients default to 0. - * This can be overridden at mount ("handletimeout=") if the user wants - * a different persistent (or resilient) handle timeout for all opens - * opens on a particular SMB3 mount. - */ - buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); - buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); - generate_random_uuid(buf->dcontext.CreateGuid); - memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); - - /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'Q'; - return buf; -} - -static struct create_durable_handle_reconnect_v2 * -create_reconnect_durable_v2_buf(struct cifs_fid *fid) -{ - struct create_durable_handle_reconnect_v2 *buf; - - buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), - GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = - cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, - dcontext)); - buf->ccontext.DataLength = - cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); - buf->ccontext.NameOffset = - cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, - Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - - buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; - buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; - buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); - memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); - - /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'C'; - return buf; -} - -static int -add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_durable_v2_buf(oparms); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable_v2); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = - cpu_to_le32(sizeof(struct smb2_create_req) + - iov[1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable_v2)); - *num_iovec = num + 1; - return 0; -} - -static int -add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - /* indicate that we don't need to relock the file */ - oparms->reconnect = false; - - iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = - cpu_to_le32(sizeof(struct smb2_create_req) + - iov[1].iov_len); - le32_add_cpu(&req->CreateContextsLength, - sizeof(struct create_durable_handle_reconnect_v2)); - *num_iovec = num + 1; - return 0; -} - -static int -add_durable_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms, bool use_persistent) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - if (use_persistent) { - if (oparms->reconnect) - return add_durable_reconnect_v2_context(iov, num_iovec, - oparms); - else - return add_durable_v2_context(iov, num_iovec, oparms); - } - - if (oparms->reconnect) { - iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); - /* indicate that we don't need to relock the file */ - oparms->reconnect = false; - } else - iov[num].iov_base = create_durable_buf(); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = - cpu_to_le32(sizeof(struct smb2_create_req) + - iov[1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable)); - *num_iovec = num + 1; - return 0; -} - -/* See MS-SMB2 2.2.13.2.7 */ -static struct crt_twarp_ctxt * -create_twarp_buf(__u64 timewarp) -{ - struct crt_twarp_ctxt *buf; - - buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct crt_twarp_ctxt, Timestamp)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct crt_twarp_ctxt, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ - buf->Name[0] = 'T'; - buf->Name[1] = 'W'; - buf->Name[2] = 'r'; - buf->Name[3] = 'p'; - buf->Timestamp = cpu_to_le64(timewarp); - return buf; -} - -/* See MS-SMB2 2.2.13.2.7 */ -static int -add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_twarp_buf(timewarp); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct crt_twarp_ctxt); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_twarp_ctxt)); - *num_iovec = num + 1; - return 0; -} - -/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ -static void setup_owner_group_sids(char *buf) -{ - struct owner_group_sids *sids = (struct owner_group_sids *)buf; - - /* Populate the user ownership fields S-1-5-88-1 */ - sids->owner.Revision = 1; - sids->owner.NumAuth = 3; - sids->owner.Authority[5] = 5; - sids->owner.SubAuthorities[0] = cpu_to_le32(88); - sids->owner.SubAuthorities[1] = cpu_to_le32(1); - sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); - - /* Populate the group ownership fields S-1-5-88-2 */ - sids->group.Revision = 1; - sids->group.NumAuth = 3; - sids->group.Authority[5] = 5; - sids->group.SubAuthorities[0] = cpu_to_le32(88); - sids->group.SubAuthorities[1] = cpu_to_le32(2); - sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); - - cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val); -} - -/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ -static struct crt_sd_ctxt * -create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) -{ - struct crt_sd_ctxt *buf; - __u8 *ptr, *aclptr; - unsigned int acelen, acl_size, ace_count; - unsigned int owner_offset = 0; - unsigned int group_offset = 0; - struct smb3_acl acl = {}; - - *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); - - if (set_owner) { - /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ - *len += sizeof(struct owner_group_sids); - } - - buf = kzalloc(*len, GFP_KERNEL); - if (buf == NULL) - return buf; - - ptr = (__u8 *)&buf[1]; - if (set_owner) { - /* offset fields are from beginning of security descriptor not of create context */ - owner_offset = ptr - (__u8 *)&buf->sd; - buf->sd.OffsetOwner = cpu_to_le32(owner_offset); - group_offset = owner_offset + offsetof(struct owner_group_sids, group); - buf->sd.OffsetGroup = cpu_to_le32(group_offset); - - setup_owner_group_sids(ptr); - ptr += sizeof(struct owner_group_sids); - } else { - buf->sd.OffsetOwner = 0; - buf->sd.OffsetGroup = 0; - } - - buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ - buf->Name[0] = 'S'; - buf->Name[1] = 'e'; - buf->Name[2] = 'c'; - buf->Name[3] = 'D'; - buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ - - /* - * ACL is "self relative" ie ACL is stored in contiguous block of memory - * and "DP" ie the DACL is present - */ - buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); - - /* offset owner, group and Sbz1 and SACL are all zero */ - buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); - /* Ship the ACL for now. we will copy it into buf later. */ - aclptr = ptr; - ptr += sizeof(struct smb3_acl); - - /* create one ACE to hold the mode embedded in reserved special SID */ - acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode); - ptr += acelen; - acl_size = acelen + sizeof(struct smb3_acl); - ace_count = 1; - - if (set_owner) { - /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ - acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr); - ptr += acelen; - acl_size += acelen; - ace_count += 1; - } - - /* and one more ACE to allow access for authenticated users */ - acelen = setup_authusers_ACE((struct cifs_ace *)ptr); - ptr += acelen; - acl_size += acelen; - ace_count += 1; - - acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ - acl.AclSize = cpu_to_le16(acl_size); - acl.AceCount = cpu_to_le16(ace_count); - /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */ - memcpy(aclptr, &acl, sizeof(struct smb3_acl)); - - buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); - *len = round_up((unsigned int)(ptr - (__u8 *)buf), 8); - - return buf; -} - -static int -add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - unsigned int len = 0; - - iov[num].iov_base = create_sd_buf(mode, set_owner, &len); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = len; - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, len); - *num_iovec = num + 1; - return 0; -} - -static struct crt_query_id_ctxt * -create_query_id_buf(void) -{ - struct crt_query_id_ctxt *buf; - - buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(0); - buf->ccontext.DataLength = cpu_to_le32(0); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct crt_query_id_ctxt, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */ - buf->Name[0] = 'Q'; - buf->Name[1] = 'F'; - buf->Name[2] = 'i'; - buf->Name[3] = 'd'; - return buf; -} - -/* See MS-SMB2 2.2.13.2.9 */ -static int -add_query_id_context(struct kvec *iov, unsigned int *num_iovec) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_query_id_buf(); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct crt_query_id_ctxt); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_query_id_ctxt)); - *num_iovec = num + 1; - return 0; -} - -static int -alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, - const char *treename, const __le16 *path) -{ - int treename_len, path_len; - struct nls_table *cp; - const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; - - /* - * skip leading "\\" - */ - treename_len = strlen(treename); - if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) - return -EINVAL; - - treename += 2; - treename_len -= 2; - - path_len = UniStrnlen((wchar_t *)path, PATH_MAX); - - /* make room for one path separator only if @path isn't empty */ - *out_len = treename_len + (path[0] ? 1 : 0) + path_len; - - /* - * final path needs to be 8-byte aligned as specified in - * MS-SMB2 2.2.13 SMB2 CREATE Request. - */ - *out_size = round_up(*out_len * sizeof(__le16), 8); - *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); - if (!*out_path) - return -ENOMEM; - - cp = load_nls_default(); - cifs_strtoUTF16(*out_path, treename, treename_len, cp); - - /* Do not append the separator if the path is empty */ - if (path[0] != cpu_to_le16(0x0000)) { - UniStrcat(*out_path, sep); - UniStrcat(*out_path, path); - } - - unload_nls(cp); - - return 0; -} - -int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *full_path, - struct cifs_sb_info *cifs_sb) -{ - struct smb_rqst rqst; - struct smb2_create_req *req; - struct smb2_create_rsp *rsp = NULL; - struct cifs_ses *ses = tcon->ses; - struct kvec iov[3]; /* make sure at least one for each open context */ - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype; - int uni_path_len; - __le16 *copy_path = NULL; - int copy_size; - int rc = 0; - unsigned int n_iov = 2; - __u32 file_attributes = 0; - char *pc_buf = NULL; - int flags = 0; - unsigned int total_len; - __le16 *utf16_path = NULL; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - - cifs_dbg(FYI, "mkdir\n"); - - /* resource #1: path allocation */ - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - if (!ses || !server) { - rc = -EIO; - goto err_free_path; - } - - /* resource #2: request */ - rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, - (void **) &req, &total_len); - if (rc) - goto err_free_path; - - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->ImpersonationLevel = IL_IMPERSONATION; - req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); - /* File attributes ignored on open (used in create though) */ - req->FileAttributes = cpu_to_le32(file_attributes); - req->ShareAccess = FILE_SHARE_ALL_LE; - req->CreateDisposition = cpu_to_le32(FILE_CREATE); - req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); - - iov[0].iov_base = (char *)req; - /* -1 since last byte is buf[0] which is sent below (path) */ - iov[0].iov_len = total_len - 1; - - req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); - - /* [MS-SMB2] 2.2.13 NameOffset: - * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of - * the SMB2 header, the file name includes a prefix that will - * be processed during DFS name normalization as specified in - * section 3.3.5.9. Otherwise, the file name is relative to - * the share that is identified by the TreeId in the SMB2 - * header. - */ - if (tcon->share_flags & SHI1005_FLAGS_DFS) { - int name_len; - - req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; - rc = alloc_path_with_tree_prefix(©_path, ©_size, - &name_len, - tcon->tree_name, utf16_path); - if (rc) - goto err_free_req; - - req->NameLength = cpu_to_le16(name_len * 2); - uni_path_len = copy_size; - /* free before overwriting resource */ - kfree(utf16_path); - utf16_path = copy_path; - } else { - uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2; - /* MUST set path len (NameLength) to 0 opening root of share */ - req->NameLength = cpu_to_le16(uni_path_len - 2); - if (uni_path_len % 8 != 0) { - copy_size = roundup(uni_path_len, 8); - copy_path = kzalloc(copy_size, GFP_KERNEL); - if (!copy_path) { - rc = -ENOMEM; - goto err_free_req; - } - memcpy((char *)copy_path, (const char *)utf16_path, - uni_path_len); - uni_path_len = copy_size; - /* free before overwriting resource */ - kfree(utf16_path); - utf16_path = copy_path; - } - } - - iov[1].iov_len = uni_path_len; - iov[1].iov_base = utf16_path; - req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; - - if (tcon->posix_extensions) { - /* resource #3: posix buf */ - rc = add_posix_context(iov, &n_iov, mode); - if (rc) - goto err_free_req; - pc_buf = iov[n_iov-1].iov_base; - } - - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = n_iov; - - /* no need to inc num_remote_opens because we close it just below */ - trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, - FILE_WRITE_ATTRIBUTES); - /* resource #4: response buffer */ - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); - trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, - CREATE_NOT_FILE, - FILE_WRITE_ATTRIBUTES, rc); - goto err_free_rsp_buf; - } - - /* - * Although unlikely to be possible for rsp to be null and rc not set, - * adding check below is slightly safer long term (and quiets Coverity - * warning) - */ - rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; - if (rsp == NULL) { - rc = -EIO; - kfree(pc_buf); - goto err_free_req; - } - - trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, - CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); - - SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); - - /* Eventually save off posix specific response info and timestaps */ - -err_free_rsp_buf: - free_rsp_buf(resp_buftype, rsp); - kfree(pc_buf); -err_free_req: - cifs_small_buf_release(req); -err_free_path: - kfree(utf16_path); - return rc; -} - -int -SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, __u8 *oplock, - struct cifs_open_parms *oparms, __le16 *path) -{ - struct smb2_create_req *req; - unsigned int n_iov = 2; - __u32 file_attributes = 0; - int copy_size; - int uni_path_len; - unsigned int total_len; - struct kvec *iov = rqst->rq_iov; - __le16 *copy_path; - int rc; - - rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - iov[0].iov_base = (char *)req; - /* -1 since last byte is buf[0] which is sent below (path) */ - iov[0].iov_len = total_len - 1; - - if (oparms->create_options & CREATE_OPTION_READONLY) - file_attributes |= ATTR_READONLY; - if (oparms->create_options & CREATE_OPTION_SPECIAL) - file_attributes |= ATTR_SYSTEM; - - req->ImpersonationLevel = IL_IMPERSONATION; - req->DesiredAccess = cpu_to_le32(oparms->desired_access); - /* File attributes ignored on open (used in create though) */ - req->FileAttributes = cpu_to_le32(file_attributes); - req->ShareAccess = FILE_SHARE_ALL_LE; - - req->CreateDisposition = cpu_to_le32(oparms->disposition); - req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); - req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); - - /* [MS-SMB2] 2.2.13 NameOffset: - * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of - * the SMB2 header, the file name includes a prefix that will - * be processed during DFS name normalization as specified in - * section 3.3.5.9. Otherwise, the file name is relative to - * the share that is identified by the TreeId in the SMB2 - * header. - */ - if (tcon->share_flags & SHI1005_FLAGS_DFS) { - int name_len; - - req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; - rc = alloc_path_with_tree_prefix(©_path, ©_size, - &name_len, - tcon->tree_name, path); - if (rc) - return rc; - req->NameLength = cpu_to_le16(name_len * 2); - uni_path_len = copy_size; - path = copy_path; - } else { - uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; - /* MUST set path len (NameLength) to 0 opening root of share */ - req->NameLength = cpu_to_le16(uni_path_len - 2); - copy_size = round_up(uni_path_len, 8); - copy_path = kzalloc(copy_size, GFP_KERNEL); - if (!copy_path) - return -ENOMEM; - memcpy((char *)copy_path, (const char *)path, - uni_path_len); - uni_path_len = copy_size; - path = copy_path; - } - - iov[1].iov_len = uni_path_len; - iov[1].iov_base = path; - - if ((!server->oplocks) || (tcon->no_lease)) - *oplock = SMB2_OPLOCK_LEVEL_NONE; - - if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || - *oplock == SMB2_OPLOCK_LEVEL_NONE) - req->RequestedOplockLevel = *oplock; - else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && - (oparms->create_options & CREATE_NOT_FILE)) - req->RequestedOplockLevel = *oplock; /* no srv lease support */ - else { - rc = add_lease_context(server, iov, &n_iov, - oparms->fid->lease_key, oplock); - if (rc) - return rc; - } - - if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { - /* need to set Next field of lease context if we request it */ - if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = - cpu_to_le32(server->vals->create_lease_size); - } - - rc = add_durable_context(iov, &n_iov, oparms, - tcon->use_persistent); - if (rc) - return rc; - } - - if (tcon->posix_extensions) { - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = - cpu_to_le32(iov[n_iov-1].iov_len); - } - - rc = add_posix_context(iov, &n_iov, oparms->mode); - if (rc) - return rc; - } - - if (tcon->snapshot_time) { - cifs_dbg(FYI, "adding snapshot context\n"); - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = - cpu_to_le32(iov[n_iov-1].iov_len); - } - - rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); - if (rc) - return rc; - } - - if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { - bool set_mode; - bool set_owner; - - if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && - (oparms->mode != ACL_NO_MODE)) - set_mode = true; - else { - set_mode = false; - oparms->mode = ACL_NO_MODE; - } - - if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) - set_owner = true; - else - set_owner = false; - - if (set_owner | set_mode) { - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); - } - - cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); - rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); - if (rc) - return rc; - } - } - - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); - } - add_query_id_context(iov, &n_iov); - - rqst->rq_nvec = n_iov; - return 0; -} - -/* rq_iov[0] is the request and is released by cifs_small_buf_release(). - * All other vectors are freed by kfree(). - */ -void -SMB2_open_free(struct smb_rqst *rqst) -{ - int i; - - if (rqst && rqst->rq_iov) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); - for (i = 1; i < rqst->rq_nvec; i++) - if (rqst->rq_iov[i].iov_base != smb2_padding) - kfree(rqst->rq_iov[i].iov_base); - } -} - -int -SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, - __u8 *oplock, struct smb2_file_all_info *buf, - struct create_posix_rsp *posix, - struct kvec *err_iov, int *buftype) -{ - struct smb_rqst rqst; - struct smb2_create_rsp *rsp = NULL; - struct cifs_tcon *tcon = oparms->tcon; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct kvec iov[SMB2_CREATE_IOV_SIZE]; - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype = CIFS_NO_BUFFER; - int rc = 0; - int flags = 0; - - cifs_dbg(FYI, "create/open\n"); - if (!ses || !server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; - - rc = SMB2_open_init(tcon, server, - &rqst, oplock, oparms, path); - if (rc) - goto creat_exit; - - trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, - oparms->create_options, oparms->desired_access); - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, - &rsp_iov); - rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); - if (err_iov && rsp) { - *err_iov = rsp_iov; - *buftype = resp_buftype; - resp_buftype = CIFS_NO_BUFFER; - rsp = NULL; - } - trace_smb3_open_err(xid, tcon->tid, ses->Suid, - oparms->create_options, oparms->desired_access, rc); - if (rc == -EREMCHG) { - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - tcon->need_reconnect = true; - } - goto creat_exit; - } else if (rsp == NULL) /* unlikely to happen, but safer to check */ - goto creat_exit; - else - trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, - oparms->create_options, oparms->desired_access); - - atomic_inc(&tcon->num_remote_opens); - oparms->fid->persistent_fid = rsp->PersistentFileId; - oparms->fid->volatile_fid = rsp->VolatileFileId; - oparms->fid->access = oparms->desired_access; -#ifdef CONFIG_CIFS_DEBUG2 - oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); -#endif /* CIFS_DEBUG2 */ - - if (buf) { - buf->CreationTime = rsp->CreationTime; - buf->LastAccessTime = rsp->LastAccessTime; - buf->LastWriteTime = rsp->LastWriteTime; - buf->ChangeTime = rsp->ChangeTime; - buf->AllocationSize = rsp->AllocationSize; - buf->EndOfFile = rsp->EndofFile; - buf->Attributes = rsp->FileAttributes; - buf->NumberOfLinks = cpu_to_le32(1); - buf->DeletePending = 0; - } - - - smb2_parse_contexts(server, rsp, &oparms->fid->epoch, - oparms->fid->lease_key, oplock, buf, posix); -creat_exit: - SMB2_open_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int -SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, - __u32 max_response_size) -{ - struct smb2_ioctl_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - char *in_data_buf; - - rc = smb2_ioctl_req_init(opcode, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (indatalen) { - /* - * indatalen is usually small at a couple of bytes max, so - * just allocate through generic pool - */ - in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); - if (!in_data_buf) { - cifs_small_buf_release(req); - return -ENOMEM; - } - } - - req->CtlCode = cpu_to_le32(opcode); - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - - iov[0].iov_base = (char *)req; - /* - * If no input data, the size of ioctl struct in - * protocol spec still includes a 1 byte data buffer, - * but if input data passed to ioctl, we do not - * want to double count this, so we do not send - * the dummy one byte of data in iovec[0] if sending - * input data (in iovec[1]). - */ - if (indatalen) { - req->InputCount = cpu_to_le32(indatalen); - /* do not set InputOffset if no input data */ - req->InputOffset = - cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); - rqst->rq_nvec = 2; - iov[0].iov_len = total_len - 1; - iov[1].iov_base = in_data_buf; - iov[1].iov_len = indatalen; - } else { - rqst->rq_nvec = 1; - iov[0].iov_len = total_len; - } - - req->OutputOffset = 0; - req->OutputCount = 0; /* MBZ */ - - /* - * In most cases max_response_size is set to 16K (CIFSMaxBufSize) - * We Could increase default MaxOutputResponse, but that could require - * more credits. Windows typically sets this smaller, but for some - * ioctls it may be useful to allow server to send more. No point - * limiting what the server can send as long as fits in one credit - * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want - * to increase this limit up in the future. - * Note that for snapshot queries that servers like Azure expect that - * the first query be minimal size (and just used to get the number/size - * of previous versions) so response size must be specified as EXACTLY - * sizeof(struct snapshot_array) which is 16 when rounded up to multiple - * of eight bytes. Currently that is the only case where we set max - * response size smaller. - */ - req->MaxOutputResponse = cpu_to_le32(max_response_size); - req->hdr.CreditCharge = - cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), - SMB2_MAX_BUFFER_SIZE)); - /* always an FSCTL (for now) */ - req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); - - /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ - if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - - return 0; -} - -void -SMB2_ioctl_free(struct smb_rqst *rqst) -{ - int i; - if (rqst && rqst->rq_iov) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ - for (i = 1; i < rqst->rq_nvec; i++) - if (rqst->rq_iov[i].iov_base != smb2_padding) - kfree(rqst->rq_iov[i].iov_base); - } -} - - -/* - * SMB2 IOCTL is used for both IOCTLs and FSCTLs - */ -int -SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, - u32 max_out_data_len, char **out_data, - u32 *plen /* returned data len */) -{ - struct smb_rqst rqst; - struct smb2_ioctl_rsp *rsp = NULL; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct kvec iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype = CIFS_NO_BUFFER; - int rc = 0; - int flags = 0; - - cifs_dbg(FYI, "SMB2 IOCTL\n"); - - if (out_data != NULL) - *out_data = NULL; - - /* zero out returned data len, in case of error */ - if (plen) - *plen = 0; - - if (!tcon) - return -EIO; - - ses = tcon->ses; - if (!ses) - return -EIO; - - server = cifs_pick_channel(ses); - if (!server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, - &rqst, persistent_fid, volatile_fid, opcode, - in_data, indatalen, max_out_data_len); - if (rc) - goto ioctl_exit; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, - &rsp_iov); - rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; - - if (rc != 0) - trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, - ses->Suid, 0, opcode, rc); - - if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) { - cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); - goto ioctl_exit; - } else if (rc == -EINVAL) { - if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && - (opcode != FSCTL_SRV_COPYCHUNK)) { - cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); - goto ioctl_exit; - } - } else if (rc == -E2BIG) { - if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) { - cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); - goto ioctl_exit; - } - } - - /* check if caller wants to look at return data or just return rc */ - if ((plen == NULL) || (out_data == NULL)) - goto ioctl_exit; - - /* - * Although unlikely to be possible for rsp to be null and rc not set, - * adding check below is slightly safer long term (and quiets Coverity - * warning) - */ - if (rsp == NULL) { - rc = -EIO; - goto ioctl_exit; - } - - *plen = le32_to_cpu(rsp->OutputCount); - - /* We check for obvious errors in the output buffer length and offset */ - if (*plen == 0) - goto ioctl_exit; /* server returned no data */ - else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { - cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); - *plen = 0; - rc = -EIO; - goto ioctl_exit; - } - - if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { - cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, - le32_to_cpu(rsp->OutputOffset)); - *plen = 0; - rc = -EIO; - goto ioctl_exit; - } - - *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), - *plen, GFP_KERNEL); - if (*out_data == NULL) { - rc = -ENOMEM; - goto ioctl_exit; - } - -ioctl_exit: - SMB2_ioctl_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -/* - * Individual callers to ioctl worker function follow - */ - -int -SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid) -{ - int rc; - struct compress_ioctl fsctl_input; - char *ret_data = NULL; - - fsctl_input.CompressionState = - cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); - - rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, - FSCTL_SET_COMPRESSION, - (char *)&fsctl_input /* data input */, - 2 /* in data len */, CIFSMaxBufSize /* max out data */, - &ret_data /* out data */, NULL); - - cifs_dbg(FYI, "set compression rc %d\n", rc); - - return rc; -} - -int -SMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, bool query_attrs) -{ - struct smb2_close_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - if (query_attrs) - req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - else - req->Flags = 0; - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - return 0; -} - -void -SMB2_close_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -int -__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_network_open_info *pbuf) -{ - struct smb_rqst rqst; - struct smb2_close_rsp *rsp = NULL; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buftype = CIFS_NO_BUFFER; - int rc = 0; - int flags = 0; - bool query_attrs = false; - - cifs_dbg(FYI, "Close\n"); - - if (!ses || !server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - /* check if need to ask server to return timestamps in close response */ - if (pbuf) - query_attrs = true; - - trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); - rc = SMB2_close_init(tcon, server, - &rqst, persistent_fid, volatile_fid, - query_attrs); - if (rc) - goto close_exit; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); - trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, - rc); - goto close_exit; - } else { - trace_smb3_close_done(xid, persistent_fid, tcon->tid, - ses->Suid); - /* - * Note that have to subtract 4 since struct network_open_info - * has a final 4 byte pad that close response does not have - */ - if (pbuf) - memcpy(pbuf, (char *)&rsp->CreationTime, sizeof(*pbuf) - 4); - } - - atomic_dec(&tcon->num_remote_opens); -close_exit: - SMB2_close_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - - /* retry close in a worker thread if this one is interrupted */ - if (is_interrupt_error(rc)) { - int tmp_rc; - - tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, - volatile_fid); - if (tmp_rc) - cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", - persistent_fid, tmp_rc); - } - return rc; -} - -int -SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid) -{ - return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); -} - -int -smb2_validate_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int min_buf_size) -{ - unsigned int smb_len = iov->iov_len; - char *end_of_smb = smb_len + (char *)iov->iov_base; - char *begin_of_buf = offset + (char *)iov->iov_base; - char *end_of_buf = begin_of_buf + buffer_length; - - - if (buffer_length < min_buf_size) { - cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", - buffer_length, min_buf_size); - return -EINVAL; - } - - /* check if beyond RFC1001 maximum length */ - if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { - cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", - buffer_length, smb_len); - return -EINVAL; - } - - if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { - cifs_dbg(VFS, "Invalid server response, bad offset to data\n"); - return -EINVAL; - } - - return 0; -} - -/* - * If SMB buffer fields are valid, copy into temporary buffer to hold result. - * Caller must free buffer. - */ -int -smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int minbufsize, - char *data) -{ - char *begin_of_buf = offset + (char *)iov->iov_base; - int rc; - - if (!data) - return -EINVAL; - - rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); - if (rc) - return rc; - - memcpy(data, begin_of_buf, minbufsize); - - return 0; -} - -int -SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - u8 info_class, u8 info_type, u32 additional_info, - size_t output_len, size_t input_len, void *input) -{ - struct smb2_query_info_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->InfoType = info_type; - req->FileInfoClass = info_class; - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - req->AdditionalInformation = cpu_to_le32(additional_info); - - req->OutputBufferLength = cpu_to_le32(output_len); - if (input_len) { - req->InputBufferLength = cpu_to_le32(input_len); - /* total_len for smb query request never close to le16 max */ - req->InputBufferOffset = cpu_to_le16(total_len - 1); - memcpy(req->Buffer, input, input_len); - } - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1 + input_len; - return 0; -} - -void -SMB2_query_info_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -static int -query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, - u32 additional_info, size_t output_len, size_t min_len, void **data, - u32 *dlen) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov[1]; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype = CIFS_NO_BUFFER; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server; - int flags = 0; - bool allocated = false; - - cifs_dbg(FYI, "Query Info\n"); - - if (!ses) - return -EIO; - server = cifs_pick_channel(ses); - if (!server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = SMB2_query_info_init(tcon, server, - &rqst, persistent_fid, volatile_fid, - info_class, info_type, additional_info, - output_len, 0, NULL); - if (rc) - goto qinf_exit; - - trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type); - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - trace_smb3_query_info_err(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type, rc); - goto qinf_exit; - } - - trace_smb3_query_info_done(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type); - - if (dlen) { - *dlen = le32_to_cpu(rsp->OutputBufferLength); - if (!*data) { - *data = kmalloc(*dlen, GFP_KERNEL); - if (!*data) { - cifs_tcon_dbg(VFS, - "Error %d allocating memory for acl\n", - rc); - *dlen = 0; - rc = -ENOMEM; - goto qinf_exit; - } - allocated = true; - } - } - - rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), - &rsp_iov, dlen ? *dlen : min_len, *data); - if (rc && allocated) { - kfree(*data); - *data = NULL; - *dlen = 0; - } - -qinf_exit: - SMB2_query_info_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) -{ - return query_info(xid, tcon, persistent_fid, volatile_fid, - FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + PATH_MAX * 2, - sizeof(struct smb2_file_all_info), (void **)&data, - NULL); -} - -#if 0 -/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ -int -SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) -{ - size_t output_len = sizeof(struct smb311_posix_qinfo *) + - (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); - *plen = 0; - - return query_info(xid, tcon, persistent_fid, volatile_fid, - SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, - output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); - /* Note caller must free "data" (passed in above). It may be allocated in query_info call */ -} -#endif - -int -SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - void **data, u32 *plen, u32 extra_info) -{ - __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | - extra_info; - *plen = 0; - - return query_info(xid, tcon, persistent_fid, volatile_fid, - 0, SMB2_O_INFO_SECURITY, additional_info, - SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); -} - -int -SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) -{ - return query_info(xid, tcon, persistent_fid, volatile_fid, - FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_internal_info), - sizeof(struct smb2_file_internal_info), - (void **)&uniqueid, NULL); -} - -/* - * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory - * See MS-SMB2 2.2.35 and 2.2.36 - */ - -static int -SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, - struct cifs_tcon *tcon, struct TCP_Server_Info *server, - u64 persistent_fid, u64 volatile_fid, - u32 completion_filter, bool watch_tree) -{ - struct smb2_change_notify_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - /* See note 354 of MS-SMB2, 64K max */ - req->OutputBufferLength = - cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); - req->CompletionFilter = cpu_to_le32(completion_filter); - if (watch_tree) - req->Flags = cpu_to_le16(SMB2_WATCH_TREE); - else - req->Flags = 0; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - return 0; -} - -int -SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, bool watch_tree, - u32 completion_filter, u32 max_out_data_len, char **out_data, - u32 *plen /* returned data len */) -{ - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct smb_rqst rqst; - struct smb2_change_notify_rsp *smb_rsp; - struct kvec iov[1]; - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype = CIFS_NO_BUFFER; - int flags = 0; - int rc = 0; - - cifs_dbg(FYI, "change notify\n"); - if (!ses || !server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - if (plen) - *plen = 0; - - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = SMB2_notify_init(xid, &rqst, tcon, server, - persistent_fid, volatile_fid, - completion_filter, watch_tree); - if (rc) - goto cnotify_exit; - - trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, - (u8)watch_tree, completion_filter); - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); - trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, - (u8)watch_tree, completion_filter, rc); - } else { - trace_smb3_notify_done(xid, persistent_fid, tcon->tid, - ses->Suid, (u8)watch_tree, completion_filter); - /* validate that notify information is plausible */ - if ((rsp_iov.iov_base == NULL) || - (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp))) - goto cnotify_exit; - - smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; - - smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), - le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov, - sizeof(struct file_notify_information)); - - *out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset), - le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL); - if (*out_data == NULL) { - rc = -ENOMEM; - goto cnotify_exit; - } else if (plen) - *plen = le32_to_cpu(smb_rsp->OutputBufferLength); - } - - cnotify_exit: - if (rqst.rq_iov) - cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - - - -/* - * This is a no-op for now. We're not really interested in the reply, but - * rather in the fact that the server sent one and that server->lstrp - * gets updated. - * - * FIXME: maybe we should consider checking that the reply matches request? - */ -static void -smb2_echo_callback(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->callback_data; - struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - - if (mid->mid_state == MID_RESPONSE_RECEIVED - || mid->mid_state == MID_RESPONSE_MALFORMED) { - credits.value = le16_to_cpu(rsp->hdr.CreditRequest); - credits.instance = server->reconnect_instance; - } - - release_mid(mid); - add_credits(server, &credits, CIFS_ECHO_OP); -} - -void smb2_reconnect_server(struct work_struct *work) -{ - struct TCP_Server_Info *server = container_of(work, - struct TCP_Server_Info, reconnect.work); - struct TCP_Server_Info *pserver; - struct cifs_ses *ses, *ses2; - struct cifs_tcon *tcon, *tcon2; - struct list_head tmp_list, tmp_ses_list; - bool tcon_exist = false, ses_exist = false; - bool tcon_selected = false; - int rc; - bool resched = false; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ - mutex_lock(&pserver->reconnect_mutex); - - INIT_LIST_HEAD(&tmp_list); - INIT_LIST_HEAD(&tmp_ses_list); - cifs_dbg(FYI, "Reconnecting tcons and channels\n"); - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - - tcon_selected = false; - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->need_reconnect || tcon->need_reopen_files) { - tcon->tc_count++; - list_add_tail(&tcon->rlist, &tmp_list); - tcon_selected = tcon_exist = true; - } - } - /* - * IPC has the same lifetime as its session and uses its - * refcount. - */ - if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { - list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); - tcon_selected = tcon_exist = true; - ses->ses_count++; - } - /* - * handle the case where channel needs to reconnect - * binding session, but tcon is healthy (some other channel - * is active) - */ - spin_lock(&ses->chan_lock); - if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) { - list_add_tail(&ses->rlist, &tmp_ses_list); - ses_exist = true; - ses->ses_count++; - } - spin_unlock(&ses->chan_lock); - } - /* - * Get the reference to server struct to be sure that the last call of - * cifs_put_tcon() in the loop below won't release the server pointer. - */ - if (tcon_exist || ses_exist) - server->srv_count++; - - spin_unlock(&cifs_tcp_ses_lock); - - list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { - rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); - if (!rc) - cifs_reopen_persistent_handles(tcon); - else - resched = true; - list_del_init(&tcon->rlist); - if (tcon->ipc) - cifs_put_smb_ses(tcon->ses); - else - cifs_put_tcon(tcon); - } - - if (!ses_exist) - goto done; - - /* allocate a dummy tcon struct used for reconnect */ - tcon = tconInfoAlloc(); - if (!tcon) { - resched = true; - list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { - list_del_init(&ses->rlist); - cifs_put_smb_ses(ses); - } - goto done; - } - - tcon->status = TID_GOOD; - tcon->retry = false; - tcon->need_reconnect = false; - - /* now reconnect sessions for necessary channels */ - list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { - tcon->ses = ses; - rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); - if (rc) - resched = true; - list_del_init(&ses->rlist); - cifs_put_smb_ses(ses); - } - tconInfoFree(tcon); - -done: - cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); - if (resched) - queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); - mutex_unlock(&pserver->reconnect_mutex); - - /* now we can safely release srv struct */ - if (tcon_exist || ses_exist) - cifs_put_tcp_session(server, 1); -} - -int -SMB2_echo(struct TCP_Server_Info *server) -{ - struct smb2_echo_req *req; - int rc = 0; - struct kvec iov[1]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 1 }; - unsigned int total_len; - - cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id); - - spin_lock(&server->srv_lock); - if (server->ops->need_neg && - server->ops->need_neg(server)) { - spin_unlock(&server->srv_lock); - /* No need to send echo on newly established connections */ - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - return rc; - } - spin_unlock(&server->srv_lock); - - rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, - (void **)&req, &total_len); - if (rc) - return rc; - - req->hdr.CreditRequest = cpu_to_le16(1); - - iov[0].iov_len = total_len; - iov[0].iov_base = (char *)req; - - rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, - server, CIFS_ECHO_OP, NULL); - if (rc) - cifs_dbg(FYI, "Echo request failed: %d\n", rc); - - cifs_small_buf_release(req); - return rc; -} - -void -SMB2_flush_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -int -SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, - struct cifs_tcon *tcon, struct TCP_Server_Info *server, - u64 persistent_fid, u64 volatile_fid) -{ - struct smb2_flush_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - return 0; -} - -int -SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid) -{ - struct cifs_ses *ses = tcon->ses; - struct smb_rqst rqst; - struct kvec iov[1]; - struct kvec rsp_iov = {NULL, 0}; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int resp_buftype = CIFS_NO_BUFFER; - int flags = 0; - int rc = 0; - - cifs_dbg(FYI, "flush\n"); - if (!ses || !(ses->server)) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = SMB2_flush_init(xid, &rqst, tcon, server, - persistent_fid, volatile_fid); - if (rc) - goto flush_exit; - - trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); - trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, - rc); - } else - trace_smb3_flush_done(xid, persistent_fid, tcon->tid, - ses->Suid); - - flush_exit: - SMB2_flush_free(&rqst); - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -#ifdef CONFIG_CIFS_SMB_DIRECT -static inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms) -{ - struct TCP_Server_Info *server = io_parms->server; - struct cifs_tcon *tcon = io_parms->tcon; - - /* we can only offload if we're connected */ - if (!server || !tcon) - return false; - - /* we can only offload on an rdma connection */ - if (!server->rdma || !server->smbd_conn) - return false; - - /* we don't support signed offload yet */ - if (server->sign) - return false; - - /* we don't support encrypted offload yet */ - if (smb3_encryption_required(tcon)) - return false; - - /* offload also has its overhead, so only do it if desired */ - if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) - return false; - - return true; -} -#endif /* CONFIG_CIFS_SMB_DIRECT */ - -/* - * To form a chain of read requests, any read requests after the first should - * have the end_of_chain boolean set to true. - */ -static int -smb2_new_read_req(void **buf, unsigned int *total_len, - struct cifs_io_parms *io_parms, struct cifs_readdata *rdata, - unsigned int remaining_bytes, int request_type) -{ - int rc = -EACCES; - struct smb2_read_req *req = NULL; - struct smb2_hdr *shdr; - struct TCP_Server_Info *server = io_parms->server; - - rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, - (void **) &req, total_len); - if (rc) - return rc; - - if (server == NULL) - return -ECONNABORTED; - - shdr = &req->hdr; - shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); - - req->PersistentFileId = io_parms->persistent_fid; - req->VolatileFileId = io_parms->volatile_fid; - req->ReadChannelInfoOffset = 0; /* reserved */ - req->ReadChannelInfoLength = 0; /* reserved */ - req->Channel = 0; /* reserved */ - req->MinimumCount = 0; - req->Length = cpu_to_le32(io_parms->length); - req->Offset = cpu_to_le64(io_parms->offset); - - trace_smb3_read_enter(0 /* xid */, - io_parms->persistent_fid, - io_parms->tcon->tid, io_parms->tcon->ses->Suid, - io_parms->offset, io_parms->length); -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If we want to do a RDMA write, fill in and append - * smbd_buffer_descriptor_v1 to the end of read request - */ - if (smb3_use_rdma_offload(io_parms)) { - struct smbd_buffer_descriptor_v1 *v1; - bool need_invalidate = server->dialect == SMB30_PROT_ID; - - rdata->mr = smbd_register_mr( - server->smbd_conn, rdata->pages, - rdata->nr_pages, rdata->page_offset, - rdata->tailsz, true, need_invalidate); - if (!rdata->mr) - return -EAGAIN; - - req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; - if (need_invalidate) - req->Channel = SMB2_CHANNEL_RDMA_V1; - req->ReadChannelInfoOffset = - cpu_to_le16(offsetof(struct smb2_read_req, Buffer)); - req->ReadChannelInfoLength = - cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); - v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; - v1->offset = cpu_to_le64(rdata->mr->mr->iova); - v1->token = cpu_to_le32(rdata->mr->mr->rkey); - v1->length = cpu_to_le32(rdata->mr->mr->length); - - *total_len += sizeof(*v1) - 1; - } -#endif - if (request_type & CHAINED_REQUEST) { - if (!(request_type & END_OF_CHAIN)) { - /* next 8-byte aligned request */ - *total_len = ALIGN(*total_len, 8); - shdr->NextCommand = cpu_to_le32(*total_len); - } else /* END_OF_CHAIN */ - shdr->NextCommand = 0; - if (request_type & RELATED_REQUEST) { - shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; - /* - * Related requests use info from previous read request - * in chain. - */ - shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF); - shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF); - req->PersistentFileId = (u64)-1; - req->VolatileFileId = (u64)-1; - } - } - if (remaining_bytes > io_parms->length) - req->RemainingBytes = cpu_to_le32(remaining_bytes); - else - req->RemainingBytes = 0; - - *buf = req; - return rc; -} - -static void -smb2_readv_callback(struct mid_q_entry *mid) -{ - struct cifs_readdata *rdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - struct TCP_Server_Info *server = rdata->server; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rdata->iov[0].iov_base; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], - .rq_nvec = 1, }; - - if (rdata->got_bytes) { - rqst.rq_pages = rdata->pages; - rqst.rq_offset = rdata->page_offset; - rqst.rq_npages = rdata->nr_pages; - rqst.rq_pagesz = rdata->pagesz; - rqst.rq_tailsz = rdata->tailsz; - } - - WARN_ONCE(rdata->server != mid->server, - "rdata server %p != mid server %p", - rdata->server, mid->server); - - cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", - __func__, mid->mid, mid->mid_state, rdata->result, - rdata->bytes); - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - credits.value = le16_to_cpu(shdr->CreditRequest); - credits.instance = server->reconnect_instance; - /* result already set, check signature */ - if (server->sign && !mid->decrypted) { - int rc; - - rc = smb2_verify_signature(&rqst, server); - if (rc) - cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - rdata->result = -EAGAIN; - if (server->sign && rdata->got_bytes) - /* reset bytes number since we can not check a sign */ - rdata->got_bytes = 0; - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - case MID_RESPONSE_MALFORMED: - credits.value = le16_to_cpu(shdr->CreditRequest); - credits.instance = server->reconnect_instance; - fallthrough; - default: - rdata->result = -EIO; - } -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If this rdata has a memmory registered, the MR can be freed - * MR needs to be freed as soon as I/O finishes to prevent deadlock - * because they have limited number and are used for future I/Os - */ - if (rdata->mr) { - smbd_deregister_mr(rdata->mr); - rdata->mr = NULL; - } -#endif - if (rdata->result && rdata->result != -ENODATA) { - cifs_stats_fail_inc(tcon, SMB2_READ_HE); - trace_smb3_read_err(0 /* xid */, - rdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, rdata->offset, - rdata->bytes, rdata->result); - } else - trace_smb3_read_done(0 /* xid */, - rdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, - rdata->offset, rdata->got_bytes); - - queue_work(cifsiod_wq, &rdata->work); - release_mid(mid); - add_credits(server, &credits, 0); -} - -/* smb2_async_readv - send an async read, and set up mid to handle result */ -int -smb2_async_readv(struct cifs_readdata *rdata) -{ - int rc, flags = 0; - char *buf; - struct smb2_hdr *shdr; - struct cifs_io_parms io_parms; - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 1 }; - struct TCP_Server_Info *server; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - unsigned int total_len; - - cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", - __func__, rdata->offset, rdata->bytes); - - if (!rdata->server) - rdata->server = cifs_pick_channel(tcon->ses); - - io_parms.tcon = tlink_tcon(rdata->cfile->tlink); - io_parms.server = server = rdata->server; - io_parms.offset = rdata->offset; - io_parms.length = rdata->bytes; - io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; - io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; - io_parms.pid = rdata->pid; - - rc = smb2_new_read_req( - (void **) &buf, &total_len, &io_parms, rdata, 0, 0); - if (rc) - return rc; - - if (smb3_encryption_required(io_parms.tcon)) - flags |= CIFS_TRANSFORM_REQ; - - rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = total_len; - - shdr = (struct smb2_hdr *)buf; - - if (rdata->credits.value > 0) { - shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, - SMB2_MAX_BUFFER_SIZE)); - shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - if (rc) - goto async_readv_out; - - flags |= CIFS_HAS_CREDITS; - } - - kref_get(&rdata->refcount); - rc = cifs_call_async(server, &rqst, - cifs_readv_receive, smb2_readv_callback, - smb3_handle_read_data, rdata, flags, - &rdata->credits); - if (rc) { - kref_put(&rdata->refcount, cifs_readdata_release); - cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); - trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid, - io_parms.tcon->tid, - io_parms.tcon->ses->Suid, - io_parms.offset, io_parms.length, rc); - } - -async_readv_out: - cifs_small_buf_release(buf); - return rc; -} - -int -SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, int *buf_type) -{ - struct smb_rqst rqst; - int resp_buftype, rc; - struct smb2_read_req *req = NULL; - struct smb2_read_rsp *rsp = NULL; - struct kvec iov[1]; - struct kvec rsp_iov; - unsigned int total_len; - int flags = CIFS_LOG_ERROR; - struct cifs_ses *ses = io_parms->tcon->ses; - - if (!io_parms->server) - io_parms->server = cifs_pick_channel(io_parms->tcon->ses); - - *nbytes = 0; - rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0); - if (rc) - return rc; - - if (smb3_encryption_required(io_parms->tcon)) - flags |= CIFS_TRANSFORM_REQ; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, io_parms->server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; - - if (rc) { - if (rc != -ENODATA) { - cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); - cifs_dbg(VFS, "Send error in read = %d\n", rc); - trace_smb3_read_err(xid, - req->PersistentFileId, - io_parms->tcon->tid, ses->Suid, - io_parms->offset, io_parms->length, - rc); - } else - trace_smb3_read_done(xid, req->PersistentFileId, io_parms->tcon->tid, - ses->Suid, io_parms->offset, 0); - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - cifs_small_buf_release(req); - return rc == -ENODATA ? 0 : rc; - } else - trace_smb3_read_done(xid, - req->PersistentFileId, - io_parms->tcon->tid, ses->Suid, - io_parms->offset, io_parms->length); - - cifs_small_buf_release(req); - - *nbytes = le32_to_cpu(rsp->DataLength); - if ((*nbytes > CIFS_MAX_MSGSIZE) || - (*nbytes > io_parms->length)) { - cifs_dbg(FYI, "bad length %d for count %d\n", - *nbytes, io_parms->length); - rc = -EIO; - *nbytes = 0; - } - - if (*buf) { - memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes); - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - } else if (resp_buftype != CIFS_NO_BUFFER) { - *buf = rsp_iov.iov_base; - if (resp_buftype == CIFS_SMALL_BUFFER) - *buf_type = CIFS_SMALL_BUFFER; - else if (resp_buftype == CIFS_LARGE_BUFFER) - *buf_type = CIFS_LARGE_BUFFER; - } - return rc; -} - -/* - * Check the mid_state and signature on received buffer (if any), and queue the - * workqueue completion task. - */ -static void -smb2_writev_callback(struct mid_q_entry *mid) -{ - struct cifs_writedata *wdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - struct TCP_Server_Info *server = wdata->server; - unsigned int written; - struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - - WARN_ONCE(wdata->server != mid->server, - "wdata server %p != mid server %p", - wdata->server, mid->server); - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - credits.value = le16_to_cpu(rsp->hdr.CreditRequest); - credits.instance = server->reconnect_instance; - wdata->result = smb2_check_receive(mid, server, 0); - if (wdata->result != 0) - break; - - written = le32_to_cpu(rsp->DataLength); - /* - * Mask off high 16 bits when bytes written as returned - * by the server is greater than bytes requested by the - * client. OS/2 servers are known to set incorrect - * CountHigh values. - */ - if (written > wdata->bytes) - written &= 0xFFFF; - - if (written < wdata->bytes) - wdata->result = -ENOSPC; - else - wdata->bytes = written; - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - wdata->result = -EAGAIN; - break; - case MID_RESPONSE_MALFORMED: - credits.value = le16_to_cpu(rsp->hdr.CreditRequest); - credits.instance = server->reconnect_instance; - fallthrough; - default: - wdata->result = -EIO; - break; - } -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If this wdata has a memory registered, the MR can be freed - * The number of MRs available is limited, it's important to recover - * used MR as soon as I/O is finished. Hold MR longer in the later - * I/O process can possibly result in I/O deadlock due to lack of MR - * to send request on I/O retry - */ - if (wdata->mr) { - smbd_deregister_mr(wdata->mr); - wdata->mr = NULL; - } -#endif - if (wdata->result) { - cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); - trace_smb3_write_err(0 /* no xid */, - wdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, wdata->offset, - wdata->bytes, wdata->result); - if (wdata->result == -ENOSPC) - pr_warn_once("Out of space writing to %s\n", - tcon->tree_name); - } else - trace_smb3_write_done(0 /* no xid */, - wdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, - wdata->offset, wdata->bytes); - - queue_work(cifsiod_wq, &wdata->work); - release_mid(mid); - add_credits(server, &credits, 0); -} - -/* smb2_async_writev - send an async write, and set up mid to handle result */ -int -smb2_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)) -{ - int rc = -EACCES, flags = 0; - struct smb2_write_req *req = NULL; - struct smb2_hdr *shdr; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - struct TCP_Server_Info *server = wdata->server; - struct kvec iov[1]; - struct smb_rqst rqst = { }; - unsigned int total_len; - struct cifs_io_parms _io_parms; - struct cifs_io_parms *io_parms = NULL; - - if (!wdata->server) - server = wdata->server = cifs_pick_channel(tcon->ses); - - /* - * in future we may get cifs_io_parms passed in from the caller, - * but for now we construct it here... - */ - _io_parms = (struct cifs_io_parms) { - .tcon = tcon, - .server = server, - .offset = wdata->offset, - .length = wdata->bytes, - .persistent_fid = wdata->cfile->fid.persistent_fid, - .volatile_fid = wdata->cfile->fid.volatile_fid, - .pid = wdata->pid, - }; - io_parms = &_io_parms; - - rc = smb2_plain_req_init(SMB2_WRITE, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - shdr = (struct smb2_hdr *)req; - shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); - - req->PersistentFileId = io_parms->persistent_fid; - req->VolatileFileId = io_parms->volatile_fid; - req->WriteChannelInfoOffset = 0; - req->WriteChannelInfoLength = 0; - req->Channel = 0; - req->Offset = cpu_to_le64(io_parms->offset); - req->DataOffset = cpu_to_le16( - offsetof(struct smb2_write_req, Buffer)); - req->RemainingBytes = 0; - - trace_smb3_write_enter(0 /* xid */, - io_parms->persistent_fid, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, - io_parms->length); - -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If we want to do a server RDMA read, fill in and append - * smbd_buffer_descriptor_v1 to the end of write request - */ - if (smb3_use_rdma_offload(io_parms)) { - struct smbd_buffer_descriptor_v1 *v1; - bool need_invalidate = server->dialect == SMB30_PROT_ID; - - wdata->mr = smbd_register_mr( - server->smbd_conn, wdata->pages, - wdata->nr_pages, wdata->page_offset, - wdata->tailsz, false, need_invalidate); - if (!wdata->mr) { - rc = -EAGAIN; - goto async_writev_out; - } - req->Length = 0; - req->DataOffset = 0; - if (wdata->nr_pages > 1) - req->RemainingBytes = - cpu_to_le32( - (wdata->nr_pages - 1) * wdata->pagesz - - wdata->page_offset + wdata->tailsz - ); - else - req->RemainingBytes = cpu_to_le32(wdata->tailsz); - req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; - if (need_invalidate) - req->Channel = SMB2_CHANNEL_RDMA_V1; - req->WriteChannelInfoOffset = - cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); - req->WriteChannelInfoLength = - cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); - v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; - v1->offset = cpu_to_le64(wdata->mr->mr->iova); - v1->token = cpu_to_le32(wdata->mr->mr->rkey); - v1->length = cpu_to_le32(wdata->mr->mr->length); - } -#endif - iov[0].iov_len = total_len - 1; - iov[0].iov_base = (char *)req; - - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - rqst.rq_pages = wdata->pages; - rqst.rq_offset = wdata->page_offset; - rqst.rq_npages = wdata->nr_pages; - rqst.rq_pagesz = wdata->pagesz; - rqst.rq_tailsz = wdata->tailsz; -#ifdef CONFIG_CIFS_SMB_DIRECT - if (wdata->mr) { - iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); - rqst.rq_npages = 0; - } -#endif - cifs_dbg(FYI, "async write at %llu %u bytes\n", - io_parms->offset, io_parms->length); - -#ifdef CONFIG_CIFS_SMB_DIRECT - /* For RDMA read, I/O size is in RemainingBytes not in Length */ - if (!wdata->mr) - req->Length = cpu_to_le32(io_parms->length); -#else - req->Length = cpu_to_le32(io_parms->length); -#endif - - if (wdata->credits.value > 0) { - shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, - SMB2_MAX_BUFFER_SIZE)); - shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); - - rc = adjust_credits(server, &wdata->credits, io_parms->length); - if (rc) - goto async_writev_out; - - flags |= CIFS_HAS_CREDITS; - } - - kref_get(&wdata->refcount); - rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, - wdata, flags, &wdata->credits); - - if (rc) { - trace_smb3_write_err(0 /* no xid */, - io_parms->persistent_fid, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, - io_parms->length, - rc); - kref_put(&wdata->refcount, release); - cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); - } - -async_writev_out: - cifs_small_buf_release(req); - return rc; -} - -/* - * SMB2_write function gets iov pointer to kvec array with n_vec as a length. - * The length field from io_parms must be at least 1 and indicates a number of - * elements with data to write that begins with position 1 in iov array. All - * data length is specified by count. - */ -int -SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, int n_vec) -{ - struct smb_rqst rqst; - int rc = 0; - struct smb2_write_req *req = NULL; - struct smb2_write_rsp *rsp = NULL; - int resp_buftype; - struct kvec rsp_iov; - int flags = 0; - unsigned int total_len; - struct TCP_Server_Info *server; - - *nbytes = 0; - - if (n_vec < 1) - return rc; - - if (!io_parms->server) - io_parms->server = cifs_pick_channel(io_parms->tcon->ses); - server = io_parms->server; - if (server == NULL) - return -ECONNABORTED; - - rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(io_parms->tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); - - req->PersistentFileId = io_parms->persistent_fid; - req->VolatileFileId = io_parms->volatile_fid; - req->WriteChannelInfoOffset = 0; - req->WriteChannelInfoLength = 0; - req->Channel = 0; - req->Length = cpu_to_le32(io_parms->length); - req->Offset = cpu_to_le64(io_parms->offset); - req->DataOffset = cpu_to_le16( - offsetof(struct smb2_write_req, Buffer)); - req->RemainingBytes = 0; - - trace_smb3_write_enter(xid, io_parms->persistent_fid, - io_parms->tcon->tid, io_parms->tcon->ses->Suid, - io_parms->offset, io_parms->length); - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = n_vec + 1; - - rc = cifs_send_recv(xid, io_parms->tcon->ses, server, - &rqst, - &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; - - if (rc) { - trace_smb3_write_err(xid, - req->PersistentFileId, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, io_parms->length, rc); - cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); - cifs_dbg(VFS, "Send error in write = %d\n", rc); - } else { - *nbytes = le32_to_cpu(rsp->DataLength); - trace_smb3_write_done(xid, - req->PersistentFileId, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, *nbytes); - } - - cifs_small_buf_release(req); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int posix_info_sid_size(const void *beg, const void *end) -{ - size_t subauth; - int total; - - if (beg + 1 > end) - return -1; - - subauth = *(u8 *)(beg+1); - if (subauth < 1 || subauth > 15) - return -1; - - total = 1 + 1 + 6 + 4*subauth; - if (beg + total > end) - return -1; - - return total; -} - -int posix_info_parse(const void *beg, const void *end, - struct smb2_posix_info_parsed *out) - -{ - int total_len = 0; - int owner_len, group_len; - int name_len; - const void *owner_sid; - const void *group_sid; - const void *name; - - /* if no end bound given, assume payload to be correct */ - if (!end) { - const struct smb2_posix_info *p = beg; - - end = beg + le32_to_cpu(p->NextEntryOffset); - /* last element will have a 0 offset, pick a sensible bound */ - if (end == beg) - end += 0xFFFF; - } - - /* check base buf */ - if (beg + sizeof(struct smb2_posix_info) > end) - return -1; - total_len = sizeof(struct smb2_posix_info); - - /* check owner sid */ - owner_sid = beg + total_len; - owner_len = posix_info_sid_size(owner_sid, end); - if (owner_len < 0) - return -1; - total_len += owner_len; - - /* check group sid */ - group_sid = beg + total_len; - group_len = posix_info_sid_size(group_sid, end); - if (group_len < 0) - return -1; - total_len += group_len; - - /* check name len */ - if (beg + total_len + 4 > end) - return -1; - name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); - if (name_len < 1 || name_len > 0xFFFF) - return -1; - total_len += 4; - - /* check name */ - name = beg + total_len; - if (name + name_len > end) - return -1; - total_len += name_len; - - if (out) { - out->base = beg; - out->size = total_len; - out->name_len = name_len; - out->name = name; - memcpy(&out->owner, owner_sid, owner_len); - memcpy(&out->group, group_sid, group_len); - } - return total_len; -} - -static int posix_info_extra_size(const void *beg, const void *end) -{ - int len = posix_info_parse(beg, end, NULL); - - if (len < 0) - return -1; - return len - sizeof(struct smb2_posix_info); -} - -static unsigned int -num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, - size_t size) -{ - int len; - unsigned int entrycount = 0; - unsigned int next_offset = 0; - char *entryptr; - FILE_DIRECTORY_INFO *dir_info; - - if (bufstart == NULL) - return 0; - - entryptr = bufstart; - - while (1) { - if (entryptr + next_offset < entryptr || - entryptr + next_offset > end_of_buf || - entryptr + next_offset + size > end_of_buf) { - cifs_dbg(VFS, "malformed search entry would overflow\n"); - break; - } - - entryptr = entryptr + next_offset; - dir_info = (FILE_DIRECTORY_INFO *)entryptr; - - if (infotype == SMB_FIND_FILE_POSIX_INFO) - len = posix_info_extra_size(entryptr, end_of_buf); - else - len = le32_to_cpu(dir_info->FileNameLength); - - if (len < 0 || - entryptr + len < entryptr || - entryptr + len > end_of_buf || - entryptr + len + size > end_of_buf) { - cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", - end_of_buf); - break; - } - - *lastentry = entryptr; - entrycount++; - - next_offset = le32_to_cpu(dir_info->NextEntryOffset); - if (!next_offset) - break; - } - - return entrycount; -} - -/* - * Readdir/FindFirst - */ -int SMB2_query_directory_init(const unsigned int xid, - struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - int index, int info_level) -{ - struct smb2_query_directory_req *req; - unsigned char *bufptr; - __le16 asteriks = cpu_to_le16('*'); - unsigned int output_size = CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE; - unsigned int total_len; - struct kvec *iov = rqst->rq_iov; - int len, rc; - - rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - switch (info_level) { - case SMB_FIND_FILE_DIRECTORY_INFO: - req->FileInformationClass = FILE_DIRECTORY_INFORMATION; - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; - break; - case SMB_FIND_FILE_POSIX_INFO: - req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; - break; - default: - cifs_tcon_dbg(VFS, "info level %u isn't supported\n", - info_level); - return -EINVAL; - } - - req->FileIndex = cpu_to_le32(index); - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - - len = 0x2; - bufptr = req->Buffer; - memcpy(bufptr, &asteriks, len); - - req->FileNameOffset = - cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1); - req->FileNameLength = cpu_to_le16(len); - /* - * BB could be 30 bytes or so longer if we used SMB2 specific - * buffer lengths, but this is safe and close enough. - */ - output_size = min_t(unsigned int, output_size, server->maxBuf); - output_size = min_t(unsigned int, output_size, 2 << 15); - req->OutputBufferLength = cpu_to_le32(output_size); - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1; - - iov[1].iov_base = (char *)(req->Buffer); - iov[1].iov_len = len; - - trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, output_size); - - return 0; -} - -void SMB2_query_directory_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ - } -} - -int -smb2_parse_query_directory(struct cifs_tcon *tcon, - struct kvec *rsp_iov, - int resp_buftype, - struct cifs_search_info *srch_inf) -{ - struct smb2_query_directory_rsp *rsp; - size_t info_buf_size; - char *end_of_smb; - int rc; - - rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; - - switch (srch_inf->info_level) { - case SMB_FIND_FILE_DIRECTORY_INFO: - info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; - break; - case SMB_FIND_FILE_POSIX_INFO: - /* note that posix payload are variable size */ - info_buf_size = sizeof(struct smb2_posix_info); - break; - default: - cifs_tcon_dbg(VFS, "info level %u isn't supported\n", - srch_inf->info_level); - return -EINVAL; - } - - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), rsp_iov, - info_buf_size); - if (rc) { - cifs_tcon_dbg(VFS, "bad info payload"); - return rc; - } - - srch_inf->unicode = true; - - if (srch_inf->ntwrk_buf_start) { - if (srch_inf->smallBuf) - cifs_small_buf_release(srch_inf->ntwrk_buf_start); - else - cifs_buf_release(srch_inf->ntwrk_buf_start); - } - srch_inf->ntwrk_buf_start = (char *)rsp; - srch_inf->srch_entries_start = srch_inf->last_entry = - (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); - end_of_smb = rsp_iov->iov_len + (char *)rsp; - - srch_inf->entries_in_buffer = num_entries( - srch_inf->info_level, - srch_inf->srch_entries_start, - end_of_smb, - &srch_inf->last_entry, - info_buf_size); - - srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; - cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", - srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, - srch_inf->srch_entries_start, srch_inf->last_entry); - if (resp_buftype == CIFS_LARGE_BUFFER) - srch_inf->smallBuf = false; - else if (resp_buftype == CIFS_SMALL_BUFFER) - srch_inf->smallBuf = true; - else - cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); - - return 0; -} - -int -SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int index, - struct cifs_search_info *srch_inf) -{ - struct smb_rqst rqst; - struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; - struct smb2_query_directory_rsp *rsp = NULL; - int resp_buftype = CIFS_NO_BUFFER; - struct kvec rsp_iov; - int rc = 0; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = 0; - - if (!ses || !(ses->server)) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; - - rc = SMB2_query_directory_init(xid, tcon, server, - &rqst, persistent_fid, - volatile_fid, index, - srch_inf->info_level); - if (rc) - goto qdir_exit; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; - - if (rc) { - if (rc == -ENODATA && - rsp->hdr.Status == STATUS_NO_MORE_FILES) { - trace_smb3_query_dir_done(xid, persistent_fid, - tcon->tid, tcon->ses->Suid, index, 0); - srch_inf->endOfSearch = true; - rc = 0; - } else { - trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, 0, rc); - cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); - } - goto qdir_exit; - } - - rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, - srch_inf); - if (rc) { - trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, 0, rc); - goto qdir_exit; - } - resp_buftype = CIFS_NO_BUFFER; - - trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, srch_inf->entries_in_buffer); - -qdir_exit: - SMB2_query_directory_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int -SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 pid, - u8 info_class, u8 info_type, u32 additional_info, - void **data, unsigned int *size) -{ - struct smb2_set_info_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int i, total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); - req->InfoType = info_type; - req->FileInfoClass = info_class; - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - req->AdditionalInformation = cpu_to_le32(additional_info); - - req->BufferOffset = - cpu_to_le16(sizeof(struct smb2_set_info_req) - 1); - req->BufferLength = cpu_to_le32(*size); - - memcpy(req->Buffer, *data, *size); - total_len += *size; - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1; - - for (i = 1; i < rqst->rq_nvec; i++) { - le32_add_cpu(&req->BufferLength, size[i]); - iov[i].iov_base = (char *)data[i]; - iov[i].iov_len = size[i]; - } - - return 0; -} - -void -SMB2_set_info_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -static int -send_set_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, - u8 info_type, u32 additional_info, unsigned int num, - void **data, unsigned int *size) -{ - struct smb_rqst rqst; - struct smb2_set_info_rsp *rsp = NULL; - struct kvec *iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = 0; - - if (!ses || !server) - return -EIO; - - if (!num) - return -EINVAL; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); - if (!iov) - return -ENOMEM; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = num; - - rc = SMB2_set_info_init(tcon, server, - &rqst, persistent_fid, volatile_fid, pid, - info_class, info_type, additional_info, - data, size); - if (rc) { - kfree(iov); - return rc; - } - - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, - &rsp_iov); - SMB2_set_info_free(&rqst); - rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); - trace_smb3_set_info_err(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type, rc); - } - - free_rsp_buf(resp_buftype, rsp); - kfree(iov); - return rc; -} - -int -SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid, u32 pid, __le64 *eof) -{ - struct smb2_file_eof_info info; - void *data; - unsigned int size; - - info.EndOfFile = *eof; - - data = &info; - size = sizeof(struct smb2_file_eof_info); - - trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof)); - - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, - 0, 1, &data, &size); -} - -int -SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct cifs_ntsd *pnntsd, int pacllen, int aclflag) -{ - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, - 1, (void **)&pnntsd, &pacllen); -} - -int -SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_full_ea_info *buf, int len) -{ - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, - 0, 1, (void **)&buf, &len); -} - -int -SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, - const u64 persistent_fid, const u64 volatile_fid, - __u8 oplock_level) -{ - struct smb_rqst rqst; - int rc; - struct smb2_oplock_break *req = NULL; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = CIFS_OBREAK_OP; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - cifs_dbg(FYI, "SMB2_oplock_break\n"); - rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->VolatileFid = volatile_fid; - req->PersistentFid = persistent_fid; - req->OplockLevel = oplock_level; - req->hdr.CreditRequest = cpu_to_le16(1); - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); - cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); - } - - return rc; -} - -void -smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, - struct kstatfs *kst) -{ - kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * - le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); - kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); - kst->f_bfree = kst->f_bavail = - le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); - return; -} - -static void -copy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data, - struct kstatfs *kst) -{ - kst->f_bsize = le32_to_cpu(response_data->BlockSize); - kst->f_blocks = le64_to_cpu(response_data->TotalBlocks); - kst->f_bfree = le64_to_cpu(response_data->BlocksAvail); - if (response_data->UserBlocksAvail == cpu_to_le64(-1)) - kst->f_bavail = kst->f_bfree; - else - kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); - if (response_data->TotalFileNodes != cpu_to_le64(-1)) - kst->f_files = le64_to_cpu(response_data->TotalFileNodes); - if (response_data->FreeFileNodes != cpu_to_le64(-1)) - kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes); - - return; -} - -static int -build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - int level, int outbuf_len, u64 persistent_fid, - u64 volatile_fid) -{ - int rc; - struct smb2_query_info_req *req; - unsigned int total_len; - - cifs_dbg(FYI, "Query FSInfo level %d\n", level); - - if ((tcon->ses == NULL) || server == NULL) - return -EIO; - - rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->InfoType = SMB2_O_INFO_FILESYSTEM; - req->FileInfoClass = level; - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - /* 1 for pad */ - req->InputBufferOffset = - cpu_to_le16(sizeof(struct smb2_query_info_req) - 1); - req->OutputBufferLength = cpu_to_le32( - outbuf_len + sizeof(struct smb2_query_info_rsp) - 1); - - iov->iov_base = (char *)req; - iov->iov_len = total_len; - return 0; -} - -int -SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - FILE_SYSTEM_POSIX_INFO *info = NULL; - int flags = 0; - - rc = build_qfs_info_req(&iov, tcon, server, - FS_POSIX_INFORMATION, - sizeof(FILE_SYSTEM_POSIX_INFO), - persistent_fid, volatile_fid); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(iov.iov_base); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto posix_qfsinf_exit; - } - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - info = (FILE_SYSTEM_POSIX_INFO *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, - sizeof(FILE_SYSTEM_POSIX_INFO)); - if (!rc) - copy_posix_fs_info_to_kstatfs(info, fsdata); - -posix_qfsinf_exit: - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -int -SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct smb2_fs_full_size_info *info = NULL; - int flags = 0; - - rc = build_qfs_info_req(&iov, tcon, server, - FS_FULL_SIZE_INFORMATION, - sizeof(struct smb2_fs_full_size_info), - persistent_fid, volatile_fid); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(iov.iov_base); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto qfsinf_exit; - } - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - info = (struct smb2_fs_full_size_info *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, - sizeof(struct smb2_fs_full_size_info)); - if (!rc) - smb2_copy_fs_info_to_kstatfs(info, fsdata); - -qfsinf_exit: - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -int -SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int level) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype, max_len, min_len; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - unsigned int rsp_len, offset; - int flags = 0; - - if (level == FS_DEVICE_INFORMATION) { - max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); - min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); - } else if (level == FS_ATTRIBUTE_INFORMATION) { - max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); - min_len = MIN_FS_ATTR_INFO_SIZE; - } else if (level == FS_SECTOR_SIZE_INFORMATION) { - max_len = sizeof(struct smb3_fs_ss_info); - min_len = sizeof(struct smb3_fs_ss_info); - } else if (level == FS_VOLUME_INFORMATION) { - max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; - min_len = sizeof(struct smb3_fs_vol_info); - } else { - cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); - return -EINVAL; - } - - rc = build_qfs_info_req(&iov, tcon, server, - level, max_len, - persistent_fid, volatile_fid); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(iov.iov_base); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto qfsattr_exit; - } - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - rsp_len = le32_to_cpu(rsp->OutputBufferLength); - offset = le16_to_cpu(rsp->OutputBufferOffset); - rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len); - if (rc) - goto qfsattr_exit; - - if (level == FS_ATTRIBUTE_INFORMATION) - memcpy(&tcon->fsAttrInfo, offset - + (char *)rsp, min_t(unsigned int, - rsp_len, max_len)); - else if (level == FS_DEVICE_INFORMATION) - memcpy(&tcon->fsDevInfo, offset - + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); - else if (level == FS_SECTOR_SIZE_INFORMATION) { - struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) - (offset + (char *)rsp); - tcon->ss_flags = le32_to_cpu(ss_info->Flags); - tcon->perf_sector_size = - le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); - } else if (level == FS_VOLUME_INFORMATION) { - struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) - (offset + (char *)rsp); - tcon->vol_serial_number = vol_info->VolumeSerialNumber; - tcon->vol_create_time = vol_info->VolumeCreationTime; - } - -qfsattr_exit: - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -int -smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, - const __u32 num_lock, struct smb2_lock_element *buf) -{ - struct smb_rqst rqst; - int rc = 0; - struct smb2_lock_req *req = NULL; - struct kvec iov[2]; - struct kvec rsp_iov; - int resp_buf_type; - unsigned int count; - int flags = CIFS_NO_RSP_BUF; - unsigned int total_len; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - - cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); - - rc = smb2_plain_req_init(SMB2_LOCK, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); - req->LockCount = cpu_to_le16(num_lock); - - req->PersistentFileId = persist_fid; - req->VolatileFileId = volatile_fid; - - count = num_lock * sizeof(struct smb2_lock_element); - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len - sizeof(struct smb2_lock_element); - iov[1].iov_base = (char *)buf; - iov[1].iov_len = count; - - cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - - rc = cifs_send_recv(xid, tcon->ses, server, - &rqst, &resp_buf_type, flags, - &rsp_iov); - cifs_small_buf_release(req); - if (rc) { - cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); - cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); - trace_smb3_lock_err(xid, persist_fid, tcon->tid, - tcon->ses->Suid, rc); - } - - return rc; -} - -int -SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, - const __u64 length, const __u64 offset, const __u32 lock_flags, - const bool wait) -{ - struct smb2_lock_element lock; - - lock.Offset = cpu_to_le64(offset); - lock.Length = cpu_to_le64(length); - lock.Flags = cpu_to_le32(lock_flags); - if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) - lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); - - return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); -} - -int -SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, - __u8 *lease_key, const __le32 lease_state) -{ - struct smb_rqst rqst; - int rc; - struct smb2_lease_ack *req = NULL; - struct cifs_ses *ses = tcon->ses; - int flags = CIFS_OBREAK_OP; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - __u64 *please_key_high; - __u64 *please_key_low; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - - cifs_dbg(FYI, "SMB2_lease_break\n"); - rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->hdr.CreditRequest = cpu_to_le16(1); - req->StructureSize = cpu_to_le16(36); - total_len += 12; - - memcpy(req->LeaseKey, lease_key, 16); - req->LeaseState = lease_state; - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - - please_key_low = (__u64 *)lease_key; - please_key_high = (__u64 *)(lease_key+8); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); - trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, - ses->Suid, *please_key_low, *please_key_high, rc); - cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); - } else - trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, - ses->Suid, *please_key_low, *please_key_high); - - return rc; -} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h deleted file mode 100644 index 1237bb86e93a..000000000000 --- a/fs/cifs/smb2pdu.h +++ /dev/null @@ -1,434 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2009, 2013 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ - -#ifndef _SMB2PDU_H -#define _SMB2PDU_H - -#include -#include "cifsacl.h" - -/* 52 transform hdr + 64 hdr + 88 create rsp */ -#define SMB2_TRANSFORM_HEADER_SIZE 52 -#define MAX_SMB2_HDR_SIZE 204 - -/* The total header size for SMB2 read and write */ -#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_hdr)) - -/* See MS-SMB2 2.2.43 */ -struct smb2_rdma_transform { - __le16 RdmaDescriptorOffset; - __le16 RdmaDescriptorLength; - __le32 Channel; /* for values see channel description in smb2 read above */ - __le16 TransformCount; - __le16 Reserved1; - __le32 Reserved2; -} __packed; - -/* TransformType */ -#define SMB2_RDMA_TRANSFORM_TYPE_ENCRYPTION 0x0001 -#define SMB2_RDMA_TRANSFORM_TYPE_SIGNING 0x0002 - -struct smb2_rdma_crypto_transform { - __le16 TransformType; - __le16 SignatureLength; - __le16 NonceLength; - __u16 Reserved; - __u8 Signature[]; /* variable length */ - /* u8 Nonce[] */ - /* followed by padding */ -} __packed; - -/* - * Definitions for SMB2 Protocol Data Units (network frames) - * - * See MS-SMB2.PDF specification for protocol details. - * The Naming convention is the lower case version of the SMB2 - * command code name for the struct. Note that structures must be packed. - * - */ - -#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL - -#define SMB2_SYMLINK_STRUCT_SIZE \ - (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) - -#define SYMLINK_ERROR_TAG 0x4c4d5953 - -struct smb2_symlink_err_rsp { - __le32 SymLinkLength; - __le32 SymLinkErrorTag; - __le32 ReparseTag; - __le16 ReparseDataLength; - __le16 UnparsedPathLength; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __le32 Flags; - __u8 PathBuffer[]; -} __packed; - -/* SMB 3.1.1 and later dialects. See MS-SMB2 section 2.2.2.1 */ -struct smb2_error_context_rsp { - __le32 ErrorDataLength; - __le32 ErrorId; - __u8 ErrorContextData; /* ErrorDataLength long array */ -} __packed; - -/* ErrorId values */ -#define SMB2_ERROR_ID_DEFAULT 0x00000000 -#define SMB2_ERROR_ID_SHARE_REDIRECT cpu_to_le32(0x72645253) /* "rdRS" */ - -/* Defines for Type field below (see MS-SMB2 2.2.2.2.2.1) */ -#define MOVE_DST_IPADDR_V4 cpu_to_le32(0x00000001) -#define MOVE_DST_IPADDR_V6 cpu_to_le32(0x00000002) - -struct move_dst_ipaddr { - __le32 Type; - __u32 Reserved; - __u8 address[16]; /* IPv4 followed by 12 bytes rsvd or IPv6 address */ -} __packed; - -struct share_redirect_error_context_rsp { - __le32 StructureSize; - __le32 NotificationType; - __le32 ResourceNameOffset; - __le32 ResourceNameLength; - __le16 Reserved; - __le16 TargetType; - __le32 IPAddrCount; - struct move_dst_ipaddr IpAddrMoveList[]; - /* __u8 ResourceName[] */ /* Name of share as counted Unicode string */ -} __packed; - -/* - * Maximum number of iovs we need for an open/create request. - * [0] : struct smb2_create_req - * [1] : path - * [2] : lease context - * [3] : durable context - * [4] : posix context - * [5] : time warp context - * [6] : query id context - * [7] : compound padding - */ -#define SMB2_CREATE_IOV_SIZE 8 - -/* - * Maximum size of a SMB2_CREATE response is 64 (smb2 header) + - * 88 (fixed part of create response) + 520 (path) + 208 (contexts) + - * 2 bytes of padding. - */ -#define MAX_SMB2_CREATE_RESPONSE_SIZE 880 - -#define SMB2_LEASE_READ_CACHING_HE 0x01 -#define SMB2_LEASE_HANDLE_CACHING_HE 0x02 -#define SMB2_LEASE_WRITE_CACHING_HE 0x04 - -struct create_durable { - struct create_context ccontext; - __u8 Name[8]; - union { - __u8 Reserved[16]; - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - } Data; -} __packed; - -/* See MS-SMB2 2.2.13.2.11 */ -/* Flags */ -#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002 -struct durable_context_v2 { - __le32 Timeout; - __le32 Flags; - __u64 Reserved; - __u8 CreateGuid[16]; -} __packed; - -struct create_durable_v2 { - struct create_context ccontext; - __u8 Name[8]; - struct durable_context_v2 dcontext; -} __packed; - -/* See MS-SMB2 2.2.13.2.12 */ -struct durable_reconnect_context_v2 { - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - __u8 CreateGuid[16]; - __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ -} __packed; - -/* See MS-SMB2 2.2.14.2.9 */ -struct create_on_disk_id { - struct create_context ccontext; - __u8 Name[8]; - __le64 DiskFileId; - __le64 VolumeId; - __u32 Reserved[4]; -} __packed; - -/* See MS-SMB2 2.2.14.2.12 */ -struct durable_reconnect_context_v2_rsp { - __le32 Timeout; - __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ -} __packed; - -struct create_durable_handle_reconnect_v2 { - struct create_context ccontext; - __u8 Name[8]; - struct durable_reconnect_context_v2 dcontext; - __u8 Pad[4]; -} __packed; - -/* See MS-SMB2 2.2.13.2.5 */ -struct crt_twarp_ctxt { - struct create_context ccontext; - __u8 Name[8]; - __le64 Timestamp; - -} __packed; - -/* See MS-SMB2 2.2.13.2.9 */ -struct crt_query_id_ctxt { - struct create_context ccontext; - __u8 Name[8]; -} __packed; - -struct crt_sd_ctxt { - struct create_context ccontext; - __u8 Name[8]; - struct smb3_sd sd; -} __packed; - - -#define COPY_CHUNK_RES_KEY_SIZE 24 -struct resume_key_req { - char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; - __le32 ContextLength; /* MBZ */ - char Context[]; /* ignored, Windows sets to 4 bytes of zero */ -} __packed; - -/* this goes in the ioctl buffer when doing a copychunk request */ -struct copychunk_ioctl { - char SourceKey[COPY_CHUNK_RES_KEY_SIZE]; - __le32 ChunkCount; /* we are only sending 1 */ - __le32 Reserved; - /* array will only be one chunk long for us */ - __le64 SourceOffset; - __le64 TargetOffset; - __le32 Length; /* how many bytes to copy */ - __u32 Reserved2; -} __packed; - -struct copychunk_ioctl_rsp { - __le32 ChunksWritten; - __le32 ChunkBytesWritten; - __le32 TotalBytesWritten; -} __packed; - -/* See MS-FSCC 2.3.29 and 2.3.30 */ -struct get_retrieval_pointer_count_req { - __le64 StartingVcn; /* virtual cluster number (signed) */ -} __packed; - -struct get_retrieval_pointer_count_rsp { - __le32 ExtentCount; -} __packed; - -/* - * See MS-FSCC 2.3.33 and 2.3.34 - * request is the same as get_retrieval_point_count_req struct above - */ -struct smb3_extents { - __le64 NextVcn; - __le64 Lcn; /* logical cluster number */ -} __packed; - -struct get_retrieval_pointers_refcount_rsp { - __le32 ExtentCount; - __u32 Reserved; - __le64 StartingVcn; - struct smb3_extents extents[]; -} __packed; - -/* See MS-DFSC 2.2.2 */ -struct fsctl_get_dfs_referral_req { - __le16 MaxReferralLevel; - __u8 RequestFileName[]; -} __packed; - -/* DFS response is struct get_dfs_refer_rsp */ - -/* See MS-SMB2 2.2.31.3 */ -struct network_resiliency_req { - __le32 Timeout; - __le32 Reserved; -} __packed; -/* There is no buffer for the response ie no struct network_resiliency_rsp */ - -#define RSS_CAPABLE cpu_to_le32(0x00000001) -#define RDMA_CAPABLE cpu_to_le32(0x00000002) - -#define INTERNETWORK cpu_to_le16(0x0002) -#define INTERNETWORKV6 cpu_to_le16(0x0017) - -struct network_interface_info_ioctl_rsp { - __le32 Next; /* next interface. zero if this is last one */ - __le32 IfIndex; - __le32 Capability; /* RSS or RDMA Capable */ - __le32 Reserved; - __le64 LinkSpeed; - __le16 Family; - __u8 Buffer[126]; -} __packed; - -struct iface_info_ipv4 { - __be16 Port; - __be32 IPv4Address; - __be64 Reserved; -} __packed; - -struct iface_info_ipv6 { - __be16 Port; - __be32 FlowInfo; - __u8 IPv6Address[16]; - __be32 ScopeId; -} __packed; - -#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ - -struct compress_ioctl { - __le16 CompressionState; /* See cifspdu.h for possible flag values */ -} __packed; - -/* - * Maximum number of iovs we need for an ioctl request. - * [0] : struct smb2_ioctl_req - * [1] : in_data - */ -#define SMB2_IOCTL_IOV_SIZE 2 - -/* - * PDU query infolevel structure definitions - * BB consider moving to a different header - */ - -struct smb2_file_full_ea_info { /* encoding of response for level 15 */ - __le32 next_entry_offset; - __u8 flags; - __u8 ea_name_length; - __le16 ea_value_length; - char ea_data[]; /* \0 terminated name plus value */ -} __packed; /* level 15 Set */ - -struct smb2_file_reparse_point_info { - __le64 IndexNumber; - __le32 Tag; -} __packed; - -struct smb2_file_network_open_info { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndOfFile; - __le32 Attributes; - __le32 Reserved; -} __packed; /* level 34 Query also similar returned in close rsp and open rsp */ - -/* See MS-FSCC 2.4.21 */ -struct smb2_file_id_information { - __le64 VolumeSerialNumber; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ -} __packed; /* level 59 */ - -/* See MS-FSCC 2.4.18 */ -struct smb2_file_id_extd_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 FileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* EA size */ - __le32 ReparsePointTag; /* valid if FILE_ATTR_REPARSE_POINT set in FileAttributes */ - __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit */ - char FileName[1]; -} __packed; /* level 60 */ - -extern char smb2_padding[7]; - -/* equivalent of the contents of SMB3.1.1 POSIX open context response */ -struct create_posix_rsp { - u32 nlink; - u32 reparse_tag; - u32 mode; - struct cifs_sid owner; /* var-sized on the wire */ - struct cifs_sid group; /* var-sized on the wire */ -} __packed; - -#define SMB2_QUERY_DIRECTORY_IOV_SIZE 2 - -/* - * SMB2-only POSIX info level for query dir - * - * See posix_info_sid_size(), posix_info_extra_size() and - * posix_info_parse() to help with the handling of this struct. - */ -struct smb2_posix_info { - __le32 NextEntryOffset; - __u32 Ignored; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 DosAttributes; - __le64 Inode; - __le32 DeviceId; - __le32 Zero; - /* beginning of POSIX Create Context Response */ - __le32 HardLinks; - __le32 ReparseTag; - __le32 Mode; - /* - * var sized owner SID - * var sized group SID - * le32 filenamelength - * u8 filename[] - */ -} __packed; - -/* - * Parsed version of the above struct. Allows direct access to the - * variable length fields - */ -struct smb2_posix_info_parsed { - const struct smb2_posix_info *base; - size_t size; - struct cifs_sid owner; - struct cifs_sid group; - int name_len; - const u8 *name; -}; - -#endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h deleted file mode 100644 index be21b5d26f67..000000000000 --- a/fs/cifs/smb2proto.h +++ /dev/null @@ -1,284 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ -#ifndef _SMB2PROTO_H -#define _SMB2PROTO_H -#include -#include - -struct statfs; -struct smb_rqst; - -/* - ***************************************************************** - * All Prototypes - ***************************************************************** - */ -extern int map_smb2_to_linux_error(char *buf, bool log_err); -extern int smb2_check_message(char *buf, unsigned int length, - struct TCP_Server_Info *server); -extern unsigned int smb2_calc_size(void *buf); -extern char *smb2_get_data_area_len(int *off, int *len, - struct smb2_hdr *shdr); -extern __le16 *cifs_convert_path_to_utf16(const char *from, - struct cifs_sb_info *cifs_sb); - -extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *); -extern int smb2_check_receive(struct mid_q_entry *mid, - struct TCP_Server_Info *server, bool log_error); -extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, - struct TCP_Server_Info *, - struct smb_rqst *rqst); -extern struct mid_q_entry *smb2_setup_async_request( - struct TCP_Server_Info *server, struct smb_rqst *rqst); -extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, - __u64 ses_id); -extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, - __u64 ses_id, __u32 tid); -extern int smb2_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - bool allocate_crypto); -extern int smb3_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - bool allocate_crypto); -extern void smb2_echo_request(struct work_struct *work); -extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); -extern bool smb2_is_valid_oplock_break(char *buffer, - struct TCP_Server_Info *srv); -extern int smb3_handle_read_data(struct TCP_Server_Info *server, - struct mid_q_entry *mid); -extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *path, - __u32 *reparse_tag); -int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); -extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, - const char *full_path, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_alloc); -extern int smb2_set_file_info(struct inode *inode, const char *full_path, - FILE_BASIC_INFO *buf, const unsigned int xid); -extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *full_path, - struct cifs_sb_info *cifs_sb); -extern int smb2_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, - struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, const unsigned int xid); -extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_written); -extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const unsigned char *path, char *pbuf, - unsigned int *pbytes_read); -int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path); -int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, - void *buf); -extern int smb2_unlock_range(struct cifsFileInfo *cfile, - struct file_lock *flock, const unsigned int xid); -extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); -extern void smb2_reconnect_server(struct work_struct *work); -extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); -extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, - struct smb_rqst *rqst); -extern void smb2_set_next_command(struct cifs_tcon *tcon, - struct smb_rqst *rqst); -extern void smb2_set_related(struct smb_rqst *rqst); - -/* - * SMB2 Worker functions - most of protocol specific implementation details - * are contained within these calls. - */ -extern int SMB2_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); -extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *); -extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); -extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, - __le16 *path, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix, - struct kvec *err_iov, int *resp_buftype); -extern int SMB2_open_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - __u8 *oplock, struct cifs_open_parms *oparms, - __le16 *path); -extern void SMB2_open_free(struct smb_rqst *rqst); -extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, u32 maxoutlen, - char **out_data, u32 *plen /* returned data len */); -extern int SMB2_ioctl_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, - __u32 max_response_size); -extern void SMB2_ioctl_free(struct smb_rqst *rqst); -extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, bool watch_tree, - u32 completion_filter, u32 max_out_data_len, - char **out_data, u32 *plen /* returned data len */); - -extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_network_open_info *pbuf); -extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id); -extern int SMB2_close_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - bool query_attrs); -extern void SMB2_close_free(struct smb_rqst *rqst); -extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id); -extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, - struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - u64 persistent_file_id, u64 volatile_file_id); -extern void SMB2_flush_free(struct smb_rqst *rqst); -extern int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen); -extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct smb2_file_all_info *data); -extern int SMB2_query_info_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - u8 info_class, u8 info_type, - u32 additional_info, size_t output_len, - size_t input_len, void *input); -extern void SMB2_query_info_free(struct smb_rqst *rqst); -extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - void **data, unsigned int *plen, u32 info); -extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - __le64 *uniqueid); -extern int smb2_async_readv(struct cifs_readdata *rdata); -extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, int *buf_type); -extern int smb2_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)); -extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, int n_vec); -extern int SMB2_echo(struct TCP_Server_Info *server); -extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int index, - struct cifs_search_info *srch_inf); -extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - int index, int info_level); -extern void SMB2_query_directory_free(struct smb_rqst *rqst); -extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 pid, - __le64 *eof); -extern int SMB2_set_info_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 pid, - u8 info_class, u8 info_type, u32 additional_info, - void **data, unsigned int *size); -extern void SMB2_set_info_free(struct smb_rqst *rqst); -extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct cifs_ntsd *pnntsd, int pacllen, int aclflag); -extern int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_full_ea_info *buf, int len); -extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid); -extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, - const u64 persistent_fid, const u64 volatile_fid, - const __u8 oplock_level); -extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon, - __u64 persistent_fid, - __u64 volatile_fid); -extern int smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server); -void smb2_cancelled_close_fid(struct work_struct *work); -extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct kstatfs *FSData); -extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct kstatfs *FSData); -extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, int lvl); -extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, - const __u32 pid, const __u64 length, const __u64 offset, - const __u32 lockFlags, const bool wait); -extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, - const __u32 pid, const __u32 num_lock, - struct smb2_lock_element *buf); -extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, - __u8 *lease_key, const __le32 lease_state); -extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); - -extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, - enum securityEnum); -extern void smb2_parse_contexts(struct TCP_Server_Info *server, - struct smb2_create_rsp *rsp, - unsigned int *epoch, char *lease_key, - __u8 *oplock, struct smb2_file_all_info *buf, - struct create_posix_rsp *posix); -extern int smb3_encryption_required(const struct cifs_tcon *tcon); -extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int min_buf_size); -extern int smb2_validate_and_copy_iov(unsigned int offset, - unsigned int buffer_length, - struct kvec *iov, - unsigned int minbufsize, char *data); -extern void smb2_copy_fs_info_to_kstatfs( - struct smb2_fs_full_size_info *pfs_inf, - struct kstatfs *kst); -extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); -extern int smb311_update_preauth_hash(struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct kvec *iov, int nvec); -extern int smb2_query_info_compound(const unsigned int xid, - struct cifs_tcon *tcon, - const char *path, u32 desired_access, - u32 class, u32 type, u32 output_len, - struct kvec *rsp, int *buftype, - struct cifs_sb_info *cifs_sb); -/* query path info from the server using SMB311 POSIX extensions*/ -int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); -int posix_info_parse(const void *beg, const void *end, - struct smb2_posix_info_parsed *out); -int posix_info_sid_size(const void *beg, const void *end); -#endif /* _SMB2PROTO_H */ diff --git a/fs/cifs/smb2status.h b/fs/cifs/smb2status.h deleted file mode 100644 index a9e958166fc5..000000000000 --- a/fs/cifs/smb2status.h +++ /dev/null @@ -1,1769 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * SMB2 Status code (network error) definitions - * Definitions are from MS-ERREF - * - * Copyright (c) International Business Machines Corp., 2009,2011 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -/* - * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F - * SEV C N <-------Facility--------> <------Error Status Code------> - * - * C is set if "customer defined" error, N bit is reserved and MBZ - */ - -#define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000) -#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) -#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) -#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) - -struct ntstatus { - /* Facility is the high 12 bits of the following field */ - __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ - __le32 Code; -}; - -#define STATUS_SUCCESS cpu_to_le32(0x00000000) -#define STATUS_WAIT_0 cpu_to_le32(0x00000000) -#define STATUS_WAIT_1 cpu_to_le32(0x00000001) -#define STATUS_WAIT_2 cpu_to_le32(0x00000002) -#define STATUS_WAIT_3 cpu_to_le32(0x00000003) -#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) -#define STATUS_ABANDONED cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) -#define STATUS_USER_APC cpu_to_le32(0x000000C0) -#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) -#define STATUS_ALERTED cpu_to_le32(0x00000101) -#define STATUS_TIMEOUT cpu_to_le32(0x00000102) -#define STATUS_PENDING cpu_to_le32(0x00000103) -#define STATUS_REPARSE cpu_to_le32(0x00000104) -#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) -#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) -#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) -#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) -#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) -#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) -#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) -#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) -#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) -#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) -#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) -#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) -#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) -#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) -#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) -#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) -#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) -#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) -#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) -#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) -#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) -#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) -#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) -#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) -#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) -#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) -#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) -#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) -#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) -#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) -#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) -#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) -#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) -#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) -#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) -#define DBG_CONTINUE cpu_to_le32(0x00010002) -#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) -#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) -#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) -#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) -#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) -#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) -#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) -#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) -#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) -#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) -#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) -#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) -#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) -#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) -#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) -#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) -#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) -#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) -#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) -#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) -#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) -#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) -#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) -#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) -#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) -#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) -#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) -#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) -#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) -#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) -#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) -#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) -#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) -#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) -#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) -#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) -#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) -#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) -#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) -#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) -#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) -#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) -#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) -#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) -#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) -#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) -#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) -#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) -#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) -#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) -#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) -#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) -#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) -#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) -#define DBG_REPLY_LATER cpu_to_le32(0x40010001) -#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) -#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) -#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) -#define DBG_CONTROL_C cpu_to_le32(0x40010005) -#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) -#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) -#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) -#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) -#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) -#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) -#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) -#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) -#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) -#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) -#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) -#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) -#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) -#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) -#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) -#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) -#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) -#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) -#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED cpu_to_le32(0x401E0351) -#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) -#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) -#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) -#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) -#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) -#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) -#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) -#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) -#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) -#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) -#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) -#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) -#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) -#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) -#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) -#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) -#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) -#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) -#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) -#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) -#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) -#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) -#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) -#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) -#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) -#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) -#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) -#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) -#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) -#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) -#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) -#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) -#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) -#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) -#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) -#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) -#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) -#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) -#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) -#define STATUS_LONGJUMP cpu_to_le32(0x80000026) -#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) -#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) -#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) -#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) -#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) -#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) -#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) -#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) -#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) -#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) -#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) -#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) -#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) -#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) -#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) -#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) -#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) -#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) -#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) -#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) -#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED cpu_to_le32(0x801B00EB) -#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) -#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) -#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) -#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) -#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) -#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) -#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) -#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) -#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) -#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) -#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) -#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) -#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) -#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) -#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) -#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) -#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) -#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) -#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) -#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) -#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) -#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) -#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) -#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) -#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) -#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) -#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) -#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) -#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) -#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) -#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) -#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) -#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) -#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) -#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) -#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) -#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) -#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) -#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) -#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) -#define STATUS_UNWIND cpu_to_le32(0xC0000027) -#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) -#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) -#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) -#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) -#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) -#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) -#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) -#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) -#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) -#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) -#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) -#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) -#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) -#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) -#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) -#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) -#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) -#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) -#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) -#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) -#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) -#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) -#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) -#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) -#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) -#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) -#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) -#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) -#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) -#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) -#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) -#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) -#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) -#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) -#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) -#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) -#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) -#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) -#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) -#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) -#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) -#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) -#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) -#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) -#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) -#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) -#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) -#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) -#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) -#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) -#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) -#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) -#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) -#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) -#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) -#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) -#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) -#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) -#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) -#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) -#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) -#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) -#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) -#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) -#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) -#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) -#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) -#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) -#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) -#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) -#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) -#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) -#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) -#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) -#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) -#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) -#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) -#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) -#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) -#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) -#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) -#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) -#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) -#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) -#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) -#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) -#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) -#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) -#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) -#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) -#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) -#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) -#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) -#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) -#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) -#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) -#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) -#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) -#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) -#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) -#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) -#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) -#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) -#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) -#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) -#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) -#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) -#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) -#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) -#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) -#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) -#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) -#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) -#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) -#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) -#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) -#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) -#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) -#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) -#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) -#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) -#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) -#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) -#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) -#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) -#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) -#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) -#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) -#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) -#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) -#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) -#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) -#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) -#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) -#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) -#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) -#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) -#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) -#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) -#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) -#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) -#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) -#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) -#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) -#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) -#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) -#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) -#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) -#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) -#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) -#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) -#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) -#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) -#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) -#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) -#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) -#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) -#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) -#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) -#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) -#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) -#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) -#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) -#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) -#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) -#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) -#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) -#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) -#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) -#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) -#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) -#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) -#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) -#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) -#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) -#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) -#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) -#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) -#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) -#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) -#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) -#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) -#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) -#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) -#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) -#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) -#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) -#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) -#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) -#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) -#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) -#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) -#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) -#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) -#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) -#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) -#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) -#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) -#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) -#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) -#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) -#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) -#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) -#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) -#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) -#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) -#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) -#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) -#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) -#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) -#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) -#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) -#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) -#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) -#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) -#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) -#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) -#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) -#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) -#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) -#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) -#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) -#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) -#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) -#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) -#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) -#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) -#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) -#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) -#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) -#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) -#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) -#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) -#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) -#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) -#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) -#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) -#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) -#define STATUS_NO_LDT cpu_to_le32(0xC0000117) -#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) -#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) -#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) -#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) -#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) -#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) -#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) -#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) -#define STATUS_CANCELLED cpu_to_le32(0xC0000120) -#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) -#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) -#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) -#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) -#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) -#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) -#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) -#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) -#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) -#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) -#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) -#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) -#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) -#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) -#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) -#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) -#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) -#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) -#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) -#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) -#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) -#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) -#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) -#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) -#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) -#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) -#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) -#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) -#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) -#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) -#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) -#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) -#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) -#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) -#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) -#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) -#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) -#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) -#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) -#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) -#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) -#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) -#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) -#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) -#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) -#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) -#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) -#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) -#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) -#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) -#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) -#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) -#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) -#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) -#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) -#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) -#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) -#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) -#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) -#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) -#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) -#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) -#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) -#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) -#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) -#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) -#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) -#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) -#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) -#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) -#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) -#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) -#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) -#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) -#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) -#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) -#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) -#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) -#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) -#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) -#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) -#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) -#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) -#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) -#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) -#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) -#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) -#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) -#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) -#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) -#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) -#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) -#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) -#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) -#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) -#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) -#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) -#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) -#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) -#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) -#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) -#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) -#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) -#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) -#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) -#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) -#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) -#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) -#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) -#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) -#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) -#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) -#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) -#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) -#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) -#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) -#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) -#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) -#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) -#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) -#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) -#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) -#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) -#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) -#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) -#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) -#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) -#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) -#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) -#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) -#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) -#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) -#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) -#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) -#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) -#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) -#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) -#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) -#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) -#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) -#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) -#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) -#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) -#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) -#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) -#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) -#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) -#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) -#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) -#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) -#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) -#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) -#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) -#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) -#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) -#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) -#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) -#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) -#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) -#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) -#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) -#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) -#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) -#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) -#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) -#define STATUS_RETRY cpu_to_le32(0xC000022D) -#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) -#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) -#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) -#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) -#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) -#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) -#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) -#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) -#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) -#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) -#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) -#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) -#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) -#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) -#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) -#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) -#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) -#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) -#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) -#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) -#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) -#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) -#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) -#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) -#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) -#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) -#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) -#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) -#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) -#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) -#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) -#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) -#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) -#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) -#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) -#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) -#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) -#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) -#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) -#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) -#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) -#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) -#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) -#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) -#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) -#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) -#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) -#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) -#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) -#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) -#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) -#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) -#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) -#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) -#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) -#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) -#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) -#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) -#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) -#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) -#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) -#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) -#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) -#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) -#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) -#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) -#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) -#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) -#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) -#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) -#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) -#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) -#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) -#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) -#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) -#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) -#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) -#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) -#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) -#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) -#define STATUS_NO_EFS cpu_to_le32(0xC000028E) -#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) -#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) -#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) -#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) -#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) -#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) -#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) -#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) -#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) -#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) -#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) -#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) -#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) -#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) -#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) -#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) -#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) -#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) -#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) -#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) -#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) -#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) -#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) -#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) -#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) -#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) -#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) -#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) -#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) -#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) -#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) -#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) -#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) -#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) -#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) -#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) -#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) -#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) -#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) -#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) -#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) -#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) -#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) -#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) -#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) -#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) -#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) -#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) -#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) -#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) -#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) -#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) -#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) -#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) -#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) -#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) -#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) -#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) -#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) -#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) -#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) -#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) -#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) -#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) -#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) -#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) -#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) -#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) -#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER cpu_to_le32(0xC00002DB) -#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) -#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) -#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) -#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) -#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) -#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) -#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) -#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) -#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) -#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) -#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) -#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) -#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) -#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) -#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) -#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) -#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) -#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) -#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) -#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) -#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) -#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) -#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) -#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) -#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) -#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) -#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) -#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) -#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) -#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) -#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) -#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) -#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) -#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) -#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) -#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) -#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) -#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) -#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) -#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) -#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) -#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) -#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) -#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) -#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) -#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) -#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) -#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) -#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) -#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) -#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) -#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) -#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) -#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) -#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) -#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) -#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) -#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) -#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) -#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) -#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) -#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) -#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) -#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) -#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) -#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) -#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) -#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) -#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) -#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) -#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) -#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) -#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) -#define STATUS_MCA_OCCURED cpu_to_le32(0xC000036A) -#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) -#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) -#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) -#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) -#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) -#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) -#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) -#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) -#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) -#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) -#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) -#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) -#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) -#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) -#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) -#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) -#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) -#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) -#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) -#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) -#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) -#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) -#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) -#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) -#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) -#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) -#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) -#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) -#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) -#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) -#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) -#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) -#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) -#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) -#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) -#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) -#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) -#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) -#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) -#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) -#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) -#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) -#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) -#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) -#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) -#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) -#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE cpu_to_le32(0xC0000416) -#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) -#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) -#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) -#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) -#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) -#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) -#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) -#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) -#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) -#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) -#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) -#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) -#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) -#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) -#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) -#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) -#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) -#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) -#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) -#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) -#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) -#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) -#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) -#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) -#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) -#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) -#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) -#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) -#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) -#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) -#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) -#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) -#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) -#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) -#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) -#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) -#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) -#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) -#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) -#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) -#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) -#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) -#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) -#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) -#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) -#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED cpu_to_le32(0xC000070C) -#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED cpu_to_le32(0xC000070D) -#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED cpu_to_le32(0xC000070E) -#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) -#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) -#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) -#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) -#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) -#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) -#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) -#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) -#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) -#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) -#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) -#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) -#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) -#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) -#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) -#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) -#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) -#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) -#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) -#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) -#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) -#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) -#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) -#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) -#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) -#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) -#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) -#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) -#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) -#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) -#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) -#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) -#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) -#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) -#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) -#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) -#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) -#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) -#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) -#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED cpu_to_le32(0xC000A080) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR cpu_to_le32(0xC000A081) -#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) -#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) -#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) -#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) -#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) -#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) -#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) -#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) -#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) -#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) -#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) -#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) -#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) -#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) -#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) -#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) -#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) -#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) -#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) -#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) -#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) -#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) -#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) -#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) -#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) -#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) -#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) -#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) -#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) -#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) -#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) -#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) -#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) -#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) -#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) -#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) -#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) -#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) -#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) -#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) -#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) -#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) -#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) -#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) -#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) -#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) -#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) -#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) -#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) -#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) -#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) -#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) -#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) -#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) -#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) -#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) -#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) -#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) -#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) -#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) -#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) -#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) -#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) -#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) -#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) -#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) -#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) -#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) -#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) -#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) -#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) -#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) -#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) -#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) -#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) -#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) -#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) -#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) -#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) -#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) -#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) -#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) -#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) -#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) -#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) -#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) -#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) -#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) -#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) -#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) -#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) -#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) -#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) -#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) -#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) -#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) -#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) -#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) -#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) -#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) -#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) -#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) -#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) -#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) -#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) -#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) -#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) -#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) -#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) -#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) -#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) -#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) -#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) -#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) -#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) -#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) -#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) -#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) -#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) -#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) -#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) -#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) -#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) -#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) -#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) -#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) -#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) -#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) -#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) -#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) -#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) -#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) -#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) -#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) -#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) -#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) -#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) -#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) -#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) -#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) -#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) -#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) -#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) -#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) -#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) -#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) -#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) -#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) -#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) -#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) -#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) -#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) -#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) -#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) -#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) -#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) -#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) -#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) -#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) -#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) -#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) -#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) -#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) -#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) -#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) -#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) -#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) -#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) -#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) -#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) -#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) -#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) -#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) -#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) -#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) -#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) -#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) -#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) -#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) -#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) -#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) -#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) -#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) -#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) -#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) -#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) -#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) -#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) -#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) -#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) -#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) -#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) -#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) -#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) -#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) -#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) -#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) -#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) -#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) -#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) -#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) -#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) -#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) -#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) -#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) -#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) -#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) -#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) -#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) -#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) -#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) -#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) -#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) -#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) -#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) -#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) -#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) -#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) -#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) -#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) -#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) -#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) -#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) -#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) -#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) -#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) -#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) -#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) -#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) -#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) -#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) -#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) -#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY cpu_to_le32(0xC0150012) -#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) -#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) -#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) -#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) -#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) -#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) -#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) -#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT cpu_to_le32(0xC015001C) -#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) -#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) -#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) -#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) -#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) -#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) -#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) -#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) -#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) -#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) -#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) -#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) -#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) -#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) -#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) -#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) -#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) -#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) -#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) -#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) -#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) -#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) -#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) -#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) -#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) -#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) -#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) -#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) -#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) -#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) -#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) -#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) -#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) -#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) -#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) -#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) -#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION cpu_to_le32(0xC0190024) -#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) -#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) -#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) -#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) -#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) -#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) -#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) -#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) -#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) -#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) -#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) -#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) -#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) -#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) -#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) -#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) -#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) -#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) -#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) -#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) -#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) -#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) -#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) -#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) -#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) -#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) -#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) -#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) -#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) -#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) -#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) -#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) -#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) -#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION cpu_to_le32(0xC0190053) -#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) -#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) -#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) -#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) -#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) -#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) -#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) -#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) -#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) -#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) -#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) -#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) -#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) -#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) -#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) -#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) -#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) -#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) -#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) -#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) -#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) -#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) -#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) -#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) -#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) -#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) -#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) -#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) -#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) -#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) -#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) -#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) -#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) -#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) -#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) -#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) -#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) -#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) -#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) -#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) -#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) -#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) -#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) -#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) -#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) -#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) -#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) -#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) -#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) -#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) -#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) -#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) -#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) -#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) -#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) -#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) -#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) -#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) -#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) -#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) -#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) -#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) -#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) -#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) -#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) -#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) -#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) -#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) -#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) -#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) -#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) -#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) -#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) -#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) -#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) -#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) -#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) -#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) -#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) -#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) -#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) -#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) -#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) -#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) -#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) -#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) -#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) -#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) -#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) -#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK cpu_to_le32(0xC01D0006) -#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK cpu_to_le32(0xC01D0007) -#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) -#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) -#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) -#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) -#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) -#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) -#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) -#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) -#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) -#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) -#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) -#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) -#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) -#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) -#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) -#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) -#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) -#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) -#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) -#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) -#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) -#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) -#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) -#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED cpu_to_le32(0xC01E0302) -#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) -#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) -#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) -#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) -#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) -#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE cpu_to_le32(0xC01E0310) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE cpu_to_le32(0xC01E0311) -#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) -#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) -#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) -#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) -#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) -#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) -#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET cpu_to_le32(0xC01E031B) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) -#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) -#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) -#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) -#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) -#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) -#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION cpu_to_le32(0xC01E0325) -#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES cpu_to_le32(0xC01E0326) -#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE cpu_to_le32(0xC01E0328) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET cpu_to_le32(0xC01E0329) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E032E) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) -#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) -#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) -#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) -#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) -#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER cpu_to_le32(0xC01E0334) -#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) -#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) -#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) -#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) -#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) -#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) -#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) -#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) -#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) -#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) -#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) -#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) -#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT cpu_to_le32(0xC01E0341) -#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) -#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) -#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION cpu_to_le32(0xC01E0345) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED cpu_to_le32(0xC01E0346) -#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) -#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) -#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) -#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON cpu_to_le32(0xC01E034D) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) -#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) -#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS cpu_to_le32(0xC01E0350) -#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) -#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) -#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) -#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) -#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) -#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN cpu_to_le32(0xC01E0357) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT cpu_to_le32(0xC01E0358) -#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) -#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION cpu_to_le32(0xC01E035A) -#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) -#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) -#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED cpu_to_le32(0xC01E0400) -#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) -#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) -#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) -#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) -#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) -#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) -#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS cpu_to_le32(0xC01E051C) -#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) -#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS cpu_to_le32(0xC01E051F) -#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) -#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST cpu_to_le32(0xC01E0521) -#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) -#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) -#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) -#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) -#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) -#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) -#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E0506) -#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E0507) -#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E0508) -#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) -#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) -#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) -#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E050D) -#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) -#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) -#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) -#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) -#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA cpu_to_le32(0xC01E0515) -#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) -#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) -#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE cpu_to_le32(0xC01E0518) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS cpu_to_le32(0xC01E051A) -#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E051B) -#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) -#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) -#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) -#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) -#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) -#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) -#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE cpu_to_le32(0xC01E0586) -#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING cpu_to_le32(0xC01E0587) -#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) -#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) -#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) -#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) -#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E05E1) -#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E05E2) -#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) -#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) -#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E05E5) -#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) -#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) -#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) -#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) -#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) -#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) -#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) -#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) -#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) -#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) -#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) -#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) -#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) -#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) -#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) -#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) -#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) -#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) -#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) -#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) -#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) -#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) -#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) -#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) -#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) -#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) -#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) -#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) -#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) -#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) -#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) -#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) -#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) -#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) -#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) -#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) -#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) -#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) -#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) -#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) -#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) -#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) -#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) -#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) -#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) -#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) -#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) -#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) -#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) -#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) -#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) -#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) -#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) -#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) -#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) -#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) -#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) -#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) -#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) -#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) -#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) -#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) -#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) -#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) -#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) -#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) -#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) -#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) -#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) -#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) -#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) -#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) -#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) -#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) -#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) -#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) -#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) -#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) -#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) -#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) -#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) -#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) -#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) -#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) -#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) -#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) -#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) -#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) -#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) -#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) -#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) -#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) -#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) -#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) -#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) -#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) -#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) -#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) -#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) -#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) -#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) -#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) -#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) -#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) -#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) -#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) -#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) -#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) -#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) -#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) -#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) -#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) -#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) -#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) -#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) -#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) -#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) -#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) -#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) -#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) -#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) -#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) -#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) -#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) -#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) -#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) -#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) -#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) -#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) -#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) -#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) -#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) -#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) -#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) -#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) -#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) -#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) -#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) -#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) -#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) -#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c deleted file mode 100644 index 790acf65a092..000000000000 --- a/fs/cifs/smb2transport.c +++ /dev/null @@ -1,934 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) 2006 - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_debug.h" -#include "smb2status.h" -#include "smb2glob.h" - -static int -smb3_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - struct cifs_secmech *p = &server->secmech; - int rc; - - rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); - if (rc) - goto err; - - rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); - if (rc) - goto err; - - return 0; -err: - cifs_free_hash(&p->hmacsha256); - return rc; -} - -int -smb311_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - struct cifs_secmech *p = &server->secmech; - int rc = 0; - - rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); - if (rc) - return rc; - - rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); - if (rc) - goto err; - - rc = cifs_alloc_hash("sha512", &p->sha512); - if (rc) - goto err; - - return 0; - -err: - cifs_free_hash(&p->aes_cmac); - cifs_free_hash(&p->hmacsha256); - return rc; -} - - -static -int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) -{ - struct cifs_chan *chan; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses = NULL; - int i; - int rc = 0; - bool is_binding = false; - - spin_lock(&cifs_tcp_ses_lock); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid == ses_id) - goto found; - } - cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n", - __func__, ses_id); - rc = -ENOENT; - goto out; - -found: - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - - is_binding = (cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD); - if (is_binding) { - /* - * If we are in the process of binding a new channel - * to an existing session, use the master connection - * session key - */ - memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - goto out; - } - - /* - * Otherwise, use the channel key. - */ - - for (i = 0; i < ses->chan_count; i++) { - chan = ses->chans + i; - if (chan->server == server) { - memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - goto out; - } - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - cifs_dbg(VFS, - "%s: Could not find channel signing key for session 0x%llx\n", - __func__, ses_id); - rc = -ENOENT; - -out: - spin_unlock(&cifs_tcp_ses_lock); - return rc; -} - -static struct cifs_ses * -smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid != ses_id) - continue; - ++ses->ses_count; - return ses; - } - - return NULL; -} - -struct cifs_ses * -smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) -{ - struct cifs_ses *ses; - - spin_lock(&cifs_tcp_ses_lock); - ses = smb2_find_smb_ses_unlocked(server, ses_id); - spin_unlock(&cifs_tcp_ses_lock); - - return ses; -} - -static struct cifs_tcon * -smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) -{ - struct cifs_tcon *tcon; - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid != tid) - continue; - ++tcon->tc_count; - return tcon; - } - - return NULL; -} - -/* - * Obtain tcon corresponding to the tid in the given - * cifs_ses - */ - -struct cifs_tcon * -smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) -{ - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - spin_lock(&cifs_tcp_ses_lock); - ses = smb2_find_smb_ses_unlocked(server, ses_id); - if (!ses) { - spin_unlock(&cifs_tcp_ses_lock); - return NULL; - } - tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); - if (!tcon) { - cifs_put_smb_ses(ses); - spin_unlock(&cifs_tcp_ses_lock); - return NULL; - } - spin_unlock(&cifs_tcp_ses_lock); - /* tcon already has a ref to ses, so we don't need ses anymore */ - cifs_put_smb_ses(ses); - - return tcon; -} - -int -smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) -{ - int rc; - unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; - unsigned char *sigptr = smb2_signature; - struct kvec *iov = rqst->rq_iov; - struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct cifs_ses *ses; - struct shash_desc *shash = NULL; - struct smb_rqst drqst; - - ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId)); - if (unlikely(!ses)) { - cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); - return -ENOENT; - } - - memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); - memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - - if (allocate_crypto) { - rc = cifs_alloc_hash("hmac(sha256)", &shash); - if (rc) { - cifs_server_dbg(VFS, - "%s: sha256 alloc failed\n", __func__); - goto out; - } - } else { - shash = server->secmech.hmacsha256; - } - - rc = crypto_shash_setkey(shash->tfm, ses->auth_key.response, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - cifs_server_dbg(VFS, - "%s: Could not update with response\n", - __func__); - goto out; - } - - rc = crypto_shash_init(shash); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); - goto out; - } - - /* - * For SMB2+, __cifs_calc_signature() expects to sign only the actual - * data, that is, iov[0] should not contain a rfc1002 length. - * - * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to - * __cifs_calc_signature(). - */ - drqst = *rqst; - if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { - rc = crypto_shash_update(shash, iov[0].iov_base, - iov[0].iov_len); - if (rc) { - cifs_server_dbg(VFS, - "%s: Could not update with payload\n", - __func__); - goto out; - } - drqst.rq_iov++; - drqst.rq_nvec--; - } - - rc = __cifs_calc_signature(&drqst, server, sigptr, shash); - if (!rc) - memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); - -out: - if (allocate_crypto) - cifs_free_hash(&shash); - if (ses) - cifs_put_smb_ses(ses); - return rc; -} - -static int generate_key(struct cifs_ses *ses, struct kvec label, - struct kvec context, __u8 *key, unsigned int key_size) -{ - unsigned char zero = 0x0; - __u8 i[4] = {0, 0, 0, 1}; - __u8 L128[4] = {0, 0, 0, 128}; - __u8 L256[4] = {0, 0, 1, 0}; - int rc = 0; - unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct TCP_Server_Info *server = ses->server; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - rc = smb3_crypto_shash_allocate(server); - if (rc) { - cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_setkey(server->secmech.hmacsha256->tfm, - ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_init(server->secmech.hmacsha256); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, i, 4); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, label.iov_base, label.iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, &zero, 1); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, context.iov_base, context.iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__); - goto smb3signkey_ret; - } - - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { - rc = crypto_shash_update(server->secmech.hmacsha256, L256, 4); - } else { - rc = crypto_shash_update(server->secmech.hmacsha256, L128, 4); - } - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_final(server->secmech.hmacsha256, hashptr); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - return rc; -} - -struct derivation { - struct kvec label; - struct kvec context; -}; - -struct derivation_triplet { - struct derivation signing; - struct derivation encryption; - struct derivation decryption; -}; - -static int -generate_smb3signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct derivation_triplet *ptriplet) -{ - int rc; - bool is_binding = false; - int chan_index = 0; - - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - is_binding = (cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD); - - chan_index = cifs_ses_get_chan_index(ses, server); - /* TODO: introduce ref counting for channels when the can be freed */ - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - /* - * All channels use the same encryption/decryption keys but - * they have their own signing key. - * - * When we generate the keys, check if it is for a new channel - * (binding) in which case we only need to generate a signing - * key and store it in the channel as to not overwrite the - * master connection signing key stored in the session - */ - - if (is_binding) { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->chans[chan_index].signkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - } else { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->smb3signingkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - - /* safe to access primary channel, since it will never go away */ - spin_lock(&ses->chan_lock); - memcpy(ses->chans[chan_index].signkey, ses->smb3signingkey, - SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - - rc = generate_key(ses, ptriplet->encryption.label, - ptriplet->encryption.context, - ses->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - rc = generate_key(ses, ptriplet->decryption.label, - ptriplet->decryption.context, - ses->smb3decryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - } - - if (rc) - return rc; - -#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS - cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__); - /* - * The session id is opaque in terms of endianness, so we can't - * print it as a long long. we dump it as we got it on the wire - */ - cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), - &ses->Suid); - cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type); - cifs_dbg(VFS, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); - cifs_dbg(VFS, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, ses->smb3signingkey); - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { - cifs_dbg(VFS, "ServerIn Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3encryptionkey); - cifs_dbg(VFS, "ServerOut Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3decryptionkey); - } else { - cifs_dbg(VFS, "ServerIn Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3encryptionkey); - cifs_dbg(VFS, "ServerOut Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey); - } -#endif - return rc; -} - -int -generate_smb30signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server) - -{ - struct derivation_triplet triplet; - struct derivation *d; - - d = &triplet.signing; - d->label.iov_base = "SMB2AESCMAC"; - d->label.iov_len = 12; - d->context.iov_base = "SmbSign"; - d->context.iov_len = 8; - - d = &triplet.encryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerIn "; - d->context.iov_len = 10; - - d = &triplet.decryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerOut"; - d->context.iov_len = 10; - - return generate_smb3signingkey(ses, server, &triplet); -} - -int -generate_smb311signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server) - -{ - struct derivation_triplet triplet; - struct derivation *d; - - d = &triplet.signing; - d->label.iov_base = "SMBSigningKey"; - d->label.iov_len = 14; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - d = &triplet.encryption; - d->label.iov_base = "SMBC2SCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - d = &triplet.decryption; - d->label.iov_base = "SMBS2CCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - return generate_smb3signingkey(ses, server, &triplet); -} - -int -smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) -{ - int rc; - unsigned char smb3_signature[SMB2_CMACAES_SIZE]; - unsigned char *sigptr = smb3_signature; - struct kvec *iov = rqst->rq_iov; - struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct shash_desc *shash = NULL; - struct smb_rqst drqst; - u8 key[SMB3_SIGN_KEY_SIZE]; - - rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); - if (unlikely(rc)) { - cifs_server_dbg(VFS, "%s: Could not get signing key\n", __func__); - return rc; - } - - if (allocate_crypto) { - rc = cifs_alloc_hash("cmac(aes)", &shash); - if (rc) - return rc; - } else { - shash = server->secmech.aes_cmac; - } - - memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); - memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - - rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); - goto out; - } - - /* - * we already allocate aes_cmac when we init smb3 signing key, - * so unlike smb2 case we do not have to check here if secmech are - * initialized - */ - rc = crypto_shash_init(shash); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); - goto out; - } - - /* - * For SMB2+, __cifs_calc_signature() expects to sign only the actual - * data, that is, iov[0] should not contain a rfc1002 length. - * - * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to - * __cifs_calc_signature(). - */ - drqst = *rqst; - if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { - rc = crypto_shash_update(shash, iov[0].iov_base, - iov[0].iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with payload\n", - __func__); - goto out; - } - drqst.rq_iov++; - drqst.rq_nvec--; - } - - rc = __cifs_calc_signature(&drqst, server, sigptr, shash); - if (!rc) - memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); - -out: - if (allocate_crypto) - cifs_free_hash(&shash); - return rc; -} - -/* must be called with server->srv_mutex held */ -static int -smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) -{ - int rc = 0; - struct smb2_hdr *shdr; - struct smb2_sess_setup_req *ssr; - bool is_binding; - bool is_signed; - - shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - ssr = (struct smb2_sess_setup_req *)shdr; - - is_binding = shdr->Command == SMB2_SESSION_SETUP && - (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING); - is_signed = shdr->Flags & SMB2_FLAGS_SIGNED; - - if (!is_signed) - return 0; - spin_lock(&server->srv_lock); - if (server->ops->need_neg && - server->ops->need_neg(server)) { - spin_unlock(&server->srv_lock); - return 0; - } - spin_unlock(&server->srv_lock); - if (!is_binding && !server->session_estab) { - strncpy(shdr->Signature, "BSRSPYL", 8); - return 0; - } - - rc = server->ops->calc_signature(rqst, server, false); - - return rc; -} - -int -smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) -{ - unsigned int rc; - char server_response_sig[SMB2_SIGNATURE_SIZE]; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - - if ((shdr->Command == SMB2_NEGOTIATE) || - (shdr->Command == SMB2_SESSION_SETUP) || - (shdr->Command == SMB2_OPLOCK_BREAK) || - server->ignore_signature || - (!server->session_estab)) - return 0; - - /* - * BB what if signatures are supposed to be on for session but - * server does not send one? BB - */ - - /* Do not need to verify session setups with signature "BSRSPYL " */ - if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0) - cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", - shdr->Command); - - /* - * Save off the origiginal signature so we can modify the smb and check - * our calculated signature against what the server sent. - */ - memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE); - - memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); - - rc = server->ops->calc_signature(rqst, server, true); - - if (rc) - return rc; - - if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) { - cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n", - shdr->Command, shdr->MessageId); - return -EACCES; - } else - return 0; -} - -/* - * Set message id for the request. Should be called after wait_for_free_request - * and when srv_mutex is held. - */ -static inline void -smb2_seq_num_into_buf(struct TCP_Server_Info *server, - struct smb2_hdr *shdr) -{ - unsigned int i, num = le16_to_cpu(shdr->CreditCharge); - - shdr->MessageId = get_next_mid64(server); - /* skip message numbers according to CreditCharge field */ - for (i = 1; i < num; i++) - get_next_mid(server); -} - -static struct mid_q_entry * -smb2_mid_entry_alloc(const struct smb2_hdr *shdr, - struct TCP_Server_Info *server) -{ - struct mid_q_entry *temp; - unsigned int credits = le16_to_cpu(shdr->CreditCharge); - - if (server == NULL) { - cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n"); - return NULL; - } - - temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); - memset(temp, 0, sizeof(struct mid_q_entry)); - kref_init(&temp->refcount); - temp->mid = le64_to_cpu(shdr->MessageId); - temp->credits = credits > 0 ? credits : 1; - temp->pid = current->pid; - temp->command = shdr->Command; /* Always LE */ - temp->when_alloc = jiffies; - temp->server = server; - - /* - * The default is for the mid to be synchronous, so the - * default callback just wakes up the current task. - */ - get_task_struct(current); - temp->creator = current; - temp->callback = cifs_wake_up_task; - temp->callback_data = current; - - atomic_inc(&mid_count); - temp->mid_state = MID_REQUEST_ALLOCATED; - trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), temp->mid); - return temp; -} - -static int -smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct smb2_hdr *shdr, struct mid_q_entry **mid) -{ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); - return -EAGAIN; - } - - if (server->tcpStatus == CifsNeedNegotiate && - shdr->Command != SMB2_NEGOTIATE) { - spin_unlock(&server->srv_lock); - return -EAGAIN; - } - spin_unlock(&server->srv_lock); - - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_NEW) { - if ((shdr->Command != SMB2_SESSION_SETUP) && - (shdr->Command != SMB2_NEGOTIATE)) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are setting up session */ - } - - if (ses->ses_status == SES_EXITING) { - if (shdr->Command != SMB2_LOGOFF) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are shutting down the session */ - } - spin_unlock(&ses->ses_lock); - - *mid = smb2_mid_entry_alloc(shdr, server); - if (*mid == NULL) - return -ENOMEM; - spin_lock(&server->mid_lock); - list_add_tail(&(*mid)->qhead, &server->pending_mid_q); - spin_unlock(&server->mid_lock); - - return 0; -} - -int -smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, - bool log_error) -{ - unsigned int len = mid->resp_buf_size; - struct kvec iov[1]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 1 }; - - iov[0].iov_base = (char *)mid->resp_buf; - iov[0].iov_len = len; - - dump_smb(mid->resp_buf, min_t(u32, 80, len)); - /* convert the length into a more usable form */ - if (len > 24 && server->sign && !mid->decrypted) { - int rc; - - rc = smb2_verify_signature(&rqst, server); - if (rc) - cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - - return map_smb2_to_linux_error(mid->resp_buf, log_error); -} - -struct mid_q_entry * -smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct smb_rqst *rqst) -{ - int rc; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - smb2_seq_num_into_buf(server, shdr); - - rc = smb2_get_mid_entry(ses, server, shdr, &mid); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - return ERR_PTR(rc); - } - - rc = smb2_sign_rqst(rqst, server); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - delete_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -struct mid_q_entry * -smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - int rc; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedNegotiate && - shdr->Command != SMB2_NEGOTIATE) { - spin_unlock(&server->srv_lock); - return ERR_PTR(-EAGAIN); - } - spin_unlock(&server->srv_lock); - - smb2_seq_num_into_buf(server, shdr); - - mid = smb2_mid_entry_alloc(shdr, server); - if (mid == NULL) { - revert_current_mid_from_hdr(server, shdr); - return ERR_PTR(-ENOMEM); - } - - rc = smb2_sign_rqst(rqst, server); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - release_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -int -smb3_crypto_aead_allocate(struct TCP_Server_Info *server) -{ - struct crypto_aead *tfm; - - if (!server->secmech.enc) { - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - else - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - if (IS_ERR(tfm)) { - cifs_server_dbg(VFS, "%s: Failed alloc encrypt aead\n", - __func__); - return PTR_ERR(tfm); - } - server->secmech.enc = tfm; - } - - if (!server->secmech.dec) { - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - else - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - if (IS_ERR(tfm)) { - crypto_free_aead(server->secmech.enc); - server->secmech.enc = NULL; - cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n", - __func__); - return PTR_ERR(tfm); - } - server->secmech.dec = tfm; - } - - return 0; -} diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c deleted file mode 100644 index cf923f211c51..000000000000 --- a/fs/cifs/smbdirect.c +++ /dev/null @@ -1,2494 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2017, Microsoft Corporation. - * - * Author(s): Long Li - */ -#include -#include -#include "smbdirect.h" -#include "cifs_debug.h" -#include "cifsproto.h" -#include "smb2proto.h" - -static struct smbd_response *get_empty_queue_buffer( - struct smbd_connection *info); -static struct smbd_response *get_receive_buffer( - struct smbd_connection *info); -static void put_receive_buffer( - struct smbd_connection *info, - struct smbd_response *response); -static int allocate_receive_buffers(struct smbd_connection *info, int num_buf); -static void destroy_receive_buffers(struct smbd_connection *info); - -static void put_empty_packet( - struct smbd_connection *info, struct smbd_response *response); -static void enqueue_reassembly( - struct smbd_connection *info, - struct smbd_response *response, int data_length); -static struct smbd_response *_get_first_reassembly( - struct smbd_connection *info); - -static int smbd_post_recv( - struct smbd_connection *info, - struct smbd_response *response); - -static int smbd_post_send_empty(struct smbd_connection *info); -static int smbd_post_send_data( - struct smbd_connection *info, - struct kvec *iov, int n_vec, int remaining_data_length); -static int smbd_post_send_page(struct smbd_connection *info, - struct page *page, unsigned long offset, - size_t size, int remaining_data_length); - -static void destroy_mr_list(struct smbd_connection *info); -static int allocate_mr_list(struct smbd_connection *info); - -/* SMBD version number */ -#define SMBD_V1 0x0100 - -/* Port numbers for SMBD transport */ -#define SMB_PORT 445 -#define SMBD_PORT 5445 - -/* Address lookup and resolve timeout in ms */ -#define RDMA_RESOLVE_TIMEOUT 5000 - -/* SMBD negotiation timeout in seconds */ -#define SMBD_NEGOTIATE_TIMEOUT 120 - -/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */ -#define SMBD_MIN_RECEIVE_SIZE 128 -#define SMBD_MIN_FRAGMENTED_SIZE 131072 - -/* - * Default maximum number of RDMA read/write outstanding on this connection - * This value is possibly decreased during QP creation on hardware limit - */ -#define SMBD_CM_RESPONDER_RESOURCES 32 - -/* Maximum number of retries on data transfer operations */ -#define SMBD_CM_RETRY 6 -/* No need to retry on Receiver Not Ready since SMBD manages credits */ -#define SMBD_CM_RNR_RETRY 0 - -/* - * User configurable initial values per SMBD transport connection - * as defined in [MS-SMBD] 3.1.1.1 - * Those may change after a SMBD negotiation - */ -/* The local peer's maximum number of credits to grant to the peer */ -int smbd_receive_credit_max = 255; - -/* The remote peer's credit request of local peer */ -int smbd_send_credit_target = 255; - -/* The maximum single message size can be sent to remote peer */ -int smbd_max_send_size = 1364; - -/* The maximum fragmented upper-layer payload receive size supported */ -int smbd_max_fragmented_recv_size = 1024 * 1024; - -/* The maximum single-message size which can be received */ -int smbd_max_receive_size = 1364; - -/* The timeout to initiate send of a keepalive message on idle */ -int smbd_keep_alive_interval = 120; - -/* - * User configurable initial values for RDMA transport - * The actual values used may be lower and are limited to hardware capabilities - */ -/* Default maximum number of pages in a single RDMA write/read */ -int smbd_max_frmr_depth = 2048; - -/* If payload is less than this byte, use RDMA send/recv not read/write */ -int rdma_readwrite_threshold = 4096; - -/* Transport logging functions - * Logging are defined as classes. They can be OR'ed to define the actual - * logging level via module parameter smbd_logging_class - * e.g. cifs.smbd_logging_class=0xa0 will log all log_rdma_recv() and - * log_rdma_event() - */ -#define LOG_OUTGOING 0x1 -#define LOG_INCOMING 0x2 -#define LOG_READ 0x4 -#define LOG_WRITE 0x8 -#define LOG_RDMA_SEND 0x10 -#define LOG_RDMA_RECV 0x20 -#define LOG_KEEP_ALIVE 0x40 -#define LOG_RDMA_EVENT 0x80 -#define LOG_RDMA_MR 0x100 -static unsigned int smbd_logging_class; -module_param(smbd_logging_class, uint, 0644); -MODULE_PARM_DESC(smbd_logging_class, - "Logging class for SMBD transport 0x0 to 0x100"); - -#define ERR 0x0 -#define INFO 0x1 -static unsigned int smbd_logging_level = ERR; -module_param(smbd_logging_level, uint, 0644); -MODULE_PARM_DESC(smbd_logging_level, - "Logging level for SMBD transport, 0 (default): error, 1: info"); - -#define log_rdma(level, class, fmt, args...) \ -do { \ - if (level <= smbd_logging_level || class & smbd_logging_class) \ - cifs_dbg(VFS, "%s:%d " fmt, __func__, __LINE__, ##args);\ -} while (0) - -#define log_outgoing(level, fmt, args...) \ - log_rdma(level, LOG_OUTGOING, fmt, ##args) -#define log_incoming(level, fmt, args...) \ - log_rdma(level, LOG_INCOMING, fmt, ##args) -#define log_read(level, fmt, args...) log_rdma(level, LOG_READ, fmt, ##args) -#define log_write(level, fmt, args...) log_rdma(level, LOG_WRITE, fmt, ##args) -#define log_rdma_send(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_SEND, fmt, ##args) -#define log_rdma_recv(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_RECV, fmt, ##args) -#define log_keep_alive(level, fmt, args...) \ - log_rdma(level, LOG_KEEP_ALIVE, fmt, ##args) -#define log_rdma_event(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_EVENT, fmt, ##args) -#define log_rdma_mr(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_MR, fmt, ##args) - -static void smbd_disconnect_rdma_work(struct work_struct *work) -{ - struct smbd_connection *info = - container_of(work, struct smbd_connection, disconnect_work); - - if (info->transport_status == SMBD_CONNECTED) { - info->transport_status = SMBD_DISCONNECTING; - rdma_disconnect(info->id); - } -} - -static void smbd_disconnect_rdma_connection(struct smbd_connection *info) -{ - queue_work(info->workqueue, &info->disconnect_work); -} - -/* Upcall from RDMA CM */ -static int smbd_conn_upcall( - struct rdma_cm_id *id, struct rdma_cm_event *event) -{ - struct smbd_connection *info = id->context; - - log_rdma_event(INFO, "event=%d status=%d\n", - event->event, event->status); - - switch (event->event) { - case RDMA_CM_EVENT_ADDR_RESOLVED: - case RDMA_CM_EVENT_ROUTE_RESOLVED: - info->ri_rc = 0; - complete(&info->ri_done); - break; - - case RDMA_CM_EVENT_ADDR_ERROR: - info->ri_rc = -EHOSTUNREACH; - complete(&info->ri_done); - break; - - case RDMA_CM_EVENT_ROUTE_ERROR: - info->ri_rc = -ENETUNREACH; - complete(&info->ri_done); - break; - - case RDMA_CM_EVENT_ESTABLISHED: - log_rdma_event(INFO, "connected event=%d\n", event->event); - info->transport_status = SMBD_CONNECTED; - wake_up_interruptible(&info->conn_wait); - break; - - case RDMA_CM_EVENT_CONNECT_ERROR: - case RDMA_CM_EVENT_UNREACHABLE: - case RDMA_CM_EVENT_REJECTED: - log_rdma_event(INFO, "connecting failed event=%d\n", event->event); - info->transport_status = SMBD_DISCONNECTED; - wake_up_interruptible(&info->conn_wait); - break; - - case RDMA_CM_EVENT_DEVICE_REMOVAL: - case RDMA_CM_EVENT_DISCONNECTED: - /* This happenes when we fail the negotiation */ - if (info->transport_status == SMBD_NEGOTIATE_FAILED) { - info->transport_status = SMBD_DISCONNECTED; - wake_up(&info->conn_wait); - break; - } - - info->transport_status = SMBD_DISCONNECTED; - wake_up_interruptible(&info->disconn_wait); - wake_up_interruptible(&info->wait_reassembly_queue); - wake_up_interruptible_all(&info->wait_send_queue); - break; - - default: - break; - } - - return 0; -} - -/* Upcall from RDMA QP */ -static void -smbd_qp_async_error_upcall(struct ib_event *event, void *context) -{ - struct smbd_connection *info = context; - - log_rdma_event(ERR, "%s on device %s info %p\n", - ib_event_msg(event->event), event->device->name, info); - - switch (event->event) { - case IB_EVENT_CQ_ERR: - case IB_EVENT_QP_FATAL: - smbd_disconnect_rdma_connection(info); - break; - - default: - break; - } -} - -static inline void *smbd_request_payload(struct smbd_request *request) -{ - return (void *)request->packet; -} - -static inline void *smbd_response_payload(struct smbd_response *response) -{ - return (void *)response->packet; -} - -/* Called when a RDMA send is done */ -static void send_done(struct ib_cq *cq, struct ib_wc *wc) -{ - int i; - struct smbd_request *request = - container_of(wc->wr_cqe, struct smbd_request, cqe); - - log_rdma_send(INFO, "smbd_request 0x%p completed wc->status=%d\n", - request, wc->status); - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { - log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n", - wc->status, wc->opcode); - smbd_disconnect_rdma_connection(request->info); - } - - for (i = 0; i < request->num_sge; i++) - ib_dma_unmap_single(request->info->id->device, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - - if (atomic_dec_and_test(&request->info->send_pending)) - wake_up(&request->info->wait_send_pending); - - wake_up(&request->info->wait_post_send); - - mempool_free(request, request->info->request_mempool); -} - -static void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp) -{ - log_rdma_event(INFO, "resp message min_version %u max_version %u negotiated_version %u credits_requested %u credits_granted %u status %u max_readwrite_size %u preferred_send_size %u max_receive_size %u max_fragmented_size %u\n", - resp->min_version, resp->max_version, - resp->negotiated_version, resp->credits_requested, - resp->credits_granted, resp->status, - resp->max_readwrite_size, resp->preferred_send_size, - resp->max_receive_size, resp->max_fragmented_size); -} - -/* - * Process a negotiation response message, according to [MS-SMBD]3.1.5.7 - * response, packet_length: the negotiation response message - * return value: true if negotiation is a success, false if failed - */ -static bool process_negotiation_response( - struct smbd_response *response, int packet_length) -{ - struct smbd_connection *info = response->info; - struct smbd_negotiate_resp *packet = smbd_response_payload(response); - - if (packet_length < sizeof(struct smbd_negotiate_resp)) { - log_rdma_event(ERR, - "error: packet_length=%d\n", packet_length); - return false; - } - - if (le16_to_cpu(packet->negotiated_version) != SMBD_V1) { - log_rdma_event(ERR, "error: negotiated_version=%x\n", - le16_to_cpu(packet->negotiated_version)); - return false; - } - info->protocol = le16_to_cpu(packet->negotiated_version); - - if (packet->credits_requested == 0) { - log_rdma_event(ERR, "error: credits_requested==0\n"); - return false; - } - info->receive_credit_target = le16_to_cpu(packet->credits_requested); - - if (packet->credits_granted == 0) { - log_rdma_event(ERR, "error: credits_granted==0\n"); - return false; - } - atomic_set(&info->send_credits, le16_to_cpu(packet->credits_granted)); - - atomic_set(&info->receive_credits, 0); - - if (le32_to_cpu(packet->preferred_send_size) > info->max_receive_size) { - log_rdma_event(ERR, "error: preferred_send_size=%d\n", - le32_to_cpu(packet->preferred_send_size)); - return false; - } - info->max_receive_size = le32_to_cpu(packet->preferred_send_size); - - if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) { - log_rdma_event(ERR, "error: max_receive_size=%d\n", - le32_to_cpu(packet->max_receive_size)); - return false; - } - info->max_send_size = min_t(int, info->max_send_size, - le32_to_cpu(packet->max_receive_size)); - - if (le32_to_cpu(packet->max_fragmented_size) < - SMBD_MIN_FRAGMENTED_SIZE) { - log_rdma_event(ERR, "error: max_fragmented_size=%d\n", - le32_to_cpu(packet->max_fragmented_size)); - return false; - } - info->max_fragmented_send_size = - le32_to_cpu(packet->max_fragmented_size); - info->rdma_readwrite_threshold = - rdma_readwrite_threshold > info->max_fragmented_send_size ? - info->max_fragmented_send_size : - rdma_readwrite_threshold; - - - info->max_readwrite_size = min_t(u32, - le32_to_cpu(packet->max_readwrite_size), - info->max_frmr_depth * PAGE_SIZE); - info->max_frmr_depth = info->max_readwrite_size / PAGE_SIZE; - - return true; -} - -static void smbd_post_send_credits(struct work_struct *work) -{ - int ret = 0; - int use_receive_queue = 1; - int rc; - struct smbd_response *response; - struct smbd_connection *info = - container_of(work, struct smbd_connection, - post_send_credits_work); - - if (info->transport_status != SMBD_CONNECTED) { - wake_up(&info->wait_receive_queues); - return; - } - - if (info->receive_credit_target > - atomic_read(&info->receive_credits)) { - while (true) { - if (use_receive_queue) - response = get_receive_buffer(info); - else - response = get_empty_queue_buffer(info); - if (!response) { - /* now switch to emtpy packet queue */ - if (use_receive_queue) { - use_receive_queue = 0; - continue; - } else - break; - } - - response->type = SMBD_TRANSFER_DATA; - response->first_segment = false; - rc = smbd_post_recv(info, response); - if (rc) { - log_rdma_recv(ERR, - "post_recv failed rc=%d\n", rc); - put_receive_buffer(info, response); - break; - } - - ret++; - } - } - - spin_lock(&info->lock_new_credits_offered); - info->new_credits_offered += ret; - spin_unlock(&info->lock_new_credits_offered); - - /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ - info->send_immediate = true; - if (atomic_read(&info->receive_credits) < - info->receive_credit_target - 1) { - if (info->keep_alive_requested == KEEP_ALIVE_PENDING || - info->send_immediate) { - log_keep_alive(INFO, "send an empty message\n"); - smbd_post_send_empty(info); - } - } -} - -/* Called from softirq, when recv is done */ -static void recv_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smbd_data_transfer *data_transfer; - struct smbd_response *response = - container_of(wc->wr_cqe, struct smbd_response, cqe); - struct smbd_connection *info = response->info; - int data_length = 0; - - log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n", - response, response->type, wc->status, wc->opcode, - wc->byte_len, wc->pkey_index); - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { - log_rdma_recv(INFO, "wc->status=%d opcode=%d\n", - wc->status, wc->opcode); - smbd_disconnect_rdma_connection(info); - goto error; - } - - ib_dma_sync_single_for_cpu( - wc->qp->device, - response->sge.addr, - response->sge.length, - DMA_FROM_DEVICE); - - switch (response->type) { - /* SMBD negotiation response */ - case SMBD_NEGOTIATE_RESP: - dump_smbd_negotiate_resp(smbd_response_payload(response)); - info->full_packet_received = true; - info->negotiate_done = - process_negotiation_response(response, wc->byte_len); - complete(&info->negotiate_completion); - break; - - /* SMBD data transfer packet */ - case SMBD_TRANSFER_DATA: - data_transfer = smbd_response_payload(response); - data_length = le32_to_cpu(data_transfer->data_length); - - /* - * If this is a packet with data playload place the data in - * reassembly queue and wake up the reading thread - */ - if (data_length) { - if (info->full_packet_received) - response->first_segment = true; - - if (le32_to_cpu(data_transfer->remaining_data_length)) - info->full_packet_received = false; - else - info->full_packet_received = true; - - enqueue_reassembly( - info, - response, - data_length); - } else - put_empty_packet(info, response); - - if (data_length) - wake_up_interruptible(&info->wait_reassembly_queue); - - atomic_dec(&info->receive_credits); - info->receive_credit_target = - le16_to_cpu(data_transfer->credits_requested); - if (le16_to_cpu(data_transfer->credits_granted)) { - atomic_add(le16_to_cpu(data_transfer->credits_granted), - &info->send_credits); - /* - * We have new send credits granted from remote peer - * If any sender is waiting for credits, unblock it - */ - wake_up_interruptible(&info->wait_send_queue); - } - - log_incoming(INFO, "data flags %d data_offset %d data_length %d remaining_data_length %d\n", - le16_to_cpu(data_transfer->flags), - le32_to_cpu(data_transfer->data_offset), - le32_to_cpu(data_transfer->data_length), - le32_to_cpu(data_transfer->remaining_data_length)); - - /* Send a KEEP_ALIVE response right away if requested */ - info->keep_alive_requested = KEEP_ALIVE_NONE; - if (le16_to_cpu(data_transfer->flags) & - SMB_DIRECT_RESPONSE_REQUESTED) { - info->keep_alive_requested = KEEP_ALIVE_PENDING; - } - - return; - - default: - log_rdma_recv(ERR, - "unexpected response type=%d\n", response->type); - } - -error: - put_receive_buffer(info, response); -} - -static struct rdma_cm_id *smbd_create_id( - struct smbd_connection *info, - struct sockaddr *dstaddr, int port) -{ - struct rdma_cm_id *id; - int rc; - __be16 *sport; - - id = rdma_create_id(&init_net, smbd_conn_upcall, info, - RDMA_PS_TCP, IB_QPT_RC); - if (IS_ERR(id)) { - rc = PTR_ERR(id); - log_rdma_event(ERR, "rdma_create_id() failed %i\n", rc); - return id; - } - - if (dstaddr->sa_family == AF_INET6) - sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; - else - sport = &((struct sockaddr_in *)dstaddr)->sin_port; - - *sport = htons(port); - - init_completion(&info->ri_done); - info->ri_rc = -ETIMEDOUT; - - rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr, - RDMA_RESOLVE_TIMEOUT); - if (rc) { - log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc); - goto out; - } - rc = wait_for_completion_interruptible_timeout( - &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); - /* e.g. if interrupted returns -ERESTARTSYS */ - if (rc < 0) { - log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); - goto out; - } - rc = info->ri_rc; - if (rc) { - log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc); - goto out; - } - - info->ri_rc = -ETIMEDOUT; - rc = rdma_resolve_route(id, RDMA_RESOLVE_TIMEOUT); - if (rc) { - log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc); - goto out; - } - rc = wait_for_completion_interruptible_timeout( - &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); - /* e.g. if interrupted returns -ERESTARTSYS */ - if (rc < 0) { - log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); - goto out; - } - rc = info->ri_rc; - if (rc) { - log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc); - goto out; - } - - return id; - -out: - rdma_destroy_id(id); - return ERR_PTR(rc); -} - -/* - * Test if FRWR (Fast Registration Work Requests) is supported on the device - * This implementation requries FRWR on RDMA read/write - * return value: true if it is supported - */ -static bool frwr_is_supported(struct ib_device_attr *attrs) -{ - if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) - return false; - if (attrs->max_fast_reg_page_list_len == 0) - return false; - return true; -} - -static int smbd_ia_open( - struct smbd_connection *info, - struct sockaddr *dstaddr, int port) -{ - int rc; - - info->id = smbd_create_id(info, dstaddr, port); - if (IS_ERR(info->id)) { - rc = PTR_ERR(info->id); - goto out1; - } - - if (!frwr_is_supported(&info->id->device->attrs)) { - log_rdma_event(ERR, "Fast Registration Work Requests (FRWR) is not supported\n"); - log_rdma_event(ERR, "Device capability flags = %llx max_fast_reg_page_list_len = %u\n", - info->id->device->attrs.device_cap_flags, - info->id->device->attrs.max_fast_reg_page_list_len); - rc = -EPROTONOSUPPORT; - goto out2; - } - info->max_frmr_depth = min_t(int, - smbd_max_frmr_depth, - info->id->device->attrs.max_fast_reg_page_list_len); - info->mr_type = IB_MR_TYPE_MEM_REG; - if (info->id->device->attrs.kernel_cap_flags & IBK_SG_GAPS_REG) - info->mr_type = IB_MR_TYPE_SG_GAPS; - - info->pd = ib_alloc_pd(info->id->device, 0); - if (IS_ERR(info->pd)) { - rc = PTR_ERR(info->pd); - log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc); - goto out2; - } - - return 0; - -out2: - rdma_destroy_id(info->id); - info->id = NULL; - -out1: - return rc; -} - -/* - * Send a negotiation request message to the peer - * The negotiation procedure is in [MS-SMBD] 3.1.5.2 and 3.1.5.3 - * After negotiation, the transport is connected and ready for - * carrying upper layer SMB payload - */ -static int smbd_post_send_negotiate_req(struct smbd_connection *info) -{ - struct ib_send_wr send_wr; - int rc = -ENOMEM; - struct smbd_request *request; - struct smbd_negotiate_req *packet; - - request = mempool_alloc(info->request_mempool, GFP_KERNEL); - if (!request) - return rc; - - request->info = info; - - packet = smbd_request_payload(request); - packet->min_version = cpu_to_le16(SMBD_V1); - packet->max_version = cpu_to_le16(SMBD_V1); - packet->reserved = 0; - packet->credits_requested = cpu_to_le16(info->send_credit_target); - packet->preferred_send_size = cpu_to_le32(info->max_send_size); - packet->max_receive_size = cpu_to_le32(info->max_receive_size); - packet->max_fragmented_size = - cpu_to_le32(info->max_fragmented_recv_size); - - request->num_sge = 1; - request->sge[0].addr = ib_dma_map_single( - info->id->device, (void *)packet, - sizeof(*packet), DMA_TO_DEVICE); - if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { - rc = -EIO; - goto dma_mapping_failed; - } - - request->sge[0].length = sizeof(*packet); - request->sge[0].lkey = info->pd->local_dma_lkey; - - ib_dma_sync_single_for_device( - info->id->device, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - request->cqe.done = send_done; - - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; - - log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n", - request->sge[0].addr, - request->sge[0].length, request->sge[0].lkey); - - atomic_inc(&info->send_pending); - rc = ib_post_send(info->id->qp, &send_wr, NULL); - if (!rc) - return 0; - - /* if we reach here, post send failed */ - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - atomic_dec(&info->send_pending); - ib_dma_unmap_single(info->id->device, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - smbd_disconnect_rdma_connection(info); - -dma_mapping_failed: - mempool_free(request, info->request_mempool); - return rc; -} - -/* - * Extend the credits to remote peer - * This implements [MS-SMBD] 3.1.5.9 - * The idea is that we should extend credits to remote peer as quickly as - * it's allowed, to maintain data flow. We allocate as much receive - * buffer as possible, and extend the receive credits to remote peer - * return value: the new credtis being granted. - */ -static int manage_credits_prior_sending(struct smbd_connection *info) -{ - int new_credits; - - spin_lock(&info->lock_new_credits_offered); - new_credits = info->new_credits_offered; - info->new_credits_offered = 0; - spin_unlock(&info->lock_new_credits_offered); - - return new_credits; -} - -/* - * Check if we need to send a KEEP_ALIVE message - * The idle connection timer triggers a KEEP_ALIVE message when expires - * SMB_DIRECT_RESPONSE_REQUESTED is set in the message flag to have peer send - * back a response. - * return value: - * 1 if SMB_DIRECT_RESPONSE_REQUESTED needs to be set - * 0: otherwise - */ -static int manage_keep_alive_before_sending(struct smbd_connection *info) -{ - if (info->keep_alive_requested == KEEP_ALIVE_PENDING) { - info->keep_alive_requested = KEEP_ALIVE_SENT; - return 1; - } - return 0; -} - -/* Post the send request */ -static int smbd_post_send(struct smbd_connection *info, - struct smbd_request *request) -{ - struct ib_send_wr send_wr; - int rc, i; - - for (i = 0; i < request->num_sge; i++) { - log_rdma_send(INFO, - "rdma_request sge[%d] addr=0x%llx length=%u\n", - i, request->sge[i].addr, request->sge[i].length); - ib_dma_sync_single_for_device( - info->id->device, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - } - - request->cqe.done = send_done; - - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; - - rc = ib_post_send(info->id->qp, &send_wr, NULL); - if (rc) { - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - smbd_disconnect_rdma_connection(info); - rc = -EAGAIN; - } else - /* Reset timer for idle connection after packet is sent */ - mod_delayed_work(info->workqueue, &info->idle_timer_work, - info->keep_alive_interval*HZ); - - return rc; -} - -static int smbd_post_send_sgl(struct smbd_connection *info, - struct scatterlist *sgl, int data_length, int remaining_data_length) -{ - int num_sgs; - int i, rc; - int header_length; - struct smbd_request *request; - struct smbd_data_transfer *packet; - int new_credits; - struct scatterlist *sg; - -wait_credit: - /* Wait for send credits. A SMBD packet needs one credit */ - rc = wait_event_interruptible(info->wait_send_queue, - atomic_read(&info->send_credits) > 0 || - info->transport_status != SMBD_CONNECTED); - if (rc) - goto err_wait_credit; - - if (info->transport_status != SMBD_CONNECTED) { - log_outgoing(ERR, "disconnected not sending on wait_credit\n"); - rc = -EAGAIN; - goto err_wait_credit; - } - if (unlikely(atomic_dec_return(&info->send_credits) < 0)) { - atomic_inc(&info->send_credits); - goto wait_credit; - } - -wait_send_queue: - wait_event(info->wait_post_send, - atomic_read(&info->send_pending) < info->send_credit_target || - info->transport_status != SMBD_CONNECTED); - - if (info->transport_status != SMBD_CONNECTED) { - log_outgoing(ERR, "disconnected not sending on wait_send_queue\n"); - rc = -EAGAIN; - goto err_wait_send_queue; - } - - if (unlikely(atomic_inc_return(&info->send_pending) > - info->send_credit_target)) { - atomic_dec(&info->send_pending); - goto wait_send_queue; - } - - request = mempool_alloc(info->request_mempool, GFP_KERNEL); - if (!request) { - rc = -ENOMEM; - goto err_alloc; - } - - request->info = info; - - /* Fill in the packet header */ - packet = smbd_request_payload(request); - packet->credits_requested = cpu_to_le16(info->send_credit_target); - - new_credits = manage_credits_prior_sending(info); - atomic_add(new_credits, &info->receive_credits); - packet->credits_granted = cpu_to_le16(new_credits); - - info->send_immediate = false; - - packet->flags = 0; - if (manage_keep_alive_before_sending(info)) - packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED); - - packet->reserved = 0; - if (!data_length) - packet->data_offset = 0; - else - packet->data_offset = cpu_to_le32(24); - packet->data_length = cpu_to_le32(data_length); - packet->remaining_data_length = cpu_to_le32(remaining_data_length); - packet->padding = 0; - - log_outgoing(INFO, "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", - le16_to_cpu(packet->credits_requested), - le16_to_cpu(packet->credits_granted), - le32_to_cpu(packet->data_offset), - le32_to_cpu(packet->data_length), - le32_to_cpu(packet->remaining_data_length)); - - /* Map the packet to DMA */ - header_length = sizeof(struct smbd_data_transfer); - /* If this is a packet without payload, don't send padding */ - if (!data_length) - header_length = offsetof(struct smbd_data_transfer, padding); - - request->num_sge = 1; - request->sge[0].addr = ib_dma_map_single(info->id->device, - (void *)packet, - header_length, - DMA_TO_DEVICE); - if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { - rc = -EIO; - request->sge[0].addr = 0; - goto err_dma; - } - - request->sge[0].length = header_length; - request->sge[0].lkey = info->pd->local_dma_lkey; - - /* Fill in the packet data payload */ - num_sgs = sgl ? sg_nents(sgl) : 0; - for_each_sg(sgl, sg, num_sgs, i) { - request->sge[i+1].addr = - ib_dma_map_page(info->id->device, sg_page(sg), - sg->offset, sg->length, DMA_TO_DEVICE); - if (ib_dma_mapping_error( - info->id->device, request->sge[i+1].addr)) { - rc = -EIO; - request->sge[i+1].addr = 0; - goto err_dma; - } - request->sge[i+1].length = sg->length; - request->sge[i+1].lkey = info->pd->local_dma_lkey; - request->num_sge++; - } - - rc = smbd_post_send(info, request); - if (!rc) - return 0; - -err_dma: - for (i = 0; i < request->num_sge; i++) - if (request->sge[i].addr) - ib_dma_unmap_single(info->id->device, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - mempool_free(request, info->request_mempool); - - /* roll back receive credits and credits to be offered */ - spin_lock(&info->lock_new_credits_offered); - info->new_credits_offered += new_credits; - spin_unlock(&info->lock_new_credits_offered); - atomic_sub(new_credits, &info->receive_credits); - -err_alloc: - if (atomic_dec_and_test(&info->send_pending)) - wake_up(&info->wait_send_pending); - -err_wait_send_queue: - /* roll back send credits and pending */ - atomic_inc(&info->send_credits); - -err_wait_credit: - return rc; -} - -/* - * Send a page - * page: the page to send - * offset: offset in the page to send - * size: length in the page to send - * remaining_data_length: remaining data to send in this payload - */ -static int smbd_post_send_page(struct smbd_connection *info, struct page *page, - unsigned long offset, size_t size, int remaining_data_length) -{ - struct scatterlist sgl; - - sg_init_table(&sgl, 1); - sg_set_page(&sgl, page, size, offset); - - return smbd_post_send_sgl(info, &sgl, size, remaining_data_length); -} - -/* - * Send an empty message - * Empty message is used to extend credits to peer to for keep live - * while there is no upper layer payload to send at the time - */ -static int smbd_post_send_empty(struct smbd_connection *info) -{ - info->count_send_empty++; - return smbd_post_send_sgl(info, NULL, 0, 0); -} - -/* - * Send a data buffer - * iov: the iov array describing the data buffers - * n_vec: number of iov array - * remaining_data_length: remaining data to send following this packet - * in segmented SMBD packet - */ -static int smbd_post_send_data( - struct smbd_connection *info, struct kvec *iov, int n_vec, - int remaining_data_length) -{ - int i; - u32 data_length = 0; - struct scatterlist sgl[SMBDIRECT_MAX_SEND_SGE - 1]; - - if (n_vec > SMBDIRECT_MAX_SEND_SGE - 1) { - cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec); - return -EINVAL; - } - - sg_init_table(sgl, n_vec); - for (i = 0; i < n_vec; i++) { - data_length += iov[i].iov_len; - sg_set_buf(&sgl[i], iov[i].iov_base, iov[i].iov_len); - } - - return smbd_post_send_sgl(info, sgl, data_length, remaining_data_length); -} - -/* - * Post a receive request to the transport - * The remote peer can only send data when a receive request is posted - * The interaction is controlled by send/receive credit system - */ -static int smbd_post_recv( - struct smbd_connection *info, struct smbd_response *response) -{ - struct ib_recv_wr recv_wr; - int rc = -EIO; - - response->sge.addr = ib_dma_map_single( - info->id->device, response->packet, - info->max_receive_size, DMA_FROM_DEVICE); - if (ib_dma_mapping_error(info->id->device, response->sge.addr)) - return rc; - - response->sge.length = info->max_receive_size; - response->sge.lkey = info->pd->local_dma_lkey; - - response->cqe.done = recv_done; - - recv_wr.wr_cqe = &response->cqe; - recv_wr.next = NULL; - recv_wr.sg_list = &response->sge; - recv_wr.num_sge = 1; - - rc = ib_post_recv(info->id->qp, &recv_wr, NULL); - if (rc) { - ib_dma_unmap_single(info->id->device, response->sge.addr, - response->sge.length, DMA_FROM_DEVICE); - smbd_disconnect_rdma_connection(info); - log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc); - } - - return rc; -} - -/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */ -static int smbd_negotiate(struct smbd_connection *info) -{ - int rc; - struct smbd_response *response = get_receive_buffer(info); - - response->type = SMBD_NEGOTIATE_RESP; - rc = smbd_post_recv(info, response); - log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n", - rc, response->sge.addr, - response->sge.length, response->sge.lkey); - if (rc) - return rc; - - init_completion(&info->negotiate_completion); - info->negotiate_done = false; - rc = smbd_post_send_negotiate_req(info); - if (rc) - return rc; - - rc = wait_for_completion_interruptible_timeout( - &info->negotiate_completion, SMBD_NEGOTIATE_TIMEOUT * HZ); - log_rdma_event(INFO, "wait_for_completion_timeout rc=%d\n", rc); - - if (info->negotiate_done) - return 0; - - if (rc == 0) - rc = -ETIMEDOUT; - else if (rc == -ERESTARTSYS) - rc = -EINTR; - else - rc = -ENOTCONN; - - return rc; -} - -static void put_empty_packet( - struct smbd_connection *info, struct smbd_response *response) -{ - spin_lock(&info->empty_packet_queue_lock); - list_add_tail(&response->list, &info->empty_packet_queue); - info->count_empty_packet_queue++; - spin_unlock(&info->empty_packet_queue_lock); - - queue_work(info->workqueue, &info->post_send_credits_work); -} - -/* - * Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1 - * This is a queue for reassembling upper layer payload and present to upper - * layer. All the inncoming payload go to the reassembly queue, regardless of - * if reassembly is required. The uuper layer code reads from the queue for all - * incoming payloads. - * Put a received packet to the reassembly queue - * response: the packet received - * data_length: the size of payload in this packet - */ -static void enqueue_reassembly( - struct smbd_connection *info, - struct smbd_response *response, - int data_length) -{ - spin_lock(&info->reassembly_queue_lock); - list_add_tail(&response->list, &info->reassembly_queue); - info->reassembly_queue_length++; - /* - * Make sure reassembly_data_length is updated after list and - * reassembly_queue_length are updated. On the dequeue side - * reassembly_data_length is checked without a lock to determine - * if reassembly_queue_length and list is up to date - */ - virt_wmb(); - info->reassembly_data_length += data_length; - spin_unlock(&info->reassembly_queue_lock); - info->count_reassembly_queue++; - info->count_enqueue_reassembly_queue++; -} - -/* - * Get the first entry at the front of reassembly queue - * Caller is responsible for locking - * return value: the first entry if any, NULL if queue is empty - */ -static struct smbd_response *_get_first_reassembly(struct smbd_connection *info) -{ - struct smbd_response *ret = NULL; - - if (!list_empty(&info->reassembly_queue)) { - ret = list_first_entry( - &info->reassembly_queue, - struct smbd_response, list); - } - return ret; -} - -static struct smbd_response *get_empty_queue_buffer( - struct smbd_connection *info) -{ - struct smbd_response *ret = NULL; - unsigned long flags; - - spin_lock_irqsave(&info->empty_packet_queue_lock, flags); - if (!list_empty(&info->empty_packet_queue)) { - ret = list_first_entry( - &info->empty_packet_queue, - struct smbd_response, list); - list_del(&ret->list); - info->count_empty_packet_queue--; - } - spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags); - - return ret; -} - -/* - * Get a receive buffer - * For each remote send, we need to post a receive. The receive buffers are - * pre-allocated in advance. - * return value: the receive buffer, NULL if none is available - */ -static struct smbd_response *get_receive_buffer(struct smbd_connection *info) -{ - struct smbd_response *ret = NULL; - unsigned long flags; - - spin_lock_irqsave(&info->receive_queue_lock, flags); - if (!list_empty(&info->receive_queue)) { - ret = list_first_entry( - &info->receive_queue, - struct smbd_response, list); - list_del(&ret->list); - info->count_receive_queue--; - info->count_get_receive_buffer++; - } - spin_unlock_irqrestore(&info->receive_queue_lock, flags); - - return ret; -} - -/* - * Return a receive buffer - * Upon returning of a receive buffer, we can post new receive and extend - * more receive credits to remote peer. This is done immediately after a - * receive buffer is returned. - */ -static void put_receive_buffer( - struct smbd_connection *info, struct smbd_response *response) -{ - unsigned long flags; - - ib_dma_unmap_single(info->id->device, response->sge.addr, - response->sge.length, DMA_FROM_DEVICE); - - spin_lock_irqsave(&info->receive_queue_lock, flags); - list_add_tail(&response->list, &info->receive_queue); - info->count_receive_queue++; - info->count_put_receive_buffer++; - spin_unlock_irqrestore(&info->receive_queue_lock, flags); - - queue_work(info->workqueue, &info->post_send_credits_work); -} - -/* Preallocate all receive buffer on transport establishment */ -static int allocate_receive_buffers(struct smbd_connection *info, int num_buf) -{ - int i; - struct smbd_response *response; - - INIT_LIST_HEAD(&info->reassembly_queue); - spin_lock_init(&info->reassembly_queue_lock); - info->reassembly_data_length = 0; - info->reassembly_queue_length = 0; - - INIT_LIST_HEAD(&info->receive_queue); - spin_lock_init(&info->receive_queue_lock); - info->count_receive_queue = 0; - - INIT_LIST_HEAD(&info->empty_packet_queue); - spin_lock_init(&info->empty_packet_queue_lock); - info->count_empty_packet_queue = 0; - - init_waitqueue_head(&info->wait_receive_queues); - - for (i = 0; i < num_buf; i++) { - response = mempool_alloc(info->response_mempool, GFP_KERNEL); - if (!response) - goto allocate_failed; - - response->info = info; - list_add_tail(&response->list, &info->receive_queue); - info->count_receive_queue++; - } - - return 0; - -allocate_failed: - while (!list_empty(&info->receive_queue)) { - response = list_first_entry( - &info->receive_queue, - struct smbd_response, list); - list_del(&response->list); - info->count_receive_queue--; - - mempool_free(response, info->response_mempool); - } - return -ENOMEM; -} - -static void destroy_receive_buffers(struct smbd_connection *info) -{ - struct smbd_response *response; - - while ((response = get_receive_buffer(info))) - mempool_free(response, info->response_mempool); - - while ((response = get_empty_queue_buffer(info))) - mempool_free(response, info->response_mempool); -} - -/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */ -static void idle_connection_timer(struct work_struct *work) -{ - struct smbd_connection *info = container_of( - work, struct smbd_connection, - idle_timer_work.work); - - if (info->keep_alive_requested != KEEP_ALIVE_NONE) { - log_keep_alive(ERR, - "error status info->keep_alive_requested=%d\n", - info->keep_alive_requested); - smbd_disconnect_rdma_connection(info); - return; - } - - log_keep_alive(INFO, "about to send an empty idle message\n"); - smbd_post_send_empty(info); - - /* Setup the next idle timeout work */ - queue_delayed_work(info->workqueue, &info->idle_timer_work, - info->keep_alive_interval*HZ); -} - -/* - * Destroy the transport and related RDMA and memory resources - * Need to go through all the pending counters and make sure on one is using - * the transport while it is destroyed - */ -void smbd_destroy(struct TCP_Server_Info *server) -{ - struct smbd_connection *info = server->smbd_conn; - struct smbd_response *response; - unsigned long flags; - - if (!info) { - log_rdma_event(INFO, "rdma session already destroyed\n"); - return; - } - - log_rdma_event(INFO, "destroying rdma session\n"); - if (info->transport_status != SMBD_DISCONNECTED) { - rdma_disconnect(server->smbd_conn->id); - log_rdma_event(INFO, "wait for transport being disconnected\n"); - wait_event_interruptible( - info->disconn_wait, - info->transport_status == SMBD_DISCONNECTED); - } - - log_rdma_event(INFO, "destroying qp\n"); - ib_drain_qp(info->id->qp); - rdma_destroy_qp(info->id); - - log_rdma_event(INFO, "cancelling idle timer\n"); - cancel_delayed_work_sync(&info->idle_timer_work); - - log_rdma_event(INFO, "wait for all send posted to IB to finish\n"); - wait_event(info->wait_send_pending, - atomic_read(&info->send_pending) == 0); - - /* It's not possible for upper layer to get to reassembly */ - log_rdma_event(INFO, "drain the reassembly queue\n"); - do { - spin_lock_irqsave(&info->reassembly_queue_lock, flags); - response = _get_first_reassembly(info); - if (response) { - list_del(&response->list); - spin_unlock_irqrestore( - &info->reassembly_queue_lock, flags); - put_receive_buffer(info, response); - } else - spin_unlock_irqrestore( - &info->reassembly_queue_lock, flags); - } while (response); - info->reassembly_data_length = 0; - - log_rdma_event(INFO, "free receive buffers\n"); - wait_event(info->wait_receive_queues, - info->count_receive_queue + info->count_empty_packet_queue - == info->receive_credit_max); - destroy_receive_buffers(info); - - /* - * For performance reasons, memory registration and deregistration - * are not locked by srv_mutex. It is possible some processes are - * blocked on transport srv_mutex while holding memory registration. - * Release the transport srv_mutex to allow them to hit the failure - * path when sending data, and then release memory registartions. - */ - log_rdma_event(INFO, "freeing mr list\n"); - wake_up_interruptible_all(&info->wait_mr); - while (atomic_read(&info->mr_used_count)) { - cifs_server_unlock(server); - msleep(1000); - cifs_server_lock(server); - } - destroy_mr_list(info); - - ib_free_cq(info->send_cq); - ib_free_cq(info->recv_cq); - ib_dealloc_pd(info->pd); - rdma_destroy_id(info->id); - - /* free mempools */ - mempool_destroy(info->request_mempool); - kmem_cache_destroy(info->request_cache); - - mempool_destroy(info->response_mempool); - kmem_cache_destroy(info->response_cache); - - info->transport_status = SMBD_DESTROYED; - - destroy_workqueue(info->workqueue); - log_rdma_event(INFO, "rdma session destroyed\n"); - kfree(info); - server->smbd_conn = NULL; -} - -/* - * Reconnect this SMBD connection, called from upper layer - * return value: 0 on success, or actual error code - */ -int smbd_reconnect(struct TCP_Server_Info *server) -{ - log_rdma_event(INFO, "reconnecting rdma session\n"); - - if (!server->smbd_conn) { - log_rdma_event(INFO, "rdma session already destroyed\n"); - goto create_conn; - } - - /* - * This is possible if transport is disconnected and we haven't received - * notification from RDMA, but upper layer has detected timeout - */ - if (server->smbd_conn->transport_status == SMBD_CONNECTED) { - log_rdma_event(INFO, "disconnecting transport\n"); - smbd_destroy(server); - } - -create_conn: - log_rdma_event(INFO, "creating rdma session\n"); - server->smbd_conn = smbd_get_connection( - server, (struct sockaddr *) &server->dstaddr); - - if (server->smbd_conn) - cifs_dbg(VFS, "RDMA transport re-established\n"); - - return server->smbd_conn ? 0 : -ENOENT; -} - -static void destroy_caches_and_workqueue(struct smbd_connection *info) -{ - destroy_receive_buffers(info); - destroy_workqueue(info->workqueue); - mempool_destroy(info->response_mempool); - kmem_cache_destroy(info->response_cache); - mempool_destroy(info->request_mempool); - kmem_cache_destroy(info->request_cache); -} - -#define MAX_NAME_LEN 80 -static int allocate_caches_and_workqueue(struct smbd_connection *info) -{ - char name[MAX_NAME_LEN]; - int rc; - - scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info); - info->request_cache = - kmem_cache_create( - name, - sizeof(struct smbd_request) + - sizeof(struct smbd_data_transfer), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!info->request_cache) - return -ENOMEM; - - info->request_mempool = - mempool_create(info->send_credit_target, mempool_alloc_slab, - mempool_free_slab, info->request_cache); - if (!info->request_mempool) - goto out1; - - scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info); - info->response_cache = - kmem_cache_create( - name, - sizeof(struct smbd_response) + - info->max_receive_size, - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!info->response_cache) - goto out2; - - info->response_mempool = - mempool_create(info->receive_credit_max, mempool_alloc_slab, - mempool_free_slab, info->response_cache); - if (!info->response_mempool) - goto out3; - - scnprintf(name, MAX_NAME_LEN, "smbd_%p", info); - info->workqueue = create_workqueue(name); - if (!info->workqueue) - goto out4; - - rc = allocate_receive_buffers(info, info->receive_credit_max); - if (rc) { - log_rdma_event(ERR, "failed to allocate receive buffers\n"); - goto out5; - } - - return 0; - -out5: - destroy_workqueue(info->workqueue); -out4: - mempool_destroy(info->response_mempool); -out3: - kmem_cache_destroy(info->response_cache); -out2: - mempool_destroy(info->request_mempool); -out1: - kmem_cache_destroy(info->request_cache); - return -ENOMEM; -} - -/* Create a SMBD connection, called by upper layer */ -static struct smbd_connection *_smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port) -{ - int rc; - struct smbd_connection *info; - struct rdma_conn_param conn_param; - struct ib_qp_init_attr qp_attr; - struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr; - struct ib_port_immutable port_immutable; - u32 ird_ord_hdr[2]; - - info = kzalloc(sizeof(struct smbd_connection), GFP_KERNEL); - if (!info) - return NULL; - - info->transport_status = SMBD_CONNECTING; - rc = smbd_ia_open(info, dstaddr, port); - if (rc) { - log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc); - goto create_id_failed; - } - - if (smbd_send_credit_target > info->id->device->attrs.max_cqe || - smbd_send_credit_target > info->id->device->attrs.max_qp_wr) { - log_rdma_event(ERR, "consider lowering send_credit_target = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - smbd_send_credit_target, - info->id->device->attrs.max_cqe, - info->id->device->attrs.max_qp_wr); - goto config_failed; - } - - if (smbd_receive_credit_max > info->id->device->attrs.max_cqe || - smbd_receive_credit_max > info->id->device->attrs.max_qp_wr) { - log_rdma_event(ERR, "consider lowering receive_credit_max = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - smbd_receive_credit_max, - info->id->device->attrs.max_cqe, - info->id->device->attrs.max_qp_wr); - goto config_failed; - } - - info->receive_credit_max = smbd_receive_credit_max; - info->send_credit_target = smbd_send_credit_target; - info->max_send_size = smbd_max_send_size; - info->max_fragmented_recv_size = smbd_max_fragmented_recv_size; - info->max_receive_size = smbd_max_receive_size; - info->keep_alive_interval = smbd_keep_alive_interval; - - if (info->id->device->attrs.max_send_sge < SMBDIRECT_MAX_SEND_SGE || - info->id->device->attrs.max_recv_sge < SMBDIRECT_MAX_RECV_SGE) { - log_rdma_event(ERR, - "device %.*s max_send_sge/max_recv_sge = %d/%d too small\n", - IB_DEVICE_NAME_MAX, - info->id->device->name, - info->id->device->attrs.max_send_sge, - info->id->device->attrs.max_recv_sge); - goto config_failed; - } - - info->send_cq = NULL; - info->recv_cq = NULL; - info->send_cq = - ib_alloc_cq_any(info->id->device, info, - info->send_credit_target, IB_POLL_SOFTIRQ); - if (IS_ERR(info->send_cq)) { - info->send_cq = NULL; - goto alloc_cq_failed; - } - - info->recv_cq = - ib_alloc_cq_any(info->id->device, info, - info->receive_credit_max, IB_POLL_SOFTIRQ); - if (IS_ERR(info->recv_cq)) { - info->recv_cq = NULL; - goto alloc_cq_failed; - } - - memset(&qp_attr, 0, sizeof(qp_attr)); - qp_attr.event_handler = smbd_qp_async_error_upcall; - qp_attr.qp_context = info; - qp_attr.cap.max_send_wr = info->send_credit_target; - qp_attr.cap.max_recv_wr = info->receive_credit_max; - qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SEND_SGE; - qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_RECV_SGE; - qp_attr.cap.max_inline_data = 0; - qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; - qp_attr.qp_type = IB_QPT_RC; - qp_attr.send_cq = info->send_cq; - qp_attr.recv_cq = info->recv_cq; - qp_attr.port_num = ~0; - - rc = rdma_create_qp(info->id, info->pd, &qp_attr); - if (rc) { - log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc); - goto create_qp_failed; - } - - memset(&conn_param, 0, sizeof(conn_param)); - conn_param.initiator_depth = 0; - - conn_param.responder_resources = - info->id->device->attrs.max_qp_rd_atom - < SMBD_CM_RESPONDER_RESOURCES ? - info->id->device->attrs.max_qp_rd_atom : - SMBD_CM_RESPONDER_RESOURCES; - info->responder_resources = conn_param.responder_resources; - log_rdma_mr(INFO, "responder_resources=%d\n", - info->responder_resources); - - /* Need to send IRD/ORD in private data for iWARP */ - info->id->device->ops.get_port_immutable( - info->id->device, info->id->port_num, &port_immutable); - if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { - ird_ord_hdr[0] = info->responder_resources; - ird_ord_hdr[1] = 1; - conn_param.private_data = ird_ord_hdr; - conn_param.private_data_len = sizeof(ird_ord_hdr); - } else { - conn_param.private_data = NULL; - conn_param.private_data_len = 0; - } - - conn_param.retry_count = SMBD_CM_RETRY; - conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY; - conn_param.flow_control = 0; - - log_rdma_event(INFO, "connecting to IP %pI4 port %d\n", - &addr_in->sin_addr, port); - - init_waitqueue_head(&info->conn_wait); - init_waitqueue_head(&info->disconn_wait); - init_waitqueue_head(&info->wait_reassembly_queue); - rc = rdma_connect(info->id, &conn_param); - if (rc) { - log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc); - goto rdma_connect_failed; - } - - wait_event_interruptible( - info->conn_wait, info->transport_status != SMBD_CONNECTING); - - if (info->transport_status != SMBD_CONNECTED) { - log_rdma_event(ERR, "rdma_connect failed port=%d\n", port); - goto rdma_connect_failed; - } - - log_rdma_event(INFO, "rdma_connect connected\n"); - - rc = allocate_caches_and_workqueue(info); - if (rc) { - log_rdma_event(ERR, "cache allocation failed\n"); - goto allocate_cache_failed; - } - - init_waitqueue_head(&info->wait_send_queue); - INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer); - queue_delayed_work(info->workqueue, &info->idle_timer_work, - info->keep_alive_interval*HZ); - - init_waitqueue_head(&info->wait_send_pending); - atomic_set(&info->send_pending, 0); - - init_waitqueue_head(&info->wait_post_send); - - INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work); - INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits); - info->new_credits_offered = 0; - spin_lock_init(&info->lock_new_credits_offered); - - rc = smbd_negotiate(info); - if (rc) { - log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc); - goto negotiation_failed; - } - - rc = allocate_mr_list(info); - if (rc) { - log_rdma_mr(ERR, "memory registration allocation failed\n"); - goto allocate_mr_failed; - } - - return info; - -allocate_mr_failed: - /* At this point, need to a full transport shutdown */ - server->smbd_conn = info; - smbd_destroy(server); - return NULL; - -negotiation_failed: - cancel_delayed_work_sync(&info->idle_timer_work); - destroy_caches_and_workqueue(info); - info->transport_status = SMBD_NEGOTIATE_FAILED; - init_waitqueue_head(&info->conn_wait); - rdma_disconnect(info->id); - wait_event(info->conn_wait, - info->transport_status == SMBD_DISCONNECTED); - -allocate_cache_failed: -rdma_connect_failed: - rdma_destroy_qp(info->id); - -create_qp_failed: -alloc_cq_failed: - if (info->send_cq) - ib_free_cq(info->send_cq); - if (info->recv_cq) - ib_free_cq(info->recv_cq); - -config_failed: - ib_dealloc_pd(info->pd); - rdma_destroy_id(info->id); - -create_id_failed: - kfree(info); - return NULL; -} - -struct smbd_connection *smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr) -{ - struct smbd_connection *ret; - int port = SMBD_PORT; - -try_again: - ret = _smbd_get_connection(server, dstaddr, port); - - /* Try SMB_PORT if SMBD_PORT doesn't work */ - if (!ret && port == SMBD_PORT) { - port = SMB_PORT; - goto try_again; - } - return ret; -} - -/* - * Receive data from receive reassembly queue - * All the incoming data packets are placed in reassembly queue - * buf: the buffer to read data into - * size: the length of data to read - * return value: actual data read - * Note: this implementation copies the data from reassebmly queue to receive - * buffers used by upper layer. This is not the optimal code path. A better way - * to do it is to not have upper layer allocate its receive buffers but rather - * borrow the buffer from reassembly queue, and return it after data is - * consumed. But this will require more changes to upper layer code, and also - * need to consider packet boundaries while they still being reassembled. - */ -static int smbd_recv_buf(struct smbd_connection *info, char *buf, - unsigned int size) -{ - struct smbd_response *response; - struct smbd_data_transfer *data_transfer; - int to_copy, to_read, data_read, offset; - u32 data_length, remaining_data_length, data_offset; - int rc; - -again: - /* - * No need to hold the reassembly queue lock all the time as we are - * the only one reading from the front of the queue. The transport - * may add more entries to the back of the queue at the same time - */ - log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size, - info->reassembly_data_length); - if (info->reassembly_data_length >= size) { - int queue_length; - int queue_removed = 0; - - /* - * Need to make sure reassembly_data_length is read before - * reading reassembly_queue_length and calling - * _get_first_reassembly. This call is lock free - * as we never read at the end of the queue which are being - * updated in SOFTIRQ as more data is received - */ - virt_rmb(); - queue_length = info->reassembly_queue_length; - data_read = 0; - to_read = size; - offset = info->first_entry_offset; - while (data_read < size) { - response = _get_first_reassembly(info); - data_transfer = smbd_response_payload(response); - data_length = le32_to_cpu(data_transfer->data_length); - remaining_data_length = - le32_to_cpu( - data_transfer->remaining_data_length); - data_offset = le32_to_cpu(data_transfer->data_offset); - - /* - * The upper layer expects RFC1002 length at the - * beginning of the payload. Return it to indicate - * the total length of the packet. This minimize the - * change to upper layer packet processing logic. This - * will be eventually remove when an intermediate - * transport layer is added - */ - if (response->first_segment && size == 4) { - unsigned int rfc1002_len = - data_length + remaining_data_length; - *((__be32 *)buf) = cpu_to_be32(rfc1002_len); - data_read = 4; - response->first_segment = false; - log_read(INFO, "returning rfc1002 length %d\n", - rfc1002_len); - goto read_rfc1002_done; - } - - to_copy = min_t(int, data_length - offset, to_read); - memcpy( - buf + data_read, - (char *)data_transfer + data_offset + offset, - to_copy); - - /* move on to the next buffer? */ - if (to_copy == data_length - offset) { - queue_length--; - /* - * No need to lock if we are not at the - * end of the queue - */ - if (queue_length) - list_del(&response->list); - else { - spin_lock_irq( - &info->reassembly_queue_lock); - list_del(&response->list); - spin_unlock_irq( - &info->reassembly_queue_lock); - } - queue_removed++; - info->count_reassembly_queue--; - info->count_dequeue_reassembly_queue++; - put_receive_buffer(info, response); - offset = 0; - log_read(INFO, "put_receive_buffer offset=0\n"); - } else - offset += to_copy; - - to_read -= to_copy; - data_read += to_copy; - - log_read(INFO, "_get_first_reassembly memcpy %d bytes data_transfer_length-offset=%d after that to_read=%d data_read=%d offset=%d\n", - to_copy, data_length - offset, - to_read, data_read, offset); - } - - spin_lock_irq(&info->reassembly_queue_lock); - info->reassembly_data_length -= data_read; - info->reassembly_queue_length -= queue_removed; - spin_unlock_irq(&info->reassembly_queue_lock); - - info->first_entry_offset = offset; - log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", - data_read, info->reassembly_data_length, - info->first_entry_offset); -read_rfc1002_done: - return data_read; - } - - log_read(INFO, "wait_event on more data\n"); - rc = wait_event_interruptible( - info->wait_reassembly_queue, - info->reassembly_data_length >= size || - info->transport_status != SMBD_CONNECTED); - /* Don't return any data if interrupted */ - if (rc) - return rc; - - if (info->transport_status != SMBD_CONNECTED) { - log_read(ERR, "disconnected\n"); - return -ECONNABORTED; - } - - goto again; -} - -/* - * Receive a page from receive reassembly queue - * page: the page to read data into - * to_read: the length of data to read - * return value: actual data read - */ -static int smbd_recv_page(struct smbd_connection *info, - struct page *page, unsigned int page_offset, - unsigned int to_read) -{ - int ret; - char *to_address; - void *page_address; - - /* make sure we have the page ready for read */ - ret = wait_event_interruptible( - info->wait_reassembly_queue, - info->reassembly_data_length >= to_read || - info->transport_status != SMBD_CONNECTED); - if (ret) - return ret; - - /* now we can read from reassembly queue and not sleep */ - page_address = kmap_atomic(page); - to_address = (char *) page_address + page_offset; - - log_read(INFO, "reading from page=%p address=%p to_read=%d\n", - page, to_address, to_read); - - ret = smbd_recv_buf(info, to_address, to_read); - kunmap_atomic(page_address); - - return ret; -} - -/* - * Receive data from transport - * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC - * return: total bytes read, or 0. SMB Direct will not do partial read. - */ -int smbd_recv(struct smbd_connection *info, struct msghdr *msg) -{ - char *buf; - struct page *page; - unsigned int to_read, page_offset; - int rc; - - if (iov_iter_rw(&msg->msg_iter) == WRITE) { - /* It's a bug in upper layer to get there */ - cifs_dbg(VFS, "Invalid msg iter dir %u\n", - iov_iter_rw(&msg->msg_iter)); - rc = -EINVAL; - goto out; - } - - switch (iov_iter_type(&msg->msg_iter)) { - case ITER_KVEC: - buf = msg->msg_iter.kvec->iov_base; - to_read = msg->msg_iter.kvec->iov_len; - rc = smbd_recv_buf(info, buf, to_read); - break; - - case ITER_BVEC: - page = msg->msg_iter.bvec->bv_page; - page_offset = msg->msg_iter.bvec->bv_offset; - to_read = msg->msg_iter.bvec->bv_len; - rc = smbd_recv_page(info, page, page_offset, to_read); - break; - - default: - /* It's a bug in upper layer to get there */ - cifs_dbg(VFS, "Invalid msg type %d\n", - iov_iter_type(&msg->msg_iter)); - rc = -EINVAL; - } - -out: - /* SMBDirect will read it all or nothing */ - if (rc > 0) - msg->msg_iter.count = 0; - return rc; -} - -/* - * Send data to transport - * Each rqst is transported as a SMBDirect payload - * rqst: the data to write - * return value: 0 if successfully write, otherwise error code - */ -int smbd_send(struct TCP_Server_Info *server, - int num_rqst, struct smb_rqst *rqst_array) -{ - struct smbd_connection *info = server->smbd_conn; - struct kvec vecs[SMBDIRECT_MAX_SEND_SGE - 1]; - int nvecs; - int size; - unsigned int buflen, remaining_data_length; - unsigned int offset, remaining_vec_data_length; - int start, i, j; - int max_iov_size = - info->max_send_size - sizeof(struct smbd_data_transfer); - struct kvec *iov; - int rc; - struct smb_rqst *rqst; - int rqst_idx; - - if (info->transport_status != SMBD_CONNECTED) - return -EAGAIN; - - /* - * Add in the page array if there is one. The caller needs to set - * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and - * ends at page boundary - */ - remaining_data_length = 0; - for (i = 0; i < num_rqst; i++) - remaining_data_length += smb_rqst_len(server, &rqst_array[i]); - - if (unlikely(remaining_data_length > info->max_fragmented_send_size)) { - /* assertion: payload never exceeds negotiated maximum */ - log_write(ERR, "payload size %d > max size %d\n", - remaining_data_length, info->max_fragmented_send_size); - return -EINVAL; - } - - log_write(INFO, "num_rqst=%d total length=%u\n", - num_rqst, remaining_data_length); - - rqst_idx = 0; - do { - rqst = &rqst_array[rqst_idx]; - iov = rqst->rq_iov; - - cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n", - rqst_idx, smb_rqst_len(server, rqst)); - remaining_vec_data_length = 0; - for (i = 0; i < rqst->rq_nvec; i++) { - remaining_vec_data_length += iov[i].iov_len; - dump_smb(iov[i].iov_base, iov[i].iov_len); - } - - log_write(INFO, "rqst_idx=%d nvec=%d rqst->rq_npages=%d rq_pagesz=%d rq_tailsz=%d buflen=%lu\n", - rqst_idx, rqst->rq_nvec, - rqst->rq_npages, rqst->rq_pagesz, - rqst->rq_tailsz, smb_rqst_len(server, rqst)); - - start = 0; - offset = 0; - do { - buflen = 0; - i = start; - j = 0; - while (i < rqst->rq_nvec && - j < SMBDIRECT_MAX_SEND_SGE - 1 && - buflen < max_iov_size) { - - vecs[j].iov_base = iov[i].iov_base + offset; - if (buflen + iov[i].iov_len > max_iov_size) { - vecs[j].iov_len = - max_iov_size - iov[i].iov_len; - buflen = max_iov_size; - offset = vecs[j].iov_len; - } else { - vecs[j].iov_len = - iov[i].iov_len - offset; - buflen += vecs[j].iov_len; - offset = 0; - ++i; - } - ++j; - } - - remaining_vec_data_length -= buflen; - remaining_data_length -= buflen; - log_write(INFO, "sending %s iov[%d] from start=%d nvecs=%d remaining_data_length=%d\n", - remaining_vec_data_length > 0 ? - "partial" : "complete", - rqst->rq_nvec, start, j, - remaining_data_length); - - start = i; - rc = smbd_post_send_data(info, vecs, j, remaining_data_length); - if (rc) - goto done; - } while (remaining_vec_data_length > 0); - - /* now sending pages if there are any */ - for (i = 0; i < rqst->rq_npages; i++) { - rqst_page_get_length(rqst, i, &buflen, &offset); - nvecs = (buflen + max_iov_size - 1) / max_iov_size; - log_write(INFO, "sending pages buflen=%d nvecs=%d\n", - buflen, nvecs); - for (j = 0; j < nvecs; j++) { - size = min_t(unsigned int, max_iov_size, remaining_data_length); - remaining_data_length -= size; - log_write(INFO, "sending pages i=%d offset=%d size=%d remaining_data_length=%d\n", - i, j * max_iov_size + offset, size, - remaining_data_length); - rc = smbd_post_send_page( - info, rqst->rq_pages[i], - j*max_iov_size + offset, - size, remaining_data_length); - if (rc) - goto done; - } - } - } while (++rqst_idx < num_rqst); - -done: - /* - * As an optimization, we don't wait for individual I/O to finish - * before sending the next one. - * Send them all and wait for pending send count to get to 0 - * that means all the I/Os have been out and we are good to return - */ - - wait_event(info->wait_send_pending, - atomic_read(&info->send_pending) == 0); - - return rc; -} - -static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smbd_mr *mr; - struct ib_cqe *cqe; - - if (wc->status) { - log_rdma_mr(ERR, "status=%d\n", wc->status); - cqe = wc->wr_cqe; - mr = container_of(cqe, struct smbd_mr, cqe); - smbd_disconnect_rdma_connection(mr->conn); - } -} - -/* - * The work queue function that recovers MRs - * We need to call ib_dereg_mr() and ib_alloc_mr() before this MR can be used - * again. Both calls are slow, so finish them in a workqueue. This will not - * block I/O path. - * There is one workqueue that recovers MRs, there is no need to lock as the - * I/O requests calling smbd_register_mr will never update the links in the - * mr_list. - */ -static void smbd_mr_recovery_work(struct work_struct *work) -{ - struct smbd_connection *info = - container_of(work, struct smbd_connection, mr_recovery_work); - struct smbd_mr *smbdirect_mr; - int rc; - - list_for_each_entry(smbdirect_mr, &info->mr_list, list) { - if (smbdirect_mr->state == MR_ERROR) { - - /* recover this MR entry */ - rc = ib_dereg_mr(smbdirect_mr->mr); - if (rc) { - log_rdma_mr(ERR, - "ib_dereg_mr failed rc=%x\n", - rc); - smbd_disconnect_rdma_connection(info); - continue; - } - - smbdirect_mr->mr = ib_alloc_mr( - info->pd, info->mr_type, - info->max_frmr_depth); - if (IS_ERR(smbdirect_mr->mr)) { - log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", - info->mr_type, - info->max_frmr_depth); - smbd_disconnect_rdma_connection(info); - continue; - } - } else - /* This MR is being used, don't recover it */ - continue; - - smbdirect_mr->state = MR_READY; - - /* smbdirect_mr->state is updated by this function - * and is read and updated by I/O issuing CPUs trying - * to get a MR, the call to atomic_inc_return - * implicates a memory barrier and guarantees this - * value is updated before waking up any calls to - * get_mr() from the I/O issuing CPUs - */ - if (atomic_inc_return(&info->mr_ready_count) == 1) - wake_up_interruptible(&info->wait_mr); - } -} - -static void destroy_mr_list(struct smbd_connection *info) -{ - struct smbd_mr *mr, *tmp; - - cancel_work_sync(&info->mr_recovery_work); - list_for_each_entry_safe(mr, tmp, &info->mr_list, list) { - if (mr->state == MR_INVALIDATED) - ib_dma_unmap_sg(info->id->device, mr->sgl, - mr->sgl_count, mr->dir); - ib_dereg_mr(mr->mr); - kfree(mr->sgl); - kfree(mr); - } -} - -/* - * Allocate MRs used for RDMA read/write - * The number of MRs will not exceed hardware capability in responder_resources - * All MRs are kept in mr_list. The MR can be recovered after it's used - * Recovery is done in smbd_mr_recovery_work. The content of list entry changes - * as MRs are used and recovered for I/O, but the list links will not change - */ -static int allocate_mr_list(struct smbd_connection *info) -{ - int i; - struct smbd_mr *smbdirect_mr, *tmp; - - INIT_LIST_HEAD(&info->mr_list); - init_waitqueue_head(&info->wait_mr); - spin_lock_init(&info->mr_list_lock); - atomic_set(&info->mr_ready_count, 0); - atomic_set(&info->mr_used_count, 0); - init_waitqueue_head(&info->wait_for_mr_cleanup); - INIT_WORK(&info->mr_recovery_work, smbd_mr_recovery_work); - /* Allocate more MRs (2x) than hardware responder_resources */ - for (i = 0; i < info->responder_resources * 2; i++) { - smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL); - if (!smbdirect_mr) - goto out; - smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type, - info->max_frmr_depth); - if (IS_ERR(smbdirect_mr->mr)) { - log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", - info->mr_type, info->max_frmr_depth); - goto out; - } - smbdirect_mr->sgl = kcalloc( - info->max_frmr_depth, - sizeof(struct scatterlist), - GFP_KERNEL); - if (!smbdirect_mr->sgl) { - log_rdma_mr(ERR, "failed to allocate sgl\n"); - ib_dereg_mr(smbdirect_mr->mr); - goto out; - } - smbdirect_mr->state = MR_READY; - smbdirect_mr->conn = info; - - list_add_tail(&smbdirect_mr->list, &info->mr_list); - atomic_inc(&info->mr_ready_count); - } - return 0; - -out: - kfree(smbdirect_mr); - - list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) { - list_del(&smbdirect_mr->list); - ib_dereg_mr(smbdirect_mr->mr); - kfree(smbdirect_mr->sgl); - kfree(smbdirect_mr); - } - return -ENOMEM; -} - -/* - * Get a MR from mr_list. This function waits until there is at least one - * MR available in the list. It may access the list while the - * smbd_mr_recovery_work is recovering the MR list. This doesn't need a lock - * as they never modify the same places. However, there may be several CPUs - * issueing I/O trying to get MR at the same time, mr_list_lock is used to - * protect this situation. - */ -static struct smbd_mr *get_mr(struct smbd_connection *info) -{ - struct smbd_mr *ret; - int rc; -again: - rc = wait_event_interruptible(info->wait_mr, - atomic_read(&info->mr_ready_count) || - info->transport_status != SMBD_CONNECTED); - if (rc) { - log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc); - return NULL; - } - - if (info->transport_status != SMBD_CONNECTED) { - log_rdma_mr(ERR, "info->transport_status=%x\n", - info->transport_status); - return NULL; - } - - spin_lock(&info->mr_list_lock); - list_for_each_entry(ret, &info->mr_list, list) { - if (ret->state == MR_READY) { - ret->state = MR_REGISTERED; - spin_unlock(&info->mr_list_lock); - atomic_dec(&info->mr_ready_count); - atomic_inc(&info->mr_used_count); - return ret; - } - } - - spin_unlock(&info->mr_list_lock); - /* - * It is possible that we could fail to get MR because other processes may - * try to acquire a MR at the same time. If this is the case, retry it. - */ - goto again; -} - -/* - * Register memory for RDMA read/write - * pages[]: the list of pages to register memory with - * num_pages: the number of pages to register - * tailsz: if non-zero, the bytes to register in the last page - * writing: true if this is a RDMA write (SMB read), false for RDMA read - * need_invalidate: true if this MR needs to be locally invalidated after I/O - * return value: the MR registered, NULL if failed. - */ -struct smbd_mr *smbd_register_mr( - struct smbd_connection *info, struct page *pages[], int num_pages, - int offset, int tailsz, bool writing, bool need_invalidate) -{ - struct smbd_mr *smbdirect_mr; - int rc, i; - enum dma_data_direction dir; - struct ib_reg_wr *reg_wr; - - if (num_pages > info->max_frmr_depth) { - log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n", - num_pages, info->max_frmr_depth); - return NULL; - } - - smbdirect_mr = get_mr(info); - if (!smbdirect_mr) { - log_rdma_mr(ERR, "get_mr returning NULL\n"); - return NULL; - } - smbdirect_mr->need_invalidate = need_invalidate; - smbdirect_mr->sgl_count = num_pages; - sg_init_table(smbdirect_mr->sgl, num_pages); - - log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n", - num_pages, offset, tailsz); - - if (num_pages == 1) { - sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset); - goto skip_multiple_pages; - } - - /* We have at least two pages to register */ - sg_set_page( - &smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset); - i = 1; - while (i < num_pages - 1) { - sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0); - i++; - } - sg_set_page(&smbdirect_mr->sgl[i], pages[i], - tailsz ? tailsz : PAGE_SIZE, 0); - -skip_multiple_pages: - dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - smbdirect_mr->dir = dir; - rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir); - if (!rc) { - log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n", - num_pages, dir, rc); - goto dma_map_error; - } - - rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages, - NULL, PAGE_SIZE); - if (rc != num_pages) { - log_rdma_mr(ERR, - "ib_map_mr_sg failed rc = %d num_pages = %x\n", - rc, num_pages); - goto map_mr_error; - } - - ib_update_fast_reg_key(smbdirect_mr->mr, - ib_inc_rkey(smbdirect_mr->mr->rkey)); - reg_wr = &smbdirect_mr->wr; - reg_wr->wr.opcode = IB_WR_REG_MR; - smbdirect_mr->cqe.done = register_mr_done; - reg_wr->wr.wr_cqe = &smbdirect_mr->cqe; - reg_wr->wr.num_sge = 0; - reg_wr->wr.send_flags = IB_SEND_SIGNALED; - reg_wr->mr = smbdirect_mr->mr; - reg_wr->key = smbdirect_mr->mr->rkey; - reg_wr->access = writing ? - IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE : - IB_ACCESS_REMOTE_READ; - - /* - * There is no need for waiting for complemtion on ib_post_send - * on IB_WR_REG_MR. Hardware enforces a barrier and order of execution - * on the next ib_post_send when we actaully send I/O to remote peer - */ - rc = ib_post_send(info->id->qp, ®_wr->wr, NULL); - if (!rc) - return smbdirect_mr; - - log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n", - rc, reg_wr->key); - - /* If all failed, attempt to recover this MR by setting it MR_ERROR*/ -map_mr_error: - ib_dma_unmap_sg(info->id->device, smbdirect_mr->sgl, - smbdirect_mr->sgl_count, smbdirect_mr->dir); - -dma_map_error: - smbdirect_mr->state = MR_ERROR; - if (atomic_dec_and_test(&info->mr_used_count)) - wake_up(&info->wait_for_mr_cleanup); - - smbd_disconnect_rdma_connection(info); - - return NULL; -} - -static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smbd_mr *smbdirect_mr; - struct ib_cqe *cqe; - - cqe = wc->wr_cqe; - smbdirect_mr = container_of(cqe, struct smbd_mr, cqe); - smbdirect_mr->state = MR_INVALIDATED; - if (wc->status != IB_WC_SUCCESS) { - log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status); - smbdirect_mr->state = MR_ERROR; - } - complete(&smbdirect_mr->invalidate_done); -} - -/* - * Deregister a MR after I/O is done - * This function may wait if remote invalidation is not used - * and we have to locally invalidate the buffer to prevent data is being - * modified by remote peer after upper layer consumes it - */ -int smbd_deregister_mr(struct smbd_mr *smbdirect_mr) -{ - struct ib_send_wr *wr; - struct smbd_connection *info = smbdirect_mr->conn; - int rc = 0; - - if (smbdirect_mr->need_invalidate) { - /* Need to finish local invalidation before returning */ - wr = &smbdirect_mr->inv_wr; - wr->opcode = IB_WR_LOCAL_INV; - smbdirect_mr->cqe.done = local_inv_done; - wr->wr_cqe = &smbdirect_mr->cqe; - wr->num_sge = 0; - wr->ex.invalidate_rkey = smbdirect_mr->mr->rkey; - wr->send_flags = IB_SEND_SIGNALED; - - init_completion(&smbdirect_mr->invalidate_done); - rc = ib_post_send(info->id->qp, wr, NULL); - if (rc) { - log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc); - smbd_disconnect_rdma_connection(info); - goto done; - } - wait_for_completion(&smbdirect_mr->invalidate_done); - smbdirect_mr->need_invalidate = false; - } else - /* - * For remote invalidation, just set it to MR_INVALIDATED - * and defer to mr_recovery_work to recover the MR for next use - */ - smbdirect_mr->state = MR_INVALIDATED; - - if (smbdirect_mr->state == MR_INVALIDATED) { - ib_dma_unmap_sg( - info->id->device, smbdirect_mr->sgl, - smbdirect_mr->sgl_count, - smbdirect_mr->dir); - smbdirect_mr->state = MR_READY; - if (atomic_inc_return(&info->mr_ready_count) == 1) - wake_up_interruptible(&info->wait_mr); - } else - /* - * Schedule the work to do MR recovery for future I/Os MR - * recovery is slow and don't want it to block current I/O - */ - queue_work(info->workqueue, &info->mr_recovery_work); - -done: - if (atomic_dec_and_test(&info->mr_used_count)) - wake_up(&info->wait_for_mr_cleanup); - - return rc; -} diff --git a/fs/cifs/smbdirect.h b/fs/cifs/smbdirect.h deleted file mode 100644 index 207ef979cd51..000000000000 --- a/fs/cifs/smbdirect.h +++ /dev/null @@ -1,320 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2017, Microsoft Corporation. - * - * Author(s): Long Li - */ -#ifndef _SMBDIRECT_H -#define _SMBDIRECT_H - -#ifdef CONFIG_CIFS_SMB_DIRECT -#define cifs_rdma_enabled(server) ((server)->rdma) - -#include "cifsglob.h" -#include -#include -#include - -extern int rdma_readwrite_threshold; -extern int smbd_max_frmr_depth; -extern int smbd_keep_alive_interval; -extern int smbd_max_receive_size; -extern int smbd_max_fragmented_recv_size; -extern int smbd_max_send_size; -extern int smbd_send_credit_target; -extern int smbd_receive_credit_max; - -enum keep_alive_status { - KEEP_ALIVE_NONE, - KEEP_ALIVE_PENDING, - KEEP_ALIVE_SENT, -}; - -enum smbd_connection_status { - SMBD_CREATED, - SMBD_CONNECTING, - SMBD_CONNECTED, - SMBD_NEGOTIATE_FAILED, - SMBD_DISCONNECTING, - SMBD_DISCONNECTED, - SMBD_DESTROYED -}; - -/* - * The context for the SMBDirect transport - * Everything related to the transport is here. It has several logical parts - * 1. RDMA related structures - * 2. SMBDirect connection parameters - * 3. Memory registrations - * 4. Receive and reassembly queues for data receive path - * 5. mempools for allocating packets - */ -struct smbd_connection { - enum smbd_connection_status transport_status; - - /* RDMA related */ - struct rdma_cm_id *id; - struct ib_qp_init_attr qp_attr; - struct ib_pd *pd; - struct ib_cq *send_cq, *recv_cq; - struct ib_device_attr dev_attr; - int ri_rc; - struct completion ri_done; - wait_queue_head_t conn_wait; - wait_queue_head_t disconn_wait; - - struct completion negotiate_completion; - bool negotiate_done; - - struct work_struct disconnect_work; - struct work_struct post_send_credits_work; - - spinlock_t lock_new_credits_offered; - int new_credits_offered; - - /* Connection parameters defined in [MS-SMBD] 3.1.1.1 */ - int receive_credit_max; - int send_credit_target; - int max_send_size; - int max_fragmented_recv_size; - int max_fragmented_send_size; - int max_receive_size; - int keep_alive_interval; - int max_readwrite_size; - enum keep_alive_status keep_alive_requested; - int protocol; - atomic_t send_credits; - atomic_t receive_credits; - int receive_credit_target; - int fragment_reassembly_remaining; - - /* Memory registrations */ - /* Maximum number of RDMA read/write outstanding on this connection */ - int responder_resources; - /* Maximum number of pages in a single RDMA write/read on this connection */ - int max_frmr_depth; - /* - * If payload is less than or equal to the threshold, - * use RDMA send/recv to send upper layer I/O. - * If payload is more than the threshold, - * use RDMA read/write through memory registration for I/O. - */ - int rdma_readwrite_threshold; - enum ib_mr_type mr_type; - struct list_head mr_list; - spinlock_t mr_list_lock; - /* The number of available MRs ready for memory registration */ - atomic_t mr_ready_count; - atomic_t mr_used_count; - wait_queue_head_t wait_mr; - struct work_struct mr_recovery_work; - /* Used by transport to wait until all MRs are returned */ - wait_queue_head_t wait_for_mr_cleanup; - - /* Activity accoutning */ - atomic_t send_pending; - wait_queue_head_t wait_send_pending; - wait_queue_head_t wait_post_send; - - /* Receive queue */ - struct list_head receive_queue; - int count_receive_queue; - spinlock_t receive_queue_lock; - - struct list_head empty_packet_queue; - int count_empty_packet_queue; - spinlock_t empty_packet_queue_lock; - - wait_queue_head_t wait_receive_queues; - - /* Reassembly queue */ - struct list_head reassembly_queue; - spinlock_t reassembly_queue_lock; - wait_queue_head_t wait_reassembly_queue; - - /* total data length of reassembly queue */ - int reassembly_data_length; - int reassembly_queue_length; - /* the offset to first buffer in reassembly queue */ - int first_entry_offset; - - bool send_immediate; - - wait_queue_head_t wait_send_queue; - - /* - * Indicate if we have received a full packet on the connection - * This is used to identify the first SMBD packet of a assembled - * payload (SMB packet) in reassembly queue so we can return a - * RFC1002 length to upper layer to indicate the length of the SMB - * packet received - */ - bool full_packet_received; - - struct workqueue_struct *workqueue; - struct delayed_work idle_timer_work; - - /* Memory pool for preallocating buffers */ - /* request pool for RDMA send */ - struct kmem_cache *request_cache; - mempool_t *request_mempool; - - /* response pool for RDMA receive */ - struct kmem_cache *response_cache; - mempool_t *response_mempool; - - /* for debug purposes */ - unsigned int count_get_receive_buffer; - unsigned int count_put_receive_buffer; - unsigned int count_reassembly_queue; - unsigned int count_enqueue_reassembly_queue; - unsigned int count_dequeue_reassembly_queue; - unsigned int count_send_empty; -}; - -enum smbd_message_type { - SMBD_NEGOTIATE_RESP, - SMBD_TRANSFER_DATA, -}; - -#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 - -/* SMBD negotiation request packet [MS-SMBD] 2.2.1 */ -struct smbd_negotiate_req { - __le16 min_version; - __le16 max_version; - __le16 reserved; - __le16 credits_requested; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -/* SMBD negotiation response packet [MS-SMBD] 2.2.2 */ -struct smbd_negotiate_resp { - __le16 min_version; - __le16 max_version; - __le16 negotiated_version; - __le16 reserved; - __le16 credits_requested; - __le16 credits_granted; - __le32 status; - __le32 max_readwrite_size; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -/* SMBD data transfer packet with payload [MS-SMBD] 2.2.3 */ -struct smbd_data_transfer { - __le16 credits_requested; - __le16 credits_granted; - __le16 flags; - __le16 reserved; - __le32 remaining_data_length; - __le32 data_offset; - __le32 data_length; - __le32 padding; - __u8 buffer[]; -} __packed; - -/* The packet fields for a registered RDMA buffer */ -struct smbd_buffer_descriptor_v1 { - __le64 offset; - __le32 token; - __le32 length; -} __packed; - -/* Maximum number of SGEs used by smbdirect.c in any send work request */ -#define SMBDIRECT_MAX_SEND_SGE 6 - -/* The context for a SMBD request */ -struct smbd_request { - struct smbd_connection *info; - struct ib_cqe cqe; - - /* the SGE entries for this work request */ - struct ib_sge sge[SMBDIRECT_MAX_SEND_SGE]; - int num_sge; - - /* SMBD packet header follows this structure */ - u8 packet[]; -}; - -/* Maximum number of SGEs used by smbdirect.c in any receive work request */ -#define SMBDIRECT_MAX_RECV_SGE 1 - -/* The context for a SMBD response */ -struct smbd_response { - struct smbd_connection *info; - struct ib_cqe cqe; - struct ib_sge sge; - - enum smbd_message_type type; - - /* Link to receive queue or reassembly queue */ - struct list_head list; - - /* Indicate if this is the 1st packet of a payload */ - bool first_segment; - - /* SMBD packet header and payload follows this structure */ - u8 packet[]; -}; - -/* Create a SMBDirect session */ -struct smbd_connection *smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr); - -/* Reconnect SMBDirect session */ -int smbd_reconnect(struct TCP_Server_Info *server); -/* Destroy SMBDirect session */ -void smbd_destroy(struct TCP_Server_Info *server); - -/* Interface for carrying upper layer I/O through send/recv */ -int smbd_recv(struct smbd_connection *info, struct msghdr *msg); -int smbd_send(struct TCP_Server_Info *server, - int num_rqst, struct smb_rqst *rqst); - -enum mr_state { - MR_READY, - MR_REGISTERED, - MR_INVALIDATED, - MR_ERROR -}; - -struct smbd_mr { - struct smbd_connection *conn; - struct list_head list; - enum mr_state state; - struct ib_mr *mr; - struct scatterlist *sgl; - int sgl_count; - enum dma_data_direction dir; - union { - struct ib_reg_wr wr; - struct ib_send_wr inv_wr; - }; - struct ib_cqe cqe; - bool need_invalidate; - struct completion invalidate_done; -}; - -/* Interfaces to register and deregister MR for RDMA read/write */ -struct smbd_mr *smbd_register_mr( - struct smbd_connection *info, struct page *pages[], int num_pages, - int offset, int tailsz, bool writing, bool need_invalidate); -int smbd_deregister_mr(struct smbd_mr *mr); - -#else -#define cifs_rdma_enabled(server) 0 -struct smbd_connection {}; -static inline void *smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr) {return NULL;} -static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; } -static inline void smbd_destroy(struct TCP_Server_Info *server) {} -static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; } -static inline int smbd_send(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst) {return -1; } -#endif - -#endif diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c deleted file mode 100644 index 4a0487753869..000000000000 --- a/fs/cifs/smbencrypt.c +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - Unix SMB/Netbios implementation. - Version 1.9. - SMB parameters and setup - Copyright (C) Andrew Tridgell 1992-2000 - Copyright (C) Luke Kenneth Casson Leighton 1996-2000 - Modified by Jeremy Allison 1995. - Copyright (C) Andrew Bartlett 2002-2003 - Modified by Steve French (sfrench@us.ibm.com) 2002-2003 - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifsproto.h" -#include "../smbfs_common/md4.h" - -#ifndef false -#define false 0 -#endif -#ifndef true -#define true 1 -#endif - -/* following came from the other byteorder.h to avoid include conflicts */ -#define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) -#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8) -#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((__u16)(val))) - -/* produce a md4 message digest from data of length n bytes */ -static int -mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len) -{ - int rc; - struct md4_ctx mctx; - - rc = cifs_md4_init(&mctx); - if (rc) { - cifs_dbg(VFS, "%s: Could not init MD4\n", __func__); - goto mdfour_err; - } - rc = cifs_md4_update(&mctx, link_str, link_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update MD4\n", __func__); - goto mdfour_err; - } - rc = cifs_md4_final(&mctx, md4_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not finalize MD4\n", __func__); - - -mdfour_err: - return rc; -} - -/* - * Creates the MD4 Hash of the users password in NT UNICODE. - */ - -int -E_md4hash(const unsigned char *passwd, unsigned char *p16, - const struct nls_table *codepage) -{ - int rc; - int len; - __le16 wpwd[129]; - - /* Password cannot be longer than 128 characters */ - if (passwd) /* Password must be converted to NT unicode */ - len = cifs_strtoUTF16(wpwd, passwd, 128, codepage); - else { - len = 0; - *wpwd = 0; /* Ensure string is null terminated */ - } - - rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__le16)); - memzero_explicit(wpwd, sizeof(wpwd)); - - return rc; -} diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h deleted file mode 100644 index aeffdad829e2..000000000000 --- a/fs/cifs/smberr.h +++ /dev/null @@ -1,171 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2004 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * See Error Codes section of the SNIA CIFS Specification - * for more information - * - */ - -#define SUCCESS 0x00 /* The request was successful. */ -#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ -#define ERRSRV 0x02 /* Error is generated by the file server daemon */ -#define ERRHRD 0x03 /* Error is a hardware error. */ -#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ - -/* The following error codes may be generated with the SUCCESS error class.*/ - -/*#define SUCCESS 0 The request was successful. */ - -/* The following error codes may be generated with the ERRDOS error class.*/ - -#define ERRbadfunc 1 /* Invalid function. The server did not - recognize or could not perform a - system call generated by the server, - e.g., set the DIRECTORY attribute on - a data file, invalid seek mode. */ -#define ERRbadfile 2 /* File not found. The last component - of a file's pathname could not be - found. */ -#define ERRbadpath 3 /* Directory invalid. A directory - component in a pathname could not be - found. */ -#define ERRnofids 4 /* Too many open files. The server has - no file handles available. */ -#define ERRnoaccess 5 /* Access denied, the client's context - does not permit the requested - function. This includes the - following conditions: invalid rename - command, write to Fid open for read - only, read on Fid open for write - only, attempt to delete a non-empty - directory */ -#define ERRbadfid 6 /* Invalid file handle. The file handle - specified was not recognized by the - server. */ -#define ERRbadmcb 7 /* Memory control blocks destroyed. */ -#define ERRnomem 8 /* Insufficient server memory to - perform the requested function. */ -#define ERRbadmem 9 /* Invalid memory block address. */ -#define ERRbadenv 10 /* Invalid environment. */ -#define ERRbadformat 11 /* Invalid format. */ -#define ERRbadaccess 12 /* Invalid open mode. */ -#define ERRbaddata 13 /* Invalid data (generated only by - IOCTL calls within the server). */ -#define ERRbaddrive 15 /* Invalid drive specified. */ -#define ERRremcd 16 /* A Delete Directory request attempted - to remove the server's current - directory. */ -#define ERRdiffdevice 17 /* Not same device (e.g., a cross - volume rename was attempted */ -#define ERRnofiles 18 /* A File Search command can find no - more files matching the specified - criteria. */ -#define ERRwriteprot 19 /* media is write protected */ -#define ERRgeneral 31 -#define ERRbadshare 32 /* The sharing mode specified for an - Open conflicts with existing FIDs on - the file. */ -#define ERRlock 33 /* A Lock request conflicted with an - existing lock or specified an - invalid mode, or an Unlock requested - attempted to remove a lock held by - another process. */ -#define ERRunsup 50 -#define ERRnosuchshare 67 -#define ERRfilexists 80 /* The file named in the request - already exists. */ -#define ERRinvparm 87 -#define ERRdiskfull 112 -#define ERRinvname 123 -#define ERRinvlevel 124 -#define ERRdirnotempty 145 -#define ERRnotlocked 158 -#define ERRcancelviolation 173 -#define ERRalreadyexists 183 -#define ERRbadpipe 230 -#define ERRpipebusy 231 -#define ERRpipeclosing 232 -#define ERRnotconnected 233 -#define ERRmoredata 234 -#define ERReasnotsupported 282 -#define ErrQuota 0x200 /* The operation would cause a quota - limit to be exceeded. */ -#define ErrNotALink 0x201 /* A link operation was performed on a - pathname that was not a link. */ - -/* Below errors are used internally (do not come over the wire) for passthrough - from STATUS codes to POSIX only */ -#define ERRsymlink 0xFFFD -#define ErrTooManyLinks 0xFFFE - -/* Following error codes may be generated with the ERRSRV error class.*/ - -#define ERRerror 1 /* Non-specific error code. It is - returned under the following - conditions: resource other than disk - space exhausted (e.g. TIDs), first - SMB command was not negotiate, - multiple negotiates attempted, and - internal server error. */ -#define ERRbadpw 2 /* Bad password - name/password pair in - a TreeConnect or Session Setup are - invalid. */ -#define ERRbadtype 3 /* used for indicating DFS referral - needed */ -#define ERRaccess 4 /* The client does not have the - necessary access rights within the - specified context for requested - function. */ -#define ERRinvtid 5 /* The Tid specified in a command was - invalid. */ -#define ERRinvnetname 6 /* Invalid network name in tree - connect. */ -#define ERRinvdevice 7 /* Invalid device - printer request - made to non-printer connection or - non-printer request made to printer - connection. */ -#define ERRqfull 49 /* Print queue full (files) -- returned - by open print file. */ -#define ERRqtoobig 50 /* Print queue full -- no space. */ -#define ERRqeof 51 /* EOF on print queue dump */ -#define ERRinvpfid 52 /* Invalid print file FID. */ -#define ERRsmbcmd 64 /* The server did not recognize the - command received. */ -#define ERRsrverror 65 /* The server encountered an internal - error, e.g., system file - unavailable. */ -#define ERRbadBID 66 /* (obsolete) */ -#define ERRfilespecs 67 /* The Fid and pathname parameters - contained an invalid combination of - values. */ -#define ERRbadLink 68 /* (obsolete) */ -#define ERRbadpermits 69 /* The access permissions specified for - a file or directory are not a valid - combination. */ -#define ERRbadPID 70 -#define ERRsetattrmode 71 /* attribute (mode) is invalid */ -#define ERRpaused 81 /* Server is paused */ -#define ERRmsgoff 82 /* reserved - messaging off */ -#define ERRnoroom 83 /* reserved - no room for message */ -#define ERRrmuns 87 /* reserved - too many remote names */ -#define ERRtimeout 88 /* operation timed out */ -#define ERRnoresource 89 /* No resources available for request - */ -#define ERRtoomanyuids 90 /* Too many UIDs active on this session - */ -#define ERRbaduid 91 /* The UID is not known as a valid user - */ -#define ERRusempx 250 /* temporarily unable to use raw */ -#define ERRusestd 251 /* temporarily unable to use either raw - or mpx */ -#define ERR_NOTIFY_ENUM_DIR 1024 -#define ERRnoSuchUser 2238 /* user account does not exist */ -#define ERRaccountexpired 2239 -#define ERRbadclient 2240 /* can not logon from this client */ -#define ERRbadLogonTime 2241 /* logon hours do not allow this */ -#define ERRpasswordExpired 2242 -#define ERRnetlogonNotStarted 2455 -#define ERRnosupport 0xFFFF diff --git a/fs/cifs/trace.c b/fs/cifs/trace.c deleted file mode 100644 index 465483787193..000000000000 --- a/fs/cifs/trace.c +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2018, Microsoft Corporation. - * - * Author(s): Steve French - */ -#define CREATE_TRACE_POINTS -#include "trace.h" diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h deleted file mode 100644 index d3053bd8ae73..000000000000 --- a/fs/cifs/trace.h +++ /dev/null @@ -1,1070 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2018, Microsoft Corporation. - * - * Author(s): Steve French - */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM cifs - -#if !defined(_CIFS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) -#define _CIFS_TRACE_H - -#include -#include -#include - -/* - * Please use this 3-part article as a reference for writing new tracepoints: - * https://lwn.net/Articles/379903/ - */ - -/* For logging errors in read or write */ -DECLARE_EVENT_CLASS(smb3_rw_err_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u64 offset, - __u32 len, - int rc), - TP_ARGS(xid, fid, tid, sesid, offset, len, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, offset) - __field(__u32, len) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->offset = offset; - __entry->len = len; - __entry->rc = rc; - ), - TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->offset, __entry->len, __entry->rc) -) - -#define DEFINE_SMB3_RW_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u64 offset, \ - __u32 len, \ - int rc), \ - TP_ARGS(xid, fid, tid, sesid, offset, len, rc)) - -DEFINE_SMB3_RW_ERR_EVENT(write_err); -DEFINE_SMB3_RW_ERR_EVENT(read_err); -DEFINE_SMB3_RW_ERR_EVENT(query_dir_err); -DEFINE_SMB3_RW_ERR_EVENT(zero_err); -DEFINE_SMB3_RW_ERR_EVENT(falloc_err); - - -/* For logging successful read or write */ -DECLARE_EVENT_CLASS(smb3_rw_done_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u64 offset, - __u32 len), - TP_ARGS(xid, fid, tid, sesid, offset, len), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, offset) - __field(__u32, len) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->offset = offset; - __entry->len = len; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->offset, __entry->len) -) - -#define DEFINE_SMB3_RW_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u64 offset, \ - __u32 len), \ - TP_ARGS(xid, fid, tid, sesid, offset, len)) - -DEFINE_SMB3_RW_DONE_EVENT(write_enter); -DEFINE_SMB3_RW_DONE_EVENT(read_enter); -DEFINE_SMB3_RW_DONE_EVENT(query_dir_enter); -DEFINE_SMB3_RW_DONE_EVENT(zero_enter); -DEFINE_SMB3_RW_DONE_EVENT(falloc_enter); -DEFINE_SMB3_RW_DONE_EVENT(write_done); -DEFINE_SMB3_RW_DONE_EVENT(read_done); -DEFINE_SMB3_RW_DONE_EVENT(query_dir_done); -DEFINE_SMB3_RW_DONE_EVENT(zero_done); -DEFINE_SMB3_RW_DONE_EVENT(falloc_done); - -/* For logging successful set EOF (truncate) */ -DECLARE_EVENT_CLASS(smb3_eof_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u64 offset), - TP_ARGS(xid, fid, tid, sesid, offset), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, offset) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->offset = offset; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->offset) -) - -#define DEFINE_SMB3_EOF_EVENT(name) \ -DEFINE_EVENT(smb3_eof_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u64 offset), \ - TP_ARGS(xid, fid, tid, sesid, offset)) - -DEFINE_SMB3_EOF_EVENT(set_eof); - -/* - * For handle based calls other than read and write, and get/set info - */ -DECLARE_EVENT_CLASS(smb3_fd_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid), - TP_ARGS(xid, fid, tid, sesid), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - ), - TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid) -) - -#define DEFINE_SMB3_FD_EVENT(name) \ -DEFINE_EVENT(smb3_fd_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid), \ - TP_ARGS(xid, fid, tid, sesid)) - -DEFINE_SMB3_FD_EVENT(flush_enter); -DEFINE_SMB3_FD_EVENT(flush_done); -DEFINE_SMB3_FD_EVENT(close_enter); -DEFINE_SMB3_FD_EVENT(close_done); -DEFINE_SMB3_FD_EVENT(oplock_not_found); - -DECLARE_EVENT_CLASS(smb3_fd_err_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - int rc), - TP_ARGS(xid, fid, tid, sesid, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->rc = rc; - ), - TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->rc) -) - -#define DEFINE_SMB3_FD_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_fd_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - int rc), \ - TP_ARGS(xid, fid, tid, sesid, rc)) - -DEFINE_SMB3_FD_ERR_EVENT(flush_err); -DEFINE_SMB3_FD_ERR_EVENT(lock_err); -DEFINE_SMB3_FD_ERR_EVENT(close_err); - -/* - * For handle based query/set info calls - */ -DECLARE_EVENT_CLASS(smb3_inf_enter_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u8 infclass, - __u32 type), - TP_ARGS(xid, fid, tid, sesid, infclass, type), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u8, infclass) - __field(__u32, type) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->infclass = infclass; - __entry->type = type; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->infclass, __entry->type) -) - -#define DEFINE_SMB3_INF_ENTER_EVENT(name) \ -DEFINE_EVENT(smb3_inf_enter_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u8 infclass, \ - __u32 type), \ - TP_ARGS(xid, fid, tid, sesid, infclass, type)) - -DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter); -DEFINE_SMB3_INF_ENTER_EVENT(query_info_done); -DEFINE_SMB3_INF_ENTER_EVENT(notify_enter); -DEFINE_SMB3_INF_ENTER_EVENT(notify_done); - -DECLARE_EVENT_CLASS(smb3_inf_err_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u8 infclass, - __u32 type, - int rc), - TP_ARGS(xid, fid, tid, sesid, infclass, type, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u8, infclass) - __field(__u32, type) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->infclass = infclass; - __entry->type = type; - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->infclass, __entry->type, __entry->rc) -) - -#define DEFINE_SMB3_INF_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_inf_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u8 infclass, \ - __u32 type, \ - int rc), \ - TP_ARGS(xid, fid, tid, sesid, infclass, type, rc)) - -DEFINE_SMB3_INF_ERR_EVENT(query_info_err); -DEFINE_SMB3_INF_ERR_EVENT(set_info_err); -DEFINE_SMB3_INF_ERR_EVENT(notify_err); -DEFINE_SMB3_INF_ERR_EVENT(fsctl_err); - -DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - const char *full_path), - TP_ARGS(xid, tid, sesid, full_path), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __string(path, full_path) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __assign_str(path, full_path); - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s", - __entry->xid, __entry->sesid, __entry->tid, - __get_str(path)) -) - -#define DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(name) \ -DEFINE_EVENT(smb3_inf_compound_enter_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - const char *full_path), \ - TP_ARGS(xid, tid, sesid, full_path)) - -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_info_compound_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(posix_query_info_compound_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(hardlink_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter); - - -DECLARE_EVENT_CLASS(smb3_inf_compound_done_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid), - TP_ARGS(xid, tid, sesid), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x", - __entry->xid, __entry->sesid, __entry->tid) -) - -#define DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_inf_compound_done_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid), \ - TP_ARGS(xid, tid, sesid)) - -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_info_compound_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(posix_query_info_compound_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(hardlink_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); - - -DECLARE_EVENT_CLASS(smb3_inf_compound_err_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - int rc), - TP_ARGS(xid, tid, sesid, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, - __entry->rc) -) - -#define DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_inf_compound_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - int rc), \ - TP_ARGS(xid, tid, sesid, rc)) - -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_info_compound_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(posix_query_info_compound_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(hardlink_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); - -/* - * For logging SMB3 Status code and Command for responses which return errors - */ -DECLARE_EVENT_CLASS(smb3_cmd_err_class, - TP_PROTO(__u32 tid, - __u64 sesid, - __u16 cmd, - __u64 mid, - __u32 status, - int rc), - TP_ARGS(tid, sesid, cmd, mid, status, rc), - TP_STRUCT__entry( - __field(__u32, tid) - __field(__u64, sesid) - __field(__u16, cmd) - __field(__u64, mid) - __field(__u32, status) - __field(int, rc) - ), - TP_fast_assign( - __entry->tid = tid; - __entry->sesid = sesid; - __entry->cmd = cmd; - __entry->mid = mid; - __entry->status = status; - __entry->rc = rc; - ), - TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d", - __entry->sesid, __entry->tid, __entry->cmd, __entry->mid, - __entry->status, __entry->rc) -) - -#define DEFINE_SMB3_CMD_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_cmd_err_class, smb3_##name, \ - TP_PROTO(__u32 tid, \ - __u64 sesid, \ - __u16 cmd, \ - __u64 mid, \ - __u32 status, \ - int rc), \ - TP_ARGS(tid, sesid, cmd, mid, status, rc)) - -DEFINE_SMB3_CMD_ERR_EVENT(cmd_err); - -DECLARE_EVENT_CLASS(smb3_cmd_done_class, - TP_PROTO(__u32 tid, - __u64 sesid, - __u16 cmd, - __u64 mid), - TP_ARGS(tid, sesid, cmd, mid), - TP_STRUCT__entry( - __field(__u32, tid) - __field(__u64, sesid) - __field(__u16, cmd) - __field(__u64, mid) - ), - TP_fast_assign( - __entry->tid = tid; - __entry->sesid = sesid; - __entry->cmd = cmd; - __entry->mid = mid; - ), - TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu", - __entry->sesid, __entry->tid, - __entry->cmd, __entry->mid) -) - -#define DEFINE_SMB3_CMD_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \ - TP_PROTO(__u32 tid, \ - __u64 sesid, \ - __u16 cmd, \ - __u64 mid), \ - TP_ARGS(tid, sesid, cmd, mid)) - -DEFINE_SMB3_CMD_DONE_EVENT(cmd_enter); -DEFINE_SMB3_CMD_DONE_EVENT(cmd_done); -DEFINE_SMB3_CMD_DONE_EVENT(ses_expired); - -DECLARE_EVENT_CLASS(smb3_mid_class, - TP_PROTO(__u16 cmd, - __u64 mid, - __u32 pid, - unsigned long when_sent, - unsigned long when_received), - TP_ARGS(cmd, mid, pid, when_sent, when_received), - TP_STRUCT__entry( - __field(__u16, cmd) - __field(__u64, mid) - __field(__u32, pid) - __field(unsigned long, when_sent) - __field(unsigned long, when_received) - ), - TP_fast_assign( - __entry->cmd = cmd; - __entry->mid = mid; - __entry->pid = pid; - __entry->when_sent = when_sent; - __entry->when_received = when_received; - ), - TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu", - __entry->cmd, __entry->mid, __entry->pid, __entry->when_sent, - __entry->when_received) -) - -#define DEFINE_SMB3_MID_EVENT(name) \ -DEFINE_EVENT(smb3_mid_class, smb3_##name, \ - TP_PROTO(__u16 cmd, \ - __u64 mid, \ - __u32 pid, \ - unsigned long when_sent, \ - unsigned long when_received), \ - TP_ARGS(cmd, mid, pid, when_sent, when_received)) - -DEFINE_SMB3_MID_EVENT(slow_rsp); - -DECLARE_EVENT_CLASS(smb3_exit_err_class, - TP_PROTO(unsigned int xid, - const char *func_name, - int rc), - TP_ARGS(xid, func_name, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __string(func_name, func_name) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __assign_str(func_name, func_name); - __entry->rc = rc; - ), - TP_printk("\t%s: xid=%u rc=%d", - __get_str(func_name), __entry->xid, __entry->rc) -) - -#define DEFINE_SMB3_EXIT_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_exit_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - const char *func_name, \ - int rc), \ - TP_ARGS(xid, func_name, rc)) - -DEFINE_SMB3_EXIT_ERR_EVENT(exit_err); - - -DECLARE_EVENT_CLASS(smb3_sync_err_class, - TP_PROTO(unsigned long ino, - int rc), - TP_ARGS(ino, rc), - TP_STRUCT__entry( - __field(unsigned long, ino) - __field(int, rc) - ), - TP_fast_assign( - __entry->ino = ino; - __entry->rc = rc; - ), - TP_printk("\tino=%lu rc=%d", - __entry->ino, __entry->rc) -) - -#define DEFINE_SMB3_SYNC_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_sync_err_class, cifs_##name, \ - TP_PROTO(unsigned long ino, \ - int rc), \ - TP_ARGS(ino, rc)) - -DEFINE_SMB3_SYNC_ERR_EVENT(fsync_err); -DEFINE_SMB3_SYNC_ERR_EVENT(flush_err); - - -DECLARE_EVENT_CLASS(smb3_enter_exit_class, - TP_PROTO(unsigned int xid, - const char *func_name), - TP_ARGS(xid, func_name), - TP_STRUCT__entry( - __field(unsigned int, xid) - __string(func_name, func_name) - ), - TP_fast_assign( - __entry->xid = xid; - __assign_str(func_name, func_name); - ), - TP_printk("\t%s: xid=%u", - __get_str(func_name), __entry->xid) -) - -#define DEFINE_SMB3_ENTER_EXIT_EVENT(name) \ -DEFINE_EVENT(smb3_enter_exit_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - const char *func_name), \ - TP_ARGS(xid, func_name)) - -DEFINE_SMB3_ENTER_EXIT_EVENT(enter); -DEFINE_SMB3_ENTER_EXIT_EVENT(exit_done); - -/* - * For SMB2/SMB3 tree connect - */ - -DECLARE_EVENT_CLASS(smb3_tcon_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - const char *unc_name, - int rc), - TP_ARGS(xid, tid, sesid, unc_name, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __string(name, unc_name) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __assign_str(name, unc_name); - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x unc_name=%s rc=%d", - __entry->xid, __entry->sesid, __entry->tid, - __get_str(name), __entry->rc) -) - -#define DEFINE_SMB3_TCON_EVENT(name) \ -DEFINE_EVENT(smb3_tcon_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - const char *unc_name, \ - int rc), \ - TP_ARGS(xid, tid, sesid, unc_name, rc)) - -DEFINE_SMB3_TCON_EVENT(tcon); - - -/* - * For smb2/smb3 open (including create and mkdir) calls - */ - -DECLARE_EVENT_CLASS(smb3_open_enter_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - const char *full_path, - int create_options, - int desired_access), - TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __string(path, full_path) - __field(int, create_options) - __field(int, desired_access) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __assign_str(path, full_path); - __entry->create_options = create_options; - __entry->desired_access = desired_access; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s cr_opts=0x%x des_access=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __get_str(path), - __entry->create_options, __entry->desired_access) -) - -#define DEFINE_SMB3_OPEN_ENTER_EVENT(name) \ -DEFINE_EVENT(smb3_open_enter_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - const char *full_path, \ - int create_options, \ - int desired_access), \ - TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access)) - -DEFINE_SMB3_OPEN_ENTER_EVENT(open_enter); -DEFINE_SMB3_OPEN_ENTER_EVENT(posix_mkdir_enter); - -DECLARE_EVENT_CLASS(smb3_open_err_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - int create_options, - int desired_access, - int rc), - TP_ARGS(xid, tid, sesid, create_options, desired_access, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, create_options) - __field(int, desired_access) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->create_options = create_options; - __entry->desired_access = desired_access; - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x cr_opts=0x%x des_access=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, - __entry->create_options, __entry->desired_access, __entry->rc) -) - -#define DEFINE_SMB3_OPEN_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_open_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - int create_options, \ - int desired_access, \ - int rc), \ - TP_ARGS(xid, tid, sesid, create_options, desired_access, rc)) - -DEFINE_SMB3_OPEN_ERR_EVENT(open_err); -DEFINE_SMB3_OPEN_ERR_EVENT(posix_mkdir_err); - -DECLARE_EVENT_CLASS(smb3_open_done_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - int create_options, - int desired_access), - TP_ARGS(xid, fid, tid, sesid, create_options, desired_access), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, create_options) - __field(int, desired_access) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->create_options = create_options; - __entry->desired_access = desired_access; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->create_options, __entry->desired_access) -) - -#define DEFINE_SMB3_OPEN_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_open_done_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - int create_options, \ - int desired_access), \ - TP_ARGS(xid, fid, tid, sesid, create_options, desired_access)) - -DEFINE_SMB3_OPEN_DONE_EVENT(open_done); -DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); - - -DECLARE_EVENT_CLASS(smb3_lease_done_class, - TP_PROTO(__u32 lease_state, - __u32 tid, - __u64 sesid, - __u64 lease_key_low, - __u64 lease_key_high), - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high), - TP_STRUCT__entry( - __field(__u32, lease_state) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, lease_key_low) - __field(__u64, lease_key_high) - ), - TP_fast_assign( - __entry->lease_state = lease_state; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->lease_key_low = lease_key_low; - __entry->lease_key_high = lease_key_high; - ), - TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x", - __entry->sesid, __entry->tid, __entry->lease_key_high, - __entry->lease_key_low, __entry->lease_state) -) - -#define DEFINE_SMB3_LEASE_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_lease_done_class, smb3_##name, \ - TP_PROTO(__u32 lease_state, \ - __u32 tid, \ - __u64 sesid, \ - __u64 lease_key_low, \ - __u64 lease_key_high), \ - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high)) - -DEFINE_SMB3_LEASE_DONE_EVENT(lease_done); -DEFINE_SMB3_LEASE_DONE_EVENT(lease_not_found); - -DECLARE_EVENT_CLASS(smb3_lease_err_class, - TP_PROTO(__u32 lease_state, - __u32 tid, - __u64 sesid, - __u64 lease_key_low, - __u64 lease_key_high, - int rc), - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc), - TP_STRUCT__entry( - __field(__u32, lease_state) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, lease_key_low) - __field(__u64, lease_key_high) - __field(int, rc) - ), - TP_fast_assign( - __entry->lease_state = lease_state; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->lease_key_low = lease_key_low; - __entry->lease_key_high = lease_key_high; - __entry->rc = rc; - ), - TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x rc=%d", - __entry->sesid, __entry->tid, __entry->lease_key_high, - __entry->lease_key_low, __entry->lease_state, __entry->rc) -) - -#define DEFINE_SMB3_LEASE_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_lease_err_class, smb3_##name, \ - TP_PROTO(__u32 lease_state, \ - __u32 tid, \ - __u64 sesid, \ - __u64 lease_key_low, \ - __u64 lease_key_high, \ - int rc), \ - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc)) - -DEFINE_SMB3_LEASE_ERR_EVENT(lease_err); - -DECLARE_EVENT_CLASS(smb3_connect_class, - TP_PROTO(char *hostname, - __u64 conn_id, - const struct __kernel_sockaddr_storage *dst_addr), - TP_ARGS(hostname, conn_id, dst_addr), - TP_STRUCT__entry( - __string(hostname, hostname) - __field(__u64, conn_id) - __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) - ), - TP_fast_assign( - struct sockaddr_storage *pss = NULL; - - __entry->conn_id = conn_id; - pss = (struct sockaddr_storage *)__entry->dst_addr; - *pss = *dst_addr; - __assign_str(hostname, hostname); - ), - TP_printk("conn_id=0x%llx server=%s addr=%pISpsfc", - __entry->conn_id, - __get_str(hostname), - __entry->dst_addr) -) - -#define DEFINE_SMB3_CONNECT_EVENT(name) \ -DEFINE_EVENT(smb3_connect_class, smb3_##name, \ - TP_PROTO(char *hostname, \ - __u64 conn_id, \ - const struct __kernel_sockaddr_storage *addr), \ - TP_ARGS(hostname, conn_id, addr)) - -DEFINE_SMB3_CONNECT_EVENT(connect_done); - -DECLARE_EVENT_CLASS(smb3_connect_err_class, - TP_PROTO(char *hostname, __u64 conn_id, - const struct __kernel_sockaddr_storage *dst_addr, int rc), - TP_ARGS(hostname, conn_id, dst_addr, rc), - TP_STRUCT__entry( - __string(hostname, hostname) - __field(__u64, conn_id) - __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) - __field(int, rc) - ), - TP_fast_assign( - struct sockaddr_storage *pss = NULL; - - __entry->conn_id = conn_id; - __entry->rc = rc; - pss = (struct sockaddr_storage *)__entry->dst_addr; - *pss = *dst_addr; - __assign_str(hostname, hostname); - ), - TP_printk("rc=%d conn_id=0x%llx server=%s addr=%pISpsfc", - __entry->rc, - __entry->conn_id, - __get_str(hostname), - __entry->dst_addr) -) - -#define DEFINE_SMB3_CONNECT_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_connect_err_class, smb3_##name, \ - TP_PROTO(char *hostname, \ - __u64 conn_id, \ - const struct __kernel_sockaddr_storage *addr, \ - int rc), \ - TP_ARGS(hostname, conn_id, addr, rc)) - -DEFINE_SMB3_CONNECT_ERR_EVENT(connect_err); - -DECLARE_EVENT_CLASS(smb3_reconnect_class, - TP_PROTO(__u64 currmid, - __u64 conn_id, - char *hostname), - TP_ARGS(currmid, conn_id, hostname), - TP_STRUCT__entry( - __field(__u64, currmid) - __field(__u64, conn_id) - __string(hostname, hostname) - ), - TP_fast_assign( - __entry->currmid = currmid; - __entry->conn_id = conn_id; - __assign_str(hostname, hostname); - ), - TP_printk("conn_id=0x%llx server=%s current_mid=%llu", - __entry->conn_id, - __get_str(hostname), - __entry->currmid) -) - -#define DEFINE_SMB3_RECONNECT_EVENT(name) \ -DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \ - TP_PROTO(__u64 currmid, \ - __u64 conn_id, \ - char *hostname), \ - TP_ARGS(currmid, conn_id, hostname)) - -DEFINE_SMB3_RECONNECT_EVENT(reconnect); -DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect); - -DECLARE_EVENT_CLASS(smb3_credit_class, - TP_PROTO(__u64 currmid, - __u64 conn_id, - char *hostname, - int credits, - int credits_to_add, - int in_flight), - TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight), - TP_STRUCT__entry( - __field(__u64, currmid) - __field(__u64, conn_id) - __string(hostname, hostname) - __field(int, credits) - __field(int, credits_to_add) - __field(int, in_flight) - ), - TP_fast_assign( - __entry->currmid = currmid; - __entry->conn_id = conn_id; - __assign_str(hostname, hostname); - __entry->credits = credits; - __entry->credits_to_add = credits_to_add; - __entry->in_flight = in_flight; - ), - TP_printk("conn_id=0x%llx server=%s current_mid=%llu " - "credits=%d credit_change=%d in_flight=%d", - __entry->conn_id, - __get_str(hostname), - __entry->currmid, - __entry->credits, - __entry->credits_to_add, - __entry->in_flight) -) - -#define DEFINE_SMB3_CREDIT_EVENT(name) \ -DEFINE_EVENT(smb3_credit_class, smb3_##name, \ - TP_PROTO(__u64 currmid, \ - __u64 conn_id, \ - char *hostname, \ - int credits, \ - int credits_to_add, \ - int in_flight), \ - TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight)) - -DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits); -DEFINE_SMB3_CREDIT_EVENT(reconnect_detected); -DEFINE_SMB3_CREDIT_EVENT(credit_timeout); -DEFINE_SMB3_CREDIT_EVENT(insufficient_credits); -DEFINE_SMB3_CREDIT_EVENT(too_many_credits); -DEFINE_SMB3_CREDIT_EVENT(add_credits); -DEFINE_SMB3_CREDIT_EVENT(adj_credits); -DEFINE_SMB3_CREDIT_EVENT(hdr_credits); -DEFINE_SMB3_CREDIT_EVENT(nblk_credits); -DEFINE_SMB3_CREDIT_EVENT(pend_credits); -DEFINE_SMB3_CREDIT_EVENT(wait_credits); -DEFINE_SMB3_CREDIT_EVENT(waitff_credits); -DEFINE_SMB3_CREDIT_EVENT(overflow_credits); -DEFINE_SMB3_CREDIT_EVENT(set_credits); - -#endif /* _CIFS_TRACE_H */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#define TRACE_INCLUDE_FILE trace -#include diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c deleted file mode 100644 index c961b90f92b9..000000000000 --- a/fs/cifs/transport.c +++ /dev/null @@ -1,1807 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) 2006. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "smb2proto.h" -#include "smbdirect.h" - -/* Max number of iovectors we can use off the stack when sending requests. */ -#define CIFS_MAX_IOV_SIZE 8 - -void -cifs_wake_up_task(struct mid_q_entry *mid) -{ - wake_up_process(mid->callback_data); -} - -static struct mid_q_entry * -alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) -{ - struct mid_q_entry *temp; - - if (server == NULL) { - cifs_dbg(VFS, "%s: null TCP session\n", __func__); - return NULL; - } - - temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); - memset(temp, 0, sizeof(struct mid_q_entry)); - kref_init(&temp->refcount); - temp->mid = get_mid(smb_buffer); - temp->pid = current->pid; - temp->command = cpu_to_le16(smb_buffer->Command); - cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); - /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ - /* when mid allocated can be before when sent */ - temp->when_alloc = jiffies; - temp->server = server; - - /* - * The default is for the mid to be synchronous, so the - * default callback just wakes up the current task. - */ - get_task_struct(current); - temp->creator = current; - temp->callback = cifs_wake_up_task; - temp->callback_data = current; - - atomic_inc(&mid_count); - temp->mid_state = MID_REQUEST_ALLOCATED; - return temp; -} - -static void __release_mid(struct kref *refcount) -{ - struct mid_q_entry *midEntry = - container_of(refcount, struct mid_q_entry, refcount); -#ifdef CONFIG_CIFS_STATS2 - __le16 command = midEntry->server->vals->lock_cmd; - __u16 smb_cmd = le16_to_cpu(midEntry->command); - unsigned long now; - unsigned long roundtrip_time; -#endif - struct TCP_Server_Info *server = midEntry->server; - - if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) && - midEntry->mid_state == MID_RESPONSE_RECEIVED && - server->ops->handle_cancelled_mid) - server->ops->handle_cancelled_mid(midEntry, server); - - midEntry->mid_state = MID_FREE; - atomic_dec(&mid_count); - if (midEntry->large_buf) - cifs_buf_release(midEntry->resp_buf); - else - cifs_small_buf_release(midEntry->resp_buf); -#ifdef CONFIG_CIFS_STATS2 - now = jiffies; - if (now < midEntry->when_alloc) - cifs_server_dbg(VFS, "Invalid mid allocation time\n"); - roundtrip_time = now - midEntry->when_alloc; - - if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) { - if (atomic_read(&server->num_cmds[smb_cmd]) == 0) { - server->slowest_cmd[smb_cmd] = roundtrip_time; - server->fastest_cmd[smb_cmd] = roundtrip_time; - } else { - if (server->slowest_cmd[smb_cmd] < roundtrip_time) - server->slowest_cmd[smb_cmd] = roundtrip_time; - else if (server->fastest_cmd[smb_cmd] > roundtrip_time) - server->fastest_cmd[smb_cmd] = roundtrip_time; - } - cifs_stats_inc(&server->num_cmds[smb_cmd]); - server->time_per_cmd[smb_cmd] += roundtrip_time; - } - /* - * commands taking longer than one second (default) can be indications - * that something is wrong, unless it is quite a slow link or a very - * busy server. Note that this calc is unlikely or impossible to wrap - * as long as slow_rsp_threshold is not set way above recommended max - * value (32767 ie 9 hours) and is generally harmless even if wrong - * since only affects debug counters - so leaving the calc as simple - * comparison rather than doing multiple conversions and overflow - * checks - */ - if ((slow_rsp_threshold != 0) && - time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) && - (midEntry->command != command)) { - /* - * smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command - * NB: le16_to_cpu returns unsigned so can not be negative below - */ - if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) - cifs_stats_inc(&server->smb2slowcmd[smb_cmd]); - - trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid, - midEntry->when_sent, midEntry->when_received); - if (cifsFYI & CIFS_TIMER) { - pr_debug("slow rsp: cmd %d mid %llu", - midEntry->command, midEntry->mid); - cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n", - now - midEntry->when_alloc, - now - midEntry->when_sent, - now - midEntry->when_received); - } - } -#endif - put_task_struct(midEntry->creator); - - mempool_free(midEntry, cifs_mid_poolp); -} - -void release_mid(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->server; - - spin_lock(&server->mid_lock); - kref_put(&mid->refcount, __release_mid); - spin_unlock(&server->mid_lock); -} - -void -delete_mid(struct mid_q_entry *mid) -{ - spin_lock(&mid->server->mid_lock); - if (!(mid->mid_flags & MID_DELETED)) { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - } - spin_unlock(&mid->server->mid_lock); - - release_mid(mid); -} - -/* - * smb_send_kvec - send an array of kvecs to the server - * @server: Server to send the data to - * @smb_msg: Message to send - * @sent: amount of data sent on socket is stored here - * - * Our basic "send data to server" function. Should be called with srv_mutex - * held. The caller is responsible for handling the results. - */ -static int -smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, - size_t *sent) -{ - int rc = 0; - int retries = 0; - struct socket *ssocket = server->ssocket; - - *sent = 0; - - if (server->noblocksnd) - smb_msg->msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; - else - smb_msg->msg_flags = MSG_NOSIGNAL; - - while (msg_data_left(smb_msg)) { - /* - * If blocking send, we try 3 times, since each can block - * for 5 seconds. For nonblocking we have to try more - * but wait increasing amounts of time allowing time for - * socket to clear. The overall time we wait in either - * case to send on the socket is about 15 seconds. - * Similarly we wait for 15 seconds for a response from - * the server in SendReceive[2] for the server to send - * a response back for most types of requests (except - * SMB Write past end of file which can be slow, and - * blocking lock operations). NFS waits slightly longer - * than CIFS, but this can make it take longer for - * nonresponsive servers to be detected and 15 seconds - * is more than enough time for modern networks to - * send a packet. In most cases if we fail to send - * after the retries we will kill the socket and - * reconnect which may clear the network problem. - */ - rc = sock_sendmsg(ssocket, smb_msg); - if (rc == -EAGAIN) { - retries++; - if (retries >= 14 || - (!server->noblocksnd && (retries > 2))) { - cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", - ssocket); - return -EAGAIN; - } - msleep(1 << retries); - continue; - } - - if (rc < 0) - return rc; - - if (rc == 0) { - /* should never happen, letting socket clear before - retrying is our only obvious option here */ - cifs_server_dbg(VFS, "tcp sent no data\n"); - msleep(500); - continue; - } - - /* send was at least partially successful */ - *sent += rc; - retries = 0; /* in case we get ENOSPC on the next send */ - } - return 0; -} - -unsigned long -smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - unsigned int i; - struct kvec *iov; - int nvec; - unsigned long buflen = 0; - - if (!is_smb1(server) && rqst->rq_nvec >= 2 && - rqst->rq_iov[0].iov_len == 4) { - iov = &rqst->rq_iov[1]; - nvec = rqst->rq_nvec - 1; - } else { - iov = rqst->rq_iov; - nvec = rqst->rq_nvec; - } - - /* total up iov array first */ - for (i = 0; i < nvec; i++) - buflen += iov[i].iov_len; - - /* - * Add in the page array if there is one. The caller needs to make - * sure rq_offset and rq_tailsz are set correctly. If a buffer of - * multiple pages ends at page boundary, rq_tailsz needs to be set to - * PAGE_SIZE. - */ - if (rqst->rq_npages) { - if (rqst->rq_npages == 1) - buflen += rqst->rq_tailsz; - else { - /* - * If there is more than one page, calculate the - * buffer length based on rq_offset and rq_tailsz - */ - buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) - - rqst->rq_offset; - buflen += rqst->rq_tailsz; - } - } - - return buflen; -} - -static int -__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *rqst) -{ - int rc; - struct kvec *iov; - int n_vec; - unsigned int send_length = 0; - unsigned int i, j; - sigset_t mask, oldmask; - size_t total_len = 0, sent, size; - struct socket *ssocket = server->ssocket; - struct msghdr smb_msg = {}; - __be32 rfc1002_marker; - - cifs_in_send_inc(server); - if (cifs_rdma_enabled(server)) { - /* return -EAGAIN when connecting or reconnecting */ - rc = -EAGAIN; - if (server->smbd_conn) - rc = smbd_send(server, num_rqst, rqst); - goto smbd_done; - } - - rc = -EAGAIN; - if (ssocket == NULL) - goto out; - - rc = -ERESTARTSYS; - if (fatal_signal_pending(current)) { - cifs_dbg(FYI, "signal pending before send request\n"); - goto out; - } - - rc = 0; - /* cork the socket */ - tcp_sock_set_cork(ssocket->sk, true); - - for (j = 0; j < num_rqst; j++) - send_length += smb_rqst_len(server, &rqst[j]); - rfc1002_marker = cpu_to_be32(send_length); - - /* - * We should not allow signals to interrupt the network send because - * any partial send will cause session reconnects thus increasing - * latency of system calls and overload a server with unnecessary - * requests. - */ - - sigfillset(&mask); - sigprocmask(SIG_BLOCK, &mask, &oldmask); - - /* Generate a rfc1002 marker for SMB2+ */ - if (!is_smb1(server)) { - struct kvec hiov = { - .iov_base = &rfc1002_marker, - .iov_len = 4 - }; - iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, &hiov, 1, 4); - rc = smb_send_kvec(server, &smb_msg, &sent); - if (rc < 0) - goto unmask; - - total_len += sent; - send_length += 4; - } - - cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); - - for (j = 0; j < num_rqst; j++) { - iov = rqst[j].rq_iov; - n_vec = rqst[j].rq_nvec; - - size = 0; - for (i = 0; i < n_vec; i++) { - dump_smb(iov[i].iov_base, iov[i].iov_len); - size += iov[i].iov_len; - } - - iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, iov, n_vec, size); - - rc = smb_send_kvec(server, &smb_msg, &sent); - if (rc < 0) - goto unmask; - - total_len += sent; - - /* now walk the page array and send each page in it */ - for (i = 0; i < rqst[j].rq_npages; i++) { - struct bio_vec bvec; - - bvec.bv_page = rqst[j].rq_pages[i]; - rqst_page_get_length(&rqst[j], i, &bvec.bv_len, - &bvec.bv_offset); - - iov_iter_bvec(&smb_msg.msg_iter, ITER_SOURCE, - &bvec, 1, bvec.bv_len); - rc = smb_send_kvec(server, &smb_msg, &sent); - if (rc < 0) - break; - - total_len += sent; - } - } - -unmask: - sigprocmask(SIG_SETMASK, &oldmask, NULL); - - /* - * If signal is pending but we have already sent the whole packet to - * the server we need to return success status to allow a corresponding - * mid entry to be kept in the pending requests queue thus allowing - * to handle responses from the server by the client. - * - * If only part of the packet has been sent there is no need to hide - * interrupt because the session will be reconnected anyway, so there - * won't be any response from the server to handle. - */ - - if (signal_pending(current) && (total_len != send_length)) { - cifs_dbg(FYI, "signal is pending after attempt to send\n"); - rc = -ERESTARTSYS; - } - - /* uncork it */ - tcp_sock_set_cork(ssocket->sk, false); - - if ((total_len > 0) && (total_len != send_length)) { - cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n", - send_length, total_len); - /* - * If we have only sent part of an SMB then the next SMB could - * be taken as the remainder of this one. We need to kill the - * socket so the server throws away the partial SMB - */ - cifs_signal_cifsd_for_reconnect(server, false); - trace_smb3_partial_send_reconnect(server->CurrentMid, - server->conn_id, server->hostname); - } -smbd_done: - if (rc < 0 && rc != -EINTR) - cifs_server_dbg(VFS, "Error %d sending data on socket to server\n", - rc); - else if (rc > 0) - rc = 0; -out: - cifs_in_send_dec(server); - return rc; -} - -static int -smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *rqst, int flags) -{ - struct kvec iov; - struct smb2_transform_hdr *tr_hdr; - struct smb_rqst cur_rqst[MAX_COMPOUND]; - int rc; - - if (!(flags & CIFS_TRANSFORM_REQ)) - return __smb_send_rqst(server, num_rqst, rqst); - - if (num_rqst > MAX_COMPOUND - 1) - return -ENOMEM; - - if (!server->ops->init_transform_rq) { - cifs_server_dbg(VFS, "Encryption requested but transform callback is missing\n"); - return -EIO; - } - - tr_hdr = kzalloc(sizeof(*tr_hdr), GFP_NOFS); - if (!tr_hdr) - return -ENOMEM; - - memset(&cur_rqst[0], 0, sizeof(cur_rqst)); - memset(&iov, 0, sizeof(iov)); - - iov.iov_base = tr_hdr; - iov.iov_len = sizeof(*tr_hdr); - cur_rqst[0].rq_iov = &iov; - cur_rqst[0].rq_nvec = 1; - - rc = server->ops->init_transform_rq(server, num_rqst + 1, - &cur_rqst[0], rqst); - if (rc) - goto out; - - rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); - smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); -out: - kfree(tr_hdr); - return rc; -} - -int -smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, - unsigned int smb_buf_length) -{ - struct kvec iov[2]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 2 }; - - iov[0].iov_base = smb_buffer; - iov[0].iov_len = 4; - iov[1].iov_base = (char *)smb_buffer + 4; - iov[1].iov_len = smb_buf_length; - - return __smb_send_rqst(server, 1, &rqst); -} - -static int -wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, - const int timeout, const int flags, - unsigned int *instance) -{ - long rc; - int *credits; - int optype; - long int t; - int scredits, in_flight; - - if (timeout < 0) - t = MAX_JIFFY_OFFSET; - else - t = msecs_to_jiffies(timeout); - - optype = flags & CIFS_OP_MASK; - - *instance = 0; - - credits = server->ops->get_credits_field(server, optype); - /* Since an echo is already inflight, no need to wait to send another */ - if (*credits <= 0 && optype == CIFS_ECHO_OP) - return -EAGAIN; - - spin_lock(&server->req_lock); - if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) { - /* oplock breaks must not be held up */ - server->in_flight++; - if (server->in_flight > server->max_in_flight) - server->max_in_flight = server->in_flight; - *credits -= 1; - *instance = server->reconnect_instance; - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_nblk_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, -1, in_flight); - cifs_dbg(FYI, "%s: remove %u credits total=%d\n", - __func__, 1, scredits); - - return 0; - } - - while (1) { - if (*credits < num_credits) { - scredits = *credits; - spin_unlock(&server->req_lock); - - cifs_num_waiters_inc(server); - rc = wait_event_killable_timeout(server->request_q, - has_credits(server, credits, num_credits), t); - cifs_num_waiters_dec(server); - if (!rc) { - spin_lock(&server->req_lock); - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_credit_timeout(server->CurrentMid, - server->conn_id, server->hostname, scredits, - num_credits, in_flight); - cifs_server_dbg(VFS, "wait timed out after %d ms\n", - timeout); - return -EBUSY; - } - if (rc == -ERESTARTSYS) - return -ERESTARTSYS; - spin_lock(&server->req_lock); - } else { - spin_unlock(&server->req_lock); - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* - * For normal commands, reserve the last MAX_COMPOUND - * credits to compound requests. - * Otherwise these compounds could be permanently - * starved for credits by single-credit requests. - * - * To prevent spinning CPU, block this thread until - * there are >MAX_COMPOUND credits available. - * But only do this is we already have a lot of - * credits in flight to avoid triggering this check - * for servers that are slow to hand out credits on - * new sessions. - */ - spin_lock(&server->req_lock); - if (!optype && num_credits == 1 && - server->in_flight > 2 * MAX_COMPOUND && - *credits <= MAX_COMPOUND) { - spin_unlock(&server->req_lock); - - cifs_num_waiters_inc(server); - rc = wait_event_killable_timeout( - server->request_q, - has_credits(server, credits, - MAX_COMPOUND + 1), - t); - cifs_num_waiters_dec(server); - if (!rc) { - spin_lock(&server->req_lock); - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_credit_timeout( - server->CurrentMid, - server->conn_id, server->hostname, - scredits, num_credits, in_flight); - cifs_server_dbg(VFS, "wait timed out after %d ms\n", - timeout); - return -EBUSY; - } - if (rc == -ERESTARTSYS) - return -ERESTARTSYS; - spin_lock(&server->req_lock); - continue; - } - - /* - * Can not count locking commands against total - * as they are allowed to block on server. - */ - - /* update # of requests on the wire to server */ - if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) { - *credits -= num_credits; - server->in_flight += num_credits; - if (server->in_flight > server->max_in_flight) - server->max_in_flight = server->in_flight; - *instance = server->reconnect_instance; - } - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_waitff_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - -(num_credits), in_flight); - cifs_dbg(FYI, "%s: remove %u credits total=%d\n", - __func__, num_credits, scredits); - break; - } - } - return 0; -} - -static int -wait_for_free_request(struct TCP_Server_Info *server, const int flags, - unsigned int *instance) -{ - return wait_for_free_credits(server, 1, -1, flags, - instance); -} - -static int -wait_for_compound_request(struct TCP_Server_Info *server, int num, - const int flags, unsigned int *instance) -{ - int *credits; - int scredits, in_flight; - - credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK); - - spin_lock(&server->req_lock); - scredits = *credits; - in_flight = server->in_flight; - - if (*credits < num) { - /* - * If the server is tight on resources or just gives us less - * credits for other reasons (e.g. requests are coming out of - * order and the server delays granting more credits until it - * processes a missing mid) and we exhausted most available - * credits there may be situations when we try to send - * a compound request but we don't have enough credits. At this - * point the client needs to decide if it should wait for - * additional credits or fail the request. If at least one - * request is in flight there is a high probability that the - * server will return enough credits to satisfy this compound - * request. - * - * Return immediately if no requests in flight since we will be - * stuck on waiting for credits. - */ - if (server->in_flight == 0) { - spin_unlock(&server->req_lock); - trace_smb3_insufficient_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - num, in_flight); - cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n", - __func__, in_flight, num, scredits); - return -EDEADLK; - } - } - spin_unlock(&server->req_lock); - - return wait_for_free_credits(server, num, 60000, flags, - instance); -} - -int -cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, - unsigned int *num, struct cifs_credits *credits) -{ - *num = size; - credits->value = 0; - credits->instance = server->reconnect_instance; - return 0; -} - -static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, - struct mid_q_entry **ppmidQ) -{ - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_NEW) { - if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && - (in_buf->Command != SMB_COM_NEGOTIATE)) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are setting up session */ - } - - if (ses->ses_status == SES_EXITING) { - /* check if SMB session is bad because we are setting it up */ - if (in_buf->Command != SMB_COM_LOGOFF_ANDX) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are shutting down session */ - } - spin_unlock(&ses->ses_lock); - - *ppmidQ = alloc_mid(in_buf, ses->server); - if (*ppmidQ == NULL) - return -ENOMEM; - spin_lock(&ses->server->mid_lock); - list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); - spin_unlock(&ses->server->mid_lock); - return 0; -} - -static int -wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) -{ - int error; - - error = wait_event_state(server->response_q, - midQ->mid_state != MID_REQUEST_SUBMITTED, - (TASK_KILLABLE|TASK_FREEZABLE_UNSAFE)); - if (error < 0) - return -ERESTARTSYS; - - return 0; -} - -struct mid_q_entry * -cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - int rc; - struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return ERR_PTR(-EIO); - - /* enable signing if server requires it */ - if (server->sign) - hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - mid = alloc_mid(hdr, server); - if (mid == NULL) - return ERR_PTR(-ENOMEM); - - rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); - if (rc) { - release_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -/* - * Send a SMB request and set the callback function in the mid to handle - * the result. Caller is responsible for dealing with timeouts. - */ -int -cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, - mid_receive_t *receive, mid_callback_t *callback, - mid_handle_t *handle, void *cbdata, const int flags, - const struct cifs_credits *exist_credits) -{ - int rc; - struct mid_q_entry *mid; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - unsigned int instance; - int optype; - - optype = flags & CIFS_OP_MASK; - - if ((flags & CIFS_HAS_CREDITS) == 0) { - rc = wait_for_free_request(server, flags, &instance); - if (rc) - return rc; - credits.value = 1; - credits.instance = instance; - } else - instance = exist_credits->instance; - - cifs_server_lock(server); - - /* - * We can't use credits obtained from the previous session to send this - * request. Check if there were reconnects after we obtained credits and - * return -EAGAIN in such cases to let callers handle it. - */ - if (instance != server->reconnect_instance) { - cifs_server_unlock(server); - add_credits_and_wake_if(server, &credits, optype); - return -EAGAIN; - } - - mid = server->ops->setup_async_request(server, rqst); - if (IS_ERR(mid)) { - cifs_server_unlock(server); - add_credits_and_wake_if(server, &credits, optype); - return PTR_ERR(mid); - } - - mid->receive = receive; - mid->callback = callback; - mid->callback_data = cbdata; - mid->handle = handle; - mid->mid_state = MID_REQUEST_SUBMITTED; - - /* put it on the pending_mid_q */ - spin_lock(&server->mid_lock); - list_add_tail(&mid->qhead, &server->pending_mid_q); - spin_unlock(&server->mid_lock); - - /* - * Need to store the time in mid before calling I/O. For call_async, - * I/O response may come back and free the mid entry on another thread. - */ - cifs_save_when_sent(mid); - rc = smb_send_rqst(server, 1, rqst, flags); - - if (rc < 0) { - revert_current_mid(server, mid->credits); - server->sequence_number -= 2; - delete_mid(mid); - } - - cifs_server_unlock(server); - - if (rc == 0) - return 0; - - add_credits_and_wake_if(server, &credits, optype); - return rc; -} - -/* - * - * Send an SMB Request. No response info (other than return code) - * needs to be parsed. - * - * flags indicate the type of request buffer and how long to wait - * and whether to log NT STATUS code (error) before mapping it to POSIX error - * - */ -int -SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - char *in_buf, int flags) -{ - int rc; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - iov[0].iov_base = in_buf; - iov[0].iov_len = get_rfc1002_length(in_buf) + 4; - flags |= CIFS_NO_RSP_BUF; - rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); - cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc); - - return rc; -} - -static int -cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) -{ - int rc = 0; - - cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n", - __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state); - - spin_lock(&server->mid_lock); - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - spin_unlock(&server->mid_lock); - return rc; - case MID_RETRY_NEEDED: - rc = -EAGAIN; - break; - case MID_RESPONSE_MALFORMED: - rc = -EIO; - break; - case MID_SHUTDOWN: - rc = -EHOSTDOWN; - break; - default: - if (!(mid->mid_flags & MID_DELETED)) { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - } - cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", - __func__, mid->mid, mid->mid_state); - rc = -EIO; - } - spin_unlock(&server->mid_lock); - - release_mid(mid); - return rc; -} - -static inline int -send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, - struct mid_q_entry *mid) -{ - return server->ops->send_cancel ? - server->ops->send_cancel(server, rqst, mid) : 0; -} - -int -cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, - bool log_error) -{ - unsigned int len = get_rfc1002_length(mid->resp_buf) + 4; - - dump_smb(mid->resp_buf, min_t(u32, 92, len)); - - /* convert the length into a more usable form */ - if (server->sign) { - struct kvec iov[2]; - int rc = 0; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 2 }; - - iov[0].iov_base = mid->resp_buf; - iov[0].iov_len = 4; - iov[1].iov_base = (char *)mid->resp_buf + 4; - iov[1].iov_len = len - 4; - /* FIXME: add code to kill session */ - rc = cifs_verify_signature(&rqst, server, - mid->sequence_number); - if (rc) - cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - - /* BB special case reconnect tid and uid here? */ - return map_and_check_smb_error(mid, log_error); -} - -struct mid_q_entry * -cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored, - struct smb_rqst *rqst) -{ - int rc; - struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return ERR_PTR(-EIO); - - rc = allocate_mid(ses, hdr, &mid); - if (rc) - return ERR_PTR(rc); - rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number); - if (rc) { - delete_mid(mid); - return ERR_PTR(rc); - } - return mid; -} - -static void -cifs_compound_callback(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->server; - struct cifs_credits credits; - - credits.value = server->ops->get_credits(mid); - credits.instance = server->reconnect_instance; - - add_credits(server, &credits, mid->optype); -} - -static void -cifs_compound_last_callback(struct mid_q_entry *mid) -{ - cifs_compound_callback(mid); - cifs_wake_up_task(mid); -} - -static void -cifs_cancelled_callback(struct mid_q_entry *mid) -{ - cifs_compound_callback(mid); - release_mid(mid); -} - -/* - * Return a channel (master if none) of @ses that can be used to send - * regular requests. - * - * If we are currently binding a new channel (negprot/sess.setup), - * return the new incomplete channel. - */ -struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) -{ - uint index = 0; - - if (!ses) - return NULL; - - /* round robin */ - index = (uint)atomic_inc_return(&ses->chan_seq); - - spin_lock(&ses->chan_lock); - index %= ses->chan_count; - spin_unlock(&ses->chan_lock); - - return ses->chans[index].server; -} - -int -compound_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const int flags, const int num_rqst, struct smb_rqst *rqst, - int *resp_buf_type, struct kvec *resp_iov) -{ - int i, j, optype, rc = 0; - struct mid_q_entry *midQ[MAX_COMPOUND]; - bool cancelled_mid[MAX_COMPOUND] = {false}; - struct cifs_credits credits[MAX_COMPOUND] = { - { .value = 0, .instance = 0 } - }; - unsigned int instance; - char *buf; - - optype = flags & CIFS_OP_MASK; - - for (i = 0; i < num_rqst; i++) - resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */ - - if (!ses || !ses->server || !server) { - cifs_dbg(VFS, "Null session\n"); - return -EIO; - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* - * Wait for all the requests to become available. - * This approach still leaves the possibility to be stuck waiting for - * credits if the server doesn't grant credits to the outstanding - * requests and if the client is completely idle, not generating any - * other requests. - * This can be handled by the eventual session reconnect. - */ - rc = wait_for_compound_request(server, num_rqst, flags, - &instance); - if (rc) - return rc; - - for (i = 0; i < num_rqst; i++) { - credits[i].value = 1; - credits[i].instance = instance; - } - - /* - * Make sure that we sign in the same order that we send on this socket - * and avoid races inside tcp sendmsg code that could cause corruption - * of smb data. - */ - - cifs_server_lock(server); - - /* - * All the parts of the compound chain belong obtained credits from the - * same session. We can not use credits obtained from the previous - * session to send this request. Check if there were reconnects after - * we obtained credits and return -EAGAIN in such cases to let callers - * handle it. - */ - if (instance != server->reconnect_instance) { - cifs_server_unlock(server); - for (j = 0; j < num_rqst; j++) - add_credits(server, &credits[j], optype); - return -EAGAIN; - } - - for (i = 0; i < num_rqst; i++) { - midQ[i] = server->ops->setup_request(ses, server, &rqst[i]); - if (IS_ERR(midQ[i])) { - revert_current_mid(server, i); - for (j = 0; j < i; j++) - delete_mid(midQ[j]); - cifs_server_unlock(server); - - /* Update # of requests on wire to server */ - for (j = 0; j < num_rqst; j++) - add_credits(server, &credits[j], optype); - return PTR_ERR(midQ[i]); - } - - midQ[i]->mid_state = MID_REQUEST_SUBMITTED; - midQ[i]->optype = optype; - /* - * Invoke callback for every part of the compound chain - * to calculate credits properly. Wake up this thread only when - * the last element is received. - */ - if (i < num_rqst - 1) - midQ[i]->callback = cifs_compound_callback; - else - midQ[i]->callback = cifs_compound_last_callback; - } - rc = smb_send_rqst(server, num_rqst, rqst, flags); - - for (i = 0; i < num_rqst; i++) - cifs_save_when_sent(midQ[i]); - - if (rc < 0) { - revert_current_mid(server, num_rqst); - server->sequence_number -= 2; - } - - cifs_server_unlock(server); - - /* - * If sending failed for some reason or it is an oplock break that we - * will not receive a response to - return credits back - */ - if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) { - for (i = 0; i < num_rqst; i++) - add_credits(server, &credits[i], optype); - goto out; - } - - /* - * At this point the request is passed to the network stack - we assume - * that any credits taken from the server structure on the client have - * been spent and we can't return them back. Once we receive responses - * we will collect credits granted by the server in the mid callbacks - * and add those credits to the server structure. - */ - - /* - * Compounding is never used during session establish. - */ - spin_lock(&ses->ses_lock); - if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { - spin_unlock(&ses->ses_lock); - - cifs_server_lock(server); - smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec); - cifs_server_unlock(server); - - spin_lock(&ses->ses_lock); - } - spin_unlock(&ses->ses_lock); - - for (i = 0; i < num_rqst; i++) { - rc = wait_for_response(server, midQ[i]); - if (rc != 0) - break; - } - if (rc != 0) { - for (; i < num_rqst; i++) { - cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n", - midQ[i]->mid, le16_to_cpu(midQ[i]->command)); - send_cancel(server, &rqst[i], midQ[i]); - spin_lock(&server->mid_lock); - midQ[i]->mid_flags |= MID_WAIT_CANCELLED; - if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { - midQ[i]->callback = cifs_cancelled_callback; - cancelled_mid[i] = true; - credits[i].value = 0; - } - spin_unlock(&server->mid_lock); - } - } - - for (i = 0; i < num_rqst; i++) { - if (rc < 0) - goto out; - - rc = cifs_sync_mid_result(midQ[i], server); - if (rc != 0) { - /* mark this mid as cancelled to not free it below */ - cancelled_mid[i] = true; - goto out; - } - - if (!midQ[i]->resp_buf || - midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { - rc = -EIO; - cifs_dbg(FYI, "Bad MID state?\n"); - goto out; - } - - buf = (char *)midQ[i]->resp_buf; - resp_iov[i].iov_base = buf; - resp_iov[i].iov_len = midQ[i]->resp_buf_size + - HEADER_PREAMBLE_SIZE(server); - - if (midQ[i]->large_buf) - resp_buf_type[i] = CIFS_LARGE_BUFFER; - else - resp_buf_type[i] = CIFS_SMALL_BUFFER; - - rc = server->ops->check_receive(midQ[i], server, - flags & CIFS_LOG_ERROR); - - /* mark it so buf will not be freed by delete_mid */ - if ((flags & CIFS_NO_RSP_BUF) == 0) - midQ[i]->resp_buf = NULL; - - } - - /* - * Compounding is never used during session establish. - */ - spin_lock(&ses->ses_lock); - if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { - struct kvec iov = { - .iov_base = resp_iov[0].iov_base, - .iov_len = resp_iov[0].iov_len - }; - spin_unlock(&ses->ses_lock); - cifs_server_lock(server); - smb311_update_preauth_hash(ses, server, &iov, 1); - cifs_server_unlock(server); - spin_lock(&ses->ses_lock); - } - spin_unlock(&ses->ses_lock); - -out: - /* - * This will dequeue all mids. After this it is important that the - * demultiplex_thread will not process any of these mids any futher. - * This is prevented above by using a noop callback that will not - * wake this thread except for the very last PDU. - */ - for (i = 0; i < num_rqst; i++) { - if (!cancelled_mid[i]) - delete_mid(midQ[i]); - } - - return rc; -} - -int -cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, int *resp_buf_type, const int flags, - struct kvec *resp_iov) -{ - return compound_send_recv(xid, ses, server, flags, 1, - rqst, resp_buf_type, resp_iov); -} - -int -SendReceive2(const unsigned int xid, struct cifs_ses *ses, - struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, - const int flags, struct kvec *resp_iov) -{ - struct smb_rqst rqst; - struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov; - int rc; - - if (n_vec + 1 > CIFS_MAX_IOV_SIZE) { - new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec), - GFP_KERNEL); - if (!new_iov) { - /* otherwise cifs_send_recv below sets resp_buf_type */ - *resp_buf_type = CIFS_NO_BUFFER; - return -ENOMEM; - } - } else - new_iov = s_iov; - - /* 1st iov is a RFC1001 length followed by the rest of the packet */ - memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec)); - - new_iov[0].iov_base = new_iov[1].iov_base; - new_iov[0].iov_len = 4; - new_iov[1].iov_base += 4; - new_iov[1].iov_len -= 4; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = new_iov; - rqst.rq_nvec = n_vec + 1; - - rc = cifs_send_recv(xid, ses, ses->server, - &rqst, resp_buf_type, flags, resp_iov); - if (n_vec + 1 > CIFS_MAX_IOV_SIZE) - kfree(new_iov); - return rc; -} - -int -SendReceive(const unsigned int xid, struct cifs_ses *ses, - struct smb_hdr *in_buf, struct smb_hdr *out_buf, - int *pbytes_returned, const int flags) -{ - int rc = 0; - struct mid_q_entry *midQ; - unsigned int len = be32_to_cpu(in_buf->smb_buf_length); - struct kvec iov = { .iov_base = in_buf, .iov_len = len }; - struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - struct TCP_Server_Info *server; - - if (ses == NULL) { - cifs_dbg(VFS, "Null smb session\n"); - return -EIO; - } - server = ses->server; - if (server == NULL) { - cifs_dbg(VFS, "Null tcp session\n"); - return -EIO; - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* Ensure that we do not send more than 50 overlapping requests - to the same server. We may make this configurable later or - use ses->maxReq */ - - if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", - len); - return -EIO; - } - - rc = wait_for_free_request(server, flags, &credits.instance); - if (rc) - return rc; - - /* make sure that we sign in the same order that we send on this socket - and avoid races inside tcp sendmsg code that could cause corruption - of smb data */ - - cifs_server_lock(server); - - rc = allocate_mid(ses, in_buf, &midQ); - if (rc) { - cifs_server_unlock(server); - /* Update # of requests on wire to server */ - add_credits(server, &credits, 0); - return rc; - } - - rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); - if (rc) { - cifs_server_unlock(server); - goto out; - } - - midQ->mid_state = MID_REQUEST_SUBMITTED; - - rc = smb_send(server, in_buf, len); - cifs_save_when_sent(midQ); - - if (rc < 0) - server->sequence_number -= 2; - - cifs_server_unlock(server); - - if (rc < 0) - goto out; - - rc = wait_for_response(server, midQ); - if (rc != 0) { - send_cancel(server, &rqst, midQ); - spin_lock(&server->mid_lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED) { - /* no longer considered to be "in-flight" */ - midQ->callback = release_mid; - spin_unlock(&server->mid_lock); - add_credits(server, &credits, 0); - return rc; - } - spin_unlock(&server->mid_lock); - } - - rc = cifs_sync_mid_result(midQ, server); - if (rc != 0) { - add_credits(server, &credits, 0); - return rc; - } - - if (!midQ->resp_buf || !out_buf || - midQ->mid_state != MID_RESPONSE_RECEIVED) { - rc = -EIO; - cifs_server_dbg(VFS, "Bad MID state?\n"); - goto out; - } - - *pbytes_returned = get_rfc1002_length(midQ->resp_buf); - memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); - rc = cifs_check_receive(midQ, server, 0); -out: - delete_mid(midQ); - add_credits(server, &credits, 0); - - return rc; -} - -/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows - blocking lock to return. */ - -static int -send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon, - struct smb_hdr *in_buf, - struct smb_hdr *out_buf) -{ - int bytes_returned; - struct cifs_ses *ses = tcon->ses; - LOCK_REQ *pSMB = (LOCK_REQ *)in_buf; - - /* We just modify the current in_buf to change - the type of lock from LOCKING_ANDX_SHARED_LOCK - or LOCKING_ANDX_EXCLUSIVE_LOCK to - LOCKING_ANDX_CANCEL_LOCK. */ - - pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES; - pSMB->Timeout = 0; - pSMB->hdr.Mid = get_next_mid(ses->server); - - return SendReceive(xid, ses, in_buf, out_buf, - &bytes_returned, 0); -} - -int -SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, - struct smb_hdr *in_buf, struct smb_hdr *out_buf, - int *pbytes_returned) -{ - int rc = 0; - int rstart = 0; - struct mid_q_entry *midQ; - struct cifs_ses *ses; - unsigned int len = be32_to_cpu(in_buf->smb_buf_length); - struct kvec iov = { .iov_base = in_buf, .iov_len = len }; - struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; - unsigned int instance; - struct TCP_Server_Info *server; - - if (tcon == NULL || tcon->ses == NULL) { - cifs_dbg(VFS, "Null smb session\n"); - return -EIO; - } - ses = tcon->ses; - server = ses->server; - - if (server == NULL) { - cifs_dbg(VFS, "Null tcp session\n"); - return -EIO; - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* Ensure that we do not send more than 50 overlapping requests - to the same server. We may make this configurable later or - use ses->maxReq */ - - if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", - len); - return -EIO; - } - - rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance); - if (rc) - return rc; - - /* make sure that we sign in the same order that we send on this socket - and avoid races inside tcp sendmsg code that could cause corruption - of smb data */ - - cifs_server_lock(server); - - rc = allocate_mid(ses, in_buf, &midQ); - if (rc) { - cifs_server_unlock(server); - return rc; - } - - rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); - if (rc) { - delete_mid(midQ); - cifs_server_unlock(server); - return rc; - } - - midQ->mid_state = MID_REQUEST_SUBMITTED; - rc = smb_send(server, in_buf, len); - cifs_save_when_sent(midQ); - - if (rc < 0) - server->sequence_number -= 2; - - cifs_server_unlock(server); - - if (rc < 0) { - delete_mid(midQ); - return rc; - } - - /* Wait for a reply - allow signals to interrupt. */ - rc = wait_event_interruptible(server->response_q, - (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || - ((server->tcpStatus != CifsGood) && - (server->tcpStatus != CifsNew))); - - /* Were we interrupted by a signal ? */ - spin_lock(&server->srv_lock); - if ((rc == -ERESTARTSYS) && - (midQ->mid_state == MID_REQUEST_SUBMITTED) && - ((server->tcpStatus == CifsGood) || - (server->tcpStatus == CifsNew))) { - spin_unlock(&server->srv_lock); - - if (in_buf->Command == SMB_COM_TRANSACTION2) { - /* POSIX lock. We send a NT_CANCEL SMB to cause the - blocking lock to return. */ - rc = send_cancel(server, &rqst, midQ); - if (rc) { - delete_mid(midQ); - return rc; - } - } else { - /* Windows lock. We send a LOCKINGX_CANCEL_LOCK - to cause the blocking lock to return. */ - - rc = send_lock_cancel(xid, tcon, in_buf, out_buf); - - /* If we get -ENOLCK back the lock may have - already been removed. Don't exit in this case. */ - if (rc && rc != -ENOLCK) { - delete_mid(midQ); - return rc; - } - } - - rc = wait_for_response(server, midQ); - if (rc) { - send_cancel(server, &rqst, midQ); - spin_lock(&server->mid_lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED) { - /* no longer considered to be "in-flight" */ - midQ->callback = release_mid; - spin_unlock(&server->mid_lock); - return rc; - } - spin_unlock(&server->mid_lock); - } - - /* We got the response - restart system call. */ - rstart = 1; - spin_lock(&server->srv_lock); - } - spin_unlock(&server->srv_lock); - - rc = cifs_sync_mid_result(midQ, server); - if (rc != 0) - return rc; - - /* rcvd frame is ok */ - if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { - rc = -EIO; - cifs_tcon_dbg(VFS, "Bad MID state?\n"); - goto out; - } - - *pbytes_returned = get_rfc1002_length(midQ->resp_buf); - memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); - rc = cifs_check_receive(midQ, server, 0); -out: - delete_mid(midQ); - if (rstart && rc == -EACCES) - return -ERESTARTSYS; - return rc; -} - -/* - * Discard any remaining data in the current SMB. To do this, we borrow the - * current bigbuf. - */ -int -cifs_discard_remaining_data(struct TCP_Server_Info *server) -{ - unsigned int rfclen = server->pdu_size; - int remaining = rfclen + HEADER_PREAMBLE_SIZE(server) - - server->total_read; - - while (remaining > 0) { - int length; - - length = cifs_discard_from_socket(server, - min_t(size_t, remaining, - CIFSMaxBufSize + MAX_HEADER_SIZE(server))); - if (length < 0) - return length; - server->total_read += length; - remaining -= length; - } - - return 0; -} - -static int -__cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid, - bool malformed) -{ - int length; - - length = cifs_discard_remaining_data(server); - dequeue_mid(mid, malformed); - mid->resp_buf = server->smallbuf; - server->smallbuf = NULL; - return length; -} - -static int -cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - struct cifs_readdata *rdata = mid->callback_data; - - return __cifs_readv_discard(server, mid, rdata->result); -} - -int -cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - int length, len; - unsigned int data_offset, data_len; - struct cifs_readdata *rdata = mid->callback_data; - char *buf = server->smallbuf; - unsigned int buflen = server->pdu_size + HEADER_PREAMBLE_SIZE(server); - bool use_rdma_mr = false; - - cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n", - __func__, mid->mid, rdata->offset, rdata->bytes); - - /* - * read the rest of READ_RSP header (sans Data array), or whatever we - * can if there's not enough data. At this point, we've read down to - * the Mid. - */ - len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - - HEADER_SIZE(server) + 1; - - length = cifs_read_from_socket(server, - buf + HEADER_SIZE(server) - 1, len); - if (length < 0) - return length; - server->total_read += length; - - if (server->ops->is_session_expired && - server->ops->is_session_expired(buf)) { - cifs_reconnect(server, true); - return -1; - } - - if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server)) { - cifs_discard_remaining_data(server); - return -1; - } - - /* set up first two iov for signature check and to get credits */ - rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = HEADER_PREAMBLE_SIZE(server); - rdata->iov[1].iov_base = buf + HEADER_PREAMBLE_SIZE(server); - rdata->iov[1].iov_len = - server->total_read - HEADER_PREAMBLE_SIZE(server); - cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", - rdata->iov[0].iov_base, rdata->iov[0].iov_len); - cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", - rdata->iov[1].iov_base, rdata->iov[1].iov_len); - - /* Was the SMB read successful? */ - rdata->result = server->ops->map_error(buf, false); - if (rdata->result != 0) { - cifs_dbg(FYI, "%s: server returned error %d\n", - __func__, rdata->result); - /* normal error on read response */ - return __cifs_readv_discard(server, mid, false); - } - - /* Is there enough to get to the rest of the READ_RSP header? */ - if (server->total_read < server->vals->read_rsp_size) { - cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n", - __func__, server->total_read, - server->vals->read_rsp_size); - rdata->result = -EIO; - return cifs_readv_discard(server, mid); - } - - data_offset = server->ops->read_data_offset(buf) + - HEADER_PREAMBLE_SIZE(server); - if (data_offset < server->total_read) { - /* - * win2k8 sometimes sends an offset of 0 when the read - * is beyond the EOF. Treat it as if the data starts just after - * the header. - */ - cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", - __func__, data_offset); - data_offset = server->total_read; - } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { - /* data_offset is beyond the end of smallbuf */ - cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", - __func__, data_offset); - rdata->result = -EIO; - return cifs_readv_discard(server, mid); - } - - cifs_dbg(FYI, "%s: total_read=%u data_offset=%u\n", - __func__, server->total_read, data_offset); - - len = data_offset - server->total_read; - if (len > 0) { - /* read any junk before data into the rest of smallbuf */ - length = cifs_read_from_socket(server, - buf + server->total_read, len); - if (length < 0) - return length; - server->total_read += length; - } - - /* how much data is in the response? */ -#ifdef CONFIG_CIFS_SMB_DIRECT - use_rdma_mr = rdata->mr; -#endif - data_len = server->ops->read_data_length(buf, use_rdma_mr); - if (!use_rdma_mr && (data_offset + data_len > buflen)) { - /* data_len is corrupt -- discard frame */ - rdata->result = -EIO; - return cifs_readv_discard(server, mid); - } - - length = rdata->read_into_pages(server, rdata, data_len); - if (length < 0) - return length; - - server->total_read += length; - - cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n", - server->total_read, buflen, data_len); - - /* discard anything left over */ - if (server->total_read < buflen) - return cifs_readv_discard(server, mid); - - dequeue_mid(mid, false); - mid->resp_buf = server->smallbuf; - server->smallbuf = NULL; - return length; -} diff --git a/fs/cifs/unc.c b/fs/cifs/unc.c deleted file mode 100644 index f6fc5e343ea4..000000000000 --- a/fs/cifs/unc.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2020, Microsoft Corporation. - * - * Author(s): Steve French - * Suresh Jayaraman - * Jeff Layton - */ - -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" - -/* extract the host portion of the UNC string */ -char *extract_hostname(const char *unc) -{ - const char *src; - char *dst, *delim; - unsigned int len; - - /* skip double chars at beginning of string */ - /* BB: check validity of these bytes? */ - if (strlen(unc) < 3) - return ERR_PTR(-EINVAL); - for (src = unc; *src && *src == '\\'; src++) - ; - if (!*src) - return ERR_PTR(-EINVAL); - - /* delimiter between hostname and sharename is always '\\' now */ - delim = strchr(src, '\\'); - if (!delim) - return ERR_PTR(-EINVAL); - - len = delim - src; - dst = kmalloc((len + 1), GFP_KERNEL); - if (dst == NULL) - return ERR_PTR(-ENOMEM); - - memcpy(dst, src, len); - dst[len] = '\0'; - - return dst; -} - -char *extract_sharename(const char *unc) -{ - const char *src; - char *delim, *dst; - - /* skip double chars at the beginning */ - src = unc + 2; - - /* share name is always preceded by '\\' now */ - delim = strchr(src, '\\'); - if (!delim) - return ERR_PTR(-EINVAL); - delim++; - - /* caller has to free the memory */ - dst = kstrdup(delim, GFP_KERNEL); - if (!dst) - return ERR_PTR(-ENOMEM); - - return dst; -} diff --git a/fs/cifs/winucase.c b/fs/cifs/winucase.c deleted file mode 100644 index 2f075b5b50df..000000000000 --- a/fs/cifs/winucase.c +++ /dev/null @@ -1,649 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) Jeffrey Layton , 2013 - * - * The const tables in this file were converted from the following info - * provided by Microsoft: - * - * 3.1.5.3 Mapping UTF-16 Strings to Upper Case: - * - * https://msdn.microsoft.com/en-us/library/hh877830.aspx - * http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=10921 - * - * In particular, the table in "Windows 8 Upper Case Mapping Table.txt" was - * post-processed using the winucase_convert.pl script. - */ - -#include - -wchar_t cifs_toupper(wchar_t in); /* quiet sparse */ - -static const wchar_t t2_00[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, - 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, - 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0000, - 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178, -}; - -static const wchar_t t2_01[256] = { - 0x0000, 0x0100, 0x0000, 0x0102, 0x0000, 0x0104, 0x0000, 0x0106, - 0x0000, 0x0108, 0x0000, 0x010a, 0x0000, 0x010c, 0x0000, 0x010e, - 0x0000, 0x0110, 0x0000, 0x0112, 0x0000, 0x0114, 0x0000, 0x0116, - 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x011c, 0x0000, 0x011e, - 0x0000, 0x0120, 0x0000, 0x0122, 0x0000, 0x0124, 0x0000, 0x0126, - 0x0000, 0x0128, 0x0000, 0x012a, 0x0000, 0x012c, 0x0000, 0x012e, - 0x0000, 0x0000, 0x0000, 0x0132, 0x0000, 0x0134, 0x0000, 0x0136, - 0x0000, 0x0000, 0x0139, 0x0000, 0x013b, 0x0000, 0x013d, 0x0000, - 0x013f, 0x0000, 0x0141, 0x0000, 0x0143, 0x0000, 0x0145, 0x0000, - 0x0147, 0x0000, 0x0000, 0x014a, 0x0000, 0x014c, 0x0000, 0x014e, - 0x0000, 0x0150, 0x0000, 0x0152, 0x0000, 0x0154, 0x0000, 0x0156, - 0x0000, 0x0158, 0x0000, 0x015a, 0x0000, 0x015c, 0x0000, 0x015e, - 0x0000, 0x0160, 0x0000, 0x0162, 0x0000, 0x0164, 0x0000, 0x0166, - 0x0000, 0x0168, 0x0000, 0x016a, 0x0000, 0x016c, 0x0000, 0x016e, - 0x0000, 0x0170, 0x0000, 0x0172, 0x0000, 0x0174, 0x0000, 0x0176, - 0x0000, 0x0000, 0x0179, 0x0000, 0x017b, 0x0000, 0x017d, 0x0000, - 0x0243, 0x0000, 0x0000, 0x0182, 0x0000, 0x0184, 0x0000, 0x0000, - 0x0187, 0x0000, 0x0000, 0x0000, 0x018b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0191, 0x0000, 0x0000, 0x01f6, 0x0000, 0x0000, - 0x0000, 0x0198, 0x023d, 0x0000, 0x0000, 0x0000, 0x0220, 0x0000, - 0x0000, 0x01a0, 0x0000, 0x01a2, 0x0000, 0x01a4, 0x0000, 0x0000, - 0x01a7, 0x0000, 0x0000, 0x0000, 0x0000, 0x01ac, 0x0000, 0x0000, - 0x01af, 0x0000, 0x0000, 0x0000, 0x01b3, 0x0000, 0x01b5, 0x0000, - 0x0000, 0x01b8, 0x0000, 0x0000, 0x0000, 0x01bc, 0x0000, 0x01f7, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01c4, 0x0000, - 0x0000, 0x01c7, 0x0000, 0x0000, 0x01ca, 0x0000, 0x01cd, 0x0000, - 0x01cf, 0x0000, 0x01d1, 0x0000, 0x01d3, 0x0000, 0x01d5, 0x0000, - 0x01d7, 0x0000, 0x01d9, 0x0000, 0x01db, 0x018e, 0x0000, 0x01de, - 0x0000, 0x01e0, 0x0000, 0x01e2, 0x0000, 0x01e4, 0x0000, 0x01e6, - 0x0000, 0x01e8, 0x0000, 0x01ea, 0x0000, 0x01ec, 0x0000, 0x01ee, - 0x0000, 0x0000, 0x0000, 0x01f1, 0x0000, 0x01f4, 0x0000, 0x0000, - 0x0000, 0x01f8, 0x0000, 0x01fa, 0x0000, 0x01fc, 0x0000, 0x01fe, -}; - -static const wchar_t t2_02[256] = { - 0x0000, 0x0200, 0x0000, 0x0202, 0x0000, 0x0204, 0x0000, 0x0206, - 0x0000, 0x0208, 0x0000, 0x020a, 0x0000, 0x020c, 0x0000, 0x020e, - 0x0000, 0x0210, 0x0000, 0x0212, 0x0000, 0x0214, 0x0000, 0x0216, - 0x0000, 0x0218, 0x0000, 0x021a, 0x0000, 0x021c, 0x0000, 0x021e, - 0x0000, 0x0000, 0x0000, 0x0222, 0x0000, 0x0224, 0x0000, 0x0226, - 0x0000, 0x0228, 0x0000, 0x022a, 0x0000, 0x022c, 0x0000, 0x022e, - 0x0000, 0x0230, 0x0000, 0x0232, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x023b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0241, 0x0000, 0x0000, 0x0000, 0x0000, 0x0246, - 0x0000, 0x0248, 0x0000, 0x024a, 0x0000, 0x024c, 0x0000, 0x024e, - 0x2c6f, 0x2c6d, 0x0000, 0x0181, 0x0186, 0x0000, 0x0189, 0x018a, - 0x0000, 0x018f, 0x0000, 0x0190, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0193, 0x0000, 0x0000, 0x0194, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0197, 0x0196, 0x0000, 0x2c62, 0x0000, 0x0000, 0x0000, 0x019c, - 0x0000, 0x2c6e, 0x019d, 0x0000, 0x0000, 0x019f, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2c64, 0x0000, 0x0000, - 0x01a6, 0x0000, 0x0000, 0x01a9, 0x0000, 0x0000, 0x0000, 0x0000, - 0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x01b7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_03[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0370, 0x0000, 0x0372, 0x0000, 0x0000, 0x0000, 0x0376, - 0x0000, 0x0000, 0x0000, 0x03fd, 0x03fe, 0x03ff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, - 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03cf, - 0x0000, 0x03d8, 0x0000, 0x03da, 0x0000, 0x03dc, 0x0000, 0x03de, - 0x0000, 0x03e0, 0x0000, 0x03e2, 0x0000, 0x03e4, 0x0000, 0x03e6, - 0x0000, 0x03e8, 0x0000, 0x03ea, 0x0000, 0x03ec, 0x0000, 0x03ee, - 0x0000, 0x0000, 0x03f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x03f7, 0x0000, 0x0000, 0x03fa, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_04[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, - 0x0000, 0x0460, 0x0000, 0x0462, 0x0000, 0x0464, 0x0000, 0x0466, - 0x0000, 0x0468, 0x0000, 0x046a, 0x0000, 0x046c, 0x0000, 0x046e, - 0x0000, 0x0470, 0x0000, 0x0472, 0x0000, 0x0474, 0x0000, 0x0476, - 0x0000, 0x0478, 0x0000, 0x047a, 0x0000, 0x047c, 0x0000, 0x047e, - 0x0000, 0x0480, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x048a, 0x0000, 0x048c, 0x0000, 0x048e, - 0x0000, 0x0490, 0x0000, 0x0492, 0x0000, 0x0494, 0x0000, 0x0496, - 0x0000, 0x0498, 0x0000, 0x049a, 0x0000, 0x049c, 0x0000, 0x049e, - 0x0000, 0x04a0, 0x0000, 0x04a2, 0x0000, 0x04a4, 0x0000, 0x04a6, - 0x0000, 0x04a8, 0x0000, 0x04aa, 0x0000, 0x04ac, 0x0000, 0x04ae, - 0x0000, 0x04b0, 0x0000, 0x04b2, 0x0000, 0x04b4, 0x0000, 0x04b6, - 0x0000, 0x04b8, 0x0000, 0x04ba, 0x0000, 0x04bc, 0x0000, 0x04be, - 0x0000, 0x0000, 0x04c1, 0x0000, 0x04c3, 0x0000, 0x04c5, 0x0000, - 0x04c7, 0x0000, 0x04c9, 0x0000, 0x04cb, 0x0000, 0x04cd, 0x04c0, - 0x0000, 0x04d0, 0x0000, 0x04d2, 0x0000, 0x04d4, 0x0000, 0x04d6, - 0x0000, 0x04d8, 0x0000, 0x04da, 0x0000, 0x04dc, 0x0000, 0x04de, - 0x0000, 0x04e0, 0x0000, 0x04e2, 0x0000, 0x04e4, 0x0000, 0x04e6, - 0x0000, 0x04e8, 0x0000, 0x04ea, 0x0000, 0x04ec, 0x0000, 0x04ee, - 0x0000, 0x04f0, 0x0000, 0x04f2, 0x0000, 0x04f4, 0x0000, 0x04f6, - 0x0000, 0x04f8, 0x0000, 0x04fa, 0x0000, 0x04fc, 0x0000, 0x04fe, -}; - -static const wchar_t t2_05[256] = { - 0x0000, 0x0500, 0x0000, 0x0502, 0x0000, 0x0504, 0x0000, 0x0506, - 0x0000, 0x0508, 0x0000, 0x050a, 0x0000, 0x050c, 0x0000, 0x050e, - 0x0000, 0x0510, 0x0000, 0x0512, 0x0000, 0x0514, 0x0000, 0x0516, - 0x0000, 0x0518, 0x0000, 0x051a, 0x0000, 0x051c, 0x0000, 0x051e, - 0x0000, 0x0520, 0x0000, 0x0522, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, - 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, - 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, - 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, - 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_1d[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xa77d, 0x0000, 0x0000, 0x0000, 0x2c63, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_1e[256] = { - 0x0000, 0x1e00, 0x0000, 0x1e02, 0x0000, 0x1e04, 0x0000, 0x1e06, - 0x0000, 0x1e08, 0x0000, 0x1e0a, 0x0000, 0x1e0c, 0x0000, 0x1e0e, - 0x0000, 0x1e10, 0x0000, 0x1e12, 0x0000, 0x1e14, 0x0000, 0x1e16, - 0x0000, 0x1e18, 0x0000, 0x1e1a, 0x0000, 0x1e1c, 0x0000, 0x1e1e, - 0x0000, 0x1e20, 0x0000, 0x1e22, 0x0000, 0x1e24, 0x0000, 0x1e26, - 0x0000, 0x1e28, 0x0000, 0x1e2a, 0x0000, 0x1e2c, 0x0000, 0x1e2e, - 0x0000, 0x1e30, 0x0000, 0x1e32, 0x0000, 0x1e34, 0x0000, 0x1e36, - 0x0000, 0x1e38, 0x0000, 0x1e3a, 0x0000, 0x1e3c, 0x0000, 0x1e3e, - 0x0000, 0x1e40, 0x0000, 0x1e42, 0x0000, 0x1e44, 0x0000, 0x1e46, - 0x0000, 0x1e48, 0x0000, 0x1e4a, 0x0000, 0x1e4c, 0x0000, 0x1e4e, - 0x0000, 0x1e50, 0x0000, 0x1e52, 0x0000, 0x1e54, 0x0000, 0x1e56, - 0x0000, 0x1e58, 0x0000, 0x1e5a, 0x0000, 0x1e5c, 0x0000, 0x1e5e, - 0x0000, 0x1e60, 0x0000, 0x1e62, 0x0000, 0x1e64, 0x0000, 0x1e66, - 0x0000, 0x1e68, 0x0000, 0x1e6a, 0x0000, 0x1e6c, 0x0000, 0x1e6e, - 0x0000, 0x1e70, 0x0000, 0x1e72, 0x0000, 0x1e74, 0x0000, 0x1e76, - 0x0000, 0x1e78, 0x0000, 0x1e7a, 0x0000, 0x1e7c, 0x0000, 0x1e7e, - 0x0000, 0x1e80, 0x0000, 0x1e82, 0x0000, 0x1e84, 0x0000, 0x1e86, - 0x0000, 0x1e88, 0x0000, 0x1e8a, 0x0000, 0x1e8c, 0x0000, 0x1e8e, - 0x0000, 0x1e90, 0x0000, 0x1e92, 0x0000, 0x1e94, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x1ea0, 0x0000, 0x1ea2, 0x0000, 0x1ea4, 0x0000, 0x1ea6, - 0x0000, 0x1ea8, 0x0000, 0x1eaa, 0x0000, 0x1eac, 0x0000, 0x1eae, - 0x0000, 0x1eb0, 0x0000, 0x1eb2, 0x0000, 0x1eb4, 0x0000, 0x1eb6, - 0x0000, 0x1eb8, 0x0000, 0x1eba, 0x0000, 0x1ebc, 0x0000, 0x1ebe, - 0x0000, 0x1ec0, 0x0000, 0x1ec2, 0x0000, 0x1ec4, 0x0000, 0x1ec6, - 0x0000, 0x1ec8, 0x0000, 0x1eca, 0x0000, 0x1ecc, 0x0000, 0x1ece, - 0x0000, 0x1ed0, 0x0000, 0x1ed2, 0x0000, 0x1ed4, 0x0000, 0x1ed6, - 0x0000, 0x1ed8, 0x0000, 0x1eda, 0x0000, 0x1edc, 0x0000, 0x1ede, - 0x0000, 0x1ee0, 0x0000, 0x1ee2, 0x0000, 0x1ee4, 0x0000, 0x1ee6, - 0x0000, 0x1ee8, 0x0000, 0x1eea, 0x0000, 0x1eec, 0x0000, 0x1eee, - 0x0000, 0x1ef0, 0x0000, 0x1ef2, 0x0000, 0x1ef4, 0x0000, 0x1ef6, - 0x0000, 0x1ef8, 0x0000, 0x1efa, 0x0000, 0x1efc, 0x0000, 0x1efe, -}; - -static const wchar_t t2_1f[256] = { - 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x1f59, 0x0000, 0x1f5b, 0x0000, 0x1f5d, 0x0000, 0x1f5f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, - 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x0000, 0x0000, - 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fb8, 0x1fb9, 0x0000, 0x1fbc, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1fcc, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fd8, 0x1fd9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fe8, 0x1fe9, 0x0000, 0x0000, 0x0000, 0x1fec, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1ffc, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_21[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2132, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, - 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x2183, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_24[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, - 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, - 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, - 0x24ce, 0x24cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_2c[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, - 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, - 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, - 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, - 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, - 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x0000, - 0x0000, 0x2c60, 0x0000, 0x0000, 0x0000, 0x023a, 0x023e, 0x0000, - 0x2c67, 0x0000, 0x2c69, 0x0000, 0x2c6b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x2c72, 0x0000, 0x0000, 0x2c75, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x2c80, 0x0000, 0x2c82, 0x0000, 0x2c84, 0x0000, 0x2c86, - 0x0000, 0x2c88, 0x0000, 0x2c8a, 0x0000, 0x2c8c, 0x0000, 0x2c8e, - 0x0000, 0x2c90, 0x0000, 0x2c92, 0x0000, 0x2c94, 0x0000, 0x2c96, - 0x0000, 0x2c98, 0x0000, 0x2c9a, 0x0000, 0x2c9c, 0x0000, 0x2c9e, - 0x0000, 0x2ca0, 0x0000, 0x2ca2, 0x0000, 0x2ca4, 0x0000, 0x2ca6, - 0x0000, 0x2ca8, 0x0000, 0x2caa, 0x0000, 0x2cac, 0x0000, 0x2cae, - 0x0000, 0x2cb0, 0x0000, 0x2cb2, 0x0000, 0x2cb4, 0x0000, 0x2cb6, - 0x0000, 0x2cb8, 0x0000, 0x2cba, 0x0000, 0x2cbc, 0x0000, 0x2cbe, - 0x0000, 0x2cc0, 0x0000, 0x2cc2, 0x0000, 0x2cc4, 0x0000, 0x2cc6, - 0x0000, 0x2cc8, 0x0000, 0x2cca, 0x0000, 0x2ccc, 0x0000, 0x2cce, - 0x0000, 0x2cd0, 0x0000, 0x2cd2, 0x0000, 0x2cd4, 0x0000, 0x2cd6, - 0x0000, 0x2cd8, 0x0000, 0x2cda, 0x0000, 0x2cdc, 0x0000, 0x2cde, - 0x0000, 0x2ce0, 0x0000, 0x2ce2, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_2d[256] = { - 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, - 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, - 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, - 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, - 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_a6[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xa640, 0x0000, 0xa642, 0x0000, 0xa644, 0x0000, 0xa646, - 0x0000, 0xa648, 0x0000, 0xa64a, 0x0000, 0xa64c, 0x0000, 0xa64e, - 0x0000, 0xa650, 0x0000, 0xa652, 0x0000, 0xa654, 0x0000, 0xa656, - 0x0000, 0xa658, 0x0000, 0xa65a, 0x0000, 0xa65c, 0x0000, 0xa65e, - 0x0000, 0x0000, 0x0000, 0xa662, 0x0000, 0xa664, 0x0000, 0xa666, - 0x0000, 0xa668, 0x0000, 0xa66a, 0x0000, 0xa66c, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xa680, 0x0000, 0xa682, 0x0000, 0xa684, 0x0000, 0xa686, - 0x0000, 0xa688, 0x0000, 0xa68a, 0x0000, 0xa68c, 0x0000, 0xa68e, - 0x0000, 0xa690, 0x0000, 0xa692, 0x0000, 0xa694, 0x0000, 0xa696, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_a7[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0xa722, 0x0000, 0xa724, 0x0000, 0xa726, - 0x0000, 0xa728, 0x0000, 0xa72a, 0x0000, 0xa72c, 0x0000, 0xa72e, - 0x0000, 0x0000, 0x0000, 0xa732, 0x0000, 0xa734, 0x0000, 0xa736, - 0x0000, 0xa738, 0x0000, 0xa73a, 0x0000, 0xa73c, 0x0000, 0xa73e, - 0x0000, 0xa740, 0x0000, 0xa742, 0x0000, 0xa744, 0x0000, 0xa746, - 0x0000, 0xa748, 0x0000, 0xa74a, 0x0000, 0xa74c, 0x0000, 0xa74e, - 0x0000, 0xa750, 0x0000, 0xa752, 0x0000, 0xa754, 0x0000, 0xa756, - 0x0000, 0xa758, 0x0000, 0xa75a, 0x0000, 0xa75c, 0x0000, 0xa75e, - 0x0000, 0xa760, 0x0000, 0xa762, 0x0000, 0xa764, 0x0000, 0xa766, - 0x0000, 0xa768, 0x0000, 0xa76a, 0x0000, 0xa76c, 0x0000, 0xa76e, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0xa779, 0x0000, 0xa77b, 0x0000, 0x0000, 0xa77e, - 0x0000, 0xa780, 0x0000, 0xa782, 0x0000, 0xa784, 0x0000, 0xa786, - 0x0000, 0x0000, 0x0000, 0x0000, 0xa78b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_ff[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, - 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, - 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, - 0xff38, 0xff39, 0xff3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t *const toplevel[256] = { - t2_00, t2_01, t2_02, t2_03, t2_04, t2_05, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, t2_1d, t2_1e, t2_1f, - NULL, t2_21, NULL, NULL, t2_24, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, t2_2c, t2_2d, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, t2_a6, t2_a7, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, t2_ff, -}; - -/** - * cifs_toupper - convert a wchar_t from lower to uppercase - * @in: character to convert from lower to uppercase - * - * This function consults the static tables above to convert a wchar_t from - * lower to uppercase. In the event that there is no mapping, the original - * "in" character is returned. - */ -wchar_t -cifs_toupper(wchar_t in) -{ - unsigned char idx; - const wchar_t *tbl; - wchar_t out; - - /* grab upper byte */ - idx = (in & 0xff00) >> 8; - - /* find pointer to 2nd layer table */ - tbl = toplevel[idx]; - if (!tbl) - return in; - - /* grab lower byte */ - idx = in & 0xff; - - /* look up character in table */ - out = tbl[idx]; - if (out) - return out; - - return in; -} diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c deleted file mode 100644 index 998fa51f9b68..000000000000 --- a/fs/cifs/xattr.c +++ /dev/null @@ -1,555 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (c) International Business Machines Corp., 2003, 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "cifs_ioctl.h" - -#define MAX_EA_VALUE_SIZE CIFSMaxBufSize -#define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */ -#define CIFS_XATTR_CIFS_NTSD "system.cifs_ntsd" /* owner plus DACL */ -#define CIFS_XATTR_CIFS_NTSD_FULL "system.cifs_ntsd_full" /* owner/DACL/SACL */ -#define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ -#define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ -/* - * Although these three are just aliases for the above, need to move away from - * confusing users and using the 20+ year old term 'cifs' when it is no longer - * secure, replaced by SMB2 (then even more highly secure SMB3) many years ago - */ -#define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */ -#define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */ -#define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */ -#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */ -#define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */ -/* BB need to add server (Samba e.g) support for security and trusted prefix */ - -enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT, - XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL }; - -static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon, - struct inode *inode, const char *full_path, - const void *value, size_t size) -{ - ssize_t rc = -EOPNOTSUPP; - __u32 *pattrib = (__u32 *)value; - __u32 attrib; - FILE_BASIC_INFO info_buf; - - if ((value == NULL) || (size != sizeof(__u32))) - return -ERANGE; - - memset(&info_buf, 0, sizeof(info_buf)); - attrib = *pattrib; - info_buf.Attributes = cpu_to_le32(attrib); - if (pTcon->ses->server->ops->set_file_info) - rc = pTcon->ses->server->ops->set_file_info(inode, full_path, - &info_buf, xid); - if (rc == 0) - CIFS_I(inode)->cifsAttrs = attrib; - - return rc; -} - -static int cifs_creation_time_set(unsigned int xid, struct cifs_tcon *pTcon, - struct inode *inode, const char *full_path, - const void *value, size_t size) -{ - ssize_t rc = -EOPNOTSUPP; - __u64 *pcreation_time = (__u64 *)value; - __u64 creation_time; - FILE_BASIC_INFO info_buf; - - if ((value == NULL) || (size != sizeof(__u64))) - return -ERANGE; - - memset(&info_buf, 0, sizeof(info_buf)); - creation_time = *pcreation_time; - info_buf.CreationTime = cpu_to_le64(creation_time); - if (pTcon->ses->server->ops->set_file_info) - rc = pTcon->ses->server->ops->set_file_info(inode, full_path, - &info_buf, xid); - if (rc == 0) - CIFS_I(inode)->createtime = creation_time; - - return rc; -} - -static int cifs_xattr_set(const struct xattr_handler *handler, - struct user_namespace *mnt_userns, - struct dentry *dentry, struct inode *inode, - const char *name, const void *value, - size_t size, int flags) -{ - int rc = -EOPNOTSUPP; - unsigned int xid; - struct super_block *sb = dentry->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); - - xid = get_xid(); - page = alloc_dentry_path(); - - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - /* return dos attributes as pseudo xattr */ - /* return alt name if available as pseudo attr */ - - /* if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to - returns as xattrs */ - if (size > MAX_EA_VALUE_SIZE) { - cifs_dbg(FYI, "size of EA value too large\n"); - rc = -EOPNOTSUPP; - goto out; - } - - switch (handler->flags) { - case XATTR_USER: - cifs_dbg(FYI, "%s:setting user xattr %s\n", __func__, name); - if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || - (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { - rc = cifs_attrib_set(xid, pTcon, inode, full_path, - value, size); - if (rc == 0) /* force revalidate of the inode */ - CIFS_I(inode)->time = 0; - break; - } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || - (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { - rc = cifs_creation_time_set(xid, pTcon, inode, - full_path, value, size); - if (rc == 0) /* force revalidate of the inode */ - CIFS_I(inode)->time = 0; - break; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto out; - - if (pTcon->ses->server->ops->set_EA) - rc = pTcon->ses->server->ops->set_EA(xid, pTcon, - full_path, name, value, (__u16)size, - cifs_sb->local_nls, cifs_sb); - break; - - case XATTR_CIFS_ACL: - case XATTR_CIFS_NTSD: - case XATTR_CIFS_NTSD_FULL: { - struct cifs_ntsd *pacl; - - if (!value) - goto out; - pacl = kmalloc(size, GFP_KERNEL); - if (!pacl) { - rc = -ENOMEM; - } else { - memcpy(pacl, value, size); - if (pTcon->ses->server->ops->set_acl) { - int aclflags = 0; - rc = 0; - - switch (handler->flags) { - case XATTR_CIFS_NTSD_FULL: - aclflags = (CIFS_ACL_OWNER | - CIFS_ACL_GROUP | - CIFS_ACL_DACL | - CIFS_ACL_SACL); - break; - case XATTR_CIFS_NTSD: - aclflags = (CIFS_ACL_OWNER | - CIFS_ACL_GROUP | - CIFS_ACL_DACL); - break; - case XATTR_CIFS_ACL: - default: - aclflags = CIFS_ACL_DACL; - } - - rc = pTcon->ses->server->ops->set_acl(pacl, - size, inode, full_path, aclflags); - } else { - rc = -EOPNOTSUPP; - } - if (rc == 0) /* force revalidate of the inode */ - CIFS_I(inode)->time = 0; - kfree(pacl); - } - break; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - case XATTR_ACL_ACCESS: -#ifdef CONFIG_CIFS_POSIX - if (!value) - goto out; - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, - value, (const int)size, - ACL_TYPE_ACCESS, cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; - - case XATTR_ACL_DEFAULT: -#ifdef CONFIG_CIFS_POSIX - if (!value) - goto out; - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, - value, (const int)size, - ACL_TYPE_DEFAULT, cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - } - -out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static int cifs_attrib_get(struct dentry *dentry, - struct inode *inode, void *value, - size_t size) -{ - ssize_t rc; - __u32 *pattribute; - - rc = cifs_revalidate_dentry_attr(dentry); - - if (rc) - return rc; - - if ((value == NULL) || (size == 0)) - return sizeof(__u32); - else if (size < sizeof(__u32)) - return -ERANGE; - - /* return dos attributes as pseudo xattr */ - pattribute = (__u32 *)value; - *pattribute = CIFS_I(inode)->cifsAttrs; - - return sizeof(__u32); -} - -static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode, - void *value, size_t size) -{ - ssize_t rc; - __u64 *pcreatetime; - - rc = cifs_revalidate_dentry_attr(dentry); - if (rc) - return rc; - - if ((value == NULL) || (size == 0)) - return sizeof(__u64); - else if (size < sizeof(__u64)) - return -ERANGE; - - /* return dos attributes as pseudo xattr */ - pcreatetime = (__u64 *)value; - *pcreatetime = CIFS_I(inode)->createtime; - return sizeof(__u64); -} - - -static int cifs_xattr_get(const struct xattr_handler *handler, - struct dentry *dentry, struct inode *inode, - const char *name, void *value, size_t size) -{ - ssize_t rc = -EOPNOTSUPP; - unsigned int xid; - struct super_block *sb = dentry->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); - - xid = get_xid(); - page = alloc_dentry_path(); - - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - /* return alt name if available as pseudo attr */ - switch (handler->flags) { - case XATTR_USER: - cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name); - if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || - (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { - rc = cifs_attrib_get(dentry, inode, value, size); - break; - } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || - (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { - rc = cifs_creation_time_get(dentry, inode, value, size); - break; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto out; - - if (pTcon->ses->server->ops->query_all_EAs) - rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, - full_path, name, value, size, cifs_sb); - break; - - case XATTR_CIFS_ACL: - case XATTR_CIFS_NTSD: - case XATTR_CIFS_NTSD_FULL: { - /* - * fetch owner, DACL, and SACL if asked for full descriptor, - * fetch owner and DACL otherwise - */ - u32 acllen, extra_info; - struct cifs_ntsd *pacl; - - if (pTcon->ses->server->ops->get_acl == NULL) - goto out; /* rc already EOPNOTSUPP */ - - if (handler->flags == XATTR_CIFS_NTSD_FULL) { - extra_info = SACL_SECINFO; - } else { - extra_info = 0; - } - pacl = pTcon->ses->server->ops->get_acl(cifs_sb, - inode, full_path, &acllen, extra_info); - if (IS_ERR(pacl)) { - rc = PTR_ERR(pacl); - cifs_dbg(VFS, "%s: error %zd getting sec desc\n", - __func__, rc); - } else { - if (value) { - if (acllen > size) - acllen = -ERANGE; - else - memcpy(value, pacl, acllen); - } - rc = acllen; - kfree(pacl); - } - break; - } -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - case XATTR_ACL_ACCESS: -#ifdef CONFIG_CIFS_POSIX - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - value, size, ACL_TYPE_ACCESS, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; - - case XATTR_ACL_DEFAULT: -#ifdef CONFIG_CIFS_POSIX - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - value, size, ACL_TYPE_DEFAULT, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; -#endif /* ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - } - - /* We could add an additional check for streams ie - if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to - returns as xattrs */ - - if (rc == -EINVAL) - rc = -EOPNOTSUPP; - -out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) -{ - ssize_t rc = -EOPNOTSUPP; - unsigned int xid; - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - return -EOPNOTSUPP; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); - - xid = get_xid(); - page = alloc_dentry_path(); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto list_ea_exit; - } - /* return dos attributes as pseudo xattr */ - /* return alt name if available as pseudo attr */ - - /* if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to - returns as xattrs */ - - if (pTcon->ses->server->ops->query_all_EAs) - rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, - full_path, NULL, data, buf_size, cifs_sb); -list_ea_exit: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static const struct xattr_handler cifs_user_xattr_handler = { - .prefix = XATTR_USER_PREFIX, - .flags = XATTR_USER, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* os2.* attributes are treated like user.* attributes */ -static const struct xattr_handler cifs_os2_xattr_handler = { - .prefix = XATTR_OS2_PREFIX, - .flags = XATTR_USER, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_cifs_acl_xattr_handler = { - .name = CIFS_XATTR_CIFS_ACL, - .flags = XATTR_CIFS_ACL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* - * Although this is just an alias for the above, need to move away from - * confusing users and using the 20 year old term 'cifs' when it is no - * longer secure and was replaced by SMB2/SMB3 a long time ago, and - * SMB3 and later are highly secure. - */ -static const struct xattr_handler smb3_acl_xattr_handler = { - .name = SMB3_XATTR_CIFS_ACL, - .flags = XATTR_CIFS_ACL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = { - .name = CIFS_XATTR_CIFS_NTSD, - .flags = XATTR_CIFS_NTSD, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* - * Although this is just an alias for the above, need to move away from - * confusing users and using the 20 year old term 'cifs' when it is no - * longer secure and was replaced by SMB2/SMB3 a long time ago, and - * SMB3 and later are highly secure. - */ -static const struct xattr_handler smb3_ntsd_xattr_handler = { - .name = SMB3_XATTR_CIFS_NTSD, - .flags = XATTR_CIFS_NTSD, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_cifs_ntsd_full_xattr_handler = { - .name = CIFS_XATTR_CIFS_NTSD_FULL, - .flags = XATTR_CIFS_NTSD_FULL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* - * Although this is just an alias for the above, need to move away from - * confusing users and using the 20 year old term 'cifs' when it is no - * longer secure and was replaced by SMB2/SMB3 a long time ago, and - * SMB3 and later are highly secure. - */ -static const struct xattr_handler smb3_ntsd_full_xattr_handler = { - .name = SMB3_XATTR_CIFS_NTSD_FULL, - .flags = XATTR_CIFS_NTSD_FULL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - - -static const struct xattr_handler cifs_posix_acl_access_xattr_handler = { - .name = XATTR_NAME_POSIX_ACL_ACCESS, - .flags = XATTR_ACL_ACCESS, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_posix_acl_default_xattr_handler = { - .name = XATTR_NAME_POSIX_ACL_DEFAULT, - .flags = XATTR_ACL_DEFAULT, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -const struct xattr_handler *cifs_xattr_handlers[] = { - &cifs_user_xattr_handler, - &cifs_os2_xattr_handler, - &cifs_cifs_acl_xattr_handler, - &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */ - &cifs_cifs_ntsd_xattr_handler, - &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */ - &cifs_cifs_ntsd_full_xattr_handler, - &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */ - &cifs_posix_acl_access_xattr_handler, - &cifs_posix_acl_default_xattr_handler, - NULL -}; diff --git a/fs/ksmbd/Kconfig b/fs/ksmbd/Kconfig deleted file mode 100644 index e1fe17747ed6..000000000000 --- a/fs/ksmbd/Kconfig +++ /dev/null @@ -1,68 +0,0 @@ -config SMB_SERVER - tristate "SMB3 server support (EXPERIMENTAL)" - depends on INET - depends on MULTIUSER - depends on FILE_LOCKING - select NLS - select NLS_UTF8 - select CRYPTO - select CRYPTO_MD5 - select CRYPTO_HMAC - select CRYPTO_ECB - select CRYPTO_LIB_DES - select CRYPTO_SHA256 - select CRYPTO_CMAC - select CRYPTO_SHA512 - select CRYPTO_AEAD2 - select CRYPTO_CCM - select CRYPTO_GCM - select ASN1 - select OID_REGISTRY - select CRC32 - default n - help - Choose Y here if you want to allow SMB3 compliant clients - to access files residing on this system using SMB3 protocol. - To compile the SMB3 server support as a module, - choose M here: the module will be called ksmbd. - - You may choose to use a samba server instead, in which - case you can choose N here. - - You also need to install user space programs which can be found - in ksmbd-tools, available from - https://github.com/cifsd-team/ksmbd-tools. - More detail about how to run the ksmbd kernel server is - available via README file - (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). - - ksmbd kernel server includes support for auto-negotiation, - Secure negotiate, Pre-authentication integrity, oplock/lease, - compound requests, multi-credit, packet signing, RDMA(smbdirect), - smb3 encryption, copy-offload, secure per-user session - establishment via NTLM or NTLMv2. - -config SMB_SERVER_SMBDIRECT - bool "Support for SMB Direct protocol" - depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y - select SG_POOL - default n - - help - Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. - - SMB Direct allows transferring SMB packets over RDMA. If unsure, - say N. - -config SMB_SERVER_CHECK_CAP_NET_ADMIN - bool "Enable check network administration capability" - depends on SMB_SERVER - default y - - help - Prevent unprivileged processes to start the ksmbd kernel server. - -config SMB_SERVER_KERBEROS5 - bool "Support for Kerberos 5" - depends on SMB_SERVER - default n diff --git a/fs/ksmbd/Makefile b/fs/ksmbd/Makefile deleted file mode 100644 index 7d6337a7dee4..000000000000 --- a/fs/ksmbd/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Makefile for Linux SMB3 kernel server -# -obj-$(CONFIG_SMB_SERVER) += ksmbd.o - -ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \ - misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ - mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ - mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ - transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ - smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ - ksmbd_spnego_negtokentarg.asn1.o asn1.o - -$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h - -$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h -$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h - -ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/ksmbd/asn1.c b/fs/ksmbd/asn1.c deleted file mode 100644 index c03eba090368..000000000000 --- a/fs/ksmbd/asn1.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in - * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich - * - * Copyright (c) 2000 RP Internet (www.rpi.net.au). - */ - -#include -#include -#include -#include -#include -#include - -#include "glob.h" - -#include "asn1.h" -#include "connection.h" -#include "auth.h" -#include "ksmbd_spnego_negtokeninit.asn1.h" -#include "ksmbd_spnego_negtokentarg.asn1.h" - -#define NTLMSSP_OID_LEN 10 - -static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, - 0x82, 0x37, 0x02, 0x02, 0x0a }; - -int -ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) -{ - return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn, - security_blob, length); -} - -int -ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) -{ - return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn, - security_blob, length); -} - -static int compute_asn_hdr_len_bytes(int len) -{ - if (len > 0xFFFFFF) - return 4; - else if (len > 0xFFFF) - return 3; - else if (len > 0xFF) - return 2; - else if (len > 0x7F) - return 1; - else - return 0; -} - -static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq, - int length) -{ - int i; - int index = *ofs; - char hdr_len = compute_asn_hdr_len_bytes(length); - int len = length + 2 + hdr_len; - - /* insert tag */ - buf[index++] = tag; - - if (!hdr_len) { - buf[index++] = len; - } else { - buf[index++] = 0x80 | hdr_len; - for (i = hdr_len - 1; i >= 0; i--) - buf[index++] = (len >> (i * 8)) & 0xFF; - } - - /* insert seq */ - len = len - (index - *ofs); - buf[index++] = seq; - - if (!hdr_len) { - buf[index++] = len; - } else { - buf[index++] = 0x80 | hdr_len; - for (i = hdr_len - 1; i >= 0; i--) - buf[index++] = (len >> (i * 8)) & 0xFF; - } - - *ofs += (index - *ofs); -} - -int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, - char *ntlm_blob, int ntlm_blob_len) -{ - char *buf; - unsigned int ofs = 0; - int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; - int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 + - NTLMSSP_OID_LEN; - int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 + - ntlm_blob_len; - int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len + - oid_len + ntlmssp_len) * 2 + - neg_result_len + oid_len + ntlmssp_len; - - buf = kmalloc(total_len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* insert main gss header */ - encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len + - ntlmssp_len); - - /* insert neg result */ - encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); - buf[ofs++] = 1; - - /* insert oid */ - encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN); - memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN); - ofs += NTLMSSP_OID_LEN; - - /* insert response token - ntlmssp blob */ - encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len); - memcpy(buf + ofs, ntlm_blob, ntlm_blob_len); - ofs += ntlm_blob_len; - - *pbuffer = buf; - *buflen = total_len; - return 0; -} - -int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - int neg_result) -{ - char *buf; - unsigned int ofs = 0; - int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; - int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + - neg_result_len; - - buf = kmalloc(total_len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* insert main gss header */ - encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len); - - /* insert neg result */ - encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); - if (neg_result) - buf[ofs++] = 2; - else - buf[ofs++] = 0; - - *pbuffer = buf; - *buflen = total_len; - return 0; -} - -int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) -{ - enum OID oid; - - oid = look_up_OID(value, vlen); - if (oid != OID_spnego) { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); - return -EBADMSG; - } - - return 0; -} - -int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, - unsigned char tag, const void *value, - size_t vlen) -{ - struct ksmbd_conn *conn = context; - enum OID oid; - int mech_type; - - oid = look_up_OID(value, vlen); - if (oid == OID_ntlmssp) { - mech_type = KSMBD_AUTH_NTLMSSP; - } else if (oid == OID_mskrb5) { - mech_type = KSMBD_AUTH_MSKRB5; - } else if (oid == OID_krb5) { - mech_type = KSMBD_AUTH_KRB5; - } else if (oid == OID_krb5u2u) { - mech_type = KSMBD_AUTH_KRB5U2U; - } else { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); - return -EBADMSG; - } - - conn->auth_mechs |= mech_type; - if (conn->preferred_auth_mech == 0) - conn->preferred_auth_mech = mech_type; - - return 0; -} - -int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, - unsigned char tag, const void *value, - size_t vlen) -{ - struct ksmbd_conn *conn = context; - - conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); - if (!conn->mechToken) - return -ENOMEM; - - memcpy(conn->mechToken, value, vlen); - conn->mechToken[vlen] = '\0'; - return 0; -} - -int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, - unsigned char tag, const void *value, - size_t vlen) -{ - struct ksmbd_conn *conn = context; - - conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); - if (!conn->mechToken) - return -ENOMEM; - - memcpy(conn->mechToken, value, vlen); - conn->mechToken[vlen] = '\0'; - return 0; -} diff --git a/fs/ksmbd/asn1.h b/fs/ksmbd/asn1.h deleted file mode 100644 index ce105f4ce305..000000000000 --- a/fs/ksmbd/asn1.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in - * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich - * - * Copyright (c) 2000 RP Internet (www.rpi.net.au). - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __ASN1_H__ -#define __ASN1_H__ - -int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, - struct ksmbd_conn *conn); -int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, - struct ksmbd_conn *conn); -int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, - char *ntlm_blob, int ntlm_blob_len); -int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - int neg_result); -#endif /* __ASN1_H__ */ diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c deleted file mode 100644 index df8fb076f6f1..000000000000 --- a/fs/ksmbd/auth.c +++ /dev/null @@ -1,1206 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "auth.h" -#include "glob.h" - -#include -#include - -#include "server.h" -#include "smb_common.h" -#include "connection.h" -#include "mgmt/user_session.h" -#include "mgmt/user_config.h" -#include "crypto_ctx.h" -#include "transport_ipc.h" -#include "../smbfs_common/arc4.h" - -/* - * Fixed format data defining GSS header and fixed string - * "not_defined_in_RFC4178@please_ignore". - * So sec blob data in neg phase could be generated statically. - */ -static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { -#ifdef CONFIG_SMB_SERVER_KERBEROS5 - 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, - 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, - 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, - 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, - 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, - 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, - 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, - 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, - 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 -#else - 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, - 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, - 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, - 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, - 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, - 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, - 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, - 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, - 0x72, 0x65 -#endif -}; - -void ksmbd_copy_gss_neg_header(void *buf) -{ - memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); -} - -/** - * ksmbd_gen_sess_key() - function to generate session key - * @sess: session of connection - * @hash: source hash value to be used for find session key - * @hmac: source hmac value to be used for finding session key - * - */ -static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, - char *hmac) -{ - struct ksmbd_crypto_ctx *ctx; - int rc; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); - goto out; - } - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), - hmac, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); - goto out; - } - -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, - char *ntlmv2_hash, char *dname) -{ - int ret, len, conv_len; - wchar_t *domain = NULL; - __le16 *uniname = NULL; - struct ksmbd_crypto_ctx *ctx; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); - return -ENOMEM; - } - - ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - user_passkey(sess->user), - CIFS_ENCPWD_SIZE); - if (ret) { - ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); - goto out; - } - - ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (ret) { - ksmbd_debug(AUTH, "could not init hmacmd5\n"); - goto out; - } - - /* convert user_name to unicode */ - len = strlen(user_name(sess->user)); - uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!uniname) { - ret = -ENOMEM; - goto out; - } - - conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - ret = -EINVAL; - goto out; - } - UniStrupr(uniname); - - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)uniname, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with user\n"); - goto out; - } - - /* Convert domain name or conn name to unicode and uppercase */ - len = strlen(dname); - domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!domain) { - ret = -ENOMEM; - goto out; - } - - conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - ret = -EINVAL; - goto out; - } - - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)domain, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with domain\n"); - goto out; - } - - ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); - if (ret) - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); -out: - kfree(uniname); - kfree(domain); - ksmbd_release_crypto_ctx(ctx); - return ret; -} - -/** - * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler - * @sess: session of connection - * @ntlmv2: NTLMv2 challenge response - * @blen: NTLMv2 blob length - * @domain_name: domain name - * - * Return: 0 on success, error number on error - */ -int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey) -{ - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - struct ksmbd_crypto_ctx *ctx = NULL; - char *construct = NULL; - int rc, len; - - rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); - if (rc) { - ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); - goto out; - } - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - ntlmv2_hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "Could not init hmacmd5\n"); - goto out; - } - - len = CIFS_CRYPTO_KEY_SIZE + blen; - construct = kzalloc(len, GFP_KERNEL); - if (!construct) { - rc = -ENOMEM; - goto out; - } - - memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); - memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); - goto out; - } - ksmbd_release_crypto_ctx(ctx); - ctx = NULL; - - rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate sess key\n"); - goto out; - } - - if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) - rc = -EINVAL; -out: - if (ctx) - ksmbd_release_crypto_ctx(ctx); - kfree(construct); - return rc; -} - -/** - * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct - * authenticate blob - * @authblob: authenticate blob source pointer - * @usr: user details - * @sess: session of connection - * - * Return: 0 on success, error number on error - */ -int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - char *domain_name; - unsigned int nt_off, dn_off; - unsigned short nt_len, dn_len; - int ret; - - if (blob_len < sizeof(struct authenticate_message)) { - ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); - return -EINVAL; - } - - if (memcmp(authblob->Signature, "NTLMSSP", 8)) { - ksmbd_debug(AUTH, "blob signature incorrect %s\n", - authblob->Signature); - return -EINVAL; - } - - nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); - nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); - dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); - dn_len = le16_to_cpu(authblob->DomainName.Length); - - if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len || - nt_len < CIFS_ENCPWD_SIZE) - return -EINVAL; - - /* TODO : use domain name that imported from configuration file */ - domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, - dn_len, true, conn->local_nls); - if (IS_ERR(domain_name)) - return PTR_ERR(domain_name); - - /* process NTLMv2 authentication */ - ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", - domain_name); - ret = ksmbd_auth_ntlmv2(conn, sess, - (struct ntlmv2_resp *)((char *)authblob + nt_off), - nt_len - CIFS_ENCPWD_SIZE, - domain_name, conn->ntlmssp.cryptkey); - kfree(domain_name); - - /* The recovered secondary session key */ - if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { - struct arc4_ctx *ctx_arc4; - unsigned int sess_key_off, sess_key_len; - - sess_key_off = le32_to_cpu(authblob->SessionKey.BufferOffset); - sess_key_len = le16_to_cpu(authblob->SessionKey.Length); - - if (blob_len < (u64)sess_key_off + sess_key_len) - return -EINVAL; - - ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); - if (!ctx_arc4) - return -ENOMEM; - - cifs_arc4_setkey(ctx_arc4, sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - cifs_arc4_crypt(ctx_arc4, sess->sess_key, - (char *)authblob + sess_key_off, sess_key_len); - kfree_sensitive(ctx_arc4); - } - - return ret; -} - -/** - * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct - * negotiate blob - * @negblob: negotiate blob source pointer - * @rsp: response header pointer to be updated - * @sess: session of connection - * - */ -int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, struct ksmbd_conn *conn) -{ - if (blob_len < sizeof(struct negotiate_message)) { - ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); - return -EINVAL; - } - - if (memcmp(negblob->Signature, "NTLMSSP", 8)) { - ksmbd_debug(AUTH, "blob signature incorrect %s\n", - negblob->Signature); - return -EINVAL; - } - - conn->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); - return 0; -} - -/** - * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct - * challenge blob - * @chgblob: challenge blob source pointer to initialize - * @rsp: response header pointer to be updated - * @sess: session of connection - * - */ -unsigned int -ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, - struct ksmbd_conn *conn) -{ - struct target_info *tinfo; - wchar_t *name; - __u8 *target_name; - unsigned int flags, blob_off, blob_len, type, target_info_len = 0; - int len, uni_len, conv_len; - int cflags = conn->ntlmssp.client_flags; - - memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); - chgblob->MessageType = NtLmChallenge; - - flags = NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | - NTLMSSP_NEGOTIATE_TARGET_INFO; - - if (cflags & NTLMSSP_NEGOTIATE_SIGN) { - flags |= NTLMSSP_NEGOTIATE_SIGN; - flags |= cflags & (NTLMSSP_NEGOTIATE_128 | - NTLMSSP_NEGOTIATE_56); - } - - if (cflags & NTLMSSP_NEGOTIATE_SEAL && smb3_encryption_negotiated(conn)) - flags |= NTLMSSP_NEGOTIATE_SEAL; - - if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) - flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; - - if (cflags & NTLMSSP_REQUEST_TARGET) - flags |= NTLMSSP_REQUEST_TARGET; - - if (conn->use_spnego && - (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) - flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; - - if (cflags & NTLMSSP_NEGOTIATE_KEY_XCH) - flags |= NTLMSSP_NEGOTIATE_KEY_XCH; - - chgblob->NegotiateFlags = cpu_to_le32(flags); - len = strlen(ksmbd_netbios_name()); - name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!name) - return -ENOMEM; - - conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - kfree(name); - return -EINVAL; - } - - uni_len = UNICODE_LEN(conv_len); - - blob_off = sizeof(struct challenge_message); - blob_len = blob_off + uni_len; - - chgblob->TargetName.Length = cpu_to_le16(uni_len); - chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); - chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); - - /* Initialize random conn challenge */ - get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); - memcpy(chgblob->Challenge, conn->ntlmssp.cryptkey, - CIFS_CRYPTO_KEY_SIZE); - - /* Add Target Information to security buffer */ - chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); - - target_name = (__u8 *)chgblob + blob_off; - memcpy(target_name, name, uni_len); - tinfo = (struct target_info *)(target_name + uni_len); - - chgblob->TargetInfoArray.Length = 0; - /* Add target info list for NetBIOS/DNS settings */ - for (type = NTLMSSP_AV_NB_COMPUTER_NAME; - type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { - tinfo->Type = cpu_to_le16(type); - tinfo->Length = cpu_to_le16(uni_len); - memcpy(tinfo->Content, name, uni_len); - tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); - target_info_len += 4 + uni_len; - } - - /* Add terminator subblock */ - tinfo->Type = 0; - tinfo->Length = 0; - target_info_len += 4; - - chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); - chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); - blob_len += target_info_len; - kfree(name); - ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); - return blob_len; -} - -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) -{ - struct ksmbd_spnego_authen_response *resp; - struct ksmbd_user *user = NULL; - int retval; - - resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); - if (!resp) { - ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); - return -EINVAL; - } - - if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { - ksmbd_debug(AUTH, "krb5 authentication failure\n"); - retval = -EPERM; - goto out; - } - - if (*out_len <= resp->spnego_blob_len) { - ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", - *out_len, resp->spnego_blob_len); - retval = -EINVAL; - goto out; - } - - if (resp->session_key_len > sizeof(sess->sess_key)) { - ksmbd_debug(AUTH, "session key is too long\n"); - retval = -EINVAL; - goto out; - } - - user = ksmbd_alloc_user(&resp->login_response); - if (!user) { - ksmbd_debug(AUTH, "login failure\n"); - retval = -ENOMEM; - goto out; - } - sess->user = user; - - memcpy(sess->sess_key, resp->payload, resp->session_key_len); - memcpy(out_blob, resp->payload + resp->session_key_len, - resp->spnego_blob_len); - *out_len = resp->spnego_blob_len; - retval = 0; -out: - kvfree(resp); - return retval; -} -#else -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) -{ - return -EOPNOTSUPP; -} -#endif - -/** - * ksmbd_sign_smb2_pdu() - function to generate packet signing - * @conn: connection - * @key: signing key - * @iov: buffer iov array - * @n_vec: number of iovecs - * @sig: signature value generated for client request packet - * - */ -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) -{ - struct ksmbd_crypto_ctx *ctx; - int rc, i; - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -/** - * ksmbd_sign_smb3_pdu() - function to generate packet signing - * @conn: connection - * @key: signing key - * @iov: buffer iov array - * @n_vec: number of iovecs - * @sig: signature value generated for client request packet - * - */ -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) -{ - struct ksmbd_crypto_ctx *ctx; - int rc, i; - - ctx = ksmbd_crypto_ctx_find_cmacaes(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), - key, - SMB2_CMACAES_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); - if (rc) { - ksmbd_debug(AUTH, "cmaces init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_CMACAES(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "cmaces update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -struct derivation { - struct kvec label; - struct kvec context; - bool binding; -}; - -static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct kvec label, struct kvec context, __u8 *key, - unsigned int key_size) -{ - unsigned char zero = 0x0; - __u8 i[4] = {0, 0, 0, 1}; - __u8 L128[4] = {0, 0, 0, 128}; - __u8 L256[4] = {0, 0, 1, 0}; - int rc; - unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct ksmbd_crypto_ctx *ctx; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto smb3signkey_ret; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - label.iov_base, - label.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with label\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); - if (rc) { - ksmbd_debug(AUTH, "could not update with zero\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - context.iov_base, - context.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with context\n"); - goto smb3signkey_ret; - } - - if (key_size == SMB3_ENC_DEC_KEY_SIZE && - (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); - else - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with L\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", - rc); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int generate_smb3signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn, - const struct derivation *signing) -{ - int rc; - struct channel *chann; - char *key; - - chann = lookup_chann_list(sess, conn); - if (!chann) - return 0; - - if (conn->dialect >= SMB30_PROT_ID && signing->binding) - key = chann->smb3signingkey; - else - key = sess->smb3signingkey; - - rc = generate_key(conn, sess, signing->label, signing->context, key, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - - if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) - memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); - - ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); - ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); - ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - ksmbd_debug(AUTH, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, key); - return 0; -} - -int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - struct derivation d; - - d.label.iov_base = "SMB2AESCMAC"; - d.label.iov_len = 12; - d.context.iov_base = "SmbSign"; - d.context.iov_len = 8; - d.binding = conn->binding; - - return generate_smb3signingkey(sess, conn, &d); -} - -int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - struct derivation d; - - d.label.iov_base = "SMBSigningKey"; - d.label.iov_len = 14; - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) - return -ENOENT; - d.context.iov_base = preauth_sess->Preauth_HashValue; - } else { - d.context.iov_base = sess->Preauth_HashValue; - } - d.context.iov_len = 64; - d.binding = conn->binding; - - return generate_smb3signingkey(sess, conn, &d); -} - -struct derivation_twin { - struct derivation encryption; - struct derivation decryption; -}; - -static int generate_smb3encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess, - const struct derivation_twin *ptwin) -{ - int rc; - - rc = generate_key(conn, sess, ptwin->encryption.label, - ptwin->encryption.context, sess->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - - rc = generate_key(conn, sess, ptwin->decryption.label, - ptwin->decryption.context, - sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - - ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); - ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); - ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); - ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { - ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); - ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); - } else { - ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); - ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); - } - return 0; -} - -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - struct derivation_twin twin; - struct derivation *d; - - d = &twin.encryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerOut"; - d->context.iov_len = 10; - - d = &twin.decryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerIn "; - d->context.iov_len = 10; - - return generate_smb3encryptionkey(conn, sess, &twin); -} - -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - struct derivation_twin twin; - struct derivation *d; - - d = &twin.encryption; - d->label.iov_base = "SMBS2CCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = sess->Preauth_HashValue; - d->context.iov_len = 64; - - d = &twin.decryption; - d->label.iov_base = "SMBC2SCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = sess->Preauth_HashValue; - d->context.iov_len = 64; - - return generate_smb3encryptionkey(conn, sess, &twin); -} - -int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, - __u8 *pi_hash) -{ - int rc; - struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); - char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; - int msg_size = get_rfc1002_len(buf); - struct ksmbd_crypto_ctx *ctx = NULL; - - if (conn->preauth_info->Preauth_HashId != - SMB2_PREAUTH_INTEGRITY_SHA512) - return -EINVAL; - - ctx = ksmbd_crypto_ctx_find_sha512(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha512\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA512(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash) -{ - int rc; - struct ksmbd_crypto_ctx *ctx = NULL; - - ctx = ksmbd_crypto_ctx_find_sha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha256\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, - int enc, u8 *key) -{ - struct ksmbd_session *sess; - u8 *ses_enc_key; - - if (enc) - sess = work->sess; - else - sess = ksmbd_session_lookup_all(work->conn, ses_id); - if (!sess) - return -EINVAL; - - ses_enc_key = enc ? sess->smb3encryptionkey : - sess->smb3decryptionkey; - memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); - - return 0; -} - -static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, - unsigned int buflen) -{ - void *addr; - - if (is_vmalloc_addr(buf)) - addr = vmalloc_to_page(buf); - else - addr = virt_to_page(buf); - sg_set_page(sg, addr, buflen, offset_in_page(buf)); -} - -static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, - u8 *sign) -{ - struct scatterlist *sg; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; - - if (!nvec) - return NULL; - - for (i = 0; i < nvec - 1; i++) { - unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; - - if (is_vmalloc_addr(iov[i + 1].iov_base)) { - nr_entries[i] = ((kaddr + iov[i + 1].iov_len + - PAGE_SIZE - 1) >> PAGE_SHIFT) - - (kaddr >> PAGE_SHIFT); - } else { - nr_entries[i]++; - } - total_entries += nr_entries[i]; - } - - /* Add two entries for transform header and signature */ - total_entries += 2; - - sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); - if (!sg) - return NULL; - - sg_init_table(sg, total_entries); - smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); - for (i = 0; i < nvec - 1; i++) { - void *data = iov[i + 1].iov_base; - int len = iov[i + 1].iov_len; - - if (is_vmalloc_addr(data)) { - int j, offset = offset_in_page(data); - - for (j = 0; j < nr_entries[i]; j++) { - unsigned int bytes = PAGE_SIZE - offset; - - if (!len) - break; - - if (bytes > len) - bytes = len; - - sg_set_page(&sg[sg_idx++], - vmalloc_to_page(data), bytes, - offset_in_page(data)); - - data += bytes; - len -= bytes; - offset = 0; - } - } else { - sg_set_page(&sg[sg_idx++], virt_to_page(data), len, - offset_in_page(data)); - } - } - smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); - return sg; -} - -int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, - unsigned int nvec, int enc) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int rc; - struct scatterlist *sg; - u8 sign[SMB2_SIGNATURE_SIZE] = {}; - u8 key[SMB3_ENC_DEC_KEY_SIZE]; - struct aead_request *req; - char *iv; - unsigned int iv_len; - struct crypto_aead *tfm; - unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); - struct ksmbd_crypto_ctx *ctx; - - rc = ksmbd_get_encryption_key(work, - le64_to_cpu(tr_hdr->SessionId), - enc, - key); - if (rc) { - pr_err("Could not get %scryption key\n", enc ? "en" : "de"); - return rc; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - ctx = ksmbd_crypto_ctx_find_gcm(); - else - ctx = ksmbd_crypto_ctx_find_ccm(); - if (!ctx) { - pr_err("crypto alloc failed\n"); - return -ENOMEM; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - tfm = CRYPTO_GCM(ctx); - else - tfm = CRYPTO_CCM(ctx); - - if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); - else - rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); - if (rc) { - pr_err("Failed to set aead key %d\n", rc); - goto free_ctx; - } - - rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); - if (rc) { - pr_err("Failed to set authsize %d\n", rc); - goto free_ctx; - } - - req = aead_request_alloc(tfm, GFP_KERNEL); - if (!req) { - rc = -ENOMEM; - goto free_ctx; - } - - if (!enc) { - memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); - crypt_len += SMB2_SIGNATURE_SIZE; - } - - sg = ksmbd_init_sg(iov, nvec, sign); - if (!sg) { - pr_err("Failed to init sg\n"); - rc = -ENOMEM; - goto free_req; - } - - iv_len = crypto_aead_ivsize(tfm); - iv = kzalloc(iv_len, GFP_KERNEL); - if (!iv) { - rc = -ENOMEM; - goto free_sg; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { - memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - } else { - iv[0] = 3; - memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - } - - aead_request_set_crypt(req, sg, sg, crypt_len, iv); - aead_request_set_ad(req, assoc_data_len); - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); - - if (enc) - rc = crypto_aead_encrypt(req); - else - rc = crypto_aead_decrypt(req); - if (rc) - goto free_iv; - - if (enc) - memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); - -free_iv: - kfree(iv); -free_sg: - kfree(sg); -free_req: - kfree(req); -free_ctx: - ksmbd_release_crypto_ctx(ctx); - return rc; -} diff --git a/fs/ksmbd/auth.h b/fs/ksmbd/auth.h deleted file mode 100644 index 362b6159a6cf..000000000000 --- a/fs/ksmbd/auth.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __AUTH_H__ -#define __AUTH_H__ - -#include "ntlmssp.h" - -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -#define AUTH_GSS_LENGTH 96 -#define AUTH_GSS_PADDING 0 -#else -#define AUTH_GSS_LENGTH 74 -#define AUTH_GSS_PADDING 6 -#endif - -#define CIFS_HMAC_MD5_HASH_SIZE (16) -#define CIFS_NTHASH_SIZE (16) - -/* - * Size of the ntlm client response - */ -#define CIFS_AUTH_RESP_SIZE 24 -#define CIFS_SMB1_SIGNATURE_SIZE 8 -#define CIFS_SMB1_SESSKEY_SIZE 16 - -#define KSMBD_AUTH_NTLMSSP 0x0001 -#define KSMBD_AUTH_KRB5 0x0002 -#define KSMBD_AUTH_MSKRB5 0x0004 -#define KSMBD_AUTH_KRB5U2U 0x0008 - -struct ksmbd_session; -struct ksmbd_conn; -struct ksmbd_work; -struct kvec; - -int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, - unsigned int nvec, int enc); -void ksmbd_copy_gss_neg_header(void *buf); -int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey); -int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, struct ksmbd_conn *conn); -unsigned int -ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, - struct ksmbd_conn *conn); -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len); -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig); -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig); -int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn); -int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn); -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, - __u8 *pi_hash); -int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash); -#endif diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c deleted file mode 100644 index e1d2be19cddf..000000000000 --- a/fs/ksmbd/connection.c +++ /dev/null @@ -1,485 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "server.h" -#include "smb_common.h" -#include "mgmt/ksmbd_ida.h" -#include "connection.h" -#include "transport_tcp.h" -#include "transport_rdma.h" - -static DEFINE_MUTEX(init_lock); - -static struct ksmbd_conn_ops default_conn_ops; - -LIST_HEAD(conn_list); -DECLARE_RWSEM(conn_list_lock); - -/** - * ksmbd_conn_free() - free resources of the connection instance - * - * @conn: connection instance to be cleand up - * - * During the thread termination, the corresponding conn instance - * resources(sock/memory) are released and finally the conn object is freed. - */ -void ksmbd_conn_free(struct ksmbd_conn *conn) -{ - down_write(&conn_list_lock); - list_del(&conn->conns_list); - up_write(&conn_list_lock); - - xa_destroy(&conn->sessions); - kvfree(conn->request_buf); - kfree(conn->preauth_info); - kfree(conn); -} - -/** - * ksmbd_conn_alloc() - initialize a new connection instance - * - * Return: ksmbd_conn struct on success, otherwise NULL - */ -struct ksmbd_conn *ksmbd_conn_alloc(void) -{ - struct ksmbd_conn *conn; - - conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); - if (!conn) - return NULL; - - conn->need_neg = true; - ksmbd_conn_set_new(conn); - conn->local_nls = load_nls("utf8"); - if (!conn->local_nls) - conn->local_nls = load_nls_default(); - if (IS_ENABLED(CONFIG_UNICODE)) - conn->um = utf8_load(UNICODE_AGE(12, 1, 0)); - else - conn->um = ERR_PTR(-EOPNOTSUPP); - if (IS_ERR(conn->um)) - conn->um = NULL; - atomic_set(&conn->req_running, 0); - atomic_set(&conn->r_count, 0); - conn->total_credits = 1; - conn->outstanding_credits = 0; - - init_waitqueue_head(&conn->req_running_q); - init_waitqueue_head(&conn->r_count_q); - INIT_LIST_HEAD(&conn->conns_list); - INIT_LIST_HEAD(&conn->requests); - INIT_LIST_HEAD(&conn->async_requests); - spin_lock_init(&conn->request_lock); - spin_lock_init(&conn->credits_lock); - ida_init(&conn->async_ida); - xa_init(&conn->sessions); - - spin_lock_init(&conn->llist_lock); - INIT_LIST_HEAD(&conn->lock_list); - - down_write(&conn_list_lock); - list_add(&conn->conns_list, &conn_list); - up_write(&conn_list_lock); - return conn; -} - -bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) -{ - struct ksmbd_conn *t; - bool ret = false; - - down_read(&conn_list_lock); - list_for_each_entry(t, &conn_list, conns_list) { - if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) - continue; - - ret = true; - break; - } - up_read(&conn_list_lock); - return ret; -} - -void ksmbd_conn_enqueue_request(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct list_head *requests_queue = NULL; - - if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { - requests_queue = &conn->requests; - work->syncronous = true; - } - - if (requests_queue) { - atomic_inc(&conn->req_running); - spin_lock(&conn->request_lock); - list_add_tail(&work->request_entry, requests_queue); - spin_unlock(&conn->request_lock); - } -} - -int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - int ret = 1; - - if (list_empty(&work->request_entry) && - list_empty(&work->async_request_entry)) - return 0; - - if (!work->multiRsp) - atomic_dec(&conn->req_running); - spin_lock(&conn->request_lock); - if (!work->multiRsp) { - list_del_init(&work->request_entry); - if (work->syncronous == false) - list_del_init(&work->async_request_entry); - ret = 0; - } - spin_unlock(&conn->request_lock); - - wake_up_all(&conn->req_running_q); - return ret; -} - -void ksmbd_conn_lock(struct ksmbd_conn *conn) -{ - mutex_lock(&conn->srv_mutex); -} - -void ksmbd_conn_unlock(struct ksmbd_conn *conn) -{ - mutex_unlock(&conn->srv_mutex); -} - -void ksmbd_all_conn_set_status(u64 sess_id, u32 status) -{ - struct ksmbd_conn *conn; - - down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { - if (conn->binding || xa_load(&conn->sessions, sess_id)) - WRITE_ONCE(conn->status, status); - } - up_read(&conn_list_lock); -} - -void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) -{ - struct ksmbd_conn *bind_conn; - - wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); - - down_read(&conn_list_lock); - list_for_each_entry(bind_conn, &conn_list, conns_list) { - if (bind_conn == conn) - continue; - - if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && - !ksmbd_conn_releasing(bind_conn) && - atomic_read(&bind_conn->req_running)) { - wait_event(bind_conn->req_running_q, - atomic_read(&bind_conn->req_running) == 0); - } - } - up_read(&conn_list_lock); -} - -int ksmbd_conn_write(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - size_t len = 0; - int sent; - struct kvec iov[3]; - int iov_idx = 0; - - if (!work->response_buf) { - pr_err("NULL response header\n"); - return -EINVAL; - } - - if (work->tr_buf) { - iov[iov_idx] = (struct kvec) { work->tr_buf, - sizeof(struct smb2_transform_hdr) + 4 }; - len += iov[iov_idx++].iov_len; - } - - if (work->aux_payload_sz) { - iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; - len += iov[iov_idx++].iov_len; - iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; - len += iov[iov_idx++].iov_len; - } else { - if (work->tr_buf) - iov[iov_idx].iov_len = work->resp_hdr_sz; - else - iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; - iov[iov_idx].iov_base = work->response_buf; - len += iov[iov_idx++].iov_len; - } - - ksmbd_conn_lock(conn); - sent = conn->transport->ops->writev(conn->transport, &iov[0], - iov_idx, len, - work->need_invalidate_rkey, - work->remote_key); - ksmbd_conn_unlock(conn); - - if (sent < 0) { - pr_err("Failed to send message: %d\n", sent); - return sent; - } - - return 0; -} - -int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - int ret = -EINVAL; - - if (conn->transport->ops->rdma_read) - ret = conn->transport->ops->rdma_read(conn->transport, - buf, buflen, - desc, desc_len); - return ret; -} - -int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - int ret = -EINVAL; - - if (conn->transport->ops->rdma_write) - ret = conn->transport->ops->rdma_write(conn->transport, - buf, buflen, - desc, desc_len); - return ret; -} - -bool ksmbd_conn_alive(struct ksmbd_conn *conn) -{ - if (!ksmbd_server_running()) - return false; - - if (ksmbd_conn_exiting(conn)) - return false; - - if (kthread_should_stop()) - return false; - - if (atomic_read(&conn->stats.open_files_count) > 0) - return true; - - /* - * Stop current session if the time that get last request from client - * is bigger than deadtime user configured and opening file count is - * zero. - */ - if (server_conf.deadtime > 0 && - time_after(jiffies, conn->last_active + server_conf.deadtime)) { - ksmbd_debug(CONN, "No response from client in %lu minutes\n", - server_conf.deadtime / SMB_ECHO_INTERVAL); - return false; - } - return true; -} - -#define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) -#define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4) - -/** - * ksmbd_conn_handler_loop() - session thread to listen on new smb requests - * @p: connection instance - * - * One thread each per connection - * - * Return: 0 on success - */ -int ksmbd_conn_handler_loop(void *p) -{ - struct ksmbd_conn *conn = (struct ksmbd_conn *)p; - struct ksmbd_transport *t = conn->transport; - unsigned int pdu_size, max_allowed_pdu_size; - char hdr_buf[4] = {0,}; - int size; - - mutex_init(&conn->srv_mutex); - __module_get(THIS_MODULE); - - if (t->ops->prepare && t->ops->prepare(t)) - goto out; - - conn->last_active = jiffies; - while (ksmbd_conn_alive(conn)) { - if (try_to_freeze()) - continue; - - kvfree(conn->request_buf); - conn->request_buf = NULL; - - size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1); - if (size != sizeof(hdr_buf)) - break; - - pdu_size = get_rfc1002_len(hdr_buf); - ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); - - if (ksmbd_conn_good(conn)) - max_allowed_pdu_size = - SMB3_MAX_MSGSIZE + conn->vals->max_write_size; - else - max_allowed_pdu_size = SMB3_MAX_MSGSIZE; - - if (pdu_size > max_allowed_pdu_size) { - pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", - pdu_size, max_allowed_pdu_size, - READ_ONCE(conn->status)); - break; - } - - /* - * Check maximum pdu size(0x00FFFFFF). - */ - if (pdu_size > MAX_STREAM_PROT_LEN) - break; - - if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE) - break; - - /* 4 for rfc1002 length field */ - /* 1 for implied bcc[0] */ - size = pdu_size + 4 + 1; - conn->request_buf = kvmalloc(size, GFP_KERNEL); - if (!conn->request_buf) - break; - - memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); - - /* - * We already read 4 bytes to find out PDU size, now - * read in PDU - */ - size = t->ops->read(t, conn->request_buf + 4, pdu_size, 2); - if (size < 0) { - pr_err("sock_read failed: %d\n", size); - break; - } - - if (size != pdu_size) { - pr_err("PDU error. Read: %d, Expected: %d\n", - size, pdu_size); - continue; - } - - if (!ksmbd_smb_request(conn)) - break; - - if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == - SMB2_PROTO_NUMBER) { - if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) - break; - } - - if (!default_conn_ops.process_fn) { - pr_err("No connection request callback\n"); - break; - } - - if (default_conn_ops.process_fn(conn)) { - pr_err("Cannot handle request\n"); - break; - } - } - -out: - ksmbd_conn_set_releasing(conn); - /* Wait till all reference dropped to the Server object*/ - wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); - - if (IS_ENABLED(CONFIG_UNICODE)) - utf8_unload(conn->um); - unload_nls(conn->local_nls); - if (default_conn_ops.terminate_fn) - default_conn_ops.terminate_fn(conn); - t->ops->disconnect(t); - module_put(THIS_MODULE); - return 0; -} - -void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) -{ - default_conn_ops.process_fn = ops->process_fn; - default_conn_ops.terminate_fn = ops->terminate_fn; -} - -int ksmbd_conn_transport_init(void) -{ - int ret; - - mutex_lock(&init_lock); - ret = ksmbd_tcp_init(); - if (ret) { - pr_err("Failed to init TCP subsystem: %d\n", ret); - goto out; - } - - ret = ksmbd_rdma_init(); - if (ret) { - pr_err("Failed to init RDMA subsystem: %d\n", ret); - goto out; - } -out: - mutex_unlock(&init_lock); - return ret; -} - -static void stop_sessions(void) -{ - struct ksmbd_conn *conn; - struct ksmbd_transport *t; - -again: - down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { - struct task_struct *task; - - t = conn->transport; - task = t->handler; - if (task) - ksmbd_debug(CONN, "Stop session handler %s/%d\n", - task->comm, task_pid_nr(task)); - ksmbd_conn_set_exiting(conn); - if (t->ops->shutdown) { - up_read(&conn_list_lock); - t->ops->shutdown(t); - down_read(&conn_list_lock); - } - } - up_read(&conn_list_lock); - - if (!list_empty(&conn_list)) { - schedule_timeout_interruptible(HZ / 10); /* 100ms */ - goto again; - } -} - -void ksmbd_conn_transport_destroy(void) -{ - mutex_lock(&init_lock); - ksmbd_tcp_destroy(); - ksmbd_rdma_destroy(); - stop_sessions(); - mutex_unlock(&init_lock); -} diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h deleted file mode 100644 index ad8dfaa48ffb..000000000000 --- a/fs/ksmbd/connection.h +++ /dev/null @@ -1,231 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_CONNECTION_H__ -#define __KSMBD_CONNECTION_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "smb_common.h" -#include "ksmbd_work.h" - -#define KSMBD_SOCKET_BACKLOG 16 - -enum { - KSMBD_SESS_NEW = 0, - KSMBD_SESS_GOOD, - KSMBD_SESS_EXITING, - KSMBD_SESS_NEED_RECONNECT, - KSMBD_SESS_NEED_NEGOTIATE, - KSMBD_SESS_RELEASING -}; - -struct ksmbd_stats { - atomic_t open_files_count; - atomic64_t request_served; -}; - -struct ksmbd_transport; - -struct ksmbd_conn { - struct smb_version_values *vals; - struct smb_version_ops *ops; - struct smb_version_cmds *cmds; - unsigned int max_cmds; - struct mutex srv_mutex; - int status; - unsigned int cli_cap; - char *request_buf; - struct ksmbd_transport *transport; - struct nls_table *local_nls; - struct unicode_map *um; - struct list_head conns_list; - /* smb session 1 per user */ - struct xarray sessions; - unsigned long last_active; - /* How many request are running currently */ - atomic_t req_running; - /* References which are made for this Server object*/ - atomic_t r_count; - unsigned int total_credits; - unsigned int outstanding_credits; - spinlock_t credits_lock; - wait_queue_head_t req_running_q; - wait_queue_head_t r_count_q; - /* Lock to protect requests list*/ - spinlock_t request_lock; - struct list_head requests; - struct list_head async_requests; - int connection_type; - struct ksmbd_stats stats; - char ClientGUID[SMB2_CLIENT_GUID_SIZE]; - struct ntlmssp_auth ntlmssp; - - spinlock_t llist_lock; - struct list_head lock_list; - - struct preauth_integrity_info *preauth_info; - - bool need_neg; - unsigned int auth_mechs; - unsigned int preferred_auth_mech; - bool sign; - bool use_spnego:1; - __u16 cli_sec_mode; - __u16 srv_sec_mode; - /* dialect index that server chose */ - __u16 dialect; - - char *mechToken; - - struct ksmbd_conn_ops *conn_ops; - - /* Preauth Session Table */ - struct list_head preauth_sess_table; - - struct sockaddr_storage peer_addr; - - /* Identifier for async message */ - struct ida async_ida; - - __le16 cipher_type; - __le16 compress_algorithm; - bool posix_ext_supported; - bool signing_negotiated; - __le16 signing_algorithm; - bool binding; -}; - -struct ksmbd_conn_ops { - int (*process_fn)(struct ksmbd_conn *conn); - int (*terminate_fn)(struct ksmbd_conn *conn); -}; - -struct ksmbd_transport_ops { - int (*prepare)(struct ksmbd_transport *t); - void (*disconnect)(struct ksmbd_transport *t); - void (*shutdown)(struct ksmbd_transport *t); - int (*read)(struct ksmbd_transport *t, char *buf, - unsigned int size, int max_retries); - int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, - int size, bool need_invalidate_rkey, - unsigned int remote_key); - int (*rdma_read)(struct ksmbd_transport *t, - void *buf, unsigned int len, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); - int (*rdma_write)(struct ksmbd_transport *t, - void *buf, unsigned int len, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); -}; - -struct ksmbd_transport { - struct ksmbd_conn *conn; - struct ksmbd_transport_ops *ops; - struct task_struct *handler; -}; - -#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) -#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) -#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) - -extern struct list_head conn_list; -extern struct rw_semaphore conn_list_lock; - -bool ksmbd_conn_alive(struct ksmbd_conn *conn); -void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id); -struct ksmbd_conn *ksmbd_conn_alloc(void); -void ksmbd_conn_free(struct ksmbd_conn *conn); -bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); -int ksmbd_conn_write(struct ksmbd_work *work); -int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); -int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); -void ksmbd_conn_enqueue_request(struct ksmbd_work *work); -int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); -void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); -int ksmbd_conn_handler_loop(void *p); -int ksmbd_conn_transport_init(void); -void ksmbd_conn_transport_destroy(void); -void ksmbd_conn_lock(struct ksmbd_conn *conn); -void ksmbd_conn_unlock(struct ksmbd_conn *conn); - -/* - * WARNING - * - * This is a hack. We will move status to a proper place once we land - * a multi-sessions support. - */ -static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; -} - -static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE; -} - -static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT; -} - -static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_EXITING; -} - -static inline bool ksmbd_conn_releasing(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_RELEASING; -} - -static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_NEW); -} - -static inline void ksmbd_conn_set_good(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_GOOD); -} - -static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE); -} - -static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT); -} - -static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_EXITING); -} - -static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING); -} - -void ksmbd_all_conn_set_status(u64 sess_id, u32 status); -#endif /* __CONNECTION_H__ */ diff --git a/fs/ksmbd/crypto_ctx.c b/fs/ksmbd/crypto_ctx.c deleted file mode 100644 index 81488d04199d..000000000000 --- a/fs/ksmbd/crypto_ctx.c +++ /dev/null @@ -1,266 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include - -#include "glob.h" -#include "crypto_ctx.h" - -struct crypto_ctx_list { - spinlock_t ctx_lock; - int avail_ctx; - struct list_head idle_ctx; - wait_queue_head_t ctx_wait; -}; - -static struct crypto_ctx_list ctx_list; - -static inline void free_aead(struct crypto_aead *aead) -{ - if (aead) - crypto_free_aead(aead); -} - -static void free_shash(struct shash_desc *shash) -{ - if (shash) { - crypto_free_shash(shash->tfm); - kfree(shash); - } -} - -static struct crypto_aead *alloc_aead(int id) -{ - struct crypto_aead *tfm = NULL; - - switch (id) { - case CRYPTO_AEAD_AES_GCM: - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - break; - case CRYPTO_AEAD_AES_CCM: - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - break; - default: - pr_err("Does not support encrypt ahead(id : %d)\n", id); - return NULL; - } - - if (IS_ERR(tfm)) { - pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); - return NULL; - } - - return tfm; -} - -static struct shash_desc *alloc_shash_desc(int id) -{ - struct crypto_shash *tfm = NULL; - struct shash_desc *shash; - - switch (id) { - case CRYPTO_SHASH_HMACMD5: - tfm = crypto_alloc_shash("hmac(md5)", 0, 0); - break; - case CRYPTO_SHASH_HMACSHA256: - tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); - break; - case CRYPTO_SHASH_CMACAES: - tfm = crypto_alloc_shash("cmac(aes)", 0, 0); - break; - case CRYPTO_SHASH_SHA256: - tfm = crypto_alloc_shash("sha256", 0, 0); - break; - case CRYPTO_SHASH_SHA512: - tfm = crypto_alloc_shash("sha512", 0, 0); - break; - default: - return NULL; - } - - if (IS_ERR(tfm)) - return NULL; - - shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), - GFP_KERNEL); - if (!shash) - crypto_free_shash(tfm); - else - shash->tfm = tfm; - return shash; -} - -static void ctx_free(struct ksmbd_crypto_ctx *ctx) -{ - int i; - - for (i = 0; i < CRYPTO_SHASH_MAX; i++) - free_shash(ctx->desc[i]); - for (i = 0; i < CRYPTO_AEAD_MAX; i++) - free_aead(ctx->ccmaes[i]); - kfree(ctx); -} - -static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) -{ - struct ksmbd_crypto_ctx *ctx; - - while (1) { - spin_lock(&ctx_list.ctx_lock); - if (!list_empty(&ctx_list.idle_ctx)) { - ctx = list_entry(ctx_list.idle_ctx.next, - struct ksmbd_crypto_ctx, - list); - list_del(&ctx->list); - spin_unlock(&ctx_list.ctx_lock); - return ctx; - } - - if (ctx_list.avail_ctx > num_online_cpus()) { - spin_unlock(&ctx_list.ctx_lock); - wait_event(ctx_list.ctx_wait, - !list_empty(&ctx_list.idle_ctx)); - continue; - } - - ctx_list.avail_ctx++; - spin_unlock(&ctx_list.ctx_lock); - - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); - if (!ctx) { - spin_lock(&ctx_list.ctx_lock); - ctx_list.avail_ctx--; - spin_unlock(&ctx_list.ctx_lock); - wait_event(ctx_list.ctx_wait, - !list_empty(&ctx_list.idle_ctx)); - continue; - } - break; - } - return ctx; -} - -void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) -{ - if (!ctx) - return; - - spin_lock(&ctx_list.ctx_lock); - if (ctx_list.avail_ctx <= num_online_cpus()) { - list_add(&ctx->list, &ctx_list.idle_ctx); - spin_unlock(&ctx_list.ctx_lock); - wake_up(&ctx_list.ctx_wait); - return; - } - - ctx_list.avail_ctx--; - spin_unlock(&ctx_list.ctx_lock); - ctx_free(ctx); -} - -static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) -{ - struct ksmbd_crypto_ctx *ctx; - - if (id >= CRYPTO_SHASH_MAX) - return NULL; - - ctx = ksmbd_find_crypto_ctx(); - if (ctx->desc[id]) - return ctx; - - ctx->desc[id] = alloc_shash_desc(id); - if (ctx->desc[id]) - return ctx; - ksmbd_release_crypto_ctx(ctx); - return NULL; -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); -} - -static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) -{ - struct ksmbd_crypto_ctx *ctx; - - if (id >= CRYPTO_AEAD_MAX) - return NULL; - - ctx = ksmbd_find_crypto_ctx(); - if (ctx->ccmaes[id]) - return ctx; - - ctx->ccmaes[id] = alloc_aead(id); - if (ctx->ccmaes[id]) - return ctx; - ksmbd_release_crypto_ctx(ctx); - return NULL; -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) -{ - return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) -{ - return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM); -} - -void ksmbd_crypto_destroy(void) -{ - struct ksmbd_crypto_ctx *ctx; - - while (!list_empty(&ctx_list.idle_ctx)) { - ctx = list_entry(ctx_list.idle_ctx.next, - struct ksmbd_crypto_ctx, - list); - list_del(&ctx->list); - ctx_free(ctx); - } -} - -int ksmbd_crypto_create(void) -{ - struct ksmbd_crypto_ctx *ctx; - - spin_lock_init(&ctx_list.ctx_lock); - INIT_LIST_HEAD(&ctx_list.idle_ctx); - init_waitqueue_head(&ctx_list.ctx_wait); - ctx_list.avail_ctx = 1; - - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - list_add(&ctx->list, &ctx_list.idle_ctx); - return 0; -} diff --git a/fs/ksmbd/crypto_ctx.h b/fs/ksmbd/crypto_ctx.h deleted file mode 100644 index 4a367c62f653..000000000000 --- a/fs/ksmbd/crypto_ctx.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#ifndef __CRYPTO_CTX_H__ -#define __CRYPTO_CTX_H__ - -#include -#include - -enum { - CRYPTO_SHASH_HMACMD5 = 0, - CRYPTO_SHASH_HMACSHA256, - CRYPTO_SHASH_CMACAES, - CRYPTO_SHASH_SHA256, - CRYPTO_SHASH_SHA512, - CRYPTO_SHASH_MAX, -}; - -enum { - CRYPTO_AEAD_AES_GCM = 16, - CRYPTO_AEAD_AES_CCM, - CRYPTO_AEAD_MAX, -}; - -enum { - CRYPTO_BLK_ECBDES = 32, - CRYPTO_BLK_MAX, -}; - -struct ksmbd_crypto_ctx { - struct list_head list; - - struct shash_desc *desc[CRYPTO_SHASH_MAX]; - struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; -}; - -#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) -#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) -#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) -#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) -#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) - -#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) -#define CRYPTO_HMACSHA256_TFM(c)\ - ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) -#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) -#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) -#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) - -#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) -#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) - -void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); -void ksmbd_crypto_destroy(void); -int ksmbd_crypto_create(void); - -#endif /* __CRYPTO_CTX_H__ */ diff --git a/fs/ksmbd/glob.h b/fs/ksmbd/glob.h deleted file mode 100644 index 5b8f3e0ebdb3..000000000000 --- a/fs/ksmbd/glob.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_GLOB_H -#define __KSMBD_GLOB_H - -#include - -#include "unicode.h" -#include "vfs_cache.h" - -#define KSMBD_VERSION "3.4.2" - -extern int ksmbd_debug_types; - -#define KSMBD_DEBUG_SMB BIT(0) -#define KSMBD_DEBUG_AUTH BIT(1) -#define KSMBD_DEBUG_VFS BIT(2) -#define KSMBD_DEBUG_OPLOCK BIT(3) -#define KSMBD_DEBUG_IPC BIT(4) -#define KSMBD_DEBUG_CONN BIT(5) -#define KSMBD_DEBUG_RDMA BIT(6) -#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ - KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ - KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ - KSMBD_DEBUG_RDMA) - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#ifdef SUBMOD_NAME -#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt -#else -#define pr_fmt(fmt) "ksmbd: " fmt -#endif - -#define ksmbd_debug(type, fmt, ...) \ - do { \ - if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ - pr_info(fmt, ##__VA_ARGS__); \ - } while (0) - -#define UNICODE_LEN(x) ((x) * 2) - -#endif /* __KSMBD_GLOB_H */ diff --git a/fs/ksmbd/ksmbd_netlink.h b/fs/ksmbd/ksmbd_netlink.h deleted file mode 100644 index ce866ff159bf..000000000000 --- a/fs/ksmbd/ksmbd_netlink.h +++ /dev/null @@ -1,412 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - * - * linux-ksmbd-devel@lists.sourceforge.net - */ - -#ifndef _LINUX_KSMBD_SERVER_H -#define _LINUX_KSMBD_SERVER_H - -#include - -/* - * This is a userspace ABI to communicate data between ksmbd and user IPC - * daemon using netlink. This is added to track and cache user account DB - * and share configuration info from userspace. - * - * - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) - * This event is to check whether user IPC daemon is alive. If user IPC - * daemon is dead, ksmbd keep existing connection till disconnecting and - * new connection will be denied. - * - * - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) - * This event is to receive the information that initializes the ksmbd - * server from the user IPC daemon and to start the server. The global - * section parameters are given from smb.conf as initialization - * information. - * - * - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) - * This event is to shutdown ksmbd server. - * - * - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) - * This event is to get user account info to user IPC daemon. - * - * - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response) - * This event is to get net share configuration info. - * - * - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response) - * This event is to get session and tree connect info. - * - * - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) - * This event is to send tree disconnect info to user IPC daemon. - * - * - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) - * This event is to send logout request to user IPC daemon. - * - * - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) - * This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, - * samr to be processed in userspace. - * - * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) - * This event is to make kerberos authentication to be processed in - * userspace. - */ - -#define KSMBD_GENL_NAME "SMBD_GENL" -#define KSMBD_GENL_VERSION 0x01 - -#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 -#define KSMBD_REQ_MAX_HASH_SZ 18 -#define KSMBD_REQ_MAX_SHARE_NAME 64 - -/* - * IPC heartbeat frame to check whether user IPC daemon is alive. - */ -struct ksmbd_heartbeat { - __u32 handle; -}; - -/* - * Global config flags. - */ -#define KSMBD_GLOBAL_FLAG_INVALID (0) -#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) -#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) -#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) - -/* - * IPC request for ksmbd server startup - */ -struct ksmbd_startup_request { - __u32 flags; /* Flags for global config */ - __s32 signing; /* Signing enabled */ - __s8 min_prot[16]; /* The minimum SMB protocol version */ - __s8 max_prot[16]; /* The maximum SMB protocol version */ - __s8 netbios_name[16]; - __s8 work_group[64]; /* Workgroup */ - __s8 server_string[64]; /* Server string */ - __u16 tcp_port; /* tcp port */ - __u16 ipc_timeout; /* - * specifies the number of seconds - * server will wait for the userspace to - * reply to heartbeat frames. - */ - __u32 deadtime; /* Number of minutes of inactivity */ - __u32 file_max; /* Limits the maximum number of open files */ - __u32 smb2_max_write; /* MAX write size */ - __u32 smb2_max_read; /* MAX read size */ - __u32 smb2_max_trans; /* MAX trans size */ - __u32 share_fake_fscaps; /* - * Support some special application that - * makes QFSINFO calls to check whether - * we set the SPARSE_FILES bit (0x40). - */ - __u32 sub_auth[3]; /* Subauth value for Security ID */ - __u32 smb2_max_credits; /* MAX credits */ - __u32 smbd_max_io_size; /* smbd read write size */ - __u32 max_connections; /* Number of maximum simultaneous connections */ - __u32 reserved[126]; /* Reserved room */ - __u32 ifc_list_sz; /* interfaces list size */ - __s8 ____payload[]; -}; - -#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) - -/* - * IPC request to shutdown ksmbd server. - */ -struct ksmbd_shutdown_request { - __s32 reserved[16]; -}; - -/* - * IPC user login request. - */ -struct ksmbd_login_request { - __u32 handle; - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC user login response. - */ -struct ksmbd_login_response { - __u32 handle; - __u32 gid; /* group id */ - __u32 uid; /* user id */ - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ - __u16 status; - __u16 hash_sz; /* hash size */ - __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC request to fetch net share config. - */ -struct ksmbd_share_config_request { - __u32 handle; - __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC response to the net share config request. - */ -struct ksmbd_share_config_response { - __u32 handle; - __u32 flags; - __u16 create_mask; - __u16 directory_mask; - __u16 force_create_mode; - __u16 force_directory_mode; - __u16 force_uid; - __u16 force_gid; - __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; - __u32 reserved[112]; /* Reserved room */ - __u32 veto_list_sz; - __s8 ____payload[]; -}; - -#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) - -static inline char * -ksmbd_share_config_path(struct ksmbd_share_config_response *sc) -{ - char *p = sc->____payload; - - if (sc->veto_list_sz) - p += sc->veto_list_sz + 1; - - return p; -} - -/* - * IPC request for tree connection. This request include session and tree - * connect info from client. - */ -struct ksmbd_tree_connect_request { - __u32 handle; - __u16 account_flags; - __u16 flags; - __u64 session_id; - __u64 connect_id; - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; - __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; - __s8 peer_addr[64]; - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC Response structure for tree connection. - */ -struct ksmbd_tree_connect_response { - __u32 handle; - __u16 status; - __u16 connection_flags; - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC Request struture to disconnect tree connection. - */ -struct ksmbd_tree_disconnect_request { - __u64 session_id; /* session id */ - __u64 connect_id; /* tree connection id */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC Response structure to logout user account. - */ -struct ksmbd_logout_request { - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ - __u32 account_flags; - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * RPC command structure to send rpc request like srvsvc or wkssvc to - * IPC user daemon. - */ -struct ksmbd_rpc_command { - __u32 handle; - __u32 flags; - __u32 payload_sz; - __u8 payload[]; -}; - -/* - * IPC Request Kerberos authentication - */ -struct ksmbd_spnego_authen_request { - __u32 handle; - __u16 spnego_blob_len; /* the length of spnego_blob */ - __u8 spnego_blob[]; /* - * the GSS token from SecurityBuffer of - * SMB2 SESSION SETUP request - */ -}; - -/* - * Response data which includes the GSS token and the session key generated by - * user daemon. - */ -struct ksmbd_spnego_authen_response { - __u32 handle; - struct ksmbd_login_response login_response; /* - * the login response with - * a user identified by the - * GSS token from a client - */ - __u16 session_key_len; /* the length of the session key */ - __u16 spnego_blob_len; /* - * the length of the GSS token which will be - * stored in SecurityBuffer of SMB2 SESSION - * SETUP response - */ - __u8 payload[]; /* session key + AP_REP */ -}; - -/* - * This also used as NETLINK attribute type value. - * - * NOTE: - * Response message type value should be equal to - * request message type value + 1. - */ -enum ksmbd_event { - KSMBD_EVENT_UNSPEC = 0, - KSMBD_EVENT_HEARTBEAT_REQUEST, - - KSMBD_EVENT_STARTING_UP, - KSMBD_EVENT_SHUTTING_DOWN, - - KSMBD_EVENT_LOGIN_REQUEST, - KSMBD_EVENT_LOGIN_RESPONSE = 5, - - KSMBD_EVENT_SHARE_CONFIG_REQUEST, - KSMBD_EVENT_SHARE_CONFIG_RESPONSE, - - KSMBD_EVENT_TREE_CONNECT_REQUEST, - KSMBD_EVENT_TREE_CONNECT_RESPONSE, - - KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, - - KSMBD_EVENT_LOGOUT_REQUEST, - - KSMBD_EVENT_RPC_REQUEST, - KSMBD_EVENT_RPC_RESPONSE, - - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, - KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, - - KSMBD_EVENT_MAX -}; - -/* - * Enumeration for IPC tree connect status. - */ -enum KSMBD_TREE_CONN_STATUS { - KSMBD_TREE_CONN_STATUS_OK = 0, - KSMBD_TREE_CONN_STATUS_NOMEM, - KSMBD_TREE_CONN_STATUS_NO_SHARE, - KSMBD_TREE_CONN_STATUS_NO_USER, - KSMBD_TREE_CONN_STATUS_INVALID_USER, - KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, - KSMBD_TREE_CONN_STATUS_CONN_EXIST, - KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, - KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, - KSMBD_TREE_CONN_STATUS_ERROR, -}; - -/* - * User config flags. - */ -#define KSMBD_USER_FLAG_INVALID (0) -#define KSMBD_USER_FLAG_OK BIT(0) -#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1) -#define KSMBD_USER_FLAG_BAD_UID BIT(2) -#define KSMBD_USER_FLAG_BAD_USER BIT(3) -#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) -#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) - -/* - * Share config flags. - */ -#define KSMBD_SHARE_FLAG_INVALID (0) -#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) -#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) -#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) -#define KSMBD_SHARE_FLAG_READONLY BIT(3) -#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) -#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) -#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) -#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) -#define KSMBD_SHARE_FLAG_PIPE BIT(8) -#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) -#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) -#define KSMBD_SHARE_FLAG_STREAMS BIT(11) -#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) -#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) -#define KSMBD_SHARE_FLAG_UPDATE BIT(14) - -/* - * Tree connect request flags. - */ -#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) -#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0) -#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1) - -/* - * Tree connect flags. - */ -#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0) -#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1) -#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2) -#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3) -#define KSMBD_TREE_CONN_FLAG_UPDATE BIT(4) - -/* - * RPC over IPC. - */ -#define KSMBD_RPC_METHOD_RETURN BIT(0) -#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1) -#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2) -#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_OPEN_METHOD BIT(4) -#define KSMBD_RPC_WRITE_METHOD BIT(5) -#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_CLOSE_METHOD BIT(7) -#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9) -#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10) -#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) -#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) - -/* - * RPC status definitions. - */ -#define KSMBD_RPC_OK 0 -#define KSMBD_RPC_EBAD_FUNC 0x00000001 -#define KSMBD_RPC_EACCESS_DENIED 0x00000005 -#define KSMBD_RPC_EBAD_FID 0x00000006 -#define KSMBD_RPC_ENOMEM 0x00000008 -#define KSMBD_RPC_EBAD_DATA 0x0000000D -#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 -#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 -#define KSMBD_RPC_EMORE_DATA 0x000000EA -#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C -#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 - -#define KSMBD_CONFIG_OPT_DISABLED 0 -#define KSMBD_CONFIG_OPT_ENABLED 1 -#define KSMBD_CONFIG_OPT_AUTO 2 -#define KSMBD_CONFIG_OPT_MANDATORY 3 - -#endif /* _LINUX_KSMBD_SERVER_H */ diff --git a/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 b/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 deleted file mode 100644 index 0065f191b54b..000000000000 --- a/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 +++ /dev/null @@ -1,31 +0,0 @@ -GSSAPI ::= - [APPLICATION 0] IMPLICIT SEQUENCE { - thisMech - OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}), - negotiationToken - NegotiationToken - } - -MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type}) - -MechTypeList ::= SEQUENCE OF MechType - -NegTokenInit ::= - SEQUENCE { - mechTypes - [0] MechTypeList, - reqFlags - [1] BIT STRING OPTIONAL, - mechToken - [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}), - mechListMIC - [3] OCTET STRING OPTIONAL - } - -NegotiationToken ::= - CHOICE { - negTokenInit - [0] NegTokenInit, - negTokenTarg - [1] ANY - } diff --git a/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 b/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 deleted file mode 100644 index 1151933e7b9c..000000000000 --- a/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 +++ /dev/null @@ -1,19 +0,0 @@ -GSSAPI ::= - CHOICE { - negTokenInit - [0] ANY, - negTokenTarg - [1] NegTokenTarg - } - -NegTokenTarg ::= - SEQUENCE { - negResult - [0] ENUMERATED OPTIONAL, - supportedMech - [1] OBJECT IDENTIFIER OPTIONAL, - responseToken - [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}), - mechListMIC - [3] OCTET STRING OPTIONAL - } diff --git a/fs/ksmbd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c deleted file mode 100644 index 14b9caebf7a4..000000000000 --- a/fs/ksmbd/ksmbd_work.c +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "server.h" -#include "connection.h" -#include "ksmbd_work.h" -#include "mgmt/ksmbd_ida.h" - -static struct kmem_cache *work_cache; -static struct workqueue_struct *ksmbd_wq; - -struct ksmbd_work *ksmbd_alloc_work_struct(void) -{ - struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); - - if (work) { - work->compound_fid = KSMBD_NO_FID; - work->compound_pfid = KSMBD_NO_FID; - INIT_LIST_HEAD(&work->request_entry); - INIT_LIST_HEAD(&work->async_request_entry); - INIT_LIST_HEAD(&work->fp_entry); - INIT_LIST_HEAD(&work->interim_entry); - } - return work; -} - -void ksmbd_free_work_struct(struct ksmbd_work *work) -{ - WARN_ON(work->saved_cred != NULL); - - kvfree(work->response_buf); - kvfree(work->aux_payload_buf); - kfree(work->tr_buf); - kvfree(work->request_buf); - if (work->async_id) - ksmbd_release_id(&work->conn->async_ida, work->async_id); - kmem_cache_free(work_cache, work); -} - -void ksmbd_work_pool_destroy(void) -{ - kmem_cache_destroy(work_cache); -} - -int ksmbd_work_pool_init(void) -{ - work_cache = kmem_cache_create("ksmbd_work_cache", - sizeof(struct ksmbd_work), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!work_cache) - return -ENOMEM; - return 0; -} - -int ksmbd_workqueue_init(void) -{ - ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); - if (!ksmbd_wq) - return -ENOMEM; - return 0; -} - -void ksmbd_workqueue_destroy(void) -{ - destroy_workqueue(ksmbd_wq); - ksmbd_wq = NULL; -} - -bool ksmbd_queue_work(struct ksmbd_work *work) -{ - return queue_work(ksmbd_wq, &work->work); -} diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h deleted file mode 100644 index 5ece58e40c97..000000000000 --- a/fs/ksmbd/ksmbd_work.h +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_WORK_H__ -#define __KSMBD_WORK_H__ - -#include -#include - -struct ksmbd_conn; -struct ksmbd_session; -struct ksmbd_tree_connect; - -enum { - KSMBD_WORK_ACTIVE = 0, - KSMBD_WORK_CANCELLED, - KSMBD_WORK_CLOSED, -}; - -/* one of these for every pending CIFS request at the connection */ -struct ksmbd_work { - /* Server corresponding to this mid */ - struct ksmbd_conn *conn; - struct ksmbd_session *sess; - struct ksmbd_tree_connect *tcon; - - /* Pointer to received SMB header */ - void *request_buf; - /* Response buffer */ - void *response_buf; - - /* Read data buffer */ - void *aux_payload_buf; - - /* Next cmd hdr in compound req buf*/ - int next_smb2_rcv_hdr_off; - /* Next cmd hdr in compound rsp buf*/ - int next_smb2_rsp_hdr_off; - - /* - * Current Local FID assigned compound response if SMB2 CREATE - * command is present in compound request - */ - u64 compound_fid; - u64 compound_pfid; - u64 compound_sid; - - const struct cred *saved_cred; - - /* Number of granted credits */ - unsigned int credits_granted; - - /* response smb header size */ - unsigned int resp_hdr_sz; - unsigned int response_sz; - /* Read data count */ - unsigned int aux_payload_sz; - - void *tr_buf; - - unsigned char state; - /* Multiple responses for one request e.g. SMB ECHO */ - bool multiRsp:1; - /* No response for cancelled request */ - bool send_no_response:1; - /* Request is encrypted */ - bool encrypted:1; - /* Is this SYNC or ASYNC ksmbd_work */ - bool syncronous:1; - bool need_invalidate_rkey:1; - - unsigned int remote_key; - /* cancel works */ - int async_id; - void **cancel_argv; - void (*cancel_fn)(void **argv); - - struct work_struct work; - /* List head at conn->requests */ - struct list_head request_entry; - /* List head at conn->async_requests */ - struct list_head async_request_entry; - struct list_head fp_entry; - struct list_head interim_entry; -}; - -/** - * ksmbd_resp_buf_next - Get next buffer on compound response. - * @work: smb work containing response buffer - */ -static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) -{ - return work->response_buf + work->next_smb2_rsp_hdr_off + 4; -} - -/** - * ksmbd_req_buf_next - Get next buffer on compound request. - * @work: smb work containing response buffer - */ -static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) -{ - return work->request_buf + work->next_smb2_rcv_hdr_off + 4; -} - -struct ksmbd_work *ksmbd_alloc_work_struct(void); -void ksmbd_free_work_struct(struct ksmbd_work *work); - -void ksmbd_work_pool_destroy(void); -int ksmbd_work_pool_init(void); - -int ksmbd_workqueue_init(void); -void ksmbd_workqueue_destroy(void); -bool ksmbd_queue_work(struct ksmbd_work *work); - -#endif /* __KSMBD_WORK_H__ */ diff --git a/fs/ksmbd/mgmt/ksmbd_ida.c b/fs/ksmbd/mgmt/ksmbd_ida.c deleted file mode 100644 index 54194d959a5e..000000000000 --- a/fs/ksmbd/mgmt/ksmbd_ida.c +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include "ksmbd_ida.h" - -static inline int __acquire_id(struct ida *ida, int from, int to) -{ - return ida_simple_get(ida, from, to, GFP_KERNEL); -} - -int ksmbd_acquire_smb2_tid(struct ida *ida) -{ - int id; - - id = __acquire_id(ida, 1, 0xFFFFFFFF); - - return id; -} - -int ksmbd_acquire_smb2_uid(struct ida *ida) -{ - int id; - - id = __acquire_id(ida, 1, 0); - if (id == 0xFFFE) - id = __acquire_id(ida, 1, 0); - - return id; -} - -int ksmbd_acquire_async_msg_id(struct ida *ida) -{ - return __acquire_id(ida, 1, 0); -} - -int ksmbd_acquire_id(struct ida *ida) -{ - return __acquire_id(ida, 0, 0); -} - -void ksmbd_release_id(struct ida *ida, int id) -{ - ida_simple_remove(ida, id); -} diff --git a/fs/ksmbd/mgmt/ksmbd_ida.h b/fs/ksmbd/mgmt/ksmbd_ida.h deleted file mode 100644 index 2bc07b16cfde..000000000000 --- a/fs/ksmbd/mgmt/ksmbd_ida.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_IDA_MANAGEMENT_H__ -#define __KSMBD_IDA_MANAGEMENT_H__ - -#include -#include - -/* - * 2.2.1.6.7 TID Generation - * The value 0xFFFF MUST NOT be used as a valid TID. All other - * possible values for TID, including zero (0x0000), are valid. - * The value 0xFFFF is used to specify all TIDs or no TID, - * depending upon the context in which it is used. - */ -int ksmbd_acquire_smb2_tid(struct ida *ida); - -/* - * 2.2.1.6.8 UID Generation - * The value 0xFFFE was declared reserved in the LAN Manager 1.0 - * documentation, so a value of 0xFFFE SHOULD NOT be used as a - * valid UID.<21> All other possible values for a UID, excluding - * zero (0x0000), are valid. - */ -int ksmbd_acquire_smb2_uid(struct ida *ida); -int ksmbd_acquire_async_msg_id(struct ida *ida); - -int ksmbd_acquire_id(struct ida *ida); - -void ksmbd_release_id(struct ida *ida, int id); -#endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/share_config.c b/fs/ksmbd/mgmt/share_config.c deleted file mode 100644 index 328a412259dc..000000000000 --- a/fs/ksmbd/mgmt/share_config.c +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "share_config.h" -#include "user_config.h" -#include "user_session.h" -#include "../transport_ipc.h" -#include "../misc.h" - -#define SHARE_HASH_BITS 3 -static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); -static DECLARE_RWSEM(shares_table_lock); - -struct ksmbd_veto_pattern { - char *pattern; - struct list_head list; -}; - -static unsigned int share_name_hash(const char *name) -{ - return jhash(name, strlen(name), 0); -} - -static void kill_share(struct ksmbd_share_config *share) -{ - while (!list_empty(&share->veto_list)) { - struct ksmbd_veto_pattern *p; - - p = list_entry(share->veto_list.next, - struct ksmbd_veto_pattern, - list); - list_del(&p->list); - kfree(p->pattern); - kfree(p); - } - - if (share->path) - path_put(&share->vfs_path); - kfree(share->name); - kfree(share->path); - kfree(share); -} - -void ksmbd_share_config_del(struct ksmbd_share_config *share) -{ - down_write(&shares_table_lock); - hash_del(&share->hlist); - up_write(&shares_table_lock); -} - -void __ksmbd_share_config_put(struct ksmbd_share_config *share) -{ - ksmbd_share_config_del(share); - kill_share(share); -} - -static struct ksmbd_share_config * -__get_share_config(struct ksmbd_share_config *share) -{ - if (!atomic_inc_not_zero(&share->refcount)) - return NULL; - return share; -} - -static struct ksmbd_share_config *__share_lookup(const char *name) -{ - struct ksmbd_share_config *share; - unsigned int key = share_name_hash(name); - - hash_for_each_possible(shares_table, share, hlist, key) { - if (!strcmp(name, share->name)) - return share; - } - return NULL; -} - -static int parse_veto_list(struct ksmbd_share_config *share, - char *veto_list, - int veto_list_sz) -{ - int sz = 0; - - if (!veto_list_sz) - return 0; - - while (veto_list_sz > 0) { - struct ksmbd_veto_pattern *p; - - sz = strlen(veto_list); - if (!sz) - break; - - p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); - if (!p) - return -ENOMEM; - - p->pattern = kstrdup(veto_list, GFP_KERNEL); - if (!p->pattern) { - kfree(p); - return -ENOMEM; - } - - list_add(&p->list, &share->veto_list); - - veto_list += sz + 1; - veto_list_sz -= (sz + 1); - } - - return 0; -} - -static struct ksmbd_share_config *share_config_request(struct unicode_map *um, - const char *name) -{ - struct ksmbd_share_config_response *resp; - struct ksmbd_share_config *share = NULL; - struct ksmbd_share_config *lookup; - int ret; - - resp = ksmbd_ipc_share_config_request(name); - if (!resp) - return NULL; - - if (resp->flags == KSMBD_SHARE_FLAG_INVALID) - goto out; - - if (*resp->share_name) { - char *cf_resp_name; - bool equal; - - cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name); - if (IS_ERR(cf_resp_name)) - goto out; - equal = !strcmp(cf_resp_name, name); - kfree(cf_resp_name); - if (!equal) - goto out; - } - - share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); - if (!share) - goto out; - - share->flags = resp->flags; - atomic_set(&share->refcount, 1); - INIT_LIST_HEAD(&share->veto_list); - share->name = kstrdup(name, GFP_KERNEL); - - if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - share->path = kstrdup(ksmbd_share_config_path(resp), - GFP_KERNEL); - if (share->path) - share->path_sz = strlen(share->path); - share->create_mask = resp->create_mask; - share->directory_mask = resp->directory_mask; - share->force_create_mode = resp->force_create_mode; - share->force_directory_mode = resp->force_directory_mode; - share->force_uid = resp->force_uid; - share->force_gid = resp->force_gid; - ret = parse_veto_list(share, - KSMBD_SHARE_CONFIG_VETO_LIST(resp), - resp->veto_list_sz); - if (!ret && share->path) { - ret = kern_path(share->path, 0, &share->vfs_path); - if (ret) { - ksmbd_debug(SMB, "failed to access '%s'\n", - share->path); - /* Avoid put_path() */ - kfree(share->path); - share->path = NULL; - } - } - if (ret || !share->name) { - kill_share(share); - share = NULL; - goto out; - } - } - - down_write(&shares_table_lock); - lookup = __share_lookup(name); - if (lookup) - lookup = __get_share_config(lookup); - if (!lookup) { - hash_add(shares_table, &share->hlist, share_name_hash(name)); - } else { - kill_share(share); - share = lookup; - } - up_write(&shares_table_lock); - -out: - kvfree(resp); - return share; -} - -struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, - const char *name) -{ - struct ksmbd_share_config *share; - - down_read(&shares_table_lock); - share = __share_lookup(name); - if (share) - share = __get_share_config(share); - up_read(&shares_table_lock); - - if (share) - return share; - return share_config_request(um, name); -} - -bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, - const char *filename) -{ - struct ksmbd_veto_pattern *p; - - list_for_each_entry(p, &share->veto_list, list) { - if (match_wildcard(p->pattern, filename)) - return true; - } - return false; -} diff --git a/fs/ksmbd/mgmt/share_config.h b/fs/ksmbd/mgmt/share_config.h deleted file mode 100644 index 3fd338293942..000000000000 --- a/fs/ksmbd/mgmt/share_config.h +++ /dev/null @@ -1,82 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __SHARE_CONFIG_MANAGEMENT_H__ -#define __SHARE_CONFIG_MANAGEMENT_H__ - -#include -#include -#include -#include - -struct ksmbd_share_config { - char *name; - char *path; - - unsigned int path_sz; - unsigned int flags; - struct list_head veto_list; - - struct path vfs_path; - - atomic_t refcount; - struct hlist_node hlist; - unsigned short create_mask; - unsigned short directory_mask; - unsigned short force_create_mode; - unsigned short force_directory_mode; - unsigned short force_uid; - unsigned short force_gid; -}; - -#define KSMBD_SHARE_INVALID_UID ((__u16)-1) -#define KSMBD_SHARE_INVALID_GID ((__u16)-1) - -static inline int share_config_create_mode(struct ksmbd_share_config *share, - umode_t posix_mode) -{ - if (!share->force_create_mode) { - if (!posix_mode) - return share->create_mask; - else - return posix_mode & share->create_mask; - } - return share->force_create_mode & share->create_mask; -} - -static inline int share_config_directory_mode(struct ksmbd_share_config *share, - umode_t posix_mode) -{ - if (!share->force_directory_mode) { - if (!posix_mode) - return share->directory_mask; - else - return posix_mode & share->directory_mask; - } - - return share->force_directory_mode & share->directory_mask; -} - -static inline int test_share_config_flag(struct ksmbd_share_config *share, - int flag) -{ - return share->flags & flag; -} - -void ksmbd_share_config_del(struct ksmbd_share_config *share); -void __ksmbd_share_config_put(struct ksmbd_share_config *share); - -static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) -{ - if (!atomic_dec_and_test(&share->refcount)) - return; - __ksmbd_share_config_put(share); -} - -struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, - const char *name); -bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, - const char *filename); -#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c deleted file mode 100644 index f07a05f37651..000000000000 --- a/fs/ksmbd/mgmt/tree_connect.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "../transport_ipc.h" -#include "../connection.h" - -#include "tree_connect.h" -#include "user_config.h" -#include "share_config.h" -#include "user_session.h" - -struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, - const char *share_name) -{ - struct ksmbd_tree_conn_status status = {-ENOENT, NULL}; - struct ksmbd_tree_connect_response *resp = NULL; - struct ksmbd_share_config *sc; - struct ksmbd_tree_connect *tree_conn = NULL; - struct sockaddr *peer_addr; - int ret; - - sc = ksmbd_share_config_get(conn->um, share_name); - if (!sc) - return status; - - tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); - if (!tree_conn) { - status.ret = -ENOMEM; - goto out_error; - } - - tree_conn->id = ksmbd_acquire_tree_conn_id(sess); - if (tree_conn->id < 0) { - status.ret = -EINVAL; - goto out_error; - } - - peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn); - resp = ksmbd_ipc_tree_connect_request(sess, - sc, - tree_conn, - peer_addr); - if (!resp) { - status.ret = -EINVAL; - goto out_error; - } - - status.ret = resp->status; - if (status.ret != KSMBD_TREE_CONN_STATUS_OK) - goto out_error; - - tree_conn->flags = resp->connection_flags; - if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) { - struct ksmbd_share_config *new_sc; - - ksmbd_share_config_del(sc); - new_sc = ksmbd_share_config_get(conn->um, share_name); - if (!new_sc) { - pr_err("Failed to update stale share config\n"); - status.ret = -ESTALE; - goto out_error; - } - ksmbd_share_config_put(sc); - sc = new_sc; - } - - tree_conn->user = sess->user; - tree_conn->share_conf = sc; - status.tree_conn = tree_conn; - - ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, - GFP_KERNEL)); - if (ret) { - status.ret = -ENOMEM; - goto out_error; - } - kvfree(resp); - return status; - -out_error: - if (tree_conn) - ksmbd_release_tree_conn_id(sess, tree_conn->id); - ksmbd_share_config_put(sc); - kfree(tree_conn); - kvfree(resp); - return status; -} - -int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, - struct ksmbd_tree_connect *tree_conn) -{ - int ret; - - ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); - ksmbd_release_tree_conn_id(sess, tree_conn->id); - xa_erase(&sess->tree_conns, tree_conn->id); - ksmbd_share_config_put(tree_conn->share_conf); - kfree(tree_conn); - return ret; -} - -struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, - unsigned int id) -{ - struct ksmbd_tree_connect *tcon; - - tcon = xa_load(&sess->tree_conns, id); - if (tcon) { - if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) - tcon = NULL; - } - - return tcon; -} - -struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, - unsigned int id) -{ - struct ksmbd_tree_connect *tc; - - tc = ksmbd_tree_conn_lookup(sess, id); - if (tc) - return tc->share_conf; - return NULL; -} - -int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) -{ - int ret = 0; - struct ksmbd_tree_connect *tc; - unsigned long id; - - if (!sess) - return -EINVAL; - - xa_for_each(&sess->tree_conns, id, tc) - ret |= ksmbd_tree_conn_disconnect(sess, tc); - xa_destroy(&sess->tree_conns); - return ret; -} diff --git a/fs/ksmbd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h deleted file mode 100644 index 700df36cf3e3..000000000000 --- a/fs/ksmbd/mgmt/tree_connect.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __TREE_CONNECT_MANAGEMENT_H__ -#define __TREE_CONNECT_MANAGEMENT_H__ - -#include - -#include "../ksmbd_netlink.h" - -struct ksmbd_share_config; -struct ksmbd_user; -struct ksmbd_conn; - -#define TREE_CONN_EXPIRE 1 - -struct ksmbd_tree_connect { - int id; - - unsigned int flags; - struct ksmbd_share_config *share_conf; - struct ksmbd_user *user; - - struct list_head list; - - int maximal_access; - bool posix_extensions; - unsigned long status; -}; - -struct ksmbd_tree_conn_status { - unsigned int ret; - struct ksmbd_tree_connect *tree_conn; -}; - -static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, - int flag) -{ - return tree_conn->flags & flag; -} - -struct ksmbd_session; - -struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, - const char *share_name); - -int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, - struct ksmbd_tree_connect *tree_conn); - -struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, - unsigned int id); - -struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, - unsigned int id); - -int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); - -#endif /* __TREE_CONNECT_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/user_config.c b/fs/ksmbd/mgmt/user_config.c deleted file mode 100644 index 279d00feff21..000000000000 --- a/fs/ksmbd/mgmt/user_config.c +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include - -#include "user_config.h" -#include "../transport_ipc.h" - -struct ksmbd_user *ksmbd_login_user(const char *account) -{ - struct ksmbd_login_response *resp; - struct ksmbd_user *user = NULL; - - resp = ksmbd_ipc_login_request(account); - if (!resp) - return NULL; - - if (!(resp->status & KSMBD_USER_FLAG_OK)) - goto out; - - user = ksmbd_alloc_user(resp); -out: - kvfree(resp); - return user; -} - -struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) -{ - struct ksmbd_user *user = NULL; - - user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); - if (!user) - return NULL; - - user->name = kstrdup(resp->account, GFP_KERNEL); - user->flags = resp->status; - user->gid = resp->gid; - user->uid = resp->uid; - user->passkey_sz = resp->hash_sz; - user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); - if (user->passkey) - memcpy(user->passkey, resp->hash, resp->hash_sz); - - if (!user->name || !user->passkey) { - kfree(user->name); - kfree(user->passkey); - kfree(user); - user = NULL; - } - return user; -} - -void ksmbd_free_user(struct ksmbd_user *user) -{ - ksmbd_ipc_logout_request(user->name, user->flags); - kfree(user->name); - kfree(user->passkey); - kfree(user); -} - -int ksmbd_anonymous_user(struct ksmbd_user *user) -{ - if (user->name[0] == '\0') - return 1; - return 0; -} - -bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) -{ - if (strcmp(u1->name, u2->name)) - return false; - if (memcmp(u1->passkey, u2->passkey, u1->passkey_sz)) - return false; - - return true; -} diff --git a/fs/ksmbd/mgmt/user_config.h b/fs/ksmbd/mgmt/user_config.h deleted file mode 100644 index 6a44109617f1..000000000000 --- a/fs/ksmbd/mgmt/user_config.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __USER_CONFIG_MANAGEMENT_H__ -#define __USER_CONFIG_MANAGEMENT_H__ - -#include "../glob.h" - -struct ksmbd_user { - unsigned short flags; - - unsigned int uid; - unsigned int gid; - - char *name; - - size_t passkey_sz; - char *passkey; - unsigned int failed_login_count; -}; - -static inline bool user_guest(struct ksmbd_user *user) -{ - return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT; -} - -static inline void set_user_flag(struct ksmbd_user *user, int flag) -{ - user->flags |= flag; -} - -static inline int test_user_flag(struct ksmbd_user *user, int flag) -{ - return user->flags & flag; -} - -static inline void set_user_guest(struct ksmbd_user *user) -{ -} - -static inline char *user_passkey(struct ksmbd_user *user) -{ - return user->passkey; -} - -static inline char *user_name(struct ksmbd_user *user) -{ - return user->name; -} - -static inline unsigned int user_uid(struct ksmbd_user *user) -{ - return user->uid; -} - -static inline unsigned int user_gid(struct ksmbd_user *user) -{ - return user->gid; -} - -struct ksmbd_user *ksmbd_login_user(const char *account); -struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); -void ksmbd_free_user(struct ksmbd_user *user); -int ksmbd_anonymous_user(struct ksmbd_user *user); -bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); -#endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c deleted file mode 100644 index ea4b56d570fb..000000000000 --- a/fs/ksmbd/mgmt/user_session.c +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "ksmbd_ida.h" -#include "user_session.h" -#include "user_config.h" -#include "tree_connect.h" -#include "../transport_ipc.h" -#include "../connection.h" -#include "../vfs_cache.h" - -static DEFINE_IDA(session_ida); - -#define SESSION_HASH_BITS 3 -static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); -static DECLARE_RWSEM(sessions_table_lock); - -struct ksmbd_session_rpc { - int id; - unsigned int method; - struct list_head list; -}; - -static void free_channel_list(struct ksmbd_session *sess) -{ - struct channel *chann; - unsigned long index; - - xa_for_each(&sess->ksmbd_chann_list, index, chann) { - xa_erase(&sess->ksmbd_chann_list, index); - kfree(chann); - } - - xa_destroy(&sess->ksmbd_chann_list); -} - -static void __session_rpc_close(struct ksmbd_session *sess, - struct ksmbd_session_rpc *entry) -{ - struct ksmbd_rpc_command *resp; - - resp = ksmbd_rpc_close(sess, entry->id); - if (!resp) - pr_err("Unable to close RPC pipe %d\n", entry->id); - - kvfree(resp); - ksmbd_rpc_id_free(entry->id); - kfree(entry); -} - -static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) -{ - struct ksmbd_session_rpc *entry; - - while (!list_empty(&sess->rpc_handle_list)) { - entry = list_entry(sess->rpc_handle_list.next, - struct ksmbd_session_rpc, - list); - - list_del(&entry->list); - __session_rpc_close(sess, entry); - } -} - -static int __rpc_method(char *rpc_name) -{ - if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc")) - return KSMBD_RPC_SRVSVC_METHOD_INVOKE; - - if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc")) - return KSMBD_RPC_WKSSVC_METHOD_INVOKE; - - if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman")) - return KSMBD_RPC_RAP_METHOD; - - if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr")) - return KSMBD_RPC_SAMR_METHOD_INVOKE; - - if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) - return KSMBD_RPC_LSARPC_METHOD_INVOKE; - - pr_err("Unsupported RPC: %s\n", rpc_name); - return 0; -} - -int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) -{ - struct ksmbd_session_rpc *entry; - struct ksmbd_rpc_command *resp; - int method; - - method = __rpc_method(rpc_name); - if (!method) - return -EINVAL; - - entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); - if (!entry) - return -EINVAL; - - list_add(&entry->list, &sess->rpc_handle_list); - entry->method = method; - entry->id = ksmbd_ipc_id_alloc(); - if (entry->id < 0) - goto free_entry; - - resp = ksmbd_rpc_open(sess, entry->id); - if (!resp) - goto free_id; - - kvfree(resp); - return entry->id; -free_id: - ksmbd_rpc_id_free(entry->id); -free_entry: - list_del(&entry->list); - kfree(entry); - return -EINVAL; -} - -void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) -{ - struct ksmbd_session_rpc *entry; - - list_for_each_entry(entry, &sess->rpc_handle_list, list) { - if (entry->id == id) { - list_del(&entry->list); - __session_rpc_close(sess, entry); - break; - } - } -} - -int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) -{ - struct ksmbd_session_rpc *entry; - - list_for_each_entry(entry, &sess->rpc_handle_list, list) { - if (entry->id == id) - return entry->method; - } - return 0; -} - -void ksmbd_session_destroy(struct ksmbd_session *sess) -{ - if (!sess) - return; - - if (sess->user) - ksmbd_free_user(sess->user); - - ksmbd_tree_conn_session_logoff(sess); - ksmbd_destroy_file_table(&sess->file_table); - ksmbd_session_rpc_clear_list(sess); - free_channel_list(sess); - kfree(sess->Preauth_HashValue); - ksmbd_release_id(&session_ida, sess->id); - kfree(sess); -} - -static struct ksmbd_session *__session_lookup(unsigned long long id) -{ - struct ksmbd_session *sess; - - hash_for_each_possible(sessions_table, sess, hlist, id) { - if (id == sess->id) { - sess->last_active = jiffies; - return sess; - } - } - return NULL; -} - -static void ksmbd_expire_session(struct ksmbd_conn *conn) -{ - unsigned long id; - struct ksmbd_session *sess; - - down_write(&sessions_table_lock); - xa_for_each(&conn->sessions, id, sess) { - if (sess->state != SMB2_SESSION_VALID || - time_after(jiffies, - sess->last_active + SMB2_SESSION_TIMEOUT)) { - xa_erase(&conn->sessions, sess->id); - hash_del(&sess->hlist); - ksmbd_session_destroy(sess); - continue; - } - } - up_write(&sessions_table_lock); -} - -int ksmbd_session_register(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - sess->dialect = conn->dialect; - memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - ksmbd_expire_session(conn); - return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); -} - -static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) -{ - struct channel *chann; - - chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); - if (!chann) - return -ENOENT; - - kfree(chann); - return 0; -} - -void ksmbd_sessions_deregister(struct ksmbd_conn *conn) -{ - struct ksmbd_session *sess; - unsigned long id; - - down_write(&sessions_table_lock); - if (conn->binding) { - int bkt; - struct hlist_node *tmp; - - hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) { - if (!ksmbd_chann_del(conn, sess) && - xa_empty(&sess->ksmbd_chann_list)) { - hash_del(&sess->hlist); - ksmbd_session_destroy(sess); - } - } - } - - xa_for_each(&conn->sessions, id, sess) { - unsigned long chann_id; - struct channel *chann; - - xa_for_each(&sess->ksmbd_chann_list, chann_id, chann) { - if (chann->conn != conn) - ksmbd_conn_set_exiting(chann->conn); - } - - ksmbd_chann_del(conn, sess); - if (xa_empty(&sess->ksmbd_chann_list)) { - xa_erase(&conn->sessions, sess->id); - hash_del(&sess->hlist); - ksmbd_session_destroy(sess); - } - } - up_write(&sessions_table_lock); -} - -struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, - unsigned long long id) -{ - struct ksmbd_session *sess; - - sess = xa_load(&conn->sessions, id); - if (sess) - sess->last_active = jiffies; - return sess; -} - -struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) -{ - struct ksmbd_session *sess; - - down_read(&sessions_table_lock); - sess = __session_lookup(id); - if (sess) - sess->last_active = jiffies; - up_read(&sessions_table_lock); - - return sess; -} - -struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, - unsigned long long id) -{ - struct ksmbd_session *sess; - - sess = ksmbd_session_lookup(conn, id); - if (!sess && conn->binding) - sess = ksmbd_session_lookup_slowpath(id); - if (sess && sess->state != SMB2_SESSION_VALID) - sess = NULL; - return sess; -} - -struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, - u64 sess_id) -{ - struct preauth_session *sess; - - sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); - if (!sess) - return NULL; - - sess->id = sess_id; - memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE); - list_add(&sess->preauth_entry, &conn->preauth_sess_table); - - return sess; -} - -static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, - unsigned long long id) -{ - return sess->id == id; -} - -struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, - unsigned long long id) -{ - struct preauth_session *sess = NULL; - - list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { - if (ksmbd_preauth_session_id_match(sess, id)) - return sess; - } - return NULL; -} - -static int __init_smb2_session(struct ksmbd_session *sess) -{ - int id = ksmbd_acquire_smb2_uid(&session_ida); - - if (id < 0) - return -EINVAL; - sess->id = id; - return 0; -} - -static struct ksmbd_session *__session_create(int protocol) -{ - struct ksmbd_session *sess; - int ret; - - if (protocol != CIFDS_SESSION_FLAG_SMB2) - return NULL; - - sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); - if (!sess) - return NULL; - - if (ksmbd_init_file_table(&sess->file_table)) - goto error; - - sess->last_active = jiffies; - sess->state = SMB2_SESSION_IN_PROGRESS; - set_session_flag(sess, protocol); - xa_init(&sess->tree_conns); - xa_init(&sess->ksmbd_chann_list); - INIT_LIST_HEAD(&sess->rpc_handle_list); - sess->sequence_number = 1; - - ret = __init_smb2_session(sess); - if (ret) - goto error; - - ida_init(&sess->tree_conn_ida); - - down_write(&sessions_table_lock); - hash_add(sessions_table, &sess->hlist, sess->id); - up_write(&sessions_table_lock); - - return sess; - -error: - ksmbd_session_destroy(sess); - return NULL; -} - -struct ksmbd_session *ksmbd_smb2_session_create(void) -{ - return __session_create(CIFDS_SESSION_FLAG_SMB2); -} - -int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) -{ - int id = -EINVAL; - - if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) - id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); - - return id; -} - -void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) -{ - if (id >= 0) - ksmbd_release_id(&sess->tree_conn_ida, id); -} diff --git a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h deleted file mode 100644 index 51f38e5b61ab..000000000000 --- a/fs/ksmbd/mgmt/user_session.h +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __USER_SESSION_MANAGEMENT_H__ -#define __USER_SESSION_MANAGEMENT_H__ - -#include -#include - -#include "../smb_common.h" -#include "../ntlmssp.h" - -#define CIFDS_SESSION_FLAG_SMB2 BIT(1) - -#define PREAUTH_HASHVALUE_SIZE 64 - -struct ksmbd_file_table; - -struct channel { - __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; - struct ksmbd_conn *conn; -}; - -struct preauth_session { - __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; - u64 id; - struct list_head preauth_entry; -}; - -struct ksmbd_session { - u64 id; - - __u16 dialect; - char ClientGUID[SMB2_CLIENT_GUID_SIZE]; - - struct ksmbd_user *user; - unsigned int sequence_number; - unsigned int flags; - - bool sign; - bool enc; - bool is_anonymous; - - int state; - __u8 *Preauth_HashValue; - - char sess_key[CIFS_KEY_SIZE]; - - struct hlist_node hlist; - struct xarray ksmbd_chann_list; - struct xarray tree_conns; - struct ida tree_conn_ida; - struct list_head rpc_handle_list; - - __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; - - struct ksmbd_file_table file_table; - unsigned long last_active; -}; - -static inline int test_session_flag(struct ksmbd_session *sess, int bit) -{ - return sess->flags & bit; -} - -static inline void set_session_flag(struct ksmbd_session *sess, int bit) -{ - sess->flags |= bit; -} - -static inline void clear_session_flag(struct ksmbd_session *sess, int bit) -{ - sess->flags &= ~bit; -} - -struct ksmbd_session *ksmbd_smb2_session_create(void); - -void ksmbd_session_destroy(struct ksmbd_session *sess); - -struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); -struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, - unsigned long long id); -int ksmbd_session_register(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -void ksmbd_sessions_deregister(struct ksmbd_conn *conn); -struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, - unsigned long long id); -struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, - u64 sess_id); -struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, - unsigned long long id); - -int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); -void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); - -int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); -void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); -int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); -#endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/misc.c b/fs/ksmbd/misc.c deleted file mode 100644 index 9e8afaa686e3..000000000000 --- a/fs/ksmbd/misc.c +++ /dev/null @@ -1,381 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "misc.h" -#include "smb_common.h" -#include "connection.h" -#include "vfs.h" - -#include "mgmt/share_config.h" - -/** - * match_pattern() - compare a string with a pattern which might include - * wildcard '*' and '?' - * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR - * - * @str: string to compare with a pattern - * @len: string length - * @pattern: pattern string which might include wildcard '*' and '?' - * - * Return: 0 if pattern matched with the string, otherwise non zero value - */ -int match_pattern(const char *str, size_t len, const char *pattern) -{ - const char *s = str; - const char *p = pattern; - bool star = false; - - while (*s && len) { - switch (*p) { - case '?': - s++; - len--; - p++; - break; - case '*': - star = true; - str = s; - if (!*++p) - return true; - pattern = p; - break; - default: - if (tolower(*s) == tolower(*p)) { - s++; - len--; - p++; - } else { - if (!star) - return false; - str++; - s = str; - p = pattern; - } - break; - } - } - - if (*p == '*') - ++p; - return !*p; -} - -/* - * is_char_allowed() - check for valid character - * @ch: input character to be checked - * - * Return: 1 if char is allowed, otherwise 0 - */ -static inline int is_char_allowed(char ch) -{ - /* check for control chars, wildcards etc. */ - if (!(ch & 0x80) && - (ch <= 0x1f || - ch == '?' || ch == '"' || ch == '<' || - ch == '>' || ch == '|' || ch == '*')) - return 0; - - return 1; -} - -int ksmbd_validate_filename(char *filename) -{ - while (*filename) { - char c = *filename; - - filename++; - if (!is_char_allowed(c)) { - ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); - return -ENOENT; - } - } - - return 0; -} - -static int ksmbd_validate_stream_name(char *stream_name) -{ - while (*stream_name) { - char c = *stream_name; - - stream_name++; - if (c == '/' || c == ':' || c == '\\') { - pr_err("Stream name validation failed: %c\n", c); - return -ENOENT; - } - } - - return 0; -} - -int parse_stream_name(char *filename, char **stream_name, int *s_type) -{ - char *stream_type; - char *s_name; - int rc = 0; - - s_name = filename; - filename = strsep(&s_name, ":"); - ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); - if (strchr(s_name, ':')) { - stream_type = s_name; - s_name = strsep(&stream_type, ":"); - - rc = ksmbd_validate_stream_name(s_name); - if (rc < 0) { - rc = -ENOENT; - goto out; - } - - ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, - stream_type); - if (!strncasecmp("$data", stream_type, 5)) - *s_type = DATA_STREAM; - else if (!strncasecmp("$index_allocation", stream_type, 17)) - *s_type = DIR_STREAM; - else - rc = -ENOENT; - } - - *stream_name = s_name; -out: - return rc; -} - -/** - * convert_to_nt_pathname() - extract and return windows path string - * whose share directory prefix was removed from file path - * @share: ksmbd_share_config pointer - * @path: path to report - * - * Return : windows path string or error - */ - -char *convert_to_nt_pathname(struct ksmbd_share_config *share, - const struct path *path) -{ - char *pathname, *ab_pathname, *nt_pathname; - int share_path_len = share->path_sz; - - pathname = kmalloc(PATH_MAX, GFP_KERNEL); - if (!pathname) - return ERR_PTR(-EACCES); - - ab_pathname = d_path(path, pathname, PATH_MAX); - if (IS_ERR(ab_pathname)) { - nt_pathname = ERR_PTR(-EACCES); - goto free_pathname; - } - - if (strncmp(ab_pathname, share->path, share_path_len)) { - nt_pathname = ERR_PTR(-EACCES); - goto free_pathname; - } - - nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL); - if (!nt_pathname) { - nt_pathname = ERR_PTR(-ENOMEM); - goto free_pathname; - } - if (ab_pathname[share_path_len] == '\0') - strcpy(nt_pathname, "/"); - strcat(nt_pathname, &ab_pathname[share_path_len]); - - ksmbd_conv_path_to_windows(nt_pathname); - -free_pathname: - kfree(pathname); - return nt_pathname; -} - -int get_nlink(struct kstat *st) -{ - int nlink; - - nlink = st->nlink; - if (S_ISDIR(st->mode)) - nlink--; - - return nlink; -} - -void ksmbd_conv_path_to_unix(char *path) -{ - strreplace(path, '\\', '/'); -} - -void ksmbd_strip_last_slash(char *path) -{ - int len = strlen(path); - - while (len && path[len - 1] == '/') { - path[len - 1] = '\0'; - len--; - } -} - -void ksmbd_conv_path_to_windows(char *path) -{ - strreplace(path, '/', '\\'); -} - -char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) -{ - char *cf_name; - int cf_len; - - cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL); - if (!cf_name) - return ERR_PTR(-ENOMEM); - - if (IS_ENABLED(CONFIG_UNICODE) && um) { - const struct qstr q_name = {.name = name, .len = strlen(name)}; - - cf_len = utf8_casefold(um, &q_name, cf_name, - KSMBD_REQ_MAX_SHARE_NAME); - if (cf_len < 0) - goto out_ascii; - - return cf_name; - } - -out_ascii: - cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME); - if (cf_len < 0) { - kfree(cf_name); - return ERR_PTR(-E2BIG); - } - - for (; *cf_name; ++cf_name) - *cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name; - return cf_name - cf_len; -} - -/** - * ksmbd_extract_sharename() - get share name from tree connect request - * @treename: buffer containing tree name and share name - * - * Return: share name on success, otherwise error - */ -char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename) -{ - const char *name = treename, *pos = strrchr(name, '\\'); - - if (pos) - name = (pos + 1); - - /* caller has to free the memory */ - return ksmbd_casefold_sharename(um, name); -} - -/** - * convert_to_unix_name() - convert windows name to unix format - * @share: ksmbd_share_config pointer - * @name: file name that is relative to share - * - * Return: converted name on success, otherwise NULL - */ -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) -{ - int no_slash = 0, name_len, path_len; - char *new_name; - - if (name[0] == '/') - name++; - - path_len = share->path_sz; - name_len = strlen(name); - new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); - if (!new_name) - return new_name; - - memcpy(new_name, share->path, path_len); - if (new_name[path_len - 1] != '/') { - new_name[path_len] = '/'; - no_slash = 1; - } - - memcpy(new_name + path_len + no_slash, name, name_len); - path_len += name_len + no_slash; - new_name[path_len] = 0x00; - return new_name; -} - -char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, - const struct nls_table *local_nls, - int *conv_len) -{ - char *conv; - int sz = min(4 * d_info->name_len, PATH_MAX); - - if (!sz) - return NULL; - - conv = kmalloc(sz, GFP_KERNEL); - if (!conv) - return NULL; - - /* XXX */ - *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name, - d_info->name_len, local_nls, 0); - *conv_len *= 2; - - /* We allocate buffer twice bigger than needed. */ - conv[*conv_len] = 0x00; - conv[*conv_len + 1] = 0x00; - return conv; -} - -/* - * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) - * into Unix UTC (based 1970-01-01, in seconds). - */ -struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) -{ - struct timespec64 ts; - - /* Subtract the NTFS time offset, then convert to 1s intervals. */ - s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; - u64 abs_t; - - /* - * Unfortunately can not use normal 64 bit division on 32 bit arch, but - * the alternative, do_div, does not work with negative numbers so have - * to special case them - */ - if (t < 0) { - abs_t = -t; - ts.tv_nsec = do_div(abs_t, 10000000) * 100; - ts.tv_nsec = -ts.tv_nsec; - ts.tv_sec = -abs_t; - } else { - abs_t = t; - ts.tv_nsec = do_div(abs_t, 10000000) * 100; - ts.tv_sec = abs_t; - } - - return ts; -} - -/* Convert the Unix UTC into NT UTC. */ -inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) -{ - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; -} - -inline long long ksmbd_systime(void) -{ - struct timespec64 ts; - - ktime_get_real_ts64(&ts); - return ksmbd_UnixTimeToNT(ts); -} diff --git a/fs/ksmbd/misc.h b/fs/ksmbd/misc.h deleted file mode 100644 index 1facfcd21200..000000000000 --- a/fs/ksmbd/misc.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_MISC_H__ -#define __KSMBD_MISC_H__ - -struct ksmbd_share_config; -struct nls_table; -struct kstat; -struct ksmbd_file; - -int match_pattern(const char *str, size_t len, const char *pattern); -int ksmbd_validate_filename(char *filename); -int parse_stream_name(char *filename, char **stream_name, int *s_type); -char *convert_to_nt_pathname(struct ksmbd_share_config *share, - const struct path *path); -int get_nlink(struct kstat *st); -void ksmbd_conv_path_to_unix(char *path); -void ksmbd_strip_last_slash(char *path); -void ksmbd_conv_path_to_windows(char *path); -char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name); -char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); - -#define KSMBD_DIR_INFO_ALIGNMENT 8 -struct ksmbd_dir_info; -char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, - const struct nls_table *local_nls, - int *conv_len); - -#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) -struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); -u64 ksmbd_UnixTimeToNT(struct timespec64 t); -long long ksmbd_systime(void); -#endif /* __KSMBD_MISC_H__ */ diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c deleted file mode 100644 index 4d9e0b54e3db..000000000000 --- a/fs/ksmbd/ndr.c +++ /dev/null @@ -1,514 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2021 Samsung Electronics Co., Ltd. - * Author(s): Namjae Jeon - */ - -#include - -#include "glob.h" -#include "ndr.h" - -static inline char *ndr_get_field(struct ndr *n) -{ - return n->data + n->offset; -} - -static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) -{ - char *data; - - data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); - if (!data) - return -ENOMEM; - - n->data = data; - n->length += 1024; - memset(n->data + n->offset, 0, 1024); - return 0; -} - -static int ndr_write_int16(struct ndr *n, __u16 value) -{ - if (n->length <= n->offset + sizeof(value)) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sizeof(value)); - if (ret) - return ret; - } - - *(__le16 *)ndr_get_field(n) = cpu_to_le16(value); - n->offset += sizeof(value); - return 0; -} - -static int ndr_write_int32(struct ndr *n, __u32 value) -{ - if (n->length <= n->offset + sizeof(value)) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sizeof(value)); - if (ret) - return ret; - } - - *(__le32 *)ndr_get_field(n) = cpu_to_le32(value); - n->offset += sizeof(value); - return 0; -} - -static int ndr_write_int64(struct ndr *n, __u64 value) -{ - if (n->length <= n->offset + sizeof(value)) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sizeof(value)); - if (ret) - return ret; - } - - *(__le64 *)ndr_get_field(n) = cpu_to_le64(value); - n->offset += sizeof(value); - return 0; -} - -static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) -{ - if (n->length <= n->offset + sz) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sz); - if (ret) - return ret; - } - - memcpy(ndr_get_field(n), value, sz); - n->offset += sz; - return 0; -} - -static int ndr_write_string(struct ndr *n, char *value) -{ - size_t sz; - - sz = strlen(value) + 1; - if (n->length <= n->offset + sz) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sz); - if (ret) - return ret; - } - - memcpy(ndr_get_field(n), value, sz); - n->offset += sz; - n->offset = ALIGN(n->offset, 2); - return 0; -} - -static int ndr_read_string(struct ndr *n, void *value, size_t sz) -{ - int len; - - if (n->offset + sz > n->length) - return -EINVAL; - - len = strnlen(ndr_get_field(n), sz); - if (value) - memcpy(value, ndr_get_field(n), len); - len++; - n->offset += len; - n->offset = ALIGN(n->offset, 2); - return 0; -} - -static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) -{ - if (n->offset + sz > n->length) - return -EINVAL; - - if (value) - memcpy(value, ndr_get_field(n), sz); - n->offset += sz; - return 0; -} - -static int ndr_read_int16(struct ndr *n, __u16 *value) -{ - if (n->offset + sizeof(__u16) > n->length) - return -EINVAL; - - if (value) - *value = le16_to_cpu(*(__le16 *)ndr_get_field(n)); - n->offset += sizeof(__u16); - return 0; -} - -static int ndr_read_int32(struct ndr *n, __u32 *value) -{ - if (n->offset + sizeof(__u32) > n->length) - return -EINVAL; - - if (value) - *value = le32_to_cpu(*(__le32 *)ndr_get_field(n)); - n->offset += sizeof(__u32); - return 0; -} - -static int ndr_read_int64(struct ndr *n, __u64 *value) -{ - if (n->offset + sizeof(__u64) > n->length) - return -EINVAL; - - if (value) - *value = le64_to_cpu(*(__le64 *)ndr_get_field(n)); - n->offset += sizeof(__u64); - return 0; -} - -int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) -{ - char hex_attr[12] = {0}; - int ret; - - n->offset = 0; - n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); - if (!n->data) - return -ENOMEM; - - if (da->version == 3) { - snprintf(hex_attr, 10, "0x%x", da->attr); - ret = ndr_write_string(n, hex_attr); - } else { - ret = ndr_write_string(n, ""); - } - if (ret) - return ret; - - ret = ndr_write_int16(n, da->version); - if (ret) - return ret; - - ret = ndr_write_int32(n, da->version); - if (ret) - return ret; - - ret = ndr_write_int32(n, da->flags); - if (ret) - return ret; - - ret = ndr_write_int32(n, da->attr); - if (ret) - return ret; - - if (da->version == 3) { - ret = ndr_write_int32(n, da->ea_size); - if (ret) - return ret; - ret = ndr_write_int64(n, da->size); - if (ret) - return ret; - ret = ndr_write_int64(n, da->alloc_size); - } else { - ret = ndr_write_int64(n, da->itime); - } - if (ret) - return ret; - - ret = ndr_write_int64(n, da->create_time); - if (ret) - return ret; - - if (da->version == 3) - ret = ndr_write_int64(n, da->change_time); - return ret; -} - -int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) -{ - char hex_attr[12]; - unsigned int version2; - int ret; - - n->offset = 0; - ret = ndr_read_string(n, hex_attr, sizeof(hex_attr)); - if (ret) - return ret; - - ret = ndr_read_int16(n, &da->version); - if (ret) - return ret; - - if (da->version != 3 && da->version != 4) { - ksmbd_debug(VFS, "v%d version is not supported\n", da->version); - return -EINVAL; - } - - ret = ndr_read_int32(n, &version2); - if (ret) - return ret; - - if (da->version != version2) { - ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", - da->version, version2); - return -EINVAL; - } - - ret = ndr_read_int32(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int32(n, &da->attr); - if (ret) - return ret; - - if (da->version == 4) { - ret = ndr_read_int64(n, &da->itime); - if (ret) - return ret; - - ret = ndr_read_int64(n, &da->create_time); - } else { - ret = ndr_read_int32(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int64(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int64(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int64(n, &da->create_time); - if (ret) - return ret; - - ret = ndr_read_int64(n, NULL); - } - - return ret; -} - -static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) -{ - int i, ret; - - ret = ndr_write_int32(n, acl->count); - if (ret) - return ret; - - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int32(n, acl->count); - if (ret) - return ret; - - ret = ndr_write_int32(n, 0); - if (ret) - return ret; - - for (i = 0; i < acl->count; i++) { - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int16(n, acl->entries[i].type); - if (ret) - return ret; - - ret = ndr_write_int16(n, acl->entries[i].type); - if (ret) - return ret; - - if (acl->entries[i].type == SMB_ACL_USER) { - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int64(n, acl->entries[i].uid); - } else if (acl->entries[i].type == SMB_ACL_GROUP) { - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int64(n, acl->entries[i].gid); - } - if (ret) - return ret; - - /* push permission */ - ret = ndr_write_int32(n, acl->entries[i].perm); - } - - return ret; -} - -int ndr_encode_posix_acl(struct ndr *n, - struct user_namespace *user_ns, - struct inode *inode, - struct xattr_smb_acl *acl, - struct xattr_smb_acl *def_acl) -{ - unsigned int ref_id = 0x00020000; - int ret; - vfsuid_t vfsuid; - vfsgid_t vfsgid; - - n->offset = 0; - n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); - if (!n->data) - return -ENOMEM; - - if (acl) { - /* ACL ACCESS */ - ret = ndr_write_int32(n, ref_id); - ref_id += 4; - } else { - ret = ndr_write_int32(n, 0); - } - if (ret) - return ret; - - if (def_acl) { - /* DEFAULT ACL ACCESS */ - ret = ndr_write_int32(n, ref_id); - ref_id += 4; - } else { - ret = ndr_write_int32(n, 0); - } - if (ret) - return ret; - - vfsuid = i_uid_into_vfsuid(user_ns, inode); - ret = ndr_write_int64(n, from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid))); - if (ret) - return ret; - vfsgid = i_gid_into_vfsgid(user_ns, inode); - ret = ndr_write_int64(n, from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid))); - if (ret) - return ret; - ret = ndr_write_int32(n, inode->i_mode); - if (ret) - return ret; - - if (acl) { - ret = ndr_encode_posix_acl_entry(n, acl); - if (def_acl && !ret) - ret = ndr_encode_posix_acl_entry(n, def_acl); - } - return ret; -} - -int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) -{ - unsigned int ref_id = 0x00020004; - int ret; - - n->offset = 0; - n->length = 2048; - n->data = kzalloc(n->length, GFP_KERNEL); - if (!n->data) - return -ENOMEM; - - ret = ndr_write_int16(n, acl->version); - if (ret) - return ret; - - ret = ndr_write_int32(n, acl->version); - if (ret) - return ret; - - ret = ndr_write_int16(n, 2); - if (ret) - return ret; - - ret = ndr_write_int32(n, ref_id); - if (ret) - return ret; - - /* push hash type and hash 64bytes */ - ret = ndr_write_int16(n, acl->hash_type); - if (ret) - return ret; - - ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - ret = ndr_write_bytes(n, acl->desc, acl->desc_len); - if (ret) - return ret; - - ret = ndr_write_int64(n, acl->current_time); - if (ret) - return ret; - - ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - /* push ndr for security descriptor */ - ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size); - return ret; -} - -int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) -{ - unsigned int version2; - int ret; - - n->offset = 0; - ret = ndr_read_int16(n, &acl->version); - if (ret) - return ret; - if (acl->version != 4) { - ksmbd_debug(VFS, "v%d version is not supported\n", acl->version); - return -EINVAL; - } - - ret = ndr_read_int32(n, &version2); - if (ret) - return ret; - if (acl->version != version2) { - ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", - acl->version, version2); - return -EINVAL; - } - - /* Read Level */ - ret = ndr_read_int16(n, NULL); - if (ret) - return ret; - - /* Read Ref Id */ - ret = ndr_read_int32(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int16(n, &acl->hash_type); - if (ret) - return ret; - - ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - ndr_read_bytes(n, acl->desc, 10); - if (strncmp(acl->desc, "posix_acl", 9)) { - pr_err("Invalid acl description : %s\n", acl->desc); - return -EINVAL; - } - - /* Read Time */ - ret = ndr_read_int64(n, NULL); - if (ret) - return ret; - - /* Read Posix ACL hash */ - ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - acl->sd_size = n->length - n->offset; - acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); - if (!acl->sd_buf) - return -ENOMEM; - - ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size); - return ret; -} diff --git a/fs/ksmbd/ndr.h b/fs/ksmbd/ndr.h deleted file mode 100644 index 60ca265d1bb0..000000000000 --- a/fs/ksmbd/ndr.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020 Samsung Electronics Co., Ltd. - * Author(s): Namjae Jeon - */ - -struct ndr { - char *data; - int offset; - int length; -}; - -#define NDR_NTSD_OFFSETOF 0xA0 - -int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); -int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); -int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns, - struct inode *inode, struct xattr_smb_acl *acl, - struct xattr_smb_acl *def_acl); -int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); -int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); -int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/ksmbd/nterr.h b/fs/ksmbd/nterr.h deleted file mode 100644 index 2f358f88a018..000000000000 --- a/fs/ksmbd/nterr.h +++ /dev/null @@ -1,543 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Unix SMB/Netbios implementation. - * Version 1.9. - * NT error code constants - * Copyright (C) Andrew Tridgell 1992-2000 - * Copyright (C) John H Terpstra 1996-2000 - * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 - * Copyright (C) Paul Ashton 1998-2000 - */ - -#ifndef _NTERR_H -#define _NTERR_H - -/* Win32 Status codes. */ -#define NT_STATUS_MORE_ENTRIES 0x0105 -#define NT_ERROR_INVALID_PARAMETER 0x0057 -#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a -#define NT_STATUS_1804 0x070c -#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c -#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) -/* - * Win32 Error codes extracted using a loop in smbclient then printing a netmon - * sniff to a file. - */ - -#define NT_STATUS_OK 0x0000 -#define NT_STATUS_SOME_UNMAPPED 0x0107 -#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 -#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a -#define NT_STATUS_MEDIA_CHANGED 0x8000001c -#define NT_STATUS_END_OF_MEDIA 0x8000001e -#define NT_STATUS_MEDIA_CHECK 0x80000020 -#define NT_STATUS_NO_DATA_DETECTED 0x8000001c -#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d -#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 -#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 -#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) -#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) -#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) -#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) -#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) -#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) -#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) -#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) -#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) -#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) -#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) -#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) -#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) -#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) -#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) -#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) -#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) -#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) -#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) -#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) -#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) -#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) -#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) -#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) -#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) -#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a) -#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) -#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) -#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) -#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) -#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) -#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) -#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) -#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) -#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) -#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) -#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) -#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) -#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) -#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) -#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) -#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) -#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) -#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) -#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) -#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) -#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) -#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) -#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) -#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) -#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) -#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) -#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) -#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) -#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) -#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) -#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) -#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) -#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) -#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) -#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) -#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) -#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) -#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) -#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) -#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) -#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) -#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) -#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) -#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) -#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) -#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) -#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) -#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) -#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) -#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) -#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) -#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) -#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) -#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) -#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) -#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) -#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) -#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) -#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) -#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) -#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) -#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) -#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) -#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) -#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) -#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) -#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) -#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) -#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) -#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) -#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) -#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) -#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) -#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) -#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) -#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) -#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) -#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) -#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) -#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) -#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) -#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) -#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) -#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) -#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) -#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) -#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) -#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) -#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) -#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) -#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) -#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) -#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) -#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) -#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) -#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) -#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) -#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) -#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) -#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) -#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) -#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) -#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) -#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) -#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) -#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) -#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) -#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) -#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) -#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) -#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) -#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) -#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) -#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) -#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) -#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) -#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) -#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) -#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) -#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) -#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) -#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) -#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) -#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) -#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) -#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) -#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) -#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) -#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) -#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) -#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) -#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) -#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) -#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) -#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) -#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) -#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) -#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) -#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) -#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) -#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) -#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) -#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) -#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) -#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) -#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) -#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) -#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) -#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) -#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) -#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) -#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) -#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) -#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) -#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) -#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) -#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) -#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) -#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) -#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) -#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) -#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) -#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) -#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) -#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) -#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) -#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) -#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) -#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) -#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) -#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) -#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) -#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) -#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) -#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) -#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) -#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) -#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) -#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) -#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) -#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) -#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) -#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) -#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) -#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) -#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) -#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) -#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) -#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) -#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) -#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) -#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) -#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) -#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) -#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) -#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) -#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) -#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) -#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) -#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) -#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) -#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) -#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) -#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) -#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) -#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) -#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) -#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) -#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) -#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) -#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) -#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) -#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) -#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) -#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) -#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) -#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) -#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) -#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) -#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) -#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) -#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) -#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) -#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) -#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) -#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) -#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) -#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) -#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) -#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) -#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) -#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) -#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) -#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) -#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) -#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) -#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) -#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) -#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) -#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) -#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) -#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) -#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) -#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) -#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) -#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) -#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) -#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) -#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) -#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) -#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) -#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) -#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) -#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) -#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) -#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) -#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) -#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) -#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) -#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) -#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) -#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) -#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) -#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) -#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) -#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) -#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) -#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) -#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) -#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) -#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) -#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) -#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) -#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) -#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) -#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) -#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) -#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) -#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) -#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) -#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) -#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) -#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) -#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) -#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) -#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) -#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) -#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) -#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) -#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) -#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) -#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) -#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) -#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) -#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) -#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) -#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) -#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) -#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) -#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) -#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) -#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) -#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) -#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) -#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) -#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) -#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) -#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) -#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) -#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) -#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) -#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) -#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) -#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) -#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) -#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) -#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) -#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) -#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) -#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) -#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) -#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) -#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) -#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) -#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) -#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) -#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) -#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) -#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) -#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) -#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) -#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) -#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) -#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) -#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) -#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) -#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) -#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) -#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) -#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) -#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) -#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) -#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) -#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) -#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) -#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) -#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) -#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) -#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) -#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) -#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) -#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) -#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) -#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) -#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) -#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) -#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) -#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) -#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) -#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) -#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) -#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) -#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) -#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) -#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) -#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) -#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) -#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) -#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) -#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) -#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) -#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) -#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) -#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) -#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) -#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) -#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) -#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) -#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) -#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) -#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) -#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) -#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) -#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) -#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) -#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) -#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) -#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) -#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) -#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) -#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) -#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) -#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) -#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) -#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) -#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) -#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) -#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) -#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) -#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) -#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) -#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) -#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) -#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) -#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) -#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) -#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) -#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) -#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) -#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) -#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) -#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) -#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) -#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) -#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) -#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) -#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) -#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) -#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) -#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) -#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) -#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) -#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) -#define NT_STATUS_RETRY (0xC0000000 | 0x022d) -#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) -#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) -#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) -#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) -#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) -#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) -#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) -#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) -#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) -#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) -#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) -#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) -#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) -#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) -#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) -#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) -#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) -#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) -#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) -#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) -#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) -#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) -#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) -#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) -#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) -#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) -#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) -#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) -#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) -#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) -#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) -#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) -#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) -#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) -#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) -#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) -#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) -#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) -#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) -#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) -#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) -#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) -#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) -#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) -#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) -#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) -#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) -#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) -#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) -#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) -#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) -#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) -#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ -#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) -#define NT_STATUS_PENDING 0x00000103 -#endif /* _NTERR_H */ diff --git a/fs/ksmbd/ntlmssp.h b/fs/ksmbd/ntlmssp.h deleted file mode 100644 index f13153c18b4e..000000000000 --- a/fs/ksmbd/ntlmssp.h +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (c) International Business Machines Corp., 2002,2007 - * Author(s): Steve French (sfrench@us.ibm.com) - */ - -#ifndef __KSMBD_NTLMSSP_H -#define __KSMBD_NTLMSSP_H - -#define NTLMSSP_SIGNATURE "NTLMSSP" - -/* Security blob target info data */ -#define TGT_Name "KSMBD" - -/* - * Size of the crypto key returned on the negotiate SMB in bytes - */ -#define CIFS_CRYPTO_KEY_SIZE (8) -#define CIFS_KEY_SIZE (40) - -/* - * Size of encrypted user password in bytes - */ -#define CIFS_ENCPWD_SIZE (16) -#define CIFS_CPHTXT_SIZE (16) - -/* Message Types */ -#define NtLmNegotiate cpu_to_le32(1) -#define NtLmChallenge cpu_to_le32(2) -#define NtLmAuthenticate cpu_to_le32(3) -#define UnknownMessage cpu_to_le32(8) - -/* Negotiate Flags */ -#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ -#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ -#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ -/* define reserved9 0x08 */ -#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ -#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ -#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 -#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ -/* defined reserved 8 0x0100 */ -#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ -#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ -#define NTLMSSP_ANONYMOUS 0x0800 -#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ -#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 -#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ -#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ -#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 -#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 -#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 -#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ -/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ -#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 -#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ -#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 -#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 -/* #define reserved4 0x1000000 */ -#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */ -/* #define reserved3 0x4000000 */ -/* #define reserved2 0x8000000 */ -/* #define reserved1 0x10000000 */ -#define NTLMSSP_NEGOTIATE_128 0x20000000 -#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 -#define NTLMSSP_NEGOTIATE_56 0x80000000 - -/* Define AV Pair Field IDs */ -enum av_field_type { - NTLMSSP_AV_EOL = 0, - NTLMSSP_AV_NB_COMPUTER_NAME, - NTLMSSP_AV_NB_DOMAIN_NAME, - NTLMSSP_AV_DNS_COMPUTER_NAME, - NTLMSSP_AV_DNS_DOMAIN_NAME, - NTLMSSP_AV_DNS_TREE_NAME, - NTLMSSP_AV_FLAGS, - NTLMSSP_AV_TIMESTAMP, - NTLMSSP_AV_RESTRICTION, - NTLMSSP_AV_TARGET_NAME, - NTLMSSP_AV_CHANNEL_BINDINGS -}; - -/* Although typedefs are not commonly used for structure definitions */ -/* in the Linux kernel, in this particular case they are useful */ -/* to more closely match the standards document for NTLMSSP from */ -/* OpenGroup and to make the code more closely match the standard in */ -/* appearance */ - -struct security_buffer { - __le16 Length; - __le16 MaximumLength; - __le32 BufferOffset; /* offset to buffer */ -} __packed; - -struct target_info { - __le16 Type; - __le16 Length; - __u8 Content[]; -} __packed; - -struct negotiate_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmNegotiate = 1 */ - __le32 NegotiateFlags; - struct security_buffer DomainName; /* RFC 1001 style and ASCII */ - struct security_buffer WorkstationName; /* RFC 1001 and ASCII */ - /* - * struct security_buffer for version info not present since we - * do not set the version is present flag - */ - char DomainString[]; - /* followed by WorkstationString */ -} __packed; - -struct challenge_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmChallenge = 2 */ - struct security_buffer TargetName; - __le32 NegotiateFlags; - __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; - __u8 Reserved[8]; - struct security_buffer TargetInfoArray; - /* - * struct security_buffer for version info not present since we - * do not set the version is present flag - */ -} __packed; - -struct authenticate_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmsAuthenticate = 3 */ - struct security_buffer LmChallengeResponse; - struct security_buffer NtChallengeResponse; - struct security_buffer DomainName; - struct security_buffer UserName; - struct security_buffer WorkstationName; - struct security_buffer SessionKey; - __le32 NegotiateFlags; - /* - * struct security_buffer for version info not present since we - * do not set the version is present flag - */ - char UserString[]; -} __packed; - -struct ntlmv2_resp { - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - __le32 blob_signature; - __u32 reserved; - __le64 time; - __u64 client_chal; /* random */ - __u32 reserved2; - /* array of name entries could follow ending in minimum 4 byte struct */ -} __packed; - -/* per smb session structure/fields */ -struct ntlmssp_auth { - /* whether session key is per smb session */ - bool sesskey_per_smbsess; - /* sent by client in type 1 ntlmsssp exchange */ - __u32 client_flags; - /* sent by server in type 2 ntlmssp exchange */ - __u32 conn_flags; - /* sent to server */ - unsigned char ciphertext[CIFS_CPHTXT_SIZE]; - /* used by ntlmssp */ - char cryptkey[CIFS_CRYPTO_KEY_SIZE]; -}; -#endif /* __KSMBD_NTLMSSP_H */ diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c deleted file mode 100644 index 4b210cdd7556..000000000000 --- a/fs/ksmbd/oplock.c +++ /dev/null @@ -1,1722 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include - -#include "glob.h" -#include "oplock.h" - -#include "smb_common.h" -#include "smbstatus.h" -#include "connection.h" -#include "mgmt/user_session.h" -#include "mgmt/share_config.h" -#include "mgmt/tree_connect.h" - -static LIST_HEAD(lease_table_list); -static DEFINE_RWLOCK(lease_list_lock); - -/** - * alloc_opinfo() - allocate a new opinfo object for oplock info - * @work: smb work - * @id: fid of open file - * @Tid: tree id of connection - * - * Return: allocated opinfo object on success, otherwise NULL - */ -static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, - u64 id, __u16 Tid) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct oplock_info *opinfo; - - opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); - if (!opinfo) - return NULL; - - opinfo->sess = sess; - opinfo->conn = conn; - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - opinfo->op_state = OPLOCK_STATE_NONE; - opinfo->pending_break = 0; - opinfo->fid = id; - opinfo->Tid = Tid; - INIT_LIST_HEAD(&opinfo->op_entry); - INIT_LIST_HEAD(&opinfo->interim_list); - init_waitqueue_head(&opinfo->oplock_q); - init_waitqueue_head(&opinfo->oplock_brk); - atomic_set(&opinfo->refcount, 1); - atomic_set(&opinfo->breaking_cnt, 0); - - return opinfo; -} - -static void lease_add_list(struct oplock_info *opinfo) -{ - struct lease_table *lb = opinfo->o_lease->l_lb; - - spin_lock(&lb->lb_lock); - list_add_rcu(&opinfo->lease_entry, &lb->lease_list); - spin_unlock(&lb->lb_lock); -} - -static void lease_del_list(struct oplock_info *opinfo) -{ - struct lease_table *lb = opinfo->o_lease->l_lb; - - if (!lb) - return; - - spin_lock(&lb->lb_lock); - if (list_empty(&opinfo->lease_entry)) { - spin_unlock(&lb->lb_lock); - return; - } - - list_del_init(&opinfo->lease_entry); - opinfo->o_lease->l_lb = NULL; - spin_unlock(&lb->lb_lock); -} - -static void lb_add(struct lease_table *lb) -{ - write_lock(&lease_list_lock); - list_add(&lb->l_entry, &lease_table_list); - write_unlock(&lease_list_lock); -} - -static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) -{ - struct lease *lease; - - lease = kmalloc(sizeof(struct lease), GFP_KERNEL); - if (!lease) - return -ENOMEM; - - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - lease->state = lctx->req_state; - lease->new_state = 0; - lease->flags = lctx->flags; - lease->duration = lctx->duration; - memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); - lease->version = lctx->version; - lease->epoch = 0; - INIT_LIST_HEAD(&opinfo->lease_entry); - opinfo->o_lease = lease; - - return 0; -} - -static void free_lease(struct oplock_info *opinfo) -{ - struct lease *lease; - - lease = opinfo->o_lease; - kfree(lease); -} - -static void free_opinfo(struct oplock_info *opinfo) -{ - if (opinfo->is_lease) - free_lease(opinfo); - kfree(opinfo); -} - -static inline void opinfo_free_rcu(struct rcu_head *rcu_head) -{ - struct oplock_info *opinfo; - - opinfo = container_of(rcu_head, struct oplock_info, rcu_head); - free_opinfo(opinfo); -} - -struct oplock_info *opinfo_get(struct ksmbd_file *fp) -{ - struct oplock_info *opinfo; - - rcu_read_lock(); - opinfo = rcu_dereference(fp->f_opinfo); - if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) - opinfo = NULL; - rcu_read_unlock(); - - return opinfo; -} - -static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) -{ - struct oplock_info *opinfo; - - if (list_empty(&ci->m_op_list)) - return NULL; - - rcu_read_lock(); - opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, - op_entry); - if (opinfo) { - if (!atomic_inc_not_zero(&opinfo->refcount)) - opinfo = NULL; - else { - atomic_inc(&opinfo->conn->r_count); - if (ksmbd_conn_releasing(opinfo->conn)) { - atomic_dec(&opinfo->conn->r_count); - atomic_dec(&opinfo->refcount); - opinfo = NULL; - } - } - } - - rcu_read_unlock(); - - return opinfo; -} - -static void opinfo_conn_put(struct oplock_info *opinfo) -{ - struct ksmbd_conn *conn; - - if (!opinfo) - return; - - conn = opinfo->conn; - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); - opinfo_put(opinfo); -} - -void opinfo_put(struct oplock_info *opinfo) -{ - if (!atomic_dec_and_test(&opinfo->refcount)) - return; - - call_rcu(&opinfo->rcu_head, opinfo_free_rcu); -} - -static void opinfo_add(struct oplock_info *opinfo) -{ - struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - - write_lock(&ci->m_lock); - list_add_rcu(&opinfo->op_entry, &ci->m_op_list); - write_unlock(&ci->m_lock); -} - -static void opinfo_del(struct oplock_info *opinfo) -{ - struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - - if (opinfo->is_lease) { - write_lock(&lease_list_lock); - lease_del_list(opinfo); - write_unlock(&lease_list_lock); - } - write_lock(&ci->m_lock); - list_del_rcu(&opinfo->op_entry); - write_unlock(&ci->m_lock); -} - -static unsigned long opinfo_count(struct ksmbd_file *fp) -{ - if (ksmbd_stream_fd(fp)) - return atomic_read(&fp->f_ci->sop_count); - else - return atomic_read(&fp->f_ci->op_count); -} - -static void opinfo_count_inc(struct ksmbd_file *fp) -{ - if (ksmbd_stream_fd(fp)) - return atomic_inc(&fp->f_ci->sop_count); - else - return atomic_inc(&fp->f_ci->op_count); -} - -static void opinfo_count_dec(struct ksmbd_file *fp) -{ - if (ksmbd_stream_fd(fp)) - return atomic_dec(&fp->f_ci->sop_count); - else - return atomic_dec(&fp->f_ci->op_count); -} - -/** - * opinfo_write_to_read() - convert a write oplock to read oplock - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_write_to_read(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || - opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { - pr_err("bad oplock(0x%x)\n", opinfo->level); - if (opinfo->is_lease) - pr_err("lease state(0x%x)\n", lease->state); - return -EINVAL; - } - opinfo->level = SMB2_OPLOCK_LEVEL_II; - - if (opinfo->is_lease) - lease->state = lease->new_state; - return 0; -} - -/** - * opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_read_handle_to_read(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - lease->state = lease->new_state; - opinfo->level = SMB2_OPLOCK_LEVEL_II; - return 0; -} - -/** - * opinfo_write_to_none() - convert a write oplock to none - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_write_to_none(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || - opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { - pr_err("bad oplock(0x%x)\n", opinfo->level); - if (opinfo->is_lease) - pr_err("lease state(0x%x)\n", lease->state); - return -EINVAL; - } - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) - lease->state = lease->new_state; - return 0; -} - -/** - * opinfo_read_to_none() - convert a write read to none - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_read_to_none(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { - pr_err("bad oplock(0x%x)\n", opinfo->level); - if (opinfo->is_lease) - pr_err("lease state(0x%x)\n", lease->state); - return -EINVAL; - } - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) - lease->state = lease->new_state; - return 0; -} - -/** - * lease_read_to_write() - upgrade lease state from read to write - * @opinfo: current lease info - * - * Return: 0 on success, otherwise -EINVAL - */ -int lease_read_to_write(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { - ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); - return -EINVAL; - } - - lease->new_state = SMB2_LEASE_NONE_LE; - lease->state |= SMB2_LEASE_WRITE_CACHING_LE; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - return 0; -} - -/** - * lease_none_upgrade() - upgrade lease state from none - * @opinfo: current lease info - * @new_state: new lease state - * - * Return: 0 on success, otherwise -EINVAL - */ -static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) -{ - struct lease *lease = opinfo->o_lease; - - if (!(lease->state == SMB2_LEASE_NONE_LE)) { - ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); - return -EINVAL; - } - - lease->new_state = SMB2_LEASE_NONE_LE; - lease->state = new_state; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_II; - else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - else if (lease->state & SMB2_LEASE_READ_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_II; - - return 0; -} - -/** - * close_id_del_oplock() - release oplock object at file close time - * @fp: ksmbd file pointer - */ -void close_id_del_oplock(struct ksmbd_file *fp) -{ - struct oplock_info *opinfo; - - if (S_ISDIR(file_inode(fp->filp)->i_mode)) - return; - - opinfo = opinfo_get(fp); - if (!opinfo) - return; - - opinfo_del(opinfo); - - rcu_assign_pointer(fp->f_opinfo, NULL); - if (opinfo->op_state == OPLOCK_ACK_WAIT) { - opinfo->op_state = OPLOCK_CLOSING; - wake_up_interruptible_all(&opinfo->oplock_q); - if (opinfo->is_lease) { - atomic_set(&opinfo->breaking_cnt, 0); - wake_up_interruptible_all(&opinfo->oplock_brk); - } - } - - opinfo_count_dec(fp); - atomic_dec(&opinfo->refcount); - opinfo_put(opinfo); -} - -/** - * grant_write_oplock() - grant exclusive/batch oplock or write lease - * @opinfo_new: new oplock info object - * @req_oplock: request oplock - * @lctx: lease context information - * - * Return: 0 - */ -static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, - struct lease_ctx_info *lctx) -{ - struct lease *lease = opinfo_new->o_lease; - - if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) - opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - - if (lctx) { - lease->state = lctx->req_state; - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - } -} - -/** - * grant_read_oplock() - grant level2 oplock or read lease - * @opinfo_new: new oplock info object - * @lctx: lease context information - * - * Return: 0 - */ -static void grant_read_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) -{ - struct lease *lease = opinfo_new->o_lease; - - opinfo_new->level = SMB2_OPLOCK_LEVEL_II; - - if (lctx) { - lease->state = SMB2_LEASE_READ_CACHING_LE; - if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) - lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - } -} - -/** - * grant_none_oplock() - grant none oplock or none lease - * @opinfo_new: new oplock info object - * @lctx: lease context information - * - * Return: 0 - */ -static void grant_none_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) -{ - struct lease *lease = opinfo_new->o_lease; - - opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; - - if (lctx) { - lease->state = 0; - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - } -} - -static inline int compare_guid_key(struct oplock_info *opinfo, - const char *guid1, const char *key1) -{ - const char *guid2, *key2; - - guid2 = opinfo->conn->ClientGUID; - key2 = opinfo->o_lease->lease_key; - if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && - !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) - return 1; - - return 0; -} - -/** - * same_client_has_lease() - check whether current lease request is - * from lease owner of file - * @ci: master file pointer - * @client_guid: Client GUID - * @lctx: lease context information - * - * Return: oplock(lease) object on success, otherwise NULL - */ -static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, - char *client_guid, - struct lease_ctx_info *lctx) -{ - int ret; - struct lease *lease; - struct oplock_info *opinfo; - struct oplock_info *m_opinfo = NULL; - - if (!lctx) - return NULL; - - /* - * Compare lease key and client_guid to know request from same owner - * of same client - */ - read_lock(&ci->m_lock); - list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { - if (!opinfo->is_lease) - continue; - read_unlock(&ci->m_lock); - lease = opinfo->o_lease; - - ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); - if (ret) { - m_opinfo = opinfo; - /* skip upgrading lease about breaking lease */ - if (atomic_read(&opinfo->breaking_cnt)) { - read_lock(&ci->m_lock); - continue; - } - - /* upgrading lease */ - if ((atomic_read(&ci->op_count) + - atomic_read(&ci->sop_count)) == 1) { - if (lease->state == - (lctx->req_state & lease->state)) { - lease->state |= lctx->req_state; - if (lctx->req_state & - SMB2_LEASE_WRITE_CACHING_LE) - lease_read_to_write(opinfo); - } - } else if ((atomic_read(&ci->op_count) + - atomic_read(&ci->sop_count)) > 1) { - if (lctx->req_state == - (SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) - lease->state = lctx->req_state; - } - - if (lctx->req_state && lease->state == - SMB2_LEASE_NONE_LE) - lease_none_upgrade(opinfo, lctx->req_state); - } - read_lock(&ci->m_lock); - } - read_unlock(&ci->m_lock); - - return m_opinfo; -} - -static void wait_for_break_ack(struct oplock_info *opinfo) -{ - int rc = 0; - - rc = wait_event_interruptible_timeout(opinfo->oplock_q, - opinfo->op_state == OPLOCK_STATE_NONE || - opinfo->op_state == OPLOCK_CLOSING, - OPLOCK_WAIT_TIME); - - /* is this a timeout ? */ - if (!rc) { - if (opinfo->is_lease) - opinfo->o_lease->state = SMB2_LEASE_NONE_LE; - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - opinfo->op_state = OPLOCK_STATE_NONE; - } -} - -static void wake_up_oplock_break(struct oplock_info *opinfo) -{ - clear_bit_unlock(0, &opinfo->pending_break); - /* memory barrier is needed for wake_up_bit() */ - smp_mb__after_atomic(); - wake_up_bit(&opinfo->pending_break, 0); -} - -static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) -{ - while (test_and_set_bit(0, &opinfo->pending_break)) { - wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); - - /* Not immediately break to none. */ - opinfo->open_trunc = 0; - - if (opinfo->op_state == OPLOCK_CLOSING) - return -ENOENT; - else if (!opinfo->is_lease && opinfo->level <= req_op_level) - return 1; - } - - if (!opinfo->is_lease && opinfo->level <= req_op_level) { - wake_up_oplock_break(opinfo); - return 1; - } - return 0; -} - -static inline int allocate_oplock_break_buf(struct ksmbd_work *work) -{ - work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); - if (!work->response_buf) - return -ENOMEM; - work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - return 0; -} - -/** - * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn - * to client - * @wk: smb work object - * - * There are two ways this function can be called. 1- while file open we break - * from exclusive/batch lock to levelII oplock and 2- while file write/truncate - * we break from levelII oplock no oplock. - * work->request_buf contains oplock_info. - */ -static void __smb2_oplock_break_noti(struct work_struct *wk) -{ - struct smb2_oplock_break *rsp = NULL; - struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct ksmbd_conn *conn = work->conn; - struct oplock_break_info *br_info = work->request_buf; - struct smb2_hdr *rsp_hdr; - struct ksmbd_file *fp; - - fp = ksmbd_lookup_durable_fd(br_info->fid); - if (!fp) - goto out; - - if (allocate_oplock_break_buf(work)) { - pr_err("smb2_allocate_rsp_buf failed! "); - ksmbd_fd_put(work, fp); - goto out; - } - - rsp_hdr = smb2_get_msg(work->response_buf); - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->CreditRequest = cpu_to_le16(0); - rsp_hdr->Command = SMB2_OPLOCK_BREAK; - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = cpu_to_le64(-1); - rsp_hdr->Id.SyncId.ProcessId = 0; - rsp_hdr->Id.SyncId.TreeId = 0; - rsp_hdr->SessionId = 0; - memset(rsp_hdr->Signature, 0, 16); - - rsp = smb2_get_msg(work->response_buf); - - rsp->StructureSize = cpu_to_le16(24); - if (!br_info->open_trunc && - (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || - br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; - else - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; - rsp->Reserved = 0; - rsp->Reserved2 = 0; - rsp->PersistentFid = fp->persistent_id; - rsp->VolatileFid = fp->volatile_id; - - inc_rfc1001_len(work->response_buf, 24); - - ksmbd_debug(OPLOCK, - "sending oplock break v_id %llu p_id = %llu lock level = %d\n", - rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); - - ksmbd_fd_put(work, fp); - ksmbd_conn_write(work); - -out: - ksmbd_free_work_struct(work); -} - -/** - * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock - * break command from server to client - * @opinfo: oplock info object - * - * Return: 0 on success, otherwise error - */ -static int smb2_oplock_break_noti(struct oplock_info *opinfo) -{ - struct ksmbd_conn *conn = opinfo->conn; - struct oplock_break_info *br_info; - int ret = 0; - struct ksmbd_work *work = ksmbd_alloc_work_struct(); - - if (!work) - return -ENOMEM; - - br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); - if (!br_info) { - ksmbd_free_work_struct(work); - return -ENOMEM; - } - - br_info->level = opinfo->level; - br_info->fid = opinfo->fid; - br_info->open_trunc = opinfo->open_trunc; - - work->request_buf = (char *)br_info; - work->conn = conn; - work->sess = opinfo->sess; - - if (opinfo->op_state == OPLOCK_ACK_WAIT) { - INIT_WORK(&work->work, __smb2_oplock_break_noti); - ksmbd_queue_work(work); - - wait_for_break_ack(opinfo); - } else { - __smb2_oplock_break_noti(&work->work); - if (opinfo->level == SMB2_OPLOCK_LEVEL_II) - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - } - return ret; -} - -/** - * __smb2_lease_break_noti() - send lease break command from server - * to client - * @wk: smb work object - */ -static void __smb2_lease_break_noti(struct work_struct *wk) -{ - struct smb2_lease_break *rsp = NULL; - struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct lease_break_info *br_info = work->request_buf; - struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *rsp_hdr; - - if (allocate_oplock_break_buf(work)) { - ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); - goto out; - } - - rsp_hdr = smb2_get_msg(work->response_buf); - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->CreditRequest = cpu_to_le16(0); - rsp_hdr->Command = SMB2_OPLOCK_BREAK; - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = cpu_to_le64(-1); - rsp_hdr->Id.SyncId.ProcessId = 0; - rsp_hdr->Id.SyncId.TreeId = 0; - rsp_hdr->SessionId = 0; - memset(rsp_hdr->Signature, 0, 16); - - rsp = smb2_get_msg(work->response_buf); - rsp->StructureSize = cpu_to_le16(44); - rsp->Epoch = br_info->epoch; - rsp->Flags = 0; - - if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) - rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; - - memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE); - rsp->CurrentLeaseState = br_info->curr_state; - rsp->NewLeaseState = br_info->new_state; - rsp->BreakReason = 0; - rsp->AccessMaskHint = 0; - rsp->ShareMaskHint = 0; - - inc_rfc1001_len(work->response_buf, 44); - - ksmbd_conn_write(work); - -out: - ksmbd_free_work_struct(work); -} - -/** - * smb2_lease_break_noti() - break lease when a new client request - * write lease - * @opinfo: conains lease state information - * - * Return: 0 on success, otherwise error - */ -static int smb2_lease_break_noti(struct oplock_info *opinfo) -{ - struct ksmbd_conn *conn = opinfo->conn; - struct list_head *tmp, *t; - struct ksmbd_work *work; - struct lease_break_info *br_info; - struct lease *lease = opinfo->o_lease; - - work = ksmbd_alloc_work_struct(); - if (!work) - return -ENOMEM; - - br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); - if (!br_info) { - ksmbd_free_work_struct(work); - return -ENOMEM; - } - - br_info->curr_state = lease->state; - br_info->new_state = lease->new_state; - if (lease->version == 2) - br_info->epoch = cpu_to_le16(++lease->epoch); - else - br_info->epoch = 0; - memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); - - work->request_buf = (char *)br_info; - work->conn = conn; - work->sess = opinfo->sess; - - if (opinfo->op_state == OPLOCK_ACK_WAIT) { - list_for_each_safe(tmp, t, &opinfo->interim_list) { - struct ksmbd_work *in_work; - - in_work = list_entry(tmp, struct ksmbd_work, - interim_entry); - setup_async_work(in_work, NULL, NULL); - smb2_send_interim_resp(in_work, STATUS_PENDING); - list_del(&in_work->interim_entry); - } - INIT_WORK(&work->work, __smb2_lease_break_noti); - ksmbd_queue_work(work); - wait_for_break_ack(opinfo); - } else { - __smb2_lease_break_noti(&work->work); - if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - opinfo->o_lease->state = SMB2_LEASE_NONE_LE; - } - } - return 0; -} - -static void wait_lease_breaking(struct oplock_info *opinfo) -{ - if (!opinfo->is_lease) - return; - - wake_up_interruptible_all(&opinfo->oplock_brk); - if (atomic_read(&opinfo->breaking_cnt)) { - int ret = 0; - - ret = wait_event_interruptible_timeout(opinfo->oplock_brk, - atomic_read(&opinfo->breaking_cnt) == 0, - HZ); - if (!ret) - atomic_set(&opinfo->breaking_cnt, 0); - } -} - -static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) -{ - int err = 0; - - /* Need to break exclusive/batch oplock, write lease or overwrite_if */ - ksmbd_debug(OPLOCK, - "request to send oplock(level : 0x%x) break notification\n", - brk_opinfo->level); - - if (brk_opinfo->is_lease) { - struct lease *lease = brk_opinfo->o_lease; - - atomic_inc(&brk_opinfo->breaking_cnt); - - err = oplock_break_pending(brk_opinfo, req_op_level); - if (err) - return err < 0 ? err : 0; - - if (brk_opinfo->open_trunc) { - /* - * Create overwrite break trigger the lease break to - * none. - */ - lease->new_state = SMB2_LEASE_NONE_LE; - } else { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - lease->new_state = - SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE; - else - lease->new_state = - SMB2_LEASE_READ_CACHING_LE; - } else { - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - lease->new_state = - SMB2_LEASE_READ_CACHING_LE; - else - lease->new_state = SMB2_LEASE_NONE_LE; - } - } - - if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) - brk_opinfo->op_state = OPLOCK_ACK_WAIT; - else - atomic_dec(&brk_opinfo->breaking_cnt); - } else { - err = oplock_break_pending(brk_opinfo, req_op_level); - if (err) - return err < 0 ? err : 0; - - if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || - brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) - brk_opinfo->op_state = OPLOCK_ACK_WAIT; - } - - if (brk_opinfo->is_lease) - err = smb2_lease_break_noti(brk_opinfo); - else - err = smb2_oplock_break_noti(brk_opinfo); - - ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); - if (brk_opinfo->op_state == OPLOCK_CLOSING) - err = -ENOENT; - wake_up_oplock_break(brk_opinfo); - - wait_lease_breaking(brk_opinfo); - - return err; -} - -void destroy_lease_table(struct ksmbd_conn *conn) -{ - struct lease_table *lb, *lbtmp; - struct oplock_info *opinfo; - - write_lock(&lease_list_lock); - if (list_empty(&lease_table_list)) { - write_unlock(&lease_list_lock); - return; - } - - list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { - if (conn && memcmp(lb->client_guid, conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) - continue; -again: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, - lease_entry) { - rcu_read_unlock(); - lease_del_list(opinfo); - goto again; - } - rcu_read_unlock(); - list_del(&lb->l_entry); - kfree(lb); - } - write_unlock(&lease_list_lock); -} - -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, - struct lease_ctx_info *lctx) -{ - struct oplock_info *opinfo; - int err = 0; - struct lease_table *lb; - - if (!lctx) - return err; - - read_lock(&lease_list_lock); - if (list_empty(&lease_table_list)) { - read_unlock(&lease_list_lock); - return 0; - } - - list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, sess->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) - goto found; - } - read_unlock(&lease_list_lock); - - return 0; - -found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) - continue; - rcu_read_unlock(); - if (opinfo->o_fp->f_ci == ci) - goto op_next; - err = compare_guid_key(opinfo, sess->ClientGUID, - lctx->lease_key); - if (err) { - err = -EINVAL; - ksmbd_debug(OPLOCK, - "found same lease key is already used in other files\n"); - opinfo_put(opinfo); - goto out; - } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); - } - rcu_read_unlock(); - -out: - read_unlock(&lease_list_lock); - return err; -} - -static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) -{ - struct lease *lease1 = op1->o_lease; - struct lease *lease2 = op2->o_lease; - - op2->level = op1->level; - lease2->state = lease1->state; - memcpy(lease2->lease_key, lease1->lease_key, - SMB2_LEASE_KEY_SIZE); - lease2->duration = lease1->duration; - lease2->flags = lease1->flags; -} - -static int add_lease_global_list(struct oplock_info *opinfo) -{ - struct lease_table *lb; - - read_lock(&lease_list_lock); - list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) { - opinfo->o_lease->l_lb = lb; - lease_add_list(opinfo); - read_unlock(&lease_list_lock); - return 0; - } - } - read_unlock(&lease_list_lock); - - lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); - if (!lb) - return -ENOMEM; - - memcpy(lb->client_guid, opinfo->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE); - INIT_LIST_HEAD(&lb->lease_list); - spin_lock_init(&lb->lb_lock); - opinfo->o_lease->l_lb = lb; - lease_add_list(opinfo); - lb_add(lb); - return 0; -} - -static void set_oplock_level(struct oplock_info *opinfo, int level, - struct lease_ctx_info *lctx) -{ - switch (level) { - case SMB2_OPLOCK_LEVEL_BATCH: - case SMB2_OPLOCK_LEVEL_EXCLUSIVE: - grant_write_oplock(opinfo, level, lctx); - break; - case SMB2_OPLOCK_LEVEL_II: - grant_read_oplock(opinfo, lctx); - break; - default: - grant_none_oplock(opinfo, lctx); - break; - } -} - -/** - * smb_grant_oplock() - handle oplock/lease request on file open - * @work: smb work - * @req_op_level: oplock level - * @pid: id of open file - * @fp: ksmbd file pointer - * @tid: Tree id of connection - * @lctx: lease context information on file open - * @share_ret: share mode - * - * Return: 0 on success, otherwise error - */ -int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, - struct ksmbd_file *fp, __u16 tid, - struct lease_ctx_info *lctx, int share_ret) -{ - struct ksmbd_session *sess = work->sess; - int err = 0; - struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; - struct ksmbd_inode *ci = fp->f_ci; - bool prev_op_has_lease; - __le32 prev_op_state = 0; - - /* not support directory lease */ - if (S_ISDIR(file_inode(fp->filp)->i_mode)) - return 0; - - opinfo = alloc_opinfo(work, pid, tid); - if (!opinfo) - return -ENOMEM; - - if (lctx) { - err = alloc_lease(opinfo, lctx); - if (err) - goto err_out; - opinfo->is_lease = 1; - } - - /* ci does not have any oplock */ - if (!opinfo_count(fp)) - goto set_lev; - - /* grant none-oplock if second open is trunc */ - if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && - fp->cdoption != FILE_OVERWRITE_LE && - fp->cdoption != FILE_SUPERSEDE_LE) { - req_op_level = SMB2_OPLOCK_LEVEL_NONE; - goto set_lev; - } - - if (lctx) { - struct oplock_info *m_opinfo; - - /* is lease already granted ? */ - m_opinfo = same_client_has_lease(ci, sess->ClientGUID, - lctx); - if (m_opinfo) { - copy_lease(m_opinfo, opinfo); - if (atomic_read(&m_opinfo->breaking_cnt)) - opinfo->o_lease->flags = - SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; - goto out; - } - } - prev_opinfo = opinfo_get_list(ci); - if (!prev_opinfo || - (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) { - opinfo_conn_put(prev_opinfo); - goto set_lev; - } - prev_op_has_lease = prev_opinfo->is_lease; - if (prev_op_has_lease) - prev_op_state = prev_opinfo->o_lease->state; - - if (share_ret < 0 && - prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - err = share_ret; - opinfo_conn_put(prev_opinfo); - goto err_out; - } - - if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && - prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_conn_put(prev_opinfo); - goto op_break_not_needed; - } - - list_add(&work->interim_entry, &prev_opinfo->interim_list); - err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_conn_put(prev_opinfo); - if (err == -ENOENT) - goto set_lev; - /* Check all oplock was freed by close */ - else if (err < 0) - goto err_out; - -op_break_not_needed: - if (share_ret < 0) { - err = share_ret; - goto err_out; - } - - if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) - req_op_level = SMB2_OPLOCK_LEVEL_II; - - /* grant fixed oplock on stacked locking between lease and oplock */ - if (prev_op_has_lease && !lctx) - if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) - req_op_level = SMB2_OPLOCK_LEVEL_NONE; - - if (!prev_op_has_lease && lctx) { - req_op_level = SMB2_OPLOCK_LEVEL_II; - lctx->req_state = SMB2_LEASE_READ_CACHING_LE; - } - -set_lev: - set_oplock_level(opinfo, req_op_level, lctx); - -out: - rcu_assign_pointer(fp->f_opinfo, opinfo); - opinfo->o_fp = fp; - - opinfo_count_inc(fp); - opinfo_add(opinfo); - if (opinfo->is_lease) { - err = add_lease_global_list(opinfo); - if (err) - goto err_out; - } - - return 0; -err_out: - free_opinfo(opinfo); - return err; -} - -/** - * smb_break_all_write_oplock() - break batch/exclusive oplock to level2 - * @work: smb work - * @fp: ksmbd file pointer - * @is_trunc: truncate on open - */ -static void smb_break_all_write_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc) -{ - struct oplock_info *brk_opinfo; - - brk_opinfo = opinfo_get_list(fp->f_ci); - if (!brk_opinfo) - return; - if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && - brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_conn_put(brk_opinfo); - return; - } - - brk_opinfo->open_trunc = is_trunc; - list_add(&work->interim_entry, &brk_opinfo->interim_list); - oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_conn_put(brk_opinfo); -} - -/** - * smb_break_all_levII_oplock() - send level2 oplock or read lease break command - * from server to client - * @work: smb work - * @fp: ksmbd file pointer - * @is_trunc: truncate on open - */ -void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, - int is_trunc) -{ - struct oplock_info *op, *brk_op; - struct ksmbd_inode *ci; - struct ksmbd_conn *conn = work->conn; - - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_OPLOCKS)) - return; - - ci = fp->f_ci; - op = opinfo_get(fp); - - rcu_read_lock(); - list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { - if (!atomic_inc_not_zero(&brk_op->refcount)) - continue; - - atomic_inc(&brk_op->conn->r_count); - if (ksmbd_conn_releasing(brk_op->conn)) { - atomic_dec(&brk_op->conn->r_count); - continue; - } - - rcu_read_unlock(); - if (brk_op->is_lease && (brk_op->o_lease->state & - (~(SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)))) { - ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", - brk_op->o_lease->state); - goto next; - } else if (brk_op->level != - SMB2_OPLOCK_LEVEL_II) { - ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", - brk_op->level); - goto next; - } - - /* Skip oplock being break to none */ - if (brk_op->is_lease && - brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && - atomic_read(&brk_op->breaking_cnt)) - goto next; - - if (op && op->is_lease && brk_op->is_lease && - !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE) && - !memcmp(op->o_lease->lease_key, brk_op->o_lease->lease_key, - SMB2_LEASE_KEY_SIZE)) - goto next; - brk_op->open_trunc = is_trunc; - oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); -next: - opinfo_conn_put(brk_op); - rcu_read_lock(); - } - rcu_read_unlock(); - - if (op) - opinfo_put(op); -} - -/** - * smb_break_all_oplock() - break both batch/exclusive and level2 oplock - * @work: smb work - * @fp: ksmbd file pointer - */ -void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) -{ - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_OPLOCKS)) - return; - - smb_break_all_write_oplock(work, fp, 1); - smb_break_all_levII_oplock(work, fp, 1); -} - -/** - * smb2_map_lease_to_oplock() - map lease state to corresponding oplock type - * @lease_state: lease type - * - * Return: 0 if no mapping, otherwise corresponding oplock type - */ -__u8 smb2_map_lease_to_oplock(__le32 lease_state) -{ - if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE)) { - return SMB2_OPLOCK_LEVEL_BATCH; - } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && - lease_state & SMB2_LEASE_WRITE_CACHING_LE) { - if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) - return SMB2_OPLOCK_LEVEL_EXCLUSIVE; - } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { - return SMB2_OPLOCK_LEVEL_II; - } - return 0; -} - -/** - * create_lease_buf() - create lease context for open cmd response - * @rbuf: buffer to create lease context response - * @lease: buffer to stored parsed lease state information - */ -void create_lease_buf(u8 *rbuf, struct lease *lease) -{ - if (lease->version == 2) { - struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; - - memset(buf, 0, sizeof(struct create_lease_v2)); - memcpy(buf->lcontext.LeaseKey, lease->lease_key, - SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; - buf->lcontext.LeaseState = lease->state; - memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, - SMB2_LEASE_KEY_SIZE); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease_v2, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease_v2, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - } else { - struct create_lease *buf = (struct create_lease *)rbuf; - - memset(buf, 0, sizeof(struct create_lease)); - memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; - buf->lcontext.LeaseState = lease->state; - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - } -} - -/** - * parse_lease_state() - parse lease context containted in file open request - * @open_req: buffer containing smb2 file open(create) request - * - * Return: oplock state, -ENOENT if create lease context not found - */ -struct lease_ctx_info *parse_lease_state(void *open_req) -{ - struct create_context *cc; - struct smb2_create_req *req = (struct smb2_create_req *)open_req; - struct lease_ctx_info *lreq; - - cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); - if (IS_ERR_OR_NULL(cc)) - return NULL; - - lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); - if (!lreq) - return NULL; - - if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { - struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; - - memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; - memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, - SMB2_LEASE_KEY_SIZE); - lreq->version = 2; - } else { - struct create_lease *lc = (struct create_lease *)cc; - - memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; - lreq->version = 1; - } - return lreq; -} - -/** - * smb2_find_context_vals() - find a particular context info in open request - * @open_req: buffer containing smb2 file open(create) request - * @tag: context name to search for - * @tag_len: the length of tag - * - * Return: pointer to requested context, NULL if @str context not found - * or error pointer if name length is invalid. - */ -struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len) -{ - struct create_context *cc; - unsigned int next = 0; - char *name; - struct smb2_create_req *req = (struct smb2_create_req *)open_req; - unsigned int remain_len, name_off, name_len, value_off, value_len, - cc_len; - - /* - * CreateContextsOffset and CreateContextsLength are guaranteed to - * be valid because of ksmbd_smb2_check_message(). - */ - cc = (struct create_context *)((char *)req + - le32_to_cpu(req->CreateContextsOffset)); - remain_len = le32_to_cpu(req->CreateContextsLength); - do { - cc = (struct create_context *)((char *)cc + next); - if (remain_len < offsetof(struct create_context, Buffer)) - return ERR_PTR(-EINVAL); - - next = le32_to_cpu(cc->Next); - name_off = le16_to_cpu(cc->NameOffset); - name_len = le16_to_cpu(cc->NameLength); - value_off = le16_to_cpu(cc->DataOffset); - value_len = le32_to_cpu(cc->DataLength); - cc_len = next ? next : remain_len; - - if ((next & 0x7) != 0 || - next > remain_len || - name_off != offsetof(struct create_context, Buffer) || - name_len < 4 || - name_off + name_len > cc_len || - (value_off & 0x7) != 0 || - (value_off && (value_off < name_off + name_len)) || - ((u64)value_off + value_len > cc_len)) - return ERR_PTR(-EINVAL); - - name = (char *)cc + name_off; - if (name_len == tag_len && !memcmp(name, tag, name_len)) - return cc; - - remain_len -= next; - } while (next != 0); - - return NULL; -} - -/** - * create_durable_rsp_buf() - create durable handle context - * @cc: buffer to create durable context response - */ -void create_durable_rsp_buf(char *cc) -{ - struct create_durable_rsp *buf; - - buf = (struct create_durable_rsp *)cc; - memset(buf, 0, sizeof(struct create_durable_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Data)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'Q'; -} - -/** - * create_durable_v2_rsp_buf() - create durable handle v2 context - * @cc: buffer to create durable context response - * @fp: ksmbd file pointer - */ -void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) -{ - struct create_durable_v2_rsp *buf; - - buf = (struct create_durable_v2_rsp *)cc; - memset(buf, 0, sizeof(struct create_durable_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Data)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'Q'; - - buf->Timeout = cpu_to_le32(fp->durable_timeout); -} - -/** - * create_mxac_rsp_buf() - create query maximal access context - * @cc: buffer to create maximal access context response - * @maximal_access: maximal access - */ -void create_mxac_rsp_buf(char *cc, int maximal_access) -{ - struct create_mxac_rsp *buf; - - buf = (struct create_mxac_rsp *)cc; - memset(buf, 0, sizeof(struct create_mxac_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_mxac_rsp, QueryStatus)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_mxac_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */ - buf->Name[0] = 'M'; - buf->Name[1] = 'x'; - buf->Name[2] = 'A'; - buf->Name[3] = 'c'; - - buf->QueryStatus = STATUS_SUCCESS; - buf->MaximalAccess = cpu_to_le32(maximal_access); -} - -void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) -{ - struct create_disk_id_rsp *buf; - - buf = (struct create_disk_id_rsp *)cc; - memset(buf, 0, sizeof(struct create_disk_id_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_disk_id_rsp, DiskFileId)); - buf->ccontext.DataLength = cpu_to_le32(32); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_mxac_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */ - buf->Name[0] = 'Q'; - buf->Name[1] = 'F'; - buf->Name[2] = 'i'; - buf->Name[3] = 'd'; - - buf->DiskFileId = cpu_to_le64(file_id); - buf->VolumeId = cpu_to_le64(vol_id); -} - -/** - * create_posix_rsp_buf() - create posix extension context - * @cc: buffer to create posix on posix response - * @fp: ksmbd file pointer - */ -void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) -{ - struct create_posix_rsp *buf; - struct inode *inode = file_inode(fp->filp); - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); - vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); - - buf = (struct create_posix_rsp *)cc; - memset(buf, 0, sizeof(struct create_posix_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_posix_rsp, nlink)); - /* - * DataLength = nlink(4) + reparse_tag(4) + mode(4) + - * domain sid(28) + unix group sid(16). - */ - buf->ccontext.DataLength = cpu_to_le32(56); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_posix_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - buf->Name[0] = 0x93; - buf->Name[1] = 0xAD; - buf->Name[2] = 0x25; - buf->Name[3] = 0x50; - buf->Name[4] = 0x9C; - buf->Name[5] = 0xB4; - buf->Name[6] = 0x11; - buf->Name[7] = 0xE7; - buf->Name[8] = 0xB4; - buf->Name[9] = 0x23; - buf->Name[10] = 0x83; - buf->Name[11] = 0xDE; - buf->Name[12] = 0x96; - buf->Name[13] = 0x8B; - buf->Name[14] = 0xCD; - buf->Name[15] = 0x7C; - - buf->nlink = cpu_to_le32(inode->i_nlink); - buf->reparse_tag = cpu_to_le32(fp->volatile_id); - buf->mode = cpu_to_le32(inode->i_mode & 0777); - /* - * SidBuffer(44) contain two sids(Domain sid(28), UNIX group sid(16)). - * Domain sid(28) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 4(num_subauth)) + RID(4). - * UNIX group id(16) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 1(num_subauth)) + RID(4). - */ - id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), - SIDOWNER, (struct smb_sid *)&buf->SidBuffer[0]); - id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), - SIDUNIX_GROUP, (struct smb_sid *)&buf->SidBuffer[28]); -} - -/* - * Find lease object(opinfo) for given lease key/fid from lease - * break/file close path. - */ -/** - * lookup_lease_in_table() - find a matching lease info object - * @conn: connection instance - * @lease_key: lease key to be searched for - * - * Return: opinfo if found matching opinfo, otherwise NULL - */ -struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key) -{ - struct oplock_info *opinfo = NULL, *ret_op = NULL; - struct lease_table *lt; - int ret; - - read_lock(&lease_list_lock); - list_for_each_entry(lt, &lease_table_list, l_entry) { - if (!memcmp(lt->client_guid, conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) - goto found; - } - - read_unlock(&lease_list_lock); - return NULL; - -found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) - continue; - rcu_read_unlock(); - if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) - goto op_next; - if (!(opinfo->o_lease->state & - (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE))) - goto op_next; - ret = compare_guid_key(opinfo, conn->ClientGUID, - lease_key); - if (ret) { - ksmbd_debug(OPLOCK, "found opinfo\n"); - ret_op = opinfo; - goto out; - } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); - } - rcu_read_unlock(); - -out: - read_unlock(&lease_list_lock); - return ret_op; -} diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h deleted file mode 100644 index 4b0fe6da7694..000000000000 --- a/fs/ksmbd/oplock.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_OPLOCK_H -#define __KSMBD_OPLOCK_H - -#include "smb_common.h" - -#define OPLOCK_WAIT_TIME (35 * HZ) - -/* SMB2 Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF - -/* Oplock states */ -#define OPLOCK_STATE_NONE 0x00 -#define OPLOCK_ACK_WAIT 0x01 -#define OPLOCK_CLOSING 0x02 - -#define OPLOCK_WRITE_TO_READ 0x01 -#define OPLOCK_READ_HANDLE_TO_READ 0x02 -#define OPLOCK_WRITE_TO_NONE 0x04 -#define OPLOCK_READ_TO_NONE 0x08 - -struct lease_ctx_info { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - __le32 req_state; - __le32 flags; - __le64 duration; - __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; - int version; -}; - -struct lease_table { - char client_guid[SMB2_CLIENT_GUID_SIZE]; - struct list_head lease_list; - struct list_head l_entry; - spinlock_t lb_lock; -}; - -struct lease { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - __le32 state; - __le32 new_state; - __le32 flags; - __le64 duration; - __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; - int version; - unsigned short epoch; - struct lease_table *l_lb; -}; - -struct oplock_info { - struct ksmbd_conn *conn; - struct ksmbd_session *sess; - struct ksmbd_work *work; - struct ksmbd_file *o_fp; - int level; - int op_state; - unsigned long pending_break; - u64 fid; - atomic_t breaking_cnt; - atomic_t refcount; - __u16 Tid; - bool is_lease; - bool open_trunc; /* truncate on open */ - struct lease *o_lease; - struct list_head interim_list; - struct list_head op_entry; - struct list_head lease_entry; - wait_queue_head_t oplock_q; /* Other server threads */ - wait_queue_head_t oplock_brk; /* oplock breaking wait */ - struct rcu_head rcu_head; -}; - -struct lease_break_info { - __le32 curr_state; - __le32 new_state; - __le16 epoch; - char lease_key[SMB2_LEASE_KEY_SIZE]; -}; - -struct oplock_break_info { - int level; - int open_trunc; - int fid; -}; - -int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, - u64 pid, struct ksmbd_file *fp, __u16 tid, - struct lease_ctx_info *lctx, int share_ret); -void smb_break_all_levII_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc); -int opinfo_write_to_read(struct oplock_info *opinfo); -int opinfo_read_handle_to_read(struct oplock_info *opinfo); -int opinfo_write_to_none(struct oplock_info *opinfo); -int opinfo_read_to_none(struct oplock_info *opinfo); -void close_id_del_oplock(struct ksmbd_file *fp); -void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp); -struct oplock_info *opinfo_get(struct ksmbd_file *fp); -void opinfo_put(struct oplock_info *opinfo); - -/* Lease related functions */ -void create_lease_buf(u8 *rbuf, struct lease *lease); -struct lease_ctx_info *parse_lease_state(void *open_req); -__u8 smb2_map_lease_to_oplock(__le32 lease_state); -int lease_read_to_write(struct oplock_info *opinfo); - -/* Durable related functions */ -void create_durable_rsp_buf(char *cc); -void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); -void create_mxac_rsp_buf(char *cc, int maximal_access); -void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); -void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); -struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); -struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key); -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, - struct lease_ctx_info *lctx); -void destroy_lease_table(struct ksmbd_conn *conn); -#endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c deleted file mode 100644 index 847ee62afb8a..000000000000 --- a/fs/ksmbd/server.c +++ /dev/null @@ -1,646 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include "glob.h" -#include "oplock.h" -#include "misc.h" -#include -#include -#include -#include -#include - -#include "server.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "connection.h" -#include "transport_ipc.h" -#include "mgmt/user_session.h" -#include "crypto_ctx.h" -#include "auth.h" - -int ksmbd_debug_types; - -struct ksmbd_server_config server_conf; - -enum SERVER_CTRL_TYPE { - SERVER_CTRL_TYPE_INIT, - SERVER_CTRL_TYPE_RESET, -}; - -struct server_ctrl_struct { - int type; - struct work_struct ctrl_work; -}; - -static DEFINE_MUTEX(ctrl_lock); - -static int ___server_conf_set(int idx, char *val) -{ - if (idx >= ARRAY_SIZE(server_conf.conf)) - return -EINVAL; - - if (!val || val[0] == 0x00) - return -EINVAL; - - kfree(server_conf.conf[idx]); - server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); - if (!server_conf.conf[idx]) - return -ENOMEM; - return 0; -} - -int ksmbd_set_netbios_name(char *v) -{ - return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); -} - -int ksmbd_set_server_string(char *v) -{ - return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); -} - -int ksmbd_set_work_group(char *v) -{ - return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); -} - -char *ksmbd_netbios_name(void) -{ - return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; -} - -char *ksmbd_server_string(void) -{ - return server_conf.conf[SERVER_CONF_SERVER_STRING]; -} - -char *ksmbd_work_group(void) -{ - return server_conf.conf[SERVER_CONF_WORK_GROUP]; -} - -/** - * check_conn_state() - check state of server thread connection - * @work: smb work containing server thread information - * - * Return: 0 on valid connection, otherwise 1 to reconnect - */ -static inline int check_conn_state(struct ksmbd_work *work) -{ - struct smb_hdr *rsp_hdr; - - if (ksmbd_conn_exiting(work->conn) || - ksmbd_conn_need_reconnect(work->conn)) { - rsp_hdr = work->response_buf; - rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; - return 1; - } - return 0; -} - -#define SERVER_HANDLER_CONTINUE 0 -#define SERVER_HANDLER_ABORT 1 - -static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, - u16 *cmd) -{ - struct smb_version_cmds *cmds; - u16 command; - int ret; - - if (check_conn_state(work)) - return SERVER_HANDLER_CONTINUE; - - if (ksmbd_verify_smb_message(work)) - return SERVER_HANDLER_ABORT; - - command = conn->ops->get_cmd_val(work); - *cmd = command; - -andx_again: - if (command >= conn->max_cmds) { - conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return SERVER_HANDLER_CONTINUE; - } - - cmds = &conn->cmds[command]; - if (!cmds->proc) { - ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); - conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); - return SERVER_HANDLER_CONTINUE; - } - - if (work->sess && conn->ops->is_sign_req(work, command)) { - ret = conn->ops->check_sign_req(work); - if (!ret) { - conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); - return SERVER_HANDLER_CONTINUE; - } - } - - ret = cmds->proc(work); - - if (ret < 0) - ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); - /* AndX commands - chained request can return positive values */ - else if (ret > 0) { - command = ret; - *cmd = command; - goto andx_again; - } - - if (work->send_no_response) - return SERVER_HANDLER_ABORT; - return SERVER_HANDLER_CONTINUE; -} - -static void __handle_ksmbd_work(struct ksmbd_work *work, - struct ksmbd_conn *conn) -{ - u16 command = 0; - int rc; - - if (conn->ops->allocate_rsp_buf(work)) - return; - - if (conn->ops->is_transform_hdr && - conn->ops->is_transform_hdr(work->request_buf)) { - rc = conn->ops->decrypt_req(work); - if (rc < 0) { - conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); - goto send; - } - - work->encrypted = true; - } - - rc = conn->ops->init_rsp_hdr(work); - if (rc) { - /* either uid or tid is not correct */ - conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); - goto send; - } - - do { - if (conn->ops->check_user_session) { - rc = conn->ops->check_user_session(work); - if (rc < 0) { - if (rc == -EINVAL) - conn->ops->set_rsp_status(work, - STATUS_INVALID_PARAMETER); - else - conn->ops->set_rsp_status(work, - STATUS_USER_SESSION_DELETED); - goto send; - } else if (rc > 0) { - rc = conn->ops->get_ksmbd_tcon(work); - if (rc < 0) { - if (rc == -EINVAL) - conn->ops->set_rsp_status(work, - STATUS_INVALID_PARAMETER); - else - conn->ops->set_rsp_status(work, - STATUS_NETWORK_NAME_DELETED); - goto send; - } - } - } - - rc = __process_request(work, conn, &command); - if (rc == SERVER_HANDLER_ABORT) - break; - - /* - * Call smb2_set_rsp_credits() function to set number of credits - * granted in hdr of smb2 response. - */ - if (conn->ops->set_rsp_credits) { - spin_lock(&conn->credits_lock); - rc = conn->ops->set_rsp_credits(work); - spin_unlock(&conn->credits_lock); - if (rc < 0) { - conn->ops->set_rsp_status(work, - STATUS_INVALID_PARAMETER); - goto send; - } - } - - if (work->sess && - (work->sess->sign || smb3_11_final_sess_setup_resp(work) || - conn->ops->is_sign_req(work, command))) - conn->ops->set_sign_rsp(work); - } while (is_chained_smb2_message(work)); - - if (work->send_no_response) - return; - -send: - smb3_preauth_hash_rsp(work); - if (work->sess && work->sess->enc && work->encrypted && - conn->ops->encrypt_resp) { - rc = conn->ops->encrypt_resp(work); - if (rc < 0) - conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); - } - - ksmbd_conn_write(work); -} - -/** - * handle_ksmbd_work() - process pending smb work requests - * @wk: smb work containing request command buffer - * - * called by kworker threads to processing remaining smb work requests - */ -static void handle_ksmbd_work(struct work_struct *wk) -{ - struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct ksmbd_conn *conn = work->conn; - - atomic64_inc(&conn->stats.request_served); - - __handle_ksmbd_work(work, conn); - - ksmbd_conn_try_dequeue_request(work); - ksmbd_free_work_struct(work); - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); -} - -/** - * queue_ksmbd_work() - queue a smb request to worker thread queue - * for proccessing smb command and sending response - * @conn: connection instance - * - * read remaining data from socket create and submit work. - */ -static int queue_ksmbd_work(struct ksmbd_conn *conn) -{ - struct ksmbd_work *work; - - work = ksmbd_alloc_work_struct(); - if (!work) { - pr_err("allocation for work failed\n"); - return -ENOMEM; - } - - work->conn = conn; - work->request_buf = conn->request_buf; - conn->request_buf = NULL; - - ksmbd_init_smb_server(work); - - ksmbd_conn_enqueue_request(work); - atomic_inc(&conn->r_count); - /* update activity on connection */ - conn->last_active = jiffies; - INIT_WORK(&work->work, handle_ksmbd_work); - ksmbd_queue_work(work); - return 0; -} - -static int ksmbd_server_process_request(struct ksmbd_conn *conn) -{ - return queue_ksmbd_work(conn); -} - -static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) -{ - ksmbd_sessions_deregister(conn); - destroy_lease_table(conn); - return 0; -} - -static void ksmbd_server_tcp_callbacks_init(void) -{ - struct ksmbd_conn_ops ops; - - ops.process_fn = ksmbd_server_process_request; - ops.terminate_fn = ksmbd_server_terminate_conn; - - ksmbd_conn_init_server_callbacks(&ops); -} - -static void server_conf_free(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { - kfree(server_conf.conf[i]); - server_conf.conf[i] = NULL; - } -} - -static int server_conf_init(void) -{ - WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); - server_conf.enforced_signing = 0; - server_conf.min_protocol = ksmbd_min_protocol(); - server_conf.max_protocol = ksmbd_max_protocol(); - server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; -#ifdef CONFIG_SMB_SERVER_KERBEROS5 - server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | - KSMBD_AUTH_MSKRB5; -#endif - return 0; -} - -static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) -{ - int ret; - - ret = ksmbd_conn_transport_init(); - if (ret) { - server_queue_ctrl_reset_work(); - return; - } - - WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); -} - -static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) -{ - ksmbd_ipc_soft_reset(); - ksmbd_conn_transport_destroy(); - server_conf_free(); - server_conf_init(); - WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); -} - -static void server_ctrl_handle_work(struct work_struct *work) -{ - struct server_ctrl_struct *ctrl; - - ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); - - mutex_lock(&ctrl_lock); - switch (ctrl->type) { - case SERVER_CTRL_TYPE_INIT: - server_ctrl_handle_init(ctrl); - break; - case SERVER_CTRL_TYPE_RESET: - server_ctrl_handle_reset(ctrl); - break; - default: - pr_err("Unknown server work type: %d\n", ctrl->type); - } - mutex_unlock(&ctrl_lock); - kfree(ctrl); - module_put(THIS_MODULE); -} - -static int __queue_ctrl_work(int type) -{ - struct server_ctrl_struct *ctrl; - - ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); - if (!ctrl) - return -ENOMEM; - - __module_get(THIS_MODULE); - ctrl->type = type; - INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); - queue_work(system_long_wq, &ctrl->ctrl_work); - return 0; -} - -int server_queue_ctrl_init_work(void) -{ - return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); -} - -int server_queue_ctrl_reset_work(void) -{ - return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); -} - -static ssize_t stats_show(struct class *class, struct class_attribute *attr, - char *buf) -{ - /* - * Inc this each time you change stats output format, - * so user space will know what to do. - */ - static int stats_version = 2; - static const char * const state[] = { - "startup", - "running", - "reset", - "shutdown" - }; - - ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, - state[server_conf.state], server_conf.tcp_port, - server_conf.ipc_last_active / HZ); - return sz; -} - -static ssize_t kill_server_store(struct class *class, - struct class_attribute *attr, const char *buf, - size_t len) -{ - if (!sysfs_streq(buf, "hard")) - return len; - - pr_info("kill command received\n"); - mutex_lock(&ctrl_lock); - WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); - __module_get(THIS_MODULE); - server_ctrl_handle_reset(NULL); - module_put(THIS_MODULE); - mutex_unlock(&ctrl_lock); - return len; -} - -static const char * const debug_type_strings[] = {"smb", "auth", "vfs", - "oplock", "ipc", "conn", - "rdma"}; - -static ssize_t debug_show(struct class *class, struct class_attribute *attr, - char *buf) -{ - ssize_t sz = 0; - int i, pos = 0; - - for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { - if ((ksmbd_debug_types >> i) & 1) { - pos = scnprintf(buf + sz, - PAGE_SIZE - sz, - "[%s] ", - debug_type_strings[i]); - } else { - pos = scnprintf(buf + sz, - PAGE_SIZE - sz, - "%s ", - debug_type_strings[i]); - } - sz += pos; - } - sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); - return sz; -} - -static ssize_t debug_store(struct class *class, struct class_attribute *attr, - const char *buf, size_t len) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { - if (sysfs_streq(buf, "all")) { - if (ksmbd_debug_types == KSMBD_DEBUG_ALL) - ksmbd_debug_types = 0; - else - ksmbd_debug_types = KSMBD_DEBUG_ALL; - break; - } - - if (sysfs_streq(buf, debug_type_strings[i])) { - if (ksmbd_debug_types & (1 << i)) - ksmbd_debug_types &= ~(1 << i); - else - ksmbd_debug_types |= (1 << i); - break; - } - } - - return len; -} - -static CLASS_ATTR_RO(stats); -static CLASS_ATTR_WO(kill_server); -static CLASS_ATTR_RW(debug); - -static struct attribute *ksmbd_control_class_attrs[] = { - &class_attr_stats.attr, - &class_attr_kill_server.attr, - &class_attr_debug.attr, - NULL, -}; -ATTRIBUTE_GROUPS(ksmbd_control_class); - -static struct class ksmbd_control_class = { - .name = "ksmbd-control", - .owner = THIS_MODULE, - .class_groups = ksmbd_control_class_groups, -}; - -static int ksmbd_server_shutdown(void) -{ - WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); - - class_unregister(&ksmbd_control_class); - ksmbd_workqueue_destroy(); - ksmbd_ipc_release(); - ksmbd_conn_transport_destroy(); - ksmbd_crypto_destroy(); - ksmbd_free_global_file_table(); - destroy_lease_table(NULL); - ksmbd_work_pool_destroy(); - ksmbd_exit_file_cache(); - server_conf_free(); - return 0; -} - -static int __init ksmbd_server_init(void) -{ - int ret; - - ret = class_register(&ksmbd_control_class); - if (ret) { - pr_err("Unable to register ksmbd-control class\n"); - return ret; - } - - ksmbd_server_tcp_callbacks_init(); - - ret = server_conf_init(); - if (ret) - goto err_unregister; - - ret = ksmbd_work_pool_init(); - if (ret) - goto err_unregister; - - ret = ksmbd_init_file_cache(); - if (ret) - goto err_destroy_work_pools; - - ret = ksmbd_ipc_init(); - if (ret) - goto err_exit_file_cache; - - ret = ksmbd_init_global_file_table(); - if (ret) - goto err_ipc_release; - - ret = ksmbd_inode_hash_init(); - if (ret) - goto err_destroy_file_table; - - ret = ksmbd_crypto_create(); - if (ret) - goto err_release_inode_hash; - - ret = ksmbd_workqueue_init(); - if (ret) - goto err_crypto_destroy; - - pr_warn_once("The ksmbd server is experimental\n"); - - return 0; - -err_crypto_destroy: - ksmbd_crypto_destroy(); -err_release_inode_hash: - ksmbd_release_inode_hash(); -err_destroy_file_table: - ksmbd_free_global_file_table(); -err_ipc_release: - ksmbd_ipc_release(); -err_exit_file_cache: - ksmbd_exit_file_cache(); -err_destroy_work_pools: - ksmbd_work_pool_destroy(); -err_unregister: - class_unregister(&ksmbd_control_class); - - return ret; -} - -/** - * ksmbd_server_exit() - shutdown forker thread and free memory at module exit - */ -static void __exit ksmbd_server_exit(void) -{ - ksmbd_server_shutdown(); - rcu_barrier(); - ksmbd_release_inode_hash(); -} - -MODULE_AUTHOR("Namjae Jeon "); -MODULE_VERSION(KSMBD_VERSION); -MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); -MODULE_LICENSE("GPL"); -MODULE_SOFTDEP("pre: ecb"); -MODULE_SOFTDEP("pre: hmac"); -MODULE_SOFTDEP("pre: md5"); -MODULE_SOFTDEP("pre: nls"); -MODULE_SOFTDEP("pre: aes"); -MODULE_SOFTDEP("pre: cmac"); -MODULE_SOFTDEP("pre: sha256"); -MODULE_SOFTDEP("pre: sha512"); -MODULE_SOFTDEP("pre: aead2"); -MODULE_SOFTDEP("pre: ccm"); -MODULE_SOFTDEP("pre: gcm"); -MODULE_SOFTDEP("pre: crc32"); -module_init(ksmbd_server_init) -module_exit(ksmbd_server_exit) diff --git a/fs/ksmbd/server.h b/fs/ksmbd/server.h deleted file mode 100644 index db7278181760..000000000000 --- a/fs/ksmbd/server.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __SERVER_H__ -#define __SERVER_H__ - -#include "smbacl.h" - -/* - * Server state type - */ -enum { - SERVER_STATE_STARTING_UP, - SERVER_STATE_RUNNING, - SERVER_STATE_RESETTING, - SERVER_STATE_SHUTTING_DOWN, -}; - -/* - * Server global config string index - */ -enum { - SERVER_CONF_NETBIOS_NAME, - SERVER_CONF_SERVER_STRING, - SERVER_CONF_WORK_GROUP, -}; - -struct ksmbd_server_config { - unsigned int flags; - unsigned int state; - short signing; - short enforced_signing; - short min_protocol; - short max_protocol; - unsigned short tcp_port; - unsigned short ipc_timeout; - unsigned long ipc_last_active; - unsigned long deadtime; - unsigned int share_fake_fscaps; - struct smb_sid domain_sid; - unsigned int auth_mechs; - unsigned int max_connections; - - char *conf[SERVER_CONF_WORK_GROUP + 1]; -}; - -extern struct ksmbd_server_config server_conf; - -int ksmbd_set_netbios_name(char *v); -int ksmbd_set_server_string(char *v); -int ksmbd_set_work_group(char *v); - -char *ksmbd_netbios_name(void); -char *ksmbd_server_string(void); -char *ksmbd_work_group(void); - -static inline int ksmbd_server_running(void) -{ - return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING; -} - -static inline int ksmbd_server_configurable(void) -{ - return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING; -} - -int server_queue_ctrl_init_work(void); -int server_queue_ctrl_reset_work(void); -#endif /* __SERVER_H__ */ diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c deleted file mode 100644 index 33b7e6c4ceff..000000000000 --- a/fs/ksmbd/smb2misc.c +++ /dev/null @@ -1,454 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include "glob.h" -#include "nterr.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "mgmt/user_session.h" -#include "connection.h" - -static int check_smb2_hdr(struct smb2_hdr *hdr) -{ - /* - * Make sure that this really is an SMB, that it is a response. - */ - if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - return 1; - return 0; -} - -/* - * The following table defines the expected "StructureSize" of SMB2 requests - * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. - * - * Note that commands are defined in smb2pdu.h in le16 but the array below is - * indexed by command in host byte order - */ -static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ cpu_to_le16(36), - /* SMB2_SESSION_SETUP */ cpu_to_le16(25), - /* SMB2_LOGOFF */ cpu_to_le16(4), - /* SMB2_TREE_CONNECT */ cpu_to_le16(9), - /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), - /* SMB2_CREATE */ cpu_to_le16(57), - /* SMB2_CLOSE */ cpu_to_le16(24), - /* SMB2_FLUSH */ cpu_to_le16(24), - /* SMB2_READ */ cpu_to_le16(49), - /* SMB2_WRITE */ cpu_to_le16(49), - /* SMB2_LOCK */ cpu_to_le16(48), - /* SMB2_IOCTL */ cpu_to_le16(57), - /* SMB2_CANCEL */ cpu_to_le16(4), - /* SMB2_ECHO */ cpu_to_le16(4), - /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33), - /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32), - /* SMB2_QUERY_INFO */ cpu_to_le16(41), - /* SMB2_SET_INFO */ cpu_to_le16(33), - /* use 44 for lease break */ - /* SMB2_OPLOCK_BREAK */ cpu_to_le16(36) -}; - -/* - * The size of the variable area depends on the offset and length fields - * located in different fields for various SMB2 requests. SMB2 requests - * with no variable length info, show an offset of zero for the offset field. - */ -static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ true, - /* SMB2_SESSION_SETUP */ true, - /* SMB2_LOGOFF */ false, - /* SMB2_TREE_CONNECT */ true, - /* SMB2_TREE_DISCONNECT */ false, - /* SMB2_CREATE */ true, - /* SMB2_CLOSE */ false, - /* SMB2_FLUSH */ false, - /* SMB2_READ */ true, - /* SMB2_WRITE */ true, - /* SMB2_LOCK */ true, - /* SMB2_IOCTL */ true, - /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ - /* SMB2_ECHO */ false, - /* SMB2_QUERY_DIRECTORY */ true, - /* SMB2_CHANGE_NOTIFY */ false, - /* SMB2_QUERY_INFO */ true, - /* SMB2_SET_INFO */ true, - /* SMB2_OPLOCK_BREAK */ false -}; - -/* - * Set length of the data area and the offset to arguments. - * if they are invalid, return error. - */ -static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, - struct smb2_hdr *hdr) -{ - int ret = 0; - - *off = 0; - *len = 0; - - /* - * Following commands have data areas so we have to get the location - * of the data buffer offset and data buffer length for the particular - * command. - */ - switch (hdr->Command) { - case SMB2_SESSION_SETUP: - *off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); - *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); - break; - case SMB2_TREE_CONNECT: - *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); - *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); - break; - case SMB2_CREATE: - { - if (((struct smb2_create_req *)hdr)->CreateContextsLength) { - *off = le32_to_cpu(((struct smb2_create_req *) - hdr)->CreateContextsOffset); - *len = le32_to_cpu(((struct smb2_create_req *) - hdr)->CreateContextsLength); - break; - } - - *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); - *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); - break; - } - case SMB2_QUERY_INFO: - *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); - *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); - break; - case SMB2_SET_INFO: - *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); - *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); - break; - case SMB2_READ: - *off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); - *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); - break; - case SMB2_WRITE: - if (((struct smb2_write_req *)hdr)->DataOffset || - ((struct smb2_write_req *)hdr)->Length) { - *off = max_t(unsigned int, - le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), - offsetof(struct smb2_write_req, Buffer)); - *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); - break; - } - - *off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); - *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); - break; - case SMB2_QUERY_DIRECTORY: - *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); - *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); - break; - case SMB2_LOCK: - { - unsigned short lock_count; - - lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount); - if (lock_count > 0) { - *off = offsetof(struct smb2_lock_req, locks); - *len = sizeof(struct smb2_lock_element) * lock_count; - } - break; - } - case SMB2_IOCTL: - *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); - *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); - break; - default: - ksmbd_debug(SMB, "no length check for command\n"); - break; - } - - if (*off > 4096) { - ksmbd_debug(SMB, "offset %d too large\n", *off); - ret = -EINVAL; - } else if ((u64)*off + *len > MAX_STREAM_PROT_LEN) { - ksmbd_debug(SMB, "Request is larger than maximum stream protocol length(%u): %llu\n", - MAX_STREAM_PROT_LEN, (u64)*off + *len); - ret = -EINVAL; - } - - return ret; -} - -/* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - */ -static int smb2_calc_size(void *buf, unsigned int *len) -{ - struct smb2_pdu *pdu = (struct smb2_pdu *)buf; - struct smb2_hdr *hdr = &pdu->hdr; - unsigned int offset; /* the offset from the beginning of SMB to data area */ - unsigned int data_length; /* the length of the variable length data area */ - int ret; - - /* Structure Size has already been checked to make sure it is 64 */ - *len = le16_to_cpu(hdr->StructureSize); - - /* - * StructureSize2, ie length of fixed parameter area has already - * been checked to make sure it is the correct length. - */ - *len += le16_to_cpu(pdu->StructureSize2); - /* - * StructureSize2 of smb2_lock pdu is set to 48, indicating - * the size of smb2 lock request with single smb2_lock_element - * regardless of number of locks. Subtract single - * smb2_lock_element for correct buffer size check. - */ - if (hdr->Command == SMB2_LOCK) - *len -= sizeof(struct smb2_lock_element); - - if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) - goto calc_size_exit; - - ret = smb2_get_data_area_len(&offset, &data_length, hdr); - if (ret) - return ret; - ksmbd_debug(SMB, "SMB2 data length %u offset %u\n", data_length, - offset); - - if (data_length > 0) { - /* - * Check to make sure that data area begins after fixed area, - * Note that last byte of the fixed area is part of data area - * for some commands, typically those with odd StructureSize, - * so we must add one to the calculation. - */ - if (offset + 1 < *len) { - ksmbd_debug(SMB, - "data area offset %d overlaps SMB2 header %u\n", - offset + 1, *len); - return -EINVAL; - } - - *len = offset + data_length; - } - -calc_size_exit: - ksmbd_debug(SMB, "SMB2 len %u\n", *len); - return 0; -} - -static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) -{ - return le32_to_cpu(h->InputBufferLength) + - le32_to_cpu(h->OutputBufferLength); -} - -static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) -{ - return le32_to_cpu(h->BufferLength); -} - -static inline int smb2_read_req_len(struct smb2_read_req *h) -{ - return le32_to_cpu(h->Length); -} - -static inline int smb2_write_req_len(struct smb2_write_req *h) -{ - return le32_to_cpu(h->Length); -} - -static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h) -{ - return le32_to_cpu(h->OutputBufferLength); -} - -static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h) -{ - return le32_to_cpu(h->InputCount) + - le32_to_cpu(h->OutputCount); -} - -static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) -{ - return le32_to_cpu(h->MaxInputResponse) + - le32_to_cpu(h->MaxOutputResponse); -} - -static int smb2_validate_credit_charge(struct ksmbd_conn *conn, - struct smb2_hdr *hdr) -{ - unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; - unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); - void *__hdr = hdr; - int ret = 0; - - switch (hdr->Command) { - case SMB2_QUERY_INFO: - req_len = smb2_query_info_req_len(__hdr); - break; - case SMB2_SET_INFO: - req_len = smb2_set_info_req_len(__hdr); - break; - case SMB2_READ: - req_len = smb2_read_req_len(__hdr); - break; - case SMB2_WRITE: - req_len = smb2_write_req_len(__hdr); - break; - case SMB2_QUERY_DIRECTORY: - req_len = smb2_query_dir_req_len(__hdr); - break; - case SMB2_IOCTL: - req_len = smb2_ioctl_req_len(__hdr); - expect_resp_len = smb2_ioctl_resp_len(__hdr); - break; - case SMB2_CANCEL: - return 0; - default: - req_len = 1; - break; - } - - credit_charge = max_t(unsigned short, credit_charge, 1); - max_len = max_t(unsigned int, req_len, expect_resp_len); - calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); - - if (credit_charge < calc_credit_num) { - ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n", - credit_charge, calc_credit_num); - return 1; - } else if (credit_charge > conn->vals->max_credits) { - ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge); - return 1; - } - - spin_lock(&conn->credits_lock); - if (credit_charge > conn->total_credits) { - ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", - credit_charge, conn->total_credits); - ret = 1; - } - - if ((u64)conn->outstanding_credits + credit_charge > conn->total_credits) { - ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n", - credit_charge, conn->outstanding_credits); - ret = 1; - } else - conn->outstanding_credits += credit_charge; - - spin_unlock(&conn->credits_lock); - - return ret; -} - -int ksmbd_smb2_check_message(struct ksmbd_work *work) -{ - struct smb2_pdu *pdu = ksmbd_req_buf_next(work); - struct smb2_hdr *hdr = &pdu->hdr; - int command; - __u32 clc_len; /* calculated length */ - __u32 len = get_rfc1002_len(work->request_buf); - __u32 req_struct_size, next_cmd = le32_to_cpu(hdr->NextCommand); - - if ((u64)work->next_smb2_rcv_hdr_off + next_cmd > len) { - pr_err("next command(%u) offset exceeds smb msg size\n", - next_cmd); - return 1; - } - - if (next_cmd > 0) - len = next_cmd; - else if (work->next_smb2_rcv_hdr_off) - len -= work->next_smb2_rcv_hdr_off; - - if (check_smb2_hdr(hdr)) - return 1; - - if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { - ksmbd_debug(SMB, "Illegal structure size %u\n", - le16_to_cpu(hdr->StructureSize)); - return 1; - } - - command = le16_to_cpu(hdr->Command); - if (command >= NUMBER_OF_SMB2_COMMANDS) { - ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command); - return 1; - } - - if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { - if (command == SMB2_OPLOCK_BREAK_HE && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { - /* special case for SMB2.1 lease break message */ - ksmbd_debug(SMB, - "Illegal request size %d for oplock break\n", - le16_to_cpu(pdu->StructureSize2)); - return 1; - } - } - - req_struct_size = le16_to_cpu(pdu->StructureSize2) + - __SMB2_HEADER_STRUCTURE_SIZE; - if (command == SMB2_LOCK_HE) - req_struct_size -= sizeof(struct smb2_lock_element); - - if (req_struct_size > len + 1) - return 1; - - if (smb2_calc_size(hdr, &clc_len)) - return 1; - - if (len != clc_len) { - /* client can return one byte more due to implied bcc[0] */ - if (clc_len == len + 1) - goto validate_credit; - - /* - * Some windows servers (win2016) will pad also the final - * PDU in a compound to 8 bytes. - */ - if (ALIGN(clc_len, 8) == len) - goto validate_credit; - - /* - * SMB2 NEGOTIATE request will be validated when message - * handling proceeds. - */ - if (command == SMB2_NEGOTIATE_HE) - goto validate_credit; - - /* - * Allow a message that padded to 8byte boundary. - * Linux 4.19.217 with smb 3.0.2 are sometimes - * sending messages where the cls_len is exactly - * 8 bytes less than len. - */ - if (clc_len < len && (len - clc_len) <= 8) - goto validate_credit; - - pr_err_ratelimited( - "cli req too short, len %d not %d. cmd:%d mid:%llu\n", - len, clc_len, command, - le64_to_cpu(hdr->MessageId)); - - return 1; - } - -validate_credit: - if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && - smb2_validate_credit_charge(work->conn, hdr)) { - work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return 1; - } - - return 0; -} - -int smb2_negotiate_request(struct ksmbd_work *work) -{ - return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE); -} diff --git a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c deleted file mode 100644 index ab23da2120b9..000000000000 --- a/fs/ksmbd/smb2ops.c +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include "glob.h" - -#include "auth.h" -#include "connection.h" -#include "smb_common.h" -#include "server.h" - -static struct smb_version_values smb21_server_values = { - .version_string = SMB21_VERSION_STRING, - .protocol_id = SMB21_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB21_DEFAULT_IOSIZE, - .max_write_size = SMB21_DEFAULT_IOSIZE, - .max_trans_size = SMB21_DEFAULT_IOSIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_values smb30_server_values = { - .version_string = SMB30_VERSION_STRING, - .protocol_id = SMB30_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB3_DEFAULT_IOSIZE, - .max_write_size = SMB3_DEFAULT_IOSIZE, - .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease_v2), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_values smb302_server_values = { - .version_string = SMB302_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB3_DEFAULT_IOSIZE, - .max_write_size = SMB3_DEFAULT_IOSIZE, - .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease_v2), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_values smb311_server_values = { - .version_string = SMB311_VERSION_STRING, - .protocol_id = SMB311_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB3_DEFAULT_IOSIZE, - .max_write_size = SMB3_DEFAULT_IOSIZE, - .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease_v2), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_ops smb2_0_server_ops = { - .get_cmd_val = get_smb2_cmd_val, - .init_rsp_hdr = init_smb2_rsp_hdr, - .set_rsp_status = set_smb2_rsp_status, - .allocate_rsp_buf = smb2_allocate_rsp_buf, - .set_rsp_credits = smb2_set_rsp_credits, - .check_user_session = smb2_check_user_session, - .get_ksmbd_tcon = smb2_get_ksmbd_tcon, - .is_sign_req = smb2_is_sign_req, - .check_sign_req = smb2_check_sign_req, - .set_sign_rsp = smb2_set_sign_rsp -}; - -static struct smb_version_ops smb3_0_server_ops = { - .get_cmd_val = get_smb2_cmd_val, - .init_rsp_hdr = init_smb2_rsp_hdr, - .set_rsp_status = set_smb2_rsp_status, - .allocate_rsp_buf = smb2_allocate_rsp_buf, - .set_rsp_credits = smb2_set_rsp_credits, - .check_user_session = smb2_check_user_session, - .get_ksmbd_tcon = smb2_get_ksmbd_tcon, - .is_sign_req = smb2_is_sign_req, - .check_sign_req = smb3_check_sign_req, - .set_sign_rsp = smb3_set_sign_rsp, - .generate_signingkey = ksmbd_gen_smb30_signingkey, - .generate_encryptionkey = ksmbd_gen_smb30_encryptionkey, - .is_transform_hdr = smb3_is_transform_hdr, - .decrypt_req = smb3_decrypt_req, - .encrypt_resp = smb3_encrypt_resp -}; - -static struct smb_version_ops smb3_11_server_ops = { - .get_cmd_val = get_smb2_cmd_val, - .init_rsp_hdr = init_smb2_rsp_hdr, - .set_rsp_status = set_smb2_rsp_status, - .allocate_rsp_buf = smb2_allocate_rsp_buf, - .set_rsp_credits = smb2_set_rsp_credits, - .check_user_session = smb2_check_user_session, - .get_ksmbd_tcon = smb2_get_ksmbd_tcon, - .is_sign_req = smb2_is_sign_req, - .check_sign_req = smb3_check_sign_req, - .set_sign_rsp = smb3_set_sign_rsp, - .generate_signingkey = ksmbd_gen_smb311_signingkey, - .generate_encryptionkey = ksmbd_gen_smb311_encryptionkey, - .is_transform_hdr = smb3_is_transform_hdr, - .decrypt_req = smb3_decrypt_req, - .encrypt_resp = smb3_encrypt_resp -}; - -static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { - [SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, }, - [SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, }, - [SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,}, - [SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,}, - [SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,}, - [SMB2_CREATE_HE] = { .proc = smb2_open}, - [SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info}, - [SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir}, - [SMB2_CLOSE_HE] = { .proc = smb2_close}, - [SMB2_ECHO_HE] = { .proc = smb2_echo}, - [SMB2_SET_INFO_HE] = { .proc = smb2_set_info}, - [SMB2_READ_HE] = { .proc = smb2_read}, - [SMB2_WRITE_HE] = { .proc = smb2_write}, - [SMB2_FLUSH_HE] = { .proc = smb2_flush}, - [SMB2_CANCEL_HE] = { .proc = smb2_cancel}, - [SMB2_LOCK_HE] = { .proc = smb2_lock}, - [SMB2_IOCTL_HE] = { .proc = smb2_ioctl}, - [SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break}, - [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, -}; - -/** - * init_smb2_1_server() - initialize a smb server connection with smb2.1 - * command dispatcher - * @conn: connection instance - */ -void init_smb2_1_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb21_server_values; - conn->ops = &smb2_0_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; -} - -/** - * init_smb3_0_server() - initialize a smb server connection with smb3.0 - * command dispatcher - * @conn: connection instance - */ -void init_smb3_0_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb30_server_values; - conn->ops = &smb3_0_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && - conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; -} - -/** - * init_smb3_02_server() - initialize a smb server connection with smb3.02 - * command dispatcher - * @conn: connection instance - */ -void init_smb3_02_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb302_server_values; - conn->ops = &smb3_0_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && - conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; -} - -/** - * init_smb3_11_server() - initialize a smb server connection with smb3.11 - * command dispatcher - * @conn: connection instance - */ -int init_smb3_11_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb311_server_values; - conn->ops = &smb3_11_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; - - INIT_LIST_HEAD(&conn->preauth_sess_table); - return 0; -} - -void init_smb2_max_read_size(unsigned int sz) -{ - sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); - smb21_server_values.max_read_size = sz; - smb30_server_values.max_read_size = sz; - smb302_server_values.max_read_size = sz; - smb311_server_values.max_read_size = sz; -} - -void init_smb2_max_write_size(unsigned int sz) -{ - sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); - smb21_server_values.max_write_size = sz; - smb30_server_values.max_write_size = sz; - smb302_server_values.max_write_size = sz; - smb311_server_values.max_write_size = sz; -} - -void init_smb2_max_trans_size(unsigned int sz) -{ - sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); - smb21_server_values.max_trans_size = sz; - smb30_server_values.max_trans_size = sz; - smb302_server_values.max_trans_size = sz; - smb311_server_values.max_trans_size = sz; -} - -void init_smb2_max_credits(unsigned int sz) -{ - smb21_server_values.max_credits = sz; - smb30_server_values.max_credits = sz; - smb302_server_values.max_credits = sz; - smb311_server_values.max_credits = sz; -} diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c deleted file mode 100644 index 2b7c0ba6a77d..000000000000 --- a/fs/ksmbd/smb2pdu.c +++ /dev/null @@ -1,8727 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glob.h" -#include "smbfsctl.h" -#include "oplock.h" -#include "smbacl.h" - -#include "auth.h" -#include "asn1.h" -#include "connection.h" -#include "transport_ipc.h" -#include "transport_rdma.h" -#include "vfs.h" -#include "vfs_cache.h" -#include "misc.h" - -#include "server.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "ksmbd_work.h" -#include "mgmt/user_config.h" -#include "mgmt/share_config.h" -#include "mgmt/tree_connect.h" -#include "mgmt/user_session.h" -#include "mgmt/ksmbd_ida.h" -#include "ndr.h" - -static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) -{ - if (work->next_smb2_rcv_hdr_off) { - *req = ksmbd_req_buf_next(work); - *rsp = ksmbd_resp_buf_next(work); - } else { - *req = smb2_get_msg(work->request_buf); - *rsp = smb2_get_msg(work->response_buf); - } -} - -#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) - -/** - * check_session_id() - check for valid session id in smb header - * @conn: connection instance - * @id: session id from smb header - * - * Return: 1 if valid session id, otherwise 0 - */ -static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) -{ - struct ksmbd_session *sess; - - if (id == 0 || id == -1) - return false; - - sess = ksmbd_session_lookup_all(conn, id); - if (sess) - return true; - pr_err("Invalid user session id: %llu\n", id); - return false; -} - -struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) -{ - return xa_load(&sess->ksmbd_chann_list, (long)conn); -} - -/** - * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. - * @work: smb work - * - * Return: 0 if there is a tree connection matched or these are - * skipable commands, otherwise error - */ -int smb2_get_ksmbd_tcon(struct ksmbd_work *work) -{ - struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); - unsigned int cmd = le16_to_cpu(req_hdr->Command); - int tree_id; - - if (cmd == SMB2_TREE_CONNECT_HE || - cmd == SMB2_CANCEL_HE || - cmd == SMB2_LOGOFF_HE) { - ksmbd_debug(SMB, "skip to check tree connect request\n"); - return 0; - } - - if (xa_empty(&work->sess->tree_conns)) { - ksmbd_debug(SMB, "NO tree connected\n"); - return -ENOENT; - } - - tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); - - /* - * If request is not the first in Compound request, - * Just validate tree id in header with work->tcon->id. - */ - if (work->next_smb2_rcv_hdr_off) { - if (!work->tcon) { - pr_err("The first operation in the compound does not have tcon\n"); - return -EINVAL; - } - if (work->tcon->id != tree_id) { - pr_err("tree id(%u) is different with id(%u) in first operation\n", - tree_id, work->tcon->id); - return -EINVAL; - } - return 1; - } - - work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); - if (!work->tcon) { - pr_err("Invalid tid %d\n", tree_id); - return -ENOENT; - } - - return 1; -} - -/** - * smb2_set_err_rsp() - set error response code on smb response - * @work: smb work containing response buffer - */ -void smb2_set_err_rsp(struct ksmbd_work *work) -{ - struct smb2_err_rsp *err_rsp; - - if (work->next_smb2_rcv_hdr_off) - err_rsp = ksmbd_resp_buf_next(work); - else - err_rsp = smb2_get_msg(work->response_buf); - - if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { - err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; - err_rsp->ErrorContextCount = 0; - err_rsp->Reserved = 0; - err_rsp->ByteCount = 0; - err_rsp->ErrorData[0] = 0; - inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); - } -} - -/** - * is_smb2_neg_cmd() - is it smb2 negotiation command - * @work: smb work containing smb header - * - * Return: true if smb2 negotiation command, otherwise false - */ -bool is_smb2_neg_cmd(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - - /* is it SMB2 header ? */ - if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return false; - - /* make sure it is request not response message */ - if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - return false; - - if (hdr->Command != SMB2_NEGOTIATE) - return false; - - return true; -} - -/** - * is_smb2_rsp() - is it smb2 response - * @work: smb work containing smb response buffer - * - * Return: true if smb2 response, otherwise false - */ -bool is_smb2_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); - - /* is it SMB2 header ? */ - if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return false; - - /* make sure it is response not request message */ - if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) - return false; - - return true; -} - -/** - * get_smb2_cmd_val() - get smb command code from smb header - * @work: smb work containing smb request buffer - * - * Return: smb2 request command value - */ -u16 get_smb2_cmd_val(struct ksmbd_work *work) -{ - struct smb2_hdr *rcv_hdr; - - if (work->next_smb2_rcv_hdr_off) - rcv_hdr = ksmbd_req_buf_next(work); - else - rcv_hdr = smb2_get_msg(work->request_buf); - return le16_to_cpu(rcv_hdr->Command); -} - -/** - * set_smb2_rsp_status() - set error response code on smb2 header - * @work: smb work containing response buffer - * @err: error response code - */ -void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) -{ - struct smb2_hdr *rsp_hdr; - - if (work->next_smb2_rcv_hdr_off) - rsp_hdr = ksmbd_resp_buf_next(work); - else - rsp_hdr = smb2_get_msg(work->response_buf); - rsp_hdr->Status = err; - smb2_set_err_rsp(work); -} - -/** - * init_smb2_neg_rsp() - initialize smb2 response for negotiate command - * @work: smb work containing smb request buffer - * - * smb2 negotiate response is sent in reply of smb1 negotiate command for - * dialect auto-negotiation. - */ -int init_smb2_neg_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *rsp_hdr; - struct smb2_negotiate_rsp *rsp; - struct ksmbd_conn *conn = work->conn; - - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - - rsp_hdr = smb2_get_msg(work->response_buf); - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->CreditRequest = cpu_to_le16(2); - rsp_hdr->Command = SMB2_NEGOTIATE; - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = 0; - rsp_hdr->Id.SyncId.ProcessId = 0; - rsp_hdr->Id.SyncId.TreeId = 0; - rsp_hdr->SessionId = 0; - memset(rsp_hdr->Signature, 0, 16); - - rsp = smb2_get_msg(work->response_buf); - - WARN_ON(ksmbd_conn_good(conn)); - - rsp->StructureSize = cpu_to_le16(65); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - rsp->DialectRevision = cpu_to_le16(conn->dialect); - /* Not setting conn guid rsp->ServerGUID, as it - * not used by client for identifying connection - */ - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); - /* Default Max Message Size till SMB2.0, 64K*/ - rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); - rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); - rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); - - rsp->SystemTime = cpu_to_le64(ksmbd_systime()); - rsp->ServerStartTime = 0; - - rsp->SecurityBufferOffset = cpu_to_le16(128); - rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + - le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(work->response_buf, - sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + - AUTH_GSS_LENGTH); - rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; - if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) - rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; - conn->use_spnego = true; - - ksmbd_conn_set_need_negotiate(conn); - return 0; -} - -/** - * smb2_set_rsp_credits() - set number of credits in response buffer - * @work: smb work containing smb response buffer - */ -int smb2_set_rsp_credits(struct ksmbd_work *work) -{ - struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); - struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); - struct ksmbd_conn *conn = work->conn; - unsigned short credits_requested, aux_max; - unsigned short credit_charge, credits_granted = 0; - - if (work->send_no_response) - return 0; - - hdr->CreditCharge = req_hdr->CreditCharge; - - if (conn->total_credits > conn->vals->max_credits) { - hdr->CreditRequest = 0; - pr_err("Total credits overflow: %d\n", conn->total_credits); - return -EINVAL; - } - - credit_charge = max_t(unsigned short, - le16_to_cpu(req_hdr->CreditCharge), 1); - if (credit_charge > conn->total_credits) { - ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", - credit_charge, conn->total_credits); - return -EINVAL; - } - - conn->total_credits -= credit_charge; - conn->outstanding_credits -= credit_charge; - credits_requested = max_t(unsigned short, - le16_to_cpu(req_hdr->CreditRequest), 1); - - /* according to smb2.credits smbtorture, Windows server - * 2016 or later grant up to 8192 credits at once. - * - * TODO: Need to adjuct CreditRequest value according to - * current cpu load - */ - if (hdr->Command == SMB2_NEGOTIATE) - aux_max = 1; - else - aux_max = conn->vals->max_credits - conn->total_credits; - credits_granted = min_t(unsigned short, credits_requested, aux_max); - - conn->total_credits += credits_granted; - work->credits_granted += credits_granted; - - if (!req_hdr->NextCommand) { - /* Update CreditRequest in last request */ - hdr->CreditRequest = cpu_to_le16(work->credits_granted); - } - ksmbd_debug(SMB, - "credits: requested[%d] granted[%d] total_granted[%d]\n", - credits_requested, credits_granted, - conn->total_credits); - return 0; -} - -/** - * init_chained_smb2_rsp() - initialize smb2 chained response - * @work: smb work containing smb response buffer - */ -static void init_chained_smb2_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *req = ksmbd_req_buf_next(work); - struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); - struct smb2_hdr *rsp_hdr; - struct smb2_hdr *rcv_hdr; - int next_hdr_offset = 0; - int len, new_len; - - /* Len of this response = updated RFC len - offset of previous cmd - * in the compound rsp - */ - - /* Storing the current local FID which may be needed by subsequent - * command in the compound request - */ - if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { - work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; - work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; - work->compound_sid = le64_to_cpu(rsp->SessionId); - } - - len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; - next_hdr_offset = le32_to_cpu(req->NextCommand); - - new_len = ALIGN(len, 8); - inc_rfc1001_len(work->response_buf, - sizeof(struct smb2_hdr) + new_len - len); - rsp->NextCommand = cpu_to_le32(new_len); - - work->next_smb2_rcv_hdr_off += next_hdr_offset; - work->next_smb2_rsp_hdr_off += new_len; - ksmbd_debug(SMB, - "Compound req new_len = %d rcv off = %d rsp off = %d\n", - new_len, work->next_smb2_rcv_hdr_off, - work->next_smb2_rsp_hdr_off); - - rsp_hdr = ksmbd_resp_buf_next(work); - rcv_hdr = ksmbd_req_buf_next(work); - - if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { - ksmbd_debug(SMB, "related flag should be set\n"); - work->compound_fid = KSMBD_NO_FID; - work->compound_pfid = KSMBD_NO_FID; - } - memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->Command = rcv_hdr->Command; - - /* - * Message is response. We don't grant oplock yet. - */ - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | - SMB2_FLAGS_RELATED_OPERATIONS); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = rcv_hdr->MessageId; - rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; - rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; - rsp_hdr->SessionId = rcv_hdr->SessionId; - memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); -} - -/** - * is_chained_smb2_message() - check for chained command - * @work: smb work containing smb request buffer - * - * Return: true if chained request, otherwise false - */ -bool is_chained_smb2_message(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - unsigned int len, next_cmd; - - if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return false; - - hdr = ksmbd_req_buf_next(work); - next_cmd = le32_to_cpu(hdr->NextCommand); - if (next_cmd > 0) { - if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + - __SMB2_HEADER_STRUCTURE_SIZE > - get_rfc1002_len(work->request_buf)) { - pr_err("next command(%u) offset exceeds smb msg size\n", - next_cmd); - return false; - } - - if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > - work->response_sz) { - pr_err("next response offset exceeds response buffer size\n"); - return false; - } - - ksmbd_debug(SMB, "got SMB2 chained command\n"); - init_chained_smb2_rsp(work); - return true; - } else if (work->next_smb2_rcv_hdr_off) { - /* - * This is last request in chained command, - * align response to 8 byte - */ - len = ALIGN(get_rfc1002_len(work->response_buf), 8); - len = len - get_rfc1002_len(work->response_buf); - if (len) { - ksmbd_debug(SMB, "padding len %u\n", len); - inc_rfc1001_len(work->response_buf, len); - if (work->aux_payload_sz) - work->aux_payload_sz += len; - } - } - return false; -} - -/** - * init_smb2_rsp_hdr() - initialize smb2 response - * @work: smb work containing smb request buffer - * - * Return: 0 - */ -int init_smb2_rsp_hdr(struct ksmbd_work *work) -{ - struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); - struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); - struct ksmbd_conn *conn = work->conn; - - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->Command = rcv_hdr->Command; - - /* - * Message is response. We don't grant oplock yet. - */ - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = rcv_hdr->MessageId; - rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; - rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; - rsp_hdr->SessionId = rcv_hdr->SessionId; - memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); - - work->syncronous = true; - if (work->async_id) { - ksmbd_release_id(&conn->async_ida, work->async_id); - work->async_id = 0; - } - - return 0; -} - -/** - * smb2_allocate_rsp_buf() - allocate smb2 response buffer - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise -ENOMEM - */ -int smb2_allocate_rsp_buf(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - size_t large_sz = small_sz + work->conn->vals->max_trans_size; - size_t sz = small_sz; - int cmd = le16_to_cpu(hdr->Command); - - if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) - sz = large_sz; - - if (cmd == SMB2_QUERY_INFO_HE) { - struct smb2_query_info_req *req; - - req = smb2_get_msg(work->request_buf); - if ((req->InfoType == SMB2_O_INFO_FILE && - (req->FileInfoClass == FILE_FULL_EA_INFORMATION || - req->FileInfoClass == FILE_ALL_INFORMATION)) || - req->InfoType == SMB2_O_INFO_SECURITY) - sz = large_sz; - } - - /* allocate large response buf for chained commands */ - if (le32_to_cpu(hdr->NextCommand) > 0) - sz = large_sz; - - work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); - if (!work->response_buf) - return -ENOMEM; - - work->response_sz = sz; - return 0; -} - -/** - * smb2_check_user_session() - check for valid session for a user - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_check_user_session(struct ksmbd_work *work) -{ - struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); - struct ksmbd_conn *conn = work->conn; - unsigned int cmd = conn->ops->get_cmd_val(work); - unsigned long long sess_id; - - /* - * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not - * require a session id, so no need to validate user session's for - * these commands. - */ - if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || - cmd == SMB2_SESSION_SETUP_HE) - return 0; - - if (!ksmbd_conn_good(conn)) - return -EIO; - - sess_id = le64_to_cpu(req_hdr->SessionId); - - /* - * If request is not the first in Compound request, - * Just validate session id in header with work->sess->id. - */ - if (work->next_smb2_rcv_hdr_off) { - if (!work->sess) { - pr_err("The first operation in the compound does not have sess\n"); - return -EINVAL; - } - if (work->sess->id != sess_id) { - pr_err("session id(%llu) is different with the first operation(%lld)\n", - sess_id, work->sess->id); - return -EINVAL; - } - return 1; - } - - /* Check for validity of user session */ - work->sess = ksmbd_session_lookup_all(conn, sess_id); - if (work->sess) - return 1; - ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); - return -ENOENT; -} - -static void destroy_previous_session(struct ksmbd_conn *conn, - struct ksmbd_user *user, u64 id) -{ - struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); - struct ksmbd_user *prev_user; - struct channel *chann; - long index; - - if (!prev_sess) - return; - - prev_user = prev_sess->user; - - if (!prev_user || - strcmp(user->name, prev_user->name) || - user->passkey_sz != prev_user->passkey_sz || - memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) - return; - - prev_sess->state = SMB2_SESSION_EXPIRED; - xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) - ksmbd_conn_set_exiting(chann->conn); -} - -/** - * smb2_get_name() - get filename string from on the wire smb format - * @src: source buffer - * @maxlen: maxlen of source string - * @local_nls: nls_table pointer - * - * Return: matching converted filename on success, otherwise error ptr - */ -static char * -smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) -{ - char *name; - - name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); - if (IS_ERR(name)) { - pr_err("failed to get name %ld\n", PTR_ERR(name)); - return name; - } - - ksmbd_conv_path_to_unix(name); - ksmbd_strip_last_slash(name); - return name; -} - -int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) -{ - struct smb2_hdr *rsp_hdr; - struct ksmbd_conn *conn = work->conn; - int id; - - rsp_hdr = smb2_get_msg(work->response_buf); - rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; - - id = ksmbd_acquire_async_msg_id(&conn->async_ida); - if (id < 0) { - pr_err("Failed to alloc async message id\n"); - return id; - } - work->syncronous = false; - work->async_id = id; - rsp_hdr->Id.AsyncId = cpu_to_le64(id); - - ksmbd_debug(SMB, - "Send interim Response to inform async request id : %d\n", - work->async_id); - - work->cancel_fn = fn; - work->cancel_argv = arg; - - if (list_empty(&work->async_request_entry)) { - spin_lock(&conn->request_lock); - list_add_tail(&work->async_request_entry, &conn->async_requests); - spin_unlock(&conn->request_lock); - } - - return 0; -} - -void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) -{ - struct smb2_hdr *rsp_hdr; - - rsp_hdr = smb2_get_msg(work->response_buf); - smb2_set_err_rsp(work); - rsp_hdr->Status = status; - - work->multiRsp = 1; - ksmbd_conn_write(work); - rsp_hdr->Status = 0; - work->multiRsp = 0; -} - -static __le32 smb2_get_reparse_tag_special_file(umode_t mode) -{ - if (S_ISDIR(mode) || S_ISREG(mode)) - return 0; - - if (S_ISLNK(mode)) - return IO_REPARSE_TAG_LX_SYMLINK_LE; - else if (S_ISFIFO(mode)) - return IO_REPARSE_TAG_LX_FIFO_LE; - else if (S_ISSOCK(mode)) - return IO_REPARSE_TAG_AF_UNIX_LE; - else if (S_ISCHR(mode)) - return IO_REPARSE_TAG_LX_CHR_LE; - else if (S_ISBLK(mode)) - return IO_REPARSE_TAG_LX_BLK_LE; - - return 0; -} - -/** - * smb2_get_dos_mode() - get file mode in dos format from unix mode - * @stat: kstat containing file mode - * @attribute: attribute flags - * - * Return: converted dos mode - */ -static int smb2_get_dos_mode(struct kstat *stat, int attribute) -{ - int attr = 0; - - if (S_ISDIR(stat->mode)) { - attr = FILE_ATTRIBUTE_DIRECTORY | - (attribute & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); - } else { - attr = (attribute & 0x00005137) | FILE_ATTRIBUTE_ARCHIVE; - attr &= ~(FILE_ATTRIBUTE_DIRECTORY); - if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & - FILE_SUPPORTS_SPARSE_FILES)) - attr |= FILE_ATTRIBUTE_SPARSE_FILE; - - if (smb2_get_reparse_tag_special_file(stat->mode)) - attr |= FILE_ATTRIBUTE_REPARSE_POINT; - } - - return attr; -} - -static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, - __le16 hash_id) -{ - pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; - pneg_ctxt->DataLength = cpu_to_le16(38); - pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); - get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); - pneg_ctxt->HashAlgorithms = hash_id; -} - -static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, - __le16 cipher_type) -{ - pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; - pneg_ctxt->DataLength = cpu_to_le16(4); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->CipherCount = cpu_to_le16(1); - pneg_ctxt->Ciphers[0] = cipher_type; -} - -static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt, - __le16 comp_algo) -{ - pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; - pneg_ctxt->DataLength = - cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - - sizeof(struct smb2_neg_context)); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->Flags = cpu_to_le32(0); - pneg_ctxt->CompressionAlgorithms[0] = comp_algo; -} - -static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, - __le16 sign_algo) -{ - pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; - pneg_ctxt->DataLength = - cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) - - sizeof(struct smb2_neg_context)); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->SigningAlgorithms[0] = sign_algo; -} - -static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; - pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - pneg_ctxt->Name[0] = 0x93; - pneg_ctxt->Name[1] = 0xAD; - pneg_ctxt->Name[2] = 0x25; - pneg_ctxt->Name[3] = 0x50; - pneg_ctxt->Name[4] = 0x9C; - pneg_ctxt->Name[5] = 0xB4; - pneg_ctxt->Name[6] = 0x11; - pneg_ctxt->Name[7] = 0xE7; - pneg_ctxt->Name[8] = 0xB4; - pneg_ctxt->Name[9] = 0x23; - pneg_ctxt->Name[10] = 0x83; - pneg_ctxt->Name[11] = 0xDE; - pneg_ctxt->Name[12] = 0x96; - pneg_ctxt->Name[13] = 0x8B; - pneg_ctxt->Name[14] = 0xCD; - pneg_ctxt->Name[15] = 0x7C; -} - -static void assemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_rsp *rsp, - void *smb2_buf_len) -{ - char *pneg_ctxt = (char *)rsp + - le32_to_cpu(rsp->NegotiateContextOffset); - int neg_ctxt_cnt = 1; - int ctxt_size; - - ksmbd_debug(SMB, - "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); - build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, - conn->preauth_info->Preauth_HashId); - rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); - inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); - ctxt_size = sizeof(struct smb2_preauth_neg_context); - /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); - - if (conn->cipher_type) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); - build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, - conn->cipher_type); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; - /* Round to 8 byte boundary */ - pneg_ctxt += - round_up(sizeof(struct smb2_encryption_neg_context) + 2, - 8); - } - - if (conn->compress_algorithm) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); - /* Temporarily set to SMB3_COMPRESS_NONE */ - build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, - conn->compress_algorithm); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; - /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, - 8); - } - - if (conn->posix_ext_supported) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); - build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_posix_neg_context); - /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); - } - - if (conn->signing_negotiated) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_SIGNING_CAPABILITIES context\n"); - build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, - conn->signing_algorithm); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; - } - - inc_rfc1001_len(smb2_buf_len, ctxt_size); -} - -static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, - struct smb2_preauth_neg_context *pneg_ctxt, - int ctxt_len) -{ - /* - * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, - * which may not be present. Only check for used HashAlgorithms[1]. - */ - if (ctxt_len < - sizeof(struct smb2_neg_context) + MIN_PREAUTH_CTXT_DATA_LEN) - return STATUS_INVALID_PARAMETER; - - if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) - return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; - - conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; - return STATUS_SUCCESS; -} - -static void decode_encrypt_ctxt(struct ksmbd_conn *conn, - struct smb2_encryption_neg_context *pneg_ctxt, - int ctxt_len) -{ - int cph_cnt; - int i, cphs_size; - - if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { - pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); - return; - } - - conn->cipher_type = 0; - - cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); - cphs_size = cph_cnt * sizeof(__le16); - - if (sizeof(struct smb2_encryption_neg_context) + cphs_size > - ctxt_len) { - pr_err("Invalid cipher count(%d)\n", cph_cnt); - return; - } - - if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) - return; - - for (i = 0; i < cph_cnt; i++) { - if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { - ksmbd_debug(SMB, "Cipher ID = 0x%x\n", - pneg_ctxt->Ciphers[i]); - conn->cipher_type = pneg_ctxt->Ciphers[i]; - break; - } - } -} - -/** - * smb3_encryption_negotiated() - checks if server and client agreed on enabling encryption - * @conn: smb connection - * - * Return: true if connection should be encrypted, else false - */ -bool smb3_encryption_negotiated(struct ksmbd_conn *conn) -{ - if (!conn->ops->generate_encryptionkey) - return false; - - /* - * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. - * SMB 3.1.1 uses the cipher_type field. - */ - return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || - conn->cipher_type; -} - -static void decode_compress_ctxt(struct ksmbd_conn *conn, - struct smb2_compression_capabilities_context *pneg_ctxt) -{ - conn->compress_algorithm = SMB3_COMPRESS_NONE; -} - -static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, - struct smb2_signing_capabilities *pneg_ctxt, - int ctxt_len) -{ - int sign_algo_cnt; - int i, sign_alos_size; - - if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { - pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); - return; - } - - conn->signing_negotiated = false; - sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); - sign_alos_size = sign_algo_cnt * sizeof(__le16); - - if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > - ctxt_len) { - pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); - return; - } - - for (i = 0; i < sign_algo_cnt; i++) { - if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256_LE || - pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC_LE) { - ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", - pneg_ctxt->SigningAlgorithms[i]); - conn->signing_negotiated = true; - conn->signing_algorithm = - pneg_ctxt->SigningAlgorithms[i]; - break; - } - } -} - -static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_req *req, - unsigned int len_of_smb) -{ - /* +4 is to account for the RFC1001 len field */ - struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; - int i = 0, len_of_ctxts; - unsigned int offset = le32_to_cpu(req->NegotiateContextOffset); - unsigned int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); - __le32 status = STATUS_INVALID_PARAMETER; - - ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); - if (len_of_smb <= offset) { - ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); - return status; - } - - len_of_ctxts = len_of_smb - offset; - - while (i++ < neg_ctxt_cnt) { - int clen, ctxt_len; - - if (len_of_ctxts < (int)sizeof(struct smb2_neg_context)) - break; - - pctx = (struct smb2_neg_context *)((char *)pctx + offset); - clen = le16_to_cpu(pctx->DataLength); - ctxt_len = clen + sizeof(struct smb2_neg_context); - - if (ctxt_len > len_of_ctxts) - break; - - if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); - if (conn->preauth_info->Preauth_HashId) - break; - - status = decode_preauth_ctxt(conn, - (struct smb2_preauth_neg_context *)pctx, - ctxt_len); - if (status != STATUS_SUCCESS) - break; - } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); - if (conn->cipher_type) - break; - - decode_encrypt_ctxt(conn, - (struct smb2_encryption_neg_context *)pctx, - ctxt_len); - } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); - if (conn->compress_algorithm) - break; - - decode_compress_ctxt(conn, - (struct smb2_compression_capabilities_context *)pctx); - } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { - ksmbd_debug(SMB, - "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); - } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { - ksmbd_debug(SMB, - "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); - conn->posix_ext_supported = true; - } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_SIGNING_CAPABILITIES context\n"); - - decode_sign_cap_ctxt(conn, - (struct smb2_signing_capabilities *)pctx, - ctxt_len); - } - - /* offsets must be 8 byte aligned */ - offset = (ctxt_len + 7) & ~0x7; - len_of_ctxts -= offset; - } - return status; -} - -/** - * smb2_handle_negotiate() - handler for smb2 negotiate command - * @work: smb work containing smb request buffer - * - * Return: 0 - */ -int smb2_handle_negotiate(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); - struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); - int rc = 0; - unsigned int smb2_buf_len, smb2_neg_size; - __le32 status; - - ksmbd_debug(SMB, "Received negotiate request\n"); - conn->need_neg = false; - if (ksmbd_conn_good(conn)) { - pr_err("conn->tcp_status is already in CifsGood State\n"); - work->send_no_response = 1; - return rc; - } - - smb2_buf_len = get_rfc1002_len(work->request_buf); - smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); - if (smb2_neg_size > smb2_buf_len) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (req->DialectCount == 0) { - pr_err("malformed packet\n"); - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (conn->dialect == SMB311_PROT_ID) { - unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); - - if (smb2_buf_len < nego_ctxt_off) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (smb2_neg_size > nego_ctxt_off) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > - nego_ctxt_off) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - } else { - if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > - smb2_buf_len) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - } - - conn->cli_cap = le32_to_cpu(req->Capabilities); - switch (conn->dialect) { - case SMB311_PROT_ID: - conn->preauth_info = - kzalloc(sizeof(struct preauth_integrity_info), - GFP_KERNEL); - if (!conn->preauth_info) { - rc = -ENOMEM; - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - goto err_out; - } - - status = deassemble_neg_contexts(conn, req, - get_rfc1002_len(work->request_buf)); - if (status != STATUS_SUCCESS) { - pr_err("deassemble_neg_contexts error(0x%x)\n", - status); - rsp->hdr.Status = status; - rc = -EINVAL; - kfree(conn->preauth_info); - conn->preauth_info = NULL; - goto err_out; - } - - rc = init_smb3_11_server(conn); - if (rc < 0) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - kfree(conn->preauth_info); - conn->preauth_info = NULL; - goto err_out; - } - - ksmbd_gen_preauth_integrity_hash(conn, - work->request_buf, - conn->preauth_info->Preauth_HashValue); - rsp->NegotiateContextOffset = - cpu_to_le32(OFFSET_OF_NEG_CONTEXT); - assemble_neg_contexts(conn, rsp, work->response_buf); - break; - case SMB302_PROT_ID: - init_smb3_02_server(conn); - break; - case SMB30_PROT_ID: - init_smb3_0_server(conn); - break; - case SMB21_PROT_ID: - init_smb2_1_server(conn); - break; - case SMB2X_PROT_ID: - case BAD_PROT_ID: - default: - ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", - conn->dialect); - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - rc = -EINVAL; - goto err_out; - } - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); - - /* For stats */ - conn->connection_type = conn->dialect; - - rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); - rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); - rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); - - memcpy(conn->ClientGUID, req->ClientGUID, - SMB2_CLIENT_GUID_SIZE); - conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); - - rsp->StructureSize = cpu_to_le16(65); - rsp->DialectRevision = cpu_to_le16(conn->dialect); - /* Not setting conn guid rsp->ServerGUID, as it - * not used by client for identifying server - */ - memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); - - rsp->SystemTime = cpu_to_le64(ksmbd_systime()); - rsp->ServerStartTime = 0; - ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", - le32_to_cpu(rsp->NegotiateContextOffset), - le16_to_cpu(rsp->NegotiateContextCount)); - - rsp->SecurityBufferOffset = cpu_to_le16(128); - rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + - le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + - AUTH_GSS_LENGTH); - rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; - conn->use_spnego = true; - - if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || - server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && - req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) - conn->sign = true; - else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { - server_conf.enforced_signing = true; - rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; - conn->sign = true; - } - - conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); - ksmbd_conn_set_need_negotiate(conn); - -err_out: - if (rc < 0) - smb2_set_err_rsp(work); - - return rc; -} - -static int alloc_preauth_hash(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - if (sess->Preauth_HashValue) - return 0; - - sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); - if (!sess->Preauth_HashValue) - return -ENOMEM; - - return 0; -} - -static int generate_preauth_hash(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - u8 *preauth_hash; - - if (conn->dialect != SMB311_PROT_ID) - return 0; - - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) { - preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); - if (!preauth_sess) - return -ENOMEM; - } - - preauth_hash = preauth_sess->Preauth_HashValue; - } else { - if (!sess->Preauth_HashValue) - if (alloc_preauth_hash(sess, conn)) - return -ENOMEM; - preauth_hash = sess->Preauth_HashValue; - } - - ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); - return 0; -} - -static int decode_negotiation_token(struct ksmbd_conn *conn, - struct negotiate_message *negblob, - size_t sz) -{ - if (!conn->use_spnego) - return -EINVAL; - - if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { - if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { - conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; - conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; - conn->use_spnego = false; - } - } - return 0; -} - -static int ntlm_negotiate(struct ksmbd_work *work, - struct negotiate_message *negblob, - size_t negblob_len) -{ - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct challenge_message *chgblob; - unsigned char *spnego_blob = NULL; - u16 spnego_blob_len; - char *neg_blob; - int sz, rc; - - ksmbd_debug(SMB, "negotiate phase\n"); - rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->conn); - if (rc) - return rc; - - sz = le16_to_cpu(rsp->SecurityBufferOffset); - chgblob = - (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); - memset(chgblob, 0, sizeof(struct challenge_message)); - - if (!work->conn->use_spnego) { - sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); - if (sz < 0) - return -ENOMEM; - - rsp->SecurityBufferLength = cpu_to_le16(sz); - return 0; - } - - sz = sizeof(struct challenge_message); - sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; - - neg_blob = kzalloc(sz, GFP_KERNEL); - if (!neg_blob) - return -ENOMEM; - - chgblob = (struct challenge_message *)neg_blob; - sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); - if (sz < 0) { - rc = -ENOMEM; - goto out; - } - - rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, - neg_blob, sz); - if (rc) { - rc = -ENOMEM; - goto out; - } - - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); - rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); - -out: - kfree(spnego_blob); - kfree(neg_blob); - return rc; -} - -static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) -{ - int sz; - - if (conn->use_spnego && conn->mechToken) - return (struct authenticate_message *)conn->mechToken; - - sz = le16_to_cpu(req->SecurityBufferOffset); - return (struct authenticate_message *)((char *)&req->hdr.ProtocolId - + sz); -} - -static struct ksmbd_user *session_user(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) -{ - struct authenticate_message *authblob; - struct ksmbd_user *user; - char *name; - unsigned int name_off, name_len, secbuf_len; - - secbuf_len = le16_to_cpu(req->SecurityBufferLength); - if (secbuf_len < sizeof(struct authenticate_message)) { - ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); - return NULL; - } - authblob = user_authblob(conn, req); - name_off = le32_to_cpu(authblob->UserName.BufferOffset); - name_len = le16_to_cpu(authblob->UserName.Length); - - if (secbuf_len < (u64)name_off + name_len) - return NULL; - - name = smb_strndup_from_utf16((const char *)authblob + name_off, - name_len, - true, - conn->local_nls); - if (IS_ERR(name)) { - pr_err("cannot allocate memory\n"); - return NULL; - } - - ksmbd_debug(SMB, "session setup request for user %s\n", name); - user = ksmbd_login_user(name); - kfree(name); - return user; -} - -static int ntlm_authenticate(struct ksmbd_work *work) -{ - struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct channel *chann = NULL; - struct ksmbd_user *user; - u64 prev_id; - int sz, rc; - - ksmbd_debug(SMB, "authenticate phase\n"); - if (conn->use_spnego) { - unsigned char *spnego_blob; - u16 spnego_blob_len; - - rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, - &spnego_blob_len, - 0); - if (rc) - return -ENOMEM; - - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); - rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); - kfree(spnego_blob); - inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); - } - - user = session_user(conn, req); - if (!user) { - ksmbd_debug(SMB, "Unknown user name or an error\n"); - return -EPERM; - } - - /* Check for previous session */ - prev_id = le64_to_cpu(req->PreviousSessionId); - if (prev_id && prev_id != sess->id) - destroy_previous_session(conn, user, prev_id); - - if (sess->state == SMB2_SESSION_VALID) { - /* - * Reuse session if anonymous try to connect - * on reauthetication. - */ - if (conn->binding == false && ksmbd_anonymous_user(user)) { - ksmbd_free_user(user); - return 0; - } - - if (!ksmbd_compare_user(sess->user, user)) { - ksmbd_free_user(user); - return -EPERM; - } - ksmbd_free_user(user); - } else { - sess->user = user; - } - - if (conn->binding == false && user_guest(sess->user)) { - rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; - } else { - struct authenticate_message *authblob; - - authblob = user_authblob(conn, req); - sz = le16_to_cpu(req->SecurityBufferLength); - rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); - if (rc) { - set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); - ksmbd_debug(SMB, "authentication failed\n"); - return -EPERM; - } - } - - /* - * If session state is SMB2_SESSION_VALID, We can assume - * that it is reauthentication. And the user/password - * has been verified, so return it here. - */ - if (sess->state == SMB2_SESSION_VALID) { - if (conn->binding) - goto binding_session; - return 0; - } - - if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && - (conn->sign || server_conf.enforced_signing)) || - (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) - sess->sign = true; - - if (smb3_encryption_negotiated(conn) && - !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - rc = conn->ops->generate_encryptionkey(conn, sess); - if (rc) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } - sess->enc = true; - rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; - /* - * signing is disable if encryption is enable - * on this session - */ - sess->sign = false; - } - -binding_session: - if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); - } - } - - if (conn->ops->generate_signingkey) { - rc = conn->ops->generate_signingkey(sess, conn); - if (rc) { - ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; - } - } - - if (!ksmbd_conn_lookup_dialect(conn)) { - pr_err("fail to verify the dialect\n"); - return -ENOENT; - } - return 0; -} - -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -static int krb5_authenticate(struct ksmbd_work *work) -{ - struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - char *in_blob, *out_blob; - struct channel *chann = NULL; - u64 prev_sess_id; - int in_len, out_len; - int retval; - - in_blob = (char *)&req->hdr.ProtocolId + - le16_to_cpu(req->SecurityBufferOffset); - in_len = le16_to_cpu(req->SecurityBufferLength); - out_blob = (char *)&rsp->hdr.ProtocolId + - le16_to_cpu(rsp->SecurityBufferOffset); - out_len = work->response_sz - - (le16_to_cpu(rsp->SecurityBufferOffset) + 4); - - /* Check previous session */ - prev_sess_id = le64_to_cpu(req->PreviousSessionId); - if (prev_sess_id && prev_sess_id != sess->id) - destroy_previous_session(conn, sess->user, prev_sess_id); - - if (sess->state == SMB2_SESSION_VALID) - ksmbd_free_user(sess->user); - - retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, - out_blob, &out_len); - if (retval) { - ksmbd_debug(SMB, "krb5 authentication failed\n"); - return -EINVAL; - } - rsp->SecurityBufferLength = cpu_to_le16(out_len); - inc_rfc1001_len(work->response_buf, out_len - 1); - - if ((conn->sign || server_conf.enforced_signing) || - (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) - sess->sign = true; - - if (smb3_encryption_negotiated(conn)) { - retval = conn->ops->generate_encryptionkey(conn, sess); - if (retval) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } - sess->enc = true; - rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; - sess->sign = false; - } - - if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); - } - } - - if (conn->ops->generate_signingkey) { - retval = conn->ops->generate_signingkey(sess, conn); - if (retval) { - ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; - } - } - - if (!ksmbd_conn_lookup_dialect(conn)) { - pr_err("fail to verify the dialect\n"); - return -ENOENT; - } - return 0; -} -#else -static int krb5_authenticate(struct ksmbd_work *work) -{ - return -EOPNOTSUPP; -} -#endif - -int smb2_sess_setup(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess; - struct negotiate_message *negblob; - unsigned int negblob_len, negblob_off; - int rc = 0; - - ksmbd_debug(SMB, "Received request for session setup\n"); - - rsp->StructureSize = cpu_to_le16(9); - rsp->SessionFlags = 0; - rsp->SecurityBufferOffset = cpu_to_le16(72); - rsp->SecurityBufferLength = 0; - inc_rfc1001_len(work->response_buf, 9); - - ksmbd_conn_lock(conn); - if (!req->hdr.SessionId) { - sess = ksmbd_smb2_session_create(); - if (!sess) { - rc = -ENOMEM; - goto out_err; - } - rsp->hdr.SessionId = cpu_to_le64(sess->id); - rc = ksmbd_session_register(conn, sess); - if (rc) - goto out_err; - } else if (conn->dialect >= SMB30_PROT_ID && - (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && - req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { - u64 sess_id = le64_to_cpu(req->hdr.SessionId); - - sess = ksmbd_session_lookup_slowpath(sess_id); - if (!sess) { - rc = -ENOENT; - goto out_err; - } - - if (conn->dialect != sess->dialect) { - rc = -EINVAL; - goto out_err; - } - - if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { - rc = -EINVAL; - goto out_err; - } - - if (strncmp(conn->ClientGUID, sess->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) { - rc = -ENOENT; - goto out_err; - } - - if (sess->state == SMB2_SESSION_IN_PROGRESS) { - rc = -EACCES; - goto out_err; - } - - if (sess->state == SMB2_SESSION_EXPIRED) { - rc = -EFAULT; - goto out_err; - } - - if (ksmbd_conn_need_reconnect(conn)) { - rc = -EFAULT; - sess = NULL; - goto out_err; - } - - if (ksmbd_session_lookup(conn, sess_id)) { - rc = -EACCES; - goto out_err; - } - - if (user_guest(sess->user)) { - rc = -EOPNOTSUPP; - goto out_err; - } - - conn->binding = true; - } else if ((conn->dialect < SMB30_PROT_ID || - server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && - (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - sess = NULL; - rc = -EACCES; - goto out_err; - } else { - sess = ksmbd_session_lookup(conn, - le64_to_cpu(req->hdr.SessionId)); - if (!sess) { - rc = -ENOENT; - goto out_err; - } - - if (sess->state == SMB2_SESSION_EXPIRED) { - rc = -EFAULT; - goto out_err; - } - - if (ksmbd_conn_need_reconnect(conn)) { - rc = -EFAULT; - sess = NULL; - goto out_err; - } - } - work->sess = sess; - - negblob_off = le16_to_cpu(req->SecurityBufferOffset); - negblob_len = le16_to_cpu(req->SecurityBufferLength); - if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || - negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { - rc = -EINVAL; - goto out_err; - } - - negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + - negblob_off); - - if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { - if (conn->mechToken) - negblob = (struct negotiate_message *)conn->mechToken; - } - - if (server_conf.auth_mechs & conn->auth_mechs) { - rc = generate_preauth_hash(work); - if (rc) - goto out_err; - - if (conn->preferred_auth_mech & - (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { - rc = krb5_authenticate(work); - if (rc) { - rc = -EINVAL; - goto out_err; - } - - if (!ksmbd_conn_need_reconnect(conn)) { - ksmbd_conn_set_good(conn); - sess->state = SMB2_SESSION_VALID; - } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; - } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { - if (negblob->MessageType == NtLmNegotiate) { - rc = ntlm_negotiate(work, negblob, negblob_len); - if (rc) - goto out_err; - rsp->hdr.Status = - STATUS_MORE_PROCESSING_REQUIRED; - /* - * Note: here total size -1 is done as an - * adjustment for 0 size blob - */ - inc_rfc1001_len(work->response_buf, - le16_to_cpu(rsp->SecurityBufferLength) - 1); - - } else if (negblob->MessageType == NtLmAuthenticate) { - rc = ntlm_authenticate(work); - if (rc) - goto out_err; - - if (!ksmbd_conn_need_reconnect(conn)) { - ksmbd_conn_set_good(conn); - sess->state = SMB2_SESSION_VALID; - } - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = - ksmbd_preauth_session_lookup(conn, sess->id); - if (preauth_sess) { - list_del(&preauth_sess->preauth_entry); - kfree(preauth_sess); - } - } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; - } else { - pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", - le32_to_cpu(negblob->MessageType)); - rc = -EINVAL; - } - } else { - /* TODO: need one more negotiation */ - pr_err("Not support the preferred authentication\n"); - rc = -EINVAL; - } - } else { - pr_err("Not support authentication\n"); - rc = -EINVAL; - } - -out_err: - if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_USER_SESSION_DELETED; - else if (rc == -EACCES) - rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; - else if (rc == -EFAULT) - rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; - else if (rc == -ENOMEM) - rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; - else if (rc == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (rc) - rsp->hdr.Status = STATUS_LOGON_FAILURE; - - if (conn->use_spnego && conn->mechToken) { - kfree(conn->mechToken); - conn->mechToken = NULL; - } - - if (rc < 0) { - /* - * SecurityBufferOffset should be set to zero - * in session setup error response. - */ - rsp->SecurityBufferOffset = 0; - - if (sess) { - bool try_delay = false; - - /* - * To avoid dictionary attacks (repeated session setups rapidly sent) to - * connect to server, ksmbd make a delay of a 5 seconds on session setup - * failure to make it harder to send enough random connection requests - * to break into a server. - */ - if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) - try_delay = true; - - sess->last_active = jiffies; - sess->state = SMB2_SESSION_EXPIRED; - if (try_delay) { - ksmbd_conn_set_need_reconnect(conn); - ssleep(5); - ksmbd_conn_set_need_negotiate(conn); - } - } - } - - ksmbd_conn_unlock(conn); - return rc; -} - -/** - * smb2_tree_connect() - handler for smb2 tree connect command - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_tree_connect(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_tree_connect_req *req = smb2_get_msg(work->request_buf); - struct smb2_tree_connect_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess = work->sess; - char *treename = NULL, *name = NULL; - struct ksmbd_tree_conn_status status; - struct ksmbd_share_config *share; - int rc = -EINVAL; - - treename = smb_strndup_from_utf16(req->Buffer, - le16_to_cpu(req->PathLength), true, - conn->local_nls); - if (IS_ERR(treename)) { - pr_err("treename is NULL\n"); - status.ret = KSMBD_TREE_CONN_STATUS_ERROR; - goto out_err1; - } - - name = ksmbd_extract_sharename(conn->um, treename); - if (IS_ERR(name)) { - status.ret = KSMBD_TREE_CONN_STATUS_ERROR; - goto out_err1; - } - - ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", - name, treename); - - status = ksmbd_tree_conn_connect(conn, sess, name); - if (status.ret == KSMBD_TREE_CONN_STATUS_OK) - rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); - else - goto out_err1; - - share = status.tree_conn->share_conf; - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC share path request\n"); - rsp->ShareType = SMB2_SHARE_TYPE_PIPE; - rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | - FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | - FILE_DELETE_LE | FILE_READ_CONTROL_LE | - FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | - FILE_SYNCHRONIZE_LE; - } else { - rsp->ShareType = SMB2_SHARE_TYPE_DISK; - rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | - FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; - if (test_tree_conn_flag(status.tree_conn, - KSMBD_TREE_CONN_FLAG_WRITABLE)) { - rsp->MaximalAccess |= FILE_WRITE_DATA_LE | - FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | - FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | - FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | - FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | - FILE_SYNCHRONIZE_LE; - } - } - - status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); - if (conn->posix_ext_supported) - status.tree_conn->posix_extensions = true; - - rsp->StructureSize = cpu_to_le16(16); - inc_rfc1001_len(work->response_buf, 16); -out_err1: - rsp->Capabilities = 0; - rsp->Reserved = 0; - /* default manual caching */ - rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; - - if (!IS_ERR(treename)) - kfree(treename); - if (!IS_ERR(name)) - kfree(name); - - switch (status.ret) { - case KSMBD_TREE_CONN_STATUS_OK: - rsp->hdr.Status = STATUS_SUCCESS; - rc = 0; - break; - case -ESTALE: - case -ENOENT: - case KSMBD_TREE_CONN_STATUS_NO_SHARE: - rsp->hdr.Status = STATUS_BAD_NETWORK_NAME; - break; - case -ENOMEM: - case KSMBD_TREE_CONN_STATUS_NOMEM: - rsp->hdr.Status = STATUS_NO_MEMORY; - break; - case KSMBD_TREE_CONN_STATUS_ERROR: - case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: - case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: - rsp->hdr.Status = STATUS_ACCESS_DENIED; - break; - case -EINVAL: - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - break; - default: - rsp->hdr.Status = STATUS_ACCESS_DENIED; - } - - if (status.ret != KSMBD_TREE_CONN_STATUS_OK) - smb2_set_err_rsp(work); - - return rc; -} - -/** - * smb2_create_open_flags() - convert smb open flags to unix open flags - * @file_present: is file already present - * @access: file access flags - * @disposition: file disposition flags - * @may_flags: set with MAY_ flags - * - * Return: file open flags - */ -static int smb2_create_open_flags(bool file_present, __le32 access, - __le32 disposition, - int *may_flags) -{ - int oflags = O_NONBLOCK | O_LARGEFILE; - - if (access & FILE_READ_DESIRED_ACCESS_LE && - access & FILE_WRITE_DESIRE_ACCESS_LE) { - oflags |= O_RDWR; - *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; - } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { - oflags |= O_WRONLY; - *may_flags = MAY_OPEN | MAY_WRITE; - } else { - oflags |= O_RDONLY; - *may_flags = MAY_OPEN | MAY_READ; - } - - if (access == FILE_READ_ATTRIBUTES_LE) - oflags |= O_PATH; - - if (file_present) { - switch (disposition & FILE_CREATE_MASK_LE) { - case FILE_OPEN_LE: - case FILE_CREATE_LE: - break; - case FILE_SUPERSEDE_LE: - case FILE_OVERWRITE_LE: - case FILE_OVERWRITE_IF_LE: - oflags |= O_TRUNC; - break; - default: - break; - } - } else { - switch (disposition & FILE_CREATE_MASK_LE) { - case FILE_SUPERSEDE_LE: - case FILE_CREATE_LE: - case FILE_OPEN_IF_LE: - case FILE_OVERWRITE_IF_LE: - oflags |= O_CREAT; - break; - case FILE_OPEN_LE: - case FILE_OVERWRITE_LE: - oflags &= ~O_CREAT; - break; - default: - break; - } - } - - return oflags; -} - -/** - * smb2_tree_disconnect() - handler for smb tree connect request - * @work: smb work containing request buffer - * - * Return: 0 - */ -int smb2_tree_disconnect(struct ksmbd_work *work) -{ - struct smb2_tree_disconnect_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess = work->sess; - struct ksmbd_tree_connect *tcon = work->tcon; - - rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(work->response_buf, 4); - - ksmbd_debug(SMB, "request\n"); - - if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { - struct smb2_tree_disconnect_req *req = - smb2_get_msg(work->request_buf); - - ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); - - rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; - smb2_set_err_rsp(work); - return 0; - } - - ksmbd_close_tree_conn_fds(work); - ksmbd_tree_conn_disconnect(sess, tcon); - work->tcon = NULL; - return 0; -} - -/** - * smb2_session_logoff() - handler for session log off request - * @work: smb work containing request buffer - * - * Return: 0 - */ -int smb2_session_logoff(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess; - struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); - u64 sess_id = le64_to_cpu(req->hdr.SessionId); - - rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(work->response_buf, 4); - - ksmbd_debug(SMB, "request\n"); - - ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); - ksmbd_close_session_fds(work); - ksmbd_conn_wait_idle(conn, sess_id); - - /* - * Re-lookup session to validate if session is deleted - * while waiting request complete - */ - sess = ksmbd_session_lookup_all(conn, sess_id); - if (ksmbd_tree_conn_session_logoff(sess)) { - ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); - rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; - smb2_set_err_rsp(work); - return 0; - } - - ksmbd_destroy_file_table(&sess->file_table); - sess->state = SMB2_SESSION_EXPIRED; - - ksmbd_free_user(sess->user); - sess->user = NULL; - ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); - return 0; -} - -/** - * create_smb2_pipe() - create IPC pipe - * @work: smb work containing request buffer - * - * Return: 0 on success, otherwise error - */ -static noinline int create_smb2_pipe(struct ksmbd_work *work) -{ - struct smb2_create_rsp *rsp = smb2_get_msg(work->response_buf); - struct smb2_create_req *req = smb2_get_msg(work->request_buf); - int id; - int err; - char *name; - - name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), - 1, work->conn->local_nls); - if (IS_ERR(name)) { - rsp->hdr.Status = STATUS_NO_MEMORY; - err = PTR_ERR(name); - goto out; - } - - id = ksmbd_session_rpc_open(work->sess, name); - if (id < 0) { - pr_err("Unable to open RPC pipe: %d\n", id); - err = id; - goto out; - } - - rsp->hdr.Status = STATUS_SUCCESS; - rsp->StructureSize = cpu_to_le16(89); - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; - rsp->Flags = 0; - rsp->CreateAction = cpu_to_le32(FILE_OPENED); - - rsp->CreationTime = cpu_to_le64(0); - rsp->LastAccessTime = cpu_to_le64(0); - rsp->ChangeTime = cpu_to_le64(0); - rsp->AllocationSize = cpu_to_le64(0); - rsp->EndofFile = cpu_to_le64(0); - rsp->FileAttributes = FILE_ATTRIBUTE_NORMAL_LE; - rsp->Reserved2 = 0; - rsp->VolatileFileId = id; - rsp->PersistentFileId = 0; - rsp->CreateContextsOffset = 0; - rsp->CreateContextsLength = 0; - - inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ - kfree(name); - return 0; - -out: - switch (err) { - case -EINVAL: - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - break; - case -ENOSPC: - case -ENOMEM: - rsp->hdr.Status = STATUS_NO_MEMORY; - break; - } - - if (!IS_ERR(name)) - kfree(name); - - smb2_set_err_rsp(work); - return err; -} - -/** - * smb2_set_ea() - handler for setting extended attributes using set - * info command - * @eabuf: set info command buffer - * @buf_len: set info command buffer length - * @path: dentry path for get ea - * - * Return: 0 on success, otherwise error - */ -static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, - const struct path *path) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - char *attr_name = NULL, *value; - int rc = 0; - unsigned int next = 0; - - if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + - le16_to_cpu(eabuf->EaValueLength)) - return -EINVAL; - - attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); - if (!attr_name) - return -ENOMEM; - - do { - if (!eabuf->EaNameLength) - goto next; - - ksmbd_debug(SMB, - "name : <%s>, name_len : %u, value_len : %u, next : %u\n", - eabuf->name, eabuf->EaNameLength, - le16_to_cpu(eabuf->EaValueLength), - le32_to_cpu(eabuf->NextEntryOffset)); - - if (eabuf->EaNameLength > - (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { - rc = -EINVAL; - break; - } - - memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); - memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, - eabuf->EaNameLength); - attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; - value = (char *)&eabuf->name + eabuf->EaNameLength + 1; - - if (!eabuf->EaValueLength) { - rc = ksmbd_vfs_casexattr_len(user_ns, - path->dentry, - attr_name, - XATTR_USER_PREFIX_LEN + - eabuf->EaNameLength); - - /* delete the EA only when it exits */ - if (rc > 0) { - rc = ksmbd_vfs_remove_xattr(user_ns, - path->dentry, - attr_name); - - if (rc < 0) { - ksmbd_debug(SMB, - "remove xattr failed(%d)\n", - rc); - break; - } - } - - /* if the EA doesn't exist, just do nothing. */ - rc = 0; - } else { - rc = ksmbd_vfs_setxattr(user_ns, - path->dentry, attr_name, value, - le16_to_cpu(eabuf->EaValueLength), 0); - if (rc < 0) { - ksmbd_debug(SMB, - "ksmbd_vfs_setxattr is failed(%d)\n", - rc); - break; - } - } - -next: - next = le32_to_cpu(eabuf->NextEntryOffset); - if (next == 0 || buf_len < next) - break; - buf_len -= next; - eabuf = (struct smb2_ea_info *)((char *)eabuf + next); - if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) - break; - - } while (next != 0); - - kfree(attr_name); - return rc; -} - -static noinline int smb2_set_stream_name_xattr(const struct path *path, - struct ksmbd_file *fp, - char *stream_name, int s_type) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - size_t xattr_stream_size; - char *xattr_stream_name; - int rc; - - rc = ksmbd_vfs_xattr_stream_name(stream_name, - &xattr_stream_name, - &xattr_stream_size, - s_type); - if (rc) - return rc; - - fp->stream.name = xattr_stream_name; - fp->stream.size = xattr_stream_size; - - /* Check if there is stream prefix in xattr space */ - rc = ksmbd_vfs_casexattr_len(user_ns, - path->dentry, - xattr_stream_name, - xattr_stream_size); - if (rc >= 0) - return 0; - - if (fp->cdoption == FILE_OPEN_LE) { - ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); - return -EBADF; - } - - rc = ksmbd_vfs_setxattr(user_ns, path->dentry, - xattr_stream_name, NULL, 0, 0); - if (rc < 0) - pr_err("Failed to store XATTR stream name :%d\n", rc); - return 0; -} - -static int smb2_remove_smb_xattrs(const struct path *path) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - char *name, *xattr_list = NULL; - ssize_t xattr_list_len; - int err = 0; - - xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); - - if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && - !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, - STREAM_PREFIX_LEN)) { - err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, - name); - if (err) - ksmbd_debug(SMB, "remove xattr failed : %s\n", - name); - } - } -out: - kvfree(xattr_list); - return err; -} - -static int smb2_create_truncate(const struct path *path) -{ - int rc = vfs_truncate(path, 0); - - if (rc) { - pr_err("vfs_truncate failed, rc %d\n", rc); - return rc; - } - - rc = smb2_remove_smb_xattrs(path); - if (rc == -EOPNOTSUPP) - rc = 0; - if (rc) - ksmbd_debug(SMB, - "ksmbd_truncate_stream_name_xattr failed, rc %d\n", - rc); - return rc; -} - -static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *path, - struct ksmbd_file *fp) -{ - struct xattr_dos_attrib da = {0}; - int rc; - - if (!test_share_config_flag(tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) - return; - - da.version = 4; - da.attr = le32_to_cpu(fp->f_ci->m_fattr); - da.itime = da.create_time = fp->create_time; - da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | - XATTR_DOSINFO_ITIME; - - rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), - path->dentry, &da); - if (rc) - ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); -} - -static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, - const struct path *path, struct ksmbd_file *fp) -{ - struct xattr_dos_attrib da; - int rc; - - fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE); - - /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ - if (!test_share_config_flag(tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) - return; - - rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path->mnt), - path->dentry, &da); - if (rc > 0) { - fp->f_ci->m_fattr = cpu_to_le32(da.attr); - fp->create_time = da.create_time; - fp->itime = da.itime; - } -} - -static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, - int open_flags, umode_t posix_mode, bool is_dir) -{ - struct ksmbd_tree_connect *tcon = work->tcon; - struct ksmbd_share_config *share = tcon->share_conf; - umode_t mode; - int rc; - - if (!(open_flags & O_CREAT)) - return -EBADF; - - ksmbd_debug(SMB, "file does not exist, so creating\n"); - if (is_dir == true) { - ksmbd_debug(SMB, "creating directory\n"); - - mode = share_config_directory_mode(share, posix_mode); - rc = ksmbd_vfs_mkdir(work, name, mode); - if (rc) - return rc; - } else { - ksmbd_debug(SMB, "creating regular file\n"); - - mode = share_config_create_mode(share, posix_mode); - rc = ksmbd_vfs_create(work, name, mode); - if (rc) - return rc; - } - - rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); - if (rc) { - pr_err("cannot get linux path (%s), err = %d\n", - name, rc); - return rc; - } - return 0; -} - -static int smb2_create_sd_buffer(struct ksmbd_work *work, - struct smb2_create_req *req, - const struct path *path) -{ - struct create_context *context; - struct create_sd_buf_req *sd_buf; - - if (!req->CreateContextsOffset) - return -ENOENT; - - /* Parse SD BUFFER create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER, 4); - if (!context) - return -ENOENT; - else if (IS_ERR(context)) - return PTR_ERR(context); - - ksmbd_debug(SMB, - "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); - sd_buf = (struct create_sd_buf_req *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_sd_buf_req)) - return -EINVAL; - return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, - le32_to_cpu(sd_buf->ccontext.DataLength), true); -} - -static void ksmbd_acls_fattr(struct smb_fattr *fattr, - struct user_namespace *mnt_userns, - struct inode *inode) -{ - vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode); - vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode); - - fattr->cf_uid = vfsuid_into_kuid(vfsuid); - fattr->cf_gid = vfsgid_into_kgid(vfsgid); - fattr->cf_mode = inode->i_mode; - fattr->cf_acls = NULL; - fattr->cf_dacls = NULL; - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); - } -} - -/** - * smb2_open() - handler for smb file open request - * @work: smb work containing request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_open(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct ksmbd_tree_connect *tcon = work->tcon; - struct smb2_create_req *req; - struct smb2_create_rsp *rsp; - struct path path; - struct ksmbd_share_config *share = tcon->share_conf; - struct ksmbd_file *fp = NULL; - struct file *filp = NULL; - struct user_namespace *user_ns = NULL; - struct kstat stat; - struct create_context *context; - struct lease_ctx_info *lc = NULL; - struct create_ea_buf_req *ea_buf = NULL; - struct oplock_info *opinfo; - __le32 *next_ptr = NULL; - int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; - int rc = 0; - int contxt_cnt = 0, query_disk_id = 0; - int maximal_access_ctxt = 0, posix_ctxt = 0; - int s_type = 0; - int next_off = 0; - char *name = NULL; - char *stream_name = NULL; - bool file_present = false, created = false, already_permitted = false; - int share_ret, need_truncate = 0; - u64 time; - umode_t posix_mode = 0; - __le32 daccess, maximal_access = 0; - - WORK_BUFFERS(work, req, rsp); - - if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && - (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { - ksmbd_debug(SMB, "invalid flag in chained command\n"); - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - smb2_set_err_rsp(work); - return -EINVAL; - } - - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe create request\n"); - return create_smb2_pipe(work); - } - - if (req->NameLength) { - if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && - *(char *)req->Buffer == '\\') { - pr_err("not allow directory name included leading slash\n"); - rc = -EINVAL; - goto err_out1; - } - - name = smb2_get_name(req->Buffer, - le16_to_cpu(req->NameLength), - work->conn->local_nls); - if (IS_ERR(name)) { - rc = PTR_ERR(name); - if (rc != -ENOMEM) - rc = -ENOENT; - name = NULL; - goto err_out1; - } - - ksmbd_debug(SMB, "converted name = %s\n", name); - if (strchr(name, ':')) { - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS)) { - rc = -EBADF; - goto err_out1; - } - rc = parse_stream_name(name, &stream_name, &s_type); - if (rc < 0) - goto err_out1; - } - - rc = ksmbd_validate_filename(name); - if (rc < 0) - goto err_out1; - - if (ksmbd_share_veto_filename(share, name)) { - rc = -ENOENT; - ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", - name); - goto err_out1; - } - } else { - name = kstrdup("", GFP_KERNEL); - if (!name) { - rc = -ENOMEM; - goto err_out1; - } - } - - req_op_level = req->RequestedOplockLevel; - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) - lc = parse_lease_state(req); - - if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { - pr_err("Invalid impersonationlevel : 0x%x\n", - le32_to_cpu(req->ImpersonationLevel)); - rc = -EIO; - rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; - goto err_out1; - } - - if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { - pr_err("Invalid create options : 0x%x\n", - le32_to_cpu(req->CreateOptions)); - rc = -EINVAL; - goto err_out1; - } else { - if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && - req->CreateOptions & FILE_RANDOM_ACCESS_LE) - req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); - - if (req->CreateOptions & - (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | - FILE_RESERVE_OPFILTER_LE)) { - rc = -EOPNOTSUPP; - goto err_out1; - } - - if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { - if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { - rc = -EINVAL; - goto err_out1; - } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { - req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); - } - } - } - - if (le32_to_cpu(req->CreateDisposition) > - le32_to_cpu(FILE_OVERWRITE_IF_LE)) { - pr_err("Invalid create disposition : 0x%x\n", - le32_to_cpu(req->CreateDisposition)); - rc = -EINVAL; - goto err_out1; - } - - if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { - pr_err("Invalid desired access : 0x%x\n", - le32_to_cpu(req->DesiredAccess)); - rc = -EACCES; - goto err_out1; - } - - if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { - pr_err("Invalid file attribute : 0x%x\n", - le32_to_cpu(req->FileAttributes)); - rc = -EINVAL; - goto err_out1; - } - - if (req->CreateContextsOffset) { - /* Parse non-durable handle create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - ea_buf = (struct create_ea_buf_req *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_ea_buf_req)) { - rc = -EINVAL; - goto err_out1; - } - if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { - rsp->hdr.Status = STATUS_ACCESS_DENIED; - rc = -EACCES; - goto err_out1; - } - } - - context = smb2_find_context_vals(req, - SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - ksmbd_debug(SMB, - "get query maximal access context\n"); - maximal_access_ctxt = 1; - } - - context = smb2_find_context_vals(req, - SMB2_CREATE_TIMEWARP_REQUEST, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - ksmbd_debug(SMB, "get timewarp context\n"); - rc = -EBADF; - goto err_out1; - } - - if (tcon->posix_extensions) { - context = smb2_find_context_vals(req, - SMB2_CREATE_TAG_POSIX, 16); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - struct create_posix *posix = - (struct create_posix *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_posix) - 4) { - rc = -EINVAL; - goto err_out1; - } - ksmbd_debug(SMB, "get posix context\n"); - - posix_mode = le32_to_cpu(posix->Mode); - posix_ctxt = 1; - } - } - } - - if (ksmbd_override_fsids(work)) { - rc = -ENOMEM; - goto err_out1; - } - - rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); - if (!rc) { - if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { - /* - * If file exists with under flags, return access - * denied error. - */ - if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || - req->CreateDisposition == FILE_OPEN_IF_LE) { - rc = -EACCES; - path_put(&path); - goto err_out; - } - - if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - rc = -EACCES; - path_put(&path); - goto err_out; - } - } else if (d_is_symlink(path.dentry)) { - rc = -EACCES; - path_put(&path); - goto err_out; - } - } - - if (rc) { - if (rc != -ENOENT) - goto err_out; - ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", - name, rc); - rc = 0; - } else { - file_present = true; - user_ns = mnt_user_ns(path.mnt); - } - if (stream_name) { - if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { - if (s_type == DATA_STREAM) { - rc = -EIO; - rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; - } - } else { - if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) && - s_type == DATA_STREAM) { - rc = -EIO; - rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; - } - } - - if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && - req->FileAttributes & FILE_ATTRIBUTE_NORMAL_LE) { - rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; - rc = -EIO; - } - - if (rc < 0) - goto err_out; - } - - if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && - S_ISDIR(d_inode(path.dentry)->i_mode) && - !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", - name, req->CreateOptions); - rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; - rc = -EIO; - goto err_out; - } - - if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && - !(req->CreateDisposition == FILE_CREATE_LE) && - !S_ISDIR(d_inode(path.dentry)->i_mode)) { - rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; - rc = -EIO; - goto err_out; - } - - if (!stream_name && file_present && - req->CreateDisposition == FILE_CREATE_LE) { - rc = -EEXIST; - goto err_out; - } - - daccess = smb_map_generic_desired_access(req->DesiredAccess); - - if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = smb_check_perm_dacl(conn, &path, &daccess, - sess->user->uid); - if (rc) - goto err_out; - } - - if (daccess & FILE_MAXIMAL_ACCESS_LE) { - if (!file_present) { - daccess = cpu_to_le32(GENERIC_ALL_FLAGS); - } else { - rc = ksmbd_vfs_query_maximal_access(user_ns, - path.dentry, - &daccess); - if (rc) - goto err_out; - already_permitted = true; - } - maximal_access = daccess; - } - - open_flags = smb2_create_open_flags(file_present, daccess, - req->CreateDisposition, - &may_flags); - - if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - if (open_flags & O_CREAT) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - rc = -EACCES; - goto err_out; - } - } - - /*create file if not present */ - if (!file_present) { - rc = smb2_creat(work, &path, name, open_flags, posix_mode, - req->CreateOptions & FILE_DIRECTORY_FILE_LE); - if (rc) { - if (rc == -ENOENT) { - rc = -EIO; - rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; - } - goto err_out; - } - - created = true; - user_ns = mnt_user_ns(path.mnt); - if (ea_buf) { - if (le32_to_cpu(ea_buf->ccontext.DataLength) < - sizeof(struct smb2_ea_info)) { - rc = -EINVAL; - goto err_out; - } - - rc = smb2_set_ea(&ea_buf->ea, - le32_to_cpu(ea_buf->ccontext.DataLength), - &path); - if (rc == -EOPNOTSUPP) - rc = 0; - else if (rc) - goto err_out; - } - } else if (!already_permitted) { - /* FILE_READ_ATTRIBUTE is allowed without inode_permission, - * because execute(search) permission on a parent directory, - * is already granted. - */ - if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { - rc = inode_permission(user_ns, - d_inode(path.dentry), - may_flags); - if (rc) - goto err_out; - - if ((daccess & FILE_DELETE_LE) || - (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = ksmbd_vfs_may_delete(user_ns, - path.dentry); - if (rc) - goto err_out; - } - } - } - - rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); - if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { - rc = -EBUSY; - goto err_out; - } - - rc = 0; - filp = dentry_open(&path, open_flags, current_cred()); - if (IS_ERR(filp)) { - rc = PTR_ERR(filp); - pr_err("dentry open for dir failed, rc %d\n", rc); - goto err_out; - } - - if (file_present) { - if (!(open_flags & O_TRUNC)) - file_info = FILE_OPENED; - else - file_info = FILE_OVERWRITTEN; - - if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == - FILE_SUPERSEDE_LE) - file_info = FILE_SUPERSEDED; - } else if (open_flags & O_CREAT) { - file_info = FILE_CREATED; - } - - ksmbd_vfs_set_fadvise(filp, req->CreateOptions); - - /* Obtain Volatile-ID */ - fp = ksmbd_open_fd(work, filp); - if (IS_ERR(fp)) { - fput(filp); - rc = PTR_ERR(fp); - fp = NULL; - goto err_out; - } - - /* Get Persistent-ID */ - ksmbd_open_durable_fd(fp); - if (!has_file_id(fp->persistent_id)) { - rc = -ENOMEM; - goto err_out; - } - - fp->cdoption = req->CreateDisposition; - fp->daccess = daccess; - fp->saccess = req->ShareAccess; - fp->coption = req->CreateOptions; - - /* Set default windows and posix acls if creating new file */ - if (created) { - int posix_acl_rc; - struct inode *inode = d_inode(path.dentry); - - posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, - inode, - d_inode(path.dentry->d_parent)); - if (posix_acl_rc) - ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { - rc = smb_inherit_dacl(conn, &path, sess->user->uid, - sess->user->gid); - } - - if (rc) { - rc = smb2_create_sd_buffer(work, req, &path); - if (rc) { - if (posix_acl_rc) - ksmbd_vfs_set_init_posix_acl(user_ns, - inode); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { - struct smb_fattr fattr; - struct smb_ntsd *pntsd; - int pntsd_size, ace_num = 0; - - ksmbd_acls_fattr(&fattr, user_ns, inode); - if (fattr.cf_acls) - ace_num = fattr.cf_acls->a_count; - if (fattr.cf_dacls) - ace_num += fattr.cf_dacls->a_count; - - pntsd = kmalloc(sizeof(struct smb_ntsd) + - sizeof(struct smb_sid) * 3 + - sizeof(struct smb_acl) + - sizeof(struct smb_ace) * ace_num * 2, - GFP_KERNEL); - if (!pntsd) { - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - goto err_out; - } - - rc = build_sec_desc(user_ns, - pntsd, NULL, 0, - OWNER_SECINFO | - GROUP_SECINFO | - DACL_SECINFO, - &pntsd_size, &fattr); - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - if (rc) { - kfree(pntsd); - goto err_out; - } - - rc = ksmbd_vfs_set_sd_xattr(conn, - user_ns, - path.dentry, - pntsd, - pntsd_size); - kfree(pntsd); - if (rc) - pr_err("failed to store ntacl in xattr : %d\n", - rc); - } - } - } - rc = 0; - } - - if (stream_name) { - rc = smb2_set_stream_name_xattr(&path, - fp, - stream_name, - s_type); - if (rc) - goto err_out; - file_info = FILE_CREATED; - } - - fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | - FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); - if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && - !fp->attrib_only && !stream_name) { - smb_break_all_oplock(work, fp); - need_truncate = 1; - } - - /* fp should be searchable through ksmbd_inode.m_fp_list - * after daccess, saccess, attrib_only, and stream are - * initialized. - */ - write_lock(&fp->f_ci->m_lock); - list_add(&fp->node, &fp->f_ci->m_fp_list); - write_unlock(&fp->f_ci->m_lock); - - /* Check delete pending among previous fp before oplock break */ - if (ksmbd_inode_pending_delete(fp)) { - rc = -EBUSY; - goto err_out; - } - - share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); - if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || - (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && - !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { - if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { - rc = share_ret; - goto err_out; - } - } else { - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { - req_op_level = smb2_map_lease_to_oplock(lc->req_state); - ksmbd_debug(SMB, - "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", - name, req_op_level, lc->req_state); - rc = find_same_lease_key(sess, fp->f_ci, lc); - if (rc) - goto err_out; - } else if (open_flags == O_RDONLY && - (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || - req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) - req_op_level = SMB2_OPLOCK_LEVEL_II; - - rc = smb_grant_oplock(work, req_op_level, - fp->persistent_id, fp, - le32_to_cpu(req->hdr.Id.SyncId.TreeId), - lc, share_ret); - if (rc < 0) - goto err_out; - } - - if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) - ksmbd_fd_set_delete_on_close(fp, file_info); - - if (need_truncate) { - rc = smb2_create_truncate(&path); - if (rc) - goto err_out; - } - - if (req->CreateContextsOffset) { - struct create_alloc_size_req *az_req; - - az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, - SMB2_CREATE_ALLOCATION_SIZE, 4); - if (IS_ERR(az_req)) { - rc = PTR_ERR(az_req); - goto err_out; - } else if (az_req) { - loff_t alloc_size; - int err; - - if (le16_to_cpu(az_req->ccontext.DataOffset) + - le32_to_cpu(az_req->ccontext.DataLength) < - sizeof(struct create_alloc_size_req)) { - rc = -EINVAL; - goto err_out; - } - alloc_size = le64_to_cpu(az_req->AllocationSize); - ksmbd_debug(SMB, - "request smb2 create allocate size : %llu\n", - alloc_size); - smb_break_all_levII_oplock(work, fp, 1); - err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, - alloc_size); - if (err < 0) - ksmbd_debug(SMB, - "vfs_fallocate is failed : %d\n", - err); - } - - context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out; - } else if (context) { - ksmbd_debug(SMB, "get query on disk id context\n"); - query_disk_id = 1; - } - } - - rc = ksmbd_vfs_getattr(&path, &stat); - if (rc) - goto err_out; - - if (stat.result_mask & STATX_BTIME) - fp->create_time = ksmbd_UnixTimeToNT(stat.btime); - else - fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); - if (req->FileAttributes || fp->f_ci->m_fattr == 0) - fp->f_ci->m_fattr = - cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); - - if (!created) - smb2_update_xattrs(tcon, &path, fp); - else - smb2_new_xattrs(tcon, &path, fp); - - memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - - rsp->StructureSize = cpu_to_le16(89); - rcu_read_lock(); - opinfo = rcu_dereference(fp->f_opinfo); - rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; - rcu_read_unlock(); - rsp->Flags = 0; - rsp->CreateAction = cpu_to_le32(file_info); - rsp->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - rsp->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - rsp->ChangeTime = cpu_to_le64(time); - rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.blocks << 9); - rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - rsp->FileAttributes = fp->f_ci->m_fattr; - - rsp->Reserved2 = 0; - - rsp->PersistentFileId = fp->persistent_id; - rsp->VolatileFileId = fp->volatile_id; - - rsp->CreateContextsOffset = 0; - rsp->CreateContextsLength = 0; - inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ - - /* If lease is request send lease context response */ - if (opinfo && opinfo->is_lease) { - struct create_context *lease_ccontext; - - ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", - name, opinfo->o_lease->state); - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; - - lease_ccontext = (struct create_context *)rsp->Buffer; - contxt_cnt++; - create_lease_buf(rsp->Buffer, opinfo->o_lease); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_lease_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_lease_size); - next_ptr = &lease_ccontext->Next; - next_off = conn->vals->create_lease_size; - } - - if (maximal_access_ctxt) { - struct create_context *mxac_ccontext; - - if (maximal_access == 0) - ksmbd_vfs_query_maximal_access(user_ns, - path.dentry, - &maximal_access); - mxac_ccontext = (struct create_context *)(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength)); - contxt_cnt++; - create_mxac_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength), - le32_to_cpu(maximal_access)); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_mxac_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_mxac_size); - if (next_ptr) - *next_ptr = cpu_to_le32(next_off); - next_ptr = &mxac_ccontext->Next; - next_off = conn->vals->create_mxac_size; - } - - if (query_disk_id) { - struct create_context *disk_id_ccontext; - - disk_id_ccontext = (struct create_context *)(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength)); - contxt_cnt++; - create_disk_id_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength), - stat.ino, tcon->id); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_disk_id_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_disk_id_size); - if (next_ptr) - *next_ptr = cpu_to_le32(next_off); - next_ptr = &disk_id_ccontext->Next; - next_off = conn->vals->create_disk_id_size; - } - - if (posix_ctxt) { - contxt_cnt++; - create_posix_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength), - fp); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_posix_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_posix_size); - if (next_ptr) - *next_ptr = cpu_to_le32(next_off); - } - - if (contxt_cnt > 0) { - rsp->CreateContextsOffset = - cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); - } - -err_out: - if (file_present || created) - path_put(&path); - ksmbd_revert_fsids(work); -err_out1: - if (rc) { - if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; - else if (rc == -EPERM) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (rc == -EBUSY) - rsp->hdr.Status = STATUS_DELETE_PENDING; - else if (rc == -EBADF) - rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; - else if (rc == -ENOEXEC) - rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; - else if (rc == -ENXIO) - rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; - else if (rc == -EEXIST) - rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; - else if (rc == -EMFILE) - rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; - if (!rsp->hdr.Status) - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - - if (fp) - ksmbd_fd_put(work, fp); - smb2_set_err_rsp(work); - ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); - } - - kfree(name); - kfree(lc); - - return 0; -} - -static int readdir_info_level_struct_sz(int info_level) -{ - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_full_directory_info); - case FILE_BOTH_DIRECTORY_INFORMATION: - return sizeof(struct file_both_directory_info); - case FILE_DIRECTORY_INFORMATION: - return sizeof(struct file_directory_info); - case FILE_NAMES_INFORMATION: - return sizeof(struct file_names_info); - case FILEID_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_id_full_dir_info); - case FILEID_BOTH_DIRECTORY_INFORMATION: - return sizeof(struct file_id_both_directory_info); - case SMB_FIND_FILE_POSIX_INFO: - return sizeof(struct smb2_posix_info); - default: - return -EOPNOTSUPP; - } -} - -static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) -{ - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - { - struct file_full_directory_info *ffdinfo; - - ffdinfo = (struct file_full_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); - d_info->name = ffdinfo->FileName; - d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); - return 0; - } - case FILE_BOTH_DIRECTORY_INFORMATION: - { - struct file_both_directory_info *fbdinfo; - - fbdinfo = (struct file_both_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); - d_info->name = fbdinfo->FileName; - d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); - return 0; - } - case FILE_DIRECTORY_INFORMATION: - { - struct file_directory_info *fdinfo; - - fdinfo = (struct file_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); - d_info->name = fdinfo->FileName; - d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); - return 0; - } - case FILE_NAMES_INFORMATION: - { - struct file_names_info *fninfo; - - fninfo = (struct file_names_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); - d_info->name = fninfo->FileName; - d_info->name_len = le32_to_cpu(fninfo->FileNameLength); - return 0; - } - case FILEID_FULL_DIRECTORY_INFORMATION: - { - struct file_id_full_dir_info *dinfo; - - dinfo = (struct file_id_full_dir_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); - d_info->name = dinfo->FileName; - d_info->name_len = le32_to_cpu(dinfo->FileNameLength); - return 0; - } - case FILEID_BOTH_DIRECTORY_INFORMATION: - { - struct file_id_both_directory_info *fibdinfo; - - fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); - d_info->name = fibdinfo->FileName; - d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); - return 0; - } - case SMB_FIND_FILE_POSIX_INFO: - { - struct smb2_posix_info *posix_info; - - posix_info = (struct smb2_posix_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); - d_info->name = posix_info->name; - d_info->name_len = le32_to_cpu(posix_info->name_len); - return 0; - } - default: - return -EINVAL; - } -} - -/** - * smb2_populate_readdir_entry() - encode directory entry in smb2 response - * buffer - * @conn: connection instance - * @info_level: smb information level - * @d_info: structure included variables for query dir - * @ksmbd_kstat: ksmbd wrapper of dirent stat information - * - * if directory has many entries, find first can't read it fully. - * find next might be called multiple times to read remaining dir entries - * - * Return: 0 on success, otherwise error - */ -static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, - struct ksmbd_dir_info *d_info, - struct ksmbd_kstat *ksmbd_kstat) -{ - int next_entry_offset = 0; - char *conv_name; - int conv_len; - void *kstat; - int struct_sz, rc = 0; - - conv_name = ksmbd_convert_dir_info_name(d_info, - conn->local_nls, - &conv_len); - if (!conv_name) - return -ENOMEM; - - /* Somehow the name has only terminating NULL bytes */ - if (conv_len < 0) { - rc = -EINVAL; - goto free_conv_name; - } - - struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len; - next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); - d_info->last_entry_off_align = next_entry_offset - struct_sz; - - if (next_entry_offset > d_info->out_buf_len) { - d_info->out_buf_len = 0; - rc = -ENOSPC; - goto free_conv_name; - } - - kstat = d_info->wptr; - if (info_level != FILE_NAMES_INFORMATION) - kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); - - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - { - struct file_full_directory_info *ffdinfo; - - ffdinfo = (struct file_full_directory_info *)kstat; - ffdinfo->FileNameLength = cpu_to_le32(conv_len); - ffdinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (ffdinfo->EaSize) - ffdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - if (d_info->hide_dot_file && d_info->name[0] == '.') - ffdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(ffdinfo->FileName, conv_name, conv_len); - ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_BOTH_DIRECTORY_INFORMATION: - { - struct file_both_directory_info *fbdinfo; - - fbdinfo = (struct file_both_directory_info *)kstat; - fbdinfo->FileNameLength = cpu_to_le32(conv_len); - fbdinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (fbdinfo->EaSize) - fbdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - fbdinfo->ShortNameLength = 0; - fbdinfo->Reserved = 0; - if (d_info->hide_dot_file && d_info->name[0] == '.') - fbdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(fbdinfo->FileName, conv_name, conv_len); - fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_DIRECTORY_INFORMATION: - { - struct file_directory_info *fdinfo; - - fdinfo = (struct file_directory_info *)kstat; - fdinfo->FileNameLength = cpu_to_le32(conv_len); - if (d_info->hide_dot_file && d_info->name[0] == '.') - fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(fdinfo->FileName, conv_name, conv_len); - fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_NAMES_INFORMATION: - { - struct file_names_info *fninfo; - - fninfo = (struct file_names_info *)kstat; - fninfo->FileNameLength = cpu_to_le32(conv_len); - memcpy(fninfo->FileName, conv_name, conv_len); - fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_FULL_DIRECTORY_INFORMATION: - { - struct file_id_full_dir_info *dinfo; - - dinfo = (struct file_id_full_dir_info *)kstat; - dinfo->FileNameLength = cpu_to_le32(conv_len); - dinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (dinfo->EaSize) - dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - dinfo->Reserved = 0; - dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); - if (d_info->hide_dot_file && d_info->name[0] == '.') - dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(dinfo->FileName, conv_name, conv_len); - dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_BOTH_DIRECTORY_INFORMATION: - { - struct file_id_both_directory_info *fibdinfo; - - fibdinfo = (struct file_id_both_directory_info *)kstat; - fibdinfo->FileNameLength = cpu_to_le32(conv_len); - fibdinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (fibdinfo->EaSize) - fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); - fibdinfo->ShortNameLength = 0; - fibdinfo->Reserved = 0; - fibdinfo->Reserved2 = cpu_to_le16(0); - if (d_info->hide_dot_file && d_info->name[0] == '.') - fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(fibdinfo->FileName, conv_name, conv_len); - fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case SMB_FIND_FILE_POSIX_INFO: - { - struct smb2_posix_info *posix_info; - u64 time; - - posix_info = (struct smb2_posix_info *)kstat; - posix_info->Ignored = 0; - posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); - posix_info->ChangeTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); - posix_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); - posix_info->LastWriteTime = cpu_to_le64(time); - posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); - posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); - posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); - posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); - posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); - posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); - posix_info->DosAttributes = - S_ISDIR(ksmbd_kstat->kstat->mode) ? - FILE_ATTRIBUTE_DIRECTORY_LE : FILE_ATTRIBUTE_ARCHIVE_LE; - if (d_info->hide_dot_file && d_info->name[0] == '.') - posix_info->DosAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - /* - * SidBuffer(32) contain two sids(Domain sid(16), UNIX group sid(16)). - * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 1(num_subauth)) + RID(4). - */ - id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), - SIDUNIX_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); - id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), - SIDUNIX_GROUP, (struct smb_sid *)&posix_info->SidBuffer[16]); - memcpy(posix_info->name, conv_name, conv_len); - posix_info->name_len = cpu_to_le32(conv_len); - posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - - } /* switch (info_level) */ - - d_info->last_entry_offset = d_info->data_count; - d_info->data_count += next_entry_offset; - d_info->out_buf_len -= next_entry_offset; - d_info->wptr += next_entry_offset; - - ksmbd_debug(SMB, - "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", - info_level, d_info->out_buf_len, - next_entry_offset, d_info->data_count); - -free_conv_name: - kfree(conv_name); - return rc; -} - -struct smb2_query_dir_private { - struct ksmbd_work *work; - char *search_pattern; - struct ksmbd_file *dir_fp; - - struct ksmbd_dir_info *d_info; - int info_level; -}; - -static void lock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); -} - -static void unlock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_unlock(d_inode(dir)); -} - -static int process_query_dir_entries(struct smb2_query_dir_private *priv) -{ - struct user_namespace *user_ns = file_mnt_user_ns(priv->dir_fp->filp); - struct kstat kstat; - struct ksmbd_kstat ksmbd_kstat; - int rc; - int i; - - for (i = 0; i < priv->d_info->num_entry; i++) { - struct dentry *dent; - - if (dentry_name(priv->d_info, priv->info_level)) - return -EINVAL; - - lock_dir(priv->dir_fp); - dent = lookup_one(user_ns, priv->d_info->name, - priv->dir_fp->filp->f_path.dentry, - priv->d_info->name_len); - unlock_dir(priv->dir_fp); - - if (IS_ERR(dent)) { - ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", - priv->d_info->name, - PTR_ERR(dent)); - continue; - } - if (unlikely(d_is_negative(dent))) { - dput(dent); - ksmbd_debug(SMB, "Negative dentry `%s'\n", - priv->d_info->name); - continue; - } - - ksmbd_kstat.kstat = &kstat; - if (priv->info_level != FILE_NAMES_INFORMATION) - ksmbd_vfs_fill_dentry_attrs(priv->work, - user_ns, - dent, - &ksmbd_kstat); - - rc = smb2_populate_readdir_entry(priv->work->conn, - priv->info_level, - priv->d_info, - &ksmbd_kstat); - dput(dent); - if (rc) - return rc; - } - return 0; -} - -static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, - int info_level) -{ - int struct_sz; - int conv_len; - int next_entry_offset; - - struct_sz = readdir_info_level_struct_sz(info_level); - if (struct_sz == -EOPNOTSUPP) - return -EOPNOTSUPP; - - conv_len = (d_info->name_len + 1) * 2; - next_entry_offset = ALIGN(struct_sz - 1 + conv_len, - KSMBD_DIR_INFO_ALIGNMENT); - - if (next_entry_offset > d_info->out_buf_len) { - d_info->out_buf_len = 0; - return -ENOSPC; - } - - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - { - struct file_full_directory_info *ffdinfo; - - ffdinfo = (struct file_full_directory_info *)d_info->wptr; - memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); - ffdinfo->FileName[d_info->name_len] = 0x00; - ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_BOTH_DIRECTORY_INFORMATION: - { - struct file_both_directory_info *fbdinfo; - - fbdinfo = (struct file_both_directory_info *)d_info->wptr; - memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); - fbdinfo->FileName[d_info->name_len] = 0x00; - fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_DIRECTORY_INFORMATION: - { - struct file_directory_info *fdinfo; - - fdinfo = (struct file_directory_info *)d_info->wptr; - memcpy(fdinfo->FileName, d_info->name, d_info->name_len); - fdinfo->FileName[d_info->name_len] = 0x00; - fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_NAMES_INFORMATION: - { - struct file_names_info *fninfo; - - fninfo = (struct file_names_info *)d_info->wptr; - memcpy(fninfo->FileName, d_info->name, d_info->name_len); - fninfo->FileName[d_info->name_len] = 0x00; - fninfo->FileNameLength = cpu_to_le32(d_info->name_len); - fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_FULL_DIRECTORY_INFORMATION: - { - struct file_id_full_dir_info *dinfo; - - dinfo = (struct file_id_full_dir_info *)d_info->wptr; - memcpy(dinfo->FileName, d_info->name, d_info->name_len); - dinfo->FileName[d_info->name_len] = 0x00; - dinfo->FileNameLength = cpu_to_le32(d_info->name_len); - dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_BOTH_DIRECTORY_INFORMATION: - { - struct file_id_both_directory_info *fibdinfo; - - fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; - memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); - fibdinfo->FileName[d_info->name_len] = 0x00; - fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case SMB_FIND_FILE_POSIX_INFO: - { - struct smb2_posix_info *posix_info; - - posix_info = (struct smb2_posix_info *)d_info->wptr; - memcpy(posix_info->name, d_info->name, d_info->name_len); - posix_info->name[d_info->name_len] = 0x00; - posix_info->name_len = cpu_to_le32(d_info->name_len); - posix_info->NextEntryOffset = - cpu_to_le32(next_entry_offset); - break; - } - } /* switch (info_level) */ - - d_info->num_entry++; - d_info->out_buf_len -= next_entry_offset; - d_info->wptr += next_entry_offset; - return 0; -} - -static bool __query_dir(struct dir_context *ctx, const char *name, int namlen, - loff_t offset, u64 ino, unsigned int d_type) -{ - struct ksmbd_readdir_data *buf; - struct smb2_query_dir_private *priv; - struct ksmbd_dir_info *d_info; - int rc; - - buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - priv = buf->private; - d_info = priv->d_info; - - /* dot and dotdot entries are already reserved */ - if (!strcmp(".", name) || !strcmp("..", name)) - return true; - if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) - return true; - if (!match_pattern(name, namlen, priv->search_pattern)) - return true; - - d_info->name = name; - d_info->name_len = namlen; - rc = reserve_populate_dentry(d_info, priv->info_level); - if (rc) - return false; - if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) - d_info->out_buf_len = 0; - return true; -} - -static int verify_info_level(int info_level) -{ - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - case FILE_BOTH_DIRECTORY_INFORMATION: - case FILE_DIRECTORY_INFORMATION: - case FILE_NAMES_INFORMATION: - case FILEID_FULL_DIRECTORY_INFORMATION: - case FILEID_BOTH_DIRECTORY_INFORMATION: - case SMB_FIND_FILE_POSIX_INFO: - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len) -{ - int free_len; - - free_len = (int)(work->response_sz - - (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len; - return free_len; -} - -static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, - unsigned short hdr2_len, - unsigned int out_buf_len) -{ - int free_len; - - if (out_buf_len > work->conn->vals->max_trans_size) - return -EINVAL; - - free_len = smb2_resp_buf_len(work, hdr2_len); - if (free_len < 0) - return -EINVAL; - - return min_t(int, out_buf_len, free_len); -} - -int smb2_query_dir(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_query_directory_req *req; - struct smb2_query_directory_rsp *rsp; - struct ksmbd_share_config *share = work->tcon->share_conf; - struct ksmbd_file *dir_fp = NULL; - struct ksmbd_dir_info d_info; - int rc = 0; - char *srch_ptr = NULL; - unsigned char srch_flag; - int buffer_sz; - struct smb2_query_dir_private query_dir_private = {NULL, }; - - WORK_BUFFERS(work, req, rsp); - - if (ksmbd_override_fsids(work)) { - rsp->hdr.Status = STATUS_NO_MEMORY; - smb2_set_err_rsp(work); - return -ENOMEM; - } - - rc = verify_info_level(req->FileInformationClass); - if (rc) { - rc = -EFAULT; - goto err_out2; - } - - dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!dir_fp) { - rc = -EBADF; - goto err_out2; - } - - if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || - inode_permission(file_mnt_user_ns(dir_fp->filp), - file_inode(dir_fp->filp), - MAY_READ | MAY_EXEC)) { - pr_err("no right to enumerate directory (%pD)\n", dir_fp->filp); - rc = -EACCES; - goto err_out2; - } - - if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { - pr_err("can't do query dir for a file\n"); - rc = -EINVAL; - goto err_out2; - } - - srch_flag = req->Flags; - srch_ptr = smb_strndup_from_utf16(req->Buffer, - le16_to_cpu(req->FileNameLength), 1, - conn->local_nls); - if (IS_ERR(srch_ptr)) { - ksmbd_debug(SMB, "Search Pattern not found\n"); - rc = -EINVAL; - goto err_out2; - } else { - ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); - } - - if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { - ksmbd_debug(SMB, "Restart directory scan\n"); - generic_file_llseek(dir_fp->filp, 0, SEEK_SET); - } - - memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); - d_info.wptr = (char *)rsp->Buffer; - d_info.rptr = (char *)rsp->Buffer; - d_info.out_buf_len = - smb2_calc_max_out_buf_len(work, 8, - le32_to_cpu(req->OutputBufferLength)); - if (d_info.out_buf_len < 0) { - rc = -EINVAL; - goto err_out; - } - d_info.flags = srch_flag; - - /* - * reserve dot and dotdot entries in head of buffer - * in first response - */ - rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, - dir_fp, &d_info, srch_ptr, - smb2_populate_readdir_entry); - if (rc == -ENOSPC) - rc = 0; - else if (rc) - goto err_out; - - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) - d_info.hide_dot_file = true; - - buffer_sz = d_info.out_buf_len; - d_info.rptr = d_info.wptr; - query_dir_private.work = work; - query_dir_private.search_pattern = srch_ptr; - query_dir_private.dir_fp = dir_fp; - query_dir_private.d_info = &d_info; - query_dir_private.info_level = req->FileInformationClass; - dir_fp->readdir_data.private = &query_dir_private; - set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); - - rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); - /* - * req->OutputBufferLength is too small to contain even one entry. - * In this case, it immediately returns OutputBufferLength 0 to client. - */ - if (!d_info.out_buf_len && !d_info.num_entry) - goto no_buf_len; - if (rc > 0 || rc == -ENOSPC) - rc = 0; - else if (rc) - goto err_out; - - d_info.wptr = d_info.rptr; - d_info.out_buf_len = buffer_sz; - rc = process_query_dir_entries(&query_dir_private); - if (rc) - goto err_out; - - if (!d_info.data_count && d_info.out_buf_len >= 0) { - if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { - rsp->hdr.Status = STATUS_NO_SUCH_FILE; - } else { - dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; - rsp->hdr.Status = STATUS_NO_MORE_FILES; - } - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(0); - rsp->OutputBufferLength = cpu_to_le32(0); - rsp->Buffer[0] = 0; - inc_rfc1001_len(work->response_buf, 9); - } else { -no_buf_len: - ((struct file_directory_info *) - ((char *)rsp->Buffer + d_info.last_entry_offset)) - ->NextEntryOffset = 0; - if (d_info.data_count >= d_info.last_entry_off_align) - d_info.data_count -= d_info.last_entry_off_align; - - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(72); - rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); - inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); - } - - kfree(srch_ptr); - ksmbd_fd_put(work, dir_fp); - ksmbd_revert_fsids(work); - return 0; - -err_out: - pr_err("error while processing smb2 query dir rc = %d\n", rc); - kfree(srch_ptr); - -err_out2: - if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_NO_SUCH_FILE; - else if (rc == -EBADF) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (rc == -ENOMEM) - rsp->hdr.Status = STATUS_NO_MEMORY; - else if (rc == -EFAULT) - rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; - else if (rc == -EIO) - rsp->hdr.Status = STATUS_FILE_CORRUPT_ERROR; - if (!rsp->hdr.Status) - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - - smb2_set_err_rsp(work); - ksmbd_fd_put(work, dir_fp); - ksmbd_revert_fsids(work); - return 0; -} - -/** - * buffer_check_err() - helper function to check buffer errors - * @reqOutputBufferLength: max buffer length expected in command response - * @rsp: query info response buffer contains output buffer length - * @rsp_org: base response buffer pointer in case of chained response - * @infoclass_size: query info class response buffer size - * - * Return: 0 on success, otherwise error - */ -static int buffer_check_err(int reqOutputBufferLength, - struct smb2_query_info_rsp *rsp, - void *rsp_org, int infoclass_size) -{ - if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { - if (reqOutputBufferLength < infoclass_size) { - pr_err("Invalid Buffer Size Requested\n"); - rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; - *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); - return -EINVAL; - } - - ksmbd_debug(SMB, "Buffer Overflow\n"); - rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + - reqOutputBufferLength); - rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); - } - return 0; -} - -static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, - void *rsp_org) -{ - struct smb2_file_standard_info *sinfo; - - sinfo = (struct smb2_file_standard_info *)rsp->Buffer; - - sinfo->AllocationSize = cpu_to_le64(4096); - sinfo->EndOfFile = cpu_to_le64(0); - sinfo->NumberOfLinks = cpu_to_le32(1); - sinfo->DeletePending = 1; - sinfo->Directory = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_standard_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); -} - -static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, - void *rsp_org) -{ - struct smb2_file_internal_info *file_info; - - file_info = (struct smb2_file_internal_info *)rsp->Buffer; - - /* any unique number */ - file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_internal_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); -} - -static int smb2_get_info_file_pipe(struct ksmbd_session *sess, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, - void *rsp_org) -{ - u64 id; - int rc; - - /* - * Windows can sometime send query file info request on - * pipe without opening it, checking error condition here - */ - id = req->VolatileFileId; - if (!ksmbd_session_rpc_method(sess, id)) - return -ENOENT; - - ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", - req->FileInfoClass, req->VolatileFileId); - - switch (req->FileInfoClass) { - case FILE_STANDARD_INFORMATION: - get_standard_info_pipe(rsp, rsp_org); - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, rsp_org, - FILE_STANDARD_INFORMATION_SIZE); - break; - case FILE_INTERNAL_INFORMATION: - get_internal_info_pipe(rsp, id, rsp_org); - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, rsp_org, - FILE_INTERNAL_INFORMATION_SIZE); - break; - default: - ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", - req->FileInfoClass); - rc = -EOPNOTSUPP; - } - return rc; -} - -/** - * smb2_get_ea() - handler for smb2 get extended attribute command - * @work: smb work containing query info command buffer - * @fp: ksmbd_file pointer - * @req: get extended attribute request - * @rsp: response buffer pointer - * @rsp_org: base response buffer pointer in case of chained response - * - * Return: 0 on success, otherwise error - */ -static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) -{ - struct smb2_ea_info *eainfo, *prev_eainfo; - char *name, *ptr, *xattr_list = NULL, *buf; - int rc, name_len, value_len, xattr_list_len, idx; - ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; - struct smb2_ea_info_req *ea_req = NULL; - const struct path *path; - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - - if (!(fp->daccess & FILE_READ_EA_LE)) { - pr_err("Not permitted to read ext attr : 0x%x\n", - fp->daccess); - return -EACCES; - } - - path = &fp->filp->f_path; - /* single EA entry is requested with given user.* name */ - if (req->InputBufferLength) { - if (le32_to_cpu(req->InputBufferLength) < - sizeof(struct smb2_ea_info_req)) - return -EINVAL; - - ea_req = (struct smb2_ea_info_req *)req->Buffer; - } else { - /* need to send all EAs, if no specific EA is requested*/ - if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) - ksmbd_debug(SMB, - "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", - le32_to_cpu(req->Flags)); - } - - buf_free_len = - smb2_calc_max_out_buf_len(work, 8, - le32_to_cpu(req->OutputBufferLength)); - if (buf_free_len < 0) - return -EINVAL; - - rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); - if (rc < 0) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - goto out; - } else if (!rc) { /* there is no EA in the file */ - ksmbd_debug(SMB, "no ea data in the file\n"); - goto done; - } - xattr_list_len = rc; - - ptr = (char *)rsp->Buffer; - eainfo = (struct smb2_ea_info *)ptr; - prev_eainfo = eainfo; - idx = 0; - - while (idx < xattr_list_len) { - name = xattr_list + idx; - name_len = strlen(name); - - ksmbd_debug(SMB, "%s, len %d\n", name, name_len); - idx += name_len + 1; - - /* - * CIFS does not support EA other than user.* namespace, - * still keep the framework generic, to list other attrs - * in future. - */ - if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - continue; - - if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, - STREAM_PREFIX_LEN)) - continue; - - if (req->InputBufferLength && - strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, - ea_req->EaNameLength)) - continue; - - if (!strncmp(&name[XATTR_USER_PREFIX_LEN], - DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) - continue; - - if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - name_len -= XATTR_USER_PREFIX_LEN; - - ptr = (char *)(&eainfo->name + name_len + 1); - buf_free_len -= (offsetof(struct smb2_ea_info, name) + - name_len + 1); - /* bailout if xattr can't fit in buf_free_len */ - value_len = ksmbd_vfs_getxattr(user_ns, path->dentry, - name, &buf); - if (value_len <= 0) { - rc = -ENOENT; - rsp->hdr.Status = STATUS_INVALID_HANDLE; - goto out; - } - - buf_free_len -= value_len; - if (buf_free_len < 0) { - kfree(buf); - break; - } - - memcpy(ptr, buf, value_len); - kfree(buf); - - ptr += value_len; - eainfo->Flags = 0; - eainfo->EaNameLength = name_len; - - if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], - name_len); - else - memcpy(eainfo->name, name, name_len); - - eainfo->name[name_len] = '\0'; - eainfo->EaValueLength = cpu_to_le16(value_len); - next_offset = offsetof(struct smb2_ea_info, name) + - name_len + 1 + value_len; - - /* align next xattr entry at 4 byte bundary */ - alignment_bytes = ((next_offset + 3) & ~3) - next_offset; - if (alignment_bytes) { - memset(ptr, '\0', alignment_bytes); - ptr += alignment_bytes; - next_offset += alignment_bytes; - buf_free_len -= alignment_bytes; - } - eainfo->NextEntryOffset = cpu_to_le32(next_offset); - prev_eainfo = eainfo; - eainfo = (struct smb2_ea_info *)ptr; - rsp_data_cnt += next_offset; - - if (req->InputBufferLength) { - ksmbd_debug(SMB, "single entry requested\n"); - break; - } - } - - /* no more ea entries */ - prev_eainfo->NextEntryOffset = 0; -done: - rc = 0; - if (rsp_data_cnt == 0) - rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; - rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); - inc_rfc1001_len(rsp_org, rsp_data_cnt); -out: - kvfree(xattr_list); - return rc; -} - -static void get_file_access_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_access_info *file_info; - - file_info = (struct smb2_file_access_info *)rsp->Buffer; - file_info->AccessFlags = fp->daccess; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_access_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); -} - -static int get_file_basic_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_basic_info *basic_info; - struct kstat stat; - u64 time; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - pr_err("no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - basic_info = (struct smb2_file_basic_info *)rsp->Buffer; - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - basic_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - basic_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - basic_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - basic_info->ChangeTime = cpu_to_le64(time); - basic_info->Attributes = fp->f_ci->m_fattr; - basic_info->Pad1 = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_basic_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); - return 0; -} - -static void get_file_standard_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_standard_info *sinfo; - unsigned int delete_pending; - struct inode *inode; - struct kstat stat; - - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); - - sinfo = (struct smb2_file_standard_info *)rsp->Buffer; - delete_pending = ksmbd_inode_pending_delete(fp); - - sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9); - sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); - sinfo->DeletePending = delete_pending; - sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_standard_info)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb2_file_standard_info)); -} - -static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, - void *rsp_org) -{ - struct smb2_file_alignment_info *file_info; - - file_info = (struct smb2_file_alignment_info *)rsp->Buffer; - file_info->AlignmentRequirement = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_alignment_info)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb2_file_alignment_info)); -} - -static int get_file_all_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_file_all_info *file_info; - unsigned int delete_pending; - struct inode *inode; - struct kstat stat; - int conv_len; - char *filename; - u64 time; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - filename = convert_to_nt_pathname(work->tcon->share_conf, &fp->filp->f_path); - if (IS_ERR(filename)) - return PTR_ERR(filename); - - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); - - ksmbd_debug(SMB, "filename = %s\n", filename); - delete_pending = ksmbd_inode_pending_delete(fp); - file_info = (struct smb2_file_all_info *)rsp->Buffer; - - file_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - file_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); - file_info->Attributes = fp->f_ci->m_fattr; - file_info->Pad1 = 0; - file_info->AllocationSize = - cpu_to_le64(inode->i_blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - file_info->NumberOfLinks = - cpu_to_le32(get_nlink(&stat) - delete_pending); - file_info->DeletePending = delete_pending; - file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; - file_info->Pad2 = 0; - file_info->IndexNumber = cpu_to_le64(stat.ino); - file_info->EASize = 0; - file_info->AccessFlags = fp->daccess; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); - file_info->Mode = fp->coption; - file_info->AlignmentRequirement = 0; - conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, - PATH_MAX, conn->local_nls, 0); - conv_len *= 2; - file_info->FileNameLength = cpu_to_le32(conv_len); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); - kfree(filename); - inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); - return 0; -} - -static void get_file_alternate_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_file_alt_name_info *file_info; - struct dentry *dentry = fp->filp->f_path.dentry; - int conv_len; - - spin_lock(&dentry->d_lock); - file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; - conv_len = ksmbd_extract_shortname(conn, - dentry->d_name.name, - file_info->FileName); - spin_unlock(&dentry->d_lock); - file_info->FileNameLength = cpu_to_le32(conv_len); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); - inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); -} - -static void get_file_stream_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_file_stream_info *file_info; - char *stream_name, *xattr_list = NULL, *stream_buf; - struct kstat stat; - const struct path *path = &fp->filp->f_path; - ssize_t xattr_list_len; - int nbytes = 0, streamlen, stream_name_len, next, idx = 0; - int buf_free_len; - struct smb2_query_info_req *req = ksmbd_req_buf_next(work); - - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - file_info = (struct smb2_file_stream_info *)rsp->Buffer; - - buf_free_len = - smb2_calc_max_out_buf_len(work, 8, - le32_to_cpu(req->OutputBufferLength)); - if (buf_free_len < 0) - goto out; - - xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - while (idx < xattr_list_len) { - stream_name = xattr_list + idx; - streamlen = strlen(stream_name); - idx += streamlen + 1; - - ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); - - if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], - STREAM_PREFIX, STREAM_PREFIX_LEN)) - continue; - - stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + - STREAM_PREFIX_LEN); - streamlen = stream_name_len; - - /* plus : size */ - streamlen += 1; - stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); - if (!stream_buf) - break; - - streamlen = snprintf(stream_buf, streamlen + 1, - ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); - - next = sizeof(struct smb2_file_stream_info) + streamlen * 2; - if (next > buf_free_len) { - kfree(stream_buf); - break; - } - - file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; - streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, - stream_buf, streamlen, - conn->local_nls, 0); - streamlen *= 2; - kfree(stream_buf); - file_info->StreamNameLength = cpu_to_le32(streamlen); - file_info->StreamSize = cpu_to_le64(stream_name_len); - file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); - - nbytes += next; - buf_free_len -= next; - file_info->NextEntryOffset = cpu_to_le32(next); - } - -out: - if (!S_ISDIR(stat.mode) && - buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { - file_info = (struct smb2_file_stream_info *) - &rsp->Buffer[nbytes]; - streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, - "::$DATA", 7, conn->local_nls, 0); - streamlen *= 2; - file_info->StreamNameLength = cpu_to_le32(streamlen); - file_info->StreamSize = cpu_to_le64(stat.size); - file_info->StreamAllocationSize = cpu_to_le64(stat.blocks << 9); - nbytes += sizeof(struct smb2_file_stream_info) + streamlen; - } - - /* last entry offset should be 0 */ - file_info->NextEntryOffset = 0; - kvfree(xattr_list); - - rsp->OutputBufferLength = cpu_to_le32(nbytes); - inc_rfc1001_len(rsp_org, nbytes); -} - -static void get_file_internal_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_internal_info *file_info; - struct kstat stat; - - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - file_info = (struct smb2_file_internal_info *)rsp->Buffer; - file_info->IndexNumber = cpu_to_le64(stat.ino); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_internal_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); -} - -static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_ntwrk_info *file_info; - struct inode *inode; - struct kstat stat; - u64 time; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - pr_err("no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; - - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); - - file_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - file_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); - file_info->Attributes = fp->f_ci->m_fattr; - file_info->AllocationSize = - cpu_to_le64(inode->i_blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - file_info->Reserved = cpu_to_le32(0); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); - return 0; -} - -static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) -{ - struct smb2_file_ea_info *file_info; - - file_info = (struct smb2_file_ea_info *)rsp->Buffer; - file_info->EASize = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_ea_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); -} - -static void get_file_position_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_pos_info *file_info; - - file_info = (struct smb2_file_pos_info *)rsp->Buffer; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_pos_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); -} - -static void get_file_mode_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_mode_info *file_info; - - file_info = (struct smb2_file_mode_info *)rsp->Buffer; - file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_mode_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); -} - -static void get_file_compression_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_comp_info *file_info; - struct kstat stat; - - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - - file_info = (struct smb2_file_comp_info *)rsp->Buffer; - file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); - file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; - file_info->CompressionUnitShift = 0; - file_info->ChunkShift = 0; - file_info->ClusterShift = 0; - memset(&file_info->Reserved[0], 0, 3); - - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_comp_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); -} - -static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_attr_tag_info *file_info; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - pr_err("no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; - file_info->FileAttributes = fp->f_ci->m_fattr; - file_info->ReparseTag = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); - return 0; -} - -static int find_file_posix_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb311_posix_qinfo *file_info; - struct inode *inode = file_inode(fp->filp); - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); - vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); - u64 time; - int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; - - file_info = (struct smb311_posix_qinfo *)rsp->Buffer; - file_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(inode->i_atime); - file_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_mtime); - file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_ctime); - file_info->ChangeTime = cpu_to_le64(time); - file_info->DosAttributes = fp->f_ci->m_fattr; - file_info->Inode = cpu_to_le64(inode->i_ino); - file_info->EndOfFile = cpu_to_le64(inode->i_size); - file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); - file_info->HardLinks = cpu_to_le32(inode->i_nlink); - file_info->Mode = cpu_to_le32(inode->i_mode & 0777); - file_info->DeviceId = cpu_to_le32(inode->i_rdev); - - /* - * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)). - * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 1(num_subauth)) + RID(4). - */ - id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), - SIDUNIX_USER, (struct smb_sid *)&file_info->Sids[0]); - id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), - SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); - - rsp->OutputBufferLength = cpu_to_le32(out_buf_len); - inc_rfc1001_len(rsp_org, out_buf_len); - return out_buf_len; -} - -static int smb2_get_info_file(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) -{ - struct ksmbd_file *fp; - int fileinfoclass = 0; - int rc = 0; - int file_infoclass_size; - unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { - /* smb2 info file called for pipe */ - return smb2_get_info_file_pipe(work->sess, req, rsp, - work->response_buf); - } - - if (work->next_smb2_rcv_hdr_off) { - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - pid = work->compound_pfid; - } - } - - if (!has_file_id(id)) { - id = req->VolatileFileId; - pid = req->PersistentFileId; - } - - fp = ksmbd_lookup_fd_slow(work, id, pid); - if (!fp) - return -ENOENT; - - fileinfoclass = req->FileInfoClass; - - switch (fileinfoclass) { - case FILE_ACCESS_INFORMATION: - get_file_access_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; - break; - - case FILE_BASIC_INFORMATION: - rc = get_file_basic_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; - break; - - case FILE_STANDARD_INFORMATION: - get_file_standard_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; - break; - - case FILE_ALIGNMENT_INFORMATION: - get_file_alignment_info(rsp, work->response_buf); - file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; - break; - - case FILE_ALL_INFORMATION: - rc = get_file_all_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_ALL_INFORMATION_SIZE; - break; - - case FILE_ALTERNATE_NAME_INFORMATION: - get_file_alternate_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; - break; - - case FILE_STREAM_INFORMATION: - get_file_stream_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; - break; - - case FILE_INTERNAL_INFORMATION: - get_file_internal_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; - break; - - case FILE_NETWORK_OPEN_INFORMATION: - rc = get_file_network_open_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; - break; - - case FILE_EA_INFORMATION: - get_file_ea_info(rsp, work->response_buf); - file_infoclass_size = FILE_EA_INFORMATION_SIZE; - break; - - case FILE_FULL_EA_INFORMATION: - rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); - file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; - break; - - case FILE_POSITION_INFORMATION: - get_file_position_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; - break; - - case FILE_MODE_INFORMATION: - get_file_mode_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_MODE_INFORMATION_SIZE; - break; - - case FILE_COMPRESSION_INFORMATION: - get_file_compression_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; - break; - - case FILE_ATTRIBUTE_TAG_INFORMATION: - rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; - break; - case SMB_FIND_FILE_POSIX_INFO: - if (!work->tcon->posix_extensions) { - pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); - rc = -EOPNOTSUPP; - } else { - file_infoclass_size = find_file_posix_info(rsp, fp, - work->response_buf); - } - break; - default: - ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", - fileinfoclass); - rc = -EOPNOTSUPP; - } - if (!rc) - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, work->response_buf, - file_infoclass_size); - ksmbd_fd_put(work, fp); - return rc; -} - -static int smb2_get_info_filesystem(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) -{ - struct ksmbd_session *sess = work->sess; - struct ksmbd_conn *conn = work->conn; - struct ksmbd_share_config *share = work->tcon->share_conf; - int fsinfoclass = 0; - struct kstatfs stfs; - struct path path; - int rc = 0, len; - int fs_infoclass_size = 0; - - if (!share->path) - return -EIO; - - rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); - if (rc) { - pr_err("cannot create vfs path\n"); - return -EIO; - } - - rc = vfs_statfs(&path, &stfs); - if (rc) { - pr_err("cannot do stat of path %s\n", share->path); - path_put(&path); - return -EIO; - } - - fsinfoclass = req->FileInfoClass; - - switch (fsinfoclass) { - case FS_DEVICE_INFORMATION: - { - struct filesystem_device_info *info; - - info = (struct filesystem_device_info *)rsp->Buffer; - - info->DeviceType = cpu_to_le32(stfs.f_type); - info->DeviceCharacteristics = cpu_to_le32(0x00000020); - rsp->OutputBufferLength = cpu_to_le32(8); - inc_rfc1001_len(work->response_buf, 8); - fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; - break; - } - case FS_ATTRIBUTE_INFORMATION: - { - struct filesystem_attribute_info *info; - size_t sz; - - info = (struct filesystem_attribute_info *)rsp->Buffer; - info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | - FILE_PERSISTENT_ACLS | - FILE_UNICODE_ON_DISK | - FILE_CASE_PRESERVED_NAMES | - FILE_CASE_SENSITIVE_SEARCH | - FILE_SUPPORTS_BLOCK_REFCOUNTING); - - info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS)) - info->Attributes |= cpu_to_le32(FILE_NAMED_STREAMS); - - info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); - len = smbConvertToUTF16((__le16 *)info->FileSystemName, - "NTFS", PATH_MAX, conn->local_nls, 0); - len = len * 2; - info->FileSystemNameLen = cpu_to_le32(len); - sz = sizeof(struct filesystem_attribute_info) - 2 + len; - rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(work->response_buf, sz); - fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; - break; - } - case FS_VOLUME_INFORMATION: - { - struct filesystem_vol_info *info; - size_t sz; - unsigned int serial_crc = 0; - - info = (struct filesystem_vol_info *)(rsp->Buffer); - info->VolumeCreationTime = 0; - serial_crc = crc32_le(serial_crc, share->name, - strlen(share->name)); - serial_crc = crc32_le(serial_crc, share->path, - strlen(share->path)); - serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), - strlen(ksmbd_netbios_name())); - /* Taking dummy value of serial number*/ - info->SerialNumber = cpu_to_le32(serial_crc); - len = smbConvertToUTF16((__le16 *)info->VolumeLabel, - share->name, PATH_MAX, - conn->local_nls, 0); - len = len * 2; - info->VolumeLabelSize = cpu_to_le32(len); - info->Reserved = 0; - sz = sizeof(struct filesystem_vol_info) - 2 + len; - rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(work->response_buf, sz); - fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; - break; - } - case FS_SIZE_INFORMATION: - { - struct filesystem_info *info; - - info = (struct filesystem_info *)(rsp->Buffer); - info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); - info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); - info->SectorsPerAllocationUnit = cpu_to_le32(1); - info->BytesPerSector = cpu_to_le32(stfs.f_bsize); - rsp->OutputBufferLength = cpu_to_le32(24); - inc_rfc1001_len(work->response_buf, 24); - fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; - break; - } - case FS_FULL_SIZE_INFORMATION: - { - struct smb2_fs_full_size_info *info; - - info = (struct smb2_fs_full_size_info *)(rsp->Buffer); - info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); - info->CallerAvailableAllocationUnits = - cpu_to_le64(stfs.f_bavail); - info->ActualAvailableAllocationUnits = - cpu_to_le64(stfs.f_bfree); - info->SectorsPerAllocationUnit = cpu_to_le32(1); - info->BytesPerSector = cpu_to_le32(stfs.f_bsize); - rsp->OutputBufferLength = cpu_to_le32(32); - inc_rfc1001_len(work->response_buf, 32); - fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; - break; - } - case FS_OBJECT_ID_INFORMATION: - { - struct object_id_info *info; - - info = (struct object_id_info *)(rsp->Buffer); - - if (!user_guest(sess->user)) - memcpy(info->objid, user_passkey(sess->user), 16); - else - memset(info->objid, 0, 16); - - info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); - info->extended_info.version = cpu_to_le32(1); - info->extended_info.release = cpu_to_le32(1); - info->extended_info.rel_date = 0; - memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); - rsp->OutputBufferLength = cpu_to_le32(64); - inc_rfc1001_len(work->response_buf, 64); - fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; - break; - } - case FS_SECTOR_SIZE_INFORMATION: - { - struct smb3_fs_ss_info *info; - unsigned int sector_size = - min_t(unsigned int, path.mnt->mnt_sb->s_blocksize, 4096); - - info = (struct smb3_fs_ss_info *)(rsp->Buffer); - - info->LogicalBytesPerSector = cpu_to_le32(sector_size); - info->PhysicalBytesPerSectorForAtomicity = - cpu_to_le32(sector_size); - info->PhysicalBytesPerSectorForPerf = cpu_to_le32(sector_size); - info->FSEffPhysicalBytesPerSectorForAtomicity = - cpu_to_le32(sector_size); - info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | - SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); - info->ByteOffsetForSectorAlignment = 0; - info->ByteOffsetForPartitionAlignment = 0; - rsp->OutputBufferLength = cpu_to_le32(28); - inc_rfc1001_len(work->response_buf, 28); - fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; - break; - } - case FS_CONTROL_INFORMATION: - { - /* - * TODO : The current implementation is based on - * test result with win7(NTFS) server. It's need to - * modify this to get valid Quota values - * from Linux kernel - */ - struct smb2_fs_control_info *info; - - info = (struct smb2_fs_control_info *)(rsp->Buffer); - info->FreeSpaceStartFiltering = 0; - info->FreeSpaceThreshold = 0; - info->FreeSpaceStopFiltering = 0; - info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); - info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); - info->Padding = 0; - rsp->OutputBufferLength = cpu_to_le32(48); - inc_rfc1001_len(work->response_buf, 48); - fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; - break; - } - case FS_POSIX_INFORMATION: - { - struct filesystem_posix_info *info; - - if (!work->tcon->posix_extensions) { - pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); - rc = -EOPNOTSUPP; - } else { - info = (struct filesystem_posix_info *)(rsp->Buffer); - info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); - info->BlockSize = cpu_to_le32(stfs.f_bsize); - info->TotalBlocks = cpu_to_le64(stfs.f_blocks); - info->BlocksAvail = cpu_to_le64(stfs.f_bfree); - info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); - info->TotalFileNodes = cpu_to_le64(stfs.f_files); - info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); - rsp->OutputBufferLength = cpu_to_le32(56); - inc_rfc1001_len(work->response_buf, 56); - fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; - } - break; - } - default: - path_put(&path); - return -EOPNOTSUPP; - } - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, work->response_buf, - fs_infoclass_size); - path_put(&path); - return rc; -} - -static int smb2_get_info_sec(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) -{ - struct ksmbd_file *fp; - struct user_namespace *user_ns; - struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; - struct smb_fattr fattr = {{0}}; - struct inode *inode; - __u32 secdesclen = 0; - unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - int addition_info = le32_to_cpu(req->AdditionalInformation); - int rc = 0, ppntsd_size = 0; - - if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | - PROTECTED_DACL_SECINFO | - UNPROTECTED_DACL_SECINFO)) { - ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", - addition_info); - - pntsd->revision = cpu_to_le16(1); - pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); - pntsd->osidoffset = 0; - pntsd->gsidoffset = 0; - pntsd->sacloffset = 0; - pntsd->dacloffset = 0; - - secdesclen = sizeof(struct smb_ntsd); - rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(work->response_buf, secdesclen); - - return 0; - } - - if (work->next_smb2_rcv_hdr_off) { - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - pid = work->compound_pfid; - } - } - - if (!has_file_id(id)) { - id = req->VolatileFileId; - pid = req->PersistentFileId; - } - - fp = ksmbd_lookup_fd_slow(work, id, pid); - if (!fp) - return -ENOENT; - - user_ns = file_mnt_user_ns(fp->filp); - inode = file_inode(fp->filp); - ksmbd_acls_fattr(&fattr, user_ns, inode); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) - ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns, - fp->filp->f_path.dentry, - &ppntsd); - - /* Check if sd buffer size exceeds response buffer size */ - if (smb2_resp_buf_len(work, 8) > ppntsd_size) - rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size, - addition_info, &secdesclen, &fattr); - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - kfree(ppntsd); - ksmbd_fd_put(work, fp); - if (rc) - return rc; - - rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(work->response_buf, secdesclen); - return 0; -} - -/** - * smb2_query_info() - handler for smb2 query info command - * @work: smb work containing query info request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_query_info(struct ksmbd_work *work) -{ - struct smb2_query_info_req *req; - struct smb2_query_info_rsp *rsp; - int rc = 0; - - WORK_BUFFERS(work, req, rsp); - - ksmbd_debug(SMB, "GOT query info request\n"); - - switch (req->InfoType) { - case SMB2_O_INFO_FILE: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); - rc = smb2_get_info_file(work, req, rsp); - break; - case SMB2_O_INFO_FILESYSTEM: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); - rc = smb2_get_info_filesystem(work, req, rsp); - break; - case SMB2_O_INFO_SECURITY: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - rc = smb2_get_info_sec(work, req, rsp); - break; - default: - ksmbd_debug(SMB, "InfoType %d not supported yet\n", - req->InfoType); - rc = -EOPNOTSUPP; - } - - if (rc < 0) { - if (rc == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (rc == -EIO) - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) - rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; - smb2_set_err_rsp(work); - - ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", - rc); - return rc; - } - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(72); - inc_rfc1001_len(work->response_buf, 8); - return 0; -} - -/** - * smb2_close_pipe() - handler for closing IPC pipe - * @work: smb work containing close request buffer - * - * Return: 0 - */ -static noinline int smb2_close_pipe(struct ksmbd_work *work) -{ - u64 id; - struct smb2_close_req *req = smb2_get_msg(work->request_buf); - struct smb2_close_rsp *rsp = smb2_get_msg(work->response_buf); - - id = req->VolatileFileId; - ksmbd_session_rpc_close(work->sess, id); - - rsp->StructureSize = cpu_to_le16(60); - rsp->Flags = 0; - rsp->Reserved = 0; - rsp->CreationTime = 0; - rsp->LastAccessTime = 0; - rsp->LastWriteTime = 0; - rsp->ChangeTime = 0; - rsp->AllocationSize = 0; - rsp->EndOfFile = 0; - rsp->Attributes = 0; - inc_rfc1001_len(work->response_buf, 60); - return 0; -} - -/** - * smb2_close() - handler for smb2 close file command - * @work: smb work containing close request buffer - * - * Return: 0 - */ -int smb2_close(struct ksmbd_work *work) -{ - u64 volatile_id = KSMBD_NO_FID; - u64 sess_id; - struct smb2_close_req *req; - struct smb2_close_rsp *rsp; - struct ksmbd_conn *conn = work->conn; - struct ksmbd_file *fp; - struct inode *inode; - u64 time; - int err = 0; - - WORK_BUFFERS(work, req, rsp); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe close request\n"); - return smb2_close_pipe(work); - } - - sess_id = le64_to_cpu(req->hdr.SessionId); - if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) - sess_id = work->compound_sid; - - work->compound_sid = 0; - if (check_session_id(conn, sess_id)) { - work->compound_sid = sess_id; - } else { - rsp->hdr.Status = STATUS_USER_SESSION_DELETED; - if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - err = -EBADF; - goto out; - } - - if (work->next_smb2_rcv_hdr_off && - !has_file_id(req->VolatileFileId)) { - if (!has_file_id(work->compound_fid)) { - /* file already closed, return FILE_CLOSED */ - ksmbd_debug(SMB, "file already closed\n"); - rsp->hdr.Status = STATUS_FILE_CLOSED; - err = -EBADF; - goto out; - } else { - ksmbd_debug(SMB, - "Compound request set FID = %llu:%llu\n", - work->compound_fid, - work->compound_pfid); - volatile_id = work->compound_fid; - - /* file closed, stored id is not valid anymore */ - work->compound_fid = KSMBD_NO_FID; - work->compound_pfid = KSMBD_NO_FID; - } - } else { - volatile_id = req->VolatileFileId; - } - ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); - - rsp->StructureSize = cpu_to_le16(60); - rsp->Reserved = 0; - - if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { - fp = ksmbd_lookup_fd_fast(work, volatile_id); - if (!fp) { - err = -ENOENT; - goto out; - } - - inode = file_inode(fp->filp); - rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : - cpu_to_le64(inode->i_blocks << 9); - rsp->EndOfFile = cpu_to_le64(inode->i_size); - rsp->Attributes = fp->f_ci->m_fattr; - rsp->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(inode->i_atime); - rsp->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_mtime); - rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_ctime); - rsp->ChangeTime = cpu_to_le64(time); - ksmbd_fd_put(work, fp); - } else { - rsp->Flags = 0; - rsp->AllocationSize = 0; - rsp->EndOfFile = 0; - rsp->Attributes = 0; - rsp->CreationTime = 0; - rsp->LastAccessTime = 0; - rsp->LastWriteTime = 0; - rsp->ChangeTime = 0; - } - - err = ksmbd_close_fd(work, volatile_id); -out: - if (err) { - if (rsp->hdr.Status == 0) - rsp->hdr.Status = STATUS_FILE_CLOSED; - smb2_set_err_rsp(work); - } else { - inc_rfc1001_len(work->response_buf, 60); - } - - return 0; -} - -/** - * smb2_echo() - handler for smb2 echo(ping) command - * @work: smb work containing echo request buffer - * - * Return: 0 - */ -int smb2_echo(struct ksmbd_work *work) -{ - struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); - - rsp->StructureSize = cpu_to_le16(4); - rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - return 0; -} - -static int smb2_rename(struct ksmbd_work *work, - struct ksmbd_file *fp, - struct user_namespace *user_ns, - struct smb2_file_rename_info *file_info, - struct nls_table *local_nls) -{ - struct ksmbd_share_config *share = fp->tcon->share_conf; - char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; - char *pathname = NULL; - struct path path; - bool file_present = true; - int rc; - - ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); - pathname = kmalloc(PATH_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; - - abs_oldname = file_path(fp->filp, pathname, PATH_MAX); - if (IS_ERR(abs_oldname)) { - rc = -EINVAL; - goto out; - } - old_name = strrchr(abs_oldname, '/'); - if (old_name && old_name[1] != '\0') { - old_name++; - } else { - ksmbd_debug(SMB, "can't get last component in path %s\n", - abs_oldname); - rc = -ENOENT; - goto out; - } - - new_name = smb2_get_name(file_info->FileName, - le32_to_cpu(file_info->FileNameLength), - local_nls); - if (IS_ERR(new_name)) { - rc = PTR_ERR(new_name); - goto out; - } - - if (strchr(new_name, ':')) { - int s_type; - char *xattr_stream_name, *stream_name = NULL; - size_t xattr_stream_size; - int len; - - rc = parse_stream_name(new_name, &stream_name, &s_type); - if (rc < 0) - goto out; - - len = strlen(new_name); - if (len > 0 && new_name[len - 1] != '/') { - pr_err("not allow base filename in rename\n"); - rc = -ESHARE; - goto out; - } - - rc = ksmbd_vfs_xattr_stream_name(stream_name, - &xattr_stream_name, - &xattr_stream_size, - s_type); - if (rc) - goto out; - - rc = ksmbd_vfs_setxattr(user_ns, - fp->filp->f_path.dentry, - xattr_stream_name, - NULL, 0, 0); - if (rc < 0) { - pr_err("failed to store stream name in xattr: %d\n", - rc); - rc = -EINVAL; - goto out; - } - - goto out; - } - - ksmbd_debug(SMB, "new name %s\n", new_name); - rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); - if (rc) { - if (rc != -ENOENT) - goto out; - file_present = false; - } else { - path_put(&path); - } - - if (ksmbd_share_veto_filename(share, new_name)) { - rc = -ENOENT; - ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); - goto out; - } - - if (file_info->ReplaceIfExists) { - if (file_present) { - rc = ksmbd_vfs_remove_file(work, new_name); - if (rc) { - if (rc != -ENOTEMPTY) - rc = -EINVAL; - ksmbd_debug(SMB, "cannot delete %s, rc %d\n", - new_name, rc); - goto out; - } - } - } else { - if (file_present && - strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { - rc = -EEXIST; - ksmbd_debug(SMB, - "cannot rename already existing file\n"); - goto out; - } - } - - rc = ksmbd_vfs_fp_rename(work, fp, new_name); -out: - kfree(pathname); - if (!IS_ERR(new_name)) - kfree(new_name); - return rc; -} - -static int smb2_create_link(struct ksmbd_work *work, - struct ksmbd_share_config *share, - struct smb2_file_link_info *file_info, - unsigned int buf_len, struct file *filp, - struct nls_table *local_nls) -{ - char *link_name = NULL, *target_name = NULL, *pathname = NULL; - struct path path; - bool file_present = true; - int rc; - - if (buf_len < (u64)sizeof(struct smb2_file_link_info) + - le32_to_cpu(file_info->FileNameLength)) - return -EINVAL; - - ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); - pathname = kmalloc(PATH_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; - - link_name = smb2_get_name(file_info->FileName, - le32_to_cpu(file_info->FileNameLength), - local_nls); - if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { - rc = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "link name is %s\n", link_name); - target_name = file_path(filp, pathname, PATH_MAX); - if (IS_ERR(target_name)) { - rc = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "target name is %s\n", target_name); - rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); - if (rc) { - if (rc != -ENOENT) - goto out; - file_present = false; - } else { - path_put(&path); - } - - if (file_info->ReplaceIfExists) { - if (file_present) { - rc = ksmbd_vfs_remove_file(work, link_name); - if (rc) { - rc = -EINVAL; - ksmbd_debug(SMB, "cannot delete %s\n", - link_name); - goto out; - } - } - } else { - if (file_present) { - rc = -EEXIST; - ksmbd_debug(SMB, "link already exists\n"); - goto out; - } - } - - rc = ksmbd_vfs_link(work, target_name, link_name); - if (rc) - rc = -EINVAL; -out: - if (!IS_ERR(link_name)) - kfree(link_name); - kfree(pathname); - return rc; -} - -static int set_file_basic_info(struct ksmbd_file *fp, - struct smb2_file_basic_info *file_info, - struct ksmbd_share_config *share) -{ - struct iattr attrs; - struct file *filp; - struct inode *inode; - struct user_namespace *user_ns; - int rc = 0; - - if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) - return -EACCES; - - attrs.ia_valid = 0; - filp = fp->filp; - inode = file_inode(filp); - user_ns = file_mnt_user_ns(filp); - - if (file_info->CreationTime) - fp->create_time = le64_to_cpu(file_info->CreationTime); - - if (file_info->LastAccessTime) { - attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); - attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); - } - - attrs.ia_valid |= ATTR_CTIME; - if (file_info->ChangeTime) - attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); - else - attrs.ia_ctime = inode->i_ctime; - - if (file_info->LastWriteTime) { - attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); - attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); - } - - if (file_info->Attributes) { - if (!S_ISDIR(inode->i_mode) && - file_info->Attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { - pr_err("can't change a file to a directory\n"); - return -EINVAL; - } - - if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == FILE_ATTRIBUTE_NORMAL_LE)) - fp->f_ci->m_fattr = file_info->Attributes | - (fp->f_ci->m_fattr & FILE_ATTRIBUTE_DIRECTORY_LE); - } - - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && - (file_info->CreationTime || file_info->Attributes)) { - struct xattr_dos_attrib da = {0}; - - da.version = 4; - da.itime = fp->itime; - da.create_time = fp->create_time; - da.attr = le32_to_cpu(fp->f_ci->m_fattr); - da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | - XATTR_DOSINFO_ITIME; - - rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, - filp->f_path.dentry, &da); - if (rc) - ksmbd_debug(SMB, - "failed to restore file attribute in EA\n"); - rc = 0; - } - - if (attrs.ia_valid) { - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = d_inode(dentry); - - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - return -EACCES; - - inode_lock(inode); - inode->i_ctime = attrs.ia_ctime; - attrs.ia_valid &= ~ATTR_CTIME; - rc = notify_change(user_ns, dentry, &attrs, NULL); - inode_unlock(inode); - } - return rc; -} - -static int set_file_allocation_info(struct ksmbd_work *work, - struct ksmbd_file *fp, - struct smb2_file_alloc_info *file_alloc_info) -{ - /* - * TODO : It's working fine only when store dos attributes - * is not yes. need to implement a logic which works - * properly with any smb.conf option - */ - - loff_t alloc_blks; - struct inode *inode; - int rc; - - if (!(fp->daccess & FILE_WRITE_DATA_LE)) - return -EACCES; - - alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; - inode = file_inode(fp->filp); - - if (alloc_blks > inode->i_blocks) { - smb_break_all_levII_oplock(work, fp, 1); - rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, - alloc_blks * 512); - if (rc && rc != -EOPNOTSUPP) { - pr_err("vfs_fallocate is failed : %d\n", rc); - return rc; - } - } else if (alloc_blks < inode->i_blocks) { - loff_t size; - - /* - * Allocation size could be smaller than original one - * which means allocated blocks in file should be - * deallocated. use truncate to cut out it, but inode - * size is also updated with truncate offset. - * inode size is retained by backup inode size. - */ - size = i_size_read(inode); - rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); - if (rc) { - pr_err("truncate failed!, err %d\n", rc); - return rc; - } - if (size < alloc_blks * 512) - i_size_write(inode, size); - } - return 0; -} - -static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_file_eof_info *file_eof_info) -{ - loff_t newsize; - struct inode *inode; - int rc; - - if (!(fp->daccess & FILE_WRITE_DATA_LE)) - return -EACCES; - - newsize = le64_to_cpu(file_eof_info->EndOfFile); - inode = file_inode(fp->filp); - - /* - * If FILE_END_OF_FILE_INFORMATION of set_info_file is called - * on FAT32 shared device, truncate execution time is too long - * and network error could cause from windows client. because - * truncate of some filesystem like FAT32 fill zero data in - * truncated range. - */ - if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { - ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); - rc = ksmbd_vfs_truncate(work, fp, newsize); - if (rc) { - ksmbd_debug(SMB, "truncate failed!, err %d\n", rc); - if (rc != -EAGAIN) - rc = -EBADF; - return rc; - } - } - return 0; -} - -static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_file_rename_info *rename_info, - unsigned int buf_len) -{ - struct user_namespace *user_ns; - struct ksmbd_file *parent_fp; - struct dentry *parent; - struct dentry *dentry = fp->filp->f_path.dentry; - int ret; - - if (!(fp->daccess & FILE_DELETE_LE)) { - pr_err("no right to delete : 0x%x\n", fp->daccess); - return -EACCES; - } - - if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + - le32_to_cpu(rename_info->FileNameLength)) - return -EINVAL; - - user_ns = file_mnt_user_ns(fp->filp); - if (ksmbd_stream_fd(fp)) - goto next; - - parent = dget_parent(dentry); - ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); - if (ret) { - dput(parent); - return ret; - } - - parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); - inode_unlock(d_inode(parent)); - dput(parent); - - if (parent_fp) { - if (parent_fp->daccess & FILE_DELETE_LE) { - pr_err("parent dir is opened with delete access\n"); - ksmbd_fd_put(work, parent_fp); - return -ESHARE; - } - ksmbd_fd_put(work, parent_fp); - } -next: - return smb2_rename(work, fp, user_ns, rename_info, - work->conn->local_nls); -} - -static int set_file_disposition_info(struct ksmbd_file *fp, - struct smb2_file_disposition_info *file_info) -{ - struct inode *inode; - - if (!(fp->daccess & FILE_DELETE_LE)) { - pr_err("no right to delete : 0x%x\n", fp->daccess); - return -EACCES; - } - - inode = file_inode(fp->filp); - if (file_info->DeletePending) { - if (S_ISDIR(inode->i_mode) && - ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) - return -EBUSY; - ksmbd_set_inode_pending_delete(fp); - } else { - ksmbd_clear_inode_pending_delete(fp); - } - return 0; -} - -static int set_file_position_info(struct ksmbd_file *fp, - struct smb2_file_pos_info *file_info) -{ - loff_t current_byte_offset; - unsigned long sector_size; - struct inode *inode; - - inode = file_inode(fp->filp); - current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); - sector_size = inode->i_sb->s_blocksize; - - if (current_byte_offset < 0 || - (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && - current_byte_offset & (sector_size - 1))) { - pr_err("CurrentByteOffset is not valid : %llu\n", - current_byte_offset); - return -EINVAL; - } - - fp->filp->f_pos = current_byte_offset; - return 0; -} - -static int set_file_mode_info(struct ksmbd_file *fp, - struct smb2_file_mode_info *file_info) -{ - __le32 mode; - - mode = file_info->Mode; - - if ((mode & ~FILE_MODE_INFO_MASK)) { - pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); - return -EINVAL; - } - - /* - * TODO : need to implement consideration for - * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT - */ - ksmbd_vfs_set_fadvise(fp->filp, mode); - fp->coption = mode; - return 0; -} - -/** - * smb2_set_info_file() - handler for smb2 set info command - * @work: smb work containing set info command buffer - * @fp: ksmbd_file pointer - * @req: request buffer pointer - * @share: ksmbd_share_config pointer - * - * Return: 0 on success, otherwise error - * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH - */ -static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_set_info_req *req, - struct ksmbd_share_config *share) -{ - unsigned int buf_len = le32_to_cpu(req->BufferLength); - - switch (req->FileInfoClass) { - case FILE_BASIC_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_basic_info)) - return -EINVAL; - - return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); - } - case FILE_ALLOCATION_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_alloc_info)) - return -EINVAL; - - return set_file_allocation_info(work, fp, - (struct smb2_file_alloc_info *)req->Buffer); - } - case FILE_END_OF_FILE_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_eof_info)) - return -EINVAL; - - return set_end_of_file_info(work, fp, - (struct smb2_file_eof_info *)req->Buffer); - } - case FILE_RENAME_INFORMATION: - { - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - return -EACCES; - } - - if (buf_len < sizeof(struct smb2_file_rename_info)) - return -EINVAL; - - return set_rename_info(work, fp, - (struct smb2_file_rename_info *)req->Buffer, - buf_len); - } - case FILE_LINK_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_link_info)) - return -EINVAL; - - return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)req->Buffer, - buf_len, fp->filp, - work->conn->local_nls); - } - case FILE_DISPOSITION_INFORMATION: - { - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - return -EACCES; - } - - if (buf_len < sizeof(struct smb2_file_disposition_info)) - return -EINVAL; - - return set_file_disposition_info(fp, - (struct smb2_file_disposition_info *)req->Buffer); - } - case FILE_FULL_EA_INFORMATION: - { - if (!(fp->daccess & FILE_WRITE_EA_LE)) { - pr_err("Not permitted to write ext attr: 0x%x\n", - fp->daccess); - return -EACCES; - } - - if (buf_len < sizeof(struct smb2_ea_info)) - return -EINVAL; - - return smb2_set_ea((struct smb2_ea_info *)req->Buffer, - buf_len, &fp->filp->f_path); - } - case FILE_POSITION_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_pos_info)) - return -EINVAL; - - return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); - } - case FILE_MODE_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_mode_info)) - return -EINVAL; - - return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); - } - } - - pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); - return -EOPNOTSUPP; -} - -static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, - char *buffer, int buf_len) -{ - struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; - - fp->saccess |= FILE_SHARE_DELETE_LE; - - return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, - buf_len, false); -} - -/** - * smb2_set_info() - handler for smb2 set info command handler - * @work: smb work containing set info request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_set_info(struct ksmbd_work *work) -{ - struct smb2_set_info_req *req; - struct smb2_set_info_rsp *rsp; - struct ksmbd_file *fp; - int rc = 0; - unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - - ksmbd_debug(SMB, "Received set info request\n"); - - if (work->next_smb2_rcv_hdr_off) { - req = ksmbd_req_buf_next(work); - rsp = ksmbd_resp_buf_next(work); - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - pid = work->compound_pfid; - } - } else { - req = smb2_get_msg(work->request_buf); - rsp = smb2_get_msg(work->response_buf); - } - - if (!has_file_id(id)) { - id = req->VolatileFileId; - pid = req->PersistentFileId; - } - - fp = ksmbd_lookup_fd_slow(work, id, pid); - if (!fp) { - ksmbd_debug(SMB, "Invalid id for close: %u\n", id); - rc = -ENOENT; - goto err_out; - } - - switch (req->InfoType) { - case SMB2_O_INFO_FILE: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); - rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); - break; - case SMB2_O_INFO_SECURITY: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - if (ksmbd_override_fsids(work)) { - rc = -ENOMEM; - goto err_out; - } - rc = smb2_set_info_sec(fp, - le32_to_cpu(req->AdditionalInformation), - req->Buffer, - le32_to_cpu(req->BufferLength)); - ksmbd_revert_fsids(work); - break; - default: - rc = -EOPNOTSUPP; - } - - if (rc < 0) - goto err_out; - - rsp->StructureSize = cpu_to_le16(2); - inc_rfc1001_len(work->response_buf, 2); - ksmbd_fd_put(work, fp); - return 0; - -err_out: - if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -ESHARE) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; - else if (rc == -EBUSY || rc == -ENOTEMPTY) - rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; - else if (rc == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (rc == -EBADF || rc == -ESTALE) - rsp->hdr.Status = STATUS_INVALID_HANDLE; - else if (rc == -EEXIST) - rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; - else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); - return rc; -} - -/** - * smb2_read_pipe() - handler for smb2 read from IPC pipe - * @work: smb work containing read IPC pipe command buffer - * - * Return: 0 on success, otherwise error - */ -static noinline int smb2_read_pipe(struct ksmbd_work *work) -{ - int nbytes = 0, err; - u64 id; - struct ksmbd_rpc_command *rpc_resp; - struct smb2_read_req *req = smb2_get_msg(work->request_buf); - struct smb2_read_rsp *rsp = smb2_get_msg(work->response_buf); - - id = req->VolatileFileId; - - inc_rfc1001_len(work->response_buf, 16); - rpc_resp = ksmbd_rpc_read(work->sess, id); - if (rpc_resp) { - if (rpc_resp->flags != KSMBD_RPC_OK) { - err = -EINVAL; - goto out; - } - - work->aux_payload_buf = - kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); - if (!work->aux_payload_buf) { - err = -ENOMEM; - goto out; - } - - memcpy(work->aux_payload_buf, rpc_resp->payload, - rpc_resp->payload_sz); - - nbytes = rpc_resp->payload_sz; - work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; - work->aux_payload_sz = nbytes; - kvfree(rpc_resp); - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 80; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(nbytes); - rsp->DataRemaining = 0; - rsp->Flags = 0; - inc_rfc1001_len(work->response_buf, nbytes); - return 0; - -out: - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - smb2_set_err_rsp(work); - kvfree(rpc_resp); - return err; -} - -static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, - struct smb2_buffer_desc_v1 *desc, - __le32 Channel, - __le16 ChannelInfoLength) -{ - unsigned int i, ch_count; - - if (work->conn->dialect == SMB30_PROT_ID && - Channel != SMB2_CHANNEL_RDMA_V1) - return -EINVAL; - - ch_count = le16_to_cpu(ChannelInfoLength) / sizeof(*desc); - if (ksmbd_debug_types & KSMBD_DEBUG_RDMA) { - for (i = 0; i < ch_count; i++) { - pr_info("RDMA r/w request %#x: token %#x, length %#x\n", - i, - le32_to_cpu(desc[i].token), - le32_to_cpu(desc[i].length)); - } - } - if (!ch_count) - return -EINVAL; - - work->need_invalidate_rkey = - (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); - if (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) - work->remote_key = le32_to_cpu(desc->token); - return 0; -} - -static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, - struct smb2_read_req *req, void *data_buf, - size_t length) -{ - int err; - - err = ksmbd_conn_rdma_write(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) - ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), - le16_to_cpu(req->ReadChannelInfoLength)); - if (err) - return err; - - return length; -} - -/** - * smb2_read() - handler for smb2 read from file - * @work: smb work containing read command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_read(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_read_req *req; - struct smb2_read_rsp *rsp; - struct ksmbd_file *fp = NULL; - loff_t offset; - size_t length, mincount; - ssize_t nbytes = 0, remain_bytes = 0; - int err = 0; - bool is_rdma_channel = false; - unsigned int max_read_size = conn->vals->max_read_size; - - WORK_BUFFERS(work, req, rsp); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe read request\n"); - return smb2_read_pipe(work); - } - - if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || - req->Channel == SMB2_CHANNEL_RDMA_V1) { - is_rdma_channel = true; - max_read_size = get_smbd_max_read_write_size(); - } - - if (is_rdma_channel == true) { - unsigned int ch_offset = le16_to_cpu(req->ReadChannelInfoOffset); - - if (ch_offset < offsetof(struct smb2_read_req, Buffer)) { - err = -EINVAL; - goto out; - } - err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) - ((char *)req + ch_offset), - req->Channel, - req->ReadChannelInfoLength); - if (err) - goto out; - } - - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) { - err = -ENOENT; - goto out; - } - - if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { - pr_err("Not permitted to read : 0x%x\n", fp->daccess); - err = -EACCES; - goto out; - } - - offset = le64_to_cpu(req->Offset); - length = le32_to_cpu(req->Length); - mincount = le32_to_cpu(req->MinimumCount); - - if (length > max_read_size) { - ksmbd_debug(SMB, "limiting read size to max size(%u)\n", - max_read_size); - err = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", - fp->filp, offset, length); - - work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); - if (!work->aux_payload_buf) { - err = -ENOMEM; - goto out; - } - - nbytes = ksmbd_vfs_read(work, fp, length, &offset); - if (nbytes < 0) { - err = nbytes; - goto out; - } - - if ((nbytes == 0 && length != 0) || nbytes < mincount) { - kvfree(work->aux_payload_buf); - work->aux_payload_buf = NULL; - rsp->hdr.Status = STATUS_END_OF_FILE; - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return 0; - } - - ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", - nbytes, offset, mincount); - - if (is_rdma_channel == true) { - /* write data to the client using rdma channel */ - remain_bytes = smb2_read_rdma_channel(work, req, - work->aux_payload_buf, - nbytes); - kvfree(work->aux_payload_buf); - work->aux_payload_buf = NULL; - - nbytes = 0; - if (remain_bytes < 0) { - err = (int)remain_bytes; - goto out; - } - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 80; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(nbytes); - rsp->DataRemaining = cpu_to_le32(remain_bytes); - rsp->Flags = 0; - inc_rfc1001_len(work->response_buf, 16); - work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; - work->aux_payload_sz = nbytes; - inc_rfc1001_len(work->response_buf, nbytes); - ksmbd_fd_put(work, fp); - return 0; - -out: - if (err) { - if (err == -EISDIR) - rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; - else if (err == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (err == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (err == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (err == -ESHARE) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (err == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else - rsp->hdr.Status = STATUS_INVALID_HANDLE; - - smb2_set_err_rsp(work); - } - ksmbd_fd_put(work, fp); - return err; -} - -/** - * smb2_write_pipe() - handler for smb2 write on IPC pipe - * @work: smb work containing write IPC pipe command buffer - * - * Return: 0 on success, otherwise error - */ -static noinline int smb2_write_pipe(struct ksmbd_work *work) -{ - struct smb2_write_req *req = smb2_get_msg(work->request_buf); - struct smb2_write_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_rpc_command *rpc_resp; - u64 id = 0; - int err = 0, ret = 0; - char *data_buf; - size_t length; - - length = le32_to_cpu(req->Length); - id = req->VolatileFileId; - - if ((u64)le16_to_cpu(req->DataOffset) + length > - get_rfc1002_len(work->request_buf)) { - pr_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), - get_rfc1002_len(work->request_buf)); - err = -EINVAL; - goto out; - } - - data_buf = (char *)(((char *)&req->hdr.ProtocolId) + - le16_to_cpu(req->DataOffset)); - - rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); - if (rpc_resp) { - if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - kvfree(rpc_resp); - smb2_set_err_rsp(work); - return -EOPNOTSUPP; - } - if (rpc_resp->flags != KSMBD_RPC_OK) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - smb2_set_err_rsp(work); - kvfree(rpc_resp); - return ret; - } - kvfree(rpc_resp); - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 0; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(length); - rsp->DataRemaining = 0; - rsp->Reserved2 = 0; - inc_rfc1001_len(work->response_buf, 16); - return 0; -out: - if (err) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - smb2_set_err_rsp(work); - } - - return err; -} - -static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, - struct smb2_write_req *req, - struct ksmbd_file *fp, - loff_t offset, size_t length, bool sync) -{ - char *data_buf; - int ret; - ssize_t nbytes; - - data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); - if (!data_buf) - return -ENOMEM; - - ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) - ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), - le16_to_cpu(req->WriteChannelInfoLength)); - if (ret < 0) { - kvfree(data_buf); - return ret; - } - - ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); - kvfree(data_buf); - if (ret < 0) - return ret; - - return nbytes; -} - -/** - * smb2_write() - handler for smb2 write from file - * @work: smb work containing write command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_write(struct ksmbd_work *work) -{ - struct smb2_write_req *req; - struct smb2_write_rsp *rsp; - struct ksmbd_file *fp = NULL; - loff_t offset; - size_t length; - ssize_t nbytes; - char *data_buf; - bool writethrough = false, is_rdma_channel = false; - int err = 0; - unsigned int max_write_size = work->conn->vals->max_write_size; - - WORK_BUFFERS(work, req, rsp); - - if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe write request\n"); - return smb2_write_pipe(work); - } - - offset = le64_to_cpu(req->Offset); - length = le32_to_cpu(req->Length); - - if (req->Channel == SMB2_CHANNEL_RDMA_V1 || - req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { - is_rdma_channel = true; - max_write_size = get_smbd_max_read_write_size(); - length = le32_to_cpu(req->RemainingBytes); - } - - if (is_rdma_channel == true) { - unsigned int ch_offset = le16_to_cpu(req->WriteChannelInfoOffset); - - if (req->Length != 0 || req->DataOffset != 0 || - ch_offset < offsetof(struct smb2_write_req, Buffer)) { - err = -EINVAL; - goto out; - } - err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) - ((char *)req + ch_offset), - req->Channel, - req->WriteChannelInfoLength); - if (err) - goto out; - } - - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, "User does not have write permission\n"); - err = -EACCES; - goto out; - } - - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) { - err = -ENOENT; - goto out; - } - - if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { - pr_err("Not permitted to write : 0x%x\n", fp->daccess); - err = -EACCES; - goto out; - } - - if (length > max_write_size) { - ksmbd_debug(SMB, "limiting write size to max size(%u)\n", - max_write_size); - err = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); - if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) - writethrough = true; - - if (is_rdma_channel == false) { - if (le16_to_cpu(req->DataOffset) < - offsetof(struct smb2_write_req, Buffer)) { - err = -EINVAL; - goto out; - } - - data_buf = (char *)(((char *)&req->hdr.ProtocolId) + - le16_to_cpu(req->DataOffset)); - - ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", - fp->filp, offset, length); - err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, - writethrough, &nbytes); - if (err < 0) - goto out; - } else { - /* read data from the client using rdma channel, and - * write the data. - */ - nbytes = smb2_write_rdma_channel(work, req, fp, offset, length, - writethrough); - if (nbytes < 0) { - err = (int)nbytes; - goto out; - } - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 0; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(nbytes); - rsp->DataRemaining = 0; - rsp->Reserved2 = 0; - inc_rfc1001_len(work->response_buf, 16); - ksmbd_fd_put(work, fp); - return 0; - -out: - if (err == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (err == -ENOSPC || err == -EFBIG) - rsp->hdr.Status = STATUS_DISK_FULL; - else if (err == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (err == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (err == -ESHARE) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (err == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else - rsp->hdr.Status = STATUS_INVALID_HANDLE; - - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return err; -} - -/** - * smb2_flush() - handler for smb2 flush file - fsync - * @work: smb work containing flush command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_flush(struct ksmbd_work *work) -{ - struct smb2_flush_req *req; - struct smb2_flush_rsp *rsp; - int err; - - WORK_BUFFERS(work, req, rsp); - - ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); - - err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); - if (err) - goto out; - - rsp->StructureSize = cpu_to_le16(4); - rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - return 0; - -out: - if (err) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - smb2_set_err_rsp(work); - } - - return err; -} - -/** - * smb2_cancel() - handler for smb2 cancel command - * @work: smb work containing cancel command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_cancel(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - struct smb2_hdr *chdr; - struct ksmbd_work *iter; - struct list_head *command_list; - - ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", - hdr->MessageId, hdr->Flags); - - if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { - command_list = &conn->async_requests; - - spin_lock(&conn->request_lock); - list_for_each_entry(iter, command_list, - async_request_entry) { - chdr = smb2_get_msg(iter->request_buf); - - if (iter->async_id != - le64_to_cpu(hdr->Id.AsyncId)) - continue; - - ksmbd_debug(SMB, - "smb2 with AsyncId %llu cancelled command = 0x%x\n", - le64_to_cpu(hdr->Id.AsyncId), - le16_to_cpu(chdr->Command)); - iter->state = KSMBD_WORK_CANCELLED; - if (iter->cancel_fn) - iter->cancel_fn(iter->cancel_argv); - break; - } - spin_unlock(&conn->request_lock); - } else { - command_list = &conn->requests; - - spin_lock(&conn->request_lock); - list_for_each_entry(iter, command_list, request_entry) { - chdr = smb2_get_msg(iter->request_buf); - - if (chdr->MessageId != hdr->MessageId || - iter == work) - continue; - - ksmbd_debug(SMB, - "smb2 with mid %llu cancelled command = 0x%x\n", - le64_to_cpu(hdr->MessageId), - le16_to_cpu(chdr->Command)); - iter->state = KSMBD_WORK_CANCELLED; - break; - } - spin_unlock(&conn->request_lock); - } - - /* For SMB2_CANCEL command itself send no response*/ - work->send_no_response = 1; - return 0; -} - -struct file_lock *smb_flock_init(struct file *f) -{ - struct file_lock *fl; - - fl = locks_alloc_lock(); - if (!fl) - goto out; - - locks_init_lock(fl); - - fl->fl_owner = f; - fl->fl_pid = current->tgid; - fl->fl_file = f; - fl->fl_flags = FL_POSIX; - fl->fl_ops = NULL; - fl->fl_lmops = NULL; - -out: - return fl; -} - -static int smb2_set_flock_flags(struct file_lock *flock, int flags) -{ - int cmd = -EINVAL; - - /* Checking for wrong flag combination during lock request*/ - switch (flags) { - case SMB2_LOCKFLAG_SHARED: - ksmbd_debug(SMB, "received shared request\n"); - cmd = F_SETLKW; - flock->fl_type = F_RDLCK; - flock->fl_flags |= FL_SLEEP; - break; - case SMB2_LOCKFLAG_EXCLUSIVE: - ksmbd_debug(SMB, "received exclusive request\n"); - cmd = F_SETLKW; - flock->fl_type = F_WRLCK; - flock->fl_flags |= FL_SLEEP; - break; - case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: - ksmbd_debug(SMB, - "received shared & fail immediately request\n"); - cmd = F_SETLK; - flock->fl_type = F_RDLCK; - break; - case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: - ksmbd_debug(SMB, - "received exclusive & fail immediately request\n"); - cmd = F_SETLK; - flock->fl_type = F_WRLCK; - break; - case SMB2_LOCKFLAG_UNLOCK: - ksmbd_debug(SMB, "received unlock request\n"); - flock->fl_type = F_UNLCK; - cmd = 0; - break; - } - - return cmd; -} - -static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, - unsigned int cmd, int flags, - struct list_head *lock_list) -{ - struct ksmbd_lock *lock; - - lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); - if (!lock) - return NULL; - - lock->cmd = cmd; - lock->fl = flock; - lock->start = flock->fl_start; - lock->end = flock->fl_end; - lock->flags = flags; - if (lock->start == lock->end) - lock->zero_len = 1; - INIT_LIST_HEAD(&lock->clist); - INIT_LIST_HEAD(&lock->flist); - INIT_LIST_HEAD(&lock->llist); - list_add_tail(&lock->llist, lock_list); - - return lock; -} - -static void smb2_remove_blocked_lock(void **argv) -{ - struct file_lock *flock = (struct file_lock *)argv[0]; - - ksmbd_vfs_posix_lock_unblock(flock); - wake_up(&flock->fl_wait); -} - -static inline bool lock_defer_pending(struct file_lock *fl) -{ - /* check pending lock waiters */ - return waitqueue_active(&fl->fl_wait); -} - -/** - * smb2_lock() - handler for smb2 file lock command - * @work: smb work containing lock command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_lock(struct ksmbd_work *work) -{ - struct smb2_lock_req *req = smb2_get_msg(work->request_buf); - struct smb2_lock_rsp *rsp = smb2_get_msg(work->response_buf); - struct smb2_lock_element *lock_ele; - struct ksmbd_file *fp = NULL; - struct file_lock *flock = NULL; - struct file *filp = NULL; - int lock_count; - int flags = 0; - int cmd = 0; - int err = -EIO, i, rc = 0; - u64 lock_start, lock_length; - struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; - struct ksmbd_conn *conn; - int nolock = 0; - LIST_HEAD(lock_list); - LIST_HEAD(rollback_list); - int prior_lock = 0; - - ksmbd_debug(SMB, "Received lock request\n"); - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) { - ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); - err = -ENOENT; - goto out2; - } - - filp = fp->filp; - lock_count = le16_to_cpu(req->LockCount); - lock_ele = req->locks; - - ksmbd_debug(SMB, "lock count is %d\n", lock_count); - if (!lock_count) { - err = -EINVAL; - goto out2; - } - - for (i = 0; i < lock_count; i++) { - flags = le32_to_cpu(lock_ele[i].Flags); - - flock = smb_flock_init(filp); - if (!flock) - goto out; - - cmd = smb2_set_flock_flags(flock, flags); - - lock_start = le64_to_cpu(lock_ele[i].Offset); - lock_length = le64_to_cpu(lock_ele[i].Length); - if (lock_start > U64_MAX - lock_length) { - pr_err("Invalid lock range requested\n"); - rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; - goto out; - } - - if (lock_start > OFFSET_MAX) - flock->fl_start = OFFSET_MAX; - else - flock->fl_start = lock_start; - - lock_length = le64_to_cpu(lock_ele[i].Length); - if (lock_length > OFFSET_MAX - flock->fl_start) - lock_length = OFFSET_MAX - flock->fl_start; - - flock->fl_end = flock->fl_start + lock_length; - - if (flock->fl_end < flock->fl_start) { - ksmbd_debug(SMB, - "the end offset(%llx) is smaller than the start offset(%llx)\n", - flock->fl_end, flock->fl_start); - rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; - goto out; - } - - /* Check conflict locks in one request */ - list_for_each_entry(cmp_lock, &lock_list, llist) { - if (cmp_lock->fl->fl_start <= flock->fl_start && - cmp_lock->fl->fl_end >= flock->fl_end) { - if (cmp_lock->fl->fl_type != F_UNLCK && - flock->fl_type != F_UNLCK) { - pr_err("conflict two locks in one request\n"); - err = -EINVAL; - goto out; - } - } - } - - smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); - if (!smb_lock) { - err = -EINVAL; - goto out; - } - } - - list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { - if (smb_lock->cmd < 0) { - err = -EINVAL; - goto out; - } - - if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { - err = -EINVAL; - goto out; - } - - if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && - smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || - (prior_lock == SMB2_LOCKFLAG_UNLOCK && - !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { - err = -EINVAL; - goto out; - } - - prior_lock = smb_lock->flags; - - if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && - !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) - goto no_check_cl; - - nolock = 1; - /* check locks in connection list */ - down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { - spin_lock(&conn->llist_lock); - list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { - if (file_inode(cmp_lock->fl->fl_file) != - file_inode(smb_lock->fl->fl_file)) - continue; - - if (smb_lock->fl->fl_type == F_UNLCK) { - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && - cmp_lock->start == smb_lock->start && - cmp_lock->end == smb_lock->end && - !lock_defer_pending(cmp_lock->fl)) { - nolock = 0; - list_del(&cmp_lock->flist); - list_del(&cmp_lock->clist); - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - - locks_free_lock(cmp_lock->fl); - kfree(cmp_lock); - goto out_check_cl; - } - continue; - } - - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { - if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) - continue; - } else { - if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) - continue; - } - - /* check zero byte lock range */ - if (cmp_lock->zero_len && !smb_lock->zero_len && - cmp_lock->start > smb_lock->start && - cmp_lock->start < smb_lock->end) { - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - pr_err("previous lock conflict with zero byte lock range\n"); - goto out; - } - - if (smb_lock->zero_len && !cmp_lock->zero_len && - smb_lock->start > cmp_lock->start && - smb_lock->start < cmp_lock->end) { - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - pr_err("current lock conflict with zero byte lock range\n"); - goto out; - } - - if (((cmp_lock->start <= smb_lock->start && - cmp_lock->end > smb_lock->start) || - (cmp_lock->start < smb_lock->end && - cmp_lock->end >= smb_lock->end)) && - !cmp_lock->zero_len && !smb_lock->zero_len) { - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - pr_err("Not allow lock operation on exclusive lock range\n"); - goto out; - } - } - spin_unlock(&conn->llist_lock); - } - up_read(&conn_list_lock); -out_check_cl: - if (smb_lock->fl->fl_type == F_UNLCK && nolock) { - pr_err("Try to unlock nolocked range\n"); - rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; - goto out; - } - -no_check_cl: - if (smb_lock->zero_len) { - err = 0; - goto skip; - } - - flock = smb_lock->fl; - list_del(&smb_lock->llist); -retry: - rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); -skip: - if (flags & SMB2_LOCKFLAG_UNLOCK) { - if (!rc) { - ksmbd_debug(SMB, "File unlocked\n"); - } else if (rc == -ENOENT) { - rsp->hdr.Status = STATUS_NOT_LOCKED; - goto out; - } - locks_free_lock(flock); - kfree(smb_lock); - } else { - if (rc == FILE_LOCK_DEFERRED) { - void **argv; - - ksmbd_debug(SMB, - "would have to wait for getting lock\n"); - spin_lock(&work->conn->llist_lock); - list_add_tail(&smb_lock->clist, - &work->conn->lock_list); - spin_unlock(&work->conn->llist_lock); - list_add(&smb_lock->llist, &rollback_list); - - argv = kmalloc(sizeof(void *), GFP_KERNEL); - if (!argv) { - err = -ENOMEM; - goto out; - } - argv[0] = flock; - - rc = setup_async_work(work, - smb2_remove_blocked_lock, - argv); - if (rc) { - err = -ENOMEM; - goto out; - } - spin_lock(&fp->f_lock); - list_add(&work->fp_entry, &fp->blocked_works); - spin_unlock(&fp->f_lock); - - smb2_send_interim_resp(work, STATUS_PENDING); - - ksmbd_vfs_posix_lock_wait(flock); - - spin_lock(&work->conn->request_lock); - spin_lock(&fp->f_lock); - list_del(&work->fp_entry); - work->cancel_fn = NULL; - kfree(argv); - spin_unlock(&fp->f_lock); - spin_unlock(&work->conn->request_lock); - - if (work->state != KSMBD_WORK_ACTIVE) { - list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); - locks_free_lock(flock); - - if (work->state == KSMBD_WORK_CANCELLED) { - rsp->hdr.Status = - STATUS_CANCELLED; - kfree(smb_lock); - smb2_send_interim_resp(work, - STATUS_CANCELLED); - work->send_no_response = 1; - goto out; - } - init_smb2_rsp_hdr(work); - smb2_set_err_rsp(work); - rsp->hdr.Status = - STATUS_RANGE_NOT_LOCKED; - kfree(smb_lock); - goto out2; - } - - list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); - - goto retry; - } else if (!rc) { - spin_lock(&work->conn->llist_lock); - list_add_tail(&smb_lock->clist, - &work->conn->lock_list); - list_add_tail(&smb_lock->flist, - &fp->lock_list); - spin_unlock(&work->conn->llist_lock); - list_add(&smb_lock->llist, &rollback_list); - ksmbd_debug(SMB, "successful in taking lock\n"); - } else { - goto out; - } - } - } - - if (atomic_read(&fp->f_ci->op_count) > 1) - smb_break_all_oplock(work, fp); - - rsp->StructureSize = cpu_to_le16(4); - ksmbd_debug(SMB, "successful in taking lock\n"); - rsp->hdr.Status = STATUS_SUCCESS; - rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - ksmbd_fd_put(work, fp); - return 0; - -out: - list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { - locks_free_lock(smb_lock->fl); - list_del(&smb_lock->llist); - kfree(smb_lock); - } - - list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { - struct file_lock *rlock = NULL; - - rlock = smb_flock_init(filp); - rlock->fl_type = F_UNLCK; - rlock->fl_start = smb_lock->start; - rlock->fl_end = smb_lock->end; - - rc = vfs_lock_file(filp, 0, rlock, NULL); - if (rc) - pr_err("rollback unlock fail : %d\n", rc); - - list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - if (!list_empty(&smb_lock->flist)) - list_del(&smb_lock->flist); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); - - locks_free_lock(smb_lock->fl); - locks_free_lock(rlock); - kfree(smb_lock); - } -out2: - ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); - - if (!rsp->hdr.Status) { - if (err == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (err == -ENOMEM) - rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; - else if (err == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - } - - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return err; -} - -static int fsctl_copychunk(struct ksmbd_work *work, - struct copychunk_ioctl_req *ci_req, - unsigned int cnt_code, - unsigned int input_count, - unsigned long long volatile_id, - unsigned long long persistent_id, - struct smb2_ioctl_rsp *rsp) -{ - struct copychunk_ioctl_rsp *ci_rsp; - struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; - struct srv_copychunk *chunks; - unsigned int i, chunk_count, chunk_count_written = 0; - unsigned int chunk_size_written = 0; - loff_t total_size_written = 0; - int ret = 0; - - ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; - - rsp->VolatileFileId = volatile_id; - rsp->PersistentFileId = persistent_id; - ci_rsp->ChunksWritten = - cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); - ci_rsp->ChunkBytesWritten = - cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); - ci_rsp->TotalBytesWritten = - cpu_to_le32(ksmbd_server_side_copy_max_total_size()); - - chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; - chunk_count = le32_to_cpu(ci_req->ChunkCount); - if (chunk_count == 0) - goto out; - total_size_written = 0; - - /* verify the SRV_COPYCHUNK_COPY packet */ - if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || - input_count < offsetof(struct copychunk_ioctl_req, Chunks) + - chunk_count * sizeof(struct srv_copychunk)) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - return -EINVAL; - } - - for (i = 0; i < chunk_count; i++) { - if (le32_to_cpu(chunks[i].Length) == 0 || - le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) - break; - total_size_written += le32_to_cpu(chunks[i].Length); - } - - if (i < chunk_count || - total_size_written > ksmbd_server_side_copy_max_total_size()) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - return -EINVAL; - } - - src_fp = ksmbd_lookup_foreign_fd(work, - le64_to_cpu(ci_req->ResumeKey[0])); - dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); - ret = -EINVAL; - if (!src_fp || - src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { - rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; - goto out; - } - - if (!dst_fp) { - rsp->hdr.Status = STATUS_FILE_CLOSED; - goto out; - } - - /* - * FILE_READ_DATA should only be included in - * the FSCTL_COPYCHUNK case - */ - if (cnt_code == FSCTL_COPYCHUNK && - !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { - rsp->hdr.Status = STATUS_ACCESS_DENIED; - goto out; - } - - ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, - chunks, chunk_count, - &chunk_count_written, - &chunk_size_written, - &total_size_written); - if (ret < 0) { - if (ret == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - if (ret == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (ret == -EBADF) - rsp->hdr.Status = STATUS_INVALID_HANDLE; - else if (ret == -EFBIG || ret == -ENOSPC) - rsp->hdr.Status = STATUS_DISK_FULL; - else if (ret == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (ret == -EISDIR) - rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; - else if (ret == -E2BIG) - rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; - else - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - } - - ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); - ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); - ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); -out: - ksmbd_fd_put(work, src_fp); - ksmbd_fd_put(work, dst_fp); - return ret; -} - -static __be32 idev_ipv4_address(struct in_device *idev) -{ - __be32 addr = 0; - - struct in_ifaddr *ifa; - - rcu_read_lock(); - in_dev_for_each_ifa_rcu(ifa, idev) { - if (ifa->ifa_flags & IFA_F_SECONDARY) - continue; - - addr = ifa->ifa_address; - break; - } - rcu_read_unlock(); - return addr; -} - -static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, - struct smb2_ioctl_rsp *rsp, - unsigned int out_buf_len) -{ - struct network_interface_info_ioctl_rsp *nii_rsp = NULL; - int nbytes = 0; - struct net_device *netdev; - struct sockaddr_storage_rsp *sockaddr_storage; - unsigned int flags; - unsigned long long speed; - - rtnl_lock(); - for_each_netdev(&init_net, netdev) { - bool ipv4_set = false; - - if (netdev->type == ARPHRD_LOOPBACK) - continue; - - flags = dev_get_flags(netdev); - if (!(flags & IFF_RUNNING)) - continue; -ipv6_retry: - if (out_buf_len < - nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { - rtnl_unlock(); - return -ENOSPC; - } - - nii_rsp = (struct network_interface_info_ioctl_rsp *) - &rsp->Buffer[nbytes]; - nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); - - nii_rsp->Capability = 0; - if (netdev->real_num_tx_queues > 1) - nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); - if (ksmbd_rdma_capable_netdev(netdev)) - nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); - - nii_rsp->Next = cpu_to_le32(152); - nii_rsp->Reserved = 0; - - if (netdev->ethtool_ops->get_link_ksettings) { - struct ethtool_link_ksettings cmd; - - netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); - speed = cmd.base.speed; - } else { - ksmbd_debug(SMB, "%s %s\n", netdev->name, - "speed is unknown, defaulting to 1Gb/sec"); - speed = SPEED_1000; - } - - speed *= 1000000; - nii_rsp->LinkSpeed = cpu_to_le64(speed); - - sockaddr_storage = (struct sockaddr_storage_rsp *) - nii_rsp->SockAddr_Storage; - memset(sockaddr_storage, 0, 128); - - if (!ipv4_set) { - struct in_device *idev; - - sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); - sockaddr_storage->addr4.Port = 0; - - idev = __in_dev_get_rtnl(netdev); - if (!idev) - continue; - sockaddr_storage->addr4.IPv4address = - idev_ipv4_address(idev); - nbytes += sizeof(struct network_interface_info_ioctl_rsp); - ipv4_set = true; - goto ipv6_retry; - } else { - struct inet6_dev *idev6; - struct inet6_ifaddr *ifa; - __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; - - sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); - sockaddr_storage->addr6.Port = 0; - sockaddr_storage->addr6.FlowInfo = 0; - - idev6 = __in6_dev_get(netdev); - if (!idev6) - continue; - - list_for_each_entry(ifa, &idev6->addr_list, if_list) { - if (ifa->flags & (IFA_F_TENTATIVE | - IFA_F_DEPRECATED)) - continue; - memcpy(ipv6_addr, ifa->addr.s6_addr, 16); - break; - } - sockaddr_storage->addr6.ScopeId = 0; - nbytes += sizeof(struct network_interface_info_ioctl_rsp); - } - } - rtnl_unlock(); - - /* zero if this is last one */ - if (nii_rsp) - nii_rsp->Next = 0; - - rsp->PersistentFileId = SMB2_NO_FID; - rsp->VolatileFileId = SMB2_NO_FID; - return nbytes; -} - -static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, - struct validate_negotiate_info_req *neg_req, - struct validate_negotiate_info_rsp *neg_rsp, - unsigned int in_buf_len) -{ - int ret = 0; - int dialect; - - if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + - le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) - return -EINVAL; - - dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, - neg_req->DialectCount); - if (dialect == BAD_PROT_ID || dialect != conn->dialect) { - ret = -EINVAL; - goto err_out; - } - - if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { - ret = -EINVAL; - goto err_out; - } - - if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { - ret = -EINVAL; - goto err_out; - } - - if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { - ret = -EINVAL; - goto err_out; - } - - neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); - memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); - neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); - neg_rsp->Dialect = cpu_to_le16(conn->dialect); -err_out: - return ret; -} - -static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, - struct file_allocated_range_buffer *qar_req, - struct file_allocated_range_buffer *qar_rsp, - unsigned int in_count, unsigned int *out_count) -{ - struct ksmbd_file *fp; - loff_t start, length; - int ret = 0; - - *out_count = 0; - if (in_count == 0) - return -EINVAL; - - start = le64_to_cpu(qar_req->file_offset); - length = le64_to_cpu(qar_req->length); - - if (start < 0 || length < 0) - return -EINVAL; - - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) - return -ENOENT; - - ret = ksmbd_vfs_fqar_lseek(fp, start, length, - qar_rsp, in_count, out_count); - if (ret && ret != -E2BIG) - *out_count = 0; - - ksmbd_fd_put(work, fp); - return ret; -} - -static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, - unsigned int out_buf_len, - struct smb2_ioctl_req *req, - struct smb2_ioctl_rsp *rsp) -{ - struct ksmbd_rpc_command *rpc_resp; - char *data_buf = (char *)&req->Buffer[0]; - int nbytes = 0; - - rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, - le32_to_cpu(req->InputCount)); - if (rpc_resp) { - if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { - /* - * set STATUS_SOME_NOT_MAPPED response - * for unknown domain sid. - */ - rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; - } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - goto out; - } else if (rpc_resp->flags != KSMBD_RPC_OK) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - goto out; - } - - nbytes = rpc_resp->payload_sz; - if (rpc_resp->payload_sz > out_buf_len) { - rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - nbytes = out_buf_len; - } - - if (!rpc_resp->payload_sz) { - rsp->hdr.Status = - STATUS_UNEXPECTED_IO_ERROR; - goto out; - } - - memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); - } -out: - kvfree(rpc_resp); - return nbytes; -} - -static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, - struct file_sparse *sparse) -{ - struct ksmbd_file *fp; - struct user_namespace *user_ns; - int ret = 0; - __le32 old_fattr; - - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) - return -ENOENT; - user_ns = file_mnt_user_ns(fp->filp); - - old_fattr = fp->f_ci->m_fattr; - if (sparse->SetSparse) - fp->f_ci->m_fattr |= FILE_ATTRIBUTE_SPARSE_FILE_LE; - else - fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_SPARSE_FILE_LE; - - if (fp->f_ci->m_fattr != old_fattr && - test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - struct xattr_dos_attrib da; - - ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns, - fp->filp->f_path.dentry, &da); - if (ret <= 0) - goto out; - - da.attr = le32_to_cpu(fp->f_ci->m_fattr); - ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, - fp->filp->f_path.dentry, &da); - if (ret) - fp->f_ci->m_fattr = old_fattr; - } - -out: - ksmbd_fd_put(work, fp); - return ret; -} - -static int fsctl_request_resume_key(struct ksmbd_work *work, - struct smb2_ioctl_req *req, - struct resume_key_ioctl_rsp *key_rsp) -{ - struct ksmbd_file *fp; - - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) - return -ENOENT; - - memset(key_rsp, 0, sizeof(*key_rsp)); - key_rsp->ResumeKey[0] = req->VolatileFileId; - key_rsp->ResumeKey[1] = req->PersistentFileId; - ksmbd_fd_put(work, fp); - - return 0; -} - -/** - * smb2_ioctl() - handler for smb2 ioctl command - * @work: smb work containing ioctl command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_ioctl(struct ksmbd_work *work) -{ - struct smb2_ioctl_req *req; - struct smb2_ioctl_rsp *rsp; - unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; - u64 id = KSMBD_NO_FID; - struct ksmbd_conn *conn = work->conn; - int ret = 0; - - if (work->next_smb2_rcv_hdr_off) { - req = ksmbd_req_buf_next(work); - rsp = ksmbd_resp_buf_next(work); - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - } - } else { - req = smb2_get_msg(work->request_buf); - rsp = smb2_get_msg(work->response_buf); - } - - if (!has_file_id(id)) - id = req->VolatileFileId; - - if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - goto out; - } - - cnt_code = le32_to_cpu(req->CtlCode); - ret = smb2_calc_max_out_buf_len(work, 48, - le32_to_cpu(req->MaxOutputResponse)); - if (ret < 0) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - goto out; - } - out_buf_len = (unsigned int)ret; - in_buf_len = le32_to_cpu(req->InputCount); - - switch (cnt_code) { - case FSCTL_DFS_GET_REFERRALS: - case FSCTL_DFS_GET_REFERRALS_EX: - /* Not support DFS yet */ - rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; - goto out; - case FSCTL_CREATE_OR_GET_OBJECT_ID: - { - struct file_object_buf_type1_ioctl_rsp *obj_buf; - - nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); - obj_buf = (struct file_object_buf_type1_ioctl_rsp *) - &rsp->Buffer[0]; - - /* - * TODO: This is dummy implementation to pass smbtorture - * Need to check correct response later - */ - memset(obj_buf->ObjectId, 0x0, 16); - memset(obj_buf->BirthVolumeId, 0x0, 16); - memset(obj_buf->BirthObjectId, 0x0, 16); - memset(obj_buf->DomainId, 0x0, 16); - - break; - } - case FSCTL_PIPE_TRANSCEIVE: - out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); - nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); - break; - case FSCTL_VALIDATE_NEGOTIATE_INFO: - if (conn->dialect < SMB30_PROT_ID) { - ret = -EOPNOTSUPP; - goto out; - } - - if (in_buf_len < offsetof(struct validate_negotiate_info_req, - Dialects)) { - ret = -EINVAL; - goto out; - } - - if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_validate_negotiate_info(conn, - (struct validate_negotiate_info_req *)&req->Buffer[0], - (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], - in_buf_len); - if (ret < 0) - goto out; - - nbytes = sizeof(struct validate_negotiate_info_rsp); - rsp->PersistentFileId = SMB2_NO_FID; - rsp->VolatileFileId = SMB2_NO_FID; - break; - case FSCTL_QUERY_NETWORK_INTERFACE_INFO: - ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); - if (ret < 0) - goto out; - nbytes = ret; - break; - case FSCTL_REQUEST_RESUME_KEY: - if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_request_resume_key(work, req, - (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); - if (ret < 0) - goto out; - rsp->PersistentFileId = req->PersistentFileId; - rsp->VolatileFileId = req->VolatileFileId; - nbytes = sizeof(struct resume_key_ioctl_rsp); - break; - case FSCTL_COPYCHUNK: - case FSCTL_COPYCHUNK_WRITE: - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - ret = -EACCES; - goto out; - } - - if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { - ret = -EINVAL; - goto out; - } - - if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { - ret = -EINVAL; - goto out; - } - - nbytes = sizeof(struct copychunk_ioctl_rsp); - rsp->VolatileFileId = req->VolatileFileId; - rsp->PersistentFileId = req->PersistentFileId; - fsctl_copychunk(work, - (struct copychunk_ioctl_req *)&req->Buffer[0], - le32_to_cpu(req->CtlCode), - le32_to_cpu(req->InputCount), - req->VolatileFileId, - req->PersistentFileId, - rsp); - break; - case FSCTL_SET_SPARSE: - if (in_buf_len < sizeof(struct file_sparse)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_set_sparse(work, id, - (struct file_sparse *)&req->Buffer[0]); - if (ret < 0) - goto out; - break; - case FSCTL_SET_ZERO_DATA: - { - struct file_zero_data_information *zero_data; - struct ksmbd_file *fp; - loff_t off, len, bfz; - - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - ret = -EACCES; - goto out; - } - - if (in_buf_len < sizeof(struct file_zero_data_information)) { - ret = -EINVAL; - goto out; - } - - zero_data = - (struct file_zero_data_information *)&req->Buffer[0]; - - off = le64_to_cpu(zero_data->FileOffset); - bfz = le64_to_cpu(zero_data->BeyondFinalZero); - if (off < 0 || bfz < 0 || off > bfz) { - ret = -EINVAL; - goto out; - } - - len = bfz - off; - if (len) { - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) { - ret = -ENOENT; - goto out; - } - - ret = ksmbd_vfs_zero_data(work, fp, off, len); - ksmbd_fd_put(work, fp); - if (ret < 0) - goto out; - } - break; - } - case FSCTL_QUERY_ALLOCATED_RANGES: - if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_query_allocated_ranges(work, id, - (struct file_allocated_range_buffer *)&req->Buffer[0], - (struct file_allocated_range_buffer *)&rsp->Buffer[0], - out_buf_len / - sizeof(struct file_allocated_range_buffer), &nbytes); - if (ret == -E2BIG) { - rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - } else if (ret < 0) { - nbytes = 0; - goto out; - } - - nbytes *= sizeof(struct file_allocated_range_buffer); - break; - case FSCTL_GET_REPARSE_POINT: - { - struct reparse_data_buffer *reparse_ptr; - struct ksmbd_file *fp; - - reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) { - pr_err("not found fp!!\n"); - ret = -ENOENT; - goto out; - } - - reparse_ptr->ReparseTag = - smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); - reparse_ptr->ReparseDataLength = 0; - ksmbd_fd_put(work, fp); - nbytes = sizeof(struct reparse_data_buffer); - break; - } - case FSCTL_DUPLICATE_EXTENTS_TO_FILE: - { - struct ksmbd_file *fp_in, *fp_out = NULL; - struct duplicate_extents_to_file *dup_ext; - loff_t src_off, dst_off, length, cloned; - - if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { - ret = -EINVAL; - goto out; - } - - dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; - - fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, - dup_ext->PersistentFileHandle); - if (!fp_in) { - pr_err("not found file handle in duplicate extent to file\n"); - ret = -ENOENT; - goto out; - } - - fp_out = ksmbd_lookup_fd_fast(work, id); - if (!fp_out) { - pr_err("not found fp\n"); - ret = -ENOENT; - goto dup_ext_out; - } - - src_off = le64_to_cpu(dup_ext->SourceFileOffset); - dst_off = le64_to_cpu(dup_ext->TargetFileOffset); - length = le64_to_cpu(dup_ext->ByteCount); - /* - * XXX: It is not clear if FSCTL_DUPLICATE_EXTENTS_TO_FILE - * should fall back to vfs_copy_file_range(). This could be - * beneficial when re-exporting nfs/smb mount, but note that - * this can result in partial copy that returns an error status. - * If/when FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX is implemented, - * fall back to vfs_copy_file_range(), should be avoided when - * the flag DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC is set. - */ - cloned = vfs_clone_file_range(fp_in->filp, src_off, - fp_out->filp, dst_off, length, 0); - if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { - ret = -EOPNOTSUPP; - goto dup_ext_out; - } else if (cloned != length) { - cloned = vfs_copy_file_range(fp_in->filp, src_off, - fp_out->filp, dst_off, - length, 0); - if (cloned != length) { - if (cloned < 0) - ret = cloned; - else - ret = -EINVAL; - } - } - -dup_ext_out: - ksmbd_fd_put(work, fp_in); - ksmbd_fd_put(work, fp_out); - if (ret < 0) - goto out; - break; - } - default: - ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", - cnt_code); - ret = -EOPNOTSUPP; - goto out; - } - - rsp->CtlCode = cpu_to_le32(cnt_code); - rsp->InputCount = cpu_to_le32(0); - rsp->InputOffset = cpu_to_le32(112); - rsp->OutputOffset = cpu_to_le32(112); - rsp->OutputCount = cpu_to_le32(nbytes); - rsp->StructureSize = cpu_to_le16(49); - rsp->Reserved = cpu_to_le16(0); - rsp->Flags = cpu_to_le32(0); - rsp->Reserved2 = cpu_to_le32(0); - inc_rfc1001_len(work->response_buf, 48 + nbytes); - - return 0; - -out: - if (ret == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (ret == -ENOENT) - rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; - else if (ret == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (ret == -ENOSPC) - rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; - else if (ret < 0 || rsp->hdr.Status == 0) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - smb2_set_err_rsp(work); - return 0; -} - -/** - * smb20_oplock_break_ack() - handler for smb2.0 oplock break command - * @work: smb work containing oplock break command buffer - * - * Return: 0 - */ -static void smb20_oplock_break_ack(struct ksmbd_work *work) -{ - struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); - struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_file *fp; - struct oplock_info *opinfo = NULL; - __le32 err = 0; - int ret = 0; - u64 volatile_id, persistent_id; - char req_oplevel = 0, rsp_oplevel = 0; - unsigned int oplock_change_type; - - volatile_id = req->VolatileFid; - persistent_id = req->PersistentFid; - req_oplevel = req->OplockLevel; - ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", - volatile_id, persistent_id, req_oplevel); - - fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); - if (!fp) { - rsp->hdr.Status = STATUS_FILE_CLOSED; - smb2_set_err_rsp(work); - return; - } - - opinfo = opinfo_get(fp); - if (!opinfo) { - pr_err("unexpected null oplock_info\n"); - rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return; - } - - if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { - rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; - goto err_out; - } - - if (opinfo->op_state == OPLOCK_STATE_NONE) { - ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - goto err_out; - } - - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - (req_oplevel != SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_READ_TO_NONE; - } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_DEVICE_STATE; - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_II) { - oplock_change_type = OPLOCK_WRITE_TO_READ; - } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_READ_TO_NONE; - } else { - oplock_change_type = 0; - } - } else { - oplock_change_type = 0; - } - - switch (oplock_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_II; - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - default: - pr_err("unknown oplock change 0x%x -> 0x%x\n", - opinfo->level, rsp_oplevel); - } - - if (ret < 0) { - rsp->hdr.Status = err; - goto err_out; - } - - opinfo_put(opinfo); - ksmbd_fd_put(work, fp); - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - - rsp->StructureSize = cpu_to_le16(24); - rsp->OplockLevel = rsp_oplevel; - rsp->Reserved = 0; - rsp->Reserved2 = 0; - rsp->VolatileFid = volatile_id; - rsp->PersistentFid = persistent_id; - inc_rfc1001_len(work->response_buf, 24); - return; - -err_out: - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - - opinfo_put(opinfo); - ksmbd_fd_put(work, fp); - smb2_set_err_rsp(work); -} - -static int check_lease_state(struct lease *lease, __le32 req_state) -{ - if ((lease->new_state == - (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && - !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { - lease->new_state = req_state; - return 0; - } - - if (lease->new_state == req_state) - return 0; - - return 1; -} - -/** - * smb21_lease_break_ack() - handler for smb2.1 lease break command - * @work: smb work containing lease break command buffer - * - * Return: 0 - */ -static void smb21_lease_break_ack(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_lease_ack *req = smb2_get_msg(work->request_buf); - struct smb2_lease_ack *rsp = smb2_get_msg(work->response_buf); - struct oplock_info *opinfo; - __le32 err = 0; - int ret = 0; - unsigned int lease_change_type; - __le32 lease_state; - struct lease *lease; - - ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", - le32_to_cpu(req->LeaseState)); - opinfo = lookup_lease_in_table(conn, req->LeaseKey); - if (!opinfo) { - ksmbd_debug(OPLOCK, "file not opened\n"); - smb2_set_err_rsp(work); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - return; - } - lease = opinfo->o_lease; - - if (opinfo->op_state == OPLOCK_STATE_NONE) { - pr_err("unexpected lease break state 0x%x\n", - opinfo->op_state); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - goto err_out; - } - - if (check_lease_state(lease, req->LeaseState)) { - rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; - ksmbd_debug(OPLOCK, - "req lease state: 0x%x, expected state: 0x%x\n", - req->LeaseState, lease->new_state); - goto err_out; - } - - if (!atomic_read(&opinfo->breaking_cnt)) { - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - goto err_out; - } - - /* check for bad lease state */ - if (req->LeaseState & - (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && - req->LeaseState != SMB2_LEASE_NONE_LE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else { - /* valid lease state changes */ - err = STATUS_INVALID_DEVICE_STATE; - if (req->LeaseState == SMB2_LEASE_NONE_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_READ; - else - lease_change_type = OPLOCK_READ_HANDLE_TO_READ; - } else { - lease_change_type = 0; - } - } - - switch (lease_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - break; - case OPLOCK_READ_HANDLE_TO_READ: - ret = opinfo_read_handle_to_read(opinfo); - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - break; - default: - ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } - - lease_state = lease->state; - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - atomic_dec(&opinfo->breaking_cnt); - wake_up_interruptible_all(&opinfo->oplock_brk); - opinfo_put(opinfo); - - if (ret < 0) { - rsp->hdr.Status = err; - goto err_out; - } - - rsp->StructureSize = cpu_to_le16(36); - rsp->Reserved = 0; - rsp->Flags = 0; - memcpy(rsp->LeaseKey, req->LeaseKey, 16); - rsp->LeaseState = lease_state; - rsp->LeaseDuration = 0; - inc_rfc1001_len(work->response_buf, 36); - return; - -err_out: - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - atomic_dec(&opinfo->breaking_cnt); - wake_up_interruptible_all(&opinfo->oplock_brk); - - opinfo_put(opinfo); - smb2_set_err_rsp(work); -} - -/** - * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break - * @work: smb work containing oplock/lease break command buffer - * - * Return: 0 - */ -int smb2_oplock_break(struct ksmbd_work *work) -{ - struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); - struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); - - switch (le16_to_cpu(req->StructureSize)) { - case OP_BREAK_STRUCT_SIZE_20: - smb20_oplock_break_ack(work); - break; - case OP_BREAK_STRUCT_SIZE_21: - smb21_lease_break_ack(work); - break; - default: - ksmbd_debug(OPLOCK, "invalid break cmd %d\n", - le16_to_cpu(req->StructureSize)); - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - smb2_set_err_rsp(work); - } - - return 0; -} - -/** - * smb2_notify() - handler for smb2 notify request - * @work: smb work containing notify command buffer - * - * Return: 0 - */ -int smb2_notify(struct ksmbd_work *work) -{ - struct smb2_change_notify_req *req; - struct smb2_change_notify_rsp *rsp; - - WORK_BUFFERS(work, req, rsp); - - if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { - rsp->hdr.Status = STATUS_INTERNAL_ERROR; - smb2_set_err_rsp(work); - return 0; - } - - smb2_set_err_rsp(work); - rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; - return 0; -} - -/** - * smb2_is_sign_req() - handler for checking packet signing status - * @work: smb work containing notify command buffer - * @command: SMB2 command id - * - * Return: true if packed is signed, false otherwise - */ -bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) -{ - struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); - - if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && - command != SMB2_NEGOTIATE_HE && - command != SMB2_SESSION_SETUP_HE && - command != SMB2_OPLOCK_BREAK_HE) - return true; - - return false; -} - -/** - * smb2_check_sign_req() - handler for req packet sign processing - * @work: smb work containing notify command buffer - * - * Return: 1 on success, 0 otherwise - */ -int smb2_check_sign_req(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr; - char signature_req[SMB2_SIGNATURE_SIZE]; - char signature[SMB2_HMACSHA256_SIZE]; - struct kvec iov[1]; - size_t len; - - hdr = smb2_get_msg(work->request_buf); - if (work->next_smb2_rcv_hdr_off) - hdr = ksmbd_req_buf_next(work); - - if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = get_rfc1002_len(work->request_buf); - else if (hdr->NextCommand) - len = le32_to_cpu(hdr->NextCommand); - else - len = get_rfc1002_len(work->request_buf) - - work->next_smb2_rcv_hdr_off; - - memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - - if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, - signature)) - return 0; - - if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { - pr_err("bad smb2 signature\n"); - return 0; - } - - return 1; -} - -/** - * smb2_set_sign_rsp() - handler for rsp packet sign processing - * @work: smb work containing notify command buffer - * - */ -void smb2_set_sign_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr; - struct smb2_hdr *req_hdr; - char signature[SMB2_HMACSHA256_SIZE]; - struct kvec iov[2]; - size_t len; - int n_vec = 1; - - hdr = smb2_get_msg(work->response_buf); - if (work->next_smb2_rsp_hdr_off) - hdr = ksmbd_resp_buf_next(work); - - req_hdr = ksmbd_req_buf_next(work); - - if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(work->response_buf); - if (req_hdr->NextCommand) - len = ALIGN(len, 8); - } else { - len = get_rfc1002_len(work->response_buf) - - work->next_smb2_rsp_hdr_off; - len = ALIGN(len, 8); - } - - if (req_hdr->NextCommand) - hdr->NextCommand = cpu_to_le32(len); - - hdr->Flags |= SMB2_FLAGS_SIGNED; - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - - if (work->aux_payload_sz) { - iov[0].iov_len -= work->aux_payload_sz; - - iov[1].iov_base = work->aux_payload_buf; - iov[1].iov_len = work->aux_payload_sz; - n_vec++; - } - - if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, - signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); -} - -/** - * smb3_check_sign_req() - handler for req packet sign processing - * @work: smb work containing notify command buffer - * - * Return: 1 on success, 0 otherwise - */ -int smb3_check_sign_req(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - char *signing_key; - struct smb2_hdr *hdr; - struct channel *chann; - char signature_req[SMB2_SIGNATURE_SIZE]; - char signature[SMB2_CMACAES_SIZE]; - struct kvec iov[1]; - size_t len; - - hdr = smb2_get_msg(work->request_buf); - if (work->next_smb2_rcv_hdr_off) - hdr = ksmbd_req_buf_next(work); - - if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = get_rfc1002_len(work->request_buf); - else if (hdr->NextCommand) - len = le32_to_cpu(hdr->NextCommand); - else - len = get_rfc1002_len(work->request_buf) - - work->next_smb2_rcv_hdr_off; - - if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { - signing_key = work->sess->smb3signingkey; - } else { - chann = lookup_chann_list(work->sess, conn); - if (!chann) { - return 0; - } - signing_key = chann->smb3signingkey; - } - - if (!signing_key) { - pr_err("SMB3 signing key is not generated\n"); - return 0; - } - - memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - - if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) - return 0; - - if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { - pr_err("bad smb2 signature\n"); - return 0; - } - - return 1; -} - -/** - * smb3_set_sign_rsp() - handler for rsp packet sign processing - * @work: smb work containing notify command buffer - * - */ -void smb3_set_sign_rsp(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *req_hdr, *hdr; - struct channel *chann; - char signature[SMB2_CMACAES_SIZE]; - struct kvec iov[2]; - int n_vec = 1; - size_t len; - char *signing_key; - - hdr = smb2_get_msg(work->response_buf); - if (work->next_smb2_rsp_hdr_off) - hdr = ksmbd_resp_buf_next(work); - - req_hdr = ksmbd_req_buf_next(work); - - if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(work->response_buf); - if (req_hdr->NextCommand) - len = ALIGN(len, 8); - } else { - len = get_rfc1002_len(work->response_buf) - - work->next_smb2_rsp_hdr_off; - len = ALIGN(len, 8); - } - - if (conn->binding == false && - le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { - signing_key = work->sess->smb3signingkey; - } else { - chann = lookup_chann_list(work->sess, work->conn); - if (!chann) { - return; - } - signing_key = chann->smb3signingkey; - } - - if (!signing_key) - return; - - if (req_hdr->NextCommand) - hdr->NextCommand = cpu_to_le32(len); - - hdr->Flags |= SMB2_FLAGS_SIGNED; - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - if (work->aux_payload_sz) { - iov[0].iov_len -= work->aux_payload_sz; - iov[1].iov_base = work->aux_payload_buf; - iov[1].iov_len = work->aux_payload_sz; - n_vec++; - } - - if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); -} - -/** - * smb3_preauth_hash_rsp() - handler for computing preauth hash on response - * @work: smb work containing response buffer - * - */ -void smb3_preauth_hash_rsp(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct smb2_hdr *req, *rsp; - - if (conn->dialect != SMB311_PROT_ID) - return; - - WORK_BUFFERS(work, req, rsp); - - if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && - conn->preauth_info) - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - conn->preauth_info->Preauth_HashValue); - - if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { - __u8 *hash_value; - - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) - return; - hash_value = preauth_sess->Preauth_HashValue; - } else { - hash_value = sess->Preauth_HashValue; - if (!hash_value) - return; - } - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - hash_value); - } -} - -static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) -{ - struct smb2_transform_hdr *tr_hdr = tr_buf + 4; - struct smb2_hdr *hdr = smb2_get_msg(old_buf); - unsigned int orig_len = get_rfc1002_len(old_buf); - - /* tr_buf must be cleared by the caller */ - tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; - tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); - tr_hdr->Flags = cpu_to_le16(TRANSFORM_FLAG_ENCRYPTED); - if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || - cipher_type == SMB2_ENCRYPTION_AES256_GCM) - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - else - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); - inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); - inc_rfc1001_len(tr_buf, orig_len); -} - -int smb3_encrypt_resp(struct ksmbd_work *work) -{ - char *buf = work->response_buf; - struct kvec iov[3]; - int rc = -ENOMEM; - int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); - - if (ARRAY_SIZE(iov) < rq_nvec) - return -ENOMEM; - - work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); - if (!work->tr_buf) - return rc; - - /* fill transform header */ - fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); - - iov[0].iov_base = work->tr_buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; - buf_size += iov[0].iov_len - 4; - - iov[1].iov_base = buf + 4; - iov[1].iov_len = get_rfc1002_len(buf); - if (work->aux_payload_sz) { - iov[1].iov_len = work->resp_hdr_sz - 4; - - iov[2].iov_base = work->aux_payload_buf; - iov[2].iov_len = work->aux_payload_sz; - buf_size += iov[2].iov_len; - } - buf_size += iov[1].iov_len; - work->resp_hdr_sz = iov[1].iov_len; - - rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); - if (rc) - return rc; - - memmove(buf, iov[1].iov_base, iov[1].iov_len); - *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); - - return rc; -} - -bool smb3_is_transform_hdr(void *buf) -{ - struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); - - return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; -} - -int smb3_decrypt_req(struct ksmbd_work *work) -{ - struct ksmbd_session *sess; - char *buf = work->request_buf; - unsigned int pdu_length = get_rfc1002_len(buf); - struct kvec iov[2]; - int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); - struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); - int rc = 0; - - if (buf_data_size < sizeof(struct smb2_hdr)) { - pr_err("Transform message is too small (%u)\n", - pdu_length); - return -ECONNABORTED; - } - - if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { - pr_err("Transform message is broken\n"); - return -ECONNABORTED; - } - - sess = ksmbd_session_lookup_all(work->conn, le64_to_cpu(tr_hdr->SessionId)); - if (!sess) { - pr_err("invalid session id(%llx) in transform header\n", - le64_to_cpu(tr_hdr->SessionId)); - return -ECONNABORTED; - } - - iov[0].iov_base = buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; - iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; - iov[1].iov_len = buf_data_size; - rc = ksmbd_crypt_message(work, iov, 2, 0); - if (rc) - return rc; - - memmove(buf + 4, iov[1].iov_base, buf_data_size); - *(__be32 *)buf = cpu_to_be32(buf_data_size); - - return rc; -} - -bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); - - if (conn->dialect < SMB30_PROT_ID) - return false; - - if (work->next_smb2_rcv_hdr_off) - rsp = ksmbd_resp_buf_next(work); - - if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && - sess->user && !user_guest(sess->user) && - rsp->Status == STATUS_SUCCESS) - return true; - return false; -} diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h deleted file mode 100644 index dd10f8031606..000000000000 --- a/fs/ksmbd/smb2pdu.h +++ /dev/null @@ -1,536 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef _SMB2PDU_H -#define _SMB2PDU_H - -#include "ntlmssp.h" -#include "smbacl.h" - -/*Create Action Flags*/ -#define FILE_SUPERSEDED 0x00000000 -#define FILE_OPENED 0x00000001 -#define FILE_CREATED 0x00000002 -#define FILE_OVERWRITTEN 0x00000003 - -/* SMB2 Max Credits */ -#define SMB2_MAX_CREDITS 8192 - -/* BB FIXME - analyze following length BB */ -#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ - -#define SMB21_DEFAULT_IOSIZE (1024 * 1024) -#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) -#define SMB3_MIN_IOSIZE (64 * 1024) -#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) -#define SMB3_MAX_MSGSIZE (4 * 4096) - -/* - * Definitions for SMB2 Protocol Data Units (network frames) - * - * See MS-SMB2.PDF specification for protocol details. - * The Naming convention is the lower case version of the SMB2 - * command code name for the struct. Note that structures must be packed. - * - */ - -struct preauth_integrity_info { - /* PreAuth integrity Hash ID */ - __le16 Preauth_HashId; - /* PreAuth integrity Hash Value */ - __u8 Preauth_HashValue[SMB2_PREAUTH_HASH_SIZE]; -}; - -/* offset is sizeof smb2_negotiate_rsp but rounded up to 8 bytes. */ -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -/* sizeof(struct smb2_negotiate_rsp) = - * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) - */ -#define OFFSET_OF_NEG_CONTEXT 0xe0 -#else -/* sizeof(struct smb2_negotiate_rsp) = - * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) - */ -#define OFFSET_OF_NEG_CONTEXT 0xd0 -#endif - -#define SMB2_SESSION_EXPIRED (0) -#define SMB2_SESSION_IN_PROGRESS BIT(0) -#define SMB2_SESSION_VALID BIT(1) - -#define SMB2_SESSION_TIMEOUT (10 * HZ) - -struct create_durable_req_v2 { - struct create_context ccontext; - __u8 Name[8]; - __le32 Timeout; - __le32 Flags; - __u8 Reserved[8]; - __u8 CreateGuid[16]; -} __packed; - -struct create_durable_reconn_req { - struct create_context ccontext; - __u8 Name[8]; - union { - __u8 Reserved[16]; - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - } Data; -} __packed; - -struct create_durable_reconn_v2_req { - struct create_context ccontext; - __u8 Name[8]; - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - __u8 CreateGuid[16]; - __le32 Flags; -} __packed; - -struct create_app_inst_id { - struct create_context ccontext; - __u8 Name[8]; - __u8 Reserved[8]; - __u8 AppInstanceId[16]; -} __packed; - -struct create_app_inst_id_vers { - struct create_context ccontext; - __u8 Name[8]; - __u8 Reserved[2]; - __u8 Padding[4]; - __le64 AppInstanceVersionHigh; - __le64 AppInstanceVersionLow; -} __packed; - -struct create_mxac_req { - struct create_context ccontext; - __u8 Name[8]; - __le64 Timestamp; -} __packed; - -struct create_alloc_size_req { - struct create_context ccontext; - __u8 Name[8]; - __le64 AllocationSize; -} __packed; - -struct create_durable_rsp { - struct create_context ccontext; - __u8 Name[8]; - union { - __u8 Reserved[8]; - __u64 data; - } Data; -} __packed; - -struct create_durable_v2_rsp { - struct create_context ccontext; - __u8 Name[8]; - __le32 Timeout; - __le32 Flags; -} __packed; - -struct create_mxac_rsp { - struct create_context ccontext; - __u8 Name[8]; - __le32 QueryStatus; - __le32 MaximalAccess; -} __packed; - -struct create_disk_id_rsp { - struct create_context ccontext; - __u8 Name[8]; - __le64 DiskFileId; - __le64 VolumeId; - __u8 Reserved[16]; -} __packed; - -/* equivalent of the contents of SMB3.1.1 POSIX open context response */ -struct create_posix_rsp { - struct create_context ccontext; - __u8 Name[16]; - __le32 nlink; - __le32 reparse_tag; - __le32 mode; - /* SidBuffer contain two sids(Domain sid(28), UNIX group sid(16)) */ - u8 SidBuffer[44]; -} __packed; - -struct smb2_buffer_desc_v1 { - __le64 offset; - __le32 token; - __le32 length; -} __packed; - -#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 - -struct smb_sockaddr_in { - __be16 Port; - __be32 IPv4address; - __u8 Reserved[8]; -} __packed; - -struct smb_sockaddr_in6 { - __be16 Port; - __be32 FlowInfo; - __u8 IPv6address[16]; - __be32 ScopeId; -} __packed; - -#define INTERNETWORK 0x0002 -#define INTERNETWORKV6 0x0017 - -struct sockaddr_storage_rsp { - __le16 Family; - union { - struct smb_sockaddr_in addr4; - struct smb_sockaddr_in6 addr6; - }; -} __packed; - -#define RSS_CAPABLE 0x00000001 -#define RDMA_CAPABLE 0x00000002 - -struct network_interface_info_ioctl_rsp { - __le32 Next; /* next interface. zero if this is last one */ - __le32 IfIndex; - __le32 Capability; /* RSS or RDMA Capable */ - __le32 Reserved; - __le64 LinkSpeed; - char SockAddr_Storage[128]; -} __packed; - -struct file_object_buf_type1_ioctl_rsp { - __u8 ObjectId[16]; - __u8 BirthVolumeId[16]; - __u8 BirthObjectId[16]; - __u8 DomainId[16]; -} __packed; - -struct resume_key_ioctl_rsp { - __u64 ResumeKey[3]; - __le32 ContextLength; - __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ -} __packed; - -struct copychunk_ioctl_req { - __le64 ResumeKey[3]; - __le32 ChunkCount; - __le32 Reserved; - __u8 Chunks[1]; /* array of srv_copychunk */ -} __packed; - -struct srv_copychunk { - __le64 SourceOffset; - __le64 TargetOffset; - __le32 Length; - __le32 Reserved; -} __packed; - -struct copychunk_ioctl_rsp { - __le32 ChunksWritten; - __le32 ChunkBytesWritten; - __le32 TotalBytesWritten; -} __packed; - -struct file_sparse { - __u8 SetSparse; -} __packed; - -/* FILE Info response size */ -#define FILE_DIRECTORY_INFORMATION_SIZE 1 -#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 -#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3 -#define FILE_BASIC_INFORMATION_SIZE 40 -#define FILE_STANDARD_INFORMATION_SIZE 24 -#define FILE_INTERNAL_INFORMATION_SIZE 8 -#define FILE_EA_INFORMATION_SIZE 4 -#define FILE_ACCESS_INFORMATION_SIZE 4 -#define FILE_NAME_INFORMATION_SIZE 9 -#define FILE_RENAME_INFORMATION_SIZE 10 -#define FILE_LINK_INFORMATION_SIZE 11 -#define FILE_NAMES_INFORMATION_SIZE 12 -#define FILE_DISPOSITION_INFORMATION_SIZE 13 -#define FILE_POSITION_INFORMATION_SIZE 14 -#define FILE_FULL_EA_INFORMATION_SIZE 15 -#define FILE_MODE_INFORMATION_SIZE 4 -#define FILE_ALIGNMENT_INFORMATION_SIZE 4 -#define FILE_ALL_INFORMATION_SIZE 104 -#define FILE_ALLOCATION_INFORMATION_SIZE 19 -#define FILE_END_OF_FILE_INFORMATION_SIZE 20 -#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8 -#define FILE_STREAM_INFORMATION_SIZE 32 -#define FILE_PIPE_INFORMATION_SIZE 23 -#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24 -#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25 -#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26 -#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27 -#define FILE_COMPRESSION_INFORMATION_SIZE 16 -#define FILE_OBJECT_ID_INFORMATION_SIZE 29 -/* Number 30 not defined in documents */ -#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31 -#define FILE_QUOTA_INFORMATION_SIZE 32 -#define FILE_REPARSE_POINT_INFORMATION_SIZE 33 -#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 -#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 - -/* FS Info response size */ -#define FS_DEVICE_INFORMATION_SIZE 8 -#define FS_ATTRIBUTE_INFORMATION_SIZE 16 -#define FS_VOLUME_INFORMATION_SIZE 24 -#define FS_SIZE_INFORMATION_SIZE 24 -#define FS_FULL_SIZE_INFORMATION_SIZE 32 -#define FS_SECTOR_SIZE_INFORMATION_SIZE 28 -#define FS_OBJECT_ID_INFORMATION_SIZE 64 -#define FS_CONTROL_INFORMATION_SIZE 48 -#define FS_POSIX_INFORMATION_SIZE 56 - -/* FS_ATTRIBUTE_File_System_Name */ -#define FS_TYPE_SUPPORT_SIZE 44 -struct fs_type_info { - char *fs_name; - long magic_number; -} __packed; - -/* - * PDU query infolevel structure definitions - * BB consider moving to a different header - */ - -struct smb2_file_access_info { - __le32 AccessFlags; -} __packed; - -struct smb2_file_alignment_info { - __le32 AlignmentRequirement; -} __packed; - -struct smb2_file_basic_info { /* data block encoding of response to level 18 */ - __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ -} __packed; - -struct smb2_file_alt_name_info { - __le32 FileNameLength; - char FileName[]; -} __packed; - -struct smb2_file_stream_info { - __le32 NextEntryOffset; - __le32 StreamNameLength; - __le64 StreamSize; - __le64 StreamAllocationSize; - char StreamName[]; -} __packed; - -struct smb2_file_ntwrk_info { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndOfFile; - __le32 Attributes; - __le32 Reserved; -} __packed; - -struct smb2_file_standard_info { - __le64 AllocationSize; - __le64 EndOfFile; - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __le16 Reserved; -} __packed; /* level 18 Query */ - -struct smb2_file_ea_info { - __le32 EASize; -} __packed; - -struct smb2_file_alloc_info { - __le64 AllocationSize; -} __packed; - -struct smb2_file_disposition_info { - __u8 DeletePending; -} __packed; - -struct smb2_file_pos_info { - __le64 CurrentByteOffset; -} __packed; - -#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000100e) - -struct smb2_file_mode_info { - __le32 Mode; -} __packed; - -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_LZNT1 0x0002 - -struct smb2_file_comp_info { - __le64 CompressedFileSize; - __le16 CompressionFormat; - __u8 CompressionUnitShift; - __u8 ChunkShift; - __u8 ClusterShift; - __u8 Reserved[3]; -} __packed; - -struct smb2_file_attr_tag_info { - __le32 FileAttributes; - __le32 ReparseTag; -} __packed; - -#define SL_RESTART_SCAN 0x00000001 -#define SL_RETURN_SINGLE_ENTRY 0x00000002 -#define SL_INDEX_SPECIFIED 0x00000004 - -struct smb2_ea_info_req { - __le32 NextEntryOffset; - __u8 EaNameLength; - char name[1]; -} __packed; /* level 15 Query */ - -struct smb2_ea_info { - __le32 NextEntryOffset; - __u8 Flags; - __u8 EaNameLength; - __le16 EaValueLength; - char name[1]; - /* optionally followed by value */ -} __packed; /* level 15 Query */ - -struct create_ea_buf_req { - struct create_context ccontext; - __u8 Name[8]; - struct smb2_ea_info ea; -} __packed; - -struct create_sd_buf_req { - struct create_context ccontext; - __u8 Name[8]; - struct smb_ntsd ntsd; -} __packed; - -struct smb2_posix_info { - __le32 NextEntryOffset; - __u32 Ignored; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 DosAttributes; - __le64 Inode; - __le32 DeviceId; - __le32 Zero; - /* beginning of POSIX Create Context Response */ - __le32 HardLinks; - __le32 ReparseTag; - __le32 Mode; - /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */ - u8 SidBuffer[32]; - __le32 name_len; - u8 name[1]; - /* - * var sized owner SID - * var sized group SID - * le32 filenamelength - * u8 filename[] - */ -} __packed; - -/* functions */ -void init_smb2_1_server(struct ksmbd_conn *conn); -void init_smb3_0_server(struct ksmbd_conn *conn); -void init_smb3_02_server(struct ksmbd_conn *conn); -int init_smb3_11_server(struct ksmbd_conn *conn); - -void init_smb2_max_read_size(unsigned int sz); -void init_smb2_max_write_size(unsigned int sz); -void init_smb2_max_trans_size(unsigned int sz); -void init_smb2_max_credits(unsigned int sz); - -bool is_smb2_neg_cmd(struct ksmbd_work *work); -bool is_smb2_rsp(struct ksmbd_work *work); - -u16 get_smb2_cmd_val(struct ksmbd_work *work); -void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); -int init_smb2_rsp_hdr(struct ksmbd_work *work); -int smb2_allocate_rsp_buf(struct ksmbd_work *work); -bool is_chained_smb2_message(struct ksmbd_work *work); -int init_smb2_neg_rsp(struct ksmbd_work *work); -void smb2_set_err_rsp(struct ksmbd_work *work); -int smb2_check_user_session(struct ksmbd_work *work); -int smb2_get_ksmbd_tcon(struct ksmbd_work *work); -bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); -int smb2_check_sign_req(struct ksmbd_work *work); -void smb2_set_sign_rsp(struct ksmbd_work *work); -int smb3_check_sign_req(struct ksmbd_work *work); -void smb3_set_sign_rsp(struct ksmbd_work *work); -int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, - __le16 dialects_count); -struct file_lock *smb_flock_init(struct file *f); -int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), - void **arg); -void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); -struct channel *lookup_chann_list(struct ksmbd_session *sess, - struct ksmbd_conn *conn); -void smb3_preauth_hash_rsp(struct ksmbd_work *work); -bool smb3_is_transform_hdr(void *buf); -int smb3_decrypt_req(struct ksmbd_work *work); -int smb3_encrypt_resp(struct ksmbd_work *work); -bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); -int smb2_set_rsp_credits(struct ksmbd_work *work); -bool smb3_encryption_negotiated(struct ksmbd_conn *conn); - -/* smb2 misc functions */ -int ksmbd_smb2_check_message(struct ksmbd_work *work); - -/* smb2 command handlers */ -int smb2_handle_negotiate(struct ksmbd_work *work); -int smb2_negotiate_request(struct ksmbd_work *work); -int smb2_sess_setup(struct ksmbd_work *work); -int smb2_tree_connect(struct ksmbd_work *work); -int smb2_tree_disconnect(struct ksmbd_work *work); -int smb2_session_logoff(struct ksmbd_work *work); -int smb2_open(struct ksmbd_work *work); -int smb2_query_info(struct ksmbd_work *work); -int smb2_query_dir(struct ksmbd_work *work); -int smb2_close(struct ksmbd_work *work); -int smb2_echo(struct ksmbd_work *work); -int smb2_set_info(struct ksmbd_work *work); -int smb2_read(struct ksmbd_work *work); -int smb2_write(struct ksmbd_work *work); -int smb2_flush(struct ksmbd_work *work); -int smb2_cancel(struct ksmbd_work *work); -int smb2_lock(struct ksmbd_work *work); -int smb2_ioctl(struct ksmbd_work *work); -int smb2_oplock_break(struct ksmbd_work *work); -int smb2_notify(struct ksmbd_work *ksmbd_work); - -/* - * Get the body of the smb2 message excluding the 4 byte rfc1002 headers - * from request/response buffer. - */ -static inline void *smb2_get_msg(void *buf) -{ - return buf + 4; -} - -#endif /* _SMB2PDU_H */ diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c deleted file mode 100644 index 05d7f3e910bf..000000000000 --- a/fs/ksmbd/smb_common.c +++ /dev/null @@ -1,797 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - * Copyright (C) 2018 Namjae Jeon - */ - -#include - -#include "smb_common.h" -#include "server.h" -#include "misc.h" -#include "smbstatus.h" -#include "connection.h" -#include "ksmbd_work.h" -#include "mgmt/user_session.h" -#include "mgmt/user_config.h" -#include "mgmt/tree_connect.h" -#include "mgmt/share_config.h" - -/*for shortname implementation */ -static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; -#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) -#define MAGIC_CHAR '~' -#define PERIOD '.' -#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) - -struct smb_protocol { - int index; - char *name; - char *prot; - __u16 prot_id; -}; - -static struct smb_protocol smb1_protos[] = { - { - SMB21_PROT, - "\2SMB 2.1", - "SMB2_10", - SMB21_PROT_ID - }, - { - SMB2X_PROT, - "\2SMB 2.???", - "SMB2_22", - SMB2X_PROT_ID - }, -}; - -static struct smb_protocol smb2_protos[] = { - { - SMB21_PROT, - "\2SMB 2.1", - "SMB2_10", - SMB21_PROT_ID - }, - { - SMB30_PROT, - "\2SMB 3.0", - "SMB3_00", - SMB30_PROT_ID - }, - { - SMB302_PROT, - "\2SMB 3.02", - "SMB3_02", - SMB302_PROT_ID - }, - { - SMB311_PROT, - "\2SMB 3.1.1", - "SMB3_11", - SMB311_PROT_ID - }, -}; - -unsigned int ksmbd_server_side_copy_max_chunk_count(void) -{ - return 256; -} - -unsigned int ksmbd_server_side_copy_max_chunk_size(void) -{ - return (2U << 30) - 1; -} - -unsigned int ksmbd_server_side_copy_max_total_size(void) -{ - return (2U << 30) - 1; -} - -inline int ksmbd_min_protocol(void) -{ - return SMB21_PROT; -} - -inline int ksmbd_max_protocol(void) -{ - return SMB311_PROT; -} - -int ksmbd_lookup_protocol_idx(char *str) -{ - int offt = ARRAY_SIZE(smb1_protos) - 1; - int len = strlen(str); - - while (offt >= 0) { - if (!strncmp(str, smb1_protos[offt].prot, len)) { - ksmbd_debug(SMB, "selected %s dialect idx = %d\n", - smb1_protos[offt].prot, offt); - return smb1_protos[offt].index; - } - offt--; - } - - offt = ARRAY_SIZE(smb2_protos) - 1; - while (offt >= 0) { - if (!strncmp(str, smb2_protos[offt].prot, len)) { - ksmbd_debug(SMB, "selected %s dialect idx = %d\n", - smb2_protos[offt].prot, offt); - return smb2_protos[offt].index; - } - offt--; - } - return -1; -} - -/** - * ksmbd_verify_smb_message() - check for valid smb2 request header - * @work: smb work - * - * check for valid smb signature and packet direction(request/response) - * - * Return: 0 on success, otherwise -EINVAL - */ -int ksmbd_verify_smb_message(struct ksmbd_work *work) -{ - struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); - struct smb_hdr *hdr; - - if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) - return ksmbd_smb2_check_message(work); - - hdr = work->request_buf; - if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && - hdr->Command == SMB_COM_NEGOTIATE) { - work->conn->outstanding_credits++; - return 0; - } - - return -EINVAL; -} - -/** - * ksmbd_smb_request() - check for valid smb request type - * @conn: connection instance - * - * Return: true on success, otherwise false - */ -bool ksmbd_smb_request(struct ksmbd_conn *conn) -{ - __le32 *proto = (__le32 *)smb2_get_msg(conn->request_buf); - - if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { - pr_err_ratelimited("smb2 compression not support yet"); - return false; - } - - if (*proto != SMB1_PROTO_NUMBER && - *proto != SMB2_PROTO_NUMBER && - *proto != SMB2_TRANSFORM_PROTO_NUM) - return false; - - return true; -} - -static bool supported_protocol(int idx) -{ - if (idx == SMB2X_PROT && - (server_conf.min_protocol >= SMB21_PROT || - server_conf.max_protocol <= SMB311_PROT)) - return true; - - return (server_conf.min_protocol <= idx && - idx <= server_conf.max_protocol); -} - -static char *next_dialect(char *dialect, int *next_off, int bcount) -{ - dialect = dialect + *next_off; - *next_off = strnlen(dialect, bcount); - if (dialect[*next_off] != '\0') - return NULL; - return dialect; -} - -static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) -{ - int i, seq_num, bcount, next; - char *dialect; - - for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { - seq_num = 0; - next = 0; - dialect = cli_dialects; - bcount = le16_to_cpu(byte_count); - do { - dialect = next_dialect(dialect, &next, bcount); - if (!dialect) - break; - ksmbd_debug(SMB, "client requested dialect %s\n", - dialect); - if (!strcmp(dialect, smb1_protos[i].name)) { - if (supported_protocol(smb1_protos[i].index)) { - ksmbd_debug(SMB, - "selected %s dialect\n", - smb1_protos[i].name); - if (smb1_protos[i].index == SMB1_PROT) - return seq_num; - return smb1_protos[i].prot_id; - } - } - seq_num++; - bcount -= (++next); - } while (bcount > 0); - } - - return BAD_PROT_ID; -} - -int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) -{ - int i; - int count; - - for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { - count = le16_to_cpu(dialects_count); - while (--count >= 0) { - ksmbd_debug(SMB, "client requested dialect 0x%x\n", - le16_to_cpu(cli_dialects[count])); - if (le16_to_cpu(cli_dialects[count]) != - smb2_protos[i].prot_id) - continue; - - if (supported_protocol(smb2_protos[i].index)) { - ksmbd_debug(SMB, "selected %s dialect\n", - smb2_protos[i].name); - return smb2_protos[i].prot_id; - } - } - } - - return BAD_PROT_ID; -} - -static int ksmbd_negotiate_smb_dialect(void *buf) -{ - int smb_buf_length = get_rfc1002_len(buf); - __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; - - if (proto == SMB2_PROTO_NUMBER) { - struct smb2_negotiate_req *req; - int smb2_neg_size = - offsetof(struct smb2_negotiate_req, Dialects); - - req = (struct smb2_negotiate_req *)smb2_get_msg(buf); - if (smb2_neg_size > smb_buf_length) - goto err_out; - - if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > - smb_buf_length) - goto err_out; - - return ksmbd_lookup_dialect_by_id(req->Dialects, - req->DialectCount); - } - - proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; - if (proto == SMB1_PROTO_NUMBER) { - struct smb_negotiate_req *req; - - req = (struct smb_negotiate_req *)buf; - if (le16_to_cpu(req->ByteCount) < 2) - goto err_out; - - if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + - le16_to_cpu(req->ByteCount) > smb_buf_length) { - goto err_out; - } - - return ksmbd_lookup_dialect_by_name(req->DialectsArray, - req->ByteCount); - } - -err_out: - return BAD_PROT_ID; -} - -#define SMB_COM_NEGOTIATE_EX 0x0 - -/** - * get_smb1_cmd_val() - get smb command value from smb header - * @work: smb work containing smb header - * - * Return: smb command value - */ -static u16 get_smb1_cmd_val(struct ksmbd_work *work) -{ - return SMB_COM_NEGOTIATE_EX; -} - -/** - * init_smb1_rsp_hdr() - initialize smb negotiate response header - * @work: smb work containing smb request - * - * Return: 0 on success, otherwise -EINVAL - */ -static int init_smb1_rsp_hdr(struct ksmbd_work *work) -{ - struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; - struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; - - /* - * Remove 4 byte direct TCP header. - */ - *(__be32 *)work->response_buf = - cpu_to_be32(sizeof(struct smb_hdr) - 4); - - rsp_hdr->Command = SMB_COM_NEGOTIATE; - *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; - rsp_hdr->Flags = SMBFLG_RESPONSE; - rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS | - SMBFLG2_EXT_SEC | SMBFLG2_IS_LONG_NAME; - rsp_hdr->Pid = rcv_hdr->Pid; - rsp_hdr->Mid = rcv_hdr->Mid; - return 0; -} - -/** - * smb1_check_user_session() - check for valid session for a user - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise error - */ -static int smb1_check_user_session(struct ksmbd_work *work) -{ - unsigned int cmd = work->conn->ops->get_cmd_val(work); - - if (cmd == SMB_COM_NEGOTIATE_EX) - return 0; - - return -EINVAL; -} - -/** - * smb1_allocate_rsp_buf() - allocate response buffer for a command - * @work: smb work containing smb request - * - * Return: 0 on success, otherwise -ENOMEM - */ -static int smb1_allocate_rsp_buf(struct ksmbd_work *work) -{ - work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE, - GFP_KERNEL | __GFP_ZERO); - work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - - if (!work->response_buf) { - pr_err("Failed to allocate %u bytes buffer\n", - MAX_CIFS_SMALL_BUFFER_SIZE); - return -ENOMEM; - } - - return 0; -} - -static struct smb_version_ops smb1_server_ops = { - .get_cmd_val = get_smb1_cmd_val, - .init_rsp_hdr = init_smb1_rsp_hdr, - .allocate_rsp_buf = smb1_allocate_rsp_buf, - .check_user_session = smb1_check_user_session, -}; - -static int smb1_negotiate(struct ksmbd_work *work) -{ - return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE); -} - -static struct smb_version_cmds smb1_server_cmds[1] = { - [SMB_COM_NEGOTIATE_EX] = { .proc = smb1_negotiate, }, -}; - -static void init_smb1_server(struct ksmbd_conn *conn) -{ - conn->ops = &smb1_server_ops; - conn->cmds = smb1_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); -} - -void ksmbd_init_smb_server(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - __le32 proto; - - if (conn->need_neg == false) - return; - - proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol; - if (proto == SMB1_PROTO_NUMBER) - init_smb1_server(conn); - else - init_smb3_11_server(conn); -} - -int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, - struct ksmbd_file *dir, - struct ksmbd_dir_info *d_info, - char *search_pattern, - int (*fn)(struct ksmbd_conn *, int, - struct ksmbd_dir_info *, - struct ksmbd_kstat *)) -{ - int i, rc = 0; - struct ksmbd_conn *conn = work->conn; - struct user_namespace *user_ns = file_mnt_user_ns(dir->filp); - - for (i = 0; i < 2; i++) { - struct kstat kstat; - struct ksmbd_kstat ksmbd_kstat; - struct dentry *dentry; - - if (!dir->dot_dotdot[i]) { /* fill dot entry info */ - if (i == 0) { - d_info->name = "."; - d_info->name_len = 1; - dentry = dir->filp->f_path.dentry; - } else { - d_info->name = ".."; - d_info->name_len = 2; - dentry = dir->filp->f_path.dentry->d_parent; - } - - if (!match_pattern(d_info->name, d_info->name_len, - search_pattern)) { - dir->dot_dotdot[i] = 1; - continue; - } - - ksmbd_kstat.kstat = &kstat; - ksmbd_vfs_fill_dentry_attrs(work, - user_ns, - dentry, - &ksmbd_kstat); - rc = fn(conn, info_level, d_info, &ksmbd_kstat); - if (rc) - break; - if (d_info->out_buf_len <= 0) - break; - - dir->dot_dotdot[i] = 1; - if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { - d_info->out_buf_len = 0; - break; - } - } - } - - return rc; -} - -/** - * ksmbd_extract_shortname() - get shortname from long filename - * @conn: connection instance - * @longname: source long filename - * @shortname: destination short filename - * - * Return: shortname length or 0 when source long name is '.' or '..' - * TODO: Though this function comforms the restriction of 8.3 Filename spec, - * but the result is different with Windows 7's one. need to check. - */ -int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, - char *shortname) -{ - const char *p; - char base[9], extension[4]; - char out[13] = {0}; - int baselen = 0; - int extlen = 0, len = 0; - unsigned int csum = 0; - const unsigned char *ptr; - bool dot_present = true; - - p = longname; - if ((*p == '.') || (!(strcmp(p, "..")))) { - /*no mangling required */ - return 0; - } - - p = strrchr(longname, '.'); - if (p == longname) { /*name starts with a dot*/ - strscpy(extension, "___", strlen("___")); - } else { - if (p) { - p++; - while (*p && extlen < 3) { - if (*p != '.') - extension[extlen++] = toupper(*p); - p++; - } - extension[extlen] = '\0'; - } else { - dot_present = false; - } - } - - p = longname; - if (*p == '.') { - p++; - longname++; - } - while (*p && (baselen < 5)) { - if (*p != '.') - base[baselen++] = toupper(*p); - p++; - } - - base[baselen] = MAGIC_CHAR; - memcpy(out, base, baselen + 1); - - ptr = longname; - len = strlen(longname); - for (; len > 0; len--, ptr++) - csum += *ptr; - - csum = csum % (MANGLE_BASE * MANGLE_BASE); - out[baselen + 1] = mangle(csum / MANGLE_BASE); - out[baselen + 2] = mangle(csum); - out[baselen + 3] = PERIOD; - - if (dot_present) - memcpy(&out[baselen + 4], extension, 4); - else - out[baselen + 4] = '\0'; - smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, - conn->local_nls, 0); - len = strlen(out) * 2; - return len; -} - -static int __smb2_negotiate(struct ksmbd_conn *conn) -{ - return (conn->dialect >= SMB20_PROT_ID && - conn->dialect <= SMB311_PROT_ID); -} - -static int smb_handle_negotiate(struct ksmbd_work *work) -{ - struct smb_negotiate_rsp *neg_rsp = work->response_buf; - - ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); - - /* Add 2 byte bcc and 2 byte DialectIndex. */ - inc_rfc1001_len(work->response_buf, 4); - neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; - - neg_rsp->hdr.WordCount = 1; - neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); - neg_rsp->ByteCount = 0; - return 0; -} - -int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) -{ - struct ksmbd_conn *conn = work->conn; - int ret; - - conn->dialect = - ksmbd_negotiate_smb_dialect(work->request_buf); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - - if (command == SMB2_NEGOTIATE_HE) { - ret = smb2_handle_negotiate(work); - return ret; - } - - if (command == SMB_COM_NEGOTIATE) { - if (__smb2_negotiate(conn)) { - init_smb3_11_server(conn); - init_smb2_neg_rsp(work); - ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); - return 0; - } - return smb_handle_negotiate(work); - } - - pr_err("Unknown SMB negotiation command: %u\n", command); - return -EINVAL; -} - -enum SHARED_MODE_ERRORS { - SHARE_DELETE_ERROR, - SHARE_READ_ERROR, - SHARE_WRITE_ERROR, - FILE_READ_ERROR, - FILE_WRITE_ERROR, - FILE_DELETE_ERROR, -}; - -static const char * const shared_mode_errors[] = { - "Current access mode does not permit SHARE_DELETE", - "Current access mode does not permit SHARE_READ", - "Current access mode does not permit SHARE_WRITE", - "Desired access mode does not permit FILE_READ", - "Desired access mode does not permit FILE_WRITE", - "Desired access mode does not permit FILE_DELETE", -}; - -static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, - struct ksmbd_file *curr_fp) -{ - ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); - ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", - prev_fp->saccess, curr_fp->daccess); -} - -int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) -{ - int rc = 0; - struct ksmbd_file *prev_fp; - - /* - * Lookup fp in master fp list, and check desired access and - * shared mode between previous open and current open. - */ - read_lock(&curr_fp->f_ci->m_lock); - list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { - if (file_inode(filp) != file_inode(prev_fp->filp)) - continue; - - if (filp == prev_fp->filp) - continue; - - if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) - if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) - continue; - - if (prev_fp->attrib_only != curr_fp->attrib_only) - continue; - - if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && - curr_fp->daccess & FILE_DELETE_LE) { - smb_shared_mode_error(SHARE_DELETE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - /* - * Only check FILE_SHARE_DELETE if stream opened and - * normal file opened. - */ - if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) - continue; - - if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && - curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { - smb_shared_mode_error(SHARE_READ_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && - curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { - smb_shared_mode_error(SHARE_WRITE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && - !(curr_fp->saccess & FILE_SHARE_READ_LE)) { - smb_shared_mode_error(FILE_READ_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && - !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { - smb_shared_mode_error(FILE_WRITE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (prev_fp->daccess & FILE_DELETE_LE && - !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { - smb_shared_mode_error(FILE_DELETE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - } - read_unlock(&curr_fp->f_ci->m_lock); - - return rc; -} - -bool is_asterisk(char *p) -{ - return p && p[0] == '*'; -} - -int ksmbd_override_fsids(struct ksmbd_work *work) -{ - struct ksmbd_session *sess = work->sess; - struct ksmbd_share_config *share = work->tcon->share_conf; - struct cred *cred; - struct group_info *gi; - unsigned int uid; - unsigned int gid; - - uid = user_uid(sess->user); - gid = user_gid(sess->user); - if (share->force_uid != KSMBD_SHARE_INVALID_UID) - uid = share->force_uid; - if (share->force_gid != KSMBD_SHARE_INVALID_GID) - gid = share->force_gid; - - cred = prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; - - cred->fsuid = make_kuid(&init_user_ns, uid); - cred->fsgid = make_kgid(&init_user_ns, gid); - - gi = groups_alloc(0); - if (!gi) { - abort_creds(cred); - return -ENOMEM; - } - set_groups(cred, gi); - put_group_info(gi); - - if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) - cred->cap_effective = cap_drop_fs_set(cred->cap_effective); - - WARN_ON(work->saved_cred); - work->saved_cred = override_creds(cred); - if (!work->saved_cred) { - abort_creds(cred); - return -EINVAL; - } - return 0; -} - -void ksmbd_revert_fsids(struct ksmbd_work *work) -{ - const struct cred *cred; - - WARN_ON(!work->saved_cred); - - cred = current_cred(); - revert_creds(work->saved_cred); - put_cred(cred); - work->saved_cred = NULL; -} - -__le32 smb_map_generic_desired_access(__le32 daccess) -{ - if (daccess & FILE_GENERIC_READ_LE) { - daccess |= cpu_to_le32(GENERIC_READ_FLAGS); - daccess &= ~FILE_GENERIC_READ_LE; - } - - if (daccess & FILE_GENERIC_WRITE_LE) { - daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); - daccess &= ~FILE_GENERIC_WRITE_LE; - } - - if (daccess & FILE_GENERIC_EXECUTE_LE) { - daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); - daccess &= ~FILE_GENERIC_EXECUTE_LE; - } - - if (daccess & FILE_GENERIC_ALL_LE) { - daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); - daccess &= ~FILE_GENERIC_ALL_LE; - } - - return daccess; -} diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h deleted file mode 100644 index 78c44978a906..000000000000 --- a/fs/ksmbd/smb_common.h +++ /dev/null @@ -1,468 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __SMB_COMMON_H__ -#define __SMB_COMMON_H__ - -#include - -#include "glob.h" -#include "nterr.h" -#include "../smbfs_common/smb2pdu.h" -#include "smb2pdu.h" - -/* ksmbd's Specific ERRNO */ -#define ESHARE 50000 - -#define SMB1_PROT 0 -#define SMB2_PROT 1 -#define SMB21_PROT 2 -/* multi-protocol negotiate request */ -#define SMB2X_PROT 3 -#define SMB30_PROT 4 -#define SMB302_PROT 5 -#define SMB311_PROT 6 -#define BAD_PROT 0xFFFF - -#define SMB1_VERSION_STRING "1.0" -#define SMB20_VERSION_STRING "2.0" -#define SMB21_VERSION_STRING "2.1" -#define SMB30_VERSION_STRING "3.0" -#define SMB302_VERSION_STRING "3.02" -#define SMB311_VERSION_STRING "3.1.1" - -#define SMB_ECHO_INTERVAL (60 * HZ) - -#define CIFS_DEFAULT_IOSIZE (64 * 1024) -#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ - -#define MAX_STREAM_PROT_LEN 0x00FFFFFF - -/* Responses when opening a file. */ -#define F_SUPERSEDED 0 -#define F_OPENED 1 -#define F_CREATED 2 -#define F_OVERWRITTEN 3 - -/* - * File Attribute flags - */ -#define ATTR_POSIX_SEMANTICS 0x01000000 -#define ATTR_BACKUP_SEMANTICS 0x02000000 -#define ATTR_DELETE_ON_CLOSE 0x04000000 -#define ATTR_SEQUENTIAL_SCAN 0x08000000 -#define ATTR_RANDOM_ACCESS 0x10000000 -#define ATTR_NO_BUFFERING 0x20000000 -#define ATTR_WRITE_THROUGH 0x80000000 - -/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ -#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ -#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ -#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 -#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 -#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 -#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 -#define FILE_SUPPORTS_HARD_LINKS 0x00400000 -#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 -#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 -#define FILE_READ_ONLY_VOLUME 0x00080000 -#define FILE_NAMED_STREAMS 0x00040000 -#define FILE_SUPPORTS_ENCRYPTION 0x00020000 -#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 -#define FILE_VOLUME_IS_COMPRESSED 0x00008000 -#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 -#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 -#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 -#define FILE_VOLUME_QUOTAS 0x00000020 -#define FILE_FILE_COMPRESSION 0x00000010 -#define FILE_PERSISTENT_ACLS 0x00000008 -#define FILE_UNICODE_ON_DISK 0x00000004 -#define FILE_CASE_PRESERVED_NAMES 0x00000002 -#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 - -#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ -#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ -#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ -#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ -/* with the file can be read */ -#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ -/* with the file can be written */ -#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ -/* the file using system paging I/O */ -#define FILE_DELETE_CHILD 0x00000040 -#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ -/* file can be read */ -#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ -/* file can be written */ -#define DELETE 0x00010000 /* The file can be deleted */ -#define READ_CONTROL 0x00020000 /* The access control list and */ -/* ownership associated with the */ -/* file can be read */ -#define WRITE_DAC 0x00040000 /* The access control list and */ -/* ownership associated with the */ -/* file can be written. */ -#define WRITE_OWNER 0x00080000 /* Ownership information associated */ -/* with the file can be written */ -#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ -/* synchronize with the completion */ -/* of an input/output request */ -#define GENERIC_ALL 0x10000000 -#define GENERIC_EXECUTE 0x20000000 -#define GENERIC_WRITE 0x40000000 -#define GENERIC_READ 0x80000000 -/* In summary - Relevant file */ -/* access flags from CIFS are */ -/* file_read_data, file_write_data */ -/* file_execute, file_read_attributes*/ -/* write_dac, and delete. */ - -#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ - | FILE_READ_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA \ - | FILE_DELETE_CHILD \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) - -#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ - | READ_CONTROL | SYNCHRONIZE) - -/* generic flags for file open */ -#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ - FILE_READ_ATTRIBUTES | \ - FILE_READ_EA | SYNCHRONIZE) - -#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \ - FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ - FILE_APPEND_DATA | SYNCHRONIZE) - -#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \ - FILE_READ_ATTRIBUTES | SYNCHRONIZE) - -#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \ - WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \ - FILE_WRITE_DATA | FILE_APPEND_DATA | \ - FILE_READ_EA | FILE_WRITE_EA | \ - FILE_EXECUTE | FILE_DELETE_CHILD | \ - FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) - -#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) -#define SMB_COM_NEGOTIATE 0x72 -#define SMB1_CLIENT_GUID_SIZE (16) - -#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ - -#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) -#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) -#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) -#define SMBFLG2_UNICODE cpu_to_le16(0x8000) - -struct smb_hdr { - __be32 smb_buf_length; - __u8 Protocol[4]; - __u8 Command; - union { - struct { - __u8 ErrorClass; - __u8 Reserved; - __le16 Error; - } __packed DosError; - __le32 CifsError; - } __packed Status; - __u8 Flags; - __le16 Flags2; /* note: le */ - __le16 PidHigh; - union { - struct { - __le32 SequenceNumber; /* le */ - __u32 Reserved; /* zero */ - } __packed Sequence; - __u8 SecuritySignature[8]; /* le */ - } __packed Signature; - __u8 pad[2]; - __le16 Tid; - __le16 Pid; - __le16 Uid; - __le16 Mid; - __u8 WordCount; -} __packed; - -struct smb_negotiate_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - unsigned char DialectsArray[1]; -} __packed; - -struct smb_negotiate_rsp { - struct smb_hdr hdr; /* wct = 17 */ - __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ - __le16 ByteCount; -} __packed; - -struct filesystem_attribute_info { - __le32 Attributes; - __le32 MaxPathNameComponentLength; - __le32 FileSystemNameLen; - __le16 FileSystemName[1]; /* do not have to save this - get subset? */ -} __packed; - -struct filesystem_device_info { - __le32 DeviceType; - __le32 DeviceCharacteristics; -} __packed; /* device info level 0x104 */ - -struct filesystem_vol_info { - __le64 VolumeCreationTime; - __le32 SerialNumber; - __le32 VolumeLabelSize; - __le16 Reserved; - __le16 VolumeLabel[1]; -} __packed; - -struct filesystem_info { - __le64 TotalAllocationUnits; - __le64 FreeAllocationUnits; - __le32 SectorsPerAllocationUnit; - __le32 BytesPerSector; -} __packed; /* size info, level 0x103 */ - -#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ -#define STRING_LENGTH 28 - -struct fs_extended_info { - __le32 magic; - __le32 version; - __le32 release; - __u64 rel_date; - char version_string[STRING_LENGTH]; -} __packed; - -struct object_id_info { - char objid[16]; - struct fs_extended_info extended_info; -} __packed; - -struct file_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - char FileName[1]; -} __packed; /* level 0x101 FF resp data */ - -struct file_names_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le32 FileNameLength; - char FileName[1]; -} __packed; /* level 0xc FF resp data */ - -struct file_full_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; - char FileName[1]; -} __packed; /* level 0x102 FF resp */ - -struct file_both_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - __u8 ShortNameLength; - __u8 Reserved; - __u8 ShortName[24]; - char FileName[1]; -} __packed; /* level 0x104 FFrsp data */ - -struct file_id_both_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - __u8 ShortNameLength; - __u8 Reserved; - __u8 ShortName[24]; - __le16 Reserved2; - __le64 UniqueId; - char FileName[1]; -} __packed; - -struct file_id_full_dir_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* EA size */ - __le32 Reserved; - __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ - char FileName[1]; -} __packed; /* level 0x105 FF rsp data */ - -struct smb_version_values { - char *version_string; - __u16 protocol_id; - __le16 lock_cmd; - __u32 capabilities; - __u32 max_read_size; - __u32 max_write_size; - __u32 max_trans_size; - __u32 max_credits; - __u32 large_lock_type; - __u32 exclusive_lock_type; - __u32 shared_lock_type; - __u32 unlock_lock_type; - size_t header_size; - size_t max_header_size; - size_t read_rsp_size; - unsigned int cap_unix; - unsigned int cap_nt_find; - unsigned int cap_large_files; - __u16 signing_enabled; - __u16 signing_required; - size_t create_lease_size; - size_t create_durable_size; - size_t create_durable_v2_size; - size_t create_mxac_size; - size_t create_disk_id_size; - size_t create_posix_size; -}; - -struct filesystem_posix_info { - /* For undefined recommended transfer size return -1 in that field */ - __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ - __le32 BlockSize; - /* The next three fields are in terms of the block size. - * (above). If block size is unknown, 4096 would be a - * reasonable block size for a server to report. - * Note that returning the blocks/blocksavail removes need - * to make a second call (to QFSInfo level 0x103 to get this info. - * UserBlockAvail is typically less than or equal to BlocksAvail, - * if no distinction is made return the same value in each - */ - __le64 TotalBlocks; - __le64 BlocksAvail; /* bfree */ - __le64 UserBlocksAvail; /* bavail */ - /* For undefined Node fields or FSID return -1 */ - __le64 TotalFileNodes; - __le64 FreeFileNodes; - __le64 FileSysIdentifier; /* fsid */ - /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ - /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ -} __packed; - -struct smb_version_ops { - u16 (*get_cmd_val)(struct ksmbd_work *swork); - int (*init_rsp_hdr)(struct ksmbd_work *swork); - void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); - int (*allocate_rsp_buf)(struct ksmbd_work *work); - int (*set_rsp_credits)(struct ksmbd_work *work); - int (*check_user_session)(struct ksmbd_work *work); - int (*get_ksmbd_tcon)(struct ksmbd_work *work); - bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); - int (*check_sign_req)(struct ksmbd_work *work); - void (*set_sign_rsp)(struct ksmbd_work *work); - int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); - int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); - bool (*is_transform_hdr)(void *buf); - int (*decrypt_req)(struct ksmbd_work *work); - int (*encrypt_resp)(struct ksmbd_work *work); -}; - -struct smb_version_cmds { - int (*proc)(struct ksmbd_work *swork); -}; - -int ksmbd_min_protocol(void); -int ksmbd_max_protocol(void); - -int ksmbd_lookup_protocol_idx(char *str); - -int ksmbd_verify_smb_message(struct ksmbd_work *work); -bool ksmbd_smb_request(struct ksmbd_conn *conn); - -int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); - -void ksmbd_init_smb_server(struct ksmbd_work *work); - -struct ksmbd_kstat; -int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, - int info_level, - struct ksmbd_file *dir, - struct ksmbd_dir_info *d_info, - char *search_pattern, - int (*fn)(struct ksmbd_conn *, - int, - struct ksmbd_dir_info *, - struct ksmbd_kstat *)); - -int ksmbd_extract_shortname(struct ksmbd_conn *conn, - const char *longname, - char *shortname); - -int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); - -int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); -int ksmbd_override_fsids(struct ksmbd_work *work); -void ksmbd_revert_fsids(struct ksmbd_work *work); - -unsigned int ksmbd_server_side_copy_max_chunk_count(void); -unsigned int ksmbd_server_side_copy_max_chunk_size(void); -unsigned int ksmbd_server_side_copy_max_total_size(void); -bool is_asterisk(char *p); -__le32 smb_map_generic_desired_access(__le32 daccess); - -static inline unsigned int get_rfc1002_len(void *buf) -{ - return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; -} - -static inline void inc_rfc1001_len(void *buf, int count) -{ - be32_add_cpu((__be32 *)buf, count); -} -#endif /* __SMB_COMMON_H__ */ diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c deleted file mode 100644 index b05ff9b146b5..000000000000 --- a/fs/ksmbd/smbacl.c +++ /dev/null @@ -1,1436 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) International Business Machines Corp., 2007,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * Copyright (C) 2020 Samsung Electronics Co., Ltd. - * Author(s): Namjae Jeon - */ - -#include -#include -#include -#include - -#include "smbacl.h" -#include "smb_common.h" -#include "server.h" -#include "misc.h" -#include "mgmt/share_config.h" - -static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* security id for everyone/world system group */ -static const struct smb_sid creator_owner = { - 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; -/* security id for everyone/world system group */ -static const struct smb_sid creator_group = { - 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; - -/* security id for everyone/world system group */ -static const struct smb_sid sid_everyone = { - 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; -/* security id for Authenticated Users system group */ -static const struct smb_sid sid_authusers = { - 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; - -/* S-1-22-1 Unmapped Unix users */ -static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-22-2 Unmapped Unix groups */ -static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* - * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ - -/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ - -/* S-1-5-88-1 Unix uid */ -static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-2 Unix gid */ -static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-3 Unix mode */ -static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* - * if the two SIDs (roughly equivalent to a UUID for a user or group) are - * the same returns zero, if they do not match returns non-zero. - */ -int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) -{ - int i; - int num_subauth, num_sat, num_saw; - - if (!ctsid || !cwsid) - return 1; - - /* compare the revision */ - if (ctsid->revision != cwsid->revision) { - if (ctsid->revision > cwsid->revision) - return 1; - else - return -1; - } - - /* compare all of the six auth values */ - for (i = 0; i < NUM_AUTHS; ++i) { - if (ctsid->authority[i] != cwsid->authority[i]) { - if (ctsid->authority[i] > cwsid->authority[i]) - return 1; - else - return -1; - } - } - - /* compare all of the subauth values if any */ - num_sat = ctsid->num_subauth; - num_saw = cwsid->num_subauth; - num_subauth = num_sat < num_saw ? num_sat : num_saw; - if (num_subauth) { - for (i = 0; i < num_subauth; ++i) { - if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { - if (le32_to_cpu(ctsid->sub_auth[i]) > - le32_to_cpu(cwsid->sub_auth[i])) - return 1; - else - return -1; - } - } - } - - return 0; /* sids compare/match */ -} - -static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) -{ - int i; - - dst->revision = src->revision; - dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); - for (i = 0; i < NUM_AUTHS; ++i) - dst->authority[i] = src->authority[i]; - for (i = 0; i < dst->num_subauth; ++i) - dst->sub_auth[i] = src->sub_auth[i]; -} - -/* - * change posix mode to reflect permissions - * pmode is the existing mode (we only want to overwrite part of this - * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 - */ -static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, - int type) -{ - __u32 flags = le32_to_cpu(ace_flags); - umode_t mode = 0; - - if (flags & GENERIC_ALL) { - mode = 0777; - ksmbd_debug(SMB, "all perms\n"); - return mode; - } - - if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) - mode = 0444; - if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { - mode |= 0222; - if (S_ISDIR(fattr->cf_mode)) - mode |= 0111; - } - if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) - mode |= 0111; - - if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) - mode = ~mode; - - ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); - - return mode; -} - -/* - * Generate access flags to reflect permissions mode is the existing mode. - * This function is called for every ACE in the DACL whose SID matches - * with either owner or group or everyone. - */ -static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, - __u32 *pace_flags) -{ - /* reset access mask */ - *pace_flags = 0x0; - - /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ - mode &= bits_to_use; - - /* - * check for R/W/X UGO since we do not know whose flags - * is this but we have cleared all the bits sans RWX for - * either user or group or other as per bits_to_use - */ - if (mode & 0444) - *pace_flags |= SET_FILE_READ_RIGHTS; - if (mode & 0222) - *pace_flags |= FILE_WRITE_RIGHTS; - if (mode & 0111) - *pace_flags |= SET_FILE_EXEC_RIGHTS; - - ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", - mode, *pace_flags); -} - -static __u16 fill_ace_for_sid(struct smb_ace *pntace, - const struct smb_sid *psid, int type, int flags, - umode_t mode, umode_t bits) -{ - int i; - __u16 size = 0; - __u32 access_req = 0; - - pntace->type = type; - pntace->flags = flags; - mode_to_access_flags(mode, bits, &access_req); - if (!access_req) - access_req = SET_MINIMUM_RIGHTS; - pntace->access_req = cpu_to_le32(access_req); - - pntace->sid.revision = psid->revision; - pntace->sid.num_subauth = psid->num_subauth; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = psid->authority[i]; - for (i = 0; i < psid->num_subauth; i++) - pntace->sid.sub_auth[i] = psid->sub_auth[i]; - - size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); - pntace->size = cpu_to_le16(size); - - return size; -} - -void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) -{ - switch (sidtype) { - case SIDOWNER: - smb_copy_sid(ssid, &server_conf.domain_sid); - break; - case SIDUNIX_USER: - smb_copy_sid(ssid, &sid_unix_users); - break; - case SIDUNIX_GROUP: - smb_copy_sid(ssid, &sid_unix_groups); - break; - case SIDCREATOR_OWNER: - smb_copy_sid(ssid, &creator_owner); - return; - case SIDCREATOR_GROUP: - smb_copy_sid(ssid, &creator_group); - return; - case SIDNFS_USER: - smb_copy_sid(ssid, &sid_unix_NFS_users); - break; - case SIDNFS_GROUP: - smb_copy_sid(ssid, &sid_unix_NFS_groups); - break; - case SIDNFS_MODE: - smb_copy_sid(ssid, &sid_unix_NFS_mode); - break; - default: - return; - } - - /* RID */ - ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); - ssid->num_subauth++; -} - -static int sid_to_id(struct user_namespace *user_ns, - struct smb_sid *psid, uint sidtype, - struct smb_fattr *fattr) -{ - int rc = -EINVAL; - - /* - * If we have too many subauthorities, then something is really wrong. - * Just return an error. - */ - if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { - pr_err("%s: %u subauthorities is too many!\n", - __func__, psid->num_subauth); - return -EIO; - } - - if (sidtype == SIDOWNER) { - kuid_t uid; - uid_t id; - - id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); - uid = KUIDT_INIT(id); - uid = from_vfsuid(user_ns, &init_user_ns, VFSUIDT_INIT(uid)); - if (uid_valid(uid)) { - fattr->cf_uid = uid; - rc = 0; - } - } else { - kgid_t gid; - gid_t id; - - id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); - gid = KGIDT_INIT(id); - gid = from_vfsgid(user_ns, &init_user_ns, VFSGIDT_INIT(gid)); - if (gid_valid(gid)) { - fattr->cf_gid = gid; - rc = 0; - } - } - - return rc; -} - -void posix_state_to_acl(struct posix_acl_state *state, - struct posix_acl_entry *pace) -{ - int i; - - pace->e_tag = ACL_USER_OBJ; - pace->e_perm = state->owner.allow; - for (i = 0; i < state->users->n; i++) { - pace++; - pace->e_tag = ACL_USER; - pace->e_uid = state->users->aces[i].uid; - pace->e_perm = state->users->aces[i].perms.allow; - } - - pace++; - pace->e_tag = ACL_GROUP_OBJ; - pace->e_perm = state->group.allow; - - for (i = 0; i < state->groups->n; i++) { - pace++; - pace->e_tag = ACL_GROUP; - pace->e_gid = state->groups->aces[i].gid; - pace->e_perm = state->groups->aces[i].perms.allow; - } - - if (state->users->n || state->groups->n) { - pace++; - pace->e_tag = ACL_MASK; - pace->e_perm = state->mask.allow; - } - - pace++; - pace->e_tag = ACL_OTHER; - pace->e_perm = state->other.allow; -} - -int init_acl_state(struct posix_acl_state *state, int cnt) -{ - int alloc; - - memset(state, 0, sizeof(struct posix_acl_state)); - /* - * In the worst case, each individual acl could be for a distinct - * named user or group, but we don't know which, so we allocate - * enough space for either: - */ - alloc = sizeof(struct posix_ace_state_array) - + cnt * sizeof(struct posix_user_ace_state); - state->users = kzalloc(alloc, GFP_KERNEL); - if (!state->users) - return -ENOMEM; - state->groups = kzalloc(alloc, GFP_KERNEL); - if (!state->groups) { - kfree(state->users); - return -ENOMEM; - } - return 0; -} - -void free_acl_state(struct posix_acl_state *state) -{ - kfree(state->users); - kfree(state->groups); -} - -static void parse_dacl(struct user_namespace *user_ns, - struct smb_acl *pdacl, char *end_of_acl, - struct smb_sid *pownersid, struct smb_sid *pgrpsid, - struct smb_fattr *fattr) -{ - int i, ret; - int num_aces = 0; - unsigned int acl_size; - char *acl_base; - struct smb_ace **ppace; - struct posix_acl_entry *cf_pace, *cf_pdace; - struct posix_acl_state acl_state, default_acl_state; - umode_t mode = 0, acl_mode; - bool owner_found = false, group_found = false, others_found = false; - - if (!pdacl) - return; - - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || - end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { - pr_err("ACL too small to parse DACL\n"); - return; - } - - ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", - le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), - le32_to_cpu(pdacl->num_aces)); - - acl_base = (char *)pdacl; - acl_size = sizeof(struct smb_acl); - - num_aces = le32_to_cpu(pdacl->num_aces); - if (num_aces <= 0) - return; - - if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) - return; - - ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); - if (!ppace) - return; - - ret = init_acl_state(&acl_state, num_aces); - if (ret) - return; - ret = init_acl_state(&default_acl_state, num_aces); - if (ret) { - free_acl_state(&acl_state); - return; - } - - /* - * reset rwx permissions for user/group/other. - * Also, if num_aces is 0 i.e. DACL has no ACEs, - * user/group/other have no permissions - */ - for (i = 0; i < num_aces; ++i) { - if (end_of_acl - acl_base < acl_size) - break; - - ppace[i] = (struct smb_ace *)(acl_base + acl_size); - acl_base = (char *)ppace[i]; - acl_size = offsetof(struct smb_ace, sid) + - offsetof(struct smb_sid, sub_auth); - - if (end_of_acl - acl_base < acl_size || - ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || - (end_of_acl - acl_base < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || - (le16_to_cpu(ppace[i]->size) < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) - break; - - acl_size = le16_to_cpu(ppace[i]->size); - ppace[i]->access_req = - smb_map_generic_desired_access(ppace[i]->access_req); - - if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { - fattr->cf_mode = - le32_to_cpu(ppace[i]->sid.sub_auth[2]); - break; - } else if (!compare_sids(&ppace[i]->sid, pownersid)) { - acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, - ppace[i]->type); - acl_mode &= 0700; - - if (!owner_found) { - mode &= ~(0700); - mode |= acl_mode; - } - owner_found = true; - } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || - ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == - DOMAIN_USER_RID_LE) { - acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, - ppace[i]->type); - acl_mode &= 0070; - if (!group_found) { - mode &= ~(0070); - mode |= acl_mode; - } - group_found = true; - } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { - acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, - ppace[i]->type); - acl_mode &= 0007; - if (!others_found) { - mode &= ~(0007); - mode |= acl_mode; - } - others_found = true; - } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { - continue; - } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { - continue; - } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { - continue; - } else { - struct smb_fattr temp_fattr; - - acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, - ppace[i]->type); - temp_fattr.cf_uid = INVALID_UID; - ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); - if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { - pr_err("%s: Error %d mapping Owner SID to uid\n", - __func__, ret); - continue; - } - - acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; - acl_state.users->aces[acl_state.users->n].uid = - temp_fattr.cf_uid; - acl_state.users->aces[acl_state.users->n++].perms.allow = - ((acl_mode & 0700) >> 6) | 0004; - default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; - default_acl_state.users->aces[default_acl_state.users->n].uid = - temp_fattr.cf_uid; - default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = - ((acl_mode & 0700) >> 6) | 0004; - } - } - kfree(ppace); - - if (owner_found) { - /* The owner must be set to at least read-only. */ - acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; - acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; - acl_state.users->aces[acl_state.users->n++].perms.allow = - ((mode & 0700) >> 6) | 0004; - default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; - default_acl_state.users->aces[default_acl_state.users->n].uid = - fattr->cf_uid; - default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = - ((mode & 0700) >> 6) | 0004; - } - - if (group_found) { - acl_state.group.allow = (mode & 0070) >> 3; - acl_state.groups->aces[acl_state.groups->n].gid = - fattr->cf_gid; - acl_state.groups->aces[acl_state.groups->n++].perms.allow = - (mode & 0070) >> 3; - default_acl_state.group.allow = (mode & 0070) >> 3; - default_acl_state.groups->aces[default_acl_state.groups->n].gid = - fattr->cf_gid; - default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = - (mode & 0070) >> 3; - } - - if (others_found) { - fattr->cf_mode &= ~(0007); - fattr->cf_mode |= mode & 0007; - - acl_state.other.allow = mode & 0007; - default_acl_state.other.allow = mode & 0007; - } - - if (acl_state.users->n || acl_state.groups->n) { - acl_state.mask.allow = 0x07; - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - fattr->cf_acls = - posix_acl_alloc(acl_state.users->n + - acl_state.groups->n + 4, GFP_KERNEL); - if (fattr->cf_acls) { - cf_pace = fattr->cf_acls->a_entries; - posix_state_to_acl(&acl_state, cf_pace); - } - } - } - - if (default_acl_state.users->n || default_acl_state.groups->n) { - default_acl_state.mask.allow = 0x07; - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - fattr->cf_dacls = - posix_acl_alloc(default_acl_state.users->n + - default_acl_state.groups->n + 4, GFP_KERNEL); - if (fattr->cf_dacls) { - cf_pdace = fattr->cf_dacls->a_entries; - posix_state_to_acl(&default_acl_state, cf_pdace); - } - } - } - free_acl_state(&acl_state); - free_acl_state(&default_acl_state); -} - -static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, - struct smb_ace *pndace, - struct smb_fattr *fattr, u32 *num_aces, - u16 *size, u32 nt_aces_num) -{ - struct posix_acl_entry *pace; - struct smb_sid *sid; - struct smb_ace *ntace; - int i, j; - - if (!fattr->cf_acls) - goto posix_default_acl; - - pace = fattr->cf_acls->a_entries; - for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { - int flags = 0; - - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!sid) - break; - - if (pace->e_tag == ACL_USER) { - uid_t uid; - unsigned int sid_type = SIDOWNER; - - uid = posix_acl_uid_translate(user_ns, pace); - if (!uid) - sid_type = SIDUNIX_USER; - id_to_sid(uid, sid_type, sid); - } else if (pace->e_tag == ACL_GROUP) { - gid_t gid; - - gid = posix_acl_gid_translate(user_ns, pace); - id_to_sid(gid, SIDUNIX_GROUP, sid); - } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { - smb_copy_sid(sid, &sid_everyone); - } else { - kfree(sid); - continue; - } - ntace = pndace; - for (j = 0; j < nt_aces_num; j++) { - if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == - sid->sub_auth[sid->num_subauth - 1]) - goto pass_same_sid; - ntace = (struct smb_ace *)((char *)ntace + - le16_to_cpu(ntace->size)); - } - - if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) - flags = 0x03; - - ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, - pace->e_perm, 0777); - (*num_aces)++; - if (pace->e_tag == ACL_USER) - ntace->access_req |= - FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - - if (S_ISDIR(fattr->cf_mode) && - (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { - ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, - 0x03, pace->e_perm, 0777); - (*num_aces)++; - if (pace->e_tag == ACL_USER) - ntace->access_req |= - FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - } - -pass_same_sid: - kfree(sid); - } - - if (nt_aces_num) - return; - -posix_default_acl: - if (!fattr->cf_dacls) - return; - - pace = fattr->cf_dacls->a_entries; - for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!sid) - break; - - if (pace->e_tag == ACL_USER) { - uid_t uid; - - uid = posix_acl_uid_translate(user_ns, pace); - id_to_sid(uid, SIDCREATOR_OWNER, sid); - } else if (pace->e_tag == ACL_GROUP) { - gid_t gid; - - gid = posix_acl_gid_translate(user_ns, pace); - id_to_sid(gid, SIDCREATOR_GROUP, sid); - } else { - kfree(sid); - continue; - } - - ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, - pace->e_perm, 0777); - (*num_aces)++; - if (pace->e_tag == ACL_USER) - ntace->access_req |= - FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - kfree(sid); - } -} - -static void set_ntacl_dacl(struct user_namespace *user_ns, - struct smb_acl *pndacl, - struct smb_acl *nt_dacl, - unsigned int aces_size, - const struct smb_sid *pownersid, - const struct smb_sid *pgrpsid, - struct smb_fattr *fattr) -{ - struct smb_ace *ntace, *pndace; - int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; - unsigned short size = 0; - int i; - - pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); - if (nt_num_aces) { - ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); - for (i = 0; i < nt_num_aces; i++) { - unsigned short nt_ace_size; - - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - - nt_ace_size = le16_to_cpu(ntace->size); - if (nt_ace_size > aces_size) - break; - - memcpy((char *)pndace + size, ntace, nt_ace_size); - size += nt_ace_size; - aces_size -= nt_ace_size; - ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); - num_aces++; - } - } - - set_posix_acl_entries_dacl(user_ns, pndace, fattr, - &num_aces, &size, nt_num_aces); - pndacl->num_aces = cpu_to_le32(num_aces); - pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); -} - -static void set_mode_dacl(struct user_namespace *user_ns, - struct smb_acl *pndacl, struct smb_fattr *fattr) -{ - struct smb_ace *pace, *pndace; - u32 num_aces = 0; - u16 size = 0, ace_size = 0; - uid_t uid; - const struct smb_sid *sid; - - pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); - - if (fattr->cf_acls) { - set_posix_acl_entries_dacl(user_ns, pndace, fattr, - &num_aces, &size, num_aces); - goto out; - } - - /* owner RID */ - uid = from_kuid(&init_user_ns, fattr->cf_uid); - if (uid) - sid = &server_conf.domain_sid; - else - sid = &sid_unix_users; - ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, - fattr->cf_mode, 0700); - pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); - pace->size = cpu_to_le16(ace_size + 4); - size += le16_to_cpu(pace->size); - pace = (struct smb_ace *)((char *)pndace + size); - - /* Group RID */ - ace_size = fill_ace_for_sid(pace, &sid_unix_groups, - ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); - pace->sid.sub_auth[pace->sid.num_subauth++] = - cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); - pace->size = cpu_to_le16(ace_size + 4); - size += le16_to_cpu(pace->size); - pace = (struct smb_ace *)((char *)pndace + size); - num_aces = 3; - - if (S_ISDIR(fattr->cf_mode)) { - pace = (struct smb_ace *)((char *)pndace + size); - - /* creator owner */ - size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, - 0x0b, fattr->cf_mode, 0700); - pace = (struct smb_ace *)((char *)pndace + size); - - /* creator group */ - size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, - 0x0b, fattr->cf_mode, 0070); - pace = (struct smb_ace *)((char *)pndace + size); - num_aces = 5; - } - - /* other */ - size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, - fattr->cf_mode, 0007); - -out: - pndacl->num_aces = cpu_to_le32(num_aces); - pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); -} - -static int parse_sid(struct smb_sid *psid, char *end_of_acl) -{ - /* - * validate that we do not go past end of ACL - sid must be at least 8 - * bytes long (assuming no sub-auths - e.g. the null SID - */ - if (end_of_acl < (char *)psid + 8) { - pr_err("ACL too small to parse SID %p\n", psid); - return -EINVAL; - } - - return 0; -} - -/* Convert CIFS ACL to POSIX form */ -int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, - int acl_len, struct smb_fattr *fattr) -{ - int rc = 0; - struct smb_sid *owner_sid_ptr, *group_sid_ptr; - struct smb_acl *dacl_ptr; /* no need for SACL ptr */ - char *end_of_acl = ((char *)pntsd) + acl_len; - __u32 dacloffset; - int pntsd_type; - - if (!pntsd) - return -EIO; - - if (acl_len < sizeof(struct smb_ntsd)) - return -EINVAL; - - owner_sid_ptr = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - group_sid_ptr = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - dacloffset = le32_to_cpu(pntsd->dacloffset); - dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); - ksmbd_debug(SMB, - "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", - pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), - le32_to_cpu(pntsd->gsidoffset), - le32_to_cpu(pntsd->sacloffset), dacloffset); - - pntsd_type = le16_to_cpu(pntsd->type); - if (!(pntsd_type & DACL_PRESENT)) { - ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); - return rc; - } - - pntsd->type = cpu_to_le16(DACL_PRESENT); - - if (pntsd->osidoffset) { - rc = parse_sid(owner_sid_ptr, end_of_acl); - if (rc) { - pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); - return rc; - } - - rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); - if (rc) { - pr_err("%s: Error %d mapping Owner SID to uid\n", - __func__, rc); - owner_sid_ptr = NULL; - } - } - - if (pntsd->gsidoffset) { - rc = parse_sid(group_sid_ptr, end_of_acl); - if (rc) { - pr_err("%s: Error %d mapping Owner SID to gid\n", - __func__, rc); - return rc; - } - rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); - if (rc) { - pr_err("%s: Error %d mapping Group SID to gid\n", - __func__, rc); - group_sid_ptr = NULL; - } - } - - if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == - (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) - pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); - if (pntsd_type & DACL_PROTECTED) - pntsd->type |= cpu_to_le16(DACL_PROTECTED); - - if (dacloffset) { - parse_dacl(user_ns, dacl_ptr, end_of_acl, - owner_sid_ptr, group_sid_ptr, fattr); - } - - return 0; -} - -/* Convert permission bits from mode to equivalent CIFS ACL */ -int build_sec_desc(struct user_namespace *user_ns, - struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, - int ppntsd_size, int addition_info, __u32 *secdesclen, - struct smb_fattr *fattr) -{ - int rc = 0; - __u32 offset; - struct smb_sid *owner_sid_ptr, *group_sid_ptr; - struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; - struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ - uid_t uid; - gid_t gid; - unsigned int sid_type = SIDOWNER; - - nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!nowner_sid_ptr) - return -ENOMEM; - - uid = from_kuid(&init_user_ns, fattr->cf_uid); - if (!uid) - sid_type = SIDUNIX_USER; - id_to_sid(uid, sid_type, nowner_sid_ptr); - - ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!ngroup_sid_ptr) { - kfree(nowner_sid_ptr); - return -ENOMEM; - } - - gid = from_kgid(&init_user_ns, fattr->cf_gid); - id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); - - offset = sizeof(struct smb_ntsd); - pntsd->sacloffset = 0; - pntsd->revision = cpu_to_le16(1); - pntsd->type = cpu_to_le16(SELF_RELATIVE); - if (ppntsd) - pntsd->type |= ppntsd->type; - - if (addition_info & OWNER_SECINFO) { - pntsd->osidoffset = cpu_to_le32(offset); - owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); - smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); - offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); - } - - if (addition_info & GROUP_SECINFO) { - pntsd->gsidoffset = cpu_to_le32(offset); - group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); - smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); - offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); - } - - if (addition_info & DACL_SECINFO) { - pntsd->type |= cpu_to_le16(DACL_PRESENT); - dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); - dacl_ptr->revision = cpu_to_le16(2); - dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); - dacl_ptr->num_aces = 0; - - if (!ppntsd) { - set_mode_dacl(user_ns, dacl_ptr, fattr); - } else { - struct smb_acl *ppdacl_ptr; - unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); - int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset; - - if (!dacl_offset || - (dacl_offset + sizeof(struct smb_acl) > ppntsd_size)) - goto out; - - ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset); - ppdacl_size = le16_to_cpu(ppdacl_ptr->size); - if (ppdacl_size > ntacl_size || - ppdacl_size < sizeof(struct smb_acl)) - goto out; - - set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, - ntacl_size - sizeof(struct smb_acl), - nowner_sid_ptr, ngroup_sid_ptr, - fattr); - } - pntsd->dacloffset = cpu_to_le32(offset); - offset += le16_to_cpu(dacl_ptr->size); - } - -out: - kfree(nowner_sid_ptr); - kfree(ngroup_sid_ptr); - *secdesclen = offset; - return rc; -} - -static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, - u8 flags, __le32 access_req) -{ - ace->type = type; - ace->flags = flags; - ace->access_req = access_req; - smb_copy_sid(&ace->sid, sid); - ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); -} - -int smb_inherit_dacl(struct ksmbd_conn *conn, - const struct path *path, - unsigned int uid, unsigned int gid) -{ - const struct smb_sid *psid, *creator = NULL; - struct smb_ace *parent_aces, *aces; - struct smb_acl *parent_pdacl; - struct smb_ntsd *parent_pntsd = NULL; - struct smb_sid owner_sid, group_sid; - struct dentry *parent = path->dentry->d_parent; - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; - int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; - char *aces_base; - bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); - - pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, - parent, &parent_pntsd); - if (pntsd_size <= 0) - return -ENOENT; - dacloffset = le32_to_cpu(parent_pntsd->dacloffset); - if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) { - rc = -EINVAL; - goto free_parent_pntsd; - } - - parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); - acl_len = pntsd_size - dacloffset; - num_aces = le32_to_cpu(parent_pdacl->num_aces); - pntsd_type = le16_to_cpu(parent_pntsd->type); - pdacl_size = le16_to_cpu(parent_pdacl->size); - - if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) { - rc = -EINVAL; - goto free_parent_pntsd; - } - - aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); - if (!aces_base) { - rc = -ENOMEM; - goto free_parent_pntsd; - } - - aces = (struct smb_ace *)aces_base; - parent_aces = (struct smb_ace *)((char *)parent_pdacl + - sizeof(struct smb_acl)); - aces_size = acl_len - sizeof(struct smb_acl); - - if (pntsd_type & DACL_AUTO_INHERITED) - inherited_flags = INHERITED_ACE; - - for (i = 0; i < num_aces; i++) { - int pace_size; - - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - - pace_size = le16_to_cpu(parent_aces->size); - if (pace_size > aces_size) - break; - - aces_size -= pace_size; - - flags = parent_aces->flags; - if (!smb_inherit_flags(flags, is_dir)) - goto pass; - if (is_dir) { - flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); - if (!(flags & CONTAINER_INHERIT_ACE)) - flags |= INHERIT_ONLY_ACE; - if (flags & NO_PROPAGATE_INHERIT_ACE) - flags = 0; - } else { - flags = 0; - } - - if (!compare_sids(&creator_owner, &parent_aces->sid)) { - creator = &creator_owner; - id_to_sid(uid, SIDOWNER, &owner_sid); - psid = &owner_sid; - } else if (!compare_sids(&creator_group, &parent_aces->sid)) { - creator = &creator_group; - id_to_sid(gid, SIDUNIX_GROUP, &group_sid); - psid = &group_sid; - } else { - creator = NULL; - psid = &parent_aces->sid; - } - - if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { - smb_set_ace(aces, psid, parent_aces->type, inherited_flags, - parent_aces->access_req); - nt_size += le16_to_cpu(aces->size); - ace_cnt++; - aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); - flags |= INHERIT_ONLY_ACE; - psid = creator; - } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { - psid = &parent_aces->sid; - } - - smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, - parent_aces->access_req); - nt_size += le16_to_cpu(aces->size); - aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); - ace_cnt++; -pass: - parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size); - } - - if (nt_size > 0) { - struct smb_ntsd *pntsd; - struct smb_acl *pdacl; - struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; - int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; - - if (parent_pntsd->osidoffset) { - powner_sid = (struct smb_sid *)((char *)parent_pntsd + - le32_to_cpu(parent_pntsd->osidoffset)); - powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); - } - if (parent_pntsd->gsidoffset) { - pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + - le32_to_cpu(parent_pntsd->gsidoffset)); - pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); - } - - pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + - pgroup_sid_size + sizeof(struct smb_acl) + - nt_size, GFP_KERNEL); - if (!pntsd) { - rc = -ENOMEM; - goto free_aces_base; - } - - pntsd->revision = cpu_to_le16(1); - pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); - if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) - pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); - pntsd_size = sizeof(struct smb_ntsd); - pntsd->osidoffset = parent_pntsd->osidoffset; - pntsd->gsidoffset = parent_pntsd->gsidoffset; - pntsd->dacloffset = parent_pntsd->dacloffset; - - if (pntsd->osidoffset) { - struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - memcpy(owner_sid, powner_sid, powner_sid_size); - pntsd_size += powner_sid_size; - } - - if (pntsd->gsidoffset) { - struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - memcpy(group_sid, pgroup_sid, pgroup_sid_size); - pntsd_size += pgroup_sid_size; - } - - if (pntsd->dacloffset) { - struct smb_ace *pace; - - pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); - pdacl->revision = cpu_to_le16(2); - pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); - pdacl->num_aces = cpu_to_le32(ace_cnt); - pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); - memcpy(pace, aces_base, nt_size); - pntsd_size += sizeof(struct smb_acl) + nt_size; - } - - ksmbd_vfs_set_sd_xattr(conn, user_ns, - path->dentry, pntsd, pntsd_size); - kfree(pntsd); - } - -free_aces_base: - kfree(aces_base); -free_parent_pntsd: - kfree(parent_pntsd); - return rc; -} - -bool smb_inherit_flags(int flags, bool is_dir) -{ - if (!is_dir) - return (flags & OBJECT_INHERIT_ACE) != 0; - - if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) - return true; - - if (flags & CONTAINER_INHERIT_ACE) - return true; - return false; -} - -int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, - __le32 *pdaccess, int uid) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - struct smb_ntsd *pntsd = NULL; - struct smb_acl *pdacl; - struct posix_acl *posix_acls; - int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset; - struct smb_sid sid; - int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); - struct smb_ace *ace; - int i, found = 0; - unsigned int access_bits = 0; - struct smb_ace *others_ace = NULL; - struct posix_acl_entry *pa_entry; - unsigned int sid_type = SIDOWNER; - unsigned short ace_size; - - ksmbd_debug(SMB, "check permission using windows acl\n"); - pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, - path->dentry, &pntsd); - if (pntsd_size <= 0 || !pntsd) - goto err_out; - - dacl_offset = le32_to_cpu(pntsd->dacloffset); - if (!dacl_offset || - (dacl_offset + sizeof(struct smb_acl) > pntsd_size)) - goto err_out; - - pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); - acl_size = pntsd_size - dacl_offset; - pdacl_size = le16_to_cpu(pdacl->size); - - if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl)) - goto err_out; - - if (!pdacl->num_aces) { - if (!(pdacl_size - sizeof(struct smb_acl)) && - *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { - rc = -EACCES; - goto err_out; - } - goto err_out; - } - - if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { - granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | - DELETE; - - ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); - aces_size = acl_size - sizeof(struct smb_acl); - for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - ace_size = le16_to_cpu(ace->size); - if (ace_size > aces_size) - break; - aces_size -= ace_size; - granted |= le32_to_cpu(ace->access_req); - ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); - } - - if (!pdacl->num_aces) - granted = GENERIC_ALL_FLAGS; - } - - if (!uid) - sid_type = SIDUNIX_USER; - id_to_sid(uid, sid_type, &sid); - - ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); - aces_size = acl_size - sizeof(struct smb_acl); - for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - ace_size = le16_to_cpu(ace->size); - if (ace_size > aces_size) - break; - aces_size -= ace_size; - - if (!compare_sids(&sid, &ace->sid) || - !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { - found = 1; - break; - } - if (!compare_sids(&sid_everyone, &ace->sid)) - others_ace = ace; - - ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); - } - - if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { - granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | - DELETE; - - granted |= le32_to_cpu(ace->access_req); - - if (!pdacl->num_aces) - granted = GENERIC_ALL_FLAGS; - } - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); - if (posix_acls && !found) { - unsigned int id = -1; - - pa_entry = posix_acls->a_entries; - for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { - if (pa_entry->e_tag == ACL_USER) - id = posix_acl_uid_translate(user_ns, pa_entry); - else if (pa_entry->e_tag == ACL_GROUP) - id = posix_acl_gid_translate(user_ns, pa_entry); - else - continue; - - if (id == uid) { - mode_to_access_flags(pa_entry->e_perm, - 0777, - &access_bits); - if (!access_bits) - access_bits = - SET_MINIMUM_RIGHTS; - posix_acl_release(posix_acls); - goto check_access_bits; - } - } - } - if (posix_acls) - posix_acl_release(posix_acls); - } - - if (!found) { - if (others_ace) { - ace = others_ace; - } else { - ksmbd_debug(SMB, "Can't find corresponding sid\n"); - rc = -EACCES; - goto err_out; - } - } - - switch (ace->type) { - case ACCESS_ALLOWED_ACE_TYPE: - access_bits = le32_to_cpu(ace->access_req); - break; - case ACCESS_DENIED_ACE_TYPE: - case ACCESS_DENIED_CALLBACK_ACE_TYPE: - access_bits = le32_to_cpu(~ace->access_req); - break; - } - -check_access_bits: - if (granted & - ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { - ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", - granted, le32_to_cpu(ace->access_req)); - rc = -EACCES; - goto err_out; - } - - *pdaccess = cpu_to_le32(granted); -err_out: - kfree(pntsd); - return rc; -} - -int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check) -{ - int rc; - struct smb_fattr fattr = {{0}}; - struct inode *inode = d_inode(path->dentry); - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - struct iattr newattrs; - - fattr.cf_uid = INVALID_UID; - fattr.cf_gid = INVALID_GID; - fattr.cf_mode = inode->i_mode; - - rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); - if (rc) - goto out; - - newattrs.ia_valid = ATTR_CTIME; - if (!uid_eq(fattr.cf_uid, INVALID_UID)) { - newattrs.ia_valid |= ATTR_UID; - newattrs.ia_uid = fattr.cf_uid; - } - if (!gid_eq(fattr.cf_gid, INVALID_GID)) { - newattrs.ia_valid |= ATTR_GID; - newattrs.ia_gid = fattr.cf_gid; - } - newattrs.ia_valid |= ATTR_MODE; - newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); - - ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); - /* Update posix acls */ - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { - rc = set_posix_acl(user_ns, inode, - ACL_TYPE_ACCESS, fattr.cf_acls); - if (rc < 0) - ksmbd_debug(SMB, - "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) { - rc = set_posix_acl(user_ns, inode, - ACL_TYPE_DEFAULT, fattr.cf_dacls); - if (rc) - ksmbd_debug(SMB, - "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } - } - - inode_lock(inode); - rc = notify_change(user_ns, path->dentry, &newattrs, NULL); - inode_unlock(inode); - if (rc) - goto out; - - /* Check it only calling from SD BUFFER context */ - if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) - goto out; - - if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { - /* Update WinACL in xattr */ - ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); - ksmbd_vfs_set_sd_xattr(conn, user_ns, - path->dentry, pntsd, ntsd_len); - } - -out: - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - mark_inode_dirty(inode); - return rc; -} - -void ksmbd_init_domain(u32 *sub_auth) -{ - int i; - - memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); - for (i = 0; i < 3; ++i) - server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); -} diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h deleted file mode 100644 index 618f2e0236b3..000000000000 --- a/fs/ksmbd/smbacl.h +++ /dev/null @@ -1,238 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (c) International Business Machines Corp., 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * Modified by Namjae Jeon (linkinjeon@kernel.org) - */ - -#ifndef _SMBACL_H -#define _SMBACL_H - -#include -#include -#include -#include - -#include "mgmt/tree_connect.h" - -#define NUM_AUTHS (6) /* number of authority fields */ -#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ - -/* - * ACE types - see MS-DTYP 2.4.4.1 - */ -enum { - ACCESS_ALLOWED, - ACCESS_DENIED, -}; - -/* - * Security ID types - */ -enum { - SIDOWNER = 1, - SIDGROUP, - SIDCREATOR_OWNER, - SIDCREATOR_GROUP, - SIDUNIX_USER, - SIDUNIX_GROUP, - SIDNFS_USER, - SIDNFS_GROUP, - SIDNFS_MODE, -}; - -/* Revision for ACLs */ -#define SD_REVISION 1 - -/* Control flags for Security Descriptor */ -#define OWNER_DEFAULTED 0x0001 -#define GROUP_DEFAULTED 0x0002 -#define DACL_PRESENT 0x0004 -#define DACL_DEFAULTED 0x0008 -#define SACL_PRESENT 0x0010 -#define SACL_DEFAULTED 0x0020 -#define DACL_TRUSTED 0x0040 -#define SERVER_SECURITY 0x0080 -#define DACL_AUTO_INHERIT_REQ 0x0100 -#define SACL_AUTO_INHERIT_REQ 0x0200 -#define DACL_AUTO_INHERITED 0x0400 -#define SACL_AUTO_INHERITED 0x0800 -#define DACL_PROTECTED 0x1000 -#define SACL_PROTECTED 0x2000 -#define RM_CONTROL_VALID 0x4000 -#define SELF_RELATIVE 0x8000 - -/* ACE types - see MS-DTYP 2.4.4.1 */ -#define ACCESS_ALLOWED_ACE_TYPE 0x00 -#define ACCESS_DENIED_ACE_TYPE 0x01 -#define SYSTEM_AUDIT_ACE_TYPE 0x02 -#define SYSTEM_ALARM_ACE_TYPE 0x03 -#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 -#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 -#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 -#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 -#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 -#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 -#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A -#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B -#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C -#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D -#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ -#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F -#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ -#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 -#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 -#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 - -/* ACE flags */ -#define OBJECT_INHERIT_ACE 0x01 -#define CONTAINER_INHERIT_ACE 0x02 -#define NO_PROPAGATE_INHERIT_ACE 0x04 -#define INHERIT_ONLY_ACE 0x08 -#define INHERITED_ACE 0x10 -#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 -#define FAILED_ACCESS_ACE_FLAG 0x80 - -/* - * Maximum size of a string representation of a SID: - * - * The fields are unsigned values in decimal. So: - * - * u8: max 3 bytes in decimal - * u32: max 10 bytes in decimal - * - * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator - * - * For authority field, max is when all 6 values are non-zero and it must be - * represented in hex. So "-0x" + 12 hex digits. - * - * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') - */ -#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) -#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ - -#define DOMAIN_USER_RID_LE cpu_to_le32(513) - -struct ksmbd_conn; - -struct smb_ntsd { - __le16 revision; /* revision level */ - __le16 type; - __le32 osidoffset; - __le32 gsidoffset; - __le32 sacloffset; - __le32 dacloffset; -} __packed; - -struct smb_sid { - __u8 revision; /* revision level */ - __u8 num_subauth; - __u8 authority[NUM_AUTHS]; - __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ -} __packed; - -/* size of a struct cifs_sid, sans sub_auth array */ -#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) - -struct smb_acl { - __le16 revision; /* revision level */ - __le16 size; - __le32 num_aces; -} __packed; - -struct smb_ace { - __u8 type; - __u8 flags; - __le16 size; - __le32 access_req; - struct smb_sid sid; /* ie UUID of user or group who gets these perms */ -} __packed; - -struct smb_fattr { - kuid_t cf_uid; - kgid_t cf_gid; - umode_t cf_mode; - __le32 daccess; - struct posix_acl *cf_acls; - struct posix_acl *cf_dacls; -}; - -struct posix_ace_state { - u32 allow; - u32 deny; -}; - -struct posix_user_ace_state { - union { - kuid_t uid; - kgid_t gid; - }; - struct posix_ace_state perms; -}; - -struct posix_ace_state_array { - int n; - struct posix_user_ace_state aces[]; -}; - -/* - * while processing the nfsv4 ace, this maintains the partial permissions - * calculated so far: - */ - -struct posix_acl_state { - struct posix_ace_state owner; - struct posix_ace_state group; - struct posix_ace_state other; - struct posix_ace_state everyone; - struct posix_ace_state mask; /* deny unused in this case */ - struct posix_ace_state_array *users; - struct posix_ace_state_array *groups; -}; - -int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, - int acl_len, struct smb_fattr *fattr); -int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, - struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, - __u32 *secdesclen, struct smb_fattr *fattr); -int init_acl_state(struct posix_acl_state *state, int cnt); -void free_acl_state(struct posix_acl_state *state); -void posix_state_to_acl(struct posix_acl_state *state, - struct posix_acl_entry *pace); -int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); -bool smb_inherit_flags(int flags, bool is_dir); -int smb_inherit_dacl(struct ksmbd_conn *conn, const struct path *path, - unsigned int uid, unsigned int gid); -int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, - __le32 *pdaccess, int uid); -int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check); -void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); -void ksmbd_init_domain(u32 *sub_auth); - -static inline uid_t posix_acl_uid_translate(struct user_namespace *mnt_userns, - struct posix_acl_entry *pace) -{ - vfsuid_t vfsuid; - - /* If this is an idmapped mount, apply the idmapping. */ - vfsuid = make_vfsuid(mnt_userns, &init_user_ns, pace->e_uid); - - /* Translate the kuid into a userspace id ksmbd would see. */ - return from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid)); -} - -static inline gid_t posix_acl_gid_translate(struct user_namespace *mnt_userns, - struct posix_acl_entry *pace) -{ - vfsgid_t vfsgid; - - /* If this is an idmapped mount, apply the idmapping. */ - vfsgid = make_vfsgid(mnt_userns, &init_user_ns, pace->e_gid); - - /* Translate the kgid into a userspace id ksmbd would see. */ - return from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid)); -} - -#endif /* _SMBACL_H */ diff --git a/fs/ksmbd/smbfsctl.h b/fs/ksmbd/smbfsctl.h deleted file mode 100644 index b98418aae20c..000000000000 --- a/fs/ksmbd/smbfsctl.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions - * - * Copyright (c) International Business Machines Corp., 2002,2009 - * Author(s): Steve French (sfrench@us.ibm.com) - */ - -/* IOCTL information */ -/* - * List of ioctl/fsctl function codes that are or could be useful in the - * future to remote clients like cifs or SMB2 client. There is probably - * a slightly larger set of fsctls that NTFS local filesystem could handle, - * including the seven below that we do not have struct definitions for. - * Even with protocol definitions for most of these now available, we still - * need to do some experimentation to identify which are practical to do - * remotely. Some of the following, such as the encryption/compression ones - * could be invoked from tools via a specialized hook into the VFS rather - * than via the standard vfs entry points - */ - -#ifndef __KSMBD_SMBFSCTL_H -#define __KSMBD_SMBFSCTL_H - -#define FSCTL_DFS_GET_REFERRALS 0x00060194 -#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 -#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 -#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 -#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 -#define FSCTL_LOCK_VOLUME 0x00090018 -#define FSCTL_UNLOCK_VOLUME 0x0009001C -#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ -#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ -#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ -#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ -/* Verify the next FSCTL number, we had it as 0x00090090 before */ -#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ -#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ -#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ -#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ -#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ -#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C -#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ -#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ -#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ -#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ -#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ -#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ -#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ -#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ -#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ -#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ -#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */ -#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ -#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ -#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ -#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ -#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ -#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ -#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ -#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ -#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ -#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ -#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ -#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ -#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ -#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 -#define FSCTL_SIS_LINK_FILES 0x0009C104 -#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ -#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ -/* strange that the number for this op is not sequential with previous op */ -#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ -#define FSCTL_REQUEST_RESUME_KEY 0x00140078 -#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ -#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ -#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 -#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC -#define FSCTL_COPYCHUNK 0x001440F2 -#define FSCTL_COPYCHUNK_WRITE 0x001480F2 - -#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 -#define IO_REPARSE_TAG_HSM 0xC0000004 -#define IO_REPARSE_TAG_SIS 0x80000007 - -/* WSL reparse tags */ -#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) -#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) -#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) -#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) -#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) -#endif /* __KSMBD_SMBFSCTL_H */ diff --git a/fs/ksmbd/smbstatus.h b/fs/ksmbd/smbstatus.h deleted file mode 100644 index 108a8b6ed24a..000000000000 --- a/fs/ksmbd/smbstatus.h +++ /dev/null @@ -1,1822 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * fs/cifs/smb2status.h - * - * SMB2 Status code (network error) definitions - * Definitions are from MS-ERREF - * - * Copyright (c) International Business Machines Corp., 2009,2011 - * Author(s): Steve French (sfrench@us.ibm.com) - */ - -/* - * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F - * SEV C N <-------Facility--------> <------Error Status Code------> - * - * C is set if "customer defined" error, N bit is reserved and MBZ - */ - -#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) -#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) -#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) -#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) - -struct ntstatus { - /* Facility is the high 12 bits of the following field */ - __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ - __le32 Code; -}; - -#define STATUS_SUCCESS 0x00000000 -#define STATUS_WAIT_0 cpu_to_le32(0x00000000) -#define STATUS_WAIT_1 cpu_to_le32(0x00000001) -#define STATUS_WAIT_2 cpu_to_le32(0x00000002) -#define STATUS_WAIT_3 cpu_to_le32(0x00000003) -#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) -#define STATUS_ABANDONED cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) -#define STATUS_USER_APC cpu_to_le32(0x000000C0) -#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) -#define STATUS_ALERTED cpu_to_le32(0x00000101) -#define STATUS_TIMEOUT cpu_to_le32(0x00000102) -#define STATUS_PENDING cpu_to_le32(0x00000103) -#define STATUS_REPARSE cpu_to_le32(0x00000104) -#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) -#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) -#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) -#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) -#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) -#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) -#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) -#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) -#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) -#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) -#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) -#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) -#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) -#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) -#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) -#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) -#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) -#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) -#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) -#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) -#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) -#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) -#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) -#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) -#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) -#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) -#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) -#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) -#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) -#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) -#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) -#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) -#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) -#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) -#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) -#define DBG_CONTINUE cpu_to_le32(0x00010002) -#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) -#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) -#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) -#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) -#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) -#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) -#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) -#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) -#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) -#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) -#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) -#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) -#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) -#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) -#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) -#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) -#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) -#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) -#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) -#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) -#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) -#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) -#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) -#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) -#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) -#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) -#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) -#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) -#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) -#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) -#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) -#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) -#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) -#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) -#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) -#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) -#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) -#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) -#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) -#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) -#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) -#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) -#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) -#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) -#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) -#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) -#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) -#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) -#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) -#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) -#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) -#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) -#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) -#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) -#define DBG_REPLY_LATER cpu_to_le32(0x40010001) -#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) -#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) -#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) -#define DBG_CONTROL_C cpu_to_le32(0x40010005) -#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) -#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) -#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) -#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) -#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) -#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) -#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) -#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) -#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) -#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) -#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) -#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) -#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) -#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) -#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) -#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) -#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) -#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) -#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ - cpu_to_le32(0x401E0351) -#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) -#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) -#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) -#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) -#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) -#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) -#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) -#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) -#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) -#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) -#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) -#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) -#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) -#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) -#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) -#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) -#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) -#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) -#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) -#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) -#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) -#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) -#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) -#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) -#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) -#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) -#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) -#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) -#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) -#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) -#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) -#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) -#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) -#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) -#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) -#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) -#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) -#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) -#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) -#define STATUS_LONGJUMP cpu_to_le32(0x80000026) -#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) -#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) -#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) -#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) -#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) -#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) -#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) -#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) -#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) -#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) -#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) -#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) -#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) -#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) -#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) -#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) -#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) -#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) -#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) -#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) -#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ - cpu_to_le32(0x801B00EB) -#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) -#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) -#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) -#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) -#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) -#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) -#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) -#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) -#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) -#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) -#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) -#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) -#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) -#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) -#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) -#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) -#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) -#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) -#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) -#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) -#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) -#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) -#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) -#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) -#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) -#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) -#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) -#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) -#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) -#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) -#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) -#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) -#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) -#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) -#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) -#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) -#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) -#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) -#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) -#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) -#define STATUS_UNWIND cpu_to_le32(0xC0000027) -#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) -#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) -#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) -#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) -#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) -#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) -#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) -#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) -#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) -#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) -#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) -#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) -#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) -#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) -#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) -#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) -#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) -#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) -#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) -#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) -#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) -#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) -#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) -#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) -#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) -#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) -#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) -#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) -#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) -#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) -#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) -#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) -#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) -#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) -#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) -#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) -#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) -#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) -#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) -#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) -#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) -#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) -#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) -#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) -#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) -#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) -#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) -#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) -#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) -#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) -#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) -#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) -#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) -#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) -#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) -#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) -#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) -#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) -#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) -#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) -#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) -#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) -#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) -#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) -#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) -#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) -#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) -#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) -#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) -#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) -#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) -#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) -#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) -#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) -#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) -#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) -#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) -#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) -#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) -#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) -#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) -#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) -#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) -#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) -#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) -#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) -#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) -#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) -#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) -#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) -#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) -#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) -#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) -#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) -#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) -#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) -#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) -#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) -#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) -#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) -#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) -#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) -#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) -#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) -#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) -#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) -#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) -#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) -#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) -#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) -#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) -#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) -#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) -#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) -#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) -#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) -#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) -#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) -#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) -#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) -#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) -#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) -#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) -#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) -#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) -#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) -#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) -#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) -#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) -#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) -#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) -#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) -#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) -#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) -#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) -#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) -#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) -#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) -#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) -#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) -#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) -#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) -#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) -#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) -#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) -#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) -#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) -#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) -#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) -#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) -#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) -#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) -#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) -#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) -#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) -#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) -#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) -#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) -#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) -#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) -#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) -#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) -#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) -#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) -#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) -#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) -#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) -#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) -#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) -#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) -#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) -#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) -#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) -#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) -#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) -#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) -#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) -#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) -#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) -#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) -#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) -#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) -#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) -#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) -#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) -#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) -#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) -#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) -#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) -#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) -#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) -#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) -#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) -#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) -#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) -#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) -#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) -#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) -#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) -#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) -#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) -#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) -#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) -#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) -#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) -#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) -#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) -#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) -#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) -#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) -#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) -#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) -#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) -#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) -#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) -#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) -#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) -#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) -#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) -#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) -#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) -#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) -#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) -#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) -#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) -#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) -#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) -#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) -#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) -#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) -#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) -#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) -#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) -#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) -#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) -#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) -#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) -#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) -#define STATUS_NO_LDT cpu_to_le32(0xC0000117) -#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) -#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) -#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) -#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) -#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) -#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) -#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) -#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) -#define STATUS_CANCELLED cpu_to_le32(0xC0000120) -#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) -#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) -#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) -#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) -#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) -#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) -#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) -#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) -#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) -#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) -#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) -#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) -#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) -#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) -#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) -#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) -#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) -#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) -#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) -#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) -#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) -#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) -#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) -#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) -#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) -#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) -#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) -#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) -#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) -#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) -#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) -#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) -#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) -#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) -#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) -#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) -#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) -#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) -#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) -#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) -#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) -#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) -#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) -#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) -#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) -#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) -#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) -#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) -#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) -#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) -#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) -#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) -#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) -#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) -#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) -#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) -#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) -#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) -#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) -#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) -#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) -#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) -#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) -#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) -#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) -#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) -#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) -#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) -#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) -#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) -#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) -#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) -#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) -#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) -#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) -#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) -#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) -#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) -#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) -#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) -#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) -#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) -#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) -#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) -#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) -#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) -#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) -#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) -#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) -#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) -#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) -#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) -#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) -#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) -#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) -#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) -#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) -#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) -#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) -#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) -#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) -#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) -#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) -#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) -#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) -#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) -#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) -#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) -#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) -#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) -#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) -#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) -#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) -#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) -#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) -#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) -#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) -#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) -#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) -#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) -#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) -#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) -#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) -#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) -#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) -#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) -#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) -#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) -#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) -#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) -#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) -#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) -#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) -#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) -#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) -#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) -#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) -#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) -#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) -#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) -#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) -#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) -#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) -#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) -#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) -#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) -#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) -#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) -#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) -#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) -#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) -#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) -#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) -#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) -#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) -#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) -#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) -#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) -#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) -#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) -#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) -#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) -#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) -#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) -#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) -#define STATUS_RETRY cpu_to_le32(0xC000022D) -#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) -#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) -#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) -#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) -#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) -#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) -#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) -#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) -#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) -#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) -#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) -#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) -#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) -#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) -#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) -#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) -#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) -#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) -#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) -#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) -#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) -#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) -#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) -#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) -#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) -#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) -#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) -#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) -#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) -#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) -#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) -#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) -#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) -#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) -#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) -#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) -#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) -#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) -#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) -#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) -#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) -#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) -#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) -#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) -#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) -#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) -#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) -#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) -#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) -#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) -#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) -#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) -#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) -#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) -#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) -#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) -#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) -#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) -#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) -#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) -#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) -#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) -#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) -#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) -#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) -#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) -#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) -#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) -#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) -#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) -#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) -#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) -#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) -#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) -#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) -#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) -#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) -#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) -#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) -#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) -#define STATUS_NO_EFS cpu_to_le32(0xC000028E) -#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) -#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) -#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) -#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) -#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) -#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) -#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) -#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) -#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) -#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) -#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) -#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) -#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) -#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) -#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) -#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) -#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) -#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) -#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) -#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) -#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) -#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) -#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) -#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) -#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) -#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) -#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) -#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) -#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) -#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) -#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) -#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) -#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) -#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) -#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) -#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) -#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) -#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) -#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) -#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) -#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) -#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) -#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) -#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) -#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) -#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) -#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) -#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) -#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) -#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) -#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) -#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) -#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) -#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) -#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) -#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) -#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) -#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) -#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) -#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) -#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) -#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) -#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) -#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) -#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) -#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) -#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) -#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) -#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ - cpu_to_le32(0xC00002DB) -#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) -#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) -#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) -#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) -#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) -#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) -#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) -#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) -#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) -#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) -#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) -#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) -#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) -#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) -#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) -#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) -#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) -#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) -#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) -#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) -#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) -#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) -#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) -#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) -#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) -#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) -#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) -#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) -#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) -#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) -#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) -#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) -#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) -#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) -#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) -#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) -#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) -#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) -#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) -#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) -#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) -#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) -#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) -#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) -#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) -#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) -#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) -#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) -#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) -#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) -#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) -#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) -#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) -#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) -#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) -#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) -#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) -#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) -#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) -#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) -#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) -#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) -#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) -#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) -#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) -#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) -#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) -#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) -#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) -#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) -#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) -#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) -#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) -#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) -#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) -#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) -#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) -#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) -#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) -#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) -#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) -#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) -#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) -#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) -#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) -#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) -#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) -#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) -#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) -#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) -#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) -#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) -#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) -#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) -#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) -#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) -#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) -#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) -#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) -#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) -#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) -#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) -#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) -#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) -#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) -#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) -#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) -#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) -#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) -#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) -#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) -#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) -#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) -#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) -#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) -#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) -#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) -#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) -#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) -#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) -#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ - cpu_to_le32(0xC0000416) -#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) -#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) -#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) -#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) -#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) -#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) -#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) -#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) -#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) -#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) -#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) -#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) -#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) -#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) -#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) -#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) -#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) -#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) -#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) -#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) -#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) -#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) -#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) -#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) -#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) -#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) -#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) -#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) -#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) -#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) -#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) -#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) -#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) -#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) -#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) -#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) -#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) -#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) -#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) -#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) -#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) -#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) -#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) -#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) -#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) -#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070C) -#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070D) -#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070E) -#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) -#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) -#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) -#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) -#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) -#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) -#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) -#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) -#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) -#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) -#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) -#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) -#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) -#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) -#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) -#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) -#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) -#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) -#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) -#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) -#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) -#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) -#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) -#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) -#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) -#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) -#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) -#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) -#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) -#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) -#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) -#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) -#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) -#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) -#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) -#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) -#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) -#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) -#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) -#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ - cpu_to_le32(0xC000A080) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ - cpu_to_le32(0xC000A081) -#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) -#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) -#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) -#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) -#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) -#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) -#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) -#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) -#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) -#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) -#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) -#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) -#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) -#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) -#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) -#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) -#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) -#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) -#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) -#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) -#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) -#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) -#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) -#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) -#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) -#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) -#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) -#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) -#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) -#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) -#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) -#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) -#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) -#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) -#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) -#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) -#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) -#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) -#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) -#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) -#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) -#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) -#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) -#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) -#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) -#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) -#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) -#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) -#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) -#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) -#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) -#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) -#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) -#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) -#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) -#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) -#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) -#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) -#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) -#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) -#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) -#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) -#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) -#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) -#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) -#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) -#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) -#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) -#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) -#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) -#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) -#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) -#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) -#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) -#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) -#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) -#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) -#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) -#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) -#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) -#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) -#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) -#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) -#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) -#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) -#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) -#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) -#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) -#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) -#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) -#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) -#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) -#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) -#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) -#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) -#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) -#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) -#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) -#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) -#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) -#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) -#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) -#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) -#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) -#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) -#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) -#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) -#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) -#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) -#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) -#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) -#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) -#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) -#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) -#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) -#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) -#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) -#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) -#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) -#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) -#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) -#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) -#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) -#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) -#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) -#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) -#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) -#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) -#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) -#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) -#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) -#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) -#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) -#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) -#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) -#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) -#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) -#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) -#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) -#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) -#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) -#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) -#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) -#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) -#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) -#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) -#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) -#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) -#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) -#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) -#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) -#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) -#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) -#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) -#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) -#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) -#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) -#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) -#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) -#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) -#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) -#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) -#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) -#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) -#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) -#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) -#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) -#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) -#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) -#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) -#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) -#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) -#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) -#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) -#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) -#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) -#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) -#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) -#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) -#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) -#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) -#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) -#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) -#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) -#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) -#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) -#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) -#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) -#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) -#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) -#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) -#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) -#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) -#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) -#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) -#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) -#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) -#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) -#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) -#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) -#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) -#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) -#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) -#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) -#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) -#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) -#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) -#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) -#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) -#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) -#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) -#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) -#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) -#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) -#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) -#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) -#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) -#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) -#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) -#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) -#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) -#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) -#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) -#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) -#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) -#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) -#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) -#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) -#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) -#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) -#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) -#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) -#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ - cpu_to_le32(0xC0150012) -#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) -#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) -#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) -#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) -#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) -#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) -#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) -#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ - cpu_to_le32(0xC015001C) -#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) -#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) -#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) -#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) -#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) -#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) -#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) -#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) -#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) -#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) -#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) -#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) -#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) -#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) -#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) -#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) -#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) -#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) -#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) -#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) -#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) -#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) -#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) -#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) -#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) -#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) -#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) -#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) -#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) -#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) -#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) -#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) -#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) -#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) -#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) -#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) -#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ - cpu_to_le32(0xC0190024) -#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) -#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) -#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) -#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) -#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) -#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) -#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) -#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) -#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) -#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) -#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) -#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) -#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) -#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) -#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) -#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) -#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) -#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) -#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) -#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) -#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) -#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) -#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) -#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) -#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) -#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) -#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) -#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) -#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) -#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) -#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) -#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) -#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) -#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ - cpu_to_le32(0xC0190053) -#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) -#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) -#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) -#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) -#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) -#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) -#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) -#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) -#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) -#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) -#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) -#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) -#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) -#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) -#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) -#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) -#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) -#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) -#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) -#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) -#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) -#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) -#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) -#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) -#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) -#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) -#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) -#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) -#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) -#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) -#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) -#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) -#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) -#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) -#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) -#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) -#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) -#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) -#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) -#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) -#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) -#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) -#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) -#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) -#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) -#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) -#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) -#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) -#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) -#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) -#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) -#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) -#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) -#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) -#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) -#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) -#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) -#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) -#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) -#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) -#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) -#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) -#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) -#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) -#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) -#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) -#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) -#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) -#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) -#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) -#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) -#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) -#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) -#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) -#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) -#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) -#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) -#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) -#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) -#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) -#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) -#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) -#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) -#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) -#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) -#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) -#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) -#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) -#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) -#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ - cpu_to_le32(0xC01D0006) -#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ - cpu_to_le32(0xC01D0007) -#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) -#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) -#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) -#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) -#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) -#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) -#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) -#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) -#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) -#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) -#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) -#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) -#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) -#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) -#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) -#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) -#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) -#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) -#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) -#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) -#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) -#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) -#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) -#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0302) -#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) -#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) -#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) -#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) -#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) -#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ - cpu_to_le32(0xC01E0310) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ - cpu_to_le32(0xC01E0311) -#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) -#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) -#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) -#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) -#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) -#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) -#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ - cpu_to_le32(0xC01E031B) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) -#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) -#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) -#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) -#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) -#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) -#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ - cpu_to_le32(0xC01E0325) -#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ - cpu_to_le32(0xC01E0326) -#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ - cpu_to_le32(0xC01E0328) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ - cpu_to_le32(0xC01E0329) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ - cpu_to_le32(0xC01E032E) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) -#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) -#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) -#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) -#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) -#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ - cpu_to_le32(0xC01E0334) -#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) -#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) -#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) -#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) -#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) -#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) -#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) -#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) -#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) -#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) -#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) -#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) -#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ - cpu_to_le32(0xC01E0341) -#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) -#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) -#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ - cpu_to_le32(0xC01E0345) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0346) -#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) -#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) -#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) -#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ - cpu_to_le32(0xC01E034D) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) -#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) -#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ - cpu_to_le32(0xC01E0350) -#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) -#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) -#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) -#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) -#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) -#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ - cpu_to_le32(0xC01E0357) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ - cpu_to_le32(0xC01E0358) -#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) -#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ - cpu_to_le32(0xC01E035A) -#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) -#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) -#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ - cpu_to_le32(0xC01E0400) -#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) -#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) -#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) -#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) -#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) -#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) -#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ - cpu_to_le32(0xC01E051C) -#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) -#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ - cpu_to_le32(0xC01E051F) -#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) -#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ - cpu_to_le32(0xC01E0521) -#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) -#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) -#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) -#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) -#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) -#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) -#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ - cpu_to_le32(0xC01E0506) -#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ - cpu_to_le32(0xC01E0507) -#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0508) -#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) -#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) -#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) -#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ - cpu_to_le32(0xC01E050D) -#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) -#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) -#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) -#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) -#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ - cpu_to_le32(0xC01E0515) -#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) -#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) -#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ - cpu_to_le32(0xC01E0518) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ - cpu_to_le32(0xC01E051A) -#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ - cpu_to_le32(0xC01E051B) -#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) -#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) -#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) -#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) -#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) -#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) -#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ - cpu_to_le32(0xC01E0586) -#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ - cpu_to_le32(0xC01E0587) -#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) -#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) -#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) -#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) -#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ - cpu_to_le32(0xC01E05E1) -#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ - cpu_to_le32(0xC01E05E2) -#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) -#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) -#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ - cpu_to_le32(0xC01E05E5) -#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) -#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) -#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) -#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) -#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) -#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) -#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) -#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) -#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) -#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) -#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) -#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) -#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) -#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) -#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) -#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) -#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) -#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) -#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) -#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) -#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) -#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) -#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) -#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) -#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) -#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) -#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) -#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) -#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) -#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) -#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) -#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) -#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) -#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) -#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) -#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) -#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) -#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) -#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) -#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) -#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) -#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) -#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) -#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) -#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) -#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) -#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) -#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) -#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) -#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) -#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) -#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) -#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) -#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) -#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) -#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) -#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) -#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) -#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) -#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) -#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) -#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) -#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) -#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) -#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) -#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) -#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) -#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) -#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) -#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) -#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) -#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) -#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) -#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) -#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) -#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) -#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) -#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) -#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) -#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) -#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) -#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) -#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) -#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) -#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) -#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) -#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) -#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) -#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) -#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) -#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) -#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) -#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) -#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) -#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) -#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) -#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) -#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) -#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) -#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) -#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) -#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) -#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) -#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) -#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) -#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) -#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) -#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) -#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) -#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) -#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) -#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) -#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) -#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) -#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) -#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) -#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) -#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) -#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) -#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) -#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) -#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) -#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) -#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) -#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) -#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) -#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) -#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) -#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) -#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) -#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) -#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) -#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) -#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) -#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) -#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) -#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) -#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) -#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) -#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) -#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) - -#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) -#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c deleted file mode 100644 index 40c721f9227e..000000000000 --- a/fs/ksmbd/transport_ipc.c +++ /dev/null @@ -1,884 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vfs_cache.h" -#include "transport_ipc.h" -#include "server.h" -#include "smb_common.h" - -#include "mgmt/user_config.h" -#include "mgmt/share_config.h" -#include "mgmt/user_session.h" -#include "mgmt/tree_connect.h" -#include "mgmt/ksmbd_ida.h" -#include "connection.h" -#include "transport_tcp.h" -#include "transport_rdma.h" - -#define IPC_WAIT_TIMEOUT (2 * HZ) - -#define IPC_MSG_HASH_BITS 3 -static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); -static DECLARE_RWSEM(ipc_msg_table_lock); -static DEFINE_MUTEX(startup_lock); - -static DEFINE_IDA(ipc_ida); - -static unsigned int ksmbd_tools_pid; - -static bool ksmbd_ipc_validate_version(struct genl_info *m) -{ - if (m->genlhdr->version != KSMBD_GENL_VERSION) { - pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n", - "Daemon and kernel module version mismatch", - m->genlhdr->version, - KSMBD_GENL_VERSION, - "User-space ksmbd should terminate"); - return false; - } - return true; -} - -struct ksmbd_ipc_msg { - unsigned int type; - unsigned int sz; - unsigned char payload[]; -}; - -struct ipc_msg_table_entry { - unsigned int handle; - unsigned int type; - wait_queue_head_t wait; - struct hlist_node ipc_table_hlist; - - void *response; -}; - -static struct delayed_work ipc_timer_work; - -static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); -static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info); -static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); -static int ksmbd_ipc_heartbeat_request(void); - -static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { - [KSMBD_EVENT_UNSPEC] = { - .len = 0, - }, - [KSMBD_EVENT_HEARTBEAT_REQUEST] = { - .len = sizeof(struct ksmbd_heartbeat), - }, - [KSMBD_EVENT_STARTING_UP] = { - .len = sizeof(struct ksmbd_startup_request), - }, - [KSMBD_EVENT_SHUTTING_DOWN] = { - .len = sizeof(struct ksmbd_shutdown_request), - }, - [KSMBD_EVENT_LOGIN_REQUEST] = { - .len = sizeof(struct ksmbd_login_request), - }, - [KSMBD_EVENT_LOGIN_RESPONSE] = { - .len = sizeof(struct ksmbd_login_response), - }, - [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { - .len = sizeof(struct ksmbd_share_config_request), - }, - [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { - .len = sizeof(struct ksmbd_share_config_response), - }, - [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { - .len = sizeof(struct ksmbd_tree_connect_request), - }, - [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { - .len = sizeof(struct ksmbd_tree_connect_response), - }, - [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { - .len = sizeof(struct ksmbd_tree_disconnect_request), - }, - [KSMBD_EVENT_LOGOUT_REQUEST] = { - .len = sizeof(struct ksmbd_logout_request), - }, - [KSMBD_EVENT_RPC_REQUEST] = { - }, - [KSMBD_EVENT_RPC_RESPONSE] = { - }, - [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { - }, - [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { - }, -}; - -static struct genl_ops ksmbd_genl_ops[] = { - { - .cmd = KSMBD_EVENT_UNSPEC, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_HEARTBEAT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_STARTING_UP, - .doit = handle_startup_event, - }, - { - .cmd = KSMBD_EVENT_SHUTTING_DOWN, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_LOGIN_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_LOGIN_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_LOGOUT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_RPC_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_RPC_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, - .doit = handle_generic_event, - }, -}; - -static struct genl_family ksmbd_genl_family = { - .name = KSMBD_GENL_NAME, - .version = KSMBD_GENL_VERSION, - .hdrsize = 0, - .maxattr = KSMBD_EVENT_MAX, - .netnsok = true, - .module = THIS_MODULE, - .ops = ksmbd_genl_ops, - .n_ops = ARRAY_SIZE(ksmbd_genl_ops), - .resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, -}; - -static void ksmbd_nl_init_fixup(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++) - ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT | - GENL_DONT_VALIDATE_DUMP; - - ksmbd_genl_family.policy = ksmbd_nl_policy; -} - -static int rpc_context_flags(struct ksmbd_session *sess) -{ - if (user_guest(sess->user)) - return KSMBD_RPC_RESTRICTED_CONTEXT; - return 0; -} - -static void ipc_update_last_active(void) -{ - if (server_conf.ipc_timeout) - server_conf.ipc_last_active = jiffies; -} - -static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) -{ - struct ksmbd_ipc_msg *msg; - size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); - - msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); - if (msg) - msg->sz = sz; - return msg; -} - -static void ipc_msg_free(struct ksmbd_ipc_msg *msg) -{ - kvfree(msg); -} - -static void ipc_msg_handle_free(int handle) -{ - if (handle >= 0) - ksmbd_release_id(&ipc_ida, handle); -} - -static int handle_response(int type, void *payload, size_t sz) -{ - unsigned int handle = *(unsigned int *)payload; - struct ipc_msg_table_entry *entry; - int ret = 0; - - ipc_update_last_active(); - down_read(&ipc_msg_table_lock); - hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { - if (handle != entry->handle) - continue; - - entry->response = NULL; - /* - * Response message type value should be equal to - * request message type + 1. - */ - if (entry->type + 1 != type) { - pr_err("Waiting for IPC type %d, got %d. Ignore.\n", - entry->type + 1, type); - } - - entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); - if (!entry->response) { - ret = -ENOMEM; - break; - } - - memcpy(entry->response, payload, sz); - wake_up_interruptible(&entry->wait); - ret = 0; - break; - } - up_read(&ipc_msg_table_lock); - - return ret; -} - -static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) -{ - int ret; - - ksmbd_set_fd_limit(req->file_max); - server_conf.flags = req->flags; - server_conf.signing = req->signing; - server_conf.tcp_port = req->tcp_port; - server_conf.ipc_timeout = req->ipc_timeout * HZ; - server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; - server_conf.share_fake_fscaps = req->share_fake_fscaps; - ksmbd_init_domain(req->sub_auth); - - if (req->smb2_max_read) - init_smb2_max_read_size(req->smb2_max_read); - if (req->smb2_max_write) - init_smb2_max_write_size(req->smb2_max_write); - if (req->smb2_max_trans) - init_smb2_max_trans_size(req->smb2_max_trans); - if (req->smb2_max_credits) - init_smb2_max_credits(req->smb2_max_credits); - if (req->smbd_max_io_size) - init_smbd_max_io_size(req->smbd_max_io_size); - - if (req->max_connections) - server_conf.max_connections = req->max_connections; - - ret = ksmbd_set_netbios_name(req->netbios_name); - ret |= ksmbd_set_server_string(req->server_string); - ret |= ksmbd_set_work_group(req->work_group); - ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), - req->ifc_list_sz); - if (ret) { - pr_err("Server configuration error: %s %s %s\n", - req->netbios_name, req->server_string, - req->work_group); - return ret; - } - - if (req->min_prot[0]) { - ret = ksmbd_lookup_protocol_idx(req->min_prot); - if (ret >= 0) - server_conf.min_protocol = ret; - } - if (req->max_prot[0]) { - ret = ksmbd_lookup_protocol_idx(req->max_prot); - if (ret >= 0) - server_conf.max_protocol = ret; - } - - if (server_conf.ipc_timeout) - schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout); - return 0; -} - -static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) -{ - int ret = 0; - -#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN - if (!netlink_capable(skb, CAP_NET_ADMIN)) - return -EPERM; -#endif - - if (!ksmbd_ipc_validate_version(info)) - return -EINVAL; - - if (!info->attrs[KSMBD_EVENT_STARTING_UP]) - return -EINVAL; - - mutex_lock(&startup_lock); - if (!ksmbd_server_configurable()) { - mutex_unlock(&startup_lock); - pr_err("Server reset is in progress, can't start daemon\n"); - return -EINVAL; - } - - if (ksmbd_tools_pid) { - if (ksmbd_ipc_heartbeat_request() == 0) { - ret = -EINVAL; - goto out; - } - - pr_err("Reconnect to a new user space daemon\n"); - } else { - struct ksmbd_startup_request *req; - - req = nla_data(info->attrs[info->genlhdr->cmd]); - ret = ipc_server_config_on_startup(req); - if (ret) - goto out; - server_queue_ctrl_init_work(); - } - - ksmbd_tools_pid = info->snd_portid; - ipc_update_last_active(); - -out: - mutex_unlock(&startup_lock); - return ret; -} - -static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) -{ - pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); - return -EINVAL; -} - -static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) -{ - void *payload; - int sz; - int type = info->genlhdr->cmd; - -#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN - if (!netlink_capable(skb, CAP_NET_ADMIN)) - return -EPERM; -#endif - - if (type >= KSMBD_EVENT_MAX) { - WARN_ON(1); - return -EINVAL; - } - - if (!ksmbd_ipc_validate_version(info)) - return -EINVAL; - - if (!info->attrs[type]) - return -EINVAL; - - payload = nla_data(info->attrs[info->genlhdr->cmd]); - sz = nla_len(info->attrs[info->genlhdr->cmd]); - return handle_response(type, payload, sz); -} - -static int ipc_msg_send(struct ksmbd_ipc_msg *msg) -{ - struct genlmsghdr *nlh; - struct sk_buff *skb; - int ret = -EINVAL; - - if (!ksmbd_tools_pid) - return ret; - - skb = genlmsg_new(msg->sz, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type); - if (!nlh) - goto out; - - ret = nla_put(skb, msg->type, msg->sz, msg->payload); - if (ret) { - genlmsg_cancel(skb, nlh); - goto out; - } - - genlmsg_end(skb, nlh); - ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid); - if (!ret) - ipc_update_last_active(); - return ret; - -out: - nlmsg_free(skb); - return ret; -} - -static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) -{ - struct ipc_msg_table_entry entry; - int ret; - - if ((int)handle < 0) - return NULL; - - entry.type = msg->type; - entry.response = NULL; - init_waitqueue_head(&entry.wait); - - down_write(&ipc_msg_table_lock); - entry.handle = handle; - hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle); - up_write(&ipc_msg_table_lock); - - ret = ipc_msg_send(msg); - if (ret) - goto out; - - ret = wait_event_interruptible_timeout(entry.wait, - entry.response != NULL, - IPC_WAIT_TIMEOUT); -out: - down_write(&ipc_msg_table_lock); - hash_del(&entry.ipc_table_hlist); - up_write(&ipc_msg_table_lock); - return entry.response; -} - -static int ksmbd_ipc_heartbeat_request(void) -{ - struct ksmbd_ipc_msg *msg; - int ret; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat)); - if (!msg) - return -EINVAL; - - msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST; - ret = ipc_msg_send(msg); - ipc_msg_free(msg); - return ret; -} - -struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_login_request *req; - struct ksmbd_login_response *resp; - - if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) - return NULL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_LOGIN_REQUEST; - req = (struct ksmbd_login_request *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_spnego_authen_response * -ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_spnego_authen_request *req; - struct ksmbd_spnego_authen_response *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + - blob_len + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; - req = (struct ksmbd_spnego_authen_request *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - req->spnego_blob_len = blob_len; - memcpy(req->spnego_blob, spnego_blob, blob_len); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_tree_connect_response * -ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_tree_connect_request *req; - struct ksmbd_tree_connect_response *resp; - - if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) - return NULL; - - if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME) - return NULL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; - req = (struct ksmbd_tree_connect_request *)msg->payload; - - req->handle = ksmbd_acquire_id(&ipc_ida); - req->account_flags = sess->user->flags; - req->session_id = sess->id; - req->connect_id = tree_conn->id; - strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); - strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME); - snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr); - - if (peer_addr->sa_family == AF_INET6) - req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6; - if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) - req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, - unsigned long long connect_id) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_tree_disconnect_request *req; - int ret; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request)); - if (!msg) - return -ENOMEM; - - msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; - req = (struct ksmbd_tree_disconnect_request *)msg->payload; - req->session_id = session_id; - req->connect_id = connect_id; - - ret = ipc_msg_send(msg); - ipc_msg_free(msg); - return ret; -} - -int ksmbd_ipc_logout_request(const char *account, int flags) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_logout_request *req; - int ret; - - if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) - return -EINVAL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request)); - if (!msg) - return -ENOMEM; - - msg->type = KSMBD_EVENT_LOGOUT_REQUEST; - req = (struct ksmbd_logout_request *)msg->payload; - req->account_flags = flags; - strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); - - ret = ipc_msg_send(msg); - ipc_msg_free(msg); - return ret; -} - -struct ksmbd_share_config_response * -ksmbd_ipc_share_config_request(const char *name) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_share_config_request *req; - struct ksmbd_share_config_response *resp; - - if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME) - return NULL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; - req = (struct ksmbd_share_config_request *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= KSMBD_RPC_OPEN_METHOD; - req->payload_sz = 0; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= KSMBD_RPC_CLOSE_METHOD; - req->payload_sz = 0; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= rpc_context_flags(sess); - req->flags |= KSMBD_RPC_WRITE_METHOD; - req->payload_sz = payload_sz; - memcpy(req->payload, payload, payload_sz); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= rpc_context_flags(sess); - req->flags |= KSMBD_RPC_READ_METHOD; - req->payload_sz = 0; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= rpc_context_flags(sess); - req->flags |= KSMBD_RPC_IOCTL_METHOD; - req->payload_sz = payload_sz; - memcpy(req->payload, payload, payload_sz); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - req->flags = rpc_context_flags(sess); - req->flags |= KSMBD_RPC_RAP_METHOD; - req->payload_sz = payload_sz; - memcpy(req->payload, payload, payload_sz); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -static int __ipc_heartbeat(void) -{ - unsigned long delta; - - if (!ksmbd_server_running()) - return 0; - - if (time_after(jiffies, server_conf.ipc_last_active)) { - delta = (jiffies - server_conf.ipc_last_active); - } else { - ipc_update_last_active(); - schedule_delayed_work(&ipc_timer_work, - server_conf.ipc_timeout); - return 0; - } - - if (delta < server_conf.ipc_timeout) { - schedule_delayed_work(&ipc_timer_work, - server_conf.ipc_timeout - delta); - return 0; - } - - if (ksmbd_ipc_heartbeat_request() == 0) { - schedule_delayed_work(&ipc_timer_work, - server_conf.ipc_timeout); - return 0; - } - - mutex_lock(&startup_lock); - WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); - server_conf.ipc_last_active = 0; - ksmbd_tools_pid = 0; - pr_err("No IPC daemon response for %lus\n", delta / HZ); - mutex_unlock(&startup_lock); - return -EINVAL; -} - -static void ipc_timer_heartbeat(struct work_struct *w) -{ - if (__ipc_heartbeat()) - server_queue_ctrl_reset_work(); -} - -int ksmbd_ipc_id_alloc(void) -{ - return ksmbd_acquire_id(&ipc_ida); -} - -void ksmbd_rpc_id_free(int handle) -{ - ksmbd_release_id(&ipc_ida, handle); -} - -void ksmbd_ipc_release(void) -{ - cancel_delayed_work_sync(&ipc_timer_work); - genl_unregister_family(&ksmbd_genl_family); -} - -void ksmbd_ipc_soft_reset(void) -{ - mutex_lock(&startup_lock); - ksmbd_tools_pid = 0; - cancel_delayed_work_sync(&ipc_timer_work); - mutex_unlock(&startup_lock); -} - -int ksmbd_ipc_init(void) -{ - int ret = 0; - - ksmbd_nl_init_fixup(); - INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); - - ret = genl_register_family(&ksmbd_genl_family); - if (ret) { - pr_err("Failed to register KSMBD netlink interface %d\n", ret); - cancel_delayed_work_sync(&ipc_timer_work); - } - - return ret; -} diff --git a/fs/ksmbd/transport_ipc.h b/fs/ksmbd/transport_ipc.h deleted file mode 100644 index 5e5b90a0c187..000000000000 --- a/fs/ksmbd/transport_ipc.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_TRANSPORT_IPC_H__ -#define __KSMBD_TRANSPORT_IPC_H__ - -#include - -#define KSMBD_IPC_MAX_PAYLOAD 4096 - -struct ksmbd_login_response * -ksmbd_ipc_login_request(const char *account); - -struct ksmbd_session; -struct ksmbd_share_config; -struct ksmbd_tree_connect; -struct sockaddr; - -struct ksmbd_tree_connect_response * -ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr); -int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, - unsigned long long connect_id); -int ksmbd_ipc_logout_request(const char *account, int flags); -struct ksmbd_share_config_response * -ksmbd_ipc_share_config_request(const char *name); -struct ksmbd_spnego_authen_response * -ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); -int ksmbd_ipc_id_alloc(void); -void ksmbd_rpc_id_free(int handle); -struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); -struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); -struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz); -struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); -struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz); -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz); -void ksmbd_ipc_release(void); -void ksmbd_ipc_soft_reset(void); -int ksmbd_ipc_init(void); -#endif /* __KSMBD_TRANSPORT_IPC_H__ */ diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c deleted file mode 100644 index c06efc020bd9..000000000000 --- a/fs/ksmbd/transport_rdma.c +++ /dev/null @@ -1,2273 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2017, Microsoft Corporation. - * Copyright (C) 2018, LG Electronics. - * - * Author(s): Long Li , - * Hyunchul Lee - */ - -#define SUBMOD_NAME "smb_direct" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glob.h" -#include "connection.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "transport_rdma.h" - -#define SMB_DIRECT_PORT_IWARP 5445 -#define SMB_DIRECT_PORT_INFINIBAND 445 - -#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) - -/* SMB_DIRECT negotiation timeout in seconds */ -#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 - -#define SMB_DIRECT_MAX_SEND_SGES 6 -#define SMB_DIRECT_MAX_RECV_SGES 1 - -/* - * Default maximum number of RDMA read/write outstanding on this connection - * This value is possibly decreased during QP creation on hardware limit - */ -#define SMB_DIRECT_CM_INITIATOR_DEPTH 8 - -/* Maximum number of retries on data transfer operations */ -#define SMB_DIRECT_CM_RETRY 6 -/* No need to retry on Receiver Not Ready since SMB_DIRECT manages credits */ -#define SMB_DIRECT_CM_RNR_RETRY 0 - -/* - * User configurable initial values per SMB_DIRECT transport connection - * as defined in [MS-SMBD] 3.1.1.1 - * Those may change after a SMB_DIRECT negotiation - */ - -/* Set 445 port to SMB Direct port by default */ -static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; - -/* The local peer's maximum number of credits to grant to the peer */ -static int smb_direct_receive_credit_max = 255; - -/* The remote peer's credit request of local peer */ -static int smb_direct_send_credit_target = 255; - -/* The maximum single message size can be sent to remote peer */ -static int smb_direct_max_send_size = 1364; - -/* The maximum fragmented upper-layer payload receive size supported */ -static int smb_direct_max_fragmented_recv_size = 1024 * 1024; - -/* The maximum single-message size which can be received */ -static int smb_direct_max_receive_size = 1364; - -static int smb_direct_max_read_write_size = SMBD_DEFAULT_IOSIZE; - -static LIST_HEAD(smb_direct_device_list); -static DEFINE_RWLOCK(smb_direct_device_lock); - -struct smb_direct_device { - struct ib_device *ib_dev; - struct list_head list; -}; - -static struct smb_direct_listener { - struct rdma_cm_id *cm_id; -} smb_direct_listener; - -static struct workqueue_struct *smb_direct_wq; - -enum smb_direct_status { - SMB_DIRECT_CS_NEW = 0, - SMB_DIRECT_CS_CONNECTED, - SMB_DIRECT_CS_DISCONNECTING, - SMB_DIRECT_CS_DISCONNECTED, -}; - -struct smb_direct_transport { - struct ksmbd_transport transport; - - enum smb_direct_status status; - bool full_packet_received; - wait_queue_head_t wait_status; - - struct rdma_cm_id *cm_id; - struct ib_cq *send_cq; - struct ib_cq *recv_cq; - struct ib_pd *pd; - struct ib_qp *qp; - - int max_send_size; - int max_recv_size; - int max_fragmented_send_size; - int max_fragmented_recv_size; - int max_rdma_rw_size; - - spinlock_t reassembly_queue_lock; - struct list_head reassembly_queue; - int reassembly_data_length; - int reassembly_queue_length; - int first_entry_offset; - wait_queue_head_t wait_reassembly_queue; - - spinlock_t receive_credit_lock; - int recv_credits; - int count_avail_recvmsg; - int recv_credit_max; - int recv_credit_target; - - spinlock_t recvmsg_queue_lock; - struct list_head recvmsg_queue; - - spinlock_t empty_recvmsg_queue_lock; - struct list_head empty_recvmsg_queue; - - int send_credit_target; - atomic_t send_credits; - spinlock_t lock_new_recv_credits; - int new_recv_credits; - int max_rw_credits; - int pages_per_rw_credit; - atomic_t rw_credits; - - wait_queue_head_t wait_send_credits; - wait_queue_head_t wait_rw_credits; - - mempool_t *sendmsg_mempool; - struct kmem_cache *sendmsg_cache; - mempool_t *recvmsg_mempool; - struct kmem_cache *recvmsg_cache; - - wait_queue_head_t wait_send_pending; - atomic_t send_pending; - - struct delayed_work post_recv_credits_work; - struct work_struct send_immediate_work; - struct work_struct disconnect_work; - - bool negotiation_requested; -}; - -#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) - -enum { - SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, - SMB_DIRECT_MSG_DATA_TRANSFER -}; - -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; - -struct smb_direct_send_ctx { - struct list_head msg_list; - int wr_cnt; - bool need_invalidate_rkey; - unsigned int remote_key; -}; - -struct smb_direct_sendmsg { - struct smb_direct_transport *transport; - struct ib_send_wr wr; - struct list_head list; - int num_sge; - struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; - struct ib_cqe cqe; - u8 packet[]; -}; - -struct smb_direct_recvmsg { - struct smb_direct_transport *transport; - struct list_head list; - int type; - struct ib_sge sge; - struct ib_cqe cqe; - bool first_segment; - u8 packet[]; -}; - -struct smb_direct_rdma_rw_msg { - struct smb_direct_transport *t; - struct ib_cqe cqe; - int status; - struct completion *completion; - struct list_head list; - struct rdma_rw_ctx rw_ctx; - struct sg_table sgt; - struct scatterlist sg_list[]; -}; - -void init_smbd_max_io_size(unsigned int sz) -{ - sz = clamp_val(sz, SMBD_MIN_IOSIZE, SMBD_MAX_IOSIZE); - smb_direct_max_read_write_size = sz; -} - -unsigned int get_smbd_max_read_write_size(void) -{ - return smb_direct_max_read_write_size; -} - -static inline int get_buf_page_count(void *buf, int size) -{ - return DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - - (uintptr_t)buf / PAGE_SIZE; -} - -static void smb_direct_destroy_pools(struct smb_direct_transport *transport); -static void smb_direct_post_recv_credits(struct work_struct *work); -static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, - int remaining_data_length); - -static inline struct smb_direct_transport * -smb_trans_direct_transfort(struct ksmbd_transport *t) -{ - return container_of(t, struct smb_direct_transport, transport); -} - -static inline void -*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) -{ - return (void *)recvmsg->packet; -} - -static inline bool is_receive_credit_post_required(int receive_credits, - int avail_recvmsg_count) -{ - return receive_credits <= (smb_direct_receive_credit_max >> 3) && - avail_recvmsg_count >= (receive_credits >> 2); -} - -static struct -smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg = NULL; - - spin_lock(&t->recvmsg_queue_lock); - if (!list_empty(&t->recvmsg_queue)) { - recvmsg = list_first_entry(&t->recvmsg_queue, - struct smb_direct_recvmsg, - list); - list_del(&recvmsg->list); - } - spin_unlock(&t->recvmsg_queue_lock); - return recvmsg; -} - -static void put_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) -{ - ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); - - spin_lock(&t->recvmsg_queue_lock); - list_add(&recvmsg->list, &t->recvmsg_queue); - spin_unlock(&t->recvmsg_queue_lock); -} - -static struct -smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg = NULL; - - spin_lock(&t->empty_recvmsg_queue_lock); - if (!list_empty(&t->empty_recvmsg_queue)) { - recvmsg = list_first_entry(&t->empty_recvmsg_queue, - struct smb_direct_recvmsg, list); - list_del(&recvmsg->list); - } - spin_unlock(&t->empty_recvmsg_queue_lock); - return recvmsg; -} - -static void put_empty_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) -{ - ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); - - spin_lock(&t->empty_recvmsg_queue_lock); - list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); - spin_unlock(&t->empty_recvmsg_queue_lock); -} - -static void enqueue_reassembly(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg, - int data_length) -{ - spin_lock(&t->reassembly_queue_lock); - list_add_tail(&recvmsg->list, &t->reassembly_queue); - t->reassembly_queue_length++; - /* - * Make sure reassembly_data_length is updated after list and - * reassembly_queue_length are updated. On the dequeue side - * reassembly_data_length is checked without a lock to determine - * if reassembly_queue_length and list is up to date - */ - virt_wmb(); - t->reassembly_data_length += data_length; - spin_unlock(&t->reassembly_queue_lock); -} - -static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) -{ - if (!list_empty(&t->reassembly_queue)) - return list_first_entry(&t->reassembly_queue, - struct smb_direct_recvmsg, list); - else - return NULL; -} - -static void smb_direct_disconnect_rdma_work(struct work_struct *work) -{ - struct smb_direct_transport *t = - container_of(work, struct smb_direct_transport, - disconnect_work); - - if (t->status == SMB_DIRECT_CS_CONNECTED) { - t->status = SMB_DIRECT_CS_DISCONNECTING; - rdma_disconnect(t->cm_id); - } -} - -static void -smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) -{ - if (t->status == SMB_DIRECT_CS_CONNECTED) - queue_work(smb_direct_wq, &t->disconnect_work); -} - -static void smb_direct_send_immediate_work(struct work_struct *work) -{ - struct smb_direct_transport *t = container_of(work, - struct smb_direct_transport, send_immediate_work); - - if (t->status != SMB_DIRECT_CS_CONNECTED) - return; - - smb_direct_post_send_data(t, NULL, NULL, 0, 0); -} - -static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) -{ - struct smb_direct_transport *t; - struct ksmbd_conn *conn; - - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return NULL; - - t->cm_id = cm_id; - cm_id->context = t; - - t->status = SMB_DIRECT_CS_NEW; - init_waitqueue_head(&t->wait_status); - - spin_lock_init(&t->reassembly_queue_lock); - INIT_LIST_HEAD(&t->reassembly_queue); - t->reassembly_data_length = 0; - t->reassembly_queue_length = 0; - init_waitqueue_head(&t->wait_reassembly_queue); - init_waitqueue_head(&t->wait_send_credits); - init_waitqueue_head(&t->wait_rw_credits); - - spin_lock_init(&t->receive_credit_lock); - spin_lock_init(&t->recvmsg_queue_lock); - INIT_LIST_HEAD(&t->recvmsg_queue); - - spin_lock_init(&t->empty_recvmsg_queue_lock); - INIT_LIST_HEAD(&t->empty_recvmsg_queue); - - init_waitqueue_head(&t->wait_send_pending); - atomic_set(&t->send_pending, 0); - - spin_lock_init(&t->lock_new_recv_credits); - - INIT_DELAYED_WORK(&t->post_recv_credits_work, - smb_direct_post_recv_credits); - INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); - INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); - - conn = ksmbd_conn_alloc(); - if (!conn) - goto err; - conn->transport = KSMBD_TRANS(t); - KSMBD_TRANS(t)->conn = conn; - KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; - return t; -err: - kfree(t); - return NULL; -} - -static void free_transport(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg; - - wake_up_interruptible(&t->wait_send_credits); - - ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); - wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); - - cancel_work_sync(&t->disconnect_work); - cancel_delayed_work_sync(&t->post_recv_credits_work); - cancel_work_sync(&t->send_immediate_work); - - if (t->qp) { - ib_drain_qp(t->qp); - ib_mr_pool_destroy(t->qp, &t->qp->rdma_mrs); - ib_destroy_qp(t->qp); - } - - ksmbd_debug(RDMA, "drain the reassembly queue\n"); - do { - spin_lock(&t->reassembly_queue_lock); - recvmsg = get_first_reassembly(t); - if (recvmsg) { - list_del(&recvmsg->list); - spin_unlock(&t->reassembly_queue_lock); - put_recvmsg(t, recvmsg); - } else { - spin_unlock(&t->reassembly_queue_lock); - } - } while (recvmsg); - t->reassembly_data_length = 0; - - if (t->send_cq) - ib_free_cq(t->send_cq); - if (t->recv_cq) - ib_free_cq(t->recv_cq); - if (t->pd) - ib_dealloc_pd(t->pd); - if (t->cm_id) - rdma_destroy_id(t->cm_id); - - smb_direct_destroy_pools(t); - ksmbd_conn_free(KSMBD_TRANS(t)->conn); - kfree(t); -} - -static struct smb_direct_sendmsg -*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) -{ - struct smb_direct_sendmsg *msg; - - msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); - if (!msg) - return ERR_PTR(-ENOMEM); - msg->transport = t; - INIT_LIST_HEAD(&msg->list); - msg->num_sge = 0; - return msg; -} - -static void smb_direct_free_sendmsg(struct smb_direct_transport *t, - struct smb_direct_sendmsg *msg) -{ - int i; - - if (msg->num_sge > 0) { - ib_dma_unmap_single(t->cm_id->device, - msg->sge[0].addr, msg->sge[0].length, - DMA_TO_DEVICE); - for (i = 1; i < msg->num_sge; i++) - ib_dma_unmap_page(t->cm_id->device, - msg->sge[i].addr, msg->sge[i].length, - DMA_TO_DEVICE); - } - mempool_free(msg, t->sendmsg_mempool); -} - -static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) -{ - switch (recvmsg->type) { - case SMB_DIRECT_MSG_DATA_TRANSFER: { - struct smb_direct_data_transfer *req = - (struct smb_direct_data_transfer *)recvmsg->packet; - struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet - + le32_to_cpu(req->data_offset)); - ksmbd_debug(RDMA, - "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", - le16_to_cpu(req->credits_granted), - le16_to_cpu(req->credits_requested), - req->data_length, req->remaining_data_length, - hdr->ProtocolId, hdr->Command); - break; - } - case SMB_DIRECT_MSG_NEGOTIATE_REQ: { - struct smb_direct_negotiate_req *req = - (struct smb_direct_negotiate_req *)recvmsg->packet; - ksmbd_debug(RDMA, - "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", - le16_to_cpu(req->min_version), - le16_to_cpu(req->max_version), - le16_to_cpu(req->credits_requested), - le32_to_cpu(req->preferred_send_size), - le32_to_cpu(req->max_receive_size), - le32_to_cpu(req->max_fragmented_size)); - if (le16_to_cpu(req->min_version) > 0x0100 || - le16_to_cpu(req->max_version) < 0x0100) - return -EOPNOTSUPP; - if (le16_to_cpu(req->credits_requested) <= 0 || - le32_to_cpu(req->max_receive_size) <= 128 || - le32_to_cpu(req->max_fragmented_size) <= - 128 * 1024) - return -ECONNABORTED; - - break; - } - default: - return -EINVAL; - } - return 0; -} - -static void recv_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_transport *t; - - recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); - t = recvmsg->transport; - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { - if (wc->status != IB_WC_WR_FLUSH_ERR) { - pr_err("Recv error. status='%s (%d)' opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - smb_direct_disconnect_rdma_connection(t); - } - put_empty_recvmsg(t, recvmsg); - return; - } - - ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - - ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); - - switch (recvmsg->type) { - case SMB_DIRECT_MSG_NEGOTIATE_REQ: - if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { - put_empty_recvmsg(t, recvmsg); - return; - } - t->negotiation_requested = true; - t->full_packet_received = true; - t->status = SMB_DIRECT_CS_CONNECTED; - enqueue_reassembly(t, recvmsg, 0); - wake_up_interruptible(&t->wait_status); - break; - case SMB_DIRECT_MSG_DATA_TRANSFER: { - struct smb_direct_data_transfer *data_transfer = - (struct smb_direct_data_transfer *)recvmsg->packet; - unsigned int data_length; - int avail_recvmsg_count, receive_credits; - - if (wc->byte_len < - offsetof(struct smb_direct_data_transfer, padding)) { - put_empty_recvmsg(t, recvmsg); - return; - } - - data_length = le32_to_cpu(data_transfer->data_length); - if (data_length) { - if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + - (u64)data_length) { - put_empty_recvmsg(t, recvmsg); - return; - } - - if (t->full_packet_received) - recvmsg->first_segment = true; - - if (le32_to_cpu(data_transfer->remaining_data_length)) - t->full_packet_received = false; - else - t->full_packet_received = true; - - enqueue_reassembly(t, recvmsg, (int)data_length); - wake_up_interruptible(&t->wait_reassembly_queue); - - spin_lock(&t->receive_credit_lock); - receive_credits = --(t->recv_credits); - avail_recvmsg_count = t->count_avail_recvmsg; - spin_unlock(&t->receive_credit_lock); - } else { - put_empty_recvmsg(t, recvmsg); - - spin_lock(&t->receive_credit_lock); - receive_credits = --(t->recv_credits); - avail_recvmsg_count = ++(t->count_avail_recvmsg); - spin_unlock(&t->receive_credit_lock); - } - - t->recv_credit_target = - le16_to_cpu(data_transfer->credits_requested); - atomic_add(le16_to_cpu(data_transfer->credits_granted), - &t->send_credits); - - if (le16_to_cpu(data_transfer->flags) & - SMB_DIRECT_RESPONSE_REQUESTED) - queue_work(smb_direct_wq, &t->send_immediate_work); - - if (atomic_read(&t->send_credits) > 0) - wake_up_interruptible(&t->wait_send_credits); - - if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) - mod_delayed_work(smb_direct_wq, - &t->post_recv_credits_work, 0); - break; - } - default: - break; - } -} - -static int smb_direct_post_recv(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) -{ - struct ib_recv_wr wr; - int ret; - - recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, - recvmsg->packet, t->max_recv_size, - DMA_FROM_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); - if (ret) - return ret; - recvmsg->sge.length = t->max_recv_size; - recvmsg->sge.lkey = t->pd->local_dma_lkey; - recvmsg->cqe.done = recv_done; - - wr.wr_cqe = &recvmsg->cqe; - wr.next = NULL; - wr.sg_list = &recvmsg->sge; - wr.num_sge = 1; - - ret = ib_post_recv(t->qp, &wr, NULL); - if (ret) { - pr_err("Can't post recv: %d\n", ret); - ib_dma_unmap_single(t->cm_id->device, - recvmsg->sge.addr, recvmsg->sge.length, - DMA_FROM_DEVICE); - smb_direct_disconnect_rdma_connection(t); - return ret; - } - return ret; -} - -static int smb_direct_read(struct ksmbd_transport *t, char *buf, - unsigned int size, int unused) -{ - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_data_transfer *data_transfer; - int to_copy, to_read, data_read, offset; - u32 data_length, remaining_data_length, data_offset; - int rc; - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - -again: - if (st->status != SMB_DIRECT_CS_CONNECTED) { - pr_err("disconnected\n"); - return -ENOTCONN; - } - - /* - * No need to hold the reassembly queue lock all the time as we are - * the only one reading from the front of the queue. The transport - * may add more entries to the back of the queue at the same time - */ - if (st->reassembly_data_length >= size) { - int queue_length; - int queue_removed = 0; - - /* - * Need to make sure reassembly_data_length is read before - * reading reassembly_queue_length and calling - * get_first_reassembly. This call is lock free - * as we never read at the end of the queue which are being - * updated in SOFTIRQ as more data is received - */ - virt_rmb(); - queue_length = st->reassembly_queue_length; - data_read = 0; - to_read = size; - offset = st->first_entry_offset; - while (data_read < size) { - recvmsg = get_first_reassembly(st); - data_transfer = smb_direct_recvmsg_payload(recvmsg); - data_length = le32_to_cpu(data_transfer->data_length); - remaining_data_length = - le32_to_cpu(data_transfer->remaining_data_length); - data_offset = le32_to_cpu(data_transfer->data_offset); - - /* - * The upper layer expects RFC1002 length at the - * beginning of the payload. Return it to indicate - * the total length of the packet. This minimize the - * change to upper layer packet processing logic. This - * will be eventually remove when an intermediate - * transport layer is added - */ - if (recvmsg->first_segment && size == 4) { - unsigned int rfc1002_len = - data_length + remaining_data_length; - *((__be32 *)buf) = cpu_to_be32(rfc1002_len); - data_read = 4; - recvmsg->first_segment = false; - ksmbd_debug(RDMA, - "returning rfc1002 length %d\n", - rfc1002_len); - goto read_rfc1002_done; - } - - to_copy = min_t(int, data_length - offset, to_read); - memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, - to_copy); - - /* move on to the next buffer? */ - if (to_copy == data_length - offset) { - queue_length--; - /* - * No need to lock if we are not at the - * end of the queue - */ - if (queue_length) { - list_del(&recvmsg->list); - } else { - spin_lock_irq(&st->reassembly_queue_lock); - list_del(&recvmsg->list); - spin_unlock_irq(&st->reassembly_queue_lock); - } - queue_removed++; - put_recvmsg(st, recvmsg); - offset = 0; - } else { - offset += to_copy; - } - - to_read -= to_copy; - data_read += to_copy; - } - - spin_lock_irq(&st->reassembly_queue_lock); - st->reassembly_data_length -= data_read; - st->reassembly_queue_length -= queue_removed; - spin_unlock_irq(&st->reassembly_queue_lock); - - spin_lock(&st->receive_credit_lock); - st->count_avail_recvmsg += queue_removed; - if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { - spin_unlock(&st->receive_credit_lock); - mod_delayed_work(smb_direct_wq, - &st->post_recv_credits_work, 0); - } else { - spin_unlock(&st->receive_credit_lock); - } - - st->first_entry_offset = offset; - ksmbd_debug(RDMA, - "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", - data_read, st->reassembly_data_length, - st->first_entry_offset); -read_rfc1002_done: - return data_read; - } - - ksmbd_debug(RDMA, "wait_event on more data\n"); - rc = wait_event_interruptible(st->wait_reassembly_queue, - st->reassembly_data_length >= size || - st->status != SMB_DIRECT_CS_CONNECTED); - if (rc) - return -EINTR; - - goto again; -} - -static void smb_direct_post_recv_credits(struct work_struct *work) -{ - struct smb_direct_transport *t = container_of(work, - struct smb_direct_transport, post_recv_credits_work.work); - struct smb_direct_recvmsg *recvmsg; - int receive_credits, credits = 0; - int ret; - int use_free = 1; - - spin_lock(&t->receive_credit_lock); - receive_credits = t->recv_credits; - spin_unlock(&t->receive_credit_lock); - - if (receive_credits < t->recv_credit_target) { - while (true) { - if (use_free) - recvmsg = get_free_recvmsg(t); - else - recvmsg = get_empty_recvmsg(t); - if (!recvmsg) { - if (use_free) { - use_free = 0; - continue; - } else { - break; - } - } - - recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; - recvmsg->first_segment = false; - - ret = smb_direct_post_recv(t, recvmsg); - if (ret) { - pr_err("Can't post recv: %d\n", ret); - put_recvmsg(t, recvmsg); - break; - } - credits++; - } - } - - spin_lock(&t->receive_credit_lock); - t->recv_credits += credits; - t->count_avail_recvmsg -= credits; - spin_unlock(&t->receive_credit_lock); - - spin_lock(&t->lock_new_recv_credits); - t->new_recv_credits += credits; - spin_unlock(&t->lock_new_recv_credits); - - if (credits) - queue_work(smb_direct_wq, &t->send_immediate_work); -} - -static void send_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smb_direct_sendmsg *sendmsg, *sibling; - struct smb_direct_transport *t; - struct list_head *pos, *prev, *end; - - sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); - t = sendmsg->transport; - - ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { - pr_err("Send error. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - smb_direct_disconnect_rdma_connection(t); - } - - if (atomic_dec_and_test(&t->send_pending)) - wake_up(&t->wait_send_pending); - - /* iterate and free the list of messages in reverse. the list's head - * is invalid. - */ - for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; - prev != end; pos = prev, prev = prev->prev) { - sibling = container_of(pos, struct smb_direct_sendmsg, list); - smb_direct_free_sendmsg(t, sibling); - } - - sibling = container_of(pos, struct smb_direct_sendmsg, list); - smb_direct_free_sendmsg(t, sibling); -} - -static int manage_credits_prior_sending(struct smb_direct_transport *t) -{ - int new_credits; - - spin_lock(&t->lock_new_recv_credits); - new_credits = t->new_recv_credits; - t->new_recv_credits = 0; - spin_unlock(&t->lock_new_recv_credits); - - return new_credits; -} - -static int smb_direct_post_send(struct smb_direct_transport *t, - struct ib_send_wr *wr) -{ - int ret; - - atomic_inc(&t->send_pending); - ret = ib_post_send(t->qp, wr, NULL); - if (ret) { - pr_err("failed to post send: %d\n", ret); - if (atomic_dec_and_test(&t->send_pending)) - wake_up(&t->wait_send_pending); - smb_direct_disconnect_rdma_connection(t); - } - return ret; -} - -static void smb_direct_send_ctx_init(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - bool need_invalidate_rkey, - unsigned int remote_key) -{ - INIT_LIST_HEAD(&send_ctx->msg_list); - send_ctx->wr_cnt = 0; - send_ctx->need_invalidate_rkey = need_invalidate_rkey; - send_ctx->remote_key = remote_key; -} - -static int smb_direct_flush_send_list(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - bool is_last) -{ - struct smb_direct_sendmsg *first, *last; - int ret; - - if (list_empty(&send_ctx->msg_list)) - return 0; - - first = list_first_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); - last = list_last_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); - - last->wr.send_flags = IB_SEND_SIGNALED; - last->wr.wr_cqe = &last->cqe; - if (is_last && send_ctx->need_invalidate_rkey) { - last->wr.opcode = IB_WR_SEND_WITH_INV; - last->wr.ex.invalidate_rkey = send_ctx->remote_key; - } - - ret = smb_direct_post_send(t, &first->wr); - if (!ret) { - smb_direct_send_ctx_init(t, send_ctx, - send_ctx->need_invalidate_rkey, - send_ctx->remote_key); - } else { - atomic_add(send_ctx->wr_cnt, &t->send_credits); - wake_up(&t->wait_send_credits); - list_for_each_entry_safe(first, last, &send_ctx->msg_list, - list) { - smb_direct_free_sendmsg(t, first); - } - } - return ret; -} - -static int wait_for_credits(struct smb_direct_transport *t, - wait_queue_head_t *waitq, atomic_t *total_credits, - int needed) -{ - int ret; - - do { - if (atomic_sub_return(needed, total_credits) >= 0) - return 0; - - atomic_add(needed, total_credits); - ret = wait_event_interruptible(*waitq, - atomic_read(total_credits) >= needed || - t->status != SMB_DIRECT_CS_CONNECTED); - - if (t->status != SMB_DIRECT_CS_CONNECTED) - return -ENOTCONN; - else if (ret < 0) - return ret; - } while (true); -} - -static int wait_for_send_credits(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx) -{ - int ret; - - if (send_ctx && - (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { - ret = smb_direct_flush_send_list(t, send_ctx, false); - if (ret) - return ret; - } - - return wait_for_credits(t, &t->wait_send_credits, &t->send_credits, 1); -} - -static int wait_for_rw_credits(struct smb_direct_transport *t, int credits) -{ - return wait_for_credits(t, &t->wait_rw_credits, &t->rw_credits, credits); -} - -static int calc_rw_credits(struct smb_direct_transport *t, - char *buf, unsigned int len) -{ - return DIV_ROUND_UP(get_buf_page_count(buf, len), - t->pages_per_rw_credit); -} - -static int smb_direct_create_header(struct smb_direct_transport *t, - int size, int remaining_data_length, - struct smb_direct_sendmsg **sendmsg_out) -{ - struct smb_direct_sendmsg *sendmsg; - struct smb_direct_data_transfer *packet; - int header_length; - int ret; - - sendmsg = smb_direct_alloc_sendmsg(t); - if (IS_ERR(sendmsg)) - return PTR_ERR(sendmsg); - - /* Fill in the packet header */ - packet = (struct smb_direct_data_transfer *)sendmsg->packet; - packet->credits_requested = cpu_to_le16(t->send_credit_target); - packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); - - packet->flags = 0; - packet->reserved = 0; - if (!size) - packet->data_offset = 0; - else - packet->data_offset = cpu_to_le32(24); - packet->data_length = cpu_to_le32(size); - packet->remaining_data_length = cpu_to_le32(remaining_data_length); - packet->padding = 0; - - ksmbd_debug(RDMA, - "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", - le16_to_cpu(packet->credits_requested), - le16_to_cpu(packet->credits_granted), - le32_to_cpu(packet->data_offset), - le32_to_cpu(packet->data_length), - le32_to_cpu(packet->remaining_data_length)); - - /* Map the packet to DMA */ - header_length = sizeof(struct smb_direct_data_transfer); - /* If this is a packet without payload, don't send padding */ - if (!size) - header_length = - offsetof(struct smb_direct_data_transfer, padding); - - sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, - (void *)packet, - header_length, - DMA_TO_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); - if (ret) { - smb_direct_free_sendmsg(t, sendmsg); - return ret; - } - - sendmsg->num_sge = 1; - sendmsg->sge[0].length = header_length; - sendmsg->sge[0].lkey = t->pd->local_dma_lkey; - - *sendmsg_out = sendmsg; - return 0; -} - -static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nentries) -{ - bool high = is_vmalloc_addr(buf); - struct page *page; - int offset, len; - int i = 0; - - if (size <= 0 || nentries < get_buf_page_count(buf, size)) - return -EINVAL; - - offset = offset_in_page(buf); - buf -= offset; - while (size > 0) { - len = min_t(int, PAGE_SIZE - offset, size); - if (high) - page = vmalloc_to_page(buf); - else - page = kmap_to_page(buf); - - if (!sg_list) - return -EINVAL; - sg_set_page(sg_list, page, len, offset); - sg_list = sg_next(sg_list); - - buf += PAGE_SIZE; - size -= len; - offset = 0; - i++; - } - return i; -} - -static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, - struct scatterlist *sg_list, int nentries, - enum dma_data_direction dir) -{ - int npages; - - npages = get_sg_list(buf, size, sg_list, nentries); - if (npages < 0) - return -EINVAL; - return ib_dma_map_sg(device, sg_list, npages, dir); -} - -static int post_sendmsg(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct smb_direct_sendmsg *msg) -{ - int i; - - for (i = 0; i < msg->num_sge; i++) - ib_dma_sync_single_for_device(t->cm_id->device, - msg->sge[i].addr, msg->sge[i].length, - DMA_TO_DEVICE); - - msg->cqe.done = send_done; - msg->wr.opcode = IB_WR_SEND; - msg->wr.sg_list = &msg->sge[0]; - msg->wr.num_sge = msg->num_sge; - msg->wr.next = NULL; - - if (send_ctx) { - msg->wr.wr_cqe = NULL; - msg->wr.send_flags = 0; - if (!list_empty(&send_ctx->msg_list)) { - struct smb_direct_sendmsg *last; - - last = list_last_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); - last->wr.next = &msg->wr; - } - list_add_tail(&msg->list, &send_ctx->msg_list); - send_ctx->wr_cnt++; - return 0; - } - - msg->wr.wr_cqe = &msg->cqe; - msg->wr.send_flags = IB_SEND_SIGNALED; - return smb_direct_post_send(t, &msg->wr); -} - -static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, - int remaining_data_length) -{ - int i, j, ret; - struct smb_direct_sendmsg *msg; - int data_length; - struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; - - ret = wait_for_send_credits(t, send_ctx); - if (ret) - return ret; - - data_length = 0; - for (i = 0; i < niov; i++) - data_length += iov[i].iov_len; - - ret = smb_direct_create_header(t, data_length, remaining_data_length, - &msg); - if (ret) { - atomic_inc(&t->send_credits); - return ret; - } - - for (i = 0; i < niov; i++) { - struct ib_sge *sge; - int sg_cnt; - - sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); - sg_cnt = get_mapped_sg_list(t->cm_id->device, - iov[i].iov_base, iov[i].iov_len, - sg, SMB_DIRECT_MAX_SEND_SGES - 1, - DMA_TO_DEVICE); - if (sg_cnt <= 0) { - pr_err("failed to map buffer\n"); - ret = -ENOMEM; - goto err; - } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) { - pr_err("buffer not fitted into sges\n"); - ret = -E2BIG; - ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, - DMA_TO_DEVICE); - goto err; - } - - for (j = 0; j < sg_cnt; j++) { - sge = &msg->sge[msg->num_sge]; - sge->addr = sg_dma_address(&sg[j]); - sge->length = sg_dma_len(&sg[j]); - sge->lkey = t->pd->local_dma_lkey; - msg->num_sge++; - } - } - - ret = post_sendmsg(t, send_ctx, msg); - if (ret) - goto err; - return 0; -err: - smb_direct_free_sendmsg(t, msg); - atomic_inc(&t->send_credits); - return ret; -} - -static int smb_direct_writev(struct ksmbd_transport *t, - struct kvec *iov, int niovs, int buflen, - bool need_invalidate, unsigned int remote_key) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - int remaining_data_length; - int start, i, j; - int max_iov_size = st->max_send_size - - sizeof(struct smb_direct_data_transfer); - int ret; - struct kvec vec; - struct smb_direct_send_ctx send_ctx; - - if (st->status != SMB_DIRECT_CS_CONNECTED) - return -ENOTCONN; - - //FIXME: skip RFC1002 header.. - buflen -= 4; - iov[0].iov_base += 4; - iov[0].iov_len -= 4; - - remaining_data_length = buflen; - ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); - - smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); - start = i = 0; - buflen = 0; - while (true) { - buflen += iov[i].iov_len; - if (buflen > max_iov_size) { - if (i > start) { - remaining_data_length -= - (buflen - iov[i].iov_len); - ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); - if (ret) - goto done; - } else { - /* iov[start] is too big, break it */ - int nvec = (buflen + max_iov_size - 1) / - max_iov_size; - - for (j = 0; j < nvec; j++) { - vec.iov_base = - (char *)iov[start].iov_base + - j * max_iov_size; - vec.iov_len = - min_t(int, max_iov_size, - buflen - max_iov_size * j); - remaining_data_length -= vec.iov_len; - ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, - remaining_data_length); - if (ret) - goto done; - } - i++; - if (i == niovs) - break; - } - start = i; - buflen = 0; - } else { - i++; - if (i == niovs) { - /* send out all remaining vecs */ - remaining_data_length -= buflen; - ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); - if (ret) - goto done; - break; - } - } - } - -done: - ret = smb_direct_flush_send_list(st, &send_ctx, true); - - /* - * As an optimization, we don't wait for individual I/O to finish - * before sending the next one. - * Send them all and wait for pending send count to get to 0 - * that means all the I/Os have been out and we are good to return - */ - - wait_event(st->wait_send_pending, - atomic_read(&st->send_pending) == 0); - return ret; -} - -static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t, - struct smb_direct_rdma_rw_msg *msg, - enum dma_data_direction dir) -{ - rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, - msg->sgt.sgl, msg->sgt.nents, dir); - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); -} - -static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, - enum dma_data_direction dir) -{ - struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, - struct smb_direct_rdma_rw_msg, cqe); - struct smb_direct_transport *t = msg->t; - - if (wc->status != IB_WC_SUCCESS) { - msg->status = -EIO; - pr_err("read/write error. opcode = %d, status = %s(%d)\n", - wc->opcode, ib_wc_status_msg(wc->status), wc->status); - if (wc->status != IB_WC_WR_FLUSH_ERR) - smb_direct_disconnect_rdma_connection(t); - } - - complete(msg->completion); -} - -static void read_done(struct ib_cq *cq, struct ib_wc *wc) -{ - read_write_done(cq, wc, DMA_FROM_DEVICE); -} - -static void write_done(struct ib_cq *cq, struct ib_wc *wc) -{ - read_write_done(cq, wc, DMA_TO_DEVICE); -} - -static int smb_direct_rdma_xmit(struct smb_direct_transport *t, - void *buf, int buf_len, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len, - bool is_read) -{ - struct smb_direct_rdma_rw_msg *msg, *next_msg; - int i, ret; - DECLARE_COMPLETION_ONSTACK(completion); - struct ib_send_wr *first_wr; - LIST_HEAD(msg_list); - char *desc_buf; - int credits_needed; - unsigned int desc_buf_len; - size_t total_length = 0; - - if (t->status != SMB_DIRECT_CS_CONNECTED) - return -ENOTCONN; - - /* calculate needed credits */ - credits_needed = 0; - desc_buf = buf; - for (i = 0; i < desc_len / sizeof(*desc); i++) { - desc_buf_len = le32_to_cpu(desc[i].length); - - credits_needed += calc_rw_credits(t, desc_buf, desc_buf_len); - desc_buf += desc_buf_len; - total_length += desc_buf_len; - if (desc_buf_len == 0 || total_length > buf_len || - total_length > t->max_rdma_rw_size) - return -EINVAL; - } - - ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n", - is_read ? "read" : "write", buf_len, credits_needed); - - ret = wait_for_rw_credits(t, credits_needed); - if (ret < 0) - return ret; - - /* build rdma_rw_ctx for each descriptor */ - desc_buf = buf; - for (i = 0; i < desc_len / sizeof(*desc); i++) { - msg = kzalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + - sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); - if (!msg) { - ret = -ENOMEM; - goto out; - } - - desc_buf_len = le32_to_cpu(desc[i].length); - - msg->t = t; - msg->cqe.done = is_read ? read_done : write_done; - msg->completion = &completion; - - msg->sgt.sgl = &msg->sg_list[0]; - ret = sg_alloc_table_chained(&msg->sgt, - get_buf_page_count(desc_buf, desc_buf_len), - msg->sg_list, SG_CHUNK_SIZE); - if (ret) { - kfree(msg); - ret = -ENOMEM; - goto out; - } - - ret = get_sg_list(desc_buf, desc_buf_len, - msg->sgt.sgl, msg->sgt.orig_nents); - if (ret < 0) { - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); - goto out; - } - - ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, - msg->sgt.sgl, - get_buf_page_count(desc_buf, desc_buf_len), - 0, - le64_to_cpu(desc[i].offset), - le32_to_cpu(desc[i].token), - is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (ret < 0) { - pr_err("failed to init rdma_rw_ctx: %d\n", ret); - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); - goto out; - } - - list_add_tail(&msg->list, &msg_list); - desc_buf += desc_buf_len; - } - - /* concatenate work requests of rdma_rw_ctxs */ - first_wr = NULL; - list_for_each_entry_reverse(msg, &msg_list, list) { - first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, - &msg->cqe, first_wr); - } - - ret = ib_post_send(t->qp, first_wr, NULL); - if (ret) { - pr_err("failed to post send wr for RDMA R/W: %d\n", ret); - goto out; - } - - msg = list_last_entry(&msg_list, struct smb_direct_rdma_rw_msg, list); - wait_for_completion(&completion); - ret = msg->status; -out: - list_for_each_entry_safe(msg, next_msg, &msg_list, list) { - list_del(&msg->list); - smb_direct_free_rdma_rw_msg(t, msg, - is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - } - atomic_add(credits_needed, &t->rw_credits); - wake_up(&t->wait_rw_credits); - return ret; -} - -static int smb_direct_rdma_write(struct ksmbd_transport *t, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, - desc, desc_len, false); -} - -static int smb_direct_rdma_read(struct ksmbd_transport *t, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, - desc, desc_len, true); -} - -static void smb_direct_disconnect(struct ksmbd_transport *t) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - - ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); - - smb_direct_disconnect_rdma_work(&st->disconnect_work); - wait_event_interruptible(st->wait_status, - st->status == SMB_DIRECT_CS_DISCONNECTED); - free_transport(st); -} - -static void smb_direct_shutdown(struct ksmbd_transport *t) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - - ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", st->cm_id); - - smb_direct_disconnect_rdma_work(&st->disconnect_work); -} - -static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) -{ - struct smb_direct_transport *t = cm_id->context; - - ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), event->event); - - switch (event->event) { - case RDMA_CM_EVENT_ESTABLISHED: { - t->status = SMB_DIRECT_CS_CONNECTED; - wake_up_interruptible(&t->wait_status); - break; - } - case RDMA_CM_EVENT_DEVICE_REMOVAL: - case RDMA_CM_EVENT_DISCONNECTED: { - ib_drain_qp(t->qp); - - t->status = SMB_DIRECT_CS_DISCONNECTED; - wake_up_interruptible(&t->wait_status); - wake_up_interruptible(&t->wait_reassembly_queue); - wake_up(&t->wait_send_credits); - break; - } - case RDMA_CM_EVENT_CONNECT_ERROR: { - t->status = SMB_DIRECT_CS_DISCONNECTED; - wake_up_interruptible(&t->wait_status); - break; - } - default: - pr_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), - event->event); - break; - } - return 0; -} - -static void smb_direct_qpair_handler(struct ib_event *event, void *context) -{ - struct smb_direct_transport *t = context; - - ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", - t->cm_id, ib_event_msg(event->event), event->event); - - switch (event->event) { - case IB_EVENT_CQ_ERR: - case IB_EVENT_QP_FATAL: - smb_direct_disconnect_rdma_connection(t); - break; - default: - break; - } -} - -static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, - int failed) -{ - struct smb_direct_sendmsg *sendmsg; - struct smb_direct_negotiate_resp *resp; - int ret; - - sendmsg = smb_direct_alloc_sendmsg(t); - if (IS_ERR(sendmsg)) - return -ENOMEM; - - resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; - if (failed) { - memset(resp, 0, sizeof(*resp)); - resp->min_version = cpu_to_le16(0x0100); - resp->max_version = cpu_to_le16(0x0100); - resp->status = STATUS_NOT_SUPPORTED; - } else { - resp->status = STATUS_SUCCESS; - resp->min_version = SMB_DIRECT_VERSION_LE; - resp->max_version = SMB_DIRECT_VERSION_LE; - resp->negotiated_version = SMB_DIRECT_VERSION_LE; - resp->reserved = 0; - resp->credits_requested = - cpu_to_le16(t->send_credit_target); - resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); - resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); - resp->preferred_send_size = cpu_to_le32(t->max_send_size); - resp->max_receive_size = cpu_to_le32(t->max_recv_size); - resp->max_fragmented_size = - cpu_to_le32(t->max_fragmented_recv_size); - } - - sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, - (void *)resp, sizeof(*resp), - DMA_TO_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); - if (ret) { - smb_direct_free_sendmsg(t, sendmsg); - return ret; - } - - sendmsg->num_sge = 1; - sendmsg->sge[0].length = sizeof(*resp); - sendmsg->sge[0].lkey = t->pd->local_dma_lkey; - - ret = post_sendmsg(t, NULL, sendmsg); - if (ret) { - smb_direct_free_sendmsg(t, sendmsg); - return ret; - } - - wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); - return 0; -} - -static int smb_direct_accept_client(struct smb_direct_transport *t) -{ - struct rdma_conn_param conn_param; - struct ib_port_immutable port_immutable; - u32 ird_ord_hdr[2]; - int ret; - - memset(&conn_param, 0, sizeof(conn_param)); - conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, - SMB_DIRECT_CM_INITIATOR_DEPTH); - conn_param.responder_resources = 0; - - t->cm_id->device->ops.get_port_immutable(t->cm_id->device, - t->cm_id->port_num, - &port_immutable); - if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { - ird_ord_hdr[0] = conn_param.responder_resources; - ird_ord_hdr[1] = 1; - conn_param.private_data = ird_ord_hdr; - conn_param.private_data_len = sizeof(ird_ord_hdr); - } else { - conn_param.private_data = NULL; - conn_param.private_data_len = 0; - } - conn_param.retry_count = SMB_DIRECT_CM_RETRY; - conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; - conn_param.flow_control = 0; - - ret = rdma_accept(t->cm_id, &conn_param); - if (ret) { - pr_err("error at rdma_accept: %d\n", ret); - return ret; - } - return 0; -} - -static int smb_direct_prepare_negotiation(struct smb_direct_transport *t) -{ - int ret; - struct smb_direct_recvmsg *recvmsg; - - recvmsg = get_free_recvmsg(t); - if (!recvmsg) - return -ENOMEM; - recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; - - ret = smb_direct_post_recv(t, recvmsg); - if (ret) { - pr_err("Can't post recv: %d\n", ret); - goto out_err; - } - - t->negotiation_requested = false; - ret = smb_direct_accept_client(t); - if (ret) { - pr_err("Can't accept client\n"); - goto out_err; - } - - smb_direct_post_recv_credits(&t->post_recv_credits_work.work); - return 0; -out_err: - put_recvmsg(t, recvmsg); - return ret; -} - -static unsigned int smb_direct_get_max_fr_pages(struct smb_direct_transport *t) -{ - return min_t(unsigned int, - t->cm_id->device->attrs.max_fast_reg_page_list_len, - 256); -} - -static int smb_direct_init_params(struct smb_direct_transport *t, - struct ib_qp_cap *cap) -{ - struct ib_device *device = t->cm_id->device; - int max_send_sges, max_rw_wrs, max_send_wrs; - unsigned int max_sge_per_wr, wrs_per_credit; - - /* need 3 more sge. because a SMB_DIRECT header, SMB2 header, - * SMB2 response could be mapped. - */ - t->max_send_size = smb_direct_max_send_size; - max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 3; - if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { - pr_err("max_send_size %d is too large\n", t->max_send_size); - return -EINVAL; - } - - /* Calculate the number of work requests for RDMA R/W. - * The maximum number of pages which can be registered - * with one Memory region can be transferred with one - * R/W credit. And at least 4 work requests for each credit - * are needed for MR registration, RDMA R/W, local & remote - * MR invalidation. - */ - t->max_rdma_rw_size = smb_direct_max_read_write_size; - t->pages_per_rw_credit = smb_direct_get_max_fr_pages(t); - t->max_rw_credits = DIV_ROUND_UP(t->max_rdma_rw_size, - (t->pages_per_rw_credit - 1) * - PAGE_SIZE); - - max_sge_per_wr = min_t(unsigned int, device->attrs.max_send_sge, - device->attrs.max_sge_rd); - max_sge_per_wr = max_t(unsigned int, max_sge_per_wr, - max_send_sges); - wrs_per_credit = max_t(unsigned int, 4, - DIV_ROUND_UP(t->pages_per_rw_credit, - max_sge_per_wr) + 1); - max_rw_wrs = t->max_rw_credits * wrs_per_credit; - - max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; - if (max_send_wrs > device->attrs.max_cqe || - max_send_wrs > device->attrs.max_qp_wr) { - pr_err("consider lowering send_credit_target = %d\n", - smb_direct_send_credit_target); - pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); - return -EINVAL; - } - - if (smb_direct_receive_credit_max > device->attrs.max_cqe || - smb_direct_receive_credit_max > device->attrs.max_qp_wr) { - pr_err("consider lowering receive_credit_max = %d\n", - smb_direct_receive_credit_max); - pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); - return -EINVAL; - } - - if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { - pr_err("warning: device max_recv_sge = %d too small\n", - device->attrs.max_recv_sge); - return -EINVAL; - } - - t->recv_credits = 0; - t->count_avail_recvmsg = 0; - - t->recv_credit_max = smb_direct_receive_credit_max; - t->recv_credit_target = 10; - t->new_recv_credits = 0; - - t->send_credit_target = smb_direct_send_credit_target; - atomic_set(&t->send_credits, 0); - atomic_set(&t->rw_credits, t->max_rw_credits); - - t->max_send_size = smb_direct_max_send_size; - t->max_recv_size = smb_direct_max_receive_size; - t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; - - cap->max_send_wr = max_send_wrs; - cap->max_recv_wr = t->recv_credit_max; - cap->max_send_sge = max_sge_per_wr; - cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; - cap->max_inline_data = 0; - cap->max_rdma_ctxs = t->max_rw_credits; - return 0; -} - -static void smb_direct_destroy_pools(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg; - - while ((recvmsg = get_free_recvmsg(t))) - mempool_free(recvmsg, t->recvmsg_mempool); - while ((recvmsg = get_empty_recvmsg(t))) - mempool_free(recvmsg, t->recvmsg_mempool); - - mempool_destroy(t->recvmsg_mempool); - t->recvmsg_mempool = NULL; - - kmem_cache_destroy(t->recvmsg_cache); - t->recvmsg_cache = NULL; - - mempool_destroy(t->sendmsg_mempool); - t->sendmsg_mempool = NULL; - - kmem_cache_destroy(t->sendmsg_cache); - t->sendmsg_cache = NULL; -} - -static int smb_direct_create_pools(struct smb_direct_transport *t) -{ - char name[80]; - int i; - struct smb_direct_recvmsg *recvmsg; - - snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); - t->sendmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_sendmsg) + - sizeof(struct smb_direct_negotiate_resp), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!t->sendmsg_cache) - return -ENOMEM; - - t->sendmsg_mempool = mempool_create(t->send_credit_target, - mempool_alloc_slab, mempool_free_slab, - t->sendmsg_cache); - if (!t->sendmsg_mempool) - goto err; - - snprintf(name, sizeof(name), "smb_direct_resp_%p", t); - t->recvmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_recvmsg) + - t->max_recv_size, - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!t->recvmsg_cache) - goto err; - - t->recvmsg_mempool = - mempool_create(t->recv_credit_max, mempool_alloc_slab, - mempool_free_slab, t->recvmsg_cache); - if (!t->recvmsg_mempool) - goto err; - - INIT_LIST_HEAD(&t->recvmsg_queue); - - for (i = 0; i < t->recv_credit_max; i++) { - recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); - if (!recvmsg) - goto err; - recvmsg->transport = t; - list_add(&recvmsg->list, &t->recvmsg_queue); - } - t->count_avail_recvmsg = t->recv_credit_max; - - return 0; -err: - smb_direct_destroy_pools(t); - return -ENOMEM; -} - -static int smb_direct_create_qpair(struct smb_direct_transport *t, - struct ib_qp_cap *cap) -{ - int ret; - struct ib_qp_init_attr qp_attr; - int pages_per_rw; - - t->pd = ib_alloc_pd(t->cm_id->device, 0); - if (IS_ERR(t->pd)) { - pr_err("Can't create RDMA PD\n"); - ret = PTR_ERR(t->pd); - t->pd = NULL; - return ret; - } - - t->send_cq = ib_alloc_cq(t->cm_id->device, t, - smb_direct_send_credit_target + cap->max_rdma_ctxs, - 0, IB_POLL_WORKQUEUE); - if (IS_ERR(t->send_cq)) { - pr_err("Can't create RDMA send CQ\n"); - ret = PTR_ERR(t->send_cq); - t->send_cq = NULL; - goto err; - } - - t->recv_cq = ib_alloc_cq(t->cm_id->device, t, - t->recv_credit_max, 0, IB_POLL_WORKQUEUE); - if (IS_ERR(t->recv_cq)) { - pr_err("Can't create RDMA recv CQ\n"); - ret = PTR_ERR(t->recv_cq); - t->recv_cq = NULL; - goto err; - } - - memset(&qp_attr, 0, sizeof(qp_attr)); - qp_attr.event_handler = smb_direct_qpair_handler; - qp_attr.qp_context = t; - qp_attr.cap = *cap; - qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; - qp_attr.qp_type = IB_QPT_RC; - qp_attr.send_cq = t->send_cq; - qp_attr.recv_cq = t->recv_cq; - qp_attr.port_num = ~0; - - ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); - if (ret) { - pr_err("Can't create RDMA QP: %d\n", ret); - goto err; - } - - t->qp = t->cm_id->qp; - t->cm_id->event_handler = smb_direct_cm_handler; - - pages_per_rw = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; - if (pages_per_rw > t->cm_id->device->attrs.max_sgl_rd) { - ret = ib_mr_pool_init(t->qp, &t->qp->rdma_mrs, - t->max_rw_credits, IB_MR_TYPE_MEM_REG, - t->pages_per_rw_credit, 0); - if (ret) { - pr_err("failed to init mr pool count %d pages %d\n", - t->max_rw_credits, t->pages_per_rw_credit); - goto err; - } - } - - return 0; -err: - if (t->qp) { - ib_destroy_qp(t->qp); - t->qp = NULL; - } - if (t->recv_cq) { - ib_destroy_cq(t->recv_cq); - t->recv_cq = NULL; - } - if (t->send_cq) { - ib_destroy_cq(t->send_cq); - t->send_cq = NULL; - } - if (t->pd) { - ib_dealloc_pd(t->pd); - t->pd = NULL; - } - return ret; -} - -static int smb_direct_prepare(struct ksmbd_transport *t) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_negotiate_req *req; - int ret; - - ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); - ret = wait_event_interruptible_timeout(st->wait_status, - st->negotiation_requested || - st->status == SMB_DIRECT_CS_DISCONNECTED, - SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); - if (ret <= 0 || st->status == SMB_DIRECT_CS_DISCONNECTED) - return ret < 0 ? ret : -ETIMEDOUT; - - recvmsg = get_first_reassembly(st); - if (!recvmsg) - return -ECONNABORTED; - - ret = smb_direct_check_recvmsg(recvmsg); - if (ret == -ECONNABORTED) - goto out; - - req = (struct smb_direct_negotiate_req *)recvmsg->packet; - st->max_recv_size = min_t(int, st->max_recv_size, - le32_to_cpu(req->preferred_send_size)); - st->max_send_size = min_t(int, st->max_send_size, - le32_to_cpu(req->max_receive_size)); - st->max_fragmented_send_size = - le32_to_cpu(req->max_fragmented_size); - st->max_fragmented_recv_size = - (st->recv_credit_max * st->max_recv_size) / 2; - - ret = smb_direct_send_negotiate_response(st, ret); -out: - spin_lock_irq(&st->reassembly_queue_lock); - st->reassembly_queue_length--; - list_del(&recvmsg->list); - spin_unlock_irq(&st->reassembly_queue_lock); - put_recvmsg(st, recvmsg); - - return ret; -} - -static int smb_direct_connect(struct smb_direct_transport *st) -{ - int ret; - struct ib_qp_cap qp_cap; - - ret = smb_direct_init_params(st, &qp_cap); - if (ret) { - pr_err("Can't configure RDMA parameters\n"); - return ret; - } - - ret = smb_direct_create_pools(st); - if (ret) { - pr_err("Can't init RDMA pool: %d\n", ret); - return ret; - } - - ret = smb_direct_create_qpair(st, &qp_cap); - if (ret) { - pr_err("Can't accept RDMA client: %d\n", ret); - return ret; - } - - ret = smb_direct_prepare_negotiation(st); - if (ret) { - pr_err("Can't negotiate: %d\n", ret); - return ret; - } - return 0; -} - -static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) -{ - if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) - return false; - if (attrs->max_fast_reg_page_list_len == 0) - return false; - return true; -} - -static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) -{ - struct smb_direct_transport *t; - int ret; - - if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { - ksmbd_debug(RDMA, - "Fast Registration Work Requests is not supported. device capabilities=%llx\n", - new_cm_id->device->attrs.device_cap_flags); - return -EPROTONOSUPPORT; - } - - t = alloc_transport(new_cm_id); - if (!t) - return -ENOMEM; - - ret = smb_direct_connect(t); - if (ret) - goto out_err; - - KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, "ksmbd:r%u", - smb_direct_port); - if (IS_ERR(KSMBD_TRANS(t)->handler)) { - ret = PTR_ERR(KSMBD_TRANS(t)->handler); - pr_err("Can't start thread\n"); - goto out_err; - } - - return 0; -out_err: - free_transport(t); - return ret; -} - -static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) -{ - switch (event->event) { - case RDMA_CM_EVENT_CONNECT_REQUEST: { - int ret = smb_direct_handle_connect_request(cm_id); - - if (ret) { - pr_err("Can't create transport: %d\n", ret); - return ret; - } - - ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", - cm_id); - break; - } - default: - pr_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), event->event); - break; - } - return 0; -} - -static int smb_direct_listen(int port) -{ - int ret; - struct rdma_cm_id *cm_id; - struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(INADDR_ANY), - .sin_port = htons(port), - }; - - cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, - &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); - if (IS_ERR(cm_id)) { - pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); - return PTR_ERR(cm_id); - } - - ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); - if (ret) { - pr_err("Can't bind: %d\n", ret); - goto err; - } - - smb_direct_listener.cm_id = cm_id; - - ret = rdma_listen(cm_id, 10); - if (ret) { - pr_err("Can't listen: %d\n", ret); - goto err; - } - return 0; -err: - smb_direct_listener.cm_id = NULL; - rdma_destroy_id(cm_id); - return ret; -} - -static int smb_direct_ib_client_add(struct ib_device *ib_dev) -{ - struct smb_direct_device *smb_dev; - - /* Set 5445 port if device type is iWARP(No IB) */ - if (ib_dev->node_type != RDMA_NODE_IB_CA) - smb_direct_port = SMB_DIRECT_PORT_IWARP; - - if (!ib_dev->ops.get_netdev || - !rdma_frwr_is_supported(&ib_dev->attrs)) - return 0; - - smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); - if (!smb_dev) - return -ENOMEM; - smb_dev->ib_dev = ib_dev; - - write_lock(&smb_direct_device_lock); - list_add(&smb_dev->list, &smb_direct_device_list); - write_unlock(&smb_direct_device_lock); - - ksmbd_debug(RDMA, "ib device added: name %s\n", ib_dev->name); - return 0; -} - -static void smb_direct_ib_client_remove(struct ib_device *ib_dev, - void *client_data) -{ - struct smb_direct_device *smb_dev, *tmp; - - write_lock(&smb_direct_device_lock); - list_for_each_entry_safe(smb_dev, tmp, &smb_direct_device_list, list) { - if (smb_dev->ib_dev == ib_dev) { - list_del(&smb_dev->list); - kfree(smb_dev); - break; - } - } - write_unlock(&smb_direct_device_lock); -} - -static struct ib_client smb_direct_ib_client = { - .name = "ksmbd_smb_direct_ib", - .add = smb_direct_ib_client_add, - .remove = smb_direct_ib_client_remove, -}; - -int ksmbd_rdma_init(void) -{ - int ret; - - smb_direct_listener.cm_id = NULL; - - ret = ib_register_client(&smb_direct_ib_client); - if (ret) { - pr_err("failed to ib_register_client\n"); - return ret; - } - - /* When a client is running out of send credits, the credits are - * granted by the server's sending a packet using this queue. - * This avoids the situation that a clients cannot send packets - * for lack of credits - */ - smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", - WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); - if (!smb_direct_wq) - return -ENOMEM; - - ret = smb_direct_listen(smb_direct_port); - if (ret) { - destroy_workqueue(smb_direct_wq); - smb_direct_wq = NULL; - pr_err("Can't listen: %d\n", ret); - return ret; - } - - ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", - smb_direct_listener.cm_id); - return 0; -} - -void ksmbd_rdma_destroy(void) -{ - if (!smb_direct_listener.cm_id) - return; - - ib_unregister_client(&smb_direct_ib_client); - rdma_destroy_id(smb_direct_listener.cm_id); - - smb_direct_listener.cm_id = NULL; - - if (smb_direct_wq) { - destroy_workqueue(smb_direct_wq); - smb_direct_wq = NULL; - } -} - -bool ksmbd_rdma_capable_netdev(struct net_device *netdev) -{ - struct smb_direct_device *smb_dev; - int i; - bool rdma_capable = false; - - read_lock(&smb_direct_device_lock); - list_for_each_entry(smb_dev, &smb_direct_device_list, list) { - for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { - struct net_device *ndev; - - ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, - i + 1); - if (!ndev) - continue; - - if (ndev == netdev) { - dev_put(ndev); - rdma_capable = true; - goto out; - } - dev_put(ndev); - } - } -out: - read_unlock(&smb_direct_device_lock); - - if (rdma_capable == false) { - struct ib_device *ibdev; - - ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); - if (ibdev) { - if (rdma_frwr_is_supported(&ibdev->attrs)) - rdma_capable = true; - ib_device_put(ibdev); - } - } - - return rdma_capable; -} - -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { - .prepare = smb_direct_prepare, - .disconnect = smb_direct_disconnect, - .shutdown = smb_direct_shutdown, - .writev = smb_direct_writev, - .read = smb_direct_read, - .rdma_read = smb_direct_rdma_read, - .rdma_write = smb_direct_rdma_write, -}; diff --git a/fs/ksmbd/transport_rdma.h b/fs/ksmbd/transport_rdma.h deleted file mode 100644 index 77aee4e5c9dc..000000000000 --- a/fs/ksmbd/transport_rdma.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2017, Microsoft Corporation. - * Copyright (C) 2018, LG Electronics. - */ - -#ifndef __KSMBD_TRANSPORT_RDMA_H__ -#define __KSMBD_TRANSPORT_RDMA_H__ - -#define SMBD_DEFAULT_IOSIZE (8 * 1024 * 1024) -#define SMBD_MIN_IOSIZE (512 * 1024) -#define SMBD_MAX_IOSIZE (16 * 1024 * 1024) - -/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ -struct smb_direct_negotiate_req { - __le16 min_version; - __le16 max_version; - __le16 reserved; - __le16 credits_requested; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ -struct smb_direct_negotiate_resp { - __le16 min_version; - __le16 max_version; - __le16 negotiated_version; - __le16 reserved; - __le16 credits_requested; - __le16 credits_granted; - __le32 status; - __le32 max_readwrite_size; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 - -/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ -struct smb_direct_data_transfer { - __le16 credits_requested; - __le16 credits_granted; - __le16 flags; - __le16 reserved; - __le32 remaining_data_length; - __le32 data_offset; - __le32 data_length; - __le32 padding; - __u8 buffer[]; -} __packed; - -#ifdef CONFIG_SMB_SERVER_SMBDIRECT -int ksmbd_rdma_init(void); -void ksmbd_rdma_destroy(void); -bool ksmbd_rdma_capable_netdev(struct net_device *netdev); -void init_smbd_max_io_size(unsigned int sz); -unsigned int get_smbd_max_read_write_size(void); -#else -static inline int ksmbd_rdma_init(void) { return 0; } -static inline int ksmbd_rdma_destroy(void) { return 0; } -static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } -static inline void init_smbd_max_io_size(unsigned int sz) { } -static inline unsigned int get_smbd_max_read_write_size(void) { return 0; } -#endif - -#endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c deleted file mode 100644 index eff7a1d793f0..000000000000 --- a/fs/ksmbd/transport_tcp.c +++ /dev/null @@ -1,649 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include - -#include "smb_common.h" -#include "server.h" -#include "auth.h" -#include "connection.h" -#include "transport_tcp.h" - -#define IFACE_STATE_DOWN BIT(0) -#define IFACE_STATE_CONFIGURED BIT(1) - -static atomic_t active_num_conn; - -struct interface { - struct task_struct *ksmbd_kthread; - struct socket *ksmbd_socket; - struct list_head entry; - char *name; - struct mutex sock_release_lock; - int state; -}; - -static LIST_HEAD(iface_list); - -static int bind_additional_ifaces; - -struct tcp_transport { - struct ksmbd_transport transport; - struct socket *sock; - struct kvec *iov; - unsigned int nr_iov; -}; - -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; - -static void tcp_stop_kthread(struct task_struct *kthread); -static struct interface *alloc_iface(char *ifname); - -#define KSMBD_TRANS(t) (&(t)->transport) -#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ - struct tcp_transport, transport)) - -static inline void ksmbd_tcp_nodelay(struct socket *sock) -{ - tcp_sock_set_nodelay(sock->sk); -} - -static inline void ksmbd_tcp_reuseaddr(struct socket *sock) -{ - sock_set_reuseaddr(sock->sk); -} - -static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) -{ - lock_sock(sock->sk); - if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) - sock->sk->sk_rcvtimeo = secs * HZ; - else - sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; - release_sock(sock->sk); -} - -static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) -{ - sock_set_sndtimeo(sock->sk, secs); -} - -static struct tcp_transport *alloc_transport(struct socket *client_sk) -{ - struct tcp_transport *t; - struct ksmbd_conn *conn; - - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return NULL; - t->sock = client_sk; - - conn = ksmbd_conn_alloc(); - if (!conn) { - kfree(t); - return NULL; - } - - conn->transport = KSMBD_TRANS(t); - KSMBD_TRANS(t)->conn = conn; - KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; - return t; -} - -static void free_transport(struct tcp_transport *t) -{ - kernel_sock_shutdown(t->sock, SHUT_RDWR); - sock_release(t->sock); - t->sock = NULL; - - ksmbd_conn_free(KSMBD_TRANS(t)->conn); - kfree(t->iov); - kfree(t); -} - -/** - * kvec_array_init() - initialize a IO vector segment - * @new: IO vector to be initialized - * @iov: base IO vector - * @nr_segs: number of segments in base iov - * @bytes: total iovec length so far for read - * - * Return: Number of IO segments - */ -static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, - unsigned int nr_segs, size_t bytes) -{ - size_t base = 0; - - while (bytes || !iov->iov_len) { - int copy = min(bytes, iov->iov_len); - - bytes -= copy; - base += copy; - if (iov->iov_len == base) { - iov++; - nr_segs--; - base = 0; - } - } - - memcpy(new, iov, sizeof(*iov) * nr_segs); - new->iov_base += base; - new->iov_len -= base; - return nr_segs; -} - -/** - * get_conn_iovec() - get connection iovec for reading from socket - * @t: TCP transport instance - * @nr_segs: number of segments in iov - * - * Return: return existing or newly allocate iovec - */ -static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs) -{ - struct kvec *new_iov; - - if (t->iov && nr_segs <= t->nr_iov) - return t->iov; - - /* not big enough -- allocate a new one and release the old */ - new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); - if (new_iov) { - kfree(t->iov); - t->iov = new_iov; - t->nr_iov = nr_segs; - } - return new_iov; -} - -static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) -{ - switch (sa->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *)sa)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); - } - return 0; -} - -/** - * ksmbd_tcp_new_connection() - create a new tcp session on mount - * @client_sk: socket associated with new connection - * - * whenever a new connection is requested, create a conn thread - * (session thread) to handle new incoming smb requests from the connection - * - * Return: 0 on success, otherwise error - */ -static int ksmbd_tcp_new_connection(struct socket *client_sk) -{ - struct sockaddr *csin; - int rc = 0; - struct tcp_transport *t; - - t = alloc_transport(client_sk); - if (!t) { - sock_release(client_sk); - return -ENOMEM; - } - - csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); - if (kernel_getpeername(client_sk, csin) < 0) { - pr_err("client ip resolution failed\n"); - rc = -EINVAL; - goto out_error; - } - - KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, - "ksmbd:%u", - ksmbd_tcp_get_port(csin)); - if (IS_ERR(KSMBD_TRANS(t)->handler)) { - pr_err("cannot start conn thread\n"); - rc = PTR_ERR(KSMBD_TRANS(t)->handler); - free_transport(t); - } - return rc; - -out_error: - free_transport(t); - return rc; -} - -/** - * ksmbd_kthread_fn() - listen to new SMB connections and callback server - * @p: arguments to forker thread - * - * Return: 0 on success, error number otherwise - */ -static int ksmbd_kthread_fn(void *p) -{ - struct socket *client_sk = NULL; - struct interface *iface = (struct interface *)p; - int ret; - - while (!kthread_should_stop()) { - mutex_lock(&iface->sock_release_lock); - if (!iface->ksmbd_socket) { - mutex_unlock(&iface->sock_release_lock); - break; - } - ret = kernel_accept(iface->ksmbd_socket, &client_sk, - SOCK_NONBLOCK); - mutex_unlock(&iface->sock_release_lock); - if (ret) { - if (ret == -EAGAIN) - /* check for new connections every 100 msecs */ - schedule_timeout_interruptible(HZ / 10); - continue; - } - - if (server_conf.max_connections && - atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { - pr_info_ratelimited("Limit the maximum number of connections(%u)\n", - atomic_read(&active_num_conn)); - atomic_dec(&active_num_conn); - sock_release(client_sk); - continue; - } - - ksmbd_debug(CONN, "connect success: accepted new connection\n"); - client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; - client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; - - ksmbd_tcp_new_connection(client_sk); - } - - ksmbd_debug(CONN, "releasing socket\n"); - return 0; -} - -/** - * ksmbd_tcp_run_kthread() - start forker thread - * @iface: pointer to struct interface - * - * start forker thread(ksmbd/0) at module init time to listen - * on port 445 for new SMB connection requests. It creates per connection - * server threads(ksmbd/x) - * - * Return: 0 on success or error number - */ -static int ksmbd_tcp_run_kthread(struct interface *iface) -{ - int rc; - struct task_struct *kthread; - - kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", - iface->name); - if (IS_ERR(kthread)) { - rc = PTR_ERR(kthread); - return rc; - } - iface->ksmbd_kthread = kthread; - - return 0; -} - -/** - * ksmbd_tcp_readv() - read data from socket in given iovec - * @t: TCP transport instance - * @iov_orig: base IO vector - * @nr_segs: number of segments in base iov - * @to_read: number of bytes to read from socket - * @max_retries: maximum retry count - * - * Return: on success return number of bytes read from socket, - * otherwise return error number - */ -static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, - unsigned int nr_segs, unsigned int to_read, - int max_retries) -{ - int length = 0; - int total_read; - unsigned int segs; - struct msghdr ksmbd_msg; - struct kvec *iov; - struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn; - - iov = get_conn_iovec(t, nr_segs); - if (!iov) - return -ENOMEM; - - ksmbd_msg.msg_control = NULL; - ksmbd_msg.msg_controllen = 0; - - for (total_read = 0; to_read; total_read += length, to_read -= length) { - try_to_freeze(); - - if (!ksmbd_conn_alive(conn)) { - total_read = -ESHUTDOWN; - break; - } - segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); - - length = kernel_recvmsg(t->sock, &ksmbd_msg, - iov, segs, to_read, 0); - - if (length == -EINTR) { - total_read = -ESHUTDOWN; - break; - } else if (ksmbd_conn_need_reconnect(conn)) { - total_read = -EAGAIN; - break; - } else if (length == -ERESTARTSYS || length == -EAGAIN) { - /* - * If max_retries is negative, Allow unlimited - * retries to keep connection with inactive sessions. - */ - if (max_retries == 0) { - total_read = length; - break; - } else if (max_retries > 0) { - max_retries--; - } - - usleep_range(1000, 2000); - length = 0; - continue; - } else if (length <= 0) { - total_read = length; - break; - } - } - return total_read; -} - -/** - * ksmbd_tcp_read() - read data from socket in given buffer - * @t: TCP transport instance - * @buf: buffer to store read data from socket - * @to_read: number of bytes to read from socket - * - * Return: on success return number of bytes read from socket, - * otherwise return error number - */ -static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, - unsigned int to_read, int max_retries) -{ - struct kvec iov; - - iov.iov_base = buf; - iov.iov_len = to_read; - - return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read, max_retries); -} - -static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, - int nvecs, int size, bool need_invalidate, - unsigned int remote_key) - -{ - struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; - - return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); -} - -static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) -{ - free_transport(TCP_TRANS(t)); - if (server_conf.max_connections) - atomic_dec(&active_num_conn); -} - -static void tcp_destroy_socket(struct socket *ksmbd_socket) -{ - int ret; - - if (!ksmbd_socket) - return; - - /* set zero to timeout */ - ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); - ksmbd_tcp_snd_timeout(ksmbd_socket, 0); - - ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); - if (ret) - pr_err("Failed to shutdown socket: %d\n", ret); - sock_release(ksmbd_socket); -} - -/** - * create_socket - create socket for ksmbd/0 - * - * Return: 0 on success, error number otherwise - */ -static int create_socket(struct interface *iface) -{ - int ret; - struct sockaddr_in6 sin6; - struct sockaddr_in sin; - struct socket *ksmbd_socket; - bool ipv4 = false; - - ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); - if (ret) { - if (ret != -EAFNOSUPPORT) - pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret); - ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, - &ksmbd_socket); - if (ret) { - pr_err("Can't create socket for ipv4: %d\n", ret); - goto out_clear; - } - - sin.sin_family = PF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(server_conf.tcp_port); - ipv4 = true; - } else { - sin6.sin6_family = PF_INET6; - sin6.sin6_addr = in6addr_any; - sin6.sin6_port = htons(server_conf.tcp_port); - } - - ksmbd_tcp_nodelay(ksmbd_socket); - ksmbd_tcp_reuseaddr(ksmbd_socket); - - ret = sock_setsockopt(ksmbd_socket, - SOL_SOCKET, - SO_BINDTODEVICE, - KERNEL_SOCKPTR(iface->name), - strlen(iface->name)); - if (ret != -ENODEV && ret < 0) { - pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret); - goto out_error; - } - - if (ipv4) - ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, - sizeof(sin)); - else - ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, - sizeof(sin6)); - if (ret) { - pr_err("Failed to bind socket: %d\n", ret); - goto out_error; - } - - ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; - ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; - - ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); - if (ret) { - pr_err("Port listen() error: %d\n", ret); - goto out_error; - } - - iface->ksmbd_socket = ksmbd_socket; - ret = ksmbd_tcp_run_kthread(iface); - if (ret) { - pr_err("Can't start ksmbd main kthread: %d\n", ret); - goto out_error; - } - iface->state = IFACE_STATE_CONFIGURED; - - return 0; - -out_error: - tcp_destroy_socket(ksmbd_socket); -out_clear: - iface->ksmbd_socket = NULL; - return ret; -} - -static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct net_device *netdev = netdev_notifier_info_to_dev(ptr); - struct interface *iface; - int ret, found = 0; - - switch (event) { - case NETDEV_UP: - if (netif_is_bridge_port(netdev)) - return NOTIFY_OK; - - list_for_each_entry(iface, &iface_list, entry) { - if (!strcmp(iface->name, netdev->name)) { - found = 1; - if (iface->state != IFACE_STATE_DOWN) - break; - ret = create_socket(iface); - if (ret) - return NOTIFY_OK; - break; - } - } - if (!found && bind_additional_ifaces) { - iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); - if (!iface) - return NOTIFY_OK; - ret = create_socket(iface); - if (ret) - break; - } - break; - case NETDEV_DOWN: - list_for_each_entry(iface, &iface_list, entry) { - if (!strcmp(iface->name, netdev->name) && - iface->state == IFACE_STATE_CONFIGURED) { - tcp_stop_kthread(iface->ksmbd_kthread); - iface->ksmbd_kthread = NULL; - mutex_lock(&iface->sock_release_lock); - tcp_destroy_socket(iface->ksmbd_socket); - iface->ksmbd_socket = NULL; - mutex_unlock(&iface->sock_release_lock); - - iface->state = IFACE_STATE_DOWN; - break; - } - } - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block ksmbd_netdev_notifier = { - .notifier_call = ksmbd_netdev_event, -}; - -int ksmbd_tcp_init(void) -{ - register_netdevice_notifier(&ksmbd_netdev_notifier); - - return 0; -} - -static void tcp_stop_kthread(struct task_struct *kthread) -{ - int ret; - - if (!kthread) - return; - - ret = kthread_stop(kthread); - if (ret) - pr_err("failed to stop forker thread\n"); -} - -void ksmbd_tcp_destroy(void) -{ - struct interface *iface, *tmp; - - unregister_netdevice_notifier(&ksmbd_netdev_notifier); - - list_for_each_entry_safe(iface, tmp, &iface_list, entry) { - list_del(&iface->entry); - kfree(iface->name); - kfree(iface); - } -} - -static struct interface *alloc_iface(char *ifname) -{ - struct interface *iface; - - if (!ifname) - return NULL; - - iface = kzalloc(sizeof(struct interface), GFP_KERNEL); - if (!iface) { - kfree(ifname); - return NULL; - } - - iface->name = ifname; - iface->state = IFACE_STATE_DOWN; - list_add(&iface->entry, &iface_list); - mutex_init(&iface->sock_release_lock); - return iface; -} - -int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) -{ - int sz = 0; - - if (!ifc_list_sz) { - struct net_device *netdev; - - rtnl_lock(); - for_each_netdev(&init_net, netdev) { - if (netif_is_bridge_port(netdev)) - continue; - if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) - return -ENOMEM; - } - rtnl_unlock(); - bind_additional_ifaces = 1; - return 0; - } - - while (ifc_list_sz > 0) { - if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) - return -ENOMEM; - - sz = strlen(ifc_list); - if (!sz) - break; - - ifc_list += sz + 1; - ifc_list_sz -= (sz + 1); - } - - bind_additional_ifaces = 0; - - return 0; -} - -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { - .read = ksmbd_tcp_read, - .writev = ksmbd_tcp_writev, - .disconnect = ksmbd_tcp_disconnect, -}; diff --git a/fs/ksmbd/transport_tcp.h b/fs/ksmbd/transport_tcp.h deleted file mode 100644 index e338bebe322f..000000000000 --- a/fs/ksmbd/transport_tcp.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_TRANSPORT_TCP_H__ -#define __KSMBD_TRANSPORT_TCP_H__ - -int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); -int ksmbd_tcp_init(void); -void ksmbd_tcp_destroy(void); - -#endif /* __KSMBD_TRANSPORT_TCP_H__ */ diff --git a/fs/ksmbd/unicode.c b/fs/ksmbd/unicode.c deleted file mode 100644 index a0db699ddafd..000000000000 --- a/fs/ksmbd/unicode.c +++ /dev/null @@ -1,384 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Some of the source code in this file came from fs/cifs/cifs_unicode.c - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * Modified by Steve French (sfrench@us.ibm.com) - * Modified by Namjae Jeon (linkinjeon@kernel.org) - */ -#include -#include -#include -#include "glob.h" -#include "unicode.h" -#include "uniupr.h" -#include "smb_common.h" - -/* - * smb_utf16_bytes() - how long will a string be after conversion? - * @from: pointer to input string - * @maxbytes: don't go past this many bytes of input string - * @codepage: destination codepage - * - * Walk a utf16le string and return the number of bytes that the string will - * be after being converted to the given charset, not including any null - * termination required. Don't walk past maxbytes in the source buffer. - * - * Return: string length after conversion - */ -static int smb_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage) -{ - int i; - int charlen, outlen = 0; - int maxwords = maxbytes / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp; - - for (i = 0; i < maxwords; i++) { - ftmp = get_unaligned_le16(&from[i]); - if (ftmp == 0) - break; - - charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); - if (charlen > 0) - outlen += charlen; - else - outlen++; - } - - return outlen; -} - -/* - * cifs_mapchar() - convert a host-endian char to proper char in codepage - * @target: where converted character should be copied - * @src_char: 2 byte host-endian source character - * @cp: codepage to which character should be converted - * @mapchar: should character be mapped according to mapchars mount option? - * - * This function handles the conversion of a single character. It is the - * responsibility of the caller to ensure that the target buffer is large - * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). - * - * Return: string length after conversion - */ -static int -cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, - bool mapchar) -{ - int len = 1; - - if (!mapchar) - goto cp_convert; - - /* - * BB: Cannot handle remapping UNI_SLASH until all the calls to - * build_path_from_dentry are modified, as they use slash as - * separator. - */ - switch (src_char) { - case UNI_COLON: - *target = ':'; - break; - case UNI_ASTERISK: - *target = '*'; - break; - case UNI_QUESTION: - *target = '?'; - break; - case UNI_PIPE: - *target = '|'; - break; - case UNI_GRTRTHAN: - *target = '>'; - break; - case UNI_LESSTHAN: - *target = '<'; - break; - default: - goto cp_convert; - } - -out: - return len; - -cp_convert: - len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); - if (len <= 0) { - *target = '?'; - len = 1; - } - - goto out; -} - -/* - * is_char_allowed() - check for valid character - * @ch: input character to be checked - * - * Return: 1 if char is allowed, otherwise 0 - */ -static inline int is_char_allowed(char *ch) -{ - /* check for control chars, wildcards etc. */ - if (!(*ch & 0x80) && - (*ch <= 0x1f || - *ch == '?' || *ch == '"' || *ch == '<' || - *ch == '>' || *ch == '|')) - return 0; - - return 1; -} - -/* - * smb_from_utf16() - convert utf16le string to local charset - * @to: destination buffer - * @from: source buffer - * @tolen: destination buffer size (in bytes) - * @fromlen: source buffer size (in bytes) - * @codepage: codepage to which characters should be converted - * @mapchar: should characters be remapped according to the mapchars option? - * - * Convert a little-endian utf16le string (as sent by the server) to a string - * in the provided codepage. The tolen and fromlen parameters are to ensure - * that the code doesn't walk off of the end of the buffer (which is always - * a danger if the alignment of the source buffer is off). The destination - * string is always properly null terminated and fits in the destination - * buffer. Returns the length of the destination string in bytes (including - * null terminator). - * - * Note that some windows versions actually send multiword UTF-16 characters - * instead of straight UTF16-2. The linux nls routines however aren't able to - * deal with those characters properly. In the event that we get some of - * those characters, they won't be translated properly. - * - * Return: string length after conversion - */ -static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *codepage, bool mapchar) -{ - int i, charlen, safelen; - int outlen = 0; - int nullsize = nls_nullsize(codepage); - int fromwords = fromlen / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp; - - /* - * because the chars can be of varying widths, we need to take care - * not to overflow the destination buffer when we get close to the - * end of it. Until we get to this offset, we don't need to check - * for overflow however. - */ - safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); - - for (i = 0; i < fromwords; i++) { - ftmp = get_unaligned_le16(&from[i]); - if (ftmp == 0) - break; - - /* - * check to see if converting this character might make the - * conversion bleed into the null terminator - */ - if (outlen >= safelen) { - charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); - if ((outlen + charlen) > (tolen - nullsize)) - break; - } - - /* put converted char into 'to' buffer */ - charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); - outlen += charlen; - } - - /* properly null-terminate string */ - for (i = 0; i < nullsize; i++) - to[outlen++] = 0; - - return outlen; -} - -/* - * smb_strtoUTF16() - Convert character string to unicode string - * @to: destination buffer - * @from: source buffer - * @len: destination buffer size (in bytes) - * @codepage: codepage to which characters should be converted - * - * Return: string length after conversion - */ -int smb_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage) -{ - int charlen; - int i; - wchar_t wchar_to; /* needed to quiet sparse */ - - /* special case for utf8 to handle no plane0 chars */ - if (!strcmp(codepage->charset, "utf8")) { - /* - * convert utf8 -> utf16, we assume we have enough space - * as caller should have assumed conversion does not overflow - * in destination len is length in wchar_t units (16bits) - */ - i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, - (wchar_t *)to, len); - - /* if success terminate and exit */ - if (i >= 0) - goto success; - /* - * if fails fall back to UCS encoding as this - * function should not return negative values - * currently can fail only if source contains - * invalid encoded characters - */ - } - - for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) { - charlen = codepage->char2uni(from, len, &wchar_to); - if (charlen < 1) { - /* A question mark */ - wchar_to = 0x003f; - charlen = 1; - } - put_unaligned_le16(wchar_to, &to[i]); - } - -success: - put_unaligned_le16(0, &to[i]); - return i; -} - -/* - * smb_strndup_from_utf16() - copy a string from wire format to the local - * codepage - * @src: source string - * @maxlen: don't walk past this many bytes in the source string - * @is_unicode: is this a unicode string? - * @codepage: destination codepage - * - * Take a string given by the server, convert it to the local codepage and - * put it in a new buffer. Returns a pointer to the new string or NULL on - * error. - * - * Return: destination string buffer or error ptr - */ -char *smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, - const struct nls_table *codepage) -{ - int len, ret; - char *dst; - - if (is_unicode) { - len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); - len += nls_nullsize(codepage); - dst = kmalloc(len, GFP_KERNEL); - if (!dst) - return ERR_PTR(-ENOMEM); - ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, - false); - if (ret < 0) { - kfree(dst); - return ERR_PTR(-EINVAL); - } - } else { - len = strnlen(src, maxlen); - len++; - dst = kmalloc(len, GFP_KERNEL); - if (!dst) - return ERR_PTR(-ENOMEM); - strscpy(dst, src, len); - } - - return dst; -} - -/* - * Convert 16 bit Unicode pathname to wire format from string in current code - * page. Conversion may involve remapping up the six characters that are - * only legal in POSIX-like OS (if they are present in the string). Path - * names are little endian 16 bit Unicode on the wire - */ -/* - * smbConvertToUTF16() - convert string from local charset to utf16 - * @target: destination buffer - * @source: source buffer - * @srclen: source buffer size (in bytes) - * @cp: codepage to which characters should be converted - * @mapchar: should characters be remapped according to the mapchars option? - * - * Convert 16 bit Unicode pathname to wire format from string in current code - * page. Conversion may involve remapping up the six characters that are - * only legal in POSIX-like OS (if they are present in the string). Path - * names are little endian 16 bit Unicode on the wire - * - * Return: char length after conversion - */ -int smbConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int mapchars) -{ - int i, j, charlen; - char src_char; - __le16 dst_char; - wchar_t tmp; - - if (!mapchars) - return smb_strtoUTF16(target, source, srclen, cp); - - for (i = 0, j = 0; i < srclen; j++) { - src_char = source[i]; - charlen = 1; - switch (src_char) { - case 0: - put_unaligned(0, &target[j]); - return j; - case ':': - dst_char = cpu_to_le16(UNI_COLON); - break; - case '*': - dst_char = cpu_to_le16(UNI_ASTERISK); - break; - case '?': - dst_char = cpu_to_le16(UNI_QUESTION); - break; - case '<': - dst_char = cpu_to_le16(UNI_LESSTHAN); - break; - case '>': - dst_char = cpu_to_le16(UNI_GRTRTHAN); - break; - case '|': - dst_char = cpu_to_le16(UNI_PIPE); - break; - /* - * FIXME: We can not handle remapping backslash (UNI_SLASH) - * until all the calls to build_path_from_dentry are modified, - * as they use backslash as separator. - */ - default: - charlen = cp->char2uni(source + i, srclen - i, &tmp); - dst_char = cpu_to_le16(tmp); - - /* - * if no match, use question mark, which at least in - * some cases serves as wild card - */ - if (charlen < 1) { - dst_char = cpu_to_le16(0x003f); - charlen = 1; - } - } - /* - * character may take more than one byte in the source string, - * but will take exactly two bytes in the target string - */ - i += charlen; - put_unaligned(dst_char, &target[j]); - } - - return j; -} diff --git a/fs/ksmbd/unicode.h b/fs/ksmbd/unicode.h deleted file mode 100644 index 076f6034a789..000000000000 --- a/fs/ksmbd/unicode.h +++ /dev/null @@ -1,358 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Some of the source code in this file came from fs/cifs/cifs_unicode.c - * cifs_unicode: Unicode kernel case support - * - * Function: - * Convert a unicode character to upper or lower case using - * compressed tables. - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * - * - * Notes: - * These APIs are based on the C library functions. The semantics - * should match the C functions but with expanded size operands. - * - * The upper/lower functions are based on a table created by mkupr. - * This is a compressed table of upper and lower case conversion. - * - */ -#ifndef _CIFS_UNICODE_H -#define _CIFS_UNICODE_H - -#include -#include -#include -#include - -#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ - -/* - * Windows maps these to the user defined 16 bit Unicode range since they are - * reserved symbols (along with \ and /), otherwise illegal to store - * in filenames in NTFS - */ -#define UNI_ASTERISK ((__u16)('*' + 0xF000)) -#define UNI_QUESTION ((__u16)('?' + 0xF000)) -#define UNI_COLON ((__u16)(':' + 0xF000)) -#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) -#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) -#define UNI_PIPE ((__u16)('|' + 0xF000)) -#define UNI_SLASH ((__u16)('\\' + 0xF000)) - -/* Just define what we want from uniupr.h. We don't want to define the tables - * in each source file. - */ -#ifndef UNICASERANGE_DEFINED -struct UniCaseRange { - wchar_t start; - wchar_t end; - signed char *table; -}; -#endif /* UNICASERANGE_DEFINED */ - -#ifndef UNIUPR_NOUPPER -extern signed char SmbUniUpperTable[512]; -extern const struct UniCaseRange SmbUniUpperRange[]; -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -extern signed char CifsUniLowerTable[512]; -extern const struct UniCaseRange CifsUniLowerRange[]; -#endif /* UNIUPR_NOLOWER */ - -#ifdef __KERNEL__ -int smb_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage); -char *smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, - const struct nls_table *codepage); -int smbConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int mapchars); -char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); -#endif - -/* - * UniStrcat: Concatenate the second string to the first - * - * Returns: - * Address of the first string - */ -static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ - - while (*ucs1++) - /*NULL*/; /* To end of first string */ - ucs1--; /* Return to the null */ - while ((*ucs1++ = *ucs2++)) - /*NULL*/; /* copy string 2 over */ - return anchor; -} - -/* - * UniStrchr: Find a character in a string - * - * Returns: - * Address of first occurrence of character in string - * or NULL if the character is not in the string - */ -static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) -{ - while ((*ucs != uc) && *ucs) - ucs++; - - if (*ucs == uc) - return (wchar_t *)ucs; - return NULL; -} - -/* - * UniStrcmp: Compare two strings - * - * Returns: - * < 0: First string is less than second - * = 0: Strings are equal - * > 0: First string is greater than second - */ -static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) -{ - while ((*ucs1 == *ucs2) && *ucs1) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)*ucs2; -} - -/* - * UniStrcpy: Copy a string - */ -static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save the start of result string */ - - while ((*ucs1++ = *ucs2++)) - /*NULL*/; - return anchor; -} - -/* - * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) - */ -static inline size_t UniStrlen(const wchar_t *ucs1) -{ - int i = 0; - - while (*ucs1++) - i++; - return i; -} - -/* - * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a - * string (length limited) - */ -static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) -{ - int i = 0; - - while (*ucs1++) { - i++; - if (i >= maxlen) - break; - } - return i; -} - -/* - * UniStrncat: Concatenate length limited string - */ -static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; /* save pointer to string 1 */ - - while (*ucs1++) - /*NULL*/; - ucs1--; /* point to null terminator of s1 */ - while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ - ucs1++; - ucs2++; - } - *ucs1 = 0; /* Null terminate the result */ - return anchor; -} - -/* - * UniStrncmp: Compare length limited string - */ -static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == *ucs2) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)*ucs2; -} - -/* - * UniStrncmp_le: Compare length limited string - native to little-endian - */ -static inline int -UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); -} - -/* - * UniStrncpy: Copy length limited string with pad - */ -static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = *ucs2++; - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrncpy_le: Copy length limited string with pad to little-endian - */ -static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = __le16_to_cpu(*ucs2++); - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrstr: Find a string in a string - * - * Returns: - * Address of first match found - * NULL if no matching string is found - */ -static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) -{ - const wchar_t *anchor1 = ucs1; - const wchar_t *anchor2 = ucs2; - - while (*ucs1) { - if (*ucs1 == *ucs2) { - /* Partial match found */ - ucs1++; - ucs2++; - } else { - if (!*ucs2) /* Match found */ - return (wchar_t *)anchor1; - ucs1 = ++anchor1; /* No match */ - ucs2 = anchor2; - } - } - - if (!*ucs2) /* Both end together */ - return (wchar_t *)anchor1; /* Match found */ - return NULL; /* No match */ -} - -#ifndef UNIUPR_NOUPPER -/* - * UniToupper: Convert a unicode character to upper case - */ -static inline wchar_t UniToupper(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(SmbUniUpperTable)) { - /* Latin characters */ - return uc + SmbUniUpperTable[uc]; /* Use base tables */ - } - - rp = SmbUniUpperRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - return uc; /* Past last range */ -} - -/* - * UniStrupr: Upper case a unicode string - */ -static inline __le16 *UniStrupr(register __le16 *upin) -{ - register __le16 *up; - - up = upin; - while (*up) { /* For all characters */ - *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); - up++; - } - return upin; /* Return input pointer */ -} -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -/* - * UniTolower: Convert a unicode character to lower case - */ -static inline wchar_t UniTolower(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(CifsUniLowerTable)) { - /* Latin characters */ - return uc + CifsUniLowerTable[uc]; /* Use base tables */ - } - - rp = CifsUniLowerRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - return uc; /* Past last range */ -} - -/* - * UniStrlwr: Lower case a unicode string - */ -static inline wchar_t *UniStrlwr(register wchar_t *upin) -{ - register wchar_t *up; - - up = upin; - while (*up) { /* For all characters */ - *up = UniTolower(*up); - up++; - } - return upin; /* Return input pointer */ -} - -#endif - -#endif /* _CIFS_UNICODE_H */ diff --git a/fs/ksmbd/uniupr.h b/fs/ksmbd/uniupr.h deleted file mode 100644 index 26583b776897..000000000000 --- a/fs/ksmbd/uniupr.h +++ /dev/null @@ -1,268 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Some of the source code in this file came from fs/cifs/uniupr.h - * Copyright (c) International Business Machines Corp., 2000,2002 - * - * uniupr.h - Unicode compressed case ranges - * - */ -#ifndef __KSMBD_UNIUPR_H -#define __KSMBD_UNIUPR_H - -#ifndef UNIUPR_NOUPPER -/* - * Latin upper case - */ -signed char SmbUniUpperTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, /* 060-06f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ - -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, - -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ - 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ - 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ - 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ - -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ - 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ -}; - -/* Upper case range - Greek */ -static signed char UniCaseRangeU03a0[47] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 3b0-3bf */ - -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, - -63, -63, -}; - -/* Upper case range - Cyrillic */ -static signed char UniCaseRangeU0430[48] = { - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 430-43f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 440-44f */ - 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, 0, -80, -80, /* 450-45f */ -}; - -/* Upper case range - Extended cyrillic */ -static signed char UniCaseRangeU0490[61] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ - 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -}; - -/* Upper case range - Extended latin and greek */ -static signed char UniCaseRangeU1e00[509] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ - 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ - 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ - 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, - 126, 126, 0, 0, /* 1f70-1f7f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ - 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ - 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ - 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Upper case range - Wide latin */ -static signed char UniCaseRangeUff40[27] = { - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, /* ff40-ff4f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -}; - -/* - * Upper Case Range - */ -const struct UniCaseRange SmbUniUpperRange[] = { - {0x03a0, 0x03ce, UniCaseRangeU03a0}, - {0x0430, 0x045f, UniCaseRangeU0430}, - {0x0490, 0x04cc, UniCaseRangeU0490}, - {0x1e00, 0x1ffc, UniCaseRangeU1e00}, - {0xff40, 0xff5a, UniCaseRangeUff40}, - {0} -}; -#endif - -#ifndef UNIUPR_NOLOWER -/* - * Latin lower case - */ -signed char CifsUniLowerTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 040-04f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, - 0, 0, 0, /* 050-05f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, /* 0c0-0cf */ - 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 0, /* 0d0-0df */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ - 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ - 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, - 0, /* 170-17f */ - 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, - 0, /* 180-18f */ - 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ - 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ - 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ -}; - -/* Lower case range - Greek */ -static signed char UniCaseRangeL0380[44] = { - 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 390-39f */ - 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* Lower case range - Cyrillic */ -static signed char UniCaseRangeL0400[48] = { - 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 0, 80, 80, /* 400-40f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 410-41f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 420-42f */ -}; - -/* Lower case range - Extended cyrillic */ -static signed char UniCaseRangeL0490[60] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ - 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, -}; - -/* Lower case range - Extended latin and greek */ -static signed char UniCaseRangeL1e00[504] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ - 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, - 0, 0, /* 1fc0-1fcf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, - 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Lower case range - Wide latin */ -static signed char UniCaseRangeLff20[27] = { - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, /* ff20-ff2f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* - * Lower Case Range - */ -const struct UniCaseRange CifsUniLowerRange[] = { - {0x0380, 0x03ab, UniCaseRangeL0380}, - {0x0400, 0x042f, UniCaseRangeL0400}, - {0x0490, 0x04cb, UniCaseRangeL0490}, - {0x1e00, 0x1ff7, UniCaseRangeL1e00}, - {0xff20, 0xff3a, UniCaseRangeLff20}, - {0} -}; -#endif - -#endif /* __KSMBD_UNIUPR_H */ diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c deleted file mode 100644 index 94b8ed4ef870..000000000000 --- a/fs/ksmbd/vfs.c +++ /dev/null @@ -1,1911 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../internal.h" /* for vfs_path_lookup */ - -#include "glob.h" -#include "oplock.h" -#include "connection.h" -#include "vfs.h" -#include "vfs_cache.h" -#include "smbacl.h" -#include "ndr.h" -#include "auth.h" -#include "misc.h" - -#include "smb_common.h" -#include "mgmt/share_config.h" -#include "mgmt/tree_connect.h" -#include "mgmt/user_session.h" -#include "mgmt/user_config.h" - -static char *extract_last_component(char *path) -{ - char *p = strrchr(path, '/'); - - if (p && p[1] != '\0') { - *p = '\0'; - p++; - } else { - p = NULL; - } - return p; -} - -static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, - struct inode *parent_inode, - struct inode *inode) -{ - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_INHERIT_OWNER)) - return; - - i_uid_write(inode, i_uid_read(parent_inode)); -} - -/** - * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable - * - * the parent dentry got by dget_parent or @parent could be - * unstable, we try to lock a parent inode and lookup the - * child dentry again. - * - * the reference count of @parent isn't incremented. - */ -int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, - struct dentry *child) -{ - struct dentry *dentry; - int ret = 0; - - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - dentry = lookup_one(user_ns, child->d_name.name, parent, - child->d_name.len); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out_err; - } - - if (dentry != child) { - ret = -ESTALE; - dput(dentry); - goto out_err; - } - - dput(dentry); - return 0; -out_err: - inode_unlock(d_inode(parent)); - return ret; -} - -int ksmbd_vfs_may_delete(struct user_namespace *user_ns, - struct dentry *dentry) -{ - struct dentry *parent; - int ret; - - parent = dget_parent(dentry); - ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); - if (ret) { - dput(parent); - return ret; - } - - ret = inode_permission(user_ns, d_inode(parent), - MAY_EXEC | MAY_WRITE); - - inode_unlock(d_inode(parent)); - dput(parent); - return ret; -} - -int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, - struct dentry *dentry, __le32 *daccess) -{ - struct dentry *parent; - int ret = 0; - - *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); - - if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) - *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | - FILE_WRITE_DATA | FILE_APPEND_DATA | - FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | - FILE_DELETE_CHILD); - - if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) - *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; - - if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) - *daccess |= FILE_EXECUTE_LE; - - parent = dget_parent(dentry); - ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); - if (ret) { - dput(parent); - return ret; - } - - if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) - *daccess |= FILE_DELETE_LE; - - inode_unlock(d_inode(parent)); - dput(parent); - return ret; -} - -/** - * ksmbd_vfs_create() - vfs helper for smb create file - * @work: work - * @name: file name that is relative to share - * @mode: file create mode - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) -{ - struct path path; - struct dentry *dentry; - int err; - - dentry = ksmbd_vfs_kern_path_create(work, name, - LOOKUP_NO_SYMLINKS, &path); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - if (err != -ENOENT) - pr_err("path create failed for %s, err %d\n", - name, err); - return err; - } - - mode |= S_IFREG; - err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), - dentry, mode, true); - if (!err) { - ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), - d_inode(dentry)); - } else { - pr_err("File(%s): creation failed (err:%d)\n", name, err); - } - done_path_create(&path, dentry); - return err; -} - -/** - * ksmbd_vfs_mkdir() - vfs helper for smb create directory - * @work: work - * @name: directory name that is relative to share - * @mode: directory create mode - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) -{ - struct user_namespace *user_ns; - struct path path; - struct dentry *dentry; - int err; - - dentry = ksmbd_vfs_kern_path_create(work, name, - LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, - &path); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - if (err != -EEXIST) - ksmbd_debug(VFS, "path create failed for %s, err %d\n", - name, err); - return err; - } - - user_ns = mnt_user_ns(path.mnt); - mode |= S_IFDIR; - err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); - if (err) { - goto out; - } else if (d_unhashed(dentry)) { - struct dentry *d; - - d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, - dentry->d_name.len); - if (IS_ERR(d)) { - err = PTR_ERR(d); - goto out; - } - if (unlikely(d_is_negative(d))) { - dput(d); - err = -ENOENT; - goto out; - } - - ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); - dput(d); - } -out: - done_path_create(&path, dentry); - if (err) - pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); - return err; -} - -static ssize_t ksmbd_vfs_getcasexattr(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name, - int attr_name_len, char **attr_value) -{ - char *name, *xattr_list = NULL; - ssize_t value_len = -ENOENT, xattr_list_len; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len <= 0) - goto out; - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); - if (strncasecmp(attr_name, name, attr_name_len)) - continue; - - value_len = ksmbd_vfs_getxattr(user_ns, - dentry, - name, - attr_value); - if (value_len < 0) - pr_err("failed to get xattr in file\n"); - break; - } - -out: - kvfree(xattr_list); - return value_len; -} - -static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) -{ - ssize_t v_len; - char *stream_buf = NULL; - - ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", - *pos, count); - - v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), - fp->filp->f_path.dentry, - fp->stream.name, - fp->stream.size, - &stream_buf); - if ((int)v_len <= 0) - return (int)v_len; - - if (v_len <= *pos) { - count = -EINVAL; - goto free_buf; - } - - if (v_len - *pos < count) - count = v_len - *pos; - - memcpy(buf, &stream_buf[*pos], count); - -free_buf: - kvfree(stream_buf); - return count; -} - -/** - * check_lock_range() - vfs helper for smb byte range file locking - * @filp: the file to apply the lock to - * @start: lock start byte offset - * @end: lock end byte offset - * @type: byte range type read/write - * - * Return: 0 on success, otherwise error - */ -static int check_lock_range(struct file *filp, loff_t start, loff_t end, - unsigned char type) -{ - struct file_lock *flock; - struct file_lock_context *ctx = file_inode(filp)->i_flctx; - int error = 0; - - if (!ctx || list_empty_careful(&ctx->flc_posix)) - return 0; - - spin_lock(&ctx->flc_lock); - list_for_each_entry(flock, &ctx->flc_posix, fl_list) { - /* check conflict locks */ - if (flock->fl_end >= start && end >= flock->fl_start) { - if (flock->fl_type == F_RDLCK) { - if (type == WRITE) { - pr_err("not allow write by shared lock\n"); - error = 1; - goto out; - } - } else if (flock->fl_type == F_WRLCK) { - /* check owner in lock */ - if (flock->fl_file != filp) { - error = 1; - pr_err("not allow rw access by exclusive lock from other opens\n"); - goto out; - } - } - } - } -out: - spin_unlock(&ctx->flc_lock); - return error; -} - -/** - * ksmbd_vfs_read() - vfs helper for smb file read - * @work: smb work - * @fid: file id of open file - * @count: read byte count - * @pos: file pos - * - * Return: number of read bytes on success, otherwise error - */ -int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, - loff_t *pos) -{ - struct file *filp = fp->filp; - ssize_t nbytes = 0; - char *rbuf = work->aux_payload_buf; - struct inode *inode = file_inode(filp); - - if (S_ISDIR(inode->i_mode)) - return -EISDIR; - - if (unlikely(count == 0)) - return 0; - - if (work->conn->connection_type) { - if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - pr_err("no right to read(%pD)\n", fp->filp); - return -EACCES; - } - } - - if (ksmbd_stream_fd(fp)) - return ksmbd_vfs_stream_read(fp, rbuf, pos, count); - - if (!work->tcon->posix_extensions) { - int ret; - - ret = check_lock_range(filp, *pos, *pos + count - 1, READ); - if (ret) { - pr_err("unable to read due to lock\n"); - return -EAGAIN; - } - } - - nbytes = kernel_read(filp, rbuf, count, pos); - if (nbytes < 0) { - pr_err("smb read failed, err = %zd\n", nbytes); - return nbytes; - } - - filp->f_pos = *pos; - return nbytes; -} - -static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) -{ - char *stream_buf = NULL, *wbuf; - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - size_t size, v_len; - int err = 0; - - ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", - *pos, count); - - size = *pos + count; - if (size > XATTR_SIZE_MAX) { - size = XATTR_SIZE_MAX; - count = (*pos + count) - XATTR_SIZE_MAX; - } - - v_len = ksmbd_vfs_getcasexattr(user_ns, - fp->filp->f_path.dentry, - fp->stream.name, - fp->stream.size, - &stream_buf); - if ((int)v_len < 0) { - pr_err("not found stream in xattr : %zd\n", v_len); - err = (int)v_len; - goto out; - } - - if (v_len < size) { - wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); - if (!wbuf) { - err = -ENOMEM; - goto out; - } - - if (v_len > 0) - memcpy(wbuf, stream_buf, v_len); - kvfree(stream_buf); - stream_buf = wbuf; - } - - memcpy(&stream_buf[*pos], buf, count); - - err = ksmbd_vfs_setxattr(user_ns, - fp->filp->f_path.dentry, - fp->stream.name, - (void *)stream_buf, - size, - 0); - if (err < 0) - goto out; - - fp->filp->f_pos = *pos; - err = 0; -out: - kvfree(stream_buf); - return err; -} - -/** - * ksmbd_vfs_write() - vfs helper for smb file write - * @work: work - * @fid: file id of open file - * @buf: buf containing data for writing - * @count: read byte count - * @pos: file pos - * @sync: fsync after write - * @written: number of bytes written - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, - ssize_t *written) -{ - struct file *filp; - loff_t offset = *pos; - int err = 0; - - if (work->conn->connection_type) { - if (!(fp->daccess & FILE_WRITE_DATA_LE)) { - pr_err("no right to write(%pD)\n", fp->filp); - err = -EACCES; - goto out; - } - } - - filp = fp->filp; - - if (ksmbd_stream_fd(fp)) { - err = ksmbd_vfs_stream_write(fp, buf, pos, count); - if (!err) - *written = count; - goto out; - } - - if (!work->tcon->posix_extensions) { - err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); - if (err) { - pr_err("unable to write due to lock\n"); - err = -EAGAIN; - goto out; - } - } - - /* Do we need to break any of a levelII oplock? */ - smb_break_all_levII_oplock(work, fp, 1); - - err = kernel_write(filp, buf, count, pos); - if (err < 0) { - ksmbd_debug(VFS, "smb write failed, err = %d\n", err); - goto out; - } - - filp->f_pos = *pos; - *written = err; - err = 0; - if (sync) { - err = vfs_fsync_range(filp, offset, offset + *written, 0); - if (err < 0) - pr_err("fsync failed for filename = %pD, err = %d\n", - fp->filp, err); - } - -out: - return err; -} - -/** - * ksmbd_vfs_getattr() - vfs helper for smb getattr - * @work: work - * @fid: file id of open file - * @attrs: inode attributes - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) -{ - int err; - - err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); - if (err) - pr_err("getattr failed, err %d\n", err); - return err; -} - -/** - * ksmbd_vfs_fsync() - vfs helper for smb fsync - * @work: work - * @fid: file id of open file - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) -{ - struct ksmbd_file *fp; - int err; - - fp = ksmbd_lookup_fd_slow(work, fid, p_id); - if (!fp) { - pr_err("failed to get filp for fid %llu\n", fid); - return -ENOENT; - } - err = vfs_fsync(fp->filp, 0); - if (err < 0) - pr_err("smb fsync failed, err = %d\n", err); - ksmbd_fd_put(work, fp); - return err; -} - -/** - * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink - * @name: directory or file name that is relative to share - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) -{ - struct user_namespace *user_ns; - struct path path; - struct dentry *parent; - int err; - - if (ksmbd_override_fsids(work)) - return -ENOMEM; - - err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); - if (err) { - ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); - ksmbd_revert_fsids(work); - return err; - } - - user_ns = mnt_user_ns(path.mnt); - parent = dget_parent(path.dentry); - err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); - if (err) { - dput(parent); - path_put(&path); - ksmbd_revert_fsids(work); - return err; - } - - if (!d_inode(path.dentry)->i_nlink) { - err = -ENOENT; - goto out_err; - } - - if (S_ISDIR(d_inode(path.dentry)->i_mode)) { - err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); - if (err && err != -ENOTEMPTY) - ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, - err); - } else { - err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); - if (err) - ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, - err); - } - -out_err: - inode_unlock(d_inode(parent)); - dput(parent); - path_put(&path); - ksmbd_revert_fsids(work); - return err; -} - -/** - * ksmbd_vfs_link() - vfs helper for creating smb hardlink - * @oldname: source file name - * @newname: hardlink name that is relative to share - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, - const char *newname) -{ - struct path oldpath, newpath; - struct dentry *dentry; - int err; - - if (ksmbd_override_fsids(work)) - return -ENOMEM; - - err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); - if (err) { - pr_err("cannot get linux path for %s, err = %d\n", - oldname, err); - goto out1; - } - - dentry = ksmbd_vfs_kern_path_create(work, newname, - LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, - &newpath); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - pr_err("path create err for %s, err %d\n", newname, err); - goto out2; - } - - err = -EXDEV; - if (oldpath.mnt != newpath.mnt) { - pr_err("vfs_link failed err %d\n", err); - goto out3; - } - - err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), - d_inode(newpath.dentry), - dentry, NULL); - if (err) - ksmbd_debug(VFS, "vfs_link failed err %d\n", err); - -out3: - done_path_create(&newpath, dentry); -out2: - path_put(&oldpath); -out1: - ksmbd_revert_fsids(work); - return err; -} - -static int ksmbd_validate_entry_in_use(struct dentry *src_dent) -{ - struct dentry *dst_dent; - - spin_lock(&src_dent->d_lock); - list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { - struct ksmbd_file *child_fp; - - if (d_really_is_negative(dst_dent)) - continue; - - child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); - if (child_fp) { - spin_unlock(&src_dent->d_lock); - ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); - return -EACCES; - } - } - spin_unlock(&src_dent->d_lock); - - return 0; -} - -static int __ksmbd_vfs_rename(struct ksmbd_work *work, - struct user_namespace *src_user_ns, - struct dentry *src_dent_parent, - struct dentry *src_dent, - struct user_namespace *dst_user_ns, - struct dentry *dst_dent_parent, - struct dentry *trap_dent, - char *dst_name) -{ - struct dentry *dst_dent; - int err; - - if (!work->tcon->posix_extensions) { - err = ksmbd_validate_entry_in_use(src_dent); - if (err) - return err; - } - - if (d_really_is_negative(src_dent_parent)) - return -ENOENT; - if (d_really_is_negative(dst_dent_parent)) - return -ENOENT; - if (d_really_is_negative(src_dent)) - return -ENOENT; - if (src_dent == trap_dent) - return -EINVAL; - - if (ksmbd_override_fsids(work)) - return -ENOMEM; - - dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, - strlen(dst_name)); - err = PTR_ERR(dst_dent); - if (IS_ERR(dst_dent)) { - pr_err("lookup failed %s [%d]\n", dst_name, err); - goto out; - } - - err = -ENOTEMPTY; - if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { - struct renamedata rd = { - .old_mnt_userns = src_user_ns, - .old_dir = d_inode(src_dent_parent), - .old_dentry = src_dent, - .new_mnt_userns = dst_user_ns, - .new_dir = d_inode(dst_dent_parent), - .new_dentry = dst_dent, - }; - err = vfs_rename(&rd); - } - if (err) - pr_err("vfs_rename failed err %d\n", err); - if (dst_dent) - dput(dst_dent); -out: - ksmbd_revert_fsids(work); - return err; -} - -int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - char *newname) -{ - struct user_namespace *user_ns; - struct path dst_path; - struct dentry *src_dent_parent, *dst_dent_parent; - struct dentry *src_dent, *trap_dent, *src_child; - char *dst_name; - int err; - - dst_name = extract_last_component(newname); - if (!dst_name) { - dst_name = newname; - newname = ""; - } - - src_dent_parent = dget_parent(fp->filp->f_path.dentry); - src_dent = fp->filp->f_path.dentry; - - err = ksmbd_vfs_kern_path(work, newname, - LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, - &dst_path, false); - if (err) { - ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); - goto out; - } - dst_dent_parent = dst_path.dentry; - - trap_dent = lock_rename(src_dent_parent, dst_dent_parent); - dget(src_dent); - dget(dst_dent_parent); - user_ns = file_mnt_user_ns(fp->filp); - src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, - src_dent->d_name.len); - if (IS_ERR(src_child)) { - err = PTR_ERR(src_child); - goto out_lock; - } - - if (src_child != src_dent) { - err = -ESTALE; - dput(src_child); - goto out_lock; - } - dput(src_child); - - err = __ksmbd_vfs_rename(work, - user_ns, - src_dent_parent, - src_dent, - mnt_user_ns(dst_path.mnt), - dst_dent_parent, - trap_dent, - dst_name); -out_lock: - dput(src_dent); - dput(dst_dent_parent); - unlock_rename(src_dent_parent, dst_dent_parent); - path_put(&dst_path); -out: - dput(src_dent_parent); - return err; -} - -/** - * ksmbd_vfs_truncate() - vfs helper for smb file truncate - * @work: work - * @fid: file id of old file - * @size: truncate to given size - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_truncate(struct ksmbd_work *work, - struct ksmbd_file *fp, loff_t size) -{ - int err = 0; - struct file *filp; - - filp = fp->filp; - - /* Do we need to break any of a levelII oplock? */ - smb_break_all_levII_oplock(work, fp, 1); - - if (!work->tcon->posix_extensions) { - struct inode *inode = file_inode(filp); - - if (size < inode->i_size) { - err = check_lock_range(filp, size, - inode->i_size - 1, WRITE); - } else { - err = check_lock_range(filp, inode->i_size, - size - 1, WRITE); - } - - if (err) { - pr_err("failed due to lock\n"); - return -EAGAIN; - } - } - - err = vfs_truncate(&filp->f_path, size); - if (err) - pr_err("truncate failed, err %d\n", err); - return err; -} - -/** - * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes - * @dentry: dentry of file for listing xattrs - * @list: destination buffer - * @size: destination buffer length - * - * Return: xattr list length on success, otherwise error - */ -ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) -{ - ssize_t size; - char *vlist = NULL; - - size = vfs_listxattr(dentry, NULL, 0); - if (size <= 0) - return size; - - vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); - if (!vlist) - return -ENOMEM; - - *list = vlist; - size = vfs_listxattr(dentry, vlist, size); - if (size < 0) { - ksmbd_debug(VFS, "listxattr failed\n"); - kvfree(vlist); - *list = NULL; - } - - return size; -} - -static ssize_t ksmbd_vfs_xattr_len(struct user_namespace *user_ns, - struct dentry *dentry, char *xattr_name) -{ - return vfs_getxattr(user_ns, dentry, xattr_name, NULL, 0); -} - -/** - * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value - * @user_ns: user namespace - * @dentry: dentry of file for getting xattrs - * @xattr_name: name of xattr name to query - * @xattr_buf: destination buffer xattr value - * - * Return: read xattr value length on success, otherwise error - */ -ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, - struct dentry *dentry, - char *xattr_name, char **xattr_buf) -{ - ssize_t xattr_len; - char *buf; - - *xattr_buf = NULL; - xattr_len = ksmbd_vfs_xattr_len(user_ns, dentry, xattr_name); - if (xattr_len < 0) - return xattr_len; - - buf = kmalloc(xattr_len + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - xattr_len = vfs_getxattr(user_ns, dentry, xattr_name, - (void *)buf, xattr_len); - if (xattr_len > 0) - *xattr_buf = buf; - else - kfree(buf); - return xattr_len; -} - -/** - * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value - * @user_ns: user namespace - * @dentry: dentry to set XATTR at - * @name: xattr name for setxattr - * @value: xattr value to set - * @size: size of xattr value - * @flags: destination buffer length - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_setxattr(struct user_namespace *user_ns, - struct dentry *dentry, const char *attr_name, - void *attr_value, size_t attr_size, int flags) -{ - int err; - - err = vfs_setxattr(user_ns, - dentry, - attr_name, - attr_value, - attr_size, - flags); - if (err) - ksmbd_debug(VFS, "setxattr failed, err %d\n", err); - return err; -} - -/** - * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options - * @filp: file pointer for IO - * @options: smb IO options - */ -void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) -{ - struct address_space *mapping; - - mapping = filp->f_mapping; - - if (!option || !mapping) - return; - - if (option & FILE_WRITE_THROUGH_LE) { - filp->f_flags |= O_SYNC; - } else if (option & FILE_SEQUENTIAL_ONLY_LE) { - filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; - spin_lock(&filp->f_lock); - filp->f_mode &= ~FMODE_RANDOM; - spin_unlock(&filp->f_lock); - } else if (option & FILE_RANDOM_ACCESS_LE) { - spin_lock(&filp->f_lock); - filp->f_mode |= FMODE_RANDOM; - spin_unlock(&filp->f_lock); - } -} - -int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t off, loff_t len) -{ - smb_break_all_levII_oplock(work, fp, 1); - if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) - return vfs_fallocate(fp->filp, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - off, len); - - return vfs_fallocate(fp->filp, - FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, - off, len); -} - -int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, - struct file_allocated_range_buffer *ranges, - unsigned int in_count, unsigned int *out_count) -{ - struct file *f = fp->filp; - struct inode *inode = file_inode(fp->filp); - loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; - loff_t extent_start, extent_end; - int ret = 0; - - if (start > maxbytes) - return -EFBIG; - - if (!in_count) - return 0; - - /* - * Shrink request scope to what the fs can actually handle. - */ - if (length > maxbytes || (maxbytes - length) < start) - length = maxbytes - start; - - if (start + length > inode->i_size) - length = inode->i_size - start; - - *out_count = 0; - end = start + length; - while (start < end && *out_count < in_count) { - extent_start = vfs_llseek(f, start, SEEK_DATA); - if (extent_start < 0) { - if (extent_start != -ENXIO) - ret = (int)extent_start; - break; - } - - if (extent_start >= end) - break; - - extent_end = vfs_llseek(f, extent_start, SEEK_HOLE); - if (extent_end < 0) { - if (extent_end != -ENXIO) - ret = (int)extent_end; - break; - } else if (extent_start >= extent_end) { - break; - } - - ranges[*out_count].file_offset = cpu_to_le64(extent_start); - ranges[(*out_count)++].length = - cpu_to_le64(min(extent_end, end) - extent_start); - - start = extent_end; - } - - return ret; -} - -int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name) -{ - return vfs_removexattr(user_ns, dentry, attr_name); -} - -int ksmbd_vfs_unlink(struct user_namespace *user_ns, - struct dentry *dir, struct dentry *dentry) -{ - int err = 0; - - err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); - if (err) - return err; - dget(dentry); - - if (S_ISDIR(d_inode(dentry)->i_mode)) - err = vfs_rmdir(user_ns, d_inode(dir), dentry); - else - err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL); - - dput(dentry); - inode_unlock(d_inode(dir)); - if (err) - ksmbd_debug(VFS, "failed to delete, err %d\n", err); - - return err; -} - -static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, - loff_t offset, u64 ino, unsigned int d_type) -{ - struct ksmbd_readdir_data *buf; - - buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - buf->dirent_count++; - - return buf->dirent_count <= 2; -} - -/** - * ksmbd_vfs_empty_dir() - check for empty directory - * @fp: ksmbd file pointer - * - * Return: true if directory empty, otherwise false - */ -int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) -{ - int err; - struct ksmbd_readdir_data readdir_data; - - memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); - - set_ctx_actor(&readdir_data.ctx, __dir_empty); - readdir_data.dirent_count = 0; - - err = iterate_dir(fp->filp, &readdir_data.ctx); - if (readdir_data.dirent_count > 2) - err = -ENOTEMPTY; - else - err = 0; - return err; -} - -static bool __caseless_lookup(struct dir_context *ctx, const char *name, - int namlen, loff_t offset, u64 ino, - unsigned int d_type) -{ - struct ksmbd_readdir_data *buf; - int cmp = -EINVAL; - - buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - - if (buf->used != namlen) - return true; - if (IS_ENABLED(CONFIG_UNICODE) && buf->um) { - const struct qstr q_buf = {.name = buf->private, - .len = buf->used}; - const struct qstr q_name = {.name = name, - .len = namlen}; - - cmp = utf8_strncasecmp(buf->um, &q_buf, &q_name); - } - if (cmp < 0) - cmp = strncasecmp((char *)buf->private, name, namlen); - if (!cmp) { - memcpy((char *)buf->private, name, namlen); - buf->dirent_count = 1; - return false; - } - return true; -} - -/** - * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory - * @dir: path info - * @name: filename to lookup - * @namelen: filename length - * - * Return: 0 on success, otherwise error - */ -static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, - size_t namelen, struct unicode_map *um) -{ - int ret; - struct file *dfilp; - int flags = O_RDONLY | O_LARGEFILE; - struct ksmbd_readdir_data readdir_data = { - .ctx.actor = __caseless_lookup, - .private = name, - .used = namelen, - .dirent_count = 0, - .um = um, - }; - - dfilp = dentry_open(dir, flags, current_cred()); - if (IS_ERR(dfilp)) - return PTR_ERR(dfilp); - - ret = iterate_dir(dfilp, &readdir_data.ctx); - if (readdir_data.dirent_count > 0) - ret = 0; - fput(dfilp); - return ret; -} - -/** - * ksmbd_vfs_kern_path() - lookup a file and get path info - * @name: file path that is relative to share - * @flags: lookup flags - * @path: if lookup succeed, return path info - * @caseless: caseless filename lookup - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, - unsigned int flags, struct path *path, bool caseless) -{ - struct ksmbd_share_config *share_conf = work->tcon->share_conf; - int err; - - flags |= LOOKUP_BENEATH; - err = vfs_path_lookup(share_conf->vfs_path.dentry, - share_conf->vfs_path.mnt, - name, - flags, - path); - if (!err) - return 0; - - if (caseless) { - char *filepath; - struct path parent; - size_t path_len, remain_len; - - filepath = kstrdup(name, GFP_KERNEL); - if (!filepath) - return -ENOMEM; - - path_len = strlen(filepath); - remain_len = path_len; - - parent = share_conf->vfs_path; - path_get(&parent); - - while (d_can_lookup(parent.dentry)) { - char *filename = filepath + path_len - remain_len; - char *next = strchrnul(filename, '/'); - size_t filename_len = next - filename; - bool is_last = !next[0]; - - if (filename_len == 0) - break; - - err = ksmbd_vfs_lookup_in_dir(&parent, filename, - filename_len, - work->conn->um); - path_put(&parent); - if (err) - goto out; - - next[0] = '\0'; - - err = vfs_path_lookup(share_conf->vfs_path.dentry, - share_conf->vfs_path.mnt, - filepath, - flags, - &parent); - if (err) - goto out; - else if (is_last) { - *path = parent; - goto out; - } - - next[0] = '/'; - remain_len -= filename_len + 1; - } - - path_put(&parent); - err = -EINVAL; -out: - kfree(filepath); - } - return err; -} - -struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, - const char *name, - unsigned int flags, - struct path *path) -{ - char *abs_name; - struct dentry *dent; - - abs_name = convert_to_unix_name(work->tcon->share_conf, name); - if (!abs_name) - return ERR_PTR(-ENOMEM); - - dent = kern_path_create(AT_FDCWD, abs_name, path, flags); - kfree(abs_name); - return dent; -} - -int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, - struct dentry *dentry) -{ - char *name, *xattr_list = NULL; - ssize_t xattr_list_len; - int err = 0; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); - - if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, - sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || - !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, - sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { - err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); - if (err) - ksmbd_debug(SMB, - "remove acl xattr failed : %s\n", name); - } - } -out: - kvfree(xattr_list); - return err; -} - -int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, - struct dentry *dentry) -{ - char *name, *xattr_list = NULL; - ssize_t xattr_list_len; - int err = 0; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); - - if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { - err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); - if (err) - ksmbd_debug(SMB, "remove xattr failed : %s\n", name); - } - } -out: - kvfree(xattr_list); - return err; -} - -static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespace *user_ns, - struct inode *inode, - int acl_type) -{ - struct xattr_smb_acl *smb_acl = NULL; - struct posix_acl *posix_acls; - struct posix_acl_entry *pa_entry; - struct xattr_acl_entry *xa_entry; - int i; - - if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) - return NULL; - - posix_acls = get_acl(inode, acl_type); - if (!posix_acls) - return NULL; - - smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + - sizeof(struct xattr_acl_entry) * posix_acls->a_count, - GFP_KERNEL); - if (!smb_acl) - goto out; - - smb_acl->count = posix_acls->a_count; - pa_entry = posix_acls->a_entries; - xa_entry = smb_acl->entries; - for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { - switch (pa_entry->e_tag) { - case ACL_USER: - xa_entry->type = SMB_ACL_USER; - xa_entry->uid = posix_acl_uid_translate(user_ns, pa_entry); - break; - case ACL_USER_OBJ: - xa_entry->type = SMB_ACL_USER_OBJ; - break; - case ACL_GROUP: - xa_entry->type = SMB_ACL_GROUP; - xa_entry->gid = posix_acl_gid_translate(user_ns, pa_entry); - break; - case ACL_GROUP_OBJ: - xa_entry->type = SMB_ACL_GROUP_OBJ; - break; - case ACL_OTHER: - xa_entry->type = SMB_ACL_OTHER; - break; - case ACL_MASK: - xa_entry->type = SMB_ACL_MASK; - break; - default: - pr_err("unknown type : 0x%x\n", pa_entry->e_tag); - goto out; - } - - if (pa_entry->e_perm & ACL_READ) - xa_entry->perm |= SMB_ACL_READ; - if (pa_entry->e_perm & ACL_WRITE) - xa_entry->perm |= SMB_ACL_WRITE; - if (pa_entry->e_perm & ACL_EXECUTE) - xa_entry->perm |= SMB_ACL_EXECUTE; - } -out: - posix_acl_release(posix_acls); - return smb_acl; -} - -int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd *pntsd, int len) -{ - int rc; - struct ndr sd_ndr = {0}, acl_ndr = {0}; - struct xattr_ntacl acl = {0}; - struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; - struct inode *inode = d_inode(dentry); - - acl.version = 4; - acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; - acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); - - memcpy(acl.desc, "posix_acl", 9); - acl.desc_len = 10; - - pntsd->osidoffset = - cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); - pntsd->gsidoffset = - cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); - pntsd->dacloffset = - cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); - - acl.sd_buf = (char *)pntsd; - acl.sd_size = len; - - rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - return rc; - } - - smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_DEFAULT); - - rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, - smb_acl, def_smb_acl); - if (rc) { - pr_err("failed to encode ndr to posix acl\n"); - goto out; - } - - rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, - acl.posix_acl_hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - goto out; - } - - rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); - if (rc) { - pr_err("failed to encode ndr to posix acl\n"); - goto out; - } - - rc = ksmbd_vfs_setxattr(user_ns, dentry, - XATTR_NAME_SD, sd_ndr.data, - sd_ndr.offset, 0); - if (rc < 0) - pr_err("Failed to store XATTR ntacl :%d\n", rc); - - kfree(sd_ndr.data); -out: - kfree(acl_ndr.data); - kfree(smb_acl); - kfree(def_smb_acl); - return rc; -} - -int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd **pntsd) -{ - int rc; - struct ndr n; - struct inode *inode = d_inode(dentry); - struct ndr acl_ndr = {0}; - struct xattr_ntacl acl; - struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; - __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; - - rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); - if (rc <= 0) - return rc; - - n.length = rc; - rc = ndr_decode_v4_ntacl(&n, &acl); - if (rc) - goto free_n_data; - - smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_DEFAULT); - - rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, smb_acl, - def_smb_acl); - if (rc) { - pr_err("failed to encode ndr to posix acl\n"); - goto out_free; - } - - rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - goto out_free; - } - - if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { - pr_err("hash value diff\n"); - rc = -EINVAL; - goto out_free; - } - - *pntsd = acl.sd_buf; - if (acl.sd_size < sizeof(struct smb_ntsd)) { - pr_err("sd size is invalid\n"); - goto out_free; - } - - (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - - NDR_NTSD_OFFSETOF); - (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - - NDR_NTSD_OFFSETOF); - (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - - NDR_NTSD_OFFSETOF); - - rc = acl.sd_size; -out_free: - kfree(acl_ndr.data); - kfree(smb_acl); - kfree(def_smb_acl); - if (rc < 0) { - kfree(acl.sd_buf); - *pntsd = NULL; - } - -free_n_data: - kfree(n.data); - return rc; -} - -int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da) -{ - struct ndr n; - int err; - - err = ndr_encode_dos_attr(&n, da); - if (err) - return err; - - err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, - (void *)n.data, n.offset, 0); - if (err) - ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); - kfree(n.data); - - return err; -} - -int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da) -{ - struct ndr n; - int err; - - err = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, - (char **)&n.data); - if (err > 0) { - n.length = err; - if (ndr_decode_dos_attr(&n, da)) - err = -EINVAL; - kfree(n.data); - } else { - ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); - } - - return err; -} - -/** - * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format - * @p: destination buffer - * @ksmbd_kstat: ksmbd kstat wrapper - */ -void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) -{ - struct file_directory_info *info = (struct file_directory_info *)(*p); - struct kstat *kstat = ksmbd_kstat->kstat; - u64 time; - - info->FileIndex = 0; - info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); - time = ksmbd_UnixTimeToNT(kstat->atime); - info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(kstat->mtime); - info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(kstat->ctime); - info->ChangeTime = cpu_to_le64(time); - - if (ksmbd_kstat->file_attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { - info->EndOfFile = 0; - info->AllocationSize = 0; - } else { - info->EndOfFile = cpu_to_le64(kstat->size); - info->AllocationSize = cpu_to_le64(kstat->blocks << 9); - } - info->ExtFileAttributes = ksmbd_kstat->file_attributes; - - return info; -} - -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, - struct user_namespace *user_ns, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat) -{ - u64 time; - int rc; - - generic_fillattr(user_ns, d_inode(dentry), ksmbd_kstat->kstat); - - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); - ksmbd_kstat->create_time = time; - - /* - * set default value for the case that store dos attributes is not yes - * or that acl is disable in server's filesystem and the config is yes. - */ - if (S_ISDIR(ksmbd_kstat->kstat->mode)) - ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_DIRECTORY_LE; - else - ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_ARCHIVE_LE; - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - struct xattr_dos_attrib da; - - rc = ksmbd_vfs_get_dos_attrib_xattr(user_ns, dentry, &da); - if (rc > 0) { - ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); - ksmbd_kstat->create_time = da.create_time; - } else { - ksmbd_debug(VFS, "fail to load dos attribute.\n"); - } - } - - return 0; -} - -ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name, - int attr_name_len) -{ - char *name, *xattr_list = NULL; - ssize_t value_len = -ENOENT, xattr_list_len; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len <= 0) - goto out; - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); - if (strncasecmp(attr_name, name, attr_name_len)) - continue; - - value_len = ksmbd_vfs_xattr_len(user_ns, dentry, name); - break; - } - -out: - kvfree(xattr_list); - return value_len; -} - -int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size, int s_type) -{ - char *type, *buf; - - if (s_type == DIR_STREAM) - type = ":$INDEX_ALLOCATION"; - else - type = ":$DATA"; - - buf = kasprintf(GFP_KERNEL, "%s%s%s", - XATTR_NAME_STREAM, stream_name, type); - if (!buf) - return -ENOMEM; - - *xattr_stream_name = buf; - *xattr_stream_name_size = strlen(buf) + 1; - - return 0; -} - -int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, - struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, - unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, - loff_t *total_size_written) -{ - unsigned int i; - loff_t src_off, dst_off, src_file_size; - size_t len; - int ret; - - *chunk_count_written = 0; - *chunk_size_written = 0; - *total_size_written = 0; - - if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - pr_err("no right to read(%pD)\n", src_fp->filp); - return -EACCES; - } - if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { - pr_err("no right to write(%pD)\n", dst_fp->filp); - return -EACCES; - } - - if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) - return -EBADF; - - smb_break_all_levII_oplock(work, dst_fp, 1); - - if (!work->tcon->posix_extensions) { - for (i = 0; i < chunk_count; i++) { - src_off = le64_to_cpu(chunks[i].SourceOffset); - dst_off = le64_to_cpu(chunks[i].TargetOffset); - len = le32_to_cpu(chunks[i].Length); - - if (check_lock_range(src_fp->filp, src_off, - src_off + len - 1, READ)) - return -EAGAIN; - if (check_lock_range(dst_fp->filp, dst_off, - dst_off + len - 1, WRITE)) - return -EAGAIN; - } - } - - src_file_size = i_size_read(file_inode(src_fp->filp)); - - for (i = 0; i < chunk_count; i++) { - src_off = le64_to_cpu(chunks[i].SourceOffset); - dst_off = le64_to_cpu(chunks[i].TargetOffset); - len = le32_to_cpu(chunks[i].Length); - - if (src_off + len > src_file_size) - return -E2BIG; - - ret = vfs_copy_file_range(src_fp->filp, src_off, - dst_fp->filp, dst_off, len, 0); - if (ret == -EOPNOTSUPP || ret == -EXDEV) - ret = vfs_copy_file_range(src_fp->filp, src_off, - dst_fp->filp, dst_off, len, - COPY_FILE_SPLICE); - if (ret < 0) - return ret; - - *chunk_count_written += 1; - *total_size_written += ret; - } - return 0; -} - -void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) -{ - wait_event(flock->fl_wait, !flock->fl_blocker); -} - -int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) -{ - return wait_event_interruptible_timeout(flock->fl_wait, - !flock->fl_blocker, - timeout); -} - -void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) -{ - locks_delete_block(flock); -} - -int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, - struct inode *inode) -{ - struct posix_acl_state acl_state; - struct posix_acl *acls; - int rc; - - if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) - return -EOPNOTSUPP; - - ksmbd_debug(SMB, "Set posix acls\n"); - rc = init_acl_state(&acl_state, 1); - if (rc) - return rc; - - /* Set default owner group */ - acl_state.owner.allow = (inode->i_mode & 0700) >> 6; - acl_state.group.allow = (inode->i_mode & 0070) >> 3; - acl_state.other.allow = inode->i_mode & 0007; - acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; - acl_state.users->aces[acl_state.users->n++].perms.allow = - acl_state.owner.allow; - acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; - acl_state.groups->aces[acl_state.groups->n++].perms.allow = - acl_state.group.allow; - acl_state.mask.allow = 0x07; - - acls = posix_acl_alloc(6, GFP_KERNEL); - if (!acls) { - free_acl_state(&acl_state); - return -ENOMEM; - } - posix_state_to_acl(&acl_state, acls->a_entries); - rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - else if (S_ISDIR(inode->i_mode)) { - posix_state_to_acl(&acl_state, acls->a_entries); - rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, - acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } - free_acl_state(&acl_state); - posix_acl_release(acls); - return rc; -} - -int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, - struct inode *inode, struct inode *parent_inode) -{ - struct posix_acl *acls; - struct posix_acl_entry *pace; - int rc, i; - - if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) - return -EOPNOTSUPP; - - acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); - if (!acls) - return -ENOENT; - pace = acls->a_entries; - - for (i = 0; i < acls->a_count; i++, pace++) { - if (pace->e_tag == ACL_MASK) { - pace->e_perm = 0x07; - break; - } - } - - rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - if (S_ISDIR(inode->i_mode)) { - rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, - acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } - posix_acl_release(acls); - return rc; -} diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h deleted file mode 100644 index 593059ca8511..000000000000 --- a/fs/ksmbd/vfs.h +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_VFS_H__ -#define __KSMBD_VFS_H__ - -#include -#include -#include -#include -#include -#include - -#include "smbacl.h" -#include "xattr.h" - -/* - * Enumeration for stream type. - */ -enum { - DATA_STREAM = 1, /* type $DATA */ - DIR_STREAM /* type $INDEX_ALLOCATION */ -}; - -/* CreateOptions */ -#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) -#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) - -#define CREATE_OPTION_READONLY 0x10000000 -/* system. NB not sent over wire */ -#define CREATE_OPTION_SPECIAL 0x20000000 - -struct ksmbd_work; -struct ksmbd_file; -struct ksmbd_conn; - -struct ksmbd_dir_info { - const char *name; - char *wptr; - char *rptr; - int name_len; - int out_buf_len; - int num_entry; - int data_count; - int last_entry_offset; - bool hide_dot_file; - int flags; - int last_entry_off_align; -}; - -struct ksmbd_readdir_data { - struct dir_context ctx; - union { - void *private; - char *dirent; - }; - - unsigned int used; - unsigned int dirent_count; - unsigned int file_attr; - struct unicode_map *um; -}; - -/* ksmbd kstat wrapper to get valid create time when reading dir entry */ -struct ksmbd_kstat { - struct kstat *kstat; - unsigned long long create_time; - __le32 file_attributes; -}; - -int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, - struct dentry *child); -int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); -int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, - struct dentry *dentry, __le32 *daccess); -int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); -int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); -int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, - size_t count, loff_t *pos); -int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, - ssize_t *written); -int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); -int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); -int ksmbd_vfs_link(struct ksmbd_work *work, - const char *oldname, const char *newname); -int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); -int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - char *newname); -int ksmbd_vfs_truncate(struct ksmbd_work *work, - struct ksmbd_file *fp, loff_t size); -struct srv_copychunk; -int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, - struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, - unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, - loff_t *total_size_written); -ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); -ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, - struct dentry *dentry, - char *xattr_name, - char **xattr_buf); -ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name, - int attr_name_len); -int ksmbd_vfs_setxattr(struct user_namespace *user_ns, - struct dentry *dentry, const char *attr_name, - void *attr_value, size_t attr_size, int flags); -int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size, int s_type); -int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name); -int ksmbd_vfs_kern_path(struct ksmbd_work *work, - char *name, unsigned int flags, struct path *path, - bool caseless); -struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, - const char *name, - unsigned int flags, - struct path *path); -int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); -void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); -int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t off, loff_t len); -struct file_allocated_range_buffer; -int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, - struct file_allocated_range_buffer *ranges, - unsigned int in_count, unsigned int *out_count); -int ksmbd_vfs_unlink(struct user_namespace *user_ns, - struct dentry *dir, struct dentry *dentry); -void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, - struct user_namespace *user_ns, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat); -void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); -int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); -void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); -int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, - struct dentry *dentry); -int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, - struct dentry *dentry); -int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd *pntsd, int len); -int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd **pntsd); -int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da); -int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da); -int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, - struct inode *inode); -int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, - struct inode *inode, - struct inode *parent_inode); -#endif /* __KSMBD_VFS_H__ */ diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c deleted file mode 100644 index 0ae5dd0829e9..000000000000 --- a/fs/ksmbd/vfs_cache.c +++ /dev/null @@ -1,708 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "glob.h" -#include "vfs_cache.h" -#include "oplock.h" -#include "vfs.h" -#include "connection.h" -#include "mgmt/tree_connect.h" -#include "mgmt/user_session.h" -#include "smb_common.h" - -#define S_DEL_PENDING 1 -#define S_DEL_ON_CLS 2 -#define S_DEL_ON_CLS_STREAM 8 - -static unsigned int inode_hash_mask __read_mostly; -static unsigned int inode_hash_shift __read_mostly; -static struct hlist_head *inode_hashtable __read_mostly; -static DEFINE_RWLOCK(inode_hash_lock); - -static struct ksmbd_file_table global_ft; -static atomic_long_t fd_limit; -static struct kmem_cache *filp_cache; - -void ksmbd_set_fd_limit(unsigned long limit) -{ - limit = min(limit, get_max_files()); - atomic_long_set(&fd_limit, limit); -} - -static bool fd_limit_depleted(void) -{ - long v = atomic_long_dec_return(&fd_limit); - - if (v >= 0) - return false; - atomic_long_inc(&fd_limit); - return true; -} - -static void fd_limit_close(void) -{ - atomic_long_inc(&fd_limit); -} - -/* - * INODE hash - */ - -static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) -{ - unsigned long tmp; - - tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / - L1_CACHE_BYTES; - tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); - return tmp & inode_hash_mask; -} - -static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) -{ - struct hlist_head *head = inode_hashtable + - inode_hash(inode->i_sb, inode->i_ino); - struct ksmbd_inode *ci = NULL, *ret_ci = NULL; - - hlist_for_each_entry(ci, head, m_hash) { - if (ci->m_inode == inode) { - if (atomic_inc_not_zero(&ci->m_count)) - ret_ci = ci; - break; - } - } - return ret_ci; -} - -static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) -{ - return __ksmbd_inode_lookup(file_inode(fp->filp)); -} - -static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) -{ - struct ksmbd_inode *ci; - - read_lock(&inode_hash_lock); - ci = __ksmbd_inode_lookup(inode); - read_unlock(&inode_hash_lock); - return ci; -} - -int ksmbd_query_inode_status(struct inode *inode) -{ - struct ksmbd_inode *ci; - int ret = KSMBD_INODE_STATUS_UNKNOWN; - - read_lock(&inode_hash_lock); - ci = __ksmbd_inode_lookup(inode); - if (ci) { - ret = KSMBD_INODE_STATUS_OK; - if (ci->m_flags & S_DEL_PENDING) - ret = KSMBD_INODE_STATUS_PENDING_DELETE; - atomic_dec(&ci->m_count); - } - read_unlock(&inode_hash_lock); - return ret; -} - -bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) -{ - return (fp->f_ci->m_flags & S_DEL_PENDING); -} - -void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) -{ - fp->f_ci->m_flags |= S_DEL_PENDING; -} - -void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) -{ - fp->f_ci->m_flags &= ~S_DEL_PENDING; -} - -void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, - int file_info) -{ - if (ksmbd_stream_fd(fp)) { - fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; - return; - } - - fp->f_ci->m_flags |= S_DEL_ON_CLS; -} - -static void ksmbd_inode_hash(struct ksmbd_inode *ci) -{ - struct hlist_head *b = inode_hashtable + - inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); - - hlist_add_head(&ci->m_hash, b); -} - -static void ksmbd_inode_unhash(struct ksmbd_inode *ci) -{ - write_lock(&inode_hash_lock); - hlist_del_init(&ci->m_hash); - write_unlock(&inode_hash_lock); -} - -static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) -{ - ci->m_inode = file_inode(fp->filp); - atomic_set(&ci->m_count, 1); - atomic_set(&ci->op_count, 0); - atomic_set(&ci->sop_count, 0); - ci->m_flags = 0; - ci->m_fattr = 0; - INIT_LIST_HEAD(&ci->m_fp_list); - INIT_LIST_HEAD(&ci->m_op_list); - rwlock_init(&ci->m_lock); - return 0; -} - -static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) -{ - struct ksmbd_inode *ci, *tmpci; - int rc; - - read_lock(&inode_hash_lock); - ci = ksmbd_inode_lookup(fp); - read_unlock(&inode_hash_lock); - if (ci) - return ci; - - ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); - if (!ci) - return NULL; - - rc = ksmbd_inode_init(ci, fp); - if (rc) { - pr_err("inode initialized failed\n"); - kfree(ci); - return NULL; - } - - write_lock(&inode_hash_lock); - tmpci = ksmbd_inode_lookup(fp); - if (!tmpci) { - ksmbd_inode_hash(ci); - } else { - kfree(ci); - ci = tmpci; - } - write_unlock(&inode_hash_lock); - return ci; -} - -static void ksmbd_inode_free(struct ksmbd_inode *ci) -{ - ksmbd_inode_unhash(ci); - kfree(ci); -} - -static void ksmbd_inode_put(struct ksmbd_inode *ci) -{ - if (atomic_dec_and_test(&ci->m_count)) - ksmbd_inode_free(ci); -} - -int __init ksmbd_inode_hash_init(void) -{ - unsigned int loop; - unsigned long numentries = 16384; - unsigned long bucketsize = sizeof(struct hlist_head); - unsigned long size; - - inode_hash_shift = ilog2(numentries); - inode_hash_mask = (1 << inode_hash_shift) - 1; - - size = bucketsize << inode_hash_shift; - - /* init master fp hash table */ - inode_hashtable = vmalloc(size); - if (!inode_hashtable) - return -ENOMEM; - - for (loop = 0; loop < (1U << inode_hash_shift); loop++) - INIT_HLIST_HEAD(&inode_hashtable[loop]); - return 0; -} - -void ksmbd_release_inode_hash(void) -{ - vfree(inode_hashtable); -} - -static void __ksmbd_inode_close(struct ksmbd_file *fp) -{ - struct dentry *dir, *dentry; - struct ksmbd_inode *ci = fp->f_ci; - int err; - struct file *filp; - - filp = fp->filp; - if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { - ci->m_flags &= ~S_DEL_ON_CLS_STREAM; - err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), - filp->f_path.dentry, - fp->stream.name); - if (err) - pr_err("remove xattr failed : %s\n", - fp->stream.name); - } - - if (atomic_dec_and_test(&ci->m_count)) { - write_lock(&ci->m_lock); - if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { - dentry = filp->f_path.dentry; - dir = dentry->d_parent; - ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); - write_unlock(&ci->m_lock); - ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); - write_lock(&ci->m_lock); - } - write_unlock(&ci->m_lock); - - ksmbd_inode_free(ci); - } -} - -static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) -{ - if (!has_file_id(fp->persistent_id)) - return; - - write_lock(&global_ft.lock); - idr_remove(global_ft.idr, fp->persistent_id); - write_unlock(&global_ft.lock); -} - -static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) -{ - if (!has_file_id(fp->volatile_id)) - return; - - write_lock(&fp->f_ci->m_lock); - list_del_init(&fp->node); - write_unlock(&fp->f_ci->m_lock); - - write_lock(&ft->lock); - idr_remove(ft->idr, fp->volatile_id); - write_unlock(&ft->lock); -} - -static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) -{ - struct file *filp; - struct ksmbd_lock *smb_lock, *tmp_lock; - - fd_limit_close(); - __ksmbd_remove_durable_fd(fp); - __ksmbd_remove_fd(ft, fp); - - close_id_del_oplock(fp); - filp = fp->filp; - - __ksmbd_inode_close(fp); - if (!IS_ERR_OR_NULL(filp)) - fput(filp); - - /* because the reference count of fp is 0, it is guaranteed that - * there are not accesses to fp->lock_list. - */ - list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&fp->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); - - list_del(&smb_lock->flist); - locks_free_lock(smb_lock->fl); - kfree(smb_lock); - } - - if (ksmbd_stream_fd(fp)) - kfree(fp->stream.name); - kmem_cache_free(filp_cache, fp); -} - -static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) -{ - if (!atomic_inc_not_zero(&fp->refcount)) - return NULL; - return fp; -} - -static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, - u64 id) -{ - struct ksmbd_file *fp; - - if (!has_file_id(id)) - return NULL; - - read_lock(&ft->lock); - fp = idr_find(ft->idr, id); - if (fp) - fp = ksmbd_fp_get(fp); - read_unlock(&ft->lock); - return fp; -} - -static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) -{ - __ksmbd_close_fd(&work->sess->file_table, fp); - atomic_dec(&work->conn->stats.open_files_count); -} - -static void set_close_state_blocked_works(struct ksmbd_file *fp) -{ - struct ksmbd_work *cancel_work; - - spin_lock(&fp->f_lock); - list_for_each_entry(cancel_work, &fp->blocked_works, - fp_entry) { - cancel_work->state = KSMBD_WORK_CLOSED; - cancel_work->cancel_fn(cancel_work->cancel_argv); - } - spin_unlock(&fp->f_lock); -} - -int ksmbd_close_fd(struct ksmbd_work *work, u64 id) -{ - struct ksmbd_file *fp; - struct ksmbd_file_table *ft; - - if (!has_file_id(id)) - return 0; - - ft = &work->sess->file_table; - read_lock(&ft->lock); - fp = idr_find(ft->idr, id); - if (fp) { - set_close_state_blocked_works(fp); - - if (!atomic_dec_and_test(&fp->refcount)) - fp = NULL; - } - read_unlock(&ft->lock); - - if (!fp) - return -EINVAL; - - __put_fd_final(work, fp); - return 0; -} - -void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) -{ - if (!fp) - return; - - if (!atomic_dec_and_test(&fp->refcount)) - return; - __put_fd_final(work, fp); -} - -static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) -{ - if (!fp) - return false; - if (fp->tcon != tcon) - return false; - return true; -} - -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) -{ - return __ksmbd_lookup_fd(&work->sess->file_table, id); -} - -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) -{ - struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); - - if (__sanity_check(work->tcon, fp)) - return fp; - - ksmbd_fd_put(work, fp); - return NULL; -} - -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, - u64 pid) -{ - struct ksmbd_file *fp; - - if (!has_file_id(id)) { - id = work->compound_fid; - pid = work->compound_pfid; - } - - fp = __ksmbd_lookup_fd(&work->sess->file_table, id); - if (!__sanity_check(work->tcon, fp)) { - ksmbd_fd_put(work, fp); - return NULL; - } - if (fp->persistent_id != pid) { - ksmbd_fd_put(work, fp); - return NULL; - } - return fp; -} - -struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) -{ - return __ksmbd_lookup_fd(&global_ft, id); -} - -struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) -{ - struct ksmbd_file *fp = NULL; - unsigned int id; - - read_lock(&global_ft.lock); - idr_for_each_entry(global_ft.idr, fp, id) { - if (!memcmp(fp->create_guid, - cguid, - SMB2_CREATE_GUID_SIZE)) { - fp = ksmbd_fp_get(fp); - break; - } - } - read_unlock(&global_ft.lock); - - return fp; -} - -struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) -{ - struct ksmbd_file *lfp; - struct ksmbd_inode *ci; - - ci = ksmbd_inode_lookup_by_vfsinode(inode); - if (!ci) - return NULL; - - read_lock(&ci->m_lock); - list_for_each_entry(lfp, &ci->m_fp_list, node) { - if (inode == file_inode(lfp->filp)) { - atomic_dec(&ci->m_count); - lfp = ksmbd_fp_get(lfp); - read_unlock(&ci->m_lock); - return lfp; - } - } - atomic_dec(&ci->m_count); - read_unlock(&ci->m_lock); - return NULL; -} - -#define OPEN_ID_TYPE_VOLATILE_ID (0) -#define OPEN_ID_TYPE_PERSISTENT_ID (1) - -static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) -{ - if (type == OPEN_ID_TYPE_VOLATILE_ID) - fp->volatile_id = id; - if (type == OPEN_ID_TYPE_PERSISTENT_ID) - fp->persistent_id = id; -} - -static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, - int type) -{ - u64 id = 0; - int ret; - - if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { - __open_id_set(fp, KSMBD_NO_FID, type); - return -EMFILE; - } - - idr_preload(GFP_KERNEL); - write_lock(&ft->lock); - ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); - if (ret >= 0) { - id = ret; - ret = 0; - } else { - id = KSMBD_NO_FID; - fd_limit_close(); - } - - __open_id_set(fp, id, type); - write_unlock(&ft->lock); - idr_preload_end(); - return ret; -} - -unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) -{ - __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); - return fp->persistent_id; -} - -struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) -{ - struct ksmbd_file *fp; - int ret; - - fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); - if (!fp) { - pr_err("Failed to allocate memory\n"); - return ERR_PTR(-ENOMEM); - } - - INIT_LIST_HEAD(&fp->blocked_works); - INIT_LIST_HEAD(&fp->node); - INIT_LIST_HEAD(&fp->lock_list); - spin_lock_init(&fp->f_lock); - atomic_set(&fp->refcount, 1); - - fp->filp = filp; - fp->conn = work->conn; - fp->tcon = work->tcon; - fp->volatile_id = KSMBD_NO_FID; - fp->persistent_id = KSMBD_NO_FID; - fp->f_ci = ksmbd_inode_get(fp); - - if (!fp->f_ci) { - ret = -ENOMEM; - goto err_out; - } - - ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); - if (ret) { - ksmbd_inode_put(fp->f_ci); - goto err_out; - } - - atomic_inc(&work->conn->stats.open_files_count); - return fp; - -err_out: - kmem_cache_free(filp_cache, fp); - return ERR_PTR(ret); -} - -static int -__close_file_table_ids(struct ksmbd_file_table *ft, - struct ksmbd_tree_connect *tcon, - bool (*skip)(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp)) -{ - unsigned int id; - struct ksmbd_file *fp; - int num = 0; - - idr_for_each_entry(ft->idr, fp, id) { - if (skip(tcon, fp)) - continue; - - set_close_state_blocked_works(fp); - - if (!atomic_dec_and_test(&fp->refcount)) - continue; - __ksmbd_close_fd(ft, fp); - num++; - } - return num; -} - -static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) -{ - return fp->tcon != tcon; -} - -static bool session_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) -{ - return false; -} - -void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) -{ - int num = __close_file_table_ids(&work->sess->file_table, - work->tcon, - tree_conn_fd_check); - - atomic_sub(num, &work->conn->stats.open_files_count); -} - -void ksmbd_close_session_fds(struct ksmbd_work *work) -{ - int num = __close_file_table_ids(&work->sess->file_table, - work->tcon, - session_fd_check); - - atomic_sub(num, &work->conn->stats.open_files_count); -} - -int ksmbd_init_global_file_table(void) -{ - return ksmbd_init_file_table(&global_ft); -} - -void ksmbd_free_global_file_table(void) -{ - struct ksmbd_file *fp = NULL; - unsigned int id; - - idr_for_each_entry(global_ft.idr, fp, id) { - __ksmbd_remove_durable_fd(fp); - kmem_cache_free(filp_cache, fp); - } - - ksmbd_destroy_file_table(&global_ft); -} - -int ksmbd_init_file_table(struct ksmbd_file_table *ft) -{ - ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); - if (!ft->idr) - return -ENOMEM; - - idr_init(ft->idr); - rwlock_init(&ft->lock); - return 0; -} - -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) -{ - if (!ft->idr) - return; - - __close_file_table_ids(ft, NULL, session_fd_check); - idr_destroy(ft->idr); - kfree(ft->idr); - ft->idr = NULL; -} - -int ksmbd_init_file_cache(void) -{ - filp_cache = kmem_cache_create("ksmbd_file_cache", - sizeof(struct ksmbd_file), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!filp_cache) - goto out; - - return 0; - -out: - pr_err("failed to allocate file cache\n"); - return -ENOMEM; -} - -void ksmbd_exit_file_cache(void) -{ - kmem_cache_destroy(filp_cache); -} diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h deleted file mode 100644 index fcb13413fa8d..000000000000 --- a/fs/ksmbd/vfs_cache.h +++ /dev/null @@ -1,166 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#ifndef __VFS_CACHE_H__ -#define __VFS_CACHE_H__ - -#include -#include -#include -#include -#include -#include - -#include "vfs.h" - -/* Windows style file permissions for extended response */ -#define FILE_GENERIC_ALL 0x1F01FF -#define FILE_GENERIC_READ 0x120089 -#define FILE_GENERIC_WRITE 0x120116 -#define FILE_GENERIC_EXECUTE 0X1200a0 - -#define KSMBD_START_FID 0 -#define KSMBD_NO_FID (INT_MAX) -#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) - -struct ksmbd_conn; -struct ksmbd_session; - -struct ksmbd_lock { - struct file_lock *fl; - struct list_head clist; - struct list_head flist; - struct list_head llist; - unsigned int flags; - int cmd; - int zero_len; - unsigned long long start; - unsigned long long end; -}; - -struct stream { - char *name; - ssize_t size; -}; - -struct ksmbd_inode { - rwlock_t m_lock; - atomic_t m_count; - atomic_t op_count; - /* opinfo count for streams */ - atomic_t sop_count; - struct inode *m_inode; - unsigned int m_flags; - struct hlist_node m_hash; - struct list_head m_fp_list; - struct list_head m_op_list; - struct oplock_info *m_opinfo; - __le32 m_fattr; -}; - -struct ksmbd_file { - struct file *filp; - u64 persistent_id; - u64 volatile_id; - - spinlock_t f_lock; - - struct ksmbd_inode *f_ci; - struct ksmbd_inode *f_parent_ci; - struct oplock_info __rcu *f_opinfo; - struct ksmbd_conn *conn; - struct ksmbd_tree_connect *tcon; - - atomic_t refcount; - __le32 daccess; - __le32 saccess; - __le32 coption; - __le32 cdoption; - __u64 create_time; - __u64 itime; - - bool is_nt_open; - bool attrib_only; - - char client_guid[16]; - char create_guid[16]; - char app_instance_id[16]; - - struct stream stream; - struct list_head node; - struct list_head blocked_works; - struct list_head lock_list; - - int durable_timeout; - - /* if ls is happening on directory, below is valid*/ - struct ksmbd_readdir_data readdir_data; - int dot_dotdot[2]; -}; - -static inline void set_ctx_actor(struct dir_context *ctx, - filldir_t actor) -{ - ctx->actor = actor; -} - -#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG - -struct ksmbd_file_table { - rwlock_t lock; - struct idr *idr; -}; - -static inline bool has_file_id(u64 id) -{ - return id < KSMBD_NO_FID; -} - -static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) -{ - return fp->stream.name != NULL; -} - -int ksmbd_init_file_table(struct ksmbd_file_table *ft); -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); -int ksmbd_close_fd(struct ksmbd_work *work, u64 id); -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, - u64 pid); -void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); -struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); -struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); -struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); -unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); -struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); -void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); -void ksmbd_close_session_fds(struct ksmbd_work *work); -int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); -int ksmbd_init_global_file_table(void); -void ksmbd_free_global_file_table(void); -void ksmbd_set_fd_limit(unsigned long limit); - -/* - * INODE hash - */ -int __init ksmbd_inode_hash_init(void); -void ksmbd_release_inode_hash(void); - -enum KSMBD_INODE_STATUS { - KSMBD_INODE_STATUS_OK, - KSMBD_INODE_STATUS_UNKNOWN, - KSMBD_INODE_STATUS_PENDING_DELETE, -}; - -int ksmbd_query_inode_status(struct inode *inode); -bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); -void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); -void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); -void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, - int file_info); -int ksmbd_init_file_cache(void); -void ksmbd_exit_file_cache(void); -#endif /* __VFS_CACHE_H__ */ diff --git a/fs/ksmbd/xattr.h b/fs/ksmbd/xattr.h deleted file mode 100644 index 16499ca5c82d..000000000000 --- a/fs/ksmbd/xattr.h +++ /dev/null @@ -1,122 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021 Samsung Electronics Co., Ltd. - */ - -#ifndef __XATTR_H__ -#define __XATTR_H__ - -/* - * These are on-disk structures to store additional metadata into xattr to - * reproduce windows filesystem semantics. And they are encoded with NDR to - * compatible with samba's xattr meta format. The compatibility with samba - * is important because it can lose the information(file attribute, - * creation time, acls) about the existing files when switching between - * ksmbd and samba. - */ - -/* - * Dos attribute flags used for what variable is valid. - */ -enum { - XATTR_DOSINFO_ATTRIB = 0x00000001, - XATTR_DOSINFO_EA_SIZE = 0x00000002, - XATTR_DOSINFO_SIZE = 0x00000004, - XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, - XATTR_DOSINFO_CREATE_TIME = 0x00000010, - XATTR_DOSINFO_CHANGE_TIME = 0x00000020, - XATTR_DOSINFO_ITIME = 0x00000040 -}; - -/* - * Dos attribute structure which is compatible with samba's one. - * Storing it into the xattr named "DOSATTRIB" separately from inode - * allows ksmbd to faithfully reproduce windows filesystem semantics - * on top of a POSIX filesystem. - */ -struct xattr_dos_attrib { - __u16 version; /* version 3 or version 4 */ - __u32 flags; /* valid flags */ - __u32 attr; /* Dos attribute */ - __u32 ea_size; /* EA size */ - __u64 size; - __u64 alloc_size; - __u64 create_time; /* File creation time */ - __u64 change_time; /* File change time */ - __u64 itime; /* Invented/Initial time */ -}; - -/* - * Enumeration is used for computing posix acl hash. - */ -enum { - SMB_ACL_TAG_INVALID = 0, - SMB_ACL_USER, - SMB_ACL_USER_OBJ, - SMB_ACL_GROUP, - SMB_ACL_GROUP_OBJ, - SMB_ACL_OTHER, - SMB_ACL_MASK -}; - -#define SMB_ACL_READ 4 -#define SMB_ACL_WRITE 2 -#define SMB_ACL_EXECUTE 1 - -struct xattr_acl_entry { - int type; - uid_t uid; - gid_t gid; - mode_t perm; -}; - -/* - * xattr_smb_acl structure is used for computing posix acl hash. - */ -struct xattr_smb_acl { - int count; - int next; - struct xattr_acl_entry entries[]; -}; - -/* 64bytes hash in xattr_ntacl is computed with sha256 */ -#define XATTR_SD_HASH_TYPE_SHA256 0x1 -#define XATTR_SD_HASH_SIZE 64 - -/* - * xattr_ntacl is used for storing ntacl and hashes. - * Hash is used for checking valid posix acl and ntacl in xattr. - */ -struct xattr_ntacl { - __u16 version; /* version 4*/ - void *sd_buf; - __u32 sd_size; - __u16 hash_type; /* hash type */ - __u8 desc[10]; /* posix_acl description */ - __u16 desc_len; - __u64 current_time; - __u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */ - __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ -}; - -/* DOS ATTRIBUITE XATTR PREFIX */ -#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" -#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) -#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) -#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ - (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) - -/* STREAM XATTR PREFIX */ -#define STREAM_PREFIX "DosStream." -#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) -#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) -#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) - -/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */ -#define SD_PREFIX "NTACL" -#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) -#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX) -#define XATTR_NAME_SD_LEN \ - (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) - -#endif /* __XATTR_H__ */ diff --git a/fs/smb/Kconfig b/fs/smb/Kconfig new file mode 100644 index 000000000000..ef425789fa6a --- /dev/null +++ b/fs/smb/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# smbfs configuration + +source "fs/smb/client/Kconfig" +source "fs/smb/server/Kconfig" + +config SMBFS + tristate + default y if CIFS=y || SMB_SERVER=y + default m if CIFS=m || SMB_SERVER=m diff --git a/fs/smb/Makefile b/fs/smb/Makefile new file mode 100644 index 000000000000..9a1bf59a1a65 --- /dev/null +++ b/fs/smb/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SMBFS) += common/ +obj-$(CONFIG_CIFS) += client/ +obj-$(CONFIG_SMB_SERVER) += server/ diff --git a/fs/smb/client/Kconfig b/fs/smb/client/Kconfig new file mode 100644 index 000000000000..3b7e3b9e4fd2 --- /dev/null +++ b/fs/smb/client/Kconfig @@ -0,0 +1,203 @@ +# SPDX-License-Identifier: GPL-2.0-only +config CIFS + tristate "SMB3 and CIFS support (advanced network filesystem)" + depends on INET + select NLS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_CMAC + select CRYPTO_HMAC + select CRYPTO_AEAD2 + select CRYPTO_CCM + select CRYPTO_GCM + select CRYPTO_ECB + select CRYPTO_AES + select KEYS + select DNS_RESOLVER + select ASN1 + select OID_REGISTRY + help + This is the client VFS module for the SMB3 family of NAS protocols, + (including support for the most recent, most secure dialect SMB3.1.1) + as well as for earlier dialects such as SMB2.1, SMB2 and the older + Common Internet File System (CIFS) protocol. CIFS was the successor + to the original dialect, the Server Message Block (SMB) protocol, the + native file sharing mechanism for most early PC operating systems. + + The SMB3 protocol is supported by most modern operating systems + and NAS appliances (e.g. Samba, Windows 10, Windows Server 2016, + MacOS) and even in the cloud (e.g. Microsoft Azure). + The older CIFS protocol was included in Windows NT4, 2000 and XP (and + later) as well by Samba (which provides excellent CIFS and SMB3 + server support for Linux and many other operating systems). Use of + dialects older than SMB2.1 is often discouraged on public networks. + This module also provides limited support for OS/2 and Windows ME + and similar very old servers. + + This module provides an advanced network file system client + for mounting to SMB3 (and CIFS) compliant servers. It includes + support for DFS (hierarchical name space), secure per-user + session establishment via Kerberos or NTLM or NTLMv2, RDMA + (smbdirect), advanced security features, per-share encryption, + directory leases, safe distributed caching (oplock), optional packet + signing, Unicode and other internationalization improvements. + + In general, the default dialects, SMB3 and later, enable better + performance, security and features, than would be possible with CIFS. + Note that when mounting to Samba, due to the CIFS POSIX extensions, + CIFS mounts can provide slightly better POSIX compatibility + than SMB3 mounts. SMB2/SMB3 mount options are also + slightly simpler (compared to CIFS) due to protocol improvements. + + If you need to mount to Samba, Azure, Macs or Windows from this machine, say Y. + +config CIFS_STATS2 + bool "Extended statistics" + depends on CIFS + default y + help + Enabling this option will allow more detailed statistics on SMB + request timing to be displayed in /proc/fs/cifs/DebugData and also + allow optional logging of slow responses to dmesg (depending on the + value of /proc/fs/cifs/cifsFYI). See Documentation/admin-guide/cifs/usage.rst + for more details. These additional statistics may have a minor effect + on performance and memory utilization. + + If unsure, say Y. + +config CIFS_ALLOW_INSECURE_LEGACY + bool "Support legacy servers which use less secure dialects" + depends on CIFS + default y + help + Modern dialects, SMB2.1 and later (including SMB3 and 3.1.1), have + additional security features, including protection against + man-in-the-middle attacks and stronger crypto hashes, so the use + of legacy dialects (SMB1/CIFS and SMB2.0) is discouraged. + + Disabling this option prevents users from using vers=1.0 or vers=2.0 + on mounts with cifs.ko + + If unsure, say Y. + +config CIFS_UPCALL + bool "Kerberos/SPNEGO advanced session setup" + depends on CIFS + help + Enables an upcall mechanism for CIFS which accesses userspace helper + utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets + which are needed to mount to certain secure servers (for which more + secure Kerberos authentication is required). If unsure, say Y. + +config CIFS_XATTR + bool "CIFS extended attributes" + depends on CIFS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page for details). + CIFS maps the name of extended attributes beginning with the user + namespace prefix to SMB/CIFS EAs. EAs are stored on Windows + servers without the user namespace prefix, but their names are + seen by Linux cifs clients prefaced by the user namespace prefix. + The system namespace (used by some filesystems to store ACLs) is + not supported at this time. + + If unsure, say Y. + +config CIFS_POSIX + bool "CIFS POSIX Extensions" + depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR + help + Enabling this option will cause the cifs client to attempt to + negotiate a newer dialect with servers, such as Samba 3.0.5 + or later, that optionally can handle more POSIX like (rather + than Windows like) file behavior. It also enables + support for POSIX ACLs (getfacl and setfacl) to servers + (such as Samba 3.10 and later) which can negotiate + CIFS POSIX ACL support. If unsure, say N. + +config CIFS_DEBUG + bool "Enable CIFS debugging routines" + default y + depends on CIFS + help + Enabling this option adds helpful debugging messages to + the cifs code which increases the size of the cifs module. + If unsure, say Y. + +config CIFS_DEBUG2 + bool "Enable additional CIFS debugging routines" + depends on CIFS_DEBUG + help + Enabling this option adds a few more debugging routines + to the cifs code which slightly increases the size of + the cifs module and can cause additional logging of debug + messages in some error paths, slowing performance. This + option can be turned off unless you are debugging + cifs problems. If unsure, say N. + +config CIFS_DEBUG_DUMP_KEYS + bool "Dump encryption keys for offline decryption (Unsafe)" + depends on CIFS_DEBUG + help + Enabling this will dump the encryption and decryption keys + used to communicate on an encrypted share connection on the + console. This allows Wireshark to decrypt and dissect + encrypted network captures. Enable this carefully. + If unsure, say N. + +config CIFS_DFS_UPCALL + bool "DFS feature support" + depends on CIFS + help + Distributed File System (DFS) support is used to access shares + transparently in an enterprise name space, even if the share + moves to a different server. This feature also enables + an upcall mechanism for CIFS which contacts userspace helper + utilities to provide server name resolution (host names to + IP addresses) which is needed in order to reconnect to + servers if their addresses change or for implicit mounts of + DFS junction points. If unsure, say Y. + +config CIFS_SWN_UPCALL + bool "SWN feature support" + depends on CIFS + help + The Service Witness Protocol (SWN) is used to get notifications + from a highly available server of resource state changes. This + feature enables an upcall mechanism for CIFS which contacts a + userspace daemon to establish the DCE/RPC connection to retrieve + the cluster available interfaces and resource change notifications. + If unsure, say Y. + +config CIFS_NFSD_EXPORT + bool "Allow nfsd to export CIFS file system" + depends on CIFS && BROKEN + help + Allows NFS server to export a CIFS mounted share (nfsd over cifs) + +config CIFS_SMB_DIRECT + bool "SMB Direct support" + depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + help + Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say Y. + +config CIFS_FSCACHE + bool "Provide CIFS client caching support" + depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y + help + Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data + to be cached locally on disk through the general filesystem cache + manager. If unsure, say N. + +config CIFS_ROOT + bool "SMB root file system (Experimental)" + depends on CIFS=y && IP_PNP + help + Enables root file system support over SMB protocol. + + Most people say N here. diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile new file mode 100644 index 000000000000..7c9785973f49 --- /dev/null +++ b/fs/smb/client/Makefile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Linux CIFS/SMB2/SMB3 VFS client +# +ccflags-y += -I$(src) # needed for trace events +obj-$(CONFIG_CIFS) += cifs.o + +cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \ + inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \ + cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \ + readdir.o ioctl.o sess.o export.o unc.o winucase.o \ + smb2ops.o smb2maperror.o smb2transport.o \ + smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \ + dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o + +$(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h + +$(obj)/cifs_spnego_negtokeninit.asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.c $(obj)/cifs_spnego_negtokeninit.asn1.h + +cifs-$(CONFIG_CIFS_XATTR) += xattr.o + +cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o + +cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o + +cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o + +cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o + +cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o + +cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o + +cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o diff --git a/fs/smb/client/asn1.c b/fs/smb/client/asn1.c new file mode 100644 index 000000000000..b5724ef9f182 --- /dev/null +++ b/fs/smb/client/asn1.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifsproto.h" +#include "cifs_spnego_negtokeninit.asn1.h" + +int +decode_negTokenInit(unsigned char *security_blob, int length, + struct TCP_Server_Info *server) +{ + if (asn1_ber_decoder(&cifs_spnego_negtokeninit_decoder, server, + security_blob, length) == 0) + return 1; + else + return 0; +} + +int cifs_gssapi_this_mech(void *context, size_t hdrlen, + unsigned char tag, const void *value, size_t vlen) +{ + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid != OID_spnego) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + cifs_dbg(FYI, "Error decoding negTokenInit header: unexpected OID %s\n", + buf); + return -EBADMSG; + } + return 0; +} + +int cifs_neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct TCP_Server_Info *server = context; + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid == OID_mskrb5) + server->sec_mskerberos = true; + else if (oid == OID_krb5u2u) + server->sec_kerberosu2u = true; + else if (oid == OID_krb5) + server->sec_kerberos = true; + else if (oid == OID_ntlmssp) + server->sec_ntlmssp = true; + else { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + cifs_dbg(FYI, "Decoding negTokenInit: unsupported OID %s\n", + buf); + } + return 0; +} diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c new file mode 100644 index 000000000000..bfc964b36c72 --- /dev/null +++ b/fs/smb/client/cached_dir.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Functions to handle the cached directory entries + * + * Copyright (c) 2022, Ronnie Sahlberg + */ + +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "smb2proto.h" +#include "cached_dir.h" + +static struct cached_fid *init_cached_dir(const char *path); +static void free_cached_dir(struct cached_fid *cfid); +static void smb2_close_cached_fid(struct kref *ref); + +static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, + const char *path, + bool lookup_only) +{ + struct cached_fid *cfid; + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry(cfid, &cfids->entries, entry) { + if (!strcmp(cfid->path, path)) { + /* + * If it doesn't have a lease it is either not yet + * fully cached or it may be in the process of + * being deleted due to a lease break. + */ + if (!cfid->has_lease) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + kref_get(&cfid->refcount); + spin_unlock(&cfids->cfid_list_lock); + return cfid; + } + } + if (lookup_only) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + if (cfids->num_entries >= MAX_CACHED_FIDS) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + cfid = init_cached_dir(path); + if (cfid == NULL) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + cfid->cfids = cfids; + cfids->num_entries++; + list_add(&cfid->entry, &cfids->entries); + cfid->on_list = true; + kref_get(&cfid->refcount); + spin_unlock(&cfids->cfid_list_lock); + return cfid; +} + +static struct dentry * +path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path) +{ + struct dentry *dentry; + const char *s, *p; + char sep; + + sep = CIFS_DIR_SEP(cifs_sb); + dentry = dget(cifs_sb->root); + s = path; + + do { + struct inode *dir = d_inode(dentry); + struct dentry *child; + + if (!S_ISDIR(dir->i_mode)) { + dput(dentry); + dentry = ERR_PTR(-ENOTDIR); + break; + } + + /* skip separators */ + while (*s == sep) + s++; + if (!*s) + break; + p = s++; + /* next separator */ + while (*s && *s != sep) + s++; + + child = lookup_positive_unlocked(p, dentry, s - p); + dput(dentry); + dentry = child; + } while (!IS_ERR(dentry)); + return dentry; +} + +static const char *path_no_prefix(struct cifs_sb_info *cifs_sb, + const char *path) +{ + size_t len = 0; + + if (!*path) + return path; + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + cifs_sb->prepath) { + len = strlen(cifs_sb->prepath) + 1; + if (unlikely(len > strlen(path))) + return ERR_PTR(-EINVAL); + } + return path + len; +} + +/* + * Open the and cache a directory handle. + * If error then *cfid is not initialized. + */ +int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + const char *path, + struct cifs_sb_info *cifs_sb, + bool lookup_only, struct cached_fid **ret_cfid) +{ + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct cifs_open_parms oparms; + struct smb2_create_rsp *o_rsp = NULL; + struct smb2_query_info_rsp *qi_rsp = NULL; + int resp_buftype[2]; + struct smb_rqst rqst[2]; + struct kvec rsp_iov[2]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + int rc, flags = 0; + __le16 *utf16_path = NULL; + u8 oplock = SMB2_OPLOCK_LEVEL_II; + struct cifs_fid *pfid; + struct dentry *dentry = NULL; + struct cached_fid *cfid; + struct cached_fids *cfids; + const char *npath; + + if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || + is_smb1_server(tcon->ses->server)) + return -EOPNOTSUPP; + + ses = tcon->ses; + server = ses->server; + cfids = tcon->cfids; + + if (!server->ops->new_lease_key) + return -EIO; + + if (cifs_sb->root == NULL) + return -ENOENT; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + cfid = find_or_create_cached_dir(cfids, path, lookup_only); + if (cfid == NULL) { + kfree(utf16_path); + return -ENOENT; + } + /* + * At this point we either have a lease already and we can just + * return it. If not we are guaranteed to be the only thread accessing + * this cfid. + */ + if (cfid->has_lease) { + *ret_cfid = cfid; + kfree(utf16_path); + return 0; + } + + /* + * Skip any prefix paths in @path as lookup_positive_unlocked() ends up + * calling ->lookup() which already adds those through + * build_path_from_dentry(). Also, do it earlier as we might reconnect + * below when trying to send compounded request and then potentially + * having a different prefix path (e.g. after DFS failover). + */ + npath = path_no_prefix(cifs_sb, path); + if (IS_ERR(npath)) { + rc = PTR_ERR(npath); + kfree(utf16_path); + return rc; + } + + /* + * We do not hold the lock for the open because in case + * SMB2_open needs to reconnect. + * This is safe because no other thread will be able to get a ref + * to the cfid until we have finished opening the file and (possibly) + * acquired a lease. + */ + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + pfid = &cfid->fid; + server->ops->new_lease_key(pfid); + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE), + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .fid = pfid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto oshr_free; + smb2_set_next_command(tcon, &rqst[0]); + + memset(&qi_iov, 0, sizeof(qi_iov)); + rqst[1].rq_iov = qi_iov; + rqst[1].rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, FILE_ALL_INFORMATION, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2, 0, NULL); + if (rc) + goto oshr_free; + + smb2_set_related(&rqst[1]); + + rc = compound_send_recv(xid, ses, server, + flags, 2, rqst, + resp_buftype, rsp_iov); + if (rc) { + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + pr_warn_once("server share %s deleted\n", + tcon->tree_name); + } + goto oshr_free; + } + cfid->tcon = tcon; + cfid->is_open = true; + + o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; + oparms.fid->persistent_fid = o_rsp->PersistentFileId; + oparms.fid->volatile_fid = o_rsp->VolatileFileId; +#ifdef CONFIG_CIFS_DEBUG2 + oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); +#endif /* CIFS_DEBUG2 */ + + if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) + goto oshr_free; + + smb2_parse_contexts(server, o_rsp, + &oparms.fid->epoch, + oparms.fid->lease_key, &oplock, + NULL, NULL); + if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) + goto oshr_free; + qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) + goto oshr_free; + if (!smb2_validate_and_copy_iov( + le16_to_cpu(qi_rsp->OutputBufferOffset), + sizeof(struct smb2_file_all_info), + &rsp_iov[1], sizeof(struct smb2_file_all_info), + (char *)&cfid->file_all_info)) + cfid->file_all_info_is_valid = true; + + if (!npath[0]) + dentry = dget(cifs_sb->root); + else { + dentry = path_to_dentry(cifs_sb, npath); + if (IS_ERR(dentry)) { + rc = -ENOENT; + goto oshr_free; + } + } + cfid->dentry = dentry; + cfid->time = jiffies; + cfid->has_lease = true; + +oshr_free: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_query_info_free(&rqst[1]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + spin_lock(&cfids->cfid_list_lock); + if (rc && !cfid->has_lease) { + if (cfid->on_list) { + list_del(&cfid->entry); + cfid->on_list = false; + cfids->num_entries--; + } + rc = -ENOENT; + } + spin_unlock(&cfids->cfid_list_lock); + if (!rc && !cfid->has_lease) { + /* + * We are guaranteed to have two references at this point. + * One for the caller and one for a potential lease. + * Release the Lease-ref so that the directory will be closed + * when the caller closes the cached handle. + */ + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + if (rc) { + if (cfid->is_open) + SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, + cfid->fid.volatile_fid); + free_cached_dir(cfid); + cfid = NULL; + } + + if (rc == 0) { + *ret_cfid = cfid; + atomic_inc(&tcon->num_remote_opens); + } + + return rc; +} + +int open_cached_dir_by_dentry(struct cifs_tcon *tcon, + struct dentry *dentry, + struct cached_fid **ret_cfid) +{ + struct cached_fid *cfid; + struct cached_fids *cfids = tcon->cfids; + + if (cfids == NULL) + return -ENOENT; + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry(cfid, &cfids->entries, entry) { + if (dentry && cfid->dentry == dentry) { + cifs_dbg(FYI, "found a cached root file handle by dentry\n"); + kref_get(&cfid->refcount); + *ret_cfid = cfid; + spin_unlock(&cfids->cfid_list_lock); + return 0; + } + } + spin_unlock(&cfids->cfid_list_lock); + return -ENOENT; +} + +static void +smb2_close_cached_fid(struct kref *ref) +{ + struct cached_fid *cfid = container_of(ref, struct cached_fid, + refcount); + + spin_lock(&cfid->cfids->cfid_list_lock); + if (cfid->on_list) { + list_del(&cfid->entry); + cfid->on_list = false; + cfid->cfids->num_entries--; + } + spin_unlock(&cfid->cfids->cfid_list_lock); + + dput(cfid->dentry); + cfid->dentry = NULL; + + if (cfid->is_open) { + SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, + cfid->fid.volatile_fid); + atomic_dec(&cfid->tcon->num_remote_opens); + } + + free_cached_dir(cfid); +} + +void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb) +{ + struct cached_fid *cfid = NULL; + int rc; + + rc = open_cached_dir(xid, tcon, name, cifs_sb, true, &cfid); + if (rc) { + cifs_dbg(FYI, "no cached dir found for rmdir(%s)\n", name); + return; + } + spin_lock(&cfid->cfids->cfid_list_lock); + if (cfid->has_lease) { + cfid->has_lease = false; + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + spin_unlock(&cfid->cfids->cfid_list_lock); + close_cached_dir(cfid); +} + + +void close_cached_dir(struct cached_fid *cfid) +{ + kref_put(&cfid->refcount, smb2_close_cached_fid); +} + +/* + * Called from cifs_kill_sb when we unmount a share + */ +void close_all_cached_dirs(struct cifs_sb_info *cifs_sb) +{ + struct rb_root *root = &cifs_sb->tlink_tree; + struct rb_node *node; + struct cached_fid *cfid; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cached_fids *cfids; + + for (node = rb_first(root); node; node = rb_next(node)) { + tlink = rb_entry(node, struct tcon_link, tl_rbnode); + tcon = tlink_tcon(tlink); + if (IS_ERR(tcon)) + continue; + cfids = tcon->cfids; + if (cfids == NULL) + continue; + list_for_each_entry(cfid, &cfids->entries, entry) { + dput(cfid->dentry); + cfid->dentry = NULL; + } + } +} + +/* + * Invalidate all cached dirs when a TCON has been reset + * due to a session loss. + */ +void invalidate_all_cached_dirs(struct cifs_tcon *tcon) +{ + struct cached_fids *cfids = tcon->cfids; + struct cached_fid *cfid, *q; + LIST_HEAD(entry); + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { + list_move(&cfid->entry, &entry); + cfids->num_entries--; + cfid->is_open = false; + cfid->on_list = false; + /* To prevent race with smb2_cached_lease_break() */ + kref_get(&cfid->refcount); + } + spin_unlock(&cfids->cfid_list_lock); + + list_for_each_entry_safe(cfid, q, &entry, entry) { + list_del(&cfid->entry); + cancel_work_sync(&cfid->lease_break); + if (cfid->has_lease) { + /* + * We lease was never cancelled from the server so we + * need to drop the reference. + */ + spin_lock(&cfids->cfid_list_lock); + cfid->has_lease = false; + spin_unlock(&cfids->cfid_list_lock); + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + /* Drop the extra reference opened above*/ + kref_put(&cfid->refcount, smb2_close_cached_fid); + } +} + +static void +smb2_cached_lease_break(struct work_struct *work) +{ + struct cached_fid *cfid = container_of(work, + struct cached_fid, lease_break); + + spin_lock(&cfid->cfids->cfid_list_lock); + cfid->has_lease = false; + spin_unlock(&cfid->cfids->cfid_list_lock); + kref_put(&cfid->refcount, smb2_close_cached_fid); +} + +int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) +{ + struct cached_fids *cfids = tcon->cfids; + struct cached_fid *cfid; + + if (cfids == NULL) + return false; + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry(cfid, &cfids->entries, entry) { + if (cfid->has_lease && + !memcmp(lease_key, + cfid->fid.lease_key, + SMB2_LEASE_KEY_SIZE)) { + cfid->time = 0; + /* + * We found a lease remove it from the list + * so no threads can access it. + */ + list_del(&cfid->entry); + cfid->on_list = false; + cfids->num_entries--; + + queue_work(cifsiod_wq, + &cfid->lease_break); + spin_unlock(&cfids->cfid_list_lock); + return true; + } + } + spin_unlock(&cfids->cfid_list_lock); + return false; +} + +static struct cached_fid *init_cached_dir(const char *path) +{ + struct cached_fid *cfid; + + cfid = kzalloc(sizeof(*cfid), GFP_ATOMIC); + if (!cfid) + return NULL; + cfid->path = kstrdup(path, GFP_ATOMIC); + if (!cfid->path) { + kfree(cfid); + return NULL; + } + + INIT_WORK(&cfid->lease_break, smb2_cached_lease_break); + INIT_LIST_HEAD(&cfid->entry); + INIT_LIST_HEAD(&cfid->dirents.entries); + mutex_init(&cfid->dirents.de_mutex); + spin_lock_init(&cfid->fid_lock); + kref_init(&cfid->refcount); + return cfid; +} + +static void free_cached_dir(struct cached_fid *cfid) +{ + struct cached_dirent *dirent, *q; + + dput(cfid->dentry); + cfid->dentry = NULL; + + /* + * Delete all cached dirent names + */ + list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) { + list_del(&dirent->entry); + kfree(dirent->name); + kfree(dirent); + } + + kfree(cfid->path); + cfid->path = NULL; + kfree(cfid); +} + +struct cached_fids *init_cached_dirs(void) +{ + struct cached_fids *cfids; + + cfids = kzalloc(sizeof(*cfids), GFP_KERNEL); + if (!cfids) + return NULL; + spin_lock_init(&cfids->cfid_list_lock); + INIT_LIST_HEAD(&cfids->entries); + return cfids; +} + +/* + * Called from tconInfoFree when we are tearing down the tcon. + * There are no active users or open files/directories at this point. + */ +void free_cached_dirs(struct cached_fids *cfids) +{ + struct cached_fid *cfid, *q; + LIST_HEAD(entry); + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { + cfid->on_list = false; + cfid->is_open = false; + list_move(&cfid->entry, &entry); + } + spin_unlock(&cfids->cfid_list_lock); + + list_for_each_entry_safe(cfid, q, &entry, entry) { + list_del(&cfid->entry); + free_cached_dir(cfid); + } + + kfree(cfids); +} diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h new file mode 100644 index 000000000000..2f4e764c9ca9 --- /dev/null +++ b/fs/smb/client/cached_dir.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Functions to handle the cached directory entries + * + * Copyright (c) 2022, Ronnie Sahlberg + */ + +#ifndef _CACHED_DIR_H +#define _CACHED_DIR_H + + +struct cached_dirent { + struct list_head entry; + char *name; + int namelen; + loff_t pos; + + struct cifs_fattr fattr; +}; + +struct cached_dirents { + bool is_valid:1; + bool is_failed:1; + struct dir_context *ctx; /* + * Only used to make sure we only take entries + * from a single context. Never dereferenced. + */ + struct mutex de_mutex; + int pos; /* Expected ctx->pos */ + struct list_head entries; +}; + +struct cached_fid { + struct list_head entry; + struct cached_fids *cfids; + const char *path; + bool has_lease:1; + bool is_open:1; + bool on_list:1; + bool file_all_info_is_valid:1; + unsigned long time; /* jiffies of when lease was taken */ + struct kref refcount; + struct cifs_fid fid; + spinlock_t fid_lock; + struct cifs_tcon *tcon; + struct dentry *dentry; + struct work_struct lease_break; + struct smb2_file_all_info file_all_info; + struct cached_dirents dirents; +}; + +#define MAX_CACHED_FIDS 16 +struct cached_fids { + /* Must be held when: + * - accessing the cfids->entries list + */ + spinlock_t cfid_list_lock; + int num_entries; + struct list_head entries; +}; + +extern struct cached_fids *init_cached_dirs(void); +extern void free_cached_dirs(struct cached_fids *cfids); +extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + const char *path, + struct cifs_sb_info *cifs_sb, + bool lookup_only, struct cached_fid **cfid); +extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon, + struct dentry *dentry, + struct cached_fid **cfid); +extern void close_cached_dir(struct cached_fid *cfid); +extern void drop_cached_dir_by_name(const unsigned int xid, + struct cifs_tcon *tcon, + const char *name, + struct cifs_sb_info *cifs_sb); +extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); +extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon); +extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); + +#endif /* _CACHED_DIR_H */ diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c new file mode 100644 index 000000000000..e41154ad96af --- /dev/null +++ b/fs/smb/client/cifs_debug.c @@ -0,0 +1,1067 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (C) International Business Machines Corp., 2000,2005 + * + * Modified by Steve French (sfrench@us.ibm.com) + */ +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifsfs.h" +#include "fs_context.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#ifdef CONFIG_CIFS_SMB_DIRECT +#include "smbdirect.h" +#endif +#include "cifs_swn.h" + +void +cifs_dump_mem(char *label, void *data, int length) +{ + pr_debug("%s: dump of %d bytes of data at 0x%p\n", label, length, data); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4, + data, length, true); +} + +void cifs_dump_detail(void *buf, struct TCP_Server_Info *server) +{ +#ifdef CONFIG_CIFS_DEBUG2 + struct smb_hdr *smb = buf; + + cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n", + smb->Command, smb->Status.CifsError, + smb->Flags, smb->Flags2, smb->Mid, smb->Pid); + cifs_dbg(VFS, "smb buf %p len %u\n", smb, + server->ops->calc_smb_size(smb)); +#endif /* CONFIG_CIFS_DEBUG2 */ +} + +void cifs_dump_mids(struct TCP_Server_Info *server) +{ +#ifdef CONFIG_CIFS_DEBUG2 + struct mid_q_entry *mid_entry; + + if (server == NULL) + return; + + cifs_dbg(VFS, "Dump pending requests:\n"); + spin_lock(&server->mid_lock); + list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { + cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n", + mid_entry->mid_state, + le16_to_cpu(mid_entry->command), + mid_entry->pid, + mid_entry->callback_data, + mid_entry->mid); +#ifdef CONFIG_CIFS_STATS2 + cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n", + mid_entry->large_buf, + mid_entry->resp_buf, + mid_entry->when_received, + jiffies); +#endif /* STATS2 */ + cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n", + mid_entry->multiRsp, mid_entry->multiEnd); + if (mid_entry->resp_buf) { + cifs_dump_detail(mid_entry->resp_buf, server); + cifs_dump_mem("existing buf: ", + mid_entry->resp_buf, 62); + } + } + spin_unlock(&server->mid_lock); +#endif /* CONFIG_CIFS_DEBUG2 */ +} + +#ifdef CONFIG_PROC_FS +static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon) +{ + __u32 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); + + seq_printf(m, "%s Mounts: %d ", tcon->tree_name, tcon->tc_count); + if (tcon->nativeFileSystem) + seq_printf(m, "Type: %s ", tcon->nativeFileSystem); + seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x\n\tPathComponentMax: %d Status: %d", + le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), + le32_to_cpu(tcon->fsAttrInfo.Attributes), + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), + tcon->status); + if (dev_type == FILE_DEVICE_DISK) + seq_puts(m, " type: DISK "); + else if (dev_type == FILE_DEVICE_CD_ROM) + seq_puts(m, " type: CDROM "); + else + seq_printf(m, " type: %d ", dev_type); + + seq_printf(m, "Serial Number: 0x%x", tcon->vol_serial_number); + + if ((tcon->seal) || + (tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || + (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) + seq_printf(m, " Encrypted"); + if (tcon->nocase) + seq_printf(m, " nocase"); + if (tcon->unix_ext) + seq_printf(m, " POSIX Extensions"); + if (tcon->ses->server->ops->dump_share_caps) + tcon->ses->server->ops->dump_share_caps(m, tcon); + if (tcon->use_witness) + seq_puts(m, " Witness"); + if (tcon->broken_sparse_sup) + seq_puts(m, " nosparse"); + if (tcon->need_reconnect) + seq_puts(m, "\tDISCONNECTED "); + seq_putc(m, '\n'); +} + +static void +cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) +{ + struct TCP_Server_Info *server = chan->server; + + seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx" + "\n\t\tNumber of credits: %d Dialect 0x%x" + "\n\t\tTCP status: %d Instance: %d" + "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d" + "\n\t\tIn Send: %d In MaxReq Wait: %d", + i+1, server->conn_id, + server->credits, + server->dialect, + server->tcpStatus, + server->reconnect_instance, + server->srv_count, + server->sec_mode, + in_flight(server), + atomic_read(&server->in_send), + atomic_read(&server->num_waiters)); +} + +static void +cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) +{ + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; + + seq_printf(m, "\tSpeed: %zu bps\n", iface->speed); + seq_puts(m, "\t\tCapabilities: "); + if (iface->rdma_capable) + seq_puts(m, "rdma "); + if (iface->rss_capable) + seq_puts(m, "rss "); + seq_putc(m, '\n'); + if (iface->sockaddr.ss_family == AF_INET) + seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr); + else if (iface->sockaddr.ss_family == AF_INET6) + seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr); + if (!iface->is_active) + seq_puts(m, "\t\t[for-cleanup]\n"); +} + +static int cifs_debug_files_proc_show(struct seq_file *m, void *v) +{ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + + seq_puts(m, "# Version:1\n"); + seq_puts(m, "# Format:\n"); + seq_puts(m, "# "); +#ifdef CONFIG_CIFS_DEBUG2 + seq_printf(m, " \n"); +#else + seq_printf(m, " \n"); +#endif /* CIFS_DEBUG2 */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + seq_printf(m, + "0x%x 0x%llx 0x%llx 0x%x %d %d %d %pd", + tcon->tid, + ses->Suid, + cfile->fid.persistent_fid, + cfile->f_flags, + cfile->count, + cfile->pid, + from_kuid(&init_user_ns, cfile->uid), + cfile->dentry); +#ifdef CONFIG_CIFS_DEBUG2 + seq_printf(m, " %llu\n", cfile->fid.mid); +#else + seq_printf(m, "\n"); +#endif /* CIFS_DEBUG2 */ + } + spin_unlock(&tcon->open_file_lock); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + seq_putc(m, '\n'); + return 0; +} + +static int cifs_debug_data_proc_show(struct seq_file *m, void *v) +{ + struct mid_q_entry *mid_entry; + struct TCP_Server_Info *server; + struct TCP_Server_Info *chan_server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifs_server_iface *iface; + int c, i, j; + + seq_puts(m, + "Display Internal CIFS Data Structures for Debugging\n" + "---------------------------------------------------\n"); + seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); + seq_printf(m, "Features:"); +#ifdef CONFIG_CIFS_DFS_UPCALL + seq_printf(m, " DFS"); +#endif +#ifdef CONFIG_CIFS_FSCACHE + seq_printf(m, ",FSCACHE"); +#endif +#ifdef CONFIG_CIFS_SMB_DIRECT + seq_printf(m, ",SMB_DIRECT"); +#endif +#ifdef CONFIG_CIFS_STATS2 + seq_printf(m, ",STATS2"); +#else + seq_printf(m, ",STATS"); +#endif +#ifdef CONFIG_CIFS_DEBUG2 + seq_printf(m, ",DEBUG2"); +#elif defined(CONFIG_CIFS_DEBUG) + seq_printf(m, ",DEBUG"); +#endif +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + seq_printf(m, ",ALLOW_INSECURE_LEGACY"); +#endif +#ifdef CONFIG_CIFS_POSIX + seq_printf(m, ",CIFS_POSIX"); +#endif +#ifdef CONFIG_CIFS_UPCALL + seq_printf(m, ",UPCALL(SPNEGO)"); +#endif +#ifdef CONFIG_CIFS_XATTR + seq_printf(m, ",XATTR"); +#endif + seq_printf(m, ",ACL"); +#ifdef CONFIG_CIFS_SWN_UPCALL + seq_puts(m, ",WITNESS"); +#endif + seq_putc(m, '\n'); + seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize); + seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); + + seq_printf(m, "\nServers: "); + + c = 0; + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + /* channel info will be printed as a part of sessions below */ + if (CIFS_SERVER_IS_CHAN(server)) + continue; + + c++; + seq_printf(m, "\n%d) ConnectionId: 0x%llx ", + c, server->conn_id); + + spin_lock(&server->srv_lock); + if (server->hostname) + seq_printf(m, "Hostname: %s ", server->hostname); + spin_unlock(&server->srv_lock); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (!server->rdma) + goto skip_rdma; + + if (!server->smbd_conn) { + seq_printf(m, "\nSMBDirect transport not available"); + goto skip_rdma; + } + + seq_printf(m, "\nSMBDirect (in hex) protocol version: %x " + "transport status: %x", + server->smbd_conn->protocol, + server->smbd_conn->transport_status); + seq_printf(m, "\nConn receive_credit_max: %x " + "send_credit_target: %x max_send_size: %x", + server->smbd_conn->receive_credit_max, + server->smbd_conn->send_credit_target, + server->smbd_conn->max_send_size); + seq_printf(m, "\nConn max_fragmented_recv_size: %x " + "max_fragmented_send_size: %x max_receive_size:%x", + server->smbd_conn->max_fragmented_recv_size, + server->smbd_conn->max_fragmented_send_size, + server->smbd_conn->max_receive_size); + seq_printf(m, "\nConn keep_alive_interval: %x " + "max_readwrite_size: %x rdma_readwrite_threshold: %x", + server->smbd_conn->keep_alive_interval, + server->smbd_conn->max_readwrite_size, + server->smbd_conn->rdma_readwrite_threshold); + seq_printf(m, "\nDebug count_get_receive_buffer: %x " + "count_put_receive_buffer: %x count_send_empty: %x", + server->smbd_conn->count_get_receive_buffer, + server->smbd_conn->count_put_receive_buffer, + server->smbd_conn->count_send_empty); + seq_printf(m, "\nRead Queue count_reassembly_queue: %x " + "count_enqueue_reassembly_queue: %x " + "count_dequeue_reassembly_queue: %x " + "fragment_reassembly_remaining: %x " + "reassembly_data_length: %x " + "reassembly_queue_length: %x", + server->smbd_conn->count_reassembly_queue, + server->smbd_conn->count_enqueue_reassembly_queue, + server->smbd_conn->count_dequeue_reassembly_queue, + server->smbd_conn->fragment_reassembly_remaining, + server->smbd_conn->reassembly_data_length, + server->smbd_conn->reassembly_queue_length); + seq_printf(m, "\nCurrent Credits send_credits: %x " + "receive_credits: %x receive_credit_target: %x", + atomic_read(&server->smbd_conn->send_credits), + atomic_read(&server->smbd_conn->receive_credits), + server->smbd_conn->receive_credit_target); + seq_printf(m, "\nPending send_pending: %x ", + atomic_read(&server->smbd_conn->send_pending)); + seq_printf(m, "\nReceive buffers count_receive_queue: %x " + "count_empty_packet_queue: %x", + server->smbd_conn->count_receive_queue, + server->smbd_conn->count_empty_packet_queue); + seq_printf(m, "\nMR responder_resources: %x " + "max_frmr_depth: %x mr_type: %x", + server->smbd_conn->responder_resources, + server->smbd_conn->max_frmr_depth, + server->smbd_conn->mr_type); + seq_printf(m, "\nMR mr_ready_count: %x mr_used_count: %x", + atomic_read(&server->smbd_conn->mr_ready_count), + atomic_read(&server->smbd_conn->mr_used_count)); +skip_rdma: +#endif + seq_printf(m, "\nNumber of credits: %d Dialect 0x%x", + server->credits, server->dialect); + if (server->compress_algorithm == SMB3_COMPRESS_LZNT1) + seq_printf(m, " COMPRESS_LZNT1"); + else if (server->compress_algorithm == SMB3_COMPRESS_LZ77) + seq_printf(m, " COMPRESS_LZ77"); + else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF) + seq_printf(m, " COMPRESS_LZ77_HUFF"); + if (server->sign) + seq_printf(m, " signed"); + if (server->posix_ext_supported) + seq_printf(m, " posix"); + if (server->nosharesock) + seq_printf(m, " nosharesock"); + + if (server->rdma) + seq_printf(m, "\nRDMA "); + seq_printf(m, "\nTCP status: %d Instance: %d" + "\nLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d", + server->tcpStatus, + server->reconnect_instance, + server->srv_count, + server->sec_mode, in_flight(server)); + + seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d", + atomic_read(&server->in_send), + atomic_read(&server->num_waiters)); + + seq_printf(m, "\n\n\tSessions: "); + i = 0; + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + i++; + if ((ses->serverDomain == NULL) || + (ses->serverOS == NULL) || + (ses->serverNOS == NULL)) { + seq_printf(m, "\n\t%d) Address: %s Uses: %d Capability: 0x%x\tSession Status: %d ", + i, ses->ip_addr, ses->ses_count, + ses->capabilities, ses->ses_status); + if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) + seq_printf(m, "Guest "); + else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) + seq_printf(m, "Anonymous "); + } else { + seq_printf(m, + "\n\t%d) Name: %s Domain: %s Uses: %d OS: %s " + "\n\tNOS: %s\tCapability: 0x%x" + "\n\tSMB session status: %d ", + i, ses->ip_addr, ses->serverDomain, + ses->ses_count, ses->serverOS, ses->serverNOS, + ses->capabilities, ses->ses_status); + } + + seq_printf(m, "\n\tSecurity type: %s ", + get_security_type_str(server->ops->select_sectype(server, ses->sectype))); + + /* dump session id helpful for use with network trace */ + seq_printf(m, " SessionId: 0x%llx", ses->Suid); + if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) + seq_puts(m, " encrypted"); + if (ses->sign) + seq_puts(m, " signed"); + + seq_printf(m, "\n\tUser: %d Cred User: %d", + from_kuid(&init_user_ns, ses->linux_uid), + from_kuid(&init_user_ns, ses->cred_uid)); + + spin_lock(&ses->chan_lock); + if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0)) + seq_puts(m, "\tPrimary channel: DISCONNECTED "); + if (CIFS_CHAN_IN_RECONNECT(ses, 0)) + seq_puts(m, "\t[RECONNECTING] "); + + if (ses->chan_count > 1) { + seq_printf(m, "\n\n\tExtra Channels: %zu ", + ses->chan_count-1); + for (j = 1; j < ses->chan_count; j++) { + cifs_dump_channel(m, j, &ses->chans[j]); + if (CIFS_CHAN_NEEDS_RECONNECT(ses, j)) + seq_puts(m, "\tDISCONNECTED "); + if (CIFS_CHAN_IN_RECONNECT(ses, j)) + seq_puts(m, "\t[RECONNECTING] "); + } + } + spin_unlock(&ses->chan_lock); + + seq_puts(m, "\n\n\tShares: "); + j = 0; + + seq_printf(m, "\n\t%d) IPC: ", j); + if (ses->tcon_ipc) + cifs_debug_tcon(m, ses->tcon_ipc); + else + seq_puts(m, "none\n"); + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + ++j; + seq_printf(m, "\n\t%d) ", j); + cifs_debug_tcon(m, tcon); + } + + spin_lock(&ses->iface_lock); + if (ses->iface_count) + seq_printf(m, "\n\n\tServer interfaces: %zu", + ses->iface_count); + j = 0; + list_for_each_entry(iface, &ses->iface_list, + iface_head) { + seq_printf(m, "\n\t%d)", ++j); + cifs_dump_iface(m, iface); + if (is_ses_using_iface(ses, iface)) + seq_puts(m, "\t\t[CONNECTED]\n"); + } + spin_unlock(&ses->iface_lock); + + seq_puts(m, "\n\n\tMIDs: "); + spin_lock(&ses->chan_lock); + for (j = 0; j < ses->chan_count; j++) { + chan_server = ses->chans[j].server; + if (!chan_server) + continue; + + if (list_empty(&chan_server->pending_mid_q)) + continue; + + seq_printf(m, "\n\tServer ConnectionId: 0x%llx", + chan_server->conn_id); + spin_lock(&chan_server->mid_lock); + list_for_each_entry(mid_entry, &chan_server->pending_mid_q, qhead) { + seq_printf(m, "\n\t\tState: %d com: %d pid: %d cbdata: %p mid %llu", + mid_entry->mid_state, + le16_to_cpu(mid_entry->command), + mid_entry->pid, + mid_entry->callback_data, + mid_entry->mid); + } + spin_unlock(&chan_server->mid_lock); + } + spin_unlock(&ses->chan_lock); + seq_puts(m, "\n--\n"); + } + if (i == 0) + seq_printf(m, "\n\t\t[NONE]"); + } + if (c == 0) + seq_printf(m, "\n\t[NONE]"); + + spin_unlock(&cifs_tcp_ses_lock); + seq_putc(m, '\n'); + cifs_swn_dump(m); + + /* BB add code to dump additional info such as TCP session info now */ + return 0; +} + +static ssize_t cifs_stats_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + bool bv; + int rc; + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + rc = kstrtobool_from_user(buffer, count, &bv); + if (rc == 0) { +#ifdef CONFIG_CIFS_STATS2 + int i; + + atomic_set(&total_buf_alloc_count, 0); + atomic_set(&total_small_buf_alloc_count, 0); +#endif /* CONFIG_CIFS_STATS2 */ + atomic_set(&tcpSesReconnectCount, 0); + atomic_set(&tconInfoReconnectCount, 0); + + spin_lock(&GlobalMid_Lock); + GlobalMaxActiveXid = 0; + GlobalCurrentXid = 0; + spin_unlock(&GlobalMid_Lock); + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + server->max_in_flight = 0; +#ifdef CONFIG_CIFS_STATS2 + for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { + atomic_set(&server->num_cmds[i], 0); + atomic_set(&server->smb2slowcmd[i], 0); + server->time_per_cmd[i] = 0; + server->slowest_cmd[i] = 0; + server->fastest_cmd[0] = 0; + } +#endif /* CONFIG_CIFS_STATS2 */ + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + atomic_set(&tcon->num_smbs_sent, 0); + spin_lock(&tcon->stat_lock); + tcon->bytes_read = 0; + tcon->bytes_written = 0; + spin_unlock(&tcon->stat_lock); + if (server->ops->clear_stats) + server->ops->clear_stats(tcon); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + } else { + return rc; + } + + return count; +} + +static int cifs_stats_proc_show(struct seq_file *m, void *v) +{ + int i; +#ifdef CONFIG_CIFS_STATS2 + int j; +#endif /* STATS2 */ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + seq_printf(m, "Resources in use\nCIFS Session: %d\n", + sesInfoAllocCount.counter); + seq_printf(m, "Share (unique mount targets): %d\n", + tconInfoAllocCount.counter); + seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n", + buf_alloc_count.counter, + cifs_min_rcv + tcpSesAllocCount.counter); + seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n", + small_buf_alloc_count.counter, cifs_min_small); +#ifdef CONFIG_CIFS_STATS2 + seq_printf(m, "Total Large %d Small %d Allocations\n", + atomic_read(&total_buf_alloc_count), + atomic_read(&total_small_buf_alloc_count)); +#endif /* CONFIG_CIFS_STATS2 */ + + seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&mid_count)); + seq_printf(m, + "\n%d session %d share reconnects\n", + tcpSesReconnectCount.counter, tconInfoReconnectCount.counter); + + seq_printf(m, + "Total vfs operations: %d maximum at one time: %d\n", + GlobalCurrentXid, GlobalMaxActiveXid); + + i = 0; + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + seq_printf(m, "\nMax requests in flight: %d", server->max_in_flight); +#ifdef CONFIG_CIFS_STATS2 + seq_puts(m, "\nTotal time spent processing by command. Time "); + seq_printf(m, "units are jiffies (%d per second)\n", HZ); + seq_puts(m, " SMB3 CMD\tNumber\tTotal Time\tFastest\tSlowest\n"); + seq_puts(m, " --------\t------\t----------\t-------\t-------\n"); + for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) + seq_printf(m, " %d\t\t%d\t%llu\t\t%u\t%u\n", j, + atomic_read(&server->num_cmds[j]), + server->time_per_cmd[j], + server->fastest_cmd[j], + server->slowest_cmd[j]); + for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) + if (atomic_read(&server->smb2slowcmd[j])) { + spin_lock(&server->srv_lock); + seq_printf(m, " %d slow responses from %s for command %d\n", + atomic_read(&server->smb2slowcmd[j]), + server->hostname, j); + spin_unlock(&server->srv_lock); + } +#endif /* STATS2 */ + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + i++; + seq_printf(m, "\n%d) %s", i, tcon->tree_name); + if (tcon->need_reconnect) + seq_puts(m, "\tDISCONNECTED "); + seq_printf(m, "\nSMBs: %d", + atomic_read(&tcon->num_smbs_sent)); + if (server->ops->print_stats) + server->ops->print_stats(m, tcon); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + + seq_putc(m, '\n'); + return 0; +} + +static int cifs_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_stats_proc_show, NULL); +} + +static const struct proc_ops cifs_stats_proc_ops = { + .proc_open = cifs_stats_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_stats_proc_write, +}; + +#ifdef CONFIG_CIFS_SMB_DIRECT +#define PROC_FILE_DEFINE(name) \ +static ssize_t name##_write(struct file *file, const char __user *buffer, \ + size_t count, loff_t *ppos) \ +{ \ + int rc; \ + rc = kstrtoint_from_user(buffer, count, 10, & name); \ + if (rc) \ + return rc; \ + return count; \ +} \ +static int name##_proc_show(struct seq_file *m, void *v) \ +{ \ + seq_printf(m, "%d\n", name ); \ + return 0; \ +} \ +static int name##_open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, name##_proc_show, NULL); \ +} \ +\ +static const struct proc_ops cifs_##name##_proc_fops = { \ + .proc_open = name##_open, \ + .proc_read = seq_read, \ + .proc_lseek = seq_lseek, \ + .proc_release = single_release, \ + .proc_write = name##_write, \ +} + +PROC_FILE_DEFINE(rdma_readwrite_threshold); +PROC_FILE_DEFINE(smbd_max_frmr_depth); +PROC_FILE_DEFINE(smbd_keep_alive_interval); +PROC_FILE_DEFINE(smbd_max_receive_size); +PROC_FILE_DEFINE(smbd_max_fragmented_recv_size); +PROC_FILE_DEFINE(smbd_max_send_size); +PROC_FILE_DEFINE(smbd_send_credit_target); +PROC_FILE_DEFINE(smbd_receive_credit_max); +#endif + +static struct proc_dir_entry *proc_fs_cifs; +static const struct proc_ops cifsFYI_proc_ops; +static const struct proc_ops cifs_lookup_cache_proc_ops; +static const struct proc_ops traceSMB_proc_ops; +static const struct proc_ops cifs_security_flags_proc_ops; +static const struct proc_ops cifs_linux_ext_proc_ops; +static const struct proc_ops cifs_mount_params_proc_ops; + +void +cifs_proc_init(void) +{ + proc_fs_cifs = proc_mkdir("fs/cifs", NULL); + if (proc_fs_cifs == NULL) + return; + + proc_create_single("DebugData", 0, proc_fs_cifs, + cifs_debug_data_proc_show); + + proc_create_single("open_files", 0400, proc_fs_cifs, + cifs_debug_files_proc_show); + + proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops); + proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops); + proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops); + proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs, + &cifs_linux_ext_proc_ops); + proc_create("SecurityFlags", 0644, proc_fs_cifs, + &cifs_security_flags_proc_ops); + proc_create("LookupCacheEnabled", 0644, proc_fs_cifs, + &cifs_lookup_cache_proc_ops); + + proc_create("mount_params", 0444, proc_fs_cifs, &cifs_mount_params_proc_ops); + +#ifdef CONFIG_CIFS_DFS_UPCALL + proc_create("dfscache", 0644, proc_fs_cifs, &dfscache_proc_ops); +#endif + +#ifdef CONFIG_CIFS_SMB_DIRECT + proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs, + &cifs_rdma_readwrite_threshold_proc_fops); + proc_create("smbd_max_frmr_depth", 0644, proc_fs_cifs, + &cifs_smbd_max_frmr_depth_proc_fops); + proc_create("smbd_keep_alive_interval", 0644, proc_fs_cifs, + &cifs_smbd_keep_alive_interval_proc_fops); + proc_create("smbd_max_receive_size", 0644, proc_fs_cifs, + &cifs_smbd_max_receive_size_proc_fops); + proc_create("smbd_max_fragmented_recv_size", 0644, proc_fs_cifs, + &cifs_smbd_max_fragmented_recv_size_proc_fops); + proc_create("smbd_max_send_size", 0644, proc_fs_cifs, + &cifs_smbd_max_send_size_proc_fops); + proc_create("smbd_send_credit_target", 0644, proc_fs_cifs, + &cifs_smbd_send_credit_target_proc_fops); + proc_create("smbd_receive_credit_max", 0644, proc_fs_cifs, + &cifs_smbd_receive_credit_max_proc_fops); +#endif +} + +void +cifs_proc_clean(void) +{ + if (proc_fs_cifs == NULL) + return; + + remove_proc_entry("DebugData", proc_fs_cifs); + remove_proc_entry("open_files", proc_fs_cifs); + remove_proc_entry("cifsFYI", proc_fs_cifs); + remove_proc_entry("traceSMB", proc_fs_cifs); + remove_proc_entry("Stats", proc_fs_cifs); + remove_proc_entry("SecurityFlags", proc_fs_cifs); + remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); + remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); + remove_proc_entry("mount_params", proc_fs_cifs); + +#ifdef CONFIG_CIFS_DFS_UPCALL + remove_proc_entry("dfscache", proc_fs_cifs); +#endif +#ifdef CONFIG_CIFS_SMB_DIRECT + remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs); + remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs); + remove_proc_entry("smbd_keep_alive_interval", proc_fs_cifs); + remove_proc_entry("smbd_max_receive_size", proc_fs_cifs); + remove_proc_entry("smbd_max_fragmented_recv_size", proc_fs_cifs); + remove_proc_entry("smbd_max_send_size", proc_fs_cifs); + remove_proc_entry("smbd_send_credit_target", proc_fs_cifs); + remove_proc_entry("smbd_receive_credit_max", proc_fs_cifs); +#endif + remove_proc_entry("fs/cifs", NULL); +} + +static int cifsFYI_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", cifsFYI); + return 0; +} + +static int cifsFYI_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifsFYI_proc_show, NULL); +} + +static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char c[2] = { '\0' }; + bool bv; + int rc; + + rc = get_user(c[0], buffer); + if (rc) + return rc; + if (strtobool(c, &bv) == 0) + cifsFYI = bv; + else if ((c[0] > '1') && (c[0] <= '9')) + cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */ + else + return -EINVAL; + + return count; +} + +static const struct proc_ops cifsFYI_proc_ops = { + .proc_open = cifsFYI_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifsFYI_proc_write, +}; + +static int cifs_linux_ext_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", linuxExtEnabled); + return 0; +} + +static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_linux_ext_proc_show, NULL); +} + +static ssize_t cifs_linux_ext_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int rc; + + rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled); + if (rc) + return rc; + + return count; +} + +static const struct proc_ops cifs_linux_ext_proc_ops = { + .proc_open = cifs_linux_ext_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_linux_ext_proc_write, +}; + +static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", lookupCacheEnabled); + return 0; +} + +static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_lookup_cache_proc_show, NULL); +} + +static ssize_t cifs_lookup_cache_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int rc; + + rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled); + if (rc) + return rc; + + return count; +} + +static const struct proc_ops cifs_lookup_cache_proc_ops = { + .proc_open = cifs_lookup_cache_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_lookup_cache_proc_write, +}; + +static int traceSMB_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", traceSMB); + return 0; +} + +static int traceSMB_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, traceSMB_proc_show, NULL); +} + +static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int rc; + + rc = kstrtobool_from_user(buffer, count, &traceSMB); + if (rc) + return rc; + + return count; +} + +static const struct proc_ops traceSMB_proc_ops = { + .proc_open = traceSMB_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = traceSMB_proc_write, +}; + +static int cifs_security_flags_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "0x%x\n", global_secflags); + return 0; +} + +static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_security_flags_proc_show, NULL); +} + +/* + * Ensure that if someone sets a MUST flag, that we disable all other MAY + * flags except for the ones corresponding to the given MUST flag. If there are + * multiple MUST flags, then try to prefer more secure ones. + */ +static void +cifs_security_flags_handle_must_flags(unsigned int *flags) +{ + unsigned int signflags = *flags & CIFSSEC_MUST_SIGN; + + if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) + *flags = CIFSSEC_MUST_KRB5; + else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) + *flags = CIFSSEC_MUST_NTLMSSP; + else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) + *flags = CIFSSEC_MUST_NTLMV2; + + *flags |= signflags; +} + +static ssize_t cifs_security_flags_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int rc; + unsigned int flags; + char flags_string[12]; + bool bv; + + if ((count < 1) || (count > 11)) + return -EINVAL; + + memset(flags_string, 0, 12); + + if (copy_from_user(flags_string, buffer, count)) + return -EFAULT; + + if (count < 3) { + /* single char or single char followed by null */ + if (strtobool(flags_string, &bv) == 0) { + global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF; + return count; + } else if (!isdigit(flags_string[0])) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); + return -EINVAL; + } + } + + /* else we have a number */ + rc = kstrtouint(flags_string, 0, &flags); + if (rc) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); + return rc; + } + + cifs_dbg(FYI, "sec flags 0x%x\n", flags); + + if (flags == 0) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string); + return -EINVAL; + } + + if (flags & ~CIFSSEC_MASK) { + cifs_dbg(VFS, "Unsupported security flags: 0x%x\n", + flags & ~CIFSSEC_MASK); + return -EINVAL; + } + + cifs_security_flags_handle_must_flags(&flags); + + /* flags look ok - update the global security flags for cifs module */ + global_secflags = flags; + if (global_secflags & CIFSSEC_MUST_SIGN) { + /* requiring signing implies signing is allowed */ + global_secflags |= CIFSSEC_MAY_SIGN; + cifs_dbg(FYI, "packet signing now required\n"); + } else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) { + cifs_dbg(FYI, "packet signing disabled\n"); + } + /* BB should we turn on MAY flags for other MUST options? */ + return count; +} + +static const struct proc_ops cifs_security_flags_proc_ops = { + .proc_open = cifs_security_flags_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_security_flags_proc_write, +}; + +/* To make it easier to debug, can help to show mount params */ +static int cifs_mount_params_proc_show(struct seq_file *m, void *v) +{ + const struct fs_parameter_spec *p; + const char *type; + + for (p = smb3_fs_parameters; p->name; p++) { + /* cannot use switch with pointers... */ + if (!p->type) { + if (p->flags == fs_param_neg_with_no) + type = "noflag"; + else + type = "flag"; + } else if (p->type == fs_param_is_bool) + type = "bool"; + else if (p->type == fs_param_is_u32) + type = "u32"; + else if (p->type == fs_param_is_u64) + type = "u64"; + else if (p->type == fs_param_is_string) + type = "string"; + else + type = "unknown"; + + seq_printf(m, "%s:%s\n", p->name, type); + } + + return 0; +} + +static int cifs_mount_params_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_mount_params_proc_show, NULL); +} + +static const struct proc_ops cifs_mount_params_proc_ops = { + .proc_open = cifs_mount_params_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + /* No need for write for now */ + /* .proc_write = cifs_mount_params_proc_write, */ +}; + +#else +inline void cifs_proc_init(void) +{ +} + +inline void cifs_proc_clean(void) +{ +} +#endif /* PROC_FS */ diff --git a/fs/smb/client/cifs_debug.h b/fs/smb/client/cifs_debug.h new file mode 100644 index 000000000000..ce5cfd236fdb --- /dev/null +++ b/fs/smb/client/cifs_debug.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Copyright (c) International Business Machines Corp., 2000,2002 + * Modified by Steve French (sfrench@us.ibm.com) + */ + +#ifndef _H_CIFS_DEBUG +#define _H_CIFS_DEBUG + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) "CIFS: " fmt + +void cifs_dump_mem(char *label, void *data, int length); +void cifs_dump_detail(void *buf, struct TCP_Server_Info *ptcp_info); +void cifs_dump_mids(struct TCP_Server_Info *); +extern bool traceSMB; /* flag which enables the function below */ +void dump_smb(void *, int); +#define CIFS_INFO 0x01 +#define CIFS_RC 0x02 +#define CIFS_TIMER 0x04 + +#define VFS 1 +#define FYI 2 +extern int cifsFYI; +#ifdef CONFIG_CIFS_DEBUG2 +#define NOISY 4 +#else +#define NOISY 0 +#endif +#define ONCE 8 + +/* + * debug ON + * -------- + */ +#ifdef CONFIG_CIFS_DEBUG + + +/* + * When adding tracepoints and debug messages we have various choices. + * Some considerations: + * + * Use cifs_dbg(VFS, ...) for things we always want logged, and the user to see + * cifs_info(...) slightly less important, admin can filter via loglevel > 6 + * cifs_dbg(FYI, ...) minor debugging messages, off by default + * trace_smb3_* ftrace functions are preferred for complex debug messages + * intended for developers or experienced admins, off by default + */ + +/* Information level messages, minor events */ +#define cifs_info_func(ratefunc, fmt, ...) \ + pr_info_ ## ratefunc(fmt, ##__VA_ARGS__) + +#define cifs_info(fmt, ...) \ + cifs_info_func(ratelimited, fmt, ##__VA_ARGS__) + +/* information message: e.g., configuration, major event */ +#define cifs_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: " fmt, \ + __FILE__, ##__VA_ARGS__); \ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("VFS: " fmt, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define cifs_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_dbg_func(once, type, fmt, ##__VA_ARGS__); \ + else \ + cifs_dbg_func(ratelimited, type, fmt, ##__VA_ARGS__); \ +} while (0) + +#define cifs_server_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + spin_lock(&server->srv_lock); \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \ + __FILE__, server->hostname, \ + ##__VA_ARGS__); \ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("VFS: \\\\%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc("\\\\%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ + } \ + spin_unlock(&server->srv_lock); \ +} while (0) + +#define cifs_server_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_server_dbg_func(once, type, fmt, ##__VA_ARGS__); \ + else \ + cifs_server_dbg_func(ratelimited, type, fmt, \ + ##__VA_ARGS__); \ +} while (0) + +#define cifs_tcon_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + const char *tn = ""; \ + if (tcon && tcon->tree_name) \ + tn = tcon->tree_name; \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: %s " fmt, \ + __FILE__, tn, ##__VA_ARGS__); \ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("VFS: %s " fmt, tn, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc("%s " fmt, tn, ##__VA_ARGS__); \ + } \ +} while (0) + +#define cifs_tcon_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_tcon_dbg_func(once, type, fmt, ##__VA_ARGS__); \ + else \ + cifs_tcon_dbg_func(ratelimited, type, fmt, \ + ##__VA_ARGS__); \ +} while (0) + +/* + * debug OFF + * --------- + */ +#else /* _CIFS_DEBUG */ +#define cifs_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) + +#define cifs_server_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug("\\\\%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ +} while (0) + +#define cifs_tcon_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug("%s " fmt, tcon->tree_name, ##__VA_ARGS__); \ +} while (0) + +#define cifs_info(fmt, ...) \ + pr_info(fmt, ##__VA_ARGS__) +#endif + +#endif /* _H_CIFS_DEBUG */ diff --git a/fs/smb/client/cifs_dfs_ref.c b/fs/smb/client/cifs_dfs_ref.c new file mode 100644 index 000000000000..b0864da9ef43 --- /dev/null +++ b/fs/smb/client/cifs_dfs_ref.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Contains the CIFS DFS referral mounting routines used for handling + * traversal via DFS junction point + * + * Copyright (c) 2007 Igor Mammedov + * Copyright (C) International Business Machines Corp., 2008 + * Author(s): Igor Mammedov (niallain@gmail.com) + * Steve French (sfrench@us.ibm.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifsfs.h" +#include "dns_resolve.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "dfs_cache.h" +#include "fs_context.h" + +static LIST_HEAD(cifs_dfs_automount_list); + +static void cifs_dfs_expire_automounts(struct work_struct *work); +static DECLARE_DELAYED_WORK(cifs_dfs_automount_task, + cifs_dfs_expire_automounts); +static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; + +static void cifs_dfs_expire_automounts(struct work_struct *work) +{ + struct list_head *list = &cifs_dfs_automount_list; + + mark_mounts_for_expiry(list); + if (!list_empty(list)) + schedule_delayed_work(&cifs_dfs_automount_task, + cifs_dfs_mountpoint_expiry_timeout); +} + +void cifs_dfs_release_automount_timer(void) +{ + BUG_ON(!list_empty(&cifs_dfs_automount_list)); + cancel_delayed_work_sync(&cifs_dfs_automount_task); +} + +/** + * cifs_build_devname - build a devicename from a UNC and optional prepath + * @nodename: pointer to UNC string + * @prepath: pointer to prefixpath (or NULL if there isn't one) + * + * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer + * big enough to hold the final thing. Copy the UNC from the nodename, and + * concatenate the prepath onto the end of it if there is one. + * + * Returns pointer to the built string, or a ERR_PTR. Caller is responsible + * for freeing the returned string. + */ +static char * +cifs_build_devname(char *nodename, const char *prepath) +{ + size_t pplen; + size_t unclen; + char *dev; + char *pos; + + /* skip over any preceding delimiters */ + nodename += strspn(nodename, "\\"); + if (!*nodename) + return ERR_PTR(-EINVAL); + + /* get length of UNC and set pos to last char */ + unclen = strlen(nodename); + pos = nodename + unclen - 1; + + /* trim off any trailing delimiters */ + while (*pos == '\\') { + --pos; + --unclen; + } + + /* allocate a buffer: + * +2 for preceding "//" + * +1 for delimiter between UNC and prepath + * +1 for trailing NULL + */ + pplen = prepath ? strlen(prepath) : 0; + dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + pos = dev; + /* add the initial "//" */ + *pos = '/'; + ++pos; + *pos = '/'; + ++pos; + + /* copy in the UNC portion from referral */ + memcpy(pos, nodename, unclen); + pos += unclen; + + /* copy the prefixpath remainder (if there is one) */ + if (pplen) { + *pos = '/'; + ++pos; + memcpy(pos, prepath, pplen); + pos += pplen; + } + + /* NULL terminator */ + *pos = '\0'; + + convert_delimiter(dev, '/'); + return dev; +} + + +/** + * cifs_compose_mount_options - creates mount options for referral + * @sb_mountdata: parent/root DFS mount options (template) + * @fullpath: full path in UNC format + * @ref: optional server's referral + * @devname: return the built cifs device name if passed pointer not NULL + * creates mount options for submount based on template options sb_mountdata + * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. + * + * Returns: pointer to new mount options or ERR_PTR. + * Caller is responsible for freeing returned value if it is not error. + */ +char *cifs_compose_mount_options(const char *sb_mountdata, + const char *fullpath, + const struct dfs_info3_param *ref, + char **devname) +{ + int rc; + char *name; + char *mountdata = NULL; + const char *prepath = NULL; + int md_len; + char *tkn_e; + char *srvIP = NULL; + char sep = ','; + int off, noff; + + if (sb_mountdata == NULL) + return ERR_PTR(-EINVAL); + + if (ref) { + if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) + return ERR_PTR(-EINVAL); + + if (strlen(fullpath) - ref->path_consumed) { + prepath = fullpath + ref->path_consumed; + /* skip initial delimiter */ + if (*prepath == '/' || *prepath == '\\') + prepath++; + } + + name = cifs_build_devname(ref->node_name, prepath); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + name = NULL; + goto compose_mount_options_err; + } + } else { + name = cifs_build_devname((char *)fullpath, NULL); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + name = NULL; + goto compose_mount_options_err; + } + } + + rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL); + if (rc < 0) { + cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", + __func__, name, rc); + goto compose_mount_options_err; + } + + /* + * In most cases, we'll be building a shorter string than the original, + * but we do have to assume that the address in the ip= option may be + * much longer than the original. Add the max length of an address + * string to the length of the original string to allow for worst case. + */ + md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN; + mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL); + if (mountdata == NULL) { + rc = -ENOMEM; + goto compose_mount_options_err; + } + + /* copy all options except of unc,ip,prefixpath */ + off = 0; + if (strncmp(sb_mountdata, "sep=", 4) == 0) { + sep = sb_mountdata[4]; + strncpy(mountdata, sb_mountdata, 5); + off += 5; + } + + do { + tkn_e = strchr(sb_mountdata + off, sep); + if (tkn_e == NULL) + noff = strlen(sb_mountdata + off); + else + noff = tkn_e - (sb_mountdata + off) + 1; + + if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) { + off += noff; + continue; + } + if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) { + off += noff; + continue; + } + if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) { + off += noff; + continue; + } + if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) { + off += noff; + continue; + } + strncat(mountdata, sb_mountdata + off, noff); + off += noff; + } while (tkn_e); + strcat(mountdata, sb_mountdata + off); + mountdata[md_len] = '\0'; + + /* copy new IP and ref share name */ + if (mountdata[strlen(mountdata) - 1] != sep) + strncat(mountdata, &sep, 1); + strcat(mountdata, "ip="); + strcat(mountdata, srvIP); + + if (devname) + *devname = name; + else + kfree(name); + + /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ + /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ + +compose_mount_options_out: + kfree(srvIP); + return mountdata; + +compose_mount_options_err: + kfree(mountdata); + mountdata = ERR_PTR(rc); + kfree(name); + goto compose_mount_options_out; +} + +/** + * cifs_dfs_do_mount - mounts specified path using DFS full path + * + * Always pass down @fullpath to smb3_do_mount() so we can use the root server + * to perform failover in case we failed to connect to the first target in the + * referral. + * + * @mntpt: directory entry for the path we are trying to automount + * @cifs_sb: parent/root superblock + * @fullpath: full path in UNC format + */ +static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, + struct cifs_sb_info *cifs_sb, + const char *fullpath) +{ + struct vfsmount *mnt; + char *mountdata; + char *devname; + + devname = kstrdup(fullpath, GFP_KERNEL); + if (!devname) + return ERR_PTR(-ENOMEM); + + convert_delimiter(devname, '/'); + + /* TODO: change to call fs_context_for_mount(), fill in context directly, call fc_mount */ + + /* See afs_mntpt_do_automount in fs/afs/mntpt.c for an example */ + + /* strip first '\' from fullpath */ + mountdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, + fullpath + 1, NULL, NULL); + if (IS_ERR(mountdata)) { + kfree(devname); + return (struct vfsmount *)mountdata; + } + + mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata); + kfree(mountdata); + kfree(devname); + return mnt; +} + +/* + * Create a vfsmount that we can automount + */ +static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) +{ + struct cifs_sb_info *cifs_sb; + void *page; + char *full_path; + struct vfsmount *mnt; + + cifs_dbg(FYI, "in %s\n", __func__); + BUG_ON(IS_ROOT(mntpt)); + + /* + * The MSDFS spec states that paths in DFS referral requests and + * responses must be prefixed by a single '\' character instead of + * the double backslashes usually used in the UNC. This function + * gives us the latter, so we must adjust the result. + */ + cifs_sb = CIFS_SB(mntpt->d_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { + mnt = ERR_PTR(-EREMOTE); + goto cdda_exit; + } + + page = alloc_dentry_path(); + /* always use tree name prefix */ + full_path = build_path_from_dentry_optional_prefix(mntpt, page, true); + if (IS_ERR(full_path)) { + mnt = ERR_CAST(full_path); + goto free_full_path; + } + + convert_delimiter(full_path, '\\'); + cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); + + mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); + cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt); + +free_full_path: + free_dentry_path(page); +cdda_exit: + cifs_dbg(FYI, "leaving %s\n" , __func__); + return mnt; +} + +/* + * Attempt to automount the referral + */ +struct vfsmount *cifs_dfs_d_automount(struct path *path) +{ + struct vfsmount *newmnt; + + cifs_dbg(FYI, "in %s\n", __func__); + + newmnt = cifs_dfs_do_automount(path->dentry); + if (IS_ERR(newmnt)) { + cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); + return newmnt; + } + + mntget(newmnt); /* prevent immediate expiration */ + mnt_set_expiry(newmnt, &cifs_dfs_automount_list); + schedule_delayed_work(&cifs_dfs_automount_task, + cifs_dfs_mountpoint_expiry_timeout); + cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); + return newmnt; +} + +const struct inode_operations cifs_dfs_referral_inode_operations = { +}; diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h new file mode 100644 index 000000000000..013a4bd65280 --- /dev/null +++ b/fs/smb/client/cifs_fs_sb.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include + +#ifndef _CIFS_FS_SB_H +#define _CIFS_FS_SB_H + +#include + +#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ +#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */ +#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */ +#define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */ +#define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */ +#define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ +#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible*/ +#define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */ +#define CIFS_MOUNT_NO_BRL 0x100 /* No sending byte range locks to srv */ +#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */ +#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */ +#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */ +#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */ +#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ +#define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ +#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ +#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ +#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ +#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */ +#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */ +#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of SB_POSIXACL in mnt_cifs_flags */ +#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ +#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */ +#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */ +#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible + * root mountable + */ +#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */ +#define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ +#define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ +#define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ +#define CIFS_MOUNT_RO_CACHE 0x20000000 /* assumes share will not change */ +#define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */ +#define CIFS_MOUNT_SHUTDOWN 0x80000000 + +struct cifs_sb_info { + struct rb_root tlink_tree; + spinlock_t tlink_tree_lock; + struct tcon_link *master_tlink; + struct nls_table *local_nls; + struct smb3_fs_context *ctx; + atomic_t active; + unsigned int mnt_cifs_flags; + struct delayed_work prune_tlinks; + struct rcu_head rcu; + + /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ + char *prepath; + + /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */ + uuid_t dfs_mount_id; + /* + * Indicate whether serverino option was turned off later + * (cifs_autodisable_serverino) in order to match new mounts. + */ + bool mnt_cifs_serverino_autodisabled; + /* + * Available once the mount has completed. + */ + struct dentry *root; +}; +#endif /* _CIFS_FS_SB_H */ diff --git a/fs/smb/client/cifs_ioctl.h b/fs/smb/client/cifs_ioctl.h new file mode 100644 index 000000000000..d86d78d5bfdc --- /dev/null +++ b/fs/smb/client/cifs_ioctl.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Structure definitions for io control for cifs/smb3 + * + * Copyright (c) 2015 Steve French + * + */ + +struct smb_mnt_fs_info { + __u32 version; /* 0001 */ + __u16 protocol_id; + __u16 tcon_flags; + __u32 vol_serial_number; + __u32 vol_create_time; + __u32 share_caps; + __u32 share_flags; + __u32 sector_flags; + __u32 optimal_sector_size; + __u32 max_bytes_chunk; + __u32 fs_attributes; + __u32 max_path_component; + __u32 device_type; + __u32 device_characteristics; + __u32 maximal_access; + __u64 cifs_posix_caps; +} __packed; + +struct smb_snapshot_array { + __u32 number_of_snapshots; + __u32 number_of_snapshots_returned; + __u32 snapshot_array_size; + /* snapshots[]; */ +} __packed; + +/* query_info flags */ +#define PASSTHRU_QUERY_INFO 0x00000000 +#define PASSTHRU_FSCTL 0x00000001 +#define PASSTHRU_SET_INFO 0x00000002 +struct smb_query_info { + __u32 info_type; + __u32 file_info_class; + __u32 additional_information; + __u32 flags; + __u32 input_buffer_length; + __u32 output_buffer_length; + /* char buffer[]; */ +} __packed; + +/* + * Dumping the commonly used 16 byte (e.g. CCM and GCM128) keys still supported + * for backlevel compatibility, but is not sufficient for dumping the less + * frequently used GCM256 (32 byte) keys (see the newer "CIFS_DUMP_FULL_KEY" + * ioctl for dumping decryption info for GCM256 mounts) + */ +struct smb3_key_debug_info { + __u64 Suid; + __u16 cipher_type; + __u8 auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */ + __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; +} __packed; + +/* + * Dump variable-sized keys + */ +struct smb3_full_key_debug_info { + /* INPUT: size of userspace buffer */ + __u32 in_size; + + /* + * INPUT: 0 for current user, otherwise session to dump + * OUTPUT: session id that was dumped + */ + __u64 session_id; + __u16 cipher_type; + __u8 session_key_length; + __u8 server_in_key_length; + __u8 server_out_key_length; + __u8 data[]; + /* + * return this struct with the keys appended at the end: + * __u8 session_key[session_key_length]; + * __u8 server_in_key[server_in_key_length]; + * __u8 server_out_key[server_out_key_length]; + */ +} __packed; + +struct smb3_notify { + __u32 completion_filter; + bool watch_tree; +} __packed; + +struct smb3_notify_info { + __u32 completion_filter; + bool watch_tree; + __u32 data_len; /* size of notify data below */ + __u8 notify_data[]; +} __packed; + +#define CIFS_IOCTL_MAGIC 0xCF +#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) +#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) +#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) +#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) +#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info) +#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info) +#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify) +#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info) +#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info) +#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32) + +/* + * Flags for going down operation + */ +#define CIFS_GOING_FLAGS_DEFAULT 0x0 /* going down */ +#define CIFS_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ +#define CIFS_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ + +static inline bool cifs_forced_shutdown(struct cifs_sb_info *sbi) +{ + if (CIFS_MOUNT_SHUTDOWN & sbi->mnt_cifs_flags) + return true; + else + return false; +} diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c new file mode 100644 index 000000000000..342717bf1dc2 --- /dev/null +++ b/fs/smb/client/cifs_spnego.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifs_spnego.h" +#include "cifs_debug.h" +#include "cifsproto.h" +static const struct cred *spnego_cred; + +/* create a new cifs key */ +static int +cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + char *payload; + int ret; + + ret = -ENOMEM; + payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); + if (!payload) + goto error; + + /* attach the data */ + key->payload.data[0] = payload; + ret = 0; + +error: + return ret; +} + +static void +cifs_spnego_key_destroy(struct key *key) +{ + kfree(key->payload.data[0]); +} + + +/* + * keytype for CIFS spnego keys + */ +struct key_type cifs_spnego_key_type = { + .name = "cifs.spnego", + .instantiate = cifs_spnego_key_instantiate, + .destroy = cifs_spnego_key_destroy, + .describe = user_describe, +}; + +/* length of longest version string e.g. strlen("ver=0xFF") */ +#define MAX_VER_STR_LEN 8 + +/* length of longest security mechanism name, eg in future could have + * strlen(";sec=ntlmsspi") */ +#define MAX_MECH_STR_LEN 13 + +/* strlen of "host=" */ +#define HOST_KEY_LEN 5 + +/* strlen of ";ip4=" or ";ip6=" */ +#define IP_KEY_LEN 5 + +/* strlen of ";uid=0x" */ +#define UID_KEY_LEN 7 + +/* strlen of ";creduid=0x" */ +#define CREDUID_KEY_LEN 11 + +/* strlen of ";user=" */ +#define USER_KEY_LEN 6 + +/* strlen of ";pid=0x" */ +#define PID_KEY_LEN 7 + +/* get a key struct with a SPNEGO security blob, suitable for session setup */ +struct key * +cifs_get_spnego_key(struct cifs_ses *sesInfo, + struct TCP_Server_Info *server) +{ + struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; + char *description, *dp; + size_t desc_len; + struct key *spnego_key; + const char *hostname = server->hostname; + const struct cred *saved_cred; + + /* length of fields (with semicolons): ver=0xyz ip4=ipaddress + host=hostname sec=mechanism uid=0xFF user=username */ + desc_len = MAX_VER_STR_LEN + + HOST_KEY_LEN + strlen(hostname) + + IP_KEY_LEN + INET6_ADDRSTRLEN + + MAX_MECH_STR_LEN + + UID_KEY_LEN + (sizeof(uid_t) * 2) + + CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + + PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; + + if (sesInfo->user_name) + desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); + + spnego_key = ERR_PTR(-ENOMEM); + description = kzalloc(desc_len, GFP_KERNEL); + if (description == NULL) + goto out; + + dp = description; + /* start with version and hostname portion of UNC string */ + spnego_key = ERR_PTR(-EINVAL); + sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, + hostname); + dp = description + strlen(description); + + /* add the server address */ + if (server->dstaddr.ss_family == AF_INET) + sprintf(dp, "ip4=%pI4", &sa->sin_addr); + else if (server->dstaddr.ss_family == AF_INET6) + sprintf(dp, "ip6=%pI6", &sa6->sin6_addr); + else + goto out; + + dp = description + strlen(description); + + /* for now, only sec=krb5 and sec=mskrb5 are valid */ + if (server->sec_kerberos) + sprintf(dp, ";sec=krb5"); + else if (server->sec_mskerberos) + sprintf(dp, ";sec=mskrb5"); + else { + cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n"); + sprintf(dp, ";sec=krb5"); + } + + dp = description + strlen(description); + sprintf(dp, ";uid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->linux_uid)); + + dp = description + strlen(description); + sprintf(dp, ";creduid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->cred_uid)); + + if (sesInfo->user_name) { + dp = description + strlen(description); + sprintf(dp, ";user=%s", sesInfo->user_name); + } + + dp = description + strlen(description); + sprintf(dp, ";pid=0x%x", current->pid); + + cifs_dbg(FYI, "key description = %s\n", description); + saved_cred = override_creds(spnego_cred); + spnego_key = request_key(&cifs_spnego_key_type, description, ""); + revert_creds(saved_cred); + +#ifdef CONFIG_CIFS_DEBUG2 + if (cifsFYI && !IS_ERR(spnego_key)) { + struct cifs_spnego_msg *msg = spnego_key->payload.data[0]; + cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U, + msg->secblob_len + msg->sesskey_len)); + } +#endif /* CONFIG_CIFS_DEBUG2 */ + +out: + kfree(description); + return spnego_key; +} + +int +init_cifs_spnego(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + cifs_dbg(FYI, "Registering the %s key type\n", + cifs_spnego_key_type.name); + + /* + * Create an override credential set with special thread keyring for + * spnego upcalls. + */ + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = keyring_alloc(".cifs_spnego", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = register_key_type(&cifs_spnego_key_type); + if (ret < 0) + goto failed_put_key; + + /* + * instruct request_key() to use this special keyring as a cache for + * the results it looks up + */ + set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + spnego_cred = cred; + + cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring)); + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +void +exit_cifs_spnego(void) +{ + key_revoke(spnego_cred->thread_keyring); + unregister_key_type(&cifs_spnego_key_type); + put_cred(spnego_cred); + cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name); +} diff --git a/fs/smb/client/cifs_spnego.h b/fs/smb/client/cifs_spnego.h new file mode 100644 index 000000000000..7f102ffeb675 --- /dev/null +++ b/fs/smb/client/cifs_spnego.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFS_SPNEGO_H +#define _CIFS_SPNEGO_H + +#define CIFS_SPNEGO_UPCALL_VERSION 2 + +/* + * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION. + * The flags field is for future use. The request-key callout should set + * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob + * and stuff it in the data field. + */ +struct cifs_spnego_msg { + uint32_t version; + uint32_t flags; + uint32_t sesskey_len; + uint32_t secblob_len; + uint8_t data[1]; +}; + +#ifdef __KERNEL__ +extern struct key_type cifs_spnego_key_type; +extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo, + struct TCP_Server_Info *server); +#endif /* KERNEL */ + +#endif /* _CIFS_SPNEGO_H */ diff --git a/fs/smb/client/cifs_spnego_negtokeninit.asn1 b/fs/smb/client/cifs_spnego_negtokeninit.asn1 new file mode 100644 index 000000000000..181c083887d5 --- /dev/null +++ b/fs/smb/client/cifs_spnego_negtokeninit.asn1 @@ -0,0 +1,40 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({cifs_gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({cifs_neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegHints ::= SEQUENCE { + hintName + [0] GeneralString OPTIONAL, + hintAddress + [1] OCTET STRING OPTIONAL + } + +NegTokenInit2 ::= + SEQUENCE { + mechTypes + [0] MechTypeList OPTIONAL, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL, + negHints + [3] NegHints OPTIONAL, + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit2, + negTokenTarg + [1] ANY + } diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c new file mode 100644 index 000000000000..7233c6a7e6d7 --- /dev/null +++ b/fs/smb/client/cifs_swn.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Witness Service client for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#include +#include +#include + +#include "cifs_swn.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "fscache.h" +#include "cifs_debug.h" +#include "netlink.h" + +static DEFINE_IDR(cifs_swnreg_idr); +static DEFINE_MUTEX(cifs_swnreg_idr_mutex); + +struct cifs_swn_reg { + int id; + struct kref ref_count; + + const char *net_name; + const char *share_name; + bool net_name_notify; + bool share_name_notify; + bool ip_notify; + + struct cifs_tcon *tcon; +}; + +static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) +{ + int ret; + + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH); + if (ret < 0) + return ret; + + return 0; +} + +static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) +{ + int ret; + + if (tcon->ses->user_name != NULL) { + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name); + if (ret < 0) + return ret; + } + + if (tcon->ses->password != NULL) { + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password); + if (ret < 0) + return ret; + } + + if (tcon->ses->domainName != NULL) { + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * Sends a register message to the userspace daemon based on the registration. + * The authentication information to connect to the witness service is bundled + * into the message. + */ +static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) +{ + struct sk_buff *skb; + struct genlmsghdr *hdr; + enum securityEnum authtype; + struct sockaddr_storage *addr; + int ret; + + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb == NULL) { + ret = -ENOMEM; + goto fail; + } + + hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER); + if (hdr == NULL) { + ret = -ENOMEM; + goto nlmsg_fail; + } + + ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); + if (ret < 0) + goto nlmsg_fail; + + /* + * If there is an address stored use it instead of the server address, because we are + * in the process of reconnecting to it after a share has been moved or we have been + * told to switch to it (client move message). In these cases we unregister from the + * server address and register to the new address when we receive the notification. + */ + if (swnreg->tcon->ses->server->use_swn_dstaddr) + addr = &swnreg->tcon->ses->server->swn_dstaddr; + else + addr = &swnreg->tcon->ses->server->dstaddr; + + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); + if (ret < 0) + goto nlmsg_fail; + + if (swnreg->net_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->share_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->ip_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); + switch (authtype) { + case Kerberos: + ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); + goto nlmsg_fail; + } + break; + case NTLMv2: + case RawNTLMSSP: + ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); + goto nlmsg_fail; + } + break; + default: + cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype); + ret = -EINVAL; + goto nlmsg_fail; + } + + genlmsg_end(skb, hdr); + genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); + + cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__, + swnreg->net_name, swnreg->id); + + return 0; + +nlmsg_fail: + genlmsg_cancel(skb, hdr); + nlmsg_free(skb); +fail: + return ret; +} + +/* + * Sends an uregister message to the userspace daemon based on the registration + */ +static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) +{ + struct sk_buff *skb; + struct genlmsghdr *hdr; + int ret; + + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb == NULL) + return -ENOMEM; + + hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER); + if (hdr == NULL) { + ret = -ENOMEM; + goto nlmsg_fail; + } + + ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), + &swnreg->tcon->ses->server->dstaddr); + if (ret < 0) + goto nlmsg_fail; + + if (swnreg->net_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->share_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->ip_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + genlmsg_end(skb, hdr); + genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); + + cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__, + swnreg->net_name, swnreg->id); + + return 0; + +nlmsg_fail: + genlmsg_cancel(skb, hdr); + nlmsg_free(skb); + return ret; +} + +/* + * Try to find a matching registration for the tcon's server name and share name. + * Calls to this function must be protected by cifs_swnreg_idr_mutex. + * TODO Try to avoid memory allocations + */ +static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *swnreg; + int id; + const char *share_name; + const char *net_name; + + net_name = extract_hostname(tcon->tree_name); + if (IS_ERR(net_name)) { + int ret; + + ret = PTR_ERR(net_name); + cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n", + __func__, tcon->tree_name, ret); + return ERR_PTR(-EINVAL); + } + + share_name = extract_sharename(tcon->tree_name); + if (IS_ERR(share_name)) { + int ret; + + ret = PTR_ERR(share_name); + cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n", + __func__, tcon->tree_name, ret); + kfree(net_name); + return ERR_PTR(-EINVAL); + } + + idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + if (strcasecmp(swnreg->net_name, net_name) != 0 + || strcasecmp(swnreg->share_name, share_name) != 0) { + continue; + } + + cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name, + swnreg->share_name); + + kfree(net_name); + kfree(share_name); + + return swnreg; + } + + kfree(net_name); + kfree(share_name); + + return ERR_PTR(-EEXIST); +} + +/* + * Get a registration for the tcon's server and share name, allocating a new one if it does not + * exists + */ +static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *reg = NULL; + int ret; + + mutex_lock(&cifs_swnreg_idr_mutex); + + /* Check if we are already registered for this network and share names */ + reg = cifs_find_swn_reg(tcon); + if (!IS_ERR(reg)) { + kref_get(®->ref_count); + mutex_unlock(&cifs_swnreg_idr_mutex); + return reg; + } else if (PTR_ERR(reg) != -EEXIST) { + mutex_unlock(&cifs_swnreg_idr_mutex); + return reg; + } + + reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC); + if (reg == NULL) { + mutex_unlock(&cifs_swnreg_idr_mutex); + return ERR_PTR(-ENOMEM); + } + + kref_init(®->ref_count); + + reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); + if (reg->id < 0) { + cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); + ret = reg->id; + goto fail; + } + + reg->net_name = extract_hostname(tcon->tree_name); + if (IS_ERR(reg->net_name)) { + ret = PTR_ERR(reg->net_name); + cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret); + goto fail_idr; + } + + reg->share_name = extract_sharename(tcon->tree_name); + if (IS_ERR(reg->share_name)) { + ret = PTR_ERR(reg->share_name); + cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret); + goto fail_net_name; + } + + reg->net_name_notify = true; + reg->share_name_notify = true; + reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); + + reg->tcon = tcon; + + mutex_unlock(&cifs_swnreg_idr_mutex); + + return reg; + +fail_net_name: + kfree(reg->net_name); +fail_idr: + idr_remove(&cifs_swnreg_idr, reg->id); +fail: + kfree(reg); + mutex_unlock(&cifs_swnreg_idr_mutex); + return ERR_PTR(ret); +} + +static void cifs_swn_reg_release(struct kref *ref) +{ + struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); + int ret; + + ret = cifs_swn_send_unregister_message(swnreg); + if (ret < 0) + cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); + + idr_remove(&cifs_swnreg_idr, swnreg->id); + kfree(swnreg->net_name); + kfree(swnreg->share_name); + kfree(swnreg); +} + +static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) +{ + mutex_lock(&cifs_swnreg_idr_mutex); + kref_put(&swnreg->ref_count, cifs_swn_reg_release); + mutex_unlock(&cifs_swnreg_idr_mutex); +} + +static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) +{ + switch (state) { + case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: + cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); + cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + break; + case CIFS_SWN_RESOURCE_STATE_AVAILABLE: + cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); + cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + break; + case CIFS_SWN_RESOURCE_STATE_UNKNOWN: + cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name); + break; + } + return 0; +} + +static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2) +{ + if (addr1->ss_family != addr2->ss_family) + return false; + + if (addr1->ss_family == AF_INET) { + return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr, + &((const struct sockaddr_in *)addr2)->sin_addr, + sizeof(struct in_addr)) == 0); + } + + if (addr1->ss_family == AF_INET6) { + return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr, + &((const struct sockaddr_in6 *)addr2)->sin6_addr, + sizeof(struct in6_addr)) == 0); + } + + return false; +} + +static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new, + const struct sockaddr_storage *old, + struct sockaddr_storage *dst) +{ + __be16 port = cpu_to_be16(CIFS_PORT); + + if (old->ss_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)old; + + port = ipv4->sin_port; + } else if (old->ss_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old; + + port = ipv6->sin6_port; + } + + if (new->ss_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)new; + + ipv4->sin_port = port; + } else if (new->ss_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new; + + ipv6->sin6_port = port; + } + + *dst = *new; + + return 0; +} + +static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr) +{ + int ret = 0; + + /* Store the reconnect address */ + cifs_server_lock(tcon->ses->server); + if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) + goto unlock; + + ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr, + &tcon->ses->server->swn_dstaddr); + if (ret < 0) { + cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret); + goto unlock; + } + tcon->ses->server->use_swn_dstaddr = true; + + /* + * Unregister to stop receiving notifications for the old IP address. + */ + ret = cifs_swn_unregister(tcon); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", + __func__, ret); + goto unlock; + } + + /* + * And register to receive notifications for the new IP address now that we have + * stored the new address. + */ + ret = cifs_swn_register(tcon); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n", + __func__, ret); + goto unlock; + } + + cifs_signal_cifsd_for_reconnect(tcon->ses->server, false); + +unlock: + cifs_server_unlock(tcon->ses->server); + + return ret; +} + +static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) +{ + struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; + + if (addr->ss_family == AF_INET) + cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr); + else if (addr->ss_family == AF_INET6) + cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); + + return cifs_swn_reconnect(swnreg->tcon, addr); +} + +int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) +{ + struct cifs_swn_reg *swnreg; + char name[256]; + int type; + + if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { + int swnreg_id; + + swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); + mutex_lock(&cifs_swnreg_idr_mutex); + swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); + mutex_unlock(&cifs_swnreg_idr_mutex); + if (swnreg == NULL) { + cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); + return -EINVAL; + } + } else { + cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); + return -EINVAL; + } + + if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { + type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); + } else { + cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); + return -EINVAL; + } + + switch (type) { + case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: { + int state; + + if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) { + nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME], + sizeof(name)); + } else { + cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); + return -EINVAL; + } + if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { + state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); + } else { + cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); + return -EINVAL; + } + return cifs_swn_resource_state_changed(swnreg, name, state); + } + case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { + struct sockaddr_storage addr; + + if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) { + nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); + } else { + cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); + return -EINVAL; + } + return cifs_swn_client_move(swnreg, &addr); + } + default: + cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); + break; + } + + return 0; +} + +int cifs_swn_register(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *swnreg; + int ret; + + swnreg = cifs_get_swn_reg(tcon); + if (IS_ERR(swnreg)) + return PTR_ERR(swnreg); + + ret = cifs_swn_send_register_message(swnreg); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); + /* Do not put the swnreg or return error, the echo task will retry */ + } + + return 0; +} + +int cifs_swn_unregister(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *swnreg; + + mutex_lock(&cifs_swnreg_idr_mutex); + + swnreg = cifs_find_swn_reg(tcon); + if (IS_ERR(swnreg)) { + mutex_unlock(&cifs_swnreg_idr_mutex); + return PTR_ERR(swnreg); + } + + mutex_unlock(&cifs_swnreg_idr_mutex); + + cifs_put_swn_reg(swnreg); + + return 0; +} + +void cifs_swn_dump(struct seq_file *m) +{ + struct cifs_swn_reg *swnreg; + struct sockaddr_in *sa; + struct sockaddr_in6 *sa6; + int id; + + seq_puts(m, "Witness registrations:"); + + mutex_lock(&cifs_swnreg_idr_mutex); + idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", + id, kref_read(&swnreg->ref_count), + swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", + swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); + switch (swnreg->tcon->ses->server->dstaddr.ss_family) { + case AF_INET: + sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; + seq_printf(m, "%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: + sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; + seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); + if (sa6->sin6_scope_id) + seq_printf(m, "%%%u", sa6->sin6_scope_id); + break; + default: + seq_puts(m, "(unknown)"); + } + seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); + } + mutex_unlock(&cifs_swnreg_idr_mutex); + seq_puts(m, "\n"); +} + +void cifs_swn_check(void) +{ + struct cifs_swn_reg *swnreg; + int id; + int ret; + + mutex_lock(&cifs_swnreg_idr_mutex); + idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + ret = cifs_swn_send_register_message(swnreg); + if (ret < 0) + cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); + } + mutex_unlock(&cifs_swnreg_idr_mutex); +} diff --git a/fs/smb/client/cifs_swn.h b/fs/smb/client/cifs_swn.h new file mode 100644 index 000000000000..8a9d2a5c9077 --- /dev/null +++ b/fs/smb/client/cifs_swn.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Witness Service client for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#ifndef _CIFS_SWN_H +#define _CIFS_SWN_H +#include "cifsglob.h" + +struct cifs_tcon; +struct sk_buff; +struct genl_info; + +#ifdef CONFIG_CIFS_SWN_UPCALL +extern int cifs_swn_register(struct cifs_tcon *tcon); + +extern int cifs_swn_unregister(struct cifs_tcon *tcon); + +extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info); + +extern void cifs_swn_dump(struct seq_file *m); + +extern void cifs_swn_check(void); + +static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) +{ + if (server->use_swn_dstaddr) { + server->dstaddr = server->swn_dstaddr; + return true; + } + return false; +} + +static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) +{ + server->use_swn_dstaddr = false; +} + +#else + +static inline int cifs_swn_register(struct cifs_tcon *tcon) { return 0; } +static inline int cifs_swn_unregister(struct cifs_tcon *tcon) { return 0; } +static inline int cifs_swn_notify(struct sk_buff *s, struct genl_info *i) { return 0; } +static inline void cifs_swn_dump(struct seq_file *m) {} +static inline void cifs_swn_check(void) {} +static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) { return false; } +static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) {} + +#endif /* CONFIG_CIFS_SWN_UPCALL */ +#endif /* _CIFS_SWN_H */ diff --git a/fs/smb/client/cifs_unicode.c b/fs/smb/client/cifs_unicode.c new file mode 100644 index 000000000000..e7582dd79179 --- /dev/null +++ b/fs/smb/client/cifs_unicode.c @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * Modified by Steve French (sfrench@us.ibm.com) + */ +#include +#include +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "cifs_uniupr.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifs_debug.h" + +int cifs_remap(struct cifs_sb_info *cifs_sb) +{ + int map_type; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) + map_type = SFM_MAP_UNI_RSVD; + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) + map_type = SFU_MAP_UNI_RSVD; + else + map_type = NO_MAP_UNI_RSVD; + + return map_type; +} + +/* Convert character using the SFU - "Services for Unix" remapping range */ +static bool +convert_sfu_char(const __u16 src_char, char *target) +{ + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (src_char) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERISK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + return false; + } + return true; +} + +/* Convert character using the SFM - "Services for Mac" remapping range */ +static bool +convert_sfm_char(const __u16 src_char, char *target) +{ + if (src_char >= 0xF001 && src_char <= 0xF01F) { + *target = src_char - 0xF000; + return true; + } + switch (src_char) { + case SFM_COLON: + *target = ':'; + break; + case SFM_DOUBLEQUOTE: + *target = '"'; + break; + case SFM_ASTERISK: + *target = '*'; + break; + case SFM_QUESTION: + *target = '?'; + break; + case SFM_PIPE: + *target = '|'; + break; + case SFM_GRTRTHAN: + *target = '>'; + break; + case SFM_LESSTHAN: + *target = '<'; + break; + case SFM_SPACE: + *target = ' '; + break; + case SFM_PERIOD: + *target = '.'; + break; + default: + return false; + } + return true; +} + + +/* + * cifs_mapchar - convert a host-endian char to proper char in codepage + * @target - where converted character should be copied + * @src_char - 2 byte host-endian source character + * @cp - codepage to which character should be converted + * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + */ +static int +cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp, + int maptype) +{ + int len = 1; + __u16 src_char; + + src_char = *from; + + if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target)) + return len; + else if ((maptype == SFU_MAP_UNI_RSVD) && + convert_sfu_char(src_char, target)) + return len; + + /* if character not one of seven in special remap set */ + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); + if (len <= 0) + goto surrogate_pair; + + return len; + +surrogate_pair: + /* convert SURROGATE_PAIR and IVS */ + if (strcmp(cp->charset, "utf8")) + goto unknown; + len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6); + if (len <= 0) + goto unknown; + return len; + +unknown: + *target = '?'; + len = 1; + return len; +} + +/* + * cifs_from_utf16 - convert utf16le string to local charset + * @to - destination buffer + * @from - source buffer + * @tolen - destination buffer size (in bytes) + * @fromlen - source buffer size (in bytes) + * @codepage - codepage to which characters should be converted + * @mapchar - should characters be remapped according to the mapchars option? + * + * Convert a little-endian utf16le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UTF16-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + */ +int +cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, int map_type) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */ + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { + ftmp[0] = get_unaligned_le16(&from[i]); + if (ftmp[0] == 0) + break; + if (i + 1 < fromwords) + ftmp[1] = get_unaligned_le16(&from[i + 1]); + else + ftmp[1] = 0; + if (i + 2 < fromwords) + ftmp[2] = get_unaligned_le16(&from[i + 2]); + else + ftmp[2] = 0; + + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, ftmp, codepage, map_type); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type); + outlen += charlen; + + /* charlen (=bytes of UTF-8 for 1 character) + * 4bytes UTF-8(surrogate pair) is charlen=4 + * (4bytes UTF-16 code) + * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4 + * (2 UTF-8 pairs divided to 2 UTF-16 pairs) */ + if (charlen == 4) + i++; + else if (charlen >= 5) + /* 5-6bytes UTF-8 */ + i += 2; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + +/* + * NAME: cifs_strtoUTF16() + * + * FUNCTION: Convert character string to unicode string + * + */ +int +cifs_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; /* needed to quiet sparse */ + + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *) to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + if (charlen < 1) { + cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n", + *from, charlen); + /* A question mark */ + wchar_to = 0x003f; + charlen = 1; + } + put_unaligned_le16(wchar_to, &to[i]); + } + +success: + put_unaligned_le16(0, &to[i]); + return i; +} + +/* + * cifs_utf16_bytes - how long will a string be after conversion? + * @utf16 - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + */ +int +cifs_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp[3]; + + for (i = 0; i < maxwords; i++) { + ftmp[0] = get_unaligned_le16(&from[i]); + if (ftmp[0] == 0) + break; + if (i + 1 < maxwords) + ftmp[1] = get_unaligned_le16(&from[i + 1]); + else + ftmp[1] = 0; + if (i + 2 < maxwords) + ftmp[2] = get_unaligned_le16(&from[i + 2]); + else + ftmp[2] = 0; + + charlen = cifs_mapchar(tmp, ftmp, codepage, NO_MAP_UNI_RSVD); + outlen += charlen; + } + + return outlen; +} + +/* + * cifs_strndup_from_utf16 - copy a string from wire format to the local + * codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @is_unicode - is this a unicode string? + * @codepage - destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +char * +cifs_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, const struct nls_table *codepage) +{ + int len; + char *dst; + + if (is_unicode) { + len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return NULL; + cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, + NO_MAP_UNI_RSVD); + } else { + dst = kstrndup(src, maxlen, GFP_KERNEL); + } + + return dst; +} + +static __le16 convert_to_sfu_char(char src_char) +{ + __le16 dest_char; + + switch (src_char) { + case ':': + dest_char = cpu_to_le16(UNI_COLON); + break; + case '*': + dest_char = cpu_to_le16(UNI_ASTERISK); + break; + case '?': + dest_char = cpu_to_le16(UNI_QUESTION); + break; + case '<': + dest_char = cpu_to_le16(UNI_LESSTHAN); + break; + case '>': + dest_char = cpu_to_le16(UNI_GRTRTHAN); + break; + case '|': + dest_char = cpu_to_le16(UNI_PIPE); + break; + default: + dest_char = 0; + } + + return dest_char; +} + +static __le16 convert_to_sfm_char(char src_char, bool end_of_string) +{ + __le16 dest_char; + + if (src_char >= 0x01 && src_char <= 0x1F) { + dest_char = cpu_to_le16(src_char + 0xF000); + return dest_char; + } + switch (src_char) { + case ':': + dest_char = cpu_to_le16(SFM_COLON); + break; + case '"': + dest_char = cpu_to_le16(SFM_DOUBLEQUOTE); + break; + case '*': + dest_char = cpu_to_le16(SFM_ASTERISK); + break; + case '?': + dest_char = cpu_to_le16(SFM_QUESTION); + break; + case '<': + dest_char = cpu_to_le16(SFM_LESSTHAN); + break; + case '>': + dest_char = cpu_to_le16(SFM_GRTRTHAN); + break; + case '|': + dest_char = cpu_to_le16(SFM_PIPE); + break; + case '.': + if (end_of_string) + dest_char = cpu_to_le16(SFM_PERIOD); + else + dest_char = 0; + break; + case ' ': + if (end_of_string) + dest_char = cpu_to_le16(SFM_SPACE); + else + dest_char = 0; + break; + default: + dest_char = 0; + } + + return dest_char; +} + +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +int +cifsConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int map_chars) +{ + int i, charlen; + int j = 0; + char src_char; + __le16 dst_char; + wchar_t tmp; + wchar_t *wchar_to; /* UTF-16 */ + int ret; + unicode_t u; + + if (map_chars == NO_MAP_UNI_RSVD) + return cifs_strtoUTF16(target, source, PATH_MAX, cp); + + wchar_to = kzalloc(6, GFP_KERNEL); + + for (i = 0; i < srclen; j++) { + src_char = source[i]; + charlen = 1; + + /* check if end of string */ + if (src_char == 0) + goto ctoUTF16_out; + + /* see if we must remap this char */ + if (map_chars == SFU_MAP_UNI_RSVD) + dst_char = convert_to_sfu_char(src_char); + else if (map_chars == SFM_MAP_UNI_RSVD) { + bool end_of_string; + + /** + * Remap spaces and periods found at the end of every + * component of the path. The special cases of '.' and + * '..' do not need to be dealt with explicitly because + * they are addressed in namei.c:link_path_walk(). + **/ + if ((i == srclen - 1) || (source[i+1] == '\\')) + end_of_string = true; + else + end_of_string = false; + + dst_char = convert_to_sfm_char(src_char, end_of_string); + } else + dst_char = 0; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + if (dst_char == 0) { + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen > 0) + goto ctoUTF16; + + /* convert SURROGATE_PAIR */ + if (strcmp(cp->charset, "utf8") || !wchar_to) + goto unknown; + if (*(source + i) & 0x80) { + charlen = utf8_to_utf32(source + i, 6, &u); + if (charlen < 0) + goto unknown; + } else + goto unknown; + ret = utf8s_to_utf16s(source + i, charlen, + UTF16_LITTLE_ENDIAN, + wchar_to, 6); + if (ret < 0) + goto unknown; + + i += charlen; + dst_char = cpu_to_le16(*wchar_to); + if (charlen <= 3) + /* 1-3bytes UTF-8 to 2bytes UTF-16 */ + put_unaligned(dst_char, &target[j]); + else if (charlen == 4) { + /* 4bytes UTF-8(surrogate pair) to 4bytes UTF-16 + * 7-8bytes UTF-8(IVS) divided to 2 UTF-16 + * (charlen=3+4 or 4+4) */ + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 1)); + j++; + put_unaligned(dst_char, &target[j]); + } else if (charlen >= 5) { + /* 5-6bytes UTF-8 to 6bytes UTF-16 */ + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 1)); + j++; + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 2)); + j++; + put_unaligned(dst_char, &target[j]); + } + continue; + +unknown: + dst_char = cpu_to_le16(0x003f); + charlen = 1; + } + +ctoUTF16: + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; + put_unaligned(dst_char, &target[j]); + } + +ctoUTF16_out: + put_unaligned(0, &target[j]); /* Null terminate target unicode string */ + kfree(wchar_to); + return j; +} + +/* + * cifs_local_to_utf16_bytes - how long will a string be after conversion? + * @from - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - source codepage + * + * Walk a string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + */ + +static int +cifs_local_to_utf16_bytes(const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; + + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + /* Failed conversion defaults to a question mark */ + if (charlen < 1) + charlen = 1; + } + return 2 * i; /* UTF16 characters are two bytes */ +} + +/* + * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @utf16_len - the length of the allocated string in bytes (including null) + * @cp - source codepage + * @remap - map special chars + * + * Take a string convert it from the local codepage to UTF16 and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +__le16 * +cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, + const struct nls_table *cp, int remap) +{ + int len; + __le16 *dst; + + len = cifs_local_to_utf16_bytes(src, maxlen, cp); + len += 2; /* NULL */ + dst = kmalloc(len, GFP_KERNEL); + if (!dst) { + *utf16_len = 0; + return NULL; + } + cifsConvertToUTF16(dst, src, strlen(src), cp, remap); + *utf16_len = len; + return dst; +} diff --git a/fs/smb/client/cifs_unicode.h b/fs/smb/client/cifs_unicode.h new file mode 100644 index 000000000000..80b3d845419f --- /dev/null +++ b/fs/smb/client/cifs_unicode.h @@ -0,0 +1,404 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * cifs_unicode: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * + * Notes: + * These APIs are based on the C library functions. The semantics + * should match the C functions but with expanded size operands. + * + * The upper/lower functions are based on a table created by mkupr. + * This is a compressed table of upper and lower case conversion. + */ +#ifndef _CIFS_UNICODE_H +#define _CIFS_UNICODE_H + +#include +#include +#include + +#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ + +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERISK (__u16) ('*' + 0xF000) +#define UNI_QUESTION (__u16) ('?' + 0xF000) +#define UNI_COLON (__u16) (':' + 0xF000) +#define UNI_GRTRTHAN (__u16) ('>' + 0xF000) +#define UNI_LESSTHAN (__u16) ('<' + 0xF000) +#define UNI_PIPE (__u16) ('|' + 0xF000) +#define UNI_SLASH (__u16) ('\\' + 0xF000) + +/* + * Macs use an older "SFM" mapping of the symbols above. Fortunately it does + * not conflict (although almost does) with the mapping above. + */ + +#define SFM_DOUBLEQUOTE ((__u16) 0xF020) +#define SFM_ASTERISK ((__u16) 0xF021) +#define SFM_QUESTION ((__u16) 0xF025) +#define SFM_COLON ((__u16) 0xF022) +#define SFM_GRTRTHAN ((__u16) 0xF024) +#define SFM_LESSTHAN ((__u16) 0xF023) +#define SFM_PIPE ((__u16) 0xF027) +#define SFM_SLASH ((__u16) 0xF026) +#define SFM_SPACE ((__u16) 0xF028) +#define SFM_PERIOD ((__u16) 0xF029) + +/* + * Mapping mechanism to use when one of the seven reserved characters is + * encountered. We can only map using one of the mechanisms at a time + * since otherwise readdir could return directory entries which we would + * not be able to open + * + * NO_MAP_UNI_RSVD = do not perform any remapping of the character + * SFM_MAP_UNI_RSVD = map reserved characters using SFM scheme (MAC compatible) + * SFU_MAP_UNI_RSVD = map reserved characters ala SFU ("mapchars" option) + * + */ +#define NO_MAP_UNI_RSVD 0 +#define SFM_MAP_UNI_RSVD 1 +#define SFU_MAP_UNI_RSVD 2 + +/* Just define what we want from uniupr.h. We don't want to define the tables + * in each source file. + */ +#ifndef UNICASERANGE_DEFINED +struct UniCaseRange { + wchar_t start; + wchar_t end; + signed char *table; +}; +#endif /* UNICASERANGE_DEFINED */ + +#ifndef UNIUPR_NOUPPER +extern signed char CifsUniUpperTable[512]; +extern const struct UniCaseRange CifsUniUpperRange[]; +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +extern signed char CifsUniLowerTable[512]; +extern const struct UniCaseRange CifsUniLowerRange[]; +#endif /* UNIUPR_NOLOWER */ + +#ifdef __KERNEL__ +int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *cp, int map_type); +int cifs_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage); +int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); +char *cifs_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, + const struct nls_table *cp, int mapChars); +extern int cifs_remap(struct cifs_sb_info *cifs_sb); +extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, + int *utf16_len, const struct nls_table *cp, + int remap); +#endif + +wchar_t cifs_toupper(wchar_t in); + +/* + * UniStrcat: Concatenate the second string to the first + * + * Returns: + * Address of the first string + */ +static inline __le16 * +UniStrcat(__le16 *ucs1, const __le16 *ucs2) +{ + __le16 *anchor = ucs1; /* save a pointer to start of ucs1 */ + + while (*ucs1++) ; /* To end of first string */ + ucs1--; /* Return to the null */ + while ((*ucs1++ = *ucs2++)) ; /* copy string 2 over */ + return anchor; +} + +/* + * UniStrchr: Find a character in a string + * + * Returns: + * Address of first occurrence of character in string + * or NULL if the character is not in the string + */ +static inline wchar_t * +UniStrchr(const wchar_t *ucs, wchar_t uc) +{ + while ((*ucs != uc) && *ucs) + ucs++; + + if (*ucs == uc) + return (wchar_t *) ucs; + return NULL; +} + +/* + * UniStrcmp: Compare two strings + * + * Returns: + * < 0: First string is less than second + * = 0: Strings are equal + * > 0: First string is greater than second + */ +static inline int +UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +{ + while ((*ucs1 == *ucs2) && *ucs1) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) *ucs2; +} + +/* + * UniStrcpy: Copy a string + */ +static inline wchar_t * +UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)) ; + return anchor; +} + +/* + * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) + */ +static inline size_t +UniStrlen(const wchar_t *ucs1) +{ + int i = 0; + + while (*ucs1++) + i++; + return i; +} + +/* + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) + */ +static inline size_t +UniStrnlen(const wchar_t *ucs1, int maxlen) +{ + int i = 0; + + while (*ucs1++) { + i++; + if (i >= maxlen) + break; + } + return i; +} + +/* + * UniStrncat: Concatenate length limited string + */ +static inline wchar_t * +UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; /* save pointer to string 1 */ + + while (*ucs1++) ; + ucs1--; /* point to null terminator of s1 */ + while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ + ucs1++; + ucs2++; + } + *ucs1 = 0; /* Null terminate the result */ + return (anchor); +} + +/* + * UniStrncmp: Compare length limited string + */ +static inline int +UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == *ucs2) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) *ucs2; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ +static inline int +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) __le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy: Copy length limited string with pad + */ +static inline wchar_t * +UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ +static inline wchar_t * +UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrstr: Find a string in a string + * + * Returns: + * Address of first match found + * NULL if no matching string is found + */ +static inline wchar_t * +UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +{ + const wchar_t *anchor1 = ucs1; + const wchar_t *anchor2 = ucs2; + + while (*ucs1) { + if (*ucs1 == *ucs2) { + /* Partial match found */ + ucs1++; + ucs2++; + } else { + if (!*ucs2) /* Match found */ + return (wchar_t *) anchor1; + ucs1 = ++anchor1; /* No match */ + ucs2 = anchor2; + } + } + + if (!*ucs2) /* Both end together */ + return (wchar_t *) anchor1; /* Match found */ + return NULL; /* No match */ +} + +#ifndef UNIUPR_NOUPPER +/* + * UniToupper: Convert a unicode character to upper case + */ +static inline wchar_t +UniToupper(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniUpperTable)) { + /* Latin characters */ + return uc + CifsUniUpperTable[uc]; /* Use base tables */ + } else { + rp = CifsUniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + } + return uc; /* Past last range */ +} + +/* + * UniStrupr: Upper case a unicode string + */ +static inline __le16 * +UniStrupr(register __le16 *upin) +{ + register __le16 *up; + + up = upin; + while (*up) { /* For all characters */ + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); + up++; + } + return upin; /* Return input pointer */ +} +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +/* + * UniTolower: Convert a unicode character to lower case + */ +static inline wchar_t +UniTolower(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniLowerTable)) { + /* Latin characters */ + return uc + CifsUniLowerTable[uc]; /* Use base tables */ + } else { + rp = CifsUniLowerRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + } + return uc; /* Past last range */ +} + +/* + * UniStrlwr: Lower case a unicode string + */ +static inline wchar_t * +UniStrlwr(register wchar_t *upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniTolower(*up); + up++; + } + return upin; /* Return input pointer */ +} + +#endif + +#endif /* _CIFS_UNICODE_H */ diff --git a/fs/smb/client/cifs_uniupr.h b/fs/smb/client/cifs_uniupr.h new file mode 100644 index 000000000000..7b272fcdf0d3 --- /dev/null +++ b/fs/smb/client/cifs_uniupr.h @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) International Business Machines Corp., 2000,2002 + * + * uniupr.h - Unicode compressed case ranges +*/ + +#ifndef UNIUPR_NOUPPER +/* + * Latin upper case + */ +signed char CifsUniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 060-06f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 3b0-3bf */ + -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, + -63, -63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 430-43f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 440-44f */ + 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, 0, -80, -80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, 126, 126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* ff40-ff4f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, +}; + +/* + * Upper Case Range + */ +const struct UniCaseRange CifsUniUpperRange[] = { + {0x03a0, 0x03ce, UniCaseRangeU03a0}, + {0x0430, 0x045f, UniCaseRangeU0430}, + {0x0490, 0x04cc, UniCaseRangeU0490}, + {0x1e00, 0x1ffc, UniCaseRangeU1e00}, + {0xff40, 0xff5a, UniCaseRangeUff40}, + {0} +}; +#endif + +#ifndef UNIUPR_NOLOWER +/* + * Latin lower case + */ +signed char CifsUniLowerTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 040-04f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, /* 050-05f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 0c0-0cf */ + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 0, /* 0d0-0df */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ + 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, 0, /* 170-17f */ + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, 0, /* 180-18f */ + 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ + 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ +}; + +/* Lower case range - Greek */ +static signed char UniCaseRangeL0380[44] = { + 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 390-39f */ + 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* Lower case range - Cyrillic */ +static signed char UniCaseRangeL0400[48] = { + 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 80, 80, /* 400-40f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 410-41f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 420-42f */ +}; + +/* Lower case range - Extended cyrillic */ +static signed char UniCaseRangeL0490[60] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +/* Lower case range - Extended latin and greek */ +static signed char UniCaseRangeL1e00[504] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, 0, 0, /* 1fc0-1fcf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Lower case range - Wide latin */ +static signed char UniCaseRangeLff20[27] = { + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* ff20-ff2f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* + * Lower Case Range + */ +const struct UniCaseRange CifsUniLowerRange[] = { + {0x0380, 0x03ab, UniCaseRangeL0380}, + {0x0400, 0x042f, UniCaseRangeL0400}, + {0x0490, 0x04cb, UniCaseRangeL0490}, + {0x1e00, 0x1ff7, UniCaseRangeL1e00}, + {0xff20, 0xff3a, UniCaseRangeLff20}, + {0} +}; +#endif diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c new file mode 100644 index 000000000000..a6c7566a0182 --- /dev/null +++ b/fs/smb/client/cifsacl.c @@ -0,0 +1,1672 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2007,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Contains the routines for mapping CIFS/NTFS ACLs + * + */ + +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "fs_context.h" + +/* security id for everyone/world system group */ +static const struct cifs_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct cifs_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; + +/* S-1-22-1 Unmapped Unix users */ +static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-22-2 Unmapped Unix groups */ +static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * See https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ + +/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ + +/* S-1-5-88-1 Unix uid */ +static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-2 Unix gid */ +static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-3 Unix mode */ +static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +static const struct cred *root_cred; + +static int +cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + char *payload; + + /* + * If the payload is less than or equal to the size of a pointer, then + * an allocation here is wasteful. Just copy the data directly to the + * payload.value union member instead. + * + * With this however, you must check the datalen before trying to + * dereference payload.data! + */ + if (prep->datalen <= sizeof(key->payload)) { + key->payload.data[0] = NULL; + memcpy(&key->payload, prep->data, prep->datalen); + } else { + payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); + if (!payload) + return -ENOMEM; + key->payload.data[0] = payload; + } + + key->datalen = prep->datalen; + return 0; +} + +static inline void +cifs_idmap_key_destroy(struct key *key) +{ + if (key->datalen > sizeof(key->payload)) + kfree(key->payload.data[0]); +} + +static struct key_type cifs_idmap_key_type = { + .name = "cifs.idmap", + .instantiate = cifs_idmap_key_instantiate, + .destroy = cifs_idmap_key_destroy, + .describe = user_describe, +}; + +static char * +sid_to_key_str(struct cifs_sid *sidptr, unsigned int type) +{ + int i, len; + unsigned int saval; + char *sidstr, *strptr; + unsigned long long id_auth_val; + + /* 3 bytes for prefix */ + sidstr = kmalloc(3 + SID_STRING_BASE_SIZE + + (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth), + GFP_KERNEL); + if (!sidstr) + return sidstr; + + strptr = sidstr; + len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g', + sidptr->revision); + strptr += len; + + /* The authority field is a single 48-bit number */ + id_auth_val = (unsigned long long)sidptr->authority[5]; + id_auth_val |= (unsigned long long)sidptr->authority[4] << 8; + id_auth_val |= (unsigned long long)sidptr->authority[3] << 16; + id_auth_val |= (unsigned long long)sidptr->authority[2] << 24; + id_auth_val |= (unsigned long long)sidptr->authority[1] << 32; + id_auth_val |= (unsigned long long)sidptr->authority[0] << 48; + + /* + * MS-DTYP states that if the authority is >= 2^32, then it should be + * expressed as a hex value. + */ + if (id_auth_val <= UINT_MAX) + len = sprintf(strptr, "-%llu", id_auth_val); + else + len = sprintf(strptr, "-0x%llx", id_auth_val); + + strptr += len; + + for (i = 0; i < sidptr->num_subauth; ++i) { + saval = le32_to_cpu(sidptr->sub_auth[i]); + len = sprintf(strptr, "-%u", saval); + strptr += len; + } + + return sidstr; +} + +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +static int +compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) +{ + int i; + int num_subauth, num_sat, num_saw; + + if ((!ctsid) || (!cwsid)) + return 1; + + /* compare the revision */ + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } + } + + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; + num_subauth = num_sat < num_saw ? num_sat : num_saw; + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (le32_to_cpu(ctsid->sub_auth[i]) > + le32_to_cpu(cwsid->sub_auth[i])) + return 1; + else + return -1; + } + } + } + + return 0; /* sids compare/match */ +} + +static bool +is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group) +{ + int i; + int num_subauth; + const struct cifs_sid *pwell_known_sid; + + if (!psid || (puid == NULL)) + return false; + + num_subauth = psid->num_subauth; + + /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */ + if (num_subauth == 2) { + if (is_group) + pwell_known_sid = &sid_unix_groups; + else + pwell_known_sid = &sid_unix_users; + } else if (num_subauth == 3) { + if (is_group) + pwell_known_sid = &sid_unix_NFS_groups; + else + pwell_known_sid = &sid_unix_NFS_users; + } else + return false; + + /* compare the revision */ + if (psid->revision != pwell_known_sid->revision) + return false; + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (psid->authority[i] != pwell_known_sid->authority[i]) { + cifs_dbg(FYI, "auth %d did not match\n", i); + return false; + } + } + + if (num_subauth == 2) { + if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) + return false; + + *puid = le32_to_cpu(psid->sub_auth[1]); + } else /* 3 subauths, ie Windows/Mac style */ { + *puid = le32_to_cpu(psid->sub_auth[0]); + if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) || + (psid->sub_auth[1] != pwell_known_sid->sub_auth[1])) + return false; + + *puid = le32_to_cpu(psid->sub_auth[2]); + } + + cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid); + return true; /* well known sid found, uid returned */ +} + +static __u16 +cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) +{ + int i; + __u16 size = 1 + 1 + 6; + + dst->revision = src->revision; + dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); + for (i = 0; i < NUM_AUTHS; ++i) + dst->authority[i] = src->authority[i]; + for (i = 0; i < dst->num_subauth; ++i) + dst->sub_auth[i] = src->sub_auth[i]; + size += (dst->num_subauth * 4); + + return size; +} + +static int +id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid) +{ + int rc; + struct key *sidkey; + struct cifs_sid *ksid; + unsigned int ksid_size; + char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */ + const struct cred *saved_cred; + + rc = snprintf(desc, sizeof(desc), "%ci:%u", + sidtype == SIDOWNER ? 'o' : 'g', cid); + if (rc >= sizeof(desc)) + return -EINVAL; + + rc = 0; + saved_cred = override_creds(root_cred); + sidkey = request_key(&cifs_idmap_key_type, desc, ""); + if (IS_ERR(sidkey)) { + rc = -EINVAL; + cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n", + __func__, sidtype == SIDOWNER ? 'u' : 'g', cid); + goto out_revert_creds; + } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) { + rc = -EIO; + cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", + __func__, sidkey->datalen); + goto invalidate_key; + } + + /* + * A sid is usually too large to be embedded in payload.value, but if + * there are no subauthorities and the host has 8-byte pointers, then + * it could be. + */ + ksid = sidkey->datalen <= sizeof(sidkey->payload) ? + (struct cifs_sid *)&sidkey->payload : + (struct cifs_sid *)sidkey->payload.data[0]; + + ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32)); + if (ksid_size > sidkey->datalen) { + rc = -EIO; + cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n", + __func__, sidkey->datalen, ksid_size); + goto invalidate_key; + } + + cifs_copy_sid(ssid, ksid); +out_key_put: + key_put(sidkey); +out_revert_creds: + revert_creds(saved_cred); + return rc; + +invalidate_key: + key_invalidate(sidkey); + goto out_key_put; +} + +int +sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, + struct cifs_fattr *fattr, uint sidtype) +{ + int rc = 0; + struct key *sidkey; + char *sidstr; + const struct cred *saved_cred; + kuid_t fuid = cifs_sb->ctx->linux_uid; + kgid_t fgid = cifs_sb->ctx->linux_gid; + + /* + * If we have too many subauthorities, then something is really wrong. + * Just return an error. + */ + if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { + cifs_dbg(FYI, "%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); + return -EIO; + } + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) || + (cifs_sb_master_tcon(cifs_sb)->posix_extensions)) { + uint32_t unix_id; + bool is_group; + + if (sidtype != SIDOWNER) + is_group = true; + else + is_group = false; + + if (is_well_known_sid(psid, &unix_id, is_group) == false) + goto try_upcall_to_get_id; + + if (is_group) { + kgid_t gid; + gid_t id; + + id = (gid_t)unix_id; + gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) { + fgid = gid; + goto got_valid_id; + } + } else { + kuid_t uid; + uid_t id; + + id = (uid_t)unix_id; + uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) { + fuid = uid; + goto got_valid_id; + } + } + /* If unable to find uid/gid easily from SID try via upcall */ + } + +try_upcall_to_get_id: + sidstr = sid_to_key_str(psid, sidtype); + if (!sidstr) + return -ENOMEM; + + saved_cred = override_creds(root_cred); + sidkey = request_key(&cifs_idmap_key_type, sidstr, ""); + if (IS_ERR(sidkey)) { + cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n", + __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g'); + goto out_revert_creds; + } + + /* + * FIXME: Here we assume that uid_t and gid_t are same size. It's + * probably a safe assumption but might be better to check based on + * sidtype. + */ + BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); + if (sidkey->datalen != sizeof(uid_t)) { + cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", + __func__, sidkey->datalen); + key_invalidate(sidkey); + goto out_key_put; + } + + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t)); + uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fuid = uid; + } else { + kgid_t gid; + gid_t id; + memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t)); + gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fgid = gid; + } + +out_key_put: + key_put(sidkey); +out_revert_creds: + revert_creds(saved_cred); + kfree(sidstr); + + /* + * Note that we return 0 here unconditionally. If the mapping + * fails then we just fall back to using the ctx->linux_uid/linux_gid. + */ +got_valid_id: + rc = 0; + if (sidtype == SIDOWNER) + fattr->cf_uid = fuid; + else + fattr->cf_gid = fgid; + return rc; +} + +int +init_cifs_idmap(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + cifs_dbg(FYI, "Registering the %s key type\n", + cifs_idmap_key_type.name); + + /* create an override credential set with a special thread keyring in + * which requests are cached + * + * this is used to prevent malicious redirections from being installed + * with add_key(). + */ + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = keyring_alloc(".cifs_idmap", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = register_key_type(&cifs_idmap_key_type); + if (ret < 0) + goto failed_put_key; + + /* instruct request_key() to use this special keyring as a cache for + * the results it looks up */ + set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + root_cred = cred; + + cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring)); + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +void +exit_cifs_idmap(void) +{ + key_revoke(root_cred->thread_keyring); + unregister_key_type(&cifs_idmap_key_type); + put_cred(root_cred); + cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name); +} + +/* copy ntsd, owner sid, and group sid from a security descriptor to another */ +static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd, + struct cifs_ntsd *pnntsd, + __u32 sidsoffset, + struct cifs_sid *pownersid, + struct cifs_sid *pgrpsid) +{ + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; + + /* copy security descriptor control portion */ + pnntsd->revision = pntsd->revision; + pnntsd->type = pntsd->type; + pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd)); + pnntsd->sacloffset = 0; + pnntsd->osidoffset = cpu_to_le32(sidsoffset); + pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid)); + + /* copy owner sid */ + if (pownersid) + owner_sid_ptr = pownersid; + else + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset); + cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr); + + /* copy group sid */ + if (pgrpsid) + group_sid_ptr = pgrpsid; + else + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset + + sizeof(struct cifs_sid)); + cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr); + + return sidsoffset + (2 * sizeof(struct cifs_sid)); +} + + +/* + change posix mode to reflect permissions + pmode is the existing mode (we only want to overwrite part of this + bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 +*/ +static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode, + umode_t *pdenied, umode_t mask) +{ + __u32 flags = le32_to_cpu(ace_flags); + /* + * Do not assume "preferred" or "canonical" order. + * The first DENY or ALLOW ACE which matches perfectly is + * the permission to be used. Once allowed or denied, same + * permission in later ACEs do not matter. + */ + + /* If not already allowed, deny these bits */ + if (type == ACCESS_DENIED) { + if (flags & GENERIC_ALL && + !(*pmode & mask & 0777)) + *pdenied |= mask & 0777; + + if (((flags & GENERIC_WRITE) || + ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && + !(*pmode & mask & 0222)) + *pdenied |= mask & 0222; + + if (((flags & GENERIC_READ) || + ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && + !(*pmode & mask & 0444)) + *pdenied |= mask & 0444; + + if (((flags & GENERIC_EXECUTE) || + ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && + !(*pmode & mask & 0111)) + *pdenied |= mask & 0111; + + return; + } else if (type != ACCESS_ALLOWED) { + cifs_dbg(VFS, "unknown access control type %d\n", type); + return; + } + /* else ACCESS_ALLOWED type */ + + if ((flags & GENERIC_ALL) && + !(*pdenied & mask & 0777)) { + *pmode |= mask & 0777; + cifs_dbg(NOISY, "all perms\n"); + return; + } + + if (((flags & GENERIC_WRITE) || + ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && + !(*pdenied & mask & 0222)) + *pmode |= mask & 0222; + + if (((flags & GENERIC_READ) || + ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && + !(*pdenied & mask & 0444)) + *pmode |= mask & 0444; + + if (((flags & GENERIC_EXECUTE) || + ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && + !(*pdenied & mask & 0111)) + *pmode |= mask & 0111; + + /* If DELETE_CHILD is set only on an owner ACE, set sticky bit */ + if (flags & FILE_DELETE_CHILD) { + if (mask == ACL_OWNER_MASK) { + if (!(*pdenied & 01000)) + *pmode |= 01000; + } else if (!(*pdenied & 01000)) { + *pmode &= ~01000; + *pdenied |= 01000; + } + } + + cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode); + return; +} + +/* + Generate access flags to reflect permissions mode is the existing mode. + This function is called for every ACE in the DACL whose SID matches + with either owner or group or everyone. +*/ + +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* check for R/W/X UGO since we do not know whose flags + is this but we have cleared all the bits sans RWX for + either user or group or other as per bits_to_use */ + if (mode & S_IRUGO) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & S_IWUGO) + *pace_flags |= SET_FILE_WRITE_RIGHTS; + if (mode & S_IXUGO) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + + cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n", + mode, *pace_flags); + return; +} + +static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src, struct cifs_sid *psid) +{ + __u16 size = 1 + 1 + 2 + 4; + + dst->type = src->type; + dst->flags = src->flags; + dst->access_req = src->access_req; + + /* Check if there's a replacement sid specified */ + if (psid) + size += cifs_copy_sid(&dst->sid, psid); + else + size += cifs_copy_sid(&dst->sid, &src->sid); + + dst->size = cpu_to_le16(size); + + return size; +} + +static __u16 fill_ace_for_sid(struct cifs_ace *pntace, + const struct cifs_sid *psid, __u64 nmode, + umode_t bits, __u8 access_type, + bool allow_delete_child) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = access_type; + pntace->flags = 0x0; + mode_to_access_flags(nmode, bits, &access_req); + + if (access_type == ACCESS_ALLOWED && allow_delete_child) + access_req |= FILE_DELETE_CHILD; + + if (access_type == ACCESS_ALLOWED && !access_req) + access_req = SET_MINIMUM_RIGHTS; + else if (access_type == ACCESS_DENIED) + access_req &= ~SET_MINIMUM_RIGHTS; + + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return size; +} + + +#ifdef CONFIG_CIFS_DEBUG2 +static void dump_ace(struct cifs_ace *pace, char *end_of_acl) +{ + int num_subauth; + + /* validate that we do not go past end of acl */ + + if (le16_to_cpu(pace->size) < 16) { + cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size)); + return; + } + + if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return; + } + + num_subauth = pace->sid.num_subauth; + if (num_subauth) { + int i; + cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n", + pace->sid.revision, pace->sid.num_subauth, pace->type, + pace->flags, le16_to_cpu(pace->size)); + for (i = 0; i < num_subauth; ++i) { + cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n", + i, le32_to_cpu(pace->sid.sub_auth[i])); + } + + /* BB add length check to make sure that we do not have huge + num auths and therefore go off the end */ + } + + return; +} +#endif + +static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + struct cifs_fattr *fattr, bool mode_from_special_sid) +{ + int i; + int num_aces = 0; + int acl_size; + char *acl_base; + struct cifs_ace **ppace; + + /* BB need to add parm so we can store the SID BB */ + + if (!pdacl) { + /* no DACL in the security descriptor, set + all the permissions for user/group/other */ + fattr->cf_mode |= 0777; + return; + } + + /* validate that we do not go past end of acl */ + if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return; + } + + cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); + + /* reset rwx permissions for user/group/other. + Also, if num_aces is 0 i.e. DACL has no ACEs, + user/group/other have no permissions */ + fattr->cf_mode &= ~(0777); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct cifs_acl); + + num_aces = le32_to_cpu(pdacl->num_aces); + if (num_aces > 0) { + umode_t denied_mode = 0; + + if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *)) + return; + ppace = kmalloc_array(num_aces, sizeof(struct cifs_ace *), + GFP_KERNEL); + if (!ppace) + return; + + for (i = 0; i < num_aces; ++i) { + ppace[i] = (struct cifs_ace *) (acl_base + acl_size); +#ifdef CONFIG_CIFS_DEBUG2 + dump_ace(ppace[i], end_of_acl); +#endif + if (mode_from_special_sid && + (compare_sids(&(ppace[i]->sid), + &sid_unix_NFS_mode) == 0)) { + /* + * Full permissions are: + * 07777 = S_ISUID | S_ISGID | S_ISVTX | + * S_IRWXU | S_IRWXG | S_IRWXO + */ + fattr->cf_mode &= ~07777; + fattr->cf_mode |= + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else { + if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &fattr->cf_mode, + &denied_mode, + ACL_OWNER_MASK); + } else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) { + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &fattr->cf_mode, + &denied_mode, + ACL_GROUP_MASK); + } else if ((compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) || + (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)) { + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &fattr->cf_mode, + &denied_mode, + ACL_EVERYONE_MASK); + } + } + + +/* memcpy((void *)(&(cifscred->aces[i])), + (void *)ppace[i], + sizeof(struct cifs_ace)); */ + + acl_base = (char *)ppace[i]; + acl_size = le16_to_cpu(ppace[i]->size); + } + + kfree(ppace); + } + + return; +} + +unsigned int setup_authusers_ACE(struct cifs_ace *pntace) +{ + int i; + unsigned int ace_size = 20; + + pntace->type = ACCESS_ALLOWED_ACE_TYPE; + pntace->flags = 0x0; + pntace->access_req = cpu_to_le32(GENERIC_ALL); + pntace->sid.num_subauth = 1; + pntace->sid.revision = 1; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = sid_authusers.authority[i]; + + pntace->sid.sub_auth[0] = sid_authusers.sub_auth[0]; + + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(ace_size); + return ace_size; +} + +/* + * Fill in the special SID based on the mode. See + * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ +unsigned int setup_special_mode_ACE(struct cifs_ace *pntace, __u64 nmode) +{ + int i; + unsigned int ace_size = 28; + + pntace->type = ACCESS_DENIED_ACE_TYPE; + pntace->flags = 0x0; + pntace->access_req = 0; + pntace->sid.num_subauth = 3; + pntace->sid.revision = 1; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = sid_unix_NFS_mode.authority[i]; + + pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0]; + pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; + pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); + + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(ace_size); + return ace_size; +} + +unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace) +{ + int i; + unsigned int ace_size = 28; + + pntace->type = ACCESS_ALLOWED_ACE_TYPE; + pntace->flags = 0x0; + pntace->access_req = cpu_to_le32(GENERIC_ALL); + pntace->sid.num_subauth = 3; + pntace->sid.revision = 1; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = sid_unix_NFS_users.authority[i]; + + pntace->sid.sub_auth[0] = sid_unix_NFS_users.sub_auth[0]; + pntace->sid.sub_auth[1] = sid_unix_NFS_users.sub_auth[1]; + pntace->sid.sub_auth[2] = cpu_to_le32(current_fsgid().val); + + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(ace_size); + return ace_size; +} + +static void populate_new_aces(char *nacl_base, + struct cifs_sid *pownersid, + struct cifs_sid *pgrpsid, + __u64 *pnmode, u32 *pnum_aces, u16 *pnsize, + bool modefromsid) +{ + __u64 nmode; + u32 num_aces = 0; + u16 nsize = 0; + __u64 user_mode; + __u64 group_mode; + __u64 other_mode; + __u64 deny_user_mode = 0; + __u64 deny_group_mode = 0; + bool sticky_set = false; + struct cifs_ace *pnntace = NULL; + + nmode = *pnmode; + num_aces = *pnum_aces; + nsize = *pnsize; + + if (modefromsid) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += setup_special_mode_ACE(pnntace, nmode); + num_aces++; + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += setup_authusers_ACE(pnntace); + num_aces++; + goto set_size; + } + + /* + * We'll try to keep the mode as requested by the user. + * But in cases where we cannot meaningfully convert that + * into ACL, return back the updated mode, so that it is + * updated in the inode. + */ + + if (!memcmp(pownersid, pgrpsid, sizeof(struct cifs_sid))) { + /* + * Case when owner and group SIDs are the same. + * Set the more restrictive of the two modes. + */ + user_mode = nmode & (nmode << 3) & 0700; + group_mode = nmode & (nmode >> 3) & 0070; + } else { + user_mode = nmode & 0700; + group_mode = nmode & 0070; + } + + other_mode = nmode & 0007; + + /* We need DENY ACE when the perm is more restrictive than the next sets. */ + deny_user_mode = ~(user_mode) & ((group_mode << 3) | (other_mode << 6)) & 0700; + deny_group_mode = ~(group_mode) & (other_mode << 3) & 0070; + + *pnmode = user_mode | group_mode | other_mode | (nmode & ~0777); + + /* This tells if we should allow delete child for group and everyone. */ + if (nmode & 01000) + sticky_set = true; + + if (deny_user_mode) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pownersid, deny_user_mode, + 0700, ACCESS_DENIED, false); + num_aces++; + } + + /* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/ + if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, + 0070, ACCESS_DENIED, false); + num_aces++; + } + + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pownersid, user_mode, + 0700, ACCESS_ALLOWED, true); + num_aces++; + + /* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */ + if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, + 0070, ACCESS_DENIED, false); + num_aces++; + } + + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pgrpsid, group_mode, + 0070, ACCESS_ALLOWED, !sticky_set); + num_aces++; + + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, &sid_everyone, other_mode, + 0007, ACCESS_ALLOWED, !sticky_set); + num_aces++; + +set_size: + *pnum_aces = num_aces; + *pnsize = nsize; +} + +static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl, + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + struct cifs_sid *pnownersid, struct cifs_sid *pngrpsid) +{ + int i; + u16 size = 0; + struct cifs_ace *pntace = NULL; + char *acl_base = NULL; + u32 src_num_aces = 0; + u16 nsize = 0; + struct cifs_ace *pnntace = NULL; + char *nacl_base = NULL; + u16 ace_size = 0; + + acl_base = (char *)pdacl; + size = sizeof(struct cifs_acl); + src_num_aces = le32_to_cpu(pdacl->num_aces); + + nacl_base = (char *)pndacl; + nsize = sizeof(struct cifs_acl); + + /* Go through all the ACEs */ + for (i = 0; i < src_num_aces; ++i) { + pntace = (struct cifs_ace *) (acl_base + size); + pnntace = (struct cifs_ace *) (nacl_base + nsize); + + if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) + ace_size = cifs_copy_ace(pnntace, pntace, pnownersid); + else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) + ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid); + else + ace_size = cifs_copy_ace(pnntace, pntace, NULL); + + size += le16_to_cpu(pntace->size); + nsize += ace_size; + } + + return nsize; +} + +static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl, + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + __u64 *pnmode, bool mode_from_sid) +{ + int i; + u16 size = 0; + struct cifs_ace *pntace = NULL; + char *acl_base = NULL; + u32 src_num_aces = 0; + u16 nsize = 0; + struct cifs_ace *pnntace = NULL; + char *nacl_base = NULL; + u32 num_aces = 0; + bool new_aces_set = false; + + /* Assuming that pndacl and pnmode are never NULL */ + nacl_base = (char *)pndacl; + nsize = sizeof(struct cifs_acl); + + /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */ + if (!pdacl) { + populate_new_aces(nacl_base, + pownersid, pgrpsid, + pnmode, &num_aces, &nsize, + mode_from_sid); + goto finalize_dacl; + } + + acl_base = (char *)pdacl; + size = sizeof(struct cifs_acl); + src_num_aces = le32_to_cpu(pdacl->num_aces); + + /* Retain old ACEs which we can retain */ + for (i = 0; i < src_num_aces; ++i) { + pntace = (struct cifs_ace *) (acl_base + size); + + if (!new_aces_set && (pntace->flags & INHERITED_ACE)) { + /* Place the new ACEs in between existing explicit and inherited */ + populate_new_aces(nacl_base, + pownersid, pgrpsid, + pnmode, &num_aces, &nsize, + mode_from_sid); + + new_aces_set = true; + } + + /* If it's any one of the ACE we're replacing, skip! */ + if (((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) || + (compare_sids(&pntace->sid, pownersid) == 0) || + (compare_sids(&pntace->sid, pgrpsid) == 0) || + (compare_sids(&pntace->sid, &sid_everyone) == 0) || + (compare_sids(&pntace->sid, &sid_authusers) == 0))) { + goto next_ace; + } + + /* update the pointer to the next ACE to populate*/ + pnntace = (struct cifs_ace *) (nacl_base + nsize); + + nsize += cifs_copy_ace(pnntace, pntace, NULL); + num_aces++; + +next_ace: + size += le16_to_cpu(pntace->size); + } + + /* If inherited ACEs are not present, place the new ones at the tail */ + if (!new_aces_set) { + populate_new_aces(nacl_base, + pownersid, pgrpsid, + pnmode, &num_aces, &nsize, + mode_from_sid); + + new_aces_set = true; + } + +finalize_dacl: + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(nsize); + + return 0; +} + +static int parse_sid(struct cifs_sid *psid, char *end_of_acl) +{ + /* BB need to add parm so we can store the SID BB */ + + /* validate that we do not go past end of ACL - sid must be at least 8 + bytes long (assuming no sub-auths - e.g. the null SID */ + if (end_of_acl < (char *)psid + 8) { + cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid); + return -EINVAL; + } + +#ifdef CONFIG_CIFS_DEBUG2 + if (psid->num_subauth) { + int i; + cifs_dbg(FYI, "SID revision %d num_auth %d\n", + psid->revision, psid->num_subauth); + + for (i = 0; i < psid->num_subauth; i++) { + cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n", + i, le32_to_cpu(psid->sub_auth[i])); + } + + /* BB add length check to make sure that we do not have huge + num auths and therefore go off the end */ + cifs_dbg(FYI, "RID 0x%x\n", + le32_to_cpu(psid->sub_auth[psid->num_subauth-1])); + } +#endif + + return 0; +} + + +/* Convert CIFS ACL to POSIX form */ +static int parse_sec_desc(struct cifs_sb_info *cifs_sb, + struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr, + bool get_mode_from_special_sid) +{ + int rc = 0; + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + + if (pntsd == NULL) + return -EIO; + + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); + cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); +/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ + rc = parse_sid(owner_sid_ptr, end_of_acl); + if (rc) { + cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc); + return rc; + } + rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + return rc; + } + + rc = parse_sid(group_sid_ptr, end_of_acl); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n", + __func__, rc); + return rc; + } + rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", + __func__, rc); + return rc; + } + + if (dacloffset) + parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, + group_sid_ptr, fattr, get_mode_from_special_sid); + else + cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ + + return rc; +} + +/* Convert permission bits from mode to equivalent CIFS ACL */ +static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, + __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid, + bool mode_from_sid, bool id_from_sid, int *aclflag) +{ + int rc = 0; + __u32 dacloffset; + __u32 ndacloffset; + __u32 sidsoffset; + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL; + struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + secdesclen; + u16 size = 0; + + dacloffset = le32_to_cpu(pntsd->dacloffset); + if (dacloffset) { + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); + if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { + cifs_dbg(VFS, "Server returned illegal ACL size\n"); + return -EINVAL; + } + } + + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + + if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ + ndacloffset = sizeof(struct cifs_ntsd); + ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); + ndacl_ptr->revision = + dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); + + ndacl_ptr->size = cpu_to_le16(0); + ndacl_ptr->num_aces = cpu_to_le32(0); + + rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, + pnmode, mode_from_sid); + + sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); + /* copy the non-dacl portion of secdesc */ + *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, + NULL, NULL); + + *aclflag |= CIFS_ACL_DACL; + } else { + ndacloffset = sizeof(struct cifs_ntsd); + ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); + ndacl_ptr->revision = + dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); + ndacl_ptr->num_aces = dacl_ptr ? dacl_ptr->num_aces : 0; + + if (uid_valid(uid)) { /* chown */ + uid_t id; + nowner_sid_ptr = kzalloc(sizeof(struct cifs_sid), + GFP_KERNEL); + if (!nowner_sid_ptr) { + rc = -ENOMEM; + goto chown_chgrp_exit; + } + id = from_kuid(&init_user_ns, uid); + if (id_from_sid) { + struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr; + /* Populate the user ownership fields S-1-5-88-1 */ + osid->Revision = 1; + osid->NumAuth = 3; + osid->Authority[5] = 5; + osid->SubAuthorities[0] = cpu_to_le32(88); + osid->SubAuthorities[1] = cpu_to_le32(1); + osid->SubAuthorities[2] = cpu_to_le32(id); + + } else { /* lookup sid with upcall */ + rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); + if (rc) { + cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", + __func__, rc, id); + goto chown_chgrp_exit; + } + } + *aclflag |= CIFS_ACL_OWNER; + } + if (gid_valid(gid)) { /* chgrp */ + gid_t id; + ngroup_sid_ptr = kzalloc(sizeof(struct cifs_sid), + GFP_KERNEL); + if (!ngroup_sid_ptr) { + rc = -ENOMEM; + goto chown_chgrp_exit; + } + id = from_kgid(&init_user_ns, gid); + if (id_from_sid) { + struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr; + /* Populate the group ownership fields S-1-5-88-2 */ + gsid->Revision = 1; + gsid->NumAuth = 3; + gsid->Authority[5] = 5; + gsid->SubAuthorities[0] = cpu_to_le32(88); + gsid->SubAuthorities[1] = cpu_to_le32(2); + gsid->SubAuthorities[2] = cpu_to_le32(id); + + } else { /* lookup sid with upcall */ + rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); + if (rc) { + cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", + __func__, rc, id); + goto chown_chgrp_exit; + } + } + *aclflag |= CIFS_ACL_GROUP; + } + + if (dacloffset) { + /* Replace ACEs for old owner with new one */ + size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, + owner_sid_ptr, group_sid_ptr, + nowner_sid_ptr, ngroup_sid_ptr); + ndacl_ptr->size = cpu_to_le16(size); + } + + sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); + /* copy the non-dacl portion of secdesc */ + *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, + nowner_sid_ptr, ngroup_sid_ptr); + +chown_chgrp_exit: + /* errors could jump here. So make sure we return soon after this */ + kfree(nowner_sid_ptr); + kfree(ngroup_sid_ptr); + } + + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, + const struct cifs_fid *cifsfid, u32 *pacllen, + u32 __maybe_unused unused) +{ + struct cifs_ntsd *pntsd = NULL; + unsigned int xid; + int rc; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + xid = get_xid(); + rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd, + pacllen); + free_xid(xid); + + cifs_put_tlink(tlink); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; +} + +static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, + const char *path, u32 *pacllen) +{ + struct cifs_ntsd *pntsd = NULL; + int oplock = 0; + unsigned int xid; + int rc; + struct cifs_tcon *tcon; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = READ_CONTROL, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (!rc) { + rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen); + CIFSSMBClose(xid, tcon, fid.netfid); + } + + cifs_put_tlink(tlink); + free_xid(xid); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; +} + +/* Retrieve an ACL from the server */ +struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, + struct inode *inode, const char *path, + u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + struct cifsFileInfo *open_file = NULL; + + if (inode) + open_file = find_readable_file(CIFS_I(inode), true); + if (!open_file) + return get_cifs_acl_by_path(cifs_sb, path, pacllen); + + pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); + cifsFileInfo_put(open_file); + return pntsd; +} + + /* Set an ACL on the server */ +int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, + struct inode *inode, const char *path, int aclflag) +{ + int oplock = 0; + unsigned int xid; + int rc, access_flags; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP) + access_flags = WRITE_OWNER; + else + access_flags = WRITE_DAC; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = access_flags, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) { + cifs_dbg(VFS, "Unable to open file to set ACL\n"); + goto out; + } + + rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag); + cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc); + + CIFSSMBClose(xid, tcon, fid.netfid); +out: + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */ +int +cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, + struct inode *inode, bool mode_from_special_sid, + const char *path, const struct cifs_fid *pfid) +{ + struct cifs_ntsd *pntsd = NULL; + u32 acllen = 0; + int rc = 0; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct smb_version_operations *ops; + const u32 info = 0; + + cifs_dbg(NOISY, "converting ACL to mode for %s\n", path); + + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + ops = tlink_tcon(tlink)->ses->server->ops; + + if (pfid && (ops->get_acl_by_fid)) + pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen, info); + else if (ops->get_acl) + pntsd = ops->get_acl(cifs_sb, inode, path, &acllen, info); + else { + cifs_put_tlink(tlink); + return -EOPNOTSUPP; + } + /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ + if (IS_ERR(pntsd)) { + rc = PTR_ERR(pntsd); + cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); + } else if (mode_from_special_sid) { + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true); + kfree(pntsd); + } else { + /* get approximated mode from ACL */ + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false); + kfree(pntsd); + if (rc) + cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); + } + + cifs_put_tlink(tlink); + + return rc; +} + +/* Convert mode bits to an ACL so we can update the ACL on the server */ +int +id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, + kuid_t uid, kgid_t gid) +{ + int rc = 0; + int aclflag = CIFS_ACL_DACL; /* default flag to set */ + __u32 secdesclen = 0; + __u32 nsecdesclen = 0; + __u32 dacloffset = 0; + struct cifs_acl *dacl_ptr = NULL; + struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */ + struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct smb_version_operations *ops; + bool mode_from_sid, id_from_sid; + const u32 info = 0; + + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + ops = tlink_tcon(tlink)->ses->server->ops; + + cifs_dbg(NOISY, "set ACL from mode for %s\n", path); + + /* Get the security descriptor */ + + if (ops->get_acl == NULL) { + cifs_put_tlink(tlink); + return -EOPNOTSUPP; + } + + pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen, info); + if (IS_ERR(pntsd)) { + rc = PTR_ERR(pntsd); + cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); + cifs_put_tlink(tlink); + return rc; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) + mode_from_sid = true; + else + mode_from_sid = false; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + id_from_sid = true; + else + id_from_sid = false; + + /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */ + nsecdesclen = secdesclen; + if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ + if (mode_from_sid) + nsecdesclen += 2 * sizeof(struct cifs_ace); + else /* cifsacl */ + nsecdesclen += 5 * sizeof(struct cifs_ace); + } else { /* chown */ + /* When ownership changes, changes new owner sid length could be different */ + nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2); + dacloffset = le32_to_cpu(pntsd->dacloffset); + if (dacloffset) { + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); + if (mode_from_sid) + nsecdesclen += + le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace); + else /* cifsacl */ + nsecdesclen += le16_to_cpu(dacl_ptr->size); + } + } + + /* + * Add three ACEs for owner, group, everyone getting rid of other ACEs + * as chmod disables ACEs and set the security descriptor. Allocate + * memory for the smb header, set security descriptor request security + * descriptor parameters, and security descriptor itself + */ + nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN); + pnntsd = kmalloc(nsecdesclen, GFP_KERNEL); + if (!pnntsd) { + kfree(pntsd); + cifs_put_tlink(tlink); + return -ENOMEM; + } + + rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid, + mode_from_sid, id_from_sid, &aclflag); + + cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); + + if (ops->set_acl == NULL) + rc = -EOPNOTSUPP; + + if (!rc) { + /* Set the security descriptor */ + rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); + cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); + } + cifs_put_tlink(tlink); + + kfree(pnntsd); + kfree(pntsd); + return rc; +} diff --git a/fs/smb/client/cifsacl.h b/fs/smb/client/cifsacl.h new file mode 100644 index 000000000000..ccbfc754bd3c --- /dev/null +++ b/fs/smb/client/cifsacl.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFSACL_H +#define _CIFSACL_H + +#define NUM_AUTHS (6) /* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ + +#define READ_BIT 0x4 +#define WRITE_BIT 0x2 +#define EXEC_BIT 0x1 + +#define ACL_OWNER_MASK 0700 +#define ACL_GROUP_MASK 0070 +#define ACL_EVERYONE_MASK 0007 + +#define UBITSHIFT 6 +#define GBITSHIFT 3 + +#define ACCESS_ALLOWED 0 +#define ACCESS_DENIED 1 + +#define SIDOWNER 1 +#define SIDGROUP 2 + +/* + * Security Descriptor length containing DACL with 3 ACEs (one each for + * owner, group and world). + */ +#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \ + sizeof(struct cifs_acl) + \ + (sizeof(struct cifs_ace) * 4)) + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8: max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ + +struct cifs_ntsd { + __le16 revision; /* revision level */ + __le16 type; + __le32 osidoffset; + __le32 gsidoffset; + __le32 sacloffset; + __le32 dacloffset; +} __attribute__((packed)); + +struct cifs_sid { + __u8 revision; /* revision level */ + __u8 num_subauth; + __u8 authority[NUM_AUTHS]; + __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +} __attribute__((packed)); + +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) + +struct cifs_acl { + __le16 revision; /* revision level */ + __le16 size; + __le32 num_aces; +} __attribute__((packed)); + +/* ACE types - see MS-DTYP 2.4.4.1 */ +#define ACCESS_ALLOWED_ACE_TYPE 0x00 +#define ACCESS_DENIED_ACE_TYPE 0x01 +#define SYSTEM_AUDIT_ACE_TYPE 0x02 +#define SYSTEM_ALARM_ACE_TYPE 0x03 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 + +/* ACE flags */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPAGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +struct cifs_ace { + __u8 type; /* see above and MS-DTYP 2.4.4.1 */ + __u8 flags; + __le16 size; + __le32 access_req; + struct cifs_sid sid; /* ie UUID of user or group who gets these perms */ +} __attribute__((packed)); + +/* + * The current SMB3 form of security descriptor is similar to what was used for + * cifs (see above) but some fields are split, and fields in the struct below + * matches names of fields to the spec, MS-DTYP (see sections 2.4.5 and + * 2.4.6). Note that "CamelCase" fields are used in this struct in order to + * match the MS-DTYP and MS-SMB2 specs which define the wire format. + */ +struct smb3_sd { + __u8 Revision; /* revision level, MUST be one */ + __u8 Sbz1; /* only meaningful if 'RM' flag set below */ + __le16 Control; + __le32 OffsetOwner; + __le32 OffsetGroup; + __le32 OffsetSacl; + __le32 OffsetDacl; +} __packed; + +/* Meaning of 'Control' field flags */ +#define ACL_CONTROL_SR 0x8000 /* Self relative */ +#define ACL_CONTROL_RM 0x4000 /* Resource manager control bits */ +#define ACL_CONTROL_PS 0x2000 /* SACL protected from inherits */ +#define ACL_CONTROL_PD 0x1000 /* DACL protected from inherits */ +#define ACL_CONTROL_SI 0x0800 /* SACL Auto-Inherited */ +#define ACL_CONTROL_DI 0x0400 /* DACL Auto-Inherited */ +#define ACL_CONTROL_SC 0x0200 /* SACL computed through inheritance */ +#define ACL_CONTROL_DC 0x0100 /* DACL computed through inheritence */ +#define ACL_CONTROL_SS 0x0080 /* Create server ACL */ +#define ACL_CONTROL_DT 0x0040 /* DACL provided by trusted source */ +#define ACL_CONTROL_SD 0x0020 /* SACL defaulted */ +#define ACL_CONTROL_SP 0x0010 /* SACL is present on object */ +#define ACL_CONTROL_DD 0x0008 /* DACL defaulted */ +#define ACL_CONTROL_DP 0x0004 /* DACL is present on object */ +#define ACL_CONTROL_GD 0x0002 /* Group was defaulted */ +#define ACL_CONTROL_OD 0x0001 /* User was defaulted */ + +/* Meaning of AclRevision flags */ +#define ACL_REVISION 0x02 /* See section 2.4.4.1 of MS-DTYP */ +#define ACL_REVISION_DS 0x04 /* Additional AceTypes allowed */ + +struct smb3_acl { + u8 AclRevision; /* revision level */ + u8 Sbz1; /* MBZ */ + __le16 AclSize; + __le16 AceCount; + __le16 Sbz2; /* MBZ */ +} __packed; + +/* + * Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ +struct owner_sid { + u8 Revision; + u8 NumAuth; + u8 Authority[6]; + __le32 SubAuthorities[3]; +} __packed; + +struct owner_group_sids { + struct owner_sid owner; + struct owner_sid group; +} __packed; + +/* + * Minimum security identifier can be one for system defined Users + * and Groups such as NULL SID and World or Built-in accounts such + * as Administrator and Guest and consists of + * Revision + Num (Sub)Auths + Authority + Domain (one Subauthority) + */ +#define MIN_SID_LEN (1 + 1 + 6 + 4) /* in bytes */ + +/* + * Minimum security descriptor can be one without any SACL and DACL and can + * consist of revision, type, and two sids of minimum size for owner and group + */ +#define MIN_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + (2 * MIN_SID_LEN)) + +#endif /* _CIFSACL_H */ diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c new file mode 100644 index 000000000000..d0ac2648c0d6 --- /dev/null +++ b/fs/smb/client/cifsencrypt.c @@ -0,0 +1,733 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP + * for more detailed information + * + * Copyright (C) International Business Machines Corp., 2005,2013 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "cifsproto.h" +#include "ntlmssp.h" +#include +#include +#include +#include +#include "../common/arc4.h" +#include + +int __cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature, + struct shash_desc *shash) +{ + int i; + int rc; + struct kvec *iov = rqst->rq_iov; + int n_vec = rqst->rq_nvec; + + /* iov[0] is actual data and not the rfc1002 length for SMB2+ */ + if (!is_smb1(server)) { + if (iov[0].iov_len <= 4) + return -EIO; + i = 0; + } else { + if (n_vec < 2 || iov[0].iov_len != 4) + return -EIO; + i = 1; /* skip rfc1002 length */ + } + + for (; i < n_vec; i++) { + if (iov[i].iov_len == 0) + continue; + if (iov[i].iov_base == NULL) { + cifs_dbg(VFS, "null iovec entry\n"); + return -EIO; + } + + rc = crypto_shash_update(shash, + iov[i].iov_base, iov[i].iov_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with payload\n", + __func__); + return rc; + } + } + + /* now hash over the rq_pages array */ + for (i = 0; i < rqst->rq_npages; i++) { + void *kaddr; + unsigned int len, offset; + + rqst_page_get_length(rqst, i, &len, &offset); + + kaddr = (char *) kmap(rqst->rq_pages[i]) + offset; + + rc = crypto_shash_update(shash, kaddr, len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with payload\n", + __func__); + kunmap(rqst->rq_pages[i]); + return rc; + } + + kunmap(rqst->rq_pages[i]); + } + + rc = crypto_shash_final(shash, signature); + if (rc) + cifs_dbg(VFS, "%s: Could not generate hash\n", __func__); + + return rc; +} + +/* + * Calculate and return the CIFS signature based on the mac key and SMB PDU. + * The 16 byte signature must be allocated by the caller. Note we only use the + * 1st eight bytes and that the smb header signature field on input contains + * the sequence number before this function is called. Also, this function + * should be called with the server->srv_mutex held. + */ +static int cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature) +{ + int rc; + + if (!rqst->rq_iov || !signature || !server) + return -EINVAL; + + rc = cifs_alloc_hash("md5", &server->secmech.md5); + if (rc) + return -1; + + rc = crypto_shash_init(server->secmech.md5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init md5\n", __func__); + return rc; + } + + rc = crypto_shash_update(server->secmech.md5, + server->session_key.response, server->session_key.len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + return rc; + } + + return __cifs_calc_signature(rqst, server, signature, server->secmech.md5); +} + +/* must be called with server->srv_mutex held */ +int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number) +{ + int rc = 0; + char smb_signature[20]; + struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return -EIO; + + if ((cifs_pdu == NULL) || (server == NULL)) + return -EINVAL; + + spin_lock(&server->srv_lock); + if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || + server->tcpStatus == CifsNeedNegotiate) { + spin_unlock(&server->srv_lock); + return rc; + } + spin_unlock(&server->srv_lock); + + if (!server->session_estab) { + memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); + return rc; + } + + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(server->sequence_number); + cifs_pdu->Signature.Sequence.Reserved = 0; + + *pexpected_response_sequence_number = ++server->sequence_number; + ++server->sequence_number; + + rc = cifs_calc_signature(rqst, server, smb_signature); + if (rc) + memset(cifs_pdu->Signature.SecuritySignature, 0, 8); + else + memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); + + return rc; +} + +int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence) +{ + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = n_vec }; + + return cifs_sign_rqst(&rqst, server, pexpected_response_sequence); +} + +/* must be called with server->srv_mutex held */ +int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number) +{ + struct kvec iov[2]; + + iov[0].iov_base = cifs_pdu; + iov[0].iov_len = 4; + iov[1].iov_base = (char *)cifs_pdu + 4; + iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length); + + return cifs_sign_smbv(iov, 2, server, + pexpected_response_sequence_number); +} + +int cifs_verify_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + __u32 expected_sequence_number) +{ + unsigned int rc; + char server_response_sig[8]; + char what_we_think_sig_should_be[20]; + struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return -EIO; + + if (cifs_pdu == NULL || server == NULL) + return -EINVAL; + + if (!server->session_estab) + return 0; + + if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { + struct smb_com_lock_req *pSMB = + (struct smb_com_lock_req *)cifs_pdu; + if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) + return 0; + } + + /* BB what if signatures are supposed to be on for session but + server does not send one? BB */ + + /* Do not need to verify session setups with signature "BSRSPYL " */ + if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0) + cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", + cifs_pdu->Command); + + /* save off the origiginal signature so we can modify the smb and check + its signature against what the server sent */ + memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8); + + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(expected_sequence_number); + cifs_pdu->Signature.Sequence.Reserved = 0; + + cifs_server_lock(server); + rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be); + cifs_server_unlock(server); + + if (rc) + return rc; + +/* cifs_dump_mem("what we think it should be: ", + what_we_think_sig_should_be, 16); */ + + if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) + return -EACCES; + else + return 0; + +} + +/* Build a proper attribute value/target info pairs blob. + * Fill in netbios and dns domain name and workstation name + * and client time (total five av pairs and + one end of fields indicator. + * Allocate domain name which gets freed when session struct is deallocated. + */ +static int +build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp) +{ + unsigned int dlen; + unsigned int size = 2 * sizeof(struct ntlmssp2_name); + char *defdmname = "WORKGROUP"; + unsigned char *blobptr; + struct ntlmssp2_name *attrptr; + + if (!ses->domainName) { + ses->domainName = kstrdup(defdmname, GFP_KERNEL); + if (!ses->domainName) + return -ENOMEM; + } + + dlen = strlen(ses->domainName); + + /* + * The length of this blob is two times the size of a + * structure (av pair) which holds name/size + * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) + + * unicode length of a netbios domain name + */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.len = size + 2 * dlen; + ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); + if (!ses->auth_key.response) { + ses->auth_key.len = 0; + return -ENOMEM; + } + + blobptr = ses->auth_key.response; + attrptr = (struct ntlmssp2_name *) blobptr; + + /* + * As defined in MS-NTLM 3.3.2, just this av pair field + * is sufficient as part of the temp + */ + attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); + attrptr->length = cpu_to_le16(2 * dlen); + blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); + cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp); + + return 0; +} + +/* Server has provided av pairs/target info in the type 2 challenge + * packet and we have plucked it and stored within smb session. + * We parse that blob here to find netbios domain name to be used + * as part of ntlmv2 authentication (in Target String), if not already + * specified on the command line. + * If this function returns without any error but without fetching + * domain name, authentication may fail against some server but + * may not fail against other (those who are not very particular + * about target string i.e. for some, just user name might suffice. + */ +static int +find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp) +{ + unsigned int attrsize; + unsigned int type; + unsigned int onesize = sizeof(struct ntlmssp2_name); + unsigned char *blobptr; + unsigned char *blobend; + struct ntlmssp2_name *attrptr; + + if (!ses->auth_key.len || !ses->auth_key.response) + return 0; + + blobptr = ses->auth_key.response; + blobend = blobptr + ses->auth_key.len; + + while (blobptr + onesize < blobend) { + attrptr = (struct ntlmssp2_name *) blobptr; + type = le16_to_cpu(attrptr->type); + if (type == NTLMSSP_AV_EOL) + break; + blobptr += 2; /* advance attr type */ + attrsize = le16_to_cpu(attrptr->length); + blobptr += 2; /* advance attr size */ + if (blobptr + attrsize > blobend) + break; + if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { + if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN) + break; + if (!ses->domainName) { + ses->domainName = + kmalloc(attrsize + 1, GFP_KERNEL); + if (!ses->domainName) + return -ENOMEM; + cifs_from_utf16(ses->domainName, + (__le16 *)blobptr, attrsize, attrsize, + nls_cp, NO_MAP_UNI_RSVD); + break; + } + } + blobptr += attrsize; /* advance attr value */ + } + + return 0; +} + +/* Server has provided av pairs/target info in the type 2 challenge + * packet and we have plucked it and stored within smb session. + * We parse that blob here to find the server given timestamp + * as part of ntlmv2 authentication (or local current time as + * default in case of failure) + */ +static __le64 +find_timestamp(struct cifs_ses *ses) +{ + unsigned int attrsize; + unsigned int type; + unsigned int onesize = sizeof(struct ntlmssp2_name); + unsigned char *blobptr; + unsigned char *blobend; + struct ntlmssp2_name *attrptr; + struct timespec64 ts; + + if (!ses->auth_key.len || !ses->auth_key.response) + return 0; + + blobptr = ses->auth_key.response; + blobend = blobptr + ses->auth_key.len; + + while (blobptr + onesize < blobend) { + attrptr = (struct ntlmssp2_name *) blobptr; + type = le16_to_cpu(attrptr->type); + if (type == NTLMSSP_AV_EOL) + break; + blobptr += 2; /* advance attr type */ + attrsize = le16_to_cpu(attrptr->length); + blobptr += 2; /* advance attr size */ + if (blobptr + attrsize > blobend) + break; + if (type == NTLMSSP_AV_TIMESTAMP) { + if (attrsize == sizeof(u64)) + return *((__le64 *)blobptr); + } + blobptr += attrsize; /* advance attr value */ + } + + ktime_get_real_ts64(&ts); + return cpu_to_le64(cifs_UnixTimeToNT(ts)); +} + +static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, + const struct nls_table *nls_cp) +{ + int rc = 0; + int len; + char nt_hash[CIFS_NTHASH_SIZE]; + __le16 *user; + wchar_t *domain; + wchar_t *server; + + if (!ses->server->secmech.hmacmd5) { + cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); + return -1; + } + + /* calculate md4 hash of password */ + E_md4hash(ses->password, nt_hash, nls_cp); + + rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, nt_hash, + CIFS_NTHASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set NT Hash as a key\n", __func__); + return rc; + } + + rc = crypto_shash_init(ses->server->secmech.hmacmd5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); + return rc; + } + + /* convert ses->user_name to unicode */ + len = ses->user_name ? strlen(ses->user_name) : 0; + user = kmalloc(2 + (len * 2), GFP_KERNEL); + if (user == NULL) { + rc = -ENOMEM; + return rc; + } + + if (len) { + len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp); + UniStrupr(user); + } else { + memset(user, '\0', 2); + } + + rc = crypto_shash_update(ses->server->secmech.hmacmd5, + (char *)user, 2 * len); + kfree(user); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with user\n", __func__); + return rc; + } + + /* convert ses->domainName to unicode and uppercase */ + if (ses->domainName) { + len = strlen(ses->domainName); + + domain = kmalloc(2 + (len * 2), GFP_KERNEL); + if (domain == NULL) { + rc = -ENOMEM; + return rc; + } + len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len, + nls_cp); + rc = + crypto_shash_update(ses->server->secmech.hmacmd5, + (char *)domain, 2 * len); + kfree(domain); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with domain\n", + __func__); + return rc; + } + } else { + /* We use ses->ip_addr if no domain name available */ + len = strlen(ses->ip_addr); + + server = kmalloc(2 + (len * 2), GFP_KERNEL); + if (server == NULL) { + rc = -ENOMEM; + return rc; + } + len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, + nls_cp); + rc = + crypto_shash_update(ses->server->secmech.hmacmd5, + (char *)server, 2 * len); + kfree(server); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with server\n", + __func__); + return rc; + } + } + + rc = crypto_shash_final(ses->server->secmech.hmacmd5, + ntlmv2_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + + return rc; +} + +static int +CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) +{ + int rc; + struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *) + (ses->auth_key.response + CIFS_SESS_KEY_SIZE); + unsigned int hash_len; + + /* The MD5 hash starts at challenge_key.key */ + hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE + + offsetof(struct ntlmv2_resp, challenge.key[0])); + + if (!ses->server->secmech.hmacmd5) { + cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); + return -1; + } + + rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, + ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", + __func__); + return rc; + } + + rc = crypto_shash_init(ses->server->secmech.hmacmd5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); + return rc; + } + + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) + memcpy(ntlmv2->challenge.key, + ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); + else + memcpy(ntlmv2->challenge.key, + ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); + rc = crypto_shash_update(ses->server->secmech.hmacmd5, + ntlmv2->challenge.key, hash_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + return rc; + } + + /* Note that the MD5 digest over writes anon.challenge_key.key */ + rc = crypto_shash_final(ses->server->secmech.hmacmd5, + ntlmv2->ntlmv2_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + + return rc; +} + +int +setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) +{ + int rc; + int baselen; + unsigned int tilen; + struct ntlmv2_resp *ntlmv2; + char ntlmv2_hash[16]; + unsigned char *tiblob = NULL; /* target info blob */ + __le64 rsp_timestamp; + + if (nls_cp == NULL) { + cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__); + return -EINVAL; + } + + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { + if (!ses->domainName) { + if (ses->domainAuto) { + rc = find_domain_name(ses, nls_cp); + if (rc) { + cifs_dbg(VFS, "error %d finding domain name\n", + rc); + goto setup_ntlmv2_rsp_ret; + } + } else { + ses->domainName = kstrdup("", GFP_KERNEL); + } + } + } else { + rc = build_avpair_blob(ses, nls_cp); + if (rc) { + cifs_dbg(VFS, "error %d building av pair blob\n", rc); + goto setup_ntlmv2_rsp_ret; + } + } + + /* Must be within 5 minutes of the server (or in range +/-2h + * in case of Mac OS X), so simply carry over server timestamp + * (as Windows 7 does) + */ + rsp_timestamp = find_timestamp(ses); + + baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp); + tilen = ses->auth_key.len; + tiblob = ses->auth_key.response; + + ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL); + if (!ses->auth_key.response) { + rc = -ENOMEM; + ses->auth_key.len = 0; + goto setup_ntlmv2_rsp_ret; + } + ses->auth_key.len += baselen; + + ntlmv2 = (struct ntlmv2_resp *) + (ses->auth_key.response + CIFS_SESS_KEY_SIZE); + ntlmv2->blob_signature = cpu_to_le32(0x00000101); + ntlmv2->reserved = 0; + ntlmv2->time = rsp_timestamp; + + get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); + ntlmv2->reserved2 = 0; + + memcpy(ses->auth_key.response + baselen, tiblob, tilen); + + cifs_server_lock(ses->server); + + rc = cifs_alloc_hash("hmac(md5)", &ses->server->secmech.hmacmd5); + if (rc) { + goto unlock; + } + + /* calculate ntlmv2_hash */ + rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); + if (rc) { + cifs_dbg(VFS, "Could not get v2 hash rc %d\n", rc); + goto unlock; + } + + /* calculate first part of the client response (CR1) */ + rc = CalcNTLMv2_response(ses, ntlmv2_hash); + if (rc) { + cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc); + goto unlock; + } + + /* now calculate the session key for NTLMv2 */ + rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, + ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", + __func__); + goto unlock; + } + + rc = crypto_shash_init(ses->server->secmech.hmacmd5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); + goto unlock; + } + + rc = crypto_shash_update(ses->server->secmech.hmacmd5, + ntlmv2->ntlmv2_hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + goto unlock; + } + + rc = crypto_shash_final(ses->server->secmech.hmacmd5, + ses->auth_key.response); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + +unlock: + cifs_server_unlock(ses->server); +setup_ntlmv2_rsp_ret: + kfree_sensitive(tiblob); + + return rc; +} + +int +calc_seckey(struct cifs_ses *ses) +{ + unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ + struct arc4_ctx *ctx_arc4; + + if (fips_enabled) + return -ENODEV; + + get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); + + ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); + if (!ctx_arc4) { + cifs_dbg(VFS, "Could not allocate arc4 context\n"); + return -ENOMEM; + } + + cifs_arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE); + cifs_arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key, + CIFS_CPHTXT_SIZE); + + /* make secondary_key/nonce as session key */ + memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); + /* and make len as that of session key only */ + ses->auth_key.len = CIFS_SESS_KEY_SIZE; + + memzero_explicit(sec_key, CIFS_SESS_KEY_SIZE); + kfree_sensitive(ctx_arc4); + return 0; +} + +void +cifs_crypto_secmech_release(struct TCP_Server_Info *server) +{ + cifs_free_hash(&server->secmech.aes_cmac); + cifs_free_hash(&server->secmech.hmacsha256); + cifs_free_hash(&server->secmech.md5); + cifs_free_hash(&server->secmech.sha512); + cifs_free_hash(&server->secmech.hmacmd5); + + if (server->secmech.enc) { + crypto_free_aead(server->secmech.enc); + server->secmech.enc = NULL; + } + + if (server->secmech.dec) { + crypto_free_aead(server->secmech.dec); + server->secmech.dec = NULL; + } +} diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c new file mode 100644 index 000000000000..078df1e2dd18 --- /dev/null +++ b/fs/smb/client/cifsfs.c @@ -0,0 +1,1857 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Common Internet FileSystem (CIFS) client + * + */ + +/* Note that BB means BUGBUG (ie something to fix eventually) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#define DECLARE_GLOBALS_HERE +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include +#include +#include "cifs_spnego.h" +#include "fscache.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#ifdef CONFIG_CIFS_SWN_UPCALL +#include "netlink.h" +#endif +#include "fs_context.h" +#include "cached_dir.h" + +/* + * DOS dates from 1980/1/1 through 2107/12/31 + * Protocol specifications indicate the range should be to 119, which + * limits maximum year to 2099. But this range has not been checked. + */ +#define SMB_DATE_MAX (127<<9 | 12<<5 | 31) +#define SMB_DATE_MIN (0<<9 | 1<<5 | 1) +#define SMB_TIME_MAX (23<<11 | 59<<5 | 29) + +int cifsFYI = 0; +bool traceSMB; +bool enable_oplocks = true; +bool linuxExtEnabled = true; +bool lookupCacheEnabled = true; +bool disable_legacy_dialects; /* false by default */ +bool enable_gcm_256 = true; +bool require_gcm_256; /* false by default */ +bool enable_negotiate_signing; /* false by default */ +unsigned int global_secflags = CIFSSEC_DEF; +/* unsigned int ntlmv2_support = 0; */ +unsigned int sign_CIFS_PDUs = 1; + +/* + * Global transaction id (XID) information + */ +unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ +unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ +unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ +spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ + +/* + * Global counters, updated atomically + */ +atomic_t sesInfoAllocCount; +atomic_t tconInfoAllocCount; +atomic_t tcpSesNextId; +atomic_t tcpSesAllocCount; +atomic_t tcpSesReconnectCount; +atomic_t tconInfoReconnectCount; + +atomic_t mid_count; +atomic_t buf_alloc_count; +atomic_t small_buf_alloc_count; +#ifdef CONFIG_CIFS_STATS2 +atomic_t total_buf_alloc_count; +atomic_t total_small_buf_alloc_count; +#endif/* STATS2 */ +struct list_head cifs_tcp_ses_list; +spinlock_t cifs_tcp_ses_lock; +static const struct super_operations cifs_super_ops; +unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; +module_param(CIFSMaxBufSize, uint, 0444); +MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header) " + "for CIFS requests. " + "Default: 16384 Range: 8192 to 130048"); +unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; +module_param(cifs_min_rcv, uint, 0444); +MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: " + "1 to 64"); +unsigned int cifs_min_small = 30; +module_param(cifs_min_small, uint, 0444); +MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 " + "Range: 2 to 256"); +unsigned int cifs_max_pending = CIFS_MAX_REQ; +module_param(cifs_max_pending, uint, 0444); +MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server for " + "CIFS/SMB1 dialect (N/A for SMB3) " + "Default: 32767 Range: 2 to 32767."); +#ifdef CONFIG_CIFS_STATS2 +unsigned int slow_rsp_threshold = 1; +module_param(slow_rsp_threshold, uint, 0644); +MODULE_PARM_DESC(slow_rsp_threshold, "Amount of time (in seconds) to wait " + "before logging that a response is delayed. " + "Default: 1 (if set to 0 disables msg)."); +#endif /* STATS2 */ + +module_param(enable_oplocks, bool, 0644); +MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1"); + +module_param(enable_gcm_256, bool, 0644); +MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: n/N/0"); + +module_param(require_gcm_256, bool, 0644); +MODULE_PARM_DESC(require_gcm_256, "Require strongest (256 bit) GCM encryption. Default: n/N/0"); + +module_param(enable_negotiate_signing, bool, 0644); +MODULE_PARM_DESC(enable_negotiate_signing, "Enable negotiating packet signing algorithm with server. Default: n/N/0"); + +module_param(disable_legacy_dialects, bool, 0644); +MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be " + "helpful to restrict the ability to " + "override the default dialects (SMB2.1, " + "SMB3 and SMB3.02) on mount with old " + "dialects (CIFS/SMB1 and SMB2) since " + "vers=1.0 (CIFS/SMB1) and vers=2.0 are weaker" + " and less secure. Default: n/N/0"); + +extern mempool_t *cifs_sm_req_poolp; +extern mempool_t *cifs_req_poolp; +extern mempool_t *cifs_mid_poolp; + +struct workqueue_struct *cifsiod_wq; +struct workqueue_struct *decrypt_wq; +struct workqueue_struct *fileinfo_put_wq; +struct workqueue_struct *cifsoplockd_wq; +struct workqueue_struct *deferredclose_wq; +__u32 cifs_lock_secret; + +/* + * Bumps refcount for cifs super block. + * Note that it should be only called if a referece to VFS super block is + * already held, e.g. in open-type syscalls context. Otherwise it can race with + * atomic_dec_and_test in deactivate_locked_super. + */ +void +cifs_sb_active(struct super_block *sb) +{ + struct cifs_sb_info *server = CIFS_SB(sb); + + if (atomic_inc_return(&server->active) == 1) + atomic_inc(&sb->s_active); +} + +void +cifs_sb_deactive(struct super_block *sb) +{ + struct cifs_sb_info *server = CIFS_SB(sb); + + if (atomic_dec_and_test(&server->active)) + deactivate_super(sb); +} + +static int +cifs_read_super(struct super_block *sb) +{ + struct inode *inode; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct timespec64 ts; + int rc = 0; + + cifs_sb = CIFS_SB(sb); + tcon = cifs_sb_master_tcon(cifs_sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL) + sb->s_flags |= SB_POSIXACL; + + if (tcon->snapshot_time) + sb->s_flags |= SB_RDONLY; + + if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files) + sb->s_maxbytes = MAX_LFS_FILESIZE; + else + sb->s_maxbytes = MAX_NON_LFS; + + /* + * Some very old servers like DOS and OS/2 used 2 second granularity + * (while all current servers use 100ns granularity - see MS-DTYP) + * but 1 second is the maximum allowed granularity for the VFS + * so for old servers set time granularity to 1 second while for + * everything else (current servers) set it to 100ns. + */ + if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) && + ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) && + !tcon->unix_ext) { + sb->s_time_gran = 1000000000; /* 1 second is max allowed gran */ + ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MIN), 0, 0); + sb->s_time_min = ts.tv_sec; + ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MAX), + cpu_to_le16(SMB_TIME_MAX), 0); + sb->s_time_max = ts.tv_sec; + } else { + /* + * Almost every server, including all SMB2+, uses DCE TIME + * ie 100 nanosecond units, since 1601. See MS-DTYP and MS-FSCC + */ + sb->s_time_gran = 100; + ts = cifs_NTtimeToUnix(0); + sb->s_time_min = ts.tv_sec; + ts = cifs_NTtimeToUnix(cpu_to_le64(S64_MAX)); + sb->s_time_max = ts.tv_sec; + } + + sb->s_magic = CIFS_SUPER_MAGIC; + sb->s_op = &cifs_super_ops; + sb->s_xattr = cifs_xattr_handlers; + rc = super_setup_bdi(sb); + if (rc) + goto out_no_root; + /* tune readahead according to rsize if readahead size not set on mount */ + if (cifs_sb->ctx->rsize == 0) + cifs_sb->ctx->rsize = + tcon->ses->server->ops->negotiate_rsize(tcon, cifs_sb->ctx); + if (cifs_sb->ctx->rasize) + sb->s_bdi->ra_pages = cifs_sb->ctx->rasize / PAGE_SIZE; + else + sb->s_bdi->ra_pages = cifs_sb->ctx->rsize / PAGE_SIZE; + + sb->s_blocksize = CIFS_MAX_MSGSIZE; + sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ + inode = cifs_root_iget(sb); + + if (IS_ERR(inode)) { + rc = PTR_ERR(inode); + goto out_no_root; + } + + if (tcon->nocase) + sb->s_d_op = &cifs_ci_dentry_ops; + else + sb->s_d_op = &cifs_dentry_ops; + + sb->s_root = d_make_root(inode); + if (!sb->s_root) { + rc = -ENOMEM; + goto out_no_root; + } + +#ifdef CONFIG_CIFS_NFSD_EXPORT + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + cifs_dbg(FYI, "export ops supported\n"); + sb->s_export_op = &cifs_export_ops; + } +#endif /* CONFIG_CIFS_NFSD_EXPORT */ + + return 0; + +out_no_root: + cifs_dbg(VFS, "%s: get root inode failed\n", __func__); + return rc; +} + +static void cifs_kill_sb(struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + /* + * We ned to release all dentries for the cached directories + * before we kill the sb. + */ + if (cifs_sb->root) { + close_all_cached_dirs(cifs_sb); + + /* finally release root dentry */ + dput(cifs_sb->root); + cifs_sb->root = NULL; + } + + kill_anon_super(sb); + cifs_umount(cifs_sb); +} + +static int +cifs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int xid; + int rc = 0; + + xid = get_xid(); + + if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) > 0) + buf->f_namelen = + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); + else + buf->f_namelen = PATH_MAX; + + buf->f_fsid.val[0] = tcon->vol_serial_number; + /* are using part of create time for more randomness, see man statfs */ + buf->f_fsid.val[1] = (int)le64_to_cpu(tcon->vol_create_time); + + buf->f_files = 0; /* undefined */ + buf->f_ffree = 0; /* unlimited */ + + if (server->ops->queryfs) + rc = server->ops->queryfs(xid, tcon, cifs_sb, buf); + + free_xid(xid); + return rc; +} + +static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len) +{ + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + + if (server->ops->fallocate) + return server->ops->fallocate(file, tcon, mode, off, len); + + return -EOPNOTSUPP; +} + +static int cifs_permission(struct user_namespace *mnt_userns, + struct inode *inode, int mask) +{ + struct cifs_sb_info *cifs_sb; + + cifs_sb = CIFS_SB(inode->i_sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { + if ((mask & MAY_EXEC) && !execute_ok(inode)) + return -EACCES; + else + return 0; + } else /* file mode might have been restricted at mount time + on the client (above and beyond ACL on servers) for + servers which do not support setting and viewing mode bits, + so allowing client to check permissions is useful */ + return generic_permission(&init_user_ns, inode, mask); +} + +static struct kmem_cache *cifs_inode_cachep; +static struct kmem_cache *cifs_req_cachep; +static struct kmem_cache *cifs_mid_cachep; +static struct kmem_cache *cifs_sm_req_cachep; +mempool_t *cifs_sm_req_poolp; +mempool_t *cifs_req_poolp; +mempool_t *cifs_mid_poolp; + +static struct inode * +cifs_alloc_inode(struct super_block *sb) +{ + struct cifsInodeInfo *cifs_inode; + cifs_inode = alloc_inode_sb(sb, cifs_inode_cachep, GFP_KERNEL); + if (!cifs_inode) + return NULL; + cifs_inode->cifsAttrs = 0x20; /* default */ + cifs_inode->time = 0; + /* + * Until the file is open and we have gotten oplock info back from the + * server, can not assume caching of file data or metadata. + */ + cifs_set_oplock_level(cifs_inode, 0); + cifs_inode->flags = 0; + spin_lock_init(&cifs_inode->writers_lock); + cifs_inode->writers = 0; + cifs_inode->netfs.inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ + cifs_inode->server_eof = 0; + cifs_inode->uniqueid = 0; + cifs_inode->createtime = 0; + cifs_inode->epoch = 0; + spin_lock_init(&cifs_inode->open_file_lock); + generate_random_uuid(cifs_inode->lease_key); + cifs_inode->symlink_target = NULL; + + /* + * Can not set i_flags here - they get immediately overwritten to zero + * by the VFS. + */ + /* cifs_inode->netfs.inode.i_flags = S_NOATIME | S_NOCMTIME; */ + INIT_LIST_HEAD(&cifs_inode->openFileList); + INIT_LIST_HEAD(&cifs_inode->llist); + INIT_LIST_HEAD(&cifs_inode->deferred_closes); + spin_lock_init(&cifs_inode->deferred_lock); + return &cifs_inode->netfs.inode; +} + +static void +cifs_free_inode(struct inode *inode) +{ + struct cifsInodeInfo *cinode = CIFS_I(inode); + + if (S_ISLNK(inode->i_mode)) + kfree(cinode->symlink_target); + kmem_cache_free(cifs_inode_cachep, cinode); +} + +static void +cifs_evict_inode(struct inode *inode) +{ + truncate_inode_pages_final(&inode->i_data); + if (inode->i_state & I_PINNING_FSCACHE_WB) + cifs_fscache_unuse_inode_cookie(inode, true); + cifs_fscache_release_inode_cookie(inode); + clear_inode(inode); +} + +static void +cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server) +{ + struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; + + seq_puts(s, ",addr="); + + switch (server->dstaddr.ss_family) { + case AF_INET: + seq_printf(s, "%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: + seq_printf(s, "%pI6", &sa6->sin6_addr.s6_addr); + if (sa6->sin6_scope_id) + seq_printf(s, "%%%u", sa6->sin6_scope_id); + break; + default: + seq_puts(s, "(unknown)"); + } + if (server->rdma) + seq_puts(s, ",rdma"); +} + +static void +cifs_show_security(struct seq_file *s, struct cifs_ses *ses) +{ + if (ses->sectype == Unspecified) { + if (ses->user_name == NULL) + seq_puts(s, ",sec=none"); + return; + } + + seq_puts(s, ",sec="); + + switch (ses->sectype) { + case NTLMv2: + seq_puts(s, "ntlmv2"); + break; + case Kerberos: + seq_puts(s, "krb5"); + break; + case RawNTLMSSP: + seq_puts(s, "ntlmssp"); + break; + default: + /* shouldn't ever happen */ + seq_puts(s, "unknown"); + break; + } + + if (ses->sign) + seq_puts(s, "i"); + + if (ses->sectype == Kerberos) + seq_printf(s, ",cruid=%u", + from_kuid_munged(&init_user_ns, ses->cred_uid)); +} + +static void +cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) +{ + seq_puts(s, ",cache="); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) + seq_puts(s, "strict"); + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) + seq_puts(s, "none"); + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) + seq_puts(s, "singleclient"); /* assume only one client access */ + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) + seq_puts(s, "ro"); /* read only caching assumed */ + else + seq_puts(s, "loose"); +} + +/* + * cifs_show_devname() is used so we show the mount device name with correct + * format (e.g. forward slashes vs. back slashes) in /proc/mounts + */ +static int cifs_show_devname(struct seq_file *m, struct dentry *root) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); + char *devname = kstrdup(cifs_sb->ctx->source, GFP_KERNEL); + + if (devname == NULL) + seq_puts(m, "none"); + else { + convert_delimiter(devname, '/'); + /* escape all spaces in share names */ + seq_escape(m, devname, " \t"); + kfree(devname); + } + return 0; +} + +/* + * cifs_show_options() is for displaying mount options in /proc/mounts. + * Not all settable options are displayed but most of the important + * ones are. + */ +static int +cifs_show_options(struct seq_file *s, struct dentry *root) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct sockaddr *srcaddr; + srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; + + seq_show_option(s, "vers", tcon->ses->server->vals->version_string); + cifs_show_security(s, tcon->ses); + cifs_show_cache_flavor(s, cifs_sb); + + if (tcon->no_lease) + seq_puts(s, ",nolease"); + if (cifs_sb->ctx->multiuser) + seq_puts(s, ",multiuser"); + else if (tcon->ses->user_name) + seq_show_option(s, "username", tcon->ses->user_name); + + if (tcon->ses->domainName && tcon->ses->domainName[0] != 0) + seq_show_option(s, "domain", tcon->ses->domainName); + + if (srcaddr->sa_family != AF_UNSPEC) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; + saddr4 = (struct sockaddr_in *)srcaddr; + saddr6 = (struct sockaddr_in6 *)srcaddr; + if (srcaddr->sa_family == AF_INET6) + seq_printf(s, ",srcaddr=%pI6c", + &saddr6->sin6_addr); + else if (srcaddr->sa_family == AF_INET) + seq_printf(s, ",srcaddr=%pI4", + &saddr4->sin_addr.s_addr); + else + seq_printf(s, ",srcaddr=BAD-AF:%i", + (int)(srcaddr->sa_family)); + } + + seq_printf(s, ",uid=%u", + from_kuid_munged(&init_user_ns, cifs_sb->ctx->linux_uid)); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) + seq_puts(s, ",forceuid"); + else + seq_puts(s, ",noforceuid"); + + seq_printf(s, ",gid=%u", + from_kgid_munged(&init_user_ns, cifs_sb->ctx->linux_gid)); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) + seq_puts(s, ",forcegid"); + else + seq_puts(s, ",noforcegid"); + + cifs_show_address(s, tcon->ses->server); + + if (!tcon->unix_ext) + seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho", + cifs_sb->ctx->file_mode, + cifs_sb->ctx->dir_mode); + if (cifs_sb->ctx->iocharset) + seq_printf(s, ",iocharset=%s", cifs_sb->ctx->iocharset); + if (tcon->seal) + seq_puts(s, ",seal"); + else if (tcon->ses->server->ignore_signature) + seq_puts(s, ",signloosely"); + if (tcon->nocase) + seq_puts(s, ",nocase"); + if (tcon->nodelete) + seq_puts(s, ",nodelete"); + if (cifs_sb->ctx->no_sparse) + seq_puts(s, ",nosparse"); + if (tcon->local_lease) + seq_puts(s, ",locallease"); + if (tcon->retry) + seq_puts(s, ",hard"); + else + seq_puts(s, ",soft"); + if (tcon->use_persistent) + seq_puts(s, ",persistenthandles"); + else if (tcon->use_resilient) + seq_puts(s, ",resilienthandles"); + if (tcon->posix_extensions) + seq_puts(s, ",posix"); + else if (tcon->unix_ext) + seq_puts(s, ",unix"); + else + seq_puts(s, ",nounix"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) + seq_puts(s, ",nodfs"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) + seq_puts(s, ",posixpaths"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) + seq_puts(s, ",setuids"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + seq_puts(s, ",idsfromsid"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + seq_puts(s, ",serverino"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + seq_puts(s, ",rwpidforward"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) + seq_puts(s, ",forcemand"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + seq_puts(s, ",nouser_xattr"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) + seq_puts(s, ",mapchars"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) + seq_puts(s, ",mapposix"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) + seq_puts(s, ",sfu"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + seq_puts(s, ",nobrl"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_HANDLE_CACHE) + seq_puts(s, ",nohandlecache"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) + seq_puts(s, ",modefromsid"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) + seq_puts(s, ",cifsacl"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) + seq_puts(s, ",dynperm"); + if (root->d_sb->s_flags & SB_POSIXACL) + seq_puts(s, ",acl"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + seq_puts(s, ",mfsymlinks"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) + seq_puts(s, ",fsc"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC) + seq_puts(s, ",nostrictsync"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + seq_puts(s, ",noperm"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) + seq_printf(s, ",backupuid=%u", + from_kuid_munged(&init_user_ns, + cifs_sb->ctx->backupuid)); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) + seq_printf(s, ",backupgid=%u", + from_kgid_munged(&init_user_ns, + cifs_sb->ctx->backupgid)); + + seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize); + seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize); + seq_printf(s, ",bsize=%u", cifs_sb->ctx->bsize); + if (cifs_sb->ctx->rasize) + seq_printf(s, ",rasize=%u", cifs_sb->ctx->rasize); + if (tcon->ses->server->min_offload) + seq_printf(s, ",esize=%u", tcon->ses->server->min_offload); + seq_printf(s, ",echo_interval=%lu", + tcon->ses->server->echo_interval / HZ); + + /* Only display the following if overridden on mount */ + if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE) + seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits); + if (tcon->ses->server->tcp_nodelay) + seq_puts(s, ",tcpnodelay"); + if (tcon->ses->server->noautotune) + seq_puts(s, ",noautotune"); + if (tcon->ses->server->noblocksnd) + seq_puts(s, ",noblocksend"); + + if (tcon->snapshot_time) + seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); + if (tcon->handle_timeout) + seq_printf(s, ",handletimeout=%u", tcon->handle_timeout); + + /* + * Display file and directory attribute timeout in seconds. + * If file and directory attribute timeout the same then actimeo + * was likely specified on mount + */ + if (cifs_sb->ctx->acdirmax == cifs_sb->ctx->acregmax) + seq_printf(s, ",actimeo=%lu", cifs_sb->ctx->acregmax / HZ); + else { + seq_printf(s, ",acdirmax=%lu", cifs_sb->ctx->acdirmax / HZ); + seq_printf(s, ",acregmax=%lu", cifs_sb->ctx->acregmax / HZ); + } + seq_printf(s, ",closetimeo=%lu", cifs_sb->ctx->closetimeo / HZ); + + if (tcon->ses->chan_max > 1) + seq_printf(s, ",multichannel,max_channels=%zu", + tcon->ses->chan_max); + + if (tcon->use_witness) + seq_puts(s, ",witness"); + + return 0; +} + +static void cifs_umount_begin(struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon; + + if (cifs_sb == NULL) + return; + + tcon = cifs_sb_master_tcon(cifs_sb); + + spin_lock(&cifs_tcp_ses_lock); + spin_lock(&tcon->tc_lock); + if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) { + /* we have other mounts to same share or we have + already tried to umount this and woken up + all waiting network requests, nothing to do */ + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return; + } + /* + * can not set tcon->status to TID_EXITING yet since we don't know if umount -f will + * fail later (e.g. due to open files). TID_EXITING will be set just before tdis req sent + */ + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + + cifs_close_all_deferred_files(tcon); + /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ + /* cancel_notify_requests(tcon); */ + if (tcon->ses && tcon->ses->server) { + cifs_dbg(FYI, "wake up tasks now - umount begin not complete\n"); + wake_up_all(&tcon->ses->server->request_q); + wake_up_all(&tcon->ses->server->response_q); + msleep(1); /* yield */ + /* we have to kick the requests once more */ + wake_up_all(&tcon->ses->server->response_q); + msleep(1); + } + + return; +} + +static int cifs_freeze(struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon; + + if (cifs_sb == NULL) + return 0; + + tcon = cifs_sb_master_tcon(cifs_sb); + + cifs_close_all_deferred_files(tcon); + return 0; +} + +#ifdef CONFIG_CIFS_STATS2 +static int cifs_show_stats(struct seq_file *s, struct dentry *root) +{ + /* BB FIXME */ + return 0; +} +#endif + +static int cifs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + fscache_unpin_writeback(wbc, cifs_inode_cookie(inode)); + return 0; +} + +static int cifs_drop_inode(struct inode *inode) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + /* no serverino => unconditional eviction */ + return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) || + generic_drop_inode(inode); +} + +static const struct super_operations cifs_super_ops = { + .statfs = cifs_statfs, + .alloc_inode = cifs_alloc_inode, + .write_inode = cifs_write_inode, + .free_inode = cifs_free_inode, + .drop_inode = cifs_drop_inode, + .evict_inode = cifs_evict_inode, +/* .show_path = cifs_show_path, */ /* Would we ever need show path? */ + .show_devname = cifs_show_devname, +/* .delete_inode = cifs_delete_inode, */ /* Do not need above + function unless later we add lazy close of inodes or unless the + kernel forgets to call us with the same number of releases (closes) + as opens */ + .show_options = cifs_show_options, + .umount_begin = cifs_umount_begin, + .freeze_fs = cifs_freeze, +#ifdef CONFIG_CIFS_STATS2 + .show_stats = cifs_show_stats, +#endif +}; + +/* + * Get root dentry from superblock according to prefix path mount option. + * Return dentry with refcount + 1 on success and NULL otherwise. + */ +static struct dentry * +cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb) +{ + struct dentry *dentry; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + char *full_path = NULL; + char *s, *p; + char sep; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) + return dget(sb->s_root); + + full_path = cifs_build_path_to_root(ctx, cifs_sb, + cifs_sb_master_tcon(cifs_sb), 0); + if (full_path == NULL) + return ERR_PTR(-ENOMEM); + + cifs_dbg(FYI, "Get root dentry for %s\n", full_path); + + sep = CIFS_DIR_SEP(cifs_sb); + dentry = dget(sb->s_root); + s = full_path; + + do { + struct inode *dir = d_inode(dentry); + struct dentry *child; + + if (!S_ISDIR(dir->i_mode)) { + dput(dentry); + dentry = ERR_PTR(-ENOTDIR); + break; + } + + /* skip separators */ + while (*s == sep) + s++; + if (!*s) + break; + p = s++; + /* next separator */ + while (*s && *s != sep) + s++; + + child = lookup_positive_unlocked(p, dentry, s - p); + dput(dentry); + dentry = child; + } while (!IS_ERR(dentry)); + kfree(full_path); + return dentry; +} + +static int cifs_set_super(struct super_block *sb, void *data) +{ + struct cifs_mnt_data *mnt_data = data; + sb->s_fs_info = mnt_data->cifs_sb; + return set_anon_super(sb, NULL); +} + +struct dentry * +cifs_smb3_do_mount(struct file_system_type *fs_type, + int flags, struct smb3_fs_context *old_ctx) +{ + int rc; + struct super_block *sb = NULL; + struct cifs_sb_info *cifs_sb = NULL; + struct cifs_mnt_data mnt_data; + struct dentry *root; + + /* + * Prints in Kernel / CIFS log the attempted mount operation + * If CIFS_DEBUG && cifs_FYI + */ + if (cifsFYI) + cifs_dbg(FYI, "Devname: %s flags: %d\n", old_ctx->UNC, flags); + else + cifs_info("Attempting to mount %s\n", old_ctx->UNC); + + cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); + if (cifs_sb == NULL) { + root = ERR_PTR(-ENOMEM); + goto out; + } + + cifs_sb->ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); + if (!cifs_sb->ctx) { + root = ERR_PTR(-ENOMEM); + goto out; + } + rc = smb3_fs_context_dup(cifs_sb->ctx, old_ctx); + if (rc) { + root = ERR_PTR(rc); + goto out; + } + + rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL); + if (rc) { + root = ERR_PTR(rc); + goto out; + } + + rc = cifs_setup_cifs_sb(cifs_sb); + if (rc) { + root = ERR_PTR(rc); + goto out; + } + + rc = cifs_mount(cifs_sb, cifs_sb->ctx); + if (rc) { + if (!(flags & SB_SILENT)) + cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n", + rc); + root = ERR_PTR(rc); + goto out; + } + + mnt_data.ctx = cifs_sb->ctx; + mnt_data.cifs_sb = cifs_sb; + mnt_data.flags = flags; + + /* BB should we make this contingent on mount parm? */ + flags |= SB_NODIRATIME | SB_NOATIME; + + sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data); + if (IS_ERR(sb)) { + root = ERR_CAST(sb); + cifs_umount(cifs_sb); + cifs_sb = NULL; + goto out; + } + + if (sb->s_root) { + cifs_dbg(FYI, "Use existing superblock\n"); + cifs_umount(cifs_sb); + cifs_sb = NULL; + } else { + rc = cifs_read_super(sb); + if (rc) { + root = ERR_PTR(rc); + goto out_super; + } + + sb->s_flags |= SB_ACTIVE; + } + + root = cifs_get_root(cifs_sb ? cifs_sb->ctx : old_ctx, sb); + if (IS_ERR(root)) + goto out_super; + + if (cifs_sb) + cifs_sb->root = dget(root); + + cifs_dbg(FYI, "dentry root is: %p\n", root); + return root; + +out_super: + deactivate_locked_super(sb); + return root; +out: + if (cifs_sb) { + if (!sb || IS_ERR(sb)) { /* otherwise kill_sb will handle */ + kfree(cifs_sb->prepath); + smb3_cleanup_fs_context(cifs_sb->ctx); + kfree(cifs_sb); + } + } + return root; +} + + +static ssize_t +cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t rc; + struct inode *inode = file_inode(iocb->ki_filp); + + if (iocb->ki_flags & IOCB_DIRECT) + return cifs_user_readv(iocb, iter); + + rc = cifs_revalidate_mapping(inode); + if (rc) + return rc; + + return generic_file_read_iter(iocb, iter); +} + +static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct cifsInodeInfo *cinode = CIFS_I(inode); + ssize_t written; + int rc; + + if (iocb->ki_filp->f_flags & O_DIRECT) { + written = cifs_user_writev(iocb, from); + if (written > 0 && CIFS_CACHE_READ(cinode)) { + cifs_zap_mapping(inode); + cifs_dbg(FYI, + "Set no oplock for inode=%p after a write operation\n", + inode); + cinode->oplock = 0; + } + return written; + } + + written = cifs_get_writer(cinode); + if (written) + return written; + + written = generic_file_write_iter(iocb, from); + + if (CIFS_CACHE_WRITE(CIFS_I(inode))) + goto out; + + rc = filemap_fdatawrite(inode->i_mapping); + if (rc) + cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n", + rc, inode); + +out: + cifs_put_writer(cinode); + return written; +} + +static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) +{ + struct cifsFileInfo *cfile = file->private_data; + struct cifs_tcon *tcon; + + /* + * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate + * the cached file length + */ + if (whence != SEEK_SET && whence != SEEK_CUR) { + int rc; + struct inode *inode = file_inode(file); + + /* + * We need to be sure that all dirty pages are written and the + * server has the newest file length. + */ + if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && + inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + if (rc) { + mapping_set_error(inode->i_mapping, rc); + return rc; + } + } + /* + * Some applications poll for the file length in this strange + * way so we must seek to end on non-oplocked files by + * setting the revalidate time to zero. + */ + CIFS_I(inode)->time = 0; + + rc = cifs_revalidate_file_attr(file); + if (rc < 0) + return (loff_t)rc; + } + if (cfile && cfile->tlink) { + tcon = tlink_tcon(cfile->tlink); + if (tcon->ses->server->ops->llseek) + return tcon->ses->server->ops->llseek(file, tcon, + offset, whence); + } + return generic_file_llseek(file, offset, whence); +} + +static int +cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv) +{ + /* + * Note that this is called by vfs setlease with i_lock held to + * protect *lease from going away. + */ + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + + if (!(S_ISREG(inode->i_mode))) + return -EINVAL; + + /* Check if file is oplocked if this is request for new lease */ + if (arg == F_UNLCK || + ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || + ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode)))) + return generic_setlease(file, arg, lease, priv); + else if (tlink_tcon(cfile->tlink)->local_lease && + !CIFS_CACHE_READ(CIFS_I(inode))) + /* + * If the server claims to support oplock on this file, then we + * still need to check oplock even if the local_lease mount + * option is set, but there are servers which do not support + * oplock for which this mount option may be useful if the user + * knows that the file won't be changed on the server by anyone + * else. + */ + return generic_setlease(file, arg, lease, priv); + else + return -EAGAIN; +} + +struct file_system_type cifs_fs_type = { + .owner = THIS_MODULE, + .name = "cifs", + .init_fs_context = smb3_init_fs_context, + .parameters = smb3_fs_parameters, + .kill_sb = cifs_kill_sb, + .fs_flags = FS_RENAME_DOES_D_MOVE, +}; +MODULE_ALIAS_FS("cifs"); + +struct file_system_type smb3_fs_type = { + .owner = THIS_MODULE, + .name = "smb3", + .init_fs_context = smb3_init_fs_context, + .parameters = smb3_fs_parameters, + .kill_sb = cifs_kill_sb, + .fs_flags = FS_RENAME_DOES_D_MOVE, +}; +MODULE_ALIAS_FS("smb3"); +MODULE_ALIAS("smb3"); + +const struct inode_operations cifs_dir_inode_ops = { + .create = cifs_create, + .atomic_open = cifs_atomic_open, + .lookup = cifs_lookup, + .getattr = cifs_getattr, + .unlink = cifs_unlink, + .link = cifs_hardlink, + .mkdir = cifs_mkdir, + .rmdir = cifs_rmdir, + .rename = cifs_rename2, + .permission = cifs_permission, + .setattr = cifs_setattr, + .symlink = cifs_symlink, + .mknod = cifs_mknod, + .listxattr = cifs_listxattr, +}; + +const struct inode_operations cifs_file_inode_ops = { + .setattr = cifs_setattr, + .getattr = cifs_getattr, + .permission = cifs_permission, + .listxattr = cifs_listxattr, + .fiemap = cifs_fiemap, +}; + +const char *cifs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + char *target_path; + + target_path = kmalloc(PATH_MAX, GFP_KERNEL); + if (!target_path) + return ERR_PTR(-ENOMEM); + + spin_lock(&inode->i_lock); + if (likely(CIFS_I(inode)->symlink_target)) { + strscpy(target_path, CIFS_I(inode)->symlink_target, PATH_MAX); + } else { + kfree(target_path); + target_path = ERR_PTR(-EOPNOTSUPP); + } + spin_unlock(&inode->i_lock); + + if (!IS_ERR(target_path)) + set_delayed_call(done, kfree_link, target_path); + + return target_path; +} + +const struct inode_operations cifs_symlink_inode_ops = { + .get_link = cifs_get_link, + .permission = cifs_permission, + .listxattr = cifs_listxattr, +}; + +static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, loff_t len, + unsigned int remap_flags) +{ + struct inode *src_inode = file_inode(src_file); + struct inode *target_inode = file_inode(dst_file); + struct cifsFileInfo *smb_file_src = src_file->private_data; + struct cifsFileInfo *smb_file_target; + struct cifs_tcon *target_tcon; + unsigned int xid; + int rc; + + if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY)) + return -EINVAL; + + cifs_dbg(FYI, "clone range\n"); + + xid = get_xid(); + + if (!src_file->private_data || !dst_file->private_data) { + rc = -EBADF; + cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); + goto out; + } + + smb_file_target = dst_file->private_data; + target_tcon = tlink_tcon(smb_file_target->tlink); + + /* + * Note: cifs case is easier than btrfs since server responsible for + * checks for proper open modes and file type and if it wants + * server could even support copy of range where source = target + */ + lock_two_nondirectories(target_inode, src_inode); + + if (len == 0) + len = src_inode->i_size - off; + + cifs_dbg(FYI, "about to flush pages\n"); + /* should we flush first and last page first */ + truncate_inode_pages_range(&target_inode->i_data, destoff, + PAGE_ALIGN(destoff + len)-1); + + if (target_tcon->ses->server->ops->duplicate_extents) + rc = target_tcon->ses->server->ops->duplicate_extents(xid, + smb_file_src, smb_file_target, off, len, destoff); + else + rc = -EOPNOTSUPP; + + /* force revalidate of size and timestamps of target file now + that target is updated on the server */ + CIFS_I(target_inode)->time = 0; + /* although unlocking in the reverse order from locking is not + strictly necessary here it is a little cleaner to be consistent */ + unlock_two_nondirectories(src_inode, target_inode); +out: + free_xid(xid); + return rc < 0 ? rc : len; +} + +ssize_t cifs_file_copychunk_range(unsigned int xid, + struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, + size_t len, unsigned int flags) +{ + struct inode *src_inode = file_inode(src_file); + struct inode *target_inode = file_inode(dst_file); + struct cifsFileInfo *smb_file_src; + struct cifsFileInfo *smb_file_target; + struct cifs_tcon *src_tcon; + struct cifs_tcon *target_tcon; + ssize_t rc; + + cifs_dbg(FYI, "copychunk range\n"); + + if (!src_file->private_data || !dst_file->private_data) { + rc = -EBADF; + cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); + goto out; + } + + rc = -EXDEV; + smb_file_target = dst_file->private_data; + smb_file_src = src_file->private_data; + src_tcon = tlink_tcon(smb_file_src->tlink); + target_tcon = tlink_tcon(smb_file_target->tlink); + + if (src_tcon->ses != target_tcon->ses) { + cifs_dbg(VFS, "source and target of copy not on same server\n"); + goto out; + } + + rc = -EOPNOTSUPP; + if (!target_tcon->ses->server->ops->copychunk_range) + goto out; + + /* + * Note: cifs case is easier than btrfs since server responsible for + * checks for proper open modes and file type and if it wants + * server could even support copy of range where source = target + */ + lock_two_nondirectories(target_inode, src_inode); + + cifs_dbg(FYI, "about to flush pages\n"); + + rc = filemap_write_and_wait_range(src_inode->i_mapping, off, + off + len - 1); + if (rc) + goto unlock; + + /* should we flush first and last page first */ + truncate_inode_pages(&target_inode->i_data, 0); + + rc = file_modified(dst_file); + if (!rc) + rc = target_tcon->ses->server->ops->copychunk_range(xid, + smb_file_src, smb_file_target, off, len, destoff); + + file_accessed(src_file); + + /* force revalidate of size and timestamps of target file now + * that target is updated on the server + */ + CIFS_I(target_inode)->time = 0; + +unlock: + /* although unlocking in the reverse order from locking is not + * strictly necessary here it is a little cleaner to be consistent + */ + unlock_two_nondirectories(src_inode, target_inode); + +out: + return rc; +} + +/* + * Directory operations under CIFS/SMB2/SMB3 are synchronous, so fsync() + * is a dummy operation. + */ +static int cifs_dir_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + cifs_dbg(FYI, "Sync directory - name: %pD datasync: 0x%x\n", + file, datasync); + + return 0; +} + +static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, + size_t len, unsigned int flags) +{ + unsigned int xid = get_xid(); + ssize_t rc; + struct cifsFileInfo *cfile = dst_file->private_data; + + if (cfile->swapfile) { + rc = -EOPNOTSUPP; + free_xid(xid); + return rc; + } + + rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff, + len, flags); + free_xid(xid); + + if (rc == -EOPNOTSUPP || rc == -EXDEV) + rc = generic_copy_file_range(src_file, off, dst_file, + destoff, len, flags); + return rc; +} + +const struct file_operations cifs_file_ops = { + .read_iter = cifs_loose_read_iter, + .write_iter = cifs_file_write_iter, + .open = cifs_open, + .release = cifs_close, + .lock = cifs_lock, + .flock = cifs_flock, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_strict_ops = { + .read_iter = cifs_strict_readv, + .write_iter = cifs_strict_writev, + .open = cifs_open, + .release = cifs_close, + .lock = cifs_lock, + .flock = cifs_flock, + .fsync = cifs_strict_fsync, + .flush = cifs_flush, + .mmap = cifs_file_strict_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_direct_ops = { + .read_iter = cifs_direct_readv, + .write_iter = cifs_direct_writev, + .open = cifs_open, + .release = cifs_close, + .lock = cifs_lock, + .flock = cifs_flock, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .llseek = cifs_llseek, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_nobrl_ops = { + .read_iter = cifs_loose_read_iter, + .write_iter = cifs_file_write_iter, + .open = cifs_open, + .release = cifs_close, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_strict_nobrl_ops = { + .read_iter = cifs_strict_readv, + .write_iter = cifs_strict_writev, + .open = cifs_open, + .release = cifs_close, + .fsync = cifs_strict_fsync, + .flush = cifs_flush, + .mmap = cifs_file_strict_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_direct_nobrl_ops = { + .read_iter = cifs_direct_readv, + .write_iter = cifs_direct_writev, + .open = cifs_open, + .release = cifs_close, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .llseek = cifs_llseek, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_dir_ops = { + .iterate_shared = cifs_readdir, + .release = cifs_closedir, + .read = generic_read_dir, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .llseek = generic_file_llseek, + .fsync = cifs_dir_fsync, +}; + +static void +cifs_init_once(void *inode) +{ + struct cifsInodeInfo *cifsi = inode; + + inode_init_once(&cifsi->netfs.inode); + init_rwsem(&cifsi->lock_sem); +} + +static int __init +cifs_init_inodecache(void) +{ + cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", + sizeof(struct cifsInodeInfo), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD|SLAB_ACCOUNT), + cifs_init_once); + if (cifs_inode_cachep == NULL) + return -ENOMEM; + + return 0; +} + +static void +cifs_destroy_inodecache(void) +{ + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(cifs_inode_cachep); +} + +static int +cifs_init_request_bufs(void) +{ + /* + * SMB2 maximum header size is bigger than CIFS one - no problems to + * allocate some more bytes for CIFS. + */ + size_t max_hdr_size = MAX_SMB2_HDR_SIZE; + + if (CIFSMaxBufSize < 8192) { + /* Buffer size can not be smaller than 2 * PATH_MAX since maximum + Unicode path name has to fit in any SMB/CIFS path based frames */ + CIFSMaxBufSize = 8192; + } else if (CIFSMaxBufSize > 1024*127) { + CIFSMaxBufSize = 1024 * 127; + } else { + CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/ + } +/* + cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n", + CIFSMaxBufSize, CIFSMaxBufSize); +*/ + cifs_req_cachep = kmem_cache_create_usercopy("cifs_request", + CIFSMaxBufSize + max_hdr_size, 0, + SLAB_HWCACHE_ALIGN, 0, + CIFSMaxBufSize + max_hdr_size, + NULL); + if (cifs_req_cachep == NULL) + return -ENOMEM; + + if (cifs_min_rcv < 1) + cifs_min_rcv = 1; + else if (cifs_min_rcv > 64) { + cifs_min_rcv = 64; + cifs_dbg(VFS, "cifs_min_rcv set to maximum (64)\n"); + } + + cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv, + cifs_req_cachep); + + if (cifs_req_poolp == NULL) { + kmem_cache_destroy(cifs_req_cachep); + return -ENOMEM; + } + /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and + almost all handle based requests (but not write response, nor is it + sufficient for path based requests). A smaller size would have + been more efficient (compacting multiple slab items on one 4k page) + for the case in which debug was on, but this larger size allows + more SMBs to use small buffer alloc and is still much more + efficient to alloc 1 per page off the slab compared to 17K (5page) + alloc of large cifs buffers even when page debugging is on */ + cifs_sm_req_cachep = kmem_cache_create_usercopy("cifs_small_rq", + MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, + 0, MAX_CIFS_SMALL_BUFFER_SIZE, NULL); + if (cifs_sm_req_cachep == NULL) { + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + return -ENOMEM; + } + + if (cifs_min_small < 2) + cifs_min_small = 2; + else if (cifs_min_small > 256) { + cifs_min_small = 256; + cifs_dbg(FYI, "cifs_min_small set to maximum (256)\n"); + } + + cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small, + cifs_sm_req_cachep); + + if (cifs_sm_req_poolp == NULL) { + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + kmem_cache_destroy(cifs_sm_req_cachep); + return -ENOMEM; + } + + return 0; +} + +static void +cifs_destroy_request_bufs(void) +{ + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + mempool_destroy(cifs_sm_req_poolp); + kmem_cache_destroy(cifs_sm_req_cachep); +} + +static int init_mids(void) +{ + cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids", + sizeof(struct mid_q_entry), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (cifs_mid_cachep == NULL) + return -ENOMEM; + + /* 3 is a reasonable minimum number of simultaneous operations */ + cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep); + if (cifs_mid_poolp == NULL) { + kmem_cache_destroy(cifs_mid_cachep); + return -ENOMEM; + } + + return 0; +} + +static void destroy_mids(void) +{ + mempool_destroy(cifs_mid_poolp); + kmem_cache_destroy(cifs_mid_cachep); +} + +static int __init +init_cifs(void) +{ + int rc = 0; + cifs_proc_init(); + INIT_LIST_HEAD(&cifs_tcp_ses_list); +/* + * Initialize Global counters + */ + atomic_set(&sesInfoAllocCount, 0); + atomic_set(&tconInfoAllocCount, 0); + atomic_set(&tcpSesNextId, 0); + atomic_set(&tcpSesAllocCount, 0); + atomic_set(&tcpSesReconnectCount, 0); + atomic_set(&tconInfoReconnectCount, 0); + + atomic_set(&buf_alloc_count, 0); + atomic_set(&small_buf_alloc_count, 0); +#ifdef CONFIG_CIFS_STATS2 + atomic_set(&total_buf_alloc_count, 0); + atomic_set(&total_small_buf_alloc_count, 0); + if (slow_rsp_threshold < 1) + cifs_dbg(FYI, "slow_response_threshold msgs disabled\n"); + else if (slow_rsp_threshold > 32767) + cifs_dbg(VFS, + "slow response threshold set higher than recommended (0 to 32767)\n"); +#endif /* CONFIG_CIFS_STATS2 */ + + atomic_set(&mid_count, 0); + GlobalCurrentXid = 0; + GlobalTotalActiveXid = 0; + GlobalMaxActiveXid = 0; + spin_lock_init(&cifs_tcp_ses_lock); + spin_lock_init(&GlobalMid_Lock); + + cifs_lock_secret = get_random_u32(); + + if (cifs_max_pending < 2) { + cifs_max_pending = 2; + cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); + } else if (cifs_max_pending > CIFS_MAX_REQ) { + cifs_max_pending = CIFS_MAX_REQ; + cifs_dbg(FYI, "cifs_max_pending set to max of %u\n", + CIFS_MAX_REQ); + } + + cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!cifsiod_wq) { + rc = -ENOMEM; + goto out_clean_proc; + } + + /* + * Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3) + * so that we don't launch too many worker threads but + * Documentation/core-api/workqueue.rst recommends setting it to 0 + */ + + /* WQ_UNBOUND allows decrypt tasks to run on any CPU */ + decrypt_wq = alloc_workqueue("smb3decryptd", + WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!decrypt_wq) { + rc = -ENOMEM; + goto out_destroy_cifsiod_wq; + } + + fileinfo_put_wq = alloc_workqueue("cifsfileinfoput", + WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!fileinfo_put_wq) { + rc = -ENOMEM; + goto out_destroy_decrypt_wq; + } + + cifsoplockd_wq = alloc_workqueue("cifsoplockd", + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!cifsoplockd_wq) { + rc = -ENOMEM; + goto out_destroy_fileinfo_put_wq; + } + + deferredclose_wq = alloc_workqueue("deferredclose", + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!deferredclose_wq) { + rc = -ENOMEM; + goto out_destroy_cifsoplockd_wq; + } + + rc = cifs_init_inodecache(); + if (rc) + goto out_destroy_deferredclose_wq; + + rc = init_mids(); + if (rc) + goto out_destroy_inodecache; + + rc = cifs_init_request_bufs(); + if (rc) + goto out_destroy_mids; + +#ifdef CONFIG_CIFS_DFS_UPCALL + rc = dfs_cache_init(); + if (rc) + goto out_destroy_request_bufs; +#endif /* CONFIG_CIFS_DFS_UPCALL */ +#ifdef CONFIG_CIFS_UPCALL + rc = init_cifs_spnego(); + if (rc) + goto out_destroy_dfs_cache; +#endif /* CONFIG_CIFS_UPCALL */ +#ifdef CONFIG_CIFS_SWN_UPCALL + rc = cifs_genl_init(); + if (rc) + goto out_register_key_type; +#endif /* CONFIG_CIFS_SWN_UPCALL */ + + rc = init_cifs_idmap(); + if (rc) + goto out_cifs_swn_init; + + rc = register_filesystem(&cifs_fs_type); + if (rc) + goto out_init_cifs_idmap; + + rc = register_filesystem(&smb3_fs_type); + if (rc) { + unregister_filesystem(&cifs_fs_type); + goto out_init_cifs_idmap; + } + + return 0; + +out_init_cifs_idmap: + exit_cifs_idmap(); +out_cifs_swn_init: +#ifdef CONFIG_CIFS_SWN_UPCALL + cifs_genl_exit(); +out_register_key_type: +#endif +#ifdef CONFIG_CIFS_UPCALL + exit_cifs_spnego(); +out_destroy_dfs_cache: +#endif +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_cache_destroy(); +out_destroy_request_bufs: +#endif + cifs_destroy_request_bufs(); +out_destroy_mids: + destroy_mids(); +out_destroy_inodecache: + cifs_destroy_inodecache(); +out_destroy_deferredclose_wq: + destroy_workqueue(deferredclose_wq); +out_destroy_cifsoplockd_wq: + destroy_workqueue(cifsoplockd_wq); +out_destroy_fileinfo_put_wq: + destroy_workqueue(fileinfo_put_wq); +out_destroy_decrypt_wq: + destroy_workqueue(decrypt_wq); +out_destroy_cifsiod_wq: + destroy_workqueue(cifsiod_wq); +out_clean_proc: + cifs_proc_clean(); + return rc; +} + +static void __exit +exit_cifs(void) +{ + cifs_dbg(NOISY, "exit_smb3\n"); + unregister_filesystem(&cifs_fs_type); + unregister_filesystem(&smb3_fs_type); + cifs_dfs_release_automount_timer(); + exit_cifs_idmap(); +#ifdef CONFIG_CIFS_SWN_UPCALL + cifs_genl_exit(); +#endif +#ifdef CONFIG_CIFS_UPCALL + exit_cifs_spnego(); +#endif +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_cache_destroy(); +#endif + cifs_destroy_request_bufs(); + destroy_mids(); + cifs_destroy_inodecache(); + destroy_workqueue(deferredclose_wq); + destroy_workqueue(cifsoplockd_wq); + destroy_workqueue(decrypt_wq); + destroy_workqueue(fileinfo_put_wq); + destroy_workqueue(cifsiod_wq); + cifs_proc_clean(); +} + +MODULE_AUTHOR("Steve French"); +MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */ +MODULE_DESCRIPTION + ("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and " + "also older servers complying with the SNIA CIFS Specification)"); +MODULE_VERSION(CIFS_VERSION); +MODULE_SOFTDEP("ecb"); +MODULE_SOFTDEP("hmac"); +MODULE_SOFTDEP("md5"); +MODULE_SOFTDEP("nls"); +MODULE_SOFTDEP("aes"); +MODULE_SOFTDEP("cmac"); +MODULE_SOFTDEP("sha256"); +MODULE_SOFTDEP("sha512"); +MODULE_SOFTDEP("aead2"); +MODULE_SOFTDEP("ccm"); +MODULE_SOFTDEP("gcm"); +module_init(init_cifs) +module_exit(exit_cifs) diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h new file mode 100644 index 000000000000..b6c38896fb2d --- /dev/null +++ b/fs/smb/client/cifsfs.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002, 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFSFS_H +#define _CIFSFS_H + +#include + +#define ROOT_I 2 + +/* + * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down + * so that it will fit. We use hash_64 to convert the value to 31 bits, and + * then add 1, to ensure that we don't end up with a 0 as the value. + */ +static inline ino_t +cifs_uniqueid_to_ino_t(u64 fileid) +{ + if ((sizeof(ino_t)) < (sizeof(u64))) + return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1; + + return (ino_t)fileid; + +} + +static inline void cifs_set_time(struct dentry *dentry, unsigned long time) +{ + dentry->d_fsdata = (void *) time; +} + +static inline unsigned long cifs_get_time(struct dentry *dentry) +{ + return (unsigned long) dentry->d_fsdata; +} + +extern struct file_system_type cifs_fs_type, smb3_fs_type; +extern const struct address_space_operations cifs_addr_ops; +extern const struct address_space_operations cifs_addr_ops_smallbuf; + +/* Functions related to super block operations */ +extern void cifs_sb_active(struct super_block *sb); +extern void cifs_sb_deactive(struct super_block *sb); + +/* Functions related to inodes */ +extern const struct inode_operations cifs_dir_inode_ops; +extern struct inode *cifs_root_iget(struct super_block *); +extern int cifs_create(struct user_namespace *, struct inode *, + struct dentry *, umode_t, bool excl); +extern int cifs_atomic_open(struct inode *, struct dentry *, + struct file *, unsigned, umode_t); +extern struct dentry *cifs_lookup(struct inode *, struct dentry *, + unsigned int); +extern int cifs_unlink(struct inode *dir, struct dentry *dentry); +extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); +extern int cifs_mknod(struct user_namespace *, struct inode *, struct dentry *, + umode_t, dev_t); +extern int cifs_mkdir(struct user_namespace *, struct inode *, struct dentry *, + umode_t); +extern int cifs_rmdir(struct inode *, struct dentry *); +extern int cifs_rename2(struct user_namespace *, struct inode *, + struct dentry *, struct inode *, struct dentry *, + unsigned int); +extern int cifs_revalidate_file_attr(struct file *filp); +extern int cifs_revalidate_dentry_attr(struct dentry *); +extern int cifs_revalidate_file(struct file *filp); +extern int cifs_revalidate_dentry(struct dentry *); +extern int cifs_invalidate_mapping(struct inode *inode); +extern int cifs_revalidate_mapping(struct inode *inode); +extern int cifs_zap_mapping(struct inode *inode); +extern int cifs_getattr(struct user_namespace *, const struct path *, + struct kstat *, u32, unsigned int); +extern int cifs_setattr(struct user_namespace *, struct dentry *, + struct iattr *); +extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start, + u64 len); + +extern const struct inode_operations cifs_file_inode_ops; +extern const struct inode_operations cifs_symlink_inode_ops; +extern const struct inode_operations cifs_dfs_referral_inode_operations; + + +/* Functions related to files and directories */ +extern const struct file_operations cifs_file_ops; +extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ +extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */ +extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */ +extern const struct file_operations cifs_file_direct_nobrl_ops; +extern const struct file_operations cifs_file_strict_nobrl_ops; +extern int cifs_open(struct inode *inode, struct file *file); +extern int cifs_close(struct inode *inode, struct file *file); +extern int cifs_closedir(struct inode *inode, struct file *file); +extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from); +extern ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from); +extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); +extern int cifs_flock(struct file *pfile, int cmd, struct file_lock *plock); +extern int cifs_lock(struct file *, int, struct file_lock *); +extern int cifs_fsync(struct file *, loff_t, loff_t, int); +extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int); +extern int cifs_flush(struct file *, fl_owner_t id); +extern int cifs_file_mmap(struct file * , struct vm_area_struct *); +extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *); +extern const struct file_operations cifs_dir_ops; +extern int cifs_dir_open(struct inode *inode, struct file *file); +extern int cifs_readdir(struct file *file, struct dir_context *ctx); + +/* Functions related to dir entries */ +extern const struct dentry_operations cifs_dentry_ops; +extern const struct dentry_operations cifs_ci_dentry_ops; + +#ifdef CONFIG_CIFS_DFS_UPCALL +extern struct vfsmount *cifs_dfs_d_automount(struct path *path); +#else +static inline struct vfsmount *cifs_dfs_d_automount(struct path *path) +{ + return ERR_PTR(-EREMOTE); +} +#endif + +/* Functions related to symlinks */ +extern const char *cifs_get_link(struct dentry *, struct inode *, + struct delayed_call *); +extern int cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, const char *symname); + +#ifdef CONFIG_CIFS_XATTR +extern const struct xattr_handler *cifs_xattr_handlers[]; +extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); +#else +# define cifs_xattr_handlers NULL +# define cifs_listxattr NULL +#endif + +extern ssize_t cifs_file_copychunk_range(unsigned int xid, + struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, + size_t len, unsigned int flags); + +extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +extern void cifs_setsize(struct inode *inode, loff_t offset); +extern int cifs_truncate_page(struct address_space *mapping, loff_t from); + +struct smb3_fs_context; +extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, + int flags, struct smb3_fs_context *ctx); + +#ifdef CONFIG_CIFS_NFSD_EXPORT +extern const struct export_operations cifs_export_ops; +#endif /* CONFIG_CIFS_NFSD_EXPORT */ + +/* when changing internal version - update following two lines at same time */ +#define SMB3_PRODUCT_BUILD 40 +#define CIFS_VERSION "2.40" +#endif /* _CIFSFS_H */ diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h new file mode 100644 index 000000000000..a37afbb7e399 --- /dev/null +++ b/fs/smb/client/cifsglob.h @@ -0,0 +1,2208 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) + * + */ +#ifndef _CIFS_GLOB_H +#define _CIFS_GLOB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifs_fs_sb.h" +#include "cifsacl.h" +#include +#include +#include +#include "../common/smb2pdu.h" +#include "smb2pdu.h" + +#define SMB_PATH_MAX 260 +#define CIFS_PORT 445 +#define RFC1001_PORT 139 + +/* + * The sizes of various internal tables and strings + */ +#define MAX_UID_INFO 16 +#define MAX_SES_INFO 2 +#define MAX_TCON_INFO 4 + +#define MAX_TREE_SIZE (2 + CIFS_NI_MAXHOST + 1 + CIFS_MAX_SHARE_LEN + 1) + +#define CIFS_MIN_RCV_POOL 4 + +#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */ +/* + * default attribute cache timeout (jiffies) + */ +#define CIFS_DEF_ACTIMEO (1 * HZ) + +/* + * max attribute cache timeout (jiffies) - 2^30 + */ +#define CIFS_MAX_ACTIMEO (1 << 30) + +/* + * Max persistent and resilient handle timeout (milliseconds). + * Windows durable max was 960000 (16 minutes) + */ +#define SMB3_MAX_HANDLE_TIMEOUT 960000 + +/* + * MAX_REQ is the maximum number of requests that WE will send + * on one socket concurrently. + */ +#define CIFS_MAX_REQ 32767 + +#define RFC1001_NAME_LEN 15 +#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1) + +/* maximum length of ip addr as a string (including ipv6 and sctp) */ +#define SERVER_NAME_LENGTH 80 +#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) + +/* echo interval in seconds */ +#define SMB_ECHO_INTERVAL_MIN 1 +#define SMB_ECHO_INTERVAL_MAX 600 +#define SMB_ECHO_INTERVAL_DEFAULT 60 + +/* dns resolution intervals in seconds */ +#define SMB_DNS_RESOLVE_INTERVAL_MIN 120 +#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600 + +/* smb multichannel query server interfaces interval in seconds */ +#define SMB_INTERFACE_POLL_INTERVAL 600 + +/* maximum number of PDUs in one compound */ +#define MAX_COMPOUND 5 + +/* + * Default number of credits to keep available for SMB3. + * This value is chosen somewhat arbitrarily. The Windows client + * defaults to 128 credits, the Windows server allows clients up to + * 512 credits (or 8K for later versions), and the NetApp server + * does not limit clients at all. Choose a high enough default value + * such that the client shouldn't limit performance, but allow mount + * to override (until you approach 64K, where we limit credits to 65000 + * to reduce possibility of seeing more server credit overflow bugs. + */ +#define SMB2_MAX_CREDITS_AVAILABLE 32000 + +#include "cifspdu.h" + +#ifndef XATTR_DOS_ATTRIB +#define XATTR_DOS_ATTRIB "user.DOSATTRIB" +#endif + +#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */ + +/* + * CIFS vfs client Status information (based on what we know.) + */ + +/* associated with each connection */ +enum statusEnum { + CifsNew = 0, + CifsGood, + CifsExiting, + CifsNeedReconnect, + CifsNeedNegotiate, + CifsInNegotiate, +}; + +/* associated with each smb session */ +enum ses_status_enum { + SES_NEW = 0, + SES_GOOD, + SES_EXITING, + SES_NEED_RECON, + SES_IN_SETUP +}; + +/* associated with each tree connection to the server */ +enum tid_status_enum { + TID_NEW = 0, + TID_GOOD, + TID_EXITING, + TID_NEED_RECON, + TID_NEED_TCON, + TID_IN_TCON, + TID_NEED_FILES_INVALIDATE, /* currently unused */ + TID_IN_FILES_INVALIDATE +}; + +enum securityEnum { + Unspecified = 0, /* not specified */ + NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ + RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ + Kerberos, /* Kerberos via SPNEGO */ +}; + +struct session_key { + unsigned int len; + char *response; +}; + +/* crypto hashing related structure/fields, not specific to a sec mech */ +struct cifs_secmech { + struct shash_desc *hmacmd5; /* hmacmd5 hash function, for NTLMv2/CR1 hashes */ + struct shash_desc *md5; /* md5 hash function, for CIFS/SMB1 signatures */ + struct shash_desc *hmacsha256; /* hmac-sha256 hash function, for SMB2 signatures */ + struct shash_desc *sha512; /* sha512 hash function, for SMB3.1.1 preauth hash */ + struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */ + + struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */ + struct crypto_aead *dec; /* smb3 decryption AEAD TFM (AES-CCM and AES-GCM) */ +}; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + bool sesskey_per_smbsess; /* whether session key is per smb session */ + __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */ + __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlmssp */ +}; + +struct cifs_cred { + int uid; + int gid; + int mode; + int cecount; + struct cifs_sid osid; + struct cifs_sid gsid; + struct cifs_ntace *ntaces; + struct cifs_ace *aces; +}; + +struct cifs_open_info_data { + char *symlink_target; + union { + struct smb2_file_all_info fi; + struct smb311_posix_qinfo posix_fi; + }; +}; + +static inline void cifs_free_open_info(struct cifs_open_info_data *data) +{ + kfree(data->symlink_target); +} + +/* + ***************************************************************** + * Except the CIFS PDUs themselves all the + * globally interesting structs should go here + ***************************************************************** + */ + +/* + * A smb_rqst represents a complete request to be issued to a server. It's + * formed by a kvec array, followed by an array of pages. Page data is assumed + * to start at the beginning of the first page. + */ +struct smb_rqst { + struct kvec *rq_iov; /* array of kvecs */ + unsigned int rq_nvec; /* number of kvecs in array */ + struct page **rq_pages; /* pointer to array of page ptrs */ + unsigned int rq_offset; /* the offset to the 1st page */ + unsigned int rq_npages; /* number pages in array */ + unsigned int rq_pagesz; /* page size to use */ + unsigned int rq_tailsz; /* length of last page */ +}; + +struct mid_q_entry; +struct TCP_Server_Info; +struct cifsFileInfo; +struct cifs_ses; +struct cifs_tcon; +struct dfs_info3_param; +struct cifs_fattr; +struct smb3_fs_context; +struct cifs_fid; +struct cifs_readdata; +struct cifs_writedata; +struct cifs_io_parms; +struct cifs_search_info; +struct cifsInodeInfo; +struct cifs_open_parms; +struct cifs_credits; + +struct smb_version_operations { + int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *, + struct mid_q_entry *); + bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); + /* setup request: allocate mid, sign message */ + struct mid_q_entry *(*setup_request)(struct cifs_ses *, + struct TCP_Server_Info *, + struct smb_rqst *); + /* setup async request: allocate mid, sign message */ + struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *, + struct smb_rqst *); + /* check response: verify signature, map error */ + int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, + bool); + void (*add_credits)(struct TCP_Server_Info *server, + const struct cifs_credits *credits, + const int optype); + void (*set_credits)(struct TCP_Server_Info *, const int); + int * (*get_credits_field)(struct TCP_Server_Info *, const int); + unsigned int (*get_credits)(struct mid_q_entry *); + __u64 (*get_next_mid)(struct TCP_Server_Info *); + void (*revert_current_mid)(struct TCP_Server_Info *server, + const unsigned int val); + /* data offset from read response message */ + unsigned int (*read_data_offset)(char *); + /* + * Data length from read response message + * When in_remaining is true, the returned data length is in + * message field DataRemaining for out-of-band data read (e.g through + * Memory Registration RDMA write in SMBD). + * Otherwise, the returned data length is in message field DataLength. + */ + unsigned int (*read_data_length)(char *, bool in_remaining); + /* map smb to linux error */ + int (*map_error)(char *, bool); + /* find mid corresponding to the response message */ + struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *); + void (*dump_detail)(void *buf, struct TCP_Server_Info *ptcp_info); + void (*clear_stats)(struct cifs_tcon *); + void (*print_stats)(struct seq_file *m, struct cifs_tcon *); + void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); + /* verify the message */ + int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); + bool (*is_oplock_break)(char *, struct TCP_Server_Info *); + int (*handle_cancelled_mid)(struct mid_q_entry *, struct TCP_Server_Info *); + void (*downgrade_oplock)(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache); + /* process transaction2 response */ + bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, + char *, int); + /* check if we need to negotiate */ + bool (*need_neg)(struct TCP_Server_Info *); + /* negotiate to the server */ + int (*negotiate)(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); + /* set negotiated write size */ + unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); + /* set negotiated read size */ + unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); + /* setup smb sessionn */ + int (*sess_setup)(const unsigned int, struct cifs_ses *, + struct TCP_Server_Info *server, + const struct nls_table *); + /* close smb session */ + int (*logoff)(const unsigned int, struct cifs_ses *); + /* connect to a server share */ + int (*tree_connect)(const unsigned int, struct cifs_ses *, const char *, + struct cifs_tcon *, const struct nls_table *); + /* close tree connecion */ + int (*tree_disconnect)(const unsigned int, struct cifs_tcon *); + /* get DFS referrals */ + int (*get_dfs_refer)(const unsigned int, struct cifs_ses *, + const char *, struct dfs_info3_param **, + unsigned int *, const struct nls_table *, int); + /* informational QFS call */ + void (*qfs_tcon)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *); + /* check if a path is accessible or not */ + int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const char *); + /* query path data from the server */ + int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); + /* query file data from the server */ + int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct cifs_open_info_data *data); + /* query reparse tag from srv to determine which type of special file */ + int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *path, + __u32 *reparse_tag); + /* get server index number */ + int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid, + struct cifs_open_info_data *data); + /* set size by path */ + int (*set_path_size)(const unsigned int, struct cifs_tcon *, + const char *, __u64, struct cifs_sb_info *, bool); + /* set size by file handle */ + int (*set_file_size)(const unsigned int, struct cifs_tcon *, + struct cifsFileInfo *, __u64, bool); + /* set attributes */ + int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *, + const unsigned int); + int (*set_compression)(const unsigned int, struct cifs_tcon *, + struct cifsFileInfo *); + /* check if we can send an echo or nor */ + bool (*can_echo)(struct TCP_Server_Info *); + /* send echo request */ + int (*echo)(struct TCP_Server_Info *); + /* create directory */ + int (*posix_mkdir)(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb); + int (*mkdir)(const unsigned int xid, struct inode *inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *sb); + /* set info on created directory */ + void (*mkdir_setinfo)(struct inode *, const char *, + struct cifs_sb_info *, struct cifs_tcon *, + const unsigned int); + /* remove directory */ + int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *, + struct cifs_sb_info *); + /* unlink file */ + int (*unlink)(const unsigned int, struct cifs_tcon *, const char *, + struct cifs_sb_info *); + /* open, rename and delete file */ + int (*rename_pending_delete)(const char *, struct dentry *, + const unsigned int); + /* send rename request */ + int (*rename)(const unsigned int, struct cifs_tcon *, const char *, + const char *, struct cifs_sb_info *); + /* send create hardlink request */ + int (*create_hardlink)(const unsigned int, struct cifs_tcon *, + const char *, const char *, + struct cifs_sb_info *); + /* query symlink target */ + int (*query_symlink)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const char *, + char **, bool); + /* open a file for non-posix mounts */ + int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, + void *buf); + /* set fid protocol-specific info */ + void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); + /* close a file */ + void (*close)(const unsigned int, struct cifs_tcon *, + struct cifs_fid *); + /* close a file, returning file attributes and timestamps */ + void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *pfile_info); + /* send a flush request to the server */ + int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *); + /* async read from the server */ + int (*async_readv)(struct cifs_readdata *); + /* async write to the server */ + int (*async_writev)(struct cifs_writedata *, + void (*release)(struct kref *)); + /* sync read from the server */ + int (*sync_read)(const unsigned int, struct cifs_fid *, + struct cifs_io_parms *, unsigned int *, char **, + int *); + /* sync write to the server */ + int (*sync_write)(const unsigned int, struct cifs_fid *, + struct cifs_io_parms *, unsigned int *, struct kvec *, + unsigned long); + /* open dir, start readdir */ + int (*query_dir_first)(const unsigned int, struct cifs_tcon *, + const char *, struct cifs_sb_info *, + struct cifs_fid *, __u16, + struct cifs_search_info *); + /* continue readdir */ + int (*query_dir_next)(const unsigned int, struct cifs_tcon *, + struct cifs_fid *, + __u16, struct cifs_search_info *srch_inf); + /* close dir */ + int (*close_dir)(const unsigned int, struct cifs_tcon *, + struct cifs_fid *); + /* calculate a size of SMB message */ + unsigned int (*calc_smb_size)(void *buf); + /* check for STATUS_PENDING and process the response if yes */ + bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server); + /* check for STATUS_NETWORK_SESSION_EXPIRED */ + bool (*is_session_expired)(char *); + /* send oplock break response */ + int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid, + __u16 net_fid, struct cifsInodeInfo *cifs_inode); + /* query remote filesystem */ + int (*queryfs)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, struct kstatfs *); + /* send mandatory brlock to the server */ + int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64, + __u64, __u32, int, int, bool); + /* unlock range of mandatory locks */ + int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *, + const unsigned int); + /* push brlocks from the cache to the server */ + int (*push_mand_locks)(struct cifsFileInfo *); + /* get lease key of the inode */ + void (*get_lease_key)(struct inode *, struct cifs_fid *); + /* set lease key of the inode */ + void (*set_lease_key)(struct inode *, struct cifs_fid *); + /* generate new lease key */ + void (*new_lease_key)(struct cifs_fid *); + int (*generate_signingkey)(struct cifs_ses *ses, + struct TCP_Server_Info *server); + int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *, + bool allocate_crypto); + int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, + struct cifsFileInfo *src_file); + int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *src_file, void __user *); + int (*notify)(const unsigned int xid, struct file *pfile, + void __user *pbuf, bool return_changes); + int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const unsigned char *, + char *, unsigned int *); + int (*create_mf_symlink)(unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const unsigned char *, + char *, unsigned int *); + /* if we can do cache read operations */ + bool (*is_read_op)(__u32); + /* set oplock level for the inode */ + void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int, + bool *); + /* create lease context buffer for CREATE request */ + char * (*create_lease_buf)(u8 *lease_key, u8 oplock); + /* parse lease context buffer and return oplock/epoch info */ + __u8 (*parse_lease_buf)(void *buf, unsigned int *epoch, char *lkey); + ssize_t (*copychunk_range)(const unsigned int, + struct cifsFileInfo *src_file, + struct cifsFileInfo *target_file, + u64 src_off, u64 len, u64 dest_off); + int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src, + struct cifsFileInfo *target_file, u64 src_off, u64 len, + u64 dest_off); + int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); + ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *, + const unsigned char *, const unsigned char *, char *, + size_t, struct cifs_sb_info *); + int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *, + const char *, const void *, const __u16, + const struct nls_table *, struct cifs_sb_info *); + struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *, + const char *, u32 *, u32); + struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *, + const struct cifs_fid *, u32 *, u32); + int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, + int); + /* writepages retry size */ + unsigned int (*wp_retry_size)(struct inode *); + /* get mtu credits */ + int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, + unsigned int *, struct cifs_credits *); + /* adjust previously taken mtu credits to request size */ + int (*adjust_credits)(struct TCP_Server_Info *server, + struct cifs_credits *credits, + const unsigned int payload_size); + /* check if we need to issue closedir */ + bool (*dir_needs_close)(struct cifsFileInfo *); + long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, + loff_t); + /* init transform request - used for encryption for now */ + int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst, + struct smb_rqst *, struct smb_rqst *); + int (*is_transform_hdr)(void *buf); + int (*receive_transform)(struct TCP_Server_Info *, + struct mid_q_entry **, char **, int *); + enum securityEnum (*select_sectype)(struct TCP_Server_Info *, + enum securityEnum); + int (*next_header)(char *); + /* ioctl passthrough for query_info */ + int (*ioctl_query_info)(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + __le16 *path, int is_dir, + unsigned long p); + /* make unix special files (block, char, fifo, socket) */ + int (*make_node)(unsigned int xid, + struct inode *inode, + struct dentry *dentry, + struct cifs_tcon *tcon, + const char *full_path, + umode_t mode, + dev_t device_number); + /* version specific fiemap implementation */ + int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *, + struct fiemap_extent_info *, u64, u64); + /* version specific llseek implementation */ + loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int); + /* Check for STATUS_IO_TIMEOUT */ + bool (*is_status_io_timeout)(char *buf); + /* Check for STATUS_NETWORK_NAME_DELETED */ + void (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); +}; + +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __u32 req_capabilities; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_preamble_size; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + __le16 lock_cmd; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; +}; + +#define HEADER_SIZE(server) (server->vals->header_size) +#define MAX_HEADER_SIZE(server) (server->vals->max_header_size) +#define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size) +#define MID_HEADER_SIZE(server) (HEADER_SIZE(server) - 1 - HEADER_PREAMBLE_SIZE(server)) + +/** + * CIFS superblock mount flags (mnt_cifs_flags) to consider when + * trying to reuse existing superblock for a new mount + */ +#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ + CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \ + CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \ + CIFS_MOUNT_MAP_SFM_CHR | \ + CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \ + CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \ + CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \ + CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \ + CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \ + CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ + CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ + CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ + CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \ + CIFS_MOUNT_RO_CACHE | CIFS_MOUNT_RW_CACHE) + +/** + * Generic VFS superblock mount flags (s_flags) to consider when + * trying to reuse existing superblock for a new mount + */ +#define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \ + SB_NODEV | SB_SYNCHRONOUS) + +struct cifs_mnt_data { + struct cifs_sb_info *cifs_sb; + struct smb3_fs_context *ctx; + int flags; +}; + +static inline unsigned int +get_rfc1002_length(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void +inc_rfc1001_len(void *buf, int count) +{ + be32_add_cpu((__be32 *)buf, count); +} + +struct TCP_Server_Info { + struct list_head tcp_ses_list; + struct list_head smb_ses_list; + spinlock_t srv_lock; /* protect anything here that is not protected */ + __u64 conn_id; /* connection identifier (useful for debugging) */ + int srv_count; /* reference counter */ + /* 15 character server name + 0x20 16th byte indicating type = srv */ + char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; + struct smb_version_operations *ops; + struct smb_version_values *vals; + /* updates to tcpStatus protected by cifs_tcp_ses_lock */ + enum statusEnum tcpStatus; /* what we think the status is */ + char *hostname; /* hostname portion of UNC string */ + struct socket *ssocket; + struct sockaddr_storage dstaddr; + struct sockaddr_storage srcaddr; /* locally bind to this IP */ +#ifdef CONFIG_NET_NS + struct net *net; +#endif + wait_queue_head_t response_q; + wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ + spinlock_t mid_lock; /* protect mid queue and it's entries */ + struct list_head pending_mid_q; + bool noblocksnd; /* use blocking sendmsg */ + bool noautotune; /* do not autotune send buf sizes */ + bool nosharesock; + bool tcp_nodelay; + unsigned int credits; /* send no more requests at once */ + unsigned int max_credits; /* can override large 32000 default at mnt */ + unsigned int in_flight; /* number of requests on the wire to server */ + unsigned int max_in_flight; /* max number of requests that were on wire */ + spinlock_t req_lock; /* protect the two values above */ + struct mutex _srv_mutex; + unsigned int nofs_flag; + struct task_struct *tsk; + char server_GUID[16]; + __u16 sec_mode; + bool sign; /* is signing enabled on this connection? */ + bool ignore_signature:1; /* skip validation of signatures in SMB2/3 rsp */ + bool session_estab; /* mark when very first sess is established */ + int echo_credits; /* echo reserved slots */ + int oplock_credits; /* oplock break reserved slots */ + bool echoes:1; /* enable echoes */ + __u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */ + u16 dialect; /* dialect index that server chose */ + bool oplocks:1; /* enable oplocks */ + unsigned int maxReq; /* Clients should submit no more */ + /* than maxReq distinct unanswered SMBs to the server when using */ + /* multiplexed reads or writes (for SMB1/CIFS only, not SMB2/SMB3) */ + unsigned int maxBuf; /* maxBuf specifies the maximum */ + /* message size the server can send or receive for non-raw SMBs */ + /* maxBuf is returned by SMB NegotiateProtocol so maxBuf is only 0 */ + /* when socket is setup (and during reconnect) before NegProt sent */ + unsigned int max_rw; /* maxRw specifies the maximum */ + /* message size the server can send or receive for */ + /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ + unsigned int capabilities; /* selective disabling of caps by smb sess */ + int timeAdj; /* Adjust for difference in server time zone in sec */ + __u64 CurrentMid; /* multiplex id - rotating counter, protected by GlobalMid_Lock */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ + /* 16th byte of RFC1001 workstation name is always null */ + char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; + __u32 sequence_number; /* for signing, protected by srv_mutex */ + __u32 reconnect_instance; /* incremented on each reconnect */ + struct session_key session_key; + unsigned long lstrp; /* when we got last response from this server */ + struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ +#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */ +#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */ + char negflavor; /* NEGOTIATE response flavor */ + /* extended security flavors that server supports */ + bool sec_ntlmssp; /* supports NTLMSSP */ + bool sec_kerberosu2u; /* supports U2U Kerberos */ + bool sec_kerberos; /* supports plain Kerberos */ + bool sec_mskerberos; /* supports legacy MS Kerberos */ + bool large_buf; /* is current buffer large? */ + /* use SMBD connection instead of socket */ + bool rdma; + /* point to the SMBD connection if RDMA is used instead of socket */ + struct smbd_connection *smbd_conn; + struct delayed_work echo; /* echo ping workqueue job */ + struct delayed_work resolve; /* dns resolution workqueue job */ + char *smallbuf; /* pointer to current "small" buffer */ + char *bigbuf; /* pointer to current "big" buffer */ + /* Total size of this PDU. Only valid from cifs_demultiplex_thread */ + unsigned int pdu_size; + unsigned int total_read; /* total amount of data read in this pass */ + atomic_t in_send; /* requests trying to send */ + atomic_t num_waiters; /* blocked waiting to get in sendrecv */ +#ifdef CONFIG_CIFS_STATS2 + atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */ + atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */ + __u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */ + __u32 slowest_cmd[NUMBER_OF_SMB2_COMMANDS]; + __u32 fastest_cmd[NUMBER_OF_SMB2_COMMANDS]; +#endif /* STATS2 */ + unsigned int max_read; + unsigned int max_write; + unsigned int min_offload; + __le16 compress_algorithm; + __u16 signing_algorithm; + __le16 cipher_type; + /* save initital negprot hash */ + __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; + bool signing_negotiated; /* true if valid signing context rcvd from server */ + bool posix_ext_supported; + struct delayed_work reconnect; /* reconnect workqueue job */ + struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ + unsigned long echo_interval; + + /* + * Number of targets available for reconnect. The more targets + * the more tasks have to wait to let the demultiplex thread + * reconnect. + */ + int nr_targets; + bool noblockcnt; /* use non-blocking connect() */ + + /* + * If this is a session channel, + * primary_server holds the ref-counted + * pointer to primary channel connection for the session. + */ +#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) + struct TCP_Server_Info *primary_server; + +#ifdef CONFIG_CIFS_SWN_UPCALL + bool use_swn_dstaddr; + struct sockaddr_storage swn_dstaddr; +#endif +#ifdef CONFIG_CIFS_DFS_UPCALL + bool is_dfs_conn; /* if a dfs connection */ + struct mutex refpath_lock; /* protects leaf_fullpath */ + /* + * Canonical DFS full paths that were used to chase referrals in mount and reconnect. + * + * origin_fullpath: first or original referral path + * leaf_fullpath: last referral path (might be changed due to nested links in reconnect) + * + * current_fullpath: pointer to either origin_fullpath or leaf_fullpath + * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect() + * + * format: \\HOST\SHARE\[OPTIONAL PATH] + */ + char *origin_fullpath, *leaf_fullpath, *current_fullpath; +#endif +}; + +static inline bool is_smb1(struct TCP_Server_Info *server) +{ + return HEADER_PREAMBLE_SIZE(server) != 0; +} + +static inline void cifs_server_lock(struct TCP_Server_Info *server) +{ + unsigned int nofs_flag = memalloc_nofs_save(); + + mutex_lock(&server->_srv_mutex); + server->nofs_flag = nofs_flag; +} + +static inline void cifs_server_unlock(struct TCP_Server_Info *server) +{ + unsigned int nofs_flag = server->nofs_flag; + + mutex_unlock(&server->_srv_mutex); + memalloc_nofs_restore(nofs_flag); +} + +struct cifs_credits { + unsigned int value; + unsigned int instance; +}; + +static inline unsigned int +in_flight(struct TCP_Server_Info *server) +{ + unsigned int num; + spin_lock(&server->req_lock); + num = server->in_flight; + spin_unlock(&server->req_lock); + return num; +} + +static inline bool +has_credits(struct TCP_Server_Info *server, int *credits, int num_credits) +{ + int num; + spin_lock(&server->req_lock); + num = *credits; + spin_unlock(&server->req_lock); + return num >= num_credits; +} + +static inline void +add_credits(struct TCP_Server_Info *server, const struct cifs_credits *credits, + const int optype) +{ + server->ops->add_credits(server, credits, optype); +} + +static inline void +add_credits_and_wake_if(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) +{ + if (credits->value) { + server->ops->add_credits(server, credits, optype); + wake_up(&server->request_q); + } +} + +static inline void +set_credits(struct TCP_Server_Info *server, const int val) +{ + server->ops->set_credits(server, val); +} + +static inline int +adjust_credits(struct TCP_Server_Info *server, struct cifs_credits *credits, + const unsigned int payload_size) +{ + return server->ops->adjust_credits ? + server->ops->adjust_credits(server, credits, payload_size) : 0; +} + +static inline __le64 +get_next_mid64(struct TCP_Server_Info *server) +{ + return cpu_to_le64(server->ops->get_next_mid(server)); +} + +static inline __le16 +get_next_mid(struct TCP_Server_Info *server) +{ + __u16 mid = server->ops->get_next_mid(server); + /* + * The value in the SMB header should be little endian for easy + * on-the-wire decoding. + */ + return cpu_to_le16(mid); +} + +static inline void +revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) +{ + if (server->ops->revert_current_mid) + server->ops->revert_current_mid(server, val); +} + +static inline void +revert_current_mid_from_hdr(struct TCP_Server_Info *server, + const struct smb2_hdr *shdr) +{ + unsigned int num = le16_to_cpu(shdr->CreditCharge); + + return revert_current_mid(server, num > 0 ? num : 1); +} + +static inline __u16 +get_mid(const struct smb_hdr *smb) +{ + return le16_to_cpu(smb->Mid); +} + +static inline bool +compare_mid(__u16 mid, const struct smb_hdr *smb) +{ + return mid == le16_to_cpu(smb->Mid); +} + +/* + * When the server supports very large reads and writes via POSIX extensions, + * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not + * including the RFC1001 length. + * + * Note that this might make for "interesting" allocation problems during + * writeback however as we have to allocate an array of pointers for the + * pages. A 16M write means ~32kb page array with PAGE_SIZE == 4096. + * + * For reads, there is a similar problem as we need to allocate an array + * of kvecs to handle the receive, though that should only need to be done + * once. + */ +#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4) +#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4) + +/* + * When the server doesn't allow large posix writes, only allow a rsize/wsize + * of 2^17-1 minus the size of the call header. That allows for a read or + * write up to the maximum size described by RFC1002. + */ +#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4) +#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4) + +#define CIFS_DEFAULT_IOSIZE (1024 * 1024) + +/* + * Windows only supports a max of 60kb reads and 65535 byte writes. Default to + * those values when posix extensions aren't in force. In actuality here, we + * use 65536 to allow for a write that is a multiple of 4k. Most servers seem + * to be ok with the extra byte even though Windows doesn't send writes that + * are that large. + * + * Citation: + * + * https://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx + */ +#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) +#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) + +/* + * Macros to allow the TCP_Server_Info->net field and related code to drop out + * when CONFIG_NET_NS isn't set. + */ + +#ifdef CONFIG_NET_NS + +static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) +{ + return srv->net; +} + +static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) +{ + srv->net = net; +} + +#else + +static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) +{ + return &init_net; +} + +static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) +{ +} + +#endif + +struct cifs_server_iface { + struct list_head iface_head; + struct kref refcount; + size_t speed; + unsigned int rdma_capable : 1; + unsigned int rss_capable : 1; + unsigned int is_active : 1; /* unset if non existent */ + struct sockaddr_storage sockaddr; +}; + +/* release iface when last ref is dropped */ +static inline void +release_iface(struct kref *ref) +{ + struct cifs_server_iface *iface = container_of(ref, + struct cifs_server_iface, + refcount); + list_del_init(&iface->iface_head); + kfree(iface); +} + +/* + * compare two interfaces a and b + * return 0 if everything matches. + * return 1 if a has higher link speed, or rdma capable, or rss capable + * return -1 otherwise. + */ +static inline int +iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b) +{ + int cmp_ret = 0; + + WARN_ON(!a || !b); + if (a->speed == b->speed) { + if (a->rdma_capable == b->rdma_capable) { + if (a->rss_capable == b->rss_capable) { + cmp_ret = memcmp(&a->sockaddr, &b->sockaddr, + sizeof(a->sockaddr)); + if (!cmp_ret) + return 0; + else if (cmp_ret > 0) + return 1; + else + return -1; + } else if (a->rss_capable > b->rss_capable) + return 1; + else + return -1; + } else if (a->rdma_capable > b->rdma_capable) + return 1; + else + return -1; + } else if (a->speed > b->speed) + return 1; + else + return -1; +} + +struct cifs_chan { + unsigned int in_reconnect : 1; /* if session setup in progress for this channel */ + struct TCP_Server_Info *server; + struct cifs_server_iface *iface; /* interface in use */ + __u8 signkey[SMB3_SIGN_KEY_SIZE]; +}; + +/* + * Session structure. One of these for each uid session with a particular host + */ +struct cifs_ses { + struct list_head smb_ses_list; + struct list_head rlist; /* reconnect list */ + struct list_head tcon_list; + struct cifs_tcon *tcon_ipc; + spinlock_t ses_lock; /* protect anything here that is not protected */ + struct mutex session_mutex; + struct TCP_Server_Info *server; /* pointer to server info */ + int ses_count; /* reference counter */ + enum ses_status_enum ses_status; /* updates protected by cifs_tcp_ses_lock */ + unsigned overrideSecFlg; /* if non-zero override global sec flags */ + char *serverOS; /* name of operating system underlying server */ + char *serverNOS; /* name of network operating system of server */ + char *serverDomain; /* security realm of server */ + __u64 Suid; /* remote smb uid */ + kuid_t linux_uid; /* overriding owner of files on the mount */ + kuid_t cred_uid; /* owner of credentials */ + unsigned int capabilities; + char ip_addr[INET6_ADDRSTRLEN + 1]; /* Max ipv6 (or v4) addr string len */ + char *user_name; /* must not be null except during init of sess + and after mount option parsing we fill it */ + char *domainName; + char *password; + char workstation_name[CIFS_MAX_WORKSTATION_LEN]; + struct session_key auth_key; + struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ + enum securityEnum sectype; /* what security flavor was specified? */ + bool sign; /* is signing required? */ + bool domainAuto:1; + __u16 session_flags; + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; + + /* + * Network interfaces available on the server this session is + * connected to. + * + * Other channels can be opened by connecting and binding this + * session to interfaces from this list. + * + * iface_lock should be taken when accessing any of these fields + */ + spinlock_t iface_lock; + /* ========= begin: protected by iface_lock ======== */ + struct list_head iface_list; + size_t iface_count; + unsigned long iface_last_update; /* jiffies */ + /* ========= end: protected by iface_lock ======== */ + + spinlock_t chan_lock; + /* ========= begin: protected by chan_lock ======== */ +#define CIFS_MAX_CHANNELS 16 +#define CIFS_ALL_CHANNELS_SET(ses) \ + ((1UL << (ses)->chan_count) - 1) +#define CIFS_ALL_CHANS_GOOD(ses) \ + (!(ses)->chans_need_reconnect) +#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \ + ((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses)) +#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \ + ((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses)) +#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \ + test_bit((index), &(ses)->chans_need_reconnect) +#define CIFS_CHAN_IN_RECONNECT(ses, index) \ + ((ses)->chans[(index)].in_reconnect) + + struct cifs_chan chans[CIFS_MAX_CHANNELS]; + size_t chan_count; + size_t chan_max; + atomic_t chan_seq; /* round robin state */ + + /* + * chans_need_reconnect is a bitmap indicating which of the channels + * under this smb session needs to be reconnected. + * If not multichannel session, only one bit will be used. + * + * We will ask for sess and tcon reconnection only if all the + * channels are marked for needing reconnection. This will + * enable the sessions on top to continue to live till any + * of the channels below are active. + */ + unsigned long chans_need_reconnect; + /* ========= end: protected by chan_lock ======== */ +}; + +static inline bool +cap_unix(struct cifs_ses *ses) +{ + return ses->server->vals->cap_unix & ses->capabilities; +} + +/* + * common struct for holding inode info when searching for or updating an + * inode with new info + */ + +#define CIFS_FATTR_DFS_REFERRAL 0x1 +#define CIFS_FATTR_DELETE_PENDING 0x2 +#define CIFS_FATTR_NEED_REVAL 0x4 +#define CIFS_FATTR_INO_COLLISION 0x8 +#define CIFS_FATTR_UNKNOWN_NLINK 0x10 +#define CIFS_FATTR_FAKE_ROOT_INO 0x20 + +struct cifs_fattr { + u32 cf_flags; + u32 cf_cifsattrs; + u64 cf_uniqueid; + u64 cf_eof; + u64 cf_bytes; + u64 cf_createtime; + kuid_t cf_uid; + kgid_t cf_gid; + umode_t cf_mode; + dev_t cf_rdev; + unsigned int cf_nlink; + unsigned int cf_dtype; + struct timespec64 cf_atime; + struct timespec64 cf_mtime; + struct timespec64 cf_ctime; + u32 cf_cifstag; + char *cf_symlink_target; +}; + +/* + * there is one of these for each connection to a resource on a particular + * session + */ +struct cifs_tcon { + struct list_head tcon_list; + int tc_count; + struct list_head rlist; /* reconnect list */ + spinlock_t tc_lock; /* protect anything here that is not protected */ + atomic_t num_local_opens; /* num of all opens including disconnected */ + atomic_t num_remote_opens; /* num of all network opens on server */ + struct list_head openFileList; + spinlock_t open_file_lock; /* protects list above */ + struct cifs_ses *ses; /* pointer to session associated with */ + char tree_name[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ + char *nativeFileSystem; + char *password; /* for share-level security */ + __u32 tid; /* The 4 byte tree id */ + __u16 Flags; /* optional support bits */ + enum tid_status_enum status; + atomic_t num_smbs_sent; + union { + struct { + atomic_t num_writes; + atomic_t num_reads; + atomic_t num_flushes; + atomic_t num_oplock_brks; + atomic_t num_opens; + atomic_t num_closes; + atomic_t num_deletes; + atomic_t num_mkdirs; + atomic_t num_posixopens; + atomic_t num_posixmkdirs; + atomic_t num_rmdirs; + atomic_t num_renames; + atomic_t num_t2renames; + atomic_t num_ffirst; + atomic_t num_fnext; + atomic_t num_fclose; + atomic_t num_hardlinks; + atomic_t num_symlinks; + atomic_t num_locks; + atomic_t num_acl_get; + atomic_t num_acl_set; + } cifs_stats; + struct { + atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS]; + atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; + } smb2_stats; + } stats; + __u64 bytes_read; + __u64 bytes_written; + spinlock_t stat_lock; /* protects the two fields above */ + FILE_SYSTEM_DEVICE_INFO fsDevInfo; + FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ + FILE_SYSTEM_UNIX_INFO fsUnixInfo; + bool ipc:1; /* set if connection to IPC$ share (always also pipe) */ + bool pipe:1; /* set if connection to pipe share */ + bool print:1; /* set if connection to printer share */ + bool retry:1; + bool nocase:1; + bool nohandlecache:1; /* if strange server resource prob can turn off */ + bool nodelete:1; + bool seal:1; /* transport encryption for this mounted share */ + bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol + for this mount even if server would support */ + bool posix_extensions; /* if true SMB3.11 posix extensions enabled */ + bool local_lease:1; /* check leases (only) on local system not remote */ + bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ + bool broken_sparse_sup; /* if server or share does not support sparse */ + bool need_reconnect:1; /* connection reset, tid now invalid */ + bool need_reopen_files:1; /* need to reopen tcon file handles */ + bool use_resilient:1; /* use resilient instead of durable handles */ + bool use_persistent:1; /* use persistent instead of durable handles */ + bool no_lease:1; /* Do not request leases on files or directories */ + bool use_witness:1; /* use witness protocol */ + __le32 capabilities; + __u32 share_flags; + __u32 maximal_access; + __u32 vol_serial_number; + __le64 vol_create_time; + __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */ + __u32 handle_timeout; /* persistent and durable handle timeout in ms */ + __u32 ss_flags; /* sector size flags */ + __u32 perf_sector_size; /* best sector size for perf */ + __u32 max_chunks; + __u32 max_bytes_chunk; + __u32 max_bytes_copy; +#ifdef CONFIG_CIFS_FSCACHE + u64 resource_id; /* server resource id */ + struct fscache_volume *fscache; /* cookie for share */ +#endif + struct list_head pending_opens; /* list of incomplete opens */ + struct cached_fids *cfids; + /* BB add field for back pointer to sb struct(s)? */ +#ifdef CONFIG_CIFS_DFS_UPCALL + struct list_head ulist; /* cache update list */ +#endif + struct delayed_work query_interfaces; /* query interfaces workqueue job */ +}; + +/* + * This is a refcounted and timestamped container for a tcon pointer. The + * container holds a tcon reference. It is considered safe to free one of + * these when the tl_count goes to 0. The tl_time is the time of the last + * "get" on the container. + */ +struct tcon_link { + struct rb_node tl_rbnode; + kuid_t tl_uid; + unsigned long tl_flags; +#define TCON_LINK_MASTER 0 +#define TCON_LINK_PENDING 1 +#define TCON_LINK_IN_TREE 2 + unsigned long tl_time; + atomic_t tl_count; + struct cifs_tcon *tl_tcon; +}; + +extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); +extern void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst); + +static inline struct cifs_tcon * +tlink_tcon(struct tcon_link *tlink) +{ + return tlink->tl_tcon; +} + +static inline struct tcon_link * +cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) +{ + return cifs_sb->master_tlink; +} + +extern void cifs_put_tlink(struct tcon_link *tlink); + +static inline struct tcon_link * +cifs_get_tlink(struct tcon_link *tlink) +{ + if (tlink && !IS_ERR(tlink)) + atomic_inc(&tlink->tl_count); + return tlink; +} + +/* This function is always expected to succeed */ +extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); + +#define CIFS_OPLOCK_NO_CHANGE 0xfe + +struct cifs_pending_open { + struct list_head olist; + struct tcon_link *tlink; + __u8 lease_key[16]; + __u32 oplock; +}; + +struct cifs_deferred_close { + struct list_head dlist; + struct tcon_link *tlink; + __u16 netfid; + __u64 persistent_fid; + __u64 volatile_fid; +}; + +/* + * This info hangs off the cifsFileInfo structure, pointed to by llist. + * This is used to track byte stream locks on the file + */ +struct cifsLockInfo { + struct list_head llist; /* pointer to next cifsLockInfo */ + struct list_head blist; /* pointer to locks blocked on this */ + wait_queue_head_t block_q; + __u64 offset; + __u64 length; + __u32 pid; + __u16 type; + __u16 flags; +}; + +/* + * One of these for each open instance of a file + */ +struct cifs_search_info { + loff_t index_of_last_entry; + __u16 entries_in_buffer; + __u16 info_level; + __u32 resume_key; + char *ntwrk_buf_start; + char *srch_entries_start; + char *last_entry; + const char *presume_name; + unsigned int resume_name_len; + bool endOfSearch:1; + bool emptyDir:1; + bool unicode:1; + bool smallBuf:1; /* so we know which buf_release function to call */ +}; + +#define ACL_NO_MODE ((umode_t)(-1)) +struct cifs_open_parms { + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb; + int disposition; + int desired_access; + int create_options; + const char *path; + struct cifs_fid *fid; + umode_t mode; + bool reconnect:1; +}; + +struct cifs_fid { + __u16 netfid; + __u64 persistent_fid; /* persist file id for smb2 */ + __u64 volatile_fid; /* volatile file id for smb2 */ + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ + __u8 create_guid[16]; + __u32 access; + struct cifs_pending_open *pending_open; + unsigned int epoch; +#ifdef CONFIG_CIFS_DEBUG2 + __u64 mid; +#endif /* CIFS_DEBUG2 */ + bool purge_cache; +}; + +struct cifs_fid_locks { + struct list_head llist; + struct cifsFileInfo *cfile; /* fid that owns locks */ + struct list_head locks; /* locks held by fid above */ +}; + +struct cifsFileInfo { + /* following two lists are protected by tcon->open_file_lock */ + struct list_head tlist; /* pointer to next fid owned by tcon */ + struct list_head flist; /* next fid (file instance) for this inode */ + /* lock list below protected by cifsi->lock_sem */ + struct cifs_fid_locks *llist; /* brlocks held by this fid */ + kuid_t uid; /* allows finding which FileInfo structure */ + __u32 pid; /* process id who opened file */ + struct cifs_fid fid; /* file id from remote */ + struct list_head rlist; /* reconnect list */ + /* BB add lock scope info here if needed */ ; + /* lock scope id (0 if none) */ + struct dentry *dentry; + struct tcon_link *tlink; + unsigned int f_flags; + bool invalidHandle:1; /* file closed via session abend */ + bool swapfile:1; + bool oplock_break_cancelled:1; + unsigned int oplock_epoch; /* epoch from the lease break */ + __u32 oplock_level; /* oplock/lease level from the lease break */ + int count; + spinlock_t file_info_lock; /* protects four flag/count fields above */ + struct mutex fh_mutex; /* prevents reopen race after dead ses*/ + struct cifs_search_info srch_inf; + struct work_struct oplock_break; /* work for oplock breaks */ + struct work_struct put; /* work for the final part of _put */ + struct delayed_work deferred; + bool deferred_close_scheduled; /* Flag to indicate close is scheduled */ + char *symlink_target; +}; + +struct cifs_io_parms { + __u16 netfid; + __u64 persistent_fid; /* persist file id for smb2 */ + __u64 volatile_fid; /* volatile file id for smb2 */ + __u32 pid; + __u64 offset; + unsigned int length; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; +}; + +struct cifs_aio_ctx { + struct kref refcount; + struct list_head list; + struct mutex aio_mutex; + struct completion done; + struct iov_iter iter; + struct kiocb *iocb; + struct cifsFileInfo *cfile; + struct bio_vec *bv; + loff_t pos; + unsigned int npages; + ssize_t rc; + unsigned int len; + unsigned int total_len; + bool should_dirty; + /* + * Indicates if this aio_ctx is for direct_io, + * If yes, iter is a copy of the user passed iov_iter + */ + bool direct_io; +}; + +/* asynchronous read support */ +struct cifs_readdata { + struct kref refcount; + struct list_head list; + struct completion done; + struct cifsFileInfo *cfile; + struct address_space *mapping; + struct cifs_aio_ctx *ctx; + __u64 offset; + unsigned int bytes; + unsigned int got_bytes; + pid_t pid; + int result; + struct work_struct work; + int (*read_into_pages)(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + unsigned int len); + int (*copy_into_pages)(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + struct iov_iter *iter); + struct kvec iov[2]; + struct TCP_Server_Info *server; +#ifdef CONFIG_CIFS_SMB_DIRECT + struct smbd_mr *mr; +#endif + unsigned int pagesz; + unsigned int page_offset; + unsigned int tailsz; + struct cifs_credits credits; + unsigned int nr_pages; + struct page **pages; +}; + +/* asynchronous write support */ +struct cifs_writedata { + struct kref refcount; + struct list_head list; + struct completion done; + enum writeback_sync_modes sync_mode; + struct work_struct work; + struct cifsFileInfo *cfile; + struct cifs_aio_ctx *ctx; + __u64 offset; + pid_t pid; + unsigned int bytes; + int result; + struct TCP_Server_Info *server; +#ifdef CONFIG_CIFS_SMB_DIRECT + struct smbd_mr *mr; +#endif + unsigned int pagesz; + unsigned int page_offset; + unsigned int tailsz; + struct cifs_credits credits; + unsigned int nr_pages; + struct page **pages; +}; + +/* + * Take a reference on the file private data. Must be called with + * cfile->file_info_lock held. + */ +static inline void +cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) +{ + ++cifs_file->count; +} + +struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr, + bool offload); +void cifsFileInfo_put(struct cifsFileInfo *cifs_file); + +#define CIFS_CACHE_READ_FLG 1 +#define CIFS_CACHE_HANDLE_FLG 2 +#define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG) +#define CIFS_CACHE_WRITE_FLG 4 +#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) +#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) + +#define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE)) +#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) +#define CIFS_CACHE_WRITE(cinode) ((cinode->oplock & CIFS_CACHE_WRITE_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE)) + +/* + * One of these for each file inode + */ + +struct cifsInodeInfo { + struct netfs_inode netfs; /* Netfslib context and vfs inode */ + bool can_cache_brlcks; + struct list_head llist; /* locks helb by this inode */ + /* + * NOTE: Some code paths call down_read(lock_sem) twice, so + * we must always use cifs_down_write() instead of down_write() + * for this semaphore to avoid deadlocks. + */ + struct rw_semaphore lock_sem; /* protect the fields above */ + /* BB add in lists for dirty pages i.e. write caching info for oplock */ + struct list_head openFileList; + spinlock_t open_file_lock; /* protects openFileList */ + __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ + unsigned int oplock; /* oplock/lease level we have */ + unsigned int epoch; /* used to track lease state changes */ +#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */ +#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */ +#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */ +#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */ +#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */ +#define CIFS_INO_LOCK (5) /* lock bit for synchronization */ +#define CIFS_INO_MODIFIED_ATTR (6) /* Indicate change in mtime/ctime */ +#define CIFS_INO_CLOSE_ON_LOCK (7) /* Not to defer the close when lock is set */ + unsigned long flags; + spinlock_t writers_lock; + unsigned int writers; /* Number of writers on this inode */ + unsigned long time; /* jiffies of last update of inode */ + u64 server_eof; /* current file size on server -- protected by i_lock */ + u64 uniqueid; /* server inode number */ + u64 createtime; /* creation time on server */ + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */ + struct list_head deferred_closes; /* list of deferred closes */ + spinlock_t deferred_lock; /* protection on deferred list */ + bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */ + char *symlink_target; +}; + +static inline struct cifsInodeInfo * +CIFS_I(struct inode *inode) +{ + return container_of(inode, struct cifsInodeInfo, netfs.inode); +} + +static inline struct cifs_sb_info * +CIFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct cifs_sb_info * +CIFS_FILE_SB(struct file *file) +{ + return CIFS_SB(file_inode(file)->i_sb); +} + +static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) + return '/'; + else + return '\\'; +} + +static inline void +convert_delimiter(char *path, char delim) +{ + char old_delim, *pos; + + if (delim == '/') + old_delim = '\\'; + else + old_delim = '/'; + + pos = path; + while ((pos = strchr(pos, old_delim))) + *pos = delim; +} + +#define cifs_stats_inc atomic_inc + +static inline void cifs_stats_bytes_written(struct cifs_tcon *tcon, + unsigned int bytes) +{ + if (bytes) { + spin_lock(&tcon->stat_lock); + tcon->bytes_written += bytes; + spin_unlock(&tcon->stat_lock); + } +} + +static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon, + unsigned int bytes) +{ + spin_lock(&tcon->stat_lock); + tcon->bytes_read += bytes; + spin_unlock(&tcon->stat_lock); +} + + +/* + * This is the prototype for the mid receive function. This function is for + * receiving the rest of the SMB frame, starting with the WordCount (which is + * just after the MID in struct smb_hdr). Note: + * + * - This will be called by cifsd, with no locks held. + * - The mid will still be on the pending_mid_q. + * - mid->resp_buf will point to the current buffer. + * + * Returns zero on a successful receive, or an error. The receive state in + * the TCP_Server_Info will also be updated. + */ +typedef int (mid_receive_t)(struct TCP_Server_Info *server, + struct mid_q_entry *mid); + +/* + * This is the prototype for the mid callback function. This is called once the + * mid has been received off of the socket. When creating one, take special + * care to avoid deadlocks. Things to bear in mind: + * + * - it will be called by cifsd, with no locks held + * - the mid will be removed from any lists + */ +typedef void (mid_callback_t)(struct mid_q_entry *mid); + +/* + * This is the protopyte for mid handle function. This is called once the mid + * has been recognized after decryption of the message. + */ +typedef int (mid_handle_t)(struct TCP_Server_Info *server, + struct mid_q_entry *mid); + +/* one of these for every pending CIFS request to the server */ +struct mid_q_entry { + struct list_head qhead; /* mids waiting on reply from this server */ + struct kref refcount; + struct TCP_Server_Info *server; /* server corresponding to this mid */ + __u64 mid; /* multiplex id */ + __u16 credits; /* number of credits consumed by this mid */ + __u16 credits_received; /* number of credits from the response */ + __u32 pid; /* process id */ + __u32 sequence_number; /* for CIFS signing */ + unsigned long when_alloc; /* when mid was created */ +#ifdef CONFIG_CIFS_STATS2 + unsigned long when_sent; /* time when smb send finished */ + unsigned long when_received; /* when demux complete (taken off wire) */ +#endif + mid_receive_t *receive; /* call receive callback */ + mid_callback_t *callback; /* call completion callback */ + mid_handle_t *handle; /* call handle mid callback */ + void *callback_data; /* general purpose pointer for callback */ + struct task_struct *creator; + void *resp_buf; /* pointer to received SMB header */ + unsigned int resp_buf_size; + int mid_state; /* wish this were enum but can not pass to wait_event */ + unsigned int mid_flags; + __le16 command; /* smb command code */ + unsigned int optype; /* operation type */ + bool large_buf:1; /* if valid response, is pointer to large buf */ + bool multiRsp:1; /* multiple trans2 responses for one request */ + bool multiEnd:1; /* both received */ + bool decrypted:1; /* decrypted entry */ +}; + +struct close_cancelled_open { + struct cifs_fid fid; + struct cifs_tcon *tcon; + struct work_struct work; + __u64 mid; + __u16 cmd; +}; + +/* Make code in transport.c a little cleaner by moving + update of optional stats into function below */ +static inline void cifs_in_send_inc(struct TCP_Server_Info *server) +{ + atomic_inc(&server->in_send); +} + +static inline void cifs_in_send_dec(struct TCP_Server_Info *server) +{ + atomic_dec(&server->in_send); +} + +static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server) +{ + atomic_inc(&server->num_waiters); +} + +static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server) +{ + atomic_dec(&server->num_waiters); +} + +#ifdef CONFIG_CIFS_STATS2 +static inline void cifs_save_when_sent(struct mid_q_entry *mid) +{ + mid->when_sent = jiffies; +} +#else +static inline void cifs_save_when_sent(struct mid_q_entry *mid) +{ +} +#endif + +/* for pending dnotify requests */ +struct dir_notify_req { + struct list_head lhead; + __le16 Pid; + __le16 PidHigh; + __u16 Mid; + __u16 Tid; + __u16 Uid; + __u16 netfid; + __u32 filter; /* CompletionFilter (for multishot) */ + int multishot; + struct file *pfile; +}; + +struct dfs_info3_param { + int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/ + int path_consumed; + int server_type; + int ref_flag; + char *path_name; + char *node_name; + int ttl; +}; + +struct file_list { + struct list_head list; + struct cifsFileInfo *cfile; +}; + +static inline void free_dfs_info_param(struct dfs_info3_param *param) +{ + if (param) { + kfree(param->path_name); + kfree(param->node_name); + } +} + +static inline void free_dfs_info_array(struct dfs_info3_param *param, + int number_of_items) +{ + int i; + if ((number_of_items == 0) || (param == NULL)) + return; + for (i = 0; i < number_of_items; i++) { + kfree(param[i].path_name); + kfree(param[i].node_name); + } + kfree(param); +} + +static inline bool is_interrupt_error(int error) +{ + switch (error) { + case -EINTR: + case -ERESTARTSYS: + case -ERESTARTNOHAND: + case -ERESTARTNOINTR: + return true; + } + return false; +} + +static inline bool is_retryable_error(int error) +{ + if (is_interrupt_error(error) || error == -EAGAIN) + return true; + return false; +} + + +/* cifs_get_writable_file() flags */ +#define FIND_WR_ANY 0 +#define FIND_WR_FSUID_ONLY 1 +#define FIND_WR_WITH_DELETE 2 + +#define MID_FREE 0 +#define MID_REQUEST_ALLOCATED 1 +#define MID_REQUEST_SUBMITTED 2 +#define MID_RESPONSE_RECEIVED 4 +#define MID_RETRY_NEEDED 8 /* session closed while this request out */ +#define MID_RESPONSE_MALFORMED 0x10 +#define MID_SHUTDOWN 0x20 + +/* Flags */ +#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */ +#define MID_DELETED 2 /* Mid has been dequeued/deleted */ + +/* Types of response buffer returned from SendReceive2 */ +#define CIFS_NO_BUFFER 0 /* Response buffer not returned */ +#define CIFS_SMALL_BUFFER 1 +#define CIFS_LARGE_BUFFER 2 +#define CIFS_IOVEC 4 /* array of response buffers */ + +/* Type of Request to SendReceive2 */ +#define CIFS_BLOCKING_OP 1 /* operation can block */ +#define CIFS_NON_BLOCKING 2 /* do not block waiting for credits */ +#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */ +#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */ +#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */ +#define CIFS_NO_RSP_BUF 0x040 /* no response buffer required */ + +/* Type of request operation */ +#define CIFS_ECHO_OP 0x080 /* echo request */ +#define CIFS_OBREAK_OP 0x0100 /* oplock break request */ +#define CIFS_NEG_OP 0x0200 /* negotiate request */ +#define CIFS_CP_CREATE_CLOSE_OP 0x0400 /* compound create+close request */ +/* Lower bitmask values are reserved by others below. */ +#define CIFS_SESS_OP 0x2000 /* session setup request */ +#define CIFS_OP_MASK 0x2780 /* mask request type */ + +#define CIFS_HAS_CREDITS 0x0400 /* already has credits */ +#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */ +#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */ + +/* Security Flags: indicate type of session setup needed */ +#define CIFSSEC_MAY_SIGN 0x00001 +#define CIFSSEC_MAY_NTLMV2 0x00004 +#define CIFSSEC_MAY_KRB5 0x00008 +#define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */ +#define CIFSSEC_MAY_NTLMSSP 0x00080 /* raw ntlmssp with ntlmv2 */ + +#define CIFSSEC_MUST_SIGN 0x01001 +/* note that only one of the following can be set so the +result of setting MUST flags more than once will be to +require use of the stronger protocol */ +#define CIFSSEC_MUST_NTLMV2 0x04004 +#define CIFSSEC_MUST_KRB5 0x08008 +#ifdef CONFIG_CIFS_UPCALL +#define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */ +#else +#define CIFSSEC_MASK 0x87087 /* flags supported if no weak allowed */ +#endif /* UPCALL */ +#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ +#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ + +#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP) +#define CIFSSEC_MAX (CIFSSEC_MUST_NTLMV2) +#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) +/* + ***************************************************************** + * All constants go here + ***************************************************************** + */ + +#define UID_HASH (16) + +/* + * Note that ONE module should define _DECLARE_GLOBALS_HERE to cause the + * following to be declared. + */ + +/**************************************************************************** + * Here are all the locks (spinlock, mutex, semaphore) in cifs.ko, arranged according + * to the locking order. i.e. if two locks are to be held together, the lock that + * appears higher in this list needs to be taken before the other. + * + * If you hold a lock that is lower in this list, and you need to take a higher lock + * (or if you think that one of the functions that you're calling may need to), first + * drop the lock you hold, pick up the higher lock, then the lower one. This will + * ensure that locks are picked up only in one direction in the below table + * (top to bottom). + * + * Also, if you expect a function to be called with a lock held, explicitly document + * this in the comments on top of your function definition. + * + * And also, try to keep the critical sections (lock hold time) to be as minimal as + * possible. Blocking / calling other functions with a lock held always increase + * the risk of a possible deadlock. + * + * Following this rule will avoid unnecessary deadlocks, which can get really hard to + * debug. Also, any new lock that you introduce, please add to this list in the correct + * order. + * + * Please populate this list whenever you introduce new locks in your changes. Or in + * case I've missed some existing locks. Please ensure that it's added in the list + * based on the locking order expected. + * + * ===================================================================================== + * Lock Protects Initialization fn + * ===================================================================================== + * vol_list_lock + * vol_info->ctx_lock vol_info->ctx + * cifs_sb_info->tlink_tree_lock cifs_sb_info->tlink_tree cifs_setup_cifs_sb + * TCP_Server_Info-> TCP_Server_Info cifs_get_tcp_session + * reconnect_mutex + * TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session + * cifs_ses->session_mutex cifs_ses sesInfoAlloc + * cifs_tcon + * cifs_tcon->open_file_lock cifs_tcon->openFileList tconInfoAlloc + * cifs_tcon->pending_opens + * cifs_tcon->stat_lock cifs_tcon->bytes_read tconInfoAlloc + * cifs_tcon->bytes_written + * cifs_tcp_ses_lock cifs_tcp_ses_list sesInfoAlloc + * GlobalMid_Lock GlobalMaxActiveXid init_cifs + * GlobalCurrentXid + * GlobalTotalActiveXid + * TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change) + * TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session + * ->CurrentMid + * (any changes in mid_q_entry fields) + * TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session + * ->credits + * ->echo_credits + * ->oplock_credits + * ->reconnect_instance + * cifs_ses->ses_lock (anything that is not protected by another lock and can change) + * cifs_ses->iface_lock cifs_ses->iface_list sesInfoAlloc + * ->iface_count + * ->iface_last_update + * cifs_ses->chan_lock cifs_ses->chans + * ->chans_need_reconnect + * ->chans_in_reconnect + * cifs_tcon->tc_lock (anything that is not protected by another lock and can change) + * cifsInodeInfo->open_file_lock cifsInodeInfo->openFileList cifs_alloc_inode + * cifsInodeInfo->writers_lock cifsInodeInfo->writers cifsInodeInfo_alloc + * cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once + * ->can_cache_brlcks + * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc + * cached_fid->fid_mutex cifs_tcon->crfid tconInfoAlloc + * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo + * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo + * ->invalidHandle initiate_cifs_search + * ->oplock_break_cancelled + * cifs_aio_ctx->aio_mutex cifs_aio_ctx cifs_aio_ctx_alloc + ****************************************************************************/ + +#ifdef DECLARE_GLOBALS_HERE +#define GLOBAL_EXTERN +#else +#define GLOBAL_EXTERN extern +#endif + +/* + * the list of TCP_Server_Info structures, ie each of the sockets + * connecting our client to a distinct server (ip address), is + * chained together by cifs_tcp_ses_list. The list of all our SMB + * sessions (and from that the tree connections) can be found + * by iterating over cifs_tcp_ses_list + */ +extern struct list_head cifs_tcp_ses_list; + +/* + * This lock protects the cifs_tcp_ses_list, the list of smb sessions per + * tcp session, and the list of tcon's per smb session. It also protects + * the reference counters for the server, smb session, and tcon. + * generally the locks should be taken in order tcp_ses_lock before + * tcon->open_file_lock and that before file->file_info_lock since the + * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file + */ +extern spinlock_t cifs_tcp_ses_lock; + +/* + * Global transaction id (XID) information + */ +extern unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ +extern unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ +extern unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ +extern spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ + +/* + * Global counters, updated atomically + */ +extern atomic_t sesInfoAllocCount; +extern atomic_t tconInfoAllocCount; +extern atomic_t tcpSesNextId; +extern atomic_t tcpSesAllocCount; +extern atomic_t tcpSesReconnectCount; +extern atomic_t tconInfoReconnectCount; + +/* Various Debug counters */ +extern atomic_t buf_alloc_count; /* current number allocated */ +extern atomic_t small_buf_alloc_count; +#ifdef CONFIG_CIFS_STATS2 +extern atomic_t total_buf_alloc_count; /* total allocated over all time */ +extern atomic_t total_small_buf_alloc_count; +extern unsigned int slow_rsp_threshold; /* number of secs before logging */ +#endif + +/* Misc globals */ +extern bool enable_oplocks; /* enable or disable oplocks */ +extern bool lookupCacheEnabled; +extern unsigned int global_secflags; /* if on, session setup sent + with more secure ntlmssp2 challenge/resp */ +extern unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ +extern bool enable_gcm_256; /* allow optional negotiate of strongest signing (aes-gcm-256) */ +extern bool require_gcm_256; /* require use of strongest signing (aes-gcm-256) */ +extern bool enable_negotiate_signing; /* request use of faster (GMAC) signing if available */ +extern bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/ +extern unsigned int CIFSMaxBufSize; /* max size not including hdr */ +extern unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ +extern unsigned int cifs_min_small; /* min size of small buf pool */ +extern unsigned int cifs_max_pending; /* MAX requests at once to server*/ +extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */ +extern atomic_t mid_count; + +void cifs_oplock_break(struct work_struct *work); +void cifs_queue_oplock_break(struct cifsFileInfo *cfile); +void smb2_deferred_work_close(struct work_struct *work); + +extern const struct slow_work_ops cifs_oplock_break_ops; +extern struct workqueue_struct *cifsiod_wq; +extern struct workqueue_struct *decrypt_wq; +extern struct workqueue_struct *fileinfo_put_wq; +extern struct workqueue_struct *cifsoplockd_wq; +extern struct workqueue_struct *deferredclose_wq; +extern __u32 cifs_lock_secret; + +extern mempool_t *cifs_mid_poolp; + +/* Operations for different SMB versions */ +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +extern struct smb_version_operations smb1_operations; +extern struct smb_version_values smb1_values; +extern struct smb_version_operations smb20_operations; +extern struct smb_version_values smb20_values; +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ +#define SMB21_VERSION_STRING "2.1" +extern struct smb_version_operations smb21_operations; +extern struct smb_version_values smb21_values; +#define SMBDEFAULT_VERSION_STRING "default" +extern struct smb_version_values smbdefault_values; +#define SMB3ANY_VERSION_STRING "3" +extern struct smb_version_values smb3any_values; +#define SMB30_VERSION_STRING "3.0" +extern struct smb_version_operations smb30_operations; +extern struct smb_version_values smb30_values; +#define SMB302_VERSION_STRING "3.02" +#define ALT_SMB302_VERSION_STRING "3.0.2" +/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ +extern struct smb_version_values smb302_values; +#define SMB311_VERSION_STRING "3.1.1" +#define ALT_SMB311_VERSION_STRING "3.11" +extern struct smb_version_operations smb311_operations; +extern struct smb_version_values smb311_values; + +static inline char *get_security_type_str(enum securityEnum sectype) +{ + switch (sectype) { + case RawNTLMSSP: + return "RawNTLMSSP"; + case Kerberos: + return "Kerberos"; + case NTLMv2: + return "NTLMv2"; + default: + return "Unknown"; + } +} + +static inline bool is_smb1_server(struct TCP_Server_Info *server) +{ + return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0; +} + +static inline bool is_tcon_dfs(struct cifs_tcon *tcon) +{ + /* + * For SMB1, see MS-CIFS 2.4.55 SMB_COM_TREE_CONNECT_ANDX (0x75) and MS-CIFS 3.3.4.4 DFS + * Subsystem Notifies That a Share Is a DFS Share. + * + * For SMB2+, see MS-SMB2 2.2.10 SMB2 TREE_CONNECT Response and MS-SMB2 3.3.4.14 Server + * Application Updates a Share. + */ + if (!tcon || !tcon->ses || !tcon->ses->server) + return false; + return is_smb1_server(tcon->ses->server) ? tcon->Flags & SMB_SHARE_IS_IN_DFS : + tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT); +} + +static inline bool cifs_is_referral_server(struct cifs_tcon *tcon, + const struct dfs_info3_param *ref) +{ + /* + * Check if all targets are capable of handling DFS referrals as per + * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. + */ + return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER)); +} + +static inline u64 cifs_flock_len(const struct file_lock *fl) +{ + return (u64)fl->fl_end - fl->fl_start + 1; +} + +static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses) +{ + if (WARN_ON_ONCE(!ses || !ses->server)) + return 0; + /* + * Make workstation name no more than 15 chars when using insecure dialects as some legacy + * servers do require it during NTLMSSP. + */ + if (ses->server->dialect <= SMB20_PROT_ID) + return min_t(size_t, sizeof(ses->workstation_name), RFC1001_NAME_LEN_WITH_NULL); + return sizeof(ses->workstation_name); +} + +static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src) +{ + memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src)); + dst->AccessFlags = src->AccessFlags; + dst->CurrentByteOffset = src->CurrentByteOffset; + dst->Mode = src->Mode; + dst->AlignmentRequirement = src->AlignmentRequirement; + dst->FileNameLength = src->FileNameLength; +} + +static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, + int num_rqst, + const u8 *sig) +{ + unsigned int len, skip; + unsigned int nents = 0; + unsigned long addr; + int i, j; + + /* Assumes the first rqst has a transform header as the first iov. + * I.e. + * rqst[0].rq_iov[0] is transform header + * rqst[0].rq_iov[1+] data to be encrypted/decrypted + * rqst[1+].rq_iov[0+] data to be encrypted/decrypted + */ + for (i = 0; i < num_rqst; i++) { + /* + * The first rqst has a transform header where the + * first 20 bytes are not part of the encrypted blob. + */ + for (j = 0; j < rqst[i].rq_nvec; j++) { + struct kvec *iov = &rqst[i].rq_iov[j]; + + skip = (i == 0) && (j == 0) ? 20 : 0; + addr = (unsigned long)iov->iov_base + skip; + if (unlikely(is_vmalloc_addr((void *)addr))) { + len = iov->iov_len - skip; + nents += DIV_ROUND_UP(offset_in_page(addr) + len, + PAGE_SIZE); + } else { + nents++; + } + } + nents += rqst[i].rq_npages; + } + nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE); + return nents; +} + +/* We can not use the normal sg_set_buf() as we will sometimes pass a + * stack object as buf. + */ +static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg, + const void *buf, + unsigned int buflen) +{ + unsigned long addr = (unsigned long)buf; + unsigned int off = offset_in_page(addr); + + addr &= PAGE_MASK; + if (unlikely(is_vmalloc_addr((void *)addr))) { + do { + unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off); + + sg_set_page(sg++, vmalloc_to_page((void *)addr), len, off); + + off = 0; + addr += PAGE_SIZE; + buflen -= len; + } while (buflen); + } else { + sg_set_page(sg++, virt_to_page(addr), buflen, off); + } + return sg; +} + +#endif /* _CIFS_GLOB_H */ diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h new file mode 100644 index 000000000000..cc458b98441c --- /dev/null +++ b/fs/smb/client/cifspdu.h @@ -0,0 +1,2730 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFSPDU_H +#define _CIFSPDU_H + +#include +#include +#include "../common/smbfsctl.h" + +#define CIFS_PROT 0 +#define POSIX_PROT (CIFS_PROT+1) +#define BAD_PROT 0xFFFF + +/* SMB command codes: + * Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie which include no useful data other than the SMB error code itself). + * This can allow us to avoid response buffer allocations and copy in some cases + */ +#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ +#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ +#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ +#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ +#define SMB_COM_DELETE 0x06 /* trivial response */ +#define SMB_COM_RENAME 0x07 /* trivial response */ +#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ +#define SMB_COM_SETATTR 0x09 /* trivial response */ +#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ +#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_ECHO 0x2B /* echo request */ +#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ +#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 /* no response */ +#define SMB_COM_NT_RENAME 0xA5 /* trivial response */ + +/* Transact2 subcommand codes */ +#define TRANS2_OPEN 0x00 +#define TRANS2_FIND_FIRST 0x01 +#define TRANS2_FIND_NEXT 0x02 +#define TRANS2_QUERY_FS_INFORMATION 0x03 +#define TRANS2_SET_FS_INFORMATION 0x04 +#define TRANS2_QUERY_PATH_INFORMATION 0x05 +#define TRANS2_SET_PATH_INFORMATION 0x06 +#define TRANS2_QUERY_FILE_INFORMATION 0x07 +#define TRANS2_SET_FILE_INFORMATION 0x08 +#define TRANS2_GET_DFS_REFERRAL 0x10 +#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 + +/* SMB Transact (Named Pipe) subcommand codes */ +#define TRANS_SET_NMPIPE_STATE 0x0001 +#define TRANS_RAW_READ_NMPIPE 0x0011 +#define TRANS_QUERY_NMPIPE_STATE 0x0021 +#define TRANS_QUERY_NMPIPE_INFO 0x0022 +#define TRANS_PEEK_NMPIPE 0x0023 +#define TRANS_TRANSACT_NMPIPE 0x0026 +#define TRANS_RAW_WRITE_NMPIPE 0x0031 +#define TRANS_READ_NMPIPE 0x0036 +#define TRANS_WRITE_NMPIPE 0x0037 +#define TRANS_WAIT_NMPIPE 0x0053 +#define TRANS_CALL_NMPIPE 0x0054 + +/* NT Transact subcommand codes */ +#define NT_TRANSACT_CREATE 0x01 +#define NT_TRANSACT_IOCTL 0x02 +#define NT_TRANSACT_SET_SECURITY_DESC 0x03 +#define NT_TRANSACT_NOTIFY_CHANGE 0x04 +#define NT_TRANSACT_RENAME 0x05 +#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 +#define NT_TRANSACT_GET_USER_QUOTA 0x07 +#define NT_TRANSACT_SET_USER_QUOTA 0x08 + +#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ +/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */ +/* among the requests (NTCreateX response is bigger with wct of 34) */ +#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */ +#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */ + +/* internal cifs vfs structures */ +/***************************************************************** + * All constants go here + ***************************************************************** + */ + +/* + * Starting value for maximum SMB size negotiation + */ +#define CIFS_MAX_MSGSIZE (4*4096) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE (24) + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define CIFS_SESS_KEY_SIZE (16) + +#define CIFS_SERVER_CHALLENGE_SIZE (8) +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Maximum user name length + */ +#define CIFS_UNLEN (20) + +/* + * Flags on SMB open + */ +#define SMBOPEN_WRITE_THROUGH 0x4000 +#define SMBOPEN_DENY_ALL 0x0010 +#define SMBOPEN_DENY_WRITE 0x0020 +#define SMBOPEN_DENY_READ 0x0030 +#define SMBOPEN_DENY_NONE 0x0040 +#define SMBOPEN_READ 0x0000 +#define SMBOPEN_WRITE 0x0001 +#define SMBOPEN_READWRITE 0x0002 +#define SMBOPEN_EXECUTE 0x0003 + +#define SMBOPEN_OCREATE 0x0010 +#define SMBOPEN_OTRUNC 0x0002 +#define SMBOPEN_OAPPEND 0x0001 + +/* + * SMB flag definitions + */ +#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */ +#define SMBFLG_RCV_POSTED 0x02 /* obsolete */ +#define SMBFLG_RSVD 0x04 +#define SMBFLG_CASELESS 0x08 /* all pathnames treated as caseless (off + implies case sensitive file handling request) */ +#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ +#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ +#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ +#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ + +/* + * SMB flag2 definitions + */ +#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3) + path names in response */ +#define SMBFLG2_KNOWS_EAS cpu_to_le16(2) +#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) +#define SMBFLG2_COMPRESSED (8) +#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) +#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) +#define SMBFLG2_REPARSE_PATH (0x400) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) +#define SMBFLG2_DFS cpu_to_le16(0x1000) +#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) +#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) +#define SMBFLG2_UNICODE cpu_to_le16(0x8000) + +/* + * These are the file access permission bits defined in CIFS for the + * NTCreateAndX as well as the level 0x107 + * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO + * responds with the AccessFlags. + * The AccessFlags specifies the access permissions a caller has to the + * file and can have any suitable combination of the following values: + */ + +#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ +#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ +#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ +#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ + /* with the file can be read */ +#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ + /* with the file can be written */ +#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ + /* the file using system paging I/O */ +#define FILE_DELETE_CHILD 0x00000040 +#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ + /* file can be read */ +#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ + /* file can be written */ +#define DELETE 0x00010000 /* The file can be deleted */ +#define READ_CONTROL 0x00020000 /* The access control list and */ + /* ownership associated with the */ + /* file can be read */ +#define WRITE_DAC 0x00040000 /* The access control list and */ + /* ownership associated with the */ + /* file can be written. */ +#define WRITE_OWNER 0x00080000 /* Ownership information associated */ + /* with the file can be written */ +#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ + /* synchronize with the completion */ + /* of an input/output request */ +#define SYSTEM_SECURITY 0x01000000 /* The system access control list */ + /* can be read and changed */ +#define GENERIC_ALL 0x10000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 + /* In summary - Relevant file */ + /* access flags from CIFS are */ + /* file_read_data, file_write_data */ + /* file_execute, file_read_attributes*/ + /* write_dac, and delete. */ + +#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) +#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) +#define FILE_EXEC_RIGHTS (FILE_EXECUTE) + +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + + +/* + * Invalid readdir handle + */ +#define CIFS_NO_HANDLE 0xFFFF + +#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL + +/* IPC$ in ASCII */ +#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" + +/* IPC$ in Unicode */ +#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00" + +/* Unicode Null terminate 2 bytes of 0 */ +#define UNICODE_NULL "\x00\x00" +#define ASCII_NULL 0x00 + +/* + * Server type values (returned on EnumServer API + */ +#define CIFS_SV_TYPE_DC 0x00000008 +#define CIFS_SV_TYPE_BACKDC 0x00000010 + +/* + * Alias type flags (From EnumAlias API call + */ +#define CIFS_ALIAS_TYPE_FILE 0x0001 +#define CIFS_SHARE_TYPE_FILE 0x0000 + +/* + * File Attribute flags + */ +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_DIRECTORY 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_DEVICE 0x0040 +#define ATTR_NORMAL 0x0080 +#define ATTR_TEMPORARY 0x0100 +#define ATTR_SPARSE 0x0200 +#define ATTR_REPARSE 0x0400 +#define ATTR_COMPRESSED 0x0800 +#define ATTR_OFFLINE 0x1000 /* ie file not immediately available - + on offline storage */ +#define ATTR_NOT_CONTENT_INDEXED 0x2000 +#define ATTR_ENCRYPTED 0x4000 +#define ATTR_POSIX_SEMANTICS 0x01000000 +#define ATTR_BACKUP_SEMANTICS 0x02000000 +#define ATTR_DELETE_ON_CLOSE 0x04000000 +#define ATTR_SEQUENTIAL_SCAN 0x08000000 +#define ATTR_RANDOM_ACCESS 0x10000000 +#define ATTR_NO_BUFFERING 0x20000000 +#define ATTR_WRITE_THROUGH 0x80000000 + +/* ShareAccess flags */ +#define FILE_NO_SHARE 0x00000000 +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define FILE_SHARE_ALL 0x00000007 + +/* CreateDisposition flags, similar to CreateAction as well */ +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 + +/* CreateOptions */ +#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ +#define CREATE_WRITE_THROUGH 0x00000002 +#define CREATE_SEQUENTIAL 0x00000004 +#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */ +#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */ +#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */ +#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ +#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */ +#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */ +#define CREATE_NO_EA_KNOWLEDGE 0x00000200 +#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete + "open for recovery" flag should + be zero in any case */ +#define CREATE_OPEN_FOR_RECOVERY 0x00000400 +#define CREATE_RANDOM_ACCESS 0x00000800 +#define CREATE_DELETE_ON_CLOSE 0x00001000 +#define CREATE_OPEN_BY_ID 0x00002000 +#define CREATE_OPEN_BACKUP_INTENT 0x00004000 +#define CREATE_NO_COMPRESSION 0x00008000 +#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */ +#define OPEN_REPARSE_POINT 0x00200000 +#define OPEN_NO_RECALL 0x00400000 +#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */ +#define CREATE_OPTIONS_MASK 0x007FFFFF +#define CREATE_OPTION_READONLY 0x10000000 +#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ + +/* ImpersonationLevel flags */ +#define SECURITY_ANONYMOUS 0 +#define SECURITY_IDENTIFICATION 1 +#define SECURITY_IMPERSONATION 2 +#define SECURITY_DELEGATION 3 + +/* SecurityFlags */ +#define SECURITY_CONTEXT_TRACKING 0x01 +#define SECURITY_EFFECTIVE_ONLY 0x02 + +/* + * Default PID value, used in all SMBs where the PID is not important + */ +#define CIFS_DFT_PID 0x1234 + +/* + * We use the same routine for Copy and Move SMBs. This flag is used to + * distinguish + */ +#define CIFS_COPY_OP 1 +#define CIFS_RENAME_OP 2 + +#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */ +#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */ + +struct smb_hdr { + __be32 smb_buf_length; /* BB length is only two (rarely three) bytes, + with one or two byte "type" preceding it that will be + zero - we could mask the type byte off */ + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __attribute__((packed)) DosError; + __le32 CifsError; + } __attribute__((packed)) Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __attribute__((packed)) Sequence; + __u8 SecuritySignature[8]; /* le */ + } __attribute__((packed)) Signature; + __u8 pad[2]; + __u16 Tid; + __le16 Pid; + __u16 Uid; + __le16 Mid; + __u8 WordCount; +} __attribute__((packed)); + +/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */ +static inline void * +BCC(struct smb_hdr *smb) +{ + return (void *)smb + sizeof(*smb) + 2 * smb->WordCount; +} + +/* given a pointer to an smb_hdr retrieve the pointer to the byte area */ +#define pByteArea(smb_var) (BCC(smb_var) + 2) + +/* get the unconverted ByteCount for a SMB packet and return it */ +static inline __u16 +get_bcc(struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + return get_unaligned_le16(bc_ptr); +} + +/* set the ByteCount for a SMB packet in little-endian */ +static inline void +put_bcc(__u16 count, struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + put_unaligned_le16(count, bc_ptr); +} + +/* + * Computer Name Length (since Netbios name was length 16 with last byte 0x20) + * No longer as important, now that TCP names are more commonly used to + * resolve hosts. + */ +#define CNLEN 15 + +/* + * Share Name Length (SNLEN) + * Note: This length was limited by the SMB used to get + * the Share info. NetShareEnum only returned 13 + * chars, including the null termination. + * This was removed because it no longer is limiting. + */ + +/* + * Comment Length + */ +#define MAXCOMMENTLEN 40 + +/* + * The OS/2 maximum path name + */ +#define MAX_PATHCONF 256 + +/* + * SMB frame definitions (following must be packed structs) + * See the SNIA CIFS Specification for details. + * + * The Naming convention is the lower case version of the + * smb command code name for the struct and this is typedef to the + * uppercase version of the same name with the prefix SMB_ removed + * for brevity. Although typedefs are not commonly used for + * structure definitions in the Linux kernel, their use in the + * CIFS standards document, which this code is based on, may + * make this one of the cases where typedefs for structures make + * sense to improve readability for readers of the standards doc. + * Typedefs can always be removed later if they are too distracting + * and they are only used for the CIFSs PDUs themselves, not + * internal cifs vfs structures + * + */ + +typedef struct negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[]; +} __attribute__((packed)) NEGOTIATE_REQ; + +#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */ + +#define READ_RAW_ENABLE 1 +#define WRITE_RAW_ENABLE 2 +#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) +#define SMB1_CLIENT_GUID_SIZE (16) +typedef struct negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __u8 SecurityMode; + __le16 MaxMpxCount; + __le16 MaxNumberVcs; + __le32 MaxBufferSize; + __le32 MaxRawSize; + __le32 SessionKey; + __le32 Capabilities; /* see below */ + __le32 SystemTimeLow; + __le32 SystemTimeHigh; + __le16 ServerTimeZone; + __u8 EncryptionKeyLength; + __u16 ByteCount; + union { + /* cap extended security off */ + DECLARE_FLEX_ARRAY(unsigned char, EncryptionKey); + /* followed by Domain name - if extended security is off */ + /* followed by 16 bytes of server GUID */ + /* then security blob if cap_extended_security negotiated */ + struct { + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; + unsigned char SecurityBlob[]; + } __attribute__((packed)) extended_response; + } __attribute__((packed)) u; +} __attribute__((packed)) NEGOTIATE_RSP; + +/* SecurityMode bits */ +#define SECMODE_USER 0x01 /* off indicates share level security */ +#define SECMODE_PW_ENCRYPT 0x02 +#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ +#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ + +/* Negotiate response Capabilities */ +#define CAP_RAW_MODE 0x00000001 +#define CAP_MPX_MODE 0x00000002 +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ +#define CAP_RPC_REMOTE_APIS 0x00000020 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_LOCK_AND_READ 0x00000100 +#define CAP_NT_FIND 0x00000200 +#define CAP_DFS 0x00001000 +#define CAP_INFOLEVEL_PASSTHRU 0x00002000 +#define CAP_LARGE_READ_X 0x00004000 +#define CAP_LARGE_WRITE_X 0x00008000 +#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ +#define CAP_UNIX 0x00800000 +#define CAP_COMPRESSED_DATA 0x02000000 +#define CAP_DYNAMIC_REAUTH 0x20000000 +#define CAP_PERSISTENT_HANDLES 0x40000000 +#define CAP_EXTENDED_SECURITY 0x80000000 + +typedef union smb_com_session_setup_andx { + struct { /* request format */ + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 SecurityBlobLength; + __u32 Reserved; + __le32 Capabilities; /* see below */ + __le16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __attribute__((packed)) req; /* NTLM request format (with + extended security */ + + struct { /* request format */ + struct smb_hdr hdr; /* wct = 13 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 CaseInsensitivePasswordLength; /* ASCII password len */ + __le16 CaseSensitivePasswordLength; /* Unicode password length*/ + __u32 Reserved; /* see below */ + __le32 Capabilities; + __le16 ByteCount; + unsigned char CaseInsensitivePassword[1]; /* followed by: */ + /* unsigned char * CaseSensitivePassword; */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __attribute__((packed)) req_no_secext; /* NTLM request format (without + extended security */ + + struct { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __le16 SecurityBlobLength; + __u16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ +/* unsigned char * NativeOS; */ +/* unsigned char * NativeLanMan; */ +/* unsigned char * PrimaryDomain; */ + } __attribute__((packed)) resp; /* NTLM response + (with or without extended sec) */ + + struct { /* request format */ + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 PasswordLength; + __u32 Reserved; /* encrypt key len and offset */ + __le16 ByteCount; + unsigned char AccountPassword[1]; /* followed by */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */ + + struct { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 3 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __u16 ByteCount; + unsigned char NativeOS[1]; /* followed by */ +/* unsigned char * NativeLanMan; */ +/* unsigned char * PrimaryDomain; */ + } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */ +} __attribute__((packed)) SESSION_SETUP_ANDX; + +/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ + +#define NTLMSSP_SERVER_TYPE 1 +#define NTLMSSP_DOMAIN_TYPE 2 +#define NTLMSSP_FQ_DOMAIN_TYPE 3 +#define NTLMSSP_DNS_DOMAIN_TYPE 4 +#define NTLMSSP_DNS_PARENT_TYPE 5 + +struct ntlmssp2_name { + __le16 type; + __le16 length; +/* char name[length]; */ +} __attribute__((packed)); + +struct ntlmv2_resp { + union { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + struct { + __u8 reserved[8]; + __u8 key[CIFS_SERVER_CHALLENGE_SIZE]; + } __attribute__((packed)) challenge; + } __attribute__((packed)); + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __attribute__((packed)); + + +#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux" + +/* Capabilities bits (for NTLM SessSetup request) */ +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_NT_FIND 0x00000200 /* reserved should be zero + (because NT_SMBs implies the same thing?) */ +#define CAP_BULK_TRANSFER 0x20000000 +#define CAP_EXTENDED_SECURITY 0x80000000 + +/* Action bits */ +#define GUEST_LOGIN 1 + +typedef struct smb_com_tconx_req { + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Flags; /* see below */ + __le16 PasswordLength; + __le16 ByteCount; + unsigned char Password[1]; /* followed by */ +/* STRING Path *//* \\server\share name */ + /* STRING Service */ +} __attribute__((packed)) TCONX_REQ; + +typedef struct smb_com_tconx_rsp { + struct smb_hdr hdr; /* wct = 3 , not extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __u16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __attribute__((packed)) TCONX_RSP; + +typedef struct smb_com_tconx_rsp_ext { + struct smb_hdr hdr; /* wct = 7, extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __le32 MaximalShareAccessRights; + __le32 GuestMaximalShareAccessRights; + __u16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __attribute__((packed)) TCONX_RSP_EXT; + + +/* tree connect Flags */ +#define DISCONNECT_TID 0x0001 +#define TCON_EXTENDED_SIGNATURES 0x0004 +#define TCON_EXTENDED_SECINFO 0x0008 + +/* OptionalSupport bits */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits + (exclusive searches supported) */ +#define SMB_SHARE_IS_IN_DFS 0x0002 +#define SMB_CSC_MASK 0x000C +/* CSC flags defined as follows */ +#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 +#define SMB_CSC_CACHE_AUTO_REINT 0x0004 +#define SMB_CSC_CACHE_VDO 0x0008 +#define SMB_CSC_NO_CACHING 0x000C +#define SMB_UNIQUE_FILE_NAME 0x0010 +#define SMB_EXTENDED_SIGNATURES 0x0020 + +/* services + * + * A: ie disk + * LPT1: ie printer + * IPC ie named pipe + * COMM + * ????? ie any type + * + */ + +typedef struct smb_com_echo_req { + struct smb_hdr hdr; + __le16 EchoCount; + __le16 ByteCount; + char Data[1]; +} __attribute__((packed)) ECHO_REQ; + +typedef struct smb_com_echo_rsp { + struct smb_hdr hdr; + __le16 SequenceNumber; + __le16 ByteCount; + char Data[1]; +} __attribute__((packed)) ECHO_RSP; + +typedef struct smb_com_logoff_andx_req { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __u16 AndXOffset; + __u16 ByteCount; +} __attribute__((packed)) LOGOFF_ANDX_REQ; + +typedef struct smb_com_logoff_andx_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __u16 AndXOffset; + __u16 ByteCount; +} __attribute__((packed)) LOGOFF_ANDX_RSP; + +typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on + tree_connect PDU to effect disconnect */ + /* tdis is probably simplest SMB PDU */ + struct { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bcc = 0 */ + } __attribute__((packed)) req; + struct { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bcc = 0 */ + } __attribute__((packed)) resp; +} __attribute__((packed)) TREE_DISCONNECT; + +typedef struct smb_com_close_req { + struct smb_hdr hdr; /* wct = 3 */ + __u16 FileID; + __u32 LastWriteTime; /* should be zero or -1 */ + __u16 ByteCount; /* 0 */ +} __attribute__((packed)) CLOSE_REQ; + +typedef struct smb_com_close_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) CLOSE_RSP; + +typedef struct smb_com_flush_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __u16 ByteCount; /* 0 */ +} __attribute__((packed)) FLUSH_REQ; + +typedef struct smb_com_findclose_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __u16 ByteCount; /* 0 */ +} __attribute__((packed)) FINDCLOSE_REQ; + +/* OpenFlags */ +#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ +#define REQ_OPLOCK 0x00000002 +#define REQ_BATCHOPLOCK 0x00000004 +#define REQ_OPENDIRONLY 0x00000008 +#define REQ_EXTENDED_INFO 0x00000010 + +/* File type */ +#define DISK_TYPE 0x0000 +#define BYTE_PIPE_TYPE 0x0001 +#define MESSAGE_PIPE_TYPE 0x0002 +#define PRINTER_TYPE 0x0003 +#define COMM_DEV_TYPE 0x0004 +#define UNKNOWN_TYPE 0xFFFF + +/* Device Type or File Status Flags */ +#define NO_EAS 0x0001 +#define NO_SUBSTREAMS 0x0002 +#define NO_REPARSETAG 0x0004 +/* following flags can apply if pipe */ +#define ICOUNT_MASK 0x00FF +#define PIPE_READ_MODE 0x0100 +#define NAMED_PIPE_TYPE 0x0400 +#define PIPE_END_POINT 0x4000 +#define BLOCKING_NAMED_PIPE 0x8000 + +typedef struct smb_com_open_req { /* also handles create */ + struct smb_hdr hdr; /* wct = 24 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 Reserved; /* Must Be Zero */ + __le16 NameLength; + __le32 OpenFlags; + __u32 RootDirectoryFid; + __le32 DesiredAccess; + __le64 AllocationSize; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le32 ImpersonationLevel; + __u8 SecurityFlags; + __le16 ByteCount; + char fileName[1]; +} __attribute__((packed)) OPEN_REQ; + +/* open response: oplock levels */ +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ + +/* open response for CreateAction shifted left */ +#define CIFS_CREATE_ACTION 0x20000 /* file created */ + +typedef struct smb_com_open_rsp { + struct smb_hdr hdr; /* wct = 34 BB */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) OPEN_RSP; + +typedef struct smb_com_open_rsp_ext { + struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u8 VolumeGUID[16]; + __u64 FileId; /* note no endian conversion - is opaque UniqueID */ + __le32 MaximalAccessRights; + __le32 GuestMaximalAccessRights; + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) OPEN_RSP_EXT; + + +/* format of legacy open request */ +typedef struct smb_com_openx_req { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OpenFlags; + __le16 Mode; + __le16 Sattr; /* search attributes */ + __le16 FileAttributes; /* dos attrs */ + __le32 CreateTime; /* os2 format */ + __le16 OpenFunction; + __le32 EndOfFile; + __le32 Timeout; + __le32 Reserved; + __le16 ByteCount; /* file name follows */ + char fileName[1]; +} __attribute__((packed)) OPENX_REQ; + +typedef struct smb_com_openx_rsp { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le16 FileAttributes; + __le32 LastWriteTime; /* os2 format */ + __le32 EndOfFile; + __le16 Access; + __le16 FileType; + __le16 IPCState; + __le16 Action; + __u32 FileId; + __u16 Reserved; + __u16 ByteCount; +} __attribute__((packed)) OPENX_RSP; + +/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ + +/* Legacy write request for older servers */ +typedef struct smb_com_writex_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; /* Timeout */ + __le16 WriteMode; /* 1 = write through */ + __le16 Remaining; + __le16 Reserved2; + __le16 DataLengthLow; + __le16 DataOffset; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ + char Data[]; +} __attribute__((packed)) WRITEX_REQ; + +typedef struct smb_com_write_req { + struct smb_hdr hdr; /* wct = 14 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; + __le16 WriteMode; + __le16 Remaining; + __le16 DataLengthHigh; + __le16 DataLengthLow; + __le16 DataOffset; + __le32 OffsetHigh; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ + char Data[]; +} __attribute__((packed)) WRITE_REQ; + +typedef struct smb_com_write_rsp { + struct smb_hdr hdr; /* wct = 6 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Count; + __le16 Remaining; + __le16 CountHigh; + __u16 Reserved; + __u16 ByteCount; +} __attribute__((packed)) WRITE_RSP; + +/* legacy read request for older servers */ +typedef struct smb_com_readx_req { + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 Reserved; + __le16 Remaining; + __le16 ByteCount; +} __attribute__((packed)) READX_REQ; + +typedef struct smb_com_read_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 MaxCountHigh; + __le16 Remaining; + __le32 OffsetHigh; + __le16 ByteCount; +} __attribute__((packed)) READ_REQ; + +typedef struct smb_com_read_rsp { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Remaining; + __le16 DataCompactionMode; + __le16 Reserved; + __le16 DataLength; + __le16 DataOffset; + __le16 DataLengthHigh; + __u64 Reserved2; + __u16 ByteCount; + /* read response data immediately follows */ +} __attribute__((packed)) READ_RSP; + +typedef struct locking_andx_range { + __le16 Pid; + __le16 Pad; + __le32 OffsetHigh; + __le32 OffsetLow; + __le32 LengthHigh; + __le32 LengthLow; +} __attribute__((packed)) LOCKING_ANDX_RANGE; + +#define LOCKING_ANDX_SHARED_LOCK 0x01 +#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define LOCKING_ANDX_CANCEL_LOCK 0x08 +#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ + +typedef struct smb_com_lock_req { + struct smb_hdr hdr; /* wct = 8 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __u8 LockType; + __u8 OplockLevel; + __le32 Timeout; + __le16 NumberOfUnlocks; + __le16 NumberOfLocks; + __le16 ByteCount; + LOCKING_ANDX_RANGE Locks[1]; +} __attribute__((packed)) LOCK_REQ; + +/* lock type */ +#define CIFS_RDLCK 0 +#define CIFS_WRLCK 1 +#define CIFS_UNLCK 2 +typedef struct cifs_posix_lock { + __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */ + __le16 lock_flags; /* 1 = Wait (only valid for setlock) */ + __le32 pid; + __le64 start; + __le64 length; + /* BB what about additional owner info to identify network client */ +} __attribute__((packed)) CIFS_POSIX_LOCK; + +typedef struct smb_com_lock_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 ByteCount; +} __attribute__((packed)) LOCK_RSP; + +typedef struct smb_com_rename_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __attribute__((packed)) RENAME_REQ; + + /* copy request flags */ +#define COPY_MUST_BE_FILE 0x0001 +#define COPY_MUST_BE_DIR 0x0002 +#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */ +#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */ +#define COPY_VERIFY_WRITES 0x0010 +#define COPY_TREE 0x0020 + +typedef struct smb_com_copy_req { + struct smb_hdr hdr; /* wct = 3 */ + __u16 Tid2; + __le16 OpenFunction; + __le16 Flags; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName string */ +} __attribute__((packed)) COPY_REQ; + +typedef struct smb_com_copy_rsp { + struct smb_hdr hdr; /* wct = 1 */ + __le16 CopyCount; /* number of files copied */ + __u16 ByteCount; /* may be zero */ + __u8 BufferFormat; /* 0x04 - only present if errored file follows */ + unsigned char ErrorFileName[1]; /* only present if error in copy */ +} __attribute__((packed)) COPY_RSP; + +#define CREATE_HARD_LINK 0x103 +#define MOVEFILE_COPY_ALLOWED 0x0002 +#define MOVEFILE_REPLACE_EXISTING 0x0001 + +typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ + struct smb_hdr hdr; /* wct = 4 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 Flags; /* spec says Information Level */ + __le32 ClusterCount; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __attribute__((packed)) NT_RENAME_REQ; + +typedef struct smb_com_rename_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) RENAME_RSP; + +typedef struct smb_com_delete_file_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __attribute__((packed)) DELETE_FILE_REQ; + +typedef struct smb_com_delete_file_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) DELETE_FILE_RSP; + +typedef struct smb_com_delete_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __attribute__((packed)) DELETE_DIRECTORY_REQ; + +typedef struct smb_com_delete_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) DELETE_DIRECTORY_RSP; + +typedef struct smb_com_create_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __attribute__((packed)) CREATE_DIRECTORY_REQ; + +typedef struct smb_com_create_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) CREATE_DIRECTORY_RSP; + +typedef struct smb_com_query_information_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* 1 + namelen + 1 */ + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char FileName[1]; +} __attribute__((packed)) QUERY_INFORMATION_REQ; + +typedef struct smb_com_query_information_rsp { + struct smb_hdr hdr; /* wct = 10 */ + __le16 attr; + __le32 last_write_time; + __le32 size; + __u16 reserved[5]; + __le16 ByteCount; /* bcc = 0 */ +} __attribute__((packed)) QUERY_INFORMATION_RSP; + +typedef struct smb_com_setattr_req { + struct smb_hdr hdr; /* wct = 8 */ + __le16 attr; + __le16 time_low; + __le16 time_high; + __le16 reserved[5]; /* must be zero */ + __u16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __attribute__((packed)) SETATTR_REQ; + +typedef struct smb_com_setattr_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) SETATTR_RSP; + +/* empty wct response to setattr */ + +/*******************************************************/ +/* NT Transact structure definitions follow */ +/* Currently only ioctl, acl (get security descriptor) */ +/* and notify are implemented */ +/*******************************************************/ +typedef struct smb_com_ntransact_req { + struct smb_hdr hdr; /* wct >= 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + /* SetupCount words follow then */ + __le16 ByteCount; + __u8 Pad[3]; + __u8 Parms[]; +} __attribute__((packed)) NTRANSACT_REQ; + +typedef struct smb_com_ntransact_rsp { + struct smb_hdr hdr; /* wct = 18 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 0 */ + __u16 ByteCount; + /* __u8 Pad[3]; */ + /* parms and data follow */ +} __attribute__((packed)) NTRANSACT_RSP; + +/* See MS-SMB 2.2.7.2.1.1 */ +struct srv_copychunk { + __le64 SourceOffset; + __le64 DestinationOffset; + __le32 CopyLength; + __u32 Reserved; +} __packed; + +typedef struct smb_com_transaction_ioctl_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + __le32 FunctionCode; + __u16 Fid; + __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ + __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ + __le16 ByteCount; + __u8 Pad[3]; + __u8 Data[1]; +} __attribute__((packed)) TRANSACT_IOCTL_REQ; + +typedef struct smb_com_transaction_compr_ioctl_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + __le32 FunctionCode; + __u16 Fid; + __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ + __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ + __le16 ByteCount; + __u8 Pad[3]; + __le16 compression_state; /* See below for valid flags */ +} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ; + +/* compression state flags */ +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_DEFAULT 0x0001 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +typedef struct smb_com_transaction_ioctl_rsp { + struct smb_hdr hdr; /* wct = 19 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 1 */ + __le16 ReturnedDataLen; + __u16 ByteCount; +} __attribute__((packed)) TRANSACT_IOCTL_RSP; + +#define CIFS_ACL_OWNER 1 +#define CIFS_ACL_GROUP 2 +#define CIFS_ACL_DACL 4 +#define CIFS_ACL_SACL 8 + +typedef struct smb_com_transaction_qsec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __attribute__((packed)) QUERY_SEC_DESC_REQ; + + +typedef struct smb_com_transaction_ssec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __attribute__((packed)) SET_SEC_DESC_REQ; + +typedef struct smb_com_transaction_change_notify_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand;/* 4 = Change Notify */ + __le32 CompletionFilter; /* operation to monitor */ + __u16 Fid; + __u8 WatchTree; /* 1 = Monitor subdirectories */ + __u8 Reserved2; + __le16 ByteCount; +/* __u8 Pad[3];*/ +/* __u8 Data[1];*/ +} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ; + +/* BB eventually change to use generic ntransact rsp struct + and validation routine */ +typedef struct smb_com_transaction_change_notify_rsp { + struct smb_hdr hdr; /* wct = 18 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 0 */ + __u16 ByteCount; + /* __u8 Pad[3]; */ +} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_RSP; +/* Completion Filter flags for Notify */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_NAME 0x00000003 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 + +/* response contains array of the following structures */ +struct file_notify_information { + __le32 NextEntryOffset; + __le32 Action; + __le32 FileNameLength; + __u8 FileName[]; +} __attribute__((packed)); + +/* For IO_REPARSE_TAG_SYMLINK */ +struct reparse_symlink_data { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __le32 Flags; + char PathBuffer[]; +} __attribute__((packed)); + +/* Flag above */ +#define SYMLINK_FLAG_RELATIVE 0x00000001 + +/* For IO_REPARSE_TAG_NFS */ +#define NFS_SPECFILE_LNK 0x00000000014B4E4C +#define NFS_SPECFILE_CHR 0x0000000000524843 +#define NFS_SPECFILE_BLK 0x00000000004B4C42 +#define NFS_SPECFILE_FIFO 0x000000004F464946 +#define NFS_SPECFILE_SOCK 0x000000004B434F53 +struct reparse_posix_data { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le64 InodeType; /* LNK, FIFO, CHR etc. */ + char PathBuffer[]; +} __attribute__((packed)); + +struct cifs_quota_data { + __u32 rsrvd1; /* 0 */ + __u32 sid_size; + __u64 rsrvd2; /* 0 */ + __u64 space_used; + __u64 soft_limit; + __u64 hard_limit; + char sid[1]; /* variable size? */ +} __attribute__((packed)); + +/* quota sub commands */ +#define QUOTA_LIST_CONTINUE 0 +#define QUOTA_LIST_START 0x100 +#define QUOTA_FOR_SID 0x101 + +struct trans2_req { + /* struct smb_hdr hdr precedes. Set wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* 1st setup word - SetupCount words follow */ + __le16 ByteCount; +} __attribute__((packed)); + +struct smb_t2_req { + struct smb_hdr hdr; + struct trans2_req t2_req; +} __attribute__((packed)); + +struct trans2_resp { + /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + /* SetupWords[SetupCount]; + __u16 ByteCount; + __u16 Reserved2;*/ + /* data area follows */ +} __attribute__((packed)); + +struct smb_t2_rsp { + struct smb_hdr hdr; + struct trans2_resp t2_rsp; +} __attribute__((packed)); + +/* PathInfo/FileInfo infolevels */ +#define SMB_INFO_STANDARD 1 +#define SMB_SET_FILE_EA 2 +#define SMB_QUERY_FILE_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 +#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_POSIX_ACL 0x204 +#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ +#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ +#define SMB_QUERY_POSIX_PERMISSION 0x207 +#define SMB_QUERY_POSIX_LOCK 0x208 +/* #define SMB_POSIX_OPEN 0x209 */ +/* #define SMB_POSIX_UNLINK 0x20a */ +#define SMB_QUERY_FILE__UNIX_INFO2 0x20b +#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee +#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 +#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ +#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 +#define SMB_QUERY_FILE_MODE_INFO 0x3f8 +#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 + + +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 +#define SMB_SET_FILE_UNIX_BASIC 0x200 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_SET_POSIX_ACL 0x204 +#define SMB_SET_XATTR 0x205 +#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ +#define SMB_SET_POSIX_LOCK 0x208 +#define SMB_POSIX_OPEN 0x209 +#define SMB_POSIX_UNLINK 0x20a +#define SMB_SET_FILE_UNIX_INFO2 0x20b +#define SMB_SET_FILE_BASIC_INFO2 0x3ec +#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ +#define SMB_FILE_ALL_INFO2 0x3fa +#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb +#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc +#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 +#define SMB_FILE_QUOTA_INFO 0x408 +#define SMB_FILE_REPARSEPOINT_INFO 0x409 +#define SMB_FILE_MAXIMUM_INFO 0x40d + +/* Find File infolevels */ +#define SMB_FIND_FILE_INFO_STANDARD 0x001 +#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 +#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 +#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 +#define SMB_FIND_FILE_UNIX 0x202 +/* #define SMB_FIND_FILE_POSIX_INFO 0x064 */ + +typedef struct smb_com_transaction2_qpi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __attribute__((packed)) TRANSACTION2_QPI_REQ; + +typedef struct smb_com_transaction2_qpi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ +} __attribute__((packed)) TRANSACTION2_QPI_RSP; + +typedef struct smb_com_transaction2_spi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __attribute__((packed)) TRANSACTION2_SPI_REQ; + +typedef struct smb_com_transaction2_spi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ +} __attribute__((packed)) TRANSACTION2_SPI_RSP; + +struct set_file_rename { + __le32 overwrite; /* 1 = overwrite dest */ + __u32 root_fid; /* zero */ + __le32 target_name_len; + char target_name[]; /* Must be unicode */ +} __attribute__((packed)); + +struct smb_com_transaction2_sfi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __u16 Fid; + __le16 InformationLevel; + __u16 Reserved4; + __u8 payload[]; +} __attribute__((packed)); + +struct smb_com_transaction2_sfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word reserved - + present for infolevels > 100 */ +} __attribute__((packed)); + +struct smb_t2_qfi_req { + struct smb_hdr hdr; + struct trans2_req t2; + __u8 Pad; + __u16 Fid; + __le16 InformationLevel; +} __attribute__((packed)); + +struct smb_t2_qfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word reserved - + present for infolevels > 100 */ +} __attribute__((packed)); + +/* + * Flags on T2 FINDFIRST and FINDNEXT + */ +#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 +#define CIFS_SEARCH_CLOSE_AT_END 0x0002 +#define CIFS_SEARCH_RETURN_RESUME 0x0004 +#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 +#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 + +/* + * Size of the resume key on FINDFIRST and FINDNEXT calls + */ +#define CIFS_SMB_RESUME_KEY_SIZE 4 + +typedef struct smb_com_transaction2_ffirst_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_FIND_FIRST */ + __le16 ByteCount; + __u8 Pad; + __le16 SearchAttributes; + __le16 SearchCount; + __le16 SearchFlags; + __le16 InformationLevel; + __le32 SearchStorageType; + char FileName[1]; +} __attribute__((packed)) TRANSACTION2_FFIRST_REQ; + +typedef struct smb_com_transaction2_ffirst_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __attribute__((packed)) TRANSACTION2_FFIRST_RSP; + +typedef struct smb_com_transaction2_ffirst_rsp_parms { + __u16 SearchHandle; + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __attribute__((packed)) T2_FFIRST_RSP_PARMS; + +typedef struct smb_com_transaction2_fnext_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_FIND_NEXT */ + __le16 ByteCount; + __u8 Pad; + __u16 SearchHandle; + __le16 SearchCount; + __le16 InformationLevel; + __u32 ResumeKey; + __le16 SearchFlags; + char ResumeFileName[]; +} __attribute__((packed)) TRANSACTION2_FNEXT_REQ; + +typedef struct smb_com_transaction2_fnext_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __attribute__((packed)) TRANSACTION2_FNEXT_RSP; + +typedef struct smb_com_transaction2_fnext_rsp_parms { + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __attribute__((packed)) T2_FNEXT_RSP_PARMS; + +/* QFSInfo Levels */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 +#define SMB_QUERY_POSIX_FS_INFO 0x201 +#define SMB_QUERY_POSIX_WHO_AM_I 0x202 +#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 +#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure + FILE_SYSTEM__UNIX_INFO to tell + whether new NTIOCTL available + (0xACE) for WAN friendly SMB + operations to be carried */ +#define SMB_QUERY_LABEL_INFO 0x3ea +#define SMB_QUERY_FS_QUOTA_INFO 0x3ee +#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef +#define SMB_QUERY_OBJECTID_INFO 0x3f0 + +typedef struct smb_com_transaction2_qfsi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; +} __attribute__((packed)) TRANSACTION2_QFSI_REQ; + +typedef struct smb_com_transaction_qfsi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u8 Pad; /* may be three bytes? *//* followed by data area */ +} __attribute__((packed)) TRANSACTION2_QFSI_RSP; + +typedef struct whoami_rsp_data { /* Query level 0x202 */ + __u32 flags; /* 0 = Authenticated user 1 = GUEST */ + __u32 mask; /* which flags bits server understands ie 0x0001 */ + __u64 unix_user_id; + __u64 unix_user_gid; + __u32 number_of_supplementary_gids; /* may be zero */ + __u32 number_of_sids; /* may be zero */ + __u32 length_of_sid_array; /* in bytes - may be zero */ + __u32 pad; /* reserved - MBZ */ + /* __u64 gid_array[0]; */ /* may be empty */ + /* __u8 * psid_list */ /* may be empty */ +} __attribute__((packed)) WHOAMI_RSP_DATA; + +/* SETFSInfo Levels */ +#define SMB_SET_CIFS_UNIX_INFO 0x200 +/* level 0x203 is defined above in list of QFS info levels */ +/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */ + +/* Level 0x200 request structure follows */ +typedef struct smb_com_transaction2_setfsi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 FileNum; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + __le16 ClientUnixMajor; /* Data start. */ + __le16 ClientUnixMinor; + __le64 ClientUnixCap; /* Data end */ +} __attribute__((packed)) TRANSACTION2_SETFSI_REQ; + +/* level 0x203 request structure follows */ +typedef struct smb_com_transaction2_setfs_enc_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 Reserved4; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + /* NTLMSSP Blob, Data start. */ +} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ; + +/* response for setfsinfo levels 0x200 and 0x203 */ +typedef struct smb_com_transaction2_setfsi_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __attribute__((packed)) TRANSACTION2_SETFSI_RSP; + +typedef struct smb_com_transaction2_get_dfs_refer_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length + perhaps?) followed by one byte pad - doesn't + seem to matter though */ + __le16 MaxReferralLevel; + char RequestFileName[1]; +} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ; + +#define DFS_VERSION cpu_to_le16(0x0003) + +/* DFS server target type */ +#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */ +#define DFS_TYPE_ROOT 0x0001 + +/* Referral Entry Flags */ +#define DFS_NAME_LIST_REF 0x0200 /* set for domain or DC referral responses */ +#define DFS_TARGET_SET_BOUNDARY 0x0400 /* only valid with version 4 dfs req */ + +typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */ + __le16 VersionNumber; /* must be 3 or 4 */ + __le16 Size; + __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ + __le16 ReferralEntryFlags; + __le32 TimeToLive; + __le16 DfsPathOffset; + __le16 DfsAlternatePathOffset; + __le16 NetworkAddressOffset; /* offset of the link target */ + __u8 ServiceSiteGuid[16]; /* MBZ, ignored */ +} __attribute__((packed)) REFERRAL3; + +struct get_dfs_referral_rsp { + __le16 PathConsumed; + __le16 NumberOfReferrals; + __le32 DFSFlags; + REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */ + /* followed by the strings pointed to by the referral structures */ +} __packed; + +typedef struct smb_com_transaction_get_dfs_refer_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; + __u8 Pad; + struct get_dfs_referral_rsp dfs_data; +} __packed TRANSACTION2_GET_DFS_REFER_RSP; + +/* DFS Flags */ +#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */ +#define DFSREF_STORAGE_SERVER 0x00000002 /* no further ref requests needed */ +#define DFSREF_TARGET_FAILBACK 0x00000004 /* only for DFS referral version 4 */ + +/* + ************************************************************************ + * All structs for everything above the SMB PDUs themselves + * (such as the T2 level specific data) go here + ************************************************************************ + */ + +/* + * Information on a server + */ + +struct serverInfo { + char name[16]; + unsigned char versionMajor; + unsigned char versionMinor; + unsigned long type; + unsigned int commentOffset; +} __attribute__((packed)); + +/* + * The following structure is the format of the data returned on a NetShareEnum + * with level "90" (x5A) + */ + +struct shareInfo { + char shareName[13]; + char pad; + unsigned short type; + unsigned int commentOffset; +} __attribute__((packed)); + +struct aliasInfo { + char aliasName[9]; + char pad; + unsigned int commentOffset; + unsigned char type[2]; +} __attribute__((packed)); + +struct aliasInfo92 { + int aliasNameOffset; + int serverNameOffset; + int shareNameOffset; +} __attribute__((packed)); + +typedef struct { + __le64 TotalAllocationUnits; + __le64 FreeAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */ + +typedef struct { + __le32 fsid; + __le32 SectorsPerAllocationUnit; + __le32 TotalAllocationUnits; + __le32 FreeAllocationUnits; + __le16 BytesPerSector; +} __attribute__((packed)) FILE_SYSTEM_ALLOC_INFO; + +typedef struct { + __le16 MajorVersionNumber; + __le16 MinorVersionNumber; + __le64 Capability; +} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/ + +/* Version numbers for CIFS UNIX major and minor. */ +#define CIFS_UNIX_MAJOR_VERSION 1 +#define CIFS_UNIX_MINOR_VERSION 0 + +/* Linux/Unix extensions capability flags */ +#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ +#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ +#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ +#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ +#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */ +#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based + calls including posix open + and posix unlink */ +#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up + to 0xFFFF00 */ +#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ +#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and + QFS PROXY call */ +#ifdef CONFIG_CIFS_POSIX +/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send + LockingX instead of posix locking call on unix sess (and we do not expect + LockingX to use different (ie Windows) semantics than posix locking on + the same session (if WINE needs to do this later, we can add this cap + back in later */ +/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ +#define CIFS_UNIX_CAP_MASK 0x000003db +#else +#define CIFS_UNIX_CAP_MASK 0x00000013 +#endif /* CONFIG_CIFS_POSIX */ + + +#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ + +typedef struct { + /* For undefined recommended transfer size return -1 in that field */ + __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ + __le32 BlockSize; + /* The next three fields are in terms of the block size. + (above). If block size is unknown, 4096 would be a + reasonable block size for a server to report. + Note that returning the blocks/blocksavail removes need + to make a second call (to QFSInfo level 0x103 to get this info. + UserBlockAvail is typically less than or equal to BlocksAvail, + if no distinction is made return the same value in each */ + __le64 TotalBlocks; + __le64 BlocksAvail; /* bfree */ + __le64 UserBlocksAvail; /* bavail */ + /* For undefined Node fields or FSID return -1 */ + __le64 TotalFileNodes; + __le64 FreeFileNodes; + __le64 FileSysIdentifier; /* fsid */ + /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ + /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ +} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO; + +/* DeviceType Flags */ +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 + +/* Device Characteristics */ +#define FILE_REMOVABLE_MEDIA 0x00000001 +#define FILE_READ_ONLY_DEVICE 0x00000002 +#define FILE_FLOPPY_DISKETTE 0x00000004 +#define FILE_WRITE_ONCE_MEDIA 0x00000008 +#define FILE_REMOTE_DEVICE 0x00000010 +#define FILE_DEVICE_IS_MOUNTED 0x00000020 +#define FILE_VIRTUAL_VOLUME 0x00000040 +#define FILE_DEVICE_SECURE_OPEN 0x00000100 +#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000 +#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000 +#define FILE_PORTABLE_DEVICE 0x00004000 +#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 + +typedef struct { + __le32 DeviceType; + __le32 DeviceCharacteristics; +} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ + +/* minimum includes first three fields, and empty FS Name */ +#define MIN_FS_ATTR_INFO_SIZE 12 + + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +typedef struct { + __le32 Attributes; + __le32 MaxPathNameComponentLength; + __le32 FileSystemNameLen; + char FileSystemName[52]; /* do not have to save this - get subset? */ +} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO; + +/******************************************************************************/ +/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ +/******************************************************************************/ +typedef struct { /* data block encoding of response to level 263 QPathInfo */ + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __u64 IndexNumber1; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ + +typedef struct { + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad; +} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ + + +/* defines for enumerating possible values of the Unix type field below */ +#define UNIX_FILE 0 +#define UNIX_DIR 1 +#define UNIX_SYMLINK 2 +#define UNIX_CHARDEV 3 +#define UNIX_BLOCKDEV 4 +#define UNIX_FIFO 5 +#define UNIX_SOCKET 6 +typedef struct { + __le64 EndOfFile; + __le64 NumOfBytes; + __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ + __le64 LastAccessTime; + __le64 LastModificationTime; + __le64 Uid; + __le64 Gid; + __le32 Type; + __le64 DevMajor; + __le64 DevMinor; + __le64 UniqueId; + __le64 Permissions; + __le64 Nlinks; +} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ + +typedef struct { + char LinkDest[1]; +} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */ + +/* The following three structures are needed only for + setting time to NT4 and some older servers via + the primitive DOS time format */ +typedef struct { + __u16 Day:5; + __u16 Month:4; + __u16 Year:7; +} __attribute__((packed)) SMB_DATE; + +typedef struct { + __u16 TwoSeconds:5; + __u16 Minutes:6; + __u16 Hours:5; +} __attribute__((packed)) SMB_TIME; + +typedef struct { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le32 EASize; +} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ + +typedef struct { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad; +} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */ + +struct file_allocation_info { + __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ +} __attribute__((packed)); /* size used on disk, for level 0x103 for set, + 0x105 for query */ + +struct file_end_of_file_info { + __le64 FileSize; /* offset to end of file */ +} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */ + +struct file_alt_name_info { + __u8 alt_name[1]; +} __attribute__((packed)); /* level 0x0108 */ + +struct file_stream_info { + __le32 number_of_streams; /* BB check sizes and verify location */ + /* followed by info on streams themselves + u64 size; + u64 allocation_size + stream info */ +}; /* level 0x109 */ + +struct file_compression_info { + __le64 compressed_size; + __le16 format; + __u8 unit_shift; + __u8 ch_shift; + __u8 cl_shift; + __u8 pad[3]; +} __attribute__((packed)); /* level 0x10b */ + +/* POSIX ACL set/query path info structures */ +#define CIFS_ACL_VERSION 1 +struct cifs_posix_ace { /* access control entry (ACE) */ + __u8 cifs_e_tag; + __u8 cifs_e_perm; + __le64 cifs_uid; /* or gid */ +} __attribute__((packed)); + +struct cifs_posix_acl { /* access conrol list (ACL) */ + __le16 version; + __le16 access_entry_count; /* access ACL - count of entries */ + __le16 default_entry_count; /* default ACL - count of entries */ + struct cifs_posix_ace ace_array[]; + /* followed by + struct cifs_posix_ace default_ace_arraay[] */ +} __attribute__((packed)); /* level 0x204 */ + +/* types of access control entries already defined in posix_acl.h */ +/* #define CIFS_POSIX_ACL_USER_OBJ 0x01 +#define CIFS_POSIX_ACL_USER 0x02 +#define CIFS_POSIX_ACL_GROUP_OBJ 0x04 +#define CIFS_POSIX_ACL_GROUP 0x08 +#define CIFS_POSIX_ACL_MASK 0x10 +#define CIFS_POSIX_ACL_OTHER 0x20 */ + +/* types of perms */ +/* #define CIFS_POSIX_ACL_EXECUTE 0x01 +#define CIFS_POSIX_ACL_WRITE 0x02 +#define CIFS_POSIX_ACL_READ 0x04 */ + +/* end of POSIX ACL definitions */ + +/* POSIX Open Flags */ +#define SMB_O_RDONLY 0x1 +#define SMB_O_WRONLY 0x2 +#define SMB_O_RDWR 0x4 +#define SMB_O_CREAT 0x10 +#define SMB_O_EXCL 0x20 +#define SMB_O_TRUNC 0x40 +#define SMB_O_APPEND 0x80 +#define SMB_O_SYNC 0x100 +#define SMB_O_DIRECTORY 0x200 +#define SMB_O_NOFOLLOW 0x400 +#define SMB_O_DIRECT 0x800 + +typedef struct { + __le32 OpenFlags; /* same as NT CreateX */ + __le32 PosixOpenFlags; + __le64 Permissions; + __le16 Level; /* reply level requested (see QPathInfo levels) */ +} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */ + +typedef struct { + __le16 OplockFlags; + __u16 Fid; + __le32 CreateAction; + __le16 ReturnedLevel; + __le16 Pad; + /* struct following varies based on requested level */ +} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ + +#define SMB_POSIX_UNLINK_FILE_TARGET 0 +#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 + +struct unlink_psx_rq { /* level 0x20a SetPathInfo */ + __le16 type; +} __attribute__((packed)); + +struct file_internal_info { + __le64 UniqueId; /* inode number */ +} __attribute__((packed)); /* level 0x3ee */ + +struct file_mode_info { + __le32 Mode; +} __attribute__((packed)); /* level 0x3f8 */ + +struct file_attrib_tag { + __le32 Attribute; + __le32 ReparseTag; +} __attribute__((packed)); /* level 0x40b */ + + +/********************************************************/ +/* FindFirst/FindNext transact2 data buffer formats */ +/********************************************************/ + +typedef struct { + __le32 NextEntryOffset; + __u32 ResumeKey; /* as with FileIndex - no need to convert */ + FILE_UNIX_BASIC_INFO basic; + char FileName[1]; +} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[1]; +} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + char FileName[1]; +} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ + char FileName[1]; +} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + char FileName[1]; +} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */ + +typedef struct { + __u32 ResumeKey; + __le16 CreationDate; /* SMB Date */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __u8 FileNameLength; + char FileName[1]; +} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */ + + +struct win_dev { + unsigned char type[8]; /* IntxCHR or IntxBLK */ + __le64 major; + __le64 minor; +} __attribute__((packed)); + +struct gea { + unsigned char name_len; + char name[1]; +} __attribute__((packed)); + +struct gealist { + unsigned long list_len; + struct gea list[1]; +} __attribute__((packed)); + +struct fea { + unsigned char EA_flags; + __u8 name_len; + __le16 value_len; + char name[1]; + /* optionally followed by value */ +} __attribute__((packed)); +/* flags for _FEA.fEA */ +#define FEA_NEEDEA 0x80 /* need EA bit */ + +struct fealist { + __le32 list_len; + struct fea list[1]; +} __attribute__((packed)); + +/* used to hold an arbitrary blob of data */ +struct data_blob { + __u8 *data; + size_t length; + void (*free) (struct data_blob *data_blob); +} __attribute__((packed)); + + +#ifdef CONFIG_CIFS_POSIX +/* + For better POSIX semantics from Linux client, (even better + than the existing CIFS Unix Extensions) we need updated PDUs for: + + 1) PosixCreateX - to set and return the mode, inode#, device info and + perhaps add a CreateDevice - to create Pipes and other special .inodes + Also note POSIX open flags + 2) Close - to return the last write time to do cache across close + more safely + 3) FindFirst return unique inode number - what about resume key, two + forms short (matches readdir) and full (enough info to cache inodes) + 4) Mkdir - set mode + + And under consideration: + 5) FindClose2 (return nanosecond timestamp ??) + 6) Use nanosecond timestamps throughout all time fields if + corresponding attribute flag is set + 7) sendfile - handle based copy + + what about fixing 64 bit alignment + + There are also various legacy SMB/CIFS requests used as is + + From existing Lanman and NTLM dialects: + -------------------------------------- + NEGOTIATE + SESSION_SETUP_ANDX (BB which?) + TREE_CONNECT_ANDX (BB which wct?) + TREE_DISCONNECT (BB add volume timestamp on response) + LOGOFF_ANDX + DELETE (note delete open file behavior) + DELETE_DIRECTORY + READ_AND_X + WRITE_AND_X + LOCKING_AND_X (note posix lock semantics) + RENAME (note rename across dirs and open file rename posix behaviors) + NT_RENAME (for hardlinks) Is this good enough for all features? + FIND_CLOSE2 + TRANSACTION2 (18 cases) + SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2 + (BB verify that never need to set allocation size) + SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via + Unix ext?) + + COPY (note support for copy across directories) - FUTURE, OPTIONAL + setting/getting OS/2 EAs - FUTURE (BB can this handle + setting Linux xattrs perfectly) - OPTIONAL + dnotify - FUTURE, OPTIONAL + quota - FUTURE, OPTIONAL + + Note that various requests implemented for NT interop such as + NT_TRANSACT (IOCTL) QueryReparseInfo + are unneeded to servers compliant with the CIFS POSIX extensions + + From CIFS Unix Extensions: + ------------------------- + T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks + T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2) + T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK) + T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing + inode fields + Actually a need QUERY_FILE_UNIX_INFO + since has inode num + BB what about a) blksize/blkbits/blocks + b) i_version + c) i_rdev + d) notify mask? + e) generation + f) size_seqcount + T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX + TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended + T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL + */ + +/* xsymlink is a symlink format (used by MacOS) that can be used + to save symlink info in a regular file when + mounted to operating systems that do not + support the cifs Unix extensions or EAs (for xattr + based symlinks). For such a file to be recognized + as containing symlink data: + + 1) file size must be 1067, + 2) signature must begin file data, + 3) length field must be set to ASCII representation + of a number which is less than or equal to 1024, + 4) md5 must match that of the path data */ + +struct xsymlink { + /* 1067 bytes */ + char signature[4]; /* XSym */ /* not null terminated */ + char cr0; /* \n */ +/* ASCII representation of length (4 bytes decimal) terminated by \n not null */ + char length[4]; + char cr1; /* \n */ +/* md5 of valid subset of path ie path[0] through path[length-1] */ + __u8 md5[32]; + char cr2; /* \n */ +/* if room left, then end with \n then 0x20s by convention but not required */ + char path[1024]; +} __attribute__((packed)); + +typedef struct file_xattr_info { + /* BB do we need another field for flags? BB */ + __u32 xattr_name_len; + __u32 xattr_value_len; + char xattr_name[]; + /* followed by xattr_value[xattr_value_len], no pad */ +} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info + level 0x205 */ + +/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */ + +typedef struct file_chattr_info { + __le64 mask; /* list of all possible attribute bits */ + __le64 mode; /* list of actual attribute bits on this inode */ +} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes + (chattr, chflags) level 0x206 */ +#endif /* POSIX */ +#endif /* _CIFSPDU_H */ diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h new file mode 100644 index 000000000000..98513f5af3f9 --- /dev/null +++ b/fs/smb/client/cifsproto.h @@ -0,0 +1,696 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#ifndef _CIFSPROTO_H +#define _CIFSPROTO_H +#include +#include "trace.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif + +struct statfs; +struct smb_rqst; +struct smb3_fs_context; + +/* + ***************************************************************** + * All Prototypes + ***************************************************************** + */ + +extern struct smb_hdr *cifs_buf_get(void); +extern void cifs_buf_release(void *); +extern struct smb_hdr *cifs_small_buf_get(void); +extern void cifs_small_buf_release(void *); +extern void free_rsp_buf(int, void *); +extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *, + unsigned int /* length */); +extern unsigned int _get_xid(void); +extern void _free_xid(unsigned int); +#define get_xid() \ +({ \ + unsigned int __xid = _get_xid(); \ + cifs_dbg(FYI, "VFS: in %s as Xid: %u with uid: %d\n", \ + __func__, __xid, \ + from_kuid(&init_user_ns, current_fsuid())); \ + trace_smb3_enter(__xid, __func__); \ + __xid; \ +}) + +#define free_xid(curr_xid) \ +do { \ + _free_xid(curr_xid); \ + cifs_dbg(FYI, "VFS: leaving %s (xid = %u) rc = %d\n", \ + __func__, curr_xid, (int)rc); \ + if (rc) \ + trace_smb3_exit_err(curr_xid, __func__, (int)rc); \ + else \ + trace_smb3_exit_done(curr_xid, __func__); \ +} while (0) +extern int init_cifs_idmap(void); +extern void exit_cifs_idmap(void); +extern int init_cifs_spnego(void); +extern void exit_cifs_spnego(void); +extern const char *build_path_from_dentry(struct dentry *, void *); +extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry, + void *page, bool prefix); +static inline void *alloc_dentry_path(void) +{ + return __getname(); +} + +static inline void free_dentry_path(void *page) +{ + if (page) + __putname(page); +} + +extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx, + struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, + int add_treename); +extern char *build_wildcard_path_from_dentry(struct dentry *direntry); +extern char *cifs_compose_mount_options(const char *sb_mountdata, + const char *fullpath, const struct dfs_info3_param *ref, + char **devname); +extern void delete_mid(struct mid_q_entry *mid); +extern void release_mid(struct mid_q_entry *mid); +extern void cifs_wake_up_task(struct mid_q_entry *mid); +extern int cifs_handle_standard(struct TCP_Server_Info *server, + struct mid_q_entry *mid); +extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); +extern int smb3_parse_opt(const char *options, const char *key, char **val); +extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); +extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); +extern int cifs_call_async(struct TCP_Server_Info *server, + struct smb_rqst *rqst, + mid_receive_t *receive, mid_callback_t *callback, + mid_handle_t *handle, void *cbdata, const int flags, + const struct cifs_credits *exist_credits); +extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses); +extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, int *resp_buf_type, + const int flags, struct kvec *resp_iov); +extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const int flags, const int num_rqst, + struct smb_rqst *rqst, int *resp_buf_type, + struct kvec *resp_iov); +extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, + struct smb_hdr * /* input */ , + struct smb_hdr * /* out */ , + int * /* bytes returned */ , const int); +extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, + char *in_buf, int flags); +extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *, + struct TCP_Server_Info *, + struct smb_rqst *); +extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, + struct smb_rqst *); +extern int cifs_check_receive(struct mid_q_entry *mid, + struct TCP_Server_Info *server, bool log_error); +extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, + unsigned int size, unsigned int *num, + struct cifs_credits *credits); +extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, + struct kvec *, int /* nvec to send */, + int * /* type of buf returned */, const int flags, + struct kvec * /* resp vec */); +extern int SendReceiveBlockingLock(const unsigned int xid, + struct cifs_tcon *ptcon, + struct smb_hdr *in_buf , + struct smb_hdr *out_buf, + int *bytes_returned); +void +cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, + bool all_channels); +void +cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session); +extern int cifs_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session); +extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr); +extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); +extern bool backup_cred(struct cifs_sb_info *); +extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); +extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, + unsigned int bytes_written); +extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int); +extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, + int flags, + struct cifsFileInfo **ret_file); +extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, + int flags, + struct cifsFileInfo **ret_file); +extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); +extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file); +extern unsigned int smbCalcSize(void *buf); +extern int decode_negTokenInit(unsigned char *security_blob, int length, + struct TCP_Server_Info *server); +extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); +extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port); +extern int map_smb_to_linux_error(char *buf, bool logErr); +extern int map_and_check_smb_error(struct mid_q_entry *mid, bool logErr); +extern void header_assemble(struct smb_hdr *, char /* command */ , + const struct cifs_tcon *, int /* length of + fixed section (word count) in two byte units */); +extern int small_smb_init_no_tc(const int smb_cmd, const int wct, + struct cifs_ses *ses, + void **request_buf); +extern enum securityEnum select_sectype(struct TCP_Server_Info *server, + enum securityEnum requested); +extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); +extern u64 cifs_UnixTimeToNT(struct timespec64); +extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, + int offset); +extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); +extern int cifs_get_writer(struct cifsInodeInfo *cinode); +extern void cifs_put_writer(struct cifsInodeInfo *cinode); +extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode); +extern int cifs_unlock_range(struct cifsFileInfo *cfile, + struct file_lock *flock, const unsigned int xid); +extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); + +extern void cifs_down_write(struct rw_semaphore *sem); +struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, + struct tcon_link *tlink, __u32 oplock, + const char *symlink_target); +extern int cifs_posix_open(const char *full_path, struct inode **inode, + struct super_block *sb, int mode, + unsigned int f_flags, __u32 *oplock, __u16 *netfid, + unsigned int xid); +void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr); +extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, + FILE_UNIX_BASIC_INFO *info, + struct cifs_sb_info *cifs_sb); +extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *, + struct cifs_sb_info *); +extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr); +extern struct inode *cifs_iget(struct super_block *sb, + struct cifs_fattr *fattr); + +int cifs_get_inode_info(struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, struct super_block *sb, int xid, + const struct cifs_fid *fid); +extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, + struct super_block *sb, unsigned int xid); +extern int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *search_path, + struct super_block *sb, unsigned int xid); +extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs, + unsigned int xid, const char *full_path, __u32 dosattr); +extern int cifs_rename_pending_delete(const char *full_path, + struct dentry *dentry, + const unsigned int xid); +extern int sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, + struct cifs_fattr *fattr, uint sidtype); +extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, + struct cifs_fattr *fattr, struct inode *inode, + bool get_mode_from_special_sid, + const char *path, const struct cifs_fid *pfid); +extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, + kuid_t uid, kgid_t gid); +extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *, + const char *, u32 *, u32); +extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *, + const struct cifs_fid *, u32 *, u32); +extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, + const char *, int); +extern unsigned int setup_authusers_ACE(struct cifs_ace *pace); +extern unsigned int setup_special_mode_ACE(struct cifs_ace *pace, __u64 nmode); +extern unsigned int setup_special_user_owner_ACE(struct cifs_ace *pace); + +extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); +extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, + unsigned int to_read); +extern ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server, + size_t to_read); +extern int cifs_read_page_from_socket(struct TCP_Server_Info *server, + struct page *page, + unsigned int page_offset, + unsigned int to_read); +extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb); +extern int cifs_match_super(struct super_block *, void *); +extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx); +extern void cifs_umount(struct cifs_sb_info *); +extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); +extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); + +extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u8 type, __u16 flags, + struct cifsLockInfo **conf_lock, + int rw_check); +extern void cifs_add_pending_open(struct cifs_fid *fid, + struct tcon_link *tlink, + struct cifs_pending_open *open); +extern void cifs_add_pending_open_locked(struct cifs_fid *fid, + struct tcon_link *tlink, + struct cifs_pending_open *open); +extern void cifs_del_pending_open(struct cifs_pending_open *open); + +extern bool cifs_is_deferred_close(struct cifsFileInfo *cfile, + struct cifs_deferred_close **dclose); + +extern void cifs_add_deferred_close(struct cifsFileInfo *cfile, + struct cifs_deferred_close *dclose); + +extern void cifs_del_deferred_close(struct cifsFileInfo *cfile); + +extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); + +extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); + +extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, + const char *path); +extern struct TCP_Server_Info * +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server); +extern void cifs_put_tcp_session(struct TCP_Server_Info *server, + int from_reconnect); +extern void cifs_put_tcon(struct cifs_tcon *tcon); + +#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) +extern void cifs_dfs_release_automount_timer(void); +#else /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ +#define cifs_dfs_release_automount_timer() do { } while (0) +#endif /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ + +void cifs_proc_init(void); +void cifs_proc_clean(void); + +extern void cifs_move_llist(struct list_head *source, struct list_head *dest); +extern void cifs_free_llist(struct list_head *llist); +extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); + +extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, + const struct nls_table *nlsc); + +extern int cifs_negotiate_protocol(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); +extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct nls_table *nls_info); +extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); +extern int CIFSSMBNegotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); + +extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *); + +extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, + const char *searchName, struct cifs_sb_info *cifs_sb, + __u16 *searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf, + bool msearch); + +extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, + __u16 searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf); + +extern int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 search_handle); + +extern int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_ALL_INFO *pFindData); +extern int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_Name, FILE_ALL_INFO *data, + int legacy /* whether to use old info level */, + const struct nls_table *nls_codepage, int remap); +extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + const struct nls_table *nls_codepage, int remap); + +extern int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); +extern int CIFSSMBUnixQPathInfo(const unsigned int xid, + struct cifs_tcon *tcon, + const unsigned char *searchName, + FILE_UNIX_BASIC_INFO *pFindData, + const struct nls_table *nls_codepage, int remap); + +extern int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, + const char *search_name, + struct dfs_info3_param **target_nodes, + unsigned int *num_of_nodes, + const struct nls_table *nls_codepage, int remap); + +extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, + unsigned int *num_of_nodes, + struct dfs_info3_param **target_nodes, + const struct nls_table *nls_codepage, int remap, + const char *searchName, bool is_unicode); +extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + struct smb3_fs_context *ctx); +extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); +extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); +extern int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, + __u64 cap); + +extern int CIFSSMBQFSAttributeInfo(const unsigned int xid, + struct cifs_tcon *tcon); +extern int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); + +extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb); +extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const FILE_BASIC_INFO *data, __u16 fid, + __u32 pid_of_opener); +extern int CIFSSMBSetFileDisposition(const unsigned int xid, + struct cifs_tcon *tcon, + bool delete_file, __u16 fid, + __u32 pid_of_opener); +extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_allocation); +extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, + bool set_allocation); + +struct cifs_unix_set_info_args { + __u64 ctime; + __u64 atime; + __u64 mtime; + __u64 mode; + kuid_t uid; + kgid_t gid; + dev_t device; +}; + +extern int CIFSSMBUnixSetFileInfo(const unsigned int xid, + struct cifs_tcon *tcon, + const struct cifs_unix_set_info_args *args, + u16 fid, u32 pid_of_opener); + +extern int CIFSSMBUnixSetPathInfo(const unsigned int xid, + struct cifs_tcon *tcon, const char *file_name, + const struct cifs_unix_set_info_args *args, + const struct nls_table *nls_codepage, + int remap); + +extern int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, __u16 type, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon, + int netfid, const char *target_name, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int CIFSUnixCreateHardLink(const unsigned int xid, + struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern int CIFSUnixCreateSymLink(const unsigned int xid, + struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, + struct cifs_tcon *tcon, + const unsigned char *searchName, char **syminfo, + const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, char **symlinkinfo, + const struct nls_table *nls_codepage); +extern int CIFSSMB_set_compression(const unsigned int xid, + struct cifs_tcon *tcon, __u16 fid); +extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, + int *oplock, FILE_ALL_INFO *buf); +extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const int disposition, + const int access_flags, const int omode, + __u16 *netfid, int *pOplock, FILE_ALL_INFO *, + const struct nls_table *nls_codepage, int remap); +extern int CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, + u32 posix_flags, __u64 mode, __u16 *netfid, + FILE_UNIX_BASIC_INFO *pRetData, + __u32 *pOplock, const char *name, + const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, + const int smb_file_id); + +extern int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, + const int smb_file_id); + +extern int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, + int *return_buf_type); +extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, const char *buf); +extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, const int nvec); +extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, __u64 *inode_number, + const struct nls_table *nls_codepage, + int remap); + +extern int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 netfid, const __u8 lock_type, + const __u32 num_unlock, const __u32 num_lock, + LOCKING_ANDX_RANGE *buf); +extern int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 netfid, const __u32 netpid, const __u64 len, + const __u64 offset, const __u32 numUnlock, + const __u32 numLock, const __u8 lockType, + const bool waitFlag, const __u8 oplock_level); +extern int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, + const loff_t start_offset, const __u64 len, + struct file_lock *, const __u16 lock_type, + const bool waitFlag); +extern int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBEcho(struct TCP_Server_Info *server); +extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); + +extern struct cifs_ses *sesInfoAlloc(void); +extern void sesInfoFree(struct cifs_ses *); +extern struct cifs_tcon *tconInfoAlloc(void); +extern void tconInfoFree(struct cifs_tcon *); + +extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number); +extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *, + __u32 *); +extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); +extern int cifs_verify_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + __u32 expected_sequence_number); +extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); +extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); +extern int calc_seckey(struct cifs_ses *); +extern int generate_smb30signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server); +extern int generate_smb311signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server); + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +extern int CIFSSMBCopy(unsigned int xid, + struct cifs_tcon *source_tcon, + const char *fromName, + const __u16 target_tid, + const char *toName, const int flags, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + const unsigned char *ea_name, char *EAData, + size_t bufsize, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const char *ea_name, + const void *ea_value, const __u16 ea_value_len, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb); +extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); +extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16, + struct cifs_ntsd *, __u32, int); +extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + char *acl_inf, const int buflen, const int acl_type, + const struct nls_table *nls_codepage, int remap_special_chars); +extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *fileName, + const char *local_acl, const int buflen, const int acl_type, + const struct nls_table *nls_codepage, int remap_special_chars); +extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, + const int netfid, __u64 *pExtAttrBits, __u64 *pMask); +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ +extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); +extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr); +extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + struct cifs_fattr *fattr, + const unsigned char *path); +extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, + const struct nls_table *codepage); + +extern int +cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname); + +extern struct TCP_Server_Info * +cifs_find_tcp_session(struct smb3_fs_context *ctx); + +extern void cifs_put_smb_ses(struct cifs_ses *ses); + +extern struct cifs_ses * +cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx); + +void cifs_readdata_release(struct kref *refcount); +int cifs_async_readv(struct cifs_readdata *rdata); +int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); + +int cifs_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)); +void cifs_writev_complete(struct work_struct *work); +struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, + work_func_t complete); +struct cifs_writedata *cifs_writedata_direct_alloc(struct page **pages, + work_func_t complete); +void cifs_writedata_release(struct kref *refcount); +int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const unsigned char *path, char *pbuf, + unsigned int *pbytes_read); +int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const unsigned char *path, char *pbuf, + unsigned int *pbytes_written); +int __cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature, + struct shash_desc *shash); +enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, + enum securityEnum); +struct cifs_aio_ctx *cifs_aio_ctx_alloc(void); +void cifs_aio_ctx_release(struct kref *refcount); +int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw); + +int cifs_alloc_hash(const char *name, struct shash_desc **sdesc); +void cifs_free_hash(struct shash_desc **sdesc); + +void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, + unsigned int *len, unsigned int *offset); +struct cifs_chan * +cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server); +int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses); +bool is_server_using_iface(struct TCP_Server_Info *server, + struct cifs_server_iface *iface); +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); +void cifs_ses_mark_for_reconnect(struct cifs_ses *ses); + +unsigned int +cifs_ses_get_chan_index(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_set_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_clear_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +bool +cifs_chan_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_set_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_clear_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +bool +cifs_chan_needs_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +bool +cifs_chan_is_iface_active(struct cifs_ses *ses, + struct TCP_Server_Info *server); +int +cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server); +int +SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount); + +void extract_unc_hostname(const char *unc, const char **h, size_t *len); +int copy_path_name(char *dst, const char *src); +int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, + int resp_buftype, + struct cifs_search_info *srch_inf); + +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); +void cifs_put_tcp_super(struct super_block *sb); +int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); +char *extract_hostname(const char *unc); +char *extract_sharename(const char *unc); + +#ifdef CONFIG_CIFS_DFS_UPCALL +static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, + const char *old_path, + const struct nls_table *nls_codepage, + struct dfs_info3_param *referral, int remap) +{ + return dfs_cache_find(xid, ses, nls_codepage, remap, old_path, + referral, NULL); +} + +int match_target_ip(struct TCP_Server_Info *server, + const char *share, size_t share_len, + bool *result); +int cifs_inval_name_dfs_link_error(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + bool *islink); +#else +static inline int cifs_inval_name_dfs_link_error(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + bool *islink) +{ + *islink = false; + return 0; +} +#endif + +static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) +{ + if (cifs_sb && (backup_cred(cifs_sb))) + return options | CREATE_OPEN_BACKUP_INTENT; + else + return options; +} + +struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); +void cifs_put_tcon_super(struct super_block *sb); +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); + +#endif /* _CIFSPROTO_H */ diff --git a/fs/smb/client/cifsroot.c b/fs/smb/client/cifsroot.c new file mode 100644 index 000000000000..56ec1b233f52 --- /dev/null +++ b/fs/smb/client/cifsroot.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMB root file system support + * + * Copyright (c) 2019 Paulo Alcantara + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_MNT_OPTS \ + "vers=1.0,cifsacl,mfsymlinks,rsize=1048576,wsize=65536,uid=0,gid=0," \ + "hard,rootfs" + +static char root_dev[2048] __initdata = ""; +static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS; + +static __be32 __init parse_srvaddr(char *start, char *end) +{ + /* TODO: ipv6 support */ + char addr[sizeof("aaa.bbb.ccc.ddd")]; + int i = 0; + + while (start < end && i < sizeof(addr) - 1) { + if (isdigit(*start) || *start == '.') + addr[i++] = *start; + start++; + } + addr[i] = '\0'; + return in_aton(addr); +} + +/* cifsroot=///[,options] */ +static int __init cifs_root_setup(char *line) +{ + char *s; + int len; + __be32 srvaddr = htonl(INADDR_NONE); + + ROOT_DEV = Root_CIFS; + + if (strlen(line) > 3 && line[0] == '/' && line[1] == '/') { + s = strchr(&line[2], '/'); + if (!s || s[1] == '\0') + return 1; + + /* make s point to ',' or '\0' at end of line */ + s = strchrnul(s, ','); + /* len is strlen(unc) + '\0' */ + len = s - line + 1; + if (len > sizeof(root_dev)) { + pr_err("Root-CIFS: UNC path too long\n"); + return 1; + } + strscpy(root_dev, line, len); + srvaddr = parse_srvaddr(&line[2], s); + if (*s) { + int n = snprintf(root_opts, + sizeof(root_opts), "%s,%s", + DEFAULT_MNT_OPTS, s + 1); + if (n >= sizeof(root_opts)) { + pr_err("Root-CIFS: mount options string too long\n"); + root_opts[sizeof(root_opts)-1] = '\0'; + return 1; + } + } + } + + root_server_addr = srvaddr; + + return 1; +} + +__setup("cifsroot=", cifs_root_setup); + +int __init cifs_root_data(char **dev, char **opts) +{ + if (!root_dev[0] || root_server_addr == htonl(INADDR_NONE)) { + pr_err("Root-CIFS: no SMB server address\n"); + return -1; + } + + *dev = root_dev; + *opts = root_opts; + + return 0; +} diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c new file mode 100644 index 000000000000..c90d4ec9292c --- /dev/null +++ b/fs/smb/client/cifssmb.c @@ -0,0 +1,5873 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2010 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Contains the routines for constructing the SMB PDUs themselves + * + */ + + /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ + /* These are mostly routines that operate on a pathname, or on a tree id */ + /* (mounted volume), but there are eight handle based routines which must be */ + /* treated slightly differently for reconnection purposes since we never */ + /* want to reuse a stale file handle and only the caller knows the file info */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "fscache.h" +#include "smbdirect.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif + +#ifdef CONFIG_CIFS_POSIX +static struct { + int index; + char *name; +} protocols[] = { + {CIFS_PROT, "\2NT LM 0.12"}, + {POSIX_PROT, "\2POSIX 2"}, + {BAD_PROT, "\2"} +}; +#else +static struct { + int index; + char *name; +} protocols[] = { + {CIFS_PROT, "\2NT LM 0.12"}, + {BAD_PROT, "\2"} +}; +#endif + +/* define the number of elements in the cifs dialect array */ +#ifdef CONFIG_CIFS_POSIX +#define CIFS_NUM_PROT 2 +#else /* not posix */ +#define CIFS_NUM_PROT 1 +#endif /* CIFS_POSIX */ + + +/* reconnect the socket, tcon, and smb session if needed */ +static int +cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) +{ + int rc; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_codepage; + + /* + * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for + * tcp and smb session status done differently for those three - in the + * calling routine + */ + if (!tcon) + return 0; + + ses = tcon->ses; + server = ses->server; + + /* + * only tree disconnect, open, and write, (and ulogoff which does not + * have tcon) are allowed as we start umount + */ + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_EXITING) { + if (smb_command != SMB_COM_TREE_DISCONNECT) { + spin_unlock(&tcon->tc_lock); + cifs_dbg(FYI, "can not send cmd %d while umounting\n", + smb_command); + return -ENODEV; + } + } + spin_unlock(&tcon->tc_lock); + + rc = cifs_wait_for_server_reconnect(server, tcon->retry); + if (rc) + return rc; + + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { + spin_unlock(&ses->chan_lock); + return 0; + } + spin_unlock(&ses->chan_lock); + + nls_codepage = load_nls_default(); + + /* + * Recheck after acquire mutex. If another thread is negotiating + * and the server never sends an answer the socket will be closed + * and tcpStatus set to reconnect. + */ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + rc = -EHOSTDOWN; + goto out; + } + spin_unlock(&server->srv_lock); + + /* + * need to prevent multiple threads trying to simultaneously + * reconnect the same SMB session + */ + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + + /* this means that we only need to tree connect */ + if (tcon->need_reconnect) + goto skip_sess_setup; + + rc = -EHOSTDOWN; + goto out; + } + spin_unlock(&ses->chan_lock); + + mutex_lock(&ses->session_mutex); + rc = cifs_negotiate_protocol(0, ses, server); + if (!rc) + rc = cifs_setup_session(0, ses, server, nls_codepage); + + /* do we need to reconnect tcon? */ + if (rc || !tcon->need_reconnect) { + mutex_unlock(&ses->session_mutex); + goto out; + } + +skip_sess_setup: + cifs_mark_open_files_invalid(tcon); + rc = cifs_tree_connect(0, tcon, nls_codepage); + mutex_unlock(&ses->session_mutex); + cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); + + if (rc) { + pr_warn_once("reconnect tcon failed rc = %d\n", rc); + goto out; + } + + atomic_inc(&tconInfoReconnectCount); + + /* tell server Unix caps we support */ + if (cap_unix(ses)) + reset_cifs_unix_caps(0, tcon, NULL, NULL); + + /* + * Removed call to reopen open files here. It is safer (and faster) to + * reopen files one at a time as needed in read and write. + * + * FIXME: what about file locks? don't we need to reclaim them ASAP? + */ + +out: + /* + * Check if handle based operation so we know whether we can continue + * or not without returning to caller to reset file handle + */ + switch (smb_command) { + case SMB_COM_READ_ANDX: + case SMB_COM_WRITE_ANDX: + case SMB_COM_CLOSE: + case SMB_COM_FIND_CLOSE2: + case SMB_COM_LOCKING_ANDX: + rc = -EAGAIN; + } + + unload_nls(nls_codepage); + return rc; +} + +/* Allocate and return pointer to an SMB request buffer, and set basic + SMB information in the SMB header. If the return code is zero, this + function must have filled in request_buf pointer */ +static int +small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf) +{ + int rc; + + rc = cifs_reconnect_tcon(tcon, smb_command); + if (rc) + return rc; + + *request_buf = cifs_small_buf_get(); + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + + header_assemble((struct smb_hdr *) *request_buf, smb_command, + tcon, wct); + + if (tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + + return 0; +} + +int +small_smb_init_no_tc(const int smb_command, const int wct, + struct cifs_ses *ses, void **request_buf) +{ + int rc; + struct smb_hdr *buffer; + + rc = small_smb_init(smb_command, wct, NULL, request_buf); + if (rc) + return rc; + + buffer = (struct smb_hdr *)*request_buf; + buffer->Mid = get_next_mid(ses->server); + if (ses->capabilities & CAP_UNICODE) + buffer->Flags2 |= SMBFLG2_UNICODE; + if (ses->capabilities & CAP_STATUS32) + buffer->Flags2 |= SMBFLG2_ERR_STATUS; + + /* uid, tid can stay at zero as set in header assemble */ + + /* BB add support for turning on the signing when + this function is used after 1st of session setup requests */ + + return rc; +} + +/* If the return code is zero, this function must fill in request_buf pointer */ +static int +__smb_init(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf, void **response_buf) +{ + *request_buf = cifs_buf_get(); + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + /* Although the original thought was we needed the response buf for */ + /* potential retries of smb operations it turns out we can determine */ + /* from the mid flags when the request buffer can be resent without */ + /* having to use a second distinct buffer for the response */ + if (response_buf) + *response_buf = *request_buf; + + header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, + wct); + + if (tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + + return 0; +} + +/* If the return code is zero, this function must fill in request_buf pointer */ +static int +smb_init(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf, void **response_buf) +{ + int rc; + + rc = cifs_reconnect_tcon(tcon, smb_command); + if (rc) + return rc; + + return __smb_init(smb_command, wct, tcon, request_buf, response_buf); +} + +static int +smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf, void **response_buf) +{ + spin_lock(&tcon->ses->chan_lock); + if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) || + tcon->need_reconnect) { + spin_unlock(&tcon->ses->chan_lock); + return -EHOSTDOWN; + } + spin_unlock(&tcon->ses->chan_lock); + + return __smb_init(smb_command, wct, tcon, request_buf, response_buf); +} + +static int validate_t2(struct smb_t2_rsp *pSMB) +{ + unsigned int total_size; + + /* check for plausible wct */ + if (pSMB->hdr.WordCount < 10) + goto vt2_err; + + /* check for parm and data offset going beyond end of smb */ + if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 || + get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024) + goto vt2_err; + + total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount); + if (total_size >= 512) + goto vt2_err; + + /* check that bcc is at least as big as parms + data, and that it is + * less than negotiated smb buffer + */ + total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount); + if (total_size > get_bcc(&pSMB->hdr) || + total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) + goto vt2_err; + + return 0; +vt2_err: + cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, + sizeof(struct smb_t2_rsp) + 16); + return -EINVAL; +} + +static int +decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) +{ + int rc = 0; + u16 count; + char *guid = pSMBr->u.extended_response.GUID; + struct TCP_Server_Info *server = ses->server; + + count = get_bcc(&pSMBr->hdr); + if (count < SMB1_CLIENT_GUID_SIZE) + return -EIO; + + spin_lock(&cifs_tcp_ses_lock); + if (server->srv_count > 1) { + spin_unlock(&cifs_tcp_ses_lock); + if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) { + cifs_dbg(FYI, "server UID changed\n"); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + } else { + spin_unlock(&cifs_tcp_ses_lock); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + + if (count == SMB1_CLIENT_GUID_SIZE) { + server->sec_ntlmssp = true; + } else { + count -= SMB1_CLIENT_GUID_SIZE; + rc = decode_negTokenInit( + pSMBr->u.extended_response.SecurityBlob, count, server); + if (rc != 1) + return -EINVAL; + } + + return 0; +} + +static bool +should_set_ext_sec_flag(enum securityEnum sectype) +{ + switch (sectype) { + case RawNTLMSSP: + case Kerberos: + return true; + case Unspecified: + if (global_secflags & + (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)) + return true; + fallthrough; + default: + return false; + } +} + +int +CIFSSMBNegotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + NEGOTIATE_REQ *pSMB; + NEGOTIATE_RSP *pSMBr; + int rc = 0; + int bytes_returned; + int i; + u16 count; + + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; + } + + rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , + (void **) &pSMB, (void **) &pSMBr); + if (rc) + return rc; + + pSMB->hdr.Mid = get_next_mid(server); + pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); + + if (should_set_ext_sec_flag(ses->sectype)) { + cifs_dbg(FYI, "Requesting extended security\n"); + pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; + } + + count = 0; + /* + * We know that all the name entries in the protocols array + * are short (< 16 bytes anyway) and are NUL terminated. + */ + for (i = 0; i < CIFS_NUM_PROT; i++) { + size_t len = strlen(protocols[i].name) + 1; + + memcpy(&pSMB->DialectsArray[count], protocols[i].name, len); + count += len; + } + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc != 0) + goto neg_err_exit; + + server->dialect = le16_to_cpu(pSMBr->DialectIndex); + cifs_dbg(FYI, "Dialect: %d\n", server->dialect); + /* Check wct = 1 error case */ + if ((pSMBr->hdr.WordCount <= 13) || (server->dialect == BAD_PROT)) { + /* core returns wct = 1, but we do not ask for core - otherwise + small wct just comes when dialect index is -1 indicating we + could not negotiate a common dialect */ + rc = -EOPNOTSUPP; + goto neg_err_exit; + } else if (pSMBr->hdr.WordCount != 17) { + /* unknown wct */ + rc = -EOPNOTSUPP; + goto neg_err_exit; + } + /* else wct == 17, NTLM or better */ + + server->sec_mode = pSMBr->SecurityMode; + if ((server->sec_mode & SECMODE_USER) == 0) + cifs_dbg(FYI, "share mode security\n"); + + /* one byte, so no need to convert this or EncryptionKeyLen from + little endian */ + server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), + cifs_max_pending); + set_credits(server, server->maxReq); + /* probably no need to store and check maxvcs */ + server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); + /* set up max_read for readahead check */ + server->max_read = server->maxBuf; + server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); + cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf); + server->capabilities = le32_to_cpu(pSMBr->Capabilities); + server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); + server->timeAdj *= 60; + + if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; + memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, + CIFS_CRYPTO_KEY_SIZE); + } else if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || + server->capabilities & CAP_EXTENDED_SECURITY) { + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; + rc = decode_ext_sec_blob(ses, pSMBr); + } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { + rc = -EIO; /* no crypt key only if plain text pwd */ + } else { + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; + server->capabilities &= ~CAP_EXTENDED_SECURITY; + } + + if (!rc) + rc = cifs_enable_signing(server, ses->sign); +neg_err_exit: + cifs_buf_release(pSMB); + + cifs_dbg(FYI, "negprot rc %d\n", rc); + return rc; +} + +int +CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) +{ + struct smb_hdr *smb_buffer; + int rc = 0; + + cifs_dbg(FYI, "In tree disconnect\n"); + + /* BB: do we need to check this? These should never be NULL. */ + if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) + return -EIO; + + /* + * No need to return error on this operation if tid invalidated and + * closed on server already e.g. due to tcp session crashing. Also, + * the tcon is no longer on the list, so no need to take lock before + * checking this. + */ + spin_lock(&tcon->ses->chan_lock); + if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) { + spin_unlock(&tcon->ses->chan_lock); + return -EIO; + } + spin_unlock(&tcon->ses->chan_lock); + + rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, + (void **)&smb_buffer); + if (rc) + return rc; + + rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0); + cifs_small_buf_release(smb_buffer); + if (rc) + cifs_dbg(FYI, "Tree disconnect failed %d\n", rc); + + /* No need to return error on this operation if tid invalidated and + closed on server already e.g. due to tcp session crashing */ + if (rc == -EAGAIN) + rc = 0; + + return rc; +} + +/* + * This is a no-op for now. We're not really interested in the reply, but + * rather in the fact that the server sent one and that server->lstrp + * gets updated. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +cifs_echo_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->callback_data; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + + release_mid(mid); + add_credits(server, &credits, CIFS_ECHO_OP); +} + +int +CIFSSMBEcho(struct TCP_Server_Info *server) +{ + ECHO_REQ *smb; + int rc = 0; + struct kvec iov[2]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 2 }; + + cifs_dbg(FYI, "In echo request\n"); + + rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb); + if (rc) + return rc; + + if (server->capabilities & CAP_UNICODE) + smb->hdr.Flags2 |= SMBFLG2_UNICODE; + + /* set up echo request */ + smb->hdr.Tid = 0xffff; + smb->hdr.WordCount = 1; + put_unaligned_le16(1, &smb->EchoCount); + put_bcc(1, &smb->hdr); + smb->Data[0] = 'a'; + inc_rfc1001_len(smb, 3); + + iov[0].iov_len = 4; + iov[0].iov_base = smb; + iov[1].iov_len = get_rfc1002_length(smb); + iov[1].iov_base = (char *)smb + 4; + + rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL, + server, CIFS_NON_BLOCKING | CIFS_ECHO_OP, NULL); + if (rc) + cifs_dbg(FYI, "Echo request failed: %d\n", rc); + + cifs_small_buf_release(smb); + + return rc; +} + +int +CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) +{ + LOGOFF_ANDX_REQ *pSMB; + int rc = 0; + + cifs_dbg(FYI, "In SMBLogoff for session disconnect\n"); + + /* + * BB: do we need to check validity of ses and server? They should + * always be valid since we have an active reference. If not, that + * should probably be a BUG() + */ + if (!ses || !ses->server) + return -EIO; + + mutex_lock(&ses->session_mutex); + spin_lock(&ses->chan_lock); + if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { + spin_unlock(&ses->chan_lock); + goto session_already_dead; /* no need to send SMBlogoff if uid + already closed due to reconnect */ + } + spin_unlock(&ses->chan_lock); + + rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); + if (rc) { + mutex_unlock(&ses->session_mutex); + return rc; + } + + pSMB->hdr.Mid = get_next_mid(ses->server); + + if (ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + pSMB->hdr.Uid = ses->Suid; + + pSMB->AndXCommand = 0xFF; + rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); +session_already_dead: + mutex_unlock(&ses->session_mutex); + + /* if session dead then we do not need to do ulogoff, + since server closed smb session, no sense reporting + error */ + if (rc == -EAGAIN) + rc = 0; + return rc; +} + +int +CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, __u16 type, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + struct unlink_psx_rq *pRqD; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cifs_dbg(FYI, "In POSIX delete\n"); +PsxDelete: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + + params = 6 + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = 0; /* BB double check this with jra */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* Setup pointer to Request Data (inode type). + * Note that SMB offsets are from the beginning of SMB which is 4 bytes + * in, after RFC1001 field + */ + pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset + 4); + pRqD->type = cpu_to_le16(type); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq); + + pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "Posix delete returned %d\n", rc); + cifs_buf_release(pSMB); + + cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes); + + if (rc == -EAGAIN) + goto PsxDelete; + + return rc; +} + +int +CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + DELETE_FILE_REQ *pSMB = NULL; + DELETE_FILE_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + int remap = cifs_remap(cifs_sb); + +DelFileRetry: + rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name, + PATH_MAX, cifs_sb->local_nls, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->fileName, name); + } + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM); + pSMB->BufferFormat = 0x04; + inc_rfc1001_len(pSMB, name_len + 1); + pSMB->ByteCount = cpu_to_le16(name_len + 1); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes); + if (rc) + cifs_dbg(FYI, "Error in RMFile = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto DelFileRetry; + + return rc; +} + +int +CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + DELETE_DIRECTORY_REQ *pSMB = NULL; + DELETE_DIRECTORY_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSSMBRmDir\n"); +RmDirRetry: + rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, + PATH_MAX, cifs_sb->local_nls, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->DirName, name); + } + + pSMB->BufferFormat = 0x04; + inc_rfc1001_len(pSMB, name_len + 1); + pSMB->ByteCount = cpu_to_le16(name_len + 1); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_rmdirs); + if (rc) + cifs_dbg(FYI, "Error in RMDir = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto RmDirRetry; + return rc; +} + +int +CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + int rc = 0; + CREATE_DIRECTORY_REQ *pSMB = NULL; + CREATE_DIRECTORY_RSP *pSMBr = NULL; + int bytes_returned; + int name_len; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSSMBMkDir\n"); +MkDirRetry: + rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, + PATH_MAX, cifs_sb->local_nls, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->DirName, name); + } + + pSMB->BufferFormat = 0x04; + inc_rfc1001_len(pSMB, name_len + 1); + pSMB->ByteCount = cpu_to_le16(name_len + 1); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_mkdirs); + if (rc) + cifs_dbg(FYI, "Error in Mkdir = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto MkDirRetry; + return rc; +} + +int +CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, + __u32 posix_flags, __u64 mode, __u16 *netfid, + FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, + const char *name, const struct nls_table *nls_codepage, + int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count, count; + OPEN_PSX_REQ *pdata; + OPEN_PSX_RSP *psx_rsp; + + cifs_dbg(FYI, "In POSIX Create\n"); +PsxCreat: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, name, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, name); + } + + params = 6 + name_len; + count = sizeof(OPEN_PSX_REQ); + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset + 4); + pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); + pdata->Permissions = cpu_to_le64(mode); + pdata->PosixOpenFlags = cpu_to_le32(posix_flags); + pdata->OpenFlags = cpu_to_le32(*pOplock); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Posix create returned %d\n", rc); + goto psx_create_err; + } + + cifs_dbg(FYI, "copying inode info\n"); + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) { + rc = -EIO; /* bad smb */ + goto psx_create_err; + } + + /* copy return information to pRetData */ + psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset)); + + *pOplock = le16_to_cpu(psx_rsp->OplockFlags); + if (netfid) + *netfid = psx_rsp->Fid; /* cifs fid stays in le */ + /* Let caller know file was created so we can set the mode. */ + /* Do we care about the CreateAction in any other cases? */ + if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) + *pOplock |= CIFS_CREATE_ACTION; + /* check to make sure response data is there */ + if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) { + pRetData->Type = cpu_to_le32(-1); /* unknown */ + cifs_dbg(NOISY, "unknown type\n"); + } else { + if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP) + + sizeof(FILE_UNIX_BASIC_INFO)) { + cifs_dbg(VFS, "Open response data too small\n"); + pRetData->Type = cpu_to_le32(-1); + goto psx_create_err; + } + memcpy((char *) pRetData, + (char *)psx_rsp + sizeof(OPEN_PSX_RSP), + sizeof(FILE_UNIX_BASIC_INFO)); + } + +psx_create_err: + cifs_buf_release(pSMB); + + if (posix_flags & SMB_O_DIRECTORY) + cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs); + else + cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens); + + if (rc == -EAGAIN) + goto PsxCreat; + + return rc; +} + +static __u16 convert_disposition(int disposition) +{ + __u16 ofun = 0; + + switch (disposition) { + case FILE_SUPERSEDE: + ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; + break; + case FILE_OPEN: + ofun = SMBOPEN_OAPPEND; + break; + case FILE_CREATE: + ofun = SMBOPEN_OCREATE; + break; + case FILE_OPEN_IF: + ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND; + break; + case FILE_OVERWRITE: + ofun = SMBOPEN_OTRUNC; + break; + case FILE_OVERWRITE_IF: + ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; + break; + default: + cifs_dbg(FYI, "unknown disposition %d\n", disposition); + ofun = SMBOPEN_OAPPEND; /* regular open */ + } + return ofun; +} + +static int +access_flags_to_smbopen_mode(const int access_flags) +{ + int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE); + + if (masked_flags == GENERIC_READ) + return SMBOPEN_READ; + else if (masked_flags == GENERIC_WRITE) + return SMBOPEN_WRITE; + + /* just go for read/write */ + return SMBOPEN_READWRITE; +} + +int +SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const int openDisposition, + const int access_flags, const int create_options, __u16 *netfid, + int *pOplock, FILE_ALL_INFO *pfile_info, + const struct nls_table *nls_codepage, int remap) +{ + int rc; + OPENX_REQ *pSMB = NULL; + OPENX_RSP *pSMBr = NULL; + int bytes_returned; + int name_len; + __u16 count; + +OldOpenRetry: + rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->AndXCommand = 0xFF; /* none */ + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + count = 1; /* account for one byte pad to word boundary */ + name_len = + cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), + fileName, PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + count = 0; /* no pad */ + name_len = copy_path_name(pSMB->fileName, fileName); + } + if (*pOplock & REQ_OPLOCK) + pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK); + else if (*pOplock & REQ_BATCHOPLOCK) + pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK); + + pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO); + pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags)); + pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ + /* set file as system file if special file such + as fifo and server expecting SFU style and + no Unix extensions */ + + if (create_options & CREATE_OPTION_SPECIAL) + pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); + else /* BB FIXME BB */ + pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); + + if (create_options & CREATE_OPTION_READONLY) + pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY); + + /* BB FIXME BB */ +/* pSMB->CreateOptions = cpu_to_le32(create_options & + CREATE_OPTIONS_MASK); */ + /* BB FIXME END BB */ + + pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); + pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition)); + count += name_len; + inc_rfc1001_len(pSMB, count); + + pSMB->ByteCount = cpu_to_le16(count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *)pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); + if (rc) { + cifs_dbg(FYI, "Error in Open = %d\n", rc); + } else { + /* BB verify if wct == 15 */ + +/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/ + + *netfid = pSMBr->Fid; /* cifs fid stays in le */ + /* Let caller know file was created so we can set the mode. */ + /* Do we care about the CreateAction in any other cases? */ + /* BB FIXME BB */ +/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) + *pOplock |= CIFS_CREATE_ACTION; */ + /* BB FIXME END */ + + if (pfile_info) { + pfile_info->CreationTime = 0; /* BB convert CreateTime*/ + pfile_info->LastAccessTime = 0; /* BB fixme */ + pfile_info->LastWriteTime = 0; /* BB fixme */ + pfile_info->ChangeTime = 0; /* BB fixme */ + pfile_info->Attributes = + cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); + /* the file_info buf is endian converted by caller */ + pfile_info->AllocationSize = + cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile)); + pfile_info->EndOfFile = pfile_info->AllocationSize; + pfile_info->NumberOfLinks = cpu_to_le32(1); + pfile_info->DeletePending = 0; + } + } + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto OldOpenRetry; + return rc; +} + +int +CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, + FILE_ALL_INFO *buf) +{ + int rc; + OPEN_REQ *req = NULL; + OPEN_RSP *rsp = NULL; + int bytes_returned; + int name_len; + __u16 count; + struct cifs_sb_info *cifs_sb = oparms->cifs_sb; + struct cifs_tcon *tcon = oparms->tcon; + int remap = cifs_remap(cifs_sb); + const struct nls_table *nls = cifs_sb->local_nls; + int create_options = oparms->create_options; + int desired_access = oparms->desired_access; + int disposition = oparms->disposition; + const char *path = oparms->path; + +openRetry: + rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req, + (void **)&rsp); + if (rc) + return rc; + + /* no commands go after this */ + req->AndXCommand = 0xFF; + + if (req->hdr.Flags2 & SMBFLG2_UNICODE) { + /* account for one byte pad to word boundary */ + count = 1; + name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1), + path, PATH_MAX, nls, remap); + /* trailing null */ + name_len++; + name_len *= 2; + req->NameLength = cpu_to_le16(name_len); + } else { + /* BB improve check for buffer overruns BB */ + /* no pad */ + count = 0; + name_len = copy_path_name(req->fileName, path); + req->NameLength = cpu_to_le16(name_len); + } + + if (*oplock & REQ_OPLOCK) + req->OpenFlags = cpu_to_le32(REQ_OPLOCK); + else if (*oplock & REQ_BATCHOPLOCK) + req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); + + req->DesiredAccess = cpu_to_le32(desired_access); + req->AllocationSize = 0; + + /* + * Set file as system file if special file such as fifo and server + * expecting SFU style and no Unix extensions. + */ + if (create_options & CREATE_OPTION_SPECIAL) + req->FileAttributes = cpu_to_le32(ATTR_SYSTEM); + else + req->FileAttributes = cpu_to_le32(ATTR_NORMAL); + + /* + * XP does not handle ATTR_POSIX_SEMANTICS but it helps speed up case + * sensitive checks for other servers such as Samba. + */ + if (tcon->ses->capabilities & CAP_UNIX) + req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); + + if (create_options & CREATE_OPTION_READONLY) + req->FileAttributes |= cpu_to_le32(ATTR_READONLY); + + req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); + req->CreateDisposition = cpu_to_le32(disposition); + req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); + + /* BB Expirement with various impersonation levels and verify */ + req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); + req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY; + + count += name_len; + inc_rfc1001_len(req, count); + + req->ByteCount = cpu_to_le16(count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, + (struct smb_hdr *)rsp, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); + if (rc) { + cifs_dbg(FYI, "Error in Open = %d\n", rc); + cifs_buf_release(req); + if (rc == -EAGAIN) + goto openRetry; + return rc; + } + + /* 1 byte no need to le_to_cpu */ + *oplock = rsp->OplockLevel; + /* cifs fid stays in le */ + oparms->fid->netfid = rsp->Fid; + oparms->fid->access = desired_access; + + /* Let caller know file was created so we can set the mode. */ + /* Do we care about the CreateAction in any other cases? */ + if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction) + *oplock |= CIFS_CREATE_ACTION; + + if (buf) { + /* copy from CreationTime to Attributes */ + memcpy((char *)buf, (char *)&rsp->CreationTime, 36); + /* the file_info buf is endian converted by caller */ + buf->AllocationSize = rsp->AllocationSize; + buf->EndOfFile = rsp->EndOfFile; + buf->NumberOfLinks = cpu_to_le32(1); + buf->DeletePending = 0; + } + + cifs_buf_release(req); + return rc; +} + +static void +cifs_readv_callback(struct mid_q_entry *mid) +{ + struct cifs_readdata *rdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 2, + .rq_pages = rdata->pages, + .rq_offset = rdata->page_offset, + .rq_npages = rdata->nr_pages, + .rq_pagesz = rdata->pagesz, + .rq_tailsz = rdata->tailsz }; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + + cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", + __func__, mid->mid, mid->mid_state, rdata->result, + rdata->bytes); + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + /* result already set, check signature */ + if (server->sign) { + int rc = 0; + + rc = cifs_verify_signature(&rqst, server, + mid->sequence_number); + if (rc) + cifs_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + rdata->result = -EAGAIN; + if (server->sign && rdata->got_bytes) + /* reset bytes number since we can not check a sign */ + rdata->got_bytes = 0; + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + default: + rdata->result = -EIO; + } + + queue_work(cifsiod_wq, &rdata->work); + release_mid(mid); + add_credits(server, &credits, 0); +} + +/* cifs_async_readv - send an async write, and set up mid to handle result */ +int +cifs_async_readv(struct cifs_readdata *rdata) +{ + int rc; + READ_REQ *smb = NULL; + int wct; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 2 }; + + cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", + __func__, rdata->offset, rdata->bytes); + + if (tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 12; + else { + wct = 10; /* old style read */ + if ((rdata->offset >> 32) > 0) { + /* can not handle this big offset for old */ + return -EIO; + } + } + + rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb); + if (rc) + return rc; + + smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid); + smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16)); + + smb->AndXCommand = 0xFF; /* none */ + smb->Fid = rdata->cfile->fid.netfid; + smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF); + if (wct == 12) + smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32); + smb->Remaining = 0; + smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF); + smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16); + if (wct == 12) + smb->ByteCount = 0; + else { + /* old style read */ + struct smb_com_readx_req *smbr = + (struct smb_com_readx_req *)smb; + smbr->ByteCount = 0; + } + + /* 4 for RFC1001 length + 1 for BCC */ + rdata->iov[0].iov_base = smb; + rdata->iov[0].iov_len = 4; + rdata->iov[1].iov_base = (char *)smb + 4; + rdata->iov[1].iov_len = get_rfc1002_length(smb); + + kref_get(&rdata->refcount); + rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, + cifs_readv_callback, NULL, rdata, 0, NULL); + + if (rc == 0) + cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); + else + kref_put(&rdata->refcount, cifs_readdata_release); + + cifs_small_buf_release(smb); + return rc; +} + +int +CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *pbuf_type) +{ + int rc = -EACCES; + READ_REQ *pSMB = NULL; + READ_RSP *pSMBr = NULL; + char *pReadData = NULL; + int wct; + int resp_buf_type = 0; + struct kvec iov[1]; + struct kvec rsp_iov; + __u32 pid = io_parms->pid; + __u16 netfid = io_parms->netfid; + __u64 offset = io_parms->offset; + struct cifs_tcon *tcon = io_parms->tcon; + unsigned int count = io_parms->length; + + cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid); + if (tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 12; + else { + wct = 10; /* old style read */ + if ((offset >> 32) > 0) { + /* can not handle this big offset for old */ + return -EIO; + } + } + + *nbytes = 0; + rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); + if (wct == 12) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + + pSMB->Remaining = 0; + pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); + pSMB->MaxCountHigh = cpu_to_le32(count >> 16); + if (wct == 12) + pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ + else { + /* old style read */ + struct smb_com_readx_req *pSMBW = + (struct smb_com_readx_req *)pSMB; + pSMBW->ByteCount = 0; + } + + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; + rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type, + CIFS_LOG_ERROR, &rsp_iov); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); + pSMBr = (READ_RSP *)rsp_iov.iov_base; + if (rc) { + cifs_dbg(VFS, "Send error in read = %d\n", rc); + } else { + int data_length = le16_to_cpu(pSMBr->DataLengthHigh); + data_length = data_length << 16; + data_length += le16_to_cpu(pSMBr->DataLength); + *nbytes = data_length; + + /*check that DataLength would not go beyond end of SMB */ + if ((data_length > CIFSMaxBufSize) + || (data_length > count)) { + cifs_dbg(FYI, "bad length %d for count %d\n", + data_length, count); + rc = -EIO; + *nbytes = 0; + } else { + pReadData = (char *) (&pSMBr->hdr.Protocol) + + le16_to_cpu(pSMBr->DataOffset); +/* if (rc = copy_to_user(buf, pReadData, data_length)) { + cifs_dbg(VFS, "Faulting on read rc = %d\n",rc); + rc = -EFAULT; + }*/ /* can not use copy_to_user when using page cache*/ + if (*buf) + memcpy(*buf, pReadData, data_length); + } + } + + if (*buf) { + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); + } else if (resp_buf_type != CIFS_NO_BUFFER) { + /* return buffer to caller to free */ + *buf = rsp_iov.iov_base; + if (resp_buf_type == CIFS_SMALL_BUFFER) + *pbuf_type = CIFS_SMALL_BUFFER; + else if (resp_buf_type == CIFS_LARGE_BUFFER) + *pbuf_type = CIFS_LARGE_BUFFER; + } /* else no valid buffer on return - leave as null */ + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + return rc; +} + + +int +CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, const char *buf) +{ + int rc = -EACCES; + WRITE_REQ *pSMB = NULL; + WRITE_RSP *pSMBr = NULL; + int bytes_returned, wct; + __u32 bytes_sent; + __u16 byte_count; + __u32 pid = io_parms->pid; + __u16 netfid = io_parms->netfid; + __u64 offset = io_parms->offset; + struct cifs_tcon *tcon = io_parms->tcon; + unsigned int count = io_parms->length; + + *nbytes = 0; + + /* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/ + if (tcon->ses == NULL) + return -ECONNABORTED; + + if (tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 14; + else { + wct = 12; + if ((offset >> 32) > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } + + rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); + if (wct == 14) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + + pSMB->Reserved = 0xFFFFFFFF; + pSMB->WriteMode = 0; + pSMB->Remaining = 0; + + /* Can increase buffer size if buffer is big enough in some cases ie we + can send more if LARGE_WRITE_X capability returned by the server and if + our buffer is big enough or if we convert to iovecs on socket writes + and eliminate the copy to the CIFS buffer */ + if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) { + bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); + } else { + bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) + & ~0xFF; + } + + if (bytes_sent > count) + bytes_sent = count; + pSMB->DataOffset = + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + if (buf) + memcpy(pSMB->Data, buf, bytes_sent); + else if (count != 0) { + /* No buffer */ + cifs_buf_release(pSMB); + return -EINVAL; + } /* else setting file size with write of zero bytes */ + if (wct == 14) + byte_count = bytes_sent + 1; /* pad */ + else /* wct == 12 */ + byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */ + + pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); + pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); + inc_rfc1001_len(pSMB, byte_count); + + if (wct == 14) + pSMB->ByteCount = cpu_to_le16(byte_count); + else { /* old style write has byte count 4 bytes earlier + so 4 bytes pad */ + struct smb_com_writex_req *pSMBW = + (struct smb_com_writex_req *)pSMB; + pSMBW->ByteCount = cpu_to_le16(byte_count); + } + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); + if (rc) { + cifs_dbg(FYI, "Send error in write = %d\n", rc); + } else { + *nbytes = le16_to_cpu(pSMBr->CountHigh); + *nbytes = (*nbytes) << 16; + *nbytes += le16_to_cpu(pSMBr->Count); + + /* + * Mask off high 16 bits when bytes written as returned by the + * server is greater than bytes requested by the client. Some + * OS/2 servers are known to set incorrect CountHigh values. + */ + if (*nbytes > count) + *nbytes &= 0xFFFF; + } + + cifs_buf_release(pSMB); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +/* + * Check the mid_state and signature on received buffer (if any), and queue the + * workqueue completion task. + */ +static void +cifs_writev_callback(struct mid_q_entry *mid) +{ + struct cifs_writedata *wdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + unsigned int written; + WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + wdata->result = cifs_check_receive(mid, tcon->ses->server, 0); + if (wdata->result != 0) + break; + + written = le16_to_cpu(smb->CountHigh); + written <<= 16; + written += le16_to_cpu(smb->Count); + /* + * Mask off high 16 bits when bytes written as returned + * by the server is greater than bytes requested by the + * client. OS/2 servers are known to set incorrect + * CountHigh values. + */ + if (written > wdata->bytes) + written &= 0xFFFF; + + if (written < wdata->bytes) + wdata->result = -ENOSPC; + else + wdata->bytes = written; + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + wdata->result = -EAGAIN; + break; + default: + wdata->result = -EIO; + break; + } + + queue_work(cifsiod_wq, &wdata->work); + release_mid(mid); + add_credits(tcon->ses->server, &credits, 0); +} + +/* cifs_async_writev - send an async write, and set up mid to handle result */ +int +cifs_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)) +{ + int rc = -EACCES; + WRITE_REQ *smb = NULL; + int wct; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct kvec iov[2]; + struct smb_rqst rqst = { }; + + if (tcon->ses->capabilities & CAP_LARGE_FILES) { + wct = 14; + } else { + wct = 12; + if (wdata->offset >> 32 > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } + + rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb); + if (rc) + goto async_writev_out; + + smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid); + smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16)); + + smb->AndXCommand = 0xFF; /* none */ + smb->Fid = wdata->cfile->fid.netfid; + smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF); + if (wct == 14) + smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32); + smb->Reserved = 0xFFFFFFFF; + smb->WriteMode = 0; + smb->Remaining = 0; + + smb->DataOffset = + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + + /* 4 for RFC1001 length + 1 for BCC */ + iov[0].iov_len = 4; + iov[0].iov_base = smb; + iov[1].iov_len = get_rfc1002_length(smb) + 1; + iov[1].iov_base = (char *)smb + 4; + + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + rqst.rq_pages = wdata->pages; + rqst.rq_offset = wdata->page_offset; + rqst.rq_npages = wdata->nr_pages; + rqst.rq_pagesz = wdata->pagesz; + rqst.rq_tailsz = wdata->tailsz; + + cifs_dbg(FYI, "async write at %llu %u bytes\n", + wdata->offset, wdata->bytes); + + smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF); + smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16); + + if (wct == 14) { + inc_rfc1001_len(&smb->hdr, wdata->bytes + 1); + put_bcc(wdata->bytes + 1, &smb->hdr); + } else { + /* wct == 12 */ + struct smb_com_writex_req *smbw = + (struct smb_com_writex_req *)smb; + inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); + put_bcc(wdata->bytes + 5, &smbw->hdr); + iov[1].iov_len += 4; /* pad bigger by four bytes */ + } + + kref_get(&wdata->refcount); + rc = cifs_call_async(tcon->ses->server, &rqst, NULL, + cifs_writev_callback, NULL, wdata, 0, NULL); + + if (rc == 0) + cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); + else + kref_put(&wdata->refcount, release); + +async_writev_out: + cifs_small_buf_release(smb); + return rc; +} + +int +CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec) +{ + int rc; + WRITE_REQ *pSMB = NULL; + int wct; + int smb_hdr_len; + int resp_buf_type = 0; + __u32 pid = io_parms->pid; + __u16 netfid = io_parms->netfid; + __u64 offset = io_parms->offset; + struct cifs_tcon *tcon = io_parms->tcon; + unsigned int count = io_parms->length; + struct kvec rsp_iov; + + *nbytes = 0; + + cifs_dbg(FYI, "write2 at %lld %d bytes\n", (long long)offset, count); + + if (tcon->ses->capabilities & CAP_LARGE_FILES) { + wct = 14; + } else { + wct = 12; + if ((offset >> 32) > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } + rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); + if (wct == 14) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + pSMB->Reserved = 0xFFFFFFFF; + pSMB->WriteMode = 0; + pSMB->Remaining = 0; + + pSMB->DataOffset = + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + + pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); + pSMB->DataLengthHigh = cpu_to_le16(count >> 16); + /* header + 1 byte pad */ + smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1; + if (wct == 14) + inc_rfc1001_len(pSMB, count + 1); + else /* wct == 12 */ + inc_rfc1001_len(pSMB, count + 5); /* smb data starts later */ + if (wct == 14) + pSMB->ByteCount = cpu_to_le16(count + 1); + else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ { + struct smb_com_writex_req *pSMBW = + (struct smb_com_writex_req *)pSMB; + pSMBW->ByteCount = cpu_to_le16(count + 5); + } + iov[0].iov_base = pSMB; + if (wct == 14) + iov[0].iov_len = smb_hdr_len + 4; + else /* wct == 12 pad bigger by four bytes */ + iov[0].iov_len = smb_hdr_len + 8; + + rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0, + &rsp_iov); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); + if (rc) { + cifs_dbg(FYI, "Send error Write2 = %d\n", rc); + } else if (resp_buf_type == 0) { + /* presumably this can not happen, but best to be safe */ + rc = -EIO; + } else { + WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base; + *nbytes = le16_to_cpu(pSMBr->CountHigh); + *nbytes = (*nbytes) << 16; + *nbytes += le16_to_cpu(pSMBr->Count); + + /* + * Mask off high 16 bits when bytes written as returned by the + * server is greater than bytes requested by the client. OS/2 + * servers are known to set incorrect CountHigh values. + */ + if (*nbytes > count) + *nbytes &= 0xFFFF; + } + + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 netfid, const __u8 lock_type, const __u32 num_unlock, + const __u32 num_lock, LOCKING_ANDX_RANGE *buf) +{ + int rc = 0; + LOCK_REQ *pSMB = NULL; + struct kvec iov[2]; + struct kvec rsp_iov; + int resp_buf_type; + __u16 count; + + cifs_dbg(FYI, "cifs_lockv num lock %d num unlock %d\n", + num_lock, num_unlock); + + rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->Timeout = 0; + pSMB->NumberOfLocks = cpu_to_le16(num_lock); + pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock); + pSMB->LockType = lock_type; + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; /* netfid stays le */ + + count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 - + (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); + iov[1].iov_base = (char *)buf; + iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); + + cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); + rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, + CIFS_NO_RSP_BUF, &rsp_iov); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc); + + return rc; +} + +int +CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, const __u64 len, + const __u64 offset, const __u32 numUnlock, + const __u32 numLock, const __u8 lockType, + const bool waitFlag, const __u8 oplock_level) +{ + int rc = 0; + LOCK_REQ *pSMB = NULL; +/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */ + int bytes_returned; + int flags = 0; + __u16 count; + + cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n", + (int)waitFlag, numLock); + rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); + + if (rc) + return rc; + + if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { + /* no response expected */ + flags = CIFS_NO_SRV_RSP | CIFS_NON_BLOCKING | CIFS_OBREAK_OP; + pSMB->Timeout = 0; + } else if (waitFlag) { + flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ + pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */ + } else { + pSMB->Timeout = 0; + } + + pSMB->NumberOfLocks = cpu_to_le16(numLock); + pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock); + pSMB->LockType = lockType; + pSMB->OplockLevel = oplock_level; + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = smb_file_id; /* netfid stays le */ + + if ((numLock != 0) || (numUnlock != 0)) { + pSMB->Locks[0].Pid = cpu_to_le16(netpid); + /* BB where to store pid high? */ + pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len); + pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32)); + pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset); + pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32)); + count = sizeof(LOCKING_ANDX_RANGE); + } else { + /* oplock break */ + count = 0; + } + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + if (waitFlag) + rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMB, &bytes_returned); + else + rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); + if (rc) + cifs_dbg(FYI, "Send error in Lock = %d\n", rc); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + return rc; +} + +int +CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, + const loff_t start_offset, const __u64 len, + struct file_lock *pLockData, const __u16 lock_type, + const bool waitFlag) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; + struct cifs_posix_lock *parm_data; + int rc = 0; + int timeout = 0; + int bytes_returned = 0; + int resp_buf_type = 0; + __u16 params, param_offset, offset, byte_count, count; + struct kvec iov[1]; + struct kvec rsp_iov; + + cifs_dbg(FYI, "Posix Lock\n"); + + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + count = sizeof(struct cifs_posix_lock); + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + if (pLockData) + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + else + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + parm_data = (struct cifs_posix_lock *) + (((char *)pSMB) + offset + 4); + + parm_data->lock_type = cpu_to_le16(lock_type); + if (waitFlag) { + timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ + parm_data->lock_flags = cpu_to_le16(1); + pSMB->Timeout = cpu_to_le32(-1); + } else + pSMB->Timeout = 0; + + parm_data->pid = cpu_to_le32(netpid); + parm_data->start = cpu_to_le64(start_offset); + parm_data->length = cpu_to_le64(len); /* normalize negative numbers */ + + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->Fid = smb_file_id; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + if (waitFlag) { + rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned); + } else { + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; + rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, + &resp_buf_type, timeout, &rsp_iov); + pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base; + } + cifs_small_buf_release(pSMB); + + if (rc) { + cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc); + } else if (pLockData) { + /* lock structure can be returned on get */ + __u16 data_offset; + __u16 data_count; + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) { + rc = -EIO; /* bad smb */ + goto plk_err_exit; + } + data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + data_count = le16_to_cpu(pSMBr->t2.DataCount); + if (data_count < sizeof(struct cifs_posix_lock)) { + rc = -EIO; + goto plk_err_exit; + } + parm_data = (struct cifs_posix_lock *) + ((char *)&pSMBr->hdr.Protocol + data_offset); + if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) + pLockData->fl_type = F_UNLCK; + else { + if (parm_data->lock_type == + cpu_to_le16(CIFS_RDLCK)) + pLockData->fl_type = F_RDLCK; + else if (parm_data->lock_type == + cpu_to_le16(CIFS_WRLCK)) + pLockData->fl_type = F_WRLCK; + + pLockData->fl_start = le64_to_cpu(parm_data->start); + pLockData->fl_end = pLockData->fl_start + + (le64_to_cpu(parm_data->length) ? + le64_to_cpu(parm_data->length) - 1 : 0); + pLockData->fl_pid = -le32_to_cpu(parm_data->pid); + } + } + +plk_err_exit: + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + + +int +CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) +{ + int rc = 0; + CLOSE_REQ *pSMB = NULL; + cifs_dbg(FYI, "In CIFSSMBClose\n"); + +/* do not retry on dead session on close */ + rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); + if (rc == -EAGAIN) + return 0; + if (rc) + return rc; + + pSMB->FileID = (__u16) smb_file_id; + pSMB->LastWriteTime = 0xFFFFFFFF; + pSMB->ByteCount = 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_closes); + if (rc) { + if (rc != -EINTR) { + /* EINTR is expected when user ctl-c to kill app */ + cifs_dbg(VFS, "Send error in Close = %d\n", rc); + } + } + + /* Since session is dead, file will be closed on server already */ + if (rc == -EAGAIN) + rc = 0; + + return rc; +} + +int +CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) +{ + int rc = 0; + FLUSH_REQ *pSMB = NULL; + cifs_dbg(FYI, "In CIFSSMBFlush\n"); + + rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->FileID = (__u16) smb_file_id; + pSMB->ByteCount = 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes); + if (rc) + cifs_dbg(VFS, "Send error in Flush = %d\n", rc); + + return rc; +} + +int +CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + int rc = 0; + RENAME_REQ *pSMB = NULL; + RENAME_RSP *pSMBr = NULL; + int bytes_returned; + int name_len, name_len2; + __u16 count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSSMBRename\n"); +renameRetry: + rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->BufferFormat = 0x04; + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | + ATTR_DIRECTORY); + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, + from_name, PATH_MAX, + cifs_sb->local_nls, remap); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->OldFileName[name_len] = 0x04; /* pad */ + /* protocol requires ASCII signature byte on Unicode string */ + pSMB->OldFileName[name_len + 1] = 0x00; + name_len2 = + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + to_name, PATH_MAX, cifs_sb->local_nls, + remap); + name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; + name_len2 *= 2; /* convert to bytes */ + } else { + name_len = copy_path_name(pSMB->OldFileName, from_name); + name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); + pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ + name_len2++; /* signature byte */ + } + + count = 1 /* 1st signature byte */ + name_len + name_len2; + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_renames); + if (rc) + cifs_dbg(FYI, "Send error in rename = %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto renameRetry; + + return rc; +} + +int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon, + int netfid, const char *target_name, + const struct nls_table *nls_codepage, int remap) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; + struct set_file_rename *rename_info; + char *data_offset; + char dummy_string[30]; + int rc = 0; + int bytes_returned = 0; + int len_of_str; + __u16 params, param_offset, offset, count, byte_count; + + cifs_dbg(FYI, "Rename to File by handle\n"); + rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)(pSMB) + offset + 4; + rename_info = (struct set_file_rename *) data_offset; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + /* construct random name ".cifs_tmp" */ + rename_info->overwrite = cpu_to_le32(1); + rename_info->root_fid = 0; + /* unicode only call */ + if (target_name == NULL) { + sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); + len_of_str = + cifsConvertToUTF16((__le16 *)rename_info->target_name, + dummy_string, 24, nls_codepage, remap); + } else { + len_of_str = + cifsConvertToUTF16((__le16 *)rename_info->target_name, + target_name, PATH_MAX, nls_codepage, + remap); + } + rename_info->target_name_len = cpu_to_le32(2 * len_of_str); + count = sizeof(struct set_file_rename) + (2 * len_of_str); + byte_count += count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->Fid = netfid; + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&pTcon->stats.cifs_stats.num_t2renames); + if (rc) + cifs_dbg(FYI, "Send error in Rename (by file handle) = %d\n", + rc); + + cifs_buf_release(pSMB); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int +CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const __u16 target_tid, const char *toName, + const int flags, const struct nls_table *nls_codepage, int remap) +{ + int rc = 0; + COPY_REQ *pSMB = NULL; + COPY_RSP *pSMBr = NULL; + int bytes_returned; + int name_len, name_len2; + __u16 count; + + cifs_dbg(FYI, "In CIFSSMBCopy\n"); +copyRetry: + rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->BufferFormat = 0x04; + pSMB->Tid2 = target_tid; + + pSMB->Flags = cpu_to_le16(flags & COPY_TREE); + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, + fromName, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->OldFileName[name_len] = 0x04; /* pad */ + /* protocol requires ASCII signature byte on Unicode string */ + pSMB->OldFileName[name_len + 1] = 0x00; + name_len2 = + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + toName, PATH_MAX, nls_codepage, remap); + name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; + name_len2 *= 2; /* convert to bytes */ + } else { + name_len = copy_path_name(pSMB->OldFileName, fromName); + pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ + name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, toName); + name_len2++; /* signature byte */ + } + + count = 1 /* 1st signature byte */ + name_len + name_len2; + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n", + rc, le16_to_cpu(pSMBr->CopyCount)); + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto copyRetry; + + return rc; +} + +int +CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + char *data_offset; + int name_len; + int name_len_target; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cifs_dbg(FYI, "In Symlink Unix style\n"); +createSymLinkRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fromName, + /* find define for this maxpathcomponent */ + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + + } else { + name_len = copy_path_name(pSMB->FileName, fromName); + } + params = 6 + name_len; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)pSMB + offset + 4; + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len_target = + cifsConvertToUTF16((__le16 *) data_offset, toName, + /* find define for this maxpathcomponent */ + PATH_MAX, nls_codepage, remap); + name_len_target++; /* trailing null */ + name_len_target *= 2; + } else { + name_len_target = copy_path_name(data_offset, toName); + } + + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max on data count below from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + name_len_target; + pSMB->DataCount = cpu_to_le16(name_len_target); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks); + if (rc) + cifs_dbg(FYI, "Send error in SetPathInfo create symlink = %d\n", + rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto createSymLinkRetry; + + return rc; +} + +int +CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + char *data_offset; + int name_len; + int name_len_target; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cifs_dbg(FYI, "In Create Hard link Unix style\n"); +createHardLinkRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + + } else { + name_len = copy_path_name(pSMB->FileName, toName); + } + params = 6 + name_len; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)pSMB + offset + 4; + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len_target = + cifsConvertToUTF16((__le16 *) data_offset, fromName, + PATH_MAX, nls_codepage, remap); + name_len_target++; /* trailing null */ + name_len_target *= 2; + } else { + name_len_target = copy_path_name(data_offset, fromName); + } + + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max on data count below from sess*/ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + name_len_target; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->DataCount = cpu_to_le16(name_len_target); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); + if (rc) + cifs_dbg(FYI, "Send error in SetPathInfo (hard link) = %d\n", + rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto createHardLinkRetry; + + return rc; +} + +int +CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + int rc = 0; + NT_RENAME_REQ *pSMB = NULL; + RENAME_RSP *pSMBr = NULL; + int bytes_returned; + int name_len, name_len2; + __u16 count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSCreateHardLink\n"); +winCreateHardLinkRetry: + + rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | + ATTR_DIRECTORY); + pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK); + pSMB->ClusterCount = 0; + + pSMB->BufferFormat = 0x04; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name, + PATH_MAX, cifs_sb->local_nls, remap); + name_len++; /* trailing null */ + name_len *= 2; + + /* protocol specifies ASCII buffer format (0x04) for unicode */ + pSMB->OldFileName[name_len] = 0x04; + pSMB->OldFileName[name_len + 1] = 0x00; /* pad */ + name_len2 = + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + to_name, PATH_MAX, cifs_sb->local_nls, + remap); + name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; + name_len2 *= 2; /* convert to bytes */ + } else { + name_len = copy_path_name(pSMB->OldFileName, from_name); + pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ + name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); + name_len2++; /* signature byte */ + } + + count = 1 /* string type byte */ + name_len + name_len2; + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); + if (rc) + cifs_dbg(FYI, "Send error in hard link (NT rename) = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto winCreateHardLinkRetry; + + return rc; +} + +int +CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, char **symlinkinfo, + const struct nls_table *nls_codepage, int remap) +{ +/* SMB_QUERY_FILE_UNIX_LINK */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + char *data_start; + + cifs_dbg(FYI, "In QPathSymLinkInfo (Unix) for path %s\n", searchName); + +querySymLinkRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + searchName, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc); + } else { + /* decode response */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + rc = -EIO; + else { + bool is_unicode; + u16 count = le16_to_cpu(pSMBr->t2.DataCount); + + data_start = ((char *) &pSMBr->hdr.Protocol) + + le16_to_cpu(pSMBr->t2.DataOffset); + + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; + + /* BB FIXME investigate remapping reserved chars here */ + *symlinkinfo = cifs_strndup_from_utf16(data_start, + count, is_unicode, nls_codepage); + if (!*symlinkinfo) + rc = -ENOMEM; + } + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto querySymLinkRetry; + return rc; +} + +/* + * Recent Windows versions now create symlinks more frequently + * and they use the "reparse point" mechanism below. We can of course + * do symlinks nicely to Samba and other servers which support the + * CIFS Unix Extensions and we can also do SFU symlinks and "client only" + * "MF" symlinks optionally, but for recent Windows we really need to + * reenable the code below and fix the cifs_symlink callers to handle this. + * In the interim this code has been moved to its own config option so + * it is not compiled in by default until callers fixed up and more tested. + */ +int +CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, char **symlinkinfo, + const struct nls_table *nls_codepage) +{ + int rc = 0; + int bytes_returned; + struct smb_com_transaction_ioctl_req *pSMB; + struct smb_com_transaction_ioctl_rsp *pSMBr; + bool is_unicode; + unsigned int sub_len; + char *sub_start; + struct reparse_symlink_data *reparse_buf; + struct reparse_posix_data *posix_buf; + __u32 data_offset, data_count; + char *end_of_smb; + + cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); + rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->TotalParameterCount = 0 ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le32(2); + /* BB find exact data count max from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); + pSMB->MaxSetupCount = 4; + pSMB->Reserved = 0; + pSMB->ParameterOffset = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 4; + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT); + pSMB->IsFsctl = 1; /* FSCTL */ + pSMB->IsRootFlag = 0; + pSMB->Fid = fid; /* file handle always le */ + pSMB->ByteCount = 0; + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); + goto qreparse_out; + } + + data_offset = le32_to_cpu(pSMBr->DataOffset); + data_count = le32_to_cpu(pSMBr->DataCount); + if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { + /* BB also check enough total bytes returned */ + rc = -EIO; /* bad smb */ + goto qreparse_out; + } + if (!data_count || (data_count > 2048)) { + rc = -EIO; + cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); + goto qreparse_out; + } + end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; + reparse_buf = (struct reparse_symlink_data *) + ((char *)&pSMBr->hdr.Protocol + data_offset); + if ((char *)reparse_buf >= end_of_smb) { + rc = -EIO; + goto qreparse_out; + } + if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { + cifs_dbg(FYI, "NFS style reparse tag\n"); + posix_buf = (struct reparse_posix_data *)reparse_buf; + + if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { + cifs_dbg(FYI, "unsupported file type 0x%llx\n", + le64_to_cpu(posix_buf->InodeType)); + rc = -EOPNOTSUPP; + goto qreparse_out; + } + is_unicode = true; + sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); + if (posix_buf->PathBuffer + sub_len > end_of_smb) { + cifs_dbg(FYI, "reparse buf beyond SMB\n"); + rc = -EIO; + goto qreparse_out; + } + *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, + sub_len, is_unicode, nls_codepage); + goto qreparse_out; + } else if (reparse_buf->ReparseTag != + cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { + rc = -EOPNOTSUPP; + goto qreparse_out; + } + + /* Reparse tag is NTFS symlink */ + sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + + reparse_buf->PathBuffer; + sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); + if (sub_start + sub_len > end_of_smb) { + cifs_dbg(FYI, "reparse buf beyond SMB\n"); + rc = -EIO; + goto qreparse_out; + } + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; + + /* BB FIXME investigate remapping reserved chars here */ + *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, + nls_codepage); + if (!*symlinkinfo) + rc = -ENOMEM; +qreparse_out: + cifs_buf_release(pSMB); + + /* + * Note: On -EAGAIN error only caller can retry on handle based calls + * since file handle passed in no longer valid. + */ + return rc; +} + +int +CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid) +{ + int rc = 0; + int bytes_returned; + struct smb_com_transaction_compr_ioctl_req *pSMB; + struct smb_com_transaction_ioctl_rsp *pSMBr; + + cifs_dbg(FYI, "Set compression for %u\n", fid); + rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + + pSMB->TotalParameterCount = 0; + pSMB->TotalDataCount = cpu_to_le32(2); + pSMB->MaxParameterCount = 0; + pSMB->MaxDataCount = 0; + pSMB->MaxSetupCount = 4; + pSMB->Reserved = 0; + pSMB->ParameterOffset = 0; + pSMB->DataCount = cpu_to_le32(2); + pSMB->DataOffset = + cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, + compression_state) - 4); /* 84 */ + pSMB->SetupCount = 4; + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); + pSMB->ParameterCount = 0; + pSMB->FunctionCode = cpu_to_le32(FSCTL_SET_COMPRESSION); + pSMB->IsFsctl = 1; /* FSCTL */ + pSMB->IsRootFlag = 0; + pSMB->Fid = fid; /* file handle always le */ + /* 3 byte pad, followed by 2 byte compress state */ + pSMB->ByteCount = cpu_to_le16(5); + inc_rfc1001_len(pSMB, 5); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); + + cifs_buf_release(pSMB); + + /* + * Note: On -EAGAIN error only caller can retry on handle based calls + * since file handle passed in no longer valid. + */ + return rc; +} + + +#ifdef CONFIG_CIFS_POSIX + +/*Convert an Access Control Entry from wire format to local POSIX xattr format*/ +static void cifs_convert_ace(struct posix_acl_xattr_entry *ace, + struct cifs_posix_ace *cifs_ace) +{ + /* u8 cifs fields do not need le conversion */ + ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); + ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag); + ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid)); +/* + cifs_dbg(FYI, "perm %d tag %d id %d\n", + ace->e_perm, ace->e_tag, ace->e_id); +*/ + + return; +} + +/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ +static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, + const int acl_type, const int size_of_data_area) +{ + int size = 0; + int i; + __u16 count; + struct cifs_posix_ace *pACE; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; + struct posix_acl_xattr_header *local_acl = (void *)trgt; + + if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) + return -EOPNOTSUPP; + + if (acl_type == ACL_TYPE_ACCESS) { + count = le16_to_cpu(cifs_acl->access_entry_count); + pACE = &cifs_acl->ace_array[0]; + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) { + cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n", + size_of_data_area, size); + return -EINVAL; + } + } else if (acl_type == ACL_TYPE_DEFAULT) { + count = le16_to_cpu(cifs_acl->access_entry_count); + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; +/* skip past access ACEs to get to default ACEs */ + pACE = &cifs_acl->ace_array[count]; + count = le16_to_cpu(cifs_acl->default_entry_count); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) + return -EINVAL; + } else { + /* illegal type */ + return -EINVAL; + } + + size = posix_acl_xattr_size(count); + if ((buflen == 0) || (local_acl == NULL)) { + /* used to query ACL EA size */ + } else if (size > buflen) { + return -ERANGE; + } else /* buffer big enough */ { + struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); + + local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); + for (i = 0; i < count ; i++) { + cifs_convert_ace(&ace[i], pACE); + pACE++; + } + } + return size; +} + +static void convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, + const struct posix_acl_xattr_entry *local_ace) +{ + cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); + cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); + /* BB is there a better way to handle the large uid? */ + if (local_ace->e_id == cpu_to_le32(-1)) { + /* Probably no need to le convert -1 on any arch but can not hurt */ + cifs_ace->cifs_uid = cpu_to_le64(-1); + } else + cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); +/* + cifs_dbg(FYI, "perm %d tag %d id %d\n", + ace->e_perm, ace->e_tag, ace->e_id); +*/ +} + +/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ +static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, + const int buflen, const int acl_type) +{ + __u16 rc = 0; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; + struct posix_acl_xattr_header *local_acl = (void *)pACL; + struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); + int count; + int i; + + if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) + return 0; + + count = posix_acl_xattr_count((size_t)buflen); + cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n", + count, buflen, le32_to_cpu(local_acl->a_version)); + if (le32_to_cpu(local_acl->a_version) != 2) { + cifs_dbg(FYI, "unknown POSIX ACL version %d\n", + le32_to_cpu(local_acl->a_version)); + return 0; + } + cifs_acl->version = cpu_to_le16(1); + if (acl_type == ACL_TYPE_ACCESS) { + cifs_acl->access_entry_count = cpu_to_le16(count); + cifs_acl->default_entry_count = cpu_to_le16(0xFFFF); + } else if (acl_type == ACL_TYPE_DEFAULT) { + cifs_acl->default_entry_count = cpu_to_le16(count); + cifs_acl->access_entry_count = cpu_to_le16(0xFFFF); + } else { + cifs_dbg(FYI, "unknown ACL type %d\n", acl_type); + return 0; + } + for (i = 0; i < count; i++) + convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]); + if (rc == 0) { + rc = (__u16)(count * sizeof(struct cifs_posix_ace)); + rc += sizeof(struct cifs_posix_acl); + /* BB add check to make sure ACL does not overflow SMB */ + } + return rc; +} + +int +CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + char *acl_inf, const int buflen, const int acl_type, + const struct nls_table *nls_codepage, int remap) +{ +/* SMB_QUERY_POSIX_ACL */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + + cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName); + +queryAclRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + searchName, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->FileName[name_len] = 0; + pSMB->FileName[name_len+1] = 0; + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max data count below from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_qpi_req, + InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); + if (rc) { + cifs_dbg(FYI, "Send error in Query POSIX ACL = %d\n", rc); + } else { + /* decode response */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + rc = cifs_copy_posix_acl(acl_inf, + (char *)&pSMBr->hdr.Protocol+data_offset, + buflen, acl_type, count); + } + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto queryAclRetry; + return rc; +} + +int +CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *fileName, + const char *local_acl, const int buflen, + const int acl_type, + const struct nls_table *nls_codepage, int remap) +{ + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; + char *parm_data; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count, data_count, param_offset, offset; + + cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName); +setAclRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + params = 6 + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB size from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + parm_data = ((char *) &pSMB->hdr.Protocol) + offset; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + + /* convert to on the wire format for POSIX ACL */ + data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type); + + if (data_count == 0) { + rc = -EOPNOTSUPP; + goto setACLerrorExit; + } + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL); + byte_count = 3 /* pad */ + params + data_count; + pSMB->DataCount = cpu_to_le16(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc); + +setACLerrorExit: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto setAclRetry; + return rc; +} + +int +CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, + const int netfid, __u64 *pExtAttrBits, __u64 *pMask) +{ + int rc = 0; + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int bytes_returned; + __u16 params, byte_count; + + cifs_dbg(FYI, "In GetExtAttr\n"); + if (tcon == NULL) + return -ENODEV; + +GetExtAttrRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(4000); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); + pSMB->Pad = 0; + pSMB->Fid = netfid; + inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "error %d in GetExtAttr\n", rc); + } else { + /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + /* If rc should we check for EOPNOSUPP and + disable the srvino flag? or in caller? */ + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + struct file_chattr_info *pfinfo; + + if (count != 16) { + cifs_dbg(FYI, "Invalid size ret in GetExtAttr\n"); + rc = -EIO; + goto GetExtAttrOut; + } + pfinfo = (struct file_chattr_info *) + (data_offset + (char *) &pSMBr->hdr.Protocol); + *pExtAttrBits = le64_to_cpu(pfinfo->mode); + *pMask = le64_to_cpu(pfinfo->mask); + } + } +GetExtAttrOut: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto GetExtAttrRetry; + return rc; +} + +#endif /* CONFIG_POSIX */ + +/* + * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that + * all NT TRANSACTS that we init here have total parm and data under about 400 + * bytes (to fit in small cifs buffer size), which is the case so far, it + * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of + * returned setup area) and MaxParameterCount (returned parms size) must be set + * by caller + */ +static int +smb_init_nttransact(const __u16 sub_command, const int setup_count, + const int parm_len, struct cifs_tcon *tcon, + void **ret_buf) +{ + int rc; + __u32 temp_offset; + struct smb_com_ntransact_req *pSMB; + + rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, + (void **)&pSMB); + if (rc) + return rc; + *ret_buf = (void *)pSMB; + pSMB->Reserved = 0; + pSMB->TotalParameterCount = cpu_to_le32(parm_len); + pSMB->TotalDataCount = 0; + pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->DataCount = pSMB->TotalDataCount; + temp_offset = offsetof(struct smb_com_ntransact_req, Parms) + + (setup_count * 2) - 4 /* for rfc1001 length itself */; + pSMB->ParameterOffset = cpu_to_le32(temp_offset); + pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len); + pSMB->SetupCount = setup_count; /* no need to le convert byte fields */ + pSMB->SubCommand = cpu_to_le16(sub_command); + return 0; +} + +static int +validate_ntransact(char *buf, char **ppparm, char **ppdata, + __u32 *pparmlen, __u32 *pdatalen) +{ + char *end_of_smb; + __u32 data_count, data_offset, parm_count, parm_offset; + struct smb_com_ntransact_rsp *pSMBr; + u16 bcc; + + *pdatalen = 0; + *pparmlen = 0; + + if (buf == NULL) + return -EINVAL; + + pSMBr = (struct smb_com_ntransact_rsp *)buf; + + bcc = get_bcc(&pSMBr->hdr); + end_of_smb = 2 /* sizeof byte count */ + bcc + + (char *)&pSMBr->ByteCount; + + data_offset = le32_to_cpu(pSMBr->DataOffset); + data_count = le32_to_cpu(pSMBr->DataCount); + parm_offset = le32_to_cpu(pSMBr->ParameterOffset); + parm_count = le32_to_cpu(pSMBr->ParameterCount); + + *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; + *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; + + /* should we also check that parm and data areas do not overlap? */ + if (*ppparm > end_of_smb) { + cifs_dbg(FYI, "parms start after end of smb\n"); + return -EINVAL; + } else if (parm_count + *ppparm > end_of_smb) { + cifs_dbg(FYI, "parm end after end of smb\n"); + return -EINVAL; + } else if (*ppdata > end_of_smb) { + cifs_dbg(FYI, "data starts after end of smb\n"); + return -EINVAL; + } else if (data_count + *ppdata > end_of_smb) { + cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n", + *ppdata, data_count, (data_count + *ppdata), + end_of_smb, pSMBr); + return -EINVAL; + } else if (parm_count + data_count > bcc) { + cifs_dbg(FYI, "parm count and data count larger than SMB\n"); + return -EINVAL; + } + *pdatalen = data_count; + *pparmlen = parm_count; + return 0; +} + +/* Get Security Descriptor (by handle) from remote server for a file or dir */ +int +CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, + struct cifs_ntsd **acl_inf, __u32 *pbuflen) +{ + int rc = 0; + int buf_type = 0; + QUERY_SEC_DESC_REQ *pSMB; + struct kvec iov[1]; + struct kvec rsp_iov; + + cifs_dbg(FYI, "GetCifsACL\n"); + + *pbuflen = 0; + *acl_inf = NULL; + + rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, + 8 /* parm len */, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->MaxParameterCount = cpu_to_le32(4); + /* BB TEST with big acls that might need to be e.g. larger than 16K */ + pSMB->MaxSetupCount = 0; + pSMB->Fid = fid; /* file handle always le */ + pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP | + CIFS_ACL_DACL); + pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */ + inc_rfc1001_len(pSMB, 11); + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; + + rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, + 0, &rsp_iov); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); + if (rc) { + cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc); + } else { /* decode response */ + __le32 *parm; + __u32 parm_len; + __u32 acl_len; + struct smb_com_ntransact_rsp *pSMBr; + char *pdata; + +/* validate_nttransact */ + rc = validate_ntransact(rsp_iov.iov_base, (char **)&parm, + &pdata, &parm_len, pbuflen); + if (rc) + goto qsec_out; + pSMBr = (struct smb_com_ntransact_rsp *)rsp_iov.iov_base; + + cifs_dbg(FYI, "smb %p parm %p data %p\n", + pSMBr, parm, *acl_inf); + + if (le32_to_cpu(pSMBr->ParameterCount) != 4) { + rc = -EIO; /* bad smb */ + *pbuflen = 0; + goto qsec_out; + } + +/* BB check that data area is minimum length and as big as acl_len */ + + acl_len = le32_to_cpu(*parm); + if (acl_len != *pbuflen) { + cifs_dbg(VFS, "acl length %d does not match %d\n", + acl_len, *pbuflen); + if (*pbuflen > acl_len) + *pbuflen = acl_len; + } + + /* check if buffer is big enough for the acl + header followed by the smallest SID */ + if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) || + (*pbuflen >= 64 * 1024)) { + cifs_dbg(VFS, "bad acl length %d\n", *pbuflen); + rc = -EINVAL; + *pbuflen = 0; + } else { + *acl_inf = kmemdup(pdata, *pbuflen, GFP_KERNEL); + if (*acl_inf == NULL) { + *pbuflen = 0; + rc = -ENOMEM; + } + } + } +qsec_out: + free_rsp_buf(buf_type, rsp_iov.iov_base); + return rc; +} + +int +CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, + struct cifs_ntsd *pntsd, __u32 acllen, int aclflag) +{ + __u16 byte_count, param_count, data_count, param_offset, data_offset; + int rc = 0; + int bytes_returned = 0; + SET_SEC_DESC_REQ *pSMB = NULL; + void *pSMBr; + +setCifsAclRetry: + rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr); + if (rc) + return rc; + + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + + param_count = 8; + param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4; + data_count = acllen; + data_offset = param_offset + param_count; + byte_count = 3 /* pad */ + param_count; + + pSMB->DataCount = cpu_to_le32(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->MaxParameterCount = cpu_to_le32(4); + pSMB->MaxDataCount = cpu_to_le32(16384); + pSMB->ParameterCount = cpu_to_le32(param_count); + pSMB->ParameterOffset = cpu_to_le32(param_offset); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->DataOffset = cpu_to_le32(data_offset); + pSMB->SetupCount = 0; + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC); + pSMB->ByteCount = cpu_to_le16(byte_count+data_count); + + pSMB->Fid = fid; /* file handle always le */ + pSMB->Reserved2 = 0; + pSMB->AclFlags = cpu_to_le32(aclflag); + + if (pntsd && acllen) { + memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) + + data_offset, pntsd, acllen); + inc_rfc1001_len(pSMB, byte_count + data_count); + } else + inc_rfc1001_len(pSMB, byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + + cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n", + bytes_returned, rc); + if (rc) + cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc); + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto setCifsAclRetry; + + return (rc); +} + + +/* Legacy Query Path Information call for lookup to old servers such + as Win9x/WinME */ +int +SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + const struct nls_table *nls_codepage, int remap) +{ + QUERY_INFORMATION_REQ *pSMB; + QUERY_INFORMATION_RSP *pSMBr; + int rc = 0; + int bytes_returned; + int name_len; + + cifs_dbg(FYI, "In SMBQPath path %s\n", search_name); +QInfRetry: + rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + search_name, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, search_name); + } + pSMB->BufferFormat = 0x04; + name_len++; /* account for buffer type byte */ + inc_rfc1001_len(pSMB, (__u16)name_len); + pSMB->ByteCount = cpu_to_le16(name_len); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc); + } else if (data) { + struct timespec64 ts; + __u32 time = le32_to_cpu(pSMBr->last_write_time); + + /* decode response */ + /* BB FIXME - add time zone adjustment BB */ + memset(data, 0, sizeof(FILE_ALL_INFO)); + ts.tv_nsec = 0; + ts.tv_sec = time; + /* decode time fields */ + data->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts)); + data->LastWriteTime = data->ChangeTime; + data->LastAccessTime = 0; + data->AllocationSize = + cpu_to_le64(le32_to_cpu(pSMBr->size)); + data->EndOfFile = data->AllocationSize; + data->Attributes = + cpu_to_le32(le16_to_cpu(pSMBr->attr)); + } else + rc = -EIO; /* bad buffer passed in */ + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QInfRetry; + + return rc; +} + +int +CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_ALL_INFO *pFindData) +{ + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int rc = 0; + int bytes_returned; + __u16 params, byte_count; + +QFileInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); + pSMB->Pad = 0; + pSMB->Fid = netfid; + inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QFileInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc) /* BB add auto retry on EOPNOTSUPP? */ + rc = -EIO; + else if (get_bcc(&pSMBr->hdr) < 40) + rc = -EIO; /* bad smb */ + else if (pFindData) { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, sizeof(FILE_ALL_INFO)); + } else + rc = -ENOMEM; + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QFileInfoRetry; + + return rc; +} + +int +CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + int legacy /* old style infolevel */, + const struct nls_table *nls_codepage, int remap) +{ + /* level 263 SMB_QUERY_FILE_ALL_INFO */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + + /* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */ +QPathInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, search_name, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, search_name); + } + + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + if (legacy) + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); + else + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc) /* BB add auto retry on EOPNOTSUPP? */ + rc = -EIO; + else if (!legacy && get_bcc(&pSMBr->hdr) < 40) + rc = -EIO; /* bad smb */ + else if (legacy && get_bcc(&pSMBr->hdr) < 24) + rc = -EIO; /* 24 or 26 expected but we do not read + last field */ + else if (data) { + int size; + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + + /* + * On legacy responses we do not read the last field, + * EAsize, fortunately since it varies by subdialect and + * also note it differs on Set vs Get, ie two bytes or 4 + * bytes depending but we don't care here. + */ + if (legacy) + size = sizeof(FILE_INFO_STANDARD); + else + size = sizeof(FILE_ALL_INFO); + memcpy((char *) data, (char *) &pSMBr->hdr.Protocol + + data_offset, size); + } else + rc = -ENOMEM; + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QPathInfoRetry; + + return rc; +} + +int +CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_UNIX_BASIC_INFO *pFindData) +{ + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int rc = 0; + int bytes_returned; + __u16 params, byte_count; + +UnixQFileInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); + pSMB->Pad = 0; + pSMB->Fid = netfid; + inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in UnixQFileInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { + cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, + sizeof(FILE_UNIX_BASIC_INFO)); + } + } + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto UnixQFileInfoRetry; + + return rc; +} + +int +CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + FILE_UNIX_BASIC_INFO *pFindData, + const struct nls_table *nls_codepage, int remap) +{ +/* SMB_QUERY_FILE_UNIX_BASIC */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned = 0; + int name_len; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QPathInfo (Unix) the path %s\n", searchName); +UnixQPathInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in UnixQPathInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { + cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, + sizeof(FILE_UNIX_BASIC_INFO)); + } + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto UnixQPathInfoRetry; + + return rc; +} + +/* xid, tcon, searchName and codepage are input parms, rest are returned */ +int +CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, + const char *searchName, struct cifs_sb_info *cifs_sb, + __u16 *pnetfid, __u16 search_flags, + struct cifs_search_info *psrch_inf, bool msearch) +{ +/* level 257 SMB_ */ + TRANSACTION2_FFIRST_REQ *pSMB = NULL; + TRANSACTION2_FFIRST_RSP *pSMBr = NULL; + T2_FFIRST_RSP_PARMS *parms; + int rc = 0; + int bytes_returned = 0; + int name_len, remap; + __u16 params, byte_count; + struct nls_table *nls_codepage; + + cifs_dbg(FYI, "In FindFirst for %s\n", searchName); + +findFirstRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + nls_codepage = cifs_sb->local_nls; + remap = cifs_remap(cifs_sb); + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + /* We can not add the asterik earlier in case + it got remapped to 0xF03A as if it were part of the + directory name instead of a wildcard */ + name_len *= 2; + if (msearch) { + pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb); + pSMB->FileName[name_len+1] = 0; + pSMB->FileName[name_len+2] = '*'; + pSMB->FileName[name_len+3] = 0; + name_len += 4; /* now the trailing null */ + /* null terminate just in case */ + pSMB->FileName[name_len] = 0; + pSMB->FileName[name_len+1] = 0; + name_len += 2; + } + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + if (msearch) { + if (WARN_ON_ONCE(name_len > PATH_MAX-2)) + name_len = PATH_MAX-2; + /* overwrite nul byte */ + pSMB->FileName[name_len-1] = CIFS_DIR_SEP(cifs_sb); + pSMB->FileName[name_len] = '*'; + pSMB->FileName[name_len+1] = 0; + name_len += 2; + } + } + + params = 12 + name_len /* includes null */ ; + pSMB->TotalDataCount = 0; /* no EAs */ + pSMB->MaxParameterCount = cpu_to_le16(10); + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes) + - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */ + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST); + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | + ATTR_DIRECTORY); + pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); + pSMB->SearchFlags = cpu_to_le16(search_flags); + pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); + + /* BB what should we set StorageType to? Does it matter? BB */ + pSMB->SearchStorageType = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_ffirst); + + if (rc) {/* BB add logic to retry regular search if Unix search + rejected unexpectedly by server */ + /* BB Add code to handle unsupported level rc */ + cifs_dbg(FYI, "Error in FindFirst = %d\n", rc); + + cifs_buf_release(pSMB); + + /* BB eventually could optimize out free and realloc of buf */ + /* for this case */ + if (rc == -EAGAIN) + goto findFirstRetry; + } else { /* decode response */ + /* BB remember to free buffer if error BB */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc == 0) { + unsigned int lnoff; + + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + psrch_inf->unicode = true; + else + psrch_inf->unicode = false; + + psrch_inf->ntwrk_buf_start = (char *)pSMBr; + psrch_inf->smallBuf = false; + psrch_inf->srch_entries_start = + (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset)); + + if (parms->EndofSearch) + psrch_inf->endOfSearch = true; + else + psrch_inf->endOfSearch = false; + + psrch_inf->entries_in_buffer = + le16_to_cpu(parms->SearchCount); + psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + + psrch_inf->entries_in_buffer; + lnoff = le16_to_cpu(parms->LastNameOffset); + if (CIFSMaxBufSize < lnoff) { + cifs_dbg(VFS, "ignoring corrupt resume name\n"); + psrch_inf->last_entry = NULL; + return rc; + } + + psrch_inf->last_entry = psrch_inf->srch_entries_start + + lnoff; + + if (pnetfid) + *pnetfid = parms->SearchHandle; + } else { + cifs_buf_release(pSMB); + } + } + + return rc; +} + +int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, + __u16 searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf) +{ + TRANSACTION2_FNEXT_REQ *pSMB = NULL; + TRANSACTION2_FNEXT_RSP *pSMBr = NULL; + T2_FNEXT_RSP_PARMS *parms; + char *response_data; + int rc = 0; + int bytes_returned; + unsigned int name_len; + __u16 params, byte_count; + + cifs_dbg(FYI, "In FindNext\n"); + + if (psrch_inf->endOfSearch) + return -ENOENT; + + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 14; /* includes 2 bytes of null string, converted to LE below*/ + byte_count = 0; + pSMB->TotalDataCount = 0; /* no EAs */ + pSMB->MaxParameterCount = cpu_to_le16(8); + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); + pSMB->SearchHandle = searchHandle; /* always kept as le */ + pSMB->SearchCount = + cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); + pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); + pSMB->ResumeKey = psrch_inf->resume_key; + pSMB->SearchFlags = cpu_to_le16(search_flags); + + name_len = psrch_inf->resume_name_len; + params += name_len; + if (name_len < PATH_MAX) { + memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); + byte_count += name_len; + /* 14 byte parm len above enough for 2 byte null terminator */ + pSMB->ResumeFileName[name_len] = 0; + pSMB->ResumeFileName[name_len+1] = 0; + } else { + rc = -EINVAL; + goto FNext2_err_exit; + } + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext); + if (rc) { + if (rc == -EBADF) { + psrch_inf->endOfSearch = true; + cifs_buf_release(pSMB); + rc = 0; /* search probably was closed at end of search*/ + } else + cifs_dbg(FYI, "FindNext returned = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc == 0) { + unsigned int lnoff; + + /* BB fixme add lock for file (srch_info) struct here */ + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + psrch_inf->unicode = true; + else + psrch_inf->unicode = false; + response_data = (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset); + parms = (T2_FNEXT_RSP_PARMS *)response_data; + response_data = (char *)&pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + if (psrch_inf->smallBuf) + cifs_small_buf_release( + psrch_inf->ntwrk_buf_start); + else + cifs_buf_release(psrch_inf->ntwrk_buf_start); + psrch_inf->srch_entries_start = response_data; + psrch_inf->ntwrk_buf_start = (char *)pSMB; + psrch_inf->smallBuf = false; + if (parms->EndofSearch) + psrch_inf->endOfSearch = true; + else + psrch_inf->endOfSearch = false; + psrch_inf->entries_in_buffer = + le16_to_cpu(parms->SearchCount); + psrch_inf->index_of_last_entry += + psrch_inf->entries_in_buffer; + lnoff = le16_to_cpu(parms->LastNameOffset); + if (CIFSMaxBufSize < lnoff) { + cifs_dbg(VFS, "ignoring corrupt resume name\n"); + psrch_inf->last_entry = NULL; + return rc; + } else + psrch_inf->last_entry = + psrch_inf->srch_entries_start + lnoff; + +/* cifs_dbg(FYI, "fnxt2 entries in buf %d index_of_last %d\n", + psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */ + + /* BB fixme add unlock here */ + } + + } + + /* BB On error, should we leave previous search buf (and count and + last entry fields) intact or free the previous one? */ + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ +FNext2_err_exit: + if (rc != 0) + cifs_buf_release(pSMB); + return rc; +} + +int +CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 searchHandle) +{ + int rc = 0; + FINDCLOSE_REQ *pSMB = NULL; + + cifs_dbg(FYI, "In CIFSSMBFindClose\n"); + rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB); + + /* no sense returning error if session restarted + as file handle has been closed */ + if (rc == -EAGAIN) + return 0; + if (rc) + return rc; + + pSMB->FileID = searchHandle; + pSMB->ByteCount = 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(VFS, "Send error in FindClose = %d\n", rc); + + cifs_stats_inc(&tcon->stats.cifs_stats.num_fclose); + + /* Since session is dead, search handle closed on server already */ + if (rc == -EAGAIN) + rc = 0; + + return rc; +} + +int +CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, __u64 *inode_number, + const struct nls_table *nls_codepage, int remap) +{ + int rc = 0; + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int name_len, bytes_returned; + __u16 params, byte_count; + + cifs_dbg(FYI, "In GetSrvInodeNum for %s\n", search_name); + if (tcon == NULL) + return -ENODEV; + +GetInodeNumberRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + search_name, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, search_name); + } + + params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max data count below from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc); + } else { + /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + /* If rc should we check for EOPNOSUPP and + disable the srvino flag? or in caller? */ + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + struct file_internal_info *pfinfo; + /* BB Do we need a cast or hash here ? */ + if (count < 8) { + cifs_dbg(FYI, "Invalid size ret in QryIntrnlInf\n"); + rc = -EIO; + goto GetInodeNumOut; + } + pfinfo = (struct file_internal_info *) + (data_offset + (char *) &pSMBr->hdr.Protocol); + *inode_number = le64_to_cpu(pfinfo->UniqueId); + } + } +GetInodeNumOut: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto GetInodeNumberRetry; + return rc; +} + +int +CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, + const char *search_name, struct dfs_info3_param **target_nodes, + unsigned int *num_of_nodes, + const struct nls_table *nls_codepage, int remap) +{ +/* TRANS2_GET_DFS_REFERRAL */ + TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; + TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + *num_of_nodes = 0; + *target_nodes = NULL; + + cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name); + if (ses == NULL || ses->tcon_ipc == NULL) + return -ENODEV; + +getDFSRetry: + /* + * Use smb_init_no_reconnect() instead of smb_init() as + * CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus + * causing an infinite recursion. + */ + rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, + (void **)&pSMB, (void **)&pSMBr); + if (rc) + return rc; + + /* server pointer checked in called function, + but should never be null here anyway */ + pSMB->hdr.Mid = get_next_mid(ses->server); + pSMB->hdr.Tid = ses->tcon_ipc->tid; + pSMB->hdr.Uid = ses->Suid; + if (ses->capabilities & CAP_STATUS32) + pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS; + if (ses->capabilities & CAP_DFS) + pSMB->hdr.Flags2 |= SMBFLG2_DFS; + + if (ses->capabilities & CAP_UNICODE) { + pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; + name_len = + cifsConvertToUTF16((__le16 *) pSMB->RequestFileName, + search_name, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB improve the check for buffer overruns BB */ + name_len = copy_path_name(pSMB->RequestFileName, search_name); + } + + if (ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + pSMB->hdr.Uid = ses->Suid; + + params = 2 /* level */ + name_len /*includes null */ ; + pSMB->TotalDataCount = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->MaxParameterCount = 0; + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL); + byte_count = params + 3 /* pad */ ; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->MaxReferralLevel = cpu_to_le16(3); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc); + goto GetDFSRefExit; + } + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + /* BB Also check if enough total bytes returned? */ + if (rc || get_bcc(&pSMBr->hdr) < 17) { + rc = -EIO; /* bad smb */ + goto GetDFSRefExit; + } + + cifs_dbg(FYI, "Decoding GetDFSRefer response BCC: %d Offset %d\n", + get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset)); + + /* parse returned result into more usable form */ + rc = parse_dfs_referrals(&pSMBr->dfs_data, + le16_to_cpu(pSMBr->t2.DataCount), + num_of_nodes, target_nodes, nls_codepage, + remap, search_name, + (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0); + +GetDFSRefExit: + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto getDFSRetry; + + return rc; +} + +/* Query File System Info such as free space to old servers such as Win 9x */ +int +SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData) +{ +/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_ALLOC_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "OldQFSInfo\n"); +oldQFSInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 18) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + cifs_dbg(FYI, "qfsinf resp BCC: %d Offset %d\n", + get_bcc(&pSMBr->hdr), data_offset); + + response_data = (FILE_SYSTEM_ALLOC_INFO *) + (((char *) &pSMBr->hdr.Protocol) + data_offset); + FSData->f_bsize = + le16_to_cpu(response_data->BytesPerSector) * + le32_to_cpu(response_data-> + SectorsPerAllocationUnit); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + + FSData->f_blocks = + le32_to_cpu(response_data->TotalAllocationUnits); + FSData->f_bfree = FSData->f_bavail = + le32_to_cpu(response_data->FreeAllocationUnits); + cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", + (unsigned long long)FSData->f_blocks, + (unsigned long long)FSData->f_bfree, + FSData->f_bsize); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto oldQFSInfoRetry; + + return rc; +} + +int +CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData) +{ +/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSInfo\n"); +QFSInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 24) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + + response_data = + (FILE_SYSTEM_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + FSData->f_bsize = + le32_to_cpu(response_data->BytesPerSector) * + le32_to_cpu(response_data-> + SectorsPerAllocationUnit); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + + FSData->f_blocks = + le64_to_cpu(response_data->TotalAllocationUnits); + FSData->f_bfree = FSData->f_bavail = + le64_to_cpu(response_data->FreeAllocationUnits); + cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", + (unsigned long long)FSData->f_blocks, + (unsigned long long)FSData->f_bfree, + FSData->f_bsize); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSInfoRetry; + + return rc; +} + +int +CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon) +{ +/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_ATTRIBUTE_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSAttributeInfo\n"); +QFSAttributeRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 13) { + /* BB also check if enough bytes returned */ + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_ATTRIBUTE_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + memcpy(&tcon->fsAttrInfo, response_data, + sizeof(FILE_SYSTEM_ATTRIBUTE_INFO)); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSAttributeRetry; + + return rc; +} + +int +CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon) +{ +/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_DEVICE_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSDeviceInfo\n"); +QFSDeviceRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < + sizeof(FILE_SYSTEM_DEVICE_INFO)) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_DEVICE_INFO *) + (((char *) &pSMBr->hdr.Protocol) + + data_offset); + memcpy(&tcon->fsDevInfo, response_data, + sizeof(FILE_SYSTEM_DEVICE_INFO)); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSDeviceRetry; + + return rc; +} + +int +CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon) +{ +/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_UNIX_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSUnixInfo\n"); +QFSUnixRetry: + rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, + (void **) &pSMB, (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(100); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof(struct + smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 13) { + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_UNIX_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + memcpy(&tcon->fsUnixInfo, response_data, + sizeof(FILE_SYSTEM_UNIX_INFO)); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSUnixRetry; + + + return rc; +} + +int +CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, __u64 cap) +{ +/* level 0x200 SMB_SET_CIFS_UNIX_INFO */ + TRANSACTION2_SETFSI_REQ *pSMB = NULL; + TRANSACTION2_SETFSI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cifs_dbg(FYI, "In SETFSUnixInfo\n"); +SETFSUnixRetry: + /* BB switch to small buf init to save memory */ + rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, + (void **) &pSMB, (void **) &pSMBr); + if (rc) + return rc; + + params = 4; /* 2 bytes zero followed by info level. */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) + - 4; + offset = param_offset + params; + + pSMB->MaxParameterCount = cpu_to_le16(4); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(100); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION); + byte_count = 1 /* pad */ + params + 12; + + pSMB->DataCount = cpu_to_le16(12); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + + /* Params. */ + pSMB->FileNum = 0; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO); + + /* Data. */ + pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION); + pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION); + pSMB->ClientUnixCap = cpu_to_le64(cap); + + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc) + rc = -EIO; /* bad smb */ + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SETFSUnixRetry; + + return rc; +} + + + +int +CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData) +{ +/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_POSIX_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSPosixInfo\n"); +QFSPosixRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(100); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof(struct + smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 13) { + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_POSIX_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + FSData->f_bsize = + le32_to_cpu(response_data->BlockSize); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + + FSData->f_blocks = + le64_to_cpu(response_data->TotalBlocks); + FSData->f_bfree = + le64_to_cpu(response_data->BlocksAvail); + if (response_data->UserBlocksAvail == cpu_to_le64(-1)) { + FSData->f_bavail = FSData->f_bfree; + } else { + FSData->f_bavail = + le64_to_cpu(response_data->UserBlocksAvail); + } + if (response_data->TotalFileNodes != cpu_to_le64(-1)) + FSData->f_files = + le64_to_cpu(response_data->TotalFileNodes); + if (response_data->FreeFileNodes != cpu_to_le64(-1)) + FSData->f_ffree = + le64_to_cpu(response_data->FreeFileNodes); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSPosixRetry; + + return rc; +} + + +/* + * We can not use write of zero bytes trick to set file size due to need for + * large file support. Also note that this SetPathInfo is preferred to + * SetFileInfo based method in next routine which is only needed to work around + * a sharing violation bugin Samba which this routine can run into. + */ +int +CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb, + bool set_allocation) +{ + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; + struct file_end_of_file_info *parm_data; + int name_len; + int rc = 0; + int bytes_returned = 0; + int remap = cifs_remap(cifs_sb); + + __u16 params, byte_count, data_count, param_offset, offset; + + cifs_dbg(FYI, "In SetEOF\n"); +SetEOFRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, + PATH_MAX, cifs_sb->local_nls, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, file_name); + } + params = 6 + name_len; + data_count = sizeof(struct file_end_of_file_info); + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(4100); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + if (set_allocation) { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); + } else /* Set File Size */ { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); + } + + parm_data = + (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + + offset); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + data_count; + pSMB->DataCount = cpu_to_le16(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + parm_data->FileSize = cpu_to_le64(size); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SetEOFRetry; + + return rc; +} + +int +CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, bool set_allocation) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + struct file_end_of_file_info *parm_data; + int rc = 0; + __u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n", + (long long)size); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + count = sizeof(struct file_end_of_file_info); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + parm_data = + (struct file_end_of_file_info *)(((char *)pSMB) + offset + 4); + pSMB->DataOffset = cpu_to_le16(offset); + parm_data->FileSize = cpu_to_le64(size); + pSMB->Fid = cfile->fid.netfid; + if (set_allocation) { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); + } else /* Set File Size */ { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); + } + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) { + cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n", + rc); + } + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +/* Some legacy servers such as NT4 require that the file times be set on + an open handle, rather than by pathname - this is awkward due to + potential access conflicts on the open, but it is unavoidable for these + old servers since the only other choice is to go from 100 nanosecond DCE + time and resort to the original setpathinfo level which takes the ancient + DOS time format with 2 second granularity */ +int +CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + char *data_offset; + int rc = 0; + __u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "Set Times (via SetFileInfo)\n"); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; + + count = sizeof(FILE_BASIC_INFO); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->Fid = fid; + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); + else + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", + rc); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int +CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, + bool delete_file, __u16 fid, __u32 pid_of_opener) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + char *data_offset; + int rc = 0; + __u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n"); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)(pSMB) + offset + 4; + + count = 1; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->Fid = fid; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + *data_offset = delete_file ? 1 : 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc); + + return rc; +} + +static int +CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + int oplock = 0; + struct cifs_open_parms oparms; + struct cifs_fid fid; + int rc; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = fileName, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) + goto out; + + rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid); + CIFSSMBClose(xid, tcon, fid.netfid); +out: + + return rc; +} + +int +CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + int name_len; + int rc = 0; + int bytes_returned = 0; + char *data_offset; + __u16 params, param_offset, offset, byte_count, count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In SetTimes\n"); + +SetTimesRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + + params = 6 + name_len; + count = sizeof(FILE_BASIC_INFO); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + data_offset = (char *) (&pSMB->hdr.Protocol) + offset; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); + else + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SetTimesRetry; + + if (rc == -EOPNOTSUPP) + return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data, + nls_codepage, cifs_sb); + + return rc; +} + +static void +cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, + const struct cifs_unix_set_info_args *args) +{ + u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64; + u64 mode = args->mode; + + if (uid_valid(args->uid)) + uid = from_kuid(&init_user_ns, args->uid); + if (gid_valid(args->gid)) + gid = from_kgid(&init_user_ns, args->gid); + + /* + * Samba server ignores set of file size to zero due to bugs in some + * older clients, but we should be precise - we use SetFileSize to + * set file size and do not want to truncate file size to zero + * accidentally as happened on one Samba server beta by putting + * zero instead of -1 here + */ + data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64); + data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64); + data_offset->LastStatusChange = cpu_to_le64(args->ctime); + data_offset->LastAccessTime = cpu_to_le64(args->atime); + data_offset->LastModificationTime = cpu_to_le64(args->mtime); + data_offset->Uid = cpu_to_le64(uid); + data_offset->Gid = cpu_to_le64(gid); + /* better to leave device as zero when it is */ + data_offset->DevMajor = cpu_to_le64(MAJOR(args->device)); + data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); + data_offset->Permissions = cpu_to_le64(mode); + + if (S_ISREG(mode)) + data_offset->Type = cpu_to_le32(UNIX_FILE); + else if (S_ISDIR(mode)) + data_offset->Type = cpu_to_le32(UNIX_DIR); + else if (S_ISLNK(mode)) + data_offset->Type = cpu_to_le32(UNIX_SYMLINK); + else if (S_ISCHR(mode)) + data_offset->Type = cpu_to_le32(UNIX_CHARDEV); + else if (S_ISBLK(mode)) + data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV); + else if (S_ISFIFO(mode)) + data_offset->Type = cpu_to_le32(UNIX_FIFO); + else if (S_ISSOCK(mode)) + data_offset->Type = cpu_to_le32(UNIX_SOCKET); +} + +int +CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const struct cifs_unix_set_info_args *args, + u16 fid, u32 pid_of_opener) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + char *data_offset; + int rc = 0; + u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n"); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; + + count = sizeof(FILE_UNIX_BASIC_INFO); + + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->Fid = fid; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); + + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", + rc); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int +CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, + const struct cifs_unix_set_info_args *args, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + int name_len; + int rc = 0; + int bytes_returned = 0; + FILE_UNIX_BASIC_INFO *data_offset; + __u16 params, param_offset, offset, count, byte_count; + + cifs_dbg(FYI, "In SetUID/GID/Mode\n"); +setPermsRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, file_name); + } + + params = 6 + name_len; + count = sizeof(FILE_UNIX_BASIC_INFO); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (FILE_UNIX_BASIC_INFO *)((char *) pSMB + offset + 4); + memset(data_offset, 0, count); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->DataCount = cpu_to_le16(count); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + + cifs_fill_unix_set_info(data_offset, args); + + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto setPermsRetry; + return rc; +} + +#ifdef CONFIG_CIFS_XATTR +/* + * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common + * function used by listxattr and getxattr type calls. When ea_name is set, + * it looks for that attribute name and stuffs that value into the EAData + * buffer. When ea_name is NULL, it stuffs a list of attribute names into the + * buffer. In both cases, the return value is either the length of the + * resulting data or a negative error code. If EAData is a NULL pointer then + * the data isn't copied to it, but the length is returned. + */ +ssize_t +CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, const unsigned char *ea_name, + char *EAData, size_t buf_size, + struct cifs_sb_info *cifs_sb) +{ + /* BB assumes one setup word */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int remap = cifs_remap(cifs_sb); + struct nls_table *nls_codepage = cifs_sb->local_nls; + int rc = 0; + int bytes_returned; + int list_len; + struct fealist *ea_response_data; + struct fea *temp_fea; + char *temp_ptr; + char *end_of_smb; + __u16 params, byte_count, data_offset; + unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; + + cifs_dbg(FYI, "In Query All EAs path %s\n", searchName); +QAllEAsRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + list_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + list_len++; /* trailing null */ + list_len *= 2; + } else { + list_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc); + goto QAllEAsOut; + } + + + /* BB also check enough total bytes returned */ + /* BB we need to improve the validity checking + of these trans2 responses */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc || get_bcc(&pSMBr->hdr) < 4) { + rc = -EIO; /* bad smb */ + goto QAllEAsOut; + } + + /* check that length of list is not more than bcc */ + /* check that each entry does not go beyond length + of list */ + /* check that each element of each entry does not + go beyond end of list */ + /* validate_trans2_offsets() */ + /* BB check if start of smb + data_offset > &bcc+ bcc */ + + data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + ea_response_data = (struct fealist *) + (((char *) &pSMBr->hdr.Protocol) + data_offset); + + list_len = le32_to_cpu(ea_response_data->list_len); + cifs_dbg(FYI, "ea length %d\n", list_len); + if (list_len <= 8) { + cifs_dbg(FYI, "empty EA list returned from server\n"); + /* didn't find the named attribute */ + if (ea_name) + rc = -ENODATA; + goto QAllEAsOut; + } + + /* make sure list_len doesn't go past end of SMB */ + end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr); + if ((char *)ea_response_data + list_len > end_of_smb) { + cifs_dbg(FYI, "EA list appears to go beyond SMB\n"); + rc = -EIO; + goto QAllEAsOut; + } + + /* account for ea list len */ + list_len -= 4; + temp_fea = ea_response_data->list; + temp_ptr = (char *)temp_fea; + while (list_len > 0) { + unsigned int name_len; + __u16 value_len; + + list_len -= 4; + temp_ptr += 4; + /* make sure we can read name_len and value_len */ + if (list_len < 0) { + cifs_dbg(FYI, "EA entry goes beyond length of list\n"); + rc = -EIO; + goto QAllEAsOut; + } + + name_len = temp_fea->name_len; + value_len = le16_to_cpu(temp_fea->value_len); + list_len -= name_len + 1 + value_len; + if (list_len < 0) { + cifs_dbg(FYI, "EA entry goes beyond length of list\n"); + rc = -EIO; + goto QAllEAsOut; + } + + if (ea_name) { + if (ea_name_len == name_len && + memcmp(ea_name, temp_ptr, name_len) == 0) { + temp_ptr += name_len + 1; + rc = value_len; + if (buf_size == 0) + goto QAllEAsOut; + if ((size_t)value_len > buf_size) { + rc = -ERANGE; + goto QAllEAsOut; + } + memcpy(EAData, temp_ptr, value_len); + goto QAllEAsOut; + } + } else { + /* account for prefix user. and trailing null */ + rc += (5 + 1 + name_len); + if (rc < (int) buf_size) { + memcpy(EAData, "user.", 5); + EAData += 5; + memcpy(EAData, temp_ptr, name_len); + EAData += name_len; + /* null terminate name */ + *EAData = 0; + ++EAData; + } else if (buf_size == 0) { + /* skip copy - calc size only */ + } else { + /* stop before overrun buffer */ + rc = -ERANGE; + break; + } + } + temp_ptr += name_len + 1 + value_len; + temp_fea = (struct fea *)temp_ptr; + } + + /* didn't find the named attribute */ + if (ea_name) + rc = -ENODATA; + +QAllEAsOut: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QAllEAsRetry; + + return (ssize_t)rc; +} + +int +CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const char *ea_name, const void *ea_value, + const __u16 ea_value_len, const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; + struct fealist *parm_data; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, byte_count, offset, count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In SetEA\n"); +SetEARetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + + params = 6 + name_len; + + /* done calculating parms using name_len of file name, + now use name_len to calculate length of ea name + we are going to create in the inode xattrs */ + if (ea_name == NULL) + name_len = 0; + else + name_len = strnlen(ea_name, 255); + + count = sizeof(*parm_data) + ea_value_len + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_EA); + + parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + parm_data->list_len = cpu_to_le32(count); + parm_data->list[0].EA_flags = 0; + /* we checked above that name len is less than 255 */ + parm_data->list[0].name_len = (__u8)name_len; + /* EA names are always ASCII */ + if (ea_name) + strncpy(parm_data->list[0].name, ea_name, name_len); + parm_data->list[0].name[name_len] = 0; + parm_data->list[0].value_len = cpu_to_le16(ea_value_len); + /* caller ensures that ea_value_len is less than 64K but + we need to ensure that it fits within the smb */ + + /*BB add length check to see if it would fit in + negotiated SMB buffer size BB */ + /* if (ea_value_len > buffer_size - 512 (enough for header)) */ + if (ea_value_len) + memcpy(parm_data->list[0].name+name_len+1, + ea_value, ea_value_len); + + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SetEARetry; + + return rc; +} +#endif diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c new file mode 100644 index 000000000000..935fe198a4ba --- /dev/null +++ b/fs/smb/client/connect.c @@ -0,0 +1,4754 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "ntlmssp.h" +#include "nterr.h" +#include "rfc1002pdu.h" +#include "fscache.h" +#include "smb2proto.h" +#include "smbdirect.h" +#include "dns_resolve.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#include "fs_context.h" +#include "cifs_swn.h" + +extern mempool_t *cifs_req_poolp; +extern bool disable_legacy_dialects; + +/* FIXME: should these be tunable? */ +#define TLINK_ERROR_EXPIRE (1 * HZ) +#define TLINK_IDLE_EXPIRE (600 * HZ) + +/* Drop the connection to not overload the server */ +#define NUM_STATUS_IO_TIMEOUT 5 + +struct mount_ctx { + struct cifs_sb_info *cifs_sb; + struct smb3_fs_context *fs_ctx; + unsigned int xid; + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; +#ifdef CONFIG_CIFS_DFS_UPCALL + struct cifs_ses *root_ses; + uuid_t mount_id; + char *origin_fullpath, *leaf_fullpath; +#endif +}; + +static int ip_connect(struct TCP_Server_Info *server); +static int generic_ip_connect(struct TCP_Server_Info *server); +static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); +static void cifs_prune_tlinks(struct work_struct *work); + +/* + * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may + * get their ip addresses changed at some point. + * + * This should be called with server->srv_mutex held. + */ +static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) +{ + int rc; + int len; + char *unc, *ipaddr = NULL; + time64_t expiry, now; + unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT; + + if (!server->hostname) + return -EINVAL; + + /* if server hostname isn't populated, there's nothing to do here */ + if (server->hostname[0] == '\0') + return 0; + + len = strlen(server->hostname) + 3; + + unc = kmalloc(len, GFP_KERNEL); + if (!unc) { + cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__); + return -ENOMEM; + } + scnprintf(unc, len, "\\\\%s", server->hostname); + + rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry); + kfree(unc); + + if (rc < 0) { + cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n", + __func__, server->hostname, rc); + goto requeue_resolve; + } + + spin_lock(&server->srv_lock); + rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr, + strlen(ipaddr)); + spin_unlock(&server->srv_lock); + kfree(ipaddr); + + /* rc == 1 means success here */ + if (rc) { + now = ktime_get_real_seconds(); + if (expiry && expiry > now) + /* + * To make sure we don't use the cached entry, retry 1s + * after expiry. + */ + ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1; + } + rc = !rc ? -1 : 0; + +requeue_resolve: + cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n", + __func__, ttl); + mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ)); + + return rc; +} + +static void smb2_query_server_interfaces(struct work_struct *work) +{ + int rc; + struct cifs_tcon *tcon = container_of(work, + struct cifs_tcon, + query_interfaces.work); + + /* + * query server network interfaces, in case they change + */ + rc = SMB3_request_interfaces(0, tcon, false); + if (rc) { + cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", + __func__, rc); + } + + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); +} + +static void cifs_resolve_server(struct work_struct *work) +{ + int rc; + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, resolve.work); + + cifs_server_lock(server); + + /* + * Resolve the hostname again to make sure that IP address is up-to-date. + */ + rc = reconn_set_ipaddr_from_hostname(server); + if (rc) { + cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", + __func__, rc); + } + + cifs_server_unlock(server); +} + +/* + * Update the tcpStatus for the server. + * This is used to signal the cifsd thread to call cifs_reconnect + * ONLY cifsd thread should call cifs_reconnect. For any other + * thread, use this function + * + * @server: the tcp ses for which reconnect is needed + * @all_channels: if this needs to be done for all channels + */ +void +cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, + bool all_channels) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + int i; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + spin_lock(&pserver->srv_lock); + if (!all_channels) { + pserver->tcpStatus = CifsNeedReconnect; + spin_unlock(&pserver->srv_lock); + return; + } + spin_unlock(&pserver->srv_lock); + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + spin_lock(&ses->chan_lock); + for (i = 0; i < ses->chan_count; i++) { + spin_lock(&ses->chans[i].server->srv_lock); + ses->chans[i].server->tcpStatus = CifsNeedReconnect; + spin_unlock(&ses->chans[i].server->srv_lock); + } + spin_unlock(&ses->chan_lock); + } + spin_unlock(&cifs_tcp_ses_lock); +} + +/* + * Mark all sessions and tcons for reconnect. + * IMPORTANT: make sure that this gets called only from + * cifsd thread. For any other thread, use + * cifs_signal_cifsd_for_reconnect + * + * @server: the tcp ses for which reconnect is needed + * @server needs to be previously set to CifsNeedReconnect. + * @mark_smb_session: whether even sessions need to be marked + */ +void +cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses, *nses; + struct cifs_tcon *tcon; + + /* + * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they + * are not used until reconnected. + */ + cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { + /* check if iface is still active */ + if (!cifs_chan_is_iface_active(ses, server)) + cifs_chan_update_iface(ses, server); + + spin_lock(&ses->chan_lock); + if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + continue; + } + + if (mark_smb_session) + CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses); + else + cifs_chan_set_need_reconnect(ses, server); + + cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", + __func__, ses->chans_need_reconnect); + + /* If all channels need reconnect, then tcon needs reconnect */ + if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { + spin_unlock(&ses->chan_lock); + continue; + } + spin_unlock(&ses->chan_lock); + + spin_lock(&ses->ses_lock); + ses->ses_status = SES_NEED_RECON; + spin_unlock(&ses->ses_lock); + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + tcon->need_reconnect = true; + spin_lock(&tcon->tc_lock); + tcon->status = TID_NEED_RECON; + spin_unlock(&tcon->tc_lock); + } + if (ses->tcon_ipc) { + ses->tcon_ipc->need_reconnect = true; + spin_lock(&ses->tcon_ipc->tc_lock); + ses->tcon_ipc->status = TID_NEED_RECON; + spin_unlock(&ses->tcon_ipc->tc_lock); + } + } + spin_unlock(&cifs_tcp_ses_lock); +} + +static void +cifs_abort_connection(struct TCP_Server_Info *server) +{ + struct mid_q_entry *mid, *nmid; + struct list_head retry_list; + + server->maxBuf = 0; + server->max_read = 0; + + /* do not want to be sending data on a socket we are freeing */ + cifs_dbg(FYI, "%s: tearing down socket\n", __func__); + cifs_server_lock(server); + if (server->ssocket) { + cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, + server->ssocket->flags); + kernel_sock_shutdown(server->ssocket, SHUT_WR); + cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state, + server->ssocket->flags); + sock_release(server->ssocket); + server->ssocket = NULL; + } + server->sequence_number = 0; + server->session_estab = false; + kfree_sensitive(server->session_key.response); + server->session_key.response = NULL; + server->session_key.len = 0; + server->lstrp = jiffies; + + /* mark submitted MIDs for retry and issue callback */ + INIT_LIST_HEAD(&retry_list); + cifs_dbg(FYI, "%s: moving mids to private list\n", __func__); + spin_lock(&server->mid_lock); + list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) { + kref_get(&mid->refcount); + if (mid->mid_state == MID_REQUEST_SUBMITTED) + mid->mid_state = MID_RETRY_NEEDED; + list_move(&mid->qhead, &retry_list); + mid->mid_flags |= MID_DELETED; + } + spin_unlock(&server->mid_lock); + cifs_server_unlock(server); + + cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); + list_for_each_entry_safe(mid, nmid, &retry_list, qhead) { + list_del_init(&mid->qhead); + mid->callback(mid); + release_mid(mid); + } + + if (cifs_rdma_enabled(server)) { + cifs_server_lock(server); + smbd_destroy(server); + cifs_server_unlock(server); + } +} + +static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets) +{ + spin_lock(&server->srv_lock); + server->nr_targets = num_targets; + if (server->tcpStatus == CifsExiting) { + /* the demux thread will exit normally next time through the loop */ + spin_unlock(&server->srv_lock); + wake_up(&server->response_q); + return false; + } + + cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); + trace_smb3_reconnect(server->CurrentMid, server->conn_id, + server->hostname); + server->tcpStatus = CifsNeedReconnect; + + spin_unlock(&server->srv_lock); + return true; +} + +/* + * cifs tcp session reconnection + * + * mark tcp session as reconnecting so temporarily locked + * mark all smb sessions as reconnecting for tcp session + * reconnect tcp session + * wake up waiters on reconnection? - (not needed currently) + * + * if mark_smb_session is passed as true, unconditionally mark + * the smb session (and tcon) for reconnect as well. This value + * doesn't really matter for non-multichannel scenario. + * + */ +static int __cifs_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session) +{ + int rc = 0; + + if (!cifs_tcp_ses_needs_reconnect(server, 1)) + return 0; + + cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session); + + cifs_abort_connection(server); + + do { + try_to_freeze(); + cifs_server_lock(server); + + if (!cifs_swn_set_server_dstaddr(server)) { + /* resolve the hostname again to make sure that IP address is up-to-date */ + rc = reconn_set_ipaddr_from_hostname(server); + cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); + } + + if (cifs_rdma_enabled(server)) + rc = smbd_reconnect(server); + else + rc = generic_ip_connect(server); + if (rc) { + cifs_server_unlock(server); + cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); + msleep(3000); + } else { + atomic_inc(&tcpSesReconnectCount); + set_credits(server, 1); + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsExiting) + server->tcpStatus = CifsNeedNegotiate; + spin_unlock(&server->srv_lock); + cifs_swn_reset_server_dstaddr(server); + cifs_server_unlock(server); + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + } + } while (server->tcpStatus == CifsNeedReconnect); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedNegotiate) + mod_delayed_work(cifsiod_wq, &server->echo, 0); + spin_unlock(&server->srv_lock); + + wake_up(&server->response_q); + return rc; +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target) +{ + int rc; + char *hostname; + + if (!cifs_swn_set_server_dstaddr(server)) { + if (server->hostname != target) { + hostname = extract_hostname(target); + if (!IS_ERR(hostname)) { + spin_lock(&server->srv_lock); + kfree(server->hostname); + server->hostname = hostname; + spin_unlock(&server->srv_lock); + } else { + cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", + __func__, PTR_ERR(hostname)); + cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__, + server->hostname); + } + } + /* resolve the hostname again to make sure that IP address is up-to-date. */ + rc = reconn_set_ipaddr_from_hostname(server); + cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); + } + /* Reconnect the socket */ + if (cifs_rdma_enabled(server)) + rc = smbd_reconnect(server); + else + rc = generic_ip_connect(server); + + return rc; +} + +static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl, + struct dfs_cache_tgt_iterator **target_hint) +{ + int rc; + struct dfs_cache_tgt_iterator *tit; + + *target_hint = NULL; + + /* If dfs target list is empty, then reconnect to last server */ + tit = dfs_cache_get_tgt_iterator(tl); + if (!tit) + return __reconnect_target_unlocked(server, server->hostname); + + /* Otherwise, try every dfs target in @tl */ + for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { + rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit)); + if (!rc) { + *target_hint = tit; + break; + } + } + return rc; +} + +static int reconnect_dfs_server(struct TCP_Server_Info *server) +{ + int rc = 0; + const char *refpath = server->current_fullpath + 1; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + struct dfs_cache_tgt_iterator *target_hint = NULL; + int num_targets = 0; + + /* + * Determine the number of dfs targets the referral path in @cifs_sb resolves to. + * + * smb2_reconnect() needs to know how long it should wait based upon the number of dfs + * targets (server->nr_targets). It's also possible that the cached referral was cleared + * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after + * refreshing the referral, so, in this case, default it to 1. + */ + if (!dfs_cache_noreq_find(refpath, NULL, &tl)) + num_targets = dfs_cache_get_nr_tgts(&tl); + if (!num_targets) + num_targets = 1; + + if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) + return 0; + + /* + * Unconditionally mark all sessions & tcons for reconnect as we might be connecting to a + * different server or share during failover. It could be improved by adding some logic to + * only do that in case it connects to a different server or share, though. + */ + cifs_mark_tcp_ses_conns_for_reconnect(server, true); + + cifs_abort_connection(server); + + do { + try_to_freeze(); + cifs_server_lock(server); + + rc = reconnect_target_unlocked(server, &tl, &target_hint); + if (rc) { + /* Failed to reconnect socket */ + cifs_server_unlock(server); + cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); + msleep(3000); + continue; + } + /* + * Socket was created. Update tcp session status to CifsNeedNegotiate so that a + * process waiting for reconnect will know it needs to re-establish session and tcon + * through the reconnected target server. + */ + atomic_inc(&tcpSesReconnectCount); + set_credits(server, 1); + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsExiting) + server->tcpStatus = CifsNeedNegotiate; + spin_unlock(&server->srv_lock); + cifs_swn_reset_server_dstaddr(server); + cifs_server_unlock(server); + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + } while (server->tcpStatus == CifsNeedReconnect); + + if (target_hint) + dfs_cache_noreq_update_tgthint(refpath, target_hint); + + dfs_cache_free_tgts(&tl); + + /* Need to set up echo worker again once connection has been established */ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedNegotiate) + mod_delayed_work(cifsiod_wq, &server->echo, 0); + spin_unlock(&server->srv_lock); + + wake_up(&server->response_q); + return rc; +} + +int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) +{ + /* If tcp session is not an dfs connection, then reconnect to last target server */ + spin_lock(&server->srv_lock); + if (!server->is_dfs_conn) { + spin_unlock(&server->srv_lock); + return __cifs_reconnect(server, mark_smb_session); + } + spin_unlock(&server->srv_lock); + + mutex_lock(&server->refpath_lock); + if (!server->origin_fullpath || !server->leaf_fullpath) { + mutex_unlock(&server->refpath_lock); + return __cifs_reconnect(server, mark_smb_session); + } + mutex_unlock(&server->refpath_lock); + + return reconnect_dfs_server(server); +} +#else +int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) +{ + return __cifs_reconnect(server, mark_smb_session); +} +#endif + +static void +cifs_echo_request(struct work_struct *work) +{ + int rc; + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, echo.work); + + /* + * We cannot send an echo if it is disabled. + * Also, no need to ping if we got a response recently. + */ + + if (server->tcpStatus == CifsNeedReconnect || + server->tcpStatus == CifsExiting || + server->tcpStatus == CifsNew || + (server->ops->can_echo && !server->ops->can_echo(server)) || + time_before(jiffies, server->lstrp + server->echo_interval - HZ)) + goto requeue_echo; + + rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS; + cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc); + + /* Check witness registrations */ + cifs_swn_check(); + +requeue_echo: + queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval); +} + +static bool +allocate_buffers(struct TCP_Server_Info *server) +{ + if (!server->bigbuf) { + server->bigbuf = (char *)cifs_buf_get(); + if (!server->bigbuf) { + cifs_server_dbg(VFS, "No memory for large SMB response\n"); + msleep(3000); + /* retry will check if exiting */ + return false; + } + } else if (server->large_buf) { + /* we are reusing a dirty large buf, clear its start */ + memset(server->bigbuf, 0, HEADER_SIZE(server)); + } + + if (!server->smallbuf) { + server->smallbuf = (char *)cifs_small_buf_get(); + if (!server->smallbuf) { + cifs_server_dbg(VFS, "No memory for SMB response\n"); + msleep(1000); + /* retry will check if exiting */ + return false; + } + /* beginning of smb buffer is cleared in our buf_get */ + } else { + /* if existing small buf clear beginning */ + memset(server->smallbuf, 0, HEADER_SIZE(server)); + } + + return true; +} + +static bool +server_unresponsive(struct TCP_Server_Info *server) +{ + /* + * We need to wait 3 echo intervals to make sure we handle such + * situations right: + * 1s client sends a normal SMB request + * 2s client gets a response + * 30s echo workqueue job pops, and decides we got a response recently + * and don't need to send another + * ... + * 65s kernel_recvmsg times out, and we see that we haven't gotten + * a response in >60s. + */ + spin_lock(&server->srv_lock); + if ((server->tcpStatus == CifsGood || + server->tcpStatus == CifsNeedNegotiate) && + (!server->ops->can_echo || server->ops->can_echo(server)) && + time_after(jiffies, server->lstrp + 3 * server->echo_interval)) { + spin_unlock(&server->srv_lock); + cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n", + (3 * server->echo_interval) / HZ); + cifs_reconnect(server, false); + return true; + } + spin_unlock(&server->srv_lock); + + return false; +} + +static inline bool +zero_credits(struct TCP_Server_Info *server) +{ + int val; + + spin_lock(&server->req_lock); + val = server->credits + server->echo_credits + server->oplock_credits; + if (server->in_flight == 0 && val == 0) { + spin_unlock(&server->req_lock); + return true; + } + spin_unlock(&server->req_lock); + return false; +} + +static int +cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) +{ + int length = 0; + int total_read; + + for (total_read = 0; msg_data_left(smb_msg); total_read += length) { + try_to_freeze(); + + /* reconnect if no credits and no requests in flight */ + if (zero_credits(server)) { + cifs_reconnect(server, false); + return -ECONNABORTED; + } + + if (server_unresponsive(server)) + return -ECONNABORTED; + if (cifs_rdma_enabled(server) && server->smbd_conn) + length = smbd_recv(server->smbd_conn, smb_msg); + else + length = sock_recvmsg(server->ssocket, smb_msg, 0); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ESHUTDOWN; + } + + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + cifs_reconnect(server, false); + return -ECONNABORTED; + } + spin_unlock(&server->srv_lock); + + if (length == -ERESTARTSYS || + length == -EAGAIN || + length == -EINTR) { + /* + * Minimum sleep to prevent looping, allowing socket + * to clear and app threads to set tcpStatus + * CifsNeedReconnect if server hung. + */ + usleep_range(1000, 2000); + length = 0; + continue; + } + + if (length <= 0) { + cifs_dbg(FYI, "Received no data or error: %d\n", length); + cifs_reconnect(server, false); + return -ECONNABORTED; + } + } + return total_read; +} + +int +cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, + unsigned int to_read) +{ + struct msghdr smb_msg = {}; + struct kvec iov = {.iov_base = buf, .iov_len = to_read}; + iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read); + + return cifs_readv_from_socket(server, &smb_msg); +} + +ssize_t +cifs_discard_from_socket(struct TCP_Server_Info *server, size_t to_read) +{ + struct msghdr smb_msg = {}; + + /* + * iov_iter_discard already sets smb_msg.type and count and iov_offset + * and cifs_readv_from_socket sets msg_control and msg_controllen + * so little to initialize in struct msghdr + */ + iov_iter_discard(&smb_msg.msg_iter, ITER_DEST, to_read); + + return cifs_readv_from_socket(server, &smb_msg); +} + +int +cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page, + unsigned int page_offset, unsigned int to_read) +{ + struct msghdr smb_msg = {}; + struct bio_vec bv = { + .bv_page = page, .bv_len = to_read, .bv_offset = page_offset}; + iov_iter_bvec(&smb_msg.msg_iter, ITER_DEST, &bv, 1, to_read); + return cifs_readv_from_socket(server, &smb_msg); +} + +static bool +is_smb_response(struct TCP_Server_Info *server, unsigned char type) +{ + /* + * The first byte big endian of the length field, + * is actually not part of the length but the type + * with the most common, zero, as regular data. + */ + switch (type) { + case RFC1002_SESSION_MESSAGE: + /* Regular SMB response */ + return true; + case RFC1002_SESSION_KEEP_ALIVE: + cifs_dbg(FYI, "RFC 1002 session keep alive\n"); + break; + case RFC1002_POSITIVE_SESSION_RESPONSE: + cifs_dbg(FYI, "RFC 1002 positive session response\n"); + break; + case RFC1002_NEGATIVE_SESSION_RESPONSE: + /* + * We get this from Windows 98 instead of an error on + * SMB negprot response. + */ + cifs_dbg(FYI, "RFC 1002 negative session response\n"); + /* give server a second to clean up */ + msleep(1000); + /* + * Always try 445 first on reconnect since we get NACK + * on some if we ever connected to port 139 (the NACK + * is since we do not begin with RFC1001 session + * initialize frame). + */ + cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT); + cifs_reconnect(server, true); + break; + default: + cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); + cifs_reconnect(server, true); + } + + return false; +} + +void +dequeue_mid(struct mid_q_entry *mid, bool malformed) +{ +#ifdef CONFIG_CIFS_STATS2 + mid->when_received = jiffies; +#endif + spin_lock(&mid->server->mid_lock); + if (!malformed) + mid->mid_state = MID_RESPONSE_RECEIVED; + else + mid->mid_state = MID_RESPONSE_MALFORMED; + /* + * Trying to handle/dequeue a mid after the send_recv() + * function has finished processing it is a bug. + */ + if (mid->mid_flags & MID_DELETED) { + spin_unlock(&mid->server->mid_lock); + pr_warn_once("trying to dequeue a deleted mid\n"); + } else { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + spin_unlock(&mid->server->mid_lock); + } +} + +static unsigned int +smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; + + /* + * SMB1 does not use credits. + */ + if (is_smb1(server)) + return 0; + + return le16_to_cpu(shdr->CreditRequest); +} + +static void +handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, + char *buf, int malformed) +{ + if (server->ops->check_trans2 && + server->ops->check_trans2(mid, server, buf, malformed)) + return; + mid->credits_received = smb2_get_credits_from_hdr(buf, server); + mid->resp_buf = buf; + mid->large_buf = server->large_buf; + /* Was previous buf put in mpx struct for multi-rsp? */ + if (!mid->multiRsp) { + /* smb buffer will be freed by user thread */ + if (server->large_buf) + server->bigbuf = NULL; + else + server->smallbuf = NULL; + } + dequeue_mid(mid, malformed); +} + +int +cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) +{ + bool srv_sign_required = server->sec_mode & server->vals->signing_required; + bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled; + bool mnt_sign_enabled; + + /* + * Is signing required by mnt options? If not then check + * global_secflags to see if it is there. + */ + if (!mnt_sign_required) + mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) == + CIFSSEC_MUST_SIGN); + + /* + * If signing is required then it's automatically enabled too, + * otherwise, check to see if the secflags allow it. + */ + mnt_sign_enabled = mnt_sign_required ? mnt_sign_required : + (global_secflags & CIFSSEC_MAY_SIGN); + + /* If server requires signing, does client allow it? */ + if (srv_sign_required) { + if (!mnt_sign_enabled) { + cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!\n"); + return -EOPNOTSUPP; + } + server->sign = true; + } + + /* If client requires signing, does server allow it? */ + if (mnt_sign_required) { + if (!srv_sign_enabled) { + cifs_dbg(VFS, "Server does not support signing!\n"); + return -EOPNOTSUPP; + } + server->sign = true; + } + + if (cifs_rdma_enabled(server) && server->sign) + cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled\n"); + + return 0; +} + + +static void clean_demultiplex_info(struct TCP_Server_Info *server) +{ + int length; + + /* take it off the list, if it's not already */ + spin_lock(&server->srv_lock); + list_del_init(&server->tcp_ses_list); + spin_unlock(&server->srv_lock); + + cancel_delayed_work_sync(&server->echo); + cancel_delayed_work_sync(&server->resolve); + + spin_lock(&server->srv_lock); + server->tcpStatus = CifsExiting; + spin_unlock(&server->srv_lock); + wake_up_all(&server->response_q); + + /* check if we have blocked requests that need to free */ + spin_lock(&server->req_lock); + if (server->credits <= 0) + server->credits = 1; + spin_unlock(&server->req_lock); + /* + * Although there should not be any requests blocked on this queue it + * can not hurt to be paranoid and try to wake up requests that may + * haven been blocked when more than 50 at time were on the wire to the + * same server - they now will see the session is in exit state and get + * out of SendReceive. + */ + wake_up_all(&server->request_q); + /* give those requests time to exit */ + msleep(125); + if (cifs_rdma_enabled(server)) + smbd_destroy(server); + if (server->ssocket) { + sock_release(server->ssocket); + server->ssocket = NULL; + } + + if (!list_empty(&server->pending_mid_q)) { + struct list_head dispose_list; + struct mid_q_entry *mid_entry; + struct list_head *tmp, *tmp2; + + INIT_LIST_HEAD(&dispose_list); + spin_lock(&server->mid_lock); + list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid); + kref_get(&mid_entry->refcount); + mid_entry->mid_state = MID_SHUTDOWN; + list_move(&mid_entry->qhead, &dispose_list); + mid_entry->mid_flags |= MID_DELETED; + } + spin_unlock(&server->mid_lock); + + /* now walk dispose list and issue callbacks */ + list_for_each_safe(tmp, tmp2, &dispose_list) { + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid); + list_del_init(&mid_entry->qhead); + mid_entry->callback(mid_entry); + release_mid(mid_entry); + } + /* 1/8th of sec is more than enough time for them to exit */ + msleep(125); + } + + if (!list_empty(&server->pending_mid_q)) { + /* + * mpx threads have not exited yet give them at least the smb + * send timeout time for long ops. + * + * Due to delays on oplock break requests, we need to wait at + * least 45 seconds before giving up on a request getting a + * response and going ahead and killing cifsd. + */ + cifs_dbg(FYI, "Wait for exit from demultiplex thread\n"); + msleep(46000); + /* + * If threads still have not exited they are probably never + * coming home not much else we can do but free the memory. + */ + } + +#ifdef CONFIG_CIFS_DFS_UPCALL + kfree(server->origin_fullpath); + kfree(server->leaf_fullpath); +#endif + kfree(server); + + length = atomic_dec_return(&tcpSesAllocCount); + if (length > 0) + mempool_resize(cifs_req_poolp, length + cifs_min_rcv); +} + +static int +standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + int length; + char *buf = server->smallbuf; + unsigned int pdu_length = server->pdu_size; + + /* make sure this will fit in a large buffer */ + if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - + HEADER_PREAMBLE_SIZE(server)) { + cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); + cifs_reconnect(server, true); + return -ECONNABORTED; + } + + /* switch to large buffer if too big for a small one */ + if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { + server->large_buf = true; + memcpy(server->bigbuf, buf, server->total_read); + buf = server->bigbuf; + } + + /* now read the rest */ + length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, + pdu_length - MID_HEADER_SIZE(server)); + + if (length < 0) + return length; + server->total_read += length; + + dump_smb(buf, server->total_read); + + return cifs_handle_standard(server, mid); +} + +int +cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + char *buf = server->large_buf ? server->bigbuf : server->smallbuf; + int rc; + + /* + * We know that we received enough to get to the MID as we + * checked the pdu_length earlier. Now check to see + * if the rest of the header is OK. + * + * 48 bytes is enough to display the header and a little bit + * into the payload for debugging purposes. + */ + rc = server->ops->check_message(buf, server->total_read, server); + if (rc) + cifs_dump_mem("Bad SMB: ", buf, + min_t(unsigned int, server->total_read, 48)); + + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + cifs_reconnect(server, true); + return -1; + } + + if (server->ops->is_status_pending && + server->ops->is_status_pending(buf, server)) + return -1; + + if (!mid) + return rc; + + handle_mid(mid, server, buf, rc); + return 0; +} + +static void +smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; + int scredits, in_flight; + + /* + * SMB1 does not use credits. + */ + if (is_smb1(server)) + return; + + if (shdr->CreditRequest) { + spin_lock(&server->req_lock); + server->credits += le16_to_cpu(shdr->CreditRequest); + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + trace_smb3_hdr_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + le16_to_cpu(shdr->CreditRequest), in_flight); + cifs_server_dbg(FYI, "%s: added %u credits total=%d\n", + __func__, le16_to_cpu(shdr->CreditRequest), + scredits); + } +} + + +static int +cifs_demultiplex_thread(void *p) +{ + int i, num_mids, length; + struct TCP_Server_Info *server = p; + unsigned int pdu_length; + unsigned int next_offset; + char *buf = NULL; + struct task_struct *task_to_wake = NULL; + struct mid_q_entry *mids[MAX_COMPOUND]; + char *bufs[MAX_COMPOUND]; + unsigned int noreclaim_flag, num_io_timeout = 0; + + noreclaim_flag = memalloc_noreclaim_save(); + cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); + + length = atomic_inc_return(&tcpSesAllocCount); + if (length > 1) + mempool_resize(cifs_req_poolp, length + cifs_min_rcv); + + set_freezable(); + allow_kernel_signal(SIGKILL); + while (server->tcpStatus != CifsExiting) { + if (try_to_freeze()) + continue; + + if (!allocate_buffers(server)) + continue; + + server->large_buf = false; + buf = server->smallbuf; + pdu_length = 4; /* enough to get RFC1001 header */ + + length = cifs_read_from_socket(server, buf, pdu_length); + if (length < 0) + continue; + + if (is_smb1(server)) + server->total_read = length; + else + server->total_read = 0; + + /* + * The right amount was read from socket - 4 bytes, + * so we can now interpret the length field. + */ + pdu_length = get_rfc1002_length(buf); + + cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); + if (!is_smb_response(server, buf[0])) + continue; +next_pdu: + server->pdu_size = pdu_length; + + /* make sure we have enough to get to the MID */ + if (server->pdu_size < MID_HEADER_SIZE(server)) { + cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", + server->pdu_size); + cifs_reconnect(server, true); + continue; + } + + /* read down to the MID */ + length = cifs_read_from_socket(server, + buf + HEADER_PREAMBLE_SIZE(server), + MID_HEADER_SIZE(server)); + if (length < 0) + continue; + server->total_read += length; + + if (server->ops->next_header) { + next_offset = server->ops->next_header(buf); + if (next_offset) + server->pdu_size = next_offset; + } + + memset(mids, 0, sizeof(mids)); + memset(bufs, 0, sizeof(bufs)); + num_mids = 0; + + if (server->ops->is_transform_hdr && + server->ops->receive_transform && + server->ops->is_transform_hdr(buf)) { + length = server->ops->receive_transform(server, + mids, + bufs, + &num_mids); + } else { + mids[0] = server->ops->find_mid(server, buf); + bufs[0] = buf; + num_mids = 1; + + if (!mids[0] || !mids[0]->receive) + length = standard_receive3(server, mids[0]); + else + length = mids[0]->receive(server, mids[0]); + } + + if (length < 0) { + for (i = 0; i < num_mids; i++) + if (mids[i]) + release_mid(mids[i]); + continue; + } + + if (server->ops->is_status_io_timeout && + server->ops->is_status_io_timeout(buf)) { + num_io_timeout++; + if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) { + cifs_reconnect(server, false); + num_io_timeout = 0; + continue; + } + } + + server->lstrp = jiffies; + + for (i = 0; i < num_mids; i++) { + if (mids[i] != NULL) { + mids[i]->resp_buf_size = server->pdu_size; + + if (bufs[i] && server->ops->is_network_name_deleted) + server->ops->is_network_name_deleted(bufs[i], + server); + + if (!mids[i]->multiRsp || mids[i]->multiEnd) + mids[i]->callback(mids[i]); + + release_mid(mids[i]); + } else if (server->ops->is_oplock_break && + server->ops->is_oplock_break(bufs[i], + server)) { + smb2_add_credits_from_hdr(bufs[i], server); + cifs_dbg(FYI, "Received oplock break\n"); + } else { + cifs_server_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", + atomic_read(&mid_count)); + cifs_dump_mem("Received Data is: ", bufs[i], + HEADER_SIZE(server)); + smb2_add_credits_from_hdr(bufs[i], server); +#ifdef CONFIG_CIFS_DEBUG2 + if (server->ops->dump_detail) + server->ops->dump_detail(bufs[i], + server); + cifs_dump_mids(server); +#endif /* CIFS_DEBUG2 */ + } + } + + if (pdu_length > server->pdu_size) { + if (!allocate_buffers(server)) + continue; + pdu_length -= server->pdu_size; + server->total_read = 0; + server->large_buf = false; + buf = server->smallbuf; + goto next_pdu; + } + } /* end while !EXITING */ + + /* buffer usually freed in free_mid - need to free it here on exit */ + cifs_buf_release(server->bigbuf); + if (server->smallbuf) /* no sense logging a debug message if NULL */ + cifs_small_buf_release(server->smallbuf); + + task_to_wake = xchg(&server->tsk, NULL); + clean_demultiplex_info(server); + + /* if server->tsk was NULL then wait for a signal before exiting */ + if (!task_to_wake) { + set_current_state(TASK_INTERRUPTIBLE); + while (!signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + set_current_state(TASK_RUNNING); + } + + memalloc_noreclaim_restore(noreclaim_flag); + module_put_and_kthread_exit(0); +} + +/* + * Returns true if srcaddr isn't specified and rhs isn't specified, or + * if srcaddr is specified and matches the IP address of the rhs argument + */ +bool +cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs) +{ + switch (srcaddr->sa_family) { + case AF_UNSPEC: + return (rhs->sa_family == AF_UNSPEC); + case AF_INET: { + struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; + struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; + return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); + } + case AF_INET6: { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; + struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs; + return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); + } + default: + WARN_ON(1); + return false; /* don't expect to be here */ + } +} + +/* + * If no port is specified in addr structure, we try to match with 445 port + * and if it fails - with 139 ports. It should be called only if address + * families of server and addr are equal. + */ +static bool +match_port(struct TCP_Server_Info *server, struct sockaddr *addr) +{ + __be16 port, *sport; + + /* SMBDirect manages its own ports, don't match it here */ + if (server->rdma) + return true; + + switch (addr->sa_family) { + case AF_INET: + sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port; + port = ((struct sockaddr_in *) addr)->sin_port; + break; + case AF_INET6: + sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port; + port = ((struct sockaddr_in6 *) addr)->sin6_port; + break; + default: + WARN_ON(1); + return false; + } + + if (!port) { + port = htons(CIFS_PORT); + if (port == *sport) + return true; + + port = htons(RFC1001_PORT); + } + + return port == *sport; +} + +static bool +match_address(struct TCP_Server_Info *server, struct sockaddr *addr, + struct sockaddr *srcaddr) +{ + switch (addr->sa_family) { + case AF_INET: { + struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + struct sockaddr_in *srv_addr4 = + (struct sockaddr_in *)&server->dstaddr; + + if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr) + return false; + break; + } + case AF_INET6: { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + struct sockaddr_in6 *srv_addr6 = + (struct sockaddr_in6 *)&server->dstaddr; + + if (!ipv6_addr_equal(&addr6->sin6_addr, + &srv_addr6->sin6_addr)) + return false; + if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id) + return false; + break; + } + default: + WARN_ON(1); + return false; /* don't expect to be here */ + } + + if (!cifs_match_ipaddr(srcaddr, (struct sockaddr *)&server->srcaddr)) + return false; + + return true; +} + +static bool +match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + /* + * The select_sectype function should either return the ctx->sectype + * that was specified, or "Unspecified" if that sectype was not + * compatible with the given NEGOTIATE request. + */ + if (server->ops->select_sectype(server, ctx->sectype) + == Unspecified) + return false; + + /* + * Now check if signing mode is acceptable. No need to check + * global_secflags at this point since if MUST_SIGN is set then + * the server->sign had better be too. + */ + if (ctx->sign && !server->sign) + return false; + + return true; +} + +/* this function must be called with srv_lock held */ +static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; + + lockdep_assert_held(&server->srv_lock); + + if (ctx->nosharesock) + return 0; + + /* this server does not share socket */ + if (server->nosharesock) + return 0; + + /* If multidialect negotiation see if existing sessions match one */ + if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { + if (server->vals->protocol_id < SMB30_PROT_ID) + return 0; + } else if (strcmp(ctx->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + if (server->vals->protocol_id < SMB21_PROT_ID) + return 0; + } else if ((server->vals != ctx->vals) || (server->ops != ctx->ops)) + return 0; + + if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) + return 0; + + if (strcasecmp(server->hostname, ctx->server_hostname)) + return 0; + + if (!match_address(server, addr, + (struct sockaddr *)&ctx->srcaddr)) + return 0; + + if (!match_port(server, addr)) + return 0; + + if (!match_security(server, ctx)) + return 0; + + if (server->echo_interval != ctx->echo_interval * HZ) + return 0; + + if (server->rdma != ctx->rdma) + return 0; + + if (server->ignore_signature != ctx->ignore_signature) + return 0; + + if (server->min_offload != ctx->min_offload) + return 0; + + return 1; +} + +struct TCP_Server_Info * +cifs_find_tcp_session(struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + spin_lock(&server->srv_lock); +#ifdef CONFIG_CIFS_DFS_UPCALL + /* + * DFS failover implementation in cifs_reconnect() requires unique tcp sessions for + * DFS connections to do failover properly, so avoid sharing them with regular + * shares or even links that may connect to same server but having completely + * different failover targets. + */ + if (server->is_dfs_conn) { + spin_unlock(&server->srv_lock); + continue; + } +#endif + /* + * Skip ses channels since they're only handled in lower layers + * (e.g. cifs_send_recv). + */ + if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) { + spin_unlock(&server->srv_lock); + continue; + } + spin_unlock(&server->srv_lock); + + ++server->srv_count; + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "Existing tcp session with server found\n"); + return server; + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + +void +cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) +{ + struct task_struct *task; + + spin_lock(&cifs_tcp_ses_lock); + if (--server->srv_count > 0) { + spin_unlock(&cifs_tcp_ses_lock); + return; + } + + /* srv_count can never go negative */ + WARN_ON(server->srv_count < 0); + + put_net(cifs_net_ns(server)); + + list_del_init(&server->tcp_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + /* For secondary channels, we pick up ref-count on the primary server */ + if (CIFS_SERVER_IS_CHAN(server)) + cifs_put_tcp_session(server->primary_server, from_reconnect); + + cancel_delayed_work_sync(&server->echo); + cancel_delayed_work_sync(&server->resolve); + + if (from_reconnect) + /* + * Avoid deadlock here: reconnect work calls + * cifs_put_tcp_session() at its end. Need to be sure + * that reconnect work does nothing with server pointer after + * that step. + */ + cancel_delayed_work(&server->reconnect); + else + cancel_delayed_work_sync(&server->reconnect); + + spin_lock(&server->srv_lock); + server->tcpStatus = CifsExiting; + spin_unlock(&server->srv_lock); + + cifs_crypto_secmech_release(server); + + kfree_sensitive(server->session_key.response); + server->session_key.response = NULL; + server->session_key.len = 0; + kfree(server->hostname); + server->hostname = NULL; + + task = xchg(&server->tsk, NULL); + if (task) + send_sig(SIGKILL, task, 1); +} + +struct TCP_Server_Info * +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server) +{ + struct TCP_Server_Info *tcp_ses = NULL; + int rc; + + cifs_dbg(FYI, "UNC: %s\n", ctx->UNC); + + /* see if we already have a matching tcp_ses */ + tcp_ses = cifs_find_tcp_session(ctx); + if (tcp_ses) + return tcp_ses; + + tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); + if (!tcp_ses) { + rc = -ENOMEM; + goto out_err; + } + + tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL); + if (!tcp_ses->hostname) { + rc = -ENOMEM; + goto out_err; + } + + if (ctx->nosharesock) + tcp_ses->nosharesock = true; + + tcp_ses->ops = ctx->ops; + tcp_ses->vals = ctx->vals; + cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns)); + + tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId); + tcp_ses->noblockcnt = ctx->rootfs; + tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs; + tcp_ses->noautotune = ctx->noautotune; + tcp_ses->tcp_nodelay = ctx->sockopt_tcp_nodelay; + tcp_ses->rdma = ctx->rdma; + tcp_ses->in_flight = 0; + tcp_ses->max_in_flight = 0; + tcp_ses->credits = 1; + if (primary_server) { + spin_lock(&cifs_tcp_ses_lock); + ++primary_server->srv_count; + spin_unlock(&cifs_tcp_ses_lock); + tcp_ses->primary_server = primary_server; + } + init_waitqueue_head(&tcp_ses->response_q); + init_waitqueue_head(&tcp_ses->request_q); + INIT_LIST_HEAD(&tcp_ses->pending_mid_q); + mutex_init(&tcp_ses->_srv_mutex); + memcpy(tcp_ses->workstation_RFC1001_name, + ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + memcpy(tcp_ses->server_RFC1001_name, + ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + tcp_ses->session_estab = false; + tcp_ses->sequence_number = 0; + tcp_ses->reconnect_instance = 1; + tcp_ses->lstrp = jiffies; + tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); + spin_lock_init(&tcp_ses->req_lock); + spin_lock_init(&tcp_ses->srv_lock); + spin_lock_init(&tcp_ses->mid_lock); + INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); + INIT_LIST_HEAD(&tcp_ses->smb_ses_list); + INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); + INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server); + INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); + mutex_init(&tcp_ses->reconnect_mutex); +#ifdef CONFIG_CIFS_DFS_UPCALL + mutex_init(&tcp_ses->refpath_lock); +#endif + memcpy(&tcp_ses->srcaddr, &ctx->srcaddr, + sizeof(tcp_ses->srcaddr)); + memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, + sizeof(tcp_ses->dstaddr)); + if (ctx->use_client_guid) + memcpy(tcp_ses->client_guid, ctx->client_guid, + SMB2_CLIENT_GUID_SIZE); + else + generate_random_uuid(tcp_ses->client_guid); + /* + * at this point we are the only ones with the pointer + * to the struct since the kernel thread not created yet + * no need to spinlock this init of tcpStatus or srv_count + */ + tcp_ses->tcpStatus = CifsNew; + ++tcp_ses->srv_count; + + if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN && + ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX) + tcp_ses->echo_interval = ctx->echo_interval * HZ; + else + tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ; + if (tcp_ses->rdma) { +#ifndef CONFIG_CIFS_SMB_DIRECT + cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n"); + rc = -ENOENT; + goto out_err_crypto_release; +#endif + tcp_ses->smbd_conn = smbd_get_connection( + tcp_ses, (struct sockaddr *)&ctx->dstaddr); + if (tcp_ses->smbd_conn) { + cifs_dbg(VFS, "RDMA transport established\n"); + rc = 0; + goto smbd_connected; + } else { + rc = -ENOENT; + goto out_err_crypto_release; + } + } + rc = ip_connect(tcp_ses); + if (rc < 0) { + cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n"); + goto out_err_crypto_release; + } +smbd_connected: + /* + * since we're in a cifs function already, we know that + * this will succeed. No need for try_module_get(). + */ + __module_get(THIS_MODULE); + tcp_ses->tsk = kthread_run(cifs_demultiplex_thread, + tcp_ses, "cifsd"); + if (IS_ERR(tcp_ses->tsk)) { + rc = PTR_ERR(tcp_ses->tsk); + cifs_dbg(VFS, "error %d create cifsd thread\n", rc); + module_put(THIS_MODULE); + goto out_err_crypto_release; + } + tcp_ses->min_offload = ctx->min_offload; + /* + * at this point we are the only ones with the pointer + * to the struct since the kernel thread not created yet + * no need to spinlock this update of tcpStatus + */ + spin_lock(&tcp_ses->srv_lock); + tcp_ses->tcpStatus = CifsNeedNegotiate; + spin_unlock(&tcp_ses->srv_lock); + + if ((ctx->max_credits < 20) || (ctx->max_credits > 60000)) + tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE; + else + tcp_ses->max_credits = ctx->max_credits; + + tcp_ses->nr_targets = 1; + tcp_ses->ignore_signature = ctx->ignore_signature; + /* thread spawned, put it on the list */ + spin_lock(&cifs_tcp_ses_lock); + list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + /* queue echo request delayed work */ + queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval); + + /* queue dns resolution delayed work */ + cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n", + __func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT); + + queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ)); + + return tcp_ses; + +out_err_crypto_release: + cifs_crypto_secmech_release(tcp_ses); + + put_net(cifs_net_ns(tcp_ses)); + +out_err: + if (tcp_ses) { + if (CIFS_SERVER_IS_CHAN(tcp_ses)) + cifs_put_tcp_session(tcp_ses->primary_server, false); + kfree(tcp_ses->hostname); + if (tcp_ses->ssocket) + sock_release(tcp_ses->ssocket); + kfree(tcp_ses); + } + return ERR_PTR(rc); +} + +/* this function must be called with ses_lock and chan_lock held */ +static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + if (ctx->sectype != Unspecified && + ctx->sectype != ses->sectype) + return 0; + + /* + * If an existing session is limited to less channels than + * requested, it should not be reused + */ + if (ses->chan_max < ctx->max_channels) + return 0; + + switch (ses->sectype) { + case Kerberos: + if (!uid_eq(ctx->cred_uid, ses->cred_uid)) + return 0; + break; + default: + /* NULL username means anonymous session */ + if (ses->user_name == NULL) { + if (!ctx->nullauth) + return 0; + break; + } + + /* anything else takes username/password */ + if (strncmp(ses->user_name, + ctx->username ? ctx->username : "", + CIFS_MAX_USERNAME_LEN)) + return 0; + if ((ctx->username && strlen(ctx->username) != 0) && + ses->password != NULL && + strncmp(ses->password, + ctx->password ? ctx->password : "", + CIFS_MAX_PASSWORD_LEN)) + return 0; + } + return 1; +} + +/** + * cifs_setup_ipc - helper to setup the IPC tcon for the session + * @ses: smb session to issue the request on + * @ctx: the superblock configuration context to use for building the + * new tree connection for the IPC (interprocess communication RPC) + * + * A new IPC connection is made and stored in the session + * tcon_ipc. The IPC tcon has the same lifetime as the session. + */ +static int +cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + int rc = 0, xid; + struct cifs_tcon *tcon; + char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; + bool seal = false; + struct TCP_Server_Info *server = ses->server; + + /* + * If the mount request that resulted in the creation of the + * session requires encryption, force IPC to be encrypted too. + */ + if (ctx->seal) { + if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) + seal = true; + else { + cifs_server_dbg(VFS, + "IPC: server doesn't support encryption\n"); + return -EOPNOTSUPP; + } + } + + tcon = tconInfoAlloc(); + if (tcon == NULL) + return -ENOMEM; + + spin_lock(&server->srv_lock); + scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname); + spin_unlock(&server->srv_lock); + + xid = get_xid(); + tcon->ses = ses; + tcon->ipc = true; + tcon->seal = seal; + rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls); + free_xid(xid); + + if (rc) { + cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); + tconInfoFree(tcon); + goto out; + } + + cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid); + + spin_lock(&tcon->tc_lock); + tcon->status = TID_GOOD; + spin_unlock(&tcon->tc_lock); + ses->tcon_ipc = tcon; +out: + return rc; +} + +/** + * cifs_free_ipc - helper to release the session IPC tcon + * @ses: smb session to unmount the IPC from + * + * Needs to be called everytime a session is destroyed. + * + * On session close, the IPC is closed and the server must release all tcons of the session. + * No need to send a tree disconnect here. + * + * Besides, it will make the server to not close durable and resilient files on session close, as + * specified in MS-SMB2 3.3.5.6 Receiving an SMB2 LOGOFF Request. + */ +static int +cifs_free_ipc(struct cifs_ses *ses) +{ + struct cifs_tcon *tcon = ses->tcon_ipc; + + if (tcon == NULL) + return 0; + + tconInfoFree(tcon); + ses->tcon_ipc = NULL; + return 0; +} + +static struct cifs_ses * +cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + struct cifs_ses *ses; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_EXITING) { + spin_unlock(&ses->ses_lock); + continue; + } + spin_lock(&ses->chan_lock); + if (!match_session(ses, ctx)) { + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + continue; + } + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + ++ses->ses_count; + spin_unlock(&cifs_tcp_ses_lock); + return ses; + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + +void cifs_put_smb_ses(struct cifs_ses *ses) +{ + unsigned int rc, xid; + unsigned int chan_count; + struct TCP_Server_Info *server = ses->server; + + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_EXITING) { + spin_unlock(&ses->ses_lock); + return; + } + spin_unlock(&ses->ses_lock); + + cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); + cifs_dbg(FYI, + "%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->tree_name : "NONE"); + + spin_lock(&cifs_tcp_ses_lock); + if (--ses->ses_count > 0) { + spin_unlock(&cifs_tcp_ses_lock); + return; + } + spin_unlock(&cifs_tcp_ses_lock); + + /* ses_count can never go negative */ + WARN_ON(ses->ses_count < 0); + + if (ses->ses_status == SES_GOOD) + ses->ses_status = SES_EXITING; + + cifs_free_ipc(ses); + + if (ses->ses_status == SES_EXITING && server->ops->logoff) { + xid = get_xid(); + rc = server->ops->logoff(xid, ses); + if (rc) + cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n", + __func__, rc); + _free_xid(xid); + } + + spin_lock(&cifs_tcp_ses_lock); + list_del_init(&ses->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + chan_count = ses->chan_count; + + /* close any extra channels */ + if (chan_count > 1) { + int i; + + for (i = 1; i < chan_count; i++) { + if (ses->chans[i].iface) { + kref_put(&ses->chans[i].iface->refcount, release_iface); + ses->chans[i].iface = NULL; + } + cifs_put_tcp_session(ses->chans[i].server, 0); + ses->chans[i].server = NULL; + } + } + + sesInfoFree(ses); + cifs_put_tcp_session(server, 0); +} + +#ifdef CONFIG_KEYS + +/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */ +#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1) + +/* Populate username and pw fields from keyring if possible */ +static int +cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses) +{ + int rc = 0; + int is_domain = 0; + const char *delim, *payload; + char *desc; + ssize_t len; + struct key *key; + struct TCP_Server_Info *server = ses->server; + struct sockaddr_in *sa; + struct sockaddr_in6 *sa6; + const struct user_key_payload *upayload; + + desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); + if (!desc) + return -ENOMEM; + + /* try to find an address key first */ + switch (server->dstaddr.ss_family) { + case AF_INET: + sa = (struct sockaddr_in *)&server->dstaddr; + sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: + sa6 = (struct sockaddr_in6 *)&server->dstaddr; + sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr); + break; + default: + cifs_dbg(FYI, "Bad ss_family (%hu)\n", + server->dstaddr.ss_family); + rc = -EINVAL; + goto out_err; + } + + cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); + key = request_key(&key_type_logon, desc, ""); + if (IS_ERR(key)) { + if (!ses->domainName) { + cifs_dbg(FYI, "domainName is NULL\n"); + rc = PTR_ERR(key); + goto out_err; + } + + /* didn't work, try to find a domain key */ + sprintf(desc, "cifs:d:%s", ses->domainName); + cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); + key = request_key(&key_type_logon, desc, ""); + if (IS_ERR(key)) { + rc = PTR_ERR(key); + goto out_err; + } + is_domain = 1; + } + + down_read(&key->sem); + upayload = user_key_payload_locked(key); + if (IS_ERR_OR_NULL(upayload)) { + rc = upayload ? PTR_ERR(upayload) : -EINVAL; + goto out_key_put; + } + + /* find first : in payload */ + payload = upayload->data; + delim = strnchr(payload, upayload->datalen, ':'); + cifs_dbg(FYI, "payload=%s\n", payload); + if (!delim) { + cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n", + upayload->datalen); + rc = -EINVAL; + goto out_key_put; + } + + len = delim - payload; + if (len > CIFS_MAX_USERNAME_LEN || len <= 0) { + cifs_dbg(FYI, "Bad value from username search (len=%zd)\n", + len); + rc = -EINVAL; + goto out_key_put; + } + + ctx->username = kstrndup(payload, len, GFP_KERNEL); + if (!ctx->username) { + cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n", + len); + rc = -ENOMEM; + goto out_key_put; + } + cifs_dbg(FYI, "%s: username=%s\n", __func__, ctx->username); + + len = key->datalen - (len + 1); + if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) { + cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len); + rc = -EINVAL; + kfree(ctx->username); + ctx->username = NULL; + goto out_key_put; + } + + ++delim; + ctx->password = kstrndup(delim, len, GFP_KERNEL); + if (!ctx->password) { + cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n", + len); + rc = -ENOMEM; + kfree(ctx->username); + ctx->username = NULL; + goto out_key_put; + } + + /* + * If we have a domain key then we must set the domainName in the + * for the request. + */ + if (is_domain && ses->domainName) { + ctx->domainname = kstrdup(ses->domainName, GFP_KERNEL); + if (!ctx->domainname) { + cifs_dbg(FYI, "Unable to allocate %zd bytes for domain\n", + len); + rc = -ENOMEM; + kfree(ctx->username); + ctx->username = NULL; + kfree_sensitive(ctx->password); + ctx->password = NULL; + goto out_key_put; + } + } + + strscpy(ctx->workstation_name, ses->workstation_name, sizeof(ctx->workstation_name)); + +out_key_put: + up_read(&key->sem); + key_put(key); +out_err: + kfree(desc); + cifs_dbg(FYI, "%s: returning %d\n", __func__, rc); + return rc; +} +#else /* ! CONFIG_KEYS */ +static inline int +cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)), + struct cifs_ses *ses __attribute__((unused))) +{ + return -ENOSYS; +} +#endif /* CONFIG_KEYS */ + +/** + * cifs_get_smb_ses - get a session matching @ctx data from @server + * @server: server to setup the session to + * @ctx: superblock configuration context to use to setup the session + * + * This function assumes it is being called from cifs_mount() where we + * already got a server reference (server refcount +1). See + * cifs_get_tcon() for refcount explanations. + */ +struct cifs_ses * +cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + int rc = 0; + unsigned int xid; + struct cifs_ses *ses; + struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; + + xid = get_xid(); + + ses = cifs_find_smb_ses(server, ctx); + if (ses) { + cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", + ses->ses_status); + + spin_lock(&ses->chan_lock); + if (cifs_chan_needs_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + cifs_dbg(FYI, "Session needs reconnect\n"); + + mutex_lock(&ses->session_mutex); + rc = cifs_negotiate_protocol(xid, ses, server); + if (rc) { + mutex_unlock(&ses->session_mutex); + /* problem -- put our ses reference */ + cifs_put_smb_ses(ses); + free_xid(xid); + return ERR_PTR(rc); + } + + rc = cifs_setup_session(xid, ses, server, + ctx->local_nls); + if (rc) { + mutex_unlock(&ses->session_mutex); + /* problem -- put our reference */ + cifs_put_smb_ses(ses); + free_xid(xid); + return ERR_PTR(rc); + } + mutex_unlock(&ses->session_mutex); + + spin_lock(&ses->chan_lock); + } + spin_unlock(&ses->chan_lock); + + /* existing SMB ses has a server reference already */ + cifs_put_tcp_session(server, 0); + free_xid(xid); + return ses; + } + + rc = -ENOMEM; + + cifs_dbg(FYI, "Existing smb sess not found\n"); + ses = sesInfoAlloc(); + if (ses == NULL) + goto get_ses_fail; + + /* new SMB session uses our server ref */ + ses->server = server; + if (server->dstaddr.ss_family == AF_INET6) + sprintf(ses->ip_addr, "%pI6", &addr6->sin6_addr); + else + sprintf(ses->ip_addr, "%pI4", &addr->sin_addr); + + if (ctx->username) { + ses->user_name = kstrdup(ctx->username, GFP_KERNEL); + if (!ses->user_name) + goto get_ses_fail; + } + + /* ctx->password freed at unmount */ + if (ctx->password) { + ses->password = kstrdup(ctx->password, GFP_KERNEL); + if (!ses->password) + goto get_ses_fail; + } + if (ctx->domainname) { + ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL); + if (!ses->domainName) + goto get_ses_fail; + } + + strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name)); + + if (ctx->domainauto) + ses->domainAuto = ctx->domainauto; + ses->cred_uid = ctx->cred_uid; + ses->linux_uid = ctx->linux_uid; + + ses->sectype = ctx->sectype; + ses->sign = ctx->sign; + + /* add server as first channel */ + spin_lock(&ses->chan_lock); + ses->chans[0].server = server; + ses->chan_count = 1; + ses->chan_max = ctx->multichannel ? ctx->max_channels:1; + ses->chans_need_reconnect = 1; + spin_unlock(&ses->chan_lock); + + mutex_lock(&ses->session_mutex); + rc = cifs_negotiate_protocol(xid, ses, server); + if (!rc) + rc = cifs_setup_session(xid, ses, server, ctx->local_nls); + mutex_unlock(&ses->session_mutex); + + /* each channel uses a different signing key */ + spin_lock(&ses->chan_lock); + memcpy(ses->chans[0].signkey, ses->smb3signingkey, + sizeof(ses->smb3signingkey)); + spin_unlock(&ses->chan_lock); + + if (rc) + goto get_ses_fail; + + /* + * success, put it on the list and add it as first channel + * note: the session becomes active soon after this. So you'll + * need to lock before changing something in the session. + */ + spin_lock(&cifs_tcp_ses_lock); + list_add(&ses->smb_ses_list, &server->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + cifs_setup_ipc(ses, ctx); + + free_xid(xid); + + return ses; + +get_ses_fail: + sesInfoFree(ses); + free_xid(xid); + return ERR_PTR(rc); +} + +/* this function must be called with tc_lock held */ +static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + if (tcon->status == TID_EXITING) + return 0; + if (strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) + return 0; + if (tcon->seal != ctx->seal) + return 0; + if (tcon->snapshot_time != ctx->snapshot_time) + return 0; + if (tcon->handle_timeout != ctx->handle_timeout) + return 0; + if (tcon->no_lease != ctx->no_lease) + return 0; + if (tcon->nodelete != ctx->nodelete) + return 0; + return 1; +} + +static struct cifs_tcon * +cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + struct cifs_tcon *tcon; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->tc_lock); + if (!match_tcon(tcon, ctx)) { + spin_unlock(&tcon->tc_lock); + continue; + } + ++tcon->tc_count; + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return tcon; + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + +void +cifs_put_tcon(struct cifs_tcon *tcon) +{ + unsigned int xid; + struct cifs_ses *ses; + + /* + * IPC tcon share the lifetime of their session and are + * destroyed in the session put function + */ + if (tcon == NULL || tcon->ipc) + return; + + ses = tcon->ses; + cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); + spin_lock(&cifs_tcp_ses_lock); + spin_lock(&tcon->tc_lock); + if (--tcon->tc_count > 0) { + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return; + } + + /* tc_count can never go negative */ + WARN_ON(tcon->tc_count < 0); + + list_del_init(&tcon->tcon_list); + tcon->status = TID_EXITING; + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + + /* cancel polling of interfaces */ + cancel_delayed_work_sync(&tcon->query_interfaces); + + if (tcon->use_witness) { + int rc; + + rc = cifs_swn_unregister(tcon); + if (rc < 0) { + cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", + __func__, rc); + } + } + + xid = get_xid(); + if (ses->server->ops->tree_disconnect) + ses->server->ops->tree_disconnect(xid, tcon); + _free_xid(xid); + + cifs_fscache_release_super_cookie(tcon); + tconInfoFree(tcon); + cifs_put_smb_ses(ses); +} + +/** + * cifs_get_tcon - get a tcon matching @ctx data from @ses + * @ses: smb session to issue the request on + * @ctx: the superblock configuration context to use for building the + * + * - tcon refcount is the number of mount points using the tcon. + * - ses refcount is the number of tcon using the session. + * + * 1. This function assumes it is being called from cifs_mount() where + * we already got a session reference (ses refcount +1). + * + * 2. Since we're in the context of adding a mount point, the end + * result should be either: + * + * a) a new tcon already allocated with refcount=1 (1 mount point) and + * its session refcount incremented (1 new tcon). This +1 was + * already done in (1). + * + * b) an existing tcon with refcount+1 (add a mount point to it) and + * identical ses refcount (no new tcon). Because of (1) we need to + * decrement the ses refcount. + */ +static struct cifs_tcon * +cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + int rc, xid; + struct cifs_tcon *tcon; + + tcon = cifs_find_tcon(ses, ctx); + if (tcon) { + /* + * tcon has refcount already incremented but we need to + * decrement extra ses reference gotten by caller (case b) + */ + cifs_dbg(FYI, "Found match on UNC path\n"); + cifs_put_smb_ses(ses); + return tcon; + } + + if (!ses->server->ops->tree_connect) { + rc = -ENOSYS; + goto out_fail; + } + + tcon = tconInfoAlloc(); + if (tcon == NULL) { + rc = -ENOMEM; + goto out_fail; + } + + if (ctx->snapshot_time) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "Use SMB2 or later for snapshot mount option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else + tcon->snapshot_time = ctx->snapshot_time; + } + + if (ctx->handle_timeout) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "Use SMB2.1 or later for handle timeout option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else + tcon->handle_timeout = ctx->handle_timeout; + } + + tcon->ses = ses; + if (ctx->password) { + tcon->password = kstrdup(ctx->password, GFP_KERNEL); + if (!tcon->password) { + rc = -ENOMEM; + goto out_fail; + } + } + + if (ctx->seal) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB3 or later required for encryption\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else if (tcon->ses->server->capabilities & + SMB2_GLOBAL_CAP_ENCRYPTION) + tcon->seal = true; + else { + cifs_dbg(VFS, "Encryption is not supported on share\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } + + if (ctx->linux_ext) { + if (ses->server->posix_ext_supported) { + tcon->posix_extensions = true; + pr_warn_once("SMB3.11 POSIX Extensions are experimental\n"); + } else if ((ses->server->vals->protocol_id == SMB311_PROT_ID) || + (strcmp(ses->server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) || + (strcmp(ses->server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0)) { + cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else { + cifs_dbg(VFS, "Check vers= mount option. SMB3.11 " + "disabled but required for POSIX extensions\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } + + xid = get_xid(); + rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon, + ctx->local_nls); + free_xid(xid); + cifs_dbg(FYI, "Tcon rc = %d\n", rc); + if (rc) + goto out_fail; + + tcon->use_persistent = false; + /* check if SMB2 or later, CIFS does not support persistent handles */ + if (ctx->persistent) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB3 or later required for persistent handles\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else if (ses->server->capabilities & + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) + tcon->use_persistent = true; + else /* persistent handles requested but not supported */ { + cifs_dbg(VFS, + "Persistent handles not supported on share\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) + && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) + && (ctx->nopersistent == false)) { + cifs_dbg(FYI, "enabling persistent handles\n"); + tcon->use_persistent = true; + } else if (ctx->resilient) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB2.1 or later required for resilient handles\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + tcon->use_resilient = true; + } + + tcon->use_witness = false; + if (IS_ENABLED(CONFIG_CIFS_SWN_UPCALL) && ctx->witness) { + if (ses->server->vals->protocol_id >= SMB30_PROT_ID) { + if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) { + /* + * Set witness in use flag in first place + * to retry registration in the echo task + */ + tcon->use_witness = true; + /* And try to register immediately */ + rc = cifs_swn_register(tcon); + if (rc < 0) { + cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc); + goto out_fail; + } + } else { + /* TODO: try to extend for non-cluster uses (eg multichannel) */ + cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } else { + cifs_dbg(VFS, "SMB3 or later required for witness option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } + + /* If the user really knows what they are doing they can override */ + if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) { + if (ctx->cache_ro) + cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n"); + else if (ctx->cache_rw) + cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n"); + } + + if (ctx->no_lease) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB2 or later required for nolease option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else + tcon->no_lease = ctx->no_lease; + } + + /* + * We can have only one retry value for a connection to a share so for + * resources mounted more than once to the same server share the last + * value passed in for the retry flag is used. + */ + tcon->retry = ctx->retry; + tcon->nocase = ctx->nocase; + tcon->broken_sparse_sup = ctx->no_sparse; + if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) + tcon->nohandlecache = ctx->nohandlecache; + else + tcon->nohandlecache = true; + tcon->nodelete = ctx->nodelete; + tcon->local_lease = ctx->local_lease; + INIT_LIST_HEAD(&tcon->pending_opens); + tcon->status = TID_GOOD; + + INIT_DELAYED_WORK(&tcon->query_interfaces, + smb2_query_server_interfaces); + if (ses->server->dialect >= SMB30_PROT_ID && + (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { + /* schedule query interfaces poll */ + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); + } + + spin_lock(&cifs_tcp_ses_lock); + list_add(&tcon->tcon_list, &ses->tcon_list); + spin_unlock(&cifs_tcp_ses_lock); + + return tcon; + +out_fail: + tconInfoFree(tcon); + return ERR_PTR(rc); +} + +void +cifs_put_tlink(struct tcon_link *tlink) +{ + if (!tlink || IS_ERR(tlink)) + return; + + if (!atomic_dec_and_test(&tlink->tl_count) || + test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { + tlink->tl_time = jiffies; + return; + } + + if (!IS_ERR(tlink_tcon(tlink))) + cifs_put_tcon(tlink_tcon(tlink)); + kfree(tlink); + return; +} + +static int +compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) +{ + struct cifs_sb_info *old = CIFS_SB(sb); + struct cifs_sb_info *new = mnt_data->cifs_sb; + unsigned int oldflags = old->mnt_cifs_flags & CIFS_MOUNT_MASK; + unsigned int newflags = new->mnt_cifs_flags & CIFS_MOUNT_MASK; + + if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK)) + return 0; + + if (old->mnt_cifs_serverino_autodisabled) + newflags &= ~CIFS_MOUNT_SERVER_INUM; + + if (oldflags != newflags) + return 0; + + /* + * We want to share sb only if we don't specify an r/wsize or + * specified r/wsize is greater than or equal to existing one. + */ + if (new->ctx->wsize && new->ctx->wsize < old->ctx->wsize) + return 0; + + if (new->ctx->rsize && new->ctx->rsize < old->ctx->rsize) + return 0; + + if (!uid_eq(old->ctx->linux_uid, new->ctx->linux_uid) || + !gid_eq(old->ctx->linux_gid, new->ctx->linux_gid)) + return 0; + + if (old->ctx->file_mode != new->ctx->file_mode || + old->ctx->dir_mode != new->ctx->dir_mode) + return 0; + + if (strcmp(old->local_nls->charset, new->local_nls->charset)) + return 0; + + if (old->ctx->acregmax != new->ctx->acregmax) + return 0; + if (old->ctx->acdirmax != new->ctx->acdirmax) + return 0; + if (old->ctx->closetimeo != new->ctx->closetimeo) + return 0; + + return 1; +} + +static int +match_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data) +{ + struct cifs_sb_info *old = CIFS_SB(sb); + struct cifs_sb_info *new = mnt_data->cifs_sb; + bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + old->prepath; + bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + new->prepath; + + if (old_set && new_set && !strcmp(new->prepath, old->prepath)) + return 1; + else if (!old_set && !new_set) + return 1; + + return 0; +} + +int +cifs_match_super(struct super_block *sb, void *data) +{ + struct cifs_mnt_data *mnt_data = data; + struct smb3_fs_context *ctx; + struct cifs_sb_info *cifs_sb; + struct TCP_Server_Info *tcp_srv; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + int rc = 0; + + spin_lock(&cifs_tcp_ses_lock); + cifs_sb = CIFS_SB(sb); + + /* We do not want to use a superblock that has been shutdown */ + if (CIFS_MOUNT_SHUTDOWN & cifs_sb->mnt_cifs_flags) { + spin_unlock(&cifs_tcp_ses_lock); + return 0; + } + + tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); + if (tlink == NULL) { + /* can not match superblock if tlink were ever null */ + spin_unlock(&cifs_tcp_ses_lock); + return 0; + } + tcon = tlink_tcon(tlink); + ses = tcon->ses; + tcp_srv = ses->server; + + ctx = mnt_data->ctx; + + spin_lock(&tcp_srv->srv_lock); + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + spin_lock(&tcon->tc_lock); + if (!match_server(tcp_srv, ctx) || + !match_session(ses, ctx) || + !match_tcon(tcon, ctx) || + !match_prepath(sb, mnt_data)) { + rc = 0; + goto out; + } + + rc = compare_mount_options(sb, mnt_data); +out: + spin_unlock(&tcon->tc_lock); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + spin_unlock(&tcp_srv->srv_lock); + + spin_unlock(&cifs_tcp_ses_lock); + cifs_put_tlink(tlink); + return rc; +} + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +static struct lock_class_key cifs_key[2]; +static struct lock_class_key cifs_slock_key[2]; + +static inline void +cifs_reclassify_socket4(struct socket *sock) +{ + struct sock *sk = sock->sk; + BUG_ON(!sock_allow_reclassification(sk)); + sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS", + &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]); +} + +static inline void +cifs_reclassify_socket6(struct socket *sock) +{ + struct sock *sk = sock->sk; + BUG_ON(!sock_allow_reclassification(sk)); + sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS", + &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]); +} +#else +static inline void +cifs_reclassify_socket4(struct socket *sock) +{ +} + +static inline void +cifs_reclassify_socket6(struct socket *sock) +{ +} +#endif + +/* See RFC1001 section 14 on representation of Netbios names */ +static void rfc1002mangle(char *target, char *source, unsigned int length) +{ + unsigned int i, j; + + for (i = 0, j = 0; i < (length); i++) { + /* mask a nibble at a time and encode */ + target[j] = 'A' + (0x0F & (source[i] >> 4)); + target[j+1] = 'A' + (0x0F & source[i]); + j += 2; + } + +} + +static int +bind_socket(struct TCP_Server_Info *server) +{ + int rc = 0; + if (server->srcaddr.ss_family != AF_UNSPEC) { + /* Bind to the specified local IP address */ + struct socket *socket = server->ssocket; + rc = socket->ops->bind(socket, + (struct sockaddr *) &server->srcaddr, + sizeof(server->srcaddr)); + if (rc < 0) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; + saddr4 = (struct sockaddr_in *)&server->srcaddr; + saddr6 = (struct sockaddr_in6 *)&server->srcaddr; + if (saddr6->sin6_family == AF_INET6) + cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", + &saddr6->sin6_addr, rc); + else + cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", + &saddr4->sin_addr.s_addr, rc); + } + } + return rc; +} + +static int +ip_rfc1001_connect(struct TCP_Server_Info *server) +{ + int rc = 0; + /* + * some servers require RFC1001 sessinit before sending + * negprot - BB check reconnection in case where second + * sessinit is sent but no second negprot + */ + struct rfc1002_session_packet req = {}; + struct smb_hdr *smb_buf = (struct smb_hdr *)&req; + unsigned int len; + + req.trailer.session_req.called_len = sizeof(req.trailer.session_req.called_name); + + if (server->server_RFC1001_name[0] != 0) + rfc1002mangle(req.trailer.session_req.called_name, + server->server_RFC1001_name, + RFC1001_NAME_LEN_WITH_NULL); + else + rfc1002mangle(req.trailer.session_req.called_name, + DEFAULT_CIFS_CALLED_NAME, + RFC1001_NAME_LEN_WITH_NULL); + + req.trailer.session_req.calling_len = sizeof(req.trailer.session_req.calling_name); + + /* calling name ends in null (byte 16) from old smb convention */ + if (server->workstation_RFC1001_name[0] != 0) + rfc1002mangle(req.trailer.session_req.calling_name, + server->workstation_RFC1001_name, + RFC1001_NAME_LEN_WITH_NULL); + else + rfc1002mangle(req.trailer.session_req.calling_name, + "LINUX_CIFS_CLNT", + RFC1001_NAME_LEN_WITH_NULL); + + /* + * As per rfc1002, @len must be the number of bytes that follows the + * length field of a rfc1002 session request payload. + */ + len = sizeof(req) - offsetof(struct rfc1002_session_packet, trailer.session_req); + + smb_buf->smb_buf_length = cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | len); + rc = smb_send(server, smb_buf, len); + /* + * RFC1001 layer in at least one server requires very short break before + * negprot presumably because not expecting negprot to follow so fast. + * This is a simple solution that works without complicating the code + * and causes no significant slowing down on mount for everyone else + */ + usleep_range(1000, 2000); + + return rc; +} + +static int +generic_ip_connect(struct TCP_Server_Info *server) +{ + int rc = 0; + __be16 sport; + int slen, sfamily; + struct socket *socket = server->ssocket; + struct sockaddr *saddr; + + saddr = (struct sockaddr *) &server->dstaddr; + + if (server->dstaddr.ss_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&server->dstaddr; + + sport = ipv6->sin6_port; + slen = sizeof(struct sockaddr_in6); + sfamily = AF_INET6; + cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr, + ntohs(sport)); + } else { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&server->dstaddr; + + sport = ipv4->sin_port; + slen = sizeof(struct sockaddr_in); + sfamily = AF_INET; + cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr, + ntohs(sport)); + } + + if (socket == NULL) { + rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM, + IPPROTO_TCP, &socket, 1); + if (rc < 0) { + cifs_server_dbg(VFS, "Error %d creating socket\n", rc); + server->ssocket = NULL; + return rc; + } + + /* BB other socket options to set KEEPALIVE, NODELAY? */ + cifs_dbg(FYI, "Socket created\n"); + server->ssocket = socket; + socket->sk->sk_allocation = GFP_NOFS; + if (sfamily == AF_INET6) + cifs_reclassify_socket6(socket); + else + cifs_reclassify_socket4(socket); + } + + rc = bind_socket(server); + if (rc < 0) + return rc; + + /* + * Eventually check for other socket options to change from + * the default. sock_setsockopt not used because it expects + * user space buffer + */ + socket->sk->sk_rcvtimeo = 7 * HZ; + socket->sk->sk_sndtimeo = 5 * HZ; + + /* make the bufsizes depend on wsize/rsize and max requests */ + if (server->noautotune) { + if (socket->sk->sk_sndbuf < (200 * 1024)) + socket->sk->sk_sndbuf = 200 * 1024; + if (socket->sk->sk_rcvbuf < (140 * 1024)) + socket->sk->sk_rcvbuf = 140 * 1024; + } + + if (server->tcp_nodelay) + tcp_sock_set_nodelay(socket->sk); + + cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n", + socket->sk->sk_sndbuf, + socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo); + + rc = socket->ops->connect(socket, saddr, slen, + server->noblockcnt ? O_NONBLOCK : 0); + /* + * When mounting SMB root file systems, we do not want to block in + * connect. Otherwise bail out and then let cifs_reconnect() perform + * reconnect failover - if possible. + */ + if (server->noblockcnt && rc == -EINPROGRESS) + rc = 0; + if (rc < 0) { + cifs_dbg(FYI, "Error %d connecting to server\n", rc); + trace_smb3_connect_err(server->hostname, server->conn_id, &server->dstaddr, rc); + sock_release(socket); + server->ssocket = NULL; + return rc; + } + trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr); + if (sport == htons(RFC1001_PORT)) + rc = ip_rfc1001_connect(server); + + return rc; +} + +static int +ip_connect(struct TCP_Server_Info *server) +{ + __be16 *sport; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; + struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; + + if (server->dstaddr.ss_family == AF_INET6) + sport = &addr6->sin6_port; + else + sport = &addr->sin_port; + + if (*sport == 0) { + int rc; + + /* try with 445 port at first */ + *sport = htons(CIFS_PORT); + + rc = generic_ip_connect(server); + if (rc >= 0) + return rc; + + /* if it failed, try with 139 port */ + *sport = htons(RFC1001_PORT); + } + + return generic_ip_connect(server); +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + /* + * If we are reconnecting then should we check to see if + * any requested capabilities changed locally e.g. via + * remount but we can not do much about it here + * if they have (even if we could detect it by the following) + * Perhaps we could add a backpointer to array of sb from tcon + * or if we change to make all sb to same share the same + * sb as NFS - then we only have one backpointer to sb. + * What if we wanted to mount the server share twice once with + * and once without posixacls or posix paths? + */ + __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + + if (ctx && ctx->no_linux_ext) { + tcon->fsUnixInfo.Capability = 0; + tcon->unix_ext = 0; /* Unix Extensions disabled */ + cifs_dbg(FYI, "Linux protocol extensions disabled\n"); + return; + } else if (ctx) + tcon->unix_ext = 1; /* Unix Extensions supported */ + + if (!tcon->unix_ext) { + cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n"); + return; + } + + if (!CIFSSMBQFSUnixInfo(xid, tcon)) { + __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); + /* + * check for reconnect case in which we do not + * want to change the mount behavior if we can avoid it + */ + if (ctx == NULL) { + /* + * turn off POSIX ACL and PATHNAMES if not set + * originally at mount time + */ + if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { + if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cifs_dbg(VFS, "POSIXPATH support change\n"); + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { + cifs_dbg(VFS, "possible reconnect error\n"); + cifs_dbg(VFS, "server disabled POSIX path support\n"); + } + } + + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cifs_dbg(VFS, "per-share encryption not supported yet\n"); + + cap &= CIFS_UNIX_CAP_MASK; + if (ctx && ctx->no_psx_acl) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { + cifs_dbg(FYI, "negotiated posix acl support\n"); + if (cifs_sb) + cifs_sb->mnt_cifs_flags |= + CIFS_MOUNT_POSIXACL; + } + + if (ctx && ctx->posix_paths == 0) + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + cifs_dbg(FYI, "negotiate posix pathnames\n"); + if (cifs_sb) + cifs_sb->mnt_cifs_flags |= + CIFS_MOUNT_POSIX_PATHS; + } + + cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap); +#ifdef CONFIG_CIFS_DEBUG2 + if (cap & CIFS_UNIX_FCNTL_CAP) + cifs_dbg(FYI, "FCNTL cap\n"); + if (cap & CIFS_UNIX_EXTATTR_CAP) + cifs_dbg(FYI, "EXTATTR cap\n"); + if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cifs_dbg(FYI, "POSIX path cap\n"); + if (cap & CIFS_UNIX_XATTR_CAP) + cifs_dbg(FYI, "XATTR cap\n"); + if (cap & CIFS_UNIX_POSIX_ACL_CAP) + cifs_dbg(FYI, "POSIX ACL cap\n"); + if (cap & CIFS_UNIX_LARGE_READ_CAP) + cifs_dbg(FYI, "very large read cap\n"); + if (cap & CIFS_UNIX_LARGE_WRITE_CAP) + cifs_dbg(FYI, "very large write cap\n"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) + cifs_dbg(FYI, "transport encryption cap\n"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cifs_dbg(FYI, "mandatory transport encryption cap\n"); +#endif /* CIFS_DEBUG2 */ + if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { + if (ctx == NULL) + cifs_dbg(FYI, "resetting capabilities failed\n"); + else + cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n"); + + } + } +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) +{ + struct smb3_fs_context *ctx = cifs_sb->ctx; + + INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); + + spin_lock_init(&cifs_sb->tlink_tree_lock); + cifs_sb->tlink_tree = RB_ROOT; + + cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", + ctx->file_mode, ctx->dir_mode); + + /* this is needed for ASCII cp to Unicode converts */ + if (ctx->iocharset == NULL) { + /* load_nls_default cannot return null */ + cifs_sb->local_nls = load_nls_default(); + } else { + cifs_sb->local_nls = load_nls(ctx->iocharset); + if (cifs_sb->local_nls == NULL) { + cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n", + ctx->iocharset); + return -ELIBACC; + } + } + ctx->local_nls = cifs_sb->local_nls; + + smb3_update_mnt_flags(cifs_sb); + + if (ctx->direct_io) + cifs_dbg(FYI, "mounting share using direct i/o\n"); + if (ctx->cache_ro) { + cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n"); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE; + } else if (ctx->cache_rw) { + cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n"); + cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE | + CIFS_MOUNT_RW_CACHE); + } + + if ((ctx->cifs_acl) && (ctx->dynperm)) + cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n"); + + if (ctx->prepath) { + cifs_sb->prepath = kstrdup(ctx->prepath, GFP_KERNEL); + if (cifs_sb->prepath == NULL) + return -ENOMEM; + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + } + + return 0; +} + +/* Release all succeed connections */ +static inline void mount_put_conns(struct mount_ctx *mnt_ctx) +{ + int rc = 0; + + if (mnt_ctx->tcon) + cifs_put_tcon(mnt_ctx->tcon); + else if (mnt_ctx->ses) + cifs_put_smb_ses(mnt_ctx->ses); + else if (mnt_ctx->server) + cifs_put_tcp_session(mnt_ctx->server, 0); + mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; + free_xid(mnt_ctx->xid); +} + +/* Get connections for tcp, ses and tcon */ +static int mount_get_conns(struct mount_ctx *mnt_ctx) +{ + int rc = 0; + struct TCP_Server_Info *server = NULL; + struct cifs_ses *ses = NULL; + struct cifs_tcon *tcon = NULL; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + unsigned int xid; + + xid = get_xid(); + + /* get a reference to a tcp session */ + server = cifs_get_tcp_session(ctx, NULL); + if (IS_ERR(server)) { + rc = PTR_ERR(server); + server = NULL; + goto out; + } + + /* get a reference to a SMB session */ + ses = cifs_get_smb_ses(server, ctx); + if (IS_ERR(ses)) { + rc = PTR_ERR(ses); + ses = NULL; + goto out; + } + + if ((ctx->persistent == true) && (!(ses->server->capabilities & + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { + cifs_server_dbg(VFS, "persistent handles not supported by server\n"); + rc = -EOPNOTSUPP; + goto out; + } + + /* search for existing tcon to this server share */ + tcon = cifs_get_tcon(ses, ctx); + if (IS_ERR(tcon)) { + rc = PTR_ERR(tcon); + tcon = NULL; + goto out; + } + + /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ + if (tcon->posix_extensions) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* tell server which Unix caps we support */ + if (cap_unix(tcon->ses)) { + /* + * reset of caps checks mount to see if unix extensions disabled + * for just this mount. + */ + reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx); + spin_lock(&tcon->ses->server->srv_lock); + if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && + (le64_to_cpu(tcon->fsUnixInfo.Capability) & + CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { + spin_unlock(&tcon->ses->server->srv_lock); + rc = -EACCES; + goto out; + } + spin_unlock(&tcon->ses->server->srv_lock); + } else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + tcon->unix_ext = 0; /* server does not support them */ + + /* do not care if a following call succeed - informational */ + if (!tcon->pipe && server->ops->qfs_tcon) { + server->ops->qfs_tcon(xid, tcon, cifs_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { + if (tcon->fsDevInfo.DeviceCharacteristics & + cpu_to_le32(FILE_READ_ONLY_DEVICE)) + cifs_dbg(VFS, "mounted to read only share\n"); + else if ((cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_RW_CACHE) == 0) + cifs_dbg(VFS, "read only mount of RW share\n"); + /* no need to log a RW mount of a typical RW share */ + } + } + + /* + * Clamp the rsize/wsize mount arguments if they are too big for the server + * and set the rsize/wsize to the negotiated values if not passed in by + * the user on mount + */ + if ((cifs_sb->ctx->wsize == 0) || + (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) + cifs_sb->ctx->wsize = server->ops->negotiate_wsize(tcon, ctx); + if ((cifs_sb->ctx->rsize == 0) || + (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) + cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); + + /* + * The cookie is initialized from volume info returned above. + * Inside cifs_fscache_get_super_cookie it checks + * that we do not get super cookie twice. + */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) + cifs_fscache_get_super_cookie(tcon); + +out: + mnt_ctx->server = server; + mnt_ctx->ses = ses; + mnt_ctx->tcon = tcon; + mnt_ctx->xid = xid; + + return rc; +} + +static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, + struct cifs_tcon *tcon) +{ + struct tcon_link *tlink; + + /* hang the tcon off of the superblock */ + tlink = kzalloc(sizeof(*tlink), GFP_KERNEL); + if (tlink == NULL) + return -ENOMEM; + + tlink->tl_uid = ses->linux_uid; + tlink->tl_tcon = tcon; + tlink->tl_time = jiffies; + set_bit(TCON_LINK_MASTER, &tlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + + cifs_sb->master_tlink = tlink; + spin_lock(&cifs_sb->tlink_tree_lock); + tlink_rb_insert(&cifs_sb->tlink_tree, tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); + return 0; +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +/* Get unique dfs connections */ +static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx) +{ + int rc; + + mnt_ctx->fs_ctx->nosharesock = true; + rc = mount_get_conns(mnt_ctx); + if (mnt_ctx->server) { + cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__); + spin_lock(&mnt_ctx->server->srv_lock); + mnt_ctx->server->is_dfs_conn = true; + spin_unlock(&mnt_ctx->server->srv_lock); + } + return rc; +} + +/* + * cifs_build_path_to_root returns full path to root when we do not have an + * existing connection (tcon) + */ +static char * +build_unc_path_to_root(const struct smb3_fs_context *ctx, + const struct cifs_sb_info *cifs_sb, bool useppath) +{ + char *full_path, *pos; + unsigned int pplen = useppath && ctx->prepath ? + strlen(ctx->prepath) + 1 : 0; + unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); + + if (unc_len > MAX_TREE_SIZE) + return ERR_PTR(-EINVAL); + + full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); + if (full_path == NULL) + return ERR_PTR(-ENOMEM); + + memcpy(full_path, ctx->UNC, unc_len); + pos = full_path + unc_len; + + if (pplen) { + *pos = CIFS_DIR_SEP(cifs_sb); + memcpy(pos + 1, ctx->prepath, pplen); + pos += pplen; + } + + *pos = '\0'; /* add trailing null */ + convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); + cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); + return full_path; +} + +/* + * expand_dfs_referral - Update cifs_sb from dfs referral path + * + * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the + * submount. Otherwise it will be left untouched. + */ +static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path, + struct dfs_info3_param *referral) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *fake_devname = NULL, *mdata = NULL; + + mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral, + &fake_devname); + if (IS_ERR(mdata)) { + rc = PTR_ERR(mdata); + mdata = NULL; + } else { + /* + * We can not clear out the whole structure since we no longer have an explicit + * function to parse a mount-string. Instead we need to clear out the individual + * fields that are no longer valid. + */ + kfree(ctx->prepath); + ctx->prepath = NULL; + rc = cifs_setup_volume_info(ctx, mdata, fake_devname); + } + kfree(fake_devname); + kfree(cifs_sb->ctx->mount_options); + cifs_sb->ctx->mount_options = mdata; + + return rc; +} +#endif + +/* TODO: all callers to this are broken. We are not parsing mount_options here + * we should pass a clone of the original context? + */ +int +cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname) +{ + int rc; + + if (devname) { + cifs_dbg(FYI, "%s: devname=%s\n", __func__, devname); + rc = smb3_parse_devname(devname, ctx); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse %s: %d\n", __func__, devname, rc); + return rc; + } + } + + if (mntopts) { + char *ip; + + rc = smb3_parse_opt(mntopts, "ip", &ip); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse ip options: %d\n", __func__, rc); + return rc; + } + + rc = cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip)); + kfree(ip); + if (!rc) { + cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__); + return -EINVAL; + } + } + + if (ctx->nullauth) { + cifs_dbg(FYI, "Anonymous login\n"); + kfree(ctx->username); + ctx->username = NULL; + } else if (ctx->username) { + /* BB fixme parse for domain name here */ + cifs_dbg(FYI, "Username: %s\n", ctx->username); + } else { + cifs_dbg(VFS, "No username specified\n"); + /* In userspace mount helper we can get user name from alternate + locations such as env variables and files on disk */ + return -EINVAL; + } + + return 0; +} + +static int +cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, + unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + char *full_path, + int added_treename) +{ + int rc; + char *s; + char sep, tmp; + int skip = added_treename ? 1 : 0; + + sep = CIFS_DIR_SEP(cifs_sb); + s = full_path; + + rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, ""); + while (rc == 0) { + /* skip separators */ + while (*s == sep) + s++; + if (!*s) + break; + /* next separator */ + while (*s && *s != sep) + s++; + /* + * if the treename is added, we then have to skip the first + * part within the separators + */ + if (skip) { + skip = 0; + continue; + } + /* + * temporarily null-terminate the path at the end of + * the current component + */ + tmp = *s; + *s = 0; + rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, + full_path); + *s = tmp; + } + return rc; +} + +/* + * Check if path is remote (i.e. a DFS share). + * + * Return -EREMOTE if it is, otherwise 0 or -errno. + */ +static int is_path_remote(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct TCP_Server_Info *server = mnt_ctx->server; + unsigned int xid = mnt_ctx->xid; + struct cifs_tcon *tcon = mnt_ctx->tcon; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + + if (!server->ops->is_path_accessible) + return -EOPNOTSUPP; + + /* + * cifs_build_path_to_root works only when we have a valid tcon + */ + full_path = cifs_build_path_to_root(ctx, cifs_sb, tcon, + tcon->Flags & SMB_SHARE_IS_IN_DFS); + if (full_path == NULL) + return -ENOMEM; + + cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); + + rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, + full_path); + if (rc != 0 && rc != -EREMOTE) + goto out; + + if (rc != -EREMOTE) { + rc = cifs_are_all_path_components_accessible(server, xid, tcon, + cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS); + if (rc != 0) { + cifs_server_dbg(VFS, "cannot query dirs between root and final path, enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + rc = 0; + } + } + +out: + kfree(full_path); + return rc; +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +static void set_root_ses(struct mount_ctx *mnt_ctx) +{ + if (mnt_ctx->ses) { + spin_lock(&cifs_tcp_ses_lock); + mnt_ctx->ses->ses_count++; + spin_unlock(&cifs_tcp_ses_lock); + dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); + } + mnt_ctx->root_ses = mnt_ctx->ses; +} + +static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + + *isdfs = true; + + rc = mount_get_conns(mnt_ctx); + /* + * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally + * try to get an DFS referral (even cached) to determine whether it is an DFS mount. + * + * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem + * to respond with PATH_NOT_COVERED to requests that include the prefix. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || + dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), + ctx->UNC + 1, NULL, root_tl)) { + if (rc) + return rc; + /* Check if it is fully accessible and then mount it */ + rc = is_path_remote(mnt_ctx); + if (!rc) + *isdfs = false; + else if (rc != -EREMOTE) + return rc; + } + return 0; +} + +static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path, + const char *ref_path, struct dfs_cache_tgt_iterator *tit) +{ + int rc; + struct dfs_info3_param ref = {}; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + char *oldmnt = cifs_sb->ctx->mount_options; + + cifs_dbg(FYI, "%s: full_path=%s ref_path=%s target=%s\n", __func__, full_path, ref_path, + dfs_cache_get_tgt_name(tit)); + + rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref); + if (rc) + goto out; + + rc = expand_dfs_referral(mnt_ctx, full_path, &ref); + if (rc) + goto out; + + /* Connect to new target only if we were redirected (e.g. mount options changed) */ + if (oldmnt != cifs_sb->ctx->mount_options) { + mount_put_conns(mnt_ctx); + rc = mount_get_dfs_conns(mnt_ctx); + } + if (!rc) { + if (cifs_is_referral_server(mnt_ctx->tcon, &ref)) + set_root_ses(mnt_ctx); + rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, + cifs_remap(cifs_sb), ref_path, tit); + } + +out: + free_dfs_info_param(&ref); + return rc; +} + +static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl) +{ + int rc; + char *full_path; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct dfs_cache_tgt_iterator *tit; + + /* Put initial connections as they might be shared with other mounts. We need unique dfs + * connections per mount to properly failover, so mount_get_dfs_conns() must be used from + * now on. + */ + mount_put_conns(mnt_ctx); + mount_get_dfs_conns(mnt_ctx); + set_root_ses(mnt_ctx); + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (IS_ERR(mnt_ctx->origin_fullpath)) { + rc = PTR_ERR(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = NULL; + goto out; + } + + /* Try all dfs root targets */ + for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl); + tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) { + rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit); + if (!rc) { + mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL); + if (!mnt_ctx->leaf_fullpath) + rc = -ENOMEM; + break; + } + } + +out: + kfree(full_path); + return rc; +} + +static int __follow_dfs_link(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + struct dfs_cache_tgt_iterator *tit; + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + kfree(mnt_ctx->leaf_fullpath); + mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (IS_ERR(mnt_ctx->leaf_fullpath)) { + rc = PTR_ERR(mnt_ctx->leaf_fullpath); + mnt_ctx->leaf_fullpath = NULL; + goto out; + } + + /* Get referral from dfs link */ + rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, + cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl); + if (rc) + goto out; + + /* Try all dfs link targets. If an I/O fails from currently connected DFS target with an + * error other than STATUS_PATH_NOT_COVERED (-EREMOTE), then retry it from other targets as + * specified in MS-DFSC "3.1.5.2 I/O Operation to Target Fails with an Error Other Than + * STATUS_PATH_NOT_COVERED." + */ + for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl); + tit; tit = dfs_cache_get_next_tgt(&tl, tit)) { + rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit); + if (!rc) { + rc = is_path_remote(mnt_ctx); + if (!rc || rc == -EREMOTE) + break; + } + } + +out: + kfree(full_path); + dfs_cache_free_tgts(&tl); + return rc; +} + +static int follow_dfs_link(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + int num_links = 0; + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + kfree(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + kfree(full_path); + + if (IS_ERR(mnt_ctx->origin_fullpath)) { + rc = PTR_ERR(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = NULL; + return rc; + } + + do { + rc = __follow_dfs_link(mnt_ctx); + if (!rc || rc != -EREMOTE) + break; + } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); + + return rc; +} + +/* Set up DFS referral paths for failover */ +static void setup_server_referral_paths(struct mount_ctx *mnt_ctx) +{ + struct TCP_Server_Info *server = mnt_ctx->server; + + mutex_lock(&server->refpath_lock); + server->origin_fullpath = mnt_ctx->origin_fullpath; + server->leaf_fullpath = mnt_ctx->leaf_fullpath; + server->current_fullpath = mnt_ctx->leaf_fullpath; + mutex_unlock(&server->refpath_lock); + mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL; +} + +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + int rc; + struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + bool isdfs; + + rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl); + if (rc) + goto error; + if (!isdfs) + goto out; + + /* proceed as DFS mount */ + uuid_gen(&mnt_ctx.mount_id); + rc = connect_dfs_root(&mnt_ctx, &tl); + dfs_cache_free_tgts(&tl); + + if (rc) + goto error; + + rc = is_path_remote(&mnt_ctx); + if (rc) + rc = follow_dfs_link(&mnt_ctx); + if (rc) + goto error; + + setup_server_referral_paths(&mnt_ctx); + /* + * After reconnecting to a different server, unique ids won't match anymore, so we disable + * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). + */ + cifs_autodisable_serverino(cifs_sb); + /* + * Force the use of prefix path to support failover on DFS paths that resolve to targets + * that have different prefix paths. + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + kfree(cifs_sb->prepath); + cifs_sb->prepath = ctx->prepath; + ctx->prepath = NULL; + uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id); + +out: + cifs_try_adding_channels(cifs_sb, mnt_ctx.ses); + rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); + if (rc) + goto error; + + free_xid(mnt_ctx.xid); + return rc; + +error: + dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); + kfree(mnt_ctx.origin_fullpath); + kfree(mnt_ctx.leaf_fullpath); + mount_put_conns(&mnt_ctx); + return rc; +} +#else +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + int rc = 0; + struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + + rc = mount_get_conns(&mnt_ctx); + if (rc) + goto error; + + if (mnt_ctx.tcon) { + rc = is_path_remote(&mnt_ctx); + if (rc == -EREMOTE) + rc = -EOPNOTSUPP; + if (rc) + goto error; + } + + rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); + if (rc) + goto error; + + free_xid(mnt_ctx.xid); + return rc; + +error: + mount_put_conns(&mnt_ctx); + return rc; +} +#endif + +/* + * Issue a TREE_CONNECT request. + */ +int +CIFSTCon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *nls_codepage) +{ + struct smb_hdr *smb_buffer; + struct smb_hdr *smb_buffer_response; + TCONX_REQ *pSMB; + TCONX_RSP *pSMBr; + unsigned char *bcc_ptr; + int rc = 0; + int length; + __u16 bytes_left, count; + + if (ses == NULL) + return -EIO; + + smb_buffer = cifs_buf_get(); + if (smb_buffer == NULL) + return -ENOMEM; + + smb_buffer_response = smb_buffer; + + header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, + NULL /*no tid */ , 4 /*wct */ ); + + smb_buffer->Mid = get_next_mid(ses->server); + smb_buffer->Uid = ses->Suid; + pSMB = (TCONX_REQ *) smb_buffer; + pSMBr = (TCONX_RSP *) smb_buffer_response; + + pSMB->AndXCommand = 0xFF; + pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); + bcc_ptr = &pSMB->Password[0]; + + pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ + *bcc_ptr = 0; /* password is null byte */ + bcc_ptr++; /* skip password */ + /* already aligned so no need to do it below */ + + if (ses->server->sign) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + if (ses->capabilities & CAP_STATUS32) { + smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; + } + if (ses->capabilities & CAP_DFS) { + smb_buffer->Flags2 |= SMBFLG2_DFS; + } + if (ses->capabilities & CAP_UNICODE) { + smb_buffer->Flags2 |= SMBFLG2_UNICODE; + length = + cifs_strtoUTF16((__le16 *) bcc_ptr, tree, + 6 /* max utf8 char length in bytes */ * + (/* server len*/ + 256 /* share len */), nls_codepage); + bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ + bcc_ptr += 2; /* skip trailing null */ + } else { /* ASCII */ + strcpy(bcc_ptr, tree); + bcc_ptr += strlen(tree) + 1; + } + strcpy(bcc_ptr, "?????"); + bcc_ptr += strlen("?????"); + bcc_ptr += 1; + count = bcc_ptr - &pSMB->Password[0]; + be32_add_cpu(&pSMB->hdr.smb_buf_length, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, + 0); + + /* above now done in SendReceive */ + if (rc == 0) { + bool is_unicode; + + tcon->tid = smb_buffer_response->Tid; + bcc_ptr = pByteArea(smb_buffer_response); + bytes_left = get_bcc(smb_buffer_response); + length = strnlen(bcc_ptr, bytes_left - 2); + if (smb_buffer->Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; + + + /* skip service field (NB: this field is always ASCII) */ + if (length == 3) { + if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && + (bcc_ptr[2] == 'C')) { + cifs_dbg(FYI, "IPC connection\n"); + tcon->ipc = true; + tcon->pipe = true; + } + } else if (length == 2) { + if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) { + /* the most common case */ + cifs_dbg(FYI, "disk share connection\n"); + } + } + bcc_ptr += length + 1; + bytes_left -= (length + 1); + strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); + + /* mostly informational -- no need to fail on error here */ + kfree(tcon->nativeFileSystem); + tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr, + bytes_left, is_unicode, + nls_codepage); + + cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem); + + if ((smb_buffer_response->WordCount == 3) || + (smb_buffer_response->WordCount == 7)) + /* field is in same location */ + tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); + else + tcon->Flags = 0; + cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags); + } + + cifs_buf_release(smb_buffer); + return rc; +} + +static void delayed_free(struct rcu_head *p) +{ + struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, rcu); + + unload_nls(cifs_sb->local_nls); + smb3_cleanup_fs_context(cifs_sb->ctx); + kfree(cifs_sb); +} + +void +cifs_umount(struct cifs_sb_info *cifs_sb) +{ + struct rb_root *root = &cifs_sb->tlink_tree; + struct rb_node *node; + struct tcon_link *tlink; + + cancel_delayed_work_sync(&cifs_sb->prune_tlinks); + + spin_lock(&cifs_sb->tlink_tree_lock); + while ((node = rb_first(root))) { + tlink = rb_entry(node, struct tcon_link, tl_rbnode); + cifs_get_tlink(tlink); + clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + rb_erase(node, root); + + spin_unlock(&cifs_sb->tlink_tree_lock); + cifs_put_tlink(tlink); + spin_lock(&cifs_sb->tlink_tree_lock); + } + spin_unlock(&cifs_sb->tlink_tree_lock); + + kfree(cifs_sb->prepath); +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id); +#endif + call_rcu(&cifs_sb->rcu, delayed_free); +} + +int +cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + int rc = 0; + + if (!server->ops->need_neg || !server->ops->negotiate) + return -ENOSYS; + + /* only send once per connect */ + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsGood && + server->tcpStatus != CifsNew && + server->tcpStatus != CifsNeedNegotiate) { + spin_unlock(&server->srv_lock); + return -EHOSTDOWN; + } + + if (!server->ops->need_neg(server) && + server->tcpStatus == CifsGood) { + spin_unlock(&server->srv_lock); + return 0; + } + + server->tcpStatus = CifsInNegotiate; + spin_unlock(&server->srv_lock); + + rc = server->ops->negotiate(xid, ses, server); + if (rc == 0) { + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsInNegotiate) + server->tcpStatus = CifsGood; + else + rc = -EHOSTDOWN; + spin_unlock(&server->srv_lock); + } else { + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsInNegotiate) + server->tcpStatus = CifsNeedNegotiate; + spin_unlock(&server->srv_lock); + } + + return rc; +} + +int +cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct nls_table *nls_info) +{ + int rc = -ENOSYS; + struct TCP_Server_Info *pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr; + struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr; + bool is_binding = false; + + spin_lock(&ses->ses_lock); + cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", + __func__, ses->chans_need_reconnect); + + if (ses->ses_status != SES_GOOD && + ses->ses_status != SES_NEW && + ses->ses_status != SES_NEED_RECON) { + spin_unlock(&ses->ses_lock); + return -EHOSTDOWN; + } + + /* only send once per connect */ + spin_lock(&ses->chan_lock); + if (CIFS_ALL_CHANS_GOOD(ses)) { + if (ses->ses_status == SES_NEED_RECON) + ses->ses_status = SES_GOOD; + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + return 0; + } + + cifs_chan_set_in_reconnect(ses, server); + is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); + spin_unlock(&ses->chan_lock); + + if (!is_binding) + ses->ses_status = SES_IN_SETUP; + spin_unlock(&ses->ses_lock); + + /* update ses ip_addr only for primary chan */ + if (server == pserver) { + if (server->dstaddr.ss_family == AF_INET6) + scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr); + else + scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr); + } + + if (!is_binding) { + ses->capabilities = server->capabilities; + if (!linuxExtEnabled) + ses->capabilities &= (~server->vals->cap_unix); + + if (ses->auth_key.response) { + cifs_dbg(FYI, "Free previous auth_key.response = %p\n", + ses->auth_key.response); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + ses->auth_key.len = 0; + } + } + + cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", + server->sec_mode, server->capabilities, server->timeAdj); + + if (server->ops->sess_setup) + rc = server->ops->sess_setup(xid, ses, server, nls_info); + + if (rc) { + cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc); + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_IN_SETUP) + ses->ses_status = SES_NEED_RECON; + spin_lock(&ses->chan_lock); + cifs_chan_clear_in_reconnect(ses, server); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + } else { + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_IN_SETUP) + ses->ses_status = SES_GOOD; + spin_lock(&ses->chan_lock); + cifs_chan_clear_in_reconnect(ses, server); + cifs_chan_clear_need_reconnect(ses, server); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + } + + return rc; +} + +static int +cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) +{ + ctx->sectype = ses->sectype; + + /* krb5 is special, since we don't need username or pw */ + if (ctx->sectype == Kerberos) + return 0; + + return cifs_set_cifscreds(ctx, ses); +} + +static struct cifs_tcon * +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) +{ + int rc; + struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); + struct cifs_ses *ses; + struct cifs_tcon *tcon = NULL; + struct smb3_fs_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (ctx == NULL) + return ERR_PTR(-ENOMEM); + + ctx->local_nls = cifs_sb->local_nls; + ctx->linux_uid = fsuid; + ctx->cred_uid = fsuid; + ctx->UNC = master_tcon->tree_name; + ctx->retry = master_tcon->retry; + ctx->nocase = master_tcon->nocase; + ctx->nohandlecache = master_tcon->nohandlecache; + ctx->local_lease = master_tcon->local_lease; + ctx->no_lease = master_tcon->no_lease; + ctx->resilient = master_tcon->use_resilient; + ctx->persistent = master_tcon->use_persistent; + ctx->handle_timeout = master_tcon->handle_timeout; + ctx->no_linux_ext = !master_tcon->unix_ext; + ctx->linux_ext = master_tcon->posix_extensions; + ctx->sectype = master_tcon->ses->sectype; + ctx->sign = master_tcon->ses->sign; + ctx->seal = master_tcon->seal; + ctx->witness = master_tcon->use_witness; + + rc = cifs_set_vol_auth(ctx, master_tcon->ses); + if (rc) { + tcon = ERR_PTR(rc); + goto out; + } + + /* get a reference for the same TCP session */ + spin_lock(&cifs_tcp_ses_lock); + ++master_tcon->ses->server->srv_count; + spin_unlock(&cifs_tcp_ses_lock); + + ses = cifs_get_smb_ses(master_tcon->ses->server, ctx); + if (IS_ERR(ses)) { + tcon = (struct cifs_tcon *)ses; + cifs_put_tcp_session(master_tcon->ses->server, 0); + goto out; + } + + tcon = cifs_get_tcon(ses, ctx); + if (IS_ERR(tcon)) { + cifs_put_smb_ses(ses); + goto out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(ses)) + reset_cifs_unix_caps(0, tcon, NULL, ctx); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +out: + kfree(ctx->username); + kfree_sensitive(ctx->password); + kfree(ctx); + + return tcon; +} + +struct cifs_tcon * +cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) +{ + return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); +} + +/* find and return a tlink with given uid */ +static struct tcon_link * +tlink_rb_search(struct rb_root *root, kuid_t uid) +{ + struct rb_node *node = root->rb_node; + struct tcon_link *tlink; + + while (node) { + tlink = rb_entry(node, struct tcon_link, tl_rbnode); + + if (uid_gt(tlink->tl_uid, uid)) + node = node->rb_left; + else if (uid_lt(tlink->tl_uid, uid)) + node = node->rb_right; + else + return tlink; + } + return NULL; +} + +/* insert a tcon_link into the tree */ +static void +tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + struct tcon_link *tlink; + + while (*new) { + tlink = rb_entry(*new, struct tcon_link, tl_rbnode); + parent = *new; + + if (uid_gt(tlink->tl_uid, new_tlink->tl_uid)) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + } + + rb_link_node(&new_tlink->tl_rbnode, parent, new); + rb_insert_color(&new_tlink->tl_rbnode, root); +} + +/* + * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the + * current task. + * + * If the superblock doesn't refer to a multiuser mount, then just return + * the master tcon for the mount. + * + * First, search the rbtree for an existing tcon for this fsuid. If one + * exists, then check to see if it's pending construction. If it is then wait + * for construction to complete. Once it's no longer pending, check to see if + * it failed and either return an error or retry construction, depending on + * the timeout. + * + * If one doesn't exist then insert a new tcon_link struct into the tree and + * try to construct a new one. + */ +struct tcon_link * +cifs_sb_tlink(struct cifs_sb_info *cifs_sb) +{ + int ret; + kuid_t fsuid = current_fsuid(); + struct tcon_link *tlink, *newtlink; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); + + spin_lock(&cifs_sb->tlink_tree_lock); + tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); + if (tlink) + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + + if (tlink == NULL) { + newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); + if (newtlink == NULL) + return ERR_PTR(-ENOMEM); + newtlink->tl_uid = fsuid; + newtlink->tl_tcon = ERR_PTR(-EACCES); + set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); + cifs_get_tlink(newtlink); + + spin_lock(&cifs_sb->tlink_tree_lock); + /* was one inserted after previous search? */ + tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); + if (tlink) { + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + kfree(newtlink); + goto wait_for_construction; + } + tlink = newtlink; + tlink_rb_insert(&cifs_sb->tlink_tree, tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + } else { +wait_for_construction: + ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, + TASK_INTERRUPTIBLE); + if (ret) { + cifs_put_tlink(tlink); + return ERR_PTR(-ERESTARTSYS); + } + + /* if it's good, return it */ + if (!IS_ERR(tlink->tl_tcon)) + return tlink; + + /* return error if we tried this already recently */ + if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) + goto wait_for_construction; + } + + tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); + clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); + wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); + + if (IS_ERR(tlink->tl_tcon)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + return tlink; +} + +/* + * periodic workqueue job that scans tcon_tree for a superblock and closes + * out tcons. + */ +static void +cifs_prune_tlinks(struct work_struct *work) +{ + struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, + prune_tlinks.work); + struct rb_root *root = &cifs_sb->tlink_tree; + struct rb_node *node; + struct rb_node *tmp; + struct tcon_link *tlink; + + /* + * Because we drop the spinlock in the loop in order to put the tlink + * it's not guarded against removal of links from the tree. The only + * places that remove entries from the tree are this function and + * umounts. Because this function is non-reentrant and is canceled + * before umount can proceed, this is safe. + */ + spin_lock(&cifs_sb->tlink_tree_lock); + node = rb_first(root); + while (node != NULL) { + tmp = node; + node = rb_next(tmp); + tlink = rb_entry(tmp, struct tcon_link, tl_rbnode); + + if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) || + atomic_read(&tlink->tl_count) != 0 || + time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies)) + continue; + + cifs_get_tlink(tlink); + clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + rb_erase(tmp, root); + + spin_unlock(&cifs_sb->tlink_tree_lock); + cifs_put_tlink(tlink); + spin_lock(&cifs_sb->tlink_tree_lock); + } + spin_unlock(&cifs_sb->tlink_tree_lock); + + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +/* Update dfs referral path of superblock */ +static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, + const char *target) +{ + int rc = 0; + size_t len = strlen(target); + char *refpath, *npath; + + if (unlikely(len < 2 || *target != '\\')) + return -EINVAL; + + if (target[1] == '\\') { + len += 1; + refpath = kmalloc(len, GFP_KERNEL); + if (!refpath) + return -ENOMEM; + + scnprintf(refpath, len, "%s", target); + } else { + len += sizeof("\\"); + refpath = kmalloc(len, GFP_KERNEL); + if (!refpath) + return -ENOMEM; + + scnprintf(refpath, len, "\\%s", target); + } + + npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); + kfree(refpath); + + if (IS_ERR(npath)) { + rc = PTR_ERR(npath); + } else { + mutex_lock(&server->refpath_lock); + kfree(server->leaf_fullpath); + server->leaf_fullpath = npath; + mutex_unlock(&server->refpath_lock); + server->current_fullpath = server->leaf_fullpath; + } + return rc; +} + +static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host, + size_t tcp_host_len, char *share, bool *target_match) +{ + int rc = 0; + const char *dfs_host; + size_t dfs_host_len; + + *target_match = true; + extract_unc_hostname(share, &dfs_host, &dfs_host_len); + + /* Check if hostnames or addresses match */ + if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { + cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, + dfs_host, (int)tcp_host_len, tcp_host); + rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); + if (rc) + cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); + } + return rc; +} + +static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, char *tree, bool islink, + struct dfs_cache_tgt_list *tl) +{ + int rc; + struct TCP_Server_Info *server = tcon->ses->server; + const struct smb_version_operations *ops = server->ops; + struct cifs_tcon *ipc = tcon->ses->tcon_ipc; + char *share = NULL, *prefix = NULL; + const char *tcp_host; + size_t tcp_host_len; + struct dfs_cache_tgt_iterator *tit; + bool target_match; + + extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); + + tit = dfs_cache_get_tgt_iterator(tl); + if (!tit) { + rc = -ENOENT; + goto out; + } + + /* Try to tree connect to all dfs targets */ + for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { + const char *target = dfs_cache_get_tgt_name(tit); + struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); + + kfree(share); + kfree(prefix); + share = prefix = NULL; + + /* Check if share matches with tcp ses */ + rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); + break; + } + + rc = target_share_matches_server(server, tcp_host, tcp_host_len, share, + &target_match); + if (rc) + break; + if (!target_match) { + rc = -EHOSTUNREACH; + continue; + } + + if (ipc->need_reconnect) { + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); + rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls); + if (rc) + break; + } + + scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); + if (!islink) { + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); + break; + } + /* + * If no dfs referrals were returned from link target, then just do a TREE_CONNECT + * to it. Otherwise, cache the dfs referral and then mark current tcp ses for + * reconnect so either the demultiplex thread or the echo worker will reconnect to + * newly resolved target. + */ + if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, + NULL, &ntl)) { + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); + if (rc) + continue; + rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit); + if (!rc) + rc = cifs_update_super_prepath(cifs_sb, prefix); + } else { + /* Target is another dfs share */ + rc = update_server_fullpath(server, cifs_sb, target); + dfs_cache_free_tgts(tl); + + if (!rc) { + rc = -EREMOTE; + list_replace_init(&ntl.tl_list, &tl->tl_list); + } else + dfs_cache_free_tgts(&ntl); + } + break; + } + +out: + kfree(share); + kfree(prefix); + + return rc; +} + +static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, char *tree, bool islink, + struct dfs_cache_tgt_list *tl) +{ + int rc; + int num_links = 0; + struct TCP_Server_Info *server = tcon->ses->server; + + do { + rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); + if (!rc || rc != -EREMOTE) + break; + } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); + /* + * If we couldn't tree connect to any targets from last referral path, then retry from + * original referral path. + */ + if (rc && server->current_fullpath != server->origin_fullpath) { + server->current_fullpath = server->origin_fullpath; + cifs_signal_cifsd_for_reconnect(server, true); + } + + dfs_cache_free_tgts(tl); + return rc; +} + +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +{ + int rc; + struct TCP_Server_Info *server = tcon->ses->server; + const struct smb_version_operations *ops = server->ops; + struct super_block *sb = NULL; + struct cifs_sb_info *cifs_sb; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + char *tree; + struct dfs_info3_param ref = {0}; + + /* only send once per connect */ + spin_lock(&tcon->tc_lock); + if (tcon->ses->ses_status != SES_GOOD || + (tcon->status != TID_NEW && + tcon->status != TID_NEED_TCON)) { + spin_unlock(&tcon->tc_lock); + return 0; + } + tcon->status = TID_IN_TCON; + spin_unlock(&tcon->tc_lock); + + tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); + if (!tree) { + rc = -ENOMEM; + goto out; + } + + if (tcon->ipc) { + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); + goto out; + } + + sb = cifs_get_tcp_super(server); + if (IS_ERR(sb)) { + rc = PTR_ERR(sb); + cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); + goto out; + } + + cifs_sb = CIFS_SB(sb); + + /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ + if (!server->current_fullpath || + dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) { + rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls); + goto out; + } + + rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, + &tl); + free_dfs_info_param(&ref); + +out: + kfree(tree); + cifs_put_tcp_super(sb); + + if (rc) { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_NEED_TCON; + spin_unlock(&tcon->tc_lock); + } else { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_GOOD; + spin_unlock(&tcon->tc_lock); + tcon->need_reconnect = false; + } + + return rc; +} +#else +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +{ + int rc; + const struct smb_version_operations *ops = tcon->ses->server->ops; + + /* only send once per connect */ + spin_lock(&tcon->tc_lock); + if (tcon->ses->ses_status != SES_GOOD || + (tcon->status != TID_NEW && + tcon->status != TID_NEED_TCON)) { + spin_unlock(&tcon->tc_lock); + return 0; + } + tcon->status = TID_IN_TCON; + spin_unlock(&tcon->tc_lock); + + rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc); + if (rc) { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_NEED_TCON; + spin_unlock(&tcon->tc_lock); + } else { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_GOOD; + tcon->need_reconnect = false; + spin_unlock(&tcon->tc_lock); + } + + return rc; +} +#endif diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c new file mode 100644 index 000000000000..3bc1d3494be3 --- /dev/null +++ b/fs/smb/client/dfs_cache.c @@ -0,0 +1,1690 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DFS referral cache routines + * + * Copyright (c) 2018-2019 Paulo Alcantara + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2glob.h" +#include "dns_resolve.h" + +#include "dfs_cache.h" + +#define CACHE_HTABLE_SIZE 32 +#define CACHE_MAX_ENTRIES 64 +#define CACHE_MIN_TTL 120 /* 2 minutes */ + +#define IS_DFS_INTERLINK(v) (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER)) + +struct cache_dfs_tgt { + char *name; + int path_consumed; + struct list_head list; +}; + +struct cache_entry { + struct hlist_node hlist; + const char *path; + int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */ + int ttl; /* DFS_REREFERRAL_V3.TimeToLive */ + int srvtype; /* DFS_REREFERRAL_V3.ServerType */ + int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */ + struct timespec64 etime; + int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */ + int numtgts; + struct list_head tlist; + struct cache_dfs_tgt *tgthint; +}; + +/* List of referral server sessions per dfs mount */ +struct mount_group { + struct list_head list; + uuid_t id; + struct cifs_ses *sessions[CACHE_MAX_ENTRIES]; + int num_sessions; + spinlock_t lock; + struct list_head refresh_list; + struct kref refcount; +}; + +static struct kmem_cache *cache_slab __read_mostly; +static struct workqueue_struct *dfscache_wq __read_mostly; + +static int cache_ttl; +static DEFINE_SPINLOCK(cache_ttl_lock); + +static struct nls_table *cache_cp; + +/* + * Number of entries in the cache + */ +static atomic_t cache_count; + +static struct hlist_head cache_htable[CACHE_HTABLE_SIZE]; +static DECLARE_RWSEM(htable_rw_lock); + +static LIST_HEAD(mount_group_list); +static DEFINE_MUTEX(mount_group_list_lock); + +static void refresh_cache_worker(struct work_struct *work); + +static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker); + +static void get_ipc_unc(const char *ref_path, char *ipc, size_t ipclen) +{ + const char *host; + size_t len; + + extract_unc_hostname(ref_path, &host, &len); + scnprintf(ipc, ipclen, "\\\\%.*s\\IPC$", (int)len, host); +} + +static struct cifs_ses *find_ipc_from_server_path(struct cifs_ses **ses, const char *path) +{ + char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; + + get_ipc_unc(path, unc, sizeof(unc)); + for (; *ses; ses++) { + if (!strcasecmp(unc, (*ses)->tcon_ipc->tree_name)) + return *ses; + } + return ERR_PTR(-ENOENT); +} + +static void __mount_group_release(struct mount_group *mg) +{ + int i; + + for (i = 0; i < mg->num_sessions; i++) + cifs_put_smb_ses(mg->sessions[i]); + kfree(mg); +} + +static void mount_group_release(struct kref *kref) +{ + struct mount_group *mg = container_of(kref, struct mount_group, refcount); + + mutex_lock(&mount_group_list_lock); + list_del(&mg->list); + mutex_unlock(&mount_group_list_lock); + __mount_group_release(mg); +} + +static struct mount_group *find_mount_group_locked(const uuid_t *id) +{ + struct mount_group *mg; + + list_for_each_entry(mg, &mount_group_list, list) { + if (uuid_equal(&mg->id, id)) + return mg; + } + return ERR_PTR(-ENOENT); +} + +static struct mount_group *__get_mount_group_locked(const uuid_t *id) +{ + struct mount_group *mg; + + mg = find_mount_group_locked(id); + if (!IS_ERR(mg)) + return mg; + + mg = kmalloc(sizeof(*mg), GFP_KERNEL); + if (!mg) + return ERR_PTR(-ENOMEM); + kref_init(&mg->refcount); + uuid_copy(&mg->id, id); + mg->num_sessions = 0; + spin_lock_init(&mg->lock); + list_add(&mg->list, &mount_group_list); + return mg; +} + +static struct mount_group *get_mount_group(const uuid_t *id) +{ + struct mount_group *mg; + + mutex_lock(&mount_group_list_lock); + mg = __get_mount_group_locked(id); + if (!IS_ERR(mg)) + kref_get(&mg->refcount); + mutex_unlock(&mount_group_list_lock); + + return mg; +} + +static void free_mount_group_list(void) +{ + struct mount_group *mg, *tmp_mg; + + list_for_each_entry_safe(mg, tmp_mg, &mount_group_list, list) { + list_del_init(&mg->list); + __mount_group_release(mg); + } +} + +/** + * dfs_cache_canonical_path - get a canonical DFS path + * + * @path: DFS path + * @cp: codepage + * @remap: mapping type + * + * Return canonical path if success, otherwise error. + */ +char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap) +{ + char *tmp; + int plen = 0; + char *npath; + + if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/')) + return ERR_PTR(-EINVAL); + + if (unlikely(strcmp(cp->charset, cache_cp->charset))) { + tmp = (char *)cifs_strndup_to_utf16(path, strlen(path), &plen, cp, remap); + if (!tmp) { + cifs_dbg(VFS, "%s: failed to convert path to utf16\n", __func__); + return ERR_PTR(-EINVAL); + } + + npath = cifs_strndup_from_utf16(tmp, plen, true, cache_cp); + kfree(tmp); + + if (!npath) { + cifs_dbg(VFS, "%s: failed to convert path from utf16\n", __func__); + return ERR_PTR(-EINVAL); + } + } else { + npath = kstrdup(path, GFP_KERNEL); + if (!npath) + return ERR_PTR(-ENOMEM); + } + convert_delimiter(npath, '\\'); + return npath; +} + +static inline bool cache_entry_expired(const struct cache_entry *ce) +{ + struct timespec64 ts; + + ktime_get_coarse_real_ts64(&ts); + return timespec64_compare(&ts, &ce->etime) >= 0; +} + +static inline void free_tgts(struct cache_entry *ce) +{ + struct cache_dfs_tgt *t, *n; + + list_for_each_entry_safe(t, n, &ce->tlist, list) { + list_del(&t->list); + kfree(t->name); + kfree(t); + } +} + +static inline void flush_cache_ent(struct cache_entry *ce) +{ + hlist_del_init(&ce->hlist); + kfree(ce->path); + free_tgts(ce); + atomic_dec(&cache_count); + kmem_cache_free(cache_slab, ce); +} + +static void flush_cache_ents(void) +{ + int i; + + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + struct hlist_node *n; + struct cache_entry *ce; + + hlist_for_each_entry_safe(ce, n, l, hlist) { + if (!hlist_unhashed(&ce->hlist)) + flush_cache_ent(ce); + } + } +} + +/* + * dfs cache /proc file + */ +static int dfscache_proc_show(struct seq_file *m, void *v) +{ + int i; + struct cache_entry *ce; + struct cache_dfs_tgt *t; + + seq_puts(m, "DFS cache\n---------\n"); + + down_read(&htable_rw_lock); + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + + hlist_for_each_entry(ce, l, hlist) { + if (hlist_unhashed(&ce->hlist)) + continue; + + seq_printf(m, + "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", + ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", + ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, + IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", + ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); + + list_for_each_entry(t, &ce->tlist, list) { + seq_printf(m, " %s%s\n", + t->name, + ce->tgthint == t ? " (target hint)" : ""); + } + } + } + up_read(&htable_rw_lock); + + return 0; +} + +static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char c; + int rc; + + rc = get_user(c, buffer); + if (rc) + return rc; + + if (c != '0') + return -EINVAL; + + cifs_dbg(FYI, "clearing dfs cache\n"); + + down_write(&htable_rw_lock); + flush_cache_ents(); + up_write(&htable_rw_lock); + + return count; +} + +static int dfscache_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dfscache_proc_show, NULL); +} + +const struct proc_ops dfscache_proc_ops = { + .proc_open = dfscache_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = dfscache_proc_write, +}; + +#ifdef CONFIG_CIFS_DEBUG2 +static inline void dump_tgts(const struct cache_entry *ce) +{ + struct cache_dfs_tgt *t; + + cifs_dbg(FYI, "target list:\n"); + list_for_each_entry(t, &ce->tlist, list) { + cifs_dbg(FYI, " %s%s\n", t->name, + ce->tgthint == t ? " (target hint)" : ""); + } +} + +static inline void dump_ce(const struct cache_entry *ce) +{ + cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", + ce->path, + ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, + ce->etime.tv_nsec, + ce->hdr_flags, ce->ref_flags, + IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", + ce->path_consumed, + cache_entry_expired(ce) ? "yes" : "no"); + dump_tgts(ce); +} + +static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs) +{ + int i; + + cifs_dbg(FYI, "DFS referrals returned by the server:\n"); + for (i = 0; i < numrefs; i++) { + const struct dfs_info3_param *ref = &refs[i]; + + cifs_dbg(FYI, + "\n" + "flags: 0x%x\n" + "path_consumed: %d\n" + "server_type: 0x%x\n" + "ref_flag: 0x%x\n" + "path_name: %s\n" + "node_name: %s\n" + "ttl: %d (%dm)\n", + ref->flags, ref->path_consumed, ref->server_type, + ref->ref_flag, ref->path_name, ref->node_name, + ref->ttl, ref->ttl / 60); + } +} +#else +#define dump_tgts(e) +#define dump_ce(e) +#define dump_refs(r, n) +#endif + +/** + * dfs_cache_init - Initialize DFS referral cache. + * + * Return zero if initialized successfully, otherwise non-zero. + */ +int dfs_cache_init(void) +{ + int rc; + int i; + + dfscache_wq = alloc_workqueue("cifs-dfscache", WQ_FREEZABLE | WQ_UNBOUND, 1); + if (!dfscache_wq) + return -ENOMEM; + + cache_slab = kmem_cache_create("cifs_dfs_cache", + sizeof(struct cache_entry), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!cache_slab) { + rc = -ENOMEM; + goto out_destroy_wq; + } + + for (i = 0; i < CACHE_HTABLE_SIZE; i++) + INIT_HLIST_HEAD(&cache_htable[i]); + + atomic_set(&cache_count, 0); + cache_cp = load_nls("utf8"); + if (!cache_cp) + cache_cp = load_nls_default(); + + cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__); + return 0; + +out_destroy_wq: + destroy_workqueue(dfscache_wq); + return rc; +} + +static int cache_entry_hash(const void *data, int size, unsigned int *hash) +{ + int i, clen; + const unsigned char *s = data; + wchar_t c; + unsigned int h = 0; + + for (i = 0; i < size; i += clen) { + clen = cache_cp->char2uni(&s[i], size - i, &c); + if (unlikely(clen < 0)) { + cifs_dbg(VFS, "%s: can't convert char\n", __func__); + return clen; + } + c = cifs_toupper(c); + h = jhash(&c, sizeof(c), h); + } + *hash = h % CACHE_HTABLE_SIZE; + return 0; +} + +/* Return target hint of a DFS cache entry */ +static inline char *get_tgt_name(const struct cache_entry *ce) +{ + struct cache_dfs_tgt *t = ce->tgthint; + + return t ? t->name : ERR_PTR(-ENOENT); +} + +/* Return expire time out of a new entry's TTL */ +static inline struct timespec64 get_expire_time(int ttl) +{ + struct timespec64 ts = { + .tv_sec = ttl, + .tv_nsec = 0, + }; + struct timespec64 now; + + ktime_get_coarse_real_ts64(&now); + return timespec64_add(now, ts); +} + +/* Allocate a new DFS target */ +static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) +{ + struct cache_dfs_tgt *t; + + t = kmalloc(sizeof(*t), GFP_ATOMIC); + if (!t) + return ERR_PTR(-ENOMEM); + t->name = kstrdup(name, GFP_ATOMIC); + if (!t->name) { + kfree(t); + return ERR_PTR(-ENOMEM); + } + t->path_consumed = path_consumed; + INIT_LIST_HEAD(&t->list); + return t; +} + +/* + * Copy DFS referral information to a cache entry and conditionally update + * target hint. + */ +static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, + struct cache_entry *ce, const char *tgthint) +{ + int i; + + ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL); + ce->etime = get_expire_time(ce->ttl); + ce->srvtype = refs[0].server_type; + ce->hdr_flags = refs[0].flags; + ce->ref_flags = refs[0].ref_flag; + ce->path_consumed = refs[0].path_consumed; + + for (i = 0; i < numrefs; i++) { + struct cache_dfs_tgt *t; + + t = alloc_target(refs[i].node_name, refs[i].path_consumed); + if (IS_ERR(t)) { + free_tgts(ce); + return PTR_ERR(t); + } + if (tgthint && !strcasecmp(t->name, tgthint)) { + list_add(&t->list, &ce->tlist); + tgthint = NULL; + } else { + list_add_tail(&t->list, &ce->tlist); + } + ce->numtgts++; + } + + ce->tgthint = list_first_entry_or_null(&ce->tlist, + struct cache_dfs_tgt, list); + + return 0; +} + +/* Allocate a new cache entry */ +static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int numrefs) +{ + struct cache_entry *ce; + int rc; + + ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL); + if (!ce) + return ERR_PTR(-ENOMEM); + + ce->path = refs[0].path_name; + refs[0].path_name = NULL; + + INIT_HLIST_NODE(&ce->hlist); + INIT_LIST_HEAD(&ce->tlist); + + rc = copy_ref_data(refs, numrefs, ce, NULL); + if (rc) { + kfree(ce->path); + kmem_cache_free(cache_slab, ce); + ce = ERR_PTR(rc); + } + return ce; +} + +static void remove_oldest_entry_locked(void) +{ + int i; + struct cache_entry *ce; + struct cache_entry *to_del = NULL; + + WARN_ON(!rwsem_is_locked(&htable_rw_lock)); + + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + + hlist_for_each_entry(ce, l, hlist) { + if (hlist_unhashed(&ce->hlist)) + continue; + if (!to_del || timespec64_compare(&ce->etime, + &to_del->etime) < 0) + to_del = ce; + } + } + + if (!to_del) { + cifs_dbg(FYI, "%s: no entry to remove\n", __func__); + return; + } + + cifs_dbg(FYI, "%s: removing entry\n", __func__); + dump_ce(to_del); + flush_cache_ent(to_del); +} + +/* Add a new DFS cache entry */ +static int add_cache_entry_locked(struct dfs_info3_param *refs, int numrefs) +{ + int rc; + struct cache_entry *ce; + unsigned int hash; + + WARN_ON(!rwsem_is_locked(&htable_rw_lock)); + + if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { + cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES); + remove_oldest_entry_locked(); + } + + rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash); + if (rc) + return rc; + + ce = alloc_cache_entry(refs, numrefs); + if (IS_ERR(ce)) + return PTR_ERR(ce); + + spin_lock(&cache_ttl_lock); + if (!cache_ttl) { + cache_ttl = ce->ttl; + queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); + } else { + cache_ttl = min_t(int, cache_ttl, ce->ttl); + mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); + } + spin_unlock(&cache_ttl_lock); + + hlist_add_head(&ce->hlist, &cache_htable[hash]); + dump_ce(ce); + + atomic_inc(&cache_count); + + return 0; +} + +/* Check if two DFS paths are equal. @s1 and @s2 are expected to be in @cache_cp's charset */ +static bool dfs_path_equal(const char *s1, int len1, const char *s2, int len2) +{ + int i, l1, l2; + wchar_t c1, c2; + + if (len1 != len2) + return false; + + for (i = 0; i < len1; i += l1) { + l1 = cache_cp->char2uni(&s1[i], len1 - i, &c1); + l2 = cache_cp->char2uni(&s2[i], len2 - i, &c2); + if (unlikely(l1 < 0 && l2 < 0)) { + if (s1[i] != s2[i]) + return false; + l1 = 1; + continue; + } + if (l1 != l2) + return false; + if (cifs_toupper(c1) != cifs_toupper(c2)) + return false; + } + return true; +} + +static struct cache_entry *__lookup_cache_entry(const char *path, unsigned int hash, int len) +{ + struct cache_entry *ce; + + hlist_for_each_entry(ce, &cache_htable[hash], hlist) { + if (dfs_path_equal(ce->path, strlen(ce->path), path, len)) { + dump_ce(ce); + return ce; + } + } + return ERR_PTR(-ENOENT); +} + +/* + * Find a DFS cache entry in hash table and optionally check prefix path against normalized @path. + * + * Use whole path components in the match. Must be called with htable_rw_lock held. + * + * Return ERR_PTR(-ENOENT) if the entry is not found. + */ +static struct cache_entry *lookup_cache_entry(const char *path) +{ + struct cache_entry *ce; + int cnt = 0; + const char *s = path, *e; + char sep = *s; + unsigned int hash; + int rc; + + while ((s = strchr(s, sep)) && ++cnt < 3) + s++; + + if (cnt < 3) { + rc = cache_entry_hash(path, strlen(path), &hash); + if (rc) + return ERR_PTR(rc); + return __lookup_cache_entry(path, hash, strlen(path)); + } + /* + * Handle paths that have more than two path components and are a complete prefix of the DFS + * referral request path (@path). + * + * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request". + */ + e = path + strlen(path) - 1; + while (e > s) { + int len; + + /* skip separators */ + while (e > s && *e == sep) + e--; + if (e == s) + break; + + len = e + 1 - path; + rc = cache_entry_hash(path, len, &hash); + if (rc) + return ERR_PTR(rc); + ce = __lookup_cache_entry(path, hash, len); + if (!IS_ERR(ce)) + return ce; + + /* backward until separator */ + while (e > s && *e != sep) + e--; + } + return ERR_PTR(-ENOENT); +} + +/** + * dfs_cache_destroy - destroy DFS referral cache + */ +void dfs_cache_destroy(void) +{ + cancel_delayed_work_sync(&refresh_task); + unload_nls(cache_cp); + free_mount_group_list(); + flush_cache_ents(); + kmem_cache_destroy(cache_slab); + destroy_workqueue(dfscache_wq); + + cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__); +} + +/* Update a cache entry with the new referral in @refs */ +static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_info3_param *refs, + int numrefs) +{ + int rc; + char *s, *th = NULL; + + WARN_ON(!rwsem_is_locked(&htable_rw_lock)); + + if (ce->tgthint) { + s = ce->tgthint->name; + th = kstrdup(s, GFP_ATOMIC); + if (!th) + return -ENOMEM; + } + + free_tgts(ce); + ce->numtgts = 0; + + rc = copy_ref_data(refs, numrefs, ce, th); + + kfree(th); + + return rc; +} + +static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const char *path, + struct dfs_info3_param **refs, int *numrefs) +{ + int rc; + int i; + + cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path); + + *refs = NULL; + *numrefs = 0; + + if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) + return -EOPNOTSUPP; + if (unlikely(!cache_cp)) + return -EINVAL; + + rc = ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp, + NO_MAP_UNI_RSVD); + if (!rc) { + struct dfs_info3_param *ref = *refs; + + for (i = 0; i < *numrefs; i++) + convert_delimiter(ref[i].path_name, '\\'); + } + return rc; +} + +/* + * Find, create or update a DFS cache entry. + * + * If the entry wasn't found, it will create a new one. Or if it was found but + * expired, then it will update the entry accordingly. + * + * For interlinks, cifs_mount() and expand_dfs_referral() are supposed to + * handle them properly. + */ +static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses, const char *path) +{ + struct dfs_info3_param *refs = NULL; + struct cache_entry *ce; + int numrefs = 0; + int rc; + + cifs_dbg(FYI, "%s: search path: %s\n", __func__, path); + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (!IS_ERR(ce) && !cache_entry_expired(ce)) { + up_read(&htable_rw_lock); + return 0; + } + /* + * Unlock shared access as we don't want to hold any locks while getting + * a new referral. The @ses used for performing the I/O could be + * reconnecting and it acquires @htable_rw_lock to look up the dfs cache + * in order to failover -- if necessary. + */ + up_read(&htable_rw_lock); + + /* + * Either the entry was not found, or it is expired. + * Request a new DFS referral in order to create or update a cache entry. + */ + rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); + if (rc) + goto out; + + dump_refs(refs, numrefs); + + down_write(&htable_rw_lock); + /* Re-check as another task might have it added or refreshed already */ + ce = lookup_cache_entry(path); + if (!IS_ERR(ce)) { + if (cache_entry_expired(ce)) + rc = update_cache_entry_locked(ce, refs, numrefs); + } else { + rc = add_cache_entry_locked(refs, numrefs); + } + + up_write(&htable_rw_lock); +out: + free_dfs_info_array(refs, numrefs); + return rc; +} + +/* + * Set up a DFS referral from a given cache entry. + * + * Must be called with htable_rw_lock held. + */ +static int setup_referral(const char *path, struct cache_entry *ce, + struct dfs_info3_param *ref, const char *target) +{ + int rc; + + cifs_dbg(FYI, "%s: set up new ref\n", __func__); + + memset(ref, 0, sizeof(*ref)); + + ref->path_name = kstrdup(path, GFP_ATOMIC); + if (!ref->path_name) + return -ENOMEM; + + ref->node_name = kstrdup(target, GFP_ATOMIC); + if (!ref->node_name) { + rc = -ENOMEM; + goto err_free_path; + } + + ref->path_consumed = ce->path_consumed; + ref->ttl = ce->ttl; + ref->server_type = ce->srvtype; + ref->ref_flag = ce->ref_flags; + ref->flags = ce->hdr_flags; + + return 0; + +err_free_path: + kfree(ref->path_name); + ref->path_name = NULL; + return rc; +} + +/* Return target list of a DFS cache entry */ +static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) +{ + int rc; + struct list_head *head = &tl->tl_list; + struct cache_dfs_tgt *t; + struct dfs_cache_tgt_iterator *it, *nit; + + memset(tl, 0, sizeof(*tl)); + INIT_LIST_HEAD(head); + + list_for_each_entry(t, &ce->tlist, list) { + it = kzalloc(sizeof(*it), GFP_ATOMIC); + if (!it) { + rc = -ENOMEM; + goto err_free_it; + } + + it->it_name = kstrdup(t->name, GFP_ATOMIC); + if (!it->it_name) { + kfree(it); + rc = -ENOMEM; + goto err_free_it; + } + it->it_path_consumed = t->path_consumed; + + if (ce->tgthint == t) + list_add(&it->it_list, head); + else + list_add_tail(&it->it_list, head); + } + + tl->tl_numtgts = ce->numtgts; + + return 0; + +err_free_it: + list_for_each_entry_safe(it, nit, head, it_list) { + list_del(&it->it_list); + kfree(it->it_name); + kfree(it); + } + return rc; +} + +/** + * dfs_cache_find - find a DFS cache entry + * + * If it doesn't find the cache entry, then it will get a DFS referral + * for @path and create a new entry. + * + * In case the cache entry exists but expired, it will get a DFS referral + * for @path and then update the respective cache entry. + * + * These parameters are passed down to the get_dfs_refer() call if it + * needs to be issued: + * @xid: syscall xid + * @ses: smb session to issue the request on + * @cp: codepage + * @remap: path character remapping type + * @path: path to lookup in DFS referral cache. + * + * @ref: when non-NULL, store single DFS referral result in it. + * @tgt_list: when non-NULL, store complete DFS target list in it. + * + * Return zero if the target was found, otherwise non-zero. + */ +int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, + int remap, const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list) +{ + int rc; + const char *npath; + struct cache_entry *ce; + + npath = dfs_cache_canonical_path(path, cp, remap); + if (IS_ERR(npath)) + return PTR_ERR(npath); + + rc = cache_refresh_path(xid, ses, npath); + if (rc) + goto out_free_path; + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(npath); + if (IS_ERR(ce)) { + up_read(&htable_rw_lock); + rc = PTR_ERR(ce); + goto out_free_path; + } + + if (ref) + rc = setup_referral(path, ce, ref, get_tgt_name(ce)); + else + rc = 0; + if (!rc && tgt_list) + rc = get_targets(ce, tgt_list); + + up_read(&htable_rw_lock); + +out_free_path: + kfree(npath); + return rc; +} + +/** + * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to + * the currently connected server. + * + * NOTE: This function will neither update a cache entry in case it was + * expired, nor create a new cache entry if @path hasn't been found. It heavily + * relies on an existing cache entry. + * + * @path: canonical DFS path to lookup in the DFS referral cache. + * @ref: when non-NULL, store single DFS referral result in it. + * @tgt_list: when non-NULL, store complete DFS target list in it. + * + * Return 0 if successful. + * Return -ENOENT if the entry was not found. + * Return non-zero for other errors. + */ +int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list) +{ + int rc; + struct cache_entry *ce; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + if (ref) + rc = setup_referral(path, ce, ref, get_tgt_name(ce)); + else + rc = 0; + if (!rc && tgt_list) + rc = get_targets(ce, tgt_list); + +out_unlock: + up_read(&htable_rw_lock); + return rc; +} + +/** + * dfs_cache_update_tgthint - update target hint of a DFS cache entry + * + * If it doesn't find the cache entry, then it will get a DFS referral for @path + * and create a new entry. + * + * In case the cache entry exists but expired, it will get a DFS referral + * for @path and then update the respective cache entry. + * + * @xid: syscall id + * @ses: smb session + * @cp: codepage + * @remap: type of character remapping for paths + * @path: path to lookup in DFS referral cache + * @it: DFS target iterator + * + * Return zero if the target hint was updated successfully, otherwise non-zero. + */ +int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, + const struct nls_table *cp, int remap, const char *path, + const struct dfs_cache_tgt_iterator *it) +{ + struct cache_dfs_tgt *t; + struct cache_entry *ce; + const char *npath; + int rc = 0; + + npath = dfs_cache_canonical_path(path, cp, remap); + if (IS_ERR(npath)) + return PTR_ERR(npath); + + cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath); + + rc = cache_refresh_path(xid, ses, npath); + if (rc) + goto out_free_path; + + down_write(&htable_rw_lock); + + ce = lookup_cache_entry(npath); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + t = ce->tgthint; + + if (likely(!strcasecmp(it->it_name, t->name))) + goto out_unlock; + + list_for_each_entry(t, &ce->tlist, list) { + if (!strcasecmp(t->name, it->it_name)) { + ce->tgthint = t; + cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, + it->it_name); + break; + } + } + +out_unlock: + up_write(&htable_rw_lock); +out_free_path: + kfree(npath); + return rc; +} + +/** + * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry + * without sending any requests to the currently connected server. + * + * NOTE: This function will neither update a cache entry in case it was + * expired, nor create a new cache entry if @path hasn't been found. It heavily + * relies on an existing cache entry. + * + * @path: canonical DFS path to lookup in DFS referral cache. + * @it: target iterator which contains the target hint to update the cache + * entry with. + * + * Return zero if the target hint was updated successfully, otherwise non-zero. + */ +int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it) +{ + int rc; + struct cache_entry *ce; + struct cache_dfs_tgt *t; + + if (!it) + return -EINVAL; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + down_write(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + rc = 0; + t = ce->tgthint; + + if (unlikely(!strcasecmp(it->it_name, t->name))) + goto out_unlock; + + list_for_each_entry(t, &ce->tlist, list) { + if (!strcasecmp(t->name, it->it_name)) { + ce->tgthint = t; + cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, + it->it_name); + break; + } + } + +out_unlock: + up_write(&htable_rw_lock); + return rc; +} + +/** + * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given + * target iterator (@it). + * + * @path: canonical DFS path to lookup in DFS referral cache. + * @it: DFS target iterator. + * @ref: DFS referral pointer to set up the gathered information. + * + * Return zero if the DFS referral was set up correctly, otherwise non-zero. + */ +int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, + struct dfs_info3_param *ref) +{ + int rc; + struct cache_entry *ce; + + if (!it || !ref) + return -EINVAL; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name); + + rc = setup_referral(path, ce, ref, it->it_name); + +out_unlock: + up_read(&htable_rw_lock); + return rc; +} + +/** + * dfs_cache_add_refsrv_session - add SMB session of referral server + * + * @mount_id: mount group uuid to lookup. + * @ses: reference counted SMB session of referral server. + */ +void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses) +{ + struct mount_group *mg; + + if (WARN_ON_ONCE(!mount_id || uuid_is_null(mount_id) || !ses)) + return; + + mg = get_mount_group(mount_id); + if (WARN_ON_ONCE(IS_ERR(mg))) + return; + + spin_lock(&mg->lock); + if (mg->num_sessions < ARRAY_SIZE(mg->sessions)) + mg->sessions[mg->num_sessions++] = ses; + spin_unlock(&mg->lock); + kref_put(&mg->refcount, mount_group_release); +} + +/** + * dfs_cache_put_refsrv_sessions - put all referral server sessions + * + * Put all SMB sessions from the given mount group id. + * + * @mount_id: mount group uuid to lookup. + */ +void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id) +{ + struct mount_group *mg; + + if (!mount_id || uuid_is_null(mount_id)) + return; + + mutex_lock(&mount_group_list_lock); + mg = find_mount_group_locked(mount_id); + if (IS_ERR(mg)) { + mutex_unlock(&mount_group_list_lock); + return; + } + mutex_unlock(&mount_group_list_lock); + kref_put(&mg->refcount, mount_group_release); +} + +/* Extract share from DFS target and return a pointer to prefix path or NULL */ +static const char *parse_target_share(const char *target, char **share) +{ + const char *s, *seps = "/\\"; + size_t len; + + s = strpbrk(target + 1, seps); + if (!s) + return ERR_PTR(-EINVAL); + + len = strcspn(s + 1, seps); + if (!len) + return ERR_PTR(-EINVAL); + s += len; + + len = s - target + 1; + *share = kstrndup(target, len, GFP_KERNEL); + if (!*share) + return ERR_PTR(-ENOMEM); + + s = target + len; + return s + strspn(s, seps); +} + +/** + * dfs_cache_get_tgt_share - parse a DFS target + * + * @path: DFS full path + * @it: DFS target iterator. + * @share: tree name. + * @prefix: prefix path. + * + * Return zero if target was parsed correctly, otherwise non-zero. + */ +int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, + char **prefix) +{ + char sep; + char *target_share; + char *ppath = NULL; + const char *target_ppath, *dfsref_ppath; + size_t target_pplen, dfsref_pplen; + size_t len, c; + + if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed) + return -EINVAL; + + sep = it->it_name[0]; + if (sep != '\\' && sep != '/') + return -EINVAL; + + target_ppath = parse_target_share(it->it_name, &target_share); + if (IS_ERR(target_ppath)) + return PTR_ERR(target_ppath); + + /* point to prefix in DFS referral path */ + dfsref_ppath = path + it->it_path_consumed; + dfsref_ppath += strspn(dfsref_ppath, "/\\"); + + target_pplen = strlen(target_ppath); + dfsref_pplen = strlen(dfsref_ppath); + + /* merge prefix paths from DFS referral path and target node */ + if (target_pplen || dfsref_pplen) { + len = target_pplen + dfsref_pplen + 2; + ppath = kzalloc(len, GFP_KERNEL); + if (!ppath) { + kfree(target_share); + return -ENOMEM; + } + c = strscpy(ppath, target_ppath, len); + if (c && dfsref_pplen) + ppath[c] = sep; + strlcat(ppath, dfsref_ppath, len); + } + *share = target_share; + *prefix = ppath; + return 0; +} + +static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) +{ + char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; + const char *host; + size_t hostlen; + char *ip = NULL; + struct sockaddr sa; + bool match; + int rc; + + if (strcasecmp(s1, s2)) + return false; + + /* + * Resolve share's hostname and check if server address matches. Otherwise just ignore it + * as we could not have upcall to resolve hostname or failed to convert ip address. + */ + match = true; + extract_unc_hostname(s1, &host, &hostlen); + scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host); + + rc = dns_resolve_server_name_to_ip(unc, &ip, NULL); + if (rc < 0) { + cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n", + __func__, (int)hostlen, host); + return true; + } + + if (!cifs_convert_address(&sa, ip, strlen(ip))) { + cifs_dbg(VFS, "%s: failed to convert address \'%s\'. skip address matching.\n", + __func__, ip); + } else { + cifs_server_lock(server); + match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, &sa); + cifs_server_unlock(server); + } + + kfree(ip); + return match; +} + +/* + * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new + * target shares in @refs. + */ +static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cache_tgt_list *tl, + const struct dfs_info3_param *refs, int numrefs) +{ + struct dfs_cache_tgt_iterator *it; + int i; + + for (it = dfs_cache_get_tgt_iterator(tl); it; it = dfs_cache_get_next_tgt(tl, it)) { + for (i = 0; i < numrefs; i++) { + if (target_share_equal(tcon->ses->server, dfs_cache_get_tgt_name(it), + refs[i].node_name)) + return; + } + } + + cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__); + cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); +} + +/* Refresh dfs referral of tcon and mark it for reconnect if needed */ +static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon, + bool force_refresh) +{ + struct cifs_ses *ses; + struct cache_entry *ce; + struct dfs_info3_param *refs = NULL; + int numrefs = 0; + bool needs_refresh = false; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + int rc = 0; + unsigned int xid; + + ses = find_ipc_from_server_path(sessions, path); + if (IS_ERR(ses)) { + cifs_dbg(FYI, "%s: could not find ipc session\n", __func__); + return PTR_ERR(ses); + } + + down_read(&htable_rw_lock); + ce = lookup_cache_entry(path); + needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); + if (!IS_ERR(ce)) { + rc = get_targets(ce, &tl); + if (rc) + cifs_dbg(FYI, "%s: could not get dfs targets: %d\n", __func__, rc); + } + up_read(&htable_rw_lock); + + if (!needs_refresh) { + rc = 0; + goto out; + } + + xid = get_xid(); + rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); + free_xid(xid); + + /* Create or update a cache entry with the new referral */ + if (!rc) { + dump_refs(refs, numrefs); + + down_write(&htable_rw_lock); + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) + add_cache_entry_locked(refs, numrefs); + else if (force_refresh || cache_entry_expired(ce)) + update_cache_entry_locked(ce, refs, numrefs); + up_write(&htable_rw_lock); + + mark_for_reconnect_if_needed(tcon, &tl, refs, numrefs); + } + +out: + dfs_cache_free_tgts(&tl); + free_dfs_info_array(refs, numrefs); + return rc; +} + +static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) +{ + struct TCP_Server_Info *server = tcon->ses->server; + + mutex_lock(&server->refpath_lock); + if (server->origin_fullpath) { + if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, + server->origin_fullpath)) + __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh); + __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh); + } + mutex_unlock(&server->refpath_lock); + + return 0; +} + +/** + * dfs_cache_remount_fs - remount a DFS share + * + * Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not + * match any of the new targets, mark it for reconnect. + * + * @cifs_sb: cifs superblock. + * + * Return zero if remounted, otherwise non-zero. + */ +int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) +{ + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct mount_group *mg; + struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; + int rc; + + if (!cifs_sb || !cifs_sb->master_tlink) + return -EINVAL; + + tcon = cifs_sb_master_tcon(cifs_sb); + server = tcon->ses->server; + + if (!server->origin_fullpath) { + cifs_dbg(FYI, "%s: not a dfs mount\n", __func__); + return 0; + } + + if (uuid_is_null(&cifs_sb->dfs_mount_id)) { + cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__); + return -EINVAL; + } + + mutex_lock(&mount_group_list_lock); + mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); + if (IS_ERR(mg)) { + mutex_unlock(&mount_group_list_lock); + cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__); + return PTR_ERR(mg); + } + kref_get(&mg->refcount); + mutex_unlock(&mount_group_list_lock); + + spin_lock(&mg->lock); + memcpy(&sessions, mg->sessions, mg->num_sessions * sizeof(mg->sessions[0])); + spin_unlock(&mg->lock); + + /* + * After reconnecting to a different server, unique ids won't match anymore, so we disable + * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). + */ + cifs_autodisable_serverino(cifs_sb); + /* + * Force the use of prefix path to support failover on DFS paths that resolve to targets + * that have different prefix paths. + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + rc = refresh_tcon(sessions, tcon, true); + + kref_put(&mg->refcount, mount_group_release); + return rc; +} + +/* + * Refresh all active dfs mounts regardless of whether they are in cache or not. + * (cache can be cleared) + */ +static void refresh_mounts(struct cifs_ses **sessions) +{ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon, *ntcon; + struct list_head tcons; + + INIT_LIST_HEAD(&tcons); + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + spin_lock(&server->srv_lock); + if (!server->is_dfs_conn) { + spin_unlock(&server->srv_lock); + continue; + } + spin_unlock(&server->srv_lock); + + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->tc_lock); + if (!tcon->ipc && !tcon->need_reconnect) { + tcon->tc_count++; + list_add_tail(&tcon->ulist, &tcons); + } + spin_unlock(&tcon->tc_lock); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + + list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { + struct TCP_Server_Info *server = tcon->ses->server; + + list_del_init(&tcon->ulist); + + mutex_lock(&server->refpath_lock); + if (server->origin_fullpath) { + if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, + server->origin_fullpath)) + __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); + __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); + } + mutex_unlock(&server->refpath_lock); + + cifs_put_tcon(tcon); + } +} + +static void refresh_cache(struct cifs_ses **sessions) +{ + int i; + struct cifs_ses *ses; + unsigned int xid; + char *ref_paths[CACHE_MAX_ENTRIES]; + int count = 0; + struct cache_entry *ce; + + /* + * Refresh all cached entries. Get all new referrals outside critical section to avoid + * starvation while performing SMB2 IOCTL on broken or slow connections. + + * The cache entries may cover more paths than the active mounts + * (e.g. domain-based DFS referrals or multi tier DFS setups). + */ + down_read(&htable_rw_lock); + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + + hlist_for_each_entry(ce, l, hlist) { + if (count == ARRAY_SIZE(ref_paths)) + goto out_unlock; + if (hlist_unhashed(&ce->hlist) || !cache_entry_expired(ce) || + IS_ERR(find_ipc_from_server_path(sessions, ce->path))) + continue; + ref_paths[count++] = kstrdup(ce->path, GFP_ATOMIC); + } + } + +out_unlock: + up_read(&htable_rw_lock); + + for (i = 0; i < count; i++) { + char *path = ref_paths[i]; + struct dfs_info3_param *refs = NULL; + int numrefs = 0; + int rc = 0; + + if (!path) + continue; + + ses = find_ipc_from_server_path(sessions, path); + if (IS_ERR(ses)) + goto next_referral; + + xid = get_xid(); + rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); + free_xid(xid); + + if (!rc) { + down_write(&htable_rw_lock); + ce = lookup_cache_entry(path); + /* + * We need to re-check it because other tasks might have it deleted or + * updated. + */ + if (!IS_ERR(ce) && cache_entry_expired(ce)) + update_cache_entry_locked(ce, refs, numrefs); + up_write(&htable_rw_lock); + } + +next_referral: + kfree(path); + free_dfs_info_array(refs, numrefs); + } +} + +/* + * Worker that will refresh DFS cache and active mounts based on lowest TTL value from a DFS + * referral. + */ +static void refresh_cache_worker(struct work_struct *work) +{ + struct list_head mglist; + struct mount_group *mg, *tmp_mg; + struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; + int max_sessions = ARRAY_SIZE(sessions) - 1; + int i = 0, count; + + INIT_LIST_HEAD(&mglist); + + /* Get refereces of mount groups */ + mutex_lock(&mount_group_list_lock); + list_for_each_entry(mg, &mount_group_list, list) { + kref_get(&mg->refcount); + list_add(&mg->refresh_list, &mglist); + } + mutex_unlock(&mount_group_list_lock); + + /* Fill in local array with an NULL-terminated list of all referral server sessions */ + list_for_each_entry(mg, &mglist, refresh_list) { + if (i >= max_sessions) + break; + + spin_lock(&mg->lock); + if (i + mg->num_sessions > max_sessions) + count = max_sessions - i; + else + count = mg->num_sessions; + memcpy(&sessions[i], mg->sessions, count * sizeof(mg->sessions[0])); + spin_unlock(&mg->lock); + i += count; + } + + if (sessions[0]) { + /* Refresh all active mounts and cached entries */ + refresh_mounts(sessions); + refresh_cache(sessions); + } + + list_for_each_entry_safe(mg, tmp_mg, &mglist, refresh_list) { + list_del_init(&mg->refresh_list); + kref_put(&mg->refcount, mount_group_release); + } + + spin_lock(&cache_ttl_lock); + queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); + spin_unlock(&cache_ttl_lock); +} diff --git a/fs/smb/client/dfs_cache.h b/fs/smb/client/dfs_cache.h new file mode 100644 index 000000000000..52070d1df189 --- /dev/null +++ b/fs/smb/client/dfs_cache.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DFS referral cache routines + * + * Copyright (c) 2018-2019 Paulo Alcantara + */ + +#ifndef _CIFS_DFS_CACHE_H +#define _CIFS_DFS_CACHE_H + +#include +#include +#include +#include "cifsglob.h" + +#define DFS_CACHE_TGT_LIST_INIT(var) { .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), } + +struct dfs_cache_tgt_list { + int tl_numtgts; + struct list_head tl_list; +}; + +struct dfs_cache_tgt_iterator { + char *it_name; + int it_path_consumed; + struct list_head it_list; +}; + +int dfs_cache_init(void); +void dfs_cache_destroy(void); +extern const struct proc_ops dfscache_proc_ops; + +int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, + int remap, const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list); +int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list); +int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, + const struct nls_table *cp, int remap, const char *path, + const struct dfs_cache_tgt_iterator *it); +int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it); +int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, + struct dfs_info3_param *ref); +int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, + char **prefix); +void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id); +void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses); +char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap); +int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb); + +static inline struct dfs_cache_tgt_iterator * +dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, + struct dfs_cache_tgt_iterator *it) +{ + if (!tl || list_empty(&tl->tl_list) || !it || + list_is_last(&it->it_list, &tl->tl_list)) + return NULL; + return list_next_entry(it, it_list); +} + +static inline struct dfs_cache_tgt_iterator * +dfs_cache_get_tgt_iterator(struct dfs_cache_tgt_list *tl) +{ + if (!tl) + return NULL; + return list_first_entry_or_null(&tl->tl_list, + struct dfs_cache_tgt_iterator, + it_list); +} + +static inline void dfs_cache_free_tgts(struct dfs_cache_tgt_list *tl) +{ + struct dfs_cache_tgt_iterator *it, *nit; + + if (!tl || list_empty(&tl->tl_list)) + return; + list_for_each_entry_safe(it, nit, &tl->tl_list, it_list) { + list_del(&it->it_list); + kfree(it->it_name); + kfree(it); + } + tl->tl_numtgts = 0; +} + +static inline const char * +dfs_cache_get_tgt_name(const struct dfs_cache_tgt_iterator *it) +{ + return it ? it->it_name : NULL; +} + +static inline int +dfs_cache_get_nr_tgts(const struct dfs_cache_tgt_list *tl) +{ + return tl ? tl->tl_numtgts : 0; +} + +#endif /* _CIFS_DFS_CACHE_H */ diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c new file mode 100644 index 000000000000..e382b794acbe --- /dev/null +++ b/fs/smb/client/dir.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * vfs operations that deal with dentries + * + * Copyright (C) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fs_context.h" +#include "cifs_ioctl.h" +#include "fscache.h" + +static void +renew_parental_timestamps(struct dentry *direntry) +{ + /* BB check if there is a way to get the kernel to do this or if we + really need this */ + do { + cifs_set_time(direntry, jiffies); + direntry = direntry->d_parent; + } while (!IS_ROOT(direntry)); +} + +char * +cifs_build_path_to_root(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, int add_treename) +{ + int pplen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0; + int dfsplen; + char *full_path = NULL; + + /* if no prefix path, simply set path to the root of share to "" */ + if (pplen == 0) { + full_path = kzalloc(1, GFP_KERNEL); + return full_path; + } + + if (add_treename) + dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); + else + dfsplen = 0; + + full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); + if (full_path == NULL) + return full_path; + + if (dfsplen) + memcpy(full_path, tcon->tree_name, dfsplen); + full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb); + memcpy(full_path + dfsplen + 1, ctx->prepath, pplen); + convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); + return full_path; +} + +/* Note: caller must free return buffer */ +const char * +build_path_from_dentry(struct dentry *direntry, void *page) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS; + + return build_path_from_dentry_optional_prefix(direntry, page, + prefix); +} + +char * +build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page, + bool prefix) +{ + int dfsplen; + int pplen = 0; + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + char dirsep = CIFS_DIR_SEP(cifs_sb); + char *s; + + if (unlikely(!page)) + return ERR_PTR(-ENOMEM); + + if (prefix) + dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); + else + dfsplen = 0; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) + pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0; + + s = dentry_path_raw(direntry, page, PATH_MAX); + if (IS_ERR(s)) + return s; + if (!s[1]) // for root we want "", not "/" + s++; + if (s < (char *)page + pplen + dfsplen) + return ERR_PTR(-ENAMETOOLONG); + if (pplen) { + cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath); + s -= pplen; + memcpy(s + 1, cifs_sb->prepath, pplen - 1); + *s = '/'; + } + if (dirsep != '/') { + /* BB test paths to Windows with '/' in the midst of prepath */ + char *p; + + for (p = s; *p; p++) + if (*p == '/') + *p = dirsep; + } + if (dfsplen) { + s -= dfsplen; + memcpy(s, tcon->tree_name, dfsplen); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { + int i; + for (i = 0; i < dfsplen; i++) { + if (s[i] == '\\') + s[i] = '/'; + } + } + } + return s; +} + +/* + * Don't allow path components longer than the server max. + * Don't allow the separator character in a path component. + * The VFS will not allow "/", but "\" is allowed by posix. + */ +static int +check_name(struct dentry *direntry, struct cifs_tcon *tcon) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + int i; + + if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength && + direntry->d_name.len > + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength))) + return -ENAMETOOLONG; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) { + for (i = 0; i < direntry->d_name.len; i++) { + if (direntry->d_name.name[i] == '\\') { + cifs_dbg(FYI, "Invalid file name\n"); + return -EINVAL; + } + } + } + return 0; +} + + +/* Inode operations in similar order to how they appear in Linux file fs.h */ + +static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, + struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock, + struct cifs_fid *fid, struct cifs_open_info_data *buf) +{ + int rc = -ENOENT; + int create_options = CREATE_NOT_DIR; + int desired_access; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = tlink_tcon(tlink); + const char *full_path; + void *page = alloc_dentry_path(); + struct inode *newinode = NULL; + int disposition; + struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_open_parms oparms; + + *oplock = 0; + if (tcon->ses->server->oplocks) + *oplock = REQ_OPLOCK; + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + free_dentry_path(page); + return PTR_ERR(full_path); + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, + oflags, oplock, &fid->netfid, xid); + switch (rc) { + case 0: + if (newinode == NULL) { + /* query inode info */ + goto cifs_create_get_file_info; + } + + if (S_ISDIR(newinode->i_mode)) { + CIFSSMBClose(xid, tcon, fid->netfid); + iput(newinode); + rc = -EISDIR; + goto out; + } + + if (!S_ISREG(newinode->i_mode)) { + /* + * The server may allow us to open things like + * FIFOs, but the client isn't set up to deal + * with that. If it's not a regular file, just + * close it and proceed as if it were a normal + * lookup. + */ + CIFSSMBClose(xid, tcon, fid->netfid); + goto cifs_create_get_file_info; + } + /* success, no need to query */ + goto cifs_create_set_dentry; + + case -ENOENT: + goto cifs_create_get_file_info; + + case -EIO: + case -EINVAL: + /* + * EIO could indicate that (posix open) operation is not + * supported, despite what server claimed in capability + * negotiation. + * + * POSIX open in samba versions 3.3.1 and earlier could + * incorrectly fail with invalid parameter. + */ + tcon->broken_posix_open = true; + break; + + case -EREMOTE: + case -EOPNOTSUPP: + /* + * EREMOTE indicates DFS junction, which is not handled + * in posix open. If either that or op not supported + * returned, follow the normal lookup. + */ + break; + + default: + goto out; + } + /* + * fallthrough to retry, using older open call, this is case + * where server does not support this SMB level, and falsely + * claims capability (also get here for DFS case which should be + * rare for path not covered on files) + */ + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + desired_access = 0; + if (OPEN_FMODE(oflags) & FMODE_READ) + desired_access |= GENERIC_READ; /* is this too little? */ + if (OPEN_FMODE(oflags) & FMODE_WRITE) + desired_access |= GENERIC_WRITE; + + disposition = FILE_OVERWRITE_IF; + if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + disposition = FILE_CREATE; + else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) + disposition = FILE_OVERWRITE_IF; + else if ((oflags & O_CREAT) == O_CREAT) + disposition = FILE_OPEN_IF; + else + cifs_dbg(FYI, "Create flag not set in create function\n"); + + /* + * BB add processing to set equivalent of mode - e.g. via CreateX with + * ACLs + */ + + if (!server->ops->open) { + rc = -ENOSYS; + goto out; + } + + /* + * if we're not using unix extensions, see if we need to set + * ATTR_READONLY on the create call + */ + if (!tcon->unix_ext && (mode & S_IWUGO) == 0) + create_options |= CREATE_OPTION_READONLY; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = desired_access, + .create_options = cifs_create_options(cifs_sb, create_options), + .disposition = disposition, + .path = full_path, + .fid = fid, + .mode = mode, + }; + rc = server->ops->open(xid, &oparms, oplock, buf); + if (rc) { + cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); + goto out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* + * If Open reported that we actually created a file then we now have to + * set the mode if possible. + */ + if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) { + struct cifs_unix_set_info_args args = { + .mode = mode, + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = 0, + }; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + args.uid = current_fsuid(); + if (inode->i_mode & S_ISGID) + args.gid = inode->i_gid; + else + args.gid = current_fsgid(); + } else { + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ + } + CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, + current->tgid); + } else { + /* + * BB implement mode setting via Windows security + * descriptors e.g. + */ + /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ + + /* Could set r/o dos attribute if mode & 0222 == 0 */ + } + +cifs_create_get_file_info: + /* server might mask mode so we have to query for it */ + if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, + xid); + else { +#else + { +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + /* TODO: Add support for calling POSIX query info here, but passing in fid */ + rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid); + if (newinode) { + if (server->ops->set_lease_key) + server->ops->set_lease_key(newinode, fid); + if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) + newinode->i_mode = mode; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + newinode->i_uid = current_fsuid(); + if (inode->i_mode & S_ISGID) + newinode->i_gid = inode->i_gid; + else + newinode->i_gid = current_fsgid(); + } + } + } + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +cifs_create_set_dentry: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + if (rc != 0) { + cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", + rc); + goto out_err; + } + + if (newinode) + if (S_ISDIR(newinode->i_mode)) { + rc = -EISDIR; + goto out_err; + } + + d_drop(direntry); + d_add(direntry, newinode); + +out: + free_dentry_path(page); + return rc; + +out_err: + if (server->ops->close) + server->ops->close(xid, tcon, fid); + if (newinode) + iput(newinode); + goto out; +} + +int +cifs_atomic_open(struct inode *inode, struct dentry *direntry, + struct file *file, unsigned oflags, umode_t mode) +{ + int rc; + unsigned int xid; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifs_fid fid = {}; + struct cifs_pending_open open; + __u32 oplock; + struct cifsFileInfo *file_info; + struct cifs_open_info_data buf = {}; + + if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) + return -EIO; + + /* + * Posix open is only called (at lookup time) for file create now. For + * opens (rather than creates), because we do not know if it is a file + * or directory yet, and current Samba no longer allows us to do posix + * open on dirs, we could end up wasting an open call on what turns out + * to be a dir. For file opens, we wait to call posix open till + * cifs_open. It could be added to atomic_open in the future but the + * performance tradeoff of the extra network request when EISDIR or + * EACCES is returned would have to be weighed against the 50% reduction + * in network traffic in the other paths. + */ + if (!(oflags & O_CREAT)) { + struct dentry *res; + + /* + * Check for hashed negative dentry. We have already revalidated + * the dentry and it is fine. No need to perform another lookup. + */ + if (!d_in_lookup(direntry)) + return -ENOENT; + + res = cifs_lookup(inode, direntry, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + return finish_no_open(file, res); + } + + xid = get_xid(); + + cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", + inode, direntry, direntry); + + tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto out_free_xid; + } + + tcon = tlink_tcon(tlink); + + rc = check_name(direntry, tcon); + if (rc) + goto out; + + server = tcon->ses->server; + + if (server->ops->new_lease_key) + server->ops->new_lease_key(&fid); + + cifs_add_pending_open(&fid, tlink, &open); + + rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, + &oplock, &fid, &buf); + if (rc) { + cifs_del_pending_open(&open); + goto out; + } + + if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + file->f_mode |= FMODE_CREATED; + + rc = finish_open(file, direntry, generic_file_open); + if (rc) { + if (server->ops->close) + server->ops->close(xid, tcon, &fid); + cifs_del_pending_open(&open); + goto out; + } + + if (file->f_flags & O_DIRECT && + CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { + if (CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + file->f_op = &cifs_file_direct_nobrl_ops; + else + file->f_op = &cifs_file_direct_ops; + } + + file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target); + if (file_info == NULL) { + if (server->ops->close) + server->ops->close(xid, tcon, &fid); + cifs_del_pending_open(&open); + rc = -ENOMEM; + goto out; + } + + fscache_use_cookie(cifs_inode_cookie(file_inode(file)), + file->f_mode & FMODE_WRITE); + +out: + cifs_put_tlink(tlink); +out_free_xid: + free_xid(xid); + cifs_free_open_info(&buf); + return rc; +} + +int cifs_create(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, umode_t mode, bool excl) +{ + int rc; + unsigned int xid = get_xid(); + /* + * BB below access is probably too much for mknod to request + * but we have to do query and setpathinfo so requesting + * less could fail (unless we want to request getatr and setatr + * permissions (only). At least for POSIX we do not have to + * request so much. + */ + unsigned oflags = O_EXCL | O_CREAT | O_RDWR; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifs_fid fid; + __u32 oplock; + struct cifs_open_info_data buf = {}; + + cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n", + inode, direntry, direntry); + + if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) { + rc = -EIO; + goto out_free_xid; + } + + tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); + rc = PTR_ERR(tlink); + if (IS_ERR(tlink)) + goto out_free_xid; + + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + if (server->ops->new_lease_key) + server->ops->new_lease_key(&fid); + + rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf); + if (!rc && server->ops->close) + server->ops->close(xid, tcon, &fid); + + cifs_free_open_info(&buf); + cifs_put_tlink(tlink); +out_free_xid: + free_xid(xid); + return rc; +} + +int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, umode_t mode, dev_t device_number) +{ + int rc = -EPERM; + unsigned int xid; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + const char *full_path; + void *page; + + if (!old_valid_dev(device_number)) + return -EINVAL; + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + page = alloc_dentry_path(); + tcon = tlink_tcon(tlink); + xid = get_xid(); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto mknod_out; + } + + rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon, + full_path, mode, + device_number); + +mknod_out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +struct dentry * +cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, + unsigned int flags) +{ + unsigned int xid; + int rc = 0; /* to get around spurious gcc warning, set to zero here */ + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + struct inode *newInode = NULL; + const char *full_path; + void *page; + int retry_count = 0; + + xid = get_xid(); + + cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", + parent_dir_inode, direntry, direntry); + + /* check whether path exists */ + + cifs_sb = CIFS_SB(parent_dir_inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + free_xid(xid); + return ERR_CAST(tlink); + } + pTcon = tlink_tcon(tlink); + + rc = check_name(direntry, pTcon); + if (unlikely(rc)) { + cifs_put_tlink(tlink); + free_xid(xid); + return ERR_PTR(rc); + } + + /* can not grab the rename sem here since it would + deadlock in the cases (beginning of sys_rename itself) + in which we already have the sb rename sem */ + page = alloc_dentry_path(); + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + cifs_put_tlink(tlink); + free_xid(xid); + free_dentry_path(page); + return ERR_CAST(full_path); + } + + if (d_really_is_positive(direntry)) { + cifs_dbg(FYI, "non-NULL inode in lookup\n"); + } else { + cifs_dbg(FYI, "NULL inode in lookup\n"); + } + cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", + full_path, d_inode(direntry)); + +again: + if (pTcon->posix_extensions) + rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid); + else if (pTcon->unix_ext) { + rc = cifs_get_inode_info_unix(&newInode, full_path, + parent_dir_inode->i_sb, xid); + } else { + rc = cifs_get_inode_info(&newInode, full_path, NULL, + parent_dir_inode->i_sb, xid, NULL); + } + + if (rc == 0) { + /* since paths are not looked up by component - the parent + directories are presumed to be good here */ + renew_parental_timestamps(direntry); + } else if (rc == -EAGAIN && retry_count++ < 10) { + goto again; + } else if (rc == -ENOENT) { + cifs_set_time(direntry, jiffies); + newInode = NULL; + } else { + if (rc != -EACCES) { + cifs_dbg(FYI, "Unexpected lookup error %d\n", rc); + /* We special case check for Access Denied - since that + is a common return code */ + } + newInode = ERR_PTR(rc); + } + free_dentry_path(page); + cifs_put_tlink(tlink); + free_xid(xid); + return d_splice_alias(newInode, direntry); +} + +static int +cifs_d_revalidate(struct dentry *direntry, unsigned int flags) +{ + struct inode *inode; + int rc; + + if (flags & LOOKUP_RCU) + return -ECHILD; + + if (d_really_is_positive(direntry)) { + inode = d_inode(direntry); + if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode))) + CIFS_I(inode)->time = 0; /* force reval */ + + rc = cifs_revalidate_dentry(direntry); + if (rc) { + cifs_dbg(FYI, "cifs_revalidate_dentry failed with rc=%d", rc); + switch (rc) { + case -ENOENT: + case -ESTALE: + /* + * Those errors mean the dentry is invalid + * (file was deleted or recreated) + */ + return 0; + default: + /* + * Otherwise some unexpected error happened + * report it as-is to VFS layer + */ + return rc; + } + } + else { + /* + * If the inode wasn't known to be a dfs entry when + * the dentry was instantiated, such as when created + * via ->readdir(), it needs to be set now since the + * attributes will have been updated by + * cifs_revalidate_dentry(). + */ + if (IS_AUTOMOUNT(inode) && + !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { + spin_lock(&direntry->d_lock); + direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; + spin_unlock(&direntry->d_lock); + } + + return 1; + } + } + + /* + * This may be nfsd (or something), anyway, we can't see the + * intent of this. So, since this can be for creation, drop it. + */ + if (!flags) + return 0; + + /* + * Drop the negative dentry, in order to make sure to use the + * case sensitive name which is specified by user if this is + * for creation. + */ + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + + if (time_after(jiffies, cifs_get_time(direntry) + HZ) || !lookupCacheEnabled) + return 0; + + return 1; +} + +/* static int cifs_d_delete(struct dentry *direntry) +{ + int rc = 0; + + cifs_dbg(FYI, "In cifs d_delete, name = %pd\n", direntry); + + return rc; +} */ + +const struct dentry_operations cifs_dentry_ops = { + .d_revalidate = cifs_d_revalidate, + .d_automount = cifs_dfs_d_automount, +/* d_delete: cifs_d_delete, */ /* not needed except for debugging */ +}; + +static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; + unsigned long hash; + wchar_t c; + int i, charlen; + + hash = init_name_hash(dentry); + for (i = 0; i < q->len; i += charlen) { + charlen = codepage->char2uni(&q->name[i], q->len - i, &c); + /* error out if we can't convert the character */ + if (unlikely(charlen < 0)) + return charlen; + hash = partial_name_hash(cifs_toupper(c), hash); + } + q->hash = end_name_hash(hash); + + return 0; +} + +static int cifs_ci_compare(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; + wchar_t c1, c2; + int i, l1, l2; + + /* + * We make the assumption here that uppercase characters in the local + * codepage are always the same length as their lowercase counterparts. + * + * If that's ever not the case, then this will fail to match it. + */ + if (name->len != len) + return 1; + + for (i = 0; i < len; i += l1) { + /* Convert characters in both strings to UTF-16. */ + l1 = codepage->char2uni(&str[i], len - i, &c1); + l2 = codepage->char2uni(&name->name[i], name->len - i, &c2); + + /* + * If we can't convert either character, just declare it to + * be 1 byte long and compare the original byte. + */ + if (unlikely(l1 < 0 && l2 < 0)) { + if (str[i] != name->name[i]) + return 1; + l1 = 1; + continue; + } + + /* + * Here, we again ass|u|me that upper/lowercase versions of + * a character are the same length in the local NLS. + */ + if (l1 != l2) + return 1; + + /* Now compare uppercase versions of these characters */ + if (cifs_toupper(c1) != cifs_toupper(c2)) + return 1; + } + + return 0; +} + +const struct dentry_operations cifs_ci_dentry_ops = { + .d_revalidate = cifs_d_revalidate, + .d_hash = cifs_ci_hash, + .d_compare = cifs_ci_compare, + .d_automount = cifs_dfs_d_automount, +}; diff --git a/fs/smb/client/dns_resolve.c b/fs/smb/client/dns_resolve.c new file mode 100644 index 000000000000..0458d28d71aa --- /dev/null +++ b/fs/smb/client/dns_resolve.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (c) 2007 Igor Mammedov + * Author(s): Igor Mammedov (niallain@gmail.com) + * Steve French (sfrench@us.ibm.com) + * Wang Lei (wang840925@gmail.com) + * David Howells (dhowells@redhat.com) + * + * Contains the CIFS DFS upcall routines used for hostname to + * IP address translation. + * + */ + +#include +#include +#include "dns_resolve.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" + +/** + * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. + * @unc: UNC path specifying the server (with '/' as delimiter) + * @ip_addr: Where to return the IP address. + * @expiry: Where to return the expiry time for the dns record. + * + * The IP address will be returned in string form, and the caller is + * responsible for freeing it. + * + * Returns length of result on success, -ve on error. + */ +int +dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry) +{ + struct sockaddr_storage ss; + const char *hostname, *sep; + char *name; + int len, rc; + + if (!ip_addr || !unc) + return -EINVAL; + + len = strlen(unc); + if (len < 3) { + cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc); + return -EINVAL; + } + + /* Discount leading slashes for cifs */ + len -= 2; + hostname = unc + 2; + + /* Search for server name delimiter */ + sep = memchr(hostname, '/', len); + if (sep) + len = sep - hostname; + else + cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n", + __func__, unc); + + /* Try to interpret hostname as an IPv4 or IPv6 address */ + rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len); + if (rc > 0) + goto name_is_IP_address; + + /* Perform the upcall */ + rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len, + NULL, ip_addr, expiry, false); + if (rc < 0) + cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n", + __func__, len, len, hostname); + else + cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n", + __func__, len, len, hostname, *ip_addr, + expiry ? (*expiry) : 0); + return rc; + +name_is_IP_address: + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + memcpy(name, hostname, len); + name[len] = 0; + cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n", + __func__, name); + *ip_addr = name; + return 0; +} diff --git a/fs/smb/client/dns_resolve.h b/fs/smb/client/dns_resolve.h new file mode 100644 index 000000000000..afc0df381246 --- /dev/null +++ b/fs/smb/client/dns_resolve.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * DNS Resolver upcall management for CIFS DFS + * Handles host name to IP address resolution + * + * Copyright (c) International Business Machines Corp., 2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _DNS_RESOLVE_H +#define _DNS_RESOLVE_H + +#ifdef __KERNEL__ +extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry); +#endif /* KERNEL */ + +#endif /* _DNS_RESOLVE_H */ diff --git a/fs/smb/client/export.c b/fs/smb/client/export.c new file mode 100644 index 000000000000..37c28415df1e --- /dev/null +++ b/fs/smb/client/export.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Common Internet FileSystem (CIFS) client + * + * Operations related to support for exporting files via NFSD + * + */ + + /* + * See Documentation/filesystems/nfs/exporting.rst + * and examples in fs/exportfs + * + * Since cifs is a network file system, an "fsid" must be included for + * any nfs exports file entries which refer to cifs paths. In addition + * the cifs mount must be mounted with the "serverino" option (ie use stable + * server inode numbers instead of locally generated temporary ones). + * Although cifs inodes do not use generation numbers (have generation number + * of zero) - the inode number alone should be good enough for simple cases + * in which users want to export cifs shares with NFS. The decode and encode + * could be improved by using a new routine which expects 64 bit inode numbers + * instead of the default 32 bit routines in fs/exportfs + * + */ + +#include +#include +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifsfs.h" + +#ifdef CONFIG_CIFS_NFSD_EXPORT +static struct dentry *cifs_get_parent(struct dentry *dentry) +{ + /* BB need to add code here eventually to enable export via NFSD */ + cifs_dbg(FYI, "get parent for %p\n", dentry); + return ERR_PTR(-EACCES); +} + +const struct export_operations cifs_export_ops = { + .get_parent = cifs_get_parent, +/* Following five export operations are unneeded so far and can default: + .get_dentry = + .get_name = + .find_exported_dentry = + .decode_fh = + .encode_fs = */ +}; + +#endif /* CONFIG_CIFS_NFSD_EXPORT */ + diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c new file mode 100644 index 000000000000..87dcffece762 --- /dev/null +++ b/fs/smb/client/file.c @@ -0,0 +1,5290 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * vfs operations that deal with files + * + * Copyright (C) International Business Machines Corp., 2002,2010 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "fscache.h" +#include "smbdirect.h" +#include "fs_context.h" +#include "cifs_ioctl.h" +#include "cached_dir.h" + +/* + * Mark as invalid, all open files on tree connections since they + * were closed when session to server was lost. + */ +void +cifs_mark_open_files_invalid(struct cifs_tcon *tcon) +{ + struct cifsFileInfo *open_file = NULL; + struct list_head *tmp; + struct list_head *tmp1; + + /* only send once per connect */ + spin_lock(&tcon->ses->ses_lock); + if ((tcon->ses->ses_status != SES_GOOD) || (tcon->status != TID_NEED_RECON)) { + spin_unlock(&tcon->ses->ses_lock); + return; + } + tcon->status = TID_IN_FILES_INVALIDATE; + spin_unlock(&tcon->ses->ses_lock); + + /* list all files open on tree connection and mark them invalid */ + spin_lock(&tcon->open_file_lock); + list_for_each_safe(tmp, tmp1, &tcon->openFileList) { + open_file = list_entry(tmp, struct cifsFileInfo, tlist); + open_file->invalidHandle = true; + open_file->oplock_break_cancelled = true; + } + spin_unlock(&tcon->open_file_lock); + + invalidate_all_cached_dirs(tcon); + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_FILES_INVALIDATE) + tcon->status = TID_NEED_TCON; + spin_unlock(&tcon->tc_lock); + + /* + * BB Add call to invalidate_inodes(sb) for all superblocks mounted + * to this tcon. + */ +} + +static inline int cifs_convert_flags(unsigned int flags) +{ + if ((flags & O_ACCMODE) == O_RDONLY) + return GENERIC_READ; + else if ((flags & O_ACCMODE) == O_WRONLY) + return GENERIC_WRITE; + else if ((flags & O_ACCMODE) == O_RDWR) { + /* GENERIC_ALL is too much permission to request + can cause unnecessary access denied on create */ + /* return GENERIC_ALL; */ + return (GENERIC_READ | GENERIC_WRITE); + } + + return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | + FILE_READ_DATA); +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static u32 cifs_posix_convert_flags(unsigned int flags) +{ + u32 posix_flags = 0; + + if ((flags & O_ACCMODE) == O_RDONLY) + posix_flags = SMB_O_RDONLY; + else if ((flags & O_ACCMODE) == O_WRONLY) + posix_flags = SMB_O_WRONLY; + else if ((flags & O_ACCMODE) == O_RDWR) + posix_flags = SMB_O_RDWR; + + if (flags & O_CREAT) { + posix_flags |= SMB_O_CREAT; + if (flags & O_EXCL) + posix_flags |= SMB_O_EXCL; + } else if (flags & O_EXCL) + cifs_dbg(FYI, "Application %s pid %d has incorrectly set O_EXCL flag but not O_CREAT on file open. Ignoring O_EXCL\n", + current->comm, current->tgid); + + if (flags & O_TRUNC) + posix_flags |= SMB_O_TRUNC; + /* be safe and imply O_SYNC for O_DSYNC */ + if (flags & O_DSYNC) + posix_flags |= SMB_O_SYNC; + if (flags & O_DIRECTORY) + posix_flags |= SMB_O_DIRECTORY; + if (flags & O_NOFOLLOW) + posix_flags |= SMB_O_NOFOLLOW; + if (flags & O_DIRECT) + posix_flags |= SMB_O_DIRECT; + + return posix_flags; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static inline int cifs_get_disposition(unsigned int flags) +{ + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + return FILE_CREATE; + else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) + return FILE_OVERWRITE_IF; + else if ((flags & O_CREAT) == O_CREAT) + return FILE_OPEN_IF; + else if ((flags & O_TRUNC) == O_TRUNC) + return FILE_OVERWRITE; + else + return FILE_OPEN; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +int cifs_posix_open(const char *full_path, struct inode **pinode, + struct super_block *sb, int mode, unsigned int f_flags, + __u32 *poplock, __u16 *pnetfid, unsigned int xid) +{ + int rc; + FILE_UNIX_BASIC_INFO *presp_data; + __u32 posix_flags = 0; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_fattr fattr; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + + cifs_dbg(FYI, "posix open %s\n", full_path); + + presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); + if (presp_data == NULL) + return -ENOMEM; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto posix_open_ret; + } + + tcon = tlink_tcon(tlink); + mode &= ~current_umask(); + + posix_flags = cifs_posix_convert_flags(f_flags); + rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, + poplock, full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + cifs_put_tlink(tlink); + + if (rc) + goto posix_open_ret; + + if (presp_data->Type == cpu_to_le32(-1)) + goto posix_open_ret; /* open ok, caller does qpathinfo */ + + if (!pinode) + goto posix_open_ret; /* caller does not need info */ + + cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb); + + /* get new inode and set it up */ + if (*pinode == NULL) { + cifs_fill_uniqueid(sb, &fattr); + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) { + rc = -ENOMEM; + goto posix_open_ret; + } + } else { + cifs_revalidate_mapping(*pinode); + rc = cifs_fattr_to_inode(*pinode, &fattr); + } + +posix_open_ret: + kfree(presp_data); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock, + struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf) +{ + int rc; + int desired_access; + int disposition; + int create_options = CREATE_NOT_DIR; + struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_open_parms oparms; + + if (!server->ops->open) + return -ENOSYS; + + desired_access = cifs_convert_flags(f_flags); + +/********************************************************************* + * open flag mapping table: + * + * POSIX Flag CIFS Disposition + * ---------- ---------------- + * O_CREAT FILE_OPEN_IF + * O_CREAT | O_EXCL FILE_CREATE + * O_CREAT | O_TRUNC FILE_OVERWRITE_IF + * O_TRUNC FILE_OVERWRITE + * none of the above FILE_OPEN + * + * Note that there is not a direct match between disposition + * FILE_SUPERSEDE (ie create whether or not file exists although + * O_CREAT | O_TRUNC is similar but truncates the existing + * file rather than creating a new file as FILE_SUPERSEDE does + * (which uses the attributes / metadata passed in on open call) + *? + *? O_SYNC is a reasonable match to CIFS writethrough flag + *? and the read write flags match reasonably. O_LARGEFILE + *? is irrelevant because largefile support is always used + *? by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY, + * O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation + *********************************************************************/ + + disposition = cifs_get_disposition(f_flags); + + /* BB pass O_SYNC flag through on file attributes .. BB */ + + /* O_SYNC also has bit for O_DSYNC so following check picks up either */ + if (f_flags & O_SYNC) + create_options |= CREATE_WRITE_THROUGH; + + if (f_flags & O_DIRECT) + create_options |= CREATE_NO_BUFFER; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = desired_access, + .create_options = cifs_create_options(cifs_sb, create_options), + .disposition = disposition, + .path = full_path, + .fid = fid, + }; + + rc = server->ops->open(xid, &oparms, oplock, buf); + if (rc) + return rc; + + /* TODO: Add support for calling posix query info but with passing in fid */ + if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, + xid); + else + rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, + xid, fid); + + if (rc) { + server->ops->close(xid, tcon, fid); + if (rc == -ESTALE) + rc = -EOPENSTALE; + } + + return rc; +} + +static bool +cifs_has_mand_locks(struct cifsInodeInfo *cinode) +{ + struct cifs_fid_locks *cur; + bool has_locks = false; + + down_read(&cinode->lock_sem); + list_for_each_entry(cur, &cinode->llist, llist) { + if (!list_empty(&cur->locks)) { + has_locks = true; + break; + } + } + up_read(&cinode->lock_sem); + return has_locks; +} + +void +cifs_down_write(struct rw_semaphore *sem) +{ + while (!down_write_trylock(sem)) + msleep(10); +} + +static void cifsFileInfo_put_work(struct work_struct *work); + +struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, + struct tcon_link *tlink, __u32 oplock, + const char *symlink_target) +{ + struct dentry *dentry = file_dentry(file); + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifsFileInfo *cfile; + struct cifs_fid_locks *fdlocks; + struct cifs_tcon *tcon = tlink_tcon(tlink); + struct TCP_Server_Info *server = tcon->ses->server; + + cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); + if (cfile == NULL) + return cfile; + + fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL); + if (!fdlocks) { + kfree(cfile); + return NULL; + } + + if (symlink_target) { + cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL); + if (!cfile->symlink_target) { + kfree(fdlocks); + kfree(cfile); + return NULL; + } + } + + INIT_LIST_HEAD(&fdlocks->locks); + fdlocks->cfile = cfile; + cfile->llist = fdlocks; + + cfile->count = 1; + cfile->pid = current->tgid; + cfile->uid = current_fsuid(); + cfile->dentry = dget(dentry); + cfile->f_flags = file->f_flags; + cfile->invalidHandle = false; + cfile->deferred_close_scheduled = false; + cfile->tlink = cifs_get_tlink(tlink); + INIT_WORK(&cfile->oplock_break, cifs_oplock_break); + INIT_WORK(&cfile->put, cifsFileInfo_put_work); + INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close); + mutex_init(&cfile->fh_mutex); + spin_lock_init(&cfile->file_info_lock); + + cifs_sb_active(inode->i_sb); + + /* + * If the server returned a read oplock and we have mandatory brlocks, + * set oplock level to None. + */ + if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { + cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); + oplock = 0; + } + + cifs_down_write(&cinode->lock_sem); + list_add(&fdlocks->llist, &cinode->llist); + up_write(&cinode->lock_sem); + + spin_lock(&tcon->open_file_lock); + if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) + oplock = fid->pending_open->oplock; + list_del(&fid->pending_open->olist); + + fid->purge_cache = false; + server->ops->set_fid(cfile, fid, oplock); + + list_add(&cfile->tlist, &tcon->openFileList); + atomic_inc(&tcon->num_local_opens); + + /* if readable file instance put first in list*/ + spin_lock(&cinode->open_file_lock); + if (file->f_mode & FMODE_READ) + list_add(&cfile->flist, &cinode->openFileList); + else + list_add_tail(&cfile->flist, &cinode->openFileList); + spin_unlock(&cinode->open_file_lock); + spin_unlock(&tcon->open_file_lock); + + if (fid->purge_cache) + cifs_zap_mapping(inode); + + file->private_data = cfile; + return cfile; +} + +struct cifsFileInfo * +cifsFileInfo_get(struct cifsFileInfo *cifs_file) +{ + spin_lock(&cifs_file->file_info_lock); + cifsFileInfo_get_locked(cifs_file); + spin_unlock(&cifs_file->file_info_lock); + return cifs_file; +} + +static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) +{ + struct inode *inode = d_inode(cifs_file->dentry); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifsLockInfo *li, *tmp; + struct super_block *sb = inode->i_sb; + + /* + * Delete any outstanding lock records. We'll lose them when the file + * is closed anyway. + */ + cifs_down_write(&cifsi->lock_sem); + list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { + list_del(&li->llist); + cifs_del_lock_waiters(li); + kfree(li); + } + list_del(&cifs_file->llist->llist); + kfree(cifs_file->llist); + up_write(&cifsi->lock_sem); + + cifs_put_tlink(cifs_file->tlink); + dput(cifs_file->dentry); + cifs_sb_deactive(sb); + kfree(cifs_file->symlink_target); + kfree(cifs_file); +} + +static void cifsFileInfo_put_work(struct work_struct *work) +{ + struct cifsFileInfo *cifs_file = container_of(work, + struct cifsFileInfo, put); + + cifsFileInfo_put_final(cifs_file); +} + +/** + * cifsFileInfo_put - release a reference of file priv data + * + * Always potentially wait for oplock handler. See _cifsFileInfo_put(). + * + * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file + */ +void cifsFileInfo_put(struct cifsFileInfo *cifs_file) +{ + _cifsFileInfo_put(cifs_file, true, true); +} + +/** + * _cifsFileInfo_put - release a reference of file priv data + * + * This may involve closing the filehandle @cifs_file out on the + * server. Must be called without holding tcon->open_file_lock, + * cinode->open_file_lock and cifs_file->file_info_lock. + * + * If @wait_for_oplock_handler is true and we are releasing the last + * reference, wait for any running oplock break handler of the file + * and cancel any pending one. + * + * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file + * @wait_oplock_handler: must be false if called from oplock_break_handler + * @offload: not offloaded on close and oplock breaks + * + */ +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, + bool wait_oplock_handler, bool offload) +{ + struct inode *inode = d_inode(cifs_file->dentry); + struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct super_block *sb = inode->i_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_fid fid = {}; + struct cifs_pending_open open; + bool oplock_break_cancelled; + + spin_lock(&tcon->open_file_lock); + spin_lock(&cifsi->open_file_lock); + spin_lock(&cifs_file->file_info_lock); + if (--cifs_file->count > 0) { + spin_unlock(&cifs_file->file_info_lock); + spin_unlock(&cifsi->open_file_lock); + spin_unlock(&tcon->open_file_lock); + return; + } + spin_unlock(&cifs_file->file_info_lock); + + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &fid); + + /* store open in pending opens to make sure we don't miss lease break */ + cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open); + + /* remove it from the lists */ + list_del(&cifs_file->flist); + list_del(&cifs_file->tlist); + atomic_dec(&tcon->num_local_opens); + + if (list_empty(&cifsi->openFileList)) { + cifs_dbg(FYI, "closing last open instance for inode %p\n", + d_inode(cifs_file->dentry)); + /* + * In strict cache mode we need invalidate mapping on the last + * close because it may cause a error when we open this file + * again and get at least level II oplock. + */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) + set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); + cifs_set_oplock_level(cifsi, 0); + } + + spin_unlock(&cifsi->open_file_lock); + spin_unlock(&tcon->open_file_lock); + + oplock_break_cancelled = wait_oplock_handler ? + cancel_work_sync(&cifs_file->oplock_break) : false; + + if (!tcon->need_reconnect && !cifs_file->invalidHandle) { + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int xid; + + xid = get_xid(); + if (server->ops->close_getattr) + server->ops->close_getattr(xid, tcon, cifs_file); + else if (server->ops->close) + server->ops->close(xid, tcon, &cifs_file->fid); + _free_xid(xid); + } + + if (oplock_break_cancelled) + cifs_done_oplock_break(cifsi); + + cifs_del_pending_open(&open); + + if (offload) + queue_work(fileinfo_put_wq, &cifs_file->put); + else + cifsFileInfo_put_final(cifs_file); +} + +int cifs_open(struct inode *inode, struct file *file) + +{ + int rc = -EACCES; + unsigned int xid; + __u32 oplock; + struct cifs_sb_info *cifs_sb; + struct TCP_Server_Info *server; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cifsFileInfo *cfile = NULL; + void *page; + const char *full_path; + bool posix_open_ok = false; + struct cifs_fid fid = {}; + struct cifs_pending_open open; + struct cifs_open_info_data data = {}; + + xid = get_xid(); + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) { + free_xid(xid); + return -EIO; + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + free_xid(xid); + return PTR_ERR(tlink); + } + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + page = alloc_dentry_path(); + full_path = build_path_from_dentry(file_dentry(file), page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + cifs_dbg(FYI, "inode = 0x%p file flags are 0x%x for %s\n", + inode, file->f_flags, full_path); + + if (file->f_flags & O_DIRECT && + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + file->f_op = &cifs_file_direct_nobrl_ops; + else + file->f_op = &cifs_file_direct_ops; + } + + /* Get the cached handle as SMB2 close is deferred */ + rc = cifs_get_readable_path(tcon, full_path, &cfile); + if (rc == 0) { + if (file->f_flags == cfile->f_flags) { + file->private_data = cfile; + spin_lock(&CIFS_I(inode)->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&CIFS_I(inode)->deferred_lock); + goto use_cache; + } else { + _cifsFileInfo_put(cfile, true, false); + } + } + + if (server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (!tcon->broken_posix_open && tcon->unix_ext && + cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + /* can not refresh inode info since size could be stale */ + rc = cifs_posix_open(full_path, &inode, inode->i_sb, + cifs_sb->ctx->file_mode /* ignored */, + file->f_flags, &oplock, &fid.netfid, xid); + if (rc == 0) { + cifs_dbg(FYI, "posix open succeeded\n"); + posix_open_ok = true; + } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { + if (tcon->ses->serverNOS) + cifs_dbg(VFS, "server %s of type %s returned unexpected error on SMB posix open, disabling posix open support. Check if server update available.\n", + tcon->ses->ip_addr, + tcon->ses->serverNOS); + tcon->broken_posix_open = true; + } else if ((rc != -EIO) && (rc != -EREMOTE) && + (rc != -EOPNOTSUPP)) /* path not found or net err */ + goto out; + /* + * Else fallthrough to retry open the old way on network i/o + * or DFS errors. + */ + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &fid); + + cifs_add_pending_open(&fid, tlink, &open); + + if (!posix_open_ok) { + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &fid); + + rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid, + xid, &data); + if (rc) { + cifs_del_pending_open(&open); + goto out; + } + } + + cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target); + if (cfile == NULL) { + if (server->ops->close) + server->ops->close(xid, tcon, &fid); + cifs_del_pending_open(&open); + rc = -ENOMEM; + goto out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) { + /* + * Time to set mode which we can not set earlier due to + * problems creating new read-only files. + */ + struct cifs_unix_set_info_args args = { + .mode = inode->i_mode, + .uid = INVALID_UID, /* no change */ + .gid = INVALID_GID, /* no change */ + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = 0, + }; + CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid, + cfile->pid); + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +use_cache: + fscache_use_cookie(cifs_inode_cookie(file_inode(file)), + file->f_mode & FMODE_WRITE); + if (file->f_flags & O_DIRECT && + (!((file->f_flags & O_ACCMODE) != O_RDONLY) || + file->f_flags & O_APPEND)) + cifs_invalidate_cache(file_inode(file), + FSCACHE_INVAL_DIO_WRITE); + +out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + cifs_free_open_info(&data); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int cifs_push_posix_locks(struct cifsFileInfo *cfile); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* + * Try to reacquire byte range locks that were released when session + * to server was lost. + */ +static int +cifs_relock_file(struct cifsFileInfo *cfile) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + int rc = 0; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + down_read_nested(&cinode->lock_sem, SINGLE_DEPTH_NESTING); + if (cinode->can_cache_brlcks) { + /* can cache locks - no need to relock */ + up_read(&cinode->lock_sem); + return rc; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + rc = cifs_push_posix_locks(cfile); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = tcon->ses->server->ops->push_mand_locks(cfile); + + up_read(&cinode->lock_sem); + return rc; +} + +static int +cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) +{ + int rc = -EACCES; + unsigned int xid; + __u32 oplock; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsInodeInfo *cinode; + struct inode *inode; + void *page; + const char *full_path; + int desired_access; + int disposition = FILE_OPEN; + int create_options = CREATE_NOT_DIR; + struct cifs_open_parms oparms; + + xid = get_xid(); + mutex_lock(&cfile->fh_mutex); + if (!cfile->invalidHandle) { + mutex_unlock(&cfile->fh_mutex); + free_xid(xid); + return 0; + } + + inode = d_inode(cfile->dentry); + cifs_sb = CIFS_SB(inode->i_sb); + tcon = tlink_tcon(cfile->tlink); + server = tcon->ses->server; + + /* + * Can not grab rename sem here because various ops, including those + * that already have the rename sem can end up causing writepage to get + * called and if the server was down that means we end up here, and we + * can never tell if the caller already has the rename_sem. + */ + page = alloc_dentry_path(); + full_path = build_path_from_dentry(cfile->dentry, page); + if (IS_ERR(full_path)) { + mutex_unlock(&cfile->fh_mutex); + free_dentry_path(page); + free_xid(xid); + return PTR_ERR(full_path); + } + + cifs_dbg(FYI, "inode = 0x%p file flags 0x%x for %s\n", + inode, cfile->f_flags, full_path); + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext && cap_unix(tcon->ses) && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + /* + * O_CREAT, O_EXCL and O_TRUNC already had their effect on the + * original open. Must mask them off for a reopen. + */ + unsigned int oflags = cfile->f_flags & + ~(O_CREAT | O_EXCL | O_TRUNC); + + rc = cifs_posix_open(full_path, NULL, inode->i_sb, + cifs_sb->ctx->file_mode /* ignored */, + oflags, &oplock, &cfile->fid.netfid, xid); + if (rc == 0) { + cifs_dbg(FYI, "posix reopen succeeded\n"); + oparms.reconnect = true; + goto reopen_success; + } + /* + * fallthrough to retry open the old way on errors, especially + * in the reconnect path it is important to retry hard + */ + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + desired_access = cifs_convert_flags(cfile->f_flags); + + /* O_SYNC also has bit for O_DSYNC so following check picks up either */ + if (cfile->f_flags & O_SYNC) + create_options |= CREATE_WRITE_THROUGH; + + if (cfile->f_flags & O_DIRECT) + create_options |= CREATE_NO_BUFFER; + + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &cfile->fid); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = desired_access, + .create_options = cifs_create_options(cifs_sb, create_options), + .disposition = disposition, + .path = full_path, + .fid = &cfile->fid, + .reconnect = true, + }; + + /* + * Can not refresh inode by passing in file_info buf to be returned by + * ops->open and then calling get_inode_info with returned buf since + * file might have write behind data that needs to be flushed and server + * version of file size can be stale. If we knew for sure that inode was + * not dirty locally we could do this. + */ + rc = server->ops->open(xid, &oparms, &oplock, NULL); + if (rc == -ENOENT && oparms.reconnect == false) { + /* durable handle timeout is expired - open the file again */ + rc = server->ops->open(xid, &oparms, &oplock, NULL); + /* indicate that we need to relock the file */ + oparms.reconnect = true; + } + + if (rc) { + mutex_unlock(&cfile->fh_mutex); + cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc); + cifs_dbg(FYI, "oplock: %d\n", oplock); + goto reopen_error_exit; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +reopen_success: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + cfile->invalidHandle = false; + mutex_unlock(&cfile->fh_mutex); + cinode = CIFS_I(inode); + + if (can_flush) { + rc = filemap_write_and_wait(inode->i_mapping); + if (!is_interrupt_error(rc)) + mapping_set_error(inode->i_mapping, rc); + + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid); + else if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, + inode->i_sb, xid); + else + rc = cifs_get_inode_info(&inode, full_path, NULL, + inode->i_sb, xid, NULL); + } + /* + * Else we are writing out data to server already and could deadlock if + * we tried to flush data, and since we do not know if we have data that + * would invalidate the current end of file on the server we can not go + * to the server to get the new inode info. + */ + + /* + * If the server returned a read oplock and we have mandatory brlocks, + * set oplock level to None. + */ + if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { + cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); + oplock = 0; + } + + server->ops->set_fid(cfile, &cfile->fid, oplock); + if (oparms.reconnect) + cifs_relock_file(cfile); + +reopen_error_exit: + free_dentry_path(page); + free_xid(xid); + return rc; +} + +void smb2_deferred_work_close(struct work_struct *work) +{ + struct cifsFileInfo *cfile = container_of(work, + struct cifsFileInfo, deferred.work); + + spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + cifs_del_deferred_close(cfile); + cfile->deferred_close_scheduled = false; + spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + _cifsFileInfo_put(cfile, true, false); +} + +int cifs_close(struct inode *inode, struct file *file) +{ + struct cifsFileInfo *cfile; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_deferred_close *dclose; + + cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE); + + if (file->private_data != NULL) { + cfile = file->private_data; + file->private_data = NULL; + dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL); + if ((cinode->oplock == CIFS_CACHE_RHW_FLG) && + cinode->lease_granted && + !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) && + dclose) { + if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { + inode->i_ctime = inode->i_mtime = current_time(inode); + } + spin_lock(&cinode->deferred_lock); + cifs_add_deferred_close(cfile, dclose); + if (cfile->deferred_close_scheduled && + delayed_work_pending(&cfile->deferred)) { + /* + * If there is no pending work, mod_delayed_work queues new work. + * So, Increase the ref count to avoid use-after-free. + */ + if (!mod_delayed_work(deferredclose_wq, + &cfile->deferred, cifs_sb->ctx->closetimeo)) + cifsFileInfo_get(cfile); + } else { + /* Deferred close for files */ + queue_delayed_work(deferredclose_wq, + &cfile->deferred, cifs_sb->ctx->closetimeo); + cfile->deferred_close_scheduled = true; + spin_unlock(&cinode->deferred_lock); + return 0; + } + spin_unlock(&cinode->deferred_lock); + _cifsFileInfo_put(cfile, true, false); + } else { + _cifsFileInfo_put(cfile, true, false); + kfree(dclose); + } + } + + /* return code from the ->release op is always ignored */ + return 0; +} + +void +cifs_reopen_persistent_handles(struct cifs_tcon *tcon) +{ + struct cifsFileInfo *open_file, *tmp; + struct list_head tmp_list; + + if (!tcon->use_persistent || !tcon->need_reopen_files) + return; + + tcon->need_reopen_files = false; + + cifs_dbg(FYI, "Reopen persistent handles\n"); + INIT_LIST_HEAD(&tmp_list); + + /* list all files open on tree connection, reopen resilient handles */ + spin_lock(&tcon->open_file_lock); + list_for_each_entry(open_file, &tcon->openFileList, tlist) { + if (!open_file->invalidHandle) + continue; + cifsFileInfo_get(open_file); + list_add_tail(&open_file->rlist, &tmp_list); + } + spin_unlock(&tcon->open_file_lock); + + list_for_each_entry_safe(open_file, tmp, &tmp_list, rlist) { + if (cifs_reopen_file(open_file, false /* do not flush */)) + tcon->need_reopen_files = true; + list_del_init(&open_file->rlist); + cifsFileInfo_put(open_file); + } +} + +int cifs_closedir(struct inode *inode, struct file *file) +{ + int rc = 0; + unsigned int xid; + struct cifsFileInfo *cfile = file->private_data; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + char *buf; + + cifs_dbg(FYI, "Closedir inode = 0x%p\n", inode); + + if (cfile == NULL) + return rc; + + xid = get_xid(); + tcon = tlink_tcon(cfile->tlink); + server = tcon->ses->server; + + cifs_dbg(FYI, "Freeing private data in close dir\n"); + spin_lock(&cfile->file_info_lock); + if (server->ops->dir_needs_close(cfile)) { + cfile->invalidHandle = true; + spin_unlock(&cfile->file_info_lock); + if (server->ops->close_dir) + rc = server->ops->close_dir(xid, tcon, &cfile->fid); + else + rc = -ENOSYS; + cifs_dbg(FYI, "Closing uncompleted readdir with rc %d\n", rc); + /* not much we can do if it fails anyway, ignore rc */ + rc = 0; + } else + spin_unlock(&cfile->file_info_lock); + + buf = cfile->srch_inf.ntwrk_buf_start; + if (buf) { + cifs_dbg(FYI, "closedir free smb buf in srch struct\n"); + cfile->srch_inf.ntwrk_buf_start = NULL; + if (cfile->srch_inf.smallBuf) + cifs_small_buf_release(buf); + else + cifs_buf_release(buf); + } + + cifs_put_tlink(cfile->tlink); + kfree(file->private_data); + file->private_data = NULL; + /* BB can we lock the filestruct while this is going on? */ + free_xid(xid); + return rc; +} + +static struct cifsLockInfo * +cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 flags) +{ + struct cifsLockInfo *lock = + kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); + if (!lock) + return lock; + lock->offset = offset; + lock->length = length; + lock->type = type; + lock->pid = current->tgid; + lock->flags = flags; + INIT_LIST_HEAD(&lock->blist); + init_waitqueue_head(&lock->block_q); + return lock; +} + +void +cifs_del_lock_waiters(struct cifsLockInfo *lock) +{ + struct cifsLockInfo *li, *tmp; + list_for_each_entry_safe(li, tmp, &lock->blist, blist) { + list_del_init(&li->blist); + wake_up(&li->block_q); + } +} + +#define CIFS_LOCK_OP 0 +#define CIFS_READ_OP 1 +#define CIFS_WRITE_OP 2 + +/* @rw_check : 0 - no op, 1 - read, 2 - write */ +static bool +cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, + __u64 length, __u8 type, __u16 flags, + struct cifsFileInfo *cfile, + struct cifsLockInfo **conf_lock, int rw_check) +{ + struct cifsLockInfo *li; + struct cifsFileInfo *cur_cfile = fdlocks->cfile; + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + + list_for_each_entry(li, &fdlocks->locks, llist) { + if (offset + length <= li->offset || + offset >= li->offset + li->length) + continue; + if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid && + server->ops->compare_fids(cfile, cur_cfile)) { + /* shared lock prevents write op through the same fid */ + if (!(li->type & server->vals->shared_lock_type) || + rw_check != CIFS_WRITE_OP) + continue; + } + if ((type & server->vals->shared_lock_type) && + ((server->ops->compare_fids(cfile, cur_cfile) && + current->tgid == li->pid) || type == li->type)) + continue; + if (rw_check == CIFS_LOCK_OP && + (flags & FL_OFDLCK) && (li->flags & FL_OFDLCK) && + server->ops->compare_fids(cfile, cur_cfile)) + continue; + if (conf_lock) + *conf_lock = li; + return true; + } + return false; +} + +bool +cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, + __u8 type, __u16 flags, + struct cifsLockInfo **conf_lock, int rw_check) +{ + bool rc = false; + struct cifs_fid_locks *cur; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + + list_for_each_entry(cur, &cinode->llist, llist) { + rc = cifs_find_fid_lock_conflict(cur, offset, length, type, + flags, cfile, conf_lock, + rw_check); + if (rc) + break; + } + + return rc; +} + +/* + * Check if there is another lock that prevents us to set the lock (mandatory + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ +static int +cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, + __u8 type, struct file_lock *flock) +{ + int rc = 0; + struct cifsLockInfo *conf_lock; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + bool exist; + + down_read(&cinode->lock_sem); + + exist = cifs_find_lock_conflict(cfile, offset, length, type, + flock->fl_flags, &conf_lock, + CIFS_LOCK_OP); + if (exist) { + flock->fl_start = conf_lock->offset; + flock->fl_end = conf_lock->offset + conf_lock->length - 1; + flock->fl_pid = conf_lock->pid; + if (conf_lock->type & server->vals->shared_lock_type) + flock->fl_type = F_RDLCK; + else + flock->fl_type = F_WRLCK; + } else if (!cinode->can_cache_brlcks) + rc = 1; + else + flock->fl_type = F_UNLCK; + + up_read(&cinode->lock_sem); + return rc; +} + +static void +cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + cifs_down_write(&cinode->lock_sem); + list_add_tail(&lock->llist, &cfile->llist->locks); + up_write(&cinode->lock_sem); +} + +/* + * Set the byte-range lock (mandatory style). Returns: + * 1) 0, if we set the lock and don't need to request to the server; + * 2) 1, if no locks prevent us but we need to request to the server; + * 3) -EACCES, if there is a lock that prevents us and wait is false. + */ +static int +cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, + bool wait) +{ + struct cifsLockInfo *conf_lock; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + bool exist; + int rc = 0; + +try_again: + exist = false; + cifs_down_write(&cinode->lock_sem); + + exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, + lock->type, lock->flags, &conf_lock, + CIFS_LOCK_OP); + if (!exist && cinode->can_cache_brlcks) { + list_add_tail(&lock->llist, &cfile->llist->locks); + up_write(&cinode->lock_sem); + return rc; + } + + if (!exist) + rc = 1; + else if (!wait) + rc = -EACCES; + else { + list_add_tail(&lock->blist, &conf_lock->blist); + up_write(&cinode->lock_sem); + rc = wait_event_interruptible(lock->block_q, + (lock->blist.prev == &lock->blist) && + (lock->blist.next == &lock->blist)); + if (!rc) + goto try_again; + cifs_down_write(&cinode->lock_sem); + list_del_init(&lock->blist); + } + + up_write(&cinode->lock_sem); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/* + * Check if there is another lock that prevents us to set the lock (posix + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ +static int +cifs_posix_lock_test(struct file *file, struct file_lock *flock) +{ + int rc = 0; + struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); + unsigned char saved_type = flock->fl_type; + + if ((flock->fl_flags & FL_POSIX) == 0) + return 1; + + down_read(&cinode->lock_sem); + posix_test_lock(file, flock); + + if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) { + flock->fl_type = saved_type; + rc = 1; + } + + up_read(&cinode->lock_sem); + return rc; +} + +/* + * Set the byte-range lock (posix style). Returns: + * 1) <0, if the error occurs while setting the lock; + * 2) 0, if we set the lock and don't need to request to the server; + * 3) FILE_LOCK_DEFERRED, if we will wait for some other file_lock; + * 4) FILE_LOCK_DEFERRED + 1, if we need to request to the server. + */ +static int +cifs_posix_lock_set(struct file *file, struct file_lock *flock) +{ + struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); + int rc = FILE_LOCK_DEFERRED + 1; + + if ((flock->fl_flags & FL_POSIX) == 0) + return rc; + + cifs_down_write(&cinode->lock_sem); + if (!cinode->can_cache_brlcks) { + up_write(&cinode->lock_sem); + return rc; + } + + rc = posix_lock_file(file, flock, NULL); + up_write(&cinode->lock_sem); + return rc; +} + +int +cifs_push_mandatory_locks(struct cifsFileInfo *cfile) +{ + unsigned int xid; + int rc = 0, stored_rc; + struct cifsLockInfo *li, *tmp; + struct cifs_tcon *tcon; + unsigned int num, max_num, max_buf; + LOCKING_ANDX_RANGE *buf, *cur; + static const int types[] = { + LOCKING_ANDX_LARGE_FILES, + LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES + }; + int i; + + xid = get_xid(); + tcon = tlink_tcon(cfile->tlink); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it before using. + */ + max_buf = tcon->ses->server->maxBuf; + if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) { + free_xid(xid); + return -EINVAL; + } + + BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > + PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), + PAGE_SIZE); + max_num = (max_buf - sizeof(struct smb_hdr)) / + sizeof(LOCKING_ANDX_RANGE); + buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); + if (!buf) { + free_xid(xid); + return -ENOMEM; + } + + for (i = 0; i < 2; i++) { + cur = buf; + num = 0; + list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { + if (li->type != types[i]) + continue; + cur->Pid = cpu_to_le16(li->pid); + cur->LengthLow = cpu_to_le32((u32)li->length); + cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); + cur->OffsetLow = cpu_to_le32((u32)li->offset); + cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); + if (++num == max_num) { + stored_rc = cifs_lockv(xid, tcon, + cfile->fid.netfid, + (__u8)li->type, 0, num, + buf); + if (stored_rc) + rc = stored_rc; + cur = buf; + num = 0; + } else + cur++; + } + + if (num) { + stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, + (__u8)types[i], 0, num, buf); + if (stored_rc) + rc = stored_rc; + } + } + + kfree(buf); + free_xid(xid); + return rc; +} + +static __u32 +hash_lockowner(fl_owner_t owner) +{ + return cifs_lock_secret ^ hash32_ptr((const void *)owner); +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +struct lock_to_push { + struct list_head llist; + __u64 offset; + __u64 length; + __u32 pid; + __u16 netfid; + __u8 type; +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_push_posix_locks(struct cifsFileInfo *cfile) +{ + struct inode *inode = d_inode(cfile->dentry); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct file_lock *flock; + struct file_lock_context *flctx = inode->i_flctx; + unsigned int count = 0, i; + int rc = 0, xid, type; + struct list_head locks_to_send, *el; + struct lock_to_push *lck, *tmp; + __u64 length; + + xid = get_xid(); + + if (!flctx) + goto out; + + spin_lock(&flctx->flc_lock); + list_for_each(el, &flctx->flc_posix) { + count++; + } + spin_unlock(&flctx->flc_lock); + + INIT_LIST_HEAD(&locks_to_send); + + /* + * Allocating count locks is enough because no FL_POSIX locks can be + * added to the list while we are holding cinode->lock_sem that + * protects locking operations of this inode. + */ + for (i = 0; i < count; i++) { + lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL); + if (!lck) { + rc = -ENOMEM; + goto err_out; + } + list_add_tail(&lck->llist, &locks_to_send); + } + + el = locks_to_send.next; + spin_lock(&flctx->flc_lock); + list_for_each_entry(flock, &flctx->flc_posix, fl_list) { + if (el == &locks_to_send) { + /* + * The list ended. We don't have enough allocated + * structures - something is really wrong. + */ + cifs_dbg(VFS, "Can't push all brlocks!\n"); + break; + } + length = cifs_flock_len(flock); + if (flock->fl_type == F_RDLCK || flock->fl_type == F_SHLCK) + type = CIFS_RDLCK; + else + type = CIFS_WRLCK; + lck = list_entry(el, struct lock_to_push, llist); + lck->pid = hash_lockowner(flock->fl_owner); + lck->netfid = cfile->fid.netfid; + lck->length = length; + lck->type = type; + lck->offset = flock->fl_start; + } + spin_unlock(&flctx->flc_lock); + + list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { + int stored_rc; + + stored_rc = CIFSSMBPosixLock(xid, tcon, lck->netfid, lck->pid, + lck->offset, lck->length, NULL, + lck->type, 0); + if (stored_rc) + rc = stored_rc; + list_del(&lck->llist); + kfree(lck); + } + +out: + free_xid(xid); + return rc; +err_out: + list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { + list_del(&lck->llist); + kfree(lck); + } + goto out; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_push_locks(struct cifsFileInfo *cfile) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + int rc = 0; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + /* we are going to update can_cache_brlcks here - need a write access */ + cifs_down_write(&cinode->lock_sem); + if (!cinode->can_cache_brlcks) { + up_write(&cinode->lock_sem); + return rc; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + rc = cifs_push_posix_locks(cfile); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = tcon->ses->server->ops->push_mand_locks(cfile); + + cinode->can_cache_brlcks = false; + up_write(&cinode->lock_sem); + return rc; +} + +static void +cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, + bool *wait_flag, struct TCP_Server_Info *server) +{ + if (flock->fl_flags & FL_POSIX) + cifs_dbg(FYI, "Posix\n"); + if (flock->fl_flags & FL_FLOCK) + cifs_dbg(FYI, "Flock\n"); + if (flock->fl_flags & FL_SLEEP) { + cifs_dbg(FYI, "Blocking lock\n"); + *wait_flag = true; + } + if (flock->fl_flags & FL_ACCESS) + cifs_dbg(FYI, "Process suspended by mandatory locking - not implemented yet\n"); + if (flock->fl_flags & FL_LEASE) + cifs_dbg(FYI, "Lease on file - not implemented yet\n"); + if (flock->fl_flags & + (~(FL_POSIX | FL_FLOCK | FL_SLEEP | + FL_ACCESS | FL_LEASE | FL_CLOSE | FL_OFDLCK))) + cifs_dbg(FYI, "Unknown lock flags 0x%x\n", flock->fl_flags); + + *type = server->vals->large_lock_type; + if (flock->fl_type == F_WRLCK) { + cifs_dbg(FYI, "F_WRLCK\n"); + *type |= server->vals->exclusive_lock_type; + *lock = 1; + } else if (flock->fl_type == F_UNLCK) { + cifs_dbg(FYI, "F_UNLCK\n"); + *type |= server->vals->unlock_lock_type; + *unlock = 1; + /* Check if unlock includes more than one lock range */ + } else if (flock->fl_type == F_RDLCK) { + cifs_dbg(FYI, "F_RDLCK\n"); + *type |= server->vals->shared_lock_type; + *lock = 1; + } else if (flock->fl_type == F_EXLCK) { + cifs_dbg(FYI, "F_EXLCK\n"); + *type |= server->vals->exclusive_lock_type; + *lock = 1; + } else if (flock->fl_type == F_SHLCK) { + cifs_dbg(FYI, "F_SHLCK\n"); + *type |= server->vals->shared_lock_type; + *lock = 1; + } else + cifs_dbg(FYI, "Unknown type of lock\n"); +} + +static int +cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, + bool wait_flag, bool posix_lck, unsigned int xid) +{ + int rc = 0; + __u64 length = cifs_flock_len(flock); + struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + __u16 netfid = cfile->fid.netfid; + + if (posix_lck) { + int posix_lock_type; + + rc = cifs_posix_lock_test(file, flock); + if (!rc) + return rc; + + if (type & server->vals->shared_lock_type) + posix_lock_type = CIFS_RDLCK; + else + posix_lock_type = CIFS_WRLCK; + rc = CIFSSMBPosixLock(xid, tcon, netfid, + hash_lockowner(flock->fl_owner), + flock->fl_start, length, flock, + posix_lock_type, wait_flag); + return rc; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock); + if (!rc) + return rc; + + /* BB we could chain these into one lock request BB */ + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type, + 1, 0, false); + if (rc == 0) { + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type, 0, 1, false); + flock->fl_type = F_UNLCK; + if (rc != 0) + cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", + rc); + return 0; + } + + if (type & server->vals->shared_lock_type) { + flock->fl_type = F_WRLCK; + return 0; + } + + type &= ~server->vals->exclusive_lock_type; + + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type | server->vals->shared_lock_type, + 1, 0, false); + if (rc == 0) { + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type | server->vals->shared_lock_type, 0, 1, false); + flock->fl_type = F_RDLCK; + if (rc != 0) + cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", + rc); + } else + flock->fl_type = F_WRLCK; + + return 0; +} + +void +cifs_move_llist(struct list_head *source, struct list_head *dest) +{ + struct list_head *li, *tmp; + list_for_each_safe(li, tmp, source) + list_move(li, dest); +} + +void +cifs_free_llist(struct list_head *llist) +{ + struct cifsLockInfo *li, *tmp; + list_for_each_entry_safe(li, tmp, llist, llist) { + cifs_del_lock_waiters(li); + list_del(&li->llist); + kfree(li); + } +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +int +cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, + unsigned int xid) +{ + int rc = 0, stored_rc; + static const int types[] = { + LOCKING_ANDX_LARGE_FILES, + LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES + }; + unsigned int i; + unsigned int max_num, num, max_buf; + LOCKING_ANDX_RANGE *buf, *cur; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifsLockInfo *li, *tmp; + __u64 length = cifs_flock_len(flock); + struct list_head tmp_llist; + + INIT_LIST_HEAD(&tmp_llist); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it before using. + */ + max_buf = tcon->ses->server->maxBuf; + if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) + return -EINVAL; + + BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > + PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), + PAGE_SIZE); + max_num = (max_buf - sizeof(struct smb_hdr)) / + sizeof(LOCKING_ANDX_RANGE); + buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cifs_down_write(&cinode->lock_sem); + for (i = 0; i < 2; i++) { + cur = buf; + num = 0; + list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { + if (flock->fl_start > li->offset || + (flock->fl_start + length) < + (li->offset + li->length)) + continue; + if (current->tgid != li->pid) + continue; + if (types[i] != li->type) + continue; + if (cinode->can_cache_brlcks) { + /* + * We can cache brlock requests - simply remove + * a lock from the file's list. + */ + list_del(&li->llist); + cifs_del_lock_waiters(li); + kfree(li); + continue; + } + cur->Pid = cpu_to_le16(li->pid); + cur->LengthLow = cpu_to_le32((u32)li->length); + cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); + cur->OffsetLow = cpu_to_le32((u32)li->offset); + cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); + /* + * We need to save a lock here to let us add it again to + * the file's list if the unlock range request fails on + * the server. + */ + list_move(&li->llist, &tmp_llist); + if (++num == max_num) { + stored_rc = cifs_lockv(xid, tcon, + cfile->fid.netfid, + li->type, num, 0, buf); + if (stored_rc) { + /* + * We failed on the unlock range + * request - add all locks from the tmp + * list to the head of the file's list. + */ + cifs_move_llist(&tmp_llist, + &cfile->llist->locks); + rc = stored_rc; + } else + /* + * The unlock range request succeed - + * free the tmp list. + */ + cifs_free_llist(&tmp_llist); + cur = buf; + num = 0; + } else + cur++; + } + if (num) { + stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, + types[i], num, 0, buf); + if (stored_rc) { + cifs_move_llist(&tmp_llist, + &cfile->llist->locks); + rc = stored_rc; + } else + cifs_free_llist(&tmp_llist); + } + } + + up_write(&cinode->lock_sem); + kfree(buf); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, + bool wait_flag, bool posix_lck, int lock, int unlock, + unsigned int xid) +{ + int rc = 0; + __u64 length = cifs_flock_len(flock); + struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + struct inode *inode = d_inode(cfile->dentry); + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (posix_lck) { + int posix_lock_type; + + rc = cifs_posix_lock_set(file, flock); + if (rc <= FILE_LOCK_DEFERRED) + return rc; + + if (type & server->vals->shared_lock_type) + posix_lock_type = CIFS_RDLCK; + else + posix_lock_type = CIFS_WRLCK; + + if (unlock == 1) + posix_lock_type = CIFS_UNLCK; + + rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid, + hash_lockowner(flock->fl_owner), + flock->fl_start, length, + NULL, posix_lock_type, wait_flag); + goto out; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + if (lock) { + struct cifsLockInfo *lock; + + lock = cifs_lock_init(flock->fl_start, length, type, + flock->fl_flags); + if (!lock) + return -ENOMEM; + + rc = cifs_lock_add_if(cfile, lock, wait_flag); + if (rc < 0) { + kfree(lock); + return rc; + } + if (!rc) + goto out; + + /* + * Windows 7 server can delay breaking lease from read to None + * if we set a byte-range lock on a file - break it explicitly + * before sending the lock to the server to be sure the next + * read won't conflict with non-overlapted locks due to + * pagereading. + */ + if (!CIFS_CACHE_WRITE(CIFS_I(inode)) && + CIFS_CACHE_READ(CIFS_I(inode))) { + cifs_zap_mapping(inode); + cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n", + inode); + CIFS_I(inode)->oplock = 0; + } + + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type, 1, 0, wait_flag); + if (rc) { + kfree(lock); + return rc; + } + + cifs_lock_add(cfile, lock); + } else if (unlock) + rc = server->ops->mand_unlock_range(cfile, flock, xid); + +out: + if ((flock->fl_flags & FL_POSIX) || (flock->fl_flags & FL_FLOCK)) { + /* + * If this is a request to remove all locks because we + * are closing the file, it doesn't matter if the + * unlocking failed as both cifs.ko and the SMB server + * remove the lock on file close + */ + if (rc) { + cifs_dbg(VFS, "%s failed rc=%d\n", __func__, rc); + if (!(flock->fl_flags & FL_CLOSE)) + return rc; + } + rc = locks_lock_file_wait(file, flock); + } + return rc; +} + +int cifs_flock(struct file *file, int cmd, struct file_lock *fl) +{ + int rc, xid; + int lock = 0, unlock = 0; + bool wait_flag = false; + bool posix_lck = false; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + __u32 type; + + xid = get_xid(); + + if (!(fl->fl_flags & FL_FLOCK)) { + rc = -ENOLCK; + free_xid(xid); + return rc; + } + + cfile = (struct cifsFileInfo *)file->private_data; + tcon = tlink_tcon(cfile->tlink); + + cifs_read_flock(fl, &type, &lock, &unlock, &wait_flag, + tcon->ses->server); + cifs_sb = CIFS_FILE_SB(file); + + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + posix_lck = true; + + if (!lock && !unlock) { + /* + * if no lock or unlock then nothing to do since we do not + * know what it is + */ + rc = -EOPNOTSUPP; + free_xid(xid); + return rc; + } + + rc = cifs_setlk(file, fl, type, wait_flag, posix_lck, lock, unlock, + xid); + free_xid(xid); + return rc; + + +} + +int cifs_lock(struct file *file, int cmd, struct file_lock *flock) +{ + int rc, xid; + int lock = 0, unlock = 0; + bool wait_flag = false; + bool posix_lck = false; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + __u32 type; + + rc = -EACCES; + xid = get_xid(); + + cifs_dbg(FYI, "%s: %pD2 cmd=0x%x type=0x%x flags=0x%x r=%lld:%lld\n", __func__, file, cmd, + flock->fl_flags, flock->fl_type, (long long)flock->fl_start, + (long long)flock->fl_end); + + cfile = (struct cifsFileInfo *)file->private_data; + tcon = tlink_tcon(cfile->tlink); + + cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, + tcon->ses->server); + cifs_sb = CIFS_FILE_SB(file); + set_bit(CIFS_INO_CLOSE_ON_LOCK, &CIFS_I(d_inode(cfile->dentry))->flags); + + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + posix_lck = true; + /* + * BB add code here to normalize offset and length to account for + * negative length which we can not accept over the wire. + */ + if (IS_GETLK(cmd)) { + rc = cifs_getlk(file, flock, type, wait_flag, posix_lck, xid); + free_xid(xid); + return rc; + } + + if (!lock && !unlock) { + /* + * if no lock or unlock then nothing to do since we do not + * know what it is + */ + free_xid(xid); + return -EOPNOTSUPP; + } + + rc = cifs_setlk(file, flock, type, wait_flag, posix_lck, lock, unlock, + xid); + free_xid(xid); + return rc; +} + +/* + * update the file size (if needed) after a write. Should be called with + * the inode->i_lock held + */ +void +cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, + unsigned int bytes_written) +{ + loff_t end_of_write = offset + bytes_written; + + if (end_of_write > cifsi->server_eof) + cifsi->server_eof = end_of_write; +} + +static ssize_t +cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, + size_t write_size, loff_t *offset) +{ + int rc = 0; + unsigned int bytes_written = 0; + unsigned int total_written; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + unsigned int xid; + struct dentry *dentry = open_file->dentry; + struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry)); + struct cifs_io_parms io_parms = {0}; + + cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n", + write_size, *offset, dentry); + + tcon = tlink_tcon(open_file->tlink); + server = tcon->ses->server; + + if (!server->ops->sync_write) + return -ENOSYS; + + xid = get_xid(); + + for (total_written = 0; write_size > total_written; + total_written += bytes_written) { + rc = -EAGAIN; + while (rc == -EAGAIN) { + struct kvec iov[2]; + unsigned int len; + + if (open_file->invalidHandle) { + /* we could deadlock if we called + filemap_fdatawait from here so tell + reopen_file not to flush data to + server now */ + rc = cifs_reopen_file(open_file, false); + if (rc != 0) + break; + } + + len = min(server->ops->wp_retry_size(d_inode(dentry)), + (unsigned int)write_size - total_written); + /* iov[0] is reserved for smb header */ + iov[1].iov_base = (char *)write_data + total_written; + iov[1].iov_len = len; + io_parms.pid = pid; + io_parms.tcon = tcon; + io_parms.offset = *offset; + io_parms.length = len; + rc = server->ops->sync_write(xid, &open_file->fid, + &io_parms, &bytes_written, iov, 1); + } + if (rc || (bytes_written == 0)) { + if (total_written) + break; + else { + free_xid(xid); + return rc; + } + } else { + spin_lock(&d_inode(dentry)->i_lock); + cifs_update_eof(cifsi, *offset, bytes_written); + spin_unlock(&d_inode(dentry)->i_lock); + *offset += bytes_written; + } + } + + cifs_stats_bytes_written(tcon, total_written); + + if (total_written > 0) { + spin_lock(&d_inode(dentry)->i_lock); + if (*offset > d_inode(dentry)->i_size) { + i_size_write(d_inode(dentry), *offset); + d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9; + } + spin_unlock(&d_inode(dentry)->i_lock); + } + mark_inode_dirty_sync(d_inode(dentry)); + free_xid(xid); + return total_written; +} + +struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, + bool fsuid_only) +{ + struct cifsFileInfo *open_file = NULL; + struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); + + /* only filter by fsuid on multiuser mounts */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + fsuid_only = false; + + spin_lock(&cifs_inode->open_file_lock); + /* we could simply get the first_list_entry since write-only entries + are always at the end of the list but since the first entry might + have a close pending, we go through the whole list */ + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) + continue; + if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { + if ((!open_file->invalidHandle)) { + /* found a good file */ + /* lock it so it will not be closed on us */ + cifsFileInfo_get(open_file); + spin_unlock(&cifs_inode->open_file_lock); + return open_file; + } /* else might as well continue, and look for + another, or simply have the caller reopen it + again rather than trying to fix this handle */ + } else /* write only file */ + break; /* write only files are last so must be done */ + } + spin_unlock(&cifs_inode->open_file_lock); + return NULL; +} + +/* Return -EBADF if no handle is found and general rc otherwise */ +int +cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags, + struct cifsFileInfo **ret_file) +{ + struct cifsFileInfo *open_file, *inv_file = NULL; + struct cifs_sb_info *cifs_sb; + bool any_available = false; + int rc = -EBADF; + unsigned int refind = 0; + bool fsuid_only = flags & FIND_WR_FSUID_ONLY; + bool with_delete = flags & FIND_WR_WITH_DELETE; + *ret_file = NULL; + + /* + * Having a null inode here (because mapping->host was set to zero by + * the VFS or MM) should not happen but we had reports of on oops (due + * to it being zero) during stress testcases so we need to check for it + */ + + if (cifs_inode == NULL) { + cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n"); + dump_stack(); + return rc; + } + + cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); + + /* only filter by fsuid on multiuser mounts */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + fsuid_only = false; + + spin_lock(&cifs_inode->open_file_lock); +refind_writable: + if (refind > MAX_REOPEN_ATT) { + spin_unlock(&cifs_inode->open_file_lock); + return rc; + } + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (!any_available && open_file->pid != current->tgid) + continue; + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) + continue; + if (with_delete && !(open_file->fid.access & DELETE)) + continue; + if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { + if (!open_file->invalidHandle) { + /* found a good writable file */ + cifsFileInfo_get(open_file); + spin_unlock(&cifs_inode->open_file_lock); + *ret_file = open_file; + return 0; + } else { + if (!inv_file) + inv_file = open_file; + } + } + } + /* couldn't find useable FH with same pid, try any available */ + if (!any_available) { + any_available = true; + goto refind_writable; + } + + if (inv_file) { + any_available = false; + cifsFileInfo_get(inv_file); + } + + spin_unlock(&cifs_inode->open_file_lock); + + if (inv_file) { + rc = cifs_reopen_file(inv_file, false); + if (!rc) { + *ret_file = inv_file; + return 0; + } + + spin_lock(&cifs_inode->open_file_lock); + list_move_tail(&inv_file->flist, &cifs_inode->openFileList); + spin_unlock(&cifs_inode->open_file_lock); + cifsFileInfo_put(inv_file); + ++refind; + inv_file = NULL; + spin_lock(&cifs_inode->open_file_lock); + goto refind_writable; + } + + return rc; +} + +struct cifsFileInfo * +find_writable_file(struct cifsInodeInfo *cifs_inode, int flags) +{ + struct cifsFileInfo *cfile; + int rc; + + rc = cifs_get_writable_file(cifs_inode, flags, &cfile); + if (rc) + cifs_dbg(FYI, "Couldn't find writable handle rc=%d\n", rc); + + return cfile; +} + +int +cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, + int flags, + struct cifsFileInfo **ret_file) +{ + struct cifsFileInfo *cfile; + void *page = alloc_dentry_path(); + + *ret_file = NULL; + + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + struct cifsInodeInfo *cinode; + const char *full_path = build_path_from_dentry(cfile->dentry, page); + if (IS_ERR(full_path)) { + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return PTR_ERR(full_path); + } + if (strcmp(full_path, name)) + continue; + + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return cifs_get_writable_file(cinode, flags, ret_file); + } + + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return -ENOENT; +} + +int +cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file) +{ + struct cifsFileInfo *cfile; + void *page = alloc_dentry_path(); + + *ret_file = NULL; + + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + struct cifsInodeInfo *cinode; + const char *full_path = build_path_from_dentry(cfile->dentry, page); + if (IS_ERR(full_path)) { + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return PTR_ERR(full_path); + } + if (strcmp(full_path, name)) + continue; + + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + *ret_file = find_readable_file(cinode, 0); + return *ret_file ? 0 : -ENOENT; + } + + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return -ENOENT; +} + +void +cifs_writedata_release(struct kref *refcount) +{ + struct cifs_writedata *wdata = container_of(refcount, + struct cifs_writedata, refcount); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) { + smbd_deregister_mr(wdata->mr); + wdata->mr = NULL; + } +#endif + + if (wdata->cfile) + cifsFileInfo_put(wdata->cfile); + + kvfree(wdata->pages); + kfree(wdata); +} + +/* + * Write failed with a retryable error. Resend the write request. It's also + * possible that the page was redirtied so re-clean the page. + */ +static void +cifs_writev_requeue(struct cifs_writedata *wdata) +{ + int i, rc = 0; + struct inode *inode = d_inode(wdata->cfile->dentry); + struct TCP_Server_Info *server; + unsigned int rest_len; + + server = tlink_tcon(wdata->cfile->tlink)->ses->server; + i = 0; + rest_len = wdata->bytes; + do { + struct cifs_writedata *wdata2; + unsigned int j, nr_pages, wsize, tailsz, cur_len; + + wsize = server->ops->wp_retry_size(inode); + if (wsize < rest_len) { + nr_pages = wsize / PAGE_SIZE; + if (!nr_pages) { + rc = -EOPNOTSUPP; + break; + } + cur_len = nr_pages * PAGE_SIZE; + tailsz = PAGE_SIZE; + } else { + nr_pages = DIV_ROUND_UP(rest_len, PAGE_SIZE); + cur_len = rest_len; + tailsz = rest_len - (nr_pages - 1) * PAGE_SIZE; + } + + wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete); + if (!wdata2) { + rc = -ENOMEM; + break; + } + + for (j = 0; j < nr_pages; j++) { + wdata2->pages[j] = wdata->pages[i + j]; + lock_page(wdata2->pages[j]); + clear_page_dirty_for_io(wdata2->pages[j]); + } + + wdata2->sync_mode = wdata->sync_mode; + wdata2->nr_pages = nr_pages; + wdata2->offset = page_offset(wdata2->pages[0]); + wdata2->pagesz = PAGE_SIZE; + wdata2->tailsz = tailsz; + wdata2->bytes = cur_len; + + rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, + &wdata2->cfile); + if (!wdata2->cfile) { + cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n", + rc); + if (!is_retryable_error(rc)) + rc = -EBADF; + } else { + wdata2->pid = wdata2->cfile->pid; + rc = server->ops->async_writev(wdata2, + cifs_writedata_release); + } + + for (j = 0; j < nr_pages; j++) { + unlock_page(wdata2->pages[j]); + if (rc != 0 && !is_retryable_error(rc)) { + SetPageError(wdata2->pages[j]); + end_page_writeback(wdata2->pages[j]); + put_page(wdata2->pages[j]); + } + } + + kref_put(&wdata2->refcount, cifs_writedata_release); + if (rc) { + if (is_retryable_error(rc)) + continue; + i += nr_pages; + break; + } + + rest_len -= cur_len; + i += nr_pages; + } while (i < wdata->nr_pages); + + /* cleanup remaining pages from the original wdata */ + for (; i < wdata->nr_pages; i++) { + SetPageError(wdata->pages[i]); + end_page_writeback(wdata->pages[i]); + put_page(wdata->pages[i]); + } + + if (rc != 0 && !is_retryable_error(rc)) + mapping_set_error(inode->i_mapping, rc); + kref_put(&wdata->refcount, cifs_writedata_release); +} + +void +cifs_writev_complete(struct work_struct *work) +{ + struct cifs_writedata *wdata = container_of(work, + struct cifs_writedata, work); + struct inode *inode = d_inode(wdata->cfile->dentry); + int i = 0; + + if (wdata->result == 0) { + spin_lock(&inode->i_lock); + cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes); + spin_unlock(&inode->i_lock); + cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink), + wdata->bytes); + } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN) + return cifs_writev_requeue(wdata); + + for (i = 0; i < wdata->nr_pages; i++) { + struct page *page = wdata->pages[i]; + + if (wdata->result == -EAGAIN) + __set_page_dirty_nobuffers(page); + else if (wdata->result < 0) + SetPageError(page); + end_page_writeback(page); + cifs_readpage_to_fscache(inode, page); + put_page(page); + } + if (wdata->result != -EAGAIN) + mapping_set_error(inode->i_mapping, wdata->result); + kref_put(&wdata->refcount, cifs_writedata_release); +} + +struct cifs_writedata * +cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) +{ + struct cifs_writedata *writedata = NULL; + struct page **pages = + kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS); + if (pages) { + writedata = cifs_writedata_direct_alloc(pages, complete); + if (!writedata) + kvfree(pages); + } + + return writedata; +} + +struct cifs_writedata * +cifs_writedata_direct_alloc(struct page **pages, work_func_t complete) +{ + struct cifs_writedata *wdata; + + wdata = kzalloc(sizeof(*wdata), GFP_NOFS); + if (wdata != NULL) { + wdata->pages = pages; + kref_init(&wdata->refcount); + INIT_LIST_HEAD(&wdata->list); + init_completion(&wdata->done); + INIT_WORK(&wdata->work, complete); + } + return wdata; +} + + +static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) +{ + struct address_space *mapping = page->mapping; + loff_t offset = (loff_t)page->index << PAGE_SHIFT; + char *write_data; + int rc = -EFAULT; + int bytes_written = 0; + struct inode *inode; + struct cifsFileInfo *open_file; + + if (!mapping || !mapping->host) + return -EFAULT; + + inode = page->mapping->host; + + offset += (loff_t)from; + write_data = kmap(page); + write_data += from; + + if ((to > PAGE_SIZE) || (from > to)) { + kunmap(page); + return -EIO; + } + + /* racing with truncate? */ + if (offset > mapping->host->i_size) { + kunmap(page); + return 0; /* don't care */ + } + + /* check to make sure that we are not extending the file */ + if (mapping->host->i_size - offset < (loff_t)to) + to = (unsigned)(mapping->host->i_size - offset); + + rc = cifs_get_writable_file(CIFS_I(mapping->host), FIND_WR_ANY, + &open_file); + if (!rc) { + bytes_written = cifs_write(open_file, open_file->pid, + write_data, to - from, &offset); + cifsFileInfo_put(open_file); + /* Does mm or vfs already set times? */ + inode->i_atime = inode->i_mtime = current_time(inode); + if ((bytes_written > 0) && (offset)) + rc = 0; + else if (bytes_written < 0) + rc = bytes_written; + else + rc = -EFAULT; + } else { + cifs_dbg(FYI, "No writable handle for write page rc=%d\n", rc); + if (!is_retryable_error(rc)) + rc = -EIO; + } + + kunmap(page); + return rc; +} + +static struct cifs_writedata * +wdata_alloc_and_fillpages(pgoff_t tofind, struct address_space *mapping, + pgoff_t end, pgoff_t *index, + unsigned int *found_pages) +{ + struct cifs_writedata *wdata; + + wdata = cifs_writedata_alloc((unsigned int)tofind, + cifs_writev_complete); + if (!wdata) + return NULL; + + *found_pages = find_get_pages_range_tag(mapping, index, end, + PAGECACHE_TAG_DIRTY, tofind, wdata->pages); + return wdata; +} + +static unsigned int +wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages, + struct address_space *mapping, + struct writeback_control *wbc, + pgoff_t end, pgoff_t *index, pgoff_t *next, bool *done) +{ + unsigned int nr_pages = 0, i; + struct page *page; + + for (i = 0; i < found_pages; i++) { + page = wdata->pages[i]; + /* + * At this point we hold neither the i_pages lock nor the + * page lock: the page may be truncated or invalidated + * (changing page->mapping to NULL), or even swizzled + * back from swapper_space to tmpfs file mapping + */ + + if (nr_pages == 0) + lock_page(page); + else if (!trylock_page(page)) + break; + + if (unlikely(page->mapping != mapping)) { + unlock_page(page); + break; + } + + if (!wbc->range_cyclic && page->index > end) { + *done = true; + unlock_page(page); + break; + } + + if (*next && (page->index != *next)) { + /* Not next consecutive page */ + unlock_page(page); + break; + } + + if (wbc->sync_mode != WB_SYNC_NONE) + wait_on_page_writeback(page); + + if (PageWriteback(page) || + !clear_page_dirty_for_io(page)) { + unlock_page(page); + break; + } + + /* + * This actually clears the dirty bit in the radix tree. + * See cifs_writepage() for more commentary. + */ + set_page_writeback(page); + if (page_offset(page) >= i_size_read(mapping->host)) { + *done = true; + unlock_page(page); + end_page_writeback(page); + break; + } + + wdata->pages[i] = page; + *next = page->index + 1; + ++nr_pages; + } + + /* reset index to refind any pages skipped */ + if (nr_pages == 0) + *index = wdata->pages[0]->index + 1; + + /* put any pages we aren't going to use */ + for (i = nr_pages; i < found_pages; i++) { + put_page(wdata->pages[i]); + wdata->pages[i] = NULL; + } + + return nr_pages; +} + +static int +wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages, + struct address_space *mapping, struct writeback_control *wbc) +{ + int rc; + + wdata->sync_mode = wbc->sync_mode; + wdata->nr_pages = nr_pages; + wdata->offset = page_offset(wdata->pages[0]); + wdata->pagesz = PAGE_SIZE; + wdata->tailsz = min(i_size_read(mapping->host) - + page_offset(wdata->pages[nr_pages - 1]), + (loff_t)PAGE_SIZE); + wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz; + wdata->pid = wdata->cfile->pid; + + rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes); + if (rc) + return rc; + + if (wdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = wdata->server->ops->async_writev(wdata, + cifs_writedata_release); + + return rc; +} + +static int cifs_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct TCP_Server_Info *server; + bool done = false, scanned = false, range_whole = false; + pgoff_t end, index; + struct cifs_writedata *wdata; + struct cifsFileInfo *cfile = NULL; + int rc = 0; + int saved_rc = 0; + unsigned int xid; + + /* + * If wsize is smaller than the page cache size, default to writing + * one page at a time via cifs_writepage + */ + if (cifs_sb->ctx->wsize < PAGE_SIZE) + return generic_writepages(mapping, wbc); + + xid = get_xid(); + if (wbc->range_cyclic) { + index = mapping->writeback_index; /* Start from prev offset */ + end = -1; + } else { + index = wbc->range_start >> PAGE_SHIFT; + end = wbc->range_end >> PAGE_SHIFT; + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) + range_whole = true; + scanned = true; + } + server = cifs_pick_channel(cifs_sb_master_tcon(cifs_sb)->ses); + +retry: + while (!done && index <= end) { + unsigned int i, nr_pages, found_pages, wsize; + pgoff_t next = 0, tofind, saved_index = index; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + int get_file_rc = 0; + + if (cfile) + cifsFileInfo_put(cfile); + + rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile); + + /* in case of an error store it to return later */ + if (rc) + get_file_rc = rc; + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, + &wsize, credits); + if (rc != 0) { + done = true; + break; + } + + tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1; + + wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index, + &found_pages); + if (!wdata) { + rc = -ENOMEM; + done = true; + add_credits_and_wake_if(server, credits, 0); + break; + } + + if (found_pages == 0) { + kref_put(&wdata->refcount, cifs_writedata_release); + add_credits_and_wake_if(server, credits, 0); + break; + } + + nr_pages = wdata_prepare_pages(wdata, found_pages, mapping, wbc, + end, &index, &next, &done); + + /* nothing to write? */ + if (nr_pages == 0) { + kref_put(&wdata->refcount, cifs_writedata_release); + add_credits_and_wake_if(server, credits, 0); + continue; + } + + wdata->credits = credits_on_stack; + wdata->cfile = cfile; + wdata->server = server; + cfile = NULL; + + if (!wdata->cfile) { + cifs_dbg(VFS, "No writable handle in writepages rc=%d\n", + get_file_rc); + if (is_retryable_error(get_file_rc)) + rc = get_file_rc; + else + rc = -EBADF; + } else + rc = wdata_send_pages(wdata, nr_pages, mapping, wbc); + + for (i = 0; i < nr_pages; ++i) + unlock_page(wdata->pages[i]); + + /* send failure -- clean up the mess */ + if (rc != 0) { + add_credits_and_wake_if(server, &wdata->credits, 0); + for (i = 0; i < nr_pages; ++i) { + if (is_retryable_error(rc)) + redirty_page_for_writepage(wbc, + wdata->pages[i]); + else + SetPageError(wdata->pages[i]); + end_page_writeback(wdata->pages[i]); + put_page(wdata->pages[i]); + } + if (!is_retryable_error(rc)) + mapping_set_error(mapping, rc); + } + kref_put(&wdata->refcount, cifs_writedata_release); + + if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) { + index = saved_index; + continue; + } + + /* Return immediately if we received a signal during writing */ + if (is_interrupt_error(rc)) { + done = true; + break; + } + + if (rc != 0 && saved_rc == 0) + saved_rc = rc; + + wbc->nr_to_write -= nr_pages; + if (wbc->nr_to_write <= 0) + done = true; + + index = next; + } + + if (!scanned && !done) { + /* + * We hit the last page and there is more work to be done: wrap + * back to the start of the file + */ + scanned = true; + index = 0; + goto retry; + } + + if (saved_rc != 0) + rc = saved_rc; + + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) + mapping->writeback_index = index; + + if (cfile) + cifsFileInfo_put(cfile); + free_xid(xid); + /* Indication to update ctime and mtime as close is deferred */ + set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); + return rc; +} + +static int +cifs_writepage_locked(struct page *page, struct writeback_control *wbc) +{ + int rc; + unsigned int xid; + + xid = get_xid(); +/* BB add check for wbc flags */ + get_page(page); + if (!PageUptodate(page)) + cifs_dbg(FYI, "ppw - page not up to date\n"); + + /* + * Set the "writeback" flag, and clear "dirty" in the radix tree. + * + * A writepage() implementation always needs to do either this, + * or re-dirty the page with "redirty_page_for_writepage()" in + * the case of a failure. + * + * Just unlocking the page will cause the radix tree tag-bits + * to fail to update with the state of the page correctly. + */ + set_page_writeback(page); +retry_write: + rc = cifs_partialpagewrite(page, 0, PAGE_SIZE); + if (is_retryable_error(rc)) { + if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) + goto retry_write; + redirty_page_for_writepage(wbc, page); + } else if (rc != 0) { + SetPageError(page); + mapping_set_error(page->mapping, rc); + } else { + SetPageUptodate(page); + } + end_page_writeback(page); + put_page(page); + free_xid(xid); + return rc; +} + +static int cifs_writepage(struct page *page, struct writeback_control *wbc) +{ + int rc = cifs_writepage_locked(page, wbc); + unlock_page(page); + return rc; +} + +static int cifs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + int rc; + struct inode *inode = mapping->host; + struct cifsFileInfo *cfile = file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); + __u32 pid; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = cfile->pid; + else + pid = current->tgid; + + cifs_dbg(FYI, "write_end for page %p from pos %lld with %d bytes\n", + page, pos, copied); + + if (PageChecked(page)) { + if (copied == len) + SetPageUptodate(page); + ClearPageChecked(page); + } else if (!PageUptodate(page) && copied == PAGE_SIZE) + SetPageUptodate(page); + + if (!PageUptodate(page)) { + char *page_data; + unsigned offset = pos & (PAGE_SIZE - 1); + unsigned int xid; + + xid = get_xid(); + /* this is probably better than directly calling + partialpage_write since in this function the file handle is + known which we might as well leverage */ + /* BB check if anything else missing out of ppw + such as updating last write time */ + page_data = kmap(page); + rc = cifs_write(cfile, pid, page_data + offset, copied, &pos); + /* if (rc < 0) should we set writebehind rc? */ + kunmap(page); + + free_xid(xid); + } else { + rc = copied; + pos += copied; + set_page_dirty(page); + } + + if (rc > 0) { + spin_lock(&inode->i_lock); + if (pos > inode->i_size) { + i_size_write(inode, pos); + inode->i_blocks = (512 - 1 + pos) >> 9; + } + spin_unlock(&inode->i_lock); + } + + unlock_page(page); + put_page(page); + /* Indication to update ctime and mtime as close is deferred */ + set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); + + return rc; +} + +int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, + int datasync) +{ + unsigned int xid; + int rc = 0; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsFileInfo *smbfile = file->private_data; + struct inode *inode = file_inode(file); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + rc = file_write_and_wait_range(file, start, end); + if (rc) { + trace_cifs_fsync_err(inode->i_ino, rc); + return rc; + } + + xid = get_xid(); + + cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", + file, datasync); + + if (!CIFS_CACHE_READ(CIFS_I(inode))) { + rc = cifs_zap_mapping(inode); + if (rc) { + cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc); + rc = 0; /* don't care about it in fsync */ + } + } + + tcon = tlink_tcon(smbfile->tlink); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { + server = tcon->ses->server; + if (server->ops->flush == NULL) { + rc = -ENOSYS; + goto strict_fsync_exit; + } + + if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { + smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); + if (smbfile) { + rc = server->ops->flush(xid, tcon, &smbfile->fid); + cifsFileInfo_put(smbfile); + } else + cifs_dbg(FYI, "ignore fsync for file not open for write\n"); + } else + rc = server->ops->flush(xid, tcon, &smbfile->fid); + } + +strict_fsync_exit: + free_xid(xid); + return rc; +} + +int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + unsigned int xid; + int rc = 0; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsFileInfo *smbfile = file->private_data; + struct inode *inode = file_inode(file); + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + + rc = file_write_and_wait_range(file, start, end); + if (rc) { + trace_cifs_fsync_err(file_inode(file)->i_ino, rc); + return rc; + } + + xid = get_xid(); + + cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", + file, datasync); + + tcon = tlink_tcon(smbfile->tlink); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { + server = tcon->ses->server; + if (server->ops->flush == NULL) { + rc = -ENOSYS; + goto fsync_exit; + } + + if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { + smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); + if (smbfile) { + rc = server->ops->flush(xid, tcon, &smbfile->fid); + cifsFileInfo_put(smbfile); + } else + cifs_dbg(FYI, "ignore fsync for file not open for write\n"); + } else + rc = server->ops->flush(xid, tcon, &smbfile->fid); + } + +fsync_exit: + free_xid(xid); + return rc; +} + +/* + * As file closes, flush all cached write data for this inode checking + * for write behind errors. + */ +int cifs_flush(struct file *file, fl_owner_t id) +{ + struct inode *inode = file_inode(file); + int rc = 0; + + if (file->f_mode & FMODE_WRITE) + rc = filemap_write_and_wait(inode->i_mapping); + + cifs_dbg(FYI, "Flush inode %p file %p rc %d\n", inode, file, rc); + if (rc) { + /* get more nuanced writeback errors */ + rc = filemap_check_wb_err(file->f_mapping, 0); + trace_cifs_flush_err(inode->i_ino, rc); + } + return rc; +} + +static int +cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) +{ + int rc = 0; + unsigned long i; + + for (i = 0; i < num_pages; i++) { + pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!pages[i]) { + /* + * save number of pages we have already allocated and + * return with ENOMEM error + */ + num_pages = i; + rc = -ENOMEM; + break; + } + } + + if (rc) { + for (i = 0; i < num_pages; i++) + put_page(pages[i]); + } + return rc; +} + +static inline +size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) +{ + size_t num_pages; + size_t clen; + + clen = min_t(const size_t, len, wsize); + num_pages = DIV_ROUND_UP(clen, PAGE_SIZE); + + if (cur_len) + *cur_len = clen; + + return num_pages; +} + +static void +cifs_uncached_writedata_release(struct kref *refcount) +{ + int i; + struct cifs_writedata *wdata = container_of(refcount, + struct cifs_writedata, refcount); + + kref_put(&wdata->ctx->refcount, cifs_aio_ctx_release); + for (i = 0; i < wdata->nr_pages; i++) + put_page(wdata->pages[i]); + cifs_writedata_release(refcount); +} + +static void collect_uncached_write_data(struct cifs_aio_ctx *ctx); + +static void +cifs_uncached_writev_complete(struct work_struct *work) +{ + struct cifs_writedata *wdata = container_of(work, + struct cifs_writedata, work); + struct inode *inode = d_inode(wdata->cfile->dentry); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + spin_lock(&inode->i_lock); + cifs_update_eof(cifsi, wdata->offset, wdata->bytes); + if (cifsi->server_eof > inode->i_size) + i_size_write(inode, cifsi->server_eof); + spin_unlock(&inode->i_lock); + + complete(&wdata->done); + collect_uncached_write_data(wdata->ctx); + /* the below call can possibly free the last ref to aio ctx */ + kref_put(&wdata->refcount, cifs_uncached_writedata_release); +} + +static int +wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from, + size_t *len, unsigned long *num_pages) +{ + size_t save_len, copied, bytes, cur_len = *len; + unsigned long i, nr_pages = *num_pages; + + save_len = cur_len; + for (i = 0; i < nr_pages; i++) { + bytes = min_t(const size_t, cur_len, PAGE_SIZE); + copied = copy_page_from_iter(wdata->pages[i], 0, bytes, from); + cur_len -= copied; + /* + * If we didn't copy as much as we expected, then that + * may mean we trod into an unmapped area. Stop copying + * at that point. On the next pass through the big + * loop, we'll likely end up getting a zero-length + * write and bailing out of it. + */ + if (copied < bytes) + break; + } + cur_len = save_len - cur_len; + *len = cur_len; + + /* + * If we have no data to send, then that probably means that + * the copy above failed altogether. That's most likely because + * the address in the iovec was bogus. Return -EFAULT and let + * the caller free anything we allocated and bail out. + */ + if (!cur_len) + return -EFAULT; + + /* + * i + 1 now represents the number of pages we actually used in + * the copy phase above. + */ + *num_pages = i + 1; + return 0; +} + +static int +cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, + struct cifs_aio_ctx *ctx) +{ + unsigned int wsize; + struct cifs_credits credits; + int rc; + struct TCP_Server_Info *server = wdata->server; + + do { + if (wdata->cfile->invalidHandle) { + rc = cifs_reopen_file(wdata->cfile, false); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + + /* + * Wait for credits to resend this wdata. + * Note: we are attempting to resend the whole wdata not in + * segments + */ + do { + rc = server->ops->wait_mtu_credits(server, wdata->bytes, + &wsize, &credits); + if (rc) + goto fail; + + if (wsize < wdata->bytes) { + add_credits_and_wake_if(server, &credits, 0); + msleep(1000); + } + } while (wsize < wdata->bytes); + wdata->credits = credits; + + rc = adjust_credits(server, &wdata->credits, wdata->bytes); + + if (!rc) { + if (wdata->cfile->invalidHandle) + rc = -EAGAIN; + else { +#ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) { + wdata->mr->need_invalidate = true; + smbd_deregister_mr(wdata->mr); + wdata->mr = NULL; + } +#endif + rc = server->ops->async_writev(wdata, + cifs_uncached_writedata_release); + } + } + + /* If the write was successfully sent, we are done */ + if (!rc) { + list_add_tail(&wdata->list, wdata_list); + return 0; + } + + /* Roll back credits and retry if needed */ + add_credits_and_wake_if(server, &wdata->credits, 0); + } while (rc == -EAGAIN); + +fail: + kref_put(&wdata->refcount, cifs_uncached_writedata_release); + return rc; +} + +static int +cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, + struct cifsFileInfo *open_file, + struct cifs_sb_info *cifs_sb, struct list_head *wdata_list, + struct cifs_aio_ctx *ctx) +{ + int rc = 0; + size_t cur_len; + unsigned long nr_pages, num_pages, i; + struct cifs_writedata *wdata; + struct iov_iter saved_from = *from; + loff_t saved_offset = offset; + pid_t pid; + struct TCP_Server_Info *server; + struct page **pagevec; + size_t start; + unsigned int xid; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); + xid = get_xid(); + + do { + unsigned int wsize; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, false); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, + &wsize, credits); + if (rc) + break; + + cur_len = min_t(const size_t, len, wsize); + + if (ctx->direct_io) { + ssize_t result; + + result = iov_iter_get_pages_alloc2( + from, &pagevec, cur_len, &start); + if (result < 0) { + cifs_dbg(VFS, + "direct_writev couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", + result, iov_iter_type(from), + from->iov_offset, from->count); + dump_stack(); + + rc = result; + add_credits_and_wake_if(server, credits, 0); + break; + } + cur_len = (size_t)result; + + nr_pages = + (cur_len + start + PAGE_SIZE - 1) / PAGE_SIZE; + + wdata = cifs_writedata_direct_alloc(pagevec, + cifs_uncached_writev_complete); + if (!wdata) { + rc = -ENOMEM; + for (i = 0; i < nr_pages; i++) + put_page(pagevec[i]); + kvfree(pagevec); + add_credits_and_wake_if(server, credits, 0); + break; + } + + + wdata->page_offset = start; + wdata->tailsz = + nr_pages > 1 ? + cur_len - (PAGE_SIZE - start) - + (nr_pages - 2) * PAGE_SIZE : + cur_len; + } else { + nr_pages = get_numpages(wsize, len, &cur_len); + wdata = cifs_writedata_alloc(nr_pages, + cifs_uncached_writev_complete); + if (!wdata) { + rc = -ENOMEM; + add_credits_and_wake_if(server, credits, 0); + break; + } + + rc = cifs_write_allocate_pages(wdata->pages, nr_pages); + if (rc) { + kvfree(wdata->pages); + kfree(wdata); + add_credits_and_wake_if(server, credits, 0); + break; + } + + num_pages = nr_pages; + rc = wdata_fill_from_iovec( + wdata, from, &cur_len, &num_pages); + if (rc) { + for (i = 0; i < nr_pages; i++) + put_page(wdata->pages[i]); + kvfree(wdata->pages); + kfree(wdata); + add_credits_and_wake_if(server, credits, 0); + break; + } + + /* + * Bring nr_pages down to the number of pages we + * actually used, and free any pages that we didn't use. + */ + for ( ; nr_pages > num_pages; nr_pages--) + put_page(wdata->pages[nr_pages - 1]); + + wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); + } + + wdata->sync_mode = WB_SYNC_ALL; + wdata->nr_pages = nr_pages; + wdata->offset = (__u64)offset; + wdata->cfile = cifsFileInfo_get(open_file); + wdata->server = server; + wdata->pid = pid; + wdata->bytes = cur_len; + wdata->pagesz = PAGE_SIZE; + wdata->credits = credits_on_stack; + wdata->ctx = ctx; + kref_get(&ctx->refcount); + + rc = adjust_credits(server, &wdata->credits, wdata->bytes); + + if (!rc) { + if (wdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = server->ops->async_writev(wdata, + cifs_uncached_writedata_release); + } + + if (rc) { + add_credits_and_wake_if(server, &wdata->credits, 0); + kref_put(&wdata->refcount, + cifs_uncached_writedata_release); + if (rc == -EAGAIN) { + *from = saved_from; + iov_iter_advance(from, offset - saved_offset); + continue; + } + break; + } + + list_add_tail(&wdata->list, wdata_list); + offset += cur_len; + len -= cur_len; + } while (len > 0); + + free_xid(xid); + return rc; +} + +static void collect_uncached_write_data(struct cifs_aio_ctx *ctx) +{ + struct cifs_writedata *wdata, *tmp; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb; + struct dentry *dentry = ctx->cfile->dentry; + ssize_t rc; + + tcon = tlink_tcon(ctx->cfile->tlink); + cifs_sb = CIFS_SB(dentry->d_sb); + + mutex_lock(&ctx->aio_mutex); + + if (list_empty(&ctx->list)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + rc = ctx->rc; + /* + * Wait for and collect replies for any successful sends in order of + * increasing offset. Once an error is hit, then return without waiting + * for any more replies. + */ +restart_loop: + list_for_each_entry_safe(wdata, tmp, &ctx->list, list) { + if (!rc) { + if (!try_wait_for_completion(&wdata->done)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + if (wdata->result) + rc = wdata->result; + else + ctx->total_len += wdata->bytes; + + /* resend call if it's a retryable error */ + if (rc == -EAGAIN) { + struct list_head tmp_list; + struct iov_iter tmp_from = ctx->iter; + + INIT_LIST_HEAD(&tmp_list); + list_del_init(&wdata->list); + + if (ctx->direct_io) + rc = cifs_resend_wdata( + wdata, &tmp_list, ctx); + else { + iov_iter_advance(&tmp_from, + wdata->offset - ctx->pos); + + rc = cifs_write_from_iter(wdata->offset, + wdata->bytes, &tmp_from, + ctx->cfile, cifs_sb, &tmp_list, + ctx); + + kref_put(&wdata->refcount, + cifs_uncached_writedata_release); + } + + list_splice(&tmp_list, &ctx->list); + goto restart_loop; + } + } + list_del_init(&wdata->list); + kref_put(&wdata->refcount, cifs_uncached_writedata_release); + } + + cifs_stats_bytes_written(tcon, ctx->total_len); + set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(dentry->d_inode)->flags); + + ctx->rc = (rc == 0) ? ctx->total_len : rc; + + mutex_unlock(&ctx->aio_mutex); + + if (ctx->iocb && ctx->iocb->ki_complete) + ctx->iocb->ki_complete(ctx->iocb, ctx->rc); + else + complete(&ctx->done); +} + +static ssize_t __cifs_writev( + struct kiocb *iocb, struct iov_iter *from, bool direct) +{ + struct file *file = iocb->ki_filp; + ssize_t total_written = 0; + struct cifsFileInfo *cfile; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb; + struct cifs_aio_ctx *ctx; + struct iov_iter saved_from = *from; + size_t len = iov_iter_count(from); + int rc; + + /* + * iov_iter_get_pages_alloc doesn't work with ITER_KVEC. + * In this case, fall back to non-direct write function. + * this could be improved by getting pages directly in ITER_KVEC + */ + if (direct && iov_iter_is_kvec(from)) { + cifs_dbg(FYI, "use non-direct cifs_writev for kvec I/O\n"); + direct = false; + } + + rc = generic_write_checks(iocb, from); + if (rc <= 0) + return rc; + + cifs_sb = CIFS_FILE_SB(file); + cfile = file->private_data; + tcon = tlink_tcon(cfile->tlink); + + if (!tcon->ses->server->ops->async_writev) + return -ENOSYS; + + ctx = cifs_aio_ctx_alloc(); + if (!ctx) + return -ENOMEM; + + ctx->cfile = cifsFileInfo_get(cfile); + + if (!is_sync_kiocb(iocb)) + ctx->iocb = iocb; + + ctx->pos = iocb->ki_pos; + + if (direct) { + ctx->direct_io = true; + ctx->iter = *from; + ctx->len = len; + } else { + rc = setup_aio_ctx_iter(ctx, from, ITER_SOURCE); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + } + + /* grab a lock here due to read response handlers can access ctx */ + mutex_lock(&ctx->aio_mutex); + + rc = cifs_write_from_iter(iocb->ki_pos, ctx->len, &saved_from, + cfile, cifs_sb, &ctx->list, ctx); + + /* + * If at least one write was successfully sent, then discard any rc + * value from the later writes. If the other write succeeds, then + * we'll end up returning whatever was written. If it fails, then + * we'll get a new rc value from that. + */ + if (!list_empty(&ctx->list)) + rc = 0; + + mutex_unlock(&ctx->aio_mutex); + + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + + if (!is_sync_kiocb(iocb)) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return -EIOCBQUEUED; + } + + rc = wait_for_completion_killable(&ctx->done); + if (rc) { + mutex_lock(&ctx->aio_mutex); + ctx->rc = rc = -EINTR; + total_written = ctx->total_len; + mutex_unlock(&ctx->aio_mutex); + } else { + rc = ctx->rc; + total_written = ctx->total_len; + } + + kref_put(&ctx->refcount, cifs_aio_ctx_release); + + if (unlikely(!total_written)) + return rc; + + iocb->ki_pos += total_written; + return total_written; +} + +ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + + cifs_revalidate_mapping(file->f_inode); + return __cifs_writev(iocb, from, true); +} + +ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) +{ + return __cifs_writev(iocb, from, false); +} + +static ssize_t +cifs_writev(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; + struct inode *inode = file->f_mapping->host; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + ssize_t rc; + + inode_lock(inode); + /* + * We need to hold the sem to be sure nobody modifies lock list + * with a brlock that prevents writing. + */ + down_read(&cinode->lock_sem); + + rc = generic_write_checks(iocb, from); + if (rc <= 0) + goto out; + + if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from), + server->vals->exclusive_lock_type, 0, + NULL, CIFS_WRITE_OP)) + rc = __generic_file_write_iter(iocb, from); + else + rc = -EACCES; +out: + up_read(&cinode->lock_sem); + inode_unlock(inode); + + if (rc > 0) + rc = generic_write_sync(iocb, rc); + return rc; +} + +ssize_t +cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = (struct cifsFileInfo *) + iocb->ki_filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + ssize_t written; + + written = cifs_get_writer(cinode); + if (written) + return written; + + if (CIFS_CACHE_WRITE(cinode)) { + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) + && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) { + written = generic_file_write_iter(iocb, from); + goto out; + } + written = cifs_writev(iocb, from); + goto out; + } + /* + * For non-oplocked files in strict cache mode we need to write the data + * to the server exactly from the pos to pos+len-1 rather than flush all + * affected pages because it may cause a error with mandatory locks on + * these pages but not on the region from pos to ppos+len-1. + */ + written = cifs_user_writev(iocb, from); + if (CIFS_CACHE_READ(cinode)) { + /* + * We have read level caching and we have just sent a write + * request to the server thus making data in the cache stale. + * Zap the cache and set oplock/lease level to NONE to avoid + * reading stale data from the cache. All subsequent read + * operations will read new data from the server. + */ + cifs_zap_mapping(inode); + cifs_dbg(FYI, "Set Oplock/Lease to NONE for inode=%p after write\n", + inode); + cinode->oplock = 0; + } +out: + cifs_put_writer(cinode); + return written; +} + +static struct cifs_readdata * +cifs_readdata_direct_alloc(struct page **pages, work_func_t complete) +{ + struct cifs_readdata *rdata; + + rdata = kzalloc(sizeof(*rdata), GFP_KERNEL); + if (rdata != NULL) { + rdata->pages = pages; + kref_init(&rdata->refcount); + INIT_LIST_HEAD(&rdata->list); + init_completion(&rdata->done); + INIT_WORK(&rdata->work, complete); + } + + return rdata; +} + +static struct cifs_readdata * +cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete) +{ + struct page **pages = + kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); + struct cifs_readdata *ret = NULL; + + if (pages) { + ret = cifs_readdata_direct_alloc(pages, complete); + if (!ret) + kfree(pages); + } + + return ret; +} + +void +cifs_readdata_release(struct kref *refcount) +{ + struct cifs_readdata *rdata = container_of(refcount, + struct cifs_readdata, refcount); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (rdata->mr) { + smbd_deregister_mr(rdata->mr); + rdata->mr = NULL; + } +#endif + if (rdata->cfile) + cifsFileInfo_put(rdata->cfile); + + kvfree(rdata->pages); + kfree(rdata); +} + +static int +cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages) +{ + int rc = 0; + struct page *page; + unsigned int i; + + for (i = 0; i < nr_pages; i++) { + page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!page) { + rc = -ENOMEM; + break; + } + rdata->pages[i] = page; + } + + if (rc) { + unsigned int nr_page_failed = i; + + for (i = 0; i < nr_page_failed; i++) { + put_page(rdata->pages[i]); + rdata->pages[i] = NULL; + } + } + return rc; +} + +static void +cifs_uncached_readdata_release(struct kref *refcount) +{ + struct cifs_readdata *rdata = container_of(refcount, + struct cifs_readdata, refcount); + unsigned int i; + + kref_put(&rdata->ctx->refcount, cifs_aio_ctx_release); + for (i = 0; i < rdata->nr_pages; i++) { + put_page(rdata->pages[i]); + } + cifs_readdata_release(refcount); +} + +/** + * cifs_readdata_to_iov - copy data from pages in response to an iovec + * @rdata: the readdata response with list of pages holding data + * @iter: destination for our data + * + * This function copies data from a list of pages in a readdata response into + * an array of iovecs. It will first calculate where the data should go + * based on the info in the readdata and then copy the data into that spot. + */ +static int +cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter) +{ + size_t remaining = rdata->got_bytes; + unsigned int i; + + for (i = 0; i < rdata->nr_pages; i++) { + struct page *page = rdata->pages[i]; + size_t copy = min_t(size_t, remaining, PAGE_SIZE); + size_t written; + + if (unlikely(iov_iter_is_pipe(iter))) { + void *addr = kmap_atomic(page); + + written = copy_to_iter(addr, copy, iter); + kunmap_atomic(addr); + } else + written = copy_page_to_iter(page, 0, copy, iter); + remaining -= written; + if (written < copy && iov_iter_count(iter) > 0) + break; + } + return remaining ? -EFAULT : 0; +} + +static void collect_uncached_read_data(struct cifs_aio_ctx *ctx); + +static void +cifs_uncached_readv_complete(struct work_struct *work) +{ + struct cifs_readdata *rdata = container_of(work, + struct cifs_readdata, work); + + complete(&rdata->done); + collect_uncached_read_data(rdata->ctx); + /* the below call can possibly free the last ref to aio ctx */ + kref_put(&rdata->refcount, cifs_uncached_readdata_release); +} + +static int +uncached_fill_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, struct iov_iter *iter, + unsigned int len) +{ + int result = 0; + unsigned int i; + unsigned int nr_pages = rdata->nr_pages; + unsigned int page_offset = rdata->page_offset; + + rdata->got_bytes = 0; + rdata->tailsz = PAGE_SIZE; + for (i = 0; i < nr_pages; i++) { + struct page *page = rdata->pages[i]; + size_t n; + unsigned int segment_size = rdata->pagesz; + + if (i == 0) + segment_size -= page_offset; + else + page_offset = 0; + + + if (len <= 0) { + /* no need to hold page hostage */ + rdata->pages[i] = NULL; + rdata->nr_pages--; + put_page(page); + continue; + } + + n = len; + if (len >= segment_size) + /* enough data to fill the page */ + n = segment_size; + else + rdata->tailsz = len; + len -= n; + + if (iter) + result = copy_page_from_iter( + page, page_offset, n, iter); +#ifdef CONFIG_CIFS_SMB_DIRECT + else if (rdata->mr) + result = n; +#endif + else + result = cifs_read_page_from_socket( + server, page, page_offset, n); + if (result < 0) + break; + + rdata->got_bytes += result; + } + + return result != -ECONNABORTED && rdata->got_bytes > 0 ? + rdata->got_bytes : result; +} + +static int +cifs_uncached_read_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, unsigned int len) +{ + return uncached_fill_pages(server, rdata, NULL, len); +} + +static int +cifs_uncached_copy_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + struct iov_iter *iter) +{ + return uncached_fill_pages(server, rdata, iter, iter->count); +} + +static int cifs_resend_rdata(struct cifs_readdata *rdata, + struct list_head *rdata_list, + struct cifs_aio_ctx *ctx) +{ + unsigned int rsize; + struct cifs_credits credits; + int rc; + struct TCP_Server_Info *server; + + /* XXX: should we pick a new channel here? */ + server = rdata->server; + + do { + if (rdata->cfile->invalidHandle) { + rc = cifs_reopen_file(rdata->cfile, true); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + /* + * Wait for credits to resend this rdata. + * Note: we are attempting to resend the whole rdata not in + * segments + */ + do { + rc = server->ops->wait_mtu_credits(server, rdata->bytes, + &rsize, &credits); + + if (rc) + goto fail; + + if (rsize < rdata->bytes) { + add_credits_and_wake_if(server, &credits, 0); + msleep(1000); + } + } while (rsize < rdata->bytes); + rdata->credits = credits; + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + if (!rc) { + if (rdata->cfile->invalidHandle) + rc = -EAGAIN; + else { +#ifdef CONFIG_CIFS_SMB_DIRECT + if (rdata->mr) { + rdata->mr->need_invalidate = true; + smbd_deregister_mr(rdata->mr); + rdata->mr = NULL; + } +#endif + rc = server->ops->async_readv(rdata); + } + } + + /* If the read was successfully sent, we are done */ + if (!rc) { + /* Add to aio pending list */ + list_add_tail(&rdata->list, rdata_list); + return 0; + } + + /* Roll back credits and retry if needed */ + add_credits_and_wake_if(server, &rdata->credits, 0); + } while (rc == -EAGAIN); + +fail: + kref_put(&rdata->refcount, cifs_uncached_readdata_release); + return rc; +} + +static int +cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, + struct cifs_sb_info *cifs_sb, struct list_head *rdata_list, + struct cifs_aio_ctx *ctx) +{ + struct cifs_readdata *rdata; + unsigned int npages, rsize; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + size_t cur_len; + int rc; + pid_t pid; + struct TCP_Server_Info *server; + struct page **pagevec; + size_t start; + struct iov_iter direct_iov = ctx->iter; + + server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + if (ctx->direct_io) + iov_iter_advance(&direct_iov, offset - ctx->pos); + + do { + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, true); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + if (cifs_sb->ctx->rsize == 0) + cifs_sb->ctx->rsize = + server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), + cifs_sb->ctx); + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, + &rsize, credits); + if (rc) + break; + + cur_len = min_t(const size_t, len, rsize); + + if (ctx->direct_io) { + ssize_t result; + + result = iov_iter_get_pages_alloc2( + &direct_iov, &pagevec, + cur_len, &start); + if (result < 0) { + cifs_dbg(VFS, + "Couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", + result, iov_iter_type(&direct_iov), + direct_iov.iov_offset, + direct_iov.count); + dump_stack(); + + rc = result; + add_credits_and_wake_if(server, credits, 0); + break; + } + cur_len = (size_t)result; + + rdata = cifs_readdata_direct_alloc( + pagevec, cifs_uncached_readv_complete); + if (!rdata) { + add_credits_and_wake_if(server, credits, 0); + rc = -ENOMEM; + break; + } + + npages = (cur_len + start + PAGE_SIZE-1) / PAGE_SIZE; + rdata->page_offset = start; + rdata->tailsz = npages > 1 ? + cur_len-(PAGE_SIZE-start)-(npages-2)*PAGE_SIZE : + cur_len; + + } else { + + npages = DIV_ROUND_UP(cur_len, PAGE_SIZE); + /* allocate a readdata struct */ + rdata = cifs_readdata_alloc(npages, + cifs_uncached_readv_complete); + if (!rdata) { + add_credits_and_wake_if(server, credits, 0); + rc = -ENOMEM; + break; + } + + rc = cifs_read_allocate_pages(rdata, npages); + if (rc) { + kvfree(rdata->pages); + kfree(rdata); + add_credits_and_wake_if(server, credits, 0); + break; + } + + rdata->tailsz = PAGE_SIZE; + } + + rdata->server = server; + rdata->cfile = cifsFileInfo_get(open_file); + rdata->nr_pages = npages; + rdata->offset = offset; + rdata->bytes = cur_len; + rdata->pid = pid; + rdata->pagesz = PAGE_SIZE; + rdata->read_into_pages = cifs_uncached_read_into_pages; + rdata->copy_into_pages = cifs_uncached_copy_into_pages; + rdata->credits = credits_on_stack; + rdata->ctx = ctx; + kref_get(&ctx->refcount); + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + + if (!rc) { + if (rdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = server->ops->async_readv(rdata); + } + + if (rc) { + add_credits_and_wake_if(server, &rdata->credits, 0); + kref_put(&rdata->refcount, + cifs_uncached_readdata_release); + if (rc == -EAGAIN) { + iov_iter_revert(&direct_iov, cur_len); + continue; + } + break; + } + + list_add_tail(&rdata->list, rdata_list); + offset += cur_len; + len -= cur_len; + } while (len > 0); + + return rc; +} + +static void +collect_uncached_read_data(struct cifs_aio_ctx *ctx) +{ + struct cifs_readdata *rdata, *tmp; + struct iov_iter *to = &ctx->iter; + struct cifs_sb_info *cifs_sb; + int rc; + + cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb); + + mutex_lock(&ctx->aio_mutex); + + if (list_empty(&ctx->list)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + rc = ctx->rc; + /* the loop below should proceed in the order of increasing offsets */ +again: + list_for_each_entry_safe(rdata, tmp, &ctx->list, list) { + if (!rc) { + if (!try_wait_for_completion(&rdata->done)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + if (rdata->result == -EAGAIN) { + /* resend call if it's a retryable error */ + struct list_head tmp_list; + unsigned int got_bytes = rdata->got_bytes; + + list_del_init(&rdata->list); + INIT_LIST_HEAD(&tmp_list); + + /* + * Got a part of data and then reconnect has + * happened -- fill the buffer and continue + * reading. + */ + if (got_bytes && got_bytes < rdata->bytes) { + rc = 0; + if (!ctx->direct_io) + rc = cifs_readdata_to_iov(rdata, to); + if (rc) { + kref_put(&rdata->refcount, + cifs_uncached_readdata_release); + continue; + } + } + + if (ctx->direct_io) { + /* + * Re-use rdata as this is a + * direct I/O + */ + rc = cifs_resend_rdata( + rdata, + &tmp_list, ctx); + } else { + rc = cifs_send_async_read( + rdata->offset + got_bytes, + rdata->bytes - got_bytes, + rdata->cfile, cifs_sb, + &tmp_list, ctx); + + kref_put(&rdata->refcount, + cifs_uncached_readdata_release); + } + + list_splice(&tmp_list, &ctx->list); + + goto again; + } else if (rdata->result) + rc = rdata->result; + else if (!ctx->direct_io) + rc = cifs_readdata_to_iov(rdata, to); + + /* if there was a short read -- discard anything left */ + if (rdata->got_bytes && rdata->got_bytes < rdata->bytes) + rc = -ENODATA; + + ctx->total_len += rdata->got_bytes; + } + list_del_init(&rdata->list); + kref_put(&rdata->refcount, cifs_uncached_readdata_release); + } + + if (!ctx->direct_io) + ctx->total_len = ctx->len - iov_iter_count(to); + + /* mask nodata case */ + if (rc == -ENODATA) + rc = 0; + + ctx->rc = (rc == 0) ? (ssize_t)ctx->total_len : rc; + + mutex_unlock(&ctx->aio_mutex); + + if (ctx->iocb && ctx->iocb->ki_complete) + ctx->iocb->ki_complete(ctx->iocb, ctx->rc); + else + complete(&ctx->done); +} + +static ssize_t __cifs_readv( + struct kiocb *iocb, struct iov_iter *to, bool direct) +{ + size_t len; + struct file *file = iocb->ki_filp; + struct cifs_sb_info *cifs_sb; + struct cifsFileInfo *cfile; + struct cifs_tcon *tcon; + ssize_t rc, total_read = 0; + loff_t offset = iocb->ki_pos; + struct cifs_aio_ctx *ctx; + + /* + * iov_iter_get_pages_alloc() doesn't work with ITER_KVEC, + * fall back to data copy read path + * this could be improved by getting pages directly in ITER_KVEC + */ + if (direct && iov_iter_is_kvec(to)) { + cifs_dbg(FYI, "use non-direct cifs_user_readv for kvec I/O\n"); + direct = false; + } + + len = iov_iter_count(to); + if (!len) + return 0; + + cifs_sb = CIFS_FILE_SB(file); + cfile = file->private_data; + tcon = tlink_tcon(cfile->tlink); + + if (!tcon->ses->server->ops->async_readv) + return -ENOSYS; + + if ((file->f_flags & O_ACCMODE) == O_WRONLY) + cifs_dbg(FYI, "attempting read on write only file instance\n"); + + ctx = cifs_aio_ctx_alloc(); + if (!ctx) + return -ENOMEM; + + ctx->cfile = cifsFileInfo_get(cfile); + + if (!is_sync_kiocb(iocb)) + ctx->iocb = iocb; + + if (user_backed_iter(to)) + ctx->should_dirty = true; + + if (direct) { + ctx->pos = offset; + ctx->direct_io = true; + ctx->iter = *to; + ctx->len = len; + } else { + rc = setup_aio_ctx_iter(ctx, to, ITER_DEST); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + len = ctx->len; + } + + if (direct) { + rc = filemap_write_and_wait_range(file->f_inode->i_mapping, + offset, offset + len - 1); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return -EAGAIN; + } + } + + /* grab a lock here due to read response handlers can access ctx */ + mutex_lock(&ctx->aio_mutex); + + rc = cifs_send_async_read(offset, len, cfile, cifs_sb, &ctx->list, ctx); + + /* if at least one read request send succeeded, then reset rc */ + if (!list_empty(&ctx->list)) + rc = 0; + + mutex_unlock(&ctx->aio_mutex); + + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + + if (!is_sync_kiocb(iocb)) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return -EIOCBQUEUED; + } + + rc = wait_for_completion_killable(&ctx->done); + if (rc) { + mutex_lock(&ctx->aio_mutex); + ctx->rc = rc = -EINTR; + total_read = ctx->total_len; + mutex_unlock(&ctx->aio_mutex); + } else { + rc = ctx->rc; + total_read = ctx->total_len; + } + + kref_put(&ctx->refcount, cifs_aio_ctx_release); + + if (total_read) { + iocb->ki_pos += total_read; + return total_read; + } + return rc; +} + +ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to) +{ + return __cifs_readv(iocb, to, true); +} + +ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to) +{ + return __cifs_readv(iocb, to, false); +} + +ssize_t +cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = (struct cifsFileInfo *) + iocb->ki_filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + int rc = -EACCES; + + /* + * In strict cache mode we need to read from the server all the time + * if we don't have level II oplock because the server can delay mtime + * change - so we can't make a decision about inode invalidating. + * And we can also fail with pagereading if there are mandatory locks + * on pages affected by this read but not on the region from pos to + * pos+len-1. + */ + if (!CIFS_CACHE_READ(cinode)) + return cifs_user_readv(iocb, to); + + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + return generic_file_read_iter(iocb, to); + + /* + * We need to hold the sem to be sure nobody modifies lock list + * with a brlock that prevents reading. + */ + down_read(&cinode->lock_sem); + if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to), + tcon->ses->server->vals->shared_lock_type, + 0, NULL, CIFS_READ_OP)) + rc = generic_file_read_iter(iocb, to); + up_read(&cinode->lock_sem); + return rc; +} + +static ssize_t +cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset) +{ + int rc = -EACCES; + unsigned int bytes_read = 0; + unsigned int total_read; + unsigned int current_read_size; + unsigned int rsize; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + unsigned int xid; + char *cur_offset; + struct cifsFileInfo *open_file; + struct cifs_io_parms io_parms = {0}; + int buf_type = CIFS_NO_BUFFER; + __u32 pid; + + xid = get_xid(); + cifs_sb = CIFS_FILE_SB(file); + + /* FIXME: set up handlers for larger reads and/or convert to async */ + rsize = min_t(unsigned int, cifs_sb->ctx->rsize, CIFSMaxBufSize); + + if (file->private_data == NULL) { + rc = -EBADF; + free_xid(xid); + return rc; + } + open_file = file->private_data; + tcon = tlink_tcon(open_file->tlink); + server = cifs_pick_channel(tcon->ses); + + if (!server->ops->sync_read) { + free_xid(xid); + return -ENOSYS; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + if ((file->f_flags & O_ACCMODE) == O_WRONLY) + cifs_dbg(FYI, "attempting read on write only file instance\n"); + + for (total_read = 0, cur_offset = read_data; read_size > total_read; + total_read += bytes_read, cur_offset += bytes_read) { + do { + current_read_size = min_t(uint, read_size - total_read, + rsize); + /* + * For windows me and 9x we do not want to request more + * than it negotiated since it will refuse the read + * then. + */ + if (!(tcon->ses->capabilities & + tcon->ses->server->vals->cap_large_files)) { + current_read_size = min_t(uint, + current_read_size, CIFSMaxBufSize); + } + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, true); + if (rc != 0) + break; + } + io_parms.pid = pid; + io_parms.tcon = tcon; + io_parms.offset = *offset; + io_parms.length = current_read_size; + io_parms.server = server; + rc = server->ops->sync_read(xid, &open_file->fid, &io_parms, + &bytes_read, &cur_offset, + &buf_type); + } while (rc == -EAGAIN); + + if (rc || (bytes_read == 0)) { + if (total_read) { + break; + } else { + free_xid(xid); + return rc; + } + } else { + cifs_stats_bytes_read(tcon, total_read); + *offset += bytes_read; + } + } + free_xid(xid); + return total_read; +} + +/* + * If the page is mmap'ed into a process' page tables, then we need to make + * sure that it doesn't change while being written back. + */ +static vm_fault_t +cifs_page_mkwrite(struct vm_fault *vmf) +{ + struct page *page = vmf->page; + + /* Wait for the page to be written to the cache before we allow it to + * be modified. We then assume the entire page will need writing back. + */ +#ifdef CONFIG_CIFS_FSCACHE + if (PageFsCache(page) && + wait_on_page_fscache_killable(page) < 0) + return VM_FAULT_RETRY; +#endif + + wait_on_page_writeback(page); + + if (lock_page_killable(page) < 0) + return VM_FAULT_RETRY; + return VM_FAULT_LOCKED; +} + +static const struct vm_operations_struct cifs_file_vm_ops = { + .fault = filemap_fault, + .map_pages = filemap_map_pages, + .page_mkwrite = cifs_page_mkwrite, +}; + +int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) +{ + int xid, rc = 0; + struct inode *inode = file_inode(file); + + xid = get_xid(); + + if (!CIFS_CACHE_READ(CIFS_I(inode))) + rc = cifs_zap_mapping(inode); + if (!rc) + rc = generic_file_mmap(file, vma); + if (!rc) + vma->vm_ops = &cifs_file_vm_ops; + + free_xid(xid); + return rc; +} + +int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + int rc, xid; + + xid = get_xid(); + + rc = cifs_revalidate_file(file); + if (rc) + cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n", + rc); + if (!rc) + rc = generic_file_mmap(file, vma); + if (!rc) + vma->vm_ops = &cifs_file_vm_ops; + + free_xid(xid); + return rc; +} + +static void +cifs_readv_complete(struct work_struct *work) +{ + unsigned int i, got_bytes; + struct cifs_readdata *rdata = container_of(work, + struct cifs_readdata, work); + + got_bytes = rdata->got_bytes; + for (i = 0; i < rdata->nr_pages; i++) { + struct page *page = rdata->pages[i]; + + if (rdata->result == 0 || + (rdata->result == -EAGAIN && got_bytes)) { + flush_dcache_page(page); + SetPageUptodate(page); + } else + SetPageError(page); + + if (rdata->result == 0 || + (rdata->result == -EAGAIN && got_bytes)) + cifs_readpage_to_fscache(rdata->mapping->host, page); + + unlock_page(page); + + got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes); + + put_page(page); + rdata->pages[i] = NULL; + } + kref_put(&rdata->refcount, cifs_readdata_release); +} + +static int +readpages_fill_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, struct iov_iter *iter, + unsigned int len) +{ + int result = 0; + unsigned int i; + u64 eof; + pgoff_t eof_index; + unsigned int nr_pages = rdata->nr_pages; + unsigned int page_offset = rdata->page_offset; + + /* determine the eof that the server (probably) has */ + eof = CIFS_I(rdata->mapping->host)->server_eof; + eof_index = eof ? (eof - 1) >> PAGE_SHIFT : 0; + cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index); + + rdata->got_bytes = 0; + rdata->tailsz = PAGE_SIZE; + for (i = 0; i < nr_pages; i++) { + struct page *page = rdata->pages[i]; + unsigned int to_read = rdata->pagesz; + size_t n; + + if (i == 0) + to_read -= page_offset; + else + page_offset = 0; + + n = to_read; + + if (len >= to_read) { + len -= to_read; + } else if (len > 0) { + /* enough for partial page, fill and zero the rest */ + zero_user(page, len + page_offset, to_read - len); + n = rdata->tailsz = len; + len = 0; + } else if (page->index > eof_index) { + /* + * The VFS will not try to do readahead past the + * i_size, but it's possible that we have outstanding + * writes with gaps in the middle and the i_size hasn't + * caught up yet. Populate those with zeroed out pages + * to prevent the VFS from repeatedly attempting to + * fill them until the writes are flushed. + */ + zero_user(page, 0, PAGE_SIZE); + flush_dcache_page(page); + SetPageUptodate(page); + unlock_page(page); + put_page(page); + rdata->pages[i] = NULL; + rdata->nr_pages--; + continue; + } else { + /* no need to hold page hostage */ + unlock_page(page); + put_page(page); + rdata->pages[i] = NULL; + rdata->nr_pages--; + continue; + } + + if (iter) + result = copy_page_from_iter( + page, page_offset, n, iter); +#ifdef CONFIG_CIFS_SMB_DIRECT + else if (rdata->mr) + result = n; +#endif + else + result = cifs_read_page_from_socket( + server, page, page_offset, n); + if (result < 0) + break; + + rdata->got_bytes += result; + } + + return result != -ECONNABORTED && rdata->got_bytes > 0 ? + rdata->got_bytes : result; +} + +static int +cifs_readpages_read_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, unsigned int len) +{ + return readpages_fill_pages(server, rdata, NULL, len); +} + +static int +cifs_readpages_copy_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + struct iov_iter *iter) +{ + return readpages_fill_pages(server, rdata, iter, iter->count); +} + +static void cifs_readahead(struct readahead_control *ractl) +{ + int rc; + struct cifsFileInfo *open_file = ractl->file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(ractl->file); + struct TCP_Server_Info *server; + pid_t pid; + unsigned int xid, nr_pages, last_batch_size = 0, cache_nr_pages = 0; + pgoff_t next_cached = ULONG_MAX; + bool caching = fscache_cookie_enabled(cifs_inode_cookie(ractl->mapping->host)) && + cifs_inode_cookie(ractl->mapping->host)->cache_priv; + bool check_cache = caching; + + xid = get_xid(); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + rc = 0; + server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); + + cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n", + __func__, ractl->file, ractl->mapping, readahead_count(ractl)); + + /* + * Chop the readahead request up into rsize-sized read requests. + */ + while ((nr_pages = readahead_count(ractl) - last_batch_size)) { + unsigned int i, got, rsize; + struct page *page; + struct cifs_readdata *rdata; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + pgoff_t index = readahead_index(ractl) + last_batch_size; + + /* + * Find out if we have anything cached in the range of + * interest, and if so, where the next chunk of cached data is. + */ + if (caching) { + if (check_cache) { + rc = cifs_fscache_query_occupancy( + ractl->mapping->host, index, nr_pages, + &next_cached, &cache_nr_pages); + if (rc < 0) + caching = false; + check_cache = false; + } + + if (index == next_cached) { + /* + * TODO: Send a whole batch of pages to be read + * by the cache. + */ + struct folio *folio = readahead_folio(ractl); + + last_batch_size = folio_nr_pages(folio); + if (cifs_readpage_from_fscache(ractl->mapping->host, + &folio->page) < 0) { + /* + * TODO: Deal with cache read failure + * here, but for the moment, delegate + * that to readpage. + */ + caching = false; + } + folio_unlock(folio); + next_cached++; + cache_nr_pages--; + if (cache_nr_pages == 0) + check_cache = true; + continue; + } + } + + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, true); + if (rc) { + if (rc == -EAGAIN) + continue; + break; + } + } + + if (cifs_sb->ctx->rsize == 0) + cifs_sb->ctx->rsize = + server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), + cifs_sb->ctx); + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, + &rsize, credits); + if (rc) + break; + nr_pages = min_t(size_t, rsize / PAGE_SIZE, readahead_count(ractl)); + nr_pages = min_t(size_t, nr_pages, next_cached - index); + + /* + * Give up immediately if rsize is too small to read an entire + * page. The VFS will fall back to readpage. We should never + * reach this point however since we set ra_pages to 0 when the + * rsize is smaller than a cache page. + */ + if (unlikely(!nr_pages)) { + add_credits_and_wake_if(server, credits, 0); + break; + } + + rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete); + if (!rdata) { + /* best to give up if we're out of mem */ + add_credits_and_wake_if(server, credits, 0); + break; + } + + got = __readahead_batch(ractl, rdata->pages, nr_pages); + if (got != nr_pages) { + pr_warn("__readahead_batch() returned %u/%u\n", + got, nr_pages); + nr_pages = got; + } + + rdata->nr_pages = nr_pages; + rdata->bytes = readahead_batch_length(ractl); + rdata->cfile = cifsFileInfo_get(open_file); + rdata->server = server; + rdata->mapping = ractl->mapping; + rdata->offset = readahead_pos(ractl); + rdata->pid = pid; + rdata->pagesz = PAGE_SIZE; + rdata->tailsz = PAGE_SIZE; + rdata->read_into_pages = cifs_readpages_read_into_pages; + rdata->copy_into_pages = cifs_readpages_copy_into_pages; + rdata->credits = credits_on_stack; + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + if (!rc) { + if (rdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = server->ops->async_readv(rdata); + } + + if (rc) { + add_credits_and_wake_if(server, &rdata->credits, 0); + for (i = 0; i < rdata->nr_pages; i++) { + page = rdata->pages[i]; + unlock_page(page); + put_page(page); + } + /* Fallback to the readpage in error/reconnect cases */ + kref_put(&rdata->refcount, cifs_readdata_release); + break; + } + + kref_put(&rdata->refcount, cifs_readdata_release); + last_batch_size = nr_pages; + } + + free_xid(xid); +} + +/* + * cifs_readpage_worker must be called with the page pinned + */ +static int cifs_readpage_worker(struct file *file, struct page *page, + loff_t *poffset) +{ + char *read_data; + int rc; + + /* Is the page cached? */ + rc = cifs_readpage_from_fscache(file_inode(file), page); + if (rc == 0) + goto read_complete; + + read_data = kmap(page); + /* for reads over a certain size could initiate async read ahead */ + + rc = cifs_read(file, read_data, PAGE_SIZE, poffset); + + if (rc < 0) + goto io_error; + else + cifs_dbg(FYI, "Bytes read %d\n", rc); + + /* we do not want atime to be less than mtime, it broke some apps */ + file_inode(file)->i_atime = current_time(file_inode(file)); + if (timespec64_compare(&(file_inode(file)->i_atime), &(file_inode(file)->i_mtime))) + file_inode(file)->i_atime = file_inode(file)->i_mtime; + else + file_inode(file)->i_atime = current_time(file_inode(file)); + + if (PAGE_SIZE > rc) + memset(read_data + rc, 0, PAGE_SIZE - rc); + + flush_dcache_page(page); + SetPageUptodate(page); + + /* send this page to the cache */ + cifs_readpage_to_fscache(file_inode(file), page); + + rc = 0; + +io_error: + kunmap(page); + unlock_page(page); + +read_complete: + return rc; +} + +static int cifs_read_folio(struct file *file, struct folio *folio) +{ + struct page *page = &folio->page; + loff_t offset = page_file_offset(page); + int rc = -EACCES; + unsigned int xid; + + xid = get_xid(); + + if (file->private_data == NULL) { + rc = -EBADF; + free_xid(xid); + return rc; + } + + cifs_dbg(FYI, "read_folio %p at offset %d 0x%x\n", + page, (int)offset, (int)offset); + + rc = cifs_readpage_worker(file, page, &offset); + + free_xid(xid); + return rc; +} + +static int is_inode_writable(struct cifsInodeInfo *cifs_inode) +{ + struct cifsFileInfo *open_file; + + spin_lock(&cifs_inode->open_file_lock); + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { + spin_unlock(&cifs_inode->open_file_lock); + return 1; + } + } + spin_unlock(&cifs_inode->open_file_lock); + return 0; +} + +/* We do not want to update the file size from server for inodes + open for write - to avoid races with writepage extending + the file - in the future we could consider allowing + refreshing the inode only on increases in the file size + but this is tricky to do without racing with writebehind + page caching in the current Linux kernel design */ +bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) +{ + if (!cifsInode) + return true; + + if (is_inode_writable(cifsInode)) { + /* This inode is open for write at least once */ + struct cifs_sb_info *cifs_sb; + + cifs_sb = CIFS_SB(cifsInode->netfs.inode.i_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { + /* since no page cache to corrupt on directio + we can change size safely */ + return true; + } + + if (i_size_read(&cifsInode->netfs.inode) < end_of_file) + return true; + + return false; + } else + return true; +} + +static int cifs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, + struct page **pagep, void **fsdata) +{ + int oncethru = 0; + pgoff_t index = pos >> PAGE_SHIFT; + loff_t offset = pos & (PAGE_SIZE - 1); + loff_t page_start = pos & PAGE_MASK; + loff_t i_size; + struct page *page; + int rc = 0; + + cifs_dbg(FYI, "write_begin from %lld len %d\n", (long long)pos, len); + +start: + page = grab_cache_page_write_begin(mapping, index); + if (!page) { + rc = -ENOMEM; + goto out; + } + + if (PageUptodate(page)) + goto out; + + /* + * If we write a full page it will be up to date, no need to read from + * the server. If the write is short, we'll end up doing a sync write + * instead. + */ + if (len == PAGE_SIZE) + goto out; + + /* + * optimize away the read when we have an oplock, and we're not + * expecting to use any of the data we'd be reading in. That + * is, when the page lies beyond the EOF, or straddles the EOF + * and the write will cover all of the existing data. + */ + if (CIFS_CACHE_READ(CIFS_I(mapping->host))) { + i_size = i_size_read(mapping->host); + if (page_start >= i_size || + (offset == 0 && (pos + len) >= i_size)) { + zero_user_segments(page, 0, offset, + offset + len, + PAGE_SIZE); + /* + * PageChecked means that the parts of the page + * to which we're not writing are considered up + * to date. Once the data is copied to the + * page, it can be set uptodate. + */ + SetPageChecked(page); + goto out; + } + } + + if ((file->f_flags & O_ACCMODE) != O_WRONLY && !oncethru) { + /* + * might as well read a page, it is fast enough. If we get + * an error, we don't need to return it. cifs_write_end will + * do a sync write instead since PG_uptodate isn't set. + */ + cifs_readpage_worker(file, page, &page_start); + put_page(page); + oncethru = 1; + goto start; + } else { + /* we could try using another file handle if there is one - + but how would we lock it to prevent close of that handle + racing with this read? In any case + this will be written out by write_end so is fine */ + } +out: + *pagep = page; + return rc; +} + +static bool cifs_release_folio(struct folio *folio, gfp_t gfp) +{ + if (folio_test_private(folio)) + return 0; + if (folio_test_fscache(folio)) { + if (current_is_kswapd() || !(gfp & __GFP_FS)) + return false; + folio_wait_fscache(folio); + } + fscache_note_page_release(cifs_inode_cookie(folio->mapping->host)); + return true; +} + +static void cifs_invalidate_folio(struct folio *folio, size_t offset, + size_t length) +{ + folio_wait_fscache(folio); +} + +static int cifs_launder_folio(struct folio *folio) +{ + int rc = 0; + loff_t range_start = folio_pos(folio); + loff_t range_end = range_start + folio_size(folio); + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 0, + .range_start = range_start, + .range_end = range_end, + }; + + cifs_dbg(FYI, "Launder page: %lu\n", folio->index); + + if (folio_clear_dirty_for_io(folio)) + rc = cifs_writepage_locked(&folio->page, &wbc); + + folio_wait_fscache(folio); + return rc; +} + +void cifs_oplock_break(struct work_struct *work) +{ + struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, + oplock_break); + struct inode *inode = d_inode(cfile->dentry); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + int rc = 0; + bool purge_cache = false, oplock_break_cancelled; + __u64 persistent_fid, volatile_fid; + __u16 net_fid; + + wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, + TASK_UNINTERRUPTIBLE); + + server->ops->downgrade_oplock(server, cinode, cfile->oplock_level, + cfile->oplock_epoch, &purge_cache); + + if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) && + cifs_has_mand_locks(cinode)) { + cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", + inode); + cinode->oplock = 0; + } + + if (inode && S_ISREG(inode->i_mode)) { + if (CIFS_CACHE_READ(cinode)) + break_lease(inode, O_RDONLY); + else + break_lease(inode, O_WRONLY); + rc = filemap_fdatawrite(inode->i_mapping); + if (!CIFS_CACHE_READ(cinode) || purge_cache) { + rc = filemap_fdatawait(inode->i_mapping); + mapping_set_error(inode->i_mapping, rc); + cifs_zap_mapping(inode); + } + cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc); + if (CIFS_CACHE_WRITE(cinode)) + goto oplock_break_ack; + } + + rc = cifs_push_locks(cfile); + if (rc) + cifs_dbg(VFS, "Push locks rc = %d\n", rc); + +oplock_break_ack: + /* + * When oplock break is received and there are no active + * file handles but cached, then schedule deferred close immediately. + * So, new open will not use cached handle. + */ + + if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes)) + cifs_close_deferred_file(cinode); + + persistent_fid = cfile->fid.persistent_fid; + volatile_fid = cfile->fid.volatile_fid; + net_fid = cfile->fid.netfid; + oplock_break_cancelled = cfile->oplock_break_cancelled; + + _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); + /* + * releasing stale oplock after recent reconnect of smb session using + * a now incorrect file handle is not a data integrity issue but do + * not bother sending an oplock release if session to server still is + * disconnected since oplock already released by the server + */ + if (!oplock_break_cancelled) { + /* check for server null since can race with kill_sb calling tree disconnect */ + if (tcon->ses && tcon->ses->server) { + rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, + volatile_fid, net_fid, cinode); + cifs_dbg(FYI, "Oplock release rc = %d\n", rc); + } else + pr_warn_once("lease break not sent for unmounted share\n"); + } + + cifs_done_oplock_break(cinode); +} + +/* + * The presence of cifs_direct_io() in the address space ops vector + * allowes open() O_DIRECT flags which would have failed otherwise. + * + * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests + * so this method should never be called. + * + * Direct IO is not yet supported in the cached mode. + */ +static ssize_t +cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter) +{ + /* + * FIXME + * Eventually need to support direct IO for non forcedirectio mounts + */ + return -EINVAL; +} + +static int cifs_swap_activate(struct swap_info_struct *sis, + struct file *swap_file, sector_t *span) +{ + struct cifsFileInfo *cfile = swap_file->private_data; + struct inode *inode = swap_file->f_mapping->host; + unsigned long blocks; + long long isize; + + cifs_dbg(FYI, "swap activate\n"); + + if (!swap_file->f_mapping->a_ops->swap_rw) + /* Cannot support swap */ + return -EINVAL; + + spin_lock(&inode->i_lock); + blocks = inode->i_blocks; + isize = inode->i_size; + spin_unlock(&inode->i_lock); + if (blocks*512 < isize) { + pr_warn("swap activate: swapfile has holes\n"); + return -EINVAL; + } + *span = sis->pages; + + pr_warn_once("Swap support over SMB3 is experimental\n"); + + /* + * TODO: consider adding ACL (or documenting how) to prevent other + * users (on this or other systems) from reading it + */ + + + /* TODO: add sk_set_memalloc(inet) or similar */ + + if (cfile) + cfile->swapfile = true; + /* + * TODO: Since file already open, we can't open with DENY_ALL here + * but we could add call to grab a byte range lock to prevent others + * from reading or writing the file + */ + + sis->flags |= SWP_FS_OPS; + return add_swap_extent(sis, 0, sis->max, 0); +} + +static void cifs_swap_deactivate(struct file *file) +{ + struct cifsFileInfo *cfile = file->private_data; + + cifs_dbg(FYI, "swap deactivate\n"); + + /* TODO: undo sk_set_memalloc(inet) will eventually be needed */ + + if (cfile) + cfile->swapfile = false; + + /* do we need to unpin (or unlock) the file */ +} + +/* + * Mark a page as having been made dirty and thus needing writeback. We also + * need to pin the cache object to write back to. + */ +#ifdef CONFIG_CIFS_FSCACHE +static bool cifs_dirty_folio(struct address_space *mapping, struct folio *folio) +{ + return fscache_dirty_folio(mapping, folio, + cifs_inode_cookie(mapping->host)); +} +#else +#define cifs_dirty_folio filemap_dirty_folio +#endif + +const struct address_space_operations cifs_addr_ops = { + .read_folio = cifs_read_folio, + .readahead = cifs_readahead, + .writepage = cifs_writepage, + .writepages = cifs_writepages, + .write_begin = cifs_write_begin, + .write_end = cifs_write_end, + .dirty_folio = cifs_dirty_folio, + .release_folio = cifs_release_folio, + .direct_IO = cifs_direct_io, + .invalidate_folio = cifs_invalidate_folio, + .launder_folio = cifs_launder_folio, + /* + * TODO: investigate and if useful we could add an cifs_migratePage + * helper (under an CONFIG_MIGRATION) in the future, and also + * investigate and add an is_dirty_writeback helper if needed + */ + .swap_activate = cifs_swap_activate, + .swap_deactivate = cifs_swap_deactivate, +}; + +/* + * cifs_readahead requires the server to support a buffer large enough to + * contain the header plus one complete page of data. Otherwise, we need + * to leave cifs_readahead out of the address space operations. + */ +const struct address_space_operations cifs_addr_ops_smallbuf = { + .read_folio = cifs_read_folio, + .writepage = cifs_writepage, + .writepages = cifs_writepages, + .write_begin = cifs_write_begin, + .write_end = cifs_write_end, + .dirty_folio = cifs_dirty_folio, + .release_folio = cifs_release_folio, + .invalidate_folio = cifs_invalidate_folio, + .launder_folio = cifs_launder_folio, +}; diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c new file mode 100644 index 000000000000..e2e2ef0fa9a0 --- /dev/null +++ b/fs/smb/client/fs_context.c @@ -0,0 +1,1773 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020, Microsoft Corporation. + * + * Author(s): Steve French + * David Howells + */ + +/* +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "ntlmssp.h" +#include "nterr.h" +#include "rfc1002pdu.h" +#include "fs_context.h" + +static DEFINE_MUTEX(cifs_mount_mutex); + +static const match_table_t cifs_smb_version_tokens = { + { Smb_1, SMB1_VERSION_STRING }, + { Smb_20, SMB20_VERSION_STRING}, + { Smb_21, SMB21_VERSION_STRING }, + { Smb_30, SMB30_VERSION_STRING }, + { Smb_302, SMB302_VERSION_STRING }, + { Smb_302, ALT_SMB302_VERSION_STRING }, + { Smb_311, SMB311_VERSION_STRING }, + { Smb_311, ALT_SMB311_VERSION_STRING }, + { Smb_3any, SMB3ANY_VERSION_STRING }, + { Smb_default, SMBDEFAULT_VERSION_STRING }, + { Smb_version_err, NULL } +}; + +static const match_table_t cifs_secflavor_tokens = { + { Opt_sec_krb5, "krb5" }, + { Opt_sec_krb5i, "krb5i" }, + { Opt_sec_krb5p, "krb5p" }, + { Opt_sec_ntlmsspi, "ntlmsspi" }, + { Opt_sec_ntlmssp, "ntlmssp" }, + { Opt_sec_ntlmv2, "nontlm" }, + { Opt_sec_ntlmv2, "ntlmv2" }, + { Opt_sec_ntlmv2i, "ntlmv2i" }, + { Opt_sec_none, "none" }, + + { Opt_sec_err, NULL } +}; + +const struct fs_parameter_spec smb3_fs_parameters[] = { + /* Mount options that take no arguments */ + fsparam_flag_no("user_xattr", Opt_user_xattr), + fsparam_flag_no("forceuid", Opt_forceuid), + fsparam_flag_no("multichannel", Opt_multichannel), + fsparam_flag_no("forcegid", Opt_forcegid), + fsparam_flag("noblocksend", Opt_noblocksend), + fsparam_flag("noautotune", Opt_noautotune), + fsparam_flag("nolease", Opt_nolease), + fsparam_flag_no("hard", Opt_hard), + fsparam_flag_no("soft", Opt_soft), + fsparam_flag_no("perm", Opt_perm), + fsparam_flag("nodelete", Opt_nodelete), + fsparam_flag_no("mapposix", Opt_mapposix), + fsparam_flag("mapchars", Opt_mapchars), + fsparam_flag("nomapchars", Opt_nomapchars), + fsparam_flag_no("sfu", Opt_sfu), + fsparam_flag("nodfs", Opt_nodfs), + fsparam_flag_no("posixpaths", Opt_posixpaths), + fsparam_flag_no("unix", Opt_unix), + fsparam_flag_no("linux", Opt_unix), + fsparam_flag_no("posix", Opt_unix), + fsparam_flag("nocase", Opt_nocase), + fsparam_flag("ignorecase", Opt_nocase), + fsparam_flag_no("brl", Opt_brl), + fsparam_flag_no("handlecache", Opt_handlecache), + fsparam_flag("forcemandatorylock", Opt_forcemandatorylock), + fsparam_flag("forcemand", Opt_forcemandatorylock), + fsparam_flag("setuidfromacl", Opt_setuidfromacl), + fsparam_flag("idsfromsid", Opt_setuidfromacl), + fsparam_flag_no("setuids", Opt_setuids), + fsparam_flag_no("dynperm", Opt_dynperm), + fsparam_flag_no("intr", Opt_intr), + fsparam_flag_no("strictsync", Opt_strictsync), + fsparam_flag_no("serverino", Opt_serverino), + fsparam_flag("rwpidforward", Opt_rwpidforward), + fsparam_flag("cifsacl", Opt_cifsacl), + fsparam_flag_no("acl", Opt_acl), + fsparam_flag("locallease", Opt_locallease), + fsparam_flag("sign", Opt_sign), + fsparam_flag("ignore_signature", Opt_ignore_signature), + fsparam_flag("signloosely", Opt_ignore_signature), + fsparam_flag("seal", Opt_seal), + fsparam_flag("noac", Opt_noac), + fsparam_flag("fsc", Opt_fsc), + fsparam_flag("mfsymlinks", Opt_mfsymlinks), + fsparam_flag("multiuser", Opt_multiuser), + fsparam_flag("sloppy", Opt_sloppy), + fsparam_flag("nosharesock", Opt_nosharesock), + fsparam_flag_no("persistenthandles", Opt_persistent), + fsparam_flag_no("resilienthandles", Opt_resilient), + fsparam_flag_no("tcpnodelay", Opt_tcp_nodelay), + fsparam_flag("nosparse", Opt_nosparse), + fsparam_flag("domainauto", Opt_domainauto), + fsparam_flag("rdma", Opt_rdma), + fsparam_flag("modesid", Opt_modesid), + fsparam_flag("modefromsid", Opt_modesid), + fsparam_flag("rootfs", Opt_rootfs), + fsparam_flag("compress", Opt_compress), + fsparam_flag("witness", Opt_witness), + + /* Mount options which take numeric value */ + fsparam_u32("backupuid", Opt_backupuid), + fsparam_u32("backupgid", Opt_backupgid), + fsparam_u32("uid", Opt_uid), + fsparam_u32("cruid", Opt_cruid), + fsparam_u32("gid", Opt_gid), + fsparam_u32("file_mode", Opt_file_mode), + fsparam_u32("dirmode", Opt_dirmode), + fsparam_u32("dir_mode", Opt_dirmode), + fsparam_u32("port", Opt_port), + fsparam_u32("min_enc_offload", Opt_min_enc_offload), + fsparam_u32("esize", Opt_min_enc_offload), + fsparam_u32("bsize", Opt_blocksize), + fsparam_u32("rasize", Opt_rasize), + fsparam_u32("rsize", Opt_rsize), + fsparam_u32("wsize", Opt_wsize), + fsparam_u32("actimeo", Opt_actimeo), + fsparam_u32("acdirmax", Opt_acdirmax), + fsparam_u32("acregmax", Opt_acregmax), + fsparam_u32("closetimeo", Opt_closetimeo), + fsparam_u32("echo_interval", Opt_echo_interval), + fsparam_u32("max_credits", Opt_max_credits), + fsparam_u32("handletimeout", Opt_handletimeout), + fsparam_u64("snapshot", Opt_snapshot), + fsparam_u32("max_channels", Opt_max_channels), + + /* Mount options which take string value */ + fsparam_string("source", Opt_source), + fsparam_string("user", Opt_user), + fsparam_string("username", Opt_user), + fsparam_string("pass", Opt_pass), + fsparam_string("password", Opt_pass), + fsparam_string("ip", Opt_ip), + fsparam_string("addr", Opt_ip), + fsparam_string("domain", Opt_domain), + fsparam_string("dom", Opt_domain), + fsparam_string("srcaddr", Opt_srcaddr), + fsparam_string("iocharset", Opt_iocharset), + fsparam_string("netbiosname", Opt_netbiosname), + fsparam_string("servern", Opt_servern), + fsparam_string("ver", Opt_ver), + fsparam_string("vers", Opt_vers), + fsparam_string("sec", Opt_sec), + fsparam_string("cache", Opt_cache), + + /* Arguments that should be ignored */ + fsparam_flag("guest", Opt_ignore), + fsparam_flag("noatime", Opt_ignore), + fsparam_flag("relatime", Opt_ignore), + fsparam_flag("_netdev", Opt_ignore), + fsparam_flag_no("suid", Opt_ignore), + fsparam_flag_no("exec", Opt_ignore), + fsparam_flag_no("dev", Opt_ignore), + fsparam_flag_no("mand", Opt_ignore), + fsparam_flag_no("auto", Opt_ignore), + fsparam_string("cred", Opt_ignore), + fsparam_string("credentials", Opt_ignore), + /* + * UNC and prefixpath is now extracted from Opt_source + * in the new mount API so we can just ignore them going forward. + */ + fsparam_string("unc", Opt_ignore), + fsparam_string("prefixpath", Opt_ignore), + {} +}; + +static int +cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) +{ + + substring_t args[MAX_OPT_ARGS]; + + /* + * With mount options, the last one should win. Reset any existing + * settings back to default. + */ + ctx->sectype = Unspecified; + ctx->sign = false; + + switch (match_token(value, cifs_secflavor_tokens, args)) { + case Opt_sec_krb5p: + cifs_errorf(fc, "sec=krb5p is not supported!\n"); + return 1; + case Opt_sec_krb5i: + ctx->sign = true; + fallthrough; + case Opt_sec_krb5: + ctx->sectype = Kerberos; + break; + case Opt_sec_ntlmsspi: + ctx->sign = true; + fallthrough; + case Opt_sec_ntlmssp: + ctx->sectype = RawNTLMSSP; + break; + case Opt_sec_ntlmv2i: + ctx->sign = true; + fallthrough; + case Opt_sec_ntlmv2: + ctx->sectype = NTLMv2; + break; + case Opt_sec_none: + ctx->nullauth = 1; + break; + default: + cifs_errorf(fc, "bad security option: %s\n", value); + return 1; + } + + return 0; +} + +static const match_table_t cifs_cacheflavor_tokens = { + { Opt_cache_loose, "loose" }, + { Opt_cache_strict, "strict" }, + { Opt_cache_none, "none" }, + { Opt_cache_ro, "ro" }, + { Opt_cache_rw, "singleclient" }, + { Opt_cache_err, NULL } +}; + +static int +cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) +{ + substring_t args[MAX_OPT_ARGS]; + + switch (match_token(value, cifs_cacheflavor_tokens, args)) { + case Opt_cache_loose: + ctx->direct_io = false; + ctx->strict_io = false; + ctx->cache_ro = false; + ctx->cache_rw = false; + break; + case Opt_cache_strict: + ctx->direct_io = false; + ctx->strict_io = true; + ctx->cache_ro = false; + ctx->cache_rw = false; + break; + case Opt_cache_none: + ctx->direct_io = true; + ctx->strict_io = false; + ctx->cache_ro = false; + ctx->cache_rw = false; + break; + case Opt_cache_ro: + ctx->direct_io = false; + ctx->strict_io = false; + ctx->cache_ro = true; + ctx->cache_rw = false; + break; + case Opt_cache_rw: + ctx->direct_io = false; + ctx->strict_io = false; + ctx->cache_ro = false; + ctx->cache_rw = true; + break; + default: + cifs_errorf(fc, "bad cache= option: %s\n", value); + return 1; + } + return 0; +} + +#define DUP_CTX_STR(field) \ +do { \ + if (ctx->field) { \ + new_ctx->field = kstrdup(ctx->field, GFP_ATOMIC); \ + if (new_ctx->field == NULL) { \ + smb3_cleanup_fs_context_contents(new_ctx); \ + return -ENOMEM; \ + } \ + } \ +} while (0) + +int +smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx) +{ + memcpy(new_ctx, ctx, sizeof(*ctx)); + new_ctx->prepath = NULL; + new_ctx->mount_options = NULL; + new_ctx->nodename = NULL; + new_ctx->username = NULL; + new_ctx->password = NULL; + new_ctx->server_hostname = NULL; + new_ctx->domainname = NULL; + new_ctx->UNC = NULL; + new_ctx->source = NULL; + new_ctx->iocharset = NULL; + /* + * Make sure to stay in sync with smb3_cleanup_fs_context_contents() + */ + DUP_CTX_STR(prepath); + DUP_CTX_STR(mount_options); + DUP_CTX_STR(username); + DUP_CTX_STR(password); + DUP_CTX_STR(server_hostname); + DUP_CTX_STR(UNC); + DUP_CTX_STR(source); + DUP_CTX_STR(domainname); + DUP_CTX_STR(nodename); + DUP_CTX_STR(iocharset); + + return 0; +} + +static int +cifs_parse_smb_version(struct fs_context *fc, char *value, struct smb3_fs_context *ctx, bool is_smb3) +{ + substring_t args[MAX_OPT_ARGS]; + + switch (match_token(value, cifs_smb_version_tokens, args)) { +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + case Smb_1: + if (disable_legacy_dialects) { + cifs_errorf(fc, "mount with legacy dialect disabled\n"); + return 1; + } + if (is_smb3) { + cifs_errorf(fc, "vers=1.0 (cifs) not permitted when mounting with smb3\n"); + return 1; + } + cifs_errorf(fc, "Use of the less secure dialect vers=1.0 is not recommended unless required for access to very old servers\n"); + ctx->ops = &smb1_operations; + ctx->vals = &smb1_values; + break; + case Smb_20: + if (disable_legacy_dialects) { + cifs_errorf(fc, "mount with legacy dialect disabled\n"); + return 1; + } + if (is_smb3) { + cifs_errorf(fc, "vers=2.0 not permitted when mounting with smb3\n"); + return 1; + } + ctx->ops = &smb20_operations; + ctx->vals = &smb20_values; + break; +#else + case Smb_1: + cifs_errorf(fc, "vers=1.0 (cifs) mount not permitted when legacy dialects disabled\n"); + return 1; + case Smb_20: + cifs_errorf(fc, "vers=2.0 mount not permitted when legacy dialects disabled\n"); + return 1; +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ + case Smb_21: + ctx->ops = &smb21_operations; + ctx->vals = &smb21_values; + break; + case Smb_30: + ctx->ops = &smb30_operations; + ctx->vals = &smb30_values; + break; + case Smb_302: + ctx->ops = &smb30_operations; /* currently identical with 3.0 */ + ctx->vals = &smb302_values; + break; + case Smb_311: + ctx->ops = &smb311_operations; + ctx->vals = &smb311_values; + break; + case Smb_3any: + ctx->ops = &smb30_operations; /* currently identical with 3.0 */ + ctx->vals = &smb3any_values; + break; + case Smb_default: + ctx->ops = &smb30_operations; + ctx->vals = &smbdefault_values; + break; + default: + cifs_errorf(fc, "Unknown vers= option specified: %s\n", value); + return 1; + } + return 0; +} + +int smb3_parse_opt(const char *options, const char *key, char **val) +{ + int rc = -ENOENT; + char *opts, *orig, *p; + + orig = opts = kstrdup(options, GFP_KERNEL); + if (!opts) + return -ENOMEM; + + while ((p = strsep(&opts, ","))) { + char *nval; + + if (!*p) + continue; + if (strncasecmp(p, key, strlen(key))) + continue; + nval = strchr(p, '='); + if (nval) { + if (nval == p) + continue; + *nval++ = 0; + *val = kstrdup(nval, GFP_KERNEL); + rc = !*val ? -ENOMEM : 0; + goto out; + } + } +out: + kfree(orig); + return rc; +} + +/* + * Remove duplicate path delimiters. Windows is supposed to do that + * but there are some bugs that prevent rename from working if there are + * multiple delimiters. + * + * Returns a sanitized duplicate of @path. @gfp indicates the GFP_* flags + * for kstrdup. + * The caller is responsible for freeing the original. + */ +#define IS_DELIM(c) ((c) == '/' || (c) == '\\') +char *cifs_sanitize_prepath(char *prepath, gfp_t gfp) +{ + char *cursor1 = prepath, *cursor2 = prepath; + + /* skip all prepended delimiters */ + while (IS_DELIM(*cursor1)) + cursor1++; + + /* copy the first letter */ + *cursor2 = *cursor1; + + /* copy the remainder... */ + while (*(cursor1++)) { + /* ... skipping all duplicated delimiters */ + if (IS_DELIM(*cursor1) && IS_DELIM(*cursor2)) + continue; + *(++cursor2) = *cursor1; + } + + /* if the last character is a delimiter, skip it */ + if (IS_DELIM(*(cursor2 - 1))) + cursor2--; + + *(cursor2) = '\0'; + return kstrdup(prepath, gfp); +} + +/* + * Parse a devname into substrings and populate the ctx->UNC and ctx->prepath + * fields with the result. Returns 0 on success and an error otherwise + * (e.g. ENOMEM or EINVAL) + */ +int +smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx) +{ + char *pos; + const char *delims = "/\\"; + size_t len; + + if (unlikely(!devname || !*devname)) { + cifs_dbg(VFS, "Device name not specified\n"); + return -EINVAL; + } + + /* make sure we have a valid UNC double delimiter prefix */ + len = strspn(devname, delims); + if (len != 2) + return -EINVAL; + + /* find delimiter between host and sharename */ + pos = strpbrk(devname + 2, delims); + if (!pos) + return -EINVAL; + + /* record the server hostname */ + kfree(ctx->server_hostname); + ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL); + if (!ctx->server_hostname) + return -ENOMEM; + + /* skip past delimiter */ + ++pos; + + /* now go until next delimiter or end of string */ + len = strcspn(pos, delims); + + /* move "pos" up to delimiter or NULL */ + pos += len; + kfree(ctx->UNC); + ctx->UNC = kstrndup(devname, pos - devname, GFP_KERNEL); + if (!ctx->UNC) + return -ENOMEM; + + convert_delimiter(ctx->UNC, '\\'); + + /* skip any delimiter */ + if (*pos == '/' || *pos == '\\') + pos++; + + kfree(ctx->prepath); + ctx->prepath = NULL; + + /* If pos is NULL then no prepath */ + if (!*pos) + return 0; + + ctx->prepath = cifs_sanitize_prepath(pos, GFP_KERNEL); + if (!ctx->prepath) + return -ENOMEM; + + return 0; +} + +static void smb3_fs_context_free(struct fs_context *fc); +static int smb3_fs_context_parse_param(struct fs_context *fc, + struct fs_parameter *param); +static int smb3_fs_context_parse_monolithic(struct fs_context *fc, + void *data); +static int smb3_get_tree(struct fs_context *fc); +static int smb3_reconfigure(struct fs_context *fc); + +static const struct fs_context_operations smb3_fs_context_ops = { + .free = smb3_fs_context_free, + .parse_param = smb3_fs_context_parse_param, + .parse_monolithic = smb3_fs_context_parse_monolithic, + .get_tree = smb3_get_tree, + .reconfigure = smb3_reconfigure, +}; + +/* + * Parse a monolithic block of data from sys_mount(). + * smb3_fs_context_parse_monolithic - Parse key[=val][,key[=val]]* mount data + * @ctx: The superblock configuration to fill in. + * @data: The data to parse + * + * Parse a blob of data that's in key[=val][,key[=val]]* form. This can be + * called from the ->monolithic_mount_data() fs_context operation. + * + * Returns 0 on success or the error returned by the ->parse_option() fs_context + * operation on failure. + */ +static int smb3_fs_context_parse_monolithic(struct fs_context *fc, + void *data) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + char *options = data, *key; + int ret = 0; + + if (!options) + return 0; + + ctx->mount_options = kstrdup(data, GFP_KERNEL); + if (ctx->mount_options == NULL) + return -ENOMEM; + + ret = security_sb_eat_lsm_opts(options, &fc->security); + if (ret) + return ret; + + /* BB Need to add support for sep= here TBD */ + while ((key = strsep(&options, ",")) != NULL) { + size_t len; + char *value; + + if (*key == 0) + break; + + /* Check if following character is the deliminator If yes, + * we have encountered a double deliminator reset the NULL + * character to the deliminator + */ + while (options && options[0] == ',') { + len = strlen(key); + strcpy(key + len, options); + options = strchr(options, ','); + if (options) + *options++ = 0; + } + + + len = 0; + value = strchr(key, '='); + if (value) { + if (value == key) + continue; + *value++ = 0; + len = strlen(value); + } + + ret = vfs_parse_fs_string(fc, key, value, len); + if (ret < 0) + break; + } + + return ret; +} + +/* + * Validate the preparsed information in the config. + */ +static int smb3_fs_context_validate(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + + if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) { + cifs_errorf(fc, "SMB Direct requires Version >=3.0\n"); + return -EOPNOTSUPP; + } + +#ifndef CONFIG_KEYS + /* Muliuser mounts require CONFIG_KEYS support */ + if (ctx->multiuser) { + cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); + return -1; + } +#endif + + if (ctx->got_version == false) + pr_warn_once("No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.\n"); + + + if (!ctx->UNC) { + cifs_errorf(fc, "CIFS mount error: No usable UNC path provided in device string!\n"); + return -1; + } + + /* make sure UNC has a share name */ + if (strlen(ctx->UNC) < 3 || !strchr(ctx->UNC + 3, '\\')) { + cifs_errorf(fc, "Malformed UNC. Unable to find share name.\n"); + return -ENOENT; + } + + if (!ctx->got_ip) { + int len; + const char *slash; + + /* No ip= option specified? Try to get it from UNC */ + /* Use the address part of the UNC. */ + slash = strchr(&ctx->UNC[2], '\\'); + len = slash - &ctx->UNC[2]; + if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, + &ctx->UNC[2], len)) { + pr_err("Unable to determine destination address\n"); + return -EHOSTUNREACH; + } + } + + /* set the port that we got earlier */ + cifs_set_port((struct sockaddr *)&ctx->dstaddr, ctx->port); + + if (ctx->override_uid && !ctx->uid_specified) { + ctx->override_uid = 0; + pr_notice("ignoring forceuid mount option specified with no uid= option\n"); + } + + if (ctx->override_gid && !ctx->gid_specified) { + ctx->override_gid = 0; + pr_notice("ignoring forcegid mount option specified with no gid= option\n"); + } + + return 0; +} + +static int smb3_get_tree_common(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + struct dentry *root; + int rc = 0; + + root = cifs_smb3_do_mount(fc->fs_type, 0, ctx); + if (IS_ERR(root)) + return PTR_ERR(root); + + fc->root = root; + + return rc; +} + +/* + * Create an SMB3 superblock from the parameters passed. + */ +static int smb3_get_tree(struct fs_context *fc) +{ + int err = smb3_fs_context_validate(fc); + int ret; + + if (err) + return err; + mutex_lock(&cifs_mount_mutex); + ret = smb3_get_tree_common(fc); + mutex_unlock(&cifs_mount_mutex); + return ret; +} + +static void smb3_fs_context_free(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + + smb3_cleanup_fs_context(ctx); +} + +/* + * Compare the old and new proposed context during reconfigure + * and check if the changes are compatible. + */ +static int smb3_verify_reconfigure_ctx(struct fs_context *fc, + struct smb3_fs_context *new_ctx, + struct smb3_fs_context *old_ctx) +{ + if (new_ctx->posix_paths != old_ctx->posix_paths) { + cifs_errorf(fc, "can not change posixpaths during remount\n"); + return -EINVAL; + } + if (new_ctx->sectype != old_ctx->sectype) { + cifs_errorf(fc, "can not change sec during remount\n"); + return -EINVAL; + } + if (new_ctx->multiuser != old_ctx->multiuser) { + cifs_errorf(fc, "can not change multiuser during remount\n"); + return -EINVAL; + } + if (new_ctx->UNC && + (!old_ctx->UNC || strcmp(new_ctx->UNC, old_ctx->UNC))) { + cifs_errorf(fc, "can not change UNC during remount\n"); + return -EINVAL; + } + if (new_ctx->username && + (!old_ctx->username || strcmp(new_ctx->username, old_ctx->username))) { + cifs_errorf(fc, "can not change username during remount\n"); + return -EINVAL; + } + if (new_ctx->password && + (!old_ctx->password || strcmp(new_ctx->password, old_ctx->password))) { + cifs_errorf(fc, "can not change password during remount\n"); + return -EINVAL; + } + if (new_ctx->domainname && + (!old_ctx->domainname || strcmp(new_ctx->domainname, old_ctx->domainname))) { + cifs_errorf(fc, "can not change domainname during remount\n"); + return -EINVAL; + } + if (strcmp(new_ctx->workstation_name, old_ctx->workstation_name)) { + cifs_errorf(fc, "can not change workstation_name during remount\n"); + return -EINVAL; + } + if (new_ctx->nodename && + (!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) { + cifs_errorf(fc, "can not change nodename during remount\n"); + return -EINVAL; + } + if (new_ctx->iocharset && + (!old_ctx->iocharset || strcmp(new_ctx->iocharset, old_ctx->iocharset))) { + cifs_errorf(fc, "can not change iocharset during remount\n"); + return -EINVAL; + } + + return 0; +} + +#define STEAL_STRING(cifs_sb, ctx, field) \ +do { \ + kfree(ctx->field); \ + ctx->field = cifs_sb->ctx->field; \ + cifs_sb->ctx->field = NULL; \ +} while (0) + +#define STEAL_STRING_SENSITIVE(cifs_sb, ctx, field) \ +do { \ + kfree_sensitive(ctx->field); \ + ctx->field = cifs_sb->ctx->field; \ + cifs_sb->ctx->field = NULL; \ +} while (0) + +static int smb3_reconfigure(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + struct dentry *root = fc->root; + struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); + int rc; + + rc = smb3_verify_reconfigure_ctx(fc, ctx, cifs_sb->ctx); + if (rc) + return rc; + + /* + * We can not change UNC/username/password/domainname/ + * workstation_name/nodename/iocharset + * during reconnect so ignore what we have in the new context and + * just use what we already have in cifs_sb->ctx. + */ + STEAL_STRING(cifs_sb, ctx, UNC); + STEAL_STRING(cifs_sb, ctx, source); + STEAL_STRING(cifs_sb, ctx, username); + STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); + STEAL_STRING(cifs_sb, ctx, domainname); + STEAL_STRING(cifs_sb, ctx, nodename); + STEAL_STRING(cifs_sb, ctx, iocharset); + + /* if rsize or wsize not passed in on remount, use previous values */ + if (ctx->rsize == 0) + ctx->rsize = cifs_sb->ctx->rsize; + if (ctx->wsize == 0) + ctx->wsize = cifs_sb->ctx->wsize; + + + smb3_cleanup_fs_context_contents(cifs_sb->ctx); + rc = smb3_fs_context_dup(cifs_sb->ctx, ctx); + smb3_update_mnt_flags(cifs_sb); +#ifdef CONFIG_CIFS_DFS_UPCALL + if (!rc) + rc = dfs_cache_remount_fs(cifs_sb); +#endif + + return rc; +} + +static int smb3_fs_context_parse_param(struct fs_context *fc, + struct fs_parameter *param) +{ + struct fs_parse_result result; + struct smb3_fs_context *ctx = smb3_fc2context(fc); + int i, opt; + bool is_smb3 = !strcmp(fc->fs_type->name, "smb3"); + bool skip_parsing = false; + kuid_t uid; + kgid_t gid; + + cifs_dbg(FYI, "CIFS: parsing cifs mount option '%s'\n", param->key); + + /* + * fs_parse can not handle string options with an empty value so + * we will need special handling of them. + */ + if (param->type == fs_value_is_string && param->string[0] == 0) { + if (!strcmp("pass", param->key) || !strcmp("password", param->key)) { + skip_parsing = true; + opt = Opt_pass; + } else if (!strcmp("user", param->key) || !strcmp("username", param->key)) { + skip_parsing = true; + opt = Opt_user; + } + } + + if (!skip_parsing) { + opt = fs_parse(fc, smb3_fs_parameters, param, &result); + if (opt < 0) + return ctx->sloppy ? 1 : opt; + } + + switch (opt) { + case Opt_compress: + ctx->compression = UNKNOWN_TYPE; + cifs_dbg(VFS, + "SMB3 compression support is experimental\n"); + break; + case Opt_nodfs: + ctx->nodfs = 1; + break; + case Opt_hard: + if (result.negated) + ctx->retry = 0; + else + ctx->retry = 1; + break; + case Opt_soft: + if (result.negated) + ctx->retry = 1; + else + ctx->retry = 0; + break; + case Opt_mapposix: + if (result.negated) + ctx->remap = false; + else { + ctx->remap = true; + ctx->sfu_remap = false; /* disable SFU mapping */ + } + break; + case Opt_mapchars: + if (result.negated) + ctx->sfu_remap = false; + else { + ctx->sfu_remap = true; + ctx->remap = false; /* disable SFM (mapposix) mapping */ + } + break; + case Opt_user_xattr: + if (result.negated) + ctx->no_xattr = 1; + else + ctx->no_xattr = 0; + break; + case Opt_forceuid: + if (result.negated) + ctx->override_uid = 0; + else + ctx->override_uid = 1; + break; + case Opt_forcegid: + if (result.negated) + ctx->override_gid = 0; + else + ctx->override_gid = 1; + break; + case Opt_perm: + if (result.negated) + ctx->noperm = 1; + else + ctx->noperm = 0; + break; + case Opt_dynperm: + if (result.negated) + ctx->dynperm = 0; + else + ctx->dynperm = 1; + break; + case Opt_sfu: + if (result.negated) + ctx->sfu_emul = 0; + else + ctx->sfu_emul = 1; + break; + case Opt_noblocksend: + ctx->noblocksnd = 1; + break; + case Opt_noautotune: + ctx->noautotune = 1; + break; + case Opt_nolease: + ctx->no_lease = 1; + break; + case Opt_nosparse: + ctx->no_sparse = 1; + break; + case Opt_nodelete: + ctx->nodelete = 1; + break; + case Opt_multichannel: + if (result.negated) { + ctx->multichannel = false; + ctx->max_channels = 1; + } else { + ctx->multichannel = true; + /* if number of channels not specified, default to 2 */ + if (ctx->max_channels < 2) + ctx->max_channels = 2; + } + break; + case Opt_uid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + goto cifs_parse_mount_err; + ctx->linux_uid = uid; + ctx->uid_specified = true; + break; + case Opt_cruid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + goto cifs_parse_mount_err; + ctx->cred_uid = uid; + ctx->cruid_specified = true; + break; + case Opt_backupuid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + goto cifs_parse_mount_err; + ctx->backupuid = uid; + ctx->backupuid_specified = true; + break; + case Opt_backupgid: + gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(gid)) + goto cifs_parse_mount_err; + ctx->backupgid = gid; + ctx->backupgid_specified = true; + break; + case Opt_gid: + gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(gid)) + goto cifs_parse_mount_err; + ctx->linux_gid = gid; + ctx->gid_specified = true; + break; + case Opt_port: + ctx->port = result.uint_32; + break; + case Opt_file_mode: + ctx->file_mode = result.uint_32; + break; + case Opt_dirmode: + ctx->dir_mode = result.uint_32; + break; + case Opt_min_enc_offload: + ctx->min_offload = result.uint_32; + break; + case Opt_blocksize: + /* + * inode blocksize realistically should never need to be + * less than 16K or greater than 16M and default is 1MB. + * Note that small inode block sizes (e.g. 64K) can lead + * to very poor performance of common tools like cp and scp + */ + if ((result.uint_32 < CIFS_MAX_MSGSIZE) || + (result.uint_32 > (4 * SMB3_DEFAULT_IOSIZE))) { + cifs_errorf(fc, "%s: Invalid blocksize\n", + __func__); + goto cifs_parse_mount_err; + } + ctx->bsize = result.uint_32; + ctx->got_bsize = true; + break; + case Opt_rasize: + /* + * readahead size realistically should never need to be + * less than 1M (CIFS_DEFAULT_IOSIZE) or greater than 32M + * (perhaps an exception should be considered in the + * for the case of a large number of channels + * when multichannel is negotiated) since that would lead + * to plenty of parallel I/O in flight to the server. + * Note that smaller read ahead sizes would + * hurt performance of common tools like cp and scp + * which often trigger sequential i/o with read ahead + */ + if ((result.uint_32 > (8 * SMB3_DEFAULT_IOSIZE)) || + (result.uint_32 < CIFS_DEFAULT_IOSIZE)) { + cifs_errorf(fc, "%s: Invalid rasize %d vs. %d\n", + __func__, result.uint_32, SMB3_DEFAULT_IOSIZE); + goto cifs_parse_mount_err; + } + ctx->rasize = result.uint_32; + break; + case Opt_rsize: + ctx->rsize = result.uint_32; + ctx->got_rsize = true; + break; + case Opt_wsize: + ctx->wsize = result.uint_32; + ctx->got_wsize = true; + break; + case Opt_acregmax: + ctx->acregmax = HZ * result.uint_32; + if (ctx->acregmax > CIFS_MAX_ACTIMEO) { + cifs_errorf(fc, "acregmax too large\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_acdirmax: + ctx->acdirmax = HZ * result.uint_32; + if (ctx->acdirmax > CIFS_MAX_ACTIMEO) { + cifs_errorf(fc, "acdirmax too large\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_actimeo: + if (HZ * result.uint_32 > CIFS_MAX_ACTIMEO) { + cifs_errorf(fc, "timeout too large\n"); + goto cifs_parse_mount_err; + } + if ((ctx->acdirmax != CIFS_DEF_ACTIMEO) || + (ctx->acregmax != CIFS_DEF_ACTIMEO)) { + cifs_errorf(fc, "actimeo ignored since acregmax or acdirmax specified\n"); + break; + } + ctx->acdirmax = ctx->acregmax = HZ * result.uint_32; + break; + case Opt_closetimeo: + ctx->closetimeo = HZ * result.uint_32; + if (ctx->closetimeo > SMB3_MAX_DCLOSETIMEO) { + cifs_errorf(fc, "closetimeo too large\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_echo_interval: + ctx->echo_interval = result.uint_32; + break; + case Opt_snapshot: + ctx->snapshot_time = result.uint_64; + break; + case Opt_max_credits: + if (result.uint_32 < 20 || result.uint_32 > 60000) { + cifs_errorf(fc, "%s: Invalid max_credits value\n", + __func__); + goto cifs_parse_mount_err; + } + ctx->max_credits = result.uint_32; + break; + case Opt_max_channels: + if (result.uint_32 < 1 || result.uint_32 > CIFS_MAX_CHANNELS) { + cifs_errorf(fc, "%s: Invalid max_channels value, needs to be 1-%d\n", + __func__, CIFS_MAX_CHANNELS); + goto cifs_parse_mount_err; + } + ctx->max_channels = result.uint_32; + /* If more than one channel requested ... they want multichan */ + if (result.uint_32 > 1) + ctx->multichannel = true; + break; + case Opt_handletimeout: + ctx->handle_timeout = result.uint_32; + if (ctx->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) { + cifs_errorf(fc, "Invalid handle cache timeout, longer than 16 minutes\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_source: + kfree(ctx->UNC); + ctx->UNC = NULL; + switch (smb3_parse_devname(param->string, ctx)) { + case 0: + break; + case -ENOMEM: + cifs_errorf(fc, "Unable to allocate memory for devname\n"); + goto cifs_parse_mount_err; + case -EINVAL: + cifs_errorf(fc, "Malformed UNC in devname\n"); + goto cifs_parse_mount_err; + default: + cifs_errorf(fc, "Unknown error parsing devname\n"); + goto cifs_parse_mount_err; + } + ctx->source = kstrdup(param->string, GFP_KERNEL); + if (ctx->source == NULL) { + cifs_errorf(fc, "OOM when copying UNC string\n"); + goto cifs_parse_mount_err; + } + fc->source = kstrdup(param->string, GFP_KERNEL); + if (fc->source == NULL) { + cifs_errorf(fc, "OOM when copying UNC string\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_user: + kfree(ctx->username); + ctx->username = NULL; + if (strlen(param->string) == 0) { + /* null user, ie. anonymous authentication */ + ctx->nullauth = 1; + break; + } + + if (strnlen(param->string, CIFS_MAX_USERNAME_LEN) > + CIFS_MAX_USERNAME_LEN) { + pr_warn("username too long\n"); + goto cifs_parse_mount_err; + } + ctx->username = kstrdup(param->string, GFP_KERNEL); + if (ctx->username == NULL) { + cifs_errorf(fc, "OOM when copying username string\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_pass: + kfree_sensitive(ctx->password); + ctx->password = NULL; + if (strlen(param->string) == 0) + break; + + ctx->password = kstrdup(param->string, GFP_KERNEL); + if (ctx->password == NULL) { + cifs_errorf(fc, "OOM when copying password string\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_ip: + if (strlen(param->string) == 0) { + ctx->got_ip = false; + break; + } + if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, + param->string, + strlen(param->string))) { + pr_err("bad ip= option (%s)\n", param->string); + goto cifs_parse_mount_err; + } + ctx->got_ip = true; + break; + case Opt_domain: + if (strnlen(param->string, CIFS_MAX_DOMAINNAME_LEN) + == CIFS_MAX_DOMAINNAME_LEN) { + pr_warn("domain name too long\n"); + goto cifs_parse_mount_err; + } + + kfree(ctx->domainname); + ctx->domainname = kstrdup(param->string, GFP_KERNEL); + if (ctx->domainname == NULL) { + cifs_errorf(fc, "OOM when copying domainname string\n"); + goto cifs_parse_mount_err; + } + cifs_dbg(FYI, "Domain name set\n"); + break; + case Opt_srcaddr: + if (!cifs_convert_address( + (struct sockaddr *)&ctx->srcaddr, + param->string, strlen(param->string))) { + pr_warn("Could not parse srcaddr: %s\n", + param->string); + goto cifs_parse_mount_err; + } + break; + case Opt_iocharset: + if (strnlen(param->string, 1024) >= 65) { + pr_warn("iocharset name too long\n"); + goto cifs_parse_mount_err; + } + + if (strncasecmp(param->string, "default", 7) != 0) { + kfree(ctx->iocharset); + ctx->iocharset = kstrdup(param->string, GFP_KERNEL); + if (ctx->iocharset == NULL) { + cifs_errorf(fc, "OOM when copying iocharset string\n"); + goto cifs_parse_mount_err; + } + } + /* if iocharset not set then load_nls_default + * is used by caller + */ + cifs_dbg(FYI, "iocharset set to %s\n", ctx->iocharset); + break; + case Opt_netbiosname: + memset(ctx->source_rfc1001_name, 0x20, + RFC1001_NAME_LEN); + /* + * FIXME: are there cases in which a comma can + * be valid in workstation netbios name (and + * need special handling)? + */ + for (i = 0; i < RFC1001_NAME_LEN; i++) { + /* don't ucase netbiosname for user */ + if (param->string[i] == 0) + break; + ctx->source_rfc1001_name[i] = param->string[i]; + } + /* The string has 16th byte zero still from + * set at top of the function + */ + if (i == RFC1001_NAME_LEN && param->string[i] != 0) + pr_warn("netbiosname longer than 15 truncated\n"); + break; + case Opt_servern: + /* last byte, type, is 0x20 for servr type */ + memset(ctx->target_rfc1001_name, 0x20, + RFC1001_NAME_LEN_WITH_NULL); + /* + * BB are there cases in which a comma can be valid in this + * workstation netbios name (and need special handling)? + */ + + /* user or mount helper must uppercase the netbios name */ + for (i = 0; i < 15; i++) { + if (param->string[i] == 0) + break; + ctx->target_rfc1001_name[i] = param->string[i]; + } + + /* The string has 16th byte zero still from set at top of function */ + if (i == RFC1001_NAME_LEN && param->string[i] != 0) + pr_warn("server netbiosname longer than 15 truncated\n"); + break; + case Opt_ver: + /* version of mount userspace tools, not dialect */ + /* If interface changes in mount.cifs bump to new ver */ + if (strncasecmp(param->string, "1", 1) == 0) { + if (strlen(param->string) > 1) { + pr_warn("Bad mount helper ver=%s. Did you want SMB1 (CIFS) dialect and mean to type vers=1.0 instead?\n", + param->string); + goto cifs_parse_mount_err; + } + /* This is the default */ + break; + } + /* For all other value, error */ + pr_warn("Invalid mount helper version specified\n"); + goto cifs_parse_mount_err; + case Opt_vers: + /* protocol version (dialect) */ + if (cifs_parse_smb_version(fc, param->string, ctx, is_smb3) != 0) + goto cifs_parse_mount_err; + ctx->got_version = true; + break; + case Opt_sec: + if (cifs_parse_security_flavors(fc, param->string, ctx) != 0) + goto cifs_parse_mount_err; + break; + case Opt_cache: + if (cifs_parse_cache_flavor(fc, param->string, ctx) != 0) + goto cifs_parse_mount_err; + break; + case Opt_witness: +#ifndef CONFIG_CIFS_SWN_UPCALL + cifs_errorf(fc, "Witness support needs CONFIG_CIFS_SWN_UPCALL config option\n"); + goto cifs_parse_mount_err; +#endif + ctx->witness = true; + pr_warn_once("Witness protocol support is experimental\n"); + break; + case Opt_rootfs: +#ifndef CONFIG_CIFS_ROOT + cifs_dbg(VFS, "rootfs support requires CONFIG_CIFS_ROOT config option\n"); + goto cifs_parse_mount_err; +#endif + ctx->rootfs = true; + break; + case Opt_posixpaths: + if (result.negated) + ctx->posix_paths = 0; + else + ctx->posix_paths = 1; + break; + case Opt_unix: + if (result.negated) { + if (ctx->linux_ext == 1) + pr_warn_once("conflicting posix mount options specified\n"); + ctx->linux_ext = 0; + ctx->no_linux_ext = 1; + } else { + if (ctx->no_linux_ext == 1) + pr_warn_once("conflicting posix mount options specified\n"); + ctx->linux_ext = 1; + ctx->no_linux_ext = 0; + } + break; + case Opt_nocase: + ctx->nocase = 1; + break; + case Opt_brl: + if (result.negated) { + /* + * turn off mandatory locking in mode + * if remote locking is turned off since the + * local vfs will do advisory + */ + if (ctx->file_mode == + (S_IALLUGO & ~(S_ISUID | S_IXGRP))) + ctx->file_mode = S_IALLUGO; + ctx->nobrl = 1; + } else + ctx->nobrl = 0; + break; + case Opt_handlecache: + if (result.negated) + ctx->nohandlecache = 1; + else + ctx->nohandlecache = 0; + break; + case Opt_forcemandatorylock: + ctx->mand_lock = 1; + break; + case Opt_setuids: + ctx->setuids = result.negated; + break; + case Opt_intr: + ctx->intr = !result.negated; + break; + case Opt_setuidfromacl: + ctx->setuidfromacl = 1; + break; + case Opt_strictsync: + ctx->nostrictsync = result.negated; + break; + case Opt_serverino: + ctx->server_ino = !result.negated; + break; + case Opt_rwpidforward: + ctx->rwpidforward = 1; + break; + case Opt_modesid: + ctx->mode_ace = 1; + break; + case Opt_cifsacl: + ctx->cifs_acl = !result.negated; + break; + case Opt_acl: + ctx->no_psx_acl = result.negated; + break; + case Opt_locallease: + ctx->local_lease = 1; + break; + case Opt_sign: + ctx->sign = true; + break; + case Opt_ignore_signature: + ctx->sign = true; + ctx->ignore_signature = true; + break; + case Opt_seal: + /* we do not do the following in secFlags because seal + * is a per tree connection (mount) not a per socket + * or per-smb connection option in the protocol + * vol->secFlg |= CIFSSEC_MUST_SEAL; + */ + ctx->seal = 1; + break; + case Opt_noac: + pr_warn("Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n"); + break; + case Opt_fsc: +#ifndef CONFIG_CIFS_FSCACHE + cifs_errorf(fc, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n"); + goto cifs_parse_mount_err; +#endif + ctx->fsc = true; + break; + case Opt_mfsymlinks: + ctx->mfsymlinks = true; + break; + case Opt_multiuser: + ctx->multiuser = true; + break; + case Opt_sloppy: + ctx->sloppy = true; + break; + case Opt_nosharesock: + ctx->nosharesock = true; + break; + case Opt_persistent: + if (result.negated) { + ctx->nopersistent = true; + if (ctx->persistent) { + cifs_errorf(fc, "persistenthandles mount options conflict\n"); + goto cifs_parse_mount_err; + } + } else { + ctx->persistent = true; + if ((ctx->nopersistent) || (ctx->resilient)) { + cifs_errorf(fc, "persistenthandles mount options conflict\n"); + goto cifs_parse_mount_err; + } + } + break; + case Opt_resilient: + if (result.negated) { + ctx->resilient = false; /* already the default */ + } else { + ctx->resilient = true; + if (ctx->persistent) { + cifs_errorf(fc, "persistenthandles mount options conflict\n"); + goto cifs_parse_mount_err; + } + } + break; + case Opt_tcp_nodelay: + /* tcp nodelay should not usually be needed since we CORK/UNCORK the socket */ + if (result.negated) + ctx->sockopt_tcp_nodelay = false; + else + ctx->sockopt_tcp_nodelay = true; + break; + case Opt_domainauto: + ctx->domainauto = true; + break; + case Opt_rdma: + ctx->rdma = true; + break; + } + /* case Opt_ignore: - is ignored as expected ... */ + + return 0; + + cifs_parse_mount_err: + kfree_sensitive(ctx->password); + return -EINVAL; +} + +int smb3_init_fs_context(struct fs_context *fc) +{ + struct smb3_fs_context *ctx; + char *nodename = utsname()->nodename; + int i; + + ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); + if (unlikely(!ctx)) + return -ENOMEM; + + strscpy(ctx->workstation_name, nodename, sizeof(ctx->workstation_name)); + + /* + * does not have to be perfect mapping since field is + * informational, only used for servers that do not support + * port 445 and it can be overridden at mount time + */ + memset(ctx->source_rfc1001_name, 0x20, RFC1001_NAME_LEN); + for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++) + ctx->source_rfc1001_name[i] = toupper(nodename[i]); + + ctx->source_rfc1001_name[RFC1001_NAME_LEN] = 0; + /* + * null target name indicates to use *SMBSERVR default called name + * if we end up sending RFC1001 session initialize + */ + ctx->target_rfc1001_name[0] = 0; + ctx->cred_uid = current_uid(); + ctx->linux_uid = current_uid(); + ctx->linux_gid = current_gid(); + /* By default 4MB read ahead size, 1MB block size */ + ctx->bsize = CIFS_DEFAULT_IOSIZE; /* can improve cp performance significantly */ + ctx->rasize = 0; /* 0 = use default (ie negotiated rsize) for read ahead pages */ + + /* + * default to SFM style remapping of seven reserved characters + * unless user overrides it or we negotiate CIFS POSIX where + * it is unnecessary. Can not simultaneously use more than one mapping + * since then readdir could list files that open could not open + */ + ctx->remap = true; + + /* default to only allowing write access to owner of the mount */ + ctx->dir_mode = ctx->file_mode = S_IRUGO | S_IXUGO | S_IWUSR; + + /* ctx->retry default is 0 (i.e. "soft" limited retry not hard retry) */ + /* default is always to request posix paths. */ + ctx->posix_paths = 1; + /* default to using server inode numbers where available */ + ctx->server_ino = 1; + + /* default is to use strict cifs caching semantics */ + ctx->strict_io = true; + + ctx->acregmax = CIFS_DEF_ACTIMEO; + ctx->acdirmax = CIFS_DEF_ACTIMEO; + ctx->closetimeo = SMB3_DEF_DCLOSETIMEO; + + /* Most clients set timeout to 0, allows server to use its default */ + ctx->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */ + + /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */ + ctx->ops = &smb30_operations; + ctx->vals = &smbdefault_values; + + ctx->echo_interval = SMB_ECHO_INTERVAL_DEFAULT; + + /* default to no multichannel (single server connection) */ + ctx->multichannel = false; + ctx->max_channels = 1; + + ctx->backupuid_specified = false; /* no backup intent for a user */ + ctx->backupgid_specified = false; /* no backup intent for a group */ + +/* + * short int override_uid = -1; + * short int override_gid = -1; + * char *nodename = strdup(utsname()->nodename); + * struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr; + */ + + fc->fs_private = ctx; + fc->ops = &smb3_fs_context_ops; + return 0; +} + +void +smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx) +{ + if (ctx == NULL) + return; + + /* + * Make sure this stays in sync with smb3_fs_context_dup() + */ + kfree(ctx->mount_options); + ctx->mount_options = NULL; + kfree(ctx->username); + ctx->username = NULL; + kfree_sensitive(ctx->password); + ctx->password = NULL; + kfree(ctx->server_hostname); + ctx->server_hostname = NULL; + kfree(ctx->UNC); + ctx->UNC = NULL; + kfree(ctx->source); + ctx->source = NULL; + kfree(ctx->domainname); + ctx->domainname = NULL; + kfree(ctx->nodename); + ctx->nodename = NULL; + kfree(ctx->iocharset); + ctx->iocharset = NULL; + kfree(ctx->prepath); + ctx->prepath = NULL; +} + +void +smb3_cleanup_fs_context(struct smb3_fs_context *ctx) +{ + if (!ctx) + return; + smb3_cleanup_fs_context_contents(ctx); + kfree(ctx); +} + +void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb) +{ + struct smb3_fs_context *ctx = cifs_sb->ctx; + + if (ctx->nodfs) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_DFS; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_DFS; + + if (ctx->noperm) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_PERM; + + if (ctx->setuids) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SET_UID; + + if (ctx->setuidfromacl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UID_FROM_ACL; + + if (ctx->server_ino) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; + + if (ctx->remap) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SFM_CHR; + + if (ctx->sfu_remap) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SPECIAL_CHR; + + if (ctx->no_xattr) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_XATTR; + + if (ctx->sfu_emul) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UNX_EMUL; + + if (ctx->nobrl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_BRL; + + if (ctx->nohandlecache) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_HANDLE_CACHE; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_HANDLE_CACHE; + + if (ctx->nostrictsync) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOSSYNC; + + if (ctx->mand_lock) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOPOSIXBRL; + + if (ctx->rwpidforward) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_RWPIDFORWARD; + + if (ctx->mode_ace) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MODE_FROM_SID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MODE_FROM_SID; + + if (ctx->cifs_acl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_ACL; + + if (ctx->backupuid_specified) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPUID; + + if (ctx->backupgid_specified) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPGID; + + if (ctx->override_uid) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_UID; + + if (ctx->override_gid) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_GID; + + if (ctx->dynperm) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DYNPERM; + + if (ctx->fsc) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_FSCACHE; + + if (ctx->multiuser) + cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | + CIFS_MOUNT_NO_PERM); + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MULTIUSER; + + + if (ctx->strict_io) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_STRICT_IO; + + if (ctx->direct_io) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DIRECT_IO; + + if (ctx->mfsymlinks) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MF_SYMLINKS; + if (ctx->mfsymlinks) { + if (ctx->sfu_emul) { + /* + * Our SFU ("Services for Unix" emulation does not allow + * creating symlinks but does allow reading existing SFU + * symlinks (it does allow both creating and reading SFU + * style mknod and FIFOs though). When "mfsymlinks" and + * "sfu" are both enabled at the same time, it allows + * reading both types of symlinks, but will only create + * them with mfsymlinks format. This allows better + * Apple compatibility (probably better for Samba too) + * while still recognizing old Windows style symlinks. + */ + cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n"); + } + } + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SHUTDOWN; + + return; +} diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h new file mode 100644 index 000000000000..26093f54d3e6 --- /dev/null +++ b/fs/smb/client/fs_context.h @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Microsoft Corporation. + * + * Author(s): Steve French + * David Howells + */ + +#ifndef _FS_CONTEXT_H +#define _FS_CONTEXT_H + +#include "cifsglob.h" +#include +#include + +/* Log errors in fs_context (new mount api) but also in dmesg (old style) */ +#define cifs_errorf(fc, fmt, ...) \ + do { \ + errorf(fc, fmt, ## __VA_ARGS__); \ + cifs_dbg(VFS, fmt, ## __VA_ARGS__); \ + } while (0) + +enum smb_version { + Smb_1 = 1, + Smb_20, + Smb_21, + Smb_30, + Smb_302, + Smb_311, + Smb_3any, + Smb_default, + Smb_version_err +}; + +enum { + Opt_cache_loose, + Opt_cache_strict, + Opt_cache_none, + Opt_cache_ro, + Opt_cache_rw, + Opt_cache_err +}; + +enum cifs_sec_param { + Opt_sec_krb5, + Opt_sec_krb5i, + Opt_sec_krb5p, + Opt_sec_ntlmsspi, + Opt_sec_ntlmssp, + Opt_sec_ntlmv2, + Opt_sec_ntlmv2i, + Opt_sec_none, + + Opt_sec_err +}; + +enum cifs_param { + /* Mount options that take no arguments */ + Opt_user_xattr, + Opt_forceuid, + Opt_forcegid, + Opt_noblocksend, + Opt_noautotune, + Opt_nolease, + Opt_nosparse, + Opt_hard, + Opt_soft, + Opt_perm, + Opt_nodelete, + Opt_mapposix, + Opt_mapchars, + Opt_nomapchars, + Opt_sfu, + Opt_nodfs, + Opt_posixpaths, + Opt_unix, + Opt_nocase, + Opt_brl, + Opt_handlecache, + Opt_forcemandatorylock, + Opt_setuidfromacl, + Opt_setuids, + Opt_dynperm, + Opt_intr, + Opt_strictsync, + Opt_serverino, + Opt_rwpidforward, + Opt_cifsacl, + Opt_acl, + Opt_locallease, + Opt_sign, + Opt_ignore_signature, + Opt_seal, + Opt_noac, + Opt_fsc, + Opt_mfsymlinks, + Opt_multiuser, + Opt_sloppy, + Opt_nosharesock, + Opt_persistent, + Opt_resilient, + Opt_tcp_nodelay, + Opt_domainauto, + Opt_rdma, + Opt_modesid, + Opt_rootfs, + Opt_multichannel, + Opt_compress, + Opt_witness, + + /* Mount options which take numeric value */ + Opt_backupuid, + Opt_backupgid, + Opt_uid, + Opt_cruid, + Opt_gid, + Opt_port, + Opt_file_mode, + Opt_dirmode, + Opt_min_enc_offload, + Opt_blocksize, + Opt_rasize, + Opt_rsize, + Opt_wsize, + Opt_actimeo, + Opt_acdirmax, + Opt_acregmax, + Opt_closetimeo, + Opt_echo_interval, + Opt_max_credits, + Opt_snapshot, + Opt_max_channels, + Opt_handletimeout, + + /* Mount options which take string value */ + Opt_source, + Opt_user, + Opt_pass, + Opt_ip, + Opt_domain, + Opt_srcaddr, + Opt_iocharset, + Opt_netbiosname, + Opt_servern, + Opt_ver, + Opt_vers, + Opt_sec, + Opt_cache, + + /* Mount options to be ignored */ + Opt_ignore, + + Opt_err +}; + +struct smb3_fs_context { + bool uid_specified; + bool cruid_specified; + bool gid_specified; + bool sloppy; + bool got_ip; + bool got_version; + bool got_rsize; + bool got_wsize; + bool got_bsize; + unsigned short port; + + char *username; + char *password; + char *domainname; + char *source; + char *server_hostname; + char *UNC; + char *nodename; + char workstation_name[CIFS_MAX_WORKSTATION_LEN]; + char *iocharset; /* local code page for mapping to and from Unicode */ + char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ + char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ + kuid_t cred_uid; + kuid_t linux_uid; + kgid_t linux_gid; + kuid_t backupuid; + kgid_t backupgid; + umode_t file_mode; + umode_t dir_mode; + enum securityEnum sectype; /* sectype requested via mnt opts */ + bool sign; /* was signing requested via mnt opts? */ + bool ignore_signature:1; + bool retry:1; + bool intr:1; + bool setuids:1; + bool setuidfromacl:1; + bool override_uid:1; + bool override_gid:1; + bool dynperm:1; + bool noperm:1; + bool nodelete:1; + bool mode_ace:1; + bool no_psx_acl:1; /* set if posix acl support should be disabled */ + bool cifs_acl:1; + bool backupuid_specified; /* mount option backupuid is specified */ + bool backupgid_specified; /* mount option backupgid is specified */ + bool no_xattr:1; /* set if xattr (EA) support should be disabled*/ + bool server_ino:1; /* use inode numbers from server ie UniqueId */ + bool direct_io:1; + bool strict_io:1; /* strict cache behavior */ + bool cache_ro:1; + bool cache_rw:1; + bool remap:1; /* set to remap seven reserved chars in filenames */ + bool sfu_remap:1; /* remap seven reserved chars ala SFU */ + bool posix_paths:1; /* unset to not ask for posix pathnames. */ + bool no_linux_ext:1; + bool linux_ext:1; + bool sfu_emul:1; + bool nullauth:1; /* attempt to authenticate with null user */ + bool nocase:1; /* request case insensitive filenames */ + bool nobrl:1; /* disable sending byte range locks to srv */ + bool nohandlecache:1; /* disable caching dir handles if srvr probs */ + bool mand_lock:1; /* send mandatory not posix byte range lock reqs */ + bool seal:1; /* request transport encryption on share */ + bool nodfs:1; /* Do not request DFS, even if available */ + bool local_lease:1; /* check leases only on local system, not remote */ + bool noblocksnd:1; + bool noautotune:1; + bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ + bool no_lease:1; /* disable requesting leases */ + bool no_sparse:1; /* do not attempt to set files sparse */ + bool fsc:1; /* enable fscache */ + bool mfsymlinks:1; /* use Minshall+French Symlinks */ + bool multiuser:1; + bool rwpidforward:1; /* pid forward for read/write operations */ + bool nosharesock:1; + bool persistent:1; + bool nopersistent:1; + bool resilient:1; /* noresilient not required since not fored for CA */ + bool domainauto:1; + bool rdma:1; + bool multichannel:1; + bool use_client_guid:1; + /* reuse existing guid for multichannel */ + u8 client_guid[SMB2_CLIENT_GUID_SIZE]; + unsigned int bsize; + unsigned int rasize; + unsigned int rsize; + unsigned int wsize; + unsigned int min_offload; + bool sockopt_tcp_nodelay:1; + /* attribute cache timemout for files and directories in jiffies */ + unsigned long acregmax; + unsigned long acdirmax; + /* timeout for deferred close of files in jiffies */ + unsigned long closetimeo; + struct smb_version_operations *ops; + struct smb_version_values *vals; + char *prepath; + struct sockaddr_storage dstaddr; /* destination address */ + struct sockaddr_storage srcaddr; /* allow binding to a local IP */ + struct nls_table *local_nls; /* This is a copy of the pointer in cifs_sb */ + unsigned int echo_interval; /* echo interval in secs */ + __u64 snapshot_time; /* needed for timewarp tokens */ + __u32 handle_timeout; /* persistent and durable handle timeout in ms */ + unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ + unsigned int max_channels; + __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ + bool rootfs:1; /* if it's a SMB root file system */ + bool witness:1; /* use witness protocol */ + + char *mount_options; +}; + +extern const struct fs_parameter_spec smb3_fs_parameters[]; + +extern int smb3_init_fs_context(struct fs_context *fc); +extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx); +extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx); + +static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *fc) +{ + return fc->fs_private; +} + +extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx); +extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); + +/* + * max deferred close timeout (jiffies) - 2^30 + */ +#define SMB3_MAX_DCLOSETIMEO (1 << 30) +#define SMB3_DEF_DCLOSETIMEO (1 * HZ) /* even 1 sec enough to help eg open/write/close/open/read */ + +extern char *cifs_sanitize_prepath(char *prepath, gfp_t gfp); + +#endif diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c new file mode 100644 index 000000000000..f6f3a6b75601 --- /dev/null +++ b/fs/smb/client/fscache.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * CIFS filesystem cache interface + * + * Copyright (c) 2010 Novell, Inc. + * Author(s): Suresh Jayaraman + * + */ +#include "fscache.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifsproto.h" + +static void cifs_fscache_fill_volume_coherency( + struct cifs_tcon *tcon, + struct cifs_fscache_volume_coherency_data *cd) +{ + memset(cd, 0, sizeof(*cd)); + cd->resource_id = cpu_to_le64(tcon->resource_id); + cd->vol_create_time = tcon->vol_create_time; + cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); +} + +int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) +{ + struct cifs_fscache_volume_coherency_data cd; + struct TCP_Server_Info *server = tcon->ses->server; + struct fscache_volume *vcookie; + const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; + size_t slen, i; + char *sharename; + char *key; + int ret = -ENOMEM; + + tcon->fscache = NULL; + switch (sa->sa_family) { + case AF_INET: + case AF_INET6: + break; + default: + cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); + return -EINVAL; + } + + memset(&key, 0, sizeof(key)); + + sharename = extract_sharename(tcon->tree_name); + if (IS_ERR(sharename)) { + cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); + return -EINVAL; + } + + slen = strlen(sharename); + for (i = 0; i < slen; i++) + if (sharename[i] == '/') + sharename[i] = ';'; + + key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); + if (!key) + goto out; + + cifs_fscache_fill_volume_coherency(tcon, &cd); + vcookie = fscache_acquire_volume(key, + NULL, /* preferred_cache */ + &cd, sizeof(cd)); + cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); + if (IS_ERR(vcookie)) { + if (vcookie != ERR_PTR(-EBUSY)) { + ret = PTR_ERR(vcookie); + goto out_2; + } + pr_err("Cache volume key already in use (%s)\n", key); + vcookie = NULL; + } + + tcon->fscache = vcookie; + ret = 0; +out_2: + kfree(key); +out: + kfree(sharename); + return ret; +} + +void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) +{ + struct cifs_fscache_volume_coherency_data cd; + + cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); + + cifs_fscache_fill_volume_coherency(tcon, &cd); + fscache_relinquish_volume(tcon->fscache, &cd, false); + tcon->fscache = NULL; +} + +void cifs_fscache_get_inode_cookie(struct inode *inode) +{ + struct cifs_fscache_inode_coherency_data cd; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); + + cifsi->netfs.cache = + fscache_acquire_cookie(tcon->fscache, 0, + &cifsi->uniqueid, sizeof(cifsi->uniqueid), + &cd, sizeof(cd), + i_size_read(&cifsi->netfs.inode)); +} + +void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) +{ + if (update) { + struct cifs_fscache_inode_coherency_data cd; + loff_t i_size = i_size_read(inode); + + cifs_fscache_fill_coherency(inode, &cd); + fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); + } else { + fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); + } +} + +void cifs_fscache_release_inode_cookie(struct inode *inode) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + + if (cookie) { + cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); + fscache_relinquish_cookie(cookie, false); + cifsi->netfs.cache = NULL; + } +} + +/* + * Fallback page reading interface. + */ +static int fscache_fallback_read_page(struct inode *inode, struct page *page) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + struct iov_iter iter; + struct bio_vec bvec[1]; + int ret; + + memset(&cres, 0, sizeof(cres)); + bvec[0].bv_page = page; + bvec[0].bv_offset = 0; + bvec[0].bv_len = PAGE_SIZE; + iov_iter_bvec(&iter, ITER_DEST, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); + + ret = fscache_begin_read_operation(&cres, cookie); + if (ret < 0) + return ret; + + ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, + NULL, NULL); + fscache_end_operation(&cres); + return ret; +} + +/* + * Fallback page writing interface. + */ +static int fscache_fallback_write_page(struct inode *inode, struct page *page, + bool no_space_allocated_yet) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + struct iov_iter iter; + struct bio_vec bvec[1]; + loff_t start = page_offset(page); + size_t len = PAGE_SIZE; + int ret; + + memset(&cres, 0, sizeof(cres)); + bvec[0].bv_page = page; + bvec[0].bv_offset = 0; + bvec[0].bv_len = PAGE_SIZE; + iov_iter_bvec(&iter, ITER_SOURCE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); + + ret = fscache_begin_write_operation(&cres, cookie); + if (ret < 0) + return ret; + + ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), + no_space_allocated_yet); + if (ret == 0) + ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL); + fscache_end_operation(&cres); + return ret; +} + +/* + * Retrieve a page from FS-Cache + */ +int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) +{ + int ret; + + cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", + __func__, cifs_inode_cookie(inode), page, inode); + + ret = fscache_fallback_read_page(inode, page); + if (ret < 0) + return ret; + + /* Read completed synchronously */ + SetPageUptodate(page); + return 0; +} + +void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) +{ + cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", + __func__, cifs_inode_cookie(inode), page, inode); + + fscache_fallback_write_page(inode, page, true); +} + +/* + * Query the cache occupancy. + */ +int __cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + loff_t start, data_start; + size_t len, data_len; + int ret; + + ret = fscache_begin_read_operation(&cres, cookie); + if (ret < 0) + return ret; + + start = first * PAGE_SIZE; + len = nr_pages * PAGE_SIZE; + ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE, + &data_start, &data_len); + if (ret == 0) { + *_data_first = data_start / PAGE_SIZE; + *_data_nr_pages = len / PAGE_SIZE; + } + + fscache_end_operation(&cres); + return ret; +} diff --git a/fs/smb/client/fscache.h b/fs/smb/client/fscache.h new file mode 100644 index 000000000000..67b601041f0a --- /dev/null +++ b/fs/smb/client/fscache.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * CIFS filesystem cache interface definitions + * + * Copyright (c) 2010 Novell, Inc. + * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> + * + */ +#ifndef _CIFS_FSCACHE_H +#define _CIFS_FSCACHE_H + +#include +#include + +#include "cifsglob.h" + +/* + * Coherency data attached to CIFS volume within the cache + */ +struct cifs_fscache_volume_coherency_data { + __le64 resource_id; /* unique server resource id */ + __le64 vol_create_time; + __le32 vol_serial_number; +} __packed; + +/* + * Coherency data attached to CIFS inode within the cache. + */ +struct cifs_fscache_inode_coherency_data { + __le64 last_write_time_sec; + __le64 last_change_time_sec; + __le32 last_write_time_nsec; + __le32 last_change_time_nsec; +}; + +#ifdef CONFIG_CIFS_FSCACHE + +/* + * fscache.c + */ +extern int cifs_fscache_get_super_cookie(struct cifs_tcon *); +extern void cifs_fscache_release_super_cookie(struct cifs_tcon *); + +extern void cifs_fscache_get_inode_cookie(struct inode *inode); +extern void cifs_fscache_release_inode_cookie(struct inode *); +extern void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update); + +static inline +void cifs_fscache_fill_coherency(struct inode *inode, + struct cifs_fscache_inode_coherency_data *cd) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + memset(cd, 0, sizeof(*cd)); + cd->last_write_time_sec = cpu_to_le64(cifsi->netfs.inode.i_mtime.tv_sec); + cd->last_write_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_mtime.tv_nsec); + cd->last_change_time_sec = cpu_to_le64(cifsi->netfs.inode.i_ctime.tv_sec); + cd->last_change_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_ctime.tv_nsec); +} + + +static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) +{ + return netfs_i_cookie(&CIFS_I(inode)->netfs); +} + +static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) +{ + struct cifs_fscache_inode_coherency_data cd; + + cifs_fscache_fill_coherency(inode, &cd); + fscache_invalidate(cifs_inode_cookie(inode), &cd, + i_size_read(inode), flags); +} + +extern int __cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages); + +static inline int cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages) +{ + if (!cifs_inode_cookie(inode)) + return -ENOBUFS; + return __cifs_fscache_query_occupancy(inode, first, nr_pages, + _data_first, _data_nr_pages); +} + +extern int __cifs_readpage_from_fscache(struct inode *pinode, struct page *ppage); +extern void __cifs_readpage_to_fscache(struct inode *pinode, struct page *ppage); + + +static inline int cifs_readpage_from_fscache(struct inode *inode, + struct page *page) +{ + if (cifs_inode_cookie(inode)) + return __cifs_readpage_from_fscache(inode, page); + return -ENOBUFS; +} + +static inline void cifs_readpage_to_fscache(struct inode *inode, + struct page *page) +{ + if (cifs_inode_cookie(inode)) + __cifs_readpage_to_fscache(inode, page); +} + +#else /* CONFIG_CIFS_FSCACHE */ +static inline +void cifs_fscache_fill_coherency(struct inode *inode, + struct cifs_fscache_inode_coherency_data *cd) +{ +} + +static inline int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { return 0; } +static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {} + +static inline void cifs_fscache_get_inode_cookie(struct inode *inode) {} +static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} +static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {} +static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; } +static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {} + +static inline int cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages) +{ + *_data_first = ULONG_MAX; + *_data_nr_pages = 0; + return -ENOBUFS; +} + +static inline int +cifs_readpage_from_fscache(struct inode *inode, struct page *page) +{ + return -ENOBUFS; +} + +static inline +void cifs_readpage_to_fscache(struct inode *inode, struct page *page) {} + +#endif /* CONFIG_CIFS_FSCACHE */ + +#endif /* _CIFS_FSCACHE_H */ diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c new file mode 100644 index 000000000000..92c1ed9304be --- /dev/null +++ b/fs/smb/client/inode.c @@ -0,0 +1,3093 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2010 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "fs_context.h" +#include "cifs_ioctl.h" +#include "cached_dir.h" + +static void cifs_set_ops(struct inode *inode) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + inode->i_op = &cifs_file_inode_ops; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop = &cifs_file_direct_nobrl_ops; + else + inode->i_fop = &cifs_file_direct_ops; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop = &cifs_file_strict_nobrl_ops; + else + inode->i_fop = &cifs_file_strict_ops; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop = &cifs_file_nobrl_ops; + else { /* not direct, send byte range locks */ + inode->i_fop = &cifs_file_ops; + } + + /* check if server can support readahead */ + if (cifs_sb_master_tcon(cifs_sb)->ses->server->max_read < + PAGE_SIZE + MAX_CIFS_HDR_SIZE) + inode->i_data.a_ops = &cifs_addr_ops_smallbuf; + else + inode->i_data.a_ops = &cifs_addr_ops; + break; + case S_IFDIR: +#ifdef CONFIG_CIFS_DFS_UPCALL + if (IS_AUTOMOUNT(inode)) { + inode->i_op = &cifs_dfs_referral_inode_operations; + } else { +#else /* NO DFS support, treat as a directory */ + { +#endif + inode->i_op = &cifs_dir_inode_ops; + inode->i_fop = &cifs_dir_ops; + } + break; + case S_IFLNK: + inode->i_op = &cifs_symlink_inode_ops; + break; + default: + init_special_inode(inode, inode->i_mode, inode->i_rdev); + break; + } +} + +/* check inode attributes against fattr. If they don't match, tag the + * inode for cache invalidation + */ +static void +cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) +{ + struct cifs_fscache_inode_coherency_data cd; + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + cifs_dbg(FYI, "%s: revalidating inode %llu\n", + __func__, cifs_i->uniqueid); + + if (inode->i_state & I_NEW) { + cifs_dbg(FYI, "%s: inode %llu is new\n", + __func__, cifs_i->uniqueid); + return; + } + + /* don't bother with revalidation if we have an oplock */ + if (CIFS_CACHE_READ(cifs_i)) { + cifs_dbg(FYI, "%s: inode %llu is oplocked\n", + __func__, cifs_i->uniqueid); + return; + } + + /* revalidate if mtime or size have changed */ + fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); + if (timespec64_equal(&inode->i_mtime, &fattr->cf_mtime) && + cifs_i->server_eof == fattr->cf_eof) { + cifs_dbg(FYI, "%s: inode %llu is unchanged\n", + __func__, cifs_i->uniqueid); + return; + } + + cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n", + __func__, cifs_i->uniqueid); + set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); + /* Invalidate fscache cookie */ + cifs_fscache_fill_coherency(&cifs_i->netfs.inode, &cd); + fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0); +} + +/* + * copy nlink to the inode, unless it wasn't provided. Provide + * sane values if we don't have an existing one and none was provided + */ +static void +cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) +{ + /* + * if we're in a situation where we can't trust what we + * got from the server (readdir, some non-unix cases) + * fake reasonable values + */ + if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { + /* only provide fake values on a new inode */ + if (inode->i_state & I_NEW) { + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) + set_nlink(inode, 2); + else + set_nlink(inode, 1); + } + return; + } + + /* we trust the server, so update it */ + set_nlink(inode, fattr->cf_nlink); +} + +/* populate an inode with info from a cifs_fattr struct */ +int +cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + if (!(inode->i_state & I_NEW) && + unlikely(inode_wrong_type(inode, fattr->cf_mode))) { + CIFS_I(inode)->time = 0; /* force reval */ + return -ESTALE; + } + + cifs_revalidate_cache(inode, fattr); + + spin_lock(&inode->i_lock); + fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); + fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode); + fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode); + /* we do not want atime to be less than mtime, it broke some apps */ + if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime) < 0) + inode->i_atime = fattr->cf_mtime; + else + inode->i_atime = fattr->cf_atime; + inode->i_mtime = fattr->cf_mtime; + inode->i_ctime = fattr->cf_ctime; + inode->i_rdev = fattr->cf_rdev; + cifs_nlink_fattr_to_inode(inode, fattr); + inode->i_uid = fattr->cf_uid; + inode->i_gid = fattr->cf_gid; + + /* if dynperm is set, don't clobber existing mode */ + if (inode->i_state & I_NEW || + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) + inode->i_mode = fattr->cf_mode; + + cifs_i->cifsAttrs = fattr->cf_cifsattrs; + + if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) + cifs_i->time = 0; + else + cifs_i->time = jiffies; + + if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) + set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); + else + clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); + + cifs_i->server_eof = fattr->cf_eof; + /* + * Can't safely change the file size here if the client is writing to + * it due to potential races. + */ + if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) { + i_size_write(inode, fattr->cf_eof); + + /* + * i_blocks is not related to (i_size / i_blksize), + * but instead 512 byte (2**9) size is required for + * calculating num blocks. + */ + inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9; + } + + if (S_ISLNK(fattr->cf_mode)) { + kfree(cifs_i->symlink_target); + cifs_i->symlink_target = fattr->cf_symlink_target; + fattr->cf_symlink_target = NULL; + } + spin_unlock(&inode->i_lock); + + if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) + inode->i_flags |= S_AUTOMOUNT; + if (inode->i_state & I_NEW) + cifs_set_ops(inode); + return 0; +} + +void +cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + return; + + fattr->cf_uniqueid = iunique(sb, ROOT_I); +} + +/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */ +void +cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_uniqueid = le64_to_cpu(info->UniqueId); + fattr->cf_bytes = le64_to_cpu(info->NumOfBytes); + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime); + fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange); + /* old POSIX extensions don't get create time */ + + fattr->cf_mode = le64_to_cpu(info->Permissions); + + /* + * Since we set the inode type below we need to mask off + * to avoid strange results if bits set above. + */ + fattr->cf_mode &= ~S_IFMT; + switch (le32_to_cpu(info->Type)) { + case UNIX_FILE: + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + break; + case UNIX_SYMLINK: + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + break; + case UNIX_DIR: + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + break; + case UNIX_CHARDEV: + fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; + fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), + le64_to_cpu(info->DevMinor) & MINORMASK); + break; + case UNIX_BLOCKDEV: + fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; + fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), + le64_to_cpu(info->DevMinor) & MINORMASK); + break; + case UNIX_FIFO: + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + break; + case UNIX_SOCKET: + fattr->cf_mode |= S_IFSOCK; + fattr->cf_dtype = DT_SOCK; + break; + default: + /* safest to call it a file if we do not know */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type)); + break; + } + + fattr->cf_uid = cifs_sb->ctx->linux_uid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { + u64 id = le64_to_cpu(info->Uid); + if (id < ((uid_t)-1)) { + kuid_t uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fattr->cf_uid = uid; + } + } + + fattr->cf_gid = cifs_sb->ctx->linux_gid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { + u64 id = le64_to_cpu(info->Gid); + if (id < ((gid_t)-1)) { + kgid_t gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fattr->cf_gid = gid; + } + } + + fattr->cf_nlink = le64_to_cpu(info->Nlinks); +} + +/* + * Fill a cifs_fattr struct with fake inode info. + * + * Needed to setup cifs_fattr data for the directory which is the + * junction to the new submount (ie to setup the fake directory + * which represents a DFS referral). + */ +static void +cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + cifs_dbg(FYI, "creating fake fattr for DFS referral\n"); + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + ktime_get_coarse_real_ts64(&fattr->cf_mtime); + fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; + fattr->cf_nlink = 2; + fattr->cf_flags = CIFS_FATTR_DFS_REFERRAL; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_get_file_info_unix(struct file *filp) +{ + int rc; + unsigned int xid; + FILE_UNIX_BASIC_INFO find_data; + struct cifs_fattr fattr = {}; + struct inode *inode = file_inode(filp); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + + xid = get_xid(); + + if (cfile->symlink_target) { + fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!fattr.cf_symlink_target) { + rc = -ENOMEM; + goto cifs_gfiunix_out; + } + } + + rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data); + if (!rc) { + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + } else + goto cifs_gfiunix_out; + + rc = cifs_fattr_to_inode(inode, &fattr); + +cifs_gfiunix_out: + free_xid(xid); + return rc; +} + +int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *full_path, + struct super_block *sb, unsigned int xid) +{ + int rc; + FILE_UNIX_BASIC_INFO find_data; + struct cifs_fattr fattr; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + cifs_dbg(FYI, "Getting info on %s\n", full_path); + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + /* could have done a find first instead but this returns more info */ + rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc); + cifs_put_tlink(tlink); + + if (!rc) { + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + } else { + return rc; + } + + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, + full_path); + if (tmprc) + cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); + } + + if (S_ISLNK(fattr.cf_mode) && !fattr.cf_symlink_target) { + if (!server->ops->query_symlink) + return -EOPNOTSUPP; + rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, + &fattr.cf_symlink_target, false); + if (rc) { + cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); + goto cgiiu_exit; + } + } + + if (*pinode == NULL) { + /* get new inode */ + cifs_fill_uniqueid(sb, &fattr); + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) + rc = -ENOMEM; + } else { + /* we already have inode, update it */ + + /* if uniqueid is different, return error */ + if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && + CIFS_I(*pinode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*pinode)->time = 0; /* force reval */ + rc = -ESTALE; + goto cgiiu_exit; + } + + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(*pinode, &fattr); + } + +cgiiu_exit: + kfree(fattr.cf_symlink_target); + return rc; +} +#else +int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *full_path, + struct super_block *sb, unsigned int xid) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_sfu_type(struct cifs_fattr *fattr, const char *path, + struct cifs_sb_info *cifs_sb, unsigned int xid) +{ + int rc; + __u32 oplock; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + char buf[24]; + unsigned int bytes_read; + char *pbuf; + int buf_type = CIFS_NO_BUFFER; + + pbuf = buf; + + fattr->cf_mode &= ~S_IFMT; + + if (fattr->cf_eof == 0) { + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + return 0; + } else if (fattr->cf_eof < 8) { + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + return -EINVAL; /* EOPNOTSUPP? */ + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_READ, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL); + if (rc) { + cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc); + cifs_put_tlink(tlink); + return rc; + } + + /* Read header */ + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = 24; + + rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms, + &bytes_read, &pbuf, &buf_type); + if ((rc == 0) && (bytes_read >= 8)) { + if (memcmp("IntxBLK", pbuf, 8) == 0) { + cifs_dbg(FYI, "Block device\n"); + fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; + if (bytes_read == 24) { + /* we have enough to decode dev num */ + __u64 mjr; /* major */ + __u64 mnr; /* minor */ + mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); + mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); + fattr->cf_rdev = MKDEV(mjr, mnr); + } + } else if (memcmp("IntxCHR", pbuf, 8) == 0) { + cifs_dbg(FYI, "Char device\n"); + fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; + if (bytes_read == 24) { + /* we have enough to decode dev num */ + __u64 mjr; /* major */ + __u64 mnr; /* minor */ + mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); + mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); + fattr->cf_rdev = MKDEV(mjr, mnr); + } + } else if (memcmp("IntxLNK", pbuf, 7) == 0) { + cifs_dbg(FYI, "Symlink\n"); + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + } else { + fattr->cf_mode |= S_IFREG; /* file? */ + fattr->cf_dtype = DT_REG; + rc = -EOPNOTSUPP; + } + } else { + fattr->cf_mode |= S_IFREG; /* then it is a file */ + fattr->cf_dtype = DT_REG; + rc = -EOPNOTSUPP; /* or some unknown SFU type */ + } + + tcon->ses->server->ops->close(xid, tcon, &fid); + cifs_put_tlink(tlink); + return rc; +} + +#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ + +/* + * Fetch mode bits as provided by SFU. + * + * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ? + */ +static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, + struct cifs_sb_info *cifs_sb, unsigned int xid) +{ +#ifdef CONFIG_CIFS_XATTR + ssize_t rc; + char ea_value[4]; + __u32 mode; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + if (tcon->ses->server->ops->query_all_EAs == NULL) { + cifs_put_tlink(tlink); + return -EOPNOTSUPP; + } + + rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path, + "SETFILEBITS", ea_value, 4 /* size of buf */, + cifs_sb); + cifs_put_tlink(tlink); + if (rc < 0) + return (int)rc; + else if (rc > 3) { + mode = le32_to_cpu(*((__le32 *)ea_value)); + fattr->cf_mode &= ~SFBITS_MASK; + cifs_dbg(FYI, "special bits 0%o org mode 0%o\n", + mode, fattr->cf_mode); + fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode; + cifs_dbg(FYI, "special mode bits 0%o\n", mode); + } + + return 0; +#else + return -EOPNOTSUPP; +#endif +} + +/* Fill a cifs_fattr struct with info from POSIX info struct */ +static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, + struct super_block *sb, bool adjust_tz, bool symlink) +{ + struct smb311_posix_qinfo *info = &data->posix_fi; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + memset(fattr, 0, sizeof(*fattr)); + + /* no fattr->flags to set */ + fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); + fattr->cf_uniqueid = le64_to_cpu(info->Inode); + + if (info->LastAccessTime) + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + else + ktime_get_coarse_real_ts64(&fattr->cf_atime); + + fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + + if (adjust_tz) { + fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; + } + + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_createtime = le64_to_cpu(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->HardLinks); + fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); + /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ + /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ + + if (symlink) { + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + fattr->cf_symlink_target = data->symlink_target; + data->symlink_target = NULL; + } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + } else { /* file */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + } + /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */ + + fattr->cf_uid = cifs_sb->ctx->linux_uid; /* TODO: map uid and gid from SID */ + fattr->cf_gid = cifs_sb->ctx->linux_gid; + + cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", + fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); +} + +static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, + struct super_block *sb, bool adjust_tz, bool symlink, + u32 reparse_tag) +{ + struct smb2_file_all_info *info = &data->fi; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); + if (info->DeletePending) + fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; + + if (info->LastAccessTime) + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + else + ktime_get_coarse_real_ts64(&fattr->cf_atime); + + fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + + if (adjust_tz) { + fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; + } + + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_createtime = le64_to_cpu(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); + if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) { + fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_LNK; + } else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) { + fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_FIFO; + } else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) { + fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_SOCK; + } else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) { + fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_CHR; + } else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) { + fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_BLK; + } else if (symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK || + reparse_tag == IO_REPARSE_TAG_NFS) { + fattr->cf_mode = S_IFLNK; + fattr->cf_dtype = DT_LNK; + } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; + fattr->cf_dtype = DT_DIR; + /* + * Server can return wrong NumberOfLinks value for directories + * when Unix extensions are disabled - fake it. + */ + if (!tcon->unix_ext) + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; + } else { + fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_REG; + + /* clear write bits if ATTR_READONLY is set */ + if (fattr->cf_cifsattrs & ATTR_READONLY) + fattr->cf_mode &= ~(S_IWUGO); + + /* + * Don't accept zero nlink from non-unix servers unless + * delete is pending. Instead mark it as unknown. + */ + if ((fattr->cf_nlink < 1) && !tcon->unix_ext && + !info->DeletePending) { + cifs_dbg(VFS, "bogus file nlink value %u\n", + fattr->cf_nlink); + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; + } + } + + if (S_ISLNK(fattr->cf_mode)) { + fattr->cf_symlink_target = data->symlink_target; + data->symlink_target = NULL; + } + + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; +} + +static int +cifs_get_file_info(struct file *filp) +{ + int rc; + unsigned int xid; + struct cifs_open_info_data data = {}; + struct cifs_fattr fattr; + struct inode *inode = file_inode(filp); + struct cifsFileInfo *cfile = filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + bool symlink = false; + u32 tag = 0; + + if (!server->ops->query_file_info) + return -ENOSYS; + + xid = get_xid(); + rc = server->ops->query_file_info(xid, tcon, cfile, &data); + switch (rc) { + case 0: + /* TODO: add support to query reparse tag */ + if (data.symlink_target) { + symlink = true; + tag = IO_REPARSE_TAG_SYMLINK; + } + cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag); + break; + case -EREMOTE: + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + break; + case -EOPNOTSUPP: + case -EINVAL: + /* + * FIXME: legacy server -- fall back to path-based call? + * for now, just skip revalidating and mark inode for + * immediate reval. + */ + rc = 0; + CIFS_I(inode)->time = 0; + goto cgfi_exit; + default: + goto cgfi_exit; + } + + /* + * don't bother with SFU junk here -- just mark inode as needing + * revalidation. + */ + fattr.cf_uniqueid = CIFS_I(inode)->uniqueid; + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(inode, &fattr); +cgfi_exit: + cifs_free_open_info(&data); + free_xid(xid); + return rc; +} + +/* Simple function to return a 64 bit hash of string. Rarely called */ +static __u64 simple_hashstr(const char *str) +{ + const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */ + __u64 hash = 0; + + while (*str) + hash = (hash + (__u64) *str++) * hash_mult; + + return hash; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/** + * cifs_backup_query_path_info - SMB1 fallback code to get ino + * + * Fallback code to get file metadata when we don't have access to + * full_path (EACCES) and have backup creds. + * + * @xid: transaction id used to identify original request in logs + * @tcon: information about the server share we have mounted + * @sb: the superblock stores info such as disk space available + * @full_path: name of the file we are getting the metadata for + * @resp_buf: will be set to cifs resp buf and needs to be freed with + * cifs_buf_release() when done with @data + * @data: will be set to search info result buffer + */ +static int +cifs_backup_query_path_info(int xid, + struct cifs_tcon *tcon, + struct super_block *sb, + const char *full_path, + void **resp_buf, + FILE_ALL_INFO **data) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_search_info info = {0}; + u16 flags; + int rc; + + *resp_buf = NULL; + info.endOfSearch = false; + if (tcon->unix_ext) + info.info_level = SMB_FIND_FILE_UNIX; + else if ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) + info.info_level = SMB_FIND_FILE_INFO_STANDARD; + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; + else /* no srvino useful for fallback to some netapp */ + info.info_level = SMB_FIND_FILE_DIRECTORY_INFO; + + flags = CIFS_SEARCH_CLOSE_ALWAYS | + CIFS_SEARCH_CLOSE_AT_END | + CIFS_SEARCH_BACKUP_SEARCH; + + rc = CIFSFindFirst(xid, tcon, full_path, + cifs_sb, NULL, flags, &info, false); + if (rc) + return rc; + + *resp_buf = (void *)info.ntwrk_buf_start; + *data = (FILE_ALL_INFO *)info.srch_entries_start; + return 0; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb, + struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, struct cifs_fattr *fattr) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct TCP_Server_Info *server = tcon->ses->server; + int rc; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { + if (*inode) + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; + else + fattr->cf_uniqueid = iunique(sb, ROOT_I); + return; + } + + /* + * If we have an inode pass a NULL tcon to ensure we don't + * make a round trip to the server. This only works for SMB2+. + */ + rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path, + &fattr->cf_uniqueid, data); + if (rc) { + /* + * If that fails reuse existing ino or generate one + * and disable server ones + */ + if (*inode) + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; + else { + fattr->cf_uniqueid = iunique(sb, ROOT_I); + cifs_autodisable_serverino(cifs_sb); + } + return; + } + + /* If no errors, check for zero root inode (invalid) */ + if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) { + cifs_dbg(FYI, "Invalid (0) inodenum\n"); + if (*inode) { + /* reuse */ + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; + } else { + /* make an ino by hashing the UNC */ + fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO; + fattr->cf_uniqueid = simple_hashstr(tcon->tree_name); + } + } +} + +static inline bool is_inode_cache_good(struct inode *ino) +{ + return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; +} + +int cifs_get_inode_info(struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, struct super_block *sb, int xid, + const struct cifs_fid *fid) +{ + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + bool adjust_tz = false; + struct cifs_fattr fattr = {0}; + bool is_reparse_point = false; + struct cifs_open_info_data tmp_data = {}; + void *smb1_backup_rsp_buf = NULL; + int rc = 0; + int tmprc = 0; + __u32 reparse_tag = 0; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + /* + * 1. Fetch file metadata if not provided (data) + */ + + if (!data) { + if (is_inode_cache_good(*inode)) { + cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); + goto out; + } + rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data, + &adjust_tz, &is_reparse_point); + data = &tmp_data; + } + + /* + * 2. Convert it to internal cifs metadata (fattr) + */ + + switch (rc) { + case 0: + /* + * If the file is a reparse point, it is more complicated + * since we have to check if its reparse tag matches a known + * special file type e.g. symlink or fifo or char etc. + */ + if (is_reparse_point && data->symlink_target) { + reparse_tag = IO_REPARSE_TAG_SYMLINK; + } else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) && + server->ops->query_reparse_tag) { + tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path, + &reparse_tag); + if (tmprc) + cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc); + if (server->ops->query_symlink) { + tmprc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, + &data->symlink_target, + is_reparse_point); + if (tmprc) + cifs_dbg(FYI, "%s: query_symlink: rc = %d\n", __func__, + tmprc); + } + } + cifs_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag); + break; + case -EREMOTE: + /* DFS link, no metadata available on this server */ + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + break; + case -EACCES: +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* + * perm errors, try again with backup flags if possible + * + * For SMB2 and later the backup intent flag + * is already sent if needed on open and there + * is no path based FindFirst operation to use + * to retry with + */ + if (backup_cred(cifs_sb) && is_smb1_server(server)) { + /* for easier reading */ + FILE_ALL_INFO *fi; + FILE_DIRECTORY_INFO *fdi; + SEARCH_ID_FULL_DIR_INFO *si; + + rc = cifs_backup_query_path_info(xid, tcon, sb, + full_path, + &smb1_backup_rsp_buf, + &fi); + if (rc) + goto out; + + move_cifs_info_to_smb2(&data->fi, fi); + fdi = (FILE_DIRECTORY_INFO *)fi; + si = (SEARCH_ID_FULL_DIR_INFO *)fi; + + cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb); + fattr.cf_uniqueid = le64_to_cpu(si->UniqueId); + /* uniqueid set, skip get inum step */ + goto handle_mnt_opt; + } else { + /* nothing we can do, bail out */ + goto out; + } +#else + goto out; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + break; + default: + cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); + goto out; + } + + /* + * 3. Get or update inode number (fattr.cf_uniqueid) + */ + + cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr); + + /* + * 4. Tweak fattr based on mount options + */ +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +handle_mnt_opt: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + /* query for SFU type info if supported and needed */ + if (fattr.cf_cifsattrs & ATTR_SYSTEM && + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { + tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid); + if (tmprc) + cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc); + } + + /* fill in 0777 bits from ACL */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) { + rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, true, + full_path, fid); + if (rc == -EREMOTE) + rc = 0; + if (rc) { + cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n", + __func__, rc); + goto out; + } + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false, + full_path, fid); + if (rc == -EREMOTE) + rc = 0; + if (rc) { + cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", + __func__, rc); + goto out; + } + } + + /* fill in remaining high mode bits e.g. SUID, VTX */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) + cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); + + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, + full_path); + if (tmprc) + cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); + } + + /* + * 5. Update inode with final fattr data + */ + + if (!*inode) { + *inode = cifs_iget(sb, &fattr); + if (!*inode) + rc = -ENOMEM; + } else { + /* we already have inode, update it */ + + /* if uniqueid is different, return error */ + if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && + CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*inode)->time = 0; /* force reval */ + rc = -ESTALE; + goto out; + } + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(*inode, &fattr); + } +out: + cifs_buf_release(smb1_backup_rsp_buf); + cifs_put_tlink(tlink); + cifs_free_open_info(&tmp_data); + kfree(fattr.cf_symlink_target); + return rc; +} + +int +smb311_posix_get_inode_info(struct inode **inode, + const char *full_path, + struct super_block *sb, unsigned int xid) +{ + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + bool adjust_tz = false; + struct cifs_fattr fattr = {0}; + bool symlink = false; + struct cifs_open_info_data data = {}; + int rc = 0; + int tmprc = 0; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + /* + * 1. Fetch file metadata + */ + + if (is_inode_cache_good(*inode)) { + cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); + goto out; + } + + rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data, &adjust_tz, + &symlink); + + /* + * 2. Convert it to internal cifs metadata (fattr) + */ + + switch (rc) { + case 0: + smb311_posix_info_to_fattr(&fattr, &data, sb, adjust_tz, symlink); + break; + case -EREMOTE: + /* DFS link, no metadata available on this server */ + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + break; + case -EACCES: + /* + * For SMB2 and later the backup intent flag + * is already sent if needed on open and there + * is no path based FindFirst operation to use + * to retry with so nothing we can do, bail out + */ + goto out; + default: + cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); + goto out; + } + + + /* + * 3. Tweak fattr based on mount options + */ + + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, + full_path); + if (tmprc) + cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); + } + + /* + * 4. Update inode with final fattr data + */ + + if (!*inode) { + *inode = cifs_iget(sb, &fattr); + if (!*inode) + rc = -ENOMEM; + } else { + /* we already have inode, update it */ + + /* if uniqueid is different, return error */ + if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && + CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*inode)->time = 0; /* force reval */ + rc = -ESTALE; + goto out; + } + + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(*inode, &fattr); + } +out: + cifs_put_tlink(tlink); + cifs_free_open_info(&data); + kfree(fattr.cf_symlink_target); + return rc; +} + + +static const struct inode_operations cifs_ipc_inode_ops = { + .lookup = cifs_lookup, +}; + +static int +cifs_find_inode(struct inode *inode, void *opaque) +{ + struct cifs_fattr *fattr = opaque; + + /* don't match inode with different uniqueid */ + if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) + return 0; + + /* use createtime like an i_generation field */ + if (CIFS_I(inode)->createtime != fattr->cf_createtime) + return 0; + + /* don't match inode of different type */ + if (inode_wrong_type(inode, fattr->cf_mode)) + return 0; + + /* if it's not a directory or has no dentries, then flag it */ + if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) + fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; + + return 1; +} + +static int +cifs_init_inode(struct inode *inode, void *opaque) +{ + struct cifs_fattr *fattr = opaque; + + CIFS_I(inode)->uniqueid = fattr->cf_uniqueid; + CIFS_I(inode)->createtime = fattr->cf_createtime; + return 0; +} + +/* + * walk dentry list for an inode and report whether it has aliases that + * are hashed. We use this to determine if a directory inode can actually + * be used. + */ +static bool +inode_has_hashed_dentries(struct inode *inode) +{ + struct dentry *dentry; + + spin_lock(&inode->i_lock); + hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + if (!d_unhashed(dentry) || IS_ROOT(dentry)) { + spin_unlock(&inode->i_lock); + return true; + } + } + spin_unlock(&inode->i_lock); + return false; +} + +/* Given fattrs, get a corresponding inode */ +struct inode * +cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) +{ + unsigned long hash; + struct inode *inode; + +retry_iget5_locked: + cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid); + + /* hash down to 32-bits on 32-bit arch */ + hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); + + inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); + if (inode) { + /* was there a potentially problematic inode collision? */ + if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { + fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; + + if (inode_has_hashed_dentries(inode)) { + cifs_autodisable_serverino(CIFS_SB(sb)); + iput(inode); + fattr->cf_uniqueid = iunique(sb, ROOT_I); + goto retry_iget5_locked; + } + } + + /* can't fail - see cifs_find_inode() */ + cifs_fattr_to_inode(inode, fattr); + if (sb->s_flags & SB_NOATIME) + inode->i_flags |= S_NOATIME | S_NOCMTIME; + if (inode->i_state & I_NEW) { + inode->i_ino = hash; + cifs_fscache_get_inode_cookie(inode); + unlock_new_inode(inode); + } + } + + return inode; +} + +/* gets root inode */ +struct inode *cifs_root_iget(struct super_block *sb) +{ + unsigned int xid; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct inode *inode = NULL; + long rc; + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + char *path = NULL; + int len; + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) + && cifs_sb->prepath) { + len = strlen(cifs_sb->prepath); + path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL); + if (path == NULL) + return ERR_PTR(-ENOMEM); + path[0] = '/'; + memcpy(path+1, cifs_sb->prepath, len); + } else { + path = kstrdup("", GFP_KERNEL); + if (path == NULL) + return ERR_PTR(-ENOMEM); + } + + xid = get_xid(); + if (tcon->unix_ext) { + rc = cifs_get_inode_info_unix(&inode, path, sb, xid); + /* some servers mistakenly claim POSIX support */ + if (rc != -EOPNOTSUPP) + goto iget_no_retry; + cifs_dbg(VFS, "server does not support POSIX extensions\n"); + tcon->unix_ext = false; + } + + convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, path, sb, xid); + else + rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL); + +iget_no_retry: + if (!inode) { + inode = ERR_PTR(rc); + goto out; + } + + if (rc && tcon->pipe) { + cifs_dbg(FYI, "ipc connection - fake read inode\n"); + spin_lock(&inode->i_lock); + inode->i_mode |= S_IFDIR; + set_nlink(inode, 2); + inode->i_op = &cifs_ipc_inode_ops; + inode->i_fop = &simple_dir_operations; + inode->i_uid = cifs_sb->ctx->linux_uid; + inode->i_gid = cifs_sb->ctx->linux_gid; + spin_unlock(&inode->i_lock); + } else if (rc) { + iget_failed(inode); + inode = ERR_PTR(rc); + } + +out: + kfree(path); + free_xid(xid); + return inode; +} + +int +cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid, + const char *full_path, __u32 dosattr) +{ + bool set_time = false; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct TCP_Server_Info *server; + FILE_BASIC_INFO info_buf; + + if (attrs == NULL) + return -EINVAL; + + server = cifs_sb_master_tcon(cifs_sb)->ses->server; + if (!server->ops->set_file_info) + return -ENOSYS; + + info_buf.Pad = 0; + + if (attrs->ia_valid & ATTR_ATIME) { + set_time = true; + info_buf.LastAccessTime = + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); + } else + info_buf.LastAccessTime = 0; + + if (attrs->ia_valid & ATTR_MTIME) { + set_time = true; + info_buf.LastWriteTime = + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); + } else + info_buf.LastWriteTime = 0; + + /* + * Samba throws this field away, but windows may actually use it. + * Do not set ctime unless other time stamps are changed explicitly + * (i.e. by utimes()) since we would then have a mix of client and + * server times. + */ + if (set_time && (attrs->ia_valid & ATTR_CTIME)) { + cifs_dbg(FYI, "CIFS - CTIME changed\n"); + info_buf.ChangeTime = + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); + } else + info_buf.ChangeTime = 0; + + info_buf.CreationTime = 0; /* don't change */ + info_buf.Attributes = cpu_to_le32(dosattr); + + return server->ops->set_file_info(inode, full_path, &info_buf, xid); +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/* + * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit + * and rename it to a random name that hopefully won't conflict with + * anything else. + */ +int +cifs_rename_pending_delete(const char *full_path, struct dentry *dentry, + const unsigned int xid) +{ + int oplock = 0; + int rc; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + __u32 dosattr, origattr; + FILE_BASIC_INFO *info_buf = NULL; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + /* + * We cannot rename the file if the server doesn't support + * CAP_INFOLEVEL_PASSTHRU + */ + if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) { + rc = -EBUSY; + goto out; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = DELETE | FILE_WRITE_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc != 0) + goto out; + + origattr = cifsInode->cifsAttrs; + if (origattr == 0) + origattr |= ATTR_NORMAL; + + dosattr = origattr & ~ATTR_READONLY; + if (dosattr == 0) + dosattr |= ATTR_NORMAL; + dosattr |= ATTR_HIDDEN; + + /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */ + if (dosattr != origattr) { + info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); + if (info_buf == NULL) { + rc = -ENOMEM; + goto out_close; + } + info_buf->Attributes = cpu_to_le32(dosattr); + rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, + current->tgid); + /* although we would like to mark the file hidden + if that fails we will still try to rename it */ + if (!rc) + cifsInode->cifsAttrs = dosattr; + else + dosattr = origattr; /* since not able to change them */ + } + + /* rename the file */ + rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (rc != 0) { + rc = -EBUSY; + goto undo_setattr; + } + + /* try to set DELETE_ON_CLOSE */ + if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) { + rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid, + current->tgid); + /* + * some samba versions return -ENOENT when we try to set the + * file disposition here. Likely a samba bug, but work around + * it for now. This means that some cifsXXX files may hang + * around after they shouldn't. + * + * BB: remove this hack after more servers have the fix + */ + if (rc == -ENOENT) + rc = 0; + else if (rc != 0) { + rc = -EBUSY; + goto undo_rename; + } + set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags); + } + +out_close: + CIFSSMBClose(xid, tcon, fid.netfid); +out: + kfree(info_buf); + cifs_put_tlink(tlink); + return rc; + + /* + * reset everything back to the original state. Don't bother + * dealing with errors here since we can't do anything about + * them anyway. + */ +undo_rename: + CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name, + cifs_sb->local_nls, cifs_remap(cifs_sb)); +undo_setattr: + if (dosattr != origattr) { + info_buf->Attributes = cpu_to_le32(origattr); + if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, + current->tgid)) + cifsInode->cifsAttrs = origattr; + } + + goto out_close; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* copied from fs/nfs/dir.c with small changes */ +static void +cifs_drop_nlink(struct inode *inode) +{ + spin_lock(&inode->i_lock); + if (inode->i_nlink > 0) + drop_nlink(inode); + spin_unlock(&inode->i_lock); +} + +/* + * If d_inode(dentry) is null (usually meaning the cached dentry + * is a negative dentry) then we would attempt a standard SMB delete, but + * if that fails we can not attempt the fall back mechanisms on EACCES + * but will return the EACCES to the caller. Note that the VFS does not call + * unlink on negative dentries currently. + */ +int cifs_unlink(struct inode *dir, struct dentry *dentry) +{ + int rc = 0; + unsigned int xid; + const char *full_path; + void *page; + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cifs_inode; + struct super_block *sb = dir->i_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct iattr *attrs = NULL; + __u32 dosattr = 0, origattr = 0; + + cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry); + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + xid = get_xid(); + page = alloc_dentry_path(); + + if (tcon->nodelete) { + rc = -EACCES; + goto unlink_out; + } + + /* Unlink can be called from rename so we can not take the + * sb->s_vfs_rename_mutex here */ + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto unlink_out; + } + + cifs_close_deferred_file_under_dentry(tcon, full_path); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + rc = CIFSPOSIXDelFile(xid, tcon, full_path, + SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + cifs_dbg(FYI, "posix del rc %d\n", rc); + if ((rc == 0) || (rc == -ENOENT)) + goto psx_del_no_retry; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +retry_std_delete: + if (!server->ops->unlink) { + rc = -ENOSYS; + goto psx_del_no_retry; + } + + rc = server->ops->unlink(xid, tcon, full_path, cifs_sb); + +psx_del_no_retry: + if (!rc) { + if (inode) + cifs_drop_nlink(inode); + } else if (rc == -ENOENT) { + d_drop(dentry); + } else if (rc == -EBUSY) { + if (server->ops->rename_pending_delete) { + rc = server->ops->rename_pending_delete(full_path, + dentry, xid); + if (rc == 0) + cifs_drop_nlink(inode); + } + } else if ((rc == -EACCES) && (dosattr == 0) && inode) { + attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); + if (attrs == NULL) { + rc = -ENOMEM; + goto out_reval; + } + + /* try to reset dos attributes */ + cifs_inode = CIFS_I(inode); + origattr = cifs_inode->cifsAttrs; + if (origattr == 0) + origattr |= ATTR_NORMAL; + dosattr = origattr & ~ATTR_READONLY; + if (dosattr == 0) + dosattr |= ATTR_NORMAL; + dosattr |= ATTR_HIDDEN; + + rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); + if (rc != 0) + goto out_reval; + + goto retry_std_delete; + } + + /* undo the setattr if we errored out and it's needed */ + if (rc != 0 && dosattr != 0) + cifs_set_file_info(inode, attrs, xid, full_path, origattr); + +out_reval: + if (inode) { + cifs_inode = CIFS_I(inode); + cifs_inode->time = 0; /* will force revalidate to get info + when needed */ + inode->i_ctime = current_time(inode); + } + dir->i_ctime = dir->i_mtime = current_time(dir); + cifs_inode = CIFS_I(dir); + CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ +unlink_out: + free_dentry_path(page); + kfree(attrs); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static int +cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, + const char *full_path, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, const unsigned int xid) +{ + int rc = 0; + struct inode *inode = NULL; + + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + else if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, + xid); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + else + rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb, + xid, NULL); + + if (rc) + return rc; + + if (!S_ISDIR(inode->i_mode)) { + /* + * mkdir succeeded, but another client has managed to remove the + * sucker and replace it with non-directory. Return success, + * but don't leave the child in dcache. + */ + iput(inode); + d_drop(dentry); + return 0; + } + /* + * setting nlink not necessary except in cases where we failed to get it + * from the server or was set bogus. Also, since this is a brand new + * inode, no need to grab the i_lock before setting the i_nlink. + */ + if (inode->i_nlink < 2) + set_nlink(inode, 2); + mode &= ~current_umask(); + /* must turn on setgid bit if parent dir has it */ + if (parent->i_mode & S_ISGID) + mode |= S_ISGID; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext) { + struct cifs_unix_set_info_args args = { + .mode = mode, + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = 0, + }; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + args.uid = current_fsuid(); + if (parent->i_mode & S_ISGID) + args.gid = parent->i_gid; + else + args.gid = current_fsgid(); + } else { + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ + } + CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + } else { +#else + { +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + struct TCP_Server_Info *server = tcon->ses->server; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && + (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo) + server->ops->mkdir_setinfo(inode, full_path, cifs_sb, + tcon, xid); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) + inode->i_mode = (mode | S_IFDIR); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + inode->i_uid = current_fsuid(); + if (inode->i_mode & S_ISGID) + inode->i_gid = parent->i_gid; + else + inode->i_gid = current_fsgid(); + } + } + d_instantiate(dentry, inode); + return 0; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode, + const char *full_path, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, const unsigned int xid) +{ + int rc = 0; + u32 oplock = 0; + FILE_UNIX_BASIC_INFO *info = NULL; + struct inode *newinode = NULL; + struct cifs_fattr fattr; + + info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); + if (info == NULL) { + rc = -ENOMEM; + goto posix_mkdir_out; + } + + mode &= ~current_umask(); + rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, + NULL /* netfid */, info, &oplock, full_path, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + if (rc == -EOPNOTSUPP) + goto posix_mkdir_out; + else if (rc) { + cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc); + d_drop(dentry); + goto posix_mkdir_out; + } + + if (info->Type == cpu_to_le32(-1)) + /* no return info, go query for it */ + goto posix_mkdir_get_info; + /* + * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if + * need to set uid/gid. + */ + + cifs_unix_basic_to_fattr(&fattr, info, cifs_sb); + cifs_fill_uniqueid(inode->i_sb, &fattr); + newinode = cifs_iget(inode->i_sb, &fattr); + if (!newinode) + goto posix_mkdir_get_info; + + d_instantiate(dentry, newinode); + +#ifdef CONFIG_CIFS_DEBUG2 + cifs_dbg(FYI, "instantiated dentry %p %pd to inode %p\n", + dentry, dentry, newinode); + + if (newinode->i_nlink != 2) + cifs_dbg(FYI, "unexpected number of links %d\n", + newinode->i_nlink); +#endif + +posix_mkdir_out: + kfree(info); + return rc; +posix_mkdir_get_info: + rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon, + xid); + goto posix_mkdir_out; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, umode_t mode) +{ + int rc = 0; + unsigned int xid; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + const char *full_path; + void *page; + + cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", + mode, inode); + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + xid = get_xid(); + + page = alloc_dentry_path(); + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto mkdir_out; + } + + server = tcon->ses->server; + + if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { + rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, + cifs_sb); + d_drop(direntry); /* for time being always refresh inode info */ + goto mkdir_out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, + tcon, xid); + if (rc != -EOPNOTSUPP) + goto mkdir_out; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (!server->ops->mkdir) { + rc = -ENOSYS; + goto mkdir_out; + } + + /* BB add setting the equivalent of mode via CreateX w/ACLs */ + rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb); + if (rc) { + cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc); + d_drop(direntry); + goto mkdir_out; + } + + /* TODO: skip this for smb2/smb3 */ + rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, + xid); +mkdir_out: + /* + * Force revalidate to get parent dir info when needed since cached + * attributes are invalid now. + */ + CIFS_I(inode)->time = 0; + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +int cifs_rmdir(struct inode *inode, struct dentry *direntry) +{ + int rc = 0; + unsigned int xid; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + const char *full_path; + void *page = alloc_dentry_path(); + struct cifsInodeInfo *cifsInode; + + cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode); + + xid = get_xid(); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto rmdir_exit; + } + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) { + rc = -EIO; + goto rmdir_exit; + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto rmdir_exit; + } + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + if (!server->ops->rmdir) { + rc = -ENOSYS; + cifs_put_tlink(tlink); + goto rmdir_exit; + } + + if (tcon->nodelete) { + rc = -EACCES; + cifs_put_tlink(tlink); + goto rmdir_exit; + } + + rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb); + cifs_put_tlink(tlink); + + if (!rc) { + spin_lock(&d_inode(direntry)->i_lock); + i_size_write(d_inode(direntry), 0); + clear_nlink(d_inode(direntry)); + spin_unlock(&d_inode(direntry)->i_lock); + } + + cifsInode = CIFS_I(d_inode(direntry)); + /* force revalidate to go get info when needed */ + cifsInode->time = 0; + + cifsInode = CIFS_I(inode); + /* + * Force revalidate to get parent dir info when needed since cached + * attributes are invalid now. + */ + cifsInode->time = 0; + + d_inode(direntry)->i_ctime = inode->i_ctime = inode->i_mtime = + current_time(inode); + +rmdir_exit: + free_dentry_path(page); + free_xid(xid); + return rc; +} + +static int +cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, + const char *from_path, struct dentry *to_dentry, + const char *to_path) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_fid fid; + struct cifs_open_parms oparms; + int oplock; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + int rc; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + if (!server->ops->rename) + return -ENOSYS; + + /* try path-based rename first */ + rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb); + + /* + * Don't bother with rename by filehandle unless file is busy and + * source. Note that cross directory moves do not work with + * rename by filehandle to various Windows servers. + */ + if (rc == 0 || rc != -EBUSY) + goto do_rename_exit; + + /* Don't fall back to using SMB on SMB 2+ mount */ + if (server->vals->protocol_id != 0) + goto do_rename_exit; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* open-file renames don't work across directories */ + if (to_dentry->d_parent != from_dentry->d_parent) + goto do_rename_exit; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + /* open the file to be renamed -- we need DELETE perms */ + .desired_access = DELETE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = from_path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc == 0) { + rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, + (const char *) to_dentry->d_name.name, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + CIFSSMBClose(xid, tcon, fid.netfid); + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +do_rename_exit: + if (rc == 0) + d_move(from_dentry, to_dentry); + cifs_put_tlink(tlink); + return rc; +} + +int +cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, + struct dentry *source_dentry, struct inode *target_dir, + struct dentry *target_dentry, unsigned int flags) +{ + const char *from_name, *to_name; + void *page1, *page2; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + unsigned int xid; + int rc, tmprc; + int retry_count = 0; + FILE_UNIX_BASIC_INFO *info_buf_source = NULL; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + FILE_UNIX_BASIC_INFO *info_buf_target; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + + cifs_sb = CIFS_SB(source_dir->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + page1 = alloc_dentry_path(); + page2 = alloc_dentry_path(); + xid = get_xid(); + + from_name = build_path_from_dentry(source_dentry, page1); + if (IS_ERR(from_name)) { + rc = PTR_ERR(from_name); + goto cifs_rename_exit; + } + + to_name = build_path_from_dentry(target_dentry, page2); + if (IS_ERR(to_name)) { + rc = PTR_ERR(to_name); + goto cifs_rename_exit; + } + + cifs_close_deferred_file_under_dentry(tcon, from_name); + if (d_inode(target_dentry) != NULL) + cifs_close_deferred_file_under_dentry(tcon, to_name); + + rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, + to_name); + + if (rc == -EACCES) { + while (retry_count < 3) { + cifs_close_all_deferred_files(tcon); + rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, + to_name); + if (rc != -EACCES) + break; + retry_count++; + } + } + + /* + * No-replace is the natural behavior for CIFS, so skip unlink hacks. + */ + if (flags & RENAME_NOREPLACE) + goto cifs_rename_exit; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (rc == -EEXIST && tcon->unix_ext) { + /* + * Are src and dst hardlinks of same inode? We can only tell + * with unix extensions enabled. + */ + info_buf_source = + kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO), + GFP_KERNEL); + if (info_buf_source == NULL) { + rc = -ENOMEM; + goto cifs_rename_exit; + } + + info_buf_target = info_buf_source + 1; + tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name, + info_buf_source, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (tmprc != 0) + goto unlink_target; + + tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name, + info_buf_target, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + + if (tmprc == 0 && (info_buf_source->UniqueId == + info_buf_target->UniqueId)) { + /* same file, POSIX says that this is a noop */ + rc = 0; + goto cifs_rename_exit; + } + } + /* + * else ... BB we could add the same check for Windows by + * checking the UniqueId via FILE_INTERNAL_INFO + */ + +unlink_target: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + /* Try unlinking the target dentry if it's not negative */ + if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) { + if (d_is_dir(target_dentry)) + tmprc = cifs_rmdir(target_dir, target_dentry); + else + tmprc = cifs_unlink(target_dir, target_dentry); + if (tmprc) + goto cifs_rename_exit; + rc = cifs_do_rename(xid, source_dentry, from_name, + target_dentry, to_name); + } + + /* force revalidate to go get info when needed */ + CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; + + source_dir->i_ctime = source_dir->i_mtime = target_dir->i_ctime = + target_dir->i_mtime = current_time(source_dir); + +cifs_rename_exit: + kfree(info_buf_source); + free_dentry_path(page2); + free_dentry_path(page1); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static bool +cifs_dentry_needs_reval(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct cached_fid *cfid = NULL; + + if (cifs_i->time == 0) + return true; + + if (CIFS_CACHE_READ(cifs_i)) + return false; + + if (!lookupCacheEnabled) + return true; + + if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { + spin_lock(&cfid->fid_lock); + if (cfid->time && cifs_i->time > cfid->time) { + spin_unlock(&cfid->fid_lock); + close_cached_dir(cfid); + return false; + } + spin_unlock(&cfid->fid_lock); + close_cached_dir(cfid); + } + /* + * depending on inode type, check if attribute caching disabled for + * files or directories + */ + if (S_ISDIR(inode->i_mode)) { + if (!cifs_sb->ctx->acdirmax) + return true; + if (!time_in_range(jiffies, cifs_i->time, + cifs_i->time + cifs_sb->ctx->acdirmax)) + return true; + } else { /* file */ + if (!cifs_sb->ctx->acregmax) + return true; + if (!time_in_range(jiffies, cifs_i->time, + cifs_i->time + cifs_sb->ctx->acregmax)) + return true; + } + + /* hardlinked files w/ noserverino get "special" treatment */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && + S_ISREG(inode->i_mode) && inode->i_nlink != 1) + return true; + + return false; +} + +/* + * Zap the cache. Called when invalid_mapping flag is set. + */ +int +cifs_invalidate_mapping(struct inode *inode) +{ + int rc = 0; + + if (inode->i_mapping && inode->i_mapping->nrpages != 0) { + rc = invalidate_inode_pages2(inode->i_mapping); + if (rc) + cifs_dbg(VFS, "%s: Could not invalidate inode %p\n", + __func__, inode); + } + + return rc; +} + +/** + * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks + * + * @key: currently unused + * @mode: the task state to sleep in + */ +static int +cifs_wait_bit_killable(struct wait_bit_key *key, int mode) +{ + schedule(); + if (signal_pending_state(mode, current)) + return -ERESTARTSYS; + return 0; +} + +int +cifs_revalidate_mapping(struct inode *inode) +{ + int rc; + unsigned long *flags = &CIFS_I(inode)->flags; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + /* swapfiles are not supposed to be shared */ + if (IS_SWAPFILE(inode)) + return 0; + + rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, + TASK_KILLABLE|TASK_FREEZABLE_UNSAFE); + if (rc) + return rc; + + if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { + /* for cache=singleclient, do not invalidate */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) + goto skip_invalidate; + + rc = cifs_invalidate_mapping(inode); + if (rc) + set_bit(CIFS_INO_INVALID_MAPPING, flags); + } + +skip_invalidate: + clear_bit_unlock(CIFS_INO_LOCK, flags); + smp_mb__after_atomic(); + wake_up_bit(flags, CIFS_INO_LOCK); + + return rc; +} + +int +cifs_zap_mapping(struct inode *inode) +{ + set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); + return cifs_revalidate_mapping(inode); +} + +int cifs_revalidate_file_attr(struct file *filp) +{ + int rc = 0; + struct dentry *dentry = file_dentry(filp); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (!cifs_dentry_needs_reval(dentry)) + return rc; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tlink_tcon(cfile->tlink)->unix_ext) + rc = cifs_get_file_info_unix(filp); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = cifs_get_file_info(filp); + + return rc; +} + +int cifs_revalidate_dentry_attr(struct dentry *dentry) +{ + unsigned int xid; + int rc = 0; + struct inode *inode = d_inode(dentry); + struct super_block *sb = dentry->d_sb; + const char *full_path; + void *page; + int count = 0; + + if (inode == NULL) + return -ENOENT; + + if (!cifs_dentry_needs_reval(dentry)) + return rc; + + xid = get_xid(); + + page = alloc_dentry_path(); + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n", + full_path, inode, inode->i_count.counter, + dentry, cifs_get_time(dentry), jiffies); + +again: + if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid); + else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); + else + rc = cifs_get_inode_info(&inode, full_path, NULL, sb, + xid, NULL); + if (rc == -EAGAIN && count++ < 10) + goto again; +out: + free_dentry_path(page); + free_xid(xid); + + return rc; +} + +int cifs_revalidate_file(struct file *filp) +{ + int rc; + struct inode *inode = file_inode(filp); + + rc = cifs_revalidate_file_attr(filp); + if (rc) + return rc; + + return cifs_revalidate_mapping(inode); +} + +/* revalidate a dentry's inode attributes */ +int cifs_revalidate_dentry(struct dentry *dentry) +{ + int rc; + struct inode *inode = d_inode(dentry); + + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + + return cifs_revalidate_mapping(inode); +} + +int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path, + struct kstat *stat, u32 request_mask, unsigned int flags) +{ + struct dentry *dentry = path->dentry; + struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct inode *inode = d_inode(dentry); + int rc; + + if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) + return -EIO; + + /* + * We need to be sure that all dirty pages are written and the server + * has actual ctime, mtime and file length. + */ + if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE | STATX_BLOCKS)) && + !CIFS_CACHE_READ(CIFS_I(inode)) && + inode->i_mapping && inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + if (rc) { + mapping_set_error(inode->i_mapping, rc); + return rc; + } + } + + if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC) + CIFS_I(inode)->time = 0; /* force revalidate */ + + /* + * If the caller doesn't require syncing, only sync if + * necessary (e.g. due to earlier truncate or setattr + * invalidating the cached metadata) + */ + if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) || + (CIFS_I(inode)->time == 0)) { + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + } + + generic_fillattr(&init_user_ns, inode, stat); + stat->blksize = cifs_sb->ctx->bsize; + stat->ino = CIFS_I(inode)->uniqueid; + + /* old CIFS Unix Extensions doesn't return create time */ + if (CIFS_I(inode)->createtime) { + stat->result_mask |= STATX_BTIME; + stat->btime = + cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime)); + } + + stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED); + if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED) + stat->attributes |= STATX_ATTR_COMPRESSED; + if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED) + stat->attributes |= STATX_ATTR_ENCRYPTED; + + /* + * If on a multiuser mount without unix extensions or cifsacl being + * enabled, and the admin hasn't overridden them, set the ownership + * to the fsuid/fsgid of the current process. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && + !tcon->unix_ext) { + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) + stat->uid = current_fsuid(); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) + stat->gid = current_fsgid(); + } + return 0; +} + +int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, + u64 len) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->netfs.inode.i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + struct cifsFileInfo *cfile; + int rc; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + /* + * We need to be sure that all dirty pages are written as they + * might fill holes on the server. + */ + if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && + inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + if (rc) { + mapping_set_error(inode->i_mapping, rc); + return rc; + } + } + + cfile = find_readable_file(cifs_i, false); + if (cfile == NULL) + return -EINVAL; + + if (server->ops->fiemap) { + rc = server->ops->fiemap(tcon, cfile, fei, start, len); + cifsFileInfo_put(cfile); + return rc; + } + + cifsFileInfo_put(cfile); + return -ENOTSUPP; +} + +int cifs_truncate_page(struct address_space *mapping, loff_t from) +{ + pgoff_t index = from >> PAGE_SHIFT; + unsigned offset = from & (PAGE_SIZE - 1); + struct page *page; + int rc = 0; + + page = grab_cache_page(mapping, index); + if (!page) + return -ENOMEM; + + zero_user_segment(page, offset, PAGE_SIZE); + unlock_page(page); + put_page(page); + return rc; +} + +void cifs_setsize(struct inode *inode, loff_t offset) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + spin_lock(&inode->i_lock); + i_size_write(inode, offset); + spin_unlock(&inode->i_lock); + + /* Cached inode must be refreshed on truncate */ + cifs_i->time = 0; + truncate_pagecache(inode, offset); +} + +static int +cifs_set_file_size(struct inode *inode, struct iattr *attrs, + unsigned int xid, const char *full_path) +{ + int rc; + struct cifsFileInfo *open_file; + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon = NULL; + struct TCP_Server_Info *server; + + /* + * To avoid spurious oplock breaks from server, in the case of + * inodes that we already have open, avoid doing path based + * setting of file size if we can do it by handle. + * This keeps our caching token (oplock) and avoids timeouts + * when the local oplock break takes longer to flush + * writebehind data than the SMB timeout for the SetPathInfo + * request would allow + */ + open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); + if (open_file) { + tcon = tlink_tcon(open_file->tlink); + server = tcon->ses->server; + if (server->ops->set_file_size) + rc = server->ops->set_file_size(xid, tcon, open_file, + attrs->ia_size, false); + else + rc = -ENOSYS; + cifsFileInfo_put(open_file); + cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc); + } else + rc = -EINVAL; + + if (!rc) + goto set_size_out; + + if (tcon == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + } + + /* + * Set file size by pathname rather than by handle either because no + * valid, writeable file handle for it was found or because there was + * an error setting it by handle. + */ + if (server->ops->set_path_size) + rc = server->ops->set_path_size(xid, tcon, full_path, + attrs->ia_size, cifs_sb, false); + else + rc = -ENOSYS; + cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); + + if (tlink) + cifs_put_tlink(tlink); + +set_size_out: + if (rc == 0) { + cifsInode->server_eof = attrs->ia_size; + cifs_setsize(inode, attrs->ia_size); + /* + * i_blocks is not related to (i_size / i_blksize), but instead + * 512 byte (2**9) size is required for calculating num blocks. + * Until we can query the server for actual allocation size, + * this is best estimate we have for blocks allocated for a file + * Number of blocks must be rounded up so size 1 is not 0 blocks + */ + inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9; + + /* + * The man page of truncate says if the size changed, + * then the st_ctime and st_mtime fields for the file + * are updated. + */ + attrs->ia_ctime = attrs->ia_mtime = current_time(inode); + attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME; + + cifs_truncate_page(inode->i_mapping, inode->i_size); + } + + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) +{ + int rc; + unsigned int xid; + const char *full_path; + void *page = alloc_dentry_path(); + struct inode *inode = d_inode(direntry); + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + struct cifs_unix_set_info_args *args = NULL; + struct cifsFileInfo *open_file; + + cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n", + direntry, attrs->ia_valid); + + xid = get_xid(); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = setattr_prepare(&init_user_ns, direntry, attrs); + if (rc < 0) + goto out; + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + /* + * Attempt to flush data before changing attributes. We need to do + * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the + * ownership or mode then we may also need to do this. Here, we take + * the safe way out and just do the flush on all setattr requests. If + * the flush returns error, store it to report later and continue. + * + * BB: This should be smarter. Why bother flushing pages that + * will be truncated anyway? Also, should we error out here if + * the flush returns error? + */ + rc = filemap_write_and_wait(inode->i_mapping); + if (is_interrupt_error(rc)) { + rc = -ERESTARTSYS; + goto out; + } + + mapping_set_error(inode->i_mapping, rc); + rc = 0; + + if (attrs->ia_valid & ATTR_SIZE) { + rc = cifs_set_file_size(inode, attrs, xid, full_path); + if (rc != 0) + goto out; + } + + /* skip mode change if it's just for clearing setuid/setgid */ + if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) + attrs->ia_valid &= ~ATTR_MODE; + + args = kmalloc(sizeof(*args), GFP_KERNEL); + if (args == NULL) { + rc = -ENOMEM; + goto out; + } + + /* set up the struct */ + if (attrs->ia_valid & ATTR_MODE) + args->mode = attrs->ia_mode; + else + args->mode = NO_CHANGE_64; + + if (attrs->ia_valid & ATTR_UID) + args->uid = attrs->ia_uid; + else + args->uid = INVALID_UID; /* no change */ + + if (attrs->ia_valid & ATTR_GID) + args->gid = attrs->ia_gid; + else + args->gid = INVALID_GID; /* no change */ + + if (attrs->ia_valid & ATTR_ATIME) + args->atime = cifs_UnixTimeToNT(attrs->ia_atime); + else + args->atime = NO_CHANGE_64; + + if (attrs->ia_valid & ATTR_MTIME) + args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime); + else + args->mtime = NO_CHANGE_64; + + if (attrs->ia_valid & ATTR_CTIME) + args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime); + else + args->ctime = NO_CHANGE_64; + + args->device = 0; + open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); + if (open_file) { + u16 nfid = open_file->fid.netfid; + u32 npid = open_file->pid; + pTcon = tlink_tcon(open_file->tlink); + rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); + cifsFileInfo_put(open_file); + } else { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto out; + } + pTcon = tlink_tcon(tlink); + rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + cifs_put_tlink(tlink); + } + + if (rc) + goto out; + + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) { + truncate_setsize(inode, attrs->ia_size); + fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); + } + + setattr_copy(&init_user_ns, inode, attrs); + mark_inode_dirty(inode); + + /* force revalidate when any of these times are set since some + of the fs types (eg ext3, fat) do not have fine enough + time granularity to match protocol, and we do not have a + a way (yet) to query the server fs's time granularity (and + whether it rounds times down). + */ + if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME)) + cifsInode->time = 0; +out: + kfree(args); + free_dentry_path(page); + free_xid(xid); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) +{ + unsigned int xid; + kuid_t uid = INVALID_UID; + kgid_t gid = INVALID_GID; + struct inode *inode = d_inode(direntry); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifsFileInfo *wfile; + struct cifs_tcon *tcon; + const char *full_path; + void *page = alloc_dentry_path(); + int rc = -EACCES; + __u32 dosattr = 0; + __u64 mode = NO_CHANGE_64; + + xid = get_xid(); + + cifs_dbg(FYI, "setattr on file %pd attrs->ia_valid 0x%x\n", + direntry, attrs->ia_valid); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = setattr_prepare(&init_user_ns, direntry, attrs); + if (rc < 0) + goto cifs_setattr_exit; + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto cifs_setattr_exit; + } + + /* + * Attempt to flush data before changing attributes. We need to do + * this for ATTR_SIZE and ATTR_MTIME. If the flush of the data + * returns error, store it to report later and continue. + * + * BB: This should be smarter. Why bother flushing pages that + * will be truncated anyway? Also, should we error out here if + * the flush returns error? Do we need to check for ATTR_MTIME_SET flag? + */ + if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) { + rc = filemap_write_and_wait(inode->i_mapping); + if (is_interrupt_error(rc)) { + rc = -ERESTARTSYS; + goto cifs_setattr_exit; + } + mapping_set_error(inode->i_mapping, rc); + } + + rc = 0; + + if ((attrs->ia_valid & ATTR_MTIME) && + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { + rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile); + if (!rc) { + tcon = tlink_tcon(wfile->tlink); + rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid); + cifsFileInfo_put(wfile); + if (rc) + goto cifs_setattr_exit; + } else if (rc != -EBADF) + goto cifs_setattr_exit; + else + rc = 0; + } + + if (attrs->ia_valid & ATTR_SIZE) { + rc = cifs_set_file_size(inode, attrs, xid, full_path); + if (rc != 0) + goto cifs_setattr_exit; + } + + if (attrs->ia_valid & ATTR_UID) + uid = attrs->ia_uid; + + if (attrs->ia_valid & ATTR_GID) + gid = attrs->ia_gid; + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { + if (uid_valid(uid) || gid_valid(gid)) { + mode = NO_CHANGE_64; + rc = id_mode_to_cifs_acl(inode, full_path, &mode, + uid, gid); + if (rc) { + cifs_dbg(FYI, "%s: Setting id failed with error: %d\n", + __func__, rc); + goto cifs_setattr_exit; + } + } + } else + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) + attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); + + /* skip mode change if it's just for clearing setuid/setgid */ + if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) + attrs->ia_valid &= ~ATTR_MODE; + + if (attrs->ia_valid & ATTR_MODE) { + mode = attrs->ia_mode; + rc = 0; + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { + rc = id_mode_to_cifs_acl(inode, full_path, &mode, + INVALID_UID, INVALID_GID); + if (rc) { + cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n", + __func__, rc); + goto cifs_setattr_exit; + } + + /* + * In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes. + * Pick up the actual mode bits that were set. + */ + if (mode != attrs->ia_mode) + attrs->ia_mode = mode; + } else + if (((mode & S_IWUGO) == 0) && + (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { + + dosattr = cifsInode->cifsAttrs | ATTR_READONLY; + + /* fix up mode if we're not using dynperm */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) + attrs->ia_mode = inode->i_mode & ~S_IWUGO; + } else if ((mode & S_IWUGO) && + (cifsInode->cifsAttrs & ATTR_READONLY)) { + + dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; + /* Attributes of 0 are ignored */ + if (dosattr == 0) + dosattr |= ATTR_NORMAL; + + /* reset local inode permissions to normal */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { + attrs->ia_mode &= ~(S_IALLUGO); + if (S_ISDIR(inode->i_mode)) + attrs->ia_mode |= + cifs_sb->ctx->dir_mode; + else + attrs->ia_mode |= + cifs_sb->ctx->file_mode; + } + } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { + /* ignore mode change - ATTR_READONLY hasn't changed */ + attrs->ia_valid &= ~ATTR_MODE; + } + } + + if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) || + ((attrs->ia_valid & ATTR_MODE) && dosattr)) { + rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); + /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */ + + /* Even if error on time set, no sense failing the call if + the server would set the time to a reasonable value anyway, + and this check ensures that we are not being called from + sys_utimes in which case we ought to fail the call back to + the user when the server rejects the call */ + if ((rc) && (attrs->ia_valid & + (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) + rc = 0; + } + + /* do not need local check to inode_check_ok since the server does + that */ + if (rc) + goto cifs_setattr_exit; + + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) { + truncate_setsize(inode, attrs->ia_size); + fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); + } + + setattr_copy(&init_user_ns, inode, attrs); + mark_inode_dirty(inode); + +cifs_setattr_exit: + free_xid(xid); + free_dentry_path(page); + return rc; +} + +int +cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry, + struct iattr *attrs) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + int rc, retries = 0; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + do { +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (pTcon->unix_ext) + rc = cifs_setattr_unix(direntry, attrs); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = cifs_setattr_nounix(direntry, attrs); + retries++; + } while (is_retryable_error(rc) && retries < 2); + + /* BB: add cifs_setattr_legacy for really old servers */ + return rc; +} diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c new file mode 100644 index 000000000000..6419ec47c2a8 --- /dev/null +++ b/fs/smb/client/ioctl.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * vfs operations that deal with io control + * + * Copyright (C) International Business Machines Corp., 2005,2013 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifsfs.h" +#include "cifs_ioctl.h" +#include "smb2proto.h" +#include "smb2glob.h" +#include + +static long cifs_ioctl_query_info(unsigned int xid, struct file *filep, + unsigned long p) +{ + struct inode *inode = file_inode(filep); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct dentry *dentry = filep->f_path.dentry; + const unsigned char *path; + void *page = alloc_dentry_path(); + __le16 *utf16_path = NULL, root_path; + int rc = 0; + + path = build_path_from_dentry(dentry, page); + if (IS_ERR(path)) { + free_dentry_path(page); + return PTR_ERR(path); + } + + cifs_dbg(FYI, "%s %s\n", __func__, path); + + if (!path[0]) { + root_path = 0; + utf16_path = &root_path; + } else { + utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + goto ici_exit; + } + } + + if (tcon->ses->server->ops->ioctl_query_info) + rc = tcon->ses->server->ops->ioctl_query_info( + xid, tcon, cifs_sb, utf16_path, + filep->private_data ? 0 : 1, p); + else + rc = -EOPNOTSUPP; + + ici_exit: + if (utf16_path != &root_path) + kfree(utf16_path); + free_dentry_path(page); + return rc; +} + +static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, + unsigned long srcfd) +{ + int rc; + struct fd src_file; + struct inode *src_inode; + + cifs_dbg(FYI, "ioctl copychunk range\n"); + /* the destination must be opened for writing */ + if (!(dst_file->f_mode & FMODE_WRITE)) { + cifs_dbg(FYI, "file target not open for write\n"); + return -EINVAL; + } + + /* check if target volume is readonly and take reference */ + rc = mnt_want_write_file(dst_file); + if (rc) { + cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); + return rc; + } + + src_file = fdget(srcfd); + if (!src_file.file) { + rc = -EBADF; + goto out_drop_write; + } + + if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) { + rc = -EBADF; + cifs_dbg(VFS, "src file seems to be from a different filesystem type\n"); + goto out_fput; + } + + src_inode = file_inode(src_file.file); + rc = -EINVAL; + if (S_ISDIR(src_inode->i_mode)) + goto out_fput; + + rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0, + src_inode->i_size, 0); + if (rc > 0) + rc = 0; +out_fput: + fdput(src_file); +out_drop_write: + mnt_drop_write_file(dst_file); + return rc; +} + +static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, + void __user *arg) +{ + int rc = 0; + struct smb_mnt_fs_info *fsinf; + + fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL); + if (fsinf == NULL) + return -ENOMEM; + + fsinf->version = 1; + fsinf->protocol_id = tcon->ses->server->vals->protocol_id; + fsinf->device_characteristics = + le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics); + fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); + fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes); + fsinf->max_path_component = + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); + fsinf->vol_serial_number = tcon->vol_serial_number; + fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time); + fsinf->share_flags = tcon->share_flags; + fsinf->share_caps = le32_to_cpu(tcon->capabilities); + fsinf->sector_flags = tcon->ss_flags; + fsinf->optimal_sector_size = tcon->perf_sector_size; + fsinf->max_bytes_chunk = tcon->max_bytes_chunk; + fsinf->maximal_access = tcon->maximal_access; + fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability); + + if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info))) + rc = -EFAULT; + + kfree(fsinf); + return rc; +} + +static int cifs_shutdown(struct super_block *sb, unsigned long arg) +{ + struct cifs_sb_info *sbi = CIFS_SB(sb); + __u32 flags; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (__u32 __user *)arg)) + return -EFAULT; + + if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH) + return -EINVAL; + + if (cifs_forced_shutdown(sbi)) + return 0; + + cifs_dbg(VFS, "shut down requested (%d)", flags); +/* trace_cifs_shutdown(sb, flags);*/ + + /* + * see: + * https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html + * for more information and description of original intent of the flags + */ + switch (flags) { + /* + * We could add support later for default flag which requires: + * "Flush all dirty data and metadata to disk" + * would need to call syncfs or equivalent to flush page cache for + * the mount and then issue fsync to server (if nostrictsync not set) + */ + case CIFS_GOING_FLAGS_DEFAULT: + cifs_dbg(FYI, "shutdown with default flag not supported\n"); + return -EINVAL; + /* + * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not + * data) but metadata writes are not cached on the client, so can treat + * it similarly to NOLOGFLUSH + */ + case CIFS_GOING_FLAGS_LOGFLUSH: + case CIFS_GOING_FLAGS_NOLOGFLUSH: + sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN; + return 0; + default: + return -EINVAL; + } + return 0; +} + +static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in) +{ + struct smb3_full_key_debug_info out; + struct cifs_ses *ses; + int rc = 0; + bool found = false; + u8 __user *end; + + if (!smb3_encryption_required(tcon)) { + rc = -EOPNOTSUPP; + goto out; + } + + /* copy user input into our output buffer */ + if (copy_from_user(&out, in, sizeof(out))) { + rc = -EINVAL; + goto out; + } + + if (!out.session_id) { + /* if ses id is 0, use current user session */ + ses = tcon->ses; + } else { + /* otherwise if a session id is given, look for it in all our sessions */ + struct cifs_ses *ses_it = NULL; + struct TCP_Server_Info *server_it = NULL; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) { + list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) { + if (ses_it->Suid == out.session_id) { + ses = ses_it; + /* + * since we are using the session outside the crit + * section, we need to make sure it won't be released + * so increment its refcount + */ + ses->ses_count++; + found = true; + goto search_end; + } + } + } +search_end: + spin_unlock(&cifs_tcp_ses_lock); + if (!found) { + rc = -ENOENT; + goto out; + } + } + + switch (ses->server->cipher_type) { + case SMB2_ENCRYPTION_AES128_CCM: + case SMB2_ENCRYPTION_AES128_GCM: + out.session_key_length = CIFS_SESS_KEY_SIZE; + out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE; + break; + case SMB2_ENCRYPTION_AES256_CCM: + case SMB2_ENCRYPTION_AES256_GCM: + out.session_key_length = CIFS_SESS_KEY_SIZE; + out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE; + break; + default: + rc = -EOPNOTSUPP; + goto out; + } + + /* check if user buffer is big enough to store all the keys */ + if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length + + out.server_out_key_length) { + rc = -ENOBUFS; + goto out; + } + + out.session_id = ses->Suid; + out.cipher_type = le16_to_cpu(ses->server->cipher_type); + + /* overwrite user input with our output */ + if (copy_to_user(in, &out, sizeof(out))) { + rc = -EINVAL; + goto out; + } + + /* append all the keys at the end of the user buffer */ + end = in->data; + if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) { + rc = -EINVAL; + goto out; + } + end += out.session_key_length; + + if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) { + rc = -EINVAL; + goto out; + } + end += out.server_in_key_length; + + if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) { + rc = -EINVAL; + goto out; + } + +out: + if (found) + cifs_put_smb_ses(ses); + return rc; +} + +long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) +{ + struct inode *inode = file_inode(filep); + struct smb3_key_debug_info pkey_inf; + int rc = -ENOTTY; /* strange error - but the precedent */ + unsigned int xid; + struct cifsFileInfo *pSMBFile = filep->private_data; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb; + __u64 ExtAttrBits = 0; + __u64 caps; + + xid = get_xid(); + + cifs_dbg(FYI, "cifs ioctl 0x%x\n", command); + switch (command) { + case FS_IOC_GETFLAGS: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + caps = le64_to_cpu(tcon->fsUnixInfo.Capability); +#ifdef CONFIG_CIFS_POSIX +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (CIFS_UNIX_EXTATTR_CAP & caps) { + __u64 ExtAttrMask = 0; + rc = CIFSGetExtAttr(xid, tcon, + pSMBFile->fid.netfid, + &ExtAttrBits, &ExtAttrMask); + if (rc == 0) + rc = put_user(ExtAttrBits & + FS_FL_USER_VISIBLE, + (int __user *)arg); + if (rc != -EOPNOTSUPP) + break; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +#endif /* CONFIG_CIFS_POSIX */ + rc = 0; + if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { + /* add in the compressed bit */ + ExtAttrBits = FS_COMPR_FL; + rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, + (int __user *)arg); + } + break; + case FS_IOC_SETFLAGS: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + /* caps = le64_to_cpu(tcon->fsUnixInfo.Capability); */ + + if (get_user(ExtAttrBits, (int __user *)arg)) { + rc = -EFAULT; + break; + } + + /* + * if (CIFS_UNIX_EXTATTR_CAP & caps) + * rc = CIFSSetExtAttr(xid, tcon, + * pSMBFile->fid.netfid, + * extAttrBits, + * &ExtAttrMask); + * if (rc != -EOPNOTSUPP) + * break; + */ + + /* Currently only flag we can set is compressed flag */ + if ((ExtAttrBits & FS_COMPR_FL) == 0) + break; + + /* Try to set compress flag */ + if (tcon->ses->server->ops->set_compression) { + rc = tcon->ses->server->ops->set_compression( + xid, tcon, pSMBFile); + cifs_dbg(FYI, "set compress flag rc %d\n", rc); + } + break; + case CIFS_IOC_COPYCHUNK_FILE: + rc = cifs_ioctl_copychunk(xid, filep, arg); + break; + case CIFS_QUERY_INFO: + rc = cifs_ioctl_query_info(xid, filep, arg); + break; + case CIFS_IOC_SET_INTEGRITY: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + if (tcon->ses->server->ops->set_integrity) + rc = tcon->ses->server->ops->set_integrity(xid, + tcon, pSMBFile); + else + rc = -EOPNOTSUPP; + break; + case CIFS_IOC_GET_MNT_INFO: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); + break; + case CIFS_ENUMERATE_SNAPSHOTS: + if (pSMBFile == NULL) + break; + if (arg == 0) { + rc = -EINVAL; + goto cifs_ioc_exit; + } + tcon = tlink_tcon(pSMBFile->tlink); + if (tcon->ses->server->ops->enum_snapshots) + rc = tcon->ses->server->ops->enum_snapshots(xid, tcon, + pSMBFile, (void __user *)arg); + else + rc = -EOPNOTSUPP; + break; + case CIFS_DUMP_KEY: + /* + * Dump encryption keys. This is an old ioctl that only + * handles AES-128-{CCM,GCM}. + */ + if (pSMBFile == NULL) + break; + if (!capable(CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + + tcon = tlink_tcon(pSMBFile->tlink); + if (!smb3_encryption_required(tcon)) { + rc = -EOPNOTSUPP; + break; + } + pkey_inf.cipher_type = + le16_to_cpu(tcon->ses->server->cipher_type); + pkey_inf.Suid = tcon->ses->Suid; + memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response, + 16 /* SMB2_NTLMV2_SESSKEY_SIZE */); + memcpy(pkey_inf.smb3decryptionkey, + tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); + memcpy(pkey_inf.smb3encryptionkey, + tcon->ses->smb3encryptionkey, SMB3_SIGN_KEY_SIZE); + if (copy_to_user((void __user *)arg, &pkey_inf, + sizeof(struct smb3_key_debug_info))) + rc = -EFAULT; + else + rc = 0; + break; + case CIFS_DUMP_FULL_KEY: + /* + * Dump encryption keys (handles any key sizes) + */ + if (pSMBFile == NULL) + break; + if (!capable(CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + tcon = tlink_tcon(pSMBFile->tlink); + rc = cifs_dump_full_key(tcon, (void __user *)arg); + break; + case CIFS_IOC_NOTIFY: + if (!S_ISDIR(inode->i_mode)) { + /* Notify can only be done on directories */ + rc = -EOPNOTSUPP; + break; + } + cifs_sb = CIFS_SB(inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + break; + } + tcon = tlink_tcon(tlink); + if (tcon && tcon->ses->server->ops->notify) { + rc = tcon->ses->server->ops->notify(xid, + filep, (void __user *)arg, + false /* no ret data */); + cifs_dbg(FYI, "ioctl notify rc %d\n", rc); + } else + rc = -EOPNOTSUPP; + cifs_put_tlink(tlink); + break; + case CIFS_IOC_NOTIFY_INFO: + if (!S_ISDIR(inode->i_mode)) { + /* Notify can only be done on directories */ + rc = -EOPNOTSUPP; + break; + } + cifs_sb = CIFS_SB(inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + break; + } + tcon = tlink_tcon(tlink); + if (tcon && tcon->ses->server->ops->notify) { + rc = tcon->ses->server->ops->notify(xid, + filep, (void __user *)arg, + true /* return details */); + cifs_dbg(FYI, "ioctl notify info rc %d\n", rc); + } else + rc = -EOPNOTSUPP; + cifs_put_tlink(tlink); + break; + case CIFS_IOC_SHUTDOWN: + rc = cifs_shutdown(inode->i_sb, arg); + break; + default: + cifs_dbg(FYI, "unsupported ioctl\n"); + break; + } +cifs_ioc_exit: + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c new file mode 100644 index 000000000000..c0f101fc1e5d --- /dev/null +++ b/fs/smb/client/link.c @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "smb2proto.h" +#include "cifs_ioctl.h" + +/* + * M-F Symlink Functions - Begin + */ + +#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1) +#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) +#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1)) +#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) +#define CIFS_MF_SYMLINK_FILE_SIZE \ + (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN) + +#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n" +#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n" +#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash + +static int +symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash) +{ + int rc; + struct shash_desc *md5 = NULL; + + rc = cifs_alloc_hash("md5", &md5); + if (rc) + goto symlink_hash_err; + + rc = crypto_shash_init(md5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__); + goto symlink_hash_err; + } + rc = crypto_shash_update(md5, link_str, link_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); + goto symlink_hash_err; + } + rc = crypto_shash_final(md5, md5_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + +symlink_hash_err: + cifs_free_hash(&md5); + return rc; +} + +static int +parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len, + char **_link_str) +{ + int rc; + unsigned int link_len; + const char *md5_str1; + const char *link_str; + u8 md5_hash[16]; + char md5_str2[34]; + + if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) + return -EINVAL; + + md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; + link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; + + rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); + if (rc != 1) + return -EINVAL; + + if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) + return -EINVAL; + + rc = symlink_hash(link_len, link_str, md5_hash); + if (rc) { + cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); + return rc; + } + + scnprintf(md5_str2, sizeof(md5_str2), + CIFS_MF_SYMLINK_MD5_FORMAT, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + + if (strncmp(md5_str1, md5_str2, 17) != 0) + return -EINVAL; + + if (_link_str) { + *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); + if (!*_link_str) + return -ENOMEM; + } + + *_link_len = link_len; + return 0; +} + +static int +format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str) +{ + int rc; + unsigned int link_len; + unsigned int ofs; + u8 md5_hash[16]; + + if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) + return -EINVAL; + + link_len = strlen(link_str); + + if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) + return -ENAMETOOLONG; + + rc = symlink_hash(link_len, link_str, md5_hash); + if (rc) { + cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); + return rc; + } + + scnprintf(buf, buf_len, + CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, + link_len, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + + ofs = CIFS_MF_SYMLINK_LINK_OFFSET; + memcpy(buf + ofs, link_str, link_len); + + ofs += link_len; + if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { + buf[ofs] = '\n'; + ofs++; + } + + while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { + buf[ofs] = ' '; + ofs++; + } + + return 0; +} + +bool +couldbe_mf_symlink(const struct cifs_fattr *fattr) +{ + if (!S_ISREG(fattr->cf_mode)) + /* it's not a symlink */ + return false; + + if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) + /* it's not a symlink */ + return false; + + return true; +} + +static int +create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *fromName, + const char *toName) +{ + int rc; + u8 *buf; + unsigned int bytes_written = 0; + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); + if (rc) + goto out; + + if (tcon->ses->server->ops->create_mf_symlink) + rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon, + cifs_sb, fromName, buf, &bytes_written); + else + rc = -EOPNOTSUPP; + + if (rc) + goto out; + + if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) + rc = -EIO; +out: + kfree(buf); + return rc; +} + +int +check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, + const unsigned char *path) +{ + int rc; + u8 *buf = NULL; + unsigned int link_len = 0; + unsigned int bytes_read = 0; + char *symlink = NULL; + + if (!couldbe_mf_symlink(fattr)) + /* it's not a symlink */ + return 0; + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (tcon->ses->server->ops->query_mf_symlink) + rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, + cifs_sb, path, buf, &bytes_read); + else + rc = -ENOSYS; + + if (rc) + goto out; + + if (bytes_read == 0) /* not a symlink */ + goto out; + + rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink); + if (rc == -EINVAL) { + /* it's not a symlink */ + rc = 0; + goto out; + } + + if (rc != 0) + goto out; + + /* it is a symlink */ + fattr->cf_eof = link_len; + fattr->cf_mode &= ~S_IFMT; + fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; + fattr->cf_dtype = DT_LNK; + fattr->cf_symlink_target = symlink; +out: + kfree(buf); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/* + * SMB 1.0 Protocol specific functions + */ + +int +cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_read) +{ + int rc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + int buf_type = CIFS_NO_BUFFER; + FILE_ALL_INFO file_info; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_READ, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, &file_info); + if (rc) + return rc; + + if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { + rc = -ENOENT; + /* it's not a symlink */ + goto out; + } + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + + rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); +out: + CIFSSMBClose(xid, tcon, fid.netfid); + return rc; +} + +int +cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_written) +{ + int rc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_CREATE, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) + return rc; + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + + rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf); + CIFSSMBClose(xid, tcon, fid.netfid); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* + * SMB 2.1/SMB3 Protocol specific functions + */ +int +smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_read) +{ + int rc; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + int buf_type = CIFS_NO_BUFFER; + __le16 *utf16_path; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct smb2_file_all_info *pfile_info = NULL; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .path = path, + .desired_access = GENERIC_READ, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .fid = &fid, + }; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (utf16_path == NULL) + return -ENOMEM; + + pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, + GFP_KERNEL); + + if (pfile_info == NULL) { + kfree(utf16_path); + return -ENOMEM; + } + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL, + NULL, NULL); + if (rc) + goto qmf_out_open_fail; + + if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { + /* it's not a symlink */ + rc = -ENOENT; /* Is there a better rc to return? */ + goto qmf_out; + } + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + io_parms.persistent_fid = fid.persistent_fid; + io_parms.volatile_fid = fid.volatile_fid; + rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type); +qmf_out: + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +qmf_out_open_fail: + kfree(utf16_path); + kfree(pfile_info); + return rc; +} + +int +smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_written) +{ + int rc; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + __le16 *utf16_path; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct kvec iov[2]; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .path = path, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_CREATE, + .fid = &fid, + .mode = 0644, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) { + kfree(utf16_path); + return rc; + } + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + io_parms.persistent_fid = fid.persistent_fid; + io_parms.volatile_fid = fid.volatile_fid; + + /* iov[0] is reserved for smb header */ + iov[1].iov_base = pbuf; + iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE; + + rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1); + + /* Make sure we wrote all of the symlink data */ + if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE)) + rc = -EIO; + + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + + kfree(utf16_path); + return rc; +} + +/* + * M-F Symlink Functions - End + */ + +int +cifs_hardlink(struct dentry *old_file, struct inode *inode, + struct dentry *direntry) +{ + int rc = -EACCES; + unsigned int xid; + const char *from_name, *to_name; + void *page1, *page2; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsInodeInfo *cifsInode; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + xid = get_xid(); + page1 = alloc_dentry_path(); + page2 = alloc_dentry_path(); + + from_name = build_path_from_dentry(old_file, page1); + if (IS_ERR(from_name)) { + rc = PTR_ERR(from_name); + goto cifs_hl_exit; + } + to_name = build_path_from_dentry(direntry, page2); + if (IS_ERR(to_name)) { + rc = PTR_ERR(to_name); + goto cifs_hl_exit; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext) + rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + else { +#else + { +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + server = tcon->ses->server; + if (!server->ops->create_hardlink) { + rc = -ENOSYS; + goto cifs_hl_exit; + } + rc = server->ops->create_hardlink(xid, tcon, from_name, to_name, + cifs_sb); + if ((rc == -EIO) || (rc == -EINVAL)) + rc = -EOPNOTSUPP; + } + + d_drop(direntry); /* force new lookup from server of target */ + + /* + * if source file is cached (oplocked) revalidate will not go to server + * until the file is closed or oplock broken so update nlinks locally + */ + if (d_really_is_positive(old_file)) { + cifsInode = CIFS_I(d_inode(old_file)); + if (rc == 0) { + spin_lock(&d_inode(old_file)->i_lock); + inc_nlink(d_inode(old_file)); + spin_unlock(&d_inode(old_file)->i_lock); + + /* + * parent dir timestamps will update from srv within a + * second, would it really be worth it to set the parent + * dir cifs inode time to zero to force revalidate + * (faster) for it too? + */ + } + /* + * if not oplocked will force revalidate to get info on source + * file from srv. Note Samba server prior to 4.2 has bug - + * not updating src file ctime on hardlinks but Windows servers + * handle it properly + */ + cifsInode->time = 0; + + /* + * Will update parent dir timestamps from srv within a second. + * Would it really be worth it to set the parent dir (cifs + * inode) time field to zero to force revalidate on parent + * directory faster ie + * + * CIFS_I(inode)->time = 0; + */ + } + +cifs_hl_exit: + free_dentry_path(page1); + free_dentry_path(page2); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +int +cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, const char *symname) +{ + int rc = -EOPNOTSUPP; + unsigned int xid; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + struct inode *newinode = NULL; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + page = alloc_dentry_path(); + if (!page) + return -ENOMEM; + + xid = get_xid(); + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto symlink_exit; + } + pTcon = tlink_tcon(tlink); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto symlink_exit; + } + + cifs_dbg(FYI, "Full path: %s\n", full_path); + cifs_dbg(FYI, "symname is %s\n", symname); + + /* BB what if DFS and this volume is on different share? BB */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + else if (pTcon->unix_ext) + rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + /* else + rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName, + cifs_sb_target->local_nls); */ + + if (rc == 0) { + if (pTcon->posix_extensions) + rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid); + else if (pTcon->unix_ext) + rc = cifs_get_inode_info_unix(&newinode, full_path, + inode->i_sb, xid); + else + rc = cifs_get_inode_info(&newinode, full_path, NULL, + inode->i_sb, xid, NULL); + + if (rc != 0) { + cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n", + rc); + } else { + d_instantiate(direntry, newinode); + } + } +symlink_exit: + free_dentry_path(page); + cifs_put_tlink(tlink); + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c new file mode 100644 index 000000000000..31e06133acc3 --- /dev/null +++ b/fs/smb/client/misc.c @@ -0,0 +1,1434 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "smberr.h" +#include "nterr.h" +#include "cifs_unicode.h" +#include "smb2pdu.h" +#include "cifsfs.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dns_resolve.h" +#include "dfs_cache.h" +#endif +#include "fs_context.h" +#include "cached_dir.h" + +extern mempool_t *cifs_sm_req_poolp; +extern mempool_t *cifs_req_poolp; + +/* The xid serves as a useful identifier for each incoming vfs request, + in a similar way to the mid which is useful to track each sent smb, + and CurrentXid can also provide a running counter (although it + will eventually wrap past zero) of the total vfs operations handled + since the cifs fs was mounted */ + +unsigned int +_get_xid(void) +{ + unsigned int xid; + + spin_lock(&GlobalMid_Lock); + GlobalTotalActiveXid++; + + /* keep high water mark for number of simultaneous ops in filesystem */ + if (GlobalTotalActiveXid > GlobalMaxActiveXid) + GlobalMaxActiveXid = GlobalTotalActiveXid; + if (GlobalTotalActiveXid > 65000) + cifs_dbg(FYI, "warning: more than 65000 requests active\n"); + xid = GlobalCurrentXid++; + spin_unlock(&GlobalMid_Lock); + return xid; +} + +void +_free_xid(unsigned int xid) +{ + spin_lock(&GlobalMid_Lock); + /* if (GlobalTotalActiveXid == 0) + BUG(); */ + GlobalTotalActiveXid--; + spin_unlock(&GlobalMid_Lock); +} + +struct cifs_ses * +sesInfoAlloc(void) +{ + struct cifs_ses *ret_buf; + + ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL); + if (ret_buf) { + atomic_inc(&sesInfoAllocCount); + spin_lock_init(&ret_buf->ses_lock); + ret_buf->ses_status = SES_NEW; + ++ret_buf->ses_count; + INIT_LIST_HEAD(&ret_buf->smb_ses_list); + INIT_LIST_HEAD(&ret_buf->tcon_list); + mutex_init(&ret_buf->session_mutex); + spin_lock_init(&ret_buf->iface_lock); + INIT_LIST_HEAD(&ret_buf->iface_list); + spin_lock_init(&ret_buf->chan_lock); + } + return ret_buf; +} + +void +sesInfoFree(struct cifs_ses *buf_to_free) +{ + struct cifs_server_iface *iface = NULL, *niface = NULL; + + if (buf_to_free == NULL) { + cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n"); + return; + } + + atomic_dec(&sesInfoAllocCount); + kfree(buf_to_free->serverOS); + kfree(buf_to_free->serverDomain); + kfree(buf_to_free->serverNOS); + kfree_sensitive(buf_to_free->password); + kfree(buf_to_free->user_name); + kfree(buf_to_free->domainName); + kfree_sensitive(buf_to_free->auth_key.response); + spin_lock(&buf_to_free->iface_lock); + list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list, + iface_head) + kref_put(&iface->refcount, release_iface); + spin_unlock(&buf_to_free->iface_lock); + kfree_sensitive(buf_to_free); +} + +struct cifs_tcon * +tconInfoAlloc(void) +{ + struct cifs_tcon *ret_buf; + + ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); + if (!ret_buf) + return NULL; + ret_buf->cfids = init_cached_dirs(); + if (!ret_buf->cfids) { + kfree(ret_buf); + return NULL; + } + + atomic_inc(&tconInfoAllocCount); + ret_buf->status = TID_NEW; + ++ret_buf->tc_count; + spin_lock_init(&ret_buf->tc_lock); + INIT_LIST_HEAD(&ret_buf->openFileList); + INIT_LIST_HEAD(&ret_buf->tcon_list); + spin_lock_init(&ret_buf->open_file_lock); + spin_lock_init(&ret_buf->stat_lock); + atomic_set(&ret_buf->num_local_opens, 0); + atomic_set(&ret_buf->num_remote_opens, 0); + + return ret_buf; +} + +void +tconInfoFree(struct cifs_tcon *tcon) +{ + if (tcon == NULL) { + cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n"); + return; + } + free_cached_dirs(tcon->cfids); + atomic_dec(&tconInfoAllocCount); + kfree(tcon->nativeFileSystem); + kfree_sensitive(tcon->password); + kfree(tcon); +} + +struct smb_hdr * +cifs_buf_get(void) +{ + struct smb_hdr *ret_buf = NULL; + /* + * SMB2 header is bigger than CIFS one - no problems to clean some + * more bytes for CIFS. + */ + size_t buf_size = sizeof(struct smb2_hdr); + + /* + * We could use negotiated size instead of max_msgsize - + * but it may be more efficient to always alloc same size + * albeit slightly larger than necessary and maxbuffersize + * defaults to this and can not be bigger. + */ + ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); + + /* clear the first few header bytes */ + /* for most paths, more is cleared in header_assemble */ + memset(ret_buf, 0, buf_size + 3); + atomic_inc(&buf_alloc_count); +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&total_buf_alloc_count); +#endif /* CONFIG_CIFS_STATS2 */ + + return ret_buf; +} + +void +cifs_buf_release(void *buf_to_free) +{ + if (buf_to_free == NULL) { + /* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/ + return; + } + mempool_free(buf_to_free, cifs_req_poolp); + + atomic_dec(&buf_alloc_count); + return; +} + +struct smb_hdr * +cifs_small_buf_get(void) +{ + struct smb_hdr *ret_buf = NULL; + +/* We could use negotiated size instead of max_msgsize - + but it may be more efficient to always alloc same size + albeit slightly larger than necessary and maxbuffersize + defaults to this and can not be bigger */ + ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS); + /* No need to clear memory here, cleared in header assemble */ + /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ + atomic_inc(&small_buf_alloc_count); +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&total_small_buf_alloc_count); +#endif /* CONFIG_CIFS_STATS2 */ + + return ret_buf; +} + +void +cifs_small_buf_release(void *buf_to_free) +{ + + if (buf_to_free == NULL) { + cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n"); + return; + } + mempool_free(buf_to_free, cifs_sm_req_poolp); + + atomic_dec(&small_buf_alloc_count); + return; +} + +void +free_rsp_buf(int resp_buftype, void *rsp) +{ + if (resp_buftype == CIFS_SMALL_BUFFER) + cifs_small_buf_release(rsp); + else if (resp_buftype == CIFS_LARGE_BUFFER) + cifs_buf_release(rsp); +} + +/* NB: MID can not be set if treeCon not passed in, in that + case it is responsbility of caller to set the mid */ +void +header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , + const struct cifs_tcon *treeCon, int word_count + /* length of fixed section (word count) in two byte units */) +{ + char *temp = (char *) buffer; + + memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ + + buffer->smb_buf_length = cpu_to_be32( + (2 * word_count) + sizeof(struct smb_hdr) - + 4 /* RFC 1001 length field does not count */ + + 2 /* for bcc field itself */) ; + + buffer->Protocol[0] = 0xFF; + buffer->Protocol[1] = 'S'; + buffer->Protocol[2] = 'M'; + buffer->Protocol[3] = 'B'; + buffer->Command = smb_command; + buffer->Flags = 0x00; /* case sensitive */ + buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; + buffer->Pid = cpu_to_le16((__u16)current->tgid); + buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); + if (treeCon) { + buffer->Tid = treeCon->tid; + if (treeCon->ses) { + if (treeCon->ses->capabilities & CAP_UNICODE) + buffer->Flags2 |= SMBFLG2_UNICODE; + if (treeCon->ses->capabilities & CAP_STATUS32) + buffer->Flags2 |= SMBFLG2_ERR_STATUS; + + /* Uid is not converted */ + buffer->Uid = treeCon->ses->Suid; + if (treeCon->ses->server) + buffer->Mid = get_next_mid(treeCon->ses->server); + } + if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) + buffer->Flags2 |= SMBFLG2_DFS; + if (treeCon->nocase) + buffer->Flags |= SMBFLG_CASELESS; + if ((treeCon->ses) && (treeCon->ses->server)) + if (treeCon->ses->server->sign) + buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + } + +/* endian conversion of flags is now done just before sending */ + buffer->WordCount = (char) word_count; + return; +} + +static int +check_smb_hdr(struct smb_hdr *smb) +{ + /* does it have the right SMB "signature" ? */ + if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { + cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", + *(unsigned int *)smb->Protocol); + return 1; + } + + /* if it's a response then accept */ + if (smb->Flags & SMBFLG_RESPONSE) + return 0; + + /* only one valid case where server sends us request */ + if (smb->Command == SMB_COM_LOCKING_ANDX) + return 0; + + cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", + get_mid(smb)); + return 1; +} + +int +checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server) +{ + struct smb_hdr *smb = (struct smb_hdr *)buf; + __u32 rfclen = be32_to_cpu(smb->smb_buf_length); + __u32 clc_len; /* calculated length */ + cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", + total_read, rfclen); + + /* is this frame too small to even get to a BCC? */ + if (total_read < 2 + sizeof(struct smb_hdr)) { + if ((total_read >= sizeof(struct smb_hdr) - 1) + && (smb->Status.CifsError != 0)) { + /* it's an error return */ + smb->WordCount = 0; + /* some error cases do not return wct and bcc */ + return 0; + } else if ((total_read == sizeof(struct smb_hdr) + 1) && + (smb->WordCount == 0)) { + char *tmp = (char *)smb; + /* Need to work around a bug in two servers here */ + /* First, check if the part of bcc they sent was zero */ + if (tmp[sizeof(struct smb_hdr)] == 0) { + /* some servers return only half of bcc + * on simple responses (wct, bcc both zero) + * in particular have seen this on + * ulogoffX and FindClose. This leaves + * one byte of bcc potentially unitialized + */ + /* zero rest of bcc */ + tmp[sizeof(struct smb_hdr)+1] = 0; + return 0; + } + cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n"); + } else { + cifs_dbg(VFS, "Length less than smb header size\n"); + } + return -EIO; + } + + /* otherwise, there is enough to get to the BCC */ + if (check_smb_hdr(smb)) + return -EIO; + clc_len = smbCalcSize(smb); + + if (4 + rfclen != total_read) { + cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n", + rfclen); + return -EIO; + } + + if (4 + rfclen != clc_len) { + __u16 mid = get_mid(smb); + /* check if bcc wrapped around for large read responses */ + if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { + /* check if lengths match mod 64K */ + if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) + return 0; /* bcc wrapped */ + } + cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", + clc_len, 4 + rfclen, mid); + + if (4 + rfclen < clc_len) { + cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", + rfclen, mid); + return -EIO; + } else if (rfclen > clc_len + 512) { + /* + * Some servers (Windows XP in particular) send more + * data than the lengths in the SMB packet would + * indicate on certain calls (byte range locks and + * trans2 find first calls in particular). While the + * client can handle such a frame by ignoring the + * trailing data, we choose limit the amount of extra + * data to 512 bytes. + */ + cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", + rfclen, mid); + return -EIO; + } + } + return 0; +} + +bool +is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) +{ + struct smb_hdr *buf = (struct smb_hdr *)buffer; + struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifsInodeInfo *pCifsInode; + struct cifsFileInfo *netfile; + + cifs_dbg(FYI, "Checking for oplock break or dnotify response\n"); + if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && + (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { + struct smb_com_transaction_change_notify_rsp *pSMBr = + (struct smb_com_transaction_change_notify_rsp *)buf; + struct file_notify_information *pnotify; + __u32 data_offset = 0; + size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length); + + if (get_bcc(buf) > sizeof(struct file_notify_information)) { + data_offset = le32_to_cpu(pSMBr->DataOffset); + + if (data_offset > + len - sizeof(struct file_notify_information)) { + cifs_dbg(FYI, "Invalid data_offset %u\n", + data_offset); + return true; + } + pnotify = (struct file_notify_information *) + ((char *)&pSMBr->hdr.Protocol + data_offset); + cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n", + pnotify->FileName, pnotify->Action); + /* cifs_dump_mem("Rcvd notify Data: ",buf, + sizeof(struct smb_hdr)+60); */ + return true; + } + if (pSMBr->hdr.Status.CifsError) { + cifs_dbg(FYI, "notify err 0x%x\n", + pSMBr->hdr.Status.CifsError); + return true; + } + return false; + } + if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) + return false; + if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { + /* no sense logging error on invalid handle on oplock + break - harmless race between close request and oplock + break response is expected from time to time writing out + large dirty files cached on the client */ + if ((NT_STATUS_INVALID_HANDLE) == + le32_to_cpu(pSMB->hdr.Status.CifsError)) { + cifs_dbg(FYI, "Invalid handle on oplock break\n"); + return true; + } else if (ERRbadfid == + le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { + return true; + } else { + return false; /* on valid oplock brk we get "request" */ + } + } + if (pSMB->hdr.WordCount != 8) + return false; + + cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n", + pSMB->LockType, pSMB->OplockLevel); + if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) + return false; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(srv) ? srv->primary_server : srv; + + /* look up tcon based on tid & uid */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->tid != buf->Tid) + continue; + + cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); + spin_lock(&tcon->open_file_lock); + list_for_each_entry(netfile, &tcon->openFileList, tlist) { + if (pSMB->Fid != netfile->fid.netfid) + continue; + + cifs_dbg(FYI, "file id match, oplock break\n"); + pCifsInode = CIFS_I(d_inode(netfile->dentry)); + + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, + &pCifsInode->flags); + + netfile->oplock_epoch = 0; + netfile->oplock_level = pSMB->OplockLevel; + netfile->oplock_break_cancelled = false; + cifs_queue_oplock_break(netfile); + + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "No matching file for oplock break\n"); + return true; + } + } + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); + return true; +} + +void +dump_smb(void *buf, int smb_buf_length) +{ + if (traceSMB == 0) + return; + + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 8, 2, buf, + smb_buf_length, true); +} + +void +cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + struct cifs_tcon *tcon = NULL; + + if (cifs_sb->master_tlink) + tcon = cifs_sb_master_tcon(cifs_sb); + + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; + cifs_sb->mnt_cifs_serverino_autodisabled = true; + cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n", + tcon ? tcon->tree_name : "new server"); + cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n"); + cifs_dbg(VFS, "Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n"); + + } +} + +void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) +{ + oplock &= 0xF; + + if (oplock == OPLOCK_EXCLUSIVE) { + cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG; + cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else if (oplock == OPLOCK_READ) { + cinode->oplock = CIFS_CACHE_READ_FLG; + cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else + cinode->oplock = 0; +} + +/* + * We wait for oplock breaks to be processed before we attempt to perform + * writes. + */ +int cifs_get_writer(struct cifsInodeInfo *cinode) +{ + int rc; + +start: + rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, + TASK_KILLABLE); + if (rc) + return rc; + + spin_lock(&cinode->writers_lock); + if (!cinode->writers) + set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); + cinode->writers++; + /* Check to see if we have started servicing an oplock break */ + if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { + cinode->writers--; + if (cinode->writers == 0) { + clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); + wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); + } + spin_unlock(&cinode->writers_lock); + goto start; + } + spin_unlock(&cinode->writers_lock); + return 0; +} + +void cifs_put_writer(struct cifsInodeInfo *cinode) +{ + spin_lock(&cinode->writers_lock); + cinode->writers--; + if (cinode->writers == 0) { + clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); + wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); + } + spin_unlock(&cinode->writers_lock); +} + +/** + * cifs_queue_oplock_break - queue the oplock break handler for cfile + * @cfile: The file to break the oplock on + * + * This function is called from the demultiplex thread when it + * receives an oplock break for @cfile. + * + * Assumes the tcon->open_file_lock is held. + * Assumes cfile->file_info_lock is NOT held. + */ +void cifs_queue_oplock_break(struct cifsFileInfo *cfile) +{ + /* + * Bump the handle refcount now while we hold the + * open_file_lock to enforce the validity of it for the oplock + * break handler. The matching put is done at the end of the + * handler. + */ + cifsFileInfo_get(cfile); + + queue_work(cifsoplockd_wq, &cfile->oplock_break); +} + +void cifs_done_oplock_break(struct cifsInodeInfo *cinode) +{ + clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); + wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); +} + +bool +backup_cred(struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { + if (uid_eq(cifs_sb->ctx->backupuid, current_fsuid())) + return true; + } + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { + if (in_group_p(cifs_sb->ctx->backupgid)) + return true; + } + + return false; +} + +void +cifs_del_pending_open(struct cifs_pending_open *open) +{ + spin_lock(&tlink_tcon(open->tlink)->open_file_lock); + list_del(&open->olist); + spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); +} + +void +cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, + struct cifs_pending_open *open) +{ + memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); + open->oplock = CIFS_OPLOCK_NO_CHANGE; + open->tlink = tlink; + fid->pending_open = open; + list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens); +} + +void +cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, + struct cifs_pending_open *open) +{ + spin_lock(&tlink_tcon(tlink)->open_file_lock); + cifs_add_pending_open_locked(fid, tlink, open); + spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); +} + +/* + * Critical section which runs after acquiring deferred_lock. + * As there is no reference count on cifs_deferred_close, pdclose + * should not be used outside deferred_lock. + */ +bool +cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose) +{ + struct cifs_deferred_close *dclose; + + list_for_each_entry(dclose, &CIFS_I(d_inode(cfile->dentry))->deferred_closes, dlist) { + if ((dclose->netfid == cfile->fid.netfid) && + (dclose->persistent_fid == cfile->fid.persistent_fid) && + (dclose->volatile_fid == cfile->fid.volatile_fid)) { + *pdclose = dclose; + return true; + } + } + return false; +} + +/* + * Critical section which runs after acquiring deferred_lock. + */ +void +cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose) +{ + bool is_deferred = false; + struct cifs_deferred_close *pdclose; + + is_deferred = cifs_is_deferred_close(cfile, &pdclose); + if (is_deferred) { + kfree(dclose); + return; + } + + dclose->tlink = cfile->tlink; + dclose->netfid = cfile->fid.netfid; + dclose->persistent_fid = cfile->fid.persistent_fid; + dclose->volatile_fid = cfile->fid.volatile_fid; + list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes); +} + +/* + * Critical section which runs after acquiring deferred_lock. + */ +void +cifs_del_deferred_close(struct cifsFileInfo *cfile) +{ + bool is_deferred = false; + struct cifs_deferred_close *dclose; + + is_deferred = cifs_is_deferred_close(cfile, &dclose); + if (!is_deferred) + return; + list_del(&dclose->dlist); + kfree(dclose); +} + +void +cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) +{ + struct cifsFileInfo *cfile = NULL; + struct file_list *tmp_list, *tmp_next_list; + struct list_head file_head; + + if (cifs_inode == NULL) + return; + + INIT_LIST_HEAD(&file_head); + spin_lock(&cifs_inode->open_file_lock); + list_for_each_entry(cfile, &cifs_inode->openFileList, flist) { + if (delayed_work_pending(&cfile->deferred)) { + if (cancel_delayed_work(&cfile->deferred)) { + spin_lock(&cifs_inode->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&cifs_inode->deferred_lock); + + tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); + if (tmp_list == NULL) + break; + tmp_list->cfile = cfile; + list_add_tail(&tmp_list->list, &file_head); + } + } + } + spin_unlock(&cifs_inode->open_file_lock); + + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { + _cifsFileInfo_put(tmp_list->cfile, false, false); + list_del(&tmp_list->list); + kfree(tmp_list); + } +} + +void +cifs_close_all_deferred_files(struct cifs_tcon *tcon) +{ + struct cifsFileInfo *cfile; + struct file_list *tmp_list, *tmp_next_list; + struct list_head file_head; + + INIT_LIST_HEAD(&file_head); + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + if (delayed_work_pending(&cfile->deferred)) { + if (cancel_delayed_work(&cfile->deferred)) { + spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + + tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); + if (tmp_list == NULL) + break; + tmp_list->cfile = cfile; + list_add_tail(&tmp_list->list, &file_head); + } + } + } + spin_unlock(&tcon->open_file_lock); + + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { + _cifsFileInfo_put(tmp_list->cfile, true, false); + list_del(&tmp_list->list); + kfree(tmp_list); + } +} +void +cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path) +{ + struct cifsFileInfo *cfile; + struct file_list *tmp_list, *tmp_next_list; + struct list_head file_head; + void *page; + const char *full_path; + + INIT_LIST_HEAD(&file_head); + page = alloc_dentry_path(); + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + full_path = build_path_from_dentry(cfile->dentry, page); + if (strstr(full_path, path)) { + if (delayed_work_pending(&cfile->deferred)) { + if (cancel_delayed_work(&cfile->deferred)) { + spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + + tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); + if (tmp_list == NULL) + break; + tmp_list->cfile = cfile; + list_add_tail(&tmp_list->list, &file_head); + } + } + } + } + spin_unlock(&tcon->open_file_lock); + + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { + _cifsFileInfo_put(tmp_list->cfile, true, false); + list_del(&tmp_list->list); + kfree(tmp_list); + } + free_dentry_path(page); +} + +/* parses DFS referral V3 structure + * caller is responsible for freeing target_nodes + * returns: + * - on success - 0 + * - on failure - errno + */ +int +parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, + unsigned int *num_of_nodes, + struct dfs_info3_param **target_nodes, + const struct nls_table *nls_codepage, int remap, + const char *searchName, bool is_unicode) +{ + int i, rc = 0; + char *data_end; + struct dfs_referral_level_3 *ref; + + *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals); + + if (*num_of_nodes < 1) { + cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n", + *num_of_nodes); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + ref = (struct dfs_referral_level_3 *) &(rsp->referrals); + if (ref->VersionNumber != cpu_to_le16(3)) { + cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n", + le16_to_cpu(ref->VersionNumber)); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + /* get the upper boundary of the resp buffer */ + data_end = (char *)rsp + rsp_size; + + cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", + *num_of_nodes, le32_to_cpu(rsp->DFSFlags)); + + *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param), + GFP_KERNEL); + if (*target_nodes == NULL) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + /* collect necessary data from referrals */ + for (i = 0; i < *num_of_nodes; i++) { + char *temp; + int max_len; + struct dfs_info3_param *node = (*target_nodes)+i; + + node->flags = le32_to_cpu(rsp->DFSFlags); + if (is_unicode) { + __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, + GFP_KERNEL); + if (tmp == NULL) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + cifsConvertToUTF16((__le16 *) tmp, searchName, + PATH_MAX, nls_codepage, remap); + node->path_consumed = cifs_utf16_bytes(tmp, + le16_to_cpu(rsp->PathConsumed), + nls_codepage); + kfree(tmp); + } else + node->path_consumed = le16_to_cpu(rsp->PathConsumed); + + node->server_type = le16_to_cpu(ref->ServerType); + node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); + + /* copy DfsPath */ + temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); + max_len = data_end - temp; + node->path_name = cifs_strndup_from_utf16(temp, max_len, + is_unicode, nls_codepage); + if (!node->path_name) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + /* copy link target UNC */ + temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); + max_len = data_end - temp; + node->node_name = cifs_strndup_from_utf16(temp, max_len, + is_unicode, nls_codepage); + if (!node->node_name) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + node->ttl = le32_to_cpu(ref->TimeToLive); + + ref++; + } + +parse_DFS_referrals_exit: + if (rc) { + free_dfs_info_array(*target_nodes, *num_of_nodes); + *target_nodes = NULL; + *num_of_nodes = 0; + } + return rc; +} + +struct cifs_aio_ctx * +cifs_aio_ctx_alloc(void) +{ + struct cifs_aio_ctx *ctx; + + /* + * Must use kzalloc to initialize ctx->bv to NULL and ctx->direct_io + * to false so that we know when we have to unreference pages within + * cifs_aio_ctx_release() + */ + ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + INIT_LIST_HEAD(&ctx->list); + mutex_init(&ctx->aio_mutex); + init_completion(&ctx->done); + kref_init(&ctx->refcount); + return ctx; +} + +void +cifs_aio_ctx_release(struct kref *refcount) +{ + struct cifs_aio_ctx *ctx = container_of(refcount, + struct cifs_aio_ctx, refcount); + + cifsFileInfo_put(ctx->cfile); + + /* + * ctx->bv is only set if setup_aio_ctx_iter() was call successfuly + * which means that iov_iter_get_pages() was a success and thus that + * we have taken reference on pages. + */ + if (ctx->bv) { + unsigned i; + + for (i = 0; i < ctx->npages; i++) { + if (ctx->should_dirty) + set_page_dirty(ctx->bv[i].bv_page); + put_page(ctx->bv[i].bv_page); + } + kvfree(ctx->bv); + } + + kfree(ctx); +} + +#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024) + +int +setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw) +{ + ssize_t rc; + unsigned int cur_npages; + unsigned int npages = 0; + unsigned int i; + size_t len; + size_t count = iov_iter_count(iter); + unsigned int saved_len; + size_t start; + unsigned int max_pages = iov_iter_npages(iter, INT_MAX); + struct page **pages = NULL; + struct bio_vec *bv = NULL; + + if (iov_iter_is_kvec(iter)) { + memcpy(&ctx->iter, iter, sizeof(*iter)); + ctx->len = count; + iov_iter_advance(iter, count); + return 0; + } + + if (array_size(max_pages, sizeof(*bv)) <= CIFS_AIO_KMALLOC_LIMIT) + bv = kmalloc_array(max_pages, sizeof(*bv), GFP_KERNEL); + + if (!bv) { + bv = vmalloc(array_size(max_pages, sizeof(*bv))); + if (!bv) + return -ENOMEM; + } + + if (array_size(max_pages, sizeof(*pages)) <= CIFS_AIO_KMALLOC_LIMIT) + pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL); + + if (!pages) { + pages = vmalloc(array_size(max_pages, sizeof(*pages))); + if (!pages) { + kvfree(bv); + return -ENOMEM; + } + } + + saved_len = count; + + while (count && npages < max_pages) { + rc = iov_iter_get_pages2(iter, pages, count, max_pages, &start); + if (rc < 0) { + cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc); + break; + } + + if (rc > count) { + cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc, + count); + break; + } + + count -= rc; + rc += start; + cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE); + + if (npages + cur_npages > max_pages) { + cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n", + npages + cur_npages, max_pages); + break; + } + + for (i = 0; i < cur_npages; i++) { + len = rc > PAGE_SIZE ? PAGE_SIZE : rc; + bv[npages + i].bv_page = pages[i]; + bv[npages + i].bv_offset = start; + bv[npages + i].bv_len = len - start; + rc -= len; + start = 0; + } + + npages += cur_npages; + } + + kvfree(pages); + ctx->bv = bv; + ctx->len = saved_len - count; + ctx->npages = npages; + iov_iter_bvec(&ctx->iter, rw, ctx->bv, npages, ctx->len); + return 0; +} + +/** + * cifs_alloc_hash - allocate hash and hash context together + * @name: The name of the crypto hash algo + * @sdesc: SHASH descriptor where to put the pointer to the hash TFM + * + * The caller has to make sure @sdesc is initialized to either NULL or + * a valid context. It can be freed via cifs_free_hash(). + */ +int +cifs_alloc_hash(const char *name, struct shash_desc **sdesc) +{ + int rc = 0; + struct crypto_shash *alg = NULL; + + if (*sdesc) + return 0; + + alg = crypto_alloc_shash(name, 0, 0); + if (IS_ERR(alg)) { + cifs_dbg(VFS, "Could not allocate shash TFM '%s'\n", name); + rc = PTR_ERR(alg); + *sdesc = NULL; + return rc; + } + + *sdesc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(alg), GFP_KERNEL); + if (*sdesc == NULL) { + cifs_dbg(VFS, "no memory left to allocate shash TFM '%s'\n", name); + crypto_free_shash(alg); + return -ENOMEM; + } + + (*sdesc)->tfm = alg; + return 0; +} + +/** + * cifs_free_hash - free hash and hash context together + * @sdesc: Where to find the pointer to the hash TFM + * + * Freeing a NULL descriptor is safe. + */ +void +cifs_free_hash(struct shash_desc **sdesc) +{ + if (unlikely(!sdesc) || !*sdesc) + return; + + if ((*sdesc)->tfm) { + crypto_free_shash((*sdesc)->tfm); + (*sdesc)->tfm = NULL; + } + + kfree_sensitive(*sdesc); + *sdesc = NULL; +} + +/** + * rqst_page_get_length - obtain the length and offset for a page in smb_rqst + * @rqst: The request descriptor + * @page: The index of the page to query + * @len: Where to store the length for this page: + * @offset: Where to store the offset for this page + */ +void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, + unsigned int *len, unsigned int *offset) +{ + *len = rqst->rq_pagesz; + *offset = (page == 0) ? rqst->rq_offset : 0; + + if (rqst->rq_npages == 1 || page == rqst->rq_npages-1) + *len = rqst->rq_tailsz; + else if (page == 0) + *len = rqst->rq_pagesz - rqst->rq_offset; +} + +void extract_unc_hostname(const char *unc, const char **h, size_t *len) +{ + const char *end; + + /* skip initial slashes */ + while (*unc && (*unc == '\\' || *unc == '/')) + unc++; + + end = unc; + + while (*end && !(*end == '\\' || *end == '/')) + end++; + + *h = unc; + *len = end - unc; +} + +/** + * copy_path_name - copy src path to dst, possibly truncating + * @dst: The destination buffer + * @src: The source name + * + * returns number of bytes written (including trailing nul) + */ +int copy_path_name(char *dst, const char *src) +{ + int name_len; + + /* + * PATH_MAX includes nul, so if strlen(src) >= PATH_MAX it + * will truncate and strlen(dst) will be PATH_MAX-1 + */ + name_len = strscpy(dst, src, PATH_MAX); + if (WARN_ON_ONCE(name_len < 0)) + name_len = PATH_MAX-1; + + /* we count the trailing nul */ + name_len++; + return name_len; +} + +struct super_cb_data { + void *data; + struct super_block *sb; +}; + +static void tcp_super_cb(struct super_block *sb, void *arg) +{ + struct super_cb_data *sd = arg; + struct TCP_Server_Info *server = sd->data; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + + if (sd->sb) + return; + + cifs_sb = CIFS_SB(sb); + tcon = cifs_sb_master_tcon(cifs_sb); + if (tcon->ses->server == server) + sd->sb = sb; +} + +static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *), + void *data) +{ + struct super_cb_data sd = { + .data = data, + .sb = NULL, + }; + struct file_system_type **fs_type = (struct file_system_type *[]) { + &cifs_fs_type, &smb3_fs_type, NULL, + }; + + for (; *fs_type; fs_type++) { + iterate_supers_type(*fs_type, f, &sd); + if (sd.sb) { + /* + * Grab an active reference in order to prevent automounts (DFS links) + * of expiring and then freeing up our cifs superblock pointer while + * we're doing failover. + */ + cifs_sb_active(sd.sb); + return sd.sb; + } + } + return ERR_PTR(-EINVAL); +} + +static void __cifs_put_super(struct super_block *sb) +{ + if (!IS_ERR_OR_NULL(sb)) + cifs_sb_deactive(sb); +} + +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) +{ + return __cifs_get_super(tcp_super_cb, server); +} + +void cifs_put_tcp_super(struct super_block *sb) +{ + __cifs_put_super(sb); +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +int match_target_ip(struct TCP_Server_Info *server, + const char *share, size_t share_len, + bool *result) +{ + int rc; + char *target, *tip = NULL; + struct sockaddr tipaddr; + + *result = false; + + target = kzalloc(share_len + 3, GFP_KERNEL); + if (!target) { + rc = -ENOMEM; + goto out; + } + + scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share); + + cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2); + + rc = dns_resolve_server_name_to_ip(target, &tip, NULL); + if (rc < 0) + goto out; + + cifs_dbg(FYI, "%s: target ip: %s\n", __func__, tip); + + if (!cifs_convert_address(&tipaddr, tip, strlen(tip))) { + cifs_dbg(VFS, "%s: failed to convert target ip address\n", + __func__); + rc = -EINVAL; + goto out; + } + + *result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, + &tipaddr); + cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result); + rc = 0; + +out: + kfree(target); + kfree(tip); + + return rc; +} + +int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix) +{ + kfree(cifs_sb->prepath); + + if (prefix && *prefix) { + cifs_sb->prepath = cifs_sanitize_prepath(prefix, GFP_ATOMIC); + if (!cifs_sb->prepath) + return -ENOMEM; + + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); + } else + cifs_sb->prepath = NULL; + + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + return 0; +} + +/* + * Handle weird Windows SMB server behaviour. It responds with + * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for + * "\\\" DFS reference, where contains + * non-ASCII unicode symbols. + */ +int cifs_inval_name_dfs_link_error(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + bool *islink) +{ + struct cifs_ses *ses = tcon->ses; + size_t len; + char *path; + char *ref_path; + + *islink = false; + + /* + * Fast path - skip check when @full_path doesn't have a prefix path to + * look up or tcon is not DFS. + */ + if (strlen(full_path) < 2 || !cifs_sb || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || + !is_tcon_dfs(tcon) || !ses->server->origin_fullpath) + return 0; + + /* + * Slow path - tcon is DFS and @full_path has prefix path, so attempt + * to get a referral to figure out whether it is an DFS link. + */ + len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1; + path = kmalloc(len, GFP_KERNEL); + if (!path) + return -ENOMEM; + + scnprintf(path, len, "%s%s", tcon->tree_name, full_path); + ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + kfree(path); + + if (IS_ERR(ref_path)) { + if (PTR_ERR(ref_path) != -EINVAL) + return PTR_ERR(ref_path); + } else { + struct dfs_info3_param *refs = NULL; + int num_refs = 0; + + /* + * XXX: we are not using dfs_cache_find() here because we might + * end filling all the DFS cache and thus potentially + * removing cached DFS targets that the client would eventually + * need during failover. + */ + if (ses->server->ops->get_dfs_refer && + !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs, + &num_refs, cifs_sb->local_nls, + cifs_remap(cifs_sb))) + *islink = refs[0].server_type == DFS_TYPE_LINK; + free_dfs_info_array(refs, num_refs); + kfree(ref_path); + } + return 0; +} +#endif + +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry) +{ + int timeout = 10; + int rc; + + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + return 0; + } + timeout *= server->nr_targets; + spin_unlock(&server->srv_lock); + + /* + * Give demultiplex thread up to 10 seconds to each target available for + * reconnect -- should be greater than cifs socket timeout which is 7 + * seconds. + * + * On "soft" mounts we wait once. Hard mounts keep retrying until + * process is killed or server comes back on-line. + */ + do { + rc = wait_event_interruptible_timeout(server->response_q, + (server->tcpStatus != CifsNeedReconnect), + timeout * HZ); + if (rc < 0) { + cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", + __func__); + return -ERESTARTSYS; + } + + /* are we still trying to reconnect? */ + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + return 0; + } + spin_unlock(&server->srv_lock); + } while (retry); + + cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); + return -EHOSTDOWN; +} diff --git a/fs/smb/client/netlink.c b/fs/smb/client/netlink.c new file mode 100644 index 000000000000..147d9409252c --- /dev/null +++ b/fs/smb/client/netlink.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Netlink routines for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#include +#include + +#include "netlink.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifs_swn.h" + +static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = { + [CIFS_GENL_ATTR_SWN_REGISTRATION_ID] = { .type = NLA_U32 }, + [CIFS_GENL_ATTR_SWN_NET_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_SHARE_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_IP] = { .len = sizeof(struct sockaddr_storage) }, + [CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_IP_NOTIFY] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_KRB_AUTH] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_USER_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_PASSWORD] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_DOMAIN_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE] = { .type = NLA_U32 }, + [CIFS_GENL_ATTR_SWN_RESOURCE_STATE] = { .type = NLA_U32 }, + [CIFS_GENL_ATTR_SWN_RESOURCE_NAME] = { .type = NLA_STRING}, +}; + +static const struct genl_ops cifs_genl_ops[] = { + { + .cmd = CIFS_GENL_CMD_SWN_NOTIFY, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = cifs_swn_notify, + }, +}; + +static const struct genl_multicast_group cifs_genl_mcgrps[] = { + [CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME }, +}; + +struct genl_family cifs_genl_family = { + .name = CIFS_GENL_NAME, + .version = CIFS_GENL_VERSION, + .hdrsize = 0, + .maxattr = CIFS_GENL_ATTR_MAX, + .module = THIS_MODULE, + .policy = cifs_genl_policy, + .ops = cifs_genl_ops, + .n_ops = ARRAY_SIZE(cifs_genl_ops), + .resv_start_op = CIFS_GENL_CMD_SWN_NOTIFY + 1, + .mcgrps = cifs_genl_mcgrps, + .n_mcgrps = ARRAY_SIZE(cifs_genl_mcgrps), +}; + +/** + * cifs_genl_init - Register generic netlink family + * + * Return zero if initialized successfully, otherwise non-zero. + */ +int cifs_genl_init(void) +{ + int ret; + + ret = genl_register_family(&cifs_genl_family); + if (ret < 0) { + cifs_dbg(VFS, "%s: failed to register netlink family\n", + __func__); + return ret; + } + + return 0; +} + +/** + * cifs_genl_exit - Unregister generic netlink family + */ +void cifs_genl_exit(void) +{ + int ret; + + ret = genl_unregister_family(&cifs_genl_family); + if (ret < 0) { + cifs_dbg(VFS, "%s: failed to unregister netlink family\n", + __func__); + } +} diff --git a/fs/smb/client/netlink.h b/fs/smb/client/netlink.h new file mode 100644 index 000000000000..e2fa8ed24c54 --- /dev/null +++ b/fs/smb/client/netlink.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Netlink routines for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#ifndef _CIFS_NETLINK_H +#define _CIFS_NETLINK_H + +extern struct genl_family cifs_genl_family; + +extern int cifs_genl_init(void); +extern void cifs_genl_exit(void); + +#endif /* _CIFS_NETLINK_H */ diff --git a/fs/smb/client/netmisc.c b/fs/smb/client/netmisc.c new file mode 100644 index 000000000000..1b52e6ac431c --- /dev/null +++ b/fs/smb/client/netmisc.c @@ -0,0 +1,1021 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Error mapping routines from Samba libsmb/errormap.c + * Copyright (C) Andrew Tridgell 2001 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smberr.h" +#include "cifs_debug.h" +#include "nterr.h" + +struct smb_to_posix_error { + __u16 smb_err; + int posix_code; +}; + +static const struct smb_to_posix_error mapping_table_ERRDOS[] = { + {ERRbadfunc, -EINVAL}, + {ERRbadfile, -ENOENT}, + {ERRbadpath, -ENOTDIR}, + {ERRnofids, -EMFILE}, + {ERRnoaccess, -EACCES}, + {ERRbadfid, -EBADF}, + {ERRbadmcb, -EIO}, + {ERRnomem, -EREMOTEIO}, + {ERRbadmem, -EFAULT}, + {ERRbadenv, -EFAULT}, + {ERRbadformat, -EINVAL}, + {ERRbadaccess, -EACCES}, + {ERRbaddata, -EIO}, + {ERRbaddrive, -ENXIO}, + {ERRremcd, -EACCES}, + {ERRdiffdevice, -EXDEV}, + {ERRnofiles, -ENOENT}, + {ERRwriteprot, -EROFS}, + {ERRbadshare, -EBUSY}, + {ERRlock, -EACCES}, + {ERRunsup, -EINVAL}, + {ERRnosuchshare, -ENXIO}, + {ERRfilexists, -EEXIST}, + {ERRinvparm, -EINVAL}, + {ERRdiskfull, -ENOSPC}, + {ERRinvname, -ENOENT}, + {ERRinvlevel, -EOPNOTSUPP}, + {ERRdirnotempty, -ENOTEMPTY}, + {ERRnotlocked, -ENOLCK}, + {ERRcancelviolation, -ENOLCK}, + {ERRalreadyexists, -EEXIST}, + {ERRmoredata, -EOVERFLOW}, + {ERReasnotsupported, -EOPNOTSUPP}, + {ErrQuota, -EDQUOT}, + {ErrNotALink, -ENOLINK}, + {ERRnetlogonNotStarted, -ENOPROTOOPT}, + {ERRsymlink, -EOPNOTSUPP}, + {ErrTooManyLinks, -EMLINK}, + {0, 0} +}; + +static const struct smb_to_posix_error mapping_table_ERRSRV[] = { + {ERRerror, -EIO}, + {ERRbadpw, -EACCES}, /* was EPERM */ + {ERRbadtype, -EREMOTE}, + {ERRaccess, -EACCES}, + {ERRinvtid, -ENXIO}, + {ERRinvnetname, -ENXIO}, + {ERRinvdevice, -ENXIO}, + {ERRqfull, -ENOSPC}, + {ERRqtoobig, -ENOSPC}, + {ERRqeof, -EIO}, + {ERRinvpfid, -EBADF}, + {ERRsmbcmd, -EBADRQC}, + {ERRsrverror, -EIO}, + {ERRbadBID, -EIO}, + {ERRfilespecs, -EINVAL}, + {ERRbadLink, -EIO}, + {ERRbadpermits, -EINVAL}, + {ERRbadPID, -ESRCH}, + {ERRsetattrmode, -EINVAL}, + {ERRpaused, -EHOSTDOWN}, + {ERRmsgoff, -EHOSTDOWN}, + {ERRnoroom, -ENOSPC}, + {ERRrmuns, -EUSERS}, + {ERRtimeout, -ETIME}, + {ERRnoresource, -EREMOTEIO}, + {ERRtoomanyuids, -EUSERS}, + {ERRbaduid, -EACCES}, + {ERRusempx, -EIO}, + {ERRusestd, -EIO}, + {ERR_NOTIFY_ENUM_DIR, -ENOBUFS}, + {ERRnoSuchUser, -EACCES}, +/* {ERRaccountexpired, -EACCES}, + {ERRbadclient, -EACCES}, + {ERRbadLogonTime, -EACCES}, + {ERRpasswordExpired, -EACCES},*/ + {ERRaccountexpired, -EKEYEXPIRED}, + {ERRbadclient, -EACCES}, + {ERRbadLogonTime, -EACCES}, + {ERRpasswordExpired, -EKEYEXPIRED}, + + {ERRnosupport, -EINVAL}, + {0, 0} +}; + +/* + * Convert a string containing text IPv4 or IPv6 address to binary form. + * + * Returns 0 on failure. + */ +static int +cifs_inet_pton(const int address_family, const char *cp, int len, void *dst) +{ + int ret = 0; + + /* calculate length by finding first slash or NULL */ + if (address_family == AF_INET) + ret = in4_pton(cp, len, dst, '\\', NULL); + else if (address_family == AF_INET6) + ret = in6_pton(cp, len, dst , '\\', NULL); + + cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n", + ret, len, len, cp); + if (ret > 0) + ret = 1; + return ret; +} + +/* + * Try to convert a string to an IPv4 address and then attempt to convert + * it to an IPv6 address if that fails. Set the family field if either + * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to + * treat the part following it as a numeric sin6_scope_id. + * + * Returns 0 on failure. + */ +int +cifs_convert_address(struct sockaddr *dst, const char *src, int len) +{ + int rc, alen, slen; + const char *pct; + char scope_id[13]; + struct sockaddr_in *s4 = (struct sockaddr_in *) dst; + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; + + /* IPv4 address */ + if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) { + s4->sin_family = AF_INET; + return 1; + } + + /* attempt to exclude the scope ID from the address part */ + pct = memchr(src, '%', len); + alen = pct ? pct - src : len; + + rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr); + if (!rc) + return rc; + + s6->sin6_family = AF_INET6; + if (pct) { + /* grab the scope ID */ + slen = len - (alen + 1); + if (slen <= 0 || slen > 12) + return 0; + memcpy(scope_id, pct + 1, slen); + scope_id[slen] = '\0'; + + rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id); + rc = (rc == 0) ? 1 : 0; + } + + return rc; +} + +void +cifs_set_port(struct sockaddr *addr, const unsigned short int port) +{ + switch (addr->sa_family) { + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + break; + } +} + +/***************************************************************************** +convert a NT status code to a dos class/code + *****************************************************************************/ +/* NT status -> dos error map */ +static const struct { + __u8 dos_class; + __u16 dos_code; + __u32 ntstatus; +} ntstatus_to_dos_map[] = { + { + ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { + ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { + ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, { + ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { + ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { + ERRDOS, 87, NT_STATUS_INVALID_CID}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { + ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { + ERRDOS, 38, NT_STATUS_END_OF_FILE}, { + ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { + ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { + ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, +/* { This NT error code was 'sqashed' + from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + during the session setup } */ + { + ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { + ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { + ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { + ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { + ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { + ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { + ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { + ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { + ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { + ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { + ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { + /* mapping changed since shell does lookup on * expects FileNotFound */ + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { + ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { + ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { + ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { + ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { + ERRDOS, 23, NT_STATUS_DATA_ERROR}, { + ERRDOS, 23, NT_STATUS_CRC_ERROR}, { + ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { + ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { + ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { + ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { + ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { + ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { + ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { + ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { + ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { + ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { + ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { + ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { + ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { + ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { + ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { + ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { + ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { + ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { + ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { + ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ + ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, +/* { This NT error code was 'sqashed' + from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { + ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { + ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { + ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { + ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { + ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { + ERRDOS, 112, NT_STATUS_DISK_FULL}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { + ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { + ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { + ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { + ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { + ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { + ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_INSUFFICIENT_RESOURCES to + NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */ + { + ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { + ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { + ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { + ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { + ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { + ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { + ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { + ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { + ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { + ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { + ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { + ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { + ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { + ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { + ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { + ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { + ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { + ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { + ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { + ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { + ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { + ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { + ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { + ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { + ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { + ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { + ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { + ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { + ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { + ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { + ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { + ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { + ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { + ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { + ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { + ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { + ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { + ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { + ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { + ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { + ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { + ERRDOS, 203, 0xc0000100}, { + ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { + ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { + ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { + ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { + ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { + ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { + ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { + ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { + ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { + ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { + ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { + ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { + ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { + ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { + ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { + ERRDOS, 59, NT_STATUS_LINK_FAILED}, { + ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { + ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { + ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { + ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { + ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { + ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { + ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { + ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { + ERRHRD, ERRgeneral, 0xc000016e}, { + ERRHRD, ERRgeneral, 0xc000016f}, { + ERRHRD, ERRgeneral, 0xc0000170}, { + ERRHRD, ERRgeneral, 0xc0000171}, { + ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { + ERRHRD, ERRgeneral, 0xc0000179}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { + ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { + ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { + ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { + ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { + ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { + ERRDOS, 19, NT_STATUS_TOO_LATE}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_TRUST_SAM_ACCOUNT to + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { + ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, +/* { This NT error code was 'sqashed' + from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { + ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { + ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { + ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { + ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { + ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { + ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { + ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { + ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { + ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { + ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { + ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { + ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { + ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { + ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { + ERRHRD, ERRgeneral, 0xc000024a}, { + ERRHRD, ERRgeneral, 0xc000024b}, { + ERRHRD, ERRgeneral, 0xc000024c}, { + ERRHRD, ERRgeneral, 0xc000024d}, { + ERRHRD, ERRgeneral, 0xc000024e}, { + ERRHRD, ERRgeneral, 0xc000024f}, { + ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { + ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { + ERRHRD, ERRgeneral, 0xc000025d}, { + ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { + ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { + ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { + ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { + ERRDOS, 21, 0xc000026e}, { + ERRDOS, 161, 0xc0000281}, { + ERRDOS, ERRnoaccess, 0xc000028a}, { + ERRDOS, ERRnoaccess, 0xc000028b}, { + ERRHRD, ERRgeneral, 0xc000028c}, { + ERRDOS, ERRnoaccess, 0xc000028d}, { + ERRDOS, ERRnoaccess, 0xc000028e}, { + ERRDOS, ERRnoaccess, 0xc000028f}, { + ERRDOS, ERRnoaccess, 0xc0000290}, { + ERRDOS, ERRbadfunc, 0xc000029c}, { + ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { + ERRDOS, ERRinvlevel, 0x007c0001}, { + 0, 0, 0 } +}; + +/***************************************************************************** + Print an error message from the status code + *****************************************************************************/ +static void +cifs_print_status(__u32 status_code) +{ + int idx = 0; + + while (nt_errs[idx].nt_errstr != NULL) { + if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) == + (status_code & 0xFFFFFF)) { + pr_notice("Status code returned 0x%08x %s\n", + status_code, nt_errs[idx].nt_errstr); + } + idx++; + } + return; +} + + +static void +ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) +{ + int i; + if (ntstatus == 0) { + *eclass = 0; + *ecode = 0; + return; + } + for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { + if (ntstatus == ntstatus_to_dos_map[i].ntstatus) { + *eclass = ntstatus_to_dos_map[i].dos_class; + *ecode = ntstatus_to_dos_map[i].dos_code; + return; + } + } + *eclass = ERRHRD; + *ecode = ERRgeneral; +} + +int +map_smb_to_linux_error(char *buf, bool logErr) +{ + struct smb_hdr *smb = (struct smb_hdr *)buf; + unsigned int i; + int rc = -EIO; /* if transport error smb error may not be set */ + __u8 smberrclass; + __u16 smberrcode; + + /* BB if NT Status codes - map NT BB */ + + /* old style smb error codes */ + if (smb->Status.CifsError == 0) + return 0; + + if (smb->Flags2 & SMBFLG2_ERR_STATUS) { + /* translate the newer STATUS codes to old style SMB errors + * and then to POSIX errors */ + __u32 err = le32_to_cpu(smb->Status.CifsError); + if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED))) + cifs_print_status(err); + else if (cifsFYI & CIFS_RC) + cifs_print_status(err); + ntstatus_to_dos(err, &smberrclass, &smberrcode); + } else { + smberrclass = smb->Status.DosError.ErrorClass; + smberrcode = le16_to_cpu(smb->Status.DosError.Error); + } + + /* old style errors */ + + /* DOS class smb error codes - map DOS */ + if (smberrclass == ERRDOS) { + /* 1 byte field no need to byte reverse */ + for (i = 0; + i < + sizeof(mapping_table_ERRDOS) / + sizeof(struct smb_to_posix_error); i++) { + if (mapping_table_ERRDOS[i].smb_err == 0) + break; + else if (mapping_table_ERRDOS[i].smb_err == + smberrcode) { + rc = mapping_table_ERRDOS[i].posix_code; + break; + } + /* else try next error mapping one to see if match */ + } + } else if (smberrclass == ERRSRV) { + /* server class of error codes */ + for (i = 0; + i < + sizeof(mapping_table_ERRSRV) / + sizeof(struct smb_to_posix_error); i++) { + if (mapping_table_ERRSRV[i].smb_err == 0) + break; + else if (mapping_table_ERRSRV[i].smb_err == + smberrcode) { + rc = mapping_table_ERRSRV[i].posix_code; + break; + } + /* else try next error mapping to see if match */ + } + } + /* else ERRHRD class errors or junk - return EIO */ + + cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", + le32_to_cpu(smb->Status.CifsError), rc); + + /* generic corrective action e.g. reconnect SMB session on + * ERRbaduid could be added */ + + return rc; +} + +int +map_and_check_smb_error(struct mid_q_entry *mid, bool logErr) +{ + int rc; + struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; + + rc = map_smb_to_linux_error((char *)smb, logErr); + if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { + /* possible ERRBaduid */ + __u8 class = smb->Status.DosError.ErrorClass; + __u16 code = le16_to_cpu(smb->Status.DosError.Error); + + /* switch can be used to handle different errors */ + if (class == ERRSRV && code == ERRbaduid) { + cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", + code); + cifs_signal_cifsd_for_reconnect(mid->server, false); + } + } + + return rc; +} + + +/* + * calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message + */ +unsigned int +smbCalcSize(void *buf) +{ + struct smb_hdr *ptr = buf; + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + + 2 /* size of the bcc field */ + get_bcc(ptr)); +} + +/* The following are taken from fs/ntfs/util.c */ + +#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 +cifs_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + /* BB what about the timezone? BB */ + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100); + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} + +/* Convert the Unix UTC into NT UTC. */ +u64 +cifs_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET; +} + +static const int total_days_of_prev_months[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset) +{ + struct timespec64 ts; + time64_t sec, days; + int min, day, month, year; + u16 date = le16_to_cpu(le_date); + u16 time = le16_to_cpu(le_time); + SMB_TIME *st = (SMB_TIME *)&time; + SMB_DATE *sd = (SMB_DATE *)&date; + + cifs_dbg(FYI, "date %d time %d\n", date, time); + + sec = 2 * st->TwoSeconds; + min = st->Minutes; + if ((sec > 59) || (min > 59)) + cifs_dbg(VFS, "Invalid time min %d sec %lld\n", min, sec); + sec += (min * 60); + sec += 60 * 60 * st->Hours; + if (st->Hours > 24) + cifs_dbg(VFS, "Invalid hours %d\n", st->Hours); + day = sd->Day; + month = sd->Month; + if (day < 1 || day > 31 || month < 1 || month > 12) { + cifs_dbg(VFS, "Invalid date, month %d day: %d\n", month, day); + day = clamp(day, 1, 31); + month = clamp(month, 1, 12); + } + month -= 1; + days = day + total_days_of_prev_months[month]; + days += 3652; /* account for difference in days between 1980 and 1970 */ + year = sd->Year; + days += year * 365; + days += (year/4); /* leap year */ + /* generalized leap year calculation is more complex, ie no leap year + for years/100 except for years/400, but since the maximum number for DOS + year is 2**7, the last year is 1980+127, which means we need only + consider 2 special case years, ie the years 2000 and 2100, and only + adjust for the lack of leap year for the year 2100, as 2000 was a + leap year (divisable by 400) */ + if (year >= 120) /* the year 2100 */ + days = days - 1; /* do not count leap year for the year 2100 */ + + /* adjust for leap year where we are still before leap day */ + if (year != 120) + days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0); + sec += 24 * 60 * 60 * days; + + ts.tv_sec = sec + offset; + + /* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */ + + ts.tv_nsec = 0; + return ts; +} diff --git a/fs/smb/client/nterr.c b/fs/smb/client/nterr.c new file mode 100644 index 000000000000..358a766375b4 --- /dev/null +++ b/fs/smb/client/nterr.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * RPC Pipe client / server routines + * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. + */ + +/* NT error codes - see nterr.h */ +#include +#include +#include "nterr.h" + +const struct nt_err_code_struct nt_errs[] = { + {"NT_STATUS_OK", NT_STATUS_OK}, + {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL}, + {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED}, + {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS}, + {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH}, + {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION}, + {"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW}, + {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR}, + {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA}, + {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE}, + {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK}, + {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC}, + {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID}, + {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED}, + {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER}, + {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE}, + {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE}, + {"NT_STATUS_INVALID_DEVICE_REQUEST", + NT_STATUS_INVALID_DEVICE_REQUEST}, + {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE}, + {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME}, + {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE}, + {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA}, + {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR}, + {"NT_STATUS_MORE_PROCESSING_REQUIRED", + NT_STATUS_MORE_PROCESSING_REQUIRED}, + {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY}, + {"NT_STATUS_CONFLICTING_ADDRESSES", + NT_STATUS_CONFLICTING_ADDRESSES}, + {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW}, + {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM}, + {"NT_STATUS_UNABLE_TO_DELETE_SECTION", + NT_STATUS_UNABLE_TO_DELETE_SECTION}, + {"NT_STATUS_INVALID_SYSTEM_SERVICE", + NT_STATUS_INVALID_SYSTEM_SERVICE}, + {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION}, + {"NT_STATUS_INVALID_LOCK_SEQUENCE", + NT_STATUS_INVALID_LOCK_SEQUENCE}, + {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE}, + {"NT_STATUS_INVALID_FILE_FOR_SECTION", + NT_STATUS_INVALID_FILE_FOR_SECTION}, + {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED}, + {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED}, + {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL}, + {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH}, + {"NT_STATUS_NONCONTINUABLE_EXCEPTION", + NT_STATUS_NONCONTINUABLE_EXCEPTION}, + {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION}, + {"NT_STATUS_UNWIND", NT_STATUS_UNWIND}, + {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK}, + {"NT_STATUS_INVALID_UNWIND_TARGET", + NT_STATUS_INVALID_UNWIND_TARGET}, + {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED}, + {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR}, + {"NT_STATUS_UNABLE_TO_DECOMMIT_VM", + NT_STATUS_UNABLE_TO_DECOMMIT_VM}, + {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED}, + {"NT_STATUS_INVALID_PORT_ATTRIBUTES", + NT_STATUS_INVALID_PORT_ATTRIBUTES}, + {"NT_STATUS_PORT_MESSAGE_TOO_LONG", + NT_STATUS_PORT_MESSAGE_TOO_LONG}, + {"NT_STATUS_INVALID_PARAMETER_MIX", + NT_STATUS_INVALID_PARAMETER_MIX}, + {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER}, + {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR}, + {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID}, + {"NT_STATUS_OBJECT_NAME_NOT_FOUND", + NT_STATUS_OBJECT_NAME_NOT_FOUND}, + {"NT_STATUS_OBJECT_NAME_COLLISION", + NT_STATUS_OBJECT_NAME_COLLISION}, + {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE}, + {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED}, + {"NT_STATUS_DEVICE_ALREADY_ATTACHED", + NT_STATUS_DEVICE_ALREADY_ATTACHED}, + {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID}, + {"NT_STATUS_OBJECT_PATH_NOT_FOUND", + NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD", + NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, + {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN}, + {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR}, + {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR}, + {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR}, + {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG}, + {"NT_STATUS_PORT_CONNECTION_REFUSED", + NT_STATUS_PORT_CONNECTION_REFUSED}, + {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE}, + {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION}, + {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED}, + {"NT_STATUS_INVALID_PAGE_PROTECTION", + NT_STATUS_INVALID_PAGE_PROTECTION}, + {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED}, + {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", + NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET}, + {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE}, + {"NT_STATUS_SUSPEND_COUNT_EXCEEDED", + NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {"NT_STATUS_THREAD_IS_TERMINATING", + NT_STATUS_THREAD_IS_TERMINATING}, + {"NT_STATUS_BAD_WORKING_SET_LIMIT", + NT_STATUS_BAD_WORKING_SET_LIMIT}, + {"NT_STATUS_INCOMPATIBLE_FILE_MAP", + NT_STATUS_INCOMPATIBLE_FILE_MAP}, + {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION}, + {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED}, + {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE}, + {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY}, + {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE}, + {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR}, + {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT}, + {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED}, + {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING}, + {"NT_STATUS_CTL_FILE_NOT_SUPPORTED", + NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION}, + {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH}, + {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER}, + {"NT_STATUS_INVALID_PRIMARY_GROUP", + NT_STATUS_INVALID_PRIMARY_GROUP}, + {"NT_STATUS_NO_IMPERSONATION_TOKEN", + NT_STATUS_NO_IMPERSONATION_TOKEN}, + {"NT_STATUS_CANT_DISABLE_MANDATORY", + NT_STATUS_CANT_DISABLE_MANDATORY}, + {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS}, + {"NT_STATUS_NO_SUCH_LOGON_SESSION", + NT_STATUS_NO_SUCH_LOGON_SESSION}, + {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE}, + {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD}, + {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME}, + {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS}, + {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER}, + {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS}, + {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP}, + {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP}, + {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP}, + {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN}, + {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD}, + {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD}, + {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION}, + {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE}, + {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION}, + {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS}, + {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION}, + {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED}, + {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED}, + {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED}, + {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED", + NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, + {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED}, + {"NT_STATUS_INVALID_SUB_AUTHORITY", + NT_STATUS_INVALID_SUB_AUTHORITY}, + {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL}, + {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID}, + {"NT_STATUS_INVALID_SECURITY_DESCR", + NT_STATUS_INVALID_SECURITY_DESCR}, + {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND}, + {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT}, + {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN}, + {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL}, + {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED}, + {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL}, + {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED}, + {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED}, + {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED", + NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED}, + {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY}, + {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED}, + {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL}, + {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED}, + {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA}, + {"NT_STATUS_RESOURCE_DATA_NOT_FOUND", + NT_STATUS_RESOURCE_DATA_NOT_FOUND}, + {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND", + NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, + {"NT_STATUS_RESOURCE_NAME_NOT_FOUND", + NT_STATUS_RESOURCE_NAME_NOT_FOUND}, + {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED", + NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, + {"NT_STATUS_FLOAT_DENORMAL_OPERAND", + NT_STATUS_FLOAT_DENORMAL_OPERAND}, + {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, + {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT}, + {"NT_STATUS_FLOAT_INVALID_OPERATION", + NT_STATUS_FLOAT_INVALID_OPERATION}, + {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW}, + {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK}, + {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW}, + {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO", + NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, + {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW}, + {"NT_STATUS_PRIVILEGED_INSTRUCTION", + NT_STATUS_PRIVILEGED_INSTRUCTION}, + {"NT_STATUS_TOO_MANY_PAGING_FILES", + NT_STATUS_TOO_MANY_PAGING_FILES}, + {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID}, + {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED", + NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, + {"NT_STATUS_INSUFFICIENT_RESOURCES", + NT_STATUS_INSUFFICIENT_RESOURCES}, + {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND}, + {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR}, + {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED}, + {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE}, + {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE}, + {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED}, + {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA}, + {"NT_STATUS_MEDIA_WRITE_PROTECTED", + NT_STATUS_MEDIA_WRITE_PROTECTED}, + {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY}, + {"NT_STATUS_INVALID_GROUP_ATTRIBUTES", + NT_STATUS_INVALID_GROUP_ATTRIBUTES}, + {"NT_STATUS_BAD_IMPERSONATION_LEVEL", + NT_STATUS_BAD_IMPERSONATION_LEVEL}, + {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS}, + {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS}, + {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE}, + {"NT_STATUS_BAD_MASTER_BOOT_RECORD", + NT_STATUS_BAD_MASTER_BOOT_RECORD}, + {"NT_STATUS_INSTRUCTION_MISALIGNMENT", + NT_STATUS_INSTRUCTION_MISALIGNMENT}, + {"NT_STATUS_INSTANCE_NOT_AVAILABLE", + NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE}, + {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE}, + {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY}, + {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION}, + {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED}, + {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING}, + {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED}, + {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING}, + {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE}, + {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT}, + {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED}, + {"NT_STATUS_PROFILING_NOT_STARTED", + NT_STATUS_PROFILING_NOT_STARTED}, + {"NT_STATUS_PROFILING_NOT_STOPPED", + NT_STATUS_PROFILING_NOT_STOPPED}, + {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET}, + {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY}, + {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED}, + {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING}, + {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME}, + {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH}, + {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY}, + {"NT_STATUS_DEVICE_DOES_NOT_EXIST", + NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS}, + {"NT_STATUS_ADAPTER_HARDWARE_ERROR", + NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {"NT_STATUS_INVALID_NETWORK_RESPONSE", + NT_STATUS_INVALID_NETWORK_RESPONSE}, + {"NT_STATUS_UNEXPECTED_NETWORK_ERROR", + NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER}, + {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL}, + {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE}, + {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED}, + {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED}, + {"NT_STATUS_NETWORK_ACCESS_DENIED", + NT_STATUS_NETWORK_ACCESS_DENIED}, + {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE}, + {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME}, + {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES}, + {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS}, + {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED}, + {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED}, + {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED}, + {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT}, + {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT}, + {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE}, + {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED}, + {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", + NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {"NT_STATUS_NO_SECURITY_ON_OBJECT", + NT_STATUS_NO_SECURITY_ON_OBJECT}, + {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT}, + {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY}, + {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO", + NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, + {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF}, + {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE}, + {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE}, + {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE}, + {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN}, + {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS}, + {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED", + NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, + {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED}, + {"NT_STATUS_INVALID_OPLOCK_PROTOCOL", + NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {"NT_STATUS_INTERNAL_DB_CORRUPTION", + NT_STATUS_INTERNAL_DB_CORRUPTION}, + {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR}, + {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED}, + {"NT_STATUS_BAD_DESCRIPTOR_FORMAT", + NT_STATUS_BAD_DESCRIPTOR_FORMAT}, + {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER}, + {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR}, + {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR", + NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, + {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR", + NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, + {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", + NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, + {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS}, + {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS}, + {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1}, + {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2}, + {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3}, + {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4}, + {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5}, + {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6}, + {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7}, + {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8}, + {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9}, + {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10}, + {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11}, + {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12}, + {"NT_STATUS_REDIRECTOR_NOT_STARTED", + NT_STATUS_REDIRECTOR_NOT_STARTED}, + {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED}, + {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW}, + {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE}, + {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE}, + {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY}, + {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR}, + {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY}, + {"NT_STATUS_BAD_LOGON_SESSION_STATE", + NT_STATUS_BAD_LOGON_SESSION_STATE}, + {"NT_STATUS_LOGON_SESSION_COLLISION", + NT_STATUS_LOGON_SESSION_COLLISION}, + {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG}, + {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN}, + {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE}, + {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND}, + {"NT_STATUS_PROCESS_IS_TERMINATING", + NT_STATUS_PROCESS_IS_TERMINATING}, + {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE}, + {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION}, + {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE}, + {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED}, + {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT}, + {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST}, + {"NT_STATUS_ABIOS_LID_ALREADY_OWNED", + NT_STATUS_ABIOS_LID_ALREADY_OWNED}, + {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER}, + {"NT_STATUS_ABIOS_INVALID_COMMAND", + NT_STATUS_ABIOS_INVALID_COMMAND}, + {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID}, + {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", + NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, + {"NT_STATUS_ABIOS_INVALID_SELECTOR", + NT_STATUS_ABIOS_INVALID_SELECTOR}, + {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT}, + {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE}, + {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET}, + {"NT_STATUS_INVALID_LDT_DESCRIPTOR", + NT_STATUS_INVALID_LDT_DESCRIPTOR}, + {"NT_STATUS_INVALID_IMAGE_NE_FORMAT", + NT_STATUS_INVALID_IMAGE_NE_FORMAT}, + {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE}, + {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE}, + {"NT_STATUS_MAPPED_FILE_SIZE_ZERO", + NT_STATUS_MAPPED_FILE_SIZE_ZERO}, + {"NT_STATUS_TOO_MANY_OPENED_FILES", + NT_STATUS_TOO_MANY_OPENED_FILES}, + {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED}, + {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE}, + {"NT_STATUS_INVALID_COMPUTER_NAME", + NT_STATUS_INVALID_COMPUTER_NAME}, + {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED}, + {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT}, + {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP}, + {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER}, + {"NT_STATUS_MEMBERS_PRIMARY_GROUP", + NT_STATUS_MEMBERS_PRIMARY_GROUP}, + {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED}, + {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS}, + {"NT_STATUS_THREAD_NOT_IN_PROCESS", + NT_STATUS_THREAD_NOT_IN_PROCESS}, + {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE}, + {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", + NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, + {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT}, + {"NT_STATUS_INVALID_IMAGE_LE_FORMAT", + NT_STATUS_INVALID_IMAGE_LE_FORMAT}, + {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ}, + {"NT_STATUS_INVALID_IMAGE_PROTECT", + NT_STATUS_INVALID_IMAGE_PROTECT}, + {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16}, + {"NT_STATUS_LOGON_SERVER_CONFLICT", + NT_STATUS_LOGON_SERVER_CONFLICT}, + {"NT_STATUS_TIME_DIFFERENCE_AT_DC", + NT_STATUS_TIME_DIFFERENCE_AT_DC}, + {"NT_STATUS_SYNCHRONIZATION_REQUIRED", + NT_STATUS_SYNCHRONIZATION_REQUIRED}, + {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND}, + {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED}, + {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED}, + {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND}, + {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND}, + {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT}, + {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT}, + {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT}, + {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES}, + {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED}, + {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT}, + {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION}, + {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS}, + {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED}, + {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE}, + {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION}, + {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE}, + {"NT_STATUS_PAGEFILE_CREATE_FAILED", + NT_STATUS_PAGEFILE_CREATE_FAILED}, + {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE}, + {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL}, + {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE}, + {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT", + NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, + {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN}, + {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT}, + {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED}, + {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR}, + {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME}, + {"NT_STATUS_SERIAL_NO_DEVICE_INITED", + NT_STATUS_SERIAL_NO_DEVICE_INITED}, + {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS}, + {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS}, + {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS}, + {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS}, + {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED}, + {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS}, + {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG}, + {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR}, + {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE}, + {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS}, + {"NT_STATUS_LOGON_TYPE_NOT_GRANTED", + NT_STATUS_LOGON_TYPE_NOT_GRANTED}, + {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE}, + {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", + NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, + {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", + NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, + {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER}, + {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY", + NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, + {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER}, + {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER}, + {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER}, + {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME}, + {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", + NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, + {"NT_STATUS_FLOPPY_WRONG_CYLINDER", + NT_STATUS_FLOPPY_WRONG_CYLINDER}, + {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR}, + {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS}, + {"NT_STATUS_DISK_RECALIBRATE_FAILED", + NT_STATUS_DISK_RECALIBRATE_FAILED}, + {"NT_STATUS_DISK_OPERATION_FAILED", + NT_STATUS_DISK_OPERATION_FAILED}, + {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED}, + {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY}, + {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING}, + {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE}, + {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH}, + {"NT_STATUS_DEVICE_NOT_PARTITIONED", + NT_STATUS_DEVICE_NOT_PARTITIONED}, + {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA}, + {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", + NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, + {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW}, + {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA}, + {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER}, + {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER}, + {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED}, + {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE}, + {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS}, + {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", + NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, + {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN}, + {"NT_STATUS_CHILD_MUST_BE_VOLATILE", + NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {"NT_STATUS_DEVICE_CONFIGURATION_ERROR", + NT_STATUS_DEVICE_CONFIGURATION_ERROR}, + {"NT_STATUS_DRIVER_INTERNAL_ERROR", + NT_STATUS_DRIVER_INTERNAL_ERROR}, + {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE}, + {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR}, + {"NT_STATUS_DEVICE_PROTOCOL_ERROR", + NT_STATUS_DEVICE_PROTOCOL_ERROR}, + {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER}, + {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL}, + {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE}, + {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET}, + {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT}, + {"NT_STATUS_TRUSTED_DOMAIN_FAILURE", + NT_STATUS_TRUSTED_DOMAIN_FAILURE}, + {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, + {"NT_STATUS_EVENTLOG_FILE_CORRUPT", + NT_STATUS_EVENTLOG_FILE_CORRUPT}, + {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START}, + {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE}, + {"NT_STATUS_MUTANT_LIMIT_EXCEEDED", + NT_STATUS_MUTANT_LIMIT_EXCEEDED}, + {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED}, + {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED}, + {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK}, + {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", + NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, + {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT}, + {"NT_STATUS_EVENTLOG_FILE_CHANGED", + NT_STATUS_EVENTLOG_FILE_CHANGED}, + {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, + {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, + {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, + {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT", + NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, + {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED}, + {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY}, + {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED}, + {"NT_STATUS_RESOURCE_LANG_NOT_FOUND", + NT_STATUS_RESOURCE_LANG_NOT_FOUND}, + {"NT_STATUS_INSUFF_SERVER_RESOURCES", + NT_STATUS_INSUFF_SERVER_RESOURCES}, + {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE}, + {"NT_STATUS_INVALID_ADDRESS_COMPONENT", + NT_STATUS_INVALID_ADDRESS_COMPONENT}, + {"NT_STATUS_INVALID_ADDRESS_WILDCARD", + NT_STATUS_INVALID_ADDRESS_WILDCARD}, + {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES}, + {"NT_STATUS_ADDRESS_ALREADY_EXISTS", + NT_STATUS_ADDRESS_ALREADY_EXISTS}, + {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED}, + {"NT_STATUS_CONNECTION_DISCONNECTED", + NT_STATUS_CONNECTION_DISCONNECTED}, + {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET}, + {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES}, + {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED}, + {"NT_STATUS_TRANSACTION_TIMED_OUT", + NT_STATUS_TRANSACTION_TIMED_OUT}, + {"NT_STATUS_TRANSACTION_NO_RELEASE", + NT_STATUS_TRANSACTION_NO_RELEASE}, + {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH}, + {"NT_STATUS_TRANSACTION_RESPONDED", + NT_STATUS_TRANSACTION_RESPONDED}, + {"NT_STATUS_TRANSACTION_INVALID_ID", + NT_STATUS_TRANSACTION_INVALID_ID}, + {"NT_STATUS_TRANSACTION_INVALID_TYPE", + NT_STATUS_TRANSACTION_INVALID_TYPE}, + {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION}, + {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION}, + {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", + NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, + {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED}, + {"NT_STATUS_SYSTEM_PROCESS_TERMINATED", + NT_STATUS_SYSTEM_PROCESS_TERMINATED}, + {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED}, + {"NT_STATUS_NO_BROWSER_SERVERS_FOUND", + NT_STATUS_NO_BROWSER_SERVERS_FOUND}, + {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR}, + {"NT_STATUS_DRIVER_CANCEL_TIMEOUT", + NT_STATUS_DRIVER_CANCEL_TIMEOUT}, + {"NT_STATUS_REPLY_MESSAGE_MISMATCH", + NT_STATUS_REPLY_MESSAGE_MISMATCH}, + {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT}, + {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH", + NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, + {"NT_STATUS_LOST_WRITEBEHIND_DATA", + NT_STATUS_LOST_WRITEBEHIND_DATA}, + {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", + NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, + {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE}, + {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND}, + {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM}, + {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE}, + {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ}, + {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK}, + {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID}, + {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS}, + {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE}, + {"NT_STATUS_RETRY", NT_STATUS_RETRY}, + {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE}, + {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET}, + {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND}, + {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW}, + {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT}, + {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, + {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT}, + {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE}, + {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED}, + {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT}, + {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", + NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, + {"NT_STATUS_ADDRESS_NOT_ASSOCIATED", + NT_STATUS_ADDRESS_NOT_ASSOCIATED}, + {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID}, + {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE}, + {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE}, + {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE}, + {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE}, + {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE}, + {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED}, + {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED}, + {"NT_STATUS_BAD_COMPRESSION_BUFFER", + NT_STATUS_BAD_COMPRESSION_BUFFER}, + {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE}, + {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED}, + {"NT_STATUS_TIMER_RESOLUTION_NOT_SET", + NT_STATUS_TIMER_RESOLUTION_NOT_SET}, + {"NT_STATUS_CONNECTION_COUNT_LIMIT", + NT_STATUS_CONNECTION_COUNT_LIMIT}, + {"NT_STATUS_LOGIN_TIME_RESTRICTION", + NT_STATUS_LOGIN_TIME_RESTRICTION}, + {"NT_STATUS_LOGIN_WKSTA_RESTRICTION", + NT_STATUS_LOGIN_WKSTA_RESTRICTION}, + {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH}, + {"NT_STATUS_INSUFFICIENT_LOGON_INFO", + NT_STATUS_INSUFFICIENT_LOGON_INFO}, + {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT}, + {"NT_STATUS_BAD_SERVICE_ENTRYPOINT", + NT_STATUS_BAD_SERVICE_ENTRYPOINT}, + {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST}, + {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1}, + {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2}, + {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT}, + {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED}, + {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE}, + {"NT_STATUS_LICENSE_QUOTA_EXCEEDED", + NT_STATUS_LICENSE_QUOTA_EXCEEDED}, + {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT}, + {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT}, + {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT}, + {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE}, + {"NT_STATUS_UNSUPPORTED_COMPRESSION", + NT_STATUS_UNSUPPORTED_COMPRESSION}, + {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE}, + {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", + NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, + {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", + NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, + {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", + NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, + {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED}, + {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS}, + {"NT_STATUS_QUOTA_LIST_INCONSISTENT", + NT_STATUS_QUOTA_LIST_INCONSISTENT}, + {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE}, + {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES}, + {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, + {"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED}, + {NULL, 0} +}; diff --git a/fs/smb/client/nterr.h b/fs/smb/client/nterr.h new file mode 100644 index 000000000000..edd4741cab0a --- /dev/null +++ b/fs/smb/client/nterr.h @@ -0,0 +1,551 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NT error code constants + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) John H Terpstra 1996-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Paul Ashton 1998-2000 + +*/ + + + +#ifndef _NTERR_H +#define _NTERR_H + +struct nt_err_code_struct { + char *nt_errstr; + __u32 nt_errcode; +}; + +extern const struct nt_err_code_struct nt_errs[]; + +/* Win32 Status codes. */ +#define NT_STATUS_MORE_ENTRIES 0x0105 +#define NT_ERROR_INVALID_PARAMETER 0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a +#define NT_STATUS_1804 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c + +/* + * Win32 Error codes extracted using a loop in smbclient then printing a netmon + * sniff to a file. + */ + +#define NT_STATUS_OK 0x0000 +#define NT_STATUS_SOME_UNMAPPED 0x0107 +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a +#define NT_STATUS_MEDIA_CHANGED 0x8000001c +#define NT_STATUS_END_OF_MEDIA 0x8000001e +#define NT_STATUS_MEDIA_CHECK 0x80000020 +#define NT_STATUS_NO_DATA_DETECTED 0x8000001c +#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d +#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 +#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 +#define NT_STATUS_UNSUCCESSFUL 0xC0000000 | 0x0001 +#define NT_STATUS_NOT_IMPLEMENTED 0xC0000000 | 0x0002 +#define NT_STATUS_INVALID_INFO_CLASS 0xC0000000 | 0x0003 +#define NT_STATUS_INFO_LENGTH_MISMATCH 0xC0000000 | 0x0004 +#define NT_STATUS_ACCESS_VIOLATION 0xC0000000 | 0x0005 +#define NT_STATUS_IN_PAGE_ERROR 0xC0000000 | 0x0006 +#define NT_STATUS_PAGEFILE_QUOTA 0xC0000000 | 0x0007 +#define NT_STATUS_INVALID_HANDLE 0xC0000000 | 0x0008 +#define NT_STATUS_BAD_INITIAL_STACK 0xC0000000 | 0x0009 +#define NT_STATUS_BAD_INITIAL_PC 0xC0000000 | 0x000a +#define NT_STATUS_INVALID_CID 0xC0000000 | 0x000b +#define NT_STATUS_TIMER_NOT_CANCELED 0xC0000000 | 0x000c +#define NT_STATUS_INVALID_PARAMETER 0xC0000000 | 0x000d +#define NT_STATUS_NO_SUCH_DEVICE 0xC0000000 | 0x000e +#define NT_STATUS_NO_SUCH_FILE 0xC0000000 | 0x000f +#define NT_STATUS_INVALID_DEVICE_REQUEST 0xC0000000 | 0x0010 +#define NT_STATUS_END_OF_FILE 0xC0000000 | 0x0011 +#define NT_STATUS_WRONG_VOLUME 0xC0000000 | 0x0012 +#define NT_STATUS_NO_MEDIA_IN_DEVICE 0xC0000000 | 0x0013 +#define NT_STATUS_UNRECOGNIZED_MEDIA 0xC0000000 | 0x0014 +#define NT_STATUS_NONEXISTENT_SECTOR 0xC0000000 | 0x0015 +#define NT_STATUS_MORE_PROCESSING_REQUIRED 0xC0000000 | 0x0016 +#define NT_STATUS_NO_MEMORY 0xC0000000 | 0x0017 +#define NT_STATUS_CONFLICTING_ADDRESSES 0xC0000000 | 0x0018 +#define NT_STATUS_NOT_MAPPED_VIEW 0xC0000000 | 0x0019 +#define NT_STATUS_UNABLE_TO_FREE_VM 0x80000000 | 0x001a +#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0xC0000000 | 0x001b +#define NT_STATUS_INVALID_SYSTEM_SERVICE 0xC0000000 | 0x001c +#define NT_STATUS_ILLEGAL_INSTRUCTION 0xC0000000 | 0x001d +#define NT_STATUS_INVALID_LOCK_SEQUENCE 0xC0000000 | 0x001e +#define NT_STATUS_INVALID_VIEW_SIZE 0xC0000000 | 0x001f +#define NT_STATUS_INVALID_FILE_FOR_SECTION 0xC0000000 | 0x0020 +#define NT_STATUS_ALREADY_COMMITTED 0xC0000000 | 0x0021 +#define NT_STATUS_ACCESS_DENIED 0xC0000000 | 0x0022 +#define NT_STATUS_BUFFER_TOO_SMALL 0xC0000000 | 0x0023 +#define NT_STATUS_OBJECT_TYPE_MISMATCH 0xC0000000 | 0x0024 +#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0xC0000000 | 0x0025 +#define NT_STATUS_INVALID_DISPOSITION 0xC0000000 | 0x0026 +#define NT_STATUS_UNWIND 0xC0000000 | 0x0027 +#define NT_STATUS_BAD_STACK 0xC0000000 | 0x0028 +#define NT_STATUS_INVALID_UNWIND_TARGET 0xC0000000 | 0x0029 +#define NT_STATUS_NOT_LOCKED 0xC0000000 | 0x002a +#define NT_STATUS_PARITY_ERROR 0xC0000000 | 0x002b +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0xC0000000 | 0x002c +#define NT_STATUS_NOT_COMMITTED 0xC0000000 | 0x002d +#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0xC0000000 | 0x002e +#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0xC0000000 | 0x002f +#define NT_STATUS_INVALID_PARAMETER_MIX 0xC0000000 | 0x0030 +#define NT_STATUS_INVALID_QUOTA_LOWER 0xC0000000 | 0x0031 +#define NT_STATUS_DISK_CORRUPT_ERROR 0xC0000000 | 0x0032 +#define NT_STATUS_OBJECT_NAME_INVALID 0xC0000000 | 0x0033 +#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000000 | 0x0034 +#define NT_STATUS_OBJECT_NAME_COLLISION 0xC0000000 | 0x0035 +#define NT_STATUS_HANDLE_NOT_WAITABLE 0xC0000000 | 0x0036 +#define NT_STATUS_PORT_DISCONNECTED 0xC0000000 | 0x0037 +#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0xC0000000 | 0x0038 +#define NT_STATUS_OBJECT_PATH_INVALID 0xC0000000 | 0x0039 +#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0xC0000000 | 0x003a +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0xC0000000 | 0x003b +#define NT_STATUS_DATA_OVERRUN 0xC0000000 | 0x003c +#define NT_STATUS_DATA_LATE_ERROR 0xC0000000 | 0x003d +#define NT_STATUS_DATA_ERROR 0xC0000000 | 0x003e +#define NT_STATUS_CRC_ERROR 0xC0000000 | 0x003f +#define NT_STATUS_SECTION_TOO_BIG 0xC0000000 | 0x0040 +#define NT_STATUS_PORT_CONNECTION_REFUSED 0xC0000000 | 0x0041 +#define NT_STATUS_INVALID_PORT_HANDLE 0xC0000000 | 0x0042 +#define NT_STATUS_SHARING_VIOLATION 0xC0000000 | 0x0043 +#define NT_STATUS_QUOTA_EXCEEDED 0xC0000000 | 0x0044 +#define NT_STATUS_INVALID_PAGE_PROTECTION 0xC0000000 | 0x0045 +#define NT_STATUS_MUTANT_NOT_OWNED 0xC0000000 | 0x0046 +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0xC0000000 | 0x0047 +#define NT_STATUS_PORT_ALREADY_SET 0xC0000000 | 0x0048 +#define NT_STATUS_SECTION_NOT_IMAGE 0xC0000000 | 0x0049 +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0xC0000000 | 0x004a +#define NT_STATUS_THREAD_IS_TERMINATING 0xC0000000 | 0x004b +#define NT_STATUS_BAD_WORKING_SET_LIMIT 0xC0000000 | 0x004c +#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0xC0000000 | 0x004d +#define NT_STATUS_SECTION_PROTECTION 0xC0000000 | 0x004e +#define NT_STATUS_EAS_NOT_SUPPORTED 0xC0000000 | 0x004f +#define NT_STATUS_EA_TOO_LARGE 0xC0000000 | 0x0050 +#define NT_STATUS_NONEXISTENT_EA_ENTRY 0xC0000000 | 0x0051 +#define NT_STATUS_NO_EAS_ON_FILE 0xC0000000 | 0x0052 +#define NT_STATUS_EA_CORRUPT_ERROR 0xC0000000 | 0x0053 +#define NT_STATUS_FILE_LOCK_CONFLICT 0xC0000000 | 0x0054 +#define NT_STATUS_LOCK_NOT_GRANTED 0xC0000000 | 0x0055 +#define NT_STATUS_DELETE_PENDING 0xC0000000 | 0x0056 +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0xC0000000 | 0x0057 +#define NT_STATUS_UNKNOWN_REVISION 0xC0000000 | 0x0058 +#define NT_STATUS_REVISION_MISMATCH 0xC0000000 | 0x0059 +#define NT_STATUS_INVALID_OWNER 0xC0000000 | 0x005a +#define NT_STATUS_INVALID_PRIMARY_GROUP 0xC0000000 | 0x005b +#define NT_STATUS_NO_IMPERSONATION_TOKEN 0xC0000000 | 0x005c +#define NT_STATUS_CANT_DISABLE_MANDATORY 0xC0000000 | 0x005d +#define NT_STATUS_NO_LOGON_SERVERS 0xC0000000 | 0x005e +#define NT_STATUS_NO_SUCH_LOGON_SESSION 0xC0000000 | 0x005f +#define NT_STATUS_NO_SUCH_PRIVILEGE 0xC0000000 | 0x0060 +#define NT_STATUS_PRIVILEGE_NOT_HELD 0xC0000000 | 0x0061 +#define NT_STATUS_INVALID_ACCOUNT_NAME 0xC0000000 | 0x0062 +#define NT_STATUS_USER_EXISTS 0xC0000000 | 0x0063 +#define NT_STATUS_NO_SUCH_USER 0xC0000000 | 0x0064 +#define NT_STATUS_GROUP_EXISTS 0xC0000000 | 0x0065 +#define NT_STATUS_NO_SUCH_GROUP 0xC0000000 | 0x0066 +#define NT_STATUS_MEMBER_IN_GROUP 0xC0000000 | 0x0067 +#define NT_STATUS_MEMBER_NOT_IN_GROUP 0xC0000000 | 0x0068 +#define NT_STATUS_LAST_ADMIN 0xC0000000 | 0x0069 +#define NT_STATUS_WRONG_PASSWORD 0xC0000000 | 0x006a +#define NT_STATUS_ILL_FORMED_PASSWORD 0xC0000000 | 0x006b +#define NT_STATUS_PASSWORD_RESTRICTION 0xC0000000 | 0x006c +#define NT_STATUS_LOGON_FAILURE 0xC0000000 | 0x006d +#define NT_STATUS_ACCOUNT_RESTRICTION 0xC0000000 | 0x006e +#define NT_STATUS_INVALID_LOGON_HOURS 0xC0000000 | 0x006f +#define NT_STATUS_INVALID_WORKSTATION 0xC0000000 | 0x0070 +#define NT_STATUS_PASSWORD_EXPIRED 0xC0000000 | 0x0071 +#define NT_STATUS_ACCOUNT_DISABLED 0xC0000000 | 0x0072 +#define NT_STATUS_NONE_MAPPED 0xC0000000 | 0x0073 +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0xC0000000 | 0x0074 +#define NT_STATUS_LUIDS_EXHAUSTED 0xC0000000 | 0x0075 +#define NT_STATUS_INVALID_SUB_AUTHORITY 0xC0000000 | 0x0076 +#define NT_STATUS_INVALID_ACL 0xC0000000 | 0x0077 +#define NT_STATUS_INVALID_SID 0xC0000000 | 0x0078 +#define NT_STATUS_INVALID_SECURITY_DESCR 0xC0000000 | 0x0079 +#define NT_STATUS_PROCEDURE_NOT_FOUND 0xC0000000 | 0x007a +#define NT_STATUS_INVALID_IMAGE_FORMAT 0xC0000000 | 0x007b +#define NT_STATUS_NO_TOKEN 0xC0000000 | 0x007c +#define NT_STATUS_BAD_INHERITANCE_ACL 0xC0000000 | 0x007d +#define NT_STATUS_RANGE_NOT_LOCKED 0xC0000000 | 0x007e +#define NT_STATUS_DISK_FULL 0xC0000000 | 0x007f +#define NT_STATUS_SERVER_DISABLED 0xC0000000 | 0x0080 +#define NT_STATUS_SERVER_NOT_DISABLED 0xC0000000 | 0x0081 +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0xC0000000 | 0x0082 +#define NT_STATUS_GUIDS_EXHAUSTED 0xC0000000 | 0x0083 +#define NT_STATUS_INVALID_ID_AUTHORITY 0xC0000000 | 0x0084 +#define NT_STATUS_AGENTS_EXHAUSTED 0xC0000000 | 0x0085 +#define NT_STATUS_INVALID_VOLUME_LABEL 0xC0000000 | 0x0086 +#define NT_STATUS_SECTION_NOT_EXTENDED 0xC0000000 | 0x0087 +#define NT_STATUS_NOT_MAPPED_DATA 0xC0000000 | 0x0088 +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0xC0000000 | 0x0089 +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0xC0000000 | 0x008a +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0xC0000000 | 0x008b +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0xC0000000 | 0x008c +#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0xC0000000 | 0x008d +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0xC0000000 | 0x008e +#define NT_STATUS_FLOAT_INEXACT_RESULT 0xC0000000 | 0x008f +#define NT_STATUS_FLOAT_INVALID_OPERATION 0xC0000000 | 0x0090 +#define NT_STATUS_FLOAT_OVERFLOW 0xC0000000 | 0x0091 +#define NT_STATUS_FLOAT_STACK_CHECK 0xC0000000 | 0x0092 +#define NT_STATUS_FLOAT_UNDERFLOW 0xC0000000 | 0x0093 +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000000 | 0x0094 +#define NT_STATUS_INTEGER_OVERFLOW 0xC0000000 | 0x0095 +#define NT_STATUS_PRIVILEGED_INSTRUCTION 0xC0000000 | 0x0096 +#define NT_STATUS_TOO_MANY_PAGING_FILES 0xC0000000 | 0x0097 +#define NT_STATUS_FILE_INVALID 0xC0000000 | 0x0098 +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0xC0000000 | 0x0099 +#define NT_STATUS_INSUFFICIENT_RESOURCES 0xC0000000 | 0x009a +#define NT_STATUS_DFS_EXIT_PATH_FOUND 0xC0000000 | 0x009b +#define NT_STATUS_DEVICE_DATA_ERROR 0xC0000000 | 0x009c +#define NT_STATUS_DEVICE_NOT_CONNECTED 0xC0000000 | 0x009d +#define NT_STATUS_DEVICE_POWER_FAILURE 0xC0000000 | 0x009e +#define NT_STATUS_FREE_VM_NOT_AT_BASE 0xC0000000 | 0x009f +#define NT_STATUS_MEMORY_NOT_ALLOCATED 0xC0000000 | 0x00a0 +#define NT_STATUS_WORKING_SET_QUOTA 0xC0000000 | 0x00a1 +#define NT_STATUS_MEDIA_WRITE_PROTECTED 0xC0000000 | 0x00a2 +#define NT_STATUS_DEVICE_NOT_READY 0xC0000000 | 0x00a3 +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0xC0000000 | 0x00a4 +#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0xC0000000 | 0x00a5 +#define NT_STATUS_CANT_OPEN_ANONYMOUS 0xC0000000 | 0x00a6 +#define NT_STATUS_BAD_VALIDATION_CLASS 0xC0000000 | 0x00a7 +#define NT_STATUS_BAD_TOKEN_TYPE 0xC0000000 | 0x00a8 +#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0xC0000000 | 0x00a9 +#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0xC0000000 | 0x00aa +#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0xC0000000 | 0x00ab +#define NT_STATUS_PIPE_NOT_AVAILABLE 0xC0000000 | 0x00ac +#define NT_STATUS_INVALID_PIPE_STATE 0xC0000000 | 0x00ad +#define NT_STATUS_PIPE_BUSY 0xC0000000 | 0x00ae +#define NT_STATUS_ILLEGAL_FUNCTION 0xC0000000 | 0x00af +#define NT_STATUS_PIPE_DISCONNECTED 0xC0000000 | 0x00b0 +#define NT_STATUS_PIPE_CLOSING 0xC0000000 | 0x00b1 +#define NT_STATUS_PIPE_CONNECTED 0xC0000000 | 0x00b2 +#define NT_STATUS_PIPE_LISTENING 0xC0000000 | 0x00b3 +#define NT_STATUS_INVALID_READ_MODE 0xC0000000 | 0x00b4 +#define NT_STATUS_IO_TIMEOUT 0xC0000000 | 0x00b5 +#define NT_STATUS_FILE_FORCED_CLOSED 0xC0000000 | 0x00b6 +#define NT_STATUS_PROFILING_NOT_STARTED 0xC0000000 | 0x00b7 +#define NT_STATUS_PROFILING_NOT_STOPPED 0xC0000000 | 0x00b8 +#define NT_STATUS_COULD_NOT_INTERPRET 0xC0000000 | 0x00b9 +#define NT_STATUS_FILE_IS_A_DIRECTORY 0xC0000000 | 0x00ba +#define NT_STATUS_NOT_SUPPORTED 0xC0000000 | 0x00bb +#define NT_STATUS_REMOTE_NOT_LISTENING 0xC0000000 | 0x00bc +#define NT_STATUS_DUPLICATE_NAME 0xC0000000 | 0x00bd +#define NT_STATUS_BAD_NETWORK_PATH 0xC0000000 | 0x00be +#define NT_STATUS_NETWORK_BUSY 0xC0000000 | 0x00bf +#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0xC0000000 | 0x00c0 +#define NT_STATUS_TOO_MANY_COMMANDS 0xC0000000 | 0x00c1 +#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0xC0000000 | 0x00c2 +#define NT_STATUS_INVALID_NETWORK_RESPONSE 0xC0000000 | 0x00c3 +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0xC0000000 | 0x00c4 +#define NT_STATUS_BAD_REMOTE_ADAPTER 0xC0000000 | 0x00c5 +#define NT_STATUS_PRINT_QUEUE_FULL 0xC0000000 | 0x00c6 +#define NT_STATUS_NO_SPOOL_SPACE 0xC0000000 | 0x00c7 +#define NT_STATUS_PRINT_CANCELLED 0xC0000000 | 0x00c8 +#define NT_STATUS_NETWORK_NAME_DELETED 0xC0000000 | 0x00c9 +#define NT_STATUS_NETWORK_ACCESS_DENIED 0xC0000000 | 0x00ca +#define NT_STATUS_BAD_DEVICE_TYPE 0xC0000000 | 0x00cb +#define NT_STATUS_BAD_NETWORK_NAME 0xC0000000 | 0x00cc +#define NT_STATUS_TOO_MANY_NAMES 0xC0000000 | 0x00cd +#define NT_STATUS_TOO_MANY_SESSIONS 0xC0000000 | 0x00ce +#define NT_STATUS_SHARING_PAUSED 0xC0000000 | 0x00cf +#define NT_STATUS_REQUEST_NOT_ACCEPTED 0xC0000000 | 0x00d0 +#define NT_STATUS_REDIRECTOR_PAUSED 0xC0000000 | 0x00d1 +#define NT_STATUS_NET_WRITE_FAULT 0xC0000000 | 0x00d2 +#define NT_STATUS_PROFILING_AT_LIMIT 0xC0000000 | 0x00d3 +#define NT_STATUS_NOT_SAME_DEVICE 0xC0000000 | 0x00d4 +#define NT_STATUS_FILE_RENAMED 0xC0000000 | 0x00d5 +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0xC0000000 | 0x00d6 +#define NT_STATUS_NO_SECURITY_ON_OBJECT 0xC0000000 | 0x00d7 +#define NT_STATUS_CANT_WAIT 0xC0000000 | 0x00d8 +#define NT_STATUS_PIPE_EMPTY 0xC0000000 | 0x00d9 +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0xC0000000 | 0x00da +#define NT_STATUS_CANT_TERMINATE_SELF 0xC0000000 | 0x00db +#define NT_STATUS_INVALID_SERVER_STATE 0xC0000000 | 0x00dc +#define NT_STATUS_INVALID_DOMAIN_STATE 0xC0000000 | 0x00dd +#define NT_STATUS_INVALID_DOMAIN_ROLE 0xC0000000 | 0x00de +#define NT_STATUS_NO_SUCH_DOMAIN 0xC0000000 | 0x00df +#define NT_STATUS_DOMAIN_EXISTS 0xC0000000 | 0x00e0 +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0xC0000000 | 0x00e1 +#define NT_STATUS_OPLOCK_NOT_GRANTED 0xC0000000 | 0x00e2 +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0xC0000000 | 0x00e3 +#define NT_STATUS_INTERNAL_DB_CORRUPTION 0xC0000000 | 0x00e4 +#define NT_STATUS_INTERNAL_ERROR 0xC0000000 | 0x00e5 +#define NT_STATUS_GENERIC_NOT_MAPPED 0xC0000000 | 0x00e6 +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0xC0000000 | 0x00e7 +#define NT_STATUS_INVALID_USER_BUFFER 0xC0000000 | 0x00e8 +#define NT_STATUS_UNEXPECTED_IO_ERROR 0xC0000000 | 0x00e9 +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0xC0000000 | 0x00ea +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0xC0000000 | 0x00eb +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0xC0000000 | 0x00ec +#define NT_STATUS_NOT_LOGON_PROCESS 0xC0000000 | 0x00ed +#define NT_STATUS_LOGON_SESSION_EXISTS 0xC0000000 | 0x00ee +#define NT_STATUS_INVALID_PARAMETER_1 0xC0000000 | 0x00ef +#define NT_STATUS_INVALID_PARAMETER_2 0xC0000000 | 0x00f0 +#define NT_STATUS_INVALID_PARAMETER_3 0xC0000000 | 0x00f1 +#define NT_STATUS_INVALID_PARAMETER_4 0xC0000000 | 0x00f2 +#define NT_STATUS_INVALID_PARAMETER_5 0xC0000000 | 0x00f3 +#define NT_STATUS_INVALID_PARAMETER_6 0xC0000000 | 0x00f4 +#define NT_STATUS_INVALID_PARAMETER_7 0xC0000000 | 0x00f5 +#define NT_STATUS_INVALID_PARAMETER_8 0xC0000000 | 0x00f6 +#define NT_STATUS_INVALID_PARAMETER_9 0xC0000000 | 0x00f7 +#define NT_STATUS_INVALID_PARAMETER_10 0xC0000000 | 0x00f8 +#define NT_STATUS_INVALID_PARAMETER_11 0xC0000000 | 0x00f9 +#define NT_STATUS_INVALID_PARAMETER_12 0xC0000000 | 0x00fa +#define NT_STATUS_REDIRECTOR_NOT_STARTED 0xC0000000 | 0x00fb +#define NT_STATUS_REDIRECTOR_STARTED 0xC0000000 | 0x00fc +#define NT_STATUS_STACK_OVERFLOW 0xC0000000 | 0x00fd +#define NT_STATUS_NO_SUCH_PACKAGE 0xC0000000 | 0x00fe +#define NT_STATUS_BAD_FUNCTION_TABLE 0xC0000000 | 0x00ff +#define NT_STATUS_DIRECTORY_NOT_EMPTY 0xC0000000 | 0x0101 +#define NT_STATUS_FILE_CORRUPT_ERROR 0xC0000000 | 0x0102 +#define NT_STATUS_NOT_A_DIRECTORY 0xC0000000 | 0x0103 +#define NT_STATUS_BAD_LOGON_SESSION_STATE 0xC0000000 | 0x0104 +#define NT_STATUS_LOGON_SESSION_COLLISION 0xC0000000 | 0x0105 +#define NT_STATUS_NAME_TOO_LONG 0xC0000000 | 0x0106 +#define NT_STATUS_FILES_OPEN 0xC0000000 | 0x0107 +#define NT_STATUS_CONNECTION_IN_USE 0xC0000000 | 0x0108 +#define NT_STATUS_MESSAGE_NOT_FOUND 0xC0000000 | 0x0109 +#define NT_STATUS_PROCESS_IS_TERMINATING 0xC0000000 | 0x010a +#define NT_STATUS_INVALID_LOGON_TYPE 0xC0000000 | 0x010b +#define NT_STATUS_NO_GUID_TRANSLATION 0xC0000000 | 0x010c +#define NT_STATUS_CANNOT_IMPERSONATE 0xC0000000 | 0x010d +#define NT_STATUS_IMAGE_ALREADY_LOADED 0xC0000000 | 0x010e +#define NT_STATUS_ABIOS_NOT_PRESENT 0xC0000000 | 0x010f +#define NT_STATUS_ABIOS_LID_NOT_EXIST 0xC0000000 | 0x0110 +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0xC0000000 | 0x0111 +#define NT_STATUS_ABIOS_NOT_LID_OWNER 0xC0000000 | 0x0112 +#define NT_STATUS_ABIOS_INVALID_COMMAND 0xC0000000 | 0x0113 +#define NT_STATUS_ABIOS_INVALID_LID 0xC0000000 | 0x0114 +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0xC0000000 | 0x0115 +#define NT_STATUS_ABIOS_INVALID_SELECTOR 0xC0000000 | 0x0116 +#define NT_STATUS_NO_LDT 0xC0000000 | 0x0117 +#define NT_STATUS_INVALID_LDT_SIZE 0xC0000000 | 0x0118 +#define NT_STATUS_INVALID_LDT_OFFSET 0xC0000000 | 0x0119 +#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0xC0000000 | 0x011a +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0xC0000000 | 0x011b +#define NT_STATUS_RXACT_INVALID_STATE 0xC0000000 | 0x011c +#define NT_STATUS_RXACT_COMMIT_FAILURE 0xC0000000 | 0x011d +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0xC0000000 | 0x011e +#define NT_STATUS_TOO_MANY_OPENED_FILES 0xC0000000 | 0x011f +#define NT_STATUS_CANCELLED 0xC0000000 | 0x0120 +#define NT_STATUS_CANNOT_DELETE 0xC0000000 | 0x0121 +#define NT_STATUS_INVALID_COMPUTER_NAME 0xC0000000 | 0x0122 +#define NT_STATUS_FILE_DELETED 0xC0000000 | 0x0123 +#define NT_STATUS_SPECIAL_ACCOUNT 0xC0000000 | 0x0124 +#define NT_STATUS_SPECIAL_GROUP 0xC0000000 | 0x0125 +#define NT_STATUS_SPECIAL_USER 0xC0000000 | 0x0126 +#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0xC0000000 | 0x0127 +#define NT_STATUS_FILE_CLOSED 0xC0000000 | 0x0128 +#define NT_STATUS_TOO_MANY_THREADS 0xC0000000 | 0x0129 +#define NT_STATUS_THREAD_NOT_IN_PROCESS 0xC0000000 | 0x012a +#define NT_STATUS_TOKEN_ALREADY_IN_USE 0xC0000000 | 0x012b +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0xC0000000 | 0x012c +#define NT_STATUS_COMMITMENT_LIMIT 0xC0000000 | 0x012d +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0xC0000000 | 0x012e +#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0xC0000000 | 0x012f +#define NT_STATUS_INVALID_IMAGE_PROTECT 0xC0000000 | 0x0130 +#define NT_STATUS_INVALID_IMAGE_WIN_16 0xC0000000 | 0x0131 +#define NT_STATUS_LOGON_SERVER_CONFLICT 0xC0000000 | 0x0132 +#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0xC0000000 | 0x0133 +#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0xC0000000 | 0x0134 +#define NT_STATUS_DLL_NOT_FOUND 0xC0000000 | 0x0135 +#define NT_STATUS_OPEN_FAILED 0xC0000000 | 0x0136 +#define NT_STATUS_IO_PRIVILEGE_FAILED 0xC0000000 | 0x0137 +#define NT_STATUS_ORDINAL_NOT_FOUND 0xC0000000 | 0x0138 +#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0139 +#define NT_STATUS_CONTROL_C_EXIT 0xC0000000 | 0x013a +#define NT_STATUS_LOCAL_DISCONNECT 0xC0000000 | 0x013b +#define NT_STATUS_REMOTE_DISCONNECT 0xC0000000 | 0x013c +#define NT_STATUS_REMOTE_RESOURCES 0xC0000000 | 0x013d +#define NT_STATUS_LINK_FAILED 0xC0000000 | 0x013e +#define NT_STATUS_LINK_TIMEOUT 0xC0000000 | 0x013f +#define NT_STATUS_INVALID_CONNECTION 0xC0000000 | 0x0140 +#define NT_STATUS_INVALID_ADDRESS 0xC0000000 | 0x0141 +#define NT_STATUS_DLL_INIT_FAILED 0xC0000000 | 0x0142 +#define NT_STATUS_MISSING_SYSTEMFILE 0xC0000000 | 0x0143 +#define NT_STATUS_UNHANDLED_EXCEPTION 0xC0000000 | 0x0144 +#define NT_STATUS_APP_INIT_FAILURE 0xC0000000 | 0x0145 +#define NT_STATUS_PAGEFILE_CREATE_FAILED 0xC0000000 | 0x0146 +#define NT_STATUS_NO_PAGEFILE 0xC0000000 | 0x0147 +#define NT_STATUS_INVALID_LEVEL 0xC0000000 | 0x0148 +#define NT_STATUS_WRONG_PASSWORD_CORE 0xC0000000 | 0x0149 +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0xC0000000 | 0x014a +#define NT_STATUS_PIPE_BROKEN 0xC0000000 | 0x014b +#define NT_STATUS_REGISTRY_CORRUPT 0xC0000000 | 0x014c +#define NT_STATUS_REGISTRY_IO_FAILED 0xC0000000 | 0x014d +#define NT_STATUS_NO_EVENT_PAIR 0xC0000000 | 0x014e +#define NT_STATUS_UNRECOGNIZED_VOLUME 0xC0000000 | 0x014f +#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0xC0000000 | 0x0150 +#define NT_STATUS_NO_SUCH_ALIAS 0xC0000000 | 0x0151 +#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0xC0000000 | 0x0152 +#define NT_STATUS_MEMBER_IN_ALIAS 0xC0000000 | 0x0153 +#define NT_STATUS_ALIAS_EXISTS 0xC0000000 | 0x0154 +#define NT_STATUS_LOGON_NOT_GRANTED 0xC0000000 | 0x0155 +#define NT_STATUS_TOO_MANY_SECRETS 0xC0000000 | 0x0156 +#define NT_STATUS_SECRET_TOO_LONG 0xC0000000 | 0x0157 +#define NT_STATUS_INTERNAL_DB_ERROR 0xC0000000 | 0x0158 +#define NT_STATUS_FULLSCREEN_MODE 0xC0000000 | 0x0159 +#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0xC0000000 | 0x015a +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0xC0000000 | 0x015b +#define NT_STATUS_NOT_REGISTRY_FILE 0xC0000000 | 0x015c +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x015d +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0xC0000000 | 0x015e +#define NT_STATUS_FT_MISSING_MEMBER 0xC0000000 | 0x015f +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0xC0000000 | 0x0160 +#define NT_STATUS_ILLEGAL_CHARACTER 0xC0000000 | 0x0161 +#define NT_STATUS_UNMAPPABLE_CHARACTER 0xC0000000 | 0x0162 +#define NT_STATUS_UNDEFINED_CHARACTER 0xC0000000 | 0x0163 +#define NT_STATUS_FLOPPY_VOLUME 0xC0000000 | 0x0164 +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0xC0000000 | 0x0165 +#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0xC0000000 | 0x0166 +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0xC0000000 | 0x0167 +#define NT_STATUS_FLOPPY_BAD_REGISTERS 0xC0000000 | 0x0168 +#define NT_STATUS_DISK_RECALIBRATE_FAILED 0xC0000000 | 0x0169 +#define NT_STATUS_DISK_OPERATION_FAILED 0xC0000000 | 0x016a +#define NT_STATUS_DISK_RESET_FAILED 0xC0000000 | 0x016b +#define NT_STATUS_SHARED_IRQ_BUSY 0xC0000000 | 0x016c +#define NT_STATUS_FT_ORPHANING 0xC0000000 | 0x016d +#define NT_STATUS_PARTITION_FAILURE 0xC0000000 | 0x0172 +#define NT_STATUS_INVALID_BLOCK_LENGTH 0xC0000000 | 0x0173 +#define NT_STATUS_DEVICE_NOT_PARTITIONED 0xC0000000 | 0x0174 +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0xC0000000 | 0x0175 +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0xC0000000 | 0x0176 +#define NT_STATUS_EOM_OVERFLOW 0xC0000000 | 0x0177 +#define NT_STATUS_NO_MEDIA 0xC0000000 | 0x0178 +#define NT_STATUS_NO_SUCH_MEMBER 0xC0000000 | 0x017a +#define NT_STATUS_INVALID_MEMBER 0xC0000000 | 0x017b +#define NT_STATUS_KEY_DELETED 0xC0000000 | 0x017c +#define NT_STATUS_NO_LOG_SPACE 0xC0000000 | 0x017d +#define NT_STATUS_TOO_MANY_SIDS 0xC0000000 | 0x017e +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x017f +#define NT_STATUS_KEY_HAS_CHILDREN 0xC0000000 | 0x0180 +#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0xC0000000 | 0x0181 +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0xC0000000 | 0x0182 +#define NT_STATUS_DRIVER_INTERNAL_ERROR 0xC0000000 | 0x0183 +#define NT_STATUS_INVALID_DEVICE_STATE 0xC0000000 | 0x0184 +#define NT_STATUS_IO_DEVICE_ERROR 0xC0000000 | 0x0185 +#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0xC0000000 | 0x0186 +#define NT_STATUS_BACKUP_CONTROLLER 0xC0000000 | 0x0187 +#define NT_STATUS_LOG_FILE_FULL 0xC0000000 | 0x0188 +#define NT_STATUS_TOO_LATE 0xC0000000 | 0x0189 +#define NT_STATUS_NO_TRUST_LSA_SECRET 0xC0000000 | 0x018a +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0xC0000000 | 0x018b +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0xC0000000 | 0x018c +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0xC0000000 | 0x018d +#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0xC0000000 | 0x018e +#define NT_STATUS_EVENTLOG_CANT_START 0xC0000000 | 0x018f +#define NT_STATUS_TRUST_FAILURE 0xC0000000 | 0x0190 +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0xC0000000 | 0x0191 +#define NT_STATUS_NETLOGON_NOT_STARTED 0xC0000000 | 0x0192 +#define NT_STATUS_ACCOUNT_EXPIRED 0xC0000000 | 0x0193 +#define NT_STATUS_POSSIBLE_DEADLOCK 0xC0000000 | 0x0194 +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0xC0000000 | 0x0195 +#define NT_STATUS_REMOTE_SESSION_LIMIT 0xC0000000 | 0x0196 +#define NT_STATUS_EVENTLOG_FILE_CHANGED 0xC0000000 | 0x0197 +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0xC0000000 | 0x0198 +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0xC0000000 | 0x0199 +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0xC0000000 | 0x019a +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0xC0000000 | 0x019b +#define NT_STATUS_FS_DRIVER_REQUIRED 0xC0000000 | 0x019c +#define NT_STATUS_NO_USER_SESSION_KEY 0xC0000000 | 0x0202 +#define NT_STATUS_USER_SESSION_DELETED 0xC0000000 | 0x0203 +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0xC0000000 | 0x0204 +#define NT_STATUS_INSUFF_SERVER_RESOURCES 0xC0000000 | 0x0205 +#define NT_STATUS_INVALID_BUFFER_SIZE 0xC0000000 | 0x0206 +#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0xC0000000 | 0x0207 +#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0xC0000000 | 0x0208 +#define NT_STATUS_TOO_MANY_ADDRESSES 0xC0000000 | 0x0209 +#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0xC0000000 | 0x020a +#define NT_STATUS_ADDRESS_CLOSED 0xC0000000 | 0x020b +#define NT_STATUS_CONNECTION_DISCONNECTED 0xC0000000 | 0x020c +#define NT_STATUS_CONNECTION_RESET 0xC0000000 | 0x020d +#define NT_STATUS_TOO_MANY_NODES 0xC0000000 | 0x020e +#define NT_STATUS_TRANSACTION_ABORTED 0xC0000000 | 0x020f +#define NT_STATUS_TRANSACTION_TIMED_OUT 0xC0000000 | 0x0210 +#define NT_STATUS_TRANSACTION_NO_RELEASE 0xC0000000 | 0x0211 +#define NT_STATUS_TRANSACTION_NO_MATCH 0xC0000000 | 0x0212 +#define NT_STATUS_TRANSACTION_RESPONDED 0xC0000000 | 0x0213 +#define NT_STATUS_TRANSACTION_INVALID_ID 0xC0000000 | 0x0214 +#define NT_STATUS_TRANSACTION_INVALID_TYPE 0xC0000000 | 0x0215 +#define NT_STATUS_NOT_SERVER_SESSION 0xC0000000 | 0x0216 +#define NT_STATUS_NOT_CLIENT_SESSION 0xC0000000 | 0x0217 +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0xC0000000 | 0x0218 +#define NT_STATUS_DEBUG_ATTACH_FAILED 0xC0000000 | 0x0219 +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0xC0000000 | 0x021a +#define NT_STATUS_DATA_NOT_ACCEPTED 0xC0000000 | 0x021b +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0xC0000000 | 0x021c +#define NT_STATUS_VDM_HARD_ERROR 0xC0000000 | 0x021d +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0xC0000000 | 0x021e +#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0xC0000000 | 0x021f +#define NT_STATUS_MAPPED_ALIGNMENT 0xC0000000 | 0x0220 +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0xC0000000 | 0x0221 +#define NT_STATUS_LOST_WRITEBEHIND_DATA 0xC0000000 | 0x0222 +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0xC0000000 | 0x0223 +#define NT_STATUS_PASSWORD_MUST_CHANGE 0xC0000000 | 0x0224 +#define NT_STATUS_NOT_FOUND 0xC0000000 | 0x0225 +#define NT_STATUS_NOT_TINY_STREAM 0xC0000000 | 0x0226 +#define NT_STATUS_RECOVERY_FAILURE 0xC0000000 | 0x0227 +#define NT_STATUS_STACK_OVERFLOW_READ 0xC0000000 | 0x0228 +#define NT_STATUS_FAIL_CHECK 0xC0000000 | 0x0229 +#define NT_STATUS_DUPLICATE_OBJECTID 0xC0000000 | 0x022a +#define NT_STATUS_OBJECTID_EXISTS 0xC0000000 | 0x022b +#define NT_STATUS_CONVERT_TO_LARGE 0xC0000000 | 0x022c +#define NT_STATUS_RETRY 0xC0000000 | 0x022d +#define NT_STATUS_FOUND_OUT_OF_SCOPE 0xC0000000 | 0x022e +#define NT_STATUS_ALLOCATE_BUCKET 0xC0000000 | 0x022f +#define NT_STATUS_PROPSET_NOT_FOUND 0xC0000000 | 0x0230 +#define NT_STATUS_MARSHALL_OVERFLOW 0xC0000000 | 0x0231 +#define NT_STATUS_INVALID_VARIANT 0xC0000000 | 0x0232 +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0xC0000000 | 0x0233 +#define NT_STATUS_ACCOUNT_LOCKED_OUT 0xC0000000 | 0x0234 +#define NT_STATUS_HANDLE_NOT_CLOSABLE 0xC0000000 | 0x0235 +#define NT_STATUS_CONNECTION_REFUSED 0xC0000000 | 0x0236 +#define NT_STATUS_GRACEFUL_DISCONNECT 0xC0000000 | 0x0237 +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0xC0000000 | 0x0238 +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0xC0000000 | 0x0239 +#define NT_STATUS_CONNECTION_INVALID 0xC0000000 | 0x023a +#define NT_STATUS_CONNECTION_ACTIVE 0xC0000000 | 0x023b +#define NT_STATUS_NETWORK_UNREACHABLE 0xC0000000 | 0x023c +#define NT_STATUS_HOST_UNREACHABLE 0xC0000000 | 0x023d +#define NT_STATUS_PROTOCOL_UNREACHABLE 0xC0000000 | 0x023e +#define NT_STATUS_PORT_UNREACHABLE 0xC0000000 | 0x023f +#define NT_STATUS_REQUEST_ABORTED 0xC0000000 | 0x0240 +#define NT_STATUS_CONNECTION_ABORTED 0xC0000000 | 0x0241 +#define NT_STATUS_BAD_COMPRESSION_BUFFER 0xC0000000 | 0x0242 +#define NT_STATUS_USER_MAPPED_FILE 0xC0000000 | 0x0243 +#define NT_STATUS_AUDIT_FAILED 0xC0000000 | 0x0244 +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000000 | 0x0245 +#define NT_STATUS_CONNECTION_COUNT_LIMIT 0xC0000000 | 0x0246 +#define NT_STATUS_LOGIN_TIME_RESTRICTION 0xC0000000 | 0x0247 +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0xC0000000 | 0x0248 +#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0xC0000000 | 0x0249 +#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0xC0000000 | 0x0250 +#define NT_STATUS_BAD_DLL_ENTRYPOINT 0xC0000000 | 0x0251 +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0xC0000000 | 0x0252 +#define NT_STATUS_LPC_REPLY_LOST 0xC0000000 | 0x0253 +#define NT_STATUS_IP_ADDRESS_CONFLICT1 0xC0000000 | 0x0254 +#define NT_STATUS_IP_ADDRESS_CONFLICT2 0xC0000000 | 0x0255 +#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0xC0000000 | 0x0256 +#define NT_STATUS_PATH_NOT_COVERED 0xC0000000 | 0x0257 +#define NT_STATUS_NO_CALLBACK_ACTIVE 0xC0000000 | 0x0258 +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0xC0000000 | 0x0259 +#define NT_STATUS_PWD_TOO_SHORT 0xC0000000 | 0x025a +#define NT_STATUS_PWD_TOO_RECENT 0xC0000000 | 0x025b +#define NT_STATUS_PWD_HISTORY_CONFLICT 0xC0000000 | 0x025c +#define NT_STATUS_PLUGPLAY_NO_DEVICE 0xC0000000 | 0x025e +#define NT_STATUS_UNSUPPORTED_COMPRESSION 0xC0000000 | 0x025f +#define NT_STATUS_INVALID_HW_PROFILE 0xC0000000 | 0x0260 +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0xC0000000 | 0x0261 +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0xC0000000 | 0x0262 +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0263 +#define NT_STATUS_RESOURCE_NOT_OWNED 0xC0000000 | 0x0264 +#define NT_STATUS_TOO_MANY_LINKS 0xC0000000 | 0x0265 +#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0xC0000000 | 0x0266 +#define NT_STATUS_FILE_IS_OFFLINE 0xC0000000 | 0x0267 +#define NT_STATUS_NO_SUCH_JOB 0xC0000000 | 0xEDE /* scheduler */ + +#endif /* _NTERR_H */ diff --git a/fs/smb/client/ntlmssp.h b/fs/smb/client/ntlmssp.h new file mode 100644 index 000000000000..55758b9ec877 --- /dev/null +++ b/fs/smb/client/ntlmssp.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#define NTLMSSP_SIGNATURE "NTLMSSP" +/* Message Types */ +#define NtLmNegotiate cpu_to_le32(1) +#define NtLmChallenge cpu_to_le32(2) +#define NtLmAuthenticate cpu_to_le32(3) +#define UnknownMessage cpu_to_le32(8) + +/* Negotiate Flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ +/* define reserved9 0x08 */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ +#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ +/* defined reserved 8 0x0100 */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ +#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ +#define NTLMSSP_ANONYMOUS 0x0800 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 +#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ +/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 +#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ +#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 +/* #define reserved4 0x1000000 */ +#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we only set for SMB2+ */ +/* #define reserved3 0x4000000 */ +/* #define reserved2 0x8000000 */ +/* #define reserved1 0x10000000 */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + +/* Although typedefs are not commonly used for structure definitions */ +/* in the Linux kernel, in this particular case they are useful */ +/* to more closely match the standards document for NTLMSSP from */ +/* OpenGroup and to make the code more closely match the standard in */ +/* appearance */ + +typedef struct _SECURITY_BUFFER { + __le16 Length; + __le16 MaximumLength; + __le32 BufferOffset; /* offset to buffer */ +} __attribute__((packed)) SECURITY_BUFFER; + +typedef struct _NEGOTIATE_MESSAGE { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ + SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ + /* SECURITY_BUFFER for version info not present since we + do not set the version is present flag */ + char DomainString[0]; + /* followed by WorkstationString */ +} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE; + +#define NTLMSSP_REVISION_W2K3 0x0F + +/* See MS-NLMP section 2.2.2.10 */ +struct ntlmssp_version { + __u8 ProductMajorVersion; + __u8 ProductMinorVersion; + __le16 ProductBuild; /* we send the cifs.ko module version here */ + __u8 Reserved[3]; + __u8 NTLMRevisionCurrent; /* currently 0x0F */ +} __packed; + +/* see MS-NLMP section 2.2.1.1 */ +struct negotiate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ + SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ + struct ntlmssp_version Version; + /* SECURITY_BUFFER */ + char DomainString[]; + /* followed by WorkstationString */ +} __packed; + +typedef struct _CHALLENGE_MESSAGE { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmChallenge = 2 */ + SECURITY_BUFFER TargetName; + __le32 NegotiateFlags; + __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; + __u8 Reserved[8]; + SECURITY_BUFFER TargetInfoArray; + /* SECURITY_BUFFER for version info not present since we + do not set the version is present flag */ +} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE; + +typedef struct _AUTHENTICATE_MESSAGE { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmsAuthenticate = 3 */ + SECURITY_BUFFER LmChallengeResponse; + SECURITY_BUFFER NtChallengeResponse; + SECURITY_BUFFER DomainName; + SECURITY_BUFFER UserName; + SECURITY_BUFFER WorkstationName; + SECURITY_BUFFER SessionKey; + __le32 NegotiateFlags; + /* SECURITY_BUFFER for version info not present since we + do not set the version is present flag */ + char UserString[0]; +} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; + +/* + * Size of the session key (crypto key encrypted with the password + */ + +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); +int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c new file mode 100644 index 000000000000..2d75ba5aaa8a --- /dev/null +++ b/fs/smb/client/readdir.c @@ -0,0 +1,1237 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Directory search handling + * + * Copyright (C) International Business Machines Corp., 2004, 2008 + * Copyright (C) Red Hat, Inc., 2011 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifsfs.h" +#include "smb2proto.h" +#include "fs_context.h" +#include "cached_dir.h" + +/* + * To be safe - for UCS to UTF-8 with strings loaded with the rare long + * characters alloc more to account for such multibyte target UTF-8 + * characters. + */ +#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2) + +#ifdef CONFIG_CIFS_DEBUG2 +static void dump_cifs_file_struct(struct file *file, char *label) +{ + struct cifsFileInfo *cf; + + if (file) { + cf = file->private_data; + if (cf == NULL) { + cifs_dbg(FYI, "empty cifs private file data\n"); + return; + } + if (cf->invalidHandle) + cifs_dbg(FYI, "Invalid handle\n"); + if (cf->srch_inf.endOfSearch) + cifs_dbg(FYI, "end of search\n"); + if (cf->srch_inf.emptyDir) + cifs_dbg(FYI, "empty dir\n"); + } +} +#else +static inline void dump_cifs_file_struct(struct file *file, char *label) +{ +} +#endif /* DEBUG2 */ + +/* + * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT + * + * Find the dentry that matches "name". If there isn't one, create one. If it's + * a negative dentry or the uniqueid or filetype(mode) changed, + * then drop it and recreate it. + */ +static void +cifs_prime_dcache(struct dentry *parent, struct qstr *name, + struct cifs_fattr *fattr) +{ + struct dentry *dentry, *alias; + struct inode *inode; + struct super_block *sb = parent->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); + + cifs_dbg(FYI, "%s: for %s\n", __func__, name->name); + + dentry = d_hash_and_lookup(parent, name); + if (!dentry) { + /* + * If we know that the inode will need to be revalidated + * immediately, then don't create a new dentry for it. + * We'll end up doing an on the wire call either way and + * this spares us an invalidation. + */ + if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) + return; +retry: + dentry = d_alloc_parallel(parent, name, &wq); + } + if (IS_ERR(dentry)) + return; + if (!d_in_lookup(dentry)) { + inode = d_inode(dentry); + if (inode) { + if (d_mountpoint(dentry)) { + dput(dentry); + return; + } + /* + * If we're generating inode numbers, then we don't + * want to clobber the existing one with the one that + * the readdir code created. + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) + fattr->cf_uniqueid = CIFS_I(inode)->uniqueid; + + /* update inode in place + * if both i_ino and i_mode didn't change */ + if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid && + cifs_fattr_to_inode(inode, fattr) == 0) { + dput(dentry); + return; + } + } + d_invalidate(dentry); + dput(dentry); + goto retry; + } else { + inode = cifs_iget(sb, fattr); + if (!inode) + inode = ERR_PTR(-ENOMEM); + alias = d_splice_alias(inode, dentry); + d_lookup_done(dentry); + if (alias && !IS_ERR(alias)) + dput(alias); + } + dput(dentry); +} + +static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) +{ + if (!(fattr->cf_cifsattrs & ATTR_REPARSE)) + return false; + /* + * The DFS tags should be only intepreted by server side as per + * MS-FSCC 2.1.2.1, but let's include them anyway. + * + * Besides, if cf_cifstag is unset (0), then we still need it to be + * revalidated to know exactly what reparse point it is. + */ + switch (fattr->cf_cifstag) { + case IO_REPARSE_TAG_DFS: + case IO_REPARSE_TAG_DFSR: + case IO_REPARSE_TAG_SYMLINK: + case IO_REPARSE_TAG_NFS: + case 0: + return true; + } + return false; +} + +static void +cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) +{ + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + + /* + * The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they + * are preferred by the Linux client in some cases since, unlike + * the NFS reparse tag (or EAs), they don't require an extra query + * to determine which type of special file they represent. + * TODO: go through all documented reparse tags to see if we can + * reasonably map some of them to directories vs. files vs. symlinks + */ + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; + fattr->cf_dtype = DT_DIR; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) { + fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_LNK; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) { + fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_FIFO; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) { + fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_SOCK; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) { + fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_CHR; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) { + fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_BLK; + } else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */ + fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_REG; + } + + /* + * We need to revalidate it further to make a decision about whether it + * is a symbolic link, DFS referral or a reparse point with a direct + * access like junctions, deduplicated files, NFS symlinks. + */ + if (reparse_file_needs_reval(fattr)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + + /* non-unix readdir doesn't provide nlink */ + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; + + if (fattr->cf_cifsattrs & ATTR_READONLY) + fattr->cf_mode &= ~S_IWUGO; + + /* + * We of course don't get ACL info in FIND_FIRST/NEXT results, so + * mark it for revalidation so that "ls -l" will look right. It might + * be super-slow, but if we don't do this then the ownership of files + * may look wrong since the inodes may not have timed out by the time + * "ls" does a stat() call on them. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL && + fattr->cf_cifsattrs & ATTR_SYSTEM) { + if (fattr->cf_eof == 0) { + fattr->cf_mode &= ~S_IFMT; + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + } else { + /* + * trying to get the type and mode via SFU can be slow, + * so just call those regular files for now, and mark + * for reval + */ + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + } + } +} + +/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */ +static void +cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, + struct cifs_sb_info *cifs_sb) +{ + struct smb2_posix_info_parsed parsed; + + posix_info_parse(info, NULL, &parsed); + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_uniqueid = le64_to_cpu(info->Inode); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->HardLinks); + fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); + + /* + * Since we set the inode type below we need to mask off + * to avoid strange results if bits set above. + * XXX: why not make server&client use the type bits? + */ + fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; + + cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n", + le32_to_cpu(info->DeviceId), + le32_to_cpu(info->ReparseTag), + le32_to_cpu(info->Mode)); + + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + } else { + /* + * mark anything that is not a dir as regular + * file. special files should have the REPARSE + * attribute and will be marked as needing revaluation + */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + } + + if (reparse_file_needs_reval(fattr)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + + sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); + sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); +} + +static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) +{ + const FILE_DIRECTORY_INFO *fi = info; + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes); + fattr->cf_eof = le64_to_cpu(fi->EndOfFile); + fattr->cf_bytes = le64_to_cpu(fi->AllocationSize); + fattr->cf_createtime = le64_to_cpu(fi->CreationTime); + fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime); + fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime); +} + +void +cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + __dir_info_to_fattr(fattr, info); + cifs_fill_common_info(fattr, cifs_sb); +} + +static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr, + SEARCH_ID_FULL_DIR_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + __dir_info_to_fattr(fattr, info); + + /* See MS-FSCC 2.4.19 FileIdFullDirectoryInformation */ + if (fattr->cf_cifsattrs & ATTR_REPARSE) + fattr->cf_cifstag = le32_to_cpu(info->EaSize); + cifs_fill_common_info(fattr, cifs_sb); +} + +static void +cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj; + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, + info->LastAccessTime, offset); + fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate, + info->LastWriteTime, offset); + fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate, + info->LastWriteTime, offset); + + fattr->cf_cifsattrs = le16_to_cpu(info->Attributes); + fattr->cf_bytes = le32_to_cpu(info->AllocationSize); + fattr->cf_eof = le32_to_cpu(info->DataSize); + + cifs_fill_common_info(fattr, cifs_sb); +} + +/* BB eventually need to add the following helper function to + resolve NT_STATUS_STOPPED_ON_SYMLINK return code when + we try to do FindFirst on (NTFS) directory symlinks */ +/* +int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, + unsigned int xid) +{ + __u16 fid; + int len; + int oplock = 0; + int rc; + struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb); + char *tmpbuffer; + + rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, + OPEN_REPARSE_POINT, &fid, &oplock, NULL, + cifs_sb->local_nls, + cifs_remap(cifs_sb); + if (!rc) { + tmpbuffer = kmalloc(maxpath); + rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path, + tmpbuffer, + maxpath -1, + fid, + cifs_sb->local_nls); + if (CIFSSMBClose(xid, ptcon, fid)) { + cifs_dbg(FYI, "Error closing temporary reparsepoint open\n"); + } + } +} + */ + +static int +_initiate_cifs_search(const unsigned int xid, struct file *file, + const char *full_path) +{ + __u16 search_flags; + int rc = 0; + struct cifsFileInfo *cifsFile; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + + if (file->private_data == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); + if (cifsFile == NULL) { + rc = -ENOMEM; + goto error_exit; + } + spin_lock_init(&cifsFile->file_info_lock); + file->private_data = cifsFile; + cifsFile->tlink = cifs_get_tlink(tlink); + tcon = tlink_tcon(tlink); + } else { + cifsFile = file->private_data; + tcon = tlink_tcon(cifsFile->tlink); + } + + server = tcon->ses->server; + + if (!server->ops->query_dir_first) { + rc = -ENOSYS; + goto error_exit; + } + + cifsFile->invalidHandle = true; + cifsFile->srch_inf.endOfSearch = false; + + cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos); + +ffirst_retry: + /* test for Unix extensions */ + /* but now check for them on the share/mount not on the SMB session */ + /* if (cap_unix(tcon->ses) { */ + if (tcon->unix_ext) + cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; + else if (tcon->posix_extensions) + cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO; + else if ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; + } else /* not srvinos - BB fixme add check for backlevel? */ { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; + } + + search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; + if (backup_cred(cifs_sb)) + search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + + rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb, + &cifsFile->fid, search_flags, + &cifsFile->srch_inf); + + if (rc == 0) + cifsFile->invalidHandle = false; + /* BB add following call to handle readdir on new NTFS symlink errors + else if STATUS_STOPPED_ON_SYMLINK + call get_symlink_reparse_path and retry with new path */ + else if ((rc == -EOPNOTSUPP) && + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; + goto ffirst_retry; + } +error_exit: + cifs_put_tlink(tlink); + return rc; +} + +static int +initiate_cifs_search(const unsigned int xid, struct file *file, + const char *full_path) +{ + int rc, retry_count = 0; + + do { + rc = _initiate_cifs_search(xid, file, full_path); + /* + * If we don't have enough credits to start reading the + * directory just try again after short wait. + */ + if (rc != -EDEADLK) + break; + + usleep_range(512, 2048); + } while (retry_count++ < 5); + + return rc; +} + +/* return length of unicode string in bytes */ +static int cifs_unicode_bytelen(const char *str) +{ + int len; + const __le16 *ustr = (const __le16 *)str; + + for (len = 0; len <= PATH_MAX; len++) { + if (ustr[len] == 0) + return len << 1; + } + cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n"); + return len << 1; +} + +static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) +{ + char *new_entry; + FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; + + if (level == SMB_FIND_FILE_INFO_STANDARD) { + FIND_FILE_STANDARD_INFO *pfData; + pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo; + + new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + + pfData->FileNameLength; + } else { + u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset); + + if (old_entry + next_offset < old_entry) { + cifs_dbg(VFS, "Invalid offset %u\n", next_offset); + return NULL; + } + new_entry = old_entry + next_offset; + } + cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry); + /* validate that new_entry is not past end of SMB */ + if (new_entry >= end_of_smb) { + cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n", + new_entry, end_of_smb, old_entry); + return NULL; + } else if (((level == SMB_FIND_FILE_INFO_STANDARD) && + (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) + || ((level != SMB_FIND_FILE_INFO_STANDARD) && + (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) { + cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n", + new_entry, end_of_smb); + return NULL; + } else + return new_entry; + +} + +struct cifs_dirent { + const char *name; + size_t namelen; + u32 resume_key; + u64 ino; +}; + +static void cifs_fill_dirent_posix(struct cifs_dirent *de, + const struct smb2_posix_info *info) +{ + struct smb2_posix_info_parsed parsed; + + /* payload should have already been checked at this point */ + if (posix_info_parse(info, NULL, &parsed) < 0) { + cifs_dbg(VFS, "Invalid POSIX info payload\n"); + return; + } + + de->name = parsed.name; + de->namelen = parsed.name_len; + de->resume_key = info->Ignored; + de->ino = le64_to_cpu(info->Inode); +} + +static void cifs_fill_dirent_unix(struct cifs_dirent *de, + const FILE_UNIX_INFO *info, bool is_unicode) +{ + de->name = &info->FileName[0]; + if (is_unicode) + de->namelen = cifs_unicode_bytelen(de->name); + else + de->namelen = strnlen(de->name, PATH_MAX); + de->resume_key = info->ResumeKey; + de->ino = le64_to_cpu(info->basic.UniqueId); +} + +static void cifs_fill_dirent_dir(struct cifs_dirent *de, + const FILE_DIRECTORY_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_full(struct cifs_dirent *de, + const FILE_FULL_DIRECTORY_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_search(struct cifs_dirent *de, + const SEARCH_ID_FULL_DIR_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; + de->ino = le64_to_cpu(info->UniqueId); +} + +static void cifs_fill_dirent_both(struct cifs_dirent *de, + const FILE_BOTH_DIRECTORY_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_std(struct cifs_dirent *de, + const FIND_FILE_STANDARD_INFO *info) +{ + de->name = &info->FileName[0]; + /* one byte length, no endianess conversion */ + de->namelen = info->FileNameLength; + de->resume_key = info->ResumeKey; +} + +static int cifs_fill_dirent(struct cifs_dirent *de, const void *info, + u16 level, bool is_unicode) +{ + memset(de, 0, sizeof(*de)); + + switch (level) { + case SMB_FIND_FILE_POSIX_INFO: + cifs_fill_dirent_posix(de, info); + break; + case SMB_FIND_FILE_UNIX: + cifs_fill_dirent_unix(de, info, is_unicode); + break; + case SMB_FIND_FILE_DIRECTORY_INFO: + cifs_fill_dirent_dir(de, info); + break; + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + cifs_fill_dirent_full(de, info); + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + cifs_fill_dirent_search(de, info); + break; + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + cifs_fill_dirent_both(de, info); + break; + case SMB_FIND_FILE_INFO_STANDARD: + cifs_fill_dirent_std(de, info); + break; + default: + cifs_dbg(FYI, "Unknown findfirst level %d\n", level); + return -EINVAL; + } + + return 0; +} + +#define UNICODE_DOT cpu_to_le16(0x2e) + +/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ +static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode) +{ + int rc = 0; + + if (!de->name) + return 0; + + if (is_unicode) { + __le16 *ufilename = (__le16 *)de->name; + if (de->namelen == 2) { + /* check for . */ + if (ufilename[0] == UNICODE_DOT) + rc = 1; + } else if (de->namelen == 4) { + /* check for .. */ + if (ufilename[0] == UNICODE_DOT && + ufilename[1] == UNICODE_DOT) + rc = 2; + } + } else /* ASCII */ { + if (de->namelen == 1) { + if (de->name[0] == '.') + rc = 1; + } else if (de->namelen == 2) { + if (de->name[0] == '.' && de->name[1] == '.') + rc = 2; + } + } + + return rc; +} + +/* Check if directory that we are searching has changed so we can decide + whether we can use the cached search results from the previous search */ +static int is_dir_changed(struct file *file) +{ + struct inode *inode = file_inode(file); + struct cifsInodeInfo *cifsInfo = CIFS_I(inode); + + if (cifsInfo->time == 0) + return 1; /* directory was changed, perhaps due to unlink */ + else + return 0; + +} + +static int cifs_save_resume_key(const char *current_entry, + struct cifsFileInfo *file_info) +{ + struct cifs_dirent de; + int rc; + + rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level, + file_info->srch_inf.unicode); + if (!rc) { + file_info->srch_inf.presume_name = de.name; + file_info->srch_inf.resume_name_len = de.namelen; + file_info->srch_inf.resume_key = de.resume_key; + } + return rc; +} + +/* + * Find the corresponding entry in the search. Note that the SMB server returns + * search entries for . and .. which complicates logic here if we choose to + * parse for them and we do not assume that they are located in the findfirst + * return buffer. We start counting in the buffer with entry 2 and increment for + * every entry (do not increment for . or .. entry). + */ +static int +find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, + struct file *file, const char *full_path, + char **current_entry, int *num_to_ret) +{ + __u16 search_flags; + int rc = 0; + int pos_in_buf = 0; + loff_t first_entry_in_buffer; + loff_t index_to_find = pos; + struct cifsFileInfo *cfile = file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + struct TCP_Server_Info *server = tcon->ses->server; + /* check if index in the buffer */ + + if (!server->ops->query_dir_first || !server->ops->query_dir_next) + return -ENOSYS; + + if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL)) + return -ENOENT; + + *current_entry = NULL; + first_entry_in_buffer = cfile->srch_inf.index_of_last_entry - + cfile->srch_inf.entries_in_buffer; + + /* + * If first entry in buf is zero then is first buffer + * in search response data which means it is likely . and .. + * will be in this buffer, although some servers do not return + * . and .. for the root of a drive and for those we need + * to start two entries earlier. + */ + + dump_cifs_file_struct(file, "In fce "); + if (((index_to_find < cfile->srch_inf.index_of_last_entry) && + is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { + /* close and restart search */ + cifs_dbg(FYI, "search backing up - close and restart search\n"); + spin_lock(&cfile->file_info_lock); + if (server->ops->dir_needs_close(cfile)) { + cfile->invalidHandle = true; + spin_unlock(&cfile->file_info_lock); + if (server->ops->close_dir) + server->ops->close_dir(xid, tcon, &cfile->fid); + } else + spin_unlock(&cfile->file_info_lock); + if (cfile->srch_inf.ntwrk_buf_start) { + cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); + if (cfile->srch_inf.smallBuf) + cifs_small_buf_release(cfile->srch_inf. + ntwrk_buf_start); + else + cifs_buf_release(cfile->srch_inf. + ntwrk_buf_start); + cfile->srch_inf.ntwrk_buf_start = NULL; + } + rc = initiate_cifs_search(xid, file, full_path); + if (rc) { + cifs_dbg(FYI, "error %d reinitiating a search on rewind\n", + rc); + return rc; + } + /* FindFirst/Next set last_entry to NULL on malformed reply */ + if (cfile->srch_inf.last_entry) + cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); + } + + search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; + if (backup_cred(cifs_sb)) + search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + + while ((index_to_find >= cfile->srch_inf.index_of_last_entry) && + (rc == 0) && !cfile->srch_inf.endOfSearch) { + cifs_dbg(FYI, "calling findnext2\n"); + rc = server->ops->query_dir_next(xid, tcon, &cfile->fid, + search_flags, + &cfile->srch_inf); + /* FindFirst/Next set last_entry to NULL on malformed reply */ + if (cfile->srch_inf.last_entry) + cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); + if (rc) + return -ENOENT; + } + if (index_to_find < cfile->srch_inf.index_of_last_entry) { + /* we found the buffer that contains the entry */ + /* scan and find it */ + int i; + char *cur_ent; + char *end_of_smb; + + if (cfile->srch_inf.ntwrk_buf_start == NULL) { + cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n"); + return -EIO; + } + + end_of_smb = cfile->srch_inf.ntwrk_buf_start + + server->ops->calc_smb_size( + cfile->srch_inf.ntwrk_buf_start); + + cur_ent = cfile->srch_inf.srch_entries_start; + first_entry_in_buffer = cfile->srch_inf.index_of_last_entry + - cfile->srch_inf.entries_in_buffer; + pos_in_buf = index_to_find - first_entry_in_buffer; + cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf); + + for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) { + /* go entry by entry figuring out which is first */ + cur_ent = nxt_dir_entry(cur_ent, end_of_smb, + cfile->srch_inf.info_level); + } + if ((cur_ent == NULL) && (i < pos_in_buf)) { + /* BB fixme - check if we should flag this error */ + cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n", + pos_in_buf, index_to_find, rc); + } + rc = 0; + *current_entry = cur_ent; + } else { + cifs_dbg(FYI, "index not in buffer - could not findnext into it\n"); + return 0; + } + + if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) { + cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n"); + *num_to_ret = 0; + } else + *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf; + + return rc; +} + +static bool emit_cached_dirents(struct cached_dirents *cde, + struct dir_context *ctx) +{ + struct cached_dirent *dirent; + bool rc; + + list_for_each_entry(dirent, &cde->entries, entry) { + /* + * Skip all early entries prior to the current lseek() + * position. + */ + if (ctx->pos > dirent->pos) + continue; + /* + * We recorded the current ->pos value for the dirent + * when we stored it in the cache. + * However, this sequence of ->pos values may have holes + * in it, for example dot-dirs returned from the server + * are suppressed. + * Handle this bu forcing ctx->pos to be the same as the + * ->pos of the current dirent we emit from the cache. + * This means that when we emit these entries from the cache + * we now emit them with the same ->pos value as in the + * initial scan. + */ + ctx->pos = dirent->pos; + rc = dir_emit(ctx, dirent->name, dirent->namelen, + dirent->fattr.cf_uniqueid, + dirent->fattr.cf_dtype); + if (!rc) + return rc; + ctx->pos++; + } + return true; +} + +static void update_cached_dirents_count(struct cached_dirents *cde, + struct dir_context *ctx) +{ + if (cde->ctx != ctx) + return; + if (cde->is_valid || cde->is_failed) + return; + + cde->pos++; +} + +static void finished_cached_dirents_count(struct cached_dirents *cde, + struct dir_context *ctx) +{ + if (cde->ctx != ctx) + return; + if (cde->is_valid || cde->is_failed) + return; + if (ctx->pos != cde->pos) + return; + + cde->is_valid = 1; +} + +static void add_cached_dirent(struct cached_dirents *cde, + struct dir_context *ctx, + const char *name, int namelen, + struct cifs_fattr *fattr) +{ + struct cached_dirent *de; + + if (cde->ctx != ctx) + return; + if (cde->is_valid || cde->is_failed) + return; + if (ctx->pos != cde->pos) { + cde->is_failed = 1; + return; + } + de = kzalloc(sizeof(*de), GFP_ATOMIC); + if (de == NULL) { + cde->is_failed = 1; + return; + } + de->namelen = namelen; + de->name = kstrndup(name, namelen, GFP_ATOMIC); + if (de->name == NULL) { + kfree(de); + cde->is_failed = 1; + return; + } + de->pos = ctx->pos; + + memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr)); + + list_add_tail(&de->entry, &cde->entries); +} + +static bool cifs_dir_emit(struct dir_context *ctx, + const char *name, int namelen, + struct cifs_fattr *fattr, + struct cached_fid *cfid) +{ + bool rc; + ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); + + rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype); + if (!rc) + return rc; + + if (cfid) { + mutex_lock(&cfid->dirents.de_mutex); + add_cached_dirent(&cfid->dirents, ctx, name, namelen, + fattr); + mutex_unlock(&cfid->dirents.de_mutex); + } + + return rc; +} + +static int cifs_filldir(char *find_entry, struct file *file, + struct dir_context *ctx, + char *scratch_buf, unsigned int max_len, + struct cached_fid *cfid) +{ + struct cifsFileInfo *file_info = file->private_data; + struct super_block *sb = file_inode(file)->i_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_dirent de = { NULL, }; + struct cifs_fattr fattr; + struct qstr name; + int rc = 0; + + rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level, + file_info->srch_inf.unicode); + if (rc) + return rc; + + if (de.namelen > max_len) { + cifs_dbg(VFS, "bad search response length %zd past smb end\n", + de.namelen); + return -EINVAL; + } + + /* skip . and .. since we added them first */ + if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode)) + return 0; + + if (file_info->srch_inf.unicode) { + struct nls_table *nlt = cifs_sb->local_nls; + int map_type; + + map_type = cifs_remap(cifs_sb); + name.name = scratch_buf; + name.len = + cifs_from_utf16((char *)name.name, (__le16 *)de.name, + UNICODE_NAME_MAX, + min_t(size_t, de.namelen, + (size_t)max_len), nlt, map_type); + name.len -= nls_nullsize(nlt); + } else { + name.name = de.name; + name.len = de.namelen; + } + + switch (file_info->srch_inf.info_level) { + case SMB_FIND_FILE_POSIX_INFO: + cifs_posix_to_fattr(&fattr, + (struct smb2_posix_info *)find_entry, + cifs_sb); + break; + case SMB_FIND_FILE_UNIX: + cifs_unix_basic_to_fattr(&fattr, + &((FILE_UNIX_INFO *)find_entry)->basic, + cifs_sb); + if (S_ISLNK(fattr.cf_mode)) + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + break; + case SMB_FIND_FILE_INFO_STANDARD: + cifs_std_info_to_fattr(&fattr, + (FIND_FILE_STANDARD_INFO *)find_entry, + cifs_sb); + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + cifs_fulldir_info_to_fattr(&fattr, + (SEARCH_ID_FULL_DIR_INFO *)find_entry, + cifs_sb); + break; + default: + cifs_dir_info_to_fattr(&fattr, + (FILE_DIRECTORY_INFO *)find_entry, + cifs_sb); + break; + } + + if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { + fattr.cf_uniqueid = de.ino; + } else { + fattr.cf_uniqueid = iunique(sb, ROOT_I); + cifs_autodisable_serverino(cifs_sb); + } + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && + couldbe_mf_symlink(&fattr)) + /* + * trying to get the type and mode can be slow, + * so just call those regular files for now, and mark + * for reval + */ + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + + cifs_prime_dcache(file_dentry(file), &name, &fattr); + + return !cifs_dir_emit(ctx, name.name, name.len, + &fattr, cfid); +} + + +int cifs_readdir(struct file *file, struct dir_context *ctx) +{ + int rc = 0; + unsigned int xid; + int i; + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon; + struct cifsFileInfo *cifsFile; + char *current_entry; + int num_to_fill = 0; + char *tmp_buf = NULL; + char *end_of_smb; + unsigned int max_len; + const char *full_path; + void *page = alloc_dentry_path(); + struct cached_fid *cfid = NULL; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + + xid = get_xid(); + + full_path = build_path_from_dentry(file_dentry(file), page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto rddir2_exit; + } + + if (file->private_data == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + goto cache_not_found; + tcon = tlink_tcon(tlink); + } else { + cifsFile = file->private_data; + tcon = tlink_tcon(cifsFile->tlink); + } + + rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); + cifs_put_tlink(tlink); + if (rc) + goto cache_not_found; + + mutex_lock(&cfid->dirents.de_mutex); + /* + * If this was reading from the start of the directory + * we need to initialize scanning and storing the + * directory content. + */ + if (ctx->pos == 0 && cfid->dirents.ctx == NULL) { + cfid->dirents.ctx = ctx; + cfid->dirents.pos = 2; + } + /* + * If we already have the entire directory cached then + * we can just serve the cache. + */ + if (cfid->dirents.is_valid) { + if (!dir_emit_dots(file, ctx)) { + mutex_unlock(&cfid->dirents.de_mutex); + goto rddir2_exit; + } + emit_cached_dirents(&cfid->dirents, ctx); + mutex_unlock(&cfid->dirents.de_mutex); + goto rddir2_exit; + } + mutex_unlock(&cfid->dirents.de_mutex); + + /* Drop the cache while calling initiate_cifs_search and + * find_cifs_entry in case there will be reconnects during + * query_directory. + */ + close_cached_dir(cfid); + cfid = NULL; + + cache_not_found: + /* + * Ensure FindFirst doesn't fail before doing filldir() for '.' and + * '..'. Otherwise we won't be able to notify VFS in case of failure. + */ + if (file->private_data == NULL) { + rc = initiate_cifs_search(xid, file, full_path); + cifs_dbg(FYI, "initiate cifs search rc %d\n", rc); + if (rc) + goto rddir2_exit; + } + + if (!dir_emit_dots(file, ctx)) + goto rddir2_exit; + + /* 1) If search is active, + is in current search buffer? + if it before then restart search + if after then keep searching till find it */ + cifsFile = file->private_data; + if (cifsFile->srch_inf.endOfSearch) { + if (cifsFile->srch_inf.emptyDir) { + cifs_dbg(FYI, "End of search, empty dir\n"); + rc = 0; + goto rddir2_exit; + } + } /* else { + cifsFile->invalidHandle = true; + tcon->ses->server->close(xid, tcon, &cifsFile->fid); + } */ + + tcon = tlink_tcon(cifsFile->tlink); + rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path, + ¤t_entry, &num_to_fill); + open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); + if (rc) { + cifs_dbg(FYI, "fce error %d\n", rc); + goto rddir2_exit; + } else if (current_entry != NULL) { + cifs_dbg(FYI, "entry %lld found\n", ctx->pos); + } else { + if (cfid) { + mutex_lock(&cfid->dirents.de_mutex); + finished_cached_dirents_count(&cfid->dirents, ctx); + mutex_unlock(&cfid->dirents.de_mutex); + } + cifs_dbg(FYI, "Could not find entry\n"); + goto rddir2_exit; + } + cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", + num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); + max_len = tcon->ses->server->ops->calc_smb_size( + cifsFile->srch_inf.ntwrk_buf_start); + end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; + + tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); + if (tmp_buf == NULL) { + rc = -ENOMEM; + goto rddir2_exit; + } + + for (i = 0; i < num_to_fill; i++) { + if (current_entry == NULL) { + /* evaluate whether this case is an error */ + cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n", + num_to_fill, i); + break; + } + /* + * if buggy server returns . and .. late do we want to + * check for that here? + */ + *tmp_buf = 0; + rc = cifs_filldir(current_entry, file, ctx, + tmp_buf, max_len, cfid); + if (rc) { + if (rc > 0) + rc = 0; + break; + } + + ctx->pos++; + if (cfid) { + mutex_lock(&cfid->dirents.de_mutex); + update_cached_dirents_count(&cfid->dirents, ctx); + mutex_unlock(&cfid->dirents.de_mutex); + } + + if (ctx->pos == + cifsFile->srch_inf.index_of_last_entry) { + cifs_dbg(FYI, "last entry in buf at pos %lld %s\n", + ctx->pos, tmp_buf); + cifs_save_resume_key(current_entry, cifsFile); + break; + } + current_entry = + nxt_dir_entry(current_entry, end_of_smb, + cifsFile->srch_inf.info_level); + } + kfree(tmp_buf); + +rddir2_exit: + if (cfid) + close_cached_dir(cfid); + free_dentry_path(page); + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/rfc1002pdu.h b/fs/smb/client/rfc1002pdu.h new file mode 100644 index 000000000000..ae1d025da294 --- /dev/null +++ b/fs/smb/client/rfc1002pdu.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Protocol Data Unit definitions for RFC 1001/1002 support + * + * Copyright (c) International Business Machines Corp., 2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +/* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */ + + /* RFC 1002 session packet types */ +#define RFC1002_SESSION_MESSAGE 0x00 +#define RFC1002_SESSION_REQUEST 0x81 +#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 +#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 +#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 +#define RFC1002_SESSION_KEEP_ALIVE 0x85 + + /* RFC 1002 flags (only one defined */ +#define RFC1002_LENGTH_EXTEND 0x80 /* high order bit of length (ie +64K) */ + +struct rfc1002_session_packet { + __u8 type; + __u8 flags; + __u16 length; + union { + struct { + __u8 called_len; + __u8 called_name[32]; + __u8 scope1; /* null */ + __u8 calling_len; + __u8 calling_name[32]; + __u8 scope2; /* null */ + } __attribute__((packed)) session_req; + struct { + __u32 retarget_ip_addr; + __u16 port; + } __attribute__((packed)) retarget_resp; + __u8 neg_ses_resp_error_code; + /* POSITIVE_SESSION_RESPONSE packet does not include trailer. + SESSION_KEEP_ALIVE packet also does not include a trailer. + Trailer for the SESSION_MESSAGE packet is SMB/CIFS header */ + } __attribute__((packed)) trailer; +} __attribute__((packed)); + +/* Negative Session Response error codes */ +#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */ +#define RFC1002_NOT_LISTENING_CALLING 0x81 /* not listening on calling name */ +#define RFC1002_NOT_PRESENT 0x82 /* called name not present */ +#define RFC1002_INSUFFICIENT_RESOURCE 0x83 +#define RFC1002_UNSPECIFIED_ERROR 0x8F + +/* RFC 1002 Datagram service packets are not defined here as they +are not needed for the network filesystem client unless we plan on +implementing broadcast resolution of the server ip address (from +server netbios name). Currently server names are resolved only via DNS +(tcp name) or ip address or an /etc/hosts equivalent mapping to ip address.*/ + +#define DEFAULT_CIFS_CALLED_NAME "*SMBSERVER " diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c new file mode 100644 index 000000000000..81be17845072 --- /dev/null +++ b/fs/smb/client/sess.c @@ -0,0 +1,1858 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * SMB/CIFS session setup handling routines + * + * Copyright (c) International Business Machines Corp., 2006, 2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "ntlmssp.h" +#include "nterr.h" +#include +#include +#include +#include "cifsfs.h" +#include "cifs_spnego.h" +#include "smb2proto.h" +#include "fs_context.h" + +static int +cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, + struct cifs_server_iface *iface); + +bool +is_server_using_iface(struct TCP_Server_Info *server, + struct cifs_server_iface *iface) +{ + struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr; + struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr; + struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr; + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr; + + if (server->dstaddr.ss_family != iface->sockaddr.ss_family) + return false; + if (server->dstaddr.ss_family == AF_INET) { + if (s4->sin_addr.s_addr != i4->sin_addr.s_addr) + return false; + } else if (server->dstaddr.ss_family == AF_INET6) { + if (memcmp(&s6->sin6_addr, &i6->sin6_addr, + sizeof(i6->sin6_addr)) != 0) + return false; + } else { + /* unknown family.. */ + return false; + } + return true; +} + +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) +{ + int i; + + spin_lock(&ses->chan_lock); + for (i = 0; i < ses->chan_count; i++) { + if (ses->chans[i].iface == iface) { + spin_unlock(&ses->chan_lock); + return true; + } + } + spin_unlock(&ses->chan_lock); + return false; +} + +/* channel helper functions. assumed that chan_lock is held by caller. */ + +unsigned int +cifs_ses_get_chan_index(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int i; + + for (i = 0; i < ses->chan_count; i++) { + if (ses->chans[i].server == server) + return i; + } + + /* If we didn't find the channel, it is likely a bug */ + if (server) + cifs_dbg(VFS, "unable to get chan index for server: 0x%llx", + server->conn_id); + WARN_ON(1); + return 0; +} + +void +cifs_chan_set_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + ses->chans[chan_index].in_reconnect = true; +} + +void +cifs_chan_clear_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + ses->chans[chan_index].in_reconnect = false; +} + +bool +cifs_chan_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + return CIFS_CHAN_IN_RECONNECT(ses, chan_index); +} + +void +cifs_chan_set_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + set_bit(chan_index, &ses->chans_need_reconnect); + cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n", + chan_index, ses->chans_need_reconnect); +} + +void +cifs_chan_clear_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + clear_bit(chan_index, &ses->chans_need_reconnect); + cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n", + chan_index, ses->chans_need_reconnect); +} + +bool +cifs_chan_needs_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index); +} + +bool +cifs_chan_is_iface_active(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + return ses->chans[chan_index].iface && + ses->chans[chan_index].iface->is_active; +} + +/* returns number of channels added */ +int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) +{ + struct TCP_Server_Info *server = ses->server; + int old_chan_count, new_chan_count; + int left; + int rc = 0; + int tries = 0; + struct cifs_server_iface *iface = NULL, *niface = NULL; + + spin_lock(&ses->chan_lock); + + new_chan_count = old_chan_count = ses->chan_count; + left = ses->chan_max - ses->chan_count; + + if (left <= 0) { + spin_unlock(&ses->chan_lock); + cifs_dbg(FYI, + "ses already at max_channels (%zu), nothing to open\n", + ses->chan_max); + return 0; + } + + if (server->dialect < SMB30_PROT_ID) { + spin_unlock(&ses->chan_lock); + cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n"); + return 0; + } + + if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { + ses->chan_max = 1; + spin_unlock(&ses->chan_lock); + cifs_server_dbg(VFS, "no multichannel support\n"); + return 0; + } + spin_unlock(&ses->chan_lock); + + /* + * Keep connecting to same, fastest, iface for all channels as + * long as its RSS. Try next fastest one if not RSS or channel + * creation fails. + */ + spin_lock(&ses->iface_lock); + iface = list_first_entry(&ses->iface_list, struct cifs_server_iface, + iface_head); + spin_unlock(&ses->iface_lock); + + while (left > 0) { + + tries++; + if (tries > 3*ses->chan_max) { + cifs_dbg(FYI, "too many channel open attempts (%d channels left to open)\n", + left); + break; + } + + spin_lock(&ses->iface_lock); + if (!ses->iface_count) { + spin_unlock(&ses->iface_lock); + break; + } + + list_for_each_entry_safe_from(iface, niface, &ses->iface_list, + iface_head) { + /* skip ifaces that are unusable */ + if (!iface->is_active || + (is_ses_using_iface(ses, iface) && + !iface->rss_capable)) { + continue; + } + + /* take ref before unlock */ + kref_get(&iface->refcount); + + spin_unlock(&ses->iface_lock); + rc = cifs_ses_add_channel(cifs_sb, ses, iface); + spin_lock(&ses->iface_lock); + + if (rc) { + cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n", + &iface->sockaddr, + rc); + kref_put(&iface->refcount, release_iface); + continue; + } + + cifs_dbg(FYI, "successfully opened new channel on iface:%pIS\n", + &iface->sockaddr); + break; + } + spin_unlock(&ses->iface_lock); + + left--; + new_chan_count++; + } + + return new_chan_count - old_chan_count; +} + +/* + * update the iface for the channel if necessary. + * will return 0 when iface is updated, 1 if removed, 2 otherwise + * Must be called with chan_lock held. + */ +int +cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) +{ + unsigned int chan_index; + struct cifs_server_iface *iface = NULL; + struct cifs_server_iface *old_iface = NULL; + int rc = 0; + + spin_lock(&ses->chan_lock); + chan_index = cifs_ses_get_chan_index(ses, server); + if (!chan_index) { + spin_unlock(&ses->chan_lock); + return 0; + } + + if (ses->chans[chan_index].iface) { + old_iface = ses->chans[chan_index].iface; + if (old_iface->is_active) { + spin_unlock(&ses->chan_lock); + return 1; + } + } + spin_unlock(&ses->chan_lock); + + spin_lock(&ses->iface_lock); + /* then look for a new one */ + list_for_each_entry(iface, &ses->iface_list, iface_head) { + if (!iface->is_active || + (is_ses_using_iface(ses, iface) && + !iface->rss_capable)) { + continue; + } + kref_get(&iface->refcount); + break; + } + + if (list_entry_is_head(iface, &ses->iface_list, iface_head)) { + rc = 1; + iface = NULL; + cifs_dbg(FYI, "unable to find a suitable iface\n"); + } + + /* now drop the ref to the current iface */ + if (old_iface && iface) { + cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n", + &old_iface->sockaddr, + &iface->sockaddr); + kref_put(&old_iface->refcount, release_iface); + } else if (old_iface) { + cifs_dbg(FYI, "releasing ref to iface: %pIS\n", + &old_iface->sockaddr); + kref_put(&old_iface->refcount, release_iface); + } else { + WARN_ON(!iface); + cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr); + } + spin_unlock(&ses->iface_lock); + + spin_lock(&ses->chan_lock); + chan_index = cifs_ses_get_chan_index(ses, server); + ses->chans[chan_index].iface = iface; + + /* No iface is found. if secondary chan, drop connection */ + if (!iface && CIFS_SERVER_IS_CHAN(server)) + ses->chans[chan_index].server = NULL; + + spin_unlock(&ses->chan_lock); + + if (!iface && CIFS_SERVER_IS_CHAN(server)) + cifs_put_tcp_session(server, false); + + return rc; +} + +/* + * If server is a channel of ses, return the corresponding enclosing + * cifs_chan otherwise return NULL. + */ +struct cifs_chan * +cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server) +{ + int i; + + spin_lock(&ses->chan_lock); + for (i = 0; i < ses->chan_count; i++) { + if (ses->chans[i].server == server) { + spin_unlock(&ses->chan_lock); + return &ses->chans[i]; + } + } + spin_unlock(&ses->chan_lock); + return NULL; +} + +static int +cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, + struct cifs_server_iface *iface) +{ + struct TCP_Server_Info *chan_server; + struct cifs_chan *chan; + struct smb3_fs_context ctx = {NULL}; + static const char unc_fmt[] = "\\%s\\foo"; + char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0}; + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; + int rc; + unsigned int xid = get_xid(); + + if (iface->sockaddr.ss_family == AF_INET) + cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI4)\n", + ses, iface->speed, iface->rdma_capable ? "yes" : "no", + &ipv4->sin_addr); + else + cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI6)\n", + ses, iface->speed, iface->rdma_capable ? "yes" : "no", + &ipv6->sin6_addr); + + /* + * Setup a ctx with mostly the same info as the existing + * session and overwrite it with the requested iface data. + * + * We need to setup at least the fields used for negprot and + * sesssetup. + * + * We only need the ctx here, so we can reuse memory from + * the session and server without caring about memory + * management. + */ + + /* Always make new connection for now (TODO?) */ + ctx.nosharesock = true; + + /* Auth */ + ctx.domainauto = ses->domainAuto; + ctx.domainname = ses->domainName; + + /* no hostname for extra channels */ + ctx.server_hostname = ""; + + ctx.username = ses->user_name; + ctx.password = ses->password; + ctx.sectype = ses->sectype; + ctx.sign = ses->sign; + + /* UNC and paths */ + /* XXX: Use ses->server->hostname? */ + sprintf(unc, unc_fmt, ses->ip_addr); + ctx.UNC = unc; + ctx.prepath = ""; + + /* Reuse same version as master connection */ + ctx.vals = ses->server->vals; + ctx.ops = ses->server->ops; + + ctx.noblocksnd = ses->server->noblocksnd; + ctx.noautotune = ses->server->noautotune; + ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay; + ctx.echo_interval = ses->server->echo_interval / HZ; + ctx.max_credits = ses->server->max_credits; + + /* + * This will be used for encoding/decoding user/domain/pw + * during sess setup auth. + */ + ctx.local_nls = cifs_sb->local_nls; + + /* Use RDMA if possible */ + ctx.rdma = iface->rdma_capable; + memcpy(&ctx.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage)); + + /* reuse master con client guid */ + memcpy(&ctx.client_guid, ses->server->client_guid, + SMB2_CLIENT_GUID_SIZE); + ctx.use_client_guid = true; + + chan_server = cifs_get_tcp_session(&ctx, ses->server); + + spin_lock(&ses->chan_lock); + chan = &ses->chans[ses->chan_count]; + chan->server = chan_server; + if (IS_ERR(chan->server)) { + rc = PTR_ERR(chan->server); + chan->server = NULL; + spin_unlock(&ses->chan_lock); + goto out; + } + chan->iface = iface; + ses->chan_count++; + atomic_set(&ses->chan_seq, 0); + + /* Mark this channel as needing connect/setup */ + cifs_chan_set_need_reconnect(ses, chan->server); + + spin_unlock(&ses->chan_lock); + + mutex_lock(&ses->session_mutex); + /* + * We need to allocate the server crypto now as we will need + * to sign packets before we generate the channel signing key + * (we sign with the session key) + */ + rc = smb311_crypto_shash_allocate(chan->server); + if (rc) { + cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); + mutex_unlock(&ses->session_mutex); + goto out; + } + + rc = cifs_negotiate_protocol(xid, ses, chan->server); + if (!rc) + rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls); + + mutex_unlock(&ses->session_mutex); + +out: + if (rc && chan->server) { + /* + * we should avoid race with these delayed works before we + * remove this channel + */ + cancel_delayed_work_sync(&chan->server->echo); + cancel_delayed_work_sync(&chan->server->resolve); + cancel_delayed_work_sync(&chan->server->reconnect); + + spin_lock(&ses->chan_lock); + /* we rely on all bits beyond chan_count to be clear */ + cifs_chan_clear_need_reconnect(ses, chan->server); + ses->chan_count--; + /* + * chan_count should never reach 0 as at least the primary + * channel is always allocated + */ + WARN_ON(ses->chan_count < 1); + spin_unlock(&ses->chan_lock); + + cifs_put_tcp_session(chan->server, 0); + } + + free_xid(xid); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, + struct TCP_Server_Info *server, + SESSION_SETUP_ANDX *pSMB) +{ + __u32 capabilities = 0; + + /* init fields common to all four types of SessSetup */ + /* Note that offsets for first seven fields in req struct are same */ + /* in CIFS Specs so does not matter which of 3 forms of struct */ + /* that we use in next few lines */ + /* Note that header is initialized to zero in header_assemble */ + pSMB->req.AndXCommand = 0xFF; + pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, + USHRT_MAX)); + pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq); + pSMB->req.VcNumber = cpu_to_le16(1); + + /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ + + /* BB verify whether signing required on neg or just on auth frame + (and NTLM case) */ + + capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; + + if (server->sign) + pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + if (ses->capabilities & CAP_UNICODE) { + pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE; + capabilities |= CAP_UNICODE; + } + if (ses->capabilities & CAP_STATUS32) { + pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS; + capabilities |= CAP_STATUS32; + } + if (ses->capabilities & CAP_DFS) { + pSMB->req.hdr.Flags2 |= SMBFLG2_DFS; + capabilities |= CAP_DFS; + } + if (ses->capabilities & CAP_UNIX) + capabilities |= CAP_UNIX; + + return capabilities; +} + +static void +unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* Copy OS version */ + bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32, + nls_cp); + bcc_ptr += 2 * bytes_ret; + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + *pbcc_area = bcc_ptr; +} + +static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* copy domain */ + if (ses->domainName == NULL) { + /* Sending null domain better than using a bogus domain name (as + we did briefly in 2.6.18) since server will use its default */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + bytes_ret = 0; + } else + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, + CIFS_MAX_DOMAINNAME_LEN, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* account for null terminator */ + + *pbcc_area = bcc_ptr; +} + +static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* BB FIXME add check that strings total less + than 335 or will need to send them as arrays */ + + /* copy user */ + if (ses->user_name == NULL) { + /* null user mount */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + } else { + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, + CIFS_MAX_USERNAME_LEN, nls_cp); + } + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* account for null termination */ + + unicode_domain_string(&bcc_ptr, ses, nls_cp); + unicode_oslm_strings(&bcc_ptr, nls_cp); + + *pbcc_area = bcc_ptr; +} + +static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int len; + + /* copy user */ + /* BB what about null user mounts - check that we do this BB */ + /* copy user */ + if (ses->user_name != NULL) { + len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN); + if (WARN_ON_ONCE(len < 0)) + len = CIFS_MAX_USERNAME_LEN - 1; + bcc_ptr += len; + } + /* else null user mount */ + *bcc_ptr = 0; + bcc_ptr++; /* account for null termination */ + + /* copy domain */ + if (ses->domainName != NULL) { + len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); + if (WARN_ON_ONCE(len < 0)) + len = CIFS_MAX_DOMAINNAME_LEN - 1; + bcc_ptr += len; + } /* else we will send a null domain name + so the server will default to its own domain */ + *bcc_ptr = 0; + bcc_ptr++; + + /* BB check for overflow here */ + + strcpy(bcc_ptr, "Linux version "); + bcc_ptr += strlen("Linux version "); + strcpy(bcc_ptr, init_utsname()->release); + bcc_ptr += strlen(init_utsname()->release) + 1; + + strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); + bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; + + *pbcc_area = bcc_ptr; +} + +static void +decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + int len; + char *data = *pbcc_area; + + cifs_dbg(FYI, "bleft %d\n", bleft); + + kfree(ses->serverOS); + ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; + + kfree(ses->serverNOS); + ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; + + kfree(ses->serverDomain); + ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain); + + return; +} + +static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, + struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + int len; + char *bcc_ptr = *pbcc_area; + + cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft); + + len = strnlen(bcc_ptr, bleft); + if (len >= bleft) + return; + + kfree(ses->serverOS); + + ses->serverOS = kmalloc(len + 1, GFP_KERNEL); + if (ses->serverOS) { + memcpy(ses->serverOS, bcc_ptr, len); + ses->serverOS[len] = 0; + if (strncmp(ses->serverOS, "OS/2", 4) == 0) + cifs_dbg(FYI, "OS/2 server\n"); + } + + bcc_ptr += len + 1; + bleft -= len + 1; + + len = strnlen(bcc_ptr, bleft); + if (len >= bleft) + return; + + kfree(ses->serverNOS); + + ses->serverNOS = kmalloc(len + 1, GFP_KERNEL); + if (ses->serverNOS) { + memcpy(ses->serverNOS, bcc_ptr, len); + ses->serverNOS[len] = 0; + } + + bcc_ptr += len + 1; + bleft -= len + 1; + + len = strnlen(bcc_ptr, bleft); + if (len > bleft) + return; + + /* No domain field in LANMAN case. Domain is + returned by old servers in the SMB negprot response */ + /* BB For newer servers which do not support Unicode, + but thus do return domain here we could add parsing + for it later, but it is not very important */ + cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, + struct cifs_ses *ses) +{ + unsigned int tioffset; /* challenge message target info area */ + unsigned int tilen; /* challenge message target info area length */ + CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; + __u32 server_flags; + + if (blob_len < sizeof(CHALLENGE_MESSAGE)) { + cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len); + return -EINVAL; + } + + if (memcmp(pblob->Signature, "NTLMSSP", 8)) { + cifs_dbg(VFS, "blob signature incorrect %s\n", + pblob->Signature); + return -EINVAL; + } + if (pblob->MessageType != NtLmChallenge) { + cifs_dbg(VFS, "Incorrect message type %d\n", + pblob->MessageType); + return -EINVAL; + } + + server_flags = le32_to_cpu(pblob->NegotiateFlags); + cifs_dbg(FYI, "%s: negotiate=0x%08x challenge=0x%08x\n", __func__, + ses->ntlmssp->client_flags, server_flags); + + if ((ses->ntlmssp->client_flags & (NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN)) && + (!(server_flags & NTLMSSP_NEGOTIATE_56) && !(server_flags & NTLMSSP_NEGOTIATE_128))) { + cifs_dbg(VFS, "%s: requested signing/encryption but server did not return either 56-bit or 128-bit session key size\n", + __func__); + return -EINVAL; + } + if (!(server_flags & NTLMSSP_NEGOTIATE_NTLM) && !(server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) { + cifs_dbg(VFS, "%s: server does not seem to support either NTLMv1 or NTLMv2\n", __func__); + return -EINVAL; + } + if (ses->server->sign && !(server_flags & NTLMSSP_NEGOTIATE_SIGN)) { + cifs_dbg(VFS, "%s: forced packet signing but server does not seem to support it\n", + __func__); + return -EOPNOTSUPP; + } + if ((ses->ntlmssp->client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && + !(server_flags & NTLMSSP_NEGOTIATE_KEY_XCH)) + pr_warn_once("%s: authentication has been weakened as server does not support key exchange\n", + __func__); + + ses->ntlmssp->server_flags = server_flags; + + memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); + /* In particular we can examine sign flags */ + /* BB spec says that if AvId field of MsvAvTimestamp is populated then + we must set the MIC field of the AUTHENTICATE_MESSAGE */ + + tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); + tilen = le16_to_cpu(pblob->TargetInfoArray.Length); + if (tioffset > blob_len || tioffset + tilen > blob_len) { + cifs_dbg(VFS, "tioffset + tilen too high %u + %u\n", + tioffset, tilen); + return -EINVAL; + } + if (tilen) { + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, "Challenge target info alloc failure\n"); + return -ENOMEM; + } + ses->auth_key.len = tilen; + } + + return 0; +} + +static int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size) +{ + int sz = base_size + ses->auth_key.len + - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; + + if (ses->domainName) + sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); + else + sz += sizeof(__le16); + + if (ses->user_name) + sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); + else + sz += sizeof(__le16); + + if (ses->workstation_name[0]) + sz += sizeof(__le16) * strnlen(ses->workstation_name, + ntlmssp_workstation_name_size(ses)); + else + sz += sizeof(__le16); + + return sz; +} + +static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf, + char *str_value, + int str_length, + unsigned char *pstart, + unsigned char **pcur, + const struct nls_table *nls_cp) +{ + unsigned char *tmp = pstart; + int len; + + if (!pbuf) + return; + + if (!pcur) + pcur = &tmp; + + if (!str_value) { + pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); + pbuf->Length = 0; + pbuf->MaximumLength = 0; + *pcur += sizeof(__le16); + } else { + len = cifs_strtoUTF16((__le16 *)*pcur, + str_value, + str_length, + nls_cp); + len *= sizeof(__le16); + pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); + pbuf->Length = cpu_to_le16(len); + pbuf->MaximumLength = cpu_to_le16(len); + *pcur += len; + } +} + +/* BB Move to ntlmssp.c eventually */ + +int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, + u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + NEGOTIATE_MESSAGE *sec_blob; + __u32 flags; + unsigned char *tmp; + int len; + + len = size_of_ntlmssp_blob(ses, sizeof(NEGOTIATE_MESSAGE)); + *pbuffer = kmalloc(len, GFP_KERNEL); + if (!*pbuffer) { + rc = -ENOMEM; + cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); + *buflen = 0; + goto setup_ntlm_neg_ret; + } + sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer; + + memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE)); + memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); + sec_blob->MessageType = NtLmNegotiate; + + /* BB is NTLMV2 session security format easier to use here? */ + flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | + NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_SIGN; + if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + + tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE); + ses->ntlmssp->client_flags = flags; + sec_blob->NegotiateFlags = cpu_to_le32(flags); + + /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ + cifs_security_buffer_from_str(&sec_blob->DomainName, + NULL, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + NULL, + CIFS_MAX_WORKSTATION_LEN, + *pbuffer, &tmp, + nls_cp); + + *buflen = tmp - *pbuffer; +setup_ntlm_neg_ret: + return rc; +} + +/* + * Build ntlmssp blob with additional fields, such as version, + * supported by modern servers. For safety limit to SMB3 or later + * See notes in MS-NLMP Section 2.2.2.1 e.g. + */ +int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, + u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + struct negotiate_message *sec_blob; + __u32 flags; + unsigned char *tmp; + int len; + + len = size_of_ntlmssp_blob(ses, sizeof(struct negotiate_message)); + *pbuffer = kmalloc(len, GFP_KERNEL); + if (!*pbuffer) { + rc = -ENOMEM; + cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); + *buflen = 0; + goto setup_ntlm_smb3_neg_ret; + } + sec_blob = (struct negotiate_message *)*pbuffer; + + memset(*pbuffer, 0, sizeof(struct negotiate_message)); + memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); + sec_blob->MessageType = NtLmNegotiate; + + /* BB is NTLMV2 session security format easier to use here? */ + flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | + NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_VERSION; + if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + + sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR; + sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL; + sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD); + sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3; + + tmp = *pbuffer + sizeof(struct negotiate_message); + ses->ntlmssp->client_flags = flags; + sec_blob->NegotiateFlags = cpu_to_le32(flags); + + /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ + cifs_security_buffer_from_str(&sec_blob->DomainName, + NULL, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + NULL, + CIFS_MAX_WORKSTATION_LEN, + *pbuffer, &tmp, + nls_cp); + + *buflen = tmp - *pbuffer; +setup_ntlm_smb3_neg_ret: + return rc; +} + + +int build_ntlmssp_auth_blob(unsigned char **pbuffer, + u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc; + AUTHENTICATE_MESSAGE *sec_blob; + __u32 flags; + unsigned char *tmp; + int len; + + rc = setup_ntlmv2_rsp(ses, nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); + *buflen = 0; + goto setup_ntlmv2_ret; + } + + len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE)); + *pbuffer = kmalloc(len, GFP_KERNEL); + if (!*pbuffer) { + rc = -ENOMEM; + cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); + *buflen = 0; + goto setup_ntlmv2_ret; + } + sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer; + + memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); + sec_blob->MessageType = NtLmAuthenticate; + + flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; + + tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE); + sec_blob->NegotiateFlags = cpu_to_le32(flags); + + sec_blob->LmChallengeResponse.BufferOffset = + cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE)); + sec_blob->LmChallengeResponse.Length = 0; + sec_blob->LmChallengeResponse.MaximumLength = 0; + + sec_blob->NtChallengeResponse.BufferOffset = + cpu_to_le32(tmp - *pbuffer); + if (ses->user_name != NULL) { + memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + + sec_blob->NtChallengeResponse.Length = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + sec_blob->NtChallengeResponse.MaximumLength = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + } else { + /* + * don't send an NT Response for anonymous access + */ + sec_blob->NtChallengeResponse.Length = 0; + sec_blob->NtChallengeResponse.MaximumLength = 0; + } + + cifs_security_buffer_from_str(&sec_blob->DomainName, + ses->domainName, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->UserName, + ses->user_name, + CIFS_MAX_USERNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + ses->workstation_name, + ntlmssp_workstation_name_size(ses), + *pbuffer, &tmp, + nls_cp); + + if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && + (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) && + !calc_seckey(ses)) { + memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); + sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); + sec_blob->SessionKey.MaximumLength = + cpu_to_le16(CIFS_CPHTXT_SIZE); + tmp += CIFS_CPHTXT_SIZE; + } else { + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); + sec_blob->SessionKey.Length = 0; + sec_blob->SessionKey.MaximumLength = 0; + } + + *buflen = tmp - *pbuffer; +setup_ntlmv2_ret: + return rc; +} + +enum securityEnum +cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) +{ + switch (server->negflavor) { + case CIFS_NEGFLAVOR_EXTENDED: + switch (requested) { + case Kerberos: + case RawNTLMSSP: + return requested; + case Unspecified: + if (server->sec_ntlmssp && + (global_secflags & CIFSSEC_MAY_NTLMSSP)) + return RawNTLMSSP; + if ((server->sec_kerberos || server->sec_mskerberos) && + (global_secflags & CIFSSEC_MAY_KRB5)) + return Kerberos; + fallthrough; + default: + return Unspecified; + } + case CIFS_NEGFLAVOR_UNENCAP: + switch (requested) { + case NTLMv2: + return requested; + case Unspecified: + if (global_secflags & CIFSSEC_MAY_NTLMV2) + return NTLMv2; + break; + default: + break; + } + fallthrough; + default: + return Unspecified; + } +} + +struct sess_data { + unsigned int xid; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_cp; + void (*func)(struct sess_data *); + int result; + + /* we will send the SMB in three pieces: + * a fixed length beginning part, an optional + * SPNEGO blob (which can be zero length), and a + * last part which will include the strings + * and rest of bcc area. This allows us to avoid + * a large buffer 17K allocation + */ + int buf0_type; + struct kvec iov[3]; +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +sess_alloc_buffer(struct sess_data *sess_data, int wct) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct smb_hdr *smb_buf; + + rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, + (void **)&smb_buf); + + if (rc) + return rc; + + sess_data->iov[0].iov_base = (char *)smb_buf; + sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4; + /* + * This variable will be used to clear the buffer + * allocated above in case of any error in the calling function. + */ + sess_data->buf0_type = CIFS_SMALL_BUFFER; + + /* 2000 big enough to fit max user, domain, NOS name etc. */ + sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL); + if (!sess_data->iov[2].iov_base) { + rc = -ENOMEM; + goto out_free_smb_buf; + } + + return 0; + +out_free_smb_buf: + cifs_small_buf_release(smb_buf); + sess_data->iov[0].iov_base = NULL; + sess_data->iov[0].iov_len = 0; + sess_data->buf0_type = CIFS_NO_BUFFER; + return rc; +} + +static void +sess_free_buffer(struct sess_data *sess_data) +{ + struct kvec *iov = sess_data->iov; + + /* + * Zero the session data before freeing, as it might contain sensitive info (keys, etc). + * Note that iov[1] is already freed by caller. + */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) + memzero_explicit(iov[0].iov_base, iov[0].iov_len); + + free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); + sess_data->buf0_type = CIFS_NO_BUFFER; + kfree_sensitive(iov[2].iov_base); +} + +static int +sess_establish_session(struct sess_data *sess_data) +{ + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + cifs_server_lock(server); + if (!server->session_estab) { + if (server->sign) { + server->session_key.response = + kmemdup(ses->auth_key.response, + ses->auth_key.len, GFP_KERNEL); + if (!server->session_key.response) { + cifs_server_unlock(server); + return -ENOMEM; + } + server->session_key.len = + ses->auth_key.len; + } + server->sequence_number = 0x2; + server->session_estab = true; + } + cifs_server_unlock(server); + + cifs_dbg(FYI, "CIFS session established successfully\n"); + return 0; +} + +static int +sess_sendreceive(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base; + __u16 count; + struct kvec rsp_iov = { NULL, 0 }; + + count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; + be32_add_cpu(&smb_buf->smb_buf_length, count); + put_bcc(count, smb_buf); + + rc = SendReceive2(sess_data->xid, sess_data->ses, + sess_data->iov, 3 /* num_iovecs */, + &sess_data->buf0_type, + CIFS_LOG_ERROR, &rsp_iov); + cifs_small_buf_release(sess_data->iov[0].iov_base); + memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); + + return rc; +} + +static void +sess_auth_ntlmv2(struct sess_data *sess_data) +{ + int rc = 0; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + char *bcc_ptr; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + __u16 bytes_remaining; + + /* old style NTLM sessionsetup */ + /* wct = 13 */ + rc = sess_alloc_buffer(sess_data, 13); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + bcc_ptr = sess_data->iov[2].iov_base; + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + + pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); + + /* LM2 password would be here if we supported it */ + pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; + + if (ses->user_name != NULL) { + /* calculate nlmv2 response and session key */ + rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); + goto out; + } + + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + + /* set case sensitive password length after tilen may get + * assigned, tilen is 0 otherwise. + */ + pSMB->req_no_secext.CaseSensitivePasswordLength = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + } else { + pSMB->req_no_secext.CaseSensitivePasswordLength = 0; + } + + if (ses->capabilities & CAP_UNICODE) { + if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } else { + ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } + + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + if (smb_buf->WordCount != 3) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + + rc = sess_establish_session(sess_data); +out: + sess_data->result = rc; + sess_data->func = NULL; + sess_free_buffer(sess_data); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; +} + +#ifdef CONFIG_CIFS_UPCALL +static void +sess_auth_kerberos(struct sess_data *sess_data) +{ + int rc = 0; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + char *bcc_ptr; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + __u16 bytes_remaining; + struct key *spnego_key = NULL; + struct cifs_spnego_msg *msg; + u16 blob_len; + + /* extended security */ + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + bcc_ptr = sess_data->iov[2].iov_base; + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + + spnego_key = cifs_get_spnego_key(ses, server); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + spnego_key = NULL; + goto out; + } + + msg = spnego_key->payload.data[0]; + /* + * check version field to make sure that cifs.upcall is + * sending us a response in an expected form + */ + if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { + cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n", + CIFS_SPNEGO_UPCALL_VERSION, msg->version); + rc = -EKEYREJECTED; + goto out_put_spnego_key; + } + + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", + msg->sesskey_len); + rc = -ENOMEM; + goto out_put_spnego_key; + } + ses->auth_key.len = msg->sesskey_len; + + pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; + capabilities |= CAP_EXTENDED_SECURITY; + pSMB->req.Capabilities = cpu_to_le32(capabilities); + sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; + sess_data->iov[1].iov_len = msg->secblob_len; + pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len); + + if (ses->capabilities & CAP_UNICODE) { + /* unicode strings must be word aligned */ + if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); + unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp); + } else { + /* BB: is this right? */ + ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out_put_spnego_key; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + if (smb_buf->WordCount != 4) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_put_spnego_key; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_put_spnego_key; + } + bcc_ptr += blob_len; + bytes_remaining -= blob_len; + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + + rc = sess_establish_session(sess_data); +out_put_spnego_key: + key_invalidate(spnego_key); + key_put(spnego_key); +out: + sess_data->result = rc; + sess_data->func = NULL; + sess_free_buffer(sess_data); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; +} + +#endif /* ! CONFIG_CIFS_UPCALL */ + +/* + * The required kvec buffers have to be allocated before calling this + * function. + */ +static int +_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) +{ + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + char *bcc_ptr; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { + cifs_dbg(VFS, "NTLMSSP requires Unicode support\n"); + return -ENOSYS; + } + + pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; + capabilities |= CAP_EXTENDED_SECURITY; + pSMB->req.Capabilities |= cpu_to_le32(capabilities); + + bcc_ptr = sess_data->iov[2].iov_base; + /* unicode strings must be word aligned */ + if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + return 0; +} + +static void +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data); + +static void +sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u16 bytes_remaining; + char *bcc_ptr; + unsigned char *ntlmsspblob = NULL; + u16 blob_len; + + cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n"); + + /* + * if memory allocation is successful, caller of this function + * frees it. + */ + ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); + if (!ses->ntlmssp) { + rc = -ENOMEM; + goto out; + } + ses->ntlmssp->sesskey_per_smbsess = false; + + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + + /* Build security blob before we assemble the request */ + rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, + &blob_len, ses, server, + sess_data->nls_cp); + if (rc) + goto out_free_ntlmsspblob; + + sess_data->iov[1].iov_len = blob_len; + sess_data->iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); + + rc = _sess_auth_rawntlmssp_assemble_req(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + rc = sess_sendreceive(sess_data); + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + /* If true, rc here is expected and not an error */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && + smb_buf->Status.CifsError == + cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) + rc = 0; + + if (rc) + goto out_free_ntlmsspblob; + + cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); + + if (smb_buf->WordCount != 4) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_free_ntlmsspblob; + } + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_free_ntlmsspblob; + } + + rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); + +out_free_ntlmsspblob: + kfree_sensitive(ntlmsspblob); +out: + sess_free_buffer(sess_data); + + if (!rc) { + sess_data->func = sess_auth_rawntlmssp_authenticate; + return; + } + + /* Else error. Cleanup */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + + sess_data->func = NULL; + sess_data->result = rc; +} + +static void +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u16 bytes_remaining; + char *bcc_ptr; + unsigned char *ntlmsspblob = NULL; + u16 blob_len; + + cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); + + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + /* Build security blob before we assemble the request */ + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)pSMB; + rc = build_ntlmssp_auth_blob(&ntlmsspblob, + &blob_len, ses, server, + sess_data->nls_cp); + if (rc) + goto out_free_ntlmsspblob; + sess_data->iov[1].iov_len = blob_len; + sess_data->iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); + /* + * Make sure that we tell the server that we are using + * the uid that it just gave us back on the response + * (challenge) + */ + smb_buf->Uid = ses->Suid; + + rc = _sess_auth_rawntlmssp_assemble_req(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + if (smb_buf->WordCount != 4) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_free_ntlmsspblob; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + if (ses->Suid != smb_buf->Uid) { + ses->Suid = smb_buf->Uid; + cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid); + } + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_free_ntlmsspblob; + } + bcc_ptr += blob_len; + bytes_remaining -= blob_len; + + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + +out_free_ntlmsspblob: + kfree_sensitive(ntlmsspblob); +out: + sess_free_buffer(sess_data); + + if (!rc) + rc = sess_establish_session(sess_data); + + /* Cleanup */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + + sess_data->func = NULL; + sess_data->result = rc; +} + +static int select_sec(struct sess_data *sess_data) +{ + int type; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + type = cifs_select_sectype(server, ses->sectype); + cifs_dbg(FYI, "sess setup type %d\n", type); + if (type == Unspecified) { + cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); + return -EINVAL; + } + + switch (type) { + case NTLMv2: + sess_data->func = sess_auth_ntlmv2; + break; + case Kerberos: +#ifdef CONFIG_CIFS_UPCALL + sess_data->func = sess_auth_kerberos; + break; +#else + cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); + return -ENOSYS; +#endif /* CONFIG_CIFS_UPCALL */ + case RawNTLMSSP: + sess_data->func = sess_auth_rawntlmssp_negotiate; + break; + default: + cifs_dbg(VFS, "secType %d not supported!\n", type); + return -ENOSYS; + } + + return 0; +} + +int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + struct sess_data *sess_data; + + if (ses == NULL) { + WARN(1, "%s: ses == NULL!", __func__); + return -EINVAL; + } + + sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); + if (!sess_data) + return -ENOMEM; + + sess_data->xid = xid; + sess_data->ses = ses; + sess_data->server = server; + sess_data->buf0_type = CIFS_NO_BUFFER; + sess_data->nls_cp = (struct nls_table *) nls_cp; + + rc = select_sec(sess_data); + if (rc) + goto out; + + while (sess_data->func) + sess_data->func(sess_data); + + /* Store result before we free sess_data */ + rc = sess_data->result; + +out: + kfree_sensitive(sess_data); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c new file mode 100644 index 000000000000..7d1b3fc014d9 --- /dev/null +++ b/fs/smb/client/smb1ops.c @@ -0,0 +1,1276 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SMB1 (CIFS) version specific operations + * + * Copyright (c) 2012, Jeff Layton + */ + +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifspdu.h" +#include "cifs_unicode.h" +#include "fs_context.h" + +/* + * An NT cancel request header looks just like the original request except: + * + * The Command is SMB_COM_NT_CANCEL + * The WordCount is zeroed out + * The ByteCount is zeroed out + * + * This function mangles an existing request buffer into a + * SMB_COM_NT_CANCEL request and then sends it. + */ +static int +send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, + struct mid_q_entry *mid) +{ + int rc = 0; + struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + /* -4 for RFC1001 length and +2 for BCC field */ + in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); + in_buf->Command = SMB_COM_NT_CANCEL; + in_buf->WordCount = 0; + put_bcc(0, in_buf); + + cifs_server_lock(server); + rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); + if (rc) { + cifs_server_unlock(server); + return rc; + } + + /* + * The response to this call was already factored into the sequence + * number when the call went out, so we must adjust it back downward + * after signing here. + */ + --server->sequence_number; + rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); + if (rc < 0) + server->sequence_number--; + + cifs_server_unlock(server); + + cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n", + get_mid(in_buf), rc); + + return rc; +} + +static bool +cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) +{ + return ob1->fid.netfid == ob2->fid.netfid; +} + +static unsigned int +cifs_read_data_offset(char *buf) +{ + READ_RSP *rsp = (READ_RSP *)buf; + return le16_to_cpu(rsp->DataOffset); +} + +static unsigned int +cifs_read_data_length(char *buf, bool in_remaining) +{ + READ_RSP *rsp = (READ_RSP *)buf; + /* It's a bug reading remaining data for SMB1 packets */ + WARN_ON(in_remaining); + return (le16_to_cpu(rsp->DataLengthHigh) << 16) + + le16_to_cpu(rsp->DataLength); +} + +static struct mid_q_entry * +cifs_find_mid(struct TCP_Server_Info *server, char *buffer) +{ + struct smb_hdr *buf = (struct smb_hdr *)buffer; + struct mid_q_entry *mid; + + spin_lock(&server->mid_lock); + list_for_each_entry(mid, &server->pending_mid_q, qhead) { + if (compare_mid(mid->mid, buf) && + mid->mid_state == MID_REQUEST_SUBMITTED && + le16_to_cpu(mid->command) == buf->Command) { + kref_get(&mid->refcount); + spin_unlock(&server->mid_lock); + return mid; + } + } + spin_unlock(&server->mid_lock); + return NULL; +} + +static void +cifs_add_credits(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) +{ + spin_lock(&server->req_lock); + server->credits += credits->value; + server->in_flight--; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); +} + +static void +cifs_set_credits(struct TCP_Server_Info *server, const int val) +{ + spin_lock(&server->req_lock); + server->credits = val; + server->oplocks = val > 1 ? enable_oplocks : false; + spin_unlock(&server->req_lock); +} + +static int * +cifs_get_credits_field(struct TCP_Server_Info *server, const int optype) +{ + return &server->credits; +} + +static unsigned int +cifs_get_credits(struct mid_q_entry *mid) +{ + return 1; +} + +/* + * Find a free multiplex id (SMB mid). Otherwise there could be + * mid collisions which might cause problems, demultiplexing the + * wrong response to this request. Multiplex ids could collide if + * one of a series requests takes much longer than the others, or + * if a very large number of long lived requests (byte range + * locks or FindNotify requests) are pending. No more than + * 64K-1 requests can be outstanding at one time. If no + * mids are available, return zero. A future optimization + * could make the combination of mids and uid the key we use + * to demultiplex on (rather than mid alone). + * In addition to the above check, the cifs demultiplex + * code already used the command code as a secondary + * check of the frame and if signing is negotiated the + * response would be discarded if the mid were the same + * but the signature was wrong. Since the mid is not put in the + * pending queue until later (when it is about to be dispatched) + * we do have to limit the number of outstanding requests + * to somewhat less than 64K-1 although it is hard to imagine + * so many threads being in the vfs at one time. + */ +static __u64 +cifs_get_next_mid(struct TCP_Server_Info *server) +{ + __u64 mid = 0; + __u16 last_mid, cur_mid; + bool collision, reconnect = false; + + spin_lock(&server->mid_lock); + + /* mid is 16 bit only for CIFS/SMB */ + cur_mid = (__u16)((server->CurrentMid) & 0xffff); + /* we do not want to loop forever */ + last_mid = cur_mid; + cur_mid++; + /* avoid 0xFFFF MID */ + if (cur_mid == 0xffff) + cur_mid++; + + /* + * This nested loop looks more expensive than it is. + * In practice the list of pending requests is short, + * fewer than 50, and the mids are likely to be unique + * on the first pass through the loop unless some request + * takes longer than the 64 thousand requests before it + * (and it would also have to have been a request that + * did not time out). + */ + while (cur_mid != last_mid) { + struct mid_q_entry *mid_entry; + unsigned int num_mids; + + collision = false; + if (cur_mid == 0) + cur_mid++; + + num_mids = 0; + list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { + ++num_mids; + if (mid_entry->mid == cur_mid && + mid_entry->mid_state == MID_REQUEST_SUBMITTED) { + /* This mid is in use, try a different one */ + collision = true; + break; + } + } + + /* + * if we have more than 32k mids in the list, then something + * is very wrong. Possibly a local user is trying to DoS the + * box by issuing long-running calls and SIGKILL'ing them. If + * we get to 2^16 mids then we're in big trouble as this + * function could loop forever. + * + * Go ahead and assign out the mid in this situation, but force + * an eventual reconnect to clean out the pending_mid_q. + */ + if (num_mids > 32768) + reconnect = true; + + if (!collision) { + mid = (__u64)cur_mid; + server->CurrentMid = mid; + break; + } + cur_mid++; + } + spin_unlock(&server->mid_lock); + + if (reconnect) { + cifs_signal_cifsd_for_reconnect(server, false); + } + + return mid; +} + +/* + return codes: + 0 not a transact2, or all data present + >0 transact2 with that much data missing + -EINVAL invalid transact2 + */ +static int +check2ndT2(char *buf) +{ + struct smb_hdr *pSMB = (struct smb_hdr *)buf; + struct smb_t2_rsp *pSMBt; + int remaining; + __u16 total_data_size, data_in_this_rsp; + + if (pSMB->Command != SMB_COM_TRANSACTION2) + return 0; + + /* check for plausible wct, bcc and t2 data and parm sizes */ + /* check for parm and data offset going beyond end of smb */ + if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ + cifs_dbg(FYI, "Invalid transact2 word count\n"); + return -EINVAL; + } + + pSMBt = (struct smb_t2_rsp *)pSMB; + + total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + + if (total_data_size == data_in_this_rsp) + return 0; + else if (total_data_size < data_in_this_rsp) { + cifs_dbg(FYI, "total data %d smaller than data in frame %d\n", + total_data_size, data_in_this_rsp); + return -EINVAL; + } + + remaining = total_data_size - data_in_this_rsp; + + cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n", + remaining); + if (total_data_size > CIFSMaxBufSize) { + cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n", + total_data_size, CIFSMaxBufSize); + return -EINVAL; + } + return remaining; +} + +static int +coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) +{ + struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; + struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; + char *data_area_of_tgt; + char *data_area_of_src; + int remaining; + unsigned int byte_count, total_in_tgt; + __u16 tgt_total_cnt, src_total_cnt, total_in_src; + + src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); + tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + + if (tgt_total_cnt != src_total_cnt) + cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n", + src_total_cnt, tgt_total_cnt); + + total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + + remaining = tgt_total_cnt - total_in_tgt; + + if (remaining < 0) { + cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n", + tgt_total_cnt, total_in_tgt); + return -EPROTO; + } + + if (remaining == 0) { + /* nothing to do, ignore */ + cifs_dbg(FYI, "no more data remains\n"); + return 0; + } + + total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); + if (remaining < total_in_src) + cifs_dbg(FYI, "transact2 2nd response contains too much data\n"); + + /* find end of first SMB data area */ + data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); + + /* validate target area */ + data_area_of_src = (char *)&pSMBs->hdr.Protocol + + get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); + + data_area_of_tgt += total_in_tgt; + + total_in_tgt += total_in_src; + /* is the result too big for the field? */ + if (total_in_tgt > USHRT_MAX) { + cifs_dbg(FYI, "coalesced DataCount too large (%u)\n", + total_in_tgt); + return -EPROTO; + } + put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); + + /* fix up the BCC */ + byte_count = get_bcc(target_hdr); + byte_count += total_in_src; + /* is the result too big for the field? */ + if (byte_count > USHRT_MAX) { + cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); + return -EPROTO; + } + put_bcc(byte_count, target_hdr); + + byte_count = be32_to_cpu(target_hdr->smb_buf_length); + byte_count += total_in_src; + /* don't allow buffer to overflow */ + if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", + byte_count); + return -ENOBUFS; + } + target_hdr->smb_buf_length = cpu_to_be32(byte_count); + + /* copy second buffer into end of first buffer */ + memcpy(data_area_of_tgt, data_area_of_src, total_in_src); + + if (remaining != total_in_src) { + /* more responses to go */ + cifs_dbg(FYI, "waiting for more secondary responses\n"); + return 1; + } + + /* we are done */ + cifs_dbg(FYI, "found the last secondary response\n"); + return 0; +} + +static void +cifs_downgrade_oplock(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + cifs_set_oplock_level(cinode, oplock); +} + +static bool +cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, + char *buf, int malformed) +{ + if (malformed) + return false; + if (check2ndT2(buf) <= 0) + return false; + mid->multiRsp = true; + if (mid->resp_buf) { + /* merge response - fix up 1st*/ + malformed = coalesce_t2(buf, mid->resp_buf); + if (malformed > 0) + return true; + /* All parts received or packet is malformed. */ + mid->multiEnd = true; + dequeue_mid(mid, malformed); + return true; + } + if (!server->large_buf) { + /*FIXME: switch to already allocated largebuf?*/ + cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n"); + } else { + /* Have first buffer */ + mid->resp_buf = buf; + mid->large_buf = true; + server->bigbuf = NULL; + } + return true; +} + +static bool +cifs_need_neg(struct TCP_Server_Info *server) +{ + return server->maxBuf == 0; +} + +static int +cifs_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + int rc; + rc = CIFSSMBNegotiate(xid, ses, server); + if (rc == -EAGAIN) { + /* retry only once on 1st time connection */ + set_credits(server, 1); + rc = CIFSSMBNegotiate(xid, ses, server); + if (rc == -EAGAIN) + rc = -EHOSTDOWN; + } + return rc; +} + +static unsigned int +cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int wsize; + + /* start with specified wsize, or default */ + if (ctx->wsize) + wsize = ctx->wsize; + else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) + wsize = CIFS_DEFAULT_IOSIZE; + else + wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; + + /* can server support 24-bit write sizes? (via UNIX extensions) */ + if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) + wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE); + + /* + * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set? + * Limit it to max buffer offered by the server, minus the size of the + * WRITEX header, not including the 4 byte RFC1001 length. + */ + if (!(server->capabilities & CAP_LARGE_WRITE_X) || + (!(server->capabilities & CAP_UNIX) && server->sign)) + wsize = min_t(unsigned int, wsize, + server->maxBuf - sizeof(WRITE_REQ) + 4); + + /* hard limit of CIFS_MAX_WSIZE */ + wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE); + + return wsize; +} + +static unsigned int +cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int rsize, defsize; + + /* + * Set default value... + * + * HACK alert! Ancient servers have very small buffers. Even though + * MS-CIFS indicates that servers are only limited by the client's + * bufsize for reads, testing against win98se shows that it throws + * INVALID_PARAMETER errors if you try to request too large a read. + * OS/2 just sends back short reads. + * + * If the server doesn't advertise CAP_LARGE_READ_X, then assume that + * it can't handle a read request larger than its MaxBufferSize either. + */ + if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP)) + defsize = CIFS_DEFAULT_IOSIZE; + else if (server->capabilities & CAP_LARGE_READ_X) + defsize = CIFS_DEFAULT_NON_POSIX_RSIZE; + else + defsize = server->maxBuf - sizeof(READ_RSP); + + rsize = ctx->rsize ? ctx->rsize : defsize; + + /* + * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to + * the client's MaxBufferSize. + */ + if (!(server->capabilities & CAP_LARGE_READ_X)) + rsize = min_t(unsigned int, CIFSMaxBufSize, rsize); + + /* hard limit of CIFS_MAX_RSIZE */ + rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE); + + return rsize; +} + +static void +cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb) +{ + CIFSSMBQFSDeviceInfo(xid, tcon); + CIFSSMBQFSAttributeInfo(xid, tcon); +} + +static int +cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path) +{ + int rc; + FILE_ALL_INFO *file_info; + + file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); + if (file_info == NULL) + return -ENOMEM; + + rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, + 0 /* not legacy */, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + + if (rc == -EOPNOTSUPP || rc == -EINVAL) + rc = SMBQueryInformation(xid, tcon, full_path, file_info, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + kfree(file_info); + return rc; +} + +static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink) +{ + int rc; + FILE_ALL_INFO fi = {}; + + *symlink = false; + + /* could do find first instead but this returns more info */ + rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + /* + * BB optimize code so we do not make the above call when server claims + * no NT SMB support and the above call failed at least once - set flag + * in tcon or mount. + */ + if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { + rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + *adjustTZ = true; + } + + if (!rc) { + int tmprc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + + move_cifs_info_to_smb2(&data->fi, &fi); + + if (!(le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) + return 0; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = FILE_READ_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + /* Need to check if this is a symbolic link or not */ + tmprc = CIFS_open(xid, &oparms, &oplock, NULL); + if (tmprc == -EOPNOTSUPP) + *symlink = true; + else if (tmprc == 0) + CIFSSMBClose(xid, tcon, fid.netfid); + } + + return rc; +} + +static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + u64 *uniqueid, struct cifs_open_info_data *unused) +{ + /* + * We can not use the IndexNumber field by default from Windows or + * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA + * CIFS spec claims that this value is unique within the scope of a + * share, and the windows docs hint that it's actually unique + * per-machine. + * + * There may be higher info levels that work but are there Windows + * server or network appliances for which IndexNumber field is not + * guaranteed unique? + */ + return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +} + +static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct cifs_open_info_data *data) +{ + int rc; + FILE_ALL_INFO fi = {}; + + if (cfile->symlink_target) { + data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!data->symlink_target) + return -ENOMEM; + } + + rc = CIFSSMBQFileInfo(xid, tcon, cfile->fid.netfid, &fi); + if (!rc) + move_cifs_info_to_smb2(&data->fi, &fi); + return rc; +} + +static void +cifs_clear_stats(struct cifs_tcon *tcon) +{ + atomic_set(&tcon->stats.cifs_stats.num_writes, 0); + atomic_set(&tcon->stats.cifs_stats.num_reads, 0); + atomic_set(&tcon->stats.cifs_stats.num_flushes, 0); + atomic_set(&tcon->stats.cifs_stats.num_oplock_brks, 0); + atomic_set(&tcon->stats.cifs_stats.num_opens, 0); + atomic_set(&tcon->stats.cifs_stats.num_posixopens, 0); + atomic_set(&tcon->stats.cifs_stats.num_posixmkdirs, 0); + atomic_set(&tcon->stats.cifs_stats.num_closes, 0); + atomic_set(&tcon->stats.cifs_stats.num_deletes, 0); + atomic_set(&tcon->stats.cifs_stats.num_mkdirs, 0); + atomic_set(&tcon->stats.cifs_stats.num_rmdirs, 0); + atomic_set(&tcon->stats.cifs_stats.num_renames, 0); + atomic_set(&tcon->stats.cifs_stats.num_t2renames, 0); + atomic_set(&tcon->stats.cifs_stats.num_ffirst, 0); + atomic_set(&tcon->stats.cifs_stats.num_fnext, 0); + atomic_set(&tcon->stats.cifs_stats.num_fclose, 0); + atomic_set(&tcon->stats.cifs_stats.num_hardlinks, 0); + atomic_set(&tcon->stats.cifs_stats.num_symlinks, 0); + atomic_set(&tcon->stats.cifs_stats.num_locks, 0); + atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0); + atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0); +} + +static void +cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon) +{ + seq_printf(m, " Oplocks breaks: %d", + atomic_read(&tcon->stats.cifs_stats.num_oplock_brks)); + seq_printf(m, "\nReads: %d Bytes: %llu", + atomic_read(&tcon->stats.cifs_stats.num_reads), + (long long)(tcon->bytes_read)); + seq_printf(m, "\nWrites: %d Bytes: %llu", + atomic_read(&tcon->stats.cifs_stats.num_writes), + (long long)(tcon->bytes_written)); + seq_printf(m, "\nFlushes: %d", + atomic_read(&tcon->stats.cifs_stats.num_flushes)); + seq_printf(m, "\nLocks: %d HardLinks: %d Symlinks: %d", + atomic_read(&tcon->stats.cifs_stats.num_locks), + atomic_read(&tcon->stats.cifs_stats.num_hardlinks), + atomic_read(&tcon->stats.cifs_stats.num_symlinks)); + seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", + atomic_read(&tcon->stats.cifs_stats.num_opens), + atomic_read(&tcon->stats.cifs_stats.num_closes), + atomic_read(&tcon->stats.cifs_stats.num_deletes)); + seq_printf(m, "\nPosix Opens: %d Posix Mkdirs: %d", + atomic_read(&tcon->stats.cifs_stats.num_posixopens), + atomic_read(&tcon->stats.cifs_stats.num_posixmkdirs)); + seq_printf(m, "\nMkdirs: %d Rmdirs: %d", + atomic_read(&tcon->stats.cifs_stats.num_mkdirs), + atomic_read(&tcon->stats.cifs_stats.num_rmdirs)); + seq_printf(m, "\nRenames: %d T2 Renames %d", + atomic_read(&tcon->stats.cifs_stats.num_renames), + atomic_read(&tcon->stats.cifs_stats.num_t2renames)); + seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", + atomic_read(&tcon->stats.cifs_stats.num_ffirst), + atomic_read(&tcon->stats.cifs_stats.num_fnext), + atomic_read(&tcon->stats.cifs_stats.num_fclose)); +} + +static void +cifs_mkdir_setinfo(struct inode *inode, const char *full_path, + struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, + const unsigned int xid) +{ + FILE_BASIC_INFO info; + struct cifsInodeInfo *cifsInode; + u32 dosattrs; + int rc; + + memset(&info, 0, sizeof(info)); + cifsInode = CIFS_I(inode); + dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; + info.Attributes = cpu_to_le32(dosattrs); + rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls, + cifs_sb); + if (rc == 0) + cifsInode->cifsAttrs = dosattrs; +} + +static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, + void *buf) +{ + struct cifs_open_info_data *data = buf; + FILE_ALL_INFO fi = {}; + int rc; + + if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS)) + rc = SMBLegacyOpen(xid, oparms->tcon, oparms->path, + oparms->disposition, + oparms->desired_access, + oparms->create_options, + &oparms->fid->netfid, oplock, &fi, + oparms->cifs_sb->local_nls, + cifs_remap(oparms->cifs_sb)); + else + rc = CIFS_open(xid, oparms, oplock, &fi); + + if (!rc && data) + move_cifs_info_to_smb2(&data->fi, &fi); + + return rc; +} + +static void +cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + cfile->fid.netfid = fid->netfid; + cifs_set_oplock_level(cinode, oplock); + cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); +} + +static void +cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + CIFSSMBClose(xid, tcon, fid->netfid); +} + +static int +cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return CIFSSMBFlush(xid, tcon, fid->netfid); +} + +static int +cifs_sync_read(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *bytes_read, + char **buf, int *buf_type) +{ + parms->netfid = pfid->netfid; + return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type); +} + +static int +cifs_sync_write(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *written, + struct kvec *iov, unsigned long nr_segs) +{ + + parms->netfid = pfid->netfid; + return CIFSSMBWrite2(xid, parms, written, iov, nr_segs); +} + +static int +smb_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid) +{ + int oplock = 0; + int rc; + __u32 netpid; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifsFileInfo *open_file; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon; + + /* if the file is already open for write, just use that fileid */ + open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY); + if (open_file) { + fid.netfid = open_file->fid.netfid; + netpid = open_file->pid; + tcon = tlink_tcon(open_file->tlink); + goto set_via_filehandle; + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + tlink = NULL; + goto out; + } + tcon = tlink_tcon(tlink); + + rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, + cifs_sb); + if (rc == 0) { + cinode->cifsAttrs = le32_to_cpu(buf->Attributes); + goto out; + } else if (rc != -EOPNOTSUPP && rc != -EINVAL) { + goto out; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc != 0) { + if (rc == -EIO) + rc = -EINVAL; + goto out; + } + + netpid = current->tgid; + +set_via_filehandle: + rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid); + if (!rc) + cinode->cifsAttrs = le32_to_cpu(buf->Attributes); + + if (open_file == NULL) + CIFSSMBClose(xid, tcon, fid.netfid); + else + cifsFileInfo_put(open_file); +out: + if (tlink != NULL) + cifs_put_tlink(tlink); + return rc; +} + +static int +cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid); +} + +static int +cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, struct cifs_sb_info *cifs_sb, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + int rc; + + rc = CIFSFindFirst(xid, tcon, path, cifs_sb, + &fid->netfid, search_flags, srch_inf, true); + if (rc) + cifs_dbg(FYI, "find first failed=%d\n", rc); + return rc; +} + +static int +cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf); +} + +static int +cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return CIFSFindClose(xid, tcon, fid->netfid); +} + +static int +cifs_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) +{ + return CIFSSMBLock(0, tcon, net_fid, current->tgid, 0, 0, 0, 0, + LOCKING_ANDX_OPLOCK_RELEASE, false, CIFS_CACHE_READ(cinode) ? 1 : 0); +} + +static int +cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct kstatfs *buf) +{ + int rc = -EOPNOTSUPP; + + buf->f_type = CIFS_SUPER_MAGIC; + + /* + * We could add a second check for a QFS Unix capability bit + */ + if ((tcon->ses->capabilities & CAP_UNIX) && + (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability))) + rc = CIFSSMBQFSPosixInfo(xid, tcon, buf); + + /* + * Only need to call the old QFSInfo if failed on newer one, + * e.g. by OS/2. + **/ + if (rc && (tcon->ses->capabilities & CAP_NT_SMBS)) + rc = CIFSSMBQFSInfo(xid, tcon, buf); + + /* + * Some old Windows servers also do not support level 103, retry with + * older level one if old server failed the previous call or we + * bypassed it because we detected that this was an older LANMAN sess + */ + if (rc) + rc = SMBOldQFSInfo(xid, tcon, buf); + return rc; +} + +static int +cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u32 type, int lock, int unlock, bool wait) +{ + return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid, + current->tgid, length, offset, unlock, lock, + (__u8)type, wait, 0); +} + +static int +cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, char **symlinkinfo, + const struct nls_table *nls_codepage) +{ +#ifdef CONFIG_CIFS_DFS_UPCALL + int rc; + struct dfs_info3_param referral = {0}; + + rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, &referral, + 0); + + if (!rc) { + *symlinkinfo = kstrdup(referral.node_name, GFP_KERNEL); + free_dfs_info_param(&referral); + if (!*symlinkinfo) + rc = -ENOMEM; + } + return rc; +#else /* No DFS support */ + return -EREMOTE; +#endif +} + +static int +cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + char **target_path, bool is_reparse_point) +{ + int rc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + + if (is_reparse_point) { + cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n"); + return -EOPNOTSUPP; + } + + /* Check for unix extensions */ + if (cap_unix(tcon->ses)) { + rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (rc == -EREMOTE) + rc = cifs_unix_dfs_readlink(xid, tcon, full_path, + target_path, + cifs_sb->local_nls); + + goto out; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = FILE_READ_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, + OPEN_REPARSE_POINT), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) + goto out; + + rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path, + cifs_sb->local_nls); + if (rc) + goto out_close; + + convert_delimiter(*target_path, '/'); +out_close: + CIFSSMBClose(xid, tcon, fid.netfid); +out: + if (!rc) + cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + return rc; +} + +static bool +cifs_is_read_op(__u32 oplock) +{ + return oplock == OPLOCK_READ; +} + +static unsigned int +cifs_wp_retry_size(struct inode *inode) +{ + return CIFS_SB(inode->i_sb)->ctx->wsize; +} + +static bool +cifs_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; +} + +static bool +cifs_can_echo(struct TCP_Server_Info *server) +{ + if (server->tcpStatus == CifsGood) + return true; + + return false; +} + +static int +cifs_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct inode *newinode = NULL; + int rc = -EPERM; + struct cifs_open_info_data buf = {}; + struct cifs_io_parms io_parms; + __u32 oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + unsigned int bytes_written; + struct win_dev *pdev; + struct kvec iov[2]; + + if (tcon->unix_ext) { + /* + * SMB1 Unix Extensions: requires server support but + * works with all special files + */ + struct cifs_unix_set_info_args args = { + .mode = mode & ~current_umask(), + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = dev, + }; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + args.uid = current_fsuid(); + args.gid = current_fsgid(); + } else { + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ + } + rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (rc) + return rc; + + rc = cifs_get_inode_info_unix(&newinode, full_path, + inode->i_sb, xid); + + if (rc == 0) + d_instantiate(dentry, newinode); + return rc; + } + + /* + * SMB1 SFU emulation: should work with all servers, but only + * support block and char device (no socket & fifo) + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) + return rc; + + if (!S_ISCHR(mode) && !S_ISBLK(mode)) + return rc; + + cifs_dbg(FYI, "sfu compat create special file\n"); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | + CREATE_OPTION_SPECIAL), + .disposition = FILE_CREATE, + .path = full_path, + .fid = &fid, + }; + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); + if (rc) + return rc; + + /* + * BB Do not bother to decode buf since no local inode yet to put + * timestamps in, but we can reuse it safely. + */ + + pdev = (struct win_dev *)&buf.fi; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = sizeof(struct win_dev); + iov[1].iov_base = &buf.fi; + iov[1].iov_len = sizeof(struct win_dev); + if (S_ISCHR(mode)) { + memcpy(pdev->type, "IntxCHR", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } else if (S_ISBLK(mode)) { + memcpy(pdev->type, "IntxBLK", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } + tcon->ses->server->ops->close(xid, tcon, &fid); + d_drop(dentry); + + /* FIXME: add code here to set EAs */ + + cifs_free_open_info(&buf); + return rc; +} + + + +struct smb_version_operations smb1_operations = { + .send_cancel = send_nt_cancel, + .compare_fids = cifs_compare_fids, + .setup_request = cifs_setup_request, + .setup_async_request = cifs_setup_async_request, + .check_receive = cifs_check_receive, + .add_credits = cifs_add_credits, + .set_credits = cifs_set_credits, + .get_credits_field = cifs_get_credits_field, + .get_credits = cifs_get_credits, + .wait_mtu_credits = cifs_wait_mtu_credits, + .get_next_mid = cifs_get_next_mid, + .read_data_offset = cifs_read_data_offset, + .read_data_length = cifs_read_data_length, + .map_error = map_smb_to_linux_error, + .find_mid = cifs_find_mid, + .check_message = checkSMB, + .dump_detail = cifs_dump_detail, + .clear_stats = cifs_clear_stats, + .print_stats = cifs_print_stats, + .is_oplock_break = is_valid_oplock_break, + .downgrade_oplock = cifs_downgrade_oplock, + .check_trans2 = cifs_check_trans2, + .need_neg = cifs_need_neg, + .negotiate = cifs_negotiate, + .negotiate_wsize = cifs_negotiate_wsize, + .negotiate_rsize = cifs_negotiate_rsize, + .sess_setup = CIFS_SessSetup, + .logoff = CIFSSMBLogoff, + .tree_connect = CIFSTCon, + .tree_disconnect = CIFSSMBTDis, + .get_dfs_refer = CIFSGetDFSRefer, + .qfs_tcon = cifs_qfs_tcon, + .is_path_accessible = cifs_is_path_accessible, + .can_echo = cifs_can_echo, + .query_path_info = cifs_query_path_info, + .query_file_info = cifs_query_file_info, + .get_srv_inum = cifs_get_srv_inum, + .set_path_size = CIFSSMBSetEOF, + .set_file_size = CIFSSMBSetFileSize, + .set_file_info = smb_set_file_info, + .set_compression = cifs_set_compression, + .echo = CIFSSMBEcho, + .mkdir = CIFSSMBMkDir, + .mkdir_setinfo = cifs_mkdir_setinfo, + .rmdir = CIFSSMBRmDir, + .unlink = CIFSSMBDelFile, + .rename_pending_delete = cifs_rename_pending_delete, + .rename = CIFSSMBRename, + .create_hardlink = CIFSCreateHardLink, + .query_symlink = cifs_query_symlink, + .open = cifs_open_file, + .set_fid = cifs_set_fid, + .close = cifs_close_file, + .flush = cifs_flush_file, + .async_readv = cifs_async_readv, + .async_writev = cifs_async_writev, + .sync_read = cifs_sync_read, + .sync_write = cifs_sync_write, + .query_dir_first = cifs_query_dir_first, + .query_dir_next = cifs_query_dir_next, + .close_dir = cifs_close_dir, + .calc_smb_size = smbCalcSize, + .oplock_response = cifs_oplock_response, + .queryfs = cifs_queryfs, + .mand_lock = cifs_mand_lock, + .mand_unlock_range = cifs_unlock_range, + .push_mand_locks = cifs_push_mandatory_locks, + .query_mf_symlink = cifs_query_mf_symlink, + .create_mf_symlink = cifs_create_mf_symlink, + .is_read_op = cifs_is_read_op, + .wp_retry_size = cifs_wp_retry_size, + .dir_needs_close = cifs_dir_needs_close, + .select_sectype = cifs_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = CIFSSMBQAllEAs, + .set_EA = CIFSSMBSetEA, +#endif /* CIFS_XATTR */ + .get_acl = get_cifs_acl, + .get_acl_by_fid = get_cifs_acl_by_fid, + .set_acl = set_cifs_acl, + .make_node = cifs_make_node, +}; + +struct smb_version_values smb1_values = { + .version_string = SMB1_VERSION_STRING, + .protocol_id = SMB10_PROT_ID, + .large_lock_type = LOCKING_ANDX_LARGE_FILES, + .exclusive_lock_type = 0, + .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, + .unlock_lock_type = 0, + .header_preamble_size = 4, + .header_size = sizeof(struct smb_hdr), + .max_header_size = MAX_CIFS_HDR_SIZE, + .read_rsp_size = sizeof(READ_RSP), + .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), + .cap_unix = CAP_UNIX, + .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, + .cap_large_files = CAP_LARGE_FILES, + .signing_enabled = SECMODE_SIGN_ENABLED, + .signing_required = SECMODE_SIGN_REQUIRED, +}; diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c new file mode 100644 index 000000000000..ba6cc50af390 --- /dev/null +++ b/fs/smb/client/smb2file.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Author(s): Steve French (sfrench@us.ibm.com), + * Pavel Shilovsky ((pshilovsky@samba.org) 2012 + * + */ +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "smb2proto.h" +#include "smb2status.h" + +static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) +{ + struct smb2_err_rsp *err = iov->iov_base; + struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL); + u32 len; + + if (err->ErrorContextCount) { + struct smb2_error_context_rsp *p, *end; + + len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp, + ErrorContextData) + + sizeof(struct smb2_symlink_err_rsp)); + if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err)) + return ERR_PTR(-EINVAL); + + p = (struct smb2_error_context_rsp *)err->ErrorData; + end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len); + do { + if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) { + sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData; + break; + } + cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n", + __func__, le32_to_cpu(p->ErrorId)); + + len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8); + p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len); + } while (p < end); + } else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) && + iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) { + sym = (struct smb2_symlink_err_rsp *)err->ErrorData; + } + + if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG || + le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK)) + sym = ERR_PTR(-EINVAL); + + return sym; +} + +int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path) +{ + struct smb2_symlink_err_rsp *sym; + unsigned int sub_offs, sub_len; + unsigned int print_offs, print_len; + char *s; + + if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path) + return -EINVAL; + + sym = symlink_data(iov); + if (IS_ERR(sym)) + return PTR_ERR(sym); + + sub_len = le16_to_cpu(sym->SubstituteNameLength); + sub_offs = le16_to_cpu(sym->SubstituteNameOffset); + print_len = le16_to_cpu(sym->PrintNameLength); + print_offs = le16_to_cpu(sym->PrintNameOffset); + + if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len || + iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len) + return -EINVAL; + + s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true, + cifs_sb->local_nls); + if (!s) + return -ENOMEM; + convert_delimiter(s, '/'); + cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s); + + *path = s; + return 0; +} + +int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf) +{ + int rc; + __le16 *smb2_path; + __u8 smb2_oplock; + struct cifs_open_info_data *data = buf; + struct smb2_file_all_info file_info = {}; + struct smb2_file_all_info *smb2_data = data ? &file_info : NULL; + struct kvec err_iov = {}; + int err_buftype = CIFS_NO_BUFFER; + struct cifs_fid *fid = oparms->fid; + struct network_resiliency_req nr_ioctl_req; + + smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); + if (smb2_path == NULL) + return -ENOMEM; + + oparms->desired_access |= FILE_READ_ATTRIBUTES; + smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; + + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, + &err_buftype); + if (rc && data) { + struct smb2_hdr *hdr = err_iov.iov_base; + + if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER)) + goto out; + if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) { + rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, + &data->symlink_target); + if (!rc) { + memset(smb2_data, 0, sizeof(*smb2_data)); + oparms->create_options |= OPEN_REPARSE_POINT; + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, + NULL, NULL, NULL); + oparms->create_options &= ~OPEN_REPARSE_POINT; + } + } + } + + if (rc) + goto out; + + if (oparms->tcon->use_resilient) { + /* default timeout is 0, servers pick default (120 seconds) */ + nr_ioctl_req.Timeout = + cpu_to_le32(oparms->tcon->handle_timeout); + nr_ioctl_req.Reserved = 0; + rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, + fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, + (char *)&nr_ioctl_req, sizeof(nr_ioctl_req), + CIFSMaxBufSize, NULL, NULL /* no return info */); + if (rc == -EOPNOTSUPP) { + cifs_dbg(VFS, + "resiliency not supported by server, disabling\n"); + oparms->tcon->use_resilient = false; + } else if (rc) + cifs_dbg(FYI, "error %d setting resiliency\n", rc); + + rc = 0; + } + + if (smb2_data) { + /* if open response does not have IndexNumber field - get it */ + if (smb2_data->IndexNumber == 0) { + rc = SMB2_get_srv_num(xid, oparms->tcon, + fid->persistent_fid, + fid->volatile_fid, + &smb2_data->IndexNumber); + if (rc) { + /* + * let get_inode_info disable server inode + * numbers + */ + smb2_data->IndexNumber = 0; + rc = 0; + } + } + memcpy(&data->fi, smb2_data, sizeof(data->fi)); + } + + *oplock = smb2_oplock; +out: + free_rsp_buf(err_buftype, err_iov.iov_base); + kfree(smb2_path); + return rc; +} + +int +smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, + const unsigned int xid) +{ + int rc = 0, stored_rc; + unsigned int max_num, num = 0, max_buf; + struct smb2_lock_element *buf, *cur; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifsLockInfo *li, *tmp; + __u64 length = 1 + flock->fl_end - flock->fl_start; + struct list_head tmp_llist; + + INIT_LIST_HEAD(&tmp_llist); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it before using. + */ + max_buf = tcon->ses->server->maxBuf; + if (max_buf < sizeof(struct smb2_lock_element)) + return -EINVAL; + + BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); + max_num = max_buf / sizeof(struct smb2_lock_element); + buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cur = buf; + + cifs_down_write(&cinode->lock_sem); + list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { + if (flock->fl_start > li->offset || + (flock->fl_start + length) < + (li->offset + li->length)) + continue; + if (current->tgid != li->pid) + /* + * flock and OFD lock are associated with an open + * file description, not the process. + */ + if (!(flock->fl_flags & (FL_FLOCK | FL_OFDLCK))) + continue; + if (cinode->can_cache_brlcks) { + /* + * We can cache brlock requests - simply remove a lock + * from the file's list. + */ + list_del(&li->llist); + cifs_del_lock_waiters(li); + kfree(li); + continue; + } + cur->Length = cpu_to_le64(li->length); + cur->Offset = cpu_to_le64(li->offset); + cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); + /* + * We need to save a lock here to let us add it again to the + * file's list if the unlock range request fails on the server. + */ + list_move(&li->llist, &tmp_llist); + if (++num == max_num) { + stored_rc = smb2_lockv(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, num, buf); + if (stored_rc) { + /* + * We failed on the unlock range request - add + * all locks from the tmp list to the head of + * the file's list. + */ + cifs_move_llist(&tmp_llist, + &cfile->llist->locks); + rc = stored_rc; + } else + /* + * The unlock range request succeed - free the + * tmp list. + */ + cifs_free_llist(&tmp_llist); + cur = buf; + num = 0; + } else + cur++; + } + if (num) { + stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, current->tgid, + num, buf); + if (stored_rc) { + cifs_move_llist(&tmp_llist, &cfile->llist->locks); + rc = stored_rc; + } else + cifs_free_llist(&tmp_llist); + } + up_write(&cinode->lock_sem); + + kfree(buf); + return rc; +} + +static int +smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid, + struct smb2_lock_element *buf, unsigned int max_num) +{ + int rc = 0, stored_rc; + struct cifsFileInfo *cfile = fdlocks->cfile; + struct cifsLockInfo *li; + unsigned int num = 0; + struct smb2_lock_element *cur = buf; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + + list_for_each_entry(li, &fdlocks->locks, llist) { + cur->Length = cpu_to_le64(li->length); + cur->Offset = cpu_to_le64(li->offset); + cur->Flags = cpu_to_le32(li->type | + SMB2_LOCKFLAG_FAIL_IMMEDIATELY); + if (++num == max_num) { + stored_rc = smb2_lockv(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, num, buf); + if (stored_rc) + rc = stored_rc; + cur = buf; + num = 0; + } else + cur++; + } + if (num) { + stored_rc = smb2_lockv(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, num, buf); + if (stored_rc) + rc = stored_rc; + } + + return rc; +} + +int +smb2_push_mandatory_locks(struct cifsFileInfo *cfile) +{ + int rc = 0, stored_rc; + unsigned int xid; + unsigned int max_num, max_buf; + struct smb2_lock_element *buf; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifs_fid_locks *fdlocks; + + xid = get_xid(); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it for zero before using. + */ + max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; + if (max_buf < sizeof(struct smb2_lock_element)) { + free_xid(xid); + return -EINVAL; + } + + BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); + max_num = max_buf / sizeof(struct smb2_lock_element); + buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); + if (!buf) { + free_xid(xid); + return -ENOMEM; + } + + list_for_each_entry(fdlocks, &cinode->llist, llist) { + stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num); + if (stored_rc) + rc = stored_rc; + } + + kfree(buf); + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h new file mode 100644 index 000000000000..82e916ad167c --- /dev/null +++ b/fs/smb/client/smb2glob.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Definitions for various global variables and structures + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ +#ifndef _SMB2_GLOB_H +#define _SMB2_GLOB_H + +/* + ***************************************************************** + * Constants go here + ***************************************************************** + */ + +/* + * Identifiers for functions that use the open, operation, close pattern + * in smb2inode.c:smb2_compound_op() + */ +#define SMB2_OP_SET_DELETE 1 +#define SMB2_OP_SET_INFO 2 +#define SMB2_OP_QUERY_INFO 3 +#define SMB2_OP_QUERY_DIR 4 +#define SMB2_OP_MKDIR 5 +#define SMB2_OP_RENAME 6 +#define SMB2_OP_DELETE 7 +#define SMB2_OP_HARDLINK 8 +#define SMB2_OP_SET_EOF 9 +#define SMB2_OP_RMDIR 10 +#define SMB2_OP_POSIX_QUERY_INFO 11 + +/* Used when constructing chained read requests. */ +#define CHAINED_REQUEST 1 +#define START_OF_CHAIN 2 +#define END_OF_CHAIN 4 +#define RELATED_REQUEST 8 + +#endif /* _SMB2_GLOB_H */ diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c new file mode 100644 index 000000000000..c97e049e29dd --- /dev/null +++ b/fs/smb/client/smb2inode.c @@ -0,0 +1,799 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Pavel Shilovsky (pshilovsky@samba.org), + * Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "smb2glob.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "cached_dir.h" +#include "smb2status.h" + +static void +free_set_inf_compound(struct smb_rqst *rqst) +{ + if (rqst[1].rq_iov) + SMB2_set_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); +} + + +struct cop_vars { + struct cifs_open_parms oparms; + struct kvec rsp_iov[3]; + struct smb_rqst rqst[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; + struct kvec close_iov[1]; + struct smb2_file_rename_info rename_info; + struct smb2_file_link_info link_info; +}; + +/* + * note: If cfile is passed, the reference to it is dropped here. + * So make sure that you do not reuse cfile after return from this func. + * + * If passing @err_iov and @err_buftype, ensure to make them both large enough (>= 3) to hold all + * error responses. Caller is also responsible for freeing them up. + */ +static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + __u32 desired_access, __u32 create_disposition, __u32 create_options, + umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile, + struct kvec *err_iov, int *err_buftype) +{ + struct cop_vars *vars = NULL; + struct kvec *rsp_iov; + struct smb_rqst *rqst; + int rc; + __le16 *utf16_path = NULL; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_fid fid; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server; + int num_rqst = 0; + int resp_buftype[3]; + struct smb2_query_info_rsp *qi_rsp = NULL; + struct cifs_open_info_data *idata; + int flags = 0; + __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; + unsigned int size[2]; + void *data[2]; + int len; + + vars = kzalloc(sizeof(*vars), GFP_ATOMIC); + if (vars == NULL) + return -ENOMEM; + rqst = &vars->rqst[0]; + rsp_iov = &vars->rsp_iov[0]; + + server = cifs_pick_channel(ses); + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + + /* We already have a handle so we can skip the open */ + if (cfile) + goto after_open; + + /* Open */ + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + goto finished; + } + + vars->oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = desired_access, + .disposition = create_disposition, + .create_options = cifs_create_options(cifs_sb, create_options), + .fid = &fid, + .mode = mode, + .cifs_sb = cifs_sb, + }; + + rqst[num_rqst].rq_iov = &vars->open_iov[0]; + rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; + rc = SMB2_open_init(tcon, server, + &rqst[num_rqst], &oplock, &vars->oparms, + utf16_path); + kfree(utf16_path); + if (rc) + goto finished; + + smb2_set_next_command(tcon, &rqst[num_rqst]); + after_open: + num_rqst++; + rc = 0; + + /* Operation */ + switch (command) { + case SMB2_OP_QUERY_INFO: + rqst[num_rqst].rq_iov = &vars->qi_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + if (cfile) + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FILE_ALL_INFORMATION, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2, 0, NULL); + else { + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, + FILE_ALL_INFORMATION, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2, 0, NULL); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + + if (rc) + goto finished; + num_rqst++; + trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, + full_path); + break; + case SMB2_OP_POSIX_QUERY_INFO: + rqst[num_rqst].rq_iov = &vars->qi_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + if (cfile) + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + SMB_FIND_FILE_POSIX_INFO, + SMB2_O_INFO_FILE, 0, + /* TBD: fix following to allow for longer SIDs */ + sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + + (sizeof(struct cifs_sid) * 2), 0, NULL); + else { + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, + SMB_FIND_FILE_POSIX_INFO, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + + (sizeof(struct cifs_sid) * 2), 0, NULL); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + + if (rc) + goto finished; + num_rqst++; + trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_DELETE: + trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_MKDIR: + /* + * Directories are created through parameters in the + * SMB2_open() call. + */ + trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_RMDIR: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ + data[0] = &delete_pending[0]; + + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_DISPOSITION_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (rc) + goto finished; + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_SET_EOF: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + size[0] = 8; /* sizeof __le64 */ + data[0] = ptr; + + if (cfile) { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, + FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, + data, size); + } else { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, + current->tgid, + FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, + data, size); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + if (rc) + goto finished; + num_rqst++; + trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_SET_INFO: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + + size[0] = sizeof(FILE_BASIC_INFO); + data[0] = ptr; + + if (cfile) + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, current->tgid, + FILE_BASIC_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + else { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_BASIC_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + + if (rc) + goto finished; + num_rqst++; + trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, + full_path); + break; + case SMB2_OP_RENAME: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 2; + + len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); + + vars->rename_info.ReplaceIfExists = 1; + vars->rename_info.RootDirectory = 0; + vars->rename_info.FileNameLength = cpu_to_le32(len); + + size[0] = sizeof(struct smb2_file_rename_info); + data[0] = &vars->rename_info; + + size[1] = len + 2 /* null */; + data[1] = (__le16 *)ptr; + + if (cfile) + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, FILE_RENAME_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + else { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, COMPOUND_FID, + current->tgid, FILE_RENAME_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + if (rc) + goto finished; + num_rqst++; + trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_HARDLINK: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 2; + + len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); + + vars->link_info.ReplaceIfExists = 0; + vars->link_info.RootDirectory = 0; + vars->link_info.FileNameLength = cpu_to_le32(len); + + size[0] = sizeof(struct smb2_file_link_info); + data[0] = &vars->link_info; + + size[1] = len + 2 /* null */; + data[1] = (__le16 *)ptr; + + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_LINK_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (rc) + goto finished; + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); + break; + default: + cifs_dbg(VFS, "Invalid command\n"); + rc = -EINVAL; + } + if (rc) + goto finished; + + /* We already have a handle so we can skip the close */ + if (cfile) + goto after_close; + /* Close */ + flags |= CIFS_CP_CREATE_CLOSE_OP; + rqst[num_rqst].rq_iov = &vars->close_iov[0]; + rqst[num_rqst].rq_nvec = 1; + rc = SMB2_close_init(tcon, server, + &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, false); + smb2_set_related(&rqst[num_rqst]); + if (rc) + goto finished; + after_close: + num_rqst++; + + if (cfile) { + rc = compound_send_recv(xid, ses, server, + flags, num_rqst - 2, + &rqst[1], &resp_buftype[1], + &rsp_iov[1]); + } else + rc = compound_send_recv(xid, ses, server, + flags, num_rqst, + rqst, resp_buftype, + rsp_iov); + + finished: + if (cfile) + cifsFileInfo_put(cfile); + + SMB2_open_free(&rqst[0]); + if (rc == -EREMCHG) { + pr_warn_once("server share %s deleted\n", tcon->tree_name); + tcon->need_reconnect = true; + } + + switch (command) { + case SMB2_OP_QUERY_INFO: + idata = ptr; + if (rc == 0 && cfile && cfile->symlink_target) { + idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!idata->symlink_target) + rc = -ENOMEM; + } + if (rc == 0) { + qi_rsp = (struct smb2_query_info_rsp *) + rsp_iov[1].iov_base; + rc = smb2_validate_and_copy_iov( + le16_to_cpu(qi_rsp->OutputBufferOffset), + le32_to_cpu(qi_rsp->OutputBufferLength), + &rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi); + } + if (rqst[1].rq_iov) + SMB2_query_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); + if (rc) + trace_smb3_query_info_compound_err(xid, ses->Suid, + tcon->tid, rc); + else + trace_smb3_query_info_compound_done(xid, ses->Suid, + tcon->tid); + break; + case SMB2_OP_POSIX_QUERY_INFO: + idata = ptr; + if (rc == 0 && cfile && cfile->symlink_target) { + idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!idata->symlink_target) + rc = -ENOMEM; + } + if (rc == 0) { + qi_rsp = (struct smb2_query_info_rsp *) + rsp_iov[1].iov_base; + rc = smb2_validate_and_copy_iov( + le16_to_cpu(qi_rsp->OutputBufferOffset), + le32_to_cpu(qi_rsp->OutputBufferLength), + &rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */, + (char *)&idata->posix_fi); + } + if (rqst[1].rq_iov) + SMB2_query_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); + if (rc) + trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid); + break; + case SMB2_OP_DELETE: + if (rc) + trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_delete_done(xid, ses->Suid, tcon->tid); + if (rqst[1].rq_iov) + SMB2_close_free(&rqst[1]); + break; + case SMB2_OP_MKDIR: + if (rc) + trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); + if (rqst[1].rq_iov) + SMB2_close_free(&rqst[1]); + break; + case SMB2_OP_HARDLINK: + if (rc) + trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_RENAME: + if (rc) + trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_rename_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_RMDIR: + if (rc) + trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_SET_EOF: + if (rc) + trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_SET_INFO: + if (rc) + trace_smb3_set_info_compound_err(xid, ses->Suid, + tcon->tid, rc); + else + trace_smb3_set_info_compound_done(xid, ses->Suid, + tcon->tid); + free_set_inf_compound(rqst); + break; + } + + if (rc && err_iov && err_buftype) { + memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov)); + memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype)); + } else { + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + } + kfree(vars); + return rc; +} + +int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) +{ + __u32 create_options = 0; + struct cifsFileInfo *cfile; + struct cached_fid *cfid = NULL; + struct kvec err_iov[3] = {}; + int err_buftype[3] = {}; + bool islink; + int rc, rc2; + + *adjust_tz = false; + *reparse = false; + + if (strcmp(full_path, "")) + rc = -ENOENT; + else + rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); + /* If it is a root and its handle is cached then use it */ + if (!rc) { + if (cfid->file_all_info_is_valid) { + memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi)); + } else { + rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid, + cfid->fid.volatile_fid, &data->fi); + } + close_cached_dir(cfid); + return rc; + } + + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile, + err_iov, err_buftype); + if (rc) { + struct smb2_hdr *hdr = err_iov[0].iov_base; + + if (unlikely(!hdr || err_buftype[0] == CIFS_NO_BUFFER)) + goto out; + if (rc == -EOPNOTSUPP && hdr->Command == SMB2_CREATE && + hdr->Status == STATUS_STOPPED_ON_SYMLINK) { + rc = smb2_parse_symlink_response(cifs_sb, err_iov, + &data->symlink_target); + if (rc) + goto out; + + *reparse = true; + create_options |= OPEN_REPARSE_POINT; + + /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, data, + SMB2_OP_QUERY_INFO, cfile, NULL, NULL); + goto out; + } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, + full_path, &islink); + if (rc2) { + rc = rc2; + goto out; + } + if (islink) + rc = -EREMOTE; + } + if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) + rc = -EOPNOTSUPP; + } + +out: + free_rsp_buf(err_buftype[0], err_iov[0].iov_base); + free_rsp_buf(err_buftype[1], err_iov[1].iov_base); + free_rsp_buf(err_buftype[2], err_iov[2].iov_base); + return rc; +} + + +int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) +{ + int rc; + __u32 create_options = 0; + struct cifsFileInfo *cfile; + struct kvec err_iov[3] = {}; + int err_buftype[3] = {}; + + *adjust_tz = false; + *reparse = false; + + /* + * BB TODO: Add support for using the cached root handle. + * Create SMB2_query_posix_info worker function to do non-compounded query + * when we already have an open file handle for this. For now this is fast enough + * (always using the compounded version). + */ + + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile, + err_iov, err_buftype); + if (rc == -EOPNOTSUPP) { + /* BB TODO: When support for special files added to Samba re-verify this path */ + if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER && + ((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE && + ((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) { + rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target); + if (rc) + goto out; + } + *reparse = true; + create_options |= OPEN_REPARSE_POINT; + + /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, + FILE_OPEN, create_options, ACL_NO_MODE, data, + SMB2_OP_POSIX_QUERY_INFO, cfile, NULL, NULL); + } + +out: + free_rsp_buf(err_buftype[0], err_iov[0].iov_base); + free_rsp_buf(err_buftype[1], err_iov[1].iov_base); + free_rsp_buf(err_buftype[2], err_iov[2].iov_base); + return rc; +} + +int +smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + return smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR, + NULL, NULL, NULL); +} + +void +smb2_mkdir_setinfo(struct inode *inode, const char *name, + struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, + const unsigned int xid) +{ + FILE_BASIC_INFO data; + struct cifsInodeInfo *cifs_i; + struct cifsFileInfo *cfile; + u32 dosattrs; + int tmprc; + + memset(&data, 0, sizeof(data)); + cifs_i = CIFS_I(inode); + dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; + data.Attributes = cpu_to_le32(dosattrs); + cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); + tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, ACL_NO_MODE, + &data, SMB2_OP_SET_INFO, cfile, NULL, NULL); + if (tmprc == 0) + cifs_i->cifsAttrs = dosattrs; +} + +int +smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + drop_cached_dir_by_name(xid, tcon, name, cifs_sb); + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_NOT_FILE, ACL_NO_MODE, + NULL, SMB2_OP_RMDIR, NULL, NULL, NULL); +} + +int +smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, + ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL); +} + +static int +smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb, __u32 access, int command, + struct cifsFileInfo *cfile) +{ + __le16 *smb2_to_name = NULL; + int rc; + + smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb); + if (smb2_to_name == NULL) { + rc = -ENOMEM; + goto smb2_rename_path; + } + rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, + FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name, + command, cfile, NULL, NULL); +smb2_rename_path: + kfree(smb2_to_name); + return rc; +} + +int +smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + struct cifsFileInfo *cfile; + + drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb); + cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); + + return smb2_set_path_attr(xid, tcon, from_name, to_name, + cifs_sb, DELETE, SMB2_OP_RENAME, cfile); +} + +int +smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, + FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK, + NULL); +} + +int +smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_alloc) +{ + __le64 eof = cpu_to_le64(size); + struct cifsFileInfo *cfile; + + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + return smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, + &eof, SMB2_OP_SET_EOF, cfile, NULL, NULL); +} + +int +smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + int rc; + + if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && + (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) && + (buf->Attributes == 0)) + return 0; /* would be a no op, no sense sending this */ + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_ATTRIBUTES, FILE_OPEN, + 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile, + NULL, NULL); + cifs_put_tlink(tlink); + return rc; +} diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c new file mode 100644 index 000000000000..194799ddd382 --- /dev/null +++ b/fs/smb/client/smb2maperror.c @@ -0,0 +1,2481 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Functions which do error mapping of SMB2 status codes to POSIX errors + * + * Copyright (C) International Business Machines Corp., 2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include "cifsglob.h" +#include "cifs_debug.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "trace.h" + +struct status_to_posix_error { + __le32 smb2_status; + int posix_error; + char *status_string; +}; + +static const struct status_to_posix_error smb2_error_map_table[] = { + {STATUS_SUCCESS, 0, "STATUS_SUCCESS"}, + {STATUS_WAIT_0, 0, "STATUS_WAIT_0"}, + {STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"}, + {STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"}, + {STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"}, + {STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"}, + {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED"}, + {STATUS_ABANDONED_WAIT_0, -EIO, "STATUS_ABANDONED_WAIT_0"}, + {STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"}, + {STATUS_USER_APC, -EIO, "STATUS_USER_APC"}, + {STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"}, + {STATUS_ALERTED, -EIO, "STATUS_ALERTED"}, + {STATUS_TIMEOUT, -ETIMEDOUT, "STATUS_TIMEOUT"}, + {STATUS_PENDING, -EIO, "STATUS_PENDING"}, + {STATUS_REPARSE, -EIO, "STATUS_REPARSE"}, + {STATUS_MORE_ENTRIES, -EIO, "STATUS_MORE_ENTRIES"}, + {STATUS_NOT_ALL_ASSIGNED, -EIO, "STATUS_NOT_ALL_ASSIGNED"}, + {STATUS_SOME_NOT_MAPPED, -EIO, "STATUS_SOME_NOT_MAPPED"}, + {STATUS_OPLOCK_BREAK_IN_PROGRESS, -EIO, + "STATUS_OPLOCK_BREAK_IN_PROGRESS"}, + {STATUS_VOLUME_MOUNTED, -EIO, "STATUS_VOLUME_MOUNTED"}, + {STATUS_RXACT_COMMITTED, -EIO, "STATUS_RXACT_COMMITTED"}, + {STATUS_NOTIFY_CLEANUP, -EIO, "STATUS_NOTIFY_CLEANUP"}, + {STATUS_NOTIFY_ENUM_DIR, -EIO, "STATUS_NOTIFY_ENUM_DIR"}, + {STATUS_NO_QUOTAS_FOR_ACCOUNT, -EIO, "STATUS_NO_QUOTAS_FOR_ACCOUNT"}, + {STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED, -EIO, + "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED"}, + {STATUS_PAGE_FAULT_TRANSITION, -EIO, "STATUS_PAGE_FAULT_TRANSITION"}, + {STATUS_PAGE_FAULT_DEMAND_ZERO, -EIO, "STATUS_PAGE_FAULT_DEMAND_ZERO"}, + {STATUS_PAGE_FAULT_COPY_ON_WRITE, -EIO, + "STATUS_PAGE_FAULT_COPY_ON_WRITE"}, + {STATUS_PAGE_FAULT_GUARD_PAGE, -EIO, "STATUS_PAGE_FAULT_GUARD_PAGE"}, + {STATUS_PAGE_FAULT_PAGING_FILE, -EIO, "STATUS_PAGE_FAULT_PAGING_FILE"}, + {STATUS_CACHE_PAGE_LOCKED, -EIO, "STATUS_CACHE_PAGE_LOCKED"}, + {STATUS_CRASH_DUMP, -EIO, "STATUS_CRASH_DUMP"}, + {STATUS_BUFFER_ALL_ZEROS, -EIO, "STATUS_BUFFER_ALL_ZEROS"}, + {STATUS_REPARSE_OBJECT, -EIO, "STATUS_REPARSE_OBJECT"}, + {STATUS_RESOURCE_REQUIREMENTS_CHANGED, -EIO, + "STATUS_RESOURCE_REQUIREMENTS_CHANGED"}, + {STATUS_TRANSLATION_COMPLETE, -EIO, "STATUS_TRANSLATION_COMPLETE"}, + {STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, -EIO, + "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY"}, + {STATUS_NOTHING_TO_TERMINATE, -EIO, "STATUS_NOTHING_TO_TERMINATE"}, + {STATUS_PROCESS_NOT_IN_JOB, -EIO, "STATUS_PROCESS_NOT_IN_JOB"}, + {STATUS_PROCESS_IN_JOB, -EIO, "STATUS_PROCESS_IN_JOB"}, + {STATUS_VOLSNAP_HIBERNATE_READY, -EIO, + "STATUS_VOLSNAP_HIBERNATE_READY"}, + {STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, -EIO, + "STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY"}, + {STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, -EIO, + "STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED"}, + {STATUS_INTERRUPT_STILL_CONNECTED, -EIO, + "STATUS_INTERRUPT_STILL_CONNECTED"}, + {STATUS_PROCESS_CLONED, -EIO, "STATUS_PROCESS_CLONED"}, + {STATUS_FILE_LOCKED_WITH_ONLY_READERS, -EIO, + "STATUS_FILE_LOCKED_WITH_ONLY_READERS"}, + {STATUS_FILE_LOCKED_WITH_WRITERS, -EIO, + "STATUS_FILE_LOCKED_WITH_WRITERS"}, + {STATUS_RESOURCEMANAGER_READ_ONLY, -EROFS, + "STATUS_RESOURCEMANAGER_READ_ONLY"}, + {STATUS_WAIT_FOR_OPLOCK, -EIO, "STATUS_WAIT_FOR_OPLOCK"}, + {DBG_EXCEPTION_HANDLED, -EIO, "DBG_EXCEPTION_HANDLED"}, + {DBG_CONTINUE, -EIO, "DBG_CONTINUE"}, + {STATUS_FLT_IO_COMPLETE, -EIO, "STATUS_FLT_IO_COMPLETE"}, + {STATUS_OBJECT_NAME_EXISTS, -EIO, "STATUS_OBJECT_NAME_EXISTS"}, + {STATUS_THREAD_WAS_SUSPENDED, -EIO, "STATUS_THREAD_WAS_SUSPENDED"}, + {STATUS_WORKING_SET_LIMIT_RANGE, -EIO, + "STATUS_WORKING_SET_LIMIT_RANGE"}, + {STATUS_IMAGE_NOT_AT_BASE, -EIO, "STATUS_IMAGE_NOT_AT_BASE"}, + {STATUS_RXACT_STATE_CREATED, -EIO, "STATUS_RXACT_STATE_CREATED"}, + {STATUS_SEGMENT_NOTIFICATION, -EIO, "STATUS_SEGMENT_NOTIFICATION"}, + {STATUS_LOCAL_USER_SESSION_KEY, -EIO, "STATUS_LOCAL_USER_SESSION_KEY"}, + {STATUS_BAD_CURRENT_DIRECTORY, -EIO, "STATUS_BAD_CURRENT_DIRECTORY"}, + {STATUS_SERIAL_MORE_WRITES, -EIO, "STATUS_SERIAL_MORE_WRITES"}, + {STATUS_REGISTRY_RECOVERED, -EIO, "STATUS_REGISTRY_RECOVERED"}, + {STATUS_FT_READ_RECOVERY_FROM_BACKUP, -EIO, + "STATUS_FT_READ_RECOVERY_FROM_BACKUP"}, + {STATUS_FT_WRITE_RECOVERY, -EIO, "STATUS_FT_WRITE_RECOVERY"}, + {STATUS_SERIAL_COUNTER_TIMEOUT, -ETIMEDOUT, + "STATUS_SERIAL_COUNTER_TIMEOUT"}, + {STATUS_NULL_LM_PASSWORD, -EIO, "STATUS_NULL_LM_PASSWORD"}, + {STATUS_IMAGE_MACHINE_TYPE_MISMATCH, -EIO, + "STATUS_IMAGE_MACHINE_TYPE_MISMATCH"}, + {STATUS_RECEIVE_PARTIAL, -EIO, "STATUS_RECEIVE_PARTIAL"}, + {STATUS_RECEIVE_EXPEDITED, -EIO, "STATUS_RECEIVE_EXPEDITED"}, + {STATUS_RECEIVE_PARTIAL_EXPEDITED, -EIO, + "STATUS_RECEIVE_PARTIAL_EXPEDITED"}, + {STATUS_EVENT_DONE, -EIO, "STATUS_EVENT_DONE"}, + {STATUS_EVENT_PENDING, -EIO, "STATUS_EVENT_PENDING"}, + {STATUS_CHECKING_FILE_SYSTEM, -EIO, "STATUS_CHECKING_FILE_SYSTEM"}, + {STATUS_FATAL_APP_EXIT, -EIO, "STATUS_FATAL_APP_EXIT"}, + {STATUS_PREDEFINED_HANDLE, -EIO, "STATUS_PREDEFINED_HANDLE"}, + {STATUS_WAS_UNLOCKED, -EIO, "STATUS_WAS_UNLOCKED"}, + {STATUS_SERVICE_NOTIFICATION, -EIO, "STATUS_SERVICE_NOTIFICATION"}, + {STATUS_WAS_LOCKED, -EIO, "STATUS_WAS_LOCKED"}, + {STATUS_LOG_HARD_ERROR, -EIO, "STATUS_LOG_HARD_ERROR"}, + {STATUS_ALREADY_WIN32, -EIO, "STATUS_ALREADY_WIN32"}, + {STATUS_WX86_UNSIMULATE, -EIO, "STATUS_WX86_UNSIMULATE"}, + {STATUS_WX86_CONTINUE, -EIO, "STATUS_WX86_CONTINUE"}, + {STATUS_WX86_SINGLE_STEP, -EIO, "STATUS_WX86_SINGLE_STEP"}, + {STATUS_WX86_BREAKPOINT, -EIO, "STATUS_WX86_BREAKPOINT"}, + {STATUS_WX86_EXCEPTION_CONTINUE, -EIO, + "STATUS_WX86_EXCEPTION_CONTINUE"}, + {STATUS_WX86_EXCEPTION_LASTCHANCE, -EIO, + "STATUS_WX86_EXCEPTION_LASTCHANCE"}, + {STATUS_WX86_EXCEPTION_CHAIN, -EIO, "STATUS_WX86_EXCEPTION_CHAIN"}, + {STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, -EIO, + "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE"}, + {STATUS_NO_YIELD_PERFORMED, -EIO, "STATUS_NO_YIELD_PERFORMED"}, + {STATUS_TIMER_RESUME_IGNORED, -EIO, "STATUS_TIMER_RESUME_IGNORED"}, + {STATUS_ARBITRATION_UNHANDLED, -EIO, "STATUS_ARBITRATION_UNHANDLED"}, + {STATUS_CARDBUS_NOT_SUPPORTED, -ENOSYS, "STATUS_CARDBUS_NOT_SUPPORTED"}, + {STATUS_WX86_CREATEWX86TIB, -EIO, "STATUS_WX86_CREATEWX86TIB"}, + {STATUS_MP_PROCESSOR_MISMATCH, -EIO, "STATUS_MP_PROCESSOR_MISMATCH"}, + {STATUS_HIBERNATED, -EIO, "STATUS_HIBERNATED"}, + {STATUS_RESUME_HIBERNATION, -EIO, "STATUS_RESUME_HIBERNATION"}, + {STATUS_FIRMWARE_UPDATED, -EIO, "STATUS_FIRMWARE_UPDATED"}, + {STATUS_DRIVERS_LEAKING_LOCKED_PAGES, -EIO, + "STATUS_DRIVERS_LEAKING_LOCKED_PAGES"}, + {STATUS_MESSAGE_RETRIEVED, -EIO, "STATUS_MESSAGE_RETRIEVED"}, + {STATUS_SYSTEM_POWERSTATE_TRANSITION, -EIO, + "STATUS_SYSTEM_POWERSTATE_TRANSITION"}, + {STATUS_ALPC_CHECK_COMPLETION_LIST, -EIO, + "STATUS_ALPC_CHECK_COMPLETION_LIST"}, + {STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, -EIO, + "STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION"}, + {STATUS_ACCESS_AUDIT_BY_POLICY, -EIO, "STATUS_ACCESS_AUDIT_BY_POLICY"}, + {STATUS_ABANDON_HIBERFILE, -EIO, "STATUS_ABANDON_HIBERFILE"}, + {STATUS_BIZRULES_NOT_ENABLED, -EIO, "STATUS_BIZRULES_NOT_ENABLED"}, + {STATUS_WAKE_SYSTEM, -EIO, "STATUS_WAKE_SYSTEM"}, + {STATUS_DS_SHUTTING_DOWN, -EIO, "STATUS_DS_SHUTTING_DOWN"}, + {DBG_REPLY_LATER, -EIO, "DBG_REPLY_LATER"}, + {DBG_UNABLE_TO_PROVIDE_HANDLE, -EIO, "DBG_UNABLE_TO_PROVIDE_HANDLE"}, + {DBG_TERMINATE_THREAD, -EIO, "DBG_TERMINATE_THREAD"}, + {DBG_TERMINATE_PROCESS, -EIO, "DBG_TERMINATE_PROCESS"}, + {DBG_CONTROL_C, -EIO, "DBG_CONTROL_C"}, + {DBG_PRINTEXCEPTION_C, -EIO, "DBG_PRINTEXCEPTION_C"}, + {DBG_RIPEXCEPTION, -EIO, "DBG_RIPEXCEPTION"}, + {DBG_CONTROL_BREAK, -EIO, "DBG_CONTROL_BREAK"}, + {DBG_COMMAND_EXCEPTION, -EIO, "DBG_COMMAND_EXCEPTION"}, + {RPC_NT_UUID_LOCAL_ONLY, -EIO, "RPC_NT_UUID_LOCAL_ONLY"}, + {RPC_NT_SEND_INCOMPLETE, -EIO, "RPC_NT_SEND_INCOMPLETE"}, + {STATUS_CTX_CDM_CONNECT, -EIO, "STATUS_CTX_CDM_CONNECT"}, + {STATUS_CTX_CDM_DISCONNECT, -EIO, "STATUS_CTX_CDM_DISCONNECT"}, + {STATUS_SXS_RELEASE_ACTIVATION_CONTEXT, -EIO, + "STATUS_SXS_RELEASE_ACTIVATION_CONTEXT"}, + {STATUS_RECOVERY_NOT_NEEDED, -EIO, "STATUS_RECOVERY_NOT_NEEDED"}, + {STATUS_RM_ALREADY_STARTED, -EIO, "STATUS_RM_ALREADY_STARTED"}, + {STATUS_LOG_NO_RESTART, -EIO, "STATUS_LOG_NO_RESTART"}, + {STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST, -EIO, + "STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST"}, + {STATUS_GRAPHICS_PARTIAL_DATA_POPULATED, -EIO, + "STATUS_GRAPHICS_PARTIAL_DATA_POPULATED"}, + {STATUS_GRAPHICS_DRIVER_MISMATCH, -EIO, + "STATUS_GRAPHICS_DRIVER_MISMATCH"}, + {STATUS_GRAPHICS_MODE_NOT_PINNED, -EIO, + "STATUS_GRAPHICS_MODE_NOT_PINNED"}, + {STATUS_GRAPHICS_NO_PREFERRED_MODE, -EIO, + "STATUS_GRAPHICS_NO_PREFERRED_MODE"}, + {STATUS_GRAPHICS_DATASET_IS_EMPTY, -EIO, + "STATUS_GRAPHICS_DATASET_IS_EMPTY"}, + {STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET, -EIO, + "STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET"}, + {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED, -EIO, + "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED"}, + {STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS, -EIO, + "STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS"}, + {STATUS_GRAPHICS_LEADLINK_START_DEFERRED, -EIO, + "STATUS_GRAPHICS_LEADLINK_START_DEFERRED"}, + {STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY, -EIO, + "STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY"}, + {STATUS_GRAPHICS_START_DEFERRED, -EIO, + "STATUS_GRAPHICS_START_DEFERRED"}, + {STATUS_NDIS_INDICATION_REQUIRED, -EIO, + "STATUS_NDIS_INDICATION_REQUIRED"}, + {STATUS_GUARD_PAGE_VIOLATION, -EIO, "STATUS_GUARD_PAGE_VIOLATION"}, + {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, + {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, + {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, + {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"}, + {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, + {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, + {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, + {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, + {STATUS_GUID_SUBSTITUTION_MADE, -EIO, "STATUS_GUID_SUBSTITUTION_MADE"}, + {STATUS_PARTIAL_COPY, -EIO, "STATUS_PARTIAL_COPY"}, + {STATUS_DEVICE_PAPER_EMPTY, -EIO, "STATUS_DEVICE_PAPER_EMPTY"}, + {STATUS_DEVICE_POWERED_OFF, -EIO, "STATUS_DEVICE_POWERED_OFF"}, + {STATUS_DEVICE_OFF_LINE, -EIO, "STATUS_DEVICE_OFF_LINE"}, + {STATUS_DEVICE_BUSY, -EBUSY, "STATUS_DEVICE_BUSY"}, + {STATUS_NO_MORE_EAS, -EIO, "STATUS_NO_MORE_EAS"}, + {STATUS_INVALID_EA_NAME, -EINVAL, "STATUS_INVALID_EA_NAME"}, + {STATUS_EA_LIST_INCONSISTENT, -EIO, "STATUS_EA_LIST_INCONSISTENT"}, + {STATUS_INVALID_EA_FLAG, -EINVAL, "STATUS_INVALID_EA_FLAG"}, + {STATUS_VERIFY_REQUIRED, -EIO, "STATUS_VERIFY_REQUIRED"}, + {STATUS_EXTRANEOUS_INFORMATION, -EIO, "STATUS_EXTRANEOUS_INFORMATION"}, + {STATUS_RXACT_COMMIT_NECESSARY, -EIO, "STATUS_RXACT_COMMIT_NECESSARY"}, + {STATUS_NO_MORE_ENTRIES, -EIO, "STATUS_NO_MORE_ENTRIES"}, + {STATUS_FILEMARK_DETECTED, -EIO, "STATUS_FILEMARK_DETECTED"}, + {STATUS_MEDIA_CHANGED, -EIO, "STATUS_MEDIA_CHANGED"}, + {STATUS_BUS_RESET, -EIO, "STATUS_BUS_RESET"}, + {STATUS_END_OF_MEDIA, -EIO, "STATUS_END_OF_MEDIA"}, + {STATUS_BEGINNING_OF_MEDIA, -EIO, "STATUS_BEGINNING_OF_MEDIA"}, + {STATUS_MEDIA_CHECK, -EIO, "STATUS_MEDIA_CHECK"}, + {STATUS_SETMARK_DETECTED, -EIO, "STATUS_SETMARK_DETECTED"}, + {STATUS_NO_DATA_DETECTED, -EIO, "STATUS_NO_DATA_DETECTED"}, + {STATUS_REDIRECTOR_HAS_OPEN_HANDLES, -EIO, + "STATUS_REDIRECTOR_HAS_OPEN_HANDLES"}, + {STATUS_SERVER_HAS_OPEN_HANDLES, -EIO, + "STATUS_SERVER_HAS_OPEN_HANDLES"}, + {STATUS_ALREADY_DISCONNECTED, -EIO, "STATUS_ALREADY_DISCONNECTED"}, + {STATUS_LONGJUMP, -EIO, "STATUS_LONGJUMP"}, + {STATUS_CLEANER_CARTRIDGE_INSTALLED, -EIO, + "STATUS_CLEANER_CARTRIDGE_INSTALLED"}, + {STATUS_PLUGPLAY_QUERY_VETOED, -EIO, "STATUS_PLUGPLAY_QUERY_VETOED"}, + {STATUS_UNWIND_CONSOLIDATE, -EIO, "STATUS_UNWIND_CONSOLIDATE"}, + {STATUS_REGISTRY_HIVE_RECOVERED, -EIO, + "STATUS_REGISTRY_HIVE_RECOVERED"}, + {STATUS_DLL_MIGHT_BE_INSECURE, -EIO, "STATUS_DLL_MIGHT_BE_INSECURE"}, + {STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO, + "STATUS_DLL_MIGHT_BE_INCOMPATIBLE"}, + {STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"}, + {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EOPNOTSUPP, + "STATUS_REPARSE_NOT_HANDLED"}, + {STATUS_DEVICE_REQUIRES_CLEANING, -EIO, + "STATUS_DEVICE_REQUIRES_CLEANING"}, + {STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"}, + {STATUS_DATA_LOST_REPAIR, -EIO, "STATUS_DATA_LOST_REPAIR"}, + {DBG_EXCEPTION_NOT_HANDLED, -EIO, "DBG_EXCEPTION_NOT_HANDLED"}, + {STATUS_CLUSTER_NODE_ALREADY_UP, -EIO, + "STATUS_CLUSTER_NODE_ALREADY_UP"}, + {STATUS_CLUSTER_NODE_ALREADY_DOWN, -EIO, + "STATUS_CLUSTER_NODE_ALREADY_DOWN"}, + {STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, -EIO, + "STATUS_CLUSTER_NETWORK_ALREADY_ONLINE"}, + {STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, -EIO, + "STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE"}, + {STATUS_CLUSTER_NODE_ALREADY_MEMBER, -EIO, + "STATUS_CLUSTER_NODE_ALREADY_MEMBER"}, + {STATUS_COULD_NOT_RESIZE_LOG, -EIO, "STATUS_COULD_NOT_RESIZE_LOG"}, + {STATUS_NO_TXF_METADATA, -EIO, "STATUS_NO_TXF_METADATA"}, + {STATUS_CANT_RECOVER_WITH_HANDLE_OPEN, -EIO, + "STATUS_CANT_RECOVER_WITH_HANDLE_OPEN"}, + {STATUS_TXF_METADATA_ALREADY_PRESENT, -EIO, + "STATUS_TXF_METADATA_ALREADY_PRESENT"}, + {STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET, -EIO, + "STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET"}, + {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED, -EIO, + "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED"}, + {STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"}, + {STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"}, + {STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"}, + {STATUS_NOT_IMPLEMENTED, -EOPNOTSUPP, "STATUS_NOT_IMPLEMENTED"}, + {STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"}, + {STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"}, + {STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"}, + {STATUS_IN_PAGE_ERROR, -EFAULT, "STATUS_IN_PAGE_ERROR"}, + {STATUS_PAGEFILE_QUOTA, -EDQUOT, "STATUS_PAGEFILE_QUOTA"}, + {STATUS_INVALID_HANDLE, -EBADF, "STATUS_INVALID_HANDLE"}, + {STATUS_BAD_INITIAL_STACK, -EIO, "STATUS_BAD_INITIAL_STACK"}, + {STATUS_BAD_INITIAL_PC, -EIO, "STATUS_BAD_INITIAL_PC"}, + {STATUS_INVALID_CID, -EIO, "STATUS_INVALID_CID"}, + {STATUS_TIMER_NOT_CANCELED, -EIO, "STATUS_TIMER_NOT_CANCELED"}, + {STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"}, + {STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"}, + {STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"}, + {STATUS_INVALID_DEVICE_REQUEST, -EOPNOTSUPP, "STATUS_INVALID_DEVICE_REQUEST"}, + {STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"}, + {STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"}, + {STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"}, + {STATUS_UNRECOGNIZED_MEDIA, -EIO, "STATUS_UNRECOGNIZED_MEDIA"}, + {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, + {STATUS_MORE_PROCESSING_REQUIRED, -EIO, + "STATUS_MORE_PROCESSING_REQUIRED"}, + {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"}, + {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, + "STATUS_CONFLICTING_ADDRESSES"}, + {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, + {STATUS_UNABLE_TO_FREE_VM, -EIO, "STATUS_UNABLE_TO_FREE_VM"}, + {STATUS_UNABLE_TO_DELETE_SECTION, -EIO, + "STATUS_UNABLE_TO_DELETE_SECTION"}, + {STATUS_INVALID_SYSTEM_SERVICE, -EIO, "STATUS_INVALID_SYSTEM_SERVICE"}, + {STATUS_ILLEGAL_INSTRUCTION, -EIO, "STATUS_ILLEGAL_INSTRUCTION"}, + {STATUS_INVALID_LOCK_SEQUENCE, -EIO, "STATUS_INVALID_LOCK_SEQUENCE"}, + {STATUS_INVALID_VIEW_SIZE, -EIO, "STATUS_INVALID_VIEW_SIZE"}, + {STATUS_INVALID_FILE_FOR_SECTION, -EIO, + "STATUS_INVALID_FILE_FOR_SECTION"}, + {STATUS_ALREADY_COMMITTED, -EIO, "STATUS_ALREADY_COMMITTED"}, + {STATUS_ACCESS_DENIED, -EACCES, "STATUS_ACCESS_DENIED"}, + {STATUS_BUFFER_TOO_SMALL, -EIO, "STATUS_BUFFER_TOO_SMALL"}, + {STATUS_OBJECT_TYPE_MISMATCH, -EIO, "STATUS_OBJECT_TYPE_MISMATCH"}, + {STATUS_NONCONTINUABLE_EXCEPTION, -EIO, + "STATUS_NONCONTINUABLE_EXCEPTION"}, + {STATUS_INVALID_DISPOSITION, -EIO, "STATUS_INVALID_DISPOSITION"}, + {STATUS_UNWIND, -EIO, "STATUS_UNWIND"}, + {STATUS_BAD_STACK, -EIO, "STATUS_BAD_STACK"}, + {STATUS_INVALID_UNWIND_TARGET, -EIO, "STATUS_INVALID_UNWIND_TARGET"}, + {STATUS_NOT_LOCKED, -EIO, "STATUS_NOT_LOCKED"}, + {STATUS_PARITY_ERROR, -EIO, "STATUS_PARITY_ERROR"}, + {STATUS_UNABLE_TO_DECOMMIT_VM, -EIO, "STATUS_UNABLE_TO_DECOMMIT_VM"}, + {STATUS_NOT_COMMITTED, -EIO, "STATUS_NOT_COMMITTED"}, + {STATUS_INVALID_PORT_ATTRIBUTES, -EIO, + "STATUS_INVALID_PORT_ATTRIBUTES"}, + {STATUS_PORT_MESSAGE_TOO_LONG, -EIO, "STATUS_PORT_MESSAGE_TOO_LONG"}, + {STATUS_INVALID_PARAMETER_MIX, -EINVAL, "STATUS_INVALID_PARAMETER_MIX"}, + {STATUS_INVALID_QUOTA_LOWER, -EIO, "STATUS_INVALID_QUOTA_LOWER"}, + {STATUS_DISK_CORRUPT_ERROR, -EIO, "STATUS_DISK_CORRUPT_ERROR"}, + {STATUS_OBJECT_NAME_INVALID, -ENOENT, "STATUS_OBJECT_NAME_INVALID"}, + {STATUS_OBJECT_NAME_NOT_FOUND, -ENOENT, "STATUS_OBJECT_NAME_NOT_FOUND"}, + {STATUS_OBJECT_NAME_COLLISION, -EEXIST, "STATUS_OBJECT_NAME_COLLISION"}, + {STATUS_PORT_DISCONNECTED, -EIO, "STATUS_PORT_DISCONNECTED"}, + {STATUS_DEVICE_ALREADY_ATTACHED, -EIO, + "STATUS_DEVICE_ALREADY_ATTACHED"}, + {STATUS_OBJECT_PATH_INVALID, -ENOTDIR, "STATUS_OBJECT_PATH_INVALID"}, + {STATUS_OBJECT_PATH_NOT_FOUND, -ENOENT, "STATUS_OBJECT_PATH_NOT_FOUND"}, + {STATUS_OBJECT_PATH_SYNTAX_BAD, -EIO, "STATUS_OBJECT_PATH_SYNTAX_BAD"}, + {STATUS_DATA_OVERRUN, -EIO, "STATUS_DATA_OVERRUN"}, + {STATUS_DATA_LATE_ERROR, -EIO, "STATUS_DATA_LATE_ERROR"}, + {STATUS_DATA_ERROR, -EIO, "STATUS_DATA_ERROR"}, + {STATUS_CRC_ERROR, -EIO, "STATUS_CRC_ERROR"}, + {STATUS_SECTION_TOO_BIG, -EIO, "STATUS_SECTION_TOO_BIG"}, + {STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED, + "STATUS_PORT_CONNECTION_REFUSED"}, + {STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"}, + {STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"}, + {STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"}, + {STATUS_INVALID_PAGE_PROTECTION, -EIO, + "STATUS_INVALID_PAGE_PROTECTION"}, + {STATUS_MUTANT_NOT_OWNED, -EIO, "STATUS_MUTANT_NOT_OWNED"}, + {STATUS_SEMAPHORE_LIMIT_EXCEEDED, -EIO, + "STATUS_SEMAPHORE_LIMIT_EXCEEDED"}, + {STATUS_PORT_ALREADY_SET, -EIO, "STATUS_PORT_ALREADY_SET"}, + {STATUS_SECTION_NOT_IMAGE, -EIO, "STATUS_SECTION_NOT_IMAGE"}, + {STATUS_SUSPEND_COUNT_EXCEEDED, -EIO, "STATUS_SUSPEND_COUNT_EXCEEDED"}, + {STATUS_THREAD_IS_TERMINATING, -EIO, "STATUS_THREAD_IS_TERMINATING"}, + {STATUS_BAD_WORKING_SET_LIMIT, -EIO, "STATUS_BAD_WORKING_SET_LIMIT"}, + {STATUS_INCOMPATIBLE_FILE_MAP, -EIO, "STATUS_INCOMPATIBLE_FILE_MAP"}, + {STATUS_SECTION_PROTECTION, -EIO, "STATUS_SECTION_PROTECTION"}, + {STATUS_EAS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_EAS_NOT_SUPPORTED"}, + {STATUS_EA_TOO_LARGE, -EIO, "STATUS_EA_TOO_LARGE"}, + {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, + {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, + {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, + {STATUS_FILE_LOCK_CONFLICT, -EACCES, "STATUS_FILE_LOCK_CONFLICT"}, + {STATUS_LOCK_NOT_GRANTED, -EACCES, "STATUS_LOCK_NOT_GRANTED"}, + {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, + {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, + "STATUS_CTL_FILE_NOT_SUPPORTED"}, + {STATUS_UNKNOWN_REVISION, -EIO, "STATUS_UNKNOWN_REVISION"}, + {STATUS_REVISION_MISMATCH, -EIO, "STATUS_REVISION_MISMATCH"}, + {STATUS_INVALID_OWNER, -EIO, "STATUS_INVALID_OWNER"}, + {STATUS_INVALID_PRIMARY_GROUP, -EIO, "STATUS_INVALID_PRIMARY_GROUP"}, + {STATUS_NO_IMPERSONATION_TOKEN, -EIO, "STATUS_NO_IMPERSONATION_TOKEN"}, + {STATUS_CANT_DISABLE_MANDATORY, -EIO, "STATUS_CANT_DISABLE_MANDATORY"}, + {STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"}, + {STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"}, + {STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"}, + {STATUS_PRIVILEGE_NOT_HELD, -EIO, "STATUS_PRIVILEGE_NOT_HELD"}, + {STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"}, + {STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"}, + {STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"}, + {STATUS_GROUP_EXISTS, -EIO, "STATUS_GROUP_EXISTS"}, + {STATUS_NO_SUCH_GROUP, -EIO, "STATUS_NO_SUCH_GROUP"}, + {STATUS_MEMBER_IN_GROUP, -EIO, "STATUS_MEMBER_IN_GROUP"}, + {STATUS_MEMBER_NOT_IN_GROUP, -EIO, "STATUS_MEMBER_NOT_IN_GROUP"}, + {STATUS_LAST_ADMIN, -EIO, "STATUS_LAST_ADMIN"}, + {STATUS_WRONG_PASSWORD, -EACCES, "STATUS_WRONG_PASSWORD"}, + {STATUS_ILL_FORMED_PASSWORD, -EINVAL, "STATUS_ILL_FORMED_PASSWORD"}, + {STATUS_PASSWORD_RESTRICTION, -EACCES, "STATUS_PASSWORD_RESTRICTION"}, + {STATUS_LOGON_FAILURE, -EACCES, "STATUS_LOGON_FAILURE"}, + {STATUS_ACCOUNT_RESTRICTION, -EACCES, "STATUS_ACCOUNT_RESTRICTION"}, + {STATUS_INVALID_LOGON_HOURS, -EACCES, "STATUS_INVALID_LOGON_HOURS"}, + {STATUS_INVALID_WORKSTATION, -EACCES, "STATUS_INVALID_WORKSTATION"}, + {STATUS_PASSWORD_EXPIRED, -EKEYEXPIRED, "STATUS_PASSWORD_EXPIRED"}, + {STATUS_ACCOUNT_DISABLED, -EKEYREVOKED, "STATUS_ACCOUNT_DISABLED"}, + {STATUS_NONE_MAPPED, -EIO, "STATUS_NONE_MAPPED"}, + {STATUS_TOO_MANY_LUIDS_REQUESTED, -EIO, + "STATUS_TOO_MANY_LUIDS_REQUESTED"}, + {STATUS_LUIDS_EXHAUSTED, -EIO, "STATUS_LUIDS_EXHAUSTED"}, + {STATUS_INVALID_SUB_AUTHORITY, -EIO, "STATUS_INVALID_SUB_AUTHORITY"}, + {STATUS_INVALID_ACL, -EIO, "STATUS_INVALID_ACL"}, + {STATUS_INVALID_SID, -EIO, "STATUS_INVALID_SID"}, + {STATUS_INVALID_SECURITY_DESCR, -EIO, "STATUS_INVALID_SECURITY_DESCR"}, + {STATUS_PROCEDURE_NOT_FOUND, -EIO, "STATUS_PROCEDURE_NOT_FOUND"}, + {STATUS_INVALID_IMAGE_FORMAT, -EIO, "STATUS_INVALID_IMAGE_FORMAT"}, + {STATUS_NO_TOKEN, -EIO, "STATUS_NO_TOKEN"}, + {STATUS_BAD_INHERITANCE_ACL, -EIO, "STATUS_BAD_INHERITANCE_ACL"}, + {STATUS_RANGE_NOT_LOCKED, -EIO, "STATUS_RANGE_NOT_LOCKED"}, + {STATUS_DISK_FULL, -ENOSPC, "STATUS_DISK_FULL"}, + {STATUS_SERVER_DISABLED, -EIO, "STATUS_SERVER_DISABLED"}, + {STATUS_SERVER_NOT_DISABLED, -EIO, "STATUS_SERVER_NOT_DISABLED"}, + {STATUS_TOO_MANY_GUIDS_REQUESTED, -EIO, + "STATUS_TOO_MANY_GUIDS_REQUESTED"}, + {STATUS_GUIDS_EXHAUSTED, -EIO, "STATUS_GUIDS_EXHAUSTED"}, + {STATUS_INVALID_ID_AUTHORITY, -EIO, "STATUS_INVALID_ID_AUTHORITY"}, + {STATUS_AGENTS_EXHAUSTED, -EIO, "STATUS_AGENTS_EXHAUSTED"}, + {STATUS_INVALID_VOLUME_LABEL, -EIO, "STATUS_INVALID_VOLUME_LABEL"}, + {STATUS_SECTION_NOT_EXTENDED, -EIO, "STATUS_SECTION_NOT_EXTENDED"}, + {STATUS_NOT_MAPPED_DATA, -EIO, "STATUS_NOT_MAPPED_DATA"}, + {STATUS_RESOURCE_DATA_NOT_FOUND, -EIO, + "STATUS_RESOURCE_DATA_NOT_FOUND"}, + {STATUS_RESOURCE_TYPE_NOT_FOUND, -EIO, + "STATUS_RESOURCE_TYPE_NOT_FOUND"}, + {STATUS_RESOURCE_NAME_NOT_FOUND, -EIO, + "STATUS_RESOURCE_NAME_NOT_FOUND"}, + {STATUS_ARRAY_BOUNDS_EXCEEDED, -EIO, "STATUS_ARRAY_BOUNDS_EXCEEDED"}, + {STATUS_FLOAT_DENORMAL_OPERAND, -EIO, "STATUS_FLOAT_DENORMAL_OPERAND"}, + {STATUS_FLOAT_DIVIDE_BY_ZERO, -EIO, "STATUS_FLOAT_DIVIDE_BY_ZERO"}, + {STATUS_FLOAT_INEXACT_RESULT, -EIO, "STATUS_FLOAT_INEXACT_RESULT"}, + {STATUS_FLOAT_INVALID_OPERATION, -EIO, + "STATUS_FLOAT_INVALID_OPERATION"}, + {STATUS_FLOAT_OVERFLOW, -EIO, "STATUS_FLOAT_OVERFLOW"}, + {STATUS_FLOAT_STACK_CHECK, -EIO, "STATUS_FLOAT_STACK_CHECK"}, + {STATUS_FLOAT_UNDERFLOW, -EIO, "STATUS_FLOAT_UNDERFLOW"}, + {STATUS_INTEGER_DIVIDE_BY_ZERO, -EIO, "STATUS_INTEGER_DIVIDE_BY_ZERO"}, + {STATUS_INTEGER_OVERFLOW, -EIO, "STATUS_INTEGER_OVERFLOW"}, + {STATUS_PRIVILEGED_INSTRUCTION, -EIO, "STATUS_PRIVILEGED_INSTRUCTION"}, + {STATUS_TOO_MANY_PAGING_FILES, -EIO, "STATUS_TOO_MANY_PAGING_FILES"}, + {STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"}, + {STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO, + "STATUS_ALLOTTED_SPACE_EXCEEDED"}, + {STATUS_INSUFFICIENT_RESOURCES, -EAGAIN, + "STATUS_INSUFFICIENT_RESOURCES"}, + {STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"}, + {STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"}, + {STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"}, + {STATUS_DEVICE_POWER_FAILURE, -EIO, "STATUS_DEVICE_POWER_FAILURE"}, + {STATUS_FREE_VM_NOT_AT_BASE, -EIO, "STATUS_FREE_VM_NOT_AT_BASE"}, + {STATUS_MEMORY_NOT_ALLOCATED, -EFAULT, "STATUS_MEMORY_NOT_ALLOCATED"}, + {STATUS_WORKING_SET_QUOTA, -EIO, "STATUS_WORKING_SET_QUOTA"}, + {STATUS_MEDIA_WRITE_PROTECTED, -EROFS, "STATUS_MEDIA_WRITE_PROTECTED"}, + {STATUS_DEVICE_NOT_READY, -EIO, "STATUS_DEVICE_NOT_READY"}, + {STATUS_INVALID_GROUP_ATTRIBUTES, -EIO, + "STATUS_INVALID_GROUP_ATTRIBUTES"}, + {STATUS_BAD_IMPERSONATION_LEVEL, -EIO, + "STATUS_BAD_IMPERSONATION_LEVEL"}, + {STATUS_CANT_OPEN_ANONYMOUS, -EIO, "STATUS_CANT_OPEN_ANONYMOUS"}, + {STATUS_BAD_VALIDATION_CLASS, -EIO, "STATUS_BAD_VALIDATION_CLASS"}, + {STATUS_BAD_TOKEN_TYPE, -EIO, "STATUS_BAD_TOKEN_TYPE"}, + {STATUS_BAD_MASTER_BOOT_RECORD, -EIO, "STATUS_BAD_MASTER_BOOT_RECORD"}, + {STATUS_INSTRUCTION_MISALIGNMENT, -EIO, + "STATUS_INSTRUCTION_MISALIGNMENT"}, + {STATUS_INSTANCE_NOT_AVAILABLE, -EIO, "STATUS_INSTANCE_NOT_AVAILABLE"}, + {STATUS_PIPE_NOT_AVAILABLE, -EIO, "STATUS_PIPE_NOT_AVAILABLE"}, + {STATUS_INVALID_PIPE_STATE, -EIO, "STATUS_INVALID_PIPE_STATE"}, + {STATUS_PIPE_BUSY, -EBUSY, "STATUS_PIPE_BUSY"}, + {STATUS_ILLEGAL_FUNCTION, -EIO, "STATUS_ILLEGAL_FUNCTION"}, + {STATUS_PIPE_DISCONNECTED, -EPIPE, "STATUS_PIPE_DISCONNECTED"}, + {STATUS_PIPE_CLOSING, -EIO, "STATUS_PIPE_CLOSING"}, + {STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"}, + {STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"}, + {STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"}, + {STATUS_IO_TIMEOUT, -EAGAIN, "STATUS_IO_TIMEOUT"}, + {STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"}, + {STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"}, + {STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"}, + {STATUS_COULD_NOT_INTERPRET, -EIO, "STATUS_COULD_NOT_INTERPRET"}, + {STATUS_FILE_IS_A_DIRECTORY, -EISDIR, "STATUS_FILE_IS_A_DIRECTORY"}, + {STATUS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_NOT_SUPPORTED"}, + {STATUS_REMOTE_NOT_LISTENING, -EHOSTDOWN, + "STATUS_REMOTE_NOT_LISTENING"}, + {STATUS_DUPLICATE_NAME, -ENOTUNIQ, "STATUS_DUPLICATE_NAME"}, + {STATUS_BAD_NETWORK_PATH, -EINVAL, "STATUS_BAD_NETWORK_PATH"}, + {STATUS_NETWORK_BUSY, -EBUSY, "STATUS_NETWORK_BUSY"}, + {STATUS_DEVICE_DOES_NOT_EXIST, -ENODEV, "STATUS_DEVICE_DOES_NOT_EXIST"}, + {STATUS_TOO_MANY_COMMANDS, -EIO, "STATUS_TOO_MANY_COMMANDS"}, + {STATUS_ADAPTER_HARDWARE_ERROR, -EIO, "STATUS_ADAPTER_HARDWARE_ERROR"}, + {STATUS_INVALID_NETWORK_RESPONSE, -EIO, + "STATUS_INVALID_NETWORK_RESPONSE"}, + {STATUS_UNEXPECTED_NETWORK_ERROR, -EIO, + "STATUS_UNEXPECTED_NETWORK_ERROR"}, + {STATUS_BAD_REMOTE_ADAPTER, -EIO, "STATUS_BAD_REMOTE_ADAPTER"}, + {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, + {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, + {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, + {STATUS_NETWORK_NAME_DELETED, -EREMCHG, "STATUS_NETWORK_NAME_DELETED"}, + {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, + {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, + {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, + {STATUS_TOO_MANY_NAMES, -EIO, "STATUS_TOO_MANY_NAMES"}, + {STATUS_TOO_MANY_SESSIONS, -EIO, "STATUS_TOO_MANY_SESSIONS"}, + {STATUS_SHARING_PAUSED, -EIO, "STATUS_SHARING_PAUSED"}, + {STATUS_REQUEST_NOT_ACCEPTED, -EIO, "STATUS_REQUEST_NOT_ACCEPTED"}, + {STATUS_REDIRECTOR_PAUSED, -EIO, "STATUS_REDIRECTOR_PAUSED"}, + {STATUS_NET_WRITE_FAULT, -EIO, "STATUS_NET_WRITE_FAULT"}, + {STATUS_PROFILING_AT_LIMIT, -EIO, "STATUS_PROFILING_AT_LIMIT"}, + {STATUS_NOT_SAME_DEVICE, -EXDEV, "STATUS_NOT_SAME_DEVICE"}, + {STATUS_FILE_RENAMED, -EIO, "STATUS_FILE_RENAMED"}, + {STATUS_VIRTUAL_CIRCUIT_CLOSED, -EIO, "STATUS_VIRTUAL_CIRCUIT_CLOSED"}, + {STATUS_NO_SECURITY_ON_OBJECT, -EIO, "STATUS_NO_SECURITY_ON_OBJECT"}, + {STATUS_CANT_WAIT, -EIO, "STATUS_CANT_WAIT"}, + {STATUS_PIPE_EMPTY, -EIO, "STATUS_PIPE_EMPTY"}, + {STATUS_CANT_ACCESS_DOMAIN_INFO, -EIO, + "STATUS_CANT_ACCESS_DOMAIN_INFO"}, + {STATUS_CANT_TERMINATE_SELF, -EIO, "STATUS_CANT_TERMINATE_SELF"}, + {STATUS_INVALID_SERVER_STATE, -EIO, "STATUS_INVALID_SERVER_STATE"}, + {STATUS_INVALID_DOMAIN_STATE, -EIO, "STATUS_INVALID_DOMAIN_STATE"}, + {STATUS_INVALID_DOMAIN_ROLE, -EIO, "STATUS_INVALID_DOMAIN_ROLE"}, + {STATUS_NO_SUCH_DOMAIN, -EIO, "STATUS_NO_SUCH_DOMAIN"}, + {STATUS_DOMAIN_EXISTS, -EIO, "STATUS_DOMAIN_EXISTS"}, + {STATUS_DOMAIN_LIMIT_EXCEEDED, -EIO, "STATUS_DOMAIN_LIMIT_EXCEEDED"}, + {STATUS_OPLOCK_NOT_GRANTED, -EIO, "STATUS_OPLOCK_NOT_GRANTED"}, + {STATUS_INVALID_OPLOCK_PROTOCOL, -EIO, + "STATUS_INVALID_OPLOCK_PROTOCOL"}, + {STATUS_INTERNAL_DB_CORRUPTION, -EIO, "STATUS_INTERNAL_DB_CORRUPTION"}, + {STATUS_INTERNAL_ERROR, -EIO, "STATUS_INTERNAL_ERROR"}, + {STATUS_GENERIC_NOT_MAPPED, -EIO, "STATUS_GENERIC_NOT_MAPPED"}, + {STATUS_BAD_DESCRIPTOR_FORMAT, -EIO, "STATUS_BAD_DESCRIPTOR_FORMAT"}, + {STATUS_INVALID_USER_BUFFER, -EIO, "STATUS_INVALID_USER_BUFFER"}, + {STATUS_UNEXPECTED_IO_ERROR, -EIO, "STATUS_UNEXPECTED_IO_ERROR"}, + {STATUS_UNEXPECTED_MM_CREATE_ERR, -EIO, + "STATUS_UNEXPECTED_MM_CREATE_ERR"}, + {STATUS_UNEXPECTED_MM_MAP_ERROR, -EIO, + "STATUS_UNEXPECTED_MM_MAP_ERROR"}, + {STATUS_UNEXPECTED_MM_EXTEND_ERR, -EIO, + "STATUS_UNEXPECTED_MM_EXTEND_ERR"}, + {STATUS_NOT_LOGON_PROCESS, -EIO, "STATUS_NOT_LOGON_PROCESS"}, + {STATUS_LOGON_SESSION_EXISTS, -EIO, "STATUS_LOGON_SESSION_EXISTS"}, + {STATUS_INVALID_PARAMETER_1, -EINVAL, "STATUS_INVALID_PARAMETER_1"}, + {STATUS_INVALID_PARAMETER_2, -EINVAL, "STATUS_INVALID_PARAMETER_2"}, + {STATUS_INVALID_PARAMETER_3, -EINVAL, "STATUS_INVALID_PARAMETER_3"}, + {STATUS_INVALID_PARAMETER_4, -EINVAL, "STATUS_INVALID_PARAMETER_4"}, + {STATUS_INVALID_PARAMETER_5, -EINVAL, "STATUS_INVALID_PARAMETER_5"}, + {STATUS_INVALID_PARAMETER_6, -EINVAL, "STATUS_INVALID_PARAMETER_6"}, + {STATUS_INVALID_PARAMETER_7, -EINVAL, "STATUS_INVALID_PARAMETER_7"}, + {STATUS_INVALID_PARAMETER_8, -EINVAL, "STATUS_INVALID_PARAMETER_8"}, + {STATUS_INVALID_PARAMETER_9, -EINVAL, "STATUS_INVALID_PARAMETER_9"}, + {STATUS_INVALID_PARAMETER_10, -EINVAL, "STATUS_INVALID_PARAMETER_10"}, + {STATUS_INVALID_PARAMETER_11, -EINVAL, "STATUS_INVALID_PARAMETER_11"}, + {STATUS_INVALID_PARAMETER_12, -EINVAL, "STATUS_INVALID_PARAMETER_12"}, + {STATUS_REDIRECTOR_NOT_STARTED, -EIO, "STATUS_REDIRECTOR_NOT_STARTED"}, + {STATUS_REDIRECTOR_STARTED, -EIO, "STATUS_REDIRECTOR_STARTED"}, + {STATUS_STACK_OVERFLOW, -EIO, "STATUS_STACK_OVERFLOW"}, + {STATUS_NO_SUCH_PACKAGE, -EIO, "STATUS_NO_SUCH_PACKAGE"}, + {STATUS_BAD_FUNCTION_TABLE, -EIO, "STATUS_BAD_FUNCTION_TABLE"}, + {STATUS_VARIABLE_NOT_FOUND, -EIO, "STATUS_VARIABLE_NOT_FOUND"}, + {STATUS_DIRECTORY_NOT_EMPTY, -ENOTEMPTY, "STATUS_DIRECTORY_NOT_EMPTY"}, + {STATUS_FILE_CORRUPT_ERROR, -EIO, "STATUS_FILE_CORRUPT_ERROR"}, + {STATUS_NOT_A_DIRECTORY, -ENOTDIR, "STATUS_NOT_A_DIRECTORY"}, + {STATUS_BAD_LOGON_SESSION_STATE, -EIO, + "STATUS_BAD_LOGON_SESSION_STATE"}, + {STATUS_LOGON_SESSION_COLLISION, -EIO, + "STATUS_LOGON_SESSION_COLLISION"}, + {STATUS_NAME_TOO_LONG, -ENAMETOOLONG, "STATUS_NAME_TOO_LONG"}, + {STATUS_FILES_OPEN, -EIO, "STATUS_FILES_OPEN"}, + {STATUS_CONNECTION_IN_USE, -EIO, "STATUS_CONNECTION_IN_USE"}, + {STATUS_MESSAGE_NOT_FOUND, -EIO, "STATUS_MESSAGE_NOT_FOUND"}, + {STATUS_PROCESS_IS_TERMINATING, -EIO, "STATUS_PROCESS_IS_TERMINATING"}, + {STATUS_INVALID_LOGON_TYPE, -EIO, "STATUS_INVALID_LOGON_TYPE"}, + {STATUS_NO_GUID_TRANSLATION, -EIO, "STATUS_NO_GUID_TRANSLATION"}, + {STATUS_CANNOT_IMPERSONATE, -EIO, "STATUS_CANNOT_IMPERSONATE"}, + {STATUS_IMAGE_ALREADY_LOADED, -EIO, "STATUS_IMAGE_ALREADY_LOADED"}, + {STATUS_ABIOS_NOT_PRESENT, -EIO, "STATUS_ABIOS_NOT_PRESENT"}, + {STATUS_ABIOS_LID_NOT_EXIST, -EIO, "STATUS_ABIOS_LID_NOT_EXIST"}, + {STATUS_ABIOS_LID_ALREADY_OWNED, -EIO, + "STATUS_ABIOS_LID_ALREADY_OWNED"}, + {STATUS_ABIOS_NOT_LID_OWNER, -EIO, "STATUS_ABIOS_NOT_LID_OWNER"}, + {STATUS_ABIOS_INVALID_COMMAND, -EIO, "STATUS_ABIOS_INVALID_COMMAND"}, + {STATUS_ABIOS_INVALID_LID, -EIO, "STATUS_ABIOS_INVALID_LID"}, + {STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, -EIO, + "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE"}, + {STATUS_ABIOS_INVALID_SELECTOR, -EIO, "STATUS_ABIOS_INVALID_SELECTOR"}, + {STATUS_NO_LDT, -EIO, "STATUS_NO_LDT"}, + {STATUS_INVALID_LDT_SIZE, -EIO, "STATUS_INVALID_LDT_SIZE"}, + {STATUS_INVALID_LDT_OFFSET, -EIO, "STATUS_INVALID_LDT_OFFSET"}, + {STATUS_INVALID_LDT_DESCRIPTOR, -EIO, "STATUS_INVALID_LDT_DESCRIPTOR"}, + {STATUS_INVALID_IMAGE_NE_FORMAT, -EIO, + "STATUS_INVALID_IMAGE_NE_FORMAT"}, + {STATUS_RXACT_INVALID_STATE, -EIO, "STATUS_RXACT_INVALID_STATE"}, + {STATUS_RXACT_COMMIT_FAILURE, -EIO, "STATUS_RXACT_COMMIT_FAILURE"}, + {STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"}, + {STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"}, + {STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"}, + {STATUS_CANNOT_DELETE, -EACCES, "STATUS_CANNOT_DELETE"}, + {STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"}, + {STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"}, + {STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"}, + {STATUS_SPECIAL_GROUP, -EIO, "STATUS_SPECIAL_GROUP"}, + {STATUS_SPECIAL_USER, -EIO, "STATUS_SPECIAL_USER"}, + {STATUS_MEMBERS_PRIMARY_GROUP, -EIO, "STATUS_MEMBERS_PRIMARY_GROUP"}, + {STATUS_FILE_CLOSED, -EBADF, "STATUS_FILE_CLOSED"}, + {STATUS_TOO_MANY_THREADS, -EIO, "STATUS_TOO_MANY_THREADS"}, + {STATUS_THREAD_NOT_IN_PROCESS, -EIO, "STATUS_THREAD_NOT_IN_PROCESS"}, + {STATUS_TOKEN_ALREADY_IN_USE, -EIO, "STATUS_TOKEN_ALREADY_IN_USE"}, + {STATUS_PAGEFILE_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_PAGEFILE_QUOTA_EXCEEDED"}, + {STATUS_COMMITMENT_LIMIT, -EIO, "STATUS_COMMITMENT_LIMIT"}, + {STATUS_INVALID_IMAGE_LE_FORMAT, -EIO, + "STATUS_INVALID_IMAGE_LE_FORMAT"}, + {STATUS_INVALID_IMAGE_NOT_MZ, -EIO, "STATUS_INVALID_IMAGE_NOT_MZ"}, + {STATUS_INVALID_IMAGE_PROTECT, -EIO, "STATUS_INVALID_IMAGE_PROTECT"}, + {STATUS_INVALID_IMAGE_WIN_16, -EIO, "STATUS_INVALID_IMAGE_WIN_16"}, + {STATUS_LOGON_SERVER_CONFLICT, -EIO, "STATUS_LOGON_SERVER_CONFLICT"}, + {STATUS_TIME_DIFFERENCE_AT_DC, -EIO, "STATUS_TIME_DIFFERENCE_AT_DC"}, + {STATUS_SYNCHRONIZATION_REQUIRED, -EIO, + "STATUS_SYNCHRONIZATION_REQUIRED"}, + {STATUS_DLL_NOT_FOUND, -ENOENT, "STATUS_DLL_NOT_FOUND"}, + {STATUS_OPEN_FAILED, -EIO, "STATUS_OPEN_FAILED"}, + {STATUS_IO_PRIVILEGE_FAILED, -EIO, "STATUS_IO_PRIVILEGE_FAILED"}, + {STATUS_ORDINAL_NOT_FOUND, -EIO, "STATUS_ORDINAL_NOT_FOUND"}, + {STATUS_ENTRYPOINT_NOT_FOUND, -EIO, "STATUS_ENTRYPOINT_NOT_FOUND"}, + {STATUS_CONTROL_C_EXIT, -EIO, "STATUS_CONTROL_C_EXIT"}, + {STATUS_LOCAL_DISCONNECT, -EIO, "STATUS_LOCAL_DISCONNECT"}, + {STATUS_REMOTE_DISCONNECT, -ESHUTDOWN, "STATUS_REMOTE_DISCONNECT"}, + {STATUS_REMOTE_RESOURCES, -EIO, "STATUS_REMOTE_RESOURCES"}, + {STATUS_LINK_FAILED, -EXDEV, "STATUS_LINK_FAILED"}, + {STATUS_LINK_TIMEOUT, -ETIMEDOUT, "STATUS_LINK_TIMEOUT"}, + {STATUS_INVALID_CONNECTION, -EIO, "STATUS_INVALID_CONNECTION"}, + {STATUS_INVALID_ADDRESS, -EIO, "STATUS_INVALID_ADDRESS"}, + {STATUS_DLL_INIT_FAILED, -EIO, "STATUS_DLL_INIT_FAILED"}, + {STATUS_MISSING_SYSTEMFILE, -EIO, "STATUS_MISSING_SYSTEMFILE"}, + {STATUS_UNHANDLED_EXCEPTION, -EIO, "STATUS_UNHANDLED_EXCEPTION"}, + {STATUS_APP_INIT_FAILURE, -EIO, "STATUS_APP_INIT_FAILURE"}, + {STATUS_PAGEFILE_CREATE_FAILED, -EIO, "STATUS_PAGEFILE_CREATE_FAILED"}, + {STATUS_NO_PAGEFILE, -EIO, "STATUS_NO_PAGEFILE"}, + {STATUS_INVALID_LEVEL, -EIO, "STATUS_INVALID_LEVEL"}, + {STATUS_WRONG_PASSWORD_CORE, -EIO, "STATUS_WRONG_PASSWORD_CORE"}, + {STATUS_ILLEGAL_FLOAT_CONTEXT, -EIO, "STATUS_ILLEGAL_FLOAT_CONTEXT"}, + {STATUS_PIPE_BROKEN, -EPIPE, "STATUS_PIPE_BROKEN"}, + {STATUS_REGISTRY_CORRUPT, -EIO, "STATUS_REGISTRY_CORRUPT"}, + {STATUS_REGISTRY_IO_FAILED, -EIO, "STATUS_REGISTRY_IO_FAILED"}, + {STATUS_NO_EVENT_PAIR, -EIO, "STATUS_NO_EVENT_PAIR"}, + {STATUS_UNRECOGNIZED_VOLUME, -EIO, "STATUS_UNRECOGNIZED_VOLUME"}, + {STATUS_SERIAL_NO_DEVICE_INITED, -EIO, + "STATUS_SERIAL_NO_DEVICE_INITED"}, + {STATUS_NO_SUCH_ALIAS, -EIO, "STATUS_NO_SUCH_ALIAS"}, + {STATUS_MEMBER_NOT_IN_ALIAS, -EIO, "STATUS_MEMBER_NOT_IN_ALIAS"}, + {STATUS_MEMBER_IN_ALIAS, -EIO, "STATUS_MEMBER_IN_ALIAS"}, + {STATUS_ALIAS_EXISTS, -EIO, "STATUS_ALIAS_EXISTS"}, + {STATUS_LOGON_NOT_GRANTED, -EIO, "STATUS_LOGON_NOT_GRANTED"}, + {STATUS_TOO_MANY_SECRETS, -EIO, "STATUS_TOO_MANY_SECRETS"}, + {STATUS_SECRET_TOO_LONG, -EIO, "STATUS_SECRET_TOO_LONG"}, + {STATUS_INTERNAL_DB_ERROR, -EIO, "STATUS_INTERNAL_DB_ERROR"}, + {STATUS_FULLSCREEN_MODE, -EIO, "STATUS_FULLSCREEN_MODE"}, + {STATUS_TOO_MANY_CONTEXT_IDS, -EIO, "STATUS_TOO_MANY_CONTEXT_IDS"}, + {STATUS_LOGON_TYPE_NOT_GRANTED, -EIO, "STATUS_LOGON_TYPE_NOT_GRANTED"}, + {STATUS_NOT_REGISTRY_FILE, -EIO, "STATUS_NOT_REGISTRY_FILE"}, + {STATUS_NT_CROSS_ENCRYPTION_REQUIRED, -EIO, + "STATUS_NT_CROSS_ENCRYPTION_REQUIRED"}, + {STATUS_DOMAIN_CTRLR_CONFIG_ERROR, -EIO, + "STATUS_DOMAIN_CTRLR_CONFIG_ERROR"}, + {STATUS_FT_MISSING_MEMBER, -EIO, "STATUS_FT_MISSING_MEMBER"}, + {STATUS_ILL_FORMED_SERVICE_ENTRY, -EIO, + "STATUS_ILL_FORMED_SERVICE_ENTRY"}, + {STATUS_ILLEGAL_CHARACTER, -EIO, "STATUS_ILLEGAL_CHARACTER"}, + {STATUS_UNMAPPABLE_CHARACTER, -EIO, "STATUS_UNMAPPABLE_CHARACTER"}, + {STATUS_UNDEFINED_CHARACTER, -EIO, "STATUS_UNDEFINED_CHARACTER"}, + {STATUS_FLOPPY_VOLUME, -EIO, "STATUS_FLOPPY_VOLUME"}, + {STATUS_FLOPPY_ID_MARK_NOT_FOUND, -EIO, + "STATUS_FLOPPY_ID_MARK_NOT_FOUND"}, + {STATUS_FLOPPY_WRONG_CYLINDER, -EIO, "STATUS_FLOPPY_WRONG_CYLINDER"}, + {STATUS_FLOPPY_UNKNOWN_ERROR, -EIO, "STATUS_FLOPPY_UNKNOWN_ERROR"}, + {STATUS_FLOPPY_BAD_REGISTERS, -EIO, "STATUS_FLOPPY_BAD_REGISTERS"}, + {STATUS_DISK_RECALIBRATE_FAILED, -EIO, + "STATUS_DISK_RECALIBRATE_FAILED"}, + {STATUS_DISK_OPERATION_FAILED, -EIO, "STATUS_DISK_OPERATION_FAILED"}, + {STATUS_DISK_RESET_FAILED, -EIO, "STATUS_DISK_RESET_FAILED"}, + {STATUS_SHARED_IRQ_BUSY, -EBUSY, "STATUS_SHARED_IRQ_BUSY"}, + {STATUS_FT_ORPHANING, -EIO, "STATUS_FT_ORPHANING"}, + {STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT, -EIO, + "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT"}, + {STATUS_PARTITION_FAILURE, -EIO, "STATUS_PARTITION_FAILURE"}, + {STATUS_INVALID_BLOCK_LENGTH, -EIO, "STATUS_INVALID_BLOCK_LENGTH"}, + {STATUS_DEVICE_NOT_PARTITIONED, -EIO, "STATUS_DEVICE_NOT_PARTITIONED"}, + {STATUS_UNABLE_TO_LOCK_MEDIA, -EIO, "STATUS_UNABLE_TO_LOCK_MEDIA"}, + {STATUS_UNABLE_TO_UNLOAD_MEDIA, -EIO, "STATUS_UNABLE_TO_UNLOAD_MEDIA"}, + {STATUS_EOM_OVERFLOW, -EIO, "STATUS_EOM_OVERFLOW"}, + {STATUS_NO_MEDIA, -EIO, "STATUS_NO_MEDIA"}, + {STATUS_NO_SUCH_MEMBER, -EIO, "STATUS_NO_SUCH_MEMBER"}, + {STATUS_INVALID_MEMBER, -EIO, "STATUS_INVALID_MEMBER"}, + {STATUS_KEY_DELETED, -EIO, "STATUS_KEY_DELETED"}, + {STATUS_NO_LOG_SPACE, -EIO, "STATUS_NO_LOG_SPACE"}, + {STATUS_TOO_MANY_SIDS, -EIO, "STATUS_TOO_MANY_SIDS"}, + {STATUS_LM_CROSS_ENCRYPTION_REQUIRED, -EIO, + "STATUS_LM_CROSS_ENCRYPTION_REQUIRED"}, + {STATUS_KEY_HAS_CHILDREN, -EIO, "STATUS_KEY_HAS_CHILDREN"}, + {STATUS_CHILD_MUST_BE_VOLATILE, -EIO, "STATUS_CHILD_MUST_BE_VOLATILE"}, + {STATUS_DEVICE_CONFIGURATION_ERROR, -EIO, + "STATUS_DEVICE_CONFIGURATION_ERROR"}, + {STATUS_DRIVER_INTERNAL_ERROR, -EIO, "STATUS_DRIVER_INTERNAL_ERROR"}, + {STATUS_INVALID_DEVICE_STATE, -EIO, "STATUS_INVALID_DEVICE_STATE"}, + {STATUS_IO_DEVICE_ERROR, -EIO, "STATUS_IO_DEVICE_ERROR"}, + {STATUS_DEVICE_PROTOCOL_ERROR, -EIO, "STATUS_DEVICE_PROTOCOL_ERROR"}, + {STATUS_BACKUP_CONTROLLER, -EIO, "STATUS_BACKUP_CONTROLLER"}, + {STATUS_LOG_FILE_FULL, -EIO, "STATUS_LOG_FILE_FULL"}, + {STATUS_TOO_LATE, -EIO, "STATUS_TOO_LATE"}, + {STATUS_NO_TRUST_LSA_SECRET, -EIO, "STATUS_NO_TRUST_LSA_SECRET"}, + {STATUS_NO_TRUST_SAM_ACCOUNT, -EIO, "STATUS_NO_TRUST_SAM_ACCOUNT"}, + {STATUS_TRUSTED_DOMAIN_FAILURE, -EIO, "STATUS_TRUSTED_DOMAIN_FAILURE"}, + {STATUS_TRUSTED_RELATIONSHIP_FAILURE, -EIO, + "STATUS_TRUSTED_RELATIONSHIP_FAILURE"}, + {STATUS_EVENTLOG_FILE_CORRUPT, -EIO, "STATUS_EVENTLOG_FILE_CORRUPT"}, + {STATUS_EVENTLOG_CANT_START, -EIO, "STATUS_EVENTLOG_CANT_START"}, + {STATUS_TRUST_FAILURE, -EIO, "STATUS_TRUST_FAILURE"}, + {STATUS_MUTANT_LIMIT_EXCEEDED, -EIO, "STATUS_MUTANT_LIMIT_EXCEEDED"}, + {STATUS_NETLOGON_NOT_STARTED, -EIO, "STATUS_NETLOGON_NOT_STARTED"}, + {STATUS_ACCOUNT_EXPIRED, -EKEYEXPIRED, "STATUS_ACCOUNT_EXPIRED"}, + {STATUS_POSSIBLE_DEADLOCK, -EIO, "STATUS_POSSIBLE_DEADLOCK"}, + {STATUS_NETWORK_CREDENTIAL_CONFLICT, -EIO, + "STATUS_NETWORK_CREDENTIAL_CONFLICT"}, + {STATUS_REMOTE_SESSION_LIMIT, -EIO, "STATUS_REMOTE_SESSION_LIMIT"}, + {STATUS_EVENTLOG_FILE_CHANGED, -EIO, "STATUS_EVENTLOG_FILE_CHANGED"}, + {STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, -EIO, + "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"}, + {STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, -EIO, + "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"}, + {STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, -EIO, + "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"}, + {STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO, + "STATUS_DOMAIN_TRUST_INCONSISTENT"}, + {STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"}, + {STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO, + "STATUS_IMAGE_ALREADY_LOADED_AS_DLL"}, + {STATUS_NETWORK_OPEN_RESTRICTION, -EIO, + "STATUS_NETWORK_OPEN_RESTRICTION"}, + {STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"}, + {STATUS_USER_SESSION_DELETED, -EIO, "STATUS_USER_SESSION_DELETED"}, + {STATUS_RESOURCE_LANG_NOT_FOUND, -EIO, + "STATUS_RESOURCE_LANG_NOT_FOUND"}, + {STATUS_INSUFF_SERVER_RESOURCES, -EIO, + "STATUS_INSUFF_SERVER_RESOURCES"}, + {STATUS_INVALID_BUFFER_SIZE, -EIO, "STATUS_INVALID_BUFFER_SIZE"}, + {STATUS_INVALID_ADDRESS_COMPONENT, -EIO, + "STATUS_INVALID_ADDRESS_COMPONENT"}, + {STATUS_INVALID_ADDRESS_WILDCARD, -EIO, + "STATUS_INVALID_ADDRESS_WILDCARD"}, + {STATUS_TOO_MANY_ADDRESSES, -EIO, "STATUS_TOO_MANY_ADDRESSES"}, + {STATUS_ADDRESS_ALREADY_EXISTS, -EADDRINUSE, + "STATUS_ADDRESS_ALREADY_EXISTS"}, + {STATUS_ADDRESS_CLOSED, -EIO, "STATUS_ADDRESS_CLOSED"}, + {STATUS_CONNECTION_DISCONNECTED, -ECONNABORTED, + "STATUS_CONNECTION_DISCONNECTED"}, + {STATUS_CONNECTION_RESET, -ENETRESET, "STATUS_CONNECTION_RESET"}, + {STATUS_TOO_MANY_NODES, -EIO, "STATUS_TOO_MANY_NODES"}, + {STATUS_TRANSACTION_ABORTED, -EIO, "STATUS_TRANSACTION_ABORTED"}, + {STATUS_TRANSACTION_TIMED_OUT, -EIO, "STATUS_TRANSACTION_TIMED_OUT"}, + {STATUS_TRANSACTION_NO_RELEASE, -EIO, "STATUS_TRANSACTION_NO_RELEASE"}, + {STATUS_TRANSACTION_NO_MATCH, -EIO, "STATUS_TRANSACTION_NO_MATCH"}, + {STATUS_TRANSACTION_RESPONDED, -EIO, "STATUS_TRANSACTION_RESPONDED"}, + {STATUS_TRANSACTION_INVALID_ID, -EIO, "STATUS_TRANSACTION_INVALID_ID"}, + {STATUS_TRANSACTION_INVALID_TYPE, -EIO, + "STATUS_TRANSACTION_INVALID_TYPE"}, + {STATUS_NOT_SERVER_SESSION, -EIO, "STATUS_NOT_SERVER_SESSION"}, + {STATUS_NOT_CLIENT_SESSION, -EIO, "STATUS_NOT_CLIENT_SESSION"}, + {STATUS_CANNOT_LOAD_REGISTRY_FILE, -EIO, + "STATUS_CANNOT_LOAD_REGISTRY_FILE"}, + {STATUS_DEBUG_ATTACH_FAILED, -EIO, "STATUS_DEBUG_ATTACH_FAILED"}, + {STATUS_SYSTEM_PROCESS_TERMINATED, -EIO, + "STATUS_SYSTEM_PROCESS_TERMINATED"}, + {STATUS_DATA_NOT_ACCEPTED, -EIO, "STATUS_DATA_NOT_ACCEPTED"}, + {STATUS_NO_BROWSER_SERVERS_FOUND, -EIO, + "STATUS_NO_BROWSER_SERVERS_FOUND"}, + {STATUS_VDM_HARD_ERROR, -EIO, "STATUS_VDM_HARD_ERROR"}, + {STATUS_DRIVER_CANCEL_TIMEOUT, -EIO, "STATUS_DRIVER_CANCEL_TIMEOUT"}, + {STATUS_REPLY_MESSAGE_MISMATCH, -EIO, "STATUS_REPLY_MESSAGE_MISMATCH"}, + {STATUS_MAPPED_ALIGNMENT, -EIO, "STATUS_MAPPED_ALIGNMENT"}, + {STATUS_IMAGE_CHECKSUM_MISMATCH, -EIO, + "STATUS_IMAGE_CHECKSUM_MISMATCH"}, + {STATUS_LOST_WRITEBEHIND_DATA, -EIO, "STATUS_LOST_WRITEBEHIND_DATA"}, + {STATUS_CLIENT_SERVER_PARAMETERS_INVALID, -EIO, + "STATUS_CLIENT_SERVER_PARAMETERS_INVALID"}, + {STATUS_PASSWORD_MUST_CHANGE, -EIO, "STATUS_PASSWORD_MUST_CHANGE"}, + {STATUS_NOT_FOUND, -ENOENT, "STATUS_NOT_FOUND"}, + {STATUS_NOT_TINY_STREAM, -EIO, "STATUS_NOT_TINY_STREAM"}, + {STATUS_RECOVERY_FAILURE, -EIO, "STATUS_RECOVERY_FAILURE"}, + {STATUS_STACK_OVERFLOW_READ, -EIO, "STATUS_STACK_OVERFLOW_READ"}, + {STATUS_FAIL_CHECK, -EIO, "STATUS_FAIL_CHECK"}, + {STATUS_DUPLICATE_OBJECTID, -EIO, "STATUS_DUPLICATE_OBJECTID"}, + {STATUS_OBJECTID_EXISTS, -EIO, "STATUS_OBJECTID_EXISTS"}, + {STATUS_CONVERT_TO_LARGE, -EIO, "STATUS_CONVERT_TO_LARGE"}, + {STATUS_RETRY, -EAGAIN, "STATUS_RETRY"}, + {STATUS_FOUND_OUT_OF_SCOPE, -EIO, "STATUS_FOUND_OUT_OF_SCOPE"}, + {STATUS_ALLOCATE_BUCKET, -EIO, "STATUS_ALLOCATE_BUCKET"}, + {STATUS_PROPSET_NOT_FOUND, -EIO, "STATUS_PROPSET_NOT_FOUND"}, + {STATUS_MARSHALL_OVERFLOW, -EIO, "STATUS_MARSHALL_OVERFLOW"}, + {STATUS_INVALID_VARIANT, -EIO, "STATUS_INVALID_VARIANT"}, + {STATUS_DOMAIN_CONTROLLER_NOT_FOUND, -EIO, + "STATUS_DOMAIN_CONTROLLER_NOT_FOUND"}, + {STATUS_ACCOUNT_LOCKED_OUT, -EACCES, "STATUS_ACCOUNT_LOCKED_OUT"}, + {STATUS_HANDLE_NOT_CLOSABLE, -EIO, "STATUS_HANDLE_NOT_CLOSABLE"}, + {STATUS_CONNECTION_REFUSED, -EIO, "STATUS_CONNECTION_REFUSED"}, + {STATUS_GRACEFUL_DISCONNECT, -EIO, "STATUS_GRACEFUL_DISCONNECT"}, + {STATUS_ADDRESS_ALREADY_ASSOCIATED, -EIO, + "STATUS_ADDRESS_ALREADY_ASSOCIATED"}, + {STATUS_ADDRESS_NOT_ASSOCIATED, -EIO, "STATUS_ADDRESS_NOT_ASSOCIATED"}, + {STATUS_CONNECTION_INVALID, -EIO, "STATUS_CONNECTION_INVALID"}, + {STATUS_CONNECTION_ACTIVE, -EIO, "STATUS_CONNECTION_ACTIVE"}, + {STATUS_NETWORK_UNREACHABLE, -ENETUNREACH, + "STATUS_NETWORK_UNREACHABLE"}, + {STATUS_HOST_UNREACHABLE, -EHOSTDOWN, "STATUS_HOST_UNREACHABLE"}, + {STATUS_PROTOCOL_UNREACHABLE, -ENETUNREACH, + "STATUS_PROTOCOL_UNREACHABLE"}, + {STATUS_PORT_UNREACHABLE, -ENETUNREACH, "STATUS_PORT_UNREACHABLE"}, + {STATUS_REQUEST_ABORTED, -EIO, "STATUS_REQUEST_ABORTED"}, + {STATUS_CONNECTION_ABORTED, -ECONNABORTED, "STATUS_CONNECTION_ABORTED"}, + {STATUS_BAD_COMPRESSION_BUFFER, -EIO, "STATUS_BAD_COMPRESSION_BUFFER"}, + {STATUS_USER_MAPPED_FILE, -EIO, "STATUS_USER_MAPPED_FILE"}, + {STATUS_AUDIT_FAILED, -EIO, "STATUS_AUDIT_FAILED"}, + {STATUS_TIMER_RESOLUTION_NOT_SET, -EIO, + "STATUS_TIMER_RESOLUTION_NOT_SET"}, + {STATUS_CONNECTION_COUNT_LIMIT, -EIO, "STATUS_CONNECTION_COUNT_LIMIT"}, + {STATUS_LOGIN_TIME_RESTRICTION, -EACCES, + "STATUS_LOGIN_TIME_RESTRICTION"}, + {STATUS_LOGIN_WKSTA_RESTRICTION, -EACCES, + "STATUS_LOGIN_WKSTA_RESTRICTION"}, + {STATUS_IMAGE_MP_UP_MISMATCH, -EIO, "STATUS_IMAGE_MP_UP_MISMATCH"}, + {STATUS_INSUFFICIENT_LOGON_INFO, -EIO, + "STATUS_INSUFFICIENT_LOGON_INFO"}, + {STATUS_BAD_DLL_ENTRYPOINT, -EIO, "STATUS_BAD_DLL_ENTRYPOINT"}, + {STATUS_BAD_SERVICE_ENTRYPOINT, -EIO, "STATUS_BAD_SERVICE_ENTRYPOINT"}, + {STATUS_LPC_REPLY_LOST, -EIO, "STATUS_LPC_REPLY_LOST"}, + {STATUS_IP_ADDRESS_CONFLICT1, -EIO, "STATUS_IP_ADDRESS_CONFLICT1"}, + {STATUS_IP_ADDRESS_CONFLICT2, -EIO, "STATUS_IP_ADDRESS_CONFLICT2"}, + {STATUS_REGISTRY_QUOTA_LIMIT, -EDQUOT, "STATUS_REGISTRY_QUOTA_LIMIT"}, + {STATUS_PATH_NOT_COVERED, -EREMOTE, "STATUS_PATH_NOT_COVERED"}, + {STATUS_NO_CALLBACK_ACTIVE, -EIO, "STATUS_NO_CALLBACK_ACTIVE"}, + {STATUS_LICENSE_QUOTA_EXCEEDED, -EACCES, + "STATUS_LICENSE_QUOTA_EXCEEDED"}, + {STATUS_PWD_TOO_SHORT, -EIO, "STATUS_PWD_TOO_SHORT"}, + {STATUS_PWD_TOO_RECENT, -EIO, "STATUS_PWD_TOO_RECENT"}, + {STATUS_PWD_HISTORY_CONFLICT, -EIO, "STATUS_PWD_HISTORY_CONFLICT"}, + {STATUS_PLUGPLAY_NO_DEVICE, -EIO, "STATUS_PLUGPLAY_NO_DEVICE"}, + {STATUS_UNSUPPORTED_COMPRESSION, -EIO, + "STATUS_UNSUPPORTED_COMPRESSION"}, + {STATUS_INVALID_HW_PROFILE, -EIO, "STATUS_INVALID_HW_PROFILE"}, + {STATUS_INVALID_PLUGPLAY_DEVICE_PATH, -EIO, + "STATUS_INVALID_PLUGPLAY_DEVICE_PATH"}, + {STATUS_DRIVER_ORDINAL_NOT_FOUND, -EIO, + "STATUS_DRIVER_ORDINAL_NOT_FOUND"}, + {STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, -EIO, + "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND"}, + {STATUS_RESOURCE_NOT_OWNED, -EIO, "STATUS_RESOURCE_NOT_OWNED"}, + {STATUS_TOO_MANY_LINKS, -EMLINK, "STATUS_TOO_MANY_LINKS"}, + {STATUS_QUOTA_LIST_INCONSISTENT, -EIO, + "STATUS_QUOTA_LIST_INCONSISTENT"}, + {STATUS_FILE_IS_OFFLINE, -EIO, "STATUS_FILE_IS_OFFLINE"}, + {STATUS_EVALUATION_EXPIRATION, -EIO, "STATUS_EVALUATION_EXPIRATION"}, + {STATUS_ILLEGAL_DLL_RELOCATION, -EIO, "STATUS_ILLEGAL_DLL_RELOCATION"}, + {STATUS_LICENSE_VIOLATION, -EIO, "STATUS_LICENSE_VIOLATION"}, + {STATUS_DLL_INIT_FAILED_LOGOFF, -EIO, "STATUS_DLL_INIT_FAILED_LOGOFF"}, + {STATUS_DRIVER_UNABLE_TO_LOAD, -EIO, "STATUS_DRIVER_UNABLE_TO_LOAD"}, + {STATUS_DFS_UNAVAILABLE, -EIO, "STATUS_DFS_UNAVAILABLE"}, + {STATUS_VOLUME_DISMOUNTED, -EIO, "STATUS_VOLUME_DISMOUNTED"}, + {STATUS_WX86_INTERNAL_ERROR, -EIO, "STATUS_WX86_INTERNAL_ERROR"}, + {STATUS_WX86_FLOAT_STACK_CHECK, -EIO, "STATUS_WX86_FLOAT_STACK_CHECK"}, + {STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"}, + {STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"}, + {STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"}, + {STATUS_NOT_A_REPARSE_POINT, -EIO, "STATUS_NOT_A_REPARSE_POINT"}, + {STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"}, + {STATUS_IO_REPARSE_TAG_MISMATCH, -EIO, + "STATUS_IO_REPARSE_TAG_MISMATCH"}, + {STATUS_IO_REPARSE_DATA_INVALID, -EIO, + "STATUS_IO_REPARSE_DATA_INVALID"}, + {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EIO, + "STATUS_IO_REPARSE_TAG_NOT_HANDLED"}, + {STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO, + "STATUS_REPARSE_POINT_NOT_RESOLVED"}, + {STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO, + "STATUS_DIRECTORY_IS_A_REPARSE_POINT"}, + {STATUS_RANGE_LIST_CONFLICT, -EIO, "STATUS_RANGE_LIST_CONFLICT"}, + {STATUS_SOURCE_ELEMENT_EMPTY, -EIO, "STATUS_SOURCE_ELEMENT_EMPTY"}, + {STATUS_DESTINATION_ELEMENT_FULL, -EIO, + "STATUS_DESTINATION_ELEMENT_FULL"}, + {STATUS_ILLEGAL_ELEMENT_ADDRESS, -EIO, + "STATUS_ILLEGAL_ELEMENT_ADDRESS"}, + {STATUS_MAGAZINE_NOT_PRESENT, -EIO, "STATUS_MAGAZINE_NOT_PRESENT"}, + {STATUS_REINITIALIZATION_NEEDED, -EIO, + "STATUS_REINITIALIZATION_NEEDED"}, + {STATUS_ENCRYPTION_FAILED, -EIO, "STATUS_ENCRYPTION_FAILED"}, + {STATUS_DECRYPTION_FAILED, -EIO, "STATUS_DECRYPTION_FAILED"}, + {STATUS_RANGE_NOT_FOUND, -EIO, "STATUS_RANGE_NOT_FOUND"}, + {STATUS_NO_RECOVERY_POLICY, -EIO, "STATUS_NO_RECOVERY_POLICY"}, + {STATUS_NO_EFS, -EIO, "STATUS_NO_EFS"}, + {STATUS_WRONG_EFS, -EIO, "STATUS_WRONG_EFS"}, + {STATUS_NO_USER_KEYS, -EIO, "STATUS_NO_USER_KEYS"}, + {STATUS_FILE_NOT_ENCRYPTED, -EIO, "STATUS_FILE_NOT_ENCRYPTED"}, + {STATUS_NOT_EXPORT_FORMAT, -EIO, "STATUS_NOT_EXPORT_FORMAT"}, + {STATUS_FILE_ENCRYPTED, -EIO, "STATUS_FILE_ENCRYPTED"}, + {STATUS_WMI_GUID_NOT_FOUND, -EIO, "STATUS_WMI_GUID_NOT_FOUND"}, + {STATUS_WMI_INSTANCE_NOT_FOUND, -EIO, "STATUS_WMI_INSTANCE_NOT_FOUND"}, + {STATUS_WMI_ITEMID_NOT_FOUND, -EIO, "STATUS_WMI_ITEMID_NOT_FOUND"}, + {STATUS_WMI_TRY_AGAIN, -EIO, "STATUS_WMI_TRY_AGAIN"}, + {STATUS_SHARED_POLICY, -EIO, "STATUS_SHARED_POLICY"}, + {STATUS_POLICY_OBJECT_NOT_FOUND, -EIO, + "STATUS_POLICY_OBJECT_NOT_FOUND"}, + {STATUS_POLICY_ONLY_IN_DS, -EIO, "STATUS_POLICY_ONLY_IN_DS"}, + {STATUS_VOLUME_NOT_UPGRADED, -EIO, "STATUS_VOLUME_NOT_UPGRADED"}, + {STATUS_REMOTE_STORAGE_NOT_ACTIVE, -EIO, + "STATUS_REMOTE_STORAGE_NOT_ACTIVE"}, + {STATUS_REMOTE_STORAGE_MEDIA_ERROR, -EIO, + "STATUS_REMOTE_STORAGE_MEDIA_ERROR"}, + {STATUS_NO_TRACKING_SERVICE, -EIO, "STATUS_NO_TRACKING_SERVICE"}, + {STATUS_SERVER_SID_MISMATCH, -EIO, "STATUS_SERVER_SID_MISMATCH"}, + {STATUS_DS_NO_ATTRIBUTE_OR_VALUE, -EIO, + "STATUS_DS_NO_ATTRIBUTE_OR_VALUE"}, + {STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, -EIO, + "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX"}, + {STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, -EIO, + "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED"}, + {STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, -EIO, + "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS"}, + {STATUS_DS_BUSY, -EBUSY, "STATUS_DS_BUSY"}, + {STATUS_DS_UNAVAILABLE, -EIO, "STATUS_DS_UNAVAILABLE"}, + {STATUS_DS_NO_RIDS_ALLOCATED, -EIO, "STATUS_DS_NO_RIDS_ALLOCATED"}, + {STATUS_DS_NO_MORE_RIDS, -EIO, "STATUS_DS_NO_MORE_RIDS"}, + {STATUS_DS_INCORRECT_ROLE_OWNER, -EIO, + "STATUS_DS_INCORRECT_ROLE_OWNER"}, + {STATUS_DS_RIDMGR_INIT_ERROR, -EIO, "STATUS_DS_RIDMGR_INIT_ERROR"}, + {STATUS_DS_OBJ_CLASS_VIOLATION, -EIO, "STATUS_DS_OBJ_CLASS_VIOLATION"}, + {STATUS_DS_CANT_ON_NON_LEAF, -EIO, "STATUS_DS_CANT_ON_NON_LEAF"}, + {STATUS_DS_CANT_ON_RDN, -EIO, "STATUS_DS_CANT_ON_RDN"}, + {STATUS_DS_CANT_MOD_OBJ_CLASS, -EIO, "STATUS_DS_CANT_MOD_OBJ_CLASS"}, + {STATUS_DS_CROSS_DOM_MOVE_FAILED, -EIO, + "STATUS_DS_CROSS_DOM_MOVE_FAILED"}, + {STATUS_DS_GC_NOT_AVAILABLE, -EIO, "STATUS_DS_GC_NOT_AVAILABLE"}, + {STATUS_DIRECTORY_SERVICE_REQUIRED, -EIO, + "STATUS_DIRECTORY_SERVICE_REQUIRED"}, + {STATUS_REPARSE_ATTRIBUTE_CONFLICT, -EIO, + "STATUS_REPARSE_ATTRIBUTE_CONFLICT"}, + {STATUS_CANT_ENABLE_DENY_ONLY, -EIO, "STATUS_CANT_ENABLE_DENY_ONLY"}, + {STATUS_FLOAT_MULTIPLE_FAULTS, -EIO, "STATUS_FLOAT_MULTIPLE_FAULTS"}, + {STATUS_FLOAT_MULTIPLE_TRAPS, -EIO, "STATUS_FLOAT_MULTIPLE_TRAPS"}, + {STATUS_DEVICE_REMOVED, -EIO, "STATUS_DEVICE_REMOVED"}, + {STATUS_JOURNAL_DELETE_IN_PROGRESS, -EIO, + "STATUS_JOURNAL_DELETE_IN_PROGRESS"}, + {STATUS_JOURNAL_NOT_ACTIVE, -EIO, "STATUS_JOURNAL_NOT_ACTIVE"}, + {STATUS_NOINTERFACE, -EIO, "STATUS_NOINTERFACE"}, + {STATUS_DS_ADMIN_LIMIT_EXCEEDED, -EIO, + "STATUS_DS_ADMIN_LIMIT_EXCEEDED"}, + {STATUS_DRIVER_FAILED_SLEEP, -EIO, "STATUS_DRIVER_FAILED_SLEEP"}, + {STATUS_MUTUAL_AUTHENTICATION_FAILED, -EIO, + "STATUS_MUTUAL_AUTHENTICATION_FAILED"}, + {STATUS_CORRUPT_SYSTEM_FILE, -EIO, "STATUS_CORRUPT_SYSTEM_FILE"}, + {STATUS_DATATYPE_MISALIGNMENT_ERROR, -EIO, + "STATUS_DATATYPE_MISALIGNMENT_ERROR"}, + {STATUS_WMI_READ_ONLY, -EROFS, "STATUS_WMI_READ_ONLY"}, + {STATUS_WMI_SET_FAILURE, -EIO, "STATUS_WMI_SET_FAILURE"}, + {STATUS_COMMITMENT_MINIMUM, -EIO, "STATUS_COMMITMENT_MINIMUM"}, + {STATUS_REG_NAT_CONSUMPTION, -EIO, "STATUS_REG_NAT_CONSUMPTION"}, + {STATUS_TRANSPORT_FULL, -EIO, "STATUS_TRANSPORT_FULL"}, + {STATUS_DS_SAM_INIT_FAILURE, -EIO, "STATUS_DS_SAM_INIT_FAILURE"}, + {STATUS_ONLY_IF_CONNECTED, -EIO, "STATUS_ONLY_IF_CONNECTED"}, + {STATUS_DS_SENSITIVE_GROUP_VIOLATION, -EIO, + "STATUS_DS_SENSITIVE_GROUP_VIOLATION"}, + {STATUS_PNP_RESTART_ENUMERATION, -EIO, + "STATUS_PNP_RESTART_ENUMERATION"}, + {STATUS_JOURNAL_ENTRY_DELETED, -EIO, "STATUS_JOURNAL_ENTRY_DELETED"}, + {STATUS_DS_CANT_MOD_PRIMARYGROUPID, -EIO, + "STATUS_DS_CANT_MOD_PRIMARYGROUPID"}, + {STATUS_SYSTEM_IMAGE_BAD_SIGNATURE, -EIO, + "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE"}, + {STATUS_PNP_REBOOT_REQUIRED, -EIO, "STATUS_PNP_REBOOT_REQUIRED"}, + {STATUS_POWER_STATE_INVALID, -EIO, "STATUS_POWER_STATE_INVALID"}, + {STATUS_DS_INVALID_GROUP_TYPE, -EIO, "STATUS_DS_INVALID_GROUP_TYPE"}, + {STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, -EIO, + "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN"}, + {STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, -EIO, + "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN"}, + {STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, -EIO, + "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER"}, + {STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, + "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER"}, + {STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, -EIO, + "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER"}, + {STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, -EIO, + "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER"}, + {STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, -EIO, + "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER"}, + {STATUS_DS_HAVE_PRIMARY_MEMBERS, -EIO, + "STATUS_DS_HAVE_PRIMARY_MEMBERS"}, + {STATUS_WMI_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_WMI_NOT_SUPPORTED"}, + {STATUS_INSUFFICIENT_POWER, -EIO, "STATUS_INSUFFICIENT_POWER"}, + {STATUS_SAM_NEED_BOOTKEY_PASSWORD, -EIO, + "STATUS_SAM_NEED_BOOTKEY_PASSWORD"}, + {STATUS_SAM_NEED_BOOTKEY_FLOPPY, -EIO, + "STATUS_SAM_NEED_BOOTKEY_FLOPPY"}, + {STATUS_DS_CANT_START, -EIO, "STATUS_DS_CANT_START"}, + {STATUS_DS_INIT_FAILURE, -EIO, "STATUS_DS_INIT_FAILURE"}, + {STATUS_SAM_INIT_FAILURE, -EIO, "STATUS_SAM_INIT_FAILURE"}, + {STATUS_DS_GC_REQUIRED, -EIO, "STATUS_DS_GC_REQUIRED"}, + {STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, -EIO, + "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY"}, + {STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, -EIO, + "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS"}, + {STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED"}, + {STATUS_MULTIPLE_FAULT_VIOLATION, -EIO, + "STATUS_MULTIPLE_FAULT_VIOLATION"}, + {STATUS_CURRENT_DOMAIN_NOT_ALLOWED, -EIO, + "STATUS_CURRENT_DOMAIN_NOT_ALLOWED"}, + {STATUS_CANNOT_MAKE, -EIO, "STATUS_CANNOT_MAKE"}, + {STATUS_SYSTEM_SHUTDOWN, -EIO, "STATUS_SYSTEM_SHUTDOWN"}, + {STATUS_DS_INIT_FAILURE_CONSOLE, -EIO, + "STATUS_DS_INIT_FAILURE_CONSOLE"}, + {STATUS_DS_SAM_INIT_FAILURE_CONSOLE, -EIO, + "STATUS_DS_SAM_INIT_FAILURE_CONSOLE"}, + {STATUS_UNFINISHED_CONTEXT_DELETED, -EIO, + "STATUS_UNFINISHED_CONTEXT_DELETED"}, + {STATUS_NO_TGT_REPLY, -EIO, "STATUS_NO_TGT_REPLY"}, + /* Note that ENOATTTR and ENODATA are the same errno */ + {STATUS_OBJECTID_NOT_FOUND, -ENODATA, "STATUS_OBJECTID_NOT_FOUND"}, + {STATUS_NO_IP_ADDRESSES, -EIO, "STATUS_NO_IP_ADDRESSES"}, + {STATUS_WRONG_CREDENTIAL_HANDLE, -EIO, + "STATUS_WRONG_CREDENTIAL_HANDLE"}, + {STATUS_CRYPTO_SYSTEM_INVALID, -EIO, "STATUS_CRYPTO_SYSTEM_INVALID"}, + {STATUS_MAX_REFERRALS_EXCEEDED, -EIO, "STATUS_MAX_REFERRALS_EXCEEDED"}, + {STATUS_MUST_BE_KDC, -EIO, "STATUS_MUST_BE_KDC"}, + {STATUS_STRONG_CRYPTO_NOT_SUPPORTED, -EIO, + "STATUS_STRONG_CRYPTO_NOT_SUPPORTED"}, + {STATUS_TOO_MANY_PRINCIPALS, -EIO, "STATUS_TOO_MANY_PRINCIPALS"}, + {STATUS_NO_PA_DATA, -EIO, "STATUS_NO_PA_DATA"}, + {STATUS_PKINIT_NAME_MISMATCH, -EIO, "STATUS_PKINIT_NAME_MISMATCH"}, + {STATUS_SMARTCARD_LOGON_REQUIRED, -EIO, + "STATUS_SMARTCARD_LOGON_REQUIRED"}, + {STATUS_KDC_INVALID_REQUEST, -EIO, "STATUS_KDC_INVALID_REQUEST"}, + {STATUS_KDC_UNABLE_TO_REFER, -EIO, "STATUS_KDC_UNABLE_TO_REFER"}, + {STATUS_KDC_UNKNOWN_ETYPE, -EIO, "STATUS_KDC_UNKNOWN_ETYPE"}, + {STATUS_SHUTDOWN_IN_PROGRESS, -EIO, "STATUS_SHUTDOWN_IN_PROGRESS"}, + {STATUS_SERVER_SHUTDOWN_IN_PROGRESS, -EIO, + "STATUS_SERVER_SHUTDOWN_IN_PROGRESS"}, + {STATUS_NOT_SUPPORTED_ON_SBS, -EOPNOTSUPP, + "STATUS_NOT_SUPPORTED_ON_SBS"}, + {STATUS_WMI_GUID_DISCONNECTED, -EIO, "STATUS_WMI_GUID_DISCONNECTED"}, + {STATUS_WMI_ALREADY_DISABLED, -EIO, "STATUS_WMI_ALREADY_DISABLED"}, + {STATUS_WMI_ALREADY_ENABLED, -EIO, "STATUS_WMI_ALREADY_ENABLED"}, + {STATUS_MFT_TOO_FRAGMENTED, -EIO, "STATUS_MFT_TOO_FRAGMENTED"}, + {STATUS_COPY_PROTECTION_FAILURE, -EIO, + "STATUS_COPY_PROTECTION_FAILURE"}, + {STATUS_CSS_AUTHENTICATION_FAILURE, -EIO, + "STATUS_CSS_AUTHENTICATION_FAILURE"}, + {STATUS_CSS_KEY_NOT_PRESENT, -EIO, "STATUS_CSS_KEY_NOT_PRESENT"}, + {STATUS_CSS_KEY_NOT_ESTABLISHED, -EIO, + "STATUS_CSS_KEY_NOT_ESTABLISHED"}, + {STATUS_CSS_SCRAMBLED_SECTOR, -EIO, "STATUS_CSS_SCRAMBLED_SECTOR"}, + {STATUS_CSS_REGION_MISMATCH, -EIO, "STATUS_CSS_REGION_MISMATCH"}, + {STATUS_CSS_RESETS_EXHAUSTED, -EIO, "STATUS_CSS_RESETS_EXHAUSTED"}, + {STATUS_PKINIT_FAILURE, -EIO, "STATUS_PKINIT_FAILURE"}, + {STATUS_SMARTCARD_SUBSYSTEM_FAILURE, -EIO, + "STATUS_SMARTCARD_SUBSYSTEM_FAILURE"}, + {STATUS_NO_KERB_KEY, -EIO, "STATUS_NO_KERB_KEY"}, + {STATUS_HOST_DOWN, -EIO, "STATUS_HOST_DOWN"}, + {STATUS_UNSUPPORTED_PREAUTH, -EIO, "STATUS_UNSUPPORTED_PREAUTH"}, + {STATUS_EFS_ALG_BLOB_TOO_BIG, -EIO, "STATUS_EFS_ALG_BLOB_TOO_BIG"}, + {STATUS_PORT_NOT_SET, -EIO, "STATUS_PORT_NOT_SET"}, + {STATUS_DEBUGGER_INACTIVE, -EIO, "STATUS_DEBUGGER_INACTIVE"}, + {STATUS_DS_VERSION_CHECK_FAILURE, -EIO, + "STATUS_DS_VERSION_CHECK_FAILURE"}, + {STATUS_AUDITING_DISABLED, -EIO, "STATUS_AUDITING_DISABLED"}, + {STATUS_PRENT4_MACHINE_ACCOUNT, -EIO, "STATUS_PRENT4_MACHINE_ACCOUNT"}, + {STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, + "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER"}, + {STATUS_INVALID_IMAGE_WIN_32, -EIO, "STATUS_INVALID_IMAGE_WIN_32"}, + {STATUS_INVALID_IMAGE_WIN_64, -EIO, "STATUS_INVALID_IMAGE_WIN_64"}, + {STATUS_BAD_BINDINGS, -EIO, "STATUS_BAD_BINDINGS"}, + {STATUS_NETWORK_SESSION_EXPIRED, -EIO, + "STATUS_NETWORK_SESSION_EXPIRED"}, + {STATUS_APPHELP_BLOCK, -EIO, "STATUS_APPHELP_BLOCK"}, + {STATUS_ALL_SIDS_FILTERED, -EIO, "STATUS_ALL_SIDS_FILTERED"}, + {STATUS_NOT_SAFE_MODE_DRIVER, -EIO, "STATUS_NOT_SAFE_MODE_DRIVER"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_PATH, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_PATH"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER"}, + {STATUS_FAILED_DRIVER_ENTRY, -EIO, "STATUS_FAILED_DRIVER_ENTRY"}, + {STATUS_DEVICE_ENUMERATION_ERROR, -EIO, + "STATUS_DEVICE_ENUMERATION_ERROR"}, + {STATUS_MOUNT_POINT_NOT_RESOLVED, -EIO, + "STATUS_MOUNT_POINT_NOT_RESOLVED"}, + {STATUS_INVALID_DEVICE_OBJECT_PARAMETER, -EIO, + "STATUS_INVALID_DEVICE_OBJECT_PARAMETER"}, + {STATUS_MCA_OCCURED, -EIO, "STATUS_MCA_OCCURED"}, + {STATUS_DRIVER_BLOCKED_CRITICAL, -EIO, + "STATUS_DRIVER_BLOCKED_CRITICAL"}, + {STATUS_DRIVER_BLOCKED, -EIO, "STATUS_DRIVER_BLOCKED"}, + {STATUS_DRIVER_DATABASE_ERROR, -EIO, "STATUS_DRIVER_DATABASE_ERROR"}, + {STATUS_SYSTEM_HIVE_TOO_LARGE, -EIO, "STATUS_SYSTEM_HIVE_TOO_LARGE"}, + {STATUS_INVALID_IMPORT_OF_NON_DLL, -EIO, + "STATUS_INVALID_IMPORT_OF_NON_DLL"}, + {STATUS_NO_SECRETS, -EIO, "STATUS_NO_SECRETS"}, + {STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, -EACCES, + "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"}, + {STATUS_FAILED_STACK_SWITCH, -EIO, "STATUS_FAILED_STACK_SWITCH"}, + {STATUS_HEAP_CORRUPTION, -EIO, "STATUS_HEAP_CORRUPTION"}, + {STATUS_SMARTCARD_WRONG_PIN, -EIO, "STATUS_SMARTCARD_WRONG_PIN"}, + {STATUS_SMARTCARD_CARD_BLOCKED, -EIO, "STATUS_SMARTCARD_CARD_BLOCKED"}, + {STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED, -EIO, + "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED"}, + {STATUS_SMARTCARD_NO_CARD, -EIO, "STATUS_SMARTCARD_NO_CARD"}, + {STATUS_SMARTCARD_NO_KEY_CONTAINER, -EIO, + "STATUS_SMARTCARD_NO_KEY_CONTAINER"}, + {STATUS_SMARTCARD_NO_CERTIFICATE, -EIO, + "STATUS_SMARTCARD_NO_CERTIFICATE"}, + {STATUS_SMARTCARD_NO_KEYSET, -EIO, "STATUS_SMARTCARD_NO_KEYSET"}, + {STATUS_SMARTCARD_IO_ERROR, -EIO, "STATUS_SMARTCARD_IO_ERROR"}, + {STATUS_DOWNGRADE_DETECTED, -EIO, "STATUS_DOWNGRADE_DETECTED"}, + {STATUS_SMARTCARD_CERT_REVOKED, -EIO, "STATUS_SMARTCARD_CERT_REVOKED"}, + {STATUS_ISSUING_CA_UNTRUSTED, -EIO, "STATUS_ISSUING_CA_UNTRUSTED"}, + {STATUS_REVOCATION_OFFLINE_C, -EIO, "STATUS_REVOCATION_OFFLINE_C"}, + {STATUS_PKINIT_CLIENT_FAILURE, -EIO, "STATUS_PKINIT_CLIENT_FAILURE"}, + {STATUS_SMARTCARD_CERT_EXPIRED, -EIO, "STATUS_SMARTCARD_CERT_EXPIRED"}, + {STATUS_DRIVER_FAILED_PRIOR_UNLOAD, -EIO, + "STATUS_DRIVER_FAILED_PRIOR_UNLOAD"}, + {STATUS_SMARTCARD_SILENT_CONTEXT, -EIO, + "STATUS_SMARTCARD_SILENT_CONTEXT"}, + {STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED"}, + {STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED"}, + {STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED"}, + {STATUS_DS_NAME_NOT_UNIQUE, -EIO, "STATUS_DS_NAME_NOT_UNIQUE"}, + {STATUS_DS_DUPLICATE_ID_FOUND, -EIO, "STATUS_DS_DUPLICATE_ID_FOUND"}, + {STATUS_DS_GROUP_CONVERSION_ERROR, -EIO, + "STATUS_DS_GROUP_CONVERSION_ERROR"}, + {STATUS_VOLSNAP_PREPARE_HIBERNATE, -EIO, + "STATUS_VOLSNAP_PREPARE_HIBERNATE"}, + {STATUS_USER2USER_REQUIRED, -EIO, "STATUS_USER2USER_REQUIRED"}, + {STATUS_STACK_BUFFER_OVERRUN, -EIO, "STATUS_STACK_BUFFER_OVERRUN"}, + {STATUS_NO_S4U_PROT_SUPPORT, -EIO, "STATUS_NO_S4U_PROT_SUPPORT"}, + {STATUS_CROSSREALM_DELEGATION_FAILURE, -EIO, + "STATUS_CROSSREALM_DELEGATION_FAILURE"}, + {STATUS_REVOCATION_OFFLINE_KDC, -EIO, "STATUS_REVOCATION_OFFLINE_KDC"}, + {STATUS_ISSUING_CA_UNTRUSTED_KDC, -EIO, + "STATUS_ISSUING_CA_UNTRUSTED_KDC"}, + {STATUS_KDC_CERT_EXPIRED, -EIO, "STATUS_KDC_CERT_EXPIRED"}, + {STATUS_KDC_CERT_REVOKED, -EIO, "STATUS_KDC_CERT_REVOKED"}, + {STATUS_PARAMETER_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_PARAMETER_QUOTA_EXCEEDED"}, + {STATUS_HIBERNATION_FAILURE, -EIO, "STATUS_HIBERNATION_FAILURE"}, + {STATUS_DELAY_LOAD_FAILED, -EIO, "STATUS_DELAY_LOAD_FAILED"}, + {STATUS_AUTHENTICATION_FIREWALL_FAILED, -EIO, + "STATUS_AUTHENTICATION_FIREWALL_FAILED"}, + {STATUS_VDM_DISALLOWED, -EIO, "STATUS_VDM_DISALLOWED"}, + {STATUS_HUNG_DISPLAY_DRIVER_THREAD, -EIO, + "STATUS_HUNG_DISPLAY_DRIVER_THREAD"}, + {STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, -EIO, + "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE"}, + {STATUS_INVALID_CRUNTIME_PARAMETER, -EIO, + "STATUS_INVALID_CRUNTIME_PARAMETER"}, + {STATUS_NTLM_BLOCKED, -EIO, "STATUS_NTLM_BLOCKED"}, + {STATUS_ASSERTION_FAILURE, -EIO, "STATUS_ASSERTION_FAILURE"}, + {STATUS_VERIFIER_STOP, -EIO, "STATUS_VERIFIER_STOP"}, + {STATUS_CALLBACK_POP_STACK, -EIO, "STATUS_CALLBACK_POP_STACK"}, + {STATUS_INCOMPATIBLE_DRIVER_BLOCKED, -EIO, + "STATUS_INCOMPATIBLE_DRIVER_BLOCKED"}, + {STATUS_HIVE_UNLOADED, -EIO, "STATUS_HIVE_UNLOADED"}, + {STATUS_COMPRESSION_DISABLED, -EIO, "STATUS_COMPRESSION_DISABLED"}, + {STATUS_FILE_SYSTEM_LIMITATION, -EIO, "STATUS_FILE_SYSTEM_LIMITATION"}, + {STATUS_INVALID_IMAGE_HASH, -EIO, "STATUS_INVALID_IMAGE_HASH"}, + {STATUS_NOT_CAPABLE, -EIO, "STATUS_NOT_CAPABLE"}, + {STATUS_REQUEST_OUT_OF_SEQUENCE, -EIO, + "STATUS_REQUEST_OUT_OF_SEQUENCE"}, + {STATUS_IMPLEMENTATION_LIMIT, -EIO, "STATUS_IMPLEMENTATION_LIMIT"}, + {STATUS_ELEVATION_REQUIRED, -EIO, "STATUS_ELEVATION_REQUIRED"}, + {STATUS_BEYOND_VDL, -EIO, "STATUS_BEYOND_VDL"}, + {STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, -EIO, + "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS"}, + {STATUS_PTE_CHANGED, -EIO, "STATUS_PTE_CHANGED"}, + {STATUS_PURGE_FAILED, -EIO, "STATUS_PURGE_FAILED"}, + {STATUS_CRED_REQUIRES_CONFIRMATION, -EIO, + "STATUS_CRED_REQUIRES_CONFIRMATION"}, + {STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, -EIO, + "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE"}, + {STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, -EIO, + "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER"}, + {STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, -EIO, + "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE"}, + {STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, -EIO, + "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE"}, + {STATUS_CS_ENCRYPTION_FILE_NOT_CSE, -EIO, + "STATUS_CS_ENCRYPTION_FILE_NOT_CSE"}, + {STATUS_INVALID_LABEL, -EIO, "STATUS_INVALID_LABEL"}, + {STATUS_DRIVER_PROCESS_TERMINATED, -EIO, + "STATUS_DRIVER_PROCESS_TERMINATED"}, + {STATUS_AMBIGUOUS_SYSTEM_DEVICE, -EIO, + "STATUS_AMBIGUOUS_SYSTEM_DEVICE"}, + {STATUS_SYSTEM_DEVICE_NOT_FOUND, -EIO, + "STATUS_SYSTEM_DEVICE_NOT_FOUND"}, + {STATUS_RESTART_BOOT_APPLICATION, -EIO, + "STATUS_RESTART_BOOT_APPLICATION"}, + {STATUS_INVALID_TASK_NAME, -EIO, "STATUS_INVALID_TASK_NAME"}, + {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"}, + {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"}, + {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"}, + {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"}, + {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"}, + {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"}, + {STATUS_REQUEST_CANCELED, -EIO, "STATUS_REQUEST_CANCELED"}, + {STATUS_RECURSIVE_DISPATCH, -EIO, "STATUS_RECURSIVE_DISPATCH"}, + {STATUS_LPC_RECEIVE_BUFFER_EXPECTED, -EIO, + "STATUS_LPC_RECEIVE_BUFFER_EXPECTED"}, + {STATUS_LPC_INVALID_CONNECTION_USAGE, -EIO, + "STATUS_LPC_INVALID_CONNECTION_USAGE"}, + {STATUS_LPC_REQUESTS_NOT_ALLOWED, -EIO, + "STATUS_LPC_REQUESTS_NOT_ALLOWED"}, + {STATUS_RESOURCE_IN_USE, -EIO, "STATUS_RESOURCE_IN_USE"}, + {STATUS_HARDWARE_MEMORY_ERROR, -EIO, "STATUS_HARDWARE_MEMORY_ERROR"}, + {STATUS_THREADPOOL_HANDLE_EXCEPTION, -EIO, + "STATUS_THREADPOOL_HANDLE_EXCEPTION"}, + {STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_RELEASED_DURING_OPERATION, -EIO, + "STATUS_THREADPOOL_RELEASED_DURING_OPERATION"}, + {STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, -EIO, + "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING"}, + {STATUS_APC_RETURNED_WHILE_IMPERSONATING, -EIO, + "STATUS_APC_RETURNED_WHILE_IMPERSONATING"}, + {STATUS_PROCESS_IS_PROTECTED, -EIO, "STATUS_PROCESS_IS_PROTECTED"}, + {STATUS_MCA_EXCEPTION, -EIO, "STATUS_MCA_EXCEPTION"}, + {STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, -EIO, + "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE"}, + {STATUS_SYMLINK_CLASS_DISABLED, -EIO, "STATUS_SYMLINK_CLASS_DISABLED"}, + {STATUS_INVALID_IDN_NORMALIZATION, -EIO, + "STATUS_INVALID_IDN_NORMALIZATION"}, + {STATUS_NO_UNICODE_TRANSLATION, -EIO, "STATUS_NO_UNICODE_TRANSLATION"}, + {STATUS_ALREADY_REGISTERED, -EIO, "STATUS_ALREADY_REGISTERED"}, + {STATUS_CONTEXT_MISMATCH, -EIO, "STATUS_CONTEXT_MISMATCH"}, + {STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, -EIO, + "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST"}, + {STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, -EIO, + "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY"}, + {STATUS_INVALID_THREAD, -EIO, "STATUS_INVALID_THREAD"}, + {STATUS_CALLBACK_RETURNED_TRANSACTION, -EIO, + "STATUS_CALLBACK_RETURNED_TRANSACTION"}, + {STATUS_CALLBACK_RETURNED_LDR_LOCK, -EIO, + "STATUS_CALLBACK_RETURNED_LDR_LOCK"}, + {STATUS_CALLBACK_RETURNED_LANG, -EIO, "STATUS_CALLBACK_RETURNED_LANG"}, + {STATUS_CALLBACK_RETURNED_PRI_BACK, -EIO, + "STATUS_CALLBACK_RETURNED_PRI_BACK"}, + {STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, -EIO, + "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY"}, + {STATUS_DISK_REPAIR_DISABLED, -EIO, "STATUS_DISK_REPAIR_DISABLED"}, + {STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, -EIO, + "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS"}, + {STATUS_DISK_QUOTA_EXCEEDED, -EDQUOT, "STATUS_DISK_QUOTA_EXCEEDED"}, + {STATUS_CONTENT_BLOCKED, -EIO, "STATUS_CONTENT_BLOCKED"}, + {STATUS_BAD_CLUSTERS, -EIO, "STATUS_BAD_CLUSTERS"}, + {STATUS_VOLUME_DIRTY, -EIO, "STATUS_VOLUME_DIRTY"}, + {STATUS_FILE_CHECKED_OUT, -EIO, "STATUS_FILE_CHECKED_OUT"}, + {STATUS_CHECKOUT_REQUIRED, -EIO, "STATUS_CHECKOUT_REQUIRED"}, + {STATUS_BAD_FILE_TYPE, -EIO, "STATUS_BAD_FILE_TYPE"}, + {STATUS_FILE_TOO_LARGE, -EIO, "STATUS_FILE_TOO_LARGE"}, + {STATUS_FORMS_AUTH_REQUIRED, -EIO, "STATUS_FORMS_AUTH_REQUIRED"}, + {STATUS_VIRUS_INFECTED, -EIO, "STATUS_VIRUS_INFECTED"}, + {STATUS_VIRUS_DELETED, -EIO, "STATUS_VIRUS_DELETED"}, + {STATUS_BAD_MCFG_TABLE, -EIO, "STATUS_BAD_MCFG_TABLE"}, + {STATUS_WOW_ASSERTION, -EIO, "STATUS_WOW_ASSERTION"}, + {STATUS_INVALID_SIGNATURE, -EIO, "STATUS_INVALID_SIGNATURE"}, + {STATUS_HMAC_NOT_SUPPORTED, -EIO, "STATUS_HMAC_NOT_SUPPORTED"}, + {STATUS_IPSEC_QUEUE_OVERFLOW, -EIO, "STATUS_IPSEC_QUEUE_OVERFLOW"}, + {STATUS_ND_QUEUE_OVERFLOW, -EIO, "STATUS_ND_QUEUE_OVERFLOW"}, + {STATUS_HOPLIMIT_EXCEEDED, -EIO, "STATUS_HOPLIMIT_EXCEEDED"}, + {STATUS_PROTOCOL_NOT_SUPPORTED, -EOPNOTSUPP, + "STATUS_PROTOCOL_NOT_SUPPORTED"}, + {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED, -EIO, + "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED"}, + {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR, -EIO, + "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR"}, + {STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR, -EIO, + "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR"}, + {STATUS_XML_PARSE_ERROR, -EIO, "STATUS_XML_PARSE_ERROR"}, + {STATUS_XMLDSIG_ERROR, -EIO, "STATUS_XMLDSIG_ERROR"}, + {STATUS_WRONG_COMPARTMENT, -EIO, "STATUS_WRONG_COMPARTMENT"}, + {STATUS_AUTHIP_FAILURE, -EIO, "STATUS_AUTHIP_FAILURE"}, + {DBG_NO_STATE_CHANGE, -EIO, "DBG_NO_STATE_CHANGE"}, + {DBG_APP_NOT_IDLE, -EIO, "DBG_APP_NOT_IDLE"}, + {RPC_NT_INVALID_STRING_BINDING, -EIO, "RPC_NT_INVALID_STRING_BINDING"}, + {RPC_NT_WRONG_KIND_OF_BINDING, -EIO, "RPC_NT_WRONG_KIND_OF_BINDING"}, + {RPC_NT_INVALID_BINDING, -EIO, "RPC_NT_INVALID_BINDING"}, + {RPC_NT_PROTSEQ_NOT_SUPPORTED, -EOPNOTSUPP, + "RPC_NT_PROTSEQ_NOT_SUPPORTED"}, + {RPC_NT_INVALID_RPC_PROTSEQ, -EIO, "RPC_NT_INVALID_RPC_PROTSEQ"}, + {RPC_NT_INVALID_STRING_UUID, -EIO, "RPC_NT_INVALID_STRING_UUID"}, + {RPC_NT_INVALID_ENDPOINT_FORMAT, -EIO, + "RPC_NT_INVALID_ENDPOINT_FORMAT"}, + {RPC_NT_INVALID_NET_ADDR, -EIO, "RPC_NT_INVALID_NET_ADDR"}, + {RPC_NT_NO_ENDPOINT_FOUND, -EIO, "RPC_NT_NO_ENDPOINT_FOUND"}, + {RPC_NT_INVALID_TIMEOUT, -EINVAL, "RPC_NT_INVALID_TIMEOUT"}, + {RPC_NT_OBJECT_NOT_FOUND, -ENOENT, "RPC_NT_OBJECT_NOT_FOUND"}, + {RPC_NT_ALREADY_REGISTERED, -EIO, "RPC_NT_ALREADY_REGISTERED"}, + {RPC_NT_TYPE_ALREADY_REGISTERED, -EIO, + "RPC_NT_TYPE_ALREADY_REGISTERED"}, + {RPC_NT_ALREADY_LISTENING, -EIO, "RPC_NT_ALREADY_LISTENING"}, + {RPC_NT_NO_PROTSEQS_REGISTERED, -EIO, "RPC_NT_NO_PROTSEQS_REGISTERED"}, + {RPC_NT_NOT_LISTENING, -EIO, "RPC_NT_NOT_LISTENING"}, + {RPC_NT_UNKNOWN_MGR_TYPE, -EIO, "RPC_NT_UNKNOWN_MGR_TYPE"}, + {RPC_NT_UNKNOWN_IF, -EIO, "RPC_NT_UNKNOWN_IF"}, + {RPC_NT_NO_BINDINGS, -EIO, "RPC_NT_NO_BINDINGS"}, + {RPC_NT_NO_PROTSEQS, -EIO, "RPC_NT_NO_PROTSEQS"}, + {RPC_NT_CANT_CREATE_ENDPOINT, -EIO, "RPC_NT_CANT_CREATE_ENDPOINT"}, + {RPC_NT_OUT_OF_RESOURCES, -EIO, "RPC_NT_OUT_OF_RESOURCES"}, + {RPC_NT_SERVER_UNAVAILABLE, -EIO, "RPC_NT_SERVER_UNAVAILABLE"}, + {RPC_NT_SERVER_TOO_BUSY, -EBUSY, "RPC_NT_SERVER_TOO_BUSY"}, + {RPC_NT_INVALID_NETWORK_OPTIONS, -EIO, + "RPC_NT_INVALID_NETWORK_OPTIONS"}, + {RPC_NT_NO_CALL_ACTIVE, -EIO, "RPC_NT_NO_CALL_ACTIVE"}, + {RPC_NT_CALL_FAILED, -EIO, "RPC_NT_CALL_FAILED"}, + {RPC_NT_CALL_FAILED_DNE, -EIO, "RPC_NT_CALL_FAILED_DNE"}, + {RPC_NT_PROTOCOL_ERROR, -EIO, "RPC_NT_PROTOCOL_ERROR"}, + {RPC_NT_UNSUPPORTED_TRANS_SYN, -EIO, "RPC_NT_UNSUPPORTED_TRANS_SYN"}, + {RPC_NT_UNSUPPORTED_TYPE, -EIO, "RPC_NT_UNSUPPORTED_TYPE"}, + {RPC_NT_INVALID_TAG, -EIO, "RPC_NT_INVALID_TAG"}, + {RPC_NT_INVALID_BOUND, -EIO, "RPC_NT_INVALID_BOUND"}, + {RPC_NT_NO_ENTRY_NAME, -EIO, "RPC_NT_NO_ENTRY_NAME"}, + {RPC_NT_INVALID_NAME_SYNTAX, -EIO, "RPC_NT_INVALID_NAME_SYNTAX"}, + {RPC_NT_UNSUPPORTED_NAME_SYNTAX, -EIO, + "RPC_NT_UNSUPPORTED_NAME_SYNTAX"}, + {RPC_NT_UUID_NO_ADDRESS, -EIO, "RPC_NT_UUID_NO_ADDRESS"}, + {RPC_NT_DUPLICATE_ENDPOINT, -ENOTUNIQ, "RPC_NT_DUPLICATE_ENDPOINT"}, + {RPC_NT_UNKNOWN_AUTHN_TYPE, -EIO, "RPC_NT_UNKNOWN_AUTHN_TYPE"}, + {RPC_NT_MAX_CALLS_TOO_SMALL, -EIO, "RPC_NT_MAX_CALLS_TOO_SMALL"}, + {RPC_NT_STRING_TOO_LONG, -EIO, "RPC_NT_STRING_TOO_LONG"}, + {RPC_NT_PROTSEQ_NOT_FOUND, -EIO, "RPC_NT_PROTSEQ_NOT_FOUND"}, + {RPC_NT_PROCNUM_OUT_OF_RANGE, -EIO, "RPC_NT_PROCNUM_OUT_OF_RANGE"}, + {RPC_NT_BINDING_HAS_NO_AUTH, -EIO, "RPC_NT_BINDING_HAS_NO_AUTH"}, + {RPC_NT_UNKNOWN_AUTHN_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHN_SERVICE"}, + {RPC_NT_UNKNOWN_AUTHN_LEVEL, -EIO, "RPC_NT_UNKNOWN_AUTHN_LEVEL"}, + {RPC_NT_INVALID_AUTH_IDENTITY, -EIO, "RPC_NT_INVALID_AUTH_IDENTITY"}, + {RPC_NT_UNKNOWN_AUTHZ_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHZ_SERVICE"}, + {EPT_NT_INVALID_ENTRY, -EIO, "EPT_NT_INVALID_ENTRY"}, + {EPT_NT_CANT_PERFORM_OP, -EIO, "EPT_NT_CANT_PERFORM_OP"}, + {EPT_NT_NOT_REGISTERED, -EIO, "EPT_NT_NOT_REGISTERED"}, + {RPC_NT_NOTHING_TO_EXPORT, -EIO, "RPC_NT_NOTHING_TO_EXPORT"}, + {RPC_NT_INCOMPLETE_NAME, -EIO, "RPC_NT_INCOMPLETE_NAME"}, + {RPC_NT_INVALID_VERS_OPTION, -EIO, "RPC_NT_INVALID_VERS_OPTION"}, + {RPC_NT_NO_MORE_MEMBERS, -EIO, "RPC_NT_NO_MORE_MEMBERS"}, + {RPC_NT_NOT_ALL_OBJS_UNEXPORTED, -EIO, + "RPC_NT_NOT_ALL_OBJS_UNEXPORTED"}, + {RPC_NT_INTERFACE_NOT_FOUND, -EIO, "RPC_NT_INTERFACE_NOT_FOUND"}, + {RPC_NT_ENTRY_ALREADY_EXISTS, -EIO, "RPC_NT_ENTRY_ALREADY_EXISTS"}, + {RPC_NT_ENTRY_NOT_FOUND, -EIO, "RPC_NT_ENTRY_NOT_FOUND"}, + {RPC_NT_NAME_SERVICE_UNAVAILABLE, -EIO, + "RPC_NT_NAME_SERVICE_UNAVAILABLE"}, + {RPC_NT_INVALID_NAF_ID, -EIO, "RPC_NT_INVALID_NAF_ID"}, + {RPC_NT_CANNOT_SUPPORT, -EOPNOTSUPP, "RPC_NT_CANNOT_SUPPORT"}, + {RPC_NT_NO_CONTEXT_AVAILABLE, -EIO, "RPC_NT_NO_CONTEXT_AVAILABLE"}, + {RPC_NT_INTERNAL_ERROR, -EIO, "RPC_NT_INTERNAL_ERROR"}, + {RPC_NT_ZERO_DIVIDE, -EIO, "RPC_NT_ZERO_DIVIDE"}, + {RPC_NT_ADDRESS_ERROR, -EIO, "RPC_NT_ADDRESS_ERROR"}, + {RPC_NT_FP_DIV_ZERO, -EIO, "RPC_NT_FP_DIV_ZERO"}, + {RPC_NT_FP_UNDERFLOW, -EIO, "RPC_NT_FP_UNDERFLOW"}, + {RPC_NT_FP_OVERFLOW, -EIO, "RPC_NT_FP_OVERFLOW"}, + {RPC_NT_CALL_IN_PROGRESS, -EIO, "RPC_NT_CALL_IN_PROGRESS"}, + {RPC_NT_NO_MORE_BINDINGS, -EIO, "RPC_NT_NO_MORE_BINDINGS"}, + {RPC_NT_GROUP_MEMBER_NOT_FOUND, -EIO, "RPC_NT_GROUP_MEMBER_NOT_FOUND"}, + {EPT_NT_CANT_CREATE, -EIO, "EPT_NT_CANT_CREATE"}, + {RPC_NT_INVALID_OBJECT, -EIO, "RPC_NT_INVALID_OBJECT"}, + {RPC_NT_NO_INTERFACES, -EIO, "RPC_NT_NO_INTERFACES"}, + {RPC_NT_CALL_CANCELLED, -EIO, "RPC_NT_CALL_CANCELLED"}, + {RPC_NT_BINDING_INCOMPLETE, -EIO, "RPC_NT_BINDING_INCOMPLETE"}, + {RPC_NT_COMM_FAILURE, -EIO, "RPC_NT_COMM_FAILURE"}, + {RPC_NT_UNSUPPORTED_AUTHN_LEVEL, -EIO, + "RPC_NT_UNSUPPORTED_AUTHN_LEVEL"}, + {RPC_NT_NO_PRINC_NAME, -EIO, "RPC_NT_NO_PRINC_NAME"}, + {RPC_NT_NOT_RPC_ERROR, -EIO, "RPC_NT_NOT_RPC_ERROR"}, + {RPC_NT_SEC_PKG_ERROR, -EIO, "RPC_NT_SEC_PKG_ERROR"}, + {RPC_NT_NOT_CANCELLED, -EIO, "RPC_NT_NOT_CANCELLED"}, + {RPC_NT_INVALID_ASYNC_HANDLE, -EIO, "RPC_NT_INVALID_ASYNC_HANDLE"}, + {RPC_NT_INVALID_ASYNC_CALL, -EIO, "RPC_NT_INVALID_ASYNC_CALL"}, + {RPC_NT_PROXY_ACCESS_DENIED, -EACCES, "RPC_NT_PROXY_ACCESS_DENIED"}, + {RPC_NT_NO_MORE_ENTRIES, -EIO, "RPC_NT_NO_MORE_ENTRIES"}, + {RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, -EIO, + "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL"}, + {RPC_NT_SS_CHAR_TRANS_SHORT_FILE, -EIO, + "RPC_NT_SS_CHAR_TRANS_SHORT_FILE"}, + {RPC_NT_SS_IN_NULL_CONTEXT, -EIO, "RPC_NT_SS_IN_NULL_CONTEXT"}, + {RPC_NT_SS_CONTEXT_MISMATCH, -EIO, "RPC_NT_SS_CONTEXT_MISMATCH"}, + {RPC_NT_SS_CONTEXT_DAMAGED, -EIO, "RPC_NT_SS_CONTEXT_DAMAGED"}, + {RPC_NT_SS_HANDLES_MISMATCH, -EIO, "RPC_NT_SS_HANDLES_MISMATCH"}, + {RPC_NT_SS_CANNOT_GET_CALL_HANDLE, -EIO, + "RPC_NT_SS_CANNOT_GET_CALL_HANDLE"}, + {RPC_NT_NULL_REF_POINTER, -EIO, "RPC_NT_NULL_REF_POINTER"}, + {RPC_NT_ENUM_VALUE_OUT_OF_RANGE, -EIO, + "RPC_NT_ENUM_VALUE_OUT_OF_RANGE"}, + {RPC_NT_BYTE_COUNT_TOO_SMALL, -EIO, "RPC_NT_BYTE_COUNT_TOO_SMALL"}, + {RPC_NT_BAD_STUB_DATA, -EIO, "RPC_NT_BAD_STUB_DATA"}, + {RPC_NT_INVALID_ES_ACTION, -EIO, "RPC_NT_INVALID_ES_ACTION"}, + {RPC_NT_WRONG_ES_VERSION, -EIO, "RPC_NT_WRONG_ES_VERSION"}, + {RPC_NT_WRONG_STUB_VERSION, -EIO, "RPC_NT_WRONG_STUB_VERSION"}, + {RPC_NT_INVALID_PIPE_OBJECT, -EIO, "RPC_NT_INVALID_PIPE_OBJECT"}, + {RPC_NT_INVALID_PIPE_OPERATION, -EIO, "RPC_NT_INVALID_PIPE_OPERATION"}, + {RPC_NT_WRONG_PIPE_VERSION, -EIO, "RPC_NT_WRONG_PIPE_VERSION"}, + {RPC_NT_PIPE_CLOSED, -EIO, "RPC_NT_PIPE_CLOSED"}, + {RPC_NT_PIPE_DISCIPLINE_ERROR, -EIO, "RPC_NT_PIPE_DISCIPLINE_ERROR"}, + {RPC_NT_PIPE_EMPTY, -EIO, "RPC_NT_PIPE_EMPTY"}, + {STATUS_PNP_BAD_MPS_TABLE, -EIO, "STATUS_PNP_BAD_MPS_TABLE"}, + {STATUS_PNP_TRANSLATION_FAILED, -EIO, "STATUS_PNP_TRANSLATION_FAILED"}, + {STATUS_PNP_IRQ_TRANSLATION_FAILED, -EIO, + "STATUS_PNP_IRQ_TRANSLATION_FAILED"}, + {STATUS_PNP_INVALID_ID, -EIO, "STATUS_PNP_INVALID_ID"}, + {STATUS_IO_REISSUE_AS_CACHED, -EIO, "STATUS_IO_REISSUE_AS_CACHED"}, + {STATUS_CTX_WINSTATION_NAME_INVALID, -EIO, + "STATUS_CTX_WINSTATION_NAME_INVALID"}, + {STATUS_CTX_INVALID_PD, -EIO, "STATUS_CTX_INVALID_PD"}, + {STATUS_CTX_PD_NOT_FOUND, -EIO, "STATUS_CTX_PD_NOT_FOUND"}, + {STATUS_CTX_CLOSE_PENDING, -EIO, "STATUS_CTX_CLOSE_PENDING"}, + {STATUS_CTX_NO_OUTBUF, -EIO, "STATUS_CTX_NO_OUTBUF"}, + {STATUS_CTX_MODEM_INF_NOT_FOUND, -EIO, + "STATUS_CTX_MODEM_INF_NOT_FOUND"}, + {STATUS_CTX_INVALID_MODEMNAME, -EIO, "STATUS_CTX_INVALID_MODEMNAME"}, + {STATUS_CTX_RESPONSE_ERROR, -EIO, "STATUS_CTX_RESPONSE_ERROR"}, + {STATUS_CTX_MODEM_RESPONSE_TIMEOUT, -ETIMEDOUT, + "STATUS_CTX_MODEM_RESPONSE_TIMEOUT"}, + {STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, -EIO, + "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER"}, + {STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, -EIO, + "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE"}, + {STATUS_CTX_MODEM_RESPONSE_BUSY, -EBUSY, + "STATUS_CTX_MODEM_RESPONSE_BUSY"}, + {STATUS_CTX_MODEM_RESPONSE_VOICE, -EIO, + "STATUS_CTX_MODEM_RESPONSE_VOICE"}, + {STATUS_CTX_TD_ERROR, -EIO, "STATUS_CTX_TD_ERROR"}, + {STATUS_CTX_LICENSE_CLIENT_INVALID, -EIO, + "STATUS_CTX_LICENSE_CLIENT_INVALID"}, + {STATUS_CTX_LICENSE_NOT_AVAILABLE, -EIO, + "STATUS_CTX_LICENSE_NOT_AVAILABLE"}, + {STATUS_CTX_LICENSE_EXPIRED, -EIO, "STATUS_CTX_LICENSE_EXPIRED"}, + {STATUS_CTX_WINSTATION_NOT_FOUND, -EIO, + "STATUS_CTX_WINSTATION_NOT_FOUND"}, + {STATUS_CTX_WINSTATION_NAME_COLLISION, -EIO, + "STATUS_CTX_WINSTATION_NAME_COLLISION"}, + {STATUS_CTX_WINSTATION_BUSY, -EBUSY, "STATUS_CTX_WINSTATION_BUSY"}, + {STATUS_CTX_BAD_VIDEO_MODE, -EIO, "STATUS_CTX_BAD_VIDEO_MODE"}, + {STATUS_CTX_GRAPHICS_INVALID, -EIO, "STATUS_CTX_GRAPHICS_INVALID"}, + {STATUS_CTX_NOT_CONSOLE, -EIO, "STATUS_CTX_NOT_CONSOLE"}, + {STATUS_CTX_CLIENT_QUERY_TIMEOUT, -EIO, + "STATUS_CTX_CLIENT_QUERY_TIMEOUT"}, + {STATUS_CTX_CONSOLE_DISCONNECT, -EIO, "STATUS_CTX_CONSOLE_DISCONNECT"}, + {STATUS_CTX_CONSOLE_CONNECT, -EIO, "STATUS_CTX_CONSOLE_CONNECT"}, + {STATUS_CTX_SHADOW_DENIED, -EIO, "STATUS_CTX_SHADOW_DENIED"}, + {STATUS_CTX_WINSTATION_ACCESS_DENIED, -EACCES, + "STATUS_CTX_WINSTATION_ACCESS_DENIED"}, + {STATUS_CTX_INVALID_WD, -EIO, "STATUS_CTX_INVALID_WD"}, + {STATUS_CTX_WD_NOT_FOUND, -EIO, "STATUS_CTX_WD_NOT_FOUND"}, + {STATUS_CTX_SHADOW_INVALID, -EIO, "STATUS_CTX_SHADOW_INVALID"}, + {STATUS_CTX_SHADOW_DISABLED, -EIO, "STATUS_CTX_SHADOW_DISABLED"}, + {STATUS_RDP_PROTOCOL_ERROR, -EIO, "STATUS_RDP_PROTOCOL_ERROR"}, + {STATUS_CTX_CLIENT_LICENSE_NOT_SET, -EIO, + "STATUS_CTX_CLIENT_LICENSE_NOT_SET"}, + {STATUS_CTX_CLIENT_LICENSE_IN_USE, -EIO, + "STATUS_CTX_CLIENT_LICENSE_IN_USE"}, + {STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, -EIO, + "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE"}, + {STATUS_CTX_SHADOW_NOT_RUNNING, -EIO, "STATUS_CTX_SHADOW_NOT_RUNNING"}, + {STATUS_CTX_LOGON_DISABLED, -EIO, "STATUS_CTX_LOGON_DISABLED"}, + {STATUS_CTX_SECURITY_LAYER_ERROR, -EIO, + "STATUS_CTX_SECURITY_LAYER_ERROR"}, + {STATUS_TS_INCOMPATIBLE_SESSIONS, -EIO, + "STATUS_TS_INCOMPATIBLE_SESSIONS"}, + {STATUS_MUI_FILE_NOT_FOUND, -EIO, "STATUS_MUI_FILE_NOT_FOUND"}, + {STATUS_MUI_INVALID_FILE, -EIO, "STATUS_MUI_INVALID_FILE"}, + {STATUS_MUI_INVALID_RC_CONFIG, -EIO, "STATUS_MUI_INVALID_RC_CONFIG"}, + {STATUS_MUI_INVALID_LOCALE_NAME, -EIO, + "STATUS_MUI_INVALID_LOCALE_NAME"}, + {STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, -EIO, + "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME"}, + {STATUS_MUI_FILE_NOT_LOADED, -EIO, "STATUS_MUI_FILE_NOT_LOADED"}, + {STATUS_RESOURCE_ENUM_USER_STOP, -EIO, + "STATUS_RESOURCE_ENUM_USER_STOP"}, + {STATUS_CLUSTER_INVALID_NODE, -EIO, "STATUS_CLUSTER_INVALID_NODE"}, + {STATUS_CLUSTER_NODE_EXISTS, -EIO, "STATUS_CLUSTER_NODE_EXISTS"}, + {STATUS_CLUSTER_JOIN_IN_PROGRESS, -EIO, + "STATUS_CLUSTER_JOIN_IN_PROGRESS"}, + {STATUS_CLUSTER_NODE_NOT_FOUND, -EIO, "STATUS_CLUSTER_NODE_NOT_FOUND"}, + {STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, -EIO, + "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND"}, + {STATUS_CLUSTER_NETWORK_EXISTS, -EIO, "STATUS_CLUSTER_NETWORK_EXISTS"}, + {STATUS_CLUSTER_NETWORK_NOT_FOUND, -EIO, + "STATUS_CLUSTER_NETWORK_NOT_FOUND"}, + {STATUS_CLUSTER_NETINTERFACE_EXISTS, -EIO, + "STATUS_CLUSTER_NETINTERFACE_EXISTS"}, + {STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, -EIO, + "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND"}, + {STATUS_CLUSTER_INVALID_REQUEST, -EIO, + "STATUS_CLUSTER_INVALID_REQUEST"}, + {STATUS_CLUSTER_INVALID_NETWORK_PROVIDER, -EIO, + "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER"}, + {STATUS_CLUSTER_NODE_DOWN, -EIO, "STATUS_CLUSTER_NODE_DOWN"}, + {STATUS_CLUSTER_NODE_UNREACHABLE, -EIO, + "STATUS_CLUSTER_NODE_UNREACHABLE"}, + {STATUS_CLUSTER_NODE_NOT_MEMBER, -EIO, + "STATUS_CLUSTER_NODE_NOT_MEMBER"}, + {STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, -EIO, + "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS"}, + {STATUS_CLUSTER_INVALID_NETWORK, -EIO, + "STATUS_CLUSTER_INVALID_NETWORK"}, + {STATUS_CLUSTER_NO_NET_ADAPTERS, -EIO, + "STATUS_CLUSTER_NO_NET_ADAPTERS"}, + {STATUS_CLUSTER_NODE_UP, -EIO, "STATUS_CLUSTER_NODE_UP"}, + {STATUS_CLUSTER_NODE_PAUSED, -EIO, "STATUS_CLUSTER_NODE_PAUSED"}, + {STATUS_CLUSTER_NODE_NOT_PAUSED, -EIO, + "STATUS_CLUSTER_NODE_NOT_PAUSED"}, + {STATUS_CLUSTER_NO_SECURITY_CONTEXT, -EIO, + "STATUS_CLUSTER_NO_SECURITY_CONTEXT"}, + {STATUS_CLUSTER_NETWORK_NOT_INTERNAL, -EIO, + "STATUS_CLUSTER_NETWORK_NOT_INTERNAL"}, + {STATUS_CLUSTER_POISONED, -EIO, "STATUS_CLUSTER_POISONED"}, + {STATUS_ACPI_INVALID_OPCODE, -EIO, "STATUS_ACPI_INVALID_OPCODE"}, + {STATUS_ACPI_STACK_OVERFLOW, -EIO, "STATUS_ACPI_STACK_OVERFLOW"}, + {STATUS_ACPI_ASSERT_FAILED, -EIO, "STATUS_ACPI_ASSERT_FAILED"}, + {STATUS_ACPI_INVALID_INDEX, -EIO, "STATUS_ACPI_INVALID_INDEX"}, + {STATUS_ACPI_INVALID_ARGUMENT, -EIO, "STATUS_ACPI_INVALID_ARGUMENT"}, + {STATUS_ACPI_FATAL, -EIO, "STATUS_ACPI_FATAL"}, + {STATUS_ACPI_INVALID_SUPERNAME, -EIO, "STATUS_ACPI_INVALID_SUPERNAME"}, + {STATUS_ACPI_INVALID_ARGTYPE, -EIO, "STATUS_ACPI_INVALID_ARGTYPE"}, + {STATUS_ACPI_INVALID_OBJTYPE, -EIO, "STATUS_ACPI_INVALID_OBJTYPE"}, + {STATUS_ACPI_INVALID_TARGETTYPE, -EIO, + "STATUS_ACPI_INVALID_TARGETTYPE"}, + {STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, -EIO, + "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT"}, + {STATUS_ACPI_ADDRESS_NOT_MAPPED, -EIO, + "STATUS_ACPI_ADDRESS_NOT_MAPPED"}, + {STATUS_ACPI_INVALID_EVENTTYPE, -EIO, "STATUS_ACPI_INVALID_EVENTTYPE"}, + {STATUS_ACPI_HANDLER_COLLISION, -EIO, "STATUS_ACPI_HANDLER_COLLISION"}, + {STATUS_ACPI_INVALID_DATA, -EIO, "STATUS_ACPI_INVALID_DATA"}, + {STATUS_ACPI_INVALID_REGION, -EIO, "STATUS_ACPI_INVALID_REGION"}, + {STATUS_ACPI_INVALID_ACCESS_SIZE, -EIO, + "STATUS_ACPI_INVALID_ACCESS_SIZE"}, + {STATUS_ACPI_ACQUIRE_GLOBAL_LOCK, -EIO, + "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK"}, + {STATUS_ACPI_ALREADY_INITIALIZED, -EIO, + "STATUS_ACPI_ALREADY_INITIALIZED"}, + {STATUS_ACPI_NOT_INITIALIZED, -EIO, "STATUS_ACPI_NOT_INITIALIZED"}, + {STATUS_ACPI_INVALID_MUTEX_LEVEL, -EIO, + "STATUS_ACPI_INVALID_MUTEX_LEVEL"}, + {STATUS_ACPI_MUTEX_NOT_OWNED, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNED"}, + {STATUS_ACPI_MUTEX_NOT_OWNER, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNER"}, + {STATUS_ACPI_RS_ACCESS, -EIO, "STATUS_ACPI_RS_ACCESS"}, + {STATUS_ACPI_INVALID_TABLE, -EIO, "STATUS_ACPI_INVALID_TABLE"}, + {STATUS_ACPI_REG_HANDLER_FAILED, -EIO, + "STATUS_ACPI_REG_HANDLER_FAILED"}, + {STATUS_ACPI_POWER_REQUEST_FAILED, -EIO, + "STATUS_ACPI_POWER_REQUEST_FAILED"}, + {STATUS_SXS_SECTION_NOT_FOUND, -EIO, "STATUS_SXS_SECTION_NOT_FOUND"}, + {STATUS_SXS_CANT_GEN_ACTCTX, -EIO, "STATUS_SXS_CANT_GEN_ACTCTX"}, + {STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, -EIO, + "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT"}, + {STATUS_SXS_ASSEMBLY_NOT_FOUND, -EIO, "STATUS_SXS_ASSEMBLY_NOT_FOUND"}, + {STATUS_SXS_MANIFEST_FORMAT_ERROR, -EIO, + "STATUS_SXS_MANIFEST_FORMAT_ERROR"}, + {STATUS_SXS_MANIFEST_PARSE_ERROR, -EIO, + "STATUS_SXS_MANIFEST_PARSE_ERROR"}, + {STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, -EIO, + "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED"}, + {STATUS_SXS_KEY_NOT_FOUND, -EIO, "STATUS_SXS_KEY_NOT_FOUND"}, + {STATUS_SXS_VERSION_CONFLICT, -EIO, "STATUS_SXS_VERSION_CONFLICT"}, + {STATUS_SXS_WRONG_SECTION_TYPE, -EIO, "STATUS_SXS_WRONG_SECTION_TYPE"}, + {STATUS_SXS_THREAD_QUERIES_DISABLED, -EIO, + "STATUS_SXS_THREAD_QUERIES_DISABLED"}, + {STATUS_SXS_ASSEMBLY_MISSING, -EIO, "STATUS_SXS_ASSEMBLY_MISSING"}, + {STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, -EIO, + "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET"}, + {STATUS_SXS_EARLY_DEACTIVATION, -EIO, "STATUS_SXS_EARLY_DEACTIVATION"}, + {STATUS_SXS_INVALID_DEACTIVATION, -EIO, + "STATUS_SXS_INVALID_DEACTIVATION"}, + {STATUS_SXS_MULTIPLE_DEACTIVATION, -EIO, + "STATUS_SXS_MULTIPLE_DEACTIVATION"}, + {STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY, -EIO, + "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY"}, + {STATUS_SXS_PROCESS_TERMINATION_REQUESTED, -EIO, + "STATUS_SXS_PROCESS_TERMINATION_REQUESTED"}, + {STATUS_SXS_CORRUPT_ACTIVATION_STACK, -EIO, + "STATUS_SXS_CORRUPT_ACTIVATION_STACK"}, + {STATUS_SXS_CORRUPTION, -EIO, "STATUS_SXS_CORRUPTION"}, + {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE, -EIO, + "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE"}, + {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME, -EIO, + "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME"}, + {STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE, -EIO, + "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE"}, + {STATUS_SXS_IDENTITY_PARSE_ERROR, -EIO, + "STATUS_SXS_IDENTITY_PARSE_ERROR"}, + {STATUS_SXS_COMPONENT_STORE_CORRUPT, -EIO, + "STATUS_SXS_COMPONENT_STORE_CORRUPT"}, + {STATUS_SXS_FILE_HASH_MISMATCH, -EIO, "STATUS_SXS_FILE_HASH_MISMATCH"}, + {STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT, -EIO, + "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT"}, + {STATUS_SXS_IDENTITIES_DIFFERENT, -EIO, + "STATUS_SXS_IDENTITIES_DIFFERENT"}, + {STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT, -EIO, + "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT"}, + {STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY, -EIO, + "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY"}, + {STATUS_ADVANCED_INSTALLER_FAILED, -EIO, + "STATUS_ADVANCED_INSTALLER_FAILED"}, + {STATUS_XML_ENCODING_MISMATCH, -EIO, "STATUS_XML_ENCODING_MISMATCH"}, + {STATUS_SXS_MANIFEST_TOO_BIG, -EIO, "STATUS_SXS_MANIFEST_TOO_BIG"}, + {STATUS_SXS_SETTING_NOT_REGISTERED, -EIO, + "STATUS_SXS_SETTING_NOT_REGISTERED"}, + {STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE, -EIO, + "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE"}, + {STATUS_SMI_PRIMITIVE_INSTALLER_FAILED, -EIO, + "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED"}, + {STATUS_GENERIC_COMMAND_FAILED, -EIO, "STATUS_GENERIC_COMMAND_FAILED"}, + {STATUS_SXS_FILE_HASH_MISSING, -EIO, "STATUS_SXS_FILE_HASH_MISSING"}, + {STATUS_TRANSACTIONAL_CONFLICT, -EIO, "STATUS_TRANSACTIONAL_CONFLICT"}, + {STATUS_INVALID_TRANSACTION, -EIO, "STATUS_INVALID_TRANSACTION"}, + {STATUS_TRANSACTION_NOT_ACTIVE, -EIO, "STATUS_TRANSACTION_NOT_ACTIVE"}, + {STATUS_TM_INITIALIZATION_FAILED, -EIO, + "STATUS_TM_INITIALIZATION_FAILED"}, + {STATUS_RM_NOT_ACTIVE, -EIO, "STATUS_RM_NOT_ACTIVE"}, + {STATUS_RM_METADATA_CORRUPT, -EIO, "STATUS_RM_METADATA_CORRUPT"}, + {STATUS_TRANSACTION_NOT_JOINED, -EIO, "STATUS_TRANSACTION_NOT_JOINED"}, + {STATUS_DIRECTORY_NOT_RM, -EIO, "STATUS_DIRECTORY_NOT_RM"}, + {STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE, -EIO, + "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE"}, + {STATUS_LOG_RESIZE_INVALID_SIZE, -EIO, + "STATUS_LOG_RESIZE_INVALID_SIZE"}, + {STATUS_REMOTE_FILE_VERSION_MISMATCH, -EIO, + "STATUS_REMOTE_FILE_VERSION_MISMATCH"}, + {STATUS_CRM_PROTOCOL_ALREADY_EXISTS, -EIO, + "STATUS_CRM_PROTOCOL_ALREADY_EXISTS"}, + {STATUS_TRANSACTION_PROPAGATION_FAILED, -EIO, + "STATUS_TRANSACTION_PROPAGATION_FAILED"}, + {STATUS_CRM_PROTOCOL_NOT_FOUND, -EIO, "STATUS_CRM_PROTOCOL_NOT_FOUND"}, + {STATUS_TRANSACTION_SUPERIOR_EXISTS, -EIO, + "STATUS_TRANSACTION_SUPERIOR_EXISTS"}, + {STATUS_TRANSACTION_REQUEST_NOT_VALID, -EIO, + "STATUS_TRANSACTION_REQUEST_NOT_VALID"}, + {STATUS_TRANSACTION_NOT_REQUESTED, -EIO, + "STATUS_TRANSACTION_NOT_REQUESTED"}, + {STATUS_TRANSACTION_ALREADY_ABORTED, -EIO, + "STATUS_TRANSACTION_ALREADY_ABORTED"}, + {STATUS_TRANSACTION_ALREADY_COMMITTED, -EIO, + "STATUS_TRANSACTION_ALREADY_COMMITTED"}, + {STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER, -EIO, + "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER"}, + {STATUS_CURRENT_TRANSACTION_NOT_VALID, -EIO, + "STATUS_CURRENT_TRANSACTION_NOT_VALID"}, + {STATUS_LOG_GROWTH_FAILED, -EIO, "STATUS_LOG_GROWTH_FAILED"}, + {STATUS_OBJECT_NO_LONGER_EXISTS, -EIO, + "STATUS_OBJECT_NO_LONGER_EXISTS"}, + {STATUS_STREAM_MINIVERSION_NOT_FOUND, -EIO, + "STATUS_STREAM_MINIVERSION_NOT_FOUND"}, + {STATUS_STREAM_MINIVERSION_NOT_VALID, -EIO, + "STATUS_STREAM_MINIVERSION_NOT_VALID"}, + {STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION, -EIO, + "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION"}, + {STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT, -EIO, + "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT"}, + {STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS, -EIO, + "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS"}, + {STATUS_HANDLE_NO_LONGER_VALID, -EIO, "STATUS_HANDLE_NO_LONGER_VALID"}, + {STATUS_LOG_CORRUPTION_DETECTED, -EIO, + "STATUS_LOG_CORRUPTION_DETECTED"}, + {STATUS_RM_DISCONNECTED, -EIO, "STATUS_RM_DISCONNECTED"}, + {STATUS_ENLISTMENT_NOT_SUPERIOR, -EIO, + "STATUS_ENLISTMENT_NOT_SUPERIOR"}, + {STATUS_FILE_IDENTITY_NOT_PERSISTENT, -EIO, + "STATUS_FILE_IDENTITY_NOT_PERSISTENT"}, + {STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY, -EIO, + "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY"}, + {STATUS_CANT_CROSS_RM_BOUNDARY, -EIO, "STATUS_CANT_CROSS_RM_BOUNDARY"}, + {STATUS_TXF_DIR_NOT_EMPTY, -EIO, "STATUS_TXF_DIR_NOT_EMPTY"}, + {STATUS_INDOUBT_TRANSACTIONS_EXIST, -EIO, + "STATUS_INDOUBT_TRANSACTIONS_EXIST"}, + {STATUS_TM_VOLATILE, -EIO, "STATUS_TM_VOLATILE"}, + {STATUS_ROLLBACK_TIMER_EXPIRED, -EIO, "STATUS_ROLLBACK_TIMER_EXPIRED"}, + {STATUS_TXF_ATTRIBUTE_CORRUPT, -EIO, "STATUS_TXF_ATTRIBUTE_CORRUPT"}, + {STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION, -EIO, + "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION"}, + {STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED, -EIO, + "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED"}, + {STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE, -EIO, + "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE"}, + {STATUS_TRANSACTION_REQUIRED_PROMOTION, -EIO, + "STATUS_TRANSACTION_REQUIRED_PROMOTION"}, + {STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION, -EIO, + "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION"}, + {STATUS_TRANSACTIONS_NOT_FROZEN, -EIO, + "STATUS_TRANSACTIONS_NOT_FROZEN"}, + {STATUS_TRANSACTION_FREEZE_IN_PROGRESS, -EIO, + "STATUS_TRANSACTION_FREEZE_IN_PROGRESS"}, + {STATUS_NOT_SNAPSHOT_VOLUME, -EIO, "STATUS_NOT_SNAPSHOT_VOLUME"}, + {STATUS_NO_SAVEPOINT_WITH_OPEN_FILES, -EIO, + "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES"}, + {STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION, -EIO, + "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION"}, + {STATUS_TM_IDENTITY_MISMATCH, -EIO, "STATUS_TM_IDENTITY_MISMATCH"}, + {STATUS_FLOATED_SECTION, -EIO, "STATUS_FLOATED_SECTION"}, + {STATUS_CANNOT_ACCEPT_TRANSACTED_WORK, -EIO, + "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK"}, + {STATUS_CANNOT_ABORT_TRANSACTIONS, -EIO, + "STATUS_CANNOT_ABORT_TRANSACTIONS"}, + {STATUS_TRANSACTION_NOT_FOUND, -EIO, "STATUS_TRANSACTION_NOT_FOUND"}, + {STATUS_RESOURCEMANAGER_NOT_FOUND, -EIO, + "STATUS_RESOURCEMANAGER_NOT_FOUND"}, + {STATUS_ENLISTMENT_NOT_FOUND, -EIO, "STATUS_ENLISTMENT_NOT_FOUND"}, + {STATUS_TRANSACTIONMANAGER_NOT_FOUND, -EIO, + "STATUS_TRANSACTIONMANAGER_NOT_FOUND"}, + {STATUS_TRANSACTIONMANAGER_NOT_ONLINE, -EIO, + "STATUS_TRANSACTIONMANAGER_NOT_ONLINE"}, + {STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION, -EIO, + "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION"}, + {STATUS_TRANSACTION_NOT_ROOT, -EIO, "STATUS_TRANSACTION_NOT_ROOT"}, + {STATUS_TRANSACTION_OBJECT_EXPIRED, -EIO, + "STATUS_TRANSACTION_OBJECT_EXPIRED"}, + {STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION, -EIO, + "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION"}, + {STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED, -EIO, + "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED"}, + {STATUS_TRANSACTION_RECORD_TOO_LONG, -EIO, + "STATUS_TRANSACTION_RECORD_TOO_LONG"}, + {STATUS_NO_LINK_TRACKING_IN_TRANSACTION, -EIO, + "STATUS_NO_LINK_TRACKING_IN_TRANSACTION"}, + {STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION, -EOPNOTSUPP, + "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION"}, + {STATUS_TRANSACTION_INTEGRITY_VIOLATED, -EIO, + "STATUS_TRANSACTION_INTEGRITY_VIOLATED"}, + {STATUS_LOG_SECTOR_INVALID, -EIO, "STATUS_LOG_SECTOR_INVALID"}, + {STATUS_LOG_SECTOR_PARITY_INVALID, -EIO, + "STATUS_LOG_SECTOR_PARITY_INVALID"}, + {STATUS_LOG_SECTOR_REMAPPED, -EIO, "STATUS_LOG_SECTOR_REMAPPED"}, + {STATUS_LOG_BLOCK_INCOMPLETE, -EIO, "STATUS_LOG_BLOCK_INCOMPLETE"}, + {STATUS_LOG_INVALID_RANGE, -EIO, "STATUS_LOG_INVALID_RANGE"}, + {STATUS_LOG_BLOCKS_EXHAUSTED, -EIO, "STATUS_LOG_BLOCKS_EXHAUSTED"}, + {STATUS_LOG_READ_CONTEXT_INVALID, -EIO, + "STATUS_LOG_READ_CONTEXT_INVALID"}, + {STATUS_LOG_RESTART_INVALID, -EIO, "STATUS_LOG_RESTART_INVALID"}, + {STATUS_LOG_BLOCK_VERSION, -EIO, "STATUS_LOG_BLOCK_VERSION"}, + {STATUS_LOG_BLOCK_INVALID, -EIO, "STATUS_LOG_BLOCK_INVALID"}, + {STATUS_LOG_READ_MODE_INVALID, -EIO, "STATUS_LOG_READ_MODE_INVALID"}, + {STATUS_LOG_METADATA_CORRUPT, -EIO, "STATUS_LOG_METADATA_CORRUPT"}, + {STATUS_LOG_METADATA_INVALID, -EIO, "STATUS_LOG_METADATA_INVALID"}, + {STATUS_LOG_METADATA_INCONSISTENT, -EIO, + "STATUS_LOG_METADATA_INCONSISTENT"}, + {STATUS_LOG_RESERVATION_INVALID, -EIO, + "STATUS_LOG_RESERVATION_INVALID"}, + {STATUS_LOG_CANT_DELETE, -EIO, "STATUS_LOG_CANT_DELETE"}, + {STATUS_LOG_CONTAINER_LIMIT_EXCEEDED, -EIO, + "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED"}, + {STATUS_LOG_START_OF_LOG, -EIO, "STATUS_LOG_START_OF_LOG"}, + {STATUS_LOG_POLICY_ALREADY_INSTALLED, -EIO, + "STATUS_LOG_POLICY_ALREADY_INSTALLED"}, + {STATUS_LOG_POLICY_NOT_INSTALLED, -EIO, + "STATUS_LOG_POLICY_NOT_INSTALLED"}, + {STATUS_LOG_POLICY_INVALID, -EIO, "STATUS_LOG_POLICY_INVALID"}, + {STATUS_LOG_POLICY_CONFLICT, -EIO, "STATUS_LOG_POLICY_CONFLICT"}, + {STATUS_LOG_PINNED_ARCHIVE_TAIL, -EIO, + "STATUS_LOG_PINNED_ARCHIVE_TAIL"}, + {STATUS_LOG_RECORD_NONEXISTENT, -EIO, "STATUS_LOG_RECORD_NONEXISTENT"}, + {STATUS_LOG_RECORDS_RESERVED_INVALID, -EIO, + "STATUS_LOG_RECORDS_RESERVED_INVALID"}, + {STATUS_LOG_SPACE_RESERVED_INVALID, -EIO, + "STATUS_LOG_SPACE_RESERVED_INVALID"}, + {STATUS_LOG_TAIL_INVALID, -EIO, "STATUS_LOG_TAIL_INVALID"}, + {STATUS_LOG_FULL, -EIO, "STATUS_LOG_FULL"}, + {STATUS_LOG_MULTIPLEXED, -EIO, "STATUS_LOG_MULTIPLEXED"}, + {STATUS_LOG_DEDICATED, -EIO, "STATUS_LOG_DEDICATED"}, + {STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS, -EIO, + "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS"}, + {STATUS_LOG_ARCHIVE_IN_PROGRESS, -EIO, + "STATUS_LOG_ARCHIVE_IN_PROGRESS"}, + {STATUS_LOG_EPHEMERAL, -EIO, "STATUS_LOG_EPHEMERAL"}, + {STATUS_LOG_NOT_ENOUGH_CONTAINERS, -EIO, + "STATUS_LOG_NOT_ENOUGH_CONTAINERS"}, + {STATUS_LOG_CLIENT_ALREADY_REGISTERED, -EIO, + "STATUS_LOG_CLIENT_ALREADY_REGISTERED"}, + {STATUS_LOG_CLIENT_NOT_REGISTERED, -EIO, + "STATUS_LOG_CLIENT_NOT_REGISTERED"}, + {STATUS_LOG_FULL_HANDLER_IN_PROGRESS, -EIO, + "STATUS_LOG_FULL_HANDLER_IN_PROGRESS"}, + {STATUS_LOG_CONTAINER_READ_FAILED, -EIO, + "STATUS_LOG_CONTAINER_READ_FAILED"}, + {STATUS_LOG_CONTAINER_WRITE_FAILED, -EIO, + "STATUS_LOG_CONTAINER_WRITE_FAILED"}, + {STATUS_LOG_CONTAINER_OPEN_FAILED, -EIO, + "STATUS_LOG_CONTAINER_OPEN_FAILED"}, + {STATUS_LOG_CONTAINER_STATE_INVALID, -EIO, + "STATUS_LOG_CONTAINER_STATE_INVALID"}, + {STATUS_LOG_STATE_INVALID, -EIO, "STATUS_LOG_STATE_INVALID"}, + {STATUS_LOG_PINNED, -EIO, "STATUS_LOG_PINNED"}, + {STATUS_LOG_METADATA_FLUSH_FAILED, -EIO, + "STATUS_LOG_METADATA_FLUSH_FAILED"}, + {STATUS_LOG_INCONSISTENT_SECURITY, -EIO, + "STATUS_LOG_INCONSISTENT_SECURITY"}, + {STATUS_LOG_APPENDED_FLUSH_FAILED, -EIO, + "STATUS_LOG_APPENDED_FLUSH_FAILED"}, + {STATUS_LOG_PINNED_RESERVATION, -EIO, "STATUS_LOG_PINNED_RESERVATION"}, + {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD, -EIO, + "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD"}, + {STATUS_FLT_NO_HANDLER_DEFINED, -EIO, "STATUS_FLT_NO_HANDLER_DEFINED"}, + {STATUS_FLT_CONTEXT_ALREADY_DEFINED, -EIO, + "STATUS_FLT_CONTEXT_ALREADY_DEFINED"}, + {STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST, -EIO, + "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST"}, + {STATUS_FLT_DISALLOW_FAST_IO, -EIO, "STATUS_FLT_DISALLOW_FAST_IO"}, + {STATUS_FLT_INVALID_NAME_REQUEST, -EIO, + "STATUS_FLT_INVALID_NAME_REQUEST"}, + {STATUS_FLT_NOT_SAFE_TO_POST_OPERATION, -EIO, + "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION"}, + {STATUS_FLT_NOT_INITIALIZED, -EIO, "STATUS_FLT_NOT_INITIALIZED"}, + {STATUS_FLT_FILTER_NOT_READY, -EIO, "STATUS_FLT_FILTER_NOT_READY"}, + {STATUS_FLT_POST_OPERATION_CLEANUP, -EIO, + "STATUS_FLT_POST_OPERATION_CLEANUP"}, + {STATUS_FLT_INTERNAL_ERROR, -EIO, "STATUS_FLT_INTERNAL_ERROR"}, + {STATUS_FLT_DELETING_OBJECT, -EIO, "STATUS_FLT_DELETING_OBJECT"}, + {STATUS_FLT_MUST_BE_NONPAGED_POOL, -EIO, + "STATUS_FLT_MUST_BE_NONPAGED_POOL"}, + {STATUS_FLT_DUPLICATE_ENTRY, -EIO, "STATUS_FLT_DUPLICATE_ENTRY"}, + {STATUS_FLT_CBDQ_DISABLED, -EIO, "STATUS_FLT_CBDQ_DISABLED"}, + {STATUS_FLT_DO_NOT_ATTACH, -EIO, "STATUS_FLT_DO_NOT_ATTACH"}, + {STATUS_FLT_DO_NOT_DETACH, -EIO, "STATUS_FLT_DO_NOT_DETACH"}, + {STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, -EIO, + "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION"}, + {STATUS_FLT_INSTANCE_NAME_COLLISION, -EIO, + "STATUS_FLT_INSTANCE_NAME_COLLISION"}, + {STATUS_FLT_FILTER_NOT_FOUND, -EIO, "STATUS_FLT_FILTER_NOT_FOUND"}, + {STATUS_FLT_VOLUME_NOT_FOUND, -EIO, "STATUS_FLT_VOLUME_NOT_FOUND"}, + {STATUS_FLT_INSTANCE_NOT_FOUND, -EIO, "STATUS_FLT_INSTANCE_NOT_FOUND"}, + {STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND, -EIO, + "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND"}, + {STATUS_FLT_INVALID_CONTEXT_REGISTRATION, -EIO, + "STATUS_FLT_INVALID_CONTEXT_REGISTRATION"}, + {STATUS_FLT_NAME_CACHE_MISS, -EIO, "STATUS_FLT_NAME_CACHE_MISS"}, + {STATUS_FLT_NO_DEVICE_OBJECT, -EIO, "STATUS_FLT_NO_DEVICE_OBJECT"}, + {STATUS_FLT_VOLUME_ALREADY_MOUNTED, -EIO, + "STATUS_FLT_VOLUME_ALREADY_MOUNTED"}, + {STATUS_FLT_ALREADY_ENLISTED, -EIO, "STATUS_FLT_ALREADY_ENLISTED"}, + {STATUS_FLT_CONTEXT_ALREADY_LINKED, -EIO, + "STATUS_FLT_CONTEXT_ALREADY_LINKED"}, + {STATUS_FLT_NO_WAITER_FOR_REPLY, -EIO, + "STATUS_FLT_NO_WAITER_FOR_REPLY"}, + {STATUS_MONITOR_NO_DESCRIPTOR, -EIO, "STATUS_MONITOR_NO_DESCRIPTOR"}, + {STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT, -EIO, + "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT"}, + {STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM, -EIO, + "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM"}, + {STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK"}, + {STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED, -EIO, + "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED"}, + {STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK"}, + {STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK"}, + {STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA, -EIO, + "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA"}, + {STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK"}, + {STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER, -EIO, + "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER"}, + {STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, -EIO, + "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER"}, + {STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER, -EIO, + "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER"}, + {STATUS_GRAPHICS_ADAPTER_WAS_RESET, -EIO, + "STATUS_GRAPHICS_ADAPTER_WAS_RESET"}, + {STATUS_GRAPHICS_INVALID_DRIVER_MODEL, -EIO, + "STATUS_GRAPHICS_INVALID_DRIVER_MODEL"}, + {STATUS_GRAPHICS_PRESENT_MODE_CHANGED, -EIO, + "STATUS_GRAPHICS_PRESENT_MODE_CHANGED"}, + {STATUS_GRAPHICS_PRESENT_OCCLUDED, -EIO, + "STATUS_GRAPHICS_PRESENT_OCCLUDED"}, + {STATUS_GRAPHICS_PRESENT_DENIED, -EIO, + "STATUS_GRAPHICS_PRESENT_DENIED"}, + {STATUS_GRAPHICS_CANNOTCOLORCONVERT, -EIO, + "STATUS_GRAPHICS_CANNOTCOLORCONVERT"}, + {STATUS_GRAPHICS_NO_VIDEO_MEMORY, -EIO, + "STATUS_GRAPHICS_NO_VIDEO_MEMORY"}, + {STATUS_GRAPHICS_CANT_LOCK_MEMORY, -EIO, + "STATUS_GRAPHICS_CANT_LOCK_MEMORY"}, + {STATUS_GRAPHICS_ALLOCATION_BUSY, -EBUSY, + "STATUS_GRAPHICS_ALLOCATION_BUSY"}, + {STATUS_GRAPHICS_TOO_MANY_REFERENCES, -EIO, + "STATUS_GRAPHICS_TOO_MANY_REFERENCES"}, + {STATUS_GRAPHICS_TRY_AGAIN_LATER, -EIO, + "STATUS_GRAPHICS_TRY_AGAIN_LATER"}, + {STATUS_GRAPHICS_TRY_AGAIN_NOW, -EIO, "STATUS_GRAPHICS_TRY_AGAIN_NOW"}, + {STATUS_GRAPHICS_ALLOCATION_INVALID, -EIO, + "STATUS_GRAPHICS_ALLOCATION_INVALID"}, + {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE, -EIO, + "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE"}, + {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED, -EIO, + "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED"}, + {STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION, -EIO, + "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION"}, + {STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE, -EIO, + "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE"}, + {STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION, -EIO, + "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION"}, + {STATUS_GRAPHICS_ALLOCATION_CLOSED, -EIO, + "STATUS_GRAPHICS_ALLOCATION_CLOSED"}, + {STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE, -EIO, + "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE"}, + {STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE, -EIO, + "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE"}, + {STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE, -EIO, + "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE"}, + {STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST, -EIO, + "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST"}, + {STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE, -EIO, + "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY"}, + {STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_VIDPN, -EIO, "STATUS_GRAPHICS_INVALID_VIDPN"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET"}, + {STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET"}, + {STATUS_GRAPHICS_INVALID_FREQUENCY, -EIO, + "STATUS_GRAPHICS_INVALID_FREQUENCY"}, + {STATUS_GRAPHICS_INVALID_ACTIVE_REGION, -EIO, + "STATUS_GRAPHICS_INVALID_ACTIVE_REGION"}, + {STATUS_GRAPHICS_INVALID_TOTAL_REGION, -EIO, + "STATUS_GRAPHICS_INVALID_TOTAL_REGION"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE"}, + {STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET, -EIO, + "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET"}, + {STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET, -EIO, + "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET"}, + {STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET"}, + {STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET"}, + {STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_TARGET_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH"}, + {STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY"}, + {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET"}, + {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE"}, + {STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET, -EIO, + "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET"}, + {STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_STALE_MODESET, -EIO, "STATUS_GRAPHICS_STALE_MODESET"}, + {STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET"}, + {STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE"}, + {STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN, -EIO, + "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN"}, + {STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION, -EIO, + "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION"}, + {STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES, -EIO, + "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES"}, + {STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE, -EIO, + "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE"}, + {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET, -EIO, + "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET"}, + {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET, -EIO, + "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET"}, + {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR, -EIO, + "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR"}, + {STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET, -EIO, + "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET"}, + {STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE"}, + {STATUS_GRAPHICS_RESOURCES_NOT_RELATED, -EIO, + "STATUS_GRAPHICS_RESOURCES_NOT_RELATED"}, + {STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET, -EIO, + "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET"}, + {STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER, -EIO, + "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER"}, + {STATUS_GRAPHICS_NO_VIDPNMGR, -EIO, "STATUS_GRAPHICS_NO_VIDPNMGR"}, + {STATUS_GRAPHICS_NO_ACTIVE_VIDPN, -EIO, + "STATUS_GRAPHICS_NO_ACTIVE_VIDPN"}, + {STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY"}, + {STATUS_GRAPHICS_MONITOR_NOT_CONNECTED, -EIO, + "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED"}, + {STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE, -EIO, + "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE"}, + {STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE, -EIO, + "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE"}, + {STATUS_GRAPHICS_INVALID_STRIDE, -EIO, + "STATUS_GRAPHICS_INVALID_STRIDE"}, + {STATUS_GRAPHICS_INVALID_PIXELFORMAT, -EIO, + "STATUS_GRAPHICS_INVALID_PIXELFORMAT"}, + {STATUS_GRAPHICS_INVALID_COLORBASIS, -EIO, + "STATUS_GRAPHICS_INVALID_COLORBASIS"}, + {STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE, -EIO, + "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE"}, + {STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT, -EIO, + "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT"}, + {STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE, -EIO, + "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE"}, + {STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN, -EIO, + "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN"}, + {STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL, -EIO, + "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL"}, + {STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION, -EIO, + "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION"}, + {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED, + -EIO, + "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_GAMMA_RAMP, -EIO, + "STATUS_GRAPHICS_INVALID_GAMMA_RAMP"}, + {STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_MODE_NOT_IN_MODESET, -EIO, + "STATUS_GRAPHICS_MODE_NOT_IN_MODESET"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON"}, + {STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE"}, + {STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE"}, + {STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS, -EIO, + "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS"}, + {STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING, -EIO, + "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING"}, + {STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED, -EIO, + "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED"}, + {STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS, -EIO, + "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS"}, + {STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT, -EIO, + "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT"}, + {STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM, -EIO, + "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM"}, + {STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN"}, + {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT"}, + {STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED, -EIO, + "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED"}, + {STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION, -EIO, + "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION"}, + {STATUS_GRAPHICS_INVALID_CLIENT_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_CLIENT_TYPE"}, + {STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET, -EIO, + "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET"}, + {STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED, -EIO, + "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED"}, + {STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER, -EIO, + "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER"}, + {STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED, -EIO, + "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED"}, + {STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED, -EIO, + "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED"}, + {STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY, -EIO, + "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY"}, + {STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED, -EIO, + "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED"}, + {STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON, -EIO, + "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON"}, + {STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE, -EIO, + "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE"}, + {STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER, -EIO, + "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER"}, + {STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED, -EIO, + "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED"}, + {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS, + -EIO, + "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS"}, + {STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST"}, + {STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS, -EIO, + "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS"}, + {STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST"}, + {STATUS_GRAPHICS_OPM_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_OPM_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_COPP_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_COPP_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_UAB_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_UAB_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS"}, + {STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL, -EIO, + "STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL"}, + {STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST, -EIO, + "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST"}, + {STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, + "STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, + {STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, + "STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, + {STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_OPM_INVALID_POINTER, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_POINTER"}, + {STATUS_GRAPHICS_OPM_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_OPM_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_OPM_INVALID_HANDLE, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_HANDLE"}, + {STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, + "STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, + {STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH, -EIO, + "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH"}, + {STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED, -EIO, + "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED"}, + {STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED, -EIO, + "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED"}, + {STATUS_GRAPHICS_PVP_HFS_FAILED, -EIO, + "STATUS_GRAPHICS_PVP_HFS_FAILED"}, + {STATUS_GRAPHICS_OPM_INVALID_SRM, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_SRM"}, + {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP, -EIO, + "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP"}, + {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP, -EIO, + "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP"}, + {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA, -EIO, + "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA"}, + {STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET, -EIO, + "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET"}, + {STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH, -EIO, + "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH"}, + {STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE, -EIO, + "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE"}, + {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS, -EIO, + "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS"}, + {STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, + "STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS"}, + {STATUS_GRAPHICS_I2C_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_I2C_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST, -EIO, + "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST"}, + {STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA, -EIO, + "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA"}, + {STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA, -EIO, + "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA"}, + {STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_DDCCI_INVALID_DATA, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_DATA"}, + {STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE, + -EIO, + "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE"}, + {STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING"}, + {STATUS_GRAPHICS_MCA_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_MCA_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND"}, + {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH"}, + {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM"}, + {STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE, -EIO, + "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE"}, + {STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS, -EIO, + "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS"}, + {STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED, -EIO, + "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED"}, + {STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, + "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, + {STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, + "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, + {STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_POINTER, -EIO, + "STATUS_GRAPHICS_INVALID_POINTER"}, + {STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, + "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, + {STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL, -EIO, + "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL"}, + {STATUS_GRAPHICS_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, + "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS"}, + {STATUS_FVE_LOCKED_VOLUME, -EIO, "STATUS_FVE_LOCKED_VOLUME"}, + {STATUS_FVE_NOT_ENCRYPTED, -EIO, "STATUS_FVE_NOT_ENCRYPTED"}, + {STATUS_FVE_BAD_INFORMATION, -EIO, "STATUS_FVE_BAD_INFORMATION"}, + {STATUS_FVE_TOO_SMALL, -EIO, "STATUS_FVE_TOO_SMALL"}, + {STATUS_FVE_FAILED_WRONG_FS, -EIO, "STATUS_FVE_FAILED_WRONG_FS"}, + {STATUS_FVE_FAILED_BAD_FS, -EIO, "STATUS_FVE_FAILED_BAD_FS"}, + {STATUS_FVE_FS_NOT_EXTENDED, -EIO, "STATUS_FVE_FS_NOT_EXTENDED"}, + {STATUS_FVE_FS_MOUNTED, -EIO, "STATUS_FVE_FS_MOUNTED"}, + {STATUS_FVE_NO_LICENSE, -EIO, "STATUS_FVE_NO_LICENSE"}, + {STATUS_FVE_ACTION_NOT_ALLOWED, -EIO, "STATUS_FVE_ACTION_NOT_ALLOWED"}, + {STATUS_FVE_BAD_DATA, -EIO, "STATUS_FVE_BAD_DATA"}, + {STATUS_FVE_VOLUME_NOT_BOUND, -EIO, "STATUS_FVE_VOLUME_NOT_BOUND"}, + {STATUS_FVE_NOT_DATA_VOLUME, -EIO, "STATUS_FVE_NOT_DATA_VOLUME"}, + {STATUS_FVE_CONV_READ_ERROR, -EIO, "STATUS_FVE_CONV_READ_ERROR"}, + {STATUS_FVE_CONV_WRITE_ERROR, -EIO, "STATUS_FVE_CONV_WRITE_ERROR"}, + {STATUS_FVE_OVERLAPPED_UPDATE, -EIO, "STATUS_FVE_OVERLAPPED_UPDATE"}, + {STATUS_FVE_FAILED_SECTOR_SIZE, -EIO, "STATUS_FVE_FAILED_SECTOR_SIZE"}, + {STATUS_FVE_FAILED_AUTHENTICATION, -EIO, + "STATUS_FVE_FAILED_AUTHENTICATION"}, + {STATUS_FVE_NOT_OS_VOLUME, -EIO, "STATUS_FVE_NOT_OS_VOLUME"}, + {STATUS_FVE_KEYFILE_NOT_FOUND, -EIO, "STATUS_FVE_KEYFILE_NOT_FOUND"}, + {STATUS_FVE_KEYFILE_INVALID, -EIO, "STATUS_FVE_KEYFILE_INVALID"}, + {STATUS_FVE_KEYFILE_NO_VMK, -EIO, "STATUS_FVE_KEYFILE_NO_VMK"}, + {STATUS_FVE_TPM_DISABLED, -EIO, "STATUS_FVE_TPM_DISABLED"}, + {STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO, -EIO, + "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO"}, + {STATUS_FVE_TPM_INVALID_PCR, -EIO, "STATUS_FVE_TPM_INVALID_PCR"}, + {STATUS_FVE_TPM_NO_VMK, -EIO, "STATUS_FVE_TPM_NO_VMK"}, + {STATUS_FVE_PIN_INVALID, -EIO, "STATUS_FVE_PIN_INVALID"}, + {STATUS_FVE_AUTH_INVALID_APPLICATION, -EIO, + "STATUS_FVE_AUTH_INVALID_APPLICATION"}, + {STATUS_FVE_AUTH_INVALID_CONFIG, -EIO, + "STATUS_FVE_AUTH_INVALID_CONFIG"}, + {STATUS_FVE_DEBUGGER_ENABLED, -EIO, "STATUS_FVE_DEBUGGER_ENABLED"}, + {STATUS_FVE_DRY_RUN_FAILED, -EIO, "STATUS_FVE_DRY_RUN_FAILED"}, + {STATUS_FVE_BAD_METADATA_POINTER, -EIO, + "STATUS_FVE_BAD_METADATA_POINTER"}, + {STATUS_FVE_OLD_METADATA_COPY, -EIO, "STATUS_FVE_OLD_METADATA_COPY"}, + {STATUS_FVE_REBOOT_REQUIRED, -EIO, "STATUS_FVE_REBOOT_REQUIRED"}, + {STATUS_FVE_RAW_ACCESS, -EIO, "STATUS_FVE_RAW_ACCESS"}, + {STATUS_FVE_RAW_BLOCKED, -EIO, "STATUS_FVE_RAW_BLOCKED"}, + {STATUS_FWP_CALLOUT_NOT_FOUND, -EIO, "STATUS_FWP_CALLOUT_NOT_FOUND"}, + {STATUS_FWP_CONDITION_NOT_FOUND, -EIO, + "STATUS_FWP_CONDITION_NOT_FOUND"}, + {STATUS_FWP_FILTER_NOT_FOUND, -EIO, "STATUS_FWP_FILTER_NOT_FOUND"}, + {STATUS_FWP_LAYER_NOT_FOUND, -EIO, "STATUS_FWP_LAYER_NOT_FOUND"}, + {STATUS_FWP_PROVIDER_NOT_FOUND, -EIO, "STATUS_FWP_PROVIDER_NOT_FOUND"}, + {STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND, -EIO, + "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND"}, + {STATUS_FWP_SUBLAYER_NOT_FOUND, -EIO, "STATUS_FWP_SUBLAYER_NOT_FOUND"}, + {STATUS_FWP_NOT_FOUND, -EIO, "STATUS_FWP_NOT_FOUND"}, + {STATUS_FWP_ALREADY_EXISTS, -EIO, "STATUS_FWP_ALREADY_EXISTS"}, + {STATUS_FWP_IN_USE, -EIO, "STATUS_FWP_IN_USE"}, + {STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS, -EIO, + "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS"}, + {STATUS_FWP_WRONG_SESSION, -EIO, "STATUS_FWP_WRONG_SESSION"}, + {STATUS_FWP_NO_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_NO_TXN_IN_PROGRESS"}, + {STATUS_FWP_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_TXN_IN_PROGRESS"}, + {STATUS_FWP_TXN_ABORTED, -EIO, "STATUS_FWP_TXN_ABORTED"}, + {STATUS_FWP_SESSION_ABORTED, -EIO, "STATUS_FWP_SESSION_ABORTED"}, + {STATUS_FWP_INCOMPATIBLE_TXN, -EIO, "STATUS_FWP_INCOMPATIBLE_TXN"}, + {STATUS_FWP_TIMEOUT, -ETIMEDOUT, "STATUS_FWP_TIMEOUT"}, + {STATUS_FWP_NET_EVENTS_DISABLED, -EIO, + "STATUS_FWP_NET_EVENTS_DISABLED"}, + {STATUS_FWP_INCOMPATIBLE_LAYER, -EIO, "STATUS_FWP_INCOMPATIBLE_LAYER"}, + {STATUS_FWP_KM_CLIENTS_ONLY, -EIO, "STATUS_FWP_KM_CLIENTS_ONLY"}, + {STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"}, + {STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"}, + {STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO, + "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS"}, + {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, "STATUS_FWP_TOO_MANY_CALLOUTS"}, + {STATUS_FWP_NOTIFICATION_DROPPED, -EIO, + "STATUS_FWP_NOTIFICATION_DROPPED"}, + {STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"}, + {STATUS_FWP_INCOMPATIBLE_SA_STATE, -EIO, + "STATUS_FWP_INCOMPATIBLE_SA_STATE"}, + {STATUS_FWP_NULL_POINTER, -EIO, "STATUS_FWP_NULL_POINTER"}, + {STATUS_FWP_INVALID_ENUMERATOR, -EIO, "STATUS_FWP_INVALID_ENUMERATOR"}, + {STATUS_FWP_INVALID_FLAGS, -EIO, "STATUS_FWP_INVALID_FLAGS"}, + {STATUS_FWP_INVALID_NET_MASK, -EIO, "STATUS_FWP_INVALID_NET_MASK"}, + {STATUS_FWP_INVALID_RANGE, -EIO, "STATUS_FWP_INVALID_RANGE"}, + {STATUS_FWP_INVALID_INTERVAL, -EIO, "STATUS_FWP_INVALID_INTERVAL"}, + {STATUS_FWP_ZERO_LENGTH_ARRAY, -EIO, "STATUS_FWP_ZERO_LENGTH_ARRAY"}, + {STATUS_FWP_NULL_DISPLAY_NAME, -EIO, "STATUS_FWP_NULL_DISPLAY_NAME"}, + {STATUS_FWP_INVALID_ACTION_TYPE, -EIO, + "STATUS_FWP_INVALID_ACTION_TYPE"}, + {STATUS_FWP_INVALID_WEIGHT, -EIO, "STATUS_FWP_INVALID_WEIGHT"}, + {STATUS_FWP_MATCH_TYPE_MISMATCH, -EIO, + "STATUS_FWP_MATCH_TYPE_MISMATCH"}, + {STATUS_FWP_TYPE_MISMATCH, -EIO, "STATUS_FWP_TYPE_MISMATCH"}, + {STATUS_FWP_OUT_OF_BOUNDS, -EIO, "STATUS_FWP_OUT_OF_BOUNDS"}, + {STATUS_FWP_RESERVED, -EIO, "STATUS_FWP_RESERVED"}, + {STATUS_FWP_DUPLICATE_CONDITION, -EIO, + "STATUS_FWP_DUPLICATE_CONDITION"}, + {STATUS_FWP_DUPLICATE_KEYMOD, -EIO, "STATUS_FWP_DUPLICATE_KEYMOD"}, + {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER, -EIO, + "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER"}, + {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER, -EIO, + "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER"}, + {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER, -EIO, + "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER"}, + {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT, -EIO, + "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT"}, + {STATUS_FWP_INCOMPATIBLE_AUTH_METHOD, -EIO, + "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD"}, + {STATUS_FWP_INCOMPATIBLE_DH_GROUP, -EIO, + "STATUS_FWP_INCOMPATIBLE_DH_GROUP"}, + {STATUS_FWP_EM_NOT_SUPPORTED, -EOPNOTSUPP, + "STATUS_FWP_EM_NOT_SUPPORTED"}, + {STATUS_FWP_NEVER_MATCH, -EIO, "STATUS_FWP_NEVER_MATCH"}, + {STATUS_FWP_PROVIDER_CONTEXT_MISMATCH, -EIO, + "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH"}, + {STATUS_FWP_INVALID_PARAMETER, -EIO, "STATUS_FWP_INVALID_PARAMETER"}, + {STATUS_FWP_TOO_MANY_SUBLAYERS, -EIO, "STATUS_FWP_TOO_MANY_SUBLAYERS"}, + {STATUS_FWP_CALLOUT_NOTIFICATION_FAILED, -EIO, + "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED"}, + {STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG, -EIO, + "STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG"}, + {STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG, -EIO, + "STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG"}, + {STATUS_FWP_TCPIP_NOT_READY, -EIO, "STATUS_FWP_TCPIP_NOT_READY"}, + {STATUS_FWP_INJECT_HANDLE_CLOSING, -EIO, + "STATUS_FWP_INJECT_HANDLE_CLOSING"}, + {STATUS_FWP_INJECT_HANDLE_STALE, -EIO, + "STATUS_FWP_INJECT_HANDLE_STALE"}, + {STATUS_FWP_CANNOT_PEND, -EIO, "STATUS_FWP_CANNOT_PEND"}, + {STATUS_NDIS_CLOSING, -EIO, "STATUS_NDIS_CLOSING"}, + {STATUS_NDIS_BAD_VERSION, -EIO, "STATUS_NDIS_BAD_VERSION"}, + {STATUS_NDIS_BAD_CHARACTERISTICS, -EIO, + "STATUS_NDIS_BAD_CHARACTERISTICS"}, + {STATUS_NDIS_ADAPTER_NOT_FOUND, -EIO, "STATUS_NDIS_ADAPTER_NOT_FOUND"}, + {STATUS_NDIS_OPEN_FAILED, -EIO, "STATUS_NDIS_OPEN_FAILED"}, + {STATUS_NDIS_DEVICE_FAILED, -EIO, "STATUS_NDIS_DEVICE_FAILED"}, + {STATUS_NDIS_MULTICAST_FULL, -EIO, "STATUS_NDIS_MULTICAST_FULL"}, + {STATUS_NDIS_MULTICAST_EXISTS, -EIO, "STATUS_NDIS_MULTICAST_EXISTS"}, + {STATUS_NDIS_MULTICAST_NOT_FOUND, -EIO, + "STATUS_NDIS_MULTICAST_NOT_FOUND"}, + {STATUS_NDIS_REQUEST_ABORTED, -EIO, "STATUS_NDIS_REQUEST_ABORTED"}, + {STATUS_NDIS_RESET_IN_PROGRESS, -EIO, "STATUS_NDIS_RESET_IN_PROGRESS"}, + {STATUS_NDIS_INVALID_PACKET, -EIO, "STATUS_NDIS_INVALID_PACKET"}, + {STATUS_NDIS_INVALID_DEVICE_REQUEST, -EIO, + "STATUS_NDIS_INVALID_DEVICE_REQUEST"}, + {STATUS_NDIS_ADAPTER_NOT_READY, -EIO, "STATUS_NDIS_ADAPTER_NOT_READY"}, + {STATUS_NDIS_INVALID_LENGTH, -EIO, "STATUS_NDIS_INVALID_LENGTH"}, + {STATUS_NDIS_INVALID_DATA, -EIO, "STATUS_NDIS_INVALID_DATA"}, + {STATUS_NDIS_BUFFER_TOO_SHORT, -ENOBUFS, + "STATUS_NDIS_BUFFER_TOO_SHORT"}, + {STATUS_NDIS_INVALID_OID, -EIO, "STATUS_NDIS_INVALID_OID"}, + {STATUS_NDIS_ADAPTER_REMOVED, -EIO, "STATUS_NDIS_ADAPTER_REMOVED"}, + {STATUS_NDIS_UNSUPPORTED_MEDIA, -EIO, "STATUS_NDIS_UNSUPPORTED_MEDIA"}, + {STATUS_NDIS_GROUP_ADDRESS_IN_USE, -EIO, + "STATUS_NDIS_GROUP_ADDRESS_IN_USE"}, + {STATUS_NDIS_FILE_NOT_FOUND, -EIO, "STATUS_NDIS_FILE_NOT_FOUND"}, + {STATUS_NDIS_ERROR_READING_FILE, -EIO, + "STATUS_NDIS_ERROR_READING_FILE"}, + {STATUS_NDIS_ALREADY_MAPPED, -EIO, "STATUS_NDIS_ALREADY_MAPPED"}, + {STATUS_NDIS_RESOURCE_CONFLICT, -EIO, "STATUS_NDIS_RESOURCE_CONFLICT"}, + {STATUS_NDIS_MEDIA_DISCONNECTED, -EIO, + "STATUS_NDIS_MEDIA_DISCONNECTED"}, + {STATUS_NDIS_INVALID_ADDRESS, -EIO, "STATUS_NDIS_INVALID_ADDRESS"}, + {STATUS_NDIS_PAUSED, -EIO, "STATUS_NDIS_PAUSED"}, + {STATUS_NDIS_INTERFACE_NOT_FOUND, -EIO, + "STATUS_NDIS_INTERFACE_NOT_FOUND"}, + {STATUS_NDIS_UNSUPPORTED_REVISION, -EIO, + "STATUS_NDIS_UNSUPPORTED_REVISION"}, + {STATUS_NDIS_INVALID_PORT, -EIO, "STATUS_NDIS_INVALID_PORT"}, + {STATUS_NDIS_INVALID_PORT_STATE, -EIO, + "STATUS_NDIS_INVALID_PORT_STATE"}, + {STATUS_NDIS_LOW_POWER_STATE, -EIO, "STATUS_NDIS_LOW_POWER_STATE"}, + {STATUS_NDIS_NOT_SUPPORTED, -ENOSYS, "STATUS_NDIS_NOT_SUPPORTED"}, + {STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED, -EIO, + "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED"}, + {STATUS_NDIS_DOT11_MEDIA_IN_USE, -EIO, + "STATUS_NDIS_DOT11_MEDIA_IN_USE"}, + {STATUS_NDIS_DOT11_POWER_STATE_INVALID, -EIO, + "STATUS_NDIS_DOT11_POWER_STATE_INVALID"}, + {STATUS_IPSEC_BAD_SPI, -EIO, "STATUS_IPSEC_BAD_SPI"}, + {STATUS_IPSEC_SA_LIFETIME_EXPIRED, -EIO, + "STATUS_IPSEC_SA_LIFETIME_EXPIRED"}, + {STATUS_IPSEC_WRONG_SA, -EIO, "STATUS_IPSEC_WRONG_SA"}, + {STATUS_IPSEC_REPLAY_CHECK_FAILED, -EIO, + "STATUS_IPSEC_REPLAY_CHECK_FAILED"}, + {STATUS_IPSEC_INVALID_PACKET, -EIO, "STATUS_IPSEC_INVALID_PACKET"}, + {STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO, + "STATUS_IPSEC_INTEGRITY_CHECK_FAILED"}, + {STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"}, + {0, 0, NULL} +}; + +/***************************************************************************** + Print an error message from the status code + *****************************************************************************/ +static void +smb2_print_status(__le32 status) +{ + int idx = 0; + + while (smb2_error_map_table[idx].status_string != NULL) { + if ((smb2_error_map_table[idx].smb2_status) == status) { + pr_notice("Status code returned 0x%08x %s\n", status, + smb2_error_map_table[idx].status_string); + } + idx++; + } + return; +} + +int +map_smb2_to_linux_error(char *buf, bool log_err) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + unsigned int i; + int rc = -EIO; + __le32 smb2err = shdr->Status; + + if (smb2err == 0) { + trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), + le64_to_cpu(shdr->MessageId)); + return 0; + } + + /* mask facility */ + if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) && + (smb2err != STATUS_END_OF_FILE)) + smb2_print_status(smb2err); + else if (cifsFYI & CIFS_RC) + smb2_print_status(smb2err); + + for (i = 0; i < sizeof(smb2_error_map_table) / + sizeof(struct status_to_posix_error); i++) { + if (smb2_error_map_table[i].smb2_status == smb2err) { + rc = smb2_error_map_table[i].posix_error; + break; + } + } + + /* on error mapping not found - return EIO */ + + cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n", + __le32_to_cpu(smb2err), rc); + + trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), + le64_to_cpu(shdr->MessageId), + le32_to_cpu(smb2err), rc); + return rc; +} diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c new file mode 100644 index 000000000000..572293c18e16 --- /dev/null +++ b/fs/smb/client/smb2misc.c @@ -0,0 +1,944 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "nterr.h" +#include "cached_dir.h" + +static int +check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) +{ + __u64 wire_mid = le64_to_cpu(shdr->MessageId); + + /* + * Make sure that this really is an SMB, that it is a response, + * and that the message ids match. + */ + if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) && + (mid == wire_mid)) { + if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 0; + else { + /* only one valid case where server sends us request */ + if (shdr->Command == SMB2_OPLOCK_BREAK) + return 0; + else + cifs_dbg(VFS, "Received Request not response\n"); + } + } else { /* bad signature or mid */ + if (shdr->ProtocolId != SMB2_PROTO_NUMBER) + cifs_dbg(VFS, "Bad protocol string signature header %x\n", + le32_to_cpu(shdr->ProtocolId)); + if (mid != wire_mid) + cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", + mid, wire_mid); + } + cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", wire_mid); + return 1; +} + +/* + * The following table defines the expected "StructureSize" of SMB2 responses + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS responses. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order + */ +static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ cpu_to_le16(65), + /* SMB2_SESSION_SETUP */ cpu_to_le16(9), + /* SMB2_LOGOFF */ cpu_to_le16(4), + /* SMB2_TREE_CONNECT */ cpu_to_le16(16), + /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), + /* SMB2_CREATE */ cpu_to_le16(89), + /* SMB2_CLOSE */ cpu_to_le16(60), + /* SMB2_FLUSH */ cpu_to_le16(4), + /* SMB2_READ */ cpu_to_le16(17), + /* SMB2_WRITE */ cpu_to_le16(17), + /* SMB2_LOCK */ cpu_to_le16(4), + /* SMB2_IOCTL */ cpu_to_le16(49), + /* BB CHECK this ... not listed in documentation */ + /* SMB2_CANCEL */ cpu_to_le16(0), + /* SMB2_ECHO */ cpu_to_le16(4), + /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(9), + /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(9), + /* SMB2_QUERY_INFO */ cpu_to_le16(9), + /* SMB2_SET_INFO */ cpu_to_le16(2), + /* BB FIXME can also be 44 for lease break */ + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) +}; + +#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp)) + +static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, + __u32 non_ctxlen) +{ + __u16 neg_count; + __u32 nc_offset, size_of_pad_before_neg_ctxts; + struct smb2_negotiate_rsp *pneg_rsp = (struct smb2_negotiate_rsp *)hdr; + + /* Negotiate contexts are only valid for latest dialect SMB3.11 */ + neg_count = le16_to_cpu(pneg_rsp->NegotiateContextCount); + if ((neg_count == 0) || + (pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID))) + return 0; + + /* + * if SPNEGO blob present (ie the RFC2478 GSS info which indicates + * which security mechanisms the server supports) make sure that + * the negotiate contexts start after it + */ + nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset); + /* + * non_ctxlen is at least shdr->StructureSize + pdu->StructureSize2 + * and the latter is 1 byte bigger than the fix-sized area of the + * NEGOTIATE response + */ + if (nc_offset + 1 < non_ctxlen) { + pr_warn_once("Invalid negotiate context offset %d\n", nc_offset); + return 0; + } else if (nc_offset + 1 == non_ctxlen) { + cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n"); + size_of_pad_before_neg_ctxts = 0; + } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE) + /* has padding, but no SPNEGO blob */ + size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1; + else + size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen; + + /* Verify that at least minimal negotiate contexts fit within frame */ + if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) { + pr_warn_once("negotiate context goes beyond end\n"); + return 0; + } + + cifs_dbg(FYI, "length of negcontexts %d pad %d\n", + len - nc_offset, size_of_pad_before_neg_ctxts); + + /* length of negcontexts including pad from end of sec blob to them */ + return (len - nc_offset) + size_of_pad_before_neg_ctxts; +} + +int +smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) +{ + struct TCP_Server_Info *pserver; + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; + int hdr_size = sizeof(struct smb2_hdr); + int pdu_size = sizeof(struct smb2_pdu); + int command; + __u32 calc_len; /* calculated length */ + __u64 mid; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* + * Add function to do table lookup of StructureSize by command + * ie Validate the wct via smb2_struct_sizes table above + */ + if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { + struct smb2_transform_hdr *thdr = + (struct smb2_transform_hdr *)buf; + struct cifs_ses *ses = NULL; + struct cifs_ses *iter; + + /* decrypt frame now that it is completely read in */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(iter, &pserver->smb_ses_list, smb_ses_list) { + if (iter->Suid == le64_to_cpu(thdr->SessionId)) { + ses = iter; + break; + } + } + spin_unlock(&cifs_tcp_ses_lock); + if (!ses) { + cifs_dbg(VFS, "no decryption - session id not found\n"); + return 1; + } + } + + mid = le64_to_cpu(shdr->MessageId); + if (len < pdu_size) { + if ((len >= hdr_size) + && (shdr->Status != 0)) { + pdu->StructureSize2 = 0; + /* + * As with SMB/CIFS, on some error cases servers may + * not return wct properly + */ + return 0; + } else { + cifs_dbg(VFS, "Length less than SMB header size\n"); + } + return 1; + } + if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) { + cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n", + mid); + return 1; + } + + if (check_smb2_hdr(shdr, mid)) + return 1; + + if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { + cifs_dbg(VFS, "Invalid structure size %u\n", + le16_to_cpu(shdr->StructureSize)); + return 1; + } + + command = le16_to_cpu(shdr->Command); + if (command >= NUMBER_OF_SMB2_COMMANDS) { + cifs_dbg(VFS, "Invalid SMB2 command %d\n", command); + return 1; + } + + if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { + if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 || + pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { + /* error packets have 9 byte structure size */ + cifs_dbg(VFS, "Invalid response size %u for command %d\n", + le16_to_cpu(pdu->StructureSize2), command); + return 1; + } else if (command == SMB2_OPLOCK_BREAK_HE + && (shdr->Status == 0) + && (le16_to_cpu(pdu->StructureSize2) != 44) + && (le16_to_cpu(pdu->StructureSize2) != 36)) { + /* special case for SMB2.1 lease break message */ + cifs_dbg(VFS, "Invalid response size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); + return 1; + } + } + + calc_len = smb2_calc_size(buf); + + /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might + * be 0, and not a real miscalculation */ + if (command == SMB2_IOCTL_HE && calc_len == 0) + return 0; + + if (command == SMB2_NEGOTIATE_HE) + calc_len += get_neg_ctxt_len(shdr, len, calc_len); + + if (len != calc_len) { + /* create failed on symlink */ + if (command == SMB2_CREATE_HE && + shdr->Status == STATUS_STOPPED_ON_SYMLINK) + return 0; + /* Windows 7 server returns 24 bytes more */ + if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) + return 0; + /* server can return one byte more due to implied bcc[0] */ + if (calc_len == len + 1) + return 0; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ + if (ALIGN(calc_len, 8) == len) + return 0; + + /* + * MacOS server pads after SMB2.1 write response with 3 bytes + * of junk. Other servers match RFC1001 len to actual + * SMB2/SMB3 frame length (header + smb2 response specific data) + * Some windows servers also pad up to 8 bytes when compounding. + */ + if (calc_len < len) + return 0; + + /* Only log a message if len was really miscalculated */ + if (unlikely(cifsFYI)) + cifs_dbg(FYI, "Server response too short: calculated " + "length %u doesn't match read length %u (cmd=%d, mid=%llu)\n", + calc_len, len, command, mid); + else + pr_warn("Server response too short: calculated length " + "%u doesn't match read length %u (cmd=%d, mid=%llu)\n", + calc_len, len, command, mid); + + return 1; + } + return 0; +} + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 responses. SMB2 responses + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ true, + /* SMB2_SESSION_SETUP */ true, + /* SMB2_LOGOFF */ false, + /* SMB2_TREE_CONNECT */ false, + /* SMB2_TREE_DISCONNECT */ false, + /* SMB2_CREATE */ true, + /* SMB2_CLOSE */ false, + /* SMB2_FLUSH */ false, + /* SMB2_READ */ true, + /* SMB2_WRITE */ false, + /* SMB2_LOCK */ false, + /* SMB2_IOCTL */ true, + /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ + /* SMB2_ECHO */ false, + /* SMB2_QUERY_DIRECTORY */ true, + /* SMB2_CHANGE_NOTIFY */ true, + /* SMB2_QUERY_INFO */ true, + /* SMB2_SET_INFO */ false, + /* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Returns the pointer to the beginning of the data area. Length of the data + * area and the offset to it (from the beginning of the smb are also returned. + */ +char * +smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) +{ + *off = 0; + *len = 0; + + /* error responses do not have data area */ + if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED && + (((struct smb2_err_rsp *)shdr)->StructureSize) == + SMB2_ERROR_STRUCTURE_SIZE2_LE) + return NULL; + + /* + * Following commands have data areas so we have to get the location + * of the data buffer offset and data buffer length for the particular + * command. + */ + switch (shdr->Command) { + case SMB2_NEGOTIATE: + *off = le16_to_cpu( + ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferOffset); + *len = le16_to_cpu( + ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferLength); + break; + case SMB2_SESSION_SETUP: + *off = le16_to_cpu( + ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferOffset); + *len = le16_to_cpu( + ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferLength); + break; + case SMB2_CREATE: + *off = le32_to_cpu( + ((struct smb2_create_rsp *)shdr)->CreateContextsOffset); + *len = le32_to_cpu( + ((struct smb2_create_rsp *)shdr)->CreateContextsLength); + break; + case SMB2_QUERY_INFO: + *off = le16_to_cpu( + ((struct smb2_query_info_rsp *)shdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_query_info_rsp *)shdr)->OutputBufferLength); + break; + case SMB2_READ: + /* TODO: is this a bug ? */ + *off = ((struct smb2_read_rsp *)shdr)->DataOffset; + *len = le32_to_cpu(((struct smb2_read_rsp *)shdr)->DataLength); + break; + case SMB2_QUERY_DIRECTORY: + *off = le16_to_cpu( + ((struct smb2_query_directory_rsp *)shdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_query_directory_rsp *)shdr)->OutputBufferLength); + break; + case SMB2_IOCTL: + *off = le32_to_cpu( + ((struct smb2_ioctl_rsp *)shdr)->OutputOffset); + *len = le32_to_cpu( + ((struct smb2_ioctl_rsp *)shdr)->OutputCount); + break; + case SMB2_CHANGE_NOTIFY: + *off = le16_to_cpu( + ((struct smb2_change_notify_rsp *)shdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_change_notify_rsp *)shdr)->OutputBufferLength); + break; + default: + cifs_dbg(VFS, "no length check for command %d\n", le16_to_cpu(shdr->Command)); + break; + } + + /* + * Invalid length or offset probably means data area is invalid, but + * we have little choice but to ignore the data area in this case. + */ + if (*off > 4096) { + cifs_dbg(VFS, "offset %d too large, data area ignored\n", *off); + *len = 0; + *off = 0; + } else if (*off < 0) { + cifs_dbg(VFS, "negative offset %d to data invalid ignore data area\n", + *off); + *off = 0; + *len = 0; + } else if (*len < 0) { + cifs_dbg(VFS, "negative data length %d invalid, data area ignored\n", + *len); + *len = 0; + } else if (*len > 128 * 1024) { + cifs_dbg(VFS, "data area larger than 128K: %d\n", *len); + *len = 0; + } + + /* return pointer to beginning of data area, ie offset from SMB start */ + if ((*off != 0) && (*len != 0)) + return (char *)shdr + *off; + else + return NULL; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +unsigned int +smb2_calc_size(void *buf) +{ + struct smb2_pdu *pdu = buf; + struct smb2_hdr *shdr = &pdu->hdr; + int offset; /* the offset from the beginning of SMB to data area */ + int data_length; /* the length of the variable length data area */ + /* Structure Size has already been checked to make sure it is 64 */ + int len = le16_to_cpu(shdr->StructureSize); + + /* + * StructureSize2, ie length of fixed parameter area has already + * been checked to make sure it is the correct length. + */ + len += le16_to_cpu(pdu->StructureSize2); + + if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false) + goto calc_size_exit; + + smb2_get_data_area_len(&offset, &data_length, shdr); + cifs_dbg(FYI, "SMB2 data length %d offset %d\n", data_length, offset); + + if (data_length > 0) { + /* + * Check to make sure that data area begins after fixed area, + * Note that last byte of the fixed area is part of data area + * for some commands, typically those with odd StructureSize, + * so we must add one to the calculation. + */ + if (offset + 1 < len) { + cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", + offset + 1, len); + data_length = 0; + } else { + len = offset + data_length; + } + } +calc_size_exit: + cifs_dbg(FYI, "SMB2 len %d\n", len); + return len; +} + +/* Note: caller must free return buffer */ +__le16 * +cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) +{ + int len; + const char *start_of_path; + __le16 *to; + int map_type; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) + map_type = SFM_MAP_UNI_RSVD; + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) + map_type = SFU_MAP_UNI_RSVD; + else + map_type = NO_MAP_UNI_RSVD; + + /* Windows doesn't allow paths beginning with \ */ + if (from[0] == '\\') + start_of_path = from + 1; + + /* SMB311 POSIX extensions paths do not include leading slash */ + else if (cifs_sb_master_tlink(cifs_sb) && + cifs_sb_master_tcon(cifs_sb)->posix_extensions && + (from[0] == '/')) { + start_of_path = from + 1; + } else + start_of_path = from; + + to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, + cifs_sb->local_nls, map_type); + return to; +} + +__le32 +smb2_get_lease_state(struct cifsInodeInfo *cinode) +{ + __le32 lease = 0; + + if (CIFS_CACHE_WRITE(cinode)) + lease |= SMB2_LEASE_WRITE_CACHING_LE; + if (CIFS_CACHE_HANDLE(cinode)) + lease |= SMB2_LEASE_HANDLE_CACHING_LE; + if (CIFS_CACHE_READ(cinode)) + lease |= SMB2_LEASE_READ_CACHING_LE; + return lease; +} + +struct smb2_lease_break_work { + struct work_struct lease_break; + struct tcon_link *tlink; + __u8 lease_key[16]; + __le32 lease_state; +}; + +static void +cifs_ses_oplock_break(struct work_struct *work) +{ + struct smb2_lease_break_work *lw = container_of(work, + struct smb2_lease_break_work, lease_break); + int rc = 0; + + rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, + lw->lease_state); + + cifs_dbg(FYI, "Lease release rc %d\n", rc); + cifs_put_tlink(lw->tlink); + kfree(lw); +} + +static void +smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key, + __le32 new_lease_state) +{ + struct smb2_lease_break_work *lw; + + lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); + if (!lw) { + cifs_put_tlink(tlink); + return; + } + + INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); + lw->tlink = tlink; + lw->lease_state = new_lease_state; + memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE); + queue_work(cifsiod_wq, &lw->lease_break); +} + +static bool +smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp) +{ + __u8 lease_state; + struct cifsFileInfo *cfile; + struct cifsInodeInfo *cinode; + int ack_req = le32_to_cpu(rsp->Flags & + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + + lease_state = le32_to_cpu(rsp->NewLeaseState); + + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + cinode = CIFS_I(d_inode(cfile->dentry)); + + if (memcmp(cinode->lease_key, rsp->LeaseKey, + SMB2_LEASE_KEY_SIZE)) + continue; + + cifs_dbg(FYI, "found in the open list\n"); + cifs_dbg(FYI, "lease key match, lease break 0x%x\n", + lease_state); + + if (ack_req) + cfile->oplock_break_cancelled = false; + else + cfile->oplock_break_cancelled = true; + + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); + + cfile->oplock_epoch = le16_to_cpu(rsp->Epoch); + cfile->oplock_level = lease_state; + + cifs_queue_oplock_break(cfile); + return true; + } + + return false; +} + +static struct cifs_pending_open * +smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, + struct smb2_lease_break *rsp) +{ + __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); + int ack_req = le32_to_cpu(rsp->Flags & + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + struct cifs_pending_open *open; + struct cifs_pending_open *found = NULL; + + list_for_each_entry(open, &tcon->pending_opens, olist) { + if (memcmp(open->lease_key, rsp->LeaseKey, + SMB2_LEASE_KEY_SIZE)) + continue; + + if (!found && ack_req) { + found = open; + } + + cifs_dbg(FYI, "found in the pending open list\n"); + cifs_dbg(FYI, "lease key match, lease break 0x%x\n", + lease_state); + + open->oplock = lease_state; + } + + return found; +} + +static bool +smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifs_pending_open *open; + + cifs_dbg(FYI, "Checking for lease break\n"); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* look up tcon based on tid & uid */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->open_file_lock); + cifs_stats_inc( + &tcon->stats.cifs_stats.num_oplock_brks); + if (smb2_tcon_has_lease(tcon, rsp)) { + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + open = smb2_tcon_find_pending_open_lease(tcon, + rsp); + if (open) { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + struct tcon_link *tlink; + + tlink = cifs_get_tlink(open->tlink); + memcpy(lease_key, open->lease_key, + SMB2_LEASE_KEY_SIZE); + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + smb2_queue_pending_open_break(tlink, + lease_key, + rsp->NewLeaseState); + return true; + } + spin_unlock(&tcon->open_file_lock); + + if (cached_dir_lease_break(tcon, rsp->LeaseKey)) { + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); + trace_smb3_lease_not_found(le32_to_cpu(rsp->CurrentLeaseState), + le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), + le64_to_cpu(rsp->hdr.SessionId), + *((u64 *)rsp->LeaseKey), + *((u64 *)&rsp->LeaseKey[8])); + + return false; +} + +bool +smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifsInodeInfo *cinode; + struct cifsFileInfo *cfile; + + cifs_dbg(FYI, "Checking for oplock break\n"); + + if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) + return false; + + if (rsp->StructureSize != + smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { + if (le16_to_cpu(rsp->StructureSize) == 44) + return smb2_is_valid_lease_break(buffer, server); + else + return false; + } + + cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* look up tcon based on tid & uid */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + if (rsp->PersistentFid != + cfile->fid.persistent_fid || + rsp->VolatileFid != + cfile->fid.volatile_fid) + continue; + + cifs_dbg(FYI, "file id match, oplock break\n"); + cifs_stats_inc( + &tcon->stats.cifs_stats.num_oplock_brks); + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_lock(&cfile->file_info_lock); + if (!CIFS_CACHE_WRITE(cinode) && + rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) + cfile->oplock_break_cancelled = true; + else + cfile->oplock_break_cancelled = false; + + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, + &cinode->flags); + + cfile->oplock_epoch = 0; + cfile->oplock_level = rsp->OplockLevel; + + spin_unlock(&cfile->file_info_lock); + + cifs_queue_oplock_break(cfile); + + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + spin_unlock(&tcon->open_file_lock); + } + } + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "No file id matched, oplock break ignored\n"); + trace_smb3_oplock_not_found(0 /* no xid */, rsp->PersistentFid, + le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), + le64_to_cpu(rsp->hdr.SessionId)); + + return true; +} + +void +smb2_cancelled_close_fid(struct work_struct *work) +{ + struct close_cancelled_open *cancelled = container_of(work, + struct close_cancelled_open, work); + struct cifs_tcon *tcon = cancelled->tcon; + int rc; + + if (cancelled->mid) + cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llu\n", + cancelled->mid); + else + cifs_tcon_dbg(VFS, "Close interrupted close\n"); + + rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid, + cancelled->fid.volatile_fid); + if (rc) + cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); + + cifs_put_tcon(tcon); + kfree(cancelled); +} + +/* + * Caller should already has an extra reference to @tcon + * This function is used to queue work to close a handle to prevent leaks + * on the server. + * We handle two cases. If an open was interrupted after we sent the + * SMB2_CREATE to the server but before we processed the reply, and second + * if a close was interrupted before we sent the SMB2_CLOSE to the server. + */ +static int +__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, + __u64 persistent_fid, __u64 volatile_fid) +{ + struct close_cancelled_open *cancelled; + + cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC); + if (!cancelled) + return -ENOMEM; + + cancelled->fid.persistent_fid = persistent_fid; + cancelled->fid.volatile_fid = volatile_fid; + cancelled->tcon = tcon; + cancelled->cmd = cmd; + cancelled->mid = mid; + INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); + WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); + + return 0; +} + +int +smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid) +{ + int rc; + + cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); + spin_lock(&cifs_tcp_ses_lock); + if (tcon->tc_count <= 0) { + struct TCP_Server_Info *server = NULL; + + WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); + spin_unlock(&cifs_tcp_ses_lock); + + if (tcon->ses) + server = tcon->ses->server; + + cifs_server_dbg(FYI, "tid=0x%x: tcon is closing, skipping async close retry of fid %llu %llu\n", + tcon->tid, persistent_fid, volatile_fid); + + return 0; + } + tcon->tc_count++; + spin_unlock(&cifs_tcp_ses_lock); + + rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, + persistent_fid, volatile_fid); + if (rc) + cifs_put_tcon(tcon); + + return rc; +} + +int +smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server) +{ + struct smb2_hdr *hdr = mid->resp_buf; + struct smb2_create_rsp *rsp = mid->resp_buf; + struct cifs_tcon *tcon; + int rc; + + if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || hdr->Command != SMB2_CREATE || + hdr->Status != STATUS_SUCCESS) + return 0; + + tcon = smb2_find_smb_tcon(server, le64_to_cpu(hdr->SessionId), + le32_to_cpu(hdr->Id.SyncId.TreeId)); + if (!tcon) + return -ENOENT; + + rc = __smb2_handle_cancelled_cmd(tcon, + le16_to_cpu(hdr->Command), + le64_to_cpu(hdr->MessageId), + rsp->PersistentFileId, + rsp->VolatileFileId); + if (rc) + cifs_put_tcon(tcon); + + return rc; +} + +/** + * smb311_update_preauth_hash - update @ses hash with the packet data in @iov + * + * Assumes @iov does not contain the rfc1002 length and iov[0] has the + * SMB2 header. + * + * @ses: server session structure + * @server: pointer to server info + * @iov: array containing the SMB request we will send to the server + * @nvec: number of array entries for the iov + */ +int +smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server, + struct kvec *iov, int nvec) +{ + int i, rc; + struct smb2_hdr *hdr; + struct shash_desc *sha512 = NULL; + + hdr = (struct smb2_hdr *)iov[0].iov_base; + /* neg prot are always taken */ + if (hdr->Command == SMB2_NEGOTIATE) + goto ok; + + /* + * If we process a command which wasn't a negprot it means the + * neg prot was already done, so the server dialect was set + * and we can test it. Preauth requires 3.1.1 for now. + */ + if (server->dialect != SMB311_PROT_ID) + return 0; + + if (hdr->Command != SMB2_SESSION_SETUP) + return 0; + + /* skip last sess setup response */ + if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + && (hdr->Status == NT_STATUS_OK + || (hdr->Status != + cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)))) + return 0; + +ok: + rc = smb311_crypto_shash_allocate(server); + if (rc) + return rc; + + sha512 = server->secmech.sha512; + rc = crypto_shash_init(sha512); + if (rc) { + cifs_dbg(VFS, "%s: Could not init sha512 shash\n", __func__); + return rc; + } + + rc = crypto_shash_update(sha512, ses->preauth_sha_hash, + SMB2_PREAUTH_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__); + return rc; + } + + for (i = 0; i < nvec; i++) { + rc = crypto_shash_update(sha512, iov[i].iov_base, iov[i].iov_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update sha512 shash\n", + __func__); + return rc; + } + } + + rc = crypto_shash_final(sha512, ses->preauth_sha_hash); + if (rc) { + cifs_dbg(VFS, "%s: Could not finalize sha512 shash\n", + __func__); + return rc; + } + + return 0; +} diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c new file mode 100644 index 000000000000..d512440d35b6 --- /dev/null +++ b/fs/smb/client/smb2ops.c @@ -0,0 +1,5805 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMB2 version specific operations + * + * Copyright (c) 2012, Jeff Layton + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifsglob.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "cifs_ioctl.h" +#include "smbdirect.h" +#include "fscache.h" +#include "fs_context.h" +#include "cached_dir.h" + +/* Change credits for different ops and return the total number of credits */ +static int +change_conf(struct TCP_Server_Info *server) +{ + server->credits += server->echo_credits + server->oplock_credits; + server->oplock_credits = server->echo_credits = 0; + switch (server->credits) { + case 0: + return 0; + case 1: + server->echoes = false; + server->oplocks = false; + break; + case 2: + server->echoes = true; + server->oplocks = false; + server->echo_credits = 1; + break; + default: + server->echoes = true; + if (enable_oplocks) { + server->oplocks = true; + server->oplock_credits = 1; + } else + server->oplocks = false; + + server->echo_credits = 1; + } + server->credits -= server->echo_credits + server->oplock_credits; + return server->credits + server->echo_credits + server->oplock_credits; +} + +static void +smb2_add_credits(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) +{ + int *val, rc = -1; + int scredits, in_flight; + unsigned int add = credits->value; + unsigned int instance = credits->instance; + bool reconnect_detected = false; + bool reconnect_with_invalid_credits = false; + + spin_lock(&server->req_lock); + val = server->ops->get_credits_field(server, optype); + + /* eg found case where write overlapping reconnect messed up credits */ + if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) + reconnect_with_invalid_credits = true; + + if ((instance == 0) || (instance == server->reconnect_instance)) + *val += add; + else + reconnect_detected = true; + + if (*val > 65000) { + *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ + pr_warn_once("server overflowed SMB3 credits\n"); + trace_smb3_overflow_credits(server->CurrentMid, + server->conn_id, server->hostname, *val, + add, server->in_flight); + } + server->in_flight--; + if (server->in_flight == 0 && + ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) && + ((optype & CIFS_OP_MASK) != CIFS_SESS_OP)) + rc = change_conf(server); + /* + * Sometimes server returns 0 credits on oplock break ack - we need to + * rebalance credits in this case. + */ + else if (server->in_flight > 0 && server->oplock_credits == 0 && + server->oplocks) { + if (server->credits > 1) { + server->credits--; + server->oplock_credits++; + } + } + scredits = *val; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + if (reconnect_detected) { + trace_smb3_reconnect_detected(server->CurrentMid, + server->conn_id, server->hostname, scredits, add, in_flight); + + cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", + add, instance); + } + + if (reconnect_with_invalid_credits) { + trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, add, in_flight); + cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n", + optype, scredits, add); + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect + || server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return; + } + spin_unlock(&server->srv_lock); + + switch (rc) { + case -1: + /* change_conf hasn't been executed */ + break; + case 0: + cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n"); + break; + case 1: + cifs_server_dbg(VFS, "disabling echoes and oplocks\n"); + break; + case 2: + cifs_dbg(FYI, "disabling oplocks\n"); + break; + default: + /* change_conf rebalanced credits for different types */ + break; + } + + trace_smb3_add_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, add, in_flight); + cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits); +} + +static void +smb2_set_credits(struct TCP_Server_Info *server, const int val) +{ + int scredits, in_flight; + + spin_lock(&server->req_lock); + server->credits = val; + if (val == 1) + server->reconnect_instance++; + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_set_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, val, in_flight); + cifs_dbg(FYI, "%s: set %u credits\n", __func__, val); + + /* don't log while holding the lock */ + if (val == 1) + cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n"); +} + +static int * +smb2_get_credits_field(struct TCP_Server_Info *server, const int optype) +{ + switch (optype) { + case CIFS_ECHO_OP: + return &server->echo_credits; + case CIFS_OBREAK_OP: + return &server->oplock_credits; + default: + return &server->credits; + } +} + +static unsigned int +smb2_get_credits(struct mid_q_entry *mid) +{ + return mid->credits_received; +} + +static int +smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, + unsigned int *num, struct cifs_credits *credits) +{ + int rc = 0; + unsigned int scredits, in_flight; + + spin_lock(&server->req_lock); + while (1) { + if (server->credits <= 0) { + spin_unlock(&server->req_lock); + cifs_num_waiters_inc(server); + rc = wait_event_killable(server->request_q, + has_credits(server, &server->credits, 1)); + cifs_num_waiters_dec(server); + if (rc) + return rc; + spin_lock(&server->req_lock); + } else { + spin_unlock(&server->req_lock); + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + spin_lock(&server->req_lock); + scredits = server->credits; + /* can deadlock with reopen */ + if (scredits <= 8) { + *num = SMB2_MAX_BUFFER_SIZE; + credits->value = 0; + credits->instance = 0; + break; + } + + /* leave some credits for reopen and other ops */ + scredits -= 8; + *num = min_t(unsigned int, size, + scredits * SMB2_MAX_BUFFER_SIZE); + + credits->value = + DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); + credits->instance = server->reconnect_instance; + server->credits -= credits->value; + server->in_flight++; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; + break; + } + } + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_wait_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, -(credits->value), in_flight); + cifs_dbg(FYI, "%s: removed %u credits total=%d\n", + __func__, credits->value, scredits); + + return rc; +} + +static int +smb2_adjust_credits(struct TCP_Server_Info *server, + struct cifs_credits *credits, + const unsigned int payload_size) +{ + int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE); + int scredits, in_flight; + + if (!credits->value || credits->value == new_val) + return 0; + + if (credits->value < new_val) { + trace_smb3_too_many_credits(server->CurrentMid, + server->conn_id, server->hostname, 0, credits->value - new_val, 0); + cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)", + credits->value, new_val); + + return -ENOTSUPP; + } + + spin_lock(&server->req_lock); + + if (server->reconnect_instance != credits->instance) { + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_reconnect_detected(server->CurrentMid, + server->conn_id, server->hostname, scredits, + credits->value - new_val, in_flight); + cifs_server_dbg(VFS, "trying to return %d credits to old session\n", + credits->value - new_val); + return -EAGAIN; + } + + server->credits += credits->value - new_val; + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + trace_smb3_adj_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + credits->value - new_val, in_flight); + cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n", + __func__, credits->value - new_val, scredits); + + credits->value = new_val; + + return 0; +} + +static __u64 +smb2_get_next_mid(struct TCP_Server_Info *server) +{ + __u64 mid; + /* for SMB2 we need the current value */ + spin_lock(&server->mid_lock); + mid = server->CurrentMid++; + spin_unlock(&server->mid_lock); + return mid; +} + +static void +smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) +{ + spin_lock(&server->mid_lock); + if (server->CurrentMid >= val) + server->CurrentMid -= val; + spin_unlock(&server->mid_lock); +} + +static struct mid_q_entry * +__smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue) +{ + struct mid_q_entry *mid; + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + __u64 wire_mid = le64_to_cpu(shdr->MessageId); + + if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { + cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n"); + return NULL; + } + + spin_lock(&server->mid_lock); + list_for_each_entry(mid, &server->pending_mid_q, qhead) { + if ((mid->mid == wire_mid) && + (mid->mid_state == MID_REQUEST_SUBMITTED) && + (mid->command == shdr->Command)) { + kref_get(&mid->refcount); + if (dequeue) { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + } + spin_unlock(&server->mid_lock); + return mid; + } + } + spin_unlock(&server->mid_lock); + return NULL; +} + +static struct mid_q_entry * +smb2_find_mid(struct TCP_Server_Info *server, char *buf) +{ + return __smb2_find_mid(server, buf, false); +} + +static struct mid_q_entry * +smb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf) +{ + return __smb2_find_mid(server, buf, true); +} + +static void +smb2_dump_detail(void *buf, struct TCP_Server_Info *server) +{ +#ifdef CONFIG_CIFS_DEBUG2 + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", + shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, + shdr->Id.SyncId.ProcessId); + cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, + server->ops->calc_smb_size(buf)); +#endif +} + +static bool +smb2_need_neg(struct TCP_Server_Info *server) +{ + return server->max_read == 0; +} + +static int +smb2_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + int rc; + + spin_lock(&server->mid_lock); + server->CurrentMid = 0; + spin_unlock(&server->mid_lock); + rc = SMB2_negotiate(xid, ses, server); + /* BB we probably don't need to retry with modern servers */ + if (rc == -EAGAIN) + rc = -EHOSTDOWN; + return rc; +} + +static unsigned int +smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int wsize; + + /* start with specified wsize, or default */ + wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE; + wsize = min_t(unsigned int, wsize, server->max_write); + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); + + return wsize; +} + +static unsigned int +smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int wsize; + + /* start with specified wsize, or default */ + wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE; + wsize = min_t(unsigned int, wsize, server->max_write); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (server->rdma) { + if (server->sign) + /* + * Account for SMB2 data transfer packet header and + * possible encryption header + */ + wsize = min_t(unsigned int, + wsize, + server->smbd_conn->max_fragmented_send_size - + SMB2_READWRITE_PDU_HEADER_SIZE - + sizeof(struct smb2_transform_hdr)); + else + wsize = min_t(unsigned int, + wsize, server->smbd_conn->max_readwrite_size); + } +#endif + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); + + return wsize; +} + +static unsigned int +smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int rsize; + + /* start with specified rsize, or default */ + rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE; + rsize = min_t(unsigned int, rsize, server->max_read); + + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); + + return rsize; +} + +static unsigned int +smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int rsize; + + /* start with specified rsize, or default */ + rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE; + rsize = min_t(unsigned int, rsize, server->max_read); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (server->rdma) { + if (server->sign) + /* + * Account for SMB2 data transfer packet header and + * possible encryption header + */ + rsize = min_t(unsigned int, + rsize, + server->smbd_conn->max_fragmented_recv_size - + SMB2_READWRITE_PDU_HEADER_SIZE - + sizeof(struct smb2_transform_hdr)); + else + rsize = min_t(unsigned int, + rsize, server->smbd_conn->max_readwrite_size); + } +#endif + + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); + + return rsize; +} + +static int +parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, + size_t buf_len, struct cifs_ses *ses, bool in_mount) +{ + struct network_interface_info_ioctl_rsp *p; + struct sockaddr_in *addr4; + struct sockaddr_in6 *addr6; + struct iface_info_ipv4 *p4; + struct iface_info_ipv6 *p6; + struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL; + struct cifs_server_iface tmp_iface; + ssize_t bytes_left; + size_t next = 0; + int nb_iface = 0; + int rc = 0, ret = 0; + + bytes_left = buf_len; + p = buf; + + spin_lock(&ses->iface_lock); + /* do not query too frequently, this time with lock held */ + if (ses->iface_last_update && + time_before(jiffies, ses->iface_last_update + + (SMB_INTERFACE_POLL_INTERVAL * HZ))) { + spin_unlock(&ses->iface_lock); + return 0; + } + + /* + * Go through iface_list and do kref_put to remove + * any unused ifaces. ifaces in use will be removed + * when the last user calls a kref_put on it + */ + list_for_each_entry_safe(iface, niface, &ses->iface_list, + iface_head) { + iface->is_active = 0; + kref_put(&iface->refcount, release_iface); + ses->iface_count--; + } + spin_unlock(&ses->iface_lock); + + /* + * Samba server e.g. can return an empty interface list in some cases, + * which would only be a problem if we were requesting multichannel + */ + if (bytes_left == 0) { + /* avoid spamming logs every 10 minutes, so log only in mount */ + if ((ses->chan_max > 1) && in_mount) + cifs_dbg(VFS, + "multichannel not available\n" + "Empty network interface list returned by server %s\n", + ses->server->hostname); + rc = -EINVAL; + goto out; + } + + while (bytes_left >= sizeof(*p)) { + memset(&tmp_iface, 0, sizeof(tmp_iface)); + tmp_iface.speed = le64_to_cpu(p->LinkSpeed); + tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0; + tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0; + + switch (p->Family) { + /* + * The kernel and wire socket structures have the same + * layout and use network byte order but make the + * conversion explicit in case either one changes. + */ + case INTERNETWORK: + addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr; + p4 = (struct iface_info_ipv4 *)p->Buffer; + addr4->sin_family = AF_INET; + memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); + + /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ + addr4->sin_port = cpu_to_be16(CIFS_PORT); + + cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, + &addr4->sin_addr); + break; + case INTERNETWORKV6: + addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr; + p6 = (struct iface_info_ipv6 *)p->Buffer; + addr6->sin6_family = AF_INET6; + memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); + + /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + addr6->sin6_port = cpu_to_be16(CIFS_PORT); + + cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, + &addr6->sin6_addr); + break; + default: + cifs_dbg(VFS, + "%s: skipping unsupported socket family\n", + __func__); + goto next_iface; + } + + /* + * The iface_list is assumed to be sorted by speed. + * Check if the new interface exists in that list. + * NEVER change iface. it could be in use. + * Add a new one instead + */ + spin_lock(&ses->iface_lock); + iface = niface = NULL; + list_for_each_entry_safe(iface, niface, &ses->iface_list, + iface_head) { + ret = iface_cmp(iface, &tmp_iface); + if (!ret) { + /* just get a ref so that it doesn't get picked/freed */ + iface->is_active = 1; + kref_get(&iface->refcount); + ses->iface_count++; + spin_unlock(&ses->iface_lock); + goto next_iface; + } else if (ret < 0) { + /* all remaining ifaces are slower */ + kref_get(&iface->refcount); + break; + } + } + spin_unlock(&ses->iface_lock); + + /* no match. insert the entry in the list */ + info = kmalloc(sizeof(struct cifs_server_iface), + GFP_KERNEL); + if (!info) { + rc = -ENOMEM; + goto out; + } + memcpy(info, &tmp_iface, sizeof(tmp_iface)); + + /* add this new entry to the list */ + kref_init(&info->refcount); + info->is_active = 1; + + cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, ses->iface_count); + cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); + cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, + le32_to_cpu(p->Capability)); + + spin_lock(&ses->iface_lock); + if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) { + list_add_tail(&info->iface_head, &iface->iface_head); + kref_put(&iface->refcount, release_iface); + } else + list_add_tail(&info->iface_head, &ses->iface_list); + + ses->iface_count++; + spin_unlock(&ses->iface_lock); + ses->iface_last_update = jiffies; +next_iface: + nb_iface++; + next = le32_to_cpu(p->Next); + if (!next) { + bytes_left -= sizeof(*p); + break; + } + p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); + bytes_left -= next; + } + + if (!nb_iface) { + cifs_dbg(VFS, "%s: malformed interface info\n", __func__); + rc = -EINVAL; + goto out; + } + + /* Azure rounds the buffer size up 8, to a 16 byte boundary */ + if ((bytes_left > 8) || p->Next) + cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); + + + if (!ses->iface_count) { + rc = -EINVAL; + goto out; + } + +out: + return rc; +} + +int +SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount) +{ + int rc; + unsigned int ret_data_len = 0; + struct network_interface_info_ioctl_rsp *out_buf = NULL; + struct cifs_ses *ses = tcon->ses; + + /* do not query too frequently */ + if (ses->iface_last_update && + time_before(jiffies, ses->iface_last_update + + (SMB_INTERFACE_POLL_INTERVAL * HZ))) + return 0; + + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + NULL /* no data input */, 0 /* no data input */, + CIFSMaxBufSize, (char **)&out_buf, &ret_data_len); + if (rc == -EOPNOTSUPP) { + cifs_dbg(FYI, + "server does not support query network interfaces\n"); + ret_data_len = 0; + } else if (rc != 0) { + cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc); + goto out; + } + + rc = parse_server_interfaces(out_buf, ret_data_len, ses, in_mount); + if (rc) + goto out; + +out: + kfree(out_buf); + return rc; +} + +static void +smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct cached_fid *cfid = NULL; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = "", + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid); + if (rc == 0) + memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid)); + else + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) + return; + + SMB3_request_interfaces(xid, tcon, true /* called during mount */); + + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_ATTRIBUTE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_DEVICE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_VOLUME_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ + if (cfid == NULL) + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + else + close_cached_dir(cfid); +} + +static void +smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = "", + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) + return; + + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_ATTRIBUTE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_DEVICE_INFORMATION); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +} + +static int +smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path) +{ + __le16 *utf16_path; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + int err_buftype = CIFS_NO_BUFFER; + struct cifs_open_parms oparms; + struct kvec err_iov = {}; + struct cifs_fid fid; + struct cached_fid *cfid; + bool islink; + int rc, rc2; + + rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid); + if (!rc) { + if (cfid->has_lease) { + close_cached_dir(cfid); + return 0; + } + close_cached_dir(cfid); + } + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, + &err_iov, &err_buftype); + if (rc) { + struct smb2_hdr *hdr = err_iov.iov_base; + + if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER)) + goto out; + + if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, + full_path, &islink); + if (rc2) { + rc = rc2; + goto out; + } + if (islink) + rc = -EREMOTE; + } + if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) + rc = -EOPNOTSUPP; + goto out; + } + + rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + +out: + free_rsp_buf(err_buftype, err_iov.iov_base); + kfree(utf16_path); + return rc; +} + +static int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + u64 *uniqueid, struct cifs_open_info_data *data) +{ + *uniqueid = le64_to_cpu(data->fi.IndexNumber); + return 0; +} + +static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct cifs_open_info_data *data) +{ + struct cifs_fid *fid = &cfile->fid; + + if (cfile->symlink_target) { + data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!data->symlink_target) + return -ENOMEM; + } + return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi); +} + +#ifdef CONFIG_CIFS_XATTR +static ssize_t +move_smb2_ea_to_cifs(char *dst, size_t dst_size, + struct smb2_file_full_ea_info *src, size_t src_size, + const unsigned char *ea_name) +{ + int rc = 0; + unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; + char *name, *value; + size_t buf_size = dst_size; + size_t name_len, value_len, user_name_len; + + while (src_size > 0) { + name_len = (size_t)src->ea_name_length; + value_len = (size_t)le16_to_cpu(src->ea_value_length); + + if (name_len == 0) + break; + + if (src_size < 8 + name_len + 1 + value_len) { + cifs_dbg(FYI, "EA entry goes beyond length of list\n"); + rc = -EIO; + goto out; + } + + name = &src->ea_data[0]; + value = &src->ea_data[src->ea_name_length + 1]; + + if (ea_name) { + if (ea_name_len == name_len && + memcmp(ea_name, name, name_len) == 0) { + rc = value_len; + if (dst_size == 0) + goto out; + if (dst_size < value_len) { + rc = -ERANGE; + goto out; + } + memcpy(dst, value, value_len); + goto out; + } + } else { + /* 'user.' plus a terminating null */ + user_name_len = 5 + 1 + name_len; + + if (buf_size == 0) { + /* skip copy - calc size only */ + rc += user_name_len; + } else if (dst_size >= user_name_len) { + dst_size -= user_name_len; + memcpy(dst, "user.", 5); + dst += 5; + memcpy(dst, src->ea_data, name_len); + dst += name_len; + *dst = 0; + ++dst; + rc += user_name_len; + } else { + /* stop before overrun buffer */ + rc = -ERANGE; + break; + } + } + + if (!src->next_entry_offset) + break; + + if (src_size < le32_to_cpu(src->next_entry_offset)) { + /* stop before overrun buffer */ + rc = -ERANGE; + break; + } + src_size -= le32_to_cpu(src->next_entry_offset); + src = (void *)((char *)src + + le32_to_cpu(src->next_entry_offset)); + } + + /* didn't find the named attribute */ + if (ea_name) + rc = -ENODATA; + +out: + return (ssize_t)rc; +} + +static ssize_t +smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *path, const unsigned char *ea_name, + char *ea_data, size_t buf_size, + struct cifs_sb_info *cifs_sb) +{ + int rc; + struct kvec rsp_iov = {NULL, 0}; + int buftype = CIFS_NO_BUFFER; + struct smb2_query_info_rsp *rsp; + struct smb2_file_full_ea_info *info = NULL; + + rc = smb2_query_info_compound(xid, tcon, path, + FILE_READ_EA, + FILE_FULL_EA_INFORMATION, + SMB2_O_INFO_FILE, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE, + &rsp_iov, &buftype, cifs_sb); + if (rc) { + /* + * If ea_name is NULL (listxattr) and there are no EAs, + * return 0 as it's not an error. Otherwise, the specified + * ea_name was not found. + */ + if (!ea_name && rc == -ENODATA) + rc = 0; + goto qeas_exit; + } + + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov, + sizeof(struct smb2_file_full_ea_info)); + if (rc) + goto qeas_exit; + + info = (struct smb2_file_full_ea_info *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, + le32_to_cpu(rsp->OutputBufferLength), ea_name); + + qeas_exit: + free_rsp_buf(buftype, rsp_iov.iov_base); + return rc; +} + + +static int +smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, const char *ea_name, const void *ea_value, + const __u16 ea_value_len, const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + __le16 *utf16_path = NULL; + int ea_name_len = strlen(ea_name); + int flags = CIFS_CP_CREATE_CLOSE_OP; + int len; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct cifs_open_parms oparms; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_fid fid; + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; + unsigned int size[1]; + void *data[1]; + struct smb2_file_full_ea_info *ea = NULL; + struct kvec close_iov[1]; + struct smb2_query_info_rsp *rsp; + int rc, used_len = 0; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + if (ea_name_len > 255) + return -EINVAL; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + if (ses->server->ops->query_all_EAs) { + if (!ea_value) { + rc = ses->server->ops->query_all_EAs(xid, tcon, path, + ea_name, NULL, 0, + cifs_sb); + if (rc == -ENODATA) + goto sea_exit; + } else { + /* If we are adding a attribute we should first check + * if there will be enough space available to store + * the new EA. If not we should not add it since we + * would not be able to even read the EAs back. + */ + rc = smb2_query_info_compound(xid, tcon, path, + FILE_READ_EA, + FILE_FULL_EA_INFORMATION, + SMB2_O_INFO_FILE, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE, + &rsp_iov[1], &resp_buftype[1], cifs_sb); + if (rc == 0) { + rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + used_len = le32_to_cpu(rsp->OutputBufferLength); + } + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + resp_buftype[1] = CIFS_NO_BUFFER; + memset(&rsp_iov[1], 0, sizeof(rsp_iov[1])); + rc = 0; + + /* Use a fudge factor of 256 bytes in case we collide + * with a different set_EAs command. + */ + if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < + used_len + ea_name_len + ea_value_len + 1) { + rc = -ENOSPC; + goto sea_exit; + } + } + } + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = FILE_WRITE_EA, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto sea_exit; + smb2_set_next_command(tcon, &rqst[0]); + + + /* Set Info */ + memset(&si_iov, 0, sizeof(si_iov)); + rqst[1].rq_iov = si_iov; + rqst[1].rq_nvec = 1; + + len = sizeof(*ea) + ea_name_len + ea_value_len + 1; + ea = kzalloc(len, GFP_KERNEL); + if (ea == NULL) { + rc = -ENOMEM; + goto sea_exit; + } + + ea->ea_name_length = ea_name_len; + ea->ea_value_length = cpu_to_le16(ea_value_len); + memcpy(ea->ea_data, ea_name, ea_name_len + 1); + memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); + + size[0] = len; + data[0] = ea; + + rc = SMB2_set_info_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_FULL_EA_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (rc) + goto sea_exit; + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto sea_exit; + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + /* no need to bump num_remote_opens because handle immediately closed */ + + sea_exit: + kfree(ea); + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_set_info_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + return rc; +} +#endif + +static bool +smb2_can_echo(struct TCP_Server_Info *server) +{ + return server->echoes; +} + +static void +smb2_clear_stats(struct cifs_tcon *tcon) +{ + int i; + + for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { + atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); + atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); + } +} + +static void +smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) +{ + seq_puts(m, "\n\tShare Capabilities:"); + if (tcon->capabilities & SMB2_SHARE_CAP_DFS) + seq_puts(m, " DFS,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) + seq_puts(m, " CONTINUOUS AVAILABILITY,"); + if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) + seq_puts(m, " SCALEOUT,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) + seq_puts(m, " CLUSTER,"); + if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) + seq_puts(m, " ASYMMETRIC,"); + if (tcon->capabilities == 0) + seq_puts(m, " None"); + if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) + seq_puts(m, " Aligned,"); + if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) + seq_puts(m, " Partition Aligned,"); + if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) + seq_puts(m, " SSD,"); + if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) + seq_puts(m, " TRIM-support,"); + + seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); + seq_printf(m, "\n\ttid: 0x%x", tcon->tid); + if (tcon->perf_sector_size) + seq_printf(m, "\tOptimal sector size: 0x%x", + tcon->perf_sector_size); + seq_printf(m, "\tMaximal Access: 0x%x", tcon->maximal_access); +} + +static void +smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) +{ + atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; + atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; + + /* + * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO + * totals (requests sent) since those SMBs are per-session not per tcon + */ + seq_printf(m, "\nBytes read: %llu Bytes written: %llu", + (long long)(tcon->bytes_read), + (long long)(tcon->bytes_written)); + seq_printf(m, "\nOpen files: %d total (local), %d open on server", + atomic_read(&tcon->num_local_opens), + atomic_read(&tcon->num_remote_opens)); + seq_printf(m, "\nTreeConnects: %d total %d failed", + atomic_read(&sent[SMB2_TREE_CONNECT_HE]), + atomic_read(&failed[SMB2_TREE_CONNECT_HE])); + seq_printf(m, "\nTreeDisconnects: %d total %d failed", + atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), + atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); + seq_printf(m, "\nCreates: %d total %d failed", + atomic_read(&sent[SMB2_CREATE_HE]), + atomic_read(&failed[SMB2_CREATE_HE])); + seq_printf(m, "\nCloses: %d total %d failed", + atomic_read(&sent[SMB2_CLOSE_HE]), + atomic_read(&failed[SMB2_CLOSE_HE])); + seq_printf(m, "\nFlushes: %d total %d failed", + atomic_read(&sent[SMB2_FLUSH_HE]), + atomic_read(&failed[SMB2_FLUSH_HE])); + seq_printf(m, "\nReads: %d total %d failed", + atomic_read(&sent[SMB2_READ_HE]), + atomic_read(&failed[SMB2_READ_HE])); + seq_printf(m, "\nWrites: %d total %d failed", + atomic_read(&sent[SMB2_WRITE_HE]), + atomic_read(&failed[SMB2_WRITE_HE])); + seq_printf(m, "\nLocks: %d total %d failed", + atomic_read(&sent[SMB2_LOCK_HE]), + atomic_read(&failed[SMB2_LOCK_HE])); + seq_printf(m, "\nIOCTLs: %d total %d failed", + atomic_read(&sent[SMB2_IOCTL_HE]), + atomic_read(&failed[SMB2_IOCTL_HE])); + seq_printf(m, "\nQueryDirectories: %d total %d failed", + atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), + atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); + seq_printf(m, "\nChangeNotifies: %d total %d failed", + atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), + atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); + seq_printf(m, "\nQueryInfos: %d total %d failed", + atomic_read(&sent[SMB2_QUERY_INFO_HE]), + atomic_read(&failed[SMB2_QUERY_INFO_HE])); + seq_printf(m, "\nSetInfos: %d total %d failed", + atomic_read(&sent[SMB2_SET_INFO_HE]), + atomic_read(&failed[SMB2_SET_INFO_HE])); + seq_printf(m, "\nOplockBreaks: %d sent %d failed", + atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), + atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); +} + +static void +smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + + cfile->fid.persistent_fid = fid->persistent_fid; + cfile->fid.volatile_fid = fid->volatile_fid; + cfile->fid.access = fid->access; +#ifdef CONFIG_CIFS_DEBUG2 + cfile->fid.mid = fid->mid; +#endif /* CIFS_DEBUG2 */ + server->ops->set_oplock_level(cinode, oplock, fid->epoch, + &fid->purge_cache); + cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); + memcpy(cfile->fid.create_guid, fid->create_guid, 16); +} + +static void +smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +static void +smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + struct smb2_file_network_open_info file_inf; + struct inode *inode; + int rc; + + rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, &file_inf); + if (rc) + return; + + inode = d_inode(cfile->dentry); + + spin_lock(&inode->i_lock); + CIFS_I(inode)->time = jiffies; + + /* Creation time should not need to be updated on close */ + if (file_inf.LastWriteTime) + inode->i_mtime = cifs_NTtimeToUnix(file_inf.LastWriteTime); + if (file_inf.ChangeTime) + inode->i_ctime = cifs_NTtimeToUnix(file_inf.ChangeTime); + if (file_inf.LastAccessTime) + inode->i_atime = cifs_NTtimeToUnix(file_inf.LastAccessTime); + + /* + * i_blocks is not related to (i_size / i_blksize), + * but instead 512 byte (2**9) size is required for + * calculating num blocks. + */ + if (le64_to_cpu(file_inf.AllocationSize) > 4096) + inode->i_blocks = + (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9; + + /* End of file and Attributes should not have to be updated on close */ + spin_unlock(&inode->i_lock); +} + +static int +SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct copychunk_ioctl *pcchunk) +{ + int rc; + unsigned int ret_data_len; + struct resume_key_req *res_key; + + rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, + FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */, + CIFSMaxBufSize, (char **)&res_key, &ret_data_len); + + if (rc == -EOPNOTSUPP) { + pr_warn_once("Server share %s does not support copy range\n", tcon->tree_name); + goto req_res_key_exit; + } else if (rc) { + cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); + goto req_res_key_exit; + } + if (ret_data_len < sizeof(struct resume_key_req)) { + cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n"); + rc = -EINVAL; + goto req_res_key_exit; + } + memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); + +req_res_key_exit: + kfree(res_key); + return rc; +} + +struct iqi_vars { + struct smb_rqst rqst[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; + struct kvec close_iov[1]; +}; + +static int +smb2_ioctl_query_info(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + __le16 *path, int is_dir, + unsigned long p) +{ + struct iqi_vars *vars; + struct smb_rqst *rqst; + struct kvec *rsp_iov; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + char __user *arg = (char __user *)p; + struct smb_query_info qi; + struct smb_query_info __user *pqi; + int rc = 0; + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb2_query_info_rsp *qi_rsp = NULL; + struct smb2_ioctl_rsp *io_rsp = NULL; + void *buffer = NULL; + int resp_buftype[3]; + struct cifs_open_parms oparms; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_fid fid; + unsigned int size[2]; + void *data[2]; + int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; + void (*free_req1_func)(struct smb_rqst *r); + + vars = kzalloc(sizeof(*vars), GFP_ATOMIC); + if (vars == NULL) + return -ENOMEM; + rqst = &vars->rqst[0]; + rsp_iov = &vars->rsp_iov[0]; + + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + + if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) { + rc = -EFAULT; + goto free_vars; + } + if (qi.output_buffer_length > 1024) { + rc = -EINVAL; + goto free_vars; + } + + if (!ses || !server) { + rc = -EIO; + goto free_vars; + } + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + if (qi.output_buffer_length) { + buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length); + if (IS_ERR(buffer)) { + rc = PTR_ERR(buffer); + goto free_vars; + } + } + + /* Open */ + rqst[0].rq_iov = &vars->open_iov[0]; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, create_options), + .fid = &fid, + }; + + if (qi.flags & PASSTHRU_FSCTL) { + switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { + case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: + oparms.desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE; + break; + case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS: + oparms.desired_access = GENERIC_ALL; + break; + case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS: + oparms.desired_access = GENERIC_READ; + break; + case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS: + oparms.desired_access = GENERIC_WRITE; + break; + } + } else if (qi.flags & PASSTHRU_SET_INFO) { + oparms.desired_access = GENERIC_WRITE; + } else { + oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; + } + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, path); + if (rc) + goto free_output_buffer; + smb2_set_next_command(tcon, &rqst[0]); + + /* Query */ + if (qi.flags & PASSTHRU_FSCTL) { + /* Can eventually relax perm check since server enforces too */ + if (!capable(CAP_SYS_ADMIN)) { + rc = -EPERM; + goto free_open_req; + } + rqst[1].rq_iov = &vars->io_iov[0]; + rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, + qi.info_type, buffer, qi.output_buffer_length, + CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE); + free_req1_func = SMB2_ioctl_free; + } else if (qi.flags == PASSTHRU_SET_INFO) { + /* Can eventually relax perm check since server enforces too */ + if (!capable(CAP_SYS_ADMIN)) { + rc = -EPERM; + goto free_open_req; + } + if (qi.output_buffer_length < 8) { + rc = -EINVAL; + goto free_open_req; + } + rqst[1].rq_iov = &vars->si_iov[0]; + rqst[1].rq_nvec = 1; + + /* MS-FSCC 2.4.13 FileEndOfFileInformation */ + size[0] = 8; + data[0] = buffer; + + rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, + current->tgid, FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + free_req1_func = SMB2_set_info_free; + } else if (qi.flags == PASSTHRU_QUERY_INFO) { + rqst[1].rq_iov = &vars->qi_iov[0]; + rqst[1].rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, qi.file_info_class, + qi.info_type, qi.additional_information, + qi.input_buffer_length, + qi.output_buffer_length, buffer); + free_req1_func = SMB2_query_info_free; + } else { /* unknown flags */ + cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n", + qi.flags); + rc = -EINVAL; + } + + if (rc) + goto free_open_req; + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + /* Close */ + rqst[2].rq_iov = &vars->close_iov[0]; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto free_req_1; + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + if (rc) + goto out; + + /* No need to bump num_remote_opens since handle immediately closed */ + if (qi.flags & PASSTHRU_FSCTL) { + pqi = (struct smb_query_info __user *)arg; + io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base; + if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) + qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); + if (qi.input_buffer_length > 0 && + le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length + > rsp_iov[1].iov_len) { + rc = -EFAULT; + goto out; + } + + if (copy_to_user(&pqi->input_buffer_length, + &qi.input_buffer_length, + sizeof(qi.input_buffer_length))) { + rc = -EFAULT; + goto out; + } + + if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info), + (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset), + qi.input_buffer_length)) + rc = -EFAULT; + } else { + pqi = (struct smb_query_info __user *)arg; + qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) + qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); + if (copy_to_user(&pqi->input_buffer_length, + &qi.input_buffer_length, + sizeof(qi.input_buffer_length))) { + rc = -EFAULT; + goto out; + } + + if (copy_to_user(pqi + 1, qi_rsp->Buffer, + qi.input_buffer_length)) + rc = -EFAULT; + } + +out: + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + SMB2_close_free(&rqst[2]); +free_req_1: + free_req1_func(&rqst[1]); +free_open_req: + SMB2_open_free(&rqst[0]); +free_output_buffer: + kfree(buffer); +free_vars: + kfree(vars); + return rc; +} + +static ssize_t +smb2_copychunk_range(const unsigned int xid, + struct cifsFileInfo *srcfile, + struct cifsFileInfo *trgtfile, u64 src_off, + u64 len, u64 dest_off) +{ + int rc; + unsigned int ret_data_len; + struct copychunk_ioctl *pcchunk; + struct copychunk_ioctl_rsp *retbuf = NULL; + struct cifs_tcon *tcon; + int chunks_copied = 0; + bool chunk_sizes_updated = false; + ssize_t bytes_written, total_bytes_written = 0; + + pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); + if (pcchunk == NULL) + return -ENOMEM; + + cifs_dbg(FYI, "%s: about to call request res key\n", __func__); + /* Request a key from the server to identify the source of the copy */ + rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), + srcfile->fid.persistent_fid, + srcfile->fid.volatile_fid, pcchunk); + + /* Note: request_res_key sets res_key null only if rc !=0 */ + if (rc) + goto cchunk_out; + + /* For now array only one chunk long, will make more flexible later */ + pcchunk->ChunkCount = cpu_to_le32(1); + pcchunk->Reserved = 0; + pcchunk->Reserved2 = 0; + + tcon = tlink_tcon(trgtfile->tlink); + + while (len > 0) { + pcchunk->SourceOffset = cpu_to_le64(src_off); + pcchunk->TargetOffset = cpu_to_le64(dest_off); + pcchunk->Length = + cpu_to_le32(min_t(u64, len, tcon->max_bytes_chunk)); + + /* Request server copy to target from src identified by key */ + kfree(retbuf); + retbuf = NULL; + rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, + trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, + (char *)pcchunk, sizeof(struct copychunk_ioctl), + CIFSMaxBufSize, (char **)&retbuf, &ret_data_len); + if (rc == 0) { + if (ret_data_len != + sizeof(struct copychunk_ioctl_rsp)) { + cifs_tcon_dbg(VFS, "Invalid cchunk response size\n"); + rc = -EIO; + goto cchunk_out; + } + if (retbuf->TotalBytesWritten == 0) { + cifs_dbg(FYI, "no bytes copied\n"); + rc = -EIO; + goto cchunk_out; + } + /* + * Check if server claimed to write more than we asked + */ + if (le32_to_cpu(retbuf->TotalBytesWritten) > + le32_to_cpu(pcchunk->Length)) { + cifs_tcon_dbg(VFS, "Invalid copy chunk response\n"); + rc = -EIO; + goto cchunk_out; + } + if (le32_to_cpu(retbuf->ChunksWritten) != 1) { + cifs_tcon_dbg(VFS, "Invalid num chunks written\n"); + rc = -EIO; + goto cchunk_out; + } + chunks_copied++; + + bytes_written = le32_to_cpu(retbuf->TotalBytesWritten); + src_off += bytes_written; + dest_off += bytes_written; + len -= bytes_written; + total_bytes_written += bytes_written; + + cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n", + le32_to_cpu(retbuf->ChunksWritten), + le32_to_cpu(retbuf->ChunkBytesWritten), + bytes_written); + } else if (rc == -EINVAL) { + if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) + goto cchunk_out; + + cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", + le32_to_cpu(retbuf->ChunksWritten), + le32_to_cpu(retbuf->ChunkBytesWritten), + le32_to_cpu(retbuf->TotalBytesWritten)); + + /* + * Check if this is the first request using these sizes, + * (ie check if copy succeed once with original sizes + * and check if the server gave us different sizes after + * we already updated max sizes on previous request). + * if not then why is the server returning an error now + */ + if ((chunks_copied != 0) || chunk_sizes_updated) + goto cchunk_out; + + /* Check that server is not asking us to grow size */ + if (le32_to_cpu(retbuf->ChunkBytesWritten) < + tcon->max_bytes_chunk) + tcon->max_bytes_chunk = + le32_to_cpu(retbuf->ChunkBytesWritten); + else + goto cchunk_out; /* server gave us bogus size */ + + /* No need to change MaxChunks since already set to 1 */ + chunk_sizes_updated = true; + } else + goto cchunk_out; + } + +cchunk_out: + kfree(pcchunk); + kfree(retbuf); + if (rc) + return rc; + else + return total_bytes_written; +} + +static int +smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +static unsigned int +smb2_read_data_offset(char *buf) +{ + struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; + + return rsp->DataOffset; +} + +static unsigned int +smb2_read_data_length(char *buf, bool in_remaining) +{ + struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; + + if (in_remaining) + return le32_to_cpu(rsp->DataRemaining); + + return le32_to_cpu(rsp->DataLength); +} + + +static int +smb2_sync_read(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *bytes_read, + char **buf, int *buf_type) +{ + parms->persistent_fid = pfid->persistent_fid; + parms->volatile_fid = pfid->volatile_fid; + return SMB2_read(xid, parms, bytes_read, buf, buf_type); +} + +static int +smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *written, + struct kvec *iov, unsigned long nr_segs) +{ + + parms->persistent_fid = pfid->persistent_fid; + parms->volatile_fid = pfid->volatile_fid; + return SMB2_write(xid, parms, written, iov, nr_segs); +} + +/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ +static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) +{ + struct cifsInodeInfo *cifsi; + int rc; + + cifsi = CIFS_I(inode); + + /* if file already sparse don't bother setting sparse again */ + if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) + return true; /* already sparse */ + + if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) + return true; /* already not sparse */ + + /* + * Can't check for sparse support on share the usual way via the + * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share + * since Samba server doesn't set the flag on the share, yet + * supports the set sparse FSCTL and returns sparse correctly + * in the file attributes. If we fail setting sparse though we + * mark that server does not support sparse files for this share + * to avoid repeatedly sending the unsupported fsctl to server + * if the file is repeatedly extended. + */ + if (tcon->broken_sparse_sup) + return false; + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_SPARSE, + &setsparse, 1, CIFSMaxBufSize, NULL, NULL); + if (rc) { + tcon->broken_sparse_sup = true; + cifs_dbg(FYI, "set sparse rc = %d\n", rc); + return false; + } + + if (setsparse) + cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; + else + cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); + + return true; +} + +static int +smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, bool set_alloc) +{ + __le64 eof = cpu_to_le64(size); + struct inode *inode; + + /* + * If extending file more than one page make sparse. Many Linux fs + * make files sparse by default when extending via ftruncate + */ + inode = d_inode(cfile->dentry); + + if (!set_alloc && (size > inode->i_size + 8192)) { + __u8 set_sparse = 1; + + /* whether set sparse succeeds or not, extend the file */ + smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); + } + + return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); +} + +static int +smb2_duplicate_extents(const unsigned int xid, + struct cifsFileInfo *srcfile, + struct cifsFileInfo *trgtfile, u64 src_off, + u64 len, u64 dest_off) +{ + int rc; + unsigned int ret_data_len; + struct inode *inode; + struct duplicate_extents_to_file dup_ext_buf; + struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); + + /* server fileays advertise duplicate extent support with this flag */ + if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & + FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) + return -EOPNOTSUPP; + + dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid; + dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid; + dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); + dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); + dup_ext_buf.ByteCount = cpu_to_le64(len); + cifs_dbg(FYI, "Duplicate extents: src off %lld dst off %lld len %lld\n", + src_off, dest_off, len); + + inode = d_inode(trgtfile->dentry); + if (inode->i_size < dest_off + len) { + rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); + if (rc) + goto duplicate_extents_out; + + /* + * Although also could set plausible allocation size (i_blocks) + * here in addition to setting the file size, in reflink + * it is likely that the target file is sparse. Its allocation + * size will be queried on next revalidate, but it is important + * to make sure that file's cached size is updated immediately + */ + cifs_setsize(inode, dest_off + len); + } + rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, + trgtfile->fid.volatile_fid, + FSCTL_DUPLICATE_EXTENTS_TO_FILE, + (char *)&dup_ext_buf, + sizeof(struct duplicate_extents_to_file), + CIFSMaxBufSize, NULL, + &ret_data_len); + + if (ret_data_len > 0) + cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); + +duplicate_extents_out: + return rc; +} + +static int +smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid); +} + +static int +smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + struct fsctl_set_integrity_information_req integr_info; + unsigned int ret_data_len; + + integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); + integr_info.Flags = 0; + integr_info.Reserved = 0; + + return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_SET_INTEGRITY_INFORMATION, + (char *)&integr_info, + sizeof(struct fsctl_set_integrity_information_req), + CIFSMaxBufSize, NULL, + &ret_data_len); + +} + +/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */ +#define GMT_TOKEN_SIZE 50 + +#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */ + +/* + * Input buffer contains (empty) struct smb_snapshot array with size filled in + * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2 + */ +static int +smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, void __user *ioc_buf) +{ + char *retbuf = NULL; + unsigned int ret_data_len = 0; + int rc; + u32 max_response_size; + struct smb_snapshot_array snapshot_in; + + /* + * On the first query to enumerate the list of snapshots available + * for this volume the buffer begins with 0 (number of snapshots + * which can be returned is zero since at that point we do not know + * how big the buffer needs to be). On the second query, + * it (ret_data_len) is set to number of snapshots so we can + * know to set the maximum response size larger (see below). + */ + if (get_user(ret_data_len, (unsigned int __user *)ioc_buf)) + return -EFAULT; + + /* + * Note that for snapshot queries that servers like Azure expect that + * the first query be minimal size (and just used to get the number/size + * of previous versions) so response size must be specified as EXACTLY + * sizeof(struct snapshot_array) which is 16 when rounded up to multiple + * of eight bytes. + */ + if (ret_data_len == 0) + max_response_size = MIN_SNAPSHOT_ARRAY_SIZE; + else + max_response_size = CIFSMaxBufSize; + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_SRV_ENUMERATE_SNAPSHOTS, + NULL, 0 /* no input data */, max_response_size, + (char **)&retbuf, + &ret_data_len); + cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", + rc, ret_data_len); + if (rc) + return rc; + + if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) { + /* Fixup buffer */ + if (copy_from_user(&snapshot_in, ioc_buf, + sizeof(struct smb_snapshot_array))) { + rc = -EFAULT; + kfree(retbuf); + return rc; + } + + /* + * Check for min size, ie not large enough to fit even one GMT + * token (snapshot). On the first ioctl some users may pass in + * smaller size (or zero) to simply get the size of the array + * so the user space caller can allocate sufficient memory + * and retry the ioctl again with larger array size sufficient + * to hold all of the snapshot GMT tokens on the second try. + */ + if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) + ret_data_len = sizeof(struct smb_snapshot_array); + + /* + * We return struct SRV_SNAPSHOT_ARRAY, followed by + * the snapshot array (of 50 byte GMT tokens) each + * representing an available previous version of the data + */ + if (ret_data_len > (snapshot_in.snapshot_array_size + + sizeof(struct smb_snapshot_array))) + ret_data_len = snapshot_in.snapshot_array_size + + sizeof(struct smb_snapshot_array); + + if (copy_to_user(ioc_buf, retbuf, ret_data_len)) + rc = -EFAULT; + } + + kfree(retbuf); + return rc; +} + + + +static int +smb3_notify(const unsigned int xid, struct file *pfile, + void __user *ioc_buf, bool return_changes) +{ + struct smb3_notify_info notify; + struct smb3_notify_info __user *pnotify_buf; + struct dentry *dentry = pfile->f_path.dentry; + struct inode *inode = file_inode(pfile); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct cifs_tcon *tcon; + const unsigned char *path; + char *returned_ioctl_info = NULL; + void *page = alloc_dentry_path(); + __le16 *utf16_path = NULL; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + int rc = 0; + __u32 ret_len = 0; + + path = build_path_from_dentry(dentry, page); + if (IS_ERR(path)) { + rc = PTR_ERR(path); + goto notify_exit; + } + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (utf16_path == NULL) { + rc = -ENOMEM; + goto notify_exit; + } + + if (return_changes) { + if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify_info))) { + rc = -EFAULT; + goto notify_exit; + } + } else { + if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) { + rc = -EFAULT; + goto notify_exit; + } + notify.data_len = 0; + } + + tcon = cifs_sb_master_tcon(cifs_sb); + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, + NULL); + if (rc) + goto notify_exit; + + rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid, + notify.watch_tree, notify.completion_filter, + notify.data_len, &returned_ioctl_info, &ret_len); + + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + + cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc); + if (return_changes && (ret_len > 0) && (notify.data_len > 0)) { + if (ret_len > notify.data_len) + ret_len = notify.data_len; + pnotify_buf = (struct smb3_notify_info __user *)ioc_buf; + if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len)) + rc = -EFAULT; + else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len))) + rc = -EFAULT; + } + kfree(returned_ioctl_info); +notify_exit: + free_dentry_path(page); + kfree(utf16_path); + return rc; +} + +static int +smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, struct cifs_sb_info *cifs_sb, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + __le16 *utf16_path; + struct smb_rqst rqst[2]; + struct kvec rsp_iov[2]; + int resp_buftype[2]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qd_iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; + int rc, flags = 0; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct smb2_query_directory_rsp *qd_rsp = NULL; + struct smb2_create_rsp *op_rsp = NULL; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + int retry_count = 0; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto qdf_free; + smb2_set_next_command(tcon, &rqst[0]); + + /* Query directory */ + srch_inf->entries_in_buffer = 0; + srch_inf->index_of_last_entry = 2; + + memset(&qd_iov, 0, sizeof(qd_iov)); + rqst[1].rq_iov = qd_iov; + rqst[1].rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; + + rc = SMB2_query_directory_init(xid, tcon, server, + &rqst[1], + COMPOUND_FID, COMPOUND_FID, + 0, srch_inf->info_level); + if (rc) + goto qdf_free; + + smb2_set_related(&rqst[1]); + +again: + rc = compound_send_recv(xid, tcon->ses, server, + flags, 2, rqst, + resp_buftype, rsp_iov); + + if (rc == -EAGAIN && retry_count++ < 10) + goto again; + + /* If the open failed there is nothing to do */ + op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; + if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) { + cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc); + goto qdf_free; + } + fid->persistent_fid = op_rsp->PersistentFileId; + fid->volatile_fid = op_rsp->VolatileFileId; + + /* Anything else than ENODATA means a genuine error */ + if (rc && rc != -ENODATA) { + SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); + cifs_dbg(FYI, "query_dir_first: query directory failed rc=%d\n", rc); + trace_smb3_query_dir_err(xid, fid->persistent_fid, + tcon->tid, tcon->ses->Suid, 0, 0, rc); + goto qdf_free; + } + + atomic_inc(&tcon->num_remote_opens); + + qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; + if (qd_rsp->hdr.Status == STATUS_NO_MORE_FILES) { + trace_smb3_query_dir_done(xid, fid->persistent_fid, + tcon->tid, tcon->ses->Suid, 0, 0); + srch_inf->endOfSearch = true; + rc = 0; + goto qdf_free; + } + + rc = smb2_parse_query_directory(tcon, &rsp_iov[1], resp_buftype[1], + srch_inf); + if (rc) { + trace_smb3_query_dir_err(xid, fid->persistent_fid, tcon->tid, + tcon->ses->Suid, 0, 0, rc); + goto qdf_free; + } + resp_buftype[1] = CIFS_NO_BUFFER; + + trace_smb3_query_dir_done(xid, fid->persistent_fid, tcon->tid, + tcon->ses->Suid, 0, srch_inf->entries_in_buffer); + + qdf_free: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_query_directory_free(&rqst[1]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + return rc; +} + +static int +smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + return SMB2_query_directory(xid, tcon, fid->persistent_fid, + fid->volatile_fid, 0, srch_inf); +} + +static int +smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +/* + * If we negotiate SMB2 protocol and get STATUS_PENDING - update + * the number of credits and return true. Otherwise - return false. + */ +static bool +smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + int scredits, in_flight; + + if (shdr->Status != STATUS_PENDING) + return false; + + if (shdr->CreditRequest) { + spin_lock(&server->req_lock); + server->credits += le16_to_cpu(shdr->CreditRequest); + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + trace_smb3_pend_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + le16_to_cpu(shdr->CreditRequest), in_flight); + cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n", + __func__, le16_to_cpu(shdr->CreditRequest), scredits); + } + + return true; +} + +static bool +smb2_is_session_expired(char *buf) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED && + shdr->Status != STATUS_USER_SESSION_DELETED) + return false; + + trace_smb3_ses_expired(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), + le64_to_cpu(shdr->MessageId)); + cifs_dbg(FYI, "Session expired or deleted\n"); + + return true; +} + +static bool +smb2_is_status_io_timeout(char *buf) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + if (shdr->Status == STATUS_IO_TIMEOUT) + return true; + else + return false; +} + +static void +smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + if (shdr->Status != STATUS_NETWORK_NAME_DELETED) + return; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) { + spin_lock(&tcon->tc_lock); + tcon->need_reconnect = true; + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + pr_warn_once("Server share %s deleted.\n", + tcon->tree_name); + return; + } + } + } + spin_unlock(&cifs_tcp_ses_lock); +} + +static int +smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) +{ + if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) + return SMB2_lease_break(0, tcon, cinode->lease_key, + smb2_get_lease_state(cinode)); + + return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid, + CIFS_CACHE_READ(cinode) ? 1 : 0); +} + +void +smb2_set_related(struct smb_rqst *rqst) +{ + struct smb2_hdr *shdr; + + shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); + if (shdr == NULL) { + cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); + return; + } + shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; +} + +char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; + +void +smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) +{ + struct smb2_hdr *shdr; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = ses->server; + unsigned long len = smb_rqst_len(server, rqst); + int i, num_padding; + + shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); + if (shdr == NULL) { + cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n"); + return; + } + + /* SMB headers in a compound are 8 byte aligned. */ + + /* No padding needed */ + if (!(len & 7)) + goto finished; + + num_padding = 8 - (len & 7); + if (!smb3_encryption_required(tcon)) { + /* + * If we do not have encryption then we can just add an extra + * iov for the padding. + */ + rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; + rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; + rqst->rq_nvec++; + len += num_padding; + } else { + /* + * We can not add a small padding iov for the encryption case + * because the encryption framework can not handle the padding + * iovs. + * We have to flatten this into a single buffer and add + * the padding to it. + */ + for (i = 1; i < rqst->rq_nvec; i++) { + memcpy(rqst->rq_iov[0].iov_base + + rqst->rq_iov[0].iov_len, + rqst->rq_iov[i].iov_base, + rqst->rq_iov[i].iov_len); + rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; + } + memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, + 0, num_padding); + rqst->rq_iov[0].iov_len += num_padding; + len += num_padding; + rqst->rq_nvec = 1; + } + + finished: + shdr->NextCommand = cpu_to_le32(len); +} + +/* + * Passes the query info response back to the caller on success. + * Caller need to free this with free_rsp_buf(). + */ +int +smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, u32 desired_access, + u32 class, u32 type, u32 output_len, + struct kvec *rsp, int *buftype, + struct cifs_sb_info *cifs_sb) +{ + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + struct kvec close_iov[1]; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + int rc; + __le16 *utf16_path; + struct cached_fid *cfid = NULL; + + if (!path) + path = ""; + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + /* + * We can only call this for things we know are directories. + */ + if (!strcmp(path, "")) + open_cached_dir(xid, tcon, path, cifs_sb, false, + &cfid); /* cfid null if open dir failed */ + + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = desired_access, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto qic_exit; + smb2_set_next_command(tcon, &rqst[0]); + + memset(&qi_iov, 0, sizeof(qi_iov)); + rqst[1].rq_iov = qi_iov; + rqst[1].rq_nvec = 1; + + if (cfid) { + rc = SMB2_query_info_init(tcon, server, + &rqst[1], + cfid->fid.persistent_fid, + cfid->fid.volatile_fid, + class, type, 0, + output_len, 0, + NULL); + } else { + rc = SMB2_query_info_init(tcon, server, + &rqst[1], + COMPOUND_FID, + COMPOUND_FID, + class, type, 0, + output_len, 0, + NULL); + } + if (rc) + goto qic_exit; + if (!cfid) { + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + } + + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto qic_exit; + smb2_set_related(&rqst[2]); + + if (cfid) { + rc = compound_send_recv(xid, ses, server, + flags, 1, &rqst[1], + &resp_buftype[1], &rsp_iov[1]); + } else { + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + } + if (rc) { + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + pr_warn_once("server share %s deleted\n", + tcon->tree_name); + } + goto qic_exit; + } + *rsp = rsp_iov[1]; + *buftype = resp_buftype[1]; + + qic_exit: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_query_info_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + if (cfid) + close_cached_dir(cfid); + return rc; +} + +static int +smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct kstatfs *buf) +{ + struct smb2_query_info_rsp *rsp; + struct smb2_fs_full_size_info *info = NULL; + struct kvec rsp_iov = {NULL, 0}; + int buftype = CIFS_NO_BUFFER; + int rc; + + + rc = smb2_query_info_compound(xid, tcon, "", + FILE_READ_ATTRIBUTES, + FS_FULL_SIZE_INFORMATION, + SMB2_O_INFO_FILESYSTEM, + sizeof(struct smb2_fs_full_size_info), + &rsp_iov, &buftype, cifs_sb); + if (rc) + goto qfs_exit; + + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + buf->f_type = SMB2_SUPER_MAGIC; + info = (struct smb2_fs_full_size_info *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov, + sizeof(struct smb2_fs_full_size_info)); + if (!rc) + smb2_copy_fs_info_to_kstatfs(info, buf); + +qfs_exit: + free_rsp_buf(buftype, rsp_iov.iov_base); + return rc; +} + +static int +smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct kstatfs *buf) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + + if (!tcon->posix_extensions) + return smb2_queryfs(xid, tcon, cifs_sb, buf); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = "", + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) + return rc; + + rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, + fid.volatile_fid, buf); + buf->f_type = SMB2_SUPER_MAGIC; + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + return rc; +} + +static bool +smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) +{ + return ob1->fid.persistent_fid == ob2->fid.persistent_fid && + ob1->fid.volatile_fid == ob2->fid.volatile_fid; +} + +static int +smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u32 type, int lock, int unlock, bool wait) +{ + if (unlock && !lock) + type = SMB2_LOCKFLAG_UNLOCK; + return SMB2_lock(xid, tlink_tcon(cfile->tlink), + cfile->fid.persistent_fid, cfile->fid.volatile_fid, + current->tgid, length, offset, type, wait); +} + +static void +smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) +{ + memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); +} + +static void +smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) +{ + memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); +} + +static void +smb2_new_lease_key(struct cifs_fid *fid) +{ + generate_random_uuid(fid->lease_key); +} + +static int +smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, + const char *search_name, + struct dfs_info3_param **target_nodes, + unsigned int *num_of_nodes, + const struct nls_table *nls_codepage, int remap) +{ + int rc; + __le16 *utf16_path = NULL; + int utf16_path_len = 0; + struct cifs_tcon *tcon; + struct fsctl_get_dfs_referral_req *dfs_req = NULL; + struct get_dfs_referral_rsp *dfs_rsp = NULL; + u32 dfs_req_size = 0, dfs_rsp_size = 0; + int retry_count = 0; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); + + /* + * Try to use the IPC tcon, otherwise just use any + */ + tcon = ses->tcon_ipc; + if (tcon == NULL) { + spin_lock(&cifs_tcp_ses_lock); + tcon = list_first_entry_or_null(&ses->tcon_list, + struct cifs_tcon, + tcon_list); + if (tcon) + tcon->tc_count++; + spin_unlock(&cifs_tcp_ses_lock); + } + + if (tcon == NULL) { + cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n", + ses); + rc = -ENOTCONN; + goto out; + } + + utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX, + &utf16_path_len, + nls_codepage, remap); + if (!utf16_path) { + rc = -ENOMEM; + goto out; + } + + dfs_req_size = sizeof(*dfs_req) + utf16_path_len; + dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); + if (!dfs_req) { + rc = -ENOMEM; + goto out; + } + + /* Highest DFS referral version understood */ + dfs_req->MaxReferralLevel = DFS_VERSION; + + /* Path to resolve in an UTF-16 null-terminated string */ + memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); + + do { + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, + FSCTL_DFS_GET_REFERRALS, + (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, + (char **)&dfs_rsp, &dfs_rsp_size); + if (!is_retryable_error(rc)) + break; + usleep_range(512, 2048); + } while (++retry_count < 5); + + if (rc) { + if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) + cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); + goto out; + } + + rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size, + num_of_nodes, target_nodes, + nls_codepage, remap, search_name, + true /* is_unicode */); + if (rc) { + cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); + goto out; + } + + out: + if (tcon && !tcon->ipc) { + /* ipc tcons are not refcounted */ + spin_lock(&cifs_tcp_ses_lock); + tcon->tc_count--; + /* tc_count can never go negative */ + WARN_ON(tcon->tc_count < 0); + spin_unlock(&cifs_tcp_ses_lock); + } + kfree(utf16_path); + kfree(dfs_req); + kfree(dfs_rsp); + return rc; +} + +static int +parse_reparse_posix(struct reparse_posix_data *symlink_buf, + u32 plen, char **target_path, + struct cifs_sb_info *cifs_sb) +{ + unsigned int len; + + /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ + len = le16_to_cpu(symlink_buf->ReparseDataLength); + + if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) { + cifs_dbg(VFS, "%lld not a supported symlink type\n", + le64_to_cpu(symlink_buf->InodeType)); + return -EOPNOTSUPP; + } + + *target_path = cifs_strndup_from_utf16( + symlink_buf->PathBuffer, + len, true, cifs_sb->local_nls); + if (!(*target_path)) + return -ENOMEM; + + convert_delimiter(*target_path, '/'); + cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + + return 0; +} + +static int +parse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf, + u32 plen, char **target_path, + struct cifs_sb_info *cifs_sb) +{ + unsigned int sub_len; + unsigned int sub_offset; + + /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ + + sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset); + sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength); + if (sub_offset + 20 > plen || + sub_offset + sub_len + 20 > plen) { + cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); + return -EIO; + } + + *target_path = cifs_strndup_from_utf16( + symlink_buf->PathBuffer + sub_offset, + sub_len, true, cifs_sb->local_nls); + if (!(*target_path)) + return -ENOMEM; + + convert_delimiter(*target_path, '/'); + cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + + return 0; +} + +static int +parse_reparse_point(struct reparse_data_buffer *buf, + u32 plen, char **target_path, + struct cifs_sb_info *cifs_sb) +{ + if (plen < sizeof(struct reparse_data_buffer)) { + cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n", + plen); + return -EIO; + } + + if (plen < le16_to_cpu(buf->ReparseDataLength) + + sizeof(struct reparse_data_buffer)) { + cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n", + plen); + return -EIO; + } + + /* See MS-FSCC 2.1.2 */ + switch (le32_to_cpu(buf->ReparseTag)) { + case IO_REPARSE_TAG_NFS: + return parse_reparse_posix( + (struct reparse_posix_data *)buf, + plen, target_path, cifs_sb); + case IO_REPARSE_TAG_SYMLINK: + return parse_reparse_symlink( + (struct reparse_symlink_data_buffer *)buf, + plen, target_path, cifs_sb); + default: + cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n", + le32_to_cpu(buf->ReparseTag)); + return -EOPNOTSUPP; + } +} + +static int +smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + char **target_path, bool is_reparse_point) +{ + int rc; + __le16 *utf16_path = NULL; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct kvec err_iov = {NULL, 0}; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec close_iov[1]; + struct smb2_create_rsp *create_rsp; + struct smb2_ioctl_rsp *ioctl_rsp; + struct reparse_data_buffer *reparse_buf; + int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0; + u32 plen; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + + *target_path = NULL; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, create_options), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto querty_exit; + smb2_set_next_command(tcon, &rqst[0]); + + + /* IOCTL */ + memset(&io_iov, 0, sizeof(io_iov)); + rqst[1].rq_iov = io_iov; + rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, + &rqst[1], fid.persistent_fid, + fid.volatile_fid, FSCTL_GET_REPARSE_POINT, NULL, 0, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE); + if (rc) + goto querty_exit; + + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto querty_exit; + + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, tcon->ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + + create_rsp = rsp_iov[0].iov_base; + if (create_rsp && create_rsp->hdr.Status) + err_iov = rsp_iov[0]; + ioctl_rsp = rsp_iov[1].iov_base; + + /* + * Open was successful and we got an ioctl response. + */ + if ((rc == 0) && (is_reparse_point)) { + /* See MS-FSCC 2.3.23 */ + + reparse_buf = (struct reparse_data_buffer *) + ((char *)ioctl_rsp + + le32_to_cpu(ioctl_rsp->OutputOffset)); + plen = le32_to_cpu(ioctl_rsp->OutputCount); + + if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > + rsp_iov[1].iov_len) { + cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n", + plen); + rc = -EIO; + goto querty_exit; + } + + rc = parse_reparse_point(reparse_buf, plen, target_path, + cifs_sb); + goto querty_exit; + } + + if (!rc || !err_iov.iov_base) { + rc = -ENOENT; + goto querty_exit; + } + + rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path); + + querty_exit: + cifs_dbg(FYI, "query symlink rc %d\n", rc); + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_ioctl_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + return rc; +} + +int +smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + __u32 *tag) +{ + int rc; + __le16 *utf16_path = NULL; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec close_iov[1]; + struct smb2_ioctl_rsp *ioctl_rsp; + struct reparse_data_buffer *reparse_buf; + u32 plen; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + /* + * setup smb2open - TODO add optimization to call cifs_get_readable_path + * to see if there is a handle already open that we can use + */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto query_rp_exit; + smb2_set_next_command(tcon, &rqst[0]); + + + /* IOCTL */ + memset(&io_iov, 0, sizeof(io_iov)); + rqst[1].rq_iov = io_iov; + rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE); + if (rc) + goto query_rp_exit; + + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto query_rp_exit; + + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, tcon->ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + + ioctl_rsp = rsp_iov[1].iov_base; + + /* + * Open was successful and we got an ioctl response. + */ + if (rc == 0) { + /* See MS-FSCC 2.3.23 */ + + reparse_buf = (struct reparse_data_buffer *) + ((char *)ioctl_rsp + + le32_to_cpu(ioctl_rsp->OutputOffset)); + plen = le32_to_cpu(ioctl_rsp->OutputCount); + + if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > + rsp_iov[1].iov_len) { + cifs_tcon_dbg(FYI, "srv returned invalid ioctl len: %d\n", + plen); + rc = -EIO; + goto query_rp_exit; + } + *tag = le32_to_cpu(reparse_buf->ReparseTag); + } + + query_rp_exit: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_ioctl_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + return rc; +} + +static struct cifs_ntsd * +get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, + const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + unsigned int xid; + int rc = -EOPNOTSUPP; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + xid = get_xid(); + cifs_dbg(FYI, "trying to get acl\n"); + + rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid, + cifsfid->volatile_fid, (void **)&pntsd, pacllen, + info); + free_xid(xid); + + cifs_put_tlink(tlink); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; + +} + +static struct cifs_ntsd * +get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, + const char *path, u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + unsigned int xid; + int rc; + struct cifs_tcon *tcon; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + __le16 *utf16_path; + + cifs_dbg(FYI, "get smb3 acl for path %s\n", path); + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + free_xid(xid); + return ERR_PTR(rc); + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = READ_CONTROL, + .disposition = FILE_OPEN, + /* + * When querying an ACL, even if the file is a symlink + * we want to open the source not the target, and so + * the protocol requires that the client specify this + * flag when opening a reparse point + */ + .create_options = cifs_create_options(cifs_sb, 0) | + OPEN_REPARSE_POINT, + .fid = &fid, + }; + + if (info & SACL_SECINFO) + oparms.desired_access |= SYSTEM_SECURITY; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, + NULL); + kfree(utf16_path); + if (!rc) { + rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, + fid.volatile_fid, (void **)&pntsd, pacllen, + info); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + } + + cifs_put_tlink(tlink); + free_xid(xid); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; +} + +static int +set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, + struct inode *inode, const char *path, int aclflag) +{ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + unsigned int xid; + int rc, access_flags = 0; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + __le16 *utf16_path; + + cifs_dbg(FYI, "set smb3 acl for path %s\n", path); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP) + access_flags |= WRITE_OWNER; + if (aclflag & CIFS_ACL_SACL) + access_flags |= SYSTEM_SECURITY; + if (aclflag & CIFS_ACL_DACL) + access_flags |= WRITE_DAC; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + free_xid(xid); + return rc; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .desired_access = access_flags, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, + NULL, NULL); + kfree(utf16_path); + if (!rc) { + rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, + fid.volatile_fid, pnntsd, acllen, aclflag); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + } + + cifs_put_tlink(tlink); + free_xid(xid); + return rc; +} + +/* Retrieve an ACL from the server */ +static struct cifs_ntsd * +get_smb2_acl(struct cifs_sb_info *cifs_sb, + struct inode *inode, const char *path, + u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + struct cifsFileInfo *open_file = NULL; + + if (inode && !(info & SACL_SECINFO)) + open_file = find_readable_file(CIFS_I(inode), true); + if (!open_file || (info & SACL_SECINFO)) + return get_smb2_acl_by_path(cifs_sb, path, pacllen, info); + + pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); + cifsFileInfo_put(open_file); + return pntsd; +} + +static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len, unsigned int xid) +{ + struct cifsFileInfo *cfile = file->private_data; + struct file_zero_data_information fsctl_buf; + + cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); + + fsctl_buf.FileOffset = cpu_to_le64(offset); + fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); + + return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, + (char *)&fsctl_buf, + sizeof(struct file_zero_data_information), + 0, NULL, NULL); +} + +static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len, bool keep_size) +{ + struct cifs_ses *ses = tcon->ses; + struct inode *inode = file_inode(file); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifsFileInfo *cfile = file->private_data; + long rc; + unsigned int xid; + __le64 eof; + + xid = get_xid(); + + trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, + ses->Suid, offset, len); + + inode_lock(inode); + filemap_invalidate_lock(inode->i_mapping); + + /* + * We zero the range through ioctl, so we need remove the page caches + * first, otherwise the data may be inconsistent with the server. + */ + truncate_pagecache_range(inode, offset, offset + len - 1); + + /* if file not oplocked can't be sure whether asking to extend size */ + rc = -EOPNOTSUPP; + if (keep_size == false && !CIFS_CACHE_READ(cifsi)) + goto zero_range_exit; + + rc = smb3_zero_data(file, tcon, offset, len, xid); + if (rc < 0) + goto zero_range_exit; + + /* + * do we also need to change the size of the file? + */ + if (keep_size == false && i_size_read(inode) < offset + len) { + eof = cpu_to_le64(offset + len); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + } + + zero_range_exit: + filemap_invalidate_unlock(inode->i_mapping); + inode_unlock(inode); + free_xid(xid); + if (rc) + trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, + ses->Suid, offset, len, rc); + else + trace_smb3_zero_done(xid, cfile->fid.persistent_fid, tcon->tid, + ses->Suid, offset, len); + return rc; +} + +static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len) +{ + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + struct file_zero_data_information fsctl_buf; + long rc; + unsigned int xid; + __u8 set_sparse = 1; + + xid = get_xid(); + + inode_lock(inode); + /* Need to make file sparse, if not already, before freeing range. */ + /* Consider adding equivalent for compressed since it could also work */ + if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { + rc = -EOPNOTSUPP; + goto out; + } + + filemap_invalidate_lock(inode->i_mapping); + /* + * We implement the punch hole through ioctl, so we need remove the page + * caches first, otherwise the data may be inconsistent with the server. + */ + truncate_pagecache_range(inode, offset, offset + len - 1); + + cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); + + fsctl_buf.FileOffset = cpu_to_le64(offset); + fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, + (char *)&fsctl_buf, + sizeof(struct file_zero_data_information), + CIFSMaxBufSize, NULL, NULL); + filemap_invalidate_unlock(inode->i_mapping); +out: + inode_unlock(inode); + free_xid(xid); + return rc; +} + +static int smb3_simple_fallocate_write_range(unsigned int xid, + struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + loff_t off, loff_t len, + char *buf) +{ + struct cifs_io_parms io_parms = {0}; + int nbytes; + int rc = 0; + struct kvec iov[2]; + + io_parms.netfid = cfile->fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.persistent_fid = cfile->fid.persistent_fid; + io_parms.volatile_fid = cfile->fid.volatile_fid; + + while (len) { + io_parms.offset = off; + io_parms.length = len; + if (io_parms.length > SMB2_MAX_BUFFER_SIZE) + io_parms.length = SMB2_MAX_BUFFER_SIZE; + /* iov[0] is reserved for smb header */ + iov[1].iov_base = buf; + iov[1].iov_len = io_parms.length; + rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1); + if (rc) + break; + if (nbytes > len) + return -EINVAL; + buf += nbytes; + off += nbytes; + len -= nbytes; + } + return rc; +} + +static int smb3_simple_fallocate_range(unsigned int xid, + struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + loff_t off, loff_t len) +{ + struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; + u32 out_data_len; + char *buf = NULL; + loff_t l; + int rc; + + in_data.file_offset = cpu_to_le64(off); + in_data.length = cpu_to_le64(len); + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_QUERY_ALLOCATED_RANGES, + (char *)&in_data, sizeof(in_data), + 1024 * sizeof(struct file_allocated_range_buffer), + (char **)&out_data, &out_data_len); + if (rc) + goto out; + + buf = kzalloc(1024 * 1024, GFP_KERNEL); + if (buf == NULL) { + rc = -ENOMEM; + goto out; + } + + tmp_data = out_data; + while (len) { + /* + * The rest of the region is unmapped so write it all. + */ + if (out_data_len == 0) { + rc = smb3_simple_fallocate_write_range(xid, tcon, + cfile, off, len, buf); + goto out; + } + + if (out_data_len < sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto out; + } + + if (off < le64_to_cpu(tmp_data->file_offset)) { + /* + * We are at a hole. Write until the end of the region + * or until the next allocated data, + * whichever comes next. + */ + l = le64_to_cpu(tmp_data->file_offset) - off; + if (len < l) + l = len; + rc = smb3_simple_fallocate_write_range(xid, tcon, + cfile, off, l, buf); + if (rc) + goto out; + off = off + l; + len = len - l; + if (len == 0) + goto out; + } + /* + * We are at a section of allocated data, just skip forward + * until the end of the data or the end of the region + * we are supposed to fallocate, whichever comes first. + */ + l = le64_to_cpu(tmp_data->length); + if (len < l) + l = len; + off += l; + len -= l; + + tmp_data = &tmp_data[1]; + out_data_len -= sizeof(struct file_allocated_range_buffer); + } + + out: + kfree(out_data); + kfree(buf); + return rc; +} + + +static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, + loff_t off, loff_t len, bool keep_size) +{ + struct inode *inode; + struct cifsInodeInfo *cifsi; + struct cifsFileInfo *cfile = file->private_data; + long rc = -EOPNOTSUPP; + unsigned int xid; + __le64 eof; + + xid = get_xid(); + + inode = d_inode(cfile->dentry); + cifsi = CIFS_I(inode); + + trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, + tcon->ses->Suid, off, len); + /* if file not oplocked can't be sure whether asking to extend size */ + if (!CIFS_CACHE_READ(cifsi)) + if (keep_size == false) { + trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, off, len, rc); + free_xid(xid); + return rc; + } + + /* + * Extending the file + */ + if ((keep_size == false) && i_size_read(inode) < off + len) { + rc = inode_newsize_ok(inode, off + len); + if (rc) + goto out; + + if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) + smb2_set_sparse(xid, tcon, cfile, inode, false); + + eof = cpu_to_le64(off + len); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + if (rc == 0) { + cifsi->server_eof = off + len; + cifs_setsize(inode, off + len); + cifs_truncate_page(inode->i_mapping, inode->i_size); + truncate_setsize(inode, off + len); + } + goto out; + } + + /* + * Files are non-sparse by default so falloc may be a no-op + * Must check if file sparse. If not sparse, and since we are not + * extending then no need to do anything since file already allocated + */ + if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { + rc = 0; + goto out; + } + + if (keep_size == true) { + /* + * We can not preallocate pages beyond the end of the file + * in SMB2 + */ + if (off >= i_size_read(inode)) { + rc = 0; + goto out; + } + /* + * For fallocates that are partially beyond the end of file, + * clamp len so we only fallocate up to the end of file. + */ + if (off + len > i_size_read(inode)) { + len = i_size_read(inode) - off; + } + } + + if ((keep_size == true) || (i_size_read(inode) >= off + len)) { + /* + * At this point, we are trying to fallocate an internal + * regions of a sparse file. Since smb2 does not have a + * fallocate command we have two otions on how to emulate this. + * We can either turn the entire file to become non-sparse + * which we only do if the fallocate is for virtually + * the whole file, or we can overwrite the region with zeroes + * using SMB2_write, which could be prohibitevly expensive + * if len is large. + */ + /* + * We are only trying to fallocate a small region so + * just write it with zero. + */ + if (len <= 1024 * 1024) { + rc = smb3_simple_fallocate_range(xid, tcon, cfile, + off, len); + goto out; + } + + /* + * Check if falloc starts within first few pages of file + * and ends within a few pages of the end of file to + * ensure that most of file is being forced to be + * fallocated now. If so then setting whole file sparse + * ie potentially making a few extra pages at the beginning + * or end of the file non-sparse via set_sparse is harmless. + */ + if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { + rc = -EOPNOTSUPP; + goto out; + } + } + + smb2_set_sparse(xid, tcon, cfile, inode, false); + rc = 0; + +out: + if (rc) + trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, + tcon->ses->Suid, off, len, rc); + else + trace_smb3_falloc_done(xid, cfile->fid.persistent_fid, tcon->tid, + tcon->ses->Suid, off, len); + + free_xid(xid); + return rc; +} + +static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, + loff_t off, loff_t len) +{ + int rc; + unsigned int xid; + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + __le64 eof; + loff_t old_eof; + + xid = get_xid(); + + inode_lock(inode); + + old_eof = i_size_read(inode); + if ((off >= old_eof) || + off + len >= old_eof) { + rc = -EINVAL; + goto out; + } + + filemap_invalidate_lock(inode->i_mapping); + rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1); + if (rc < 0) + goto out_2; + + truncate_pagecache_range(inode, off, old_eof); + + rc = smb2_copychunk_range(xid, cfile, cfile, off + len, + old_eof - off - len, off); + if (rc < 0) + goto out_2; + + eof = cpu_to_le64(old_eof - len); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + if (rc < 0) + goto out_2; + + rc = 0; + + cifsi->server_eof = i_size_read(inode) - len; + truncate_setsize(inode, cifsi->server_eof); + fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof); +out_2: + filemap_invalidate_unlock(inode->i_mapping); + out: + inode_unlock(inode); + free_xid(xid); + return rc; +} + +static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, + loff_t off, loff_t len) +{ + int rc; + unsigned int xid; + struct cifsFileInfo *cfile = file->private_data; + struct inode *inode = file_inode(file); + __le64 eof; + __u64 count, old_eof; + + xid = get_xid(); + + inode_lock(inode); + + old_eof = i_size_read(inode); + if (off >= old_eof) { + rc = -EINVAL; + goto out; + } + + count = old_eof - off; + eof = cpu_to_le64(old_eof + len); + + filemap_invalidate_lock(inode->i_mapping); + rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1); + if (rc < 0) + goto out_2; + truncate_pagecache_range(inode, off, old_eof); + + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + if (rc < 0) + goto out_2; + + rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); + if (rc < 0) + goto out_2; + + rc = smb3_zero_data(file, tcon, off, len, xid); + if (rc < 0) + goto out_2; + + rc = 0; +out_2: + filemap_invalidate_unlock(inode->i_mapping); + out: + inode_unlock(inode); + free_xid(xid); + return rc; +} + +static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence) +{ + struct cifsFileInfo *wrcfile, *cfile = file->private_data; + struct cifsInodeInfo *cifsi; + struct inode *inode; + int rc = 0; + struct file_allocated_range_buffer in_data, *out_data = NULL; + u32 out_data_len; + unsigned int xid; + + if (whence != SEEK_HOLE && whence != SEEK_DATA) + return generic_file_llseek(file, offset, whence); + + inode = d_inode(cfile->dentry); + cifsi = CIFS_I(inode); + + if (offset < 0 || offset >= i_size_read(inode)) + return -ENXIO; + + xid = get_xid(); + /* + * We need to be sure that all dirty pages are written as they + * might fill holes on the server. + * Note that we also MUST flush any written pages since at least + * some servers (Windows2016) will not reflect recent writes in + * QUERY_ALLOCATED_RANGES until SMB2_flush is called. + */ + wrcfile = find_writable_file(cifsi, FIND_WR_ANY); + if (wrcfile) { + filemap_write_and_wait(inode->i_mapping); + smb2_flush_file(xid, tcon, &wrcfile->fid); + cifsFileInfo_put(wrcfile); + } + + if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) { + if (whence == SEEK_HOLE) + offset = i_size_read(inode); + goto lseek_exit; + } + + in_data.file_offset = cpu_to_le64(offset); + in_data.length = cpu_to_le64(i_size_read(inode)); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_QUERY_ALLOCATED_RANGES, + (char *)&in_data, sizeof(in_data), + sizeof(struct file_allocated_range_buffer), + (char **)&out_data, &out_data_len); + if (rc == -E2BIG) + rc = 0; + if (rc) + goto lseek_exit; + + if (whence == SEEK_HOLE && out_data_len == 0) + goto lseek_exit; + + if (whence == SEEK_DATA && out_data_len == 0) { + rc = -ENXIO; + goto lseek_exit; + } + + if (out_data_len < sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto lseek_exit; + } + if (whence == SEEK_DATA) { + offset = le64_to_cpu(out_data->file_offset); + goto lseek_exit; + } + if (offset < le64_to_cpu(out_data->file_offset)) + goto lseek_exit; + + offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length); + + lseek_exit: + free_xid(xid); + kfree(out_data); + if (!rc) + return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); + else + return rc; +} + +static int smb3_fiemap(struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + struct fiemap_extent_info *fei, u64 start, u64 len) +{ + unsigned int xid; + struct file_allocated_range_buffer in_data, *out_data; + u32 out_data_len; + int i, num, rc, flags, last_blob; + u64 next; + + rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len, 0); + if (rc) + return rc; + + xid = get_xid(); + again: + in_data.file_offset = cpu_to_le64(start); + in_data.length = cpu_to_le64(len); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_QUERY_ALLOCATED_RANGES, + (char *)&in_data, sizeof(in_data), + 1024 * sizeof(struct file_allocated_range_buffer), + (char **)&out_data, &out_data_len); + if (rc == -E2BIG) { + last_blob = 0; + rc = 0; + } else + last_blob = 1; + if (rc) + goto out; + + if (out_data_len && out_data_len < sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto out; + } + if (out_data_len % sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto out; + } + + num = out_data_len / sizeof(struct file_allocated_range_buffer); + for (i = 0; i < num; i++) { + flags = 0; + if (i == num - 1 && last_blob) + flags |= FIEMAP_EXTENT_LAST; + + rc = fiemap_fill_next_extent(fei, + le64_to_cpu(out_data[i].file_offset), + le64_to_cpu(out_data[i].file_offset), + le64_to_cpu(out_data[i].length), + flags); + if (rc < 0) + goto out; + if (rc == 1) { + rc = 0; + goto out; + } + } + + if (!last_blob) { + next = le64_to_cpu(out_data[num - 1].file_offset) + + le64_to_cpu(out_data[num - 1].length); + len = len - (next - start); + start = next; + goto again; + } + + out: + free_xid(xid); + kfree(out_data); + return rc; +} + +static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, + loff_t off, loff_t len) +{ + /* KEEP_SIZE already checked for by do_fallocate */ + if (mode & FALLOC_FL_PUNCH_HOLE) + return smb3_punch_hole(file, tcon, off, len); + else if (mode & FALLOC_FL_ZERO_RANGE) { + if (mode & FALLOC_FL_KEEP_SIZE) + return smb3_zero_range(file, tcon, off, len, true); + return smb3_zero_range(file, tcon, off, len, false); + } else if (mode == FALLOC_FL_KEEP_SIZE) + return smb3_simple_falloc(file, tcon, off, len, true); + else if (mode == FALLOC_FL_COLLAPSE_RANGE) + return smb3_collapse_range(file, tcon, off, len); + else if (mode == FALLOC_FL_INSERT_RANGE) + return smb3_insert_range(file, tcon, off, len); + else if (mode == 0) + return smb3_simple_falloc(file, tcon, off, len, false); + + return -EOPNOTSUPP; +} + +static void +smb2_downgrade_oplock(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + server->ops->set_oplock_level(cinode, oplock, 0, NULL); +} + +static void +smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache); + +static void +smb3_downgrade_oplock(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + unsigned int old_state = cinode->oplock; + unsigned int old_epoch = cinode->epoch; + unsigned int new_state; + + if (epoch > old_epoch) { + smb21_set_oplock_level(cinode, oplock, 0, NULL); + cinode->epoch = epoch; + } + + new_state = cinode->oplock; + *purge_cache = false; + + if ((old_state & CIFS_CACHE_READ_FLG) != 0 && + (new_state & CIFS_CACHE_READ_FLG) == 0) + *purge_cache = true; + else if (old_state == new_state && (epoch - old_epoch > 1)) + *purge_cache = true; +} + +static void +smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + oplock &= 0xFF; + cinode->lease_granted = false; + if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) + return; + if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { + cinode->oplock = CIFS_CACHE_RHW_FLG; + cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + cinode->oplock = CIFS_CACHE_RW_FLG; + cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else if (oplock == SMB2_OPLOCK_LEVEL_II) { + cinode->oplock = CIFS_CACHE_READ_FLG; + cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else + cinode->oplock = 0; +} + +static void +smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + char message[5] = {0}; + unsigned int new_oplock = 0; + + oplock &= 0xFF; + cinode->lease_granted = true; + if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) + return; + + /* Check if the server granted an oplock rather than a lease */ + if (oplock & SMB2_OPLOCK_LEVEL_EXCLUSIVE) + return smb2_set_oplock_level(cinode, oplock, epoch, + purge_cache); + + if (oplock & SMB2_LEASE_READ_CACHING_HE) { + new_oplock |= CIFS_CACHE_READ_FLG; + strcat(message, "R"); + } + if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { + new_oplock |= CIFS_CACHE_HANDLE_FLG; + strcat(message, "H"); + } + if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { + new_oplock |= CIFS_CACHE_WRITE_FLG; + strcat(message, "W"); + } + if (!new_oplock) + strncpy(message, "None", sizeof(message)); + + cinode->oplock = new_oplock; + cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, + &cinode->netfs.inode); +} + +static void +smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + unsigned int old_oplock = cinode->oplock; + + smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); + + if (purge_cache) { + *purge_cache = false; + if (old_oplock == CIFS_CACHE_READ_FLG) { + if (cinode->oplock == CIFS_CACHE_READ_FLG && + (epoch - cinode->epoch > 0)) + *purge_cache = true; + else if (cinode->oplock == CIFS_CACHE_RH_FLG && + (epoch - cinode->epoch > 1)) + *purge_cache = true; + else if (cinode->oplock == CIFS_CACHE_RHW_FLG && + (epoch - cinode->epoch > 1)) + *purge_cache = true; + else if (cinode->oplock == 0 && + (epoch - cinode->epoch > 0)) + *purge_cache = true; + } else if (old_oplock == CIFS_CACHE_RH_FLG) { + if (cinode->oplock == CIFS_CACHE_RH_FLG && + (epoch - cinode->epoch > 0)) + *purge_cache = true; + else if (cinode->oplock == CIFS_CACHE_RHW_FLG && + (epoch - cinode->epoch > 1)) + *purge_cache = true; + } + cinode->epoch = epoch; + } +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static bool +smb2_is_read_op(__u32 oplock) +{ + return oplock == SMB2_OPLOCK_LEVEL_II; +} +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ + +static bool +smb21_is_read_op(__u32 oplock) +{ + return (oplock & SMB2_LEASE_READ_CACHING_HE) && + !(oplock & SMB2_LEASE_WRITE_CACHING_HE); +} + +static __le32 +map_oplock_to_lease(u8 oplock) +{ + if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; + else if (oplock == SMB2_OPLOCK_LEVEL_II) + return SMB2_LEASE_READ_CACHING_LE; + else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) + return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE; + return 0; +} + +static char * +smb2_create_lease_buf(u8 *lease_key, u8 oplock) +{ + struct create_lease *buf; + + buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); + if (!buf) + return NULL; + + memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseState = map_oplock_to_lease(oplock); + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + return (char *)buf; +} + +static char * +smb3_create_lease_buf(u8 *lease_key, u8 oplock) +{ + struct create_lease_v2 *buf; + + buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); + if (!buf) + return NULL; + + memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseState = map_oplock_to_lease(oplock); + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + return (char *)buf; +} + +static __u8 +smb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) +{ + struct create_lease *lc = (struct create_lease *)buf; + + *epoch = 0; /* not used */ + if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) + return SMB2_OPLOCK_LEVEL_NOCHANGE; + return le32_to_cpu(lc->lcontext.LeaseState); +} + +static __u8 +smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) +{ + struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; + + *epoch = le16_to_cpu(lc->lcontext.Epoch); + if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) + return SMB2_OPLOCK_LEVEL_NOCHANGE; + if (lease_key) + memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + return le32_to_cpu(lc->lcontext.LeaseState); +} + +static unsigned int +smb2_wp_retry_size(struct inode *inode) +{ + return min_t(unsigned int, CIFS_SB(inode->i_sb)->ctx->wsize, + SMB2_MAX_BUFFER_SIZE); +} + +static bool +smb2_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->invalidHandle; +} + +static void +fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, + struct smb_rqst *old_rq, __le16 cipher_type) +{ + struct smb2_hdr *shdr = + (struct smb2_hdr *)old_rq->rq_iov[0].iov_base; + + memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; + tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); + tr_hdr->Flags = cpu_to_le16(0x01); + if ((cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8); +} + +static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst, + int num_rqst, const u8 *sig, u8 **iv, + struct aead_request **req, struct scatterlist **sgl, + unsigned int *num_sgs) +{ + unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); + unsigned int iv_size = crypto_aead_ivsize(tfm); + unsigned int len; + u8 *p; + + *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); + + len = iv_size; + len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); + len = ALIGN(len, crypto_tfm_ctx_alignment()); + len += req_size; + len = ALIGN(len, __alignof__(struct scatterlist)); + len += *num_sgs * sizeof(**sgl); + + p = kmalloc(len, GFP_ATOMIC); + if (!p) + return NULL; + + *iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1); + *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size, + crypto_tfm_ctx_alignment()); + *sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size, + __alignof__(struct scatterlist)); + return p; +} + +static void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst, + int num_rqst, const u8 *sig, u8 **iv, + struct aead_request **req, struct scatterlist **sgl) +{ + unsigned int off, len, skip; + struct scatterlist *sg; + unsigned int num_sgs; + unsigned long addr; + int i, j; + void *p; + + p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs); + if (!p) + return NULL; + + sg_init_table(*sgl, num_sgs); + sg = *sgl; + + /* Assumes the first rqst has a transform header as the first iov. + * I.e. + * rqst[0].rq_iov[0] is transform header + * rqst[0].rq_iov[1+] data to be encrypted/decrypted + * rqst[1+].rq_iov[0+] data to be encrypted/decrypted + */ + for (i = 0; i < num_rqst; i++) { + /* + * The first rqst has a transform header where the + * first 20 bytes are not part of the encrypted blob. + */ + for (j = 0; j < rqst[i].rq_nvec; j++) { + struct kvec *iov = &rqst[i].rq_iov[j]; + + skip = (i == 0) && (j == 0) ? 20 : 0; + addr = (unsigned long)iov->iov_base + skip; + len = iov->iov_len - skip; + sg = cifs_sg_set_buf(sg, (void *)addr, len); + } + for (j = 0; j < rqst[i].rq_npages; j++) { + rqst_page_get_length(&rqst[i], j, &len, &off); + sg_set_page(sg++, rqst[i].rq_pages[j], len, off); + } + } + cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE); + + return p; +} + +static int +smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + u8 *ses_enc_key; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + if (ses->Suid == ses_id) { + spin_lock(&ses->ses_lock); + ses_enc_key = enc ? ses->smb3encryptionkey : + ses->smb3decryptionkey; + memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + spin_unlock(&ses->ses_lock); + spin_unlock(&cifs_tcp_ses_lock); + return 0; + } + } + spin_unlock(&cifs_tcp_ses_lock); + + return -EAGAIN; +} +/* + * Encrypt or decrypt @rqst message. @rqst[0] has the following format: + * iov[0] - transform header (associate data), + * iov[1-N] - SMB2 header and pages - data to encrypt. + * On success return encrypted data in iov[1-N] and pages, leave iov[0] + * untouched. + */ +static int +crypt_message(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *rqst, int enc) +{ + struct smb2_transform_hdr *tr_hdr = + (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int rc = 0; + struct scatterlist *sg; + u8 sign[SMB2_SIGNATURE_SIZE] = {}; + u8 key[SMB3_ENC_DEC_KEY_SIZE]; + struct aead_request *req; + u8 *iv; + DECLARE_CRYPTO_WAIT(wait); + struct crypto_aead *tfm; + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + void *creq; + + rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__, + enc ? "en" : "de"); + return rc; + } + + rc = smb3_crypto_aead_allocate(server); + if (rc) { + cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); + return rc; + } + + tfm = enc ? server->secmech.enc : server->secmech.dec; + + if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); + else + rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); + + if (rc) { + cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); + return rc; + } + + rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); + if (rc) { + cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); + return rc; + } + + creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg); + if (unlikely(!creq)) + return -ENOMEM; + + if (!enc) { + memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); + crypt_len += SMB2_SIGNATURE_SIZE; + } + + if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else { + iv[0] = 3; + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + } + + aead_request_set_tfm(req, tfm); + aead_request_set_crypt(req, sg, sg, crypt_len, iv); + aead_request_set_ad(req, assoc_data_len); + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); + + rc = crypto_wait_req(enc ? crypto_aead_encrypt(req) + : crypto_aead_decrypt(req), &wait); + + if (!rc && enc) + memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); + + kfree_sensitive(creq); + return rc; +} + +void +smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst) +{ + int i, j; + + for (i = 0; i < num_rqst; i++) { + if (rqst[i].rq_pages) { + for (j = rqst[i].rq_npages - 1; j >= 0; j--) + put_page(rqst[i].rq_pages[j]); + kfree(rqst[i].rq_pages); + } + } +} + +/* + * This function will initialize new_rq and encrypt the content. + * The first entry, new_rq[0], only contains a single iov which contains + * a smb2_transform_hdr and is pre-allocated by the caller. + * This function then populates new_rq[1+] with the content from olq_rq[0+]. + * + * The end result is an array of smb_rqst structures where the first structure + * only contains a single iov for the transform header which we then can pass + * to crypt_message(). + * + * new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller + * new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests + */ +static int +smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *new_rq, struct smb_rqst *old_rq) +{ + struct page **pages; + struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base; + unsigned int npages; + unsigned int orig_len = 0; + int i, j; + int rc = -ENOMEM; + + for (i = 1; i < num_rqst; i++) { + npages = old_rq[i - 1].rq_npages; + pages = kmalloc_array(npages, sizeof(struct page *), + GFP_KERNEL); + if (!pages) + goto err_free; + + new_rq[i].rq_pages = pages; + new_rq[i].rq_npages = npages; + new_rq[i].rq_offset = old_rq[i - 1].rq_offset; + new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz; + new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz; + new_rq[i].rq_iov = old_rq[i - 1].rq_iov; + new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec; + + orig_len += smb_rqst_len(server, &old_rq[i - 1]); + + for (j = 0; j < npages; j++) { + pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!pages[j]) + goto err_free; + } + + /* copy pages form the old */ + for (j = 0; j < npages; j++) { + char *dst, *src; + unsigned int offset, len; + + rqst_page_get_length(&new_rq[i], j, &len, &offset); + + dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset; + src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset; + + memcpy(dst, src, len); + kunmap(new_rq[i].rq_pages[j]); + kunmap(old_rq[i - 1].rq_pages[j]); + } + } + + /* fill the 1st iov with a transform header */ + fill_transform_hdr(tr_hdr, orig_len, old_rq, server->cipher_type); + + rc = crypt_message(server, num_rqst, new_rq, 1); + cifs_dbg(FYI, "Encrypt message returned %d\n", rc); + if (rc) + goto err_free; + + return rc; + +err_free: + smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]); + return rc; +} + +static int +smb3_is_transform_hdr(void *buf) +{ + struct smb2_transform_hdr *trhdr = buf; + + return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; +} + +static int +decrypt_raw_data(struct TCP_Server_Info *server, char *buf, + unsigned int buf_data_size, struct page **pages, + unsigned int npages, unsigned int page_data_size, + bool is_offloaded) +{ + struct kvec iov[2]; + struct smb_rqst rqst = {NULL}; + int rc; + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr); + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); + iov[1].iov_len = buf_data_size; + + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + rqst.rq_pages = pages; + rqst.rq_npages = npages; + rqst.rq_pagesz = PAGE_SIZE; + rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE; + + rc = crypt_message(server, 1, &rqst, 0); + cifs_dbg(FYI, "Decrypt message returned %d\n", rc); + + if (rc) + return rc; + + memmove(buf, iov[1].iov_base, buf_data_size); + + if (!is_offloaded) + server->total_read = buf_data_size + page_data_size; + + return rc; +} + +static int +read_data_into_pages(struct TCP_Server_Info *server, struct page **pages, + unsigned int npages, unsigned int len) +{ + int i; + int length; + + for (i = 0; i < npages; i++) { + struct page *page = pages[i]; + size_t n; + + n = len; + if (len >= PAGE_SIZE) { + /* enough data to fill the page */ + n = PAGE_SIZE; + len -= n; + } else { + zero_user(page, len, PAGE_SIZE - len); + len = 0; + } + length = cifs_read_page_from_socket(server, page, 0, n); + if (length < 0) + return length; + server->total_read += length; + } + + return 0; +} + +static int +init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size, + unsigned int cur_off, struct bio_vec **page_vec) +{ + struct bio_vec *bvec; + int i; + + bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL); + if (!bvec) + return -ENOMEM; + + for (i = 0; i < npages; i++) { + bvec[i].bv_page = pages[i]; + bvec[i].bv_offset = (i == 0) ? cur_off : 0; + bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size); + data_size -= bvec[i].bv_len; + } + + if (data_size != 0) { + cifs_dbg(VFS, "%s: something went wrong\n", __func__); + kfree(bvec); + return -EIO; + } + + *page_vec = bvec; + return 0; +} + +static int +handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, + char *buf, unsigned int buf_len, struct page **pages, + unsigned int npages, unsigned int page_data_size, + bool is_offloaded) +{ + unsigned int data_offset; + unsigned int data_len; + unsigned int cur_off; + unsigned int cur_page_idx; + unsigned int pad_len; + struct cifs_readdata *rdata = mid->callback_data; + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct bio_vec *bvec = NULL; + struct iov_iter iter; + struct kvec iov; + int length; + bool use_rdma_mr = false; + + if (shdr->Command != SMB2_READ) { + cifs_server_dbg(VFS, "only big read responses are supported\n"); + return -ENOTSUPP; + } + + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + if (!is_offloaded) + cifs_reconnect(server, true); + return -1; + } + + if (server->ops->is_status_pending && + server->ops->is_status_pending(buf, server)) + return -1; + + /* set up first two iov to get credits */ + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = 0; + rdata->iov[1].iov_base = buf; + rdata->iov[1].iov_len = + min_t(unsigned int, buf_len, server->vals->read_rsp_size); + cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", + rdata->iov[0].iov_base, rdata->iov[0].iov_len); + cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", + rdata->iov[1].iov_base, rdata->iov[1].iov_len); + + rdata->result = server->ops->map_error(buf, true); + if (rdata->result != 0) { + cifs_dbg(FYI, "%s: server returned error %d\n", + __func__, rdata->result); + /* normal error on read response */ + if (is_offloaded) + mid->mid_state = MID_RESPONSE_RECEIVED; + else + dequeue_mid(mid, false); + return 0; + } + + data_offset = server->ops->read_data_offset(buf); +#ifdef CONFIG_CIFS_SMB_DIRECT + use_rdma_mr = rdata->mr; +#endif + data_len = server->ops->read_data_length(buf, use_rdma_mr); + + if (data_offset < server->vals->read_rsp_size) { + /* + * win2k8 sometimes sends an offset of 0 when the read + * is beyond the EOF. Treat it as if the data starts just after + * the header. + */ + cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", + __func__, data_offset); + data_offset = server->vals->read_rsp_size; + } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { + /* data_offset is beyond the end of smallbuf */ + cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", + __func__, data_offset); + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + pad_len = data_offset - server->vals->read_rsp_size; + + if (buf_len <= data_offset) { + /* read response payload is in pages */ + cur_page_idx = pad_len / PAGE_SIZE; + cur_off = pad_len % PAGE_SIZE; + + if (cur_page_idx != 0) { + /* data offset is beyond the 1st page of response */ + cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n", + __func__, data_offset); + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + if (data_len > page_data_size - pad_len) { + /* data_len is corrupt -- discard frame */ + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + rdata->result = init_read_bvec(pages, npages, page_data_size, + cur_off, &bvec); + if (rdata->result != 0) { + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + iov_iter_bvec(&iter, ITER_SOURCE, bvec, npages, data_len); + } else if (buf_len >= data_offset + data_len) { + /* read response payload is in buf */ + WARN_ONCE(npages > 0, "read data can be either in buf or in pages"); + iov.iov_base = buf + data_offset; + iov.iov_len = data_len; + iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, data_len); + } else { + /* read response payload cannot be in both buf and pages */ + WARN_ONCE(1, "buf can not contain only a part of read data"); + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + length = rdata->copy_into_pages(server, rdata, &iter); + + kfree(bvec); + + if (length < 0) + return length; + + if (is_offloaded) + mid->mid_state = MID_RESPONSE_RECEIVED; + else + dequeue_mid(mid, false); + return length; +} + +struct smb2_decrypt_work { + struct work_struct decrypt; + struct TCP_Server_Info *server; + struct page **ppages; + char *buf; + unsigned int npages; + unsigned int len; +}; + + +static void smb2_decrypt_offload(struct work_struct *work) +{ + struct smb2_decrypt_work *dw = container_of(work, + struct smb2_decrypt_work, decrypt); + int i, rc; + struct mid_q_entry *mid; + + rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, + dw->ppages, dw->npages, dw->len, true); + if (rc) { + cifs_dbg(VFS, "error decrypting rc=%d\n", rc); + goto free_pages; + } + + dw->server->lstrp = jiffies; + mid = smb2_find_dequeue_mid(dw->server, dw->buf); + if (mid == NULL) + cifs_dbg(FYI, "mid not found\n"); + else { + mid->decrypted = true; + rc = handle_read_data(dw->server, mid, dw->buf, + dw->server->vals->read_rsp_size, + dw->ppages, dw->npages, dw->len, + true); + if (rc >= 0) { +#ifdef CONFIG_CIFS_STATS2 + mid->when_received = jiffies; +#endif + if (dw->server->ops->is_network_name_deleted) + dw->server->ops->is_network_name_deleted(dw->buf, + dw->server); + + mid->callback(mid); + } else { + spin_lock(&dw->server->srv_lock); + if (dw->server->tcpStatus == CifsNeedReconnect) { + spin_lock(&dw->server->mid_lock); + mid->mid_state = MID_RETRY_NEEDED; + spin_unlock(&dw->server->mid_lock); + spin_unlock(&dw->server->srv_lock); + mid->callback(mid); + } else { + spin_lock(&dw->server->mid_lock); + mid->mid_state = MID_REQUEST_SUBMITTED; + mid->mid_flags &= ~(MID_DELETED); + list_add_tail(&mid->qhead, + &dw->server->pending_mid_q); + spin_unlock(&dw->server->mid_lock); + spin_unlock(&dw->server->srv_lock); + } + } + release_mid(mid); + } + +free_pages: + for (i = dw->npages-1; i >= 0; i--) + put_page(dw->ppages[i]); + + kfree(dw->ppages); + cifs_small_buf_release(dw->buf); + kfree(dw); +} + + +static int +receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, + int *num_mids) +{ + char *buf = server->smallbuf; + struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; + unsigned int npages; + struct page **pages; + unsigned int len; + unsigned int buflen = server->pdu_size; + int rc; + int i = 0; + struct smb2_decrypt_work *dw; + + *num_mids = 1; + len = min_t(unsigned int, buflen, server->vals->read_rsp_size + + sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; + + rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len); + if (rc < 0) + return rc; + server->total_read += rc; + + len = le32_to_cpu(tr_hdr->OriginalMessageSize) - + server->vals->read_rsp_size; + npages = DIV_ROUND_UP(len, PAGE_SIZE); + + pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); + if (!pages) { + rc = -ENOMEM; + goto discard_data; + } + + for (; i < npages; i++) { + pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!pages[i]) { + rc = -ENOMEM; + goto discard_data; + } + } + + /* read read data into pages */ + rc = read_data_into_pages(server, pages, npages, len); + if (rc) + goto free_pages; + + rc = cifs_discard_remaining_data(server); + if (rc) + goto free_pages; + + /* + * For large reads, offload to different thread for better performance, + * use more cores decrypting which can be expensive + */ + + if ((server->min_offload) && (server->in_flight > 1) && + (server->pdu_size >= server->min_offload)) { + dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); + if (dw == NULL) + goto non_offloaded_decrypt; + + dw->buf = server->smallbuf; + server->smallbuf = (char *)cifs_small_buf_get(); + + INIT_WORK(&dw->decrypt, smb2_decrypt_offload); + + dw->npages = npages; + dw->server = server; + dw->ppages = pages; + dw->len = len; + queue_work(decrypt_wq, &dw->decrypt); + *num_mids = 0; /* worker thread takes care of finding mid */ + return -1; + } + +non_offloaded_decrypt: + rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, + pages, npages, len, false); + if (rc) + goto free_pages; + + *mid = smb2_find_mid(server, buf); + if (*mid == NULL) + cifs_dbg(FYI, "mid not found\n"); + else { + cifs_dbg(FYI, "mid found\n"); + (*mid)->decrypted = true; + rc = handle_read_data(server, *mid, buf, + server->vals->read_rsp_size, + pages, npages, len, false); + if (rc >= 0) { + if (server->ops->is_network_name_deleted) { + server->ops->is_network_name_deleted(buf, + server); + } + } + } + +free_pages: + for (i = i - 1; i >= 0; i--) + put_page(pages[i]); + kfree(pages); + return rc; +discard_data: + cifs_discard_remaining_data(server); + goto free_pages; +} + +static int +receive_encrypted_standard(struct TCP_Server_Info *server, + struct mid_q_entry **mids, char **bufs, + int *num_mids) +{ + int ret, length; + char *buf = server->smallbuf; + struct smb2_hdr *shdr; + unsigned int pdu_length = server->pdu_size; + unsigned int buf_size; + struct mid_q_entry *mid_entry; + int next_is_large; + char *next_buffer = NULL; + + *num_mids = 0; + + /* switch to large buffer if too big for a small one */ + if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { + server->large_buf = true; + memcpy(server->bigbuf, buf, server->total_read); + buf = server->bigbuf; + } + + /* now read the rest */ + length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, + pdu_length - HEADER_SIZE(server) + 1); + if (length < 0) + return length; + server->total_read += length; + + buf_size = pdu_length - sizeof(struct smb2_transform_hdr); + length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0, false); + if (length) + return length; + + next_is_large = server->large_buf; +one_more: + shdr = (struct smb2_hdr *)buf; + if (shdr->NextCommand) { + if (next_is_large) + next_buffer = (char *)cifs_buf_get(); + else + next_buffer = (char *)cifs_small_buf_get(); + memcpy(next_buffer, + buf + le32_to_cpu(shdr->NextCommand), + pdu_length - le32_to_cpu(shdr->NextCommand)); + } + + mid_entry = smb2_find_mid(server, buf); + if (mid_entry == NULL) + cifs_dbg(FYI, "mid not found\n"); + else { + cifs_dbg(FYI, "mid found\n"); + mid_entry->decrypted = true; + mid_entry->resp_buf_size = server->pdu_size; + } + + if (*num_mids >= MAX_COMPOUND) { + cifs_server_dbg(VFS, "too many PDUs in compound\n"); + return -1; + } + bufs[*num_mids] = buf; + mids[(*num_mids)++] = mid_entry; + + if (mid_entry && mid_entry->handle) + ret = mid_entry->handle(server, mid_entry); + else + ret = cifs_handle_standard(server, mid_entry); + + if (ret == 0 && shdr->NextCommand) { + pdu_length -= le32_to_cpu(shdr->NextCommand); + server->large_buf = next_is_large; + if (next_is_large) + server->bigbuf = buf = next_buffer; + else + server->smallbuf = buf = next_buffer; + goto one_more; + } else if (ret != 0) { + /* + * ret != 0 here means that we didn't get to handle_mid() thus + * server->smallbuf and server->bigbuf are still valid. We need + * to free next_buffer because it is not going to be used + * anywhere. + */ + if (next_is_large) + free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer); + else + free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer); + } + + return ret; +} + +static int +smb3_receive_transform(struct TCP_Server_Info *server, + struct mid_q_entry **mids, char **bufs, int *num_mids) +{ + char *buf = server->smallbuf; + unsigned int pdu_length = server->pdu_size; + struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; + unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + + if (pdu_length < sizeof(struct smb2_transform_hdr) + + sizeof(struct smb2_hdr)) { + cifs_server_dbg(VFS, "Transform message is too small (%u)\n", + pdu_length); + cifs_reconnect(server, true); + return -ECONNABORTED; + } + + if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { + cifs_server_dbg(VFS, "Transform message is broken\n"); + cifs_reconnect(server, true); + return -ECONNABORTED; + } + + /* TODO: add support for compounds containing READ. */ + if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { + return receive_encrypted_read(server, &mids[0], num_mids); + } + + return receive_encrypted_standard(server, mids, bufs, num_mids); +} + +int +smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + char *buf = server->large_buf ? server->bigbuf : server->smallbuf; + + return handle_read_data(server, mid, buf, server->pdu_size, + NULL, 0, 0, false); +} + +static int +smb2_next_header(char *buf) +{ + struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; + + if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) + return sizeof(struct smb2_transform_hdr) + + le32_to_cpu(t_hdr->OriginalMessageSize); + + return le32_to_cpu(hdr->NextCommand); +} + +static int +smb2_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + int rc = -EPERM; + struct cifs_open_info_data buf = {}; + struct cifs_io_parms io_parms = {0}; + __u32 oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + unsigned int bytes_written; + struct win_dev *pdev; + struct kvec iov[2]; + + /* + * Check if mounted with mount parm 'sfu' mount parm. + * SFU emulation should work with all servers, but only + * supports block and char device (no socket & fifo), + * and was used by default in earlier versions of Windows + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) + return rc; + + /* + * TODO: Add ability to create instead via reparse point. Windows (e.g. + * their current NFS server) uses this approach to expose special files + * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions + */ + + if (!S_ISCHR(mode) && !S_ISBLK(mode)) + return rc; + + cifs_dbg(FYI, "sfu compat create special file\n"); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | + CREATE_OPTION_SPECIAL), + .disposition = FILE_CREATE, + .path = full_path, + .fid = &fid, + }; + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); + if (rc) + return rc; + + /* + * BB Do not bother to decode buf since no local inode yet to put + * timestamps in, but we can reuse it safely. + */ + + pdev = (struct win_dev *)&buf.fi; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = sizeof(struct win_dev); + iov[1].iov_base = &buf.fi; + iov[1].iov_len = sizeof(struct win_dev); + if (S_ISCHR(mode)) { + memcpy(pdev->type, "IntxCHR", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } else if (S_ISBLK(mode)) { + memcpy(pdev->type, "IntxBLK", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } + tcon->ses->server->ops->close(xid, tcon, &fid); + d_drop(dentry); + + /* FIXME: add code here to set EAs */ + + cifs_free_open_info(&buf); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +struct smb_version_operations smb20_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = cifs_wait_mtu_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb2_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb2_negotiate_wsize, + .negotiate_rsize = smb2_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb2_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .calc_signature = smb2_calc_signature, + .is_read_op = smb2_is_read_op, + .set_oplock_level = smb2_set_oplock_level, + .create_lease_buf = smb2_create_lease_buf, + .parse_lease_buf = smb2_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ + +struct smb_version_operations smb21_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, + .adjust_credits = smb2_adjust_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb2_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb2_negotiate_wsize, + .negotiate_rsize = smb2_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb2_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .calc_signature = smb2_calc_signature, + .is_read_op = smb21_is_read_op, + .set_oplock_level = smb21_set_oplock_level, + .create_lease_buf = smb2_create_lease_buf, + .parse_lease_buf = smb2_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .enum_snapshots = smb3_enum_snapshots, + .notify = smb3_notify, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; + +struct smb_version_operations smb30_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, + .adjust_credits = smb2_adjust_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .dump_share_caps = smb2_dump_share_caps, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb3_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb3_negotiate_wsize, + .negotiate_rsize = smb3_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb3_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + /* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */ + .query_reparse_tag = smb2_query_reparse_tag, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .close_getattr = smb2_close_getattr, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb2_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .generate_signingkey = generate_smb30signingkey, + .calc_signature = smb3_calc_signature, + .set_integrity = smb3_set_integrity, + .is_read_op = smb21_is_read_op, + .set_oplock_level = smb3_set_oplock_level, + .create_lease_buf = smb3_create_lease_buf, + .parse_lease_buf = smb3_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .duplicate_extents = smb2_duplicate_extents, + .validate_negotiate = smb3_validate_negotiate, + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .fallocate = smb3_fallocate, + .enum_snapshots = smb3_enum_snapshots, + .notify = smb3_notify, + .init_transform_rq = smb3_init_transform_rq, + .is_transform_hdr = smb3_is_transform_hdr, + .receive_transform = smb3_receive_transform, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; + +struct smb_version_operations smb311_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, + .adjust_credits = smb2_adjust_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .dump_share_caps = smb2_dump_share_caps, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb3_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb3_negotiate_wsize, + .negotiate_rsize = smb3_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb3_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + .query_reparse_tag = smb2_query_reparse_tag, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .posix_mkdir = smb311_posix_mkdir, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .close_getattr = smb2_close_getattr, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb311_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .generate_signingkey = generate_smb311signingkey, + .calc_signature = smb3_calc_signature, + .set_integrity = smb3_set_integrity, + .is_read_op = smb21_is_read_op, + .set_oplock_level = smb3_set_oplock_level, + .create_lease_buf = smb3_create_lease_buf, + .parse_lease_buf = smb3_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .duplicate_extents = smb2_duplicate_extents, +/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */ + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .fallocate = smb3_fallocate, + .enum_snapshots = smb3_enum_snapshots, + .notify = smb3_notify, + .init_transform_rq = smb3_init_transform_rq, + .is_transform_hdr = smb3_is_transform_hdr, + .receive_transform = smb3_receive_transform, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +struct smb_version_values smb20_values = { + .version_string = SMB20_VERSION_STRING, + .protocol_id = SMB20_PROT_ID, + .req_capabilities = 0, /* MBZ */ + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease), +}; +#endif /* ALLOW_INSECURE_LEGACY */ + +struct smb_version_values smb21_values = { + .version_string = SMB21_VERSION_STRING, + .protocol_id = SMB21_PROT_ID, + .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */ + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease), +}; + +struct smb_version_values smb3any_values = { + .version_string = SMB3ANY_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smbdefault_values = { + .version_string = SMBDEFAULT_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smb30_values = { + .version_string = SMB30_VERSION_STRING, + .protocol_id = SMB30_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smb302_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smb311_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c new file mode 100644 index 000000000000..3ca593cdda76 --- /dev/null +++ b/fs/smb/client/smb2pdu.c @@ -0,0 +1,5722 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2009, 2013 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + * Contains the routines for constructing the SMB2 PDUs themselves + * + */ + + /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ + /* Note that there are handle based routines which must be */ + /* treated slightly differently for reconnection purposes since we never */ + /* want to reuse a stale file handle and only the caller knows the file info */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "ntlmssp.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "cifspdu.h" +#include "cifs_spnego.h" +#include "smbdirect.h" +#include "trace.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#include "cached_dir.h" + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order. + */ +static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ 36, + /* SMB2_SESSION_SETUP */ 25, + /* SMB2_LOGOFF */ 4, + /* SMB2_TREE_CONNECT */ 9, + /* SMB2_TREE_DISCONNECT */ 4, + /* SMB2_CREATE */ 57, + /* SMB2_CLOSE */ 24, + /* SMB2_FLUSH */ 24, + /* SMB2_READ */ 49, + /* SMB2_WRITE */ 49, + /* SMB2_LOCK */ 48, + /* SMB2_IOCTL */ 57, + /* SMB2_CANCEL */ 4, + /* SMB2_ECHO */ 4, + /* SMB2_QUERY_DIRECTORY */ 33, + /* SMB2_CHANGE_NOTIFY */ 32, + /* SMB2_QUERY_INFO */ 41, + /* SMB2_SET_INFO */ 33, + /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ +}; + +int smb3_encryption_required(const struct cifs_tcon *tcon) +{ + if (!tcon || !tcon->ses) + return 0; + if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || + (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) + return 1; + if (tcon->seal && + (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + return 1; + return 0; +} + +static void +smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, + const struct cifs_tcon *tcon, + struct TCP_Server_Info *server) +{ + shdr->ProtocolId = SMB2_PROTO_NUMBER; + shdr->StructureSize = cpu_to_le16(64); + shdr->Command = smb2_cmd; + if (server) { + spin_lock(&server->req_lock); + /* Request up to 10 credits but don't go over the limit. */ + if (server->credits >= server->max_credits) + shdr->CreditRequest = cpu_to_le16(0); + else + shdr->CreditRequest = cpu_to_le16( + min_t(int, server->max_credits - + server->credits, 10)); + spin_unlock(&server->req_lock); + } else { + shdr->CreditRequest = cpu_to_le16(2); + } + shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid); + + if (!tcon) + goto out; + + /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ + /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ + if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + shdr->CreditCharge = cpu_to_le16(1); + /* else CreditCharge MBZ */ + + shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); + /* Uid is not converted */ + if (tcon->ses) + shdr->SessionId = cpu_to_le64(tcon->ses->Suid); + + /* + * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have + * to pass the path on the Open SMB prefixed by \\server\share. + * Not sure when we would need to do the augmented path (if ever) and + * setting this flag breaks the SMB2 open operation since it is + * illegal to send an empty path name (without \\server\share prefix) + * when the DFS flag is set in the SMB open header. We could + * consider setting the flag on all operations other than open + * but it is safer to net set it for now. + */ +/* if (tcon->share_flags & SHI1005_FLAGS_DFS) + shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ + + if (server && server->sign && !smb3_encryption_required(tcon)) + shdr->Flags |= SMB2_FLAGS_SIGNED; +out: + return; +} + +static int +smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server) +{ + int rc = 0; + struct nls_table *nls_codepage = NULL; + struct cifs_ses *ses; + + /* + * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so + * check for tcp and smb session status done differently + * for those three - in the calling routine. + */ + if (tcon == NULL) + return 0; + + /* + * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in + * cifs_tree_connect(). + */ + if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) + return 0; + + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_EXITING) { + /* + * only tree disconnect allowed when disconnecting ... + */ + if (smb2_command != SMB2_TREE_DISCONNECT) { + spin_unlock(&tcon->tc_lock); + cifs_dbg(FYI, "can not send cmd %d while umounting\n", + smb2_command); + return -ENODEV; + } + } + spin_unlock(&tcon->tc_lock); + + ses = tcon->ses; + if (!ses) + return -EIO; + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_EXITING) { + spin_unlock(&ses->ses_lock); + return -EIO; + } + spin_unlock(&ses->ses_lock); + if (!ses->server || !server) + return -EIO; + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect) { + /* + * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE + * here since they are implicitly done when session drops. + */ + switch (smb2_command) { + /* + * BB Should we keep oplock break and add flush to exceptions? + */ + case SMB2_TREE_DISCONNECT: + case SMB2_CANCEL: + case SMB2_CLOSE: + case SMB2_OPLOCK_BREAK: + spin_unlock(&server->srv_lock); + return -EAGAIN; + } + } + spin_unlock(&server->srv_lock); + +again: + rc = cifs_wait_for_server_reconnect(server, tcon->retry); + if (rc) + return rc; + + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { + spin_unlock(&ses->chan_lock); + return 0; + } + spin_unlock(&ses->chan_lock); + cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", + tcon->ses->chans_need_reconnect, + tcon->need_reconnect); + + mutex_lock(&ses->session_mutex); + /* + * Recheck after acquire mutex. If another thread is negotiating + * and the server never sends an answer the socket will be closed + * and tcpStatus set to reconnect. + */ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + mutex_unlock(&ses->session_mutex); + + if (tcon->retry) + goto again; + + rc = -EHOSTDOWN; + goto out; + } + spin_unlock(&server->srv_lock); + + nls_codepage = load_nls_default(); + + /* + * need to prevent multiple threads trying to simultaneously + * reconnect the same SMB session + */ + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server) && + ses->ses_status == SES_GOOD) { + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + /* this means that we only need to tree connect */ + if (tcon->need_reconnect) + goto skip_sess_setup; + + mutex_unlock(&ses->session_mutex); + goto out; + } + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + rc = cifs_negotiate_protocol(0, ses, server); + if (!rc) { + rc = cifs_setup_session(0, ses, server, nls_codepage); + if ((rc == -EACCES) && !tcon->retry) { + mutex_unlock(&ses->session_mutex); + rc = -EHOSTDOWN; + goto failed; + } else if (rc) { + mutex_unlock(&ses->session_mutex); + goto out; + } + } else { + mutex_unlock(&ses->session_mutex); + goto out; + } + +skip_sess_setup: + if (!tcon->need_reconnect) { + mutex_unlock(&ses->session_mutex); + goto out; + } + cifs_mark_open_files_invalid(tcon); + if (tcon->use_persistent) + tcon->need_reopen_files = true; + + rc = cifs_tree_connect(0, tcon, nls_codepage); + mutex_unlock(&ses->session_mutex); + + cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); + if (rc) { + /* If sess reconnected but tcon didn't, something strange ... */ + cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); + goto out; + } + + if (smb2_command != SMB2_INTERNAL_CMD) + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + + atomic_inc(&tconInfoReconnectCount); +out: + /* + * Check if handle based operation so we know whether we can continue + * or not without returning to caller to reset file handle. + */ + /* + * BB Is flush done by server on drop of tcp session? Should we special + * case it and skip above? + */ + switch (smb2_command) { + case SMB2_FLUSH: + case SMB2_READ: + case SMB2_WRITE: + case SMB2_LOCK: + case SMB2_IOCTL: + case SMB2_QUERY_DIRECTORY: + case SMB2_CHANGE_NOTIFY: + case SMB2_QUERY_INFO: + case SMB2_SET_INFO: + rc = -EAGAIN; + } +failed: + unload_nls(nls_codepage); + return rc; +} + +static void +fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void *buf, + unsigned int *total_len) +{ + struct smb2_pdu *spdu = buf; + /* lookup word count ie StructureSize from table */ + __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; + + /* + * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of + * largest operations (Create) + */ + memset(buf, 0, 256); + + smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server); + spdu->StructureSize2 = cpu_to_le16(parmsize); + + *total_len = parmsize + sizeof(struct smb2_hdr); +} + +/* + * Allocate and return pointer to an SMB request hdr, and set basic + * SMB information in the SMB header. If the return code is zero, this + * function must have filled in request_buf pointer. + */ +static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void **request_buf, unsigned int *total_len) +{ + /* BB eventually switch this to SMB2 specific small buf size */ + if (smb2_command == SMB2_SET_INFO) + *request_buf = cifs_buf_get(); + else + *request_buf = cifs_small_buf_get(); + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + + fill_small_buf(smb2_command, tcon, server, + (struct smb2_hdr *)(*request_buf), + total_len); + + if (tcon != NULL) { + uint16_t com_code = le16_to_cpu(smb2_command); + cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); + cifs_stats_inc(&tcon->num_smbs_sent); + } + + return 0; +} + +static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void **request_buf, unsigned int *total_len) +{ + int rc; + + rc = smb2_reconnect(smb2_command, tcon, server); + if (rc) + return rc; + + return __smb2_plain_req_init(smb2_command, tcon, server, request_buf, + total_len); +} + +static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void **request_buf, unsigned int *total_len) +{ + /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ + if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { + return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, + request_buf, total_len); + } + return smb2_plain_req_init(SMB2_IOCTL, tcon, server, + request_buf, total_len); +} + +/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ + +static void +build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; +} + +static void +build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); + pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; + pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; + pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; +} + +static unsigned int +build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) +{ + unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities); + unsigned short num_algs = 1; /* number of signing algorithms sent */ + + pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; + /* + * Context Data length must be rounded to multiple of 8 for some servers + */ + pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) - + sizeof(struct smb2_neg_context) + + (num_algs * sizeof(u16)), 8)); + pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); + pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); + + ctxt_len += sizeof(__le16) * num_algs; + ctxt_len = ALIGN(ctxt_len, 8); + return ctxt_len; + /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ +} + +static void +build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + if (require_gcm_256) { + pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */ + pneg_ctxt->CipherCount = cpu_to_le16(1); + pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM; + } else if (enable_gcm_256) { + pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */ + pneg_ctxt->CipherCount = cpu_to_le16(3); + pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; + pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM; + pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM; + } else { + pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */ + pneg_ctxt->CipherCount = cpu_to_le16(2); + pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; + pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; + } +} + +static unsigned int +build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) +{ + struct nls_table *cp = load_nls_default(); + + pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; + + /* copy up to max of first 100 bytes of server name to NetName field */ + pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); + /* context size is DataLength + minimal smb2_neg_context */ + return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8); +} + +static void +build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + pneg_ctxt->Name[0] = 0x93; + pneg_ctxt->Name[1] = 0xAD; + pneg_ctxt->Name[2] = 0x25; + pneg_ctxt->Name[3] = 0x50; + pneg_ctxt->Name[4] = 0x9C; + pneg_ctxt->Name[5] = 0xB4; + pneg_ctxt->Name[6] = 0x11; + pneg_ctxt->Name[7] = 0xE7; + pneg_ctxt->Name[8] = 0xB4; + pneg_ctxt->Name[9] = 0x23; + pneg_ctxt->Name[10] = 0x83; + pneg_ctxt->Name[11] = 0xDE; + pneg_ctxt->Name[12] = 0x96; + pneg_ctxt->Name[13] = 0x8B; + pneg_ctxt->Name[14] = 0xCD; + pneg_ctxt->Name[15] = 0x7C; +} + +static void +assemble_neg_contexts(struct smb2_negotiate_req *req, + struct TCP_Server_Info *server, unsigned int *total_len) +{ + unsigned int ctxt_len, neg_context_count; + struct TCP_Server_Info *pserver; + char *pneg_ctxt; + char *hostname; + + if (*total_len > 200) { + /* In case length corrupted don't want to overrun smb buffer */ + cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); + return; + } + + /* + * round up total_len of fixed part of SMB3 negotiate request to 8 + * byte boundary before adding negotiate contexts + */ + *total_len = ALIGN(*total_len, 8); + + pneg_ctxt = (*total_len) + (char *)req; + req->NegotiateContextOffset = cpu_to_le32(*total_len); + + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); + ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); + ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + + /* + * secondary channels don't have the hostname field populated + * use the hostname field in the primary channel instead + */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + cifs_server_lock(pserver); + hostname = pserver->hostname; + if (hostname && (hostname[0] != 0)) { + ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt, + hostname); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + neg_context_count = 3; + } else + neg_context_count = 2; + cifs_server_unlock(pserver); + + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); + *total_len += sizeof(struct smb2_posix_neg_context); + pneg_ctxt += sizeof(struct smb2_posix_neg_context); + neg_context_count++; + + if (server->compress_algorithm) { + build_compression_ctxt((struct smb2_compression_capabilities_context *) + pneg_ctxt); + ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + neg_context_count++; + } + + if (enable_negotiate_signing) { + ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *) + pneg_ctxt); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + neg_context_count++; + } + + /* check for and add transport_capabilities and signing capabilities */ + req->NegotiateContextCount = cpu_to_le16(neg_context_count); + +} + +/* If invalid preauth context warn but use what we requested, SHA-512 */ +static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt) +{ + unsigned int len = le16_to_cpu(ctxt->DataLength); + + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one HashAlgorithms member is accounted for. + */ + if (len < MIN_PREAUTH_CTXT_DATA_LEN) { + pr_warn_once("server sent bad preauth context\n"); + return; + } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) { + pr_warn_once("server sent invalid SaltLength\n"); + return; + } + if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1) + pr_warn_once("Invalid SMB3 hash algorithm count\n"); + if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) + pr_warn_once("unknown SMB3 hash algorithm\n"); +} + +static void decode_compress_ctx(struct TCP_Server_Info *server, + struct smb2_compression_capabilities_context *ctxt) +{ + unsigned int len = le16_to_cpu(ctxt->DataLength); + + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one CompressionAlgorithms member is accounted + * for. + */ + if (len < 10) { + pr_warn_once("server sent bad compression cntxt\n"); + return; + } + if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { + pr_warn_once("Invalid SMB3 compress algorithm count\n"); + return; + } + if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) { + pr_warn_once("unknown compression algorithm\n"); + return; + } + server->compress_algorithm = ctxt->CompressionAlgorithms[0]; +} + +static int decode_encrypt_ctx(struct TCP_Server_Info *server, + struct smb2_encryption_neg_context *ctxt) +{ + unsigned int len = le16_to_cpu(ctxt->DataLength); + + cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one Cipher flexible array member is accounted + * for. + */ + if (len < MIN_ENCRYPT_CTXT_DATA_LEN) { + pr_warn_once("server sent bad crypto ctxt len\n"); + return -EINVAL; + } + + if (le16_to_cpu(ctxt->CipherCount) != 1) { + pr_warn_once("Invalid SMB3.11 cipher count\n"); + return -EINVAL; + } + cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); + if (require_gcm_256) { + if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) { + cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); + return -EOPNOTSUPP; + } + } else if (ctxt->Ciphers[0] == 0) { + /* + * e.g. if server only supported AES256_CCM (very unlikely) + * or server supported no encryption types or had all disabled. + * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case + * in which mount requested encryption ("seal") checks later + * on during tree connection will return proper rc, but if + * seal not requested by client, since server is allowed to + * return 0 to indicate no supported cipher, we can't fail here + */ + server->cipher_type = 0; + server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION; + pr_warn_once("Server does not support requested encryption types\n"); + return 0; + } else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) && + (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) && + (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { + /* server returned a cipher we didn't ask for */ + pr_warn_once("Invalid SMB3.11 cipher returned\n"); + return -EINVAL; + } + server->cipher_type = ctxt->Ciphers[0]; + server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + return 0; +} + +static void decode_signing_ctx(struct TCP_Server_Info *server, + struct smb2_signing_capabilities *pctxt) +{ + unsigned int len = le16_to_cpu(pctxt->DataLength); + + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one SigningAlgorithms flexible array member is + * accounted for. + */ + if ((len < 4) || (len > 16)) { + pr_warn_once("server sent bad signing negcontext\n"); + return; + } + if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) { + pr_warn_once("Invalid signing algorithm count\n"); + return; + } + if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) { + pr_warn_once("unknown signing algorithm\n"); + return; + } + + server->signing_negotiated = true; + server->signing_algorithm = le16_to_cpu(pctxt->SigningAlgorithms[0]); + cifs_dbg(FYI, "signing algorithm %d chosen\n", + server->signing_algorithm); +} + + +static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, + struct TCP_Server_Info *server, + unsigned int len_of_smb) +{ + struct smb2_neg_context *pctx; + unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); + unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); + unsigned int len_of_ctxts, i; + int rc = 0; + + cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); + if (len_of_smb <= offset) { + cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); + return -EINVAL; + } + + len_of_ctxts = len_of_smb - offset; + + for (i = 0; i < ctxt_cnt; i++) { + int clen; + /* check that offset is not beyond end of SMB */ + if (len_of_ctxts < sizeof(struct smb2_neg_context)) + break; + + pctx = (struct smb2_neg_context *)(offset + (char *)rsp); + clen = sizeof(struct smb2_neg_context) + + le16_to_cpu(pctx->DataLength); + /* + * 2.2.4 SMB2 NEGOTIATE Response + * Subsequent negotiate contexts MUST appear at the first 8-byte + * aligned offset following the previous negotiate context. + */ + if (i + 1 != ctxt_cnt) + clen = ALIGN(clen, 8); + if (clen > len_of_ctxts) + break; + + if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) + decode_preauth_context( + (struct smb2_preauth_neg_context *)pctx); + else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) + rc = decode_encrypt_ctx(server, + (struct smb2_encryption_neg_context *)pctx); + else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) + decode_compress_ctx(server, + (struct smb2_compression_capabilities_context *)pctx); + else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) + server->posix_ext_supported = true; + else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) + decode_signing_ctx(server, + (struct smb2_signing_capabilities *)pctx); + else + cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", + le16_to_cpu(pctx->ContextType)); + if (rc) + break; + + offset += clen; + len_of_ctxts -= clen; + } + return rc; +} + +static struct create_posix * +create_posix_buf(umode_t mode) +{ + struct create_posix *buf; + + buf = kzalloc(sizeof(struct create_posix), + GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = + cpu_to_le16(offsetof(struct create_posix, Mode)); + buf->ccontext.DataLength = cpu_to_le32(4); + buf->ccontext.NameOffset = + cpu_to_le16(offsetof(struct create_posix, Name)); + buf->ccontext.NameLength = cpu_to_le16(16); + + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + buf->Name[0] = 0x93; + buf->Name[1] = 0xAD; + buf->Name[2] = 0x25; + buf->Name[3] = 0x50; + buf->Name[4] = 0x9C; + buf->Name[5] = 0xB4; + buf->Name[6] = 0x11; + buf->Name[7] = 0xE7; + buf->Name[8] = 0xB4; + buf->Name[9] = 0x23; + buf->Name[10] = 0x83; + buf->Name[11] = 0xDE; + buf->Name[12] = 0x96; + buf->Name[13] = 0x8B; + buf->Name[14] = 0xCD; + buf->Name[15] = 0x7C; + buf->Mode = cpu_to_le32(mode); + cifs_dbg(FYI, "mode on posix create 0%o\n", mode); + return buf; +} + +static int +add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_posix_buf(mode); + if (mode == ACL_NO_MODE) + cifs_dbg(FYI, "Invalid mode\n"); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_posix); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_posix)); + *num_iovec = num + 1; + return 0; +} + + +/* + * + * SMB2 Worker functions follow: + * + * The general structure of the worker functions is: + * 1) Call smb2_init (assembles SMB2 header) + * 2) Initialize SMB2 command specific fields in fixed length area of SMB + * 3) Call smb_sendrcv2 (sends request on socket and waits for response) + * 4) Decode SMB2 command specific fields in the fixed length area + * 5) Decode variable length data area (if any for this SMB2 command type) + * 6) Call free smb buffer + * 7) return + * + */ + +int +SMB2_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + struct smb_rqst rqst; + struct smb2_negotiate_req *req; + struct smb2_negotiate_rsp *rsp; + struct kvec iov[1]; + struct kvec rsp_iov; + int rc; + int resp_buftype; + int blob_offset, blob_length; + char *security_blob; + int flags = CIFS_NEG_OP; + unsigned int total_len; + + cifs_dbg(FYI, "Negotiate protocol\n"); + + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; + } + + rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->hdr.SessionId = 0; + + memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); + memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); + + if (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) { + req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); + req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); + req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); + req->DialectCount = cpu_to_le16(3); + total_len += 6; + } else if (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); + req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); + req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); + req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); + req->DialectCount = cpu_to_le16(4); + total_len += 8; + } else { + /* otherwise send specific dialect */ + req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); + req->DialectCount = cpu_to_le16(1); + total_len += 2; + } + + /* only one of SMB2 signing flags may be set in SMB2 request */ + if (ses->sign) + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); + else if (global_secflags & CIFSSEC_MAY_SIGN) + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); + else + req->SecurityMode = 0; + + req->Capabilities = cpu_to_le32(server->vals->req_capabilities); + if (ses->chan_max > 1) + req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); + + /* ClientGUID must be zero for SMB2.02 dialect */ + if (server->vals->protocol_id == SMB20_PROT_ID) + memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); + else { + memcpy(req->ClientGUID, server->client_guid, + SMB2_CLIENT_GUID_SIZE); + if ((server->vals->protocol_id == SMB311_PROT_ID) || + (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) || + (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0)) + assemble_neg_contexts(req, server, &total_len); + } + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(req); + rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; + /* + * No tcon so can't do + * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); + */ + if (rc == -EOPNOTSUPP) { + cifs_server_dbg(VFS, "Dialect not supported by server. Consider specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n"); + goto neg_exit; + } else if (rc != 0) + goto neg_exit; + + rc = -EIO; + if (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) { + if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { + cifs_server_dbg(VFS, + "SMB2 dialect returned but not requested\n"); + goto neg_exit; + } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { + cifs_server_dbg(VFS, + "SMB2.1 dialect returned but not requested\n"); + goto neg_exit; + } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + /* ops set to 3.0 by default for default so update */ + server->ops = &smb311_operations; + server->vals = &smb311_values; + } + } else if (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { + cifs_server_dbg(VFS, + "SMB2 dialect returned but not requested\n"); + goto neg_exit; + } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { + /* ops set to 3.0 by default for default so update */ + server->ops = &smb21_operations; + server->vals = &smb21_values; + } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + server->ops = &smb311_operations; + server->vals = &smb311_values; + } + } else if (le16_to_cpu(rsp->DialectRevision) != + server->vals->protocol_id) { + /* if requested single dialect ensure returned dialect matched */ + cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", + le16_to_cpu(rsp->DialectRevision)); + goto neg_exit; + } + + cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); + + if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) + cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) + cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); + else { + cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", + le16_to_cpu(rsp->DialectRevision)); + goto neg_exit; + } + + rc = 0; + server->dialect = le16_to_cpu(rsp->DialectRevision); + + /* + * Keep a copy of the hash after negprot. This hash will be + * the starting hash value for all sessions made from this + * server. + */ + memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, + SMB2_PREAUTH_HASH_SIZE); + + /* SMB2 only has an extended negflavor */ + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; + /* set it to the maximum buffer size value we can send with 1 credit */ + server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), + SMB2_MAX_BUFFER_SIZE); + server->max_read = le32_to_cpu(rsp->MaxReadSize); + server->max_write = le32_to_cpu(rsp->MaxWriteSize); + server->sec_mode = le16_to_cpu(rsp->SecurityMode); + if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode) + cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n", + server->sec_mode); + server->capabilities = le32_to_cpu(rsp->Capabilities); + /* Internal types */ + server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; + + /* + * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context + * Set the cipher type manually. + */ + if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; + + security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, + (struct smb2_hdr *)rsp); + /* + * See MS-SMB2 section 2.2.4: if no blob, client picks default which + * for us will be + * ses->sectype = RawNTLMSSP; + * but for time being this is our only auth choice so doesn't matter. + * We just found a server which sets blob length to zero expecting raw. + */ + if (blob_length == 0) { + cifs_dbg(FYI, "missing security blob on negprot\n"); + server->sec_ntlmssp = true; + } + + rc = cifs_enable_signing(server, ses->sign); + if (rc) + goto neg_exit; + if (blob_length) { + rc = decode_negTokenInit(security_blob, blob_length, server); + if (rc == 1) + rc = 0; + else if (rc == 0) + rc = -EIO; + } + + if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + if (rsp->NegotiateContextCount) + rc = smb311_decode_neg_context(rsp, server, + rsp_iov.iov_len); + else + cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); + } +neg_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) +{ + int rc; + struct validate_negotiate_info_req *pneg_inbuf; + struct validate_negotiate_info_rsp *pneg_rsp = NULL; + u32 rsplen; + u32 inbuflen; /* max of 4 dialects */ + struct TCP_Server_Info *server = tcon->ses->server; + + cifs_dbg(FYI, "validate negotiate\n"); + + /* In SMB3.11 preauth integrity supersedes validate negotiate */ + if (server->dialect == SMB311_PROT_ID) + return 0; + + /* + * validation ioctl must be signed, so no point sending this if we + * can not sign it (ie are not known user). Even if signing is not + * required (enabled but not negotiated), in those cases we selectively + * sign just this, the first and only signed request on a connection. + * Having validation of negotiate info helps reduce attack vectors. + */ + if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) + return 0; /* validation requires signing */ + + if (tcon->ses->user_name == NULL) { + cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); + return 0; /* validation requires signing */ + } + + if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) + cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); + + pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); + if (!pneg_inbuf) + return -ENOMEM; + + pneg_inbuf->Capabilities = + cpu_to_le32(server->vals->req_capabilities); + if (tcon->ses->chan_max > 1) + pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); + + memcpy(pneg_inbuf->Guid, server->client_guid, + SMB2_CLIENT_GUID_SIZE); + + if (tcon->ses->sign) + pneg_inbuf->SecurityMode = + cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); + else if (global_secflags & CIFSSEC_MAY_SIGN) + pneg_inbuf->SecurityMode = + cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); + else + pneg_inbuf->SecurityMode = 0; + + + if (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) { + pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); + pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); + pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); + pneg_inbuf->DialectCount = cpu_to_le16(3); + /* SMB 2.1 not included so subtract one dialect from len */ + inbuflen = sizeof(*pneg_inbuf) - + (sizeof(pneg_inbuf->Dialects[0])); + } else if (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); + pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); + pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); + pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); + pneg_inbuf->DialectCount = cpu_to_le16(4); + /* structure is big enough for 4 dialects */ + inbuflen = sizeof(*pneg_inbuf); + } else { + /* otherwise specific dialect was requested */ + pneg_inbuf->Dialects[0] = + cpu_to_le16(server->vals->protocol_id); + pneg_inbuf->DialectCount = cpu_to_le16(1); + /* structure is big enough for 4 dialects, sending only 1 */ + inbuflen = sizeof(*pneg_inbuf) - + sizeof(pneg_inbuf->Dialects[0]) * 3; + } + + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, + FSCTL_VALIDATE_NEGOTIATE_INFO, + (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize, + (char **)&pneg_rsp, &rsplen); + if (rc == -EOPNOTSUPP) { + /* + * Old Windows versions or Netapp SMB server can return + * not supported error. Client should accept it. + */ + cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); + rc = 0; + goto out_free_inbuf; + } else if (rc != 0) { + cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", + rc); + rc = -EIO; + goto out_free_inbuf; + } + + rc = -EIO; + if (rsplen != sizeof(*pneg_rsp)) { + cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", + rsplen); + + /* relax check since Mac returns max bufsize allowed on ioctl */ + if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) + goto out_free_rsp; + } + + /* check validate negotiate info response matches what we got earlier */ + if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) + goto vneg_out; + + if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) + goto vneg_out; + + /* do not validate server guid because not saved at negprot time yet */ + + if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | + SMB2_LARGE_FILES) != server->capabilities) + goto vneg_out; + + /* validate negotiate successful */ + rc = 0; + cifs_dbg(FYI, "validate negotiate info successful\n"); + goto out_free_rsp; + +vneg_out: + cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); +out_free_rsp: + kfree(pneg_rsp); +out_free_inbuf: + kfree(pneg_inbuf); + return rc; +} + +enum securityEnum +smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) +{ + switch (requested) { + case Kerberos: + case RawNTLMSSP: + return requested; + case NTLMv2: + return RawNTLMSSP; + case Unspecified: + if (server->sec_ntlmssp && + (global_secflags & CIFSSEC_MAY_NTLMSSP)) + return RawNTLMSSP; + if ((server->sec_kerberos || server->sec_mskerberos) && + (global_secflags & CIFSSEC_MAY_KRB5)) + return Kerberos; + fallthrough; + default: + return Unspecified; + } +} + +struct SMB2_sess_data { + unsigned int xid; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_cp; + void (*func)(struct SMB2_sess_data *); + int result; + u64 previous_session; + + /* we will send the SMB in three pieces: + * a fixed length beginning part, an optional + * SPNEGO blob (which can be zero length), and a + * last part which will include the strings + * and rest of bcc area. This allows us to avoid + * a large buffer 17K allocation + */ + int buf0_type; + struct kvec iov[2]; +}; + +static int +SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct smb2_sess_setup_req *req; + unsigned int total_len; + bool is_binding = false; + + rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, + (void **) &req, + &total_len); + if (rc) + return rc; + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + if (is_binding) { + req->hdr.SessionId = cpu_to_le64(ses->Suid); + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + req->PreviousSessionId = 0; + req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; + cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid); + } else { + /* First session, not a reauthenticate */ + req->hdr.SessionId = 0; + /* + * if reconnect, we need to send previous sess id + * otherwise it is 0 + */ + req->PreviousSessionId = cpu_to_le64(sess_data->previous_session); + req->Flags = 0; /* MBZ */ + cifs_dbg(FYI, "Fresh session. Previous: %llx\n", + sess_data->previous_session); + } + + /* enough to enable echos and oplocks and one max size write */ + req->hdr.CreditRequest = cpu_to_le16(130); + + /* only one of SMB2 signing flags may be set in SMB2 request */ + if (server->sign) + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; + else + req->SecurityMode = 0; + +#ifdef CONFIG_CIFS_DFS_UPCALL + req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); +#else + req->Capabilities = 0; +#endif /* DFS_UPCALL */ + + req->Channel = 0; /* MBZ */ + + sess_data->iov[0].iov_base = (char *)req; + /* 1 for pad */ + sess_data->iov[0].iov_len = total_len - 1; + /* + * This variable will be used to clear the buffer + * allocated above in case of any error in the calling function. + */ + sess_data->buf0_type = CIFS_SMALL_BUFFER; + + return 0; +} + +static void +SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) +{ + struct kvec *iov = sess_data->iov; + + /* iov[1] is already freed by caller */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) + memzero_explicit(iov[0].iov_base, iov[0].iov_len); + + free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); + sess_data->buf0_type = CIFS_NO_BUFFER; +} + +static int +SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) +{ + int rc; + struct smb_rqst rqst; + struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; + struct kvec rsp_iov = { NULL, 0 }; + + /* Testing shows that buffer offset must be at location of Buffer[0] */ + req->SecurityBufferOffset = + cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */); + req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = sess_data->iov; + rqst.rq_nvec = 2; + + /* BB add code to build os and lm fields */ + rc = cifs_send_recv(sess_data->xid, sess_data->ses, + sess_data->server, + &rqst, + &sess_data->buf0_type, + CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov); + cifs_small_buf_release(sess_data->iov[0].iov_base); + memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); + + return rc; +} + +static int +SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) +{ + int rc = 0; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + cifs_server_lock(server); + if (server->ops->generate_signingkey) { + rc = server->ops->generate_signingkey(ses, server); + if (rc) { + cifs_dbg(FYI, + "SMB3 session key generation failed\n"); + cifs_server_unlock(server); + return rc; + } + } + if (!server->session_estab) { + server->sequence_number = 0x2; + server->session_estab = true; + } + cifs_server_unlock(server); + + cifs_dbg(FYI, "SMB2/3 session established successfully\n"); + return rc; +} + +#ifdef CONFIG_CIFS_UPCALL +static void +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct cifs_spnego_msg *msg; + struct key *spnego_key = NULL; + struct smb2_sess_setup_rsp *rsp = NULL; + bool is_binding = false; + + rc = SMB2_sess_alloc_buffer(sess_data); + if (rc) + goto out; + + spnego_key = cifs_get_spnego_key(ses, server); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + if (rc == -ENOKEY) + cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); + spnego_key = NULL; + goto out; + } + + msg = spnego_key->payload.data[0]; + /* + * check version field to make sure that cifs.upcall is + * sending us a response in an expected form + */ + if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { + cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n", + CIFS_SPNEGO_UPCALL_VERSION, msg->version); + rc = -EKEYREJECTED; + goto out_put_spnego_key; + } + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + /* keep session key if binding */ + if (!is_binding) { + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", + msg->sesskey_len); + rc = -ENOMEM; + goto out_put_spnego_key; + } + ses->auth_key.len = msg->sesskey_len; + } + + sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; + sess_data->iov[1].iov_len = msg->secblob_len; + + rc = SMB2_sess_sendreceive(sess_data); + if (rc) + goto out_put_spnego_key; + + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + /* keep session id and flags if binding */ + if (!is_binding) { + ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + + rc = SMB2_sess_establish_session(sess_data); +out_put_spnego_key: + key_invalidate(spnego_key); + key_put(spnego_key); + if (rc) { + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + ses->auth_key.len = 0; + } +out: + sess_data->result = rc; + sess_data->func = NULL; + SMB2_sess_free_buffer(sess_data); +} +#else +static void +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) +{ + cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); + sess_data->result = -EOPNOTSUPP; + sess_data->func = NULL; +} +#endif + +static void +SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); + +static void +SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct smb2_sess_setup_rsp *rsp = NULL; + unsigned char *ntlmssp_blob = NULL; + bool use_spnego = false; /* else use raw ntlmssp */ + u16 blob_length = 0; + bool is_binding = false; + + /* + * If memory allocation is successful, caller of this function + * frees it. + */ + ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); + if (!ses->ntlmssp) { + rc = -ENOMEM; + goto out_err; + } + ses->ntlmssp->sesskey_per_smbsess = true; + + rc = SMB2_sess_alloc_buffer(sess_data); + if (rc) + goto out_err; + + rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob, + &blob_length, ses, server, + sess_data->nls_cp); + if (rc) + goto out; + + if (use_spnego) { + /* BB eventually need to add this */ + cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); + rc = -EOPNOTSUPP; + goto out; + } + sess_data->iov[1].iov_base = ntlmssp_blob; + sess_data->iov[1].iov_len = blob_length; + + rc = SMB2_sess_sendreceive(sess_data); + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + + /* If true, rc here is expected and not an error */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && + rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) + rc = 0; + + if (rc) + goto out; + + if (offsetof(struct smb2_sess_setup_rsp, Buffer) != + le16_to_cpu(rsp->SecurityBufferOffset)) { + cifs_dbg(VFS, "Invalid security buffer offset %d\n", + le16_to_cpu(rsp->SecurityBufferOffset)); + rc = -EIO; + goto out; + } + rc = decode_ntlmssp_challenge(rsp->Buffer, + le16_to_cpu(rsp->SecurityBufferLength), ses); + if (rc) + goto out; + + cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + /* keep existing ses id and flags if binding */ + if (!is_binding) { + ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + +out: + kfree_sensitive(ntlmssp_blob); + SMB2_sess_free_buffer(sess_data); + if (!rc) { + sess_data->result = 0; + sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; + return; + } +out_err: + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + sess_data->result = rc; + sess_data->func = NULL; +} + +static void +SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct smb2_sess_setup_req *req; + struct smb2_sess_setup_rsp *rsp = NULL; + unsigned char *ntlmssp_blob = NULL; + bool use_spnego = false; /* else use raw ntlmssp */ + u16 blob_length = 0; + bool is_binding = false; + + rc = SMB2_sess_alloc_buffer(sess_data); + if (rc) + goto out; + + req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; + req->hdr.SessionId = cpu_to_le64(ses->Suid); + + rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, + ses, server, + sess_data->nls_cp); + if (rc) { + cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); + goto out; + } + + if (use_spnego) { + /* BB eventually need to add this */ + cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); + rc = -EOPNOTSUPP; + goto out; + } + sess_data->iov[1].iov_base = ntlmssp_blob; + sess_data->iov[1].iov_len = blob_length; + + rc = SMB2_sess_sendreceive(sess_data); + if (rc) + goto out; + + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + /* keep existing ses id and flags if binding */ + if (!is_binding) { + ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + + rc = SMB2_sess_establish_session(sess_data); +#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS + if (ses->server->dialect < SMB30_PROT_ID) { + cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); + /* + * The session id is opaque in terms of endianness, so we can't + * print it as a long long. we dump it as we got it on the wire + */ + cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), + &ses->Suid); + cifs_dbg(VFS, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); + cifs_dbg(VFS, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, ses->auth_key.response); + } +#endif +out: + kfree_sensitive(ntlmssp_blob); + SMB2_sess_free_buffer(sess_data); + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + sess_data->result = rc; + sess_data->func = NULL; +} + +static int +SMB2_select_sec(struct SMB2_sess_data *sess_data) +{ + int type; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + type = smb2_select_sectype(server, ses->sectype); + cifs_dbg(FYI, "sess setup type %d\n", type); + if (type == Unspecified) { + cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); + return -EINVAL; + } + + switch (type) { + case Kerberos: + sess_data->func = SMB2_auth_kerberos; + break; + case RawNTLMSSP: + sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; + break; + default: + cifs_dbg(VFS, "secType %d not supported!\n", type); + return -EOPNOTSUPP; + } + + return 0; +} + +int +SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + struct SMB2_sess_data *sess_data; + + cifs_dbg(FYI, "Session Setup\n"); + + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; + } + + sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); + if (!sess_data) + return -ENOMEM; + + sess_data->xid = xid; + sess_data->ses = ses; + sess_data->server = server; + sess_data->buf0_type = CIFS_NO_BUFFER; + sess_data->nls_cp = (struct nls_table *) nls_cp; + sess_data->previous_session = ses->Suid; + + rc = SMB2_select_sec(sess_data); + if (rc) + goto out; + + /* + * Initialize the session hash with the server one. + */ + memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, + SMB2_PREAUTH_HASH_SIZE); + + while (sess_data->func) + sess_data->func(sess_data); + + if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) + cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); + rc = sess_data->result; +out: + kfree_sensitive(sess_data); + return rc; +} + +int +SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) +{ + struct smb_rqst rqst; + struct smb2_logoff_req *req; /* response is also trivial struct */ + int rc = 0; + struct TCP_Server_Info *server; + int flags = 0; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + cifs_dbg(FYI, "disconnect session %p\n", ses); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + /* no need to send SMB logoff if uid already closed due to reconnect */ + spin_lock(&ses->chan_lock); + if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { + spin_unlock(&ses->chan_lock); + goto smb2_session_already_dead; + } + spin_unlock(&ses->chan_lock); + + rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, + (void **) &req, &total_len); + if (rc) + return rc; + + /* since no tcon, smb2_init can not do this, so do here */ + req->hdr.SessionId = cpu_to_le64(ses->Suid); + + if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) + flags |= CIFS_TRANSFORM_REQ; + else if (server->sign) + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, ses->server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + /* + * No tcon so can't do + * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); + */ + +smb2_session_already_dead: + return rc; +} + +static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) +{ + cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); +} + +#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) + +/* These are similar values to what Windows uses */ +static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) +{ + tcon->max_chunks = 256; + tcon->max_bytes_chunk = 1048576; + tcon->max_bytes_copy = 16777216; +} + +int +SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, + struct cifs_tcon *tcon, const struct nls_table *cp) +{ + struct smb_rqst rqst; + struct smb2_tree_connect_req *req; + struct smb2_tree_connect_rsp *rsp = NULL; + struct kvec iov[2]; + struct kvec rsp_iov = { NULL, 0 }; + int rc = 0; + int resp_buftype; + int unc_path_len; + __le16 *unc_path = NULL; + int flags = 0; + unsigned int total_len; + struct TCP_Server_Info *server; + + /* always use master channel */ + server = ses->server; + + cifs_dbg(FYI, "TCON\n"); + + if (!server || !tree) + return -EIO; + + unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); + if (unc_path == NULL) + return -ENOMEM; + + unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; + unc_path_len *= 2; + if (unc_path_len < 2) { + kfree(unc_path); + return -EINVAL; + } + + /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ + tcon->tid = 0; + atomic_set(&tcon->num_remote_opens, 0); + rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server, + (void **) &req, &total_len); + if (rc) { + kfree(unc_path); + return rc; + } + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + iov[0].iov_base = (char *)req; + /* 1 for pad */ + iov[0].iov_len = total_len - 1; + + /* Testing shows that buffer offset must be at location of Buffer[0] */ + req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) + - 1 /* pad */); + req->PathLength = cpu_to_le16(unc_path_len - 2); + iov[1].iov_base = unc_path; + iov[1].iov_len = unc_path_len; + + /* + * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 + * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 + * (Samba servers don't always set the flag so also check if null user) + */ + if ((server->dialect == SMB311_PROT_ID) && + !smb3_encryption_required(tcon) && + !(ses->session_flags & + (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && + ((ses->user_name != NULL) || (ses->sectype == Kerberos))) + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + /* Need 64 for max size write so ask for more in case not there yet */ + req->hdr.CreditRequest = cpu_to_le16(64); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(req); + rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; + trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); + if ((rc != 0) || (rsp == NULL)) { + cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); + tcon->need_reconnect = true; + goto tcon_error_exit; + } + + switch (rsp->ShareType) { + case SMB2_SHARE_TYPE_DISK: + cifs_dbg(FYI, "connection to disk share\n"); + break; + case SMB2_SHARE_TYPE_PIPE: + tcon->pipe = true; + cifs_dbg(FYI, "connection to pipe share\n"); + break; + case SMB2_SHARE_TYPE_PRINT: + tcon->print = true; + cifs_dbg(FYI, "connection to printer\n"); + break; + default: + cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); + rc = -EOPNOTSUPP; + goto tcon_error_exit; + } + + tcon->share_flags = le32_to_cpu(rsp->ShareFlags); + tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ + tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); + tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); + strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); + + if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && + ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) + cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); + + if (tcon->seal && + !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); + + init_copy_chunk_defaults(tcon); + if (server->ops->validate_negotiate) + rc = server->ops->validate_negotiate(xid, tcon); +tcon_exit: + + free_rsp_buf(resp_buftype, rsp); + kfree(unc_path); + return rc; + +tcon_error_exit: + if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) + cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); + goto tcon_exit; +} + +int +SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) +{ + struct smb_rqst rqst; + struct smb2_tree_disconnect_req *req; /* response is trivial */ + int rc = 0; + struct cifs_ses *ses = tcon->ses; + int flags = 0; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + cifs_dbg(FYI, "Tree Disconnect\n"); + + if (!ses || !(ses->server)) + return -EIO; + + trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name); + spin_lock(&ses->chan_lock); + if ((tcon->need_reconnect) || + (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) { + spin_unlock(&ses->chan_lock); + return 0; + } + spin_unlock(&ses->chan_lock); + + invalidate_all_cached_dirs(tcon); + + rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server, + (void **) &req, + &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, ses->server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); + trace_smb3_tdis_err(xid, tcon->tid, ses->Suid, rc); + } + trace_smb3_tdis_done(xid, tcon->tid, ses->Suid); + + return rc; +} + + +static struct create_durable * +create_durable_buf(void) +{ + struct create_durable *buf; + + buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable, Data)); + buf->ccontext.DataLength = cpu_to_le32(16); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'Q'; + return buf; +} + +static struct create_durable * +create_reconnect_durable_buf(struct cifs_fid *fid) +{ + struct create_durable *buf; + + buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable, Data)); + buf->ccontext.DataLength = cpu_to_le32(16); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Data.Fid.PersistentFileId = fid->persistent_fid; + buf->Data.Fid.VolatileFileId = fid->volatile_fid; + /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'C'; + return buf; +} + +static void +parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) +{ + struct create_on_disk_id *pdisk_id = (struct create_on_disk_id *)cc; + + cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", + pdisk_id->DiskFileId, pdisk_id->VolumeId); + buf->IndexNumber = pdisk_id->DiskFileId; +} + +static void +parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, + struct create_posix_rsp *posix) +{ + int sid_len; + u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); + u8 *end = beg + le32_to_cpu(cc->DataLength); + u8 *sid; + + memset(posix, 0, sizeof(*posix)); + + posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); + posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); + posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); + + sid = beg + 12; + sid_len = posix_info_sid_size(sid, end); + if (sid_len < 0) { + cifs_dbg(VFS, "bad owner sid in posix create response\n"); + return; + } + memcpy(&posix->owner, sid, sid_len); + + sid = sid + sid_len; + sid_len = posix_info_sid_size(sid, end); + if (sid_len < 0) { + cifs_dbg(VFS, "bad group sid in posix create response\n"); + return; + } + memcpy(&posix->group, sid, sid_len); + + cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", + posix->nlink, posix->mode, posix->reparse_tag); +} + +void +smb2_parse_contexts(struct TCP_Server_Info *server, + struct smb2_create_rsp *rsp, + unsigned int *epoch, char *lease_key, __u8 *oplock, + struct smb2_file_all_info *buf, + struct create_posix_rsp *posix) +{ + char *data_offset; + struct create_context *cc; + unsigned int next; + unsigned int remaining; + char *name; + static const char smb3_create_tag_posix[] = { + 0x93, 0xAD, 0x25, 0x50, 0x9C, + 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, + 0xDE, 0x96, 0x8B, 0xCD, 0x7C + }; + + *oplock = 0; + data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); + remaining = le32_to_cpu(rsp->CreateContextsLength); + cc = (struct create_context *)data_offset; + + /* Initialize inode number to 0 in case no valid data in qfid context */ + if (buf) + buf->IndexNumber = 0; + + while (remaining >= sizeof(struct create_context)) { + name = le16_to_cpu(cc->NameOffset) + (char *)cc; + if (le16_to_cpu(cc->NameLength) == 4 && + strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4) == 0) + *oplock = server->ops->parse_lease_buf(cc, epoch, + lease_key); + else if (buf && (le16_to_cpu(cc->NameLength) == 4) && + strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) + parse_query_id_ctxt(cc, buf); + else if ((le16_to_cpu(cc->NameLength) == 16)) { + if (posix && + memcmp(name, smb3_create_tag_posix, 16) == 0) + parse_posix_ctxt(cc, buf, posix); + } + /* else { + cifs_dbg(FYI, "Context not matched with len %d\n", + le16_to_cpu(cc->NameLength)); + cifs_dump_mem("Cctxt name: ", name, 4); + } */ + + next = le32_to_cpu(cc->Next); + if (!next) + break; + remaining -= next; + cc = (struct create_context *)((char *)cc + next); + } + + if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) + *oplock = rsp->OplockLevel; + + return; +} + +static int +add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, + unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = server->vals->create_lease_size; + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, + server->vals->create_lease_size); + *num_iovec = num + 1; + return 0; +} + +static struct create_durable_v2 * +create_durable_v2_buf(struct cifs_open_parms *oparms) +{ + struct cifs_fid *pfid = oparms->fid; + struct create_durable_v2 *buf; + + buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_v2, dcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + + /* + * NB: Handle timeout defaults to 0, which allows server to choose + * (most servers default to 120 seconds) and most clients default to 0. + * This can be overridden at mount ("handletimeout=") if the user wants + * a different persistent (or resilient) handle timeout for all opens + * opens on a particular SMB3 mount. + */ + buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); + buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); + generate_random_uuid(buf->dcontext.CreateGuid); + memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); + + /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'Q'; + return buf; +} + +static struct create_durable_handle_reconnect_v2 * +create_reconnect_durable_v2_buf(struct cifs_fid *fid) +{ + struct create_durable_handle_reconnect_v2 *buf; + + buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), + GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = + cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, + dcontext)); + buf->ccontext.DataLength = + cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); + buf->ccontext.NameOffset = + cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, + Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + + buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; + buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; + buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); + memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); + + /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'C'; + return buf; +} + +static int +add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, + struct cifs_open_parms *oparms) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_durable_v2_buf(oparms); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_durable_v2); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = + cpu_to_le32(sizeof(struct smb2_create_req) + + iov[1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable_v2)); + *num_iovec = num + 1; + return 0; +} + +static int +add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, + struct cifs_open_parms *oparms) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + /* indicate that we don't need to relock the file */ + oparms->reconnect = false; + + iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = + cpu_to_le32(sizeof(struct smb2_create_req) + + iov[1].iov_len); + le32_add_cpu(&req->CreateContextsLength, + sizeof(struct create_durable_handle_reconnect_v2)); + *num_iovec = num + 1; + return 0; +} + +static int +add_durable_context(struct kvec *iov, unsigned int *num_iovec, + struct cifs_open_parms *oparms, bool use_persistent) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + if (use_persistent) { + if (oparms->reconnect) + return add_durable_reconnect_v2_context(iov, num_iovec, + oparms); + else + return add_durable_v2_context(iov, num_iovec, oparms); + } + + if (oparms->reconnect) { + iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); + /* indicate that we don't need to relock the file */ + oparms->reconnect = false; + } else + iov[num].iov_base = create_durable_buf(); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_durable); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = + cpu_to_le32(sizeof(struct smb2_create_req) + + iov[1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable)); + *num_iovec = num + 1; + return 0; +} + +/* See MS-SMB2 2.2.13.2.7 */ +static struct crt_twarp_ctxt * +create_twarp_buf(__u64 timewarp) +{ + struct crt_twarp_ctxt *buf; + + buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct crt_twarp_ctxt, Timestamp)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct crt_twarp_ctxt, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ + buf->Name[0] = 'T'; + buf->Name[1] = 'W'; + buf->Name[2] = 'r'; + buf->Name[3] = 'p'; + buf->Timestamp = cpu_to_le64(timewarp); + return buf; +} + +/* See MS-SMB2 2.2.13.2.7 */ +static int +add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_twarp_buf(timewarp); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct crt_twarp_ctxt); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_twarp_ctxt)); + *num_iovec = num + 1; + return 0; +} + +/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ +static void setup_owner_group_sids(char *buf) +{ + struct owner_group_sids *sids = (struct owner_group_sids *)buf; + + /* Populate the user ownership fields S-1-5-88-1 */ + sids->owner.Revision = 1; + sids->owner.NumAuth = 3; + sids->owner.Authority[5] = 5; + sids->owner.SubAuthorities[0] = cpu_to_le32(88); + sids->owner.SubAuthorities[1] = cpu_to_le32(1); + sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); + + /* Populate the group ownership fields S-1-5-88-2 */ + sids->group.Revision = 1; + sids->group.NumAuth = 3; + sids->group.Authority[5] = 5; + sids->group.SubAuthorities[0] = cpu_to_le32(88); + sids->group.SubAuthorities[1] = cpu_to_le32(2); + sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); + + cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val); +} + +/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ +static struct crt_sd_ctxt * +create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) +{ + struct crt_sd_ctxt *buf; + __u8 *ptr, *aclptr; + unsigned int acelen, acl_size, ace_count; + unsigned int owner_offset = 0; + unsigned int group_offset = 0; + struct smb3_acl acl = {}; + + *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); + + if (set_owner) { + /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ + *len += sizeof(struct owner_group_sids); + } + + buf = kzalloc(*len, GFP_KERNEL); + if (buf == NULL) + return buf; + + ptr = (__u8 *)&buf[1]; + if (set_owner) { + /* offset fields are from beginning of security descriptor not of create context */ + owner_offset = ptr - (__u8 *)&buf->sd; + buf->sd.OffsetOwner = cpu_to_le32(owner_offset); + group_offset = owner_offset + offsetof(struct owner_group_sids, group); + buf->sd.OffsetGroup = cpu_to_le32(group_offset); + + setup_owner_group_sids(ptr); + ptr += sizeof(struct owner_group_sids); + } else { + buf->sd.OffsetOwner = 0; + buf->sd.OffsetGroup = 0; + } + + buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ + buf->Name[0] = 'S'; + buf->Name[1] = 'e'; + buf->Name[2] = 'c'; + buf->Name[3] = 'D'; + buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ + + /* + * ACL is "self relative" ie ACL is stored in contiguous block of memory + * and "DP" ie the DACL is present + */ + buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); + + /* offset owner, group and Sbz1 and SACL are all zero */ + buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); + /* Ship the ACL for now. we will copy it into buf later. */ + aclptr = ptr; + ptr += sizeof(struct smb3_acl); + + /* create one ACE to hold the mode embedded in reserved special SID */ + acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode); + ptr += acelen; + acl_size = acelen + sizeof(struct smb3_acl); + ace_count = 1; + + if (set_owner) { + /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ + acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr); + ptr += acelen; + acl_size += acelen; + ace_count += 1; + } + + /* and one more ACE to allow access for authenticated users */ + acelen = setup_authusers_ACE((struct cifs_ace *)ptr); + ptr += acelen; + acl_size += acelen; + ace_count += 1; + + acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ + acl.AclSize = cpu_to_le16(acl_size); + acl.AceCount = cpu_to_le16(ace_count); + /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */ + memcpy(aclptr, &acl, sizeof(struct smb3_acl)); + + buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); + *len = round_up((unsigned int)(ptr - (__u8 *)buf), 8); + + return buf; +} + +static int +add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + unsigned int len = 0; + + iov[num].iov_base = create_sd_buf(mode, set_owner, &len); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = len; + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, len); + *num_iovec = num + 1; + return 0; +} + +static struct crt_query_id_ctxt * +create_query_id_buf(void) +{ + struct crt_query_id_ctxt *buf; + + buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(0); + buf->ccontext.DataLength = cpu_to_le32(0); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct crt_query_id_ctxt, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */ + buf->Name[0] = 'Q'; + buf->Name[1] = 'F'; + buf->Name[2] = 'i'; + buf->Name[3] = 'd'; + return buf; +} + +/* See MS-SMB2 2.2.13.2.9 */ +static int +add_query_id_context(struct kvec *iov, unsigned int *num_iovec) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_query_id_buf(); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct crt_query_id_ctxt); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_query_id_ctxt)); + *num_iovec = num + 1; + return 0; +} + +static int +alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, + const char *treename, const __le16 *path) +{ + int treename_len, path_len; + struct nls_table *cp; + const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; + + /* + * skip leading "\\" + */ + treename_len = strlen(treename); + if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) + return -EINVAL; + + treename += 2; + treename_len -= 2; + + path_len = UniStrnlen((wchar_t *)path, PATH_MAX); + + /* make room for one path separator only if @path isn't empty */ + *out_len = treename_len + (path[0] ? 1 : 0) + path_len; + + /* + * final path needs to be 8-byte aligned as specified in + * MS-SMB2 2.2.13 SMB2 CREATE Request. + */ + *out_size = round_up(*out_len * sizeof(__le16), 8); + *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); + if (!*out_path) + return -ENOMEM; + + cp = load_nls_default(); + cifs_strtoUTF16(*out_path, treename, treename_len, cp); + + /* Do not append the separator if the path is empty */ + if (path[0] != cpu_to_le16(0x0000)) { + UniStrcat(*out_path, sep); + UniStrcat(*out_path, path); + } + + unload_nls(cp); + + return 0; +} + +int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb) +{ + struct smb_rqst rqst; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp = NULL; + struct cifs_ses *ses = tcon->ses; + struct kvec iov[3]; /* make sure at least one for each open context */ + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype; + int uni_path_len; + __le16 *copy_path = NULL; + int copy_size; + int rc = 0; + unsigned int n_iov = 2; + __u32 file_attributes = 0; + char *pc_buf = NULL; + int flags = 0; + unsigned int total_len; + __le16 *utf16_path = NULL; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "mkdir\n"); + + /* resource #1: path allocation */ + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + if (!ses || !server) { + rc = -EIO; + goto err_free_path; + } + + /* resource #2: request */ + rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, + (void **) &req, &total_len); + if (rc) + goto err_free_path; + + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->ImpersonationLevel = IL_IMPERSONATION; + req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); + /* File attributes ignored on open (used in create though) */ + req->FileAttributes = cpu_to_le32(file_attributes); + req->ShareAccess = FILE_SHARE_ALL_LE; + req->CreateDisposition = cpu_to_le32(FILE_CREATE); + req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); + + iov[0].iov_base = (char *)req; + /* -1 since last byte is buf[0] which is sent below (path) */ + iov[0].iov_len = total_len - 1; + + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); + + /* [MS-SMB2] 2.2.13 NameOffset: + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of + * the SMB2 header, the file name includes a prefix that will + * be processed during DFS name normalization as specified in + * section 3.3.5.9. Otherwise, the file name is relative to + * the share that is identified by the TreeId in the SMB2 + * header. + */ + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + + req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->tree_name, utf16_path); + if (rc) + goto err_free_req; + + req->NameLength = cpu_to_le16(name_len * 2); + uni_path_len = copy_size; + /* free before overwriting resource */ + kfree(utf16_path); + utf16_path = copy_path; + } else { + uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2; + /* MUST set path len (NameLength) to 0 opening root of share */ + req->NameLength = cpu_to_le16(uni_path_len - 2); + if (uni_path_len % 8 != 0) { + copy_size = roundup(uni_path_len, 8); + copy_path = kzalloc(copy_size, GFP_KERNEL); + if (!copy_path) { + rc = -ENOMEM; + goto err_free_req; + } + memcpy((char *)copy_path, (const char *)utf16_path, + uni_path_len); + uni_path_len = copy_size; + /* free before overwriting resource */ + kfree(utf16_path); + utf16_path = copy_path; + } + } + + iov[1].iov_len = uni_path_len; + iov[1].iov_base = utf16_path; + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; + + if (tcon->posix_extensions) { + /* resource #3: posix buf */ + rc = add_posix_context(iov, &n_iov, mode); + if (rc) + goto err_free_req; + pc_buf = iov[n_iov-1].iov_base; + } + + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_iov; + + /* no need to inc num_remote_opens because we close it just below */ + trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, + FILE_WRITE_ATTRIBUTES); + /* resource #4: response buffer */ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); + trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, + CREATE_NOT_FILE, + FILE_WRITE_ATTRIBUTES, rc); + goto err_free_rsp_buf; + } + + /* + * Although unlikely to be possible for rsp to be null and rc not set, + * adding check below is slightly safer long term (and quiets Coverity + * warning) + */ + rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; + if (rsp == NULL) { + rc = -EIO; + kfree(pc_buf); + goto err_free_req; + } + + trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, + CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); + + SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); + + /* Eventually save off posix specific response info and timestaps */ + +err_free_rsp_buf: + free_rsp_buf(resp_buftype, rsp); + kfree(pc_buf); +err_free_req: + cifs_small_buf_release(req); +err_free_path: + kfree(utf16_path); + return rc; +} + +int +SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, __u8 *oplock, + struct cifs_open_parms *oparms, __le16 *path) +{ + struct smb2_create_req *req; + unsigned int n_iov = 2; + __u32 file_attributes = 0; + int copy_size; + int uni_path_len; + unsigned int total_len; + struct kvec *iov = rqst->rq_iov; + __le16 *copy_path; + int rc; + + rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + iov[0].iov_base = (char *)req; + /* -1 since last byte is buf[0] which is sent below (path) */ + iov[0].iov_len = total_len - 1; + + if (oparms->create_options & CREATE_OPTION_READONLY) + file_attributes |= ATTR_READONLY; + if (oparms->create_options & CREATE_OPTION_SPECIAL) + file_attributes |= ATTR_SYSTEM; + + req->ImpersonationLevel = IL_IMPERSONATION; + req->DesiredAccess = cpu_to_le32(oparms->desired_access); + /* File attributes ignored on open (used in create though) */ + req->FileAttributes = cpu_to_le32(file_attributes); + req->ShareAccess = FILE_SHARE_ALL_LE; + + req->CreateDisposition = cpu_to_le32(oparms->disposition); + req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); + + /* [MS-SMB2] 2.2.13 NameOffset: + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of + * the SMB2 header, the file name includes a prefix that will + * be processed during DFS name normalization as specified in + * section 3.3.5.9. Otherwise, the file name is relative to + * the share that is identified by the TreeId in the SMB2 + * header. + */ + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + + req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->tree_name, path); + if (rc) + return rc; + req->NameLength = cpu_to_le16(name_len * 2); + uni_path_len = copy_size; + path = copy_path; + } else { + uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; + /* MUST set path len (NameLength) to 0 opening root of share */ + req->NameLength = cpu_to_le16(uni_path_len - 2); + copy_size = round_up(uni_path_len, 8); + copy_path = kzalloc(copy_size, GFP_KERNEL); + if (!copy_path) + return -ENOMEM; + memcpy((char *)copy_path, (const char *)path, + uni_path_len); + uni_path_len = copy_size; + path = copy_path; + } + + iov[1].iov_len = uni_path_len; + iov[1].iov_base = path; + + if ((!server->oplocks) || (tcon->no_lease)) + *oplock = SMB2_OPLOCK_LEVEL_NONE; + + if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || + *oplock == SMB2_OPLOCK_LEVEL_NONE) + req->RequestedOplockLevel = *oplock; + else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && + (oparms->create_options & CREATE_NOT_FILE)) + req->RequestedOplockLevel = *oplock; /* no srv lease support */ + else { + rc = add_lease_context(server, iov, &n_iov, + oparms->fid->lease_key, oplock); + if (rc) + return rc; + } + + if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { + /* need to set Next field of lease context if we request it */ + if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(server->vals->create_lease_size); + } + + rc = add_durable_context(iov, &n_iov, oparms, + tcon->use_persistent); + if (rc) + return rc; + } + + if (tcon->posix_extensions) { + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(iov[n_iov-1].iov_len); + } + + rc = add_posix_context(iov, &n_iov, oparms->mode); + if (rc) + return rc; + } + + if (tcon->snapshot_time) { + cifs_dbg(FYI, "adding snapshot context\n"); + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(iov[n_iov-1].iov_len); + } + + rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); + if (rc) + return rc; + } + + if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { + bool set_mode; + bool set_owner; + + if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && + (oparms->mode != ACL_NO_MODE)) + set_mode = true; + else { + set_mode = false; + oparms->mode = ACL_NO_MODE; + } + + if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + set_owner = true; + else + set_owner = false; + + if (set_owner | set_mode) { + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); + } + + cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); + rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); + if (rc) + return rc; + } + } + + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); + } + add_query_id_context(iov, &n_iov); + + rqst->rq_nvec = n_iov; + return 0; +} + +/* rq_iov[0] is the request and is released by cifs_small_buf_release(). + * All other vectors are freed by kfree(). + */ +void +SMB2_open_free(struct smb_rqst *rqst) +{ + int i; + + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); + for (i = 1; i < rqst->rq_nvec; i++) + if (rqst->rq_iov[i].iov_base != smb2_padding) + kfree(rqst->rq_iov[i].iov_base); + } +} + +int +SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, + struct kvec *err_iov, int *buftype) +{ + struct smb_rqst rqst; + struct smb2_create_rsp *rsp = NULL; + struct cifs_tcon *tcon = oparms->tcon; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct kvec iov[SMB2_CREATE_IOV_SIZE]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; + + cifs_dbg(FYI, "create/open\n"); + if (!ses || !server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; + + rc = SMB2_open_init(tcon, server, + &rqst, oplock, oparms, path); + if (rc) + goto creat_exit; + + trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, + oparms->create_options, oparms->desired_access); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); + rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); + if (err_iov && rsp) { + *err_iov = rsp_iov; + *buftype = resp_buftype; + resp_buftype = CIFS_NO_BUFFER; + rsp = NULL; + } + trace_smb3_open_err(xid, tcon->tid, ses->Suid, + oparms->create_options, oparms->desired_access, rc); + if (rc == -EREMCHG) { + pr_warn_once("server share %s deleted\n", + tcon->tree_name); + tcon->need_reconnect = true; + } + goto creat_exit; + } else if (rsp == NULL) /* unlikely to happen, but safer to check */ + goto creat_exit; + else + trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, + oparms->create_options, oparms->desired_access); + + atomic_inc(&tcon->num_remote_opens); + oparms->fid->persistent_fid = rsp->PersistentFileId; + oparms->fid->volatile_fid = rsp->VolatileFileId; + oparms->fid->access = oparms->desired_access; +#ifdef CONFIG_CIFS_DEBUG2 + oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); +#endif /* CIFS_DEBUG2 */ + + if (buf) { + buf->CreationTime = rsp->CreationTime; + buf->LastAccessTime = rsp->LastAccessTime; + buf->LastWriteTime = rsp->LastWriteTime; + buf->ChangeTime = rsp->ChangeTime; + buf->AllocationSize = rsp->AllocationSize; + buf->EndOfFile = rsp->EndofFile; + buf->Attributes = rsp->FileAttributes; + buf->NumberOfLinks = cpu_to_le32(1); + buf->DeletePending = 0; + } + + + smb2_parse_contexts(server, rsp, &oparms->fid->epoch, + oparms->fid->lease_key, oplock, buf, posix); +creat_exit: + SMB2_open_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int +SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + char *in_data, u32 indatalen, + __u32 max_response_size) +{ + struct smb2_ioctl_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + char *in_data_buf; + + rc = smb2_ioctl_req_init(opcode, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (indatalen) { + /* + * indatalen is usually small at a couple of bytes max, so + * just allocate through generic pool + */ + in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); + if (!in_data_buf) { + cifs_small_buf_release(req); + return -ENOMEM; + } + } + + req->CtlCode = cpu_to_le32(opcode); + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + iov[0].iov_base = (char *)req; + /* + * If no input data, the size of ioctl struct in + * protocol spec still includes a 1 byte data buffer, + * but if input data passed to ioctl, we do not + * want to double count this, so we do not send + * the dummy one byte of data in iovec[0] if sending + * input data (in iovec[1]). + */ + if (indatalen) { + req->InputCount = cpu_to_le32(indatalen); + /* do not set InputOffset if no input data */ + req->InputOffset = + cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); + rqst->rq_nvec = 2; + iov[0].iov_len = total_len - 1; + iov[1].iov_base = in_data_buf; + iov[1].iov_len = indatalen; + } else { + rqst->rq_nvec = 1; + iov[0].iov_len = total_len; + } + + req->OutputOffset = 0; + req->OutputCount = 0; /* MBZ */ + + /* + * In most cases max_response_size is set to 16K (CIFSMaxBufSize) + * We Could increase default MaxOutputResponse, but that could require + * more credits. Windows typically sets this smaller, but for some + * ioctls it may be useful to allow server to send more. No point + * limiting what the server can send as long as fits in one credit + * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want + * to increase this limit up in the future. + * Note that for snapshot queries that servers like Azure expect that + * the first query be minimal size (and just used to get the number/size + * of previous versions) so response size must be specified as EXACTLY + * sizeof(struct snapshot_array) which is 16 when rounded up to multiple + * of eight bytes. Currently that is the only case where we set max + * response size smaller. + */ + req->MaxOutputResponse = cpu_to_le32(max_response_size); + req->hdr.CreditCharge = + cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), + SMB2_MAX_BUFFER_SIZE)); + /* always an FSCTL (for now) */ + req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); + + /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ + if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + return 0; +} + +void +SMB2_ioctl_free(struct smb_rqst *rqst) +{ + int i; + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ + for (i = 1; i < rqst->rq_nvec; i++) + if (rqst->rq_iov[i].iov_base != smb2_padding) + kfree(rqst->rq_iov[i].iov_base); + } +} + + +/* + * SMB2 IOCTL is used for both IOCTLs and FSCTLs + */ +int +SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, + u32 max_out_data_len, char **out_data, + u32 *plen /* returned data len */) +{ + struct smb_rqst rqst; + struct smb2_ioctl_rsp *rsp = NULL; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct kvec iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; + + cifs_dbg(FYI, "SMB2 IOCTL\n"); + + if (out_data != NULL) + *out_data = NULL; + + /* zero out returned data len, in case of error */ + if (plen) + *plen = 0; + + if (!tcon) + return -EIO; + + ses = tcon->ses; + if (!ses) + return -EIO; + + server = cifs_pick_channel(ses); + if (!server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, + &rqst, persistent_fid, volatile_fid, opcode, + in_data, indatalen, max_out_data_len); + if (rc) + goto ioctl_exit; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); + rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; + + if (rc != 0) + trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, + ses->Suid, 0, opcode, rc); + + if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) { + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } else if (rc == -EINVAL) { + if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && + (opcode != FSCTL_SRV_COPYCHUNK)) { + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } + } else if (rc == -E2BIG) { + if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) { + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } + } + + /* check if caller wants to look at return data or just return rc */ + if ((plen == NULL) || (out_data == NULL)) + goto ioctl_exit; + + /* + * Although unlikely to be possible for rsp to be null and rc not set, + * adding check below is slightly safer long term (and quiets Coverity + * warning) + */ + if (rsp == NULL) { + rc = -EIO; + goto ioctl_exit; + } + + *plen = le32_to_cpu(rsp->OutputCount); + + /* We check for obvious errors in the output buffer length and offset */ + if (*plen == 0) + goto ioctl_exit; /* server returned no data */ + else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { + cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { + cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, + le32_to_cpu(rsp->OutputOffset)); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), + *plen, GFP_KERNEL); + if (*out_data == NULL) { + rc = -ENOMEM; + goto ioctl_exit; + } + +ioctl_exit: + SMB2_ioctl_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +/* + * Individual callers to ioctl worker function follow + */ + +int +SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + int rc; + struct compress_ioctl fsctl_input; + char *ret_data = NULL; + + fsctl_input.CompressionState = + cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + + rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, + FSCTL_SET_COMPRESSION, + (char *)&fsctl_input /* data input */, + 2 /* in data len */, CIFSMaxBufSize /* max out data */, + &ret_data /* out data */, NULL); + + cifs_dbg(FYI, "set compression rc %d\n", rc); + + return rc; +} + +int +SMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, bool query_attrs) +{ + struct smb2_close_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + if (query_attrs) + req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + else + req->Flags = 0; + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +void +SMB2_close_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +int +__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_network_open_info *pbuf) +{ + struct smb_rqst rqst; + struct smb2_close_rsp *rsp = NULL; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; + bool query_attrs = false; + + cifs_dbg(FYI, "Close\n"); + + if (!ses || !server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + /* check if need to ask server to return timestamps in close response */ + if (pbuf) + query_attrs = true; + + trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); + rc = SMB2_close_init(tcon, server, + &rqst, persistent_fid, volatile_fid, + query_attrs); + if (rc) + goto close_exit; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); + trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, + rc); + goto close_exit; + } else { + trace_smb3_close_done(xid, persistent_fid, tcon->tid, + ses->Suid); + /* + * Note that have to subtract 4 since struct network_open_info + * has a final 4 byte pad that close response does not have + */ + if (pbuf) + memcpy(pbuf, (char *)&rsp->CreationTime, sizeof(*pbuf) - 4); + } + + atomic_dec(&tcon->num_remote_opens); +close_exit: + SMB2_close_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + + /* retry close in a worker thread if this one is interrupted */ + if (is_interrupt_error(rc)) { + int tmp_rc; + + tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, + volatile_fid); + if (tmp_rc) + cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", + persistent_fid, tmp_rc); + } + return rc; +} + +int +SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); +} + +int +smb2_validate_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int min_buf_size) +{ + unsigned int smb_len = iov->iov_len; + char *end_of_smb = smb_len + (char *)iov->iov_base; + char *begin_of_buf = offset + (char *)iov->iov_base; + char *end_of_buf = begin_of_buf + buffer_length; + + + if (buffer_length < min_buf_size) { + cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", + buffer_length, min_buf_size); + return -EINVAL; + } + + /* check if beyond RFC1001 maximum length */ + if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { + cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", + buffer_length, smb_len); + return -EINVAL; + } + + if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { + cifs_dbg(VFS, "Invalid server response, bad offset to data\n"); + return -EINVAL; + } + + return 0; +} + +/* + * If SMB buffer fields are valid, copy into temporary buffer to hold result. + * Caller must free buffer. + */ +int +smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int minbufsize, + char *data) +{ + char *begin_of_buf = offset + (char *)iov->iov_base; + int rc; + + if (!data) + return -EINVAL; + + rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); + if (rc) + return rc; + + memcpy(data, begin_of_buf, minbufsize); + + return 0; +} + +int +SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + u8 info_class, u8 info_type, u32 additional_info, + size_t output_len, size_t input_len, void *input) +{ + struct smb2_query_info_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->InfoType = info_type; + req->FileInfoClass = info_class; + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + req->AdditionalInformation = cpu_to_le32(additional_info); + + req->OutputBufferLength = cpu_to_le32(output_len); + if (input_len) { + req->InputBufferLength = cpu_to_le32(input_len); + /* total_len for smb query request never close to le16 max */ + req->InputBufferOffset = cpu_to_le16(total_len - 1); + memcpy(req->Buffer, input, input_len); + } + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1 + input_len; + return 0; +} + +void +SMB2_query_info_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +static int +query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, + u32 additional_info, size_t output_len, size_t min_len, void **data, + u32 *dlen) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov[1]; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype = CIFS_NO_BUFFER; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server; + int flags = 0; + bool allocated = false; + + cifs_dbg(FYI, "Query Info\n"); + + if (!ses) + return -EIO; + server = cifs_pick_channel(ses); + if (!server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, server, + &rqst, persistent_fid, volatile_fid, + info_class, info_type, additional_info, + output_len, 0, NULL); + if (rc) + goto qinf_exit; + + trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + trace_smb3_query_info_err(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type, rc); + goto qinf_exit; + } + + trace_smb3_query_info_done(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type); + + if (dlen) { + *dlen = le32_to_cpu(rsp->OutputBufferLength); + if (!*data) { + *data = kmalloc(*dlen, GFP_KERNEL); + if (!*data) { + cifs_tcon_dbg(VFS, + "Error %d allocating memory for acl\n", + rc); + *dlen = 0; + rc = -ENOMEM; + goto qinf_exit; + } + allocated = true; + } + } + + rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov, dlen ? *dlen : min_len, *data); + if (rc && allocated) { + kfree(*data); + *data = NULL; + *dlen = 0; + } + +qinf_exit: + SMB2_query_info_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) +{ + return query_info(xid, tcon, persistent_fid, volatile_fid, + FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + PATH_MAX * 2, + sizeof(struct smb2_file_all_info), (void **)&data, + NULL); +} + +#if 0 +/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ +int +SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) +{ + size_t output_len = sizeof(struct smb311_posix_qinfo *) + + (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); + *plen = 0; + + return query_info(xid, tcon, persistent_fid, volatile_fid, + SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, + output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); + /* Note caller must free "data" (passed in above). It may be allocated in query_info call */ +} +#endif + +int +SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + void **data, u32 *plen, u32 extra_info) +{ + __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | + extra_info; + *plen = 0; + + return query_info(xid, tcon, persistent_fid, volatile_fid, + 0, SMB2_O_INFO_SECURITY, additional_info, + SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); +} + +int +SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) +{ + return query_info(xid, tcon, persistent_fid, volatile_fid, + FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_internal_info), + sizeof(struct smb2_file_internal_info), + (void **)&uniqueid, NULL); +} + +/* + * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory + * See MS-SMB2 2.2.35 and 2.2.36 + */ + +static int +SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, struct TCP_Server_Info *server, + u64 persistent_fid, u64 volatile_fid, + u32 completion_filter, bool watch_tree) +{ + struct smb2_change_notify_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + /* See note 354 of MS-SMB2, 64K max */ + req->OutputBufferLength = + cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); + req->CompletionFilter = cpu_to_le32(completion_filter); + if (watch_tree) + req->Flags = cpu_to_le16(SMB2_WATCH_TREE); + else + req->Flags = 0; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +int +SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter, u32 max_out_data_len, char **out_data, + u32 *plen /* returned data len */) +{ + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct smb_rqst rqst; + struct smb2_change_notify_rsp *smb_rsp; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "change notify\n"); + if (!ses || !server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + if (plen) + *plen = 0; + + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_notify_init(xid, &rqst, tcon, server, + persistent_fid, volatile_fid, + completion_filter, watch_tree); + if (rc) + goto cnotify_exit; + + trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter); + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); + trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter, rc); + } else { + trace_smb3_notify_done(xid, persistent_fid, tcon->tid, + ses->Suid, (u8)watch_tree, completion_filter); + /* validate that notify information is plausible */ + if ((rsp_iov.iov_base == NULL) || + (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp))) + goto cnotify_exit; + + smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; + + smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), + le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov, + sizeof(struct file_notify_information)); + + *out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset), + le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL); + if (*out_data == NULL) { + rc = -ENOMEM; + goto cnotify_exit; + } else if (plen) + *plen = le32_to_cpu(smb_rsp->OutputBufferLength); + } + + cnotify_exit: + if (rqst.rq_iov) + cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + + + +/* + * This is a no-op for now. We're not really interested in the reply, but + * rather in the fact that the server sent one and that server->lstrp + * gets updated. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +smb2_echo_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->callback_data; + struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + + if (mid->mid_state == MID_RESPONSE_RECEIVED + || mid->mid_state == MID_RESPONSE_MALFORMED) { + credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + } + + release_mid(mid); + add_credits(server, &credits, CIFS_ECHO_OP); +} + +void smb2_reconnect_server(struct work_struct *work) +{ + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, reconnect.work); + struct TCP_Server_Info *pserver; + struct cifs_ses *ses, *ses2; + struct cifs_tcon *tcon, *tcon2; + struct list_head tmp_list, tmp_ses_list; + bool tcon_exist = false, ses_exist = false; + bool tcon_selected = false; + int rc; + bool resched = false; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ + mutex_lock(&pserver->reconnect_mutex); + + INIT_LIST_HEAD(&tmp_list); + INIT_LIST_HEAD(&tmp_ses_list); + cifs_dbg(FYI, "Reconnecting tcons and channels\n"); + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + + tcon_selected = false; + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->need_reconnect || tcon->need_reopen_files) { + tcon->tc_count++; + list_add_tail(&tcon->rlist, &tmp_list); + tcon_selected = tcon_exist = true; + } + } + /* + * IPC has the same lifetime as its session and uses its + * refcount. + */ + if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { + list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); + tcon_selected = tcon_exist = true; + ses->ses_count++; + } + /* + * handle the case where channel needs to reconnect + * binding session, but tcon is healthy (some other channel + * is active) + */ + spin_lock(&ses->chan_lock); + if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) { + list_add_tail(&ses->rlist, &tmp_ses_list); + ses_exist = true; + ses->ses_count++; + } + spin_unlock(&ses->chan_lock); + } + /* + * Get the reference to server struct to be sure that the last call of + * cifs_put_tcon() in the loop below won't release the server pointer. + */ + if (tcon_exist || ses_exist) + server->srv_count++; + + spin_unlock(&cifs_tcp_ses_lock); + + list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { + rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); + if (!rc) + cifs_reopen_persistent_handles(tcon); + else + resched = true; + list_del_init(&tcon->rlist); + if (tcon->ipc) + cifs_put_smb_ses(tcon->ses); + else + cifs_put_tcon(tcon); + } + + if (!ses_exist) + goto done; + + /* allocate a dummy tcon struct used for reconnect */ + tcon = tconInfoAlloc(); + if (!tcon) { + resched = true; + list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { + list_del_init(&ses->rlist); + cifs_put_smb_ses(ses); + } + goto done; + } + + tcon->status = TID_GOOD; + tcon->retry = false; + tcon->need_reconnect = false; + + /* now reconnect sessions for necessary channels */ + list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { + tcon->ses = ses; + rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); + if (rc) + resched = true; + list_del_init(&ses->rlist); + cifs_put_smb_ses(ses); + } + tconInfoFree(tcon); + +done: + cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); + if (resched) + queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); + mutex_unlock(&pserver->reconnect_mutex); + + /* now we can safely release srv struct */ + if (tcon_exist || ses_exist) + cifs_put_tcp_session(server, 1); +} + +int +SMB2_echo(struct TCP_Server_Info *server) +{ + struct smb2_echo_req *req; + int rc = 0; + struct kvec iov[1]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 1 }; + unsigned int total_len; + + cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id); + + spin_lock(&server->srv_lock); + if (server->ops->need_neg && + server->ops->need_neg(server)) { + spin_unlock(&server->srv_lock); + /* No need to send echo on newly established connections */ + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + return rc; + } + spin_unlock(&server->srv_lock); + + rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, + (void **)&req, &total_len); + if (rc) + return rc; + + req->hdr.CreditRequest = cpu_to_le16(1); + + iov[0].iov_len = total_len; + iov[0].iov_base = (char *)req; + + rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, + server, CIFS_ECHO_OP, NULL); + if (rc) + cifs_dbg(FYI, "Echo request failed: %d\n", rc); + + cifs_small_buf_release(req); + return rc; +} + +void +SMB2_flush_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +int +SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, struct TCP_Server_Info *server, + u64 persistent_fid, u64 volatile_fid) +{ + struct smb2_flush_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +int +SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid) +{ + struct cifs_ses *ses = tcon->ses; + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "flush\n"); + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_flush_init(xid, &rqst, tcon, server, + persistent_fid, volatile_fid); + if (rc) + goto flush_exit; + + trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); + trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, + rc); + } else + trace_smb3_flush_done(xid, persistent_fid, tcon->tid, + ses->Suid); + + flush_exit: + SMB2_flush_free(&rqst); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +#ifdef CONFIG_CIFS_SMB_DIRECT +static inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms) +{ + struct TCP_Server_Info *server = io_parms->server; + struct cifs_tcon *tcon = io_parms->tcon; + + /* we can only offload if we're connected */ + if (!server || !tcon) + return false; + + /* we can only offload on an rdma connection */ + if (!server->rdma || !server->smbd_conn) + return false; + + /* we don't support signed offload yet */ + if (server->sign) + return false; + + /* we don't support encrypted offload yet */ + if (smb3_encryption_required(tcon)) + return false; + + /* offload also has its overhead, so only do it if desired */ + if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) + return false; + + return true; +} +#endif /* CONFIG_CIFS_SMB_DIRECT */ + +/* + * To form a chain of read requests, any read requests after the first should + * have the end_of_chain boolean set to true. + */ +static int +smb2_new_read_req(void **buf, unsigned int *total_len, + struct cifs_io_parms *io_parms, struct cifs_readdata *rdata, + unsigned int remaining_bytes, int request_type) +{ + int rc = -EACCES; + struct smb2_read_req *req = NULL; + struct smb2_hdr *shdr; + struct TCP_Server_Info *server = io_parms->server; + + rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, + (void **) &req, total_len); + if (rc) + return rc; + + if (server == NULL) + return -ECONNABORTED; + + shdr = &req->hdr; + shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; + req->ReadChannelInfoOffset = 0; /* reserved */ + req->ReadChannelInfoLength = 0; /* reserved */ + req->Channel = 0; /* reserved */ + req->MinimumCount = 0; + req->Length = cpu_to_le32(io_parms->length); + req->Offset = cpu_to_le64(io_parms->offset); + + trace_smb3_read_enter(0 /* xid */, + io_parms->persistent_fid, + io_parms->tcon->tid, io_parms->tcon->ses->Suid, + io_parms->offset, io_parms->length); +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If we want to do a RDMA write, fill in and append + * smbd_buffer_descriptor_v1 to the end of read request + */ + if (smb3_use_rdma_offload(io_parms)) { + struct smbd_buffer_descriptor_v1 *v1; + bool need_invalidate = server->dialect == SMB30_PROT_ID; + + rdata->mr = smbd_register_mr( + server->smbd_conn, rdata->pages, + rdata->nr_pages, rdata->page_offset, + rdata->tailsz, true, need_invalidate); + if (!rdata->mr) + return -EAGAIN; + + req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; + if (need_invalidate) + req->Channel = SMB2_CHANNEL_RDMA_V1; + req->ReadChannelInfoOffset = + cpu_to_le16(offsetof(struct smb2_read_req, Buffer)); + req->ReadChannelInfoLength = + cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); + v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; + v1->offset = cpu_to_le64(rdata->mr->mr->iova); + v1->token = cpu_to_le32(rdata->mr->mr->rkey); + v1->length = cpu_to_le32(rdata->mr->mr->length); + + *total_len += sizeof(*v1) - 1; + } +#endif + if (request_type & CHAINED_REQUEST) { + if (!(request_type & END_OF_CHAIN)) { + /* next 8-byte aligned request */ + *total_len = ALIGN(*total_len, 8); + shdr->NextCommand = cpu_to_le32(*total_len); + } else /* END_OF_CHAIN */ + shdr->NextCommand = 0; + if (request_type & RELATED_REQUEST) { + shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; + /* + * Related requests use info from previous read request + * in chain. + */ + shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF); + shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF); + req->PersistentFileId = (u64)-1; + req->VolatileFileId = (u64)-1; + } + } + if (remaining_bytes > io_parms->length) + req->RemainingBytes = cpu_to_le32(remaining_bytes); + else + req->RemainingBytes = 0; + + *buf = req; + return rc; +} + +static void +smb2_readv_callback(struct mid_q_entry *mid) +{ + struct cifs_readdata *rdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct TCP_Server_Info *server = rdata->server; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rdata->iov[0].iov_base; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], + .rq_nvec = 1, }; + + if (rdata->got_bytes) { + rqst.rq_pages = rdata->pages; + rqst.rq_offset = rdata->page_offset; + rqst.rq_npages = rdata->nr_pages; + rqst.rq_pagesz = rdata->pagesz; + rqst.rq_tailsz = rdata->tailsz; + } + + WARN_ONCE(rdata->server != mid->server, + "rdata server %p != mid server %p", + rdata->server, mid->server); + + cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", + __func__, mid->mid, mid->mid_state, rdata->result, + rdata->bytes); + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + credits.value = le16_to_cpu(shdr->CreditRequest); + credits.instance = server->reconnect_instance; + /* result already set, check signature */ + if (server->sign && !mid->decrypted) { + int rc; + + rc = smb2_verify_signature(&rqst, server); + if (rc) + cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + rdata->result = -EAGAIN; + if (server->sign && rdata->got_bytes) + /* reset bytes number since we can not check a sign */ + rdata->got_bytes = 0; + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + case MID_RESPONSE_MALFORMED: + credits.value = le16_to_cpu(shdr->CreditRequest); + credits.instance = server->reconnect_instance; + fallthrough; + default: + rdata->result = -EIO; + } +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If this rdata has a memmory registered, the MR can be freed + * MR needs to be freed as soon as I/O finishes to prevent deadlock + * because they have limited number and are used for future I/Os + */ + if (rdata->mr) { + smbd_deregister_mr(rdata->mr); + rdata->mr = NULL; + } +#endif + if (rdata->result && rdata->result != -ENODATA) { + cifs_stats_fail_inc(tcon, SMB2_READ_HE); + trace_smb3_read_err(0 /* xid */, + rdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, rdata->offset, + rdata->bytes, rdata->result); + } else + trace_smb3_read_done(0 /* xid */, + rdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, + rdata->offset, rdata->got_bytes); + + queue_work(cifsiod_wq, &rdata->work); + release_mid(mid); + add_credits(server, &credits, 0); +} + +/* smb2_async_readv - send an async read, and set up mid to handle result */ +int +smb2_async_readv(struct cifs_readdata *rdata) +{ + int rc, flags = 0; + char *buf; + struct smb2_hdr *shdr; + struct cifs_io_parms io_parms; + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 1 }; + struct TCP_Server_Info *server; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + unsigned int total_len; + + cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", + __func__, rdata->offset, rdata->bytes); + + if (!rdata->server) + rdata->server = cifs_pick_channel(tcon->ses); + + io_parms.tcon = tlink_tcon(rdata->cfile->tlink); + io_parms.server = server = rdata->server; + io_parms.offset = rdata->offset; + io_parms.length = rdata->bytes; + io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; + io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; + io_parms.pid = rdata->pid; + + rc = smb2_new_read_req( + (void **) &buf, &total_len, &io_parms, rdata, 0, 0); + if (rc) + return rc; + + if (smb3_encryption_required(io_parms.tcon)) + flags |= CIFS_TRANSFORM_REQ; + + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = total_len; + + shdr = (struct smb2_hdr *)buf; + + if (rdata->credits.value > 0) { + shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, + SMB2_MAX_BUFFER_SIZE)); + shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + if (rc) + goto async_readv_out; + + flags |= CIFS_HAS_CREDITS; + } + + kref_get(&rdata->refcount); + rc = cifs_call_async(server, &rqst, + cifs_readv_receive, smb2_readv_callback, + smb3_handle_read_data, rdata, flags, + &rdata->credits); + if (rc) { + kref_put(&rdata->refcount, cifs_readdata_release); + cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); + trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid, + io_parms.tcon->tid, + io_parms.tcon->ses->Suid, + io_parms.offset, io_parms.length, rc); + } + +async_readv_out: + cifs_small_buf_release(buf); + return rc; +} + +int +SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *buf_type) +{ + struct smb_rqst rqst; + int resp_buftype, rc; + struct smb2_read_req *req = NULL; + struct smb2_read_rsp *rsp = NULL; + struct kvec iov[1]; + struct kvec rsp_iov; + unsigned int total_len; + int flags = CIFS_LOG_ERROR; + struct cifs_ses *ses = io_parms->tcon->ses; + + if (!io_parms->server) + io_parms->server = cifs_pick_channel(io_parms->tcon->ses); + + *nbytes = 0; + rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0); + if (rc) + return rc; + + if (smb3_encryption_required(io_parms->tcon)) + flags |= CIFS_TRANSFORM_REQ; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, io_parms->server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; + + if (rc) { + if (rc != -ENODATA) { + cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); + cifs_dbg(VFS, "Send error in read = %d\n", rc); + trace_smb3_read_err(xid, + req->PersistentFileId, + io_parms->tcon->tid, ses->Suid, + io_parms->offset, io_parms->length, + rc); + } else + trace_smb3_read_done(xid, req->PersistentFileId, io_parms->tcon->tid, + ses->Suid, io_parms->offset, 0); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + cifs_small_buf_release(req); + return rc == -ENODATA ? 0 : rc; + } else + trace_smb3_read_done(xid, + req->PersistentFileId, + io_parms->tcon->tid, ses->Suid, + io_parms->offset, io_parms->length); + + cifs_small_buf_release(req); + + *nbytes = le32_to_cpu(rsp->DataLength); + if ((*nbytes > CIFS_MAX_MSGSIZE) || + (*nbytes > io_parms->length)) { + cifs_dbg(FYI, "bad length %d for count %d\n", + *nbytes, io_parms->length); + rc = -EIO; + *nbytes = 0; + } + + if (*buf) { + memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + } else if (resp_buftype != CIFS_NO_BUFFER) { + *buf = rsp_iov.iov_base; + if (resp_buftype == CIFS_SMALL_BUFFER) + *buf_type = CIFS_SMALL_BUFFER; + else if (resp_buftype == CIFS_LARGE_BUFFER) + *buf_type = CIFS_LARGE_BUFFER; + } + return rc; +} + +/* + * Check the mid_state and signature on received buffer (if any), and queue the + * workqueue completion task. + */ +static void +smb2_writev_callback(struct mid_q_entry *mid) +{ + struct cifs_writedata *wdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct TCP_Server_Info *server = wdata->server; + unsigned int written; + struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + + WARN_ONCE(wdata->server != mid->server, + "wdata server %p != mid server %p", + wdata->server, mid->server); + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + wdata->result = smb2_check_receive(mid, server, 0); + if (wdata->result != 0) + break; + + written = le32_to_cpu(rsp->DataLength); + /* + * Mask off high 16 bits when bytes written as returned + * by the server is greater than bytes requested by the + * client. OS/2 servers are known to set incorrect + * CountHigh values. + */ + if (written > wdata->bytes) + written &= 0xFFFF; + + if (written < wdata->bytes) + wdata->result = -ENOSPC; + else + wdata->bytes = written; + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + wdata->result = -EAGAIN; + break; + case MID_RESPONSE_MALFORMED: + credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + fallthrough; + default: + wdata->result = -EIO; + break; + } +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If this wdata has a memory registered, the MR can be freed + * The number of MRs available is limited, it's important to recover + * used MR as soon as I/O is finished. Hold MR longer in the later + * I/O process can possibly result in I/O deadlock due to lack of MR + * to send request on I/O retry + */ + if (wdata->mr) { + smbd_deregister_mr(wdata->mr); + wdata->mr = NULL; + } +#endif + if (wdata->result) { + cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); + trace_smb3_write_err(0 /* no xid */, + wdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, wdata->offset, + wdata->bytes, wdata->result); + if (wdata->result == -ENOSPC) + pr_warn_once("Out of space writing to %s\n", + tcon->tree_name); + } else + trace_smb3_write_done(0 /* no xid */, + wdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, + wdata->offset, wdata->bytes); + + queue_work(cifsiod_wq, &wdata->work); + release_mid(mid); + add_credits(server, &credits, 0); +} + +/* smb2_async_writev - send an async write, and set up mid to handle result */ +int +smb2_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)) +{ + int rc = -EACCES, flags = 0; + struct smb2_write_req *req = NULL; + struct smb2_hdr *shdr; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct TCP_Server_Info *server = wdata->server; + struct kvec iov[1]; + struct smb_rqst rqst = { }; + unsigned int total_len; + struct cifs_io_parms _io_parms; + struct cifs_io_parms *io_parms = NULL; + + if (!wdata->server) + server = wdata->server = cifs_pick_channel(tcon->ses); + + /* + * in future we may get cifs_io_parms passed in from the caller, + * but for now we construct it here... + */ + _io_parms = (struct cifs_io_parms) { + .tcon = tcon, + .server = server, + .offset = wdata->offset, + .length = wdata->bytes, + .persistent_fid = wdata->cfile->fid.persistent_fid, + .volatile_fid = wdata->cfile->fid.volatile_fid, + .pid = wdata->pid, + }; + io_parms = &_io_parms; + + rc = smb2_plain_req_init(SMB2_WRITE, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + shdr = (struct smb2_hdr *)req; + shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; + req->WriteChannelInfoOffset = 0; + req->WriteChannelInfoLength = 0; + req->Channel = 0; + req->Offset = cpu_to_le64(io_parms->offset); + req->DataOffset = cpu_to_le16( + offsetof(struct smb2_write_req, Buffer)); + req->RemainingBytes = 0; + + trace_smb3_write_enter(0 /* xid */, + io_parms->persistent_fid, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, + io_parms->length); + +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If we want to do a server RDMA read, fill in and append + * smbd_buffer_descriptor_v1 to the end of write request + */ + if (smb3_use_rdma_offload(io_parms)) { + struct smbd_buffer_descriptor_v1 *v1; + bool need_invalidate = server->dialect == SMB30_PROT_ID; + + wdata->mr = smbd_register_mr( + server->smbd_conn, wdata->pages, + wdata->nr_pages, wdata->page_offset, + wdata->tailsz, false, need_invalidate); + if (!wdata->mr) { + rc = -EAGAIN; + goto async_writev_out; + } + req->Length = 0; + req->DataOffset = 0; + if (wdata->nr_pages > 1) + req->RemainingBytes = + cpu_to_le32( + (wdata->nr_pages - 1) * wdata->pagesz - + wdata->page_offset + wdata->tailsz + ); + else + req->RemainingBytes = cpu_to_le32(wdata->tailsz); + req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; + if (need_invalidate) + req->Channel = SMB2_CHANNEL_RDMA_V1; + req->WriteChannelInfoOffset = + cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); + req->WriteChannelInfoLength = + cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); + v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; + v1->offset = cpu_to_le64(wdata->mr->mr->iova); + v1->token = cpu_to_le32(wdata->mr->mr->rkey); + v1->length = cpu_to_le32(wdata->mr->mr->length); + } +#endif + iov[0].iov_len = total_len - 1; + iov[0].iov_base = (char *)req; + + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + rqst.rq_pages = wdata->pages; + rqst.rq_offset = wdata->page_offset; + rqst.rq_npages = wdata->nr_pages; + rqst.rq_pagesz = wdata->pagesz; + rqst.rq_tailsz = wdata->tailsz; +#ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) { + iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); + rqst.rq_npages = 0; + } +#endif + cifs_dbg(FYI, "async write at %llu %u bytes\n", + io_parms->offset, io_parms->length); + +#ifdef CONFIG_CIFS_SMB_DIRECT + /* For RDMA read, I/O size is in RemainingBytes not in Length */ + if (!wdata->mr) + req->Length = cpu_to_le32(io_parms->length); +#else + req->Length = cpu_to_le32(io_parms->length); +#endif + + if (wdata->credits.value > 0) { + shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, + SMB2_MAX_BUFFER_SIZE)); + shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); + + rc = adjust_credits(server, &wdata->credits, io_parms->length); + if (rc) + goto async_writev_out; + + flags |= CIFS_HAS_CREDITS; + } + + kref_get(&wdata->refcount); + rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, + wdata, flags, &wdata->credits); + + if (rc) { + trace_smb3_write_err(0 /* no xid */, + io_parms->persistent_fid, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, + io_parms->length, + rc); + kref_put(&wdata->refcount, release); + cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); + } + +async_writev_out: + cifs_small_buf_release(req); + return rc; +} + +/* + * SMB2_write function gets iov pointer to kvec array with n_vec as a length. + * The length field from io_parms must be at least 1 and indicates a number of + * elements with data to write that begins with position 1 in iov array. All + * data length is specified by count. + */ +int +SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec) +{ + struct smb_rqst rqst; + int rc = 0; + struct smb2_write_req *req = NULL; + struct smb2_write_rsp *rsp = NULL; + int resp_buftype; + struct kvec rsp_iov; + int flags = 0; + unsigned int total_len; + struct TCP_Server_Info *server; + + *nbytes = 0; + + if (n_vec < 1) + return rc; + + if (!io_parms->server) + io_parms->server = cifs_pick_channel(io_parms->tcon->ses); + server = io_parms->server; + if (server == NULL) + return -ECONNABORTED; + + rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(io_parms->tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; + req->WriteChannelInfoOffset = 0; + req->WriteChannelInfoLength = 0; + req->Channel = 0; + req->Length = cpu_to_le32(io_parms->length); + req->Offset = cpu_to_le64(io_parms->offset); + req->DataOffset = cpu_to_le16( + offsetof(struct smb2_write_req, Buffer)); + req->RemainingBytes = 0; + + trace_smb3_write_enter(xid, io_parms->persistent_fid, + io_parms->tcon->tid, io_parms->tcon->ses->Suid, + io_parms->offset, io_parms->length); + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_vec + 1; + + rc = cifs_send_recv(xid, io_parms->tcon->ses, server, + &rqst, + &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; + + if (rc) { + trace_smb3_write_err(xid, + req->PersistentFileId, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, io_parms->length, rc); + cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); + cifs_dbg(VFS, "Send error in write = %d\n", rc); + } else { + *nbytes = le32_to_cpu(rsp->DataLength); + trace_smb3_write_done(xid, + req->PersistentFileId, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, *nbytes); + } + + cifs_small_buf_release(req); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int posix_info_sid_size(const void *beg, const void *end) +{ + size_t subauth; + int total; + + if (beg + 1 > end) + return -1; + + subauth = *(u8 *)(beg+1); + if (subauth < 1 || subauth > 15) + return -1; + + total = 1 + 1 + 6 + 4*subauth; + if (beg + total > end) + return -1; + + return total; +} + +int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out) + +{ + int total_len = 0; + int owner_len, group_len; + int name_len; + const void *owner_sid; + const void *group_sid; + const void *name; + + /* if no end bound given, assume payload to be correct */ + if (!end) { + const struct smb2_posix_info *p = beg; + + end = beg + le32_to_cpu(p->NextEntryOffset); + /* last element will have a 0 offset, pick a sensible bound */ + if (end == beg) + end += 0xFFFF; + } + + /* check base buf */ + if (beg + sizeof(struct smb2_posix_info) > end) + return -1; + total_len = sizeof(struct smb2_posix_info); + + /* check owner sid */ + owner_sid = beg + total_len; + owner_len = posix_info_sid_size(owner_sid, end); + if (owner_len < 0) + return -1; + total_len += owner_len; + + /* check group sid */ + group_sid = beg + total_len; + group_len = posix_info_sid_size(group_sid, end); + if (group_len < 0) + return -1; + total_len += group_len; + + /* check name len */ + if (beg + total_len + 4 > end) + return -1; + name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); + if (name_len < 1 || name_len > 0xFFFF) + return -1; + total_len += 4; + + /* check name */ + name = beg + total_len; + if (name + name_len > end) + return -1; + total_len += name_len; + + if (out) { + out->base = beg; + out->size = total_len; + out->name_len = name_len; + out->name = name; + memcpy(&out->owner, owner_sid, owner_len); + memcpy(&out->group, group_sid, group_len); + } + return total_len; +} + +static int posix_info_extra_size(const void *beg, const void *end) +{ + int len = posix_info_parse(beg, end, NULL); + + if (len < 0) + return -1; + return len - sizeof(struct smb2_posix_info); +} + +static unsigned int +num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, + size_t size) +{ + int len; + unsigned int entrycount = 0; + unsigned int next_offset = 0; + char *entryptr; + FILE_DIRECTORY_INFO *dir_info; + + if (bufstart == NULL) + return 0; + + entryptr = bufstart; + + while (1) { + if (entryptr + next_offset < entryptr || + entryptr + next_offset > end_of_buf || + entryptr + next_offset + size > end_of_buf) { + cifs_dbg(VFS, "malformed search entry would overflow\n"); + break; + } + + entryptr = entryptr + next_offset; + dir_info = (FILE_DIRECTORY_INFO *)entryptr; + + if (infotype == SMB_FIND_FILE_POSIX_INFO) + len = posix_info_extra_size(entryptr, end_of_buf); + else + len = le32_to_cpu(dir_info->FileNameLength); + + if (len < 0 || + entryptr + len < entryptr || + entryptr + len > end_of_buf || + entryptr + len + size > end_of_buf) { + cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", + end_of_buf); + break; + } + + *lastentry = entryptr; + entrycount++; + + next_offset = le32_to_cpu(dir_info->NextEntryOffset); + if (!next_offset) + break; + } + + return entrycount; +} + +/* + * Readdir/FindFirst + */ +int SMB2_query_directory_init(const unsigned int xid, + struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + int index, int info_level) +{ + struct smb2_query_directory_req *req; + unsigned char *bufptr; + __le16 asteriks = cpu_to_le16('*'); + unsigned int output_size = CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE; + unsigned int total_len; + struct kvec *iov = rqst->rq_iov; + int len, rc; + + rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + switch (info_level) { + case SMB_FIND_FILE_DIRECTORY_INFO: + req->FileInformationClass = FILE_DIRECTORY_INFORMATION; + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; + break; + case SMB_FIND_FILE_POSIX_INFO: + req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; + break; + default: + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", + info_level); + return -EINVAL; + } + + req->FileIndex = cpu_to_le32(index); + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + len = 0x2; + bufptr = req->Buffer; + memcpy(bufptr, &asteriks, len); + + req->FileNameOffset = + cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1); + req->FileNameLength = cpu_to_le16(len); + /* + * BB could be 30 bytes or so longer if we used SMB2 specific + * buffer lengths, but this is safe and close enough. + */ + output_size = min_t(unsigned int, output_size, server->maxBuf); + output_size = min_t(unsigned int, output_size, 2 << 15); + req->OutputBufferLength = cpu_to_le32(output_size); + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1; + + iov[1].iov_base = (char *)(req->Buffer); + iov[1].iov_len = len; + + trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, output_size); + + return 0; +} + +void SMB2_query_directory_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ + } +} + +int +smb2_parse_query_directory(struct cifs_tcon *tcon, + struct kvec *rsp_iov, + int resp_buftype, + struct cifs_search_info *srch_inf) +{ + struct smb2_query_directory_rsp *rsp; + size_t info_buf_size; + char *end_of_smb; + int rc; + + rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; + + switch (srch_inf->info_level) { + case SMB_FIND_FILE_DIRECTORY_INFO: + info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; + break; + case SMB_FIND_FILE_POSIX_INFO: + /* note that posix payload are variable size */ + info_buf_size = sizeof(struct smb2_posix_info); + break; + default: + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", + srch_inf->info_level); + return -EINVAL; + } + + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), rsp_iov, + info_buf_size); + if (rc) { + cifs_tcon_dbg(VFS, "bad info payload"); + return rc; + } + + srch_inf->unicode = true; + + if (srch_inf->ntwrk_buf_start) { + if (srch_inf->smallBuf) + cifs_small_buf_release(srch_inf->ntwrk_buf_start); + else + cifs_buf_release(srch_inf->ntwrk_buf_start); + } + srch_inf->ntwrk_buf_start = (char *)rsp; + srch_inf->srch_entries_start = srch_inf->last_entry = + (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); + end_of_smb = rsp_iov->iov_len + (char *)rsp; + + srch_inf->entries_in_buffer = num_entries( + srch_inf->info_level, + srch_inf->srch_entries_start, + end_of_smb, + &srch_inf->last_entry, + info_buf_size); + + srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; + cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", + srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, + srch_inf->srch_entries_start, srch_inf->last_entry); + if (resp_buftype == CIFS_LARGE_BUFFER) + srch_inf->smallBuf = false; + else if (resp_buftype == CIFS_SMALL_BUFFER) + srch_inf->smallBuf = true; + else + cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); + + return 0; +} + +int +SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int index, + struct cifs_search_info *srch_inf) +{ + struct smb_rqst rqst; + struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; + struct smb2_query_directory_rsp *rsp = NULL; + int resp_buftype = CIFS_NO_BUFFER; + struct kvec rsp_iov; + int rc = 0; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = 0; + + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; + + rc = SMB2_query_directory_init(xid, tcon, server, + &rqst, persistent_fid, + volatile_fid, index, + srch_inf->info_level); + if (rc) + goto qdir_exit; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; + + if (rc) { + if (rc == -ENODATA && + rsp->hdr.Status == STATUS_NO_MORE_FILES) { + trace_smb3_query_dir_done(xid, persistent_fid, + tcon->tid, tcon->ses->Suid, index, 0); + srch_inf->endOfSearch = true; + rc = 0; + } else { + trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, 0, rc); + cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); + } + goto qdir_exit; + } + + rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, + srch_inf); + if (rc) { + trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, 0, rc); + goto qdir_exit; + } + resp_buftype = CIFS_NO_BUFFER; + + trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, srch_inf->entries_in_buffer); + +qdir_exit: + SMB2_query_directory_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int +SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 pid, + u8 info_class, u8 info_type, u32 additional_info, + void **data, unsigned int *size) +{ + struct smb2_set_info_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int i, total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); + req->InfoType = info_type; + req->FileInfoClass = info_class; + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + req->AdditionalInformation = cpu_to_le32(additional_info); + + req->BufferOffset = + cpu_to_le16(sizeof(struct smb2_set_info_req) - 1); + req->BufferLength = cpu_to_le32(*size); + + memcpy(req->Buffer, *data, *size); + total_len += *size; + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1; + + for (i = 1; i < rqst->rq_nvec; i++) { + le32_add_cpu(&req->BufferLength, size[i]); + iov[i].iov_base = (char *)data[i]; + iov[i].iov_len = size[i]; + } + + return 0; +} + +void +SMB2_set_info_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +static int +send_set_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, + u8 info_type, u32 additional_info, unsigned int num, + void **data, unsigned int *size) +{ + struct smb_rqst rqst; + struct smb2_set_info_rsp *rsp = NULL; + struct kvec *iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = 0; + + if (!ses || !server) + return -EIO; + + if (!num) + return -EINVAL; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = num; + + rc = SMB2_set_info_init(tcon, server, + &rqst, persistent_fid, volatile_fid, pid, + info_class, info_type, additional_info, + data, size); + if (rc) { + kfree(iov); + return rc; + } + + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); + SMB2_set_info_free(&rqst); + rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); + trace_smb3_set_info_err(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type, rc); + } + + free_rsp_buf(resp_buftype, rsp); + kfree(iov); + return rc; +} + +int +SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid, u32 pid, __le64 *eof) +{ + struct smb2_file_eof_info info; + void *data; + unsigned int size; + + info.EndOfFile = *eof; + + data = &info; + size = sizeof(struct smb2_file_eof_info); + + trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof)); + + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, + 0, 1, &data, &size); +} + +int +SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct cifs_ntsd *pnntsd, int pacllen, int aclflag) +{ + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, + 1, (void **)&pnntsd, &pacllen); +} + +int +SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_full_ea_info *buf, int len) +{ + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, + 0, 1, (void **)&buf, &len); +} + +int +SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, + const u64 persistent_fid, const u64 volatile_fid, + __u8 oplock_level) +{ + struct smb_rqst rqst; + int rc; + struct smb2_oplock_break *req = NULL; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = CIFS_OBREAK_OP; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + cifs_dbg(FYI, "SMB2_oplock_break\n"); + rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->VolatileFid = volatile_fid; + req->PersistentFid = persistent_fid; + req->OplockLevel = oplock_level; + req->hdr.CreditRequest = cpu_to_le16(1); + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); + cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); + } + + return rc; +} + +void +smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, + struct kstatfs *kst) +{ + kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * + le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); + kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); + kst->f_bfree = kst->f_bavail = + le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); + return; +} + +static void +copy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data, + struct kstatfs *kst) +{ + kst->f_bsize = le32_to_cpu(response_data->BlockSize); + kst->f_blocks = le64_to_cpu(response_data->TotalBlocks); + kst->f_bfree = le64_to_cpu(response_data->BlocksAvail); + if (response_data->UserBlocksAvail == cpu_to_le64(-1)) + kst->f_bavail = kst->f_bfree; + else + kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); + if (response_data->TotalFileNodes != cpu_to_le64(-1)) + kst->f_files = le64_to_cpu(response_data->TotalFileNodes); + if (response_data->FreeFileNodes != cpu_to_le64(-1)) + kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes); + + return; +} + +static int +build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + int level, int outbuf_len, u64 persistent_fid, + u64 volatile_fid) +{ + int rc; + struct smb2_query_info_req *req; + unsigned int total_len; + + cifs_dbg(FYI, "Query FSInfo level %d\n", level); + + if ((tcon->ses == NULL) || server == NULL) + return -EIO; + + rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->InfoType = SMB2_O_INFO_FILESYSTEM; + req->FileInfoClass = level; + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + /* 1 for pad */ + req->InputBufferOffset = + cpu_to_le16(sizeof(struct smb2_query_info_req) - 1); + req->OutputBufferLength = cpu_to_le32( + outbuf_len + sizeof(struct smb2_query_info_rsp) - 1); + + iov->iov_base = (char *)req; + iov->iov_len = total_len; + return 0; +} + +int +SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + FILE_SYSTEM_POSIX_INFO *info = NULL; + int flags = 0; + + rc = build_qfs_info_req(&iov, tcon, server, + FS_POSIX_INFORMATION, + sizeof(FILE_SYSTEM_POSIX_INFO), + persistent_fid, volatile_fid); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(iov.iov_base); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto posix_qfsinf_exit; + } + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + info = (FILE_SYSTEM_POSIX_INFO *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, + sizeof(FILE_SYSTEM_POSIX_INFO)); + if (!rc) + copy_posix_fs_info_to_kstatfs(info, fsdata); + +posix_qfsinf_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +int +SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct smb2_fs_full_size_info *info = NULL; + int flags = 0; + + rc = build_qfs_info_req(&iov, tcon, server, + FS_FULL_SIZE_INFORMATION, + sizeof(struct smb2_fs_full_size_info), + persistent_fid, volatile_fid); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(iov.iov_base); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsinf_exit; + } + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + info = (struct smb2_fs_full_size_info *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, + sizeof(struct smb2_fs_full_size_info)); + if (!rc) + smb2_copy_fs_info_to_kstatfs(info, fsdata); + +qfsinf_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +int +SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int level) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype, max_len, min_len; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + unsigned int rsp_len, offset; + int flags = 0; + + if (level == FS_DEVICE_INFORMATION) { + max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); + min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); + } else if (level == FS_ATTRIBUTE_INFORMATION) { + max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); + min_len = MIN_FS_ATTR_INFO_SIZE; + } else if (level == FS_SECTOR_SIZE_INFORMATION) { + max_len = sizeof(struct smb3_fs_ss_info); + min_len = sizeof(struct smb3_fs_ss_info); + } else if (level == FS_VOLUME_INFORMATION) { + max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; + min_len = sizeof(struct smb3_fs_vol_info); + } else { + cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); + return -EINVAL; + } + + rc = build_qfs_info_req(&iov, tcon, server, + level, max_len, + persistent_fid, volatile_fid); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(iov.iov_base); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsattr_exit; + } + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + rsp_len = le32_to_cpu(rsp->OutputBufferLength); + offset = le16_to_cpu(rsp->OutputBufferOffset); + rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len); + if (rc) + goto qfsattr_exit; + + if (level == FS_ATTRIBUTE_INFORMATION) + memcpy(&tcon->fsAttrInfo, offset + + (char *)rsp, min_t(unsigned int, + rsp_len, max_len)); + else if (level == FS_DEVICE_INFORMATION) + memcpy(&tcon->fsDevInfo, offset + + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); + else if (level == FS_SECTOR_SIZE_INFORMATION) { + struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) + (offset + (char *)rsp); + tcon->ss_flags = le32_to_cpu(ss_info->Flags); + tcon->perf_sector_size = + le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); + } else if (level == FS_VOLUME_INFORMATION) { + struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) + (offset + (char *)rsp); + tcon->vol_serial_number = vol_info->VolumeSerialNumber; + tcon->vol_create_time = vol_info->VolumeCreationTime; + } + +qfsattr_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +int +smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, + const __u32 num_lock, struct smb2_lock_element *buf) +{ + struct smb_rqst rqst; + int rc = 0; + struct smb2_lock_req *req = NULL; + struct kvec iov[2]; + struct kvec rsp_iov; + int resp_buf_type; + unsigned int count; + int flags = CIFS_NO_RSP_BUF; + unsigned int total_len; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + + cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); + + rc = smb2_plain_req_init(SMB2_LOCK, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); + req->LockCount = cpu_to_le16(num_lock); + + req->PersistentFileId = persist_fid; + req->VolatileFileId = volatile_fid; + + count = num_lock * sizeof(struct smb2_lock_element); + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len - sizeof(struct smb2_lock_element); + iov[1].iov_base = (char *)buf; + iov[1].iov_len = count; + + cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + rc = cifs_send_recv(xid, tcon->ses, server, + &rqst, &resp_buf_type, flags, + &rsp_iov); + cifs_small_buf_release(req); + if (rc) { + cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); + cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); + trace_smb3_lock_err(xid, persist_fid, tcon->tid, + tcon->ses->Suid, rc); + } + + return rc; +} + +int +SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, + const __u64 length, const __u64 offset, const __u32 lock_flags, + const bool wait) +{ + struct smb2_lock_element lock; + + lock.Offset = cpu_to_le64(offset); + lock.Length = cpu_to_le64(length); + lock.Flags = cpu_to_le32(lock_flags); + if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) + lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); + + return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); +} + +int +SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, + __u8 *lease_key, const __le32 lease_state) +{ + struct smb_rqst rqst; + int rc; + struct smb2_lease_ack *req = NULL; + struct cifs_ses *ses = tcon->ses; + int flags = CIFS_OBREAK_OP; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + __u64 *please_key_high; + __u64 *please_key_low; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + + cifs_dbg(FYI, "SMB2_lease_break\n"); + rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->hdr.CreditRequest = cpu_to_le16(1); + req->StructureSize = cpu_to_le16(36); + total_len += 12; + + memcpy(req->LeaseKey, lease_key, 16); + req->LeaseState = lease_state; + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + + please_key_low = (__u64 *)lease_key; + please_key_high = (__u64 *)(lease_key+8); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); + trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, + ses->Suid, *please_key_low, *please_key_high, rc); + cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); + } else + trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, + ses->Suid, *please_key_low, *please_key_high); + + return rc; +} diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h new file mode 100644 index 000000000000..1237bb86e93a --- /dev/null +++ b/fs/smb/client/smb2pdu.h @@ -0,0 +1,434 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2009, 2013 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include +#include "cifsacl.h" + +/* 52 transform hdr + 64 hdr + 88 create rsp */ +#define SMB2_TRANSFORM_HEADER_SIZE 52 +#define MAX_SMB2_HDR_SIZE 204 + +/* The total header size for SMB2 read and write */ +#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_hdr)) + +/* See MS-SMB2 2.2.43 */ +struct smb2_rdma_transform { + __le16 RdmaDescriptorOffset; + __le16 RdmaDescriptorLength; + __le32 Channel; /* for values see channel description in smb2 read above */ + __le16 TransformCount; + __le16 Reserved1; + __le32 Reserved2; +} __packed; + +/* TransformType */ +#define SMB2_RDMA_TRANSFORM_TYPE_ENCRYPTION 0x0001 +#define SMB2_RDMA_TRANSFORM_TYPE_SIGNING 0x0002 + +struct smb2_rdma_crypto_transform { + __le16 TransformType; + __le16 SignatureLength; + __le16 NonceLength; + __u16 Reserved; + __u8 Signature[]; /* variable length */ + /* u8 Nonce[] */ + /* followed by padding */ +} __packed; + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL + +#define SMB2_SYMLINK_STRUCT_SIZE \ + (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) + +#define SYMLINK_ERROR_TAG 0x4c4d5953 + +struct smb2_symlink_err_rsp { + __le32 SymLinkLength; + __le32 SymLinkErrorTag; + __le32 ReparseTag; + __le16 ReparseDataLength; + __le16 UnparsedPathLength; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __le32 Flags; + __u8 PathBuffer[]; +} __packed; + +/* SMB 3.1.1 and later dialects. See MS-SMB2 section 2.2.2.1 */ +struct smb2_error_context_rsp { + __le32 ErrorDataLength; + __le32 ErrorId; + __u8 ErrorContextData; /* ErrorDataLength long array */ +} __packed; + +/* ErrorId values */ +#define SMB2_ERROR_ID_DEFAULT 0x00000000 +#define SMB2_ERROR_ID_SHARE_REDIRECT cpu_to_le32(0x72645253) /* "rdRS" */ + +/* Defines for Type field below (see MS-SMB2 2.2.2.2.2.1) */ +#define MOVE_DST_IPADDR_V4 cpu_to_le32(0x00000001) +#define MOVE_DST_IPADDR_V6 cpu_to_le32(0x00000002) + +struct move_dst_ipaddr { + __le32 Type; + __u32 Reserved; + __u8 address[16]; /* IPv4 followed by 12 bytes rsvd or IPv6 address */ +} __packed; + +struct share_redirect_error_context_rsp { + __le32 StructureSize; + __le32 NotificationType; + __le32 ResourceNameOffset; + __le32 ResourceNameLength; + __le16 Reserved; + __le16 TargetType; + __le32 IPAddrCount; + struct move_dst_ipaddr IpAddrMoveList[]; + /* __u8 ResourceName[] */ /* Name of share as counted Unicode string */ +} __packed; + +/* + * Maximum number of iovs we need for an open/create request. + * [0] : struct smb2_create_req + * [1] : path + * [2] : lease context + * [3] : durable context + * [4] : posix context + * [5] : time warp context + * [6] : query id context + * [7] : compound padding + */ +#define SMB2_CREATE_IOV_SIZE 8 + +/* + * Maximum size of a SMB2_CREATE response is 64 (smb2 header) + + * 88 (fixed part of create response) + 520 (path) + 208 (contexts) + + * 2 bytes of padding. + */ +#define MAX_SMB2_CREATE_RESPONSE_SIZE 880 + +#define SMB2_LEASE_READ_CACHING_HE 0x01 +#define SMB2_LEASE_HANDLE_CACHING_HE 0x02 +#define SMB2_LEASE_WRITE_CACHING_HE 0x04 + +struct create_durable { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + } Data; +} __packed; + +/* See MS-SMB2 2.2.13.2.11 */ +/* Flags */ +#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002 +struct durable_context_v2 { + __le32 Timeout; + __le32 Flags; + __u64 Reserved; + __u8 CreateGuid[16]; +} __packed; + +struct create_durable_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct durable_context_v2 dcontext; +} __packed; + +/* See MS-SMB2 2.2.13.2.12 */ +struct durable_reconnect_context_v2 { + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + __u8 CreateGuid[16]; + __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ +} __packed; + +/* See MS-SMB2 2.2.14.2.9 */ +struct create_on_disk_id { + struct create_context ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; + __u32 Reserved[4]; +} __packed; + +/* See MS-SMB2 2.2.14.2.12 */ +struct durable_reconnect_context_v2_rsp { + __le32 Timeout; + __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ +} __packed; + +struct create_durable_handle_reconnect_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct durable_reconnect_context_v2 dcontext; + __u8 Pad[4]; +} __packed; + +/* See MS-SMB2 2.2.13.2.5 */ +struct crt_twarp_ctxt { + struct create_context ccontext; + __u8 Name[8]; + __le64 Timestamp; + +} __packed; + +/* See MS-SMB2 2.2.13.2.9 */ +struct crt_query_id_ctxt { + struct create_context ccontext; + __u8 Name[8]; +} __packed; + +struct crt_sd_ctxt { + struct create_context ccontext; + __u8 Name[8]; + struct smb3_sd sd; +} __packed; + + +#define COPY_CHUNK_RES_KEY_SIZE 24 +struct resume_key_req { + char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; + __le32 ContextLength; /* MBZ */ + char Context[]; /* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +/* this goes in the ioctl buffer when doing a copychunk request */ +struct copychunk_ioctl { + char SourceKey[COPY_CHUNK_RES_KEY_SIZE]; + __le32 ChunkCount; /* we are only sending 1 */ + __le32 Reserved; + /* array will only be one chunk long for us */ + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; /* how many bytes to copy */ + __u32 Reserved2; +} __packed; + +struct copychunk_ioctl_rsp { + __le32 ChunksWritten; + __le32 ChunkBytesWritten; + __le32 TotalBytesWritten; +} __packed; + +/* See MS-FSCC 2.3.29 and 2.3.30 */ +struct get_retrieval_pointer_count_req { + __le64 StartingVcn; /* virtual cluster number (signed) */ +} __packed; + +struct get_retrieval_pointer_count_rsp { + __le32 ExtentCount; +} __packed; + +/* + * See MS-FSCC 2.3.33 and 2.3.34 + * request is the same as get_retrieval_point_count_req struct above + */ +struct smb3_extents { + __le64 NextVcn; + __le64 Lcn; /* logical cluster number */ +} __packed; + +struct get_retrieval_pointers_refcount_rsp { + __le32 ExtentCount; + __u32 Reserved; + __le64 StartingVcn; + struct smb3_extents extents[]; +} __packed; + +/* See MS-DFSC 2.2.2 */ +struct fsctl_get_dfs_referral_req { + __le16 MaxReferralLevel; + __u8 RequestFileName[]; +} __packed; + +/* DFS response is struct get_dfs_refer_rsp */ + +/* See MS-SMB2 2.2.31.3 */ +struct network_resiliency_req { + __le32 Timeout; + __le32 Reserved; +} __packed; +/* There is no buffer for the response ie no struct network_resiliency_rsp */ + +#define RSS_CAPABLE cpu_to_le32(0x00000001) +#define RDMA_CAPABLE cpu_to_le32(0x00000002) + +#define INTERNETWORK cpu_to_le16(0x0002) +#define INTERNETWORKV6 cpu_to_le16(0x0017) + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + __le16 Family; + __u8 Buffer[126]; +} __packed; + +struct iface_info_ipv4 { + __be16 Port; + __be32 IPv4Address; + __be64 Reserved; +} __packed; + +struct iface_info_ipv6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6Address[16]; + __be32 ScopeId; +} __packed; + +#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ + +struct compress_ioctl { + __le16 CompressionState; /* See cifspdu.h for possible flag values */ +} __packed; + +/* + * Maximum number of iovs we need for an ioctl request. + * [0] : struct smb2_ioctl_req + * [1] : in_data + */ +#define SMB2_IOCTL_IOV_SIZE 2 + +/* + * PDU query infolevel structure definitions + * BB consider moving to a different header + */ + +struct smb2_file_full_ea_info { /* encoding of response for level 15 */ + __le32 next_entry_offset; + __u8 flags; + __u8 ea_name_length; + __le16 ea_value_length; + char ea_data[]; /* \0 terminated name plus value */ +} __packed; /* level 15 Set */ + +struct smb2_file_reparse_point_info { + __le64 IndexNumber; + __le32 Tag; +} __packed; + +struct smb2_file_network_open_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndOfFile; + __le32 Attributes; + __le32 Reserved; +} __packed; /* level 34 Query also similar returned in close rsp and open rsp */ + +/* See MS-FSCC 2.4.21 */ +struct smb2_file_id_information { + __le64 VolumeSerialNumber; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ +} __packed; /* level 59 */ + +/* See MS-FSCC 2.4.18 */ +struct smb2_file_id_extd_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 FileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 ReparsePointTag; /* valid if FILE_ATTR_REPARSE_POINT set in FileAttributes */ + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit */ + char FileName[1]; +} __packed; /* level 60 */ + +extern char smb2_padding[7]; + +/* equivalent of the contents of SMB3.1.1 POSIX open context response */ +struct create_posix_rsp { + u32 nlink; + u32 reparse_tag; + u32 mode; + struct cifs_sid owner; /* var-sized on the wire */ + struct cifs_sid group; /* var-sized on the wire */ +} __packed; + +#define SMB2_QUERY_DIRECTORY_IOV_SIZE 2 + +/* + * SMB2-only POSIX info level for query dir + * + * See posix_info_sid_size(), posix_info_extra_size() and + * posix_info_parse() to help with the handling of this struct. + */ +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* + * Parsed version of the above struct. Allows direct access to the + * variable length fields + */ +struct smb2_posix_info_parsed { + const struct smb2_posix_info *base; + size_t size; + struct cifs_sid owner; + struct cifs_sid group; + int name_len; + const u8 *name; +}; + +#endif /* _SMB2PDU_H */ diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h new file mode 100644 index 000000000000..be21b5d26f67 --- /dev/null +++ b/fs/smb/client/smb2proto.h @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ +#ifndef _SMB2PROTO_H +#define _SMB2PROTO_H +#include +#include + +struct statfs; +struct smb_rqst; + +/* + ***************************************************************** + * All Prototypes + ***************************************************************** + */ +extern int map_smb2_to_linux_error(char *buf, bool log_err); +extern int smb2_check_message(char *buf, unsigned int length, + struct TCP_Server_Info *server); +extern unsigned int smb2_calc_size(void *buf); +extern char *smb2_get_data_area_len(int *off, int *len, + struct smb2_hdr *shdr); +extern __le16 *cifs_convert_path_to_utf16(const char *from, + struct cifs_sb_info *cifs_sb); + +extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *); +extern int smb2_check_receive(struct mid_q_entry *mid, + struct TCP_Server_Info *server, bool log_error); +extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, + struct TCP_Server_Info *, + struct smb_rqst *rqst); +extern struct mid_q_entry *smb2_setup_async_request( + struct TCP_Server_Info *server, struct smb_rqst *rqst); +extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, + __u64 ses_id); +extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, + __u64 ses_id, __u32 tid); +extern int smb2_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + bool allocate_crypto); +extern int smb3_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + bool allocate_crypto); +extern void smb2_echo_request(struct work_struct *work); +extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); +extern bool smb2_is_valid_oplock_break(char *buffer, + struct TCP_Server_Info *srv); +extern int smb3_handle_read_data(struct TCP_Server_Info *server, + struct mid_q_entry *mid); +extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *path, + __u32 *reparse_tag); +int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); +extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_alloc); +extern int smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid); +extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb); +extern int smb2_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, + struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, const unsigned int xid); +extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_written); +extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const unsigned char *path, char *pbuf, + unsigned int *pbytes_read); +int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path); +int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, + void *buf); +extern int smb2_unlock_range(struct cifsFileInfo *cfile, + struct file_lock *flock, const unsigned int xid); +extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); +extern void smb2_reconnect_server(struct work_struct *work); +extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); +extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, + struct smb_rqst *rqst); +extern void smb2_set_next_command(struct cifs_tcon *tcon, + struct smb_rqst *rqst); +extern void smb2_set_related(struct smb_rqst *rqst); + +/* + * SMB2 Worker functions - most of protocol specific implementation details + * are contained within these calls. + */ +extern int SMB2_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); +extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); +extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *); +extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); +extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, + __le16 *path, __u8 *oplock, + struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, + struct kvec *err_iov, int *resp_buftype); +extern int SMB2_open_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + __u8 *oplock, struct cifs_open_parms *oparms, + __le16 *path); +extern void SMB2_open_free(struct smb_rqst *rqst); +extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + char *in_data, u32 indatalen, u32 maxoutlen, + char **out_data, u32 *plen /* returned data len */); +extern int SMB2_ioctl_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + char *in_data, u32 indatalen, + __u32 max_response_size); +extern void SMB2_ioctl_free(struct smb_rqst *rqst); +extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter, u32 max_out_data_len, + char **out_data, u32 *plen /* returned data len */); + +extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_network_open_info *pbuf); +extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_close_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + bool query_attrs); +extern void SMB2_close_free(struct smb_rqst *rqst); +extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + u64 persistent_file_id, u64 volatile_file_id); +extern void SMB2_flush_free(struct smb_rqst *rqst); +extern int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen); +extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + struct smb2_file_all_info *data); +extern int SMB2_query_info_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + u8 info_class, u8 info_type, + u32 additional_info, size_t output_len, + size_t input_len, void *input); +extern void SMB2_query_info_free(struct smb_rqst *rqst); +extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + void **data, unsigned int *plen, u32 info); +extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + __le64 *uniqueid); +extern int smb2_async_readv(struct cifs_readdata *rdata); +extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *buf_type); +extern int smb2_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)); +extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec); +extern int SMB2_echo(struct TCP_Server_Info *server); +extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int index, + struct cifs_search_info *srch_inf); +extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + int index, int info_level); +extern void SMB2_query_directory_free(struct smb_rqst *rqst); +extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, + __le64 *eof); +extern int SMB2_set_info_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 pid, + u8 info_class, u8 info_type, u32 additional_info, + void **data, unsigned int *size); +extern void SMB2_set_info_free(struct smb_rqst *rqst); +extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct cifs_ntsd *pnntsd, int pacllen, int aclflag); +extern int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_full_ea_info *buf, int len); +extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid); +extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, + const u64 persistent_fid, const u64 volatile_fid, + const __u8 oplock_level); +extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon, + __u64 persistent_fid, + __u64 volatile_fid); +extern int smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server); +void smb2_cancelled_close_fid(struct work_struct *work); +extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + struct kstatfs *FSData); +extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + struct kstatfs *FSData); +extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, int lvl); +extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, + const __u32 pid, const __u64 length, const __u64 offset, + const __u32 lockFlags, const bool wait); +extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, + const __u32 pid, const __u32 num_lock, + struct smb2_lock_element *buf); +extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, + __u8 *lease_key, const __le32 lease_state); +extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); + +extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, + enum securityEnum); +extern void smb2_parse_contexts(struct TCP_Server_Info *server, + struct smb2_create_rsp *rsp, + unsigned int *epoch, char *lease_key, + __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix); +extern int smb3_encryption_required(const struct cifs_tcon *tcon); +extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int min_buf_size); +extern int smb2_validate_and_copy_iov(unsigned int offset, + unsigned int buffer_length, + struct kvec *iov, + unsigned int minbufsize, char *data); +extern void smb2_copy_fs_info_to_kstatfs( + struct smb2_fs_full_size_info *pfs_inf, + struct kstatfs *kst); +extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); +extern int smb311_update_preauth_hash(struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct kvec *iov, int nvec); +extern int smb2_query_info_compound(const unsigned int xid, + struct cifs_tcon *tcon, + const char *path, u32 desired_access, + u32 class, u32 type, u32 output_len, + struct kvec *rsp, int *buftype, + struct cifs_sb_info *cifs_sb); +/* query path info from the server using SMB311 POSIX extensions*/ +int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); +int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out); +int posix_info_sid_size(const void *beg, const void *end); +#endif /* _SMB2PROTO_H */ diff --git a/fs/smb/client/smb2status.h b/fs/smb/client/smb2status.h new file mode 100644 index 000000000000..a9e958166fc5 --- /dev/null +++ b/fs/smb/client/smb2status.h @@ -0,0 +1,1769 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * SMB2 Status code (network error) definitions + * Definitions are from MS-ERREF + * + * Copyright (c) International Business Machines Corp., 2009,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +/* + * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * SEV C N <-------Facility--------> <------Error Status Code------> + * + * C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) + +struct ntstatus { + /* Facility is the high 12 bits of the following field */ + __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ + __le32 Code; +}; + +#define STATUS_SUCCESS cpu_to_le32(0x00000000) +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) +#define STATUS_USER_APC cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) +#define STATUS_ALERTED cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) +#define STATUS_PENDING cpu_to_le32(0x00000103) +#define STATUS_REPARSE cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) +#define DBG_CONTINUE cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) +#define DBG_CONTROL_C cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) +#define STATUS_UNWIND cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) +#define STATUS_RETRY cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURED cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c new file mode 100644 index 000000000000..790acf65a092 --- /dev/null +++ b/fs/smb/client/smb2transport.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) 2006 + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "smb2status.h" +#include "smb2glob.h" + +static int +smb3_crypto_shash_allocate(struct TCP_Server_Info *server) +{ + struct cifs_secmech *p = &server->secmech; + int rc; + + rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); + if (rc) + goto err; + + rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); + if (rc) + goto err; + + return 0; +err: + cifs_free_hash(&p->hmacsha256); + return rc; +} + +int +smb311_crypto_shash_allocate(struct TCP_Server_Info *server) +{ + struct cifs_secmech *p = &server->secmech; + int rc = 0; + + rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); + if (rc) + return rc; + + rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); + if (rc) + goto err; + + rc = cifs_alloc_hash("sha512", &p->sha512); + if (rc) + goto err; + + return 0; + +err: + cifs_free_hash(&p->aes_cmac); + cifs_free_hash(&p->hmacsha256); + return rc; +} + + +static +int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) +{ + struct cifs_chan *chan; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses = NULL; + int i; + int rc = 0; + bool is_binding = false; + + spin_lock(&cifs_tcp_ses_lock); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + if (ses->Suid == ses_id) + goto found; + } + cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n", + __func__, ses_id); + rc = -ENOENT; + goto out; + +found: + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + + is_binding = (cifs_chan_needs_reconnect(ses, server) && + ses->ses_status == SES_GOOD); + if (is_binding) { + /* + * If we are in the process of binding a new channel + * to an existing session, use the master connection + * session key + */ + memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + goto out; + } + + /* + * Otherwise, use the channel key. + */ + + for (i = 0; i < ses->chan_count; i++) { + chan = ses->chans + i; + if (chan->server == server) { + memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + goto out; + } + } + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + cifs_dbg(VFS, + "%s: Could not find channel signing key for session 0x%llx\n", + __func__, ses_id); + rc = -ENOENT; + +out: + spin_unlock(&cifs_tcp_ses_lock); + return rc; +} + +static struct cifs_ses * +smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + if (ses->Suid != ses_id) + continue; + ++ses->ses_count; + return ses; + } + + return NULL; +} + +struct cifs_ses * +smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) +{ + struct cifs_ses *ses; + + spin_lock(&cifs_tcp_ses_lock); + ses = smb2_find_smb_ses_unlocked(server, ses_id); + spin_unlock(&cifs_tcp_ses_lock); + + return ses; +} + +static struct cifs_tcon * +smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) +{ + struct cifs_tcon *tcon; + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->tid != tid) + continue; + ++tcon->tc_count; + return tcon; + } + + return NULL; +} + +/* + * Obtain tcon corresponding to the tid in the given + * cifs_ses + */ + +struct cifs_tcon * +smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) +{ + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + spin_lock(&cifs_tcp_ses_lock); + ses = smb2_find_smb_ses_unlocked(server, ses_id); + if (!ses) { + spin_unlock(&cifs_tcp_ses_lock); + return NULL; + } + tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); + if (!tcon) { + cifs_put_smb_ses(ses); + spin_unlock(&cifs_tcp_ses_lock); + return NULL; + } + spin_unlock(&cifs_tcp_ses_lock); + /* tcon already has a ref to ses, so we don't need ses anymore */ + cifs_put_smb_ses(ses); + + return tcon; +} + +int +smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, + bool allocate_crypto) +{ + int rc; + unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; + unsigned char *sigptr = smb2_signature; + struct kvec *iov = rqst->rq_iov; + struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; + struct cifs_ses *ses; + struct shash_desc *shash = NULL; + struct smb_rqst drqst; + + ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId)); + if (unlikely(!ses)) { + cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); + return -ENOENT; + } + + memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); + memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); + + if (allocate_crypto) { + rc = cifs_alloc_hash("hmac(sha256)", &shash); + if (rc) { + cifs_server_dbg(VFS, + "%s: sha256 alloc failed\n", __func__); + goto out; + } + } else { + shash = server->secmech.hmacsha256; + } + + rc = crypto_shash_setkey(shash->tfm, ses->auth_key.response, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + cifs_server_dbg(VFS, + "%s: Could not update with response\n", + __func__); + goto out; + } + + rc = crypto_shash_init(shash); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); + goto out; + } + + /* + * For SMB2+, __cifs_calc_signature() expects to sign only the actual + * data, that is, iov[0] should not contain a rfc1002 length. + * + * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to + * __cifs_calc_signature(). + */ + drqst = *rqst; + if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { + rc = crypto_shash_update(shash, iov[0].iov_base, + iov[0].iov_len); + if (rc) { + cifs_server_dbg(VFS, + "%s: Could not update with payload\n", + __func__); + goto out; + } + drqst.rq_iov++; + drqst.rq_nvec--; + } + + rc = __cifs_calc_signature(&drqst, server, sigptr, shash); + if (!rc) + memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); + +out: + if (allocate_crypto) + cifs_free_hash(&shash); + if (ses) + cifs_put_smb_ses(ses); + return rc; +} + +static int generate_key(struct cifs_ses *ses, struct kvec label, + struct kvec context, __u8 *key, unsigned int key_size) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L128[4] = {0, 0, 0, 128}; + __u8 L256[4] = {0, 0, 1, 0}; + int rc = 0; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + struct TCP_Server_Info *server = ses->server; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(key, 0x0, key_size); + + rc = smb3_crypto_shash_allocate(server); + if (rc) { + cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_setkey(server->secmech.hmacsha256->tfm, + ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_init(server->secmech.hmacsha256); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, i, 4); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, label.iov_base, label.iov_len); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, &zero, 1); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, context.iov_base, context.iov_len); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__); + goto smb3signkey_ret; + } + + if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { + rc = crypto_shash_update(server->secmech.hmacsha256, L256, 4); + } else { + rc = crypto_shash_update(server->secmech.hmacsha256, L128, 4); + } + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(server->secmech.hmacsha256, hashptr); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); + goto smb3signkey_ret; + } + + memcpy(key, hashptr, key_size); + +smb3signkey_ret: + return rc; +} + +struct derivation { + struct kvec label; + struct kvec context; +}; + +struct derivation_triplet { + struct derivation signing; + struct derivation encryption; + struct derivation decryption; +}; + +static int +generate_smb3signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct derivation_triplet *ptriplet) +{ + int rc; + bool is_binding = false; + int chan_index = 0; + + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + is_binding = (cifs_chan_needs_reconnect(ses, server) && + ses->ses_status == SES_GOOD); + + chan_index = cifs_ses_get_chan_index(ses, server); + /* TODO: introduce ref counting for channels when the can be freed */ + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + /* + * All channels use the same encryption/decryption keys but + * they have their own signing key. + * + * When we generate the keys, check if it is for a new channel + * (binding) in which case we only need to generate a signing + * key and store it in the channel as to not overwrite the + * master connection signing key stored in the session + */ + + if (is_binding) { + rc = generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, + ses->chans[chan_index].signkey, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + } else { + rc = generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, + ses->smb3signingkey, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + /* safe to access primary channel, since it will never go away */ + spin_lock(&ses->chan_lock); + memcpy(ses->chans[chan_index].signkey, ses->smb3signingkey, + SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); + + rc = generate_key(ses, ptriplet->encryption.label, + ptriplet->encryption.context, + ses->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + rc = generate_key(ses, ptriplet->decryption.label, + ptriplet->decryption.context, + ses->smb3decryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + } + + if (rc) + return rc; + +#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS + cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__); + /* + * The session id is opaque in terms of endianness, so we can't + * print it as a long long. we dump it as we got it on the wire + */ + cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), + &ses->Suid); + cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type); + cifs_dbg(VFS, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); + cifs_dbg(VFS, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, ses->smb3signingkey); + if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { + cifs_dbg(VFS, "ServerIn Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3encryptionkey); + cifs_dbg(VFS, "ServerOut Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3decryptionkey); + } else { + cifs_dbg(VFS, "ServerIn Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3encryptionkey); + cifs_dbg(VFS, "ServerOut Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey); + } +#endif + return rc; +} + +int +generate_smb30signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server) + +{ + struct derivation_triplet triplet; + struct derivation *d; + + d = &triplet.signing; + d->label.iov_base = "SMB2AESCMAC"; + d->label.iov_len = 12; + d->context.iov_base = "SmbSign"; + d->context.iov_len = 8; + + d = &triplet.encryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerIn "; + d->context.iov_len = 10; + + d = &triplet.decryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerOut"; + d->context.iov_len = 10; + + return generate_smb3signingkey(ses, server, &triplet); +} + +int +generate_smb311signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server) + +{ + struct derivation_triplet triplet; + struct derivation *d; + + d = &triplet.signing; + d->label.iov_base = "SMBSigningKey"; + d->label.iov_len = 14; + d->context.iov_base = ses->preauth_sha_hash; + d->context.iov_len = 64; + + d = &triplet.encryption; + d->label.iov_base = "SMBC2SCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = ses->preauth_sha_hash; + d->context.iov_len = 64; + + d = &triplet.decryption; + d->label.iov_base = "SMBS2CCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = ses->preauth_sha_hash; + d->context.iov_len = 64; + + return generate_smb3signingkey(ses, server, &triplet); +} + +int +smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, + bool allocate_crypto) +{ + int rc; + unsigned char smb3_signature[SMB2_CMACAES_SIZE]; + unsigned char *sigptr = smb3_signature; + struct kvec *iov = rqst->rq_iov; + struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; + struct shash_desc *shash = NULL; + struct smb_rqst drqst; + u8 key[SMB3_SIGN_KEY_SIZE]; + + rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); + if (unlikely(rc)) { + cifs_server_dbg(VFS, "%s: Could not get signing key\n", __func__); + return rc; + } + + if (allocate_crypto) { + rc = cifs_alloc_hash("cmac(aes)", &shash); + if (rc) + return rc; + } else { + shash = server->secmech.aes_cmac; + } + + memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); + memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); + + rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); + goto out; + } + + /* + * we already allocate aes_cmac when we init smb3 signing key, + * so unlike smb2 case we do not have to check here if secmech are + * initialized + */ + rc = crypto_shash_init(shash); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); + goto out; + } + + /* + * For SMB2+, __cifs_calc_signature() expects to sign only the actual + * data, that is, iov[0] should not contain a rfc1002 length. + * + * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to + * __cifs_calc_signature(). + */ + drqst = *rqst; + if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { + rc = crypto_shash_update(shash, iov[0].iov_base, + iov[0].iov_len); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with payload\n", + __func__); + goto out; + } + drqst.rq_iov++; + drqst.rq_nvec--; + } + + rc = __cifs_calc_signature(&drqst, server, sigptr, shash); + if (!rc) + memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); + +out: + if (allocate_crypto) + cifs_free_hash(&shash); + return rc; +} + +/* must be called with server->srv_mutex held */ +static int +smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ + int rc = 0; + struct smb2_hdr *shdr; + struct smb2_sess_setup_req *ssr; + bool is_binding; + bool is_signed; + + shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + ssr = (struct smb2_sess_setup_req *)shdr; + + is_binding = shdr->Command == SMB2_SESSION_SETUP && + (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING); + is_signed = shdr->Flags & SMB2_FLAGS_SIGNED; + + if (!is_signed) + return 0; + spin_lock(&server->srv_lock); + if (server->ops->need_neg && + server->ops->need_neg(server)) { + spin_unlock(&server->srv_lock); + return 0; + } + spin_unlock(&server->srv_lock); + if (!is_binding && !server->session_estab) { + strncpy(shdr->Signature, "BSRSPYL", 8); + return 0; + } + + rc = server->ops->calc_signature(rqst, server, false); + + return rc; +} + +int +smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ + unsigned int rc; + char server_response_sig[SMB2_SIGNATURE_SIZE]; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + + if ((shdr->Command == SMB2_NEGOTIATE) || + (shdr->Command == SMB2_SESSION_SETUP) || + (shdr->Command == SMB2_OPLOCK_BREAK) || + server->ignore_signature || + (!server->session_estab)) + return 0; + + /* + * BB what if signatures are supposed to be on for session but + * server does not send one? BB + */ + + /* Do not need to verify session setups with signature "BSRSPYL " */ + if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0) + cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", + shdr->Command); + + /* + * Save off the origiginal signature so we can modify the smb and check + * our calculated signature against what the server sent. + */ + memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE); + + memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + rc = server->ops->calc_signature(rqst, server, true); + + if (rc) + return rc; + + if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) { + cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n", + shdr->Command, shdr->MessageId); + return -EACCES; + } else + return 0; +} + +/* + * Set message id for the request. Should be called after wait_for_free_request + * and when srv_mutex is held. + */ +static inline void +smb2_seq_num_into_buf(struct TCP_Server_Info *server, + struct smb2_hdr *shdr) +{ + unsigned int i, num = le16_to_cpu(shdr->CreditCharge); + + shdr->MessageId = get_next_mid64(server); + /* skip message numbers according to CreditCharge field */ + for (i = 1; i < num; i++) + get_next_mid(server); +} + +static struct mid_q_entry * +smb2_mid_entry_alloc(const struct smb2_hdr *shdr, + struct TCP_Server_Info *server) +{ + struct mid_q_entry *temp; + unsigned int credits = le16_to_cpu(shdr->CreditCharge); + + if (server == NULL) { + cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n"); + return NULL; + } + + temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); + memset(temp, 0, sizeof(struct mid_q_entry)); + kref_init(&temp->refcount); + temp->mid = le64_to_cpu(shdr->MessageId); + temp->credits = credits > 0 ? credits : 1; + temp->pid = current->pid; + temp->command = shdr->Command; /* Always LE */ + temp->when_alloc = jiffies; + temp->server = server; + + /* + * The default is for the mid to be synchronous, so the + * default callback just wakes up the current task. + */ + get_task_struct(current); + temp->creator = current; + temp->callback = cifs_wake_up_task; + temp->callback_data = current; + + atomic_inc(&mid_count); + temp->mid_state = MID_REQUEST_ALLOCATED; + trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), temp->mid); + return temp; +} + +static int +smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, + struct smb2_hdr *shdr, struct mid_q_entry **mid) +{ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); + return -EAGAIN; + } + + if (server->tcpStatus == CifsNeedNegotiate && + shdr->Command != SMB2_NEGOTIATE) { + spin_unlock(&server->srv_lock); + return -EAGAIN; + } + spin_unlock(&server->srv_lock); + + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_NEW) { + if ((shdr->Command != SMB2_SESSION_SETUP) && + (shdr->Command != SMB2_NEGOTIATE)) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are setting up session */ + } + + if (ses->ses_status == SES_EXITING) { + if (shdr->Command != SMB2_LOGOFF) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are shutting down the session */ + } + spin_unlock(&ses->ses_lock); + + *mid = smb2_mid_entry_alloc(shdr, server); + if (*mid == NULL) + return -ENOMEM; + spin_lock(&server->mid_lock); + list_add_tail(&(*mid)->qhead, &server->pending_mid_q); + spin_unlock(&server->mid_lock); + + return 0; +} + +int +smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, + bool log_error) +{ + unsigned int len = mid->resp_buf_size; + struct kvec iov[1]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 1 }; + + iov[0].iov_base = (char *)mid->resp_buf; + iov[0].iov_len = len; + + dump_smb(mid->resp_buf, min_t(u32, 80, len)); + /* convert the length into a more usable form */ + if (len > 24 && server->sign && !mid->decrypted) { + int rc; + + rc = smb2_verify_signature(&rqst, server); + if (rc) + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + + return map_smb2_to_linux_error(mid->resp_buf, log_error); +} + +struct mid_q_entry * +smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, + struct smb_rqst *rqst) +{ + int rc; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + smb2_seq_num_into_buf(server, shdr); + + rc = smb2_get_mid_entry(ses, server, shdr, &mid); + if (rc) { + revert_current_mid_from_hdr(server, shdr); + return ERR_PTR(rc); + } + + rc = smb2_sign_rqst(rqst, server); + if (rc) { + revert_current_mid_from_hdr(server, shdr); + delete_mid(mid); + return ERR_PTR(rc); + } + + return mid; +} + +struct mid_q_entry * +smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ + int rc; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedNegotiate && + shdr->Command != SMB2_NEGOTIATE) { + spin_unlock(&server->srv_lock); + return ERR_PTR(-EAGAIN); + } + spin_unlock(&server->srv_lock); + + smb2_seq_num_into_buf(server, shdr); + + mid = smb2_mid_entry_alloc(shdr, server); + if (mid == NULL) { + revert_current_mid_from_hdr(server, shdr); + return ERR_PTR(-ENOMEM); + } + + rc = smb2_sign_rqst(rqst, server); + if (rc) { + revert_current_mid_from_hdr(server, shdr); + release_mid(mid); + return ERR_PTR(rc); + } + + return mid; +} + +int +smb3_crypto_aead_allocate(struct TCP_Server_Info *server) +{ + struct crypto_aead *tfm; + + if (!server->secmech.enc) { + if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + else + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + if (IS_ERR(tfm)) { + cifs_server_dbg(VFS, "%s: Failed alloc encrypt aead\n", + __func__); + return PTR_ERR(tfm); + } + server->secmech.enc = tfm; + } + + if (!server->secmech.dec) { + if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + else + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + if (IS_ERR(tfm)) { + crypto_free_aead(server->secmech.enc); + server->secmech.enc = NULL; + cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n", + __func__); + return PTR_ERR(tfm); + } + server->secmech.dec = tfm; + } + + return 0; +} diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c new file mode 100644 index 000000000000..cf923f211c51 --- /dev/null +++ b/fs/smb/client/smbdirect.c @@ -0,0 +1,2494 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017, Microsoft Corporation. + * + * Author(s): Long Li + */ +#include +#include +#include "smbdirect.h" +#include "cifs_debug.h" +#include "cifsproto.h" +#include "smb2proto.h" + +static struct smbd_response *get_empty_queue_buffer( + struct smbd_connection *info); +static struct smbd_response *get_receive_buffer( + struct smbd_connection *info); +static void put_receive_buffer( + struct smbd_connection *info, + struct smbd_response *response); +static int allocate_receive_buffers(struct smbd_connection *info, int num_buf); +static void destroy_receive_buffers(struct smbd_connection *info); + +static void put_empty_packet( + struct smbd_connection *info, struct smbd_response *response); +static void enqueue_reassembly( + struct smbd_connection *info, + struct smbd_response *response, int data_length); +static struct smbd_response *_get_first_reassembly( + struct smbd_connection *info); + +static int smbd_post_recv( + struct smbd_connection *info, + struct smbd_response *response); + +static int smbd_post_send_empty(struct smbd_connection *info); +static int smbd_post_send_data( + struct smbd_connection *info, + struct kvec *iov, int n_vec, int remaining_data_length); +static int smbd_post_send_page(struct smbd_connection *info, + struct page *page, unsigned long offset, + size_t size, int remaining_data_length); + +static void destroy_mr_list(struct smbd_connection *info); +static int allocate_mr_list(struct smbd_connection *info); + +/* SMBD version number */ +#define SMBD_V1 0x0100 + +/* Port numbers for SMBD transport */ +#define SMB_PORT 445 +#define SMBD_PORT 5445 + +/* Address lookup and resolve timeout in ms */ +#define RDMA_RESOLVE_TIMEOUT 5000 + +/* SMBD negotiation timeout in seconds */ +#define SMBD_NEGOTIATE_TIMEOUT 120 + +/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */ +#define SMBD_MIN_RECEIVE_SIZE 128 +#define SMBD_MIN_FRAGMENTED_SIZE 131072 + +/* + * Default maximum number of RDMA read/write outstanding on this connection + * This value is possibly decreased during QP creation on hardware limit + */ +#define SMBD_CM_RESPONDER_RESOURCES 32 + +/* Maximum number of retries on data transfer operations */ +#define SMBD_CM_RETRY 6 +/* No need to retry on Receiver Not Ready since SMBD manages credits */ +#define SMBD_CM_RNR_RETRY 0 + +/* + * User configurable initial values per SMBD transport connection + * as defined in [MS-SMBD] 3.1.1.1 + * Those may change after a SMBD negotiation + */ +/* The local peer's maximum number of credits to grant to the peer */ +int smbd_receive_credit_max = 255; + +/* The remote peer's credit request of local peer */ +int smbd_send_credit_target = 255; + +/* The maximum single message size can be sent to remote peer */ +int smbd_max_send_size = 1364; + +/* The maximum fragmented upper-layer payload receive size supported */ +int smbd_max_fragmented_recv_size = 1024 * 1024; + +/* The maximum single-message size which can be received */ +int smbd_max_receive_size = 1364; + +/* The timeout to initiate send of a keepalive message on idle */ +int smbd_keep_alive_interval = 120; + +/* + * User configurable initial values for RDMA transport + * The actual values used may be lower and are limited to hardware capabilities + */ +/* Default maximum number of pages in a single RDMA write/read */ +int smbd_max_frmr_depth = 2048; + +/* If payload is less than this byte, use RDMA send/recv not read/write */ +int rdma_readwrite_threshold = 4096; + +/* Transport logging functions + * Logging are defined as classes. They can be OR'ed to define the actual + * logging level via module parameter smbd_logging_class + * e.g. cifs.smbd_logging_class=0xa0 will log all log_rdma_recv() and + * log_rdma_event() + */ +#define LOG_OUTGOING 0x1 +#define LOG_INCOMING 0x2 +#define LOG_READ 0x4 +#define LOG_WRITE 0x8 +#define LOG_RDMA_SEND 0x10 +#define LOG_RDMA_RECV 0x20 +#define LOG_KEEP_ALIVE 0x40 +#define LOG_RDMA_EVENT 0x80 +#define LOG_RDMA_MR 0x100 +static unsigned int smbd_logging_class; +module_param(smbd_logging_class, uint, 0644); +MODULE_PARM_DESC(smbd_logging_class, + "Logging class for SMBD transport 0x0 to 0x100"); + +#define ERR 0x0 +#define INFO 0x1 +static unsigned int smbd_logging_level = ERR; +module_param(smbd_logging_level, uint, 0644); +MODULE_PARM_DESC(smbd_logging_level, + "Logging level for SMBD transport, 0 (default): error, 1: info"); + +#define log_rdma(level, class, fmt, args...) \ +do { \ + if (level <= smbd_logging_level || class & smbd_logging_class) \ + cifs_dbg(VFS, "%s:%d " fmt, __func__, __LINE__, ##args);\ +} while (0) + +#define log_outgoing(level, fmt, args...) \ + log_rdma(level, LOG_OUTGOING, fmt, ##args) +#define log_incoming(level, fmt, args...) \ + log_rdma(level, LOG_INCOMING, fmt, ##args) +#define log_read(level, fmt, args...) log_rdma(level, LOG_READ, fmt, ##args) +#define log_write(level, fmt, args...) log_rdma(level, LOG_WRITE, fmt, ##args) +#define log_rdma_send(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_SEND, fmt, ##args) +#define log_rdma_recv(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_RECV, fmt, ##args) +#define log_keep_alive(level, fmt, args...) \ + log_rdma(level, LOG_KEEP_ALIVE, fmt, ##args) +#define log_rdma_event(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_EVENT, fmt, ##args) +#define log_rdma_mr(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_MR, fmt, ##args) + +static void smbd_disconnect_rdma_work(struct work_struct *work) +{ + struct smbd_connection *info = + container_of(work, struct smbd_connection, disconnect_work); + + if (info->transport_status == SMBD_CONNECTED) { + info->transport_status = SMBD_DISCONNECTING; + rdma_disconnect(info->id); + } +} + +static void smbd_disconnect_rdma_connection(struct smbd_connection *info) +{ + queue_work(info->workqueue, &info->disconnect_work); +} + +/* Upcall from RDMA CM */ +static int smbd_conn_upcall( + struct rdma_cm_id *id, struct rdma_cm_event *event) +{ + struct smbd_connection *info = id->context; + + log_rdma_event(INFO, "event=%d status=%d\n", + event->event, event->status); + + switch (event->event) { + case RDMA_CM_EVENT_ADDR_RESOLVED: + case RDMA_CM_EVENT_ROUTE_RESOLVED: + info->ri_rc = 0; + complete(&info->ri_done); + break; + + case RDMA_CM_EVENT_ADDR_ERROR: + info->ri_rc = -EHOSTUNREACH; + complete(&info->ri_done); + break; + + case RDMA_CM_EVENT_ROUTE_ERROR: + info->ri_rc = -ENETUNREACH; + complete(&info->ri_done); + break; + + case RDMA_CM_EVENT_ESTABLISHED: + log_rdma_event(INFO, "connected event=%d\n", event->event); + info->transport_status = SMBD_CONNECTED; + wake_up_interruptible(&info->conn_wait); + break; + + case RDMA_CM_EVENT_CONNECT_ERROR: + case RDMA_CM_EVENT_UNREACHABLE: + case RDMA_CM_EVENT_REJECTED: + log_rdma_event(INFO, "connecting failed event=%d\n", event->event); + info->transport_status = SMBD_DISCONNECTED; + wake_up_interruptible(&info->conn_wait); + break; + + case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_DISCONNECTED: + /* This happenes when we fail the negotiation */ + if (info->transport_status == SMBD_NEGOTIATE_FAILED) { + info->transport_status = SMBD_DISCONNECTED; + wake_up(&info->conn_wait); + break; + } + + info->transport_status = SMBD_DISCONNECTED; + wake_up_interruptible(&info->disconn_wait); + wake_up_interruptible(&info->wait_reassembly_queue); + wake_up_interruptible_all(&info->wait_send_queue); + break; + + default: + break; + } + + return 0; +} + +/* Upcall from RDMA QP */ +static void +smbd_qp_async_error_upcall(struct ib_event *event, void *context) +{ + struct smbd_connection *info = context; + + log_rdma_event(ERR, "%s on device %s info %p\n", + ib_event_msg(event->event), event->device->name, info); + + switch (event->event) { + case IB_EVENT_CQ_ERR: + case IB_EVENT_QP_FATAL: + smbd_disconnect_rdma_connection(info); + break; + + default: + break; + } +} + +static inline void *smbd_request_payload(struct smbd_request *request) +{ + return (void *)request->packet; +} + +static inline void *smbd_response_payload(struct smbd_response *response) +{ + return (void *)response->packet; +} + +/* Called when a RDMA send is done */ +static void send_done(struct ib_cq *cq, struct ib_wc *wc) +{ + int i; + struct smbd_request *request = + container_of(wc->wr_cqe, struct smbd_request, cqe); + + log_rdma_send(INFO, "smbd_request 0x%p completed wc->status=%d\n", + request, wc->status); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { + log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n", + wc->status, wc->opcode); + smbd_disconnect_rdma_connection(request->info); + } + + for (i = 0; i < request->num_sge; i++) + ib_dma_unmap_single(request->info->id->device, + request->sge[i].addr, + request->sge[i].length, + DMA_TO_DEVICE); + + if (atomic_dec_and_test(&request->info->send_pending)) + wake_up(&request->info->wait_send_pending); + + wake_up(&request->info->wait_post_send); + + mempool_free(request, request->info->request_mempool); +} + +static void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp) +{ + log_rdma_event(INFO, "resp message min_version %u max_version %u negotiated_version %u credits_requested %u credits_granted %u status %u max_readwrite_size %u preferred_send_size %u max_receive_size %u max_fragmented_size %u\n", + resp->min_version, resp->max_version, + resp->negotiated_version, resp->credits_requested, + resp->credits_granted, resp->status, + resp->max_readwrite_size, resp->preferred_send_size, + resp->max_receive_size, resp->max_fragmented_size); +} + +/* + * Process a negotiation response message, according to [MS-SMBD]3.1.5.7 + * response, packet_length: the negotiation response message + * return value: true if negotiation is a success, false if failed + */ +static bool process_negotiation_response( + struct smbd_response *response, int packet_length) +{ + struct smbd_connection *info = response->info; + struct smbd_negotiate_resp *packet = smbd_response_payload(response); + + if (packet_length < sizeof(struct smbd_negotiate_resp)) { + log_rdma_event(ERR, + "error: packet_length=%d\n", packet_length); + return false; + } + + if (le16_to_cpu(packet->negotiated_version) != SMBD_V1) { + log_rdma_event(ERR, "error: negotiated_version=%x\n", + le16_to_cpu(packet->negotiated_version)); + return false; + } + info->protocol = le16_to_cpu(packet->negotiated_version); + + if (packet->credits_requested == 0) { + log_rdma_event(ERR, "error: credits_requested==0\n"); + return false; + } + info->receive_credit_target = le16_to_cpu(packet->credits_requested); + + if (packet->credits_granted == 0) { + log_rdma_event(ERR, "error: credits_granted==0\n"); + return false; + } + atomic_set(&info->send_credits, le16_to_cpu(packet->credits_granted)); + + atomic_set(&info->receive_credits, 0); + + if (le32_to_cpu(packet->preferred_send_size) > info->max_receive_size) { + log_rdma_event(ERR, "error: preferred_send_size=%d\n", + le32_to_cpu(packet->preferred_send_size)); + return false; + } + info->max_receive_size = le32_to_cpu(packet->preferred_send_size); + + if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) { + log_rdma_event(ERR, "error: max_receive_size=%d\n", + le32_to_cpu(packet->max_receive_size)); + return false; + } + info->max_send_size = min_t(int, info->max_send_size, + le32_to_cpu(packet->max_receive_size)); + + if (le32_to_cpu(packet->max_fragmented_size) < + SMBD_MIN_FRAGMENTED_SIZE) { + log_rdma_event(ERR, "error: max_fragmented_size=%d\n", + le32_to_cpu(packet->max_fragmented_size)); + return false; + } + info->max_fragmented_send_size = + le32_to_cpu(packet->max_fragmented_size); + info->rdma_readwrite_threshold = + rdma_readwrite_threshold > info->max_fragmented_send_size ? + info->max_fragmented_send_size : + rdma_readwrite_threshold; + + + info->max_readwrite_size = min_t(u32, + le32_to_cpu(packet->max_readwrite_size), + info->max_frmr_depth * PAGE_SIZE); + info->max_frmr_depth = info->max_readwrite_size / PAGE_SIZE; + + return true; +} + +static void smbd_post_send_credits(struct work_struct *work) +{ + int ret = 0; + int use_receive_queue = 1; + int rc; + struct smbd_response *response; + struct smbd_connection *info = + container_of(work, struct smbd_connection, + post_send_credits_work); + + if (info->transport_status != SMBD_CONNECTED) { + wake_up(&info->wait_receive_queues); + return; + } + + if (info->receive_credit_target > + atomic_read(&info->receive_credits)) { + while (true) { + if (use_receive_queue) + response = get_receive_buffer(info); + else + response = get_empty_queue_buffer(info); + if (!response) { + /* now switch to emtpy packet queue */ + if (use_receive_queue) { + use_receive_queue = 0; + continue; + } else + break; + } + + response->type = SMBD_TRANSFER_DATA; + response->first_segment = false; + rc = smbd_post_recv(info, response); + if (rc) { + log_rdma_recv(ERR, + "post_recv failed rc=%d\n", rc); + put_receive_buffer(info, response); + break; + } + + ret++; + } + } + + spin_lock(&info->lock_new_credits_offered); + info->new_credits_offered += ret; + spin_unlock(&info->lock_new_credits_offered); + + /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ + info->send_immediate = true; + if (atomic_read(&info->receive_credits) < + info->receive_credit_target - 1) { + if (info->keep_alive_requested == KEEP_ALIVE_PENDING || + info->send_immediate) { + log_keep_alive(INFO, "send an empty message\n"); + smbd_post_send_empty(info); + } + } +} + +/* Called from softirq, when recv is done */ +static void recv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smbd_data_transfer *data_transfer; + struct smbd_response *response = + container_of(wc->wr_cqe, struct smbd_response, cqe); + struct smbd_connection *info = response->info; + int data_length = 0; + + log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n", + response, response->type, wc->status, wc->opcode, + wc->byte_len, wc->pkey_index); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + log_rdma_recv(INFO, "wc->status=%d opcode=%d\n", + wc->status, wc->opcode); + smbd_disconnect_rdma_connection(info); + goto error; + } + + ib_dma_sync_single_for_cpu( + wc->qp->device, + response->sge.addr, + response->sge.length, + DMA_FROM_DEVICE); + + switch (response->type) { + /* SMBD negotiation response */ + case SMBD_NEGOTIATE_RESP: + dump_smbd_negotiate_resp(smbd_response_payload(response)); + info->full_packet_received = true; + info->negotiate_done = + process_negotiation_response(response, wc->byte_len); + complete(&info->negotiate_completion); + break; + + /* SMBD data transfer packet */ + case SMBD_TRANSFER_DATA: + data_transfer = smbd_response_payload(response); + data_length = le32_to_cpu(data_transfer->data_length); + + /* + * If this is a packet with data playload place the data in + * reassembly queue and wake up the reading thread + */ + if (data_length) { + if (info->full_packet_received) + response->first_segment = true; + + if (le32_to_cpu(data_transfer->remaining_data_length)) + info->full_packet_received = false; + else + info->full_packet_received = true; + + enqueue_reassembly( + info, + response, + data_length); + } else + put_empty_packet(info, response); + + if (data_length) + wake_up_interruptible(&info->wait_reassembly_queue); + + atomic_dec(&info->receive_credits); + info->receive_credit_target = + le16_to_cpu(data_transfer->credits_requested); + if (le16_to_cpu(data_transfer->credits_granted)) { + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &info->send_credits); + /* + * We have new send credits granted from remote peer + * If any sender is waiting for credits, unblock it + */ + wake_up_interruptible(&info->wait_send_queue); + } + + log_incoming(INFO, "data flags %d data_offset %d data_length %d remaining_data_length %d\n", + le16_to_cpu(data_transfer->flags), + le32_to_cpu(data_transfer->data_offset), + le32_to_cpu(data_transfer->data_length), + le32_to_cpu(data_transfer->remaining_data_length)); + + /* Send a KEEP_ALIVE response right away if requested */ + info->keep_alive_requested = KEEP_ALIVE_NONE; + if (le16_to_cpu(data_transfer->flags) & + SMB_DIRECT_RESPONSE_REQUESTED) { + info->keep_alive_requested = KEEP_ALIVE_PENDING; + } + + return; + + default: + log_rdma_recv(ERR, + "unexpected response type=%d\n", response->type); + } + +error: + put_receive_buffer(info, response); +} + +static struct rdma_cm_id *smbd_create_id( + struct smbd_connection *info, + struct sockaddr *dstaddr, int port) +{ + struct rdma_cm_id *id; + int rc; + __be16 *sport; + + id = rdma_create_id(&init_net, smbd_conn_upcall, info, + RDMA_PS_TCP, IB_QPT_RC); + if (IS_ERR(id)) { + rc = PTR_ERR(id); + log_rdma_event(ERR, "rdma_create_id() failed %i\n", rc); + return id; + } + + if (dstaddr->sa_family == AF_INET6) + sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; + else + sport = &((struct sockaddr_in *)dstaddr)->sin_port; + + *sport = htons(port); + + init_completion(&info->ri_done); + info->ri_rc = -ETIMEDOUT; + + rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr, + RDMA_RESOLVE_TIMEOUT); + if (rc) { + log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc); + goto out; + } + rc = wait_for_completion_interruptible_timeout( + &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); + /* e.g. if interrupted returns -ERESTARTSYS */ + if (rc < 0) { + log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); + goto out; + } + rc = info->ri_rc; + if (rc) { + log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc); + goto out; + } + + info->ri_rc = -ETIMEDOUT; + rc = rdma_resolve_route(id, RDMA_RESOLVE_TIMEOUT); + if (rc) { + log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc); + goto out; + } + rc = wait_for_completion_interruptible_timeout( + &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); + /* e.g. if interrupted returns -ERESTARTSYS */ + if (rc < 0) { + log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); + goto out; + } + rc = info->ri_rc; + if (rc) { + log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc); + goto out; + } + + return id; + +out: + rdma_destroy_id(id); + return ERR_PTR(rc); +} + +/* + * Test if FRWR (Fast Registration Work Requests) is supported on the device + * This implementation requries FRWR on RDMA read/write + * return value: true if it is supported + */ +static bool frwr_is_supported(struct ib_device_attr *attrs) +{ + if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) + return false; + if (attrs->max_fast_reg_page_list_len == 0) + return false; + return true; +} + +static int smbd_ia_open( + struct smbd_connection *info, + struct sockaddr *dstaddr, int port) +{ + int rc; + + info->id = smbd_create_id(info, dstaddr, port); + if (IS_ERR(info->id)) { + rc = PTR_ERR(info->id); + goto out1; + } + + if (!frwr_is_supported(&info->id->device->attrs)) { + log_rdma_event(ERR, "Fast Registration Work Requests (FRWR) is not supported\n"); + log_rdma_event(ERR, "Device capability flags = %llx max_fast_reg_page_list_len = %u\n", + info->id->device->attrs.device_cap_flags, + info->id->device->attrs.max_fast_reg_page_list_len); + rc = -EPROTONOSUPPORT; + goto out2; + } + info->max_frmr_depth = min_t(int, + smbd_max_frmr_depth, + info->id->device->attrs.max_fast_reg_page_list_len); + info->mr_type = IB_MR_TYPE_MEM_REG; + if (info->id->device->attrs.kernel_cap_flags & IBK_SG_GAPS_REG) + info->mr_type = IB_MR_TYPE_SG_GAPS; + + info->pd = ib_alloc_pd(info->id->device, 0); + if (IS_ERR(info->pd)) { + rc = PTR_ERR(info->pd); + log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc); + goto out2; + } + + return 0; + +out2: + rdma_destroy_id(info->id); + info->id = NULL; + +out1: + return rc; +} + +/* + * Send a negotiation request message to the peer + * The negotiation procedure is in [MS-SMBD] 3.1.5.2 and 3.1.5.3 + * After negotiation, the transport is connected and ready for + * carrying upper layer SMB payload + */ +static int smbd_post_send_negotiate_req(struct smbd_connection *info) +{ + struct ib_send_wr send_wr; + int rc = -ENOMEM; + struct smbd_request *request; + struct smbd_negotiate_req *packet; + + request = mempool_alloc(info->request_mempool, GFP_KERNEL); + if (!request) + return rc; + + request->info = info; + + packet = smbd_request_payload(request); + packet->min_version = cpu_to_le16(SMBD_V1); + packet->max_version = cpu_to_le16(SMBD_V1); + packet->reserved = 0; + packet->credits_requested = cpu_to_le16(info->send_credit_target); + packet->preferred_send_size = cpu_to_le32(info->max_send_size); + packet->max_receive_size = cpu_to_le32(info->max_receive_size); + packet->max_fragmented_size = + cpu_to_le32(info->max_fragmented_recv_size); + + request->num_sge = 1; + request->sge[0].addr = ib_dma_map_single( + info->id->device, (void *)packet, + sizeof(*packet), DMA_TO_DEVICE); + if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { + rc = -EIO; + goto dma_mapping_failed; + } + + request->sge[0].length = sizeof(*packet); + request->sge[0].lkey = info->pd->local_dma_lkey; + + ib_dma_sync_single_for_device( + info->id->device, request->sge[0].addr, + request->sge[0].length, DMA_TO_DEVICE); + + request->cqe.done = send_done; + + send_wr.next = NULL; + send_wr.wr_cqe = &request->cqe; + send_wr.sg_list = request->sge; + send_wr.num_sge = request->num_sge; + send_wr.opcode = IB_WR_SEND; + send_wr.send_flags = IB_SEND_SIGNALED; + + log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n", + request->sge[0].addr, + request->sge[0].length, request->sge[0].lkey); + + atomic_inc(&info->send_pending); + rc = ib_post_send(info->id->qp, &send_wr, NULL); + if (!rc) + return 0; + + /* if we reach here, post send failed */ + log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); + atomic_dec(&info->send_pending); + ib_dma_unmap_single(info->id->device, request->sge[0].addr, + request->sge[0].length, DMA_TO_DEVICE); + + smbd_disconnect_rdma_connection(info); + +dma_mapping_failed: + mempool_free(request, info->request_mempool); + return rc; +} + +/* + * Extend the credits to remote peer + * This implements [MS-SMBD] 3.1.5.9 + * The idea is that we should extend credits to remote peer as quickly as + * it's allowed, to maintain data flow. We allocate as much receive + * buffer as possible, and extend the receive credits to remote peer + * return value: the new credtis being granted. + */ +static int manage_credits_prior_sending(struct smbd_connection *info) +{ + int new_credits; + + spin_lock(&info->lock_new_credits_offered); + new_credits = info->new_credits_offered; + info->new_credits_offered = 0; + spin_unlock(&info->lock_new_credits_offered); + + return new_credits; +} + +/* + * Check if we need to send a KEEP_ALIVE message + * The idle connection timer triggers a KEEP_ALIVE message when expires + * SMB_DIRECT_RESPONSE_REQUESTED is set in the message flag to have peer send + * back a response. + * return value: + * 1 if SMB_DIRECT_RESPONSE_REQUESTED needs to be set + * 0: otherwise + */ +static int manage_keep_alive_before_sending(struct smbd_connection *info) +{ + if (info->keep_alive_requested == KEEP_ALIVE_PENDING) { + info->keep_alive_requested = KEEP_ALIVE_SENT; + return 1; + } + return 0; +} + +/* Post the send request */ +static int smbd_post_send(struct smbd_connection *info, + struct smbd_request *request) +{ + struct ib_send_wr send_wr; + int rc, i; + + for (i = 0; i < request->num_sge; i++) { + log_rdma_send(INFO, + "rdma_request sge[%d] addr=0x%llx length=%u\n", + i, request->sge[i].addr, request->sge[i].length); + ib_dma_sync_single_for_device( + info->id->device, + request->sge[i].addr, + request->sge[i].length, + DMA_TO_DEVICE); + } + + request->cqe.done = send_done; + + send_wr.next = NULL; + send_wr.wr_cqe = &request->cqe; + send_wr.sg_list = request->sge; + send_wr.num_sge = request->num_sge; + send_wr.opcode = IB_WR_SEND; + send_wr.send_flags = IB_SEND_SIGNALED; + + rc = ib_post_send(info->id->qp, &send_wr, NULL); + if (rc) { + log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); + smbd_disconnect_rdma_connection(info); + rc = -EAGAIN; + } else + /* Reset timer for idle connection after packet is sent */ + mod_delayed_work(info->workqueue, &info->idle_timer_work, + info->keep_alive_interval*HZ); + + return rc; +} + +static int smbd_post_send_sgl(struct smbd_connection *info, + struct scatterlist *sgl, int data_length, int remaining_data_length) +{ + int num_sgs; + int i, rc; + int header_length; + struct smbd_request *request; + struct smbd_data_transfer *packet; + int new_credits; + struct scatterlist *sg; + +wait_credit: + /* Wait for send credits. A SMBD packet needs one credit */ + rc = wait_event_interruptible(info->wait_send_queue, + atomic_read(&info->send_credits) > 0 || + info->transport_status != SMBD_CONNECTED); + if (rc) + goto err_wait_credit; + + if (info->transport_status != SMBD_CONNECTED) { + log_outgoing(ERR, "disconnected not sending on wait_credit\n"); + rc = -EAGAIN; + goto err_wait_credit; + } + if (unlikely(atomic_dec_return(&info->send_credits) < 0)) { + atomic_inc(&info->send_credits); + goto wait_credit; + } + +wait_send_queue: + wait_event(info->wait_post_send, + atomic_read(&info->send_pending) < info->send_credit_target || + info->transport_status != SMBD_CONNECTED); + + if (info->transport_status != SMBD_CONNECTED) { + log_outgoing(ERR, "disconnected not sending on wait_send_queue\n"); + rc = -EAGAIN; + goto err_wait_send_queue; + } + + if (unlikely(atomic_inc_return(&info->send_pending) > + info->send_credit_target)) { + atomic_dec(&info->send_pending); + goto wait_send_queue; + } + + request = mempool_alloc(info->request_mempool, GFP_KERNEL); + if (!request) { + rc = -ENOMEM; + goto err_alloc; + } + + request->info = info; + + /* Fill in the packet header */ + packet = smbd_request_payload(request); + packet->credits_requested = cpu_to_le16(info->send_credit_target); + + new_credits = manage_credits_prior_sending(info); + atomic_add(new_credits, &info->receive_credits); + packet->credits_granted = cpu_to_le16(new_credits); + + info->send_immediate = false; + + packet->flags = 0; + if (manage_keep_alive_before_sending(info)) + packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED); + + packet->reserved = 0; + if (!data_length) + packet->data_offset = 0; + else + packet->data_offset = cpu_to_le32(24); + packet->data_length = cpu_to_le32(data_length); + packet->remaining_data_length = cpu_to_le32(remaining_data_length); + packet->padding = 0; + + log_outgoing(INFO, "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); + + /* Map the packet to DMA */ + header_length = sizeof(struct smbd_data_transfer); + /* If this is a packet without payload, don't send padding */ + if (!data_length) + header_length = offsetof(struct smbd_data_transfer, padding); + + request->num_sge = 1; + request->sge[0].addr = ib_dma_map_single(info->id->device, + (void *)packet, + header_length, + DMA_TO_DEVICE); + if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { + rc = -EIO; + request->sge[0].addr = 0; + goto err_dma; + } + + request->sge[0].length = header_length; + request->sge[0].lkey = info->pd->local_dma_lkey; + + /* Fill in the packet data payload */ + num_sgs = sgl ? sg_nents(sgl) : 0; + for_each_sg(sgl, sg, num_sgs, i) { + request->sge[i+1].addr = + ib_dma_map_page(info->id->device, sg_page(sg), + sg->offset, sg->length, DMA_TO_DEVICE); + if (ib_dma_mapping_error( + info->id->device, request->sge[i+1].addr)) { + rc = -EIO; + request->sge[i+1].addr = 0; + goto err_dma; + } + request->sge[i+1].length = sg->length; + request->sge[i+1].lkey = info->pd->local_dma_lkey; + request->num_sge++; + } + + rc = smbd_post_send(info, request); + if (!rc) + return 0; + +err_dma: + for (i = 0; i < request->num_sge; i++) + if (request->sge[i].addr) + ib_dma_unmap_single(info->id->device, + request->sge[i].addr, + request->sge[i].length, + DMA_TO_DEVICE); + mempool_free(request, info->request_mempool); + + /* roll back receive credits and credits to be offered */ + spin_lock(&info->lock_new_credits_offered); + info->new_credits_offered += new_credits; + spin_unlock(&info->lock_new_credits_offered); + atomic_sub(new_credits, &info->receive_credits); + +err_alloc: + if (atomic_dec_and_test(&info->send_pending)) + wake_up(&info->wait_send_pending); + +err_wait_send_queue: + /* roll back send credits and pending */ + atomic_inc(&info->send_credits); + +err_wait_credit: + return rc; +} + +/* + * Send a page + * page: the page to send + * offset: offset in the page to send + * size: length in the page to send + * remaining_data_length: remaining data to send in this payload + */ +static int smbd_post_send_page(struct smbd_connection *info, struct page *page, + unsigned long offset, size_t size, int remaining_data_length) +{ + struct scatterlist sgl; + + sg_init_table(&sgl, 1); + sg_set_page(&sgl, page, size, offset); + + return smbd_post_send_sgl(info, &sgl, size, remaining_data_length); +} + +/* + * Send an empty message + * Empty message is used to extend credits to peer to for keep live + * while there is no upper layer payload to send at the time + */ +static int smbd_post_send_empty(struct smbd_connection *info) +{ + info->count_send_empty++; + return smbd_post_send_sgl(info, NULL, 0, 0); +} + +/* + * Send a data buffer + * iov: the iov array describing the data buffers + * n_vec: number of iov array + * remaining_data_length: remaining data to send following this packet + * in segmented SMBD packet + */ +static int smbd_post_send_data( + struct smbd_connection *info, struct kvec *iov, int n_vec, + int remaining_data_length) +{ + int i; + u32 data_length = 0; + struct scatterlist sgl[SMBDIRECT_MAX_SEND_SGE - 1]; + + if (n_vec > SMBDIRECT_MAX_SEND_SGE - 1) { + cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec); + return -EINVAL; + } + + sg_init_table(sgl, n_vec); + for (i = 0; i < n_vec; i++) { + data_length += iov[i].iov_len; + sg_set_buf(&sgl[i], iov[i].iov_base, iov[i].iov_len); + } + + return smbd_post_send_sgl(info, sgl, data_length, remaining_data_length); +} + +/* + * Post a receive request to the transport + * The remote peer can only send data when a receive request is posted + * The interaction is controlled by send/receive credit system + */ +static int smbd_post_recv( + struct smbd_connection *info, struct smbd_response *response) +{ + struct ib_recv_wr recv_wr; + int rc = -EIO; + + response->sge.addr = ib_dma_map_single( + info->id->device, response->packet, + info->max_receive_size, DMA_FROM_DEVICE); + if (ib_dma_mapping_error(info->id->device, response->sge.addr)) + return rc; + + response->sge.length = info->max_receive_size; + response->sge.lkey = info->pd->local_dma_lkey; + + response->cqe.done = recv_done; + + recv_wr.wr_cqe = &response->cqe; + recv_wr.next = NULL; + recv_wr.sg_list = &response->sge; + recv_wr.num_sge = 1; + + rc = ib_post_recv(info->id->qp, &recv_wr, NULL); + if (rc) { + ib_dma_unmap_single(info->id->device, response->sge.addr, + response->sge.length, DMA_FROM_DEVICE); + smbd_disconnect_rdma_connection(info); + log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc); + } + + return rc; +} + +/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */ +static int smbd_negotiate(struct smbd_connection *info) +{ + int rc; + struct smbd_response *response = get_receive_buffer(info); + + response->type = SMBD_NEGOTIATE_RESP; + rc = smbd_post_recv(info, response); + log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n", + rc, response->sge.addr, + response->sge.length, response->sge.lkey); + if (rc) + return rc; + + init_completion(&info->negotiate_completion); + info->negotiate_done = false; + rc = smbd_post_send_negotiate_req(info); + if (rc) + return rc; + + rc = wait_for_completion_interruptible_timeout( + &info->negotiate_completion, SMBD_NEGOTIATE_TIMEOUT * HZ); + log_rdma_event(INFO, "wait_for_completion_timeout rc=%d\n", rc); + + if (info->negotiate_done) + return 0; + + if (rc == 0) + rc = -ETIMEDOUT; + else if (rc == -ERESTARTSYS) + rc = -EINTR; + else + rc = -ENOTCONN; + + return rc; +} + +static void put_empty_packet( + struct smbd_connection *info, struct smbd_response *response) +{ + spin_lock(&info->empty_packet_queue_lock); + list_add_tail(&response->list, &info->empty_packet_queue); + info->count_empty_packet_queue++; + spin_unlock(&info->empty_packet_queue_lock); + + queue_work(info->workqueue, &info->post_send_credits_work); +} + +/* + * Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1 + * This is a queue for reassembling upper layer payload and present to upper + * layer. All the inncoming payload go to the reassembly queue, regardless of + * if reassembly is required. The uuper layer code reads from the queue for all + * incoming payloads. + * Put a received packet to the reassembly queue + * response: the packet received + * data_length: the size of payload in this packet + */ +static void enqueue_reassembly( + struct smbd_connection *info, + struct smbd_response *response, + int data_length) +{ + spin_lock(&info->reassembly_queue_lock); + list_add_tail(&response->list, &info->reassembly_queue); + info->reassembly_queue_length++; + /* + * Make sure reassembly_data_length is updated after list and + * reassembly_queue_length are updated. On the dequeue side + * reassembly_data_length is checked without a lock to determine + * if reassembly_queue_length and list is up to date + */ + virt_wmb(); + info->reassembly_data_length += data_length; + spin_unlock(&info->reassembly_queue_lock); + info->count_reassembly_queue++; + info->count_enqueue_reassembly_queue++; +} + +/* + * Get the first entry at the front of reassembly queue + * Caller is responsible for locking + * return value: the first entry if any, NULL if queue is empty + */ +static struct smbd_response *_get_first_reassembly(struct smbd_connection *info) +{ + struct smbd_response *ret = NULL; + + if (!list_empty(&info->reassembly_queue)) { + ret = list_first_entry( + &info->reassembly_queue, + struct smbd_response, list); + } + return ret; +} + +static struct smbd_response *get_empty_queue_buffer( + struct smbd_connection *info) +{ + struct smbd_response *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&info->empty_packet_queue_lock, flags); + if (!list_empty(&info->empty_packet_queue)) { + ret = list_first_entry( + &info->empty_packet_queue, + struct smbd_response, list); + list_del(&ret->list); + info->count_empty_packet_queue--; + } + spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags); + + return ret; +} + +/* + * Get a receive buffer + * For each remote send, we need to post a receive. The receive buffers are + * pre-allocated in advance. + * return value: the receive buffer, NULL if none is available + */ +static struct smbd_response *get_receive_buffer(struct smbd_connection *info) +{ + struct smbd_response *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&info->receive_queue_lock, flags); + if (!list_empty(&info->receive_queue)) { + ret = list_first_entry( + &info->receive_queue, + struct smbd_response, list); + list_del(&ret->list); + info->count_receive_queue--; + info->count_get_receive_buffer++; + } + spin_unlock_irqrestore(&info->receive_queue_lock, flags); + + return ret; +} + +/* + * Return a receive buffer + * Upon returning of a receive buffer, we can post new receive and extend + * more receive credits to remote peer. This is done immediately after a + * receive buffer is returned. + */ +static void put_receive_buffer( + struct smbd_connection *info, struct smbd_response *response) +{ + unsigned long flags; + + ib_dma_unmap_single(info->id->device, response->sge.addr, + response->sge.length, DMA_FROM_DEVICE); + + spin_lock_irqsave(&info->receive_queue_lock, flags); + list_add_tail(&response->list, &info->receive_queue); + info->count_receive_queue++; + info->count_put_receive_buffer++; + spin_unlock_irqrestore(&info->receive_queue_lock, flags); + + queue_work(info->workqueue, &info->post_send_credits_work); +} + +/* Preallocate all receive buffer on transport establishment */ +static int allocate_receive_buffers(struct smbd_connection *info, int num_buf) +{ + int i; + struct smbd_response *response; + + INIT_LIST_HEAD(&info->reassembly_queue); + spin_lock_init(&info->reassembly_queue_lock); + info->reassembly_data_length = 0; + info->reassembly_queue_length = 0; + + INIT_LIST_HEAD(&info->receive_queue); + spin_lock_init(&info->receive_queue_lock); + info->count_receive_queue = 0; + + INIT_LIST_HEAD(&info->empty_packet_queue); + spin_lock_init(&info->empty_packet_queue_lock); + info->count_empty_packet_queue = 0; + + init_waitqueue_head(&info->wait_receive_queues); + + for (i = 0; i < num_buf; i++) { + response = mempool_alloc(info->response_mempool, GFP_KERNEL); + if (!response) + goto allocate_failed; + + response->info = info; + list_add_tail(&response->list, &info->receive_queue); + info->count_receive_queue++; + } + + return 0; + +allocate_failed: + while (!list_empty(&info->receive_queue)) { + response = list_first_entry( + &info->receive_queue, + struct smbd_response, list); + list_del(&response->list); + info->count_receive_queue--; + + mempool_free(response, info->response_mempool); + } + return -ENOMEM; +} + +static void destroy_receive_buffers(struct smbd_connection *info) +{ + struct smbd_response *response; + + while ((response = get_receive_buffer(info))) + mempool_free(response, info->response_mempool); + + while ((response = get_empty_queue_buffer(info))) + mempool_free(response, info->response_mempool); +} + +/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */ +static void idle_connection_timer(struct work_struct *work) +{ + struct smbd_connection *info = container_of( + work, struct smbd_connection, + idle_timer_work.work); + + if (info->keep_alive_requested != KEEP_ALIVE_NONE) { + log_keep_alive(ERR, + "error status info->keep_alive_requested=%d\n", + info->keep_alive_requested); + smbd_disconnect_rdma_connection(info); + return; + } + + log_keep_alive(INFO, "about to send an empty idle message\n"); + smbd_post_send_empty(info); + + /* Setup the next idle timeout work */ + queue_delayed_work(info->workqueue, &info->idle_timer_work, + info->keep_alive_interval*HZ); +} + +/* + * Destroy the transport and related RDMA and memory resources + * Need to go through all the pending counters and make sure on one is using + * the transport while it is destroyed + */ +void smbd_destroy(struct TCP_Server_Info *server) +{ + struct smbd_connection *info = server->smbd_conn; + struct smbd_response *response; + unsigned long flags; + + if (!info) { + log_rdma_event(INFO, "rdma session already destroyed\n"); + return; + } + + log_rdma_event(INFO, "destroying rdma session\n"); + if (info->transport_status != SMBD_DISCONNECTED) { + rdma_disconnect(server->smbd_conn->id); + log_rdma_event(INFO, "wait for transport being disconnected\n"); + wait_event_interruptible( + info->disconn_wait, + info->transport_status == SMBD_DISCONNECTED); + } + + log_rdma_event(INFO, "destroying qp\n"); + ib_drain_qp(info->id->qp); + rdma_destroy_qp(info->id); + + log_rdma_event(INFO, "cancelling idle timer\n"); + cancel_delayed_work_sync(&info->idle_timer_work); + + log_rdma_event(INFO, "wait for all send posted to IB to finish\n"); + wait_event(info->wait_send_pending, + atomic_read(&info->send_pending) == 0); + + /* It's not possible for upper layer to get to reassembly */ + log_rdma_event(INFO, "drain the reassembly queue\n"); + do { + spin_lock_irqsave(&info->reassembly_queue_lock, flags); + response = _get_first_reassembly(info); + if (response) { + list_del(&response->list); + spin_unlock_irqrestore( + &info->reassembly_queue_lock, flags); + put_receive_buffer(info, response); + } else + spin_unlock_irqrestore( + &info->reassembly_queue_lock, flags); + } while (response); + info->reassembly_data_length = 0; + + log_rdma_event(INFO, "free receive buffers\n"); + wait_event(info->wait_receive_queues, + info->count_receive_queue + info->count_empty_packet_queue + == info->receive_credit_max); + destroy_receive_buffers(info); + + /* + * For performance reasons, memory registration and deregistration + * are not locked by srv_mutex. It is possible some processes are + * blocked on transport srv_mutex while holding memory registration. + * Release the transport srv_mutex to allow them to hit the failure + * path when sending data, and then release memory registartions. + */ + log_rdma_event(INFO, "freeing mr list\n"); + wake_up_interruptible_all(&info->wait_mr); + while (atomic_read(&info->mr_used_count)) { + cifs_server_unlock(server); + msleep(1000); + cifs_server_lock(server); + } + destroy_mr_list(info); + + ib_free_cq(info->send_cq); + ib_free_cq(info->recv_cq); + ib_dealloc_pd(info->pd); + rdma_destroy_id(info->id); + + /* free mempools */ + mempool_destroy(info->request_mempool); + kmem_cache_destroy(info->request_cache); + + mempool_destroy(info->response_mempool); + kmem_cache_destroy(info->response_cache); + + info->transport_status = SMBD_DESTROYED; + + destroy_workqueue(info->workqueue); + log_rdma_event(INFO, "rdma session destroyed\n"); + kfree(info); + server->smbd_conn = NULL; +} + +/* + * Reconnect this SMBD connection, called from upper layer + * return value: 0 on success, or actual error code + */ +int smbd_reconnect(struct TCP_Server_Info *server) +{ + log_rdma_event(INFO, "reconnecting rdma session\n"); + + if (!server->smbd_conn) { + log_rdma_event(INFO, "rdma session already destroyed\n"); + goto create_conn; + } + + /* + * This is possible if transport is disconnected and we haven't received + * notification from RDMA, but upper layer has detected timeout + */ + if (server->smbd_conn->transport_status == SMBD_CONNECTED) { + log_rdma_event(INFO, "disconnecting transport\n"); + smbd_destroy(server); + } + +create_conn: + log_rdma_event(INFO, "creating rdma session\n"); + server->smbd_conn = smbd_get_connection( + server, (struct sockaddr *) &server->dstaddr); + + if (server->smbd_conn) + cifs_dbg(VFS, "RDMA transport re-established\n"); + + return server->smbd_conn ? 0 : -ENOENT; +} + +static void destroy_caches_and_workqueue(struct smbd_connection *info) +{ + destroy_receive_buffers(info); + destroy_workqueue(info->workqueue); + mempool_destroy(info->response_mempool); + kmem_cache_destroy(info->response_cache); + mempool_destroy(info->request_mempool); + kmem_cache_destroy(info->request_cache); +} + +#define MAX_NAME_LEN 80 +static int allocate_caches_and_workqueue(struct smbd_connection *info) +{ + char name[MAX_NAME_LEN]; + int rc; + + scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info); + info->request_cache = + kmem_cache_create( + name, + sizeof(struct smbd_request) + + sizeof(struct smbd_data_transfer), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!info->request_cache) + return -ENOMEM; + + info->request_mempool = + mempool_create(info->send_credit_target, mempool_alloc_slab, + mempool_free_slab, info->request_cache); + if (!info->request_mempool) + goto out1; + + scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info); + info->response_cache = + kmem_cache_create( + name, + sizeof(struct smbd_response) + + info->max_receive_size, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!info->response_cache) + goto out2; + + info->response_mempool = + mempool_create(info->receive_credit_max, mempool_alloc_slab, + mempool_free_slab, info->response_cache); + if (!info->response_mempool) + goto out3; + + scnprintf(name, MAX_NAME_LEN, "smbd_%p", info); + info->workqueue = create_workqueue(name); + if (!info->workqueue) + goto out4; + + rc = allocate_receive_buffers(info, info->receive_credit_max); + if (rc) { + log_rdma_event(ERR, "failed to allocate receive buffers\n"); + goto out5; + } + + return 0; + +out5: + destroy_workqueue(info->workqueue); +out4: + mempool_destroy(info->response_mempool); +out3: + kmem_cache_destroy(info->response_cache); +out2: + mempool_destroy(info->request_mempool); +out1: + kmem_cache_destroy(info->request_cache); + return -ENOMEM; +} + +/* Create a SMBD connection, called by upper layer */ +static struct smbd_connection *_smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port) +{ + int rc; + struct smbd_connection *info; + struct rdma_conn_param conn_param; + struct ib_qp_init_attr qp_attr; + struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr; + struct ib_port_immutable port_immutable; + u32 ird_ord_hdr[2]; + + info = kzalloc(sizeof(struct smbd_connection), GFP_KERNEL); + if (!info) + return NULL; + + info->transport_status = SMBD_CONNECTING; + rc = smbd_ia_open(info, dstaddr, port); + if (rc) { + log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc); + goto create_id_failed; + } + + if (smbd_send_credit_target > info->id->device->attrs.max_cqe || + smbd_send_credit_target > info->id->device->attrs.max_qp_wr) { + log_rdma_event(ERR, "consider lowering send_credit_target = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + smbd_send_credit_target, + info->id->device->attrs.max_cqe, + info->id->device->attrs.max_qp_wr); + goto config_failed; + } + + if (smbd_receive_credit_max > info->id->device->attrs.max_cqe || + smbd_receive_credit_max > info->id->device->attrs.max_qp_wr) { + log_rdma_event(ERR, "consider lowering receive_credit_max = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + smbd_receive_credit_max, + info->id->device->attrs.max_cqe, + info->id->device->attrs.max_qp_wr); + goto config_failed; + } + + info->receive_credit_max = smbd_receive_credit_max; + info->send_credit_target = smbd_send_credit_target; + info->max_send_size = smbd_max_send_size; + info->max_fragmented_recv_size = smbd_max_fragmented_recv_size; + info->max_receive_size = smbd_max_receive_size; + info->keep_alive_interval = smbd_keep_alive_interval; + + if (info->id->device->attrs.max_send_sge < SMBDIRECT_MAX_SEND_SGE || + info->id->device->attrs.max_recv_sge < SMBDIRECT_MAX_RECV_SGE) { + log_rdma_event(ERR, + "device %.*s max_send_sge/max_recv_sge = %d/%d too small\n", + IB_DEVICE_NAME_MAX, + info->id->device->name, + info->id->device->attrs.max_send_sge, + info->id->device->attrs.max_recv_sge); + goto config_failed; + } + + info->send_cq = NULL; + info->recv_cq = NULL; + info->send_cq = + ib_alloc_cq_any(info->id->device, info, + info->send_credit_target, IB_POLL_SOFTIRQ); + if (IS_ERR(info->send_cq)) { + info->send_cq = NULL; + goto alloc_cq_failed; + } + + info->recv_cq = + ib_alloc_cq_any(info->id->device, info, + info->receive_credit_max, IB_POLL_SOFTIRQ); + if (IS_ERR(info->recv_cq)) { + info->recv_cq = NULL; + goto alloc_cq_failed; + } + + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.event_handler = smbd_qp_async_error_upcall; + qp_attr.qp_context = info; + qp_attr.cap.max_send_wr = info->send_credit_target; + qp_attr.cap.max_recv_wr = info->receive_credit_max; + qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SEND_SGE; + qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_RECV_SGE; + qp_attr.cap.max_inline_data = 0; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + qp_attr.send_cq = info->send_cq; + qp_attr.recv_cq = info->recv_cq; + qp_attr.port_num = ~0; + + rc = rdma_create_qp(info->id, info->pd, &qp_attr); + if (rc) { + log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc); + goto create_qp_failed; + } + + memset(&conn_param, 0, sizeof(conn_param)); + conn_param.initiator_depth = 0; + + conn_param.responder_resources = + info->id->device->attrs.max_qp_rd_atom + < SMBD_CM_RESPONDER_RESOURCES ? + info->id->device->attrs.max_qp_rd_atom : + SMBD_CM_RESPONDER_RESOURCES; + info->responder_resources = conn_param.responder_resources; + log_rdma_mr(INFO, "responder_resources=%d\n", + info->responder_resources); + + /* Need to send IRD/ORD in private data for iWARP */ + info->id->device->ops.get_port_immutable( + info->id->device, info->id->port_num, &port_immutable); + if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { + ird_ord_hdr[0] = info->responder_resources; + ird_ord_hdr[1] = 1; + conn_param.private_data = ird_ord_hdr; + conn_param.private_data_len = sizeof(ird_ord_hdr); + } else { + conn_param.private_data = NULL; + conn_param.private_data_len = 0; + } + + conn_param.retry_count = SMBD_CM_RETRY; + conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY; + conn_param.flow_control = 0; + + log_rdma_event(INFO, "connecting to IP %pI4 port %d\n", + &addr_in->sin_addr, port); + + init_waitqueue_head(&info->conn_wait); + init_waitqueue_head(&info->disconn_wait); + init_waitqueue_head(&info->wait_reassembly_queue); + rc = rdma_connect(info->id, &conn_param); + if (rc) { + log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc); + goto rdma_connect_failed; + } + + wait_event_interruptible( + info->conn_wait, info->transport_status != SMBD_CONNECTING); + + if (info->transport_status != SMBD_CONNECTED) { + log_rdma_event(ERR, "rdma_connect failed port=%d\n", port); + goto rdma_connect_failed; + } + + log_rdma_event(INFO, "rdma_connect connected\n"); + + rc = allocate_caches_and_workqueue(info); + if (rc) { + log_rdma_event(ERR, "cache allocation failed\n"); + goto allocate_cache_failed; + } + + init_waitqueue_head(&info->wait_send_queue); + INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer); + queue_delayed_work(info->workqueue, &info->idle_timer_work, + info->keep_alive_interval*HZ); + + init_waitqueue_head(&info->wait_send_pending); + atomic_set(&info->send_pending, 0); + + init_waitqueue_head(&info->wait_post_send); + + INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work); + INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits); + info->new_credits_offered = 0; + spin_lock_init(&info->lock_new_credits_offered); + + rc = smbd_negotiate(info); + if (rc) { + log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc); + goto negotiation_failed; + } + + rc = allocate_mr_list(info); + if (rc) { + log_rdma_mr(ERR, "memory registration allocation failed\n"); + goto allocate_mr_failed; + } + + return info; + +allocate_mr_failed: + /* At this point, need to a full transport shutdown */ + server->smbd_conn = info; + smbd_destroy(server); + return NULL; + +negotiation_failed: + cancel_delayed_work_sync(&info->idle_timer_work); + destroy_caches_and_workqueue(info); + info->transport_status = SMBD_NEGOTIATE_FAILED; + init_waitqueue_head(&info->conn_wait); + rdma_disconnect(info->id); + wait_event(info->conn_wait, + info->transport_status == SMBD_DISCONNECTED); + +allocate_cache_failed: +rdma_connect_failed: + rdma_destroy_qp(info->id); + +create_qp_failed: +alloc_cq_failed: + if (info->send_cq) + ib_free_cq(info->send_cq); + if (info->recv_cq) + ib_free_cq(info->recv_cq); + +config_failed: + ib_dealloc_pd(info->pd); + rdma_destroy_id(info->id); + +create_id_failed: + kfree(info); + return NULL; +} + +struct smbd_connection *smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr) +{ + struct smbd_connection *ret; + int port = SMBD_PORT; + +try_again: + ret = _smbd_get_connection(server, dstaddr, port); + + /* Try SMB_PORT if SMBD_PORT doesn't work */ + if (!ret && port == SMBD_PORT) { + port = SMB_PORT; + goto try_again; + } + return ret; +} + +/* + * Receive data from receive reassembly queue + * All the incoming data packets are placed in reassembly queue + * buf: the buffer to read data into + * size: the length of data to read + * return value: actual data read + * Note: this implementation copies the data from reassebmly queue to receive + * buffers used by upper layer. This is not the optimal code path. A better way + * to do it is to not have upper layer allocate its receive buffers but rather + * borrow the buffer from reassembly queue, and return it after data is + * consumed. But this will require more changes to upper layer code, and also + * need to consider packet boundaries while they still being reassembled. + */ +static int smbd_recv_buf(struct smbd_connection *info, char *buf, + unsigned int size) +{ + struct smbd_response *response; + struct smbd_data_transfer *data_transfer; + int to_copy, to_read, data_read, offset; + u32 data_length, remaining_data_length, data_offset; + int rc; + +again: + /* + * No need to hold the reassembly queue lock all the time as we are + * the only one reading from the front of the queue. The transport + * may add more entries to the back of the queue at the same time + */ + log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size, + info->reassembly_data_length); + if (info->reassembly_data_length >= size) { + int queue_length; + int queue_removed = 0; + + /* + * Need to make sure reassembly_data_length is read before + * reading reassembly_queue_length and calling + * _get_first_reassembly. This call is lock free + * as we never read at the end of the queue which are being + * updated in SOFTIRQ as more data is received + */ + virt_rmb(); + queue_length = info->reassembly_queue_length; + data_read = 0; + to_read = size; + offset = info->first_entry_offset; + while (data_read < size) { + response = _get_first_reassembly(info); + data_transfer = smbd_response_payload(response); + data_length = le32_to_cpu(data_transfer->data_length); + remaining_data_length = + le32_to_cpu( + data_transfer->remaining_data_length); + data_offset = le32_to_cpu(data_transfer->data_offset); + + /* + * The upper layer expects RFC1002 length at the + * beginning of the payload. Return it to indicate + * the total length of the packet. This minimize the + * change to upper layer packet processing logic. This + * will be eventually remove when an intermediate + * transport layer is added + */ + if (response->first_segment && size == 4) { + unsigned int rfc1002_len = + data_length + remaining_data_length; + *((__be32 *)buf) = cpu_to_be32(rfc1002_len); + data_read = 4; + response->first_segment = false; + log_read(INFO, "returning rfc1002 length %d\n", + rfc1002_len); + goto read_rfc1002_done; + } + + to_copy = min_t(int, data_length - offset, to_read); + memcpy( + buf + data_read, + (char *)data_transfer + data_offset + offset, + to_copy); + + /* move on to the next buffer? */ + if (to_copy == data_length - offset) { + queue_length--; + /* + * No need to lock if we are not at the + * end of the queue + */ + if (queue_length) + list_del(&response->list); + else { + spin_lock_irq( + &info->reassembly_queue_lock); + list_del(&response->list); + spin_unlock_irq( + &info->reassembly_queue_lock); + } + queue_removed++; + info->count_reassembly_queue--; + info->count_dequeue_reassembly_queue++; + put_receive_buffer(info, response); + offset = 0; + log_read(INFO, "put_receive_buffer offset=0\n"); + } else + offset += to_copy; + + to_read -= to_copy; + data_read += to_copy; + + log_read(INFO, "_get_first_reassembly memcpy %d bytes data_transfer_length-offset=%d after that to_read=%d data_read=%d offset=%d\n", + to_copy, data_length - offset, + to_read, data_read, offset); + } + + spin_lock_irq(&info->reassembly_queue_lock); + info->reassembly_data_length -= data_read; + info->reassembly_queue_length -= queue_removed; + spin_unlock_irq(&info->reassembly_queue_lock); + + info->first_entry_offset = offset; + log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, info->reassembly_data_length, + info->first_entry_offset); +read_rfc1002_done: + return data_read; + } + + log_read(INFO, "wait_event on more data\n"); + rc = wait_event_interruptible( + info->wait_reassembly_queue, + info->reassembly_data_length >= size || + info->transport_status != SMBD_CONNECTED); + /* Don't return any data if interrupted */ + if (rc) + return rc; + + if (info->transport_status != SMBD_CONNECTED) { + log_read(ERR, "disconnected\n"); + return -ECONNABORTED; + } + + goto again; +} + +/* + * Receive a page from receive reassembly queue + * page: the page to read data into + * to_read: the length of data to read + * return value: actual data read + */ +static int smbd_recv_page(struct smbd_connection *info, + struct page *page, unsigned int page_offset, + unsigned int to_read) +{ + int ret; + char *to_address; + void *page_address; + + /* make sure we have the page ready for read */ + ret = wait_event_interruptible( + info->wait_reassembly_queue, + info->reassembly_data_length >= to_read || + info->transport_status != SMBD_CONNECTED); + if (ret) + return ret; + + /* now we can read from reassembly queue and not sleep */ + page_address = kmap_atomic(page); + to_address = (char *) page_address + page_offset; + + log_read(INFO, "reading from page=%p address=%p to_read=%d\n", + page, to_address, to_read); + + ret = smbd_recv_buf(info, to_address, to_read); + kunmap_atomic(page_address); + + return ret; +} + +/* + * Receive data from transport + * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC + * return: total bytes read, or 0. SMB Direct will not do partial read. + */ +int smbd_recv(struct smbd_connection *info, struct msghdr *msg) +{ + char *buf; + struct page *page; + unsigned int to_read, page_offset; + int rc; + + if (iov_iter_rw(&msg->msg_iter) == WRITE) { + /* It's a bug in upper layer to get there */ + cifs_dbg(VFS, "Invalid msg iter dir %u\n", + iov_iter_rw(&msg->msg_iter)); + rc = -EINVAL; + goto out; + } + + switch (iov_iter_type(&msg->msg_iter)) { + case ITER_KVEC: + buf = msg->msg_iter.kvec->iov_base; + to_read = msg->msg_iter.kvec->iov_len; + rc = smbd_recv_buf(info, buf, to_read); + break; + + case ITER_BVEC: + page = msg->msg_iter.bvec->bv_page; + page_offset = msg->msg_iter.bvec->bv_offset; + to_read = msg->msg_iter.bvec->bv_len; + rc = smbd_recv_page(info, page, page_offset, to_read); + break; + + default: + /* It's a bug in upper layer to get there */ + cifs_dbg(VFS, "Invalid msg type %d\n", + iov_iter_type(&msg->msg_iter)); + rc = -EINVAL; + } + +out: + /* SMBDirect will read it all or nothing */ + if (rc > 0) + msg->msg_iter.count = 0; + return rc; +} + +/* + * Send data to transport + * Each rqst is transported as a SMBDirect payload + * rqst: the data to write + * return value: 0 if successfully write, otherwise error code + */ +int smbd_send(struct TCP_Server_Info *server, + int num_rqst, struct smb_rqst *rqst_array) +{ + struct smbd_connection *info = server->smbd_conn; + struct kvec vecs[SMBDIRECT_MAX_SEND_SGE - 1]; + int nvecs; + int size; + unsigned int buflen, remaining_data_length; + unsigned int offset, remaining_vec_data_length; + int start, i, j; + int max_iov_size = + info->max_send_size - sizeof(struct smbd_data_transfer); + struct kvec *iov; + int rc; + struct smb_rqst *rqst; + int rqst_idx; + + if (info->transport_status != SMBD_CONNECTED) + return -EAGAIN; + + /* + * Add in the page array if there is one. The caller needs to set + * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and + * ends at page boundary + */ + remaining_data_length = 0; + for (i = 0; i < num_rqst; i++) + remaining_data_length += smb_rqst_len(server, &rqst_array[i]); + + if (unlikely(remaining_data_length > info->max_fragmented_send_size)) { + /* assertion: payload never exceeds negotiated maximum */ + log_write(ERR, "payload size %d > max size %d\n", + remaining_data_length, info->max_fragmented_send_size); + return -EINVAL; + } + + log_write(INFO, "num_rqst=%d total length=%u\n", + num_rqst, remaining_data_length); + + rqst_idx = 0; + do { + rqst = &rqst_array[rqst_idx]; + iov = rqst->rq_iov; + + cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n", + rqst_idx, smb_rqst_len(server, rqst)); + remaining_vec_data_length = 0; + for (i = 0; i < rqst->rq_nvec; i++) { + remaining_vec_data_length += iov[i].iov_len; + dump_smb(iov[i].iov_base, iov[i].iov_len); + } + + log_write(INFO, "rqst_idx=%d nvec=%d rqst->rq_npages=%d rq_pagesz=%d rq_tailsz=%d buflen=%lu\n", + rqst_idx, rqst->rq_nvec, + rqst->rq_npages, rqst->rq_pagesz, + rqst->rq_tailsz, smb_rqst_len(server, rqst)); + + start = 0; + offset = 0; + do { + buflen = 0; + i = start; + j = 0; + while (i < rqst->rq_nvec && + j < SMBDIRECT_MAX_SEND_SGE - 1 && + buflen < max_iov_size) { + + vecs[j].iov_base = iov[i].iov_base + offset; + if (buflen + iov[i].iov_len > max_iov_size) { + vecs[j].iov_len = + max_iov_size - iov[i].iov_len; + buflen = max_iov_size; + offset = vecs[j].iov_len; + } else { + vecs[j].iov_len = + iov[i].iov_len - offset; + buflen += vecs[j].iov_len; + offset = 0; + ++i; + } + ++j; + } + + remaining_vec_data_length -= buflen; + remaining_data_length -= buflen; + log_write(INFO, "sending %s iov[%d] from start=%d nvecs=%d remaining_data_length=%d\n", + remaining_vec_data_length > 0 ? + "partial" : "complete", + rqst->rq_nvec, start, j, + remaining_data_length); + + start = i; + rc = smbd_post_send_data(info, vecs, j, remaining_data_length); + if (rc) + goto done; + } while (remaining_vec_data_length > 0); + + /* now sending pages if there are any */ + for (i = 0; i < rqst->rq_npages; i++) { + rqst_page_get_length(rqst, i, &buflen, &offset); + nvecs = (buflen + max_iov_size - 1) / max_iov_size; + log_write(INFO, "sending pages buflen=%d nvecs=%d\n", + buflen, nvecs); + for (j = 0; j < nvecs; j++) { + size = min_t(unsigned int, max_iov_size, remaining_data_length); + remaining_data_length -= size; + log_write(INFO, "sending pages i=%d offset=%d size=%d remaining_data_length=%d\n", + i, j * max_iov_size + offset, size, + remaining_data_length); + rc = smbd_post_send_page( + info, rqst->rq_pages[i], + j*max_iov_size + offset, + size, remaining_data_length); + if (rc) + goto done; + } + } + } while (++rqst_idx < num_rqst); + +done: + /* + * As an optimization, we don't wait for individual I/O to finish + * before sending the next one. + * Send them all and wait for pending send count to get to 0 + * that means all the I/Os have been out and we are good to return + */ + + wait_event(info->wait_send_pending, + atomic_read(&info->send_pending) == 0); + + return rc; +} + +static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smbd_mr *mr; + struct ib_cqe *cqe; + + if (wc->status) { + log_rdma_mr(ERR, "status=%d\n", wc->status); + cqe = wc->wr_cqe; + mr = container_of(cqe, struct smbd_mr, cqe); + smbd_disconnect_rdma_connection(mr->conn); + } +} + +/* + * The work queue function that recovers MRs + * We need to call ib_dereg_mr() and ib_alloc_mr() before this MR can be used + * again. Both calls are slow, so finish them in a workqueue. This will not + * block I/O path. + * There is one workqueue that recovers MRs, there is no need to lock as the + * I/O requests calling smbd_register_mr will never update the links in the + * mr_list. + */ +static void smbd_mr_recovery_work(struct work_struct *work) +{ + struct smbd_connection *info = + container_of(work, struct smbd_connection, mr_recovery_work); + struct smbd_mr *smbdirect_mr; + int rc; + + list_for_each_entry(smbdirect_mr, &info->mr_list, list) { + if (smbdirect_mr->state == MR_ERROR) { + + /* recover this MR entry */ + rc = ib_dereg_mr(smbdirect_mr->mr); + if (rc) { + log_rdma_mr(ERR, + "ib_dereg_mr failed rc=%x\n", + rc); + smbd_disconnect_rdma_connection(info); + continue; + } + + smbdirect_mr->mr = ib_alloc_mr( + info->pd, info->mr_type, + info->max_frmr_depth); + if (IS_ERR(smbdirect_mr->mr)) { + log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", + info->mr_type, + info->max_frmr_depth); + smbd_disconnect_rdma_connection(info); + continue; + } + } else + /* This MR is being used, don't recover it */ + continue; + + smbdirect_mr->state = MR_READY; + + /* smbdirect_mr->state is updated by this function + * and is read and updated by I/O issuing CPUs trying + * to get a MR, the call to atomic_inc_return + * implicates a memory barrier and guarantees this + * value is updated before waking up any calls to + * get_mr() from the I/O issuing CPUs + */ + if (atomic_inc_return(&info->mr_ready_count) == 1) + wake_up_interruptible(&info->wait_mr); + } +} + +static void destroy_mr_list(struct smbd_connection *info) +{ + struct smbd_mr *mr, *tmp; + + cancel_work_sync(&info->mr_recovery_work); + list_for_each_entry_safe(mr, tmp, &info->mr_list, list) { + if (mr->state == MR_INVALIDATED) + ib_dma_unmap_sg(info->id->device, mr->sgl, + mr->sgl_count, mr->dir); + ib_dereg_mr(mr->mr); + kfree(mr->sgl); + kfree(mr); + } +} + +/* + * Allocate MRs used for RDMA read/write + * The number of MRs will not exceed hardware capability in responder_resources + * All MRs are kept in mr_list. The MR can be recovered after it's used + * Recovery is done in smbd_mr_recovery_work. The content of list entry changes + * as MRs are used and recovered for I/O, but the list links will not change + */ +static int allocate_mr_list(struct smbd_connection *info) +{ + int i; + struct smbd_mr *smbdirect_mr, *tmp; + + INIT_LIST_HEAD(&info->mr_list); + init_waitqueue_head(&info->wait_mr); + spin_lock_init(&info->mr_list_lock); + atomic_set(&info->mr_ready_count, 0); + atomic_set(&info->mr_used_count, 0); + init_waitqueue_head(&info->wait_for_mr_cleanup); + INIT_WORK(&info->mr_recovery_work, smbd_mr_recovery_work); + /* Allocate more MRs (2x) than hardware responder_resources */ + for (i = 0; i < info->responder_resources * 2; i++) { + smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL); + if (!smbdirect_mr) + goto out; + smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type, + info->max_frmr_depth); + if (IS_ERR(smbdirect_mr->mr)) { + log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", + info->mr_type, info->max_frmr_depth); + goto out; + } + smbdirect_mr->sgl = kcalloc( + info->max_frmr_depth, + sizeof(struct scatterlist), + GFP_KERNEL); + if (!smbdirect_mr->sgl) { + log_rdma_mr(ERR, "failed to allocate sgl\n"); + ib_dereg_mr(smbdirect_mr->mr); + goto out; + } + smbdirect_mr->state = MR_READY; + smbdirect_mr->conn = info; + + list_add_tail(&smbdirect_mr->list, &info->mr_list); + atomic_inc(&info->mr_ready_count); + } + return 0; + +out: + kfree(smbdirect_mr); + + list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) { + list_del(&smbdirect_mr->list); + ib_dereg_mr(smbdirect_mr->mr); + kfree(smbdirect_mr->sgl); + kfree(smbdirect_mr); + } + return -ENOMEM; +} + +/* + * Get a MR from mr_list. This function waits until there is at least one + * MR available in the list. It may access the list while the + * smbd_mr_recovery_work is recovering the MR list. This doesn't need a lock + * as they never modify the same places. However, there may be several CPUs + * issueing I/O trying to get MR at the same time, mr_list_lock is used to + * protect this situation. + */ +static struct smbd_mr *get_mr(struct smbd_connection *info) +{ + struct smbd_mr *ret; + int rc; +again: + rc = wait_event_interruptible(info->wait_mr, + atomic_read(&info->mr_ready_count) || + info->transport_status != SMBD_CONNECTED); + if (rc) { + log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc); + return NULL; + } + + if (info->transport_status != SMBD_CONNECTED) { + log_rdma_mr(ERR, "info->transport_status=%x\n", + info->transport_status); + return NULL; + } + + spin_lock(&info->mr_list_lock); + list_for_each_entry(ret, &info->mr_list, list) { + if (ret->state == MR_READY) { + ret->state = MR_REGISTERED; + spin_unlock(&info->mr_list_lock); + atomic_dec(&info->mr_ready_count); + atomic_inc(&info->mr_used_count); + return ret; + } + } + + spin_unlock(&info->mr_list_lock); + /* + * It is possible that we could fail to get MR because other processes may + * try to acquire a MR at the same time. If this is the case, retry it. + */ + goto again; +} + +/* + * Register memory for RDMA read/write + * pages[]: the list of pages to register memory with + * num_pages: the number of pages to register + * tailsz: if non-zero, the bytes to register in the last page + * writing: true if this is a RDMA write (SMB read), false for RDMA read + * need_invalidate: true if this MR needs to be locally invalidated after I/O + * return value: the MR registered, NULL if failed. + */ +struct smbd_mr *smbd_register_mr( + struct smbd_connection *info, struct page *pages[], int num_pages, + int offset, int tailsz, bool writing, bool need_invalidate) +{ + struct smbd_mr *smbdirect_mr; + int rc, i; + enum dma_data_direction dir; + struct ib_reg_wr *reg_wr; + + if (num_pages > info->max_frmr_depth) { + log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n", + num_pages, info->max_frmr_depth); + return NULL; + } + + smbdirect_mr = get_mr(info); + if (!smbdirect_mr) { + log_rdma_mr(ERR, "get_mr returning NULL\n"); + return NULL; + } + smbdirect_mr->need_invalidate = need_invalidate; + smbdirect_mr->sgl_count = num_pages; + sg_init_table(smbdirect_mr->sgl, num_pages); + + log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n", + num_pages, offset, tailsz); + + if (num_pages == 1) { + sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset); + goto skip_multiple_pages; + } + + /* We have at least two pages to register */ + sg_set_page( + &smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset); + i = 1; + while (i < num_pages - 1) { + sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0); + i++; + } + sg_set_page(&smbdirect_mr->sgl[i], pages[i], + tailsz ? tailsz : PAGE_SIZE, 0); + +skip_multiple_pages: + dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + smbdirect_mr->dir = dir; + rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir); + if (!rc) { + log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n", + num_pages, dir, rc); + goto dma_map_error; + } + + rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages, + NULL, PAGE_SIZE); + if (rc != num_pages) { + log_rdma_mr(ERR, + "ib_map_mr_sg failed rc = %d num_pages = %x\n", + rc, num_pages); + goto map_mr_error; + } + + ib_update_fast_reg_key(smbdirect_mr->mr, + ib_inc_rkey(smbdirect_mr->mr->rkey)); + reg_wr = &smbdirect_mr->wr; + reg_wr->wr.opcode = IB_WR_REG_MR; + smbdirect_mr->cqe.done = register_mr_done; + reg_wr->wr.wr_cqe = &smbdirect_mr->cqe; + reg_wr->wr.num_sge = 0; + reg_wr->wr.send_flags = IB_SEND_SIGNALED; + reg_wr->mr = smbdirect_mr->mr; + reg_wr->key = smbdirect_mr->mr->rkey; + reg_wr->access = writing ? + IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE : + IB_ACCESS_REMOTE_READ; + + /* + * There is no need for waiting for complemtion on ib_post_send + * on IB_WR_REG_MR. Hardware enforces a barrier and order of execution + * on the next ib_post_send when we actaully send I/O to remote peer + */ + rc = ib_post_send(info->id->qp, ®_wr->wr, NULL); + if (!rc) + return smbdirect_mr; + + log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n", + rc, reg_wr->key); + + /* If all failed, attempt to recover this MR by setting it MR_ERROR*/ +map_mr_error: + ib_dma_unmap_sg(info->id->device, smbdirect_mr->sgl, + smbdirect_mr->sgl_count, smbdirect_mr->dir); + +dma_map_error: + smbdirect_mr->state = MR_ERROR; + if (atomic_dec_and_test(&info->mr_used_count)) + wake_up(&info->wait_for_mr_cleanup); + + smbd_disconnect_rdma_connection(info); + + return NULL; +} + +static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smbd_mr *smbdirect_mr; + struct ib_cqe *cqe; + + cqe = wc->wr_cqe; + smbdirect_mr = container_of(cqe, struct smbd_mr, cqe); + smbdirect_mr->state = MR_INVALIDATED; + if (wc->status != IB_WC_SUCCESS) { + log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status); + smbdirect_mr->state = MR_ERROR; + } + complete(&smbdirect_mr->invalidate_done); +} + +/* + * Deregister a MR after I/O is done + * This function may wait if remote invalidation is not used + * and we have to locally invalidate the buffer to prevent data is being + * modified by remote peer after upper layer consumes it + */ +int smbd_deregister_mr(struct smbd_mr *smbdirect_mr) +{ + struct ib_send_wr *wr; + struct smbd_connection *info = smbdirect_mr->conn; + int rc = 0; + + if (smbdirect_mr->need_invalidate) { + /* Need to finish local invalidation before returning */ + wr = &smbdirect_mr->inv_wr; + wr->opcode = IB_WR_LOCAL_INV; + smbdirect_mr->cqe.done = local_inv_done; + wr->wr_cqe = &smbdirect_mr->cqe; + wr->num_sge = 0; + wr->ex.invalidate_rkey = smbdirect_mr->mr->rkey; + wr->send_flags = IB_SEND_SIGNALED; + + init_completion(&smbdirect_mr->invalidate_done); + rc = ib_post_send(info->id->qp, wr, NULL); + if (rc) { + log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc); + smbd_disconnect_rdma_connection(info); + goto done; + } + wait_for_completion(&smbdirect_mr->invalidate_done); + smbdirect_mr->need_invalidate = false; + } else + /* + * For remote invalidation, just set it to MR_INVALIDATED + * and defer to mr_recovery_work to recover the MR for next use + */ + smbdirect_mr->state = MR_INVALIDATED; + + if (smbdirect_mr->state == MR_INVALIDATED) { + ib_dma_unmap_sg( + info->id->device, smbdirect_mr->sgl, + smbdirect_mr->sgl_count, + smbdirect_mr->dir); + smbdirect_mr->state = MR_READY; + if (atomic_inc_return(&info->mr_ready_count) == 1) + wake_up_interruptible(&info->wait_mr); + } else + /* + * Schedule the work to do MR recovery for future I/Os MR + * recovery is slow and don't want it to block current I/O + */ + queue_work(info->workqueue, &info->mr_recovery_work); + +done: + if (atomic_dec_and_test(&info->mr_used_count)) + wake_up(&info->wait_for_mr_cleanup); + + return rc; +} diff --git a/fs/smb/client/smbdirect.h b/fs/smb/client/smbdirect.h new file mode 100644 index 000000000000..207ef979cd51 --- /dev/null +++ b/fs/smb/client/smbdirect.h @@ -0,0 +1,320 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017, Microsoft Corporation. + * + * Author(s): Long Li + */ +#ifndef _SMBDIRECT_H +#define _SMBDIRECT_H + +#ifdef CONFIG_CIFS_SMB_DIRECT +#define cifs_rdma_enabled(server) ((server)->rdma) + +#include "cifsglob.h" +#include +#include +#include + +extern int rdma_readwrite_threshold; +extern int smbd_max_frmr_depth; +extern int smbd_keep_alive_interval; +extern int smbd_max_receive_size; +extern int smbd_max_fragmented_recv_size; +extern int smbd_max_send_size; +extern int smbd_send_credit_target; +extern int smbd_receive_credit_max; + +enum keep_alive_status { + KEEP_ALIVE_NONE, + KEEP_ALIVE_PENDING, + KEEP_ALIVE_SENT, +}; + +enum smbd_connection_status { + SMBD_CREATED, + SMBD_CONNECTING, + SMBD_CONNECTED, + SMBD_NEGOTIATE_FAILED, + SMBD_DISCONNECTING, + SMBD_DISCONNECTED, + SMBD_DESTROYED +}; + +/* + * The context for the SMBDirect transport + * Everything related to the transport is here. It has several logical parts + * 1. RDMA related structures + * 2. SMBDirect connection parameters + * 3. Memory registrations + * 4. Receive and reassembly queues for data receive path + * 5. mempools for allocating packets + */ +struct smbd_connection { + enum smbd_connection_status transport_status; + + /* RDMA related */ + struct rdma_cm_id *id; + struct ib_qp_init_attr qp_attr; + struct ib_pd *pd; + struct ib_cq *send_cq, *recv_cq; + struct ib_device_attr dev_attr; + int ri_rc; + struct completion ri_done; + wait_queue_head_t conn_wait; + wait_queue_head_t disconn_wait; + + struct completion negotiate_completion; + bool negotiate_done; + + struct work_struct disconnect_work; + struct work_struct post_send_credits_work; + + spinlock_t lock_new_credits_offered; + int new_credits_offered; + + /* Connection parameters defined in [MS-SMBD] 3.1.1.1 */ + int receive_credit_max; + int send_credit_target; + int max_send_size; + int max_fragmented_recv_size; + int max_fragmented_send_size; + int max_receive_size; + int keep_alive_interval; + int max_readwrite_size; + enum keep_alive_status keep_alive_requested; + int protocol; + atomic_t send_credits; + atomic_t receive_credits; + int receive_credit_target; + int fragment_reassembly_remaining; + + /* Memory registrations */ + /* Maximum number of RDMA read/write outstanding on this connection */ + int responder_resources; + /* Maximum number of pages in a single RDMA write/read on this connection */ + int max_frmr_depth; + /* + * If payload is less than or equal to the threshold, + * use RDMA send/recv to send upper layer I/O. + * If payload is more than the threshold, + * use RDMA read/write through memory registration for I/O. + */ + int rdma_readwrite_threshold; + enum ib_mr_type mr_type; + struct list_head mr_list; + spinlock_t mr_list_lock; + /* The number of available MRs ready for memory registration */ + atomic_t mr_ready_count; + atomic_t mr_used_count; + wait_queue_head_t wait_mr; + struct work_struct mr_recovery_work; + /* Used by transport to wait until all MRs are returned */ + wait_queue_head_t wait_for_mr_cleanup; + + /* Activity accoutning */ + atomic_t send_pending; + wait_queue_head_t wait_send_pending; + wait_queue_head_t wait_post_send; + + /* Receive queue */ + struct list_head receive_queue; + int count_receive_queue; + spinlock_t receive_queue_lock; + + struct list_head empty_packet_queue; + int count_empty_packet_queue; + spinlock_t empty_packet_queue_lock; + + wait_queue_head_t wait_receive_queues; + + /* Reassembly queue */ + struct list_head reassembly_queue; + spinlock_t reassembly_queue_lock; + wait_queue_head_t wait_reassembly_queue; + + /* total data length of reassembly queue */ + int reassembly_data_length; + int reassembly_queue_length; + /* the offset to first buffer in reassembly queue */ + int first_entry_offset; + + bool send_immediate; + + wait_queue_head_t wait_send_queue; + + /* + * Indicate if we have received a full packet on the connection + * This is used to identify the first SMBD packet of a assembled + * payload (SMB packet) in reassembly queue so we can return a + * RFC1002 length to upper layer to indicate the length of the SMB + * packet received + */ + bool full_packet_received; + + struct workqueue_struct *workqueue; + struct delayed_work idle_timer_work; + + /* Memory pool for preallocating buffers */ + /* request pool for RDMA send */ + struct kmem_cache *request_cache; + mempool_t *request_mempool; + + /* response pool for RDMA receive */ + struct kmem_cache *response_cache; + mempool_t *response_mempool; + + /* for debug purposes */ + unsigned int count_get_receive_buffer; + unsigned int count_put_receive_buffer; + unsigned int count_reassembly_queue; + unsigned int count_enqueue_reassembly_queue; + unsigned int count_dequeue_reassembly_queue; + unsigned int count_send_empty; +}; + +enum smbd_message_type { + SMBD_NEGOTIATE_RESP, + SMBD_TRANSFER_DATA, +}; + +#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 + +/* SMBD negotiation request packet [MS-SMBD] 2.2.1 */ +struct smbd_negotiate_req { + __le16 min_version; + __le16 max_version; + __le16 reserved; + __le16 credits_requested; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMBD negotiation response packet [MS-SMBD] 2.2.2 */ +struct smbd_negotiate_resp { + __le16 min_version; + __le16 max_version; + __le16 negotiated_version; + __le16 reserved; + __le16 credits_requested; + __le16 credits_granted; + __le32 status; + __le32 max_readwrite_size; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMBD data transfer packet with payload [MS-SMBD] 2.2.3 */ +struct smbd_data_transfer { + __le16 credits_requested; + __le16 credits_granted; + __le16 flags; + __le16 reserved; + __le32 remaining_data_length; + __le32 data_offset; + __le32 data_length; + __le32 padding; + __u8 buffer[]; +} __packed; + +/* The packet fields for a registered RDMA buffer */ +struct smbd_buffer_descriptor_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +/* Maximum number of SGEs used by smbdirect.c in any send work request */ +#define SMBDIRECT_MAX_SEND_SGE 6 + +/* The context for a SMBD request */ +struct smbd_request { + struct smbd_connection *info; + struct ib_cqe cqe; + + /* the SGE entries for this work request */ + struct ib_sge sge[SMBDIRECT_MAX_SEND_SGE]; + int num_sge; + + /* SMBD packet header follows this structure */ + u8 packet[]; +}; + +/* Maximum number of SGEs used by smbdirect.c in any receive work request */ +#define SMBDIRECT_MAX_RECV_SGE 1 + +/* The context for a SMBD response */ +struct smbd_response { + struct smbd_connection *info; + struct ib_cqe cqe; + struct ib_sge sge; + + enum smbd_message_type type; + + /* Link to receive queue or reassembly queue */ + struct list_head list; + + /* Indicate if this is the 1st packet of a payload */ + bool first_segment; + + /* SMBD packet header and payload follows this structure */ + u8 packet[]; +}; + +/* Create a SMBDirect session */ +struct smbd_connection *smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr); + +/* Reconnect SMBDirect session */ +int smbd_reconnect(struct TCP_Server_Info *server); +/* Destroy SMBDirect session */ +void smbd_destroy(struct TCP_Server_Info *server); + +/* Interface for carrying upper layer I/O through send/recv */ +int smbd_recv(struct smbd_connection *info, struct msghdr *msg); +int smbd_send(struct TCP_Server_Info *server, + int num_rqst, struct smb_rqst *rqst); + +enum mr_state { + MR_READY, + MR_REGISTERED, + MR_INVALIDATED, + MR_ERROR +}; + +struct smbd_mr { + struct smbd_connection *conn; + struct list_head list; + enum mr_state state; + struct ib_mr *mr; + struct scatterlist *sgl; + int sgl_count; + enum dma_data_direction dir; + union { + struct ib_reg_wr wr; + struct ib_send_wr inv_wr; + }; + struct ib_cqe cqe; + bool need_invalidate; + struct completion invalidate_done; +}; + +/* Interfaces to register and deregister MR for RDMA read/write */ +struct smbd_mr *smbd_register_mr( + struct smbd_connection *info, struct page *pages[], int num_pages, + int offset, int tailsz, bool writing, bool need_invalidate); +int smbd_deregister_mr(struct smbd_mr *mr); + +#else +#define cifs_rdma_enabled(server) 0 +struct smbd_connection {}; +static inline void *smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr) {return NULL;} +static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; } +static inline void smbd_destroy(struct TCP_Server_Info *server) {} +static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; } +static inline int smbd_send(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst) {return -1; } +#endif + +#endif diff --git a/fs/smb/client/smbencrypt.c b/fs/smb/client/smbencrypt.c new file mode 100644 index 000000000000..f0ce26414f17 --- /dev/null +++ b/fs/smb/client/smbencrypt.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB parameters and setup + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Modified by Jeremy Allison 1995. + Copyright (C) Andrew Bartlett 2002-2003 + Modified by Steve French (sfrench@us.ibm.com) 2002-2003 + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifsproto.h" +#include "../common/md4.h" + +#ifndef false +#define false 0 +#endif +#ifndef true +#define true 1 +#endif + +/* following came from the other byteorder.h to avoid include conflicts */ +#define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) +#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8) +#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((__u16)(val))) + +/* produce a md4 message digest from data of length n bytes */ +static int +mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len) +{ + int rc; + struct md4_ctx mctx; + + rc = cifs_md4_init(&mctx); + if (rc) { + cifs_dbg(VFS, "%s: Could not init MD4\n", __func__); + goto mdfour_err; + } + rc = cifs_md4_update(&mctx, link_str, link_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update MD4\n", __func__); + goto mdfour_err; + } + rc = cifs_md4_final(&mctx, md4_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not finalize MD4\n", __func__); + + +mdfour_err: + return rc; +} + +/* + * Creates the MD4 Hash of the users password in NT UNICODE. + */ + +int +E_md4hash(const unsigned char *passwd, unsigned char *p16, + const struct nls_table *codepage) +{ + int rc; + int len; + __le16 wpwd[129]; + + /* Password cannot be longer than 128 characters */ + if (passwd) /* Password must be converted to NT unicode */ + len = cifs_strtoUTF16(wpwd, passwd, 128, codepage); + else { + len = 0; + *wpwd = 0; /* Ensure string is null terminated */ + } + + rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__le16)); + memzero_explicit(wpwd, sizeof(wpwd)); + + return rc; +} diff --git a/fs/smb/client/smberr.h b/fs/smb/client/smberr.h new file mode 100644 index 000000000000..aeffdad829e2 --- /dev/null +++ b/fs/smb/client/smberr.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * See Error Codes section of the SNIA CIFS Specification + * for more information + * + */ + +#define SUCCESS 0x00 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ +#define ERRSRV 0x02 /* Error is generated by the file server daemon */ +#define ERRHRD 0x03 /* Error is a hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* The following error codes may be generated with the SUCCESS error class.*/ + +/*#define SUCCESS 0 The request was successful. */ + +/* The following error codes may be generated with the ERRDOS error class.*/ + +#define ERRbadfunc 1 /* Invalid function. The server did not + recognize or could not perform a + system call generated by the server, + e.g., set the DIRECTORY attribute on + a data file, invalid seek mode. */ +#define ERRbadfile 2 /* File not found. The last component + of a file's pathname could not be + found. */ +#define ERRbadpath 3 /* Directory invalid. A directory + component in a pathname could not be + found. */ +#define ERRnofids 4 /* Too many open files. The server has + no file handles available. */ +#define ERRnoaccess 5 /* Access denied, the client's context + does not permit the requested + function. This includes the + following conditions: invalid rename + command, write to Fid open for read + only, read on Fid open for write + only, attempt to delete a non-empty + directory */ +#define ERRbadfid 6 /* Invalid file handle. The file handle + specified was not recognized by the + server. */ +#define ERRbadmcb 7 /* Memory control blocks destroyed. */ +#define ERRnomem 8 /* Insufficient server memory to + perform the requested function. */ +#define ERRbadmem 9 /* Invalid memory block address. */ +#define ERRbadenv 10 /* Invalid environment. */ +#define ERRbadformat 11 /* Invalid format. */ +#define ERRbadaccess 12 /* Invalid open mode. */ +#define ERRbaddata 13 /* Invalid data (generated only by + IOCTL calls within the server). */ +#define ERRbaddrive 15 /* Invalid drive specified. */ +#define ERRremcd 16 /* A Delete Directory request attempted + to remove the server's current + directory. */ +#define ERRdiffdevice 17 /* Not same device (e.g., a cross + volume rename was attempted */ +#define ERRnofiles 18 /* A File Search command can find no + more files matching the specified + criteria. */ +#define ERRwriteprot 19 /* media is write protected */ +#define ERRgeneral 31 +#define ERRbadshare 32 /* The sharing mode specified for an + Open conflicts with existing FIDs on + the file. */ +#define ERRlock 33 /* A Lock request conflicted with an + existing lock or specified an + invalid mode, or an Unlock requested + attempted to remove a lock held by + another process. */ +#define ERRunsup 50 +#define ERRnosuchshare 67 +#define ERRfilexists 80 /* The file named in the request + already exists. */ +#define ERRinvparm 87 +#define ERRdiskfull 112 +#define ERRinvname 123 +#define ERRinvlevel 124 +#define ERRdirnotempty 145 +#define ERRnotlocked 158 +#define ERRcancelviolation 173 +#define ERRalreadyexists 183 +#define ERRbadpipe 230 +#define ERRpipebusy 231 +#define ERRpipeclosing 232 +#define ERRnotconnected 233 +#define ERRmoredata 234 +#define ERReasnotsupported 282 +#define ErrQuota 0x200 /* The operation would cause a quota + limit to be exceeded. */ +#define ErrNotALink 0x201 /* A link operation was performed on a + pathname that was not a link. */ + +/* Below errors are used internally (do not come over the wire) for passthrough + from STATUS codes to POSIX only */ +#define ERRsymlink 0xFFFD +#define ErrTooManyLinks 0xFFFE + +/* Following error codes may be generated with the ERRSRV error class.*/ + +#define ERRerror 1 /* Non-specific error code. It is + returned under the following + conditions: resource other than disk + space exhausted (e.g. TIDs), first + SMB command was not negotiate, + multiple negotiates attempted, and + internal server error. */ +#define ERRbadpw 2 /* Bad password - name/password pair in + a TreeConnect or Session Setup are + invalid. */ +#define ERRbadtype 3 /* used for indicating DFS referral + needed */ +#define ERRaccess 4 /* The client does not have the + necessary access rights within the + specified context for requested + function. */ +#define ERRinvtid 5 /* The Tid specified in a command was + invalid. */ +#define ERRinvnetname 6 /* Invalid network name in tree + connect. */ +#define ERRinvdevice 7 /* Invalid device - printer request + made to non-printer connection or + non-printer request made to printer + connection. */ +#define ERRqfull 49 /* Print queue full (files) -- returned + by open print file. */ +#define ERRqtoobig 50 /* Print queue full -- no space. */ +#define ERRqeof 51 /* EOF on print queue dump */ +#define ERRinvpfid 52 /* Invalid print file FID. */ +#define ERRsmbcmd 64 /* The server did not recognize the + command received. */ +#define ERRsrverror 65 /* The server encountered an internal + error, e.g., system file + unavailable. */ +#define ERRbadBID 66 /* (obsolete) */ +#define ERRfilespecs 67 /* The Fid and pathname parameters + contained an invalid combination of + values. */ +#define ERRbadLink 68 /* (obsolete) */ +#define ERRbadpermits 69 /* The access permissions specified for + a file or directory are not a valid + combination. */ +#define ERRbadPID 70 +#define ERRsetattrmode 71 /* attribute (mode) is invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* reserved - messaging off */ +#define ERRnoroom 83 /* reserved - no room for message */ +#define ERRrmuns 87 /* reserved - too many remote names */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources available for request + */ +#define ERRtoomanyuids 90 /* Too many UIDs active on this session + */ +#define ERRbaduid 91 /* The UID is not known as a valid user + */ +#define ERRusempx 250 /* temporarily unable to use raw */ +#define ERRusestd 251 /* temporarily unable to use either raw + or mpx */ +#define ERR_NOTIFY_ENUM_DIR 1024 +#define ERRnoSuchUser 2238 /* user account does not exist */ +#define ERRaccountexpired 2239 +#define ERRbadclient 2240 /* can not logon from this client */ +#define ERRbadLogonTime 2241 /* logon hours do not allow this */ +#define ERRpasswordExpired 2242 +#define ERRnetlogonNotStarted 2455 +#define ERRnosupport 0xFFFF diff --git a/fs/smb/client/trace.c b/fs/smb/client/trace.c new file mode 100644 index 000000000000..465483787193 --- /dev/null +++ b/fs/smb/client/trace.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018, Microsoft Corporation. + * + * Author(s): Steve French + */ +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h new file mode 100644 index 000000000000..d3053bd8ae73 --- /dev/null +++ b/fs/smb/client/trace.h @@ -0,0 +1,1070 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018, Microsoft Corporation. + * + * Author(s): Steve French + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cifs + +#if !defined(_CIFS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _CIFS_TRACE_H + +#include +#include +#include + +/* + * Please use this 3-part article as a reference for writing new tracepoints: + * https://lwn.net/Articles/379903/ + */ + +/* For logging errors in read or write */ +DECLARE_EVENT_CLASS(smb3_rw_err_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u64 offset, + __u32 len, + int rc), + TP_ARGS(xid, fid, tid, sesid, offset, len, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, offset) + __field(__u32, len) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->offset = offset; + __entry->len = len; + __entry->rc = rc; + ), + TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->offset, __entry->len, __entry->rc) +) + +#define DEFINE_SMB3_RW_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u64 offset, \ + __u32 len, \ + int rc), \ + TP_ARGS(xid, fid, tid, sesid, offset, len, rc)) + +DEFINE_SMB3_RW_ERR_EVENT(write_err); +DEFINE_SMB3_RW_ERR_EVENT(read_err); +DEFINE_SMB3_RW_ERR_EVENT(query_dir_err); +DEFINE_SMB3_RW_ERR_EVENT(zero_err); +DEFINE_SMB3_RW_ERR_EVENT(falloc_err); + + +/* For logging successful read or write */ +DECLARE_EVENT_CLASS(smb3_rw_done_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u64 offset, + __u32 len), + TP_ARGS(xid, fid, tid, sesid, offset, len), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, offset) + __field(__u32, len) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->offset = offset; + __entry->len = len; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->offset, __entry->len) +) + +#define DEFINE_SMB3_RW_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u64 offset, \ + __u32 len), \ + TP_ARGS(xid, fid, tid, sesid, offset, len)) + +DEFINE_SMB3_RW_DONE_EVENT(write_enter); +DEFINE_SMB3_RW_DONE_EVENT(read_enter); +DEFINE_SMB3_RW_DONE_EVENT(query_dir_enter); +DEFINE_SMB3_RW_DONE_EVENT(zero_enter); +DEFINE_SMB3_RW_DONE_EVENT(falloc_enter); +DEFINE_SMB3_RW_DONE_EVENT(write_done); +DEFINE_SMB3_RW_DONE_EVENT(read_done); +DEFINE_SMB3_RW_DONE_EVENT(query_dir_done); +DEFINE_SMB3_RW_DONE_EVENT(zero_done); +DEFINE_SMB3_RW_DONE_EVENT(falloc_done); + +/* For logging successful set EOF (truncate) */ +DECLARE_EVENT_CLASS(smb3_eof_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u64 offset), + TP_ARGS(xid, fid, tid, sesid, offset), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, offset) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->offset = offset; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->offset) +) + +#define DEFINE_SMB3_EOF_EVENT(name) \ +DEFINE_EVENT(smb3_eof_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u64 offset), \ + TP_ARGS(xid, fid, tid, sesid, offset)) + +DEFINE_SMB3_EOF_EVENT(set_eof); + +/* + * For handle based calls other than read and write, and get/set info + */ +DECLARE_EVENT_CLASS(smb3_fd_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid), + TP_ARGS(xid, fid, tid, sesid), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + ), + TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid) +) + +#define DEFINE_SMB3_FD_EVENT(name) \ +DEFINE_EVENT(smb3_fd_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid), \ + TP_ARGS(xid, fid, tid, sesid)) + +DEFINE_SMB3_FD_EVENT(flush_enter); +DEFINE_SMB3_FD_EVENT(flush_done); +DEFINE_SMB3_FD_EVENT(close_enter); +DEFINE_SMB3_FD_EVENT(close_done); +DEFINE_SMB3_FD_EVENT(oplock_not_found); + +DECLARE_EVENT_CLASS(smb3_fd_err_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + int rc), + TP_ARGS(xid, fid, tid, sesid, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->rc = rc; + ), + TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->rc) +) + +#define DEFINE_SMB3_FD_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_fd_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + int rc), \ + TP_ARGS(xid, fid, tid, sesid, rc)) + +DEFINE_SMB3_FD_ERR_EVENT(flush_err); +DEFINE_SMB3_FD_ERR_EVENT(lock_err); +DEFINE_SMB3_FD_ERR_EVENT(close_err); + +/* + * For handle based query/set info calls + */ +DECLARE_EVENT_CLASS(smb3_inf_enter_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u8 infclass, + __u32 type), + TP_ARGS(xid, fid, tid, sesid, infclass, type), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u8, infclass) + __field(__u32, type) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->infclass = infclass; + __entry->type = type; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->infclass, __entry->type) +) + +#define DEFINE_SMB3_INF_ENTER_EVENT(name) \ +DEFINE_EVENT(smb3_inf_enter_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u8 infclass, \ + __u32 type), \ + TP_ARGS(xid, fid, tid, sesid, infclass, type)) + +DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter); +DEFINE_SMB3_INF_ENTER_EVENT(query_info_done); +DEFINE_SMB3_INF_ENTER_EVENT(notify_enter); +DEFINE_SMB3_INF_ENTER_EVENT(notify_done); + +DECLARE_EVENT_CLASS(smb3_inf_err_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u8 infclass, + __u32 type, + int rc), + TP_ARGS(xid, fid, tid, sesid, infclass, type, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u8, infclass) + __field(__u32, type) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->infclass = infclass; + __entry->type = type; + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->infclass, __entry->type, __entry->rc) +) + +#define DEFINE_SMB3_INF_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_inf_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u8 infclass, \ + __u32 type, \ + int rc), \ + TP_ARGS(xid, fid, tid, sesid, infclass, type, rc)) + +DEFINE_SMB3_INF_ERR_EVENT(query_info_err); +DEFINE_SMB3_INF_ERR_EVENT(set_info_err); +DEFINE_SMB3_INF_ERR_EVENT(notify_err); +DEFINE_SMB3_INF_ERR_EVENT(fsctl_err); + +DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + const char *full_path), + TP_ARGS(xid, tid, sesid, full_path), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __string(path, full_path) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __assign_str(path, full_path); + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s", + __entry->xid, __entry->sesid, __entry->tid, + __get_str(path)) +) + +#define DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(name) \ +DEFINE_EVENT(smb3_inf_compound_enter_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + const char *full_path), \ + TP_ARGS(xid, tid, sesid, full_path)) + +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_info_compound_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(posix_query_info_compound_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(hardlink_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter); + + +DECLARE_EVENT_CLASS(smb3_inf_compound_done_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid), + TP_ARGS(xid, tid, sesid), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x", + __entry->xid, __entry->sesid, __entry->tid) +) + +#define DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_inf_compound_done_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid), \ + TP_ARGS(xid, tid, sesid)) + +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_info_compound_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(posix_query_info_compound_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(hardlink_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); + + +DECLARE_EVENT_CLASS(smb3_inf_compound_err_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + int rc), + TP_ARGS(xid, tid, sesid, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, + __entry->rc) +) + +#define DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_inf_compound_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + int rc), \ + TP_ARGS(xid, tid, sesid, rc)) + +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_info_compound_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(posix_query_info_compound_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(hardlink_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); + +/* + * For logging SMB3 Status code and Command for responses which return errors + */ +DECLARE_EVENT_CLASS(smb3_cmd_err_class, + TP_PROTO(__u32 tid, + __u64 sesid, + __u16 cmd, + __u64 mid, + __u32 status, + int rc), + TP_ARGS(tid, sesid, cmd, mid, status, rc), + TP_STRUCT__entry( + __field(__u32, tid) + __field(__u64, sesid) + __field(__u16, cmd) + __field(__u64, mid) + __field(__u32, status) + __field(int, rc) + ), + TP_fast_assign( + __entry->tid = tid; + __entry->sesid = sesid; + __entry->cmd = cmd; + __entry->mid = mid; + __entry->status = status; + __entry->rc = rc; + ), + TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d", + __entry->sesid, __entry->tid, __entry->cmd, __entry->mid, + __entry->status, __entry->rc) +) + +#define DEFINE_SMB3_CMD_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_cmd_err_class, smb3_##name, \ + TP_PROTO(__u32 tid, \ + __u64 sesid, \ + __u16 cmd, \ + __u64 mid, \ + __u32 status, \ + int rc), \ + TP_ARGS(tid, sesid, cmd, mid, status, rc)) + +DEFINE_SMB3_CMD_ERR_EVENT(cmd_err); + +DECLARE_EVENT_CLASS(smb3_cmd_done_class, + TP_PROTO(__u32 tid, + __u64 sesid, + __u16 cmd, + __u64 mid), + TP_ARGS(tid, sesid, cmd, mid), + TP_STRUCT__entry( + __field(__u32, tid) + __field(__u64, sesid) + __field(__u16, cmd) + __field(__u64, mid) + ), + TP_fast_assign( + __entry->tid = tid; + __entry->sesid = sesid; + __entry->cmd = cmd; + __entry->mid = mid; + ), + TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu", + __entry->sesid, __entry->tid, + __entry->cmd, __entry->mid) +) + +#define DEFINE_SMB3_CMD_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \ + TP_PROTO(__u32 tid, \ + __u64 sesid, \ + __u16 cmd, \ + __u64 mid), \ + TP_ARGS(tid, sesid, cmd, mid)) + +DEFINE_SMB3_CMD_DONE_EVENT(cmd_enter); +DEFINE_SMB3_CMD_DONE_EVENT(cmd_done); +DEFINE_SMB3_CMD_DONE_EVENT(ses_expired); + +DECLARE_EVENT_CLASS(smb3_mid_class, + TP_PROTO(__u16 cmd, + __u64 mid, + __u32 pid, + unsigned long when_sent, + unsigned long when_received), + TP_ARGS(cmd, mid, pid, when_sent, when_received), + TP_STRUCT__entry( + __field(__u16, cmd) + __field(__u64, mid) + __field(__u32, pid) + __field(unsigned long, when_sent) + __field(unsigned long, when_received) + ), + TP_fast_assign( + __entry->cmd = cmd; + __entry->mid = mid; + __entry->pid = pid; + __entry->when_sent = when_sent; + __entry->when_received = when_received; + ), + TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu", + __entry->cmd, __entry->mid, __entry->pid, __entry->when_sent, + __entry->when_received) +) + +#define DEFINE_SMB3_MID_EVENT(name) \ +DEFINE_EVENT(smb3_mid_class, smb3_##name, \ + TP_PROTO(__u16 cmd, \ + __u64 mid, \ + __u32 pid, \ + unsigned long when_sent, \ + unsigned long when_received), \ + TP_ARGS(cmd, mid, pid, when_sent, when_received)) + +DEFINE_SMB3_MID_EVENT(slow_rsp); + +DECLARE_EVENT_CLASS(smb3_exit_err_class, + TP_PROTO(unsigned int xid, + const char *func_name, + int rc), + TP_ARGS(xid, func_name, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __string(func_name, func_name) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __assign_str(func_name, func_name); + __entry->rc = rc; + ), + TP_printk("\t%s: xid=%u rc=%d", + __get_str(func_name), __entry->xid, __entry->rc) +) + +#define DEFINE_SMB3_EXIT_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_exit_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + const char *func_name, \ + int rc), \ + TP_ARGS(xid, func_name, rc)) + +DEFINE_SMB3_EXIT_ERR_EVENT(exit_err); + + +DECLARE_EVENT_CLASS(smb3_sync_err_class, + TP_PROTO(unsigned long ino, + int rc), + TP_ARGS(ino, rc), + TP_STRUCT__entry( + __field(unsigned long, ino) + __field(int, rc) + ), + TP_fast_assign( + __entry->ino = ino; + __entry->rc = rc; + ), + TP_printk("\tino=%lu rc=%d", + __entry->ino, __entry->rc) +) + +#define DEFINE_SMB3_SYNC_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_sync_err_class, cifs_##name, \ + TP_PROTO(unsigned long ino, \ + int rc), \ + TP_ARGS(ino, rc)) + +DEFINE_SMB3_SYNC_ERR_EVENT(fsync_err); +DEFINE_SMB3_SYNC_ERR_EVENT(flush_err); + + +DECLARE_EVENT_CLASS(smb3_enter_exit_class, + TP_PROTO(unsigned int xid, + const char *func_name), + TP_ARGS(xid, func_name), + TP_STRUCT__entry( + __field(unsigned int, xid) + __string(func_name, func_name) + ), + TP_fast_assign( + __entry->xid = xid; + __assign_str(func_name, func_name); + ), + TP_printk("\t%s: xid=%u", + __get_str(func_name), __entry->xid) +) + +#define DEFINE_SMB3_ENTER_EXIT_EVENT(name) \ +DEFINE_EVENT(smb3_enter_exit_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + const char *func_name), \ + TP_ARGS(xid, func_name)) + +DEFINE_SMB3_ENTER_EXIT_EVENT(enter); +DEFINE_SMB3_ENTER_EXIT_EVENT(exit_done); + +/* + * For SMB2/SMB3 tree connect + */ + +DECLARE_EVENT_CLASS(smb3_tcon_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + const char *unc_name, + int rc), + TP_ARGS(xid, tid, sesid, unc_name, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __string(name, unc_name) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __assign_str(name, unc_name); + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x unc_name=%s rc=%d", + __entry->xid, __entry->sesid, __entry->tid, + __get_str(name), __entry->rc) +) + +#define DEFINE_SMB3_TCON_EVENT(name) \ +DEFINE_EVENT(smb3_tcon_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + const char *unc_name, \ + int rc), \ + TP_ARGS(xid, tid, sesid, unc_name, rc)) + +DEFINE_SMB3_TCON_EVENT(tcon); + + +/* + * For smb2/smb3 open (including create and mkdir) calls + */ + +DECLARE_EVENT_CLASS(smb3_open_enter_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + const char *full_path, + int create_options, + int desired_access), + TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __string(path, full_path) + __field(int, create_options) + __field(int, desired_access) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __assign_str(path, full_path); + __entry->create_options = create_options; + __entry->desired_access = desired_access; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s cr_opts=0x%x des_access=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __get_str(path), + __entry->create_options, __entry->desired_access) +) + +#define DEFINE_SMB3_OPEN_ENTER_EVENT(name) \ +DEFINE_EVENT(smb3_open_enter_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + const char *full_path, \ + int create_options, \ + int desired_access), \ + TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access)) + +DEFINE_SMB3_OPEN_ENTER_EVENT(open_enter); +DEFINE_SMB3_OPEN_ENTER_EVENT(posix_mkdir_enter); + +DECLARE_EVENT_CLASS(smb3_open_err_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + int create_options, + int desired_access, + int rc), + TP_ARGS(xid, tid, sesid, create_options, desired_access, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, create_options) + __field(int, desired_access) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->create_options = create_options; + __entry->desired_access = desired_access; + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x cr_opts=0x%x des_access=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, + __entry->create_options, __entry->desired_access, __entry->rc) +) + +#define DEFINE_SMB3_OPEN_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_open_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + int create_options, \ + int desired_access, \ + int rc), \ + TP_ARGS(xid, tid, sesid, create_options, desired_access, rc)) + +DEFINE_SMB3_OPEN_ERR_EVENT(open_err); +DEFINE_SMB3_OPEN_ERR_EVENT(posix_mkdir_err); + +DECLARE_EVENT_CLASS(smb3_open_done_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + int create_options, + int desired_access), + TP_ARGS(xid, fid, tid, sesid, create_options, desired_access), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, create_options) + __field(int, desired_access) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->create_options = create_options; + __entry->desired_access = desired_access; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->create_options, __entry->desired_access) +) + +#define DEFINE_SMB3_OPEN_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_open_done_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + int create_options, \ + int desired_access), \ + TP_ARGS(xid, fid, tid, sesid, create_options, desired_access)) + +DEFINE_SMB3_OPEN_DONE_EVENT(open_done); +DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); + + +DECLARE_EVENT_CLASS(smb3_lease_done_class, + TP_PROTO(__u32 lease_state, + __u32 tid, + __u64 sesid, + __u64 lease_key_low, + __u64 lease_key_high), + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high), + TP_STRUCT__entry( + __field(__u32, lease_state) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, lease_key_low) + __field(__u64, lease_key_high) + ), + TP_fast_assign( + __entry->lease_state = lease_state; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->lease_key_low = lease_key_low; + __entry->lease_key_high = lease_key_high; + ), + TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x", + __entry->sesid, __entry->tid, __entry->lease_key_high, + __entry->lease_key_low, __entry->lease_state) +) + +#define DEFINE_SMB3_LEASE_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_lease_done_class, smb3_##name, \ + TP_PROTO(__u32 lease_state, \ + __u32 tid, \ + __u64 sesid, \ + __u64 lease_key_low, \ + __u64 lease_key_high), \ + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high)) + +DEFINE_SMB3_LEASE_DONE_EVENT(lease_done); +DEFINE_SMB3_LEASE_DONE_EVENT(lease_not_found); + +DECLARE_EVENT_CLASS(smb3_lease_err_class, + TP_PROTO(__u32 lease_state, + __u32 tid, + __u64 sesid, + __u64 lease_key_low, + __u64 lease_key_high, + int rc), + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc), + TP_STRUCT__entry( + __field(__u32, lease_state) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, lease_key_low) + __field(__u64, lease_key_high) + __field(int, rc) + ), + TP_fast_assign( + __entry->lease_state = lease_state; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->lease_key_low = lease_key_low; + __entry->lease_key_high = lease_key_high; + __entry->rc = rc; + ), + TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x rc=%d", + __entry->sesid, __entry->tid, __entry->lease_key_high, + __entry->lease_key_low, __entry->lease_state, __entry->rc) +) + +#define DEFINE_SMB3_LEASE_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_lease_err_class, smb3_##name, \ + TP_PROTO(__u32 lease_state, \ + __u32 tid, \ + __u64 sesid, \ + __u64 lease_key_low, \ + __u64 lease_key_high, \ + int rc), \ + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc)) + +DEFINE_SMB3_LEASE_ERR_EVENT(lease_err); + +DECLARE_EVENT_CLASS(smb3_connect_class, + TP_PROTO(char *hostname, + __u64 conn_id, + const struct __kernel_sockaddr_storage *dst_addr), + TP_ARGS(hostname, conn_id, dst_addr), + TP_STRUCT__entry( + __string(hostname, hostname) + __field(__u64, conn_id) + __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) + ), + TP_fast_assign( + struct sockaddr_storage *pss = NULL; + + __entry->conn_id = conn_id; + pss = (struct sockaddr_storage *)__entry->dst_addr; + *pss = *dst_addr; + __assign_str(hostname, hostname); + ), + TP_printk("conn_id=0x%llx server=%s addr=%pISpsfc", + __entry->conn_id, + __get_str(hostname), + __entry->dst_addr) +) + +#define DEFINE_SMB3_CONNECT_EVENT(name) \ +DEFINE_EVENT(smb3_connect_class, smb3_##name, \ + TP_PROTO(char *hostname, \ + __u64 conn_id, \ + const struct __kernel_sockaddr_storage *addr), \ + TP_ARGS(hostname, conn_id, addr)) + +DEFINE_SMB3_CONNECT_EVENT(connect_done); + +DECLARE_EVENT_CLASS(smb3_connect_err_class, + TP_PROTO(char *hostname, __u64 conn_id, + const struct __kernel_sockaddr_storage *dst_addr, int rc), + TP_ARGS(hostname, conn_id, dst_addr, rc), + TP_STRUCT__entry( + __string(hostname, hostname) + __field(__u64, conn_id) + __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) + __field(int, rc) + ), + TP_fast_assign( + struct sockaddr_storage *pss = NULL; + + __entry->conn_id = conn_id; + __entry->rc = rc; + pss = (struct sockaddr_storage *)__entry->dst_addr; + *pss = *dst_addr; + __assign_str(hostname, hostname); + ), + TP_printk("rc=%d conn_id=0x%llx server=%s addr=%pISpsfc", + __entry->rc, + __entry->conn_id, + __get_str(hostname), + __entry->dst_addr) +) + +#define DEFINE_SMB3_CONNECT_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_connect_err_class, smb3_##name, \ + TP_PROTO(char *hostname, \ + __u64 conn_id, \ + const struct __kernel_sockaddr_storage *addr, \ + int rc), \ + TP_ARGS(hostname, conn_id, addr, rc)) + +DEFINE_SMB3_CONNECT_ERR_EVENT(connect_err); + +DECLARE_EVENT_CLASS(smb3_reconnect_class, + TP_PROTO(__u64 currmid, + __u64 conn_id, + char *hostname), + TP_ARGS(currmid, conn_id, hostname), + TP_STRUCT__entry( + __field(__u64, currmid) + __field(__u64, conn_id) + __string(hostname, hostname) + ), + TP_fast_assign( + __entry->currmid = currmid; + __entry->conn_id = conn_id; + __assign_str(hostname, hostname); + ), + TP_printk("conn_id=0x%llx server=%s current_mid=%llu", + __entry->conn_id, + __get_str(hostname), + __entry->currmid) +) + +#define DEFINE_SMB3_RECONNECT_EVENT(name) \ +DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \ + TP_PROTO(__u64 currmid, \ + __u64 conn_id, \ + char *hostname), \ + TP_ARGS(currmid, conn_id, hostname)) + +DEFINE_SMB3_RECONNECT_EVENT(reconnect); +DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect); + +DECLARE_EVENT_CLASS(smb3_credit_class, + TP_PROTO(__u64 currmid, + __u64 conn_id, + char *hostname, + int credits, + int credits_to_add, + int in_flight), + TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight), + TP_STRUCT__entry( + __field(__u64, currmid) + __field(__u64, conn_id) + __string(hostname, hostname) + __field(int, credits) + __field(int, credits_to_add) + __field(int, in_flight) + ), + TP_fast_assign( + __entry->currmid = currmid; + __entry->conn_id = conn_id; + __assign_str(hostname, hostname); + __entry->credits = credits; + __entry->credits_to_add = credits_to_add; + __entry->in_flight = in_flight; + ), + TP_printk("conn_id=0x%llx server=%s current_mid=%llu " + "credits=%d credit_change=%d in_flight=%d", + __entry->conn_id, + __get_str(hostname), + __entry->currmid, + __entry->credits, + __entry->credits_to_add, + __entry->in_flight) +) + +#define DEFINE_SMB3_CREDIT_EVENT(name) \ +DEFINE_EVENT(smb3_credit_class, smb3_##name, \ + TP_PROTO(__u64 currmid, \ + __u64 conn_id, \ + char *hostname, \ + int credits, \ + int credits_to_add, \ + int in_flight), \ + TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight)) + +DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits); +DEFINE_SMB3_CREDIT_EVENT(reconnect_detected); +DEFINE_SMB3_CREDIT_EVENT(credit_timeout); +DEFINE_SMB3_CREDIT_EVENT(insufficient_credits); +DEFINE_SMB3_CREDIT_EVENT(too_many_credits); +DEFINE_SMB3_CREDIT_EVENT(add_credits); +DEFINE_SMB3_CREDIT_EVENT(adj_credits); +DEFINE_SMB3_CREDIT_EVENT(hdr_credits); +DEFINE_SMB3_CREDIT_EVENT(nblk_credits); +DEFINE_SMB3_CREDIT_EVENT(pend_credits); +DEFINE_SMB3_CREDIT_EVENT(wait_credits); +DEFINE_SMB3_CREDIT_EVENT(waitff_credits); +DEFINE_SMB3_CREDIT_EVENT(overflow_credits); +DEFINE_SMB3_CREDIT_EVENT(set_credits); + +#endif /* _CIFS_TRACE_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace +#include diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c new file mode 100644 index 000000000000..c961b90f92b9 --- /dev/null +++ b/fs/smb/client/transport.c @@ -0,0 +1,1807 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) 2006. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "smb2proto.h" +#include "smbdirect.h" + +/* Max number of iovectors we can use off the stack when sending requests. */ +#define CIFS_MAX_IOV_SIZE 8 + +void +cifs_wake_up_task(struct mid_q_entry *mid) +{ + wake_up_process(mid->callback_data); +} + +static struct mid_q_entry * +alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) +{ + struct mid_q_entry *temp; + + if (server == NULL) { + cifs_dbg(VFS, "%s: null TCP session\n", __func__); + return NULL; + } + + temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); + memset(temp, 0, sizeof(struct mid_q_entry)); + kref_init(&temp->refcount); + temp->mid = get_mid(smb_buffer); + temp->pid = current->pid; + temp->command = cpu_to_le16(smb_buffer->Command); + cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); + /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ + /* when mid allocated can be before when sent */ + temp->when_alloc = jiffies; + temp->server = server; + + /* + * The default is for the mid to be synchronous, so the + * default callback just wakes up the current task. + */ + get_task_struct(current); + temp->creator = current; + temp->callback = cifs_wake_up_task; + temp->callback_data = current; + + atomic_inc(&mid_count); + temp->mid_state = MID_REQUEST_ALLOCATED; + return temp; +} + +static void __release_mid(struct kref *refcount) +{ + struct mid_q_entry *midEntry = + container_of(refcount, struct mid_q_entry, refcount); +#ifdef CONFIG_CIFS_STATS2 + __le16 command = midEntry->server->vals->lock_cmd; + __u16 smb_cmd = le16_to_cpu(midEntry->command); + unsigned long now; + unsigned long roundtrip_time; +#endif + struct TCP_Server_Info *server = midEntry->server; + + if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) && + midEntry->mid_state == MID_RESPONSE_RECEIVED && + server->ops->handle_cancelled_mid) + server->ops->handle_cancelled_mid(midEntry, server); + + midEntry->mid_state = MID_FREE; + atomic_dec(&mid_count); + if (midEntry->large_buf) + cifs_buf_release(midEntry->resp_buf); + else + cifs_small_buf_release(midEntry->resp_buf); +#ifdef CONFIG_CIFS_STATS2 + now = jiffies; + if (now < midEntry->when_alloc) + cifs_server_dbg(VFS, "Invalid mid allocation time\n"); + roundtrip_time = now - midEntry->when_alloc; + + if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) { + if (atomic_read(&server->num_cmds[smb_cmd]) == 0) { + server->slowest_cmd[smb_cmd] = roundtrip_time; + server->fastest_cmd[smb_cmd] = roundtrip_time; + } else { + if (server->slowest_cmd[smb_cmd] < roundtrip_time) + server->slowest_cmd[smb_cmd] = roundtrip_time; + else if (server->fastest_cmd[smb_cmd] > roundtrip_time) + server->fastest_cmd[smb_cmd] = roundtrip_time; + } + cifs_stats_inc(&server->num_cmds[smb_cmd]); + server->time_per_cmd[smb_cmd] += roundtrip_time; + } + /* + * commands taking longer than one second (default) can be indications + * that something is wrong, unless it is quite a slow link or a very + * busy server. Note that this calc is unlikely or impossible to wrap + * as long as slow_rsp_threshold is not set way above recommended max + * value (32767 ie 9 hours) and is generally harmless even if wrong + * since only affects debug counters - so leaving the calc as simple + * comparison rather than doing multiple conversions and overflow + * checks + */ + if ((slow_rsp_threshold != 0) && + time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) && + (midEntry->command != command)) { + /* + * smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command + * NB: le16_to_cpu returns unsigned so can not be negative below + */ + if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) + cifs_stats_inc(&server->smb2slowcmd[smb_cmd]); + + trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid, + midEntry->when_sent, midEntry->when_received); + if (cifsFYI & CIFS_TIMER) { + pr_debug("slow rsp: cmd %d mid %llu", + midEntry->command, midEntry->mid); + cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n", + now - midEntry->when_alloc, + now - midEntry->when_sent, + now - midEntry->when_received); + } + } +#endif + put_task_struct(midEntry->creator); + + mempool_free(midEntry, cifs_mid_poolp); +} + +void release_mid(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->server; + + spin_lock(&server->mid_lock); + kref_put(&mid->refcount, __release_mid); + spin_unlock(&server->mid_lock); +} + +void +delete_mid(struct mid_q_entry *mid) +{ + spin_lock(&mid->server->mid_lock); + if (!(mid->mid_flags & MID_DELETED)) { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + } + spin_unlock(&mid->server->mid_lock); + + release_mid(mid); +} + +/* + * smb_send_kvec - send an array of kvecs to the server + * @server: Server to send the data to + * @smb_msg: Message to send + * @sent: amount of data sent on socket is stored here + * + * Our basic "send data to server" function. Should be called with srv_mutex + * held. The caller is responsible for handling the results. + */ +static int +smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, + size_t *sent) +{ + int rc = 0; + int retries = 0; + struct socket *ssocket = server->ssocket; + + *sent = 0; + + if (server->noblocksnd) + smb_msg->msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; + else + smb_msg->msg_flags = MSG_NOSIGNAL; + + while (msg_data_left(smb_msg)) { + /* + * If blocking send, we try 3 times, since each can block + * for 5 seconds. For nonblocking we have to try more + * but wait increasing amounts of time allowing time for + * socket to clear. The overall time we wait in either + * case to send on the socket is about 15 seconds. + * Similarly we wait for 15 seconds for a response from + * the server in SendReceive[2] for the server to send + * a response back for most types of requests (except + * SMB Write past end of file which can be slow, and + * blocking lock operations). NFS waits slightly longer + * than CIFS, but this can make it take longer for + * nonresponsive servers to be detected and 15 seconds + * is more than enough time for modern networks to + * send a packet. In most cases if we fail to send + * after the retries we will kill the socket and + * reconnect which may clear the network problem. + */ + rc = sock_sendmsg(ssocket, smb_msg); + if (rc == -EAGAIN) { + retries++; + if (retries >= 14 || + (!server->noblocksnd && (retries > 2))) { + cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", + ssocket); + return -EAGAIN; + } + msleep(1 << retries); + continue; + } + + if (rc < 0) + return rc; + + if (rc == 0) { + /* should never happen, letting socket clear before + retrying is our only obvious option here */ + cifs_server_dbg(VFS, "tcp sent no data\n"); + msleep(500); + continue; + } + + /* send was at least partially successful */ + *sent += rc; + retries = 0; /* in case we get ENOSPC on the next send */ + } + return 0; +} + +unsigned long +smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ + unsigned int i; + struct kvec *iov; + int nvec; + unsigned long buflen = 0; + + if (!is_smb1(server) && rqst->rq_nvec >= 2 && + rqst->rq_iov[0].iov_len == 4) { + iov = &rqst->rq_iov[1]; + nvec = rqst->rq_nvec - 1; + } else { + iov = rqst->rq_iov; + nvec = rqst->rq_nvec; + } + + /* total up iov array first */ + for (i = 0; i < nvec; i++) + buflen += iov[i].iov_len; + + /* + * Add in the page array if there is one. The caller needs to make + * sure rq_offset and rq_tailsz are set correctly. If a buffer of + * multiple pages ends at page boundary, rq_tailsz needs to be set to + * PAGE_SIZE. + */ + if (rqst->rq_npages) { + if (rqst->rq_npages == 1) + buflen += rqst->rq_tailsz; + else { + /* + * If there is more than one page, calculate the + * buffer length based on rq_offset and rq_tailsz + */ + buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) - + rqst->rq_offset; + buflen += rqst->rq_tailsz; + } + } + + return buflen; +} + +static int +__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *rqst) +{ + int rc; + struct kvec *iov; + int n_vec; + unsigned int send_length = 0; + unsigned int i, j; + sigset_t mask, oldmask; + size_t total_len = 0, sent, size; + struct socket *ssocket = server->ssocket; + struct msghdr smb_msg = {}; + __be32 rfc1002_marker; + + cifs_in_send_inc(server); + if (cifs_rdma_enabled(server)) { + /* return -EAGAIN when connecting or reconnecting */ + rc = -EAGAIN; + if (server->smbd_conn) + rc = smbd_send(server, num_rqst, rqst); + goto smbd_done; + } + + rc = -EAGAIN; + if (ssocket == NULL) + goto out; + + rc = -ERESTARTSYS; + if (fatal_signal_pending(current)) { + cifs_dbg(FYI, "signal pending before send request\n"); + goto out; + } + + rc = 0; + /* cork the socket */ + tcp_sock_set_cork(ssocket->sk, true); + + for (j = 0; j < num_rqst; j++) + send_length += smb_rqst_len(server, &rqst[j]); + rfc1002_marker = cpu_to_be32(send_length); + + /* + * We should not allow signals to interrupt the network send because + * any partial send will cause session reconnects thus increasing + * latency of system calls and overload a server with unnecessary + * requests. + */ + + sigfillset(&mask); + sigprocmask(SIG_BLOCK, &mask, &oldmask); + + /* Generate a rfc1002 marker for SMB2+ */ + if (!is_smb1(server)) { + struct kvec hiov = { + .iov_base = &rfc1002_marker, + .iov_len = 4 + }; + iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, &hiov, 1, 4); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + goto unmask; + + total_len += sent; + send_length += 4; + } + + cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); + + for (j = 0; j < num_rqst; j++) { + iov = rqst[j].rq_iov; + n_vec = rqst[j].rq_nvec; + + size = 0; + for (i = 0; i < n_vec; i++) { + dump_smb(iov[i].iov_base, iov[i].iov_len); + size += iov[i].iov_len; + } + + iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, iov, n_vec, size); + + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + goto unmask; + + total_len += sent; + + /* now walk the page array and send each page in it */ + for (i = 0; i < rqst[j].rq_npages; i++) { + struct bio_vec bvec; + + bvec.bv_page = rqst[j].rq_pages[i]; + rqst_page_get_length(&rqst[j], i, &bvec.bv_len, + &bvec.bv_offset); + + iov_iter_bvec(&smb_msg.msg_iter, ITER_SOURCE, + &bvec, 1, bvec.bv_len); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + break; + + total_len += sent; + } + } + +unmask: + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + /* + * If signal is pending but we have already sent the whole packet to + * the server we need to return success status to allow a corresponding + * mid entry to be kept in the pending requests queue thus allowing + * to handle responses from the server by the client. + * + * If only part of the packet has been sent there is no need to hide + * interrupt because the session will be reconnected anyway, so there + * won't be any response from the server to handle. + */ + + if (signal_pending(current) && (total_len != send_length)) { + cifs_dbg(FYI, "signal is pending after attempt to send\n"); + rc = -ERESTARTSYS; + } + + /* uncork it */ + tcp_sock_set_cork(ssocket->sk, false); + + if ((total_len > 0) && (total_len != send_length)) { + cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n", + send_length, total_len); + /* + * If we have only sent part of an SMB then the next SMB could + * be taken as the remainder of this one. We need to kill the + * socket so the server throws away the partial SMB + */ + cifs_signal_cifsd_for_reconnect(server, false); + trace_smb3_partial_send_reconnect(server->CurrentMid, + server->conn_id, server->hostname); + } +smbd_done: + if (rc < 0 && rc != -EINTR) + cifs_server_dbg(VFS, "Error %d sending data on socket to server\n", + rc); + else if (rc > 0) + rc = 0; +out: + cifs_in_send_dec(server); + return rc; +} + +static int +smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *rqst, int flags) +{ + struct kvec iov; + struct smb2_transform_hdr *tr_hdr; + struct smb_rqst cur_rqst[MAX_COMPOUND]; + int rc; + + if (!(flags & CIFS_TRANSFORM_REQ)) + return __smb_send_rqst(server, num_rqst, rqst); + + if (num_rqst > MAX_COMPOUND - 1) + return -ENOMEM; + + if (!server->ops->init_transform_rq) { + cifs_server_dbg(VFS, "Encryption requested but transform callback is missing\n"); + return -EIO; + } + + tr_hdr = kzalloc(sizeof(*tr_hdr), GFP_NOFS); + if (!tr_hdr) + return -ENOMEM; + + memset(&cur_rqst[0], 0, sizeof(cur_rqst)); + memset(&iov, 0, sizeof(iov)); + + iov.iov_base = tr_hdr; + iov.iov_len = sizeof(*tr_hdr); + cur_rqst[0].rq_iov = &iov; + cur_rqst[0].rq_nvec = 1; + + rc = server->ops->init_transform_rq(server, num_rqst + 1, + &cur_rqst[0], rqst); + if (rc) + goto out; + + rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); + smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); +out: + kfree(tr_hdr); + return rc; +} + +int +smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, + unsigned int smb_buf_length) +{ + struct kvec iov[2]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 2 }; + + iov[0].iov_base = smb_buffer; + iov[0].iov_len = 4; + iov[1].iov_base = (char *)smb_buffer + 4; + iov[1].iov_len = smb_buf_length; + + return __smb_send_rqst(server, 1, &rqst); +} + +static int +wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, + const int timeout, const int flags, + unsigned int *instance) +{ + long rc; + int *credits; + int optype; + long int t; + int scredits, in_flight; + + if (timeout < 0) + t = MAX_JIFFY_OFFSET; + else + t = msecs_to_jiffies(timeout); + + optype = flags & CIFS_OP_MASK; + + *instance = 0; + + credits = server->ops->get_credits_field(server, optype); + /* Since an echo is already inflight, no need to wait to send another */ + if (*credits <= 0 && optype == CIFS_ECHO_OP) + return -EAGAIN; + + spin_lock(&server->req_lock); + if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) { + /* oplock breaks must not be held up */ + server->in_flight++; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; + *credits -= 1; + *instance = server->reconnect_instance; + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_nblk_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, -1, in_flight); + cifs_dbg(FYI, "%s: remove %u credits total=%d\n", + __func__, 1, scredits); + + return 0; + } + + while (1) { + if (*credits < num_credits) { + scredits = *credits; + spin_unlock(&server->req_lock); + + cifs_num_waiters_inc(server); + rc = wait_event_killable_timeout(server->request_q, + has_credits(server, credits, num_credits), t); + cifs_num_waiters_dec(server); + if (!rc) { + spin_lock(&server->req_lock); + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_credit_timeout(server->CurrentMid, + server->conn_id, server->hostname, scredits, + num_credits, in_flight); + cifs_server_dbg(VFS, "wait timed out after %d ms\n", + timeout); + return -EBUSY; + } + if (rc == -ERESTARTSYS) + return -ERESTARTSYS; + spin_lock(&server->req_lock); + } else { + spin_unlock(&server->req_lock); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* + * For normal commands, reserve the last MAX_COMPOUND + * credits to compound requests. + * Otherwise these compounds could be permanently + * starved for credits by single-credit requests. + * + * To prevent spinning CPU, block this thread until + * there are >MAX_COMPOUND credits available. + * But only do this is we already have a lot of + * credits in flight to avoid triggering this check + * for servers that are slow to hand out credits on + * new sessions. + */ + spin_lock(&server->req_lock); + if (!optype && num_credits == 1 && + server->in_flight > 2 * MAX_COMPOUND && + *credits <= MAX_COMPOUND) { + spin_unlock(&server->req_lock); + + cifs_num_waiters_inc(server); + rc = wait_event_killable_timeout( + server->request_q, + has_credits(server, credits, + MAX_COMPOUND + 1), + t); + cifs_num_waiters_dec(server); + if (!rc) { + spin_lock(&server->req_lock); + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_credit_timeout( + server->CurrentMid, + server->conn_id, server->hostname, + scredits, num_credits, in_flight); + cifs_server_dbg(VFS, "wait timed out after %d ms\n", + timeout); + return -EBUSY; + } + if (rc == -ERESTARTSYS) + return -ERESTARTSYS; + spin_lock(&server->req_lock); + continue; + } + + /* + * Can not count locking commands against total + * as they are allowed to block on server. + */ + + /* update # of requests on the wire to server */ + if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) { + *credits -= num_credits; + server->in_flight += num_credits; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; + *instance = server->reconnect_instance; + } + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_waitff_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + -(num_credits), in_flight); + cifs_dbg(FYI, "%s: remove %u credits total=%d\n", + __func__, num_credits, scredits); + break; + } + } + return 0; +} + +static int +wait_for_free_request(struct TCP_Server_Info *server, const int flags, + unsigned int *instance) +{ + return wait_for_free_credits(server, 1, -1, flags, + instance); +} + +static int +wait_for_compound_request(struct TCP_Server_Info *server, int num, + const int flags, unsigned int *instance) +{ + int *credits; + int scredits, in_flight; + + credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK); + + spin_lock(&server->req_lock); + scredits = *credits; + in_flight = server->in_flight; + + if (*credits < num) { + /* + * If the server is tight on resources or just gives us less + * credits for other reasons (e.g. requests are coming out of + * order and the server delays granting more credits until it + * processes a missing mid) and we exhausted most available + * credits there may be situations when we try to send + * a compound request but we don't have enough credits. At this + * point the client needs to decide if it should wait for + * additional credits or fail the request. If at least one + * request is in flight there is a high probability that the + * server will return enough credits to satisfy this compound + * request. + * + * Return immediately if no requests in flight since we will be + * stuck on waiting for credits. + */ + if (server->in_flight == 0) { + spin_unlock(&server->req_lock); + trace_smb3_insufficient_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + num, in_flight); + cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n", + __func__, in_flight, num, scredits); + return -EDEADLK; + } + } + spin_unlock(&server->req_lock); + + return wait_for_free_credits(server, num, 60000, flags, + instance); +} + +int +cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, + unsigned int *num, struct cifs_credits *credits) +{ + *num = size; + credits->value = 0; + credits->instance = server->reconnect_instance; + return 0; +} + +static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, + struct mid_q_entry **ppmidQ) +{ + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_NEW) { + if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && + (in_buf->Command != SMB_COM_NEGOTIATE)) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are setting up session */ + } + + if (ses->ses_status == SES_EXITING) { + /* check if SMB session is bad because we are setting it up */ + if (in_buf->Command != SMB_COM_LOGOFF_ANDX) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are shutting down session */ + } + spin_unlock(&ses->ses_lock); + + *ppmidQ = alloc_mid(in_buf, ses->server); + if (*ppmidQ == NULL) + return -ENOMEM; + spin_lock(&ses->server->mid_lock); + list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); + spin_unlock(&ses->server->mid_lock); + return 0; +} + +static int +wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) +{ + int error; + + error = wait_event_state(server->response_q, + midQ->mid_state != MID_REQUEST_SUBMITTED, + (TASK_KILLABLE|TASK_FREEZABLE_UNSAFE)); + if (error < 0) + return -ERESTARTSYS; + + return 0; +} + +struct mid_q_entry * +cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return ERR_PTR(-EIO); + + /* enable signing if server requires it */ + if (server->sign) + hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + mid = alloc_mid(hdr, server); + if (mid == NULL) + return ERR_PTR(-ENOMEM); + + rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); + if (rc) { + release_mid(mid); + return ERR_PTR(rc); + } + + return mid; +} + +/* + * Send a SMB request and set the callback function in the mid to handle + * the result. Caller is responsible for dealing with timeouts. + */ +int +cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, + mid_receive_t *receive, mid_callback_t *callback, + mid_handle_t *handle, void *cbdata, const int flags, + const struct cifs_credits *exist_credits) +{ + int rc; + struct mid_q_entry *mid; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + unsigned int instance; + int optype; + + optype = flags & CIFS_OP_MASK; + + if ((flags & CIFS_HAS_CREDITS) == 0) { + rc = wait_for_free_request(server, flags, &instance); + if (rc) + return rc; + credits.value = 1; + credits.instance = instance; + } else + instance = exist_credits->instance; + + cifs_server_lock(server); + + /* + * We can't use credits obtained from the previous session to send this + * request. Check if there were reconnects after we obtained credits and + * return -EAGAIN in such cases to let callers handle it. + */ + if (instance != server->reconnect_instance) { + cifs_server_unlock(server); + add_credits_and_wake_if(server, &credits, optype); + return -EAGAIN; + } + + mid = server->ops->setup_async_request(server, rqst); + if (IS_ERR(mid)) { + cifs_server_unlock(server); + add_credits_and_wake_if(server, &credits, optype); + return PTR_ERR(mid); + } + + mid->receive = receive; + mid->callback = callback; + mid->callback_data = cbdata; + mid->handle = handle; + mid->mid_state = MID_REQUEST_SUBMITTED; + + /* put it on the pending_mid_q */ + spin_lock(&server->mid_lock); + list_add_tail(&mid->qhead, &server->pending_mid_q); + spin_unlock(&server->mid_lock); + + /* + * Need to store the time in mid before calling I/O. For call_async, + * I/O response may come back and free the mid entry on another thread. + */ + cifs_save_when_sent(mid); + rc = smb_send_rqst(server, 1, rqst, flags); + + if (rc < 0) { + revert_current_mid(server, mid->credits); + server->sequence_number -= 2; + delete_mid(mid); + } + + cifs_server_unlock(server); + + if (rc == 0) + return 0; + + add_credits_and_wake_if(server, &credits, optype); + return rc; +} + +/* + * + * Send an SMB Request. No response info (other than return code) + * needs to be parsed. + * + * flags indicate the type of request buffer and how long to wait + * and whether to log NT STATUS code (error) before mapping it to POSIX error + * + */ +int +SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, + char *in_buf, int flags) +{ + int rc; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + iov[0].iov_base = in_buf; + iov[0].iov_len = get_rfc1002_length(in_buf) + 4; + flags |= CIFS_NO_RSP_BUF; + rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); + cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc); + + return rc; +} + +static int +cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) +{ + int rc = 0; + + cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n", + __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state); + + spin_lock(&server->mid_lock); + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + spin_unlock(&server->mid_lock); + return rc; + case MID_RETRY_NEEDED: + rc = -EAGAIN; + break; + case MID_RESPONSE_MALFORMED: + rc = -EIO; + break; + case MID_SHUTDOWN: + rc = -EHOSTDOWN; + break; + default: + if (!(mid->mid_flags & MID_DELETED)) { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + } + cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", + __func__, mid->mid, mid->mid_state); + rc = -EIO; + } + spin_unlock(&server->mid_lock); + + release_mid(mid); + return rc; +} + +static inline int +send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, + struct mid_q_entry *mid) +{ + return server->ops->send_cancel ? + server->ops->send_cancel(server, rqst, mid) : 0; +} + +int +cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, + bool log_error) +{ + unsigned int len = get_rfc1002_length(mid->resp_buf) + 4; + + dump_smb(mid->resp_buf, min_t(u32, 92, len)); + + /* convert the length into a more usable form */ + if (server->sign) { + struct kvec iov[2]; + int rc = 0; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 2 }; + + iov[0].iov_base = mid->resp_buf; + iov[0].iov_len = 4; + iov[1].iov_base = (char *)mid->resp_buf + 4; + iov[1].iov_len = len - 4; + /* FIXME: add code to kill session */ + rc = cifs_verify_signature(&rqst, server, + mid->sequence_number); + if (rc) + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + + /* BB special case reconnect tid and uid here? */ + return map_and_check_smb_error(mid, log_error); +} + +struct mid_q_entry * +cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored, + struct smb_rqst *rqst) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return ERR_PTR(-EIO); + + rc = allocate_mid(ses, hdr, &mid); + if (rc) + return ERR_PTR(rc); + rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number); + if (rc) { + delete_mid(mid); + return ERR_PTR(rc); + } + return mid; +} + +static void +cifs_compound_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->server; + struct cifs_credits credits; + + credits.value = server->ops->get_credits(mid); + credits.instance = server->reconnect_instance; + + add_credits(server, &credits, mid->optype); +} + +static void +cifs_compound_last_callback(struct mid_q_entry *mid) +{ + cifs_compound_callback(mid); + cifs_wake_up_task(mid); +} + +static void +cifs_cancelled_callback(struct mid_q_entry *mid) +{ + cifs_compound_callback(mid); + release_mid(mid); +} + +/* + * Return a channel (master if none) of @ses that can be used to send + * regular requests. + * + * If we are currently binding a new channel (negprot/sess.setup), + * return the new incomplete channel. + */ +struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) +{ + uint index = 0; + + if (!ses) + return NULL; + + /* round robin */ + index = (uint)atomic_inc_return(&ses->chan_seq); + + spin_lock(&ses->chan_lock); + index %= ses->chan_count; + spin_unlock(&ses->chan_lock); + + return ses->chans[index].server; +} + +int +compound_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const int flags, const int num_rqst, struct smb_rqst *rqst, + int *resp_buf_type, struct kvec *resp_iov) +{ + int i, j, optype, rc = 0; + struct mid_q_entry *midQ[MAX_COMPOUND]; + bool cancelled_mid[MAX_COMPOUND] = {false}; + struct cifs_credits credits[MAX_COMPOUND] = { + { .value = 0, .instance = 0 } + }; + unsigned int instance; + char *buf; + + optype = flags & CIFS_OP_MASK; + + for (i = 0; i < num_rqst; i++) + resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */ + + if (!ses || !ses->server || !server) { + cifs_dbg(VFS, "Null session\n"); + return -EIO; + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* + * Wait for all the requests to become available. + * This approach still leaves the possibility to be stuck waiting for + * credits if the server doesn't grant credits to the outstanding + * requests and if the client is completely idle, not generating any + * other requests. + * This can be handled by the eventual session reconnect. + */ + rc = wait_for_compound_request(server, num_rqst, flags, + &instance); + if (rc) + return rc; + + for (i = 0; i < num_rqst; i++) { + credits[i].value = 1; + credits[i].instance = instance; + } + + /* + * Make sure that we sign in the same order that we send on this socket + * and avoid races inside tcp sendmsg code that could cause corruption + * of smb data. + */ + + cifs_server_lock(server); + + /* + * All the parts of the compound chain belong obtained credits from the + * same session. We can not use credits obtained from the previous + * session to send this request. Check if there were reconnects after + * we obtained credits and return -EAGAIN in such cases to let callers + * handle it. + */ + if (instance != server->reconnect_instance) { + cifs_server_unlock(server); + for (j = 0; j < num_rqst; j++) + add_credits(server, &credits[j], optype); + return -EAGAIN; + } + + for (i = 0; i < num_rqst; i++) { + midQ[i] = server->ops->setup_request(ses, server, &rqst[i]); + if (IS_ERR(midQ[i])) { + revert_current_mid(server, i); + for (j = 0; j < i; j++) + delete_mid(midQ[j]); + cifs_server_unlock(server); + + /* Update # of requests on wire to server */ + for (j = 0; j < num_rqst; j++) + add_credits(server, &credits[j], optype); + return PTR_ERR(midQ[i]); + } + + midQ[i]->mid_state = MID_REQUEST_SUBMITTED; + midQ[i]->optype = optype; + /* + * Invoke callback for every part of the compound chain + * to calculate credits properly. Wake up this thread only when + * the last element is received. + */ + if (i < num_rqst - 1) + midQ[i]->callback = cifs_compound_callback; + else + midQ[i]->callback = cifs_compound_last_callback; + } + rc = smb_send_rqst(server, num_rqst, rqst, flags); + + for (i = 0; i < num_rqst; i++) + cifs_save_when_sent(midQ[i]); + + if (rc < 0) { + revert_current_mid(server, num_rqst); + server->sequence_number -= 2; + } + + cifs_server_unlock(server); + + /* + * If sending failed for some reason or it is an oplock break that we + * will not receive a response to - return credits back + */ + if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) { + for (i = 0; i < num_rqst; i++) + add_credits(server, &credits[i], optype); + goto out; + } + + /* + * At this point the request is passed to the network stack - we assume + * that any credits taken from the server structure on the client have + * been spent and we can't return them back. Once we receive responses + * we will collect credits granted by the server in the mid callbacks + * and add those credits to the server structure. + */ + + /* + * Compounding is never used during session establish. + */ + spin_lock(&ses->ses_lock); + if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { + spin_unlock(&ses->ses_lock); + + cifs_server_lock(server); + smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec); + cifs_server_unlock(server); + + spin_lock(&ses->ses_lock); + } + spin_unlock(&ses->ses_lock); + + for (i = 0; i < num_rqst; i++) { + rc = wait_for_response(server, midQ[i]); + if (rc != 0) + break; + } + if (rc != 0) { + for (; i < num_rqst; i++) { + cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n", + midQ[i]->mid, le16_to_cpu(midQ[i]->command)); + send_cancel(server, &rqst[i], midQ[i]); + spin_lock(&server->mid_lock); + midQ[i]->mid_flags |= MID_WAIT_CANCELLED; + if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { + midQ[i]->callback = cifs_cancelled_callback; + cancelled_mid[i] = true; + credits[i].value = 0; + } + spin_unlock(&server->mid_lock); + } + } + + for (i = 0; i < num_rqst; i++) { + if (rc < 0) + goto out; + + rc = cifs_sync_mid_result(midQ[i], server); + if (rc != 0) { + /* mark this mid as cancelled to not free it below */ + cancelled_mid[i] = true; + goto out; + } + + if (!midQ[i]->resp_buf || + midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { + rc = -EIO; + cifs_dbg(FYI, "Bad MID state?\n"); + goto out; + } + + buf = (char *)midQ[i]->resp_buf; + resp_iov[i].iov_base = buf; + resp_iov[i].iov_len = midQ[i]->resp_buf_size + + HEADER_PREAMBLE_SIZE(server); + + if (midQ[i]->large_buf) + resp_buf_type[i] = CIFS_LARGE_BUFFER; + else + resp_buf_type[i] = CIFS_SMALL_BUFFER; + + rc = server->ops->check_receive(midQ[i], server, + flags & CIFS_LOG_ERROR); + + /* mark it so buf will not be freed by delete_mid */ + if ((flags & CIFS_NO_RSP_BUF) == 0) + midQ[i]->resp_buf = NULL; + + } + + /* + * Compounding is never used during session establish. + */ + spin_lock(&ses->ses_lock); + if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { + struct kvec iov = { + .iov_base = resp_iov[0].iov_base, + .iov_len = resp_iov[0].iov_len + }; + spin_unlock(&ses->ses_lock); + cifs_server_lock(server); + smb311_update_preauth_hash(ses, server, &iov, 1); + cifs_server_unlock(server); + spin_lock(&ses->ses_lock); + } + spin_unlock(&ses->ses_lock); + +out: + /* + * This will dequeue all mids. After this it is important that the + * demultiplex_thread will not process any of these mids any futher. + * This is prevented above by using a noop callback that will not + * wake this thread except for the very last PDU. + */ + for (i = 0; i < num_rqst; i++) { + if (!cancelled_mid[i]) + delete_mid(midQ[i]); + } + + return rc; +} + +int +cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, int *resp_buf_type, const int flags, + struct kvec *resp_iov) +{ + return compound_send_recv(xid, ses, server, flags, 1, + rqst, resp_buf_type, resp_iov); +} + +int +SendReceive2(const unsigned int xid, struct cifs_ses *ses, + struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, + const int flags, struct kvec *resp_iov) +{ + struct smb_rqst rqst; + struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov; + int rc; + + if (n_vec + 1 > CIFS_MAX_IOV_SIZE) { + new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec), + GFP_KERNEL); + if (!new_iov) { + /* otherwise cifs_send_recv below sets resp_buf_type */ + *resp_buf_type = CIFS_NO_BUFFER; + return -ENOMEM; + } + } else + new_iov = s_iov; + + /* 1st iov is a RFC1001 length followed by the rest of the packet */ + memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec)); + + new_iov[0].iov_base = new_iov[1].iov_base; + new_iov[0].iov_len = 4; + new_iov[1].iov_base += 4; + new_iov[1].iov_len -= 4; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = new_iov; + rqst.rq_nvec = n_vec + 1; + + rc = cifs_send_recv(xid, ses, ses->server, + &rqst, resp_buf_type, flags, resp_iov); + if (n_vec + 1 > CIFS_MAX_IOV_SIZE) + kfree(new_iov); + return rc; +} + +int +SendReceive(const unsigned int xid, struct cifs_ses *ses, + struct smb_hdr *in_buf, struct smb_hdr *out_buf, + int *pbytes_returned, const int flags) +{ + int rc = 0; + struct mid_q_entry *midQ; + unsigned int len = be32_to_cpu(in_buf->smb_buf_length); + struct kvec iov = { .iov_base = in_buf, .iov_len = len }; + struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + struct TCP_Server_Info *server; + + if (ses == NULL) { + cifs_dbg(VFS, "Null smb session\n"); + return -EIO; + } + server = ses->server; + if (server == NULL) { + cifs_dbg(VFS, "Null tcp session\n"); + return -EIO; + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", + len); + return -EIO; + } + + rc = wait_for_free_request(server, flags, &credits.instance); + if (rc) + return rc; + + /* make sure that we sign in the same order that we send on this socket + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ + + cifs_server_lock(server); + + rc = allocate_mid(ses, in_buf, &midQ); + if (rc) { + cifs_server_unlock(server); + /* Update # of requests on wire to server */ + add_credits(server, &credits, 0); + return rc; + } + + rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); + if (rc) { + cifs_server_unlock(server); + goto out; + } + + midQ->mid_state = MID_REQUEST_SUBMITTED; + + rc = smb_send(server, in_buf, len); + cifs_save_when_sent(midQ); + + if (rc < 0) + server->sequence_number -= 2; + + cifs_server_unlock(server); + + if (rc < 0) + goto out; + + rc = wait_for_response(server, midQ); + if (rc != 0) { + send_cancel(server, &rqst, midQ); + spin_lock(&server->mid_lock); + if (midQ->mid_state == MID_REQUEST_SUBMITTED) { + /* no longer considered to be "in-flight" */ + midQ->callback = release_mid; + spin_unlock(&server->mid_lock); + add_credits(server, &credits, 0); + return rc; + } + spin_unlock(&server->mid_lock); + } + + rc = cifs_sync_mid_result(midQ, server); + if (rc != 0) { + add_credits(server, &credits, 0); + return rc; + } + + if (!midQ->resp_buf || !out_buf || + midQ->mid_state != MID_RESPONSE_RECEIVED) { + rc = -EIO; + cifs_server_dbg(VFS, "Bad MID state?\n"); + goto out; + } + + *pbytes_returned = get_rfc1002_length(midQ->resp_buf); + memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); + rc = cifs_check_receive(midQ, server, 0); +out: + delete_mid(midQ); + add_credits(server, &credits, 0); + + return rc; +} + +/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows + blocking lock to return. */ + +static int +send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon, + struct smb_hdr *in_buf, + struct smb_hdr *out_buf) +{ + int bytes_returned; + struct cifs_ses *ses = tcon->ses; + LOCK_REQ *pSMB = (LOCK_REQ *)in_buf; + + /* We just modify the current in_buf to change + the type of lock from LOCKING_ANDX_SHARED_LOCK + or LOCKING_ANDX_EXCLUSIVE_LOCK to + LOCKING_ANDX_CANCEL_LOCK. */ + + pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES; + pSMB->Timeout = 0; + pSMB->hdr.Mid = get_next_mid(ses->server); + + return SendReceive(xid, ses, in_buf, out_buf, + &bytes_returned, 0); +} + +int +SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, + struct smb_hdr *in_buf, struct smb_hdr *out_buf, + int *pbytes_returned) +{ + int rc = 0; + int rstart = 0; + struct mid_q_entry *midQ; + struct cifs_ses *ses; + unsigned int len = be32_to_cpu(in_buf->smb_buf_length); + struct kvec iov = { .iov_base = in_buf, .iov_len = len }; + struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; + unsigned int instance; + struct TCP_Server_Info *server; + + if (tcon == NULL || tcon->ses == NULL) { + cifs_dbg(VFS, "Null smb session\n"); + return -EIO; + } + ses = tcon->ses; + server = ses->server; + + if (server == NULL) { + cifs_dbg(VFS, "Null tcp session\n"); + return -EIO; + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", + len); + return -EIO; + } + + rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance); + if (rc) + return rc; + + /* make sure that we sign in the same order that we send on this socket + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ + + cifs_server_lock(server); + + rc = allocate_mid(ses, in_buf, &midQ); + if (rc) { + cifs_server_unlock(server); + return rc; + } + + rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); + if (rc) { + delete_mid(midQ); + cifs_server_unlock(server); + return rc; + } + + midQ->mid_state = MID_REQUEST_SUBMITTED; + rc = smb_send(server, in_buf, len); + cifs_save_when_sent(midQ); + + if (rc < 0) + server->sequence_number -= 2; + + cifs_server_unlock(server); + + if (rc < 0) { + delete_mid(midQ); + return rc; + } + + /* Wait for a reply - allow signals to interrupt. */ + rc = wait_event_interruptible(server->response_q, + (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || + ((server->tcpStatus != CifsGood) && + (server->tcpStatus != CifsNew))); + + /* Were we interrupted by a signal ? */ + spin_lock(&server->srv_lock); + if ((rc == -ERESTARTSYS) && + (midQ->mid_state == MID_REQUEST_SUBMITTED) && + ((server->tcpStatus == CifsGood) || + (server->tcpStatus == CifsNew))) { + spin_unlock(&server->srv_lock); + + if (in_buf->Command == SMB_COM_TRANSACTION2) { + /* POSIX lock. We send a NT_CANCEL SMB to cause the + blocking lock to return. */ + rc = send_cancel(server, &rqst, midQ); + if (rc) { + delete_mid(midQ); + return rc; + } + } else { + /* Windows lock. We send a LOCKINGX_CANCEL_LOCK + to cause the blocking lock to return. */ + + rc = send_lock_cancel(xid, tcon, in_buf, out_buf); + + /* If we get -ENOLCK back the lock may have + already been removed. Don't exit in this case. */ + if (rc && rc != -ENOLCK) { + delete_mid(midQ); + return rc; + } + } + + rc = wait_for_response(server, midQ); + if (rc) { + send_cancel(server, &rqst, midQ); + spin_lock(&server->mid_lock); + if (midQ->mid_state == MID_REQUEST_SUBMITTED) { + /* no longer considered to be "in-flight" */ + midQ->callback = release_mid; + spin_unlock(&server->mid_lock); + return rc; + } + spin_unlock(&server->mid_lock); + } + + /* We got the response - restart system call. */ + rstart = 1; + spin_lock(&server->srv_lock); + } + spin_unlock(&server->srv_lock); + + rc = cifs_sync_mid_result(midQ, server); + if (rc != 0) + return rc; + + /* rcvd frame is ok */ + if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { + rc = -EIO; + cifs_tcon_dbg(VFS, "Bad MID state?\n"); + goto out; + } + + *pbytes_returned = get_rfc1002_length(midQ->resp_buf); + memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); + rc = cifs_check_receive(midQ, server, 0); +out: + delete_mid(midQ); + if (rstart && rc == -EACCES) + return -ERESTARTSYS; + return rc; +} + +/* + * Discard any remaining data in the current SMB. To do this, we borrow the + * current bigbuf. + */ +int +cifs_discard_remaining_data(struct TCP_Server_Info *server) +{ + unsigned int rfclen = server->pdu_size; + int remaining = rfclen + HEADER_PREAMBLE_SIZE(server) - + server->total_read; + + while (remaining > 0) { + int length; + + length = cifs_discard_from_socket(server, + min_t(size_t, remaining, + CIFSMaxBufSize + MAX_HEADER_SIZE(server))); + if (length < 0) + return length; + server->total_read += length; + remaining -= length; + } + + return 0; +} + +static int +__cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid, + bool malformed) +{ + int length; + + length = cifs_discard_remaining_data(server); + dequeue_mid(mid, malformed); + mid->resp_buf = server->smallbuf; + server->smallbuf = NULL; + return length; +} + +static int +cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + struct cifs_readdata *rdata = mid->callback_data; + + return __cifs_readv_discard(server, mid, rdata->result); +} + +int +cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + int length, len; + unsigned int data_offset, data_len; + struct cifs_readdata *rdata = mid->callback_data; + char *buf = server->smallbuf; + unsigned int buflen = server->pdu_size + HEADER_PREAMBLE_SIZE(server); + bool use_rdma_mr = false; + + cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n", + __func__, mid->mid, rdata->offset, rdata->bytes); + + /* + * read the rest of READ_RSP header (sans Data array), or whatever we + * can if there's not enough data. At this point, we've read down to + * the Mid. + */ + len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - + HEADER_SIZE(server) + 1; + + length = cifs_read_from_socket(server, + buf + HEADER_SIZE(server) - 1, len); + if (length < 0) + return length; + server->total_read += length; + + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + cifs_reconnect(server, true); + return -1; + } + + if (server->ops->is_status_pending && + server->ops->is_status_pending(buf, server)) { + cifs_discard_remaining_data(server); + return -1; + } + + /* set up first two iov for signature check and to get credits */ + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = HEADER_PREAMBLE_SIZE(server); + rdata->iov[1].iov_base = buf + HEADER_PREAMBLE_SIZE(server); + rdata->iov[1].iov_len = + server->total_read - HEADER_PREAMBLE_SIZE(server); + cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", + rdata->iov[0].iov_base, rdata->iov[0].iov_len); + cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", + rdata->iov[1].iov_base, rdata->iov[1].iov_len); + + /* Was the SMB read successful? */ + rdata->result = server->ops->map_error(buf, false); + if (rdata->result != 0) { + cifs_dbg(FYI, "%s: server returned error %d\n", + __func__, rdata->result); + /* normal error on read response */ + return __cifs_readv_discard(server, mid, false); + } + + /* Is there enough to get to the rest of the READ_RSP header? */ + if (server->total_read < server->vals->read_rsp_size) { + cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n", + __func__, server->total_read, + server->vals->read_rsp_size); + rdata->result = -EIO; + return cifs_readv_discard(server, mid); + } + + data_offset = server->ops->read_data_offset(buf) + + HEADER_PREAMBLE_SIZE(server); + if (data_offset < server->total_read) { + /* + * win2k8 sometimes sends an offset of 0 when the read + * is beyond the EOF. Treat it as if the data starts just after + * the header. + */ + cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", + __func__, data_offset); + data_offset = server->total_read; + } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { + /* data_offset is beyond the end of smallbuf */ + cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", + __func__, data_offset); + rdata->result = -EIO; + return cifs_readv_discard(server, mid); + } + + cifs_dbg(FYI, "%s: total_read=%u data_offset=%u\n", + __func__, server->total_read, data_offset); + + len = data_offset - server->total_read; + if (len > 0) { + /* read any junk before data into the rest of smallbuf */ + length = cifs_read_from_socket(server, + buf + server->total_read, len); + if (length < 0) + return length; + server->total_read += length; + } + + /* how much data is in the response? */ +#ifdef CONFIG_CIFS_SMB_DIRECT + use_rdma_mr = rdata->mr; +#endif + data_len = server->ops->read_data_length(buf, use_rdma_mr); + if (!use_rdma_mr && (data_offset + data_len > buflen)) { + /* data_len is corrupt -- discard frame */ + rdata->result = -EIO; + return cifs_readv_discard(server, mid); + } + + length = rdata->read_into_pages(server, rdata, data_len); + if (length < 0) + return length; + + server->total_read += length; + + cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n", + server->total_read, buflen, data_len); + + /* discard anything left over */ + if (server->total_read < buflen) + return cifs_readv_discard(server, mid); + + dequeue_mid(mid, false); + mid->resp_buf = server->smallbuf; + server->smallbuf = NULL; + return length; +} diff --git a/fs/smb/client/unc.c b/fs/smb/client/unc.c new file mode 100644 index 000000000000..f6fc5e343ea4 --- /dev/null +++ b/fs/smb/client/unc.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020, Microsoft Corporation. + * + * Author(s): Steve French + * Suresh Jayaraman + * Jeff Layton + */ + +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" + +/* extract the host portion of the UNC string */ +char *extract_hostname(const char *unc) +{ + const char *src; + char *dst, *delim; + unsigned int len; + + /* skip double chars at beginning of string */ + /* BB: check validity of these bytes? */ + if (strlen(unc) < 3) + return ERR_PTR(-EINVAL); + for (src = unc; *src && *src == '\\'; src++) + ; + if (!*src) + return ERR_PTR(-EINVAL); + + /* delimiter between hostname and sharename is always '\\' now */ + delim = strchr(src, '\\'); + if (!delim) + return ERR_PTR(-EINVAL); + + len = delim - src; + dst = kmalloc((len + 1), GFP_KERNEL); + if (dst == NULL) + return ERR_PTR(-ENOMEM); + + memcpy(dst, src, len); + dst[len] = '\0'; + + return dst; +} + +char *extract_sharename(const char *unc) +{ + const char *src; + char *delim, *dst; + + /* skip double chars at the beginning */ + src = unc + 2; + + /* share name is always preceded by '\\' now */ + delim = strchr(src, '\\'); + if (!delim) + return ERR_PTR(-EINVAL); + delim++; + + /* caller has to free the memory */ + dst = kstrdup(delim, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + + return dst; +} diff --git a/fs/smb/client/winucase.c b/fs/smb/client/winucase.c new file mode 100644 index 000000000000..2f075b5b50df --- /dev/null +++ b/fs/smb/client/winucase.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (c) Jeffrey Layton , 2013 + * + * The const tables in this file were converted from the following info + * provided by Microsoft: + * + * 3.1.5.3 Mapping UTF-16 Strings to Upper Case: + * + * https://msdn.microsoft.com/en-us/library/hh877830.aspx + * http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=10921 + * + * In particular, the table in "Windows 8 Upper Case Mapping Table.txt" was + * post-processed using the winucase_convert.pl script. + */ + +#include + +wchar_t cifs_toupper(wchar_t in); /* quiet sparse */ + +static const wchar_t t2_00[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0000, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178, +}; + +static const wchar_t t2_01[256] = { + 0x0000, 0x0100, 0x0000, 0x0102, 0x0000, 0x0104, 0x0000, 0x0106, + 0x0000, 0x0108, 0x0000, 0x010a, 0x0000, 0x010c, 0x0000, 0x010e, + 0x0000, 0x0110, 0x0000, 0x0112, 0x0000, 0x0114, 0x0000, 0x0116, + 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x011c, 0x0000, 0x011e, + 0x0000, 0x0120, 0x0000, 0x0122, 0x0000, 0x0124, 0x0000, 0x0126, + 0x0000, 0x0128, 0x0000, 0x012a, 0x0000, 0x012c, 0x0000, 0x012e, + 0x0000, 0x0000, 0x0000, 0x0132, 0x0000, 0x0134, 0x0000, 0x0136, + 0x0000, 0x0000, 0x0139, 0x0000, 0x013b, 0x0000, 0x013d, 0x0000, + 0x013f, 0x0000, 0x0141, 0x0000, 0x0143, 0x0000, 0x0145, 0x0000, + 0x0147, 0x0000, 0x0000, 0x014a, 0x0000, 0x014c, 0x0000, 0x014e, + 0x0000, 0x0150, 0x0000, 0x0152, 0x0000, 0x0154, 0x0000, 0x0156, + 0x0000, 0x0158, 0x0000, 0x015a, 0x0000, 0x015c, 0x0000, 0x015e, + 0x0000, 0x0160, 0x0000, 0x0162, 0x0000, 0x0164, 0x0000, 0x0166, + 0x0000, 0x0168, 0x0000, 0x016a, 0x0000, 0x016c, 0x0000, 0x016e, + 0x0000, 0x0170, 0x0000, 0x0172, 0x0000, 0x0174, 0x0000, 0x0176, + 0x0000, 0x0000, 0x0179, 0x0000, 0x017b, 0x0000, 0x017d, 0x0000, + 0x0243, 0x0000, 0x0000, 0x0182, 0x0000, 0x0184, 0x0000, 0x0000, + 0x0187, 0x0000, 0x0000, 0x0000, 0x018b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0191, 0x0000, 0x0000, 0x01f6, 0x0000, 0x0000, + 0x0000, 0x0198, 0x023d, 0x0000, 0x0000, 0x0000, 0x0220, 0x0000, + 0x0000, 0x01a0, 0x0000, 0x01a2, 0x0000, 0x01a4, 0x0000, 0x0000, + 0x01a7, 0x0000, 0x0000, 0x0000, 0x0000, 0x01ac, 0x0000, 0x0000, + 0x01af, 0x0000, 0x0000, 0x0000, 0x01b3, 0x0000, 0x01b5, 0x0000, + 0x0000, 0x01b8, 0x0000, 0x0000, 0x0000, 0x01bc, 0x0000, 0x01f7, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01c4, 0x0000, + 0x0000, 0x01c7, 0x0000, 0x0000, 0x01ca, 0x0000, 0x01cd, 0x0000, + 0x01cf, 0x0000, 0x01d1, 0x0000, 0x01d3, 0x0000, 0x01d5, 0x0000, + 0x01d7, 0x0000, 0x01d9, 0x0000, 0x01db, 0x018e, 0x0000, 0x01de, + 0x0000, 0x01e0, 0x0000, 0x01e2, 0x0000, 0x01e4, 0x0000, 0x01e6, + 0x0000, 0x01e8, 0x0000, 0x01ea, 0x0000, 0x01ec, 0x0000, 0x01ee, + 0x0000, 0x0000, 0x0000, 0x01f1, 0x0000, 0x01f4, 0x0000, 0x0000, + 0x0000, 0x01f8, 0x0000, 0x01fa, 0x0000, 0x01fc, 0x0000, 0x01fe, +}; + +static const wchar_t t2_02[256] = { + 0x0000, 0x0200, 0x0000, 0x0202, 0x0000, 0x0204, 0x0000, 0x0206, + 0x0000, 0x0208, 0x0000, 0x020a, 0x0000, 0x020c, 0x0000, 0x020e, + 0x0000, 0x0210, 0x0000, 0x0212, 0x0000, 0x0214, 0x0000, 0x0216, + 0x0000, 0x0218, 0x0000, 0x021a, 0x0000, 0x021c, 0x0000, 0x021e, + 0x0000, 0x0000, 0x0000, 0x0222, 0x0000, 0x0224, 0x0000, 0x0226, + 0x0000, 0x0228, 0x0000, 0x022a, 0x0000, 0x022c, 0x0000, 0x022e, + 0x0000, 0x0230, 0x0000, 0x0232, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x023b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0241, 0x0000, 0x0000, 0x0000, 0x0000, 0x0246, + 0x0000, 0x0248, 0x0000, 0x024a, 0x0000, 0x024c, 0x0000, 0x024e, + 0x2c6f, 0x2c6d, 0x0000, 0x0181, 0x0186, 0x0000, 0x0189, 0x018a, + 0x0000, 0x018f, 0x0000, 0x0190, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0193, 0x0000, 0x0000, 0x0194, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0197, 0x0196, 0x0000, 0x2c62, 0x0000, 0x0000, 0x0000, 0x019c, + 0x0000, 0x2c6e, 0x019d, 0x0000, 0x0000, 0x019f, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2c64, 0x0000, 0x0000, + 0x01a6, 0x0000, 0x0000, 0x01a9, 0x0000, 0x0000, 0x0000, 0x0000, + 0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01b7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_03[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0370, 0x0000, 0x0372, 0x0000, 0x0000, 0x0000, 0x0376, + 0x0000, 0x0000, 0x0000, 0x03fd, 0x03fe, 0x03ff, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, + 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, + 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03cf, + 0x0000, 0x03d8, 0x0000, 0x03da, 0x0000, 0x03dc, 0x0000, 0x03de, + 0x0000, 0x03e0, 0x0000, 0x03e2, 0x0000, 0x03e4, 0x0000, 0x03e6, + 0x0000, 0x03e8, 0x0000, 0x03ea, 0x0000, 0x03ec, 0x0000, 0x03ee, + 0x0000, 0x0000, 0x03f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x03f7, 0x0000, 0x0000, 0x03fa, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_04[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, + 0x0000, 0x0460, 0x0000, 0x0462, 0x0000, 0x0464, 0x0000, 0x0466, + 0x0000, 0x0468, 0x0000, 0x046a, 0x0000, 0x046c, 0x0000, 0x046e, + 0x0000, 0x0470, 0x0000, 0x0472, 0x0000, 0x0474, 0x0000, 0x0476, + 0x0000, 0x0478, 0x0000, 0x047a, 0x0000, 0x047c, 0x0000, 0x047e, + 0x0000, 0x0480, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x048a, 0x0000, 0x048c, 0x0000, 0x048e, + 0x0000, 0x0490, 0x0000, 0x0492, 0x0000, 0x0494, 0x0000, 0x0496, + 0x0000, 0x0498, 0x0000, 0x049a, 0x0000, 0x049c, 0x0000, 0x049e, + 0x0000, 0x04a0, 0x0000, 0x04a2, 0x0000, 0x04a4, 0x0000, 0x04a6, + 0x0000, 0x04a8, 0x0000, 0x04aa, 0x0000, 0x04ac, 0x0000, 0x04ae, + 0x0000, 0x04b0, 0x0000, 0x04b2, 0x0000, 0x04b4, 0x0000, 0x04b6, + 0x0000, 0x04b8, 0x0000, 0x04ba, 0x0000, 0x04bc, 0x0000, 0x04be, + 0x0000, 0x0000, 0x04c1, 0x0000, 0x04c3, 0x0000, 0x04c5, 0x0000, + 0x04c7, 0x0000, 0x04c9, 0x0000, 0x04cb, 0x0000, 0x04cd, 0x04c0, + 0x0000, 0x04d0, 0x0000, 0x04d2, 0x0000, 0x04d4, 0x0000, 0x04d6, + 0x0000, 0x04d8, 0x0000, 0x04da, 0x0000, 0x04dc, 0x0000, 0x04de, + 0x0000, 0x04e0, 0x0000, 0x04e2, 0x0000, 0x04e4, 0x0000, 0x04e6, + 0x0000, 0x04e8, 0x0000, 0x04ea, 0x0000, 0x04ec, 0x0000, 0x04ee, + 0x0000, 0x04f0, 0x0000, 0x04f2, 0x0000, 0x04f4, 0x0000, 0x04f6, + 0x0000, 0x04f8, 0x0000, 0x04fa, 0x0000, 0x04fc, 0x0000, 0x04fe, +}; + +static const wchar_t t2_05[256] = { + 0x0000, 0x0500, 0x0000, 0x0502, 0x0000, 0x0504, 0x0000, 0x0506, + 0x0000, 0x0508, 0x0000, 0x050a, 0x0000, 0x050c, 0x0000, 0x050e, + 0x0000, 0x0510, 0x0000, 0x0512, 0x0000, 0x0514, 0x0000, 0x0516, + 0x0000, 0x0518, 0x0000, 0x051a, 0x0000, 0x051c, 0x0000, 0x051e, + 0x0000, 0x0520, 0x0000, 0x0522, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, + 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, + 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, + 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, + 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_1d[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xa77d, 0x0000, 0x0000, 0x0000, 0x2c63, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_1e[256] = { + 0x0000, 0x1e00, 0x0000, 0x1e02, 0x0000, 0x1e04, 0x0000, 0x1e06, + 0x0000, 0x1e08, 0x0000, 0x1e0a, 0x0000, 0x1e0c, 0x0000, 0x1e0e, + 0x0000, 0x1e10, 0x0000, 0x1e12, 0x0000, 0x1e14, 0x0000, 0x1e16, + 0x0000, 0x1e18, 0x0000, 0x1e1a, 0x0000, 0x1e1c, 0x0000, 0x1e1e, + 0x0000, 0x1e20, 0x0000, 0x1e22, 0x0000, 0x1e24, 0x0000, 0x1e26, + 0x0000, 0x1e28, 0x0000, 0x1e2a, 0x0000, 0x1e2c, 0x0000, 0x1e2e, + 0x0000, 0x1e30, 0x0000, 0x1e32, 0x0000, 0x1e34, 0x0000, 0x1e36, + 0x0000, 0x1e38, 0x0000, 0x1e3a, 0x0000, 0x1e3c, 0x0000, 0x1e3e, + 0x0000, 0x1e40, 0x0000, 0x1e42, 0x0000, 0x1e44, 0x0000, 0x1e46, + 0x0000, 0x1e48, 0x0000, 0x1e4a, 0x0000, 0x1e4c, 0x0000, 0x1e4e, + 0x0000, 0x1e50, 0x0000, 0x1e52, 0x0000, 0x1e54, 0x0000, 0x1e56, + 0x0000, 0x1e58, 0x0000, 0x1e5a, 0x0000, 0x1e5c, 0x0000, 0x1e5e, + 0x0000, 0x1e60, 0x0000, 0x1e62, 0x0000, 0x1e64, 0x0000, 0x1e66, + 0x0000, 0x1e68, 0x0000, 0x1e6a, 0x0000, 0x1e6c, 0x0000, 0x1e6e, + 0x0000, 0x1e70, 0x0000, 0x1e72, 0x0000, 0x1e74, 0x0000, 0x1e76, + 0x0000, 0x1e78, 0x0000, 0x1e7a, 0x0000, 0x1e7c, 0x0000, 0x1e7e, + 0x0000, 0x1e80, 0x0000, 0x1e82, 0x0000, 0x1e84, 0x0000, 0x1e86, + 0x0000, 0x1e88, 0x0000, 0x1e8a, 0x0000, 0x1e8c, 0x0000, 0x1e8e, + 0x0000, 0x1e90, 0x0000, 0x1e92, 0x0000, 0x1e94, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x1ea0, 0x0000, 0x1ea2, 0x0000, 0x1ea4, 0x0000, 0x1ea6, + 0x0000, 0x1ea8, 0x0000, 0x1eaa, 0x0000, 0x1eac, 0x0000, 0x1eae, + 0x0000, 0x1eb0, 0x0000, 0x1eb2, 0x0000, 0x1eb4, 0x0000, 0x1eb6, + 0x0000, 0x1eb8, 0x0000, 0x1eba, 0x0000, 0x1ebc, 0x0000, 0x1ebe, + 0x0000, 0x1ec0, 0x0000, 0x1ec2, 0x0000, 0x1ec4, 0x0000, 0x1ec6, + 0x0000, 0x1ec8, 0x0000, 0x1eca, 0x0000, 0x1ecc, 0x0000, 0x1ece, + 0x0000, 0x1ed0, 0x0000, 0x1ed2, 0x0000, 0x1ed4, 0x0000, 0x1ed6, + 0x0000, 0x1ed8, 0x0000, 0x1eda, 0x0000, 0x1edc, 0x0000, 0x1ede, + 0x0000, 0x1ee0, 0x0000, 0x1ee2, 0x0000, 0x1ee4, 0x0000, 0x1ee6, + 0x0000, 0x1ee8, 0x0000, 0x1eea, 0x0000, 0x1eec, 0x0000, 0x1eee, + 0x0000, 0x1ef0, 0x0000, 0x1ef2, 0x0000, 0x1ef4, 0x0000, 0x1ef6, + 0x0000, 0x1ef8, 0x0000, 0x1efa, 0x0000, 0x1efc, 0x0000, 0x1efe, +}; + +static const wchar_t t2_1f[256] = { + 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x1f59, 0x0000, 0x1f5b, 0x0000, 0x1f5d, 0x0000, 0x1f5f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, + 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x0000, 0x0000, + 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fb8, 0x1fb9, 0x0000, 0x1fbc, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1fcc, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fd8, 0x1fd9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fe8, 0x1fe9, 0x0000, 0x0000, 0x0000, 0x1fec, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1ffc, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_21[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2132, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, + 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x2183, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_24[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, + 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, + 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, + 0x24ce, 0x24cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_2c[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, + 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, + 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, + 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, + 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, + 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x0000, + 0x0000, 0x2c60, 0x0000, 0x0000, 0x0000, 0x023a, 0x023e, 0x0000, + 0x2c67, 0x0000, 0x2c69, 0x0000, 0x2c6b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x2c72, 0x0000, 0x0000, 0x2c75, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2c80, 0x0000, 0x2c82, 0x0000, 0x2c84, 0x0000, 0x2c86, + 0x0000, 0x2c88, 0x0000, 0x2c8a, 0x0000, 0x2c8c, 0x0000, 0x2c8e, + 0x0000, 0x2c90, 0x0000, 0x2c92, 0x0000, 0x2c94, 0x0000, 0x2c96, + 0x0000, 0x2c98, 0x0000, 0x2c9a, 0x0000, 0x2c9c, 0x0000, 0x2c9e, + 0x0000, 0x2ca0, 0x0000, 0x2ca2, 0x0000, 0x2ca4, 0x0000, 0x2ca6, + 0x0000, 0x2ca8, 0x0000, 0x2caa, 0x0000, 0x2cac, 0x0000, 0x2cae, + 0x0000, 0x2cb0, 0x0000, 0x2cb2, 0x0000, 0x2cb4, 0x0000, 0x2cb6, + 0x0000, 0x2cb8, 0x0000, 0x2cba, 0x0000, 0x2cbc, 0x0000, 0x2cbe, + 0x0000, 0x2cc0, 0x0000, 0x2cc2, 0x0000, 0x2cc4, 0x0000, 0x2cc6, + 0x0000, 0x2cc8, 0x0000, 0x2cca, 0x0000, 0x2ccc, 0x0000, 0x2cce, + 0x0000, 0x2cd0, 0x0000, 0x2cd2, 0x0000, 0x2cd4, 0x0000, 0x2cd6, + 0x0000, 0x2cd8, 0x0000, 0x2cda, 0x0000, 0x2cdc, 0x0000, 0x2cde, + 0x0000, 0x2ce0, 0x0000, 0x2ce2, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_2d[256] = { + 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, + 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, + 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, + 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, + 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_a6[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xa640, 0x0000, 0xa642, 0x0000, 0xa644, 0x0000, 0xa646, + 0x0000, 0xa648, 0x0000, 0xa64a, 0x0000, 0xa64c, 0x0000, 0xa64e, + 0x0000, 0xa650, 0x0000, 0xa652, 0x0000, 0xa654, 0x0000, 0xa656, + 0x0000, 0xa658, 0x0000, 0xa65a, 0x0000, 0xa65c, 0x0000, 0xa65e, + 0x0000, 0x0000, 0x0000, 0xa662, 0x0000, 0xa664, 0x0000, 0xa666, + 0x0000, 0xa668, 0x0000, 0xa66a, 0x0000, 0xa66c, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xa680, 0x0000, 0xa682, 0x0000, 0xa684, 0x0000, 0xa686, + 0x0000, 0xa688, 0x0000, 0xa68a, 0x0000, 0xa68c, 0x0000, 0xa68e, + 0x0000, 0xa690, 0x0000, 0xa692, 0x0000, 0xa694, 0x0000, 0xa696, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_a7[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xa722, 0x0000, 0xa724, 0x0000, 0xa726, + 0x0000, 0xa728, 0x0000, 0xa72a, 0x0000, 0xa72c, 0x0000, 0xa72e, + 0x0000, 0x0000, 0x0000, 0xa732, 0x0000, 0xa734, 0x0000, 0xa736, + 0x0000, 0xa738, 0x0000, 0xa73a, 0x0000, 0xa73c, 0x0000, 0xa73e, + 0x0000, 0xa740, 0x0000, 0xa742, 0x0000, 0xa744, 0x0000, 0xa746, + 0x0000, 0xa748, 0x0000, 0xa74a, 0x0000, 0xa74c, 0x0000, 0xa74e, + 0x0000, 0xa750, 0x0000, 0xa752, 0x0000, 0xa754, 0x0000, 0xa756, + 0x0000, 0xa758, 0x0000, 0xa75a, 0x0000, 0xa75c, 0x0000, 0xa75e, + 0x0000, 0xa760, 0x0000, 0xa762, 0x0000, 0xa764, 0x0000, 0xa766, + 0x0000, 0xa768, 0x0000, 0xa76a, 0x0000, 0xa76c, 0x0000, 0xa76e, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xa779, 0x0000, 0xa77b, 0x0000, 0x0000, 0xa77e, + 0x0000, 0xa780, 0x0000, 0xa782, 0x0000, 0xa784, 0x0000, 0xa786, + 0x0000, 0x0000, 0x0000, 0x0000, 0xa78b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_ff[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, + 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, + 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, + 0xff38, 0xff39, 0xff3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t *const toplevel[256] = { + t2_00, t2_01, t2_02, t2_03, t2_04, t2_05, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, t2_1d, t2_1e, t2_1f, + NULL, t2_21, NULL, NULL, t2_24, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, t2_2c, t2_2d, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, t2_a6, t2_a7, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, t2_ff, +}; + +/** + * cifs_toupper - convert a wchar_t from lower to uppercase + * @in: character to convert from lower to uppercase + * + * This function consults the static tables above to convert a wchar_t from + * lower to uppercase. In the event that there is no mapping, the original + * "in" character is returned. + */ +wchar_t +cifs_toupper(wchar_t in) +{ + unsigned char idx; + const wchar_t *tbl; + wchar_t out; + + /* grab upper byte */ + idx = (in & 0xff00) >> 8; + + /* find pointer to 2nd layer table */ + tbl = toplevel[idx]; + if (!tbl) + return in; + + /* grab lower byte */ + idx = in & 0xff; + + /* look up character in table */ + out = tbl[idx]; + if (out) + return out; + + return in; +} diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c new file mode 100644 index 000000000000..998fa51f9b68 --- /dev/null +++ b/fs/smb/client/xattr.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (c) International Business Machines Corp., 2003, 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "cifs_ioctl.h" + +#define MAX_EA_VALUE_SIZE CIFSMaxBufSize +#define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */ +#define CIFS_XATTR_CIFS_NTSD "system.cifs_ntsd" /* owner plus DACL */ +#define CIFS_XATTR_CIFS_NTSD_FULL "system.cifs_ntsd_full" /* owner/DACL/SACL */ +#define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ +#define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ +/* + * Although these three are just aliases for the above, need to move away from + * confusing users and using the 20+ year old term 'cifs' when it is no longer + * secure, replaced by SMB2 (then even more highly secure SMB3) many years ago + */ +#define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */ +#define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */ +#define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */ +#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */ +#define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */ +/* BB need to add server (Samba e.g) support for security and trusted prefix */ + +enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT, + XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL }; + +static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon, + struct inode *inode, const char *full_path, + const void *value, size_t size) +{ + ssize_t rc = -EOPNOTSUPP; + __u32 *pattrib = (__u32 *)value; + __u32 attrib; + FILE_BASIC_INFO info_buf; + + if ((value == NULL) || (size != sizeof(__u32))) + return -ERANGE; + + memset(&info_buf, 0, sizeof(info_buf)); + attrib = *pattrib; + info_buf.Attributes = cpu_to_le32(attrib); + if (pTcon->ses->server->ops->set_file_info) + rc = pTcon->ses->server->ops->set_file_info(inode, full_path, + &info_buf, xid); + if (rc == 0) + CIFS_I(inode)->cifsAttrs = attrib; + + return rc; +} + +static int cifs_creation_time_set(unsigned int xid, struct cifs_tcon *pTcon, + struct inode *inode, const char *full_path, + const void *value, size_t size) +{ + ssize_t rc = -EOPNOTSUPP; + __u64 *pcreation_time = (__u64 *)value; + __u64 creation_time; + FILE_BASIC_INFO info_buf; + + if ((value == NULL) || (size != sizeof(__u64))) + return -ERANGE; + + memset(&info_buf, 0, sizeof(info_buf)); + creation_time = *pcreation_time; + info_buf.CreationTime = cpu_to_le64(creation_time); + if (pTcon->ses->server->ops->set_file_info) + rc = pTcon->ses->server->ops->set_file_info(inode, full_path, + &info_buf, xid); + if (rc == 0) + CIFS_I(inode)->createtime = creation_time; + + return rc; +} + +static int cifs_xattr_set(const struct xattr_handler *handler, + struct user_namespace *mnt_userns, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +{ + int rc = -EOPNOTSUPP; + unsigned int xid; + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = get_xid(); + page = alloc_dentry_path(); + + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + /* return dos attributes as pseudo xattr */ + /* return alt name if available as pseudo attr */ + + /* if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ + if (size > MAX_EA_VALUE_SIZE) { + cifs_dbg(FYI, "size of EA value too large\n"); + rc = -EOPNOTSUPP; + goto out; + } + + switch (handler->flags) { + case XATTR_USER: + cifs_dbg(FYI, "%s:setting user xattr %s\n", __func__, name); + if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || + (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { + rc = cifs_attrib_set(xid, pTcon, inode, full_path, + value, size); + if (rc == 0) /* force revalidate of the inode */ + CIFS_I(inode)->time = 0; + break; + } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || + (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { + rc = cifs_creation_time_set(xid, pTcon, inode, + full_path, value, size); + if (rc == 0) /* force revalidate of the inode */ + CIFS_I(inode)->time = 0; + break; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + goto out; + + if (pTcon->ses->server->ops->set_EA) + rc = pTcon->ses->server->ops->set_EA(xid, pTcon, + full_path, name, value, (__u16)size, + cifs_sb->local_nls, cifs_sb); + break; + + case XATTR_CIFS_ACL: + case XATTR_CIFS_NTSD: + case XATTR_CIFS_NTSD_FULL: { + struct cifs_ntsd *pacl; + + if (!value) + goto out; + pacl = kmalloc(size, GFP_KERNEL); + if (!pacl) { + rc = -ENOMEM; + } else { + memcpy(pacl, value, size); + if (pTcon->ses->server->ops->set_acl) { + int aclflags = 0; + rc = 0; + + switch (handler->flags) { + case XATTR_CIFS_NTSD_FULL: + aclflags = (CIFS_ACL_OWNER | + CIFS_ACL_GROUP | + CIFS_ACL_DACL | + CIFS_ACL_SACL); + break; + case XATTR_CIFS_NTSD: + aclflags = (CIFS_ACL_OWNER | + CIFS_ACL_GROUP | + CIFS_ACL_DACL); + break; + case XATTR_CIFS_ACL: + default: + aclflags = CIFS_ACL_DACL; + } + + rc = pTcon->ses->server->ops->set_acl(pacl, + size, inode, full_path, aclflags); + } else { + rc = -EOPNOTSUPP; + } + if (rc == 0) /* force revalidate of the inode */ + CIFS_I(inode)->time = 0; + kfree(pacl); + } + break; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + case XATTR_ACL_ACCESS: +#ifdef CONFIG_CIFS_POSIX + if (!value) + goto out; + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + value, (const int)size, + ACL_TYPE_ACCESS, cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; + + case XATTR_ACL_DEFAULT: +#ifdef CONFIG_CIFS_POSIX + if (!value) + goto out; + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + value, (const int)size, + ACL_TYPE_DEFAULT, cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + } + +out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static int cifs_attrib_get(struct dentry *dentry, + struct inode *inode, void *value, + size_t size) +{ + ssize_t rc; + __u32 *pattribute; + + rc = cifs_revalidate_dentry_attr(dentry); + + if (rc) + return rc; + + if ((value == NULL) || (size == 0)) + return sizeof(__u32); + else if (size < sizeof(__u32)) + return -ERANGE; + + /* return dos attributes as pseudo xattr */ + pattribute = (__u32 *)value; + *pattribute = CIFS_I(inode)->cifsAttrs; + + return sizeof(__u32); +} + +static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode, + void *value, size_t size) +{ + ssize_t rc; + __u64 *pcreatetime; + + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + + if ((value == NULL) || (size == 0)) + return sizeof(__u64); + else if (size < sizeof(__u64)) + return -ERANGE; + + /* return dos attributes as pseudo xattr */ + pcreatetime = (__u64 *)value; + *pcreatetime = CIFS_I(inode)->createtime; + return sizeof(__u64); +} + + +static int cifs_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *value, size_t size) +{ + ssize_t rc = -EOPNOTSUPP; + unsigned int xid; + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = get_xid(); + page = alloc_dentry_path(); + + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + /* return alt name if available as pseudo attr */ + switch (handler->flags) { + case XATTR_USER: + cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name); + if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || + (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { + rc = cifs_attrib_get(dentry, inode, value, size); + break; + } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || + (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { + rc = cifs_creation_time_get(dentry, inode, value, size); + break; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + goto out; + + if (pTcon->ses->server->ops->query_all_EAs) + rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, + full_path, name, value, size, cifs_sb); + break; + + case XATTR_CIFS_ACL: + case XATTR_CIFS_NTSD: + case XATTR_CIFS_NTSD_FULL: { + /* + * fetch owner, DACL, and SACL if asked for full descriptor, + * fetch owner and DACL otherwise + */ + u32 acllen, extra_info; + struct cifs_ntsd *pacl; + + if (pTcon->ses->server->ops->get_acl == NULL) + goto out; /* rc already EOPNOTSUPP */ + + if (handler->flags == XATTR_CIFS_NTSD_FULL) { + extra_info = SACL_SECINFO; + } else { + extra_info = 0; + } + pacl = pTcon->ses->server->ops->get_acl(cifs_sb, + inode, full_path, &acllen, extra_info); + if (IS_ERR(pacl)) { + rc = PTR_ERR(pacl); + cifs_dbg(VFS, "%s: error %zd getting sec desc\n", + __func__, rc); + } else { + if (value) { + if (acllen > size) + acllen = -ERANGE; + else + memcpy(value, pacl, acllen); + } + rc = acllen; + kfree(pacl); + } + break; + } +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + case XATTR_ACL_ACCESS: +#ifdef CONFIG_CIFS_POSIX + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, + value, size, ACL_TYPE_ACCESS, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; + + case XATTR_ACL_DEFAULT: +#ifdef CONFIG_CIFS_POSIX + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, + value, size, ACL_TYPE_DEFAULT, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; +#endif /* ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + } + + /* We could add an additional check for streams ie + if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ + + if (rc == -EINVAL) + rc = -EOPNOTSUPP; + +out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) +{ + ssize_t rc = -EOPNOTSUPP; + unsigned int xid; + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + return -EOPNOTSUPP; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = get_xid(); + page = alloc_dentry_path(); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto list_ea_exit; + } + /* return dos attributes as pseudo xattr */ + /* return alt name if available as pseudo attr */ + + /* if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ + + if (pTcon->ses->server->ops->query_all_EAs) + rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, + full_path, NULL, data, buf_size, cifs_sb); +list_ea_exit: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static const struct xattr_handler cifs_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .flags = XATTR_USER, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* os2.* attributes are treated like user.* attributes */ +static const struct xattr_handler cifs_os2_xattr_handler = { + .prefix = XATTR_OS2_PREFIX, + .flags = XATTR_USER, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_cifs_acl_xattr_handler = { + .name = CIFS_XATTR_CIFS_ACL, + .flags = XATTR_CIFS_ACL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* + * Although this is just an alias for the above, need to move away from + * confusing users and using the 20 year old term 'cifs' when it is no + * longer secure and was replaced by SMB2/SMB3 a long time ago, and + * SMB3 and later are highly secure. + */ +static const struct xattr_handler smb3_acl_xattr_handler = { + .name = SMB3_XATTR_CIFS_ACL, + .flags = XATTR_CIFS_ACL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = { + .name = CIFS_XATTR_CIFS_NTSD, + .flags = XATTR_CIFS_NTSD, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* + * Although this is just an alias for the above, need to move away from + * confusing users and using the 20 year old term 'cifs' when it is no + * longer secure and was replaced by SMB2/SMB3 a long time ago, and + * SMB3 and later are highly secure. + */ +static const struct xattr_handler smb3_ntsd_xattr_handler = { + .name = SMB3_XATTR_CIFS_NTSD, + .flags = XATTR_CIFS_NTSD, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_cifs_ntsd_full_xattr_handler = { + .name = CIFS_XATTR_CIFS_NTSD_FULL, + .flags = XATTR_CIFS_NTSD_FULL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* + * Although this is just an alias for the above, need to move away from + * confusing users and using the 20 year old term 'cifs' when it is no + * longer secure and was replaced by SMB2/SMB3 a long time ago, and + * SMB3 and later are highly secure. + */ +static const struct xattr_handler smb3_ntsd_full_xattr_handler = { + .name = SMB3_XATTR_CIFS_NTSD_FULL, + .flags = XATTR_CIFS_NTSD_FULL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + + +static const struct xattr_handler cifs_posix_acl_access_xattr_handler = { + .name = XATTR_NAME_POSIX_ACL_ACCESS, + .flags = XATTR_ACL_ACCESS, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_posix_acl_default_xattr_handler = { + .name = XATTR_NAME_POSIX_ACL_DEFAULT, + .flags = XATTR_ACL_DEFAULT, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +const struct xattr_handler *cifs_xattr_handlers[] = { + &cifs_user_xattr_handler, + &cifs_os2_xattr_handler, + &cifs_cifs_acl_xattr_handler, + &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */ + &cifs_cifs_ntsd_xattr_handler, + &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */ + &cifs_cifs_ntsd_full_xattr_handler, + &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */ + &cifs_posix_acl_access_xattr_handler, + &cifs_posix_acl_default_xattr_handler, + NULL +}; diff --git a/fs/smb/common/Makefile b/fs/smb/common/Makefile new file mode 100644 index 000000000000..c66dbbc1469c --- /dev/null +++ b/fs/smb/common/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for Linux filesystem routines that are shared by client and server. +# + +obj-$(CONFIG_SMBFS) += cifs_arc4.o +obj-$(CONFIG_SMBFS) += cifs_md4.o diff --git a/fs/smb/common/arc4.h b/fs/smb/common/arc4.h new file mode 100644 index 000000000000..12e71ec033a1 --- /dev/null +++ b/fs/smb/common/arc4.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common values for ARC4 Cipher Algorithm + */ + +#ifndef _CRYPTO_ARC4_H +#define _CRYPTO_ARC4_H + +#include + +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_BLOCK_SIZE 1 + +struct arc4_ctx { + u32 S[256]; + u32 x, y; +}; + +int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len); +void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len); + +#endif /* _CRYPTO_ARC4_H */ diff --git a/fs/smb/common/cifs_arc4.c b/fs/smb/common/cifs_arc4.c new file mode 100644 index 000000000000..043e4cb839fa --- /dev/null +++ b/fs/smb/common/cifs_arc4.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cryptographic API + * + * ARC4 Cipher Algorithm + * + * Jon Oberheide + */ + +#include +#include "arc4.h" + +MODULE_LICENSE("GPL"); + +int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len) +{ + int i, j = 0, k = 0; + + ctx->x = 1; + ctx->y = 0; + + for (i = 0; i < 256; i++) + ctx->S[i] = i; + + for (i = 0; i < 256; i++) { + u32 a = ctx->S[i]; + + j = (j + in_key[k] + a) & 0xff; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = a; + if (++k >= key_len) + k = 0; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_arc4_setkey); + +void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len) +{ + u32 *const S = ctx->S; + u32 x, y, a, b; + u32 ty, ta, tb; + + if (len == 0) + return; + + x = ctx->x; + y = ctx->y; + + a = S[x]; + y = (y + a) & 0xff; + b = S[y]; + + do { + S[y] = a; + a = (a + b) & 0xff; + S[x] = b; + x = (x + 1) & 0xff; + ta = S[x]; + ty = (y + ta) & 0xff; + tb = S[ty]; + *out++ = *in++ ^ S[a]; + if (--len == 0) + break; + y = ty; + a = ta; + b = tb; + } while (true); + + ctx->x = x; + ctx->y = y; +} +EXPORT_SYMBOL_GPL(cifs_arc4_crypt); diff --git a/fs/smb/common/cifs_md4.c b/fs/smb/common/cifs_md4.c new file mode 100644 index 000000000000..50f78cfc6ce9 --- /dev/null +++ b/fs/smb/common/cifs_md4.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cryptographic API. + * + * MD4 Message Digest Algorithm (RFC1320). + * + * Implementation derived from Andrew Tridgell and Steve French's + * CIFS MD4 implementation, and the cryptoapi implementation + * originally based on the public domain implementation written + * by Colin Plumb in 1993. + * + * Copyright (c) Andrew Tridgell 1997-1998. + * Modified by Steve French (sfrench@us.ibm.com) 2002 + * Copyright (c) Cryptoapi developers. + * Copyright (c) 2002 David S. Miller (davem@redhat.com) + * Copyright (c) 2002 James Morris + * + */ +#include +#include +#include +#include +#include +#include +#include "md4.h" + +MODULE_LICENSE("GPL"); + +static inline u32 lshift(u32 x, unsigned int s) +{ + x &= 0xFFFFFFFF; + return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); +} + +static inline u32 F(u32 x, u32 y, u32 z) +{ + return (x & y) | ((~x) & z); +} + +static inline u32 G(u32 x, u32 y, u32 z) +{ + return (x & y) | (x & z) | (y & z); +} + +static inline u32 H(u32 x, u32 y, u32 z) +{ + return x ^ y ^ z; +} + +#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s)) +#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s)) +#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s)) + +static void md4_transform(u32 *hash, u32 const *in) +{ + u32 a, b, c, d; + + a = hash[0]; + b = hash[1]; + c = hash[2]; + d = hash[3]; + + ROUND1(a, b, c, d, in[0], 3); + ROUND1(d, a, b, c, in[1], 7); + ROUND1(c, d, a, b, in[2], 11); + ROUND1(b, c, d, a, in[3], 19); + ROUND1(a, b, c, d, in[4], 3); + ROUND1(d, a, b, c, in[5], 7); + ROUND1(c, d, a, b, in[6], 11); + ROUND1(b, c, d, a, in[7], 19); + ROUND1(a, b, c, d, in[8], 3); + ROUND1(d, a, b, c, in[9], 7); + ROUND1(c, d, a, b, in[10], 11); + ROUND1(b, c, d, a, in[11], 19); + ROUND1(a, b, c, d, in[12], 3); + ROUND1(d, a, b, c, in[13], 7); + ROUND1(c, d, a, b, in[14], 11); + ROUND1(b, c, d, a, in[15], 19); + + ROUND2(a, b, c, d, in[0], 3); + ROUND2(d, a, b, c, in[4], 5); + ROUND2(c, d, a, b, in[8], 9); + ROUND2(b, c, d, a, in[12], 13); + ROUND2(a, b, c, d, in[1], 3); + ROUND2(d, a, b, c, in[5], 5); + ROUND2(c, d, a, b, in[9], 9); + ROUND2(b, c, d, a, in[13], 13); + ROUND2(a, b, c, d, in[2], 3); + ROUND2(d, a, b, c, in[6], 5); + ROUND2(c, d, a, b, in[10], 9); + ROUND2(b, c, d, a, in[14], 13); + ROUND2(a, b, c, d, in[3], 3); + ROUND2(d, a, b, c, in[7], 5); + ROUND2(c, d, a, b, in[11], 9); + ROUND2(b, c, d, a, in[15], 13); + + ROUND3(a, b, c, d, in[0], 3); + ROUND3(d, a, b, c, in[8], 9); + ROUND3(c, d, a, b, in[4], 11); + ROUND3(b, c, d, a, in[12], 15); + ROUND3(a, b, c, d, in[2], 3); + ROUND3(d, a, b, c, in[10], 9); + ROUND3(c, d, a, b, in[6], 11); + ROUND3(b, c, d, a, in[14], 15); + ROUND3(a, b, c, d, in[1], 3); + ROUND3(d, a, b, c, in[9], 9); + ROUND3(c, d, a, b, in[5], 11); + ROUND3(b, c, d, a, in[13], 15); + ROUND3(a, b, c, d, in[3], 3); + ROUND3(d, a, b, c, in[11], 9); + ROUND3(c, d, a, b, in[7], 11); + ROUND3(b, c, d, a, in[15], 15); + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; +} + +static inline void md4_transform_helper(struct md4_ctx *ctx) +{ + le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block)); + md4_transform(ctx->hash, ctx->block); +} + +int cifs_md4_init(struct md4_ctx *mctx) +{ + memset(mctx, 0, sizeof(struct md4_ctx)); + mctx->hash[0] = 0x67452301; + mctx->hash[1] = 0xefcdab89; + mctx->hash[2] = 0x98badcfe; + mctx->hash[3] = 0x10325476; + mctx->byte_count = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_init); + +int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len) +{ + const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); + + mctx->byte_count += len; + + if (avail > len) { + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, len); + return 0; + } + + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, avail); + + md4_transform_helper(mctx); + data += avail; + len -= avail; + + while (len >= sizeof(mctx->block)) { + memcpy(mctx->block, data, sizeof(mctx->block)); + md4_transform_helper(mctx); + data += sizeof(mctx->block); + len -= sizeof(mctx->block); + } + + memcpy(mctx->block, data, len); + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_update); + +int cifs_md4_final(struct md4_ctx *mctx, u8 *out) +{ + const unsigned int offset = mctx->byte_count & 0x3f; + char *p = (char *)mctx->block + offset; + int padding = 56 - (offset + 1); + + *p++ = 0x80; + if (padding < 0) { + memset(p, 0x00, padding + sizeof(u64)); + md4_transform_helper(mctx); + p = (char *)mctx->block; + padding = 56; + } + + memset(p, 0, padding); + mctx->block[14] = mctx->byte_count << 3; + mctx->block[15] = mctx->byte_count >> 29; + le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - + sizeof(u64)) / sizeof(u32)); + md4_transform(mctx->hash, mctx->block); + cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash)); + memcpy(out, mctx->hash, sizeof(mctx->hash)); + memset(mctx, 0, sizeof(*mctx)); + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_final); diff --git a/fs/smb/common/md4.h b/fs/smb/common/md4.h new file mode 100644 index 000000000000..5337becc699a --- /dev/null +++ b/fs/smb/common/md4.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common values for ARC4 Cipher Algorithm + */ + +#ifndef _CIFS_MD4_H +#define _CIFS_MD4_H + +#include + +#define MD4_DIGEST_SIZE 16 +#define MD4_HMAC_BLOCK_SIZE 64 +#define MD4_BLOCK_WORDS 16 +#define MD4_HASH_WORDS 4 + +struct md4_ctx { + u32 hash[MD4_HASH_WORDS]; + u32 block[MD4_BLOCK_WORDS]; + u64 byte_count; +}; + + +int cifs_md4_init(struct md4_ctx *mctx); +int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len); +int cifs_md4_final(struct md4_ctx *mctx, u8 *out); + +#endif /* _CIFS_MD4_H */ diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h new file mode 100644 index 000000000000..7d605db3bb3b --- /dev/null +++ b/fs/smb/common/smb2pdu.h @@ -0,0 +1,1702 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +#ifndef _COMMON_SMB2PDU_H +#define _COMMON_SMB2PDU_H + +/* + * Note that, due to trying to use names similar to the protocol specifications, + * there are many mixed case field names in the structures below. Although + * this does not match typical Linux kernel style, it is necessary to be + * able to match against the protocol specfication. + * + * SMB2 commands + * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie no useful data other than the SMB error code itself) and are marked such. + * Knowing this helps avoid response buffer allocations and copy in some cases. + */ + +/* List of commands in host endian */ +#define SMB2_NEGOTIATE_HE 0x0000 +#define SMB2_SESSION_SETUP_HE 0x0001 +#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ +#define SMB2_TREE_CONNECT_HE 0x0003 +#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ +#define SMB2_CREATE_HE 0x0005 +#define SMB2_CLOSE_HE 0x0006 +#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ +#define SMB2_READ_HE 0x0008 +#define SMB2_WRITE_HE 0x0009 +#define SMB2_LOCK_HE 0x000A +#define SMB2_IOCTL_HE 0x000B +#define SMB2_CANCEL_HE 0x000C +#define SMB2_ECHO_HE 0x000D +#define SMB2_QUERY_DIRECTORY_HE 0x000E +#define SMB2_CHANGE_NOTIFY_HE 0x000F +#define SMB2_QUERY_INFO_HE 0x0010 +#define SMB2_SET_INFO_HE 0x0011 +#define SMB2_OPLOCK_BREAK_HE 0x0012 + +/* The same list in little endian */ +#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) +#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) +#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) +#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) +#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) +#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) +#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) +#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) +#define SMB2_READ cpu_to_le16(SMB2_READ_HE) +#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) +#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) +#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) +#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) +#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) +#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) +#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) +#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) +#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) +#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) + +#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF) + +#define NUMBER_OF_SMB2_COMMANDS 0x0013 + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define SMB2_NTLMV2_SESSKEY_SIZE 16 +#define SMB2_SIGNATURE_SIZE 16 +#define SMB2_HMACSHA256_SIZE 32 +#define SMB2_CMACAES_SIZE 16 +#define SMB3_GCM128_CRYPTKEY_SIZE 16 +#define SMB3_GCM256_CRYPTKEY_SIZE 32 + +/* + * Size of the smb3 encryption/decryption keys + * This size is big enough to store any cipher key types. + */ +#define SMB3_ENC_DEC_KEY_SIZE 32 + +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE 16 + +#define CIFS_CLIENT_CHALLENGE_SIZE 8 + +/* Maximum buffer size value we can send with 1 credit */ +#define SMB2_MAX_BUFFER_SIZE 65536 + +/* + * The default wsize is 1M for SMB2 (and for some CIFS cases). + * find_get_pages seems to return a maximum of 256 + * pages in a single call. With PAGE_SIZE == 4k, this means we can + * fill a single wsize request with a single call. + */ +#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) + +/* + * SMB2 Header Definition + * + * "MBZ" : Must be Zero + * "BB" : BugBug, Something to check/review/analyze later + * "PDU" : "Protocol Data Unit" (ie a network "frame") + * + */ + +#define __SMB2_HEADER_STRUCTURE_SIZE 64 +#define SMB2_HEADER_STRUCTURE_SIZE \ + cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) + +#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) +#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) +#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc) + +/* + * SMB2 flag definitions + */ +#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) +#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) +#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) +#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) +#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */ +#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) +#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */ + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +/* See MS-SMB2 section 2.2.1 */ +struct smb2_hdr { + __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ + __le16 StructureSize; /* 64 */ + __le16 CreditCharge; /* MBZ */ + __le32 Status; /* Error from server */ + __le16 Command; + __le16 CreditRequest; /* CreditResponse */ + __le32 Flags; + __le32 NextCommand; + __le64 MessageId; + union { + struct { + __le32 ProcessId; + __le32 TreeId; + } __packed SyncId; + __le64 AsyncId; + } __packed Id; + __le64 SessionId; + __u8 Signature[16]; +} __packed; + +struct smb2_pdu { + struct smb2_hdr hdr; + __le16 StructureSize2; /* size of wct area (varies, request specific) */ +} __packed; + +#define SMB2_ERROR_STRUCTURE_SIZE2 9 +#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2) + +struct smb2_err_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __u8 ErrorContextCount; + __u8 Reserved; + __le32 ByteCount; /* even if zero, at least one byte follows */ + __u8 ErrorData[1]; /* variable length */ +} __packed; + +#define SMB3_AES_CCM_NONCE 11 +#define SMB3_AES_GCM_NONCE 12 + +/* Transform flags (for 3.0 dialect this flag indicates CCM */ +#define TRANSFORM_FLAG_ENCRYPTED 0x0001 +struct smb2_transform_hdr { + __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ + __u8 Signature[16]; + __u8 Nonce[16]; + __le32 OriginalMessageSize; + __u16 Reserved1; + __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */ + __le64 SessionId; +} __packed; + + +/* See MS-SMB2 2.2.42 */ +struct smb2_compression_transform_hdr_unchained { + __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ + __le32 OriginalCompressedSegmentSize; + __le16 CompressionAlgorithm; + __le16 Flags; + __le16 Length; /* if chained it is length, else offset */ +} __packed; + +/* See MS-SMB2 2.2.42.1 */ +#define SMB2_COMPRESSION_FLAG_NONE 0x0000 +#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 + +struct compression_payload_header { + __le16 CompressionAlgorithm; + __le16 Flags; + __le32 Length; /* length of compressed playload including field below if present */ + /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */ +} __packed; + +/* See MS-SMB2 2.2.42.2 */ +struct smb2_compression_transform_hdr_chained { + __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ + __le32 OriginalCompressedSegmentSize; + /* struct compression_payload_header[] */ +} __packed; + +/* See MS-SMB2 2.2.42.2.2 */ +struct compression_pattern_payload_v1 { + __le16 Pattern; + __le16 Reserved1; + __le16 Reserved2; + __le32 Repetitions; +} __packed; + +/* See MS-SMB2 section 2.2.9.2 */ +/* Context Types */ +#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000 +#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001) + +struct tree_connect_contexts { + __le16 ContextType; + __le16 DataLength; + __le32 Reserved; + __u8 Data[]; +} __packed; + +/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */ +struct smb3_blob_data { + __le16 BlobSize; + __u8 BlobData[]; +} __packed; + +/* Valid values for Attr */ +#define SE_GROUP_MANDATORY 0x00000001 +#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 +#define SE_GROUP_ENABLED 0x00000004 +#define SE_GROUP_OWNER 0x00000008 +#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 +#define SE_GROUP_INTEGRITY 0x00000020 +#define SE_GROUP_INTEGRITY_ENABLED 0x00000040 +#define SE_GROUP_RESOURCE 0x20000000 +#define SE_GROUP_LOGON_ID 0xC0000000 + +/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ + +struct sid_array_data { + __le16 SidAttrCount; + /* SidAttrList - array of sid_attr_data structs */ +} __packed; + +struct luid_attr_data { + +} __packed; + +/* + * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5 + * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA + */ + +struct privilege_array_data { + __le16 PrivilegeCount; + /* array of privilege_data structs */ +} __packed; + +struct remoted_identity_tcon_context { + __le16 TicketType; /* must be 0x0001 */ + __le16 TicketSize; /* total size of this struct */ + __le16 User; /* offset to SID_ATTR_DATA struct with user info */ + __le16 UserName; /* offset to null terminated Unicode username string */ + __le16 Domain; /* offset to null terminated Unicode domain name */ + __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */ + __le16 RestrictedGroups; /* similar to above */ + __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */ + __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */ + __le16 Owner; /* offset to BLOB_DATA struct */ + __le16 DefaultDacl; /* offset to BLOB_DATA struct */ + __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */ + __le16 UserClaims; /* offset to BLOB_DATA struct */ + __le16 DeviceClaims; /* offset to BLOB_DATA struct */ + __u8 TicketInfo[]; /* variable length buf - remoted identity data */ +} __packed; + +struct smb2_tree_connect_req_extension { + __le32 TreeConnectContextOffset; + __le16 TreeConnectContextCount; + __u8 Reserved[10]; + __u8 PathName[]; /* variable sized array */ + /* followed by array of TreeConnectContexts */ +} __packed; + +/* Flags/Reserved for SMB3.1.1 */ +#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001) +#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002) +#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004) + +struct smb2_tree_connect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 Flags; /* Flags in SMB3.1.1 */ + __le16 PathOffset; + __le16 PathLength; + __u8 Buffer[1]; /* variable length */ +} __packed; + +/* Possible ShareType values */ +#define SMB2_SHARE_TYPE_DISK 0x01 +#define SMB2_SHARE_TYPE_PIPE 0x02 +#define SMB2_SHARE_TYPE_PRINT 0x03 + +/* + * Possible ShareFlags - exactly one and only one of the first 4 caching flags + * must be set (any of the remaining, SHI1005, flags may be set individually + * or in combination. + */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +#define SHI1005_FLAGS_DFS 0x00000001 +#define SHI1005_FLAGS_DFS_ROOT 0x00000002 +#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 +#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 +#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 +#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 +#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 +#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 +#define SMB2_SHAREFLAG_IDENTITY_REMOTING 0x00040000 /* 3.1.1 */ +#define SMB2_SHAREFLAG_COMPRESS_DATA 0x00100000 /* 3.1.1 */ +#define SHI1005_FLAGS_ALL 0x0014FF33 + +/* Possible share capabilities */ +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ +#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ +#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ +#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ +#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ +#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */ + +struct smb2_tree_connect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 16 */ + __u8 ShareType; /* see below */ + __u8 Reserved; + __le32 ShareFlags; /* see below */ + __le32 Capabilities; /* see below */ + __le32 MaximalAccess; +} __packed; + +struct smb2_tree_disconnect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_disconnect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + + +/* + * SMB2_NEGOTIATE_PROTOCOL See MS-SMB2 section 2.2.3 + */ +/* SecurityMode flags */ +#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001 +#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) +#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) +#define SMB2_SEC_MODE_FLAGS_ALL 0x0003 + +/* Capabilities flags */ +#define SMB2_GLOBAL_CAP_DFS 0x00000001 +#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ +/* Internal types */ +#define SMB2_NT_FIND 0x00100000 +#define SMB2_LARGE_FILES 0x00200000 + +#define SMB2_CLIENT_GUID_SIZE 16 +#define SMB2_CREATE_GUID_SIZE 16 + +/* Dialects */ +#define SMB10_PROT_ID 0x0000 /* local only, not sent on wire w/CIFS negprot */ +#define SMB20_PROT_ID 0x0202 +#define SMB21_PROT_ID 0x0210 +#define SMB2X_PROT_ID 0x02FF +#define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 +#define SMB311_PROT_ID 0x0311 +#define BAD_PROT_ID 0xFFFF + +#define SMB311_SALT_SIZE 32 +/* Hash Algorithm Types */ +#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) +#define SMB2_PREAUTH_HASH_SIZE 64 + +/* Negotiate Contexts - ContextTypes. See MS-SMB2 section 2.2.3.1 for details */ +#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) +#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) +#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) +#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) +#define SMB2_TRANSPORT_CAPABILITIES cpu_to_le16(6) +#define SMB2_RDMA_TRANSFORM_CAPABILITIES cpu_to_le16(7) +#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) +#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) + +struct smb2_neg_context { + __le16 ContextType; + __le16 DataLength; + __le32 Reserved; + /* Followed by array of data. NOTE: some servers require padding to 8 byte boundary */ +} __packed; + +/* + * SaltLength that the server send can be zero, so the only three required + * fields (all __le16) end up six bytes total, so the minimum context data len + * in the response is six bytes which accounts for + * + * HashAlgorithmCount, SaltLength, and 1 HashAlgorithm. + */ +#define MIN_PREAUTH_CTXT_DATA_LEN 6 + +struct smb2_preauth_neg_context { + __le16 ContextType; /* 1 */ + __le16 DataLength; + __le32 Reserved; + __le16 HashAlgorithmCount; /* 1 */ + __le16 SaltLength; + __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ + __u8 Salt[SMB311_SALT_SIZE]; +} __packed; + +/* Encryption Algorithms Ciphers */ +#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) +#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) +#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) +#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) + +/* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */ +#define MIN_ENCRYPT_CTXT_DATA_LEN 4 +struct smb2_encryption_neg_context { + __le16 ContextType; /* 2 */ + __le16 DataLength; + __le32 Reserved; + /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ + __le16 CipherCount; /* AES128-GCM and AES128-CCM by default */ + __le16 Ciphers[]; +} __packed; + +/* See MS-SMB2 2.2.3.1.3 */ +#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) +#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) +#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) +#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) +/* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */ +#define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */ + +/* Compression Flags */ +#define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000) +#define SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED cpu_to_le32(0x00000001) + +struct smb2_compression_capabilities_context { + __le16 ContextType; /* 3 */ + __le16 DataLength; + __le32 Reserved; + __le16 CompressionAlgorithmCount; + __le16 Padding; + __le32 Flags; + __le16 CompressionAlgorithms[3]; + __u16 Pad; /* Some servers require pad to DataLen multiple of 8 */ + /* Check if pad needed */ +} __packed; + +/* + * For smb2_netname_negotiate_context_id See MS-SMB2 2.2.3.1.4. + * Its struct simply contains NetName, an array of Unicode characters + */ +struct smb2_netname_neg_context { + __le16 ContextType; /* 5 */ + __le16 DataLength; + __le32 Reserved; + __le16 NetName[]; /* hostname of target converted to UCS-2 */ +} __packed; + +/* + * For smb2_transport_capabilities context see MS-SMB2 2.2.3.1.5 + * and 2.2.4.1.5 + */ + +/* Flags */ +#define SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY 0x00000001 + +struct smb2_transport_capabilities_context { + __le16 ContextType; /* 6 */ + __le16 DataLength; + __u32 Reserved; + __le32 Flags; + __u32 Pad; +} __packed; + +/* + * For rdma transform capabilities context see MS-SMB2 2.2.3.1.6 + * and 2.2.4.1.6 + */ + +/* RDMA Transform IDs */ +#define SMB2_RDMA_TRANSFORM_NONE 0x0000 +#define SMB2_RDMA_TRANSFORM_ENCRYPTION 0x0001 +#define SMB2_RDMA_TRANSFORM_SIGNING 0x0002 + +struct smb2_rdma_transform_capabilities_context { + __le16 ContextType; /* 7 */ + __le16 DataLength; + __u32 Reserved; + __le16 TransformCount; + __u16 Reserved1; + __u32 Reserved2; + __le16 RDMATransformIds[]; +} __packed; + +/* + * For signing capabilities context see MS-SMB2 2.2.3.1.7 + * and 2.2.4.1.7 + */ + +/* Signing algorithms */ +#define SIGNING_ALG_HMAC_SHA256 0 +#define SIGNING_ALG_HMAC_SHA256_LE cpu_to_le16(0) +#define SIGNING_ALG_AES_CMAC 1 +#define SIGNING_ALG_AES_CMAC_LE cpu_to_le16(1) +#define SIGNING_ALG_AES_GMAC 2 +#define SIGNING_ALG_AES_GMAC_LE cpu_to_le16(2) + +struct smb2_signing_capabilities { + __le16 ContextType; /* 8 */ + __le16 DataLength; + __le32 Reserved; + __le16 SigningAlgorithmCount; + __le16 SigningAlgorithms[]; + /* Followed by padding to 8 byte boundary (required by some servers) */ +} __packed; + +#define POSIX_CTXT_DATA_LEN 16 +struct smb2_posix_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ +} __packed; + +struct smb2_negotiate_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 DialectCount; + __le16 SecurityMode; + __le16 Reserved; /* MBZ */ + __le32 Capabilities; + __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; + /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ + __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ + __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ + __le16 Reserved2; + __le16 Dialects[]; +} __packed; + +struct smb2_negotiate_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 65 */ + __le16 SecurityMode; + __le16 DialectRevision; + __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ + __u8 ServerGUID[16]; + __le32 Capabilities; + __le32 MaxTransactSize; + __le32 MaxReadSize; + __le32 MaxWriteSize; + __le64 SystemTime; /* MBZ */ + __le64 ServerStartTime; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + + +/* + * SMB2_SESSION_SETUP See MS-SMB2 section 2.2.5 + */ +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +struct smb2_sess_setup_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 25 */ + __u8 Flags; + __u8 SecurityMode; + __le32 Capabilities; + __le32 Channel; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le64 PreviousSessionId; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Currently defined SessionFlags */ +#define SMB2_SESSION_FLAG_IS_GUEST 0x0001 +#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) +#define SMB2_SESSION_FLAG_IS_NULL 0x0002 +#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) +#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004 +#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) + +struct smb2_sess_setup_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 SessionFlags; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + + +/* + * SMB2_LOGOFF See MS-SMB2 section 2.2.7 + */ +struct smb2_logoff_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_logoff_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + + +/* + * SMB2_CLOSE See MS-SMB2 section 2.2.15 + */ +/* Currently defined values for close flags */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) +struct smb2_close_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Flags; + __le32 Reserved; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ +} __packed; + +/* + * Maximum size of a SMB2_CLOSE response is 64 (smb2 header) + 60 (data) + */ +#define MAX_SMB2_CLOSE_RESPONSE_SIZE 124 + +struct smb2_close_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; + __le32 Attributes; +} __packed; + + +/* + * SMB2_READ See MS-SMB2 section 2.2.19 + */ +/* For read request Flags field below, following flag is defined for SMB3.02 */ +#define SMB2_READFLAG_READ_UNBUFFERED 0x01 +#define SMB2_READFLAG_REQUEST_COMPRESSED 0x02 /* See MS-SMB2 2.2.19 */ + +/* Channel field for read and write: exactly one of following flags can be set*/ +#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) +#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) +#define SMB2_CHANNEL_RDMA_TRANSFORM cpu_to_le32(0x00000003) + +/* SMB2 read request without RFC1001 length at the beginning */ +struct smb2_read_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __u8 Padding; /* offset from start of SMB2 header to place read */ + __u8 Flags; /* MBZ unless SMB3.02 or later */ + __le32 Length; + __le64 Offset; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 MinimumCount; + __le32 Channel; /* MBZ except for SMB3 or later */ + __le32 RemainingBytes; + __le16 ReadChannelInfoOffset; + __le16 ReadChannelInfoLength; + __u8 Buffer[1]; +} __packed; + +/* Read flags */ +#define SMB2_READFLAG_RESPONSE_NONE cpu_to_le32(0x00000000) +#define SMB2_READFLAG_RESPONSE_RDMA_TRANSFORM cpu_to_le32(0x00000001) + +struct smb2_read_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __le32 Flags; + __u8 Buffer[1]; +} __packed; + + +/* + * SMB2_WRITE See MS-SMB2 section 2.2.21 + */ +/* For write request Flags field below the following flags are defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 /* SMB2.1 or later */ +#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */ + +struct smb2_write_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 DataOffset; /* offset from start of SMB2 header to write data */ + __le32 Length; + __le64 Offset; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 Channel; /* MBZ unless SMB3.02 or later */ + __le32 RemainingBytes; + __le16 WriteChannelInfoOffset; + __le16 WriteChannelInfoLength; + __le32 Flags; + __u8 Buffer[1]; +} __packed; + +struct smb2_write_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + + +/* + * SMB2_FLUSH See MS-SMB2 section 2.2.17 + */ +struct smb2_flush_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Reserved1; + __le32 Reserved2; + __u64 PersistentFileId; + __u64 VolatileFileId; +} __packed; + +struct smb2_flush_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Reserved; +} __packed; + +#define SMB2_LOCKFLAG_SHARED 0x0001 +#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002 +#define SMB2_LOCKFLAG_UNLOCK 0x0004 +#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 +#define SMB2_LOCKFLAG_MASK 0x0007 + +struct smb2_lock_element { + __le64 Offset; + __le64 Length; + __le32 Flags; + __le32 Reserved; +} __packed; + +struct smb2_lock_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 48 */ + __le16 LockCount; + /* + * The least significant four bits are the index, the other 28 bits are + * the lock sequence number (0 to 64). See MS-SMB2 2.2.26 + */ + __le32 LockSequenceNumber; + __u64 PersistentFileId; + __u64 VolatileFileId; + /* Followed by at least one */ + struct smb2_lock_element locks[1]; +} __packed; + +struct smb2_lock_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_echo_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +struct smb2_echo_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +/* + * Valid FileInformation classes for query directory + * + * Note that these are a subset of the (file) QUERY_INFO levels defined + * later in this file (but since QUERY_DIRECTORY uses equivalent numbers + * we do not redefine them here) + * + * FileDirectoryInfomation 0x01 + * FileFullDirectoryInformation 0x02 + * FileIdFullDirectoryInformation 0x26 + * FileBothDirectoryInformation 0x03 + * FileIdBothDirectoryInformation 0x25 + * FileNamesInformation 0x0C + * FileIdExtdDirectoryInformation 0x3C + */ + +/* search (query_directory) Flags field */ +#define SMB2_RESTART_SCANS 0x01 +#define SMB2_RETURN_SINGLE_ENTRY 0x02 +#define SMB2_INDEX_SPECIFIED 0x04 +#define SMB2_REOPEN 0x10 + +struct smb2_query_directory_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 FileInformationClass; + __u8 Flags; + __le32 FileIndex; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le16 FileNameOffset; + __le16 FileNameLength; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_directory_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* + * Maximum number of iovs we need for a set-info request. + * The largest one is rename/hardlink + * [0] : struct smb2_set_info_req + smb2_file_[rename|link]_info + * [1] : path + * [2] : compound padding + */ +#define SMB2_SET_INFO_IOV_SIZE 3 + +struct smb2_set_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 BufferLength; + __le16 BufferOffset; + __u16 Reserved; + __le32 AdditionalInformation; + __u64 PersistentFileId; + __u64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 2 */ +} __packed; + +/* + * SMB2_NOTIFY See MS-SMB2 section 2.2.35 + */ +/* notify flags */ +#define SMB2_WATCH_TREE 0x0001 + +/* notify completion filter flags. See MS-FSCC 2.6 and MS-SMB2 2.2.35 */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +/* SMB2 Notify Action Flags */ +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 +#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 + +struct smb2_change_notify_req { + struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Flags; + __le32 OutputBufferLength; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 CompletionFilter; + __u32 Reserved; +} __packed; + +struct smb2_change_notify_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; /* array of file notify structs */ +} __packed; + + +/* + * SMB2_CREATE See MS-SMB2 section 2.2.13 + */ +/* Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF +/* Non-spec internal type */ +#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 + +/* Impersonation Levels. See MS-WPO section 9.7 and MSDN-IMPERS */ +#define IL_ANONYMOUS cpu_to_le32(0x00000000) +#define IL_IDENTIFICATION cpu_to_le32(0x00000001) +#define IL_IMPERSONATION cpu_to_le32(0x00000002) +#define IL_DELEGATE cpu_to_le32(0x00000003) + +/* File Attrubutes */ +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 +#define FILE_ATTRIBUTE_OFFLINE 0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 +#define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 +#define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 +#define FILE_ATTRIBUTE__MASK 0x00007FB7 + +#define FILE_ATTRIBUTE_READONLY_LE cpu_to_le32(0x00000001) +#define FILE_ATTRIBUTE_HIDDEN_LE cpu_to_le32(0x00000002) +#define FILE_ATTRIBUTE_SYSTEM_LE cpu_to_le32(0x00000004) +#define FILE_ATTRIBUTE_DIRECTORY_LE cpu_to_le32(0x00000010) +#define FILE_ATTRIBUTE_ARCHIVE_LE cpu_to_le32(0x00000020) +#define FILE_ATTRIBUTE_NORMAL_LE cpu_to_le32(0x00000080) +#define FILE_ATTRIBUTE_TEMPORARY_LE cpu_to_le32(0x00000100) +#define FILE_ATTRIBUTE_SPARSE_FILE_LE cpu_to_le32(0x00000200) +#define FILE_ATTRIBUTE_REPARSE_POINT_LE cpu_to_le32(0x00000400) +#define FILE_ATTRIBUTE_COMPRESSED_LE cpu_to_le32(0x00000800) +#define FILE_ATTRIBUTE_OFFLINE_LE cpu_to_le32(0x00001000) +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED_LE cpu_to_le32(0x00002000) +#define FILE_ATTRIBUTE_ENCRYPTED_LE cpu_to_le32(0x00004000) +#define FILE_ATTRIBUTE_INTEGRITY_STREAM_LE cpu_to_le32(0x00008000) +#define FILE_ATTRIBUTE_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define FILE_ATTRIBUTE_MASK_LE cpu_to_le32(0x00007FB7) + +/* Desired Access Flags */ +#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) +#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) +#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) +#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) +#define FILE_READ_EA_LE cpu_to_le32(0x00000008) +#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) +#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) +#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) +#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) +#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) +#define FILE_DELETE_LE cpu_to_le32(0x00010000) +#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) +#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) +#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) +#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) +#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) +#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) +#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) +#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) +#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) +#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) +#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) + + +#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ + FILE_READ_EA_LE | \ + FILE_GENERIC_READ_LE) +#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ + FILE_APPEND_DATA_LE | \ + FILE_WRITE_EA_LE | \ + FILE_WRITE_ATTRIBUTES_LE | \ + FILE_GENERIC_WRITE_LE) + +/* ShareAccess Flags */ +#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) +#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) +#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) +#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) + +/* CreateDisposition Flags */ +#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) +#define FILE_OPEN_LE cpu_to_le32(0x00000001) +#define FILE_CREATE_LE cpu_to_le32(0x00000002) +#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) +#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) +#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) +#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) + +#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ + | FILE_READ_ATTRIBUTES) +#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) +#define FILE_EXEC_RIGHTS (FILE_EXECUTE) + +/* CreateOptions Flags */ +#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) +/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */ +#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) +#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) +#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) +#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) +#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) +#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) +#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) +#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) +#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) +#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) +#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) +#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) +#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) +#define CREATE_OPTIONS_MASK_LE cpu_to_le32(0x00FFFFFF) + +#define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \ + | FILE_READ_ATTRIBUTES_LE) +#define FILE_WRITE_RIGHTS_LE (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE \ + | FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE) +#define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE) + +/* Create Context Values */ +#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ +#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" +#define SMB2_CREATE_ALLOCATION_SIZE "AISi" +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" +#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" +#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" +#define SMB2_CREATE_REQUEST_LEASE "RqLs" +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" +#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" +#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" +#define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" +#define SVHDX_OPEN_DEVICE_CONTEXT "\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83" +#define SMB2_CREATE_TAG_AAPL "AAPL" + +/* Flag (SMB3 open response) values */ +#define SMB2_CREATE_FLAG_REPARSEPOINT 0x01 + +struct create_context { + __le32 Next; + __le16 NameOffset; + __le16 NameLength; + __le16 Reserved; + __le16 DataOffset; + __le32 DataLength; + __u8 Buffer[]; +} __packed; + +struct smb2_create_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u8 SecurityFlags; + __u8 RequestedOplockLevel; + __le32 ImpersonationLevel; + __le64 SmbCreateFlags; + __le64 Reserved; + __le32 DesiredAccess; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le16 NameOffset; + __le16 NameLength; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[]; +} __packed; + +struct smb2_create_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 89 */ + __u8 OplockLevel; + __u8 Flags; /* 0x01 if reparse point */ + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndofFile; + __le32 FileAttributes; + __le32 Reserved2; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[1]; +} __packed; + +struct create_posix { + struct create_context ccontext; + __u8 Name[16]; + __le32 Mode; + __u32 Reserved; +} __packed; + +#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00) +#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01) +#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02) +#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) + +#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) + +#define SMB2_LEASE_KEY_SIZE 16 + +struct lease_context { + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; +} __packed; + +struct lease_context_v2 { + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; + __u8 ParentLeaseKey[SMB2_LEASE_KEY_SIZE]; + __le16 Epoch; + __le16 Reserved; +} __packed; + +struct create_lease { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context lcontext; +} __packed; + +struct create_lease_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context_v2 lcontext; + __u8 Pad[4]; +} __packed; + +/* See MS-SMB2 2.2.31 and 2.2.32 */ +struct smb2_ioctl_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CtlCode; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 MaxInputResponse; + __le32 OutputOffset; + __le32 OutputCount; + __le32 MaxOutputResponse; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[]; +} __packed; + +struct smb2_ioctl_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 Reserved; + __le32 CtlCode; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 OutputOffset; + __le32 OutputCount; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[]; +} __packed; + +/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */ +struct file_zero_data_information { + __le64 FileOffset; + __le64 BeyondFinalZero; +} __packed; + +/* See MS-FSCC 2.3.7 */ +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + +/* See MS-FSCC 2.3.8 */ +#define DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC 0x00000001 +struct duplicate_extents_to_file_ex { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ + __le32 Flags; + __le32 Reserved; +} __packed; + + +/* See MS-FSCC 2.3.20 */ +struct fsctl_get_integrity_information_rsp { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; + __le32 ChecksumChunkSizeInBytes; + __le32 ClusterSizeInBytes; +} __packed; + +/* See MS-FSCC 2.3.55 */ +struct fsctl_query_file_regions_req { + __le64 FileOffset; + __le64 Length; + __le32 DesiredUsage; + __le32 Reserved; +} __packed; + +/* DesiredUsage flags see MS-FSCC 2.3.56.1 */ +#define FILE_USAGE_INVALID_RANGE 0x00000000 +#define FILE_USAGE_VALID_CACHED_DATA 0x00000001 +#define FILE_USAGE_NONCACHED_DATA 0x00000002 + +struct file_region_info { + __le64 FileOffset; + __le64 Length; + __le32 DesiredUsage; + __le32 Reserved; +} __packed; + +/* See MS-FSCC 2.3.56 */ +struct fsctl_query_file_region_rsp { + __le32 Flags; + __le32 TotalRegionEntryCount; + __le32 RegionEntryCount; + __u32 Reserved; + struct file_region_info Regions[]; +} __packed; + +/* See MS-FSCC 2.3.58 */ +struct fsctl_query_on_disk_vol_info_rsp { + __le64 DirectoryCount; + __le64 FileCount; + __le16 FsFormatMajVersion; + __le16 FsFormatMinVersion; + __u8 FsFormatName[24]; + __le64 FormatTime; + __le64 LastUpdateTime; + __u8 CopyrightInfo[68]; + __u8 AbstractInfo[68]; + __u8 FormatImplInfo[68]; + __u8 LastModifyImplInfo[68]; +} __packed; + +/* See MS-FSCC 2.3.73 */ +struct fsctl_set_integrity_information_req { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; +} __packed; + +/* See MS-FSCC 2.3.75 */ +struct fsctl_set_integrity_info_ex_req { + __u8 EnableIntegrity; + __u8 KeepState; + __u16 Reserved; + __le32 Flags; + __u8 Version; + __u8 Reserved2[7]; +} __packed; + +/* Integrity ChecksumAlgorithm choices for above */ +#define CHECKSUM_TYPE_NONE 0x0000 +#define CHECKSUM_TYPE_CRC64 0x0002 +#define CHECKSUM_TYPE_UNCHANGED 0xFFFF /* set only */ + +/* Integrity flags for above */ +#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 + +/* Reparse structures - see MS-FSCC 2.1.2 */ + +/* struct fsctl_reparse_info_req is empty, only response structs (see below) */ +struct reparse_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +struct reparse_guid_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 ReparseGuid[16]; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +struct reparse_mount_point_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __u8 PathBuffer[]; /* Variable Length */ +} __packed; + +#define SYMLINK_FLAG_RELATIVE 0x00000001 + +struct reparse_symlink_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __le32 Flags; + __u8 PathBuffer[]; /* Variable Length */ +} __packed; + +/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */ + +struct validate_negotiate_info_req { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 DialectCount; + __le16 Dialects[4]; /* BB expand this if autonegotiate > 4 dialects */ +} __packed; + +struct validate_negotiate_info_rsp { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 Dialect; /* Dialect in use for the connection */ +} __packed; + + +/* Possible InfoType values */ +#define SMB2_O_INFO_FILE 0x01 +#define SMB2_O_INFO_FILESYSTEM 0x02 +#define SMB2_O_INFO_SECURITY 0x03 +#define SMB2_O_INFO_QUOTA 0x04 + +/* SMB2 Query Info see MS-SMB2 (2.2.37) or MS-DTYP */ + +/* List of QUERY INFO levels (those also valid for QUERY_DIR are noted below */ +#define FILE_DIRECTORY_INFORMATION 1 /* also for QUERY_DIR */ +#define FILE_FULL_DIRECTORY_INFORMATION 2 /* also for QUERY_DIR */ +#define FILE_BOTH_DIRECTORY_INFORMATION 3 /* also for QUERY_DIR */ +#define FILE_BASIC_INFORMATION 4 +#define FILE_STANDARD_INFORMATION 5 +#define FILE_INTERNAL_INFORMATION 6 +#define FILE_EA_INFORMATION 7 +#define FILE_ACCESS_INFORMATION 8 +#define FILE_NAME_INFORMATION 9 +#define FILE_RENAME_INFORMATION 10 +#define FILE_LINK_INFORMATION 11 +#define FILE_NAMES_INFORMATION 12 /* also for QUERY_DIR */ +#define FILE_DISPOSITION_INFORMATION 13 +#define FILE_POSITION_INFORMATION 14 +#define FILE_FULL_EA_INFORMATION 15 +#define FILE_MODE_INFORMATION 16 +#define FILE_ALIGNMENT_INFORMATION 17 +#define FILE_ALL_INFORMATION 18 +#define FILE_ALLOCATION_INFORMATION 19 +#define FILE_END_OF_FILE_INFORMATION 20 +#define FILE_ALTERNATE_NAME_INFORMATION 21 +#define FILE_STREAM_INFORMATION 22 +#define FILE_PIPE_INFORMATION 23 +#define FILE_PIPE_LOCAL_INFORMATION 24 +#define FILE_PIPE_REMOTE_INFORMATION 25 +#define FILE_MAILSLOT_QUERY_INFORMATION 26 +#define FILE_MAILSLOT_SET_INFORMATION 27 +#define FILE_COMPRESSION_INFORMATION 28 +#define FILE_OBJECT_ID_INFORMATION 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION 31 +#define FILE_QUOTA_INFORMATION 32 +#define FILE_REPARSE_POINT_INFORMATION 33 +#define FILE_NETWORK_OPEN_INFORMATION 34 +#define FILE_ATTRIBUTE_TAG_INFORMATION 35 +#define FILE_TRACKING_INFORMATION 36 +#define FILEID_BOTH_DIRECTORY_INFORMATION 37 /* also for QUERY_DIR */ +#define FILEID_FULL_DIRECTORY_INFORMATION 38 /* also for QUERY_DIR */ +#define FILE_VALID_DATA_LENGTH_INFORMATION 39 +#define FILE_SHORT_NAME_INFORMATION 40 +#define FILE_SFIO_RESERVE_INFORMATION 44 +#define FILE_SFIO_VOLUME_INFORMATION 45 +#define FILE_HARD_LINK_INFORMATION 46 +#define FILE_NORMALIZED_NAME_INFORMATION 48 +#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 +#define FILE_STANDARD_LINK_INFORMATION 54 +#define FILE_ID_INFORMATION 59 +#define FILE_ID_EXTD_DIRECTORY_INFORMATION 60 /* also for QUERY_DIR */ +/* Used for Query Info and Find File POSIX Info for SMB3.1.1 and SMB1 */ +#define SMB_FIND_FILE_POSIX_INFO 0x064 + +/* Security info type additionalinfo flags. */ +#define OWNER_SECINFO 0x00000001 +#define GROUP_SECINFO 0x00000002 +#define DACL_SECINFO 0x00000004 +#define SACL_SECINFO 0x00000008 +#define LABEL_SECINFO 0x00000010 +#define ATTRIBUTE_SECINFO 0x00000020 +#define SCOPE_SECINFO 0x00000040 +#define BACKUP_SECINFO 0x00010000 +#define UNPROTECTED_SACL_SECINFO 0x10000000 +#define UNPROTECTED_DACL_SECINFO 0x20000000 +#define PROTECTED_SACL_SECINFO 0x40000000 +#define PROTECTED_DACL_SECINFO 0x80000000 + +/* Flags used for FileFullEAinfo */ +#define SL_RESTART_SCAN 0x00000001 +#define SL_RETURN_SINGLE_ENTRY 0x00000002 +#define SL_INDEX_SPECIFIED 0x00000004 + +struct smb2_query_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 41 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 OutputBufferLength; + __le16 InputBufferOffset; + __u16 Reserved; + __le32 InputBufferLength; + __le32 AdditionalInformation; + __le32 Flags; + __u64 PersistentFileId; + __u64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* + * PDU query infolevel structure definitions + */ + +/* See MS-FSCC 2.3.52 */ +struct file_allocated_range_buffer { + __le64 file_offset; + __le64 length; +} __packed; + +struct smb2_file_internal_info { + __le64 IndexNumber; +} __packed; /* level 6 Query */ + +struct smb2_file_rename_info { /* encoding of request for level 10 */ + __u8 ReplaceIfExists; /* 1 = replace existing target with new */ + /* 0 = fail if target already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[]; /* New name to be assigned */ + /* padding - overall struct size must be >= 24 so filename + pad >= 6 */ +} __packed; /* level 10 Set */ + +struct smb2_file_link_info { /* encoding of request for level 11 */ + __u8 ReplaceIfExists; /* 1 = replace existing link with new */ + /* 0 = fail if link already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[]; /* Name to be assigned to new link */ +} __packed; /* level 11 Set */ + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 18 Query */ + +struct smb2_file_eof_info { /* encoding of request for level 10 */ + __le64 EndOfFile; /* new end of file value */ +} __packed; /* level 20 Set */ + +/* Level 100 query info */ +struct smb311_posix_qinfo { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 Sids[]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* File System Information Classes */ +#define FS_VOLUME_INFORMATION 1 /* Query */ +#define FS_LABEL_INFORMATION 2 /* Set */ +#define FS_SIZE_INFORMATION 3 /* Query */ +#define FS_DEVICE_INFORMATION 4 /* Query */ +#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ +#define FS_CONTROL_INFORMATION 6 /* Query, Set */ +#define FS_FULL_SIZE_INFORMATION 7 /* Query */ +#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ +#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ +#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ +#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ + +struct smb2_fs_full_size_info { + __le64 TotalAllocationUnits; + __le64 CallerAvailableAllocationUnits; + __le64 ActualAvailableAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; + +#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { + __le32 LogicalBytesPerSector; + __le32 PhysicalBytesPerSectorForAtomicity; + __le32 PhysicalBytesPerSectorForPerf; + __le32 FSEffPhysicalBytesPerSectorForAtomicity; + __le32 Flags; + __le32 ByteOffsetForSectorAlignment; + __le32 ByteOffsetForPartitionAlignment; +} __packed; + +/* File System Control Information */ +struct smb2_fs_control_info { + __le64 FreeSpaceStartFiltering; + __le64 FreeSpaceThreshold; + __le64 FreeSpaceStopFiltering; + __le64 DefaultQuotaThreshold; + __le64 DefaultQuotaLimit; + __le32 FileSystemControlFlags; + __le32 Padding; +} __packed; + +/* volume info struct - see MS-FSCC 2.5.9 */ +#define MAX_VOL_LABEL_LEN 32 +struct smb3_fs_vol_info { + __le64 VolumeCreationTime; + __u32 VolumeSerialNumber; + __le32 VolumeLabelLength; /* includes trailing null */ + __u8 SupportsObjects; /* True if eg like NTFS, supports objects */ + __u8 Reserved; + __u8 VolumeLabel[]; /* variable len */ +} __packed; + +/* See MS-SMB2 2.2.23 through 2.2.25 */ +struct smb2_oplock_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 Reserved2; + __u64 PersistentFid; + __u64 VolatileFid; +} __packed; + +#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) + +struct smb2_lease_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 44 */ + __le16 Epoch; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 CurrentLeaseState; + __le32 NewLeaseState; + __le32 BreakReason; + __le32 AccessMaskHint; + __le32 ShareMaskHint; +} __packed; + +struct smb2_lease_ack { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 Reserved; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 LeaseState; + __le64 LeaseDuration; +} __packed; + +#define OP_BREAK_STRUCT_SIZE_20 24 +#define OP_BREAK_STRUCT_SIZE_21 36 +#endif /* _COMMON_SMB2PDU_H */ diff --git a/fs/smb/common/smbfsctl.h b/fs/smb/common/smbfsctl.h new file mode 100644 index 000000000000..edd7fc2a7921 --- /dev/null +++ b/fs/smb/common/smbfsctl.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * SMB, CIFS, SMB2 FSCTL definitions + * + * Copyright (c) International Business Machines Corp., 2002,2013 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +/* IOCTL information */ +/* + * List of ioctl/fsctl function codes that are or could be useful in the + * future to remote clients like cifs or SMB2/SMB3 client. This is probably + * a slightly larger set of fsctls that NTFS local filesystem could handle, + * including the seven below that we do not have struct definitions for. + * Even with protocol definitions for most of these now available, we still + * need to do some experimentation to identify which are practical to do + * remotely. Some of the following, such as the encryption/compression ones + * could be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + * + * See MS-SMB2 Section 2.2.31 (last checked September 2021, all of that list are + * below). Additional detail on less common ones can be found in MS-FSCC + * section 2.3. + */ + +#ifndef __SMBFSCTL_H +#define __SMBFSCTL_H + +/* + * FSCTL values are 32 bits and are constructed as + * + */ +/* Device */ +#define FSCTL_DEVICE_DFS (0x0006 << 16) +#define FSCTL_DEVICE_FILE_SYSTEM (0x0009 << 16) +#define FSCTL_DEVICE_NAMED_PIPE (0x0011 << 16) +#define FSCTL_DEVICE_NETWORK_FILE_SYSTEM (0x0014 << 16) +#define FSCTL_DEVICE_MASK 0xffff0000 +/* Access */ +#define FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS (0x00 << 14) +#define FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS (0x01 << 14) +#define FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS (0x02 << 14) +#define FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS (0x03 << 14) +#define FSCTL_DEVICE_ACCESS_MASK 0x0000c000 +/* Function */ +#define FSCTL_DEVICE_FUNCTION_MASK 0x00003ffc +/* Method */ +#define FSCTL_DEVICE_METHOD_BUFFERED 0x00 +#define FSCTL_DEVICE_METHOD_IN_DIRECT 0x01 +#define FSCTL_DEVICE_METHOD_OUT_DIRECT 0x02 +#define FSCTL_DEVICE_METHOD_NEITHER 0x03 +#define FSCTL_DEVICE_METHOD_MASK 0x00000003 + + +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 +#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 +#define FSCTL_LOCK_VOLUME 0x00090018 +#define FSCTL_UNLOCK_VOLUME 0x0009001C +#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ +#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ +#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ +#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ +/* Verify the next FSCTL number, we had it as 0x00090090 before */ +#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ +#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ +#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ +#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ +#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C +#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ +#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ +#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ +#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ +#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ +#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ +#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ +#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ +#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 +#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ +#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ +#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ +#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ +#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ +#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ +#define FSCTL_MARK_HANDLE 0x000900FC /* BB add struct */ +#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ +#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ +#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ +#define FSCTL_QUERY_ON_DISK_VOLUME_INFO 0x0009013C +#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ +#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C +#define FSCTL_QUERY_FILE_REGIONS 0x00090284 +#define FSCTL_GET_REFS_VOLUME_DATA 0x000902D8 /* See MS-FSCC 2.3.24 */ +#define FSCTL_SET_INTEGRITY_INFORMATION_EXT 0x00090380 +#define FSCTL_GET_RETRIEVAL_POINTERS_AND_REFCOUNT 0x000903d3 +#define FSCTL_GET_RETRIEVAL_POINTER_COUNT 0x0009042b +#define FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT 0x00090440 +#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF +#define FSCTL_OFFLOAD_READ 0x00094264 /* BB add struct */ +#define FSCTL_OFFLOAD_WRITE 0x00098268 /* BB add struct */ +#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX 0x000983E8 +#define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_SET_INTEGRITY_INFORMATION 0x0009C280 +#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ +#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ +/* strange that the number for this op is not sequential with previous op */ +#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +/* Enumerate previous versions of a file */ +#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064 +/* Retrieve an opaque file reference for server-side data movement ie copy */ +#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 +#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ +#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +/* Perform server-side data movement */ +#define FSCTL_SRV_COPYCHUNK 0x001440F2 +#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */ +#define FSCTL_SRV_READ_HASH 0x001441BB /* BB add struct */ + +/* See FSCC 2.1.2.5 */ +#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define IO_REPARSE_TAG_HSM 0xC0000004 +#define IO_REPARSE_TAG_SIS 0x80000007 +#define IO_REPARSE_TAG_HSM2 0x80000006 +#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 +/* Used by the DFS filter. See MS-DFSC */ +#define IO_REPARSE_TAG_DFS 0x8000000A +/* Used by the DFS filter See MS-DFSC */ +#define IO_REPARSE_TAG_DFSR 0x80000012 +#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B +/* See section MS-FSCC 2.1.2.4 */ +#define IO_REPARSE_TAG_SYMLINK 0xA000000C +#define IO_REPARSE_TAG_DEDUP 0x80000013 +#define IO_REPARSE_APPXSTREAM 0xC0000014 +/* NFS symlinks, Win 8/SMB3 and later */ +#define IO_REPARSE_TAG_NFS 0x80000014 +/* + * AzureFileSync - see + * https://docs.microsoft.com/en-us/azure/storage/files/storage-sync-cloud-tiering + */ +#define IO_REPARSE_TAG_AZ_FILE_SYNC 0x8000001e +/* WSL reparse tags */ +#define IO_REPARSE_TAG_LX_SYMLINK 0xA000001D +#define IO_REPARSE_TAG_AF_UNIX 0x80000023 +#define IO_REPARSE_TAG_LX_FIFO 0x80000024 +#define IO_REPARSE_TAG_LX_CHR 0x80000025 +#define IO_REPARSE_TAG_LX_BLK 0x80000026 + +#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) + +/* fsctl flags */ +/* If Flags is set to this value, the request is an FSCTL not ioctl request */ +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 +#endif /* __SMBFSCTL_H */ diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig new file mode 100644 index 000000000000..e1fe17747ed6 --- /dev/null +++ b/fs/smb/server/Kconfig @@ -0,0 +1,68 @@ +config SMB_SERVER + tristate "SMB3 server support (EXPERIMENTAL)" + depends on INET + depends on MULTIUSER + depends on FILE_LOCKING + select NLS + select NLS_UTF8 + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_HMAC + select CRYPTO_ECB + select CRYPTO_LIB_DES + select CRYPTO_SHA256 + select CRYPTO_CMAC + select CRYPTO_SHA512 + select CRYPTO_AEAD2 + select CRYPTO_CCM + select CRYPTO_GCM + select ASN1 + select OID_REGISTRY + select CRC32 + default n + help + Choose Y here if you want to allow SMB3 compliant clients + to access files residing on this system using SMB3 protocol. + To compile the SMB3 server support as a module, + choose M here: the module will be called ksmbd. + + You may choose to use a samba server instead, in which + case you can choose N here. + + You also need to install user space programs which can be found + in ksmbd-tools, available from + https://github.com/cifsd-team/ksmbd-tools. + More detail about how to run the ksmbd kernel server is + available via README file + (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). + + ksmbd kernel server includes support for auto-negotiation, + Secure negotiate, Pre-authentication integrity, oplock/lease, + compound requests, multi-credit, packet signing, RDMA(smbdirect), + smb3 encryption, copy-offload, secure per-user session + establishment via NTLM or NTLMv2. + +config SMB_SERVER_SMBDIRECT + bool "Support for SMB Direct protocol" + depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + select SG_POOL + default n + + help + Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. + + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say N. + +config SMB_SERVER_CHECK_CAP_NET_ADMIN + bool "Enable check network administration capability" + depends on SMB_SERVER + default y + + help + Prevent unprivileged processes to start the ksmbd kernel server. + +config SMB_SERVER_KERBEROS5 + bool "Support for Kerberos 5" + depends on SMB_SERVER + default n diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile new file mode 100644 index 000000000000..7d6337a7dee4 --- /dev/null +++ b/fs/smb/server/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for Linux SMB3 kernel server +# +obj-$(CONFIG_SMB_SERVER) += ksmbd.o + +ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \ + misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ + mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ + mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ + transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ + smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ + ksmbd_spnego_negtokentarg.asn1.o asn1.o + +$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h +$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c new file mode 100644 index 000000000000..c03eba090368 --- /dev/null +++ b/fs/smb/server/asn1.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" + +#include "asn1.h" +#include "connection.h" +#include "auth.h" +#include "ksmbd_spnego_negtokeninit.asn1.h" +#include "ksmbd_spnego_negtokentarg.asn1.h" + +#define NTLMSSP_OID_LEN 10 + +static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x02, 0x02, 0x0a }; + +int +ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn, + security_blob, length); +} + +int +ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn, + security_blob, length); +} + +static int compute_asn_hdr_len_bytes(int len) +{ + if (len > 0xFFFFFF) + return 4; + else if (len > 0xFFFF) + return 3; + else if (len > 0xFF) + return 2; + else if (len > 0x7F) + return 1; + else + return 0; +} + +static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq, + int length) +{ + int i; + int index = *ofs; + char hdr_len = compute_asn_hdr_len_bytes(length); + int len = length + 2 + hdr_len; + + /* insert tag */ + buf[index++] = tag; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + /* insert seq */ + len = len - (index - *ofs); + buf[index++] = seq; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + *ofs += (index - *ofs); +} + +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 + + NTLMSSP_OID_LEN; + int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 + + ntlm_blob_len; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len + + oid_len + ntlmssp_len) * 2 + + neg_result_len + oid_len + ntlmssp_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len + + ntlmssp_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + buf[ofs++] = 1; + + /* insert oid */ + encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN); + memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN); + ofs += NTLMSSP_OID_LEN; + + /* insert response token - ntlmssp blob */ + encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len); + memcpy(buf + ofs, ntlm_blob, ntlm_blob_len); + ofs += ntlm_blob_len; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + + neg_result_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + if (neg_result) + buf[ofs++] = 2; + else + buf[ofs++] = 0; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid != OID_spnego) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; + } + + return 0; +} + +int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + enum OID oid; + int mech_type; + + oid = look_up_OID(value, vlen); + if (oid == OID_ntlmssp) { + mech_type = KSMBD_AUTH_NTLMSSP; + } else if (oid == OID_mskrb5) { + mech_type = KSMBD_AUTH_MSKRB5; + } else if (oid == OID_krb5) { + mech_type = KSMBD_AUTH_KRB5; + } else if (oid == OID_krb5u2u) { + mech_type = KSMBD_AUTH_KRB5U2U; + } else { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; + } + + conn->auth_mechs |= mech_type; + if (conn->preferred_auth_mech == 0) + conn->preferred_auth_mech = mech_type; + + return 0; +} + +int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} + +int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} diff --git a/fs/smb/server/asn1.h b/fs/smb/server/asn1.h new file mode 100644 index 000000000000..ce105f4ce305 --- /dev/null +++ b/fs/smb/server/asn1.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __ASN1_H__ +#define __ASN1_H__ + +int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len); +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result); +#endif /* __ASN1_H__ */ diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c new file mode 100644 index 000000000000..5e5e120edcc2 --- /dev/null +++ b/fs/smb/server/auth.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "auth.h" +#include "glob.h" + +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "crypto_ctx.h" +#include "transport_ipc.h" +#include "../common/arc4.h" + +/* + * Fixed format data defining GSS header and fixed string + * "not_defined_in_RFC4178@please_ignore". + * So sec blob data in neg phase could be generated statically. + */ +static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, + 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, + 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, + 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, + 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 +#else + 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, + 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, + 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, + 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, + 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, + 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65 +#endif +}; + +void ksmbd_copy_gss_neg_header(void *buf) +{ + memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); +} + +/** + * ksmbd_gen_sess_key() - function to generate session key + * @sess: session of connection + * @hash: source hash value to be used for find session key + * @hmac: source hmac value to be used for finding session key + * + */ +static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, + char *hmac) +{ + struct ksmbd_crypto_ctx *ctx; + int rc; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); + goto out; + } + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), + hmac, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); + goto out; + } + +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, + char *ntlmv2_hash, char *dname) +{ + int ret, len, conv_len; + wchar_t *domain = NULL; + __le16 *uniname = NULL; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); + return -ENOMEM; + } + + ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + user_passkey(sess->user), + CIFS_ENCPWD_SIZE); + if (ret) { + ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); + goto out; + } + + ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (ret) { + ksmbd_debug(AUTH, "could not init hmacmd5\n"); + goto out; + } + + /* convert user_name to unicode */ + len = strlen(user_name(sess->user)); + uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!uniname) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + UniStrupr(uniname); + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)uniname, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with user\n"); + goto out; + } + + /* Convert domain name or conn name to unicode and uppercase */ + len = strlen(dname); + domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!domain) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)domain, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with domain\n"); + goto out; + } + + ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); + if (ret) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: + kfree(uniname); + kfree(domain); + ksmbd_release_crypto_ctx(ctx); + return ret; +} + +/** + * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler + * @sess: session of connection + * @ntlmv2: NTLMv2 challenge response + * @blen: NTLMv2 blob length + * @domain_name: domain name + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey) +{ + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + struct ksmbd_crypto_ctx *ctx = NULL; + char *construct = NULL; + int rc, len; + + rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); + if (rc) { + ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); + goto out; + } + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + ntlmv2_hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init hmacmd5\n"); + goto out; + } + + len = CIFS_CRYPTO_KEY_SIZE + blen; + construct = kzalloc(len, GFP_KERNEL); + if (!construct) { + rc = -ENOMEM; + goto out; + } + + memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); + memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + goto out; + } + ksmbd_release_crypto_ctx(ctx); + ctx = NULL; + + rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate sess key\n"); + goto out; + } + + if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) + rc = -EINVAL; +out: + if (ctx) + ksmbd_release_crypto_ctx(ctx); + kfree(construct); + return rc; +} + +/** + * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct + * authenticate blob + * @authblob: authenticate blob source pointer + * @usr: user details + * @sess: session of connection + * + * Return: 0 on success, error number on error + */ +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + char *domain_name; + unsigned int nt_off, dn_off; + unsigned short nt_len, dn_len; + int ret; + + if (blob_len < sizeof(struct authenticate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(authblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + authblob->Signature); + return -EINVAL; + } + + nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); + nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); + dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); + dn_len = le16_to_cpu(authblob->DomainName.Length); + + if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len || + nt_len < CIFS_ENCPWD_SIZE) + return -EINVAL; + + /* TODO : use domain name that imported from configuration file */ + domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, + dn_len, true, conn->local_nls); + if (IS_ERR(domain_name)) + return PTR_ERR(domain_name); + + /* process NTLMv2 authentication */ + ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", + domain_name); + ret = ksmbd_auth_ntlmv2(conn, sess, + (struct ntlmv2_resp *)((char *)authblob + nt_off), + nt_len - CIFS_ENCPWD_SIZE, + domain_name, conn->ntlmssp.cryptkey); + kfree(domain_name); + + /* The recovered secondary session key */ + if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { + struct arc4_ctx *ctx_arc4; + unsigned int sess_key_off, sess_key_len; + + sess_key_off = le32_to_cpu(authblob->SessionKey.BufferOffset); + sess_key_len = le16_to_cpu(authblob->SessionKey.Length); + + if (blob_len < (u64)sess_key_off + sess_key_len) + return -EINVAL; + + ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); + if (!ctx_arc4) + return -ENOMEM; + + cifs_arc4_setkey(ctx_arc4, sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + cifs_arc4_crypt(ctx_arc4, sess->sess_key, + (char *)authblob + sess_key_off, sess_key_len); + kfree_sensitive(ctx_arc4); + } + + return ret; +} + +/** + * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct + * negotiate blob + * @negblob: negotiate blob source pointer + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn) +{ + if (blob_len < sizeof(struct negotiate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(negblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + negblob->Signature); + return -EINVAL; + } + + conn->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); + return 0; +} + +/** + * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct + * challenge blob + * @chgblob: challenge blob source pointer to initialize + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn) +{ + struct target_info *tinfo; + wchar_t *name; + __u8 *target_name; + unsigned int flags, blob_off, blob_len, type, target_info_len = 0; + int len, uni_len, conv_len; + int cflags = conn->ntlmssp.client_flags; + + memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); + chgblob->MessageType = NtLmChallenge; + + flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | + NTLMSSP_NEGOTIATE_TARGET_INFO; + + if (cflags & NTLMSSP_NEGOTIATE_SIGN) { + flags |= NTLMSSP_NEGOTIATE_SIGN; + flags |= cflags & (NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_56); + } + + if (cflags & NTLMSSP_NEGOTIATE_SEAL && smb3_encryption_negotiated(conn)) + flags |= NTLMSSP_NEGOTIATE_SEAL; + + if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + + if (cflags & NTLMSSP_REQUEST_TARGET) + flags |= NTLMSSP_REQUEST_TARGET; + + if (conn->use_spnego && + (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) + flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; + + if (cflags & NTLMSSP_NEGOTIATE_KEY_XCH) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + + chgblob->NegotiateFlags = cpu_to_le32(flags); + len = strlen(ksmbd_netbios_name()); + name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!name) + return -ENOMEM; + + conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + kfree(name); + return -EINVAL; + } + + uni_len = UNICODE_LEN(conv_len); + + blob_off = sizeof(struct challenge_message); + blob_len = blob_off + uni_len; + + chgblob->TargetName.Length = cpu_to_le16(uni_len); + chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); + chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); + + /* Initialize random conn challenge */ + get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); + memcpy(chgblob->Challenge, conn->ntlmssp.cryptkey, + CIFS_CRYPTO_KEY_SIZE); + + /* Add Target Information to security buffer */ + chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); + + target_name = (__u8 *)chgblob + blob_off; + memcpy(target_name, name, uni_len); + tinfo = (struct target_info *)(target_name + uni_len); + + chgblob->TargetInfoArray.Length = 0; + /* Add target info list for NetBIOS/DNS settings */ + for (type = NTLMSSP_AV_NB_COMPUTER_NAME; + type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { + tinfo->Type = cpu_to_le16(type); + tinfo->Length = cpu_to_le16(uni_len); + memcpy(tinfo->Content, name, uni_len); + tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); + target_info_len += 4 + uni_len; + } + + /* Add terminator subblock */ + tinfo->Type = 0; + tinfo->Length = 0; + target_info_len += 4; + + chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); + chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); + blob_len += target_info_len; + kfree(name); + ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); + return blob_len; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + struct ksmbd_spnego_authen_response *resp; + struct ksmbd_user *user = NULL; + int retval; + + resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); + if (!resp) { + ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); + return -EINVAL; + } + + if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { + ksmbd_debug(AUTH, "krb5 authentication failure\n"); + retval = -EPERM; + goto out; + } + + if (*out_len <= resp->spnego_blob_len) { + ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", + *out_len, resp->spnego_blob_len); + retval = -EINVAL; + goto out; + } + + if (resp->session_key_len > sizeof(sess->sess_key)) { + ksmbd_debug(AUTH, "session key is too long\n"); + retval = -EINVAL; + goto out; + } + + user = ksmbd_alloc_user(&resp->login_response); + if (!user) { + ksmbd_debug(AUTH, "login failure\n"); + retval = -ENOMEM; + goto out; + } + sess->user = user; + + memcpy(sess->sess_key, resp->payload, resp->session_key_len); + memcpy(out_blob, resp->payload + resp->session_key_len, + resp->spnego_blob_len); + *out_len = resp->spnego_blob_len; + retval = 0; +out: + kvfree(resp); + return retval; +} +#else +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + return -EOPNOTSUPP; +} +#endif + +/** + * ksmbd_sign_smb2_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +/** + * ksmbd_sign_smb3_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_cmacaes(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), + key, + SMB2_CMACAES_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); + if (rc) { + ksmbd_debug(AUTH, "cmaces init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_CMACAES(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "cmaces update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +struct derivation { + struct kvec label; + struct kvec context; + bool binding; +}; + +static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct kvec label, struct kvec context, __u8 *key, + unsigned int key_size) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L128[4] = {0, 0, 0, 128}; + __u8 L256[4] = {0, 0, 1, 0}; + int rc; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + struct ksmbd_crypto_ctx *ctx; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(key, 0x0, key_size); + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto smb3signkey_ret; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + label.iov_base, + label.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with label\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); + if (rc) { + ksmbd_debug(AUTH, "could not update with zero\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + context.iov_base, + context.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with context\n"); + goto smb3signkey_ret; + } + + if (key_size == SMB3_ENC_DEC_KEY_SIZE && + (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); + else + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with L\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", + rc); + goto smb3signkey_ret; + } + + memcpy(key, hashptr, key_size); + +smb3signkey_ret: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int generate_smb3signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn, + const struct derivation *signing) +{ + int rc; + struct channel *chann; + char *key; + + chann = lookup_chann_list(sess, conn); + if (!chann) + return 0; + + if (conn->dialect >= SMB30_PROT_ID && signing->binding) + key = chann->smb3signingkey; + else + key = sess->smb3signingkey; + + rc = generate_key(conn, sess, signing->label, signing->context, key, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) + memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); + + ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + ksmbd_debug(AUTH, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, key); + return 0; +} + +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMB2AESCMAC"; + d.label.iov_len = 12; + d.context.iov_base = "SmbSign"; + d.context.iov_len = 8; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMBSigningKey"; + d.label.iov_len = 14; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return -ENOENT; + d.context.iov_base = preauth_sess->Preauth_HashValue; + } else { + d.context.iov_base = sess->Preauth_HashValue; + } + d.context.iov_len = 64; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +struct derivation_twin { + struct derivation encryption; + struct derivation decryption; +}; + +static int generate_smb3encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + const struct derivation_twin *ptwin) +{ + int rc; + + rc = generate_key(conn, sess, ptwin->encryption.label, + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + rc = generate_key(conn, sess, ptwin->decryption.label, + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); + ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } else { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } + return 0; +} + +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerOut"; + d->context.iov_len = 10; + + d = &twin.decryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerIn "; + d->context.iov_len = 10; + + return generate_smb3encryptionkey(conn, sess, &twin); +} + +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMBS2CCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + d = &twin.decryption; + d->label.iov_base = "SMBC2SCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + return generate_smb3encryptionkey(conn, sess, &twin); +} + +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash) +{ + int rc; + struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); + char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; + int msg_size = get_rfc1002_len(buf); + struct ksmbd_crypto_ctx *ctx = NULL; + + if (conn->preauth_info->Preauth_HashId != + SMB2_PREAUTH_INTEGRITY_SHA512) + return -EINVAL; + + ctx = ksmbd_crypto_ctx_find_sha512(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha512\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA512(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash) +{ + int rc; + struct ksmbd_crypto_ctx *ctx = NULL; + + ctx = ksmbd_crypto_ctx_find_sha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha256\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, + int enc, u8 *key) +{ + struct ksmbd_session *sess; + u8 *ses_enc_key; + + if (enc) + sess = work->sess; + else + sess = ksmbd_session_lookup_all(work->conn, ses_id); + if (!sess) + return -EINVAL; + + ses_enc_key = enc ? sess->smb3encryptionkey : + sess->smb3decryptionkey; + memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + + return 0; +} + +static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, + unsigned int buflen) +{ + void *addr; + + if (is_vmalloc_addr(buf)) + addr = vmalloc_to_page(buf); + else + addr = virt_to_page(buf); + sg_set_page(sg, addr, buflen, offset_in_page(buf)); +} + +static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + u8 *sign) +{ + struct scatterlist *sg; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; + + if (!nvec) + return NULL; + + for (i = 0; i < nvec - 1; i++) { + unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; + + if (is_vmalloc_addr(iov[i + 1].iov_base)) { + nr_entries[i] = ((kaddr + iov[i + 1].iov_len + + PAGE_SIZE - 1) >> PAGE_SHIFT) - + (kaddr >> PAGE_SHIFT); + } else { + nr_entries[i]++; + } + total_entries += nr_entries[i]; + } + + /* Add two entries for transform header and signature */ + total_entries += 2; + + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); + if (!sg) + return NULL; + + sg_init_table(sg, total_entries); + smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); + for (i = 0; i < nvec - 1; i++) { + void *data = iov[i + 1].iov_base; + int len = iov[i + 1].iov_len; + + if (is_vmalloc_addr(data)) { + int j, offset = offset_in_page(data); + + for (j = 0; j < nr_entries[i]; j++) { + unsigned int bytes = PAGE_SIZE - offset; + + if (!len) + break; + + if (bytes > len) + bytes = len; + + sg_set_page(&sg[sg_idx++], + vmalloc_to_page(data), bytes, + offset_in_page(data)); + + data += bytes; + len -= bytes; + offset = 0; + } + } else { + sg_set_page(&sg[sg_idx++], virt_to_page(data), len, + offset_in_page(data)); + } + } + smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); + return sg; +} + +int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, + unsigned int nvec, int enc) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int rc; + struct scatterlist *sg; + u8 sign[SMB2_SIGNATURE_SIZE] = {}; + u8 key[SMB3_ENC_DEC_KEY_SIZE]; + struct aead_request *req; + char *iv; + unsigned int iv_len; + struct crypto_aead *tfm; + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + struct ksmbd_crypto_ctx *ctx; + + rc = ksmbd_get_encryption_key(work, + le64_to_cpu(tr_hdr->SessionId), + enc, + key); + if (rc) { + pr_err("Could not get %scryption key\n", enc ? "en" : "de"); + return rc; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + ctx = ksmbd_crypto_ctx_find_gcm(); + else + ctx = ksmbd_crypto_ctx_find_ccm(); + if (!ctx) { + pr_err("crypto alloc failed\n"); + return -ENOMEM; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + tfm = CRYPTO_GCM(ctx); + else + tfm = CRYPTO_CCM(ctx); + + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); + else + rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); + if (rc) { + pr_err("Failed to set aead key %d\n", rc); + goto free_ctx; + } + + rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); + if (rc) { + pr_err("Failed to set authsize %d\n", rc); + goto free_ctx; + } + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + rc = -ENOMEM; + goto free_ctx; + } + + if (!enc) { + memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); + crypt_len += SMB2_SIGNATURE_SIZE; + } + + sg = ksmbd_init_sg(iov, nvec, sign); + if (!sg) { + pr_err("Failed to init sg\n"); + rc = -ENOMEM; + goto free_req; + } + + iv_len = crypto_aead_ivsize(tfm); + iv = kzalloc(iv_len, GFP_KERNEL); + if (!iv) { + rc = -ENOMEM; + goto free_sg; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + } else { + iv[0] = 3; + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + } + + aead_request_set_crypt(req, sg, sg, crypt_len, iv); + aead_request_set_ad(req, assoc_data_len); + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); + + if (enc) + rc = crypto_aead_encrypt(req); + else + rc = crypto_aead_decrypt(req); + if (rc) + goto free_iv; + + if (enc) + memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); + +free_iv: + kfree(iv); +free_sg: + kfree(sg); +free_req: + kfree(req); +free_ctx: + ksmbd_release_crypto_ctx(ctx); + return rc; +} diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h new file mode 100644 index 000000000000..362b6159a6cf --- /dev/null +++ b/fs/smb/server/auth.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __AUTH_H__ +#define __AUTH_H__ + +#include "ntlmssp.h" + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +#define AUTH_GSS_LENGTH 96 +#define AUTH_GSS_PADDING 0 +#else +#define AUTH_GSS_LENGTH 74 +#define AUTH_GSS_PADDING 6 +#endif + +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE 24 +#define CIFS_SMB1_SIGNATURE_SIZE 8 +#define CIFS_SMB1_SESSKEY_SIZE 16 + +#define KSMBD_AUTH_NTLMSSP 0x0001 +#define KSMBD_AUTH_KRB5 0x0002 +#define KSMBD_AUTH_MSKRB5 0x0004 +#define KSMBD_AUTH_KRB5U2U 0x0008 + +struct ksmbd_session; +struct ksmbd_conn; +struct ksmbd_work; +struct kvec; + +int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, + unsigned int nvec, int enc); +void ksmbd_copy_gss_neg_header(void *buf); +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey); +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn); +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn); +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len); +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash); +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash); +#endif diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c new file mode 100644 index 000000000000..e1d2be19cddf --- /dev/null +++ b/fs/smb/server/connection.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +static DEFINE_MUTEX(init_lock); + +static struct ksmbd_conn_ops default_conn_ops; + +LIST_HEAD(conn_list); +DECLARE_RWSEM(conn_list_lock); + +/** + * ksmbd_conn_free() - free resources of the connection instance + * + * @conn: connection instance to be cleand up + * + * During the thread termination, the corresponding conn instance + * resources(sock/memory) are released and finally the conn object is freed. + */ +void ksmbd_conn_free(struct ksmbd_conn *conn) +{ + down_write(&conn_list_lock); + list_del(&conn->conns_list); + up_write(&conn_list_lock); + + xa_destroy(&conn->sessions); + kvfree(conn->request_buf); + kfree(conn->preauth_info); + kfree(conn); +} + +/** + * ksmbd_conn_alloc() - initialize a new connection instance + * + * Return: ksmbd_conn struct on success, otherwise NULL + */ +struct ksmbd_conn *ksmbd_conn_alloc(void) +{ + struct ksmbd_conn *conn; + + conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); + if (!conn) + return NULL; + + conn->need_neg = true; + ksmbd_conn_set_new(conn); + conn->local_nls = load_nls("utf8"); + if (!conn->local_nls) + conn->local_nls = load_nls_default(); + if (IS_ENABLED(CONFIG_UNICODE)) + conn->um = utf8_load(UNICODE_AGE(12, 1, 0)); + else + conn->um = ERR_PTR(-EOPNOTSUPP); + if (IS_ERR(conn->um)) + conn->um = NULL; + atomic_set(&conn->req_running, 0); + atomic_set(&conn->r_count, 0); + conn->total_credits = 1; + conn->outstanding_credits = 0; + + init_waitqueue_head(&conn->req_running_q); + init_waitqueue_head(&conn->r_count_q); + INIT_LIST_HEAD(&conn->conns_list); + INIT_LIST_HEAD(&conn->requests); + INIT_LIST_HEAD(&conn->async_requests); + spin_lock_init(&conn->request_lock); + spin_lock_init(&conn->credits_lock); + ida_init(&conn->async_ida); + xa_init(&conn->sessions); + + spin_lock_init(&conn->llist_lock); + INIT_LIST_HEAD(&conn->lock_list); + + down_write(&conn_list_lock); + list_add(&conn->conns_list, &conn_list); + up_write(&conn_list_lock); + return conn; +} + +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) +{ + struct ksmbd_conn *t; + bool ret = false; + + down_read(&conn_list_lock); + list_for_each_entry(t, &conn_list, conns_list) { + if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) + continue; + + ret = true; + break; + } + up_read(&conn_list_lock); + return ret; +} + +void ksmbd_conn_enqueue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct list_head *requests_queue = NULL; + + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; + work->syncronous = true; + } + + if (requests_queue) { + atomic_inc(&conn->req_running); + spin_lock(&conn->request_lock); + list_add_tail(&work->request_entry, requests_queue); + spin_unlock(&conn->request_lock); + } +} + +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + int ret = 1; + + if (list_empty(&work->request_entry) && + list_empty(&work->async_request_entry)) + return 0; + + if (!work->multiRsp) + atomic_dec(&conn->req_running); + spin_lock(&conn->request_lock); + if (!work->multiRsp) { + list_del_init(&work->request_entry); + if (work->syncronous == false) + list_del_init(&work->async_request_entry); + ret = 0; + } + spin_unlock(&conn->request_lock); + + wake_up_all(&conn->req_running_q); + return ret; +} + +void ksmbd_conn_lock(struct ksmbd_conn *conn) +{ + mutex_lock(&conn->srv_mutex); +} + +void ksmbd_conn_unlock(struct ksmbd_conn *conn) +{ + mutex_unlock(&conn->srv_mutex); +} + +void ksmbd_all_conn_set_status(u64 sess_id, u32 status) +{ + struct ksmbd_conn *conn; + + down_read(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + if (conn->binding || xa_load(&conn->sessions, sess_id)) + WRITE_ONCE(conn->status, status); + } + up_read(&conn_list_lock); +} + +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) +{ + struct ksmbd_conn *bind_conn; + + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); + + down_read(&conn_list_lock); + list_for_each_entry(bind_conn, &conn_list, conns_list) { + if (bind_conn == conn) + continue; + + if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && + !ksmbd_conn_releasing(bind_conn) && + atomic_read(&bind_conn->req_running)) { + wait_event(bind_conn->req_running_q, + atomic_read(&bind_conn->req_running) == 0); + } + } + up_read(&conn_list_lock); +} + +int ksmbd_conn_write(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + size_t len = 0; + int sent; + struct kvec iov[3]; + int iov_idx = 0; + + if (!work->response_buf) { + pr_err("NULL response header\n"); + return -EINVAL; + } + + if (work->tr_buf) { + iov[iov_idx] = (struct kvec) { work->tr_buf, + sizeof(struct smb2_transform_hdr) + 4 }; + len += iov[iov_idx++].iov_len; + } + + if (work->aux_payload_sz) { + iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; + len += iov[iov_idx++].iov_len; + iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; + len += iov[iov_idx++].iov_len; + } else { + if (work->tr_buf) + iov[iov_idx].iov_len = work->resp_hdr_sz; + else + iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; + iov[iov_idx].iov_base = work->response_buf; + len += iov[iov_idx++].iov_len; + } + + ksmbd_conn_lock(conn); + sent = conn->transport->ops->writev(conn->transport, &iov[0], + iov_idx, len, + work->need_invalidate_rkey, + work->remote_key); + ksmbd_conn_unlock(conn); + + if (sent < 0) { + pr_err("Failed to send message: %d\n", sent); + return sent; + } + + return 0; +} + +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_read) + ret = conn->transport->ops->rdma_read(conn->transport, + buf, buflen, + desc, desc_len); + return ret; +} + +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_write) + ret = conn->transport->ops->rdma_write(conn->transport, + buf, buflen, + desc, desc_len); + return ret; +} + +bool ksmbd_conn_alive(struct ksmbd_conn *conn) +{ + if (!ksmbd_server_running()) + return false; + + if (ksmbd_conn_exiting(conn)) + return false; + + if (kthread_should_stop()) + return false; + + if (atomic_read(&conn->stats.open_files_count) > 0) + return true; + + /* + * Stop current session if the time that get last request from client + * is bigger than deadtime user configured and opening file count is + * zero. + */ + if (server_conf.deadtime > 0 && + time_after(jiffies, conn->last_active + server_conf.deadtime)) { + ksmbd_debug(CONN, "No response from client in %lu minutes\n", + server_conf.deadtime / SMB_ECHO_INTERVAL); + return false; + } + return true; +} + +#define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) +#define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4) + +/** + * ksmbd_conn_handler_loop() - session thread to listen on new smb requests + * @p: connection instance + * + * One thread each per connection + * + * Return: 0 on success + */ +int ksmbd_conn_handler_loop(void *p) +{ + struct ksmbd_conn *conn = (struct ksmbd_conn *)p; + struct ksmbd_transport *t = conn->transport; + unsigned int pdu_size, max_allowed_pdu_size; + char hdr_buf[4] = {0,}; + int size; + + mutex_init(&conn->srv_mutex); + __module_get(THIS_MODULE); + + if (t->ops->prepare && t->ops->prepare(t)) + goto out; + + conn->last_active = jiffies; + while (ksmbd_conn_alive(conn)) { + if (try_to_freeze()) + continue; + + kvfree(conn->request_buf); + conn->request_buf = NULL; + + size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1); + if (size != sizeof(hdr_buf)) + break; + + pdu_size = get_rfc1002_len(hdr_buf); + ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); + + if (ksmbd_conn_good(conn)) + max_allowed_pdu_size = + SMB3_MAX_MSGSIZE + conn->vals->max_write_size; + else + max_allowed_pdu_size = SMB3_MAX_MSGSIZE; + + if (pdu_size > max_allowed_pdu_size) { + pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", + pdu_size, max_allowed_pdu_size, + READ_ONCE(conn->status)); + break; + } + + /* + * Check maximum pdu size(0x00FFFFFF). + */ + if (pdu_size > MAX_STREAM_PROT_LEN) + break; + + if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE) + break; + + /* 4 for rfc1002 length field */ + /* 1 for implied bcc[0] */ + size = pdu_size + 4 + 1; + conn->request_buf = kvmalloc(size, GFP_KERNEL); + if (!conn->request_buf) + break; + + memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); + + /* + * We already read 4 bytes to find out PDU size, now + * read in PDU + */ + size = t->ops->read(t, conn->request_buf + 4, pdu_size, 2); + if (size < 0) { + pr_err("sock_read failed: %d\n", size); + break; + } + + if (size != pdu_size) { + pr_err("PDU error. Read: %d, Expected: %d\n", + size, pdu_size); + continue; + } + + if (!ksmbd_smb_request(conn)) + break; + + if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == + SMB2_PROTO_NUMBER) { + if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) + break; + } + + if (!default_conn_ops.process_fn) { + pr_err("No connection request callback\n"); + break; + } + + if (default_conn_ops.process_fn(conn)) { + pr_err("Cannot handle request\n"); + break; + } + } + +out: + ksmbd_conn_set_releasing(conn); + /* Wait till all reference dropped to the Server object*/ + wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); + + if (IS_ENABLED(CONFIG_UNICODE)) + utf8_unload(conn->um); + unload_nls(conn->local_nls); + if (default_conn_ops.terminate_fn) + default_conn_ops.terminate_fn(conn); + t->ops->disconnect(t); + module_put(THIS_MODULE); + return 0; +} + +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) +{ + default_conn_ops.process_fn = ops->process_fn; + default_conn_ops.terminate_fn = ops->terminate_fn; +} + +int ksmbd_conn_transport_init(void) +{ + int ret; + + mutex_lock(&init_lock); + ret = ksmbd_tcp_init(); + if (ret) { + pr_err("Failed to init TCP subsystem: %d\n", ret); + goto out; + } + + ret = ksmbd_rdma_init(); + if (ret) { + pr_err("Failed to init RDMA subsystem: %d\n", ret); + goto out; + } +out: + mutex_unlock(&init_lock); + return ret; +} + +static void stop_sessions(void) +{ + struct ksmbd_conn *conn; + struct ksmbd_transport *t; + +again: + down_read(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + struct task_struct *task; + + t = conn->transport; + task = t->handler; + if (task) + ksmbd_debug(CONN, "Stop session handler %s/%d\n", + task->comm, task_pid_nr(task)); + ksmbd_conn_set_exiting(conn); + if (t->ops->shutdown) { + up_read(&conn_list_lock); + t->ops->shutdown(t); + down_read(&conn_list_lock); + } + } + up_read(&conn_list_lock); + + if (!list_empty(&conn_list)) { + schedule_timeout_interruptible(HZ / 10); /* 100ms */ + goto again; + } +} + +void ksmbd_conn_transport_destroy(void) +{ + mutex_lock(&init_lock); + ksmbd_tcp_destroy(); + ksmbd_rdma_destroy(); + stop_sessions(); + mutex_unlock(&init_lock); +} diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h new file mode 100644 index 000000000000..ad8dfaa48ffb --- /dev/null +++ b/fs/smb/server/connection.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_CONNECTION_H__ +#define __KSMBD_CONNECTION_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smb_common.h" +#include "ksmbd_work.h" + +#define KSMBD_SOCKET_BACKLOG 16 + +enum { + KSMBD_SESS_NEW = 0, + KSMBD_SESS_GOOD, + KSMBD_SESS_EXITING, + KSMBD_SESS_NEED_RECONNECT, + KSMBD_SESS_NEED_NEGOTIATE, + KSMBD_SESS_RELEASING +}; + +struct ksmbd_stats { + atomic_t open_files_count; + atomic64_t request_served; +}; + +struct ksmbd_transport; + +struct ksmbd_conn { + struct smb_version_values *vals; + struct smb_version_ops *ops; + struct smb_version_cmds *cmds; + unsigned int max_cmds; + struct mutex srv_mutex; + int status; + unsigned int cli_cap; + char *request_buf; + struct ksmbd_transport *transport; + struct nls_table *local_nls; + struct unicode_map *um; + struct list_head conns_list; + /* smb session 1 per user */ + struct xarray sessions; + unsigned long last_active; + /* How many request are running currently */ + atomic_t req_running; + /* References which are made for this Server object*/ + atomic_t r_count; + unsigned int total_credits; + unsigned int outstanding_credits; + spinlock_t credits_lock; + wait_queue_head_t req_running_q; + wait_queue_head_t r_count_q; + /* Lock to protect requests list*/ + spinlock_t request_lock; + struct list_head requests; + struct list_head async_requests; + int connection_type; + struct ksmbd_stats stats; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + struct ntlmssp_auth ntlmssp; + + spinlock_t llist_lock; + struct list_head lock_list; + + struct preauth_integrity_info *preauth_info; + + bool need_neg; + unsigned int auth_mechs; + unsigned int preferred_auth_mech; + bool sign; + bool use_spnego:1; + __u16 cli_sec_mode; + __u16 srv_sec_mode; + /* dialect index that server chose */ + __u16 dialect; + + char *mechToken; + + struct ksmbd_conn_ops *conn_ops; + + /* Preauth Session Table */ + struct list_head preauth_sess_table; + + struct sockaddr_storage peer_addr; + + /* Identifier for async message */ + struct ida async_ida; + + __le16 cipher_type; + __le16 compress_algorithm; + bool posix_ext_supported; + bool signing_negotiated; + __le16 signing_algorithm; + bool binding; +}; + +struct ksmbd_conn_ops { + int (*process_fn)(struct ksmbd_conn *conn); + int (*terminate_fn)(struct ksmbd_conn *conn); +}; + +struct ksmbd_transport_ops { + int (*prepare)(struct ksmbd_transport *t); + void (*disconnect)(struct ksmbd_transport *t); + void (*shutdown)(struct ksmbd_transport *t); + int (*read)(struct ksmbd_transport *t, char *buf, + unsigned int size, int max_retries); + int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, + int size, bool need_invalidate_rkey, + unsigned int remote_key); + int (*rdma_read)(struct ksmbd_transport *t, + void *buf, unsigned int len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); + int (*rdma_write)(struct ksmbd_transport *t, + void *buf, unsigned int len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +}; + +struct ksmbd_transport { + struct ksmbd_conn *conn; + struct ksmbd_transport_ops *ops; + struct task_struct *handler; +}; + +#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) +#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) +#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) + +extern struct list_head conn_list; +extern struct rw_semaphore conn_list_lock; + +bool ksmbd_conn_alive(struct ksmbd_conn *conn); +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id); +struct ksmbd_conn *ksmbd_conn_alloc(void); +void ksmbd_conn_free(struct ksmbd_conn *conn); +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); +int ksmbd_conn_write(struct ksmbd_work *work); +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +void ksmbd_conn_enqueue_request(struct ksmbd_work *work); +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); +int ksmbd_conn_handler_loop(void *p); +int ksmbd_conn_transport_init(void); +void ksmbd_conn_transport_destroy(void); +void ksmbd_conn_lock(struct ksmbd_conn *conn); +void ksmbd_conn_unlock(struct ksmbd_conn *conn); + +/* + * WARNING + * + * This is a hack. We will move status to a proper place once we land + * a multi-sessions support. + */ +static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; +} + +static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT; +} + +static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_EXITING; +} + +static inline bool ksmbd_conn_releasing(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_RELEASING; +} + +static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_NEW); +} + +static inline void ksmbd_conn_set_good(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_GOOD); +} + +static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE); +} + +static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT); +} + +static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_EXITING); +} + +static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING); +} + +void ksmbd_all_conn_set_status(u64 sess_id, u32 status); +#endif /* __CONNECTION_H__ */ diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c new file mode 100644 index 000000000000..81488d04199d --- /dev/null +++ b/fs/smb/server/crypto_ctx.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "crypto_ctx.h" + +struct crypto_ctx_list { + spinlock_t ctx_lock; + int avail_ctx; + struct list_head idle_ctx; + wait_queue_head_t ctx_wait; +}; + +static struct crypto_ctx_list ctx_list; + +static inline void free_aead(struct crypto_aead *aead) +{ + if (aead) + crypto_free_aead(aead); +} + +static void free_shash(struct shash_desc *shash) +{ + if (shash) { + crypto_free_shash(shash->tfm); + kfree(shash); + } +} + +static struct crypto_aead *alloc_aead(int id) +{ + struct crypto_aead *tfm = NULL; + + switch (id) { + case CRYPTO_AEAD_AES_GCM: + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + break; + case CRYPTO_AEAD_AES_CCM: + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + break; + default: + pr_err("Does not support encrypt ahead(id : %d)\n", id); + return NULL; + } + + if (IS_ERR(tfm)) { + pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); + return NULL; + } + + return tfm; +} + +static struct shash_desc *alloc_shash_desc(int id) +{ + struct crypto_shash *tfm = NULL; + struct shash_desc *shash; + + switch (id) { + case CRYPTO_SHASH_HMACMD5: + tfm = crypto_alloc_shash("hmac(md5)", 0, 0); + break; + case CRYPTO_SHASH_HMACSHA256: + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + break; + case CRYPTO_SHASH_CMACAES: + tfm = crypto_alloc_shash("cmac(aes)", 0, 0); + break; + case CRYPTO_SHASH_SHA256: + tfm = crypto_alloc_shash("sha256", 0, 0); + break; + case CRYPTO_SHASH_SHA512: + tfm = crypto_alloc_shash("sha512", 0, 0); + break; + default: + return NULL; + } + + if (IS_ERR(tfm)) + return NULL; + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!shash) + crypto_free_shash(tfm); + else + shash->tfm = tfm; + return shash; +} + +static void ctx_free(struct ksmbd_crypto_ctx *ctx) +{ + int i; + + for (i = 0; i < CRYPTO_SHASH_MAX; i++) + free_shash(ctx->desc[i]); + for (i = 0; i < CRYPTO_AEAD_MAX; i++) + free_aead(ctx->ccmaes[i]); + kfree(ctx); +} + +static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (1) { + spin_lock(&ctx_list.ctx_lock); + if (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + spin_unlock(&ctx_list.ctx_lock); + return ctx; + } + + if (ctx_list.avail_ctx > num_online_cpus()) { + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + + ctx_list.avail_ctx++; + spin_unlock(&ctx_list.ctx_lock); + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) { + spin_lock(&ctx_list.ctx_lock); + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + break; + } + return ctx; +} + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) +{ + if (!ctx) + return; + + spin_lock(&ctx_list.ctx_lock); + if (ctx_list.avail_ctx <= num_online_cpus()) { + list_add(&ctx->list, &ctx_list.idle_ctx); + spin_unlock(&ctx_list.ctx_lock); + wake_up(&ctx_list.ctx_wait); + return; + } + + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + ctx_free(ctx); +} + +static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_SHASH_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->desc[id]) + return ctx; + + ctx->desc[id] = alloc_shash_desc(id); + if (ctx->desc[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); +} + +static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_AEAD_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->ccmaes[id]) + return ctx; + + ctx->ccmaes[id] = alloc_aead(id); + if (ctx->ccmaes[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM); +} + +void ksmbd_crypto_destroy(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + ctx_free(ctx); + } +} + +int ksmbd_crypto_create(void) +{ + struct ksmbd_crypto_ctx *ctx; + + spin_lock_init(&ctx_list.ctx_lock); + INIT_LIST_HEAD(&ctx_list.idle_ctx); + init_waitqueue_head(&ctx_list.ctx_wait); + ctx_list.avail_ctx = 1; + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + list_add(&ctx->list, &ctx_list.idle_ctx); + return 0; +} diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h new file mode 100644 index 000000000000..4a367c62f653 --- /dev/null +++ b/fs/smb/server/crypto_ctx.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __CRYPTO_CTX_H__ +#define __CRYPTO_CTX_H__ + +#include +#include + +enum { + CRYPTO_SHASH_HMACMD5 = 0, + CRYPTO_SHASH_HMACSHA256, + CRYPTO_SHASH_CMACAES, + CRYPTO_SHASH_SHA256, + CRYPTO_SHASH_SHA512, + CRYPTO_SHASH_MAX, +}; + +enum { + CRYPTO_AEAD_AES_GCM = 16, + CRYPTO_AEAD_AES_CCM, + CRYPTO_AEAD_MAX, +}; + +enum { + CRYPTO_BLK_ECBDES = 32, + CRYPTO_BLK_MAX, +}; + +struct ksmbd_crypto_ctx { + struct list_head list; + + struct shash_desc *desc[CRYPTO_SHASH_MAX]; + struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; +}; + +#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) +#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) +#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) +#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) +#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) + +#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) +#define CRYPTO_HMACSHA256_TFM(c)\ + ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) +#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) +#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) +#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) + +#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) +#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); +void ksmbd_crypto_destroy(void); +int ksmbd_crypto_create(void); + +#endif /* __CRYPTO_CTX_H__ */ diff --git a/fs/smb/server/glob.h b/fs/smb/server/glob.h new file mode 100644 index 000000000000..5b8f3e0ebdb3 --- /dev/null +++ b/fs/smb/server/glob.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_GLOB_H +#define __KSMBD_GLOB_H + +#include + +#include "unicode.h" +#include "vfs_cache.h" + +#define KSMBD_VERSION "3.4.2" + +extern int ksmbd_debug_types; + +#define KSMBD_DEBUG_SMB BIT(0) +#define KSMBD_DEBUG_AUTH BIT(1) +#define KSMBD_DEBUG_VFS BIT(2) +#define KSMBD_DEBUG_OPLOCK BIT(3) +#define KSMBD_DEBUG_IPC BIT(4) +#define KSMBD_DEBUG_CONN BIT(5) +#define KSMBD_DEBUG_RDMA BIT(6) +#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ + KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ + KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ + KSMBD_DEBUG_RDMA) + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#ifdef SUBMOD_NAME +#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt +#else +#define pr_fmt(fmt) "ksmbd: " fmt +#endif + +#define ksmbd_debug(type, fmt, ...) \ + do { \ + if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ + pr_info(fmt, ##__VA_ARGS__); \ + } while (0) + +#define UNICODE_LEN(x) ((x) * 2) + +#endif /* __KSMBD_GLOB_H */ diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h new file mode 100644 index 000000000000..ce866ff159bf --- /dev/null +++ b/fs/smb/server/ksmbd_netlink.h @@ -0,0 +1,412 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * + * linux-ksmbd-devel@lists.sourceforge.net + */ + +#ifndef _LINUX_KSMBD_SERVER_H +#define _LINUX_KSMBD_SERVER_H + +#include + +/* + * This is a userspace ABI to communicate data between ksmbd and user IPC + * daemon using netlink. This is added to track and cache user account DB + * and share configuration info from userspace. + * + * - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) + * This event is to check whether user IPC daemon is alive. If user IPC + * daemon is dead, ksmbd keep existing connection till disconnecting and + * new connection will be denied. + * + * - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) + * This event is to receive the information that initializes the ksmbd + * server from the user IPC daemon and to start the server. The global + * section parameters are given from smb.conf as initialization + * information. + * + * - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) + * This event is to shutdown ksmbd server. + * + * - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) + * This event is to get user account info to user IPC daemon. + * + * - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response) + * This event is to get net share configuration info. + * + * - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response) + * This event is to get session and tree connect info. + * + * - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) + * This event is to send tree disconnect info to user IPC daemon. + * + * - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) + * This event is to send logout request to user IPC daemon. + * + * - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) + * This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, + * samr to be processed in userspace. + * + * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) + * This event is to make kerberos authentication to be processed in + * userspace. + */ + +#define KSMBD_GENL_NAME "SMBD_GENL" +#define KSMBD_GENL_VERSION 0x01 + +#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 +#define KSMBD_REQ_MAX_HASH_SZ 18 +#define KSMBD_REQ_MAX_SHARE_NAME 64 + +/* + * IPC heartbeat frame to check whether user IPC daemon is alive. + */ +struct ksmbd_heartbeat { + __u32 handle; +}; + +/* + * Global config flags. + */ +#define KSMBD_GLOBAL_FLAG_INVALID (0) +#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) +#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) + +/* + * IPC request for ksmbd server startup + */ +struct ksmbd_startup_request { + __u32 flags; /* Flags for global config */ + __s32 signing; /* Signing enabled */ + __s8 min_prot[16]; /* The minimum SMB protocol version */ + __s8 max_prot[16]; /* The maximum SMB protocol version */ + __s8 netbios_name[16]; + __s8 work_group[64]; /* Workgroup */ + __s8 server_string[64]; /* Server string */ + __u16 tcp_port; /* tcp port */ + __u16 ipc_timeout; /* + * specifies the number of seconds + * server will wait for the userspace to + * reply to heartbeat frames. + */ + __u32 deadtime; /* Number of minutes of inactivity */ + __u32 file_max; /* Limits the maximum number of open files */ + __u32 smb2_max_write; /* MAX write size */ + __u32 smb2_max_read; /* MAX read size */ + __u32 smb2_max_trans; /* MAX trans size */ + __u32 share_fake_fscaps; /* + * Support some special application that + * makes QFSINFO calls to check whether + * we set the SPARSE_FILES bit (0x40). + */ + __u32 sub_auth[3]; /* Subauth value for Security ID */ + __u32 smb2_max_credits; /* MAX credits */ + __u32 smbd_max_io_size; /* smbd read write size */ + __u32 max_connections; /* Number of maximum simultaneous connections */ + __u32 reserved[126]; /* Reserved room */ + __u32 ifc_list_sz; /* interfaces list size */ + __s8 ____payload[]; +}; + +#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) + +/* + * IPC request to shutdown ksmbd server. + */ +struct ksmbd_shutdown_request { + __s32 reserved[16]; +}; + +/* + * IPC user login request. + */ +struct ksmbd_login_request { + __u32 handle; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC user login response. + */ +struct ksmbd_login_response { + __u32 handle; + __u32 gid; /* group id */ + __u32 uid; /* user id */ + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u16 status; + __u16 hash_sz; /* hash size */ + __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC request to fetch net share config. + */ +struct ksmbd_share_config_request { + __u32 handle; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC response to the net share config request. + */ +struct ksmbd_share_config_response { + __u32 handle; + __u32 flags; + __u16 create_mask; + __u16 directory_mask; + __u16 force_create_mode; + __u16 force_directory_mode; + __u16 force_uid; + __u16 force_gid; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; + __u32 reserved[112]; /* Reserved room */ + __u32 veto_list_sz; + __s8 ____payload[]; +}; + +#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) + +static inline char * +ksmbd_share_config_path(struct ksmbd_share_config_response *sc) +{ + char *p = sc->____payload; + + if (sc->veto_list_sz) + p += sc->veto_list_sz + 1; + + return p; +} + +/* + * IPC request for tree connection. This request include session and tree + * connect info from client. + */ +struct ksmbd_tree_connect_request { + __u32 handle; + __u16 account_flags; + __u16 flags; + __u64 session_id; + __u64 connect_id; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; + __s8 peer_addr[64]; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Response structure for tree connection. + */ +struct ksmbd_tree_connect_response { + __u32 handle; + __u16 status; + __u16 connection_flags; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Request struture to disconnect tree connection. + */ +struct ksmbd_tree_disconnect_request { + __u64 session_id; /* session id */ + __u64 connect_id; /* tree connection id */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Response structure to logout user account. + */ +struct ksmbd_logout_request { + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 account_flags; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * RPC command structure to send rpc request like srvsvc or wkssvc to + * IPC user daemon. + */ +struct ksmbd_rpc_command { + __u32 handle; + __u32 flags; + __u32 payload_sz; + __u8 payload[]; +}; + +/* + * IPC Request Kerberos authentication + */ +struct ksmbd_spnego_authen_request { + __u32 handle; + __u16 spnego_blob_len; /* the length of spnego_blob */ + __u8 spnego_blob[]; /* + * the GSS token from SecurityBuffer of + * SMB2 SESSION SETUP request + */ +}; + +/* + * Response data which includes the GSS token and the session key generated by + * user daemon. + */ +struct ksmbd_spnego_authen_response { + __u32 handle; + struct ksmbd_login_response login_response; /* + * the login response with + * a user identified by the + * GSS token from a client + */ + __u16 session_key_len; /* the length of the session key */ + __u16 spnego_blob_len; /* + * the length of the GSS token which will be + * stored in SecurityBuffer of SMB2 SESSION + * SETUP response + */ + __u8 payload[]; /* session key + AP_REP */ +}; + +/* + * This also used as NETLINK attribute type value. + * + * NOTE: + * Response message type value should be equal to + * request message type value + 1. + */ +enum ksmbd_event { + KSMBD_EVENT_UNSPEC = 0, + KSMBD_EVENT_HEARTBEAT_REQUEST, + + KSMBD_EVENT_STARTING_UP, + KSMBD_EVENT_SHUTTING_DOWN, + + KSMBD_EVENT_LOGIN_REQUEST, + KSMBD_EVENT_LOGIN_RESPONSE = 5, + + KSMBD_EVENT_SHARE_CONFIG_REQUEST, + KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + + KSMBD_EVENT_TREE_CONNECT_REQUEST, + KSMBD_EVENT_TREE_CONNECT_RESPONSE, + + KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, + + KSMBD_EVENT_LOGOUT_REQUEST, + + KSMBD_EVENT_RPC_REQUEST, + KSMBD_EVENT_RPC_RESPONSE, + + KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, + + KSMBD_EVENT_MAX +}; + +/* + * Enumeration for IPC tree connect status. + */ +enum KSMBD_TREE_CONN_STATUS { + KSMBD_TREE_CONN_STATUS_OK = 0, + KSMBD_TREE_CONN_STATUS_NOMEM, + KSMBD_TREE_CONN_STATUS_NO_SHARE, + KSMBD_TREE_CONN_STATUS_NO_USER, + KSMBD_TREE_CONN_STATUS_INVALID_USER, + KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, + KSMBD_TREE_CONN_STATUS_CONN_EXIST, + KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, + KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, + KSMBD_TREE_CONN_STATUS_ERROR, +}; + +/* + * User config flags. + */ +#define KSMBD_USER_FLAG_INVALID (0) +#define KSMBD_USER_FLAG_OK BIT(0) +#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1) +#define KSMBD_USER_FLAG_BAD_UID BIT(2) +#define KSMBD_USER_FLAG_BAD_USER BIT(3) +#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) +#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) + +/* + * Share config flags. + */ +#define KSMBD_SHARE_FLAG_INVALID (0) +#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) +#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) +#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) +#define KSMBD_SHARE_FLAG_READONLY BIT(3) +#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) +#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) +#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) +#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) +#define KSMBD_SHARE_FLAG_PIPE BIT(8) +#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) +#define KSMBD_SHARE_FLAG_STREAMS BIT(11) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) +#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) +#define KSMBD_SHARE_FLAG_UPDATE BIT(14) + +/* + * Tree connect request flags. + */ +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1) + +/* + * Tree connect flags. + */ +#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0) +#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1) +#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2) +#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3) +#define KSMBD_TREE_CONN_FLAG_UPDATE BIT(4) + +/* + * RPC over IPC. + */ +#define KSMBD_RPC_METHOD_RETURN BIT(0) +#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1) +#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2) +#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_OPEN_METHOD BIT(4) +#define KSMBD_RPC_WRITE_METHOD BIT(5) +#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_CLOSE_METHOD BIT(7) +#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9) +#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10) +#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) +#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) + +/* + * RPC status definitions. + */ +#define KSMBD_RPC_OK 0 +#define KSMBD_RPC_EBAD_FUNC 0x00000001 +#define KSMBD_RPC_EACCESS_DENIED 0x00000005 +#define KSMBD_RPC_EBAD_FID 0x00000006 +#define KSMBD_RPC_ENOMEM 0x00000008 +#define KSMBD_RPC_EBAD_DATA 0x0000000D +#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 +#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 +#define KSMBD_RPC_EMORE_DATA 0x000000EA +#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C +#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 + +#define KSMBD_CONFIG_OPT_DISABLED 0 +#define KSMBD_CONFIG_OPT_ENABLED 1 +#define KSMBD_CONFIG_OPT_AUTO 2 +#define KSMBD_CONFIG_OPT_MANDATORY 3 + +#endif /* _LINUX_KSMBD_SERVER_H */ diff --git a/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 b/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 new file mode 100644 index 000000000000..0065f191b54b --- /dev/null +++ b/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 @@ -0,0 +1,31 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegTokenInit ::= + SEQUENCE { + mechTypes + [0] MechTypeList, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit, + negTokenTarg + [1] ANY + } diff --git a/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 b/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 new file mode 100644 index 000000000000..1151933e7b9c --- /dev/null +++ b/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 @@ -0,0 +1,19 @@ +GSSAPI ::= + CHOICE { + negTokenInit + [0] ANY, + negTokenTarg + [1] NegTokenTarg + } + +NegTokenTarg ::= + SEQUENCE { + negResult + [0] ENUMERATED OPTIONAL, + supportedMech + [1] OBJECT IDENTIFIER OPTIONAL, + responseToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c new file mode 100644 index 000000000000..14b9caebf7a4 --- /dev/null +++ b/fs/smb/server/ksmbd_work.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "server.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/ksmbd_ida.h" + +static struct kmem_cache *work_cache; +static struct workqueue_struct *ksmbd_wq; + +struct ksmbd_work *ksmbd_alloc_work_struct(void) +{ + struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); + + if (work) { + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + INIT_LIST_HEAD(&work->request_entry); + INIT_LIST_HEAD(&work->async_request_entry); + INIT_LIST_HEAD(&work->fp_entry); + INIT_LIST_HEAD(&work->interim_entry); + } + return work; +} + +void ksmbd_free_work_struct(struct ksmbd_work *work) +{ + WARN_ON(work->saved_cred != NULL); + + kvfree(work->response_buf); + kvfree(work->aux_payload_buf); + kfree(work->tr_buf); + kvfree(work->request_buf); + if (work->async_id) + ksmbd_release_id(&work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +} + +void ksmbd_work_pool_destroy(void) +{ + kmem_cache_destroy(work_cache); +} + +int ksmbd_work_pool_init(void) +{ + work_cache = kmem_cache_create("ksmbd_work_cache", + sizeof(struct ksmbd_work), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!work_cache) + return -ENOMEM; + return 0; +} + +int ksmbd_workqueue_init(void) +{ + ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); + if (!ksmbd_wq) + return -ENOMEM; + return 0; +} + +void ksmbd_workqueue_destroy(void) +{ + destroy_workqueue(ksmbd_wq); + ksmbd_wq = NULL; +} + +bool ksmbd_queue_work(struct ksmbd_work *work) +{ + return queue_work(ksmbd_wq, &work->work); +} diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h new file mode 100644 index 000000000000..5ece58e40c97 --- /dev/null +++ b/fs/smb/server/ksmbd_work.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_WORK_H__ +#define __KSMBD_WORK_H__ + +#include +#include + +struct ksmbd_conn; +struct ksmbd_session; +struct ksmbd_tree_connect; + +enum { + KSMBD_WORK_ACTIVE = 0, + KSMBD_WORK_CANCELLED, + KSMBD_WORK_CLOSED, +}; + +/* one of these for every pending CIFS request at the connection */ +struct ksmbd_work { + /* Server corresponding to this mid */ + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_tree_connect *tcon; + + /* Pointer to received SMB header */ + void *request_buf; + /* Response buffer */ + void *response_buf; + + /* Read data buffer */ + void *aux_payload_buf; + + /* Next cmd hdr in compound req buf*/ + int next_smb2_rcv_hdr_off; + /* Next cmd hdr in compound rsp buf*/ + int next_smb2_rsp_hdr_off; + + /* + * Current Local FID assigned compound response if SMB2 CREATE + * command is present in compound request + */ + u64 compound_fid; + u64 compound_pfid; + u64 compound_sid; + + const struct cred *saved_cred; + + /* Number of granted credits */ + unsigned int credits_granted; + + /* response smb header size */ + unsigned int resp_hdr_sz; + unsigned int response_sz; + /* Read data count */ + unsigned int aux_payload_sz; + + void *tr_buf; + + unsigned char state; + /* Multiple responses for one request e.g. SMB ECHO */ + bool multiRsp:1; + /* No response for cancelled request */ + bool send_no_response:1; + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ + bool syncronous:1; + bool need_invalidate_rkey:1; + + unsigned int remote_key; + /* cancel works */ + int async_id; + void **cancel_argv; + void (*cancel_fn)(void **argv); + + struct work_struct work; + /* List head at conn->requests */ + struct list_head request_entry; + /* List head at conn->async_requests */ + struct list_head async_request_entry; + struct list_head fp_entry; + struct list_head interim_entry; +}; + +/** + * ksmbd_resp_buf_next - Get next buffer on compound response. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) +{ + return work->response_buf + work->next_smb2_rsp_hdr_off + 4; +} + +/** + * ksmbd_req_buf_next - Get next buffer on compound request. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) +{ + return work->request_buf + work->next_smb2_rcv_hdr_off + 4; +} + +struct ksmbd_work *ksmbd_alloc_work_struct(void); +void ksmbd_free_work_struct(struct ksmbd_work *work); + +void ksmbd_work_pool_destroy(void); +int ksmbd_work_pool_init(void); + +int ksmbd_workqueue_init(void); +void ksmbd_workqueue_destroy(void); +bool ksmbd_queue_work(struct ksmbd_work *work); + +#endif /* __KSMBD_WORK_H__ */ diff --git a/fs/smb/server/mgmt/ksmbd_ida.c b/fs/smb/server/mgmt/ksmbd_ida.c new file mode 100644 index 000000000000..54194d959a5e --- /dev/null +++ b/fs/smb/server/mgmt/ksmbd_ida.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "ksmbd_ida.h" + +static inline int __acquire_id(struct ida *ida, int from, int to) +{ + return ida_simple_get(ida, from, to, GFP_KERNEL); +} + +int ksmbd_acquire_smb2_tid(struct ida *ida) +{ + int id; + + id = __acquire_id(ida, 1, 0xFFFFFFFF); + + return id; +} + +int ksmbd_acquire_smb2_uid(struct ida *ida) +{ + int id; + + id = __acquire_id(ida, 1, 0); + if (id == 0xFFFE) + id = __acquire_id(ida, 1, 0); + + return id; +} + +int ksmbd_acquire_async_msg_id(struct ida *ida) +{ + return __acquire_id(ida, 1, 0); +} + +int ksmbd_acquire_id(struct ida *ida) +{ + return __acquire_id(ida, 0, 0); +} + +void ksmbd_release_id(struct ida *ida, int id) +{ + ida_simple_remove(ida, id); +} diff --git a/fs/smb/server/mgmt/ksmbd_ida.h b/fs/smb/server/mgmt/ksmbd_ida.h new file mode 100644 index 000000000000..2bc07b16cfde --- /dev/null +++ b/fs/smb/server/mgmt/ksmbd_ida.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_IDA_MANAGEMENT_H__ +#define __KSMBD_IDA_MANAGEMENT_H__ + +#include +#include + +/* + * 2.2.1.6.7 TID Generation + * The value 0xFFFF MUST NOT be used as a valid TID. All other + * possible values for TID, including zero (0x0000), are valid. + * The value 0xFFFF is used to specify all TIDs or no TID, + * depending upon the context in which it is used. + */ +int ksmbd_acquire_smb2_tid(struct ida *ida); + +/* + * 2.2.1.6.8 UID Generation + * The value 0xFFFE was declared reserved in the LAN Manager 1.0 + * documentation, so a value of 0xFFFE SHOULD NOT be used as a + * valid UID.<21> All other possible values for a UID, excluding + * zero (0x0000), are valid. + */ +int ksmbd_acquire_smb2_uid(struct ida *ida); +int ksmbd_acquire_async_msg_id(struct ida *ida); + +int ksmbd_acquire_id(struct ida *ida); + +void ksmbd_release_id(struct ida *ida, int id); +#endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c new file mode 100644 index 000000000000..328a412259dc --- /dev/null +++ b/fs/smb/server/mgmt/share_config.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "share_config.h" +#include "user_config.h" +#include "user_session.h" +#include "../transport_ipc.h" +#include "../misc.h" + +#define SHARE_HASH_BITS 3 +static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); +static DECLARE_RWSEM(shares_table_lock); + +struct ksmbd_veto_pattern { + char *pattern; + struct list_head list; +}; + +static unsigned int share_name_hash(const char *name) +{ + return jhash(name, strlen(name), 0); +} + +static void kill_share(struct ksmbd_share_config *share) +{ + while (!list_empty(&share->veto_list)) { + struct ksmbd_veto_pattern *p; + + p = list_entry(share->veto_list.next, + struct ksmbd_veto_pattern, + list); + list_del(&p->list); + kfree(p->pattern); + kfree(p); + } + + if (share->path) + path_put(&share->vfs_path); + kfree(share->name); + kfree(share->path); + kfree(share); +} + +void ksmbd_share_config_del(struct ksmbd_share_config *share) +{ + down_write(&shares_table_lock); + hash_del(&share->hlist); + up_write(&shares_table_lock); +} + +void __ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + ksmbd_share_config_del(share); + kill_share(share); +} + +static struct ksmbd_share_config * +__get_share_config(struct ksmbd_share_config *share) +{ + if (!atomic_inc_not_zero(&share->refcount)) + return NULL; + return share; +} + +static struct ksmbd_share_config *__share_lookup(const char *name) +{ + struct ksmbd_share_config *share; + unsigned int key = share_name_hash(name); + + hash_for_each_possible(shares_table, share, hlist, key) { + if (!strcmp(name, share->name)) + return share; + } + return NULL; +} + +static int parse_veto_list(struct ksmbd_share_config *share, + char *veto_list, + int veto_list_sz) +{ + int sz = 0; + + if (!veto_list_sz) + return 0; + + while (veto_list_sz > 0) { + struct ksmbd_veto_pattern *p; + + sz = strlen(veto_list); + if (!sz) + break; + + p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->pattern = kstrdup(veto_list, GFP_KERNEL); + if (!p->pattern) { + kfree(p); + return -ENOMEM; + } + + list_add(&p->list, &share->veto_list); + + veto_list += sz + 1; + veto_list_sz -= (sz + 1); + } + + return 0; +} + +static struct ksmbd_share_config *share_config_request(struct unicode_map *um, + const char *name) +{ + struct ksmbd_share_config_response *resp; + struct ksmbd_share_config *share = NULL; + struct ksmbd_share_config *lookup; + int ret; + + resp = ksmbd_ipc_share_config_request(name); + if (!resp) + return NULL; + + if (resp->flags == KSMBD_SHARE_FLAG_INVALID) + goto out; + + if (*resp->share_name) { + char *cf_resp_name; + bool equal; + + cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name); + if (IS_ERR(cf_resp_name)) + goto out; + equal = !strcmp(cf_resp_name, name); + kfree(cf_resp_name); + if (!equal) + goto out; + } + + share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); + if (!share) + goto out; + + share->flags = resp->flags; + atomic_set(&share->refcount, 1); + INIT_LIST_HEAD(&share->veto_list); + share->name = kstrdup(name, GFP_KERNEL); + + if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + share->path = kstrdup(ksmbd_share_config_path(resp), + GFP_KERNEL); + if (share->path) + share->path_sz = strlen(share->path); + share->create_mask = resp->create_mask; + share->directory_mask = resp->directory_mask; + share->force_create_mode = resp->force_create_mode; + share->force_directory_mode = resp->force_directory_mode; + share->force_uid = resp->force_uid; + share->force_gid = resp->force_gid; + ret = parse_veto_list(share, + KSMBD_SHARE_CONFIG_VETO_LIST(resp), + resp->veto_list_sz); + if (!ret && share->path) { + ret = kern_path(share->path, 0, &share->vfs_path); + if (ret) { + ksmbd_debug(SMB, "failed to access '%s'\n", + share->path); + /* Avoid put_path() */ + kfree(share->path); + share->path = NULL; + } + } + if (ret || !share->name) { + kill_share(share); + share = NULL; + goto out; + } + } + + down_write(&shares_table_lock); + lookup = __share_lookup(name); + if (lookup) + lookup = __get_share_config(lookup); + if (!lookup) { + hash_add(shares_table, &share->hlist, share_name_hash(name)); + } else { + kill_share(share); + share = lookup; + } + up_write(&shares_table_lock); + +out: + kvfree(resp); + return share; +} + +struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, + const char *name) +{ + struct ksmbd_share_config *share; + + down_read(&shares_table_lock); + share = __share_lookup(name); + if (share) + share = __get_share_config(share); + up_read(&shares_table_lock); + + if (share) + return share; + return share_config_request(um, name); +} + +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename) +{ + struct ksmbd_veto_pattern *p; + + list_for_each_entry(p, &share->veto_list, list) { + if (match_wildcard(p->pattern, filename)) + return true; + } + return false; +} diff --git a/fs/smb/server/mgmt/share_config.h b/fs/smb/server/mgmt/share_config.h new file mode 100644 index 000000000000..3fd338293942 --- /dev/null +++ b/fs/smb/server/mgmt/share_config.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SHARE_CONFIG_MANAGEMENT_H__ +#define __SHARE_CONFIG_MANAGEMENT_H__ + +#include +#include +#include +#include + +struct ksmbd_share_config { + char *name; + char *path; + + unsigned int path_sz; + unsigned int flags; + struct list_head veto_list; + + struct path vfs_path; + + atomic_t refcount; + struct hlist_node hlist; + unsigned short create_mask; + unsigned short directory_mask; + unsigned short force_create_mode; + unsigned short force_directory_mode; + unsigned short force_uid; + unsigned short force_gid; +}; + +#define KSMBD_SHARE_INVALID_UID ((__u16)-1) +#define KSMBD_SHARE_INVALID_GID ((__u16)-1) + +static inline int share_config_create_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_create_mode) { + if (!posix_mode) + return share->create_mask; + else + return posix_mode & share->create_mask; + } + return share->force_create_mode & share->create_mask; +} + +static inline int share_config_directory_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_directory_mode) { + if (!posix_mode) + return share->directory_mask; + else + return posix_mode & share->directory_mask; + } + + return share->force_directory_mode & share->directory_mask; +} + +static inline int test_share_config_flag(struct ksmbd_share_config *share, + int flag) +{ + return share->flags & flag; +} + +void ksmbd_share_config_del(struct ksmbd_share_config *share); +void __ksmbd_share_config_put(struct ksmbd_share_config *share); + +static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + if (!atomic_dec_and_test(&share->refcount)) + return; + __ksmbd_share_config_put(share); +} + +struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, + const char *name); +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename); +#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c new file mode 100644 index 000000000000..f07a05f37651 --- /dev/null +++ b/fs/smb/server/mgmt/tree_connect.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "../transport_ipc.h" +#include "../connection.h" + +#include "tree_connect.h" +#include "user_config.h" +#include "share_config.h" +#include "user_session.h" + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name) +{ + struct ksmbd_tree_conn_status status = {-ENOENT, NULL}; + struct ksmbd_tree_connect_response *resp = NULL; + struct ksmbd_share_config *sc; + struct ksmbd_tree_connect *tree_conn = NULL; + struct sockaddr *peer_addr; + int ret; + + sc = ksmbd_share_config_get(conn->um, share_name); + if (!sc) + return status; + + tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); + if (!tree_conn) { + status.ret = -ENOMEM; + goto out_error; + } + + tree_conn->id = ksmbd_acquire_tree_conn_id(sess); + if (tree_conn->id < 0) { + status.ret = -EINVAL; + goto out_error; + } + + peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn); + resp = ksmbd_ipc_tree_connect_request(sess, + sc, + tree_conn, + peer_addr); + if (!resp) { + status.ret = -EINVAL; + goto out_error; + } + + status.ret = resp->status; + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + goto out_error; + + tree_conn->flags = resp->connection_flags; + if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) { + struct ksmbd_share_config *new_sc; + + ksmbd_share_config_del(sc); + new_sc = ksmbd_share_config_get(conn->um, share_name); + if (!new_sc) { + pr_err("Failed to update stale share config\n"); + status.ret = -ESTALE; + goto out_error; + } + ksmbd_share_config_put(sc); + sc = new_sc; + } + + tree_conn->user = sess->user; + tree_conn->share_conf = sc; + status.tree_conn = tree_conn; + + ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, + GFP_KERNEL)); + if (ret) { + status.ret = -ENOMEM; + goto out_error; + } + kvfree(resp); + return status; + +out_error: + if (tree_conn) + ksmbd_release_tree_conn_id(sess, tree_conn->id); + ksmbd_share_config_put(sc); + kfree(tree_conn); + kvfree(resp); + return status; +} + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) +{ + int ret; + + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); + ksmbd_release_tree_conn_id(sess, tree_conn->id); + xa_erase(&sess->tree_conns, tree_conn->id); + ksmbd_share_config_put(tree_conn->share_conf); + kfree(tree_conn); + return ret; +} + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tcon; + + tcon = xa_load(&sess->tree_conns, id); + if (tcon) { + if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) + tcon = NULL; + } + + return tcon; +} + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tc; + + tc = ksmbd_tree_conn_lookup(sess, id); + if (tc) + return tc->share_conf; + return NULL; +} + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) +{ + int ret = 0; + struct ksmbd_tree_connect *tc; + unsigned long id; + + if (!sess) + return -EINVAL; + + xa_for_each(&sess->tree_conns, id, tc) + ret |= ksmbd_tree_conn_disconnect(sess, tc); + xa_destroy(&sess->tree_conns); + return ret; +} diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h new file mode 100644 index 000000000000..700df36cf3e3 --- /dev/null +++ b/fs/smb/server/mgmt/tree_connect.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __TREE_CONNECT_MANAGEMENT_H__ +#define __TREE_CONNECT_MANAGEMENT_H__ + +#include + +#include "../ksmbd_netlink.h" + +struct ksmbd_share_config; +struct ksmbd_user; +struct ksmbd_conn; + +#define TREE_CONN_EXPIRE 1 + +struct ksmbd_tree_connect { + int id; + + unsigned int flags; + struct ksmbd_share_config *share_conf; + struct ksmbd_user *user; + + struct list_head list; + + int maximal_access; + bool posix_extensions; + unsigned long status; +}; + +struct ksmbd_tree_conn_status { + unsigned int ret; + struct ksmbd_tree_connect *tree_conn; +}; + +static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, + int flag) +{ + return tree_conn->flags & flag; +} + +struct ksmbd_session; + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name); + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn); + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id); + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id); + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); + +#endif /* __TREE_CONNECT_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c new file mode 100644 index 000000000000..279d00feff21 --- /dev/null +++ b/fs/smb/server/mgmt/user_config.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include + +#include "user_config.h" +#include "../transport_ipc.h" + +struct ksmbd_user *ksmbd_login_user(const char *account) +{ + struct ksmbd_login_response *resp; + struct ksmbd_user *user = NULL; + + resp = ksmbd_ipc_login_request(account); + if (!resp) + return NULL; + + if (!(resp->status & KSMBD_USER_FLAG_OK)) + goto out; + + user = ksmbd_alloc_user(resp); +out: + kvfree(resp); + return user; +} + +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) +{ + struct ksmbd_user *user = NULL; + + user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); + if (!user) + return NULL; + + user->name = kstrdup(resp->account, GFP_KERNEL); + user->flags = resp->status; + user->gid = resp->gid; + user->uid = resp->uid; + user->passkey_sz = resp->hash_sz; + user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); + if (user->passkey) + memcpy(user->passkey, resp->hash, resp->hash_sz); + + if (!user->name || !user->passkey) { + kfree(user->name); + kfree(user->passkey); + kfree(user); + user = NULL; + } + return user; +} + +void ksmbd_free_user(struct ksmbd_user *user) +{ + ksmbd_ipc_logout_request(user->name, user->flags); + kfree(user->name); + kfree(user->passkey); + kfree(user); +} + +int ksmbd_anonymous_user(struct ksmbd_user *user) +{ + if (user->name[0] == '\0') + return 1; + return 0; +} + +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) +{ + if (strcmp(u1->name, u2->name)) + return false; + if (memcmp(u1->passkey, u2->passkey, u1->passkey_sz)) + return false; + + return true; +} diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h new file mode 100644 index 000000000000..6a44109617f1 --- /dev/null +++ b/fs/smb/server/mgmt/user_config.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_CONFIG_MANAGEMENT_H__ +#define __USER_CONFIG_MANAGEMENT_H__ + +#include "../glob.h" + +struct ksmbd_user { + unsigned short flags; + + unsigned int uid; + unsigned int gid; + + char *name; + + size_t passkey_sz; + char *passkey; + unsigned int failed_login_count; +}; + +static inline bool user_guest(struct ksmbd_user *user) +{ + return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT; +} + +static inline void set_user_flag(struct ksmbd_user *user, int flag) +{ + user->flags |= flag; +} + +static inline int test_user_flag(struct ksmbd_user *user, int flag) +{ + return user->flags & flag; +} + +static inline void set_user_guest(struct ksmbd_user *user) +{ +} + +static inline char *user_passkey(struct ksmbd_user *user) +{ + return user->passkey; +} + +static inline char *user_name(struct ksmbd_user *user) +{ + return user->name; +} + +static inline unsigned int user_uid(struct ksmbd_user *user) +{ + return user->uid; +} + +static inline unsigned int user_gid(struct ksmbd_user *user) +{ + return user->gid; +} + +struct ksmbd_user *ksmbd_login_user(const char *account); +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); +void ksmbd_free_user(struct ksmbd_user *user); +int ksmbd_anonymous_user(struct ksmbd_user *user); +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); +#endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c new file mode 100644 index 000000000000..ea4b56d570fb --- /dev/null +++ b/fs/smb/server/mgmt/user_session.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "ksmbd_ida.h" +#include "user_session.h" +#include "user_config.h" +#include "tree_connect.h" +#include "../transport_ipc.h" +#include "../connection.h" +#include "../vfs_cache.h" + +static DEFINE_IDA(session_ida); + +#define SESSION_HASH_BITS 3 +static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); +static DECLARE_RWSEM(sessions_table_lock); + +struct ksmbd_session_rpc { + int id; + unsigned int method; + struct list_head list; +}; + +static void free_channel_list(struct ksmbd_session *sess) +{ + struct channel *chann; + unsigned long index; + + xa_for_each(&sess->ksmbd_chann_list, index, chann) { + xa_erase(&sess->ksmbd_chann_list, index); + kfree(chann); + } + + xa_destroy(&sess->ksmbd_chann_list); +} + +static void __session_rpc_close(struct ksmbd_session *sess, + struct ksmbd_session_rpc *entry) +{ + struct ksmbd_rpc_command *resp; + + resp = ksmbd_rpc_close(sess, entry->id); + if (!resp) + pr_err("Unable to close RPC pipe %d\n", entry->id); + + kvfree(resp); + ksmbd_rpc_id_free(entry->id); + kfree(entry); +} + +static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) +{ + struct ksmbd_session_rpc *entry; + + while (!list_empty(&sess->rpc_handle_list)) { + entry = list_entry(sess->rpc_handle_list.next, + struct ksmbd_session_rpc, + list); + + list_del(&entry->list); + __session_rpc_close(sess, entry); + } +} + +static int __rpc_method(char *rpc_name) +{ + if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc")) + return KSMBD_RPC_SRVSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc")) + return KSMBD_RPC_WKSSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman")) + return KSMBD_RPC_RAP_METHOD; + + if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr")) + return KSMBD_RPC_SAMR_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) + return KSMBD_RPC_LSARPC_METHOD_INVOKE; + + pr_err("Unsupported RPC: %s\n", rpc_name); + return 0; +} + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) +{ + struct ksmbd_session_rpc *entry; + struct ksmbd_rpc_command *resp; + int method; + + method = __rpc_method(rpc_name); + if (!method) + return -EINVAL; + + entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + if (!entry) + return -EINVAL; + + list_add(&entry->list, &sess->rpc_handle_list); + entry->method = method; + entry->id = ksmbd_ipc_id_alloc(); + if (entry->id < 0) + goto free_entry; + + resp = ksmbd_rpc_open(sess, entry->id); + if (!resp) + goto free_id; + + kvfree(resp); + return entry->id; +free_id: + ksmbd_rpc_id_free(entry->id); +free_entry: + list_del(&entry->list); + kfree(entry); + return -EINVAL; +} + +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) { + list_del(&entry->list); + __session_rpc_close(sess, entry); + break; + } + } +} + +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) + return entry->method; + } + return 0; +} + +void ksmbd_session_destroy(struct ksmbd_session *sess) +{ + if (!sess) + return; + + if (sess->user) + ksmbd_free_user(sess->user); + + ksmbd_tree_conn_session_logoff(sess); + ksmbd_destroy_file_table(&sess->file_table); + ksmbd_session_rpc_clear_list(sess); + free_channel_list(sess); + kfree(sess->Preauth_HashValue); + ksmbd_release_id(&session_ida, sess->id); + kfree(sess); +} + +static struct ksmbd_session *__session_lookup(unsigned long long id) +{ + struct ksmbd_session *sess; + + hash_for_each_possible(sessions_table, sess, hlist, id) { + if (id == sess->id) { + sess->last_active = jiffies; + return sess; + } + } + return NULL; +} + +static void ksmbd_expire_session(struct ksmbd_conn *conn) +{ + unsigned long id; + struct ksmbd_session *sess; + + down_write(&sessions_table_lock); + xa_for_each(&conn->sessions, id, sess) { + if (sess->state != SMB2_SESSION_VALID || + time_after(jiffies, + sess->last_active + SMB2_SESSION_TIMEOUT)) { + xa_erase(&conn->sessions, sess->id); + hash_del(&sess->hlist); + ksmbd_session_destroy(sess); + continue; + } + } + up_write(&sessions_table_lock); +} + +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + sess->dialect = conn->dialect; + memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + ksmbd_expire_session(conn); + return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); +} + +static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) +{ + struct channel *chann; + + chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); + if (!chann) + return -ENOENT; + + kfree(chann); + return 0; +} + +void ksmbd_sessions_deregister(struct ksmbd_conn *conn) +{ + struct ksmbd_session *sess; + unsigned long id; + + down_write(&sessions_table_lock); + if (conn->binding) { + int bkt; + struct hlist_node *tmp; + + hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) { + if (!ksmbd_chann_del(conn, sess) && + xa_empty(&sess->ksmbd_chann_list)) { + hash_del(&sess->hlist); + ksmbd_session_destroy(sess); + } + } + } + + xa_for_each(&conn->sessions, id, sess) { + unsigned long chann_id; + struct channel *chann; + + xa_for_each(&sess->ksmbd_chann_list, chann_id, chann) { + if (chann->conn != conn) + ksmbd_conn_set_exiting(chann->conn); + } + + ksmbd_chann_del(conn, sess); + if (xa_empty(&sess->ksmbd_chann_list)) { + xa_erase(&conn->sessions, sess->id); + hash_del(&sess->hlist); + ksmbd_session_destroy(sess); + } + } + up_write(&sessions_table_lock); +} + +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + sess = xa_load(&conn->sessions, id); + if (sess) + sess->last_active = jiffies; + return sess; +} + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) +{ + struct ksmbd_session *sess; + + down_read(&sessions_table_lock); + sess = __session_lookup(id); + if (sess) + sess->last_active = jiffies; + up_read(&sessions_table_lock); + + return sess; +} + +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + sess = ksmbd_session_lookup(conn, id); + if (!sess && conn->binding) + sess = ksmbd_session_lookup_slowpath(id); + if (sess && sess->state != SMB2_SESSION_VALID) + sess = NULL; + return sess; +} + +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id) +{ + struct preauth_session *sess; + + sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); + if (!sess) + return NULL; + + sess->id = sess_id; + memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE); + list_add(&sess->preauth_entry, &conn->preauth_sess_table); + + return sess; +} + +static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, + unsigned long long id) +{ + return sess->id == id; +} + +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct preauth_session *sess = NULL; + + list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { + if (ksmbd_preauth_session_id_match(sess, id)) + return sess; + } + return NULL; +} + +static int __init_smb2_session(struct ksmbd_session *sess) +{ + int id = ksmbd_acquire_smb2_uid(&session_ida); + + if (id < 0) + return -EINVAL; + sess->id = id; + return 0; +} + +static struct ksmbd_session *__session_create(int protocol) +{ + struct ksmbd_session *sess; + int ret; + + if (protocol != CIFDS_SESSION_FLAG_SMB2) + return NULL; + + sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); + if (!sess) + return NULL; + + if (ksmbd_init_file_table(&sess->file_table)) + goto error; + + sess->last_active = jiffies; + sess->state = SMB2_SESSION_IN_PROGRESS; + set_session_flag(sess, protocol); + xa_init(&sess->tree_conns); + xa_init(&sess->ksmbd_chann_list); + INIT_LIST_HEAD(&sess->rpc_handle_list); + sess->sequence_number = 1; + + ret = __init_smb2_session(sess); + if (ret) + goto error; + + ida_init(&sess->tree_conn_ida); + + down_write(&sessions_table_lock); + hash_add(sessions_table, &sess->hlist, sess->id); + up_write(&sessions_table_lock); + + return sess; + +error: + ksmbd_session_destroy(sess); + return NULL; +} + +struct ksmbd_session *ksmbd_smb2_session_create(void) +{ + return __session_create(CIFDS_SESSION_FLAG_SMB2); +} + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) +{ + int id = -EINVAL; + + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); + + return id; +} + +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) +{ + if (id >= 0) + ksmbd_release_id(&sess->tree_conn_ida, id); +} diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h new file mode 100644 index 000000000000..51f38e5b61ab --- /dev/null +++ b/fs/smb/server/mgmt/user_session.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_SESSION_MANAGEMENT_H__ +#define __USER_SESSION_MANAGEMENT_H__ + +#include +#include + +#include "../smb_common.h" +#include "../ntlmssp.h" + +#define CIFDS_SESSION_FLAG_SMB2 BIT(1) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct ksmbd_file_table; + +struct channel { + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + struct ksmbd_conn *conn; +}; + +struct preauth_session { + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; + u64 id; + struct list_head preauth_entry; +}; + +struct ksmbd_session { + u64 id; + + __u16 dialect; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + + struct ksmbd_user *user; + unsigned int sequence_number; + unsigned int flags; + + bool sign; + bool enc; + bool is_anonymous; + + int state; + __u8 *Preauth_HashValue; + + char sess_key[CIFS_KEY_SIZE]; + + struct hlist_node hlist; + struct xarray ksmbd_chann_list; + struct xarray tree_conns; + struct ida tree_conn_ida; + struct list_head rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + + struct ksmbd_file_table file_table; + unsigned long last_active; +}; + +static inline int test_session_flag(struct ksmbd_session *sess, int bit) +{ + return sess->flags & bit; +} + +static inline void set_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags |= bit; +} + +static inline void clear_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags &= ~bit; +} + +struct ksmbd_session *ksmbd_smb2_session_create(void); + +void ksmbd_session_destroy(struct ksmbd_session *sess); + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +void ksmbd_sessions_deregister(struct ksmbd_conn *conn); +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id); +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id); +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); +#endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c new file mode 100644 index 000000000000..9e8afaa686e3 --- /dev/null +++ b/fs/smb/server/misc.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "misc.h" +#include "smb_common.h" +#include "connection.h" +#include "vfs.h" + +#include "mgmt/share_config.h" + +/** + * match_pattern() - compare a string with a pattern which might include + * wildcard '*' and '?' + * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR + * + * @str: string to compare with a pattern + * @len: string length + * @pattern: pattern string which might include wildcard '*' and '?' + * + * Return: 0 if pattern matched with the string, otherwise non zero value + */ +int match_pattern(const char *str, size_t len, const char *pattern) +{ + const char *s = str; + const char *p = pattern; + bool star = false; + + while (*s && len) { + switch (*p) { + case '?': + s++; + len--; + p++; + break; + case '*': + star = true; + str = s; + if (!*++p) + return true; + pattern = p; + break; + default: + if (tolower(*s) == tolower(*p)) { + s++; + len--; + p++; + } else { + if (!star) + return false; + str++; + s = str; + p = pattern; + } + break; + } + } + + if (*p == '*') + ++p; + return !*p; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char ch) +{ + /* check for control chars, wildcards etc. */ + if (!(ch & 0x80) && + (ch <= 0x1f || + ch == '?' || ch == '"' || ch == '<' || + ch == '>' || ch == '|' || ch == '*')) + return 0; + + return 1; +} + +int ksmbd_validate_filename(char *filename) +{ + while (*filename) { + char c = *filename; + + filename++; + if (!is_char_allowed(c)) { + ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); + return -ENOENT; + } + } + + return 0; +} + +static int ksmbd_validate_stream_name(char *stream_name) +{ + while (*stream_name) { + char c = *stream_name; + + stream_name++; + if (c == '/' || c == ':' || c == '\\') { + pr_err("Stream name validation failed: %c\n", c); + return -ENOENT; + } + } + + return 0; +} + +int parse_stream_name(char *filename, char **stream_name, int *s_type) +{ + char *stream_type; + char *s_name; + int rc = 0; + + s_name = filename; + filename = strsep(&s_name, ":"); + ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); + if (strchr(s_name, ':')) { + stream_type = s_name; + s_name = strsep(&stream_type, ":"); + + rc = ksmbd_validate_stream_name(s_name); + if (rc < 0) { + rc = -ENOENT; + goto out; + } + + ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, + stream_type); + if (!strncasecmp("$data", stream_type, 5)) + *s_type = DATA_STREAM; + else if (!strncasecmp("$index_allocation", stream_type, 17)) + *s_type = DIR_STREAM; + else + rc = -ENOENT; + } + + *stream_name = s_name; +out: + return rc; +} + +/** + * convert_to_nt_pathname() - extract and return windows path string + * whose share directory prefix was removed from file path + * @share: ksmbd_share_config pointer + * @path: path to report + * + * Return : windows path string or error + */ + +char *convert_to_nt_pathname(struct ksmbd_share_config *share, + const struct path *path) +{ + char *pathname, *ab_pathname, *nt_pathname; + int share_path_len = share->path_sz; + + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return ERR_PTR(-EACCES); + + ab_pathname = d_path(path, pathname, PATH_MAX); + if (IS_ERR(ab_pathname)) { + nt_pathname = ERR_PTR(-EACCES); + goto free_pathname; + } + + if (strncmp(ab_pathname, share->path, share_path_len)) { + nt_pathname = ERR_PTR(-EACCES); + goto free_pathname; + } + + nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL); + if (!nt_pathname) { + nt_pathname = ERR_PTR(-ENOMEM); + goto free_pathname; + } + if (ab_pathname[share_path_len] == '\0') + strcpy(nt_pathname, "/"); + strcat(nt_pathname, &ab_pathname[share_path_len]); + + ksmbd_conv_path_to_windows(nt_pathname); + +free_pathname: + kfree(pathname); + return nt_pathname; +} + +int get_nlink(struct kstat *st) +{ + int nlink; + + nlink = st->nlink; + if (S_ISDIR(st->mode)) + nlink--; + + return nlink; +} + +void ksmbd_conv_path_to_unix(char *path) +{ + strreplace(path, '\\', '/'); +} + +void ksmbd_strip_last_slash(char *path) +{ + int len = strlen(path); + + while (len && path[len - 1] == '/') { + path[len - 1] = '\0'; + len--; + } +} + +void ksmbd_conv_path_to_windows(char *path) +{ + strreplace(path, '/', '\\'); +} + +char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) +{ + char *cf_name; + int cf_len; + + cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL); + if (!cf_name) + return ERR_PTR(-ENOMEM); + + if (IS_ENABLED(CONFIG_UNICODE) && um) { + const struct qstr q_name = {.name = name, .len = strlen(name)}; + + cf_len = utf8_casefold(um, &q_name, cf_name, + KSMBD_REQ_MAX_SHARE_NAME); + if (cf_len < 0) + goto out_ascii; + + return cf_name; + } + +out_ascii: + cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME); + if (cf_len < 0) { + kfree(cf_name); + return ERR_PTR(-E2BIG); + } + + for (; *cf_name; ++cf_name) + *cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name; + return cf_name - cf_len; +} + +/** + * ksmbd_extract_sharename() - get share name from tree connect request + * @treename: buffer containing tree name and share name + * + * Return: share name on success, otherwise error + */ +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename) +{ + const char *name = treename, *pos = strrchr(name, '\\'); + + if (pos) + name = (pos + 1); + + /* caller has to free the memory */ + return ksmbd_casefold_sharename(um, name); +} + +/** + * convert_to_unix_name() - convert windows name to unix format + * @share: ksmbd_share_config pointer + * @name: file name that is relative to share + * + * Return: converted name on success, otherwise NULL + */ +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) +{ + int no_slash = 0, name_len, path_len; + char *new_name; + + if (name[0] == '/') + name++; + + path_len = share->path_sz; + name_len = strlen(name); + new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + if (!new_name) + return new_name; + + memcpy(new_name, share->path, path_len); + if (new_name[path_len - 1] != '/') { + new_name[path_len] = '/'; + no_slash = 1; + } + + memcpy(new_name + path_len + no_slash, name, name_len); + path_len += name_len + no_slash; + new_name[path_len] = 0x00; + return new_name; +} + +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len) +{ + char *conv; + int sz = min(4 * d_info->name_len, PATH_MAX); + + if (!sz) + return NULL; + + conv = kmalloc(sz, GFP_KERNEL); + if (!conv) + return NULL; + + /* XXX */ + *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name, + d_info->name_len, local_nls, 0); + *conv_len *= 2; + + /* We allocate buffer twice bigger than needed. */ + conv[*conv_len] = 0x00; + conv[*conv_len + 1] = 0x00; + return conv; +} + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} + +/* Convert the Unix UTC into NT UTC. */ +inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; +} + +inline long long ksmbd_systime(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return ksmbd_UnixTimeToNT(ts); +} diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h new file mode 100644 index 000000000000..1facfcd21200 --- /dev/null +++ b/fs/smb/server/misc.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_MISC_H__ +#define __KSMBD_MISC_H__ + +struct ksmbd_share_config; +struct nls_table; +struct kstat; +struct ksmbd_file; + +int match_pattern(const char *str, size_t len, const char *pattern); +int ksmbd_validate_filename(char *filename); +int parse_stream_name(char *filename, char **stream_name, int *s_type); +char *convert_to_nt_pathname(struct ksmbd_share_config *share, + const struct path *path); +int get_nlink(struct kstat *st); +void ksmbd_conv_path_to_unix(char *path); +void ksmbd_strip_last_slash(char *path); +void ksmbd_conv_path_to_windows(char *path); +char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name); +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); + +#define KSMBD_DIR_INFO_ALIGNMENT 8 +struct ksmbd_dir_info; +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len); + +#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); +u64 ksmbd_UnixTimeToNT(struct timespec64 t); +long long ksmbd_systime(void); +#endif /* __KSMBD_MISC_H__ */ diff --git a/fs/smb/server/ndr.c b/fs/smb/server/ndr.c new file mode 100644 index 000000000000..4d9e0b54e3db --- /dev/null +++ b/fs/smb/server/ndr.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include + +#include "glob.h" +#include "ndr.h" + +static inline char *ndr_get_field(struct ndr *n) +{ + return n->data + n->offset; +} + +static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) +{ + char *data; + + data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + if (!data) + return -ENOMEM; + + n->data = data; + n->length += 1024; + memset(n->data + n->offset, 0, 1024); + return 0; +} + +static int ndr_write_int16(struct ndr *n, __u16 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le16 *)ndr_get_field(n) = cpu_to_le16(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int32(struct ndr *n, __u32 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le32 *)ndr_get_field(n) = cpu_to_le32(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int64(struct ndr *n, __u64 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le64 *)ndr_get_field(n) = cpu_to_le64(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + return 0; +} + +static int ndr_write_string(struct ndr *n, char *value) +{ + size_t sz; + + sz = strlen(value) + 1; + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_string(struct ndr *n, void *value, size_t sz) +{ + int len; + + if (n->offset + sz > n->length) + return -EINVAL; + + len = strnlen(ndr_get_field(n), sz); + if (value) + memcpy(value, ndr_get_field(n), len); + len++; + n->offset += len; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->offset + sz > n->length) + return -EINVAL; + + if (value) + memcpy(value, ndr_get_field(n), sz); + n->offset += sz; + return 0; +} + +static int ndr_read_int16(struct ndr *n, __u16 *value) +{ + if (n->offset + sizeof(__u16) > n->length) + return -EINVAL; + + if (value) + *value = le16_to_cpu(*(__le16 *)ndr_get_field(n)); + n->offset += sizeof(__u16); + return 0; +} + +static int ndr_read_int32(struct ndr *n, __u32 *value) +{ + if (n->offset + sizeof(__u32) > n->length) + return -EINVAL; + + if (value) + *value = le32_to_cpu(*(__le32 *)ndr_get_field(n)); + n->offset += sizeof(__u32); + return 0; +} + +static int ndr_read_int64(struct ndr *n, __u64 *value) +{ + if (n->offset + sizeof(__u64) > n->length) + return -EINVAL; + + if (value) + *value = le64_to_cpu(*(__le64 *)ndr_get_field(n)); + n->offset += sizeof(__u64); + return 0; +} + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + int ret; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (da->version == 3) { + snprintf(hex_attr, 10, "0x%x", da->attr); + ret = ndr_write_string(n, hex_attr); + } else { + ret = ndr_write_string(n, ""); + } + if (ret) + return ret; + + ret = ndr_write_int16(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->flags); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->attr); + if (ret) + return ret; + + if (da->version == 3) { + ret = ndr_write_int32(n, da->ea_size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->alloc_size); + } else { + ret = ndr_write_int64(n, da->itime); + } + if (ret) + return ret; + + ret = ndr_write_int64(n, da->create_time); + if (ret) + return ret; + + if (da->version == 3) + ret = ndr_write_int64(n, da->change_time); + return ret; +} + +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12]; + unsigned int version2; + int ret; + + n->offset = 0; + ret = ndr_read_string(n, hex_attr, sizeof(hex_attr)); + if (ret) + return ret; + + ret = ndr_read_int16(n, &da->version); + if (ret) + return ret; + + if (da->version != 3 && da->version != 4) { + ksmbd_debug(VFS, "v%d version is not supported\n", da->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + + if (da->version != version2) { + ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", + da->version, version2); + return -EINVAL; + } + + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int32(n, &da->attr); + if (ret) + return ret; + + if (da->version == 4) { + ret = ndr_read_int64(n, &da->itime); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + } else { + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + } + + return ret; +} + +static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) +{ + int i, ret; + + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + ret = ndr_write_int32(n, 0); + if (ret) + return ret; + + for (i = 0; i < acl->count; i++) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + if (acl->entries[i].type == SMB_ACL_USER) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].uid); + } else if (acl->entries[i].type == SMB_ACL_GROUP) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].gid); + } + if (ret) + return ret; + + /* push permission */ + ret = ndr_write_int32(n, acl->entries[i].perm); + } + + return ret; +} + +int ndr_encode_posix_acl(struct ndr *n, + struct user_namespace *user_ns, + struct inode *inode, + struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl) +{ + unsigned int ref_id = 0x00020000; + int ret; + vfsuid_t vfsuid; + vfsgid_t vfsgid; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (acl) { + /* ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + + if (def_acl) { + /* DEFAULT ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + + vfsuid = i_uid_into_vfsuid(user_ns, inode); + ret = ndr_write_int64(n, from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid))); + if (ret) + return ret; + vfsgid = i_gid_into_vfsgid(user_ns, inode); + ret = ndr_write_int64(n, from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid))); + if (ret) + return ret; + ret = ndr_write_int32(n, inode->i_mode); + if (ret) + return ret; + + if (acl) { + ret = ndr_encode_posix_acl_entry(n, acl); + if (def_acl && !ret) + ret = ndr_encode_posix_acl_entry(n, def_acl); + } + return ret; +} + +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int ref_id = 0x00020004; + int ret; + + n->offset = 0; + n->length = 2048; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + ret = ndr_write_int16(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int16(n, 2); + if (ret) + return ret; + + ret = ndr_write_int32(n, ref_id); + if (ret) + return ret; + + /* push hash type and hash 64bytes */ + ret = ndr_write_int16(n, acl->hash_type); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->desc, acl->desc_len); + if (ret) + return ret; + + ret = ndr_write_int64(n, acl->current_time); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + /* push ndr for security descriptor */ + ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} + +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int version2; + int ret; + + n->offset = 0; + ret = ndr_read_int16(n, &acl->version); + if (ret) + return ret; + if (acl->version != 4) { + ksmbd_debug(VFS, "v%d version is not supported\n", acl->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + if (acl->version != version2) { + ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", + acl->version, version2); + return -EINVAL; + } + + /* Read Level */ + ret = ndr_read_int16(n, NULL); + if (ret) + return ret; + + /* Read Ref Id */ + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int16(n, &acl->hash_type); + if (ret) + return ret; + + ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ndr_read_bytes(n, acl->desc, 10); + if (strncmp(acl->desc, "posix_acl", 9)) { + pr_err("Invalid acl description : %s\n", acl->desc); + return -EINVAL; + } + + /* Read Time */ + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + /* Read Posix ACL hash */ + ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + acl->sd_size = n->length - n->offset; + acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + if (!acl->sd_buf) + return -ENOMEM; + + ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} diff --git a/fs/smb/server/ndr.h b/fs/smb/server/ndr.h new file mode 100644 index 000000000000..60ca265d1bb0 --- /dev/null +++ b/fs/smb/server/ndr.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +struct ndr { + char *data; + int offset; + int length; +}; + +#define NDR_NTSD_OFFSETOF 0xA0 + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns, + struct inode *inode, struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl); +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/smb/server/nterr.h b/fs/smb/server/nterr.h new file mode 100644 index 000000000000..2f358f88a018 --- /dev/null +++ b/fs/smb/server/nterr.h @@ -0,0 +1,543 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * NT error code constants + * Copyright (C) Andrew Tridgell 1992-2000 + * Copyright (C) John H Terpstra 1996-2000 + * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + * Copyright (C) Paul Ashton 1998-2000 + */ + +#ifndef _NTERR_H +#define _NTERR_H + +/* Win32 Status codes. */ +#define NT_STATUS_MORE_ENTRIES 0x0105 +#define NT_ERROR_INVALID_PARAMETER 0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a +#define NT_STATUS_1804 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c +#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) +/* + * Win32 Error codes extracted using a loop in smbclient then printing a netmon + * sniff to a file. + */ + +#define NT_STATUS_OK 0x0000 +#define NT_STATUS_SOME_UNMAPPED 0x0107 +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a +#define NT_STATUS_MEDIA_CHANGED 0x8000001c +#define NT_STATUS_END_OF_MEDIA 0x8000001e +#define NT_STATUS_MEDIA_CHECK 0x80000020 +#define NT_STATUS_NO_DATA_DETECTED 0x8000001c +#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d +#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 +#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 +#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) +#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) +#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) +#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) +#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) +#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) +#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) +#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) +#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) +#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) +#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) +#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) +#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) +#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) +#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) +#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) +#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) +#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) +#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) +#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) +#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) +#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) +#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) +#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) +#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) +#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a) +#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) +#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) +#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) +#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) +#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) +#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) +#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) +#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) +#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) +#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) +#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) +#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) +#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) +#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) +#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) +#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) +#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) +#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) +#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) +#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) +#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) +#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) +#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) +#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) +#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) +#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) +#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) +#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) +#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) +#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) +#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) +#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) +#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) +#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) +#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) +#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) +#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) +#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) +#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) +#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) +#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) +#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) +#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) +#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) +#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) +#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) +#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) +#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) +#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) +#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) +#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) +#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) +#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) +#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) +#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) +#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) +#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) +#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) +#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) +#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) +#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) +#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) +#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) +#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) +#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) +#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) +#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) +#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) +#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) +#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) +#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) +#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) +#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) +#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) +#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) +#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) +#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) +#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) +#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) +#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) +#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) +#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) +#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) +#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) +#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) +#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) +#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) +#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) +#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) +#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) +#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) +#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) +#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) +#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) +#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) +#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) +#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) +#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) +#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) +#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) +#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) +#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) +#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) +#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) +#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) +#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) +#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) +#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) +#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) +#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) +#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) +#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) +#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) +#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) +#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) +#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) +#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) +#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) +#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) +#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) +#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) +#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) +#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) +#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) +#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) +#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) +#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) +#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) +#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) +#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) +#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) +#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) +#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) +#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) +#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) +#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) +#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) +#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) +#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) +#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) +#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) +#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) +#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) +#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) +#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) +#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) +#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) +#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) +#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) +#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) +#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) +#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) +#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) +#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) +#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) +#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) +#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) +#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) +#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) +#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) +#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) +#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) +#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) +#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) +#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) +#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) +#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) +#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) +#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) +#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) +#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) +#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) +#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) +#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) +#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) +#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) +#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) +#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) +#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) +#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) +#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) +#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) +#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) +#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) +#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) +#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) +#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) +#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) +#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) +#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) +#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) +#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) +#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) +#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) +#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) +#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) +#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) +#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) +#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) +#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) +#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) +#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) +#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) +#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) +#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) +#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) +#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) +#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) +#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) +#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) +#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) +#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) +#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) +#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) +#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) +#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) +#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) +#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) +#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) +#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) +#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) +#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) +#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) +#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) +#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) +#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) +#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) +#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) +#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) +#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) +#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) +#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) +#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) +#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) +#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) +#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) +#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) +#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) +#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) +#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) +#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) +#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) +#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) +#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) +#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) +#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) +#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) +#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) +#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) +#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) +#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) +#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) +#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) +#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) +#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) +#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) +#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) +#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) +#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) +#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) +#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) +#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) +#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) +#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) +#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) +#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) +#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) +#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) +#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) +#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) +#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) +#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) +#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) +#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) +#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) +#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) +#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) +#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) +#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) +#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) +#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) +#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) +#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) +#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) +#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) +#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) +#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) +#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) +#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) +#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) +#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) +#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) +#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) +#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) +#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) +#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) +#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) +#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) +#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) +#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) +#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) +#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) +#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) +#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) +#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) +#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) +#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) +#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) +#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) +#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) +#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) +#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) +#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) +#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) +#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) +#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) +#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) +#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) +#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) +#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) +#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) +#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) +#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) +#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) +#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) +#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) +#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) +#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) +#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) +#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) +#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) +#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) +#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) +#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) +#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) +#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) +#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) +#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) +#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) +#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) +#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) +#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) +#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) +#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) +#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) +#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) +#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) +#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) +#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) +#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) +#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) +#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) +#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) +#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) +#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) +#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) +#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) +#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) +#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) +#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) +#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) +#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) +#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) +#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) +#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) +#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) +#define NT_STATUS_RETRY (0xC0000000 | 0x022d) +#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) +#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) +#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) +#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) +#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) +#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) +#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) +#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) +#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) +#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) +#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) +#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) +#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) +#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) +#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) +#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) +#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) +#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) +#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) +#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) +#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) +#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) +#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) +#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) +#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) +#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) +#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) +#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) +#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) +#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) +#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) +#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) +#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) +#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) +#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) +#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) +#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) +#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) +#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) +#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) +#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) +#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) +#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ +#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) +#define NT_STATUS_PENDING 0x00000103 +#endif /* _NTERR_H */ diff --git a/fs/smb/server/ntlmssp.h b/fs/smb/server/ntlmssp.h new file mode 100644 index 000000000000..f13153c18b4e --- /dev/null +++ b/fs/smb/server/ntlmssp.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2007 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +#ifndef __KSMBD_NTLMSSP_H +#define __KSMBD_NTLMSSP_H + +#define NTLMSSP_SIGNATURE "NTLMSSP" + +/* Security blob target info data */ +#define TGT_Name "KSMBD" + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) +#define CIFS_KEY_SIZE (40) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) + +/* Message Types */ +#define NtLmNegotiate cpu_to_le32(1) +#define NtLmChallenge cpu_to_le32(2) +#define NtLmAuthenticate cpu_to_le32(3) +#define UnknownMessage cpu_to_le32(8) + +/* Negotiate Flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ +/* define reserved9 0x08 */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ +#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ +/* defined reserved 8 0x0100 */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ +#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ +#define NTLMSSP_ANONYMOUS 0x0800 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 +#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ +/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 +#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ +#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 +/* #define reserved4 0x1000000 */ +#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */ +/* #define reserved3 0x4000000 */ +/* #define reserved2 0x8000000 */ +/* #define reserved1 0x10000000 */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + +/* Although typedefs are not commonly used for structure definitions */ +/* in the Linux kernel, in this particular case they are useful */ +/* to more closely match the standards document for NTLMSSP from */ +/* OpenGroup and to make the code more closely match the standard in */ +/* appearance */ + +struct security_buffer { + __le16 Length; + __le16 MaximumLength; + __le32 BufferOffset; /* offset to buffer */ +} __packed; + +struct target_info { + __le16 Type; + __le16 Length; + __u8 Content[]; +} __packed; + +struct negotiate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + struct security_buffer DomainName; /* RFC 1001 style and ASCII */ + struct security_buffer WorkstationName; /* RFC 1001 and ASCII */ + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char DomainString[]; + /* followed by WorkstationString */ +} __packed; + +struct challenge_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmChallenge = 2 */ + struct security_buffer TargetName; + __le32 NegotiateFlags; + __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; + __u8 Reserved[8]; + struct security_buffer TargetInfoArray; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ +} __packed; + +struct authenticate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmsAuthenticate = 3 */ + struct security_buffer LmChallengeResponse; + struct security_buffer NtChallengeResponse; + struct security_buffer DomainName; + struct security_buffer UserName; + struct security_buffer WorkstationName; + struct security_buffer SessionKey; + __le32 NegotiateFlags; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char UserString[]; +} __packed; + +struct ntlmv2_resp { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __packed; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + /* whether session key is per smb session */ + bool sesskey_per_smbsess; + /* sent by client in type 1 ntlmsssp exchange */ + __u32 client_flags; + /* sent by server in type 2 ntlmssp exchange */ + __u32 conn_flags; + /* sent to server */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; + /* used by ntlmssp */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; +}; +#endif /* __KSMBD_NTLMSSP_H */ diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c new file mode 100644 index 000000000000..4b210cdd7556 --- /dev/null +++ b/fs/smb/server/oplock.c @@ -0,0 +1,1722 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "glob.h" +#include "oplock.h" + +#include "smb_common.h" +#include "smbstatus.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" + +static LIST_HEAD(lease_table_list); +static DEFINE_RWLOCK(lease_list_lock); + +/** + * alloc_opinfo() - allocate a new opinfo object for oplock info + * @work: smb work + * @id: fid of open file + * @Tid: tree id of connection + * + * Return: allocated opinfo object on success, otherwise NULL + */ +static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, + u64 id, __u16 Tid) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct oplock_info *opinfo; + + opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); + if (!opinfo) + return NULL; + + opinfo->sess = sess; + opinfo->conn = conn; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + opinfo->pending_break = 0; + opinfo->fid = id; + opinfo->Tid = Tid; + INIT_LIST_HEAD(&opinfo->op_entry); + INIT_LIST_HEAD(&opinfo->interim_list); + init_waitqueue_head(&opinfo->oplock_q); + init_waitqueue_head(&opinfo->oplock_brk); + atomic_set(&opinfo->refcount, 1); + atomic_set(&opinfo->breaking_cnt, 0); + + return opinfo; +} + +static void lease_add_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + spin_lock(&lb->lb_lock); + list_add_rcu(&opinfo->lease_entry, &lb->lease_list); + spin_unlock(&lb->lb_lock); +} + +static void lease_del_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + if (!lb) + return; + + spin_lock(&lb->lb_lock); + if (list_empty(&opinfo->lease_entry)) { + spin_unlock(&lb->lb_lock); + return; + } + + list_del_init(&opinfo->lease_entry); + opinfo->o_lease->l_lb = NULL; + spin_unlock(&lb->lb_lock); +} + +static void lb_add(struct lease_table *lb) +{ + write_lock(&lease_list_lock); + list_add(&lb->l_entry, &lease_table_list); + write_unlock(&lease_list_lock); +} + +static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) +{ + struct lease *lease; + + lease = kmalloc(sizeof(struct lease), GFP_KERNEL); + if (!lease) + return -ENOMEM; + + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + lease->state = lctx->req_state; + lease->new_state = 0; + lease->flags = lctx->flags; + lease->duration = lctx->duration; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; + lease->epoch = 0; + INIT_LIST_HEAD(&opinfo->lease_entry); + opinfo->o_lease = lease; + + return 0; +} + +static void free_lease(struct oplock_info *opinfo) +{ + struct lease *lease; + + lease = opinfo->o_lease; + kfree(lease); +} + +static void free_opinfo(struct oplock_info *opinfo) +{ + if (opinfo->is_lease) + free_lease(opinfo); + kfree(opinfo); +} + +static inline void opinfo_free_rcu(struct rcu_head *rcu_head) +{ + struct oplock_info *opinfo; + + opinfo = container_of(rcu_head, struct oplock_info, rcu_head); + free_opinfo(opinfo); +} + +struct oplock_info *opinfo_get(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) +{ + struct oplock_info *opinfo; + + if (list_empty(&ci->m_op_list)) + return NULL; + + rcu_read_lock(); + opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, + op_entry); + if (opinfo) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + else { + atomic_inc(&opinfo->conn->r_count); + if (ksmbd_conn_releasing(opinfo->conn)) { + atomic_dec(&opinfo->conn->r_count); + atomic_dec(&opinfo->refcount); + opinfo = NULL; + } + } + } + + rcu_read_unlock(); + + return opinfo; +} + +static void opinfo_conn_put(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn; + + if (!opinfo) + return; + + conn = opinfo->conn; + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); + opinfo_put(opinfo); +} + +void opinfo_put(struct oplock_info *opinfo) +{ + if (!atomic_dec_and_test(&opinfo->refcount)) + return; + + call_rcu(&opinfo->rcu_head, opinfo_free_rcu); +} + +static void opinfo_add(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + write_lock(&ci->m_lock); + list_add_rcu(&opinfo->op_entry, &ci->m_op_list); + write_unlock(&ci->m_lock); +} + +static void opinfo_del(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + if (opinfo->is_lease) { + write_lock(&lease_list_lock); + lease_del_list(opinfo); + write_unlock(&lease_list_lock); + } + write_lock(&ci->m_lock); + list_del_rcu(&opinfo->op_entry); + write_unlock(&ci->m_lock); +} + +static unsigned long opinfo_count(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_read(&fp->f_ci->sop_count); + else + return atomic_read(&fp->f_ci->op_count); +} + +static void opinfo_count_inc(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_inc(&fp->f_ci->sop_count); + else + return atomic_inc(&fp->f_ci->op_count); +} + +static void opinfo_count_dec(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_dec(&fp->f_ci->sop_count); + else + return atomic_dec(&fp->f_ci->op_count); +} + +/** + * opinfo_write_to_read() - convert a write oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_handle_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + lease->state = lease->new_state; + opinfo->level = SMB2_OPLOCK_LEVEL_II; + return 0; +} + +/** + * opinfo_write_to_none() - convert a write oplock to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * opinfo_read_to_none() - convert a write read to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * lease_read_to_write() - upgrade lease state from read to write + * @opinfo: current lease info + * + * Return: 0 on success, otherwise -EINVAL + */ +int lease_read_to_write(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state |= SMB2_LEASE_WRITE_CACHING_LE; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + return 0; +} + +/** + * lease_none_upgrade() - upgrade lease state from none + * @opinfo: current lease info + * @new_state: new lease state + * + * Return: 0 on success, otherwise -EINVAL + */ +static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state == SMB2_LEASE_NONE_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state = new_state; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_II; + else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + else if (lease->state & SMB2_LEASE_READ_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + return 0; +} + +/** + * close_id_del_oplock() - release oplock object at file close time + * @fp: ksmbd file pointer + */ +void close_id_del_oplock(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return; + + opinfo = opinfo_get(fp); + if (!opinfo) + return; + + opinfo_del(opinfo); + + rcu_assign_pointer(fp->f_opinfo, NULL); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + opinfo->op_state = OPLOCK_CLOSING; + wake_up_interruptible_all(&opinfo->oplock_q); + if (opinfo->is_lease) { + atomic_set(&opinfo->breaking_cnt, 0); + wake_up_interruptible_all(&opinfo->oplock_brk); + } + } + + opinfo_count_dec(fp); + atomic_dec(&opinfo->refcount); + opinfo_put(opinfo); +} + +/** + * grant_write_oplock() - grant exclusive/batch oplock or write lease + * @opinfo_new: new oplock info object + * @req_oplock: request oplock + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) + opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + if (lctx) { + lease->state = lctx->req_state; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_read_oplock() - grant level2 oplock or read lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_read_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + opinfo_new->level = SMB2_OPLOCK_LEVEL_II; + + if (lctx) { + lease->state = SMB2_LEASE_READ_CACHING_LE; + if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_none_oplock() - grant none oplock or none lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_none_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; + + if (lctx) { + lease->state = 0; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +static inline int compare_guid_key(struct oplock_info *opinfo, + const char *guid1, const char *key1) +{ + const char *guid2, *key2; + + guid2 = opinfo->conn->ClientGUID; + key2 = opinfo->o_lease->lease_key; + if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && + !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) + return 1; + + return 0; +} + +/** + * same_client_has_lease() - check whether current lease request is + * from lease owner of file + * @ci: master file pointer + * @client_guid: Client GUID + * @lctx: lease context information + * + * Return: oplock(lease) object on success, otherwise NULL + */ +static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + char *client_guid, + struct lease_ctx_info *lctx) +{ + int ret; + struct lease *lease; + struct oplock_info *opinfo; + struct oplock_info *m_opinfo = NULL; + + if (!lctx) + return NULL; + + /* + * Compare lease key and client_guid to know request from same owner + * of same client + */ + read_lock(&ci->m_lock); + list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { + if (!opinfo->is_lease) + continue; + read_unlock(&ci->m_lock); + lease = opinfo->o_lease; + + ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); + if (ret) { + m_opinfo = opinfo; + /* skip upgrading lease about breaking lease */ + if (atomic_read(&opinfo->breaking_cnt)) { + read_lock(&ci->m_lock); + continue; + } + + /* upgrading lease */ + if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) == 1) { + if (lease->state == + (lctx->req_state & lease->state)) { + lease->state |= lctx->req_state; + if (lctx->req_state & + SMB2_LEASE_WRITE_CACHING_LE) + lease_read_to_write(opinfo); + } + } else if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) > 1) { + if (lctx->req_state == + (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + lease->state = lctx->req_state; + } + + if (lctx->req_state && lease->state == + SMB2_LEASE_NONE_LE) + lease_none_upgrade(opinfo, lctx->req_state); + } + read_lock(&ci->m_lock); + } + read_unlock(&ci->m_lock); + + return m_opinfo; +} + +static void wait_for_break_ack(struct oplock_info *opinfo) +{ + int rc = 0; + + rc = wait_event_interruptible_timeout(opinfo->oplock_q, + opinfo->op_state == OPLOCK_STATE_NONE || + opinfo->op_state == OPLOCK_CLOSING, + OPLOCK_WAIT_TIME); + + /* is this a timeout ? */ + if (!rc) { + if (opinfo->is_lease) + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + } +} + +static void wake_up_oplock_break(struct oplock_info *opinfo) +{ + clear_bit_unlock(0, &opinfo->pending_break); + /* memory barrier is needed for wake_up_bit() */ + smp_mb__after_atomic(); + wake_up_bit(&opinfo->pending_break, 0); +} + +static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) +{ + while (test_and_set_bit(0, &opinfo->pending_break)) { + wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); + + /* Not immediately break to none. */ + opinfo->open_trunc = 0; + + if (opinfo->op_state == OPLOCK_CLOSING) + return -ENOENT; + else if (!opinfo->is_lease && opinfo->level <= req_op_level) + return 1; + } + + if (!opinfo->is_lease && opinfo->level <= req_op_level) { + wake_up_oplock_break(opinfo); + return 1; + } + return 0; +} + +static inline int allocate_oplock_break_buf(struct ksmbd_work *work) +{ + work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); + if (!work->response_buf) + return -ENOMEM; + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + return 0; +} + +/** + * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * to client + * @wk: smb work object + * + * There are two ways this function can be called. 1- while file open we break + * from exclusive/batch lock to levelII oplock and 2- while file write/truncate + * we break from levelII oplock no oplock. + * work->request_buf contains oplock_info. + */ +static void __smb2_oplock_break_noti(struct work_struct *wk) +{ + struct smb2_oplock_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + struct oplock_break_info *br_info = work->request_buf; + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; + + fp = ksmbd_lookup_durable_fd(br_info->fid); + if (!fp) + goto out; + + if (allocate_oplock_break_buf(work)) { + pr_err("smb2_allocate_rsp_buf failed! "); + ksmbd_fd_put(work, fp); + goto out; + } + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + + rsp->StructureSize = cpu_to_le16(24); + if (!br_info->open_trunc && + (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || + br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; + else + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->PersistentFid = fp->persistent_id; + rsp->VolatileFid = fp->volatile_id; + + inc_rfc1001_len(work->response_buf, 24); + + ksmbd_debug(OPLOCK, + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + + ksmbd_fd_put(work, fp); + ksmbd_conn_write(work); + +out: + ksmbd_free_work_struct(work); +} + +/** + * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock + * break command from server to client + * @opinfo: oplock info object + * + * Return: 0 on success, otherwise error + */ +static int smb2_oplock_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct oplock_break_info *br_info; + int ret = 0; + struct ksmbd_work *work = ksmbd_alloc_work_struct(); + + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->level = opinfo->level; + br_info->fid = opinfo->fid; + br_info->open_trunc = opinfo->open_trunc; + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + INIT_WORK(&work->work, __smb2_oplock_break_noti); + ksmbd_queue_work(work); + + wait_for_break_ack(opinfo); + } else { + __smb2_oplock_break_noti(&work->work); + if (opinfo->level == SMB2_OPLOCK_LEVEL_II) + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + } + return ret; +} + +/** + * __smb2_lease_break_noti() - send lease break command from server + * to client + * @wk: smb work object + */ +static void __smb2_lease_break_noti(struct work_struct *wk) +{ + struct smb2_lease_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct lease_break_info *br_info = work->request_buf; + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp_hdr; + + if (allocate_oplock_break_buf(work)) { + ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); + goto out; + } + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + rsp->StructureSize = cpu_to_le16(44); + rsp->Epoch = br_info->epoch; + rsp->Flags = 0; + + if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; + + memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE); + rsp->CurrentLeaseState = br_info->curr_state; + rsp->NewLeaseState = br_info->new_state; + rsp->BreakReason = 0; + rsp->AccessMaskHint = 0; + rsp->ShareMaskHint = 0; + + inc_rfc1001_len(work->response_buf, 44); + + ksmbd_conn_write(work); + +out: + ksmbd_free_work_struct(work); +} + +/** + * smb2_lease_break_noti() - break lease when a new client request + * write lease + * @opinfo: conains lease state information + * + * Return: 0 on success, otherwise error + */ +static int smb2_lease_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct list_head *tmp, *t; + struct ksmbd_work *work; + struct lease_break_info *br_info; + struct lease *lease = opinfo->o_lease; + + work = ksmbd_alloc_work_struct(); + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->curr_state = lease->state; + br_info->new_state = lease->new_state; + if (lease->version == 2) + br_info->epoch = cpu_to_le16(++lease->epoch); + else + br_info->epoch = 0; + memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + list_for_each_safe(tmp, t, &opinfo->interim_list) { + struct ksmbd_work *in_work; + + in_work = list_entry(tmp, struct ksmbd_work, + interim_entry); + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + list_del(&in_work->interim_entry); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); + wait_for_break_ack(opinfo); + } else { + __smb2_lease_break_noti(&work->work); + if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + } + } + return 0; +} + +static void wait_lease_breaking(struct oplock_info *opinfo) +{ + if (!opinfo->is_lease) + return; + + wake_up_interruptible_all(&opinfo->oplock_brk); + if (atomic_read(&opinfo->breaking_cnt)) { + int ret = 0; + + ret = wait_event_interruptible_timeout(opinfo->oplock_brk, + atomic_read(&opinfo->breaking_cnt) == 0, + HZ); + if (!ret) + atomic_set(&opinfo->breaking_cnt, 0); + } +} + +static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) +{ + int err = 0; + + /* Need to break exclusive/batch oplock, write lease or overwrite_if */ + ksmbd_debug(OPLOCK, + "request to send oplock(level : 0x%x) break notification\n", + brk_opinfo->level); + + if (brk_opinfo->is_lease) { + struct lease *lease = brk_opinfo->o_lease; + + atomic_inc(&brk_opinfo->breaking_cnt); + + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->open_trunc) { + /* + * Create overwrite break trigger the lease break to + * none. + */ + lease->new_state = SMB2_LEASE_NONE_LE; + } else { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE; + else + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + } else { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + else + lease->new_state = SMB2_LEASE_NONE_LE; + } + } + + if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + else + atomic_dec(&brk_opinfo->breaking_cnt); + } else { + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + } + + if (brk_opinfo->is_lease) + err = smb2_lease_break_noti(brk_opinfo); + else + err = smb2_oplock_break_noti(brk_opinfo); + + ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); + if (brk_opinfo->op_state == OPLOCK_CLOSING) + err = -ENOENT; + wake_up_oplock_break(brk_opinfo); + + wait_lease_breaking(brk_opinfo); + + return err; +} + +void destroy_lease_table(struct ksmbd_conn *conn) +{ + struct lease_table *lb, *lbtmp; + struct oplock_info *opinfo; + + write_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + write_unlock(&lease_list_lock); + return; + } + + list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { + if (conn && memcmp(lb->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + continue; +again: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, + lease_entry) { + rcu_read_unlock(); + lease_del_list(opinfo); + goto again; + } + rcu_read_unlock(); + list_del(&lb->l_entry); + kfree(lb); + } + write_unlock(&lease_list_lock); +} + +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx) +{ + struct oplock_info *opinfo; + int err = 0; + struct lease_table *lb; + + if (!lctx) + return err; + + read_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + read_unlock(&lease_list_lock); + return 0; + } + + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, sess->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + read_unlock(&lease_list_lock); + + return 0; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (opinfo->o_fp->f_ci == ci) + goto op_next; + err = compare_guid_key(opinfo, sess->ClientGUID, + lctx->lease_key); + if (err) { + err = -EINVAL; + ksmbd_debug(OPLOCK, + "found same lease key is already used in other files\n"); + opinfo_put(opinfo); + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return err; +} + +static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) +{ + struct lease *lease1 = op1->o_lease; + struct lease *lease2 = op2->o_lease; + + op2->level = op1->level; + lease2->state = lease1->state; + memcpy(lease2->lease_key, lease1->lease_key, + SMB2_LEASE_KEY_SIZE); + lease2->duration = lease1->duration; + lease2->flags = lease1->flags; +} + +static int add_lease_global_list(struct oplock_info *opinfo) +{ + struct lease_table *lb; + + read_lock(&lease_list_lock); + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + read_unlock(&lease_list_lock); + return 0; + } + } + read_unlock(&lease_list_lock); + + lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); + if (!lb) + return -ENOMEM; + + memcpy(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + INIT_LIST_HEAD(&lb->lease_list); + spin_lock_init(&lb->lb_lock); + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + lb_add(lb); + return 0; +} + +static void set_oplock_level(struct oplock_info *opinfo, int level, + struct lease_ctx_info *lctx) +{ + switch (level) { + case SMB2_OPLOCK_LEVEL_BATCH: + case SMB2_OPLOCK_LEVEL_EXCLUSIVE: + grant_write_oplock(opinfo, level, lctx); + break; + case SMB2_OPLOCK_LEVEL_II: + grant_read_oplock(opinfo, lctx); + break; + default: + grant_none_oplock(opinfo, lctx); + break; + } +} + +/** + * smb_grant_oplock() - handle oplock/lease request on file open + * @work: smb work + * @req_op_level: oplock level + * @pid: id of open file + * @fp: ksmbd file pointer + * @tid: Tree id of connection + * @lctx: lease context information on file open + * @share_ret: share mode + * + * Return: 0 on success, otherwise error + */ +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, + struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret) +{ + struct ksmbd_session *sess = work->sess; + int err = 0; + struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; + struct ksmbd_inode *ci = fp->f_ci; + bool prev_op_has_lease; + __le32 prev_op_state = 0; + + /* not support directory lease */ + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return 0; + + opinfo = alloc_opinfo(work, pid, tid); + if (!opinfo) + return -ENOMEM; + + if (lctx) { + err = alloc_lease(opinfo, lctx); + if (err) + goto err_out; + opinfo->is_lease = 1; + } + + /* ci does not have any oplock */ + if (!opinfo_count(fp)) + goto set_lev; + + /* grant none-oplock if second open is trunc */ + if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && + fp->cdoption != FILE_OVERWRITE_LE && + fp->cdoption != FILE_SUPERSEDE_LE) { + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + goto set_lev; + } + + if (lctx) { + struct oplock_info *m_opinfo; + + /* is lease already granted ? */ + m_opinfo = same_client_has_lease(ci, sess->ClientGUID, + lctx); + if (m_opinfo) { + copy_lease(m_opinfo, opinfo); + if (atomic_read(&m_opinfo->breaking_cnt)) + opinfo->o_lease->flags = + SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + goto out; + } + } + prev_opinfo = opinfo_get_list(ci); + if (!prev_opinfo || + (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) { + opinfo_conn_put(prev_opinfo); + goto set_lev; + } + prev_op_has_lease = prev_opinfo->is_lease; + if (prev_op_has_lease) + prev_op_state = prev_opinfo->o_lease->state; + + if (share_ret < 0 && + prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + err = share_ret; + opinfo_conn_put(prev_opinfo); + goto err_out; + } + + if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_conn_put(prev_opinfo); + goto op_break_not_needed; + } + + list_add(&work->interim_entry, &prev_opinfo->interim_list); + err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_conn_put(prev_opinfo); + if (err == -ENOENT) + goto set_lev; + /* Check all oplock was freed by close */ + else if (err < 0) + goto err_out; + +op_break_not_needed: + if (share_ret < 0) { + err = share_ret; + goto err_out; + } + + if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + /* grant fixed oplock on stacked locking between lease and oplock */ + if (prev_op_has_lease && !lctx) + if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + + if (!prev_op_has_lease && lctx) { + req_op_level = SMB2_OPLOCK_LEVEL_II; + lctx->req_state = SMB2_LEASE_READ_CACHING_LE; + } + +set_lev: + set_oplock_level(opinfo, req_op_level, lctx); + +out: + rcu_assign_pointer(fp->f_opinfo, opinfo); + opinfo->o_fp = fp; + + opinfo_count_inc(fp); + opinfo_add(opinfo); + if (opinfo->is_lease) { + err = add_lease_global_list(opinfo); + if (err) + goto err_out; + } + + return 0; +err_out: + free_opinfo(opinfo); + return err; +} + +/** + * smb_break_all_write_oplock() - break batch/exclusive oplock to level2 + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +static void smb_break_all_write_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + struct oplock_info *brk_opinfo; + + brk_opinfo = opinfo_get_list(fp->f_ci); + if (!brk_opinfo) + return; + if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_conn_put(brk_opinfo); + return; + } + + brk_opinfo->open_trunc = is_trunc; + list_add(&work->interim_entry, &brk_opinfo->interim_list); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_conn_put(brk_opinfo); +} + +/** + * smb_break_all_levII_oplock() - send level2 oplock or read lease break command + * from server to client + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + int is_trunc) +{ + struct oplock_info *op, *brk_op; + struct ksmbd_inode *ci; + struct ksmbd_conn *conn = work->conn; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + ci = fp->f_ci; + op = opinfo_get(fp); + + rcu_read_lock(); + list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { + if (!atomic_inc_not_zero(&brk_op->refcount)) + continue; + + atomic_inc(&brk_op->conn->r_count); + if (ksmbd_conn_releasing(brk_op->conn)) { + atomic_dec(&brk_op->conn->r_count); + continue; + } + + rcu_read_unlock(); + if (brk_op->is_lease && (brk_op->o_lease->state & + (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)))) { + ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", + brk_op->o_lease->state); + goto next; + } else if (brk_op->level != + SMB2_OPLOCK_LEVEL_II) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + + /* Skip oplock being break to none */ + if (brk_op->is_lease && + brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && + atomic_read(&brk_op->breaking_cnt)) + goto next; + + if (op && op->is_lease && brk_op->is_lease && + !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE) && + !memcmp(op->o_lease->lease_key, brk_op->o_lease->lease_key, + SMB2_LEASE_KEY_SIZE)) + goto next; + brk_op->open_trunc = is_trunc; + oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); +next: + opinfo_conn_put(brk_op); + rcu_read_lock(); + } + rcu_read_unlock(); + + if (op) + opinfo_put(op); +} + +/** + * smb_break_all_oplock() - break both batch/exclusive and level2 oplock + * @work: smb work + * @fp: ksmbd file pointer + */ +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + smb_break_all_write_oplock(work, fp, 1); + smb_break_all_levII_oplock(work, fp, 1); +} + +/** + * smb2_map_lease_to_oplock() - map lease state to corresponding oplock type + * @lease_state: lease type + * + * Return: 0 if no mapping, otherwise corresponding oplock type + */ +__u8 smb2_map_lease_to_oplock(__le32 lease_state) +{ + if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE)) { + return SMB2_OPLOCK_LEVEL_BATCH; + } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && + lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { + return SMB2_OPLOCK_LEVEL_II; + } + return 0; +} + +/** + * create_lease_buf() - create lease context for open cmd response + * @rbuf: buffer to create lease context response + * @lease: buffer to stored parsed lease state information + */ +void create_lease_buf(u8 *rbuf, struct lease *lease) +{ + if (lease->version == 2) { + struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + + memset(buf, 0, sizeof(struct create_lease_v2)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, + SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } else { + struct create_lease *buf = (struct create_lease *)rbuf; + + memset(buf, 0, sizeof(struct create_lease)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } +} + +/** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request + * + * Return: oplock state, -ENOENT if create lease context not found + */ +struct lease_ctx_info *parse_lease_state(void *open_req) +{ + struct create_context *cc; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + struct lease_ctx_info *lreq; + + cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); + if (IS_ERR_OR_NULL(cc)) + return NULL; + + lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); + if (!lreq) + return NULL; + + if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); + lreq->version = 2; + } else { + struct create_lease *lc = (struct create_lease *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + lreq->version = 1; + } + return lreq; +} + +/** + * smb2_find_context_vals() - find a particular context info in open request + * @open_req: buffer containing smb2 file open(create) request + * @tag: context name to search for + * @tag_len: the length of tag + * + * Return: pointer to requested context, NULL if @str context not found + * or error pointer if name length is invalid. + */ +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len) +{ + struct create_context *cc; + unsigned int next = 0; + char *name; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + unsigned int remain_len, name_off, name_len, value_off, value_len, + cc_len; + + /* + * CreateContextsOffset and CreateContextsLength are guaranteed to + * be valid because of ksmbd_smb2_check_message(). + */ + cc = (struct create_context *)((char *)req + + le32_to_cpu(req->CreateContextsOffset)); + remain_len = le32_to_cpu(req->CreateContextsLength); + do { + cc = (struct create_context *)((char *)cc + next); + if (remain_len < offsetof(struct create_context, Buffer)) + return ERR_PTR(-EINVAL); + + next = le32_to_cpu(cc->Next); + name_off = le16_to_cpu(cc->NameOffset); + name_len = le16_to_cpu(cc->NameLength); + value_off = le16_to_cpu(cc->DataOffset); + value_len = le32_to_cpu(cc->DataLength); + cc_len = next ? next : remain_len; + + if ((next & 0x7) != 0 || + next > remain_len || + name_off != offsetof(struct create_context, Buffer) || + name_len < 4 || + name_off + name_len > cc_len || + (value_off & 0x7) != 0 || + (value_off && (value_off < name_off + name_len)) || + ((u64)value_off + value_len > cc_len)) + return ERR_PTR(-EINVAL); + + name = (char *)cc + name_off; + if (name_len == tag_len && !memcmp(name, tag, name_len)) + return cc; + + remain_len -= next; + } while (next != 0); + + return NULL; +} + +/** + * create_durable_rsp_buf() - create durable handle context + * @cc: buffer to create durable context response + */ +void create_durable_rsp_buf(char *cc) +{ + struct create_durable_rsp *buf; + + buf = (struct create_durable_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'Q'; +} + +/** + * create_durable_v2_rsp_buf() - create durable handle v2 context + * @cc: buffer to create durable context response + * @fp: ksmbd file pointer + */ +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_durable_v2_rsp *buf; + + buf = (struct create_durable_v2_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'Q'; + + buf->Timeout = cpu_to_le32(fp->durable_timeout); +} + +/** + * create_mxac_rsp_buf() - create query maximal access context + * @cc: buffer to create maximal access context response + * @maximal_access: maximal access + */ +void create_mxac_rsp_buf(char *cc, int maximal_access) +{ + struct create_mxac_rsp *buf; + + buf = (struct create_mxac_rsp *)cc; + memset(buf, 0, sizeof(struct create_mxac_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, QueryStatus)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */ + buf->Name[0] = 'M'; + buf->Name[1] = 'x'; + buf->Name[2] = 'A'; + buf->Name[3] = 'c'; + + buf->QueryStatus = STATUS_SUCCESS; + buf->MaximalAccess = cpu_to_le32(maximal_access); +} + +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) +{ + struct create_disk_id_rsp *buf; + + buf = (struct create_disk_id_rsp *)cc; + memset(buf, 0, sizeof(struct create_disk_id_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_disk_id_rsp, DiskFileId)); + buf->ccontext.DataLength = cpu_to_le32(32); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */ + buf->Name[0] = 'Q'; + buf->Name[1] = 'F'; + buf->Name[2] = 'i'; + buf->Name[3] = 'd'; + + buf->DiskFileId = cpu_to_le64(file_id); + buf->VolumeId = cpu_to_le64(vol_id); +} + +/** + * create_posix_rsp_buf() - create posix extension context + * @cc: buffer to create posix on posix response + * @fp: ksmbd file pointer + */ +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_posix_rsp *buf; + struct inode *inode = file_inode(fp->filp); + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); + + buf = (struct create_posix_rsp *)cc; + memset(buf, 0, sizeof(struct create_posix_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, nlink)); + /* + * DataLength = nlink(4) + reparse_tag(4) + mode(4) + + * domain sid(28) + unix group sid(16). + */ + buf->ccontext.DataLength = cpu_to_le32(56); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + buf->Name[0] = 0x93; + buf->Name[1] = 0xAD; + buf->Name[2] = 0x25; + buf->Name[3] = 0x50; + buf->Name[4] = 0x9C; + buf->Name[5] = 0xB4; + buf->Name[6] = 0x11; + buf->Name[7] = 0xE7; + buf->Name[8] = 0xB4; + buf->Name[9] = 0x23; + buf->Name[10] = 0x83; + buf->Name[11] = 0xDE; + buf->Name[12] = 0x96; + buf->Name[13] = 0x8B; + buf->Name[14] = 0xCD; + buf->Name[15] = 0x7C; + + buf->nlink = cpu_to_le32(inode->i_nlink); + buf->reparse_tag = cpu_to_le32(fp->volatile_id); + buf->mode = cpu_to_le32(inode->i_mode & 0777); + /* + * SidBuffer(44) contain two sids(Domain sid(28), UNIX group sid(16)). + * Domain sid(28) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 4(num_subauth)) + RID(4). + * UNIX group id(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ + id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), + SIDOWNER, (struct smb_sid *)&buf->SidBuffer[0]); + id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), + SIDUNIX_GROUP, (struct smb_sid *)&buf->SidBuffer[28]); +} + +/* + * Find lease object(opinfo) for given lease key/fid from lease + * break/file close path. + */ +/** + * lookup_lease_in_table() - find a matching lease info object + * @conn: connection instance + * @lease_key: lease key to be searched for + * + * Return: opinfo if found matching opinfo, otherwise NULL + */ +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key) +{ + struct oplock_info *opinfo = NULL, *ret_op = NULL; + struct lease_table *lt; + int ret; + + read_lock(&lease_list_lock); + list_for_each_entry(lt, &lease_table_list, l_entry) { + if (!memcmp(lt->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + + read_unlock(&lease_list_lock); + return NULL; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) + goto op_next; + if (!(opinfo->o_lease->state & + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) + goto op_next; + ret = compare_guid_key(opinfo, conn->ClientGUID, + lease_key); + if (ret) { + ksmbd_debug(OPLOCK, "found opinfo\n"); + ret_op = opinfo; + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return ret_op; +} diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h new file mode 100644 index 000000000000..4b0fe6da7694 --- /dev/null +++ b/fs/smb/server/oplock.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_OPLOCK_H +#define __KSMBD_OPLOCK_H + +#include "smb_common.h" + +#define OPLOCK_WAIT_TIME (35 * HZ) + +/* SMB2 Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* Oplock states */ +#define OPLOCK_STATE_NONE 0x00 +#define OPLOCK_ACK_WAIT 0x01 +#define OPLOCK_CLOSING 0x02 + +#define OPLOCK_WRITE_TO_READ 0x01 +#define OPLOCK_READ_HANDLE_TO_READ 0x02 +#define OPLOCK_WRITE_TO_NONE 0x04 +#define OPLOCK_READ_TO_NONE 0x08 + +struct lease_ctx_info { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 req_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; +}; + +struct lease_table { + char client_guid[SMB2_CLIENT_GUID_SIZE]; + struct list_head lease_list; + struct list_head l_entry; + spinlock_t lb_lock; +}; + +struct lease { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 state; + __le32 new_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; + struct lease_table *l_lb; +}; + +struct oplock_info { + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_work *work; + struct ksmbd_file *o_fp; + int level; + int op_state; + unsigned long pending_break; + u64 fid; + atomic_t breaking_cnt; + atomic_t refcount; + __u16 Tid; + bool is_lease; + bool open_trunc; /* truncate on open */ + struct lease *o_lease; + struct list_head interim_list; + struct list_head op_entry; + struct list_head lease_entry; + wait_queue_head_t oplock_q; /* Other server threads */ + wait_queue_head_t oplock_brk; /* oplock breaking wait */ + struct rcu_head rcu_head; +}; + +struct lease_break_info { + __le32 curr_state; + __le32 new_state; + __le16 epoch; + char lease_key[SMB2_LEASE_KEY_SIZE]; +}; + +struct oplock_break_info { + int level; + int open_trunc; + int fid; +}; + +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, + u64 pid, struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret); +void smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); +int opinfo_write_to_read(struct oplock_info *opinfo); +int opinfo_read_handle_to_read(struct oplock_info *opinfo); +int opinfo_write_to_none(struct oplock_info *opinfo); +int opinfo_read_to_none(struct oplock_info *opinfo); +void close_id_del_oplock(struct ksmbd_file *fp); +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp); +struct oplock_info *opinfo_get(struct ksmbd_file *fp); +void opinfo_put(struct oplock_info *opinfo); + +/* Lease related functions */ +void create_lease_buf(u8 *rbuf, struct lease *lease); +struct lease_ctx_info *parse_lease_state(void *open_req); +__u8 smb2_map_lease_to_oplock(__le32 lease_state); +int lease_read_to_write(struct oplock_info *opinfo); + +/* Durable related functions */ +void create_durable_rsp_buf(char *cc); +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); +void create_mxac_rsp_buf(char *cc, int maximal_access); +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key); +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx); +void destroy_lease_table(struct ksmbd_conn *conn); +#endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c new file mode 100644 index 000000000000..847ee62afb8a --- /dev/null +++ b/fs/smb/server/server.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "oplock.h" +#include "misc.h" +#include +#include +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "connection.h" +#include "transport_ipc.h" +#include "mgmt/user_session.h" +#include "crypto_ctx.h" +#include "auth.h" + +int ksmbd_debug_types; + +struct ksmbd_server_config server_conf; + +enum SERVER_CTRL_TYPE { + SERVER_CTRL_TYPE_INIT, + SERVER_CTRL_TYPE_RESET, +}; + +struct server_ctrl_struct { + int type; + struct work_struct ctrl_work; +}; + +static DEFINE_MUTEX(ctrl_lock); + +static int ___server_conf_set(int idx, char *val) +{ + if (idx >= ARRAY_SIZE(server_conf.conf)) + return -EINVAL; + + if (!val || val[0] == 0x00) + return -EINVAL; + + kfree(server_conf.conf[idx]); + server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); + if (!server_conf.conf[idx]) + return -ENOMEM; + return 0; +} + +int ksmbd_set_netbios_name(char *v) +{ + return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); +} + +int ksmbd_set_server_string(char *v) +{ + return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); +} + +int ksmbd_set_work_group(char *v) +{ + return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); +} + +char *ksmbd_netbios_name(void) +{ + return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; +} + +char *ksmbd_server_string(void) +{ + return server_conf.conf[SERVER_CONF_SERVER_STRING]; +} + +char *ksmbd_work_group(void) +{ + return server_conf.conf[SERVER_CONF_WORK_GROUP]; +} + +/** + * check_conn_state() - check state of server thread connection + * @work: smb work containing server thread information + * + * Return: 0 on valid connection, otherwise 1 to reconnect + */ +static inline int check_conn_state(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr; + + if (ksmbd_conn_exiting(work->conn) || + ksmbd_conn_need_reconnect(work->conn)) { + rsp_hdr = work->response_buf; + rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; + return 1; + } + return 0; +} + +#define SERVER_HANDLER_CONTINUE 0 +#define SERVER_HANDLER_ABORT 1 + +static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, + u16 *cmd) +{ + struct smb_version_cmds *cmds; + u16 command; + int ret; + + if (check_conn_state(work)) + return SERVER_HANDLER_CONTINUE; + + if (ksmbd_verify_smb_message(work)) + return SERVER_HANDLER_ABORT; + + command = conn->ops->get_cmd_val(work); + *cmd = command; + +andx_again: + if (command >= conn->max_cmds) { + conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return SERVER_HANDLER_CONTINUE; + } + + cmds = &conn->cmds[command]; + if (!cmds->proc) { + ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); + conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); + return SERVER_HANDLER_CONTINUE; + } + + if (work->sess && conn->ops->is_sign_req(work, command)) { + ret = conn->ops->check_sign_req(work); + if (!ret) { + conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); + return SERVER_HANDLER_CONTINUE; + } + } + + ret = cmds->proc(work); + + if (ret < 0) + ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); + /* AndX commands - chained request can return positive values */ + else if (ret > 0) { + command = ret; + *cmd = command; + goto andx_again; + } + + if (work->send_no_response) + return SERVER_HANDLER_ABORT; + return SERVER_HANDLER_CONTINUE; +} + +static void __handle_ksmbd_work(struct ksmbd_work *work, + struct ksmbd_conn *conn) +{ + u16 command = 0; + int rc; + + if (conn->ops->allocate_rsp_buf(work)) + return; + + if (conn->ops->is_transform_hdr && + conn->ops->is_transform_hdr(work->request_buf)) { + rc = conn->ops->decrypt_req(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + goto send; + } + + work->encrypted = true; + } + + rc = conn->ops->init_rsp_hdr(work); + if (rc) { + /* either uid or tid is not correct */ + conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); + goto send; + } + + do { + if (conn->ops->check_user_session) { + rc = conn->ops->check_user_session(work); + if (rc < 0) { + if (rc == -EINVAL) + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + else + conn->ops->set_rsp_status(work, + STATUS_USER_SESSION_DELETED); + goto send; + } else if (rc > 0) { + rc = conn->ops->get_ksmbd_tcon(work); + if (rc < 0) { + if (rc == -EINVAL) + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + else + conn->ops->set_rsp_status(work, + STATUS_NETWORK_NAME_DELETED); + goto send; + } + } + } + + rc = __process_request(work, conn, &command); + if (rc == SERVER_HANDLER_ABORT) + break; + + /* + * Call smb2_set_rsp_credits() function to set number of credits + * granted in hdr of smb2 response. + */ + if (conn->ops->set_rsp_credits) { + spin_lock(&conn->credits_lock); + rc = conn->ops->set_rsp_credits(work); + spin_unlock(&conn->credits_lock); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + goto send; + } + } + + if (work->sess && + (work->sess->sign || smb3_11_final_sess_setup_resp(work) || + conn->ops->is_sign_req(work, command))) + conn->ops->set_sign_rsp(work); + } while (is_chained_smb2_message(work)); + + if (work->send_no_response) + return; + +send: + smb3_preauth_hash_rsp(work); + if (work->sess && work->sess->enc && work->encrypted && + conn->ops->encrypt_resp) { + rc = conn->ops->encrypt_resp(work); + if (rc < 0) + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + } + + ksmbd_conn_write(work); +} + +/** + * handle_ksmbd_work() - process pending smb work requests + * @wk: smb work containing request command buffer + * + * called by kworker threads to processing remaining smb work requests + */ +static void handle_ksmbd_work(struct work_struct *wk) +{ + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + + atomic64_inc(&conn->stats.request_served); + + __handle_ksmbd_work(work, conn); + + ksmbd_conn_try_dequeue_request(work); + ksmbd_free_work_struct(work); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); +} + +/** + * queue_ksmbd_work() - queue a smb request to worker thread queue + * for proccessing smb command and sending response + * @conn: connection instance + * + * read remaining data from socket create and submit work. + */ +static int queue_ksmbd_work(struct ksmbd_conn *conn) +{ + struct ksmbd_work *work; + + work = ksmbd_alloc_work_struct(); + if (!work) { + pr_err("allocation for work failed\n"); + return -ENOMEM; + } + + work->conn = conn; + work->request_buf = conn->request_buf; + conn->request_buf = NULL; + + ksmbd_init_smb_server(work); + + ksmbd_conn_enqueue_request(work); + atomic_inc(&conn->r_count); + /* update activity on connection */ + conn->last_active = jiffies; + INIT_WORK(&work->work, handle_ksmbd_work); + ksmbd_queue_work(work); + return 0; +} + +static int ksmbd_server_process_request(struct ksmbd_conn *conn) +{ + return queue_ksmbd_work(conn); +} + +static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) +{ + ksmbd_sessions_deregister(conn); + destroy_lease_table(conn); + return 0; +} + +static void ksmbd_server_tcp_callbacks_init(void) +{ + struct ksmbd_conn_ops ops; + + ops.process_fn = ksmbd_server_process_request; + ops.terminate_fn = ksmbd_server_terminate_conn; + + ksmbd_conn_init_server_callbacks(&ops); +} + +static void server_conf_free(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { + kfree(server_conf.conf[i]); + server_conf.conf[i] = NULL; + } +} + +static int server_conf_init(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); + server_conf.enforced_signing = 0; + server_conf.min_protocol = ksmbd_min_protocol(); + server_conf.max_protocol = ksmbd_max_protocol(); + server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | + KSMBD_AUTH_MSKRB5; +#endif + return 0; +} + +static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) +{ + int ret; + + ret = ksmbd_conn_transport_init(); + if (ret) { + server_queue_ctrl_reset_work(); + return; + } + + WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); +} + +static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) +{ + ksmbd_ipc_soft_reset(); + ksmbd_conn_transport_destroy(); + server_conf_free(); + server_conf_init(); + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); +} + +static void server_ctrl_handle_work(struct work_struct *work) +{ + struct server_ctrl_struct *ctrl; + + ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); + + mutex_lock(&ctrl_lock); + switch (ctrl->type) { + case SERVER_CTRL_TYPE_INIT: + server_ctrl_handle_init(ctrl); + break; + case SERVER_CTRL_TYPE_RESET: + server_ctrl_handle_reset(ctrl); + break; + default: + pr_err("Unknown server work type: %d\n", ctrl->type); + } + mutex_unlock(&ctrl_lock); + kfree(ctrl); + module_put(THIS_MODULE); +} + +static int __queue_ctrl_work(int type) +{ + struct server_ctrl_struct *ctrl; + + ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + __module_get(THIS_MODULE); + ctrl->type = type; + INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); + queue_work(system_long_wq, &ctrl->ctrl_work); + return 0; +} + +int server_queue_ctrl_init_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); +} + +int server_queue_ctrl_reset_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); +} + +static ssize_t stats_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + /* + * Inc this each time you change stats output format, + * so user space will know what to do. + */ + static int stats_version = 2; + static const char * const state[] = { + "startup", + "running", + "reset", + "shutdown" + }; + + ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, + state[server_conf.state], server_conf.tcp_port, + server_conf.ipc_last_active / HZ); + return sz; +} + +static ssize_t kill_server_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t len) +{ + if (!sysfs_streq(buf, "hard")) + return len; + + pr_info("kill command received\n"); + mutex_lock(&ctrl_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + __module_get(THIS_MODULE); + server_ctrl_handle_reset(NULL); + module_put(THIS_MODULE); + mutex_unlock(&ctrl_lock); + return len; +} + +static const char * const debug_type_strings[] = {"smb", "auth", "vfs", + "oplock", "ipc", "conn", + "rdma"}; + +static ssize_t debug_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + ssize_t sz = 0; + int i, pos = 0; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if ((ksmbd_debug_types >> i) & 1) { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "[%s] ", + debug_type_strings[i]); + } else { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "%s ", + debug_type_strings[i]); + } + sz += pos; + } + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +static ssize_t debug_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t len) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if (sysfs_streq(buf, "all")) { + if (ksmbd_debug_types == KSMBD_DEBUG_ALL) + ksmbd_debug_types = 0; + else + ksmbd_debug_types = KSMBD_DEBUG_ALL; + break; + } + + if (sysfs_streq(buf, debug_type_strings[i])) { + if (ksmbd_debug_types & (1 << i)) + ksmbd_debug_types &= ~(1 << i); + else + ksmbd_debug_types |= (1 << i); + break; + } + } + + return len; +} + +static CLASS_ATTR_RO(stats); +static CLASS_ATTR_WO(kill_server); +static CLASS_ATTR_RW(debug); + +static struct attribute *ksmbd_control_class_attrs[] = { + &class_attr_stats.attr, + &class_attr_kill_server.attr, + &class_attr_debug.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ksmbd_control_class); + +static struct class ksmbd_control_class = { + .name = "ksmbd-control", + .owner = THIS_MODULE, + .class_groups = ksmbd_control_class_groups, +}; + +static int ksmbd_server_shutdown(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); + + class_unregister(&ksmbd_control_class); + ksmbd_workqueue_destroy(); + ksmbd_ipc_release(); + ksmbd_conn_transport_destroy(); + ksmbd_crypto_destroy(); + ksmbd_free_global_file_table(); + destroy_lease_table(NULL); + ksmbd_work_pool_destroy(); + ksmbd_exit_file_cache(); + server_conf_free(); + return 0; +} + +static int __init ksmbd_server_init(void) +{ + int ret; + + ret = class_register(&ksmbd_control_class); + if (ret) { + pr_err("Unable to register ksmbd-control class\n"); + return ret; + } + + ksmbd_server_tcp_callbacks_init(); + + ret = server_conf_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_work_pool_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_init_file_cache(); + if (ret) + goto err_destroy_work_pools; + + ret = ksmbd_ipc_init(); + if (ret) + goto err_exit_file_cache; + + ret = ksmbd_init_global_file_table(); + if (ret) + goto err_ipc_release; + + ret = ksmbd_inode_hash_init(); + if (ret) + goto err_destroy_file_table; + + ret = ksmbd_crypto_create(); + if (ret) + goto err_release_inode_hash; + + ret = ksmbd_workqueue_init(); + if (ret) + goto err_crypto_destroy; + + pr_warn_once("The ksmbd server is experimental\n"); + + return 0; + +err_crypto_destroy: + ksmbd_crypto_destroy(); +err_release_inode_hash: + ksmbd_release_inode_hash(); +err_destroy_file_table: + ksmbd_free_global_file_table(); +err_ipc_release: + ksmbd_ipc_release(); +err_exit_file_cache: + ksmbd_exit_file_cache(); +err_destroy_work_pools: + ksmbd_work_pool_destroy(); +err_unregister: + class_unregister(&ksmbd_control_class); + + return ret; +} + +/** + * ksmbd_server_exit() - shutdown forker thread and free memory at module exit + */ +static void __exit ksmbd_server_exit(void) +{ + ksmbd_server_shutdown(); + rcu_barrier(); + ksmbd_release_inode_hash(); +} + +MODULE_AUTHOR("Namjae Jeon "); +MODULE_VERSION(KSMBD_VERSION); +MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ecb"); +MODULE_SOFTDEP("pre: hmac"); +MODULE_SOFTDEP("pre: md5"); +MODULE_SOFTDEP("pre: nls"); +MODULE_SOFTDEP("pre: aes"); +MODULE_SOFTDEP("pre: cmac"); +MODULE_SOFTDEP("pre: sha256"); +MODULE_SOFTDEP("pre: sha512"); +MODULE_SOFTDEP("pre: aead2"); +MODULE_SOFTDEP("pre: ccm"); +MODULE_SOFTDEP("pre: gcm"); +MODULE_SOFTDEP("pre: crc32"); +module_init(ksmbd_server_init) +module_exit(ksmbd_server_exit) diff --git a/fs/smb/server/server.h b/fs/smb/server/server.h new file mode 100644 index 000000000000..db7278181760 --- /dev/null +++ b/fs/smb/server/server.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SERVER_H__ +#define __SERVER_H__ + +#include "smbacl.h" + +/* + * Server state type + */ +enum { + SERVER_STATE_STARTING_UP, + SERVER_STATE_RUNNING, + SERVER_STATE_RESETTING, + SERVER_STATE_SHUTTING_DOWN, +}; + +/* + * Server global config string index + */ +enum { + SERVER_CONF_NETBIOS_NAME, + SERVER_CONF_SERVER_STRING, + SERVER_CONF_WORK_GROUP, +}; + +struct ksmbd_server_config { + unsigned int flags; + unsigned int state; + short signing; + short enforced_signing; + short min_protocol; + short max_protocol; + unsigned short tcp_port; + unsigned short ipc_timeout; + unsigned long ipc_last_active; + unsigned long deadtime; + unsigned int share_fake_fscaps; + struct smb_sid domain_sid; + unsigned int auth_mechs; + unsigned int max_connections; + + char *conf[SERVER_CONF_WORK_GROUP + 1]; +}; + +extern struct ksmbd_server_config server_conf; + +int ksmbd_set_netbios_name(char *v); +int ksmbd_set_server_string(char *v); +int ksmbd_set_work_group(char *v); + +char *ksmbd_netbios_name(void); +char *ksmbd_server_string(void); +char *ksmbd_work_group(void); + +static inline int ksmbd_server_running(void) +{ + return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING; +} + +static inline int ksmbd_server_configurable(void) +{ + return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING; +} + +int server_queue_ctrl_init_work(void); +int server_queue_ctrl_reset_work(void); +#endif /* __SERVER_H__ */ diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c new file mode 100644 index 000000000000..33b7e6c4ceff --- /dev/null +++ b/fs/smb/server/smb2misc.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "nterr.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "mgmt/user_session.h" +#include "connection.h" + +static int check_smb2_hdr(struct smb2_hdr *hdr) +{ + /* + * Make sure that this really is an SMB, that it is a response. + */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 1; + return 0; +} + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order + */ +static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ cpu_to_le16(36), + /* SMB2_SESSION_SETUP */ cpu_to_le16(25), + /* SMB2_LOGOFF */ cpu_to_le16(4), + /* SMB2_TREE_CONNECT */ cpu_to_le16(9), + /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), + /* SMB2_CREATE */ cpu_to_le16(57), + /* SMB2_CLOSE */ cpu_to_le16(24), + /* SMB2_FLUSH */ cpu_to_le16(24), + /* SMB2_READ */ cpu_to_le16(49), + /* SMB2_WRITE */ cpu_to_le16(49), + /* SMB2_LOCK */ cpu_to_le16(48), + /* SMB2_IOCTL */ cpu_to_le16(57), + /* SMB2_CANCEL */ cpu_to_le16(4), + /* SMB2_ECHO */ cpu_to_le16(4), + /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33), + /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32), + /* SMB2_QUERY_INFO */ cpu_to_le16(41), + /* SMB2_SET_INFO */ cpu_to_le16(33), + /* use 44 for lease break */ + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(36) +}; + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 requests. SMB2 requests + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ true, + /* SMB2_SESSION_SETUP */ true, + /* SMB2_LOGOFF */ false, + /* SMB2_TREE_CONNECT */ true, + /* SMB2_TREE_DISCONNECT */ false, + /* SMB2_CREATE */ true, + /* SMB2_CLOSE */ false, + /* SMB2_FLUSH */ false, + /* SMB2_READ */ true, + /* SMB2_WRITE */ true, + /* SMB2_LOCK */ true, + /* SMB2_IOCTL */ true, + /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ + /* SMB2_ECHO */ false, + /* SMB2_QUERY_DIRECTORY */ true, + /* SMB2_CHANGE_NOTIFY */ false, + /* SMB2_QUERY_INFO */ true, + /* SMB2_SET_INFO */ true, + /* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Set length of the data area and the offset to arguments. + * if they are invalid, return error. + */ +static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + struct smb2_hdr *hdr) +{ + int ret = 0; + + *off = 0; + *len = 0; + + /* + * Following commands have data areas so we have to get the location + * of the data buffer offset and data buffer length for the particular + * command. + */ + switch (hdr->Command) { + case SMB2_SESSION_SETUP: + *off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); + *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); + break; + case SMB2_TREE_CONNECT: + *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); + *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); + break; + case SMB2_CREATE: + { + if (((struct smb2_create_req *)hdr)->CreateContextsLength) { + *off = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsOffset); + *len = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsLength); + break; + } + + *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); + *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + break; + } + case SMB2_QUERY_INFO: + *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); + *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); + break; + case SMB2_SET_INFO: + *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); + *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); + break; + case SMB2_READ: + *off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); + break; + case SMB2_WRITE: + if (((struct smb2_write_req *)hdr)->DataOffset || + ((struct smb2_write_req *)hdr)->Length) { + *off = max_t(unsigned int, + le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), + offsetof(struct smb2_write_req, Buffer)); + *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); + break; + } + + *off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); + break; + case SMB2_QUERY_DIRECTORY: + *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); + *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); + break; + case SMB2_LOCK: + { + unsigned short lock_count; + + lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount); + if (lock_count > 0) { + *off = offsetof(struct smb2_lock_req, locks); + *len = sizeof(struct smb2_lock_element) * lock_count; + } + break; + } + case SMB2_IOCTL: + *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); + *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); + break; + default: + ksmbd_debug(SMB, "no length check for command\n"); + break; + } + + if (*off > 4096) { + ksmbd_debug(SMB, "offset %d too large\n", *off); + ret = -EINVAL; + } else if ((u64)*off + *len > MAX_STREAM_PROT_LEN) { + ksmbd_debug(SMB, "Request is larger than maximum stream protocol length(%u): %llu\n", + MAX_STREAM_PROT_LEN, (u64)*off + *len); + ret = -EINVAL; + } + + return ret; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +static int smb2_calc_size(void *buf, unsigned int *len) +{ + struct smb2_pdu *pdu = (struct smb2_pdu *)buf; + struct smb2_hdr *hdr = &pdu->hdr; + unsigned int offset; /* the offset from the beginning of SMB to data area */ + unsigned int data_length; /* the length of the variable length data area */ + int ret; + + /* Structure Size has already been checked to make sure it is 64 */ + *len = le16_to_cpu(hdr->StructureSize); + + /* + * StructureSize2, ie length of fixed parameter area has already + * been checked to make sure it is the correct length. + */ + *len += le16_to_cpu(pdu->StructureSize2); + /* + * StructureSize2 of smb2_lock pdu is set to 48, indicating + * the size of smb2 lock request with single smb2_lock_element + * regardless of number of locks. Subtract single + * smb2_lock_element for correct buffer size check. + */ + if (hdr->Command == SMB2_LOCK) + *len -= sizeof(struct smb2_lock_element); + + if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) + goto calc_size_exit; + + ret = smb2_get_data_area_len(&offset, &data_length, hdr); + if (ret) + return ret; + ksmbd_debug(SMB, "SMB2 data length %u offset %u\n", data_length, + offset); + + if (data_length > 0) { + /* + * Check to make sure that data area begins after fixed area, + * Note that last byte of the fixed area is part of data area + * for some commands, typically those with odd StructureSize, + * so we must add one to the calculation. + */ + if (offset + 1 < *len) { + ksmbd_debug(SMB, + "data area offset %d overlaps SMB2 header %u\n", + offset + 1, *len); + return -EINVAL; + } + + *len = offset + data_length; + } + +calc_size_exit: + ksmbd_debug(SMB, "SMB2 len %u\n", *len); + return 0; +} + +static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) +{ + return le32_to_cpu(h->InputBufferLength) + + le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) +{ + return le32_to_cpu(h->BufferLength); +} + +static inline int smb2_read_req_len(struct smb2_read_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_write_req_len(struct smb2_write_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h) +{ + return le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->InputCount) + + le32_to_cpu(h->OutputCount); +} + +static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->MaxInputResponse) + + le32_to_cpu(h->MaxOutputResponse); +} + +static int smb2_validate_credit_charge(struct ksmbd_conn *conn, + struct smb2_hdr *hdr) +{ + unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; + unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); + void *__hdr = hdr; + int ret = 0; + + switch (hdr->Command) { + case SMB2_QUERY_INFO: + req_len = smb2_query_info_req_len(__hdr); + break; + case SMB2_SET_INFO: + req_len = smb2_set_info_req_len(__hdr); + break; + case SMB2_READ: + req_len = smb2_read_req_len(__hdr); + break; + case SMB2_WRITE: + req_len = smb2_write_req_len(__hdr); + break; + case SMB2_QUERY_DIRECTORY: + req_len = smb2_query_dir_req_len(__hdr); + break; + case SMB2_IOCTL: + req_len = smb2_ioctl_req_len(__hdr); + expect_resp_len = smb2_ioctl_resp_len(__hdr); + break; + case SMB2_CANCEL: + return 0; + default: + req_len = 1; + break; + } + + credit_charge = max_t(unsigned short, credit_charge, 1); + max_len = max_t(unsigned int, req_len, expect_resp_len); + calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); + + if (credit_charge < calc_credit_num) { + ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n", + credit_charge, calc_credit_num); + return 1; + } else if (credit_charge > conn->vals->max_credits) { + ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge); + return 1; + } + + spin_lock(&conn->credits_lock); + if (credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + ret = 1; + } + + if ((u64)conn->outstanding_credits + credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n", + credit_charge, conn->outstanding_credits); + ret = 1; + } else + conn->outstanding_credits += credit_charge; + + spin_unlock(&conn->credits_lock); + + return ret; +} + +int ksmbd_smb2_check_message(struct ksmbd_work *work) +{ + struct smb2_pdu *pdu = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = &pdu->hdr; + int command; + __u32 clc_len; /* calculated length */ + __u32 len = get_rfc1002_len(work->request_buf); + __u32 req_struct_size, next_cmd = le32_to_cpu(hdr->NextCommand); + + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd > len) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return 1; + } + + if (next_cmd > 0) + len = next_cmd; + else if (work->next_smb2_rcv_hdr_off) + len -= work->next_smb2_rcv_hdr_off; + + if (check_smb2_hdr(hdr)) + return 1; + + if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { + ksmbd_debug(SMB, "Illegal structure size %u\n", + le16_to_cpu(hdr->StructureSize)); + return 1; + } + + command = le16_to_cpu(hdr->Command); + if (command >= NUMBER_OF_SMB2_COMMANDS) { + ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command); + return 1; + } + + if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { + if (command == SMB2_OPLOCK_BREAK_HE && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { + /* special case for SMB2.1 lease break message */ + ksmbd_debug(SMB, + "Illegal request size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); + return 1; + } + } + + req_struct_size = le16_to_cpu(pdu->StructureSize2) + + __SMB2_HEADER_STRUCTURE_SIZE; + if (command == SMB2_LOCK_HE) + req_struct_size -= sizeof(struct smb2_lock_element); + + if (req_struct_size > len + 1) + return 1; + + if (smb2_calc_size(hdr, &clc_len)) + return 1; + + if (len != clc_len) { + /* client can return one byte more due to implied bcc[0] */ + if (clc_len == len + 1) + goto validate_credit; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ + if (ALIGN(clc_len, 8) == len) + goto validate_credit; + + /* + * SMB2 NEGOTIATE request will be validated when message + * handling proceeds. + */ + if (command == SMB2_NEGOTIATE_HE) + goto validate_credit; + + /* + * Allow a message that padded to 8byte boundary. + * Linux 4.19.217 with smb 3.0.2 are sometimes + * sending messages where the cls_len is exactly + * 8 bytes less than len. + */ + if (clc_len < len && (len - clc_len) <= 8) + goto validate_credit; + + pr_err_ratelimited( + "cli req too short, len %d not %d. cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + + return 1; + } + +validate_credit: + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(work->conn, hdr)) { + work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return 1; + } + + return 0; +} + +int smb2_negotiate_request(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE); +} diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c new file mode 100644 index 000000000000..ab23da2120b9 --- /dev/null +++ b/fs/smb/server/smb2ops.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include "glob.h" + +#include "auth.h" +#include "connection.h" +#include "smb_common.h" +#include "server.h" + +static struct smb_version_values smb21_server_values = { + .version_string = SMB21_VERSION_STRING, + .protocol_id = SMB21_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB21_DEFAULT_IOSIZE, + .max_write_size = SMB21_DEFAULT_IOSIZE, + .max_trans_size = SMB21_DEFAULT_IOSIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb30_server_values = { + .version_string = SMB30_VERSION_STRING, + .protocol_id = SMB30_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb302_server_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb311_server_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_ops smb2_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb2_check_sign_req, + .set_sign_rsp = smb2_set_sign_rsp +}; + +static struct smb_version_ops smb3_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb30_signingkey, + .generate_encryptionkey = ksmbd_gen_smb30_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_ops smb3_11_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb311_signingkey, + .generate_encryptionkey = ksmbd_gen_smb311_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { + [SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, }, + [SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, }, + [SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,}, + [SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,}, + [SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,}, + [SMB2_CREATE_HE] = { .proc = smb2_open}, + [SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info}, + [SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir}, + [SMB2_CLOSE_HE] = { .proc = smb2_close}, + [SMB2_ECHO_HE] = { .proc = smb2_echo}, + [SMB2_SET_INFO_HE] = { .proc = smb2_set_info}, + [SMB2_READ_HE] = { .proc = smb2_read}, + [SMB2_WRITE_HE] = { .proc = smb2_write}, + [SMB2_FLUSH_HE] = { .proc = smb2_flush}, + [SMB2_CANCEL_HE] = { .proc = smb2_cancel}, + [SMB2_LOCK_HE] = { .proc = smb2_lock}, + [SMB2_IOCTL_HE] = { .proc = smb2_ioctl}, + [SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break}, + [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, +}; + +/** + * init_smb2_1_server() - initialize a smb server connection with smb2.1 + * command dispatcher + * @conn: connection instance + */ +void init_smb2_1_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb21_server_values; + conn->ops = &smb2_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; +} + +/** + * init_smb3_0_server() - initialize a smb server connection with smb3.0 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_0_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb30_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_02_server() - initialize a smb server connection with smb3.02 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_02_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb302_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_11_server() - initialize a smb server connection with smb3.11 + * command dispatcher + * @conn: connection instance + */ +int init_smb3_11_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb311_server_values; + conn->ops = &smb3_11_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + + INIT_LIST_HEAD(&conn->preauth_sess_table); + return 0; +} + +void init_smb2_max_read_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_read_size = sz; + smb30_server_values.max_read_size = sz; + smb302_server_values.max_read_size = sz; + smb311_server_values.max_read_size = sz; +} + +void init_smb2_max_write_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_write_size = sz; + smb30_server_values.max_write_size = sz; + smb302_server_values.max_write_size = sz; + smb311_server_values.max_write_size = sz; +} + +void init_smb2_max_trans_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_trans_size = sz; + smb30_server_values.max_trans_size = sz; + smb302_server_values.max_trans_size = sz; + smb311_server_values.max_trans_size = sz; +} + +void init_smb2_max_credits(unsigned int sz) +{ + smb21_server_values.max_credits = sz; + smb30_server_values.max_credits = sz; + smb302_server_values.max_credits = sz; + smb311_server_values.max_credits = sz; +} diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c new file mode 100644 index 000000000000..2b7c0ba6a77d --- /dev/null +++ b/fs/smb/server/smb2pdu.c @@ -0,0 +1,8727 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "smbfsctl.h" +#include "oplock.h" +#include "smbacl.h" + +#include "auth.h" +#include "asn1.h" +#include "connection.h" +#include "transport_ipc.h" +#include "transport_rdma.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "misc.h" + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "ksmbd_work.h" +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/ksmbd_ida.h" +#include "ndr.h" + +static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) +{ + if (work->next_smb2_rcv_hdr_off) { + *req = ksmbd_req_buf_next(work); + *rsp = ksmbd_resp_buf_next(work); + } else { + *req = smb2_get_msg(work->request_buf); + *rsp = smb2_get_msg(work->response_buf); + } +} + +#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) + +/** + * check_session_id() - check for valid session id in smb header + * @conn: connection instance + * @id: session id from smb header + * + * Return: 1 if valid session id, otherwise 0 + */ +static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) +{ + struct ksmbd_session *sess; + + if (id == 0 || id == -1) + return false; + + sess = ksmbd_session_lookup_all(conn, id); + if (sess) + return true; + pr_err("Invalid user session id: %llu\n", id); + return false; +} + +struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) +{ + return xa_load(&sess->ksmbd_chann_list, (long)conn); +} + +/** + * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. + * @work: smb work + * + * Return: 0 if there is a tree connection matched or these are + * skipable commands, otherwise error + */ +int smb2_get_ksmbd_tcon(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + unsigned int cmd = le16_to_cpu(req_hdr->Command); + int tree_id; + + if (cmd == SMB2_TREE_CONNECT_HE || + cmd == SMB2_CANCEL_HE || + cmd == SMB2_LOGOFF_HE) { + ksmbd_debug(SMB, "skip to check tree connect request\n"); + return 0; + } + + if (xa_empty(&work->sess->tree_conns)) { + ksmbd_debug(SMB, "NO tree connected\n"); + return -ENOENT; + } + + tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); + + /* + * If request is not the first in Compound request, + * Just validate tree id in header with work->tcon->id. + */ + if (work->next_smb2_rcv_hdr_off) { + if (!work->tcon) { + pr_err("The first operation in the compound does not have tcon\n"); + return -EINVAL; + } + if (work->tcon->id != tree_id) { + pr_err("tree id(%u) is different with id(%u) in first operation\n", + tree_id, work->tcon->id); + return -EINVAL; + } + return 1; + } + + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); + if (!work->tcon) { + pr_err("Invalid tid %d\n", tree_id); + return -ENOENT; + } + + return 1; +} + +/** + * smb2_set_err_rsp() - set error response code on smb response + * @work: smb work containing response buffer + */ +void smb2_set_err_rsp(struct ksmbd_work *work) +{ + struct smb2_err_rsp *err_rsp; + + if (work->next_smb2_rcv_hdr_off) + err_rsp = ksmbd_resp_buf_next(work); + else + err_rsp = smb2_get_msg(work->response_buf); + + if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; + err_rsp->ErrorContextCount = 0; + err_rsp->Reserved = 0; + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; + inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); + } +} + +/** + * is_smb2_neg_cmd() - is it smb2 negotiation command + * @work: smb work containing smb header + * + * Return: true if smb2 negotiation command, otherwise false + */ +bool is_smb2_neg_cmd(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is request not response message */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return false; + + if (hdr->Command != SMB2_NEGOTIATE) + return false; + + return true; +} + +/** + * is_smb2_rsp() - is it smb2 response + * @work: smb work containing smb response buffer + * + * Return: true if smb2 response, otherwise false + */ +bool is_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is response not request message */ + if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) + return false; + + return true; +} + +/** + * get_smb2_cmd_val() - get smb command code from smb header + * @work: smb work containing smb request buffer + * + * Return: smb2 request command value + */ +u16 get_smb2_cmd_val(struct ksmbd_work *work) +{ + struct smb2_hdr *rcv_hdr; + + if (work->next_smb2_rcv_hdr_off) + rcv_hdr = ksmbd_req_buf_next(work); + else + rcv_hdr = smb2_get_msg(work->request_buf); + return le16_to_cpu(rcv_hdr->Command); +} + +/** + * set_smb2_rsp_status() - set error response code on smb2 header + * @work: smb work containing response buffer + * @err: error response code + */ +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) +{ + struct smb2_hdr *rsp_hdr; + + if (work->next_smb2_rcv_hdr_off) + rsp_hdr = ksmbd_resp_buf_next(work); + else + rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Status = err; + smb2_set_err_rsp(work); +} + +/** + * init_smb2_neg_rsp() - initialize smb2 response for negotiate command + * @work: smb work containing smb request buffer + * + * smb2 negotiate response is sent in reply of smb1 negotiate command for + * dialect auto-negotiation. + */ +int init_smb2_neg_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr; + struct smb2_negotiate_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(2); + rsp_hdr->Command = SMB2_NEGOTIATE; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = 0; + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + + WARN_ON(ksmbd_conn_good(conn)); + + rsp->StructureSize = cpu_to_le16(65); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying connection + */ + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + /* Default Max Message Size till SMB2.0, 64K*/ + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->use_spnego = true; + + ksmbd_conn_set_need_negotiate(conn); + return 0; +} + +/** + * smb2_set_rsp_credits() - set number of credits in response buffer + * @work: smb work containing smb response buffer + */ +int smb2_set_rsp_credits(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); + struct ksmbd_conn *conn = work->conn; + unsigned short credits_requested, aux_max; + unsigned short credit_charge, credits_granted = 0; + + if (work->send_no_response) + return 0; + + hdr->CreditCharge = req_hdr->CreditCharge; + + if (conn->total_credits > conn->vals->max_credits) { + hdr->CreditRequest = 0; + pr_err("Total credits overflow: %d\n", conn->total_credits); + return -EINVAL; + } + + credit_charge = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditCharge), 1); + if (credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + return -EINVAL; + } + + conn->total_credits -= credit_charge; + conn->outstanding_credits -= credit_charge; + credits_requested = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditRequest), 1); + + /* according to smb2.credits smbtorture, Windows server + * 2016 or later grant up to 8192 credits at once. + * + * TODO: Need to adjuct CreditRequest value according to + * current cpu load + */ + if (hdr->Command == SMB2_NEGOTIATE) + aux_max = 1; + else + aux_max = conn->vals->max_credits - conn->total_credits; + credits_granted = min_t(unsigned short, credits_requested, aux_max); + + conn->total_credits += credits_granted; + work->credits_granted += credits_granted; + + if (!req_hdr->NextCommand) { + /* Update CreditRequest in last request */ + hdr->CreditRequest = cpu_to_le16(work->credits_granted); + } + ksmbd_debug(SMB, + "credits: requested[%d] granted[%d] total_granted[%d]\n", + credits_requested, credits_granted, + conn->total_credits); + return 0; +} + +/** + * init_chained_smb2_rsp() - initialize smb2 chained response + * @work: smb work containing smb response buffer + */ +static void init_chained_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *req = ksmbd_req_buf_next(work); + struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); + struct smb2_hdr *rsp_hdr; + struct smb2_hdr *rcv_hdr; + int next_hdr_offset = 0; + int len, new_len; + + /* Len of this response = updated RFC len - offset of previous cmd + * in the compound rsp + */ + + /* Storing the current local FID which may be needed by subsequent + * command in the compound request + */ + if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { + work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; + work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; + work->compound_sid = le64_to_cpu(rsp->SessionId); + } + + len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; + next_hdr_offset = le32_to_cpu(req->NextCommand); + + new_len = ALIGN(len, 8); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_hdr) + new_len - len); + rsp->NextCommand = cpu_to_le32(new_len); + + work->next_smb2_rcv_hdr_off += next_hdr_offset; + work->next_smb2_rsp_hdr_off += new_len; + ksmbd_debug(SMB, + "Compound req new_len = %d rcv off = %d rsp off = %d\n", + new_len, work->next_smb2_rcv_hdr_off, + work->next_smb2_rsp_hdr_off); + + rsp_hdr = ksmbd_resp_buf_next(work); + rcv_hdr = ksmbd_req_buf_next(work); + + if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "related flag should be set\n"); + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | + SMB2_FLAGS_RELATED_OPERATIONS); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); +} + +/** + * is_chained_smb2_message() - check for chained command + * @work: smb work containing smb request buffer + * + * Return: true if chained request, otherwise false + */ +bool is_chained_smb2_message(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + unsigned int len, next_cmd; + + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + hdr = ksmbd_req_buf_next(work); + next_cmd = le32_to_cpu(hdr->NextCommand); + if (next_cmd > 0) { + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + + __SMB2_HEADER_STRUCTURE_SIZE > + get_rfc1002_len(work->request_buf)) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return false; + } + + if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > + work->response_sz) { + pr_err("next response offset exceeds response buffer size\n"); + return false; + } + + ksmbd_debug(SMB, "got SMB2 chained command\n"); + init_chained_smb2_rsp(work); + return true; + } else if (work->next_smb2_rcv_hdr_off) { + /* + * This is last request in chained command, + * align response to 8 byte + */ + len = ALIGN(get_rfc1002_len(work->response_buf), 8); + len = len - get_rfc1002_len(work->response_buf); + if (len) { + ksmbd_debug(SMB, "padding len %u\n", len); + inc_rfc1001_len(work->response_buf, len); + if (work->aux_payload_sz) + work->aux_payload_sz += len; + } + } + return false; +} + +/** + * init_smb2_rsp_hdr() - initialize smb2 response + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int init_smb2_rsp_hdr(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); + struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); + struct ksmbd_conn *conn = work->conn; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + + work->syncronous = true; + if (work->async_id) { + ksmbd_release_id(&conn->async_ida, work->async_id); + work->async_id = 0; + } + + return 0; +} + +/** + * smb2_allocate_rsp_buf() - allocate smb2 response buffer + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise -ENOMEM + */ +int smb2_allocate_rsp_buf(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + size_t large_sz = small_sz + work->conn->vals->max_trans_size; + size_t sz = small_sz; + int cmd = le16_to_cpu(hdr->Command); + + if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) + sz = large_sz; + + if (cmd == SMB2_QUERY_INFO_HE) { + struct smb2_query_info_req *req; + + req = smb2_get_msg(work->request_buf); + if ((req->InfoType == SMB2_O_INFO_FILE && + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || + req->FileInfoClass == FILE_ALL_INFORMATION)) || + req->InfoType == SMB2_O_INFO_SECURITY) + sz = large_sz; + } + + /* allocate large response buf for chained commands */ + if (le32_to_cpu(hdr->NextCommand) > 0) + sz = large_sz; + + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!work->response_buf) + return -ENOMEM; + + work->response_sz = sz; + return 0; +} + +/** + * smb2_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_check_user_session(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + struct ksmbd_conn *conn = work->conn; + unsigned int cmd = conn->ops->get_cmd_val(work); + unsigned long long sess_id; + + /* + * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not + * require a session id, so no need to validate user session's for + * these commands. + */ + if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || + cmd == SMB2_SESSION_SETUP_HE) + return 0; + + if (!ksmbd_conn_good(conn)) + return -EIO; + + sess_id = le64_to_cpu(req_hdr->SessionId); + + /* + * If request is not the first in Compound request, + * Just validate session id in header with work->sess->id. + */ + if (work->next_smb2_rcv_hdr_off) { + if (!work->sess) { + pr_err("The first operation in the compound does not have sess\n"); + return -EINVAL; + } + if (work->sess->id != sess_id) { + pr_err("session id(%llu) is different with the first operation(%lld)\n", + sess_id, work->sess->id); + return -EINVAL; + } + return 1; + } + + /* Check for validity of user session */ + work->sess = ksmbd_session_lookup_all(conn, sess_id); + if (work->sess) + return 1; + ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); + return -ENOENT; +} + +static void destroy_previous_session(struct ksmbd_conn *conn, + struct ksmbd_user *user, u64 id) +{ + struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); + struct ksmbd_user *prev_user; + struct channel *chann; + long index; + + if (!prev_sess) + return; + + prev_user = prev_sess->user; + + if (!prev_user || + strcmp(user->name, prev_user->name) || + user->passkey_sz != prev_user->passkey_sz || + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) + return; + + prev_sess->state = SMB2_SESSION_EXPIRED; + xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) + ksmbd_conn_set_exiting(chann->conn); +} + +/** + * smb2_get_name() - get filename string from on the wire smb format + * @src: source buffer + * @maxlen: maxlen of source string + * @local_nls: nls_table pointer + * + * Return: matching converted filename on success, otherwise error ptr + */ +static char * +smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) +{ + char *name; + + name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); + if (IS_ERR(name)) { + pr_err("failed to get name %ld\n", PTR_ERR(name)); + return name; + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + return name; +} + +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) +{ + struct smb2_hdr *rsp_hdr; + struct ksmbd_conn *conn = work->conn; + int id; + + rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; + + id = ksmbd_acquire_async_msg_id(&conn->async_ida); + if (id < 0) { + pr_err("Failed to alloc async message id\n"); + return id; + } + work->syncronous = false; + work->async_id = id; + rsp_hdr->Id.AsyncId = cpu_to_le64(id); + + ksmbd_debug(SMB, + "Send interim Response to inform async request id : %d\n", + work->async_id); + + work->cancel_fn = fn; + work->cancel_argv = arg; + + if (list_empty(&work->async_request_entry)) { + spin_lock(&conn->request_lock); + list_add_tail(&work->async_request_entry, &conn->async_requests); + spin_unlock(&conn->request_lock); + } + + return 0; +} + +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) +{ + struct smb2_hdr *rsp_hdr; + + rsp_hdr = smb2_get_msg(work->response_buf); + smb2_set_err_rsp(work); + rsp_hdr->Status = status; + + work->multiRsp = 1; + ksmbd_conn_write(work); + rsp_hdr->Status = 0; + work->multiRsp = 0; +} + +static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +{ + if (S_ISDIR(mode) || S_ISREG(mode)) + return 0; + + if (S_ISLNK(mode)) + return IO_REPARSE_TAG_LX_SYMLINK_LE; + else if (S_ISFIFO(mode)) + return IO_REPARSE_TAG_LX_FIFO_LE; + else if (S_ISSOCK(mode)) + return IO_REPARSE_TAG_AF_UNIX_LE; + else if (S_ISCHR(mode)) + return IO_REPARSE_TAG_LX_CHR_LE; + else if (S_ISBLK(mode)) + return IO_REPARSE_TAG_LX_BLK_LE; + + return 0; +} + +/** + * smb2_get_dos_mode() - get file mode in dos format from unix mode + * @stat: kstat containing file mode + * @attribute: attribute flags + * + * Return: converted dos mode + */ +static int smb2_get_dos_mode(struct kstat *stat, int attribute) +{ + int attr = 0; + + if (S_ISDIR(stat->mode)) { + attr = FILE_ATTRIBUTE_DIRECTORY | + (attribute & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); + } else { + attr = (attribute & 0x00005137) | FILE_ATTRIBUTE_ARCHIVE; + attr &= ~(FILE_ATTRIBUTE_DIRECTORY); + if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & + FILE_SUPPORTS_SPARSE_FILES)) + attr |= FILE_ATTRIBUTE_SPARSE_FILE; + + if (smb2_get_reparse_tag_special_file(stat->mode)) + attr |= FILE_ATTRIBUTE_REPARSE_POINT; + } + + return attr; +} + +static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, + __le16 hash_id) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = hash_id; +} + +static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, + __le16 cipher_type) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(4); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CipherCount = cpu_to_le16(1); + pneg_ctxt->Ciphers[0] = cipher_type; +} + +static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt, + __le16 comp_algo) +{ + pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Flags = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithms[0] = comp_algo; +} + +static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, + __le16 sign_algo) +{ + pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SigningAlgorithms[0] = sign_algo; +} + +static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + pneg_ctxt->Name[0] = 0x93; + pneg_ctxt->Name[1] = 0xAD; + pneg_ctxt->Name[2] = 0x25; + pneg_ctxt->Name[3] = 0x50; + pneg_ctxt->Name[4] = 0x9C; + pneg_ctxt->Name[5] = 0xB4; + pneg_ctxt->Name[6] = 0x11; + pneg_ctxt->Name[7] = 0xE7; + pneg_ctxt->Name[8] = 0xB4; + pneg_ctxt->Name[9] = 0x23; + pneg_ctxt->Name[10] = 0x83; + pneg_ctxt->Name[11] = 0xDE; + pneg_ctxt->Name[12] = 0x96; + pneg_ctxt->Name[13] = 0x8B; + pneg_ctxt->Name[14] = 0xCD; + pneg_ctxt->Name[15] = 0x7C; +} + +static void assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp, + void *smb2_buf_len) +{ + char *pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset); + int neg_ctxt_cnt = 1; + int ctxt_size; + + ksmbd_debug(SMB, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); + rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); + inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); + + if (conn->cipher_type) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, + conn->cipher_type); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += + round_up(sizeof(struct smb2_encryption_neg_context) + 2, + 8); + } + + if (conn->compress_algorithm) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); + /* Temporarily set to SMB3_COMPRESS_NONE */ + build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, + conn->compress_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, + 8); + } + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_posix_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); + } + + if (conn->signing_negotiated) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); + build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, + conn->signing_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; + } + + inc_rfc1001_len(smb2_buf_len, ctxt_size); +} + +static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, + struct smb2_preauth_neg_context *pneg_ctxt, + int ctxt_len) +{ + /* + * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, + * which may not be present. Only check for used HashAlgorithms[1]. + */ + if (ctxt_len < + sizeof(struct smb2_neg_context) + MIN_PREAUTH_CTXT_DATA_LEN) + return STATUS_INVALID_PARAMETER; + + if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) + return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; + + conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; + return STATUS_SUCCESS; +} + +static void decode_encrypt_ctxt(struct ksmbd_conn *conn, + struct smb2_encryption_neg_context *pneg_ctxt, + int ctxt_len) +{ + int cph_cnt; + int i, cphs_size; + + if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { + pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); + return; + } + + conn->cipher_type = 0; + + cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + cphs_size = cph_cnt * sizeof(__le16); + + if (sizeof(struct smb2_encryption_neg_context) + cphs_size > + ctxt_len) { + pr_err("Invalid cipher count(%d)\n", cph_cnt); + return; + } + + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) + return; + + for (i = 0; i < cph_cnt; i++) { + if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(SMB, "Cipher ID = 0x%x\n", + pneg_ctxt->Ciphers[i]); + conn->cipher_type = pneg_ctxt->Ciphers[i]; + break; + } + } +} + +/** + * smb3_encryption_negotiated() - checks if server and client agreed on enabling encryption + * @conn: smb connection + * + * Return: true if connection should be encrypted, else false + */ +bool smb3_encryption_negotiated(struct ksmbd_conn *conn) +{ + if (!conn->ops->generate_encryptionkey) + return false; + + /* + * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. + * SMB 3.1.1 uses the cipher_type field. + */ + return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || + conn->cipher_type; +} + +static void decode_compress_ctxt(struct ksmbd_conn *conn, + struct smb2_compression_capabilities_context *pneg_ctxt) +{ + conn->compress_algorithm = SMB3_COMPRESS_NONE; +} + +static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, + struct smb2_signing_capabilities *pneg_ctxt, + int ctxt_len) +{ + int sign_algo_cnt; + int i, sign_alos_size; + + if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { + pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); + return; + } + + conn->signing_negotiated = false; + sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); + sign_alos_size = sign_algo_cnt * sizeof(__le16); + + if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > + ctxt_len) { + pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); + return; + } + + for (i = 0; i < sign_algo_cnt; i++) { + if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256_LE || + pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC_LE) { + ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", + pneg_ctxt->SigningAlgorithms[i]); + conn->signing_negotiated = true; + conn->signing_algorithm = + pneg_ctxt->SigningAlgorithms[i]; + break; + } + } +} + +static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_req *req, + unsigned int len_of_smb) +{ + /* +4 is to account for the RFC1001 len field */ + struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; + int i = 0, len_of_ctxts; + unsigned int offset = le32_to_cpu(req->NegotiateContextOffset); + unsigned int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); + __le32 status = STATUS_INVALID_PARAMETER; + + ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); + if (len_of_smb <= offset) { + ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); + return status; + } + + len_of_ctxts = len_of_smb - offset; + + while (i++ < neg_ctxt_cnt) { + int clen, ctxt_len; + + if (len_of_ctxts < (int)sizeof(struct smb2_neg_context)) + break; + + pctx = (struct smb2_neg_context *)((char *)pctx + offset); + clen = le16_to_cpu(pctx->DataLength); + ctxt_len = clen + sizeof(struct smb2_neg_context); + + if (ctxt_len > len_of_ctxts) + break; + + if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + if (conn->preauth_info->Preauth_HashId) + break; + + status = decode_preauth_ctxt(conn, + (struct smb2_preauth_neg_context *)pctx, + ctxt_len); + if (status != STATUS_SUCCESS) + break; + } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + if (conn->cipher_type) + break; + + decode_encrypt_ctxt(conn, + (struct smb2_encryption_neg_context *)pctx, + ctxt_len); + } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); + if (conn->compress_algorithm) + break; + + decode_compress_ctxt(conn, + (struct smb2_compression_capabilities_context *)pctx); + } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { + ksmbd_debug(SMB, + "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); + } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { + ksmbd_debug(SMB, + "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + conn->posix_ext_supported = true; + } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + + decode_sign_cap_ctxt(conn, + (struct smb2_signing_capabilities *)pctx, + ctxt_len); + } + + /* offsets must be 8 byte aligned */ + offset = (ctxt_len + 7) & ~0x7; + len_of_ctxts -= offset; + } + return status; +} + +/** + * smb2_handle_negotiate() - handler for smb2 negotiate command + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int smb2_handle_negotiate(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); + int rc = 0; + unsigned int smb2_buf_len, smb2_neg_size; + __le32 status; + + ksmbd_debug(SMB, "Received negotiate request\n"); + conn->need_neg = false; + if (ksmbd_conn_good(conn)) { + pr_err("conn->tcp_status is already in CifsGood State\n"); + work->send_no_response = 1; + return rc; + } + + smb2_buf_len = get_rfc1002_len(work->request_buf); + smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); + if (smb2_neg_size > smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (req->DialectCount == 0) { + pr_err("malformed packet\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (conn->dialect == SMB311_PROT_ID) { + unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); + + if (smb2_buf_len < nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size > nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } else { + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } + + conn->cli_cap = le32_to_cpu(req->Capabilities); + switch (conn->dialect) { + case SMB311_PROT_ID: + conn->preauth_info = + kzalloc(sizeof(struct preauth_integrity_info), + GFP_KERNEL); + if (!conn->preauth_info) { + rc = -ENOMEM; + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto err_out; + } + + status = deassemble_neg_contexts(conn, req, + get_rfc1002_len(work->request_buf)); + if (status != STATUS_SUCCESS) { + pr_err("deassemble_neg_contexts error(0x%x)\n", + status); + rsp->hdr.Status = status; + rc = -EINVAL; + kfree(conn->preauth_info); + conn->preauth_info = NULL; + goto err_out; + } + + rc = init_smb3_11_server(conn); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + kfree(conn->preauth_info); + conn->preauth_info = NULL; + goto err_out; + } + + ksmbd_gen_preauth_integrity_hash(conn, + work->request_buf, + conn->preauth_info->Preauth_HashValue); + rsp->NegotiateContextOffset = + cpu_to_le32(OFFSET_OF_NEG_CONTEXT); + assemble_neg_contexts(conn, rsp, work->response_buf); + break; + case SMB302_PROT_ID: + init_smb3_02_server(conn); + break; + case SMB30_PROT_ID: + init_smb3_0_server(conn); + break; + case SMB21_PROT_ID: + init_smb2_1_server(conn); + break; + case SMB2X_PROT_ID: + case BAD_PROT_ID: + default: + ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", + conn->dialect); + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + rc = -EINVAL; + goto err_out; + } + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + + /* For stats */ + conn->connection_type = conn->dialect; + + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + memcpy(conn->ClientGUID, req->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); + + rsp->StructureSize = cpu_to_le16(65); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying server + */ + memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", + le32_to_cpu(rsp->NegotiateContextOffset), + le16_to_cpu(rsp->NegotiateContextCount)); + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + conn->use_spnego = true; + + if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || + server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && + req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) + conn->sign = true; + else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { + server_conf.enforced_signing = true; + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->sign = true; + } + + conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); + ksmbd_conn_set_need_negotiate(conn); + +err_out: + if (rc < 0) + smb2_set_err_rsp(work); + + return rc; +} + +static int alloc_preauth_hash(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + if (sess->Preauth_HashValue) + return 0; + + sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + if (!sess->Preauth_HashValue) + return -ENOMEM; + + return 0; +} + +static int generate_preauth_hash(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + u8 *preauth_hash; + + if (conn->dialect != SMB311_PROT_ID) + return 0; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) { + preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); + if (!preauth_sess) + return -ENOMEM; + } + + preauth_hash = preauth_sess->Preauth_HashValue; + } else { + if (!sess->Preauth_HashValue) + if (alloc_preauth_hash(sess, conn)) + return -ENOMEM; + preauth_hash = sess->Preauth_HashValue; + } + + ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); + return 0; +} + +static int decode_negotiation_token(struct ksmbd_conn *conn, + struct negotiate_message *negblob, + size_t sz) +{ + if (!conn->use_spnego) + return -EINVAL; + + if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; + conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; + conn->use_spnego = false; + } + } + return 0; +} + +static int ntlm_negotiate(struct ksmbd_work *work, + struct negotiate_message *negblob, + size_t negblob_len) +{ + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct challenge_message *chgblob; + unsigned char *spnego_blob = NULL; + u16 spnego_blob_len; + char *neg_blob; + int sz, rc; + + ksmbd_debug(SMB, "negotiate phase\n"); + rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->conn); + if (rc) + return rc; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + chgblob = + (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); + memset(chgblob, 0, sizeof(struct challenge_message)); + + if (!work->conn->use_spnego) { + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) + return -ENOMEM; + + rsp->SecurityBufferLength = cpu_to_le16(sz); + return 0; + } + + sz = sizeof(struct challenge_message); + sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; + + neg_blob = kzalloc(sz, GFP_KERNEL); + if (!neg_blob) + return -ENOMEM; + + chgblob = (struct challenge_message *)neg_blob; + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) { + rc = -ENOMEM; + goto out; + } + + rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, + neg_blob, sz); + if (rc) { + rc = -ENOMEM; + goto out; + } + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + +out: + kfree(spnego_blob); + kfree(neg_blob); + return rc; +} + +static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + int sz; + + if (conn->use_spnego && conn->mechToken) + return (struct authenticate_message *)conn->mechToken; + + sz = le16_to_cpu(req->SecurityBufferOffset); + return (struct authenticate_message *)((char *)&req->hdr.ProtocolId + + sz); +} + +static struct ksmbd_user *session_user(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + struct authenticate_message *authblob; + struct ksmbd_user *user; + char *name; + unsigned int name_off, name_len, secbuf_len; + + secbuf_len = le16_to_cpu(req->SecurityBufferLength); + if (secbuf_len < sizeof(struct authenticate_message)) { + ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); + return NULL; + } + authblob = user_authblob(conn, req); + name_off = le32_to_cpu(authblob->UserName.BufferOffset); + name_len = le16_to_cpu(authblob->UserName.Length); + + if (secbuf_len < (u64)name_off + name_len) + return NULL; + + name = smb_strndup_from_utf16((const char *)authblob + name_off, + name_len, + true, + conn->local_nls); + if (IS_ERR(name)) { + pr_err("cannot allocate memory\n"); + return NULL; + } + + ksmbd_debug(SMB, "session setup request for user %s\n", name); + user = ksmbd_login_user(name); + kfree(name); + return user; +} + +static int ntlm_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct channel *chann = NULL; + struct ksmbd_user *user; + u64 prev_id; + int sz, rc; + + ksmbd_debug(SMB, "authenticate phase\n"); + if (conn->use_spnego) { + unsigned char *spnego_blob; + u16 spnego_blob_len; + + rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, + &spnego_blob_len, + 0); + if (rc) + return -ENOMEM; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); + } + + user = session_user(conn, req); + if (!user) { + ksmbd_debug(SMB, "Unknown user name or an error\n"); + return -EPERM; + } + + /* Check for previous session */ + prev_id = le64_to_cpu(req->PreviousSessionId); + if (prev_id && prev_id != sess->id) + destroy_previous_session(conn, user, prev_id); + + if (sess->state == SMB2_SESSION_VALID) { + /* + * Reuse session if anonymous try to connect + * on reauthetication. + */ + if (conn->binding == false && ksmbd_anonymous_user(user)) { + ksmbd_free_user(user); + return 0; + } + + if (!ksmbd_compare_user(sess->user, user)) { + ksmbd_free_user(user); + return -EPERM; + } + ksmbd_free_user(user); + } else { + sess->user = user; + } + + if (conn->binding == false && user_guest(sess->user)) { + rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; + } else { + struct authenticate_message *authblob; + + authblob = user_authblob(conn, req); + sz = le16_to_cpu(req->SecurityBufferLength); + rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); + if (rc) { + set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); + ksmbd_debug(SMB, "authentication failed\n"); + return -EPERM; + } + } + + /* + * If session state is SMB2_SESSION_VALID, We can assume + * that it is reauthentication. And the user/password + * has been verified, so return it here. + */ + if (sess->state == SMB2_SESSION_VALID) { + if (conn->binding) + goto binding_session; + return 0; + } + + if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && + (conn->sign || server_conf.enforced_signing)) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn) && + !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + rc = conn->ops->generate_encryptionkey(conn, sess); + if (rc) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + /* + * signing is disable if encryption is enable + * on this session + */ + sess->sign = false; + } + +binding_session: + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + } + } + + if (conn->ops->generate_signingkey) { + rc = conn->ops->generate_signingkey(sess, conn); + if (rc) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + return 0; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +static int krb5_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + char *in_blob, *out_blob; + struct channel *chann = NULL; + u64 prev_sess_id; + int in_len, out_len; + int retval; + + in_blob = (char *)&req->hdr.ProtocolId + + le16_to_cpu(req->SecurityBufferOffset); + in_len = le16_to_cpu(req->SecurityBufferLength); + out_blob = (char *)&rsp->hdr.ProtocolId + + le16_to_cpu(rsp->SecurityBufferOffset); + out_len = work->response_sz - + (le16_to_cpu(rsp->SecurityBufferOffset) + 4); + + /* Check previous session */ + prev_sess_id = le64_to_cpu(req->PreviousSessionId); + if (prev_sess_id && prev_sess_id != sess->id) + destroy_previous_session(conn, sess->user, prev_sess_id); + + if (sess->state == SMB2_SESSION_VALID) + ksmbd_free_user(sess->user); + + retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, + out_blob, &out_len); + if (retval) { + ksmbd_debug(SMB, "krb5 authentication failed\n"); + return -EINVAL; + } + rsp->SecurityBufferLength = cpu_to_le16(out_len); + inc_rfc1001_len(work->response_buf, out_len - 1); + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn)) { + retval = conn->ops->generate_encryptionkey(conn, sess); + if (retval) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + sess->sign = false; + } + + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + } + } + + if (conn->ops->generate_signingkey) { + retval = conn->ops->generate_signingkey(sess, conn); + if (retval) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + return 0; +} +#else +static int krb5_authenticate(struct ksmbd_work *work) +{ + return -EOPNOTSUPP; +} +#endif + +int smb2_sess_setup(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess; + struct negotiate_message *negblob; + unsigned int negblob_len, negblob_off; + int rc = 0; + + ksmbd_debug(SMB, "Received request for session setup\n"); + + rsp->StructureSize = cpu_to_le16(9); + rsp->SessionFlags = 0; + rsp->SecurityBufferOffset = cpu_to_le16(72); + rsp->SecurityBufferLength = 0; + inc_rfc1001_len(work->response_buf, 9); + + ksmbd_conn_lock(conn); + if (!req->hdr.SessionId) { + sess = ksmbd_smb2_session_create(); + if (!sess) { + rc = -ENOMEM; + goto out_err; + } + rsp->hdr.SessionId = cpu_to_le64(sess->id); + rc = ksmbd_session_register(conn, sess); + if (rc) + goto out_err; + } else if (conn->dialect >= SMB30_PROT_ID && + (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { + u64 sess_id = le64_to_cpu(req->hdr.SessionId); + + sess = ksmbd_session_lookup_slowpath(sess_id); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + + if (conn->dialect != sess->dialect) { + rc = -EINVAL; + goto out_err; + } + + if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { + rc = -EINVAL; + goto out_err; + } + + if (strncmp(conn->ClientGUID, sess->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + rc = -ENOENT; + goto out_err; + } + + if (sess->state == SMB2_SESSION_IN_PROGRESS) { + rc = -EACCES; + goto out_err; + } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_conn_need_reconnect(conn)) { + rc = -EFAULT; + sess = NULL; + goto out_err; + } + + if (ksmbd_session_lookup(conn, sess_id)) { + rc = -EACCES; + goto out_err; + } + + if (user_guest(sess->user)) { + rc = -EOPNOTSUPP; + goto out_err; + } + + conn->binding = true; + } else if ((conn->dialect < SMB30_PROT_ID || + server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + sess = NULL; + rc = -EACCES; + goto out_err; + } else { + sess = ksmbd_session_lookup(conn, + le64_to_cpu(req->hdr.SessionId)); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_conn_need_reconnect(conn)) { + rc = -EFAULT; + sess = NULL; + goto out_err; + } + } + work->sess = sess; + + negblob_off = le16_to_cpu(req->SecurityBufferOffset); + negblob_len = le16_to_cpu(req->SecurityBufferLength); + if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || + negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { + rc = -EINVAL; + goto out_err; + } + + negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + + negblob_off); + + if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { + if (conn->mechToken) + negblob = (struct negotiate_message *)conn->mechToken; + } + + if (server_conf.auth_mechs & conn->auth_mechs) { + rc = generate_preauth_hash(work); + if (rc) + goto out_err; + + if (conn->preferred_auth_mech & + (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { + rc = krb5_authenticate(work); + if (rc) { + rc = -EINVAL; + goto out_err; + } + + if (!ksmbd_conn_need_reconnect(conn)) { + ksmbd_conn_set_good(conn); + sess->state = SMB2_SESSION_VALID; + } + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { + if (negblob->MessageType == NtLmNegotiate) { + rc = ntlm_negotiate(work, negblob, negblob_len); + if (rc) + goto out_err; + rsp->hdr.Status = + STATUS_MORE_PROCESSING_REQUIRED; + /* + * Note: here total size -1 is done as an + * adjustment for 0 size blob + */ + inc_rfc1001_len(work->response_buf, + le16_to_cpu(rsp->SecurityBufferLength) - 1); + + } else if (negblob->MessageType == NtLmAuthenticate) { + rc = ntlm_authenticate(work); + if (rc) + goto out_err; + + if (!ksmbd_conn_need_reconnect(conn)) { + ksmbd_conn_set_good(conn); + sess->state = SMB2_SESSION_VALID; + } + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = + ksmbd_preauth_session_lookup(conn, sess->id); + if (preauth_sess) { + list_del(&preauth_sess->preauth_entry); + kfree(preauth_sess); + } + } + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } else { + pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", + le32_to_cpu(negblob->MessageType)); + rc = -EINVAL; + } + } else { + /* TODO: need one more negotiation */ + pr_err("Not support the preferred authentication\n"); + rc = -EINVAL; + } + } else { + pr_err("Not support authentication\n"); + rc = -EINVAL; + } + +out_err: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc) + rsp->hdr.Status = STATUS_LOGON_FAILURE; + + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + + if (rc < 0) { + /* + * SecurityBufferOffset should be set to zero + * in session setup error response. + */ + rsp->SecurityBufferOffset = 0; + + if (sess) { + bool try_delay = false; + + /* + * To avoid dictionary attacks (repeated session setups rapidly sent) to + * connect to server, ksmbd make a delay of a 5 seconds on session setup + * failure to make it harder to send enough random connection requests + * to break into a server. + */ + if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) + try_delay = true; + + sess->last_active = jiffies; + sess->state = SMB2_SESSION_EXPIRED; + if (try_delay) { + ksmbd_conn_set_need_reconnect(conn); + ssleep(5); + ksmbd_conn_set_need_negotiate(conn); + } + } + } + + ksmbd_conn_unlock(conn); + return rc; +} + +/** + * smb2_tree_connect() - handler for smb2 tree connect command + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_tree_connect(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_tree_connect_req *req = smb2_get_msg(work->request_buf); + struct smb2_tree_connect_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess = work->sess; + char *treename = NULL, *name = NULL; + struct ksmbd_tree_conn_status status; + struct ksmbd_share_config *share; + int rc = -EINVAL; + + treename = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->PathLength), true, + conn->local_nls); + if (IS_ERR(treename)) { + pr_err("treename is NULL\n"); + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + name = ksmbd_extract_sharename(conn->um, treename); + if (IS_ERR(name)) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", + name, treename); + + status = ksmbd_tree_conn_connect(conn, sess, name); + if (status.ret == KSMBD_TREE_CONN_STATUS_OK) + rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); + else + goto out_err1; + + share = status.tree_conn->share_conf; + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC share path request\n"); + rsp->ShareType = SMB2_SHARE_TYPE_PIPE; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | + FILE_DELETE_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } else { + rsp->ShareType = SMB2_SHARE_TYPE_DISK; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; + if (test_tree_conn_flag(status.tree_conn, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + rsp->MaximalAccess |= FILE_WRITE_DATA_LE | + FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | + FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | + FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } + } + + status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); + if (conn->posix_ext_supported) + status.tree_conn->posix_extensions = true; + + rsp->StructureSize = cpu_to_le16(16); + inc_rfc1001_len(work->response_buf, 16); +out_err1: + rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(name)) + kfree(name); + + switch (status.ret) { + case KSMBD_TREE_CONN_STATUS_OK: + rsp->hdr.Status = STATUS_SUCCESS; + rc = 0; + break; + case -ESTALE: + case -ENOENT: + case KSMBD_TREE_CONN_STATUS_NO_SHARE: + rsp->hdr.Status = STATUS_BAD_NETWORK_NAME; + break; + case -ENOMEM: + case KSMBD_TREE_CONN_STATUS_NOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + case KSMBD_TREE_CONN_STATUS_ERROR: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + break; + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + default: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + } + + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + smb2_set_err_rsp(work); + + return rc; +} + +/** + * smb2_create_open_flags() - convert smb open flags to unix open flags + * @file_present: is file already present + * @access: file access flags + * @disposition: file disposition flags + * @may_flags: set with MAY_ flags + * + * Return: file open flags + */ +static int smb2_create_open_flags(bool file_present, __le32 access, + __le32 disposition, + int *may_flags) +{ + int oflags = O_NONBLOCK | O_LARGEFILE; + + if (access & FILE_READ_DESIRED_ACCESS_LE && + access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_RDWR; + *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; + } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_WRONLY; + *may_flags = MAY_OPEN | MAY_WRITE; + } else { + oflags |= O_RDONLY; + *may_flags = MAY_OPEN | MAY_READ; + } + + if (access == FILE_READ_ATTRIBUTES_LE) + oflags |= O_PATH; + + if (file_present) { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_OPEN_LE: + case FILE_CREATE_LE: + break; + case FILE_SUPERSEDE_LE: + case FILE_OVERWRITE_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_TRUNC; + break; + default: + break; + } + } else { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_SUPERSEDE_LE: + case FILE_CREATE_LE: + case FILE_OPEN_IF_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_CREAT; + break; + case FILE_OPEN_LE: + case FILE_OVERWRITE_LE: + oflags &= ~O_CREAT; + break; + default: + break; + } + } + + return oflags; +} + +/** + * smb2_tree_disconnect() - handler for smb tree connect request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_tree_disconnect(struct ksmbd_work *work) +{ + struct smb2_tree_disconnect_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(work->response_buf, 4); + + ksmbd_debug(SMB, "request\n"); + + if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { + struct smb2_tree_disconnect_req *req = + smb2_get_msg(work->request_buf); + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_close_tree_conn_fds(work); + ksmbd_tree_conn_disconnect(sess, tcon); + work->tcon = NULL; + return 0; +} + +/** + * smb2_session_logoff() - handler for session log off request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_session_logoff(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess; + struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); + u64 sess_id = le64_to_cpu(req->hdr.SessionId); + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(work->response_buf, 4); + + ksmbd_debug(SMB, "request\n"); + + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); + ksmbd_close_session_fds(work); + ksmbd_conn_wait_idle(conn, sess_id); + + /* + * Re-lookup session to validate if session is deleted + * while waiting request complete + */ + sess = ksmbd_session_lookup_all(conn, sess_id); + if (ksmbd_tree_conn_session_logoff(sess)) { + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_destroy_file_table(&sess->file_table); + sess->state = SMB2_SESSION_EXPIRED; + + ksmbd_free_user(sess->user); + sess->user = NULL; + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); + return 0; +} + +/** + * create_smb2_pipe() - create IPC pipe + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int create_smb2_pipe(struct ksmbd_work *work) +{ + struct smb2_create_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_create_req *req = smb2_get_msg(work->request_buf); + int id; + int err; + char *name; + + name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), + 1, work->conn->local_nls); + if (IS_ERR(name)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + err = PTR_ERR(name); + goto out; + } + + id = ksmbd_session_rpc_open(work->sess, name); + if (id < 0) { + pr_err("Unable to open RPC pipe: %d\n", id); + err = id; + goto out; + } + + rsp->hdr.Status = STATUS_SUCCESS; + rsp->StructureSize = cpu_to_le16(89); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Flags = 0; + rsp->CreateAction = cpu_to_le32(FILE_OPENED); + + rsp->CreationTime = cpu_to_le64(0); + rsp->LastAccessTime = cpu_to_le64(0); + rsp->ChangeTime = cpu_to_le64(0); + rsp->AllocationSize = cpu_to_le64(0); + rsp->EndofFile = cpu_to_le64(0); + rsp->FileAttributes = FILE_ATTRIBUTE_NORMAL_LE; + rsp->Reserved2 = 0; + rsp->VolatileFileId = id; + rsp->PersistentFileId = 0; + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + kfree(name); + return 0; + +out: + switch (err) { + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + case -ENOSPC: + case -ENOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + } + + if (!IS_ERR(name)) + kfree(name); + + smb2_set_err_rsp(work); + return err; +} + +/** + * smb2_set_ea() - handler for setting extended attributes using set + * info command + * @eabuf: set info command buffer + * @buf_len: set info command buffer length + * @path: dentry path for get ea + * + * Return: 0 on success, otherwise error + */ +static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + const struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *attr_name = NULL, *value; + int rc = 0; + unsigned int next = 0; + + if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + + le16_to_cpu(eabuf->EaValueLength)) + return -EINVAL; + + attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + if (!attr_name) + return -ENOMEM; + + do { + if (!eabuf->EaNameLength) + goto next; + + ksmbd_debug(SMB, + "name : <%s>, name_len : %u, value_len : %u, next : %u\n", + eabuf->name, eabuf->EaNameLength, + le16_to_cpu(eabuf->EaValueLength), + le32_to_cpu(eabuf->NextEntryOffset)); + + if (eabuf->EaNameLength > + (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + rc = -EINVAL; + break; + } + + memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); + memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, + eabuf->EaNameLength); + attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; + value = (char *)&eabuf->name + eabuf->EaNameLength + 1; + + if (!eabuf->EaValueLength) { + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + attr_name, + XATTR_USER_PREFIX_LEN + + eabuf->EaNameLength); + + /* delete the EA only when it exits */ + if (rc > 0) { + rc = ksmbd_vfs_remove_xattr(user_ns, + path->dentry, + attr_name); + + if (rc < 0) { + ksmbd_debug(SMB, + "remove xattr failed(%d)\n", + rc); + break; + } + } + + /* if the EA doesn't exist, just do nothing. */ + rc = 0; + } else { + rc = ksmbd_vfs_setxattr(user_ns, + path->dentry, attr_name, value, + le16_to_cpu(eabuf->EaValueLength), 0); + if (rc < 0) { + ksmbd_debug(SMB, + "ksmbd_vfs_setxattr is failed(%d)\n", + rc); + break; + } + } + +next: + next = le32_to_cpu(eabuf->NextEntryOffset); + if (next == 0 || buf_len < next) + break; + buf_len -= next; + eabuf = (struct smb2_ea_info *)((char *)eabuf + next); + if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) + break; + + } while (next != 0); + + kfree(attr_name); + return rc; +} + +static noinline int smb2_set_stream_name_xattr(const struct path *path, + struct ksmbd_file *fp, + char *stream_name, int s_type) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + size_t xattr_stream_size; + char *xattr_stream_name; + int rc; + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + return rc; + + fp->stream.name = xattr_stream_name; + fp->stream.size = xattr_stream_size; + + /* Check if there is stream prefix in xattr space */ + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + xattr_stream_name, + xattr_stream_size); + if (rc >= 0) + return 0; + + if (fp->cdoption == FILE_OPEN_LE) { + ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); + return -EBADF; + } + + rc = ksmbd_vfs_setxattr(user_ns, path->dentry, + xattr_stream_name, NULL, 0, 0); + if (rc < 0) + pr_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +} + +static int smb2_remove_smb_xattrs(const struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) { + err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, + name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", + name); + } + } +out: + kvfree(xattr_list); + return err; +} + +static int smb2_create_truncate(const struct path *path) +{ + int rc = vfs_truncate(path, 0); + + if (rc) { + pr_err("vfs_truncate failed, rc %d\n", rc); + return rc; + } + + rc = smb2_remove_smb_xattrs(path); + if (rc == -EOPNOTSUPP) + rc = 0; + if (rc) + ksmbd_debug(SMB, + "ksmbd_truncate_stream_name_xattr failed, rc %d\n", + rc); + return rc; +} + +static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *path, + struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da = {0}; + int rc; + + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + da.version = 4; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.itime = da.create_time = fp->create_time; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); +} + +static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, + const struct path *path, struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da; + int rc; + + fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE); + + /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc > 0) { + fp->f_ci->m_fattr = cpu_to_le32(da.attr); + fp->create_time = da.create_time; + fp->itime = da.itime; + } +} + +static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + int open_flags, umode_t posix_mode, bool is_dir) +{ + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = tcon->share_conf; + umode_t mode; + int rc; + + if (!(open_flags & O_CREAT)) + return -EBADF; + + ksmbd_debug(SMB, "file does not exist, so creating\n"); + if (is_dir == true) { + ksmbd_debug(SMB, "creating directory\n"); + + mode = share_config_directory_mode(share, posix_mode); + rc = ksmbd_vfs_mkdir(work, name, mode); + if (rc) + return rc; + } else { + ksmbd_debug(SMB, "creating regular file\n"); + + mode = share_config_create_mode(share, posix_mode); + rc = ksmbd_vfs_create(work, name, mode); + if (rc) + return rc; + } + + rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); + if (rc) { + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); + return rc; + } + return 0; +} + +static int smb2_create_sd_buffer(struct ksmbd_work *work, + struct smb2_create_req *req, + const struct path *path) +{ + struct create_context *context; + struct create_sd_buf_req *sd_buf; + + if (!req->CreateContextsOffset) + return -ENOENT; + + /* Parse SD BUFFER create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER, 4); + if (!context) + return -ENOENT; + else if (IS_ERR(context)) + return PTR_ERR(context); + + ksmbd_debug(SMB, + "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); + sd_buf = (struct create_sd_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_sd_buf_req)) + return -EINVAL; + return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, + le32_to_cpu(sd_buf->ccontext.DataLength), true); +} + +static void ksmbd_acls_fattr(struct smb_fattr *fattr, + struct user_namespace *mnt_userns, + struct inode *inode) +{ + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode); + + fattr->cf_uid = vfsuid_into_kuid(vfsuid); + fattr->cf_gid = vfsgid_into_kgid(vfsgid); + fattr->cf_mode = inode->i_mode; + fattr->cf_acls = NULL; + fattr->cf_dacls = NULL; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); + } +} + +/** + * smb2_open() - handler for smb file open request + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_open(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp; + struct path path; + struct ksmbd_share_config *share = tcon->share_conf; + struct ksmbd_file *fp = NULL; + struct file *filp = NULL; + struct user_namespace *user_ns = NULL; + struct kstat stat; + struct create_context *context; + struct lease_ctx_info *lc = NULL; + struct create_ea_buf_req *ea_buf = NULL; + struct oplock_info *opinfo; + __le32 *next_ptr = NULL; + int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; + int rc = 0; + int contxt_cnt = 0, query_disk_id = 0; + int maximal_access_ctxt = 0, posix_ctxt = 0; + int s_type = 0; + int next_off = 0; + char *name = NULL; + char *stream_name = NULL; + bool file_present = false, created = false, already_permitted = false; + int share_ret, need_truncate = 0; + u64 time; + umode_t posix_mode = 0; + __le32 daccess, maximal_access = 0; + + WORK_BUFFERS(work, req, rsp); + + if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && + (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "invalid flag in chained command\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return -EINVAL; + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe create request\n"); + return create_smb2_pipe(work); + } + + if (req->NameLength) { + if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + *(char *)req->Buffer == '\\') { + pr_err("not allow directory name included leading slash\n"); + rc = -EINVAL; + goto err_out1; + } + + name = smb2_get_name(req->Buffer, + le16_to_cpu(req->NameLength), + work->conn->local_nls); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + if (rc != -ENOMEM) + rc = -ENOENT; + name = NULL; + goto err_out1; + } + + ksmbd_debug(SMB, "converted name = %s\n", name); + if (strchr(name, ':')) { + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; + goto err_out1; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) + goto err_out1; + } + + rc = ksmbd_validate_filename(name); + if (rc < 0) + goto err_out1; + + if (ksmbd_share_veto_filename(share, name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", + name); + goto err_out1; + } + } else { + name = kstrdup("", GFP_KERNEL); + if (!name) { + rc = -ENOMEM; + goto err_out1; + } + } + + req_op_level = req->RequestedOplockLevel; + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + lc = parse_lease_state(req); + + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); + rc = -EIO; + rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; + goto err_out1; + } + + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { + pr_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); + rc = -EINVAL; + goto err_out1; + } else { + if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && + req->CreateOptions & FILE_RANDOM_ACCESS_LE) + req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); + + if (req->CreateOptions & + (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | + FILE_RESERVE_OPFILTER_LE)) { + rc = -EOPNOTSUPP; + goto err_out1; + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { + rc = -EINVAL; + goto err_out1; + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { + req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } + } + } + + if (le32_to_cpu(req->CreateDisposition) > + le32_to_cpu(FILE_OVERWRITE_IF_LE)) { + pr_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); + rc = -EINVAL; + goto err_out1; + } + + if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { + pr_err("Invalid desired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); + rc = -EACCES; + goto err_out1; + } + + if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { + pr_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); + rc = -EINVAL; + goto err_out1; + } + + if (req->CreateContextsOffset) { + /* Parse non-durable handle create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ea_buf = (struct create_ea_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_ea_buf_req)) { + rc = -EINVAL; + goto err_out1; + } + if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + rc = -EACCES; + goto err_out1; + } + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, + "get query maximal access context\n"); + maximal_access_ctxt = 1; + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_TIMEWARP_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, "get timewarp context\n"); + rc = -EBADF; + goto err_out1; + } + + if (tcon->posix_extensions) { + context = smb2_find_context_vals(req, + SMB2_CREATE_TAG_POSIX, 16); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + struct create_posix *posix = + (struct create_posix *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix) - 4) { + rc = -EINVAL; + goto err_out1; + } + ksmbd_debug(SMB, "get posix context\n"); + + posix_mode = le32_to_cpu(posix->Mode); + posix_ctxt = 1; + } + } + } + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out1; + } + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (!rc) { + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + /* + * If file exists with under flags, return access + * denied error. + */ + if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + path_put(&path); + goto err_out; + } + } else if (d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + } + + if (rc) { + if (rc != -ENOENT) + goto err_out; + ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", + name, rc); + rc = 0; + } else { + file_present = true; + user_ns = mnt_user_ns(path.mnt); + } + if (stream_name) { + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + } + } else { + if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) && + s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + } + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && + req->FileAttributes & FILE_ATTRIBUTE_NORMAL_LE) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + } + + if (rc < 0) + goto err_out; + } + + if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && + S_ISDIR(d_inode(path.dentry)->i_mode) && + !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", + name, req->CreateOptions); + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + !(req->CreateDisposition == FILE_CREATE_LE) && + !S_ISDIR(d_inode(path.dentry)->i_mode)) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (!stream_name && file_present && + req->CreateDisposition == FILE_CREATE_LE) { + rc = -EEXIST; + goto err_out; + } + + daccess = smb_map_generic_desired_access(req->DesiredAccess); + + if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = smb_check_perm_dacl(conn, &path, &daccess, + sess->user->uid); + if (rc) + goto err_out; + } + + if (daccess & FILE_MAXIMAL_ACCESS_LE) { + if (!file_present) { + daccess = cpu_to_le32(GENERIC_ALL_FLAGS); + } else { + rc = ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &daccess); + if (rc) + goto err_out; + already_permitted = true; + } + maximal_access = daccess; + } + + open_flags = smb2_create_open_flags(file_present, daccess, + req->CreateDisposition, + &may_flags); + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (open_flags & O_CREAT) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + goto err_out; + } + } + + /*create file if not present */ + if (!file_present) { + rc = smb2_creat(work, &path, name, open_flags, posix_mode, + req->CreateOptions & FILE_DIRECTORY_FILE_LE); + if (rc) { + if (rc == -ENOENT) { + rc = -EIO; + rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + goto err_out; + } + + created = true; + user_ns = mnt_user_ns(path.mnt); + if (ea_buf) { + if (le32_to_cpu(ea_buf->ccontext.DataLength) < + sizeof(struct smb2_ea_info)) { + rc = -EINVAL; + goto err_out; + } + + rc = smb2_set_ea(&ea_buf->ea, + le32_to_cpu(ea_buf->ccontext.DataLength), + &path); + if (rc == -EOPNOTSUPP) + rc = 0; + else if (rc) + goto err_out; + } + } else if (!already_permitted) { + /* FILE_READ_ATTRIBUTE is allowed without inode_permission, + * because execute(search) permission on a parent directory, + * is already granted. + */ + if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { + rc = inode_permission(user_ns, + d_inode(path.dentry), + may_flags); + if (rc) + goto err_out; + + if ((daccess & FILE_DELETE_LE) || + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = ksmbd_vfs_may_delete(user_ns, + path.dentry); + if (rc) + goto err_out; + } + } + } + + rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; + } + + rc = 0; + filp = dentry_open(&path, open_flags, current_cred()); + if (IS_ERR(filp)) { + rc = PTR_ERR(filp); + pr_err("dentry open for dir failed, rc %d\n", rc); + goto err_out; + } + + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = FILE_OPENED; + else + file_info = FILE_OVERWRITTEN; + + if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == + FILE_SUPERSEDE_LE) + file_info = FILE_SUPERSEDED; + } else if (open_flags & O_CREAT) { + file_info = FILE_CREATED; + } + + ksmbd_vfs_set_fadvise(filp, req->CreateOptions); + + /* Obtain Volatile-ID */ + fp = ksmbd_open_fd(work, filp); + if (IS_ERR(fp)) { + fput(filp); + rc = PTR_ERR(fp); + fp = NULL; + goto err_out; + } + + /* Get Persistent-ID */ + ksmbd_open_durable_fd(fp); + if (!has_file_id(fp->persistent_id)) { + rc = -ENOMEM; + goto err_out; + } + + fp->cdoption = req->CreateDisposition; + fp->daccess = daccess; + fp->saccess = req->ShareAccess; + fp->coption = req->CreateOptions; + + /* Set default windows and posix acls if creating new file */ + if (created) { + int posix_acl_rc; + struct inode *inode = d_inode(path.dentry); + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, + inode, + d_inode(path.dentry->d_parent)); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, &path, sess->user->uid, + sess->user->gid); + } + + if (rc) { + rc = smb2_create_sd_buffer(work, req, &path); + if (rc) { + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(user_ns, + inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + struct smb_fattr fattr; + struct smb_ntsd *pntsd; + int pntsd_size, ace_num = 0; + + ksmbd_acls_fattr(&fattr, user_ns, inode); + if (fattr.cf_acls) + ace_num = fattr.cf_acls->a_count; + if (fattr.cf_dacls) + ace_num += fattr.cf_dacls->a_count; + + pntsd = kmalloc(sizeof(struct smb_ntsd) + + sizeof(struct smb_sid) * 3 + + sizeof(struct smb_acl) + + sizeof(struct smb_ace) * ace_num * 2, + GFP_KERNEL); + if (!pntsd) { + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + goto err_out; + } + + rc = build_sec_desc(user_ns, + pntsd, NULL, 0, + OWNER_SECINFO | + GROUP_SECINFO | + DACL_SECINFO, + &pntsd_size, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + if (rc) { + kfree(pntsd); + goto err_out; + } + + rc = ksmbd_vfs_set_sd_xattr(conn, + user_ns, + path.dentry, + pntsd, + pntsd_size); + kfree(pntsd); + if (rc) + pr_err("failed to store ntacl in xattr : %d\n", + rc); + } + } + } + rc = 0; + } + + if (stream_name) { + rc = smb2_set_stream_name_xattr(&path, + fp, + stream_name, + s_type); + if (rc) + goto err_out; + file_info = FILE_CREATED; + } + + fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); + if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && + !fp->attrib_only && !stream_name) { + smb_break_all_oplock(work, fp); + need_truncate = 1; + } + + /* fp should be searchable through ksmbd_inode.m_fp_list + * after daccess, saccess, attrib_only, and stream are + * initialized. + */ + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + /* Check delete pending among previous fp before oplock break */ + if (ksmbd_inode_pending_delete(fp)) { + rc = -EBUSY; + goto err_out; + } + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && + !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { + rc = share_ret; + goto err_out; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { + req_op_level = smb2_map_lease_to_oplock(lc->req_state); + ksmbd_debug(SMB, + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", + name, req_op_level, lc->req_state); + rc = find_same_lease_key(sess, fp->f_ci, lc); + if (rc) + goto err_out; + } else if (open_flags == O_RDONLY && + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + rc = smb_grant_oplock(work, req_op_level, + fp->persistent_id, fp, + le32_to_cpu(req->hdr.Id.SyncId.TreeId), + lc, share_ret); + if (rc < 0) + goto err_out; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + + if (need_truncate) { + rc = smb2_create_truncate(&path); + if (rc) + goto err_out; + } + + if (req->CreateContextsOffset) { + struct create_alloc_size_req *az_req; + + az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, + SMB2_CREATE_ALLOCATION_SIZE, 4); + if (IS_ERR(az_req)) { + rc = PTR_ERR(az_req); + goto err_out; + } else if (az_req) { + loff_t alloc_size; + int err; + + if (le16_to_cpu(az_req->ccontext.DataOffset) + + le32_to_cpu(az_req->ccontext.DataLength) < + sizeof(struct create_alloc_size_req)) { + rc = -EINVAL; + goto err_out; + } + alloc_size = le64_to_cpu(az_req->AllocationSize); + ksmbd_debug(SMB, + "request smb2 create allocate size : %llu\n", + alloc_size); + smb_break_all_levII_oplock(work, fp, 1); + err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_size); + if (err < 0) + ksmbd_debug(SMB, + "vfs_fallocate is failed : %d\n", + err); + } + + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out; + } else if (context) { + ksmbd_debug(SMB, "get query on disk id context\n"); + query_disk_id = 1; + } + } + + rc = ksmbd_vfs_getattr(&path, &stat); + if (rc) + goto err_out; + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (req->FileAttributes || fp->f_ci->m_fattr == 0) + fp->f_ci->m_fattr = + cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); + + if (!created) + smb2_update_xattrs(tcon, &path, fp); + else + smb2_new_xattrs(tcon, &path, fp); + + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + + rsp->StructureSize = cpu_to_le16(89); + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; + rcu_read_unlock(); + rsp->Flags = 0; + rsp->CreateAction = cpu_to_le32(file_info); + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + rsp->ChangeTime = cpu_to_le64(time); + rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.blocks << 9); + rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + rsp->FileAttributes = fp->f_ci->m_fattr; + + rsp->Reserved2 = 0; + + rsp->PersistentFileId = fp->persistent_id; + rsp->VolatileFileId = fp->volatile_id; + + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + + /* If lease is request send lease context response */ + if (opinfo && opinfo->is_lease) { + struct create_context *lease_ccontext; + + ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", + name, opinfo->o_lease->state); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + + lease_ccontext = (struct create_context *)rsp->Buffer; + contxt_cnt++; + create_lease_buf(rsp->Buffer, opinfo->o_lease); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_lease_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_lease_size); + next_ptr = &lease_ccontext->Next; + next_off = conn->vals->create_lease_size; + } + + if (maximal_access_ctxt) { + struct create_context *mxac_ccontext; + + if (maximal_access == 0) + ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &maximal_access); + mxac_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_mxac_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + le32_to_cpu(maximal_access)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_mxac_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_mxac_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &mxac_ccontext->Next; + next_off = conn->vals->create_mxac_size; + } + + if (query_disk_id) { + struct create_context *disk_id_ccontext; + + disk_id_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_disk_id_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + stat.ino, tcon->id); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_disk_id_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_disk_id_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &disk_id_ccontext->Next; + next_off = conn->vals->create_disk_id_size; + } + + if (posix_ctxt) { + contxt_cnt++; + create_posix_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_posix_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_posix_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + } + + if (contxt_cnt > 0) { + rsp->CreateContextsOffset = + cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); + } + +err_out: + if (file_present || created) + path_put(&path); + ksmbd_revert_fsids(work); +err_out1: + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EPERM) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -EBUSY) + rsp->hdr.Status = STATUS_DELETE_PENDING; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (rc == -ENOEXEC) + rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; + else if (rc == -ENXIO) + rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rc == -EMFILE) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + if (fp) + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); + ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); + } + + kfree(name); + kfree(lc); + + return 0; +} + +static int readdir_info_level_struct_sz(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_full_directory_info); + case FILE_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_both_directory_info); + case FILE_DIRECTORY_INFORMATION: + return sizeof(struct file_directory_info); + case FILE_NAMES_INFORMATION: + return sizeof(struct file_names_info); + case FILEID_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_id_full_dir_info); + case FILEID_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_id_both_directory_info); + case SMB_FIND_FILE_POSIX_INFO: + return sizeof(struct smb2_posix_info); + default: + return -EOPNOTSUPP; + } +} + +static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); + d_info->name = ffdinfo->FileName; + d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); + return 0; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); + d_info->name = fbdinfo->FileName; + d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); + return 0; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); + d_info->name = fdinfo->FileName; + d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); + return 0; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); + d_info->name = fninfo->FileName; + d_info->name_len = le32_to_cpu(fninfo->FileNameLength); + return 0; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); + d_info->name = dinfo->FileName; + d_info->name_len = le32_to_cpu(dinfo->FileNameLength); + return 0; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); + d_info->name = fibdinfo->FileName; + d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); + return 0; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); + d_info->name = posix_info->name; + d_info->name_len = le32_to_cpu(posix_info->name_len); + return 0; + } + default: + return -EINVAL; + } +} + +/** + * smb2_populate_readdir_entry() - encode directory entry in smb2 response + * buffer + * @conn: connection instance + * @info_level: smb information level + * @d_info: structure included variables for query dir + * @ksmbd_kstat: ksmbd wrapper of dirent stat information + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + struct ksmbd_dir_info *d_info, + struct ksmbd_kstat *ksmbd_kstat) +{ + int next_entry_offset = 0; + char *conv_name; + int conv_len; + void *kstat; + int struct_sz, rc = 0; + + conv_name = ksmbd_convert_dir_info_name(d_info, + conn->local_nls, + &conv_len); + if (!conv_name) + return -ENOMEM; + + /* Somehow the name has only terminating NULL bytes */ + if (conv_len < 0) { + rc = -EINVAL; + goto free_conv_name; + } + + struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len; + next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); + d_info->last_entry_off_align = next_entry_offset - struct_sz; + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + rc = -ENOSPC; + goto free_conv_name; + } + + kstat = d_info->wptr; + if (info_level != FILE_NAMES_INFORMATION) + kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)kstat; + ffdinfo->FileNameLength = cpu_to_le32(conv_len); + ffdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (ffdinfo->EaSize) + ffdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + ffdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(ffdinfo->FileName, conv_name, conv_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)kstat; + fbdinfo->FileNameLength = cpu_to_le32(conv_len); + fbdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fbdinfo->EaSize) + fbdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + fbdinfo->ShortNameLength = 0; + fbdinfo->Reserved = 0; + if (d_info->hide_dot_file && d_info->name[0] == '.') + fbdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(fbdinfo->FileName, conv_name, conv_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)kstat; + fdinfo->FileNameLength = cpu_to_le32(conv_len); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(fdinfo->FileName, conv_name, conv_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)kstat; + fninfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fninfo->FileName, conv_name, conv_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)kstat; + dinfo->FileNameLength = cpu_to_le32(conv_len); + dinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (dinfo->EaSize) + dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + dinfo->Reserved = 0; + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (d_info->hide_dot_file && d_info->name[0] == '.') + dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(dinfo->FileName, conv_name, conv_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)kstat; + fibdinfo->FileNameLength = cpu_to_le32(conv_len); + fibdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fibdinfo->EaSize) + fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + fibdinfo->ShortNameLength = 0; + fibdinfo->Reserved = 0; + fibdinfo->Reserved2 = cpu_to_le16(0); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(fibdinfo->FileName, conv_name, conv_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + u64 time; + + posix_info = (struct smb2_posix_info *)kstat; + posix_info->Ignored = 0; + posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + posix_info->ChangeTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); + posix_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); + posix_info->LastWriteTime = cpu_to_le64(time); + posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); + posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); + posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); + posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); + posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); + posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); + posix_info->DosAttributes = + S_ISDIR(ksmbd_kstat->kstat->mode) ? + FILE_ATTRIBUTE_DIRECTORY_LE : FILE_ATTRIBUTE_ARCHIVE_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + posix_info->DosAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + /* + * SidBuffer(32) contain two sids(Domain sid(16), UNIX group sid(16)). + * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ + id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), + SIDUNIX_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); + id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), + SIDUNIX_GROUP, (struct smb_sid *)&posix_info->SidBuffer[16]); + memcpy(posix_info->name, conv_name, conv_len); + posix_info->name_len = cpu_to_le32(conv_len); + posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + + } /* switch (info_level) */ + + d_info->last_entry_offset = d_info->data_count; + d_info->data_count += next_entry_offset; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + + ksmbd_debug(SMB, + "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); + +free_conv_name: + kfree(conv_name); + return rc; +} + +struct smb2_query_dir_private { + struct ksmbd_work *work; + char *search_pattern; + struct ksmbd_file *dir_fp; + + struct ksmbd_dir_info *d_info; + int info_level; +}; + +static void lock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); +} + +static void unlock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_unlock(d_inode(dir)); +} + +static int process_query_dir_entries(struct smb2_query_dir_private *priv) +{ + struct user_namespace *user_ns = file_mnt_user_ns(priv->dir_fp->filp); + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + int rc; + int i; + + for (i = 0; i < priv->d_info->num_entry; i++) { + struct dentry *dent; + + if (dentry_name(priv->d_info, priv->info_level)) + return -EINVAL; + + lock_dir(priv->dir_fp); + dent = lookup_one(user_ns, priv->d_info->name, + priv->dir_fp->filp->f_path.dentry, + priv->d_info->name_len); + unlock_dir(priv->dir_fp); + + if (IS_ERR(dent)) { + ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", + priv->d_info->name, + PTR_ERR(dent)); + continue; + } + if (unlikely(d_is_negative(dent))) { + dput(dent); + ksmbd_debug(SMB, "Negative dentry `%s'\n", + priv->d_info->name); + continue; + } + + ksmbd_kstat.kstat = &kstat; + if (priv->info_level != FILE_NAMES_INFORMATION) + ksmbd_vfs_fill_dentry_attrs(priv->work, + user_ns, + dent, + &ksmbd_kstat); + + rc = smb2_populate_readdir_entry(priv->work->conn, + priv->info_level, + priv->d_info, + &ksmbd_kstat); + dput(dent); + if (rc) + return rc; + } + return 0; +} + +static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, + int info_level) +{ + int struct_sz; + int conv_len; + int next_entry_offset; + + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) + return -EOPNOTSUPP; + + conv_len = (d_info->name_len + 1) * 2; + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + return -ENOSPC; + } + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->wptr; + memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); + ffdinfo->FileName[d_info->name_len] = 0x00; + ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->wptr; + memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); + fbdinfo->FileName[d_info->name_len] = 0x00; + fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->wptr; + memcpy(fdinfo->FileName, d_info->name, d_info->name_len); + fdinfo->FileName[d_info->name_len] = 0x00; + fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->wptr; + memcpy(fninfo->FileName, d_info->name, d_info->name_len); + fninfo->FileName[d_info->name_len] = 0x00; + fninfo->FileNameLength = cpu_to_le32(d_info->name_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->wptr; + memcpy(dinfo->FileName, d_info->name, d_info->name_len); + dinfo->FileName[d_info->name_len] = 0x00; + dinfo->FileNameLength = cpu_to_le32(d_info->name_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; + memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); + fibdinfo->FileName[d_info->name_len] = 0x00; + fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->wptr; + memcpy(posix_info->name, d_info->name, d_info->name_len); + posix_info->name[d_info->name_len] = 0x00; + posix_info->name_len = cpu_to_le32(d_info->name_len); + posix_info->NextEntryOffset = + cpu_to_le32(next_entry_offset); + break; + } + } /* switch (info_level) */ + + d_info->num_entry++; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + return 0; +} + +static bool __query_dir(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + struct smb2_query_dir_private *priv; + struct ksmbd_dir_info *d_info; + int rc; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + priv = buf->private; + d_info = priv->d_info; + + /* dot and dotdot entries are already reserved */ + if (!strcmp(".", name) || !strcmp("..", name)) + return true; + if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) + return true; + if (!match_pattern(name, namlen, priv->search_pattern)) + return true; + + d_info->name = name; + d_info->name_len = namlen; + rc = reserve_populate_dentry(d_info, priv->info_level); + if (rc) + return false; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) + d_info->out_buf_len = 0; + return true; +} + +static int verify_info_level(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + case FILE_BOTH_DIRECTORY_INFORMATION: + case FILE_DIRECTORY_INFORMATION: + case FILE_NAMES_INFORMATION: + case FILEID_FULL_DIRECTORY_INFORMATION: + case FILEID_BOTH_DIRECTORY_INFORMATION: + case SMB_FIND_FILE_POSIX_INFO: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len) +{ + int free_len; + + free_len = (int)(work->response_sz - + (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len; + return free_len; +} + +static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, + unsigned short hdr2_len, + unsigned int out_buf_len) +{ + int free_len; + + if (out_buf_len > work->conn->vals->max_trans_size) + return -EINVAL; + + free_len = smb2_resp_buf_len(work, hdr2_len); + if (free_len < 0) + return -EINVAL; + + return min_t(int, out_buf_len, free_len); +} + +int smb2_query_dir(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_query_directory_req *req; + struct smb2_query_directory_rsp *rsp; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct ksmbd_file *dir_fp = NULL; + struct ksmbd_dir_info d_info; + int rc = 0; + char *srch_ptr = NULL; + unsigned char srch_flag; + int buffer_sz; + struct smb2_query_dir_private query_dir_private = {NULL, }; + + WORK_BUFFERS(work, req, rsp); + + if (ksmbd_override_fsids(work)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + smb2_set_err_rsp(work); + return -ENOMEM; + } + + rc = verify_info_level(req->FileInformationClass); + if (rc) { + rc = -EFAULT; + goto err_out2; + } + + dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!dir_fp) { + rc = -EBADF; + goto err_out2; + } + + if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || + inode_permission(file_mnt_user_ns(dir_fp->filp), + file_inode(dir_fp->filp), + MAY_READ | MAY_EXEC)) { + pr_err("no right to enumerate directory (%pD)\n", dir_fp->filp); + rc = -EACCES; + goto err_out2; + } + + if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { + pr_err("can't do query dir for a file\n"); + rc = -EINVAL; + goto err_out2; + } + + srch_flag = req->Flags; + srch_ptr = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->FileNameLength), 1, + conn->local_nls); + if (IS_ERR(srch_ptr)) { + ksmbd_debug(SMB, "Search Pattern not found\n"); + rc = -EINVAL; + goto err_out2; + } else { + ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); + } + + if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { + ksmbd_debug(SMB, "Restart directory scan\n"); + generic_file_llseek(dir_fp->filp, 0, SEEK_SET); + } + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + d_info.wptr = (char *)rsp->Buffer; + d_info.rptr = (char *)rsp->Buffer; + d_info.out_buf_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (d_info.out_buf_len < 0) { + rc = -EINVAL; + goto err_out; + } + d_info.flags = srch_flag; + + /* + * reserve dot and dotdot entries in head of buffer + * in first response + */ + rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, + dir_fp, &d_info, srch_ptr, + smb2_populate_readdir_entry); + if (rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) + d_info.hide_dot_file = true; + + buffer_sz = d_info.out_buf_len; + d_info.rptr = d_info.wptr; + query_dir_private.work = work; + query_dir_private.search_pattern = srch_ptr; + query_dir_private.dir_fp = dir_fp; + query_dir_private.d_info = &d_info; + query_dir_private.info_level = req->FileInformationClass; + dir_fp->readdir_data.private = &query_dir_private; + set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); + + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + /* + * req->OutputBufferLength is too small to contain even one entry. + * In this case, it immediately returns OutputBufferLength 0 to client. + */ + if (!d_info.out_buf_len && !d_info.num_entry) + goto no_buf_len; + if (rc > 0 || rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + d_info.wptr = d_info.rptr; + d_info.out_buf_len = buffer_sz; + rc = process_query_dir_entries(&query_dir_private); + if (rc) + goto err_out; + + if (!d_info.data_count && d_info.out_buf_len >= 0) { + if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + } else { + dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; + rsp->hdr.Status = STATUS_NO_MORE_FILES; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(0); + rsp->OutputBufferLength = cpu_to_le32(0); + rsp->Buffer[0] = 0; + inc_rfc1001_len(work->response_buf, 9); + } else { +no_buf_len: + ((struct file_directory_info *) + ((char *)rsp->Buffer + d_info.last_entry_offset)) + ->NextEntryOffset = 0; + if (d_info.data_count >= d_info.last_entry_off_align) + d_info.data_count -= d_info.last_entry_off_align; + + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); + inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); + } + + kfree(srch_ptr); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; + +err_out: + pr_err("error while processing smb2 query dir rc = %d\n", rc); + kfree(srch_ptr); + +err_out2: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_FILE_CORRUPT_ERROR; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; +} + +/** + * buffer_check_err() - helper function to check buffer errors + * @reqOutputBufferLength: max buffer length expected in command response + * @rsp: query info response buffer contains output buffer length + * @rsp_org: base response buffer pointer in case of chained response + * @infoclass_size: query info class response buffer size + * + * Return: 0 on success, otherwise error + */ +static int buffer_check_err(int reqOutputBufferLength, + struct smb2_query_info_rsp *rsp, + void *rsp_org, int infoclass_size) +{ + if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { + if (reqOutputBufferLength < infoclass_size) { + pr_err("Invalid Buffer Size Requested\n"); + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); + return -EINVAL; + } + + ksmbd_debug(SMB, "Buffer Overflow\n"); + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + + reqOutputBufferLength); + rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); + } + return 0; +} + +static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + + sinfo->AllocationSize = cpu_to_le64(4096); + sinfo->EndOfFile = cpu_to_le64(0); + sinfo->NumberOfLinks = cpu_to_le32(1); + sinfo->DeletePending = 1; + sinfo->Directory = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); +} + +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, + void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + + /* any unique number */ + file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +static int smb2_get_info_file_pipe(struct ksmbd_session *sess, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + u64 id; + int rc; + + /* + * Windows can sometime send query file info request on + * pipe without opening it, checking error condition here + */ + id = req->VolatileFileId; + if (!ksmbd_session_rpc_method(sess, id)) + return -ENOENT; + + ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", + req->FileInfoClass, req->VolatileFileId); + + switch (req->FileInfoClass) { + case FILE_STANDARD_INFORMATION: + get_standard_info_pipe(rsp, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, rsp_org, + FILE_STANDARD_INFORMATION_SIZE); + break; + case FILE_INTERNAL_INFORMATION: + get_internal_info_pipe(rsp, id, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, rsp_org, + FILE_INTERNAL_INFORMATION_SIZE); + break; + default: + ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", + req->FileInfoClass); + rc = -EOPNOTSUPP; + } + return rc; +} + +/** + * smb2_get_ea() - handler for smb2 get extended attribute command + * @work: smb work containing query info command buffer + * @fp: ksmbd_file pointer + * @req: get extended attribute request + * @rsp: response buffer pointer + * @rsp_org: base response buffer pointer in case of chained response + * + * Return: 0 on success, otherwise error + */ +static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_ea_info *eainfo, *prev_eainfo; + char *name, *ptr, *xattr_list = NULL, *buf; + int rc, name_len, value_len, xattr_list_len, idx; + ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; + struct smb2_ea_info_req *ea_req = NULL; + const struct path *path; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + + if (!(fp->daccess & FILE_READ_EA_LE)) { + pr_err("Not permitted to read ext attr : 0x%x\n", + fp->daccess); + return -EACCES; + } + + path = &fp->filp->f_path; + /* single EA entry is requested with given user.* name */ + if (req->InputBufferLength) { + if (le32_to_cpu(req->InputBufferLength) < + sizeof(struct smb2_ea_info_req)) + return -EINVAL; + + ea_req = (struct smb2_ea_info_req *)req->Buffer; + } else { + /* need to send all EAs, if no specific EA is requested*/ + if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) + ksmbd_debug(SMB, + "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", + le32_to_cpu(req->Flags)); + } + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + return -EINVAL; + + rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } else if (!rc) { /* there is no EA in the file */ + ksmbd_debug(SMB, "no ea data in the file\n"); + goto done; + } + xattr_list_len = rc; + + ptr = (char *)rsp->Buffer; + eainfo = (struct smb2_ea_info *)ptr; + prev_eainfo = eainfo; + idx = 0; + + while (idx < xattr_list_len) { + name = xattr_list + idx; + name_len = strlen(name); + + ksmbd_debug(SMB, "%s, len %d\n", name, name_len); + idx += name_len + 1; + + /* + * CIFS does not support EA other than user.* namespace, + * still keep the framework generic, to list other attrs + * in future. + */ + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) + continue; + + if (req->InputBufferLength && + strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, + ea_req->EaNameLength)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], + DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) + continue; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + name_len -= XATTR_USER_PREFIX_LEN; + + ptr = (char *)(&eainfo->name + name_len + 1); + buf_free_len -= (offsetof(struct smb2_ea_info, name) + + name_len + 1); + /* bailout if xattr can't fit in buf_free_len */ + value_len = ksmbd_vfs_getxattr(user_ns, path->dentry, + name, &buf); + if (value_len <= 0) { + rc = -ENOENT; + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } + + buf_free_len -= value_len; + if (buf_free_len < 0) { + kfree(buf); + break; + } + + memcpy(ptr, buf, value_len); + kfree(buf); + + ptr += value_len; + eainfo->Flags = 0; + eainfo->EaNameLength = name_len; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], + name_len); + else + memcpy(eainfo->name, name, name_len); + + eainfo->name[name_len] = '\0'; + eainfo->EaValueLength = cpu_to_le16(value_len); + next_offset = offsetof(struct smb2_ea_info, name) + + name_len + 1 + value_len; + + /* align next xattr entry at 4 byte bundary */ + alignment_bytes = ((next_offset + 3) & ~3) - next_offset; + if (alignment_bytes) { + memset(ptr, '\0', alignment_bytes); + ptr += alignment_bytes; + next_offset += alignment_bytes; + buf_free_len -= alignment_bytes; + } + eainfo->NextEntryOffset = cpu_to_le32(next_offset); + prev_eainfo = eainfo; + eainfo = (struct smb2_ea_info *)ptr; + rsp_data_cnt += next_offset; + + if (req->InputBufferLength) { + ksmbd_debug(SMB, "single entry requested\n"); + break; + } + } + + /* no more ea entries */ + prev_eainfo->NextEntryOffset = 0; +done: + rc = 0; + if (rsp_data_cnt == 0) + rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; + rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); + inc_rfc1001_len(rsp_org, rsp_data_cnt); +out: + kvfree(xattr_list); + return rc; +} + +static void get_file_access_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_access_info *file_info; + + file_info = (struct smb2_file_access_info *)rsp->Buffer; + file_info->AccessFlags = fp->daccess; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_access_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); +} + +static int get_file_basic_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_basic_info *basic_info; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + basic_info = (struct smb2_file_basic_info *)rsp->Buffer; + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + basic_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = fp->f_ci->m_fattr; + basic_info->Pad1 = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_basic_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); + return 0; +} + +static void get_file_standard_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + + inode = file_inode(fp->filp); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + delete_pending = ksmbd_inode_pending_delete(fp); + + sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9); + sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); + sinfo->DeletePending = delete_pending; + sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_standard_info)); +} + +static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_alignment_info *file_info; + + file_info = (struct smb2_file_alignment_info *)rsp->Buffer; + file_info->AlignmentRequirement = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alignment_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_alignment_info)); +} + +static int get_file_all_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_all_info *file_info; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + int conv_len; + char *filename; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + filename = convert_to_nt_pathname(work->tcon->share_conf, &fp->filp->f_path); + if (IS_ERR(filename)) + return PTR_ERR(filename); + + inode = file_inode(fp->filp); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); + + ksmbd_debug(SMB, "filename = %s\n", filename); + delete_pending = ksmbd_inode_pending_delete(fp); + file_info = (struct smb2_file_all_info *)rsp->Buffer; + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->Pad1 = 0; + file_info->AllocationSize = + cpu_to_le64(inode->i_blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->NumberOfLinks = + cpu_to_le32(get_nlink(&stat) - delete_pending); + file_info->DeletePending = delete_pending; + file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; + file_info->Pad2 = 0; + file_info->IndexNumber = cpu_to_le64(stat.ino); + file_info->EASize = 0; + file_info->AccessFlags = fp->daccess; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + file_info->Mode = fp->coption; + file_info->AlignmentRequirement = 0; + conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, + PATH_MAX, conn->local_nls, 0); + conv_len *= 2; + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); + kfree(filename); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + return 0; +} + +static void get_file_alternate_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_alt_name_info *file_info; + struct dentry *dentry = fp->filp->f_path.dentry; + int conv_len; + + spin_lock(&dentry->d_lock); + file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; + conv_len = ksmbd_extract_shortname(conn, + dentry->d_name.name, + file_info->FileName); + spin_unlock(&dentry->d_lock); + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); +} + +static void get_file_stream_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_stream_info *file_info; + char *stream_name, *xattr_list = NULL, *stream_buf; + struct kstat stat; + const struct path *path = &fp->filp->f_path; + ssize_t xattr_list_len; + int nbytes = 0, streamlen, stream_name_len, next, idx = 0; + int buf_free_len; + struct smb2_query_info_req *req = ksmbd_req_buf_next(work); + + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + file_info = (struct smb2_file_stream_info *)rsp->Buffer; + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + goto out; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + while (idx < xattr_list_len) { + stream_name = xattr_list + idx; + streamlen = strlen(stream_name); + idx += streamlen + 1; + + ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); + + if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], + STREAM_PREFIX, STREAM_PREFIX_LEN)) + continue; + + stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + + STREAM_PREFIX_LEN); + streamlen = stream_name_len; + + /* plus : size */ + streamlen += 1; + stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + if (!stream_buf) + break; + + streamlen = snprintf(stream_buf, streamlen + 1, + ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); + + next = sizeof(struct smb2_file_stream_info) + streamlen * 2; + if (next > buf_free_len) { + kfree(stream_buf); + break; + } + + file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + stream_buf, streamlen, + conn->local_nls, 0); + streamlen *= 2; + kfree(stream_buf); + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stream_name_len); + file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); + + nbytes += next; + buf_free_len -= next; + file_info->NextEntryOffset = cpu_to_le32(next); + } + +out: + if (!S_ISDIR(stat.mode) && + buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { + file_info = (struct smb2_file_stream_info *) + &rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + "::$DATA", 7, conn->local_nls, 0); + streamlen *= 2; + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stat.size); + file_info->StreamAllocationSize = cpu_to_le64(stat.blocks << 9); + nbytes += sizeof(struct smb2_file_stream_info) + streamlen; + } + + /* last entry offset should be 0 */ + file_info->NextEntryOffset = 0; + kvfree(xattr_list); + + rsp->OutputBufferLength = cpu_to_le32(nbytes); + inc_rfc1001_len(rsp_org, nbytes); +} + +static void get_file_internal_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + struct kstat stat; + + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + file_info->IndexNumber = cpu_to_le64(stat.ino); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_ntwrk_info *file_info; + struct inode *inode; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; + + inode = file_inode(fp->filp); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->AllocationSize = + cpu_to_le64(inode->i_blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->Reserved = cpu_to_le32(0); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + return 0; +} + +static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_file_ea_info *file_info; + + file_info = (struct smb2_file_ea_info *)rsp->Buffer; + file_info->EASize = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ea_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); +} + +static void get_file_position_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_pos_info *file_info; + + file_info = (struct smb2_file_pos_info *)rsp->Buffer; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_pos_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); +} + +static void get_file_mode_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_mode_info *file_info; + + file_info = (struct smb2_file_mode_info *)rsp->Buffer; + file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_mode_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); +} + +static void get_file_compression_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_comp_info *file_info; + struct kstat stat; + + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + + file_info = (struct smb2_file_comp_info *)rsp->Buffer; + file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); + file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; + file_info->CompressionUnitShift = 0; + file_info->ChunkShift = 0; + file_info->ClusterShift = 0; + memset(&file_info->Reserved[0], 0, 3); + + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_comp_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); +} + +static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_attr_tag_info *file_info; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; + file_info->FileAttributes = fp->f_ci->m_fattr; + file_info->ReparseTag = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); + return 0; +} + +static int find_file_posix_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb311_posix_qinfo *file_info; + struct inode *inode = file_inode(fp->filp); + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); + u64 time; + int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; + + file_info = (struct smb311_posix_qinfo *)rsp->Buffer; + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->DosAttributes = fp->f_ci->m_fattr; + file_info->Inode = cpu_to_le64(inode->i_ino); + file_info->EndOfFile = cpu_to_le64(inode->i_size); + file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); + file_info->HardLinks = cpu_to_le32(inode->i_nlink); + file_info->Mode = cpu_to_le32(inode->i_mode & 0777); + file_info->DeviceId = cpu_to_le32(inode->i_rdev); + + /* + * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)). + * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ + id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), + SIDUNIX_USER, (struct smb_sid *)&file_info->Sids[0]); + id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), + SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); + + rsp->OutputBufferLength = cpu_to_le32(out_buf_len); + inc_rfc1001_len(rsp_org, out_buf_len); + return out_buf_len; +} + +static int smb2_get_info_file(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_file *fp; + int fileinfoclass = 0; + int rc = 0; + int file_infoclass_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + /* smb2 info file called for pipe */ + return smb2_get_info_file_pipe(work->sess, req, rsp, + work->response_buf); + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + fileinfoclass = req->FileInfoClass; + + switch (fileinfoclass) { + case FILE_ACCESS_INFORMATION: + get_file_access_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; + break; + + case FILE_BASIC_INFORMATION: + rc = get_file_basic_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; + break; + + case FILE_STANDARD_INFORMATION: + get_file_standard_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + break; + + case FILE_ALIGNMENT_INFORMATION: + get_file_alignment_info(rsp, work->response_buf); + file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; + break; + + case FILE_ALL_INFORMATION: + rc = get_file_all_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_ALL_INFORMATION_SIZE; + break; + + case FILE_ALTERNATE_NAME_INFORMATION: + get_file_alternate_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; + break; + + case FILE_STREAM_INFORMATION: + get_file_stream_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + break; + + case FILE_INTERNAL_INFORMATION: + get_file_internal_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + break; + + case FILE_NETWORK_OPEN_INFORMATION: + rc = get_file_network_open_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; + break; + + case FILE_EA_INFORMATION: + get_file_ea_info(rsp, work->response_buf); + file_infoclass_size = FILE_EA_INFORMATION_SIZE; + break; + + case FILE_FULL_EA_INFORMATION: + rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); + file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; + break; + + case FILE_POSITION_INFORMATION: + get_file_position_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; + break; + + case FILE_MODE_INFORMATION: + get_file_mode_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_MODE_INFORMATION_SIZE; + break; + + case FILE_COMPRESSION_INFORMATION: + get_file_compression_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + break; + + case FILE_ATTRIBUTE_TAG_INFORMATION: + rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; + break; + case SMB_FIND_FILE_POSIX_INFO: + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + file_infoclass_size = find_file_posix_info(rsp, fp, + work->response_buf); + } + break; + default: + ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", + fileinfoclass); + rc = -EOPNOTSUPP; + } + if (!rc) + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf, + file_infoclass_size); + ksmbd_fd_put(work, fp); + return rc; +} + +static int smb2_get_info_filesystem(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + int fsinfoclass = 0; + struct kstatfs stfs; + struct path path; + int rc = 0, len; + int fs_infoclass_size = 0; + + if (!share->path) + return -EIO; + + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); + if (rc) { + pr_err("cannot create vfs path\n"); + return -EIO; + } + + rc = vfs_statfs(&path, &stfs); + if (rc) { + pr_err("cannot do stat of path %s\n", share->path); + path_put(&path); + return -EIO; + } + + fsinfoclass = req->FileInfoClass; + + switch (fsinfoclass) { + case FS_DEVICE_INFORMATION: + { + struct filesystem_device_info *info; + + info = (struct filesystem_device_info *)rsp->Buffer; + + info->DeviceType = cpu_to_le32(stfs.f_type); + info->DeviceCharacteristics = cpu_to_le32(0x00000020); + rsp->OutputBufferLength = cpu_to_le32(8); + inc_rfc1001_len(work->response_buf, 8); + fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; + break; + } + case FS_ATTRIBUTE_INFORMATION: + { + struct filesystem_attribute_info *info; + size_t sz; + + info = (struct filesystem_attribute_info *)rsp->Buffer; + info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | + FILE_PERSISTENT_ACLS | + FILE_UNICODE_ON_DISK | + FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_SUPPORTS_BLOCK_REFCOUNTING); + + info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) + info->Attributes |= cpu_to_le32(FILE_NAMED_STREAMS); + + info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); + len = smbConvertToUTF16((__le16 *)info->FileSystemName, + "NTFS", PATH_MAX, conn->local_nls, 0); + len = len * 2; + info->FileSystemNameLen = cpu_to_le32(len); + sz = sizeof(struct filesystem_attribute_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(work->response_buf, sz); + fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; + break; + } + case FS_VOLUME_INFORMATION: + { + struct filesystem_vol_info *info; + size_t sz; + unsigned int serial_crc = 0; + + info = (struct filesystem_vol_info *)(rsp->Buffer); + info->VolumeCreationTime = 0; + serial_crc = crc32_le(serial_crc, share->name, + strlen(share->name)); + serial_crc = crc32_le(serial_crc, share->path, + strlen(share->path)); + serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), + strlen(ksmbd_netbios_name())); + /* Taking dummy value of serial number*/ + info->SerialNumber = cpu_to_le32(serial_crc); + len = smbConvertToUTF16((__le16 *)info->VolumeLabel, + share->name, PATH_MAX, + conn->local_nls, 0); + len = len * 2; + info->VolumeLabelSize = cpu_to_le32(len); + info->Reserved = 0; + sz = sizeof(struct filesystem_vol_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(work->response_buf, sz); + fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; + break; + } + case FS_SIZE_INFORMATION: + { + struct filesystem_info *info; + + info = (struct filesystem_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(24); + inc_rfc1001_len(work->response_buf, 24); + fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; + break; + } + case FS_FULL_SIZE_INFORMATION: + { + struct smb2_fs_full_size_info *info; + + info = (struct smb2_fs_full_size_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->CallerAvailableAllocationUnits = + cpu_to_le64(stfs.f_bavail); + info->ActualAvailableAllocationUnits = + cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(32); + inc_rfc1001_len(work->response_buf, 32); + fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; + break; + } + case FS_OBJECT_ID_INFORMATION: + { + struct object_id_info *info; + + info = (struct object_id_info *)(rsp->Buffer); + + if (!user_guest(sess->user)) + memcpy(info->objid, user_passkey(sess->user), 16); + else + memset(info->objid, 0, 16); + + info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); + info->extended_info.version = cpu_to_le32(1); + info->extended_info.release = cpu_to_le32(1); + info->extended_info.rel_date = 0; + memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); + rsp->OutputBufferLength = cpu_to_le32(64); + inc_rfc1001_len(work->response_buf, 64); + fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; + break; + } + case FS_SECTOR_SIZE_INFORMATION: + { + struct smb3_fs_ss_info *info; + unsigned int sector_size = + min_t(unsigned int, path.mnt->mnt_sb->s_blocksize, 4096); + + info = (struct smb3_fs_ss_info *)(rsp->Buffer); + + info->LogicalBytesPerSector = cpu_to_le32(sector_size); + info->PhysicalBytesPerSectorForAtomicity = + cpu_to_le32(sector_size); + info->PhysicalBytesPerSectorForPerf = cpu_to_le32(sector_size); + info->FSEffPhysicalBytesPerSectorForAtomicity = + cpu_to_le32(sector_size); + info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | + SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); + info->ByteOffsetForSectorAlignment = 0; + info->ByteOffsetForPartitionAlignment = 0; + rsp->OutputBufferLength = cpu_to_le32(28); + inc_rfc1001_len(work->response_buf, 28); + fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; + break; + } + case FS_CONTROL_INFORMATION: + { + /* + * TODO : The current implementation is based on + * test result with win7(NTFS) server. It's need to + * modify this to get valid Quota values + * from Linux kernel + */ + struct smb2_fs_control_info *info; + + info = (struct smb2_fs_control_info *)(rsp->Buffer); + info->FreeSpaceStartFiltering = 0; + info->FreeSpaceThreshold = 0; + info->FreeSpaceStopFiltering = 0; + info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); + info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->Padding = 0; + rsp->OutputBufferLength = cpu_to_le32(48); + inc_rfc1001_len(work->response_buf, 48); + fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; + break; + } + case FS_POSIX_INFORMATION: + { + struct filesystem_posix_info *info; + + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + info = (struct filesystem_posix_info *)(rsp->Buffer); + info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); + info->BlockSize = cpu_to_le32(stfs.f_bsize); + info->TotalBlocks = cpu_to_le64(stfs.f_blocks); + info->BlocksAvail = cpu_to_le64(stfs.f_bfree); + info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); + info->TotalFileNodes = cpu_to_le64(stfs.f_files); + info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + rsp->OutputBufferLength = cpu_to_le32(56); + inc_rfc1001_len(work->response_buf, 56); + fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; + } + break; + } + default: + path_put(&path); + return -EOPNOTSUPP; + } + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf, + fs_infoclass_size); + path_put(&path); + return rc; +} + +static int smb2_get_info_sec(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; + struct smb_fattr fattr = {{0}}; + struct inode *inode; + __u32 secdesclen = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + int addition_info = le32_to_cpu(req->AdditionalInformation); + int rc = 0, ppntsd_size = 0; + + if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | + PROTECTED_DACL_SECINFO | + UNPROTECTED_DACL_SECINFO)) { + ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", + addition_info); + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); + pntsd->osidoffset = 0; + pntsd->gsidoffset = 0; + pntsd->sacloffset = 0; + pntsd->dacloffset = 0; + + secdesclen = sizeof(struct smb_ntsd); + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); + + return 0; + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + user_ns = file_mnt_user_ns(fp->filp); + inode = file_inode(fp->filp); + ksmbd_acls_fattr(&fattr, user_ns, inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) + ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns, + fp->filp->f_path.dentry, + &ppntsd); + + /* Check if sd buffer size exceeds response buffer size */ + if (smb2_resp_buf_len(work, 8) > ppntsd_size) + rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size, + addition_info, &secdesclen, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + kfree(ppntsd); + ksmbd_fd_put(work, fp); + if (rc) + return rc; + + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); + return 0; +} + +/** + * smb2_query_info() - handler for smb2 query info command + * @work: smb work containing query info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_query_info(struct ksmbd_work *work) +{ + struct smb2_query_info_req *req; + struct smb2_query_info_rsp *rsp; + int rc = 0; + + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "GOT query info request\n"); + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_get_info_file(work, req, rsp); + break; + case SMB2_O_INFO_FILESYSTEM: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); + rc = smb2_get_info_filesystem(work, req, rsp); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + rc = smb2_get_info_sec(work, req, rsp); + break; + default: + ksmbd_debug(SMB, "InfoType %d not supported yet\n", + req->InfoType); + rc = -EOPNOTSUPP; + } + + if (rc < 0) { + if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", + rc); + return rc; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + inc_rfc1001_len(work->response_buf, 8); + return 0; +} + +/** + * smb2_close_pipe() - handler for closing IPC pipe + * @work: smb work containing close request buffer + * + * Return: 0 + */ +static noinline int smb2_close_pipe(struct ksmbd_work *work) +{ + u64 id; + struct smb2_close_req *req = smb2_get_msg(work->request_buf); + struct smb2_close_rsp *rsp = smb2_get_msg(work->response_buf); + + id = req->VolatileFileId; + ksmbd_session_rpc_close(work->sess, id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Flags = 0; + rsp->Reserved = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + inc_rfc1001_len(work->response_buf, 60); + return 0; +} + +/** + * smb2_close() - handler for smb2 close file command + * @work: smb work containing close request buffer + * + * Return: 0 + */ +int smb2_close(struct ksmbd_work *work) +{ + u64 volatile_id = KSMBD_NO_FID; + u64 sess_id; + struct smb2_close_req *req; + struct smb2_close_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_file *fp; + struct inode *inode; + u64 time; + int err = 0; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe close request\n"); + return smb2_close_pipe(work); + } + + sess_id = le64_to_cpu(req->hdr.SessionId); + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + sess_id = work->compound_sid; + + work->compound_sid = 0; + if (check_session_id(conn, sess_id)) { + work->compound_sid = sess_id; + } else { + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EBADF; + goto out; + } + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + if (!has_file_id(work->compound_fid)) { + /* file already closed, return FILE_CLOSED */ + ksmbd_debug(SMB, "file already closed\n"); + rsp->hdr.Status = STATUS_FILE_CLOSED; + err = -EBADF; + goto out; + } else { + ksmbd_debug(SMB, + "Compound request set FID = %llu:%llu\n", + work->compound_fid, + work->compound_pfid); + volatile_id = work->compound_fid; + + /* file closed, stored id is not valid anymore */ + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + } else { + volatile_id = req->VolatileFileId; + } + ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Reserved = 0; + + if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { + fp = ksmbd_lookup_fd_fast(work, volatile_id); + if (!fp) { + err = -ENOENT; + goto out; + } + + inode = file_inode(fp->filp); + rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : + cpu_to_le64(inode->i_blocks << 9); + rsp->EndOfFile = cpu_to_le64(inode->i_size); + rsp->Attributes = fp->f_ci->m_fattr; + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + rsp->ChangeTime = cpu_to_le64(time); + ksmbd_fd_put(work, fp); + } else { + rsp->Flags = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + } + + err = ksmbd_close_fd(work, volatile_id); +out: + if (err) { + if (rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + } else { + inc_rfc1001_len(work->response_buf, 60); + } + + return 0; +} + +/** + * smb2_echo() - handler for smb2 echo(ping) command + * @work: smb work containing echo request buffer + * + * Return: 0 + */ +int smb2_echo(struct ksmbd_work *work) +{ + struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + return 0; +} + +static int smb2_rename(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct user_namespace *user_ns, + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) +{ + struct ksmbd_share_config *share = fp->tcon->share_conf; + char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; + char *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + abs_oldname = file_path(fp->filp, pathname, PATH_MAX); + if (IS_ERR(abs_oldname)) { + rc = -EINVAL; + goto out; + } + old_name = strrchr(abs_oldname, '/'); + if (old_name && old_name[1] != '\0') { + old_name++; + } else { + ksmbd_debug(SMB, "can't get last component in path %s\n", + abs_oldname); + rc = -ENOENT; + goto out; + } + + new_name = smb2_get_name(file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(new_name)) { + rc = PTR_ERR(new_name); + goto out; + } + + if (strchr(new_name, ':')) { + int s_type; + char *xattr_stream_name, *stream_name = NULL; + size_t xattr_stream_size; + int len; + + rc = parse_stream_name(new_name, &stream_name, &s_type); + if (rc < 0) + goto out; + + len = strlen(new_name); + if (len > 0 && new_name[len - 1] != '/') { + pr_err("not allow base filename in rename\n"); + rc = -ESHARE; + goto out; + } + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + goto out; + + rc = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + xattr_stream_name, + NULL, 0, 0); + if (rc < 0) { + pr_err("failed to store stream name in xattr: %d\n", + rc); + rc = -EINVAL; + goto out; + } + + goto out; + } + + ksmbd_debug(SMB, "new name %s\n", new_name); + rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (ksmbd_share_veto_filename(share, new_name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); + goto out; + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, new_name); + if (rc) { + if (rc != -ENOTEMPTY) + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s, rc %d\n", + new_name, rc); + goto out; + } + } + } else { + if (file_present && + strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { + rc = -EEXIST; + ksmbd_debug(SMB, + "cannot rename already existing file\n"); + goto out; + } + } + + rc = ksmbd_vfs_fp_rename(work, fp, new_name); +out: + kfree(pathname); + if (!IS_ERR(new_name)) + kfree(new_name); + return rc; +} + +static int smb2_create_link(struct ksmbd_work *work, + struct ksmbd_share_config *share, + struct smb2_file_link_info *file_info, + unsigned int buf_len, struct file *filp, + struct nls_table *local_nls) +{ + char *link_name = NULL, *target_name = NULL, *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + if (buf_len < (u64)sizeof(struct smb2_file_link_info) + + le32_to_cpu(file_info->FileNameLength)) + return -EINVAL; + + ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + link_name = smb2_get_name(file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "link name is %s\n", link_name); + target_name = file_path(filp, pathname, PATH_MAX); + if (IS_ERR(target_name)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "target name is %s\n", target_name); + rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, link_name); + if (rc) { + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s\n", + link_name); + goto out; + } + } + } else { + if (file_present) { + rc = -EEXIST; + ksmbd_debug(SMB, "link already exists\n"); + goto out; + } + } + + rc = ksmbd_vfs_link(work, target_name, link_name); + if (rc) + rc = -EINVAL; +out: + if (!IS_ERR(link_name)) + kfree(link_name); + kfree(pathname); + return rc; +} + +static int set_file_basic_info(struct ksmbd_file *fp, + struct smb2_file_basic_info *file_info, + struct ksmbd_share_config *share) +{ + struct iattr attrs; + struct file *filp; + struct inode *inode; + struct user_namespace *user_ns; + int rc = 0; + + if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) + return -EACCES; + + attrs.ia_valid = 0; + filp = fp->filp; + inode = file_inode(filp); + user_ns = file_mnt_user_ns(filp); + + if (file_info->CreationTime) + fp->create_time = le64_to_cpu(file_info->CreationTime); + + if (file_info->LastAccessTime) { + attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + attrs.ia_valid |= ATTR_CTIME; + if (file_info->ChangeTime) + attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); + else + attrs.ia_ctime = inode->i_ctime; + + if (file_info->LastWriteTime) { + attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + + if (file_info->Attributes) { + if (!S_ISDIR(inode->i_mode) && + file_info->Attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { + pr_err("can't change a file to a directory\n"); + return -EINVAL; + } + + if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == FILE_ATTRIBUTE_NORMAL_LE)) + fp->f_ci->m_fattr = file_info->Attributes | + (fp->f_ci->m_fattr & FILE_ATTRIBUTE_DIRECTORY_LE); + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && + (file_info->CreationTime || file_info->Attributes)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.itime = fp->itime; + da.create_time = fp->create_time; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + filp->f_path.dentry, &da); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); + rc = 0; + } + + if (attrs.ia_valid) { + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = d_inode(dentry); + + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EACCES; + + inode_lock(inode); + inode->i_ctime = attrs.ia_ctime; + attrs.ia_valid &= ~ATTR_CTIME; + rc = notify_change(user_ns, dentry, &attrs, NULL); + inode_unlock(inode); + } + return rc; +} + +static int set_file_allocation_info(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct smb2_file_alloc_info *file_alloc_info) +{ + /* + * TODO : It's working fine only when store dos attributes + * is not yes. need to implement a logic which works + * properly with any smb.conf option + */ + + loff_t alloc_blks; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + inode = file_inode(fp->filp); + + if (alloc_blks > inode->i_blocks) { + smb_break_all_levII_oplock(work, fp, 1); + rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_blks * 512); + if (rc && rc != -EOPNOTSUPP) { + pr_err("vfs_fallocate is failed : %d\n", rc); + return rc; + } + } else if (alloc_blks < inode->i_blocks) { + loff_t size; + + /* + * Allocation size could be smaller than original one + * which means allocated blocks in file should be + * deallocated. use truncate to cut out it, but inode + * size is also updated with truncate offset. + * inode size is retained by backup inode size. + */ + size = i_size_read(inode); + rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); + if (rc) { + pr_err("truncate failed!, err %d\n", rc); + return rc; + } + if (size < alloc_blks * 512) + i_size_write(inode, size); + } + return 0; +} + +static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_eof_info *file_eof_info) +{ + loff_t newsize; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + newsize = le64_to_cpu(file_eof_info->EndOfFile); + inode = file_inode(fp->filp); + + /* + * If FILE_END_OF_FILE_INFORMATION of set_info_file is called + * on FAT32 shared device, truncate execution time is too long + * and network error could cause from windows client. because + * truncate of some filesystem like FAT32 fill zero data in + * truncated range. + */ + if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { + ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); + rc = ksmbd_vfs_truncate(work, fp, newsize); + if (rc) { + ksmbd_debug(SMB, "truncate failed!, err %d\n", rc); + if (rc != -EAGAIN) + rc = -EBADF; + return rc; + } + } + return 0; +} + +static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_rename_info *rename_info, + unsigned int buf_len) +{ + struct user_namespace *user_ns; + struct ksmbd_file *parent_fp; + struct dentry *parent; + struct dentry *dentry = fp->filp->f_path.dentry; + int ret; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + + le32_to_cpu(rename_info->FileNameLength)) + return -EINVAL; + + user_ns = file_mnt_user_ns(fp->filp); + if (ksmbd_stream_fd(fp)) + goto next; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); + inode_unlock(d_inode(parent)); + dput(parent); + + if (parent_fp) { + if (parent_fp->daccess & FILE_DELETE_LE) { + pr_err("parent dir is opened with delete access\n"); + ksmbd_fd_put(work, parent_fp); + return -ESHARE; + } + ksmbd_fd_put(work, parent_fp); + } +next: + return smb2_rename(work, fp, user_ns, rename_info, + work->conn->local_nls); +} + +static int set_file_disposition_info(struct ksmbd_file *fp, + struct smb2_file_disposition_info *file_info) +{ + struct inode *inode; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + inode = file_inode(fp->filp); + if (file_info->DeletePending) { + if (S_ISDIR(inode->i_mode) && + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) + return -EBUSY; + ksmbd_set_inode_pending_delete(fp); + } else { + ksmbd_clear_inode_pending_delete(fp); + } + return 0; +} + +static int set_file_position_info(struct ksmbd_file *fp, + struct smb2_file_pos_info *file_info) +{ + loff_t current_byte_offset; + unsigned long sector_size; + struct inode *inode; + + inode = file_inode(fp->filp); + current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); + sector_size = inode->i_sb->s_blocksize; + + if (current_byte_offset < 0 || + (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && + current_byte_offset & (sector_size - 1))) { + pr_err("CurrentByteOffset is not valid : %llu\n", + current_byte_offset); + return -EINVAL; + } + + fp->filp->f_pos = current_byte_offset; + return 0; +} + +static int set_file_mode_info(struct ksmbd_file *fp, + struct smb2_file_mode_info *file_info) +{ + __le32 mode; + + mode = file_info->Mode; + + if ((mode & ~FILE_MODE_INFO_MASK)) { + pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); + return -EINVAL; + } + + /* + * TODO : need to implement consideration for + * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT + */ + ksmbd_vfs_set_fadvise(fp->filp, mode); + fp->coption = mode; + return 0; +} + +/** + * smb2_set_info_file() - handler for smb2 set info command + * @work: smb work containing set info command buffer + * @fp: ksmbd_file pointer + * @req: request buffer pointer + * @share: ksmbd_share_config pointer + * + * Return: 0 on success, otherwise error + * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH + */ +static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_set_info_req *req, + struct ksmbd_share_config *share) +{ + unsigned int buf_len = le32_to_cpu(req->BufferLength); + + switch (req->FileInfoClass) { + case FILE_BASIC_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_basic_info)) + return -EINVAL; + + return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); + } + case FILE_ALLOCATION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_alloc_info)) + return -EINVAL; + + return set_file_allocation_info(work, fp, + (struct smb2_file_alloc_info *)req->Buffer); + } + case FILE_END_OF_FILE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_eof_info)) + return -EINVAL; + + return set_end_of_file_info(work, fp, + (struct smb2_file_eof_info *)req->Buffer); + } + case FILE_RENAME_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_rename_info)) + return -EINVAL; + + return set_rename_info(work, fp, + (struct smb2_file_rename_info *)req->Buffer, + buf_len); + } + case FILE_LINK_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_link_info)) + return -EINVAL; + + return smb2_create_link(work, work->tcon->share_conf, + (struct smb2_file_link_info *)req->Buffer, + buf_len, fp->filp, + work->conn->local_nls); + } + case FILE_DISPOSITION_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_disposition_info)) + return -EINVAL; + + return set_file_disposition_info(fp, + (struct smb2_file_disposition_info *)req->Buffer); + } + case FILE_FULL_EA_INFORMATION: + { + if (!(fp->daccess & FILE_WRITE_EA_LE)) { + pr_err("Not permitted to write ext attr: 0x%x\n", + fp->daccess); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_ea_info)) + return -EINVAL; + + return smb2_set_ea((struct smb2_ea_info *)req->Buffer, + buf_len, &fp->filp->f_path); + } + case FILE_POSITION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_pos_info)) + return -EINVAL; + + return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); + } + case FILE_MODE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_mode_info)) + return -EINVAL; + + return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); + } + } + + pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); + return -EOPNOTSUPP; +} + +static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, + char *buffer, int buf_len) +{ + struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; + + fp->saccess |= FILE_SHARE_DELETE_LE; + + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, + buf_len, false); +} + +/** + * smb2_set_info() - handler for smb2 set info command handler + * @work: smb work containing set info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_set_info(struct ksmbd_work *work) +{ + struct smb2_set_info_req *req; + struct smb2_set_info_rsp *rsp; + struct ksmbd_file *fp; + int rc = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + ksmbd_debug(SMB, "Received set info request\n"); + + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } else { + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) { + ksmbd_debug(SMB, "Invalid id for close: %u\n", id); + rc = -ENOENT; + goto err_out; + } + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out; + } + rc = smb2_set_info_sec(fp, + le32_to_cpu(req->AdditionalInformation), + req->Buffer, + le32_to_cpu(req->BufferLength)); + ksmbd_revert_fsids(work); + break; + default: + rc = -EOPNOTSUPP; + } + + if (rc < 0) + goto err_out; + + rsp->StructureSize = cpu_to_le16(2); + inc_rfc1001_len(work->response_buf, 2); + ksmbd_fd_put(work, fp); + return 0; + +err_out: + if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EBUSY || rc == -ENOTEMPTY) + rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; + else if (rc == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (rc == -EBADF || rc == -ESTALE) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); + return rc; +} + +/** + * smb2_read_pipe() - handler for smb2 read from IPC pipe + * @work: smb work containing read IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_read_pipe(struct ksmbd_work *work) +{ + int nbytes = 0, err; + u64 id; + struct ksmbd_rpc_command *rpc_resp; + struct smb2_read_req *req = smb2_get_msg(work->request_buf); + struct smb2_read_rsp *rsp = smb2_get_msg(work->response_buf); + + id = req->VolatileFileId; + + inc_rfc1001_len(work->response_buf, 16); + rpc_resp = ksmbd_rpc_read(work->sess, id); + if (rpc_resp) { + if (rpc_resp->flags != KSMBD_RPC_OK) { + err = -EINVAL; + goto out; + } + + work->aux_payload_buf = + kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + memcpy(work->aux_payload_buf, rpc_resp->payload, + rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; + work->aux_payload_sz = nbytes; + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Flags = 0; + inc_rfc1001_len(work->response_buf, nbytes); + return 0; + +out: + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return err; +} + +static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, + struct smb2_buffer_desc_v1 *desc, + __le32 Channel, + __le16 ChannelInfoLength) +{ + unsigned int i, ch_count; + + if (work->conn->dialect == SMB30_PROT_ID && + Channel != SMB2_CHANNEL_RDMA_V1) + return -EINVAL; + + ch_count = le16_to_cpu(ChannelInfoLength) / sizeof(*desc); + if (ksmbd_debug_types & KSMBD_DEBUG_RDMA) { + for (i = 0; i < ch_count; i++) { + pr_info("RDMA r/w request %#x: token %#x, length %#x\n", + i, + le32_to_cpu(desc[i].token), + le32_to_cpu(desc[i].length)); + } + } + if (!ch_count) + return -EINVAL; + + work->need_invalidate_rkey = + (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); + if (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) + work->remote_key = le32_to_cpu(desc->token); + return 0; +} + +static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, + struct smb2_read_req *req, void *data_buf, + size_t length) +{ + int err; + + err = ksmbd_conn_rdma_write(work->conn, data_buf, length, + (struct smb2_buffer_desc_v1 *) + ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), + le16_to_cpu(req->ReadChannelInfoLength)); + if (err) + return err; + + return length; +} + +/** + * smb2_read() - handler for smb2 read from file + * @work: smb work containing read command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_read(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_read_req *req; + struct smb2_read_rsp *rsp; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length, mincount; + ssize_t nbytes = 0, remain_bytes = 0; + int err = 0; + bool is_rdma_channel = false; + unsigned int max_read_size = conn->vals->max_read_size; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe read request\n"); + return smb2_read_pipe(work); + } + + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || + req->Channel == SMB2_CHANNEL_RDMA_V1) { + is_rdma_channel = true; + max_read_size = get_smbd_max_read_write_size(); + } + + if (is_rdma_channel == true) { + unsigned int ch_offset = le16_to_cpu(req->ReadChannelInfoOffset); + + if (ch_offset < offsetof(struct smb2_read_req, Buffer)) { + err = -EINVAL; + goto out; + } + err = smb2_set_remote_key_for_rdma(work, + (struct smb2_buffer_desc_v1 *) + ((char *)req + ch_offset), + req->Channel, + req->ReadChannelInfoLength); + if (err) + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to read : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + mincount = le32_to_cpu(req->MinimumCount); + + if (length > max_read_size) { + ksmbd_debug(SMB, "limiting read size to max size(%u)\n", + max_read_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + + work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + nbytes = ksmbd_vfs_read(work, fp, length, &offset); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + if ((nbytes == 0 && length != 0) || nbytes < mincount) { + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + rsp->hdr.Status = STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return 0; + } + + ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", + nbytes, offset, mincount); + + if (is_rdma_channel == true) { + /* write data to the client using rdma channel */ + remain_bytes = smb2_read_rdma_channel(work, req, + work->aux_payload_buf, + nbytes); + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + + nbytes = 0; + if (remain_bytes < 0) { + err = (int)remain_bytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = cpu_to_le32(remain_bytes); + rsp->Flags = 0; + inc_rfc1001_len(work->response_buf, 16); + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; + work->aux_payload_sz = nbytes; + inc_rfc1001_len(work->response_buf, nbytes); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err) { + if (err == -EISDIR) + rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; + else if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + } + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_write_pipe() - handler for smb2 write on IPC pipe + * @work: smb work containing write IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_write_pipe(struct ksmbd_work *work) +{ + struct smb2_write_req *req = smb2_get_msg(work->request_buf); + struct smb2_write_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_rpc_command *rpc_resp; + u64 id = 0; + int err = 0, ret = 0; + char *data_buf; + size_t length; + + length = le32_to_cpu(req->Length); + id = req->VolatileFileId; + + if ((u64)le16_to_cpu(req->DataOffset) + length > + get_rfc1002_len(work->request_buf)) { + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(work->request_buf)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + + rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + smb2_set_err_rsp(work); + return -EOPNOTSUPP; + } + if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return ret; + } + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(length); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, 16); + return 0; +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, + struct smb2_write_req *req, + struct ksmbd_file *fp, + loff_t offset, size_t length, bool sync) +{ + char *data_buf; + int ret; + ssize_t nbytes; + + data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!data_buf) + return -ENOMEM; + + ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, + (struct smb2_buffer_desc_v1 *) + ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), + le16_to_cpu(req->WriteChannelInfoLength)); + if (ret < 0) { + kvfree(data_buf); + return ret; + } + + ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); + kvfree(data_buf); + if (ret < 0) + return ret; + + return nbytes; +} + +/** + * smb2_write() - handler for smb2 write from file + * @work: smb work containing write command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_write(struct ksmbd_work *work) +{ + struct smb2_write_req *req; + struct smb2_write_rsp *rsp; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length; + ssize_t nbytes; + char *data_buf; + bool writethrough = false, is_rdma_channel = false; + int err = 0; + unsigned int max_write_size = work->conn->vals->max_write_size; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe write request\n"); + return smb2_write_pipe(work); + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + + if (req->Channel == SMB2_CHANNEL_RDMA_V1 || + req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { + is_rdma_channel = true; + max_write_size = get_smbd_max_read_write_size(); + length = le32_to_cpu(req->RemainingBytes); + } + + if (is_rdma_channel == true) { + unsigned int ch_offset = le16_to_cpu(req->WriteChannelInfoOffset); + + if (req->Length != 0 || req->DataOffset != 0 || + ch_offset < offsetof(struct smb2_write_req, Buffer)) { + err = -EINVAL; + goto out; + } + err = smb2_set_remote_key_for_rdma(work, + (struct smb2_buffer_desc_v1 *) + ((char *)req + ch_offset), + req->Channel, + req->WriteChannelInfoLength); + if (err) + goto out; + } + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + err = -EACCES; + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to write : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + if (length > max_write_size) { + ksmbd_debug(SMB, "limiting write size to max size(%u)\n", + max_write_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); + if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) + writethrough = true; + + if (is_rdma_channel == false) { + if (le16_to_cpu(req->DataOffset) < + offsetof(struct smb2_write_req, Buffer)) { + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, + writethrough, &nbytes); + if (err < 0) + goto out; + } else { + /* read data from the client using rdma channel, and + * write the data. + */ + nbytes = smb2_write_rdma_channel(work, req, fp, offset, length, + writethrough); + if (nbytes < 0) { + err = (int)nbytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, 16); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_flush() - handler for smb2 flush file - fsync + * @work: smb work containing flush command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_flush(struct ksmbd_work *work) +{ + struct smb2_flush_req *req; + struct smb2_flush_rsp *rsp; + int err; + + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); + + err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); + if (err) + goto out; + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + return 0; + +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +/** + * smb2_cancel() - handler for smb2 cancel command + * @work: smb work containing cancel command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_cancel(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + struct smb2_hdr *chdr; + struct ksmbd_work *iter; + struct list_head *command_list; + + ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", + hdr->MessageId, hdr->Flags); + + if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { + command_list = &conn->async_requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(iter, command_list, + async_request_entry) { + chdr = smb2_get_msg(iter->request_buf); + + if (iter->async_id != + le64_to_cpu(hdr->Id.AsyncId)) + continue; + + ksmbd_debug(SMB, + "smb2 with AsyncId %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->Id.AsyncId), + le16_to_cpu(chdr->Command)); + iter->state = KSMBD_WORK_CANCELLED; + if (iter->cancel_fn) + iter->cancel_fn(iter->cancel_argv); + break; + } + spin_unlock(&conn->request_lock); + } else { + command_list = &conn->requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(iter, command_list, request_entry) { + chdr = smb2_get_msg(iter->request_buf); + + if (chdr->MessageId != hdr->MessageId || + iter == work) + continue; + + ksmbd_debug(SMB, + "smb2 with mid %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->MessageId), + le16_to_cpu(chdr->Command)); + iter->state = KSMBD_WORK_CANCELLED; + break; + } + spin_unlock(&conn->request_lock); + } + + /* For SMB2_CANCEL command itself send no response*/ + work->send_no_response = 1; + return 0; +} + +struct file_lock *smb_flock_init(struct file *f) +{ + struct file_lock *fl; + + fl = locks_alloc_lock(); + if (!fl) + goto out; + + locks_init_lock(fl); + + fl->fl_owner = f; + fl->fl_pid = current->tgid; + fl->fl_file = f; + fl->fl_flags = FL_POSIX; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; + +out: + return fl; +} + +static int smb2_set_flock_flags(struct file_lock *flock, int flags) +{ + int cmd = -EINVAL; + + /* Checking for wrong flag combination during lock request*/ + switch (flags) { + case SMB2_LOCKFLAG_SHARED: + ksmbd_debug(SMB, "received shared request\n"); + cmd = F_SETLKW; + flock->fl_type = F_RDLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_EXCLUSIVE: + ksmbd_debug(SMB, "received exclusive request\n"); + cmd = F_SETLKW; + flock->fl_type = F_WRLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received shared & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_RDLCK; + break; + case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received exclusive & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_WRLCK; + break; + case SMB2_LOCKFLAG_UNLOCK: + ksmbd_debug(SMB, "received unlock request\n"); + flock->fl_type = F_UNLCK; + cmd = 0; + break; + } + + return cmd; +} + +static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, + unsigned int cmd, int flags, + struct list_head *lock_list) +{ + struct ksmbd_lock *lock; + + lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + if (!lock) + return NULL; + + lock->cmd = cmd; + lock->fl = flock; + lock->start = flock->fl_start; + lock->end = flock->fl_end; + lock->flags = flags; + if (lock->start == lock->end) + lock->zero_len = 1; + INIT_LIST_HEAD(&lock->clist); + INIT_LIST_HEAD(&lock->flist); + INIT_LIST_HEAD(&lock->llist); + list_add_tail(&lock->llist, lock_list); + + return lock; +} + +static void smb2_remove_blocked_lock(void **argv) +{ + struct file_lock *flock = (struct file_lock *)argv[0]; + + ksmbd_vfs_posix_lock_unblock(flock); + wake_up(&flock->fl_wait); +} + +static inline bool lock_defer_pending(struct file_lock *fl) +{ + /* check pending lock waiters */ + return waitqueue_active(&fl->fl_wait); +} + +/** + * smb2_lock() - handler for smb2 file lock command + * @work: smb work containing lock command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_lock(struct ksmbd_work *work) +{ + struct smb2_lock_req *req = smb2_get_msg(work->request_buf); + struct smb2_lock_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_lock_element *lock_ele; + struct ksmbd_file *fp = NULL; + struct file_lock *flock = NULL; + struct file *filp = NULL; + int lock_count; + int flags = 0; + int cmd = 0; + int err = -EIO, i, rc = 0; + u64 lock_start, lock_length; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; + struct ksmbd_conn *conn; + int nolock = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); + int prior_lock = 0; + + ksmbd_debug(SMB, "Received lock request\n"); + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); + err = -ENOENT; + goto out2; + } + + filp = fp->filp; + lock_count = le16_to_cpu(req->LockCount); + lock_ele = req->locks; + + ksmbd_debug(SMB, "lock count is %d\n", lock_count); + if (!lock_count) { + err = -EINVAL; + goto out2; + } + + for (i = 0; i < lock_count; i++) { + flags = le32_to_cpu(lock_ele[i].Flags); + + flock = smb_flock_init(filp); + if (!flock) + goto out; + + cmd = smb2_set_flock_flags(flock, flags); + + lock_start = le64_to_cpu(lock_ele[i].Offset); + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_start > U64_MAX - lock_length) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + if (lock_start > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = lock_start; + + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_length > OFFSET_MAX - flock->fl_start) + lock_length = OFFSET_MAX - flock->fl_start; + + flock->fl_end = flock->fl_start + lock_length; + + if (flock->fl_end < flock->fl_start) { + ksmbd_debug(SMB, + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + /* Check conflict locks in one request */ + list_for_each_entry(cmp_lock, &lock_list, llist) { + if (cmp_lock->fl->fl_start <= flock->fl_start && + cmp_lock->fl->fl_end >= flock->fl_end) { + if (cmp_lock->fl->fl_type != F_UNLCK && + flock->fl_type != F_UNLCK) { + pr_err("conflict two locks in one request\n"); + err = -EINVAL; + goto out; + } + } + } + + smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); + if (!smb_lock) { + err = -EINVAL; + goto out; + } + } + + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + if (smb_lock->cmd < 0) { + err = -EINVAL; + goto out; + } + + if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { + err = -EINVAL; + goto out; + } + + if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && + smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || + (prior_lock == SMB2_LOCKFLAG_UNLOCK && + !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { + err = -EINVAL; + goto out; + } + + prior_lock = smb_lock->flags; + + if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && + !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) + goto no_check_cl; + + nolock = 1; + /* check locks in connection list */ + down_read(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; + + if (smb_lock->fl->fl_type == F_UNLCK) { + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end && + !lock_defer_pending(cmp_lock->fl)) { + nolock = 0; + list_del(&cmp_lock->flist); + list_del(&cmp_lock->clist); + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + + locks_free_lock(cmp_lock->fl); + kfree(cmp_lock); + goto out_check_cl; + } + continue; + } + + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { + if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } else { + if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } + + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + pr_err("previous lock conflict with zero byte lock range\n"); + goto out; + } + + if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + pr_err("current lock conflict with zero byte lock range\n"); + goto out; + } + + if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + pr_err("Not allow lock operation on exclusive lock range\n"); + goto out; + } + } + spin_unlock(&conn->llist_lock); + } + up_read(&conn_list_lock); +out_check_cl: + if (smb_lock->fl->fl_type == F_UNLCK && nolock) { + pr_err("Try to unlock nolocked range\n"); + rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; + goto out; + } + +no_check_cl: + if (smb_lock->zero_len) { + err = 0; + goto skip; + } + + flock = smb_lock->fl; + list_del(&smb_lock->llist); +retry: + rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); +skip: + if (flags & SMB2_LOCKFLAG_UNLOCK) { + if (!rc) { + ksmbd_debug(SMB, "File unlocked\n"); + } else if (rc == -ENOENT) { + rsp->hdr.Status = STATUS_NOT_LOCKED; + goto out; + } + locks_free_lock(flock); + kfree(smb_lock); + } else { + if (rc == FILE_LOCK_DEFERRED) { + void **argv; + + ksmbd_debug(SMB, + "would have to wait for getting lock\n"); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + + argv = kmalloc(sizeof(void *), GFP_KERNEL); + if (!argv) { + err = -ENOMEM; + goto out; + } + argv[0] = flock; + + rc = setup_async_work(work, + smb2_remove_blocked_lock, + argv); + if (rc) { + err = -ENOMEM; + goto out; + } + spin_lock(&fp->f_lock); + list_add(&work->fp_entry, &fp->blocked_works); + spin_unlock(&fp->f_lock); + + smb2_send_interim_resp(work, STATUS_PENDING); + + ksmbd_vfs_posix_lock_wait(flock); + + spin_lock(&work->conn->request_lock); + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + work->cancel_fn = NULL; + kfree(argv); + spin_unlock(&fp->f_lock); + spin_unlock(&work->conn->request_lock); + + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + locks_free_lock(flock); + + if (work->state == KSMBD_WORK_CANCELLED) { + rsp->hdr.Status = + STATUS_CANCELLED; + kfree(smb_lock); + smb2_send_interim_resp(work, + STATUS_CANCELLED); + work->send_no_response = 1; + goto out; + } + init_smb2_rsp_hdr(work); + smb2_set_err_rsp(work); + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); + goto out2; + } + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + goto retry; + } else if (!rc) { + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + ksmbd_debug(SMB, "successful in taking lock\n"); + } else { + goto out; + } + } + } + + if (atomic_read(&fp->f_ci->op_count) > 1) + smb_break_all_oplock(work, fp); + + rsp->StructureSize = cpu_to_le16(4); + ksmbd_debug(SMB, "successful in taking lock\n"); + rsp->hdr.Status = STATUS_SUCCESS; + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + ksmbd_fd_put(work, fp); + return 0; + +out: + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + locks_free_lock(smb_lock->fl); + list_del(&smb_lock->llist); + kfree(smb_lock); + } + + list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { + struct file_lock *rlock = NULL; + + rlock = smb_flock_init(filp); + rlock->fl_type = F_UNLCK; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + rc = vfs_lock_file(filp, 0, rlock, NULL); + if (rc) + pr_err("rollback unlock fail : %d\n", rc); + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + if (!list_empty(&smb_lock->flist)) + list_del(&smb_lock->flist); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + locks_free_lock(smb_lock->fl); + locks_free_lock(rlock); + kfree(smb_lock); + } +out2: + ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); + + if (!rsp->hdr.Status) { + if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (err == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + } + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +static int fsctl_copychunk(struct ksmbd_work *work, + struct copychunk_ioctl_req *ci_req, + unsigned int cnt_code, + unsigned int input_count, + unsigned long long volatile_id, + unsigned long long persistent_id, + struct smb2_ioctl_rsp *rsp) +{ + struct copychunk_ioctl_rsp *ci_rsp; + struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; + struct srv_copychunk *chunks; + unsigned int i, chunk_count, chunk_count_written = 0; + unsigned int chunk_size_written = 0; + loff_t total_size_written = 0; + int ret = 0; + + ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; + + rsp->VolatileFileId = volatile_id; + rsp->PersistentFileId = persistent_id; + ci_rsp->ChunksWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); + ci_rsp->ChunkBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); + ci_rsp->TotalBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_total_size()); + + chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; + chunk_count = le32_to_cpu(ci_req->ChunkCount); + if (chunk_count == 0) + goto out; + total_size_written = 0; + + /* verify the SRV_COPYCHUNK_COPY packet */ + if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || + input_count < offsetof(struct copychunk_ioctl_req, Chunks) + + chunk_count * sizeof(struct srv_copychunk)) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + for (i = 0; i < chunk_count; i++) { + if (le32_to_cpu(chunks[i].Length) == 0 || + le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) + break; + total_size_written += le32_to_cpu(chunks[i].Length); + } + + if (i < chunk_count || + total_size_written > ksmbd_server_side_copy_max_total_size()) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + src_fp = ksmbd_lookup_foreign_fd(work, + le64_to_cpu(ci_req->ResumeKey[0])); + dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + ret = -EINVAL; + if (!src_fp || + src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto out; + } + + if (!dst_fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out; + } + + /* + * FILE_READ_DATA should only be included in + * the FSCTL_COPYCHUNK case + */ + if (cnt_code == FSCTL_COPYCHUNK && + !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + goto out; + } + + ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, + chunks, chunk_count, + &chunk_count_written, + &chunk_size_written, + &total_size_written); + if (ret < 0) { + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + if (ret == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (ret == -EBADF) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (ret == -EFBIG || ret == -ENOSPC) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (ret == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (ret == -EISDIR) + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + else if (ret == -E2BIG) + rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; + else + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + } + + ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); + ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); + ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); +out: + ksmbd_fd_put(work, src_fp); + ksmbd_fd_put(work, dst_fp); + return ret; +} + +static __be32 idev_ipv4_address(struct in_device *idev) +{ + __be32 addr = 0; + + struct in_ifaddr *ifa; + + rcu_read_lock(); + in_dev_for_each_ifa_rcu(ifa, idev) { + if (ifa->ifa_flags & IFA_F_SECONDARY) + continue; + + addr = ifa->ifa_address; + break; + } + rcu_read_unlock(); + return addr; +} + +static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, + struct smb2_ioctl_rsp *rsp, + unsigned int out_buf_len) +{ + struct network_interface_info_ioctl_rsp *nii_rsp = NULL; + int nbytes = 0; + struct net_device *netdev; + struct sockaddr_storage_rsp *sockaddr_storage; + unsigned int flags; + unsigned long long speed; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + bool ipv4_set = false; + + if (netdev->type == ARPHRD_LOOPBACK) + continue; + + flags = dev_get_flags(netdev); + if (!(flags & IFF_RUNNING)) + continue; +ipv6_retry: + if (out_buf_len < + nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { + rtnl_unlock(); + return -ENOSPC; + } + + nii_rsp = (struct network_interface_info_ioctl_rsp *) + &rsp->Buffer[nbytes]; + nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); + + nii_rsp->Capability = 0; + if (netdev->real_num_tx_queues > 1) + nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); + if (ksmbd_rdma_capable_netdev(netdev)) + nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); + + nii_rsp->Next = cpu_to_le32(152); + nii_rsp->Reserved = 0; + + if (netdev->ethtool_ops->get_link_ksettings) { + struct ethtool_link_ksettings cmd; + + netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); + speed = cmd.base.speed; + } else { + ksmbd_debug(SMB, "%s %s\n", netdev->name, + "speed is unknown, defaulting to 1Gb/sec"); + speed = SPEED_1000; + } + + speed *= 1000000; + nii_rsp->LinkSpeed = cpu_to_le64(speed); + + sockaddr_storage = (struct sockaddr_storage_rsp *) + nii_rsp->SockAddr_Storage; + memset(sockaddr_storage, 0, 128); + + if (!ipv4_set) { + struct in_device *idev; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); + sockaddr_storage->addr4.Port = 0; + + idev = __in_dev_get_rtnl(netdev); + if (!idev) + continue; + sockaddr_storage->addr4.IPv4address = + idev_ipv4_address(idev); + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + ipv4_set = true; + goto ipv6_retry; + } else { + struct inet6_dev *idev6; + struct inet6_ifaddr *ifa; + __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); + sockaddr_storage->addr6.Port = 0; + sockaddr_storage->addr6.FlowInfo = 0; + + idev6 = __in6_dev_get(netdev); + if (!idev6) + continue; + + list_for_each_entry(ifa, &idev6->addr_list, if_list) { + if (ifa->flags & (IFA_F_TENTATIVE | + IFA_F_DEPRECATED)) + continue; + memcpy(ipv6_addr, ifa->addr.s6_addr, 16); + break; + } + sockaddr_storage->addr6.ScopeId = 0; + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + } + } + rtnl_unlock(); + + /* zero if this is last one */ + if (nii_rsp) + nii_rsp->Next = 0; + + rsp->PersistentFileId = SMB2_NO_FID; + rsp->VolatileFileId = SMB2_NO_FID; + return nbytes; +} + +static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, + struct validate_negotiate_info_req *neg_req, + struct validate_negotiate_info_rsp *neg_rsp, + unsigned int in_buf_len) +{ + int ret = 0; + int dialect; + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + + le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) + return -EINVAL; + + dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, + neg_req->DialectCount); + if (dialect == BAD_PROT_ID || dialect != conn->dialect) { + ret = -EINVAL; + goto err_out; + } + + if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + ret = -EINVAL; + goto err_out; + } + + if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { + ret = -EINVAL; + goto err_out; + } + + if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { + ret = -EINVAL; + goto err_out; + } + + neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); + neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); + neg_rsp->Dialect = cpu_to_le16(conn->dialect); +err_out: + return ret; +} + +static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, + struct file_allocated_range_buffer *qar_req, + struct file_allocated_range_buffer *qar_rsp, + unsigned int in_count, unsigned int *out_count) +{ + struct ksmbd_file *fp; + loff_t start, length; + int ret = 0; + + *out_count = 0; + if (in_count == 0) + return -EINVAL; + + start = le64_to_cpu(qar_req->file_offset); + length = le64_to_cpu(qar_req->length); + + if (start < 0 || length < 0) + return -EINVAL; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + + ret = ksmbd_vfs_fqar_lseek(fp, start, length, + qar_rsp, in_count, out_count); + if (ret && ret != -E2BIG) + *out_count = 0; + + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, + unsigned int out_buf_len, + struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) +{ + struct ksmbd_rpc_command *rpc_resp; + char *data_buf = (char *)&req->Buffer[0]; + int nbytes = 0; + + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, + le32_to_cpu(req->InputCount)); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { + /* + * set STATUS_SOME_NOT_MAPPED response + * for unknown domain sid. + */ + rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; + } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + nbytes = rpc_resp->payload_sz; + if (rpc_resp->payload_sz > out_buf_len) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + nbytes = out_buf_len; + } + + if (!rpc_resp->payload_sz) { + rsp->hdr.Status = + STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); + } +out: + kvfree(rpc_resp); + return nbytes; +} + +static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + struct file_sparse *sparse) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + int ret = 0; + __le32 old_fattr; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + user_ns = file_mnt_user_ns(fp->filp); + + old_fattr = fp->f_ci->m_fattr; + if (sparse->SetSparse) + fp->f_ci->m_fattr |= FILE_ATTRIBUTE_SPARSE_FILE_LE; + else + fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_SPARSE_FILE_LE; + + if (fp->f_ci->m_fattr != old_fattr && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret <= 0) + goto out; + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } + +out: + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_request_resume_key(struct ksmbd_work *work, + struct smb2_ioctl_req *req, + struct resume_key_ioctl_rsp *key_rsp) +{ + struct ksmbd_file *fp; + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) + return -ENOENT; + + memset(key_rsp, 0, sizeof(*key_rsp)); + key_rsp->ResumeKey[0] = req->VolatileFileId; + key_rsp->ResumeKey[1] = req->PersistentFileId; + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * smb2_ioctl() - handler for smb2 ioctl command + * @work: smb work containing ioctl command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_ioctl(struct ksmbd_work *work) +{ + struct smb2_ioctl_req *req; + struct smb2_ioctl_rsp *rsp; + unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; + u64 id = KSMBD_NO_FID; + struct ksmbd_conn *conn = work->conn; + int ret = 0; + + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + } + } else { + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); + } + + if (!has_file_id(id)) + id = req->VolatileFileId; + + if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } + + cnt_code = le32_to_cpu(req->CtlCode); + ret = smb2_calc_max_out_buf_len(work, 48, + le32_to_cpu(req->MaxOutputResponse)); + if (ret < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + out_buf_len = (unsigned int)ret; + in_buf_len = le32_to_cpu(req->InputCount); + + switch (cnt_code) { + case FSCTL_DFS_GET_REFERRALS: + case FSCTL_DFS_GET_REFERRALS_EX: + /* Not support DFS yet */ + rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; + goto out; + case FSCTL_CREATE_OR_GET_OBJECT_ID: + { + struct file_object_buf_type1_ioctl_rsp *obj_buf; + + nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); + obj_buf = (struct file_object_buf_type1_ioctl_rsp *) + &rsp->Buffer[0]; + + /* + * TODO: This is dummy implementation to pass smbtorture + * Need to check correct response later + */ + memset(obj_buf->ObjectId, 0x0, 16); + memset(obj_buf->BirthVolumeId, 0x0, 16); + memset(obj_buf->BirthObjectId, 0x0, 16); + memset(obj_buf->DomainId, 0x0, 16); + + break; + } + case FSCTL_PIPE_TRANSCEIVE: + out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); + nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); + break; + case FSCTL_VALIDATE_NEGOTIATE_INFO: + if (conn->dialect < SMB30_PROT_ID) { + ret = -EOPNOTSUPP; + goto out; + } + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, + Dialects)) { + ret = -EINVAL; + goto out; + } + + if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_validate_negotiate_info(conn, + (struct validate_negotiate_info_req *)&req->Buffer[0], + (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], + in_buf_len); + if (ret < 0) + goto out; + + nbytes = sizeof(struct validate_negotiate_info_rsp); + rsp->PersistentFileId = SMB2_NO_FID; + rsp->VolatileFileId = SMB2_NO_FID; + break; + case FSCTL_QUERY_NETWORK_INTERFACE_INFO: + ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); + if (ret < 0) + goto out; + nbytes = ret; + break; + case FSCTL_REQUEST_RESUME_KEY: + if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_request_resume_key(work, req, + (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); + if (ret < 0) + goto out; + rsp->PersistentFileId = req->PersistentFileId; + rsp->VolatileFileId = req->VolatileFileId; + nbytes = sizeof(struct resume_key_ioctl_rsp); + break; + case FSCTL_COPYCHUNK: + case FSCTL_COPYCHUNK_WRITE: + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { + ret = -EINVAL; + goto out; + } + + if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + nbytes = sizeof(struct copychunk_ioctl_rsp); + rsp->VolatileFileId = req->VolatileFileId; + rsp->PersistentFileId = req->PersistentFileId; + fsctl_copychunk(work, + (struct copychunk_ioctl_req *)&req->Buffer[0], + le32_to_cpu(req->CtlCode), + le32_to_cpu(req->InputCount), + req->VolatileFileId, + req->PersistentFileId, + rsp); + break; + case FSCTL_SET_SPARSE: + if (in_buf_len < sizeof(struct file_sparse)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_set_sparse(work, id, + (struct file_sparse *)&req->Buffer[0]); + if (ret < 0) + goto out; + break; + case FSCTL_SET_ZERO_DATA: + { + struct file_zero_data_information *zero_data; + struct ksmbd_file *fp; + loff_t off, len, bfz; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct file_zero_data_information)) { + ret = -EINVAL; + goto out; + } + + zero_data = + (struct file_zero_data_information *)&req->Buffer[0]; + + off = le64_to_cpu(zero_data->FileOffset); + bfz = le64_to_cpu(zero_data->BeyondFinalZero); + if (off < 0 || bfz < 0 || off > bfz) { + ret = -EINVAL; + goto out; + } + + len = bfz - off; + if (len) { + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ret = -ENOENT; + goto out; + } + + ret = ksmbd_vfs_zero_data(work, fp, off, len); + ksmbd_fd_put(work, fp); + if (ret < 0) + goto out; + } + break; + } + case FSCTL_QUERY_ALLOCATED_RANGES: + if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_query_allocated_ranges(work, id, + (struct file_allocated_range_buffer *)&req->Buffer[0], + (struct file_allocated_range_buffer *)&rsp->Buffer[0], + out_buf_len / + sizeof(struct file_allocated_range_buffer), &nbytes); + if (ret == -E2BIG) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + } else if (ret < 0) { + nbytes = 0; + goto out; + } + + nbytes *= sizeof(struct file_allocated_range_buffer); + break; + case FSCTL_GET_REPARSE_POINT: + { + struct reparse_data_buffer *reparse_ptr; + struct ksmbd_file *fp; + + reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + pr_err("not found fp!!\n"); + ret = -ENOENT; + goto out; + } + + reparse_ptr->ReparseTag = + smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); + reparse_ptr->ReparseDataLength = 0; + ksmbd_fd_put(work, fp); + nbytes = sizeof(struct reparse_data_buffer); + break; + } + case FSCTL_DUPLICATE_EXTENTS_TO_FILE: + { + struct ksmbd_file *fp_in, *fp_out = NULL; + struct duplicate_extents_to_file *dup_ext; + loff_t src_off, dst_off, length, cloned; + + if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { + ret = -EINVAL; + goto out; + } + + dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; + + fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, + dup_ext->PersistentFileHandle); + if (!fp_in) { + pr_err("not found file handle in duplicate extent to file\n"); + ret = -ENOENT; + goto out; + } + + fp_out = ksmbd_lookup_fd_fast(work, id); + if (!fp_out) { + pr_err("not found fp\n"); + ret = -ENOENT; + goto dup_ext_out; + } + + src_off = le64_to_cpu(dup_ext->SourceFileOffset); + dst_off = le64_to_cpu(dup_ext->TargetFileOffset); + length = le64_to_cpu(dup_ext->ByteCount); + /* + * XXX: It is not clear if FSCTL_DUPLICATE_EXTENTS_TO_FILE + * should fall back to vfs_copy_file_range(). This could be + * beneficial when re-exporting nfs/smb mount, but note that + * this can result in partial copy that returns an error status. + * If/when FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX is implemented, + * fall back to vfs_copy_file_range(), should be avoided when + * the flag DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC is set. + */ + cloned = vfs_clone_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, length, 0); + if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { + ret = -EOPNOTSUPP; + goto dup_ext_out; + } else if (cloned != length) { + cloned = vfs_copy_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, + length, 0); + if (cloned != length) { + if (cloned < 0) + ret = cloned; + else + ret = -EINVAL; + } + } + +dup_ext_out: + ksmbd_fd_put(work, fp_in); + ksmbd_fd_put(work, fp_out); + if (ret < 0) + goto out; + break; + } + default: + ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", + cnt_code); + ret = -EOPNOTSUPP; + goto out; + } + + rsp->CtlCode = cpu_to_le32(cnt_code); + rsp->InputCount = cpu_to_le32(0); + rsp->InputOffset = cpu_to_le32(112); + rsp->OutputOffset = cpu_to_le32(112); + rsp->OutputCount = cpu_to_le32(nbytes); + rsp->StructureSize = cpu_to_le16(49); + rsp->Reserved = cpu_to_le16(0); + rsp->Flags = cpu_to_le32(0); + rsp->Reserved2 = cpu_to_le32(0); + inc_rfc1001_len(work->response_buf, 48 + nbytes); + + return 0; + +out: + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (ret == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (ret == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (ret == -ENOSPC) + rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; + else if (ret < 0 || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return 0; +} + +/** + * smb20_oplock_break_ack() - handler for smb2.0 oplock break command + * @work: smb work containing oplock break command buffer + * + * Return: 0 + */ +static void smb20_oplock_break_ack(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_file *fp; + struct oplock_info *opinfo = NULL; + __le32 err = 0; + int ret = 0; + u64 volatile_id, persistent_id; + char req_oplevel = 0, rsp_oplevel = 0; + unsigned int oplock_change_type; + + volatile_id = req->VolatileFid; + persistent_id = req->PersistentFid; + req_oplevel = req->OplockLevel; + ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", + volatile_id, persistent_id, req_oplevel); + + fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + if (!fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + return; + } + + opinfo = opinfo_get(fp); + if (!opinfo) { + pr_err("unexpected null oplock_info\n"); + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return; + } + + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; + } + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + (req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_READ_TO_NONE; + } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_DEVICE_STATE; + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_II) { + oplock_change_type = OPLOCK_WRITE_TO_READ; + } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_READ_TO_NONE; + } else { + oplock_change_type = 0; + } + } else { + oplock_change_type = 0; + } + + switch (oplock_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_II; + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + default: + pr_err("unknown oplock change 0x%x -> 0x%x\n", + opinfo->level, rsp_oplevel); + } + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + rsp->StructureSize = cpu_to_le16(24); + rsp->OplockLevel = rsp_oplevel; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->VolatileFid = volatile_id; + rsp->PersistentFid = persistent_id; + inc_rfc1001_len(work->response_buf, 24); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); +} + +static int check_lease_state(struct lease *lease, __le32 req_state) +{ + if ((lease->new_state == + (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && + !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { + lease->new_state = req_state; + return 0; + } + + if (lease->new_state == req_state) + return 0; + + return 1; +} + +/** + * smb21_lease_break_ack() - handler for smb2.1 lease break command + * @work: smb work containing lease break command buffer + * + * Return: 0 + */ +static void smb21_lease_break_ack(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_lease_ack *req = smb2_get_msg(work->request_buf); + struct smb2_lease_ack *rsp = smb2_get_msg(work->response_buf); + struct oplock_info *opinfo; + __le32 err = 0; + int ret = 0; + unsigned int lease_change_type; + __le32 lease_state; + struct lease *lease; + + ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", + le32_to_cpu(req->LeaseState)); + opinfo = lookup_lease_in_table(conn, req->LeaseKey); + if (!opinfo) { + ksmbd_debug(OPLOCK, "file not opened\n"); + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + return; + } + lease = opinfo->o_lease; + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + pr_err("unexpected lease break state 0x%x\n", + opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if (check_lease_state(lease, req->LeaseState)) { + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + ksmbd_debug(OPLOCK, + "req lease state: 0x%x, expected state: 0x%x\n", + req->LeaseState, lease->new_state); + goto err_out; + } + + if (!atomic_read(&opinfo->breaking_cnt)) { + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + /* check for bad lease state */ + if (req->LeaseState & + (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && + req->LeaseState != SMB2_LEASE_NONE_LE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else { + /* valid lease state changes */ + err = STATUS_INVALID_DEVICE_STATE; + if (req->LeaseState == SMB2_LEASE_NONE_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_READ; + else + lease_change_type = OPLOCK_READ_HANDLE_TO_READ; + } else { + lease_change_type = 0; + } + } + + switch (lease_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + break; + case OPLOCK_READ_HANDLE_TO_READ: + ret = opinfo_read_handle_to_read(opinfo); + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + break; + default: + ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } + + lease_state = lease->state; + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + opinfo_put(opinfo); + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + rsp->StructureSize = cpu_to_le16(36); + rsp->Reserved = 0; + rsp->Flags = 0; + memcpy(rsp->LeaseKey, req->LeaseKey, 16); + rsp->LeaseState = lease_state; + rsp->LeaseDuration = 0; + inc_rfc1001_len(work->response_buf, 36); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + + opinfo_put(opinfo); + smb2_set_err_rsp(work); +} + +/** + * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break + * @work: smb work containing oplock/lease break command buffer + * + * Return: 0 + */ +int smb2_oplock_break(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); + + switch (le16_to_cpu(req->StructureSize)) { + case OP_BREAK_STRUCT_SIZE_20: + smb20_oplock_break_ack(work); + break; + case OP_BREAK_STRUCT_SIZE_21: + smb21_lease_break_ack(work); + break; + default: + ksmbd_debug(OPLOCK, "invalid break cmd %d\n", + le16_to_cpu(req->StructureSize)); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + } + + return 0; +} + +/** + * smb2_notify() - handler for smb2 notify request + * @work: smb work containing notify command buffer + * + * Return: 0 + */ +int smb2_notify(struct ksmbd_work *work) +{ + struct smb2_change_notify_req *req; + struct smb2_change_notify_rsp *rsp; + + WORK_BUFFERS(work, req, rsp); + + if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { + rsp->hdr.Status = STATUS_INTERNAL_ERROR; + smb2_set_err_rsp(work); + return 0; + } + + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; + return 0; +} + +/** + * smb2_is_sign_req() - handler for checking packet signing status + * @work: smb work containing notify command buffer + * @command: SMB2 command id + * + * Return: true if packed is signed, false otherwise + */ +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) +{ + struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); + + if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && + command != SMB2_NEGOTIATE_HE && + command != SMB2_SESSION_SETUP_HE && + command != SMB2_OPLOCK_BREAK_HE) + return true; + + return false; +} + +/** + * smb2_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb2_check_sign_req(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr = smb2_get_msg(work->request_buf); + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = get_rfc1002_len(work->request_buf); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = get_rfc1002_len(work->request_buf) - + work->next_smb2_rcv_hdr_off; + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, + signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb2_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb2_set_sign_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr; + struct smb2_hdr *req_hdr; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[2]; + size_t len; + int n_vec = 1; + + hdr = smb2_get_msg(work->response_buf); + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(work->response_buf); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb3_check_sign_req(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + char *signing_key; + struct smb2_hdr *hdr; + struct channel *chann; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr = smb2_get_msg(work->request_buf); + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = get_rfc1002_len(work->request_buf); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = get_rfc1002_len(work->request_buf) - + work->next_smb2_rcv_hdr_off; + + if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, conn); + if (!chann) { + return 0; + } + signing_key = chann->smb3signingkey; + } + + if (!signing_key) { + pr_err("SMB3 signing key is not generated\n"); + return 0; + } + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb3_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb3_set_sign_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *req_hdr, *hdr; + struct channel *chann; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[2]; + int n_vec = 1; + size_t len; + char *signing_key; + + hdr = smb2_get_msg(work->response_buf); + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(work->response_buf); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (conn->binding == false && + le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, work->conn); + if (!chann) { + return; + } + signing_key = chann->smb3signingkey; + } + + if (!signing_key) + return; + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_preauth_hash_rsp() - handler for computing preauth hash on response + * @work: smb work containing response buffer + * + */ +void smb3_preauth_hash_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *req, *rsp; + + if (conn->dialect != SMB311_PROT_ID) + return; + + WORK_BUFFERS(work, req, rsp); + + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && + conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + conn->preauth_info->Preauth_HashValue); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { + __u8 *hash_value; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return; + hash_value = preauth_sess->Preauth_HashValue; + } else { + hash_value = sess->Preauth_HashValue; + if (!hash_value) + return; + } + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + hash_value); + } +} + +static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) +{ + struct smb2_transform_hdr *tr_hdr = tr_buf + 4; + struct smb2_hdr *hdr = smb2_get_msg(old_buf); + unsigned int orig_len = get_rfc1002_len(old_buf); + + /* tr_buf must be cleared by the caller */ + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; + tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); + tr_hdr->Flags = cpu_to_le16(TRANSFORM_FLAG_ENCRYPTED); + if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || + cipher_type == SMB2_ENCRYPTION_AES256_GCM) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); + inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); + inc_rfc1001_len(tr_buf, orig_len); +} + +int smb3_encrypt_resp(struct ksmbd_work *work) +{ + char *buf = work->response_buf; + struct kvec iov[3]; + int rc = -ENOMEM; + int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); + + if (ARRAY_SIZE(iov) < rq_nvec) + return -ENOMEM; + + work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); + if (!work->tr_buf) + return rc; + + /* fill transform header */ + fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); + + iov[0].iov_base = work->tr_buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + buf_size += iov[0].iov_len - 4; + + iov[1].iov_base = buf + 4; + iov[1].iov_len = get_rfc1002_len(buf); + if (work->aux_payload_sz) { + iov[1].iov_len = work->resp_hdr_sz - 4; + + iov[2].iov_base = work->aux_payload_buf; + iov[2].iov_len = work->aux_payload_sz; + buf_size += iov[2].iov_len; + } + buf_size += iov[1].iov_len; + work->resp_hdr_sz = iov[1].iov_len; + + rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); + if (rc) + return rc; + + memmove(buf, iov[1].iov_base, iov[1].iov_len); + *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); + + return rc; +} + +bool smb3_is_transform_hdr(void *buf) +{ + struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); + + return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; +} + +int smb3_decrypt_req(struct ksmbd_work *work) +{ + struct ksmbd_session *sess; + char *buf = work->request_buf; + unsigned int pdu_length = get_rfc1002_len(buf); + struct kvec iov[2]; + int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); + int rc = 0; + + if (buf_data_size < sizeof(struct smb2_hdr)) { + pr_err("Transform message is too small (%u)\n", + pdu_length); + return -ECONNABORTED; + } + + if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { + pr_err("Transform message is broken\n"); + return -ECONNABORTED; + } + + sess = ksmbd_session_lookup_all(work->conn, le64_to_cpu(tr_hdr->SessionId)); + if (!sess) { + pr_err("invalid session id(%llx) in transform header\n", + le64_to_cpu(tr_hdr->SessionId)); + return -ECONNABORTED; + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_len = buf_data_size; + rc = ksmbd_crypt_message(work, iov, 2, 0); + if (rc) + return rc; + + memmove(buf + 4, iov[1].iov_base, buf_data_size); + *(__be32 *)buf = cpu_to_be32(buf_data_size); + + return rc; +} + +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); + + if (conn->dialect < SMB30_PROT_ID) + return false; + + if (work->next_smb2_rcv_hdr_off) + rsp = ksmbd_resp_buf_next(work); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && + sess->user && !user_guest(sess->user) && + rsp->Status == STATUS_SUCCESS) + return true; + return false; +} diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h new file mode 100644 index 000000000000..dd10f8031606 --- /dev/null +++ b/fs/smb/server/smb2pdu.h @@ -0,0 +1,536 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include "ntlmssp.h" +#include "smbacl.h" + +/*Create Action Flags*/ +#define FILE_SUPERSEDED 0x00000000 +#define FILE_OPENED 0x00000001 +#define FILE_CREATED 0x00000002 +#define FILE_OVERWRITTEN 0x00000003 + +/* SMB2 Max Credits */ +#define SMB2_MAX_CREDITS 8192 + +/* BB FIXME - analyze following length BB */ +#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ + +#define SMB21_DEFAULT_IOSIZE (1024 * 1024) +#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) +#define SMB3_MIN_IOSIZE (64 * 1024) +#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) +#define SMB3_MAX_MSGSIZE (4 * 4096) + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +struct preauth_integrity_info { + /* PreAuth integrity Hash ID */ + __le16 Preauth_HashId; + /* PreAuth integrity Hash Value */ + __u8 Preauth_HashValue[SMB2_PREAUTH_HASH_SIZE]; +}; + +/* offset is sizeof smb2_negotiate_rsp but rounded up to 8 bytes. */ +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +/* sizeof(struct smb2_negotiate_rsp) = + * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) + */ +#define OFFSET_OF_NEG_CONTEXT 0xe0 +#else +/* sizeof(struct smb2_negotiate_rsp) = + * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) + */ +#define OFFSET_OF_NEG_CONTEXT 0xd0 +#endif + +#define SMB2_SESSION_EXPIRED (0) +#define SMB2_SESSION_IN_PROGRESS BIT(0) +#define SMB2_SESSION_VALID BIT(1) + +#define SMB2_SESSION_TIMEOUT (10 * HZ) + +struct create_durable_req_v2 { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; + __u8 Reserved[8]; + __u8 CreateGuid[16]; +} __packed; + +struct create_durable_reconn_req { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + } Data; +} __packed; + +struct create_durable_reconn_v2_req { + struct create_context ccontext; + __u8 Name[8]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + __u8 CreateGuid[16]; + __le32 Flags; +} __packed; + +struct create_app_inst_id { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[8]; + __u8 AppInstanceId[16]; +} __packed; + +struct create_app_inst_id_vers { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[2]; + __u8 Padding[4]; + __le64 AppInstanceVersionHigh; + __le64 AppInstanceVersionLow; +} __packed; + +struct create_mxac_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 Timestamp; +} __packed; + +struct create_alloc_size_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 AllocationSize; +} __packed; + +struct create_durable_rsp { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[8]; + __u64 data; + } Data; +} __packed; + +struct create_durable_v2_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; +} __packed; + +struct create_mxac_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 QueryStatus; + __le32 MaximalAccess; +} __packed; + +struct create_disk_id_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; + __u8 Reserved[16]; +} __packed; + +/* equivalent of the contents of SMB3.1.1 POSIX open context response */ +struct create_posix_rsp { + struct create_context ccontext; + __u8 Name[16]; + __le32 nlink; + __le32 reparse_tag; + __le32 mode; + /* SidBuffer contain two sids(Domain sid(28), UNIX group sid(16)) */ + u8 SidBuffer[44]; +} __packed; + +struct smb2_buffer_desc_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 + +struct smb_sockaddr_in { + __be16 Port; + __be32 IPv4address; + __u8 Reserved[8]; +} __packed; + +struct smb_sockaddr_in6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6address[16]; + __be32 ScopeId; +} __packed; + +#define INTERNETWORK 0x0002 +#define INTERNETWORKV6 0x0017 + +struct sockaddr_storage_rsp { + __le16 Family; + union { + struct smb_sockaddr_in addr4; + struct smb_sockaddr_in6 addr6; + }; +} __packed; + +#define RSS_CAPABLE 0x00000001 +#define RDMA_CAPABLE 0x00000002 + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + char SockAddr_Storage[128]; +} __packed; + +struct file_object_buf_type1_ioctl_rsp { + __u8 ObjectId[16]; + __u8 BirthVolumeId[16]; + __u8 BirthObjectId[16]; + __u8 DomainId[16]; +} __packed; + +struct resume_key_ioctl_rsp { + __u64 ResumeKey[3]; + __le32 ContextLength; + __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +struct copychunk_ioctl_req { + __le64 ResumeKey[3]; + __le32 ChunkCount; + __le32 Reserved; + __u8 Chunks[1]; /* array of srv_copychunk */ +} __packed; + +struct srv_copychunk { + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; + __le32 Reserved; +} __packed; + +struct copychunk_ioctl_rsp { + __le32 ChunksWritten; + __le32 ChunkBytesWritten; + __le32 TotalBytesWritten; +} __packed; + +struct file_sparse { + __u8 SetSparse; +} __packed; + +/* FILE Info response size */ +#define FILE_DIRECTORY_INFORMATION_SIZE 1 +#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 +#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3 +#define FILE_BASIC_INFORMATION_SIZE 40 +#define FILE_STANDARD_INFORMATION_SIZE 24 +#define FILE_INTERNAL_INFORMATION_SIZE 8 +#define FILE_EA_INFORMATION_SIZE 4 +#define FILE_ACCESS_INFORMATION_SIZE 4 +#define FILE_NAME_INFORMATION_SIZE 9 +#define FILE_RENAME_INFORMATION_SIZE 10 +#define FILE_LINK_INFORMATION_SIZE 11 +#define FILE_NAMES_INFORMATION_SIZE 12 +#define FILE_DISPOSITION_INFORMATION_SIZE 13 +#define FILE_POSITION_INFORMATION_SIZE 14 +#define FILE_FULL_EA_INFORMATION_SIZE 15 +#define FILE_MODE_INFORMATION_SIZE 4 +#define FILE_ALIGNMENT_INFORMATION_SIZE 4 +#define FILE_ALL_INFORMATION_SIZE 104 +#define FILE_ALLOCATION_INFORMATION_SIZE 19 +#define FILE_END_OF_FILE_INFORMATION_SIZE 20 +#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8 +#define FILE_STREAM_INFORMATION_SIZE 32 +#define FILE_PIPE_INFORMATION_SIZE 23 +#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24 +#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25 +#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26 +#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27 +#define FILE_COMPRESSION_INFORMATION_SIZE 16 +#define FILE_OBJECT_ID_INFORMATION_SIZE 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31 +#define FILE_QUOTA_INFORMATION_SIZE 32 +#define FILE_REPARSE_POINT_INFORMATION_SIZE 33 +#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 +#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 + +/* FS Info response size */ +#define FS_DEVICE_INFORMATION_SIZE 8 +#define FS_ATTRIBUTE_INFORMATION_SIZE 16 +#define FS_VOLUME_INFORMATION_SIZE 24 +#define FS_SIZE_INFORMATION_SIZE 24 +#define FS_FULL_SIZE_INFORMATION_SIZE 32 +#define FS_SECTOR_SIZE_INFORMATION_SIZE 28 +#define FS_OBJECT_ID_INFORMATION_SIZE 64 +#define FS_CONTROL_INFORMATION_SIZE 48 +#define FS_POSIX_INFORMATION_SIZE 56 + +/* FS_ATTRIBUTE_File_System_Name */ +#define FS_TYPE_SUPPORT_SIZE 44 +struct fs_type_info { + char *fs_name; + long magic_number; +} __packed; + +/* + * PDU query infolevel structure definitions + * BB consider moving to a different header + */ + +struct smb2_file_access_info { + __le32 AccessFlags; +} __packed; + +struct smb2_file_alignment_info { + __le32 AlignmentRequirement; +} __packed; + +struct smb2_file_basic_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ +} __packed; + +struct smb2_file_alt_name_info { + __le32 FileNameLength; + char FileName[]; +} __packed; + +struct smb2_file_stream_info { + __le32 NextEntryOffset; + __le32 StreamNameLength; + __le64 StreamSize; + __le64 StreamAllocationSize; + char StreamName[]; +} __packed; + +struct smb2_file_ntwrk_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndOfFile; + __le32 Attributes; + __le32 Reserved; +} __packed; + +struct smb2_file_standard_info { + __le64 AllocationSize; + __le64 EndOfFile; + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __le16 Reserved; +} __packed; /* level 18 Query */ + +struct smb2_file_ea_info { + __le32 EASize; +} __packed; + +struct smb2_file_alloc_info { + __le64 AllocationSize; +} __packed; + +struct smb2_file_disposition_info { + __u8 DeletePending; +} __packed; + +struct smb2_file_pos_info { + __le64 CurrentByteOffset; +} __packed; + +#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000100e) + +struct smb2_file_mode_info { + __le32 Mode; +} __packed; + +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +struct smb2_file_comp_info { + __le64 CompressedFileSize; + __le16 CompressionFormat; + __u8 CompressionUnitShift; + __u8 ChunkShift; + __u8 ClusterShift; + __u8 Reserved[3]; +} __packed; + +struct smb2_file_attr_tag_info { + __le32 FileAttributes; + __le32 ReparseTag; +} __packed; + +#define SL_RESTART_SCAN 0x00000001 +#define SL_RETURN_SINGLE_ENTRY 0x00000002 +#define SL_INDEX_SPECIFIED 0x00000004 + +struct smb2_ea_info_req { + __le32 NextEntryOffset; + __u8 EaNameLength; + char name[1]; +} __packed; /* level 15 Query */ + +struct smb2_ea_info { + __le32 NextEntryOffset; + __u8 Flags; + __u8 EaNameLength; + __le16 EaValueLength; + char name[1]; + /* optionally followed by value */ +} __packed; /* level 15 Query */ + +struct create_ea_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb2_ea_info ea; +} __packed; + +struct create_sd_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb_ntsd ntsd; +} __packed; + +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */ + u8 SidBuffer[32]; + __le32 name_len; + u8 name[1]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* functions */ +void init_smb2_1_server(struct ksmbd_conn *conn); +void init_smb3_0_server(struct ksmbd_conn *conn); +void init_smb3_02_server(struct ksmbd_conn *conn); +int init_smb3_11_server(struct ksmbd_conn *conn); + +void init_smb2_max_read_size(unsigned int sz); +void init_smb2_max_write_size(unsigned int sz); +void init_smb2_max_trans_size(unsigned int sz); +void init_smb2_max_credits(unsigned int sz); + +bool is_smb2_neg_cmd(struct ksmbd_work *work); +bool is_smb2_rsp(struct ksmbd_work *work); + +u16 get_smb2_cmd_val(struct ksmbd_work *work); +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); +int init_smb2_rsp_hdr(struct ksmbd_work *work); +int smb2_allocate_rsp_buf(struct ksmbd_work *work); +bool is_chained_smb2_message(struct ksmbd_work *work); +int init_smb2_neg_rsp(struct ksmbd_work *work); +void smb2_set_err_rsp(struct ksmbd_work *work); +int smb2_check_user_session(struct ksmbd_work *work); +int smb2_get_ksmbd_tcon(struct ksmbd_work *work); +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); +int smb2_check_sign_req(struct ksmbd_work *work); +void smb2_set_sign_rsp(struct ksmbd_work *work); +int smb3_check_sign_req(struct ksmbd_work *work); +void smb3_set_sign_rsp(struct ksmbd_work *work); +int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + __le16 dialects_count); +struct file_lock *smb_flock_init(struct file *f); +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); +struct channel *lookup_chann_list(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +void smb3_preauth_hash_rsp(struct ksmbd_work *work); +bool smb3_is_transform_hdr(void *buf); +int smb3_decrypt_req(struct ksmbd_work *work); +int smb3_encrypt_resp(struct ksmbd_work *work); +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); +int smb2_set_rsp_credits(struct ksmbd_work *work); +bool smb3_encryption_negotiated(struct ksmbd_conn *conn); + +/* smb2 misc functions */ +int ksmbd_smb2_check_message(struct ksmbd_work *work); + +/* smb2 command handlers */ +int smb2_handle_negotiate(struct ksmbd_work *work); +int smb2_negotiate_request(struct ksmbd_work *work); +int smb2_sess_setup(struct ksmbd_work *work); +int smb2_tree_connect(struct ksmbd_work *work); +int smb2_tree_disconnect(struct ksmbd_work *work); +int smb2_session_logoff(struct ksmbd_work *work); +int smb2_open(struct ksmbd_work *work); +int smb2_query_info(struct ksmbd_work *work); +int smb2_query_dir(struct ksmbd_work *work); +int smb2_close(struct ksmbd_work *work); +int smb2_echo(struct ksmbd_work *work); +int smb2_set_info(struct ksmbd_work *work); +int smb2_read(struct ksmbd_work *work); +int smb2_write(struct ksmbd_work *work); +int smb2_flush(struct ksmbd_work *work); +int smb2_cancel(struct ksmbd_work *work); +int smb2_lock(struct ksmbd_work *work); +int smb2_ioctl(struct ksmbd_work *work); +int smb2_oplock_break(struct ksmbd_work *work); +int smb2_notify(struct ksmbd_work *ksmbd_work); + +/* + * Get the body of the smb2 message excluding the 4 byte rfc1002 headers + * from request/response buffer. + */ +static inline void *smb2_get_msg(void *buf) +{ + return buf + 4; +} + +#endif /* _SMB2PDU_H */ diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c new file mode 100644 index 000000000000..05d7f3e910bf --- /dev/null +++ b/fs/smb/server/smb_common.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * Copyright (C) 2018 Namjae Jeon + */ + +#include + +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "smbstatus.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/share_config.h" + +/*for shortname implementation */ +static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) +#define MAGIC_CHAR '~' +#define PERIOD '.' +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) + +struct smb_protocol { + int index; + char *name; + char *prot; + __u16 prot_id; +}; + +static struct smb_protocol smb1_protos[] = { + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, + { + SMB2X_PROT, + "\2SMB 2.???", + "SMB2_22", + SMB2X_PROT_ID + }, +}; + +static struct smb_protocol smb2_protos[] = { + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, + { + SMB30_PROT, + "\2SMB 3.0", + "SMB3_00", + SMB30_PROT_ID + }, + { + SMB302_PROT, + "\2SMB 3.02", + "SMB3_02", + SMB302_PROT_ID + }, + { + SMB311_PROT, + "\2SMB 3.1.1", + "SMB3_11", + SMB311_PROT_ID + }, +}; + +unsigned int ksmbd_server_side_copy_max_chunk_count(void) +{ + return 256; +} + +unsigned int ksmbd_server_side_copy_max_chunk_size(void) +{ + return (2U << 30) - 1; +} + +unsigned int ksmbd_server_side_copy_max_total_size(void) +{ + return (2U << 30) - 1; +} + +inline int ksmbd_min_protocol(void) +{ + return SMB21_PROT; +} + +inline int ksmbd_max_protocol(void) +{ + return SMB311_PROT; +} + +int ksmbd_lookup_protocol_idx(char *str) +{ + int offt = ARRAY_SIZE(smb1_protos) - 1; + int len = strlen(str); + + while (offt >= 0) { + if (!strncmp(str, smb1_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb1_protos[offt].prot, offt); + return smb1_protos[offt].index; + } + offt--; + } + + offt = ARRAY_SIZE(smb2_protos) - 1; + while (offt >= 0) { + if (!strncmp(str, smb2_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb2_protos[offt].prot, offt); + return smb2_protos[offt].index; + } + offt--; + } + return -1; +} + +/** + * ksmbd_verify_smb_message() - check for valid smb2 request header + * @work: smb work + * + * check for valid smb signature and packet direction(request/response) + * + * Return: 0 on success, otherwise -EINVAL + */ +int ksmbd_verify_smb_message(struct ksmbd_work *work) +{ + struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); + struct smb_hdr *hdr; + + if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) + return ksmbd_smb2_check_message(work); + + hdr = work->request_buf; + if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && + hdr->Command == SMB_COM_NEGOTIATE) { + work->conn->outstanding_credits++; + return 0; + } + + return -EINVAL; +} + +/** + * ksmbd_smb_request() - check for valid smb request type + * @conn: connection instance + * + * Return: true on success, otherwise false + */ +bool ksmbd_smb_request(struct ksmbd_conn *conn) +{ + __le32 *proto = (__le32 *)smb2_get_msg(conn->request_buf); + + if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { + pr_err_ratelimited("smb2 compression not support yet"); + return false; + } + + if (*proto != SMB1_PROTO_NUMBER && + *proto != SMB2_PROTO_NUMBER && + *proto != SMB2_TRANSFORM_PROTO_NUM) + return false; + + return true; +} + +static bool supported_protocol(int idx) +{ + if (idx == SMB2X_PROT && + (server_conf.min_protocol >= SMB21_PROT || + server_conf.max_protocol <= SMB311_PROT)) + return true; + + return (server_conf.min_protocol <= idx && + idx <= server_conf.max_protocol); +} + +static char *next_dialect(char *dialect, int *next_off, int bcount) +{ + dialect = dialect + *next_off; + *next_off = strnlen(dialect, bcount); + if (dialect[*next_off] != '\0') + return NULL; + return dialect; +} + +static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) +{ + int i, seq_num, bcount, next; + char *dialect; + + for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { + seq_num = 0; + next = 0; + dialect = cli_dialects; + bcount = le16_to_cpu(byte_count); + do { + dialect = next_dialect(dialect, &next, bcount); + if (!dialect) + break; + ksmbd_debug(SMB, "client requested dialect %s\n", + dialect); + if (!strcmp(dialect, smb1_protos[i].name)) { + if (supported_protocol(smb1_protos[i].index)) { + ksmbd_debug(SMB, + "selected %s dialect\n", + smb1_protos[i].name); + if (smb1_protos[i].index == SMB1_PROT) + return seq_num; + return smb1_protos[i].prot_id; + } + } + seq_num++; + bcount -= (++next); + } while (bcount > 0); + } + + return BAD_PROT_ID; +} + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) +{ + int i; + int count; + + for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { + count = le16_to_cpu(dialects_count); + while (--count >= 0) { + ksmbd_debug(SMB, "client requested dialect 0x%x\n", + le16_to_cpu(cli_dialects[count])); + if (le16_to_cpu(cli_dialects[count]) != + smb2_protos[i].prot_id) + continue; + + if (supported_protocol(smb2_protos[i].index)) { + ksmbd_debug(SMB, "selected %s dialect\n", + smb2_protos[i].name); + return smb2_protos[i].prot_id; + } + } + } + + return BAD_PROT_ID; +} + +static int ksmbd_negotiate_smb_dialect(void *buf) +{ + int smb_buf_length = get_rfc1002_len(buf); + __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; + + if (proto == SMB2_PROTO_NUMBER) { + struct smb2_negotiate_req *req; + int smb2_neg_size = + offsetof(struct smb2_negotiate_req, Dialects); + + req = (struct smb2_negotiate_req *)smb2_get_msg(buf); + if (smb2_neg_size > smb_buf_length) + goto err_out; + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb_buf_length) + goto err_out; + + return ksmbd_lookup_dialect_by_id(req->Dialects, + req->DialectCount); + } + + proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) { + struct smb_negotiate_req *req; + + req = (struct smb_negotiate_req *)buf; + if (le16_to_cpu(req->ByteCount) < 2) + goto err_out; + + if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + + le16_to_cpu(req->ByteCount) > smb_buf_length) { + goto err_out; + } + + return ksmbd_lookup_dialect_by_name(req->DialectsArray, + req->ByteCount); + } + +err_out: + return BAD_PROT_ID; +} + +#define SMB_COM_NEGOTIATE_EX 0x0 + +/** + * get_smb1_cmd_val() - get smb command value from smb header + * @work: smb work containing smb header + * + * Return: smb command value + */ +static u16 get_smb1_cmd_val(struct ksmbd_work *work) +{ + return SMB_COM_NEGOTIATE_EX; +} + +/** + * init_smb1_rsp_hdr() - initialize smb negotiate response header + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -EINVAL + */ +static int init_smb1_rsp_hdr(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + + /* + * Remove 4 byte direct TCP header. + */ + *(__be32 *)work->response_buf = + cpu_to_be32(sizeof(struct smb_hdr) - 4); + + rsp_hdr->Command = SMB_COM_NEGOTIATE; + *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; + rsp_hdr->Flags = SMBFLG_RESPONSE; + rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS | + SMBFLG2_EXT_SEC | SMBFLG2_IS_LONG_NAME; + rsp_hdr->Pid = rcv_hdr->Pid; + rsp_hdr->Mid = rcv_hdr->Mid; + return 0; +} + +/** + * smb1_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +static int smb1_check_user_session(struct ksmbd_work *work) +{ + unsigned int cmd = work->conn->ops->get_cmd_val(work); + + if (cmd == SMB_COM_NEGOTIATE_EX) + return 0; + + return -EINVAL; +} + +/** + * smb1_allocate_rsp_buf() - allocate response buffer for a command + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -ENOMEM + */ +static int smb1_allocate_rsp_buf(struct ksmbd_work *work) +{ + work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE, + GFP_KERNEL | __GFP_ZERO); + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + + if (!work->response_buf) { + pr_err("Failed to allocate %u bytes buffer\n", + MAX_CIFS_SMALL_BUFFER_SIZE); + return -ENOMEM; + } + + return 0; +} + +static struct smb_version_ops smb1_server_ops = { + .get_cmd_val = get_smb1_cmd_val, + .init_rsp_hdr = init_smb1_rsp_hdr, + .allocate_rsp_buf = smb1_allocate_rsp_buf, + .check_user_session = smb1_check_user_session, +}; + +static int smb1_negotiate(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE); +} + +static struct smb_version_cmds smb1_server_cmds[1] = { + [SMB_COM_NEGOTIATE_EX] = { .proc = smb1_negotiate, }, +}; + +static void init_smb1_server(struct ksmbd_conn *conn) +{ + conn->ops = &smb1_server_ops; + conn->cmds = smb1_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); +} + +void ksmbd_init_smb_server(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + __le32 proto; + + if (conn->need_neg == false) + return; + + proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) + init_smb1_server(conn); + else + init_smb3_11_server(conn); +} + +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)) +{ + int i, rc = 0; + struct ksmbd_conn *conn = work->conn; + struct user_namespace *user_ns = file_mnt_user_ns(dir->filp); + + for (i = 0; i < 2; i++) { + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + struct dentry *dentry; + + if (!dir->dot_dotdot[i]) { /* fill dot entry info */ + if (i == 0) { + d_info->name = "."; + d_info->name_len = 1; + dentry = dir->filp->f_path.dentry; + } else { + d_info->name = ".."; + d_info->name_len = 2; + dentry = dir->filp->f_path.dentry->d_parent; + } + + if (!match_pattern(d_info->name, d_info->name_len, + search_pattern)) { + dir->dot_dotdot[i] = 1; + continue; + } + + ksmbd_kstat.kstat = &kstat; + ksmbd_vfs_fill_dentry_attrs(work, + user_ns, + dentry, + &ksmbd_kstat); + rc = fn(conn, info_level, d_info, &ksmbd_kstat); + if (rc) + break; + if (d_info->out_buf_len <= 0) + break; + + dir->dot_dotdot[i] = 1; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; + break; + } + } + } + + return rc; +} + +/** + * ksmbd_extract_shortname() - get shortname from long filename + * @conn: connection instance + * @longname: source long filename + * @shortname: destination short filename + * + * Return: shortname length or 0 when source long name is '.' or '..' + * TODO: Though this function comforms the restriction of 8.3 Filename spec, + * but the result is different with Windows 7's one. need to check. + */ +int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, + char *shortname) +{ + const char *p; + char base[9], extension[4]; + char out[13] = {0}; + int baselen = 0; + int extlen = 0, len = 0; + unsigned int csum = 0; + const unsigned char *ptr; + bool dot_present = true; + + p = longname; + if ((*p == '.') || (!(strcmp(p, "..")))) { + /*no mangling required */ + return 0; + } + + p = strrchr(longname, '.'); + if (p == longname) { /*name starts with a dot*/ + strscpy(extension, "___", strlen("___")); + } else { + if (p) { + p++; + while (*p && extlen < 3) { + if (*p != '.') + extension[extlen++] = toupper(*p); + p++; + } + extension[extlen] = '\0'; + } else { + dot_present = false; + } + } + + p = longname; + if (*p == '.') { + p++; + longname++; + } + while (*p && (baselen < 5)) { + if (*p != '.') + base[baselen++] = toupper(*p); + p++; + } + + base[baselen] = MAGIC_CHAR; + memcpy(out, base, baselen + 1); + + ptr = longname; + len = strlen(longname); + for (; len > 0; len--, ptr++) + csum += *ptr; + + csum = csum % (MANGLE_BASE * MANGLE_BASE); + out[baselen + 1] = mangle(csum / MANGLE_BASE); + out[baselen + 2] = mangle(csum); + out[baselen + 3] = PERIOD; + + if (dot_present) + memcpy(&out[baselen + 4], extension, 4); + else + out[baselen + 4] = '\0'; + smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, + conn->local_nls, 0); + len = strlen(out) * 2; + return len; +} + +static int __smb2_negotiate(struct ksmbd_conn *conn) +{ + return (conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID); +} + +static int smb_handle_negotiate(struct ksmbd_work *work) +{ + struct smb_negotiate_rsp *neg_rsp = work->response_buf; + + ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); + + /* Add 2 byte bcc and 2 byte DialectIndex. */ + inc_rfc1001_len(work->response_buf, 4); + neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + neg_rsp->hdr.WordCount = 1; + neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); + neg_rsp->ByteCount = 0; + return 0; +} + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) +{ + struct ksmbd_conn *conn = work->conn; + int ret; + + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + + if (command == SMB2_NEGOTIATE_HE) { + ret = smb2_handle_negotiate(work); + return ret; + } + + if (command == SMB_COM_NEGOTIATE) { + if (__smb2_negotiate(conn)) { + init_smb3_11_server(conn); + init_smb2_neg_rsp(work); + ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); + return 0; + } + return smb_handle_negotiate(work); + } + + pr_err("Unknown SMB negotiation command: %u\n", command); + return -EINVAL; +} + +enum SHARED_MODE_ERRORS { + SHARE_DELETE_ERROR, + SHARE_READ_ERROR, + SHARE_WRITE_ERROR, + FILE_READ_ERROR, + FILE_WRITE_ERROR, + FILE_DELETE_ERROR, +}; + +static const char * const shared_mode_errors[] = { + "Current access mode does not permit SHARE_DELETE", + "Current access mode does not permit SHARE_READ", + "Current access mode does not permit SHARE_WRITE", + "Desired access mode does not permit FILE_READ", + "Desired access mode does not permit FILE_WRITE", + "Desired access mode does not permit FILE_DELETE", +}; + +static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, + struct ksmbd_file *curr_fp) +{ + ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); + ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", + prev_fp->saccess, curr_fp->daccess); +} + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) +{ + int rc = 0; + struct ksmbd_file *prev_fp; + + /* + * Lookup fp in master fp list, and check desired access and + * shared mode between previous open and current open. + */ + read_lock(&curr_fp->f_ci->m_lock); + list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { + if (file_inode(filp) != file_inode(prev_fp->filp)) + continue; + + if (filp == prev_fp->filp) + continue; + + if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) + if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) + continue; + + if (prev_fp->attrib_only != curr_fp->attrib_only) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && + curr_fp->daccess & FILE_DELETE_LE) { + smb_shared_mode_error(SHARE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + /* + * Only check FILE_SHARE_DELETE if stream opened and + * normal file opened. + */ + if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && + curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { + smb_shared_mode_error(SHARE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && + curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { + smb_shared_mode_error(SHARE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_READ_LE)) { + smb_shared_mode_error(FILE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { + smb_shared_mode_error(FILE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & FILE_DELETE_LE && + !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { + smb_shared_mode_error(FILE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + } + read_unlock(&curr_fp->f_ci->m_lock); + + return rc; +} + +bool is_asterisk(char *p) +{ + return p && p[0] == '*'; +} + +int ksmbd_override_fsids(struct ksmbd_work *work) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct cred *cred; + struct group_info *gi; + unsigned int uid; + unsigned int gid; + + uid = user_uid(sess->user); + gid = user_gid(sess->user); + if (share->force_uid != KSMBD_SHARE_INVALID_UID) + uid = share->force_uid; + if (share->force_gid != KSMBD_SHARE_INVALID_GID) + gid = share->force_gid; + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + cred->fsuid = make_kuid(&init_user_ns, uid); + cred->fsgid = make_kgid(&init_user_ns, gid); + + gi = groups_alloc(0); + if (!gi) { + abort_creds(cred); + return -ENOMEM; + } + set_groups(cred, gi); + put_group_info(gi); + + if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) + cred->cap_effective = cap_drop_fs_set(cred->cap_effective); + + WARN_ON(work->saved_cred); + work->saved_cred = override_creds(cred); + if (!work->saved_cred) { + abort_creds(cred); + return -EINVAL; + } + return 0; +} + +void ksmbd_revert_fsids(struct ksmbd_work *work) +{ + const struct cred *cred; + + WARN_ON(!work->saved_cred); + + cred = current_cred(); + revert_creds(work->saved_cred); + put_cred(cred); + work->saved_cred = NULL; +} + +__le32 smb_map_generic_desired_access(__le32 daccess) +{ + if (daccess & FILE_GENERIC_READ_LE) { + daccess |= cpu_to_le32(GENERIC_READ_FLAGS); + daccess &= ~FILE_GENERIC_READ_LE; + } + + if (daccess & FILE_GENERIC_WRITE_LE) { + daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); + daccess &= ~FILE_GENERIC_WRITE_LE; + } + + if (daccess & FILE_GENERIC_EXECUTE_LE) { + daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); + daccess &= ~FILE_GENERIC_EXECUTE_LE; + } + + if (daccess & FILE_GENERIC_ALL_LE) { + daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); + daccess &= ~FILE_GENERIC_ALL_LE; + } + + return daccess; +} diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h new file mode 100644 index 000000000000..e63d2a4f466b --- /dev/null +++ b/fs/smb/server/smb_common.h @@ -0,0 +1,468 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SMB_COMMON_H__ +#define __SMB_COMMON_H__ + +#include + +#include "glob.h" +#include "nterr.h" +#include "../common/smb2pdu.h" +#include "smb2pdu.h" + +/* ksmbd's Specific ERRNO */ +#define ESHARE 50000 + +#define SMB1_PROT 0 +#define SMB2_PROT 1 +#define SMB21_PROT 2 +/* multi-protocol negotiate request */ +#define SMB2X_PROT 3 +#define SMB30_PROT 4 +#define SMB302_PROT 5 +#define SMB311_PROT 6 +#define BAD_PROT 0xFFFF + +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#define SMB21_VERSION_STRING "2.1" +#define SMB30_VERSION_STRING "3.0" +#define SMB302_VERSION_STRING "3.02" +#define SMB311_VERSION_STRING "3.1.1" + +#define SMB_ECHO_INTERVAL (60 * HZ) + +#define CIFS_DEFAULT_IOSIZE (64 * 1024) +#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ + +#define MAX_STREAM_PROT_LEN 0x00FFFFFF + +/* Responses when opening a file. */ +#define F_SUPERSEDED 0 +#define F_OPENED 1 +#define F_CREATED 2 +#define F_OVERWRITTEN 3 + +/* + * File Attribute flags + */ +#define ATTR_POSIX_SEMANTICS 0x01000000 +#define ATTR_BACKUP_SEMANTICS 0x02000000 +#define ATTR_DELETE_ON_CLOSE 0x04000000 +#define ATTR_SEQUENTIAL_SCAN 0x08000000 +#define ATTR_RANDOM_ACCESS 0x10000000 +#define ATTR_NO_BUFFERING 0x20000000 +#define ATTR_WRITE_THROUGH 0x80000000 + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 + +#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ +#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ +#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ +#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ +/* with the file can be read */ +#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ +/* with the file can be written */ +#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ +/* the file using system paging I/O */ +#define FILE_DELETE_CHILD 0x00000040 +#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ +/* file can be read */ +#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ +/* file can be written */ +#define DELETE 0x00010000 /* The file can be deleted */ +#define READ_CONTROL 0x00020000 /* The access control list and */ +/* ownership associated with the */ +/* file can be read */ +#define WRITE_DAC 0x00040000 /* The access control list and */ +/* ownership associated with the */ +/* file can be written. */ +#define WRITE_OWNER 0x00080000 /* Ownership information associated */ +/* with the file can be written */ +#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ +/* synchronize with the completion */ +/* of an input/output request */ +#define GENERIC_ALL 0x10000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 +/* In summary - Relevant file */ +/* access flags from CIFS are */ +/* file_read_data, file_write_data */ +/* file_execute, file_read_attributes*/ +/* write_dac, and delete. */ + +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ + | FILE_READ_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA \ + | FILE_DELETE_CHILD \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + +/* generic flags for file open */ +#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ + FILE_READ_ATTRIBUTES | \ + FILE_READ_EA | SYNCHRONIZE) + +#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \ + FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ + FILE_APPEND_DATA | SYNCHRONIZE) + +#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \ + FILE_READ_ATTRIBUTES | SYNCHRONIZE) + +#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \ + WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \ + FILE_WRITE_DATA | FILE_APPEND_DATA | \ + FILE_READ_EA | FILE_WRITE_EA | \ + FILE_EXECUTE | FILE_DELETE_CHILD | \ + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) + +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) +#define SMB_COM_NEGOTIATE 0x72 +#define SMB1_CLIENT_GUID_SIZE (16) + +#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ + +#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) +#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) +#define SMBFLG2_UNICODE cpu_to_le16(0x8000) + +struct smb_hdr { + __be32 smb_buf_length; + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __packed DosError; + __le32 CifsError; + } __packed Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __packed Sequence; + __u8 SecuritySignature[8]; /* le */ + } __packed Signature; + __u8 pad[2]; + __le16 Tid; + __le16 Pid; + __le16 Uid; + __le16 Mid; + __u8 WordCount; +} __packed; + +struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[1]; +} __packed; + +struct smb_negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __le16 ByteCount; +} __packed; + +struct filesystem_attribute_info { + __le32 Attributes; + __le32 MaxPathNameComponentLength; + __le32 FileSystemNameLen; + __le16 FileSystemName[1]; /* do not have to save this - get subset? */ +} __packed; + +struct filesystem_device_info { + __le32 DeviceType; + __le32 DeviceCharacteristics; +} __packed; /* device info level 0x104 */ + +struct filesystem_vol_info { + __le64 VolumeCreationTime; + __le32 SerialNumber; + __le32 VolumeLabelSize; + __le16 Reserved; + __le16 VolumeLabel[1]; +} __packed; + +struct filesystem_info { + __le64 TotalAllocationUnits; + __le64 FreeAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; /* size info, level 0x103 */ + +#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ +#define STRING_LENGTH 28 + +struct fs_extended_info { + __le32 magic; + __le32 version; + __le32 release; + __u64 rel_date; + char version_string[STRING_LENGTH]; +} __packed; + +struct object_id_info { + char objid[16]; + struct fs_extended_info extended_info; +} __packed; + +struct file_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0x101 FF resp data */ + +struct file_names_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0xc FF resp data */ + +struct file_full_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; + char FileName[1]; +} __packed; /* level 0x102 FF resp */ + +struct file_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + char FileName[1]; +} __packed; /* level 0x104 FFrsp data */ + +struct file_id_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + __le16 Reserved2; + __le64 UniqueId; + char FileName[1]; +} __packed; + +struct file_id_full_dir_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ + char FileName[1]; +} __packed; /* level 0x105 FF rsp data */ + +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __le16 lock_cmd; + __u32 capabilities; + __u32 max_read_size; + __u32 max_write_size; + __u32 max_trans_size; + __u32 max_credits; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; + size_t create_durable_size; + size_t create_durable_v2_size; + size_t create_mxac_size; + size_t create_disk_id_size; + size_t create_posix_size; +}; + +struct filesystem_posix_info { + /* For undefined recommended transfer size return -1 in that field */ + __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ + __le32 BlockSize; + /* The next three fields are in terms of the block size. + * (above). If block size is unknown, 4096 would be a + * reasonable block size for a server to report. + * Note that returning the blocks/blocksavail removes need + * to make a second call (to QFSInfo level 0x103 to get this info. + * UserBlockAvail is typically less than or equal to BlocksAvail, + * if no distinction is made return the same value in each + */ + __le64 TotalBlocks; + __le64 BlocksAvail; /* bfree */ + __le64 UserBlocksAvail; /* bavail */ + /* For undefined Node fields or FSID return -1 */ + __le64 TotalFileNodes; + __le64 FreeFileNodes; + __le64 FileSysIdentifier; /* fsid */ + /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ + /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ +} __packed; + +struct smb_version_ops { + u16 (*get_cmd_val)(struct ksmbd_work *swork); + int (*init_rsp_hdr)(struct ksmbd_work *swork); + void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); + int (*allocate_rsp_buf)(struct ksmbd_work *work); + int (*set_rsp_credits)(struct ksmbd_work *work); + int (*check_user_session)(struct ksmbd_work *work); + int (*get_ksmbd_tcon)(struct ksmbd_work *work); + bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); + int (*check_sign_req)(struct ksmbd_work *work); + void (*set_sign_rsp)(struct ksmbd_work *work); + int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); + int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); + bool (*is_transform_hdr)(void *buf); + int (*decrypt_req)(struct ksmbd_work *work); + int (*encrypt_resp)(struct ksmbd_work *work); +}; + +struct smb_version_cmds { + int (*proc)(struct ksmbd_work *swork); +}; + +int ksmbd_min_protocol(void); +int ksmbd_max_protocol(void); + +int ksmbd_lookup_protocol_idx(char *str); + +int ksmbd_verify_smb_message(struct ksmbd_work *work); +bool ksmbd_smb_request(struct ksmbd_conn *conn); + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); + +void ksmbd_init_smb_server(struct ksmbd_work *work); + +struct ksmbd_kstat; +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, + int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, + int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)); + +int ksmbd_extract_shortname(struct ksmbd_conn *conn, + const char *longname, + char *shortname); + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); +int ksmbd_override_fsids(struct ksmbd_work *work); +void ksmbd_revert_fsids(struct ksmbd_work *work); + +unsigned int ksmbd_server_side_copy_max_chunk_count(void); +unsigned int ksmbd_server_side_copy_max_chunk_size(void); +unsigned int ksmbd_server_side_copy_max_total_size(void); +bool is_asterisk(char *p); +__le32 smb_map_generic_desired_access(__le32 daccess); + +static inline unsigned int get_rfc1002_len(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void inc_rfc1001_len(void *buf, int count) +{ + be32_add_cpu((__be32 *)buf, count); +} +#endif /* __SMB_COMMON_H__ */ diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c new file mode 100644 index 000000000000..b05ff9b146b5 --- /dev/null +++ b/fs/smb/server/smbacl.c @@ -0,0 +1,1436 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) International Business Machines Corp., 2007,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include +#include +#include +#include + +#include "smbacl.h" +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "mgmt/share_config.h" + +static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* security id for everyone/world system group */ +static const struct smb_sid creator_owner = { + 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; +/* security id for everyone/world system group */ +static const struct smb_sid creator_group = { + 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; + +/* security id for everyone/world system group */ +static const struct smb_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct smb_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; + +/* S-1-22-1 Unmapped Unix users */ +static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-22-2 Unmapped Unix groups */ +static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ + +/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ + +/* S-1-5-88-1 Unix uid */ +static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-2 Unix gid */ +static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-3 Unix mode */ +static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) +{ + int i; + int num_subauth, num_sat, num_saw; + + if (!ctsid || !cwsid) + return 1; + + /* compare the revision */ + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } + } + + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; + num_subauth = num_sat < num_saw ? num_sat : num_saw; + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (le32_to_cpu(ctsid->sub_auth[i]) > + le32_to_cpu(cwsid->sub_auth[i])) + return 1; + else + return -1; + } + } + } + + return 0; /* sids compare/match */ +} + +static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) +{ + int i; + + dst->revision = src->revision; + dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); + for (i = 0; i < NUM_AUTHS; ++i) + dst->authority[i] = src->authority[i]; + for (i = 0; i < dst->num_subauth; ++i) + dst->sub_auth[i] = src->sub_auth[i]; +} + +/* + * change posix mode to reflect permissions + * pmode is the existing mode (we only want to overwrite part of this + * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 + */ +static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, + int type) +{ + __u32 flags = le32_to_cpu(ace_flags); + umode_t mode = 0; + + if (flags & GENERIC_ALL) { + mode = 0777; + ksmbd_debug(SMB, "all perms\n"); + return mode; + } + + if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) + mode = 0444; + if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { + mode |= 0222; + if (S_ISDIR(fattr->cf_mode)) + mode |= 0111; + } + if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) + mode |= 0111; + + if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) + mode = ~mode; + + ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); + + return mode; +} + +/* + * Generate access flags to reflect permissions mode is the existing mode. + * This function is called for every ACE in the DACL whose SID matches + * with either owner or group or everyone. + */ +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* + * check for R/W/X UGO since we do not know whose flags + * is this but we have cleared all the bits sans RWX for + * either user or group or other as per bits_to_use + */ + if (mode & 0444) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & 0222) + *pace_flags |= FILE_WRITE_RIGHTS; + if (mode & 0111) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + + ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", + mode, *pace_flags); +} + +static __u16 fill_ace_for_sid(struct smb_ace *pntace, + const struct smb_sid *psid, int type, int flags, + umode_t mode, umode_t bits) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = type; + pntace->flags = flags; + mode_to_access_flags(mode, bits, &access_req); + if (!access_req) + access_req = SET_MINIMUM_RIGHTS; + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return size; +} + +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) +{ + switch (sidtype) { + case SIDOWNER: + smb_copy_sid(ssid, &server_conf.domain_sid); + break; + case SIDUNIX_USER: + smb_copy_sid(ssid, &sid_unix_users); + break; + case SIDUNIX_GROUP: + smb_copy_sid(ssid, &sid_unix_groups); + break; + case SIDCREATOR_OWNER: + smb_copy_sid(ssid, &creator_owner); + return; + case SIDCREATOR_GROUP: + smb_copy_sid(ssid, &creator_group); + return; + case SIDNFS_USER: + smb_copy_sid(ssid, &sid_unix_NFS_users); + break; + case SIDNFS_GROUP: + smb_copy_sid(ssid, &sid_unix_NFS_groups); + break; + case SIDNFS_MODE: + smb_copy_sid(ssid, &sid_unix_NFS_mode); + break; + default: + return; + } + + /* RID */ + ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); + ssid->num_subauth++; +} + +static int sid_to_id(struct user_namespace *user_ns, + struct smb_sid *psid, uint sidtype, + struct smb_fattr *fattr) +{ + int rc = -EINVAL; + + /* + * If we have too many subauthorities, then something is really wrong. + * Just return an error. + */ + if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { + pr_err("%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); + return -EIO; + } + + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + uid = KUIDT_INIT(id); + uid = from_vfsuid(user_ns, &init_user_ns, VFSUIDT_INIT(uid)); + if (uid_valid(uid)) { + fattr->cf_uid = uid; + rc = 0; + } + } else { + kgid_t gid; + gid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + gid = KGIDT_INIT(id); + gid = from_vfsgid(user_ns, &init_user_ns, VFSGIDT_INIT(gid)); + if (gid_valid(gid)) { + fattr->cf_gid = gid; + rc = 0; + } + } + + return rc; +} + +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace) +{ + int i; + + pace->e_tag = ACL_USER_OBJ; + pace->e_perm = state->owner.allow; + for (i = 0; i < state->users->n; i++) { + pace++; + pace->e_tag = ACL_USER; + pace->e_uid = state->users->aces[i].uid; + pace->e_perm = state->users->aces[i].perms.allow; + } + + pace++; + pace->e_tag = ACL_GROUP_OBJ; + pace->e_perm = state->group.allow; + + for (i = 0; i < state->groups->n; i++) { + pace++; + pace->e_tag = ACL_GROUP; + pace->e_gid = state->groups->aces[i].gid; + pace->e_perm = state->groups->aces[i].perms.allow; + } + + if (state->users->n || state->groups->n) { + pace++; + pace->e_tag = ACL_MASK; + pace->e_perm = state->mask.allow; + } + + pace++; + pace->e_tag = ACL_OTHER; + pace->e_perm = state->other.allow; +} + +int init_acl_state(struct posix_acl_state *state, int cnt) +{ + int alloc; + + memset(state, 0, sizeof(struct posix_acl_state)); + /* + * In the worst case, each individual acl could be for a distinct + * named user or group, but we don't know which, so we allocate + * enough space for either: + */ + alloc = sizeof(struct posix_ace_state_array) + + cnt * sizeof(struct posix_user_ace_state); + state->users = kzalloc(alloc, GFP_KERNEL); + if (!state->users) + return -ENOMEM; + state->groups = kzalloc(alloc, GFP_KERNEL); + if (!state->groups) { + kfree(state->users); + return -ENOMEM; + } + return 0; +} + +void free_acl_state(struct posix_acl_state *state) +{ + kfree(state->users); + kfree(state->groups); +} + +static void parse_dacl(struct user_namespace *user_ns, + struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + int i, ret; + int num_aces = 0; + unsigned int acl_size; + char *acl_base; + struct smb_ace **ppace; + struct posix_acl_entry *cf_pace, *cf_pdace; + struct posix_acl_state acl_state, default_acl_state; + umode_t mode = 0, acl_mode; + bool owner_found = false, group_found = false, others_found = false; + + if (!pdacl) + return; + + /* validate that we do not go past end of acl */ + if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || + end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + pr_err("ACL too small to parse DACL\n"); + return; + } + + ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + + num_aces = le32_to_cpu(pdacl->num_aces); + if (num_aces <= 0) + return; + + if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) + return; + + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); + if (!ppace) + return; + + ret = init_acl_state(&acl_state, num_aces); + if (ret) + return; + ret = init_acl_state(&default_acl_state, num_aces); + if (ret) { + free_acl_state(&acl_state); + return; + } + + /* + * reset rwx permissions for user/group/other. + * Also, if num_aces is 0 i.e. DACL has no ACEs, + * user/group/other have no permissions + */ + for (i = 0; i < num_aces; ++i) { + if (end_of_acl - acl_base < acl_size) + break; + + ppace[i] = (struct smb_ace *)(acl_base + acl_size); + acl_base = (char *)ppace[i]; + acl_size = offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth); + + if (end_of_acl - acl_base < acl_size || + ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + (end_of_acl - acl_base < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || + (le16_to_cpu(ppace[i]->size) < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) + break; + + acl_size = le16_to_cpu(ppace[i]->size); + ppace[i]->access_req = + smb_map_generic_desired_access(ppace[i]->access_req); + + if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { + fattr->cf_mode = + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else if (!compare_sids(&ppace[i]->sid, pownersid)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0700; + + if (!owner_found) { + mode &= ~(0700); + mode |= acl_mode; + } + owner_found = true; + } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || + ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == + DOMAIN_USER_RID_LE) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0070; + if (!group_found) { + mode &= ~(0070); + mode |= acl_mode; + } + group_found = true; + } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0007; + if (!others_found) { + mode &= ~(0007); + mode |= acl_mode; + } + others_found = true; + } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { + continue; + } else { + struct smb_fattr temp_fattr; + + acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, + ppace[i]->type); + temp_fattr.cf_uid = INVALID_UID; + ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); + if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, ret); + continue; + } + + acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = + temp_fattr.cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + temp_fattr.cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + } + } + kfree(ppace); + + if (owner_found) { + /* The owner must be set to at least read-only. */ + acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + fattr->cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + } + + if (group_found) { + acl_state.group.allow = (mode & 0070) >> 3; + acl_state.groups->aces[acl_state.groups->n].gid = + fattr->cf_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + default_acl_state.group.allow = (mode & 0070) >> 3; + default_acl_state.groups->aces[default_acl_state.groups->n].gid = + fattr->cf_gid; + default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + } + + if (others_found) { + fattr->cf_mode &= ~(0007); + fattr->cf_mode |= mode & 0007; + + acl_state.other.allow = mode & 0007; + default_acl_state.other.allow = mode & 0007; + } + + if (acl_state.users->n || acl_state.groups->n) { + acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = + posix_acl_alloc(acl_state.users->n + + acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_acls) { + cf_pace = fattr->cf_acls->a_entries; + posix_state_to_acl(&acl_state, cf_pace); + } + } + } + + if (default_acl_state.users->n || default_acl_state.groups->n) { + default_acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_dacls = + posix_acl_alloc(default_acl_state.users->n + + default_acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_dacls) { + cf_pdace = fattr->cf_dacls->a_entries; + posix_state_to_acl(&default_acl_state, cf_pdace); + } + } + } + free_acl_state(&acl_state); + free_acl_state(&default_acl_state); +} + +static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, + struct smb_ace *pndace, + struct smb_fattr *fattr, u32 *num_aces, + u16 *size, u32 nt_aces_num) +{ + struct posix_acl_entry *pace; + struct smb_sid *sid; + struct smb_ace *ntace; + int i, j; + + if (!fattr->cf_acls) + goto posix_default_acl; + + pace = fattr->cf_acls->a_entries; + for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { + int flags = 0; + + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + unsigned int sid_type = SIDOWNER; + + uid = posix_acl_uid_translate(user_ns, pace); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDUNIX_GROUP, sid); + } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { + smb_copy_sid(sid, &sid_everyone); + } else { + kfree(sid); + continue; + } + ntace = pndace; + for (j = 0; j < nt_aces_num; j++) { + if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == + sid->sub_auth[sid->num_subauth - 1]) + goto pass_same_sid; + ntace = (struct smb_ace *)((char *)ntace + + le16_to_cpu(ntace->size)); + } + + if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) + flags = 0x03; + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + + if (S_ISDIR(fattr->cf_mode) && + (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, + 0x03, pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + } + +pass_same_sid: + kfree(sid); + } + + if (nt_aces_num) + return; + +posix_default_acl: + if (!fattr->cf_dacls) + return; + + pace = fattr->cf_dacls->a_entries; + for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + + uid = posix_acl_uid_translate(user_ns, pace); + id_to_sid(uid, SIDCREATOR_OWNER, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDCREATOR_GROUP, sid); + } else { + kfree(sid); + continue; + } + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + kfree(sid); + } +} + +static void set_ntacl_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, + struct smb_acl *nt_dacl, + unsigned int aces_size, + const struct smb_sid *pownersid, + const struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + struct smb_ace *ntace, *pndace; + int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; + unsigned short size = 0; + int i; + + pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + if (nt_num_aces) { + ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); + for (i = 0; i < nt_num_aces; i++) { + unsigned short nt_ace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + nt_ace_size = le16_to_cpu(ntace->size); + if (nt_ace_size > aces_size) + break; + + memcpy((char *)pndace + size, ntace, nt_ace_size); + size += nt_ace_size; + aces_size -= nt_ace_size; + ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); + num_aces++; + } + } + + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, nt_num_aces); + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static void set_mode_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, struct smb_fattr *fattr) +{ + struct smb_ace *pace, *pndace; + u32 num_aces = 0; + u16 size = 0, ace_size = 0; + uid_t uid; + const struct smb_sid *sid; + + pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + + if (fattr->cf_acls) { + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, num_aces); + goto out; + } + + /* owner RID */ + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (uid) + sid = &server_conf.domain_sid; + else + sid = &sid_unix_users; + ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0700); + pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + + /* Group RID */ + ace_size = fill_ace_for_sid(pace, &sid_unix_groups, + ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); + pace->sid.sub_auth[pace->sid.num_subauth++] = + cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 3; + + if (S_ISDIR(fattr->cf_mode)) { + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator owner */ + size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0700); + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator group */ + size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0070); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 5; + } + + /* other */ + size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0007); + +out: + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static int parse_sid(struct smb_sid *psid, char *end_of_acl) +{ + /* + * validate that we do not go past end of ACL - sid must be at least 8 + * bytes long (assuming no sub-auths - e.g. the null SID + */ + if (end_of_acl < (char *)psid + 8) { + pr_err("ACL too small to parse SID %p\n", psid); + return -EINVAL; + } + + return 0; +} + +/* Convert CIFS ACL to POSIX form */ +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr) +{ + int rc = 0; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_acl *dacl_ptr; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + int pntsd_type; + + if (!pntsd) + return -EIO; + + if (acl_len < sizeof(struct smb_ntsd)) + return -EINVAL; + + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + ksmbd_debug(SMB, + "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); + + pntsd_type = le16_to_cpu(pntsd->type); + if (!(pntsd_type & DACL_PRESENT)) { + ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); + return rc; + } + + pntsd->type = cpu_to_le16(DACL_PRESENT); + + if (pntsd->osidoffset) { + rc = parse_sid(owner_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); + return rc; + } + + rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + owner_sid_ptr = NULL; + } + } + + if (pntsd->gsidoffset) { + rc = parse_sid(group_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to gid\n", + __func__, rc); + return rc; + } + rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); + if (rc) { + pr_err("%s: Error %d mapping Group SID to gid\n", + __func__, rc); + group_sid_ptr = NULL; + } + } + + if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == + (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + if (pntsd_type & DACL_PROTECTED) + pntsd->type |= cpu_to_le16(DACL_PROTECTED); + + if (dacloffset) { + parse_dacl(user_ns, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); + } + + return 0; +} + +/* Convert permission bits from mode to equivalent CIFS ACL */ +int build_sec_desc(struct user_namespace *user_ns, + struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int ppntsd_size, int addition_info, __u32 *secdesclen, + struct smb_fattr *fattr) +{ + int rc = 0; + __u32 offset; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; + struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + uid_t uid; + gid_t gid; + unsigned int sid_type = SIDOWNER; + + nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!nowner_sid_ptr) + return -ENOMEM; + + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, nowner_sid_ptr); + + ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!ngroup_sid_ptr) { + kfree(nowner_sid_ptr); + return -ENOMEM; + } + + gid = from_kgid(&init_user_ns, fattr->cf_gid); + id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); + + offset = sizeof(struct smb_ntsd); + pntsd->sacloffset = 0; + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE); + if (ppntsd) + pntsd->type |= ppntsd->type; + + if (addition_info & OWNER_SECINFO) { + pntsd->osidoffset = cpu_to_le32(offset); + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); + offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); + } + + if (addition_info & GROUP_SECINFO) { + pntsd->gsidoffset = cpu_to_le32(offset); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); + offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); + } + + if (addition_info & DACL_SECINFO) { + pntsd->type |= cpu_to_le16(DACL_PRESENT); + dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); + dacl_ptr->revision = cpu_to_le16(2); + dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); + dacl_ptr->num_aces = 0; + + if (!ppntsd) { + set_mode_dacl(user_ns, dacl_ptr, fattr); + } else { + struct smb_acl *ppdacl_ptr; + unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); + int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset; + + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > ppntsd_size)) + goto out; + + ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset); + ppdacl_size = le16_to_cpu(ppdacl_ptr->size); + if (ppdacl_size > ntacl_size || + ppdacl_size < sizeof(struct smb_acl)) + goto out; + + set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, + ntacl_size - sizeof(struct smb_acl), + nowner_sid_ptr, ngroup_sid_ptr, + fattr); + } + pntsd->dacloffset = cpu_to_le32(offset); + offset += le16_to_cpu(dacl_ptr->size); + } + +out: + kfree(nowner_sid_ptr); + kfree(ngroup_sid_ptr); + *secdesclen = offset; + return rc; +} + +static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, + u8 flags, __le32 access_req) +{ + ace->type = type; + ace->flags = flags; + ace->access_req = access_req; + smb_copy_sid(&ace->sid, sid); + ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); +} + +int smb_inherit_dacl(struct ksmbd_conn *conn, + const struct path *path, + unsigned int uid, unsigned int gid) +{ + const struct smb_sid *psid, *creator = NULL; + struct smb_ace *parent_aces, *aces; + struct smb_acl *parent_pdacl; + struct smb_ntsd *parent_pntsd = NULL; + struct smb_sid owner_sid, group_sid; + struct dentry *parent = path->dentry->d_parent; + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; + int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; + char *aces_base; + bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); + + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + parent, &parent_pntsd); + if (pntsd_size <= 0) + return -ENOENT; + dacloffset = le32_to_cpu(parent_pntsd->dacloffset); + if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + acl_len = pntsd_size - dacloffset; + num_aces = le32_to_cpu(parent_pdacl->num_aces); + pntsd_type = le16_to_cpu(parent_pntsd->type); + pdacl_size = le16_to_cpu(parent_pdacl->size); + + if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); + if (!aces_base) { + rc = -ENOMEM; + goto free_parent_pntsd; + } + + aces = (struct smb_ace *)aces_base; + parent_aces = (struct smb_ace *)((char *)parent_pdacl + + sizeof(struct smb_acl)); + aces_size = acl_len - sizeof(struct smb_acl); + + if (pntsd_type & DACL_AUTO_INHERITED) + inherited_flags = INHERITED_ACE; + + for (i = 0; i < num_aces; i++) { + int pace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + pace_size = le16_to_cpu(parent_aces->size); + if (pace_size > aces_size) + break; + + aces_size -= pace_size; + + flags = parent_aces->flags; + if (!smb_inherit_flags(flags, is_dir)) + goto pass; + if (is_dir) { + flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); + if (!(flags & CONTAINER_INHERIT_ACE)) + flags |= INHERIT_ONLY_ACE; + if (flags & NO_PROPAGATE_INHERIT_ACE) + flags = 0; + } else { + flags = 0; + } + + if (!compare_sids(&creator_owner, &parent_aces->sid)) { + creator = &creator_owner; + id_to_sid(uid, SIDOWNER, &owner_sid); + psid = &owner_sid; + } else if (!compare_sids(&creator_group, &parent_aces->sid)) { + creator = &creator_group; + id_to_sid(gid, SIDUNIX_GROUP, &group_sid); + psid = &group_sid; + } else { + creator = NULL; + psid = &parent_aces->sid; + } + + if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { + smb_set_ace(aces, psid, parent_aces->type, inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + ace_cnt++; + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + flags |= INHERIT_ONLY_ACE; + psid = creator; + } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { + psid = &parent_aces->sid; + } + + smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + ace_cnt++; +pass: + parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size); + } + + if (nt_size > 0) { + struct smb_ntsd *pntsd; + struct smb_acl *pdacl; + struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; + int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + + if (parent_pntsd->osidoffset) { + powner_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->osidoffset)); + powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); + } + if (parent_pntsd->gsidoffset) { + pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->gsidoffset)); + pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); + } + + pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + + pgroup_sid_size + sizeof(struct smb_acl) + + nt_size, GFP_KERNEL); + if (!pntsd) { + rc = -ENOMEM; + goto free_aces_base; + } + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); + if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + pntsd_size = sizeof(struct smb_ntsd); + pntsd->osidoffset = parent_pntsd->osidoffset; + pntsd->gsidoffset = parent_pntsd->gsidoffset; + pntsd->dacloffset = parent_pntsd->dacloffset; + + if (pntsd->osidoffset) { + struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + memcpy(owner_sid, powner_sid, powner_sid_size); + pntsd_size += powner_sid_size; + } + + if (pntsd->gsidoffset) { + struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + memcpy(group_sid, pgroup_sid, pgroup_sid_size); + pntsd_size += pgroup_sid_size; + } + + if (pntsd->dacloffset) { + struct smb_ace *pace; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + pdacl->revision = cpu_to_le16(2); + pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); + pdacl->num_aces = cpu_to_le32(ace_cnt); + pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + memcpy(pace, aces_base, nt_size); + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, pntsd_size); + kfree(pntsd); + } + +free_aces_base: + kfree(aces_base); +free_parent_pntsd: + kfree(parent_pntsd); + return rc; +} + +bool smb_inherit_flags(int flags, bool is_dir) +{ + if (!is_dir) + return (flags & OBJECT_INHERIT_ACE) != 0; + + if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) + return true; + + if (flags & CONTAINER_INHERIT_ACE) + return true; + return false; +} + +int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct smb_ntsd *pntsd = NULL; + struct smb_acl *pdacl; + struct posix_acl *posix_acls; + int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset; + struct smb_sid sid; + int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); + struct smb_ace *ace; + int i, found = 0; + unsigned int access_bits = 0; + struct smb_ace *others_ace = NULL; + struct posix_acl_entry *pa_entry; + unsigned int sid_type = SIDOWNER; + unsigned short ace_size; + + ksmbd_debug(SMB, "check permission using windows acl\n"); + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + path->dentry, &pntsd); + if (pntsd_size <= 0 || !pntsd) + goto err_out; + + dacl_offset = le32_to_cpu(pntsd->dacloffset); + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > pntsd_size)) + goto err_out; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + acl_size = pntsd_size - dacl_offset; + pdacl_size = le16_to_cpu(pdacl->size); + + if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl)) + goto err_out; + + if (!pdacl->num_aces) { + if (!(pdacl_size - sizeof(struct smb_acl)) && + *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { + rc = -EACCES; + goto err_out; + } + goto err_out; + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; + granted |= le32_to_cpu(ace->access_req); + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + } + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, &sid); + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; + + if (!compare_sids(&sid, &ace->sid) || + !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { + found = 1; + break; + } + if (!compare_sids(&sid_everyone, &ace->sid)) + others_ace = ace; + + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + granted |= le32_to_cpu(ace->access_req); + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); + if (posix_acls && !found) { + unsigned int id = -1; + + pa_entry = posix_acls->a_entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { + if (pa_entry->e_tag == ACL_USER) + id = posix_acl_uid_translate(user_ns, pa_entry); + else if (pa_entry->e_tag == ACL_GROUP) + id = posix_acl_gid_translate(user_ns, pa_entry); + else + continue; + + if (id == uid) { + mode_to_access_flags(pa_entry->e_perm, + 0777, + &access_bits); + if (!access_bits) + access_bits = + SET_MINIMUM_RIGHTS; + posix_acl_release(posix_acls); + goto check_access_bits; + } + } + } + if (posix_acls) + posix_acl_release(posix_acls); + } + + if (!found) { + if (others_ace) { + ace = others_ace; + } else { + ksmbd_debug(SMB, "Can't find corresponding sid\n"); + rc = -EACCES; + goto err_out; + } + } + + switch (ace->type) { + case ACCESS_ALLOWED_ACE_TYPE: + access_bits = le32_to_cpu(ace->access_req); + break; + case ACCESS_DENIED_ACE_TYPE: + case ACCESS_DENIED_CALLBACK_ACE_TYPE: + access_bits = le32_to_cpu(~ace->access_req); + break; + } + +check_access_bits: + if (granted & + ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { + ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", + granted, le32_to_cpu(ace->access_req)); + rc = -EACCES; + goto err_out; + } + + *pdaccess = cpu_to_le32(granted); +err_out: + kfree(pntsd); + return rc; +} + +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check) +{ + int rc; + struct smb_fattr fattr = {{0}}; + struct inode *inode = d_inode(path->dentry); + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct iattr newattrs; + + fattr.cf_uid = INVALID_UID; + fattr.cf_gid = INVALID_GID; + fattr.cf_mode = inode->i_mode; + + rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); + if (rc) + goto out; + + newattrs.ia_valid = ATTR_CTIME; + if (!uid_eq(fattr.cf_uid, INVALID_UID)) { + newattrs.ia_valid |= ATTR_UID; + newattrs.ia_uid = fattr.cf_uid; + } + if (!gid_eq(fattr.cf_gid, INVALID_GID)) { + newattrs.ia_valid |= ATTR_GID; + newattrs.ia_gid = fattr.cf_gid; + } + newattrs.ia_valid |= ATTR_MODE; + newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + + ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); + /* Update posix acls */ + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { + rc = set_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS, fattr.cf_acls); + if (rc < 0) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) { + rc = set_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT, fattr.cf_dacls); + if (rc) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + } + + inode_lock(inode); + rc = notify_change(user_ns, path->dentry, &newattrs, NULL); + inode_unlock(inode); + if (rc) + goto out; + + /* Check it only calling from SD BUFFER context */ + if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) + goto out; + + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ + ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, ntsd_len); + } + +out: + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + mark_inode_dirty(inode); + return rc; +} + +void ksmbd_init_domain(u32 *sub_auth) +{ + int i; + + memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); + for (i = 0; i < 3; ++i) + server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); +} diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h new file mode 100644 index 000000000000..618f2e0236b3 --- /dev/null +++ b/fs/smb/server/smbacl.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ + +#ifndef _SMBACL_H +#define _SMBACL_H + +#include +#include +#include +#include + +#include "mgmt/tree_connect.h" + +#define NUM_AUTHS (6) /* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ + +/* + * ACE types - see MS-DTYP 2.4.4.1 + */ +enum { + ACCESS_ALLOWED, + ACCESS_DENIED, +}; + +/* + * Security ID types + */ +enum { + SIDOWNER = 1, + SIDGROUP, + SIDCREATOR_OWNER, + SIDCREATOR_GROUP, + SIDUNIX_USER, + SIDUNIX_GROUP, + SIDNFS_USER, + SIDNFS_GROUP, + SIDNFS_MODE, +}; + +/* Revision for ACLs */ +#define SD_REVISION 1 + +/* Control flags for Security Descriptor */ +#define OWNER_DEFAULTED 0x0001 +#define GROUP_DEFAULTED 0x0002 +#define DACL_PRESENT 0x0004 +#define DACL_DEFAULTED 0x0008 +#define SACL_PRESENT 0x0010 +#define SACL_DEFAULTED 0x0020 +#define DACL_TRUSTED 0x0040 +#define SERVER_SECURITY 0x0080 +#define DACL_AUTO_INHERIT_REQ 0x0100 +#define SACL_AUTO_INHERIT_REQ 0x0200 +#define DACL_AUTO_INHERITED 0x0400 +#define SACL_AUTO_INHERITED 0x0800 +#define DACL_PROTECTED 0x1000 +#define SACL_PROTECTED 0x2000 +#define RM_CONTROL_VALID 0x4000 +#define SELF_RELATIVE 0x8000 + +/* ACE types - see MS-DTYP 2.4.4.1 */ +#define ACCESS_ALLOWED_ACE_TYPE 0x00 +#define ACCESS_DENIED_ACE_TYPE 0x01 +#define SYSTEM_AUDIT_ACE_TYPE 0x02 +#define SYSTEM_ALARM_ACE_TYPE 0x03 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 + +/* ACE flags */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPAGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8: max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ + +#define DOMAIN_USER_RID_LE cpu_to_le32(513) + +struct ksmbd_conn; + +struct smb_ntsd { + __le16 revision; /* revision level */ + __le16 type; + __le32 osidoffset; + __le32 gsidoffset; + __le32 sacloffset; + __le32 dacloffset; +} __packed; + +struct smb_sid { + __u8 revision; /* revision level */ + __u8 num_subauth; + __u8 authority[NUM_AUTHS]; + __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +} __packed; + +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) + +struct smb_acl { + __le16 revision; /* revision level */ + __le16 size; + __le32 num_aces; +} __packed; + +struct smb_ace { + __u8 type; + __u8 flags; + __le16 size; + __le32 access_req; + struct smb_sid sid; /* ie UUID of user or group who gets these perms */ +} __packed; + +struct smb_fattr { + kuid_t cf_uid; + kgid_t cf_gid; + umode_t cf_mode; + __le32 daccess; + struct posix_acl *cf_acls; + struct posix_acl *cf_dacls; +}; + +struct posix_ace_state { + u32 allow; + u32 deny; +}; + +struct posix_user_ace_state { + union { + kuid_t uid; + kgid_t gid; + }; + struct posix_ace_state perms; +}; + +struct posix_ace_state_array { + int n; + struct posix_user_ace_state aces[]; +}; + +/* + * while processing the nfsv4 ace, this maintains the partial permissions + * calculated so far: + */ + +struct posix_acl_state { + struct posix_ace_state owner; + struct posix_ace_state group; + struct posix_ace_state other; + struct posix_ace_state everyone; + struct posix_ace_state mask; /* deny unused in this case */ + struct posix_ace_state_array *users; + struct posix_ace_state_array *groups; +}; + +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr); +int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, + __u32 *secdesclen, struct smb_fattr *fattr); +int init_acl_state(struct posix_acl_state *state, int cnt); +void free_acl_state(struct posix_acl_state *state); +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace); +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); +bool smb_inherit_flags(int flags, bool is_dir); +int smb_inherit_dacl(struct ksmbd_conn *conn, const struct path *path, + unsigned int uid, unsigned int gid); +int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid); +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check); +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); +void ksmbd_init_domain(u32 *sub_auth); + +static inline uid_t posix_acl_uid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ + vfsuid_t vfsuid; + + /* If this is an idmapped mount, apply the idmapping. */ + vfsuid = make_vfsuid(mnt_userns, &init_user_ns, pace->e_uid); + + /* Translate the kuid into a userspace id ksmbd would see. */ + return from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid)); +} + +static inline gid_t posix_acl_gid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ + vfsgid_t vfsgid; + + /* If this is an idmapped mount, apply the idmapping. */ + vfsgid = make_vfsgid(mnt_userns, &init_user_ns, pace->e_gid); + + /* Translate the kgid into a userspace id ksmbd would see. */ + return from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid)); +} + +#endif /* _SMBACL_H */ diff --git a/fs/smb/server/smbfsctl.h b/fs/smb/server/smbfsctl.h new file mode 100644 index 000000000000..b98418aae20c --- /dev/null +++ b/fs/smb/server/smbfsctl.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* IOCTL information */ +/* + * List of ioctl/fsctl function codes that are or could be useful in the + * future to remote clients like cifs or SMB2 client. There is probably + * a slightly larger set of fsctls that NTFS local filesystem could handle, + * including the seven below that we do not have struct definitions for. + * Even with protocol definitions for most of these now available, we still + * need to do some experimentation to identify which are practical to do + * remotely. Some of the following, such as the encryption/compression ones + * could be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + */ + +#ifndef __KSMBD_SMBFSCTL_H +#define __KSMBD_SMBFSCTL_H + +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 +#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 +#define FSCTL_LOCK_VOLUME 0x00090018 +#define FSCTL_UNLOCK_VOLUME 0x0009001C +#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ +#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ +#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ +#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ +/* Verify the next FSCTL number, we had it as 0x00090090 before */ +#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ +#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ +#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ +#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ +#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C +#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ +#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ +#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ +#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ +#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ +#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ +#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ +#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ +#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */ +#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ +#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ +#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ +#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ +#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ +#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ +#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ +#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ +#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ +#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ +#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ +#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 +#define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ +#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ +/* strange that the number for this op is not sequential with previous op */ +#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +#define FSCTL_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ +#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC +#define FSCTL_COPYCHUNK 0x001440F2 +#define FSCTL_COPYCHUNK_WRITE 0x001480F2 + +#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define IO_REPARSE_TAG_HSM 0xC0000004 +#define IO_REPARSE_TAG_SIS 0x80000007 + +/* WSL reparse tags */ +#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) +#endif /* __KSMBD_SMBFSCTL_H */ diff --git a/fs/smb/server/smbstatus.h b/fs/smb/server/smbstatus.h new file mode 100644 index 000000000000..108a8b6ed24a --- /dev/null +++ b/fs/smb/server/smbstatus.h @@ -0,0 +1,1822 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smb2status.h + * + * SMB2 Status code (network error) definitions + * Definitions are from MS-ERREF + * + * Copyright (c) International Business Machines Corp., 2009,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* + * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * SEV C N <-------Facility--------> <------Error Status Code------> + * + * C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) + +struct ntstatus { + /* Facility is the high 12 bits of the following field */ + __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ + __le32 Code; +}; + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) +#define STATUS_USER_APC cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) +#define STATUS_ALERTED cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) +#define STATUS_PENDING cpu_to_le32(0x00000103) +#define STATUS_REPARSE cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) +#define DBG_CONTINUE cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) +#define DBG_CONTROL_C cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ + cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ + cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) +#define STATUS_UNWIND cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) +#define STATUS_RETRY cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ + cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ + cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ + cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ + cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ + cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ + cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ + cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ + cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ + cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ + cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ + cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ + cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ + cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ + cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ + cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ + cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ + cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ + cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ + cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ + cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ + cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ + cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ + cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ + cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ + cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ + cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ + cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ + cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ + cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ + cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ + cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ + cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ + cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ + cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) + +#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) +#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c new file mode 100644 index 000000000000..40c721f9227e --- /dev/null +++ b/fs/smb/server/transport_ipc.c @@ -0,0 +1,884 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfs_cache.h" +#include "transport_ipc.h" +#include "server.h" +#include "smb_common.h" + +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/user_session.h" +#include "mgmt/tree_connect.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +#define IPC_WAIT_TIMEOUT (2 * HZ) + +#define IPC_MSG_HASH_BITS 3 +static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); +static DECLARE_RWSEM(ipc_msg_table_lock); +static DEFINE_MUTEX(startup_lock); + +static DEFINE_IDA(ipc_ida); + +static unsigned int ksmbd_tools_pid; + +static bool ksmbd_ipc_validate_version(struct genl_info *m) +{ + if (m->genlhdr->version != KSMBD_GENL_VERSION) { + pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n", + "Daemon and kernel module version mismatch", + m->genlhdr->version, + KSMBD_GENL_VERSION, + "User-space ksmbd should terminate"); + return false; + } + return true; +} + +struct ksmbd_ipc_msg { + unsigned int type; + unsigned int sz; + unsigned char payload[]; +}; + +struct ipc_msg_table_entry { + unsigned int handle; + unsigned int type; + wait_queue_head_t wait; + struct hlist_node ipc_table_hlist; + + void *response; +}; + +static struct delayed_work ipc_timer_work; + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info); +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); +static int ksmbd_ipc_heartbeat_request(void); + +static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { + [KSMBD_EVENT_UNSPEC] = { + .len = 0, + }, + [KSMBD_EVENT_HEARTBEAT_REQUEST] = { + .len = sizeof(struct ksmbd_heartbeat), + }, + [KSMBD_EVENT_STARTING_UP] = { + .len = sizeof(struct ksmbd_startup_request), + }, + [KSMBD_EVENT_SHUTTING_DOWN] = { + .len = sizeof(struct ksmbd_shutdown_request), + }, + [KSMBD_EVENT_LOGIN_REQUEST] = { + .len = sizeof(struct ksmbd_login_request), + }, + [KSMBD_EVENT_LOGIN_RESPONSE] = { + .len = sizeof(struct ksmbd_login_response), + }, + [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { + .len = sizeof(struct ksmbd_share_config_request), + }, + [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { + .len = sizeof(struct ksmbd_share_config_response), + }, + [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_connect_request), + }, + [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { + .len = sizeof(struct ksmbd_tree_connect_response), + }, + [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_disconnect_request), + }, + [KSMBD_EVENT_LOGOUT_REQUEST] = { + .len = sizeof(struct ksmbd_logout_request), + }, + [KSMBD_EVENT_RPC_REQUEST] = { + }, + [KSMBD_EVENT_RPC_RESPONSE] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { + }, +}; + +static struct genl_ops ksmbd_genl_ops[] = { + { + .cmd = KSMBD_EVENT_UNSPEC, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_HEARTBEAT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_STARTING_UP, + .doit = handle_startup_event, + }, + { + .cmd = KSMBD_EVENT_SHUTTING_DOWN, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGOUT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, + .doit = handle_generic_event, + }, +}; + +static struct genl_family ksmbd_genl_family = { + .name = KSMBD_GENL_NAME, + .version = KSMBD_GENL_VERSION, + .hdrsize = 0, + .maxattr = KSMBD_EVENT_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = ksmbd_genl_ops, + .n_ops = ARRAY_SIZE(ksmbd_genl_ops), + .resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, +}; + +static void ksmbd_nl_init_fixup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++) + ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT | + GENL_DONT_VALIDATE_DUMP; + + ksmbd_genl_family.policy = ksmbd_nl_policy; +} + +static int rpc_context_flags(struct ksmbd_session *sess) +{ + if (user_guest(sess->user)) + return KSMBD_RPC_RESTRICTED_CONTEXT; + return 0; +} + +static void ipc_update_last_active(void) +{ + if (server_conf.ipc_timeout) + server_conf.ipc_last_active = jiffies; +} + +static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) +{ + struct ksmbd_ipc_msg *msg; + size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); + + msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); + if (msg) + msg->sz = sz; + return msg; +} + +static void ipc_msg_free(struct ksmbd_ipc_msg *msg) +{ + kvfree(msg); +} + +static void ipc_msg_handle_free(int handle) +{ + if (handle >= 0) + ksmbd_release_id(&ipc_ida, handle); +} + +static int handle_response(int type, void *payload, size_t sz) +{ + unsigned int handle = *(unsigned int *)payload; + struct ipc_msg_table_entry *entry; + int ret = 0; + + ipc_update_last_active(); + down_read(&ipc_msg_table_lock); + hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { + if (handle != entry->handle) + continue; + + entry->response = NULL; + /* + * Response message type value should be equal to + * request message type + 1. + */ + if (entry->type + 1 != type) { + pr_err("Waiting for IPC type %d, got %d. Ignore.\n", + entry->type + 1, type); + } + + entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!entry->response) { + ret = -ENOMEM; + break; + } + + memcpy(entry->response, payload, sz); + wake_up_interruptible(&entry->wait); + ret = 0; + break; + } + up_read(&ipc_msg_table_lock); + + return ret; +} + +static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) +{ + int ret; + + ksmbd_set_fd_limit(req->file_max); + server_conf.flags = req->flags; + server_conf.signing = req->signing; + server_conf.tcp_port = req->tcp_port; + server_conf.ipc_timeout = req->ipc_timeout * HZ; + server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; + server_conf.share_fake_fscaps = req->share_fake_fscaps; + ksmbd_init_domain(req->sub_auth); + + if (req->smb2_max_read) + init_smb2_max_read_size(req->smb2_max_read); + if (req->smb2_max_write) + init_smb2_max_write_size(req->smb2_max_write); + if (req->smb2_max_trans) + init_smb2_max_trans_size(req->smb2_max_trans); + if (req->smb2_max_credits) + init_smb2_max_credits(req->smb2_max_credits); + if (req->smbd_max_io_size) + init_smbd_max_io_size(req->smbd_max_io_size); + + if (req->max_connections) + server_conf.max_connections = req->max_connections; + + ret = ksmbd_set_netbios_name(req->netbios_name); + ret |= ksmbd_set_server_string(req->server_string); + ret |= ksmbd_set_work_group(req->work_group); + ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), + req->ifc_list_sz); + if (ret) { + pr_err("Server configuration error: %s %s %s\n", + req->netbios_name, req->server_string, + req->work_group); + return ret; + } + + if (req->min_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->min_prot); + if (ret >= 0) + server_conf.min_protocol = ret; + } + if (req->max_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->max_prot); + if (ret >= 0) + server_conf.max_protocol = ret; + } + + if (server_conf.ipc_timeout) + schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout); + return 0; +} + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) +{ + int ret = 0; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[KSMBD_EVENT_STARTING_UP]) + return -EINVAL; + + mutex_lock(&startup_lock); + if (!ksmbd_server_configurable()) { + mutex_unlock(&startup_lock); + pr_err("Server reset is in progress, can't start daemon\n"); + return -EINVAL; + } + + if (ksmbd_tools_pid) { + if (ksmbd_ipc_heartbeat_request() == 0) { + ret = -EINVAL; + goto out; + } + + pr_err("Reconnect to a new user space daemon\n"); + } else { + struct ksmbd_startup_request *req; + + req = nla_data(info->attrs[info->genlhdr->cmd]); + ret = ipc_server_config_on_startup(req); + if (ret) + goto out; + server_queue_ctrl_init_work(); + } + + ksmbd_tools_pid = info->snd_portid; + ipc_update_last_active(); + +out: + mutex_unlock(&startup_lock); + return ret; +} + +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) +{ + pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); + return -EINVAL; +} + +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) +{ + void *payload; + int sz; + int type = info->genlhdr->cmd; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (type >= KSMBD_EVENT_MAX) { + WARN_ON(1); + return -EINVAL; + } + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[type]) + return -EINVAL; + + payload = nla_data(info->attrs[info->genlhdr->cmd]); + sz = nla_len(info->attrs[info->genlhdr->cmd]); + return handle_response(type, payload, sz); +} + +static int ipc_msg_send(struct ksmbd_ipc_msg *msg) +{ + struct genlmsghdr *nlh; + struct sk_buff *skb; + int ret = -EINVAL; + + if (!ksmbd_tools_pid) + return ret; + + skb = genlmsg_new(msg->sz, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type); + if (!nlh) + goto out; + + ret = nla_put(skb, msg->type, msg->sz, msg->payload); + if (ret) { + genlmsg_cancel(skb, nlh); + goto out; + } + + genlmsg_end(skb, nlh); + ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid); + if (!ret) + ipc_update_last_active(); + return ret; + +out: + nlmsg_free(skb); + return ret; +} + +static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) +{ + struct ipc_msg_table_entry entry; + int ret; + + if ((int)handle < 0) + return NULL; + + entry.type = msg->type; + entry.response = NULL; + init_waitqueue_head(&entry.wait); + + down_write(&ipc_msg_table_lock); + entry.handle = handle; + hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle); + up_write(&ipc_msg_table_lock); + + ret = ipc_msg_send(msg); + if (ret) + goto out; + + ret = wait_event_interruptible_timeout(entry.wait, + entry.response != NULL, + IPC_WAIT_TIMEOUT); +out: + down_write(&ipc_msg_table_lock); + hash_del(&entry.ipc_table_hlist); + up_write(&ipc_msg_table_lock); + return entry.response; +} + +static int ksmbd_ipc_heartbeat_request(void) +{ + struct ksmbd_ipc_msg *msg; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat)); + if (!msg) + return -EINVAL; + + msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST; + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_login_request *req; + struct ksmbd_login_response *resp; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_LOGIN_REQUEST; + req = (struct ksmbd_login_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_spnego_authen_request *req; + struct ksmbd_spnego_authen_response *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + + blob_len + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; + req = (struct ksmbd_spnego_authen_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->spnego_blob_len = blob_len; + memcpy(req->spnego_blob, spnego_blob, blob_len); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_connect_request *req; + struct ksmbd_tree_connect_response *resp; + + if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; + req = (struct ksmbd_tree_connect_request *)msg->payload; + + req->handle = ksmbd_acquire_id(&ipc_ida); + req->account_flags = sess->user->flags; + req->session_id = sess->id; + req->connect_id = tree_conn->id; + strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME); + snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr); + + if (peer_addr->sa_family == AF_INET6) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6; + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_disconnect_request *req; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; + req = (struct ksmbd_tree_disconnect_request *)msg->payload; + req->session_id = session_id; + req->connect_id = connect_id; + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +int ksmbd_ipc_logout_request(const char *account, int flags) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_logout_request *req; + int ret; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return -EINVAL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_LOGOUT_REQUEST; + req = (struct ksmbd_logout_request *)msg->payload; + req->account_flags = flags; + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_share_config_request *req; + struct ksmbd_share_config_response *resp; + + if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; + req = (struct ksmbd_share_config_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_OPEN_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_CLOSE_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_WRITE_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_READ_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_IOCTL_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->flags = rpc_context_flags(sess); + req->flags |= KSMBD_RPC_RAP_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +static int __ipc_heartbeat(void) +{ + unsigned long delta; + + if (!ksmbd_server_running()) + return 0; + + if (time_after(jiffies, server_conf.ipc_last_active)) { + delta = (jiffies - server_conf.ipc_last_active); + } else { + ipc_update_last_active(); + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + if (delta < server_conf.ipc_timeout) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout - delta); + return 0; + } + + if (ksmbd_ipc_heartbeat_request() == 0) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + mutex_lock(&startup_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + server_conf.ipc_last_active = 0; + ksmbd_tools_pid = 0; + pr_err("No IPC daemon response for %lus\n", delta / HZ); + mutex_unlock(&startup_lock); + return -EINVAL; +} + +static void ipc_timer_heartbeat(struct work_struct *w) +{ + if (__ipc_heartbeat()) + server_queue_ctrl_reset_work(); +} + +int ksmbd_ipc_id_alloc(void) +{ + return ksmbd_acquire_id(&ipc_ida); +} + +void ksmbd_rpc_id_free(int handle) +{ + ksmbd_release_id(&ipc_ida, handle); +} + +void ksmbd_ipc_release(void) +{ + cancel_delayed_work_sync(&ipc_timer_work); + genl_unregister_family(&ksmbd_genl_family); +} + +void ksmbd_ipc_soft_reset(void) +{ + mutex_lock(&startup_lock); + ksmbd_tools_pid = 0; + cancel_delayed_work_sync(&ipc_timer_work); + mutex_unlock(&startup_lock); +} + +int ksmbd_ipc_init(void) +{ + int ret = 0; + + ksmbd_nl_init_fixup(); + INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); + + ret = genl_register_family(&ksmbd_genl_family); + if (ret) { + pr_err("Failed to register KSMBD netlink interface %d\n", ret); + cancel_delayed_work_sync(&ipc_timer_work); + } + + return ret; +} diff --git a/fs/smb/server/transport_ipc.h b/fs/smb/server/transport_ipc.h new file mode 100644 index 000000000000..5e5b90a0c187 --- /dev/null +++ b/fs/smb/server/transport_ipc.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_IPC_H__ +#define __KSMBD_TRANSPORT_IPC_H__ + +#include + +#define KSMBD_IPC_MAX_PAYLOAD 4096 + +struct ksmbd_login_response * +ksmbd_ipc_login_request(const char *account); + +struct ksmbd_session; +struct ksmbd_share_config; +struct ksmbd_tree_connect; +struct sockaddr; + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr); +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id); +int ksmbd_ipc_logout_request(const char *account, int flags); +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name); +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); +int ksmbd_ipc_id_alloc(void); +void ksmbd_rpc_id_free(int handle); +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz); +void ksmbd_ipc_release(void); +void ksmbd_ipc_soft_reset(void); +int ksmbd_ipc_init(void); +#endif /* __KSMBD_TRANSPORT_IPC_H__ */ diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c new file mode 100644 index 000000000000..c06efc020bd9 --- /dev/null +++ b/fs/smb/server/transport_rdma.c @@ -0,0 +1,2273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + * + * Author(s): Long Li , + * Hyunchul Lee + */ + +#define SUBMOD_NAME "smb_direct" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "connection.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "transport_rdma.h" + +#define SMB_DIRECT_PORT_IWARP 5445 +#define SMB_DIRECT_PORT_INFINIBAND 445 + +#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) + +/* SMB_DIRECT negotiation timeout in seconds */ +#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 + +#define SMB_DIRECT_MAX_SEND_SGES 6 +#define SMB_DIRECT_MAX_RECV_SGES 1 + +/* + * Default maximum number of RDMA read/write outstanding on this connection + * This value is possibly decreased during QP creation on hardware limit + */ +#define SMB_DIRECT_CM_INITIATOR_DEPTH 8 + +/* Maximum number of retries on data transfer operations */ +#define SMB_DIRECT_CM_RETRY 6 +/* No need to retry on Receiver Not Ready since SMB_DIRECT manages credits */ +#define SMB_DIRECT_CM_RNR_RETRY 0 + +/* + * User configurable initial values per SMB_DIRECT transport connection + * as defined in [MS-SMBD] 3.1.1.1 + * Those may change after a SMB_DIRECT negotiation + */ + +/* Set 445 port to SMB Direct port by default */ +static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; + +/* The local peer's maximum number of credits to grant to the peer */ +static int smb_direct_receive_credit_max = 255; + +/* The remote peer's credit request of local peer */ +static int smb_direct_send_credit_target = 255; + +/* The maximum single message size can be sent to remote peer */ +static int smb_direct_max_send_size = 1364; + +/* The maximum fragmented upper-layer payload receive size supported */ +static int smb_direct_max_fragmented_recv_size = 1024 * 1024; + +/* The maximum single-message size which can be received */ +static int smb_direct_max_receive_size = 1364; + +static int smb_direct_max_read_write_size = SMBD_DEFAULT_IOSIZE; + +static LIST_HEAD(smb_direct_device_list); +static DEFINE_RWLOCK(smb_direct_device_lock); + +struct smb_direct_device { + struct ib_device *ib_dev; + struct list_head list; +}; + +static struct smb_direct_listener { + struct rdma_cm_id *cm_id; +} smb_direct_listener; + +static struct workqueue_struct *smb_direct_wq; + +enum smb_direct_status { + SMB_DIRECT_CS_NEW = 0, + SMB_DIRECT_CS_CONNECTED, + SMB_DIRECT_CS_DISCONNECTING, + SMB_DIRECT_CS_DISCONNECTED, +}; + +struct smb_direct_transport { + struct ksmbd_transport transport; + + enum smb_direct_status status; + bool full_packet_received; + wait_queue_head_t wait_status; + + struct rdma_cm_id *cm_id; + struct ib_cq *send_cq; + struct ib_cq *recv_cq; + struct ib_pd *pd; + struct ib_qp *qp; + + int max_send_size; + int max_recv_size; + int max_fragmented_send_size; + int max_fragmented_recv_size; + int max_rdma_rw_size; + + spinlock_t reassembly_queue_lock; + struct list_head reassembly_queue; + int reassembly_data_length; + int reassembly_queue_length; + int first_entry_offset; + wait_queue_head_t wait_reassembly_queue; + + spinlock_t receive_credit_lock; + int recv_credits; + int count_avail_recvmsg; + int recv_credit_max; + int recv_credit_target; + + spinlock_t recvmsg_queue_lock; + struct list_head recvmsg_queue; + + spinlock_t empty_recvmsg_queue_lock; + struct list_head empty_recvmsg_queue; + + int send_credit_target; + atomic_t send_credits; + spinlock_t lock_new_recv_credits; + int new_recv_credits; + int max_rw_credits; + int pages_per_rw_credit; + atomic_t rw_credits; + + wait_queue_head_t wait_send_credits; + wait_queue_head_t wait_rw_credits; + + mempool_t *sendmsg_mempool; + struct kmem_cache *sendmsg_cache; + mempool_t *recvmsg_mempool; + struct kmem_cache *recvmsg_cache; + + wait_queue_head_t wait_send_pending; + atomic_t send_pending; + + struct delayed_work post_recv_credits_work; + struct work_struct send_immediate_work; + struct work_struct disconnect_work; + + bool negotiation_requested; +}; + +#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) + +enum { + SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, + SMB_DIRECT_MSG_DATA_TRANSFER +}; + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; + +struct smb_direct_send_ctx { + struct list_head msg_list; + int wr_cnt; + bool need_invalidate_rkey; + unsigned int remote_key; +}; + +struct smb_direct_sendmsg { + struct smb_direct_transport *transport; + struct ib_send_wr wr; + struct list_head list; + int num_sge; + struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; + struct ib_cqe cqe; + u8 packet[]; +}; + +struct smb_direct_recvmsg { + struct smb_direct_transport *transport; + struct list_head list; + int type; + struct ib_sge sge; + struct ib_cqe cqe; + bool first_segment; + u8 packet[]; +}; + +struct smb_direct_rdma_rw_msg { + struct smb_direct_transport *t; + struct ib_cqe cqe; + int status; + struct completion *completion; + struct list_head list; + struct rdma_rw_ctx rw_ctx; + struct sg_table sgt; + struct scatterlist sg_list[]; +}; + +void init_smbd_max_io_size(unsigned int sz) +{ + sz = clamp_val(sz, SMBD_MIN_IOSIZE, SMBD_MAX_IOSIZE); + smb_direct_max_read_write_size = sz; +} + +unsigned int get_smbd_max_read_write_size(void) +{ + return smb_direct_max_read_write_size; +} + +static inline int get_buf_page_count(void *buf, int size) +{ + return DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - + (uintptr_t)buf / PAGE_SIZE; +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *transport); +static void smb_direct_post_recv_credits(struct work_struct *work); +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length); + +static inline struct smb_direct_transport * +smb_trans_direct_transfort(struct ksmbd_transport *t) +{ + return container_of(t, struct smb_direct_transport, transport); +} + +static inline void +*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) +{ + return (void *)recvmsg->packet; +} + +static inline bool is_receive_credit_post_required(int receive_credits, + int avail_recvmsg_count) +{ + return receive_credits <= (smb_direct_receive_credit_max >> 3) && + avail_recvmsg_count >= (receive_credits >> 2); +} + +static struct +smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->recvmsg_queue_lock); + if (!list_empty(&t->recvmsg_queue)) { + recvmsg = list_first_entry(&t->recvmsg_queue, + struct smb_direct_recvmsg, + list); + list_del(&recvmsg->list); + } + spin_unlock(&t->recvmsg_queue_lock); + return recvmsg; +} + +static void put_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->recvmsg_queue_lock); + list_add(&recvmsg->list, &t->recvmsg_queue); + spin_unlock(&t->recvmsg_queue_lock); +} + +static struct +smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->empty_recvmsg_queue_lock); + if (!list_empty(&t->empty_recvmsg_queue)) { + recvmsg = list_first_entry(&t->empty_recvmsg_queue, + struct smb_direct_recvmsg, list); + list_del(&recvmsg->list); + } + spin_unlock(&t->empty_recvmsg_queue_lock); + return recvmsg; +} + +static void put_empty_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->empty_recvmsg_queue_lock); + list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); + spin_unlock(&t->empty_recvmsg_queue_lock); +} + +static void enqueue_reassembly(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg, + int data_length) +{ + spin_lock(&t->reassembly_queue_lock); + list_add_tail(&recvmsg->list, &t->reassembly_queue); + t->reassembly_queue_length++; + /* + * Make sure reassembly_data_length is updated after list and + * reassembly_queue_length are updated. On the dequeue side + * reassembly_data_length is checked without a lock to determine + * if reassembly_queue_length and list is up to date + */ + virt_wmb(); + t->reassembly_data_length += data_length; + spin_unlock(&t->reassembly_queue_lock); +} + +static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) +{ + if (!list_empty(&t->reassembly_queue)) + return list_first_entry(&t->reassembly_queue, + struct smb_direct_recvmsg, list); + else + return NULL; +} + +static void smb_direct_disconnect_rdma_work(struct work_struct *work) +{ + struct smb_direct_transport *t = + container_of(work, struct smb_direct_transport, + disconnect_work); + + if (t->status == SMB_DIRECT_CS_CONNECTED) { + t->status = SMB_DIRECT_CS_DISCONNECTING; + rdma_disconnect(t->cm_id); + } +} + +static void +smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) +{ + if (t->status == SMB_DIRECT_CS_CONNECTED) + queue_work(smb_direct_wq, &t->disconnect_work); +} + +static void smb_direct_send_immediate_work(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, send_immediate_work); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return; + + smb_direct_post_send_data(t, NULL, NULL, 0, 0); +} + +static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) +{ + struct smb_direct_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + + t->cm_id = cm_id; + cm_id->context = t; + + t->status = SMB_DIRECT_CS_NEW; + init_waitqueue_head(&t->wait_status); + + spin_lock_init(&t->reassembly_queue_lock); + INIT_LIST_HEAD(&t->reassembly_queue); + t->reassembly_data_length = 0; + t->reassembly_queue_length = 0; + init_waitqueue_head(&t->wait_reassembly_queue); + init_waitqueue_head(&t->wait_send_credits); + init_waitqueue_head(&t->wait_rw_credits); + + spin_lock_init(&t->receive_credit_lock); + spin_lock_init(&t->recvmsg_queue_lock); + INIT_LIST_HEAD(&t->recvmsg_queue); + + spin_lock_init(&t->empty_recvmsg_queue_lock); + INIT_LIST_HEAD(&t->empty_recvmsg_queue); + + init_waitqueue_head(&t->wait_send_pending); + atomic_set(&t->send_pending, 0); + + spin_lock_init(&t->lock_new_recv_credits); + + INIT_DELAYED_WORK(&t->post_recv_credits_work, + smb_direct_post_recv_credits); + INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); + INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); + + conn = ksmbd_conn_alloc(); + if (!conn) + goto err; + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; + return t; +err: + kfree(t); + return NULL; +} + +static void free_transport(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + wake_up_interruptible(&t->wait_send_credits); + + ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + + cancel_work_sync(&t->disconnect_work); + cancel_delayed_work_sync(&t->post_recv_credits_work); + cancel_work_sync(&t->send_immediate_work); + + if (t->qp) { + ib_drain_qp(t->qp); + ib_mr_pool_destroy(t->qp, &t->qp->rdma_mrs); + ib_destroy_qp(t->qp); + } + + ksmbd_debug(RDMA, "drain the reassembly queue\n"); + do { + spin_lock(&t->reassembly_queue_lock); + recvmsg = get_first_reassembly(t); + if (recvmsg) { + list_del(&recvmsg->list); + spin_unlock(&t->reassembly_queue_lock); + put_recvmsg(t, recvmsg); + } else { + spin_unlock(&t->reassembly_queue_lock); + } + } while (recvmsg); + t->reassembly_data_length = 0; + + if (t->send_cq) + ib_free_cq(t->send_cq); + if (t->recv_cq) + ib_free_cq(t->recv_cq); + if (t->pd) + ib_dealloc_pd(t->pd); + if (t->cm_id) + rdma_destroy_id(t->cm_id); + + smb_direct_destroy_pools(t); + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t); +} + +static struct smb_direct_sendmsg +*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) +{ + struct smb_direct_sendmsg *msg; + + msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); + if (!msg) + return ERR_PTR(-ENOMEM); + msg->transport = t; + INIT_LIST_HEAD(&msg->list); + msg->num_sge = 0; + return msg; +} + +static void smb_direct_free_sendmsg(struct smb_direct_transport *t, + struct smb_direct_sendmsg *msg) +{ + int i; + + if (msg->num_sge > 0) { + ib_dma_unmap_single(t->cm_id->device, + msg->sge[0].addr, msg->sge[0].length, + DMA_TO_DEVICE); + for (i = 1; i < msg->num_sge; i++) + ib_dma_unmap_page(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + } + mempool_free(msg, t->sendmsg_mempool); +} + +static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) +{ + switch (recvmsg->type) { + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *req = + (struct smb_direct_data_transfer *)recvmsg->packet; + struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet + + le32_to_cpu(req->data_offset)); + ksmbd_debug(RDMA, + "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", + le16_to_cpu(req->credits_granted), + le16_to_cpu(req->credits_requested), + req->data_length, req->remaining_data_length, + hdr->ProtocolId, hdr->Command); + break; + } + case SMB_DIRECT_MSG_NEGOTIATE_REQ: { + struct smb_direct_negotiate_req *req = + (struct smb_direct_negotiate_req *)recvmsg->packet; + ksmbd_debug(RDMA, + "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", + le16_to_cpu(req->min_version), + le16_to_cpu(req->max_version), + le16_to_cpu(req->credits_requested), + le32_to_cpu(req->preferred_send_size), + le32_to_cpu(req->max_receive_size), + le32_to_cpu(req->max_fragmented_size)); + if (le16_to_cpu(req->min_version) > 0x0100 || + le16_to_cpu(req->max_version) < 0x0100) + return -EOPNOTSUPP; + if (le16_to_cpu(req->credits_requested) <= 0 || + le32_to_cpu(req->max_receive_size) <= 128 || + le32_to_cpu(req->max_fragmented_size) <= + 128 * 1024) + return -ECONNABORTED; + + break; + } + default: + return -EINVAL; + } + return 0; +} + +static void recv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_transport *t; + + recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); + t = recvmsg->transport; + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + if (wc->status != IB_WC_WR_FLUSH_ERR) { + pr_err("Recv error. status='%s (%d)' opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + put_empty_recvmsg(t, recvmsg); + return; + } + + ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + switch (recvmsg->type) { + case SMB_DIRECT_MSG_NEGOTIATE_REQ: + if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { + put_empty_recvmsg(t, recvmsg); + return; + } + t->negotiation_requested = true; + t->full_packet_received = true; + t->status = SMB_DIRECT_CS_CONNECTED; + enqueue_reassembly(t, recvmsg, 0); + wake_up_interruptible(&t->wait_status); + break; + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *data_transfer = + (struct smb_direct_data_transfer *)recvmsg->packet; + unsigned int data_length; + int avail_recvmsg_count, receive_credits; + + if (wc->byte_len < + offsetof(struct smb_direct_data_transfer, padding)) { + put_empty_recvmsg(t, recvmsg); + return; + } + + data_length = le32_to_cpu(data_transfer->data_length); + if (data_length) { + if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + + (u64)data_length) { + put_empty_recvmsg(t, recvmsg); + return; + } + + if (t->full_packet_received) + recvmsg->first_segment = true; + + if (le32_to_cpu(data_transfer->remaining_data_length)) + t->full_packet_received = false; + else + t->full_packet_received = true; + + enqueue_reassembly(t, recvmsg, (int)data_length); + wake_up_interruptible(&t->wait_reassembly_queue); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = t->count_avail_recvmsg; + spin_unlock(&t->receive_credit_lock); + } else { + put_empty_recvmsg(t, recvmsg); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = ++(t->count_avail_recvmsg); + spin_unlock(&t->receive_credit_lock); + } + + t->recv_credit_target = + le16_to_cpu(data_transfer->credits_requested); + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &t->send_credits); + + if (le16_to_cpu(data_transfer->flags) & + SMB_DIRECT_RESPONSE_REQUESTED) + queue_work(smb_direct_wq, &t->send_immediate_work); + + if (atomic_read(&t->send_credits) > 0) + wake_up_interruptible(&t->wait_send_credits); + + if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) + mod_delayed_work(smb_direct_wq, + &t->post_recv_credits_work, 0); + break; + } + default: + break; + } +} + +static int smb_direct_post_recv(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + struct ib_recv_wr wr; + int ret; + + recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, + recvmsg->packet, t->max_recv_size, + DMA_FROM_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); + if (ret) + return ret; + recvmsg->sge.length = t->max_recv_size; + recvmsg->sge.lkey = t->pd->local_dma_lkey; + recvmsg->cqe.done = recv_done; + + wr.wr_cqe = &recvmsg->cqe; + wr.next = NULL; + wr.sg_list = &recvmsg->sge; + wr.num_sge = 1; + + ret = ib_post_recv(t->qp, &wr, NULL); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + ib_dma_unmap_single(t->cm_id->device, + recvmsg->sge.addr, recvmsg->sge.length, + DMA_FROM_DEVICE); + smb_direct_disconnect_rdma_connection(t); + return ret; + } + return ret; +} + +static int smb_direct_read(struct ksmbd_transport *t, char *buf, + unsigned int size, int unused) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_data_transfer *data_transfer; + int to_copy, to_read, data_read, offset; + u32 data_length, remaining_data_length, data_offset; + int rc; + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + +again: + if (st->status != SMB_DIRECT_CS_CONNECTED) { + pr_err("disconnected\n"); + return -ENOTCONN; + } + + /* + * No need to hold the reassembly queue lock all the time as we are + * the only one reading from the front of the queue. The transport + * may add more entries to the back of the queue at the same time + */ + if (st->reassembly_data_length >= size) { + int queue_length; + int queue_removed = 0; + + /* + * Need to make sure reassembly_data_length is read before + * reading reassembly_queue_length and calling + * get_first_reassembly. This call is lock free + * as we never read at the end of the queue which are being + * updated in SOFTIRQ as more data is received + */ + virt_rmb(); + queue_length = st->reassembly_queue_length; + data_read = 0; + to_read = size; + offset = st->first_entry_offset; + while (data_read < size) { + recvmsg = get_first_reassembly(st); + data_transfer = smb_direct_recvmsg_payload(recvmsg); + data_length = le32_to_cpu(data_transfer->data_length); + remaining_data_length = + le32_to_cpu(data_transfer->remaining_data_length); + data_offset = le32_to_cpu(data_transfer->data_offset); + + /* + * The upper layer expects RFC1002 length at the + * beginning of the payload. Return it to indicate + * the total length of the packet. This minimize the + * change to upper layer packet processing logic. This + * will be eventually remove when an intermediate + * transport layer is added + */ + if (recvmsg->first_segment && size == 4) { + unsigned int rfc1002_len = + data_length + remaining_data_length; + *((__be32 *)buf) = cpu_to_be32(rfc1002_len); + data_read = 4; + recvmsg->first_segment = false; + ksmbd_debug(RDMA, + "returning rfc1002 length %d\n", + rfc1002_len); + goto read_rfc1002_done; + } + + to_copy = min_t(int, data_length - offset, to_read); + memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, + to_copy); + + /* move on to the next buffer? */ + if (to_copy == data_length - offset) { + queue_length--; + /* + * No need to lock if we are not at the + * end of the queue + */ + if (queue_length) { + list_del(&recvmsg->list); + } else { + spin_lock_irq(&st->reassembly_queue_lock); + list_del(&recvmsg->list); + spin_unlock_irq(&st->reassembly_queue_lock); + } + queue_removed++; + put_recvmsg(st, recvmsg); + offset = 0; + } else { + offset += to_copy; + } + + to_read -= to_copy; + data_read += to_copy; + } + + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_data_length -= data_read; + st->reassembly_queue_length -= queue_removed; + spin_unlock_irq(&st->reassembly_queue_lock); + + spin_lock(&st->receive_credit_lock); + st->count_avail_recvmsg += queue_removed; + if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { + spin_unlock(&st->receive_credit_lock); + mod_delayed_work(smb_direct_wq, + &st->post_recv_credits_work, 0); + } else { + spin_unlock(&st->receive_credit_lock); + } + + st->first_entry_offset = offset; + ksmbd_debug(RDMA, + "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, st->reassembly_data_length, + st->first_entry_offset); +read_rfc1002_done: + return data_read; + } + + ksmbd_debug(RDMA, "wait_event on more data\n"); + rc = wait_event_interruptible(st->wait_reassembly_queue, + st->reassembly_data_length >= size || + st->status != SMB_DIRECT_CS_CONNECTED); + if (rc) + return -EINTR; + + goto again; +} + +static void smb_direct_post_recv_credits(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, post_recv_credits_work.work); + struct smb_direct_recvmsg *recvmsg; + int receive_credits, credits = 0; + int ret; + int use_free = 1; + + spin_lock(&t->receive_credit_lock); + receive_credits = t->recv_credits; + spin_unlock(&t->receive_credit_lock); + + if (receive_credits < t->recv_credit_target) { + while (true) { + if (use_free) + recvmsg = get_free_recvmsg(t); + else + recvmsg = get_empty_recvmsg(t); + if (!recvmsg) { + if (use_free) { + use_free = 0; + continue; + } else { + break; + } + } + + recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; + recvmsg->first_segment = false; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + put_recvmsg(t, recvmsg); + break; + } + credits++; + } + } + + spin_lock(&t->receive_credit_lock); + t->recv_credits += credits; + t->count_avail_recvmsg -= credits; + spin_unlock(&t->receive_credit_lock); + + spin_lock(&t->lock_new_recv_credits); + t->new_recv_credits += credits; + spin_unlock(&t->lock_new_recv_credits); + + if (credits) + queue_work(smb_direct_wq, &t->send_immediate_work); +} + +static void send_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_sendmsg *sendmsg, *sibling; + struct smb_direct_transport *t; + struct list_head *pos, *prev, *end; + + sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); + t = sendmsg->transport; + + ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { + pr_err("Send error. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + + /* iterate and free the list of messages in reverse. the list's head + * is invalid. + */ + for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; + prev != end; pos = prev, prev = prev->prev) { + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); + } + + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); +} + +static int manage_credits_prior_sending(struct smb_direct_transport *t) +{ + int new_credits; + + spin_lock(&t->lock_new_recv_credits); + new_credits = t->new_recv_credits; + t->new_recv_credits = 0; + spin_unlock(&t->lock_new_recv_credits); + + return new_credits; +} + +static int smb_direct_post_send(struct smb_direct_transport *t, + struct ib_send_wr *wr) +{ + int ret; + + atomic_inc(&t->send_pending); + ret = ib_post_send(t->qp, wr, NULL); + if (ret) { + pr_err("failed to post send: %d\n", ret); + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + smb_direct_disconnect_rdma_connection(t); + } + return ret; +} + +static void smb_direct_send_ctx_init(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool need_invalidate_rkey, + unsigned int remote_key) +{ + INIT_LIST_HEAD(&send_ctx->msg_list); + send_ctx->wr_cnt = 0; + send_ctx->need_invalidate_rkey = need_invalidate_rkey; + send_ctx->remote_key = remote_key; +} + +static int smb_direct_flush_send_list(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool is_last) +{ + struct smb_direct_sendmsg *first, *last; + int ret; + + if (list_empty(&send_ctx->msg_list)) + return 0; + + first = list_first_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + + last->wr.send_flags = IB_SEND_SIGNALED; + last->wr.wr_cqe = &last->cqe; + if (is_last && send_ctx->need_invalidate_rkey) { + last->wr.opcode = IB_WR_SEND_WITH_INV; + last->wr.ex.invalidate_rkey = send_ctx->remote_key; + } + + ret = smb_direct_post_send(t, &first->wr); + if (!ret) { + smb_direct_send_ctx_init(t, send_ctx, + send_ctx->need_invalidate_rkey, + send_ctx->remote_key); + } else { + atomic_add(send_ctx->wr_cnt, &t->send_credits); + wake_up(&t->wait_send_credits); + list_for_each_entry_safe(first, last, &send_ctx->msg_list, + list) { + smb_direct_free_sendmsg(t, first); + } + } + return ret; +} + +static int wait_for_credits(struct smb_direct_transport *t, + wait_queue_head_t *waitq, atomic_t *total_credits, + int needed) +{ + int ret; + + do { + if (atomic_sub_return(needed, total_credits) >= 0) + return 0; + + atomic_add(needed, total_credits); + ret = wait_event_interruptible(*waitq, + atomic_read(total_credits) >= needed || + t->status != SMB_DIRECT_CS_CONNECTED); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + else if (ret < 0) + return ret; + } while (true); +} + +static int wait_for_send_credits(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx) +{ + int ret; + + if (send_ctx && + (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { + ret = smb_direct_flush_send_list(t, send_ctx, false); + if (ret) + return ret; + } + + return wait_for_credits(t, &t->wait_send_credits, &t->send_credits, 1); +} + +static int wait_for_rw_credits(struct smb_direct_transport *t, int credits) +{ + return wait_for_credits(t, &t->wait_rw_credits, &t->rw_credits, credits); +} + +static int calc_rw_credits(struct smb_direct_transport *t, + char *buf, unsigned int len) +{ + return DIV_ROUND_UP(get_buf_page_count(buf, len), + t->pages_per_rw_credit); +} + +static int smb_direct_create_header(struct smb_direct_transport *t, + int size, int remaining_data_length, + struct smb_direct_sendmsg **sendmsg_out) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_data_transfer *packet; + int header_length; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return PTR_ERR(sendmsg); + + /* Fill in the packet header */ + packet = (struct smb_direct_data_transfer *)sendmsg->packet; + packet->credits_requested = cpu_to_le16(t->send_credit_target); + packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + + packet->flags = 0; + packet->reserved = 0; + if (!size) + packet->data_offset = 0; + else + packet->data_offset = cpu_to_le32(24); + packet->data_length = cpu_to_le32(size); + packet->remaining_data_length = cpu_to_le32(remaining_data_length); + packet->padding = 0; + + ksmbd_debug(RDMA, + "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); + + /* Map the packet to DMA */ + header_length = sizeof(struct smb_direct_data_transfer); + /* If this is a packet without payload, don't send padding */ + if (!size) + header_length = + offsetof(struct smb_direct_data_transfer, padding); + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)packet, + header_length, + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = header_length; + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + *sendmsg_out = sendmsg; + return 0; +} + +static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nentries) +{ + bool high = is_vmalloc_addr(buf); + struct page *page; + int offset, len; + int i = 0; + + if (size <= 0 || nentries < get_buf_page_count(buf, size)) + return -EINVAL; + + offset = offset_in_page(buf); + buf -= offset; + while (size > 0) { + len = min_t(int, PAGE_SIZE - offset, size); + if (high) + page = vmalloc_to_page(buf); + else + page = kmap_to_page(buf); + + if (!sg_list) + return -EINVAL; + sg_set_page(sg_list, page, len, offset); + sg_list = sg_next(sg_list); + + buf += PAGE_SIZE; + size -= len; + offset = 0; + i++; + } + return i; +} + +static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, + struct scatterlist *sg_list, int nentries, + enum dma_data_direction dir) +{ + int npages; + + npages = get_sg_list(buf, size, sg_list, nentries); + if (npages < 0) + return -EINVAL; + return ib_dma_map_sg(device, sg_list, npages, dir); +} + +static int post_sendmsg(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct smb_direct_sendmsg *msg) +{ + int i; + + for (i = 0; i < msg->num_sge; i++) + ib_dma_sync_single_for_device(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + + msg->cqe.done = send_done; + msg->wr.opcode = IB_WR_SEND; + msg->wr.sg_list = &msg->sge[0]; + msg->wr.num_sge = msg->num_sge; + msg->wr.next = NULL; + + if (send_ctx) { + msg->wr.wr_cqe = NULL; + msg->wr.send_flags = 0; + if (!list_empty(&send_ctx->msg_list)) { + struct smb_direct_sendmsg *last; + + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last->wr.next = &msg->wr; + } + list_add_tail(&msg->list, &send_ctx->msg_list); + send_ctx->wr_cnt++; + return 0; + } + + msg->wr.wr_cqe = &msg->cqe; + msg->wr.send_flags = IB_SEND_SIGNALED; + return smb_direct_post_send(t, &msg->wr); +} + +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length) +{ + int i, j, ret; + struct smb_direct_sendmsg *msg; + int data_length; + struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; + + ret = wait_for_send_credits(t, send_ctx); + if (ret) + return ret; + + data_length = 0; + for (i = 0; i < niov; i++) + data_length += iov[i].iov_len; + + ret = smb_direct_create_header(t, data_length, remaining_data_length, + &msg); + if (ret) { + atomic_inc(&t->send_credits); + return ret; + } + + for (i = 0; i < niov; i++) { + struct ib_sge *sge; + int sg_cnt; + + sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); + sg_cnt = get_mapped_sg_list(t->cm_id->device, + iov[i].iov_base, iov[i].iov_len, + sg, SMB_DIRECT_MAX_SEND_SGES - 1, + DMA_TO_DEVICE); + if (sg_cnt <= 0) { + pr_err("failed to map buffer\n"); + ret = -ENOMEM; + goto err; + } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("buffer not fitted into sges\n"); + ret = -E2BIG; + ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, + DMA_TO_DEVICE); + goto err; + } + + for (j = 0; j < sg_cnt; j++) { + sge = &msg->sge[msg->num_sge]; + sge->addr = sg_dma_address(&sg[j]); + sge->length = sg_dma_len(&sg[j]); + sge->lkey = t->pd->local_dma_lkey; + msg->num_sge++; + } + } + + ret = post_sendmsg(t, send_ctx, msg); + if (ret) + goto err; + return 0; +err: + smb_direct_free_sendmsg(t, msg); + atomic_inc(&t->send_credits); + return ret; +} + +static int smb_direct_writev(struct ksmbd_transport *t, + struct kvec *iov, int niovs, int buflen, + bool need_invalidate, unsigned int remote_key) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + int remaining_data_length; + int start, i, j; + int max_iov_size = st->max_send_size - + sizeof(struct smb_direct_data_transfer); + int ret; + struct kvec vec; + struct smb_direct_send_ctx send_ctx; + + if (st->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + + //FIXME: skip RFC1002 header.. + buflen -= 4; + iov[0].iov_base += 4; + iov[0].iov_len -= 4; + + remaining_data_length = buflen; + ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); + + smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); + start = i = 0; + buflen = 0; + while (true) { + buflen += iov[i].iov_len; + if (buflen > max_iov_size) { + if (i > start) { + remaining_data_length -= + (buflen - iov[i].iov_len); + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + } else { + /* iov[start] is too big, break it */ + int nvec = (buflen + max_iov_size - 1) / + max_iov_size; + + for (j = 0; j < nvec; j++) { + vec.iov_base = + (char *)iov[start].iov_base + + j * max_iov_size; + vec.iov_len = + min_t(int, max_iov_size, + buflen - max_iov_size * j); + remaining_data_length -= vec.iov_len; + ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, + remaining_data_length); + if (ret) + goto done; + } + i++; + if (i == niovs) + break; + } + start = i; + buflen = 0; + } else { + i++; + if (i == niovs) { + /* send out all remaining vecs */ + remaining_data_length -= buflen; + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + break; + } + } + } + +done: + ret = smb_direct_flush_send_list(st, &send_ctx, true); + + /* + * As an optimization, we don't wait for individual I/O to finish + * before sending the next one. + * Send them all and wait for pending send count to get to 0 + * that means all the I/Os have been out and we are good to return + */ + + wait_event(st->wait_send_pending, + atomic_read(&st->send_pending) == 0); + return ret; +} + +static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t, + struct smb_direct_rdma_rw_msg *msg, + enum dma_data_direction dir) +{ + rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + msg->sgt.sgl, msg->sgt.nents, dir); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); +} + +static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, + enum dma_data_direction dir) +{ + struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, + struct smb_direct_rdma_rw_msg, cqe); + struct smb_direct_transport *t = msg->t; + + if (wc->status != IB_WC_SUCCESS) { + msg->status = -EIO; + pr_err("read/write error. opcode = %d, status = %s(%d)\n", + wc->opcode, ib_wc_status_msg(wc->status), wc->status); + if (wc->status != IB_WC_WR_FLUSH_ERR) + smb_direct_disconnect_rdma_connection(t); + } + + complete(msg->completion); +} + +static void read_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_FROM_DEVICE); +} + +static void write_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_TO_DEVICE); +} + +static int smb_direct_rdma_xmit(struct smb_direct_transport *t, + void *buf, int buf_len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len, + bool is_read) +{ + struct smb_direct_rdma_rw_msg *msg, *next_msg; + int i, ret; + DECLARE_COMPLETION_ONSTACK(completion); + struct ib_send_wr *first_wr; + LIST_HEAD(msg_list); + char *desc_buf; + int credits_needed; + unsigned int desc_buf_len; + size_t total_length = 0; + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + + /* calculate needed credits */ + credits_needed = 0; + desc_buf = buf; + for (i = 0; i < desc_len / sizeof(*desc); i++) { + desc_buf_len = le32_to_cpu(desc[i].length); + + credits_needed += calc_rw_credits(t, desc_buf, desc_buf_len); + desc_buf += desc_buf_len; + total_length += desc_buf_len; + if (desc_buf_len == 0 || total_length > buf_len || + total_length > t->max_rdma_rw_size) + return -EINVAL; + } + + ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n", + is_read ? "read" : "write", buf_len, credits_needed); + + ret = wait_for_rw_credits(t, credits_needed); + if (ret < 0) + return ret; + + /* build rdma_rw_ctx for each descriptor */ + desc_buf = buf; + for (i = 0; i < desc_len / sizeof(*desc); i++) { + msg = kzalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + + sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + desc_buf_len = le32_to_cpu(desc[i].length); + + msg->t = t; + msg->cqe.done = is_read ? read_done : write_done; + msg->completion = &completion; + + msg->sgt.sgl = &msg->sg_list[0]; + ret = sg_alloc_table_chained(&msg->sgt, + get_buf_page_count(desc_buf, desc_buf_len), + msg->sg_list, SG_CHUNK_SIZE); + if (ret) { + kfree(msg); + ret = -ENOMEM; + goto out; + } + + ret = get_sg_list(desc_buf, desc_buf_len, + msg->sgt.sgl, msg->sgt.orig_nents); + if (ret < 0) { + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + goto out; + } + + ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, + msg->sgt.sgl, + get_buf_page_count(desc_buf, desc_buf_len), + 0, + le64_to_cpu(desc[i].offset), + le32_to_cpu(desc[i].token), + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (ret < 0) { + pr_err("failed to init rdma_rw_ctx: %d\n", ret); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + goto out; + } + + list_add_tail(&msg->list, &msg_list); + desc_buf += desc_buf_len; + } + + /* concatenate work requests of rdma_rw_ctxs */ + first_wr = NULL; + list_for_each_entry_reverse(msg, &msg_list, list) { + first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, + &msg->cqe, first_wr); + } + + ret = ib_post_send(t->qp, first_wr, NULL); + if (ret) { + pr_err("failed to post send wr for RDMA R/W: %d\n", ret); + goto out; + } + + msg = list_last_entry(&msg_list, struct smb_direct_rdma_rw_msg, list); + wait_for_completion(&completion); + ret = msg->status; +out: + list_for_each_entry_safe(msg, next_msg, &msg_list, list) { + list_del(&msg->list); + smb_direct_free_rdma_rw_msg(t, msg, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + atomic_add(credits_needed, &t->rw_credits); + wake_up(&t->wait_rw_credits); + return ret; +} + +static int smb_direct_rdma_write(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + desc, desc_len, false); +} + +static int smb_direct_rdma_read(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + desc, desc_len, true); +} + +static void smb_direct_disconnect(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + + ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_work(&st->disconnect_work); + wait_event_interruptible(st->wait_status, + st->status == SMB_DIRECT_CS_DISCONNECTED); + free_transport(st); +} + +static void smb_direct_shutdown(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + + ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_work(&st->disconnect_work); +} + +static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct smb_direct_transport *t = cm_id->context; + + ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + + switch (event->event) { + case RDMA_CM_EVENT_ESTABLISHED: { + t->status = SMB_DIRECT_CS_CONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_DISCONNECTED: { + ib_drain_qp(t->qp); + + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + wake_up_interruptible(&t->wait_reassembly_queue); + wake_up(&t->wait_send_credits); + break; + } + case RDMA_CM_EVENT_CONNECT_ERROR: { + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + default: + pr_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), + event->event); + break; + } + return 0; +} + +static void smb_direct_qpair_handler(struct ib_event *event, void *context) +{ + struct smb_direct_transport *t = context; + + ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", + t->cm_id, ib_event_msg(event->event), event->event); + + switch (event->event) { + case IB_EVENT_CQ_ERR: + case IB_EVENT_QP_FATAL: + smb_direct_disconnect_rdma_connection(t); + break; + default: + break; + } +} + +static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, + int failed) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_negotiate_resp *resp; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return -ENOMEM; + + resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; + if (failed) { + memset(resp, 0, sizeof(*resp)); + resp->min_version = cpu_to_le16(0x0100); + resp->max_version = cpu_to_le16(0x0100); + resp->status = STATUS_NOT_SUPPORTED; + } else { + resp->status = STATUS_SUCCESS; + resp->min_version = SMB_DIRECT_VERSION_LE; + resp->max_version = SMB_DIRECT_VERSION_LE; + resp->negotiated_version = SMB_DIRECT_VERSION_LE; + resp->reserved = 0; + resp->credits_requested = + cpu_to_le16(t->send_credit_target); + resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); + resp->preferred_send_size = cpu_to_le32(t->max_send_size); + resp->max_receive_size = cpu_to_le32(t->max_recv_size); + resp->max_fragmented_size = + cpu_to_le32(t->max_fragmented_recv_size); + } + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)resp, sizeof(*resp), + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = sizeof(*resp); + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + ret = post_sendmsg(t, NULL, sendmsg); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + return 0; +} + +static int smb_direct_accept_client(struct smb_direct_transport *t) +{ + struct rdma_conn_param conn_param; + struct ib_port_immutable port_immutable; + u32 ird_ord_hdr[2]; + int ret; + + memset(&conn_param, 0, sizeof(conn_param)); + conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, + SMB_DIRECT_CM_INITIATOR_DEPTH); + conn_param.responder_resources = 0; + + t->cm_id->device->ops.get_port_immutable(t->cm_id->device, + t->cm_id->port_num, + &port_immutable); + if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { + ird_ord_hdr[0] = conn_param.responder_resources; + ird_ord_hdr[1] = 1; + conn_param.private_data = ird_ord_hdr; + conn_param.private_data_len = sizeof(ird_ord_hdr); + } else { + conn_param.private_data = NULL; + conn_param.private_data_len = 0; + } + conn_param.retry_count = SMB_DIRECT_CM_RETRY; + conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; + conn_param.flow_control = 0; + + ret = rdma_accept(t->cm_id, &conn_param); + if (ret) { + pr_err("error at rdma_accept: %d\n", ret); + return ret; + } + return 0; +} + +static int smb_direct_prepare_negotiation(struct smb_direct_transport *t) +{ + int ret; + struct smb_direct_recvmsg *recvmsg; + + recvmsg = get_free_recvmsg(t); + if (!recvmsg) + return -ENOMEM; + recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + goto out_err; + } + + t->negotiation_requested = false; + ret = smb_direct_accept_client(t); + if (ret) { + pr_err("Can't accept client\n"); + goto out_err; + } + + smb_direct_post_recv_credits(&t->post_recv_credits_work.work); + return 0; +out_err: + put_recvmsg(t, recvmsg); + return ret; +} + +static unsigned int smb_direct_get_max_fr_pages(struct smb_direct_transport *t) +{ + return min_t(unsigned int, + t->cm_id->device->attrs.max_fast_reg_page_list_len, + 256); +} + +static int smb_direct_init_params(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + struct ib_device *device = t->cm_id->device; + int max_send_sges, max_rw_wrs, max_send_wrs; + unsigned int max_sge_per_wr, wrs_per_credit; + + /* need 3 more sge. because a SMB_DIRECT header, SMB2 header, + * SMB2 response could be mapped. + */ + t->max_send_size = smb_direct_max_send_size; + max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 3; + if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("max_send_size %d is too large\n", t->max_send_size); + return -EINVAL; + } + + /* Calculate the number of work requests for RDMA R/W. + * The maximum number of pages which can be registered + * with one Memory region can be transferred with one + * R/W credit. And at least 4 work requests for each credit + * are needed for MR registration, RDMA R/W, local & remote + * MR invalidation. + */ + t->max_rdma_rw_size = smb_direct_max_read_write_size; + t->pages_per_rw_credit = smb_direct_get_max_fr_pages(t); + t->max_rw_credits = DIV_ROUND_UP(t->max_rdma_rw_size, + (t->pages_per_rw_credit - 1) * + PAGE_SIZE); + + max_sge_per_wr = min_t(unsigned int, device->attrs.max_send_sge, + device->attrs.max_sge_rd); + max_sge_per_wr = max_t(unsigned int, max_sge_per_wr, + max_send_sges); + wrs_per_credit = max_t(unsigned int, 4, + DIV_ROUND_UP(t->pages_per_rw_credit, + max_sge_per_wr) + 1); + max_rw_wrs = t->max_rw_credits * wrs_per_credit; + + max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; + if (max_send_wrs > device->attrs.max_cqe || + max_send_wrs > device->attrs.max_qp_wr) { + pr_err("consider lowering send_credit_target = %d\n", + smb_direct_send_credit_target); + pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (smb_direct_receive_credit_max > device->attrs.max_cqe || + smb_direct_receive_credit_max > device->attrs.max_qp_wr) { + pr_err("consider lowering receive_credit_max = %d\n", + smb_direct_receive_credit_max); + pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { + pr_err("warning: device max_recv_sge = %d too small\n", + device->attrs.max_recv_sge); + return -EINVAL; + } + + t->recv_credits = 0; + t->count_avail_recvmsg = 0; + + t->recv_credit_max = smb_direct_receive_credit_max; + t->recv_credit_target = 10; + t->new_recv_credits = 0; + + t->send_credit_target = smb_direct_send_credit_target; + atomic_set(&t->send_credits, 0); + atomic_set(&t->rw_credits, t->max_rw_credits); + + t->max_send_size = smb_direct_max_send_size; + t->max_recv_size = smb_direct_max_receive_size; + t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; + + cap->max_send_wr = max_send_wrs; + cap->max_recv_wr = t->recv_credit_max; + cap->max_send_sge = max_sge_per_wr; + cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; + cap->max_inline_data = 0; + cap->max_rdma_ctxs = t->max_rw_credits; + return 0; +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + while ((recvmsg = get_free_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + while ((recvmsg = get_empty_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + + mempool_destroy(t->recvmsg_mempool); + t->recvmsg_mempool = NULL; + + kmem_cache_destroy(t->recvmsg_cache); + t->recvmsg_cache = NULL; + + mempool_destroy(t->sendmsg_mempool); + t->sendmsg_mempool = NULL; + + kmem_cache_destroy(t->sendmsg_cache); + t->sendmsg_cache = NULL; +} + +static int smb_direct_create_pools(struct smb_direct_transport *t) +{ + char name[80]; + int i; + struct smb_direct_recvmsg *recvmsg; + + snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); + t->sendmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_sendmsg) + + sizeof(struct smb_direct_negotiate_resp), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->sendmsg_cache) + return -ENOMEM; + + t->sendmsg_mempool = mempool_create(t->send_credit_target, + mempool_alloc_slab, mempool_free_slab, + t->sendmsg_cache); + if (!t->sendmsg_mempool) + goto err; + + snprintf(name, sizeof(name), "smb_direct_resp_%p", t); + t->recvmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_recvmsg) + + t->max_recv_size, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->recvmsg_cache) + goto err; + + t->recvmsg_mempool = + mempool_create(t->recv_credit_max, mempool_alloc_slab, + mempool_free_slab, t->recvmsg_cache); + if (!t->recvmsg_mempool) + goto err; + + INIT_LIST_HEAD(&t->recvmsg_queue); + + for (i = 0; i < t->recv_credit_max; i++) { + recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); + if (!recvmsg) + goto err; + recvmsg->transport = t; + list_add(&recvmsg->list, &t->recvmsg_queue); + } + t->count_avail_recvmsg = t->recv_credit_max; + + return 0; +err: + smb_direct_destroy_pools(t); + return -ENOMEM; +} + +static int smb_direct_create_qpair(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + int ret; + struct ib_qp_init_attr qp_attr; + int pages_per_rw; + + t->pd = ib_alloc_pd(t->cm_id->device, 0); + if (IS_ERR(t->pd)) { + pr_err("Can't create RDMA PD\n"); + ret = PTR_ERR(t->pd); + t->pd = NULL; + return ret; + } + + t->send_cq = ib_alloc_cq(t->cm_id->device, t, + smb_direct_send_credit_target + cap->max_rdma_ctxs, + 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->send_cq)) { + pr_err("Can't create RDMA send CQ\n"); + ret = PTR_ERR(t->send_cq); + t->send_cq = NULL; + goto err; + } + + t->recv_cq = ib_alloc_cq(t->cm_id->device, t, + t->recv_credit_max, 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->recv_cq)) { + pr_err("Can't create RDMA recv CQ\n"); + ret = PTR_ERR(t->recv_cq); + t->recv_cq = NULL; + goto err; + } + + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.event_handler = smb_direct_qpair_handler; + qp_attr.qp_context = t; + qp_attr.cap = *cap; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + qp_attr.send_cq = t->send_cq; + qp_attr.recv_cq = t->recv_cq; + qp_attr.port_num = ~0; + + ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); + if (ret) { + pr_err("Can't create RDMA QP: %d\n", ret); + goto err; + } + + t->qp = t->cm_id->qp; + t->cm_id->event_handler = smb_direct_cm_handler; + + pages_per_rw = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; + if (pages_per_rw > t->cm_id->device->attrs.max_sgl_rd) { + ret = ib_mr_pool_init(t->qp, &t->qp->rdma_mrs, + t->max_rw_credits, IB_MR_TYPE_MEM_REG, + t->pages_per_rw_credit, 0); + if (ret) { + pr_err("failed to init mr pool count %d pages %d\n", + t->max_rw_credits, t->pages_per_rw_credit); + goto err; + } + } + + return 0; +err: + if (t->qp) { + ib_destroy_qp(t->qp); + t->qp = NULL; + } + if (t->recv_cq) { + ib_destroy_cq(t->recv_cq); + t->recv_cq = NULL; + } + if (t->send_cq) { + ib_destroy_cq(t->send_cq); + t->send_cq = NULL; + } + if (t->pd) { + ib_dealloc_pd(t->pd); + t->pd = NULL; + } + return ret; +} + +static int smb_direct_prepare(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_negotiate_req *req; + int ret; + + ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); + ret = wait_event_interruptible_timeout(st->wait_status, + st->negotiation_requested || + st->status == SMB_DIRECT_CS_DISCONNECTED, + SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); + if (ret <= 0 || st->status == SMB_DIRECT_CS_DISCONNECTED) + return ret < 0 ? ret : -ETIMEDOUT; + + recvmsg = get_first_reassembly(st); + if (!recvmsg) + return -ECONNABORTED; + + ret = smb_direct_check_recvmsg(recvmsg); + if (ret == -ECONNABORTED) + goto out; + + req = (struct smb_direct_negotiate_req *)recvmsg->packet; + st->max_recv_size = min_t(int, st->max_recv_size, + le32_to_cpu(req->preferred_send_size)); + st->max_send_size = min_t(int, st->max_send_size, + le32_to_cpu(req->max_receive_size)); + st->max_fragmented_send_size = + le32_to_cpu(req->max_fragmented_size); + st->max_fragmented_recv_size = + (st->recv_credit_max * st->max_recv_size) / 2; + + ret = smb_direct_send_negotiate_response(st, ret); +out: + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_queue_length--; + list_del(&recvmsg->list); + spin_unlock_irq(&st->reassembly_queue_lock); + put_recvmsg(st, recvmsg); + + return ret; +} + +static int smb_direct_connect(struct smb_direct_transport *st) +{ + int ret; + struct ib_qp_cap qp_cap; + + ret = smb_direct_init_params(st, &qp_cap); + if (ret) { + pr_err("Can't configure RDMA parameters\n"); + return ret; + } + + ret = smb_direct_create_pools(st); + if (ret) { + pr_err("Can't init RDMA pool: %d\n", ret); + return ret; + } + + ret = smb_direct_create_qpair(st, &qp_cap); + if (ret) { + pr_err("Can't accept RDMA client: %d\n", ret); + return ret; + } + + ret = smb_direct_prepare_negotiation(st); + if (ret) { + pr_err("Can't negotiate: %d\n", ret); + return ret; + } + return 0; +} + +static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) +{ + if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) + return false; + if (attrs->max_fast_reg_page_list_len == 0) + return false; + return true; +} + +static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) +{ + struct smb_direct_transport *t; + int ret; + + if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { + ksmbd_debug(RDMA, + "Fast Registration Work Requests is not supported. device capabilities=%llx\n", + new_cm_id->device->attrs.device_cap_flags); + return -EPROTONOSUPPORT; + } + + t = alloc_transport(new_cm_id); + if (!t) + return -ENOMEM; + + ret = smb_direct_connect(t); + if (ret) + goto out_err; + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:r%u", + smb_direct_port); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + ret = PTR_ERR(KSMBD_TRANS(t)->handler); + pr_err("Can't start thread\n"); + goto out_err; + } + + return 0; +out_err: + free_transport(t); + return ret; +} + +static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: { + int ret = smb_direct_handle_connect_request(cm_id); + + if (ret) { + pr_err("Can't create transport: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", + cm_id); + break; + } + default: + pr_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + break; + } + return 0; +} + +static int smb_direct_listen(int port) +{ + int ret; + struct rdma_cm_id *cm_id; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + }; + + cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, + &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + if (IS_ERR(cm_id)) { + pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); + return PTR_ERR(cm_id); + } + + ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); + if (ret) { + pr_err("Can't bind: %d\n", ret); + goto err; + } + + smb_direct_listener.cm_id = cm_id; + + ret = rdma_listen(cm_id, 10); + if (ret) { + pr_err("Can't listen: %d\n", ret); + goto err; + } + return 0; +err: + smb_direct_listener.cm_id = NULL; + rdma_destroy_id(cm_id); + return ret; +} + +static int smb_direct_ib_client_add(struct ib_device *ib_dev) +{ + struct smb_direct_device *smb_dev; + + /* Set 5445 port if device type is iWARP(No IB) */ + if (ib_dev->node_type != RDMA_NODE_IB_CA) + smb_direct_port = SMB_DIRECT_PORT_IWARP; + + if (!ib_dev->ops.get_netdev || + !rdma_frwr_is_supported(&ib_dev->attrs)) + return 0; + + smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); + if (!smb_dev) + return -ENOMEM; + smb_dev->ib_dev = ib_dev; + + write_lock(&smb_direct_device_lock); + list_add(&smb_dev->list, &smb_direct_device_list); + write_unlock(&smb_direct_device_lock); + + ksmbd_debug(RDMA, "ib device added: name %s\n", ib_dev->name); + return 0; +} + +static void smb_direct_ib_client_remove(struct ib_device *ib_dev, + void *client_data) +{ + struct smb_direct_device *smb_dev, *tmp; + + write_lock(&smb_direct_device_lock); + list_for_each_entry_safe(smb_dev, tmp, &smb_direct_device_list, list) { + if (smb_dev->ib_dev == ib_dev) { + list_del(&smb_dev->list); + kfree(smb_dev); + break; + } + } + write_unlock(&smb_direct_device_lock); +} + +static struct ib_client smb_direct_ib_client = { + .name = "ksmbd_smb_direct_ib", + .add = smb_direct_ib_client_add, + .remove = smb_direct_ib_client_remove, +}; + +int ksmbd_rdma_init(void) +{ + int ret; + + smb_direct_listener.cm_id = NULL; + + ret = ib_register_client(&smb_direct_ib_client); + if (ret) { + pr_err("failed to ib_register_client\n"); + return ret; + } + + /* When a client is running out of send credits, the credits are + * granted by the server's sending a packet using this queue. + * This avoids the situation that a clients cannot send packets + * for lack of credits + */ + smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", + WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + if (!smb_direct_wq) + return -ENOMEM; + + ret = smb_direct_listen(smb_direct_port); + if (ret) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + pr_err("Can't listen: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", + smb_direct_listener.cm_id); + return 0; +} + +void ksmbd_rdma_destroy(void) +{ + if (!smb_direct_listener.cm_id) + return; + + ib_unregister_client(&smb_direct_ib_client); + rdma_destroy_id(smb_direct_listener.cm_id); + + smb_direct_listener.cm_id = NULL; + + if (smb_direct_wq) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + } +} + +bool ksmbd_rdma_capable_netdev(struct net_device *netdev) +{ + struct smb_direct_device *smb_dev; + int i; + bool rdma_capable = false; + + read_lock(&smb_direct_device_lock); + list_for_each_entry(smb_dev, &smb_direct_device_list, list) { + for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { + struct net_device *ndev; + + ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, + i + 1); + if (!ndev) + continue; + + if (ndev == netdev) { + dev_put(ndev); + rdma_capable = true; + goto out; + } + dev_put(ndev); + } + } +out: + read_unlock(&smb_direct_device_lock); + + if (rdma_capable == false) { + struct ib_device *ibdev; + + ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); + if (ibdev) { + if (rdma_frwr_is_supported(&ibdev->attrs)) + rdma_capable = true; + ib_device_put(ibdev); + } + } + + return rdma_capable; +} + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { + .prepare = smb_direct_prepare, + .disconnect = smb_direct_disconnect, + .shutdown = smb_direct_shutdown, + .writev = smb_direct_writev, + .read = smb_direct_read, + .rdma_read = smb_direct_rdma_read, + .rdma_write = smb_direct_rdma_write, +}; diff --git a/fs/smb/server/transport_rdma.h b/fs/smb/server/transport_rdma.h new file mode 100644 index 000000000000..77aee4e5c9dc --- /dev/null +++ b/fs/smb/server/transport_rdma.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + */ + +#ifndef __KSMBD_TRANSPORT_RDMA_H__ +#define __KSMBD_TRANSPORT_RDMA_H__ + +#define SMBD_DEFAULT_IOSIZE (8 * 1024 * 1024) +#define SMBD_MIN_IOSIZE (512 * 1024) +#define SMBD_MAX_IOSIZE (16 * 1024 * 1024) + +/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ +struct smb_direct_negotiate_req { + __le16 min_version; + __le16 max_version; + __le16 reserved; + __le16 credits_requested; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ +struct smb_direct_negotiate_resp { + __le16 min_version; + __le16 max_version; + __le16 negotiated_version; + __le16 reserved; + __le16 credits_requested; + __le16 credits_granted; + __le32 status; + __le32 max_readwrite_size; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 + +/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ +struct smb_direct_data_transfer { + __le16 credits_requested; + __le16 credits_granted; + __le16 flags; + __le16 reserved; + __le32 remaining_data_length; + __le32 data_offset; + __le32 data_length; + __le32 padding; + __u8 buffer[]; +} __packed; + +#ifdef CONFIG_SMB_SERVER_SMBDIRECT +int ksmbd_rdma_init(void); +void ksmbd_rdma_destroy(void); +bool ksmbd_rdma_capable_netdev(struct net_device *netdev); +void init_smbd_max_io_size(unsigned int sz); +unsigned int get_smbd_max_read_write_size(void); +#else +static inline int ksmbd_rdma_init(void) { return 0; } +static inline int ksmbd_rdma_destroy(void) { return 0; } +static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } +static inline void init_smbd_max_io_size(unsigned int sz) { } +static inline unsigned int get_smbd_max_read_write_size(void) { return 0; } +#endif + +#endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c new file mode 100644 index 000000000000..eff7a1d793f0 --- /dev/null +++ b/fs/smb/server/transport_tcp.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "smb_common.h" +#include "server.h" +#include "auth.h" +#include "connection.h" +#include "transport_tcp.h" + +#define IFACE_STATE_DOWN BIT(0) +#define IFACE_STATE_CONFIGURED BIT(1) + +static atomic_t active_num_conn; + +struct interface { + struct task_struct *ksmbd_kthread; + struct socket *ksmbd_socket; + struct list_head entry; + char *name; + struct mutex sock_release_lock; + int state; +}; + +static LIST_HEAD(iface_list); + +static int bind_additional_ifaces; + +struct tcp_transport { + struct ksmbd_transport transport; + struct socket *sock; + struct kvec *iov; + unsigned int nr_iov; +}; + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; + +static void tcp_stop_kthread(struct task_struct *kthread); +static struct interface *alloc_iface(char *ifname); + +#define KSMBD_TRANS(t) (&(t)->transport) +#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ + struct tcp_transport, transport)) + +static inline void ksmbd_tcp_nodelay(struct socket *sock) +{ + tcp_sock_set_nodelay(sock->sk); +} + +static inline void ksmbd_tcp_reuseaddr(struct socket *sock) +{ + sock_set_reuseaddr(sock->sk); +} + +static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) +{ + lock_sock(sock->sk); + if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) + sock->sk->sk_rcvtimeo = secs * HZ; + else + sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; + release_sock(sock->sk); +} + +static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) +{ + sock_set_sndtimeo(sock->sk, secs); +} + +static struct tcp_transport *alloc_transport(struct socket *client_sk) +{ + struct tcp_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + t->sock = client_sk; + + conn = ksmbd_conn_alloc(); + if (!conn) { + kfree(t); + return NULL; + } + + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; + return t; +} + +static void free_transport(struct tcp_transport *t) +{ + kernel_sock_shutdown(t->sock, SHUT_RDWR); + sock_release(t->sock); + t->sock = NULL; + + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t->iov); + kfree(t); +} + +/** + * kvec_array_init() - initialize a IO vector segment + * @new: IO vector to be initialized + * @iov: base IO vector + * @nr_segs: number of segments in base iov + * @bytes: total iovec length so far for read + * + * Return: Number of IO segments + */ +static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, + unsigned int nr_segs, size_t bytes) +{ + size_t base = 0; + + while (bytes || !iov->iov_len) { + int copy = min(bytes, iov->iov_len); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + nr_segs--; + base = 0; + } + } + + memcpy(new, iov, sizeof(*iov) * nr_segs); + new->iov_base += base; + new->iov_len -= base; + return nr_segs; +} + +/** + * get_conn_iovec() - get connection iovec for reading from socket + * @t: TCP transport instance + * @nr_segs: number of segments in iov + * + * Return: return existing or newly allocate iovec + */ +static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs) +{ + struct kvec *new_iov; + + if (t->iov && nr_segs <= t->nr_iov) + return t->iov; + + /* not big enough -- allocate a new one and release the old */ + new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); + if (new_iov) { + kfree(t->iov); + t->iov = new_iov; + t->nr_iov = nr_segs; + } + return new_iov; +} + +static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)sa)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + } + return 0; +} + +/** + * ksmbd_tcp_new_connection() - create a new tcp session on mount + * @client_sk: socket associated with new connection + * + * whenever a new connection is requested, create a conn thread + * (session thread) to handle new incoming smb requests from the connection + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_tcp_new_connection(struct socket *client_sk) +{ + struct sockaddr *csin; + int rc = 0; + struct tcp_transport *t; + + t = alloc_transport(client_sk); + if (!t) { + sock_release(client_sk); + return -ENOMEM; + } + + csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); + if (kernel_getpeername(client_sk, csin) < 0) { + pr_err("client ip resolution failed\n"); + rc = -EINVAL; + goto out_error; + } + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, + "ksmbd:%u", + ksmbd_tcp_get_port(csin)); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + pr_err("cannot start conn thread\n"); + rc = PTR_ERR(KSMBD_TRANS(t)->handler); + free_transport(t); + } + return rc; + +out_error: + free_transport(t); + return rc; +} + +/** + * ksmbd_kthread_fn() - listen to new SMB connections and callback server + * @p: arguments to forker thread + * + * Return: 0 on success, error number otherwise + */ +static int ksmbd_kthread_fn(void *p) +{ + struct socket *client_sk = NULL; + struct interface *iface = (struct interface *)p; + int ret; + + while (!kthread_should_stop()) { + mutex_lock(&iface->sock_release_lock); + if (!iface->ksmbd_socket) { + mutex_unlock(&iface->sock_release_lock); + break; + } + ret = kernel_accept(iface->ksmbd_socket, &client_sk, + SOCK_NONBLOCK); + mutex_unlock(&iface->sock_release_lock); + if (ret) { + if (ret == -EAGAIN) + /* check for new connections every 100 msecs */ + schedule_timeout_interruptible(HZ / 10); + continue; + } + + if (server_conf.max_connections && + atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { + pr_info_ratelimited("Limit the maximum number of connections(%u)\n", + atomic_read(&active_num_conn)); + atomic_dec(&active_num_conn); + sock_release(client_sk); + continue; + } + + ksmbd_debug(CONN, "connect success: accepted new connection\n"); + client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ksmbd_tcp_new_connection(client_sk); + } + + ksmbd_debug(CONN, "releasing socket\n"); + return 0; +} + +/** + * ksmbd_tcp_run_kthread() - start forker thread + * @iface: pointer to struct interface + * + * start forker thread(ksmbd/0) at module init time to listen + * on port 445 for new SMB connection requests. It creates per connection + * server threads(ksmbd/x) + * + * Return: 0 on success or error number + */ +static int ksmbd_tcp_run_kthread(struct interface *iface) +{ + int rc; + struct task_struct *kthread; + + kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", + iface->name); + if (IS_ERR(kthread)) { + rc = PTR_ERR(kthread); + return rc; + } + iface->ksmbd_kthread = kthread; + + return 0; +} + +/** + * ksmbd_tcp_readv() - read data from socket in given iovec + * @t: TCP transport instance + * @iov_orig: base IO vector + * @nr_segs: number of segments in base iov + * @to_read: number of bytes to read from socket + * @max_retries: maximum retry count + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, + unsigned int nr_segs, unsigned int to_read, + int max_retries) +{ + int length = 0; + int total_read; + unsigned int segs; + struct msghdr ksmbd_msg; + struct kvec *iov; + struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn; + + iov = get_conn_iovec(t, nr_segs); + if (!iov) + return -ENOMEM; + + ksmbd_msg.msg_control = NULL; + ksmbd_msg.msg_controllen = 0; + + for (total_read = 0; to_read; total_read += length, to_read -= length) { + try_to_freeze(); + + if (!ksmbd_conn_alive(conn)) { + total_read = -ESHUTDOWN; + break; + } + segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); + + length = kernel_recvmsg(t->sock, &ksmbd_msg, + iov, segs, to_read, 0); + + if (length == -EINTR) { + total_read = -ESHUTDOWN; + break; + } else if (ksmbd_conn_need_reconnect(conn)) { + total_read = -EAGAIN; + break; + } else if (length == -ERESTARTSYS || length == -EAGAIN) { + /* + * If max_retries is negative, Allow unlimited + * retries to keep connection with inactive sessions. + */ + if (max_retries == 0) { + total_read = length; + break; + } else if (max_retries > 0) { + max_retries--; + } + + usleep_range(1000, 2000); + length = 0; + continue; + } else if (length <= 0) { + total_read = length; + break; + } + } + return total_read; +} + +/** + * ksmbd_tcp_read() - read data from socket in given buffer + * @t: TCP transport instance + * @buf: buffer to store read data from socket + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, + unsigned int to_read, int max_retries) +{ + struct kvec iov; + + iov.iov_base = buf; + iov.iov_len = to_read; + + return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read, max_retries); +} + +static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, + int nvecs, int size, bool need_invalidate, + unsigned int remote_key) + +{ + struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; + + return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); +} + +static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) +{ + free_transport(TCP_TRANS(t)); + if (server_conf.max_connections) + atomic_dec(&active_num_conn); +} + +static void tcp_destroy_socket(struct socket *ksmbd_socket) +{ + int ret; + + if (!ksmbd_socket) + return; + + /* set zero to timeout */ + ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); + ksmbd_tcp_snd_timeout(ksmbd_socket, 0); + + ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); + if (ret) + pr_err("Failed to shutdown socket: %d\n", ret); + sock_release(ksmbd_socket); +} + +/** + * create_socket - create socket for ksmbd/0 + * + * Return: 0 on success, error number otherwise + */ +static int create_socket(struct interface *iface) +{ + int ret; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct socket *ksmbd_socket; + bool ipv4 = false; + + ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); + if (ret) { + if (ret != -EAFNOSUPPORT) + pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret); + ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, + &ksmbd_socket); + if (ret) { + pr_err("Can't create socket for ipv4: %d\n", ret); + goto out_clear; + } + + sin.sin_family = PF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(server_conf.tcp_port); + ipv4 = true; + } else { + sin6.sin6_family = PF_INET6; + sin6.sin6_addr = in6addr_any; + sin6.sin6_port = htons(server_conf.tcp_port); + } + + ksmbd_tcp_nodelay(ksmbd_socket); + ksmbd_tcp_reuseaddr(ksmbd_socket); + + ret = sock_setsockopt(ksmbd_socket, + SOL_SOCKET, + SO_BINDTODEVICE, + KERNEL_SOCKPTR(iface->name), + strlen(iface->name)); + if (ret != -ENODEV && ret < 0) { + pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret); + goto out_error; + } + + if (ipv4) + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, + sizeof(sin)); + else + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, + sizeof(sin6)); + if (ret) { + pr_err("Failed to bind socket: %d\n", ret); + goto out_error; + } + + ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); + if (ret) { + pr_err("Port listen() error: %d\n", ret); + goto out_error; + } + + iface->ksmbd_socket = ksmbd_socket; + ret = ksmbd_tcp_run_kthread(iface); + if (ret) { + pr_err("Can't start ksmbd main kthread: %d\n", ret); + goto out_error; + } + iface->state = IFACE_STATE_CONFIGURED; + + return 0; + +out_error: + tcp_destroy_socket(ksmbd_socket); +out_clear: + iface->ksmbd_socket = NULL; + return ret; +} + +static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct interface *iface; + int ret, found = 0; + + switch (event) { + case NETDEV_UP: + if (netif_is_bridge_port(netdev)) + return NOTIFY_OK; + + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name)) { + found = 1; + if (iface->state != IFACE_STATE_DOWN) + break; + ret = create_socket(iface); + if (ret) + return NOTIFY_OK; + break; + } + } + if (!found && bind_additional_ifaces) { + iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + if (!iface) + return NOTIFY_OK; + ret = create_socket(iface); + if (ret) + break; + } + break; + case NETDEV_DOWN: + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name) && + iface->state == IFACE_STATE_CONFIGURED) { + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + mutex_lock(&iface->sock_release_lock); + tcp_destroy_socket(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + mutex_unlock(&iface->sock_release_lock); + + iface->state = IFACE_STATE_DOWN; + break; + } + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block ksmbd_netdev_notifier = { + .notifier_call = ksmbd_netdev_event, +}; + +int ksmbd_tcp_init(void) +{ + register_netdevice_notifier(&ksmbd_netdev_notifier); + + return 0; +} + +static void tcp_stop_kthread(struct task_struct *kthread) +{ + int ret; + + if (!kthread) + return; + + ret = kthread_stop(kthread); + if (ret) + pr_err("failed to stop forker thread\n"); +} + +void ksmbd_tcp_destroy(void) +{ + struct interface *iface, *tmp; + + unregister_netdevice_notifier(&ksmbd_netdev_notifier); + + list_for_each_entry_safe(iface, tmp, &iface_list, entry) { + list_del(&iface->entry); + kfree(iface->name); + kfree(iface); + } +} + +static struct interface *alloc_iface(char *ifname) +{ + struct interface *iface; + + if (!ifname) + return NULL; + + iface = kzalloc(sizeof(struct interface), GFP_KERNEL); + if (!iface) { + kfree(ifname); + return NULL; + } + + iface->name = ifname; + iface->state = IFACE_STATE_DOWN; + list_add(&iface->entry, &iface_list); + mutex_init(&iface->sock_release_lock); + return iface; +} + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) +{ + int sz = 0; + + if (!ifc_list_sz) { + struct net_device *netdev; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + if (netif_is_bridge_port(netdev)) + continue; + if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) + return -ENOMEM; + } + rtnl_unlock(); + bind_additional_ifaces = 1; + return 0; + } + + while (ifc_list_sz > 0) { + if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + return -ENOMEM; + + sz = strlen(ifc_list); + if (!sz) + break; + + ifc_list += sz + 1; + ifc_list_sz -= (sz + 1); + } + + bind_additional_ifaces = 0; + + return 0; +} + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { + .read = ksmbd_tcp_read, + .writev = ksmbd_tcp_writev, + .disconnect = ksmbd_tcp_disconnect, +}; diff --git a/fs/smb/server/transport_tcp.h b/fs/smb/server/transport_tcp.h new file mode 100644 index 000000000000..e338bebe322f --- /dev/null +++ b/fs/smb/server/transport_tcp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_TCP_H__ +#define __KSMBD_TRANSPORT_TCP_H__ + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); +int ksmbd_tcp_init(void); +void ksmbd_tcp_destroy(void); + +#endif /* __KSMBD_TRANSPORT_TCP_H__ */ diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c new file mode 100644 index 000000000000..a0db699ddafd --- /dev/null +++ b/fs/smb/server/unicode.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * Modified by Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ +#include +#include +#include +#include "glob.h" +#include "unicode.h" +#include "uniupr.h" +#include "smb_common.h" + +/* + * smb_utf16_bytes() - how long will a string be after conversion? + * @from: pointer to input string + * @maxbytes: don't go past this many bytes of input string + * @codepage: destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + * + * Return: string length after conversion + */ +static int smb_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); + if (charlen > 0) + outlen += charlen; + else + outlen++; + } + + return outlen; +} + +/* + * cifs_mapchar() - convert a host-endian char to proper char in codepage + * @target: where converted character should be copied + * @src_char: 2 byte host-endian source character + * @cp: codepage to which character should be converted + * @mapchar: should character be mapped according to mapchars mount option? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + * + * Return: string length after conversion + */ +static int +cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + bool mapchar) +{ + int len = 1; + + if (!mapchar) + goto cp_convert; + + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (src_char) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERISK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + goto cp_convert; + } + +out: + return len; + +cp_convert: + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); + if (len <= 0) { + *target = '?'; + len = 1; + } + + goto out; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char *ch) +{ + /* check for control chars, wildcards etc. */ + if (!(*ch & 0x80) && + (*ch <= 0x1f || + *ch == '?' || *ch == '"' || *ch == '<' || + *ch == '>' || *ch == '|')) + return 0; + + return 1; +} + +/* + * smb_from_utf16() - convert utf16le string to local charset + * @to: destination buffer + * @from: source buffer + * @tolen: destination buffer size (in bytes) + * @fromlen: source buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert a little-endian utf16le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UTF16-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + * + * Return: string length after conversion + */ +static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); + outlen += charlen; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + +/* + * smb_strtoUTF16() - Convert character string to unicode string + * @to: destination buffer + * @from: source buffer + * @len: destination buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * + * Return: string length after conversion + */ +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; /* needed to quiet sparse */ + + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + + for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + if (charlen < 1) { + /* A question mark */ + wchar_to = 0x003f; + charlen = 1; + } + put_unaligned_le16(wchar_to, &to[i]); + } + +success: + put_unaligned_le16(0, &to[i]); + return i; +} + +/* + * smb_strndup_from_utf16() - copy a string from wire format to the local + * codepage + * @src: source string + * @maxlen: don't walk past this many bytes in the source string + * @is_unicode: is this a unicode string? + * @codepage: destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + * + * Return: destination string buffer or error ptr + */ +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage) +{ + int len, ret; + char *dst; + + if (is_unicode) { + len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, + false); + if (ret < 0) { + kfree(dst); + return ERR_PTR(-EINVAL); + } + } else { + len = strnlen(src, maxlen); + len++; + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + strscpy(dst, src, len); + } + + return dst; +} + +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +/* + * smbConvertToUTF16() - convert string from local charset to utf16 + * @target: destination buffer + * @source: source buffer + * @srclen: source buffer size (in bytes) + * @cp: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + * + * Return: char length after conversion + */ +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars) +{ + int i, j, charlen; + char src_char; + __le16 dst_char; + wchar_t tmp; + + if (!mapchars) + return smb_strtoUTF16(target, source, srclen, cp); + + for (i = 0, j = 0; i < srclen; j++) { + src_char = source[i]; + charlen = 1; + switch (src_char) { + case 0: + put_unaligned(0, &target[j]); + return j; + case ':': + dst_char = cpu_to_le16(UNI_COLON); + break; + case '*': + dst_char = cpu_to_le16(UNI_ASTERISK); + break; + case '?': + dst_char = cpu_to_le16(UNI_QUESTION); + break; + case '<': + dst_char = cpu_to_le16(UNI_LESSTHAN); + break; + case '>': + dst_char = cpu_to_le16(UNI_GRTRTHAN); + break; + case '|': + dst_char = cpu_to_le16(UNI_PIPE); + break; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + default: + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen < 1) { + dst_char = cpu_to_le16(0x003f); + charlen = 1; + } + } + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; + put_unaligned(dst_char, &target[j]); + } + + return j; +} diff --git a/fs/smb/server/unicode.h b/fs/smb/server/unicode.h new file mode 100644 index 000000000000..076f6034a789 --- /dev/null +++ b/fs/smb/server/unicode.h @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * cifs_unicode: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * + * + * Notes: + * These APIs are based on the C library functions. The semantics + * should match the C functions but with expanded size operands. + * + * The upper/lower functions are based on a table created by mkupr. + * This is a compressed table of upper and lower case conversion. + * + */ +#ifndef _CIFS_UNICODE_H +#define _CIFS_UNICODE_H + +#include +#include +#include +#include + +#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ + +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERISK ((__u16)('*' + 0xF000)) +#define UNI_QUESTION ((__u16)('?' + 0xF000)) +#define UNI_COLON ((__u16)(':' + 0xF000)) +#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) +#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) +#define UNI_PIPE ((__u16)('|' + 0xF000)) +#define UNI_SLASH ((__u16)('\\' + 0xF000)) + +/* Just define what we want from uniupr.h. We don't want to define the tables + * in each source file. + */ +#ifndef UNICASERANGE_DEFINED +struct UniCaseRange { + wchar_t start; + wchar_t end; + signed char *table; +}; +#endif /* UNICASERANGE_DEFINED */ + +#ifndef UNIUPR_NOUPPER +extern signed char SmbUniUpperTable[512]; +extern const struct UniCaseRange SmbUniUpperRange[]; +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +extern signed char CifsUniLowerTable[512]; +extern const struct UniCaseRange CifsUniLowerRange[]; +#endif /* UNIUPR_NOLOWER */ + +#ifdef __KERNEL__ +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage); +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars); +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); +#endif + +/* + * UniStrcat: Concatenate the second string to the first + * + * Returns: + * Address of the first string + */ +static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ + + while (*ucs1++) + /*NULL*/; /* To end of first string */ + ucs1--; /* Return to the null */ + while ((*ucs1++ = *ucs2++)) + /*NULL*/; /* copy string 2 over */ + return anchor; +} + +/* + * UniStrchr: Find a character in a string + * + * Returns: + * Address of first occurrence of character in string + * or NULL if the character is not in the string + */ +static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) +{ + while ((*ucs != uc) && *ucs) + ucs++; + + if (*ucs == uc) + return (wchar_t *)ucs; + return NULL; +} + +/* + * UniStrcmp: Compare two strings + * + * Returns: + * < 0: First string is less than second + * = 0: Strings are equal + * > 0: First string is greater than second + */ +static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +{ + while ((*ucs1 == *ucs2) && *ucs1) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrcpy: Copy a string + */ +static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)) + /*NULL*/; + return anchor; +} + +/* + * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) + */ +static inline size_t UniStrlen(const wchar_t *ucs1) +{ + int i = 0; + + while (*ucs1++) + i++; + return i; +} + +/* + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) + */ +static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) +{ + int i = 0; + + while (*ucs1++) { + i++; + if (i >= maxlen) + break; + } + return i; +} + +/* + * UniStrncat: Concatenate length limited string + */ +static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; /* save pointer to string 1 */ + + while (*ucs1++) + /*NULL*/; + ucs1--; /* point to null terminator of s1 */ + while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ + ucs1++; + ucs2++; + } + *ucs1 = 0; /* Null terminate the result */ + return anchor; +} + +/* + * UniStrncmp: Compare length limited string + */ +static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == *ucs2) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ +static inline int +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy: Copy length limited string with pad + */ +static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ +static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrstr: Find a string in a string + * + * Returns: + * Address of first match found + * NULL if no matching string is found + */ +static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +{ + const wchar_t *anchor1 = ucs1; + const wchar_t *anchor2 = ucs2; + + while (*ucs1) { + if (*ucs1 == *ucs2) { + /* Partial match found */ + ucs1++; + ucs2++; + } else { + if (!*ucs2) /* Match found */ + return (wchar_t *)anchor1; + ucs1 = ++anchor1; /* No match */ + ucs2 = anchor2; + } + } + + if (!*ucs2) /* Both end together */ + return (wchar_t *)anchor1; /* Match found */ + return NULL; /* No match */ +} + +#ifndef UNIUPR_NOUPPER +/* + * UniToupper: Convert a unicode character to upper case + */ +static inline wchar_t UniToupper(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(SmbUniUpperTable)) { + /* Latin characters */ + return uc + SmbUniUpperTable[uc]; /* Use base tables */ + } + + rp = SmbUniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrupr: Upper case a unicode string + */ +static inline __le16 *UniStrupr(register __le16 *upin) +{ + register __le16 *up; + + up = upin; + while (*up) { /* For all characters */ + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); + up++; + } + return upin; /* Return input pointer */ +} +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +/* + * UniTolower: Convert a unicode character to lower case + */ +static inline wchar_t UniTolower(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniLowerTable)) { + /* Latin characters */ + return uc + CifsUniLowerTable[uc]; /* Use base tables */ + } + + rp = CifsUniLowerRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrlwr: Lower case a unicode string + */ +static inline wchar_t *UniStrlwr(register wchar_t *upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniTolower(*up); + up++; + } + return upin; /* Return input pointer */ +} + +#endif + +#endif /* _CIFS_UNICODE_H */ diff --git a/fs/smb/server/uniupr.h b/fs/smb/server/uniupr.h new file mode 100644 index 000000000000..26583b776897 --- /dev/null +++ b/fs/smb/server/uniupr.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/uniupr.h + * Copyright (c) International Business Machines Corp., 2000,2002 + * + * uniupr.h - Unicode compressed case ranges + * + */ +#ifndef __KSMBD_UNIUPR_H +#define __KSMBD_UNIUPR_H + +#ifndef UNIUPR_NOUPPER +/* + * Latin upper case + */ +signed char SmbUniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* 060-06f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, + -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 3b0-3bf */ + -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, + -63, -63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 430-43f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 440-44f */ + 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, 0, -80, -80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, + 126, 126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* ff40-ff4f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, +}; + +/* + * Upper Case Range + */ +const struct UniCaseRange SmbUniUpperRange[] = { + {0x03a0, 0x03ce, UniCaseRangeU03a0}, + {0x0430, 0x045f, UniCaseRangeU0430}, + {0x0490, 0x04cc, UniCaseRangeU0490}, + {0x1e00, 0x1ffc, UniCaseRangeU1e00}, + {0xff40, 0xff5a, UniCaseRangeUff40}, + {0} +}; +#endif + +#ifndef UNIUPR_NOLOWER +/* + * Latin lower case + */ +signed char CifsUniLowerTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 040-04f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, + 0, 0, 0, /* 050-05f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, /* 0c0-0cf */ + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 0, /* 0d0-0df */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ + 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, + 0, /* 170-17f */ + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, + 0, /* 180-18f */ + 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ + 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ +}; + +/* Lower case range - Greek */ +static signed char UniCaseRangeL0380[44] = { + 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 390-39f */ + 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* Lower case range - Cyrillic */ +static signed char UniCaseRangeL0400[48] = { + 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 0, 80, 80, /* 400-40f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 410-41f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 420-42f */ +}; + +/* Lower case range - Extended cyrillic */ +static signed char UniCaseRangeL0490[60] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +/* Lower case range - Extended latin and greek */ +static signed char UniCaseRangeL1e00[504] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, + 0, 0, /* 1fc0-1fcf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, + 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Lower case range - Wide latin */ +static signed char UniCaseRangeLff20[27] = { + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, /* ff20-ff2f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* + * Lower Case Range + */ +const struct UniCaseRange CifsUniLowerRange[] = { + {0x0380, 0x03ab, UniCaseRangeL0380}, + {0x0400, 0x042f, UniCaseRangeL0400}, + {0x0490, 0x04cb, UniCaseRangeL0490}, + {0x1e00, 0x1ff7, UniCaseRangeL1e00}, + {0xff20, 0xff3a, UniCaseRangeLff20}, + {0} +}; +#endif + +#endif /* __KSMBD_UNIUPR_H */ diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c new file mode 100644 index 000000000000..36914db8b661 --- /dev/null +++ b/fs/smb/server/vfs.c @@ -0,0 +1,1911 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../internal.h" /* for vfs_path_lookup */ + +#include "glob.h" +#include "oplock.h" +#include "connection.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "smbacl.h" +#include "ndr.h" +#include "auth.h" +#include "misc.h" + +#include "smb_common.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" + +static char *extract_last_component(char *path) +{ + char *p = strrchr(path, '/'); + + if (p && p[1] != '\0') { + *p = '\0'; + p++; + } else { + p = NULL; + } + return p; +} + +static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + struct inode *parent_inode, + struct inode *inode) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_INHERIT_OWNER)) + return; + + i_uid_write(inode, i_uid_read(parent_inode)); +} + +/** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable + * + * the parent dentry got by dget_parent or @parent could be + * unstable, we try to lock a parent inode and lookup the + * child dentry again. + * + * the reference count of @parent isn't incremented. + */ +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child) +{ + struct dentry *dentry; + int ret = 0; + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + dentry = lookup_one(user_ns, child->d_name.name, parent, + child->d_name.len); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto out_err; + } + + if (dentry != child) { + ret = -ESTALE; + dput(dentry); + goto out_err; + } + + dput(dentry); + return 0; +out_err: + inode_unlock(d_inode(parent)); + return ret; +} + +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, + struct dentry *dentry) +{ + struct dentry *parent; + int ret; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + ret = inode_permission(user_ns, d_inode(parent), + MAY_EXEC | MAY_WRITE); + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) +{ + struct dentry *parent; + int ret = 0; + + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) + *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | + FILE_WRITE_DATA | FILE_APPEND_DATA | + FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | + FILE_DELETE_CHILD); + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) + *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) + *daccess |= FILE_EXECUTE_LE; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) + *daccess |= FILE_DELETE_LE; + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +/** + * ksmbd_vfs_create() - vfs helper for smb create file + * @work: work + * @name: file name that is relative to share + * @mode: file create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS, &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -ENOENT) + pr_err("path create failed for %s, err %d\n", + name, err); + return err; + } + + mode |= S_IFREG; + err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), + dentry, mode, true); + if (!err) { + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), + d_inode(dentry)); + } else { + pr_err("File(%s): creation failed (err:%d)\n", name, err); + } + done_path_create(&path, dentry); + return err; +} + +/** + * ksmbd_vfs_mkdir() - vfs helper for smb create directory + * @work: work + * @name: directory name that is relative to share + * @mode: directory create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -EEXIST) + ksmbd_debug(VFS, "path create failed for %s, err %d\n", + name, err); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + mode |= S_IFDIR; + err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); + if (err) { + goto out; + } else if (d_unhashed(dentry)) { + struct dentry *d; + + d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); + if (IS_ERR(d)) { + err = PTR_ERR(d); + goto out; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; + goto out; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); + dput(d); + } +out: + done_path_create(&path, dentry); + if (err) + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); + return err; +} + +static ssize_t ksmbd_vfs_getcasexattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len, char **attr_value) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_getxattr(user_ns, + dentry, + name, + attr_value); + if (value_len < 0) + pr_err("failed to get xattr in file\n"); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + ssize_t v_len; + char *stream_buf = NULL; + + ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", + *pos, count); + + v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len <= 0) + return (int)v_len; + + if (v_len <= *pos) { + count = -EINVAL; + goto free_buf; + } + + if (v_len - *pos < count) + count = v_len - *pos; + + memcpy(buf, &stream_buf[*pos], count); + +free_buf: + kvfree(stream_buf); + return count; +} + +/** + * check_lock_range() - vfs helper for smb byte range file locking + * @filp: the file to apply the lock to + * @start: lock start byte offset + * @end: lock end byte offset + * @type: byte range type read/write + * + * Return: 0 on success, otherwise error + */ +static int check_lock_range(struct file *filp, loff_t start, loff_t end, + unsigned char type) +{ + struct file_lock *flock; + struct file_lock_context *ctx = file_inode(filp)->i_flctx; + int error = 0; + + if (!ctx || list_empty_careful(&ctx->flc_posix)) + return 0; + + spin_lock(&ctx->flc_lock); + list_for_each_entry(flock, &ctx->flc_posix, fl_list) { + /* check conflict locks */ + if (flock->fl_end >= start && end >= flock->fl_start) { + if (flock->fl_type == F_RDLCK) { + if (type == WRITE) { + pr_err("not allow write by shared lock\n"); + error = 1; + goto out; + } + } else if (flock->fl_type == F_WRLCK) { + /* check owner in lock */ + if (flock->fl_file != filp) { + error = 1; + pr_err("not allow rw access by exclusive lock from other opens\n"); + goto out; + } + } + } + } +out: + spin_unlock(&ctx->flc_lock); + return error; +} + +/** + * ksmbd_vfs_read() - vfs helper for smb file read + * @work: smb work + * @fid: file id of open file + * @count: read byte count + * @pos: file pos + * + * Return: number of read bytes on success, otherwise error + */ +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, + loff_t *pos) +{ + struct file *filp = fp->filp; + ssize_t nbytes = 0; + char *rbuf = work->aux_payload_buf; + struct inode *inode = file_inode(filp); + + if (S_ISDIR(inode->i_mode)) + return -EISDIR; + + if (unlikely(count == 0)) + return 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pD)\n", fp->filp); + return -EACCES; + } + } + + if (ksmbd_stream_fd(fp)) + return ksmbd_vfs_stream_read(fp, rbuf, pos, count); + + if (!work->tcon->posix_extensions) { + int ret; + + ret = check_lock_range(filp, *pos, *pos + count - 1, READ); + if (ret) { + pr_err("unable to read due to lock\n"); + return -EAGAIN; + } + } + + nbytes = kernel_read(filp, rbuf, count, pos); + if (nbytes < 0) { + pr_err("smb read failed, err = %zd\n", nbytes); + return nbytes; + } + + filp->f_pos = *pos; + return nbytes; +} + +static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + char *stream_buf = NULL, *wbuf; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + size_t size, v_len; + int err = 0; + + ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", + *pos, count); + + size = *pos + count; + if (size > XATTR_SIZE_MAX) { + size = XATTR_SIZE_MAX; + count = (*pos + count) - XATTR_SIZE_MAX; + } + + v_len = ksmbd_vfs_getcasexattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len < 0) { + pr_err("not found stream in xattr : %zd\n", v_len); + err = (int)v_len; + goto out; + } + + if (v_len < size) { + wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!wbuf) { + err = -ENOMEM; + goto out; + } + + if (v_len > 0) + memcpy(wbuf, stream_buf, v_len); + kvfree(stream_buf); + stream_buf = wbuf; + } + + memcpy(&stream_buf[*pos], buf, count); + + err = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + (void *)stream_buf, + size, + 0); + if (err < 0) + goto out; + + fp->filp->f_pos = *pos; + err = 0; +out: + kvfree(stream_buf); + return err; +} + +/** + * ksmbd_vfs_write() - vfs helper for smb file write + * @work: work + * @fid: file id of open file + * @buf: buf containing data for writing + * @count: read byte count + * @pos: file pos + * @sync: fsync after write + * @written: number of bytes written + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written) +{ + struct file *filp; + loff_t offset = *pos; + int err = 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + pr_err("no right to write(%pD)\n", fp->filp); + err = -EACCES; + goto out; + } + } + + filp = fp->filp; + + if (ksmbd_stream_fd(fp)) { + err = ksmbd_vfs_stream_write(fp, buf, pos, count); + if (!err) + *written = count; + goto out; + } + + if (!work->tcon->posix_extensions) { + err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); + if (err) { + pr_err("unable to write due to lock\n"); + err = -EAGAIN; + goto out; + } + } + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + err = kernel_write(filp, buf, count, pos); + if (err < 0) { + ksmbd_debug(VFS, "smb write failed, err = %d\n", err); + goto out; + } + + filp->f_pos = *pos; + *written = err; + err = 0; + if (sync) { + err = vfs_fsync_range(filp, offset, offset + *written, 0); + if (err < 0) + pr_err("fsync failed for filename = %pD, err = %d\n", + fp->filp, err); + } + +out: + return err; +} + +/** + * ksmbd_vfs_getattr() - vfs helper for smb getattr + * @work: work + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) +{ + int err; + + err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); + if (err) + pr_err("getattr failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_fsync() - vfs helper for smb fsync + * @work: work + * @fid: file id of open file + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) +{ + struct ksmbd_file *fp; + int err; + + fp = ksmbd_lookup_fd_slow(work, fid, p_id); + if (!fp) { + pr_err("failed to get filp for fid %llu\n", fid); + return -ENOENT; + } + err = vfs_fsync(fp->filp, 0); + if (err < 0) + pr_err("smb fsync failed, err = %d\n", err); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink + * @name: directory or file name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *parent; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); + if (err) { + ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); + ksmbd_revert_fsids(work); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + parent = dget_parent(path.dentry); + err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); + if (err) { + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; + } + + if (!d_inode(path.dentry)->i_nlink) { + err = -ENOENT; + goto out_err; + } + + if (S_ISDIR(d_inode(path.dentry)->i_mode)) { + err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); + if (err && err != -ENOTEMPTY) + ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, + err); + } else { + err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); + if (err) + ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, + err); + } + +out_err: + inode_unlock(d_inode(parent)); + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_link() - vfs helper for creating smb hardlink + * @oldname: source file name + * @newname: hardlink name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + const char *newname) +{ + struct path oldpath, newpath; + struct dentry *dentry; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); + if (err) { + pr_err("cannot get linux path for %s, err = %d\n", + oldname, err); + goto out1; + } + + dentry = ksmbd_vfs_kern_path_create(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, + &newpath); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + pr_err("path create err for %s, err %d\n", newname, err); + goto out2; + } + + err = -EXDEV; + if (oldpath.mnt != newpath.mnt) { + pr_err("vfs_link failed err %d\n", err); + goto out3; + } + + err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), + d_inode(newpath.dentry), + dentry, NULL); + if (err) + ksmbd_debug(VFS, "vfs_link failed err %d\n", err); + +out3: + done_path_create(&newpath, dentry); +out2: + path_put(&oldpath); +out1: + ksmbd_revert_fsids(work); + return err; +} + +static int ksmbd_validate_entry_in_use(struct dentry *src_dent) +{ + struct dentry *dst_dent; + + spin_lock(&src_dent->d_lock); + list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { + struct ksmbd_file *child_fp; + + if (d_really_is_negative(dst_dent)) + continue; + + child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); + if (child_fp) { + spin_unlock(&src_dent->d_lock); + ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); + return -EACCES; + } + } + spin_unlock(&src_dent->d_lock); + + return 0; +} + +static int __ksmbd_vfs_rename(struct ksmbd_work *work, + struct user_namespace *src_user_ns, + struct dentry *src_dent_parent, + struct dentry *src_dent, + struct user_namespace *dst_user_ns, + struct dentry *dst_dent_parent, + struct dentry *trap_dent, + char *dst_name) +{ + struct dentry *dst_dent; + int err; + + if (!work->tcon->posix_extensions) { + err = ksmbd_validate_entry_in_use(src_dent); + if (err) + return err; + } + + if (d_really_is_negative(src_dent_parent)) + return -ENOENT; + if (d_really_is_negative(dst_dent_parent)) + return -ENOENT; + if (d_really_is_negative(src_dent)) + return -ENOENT; + if (src_dent == trap_dent) + return -EINVAL; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, + strlen(dst_name)); + err = PTR_ERR(dst_dent); + if (IS_ERR(dst_dent)) { + pr_err("lookup failed %s [%d]\n", dst_name, err); + goto out; + } + + err = -ENOTEMPTY; + if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { + struct renamedata rd = { + .old_mnt_userns = src_user_ns, + .old_dir = d_inode(src_dent_parent), + .old_dentry = src_dent, + .new_mnt_userns = dst_user_ns, + .new_dir = d_inode(dst_dent_parent), + .new_dentry = dst_dent, + }; + err = vfs_rename(&rd); + } + if (err) + pr_err("vfs_rename failed err %d\n", err); + if (dst_dent) + dput(dst_dent); +out: + ksmbd_revert_fsids(work); + return err; +} + +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname) +{ + struct user_namespace *user_ns; + struct path dst_path; + struct dentry *src_dent_parent, *dst_dent_parent; + struct dentry *src_dent, *trap_dent, *src_child; + char *dst_name; + int err; + + dst_name = extract_last_component(newname); + if (!dst_name) { + dst_name = newname; + newname = ""; + } + + src_dent_parent = dget_parent(fp->filp->f_path.dentry); + src_dent = fp->filp->f_path.dentry; + + err = ksmbd_vfs_kern_path(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &dst_path, false); + if (err) { + ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); + goto out; + } + dst_dent_parent = dst_path.dentry; + + trap_dent = lock_rename(src_dent_parent, dst_dent_parent); + dget(src_dent); + dget(dst_dent_parent); + user_ns = file_mnt_user_ns(fp->filp); + src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, + src_dent->d_name.len); + if (IS_ERR(src_child)) { + err = PTR_ERR(src_child); + goto out_lock; + } + + if (src_child != src_dent) { + err = -ESTALE; + dput(src_child); + goto out_lock; + } + dput(src_child); + + err = __ksmbd_vfs_rename(work, + user_ns, + src_dent_parent, + src_dent, + mnt_user_ns(dst_path.mnt), + dst_dent_parent, + trap_dent, + dst_name); +out_lock: + dput(src_dent); + dput(dst_dent_parent); + unlock_rename(src_dent_parent, dst_dent_parent); + path_put(&dst_path); +out: + dput(src_dent_parent); + return err; +} + +/** + * ksmbd_vfs_truncate() - vfs helper for smb file truncate + * @work: work + * @fid: file id of old file + * @size: truncate to given size + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size) +{ + int err = 0; + struct file *filp; + + filp = fp->filp; + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + if (!work->tcon->posix_extensions) { + struct inode *inode = file_inode(filp); + + if (size < inode->i_size) { + err = check_lock_range(filp, size, + inode->i_size - 1, WRITE); + } else { + err = check_lock_range(filp, inode->i_size, + size - 1, WRITE); + } + + if (err) { + pr_err("failed due to lock\n"); + return -EAGAIN; + } + } + + err = vfs_truncate(&filp->f_path, size); + if (err) + pr_err("truncate failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes + * @dentry: dentry of file for listing xattrs + * @list: destination buffer + * @size: destination buffer length + * + * Return: xattr list length on success, otherwise error + */ +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) +{ + ssize_t size; + char *vlist = NULL; + + size = vfs_listxattr(dentry, NULL, 0); + if (size <= 0) + return size; + + vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!vlist) + return -ENOMEM; + + *list = vlist; + size = vfs_listxattr(dentry, vlist, size); + if (size < 0) { + ksmbd_debug(VFS, "listxattr failed\n"); + kvfree(vlist); + *list = NULL; + } + + return size; +} + +static ssize_t ksmbd_vfs_xattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *xattr_name) +{ + return vfs_getxattr(user_ns, dentry, xattr_name, NULL, 0); +} + +/** + * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value + * @user_ns: user namespace + * @dentry: dentry of file for getting xattrs + * @xattr_name: name of xattr name to query + * @xattr_buf: destination buffer xattr value + * + * Return: read xattr value length on success, otherwise error + */ +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, char **xattr_buf) +{ + ssize_t xattr_len; + char *buf; + + *xattr_buf = NULL; + xattr_len = ksmbd_vfs_xattr_len(user_ns, dentry, xattr_name); + if (xattr_len < 0) + return xattr_len; + + buf = kmalloc(xattr_len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + xattr_len = vfs_getxattr(user_ns, dentry, xattr_name, + (void *)buf, xattr_len); + if (xattr_len > 0) + *xattr_buf = buf; + else + kfree(buf); + return xattr_len; +} + +/** + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace + * @dentry: dentry to set XATTR at + * @name: xattr name for setxattr + * @value: xattr value to set + * @size: size of xattr value + * @flags: destination buffer length + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, + void *attr_value, size_t attr_size, int flags) +{ + int err; + + err = vfs_setxattr(user_ns, + dentry, + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options + * @filp: file pointer for IO + * @options: smb IO options + */ +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) +{ + struct address_space *mapping; + + mapping = filp->f_mapping; + + if (!option || !mapping) + return; + + if (option & FILE_WRITE_THROUGH_LE) { + filp->f_flags |= O_SYNC; + } else if (option & FILE_SEQUENTIAL_ONLY_LE) { + filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; + spin_lock(&filp->f_lock); + filp->f_mode &= ~FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } else if (option & FILE_RANDOM_ACCESS_LE) { + spin_lock(&filp->f_lock); + filp->f_mode |= FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } +} + +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len) +{ + smb_break_all_levII_oplock(work, fp, 1); + if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) + return vfs_fallocate(fp->filp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + off, len); + + return vfs_fallocate(fp->filp, + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, + off, len); +} + +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count) +{ + struct file *f = fp->filp; + struct inode *inode = file_inode(fp->filp); + loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; + loff_t extent_start, extent_end; + int ret = 0; + + if (start > maxbytes) + return -EFBIG; + + if (!in_count) + return 0; + + /* + * Shrink request scope to what the fs can actually handle. + */ + if (length > maxbytes || (maxbytes - length) < start) + length = maxbytes - start; + + if (start + length > inode->i_size) + length = inode->i_size - start; + + *out_count = 0; + end = start + length; + while (start < end && *out_count < in_count) { + extent_start = vfs_llseek(f, start, SEEK_DATA); + if (extent_start < 0) { + if (extent_start != -ENXIO) + ret = (int)extent_start; + break; + } + + if (extent_start >= end) + break; + + extent_end = vfs_llseek(f, extent_start, SEEK_HOLE); + if (extent_end < 0) { + if (extent_end != -ENXIO) + ret = (int)extent_end; + break; + } else if (extent_start >= extent_end) { + break; + } + + ranges[*out_count].file_offset = cpu_to_le64(extent_start); + ranges[(*out_count)++].length = + cpu_to_le64(min(extent_end, end) - extent_start); + + start = extent_end; + } + + return ret; +} + +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name) +{ + return vfs_removexattr(user_ns, dentry, attr_name); +} + +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry) +{ + int err = 0; + + err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); + if (err) + return err; + dget(dentry); + + if (S_ISDIR(d_inode(dentry)->i_mode)) + err = vfs_rmdir(user_ns, d_inode(dir), dentry); + else + err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL); + + dput(dentry); + inode_unlock(d_inode(dir)); + if (err) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); + + return err; +} + +static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + buf->dirent_count++; + + return buf->dirent_count <= 2; +} + +/** + * ksmbd_vfs_empty_dir() - check for empty directory + * @fp: ksmbd file pointer + * + * Return: true if directory empty, otherwise false + */ +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) +{ + int err; + struct ksmbd_readdir_data readdir_data; + + memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); + + set_ctx_actor(&readdir_data.ctx, __dir_empty); + readdir_data.dirent_count = 0; + + err = iterate_dir(fp->filp, &readdir_data.ctx); + if (readdir_data.dirent_count > 2) + err = -ENOTEMPTY; + else + err = 0; + return err; +} + +static bool __caseless_lookup(struct dir_context *ctx, const char *name, + int namlen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + int cmp = -EINVAL; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + + if (buf->used != namlen) + return true; + if (IS_ENABLED(CONFIG_UNICODE) && buf->um) { + const struct qstr q_buf = {.name = buf->private, + .len = buf->used}; + const struct qstr q_name = {.name = name, + .len = namlen}; + + cmp = utf8_strncasecmp(buf->um, &q_buf, &q_name); + } + if (cmp < 0) + cmp = strncasecmp((char *)buf->private, name, namlen); + if (!cmp) { + memcpy((char *)buf->private, name, namlen); + buf->dirent_count = 1; + return false; + } + return true; +} + +/** + * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory + * @dir: path info + * @name: filename to lookup + * @namelen: filename length + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + size_t namelen, struct unicode_map *um) +{ + int ret; + struct file *dfilp; + int flags = O_RDONLY | O_LARGEFILE; + struct ksmbd_readdir_data readdir_data = { + .ctx.actor = __caseless_lookup, + .private = name, + .used = namelen, + .dirent_count = 0, + .um = um, + }; + + dfilp = dentry_open(dir, flags, current_cred()); + if (IS_ERR(dfilp)) + return PTR_ERR(dfilp); + + ret = iterate_dir(dfilp, &readdir_data.ctx); + if (readdir_data.dirent_count > 0) + ret = 0; + fput(dfilp); + return ret; +} + +/** + * ksmbd_vfs_kern_path() - lookup a file and get path info + * @name: file path that is relative to share + * @flags: lookup flags + * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, bool caseless) +{ + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + int err; + + flags |= LOOKUP_BENEATH; + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + name, + flags, + path); + if (!err) + return 0; + + if (caseless) { + char *filepath; + struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(name, GFP_KERNEL); + if (!filepath) + return -ENOMEM; + + path_len = strlen(filepath); + remain_len = path_len; + + parent = share_conf->vfs_path; + path_get(&parent); + + while (d_can_lookup(parent.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; + + if (filename_len == 0) + break; + + err = ksmbd_vfs_lookup_in_dir(&parent, filename, + filename_len, + work->conn->um); + path_put(&parent); + if (err) + goto out; + + next[0] = '\0'; + + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + filepath, + flags, + &parent); + if (err) + goto out; + else if (is_last) { + *path = parent; + goto out; + } + + next[0] = '/'; + remain_len -= filename_len + 1; + } + + path_put(&parent); + err = -EINVAL; +out: + kfree(filepath); + } + return err; +} + +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path) +{ + char *abs_name; + struct dentry *dent; + + abs_name = convert_to_unix_name(work->tcon->share_conf, name); + if (!abs_name) + return ERR_PTR(-ENOMEM); + + dent = kern_path_create(AT_FDCWD, abs_name, path, flags); + kfree(abs_name); + return dent; +} + +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || + !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); + if (err) + ksmbd_debug(SMB, + "remove acl xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespace *user_ns, + struct inode *inode, + int acl_type) +{ + struct xattr_smb_acl *smb_acl = NULL; + struct posix_acl *posix_acls; + struct posix_acl_entry *pa_entry; + struct xattr_acl_entry *xa_entry; + int i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return NULL; + + posix_acls = get_acl(inode, acl_type); + if (!posix_acls) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + + sizeof(struct xattr_acl_entry) * posix_acls->a_count, + GFP_KERNEL); + if (!smb_acl) + goto out; + + smb_acl->count = posix_acls->a_count; + pa_entry = posix_acls->a_entries; + xa_entry = smb_acl->entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { + switch (pa_entry->e_tag) { + case ACL_USER: + xa_entry->type = SMB_ACL_USER; + xa_entry->uid = posix_acl_uid_translate(user_ns, pa_entry); + break; + case ACL_USER_OBJ: + xa_entry->type = SMB_ACL_USER_OBJ; + break; + case ACL_GROUP: + xa_entry->type = SMB_ACL_GROUP; + xa_entry->gid = posix_acl_gid_translate(user_ns, pa_entry); + break; + case ACL_GROUP_OBJ: + xa_entry->type = SMB_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + xa_entry->type = SMB_ACL_OTHER; + break; + case ACL_MASK: + xa_entry->type = SMB_ACL_MASK; + break; + default: + pr_err("unknown type : 0x%x\n", pa_entry->e_tag); + goto out; + } + + if (pa_entry->e_perm & ACL_READ) + xa_entry->perm |= SMB_ACL_READ; + if (pa_entry->e_perm & ACL_WRITE) + xa_entry->perm |= SMB_ACL_WRITE; + if (pa_entry->e_perm & ACL_EXECUTE) + xa_entry->perm |= SMB_ACL_EXECUTE; + } +out: + posix_acl_release(posix_acls); + return smb_acl; +} + +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len) +{ + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; + struct inode *inode = d_inode(dentry); + + acl.version = 4; + acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; + acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); + + memcpy(acl.desc, "posix_acl", 9); + acl.desc_len = 10; + + pntsd->osidoffset = + cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); + pntsd->gsidoffset = + cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); + pntsd->dacloffset = + cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); + + acl.sd_buf = (char *)pntsd; + acl.sd_size = len; + + rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + return rc; + } + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, + smb_acl, def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + acl.posix_acl_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out; + } + + rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_vfs_setxattr(user_ns, dentry, + XATTR_NAME_SD, sd_ndr.data, + sd_ndr.offset, 0); + if (rc < 0) + pr_err("Failed to store XATTR ntacl :%d\n", rc); + + kfree(sd_ndr.data); +out: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + return rc; +} + +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd) +{ + int rc; + struct ndr n; + struct inode *inode = d_inode(dentry); + struct ndr acl_ndr = {0}; + struct xattr_ntacl acl; + struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; + __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; + + rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); + if (rc <= 0) + return rc; + + n.length = rc; + rc = ndr_decode_v4_ntacl(&n, &acl); + if (rc) + goto free_n_data; + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, smb_acl, + def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out_free; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out_free; + } + + if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { + pr_err("hash value diff\n"); + rc = -EINVAL; + goto out_free; + } + + *pntsd = acl.sd_buf; + if (acl.sd_size < sizeof(struct smb_ntsd)) { + pr_err("sd size is invalid\n"); + goto out_free; + } + + (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - + NDR_NTSD_OFFSETOF); + + rc = acl.sd_size; +out_free: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + if (rc < 0) { + kfree(acl.sd_buf); + *pntsd = NULL; + } + +free_n_data: + kfree(n.data); + return rc; +} + +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ndr_encode_dos_attr(&n, da); + if (err) + return err; + + err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, n.offset, 0); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); + + return err; +} + +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (char **)&n.data); + if (err > 0) { + n.length = err; + if (ndr_decode_dos_attr(&n, da)) + err = -EINVAL; + kfree(n.data); + } else { + ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); + } + + return err; +} + +/** + * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format + * @p: destination buffer + * @ksmbd_kstat: ksmbd kstat wrapper + */ +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) +{ + struct file_directory_info *info = (struct file_directory_info *)(*p); + struct kstat *kstat = ksmbd_kstat->kstat; + u64 time; + + info->FileIndex = 0; + info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(kstat->atime); + info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->mtime); + info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->ctime); + info->ChangeTime = cpu_to_le64(time); + + if (ksmbd_kstat->file_attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { + info->EndOfFile = 0; + info->AllocationSize = 0; + } else { + info->EndOfFile = cpu_to_le64(kstat->size); + info->AllocationSize = cpu_to_le64(kstat->blocks << 9); + } + info->ExtFileAttributes = ksmbd_kstat->file_attributes; + + return info; +} + +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat) +{ + u64 time; + int rc; + + generic_fillattr(user_ns, d_inode(dentry), ksmbd_kstat->kstat); + + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + ksmbd_kstat->create_time = time; + + /* + * set default value for the case that store dos attributes is not yes + * or that acl is disable in server's filesystem and the config is yes. + */ + if (S_ISDIR(ksmbd_kstat->kstat->mode)) + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_DIRECTORY_LE; + else + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_ARCHIVE_LE; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(user_ns, dentry, &da); + if (rc > 0) { + ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); + ksmbd_kstat->create_time = da.create_time; + } else { + ksmbd_debug(VFS, "fail to load dos attribute.\n"); + } + } + + return 0; +} + +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_xattr_len(user_ns, dentry, name); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type) +{ + char *type, *buf; + + if (s_type == DIR_STREAM) + type = ":$INDEX_ALLOCATION"; + else + type = ":$DATA"; + + buf = kasprintf(GFP_KERNEL, "%s%s%s", + XATTR_NAME_STREAM, stream_name, type); + if (!buf) + return -ENOMEM; + + *xattr_stream_name = buf; + *xattr_stream_name_size = strlen(buf) + 1; + + return 0; +} + +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written) +{ + unsigned int i; + loff_t src_off, dst_off, src_file_size; + size_t len; + int ret; + + *chunk_count_written = 0; + *chunk_size_written = 0; + *total_size_written = 0; + + if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pD)\n", src_fp->filp); + return -EACCES; + } + if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { + pr_err("no right to write(%pD)\n", dst_fp->filp); + return -EACCES; + } + + if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) + return -EBADF; + + smb_break_all_levII_oplock(work, dst_fp, 1); + + if (!work->tcon->posix_extensions) { + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (check_lock_range(src_fp->filp, src_off, + src_off + len - 1, READ)) + return -EAGAIN; + if (check_lock_range(dst_fp->filp, dst_off, + dst_off + len - 1, WRITE)) + return -EAGAIN; + } + } + + src_file_size = i_size_read(file_inode(src_fp->filp)); + + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (src_off + len > src_file_size) + return -E2BIG; + + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, 0); + if (ret == -EOPNOTSUPP || ret == -EXDEV) + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, + COPY_FILE_SPLICE); + if (ret < 0) + return ret; + + *chunk_count_written += 1; + *total_size_written += ret; + } + return 0; +} + +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) +{ + wait_event(flock->fl_wait, !flock->fl_blocker); +} + +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) +{ + return wait_event_interruptible_timeout(flock->fl_wait, + !flock->fl_blocker, + timeout); +} + +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) +{ + locks_delete_block(flock); +} + +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode) +{ + struct posix_acl_state acl_state; + struct posix_acl *acls; + int rc; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + ksmbd_debug(SMB, "Set posix acls\n"); + rc = init_acl_state(&acl_state, 1); + if (rc) + return rc; + + /* Set default owner group */ + acl_state.owner.allow = (inode->i_mode & 0700) >> 6; + acl_state.group.allow = (inode->i_mode & 0070) >> 3; + acl_state.other.allow = inode->i_mode & 0007; + acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + acl_state.owner.allow; + acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + acl_state.group.allow; + acl_state.mask.allow = 0x07; + + acls = posix_acl_alloc(6, GFP_KERNEL); + if (!acls) { + free_acl_state(&acl_state); + return -ENOMEM; + } + posix_state_to_acl(&acl_state, acls->a_entries); + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + else if (S_ISDIR(inode->i_mode)) { + posix_state_to_acl(&acl_state, acls->a_entries); + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, + acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; +} + +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, struct inode *parent_inode) +{ + struct posix_acl *acls; + struct posix_acl_entry *pace; + int rc, i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); + if (!acls) + return -ENOENT; + pace = acls->a_entries; + + for (i = 0; i < acls->a_count; i++, pace++) { + if (pace->e_tag == ACL_MASK) { + pace->e_perm = 0x07; + break; + } + } + + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode)) { + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, + acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + posix_acl_release(acls); + return rc; +} diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h new file mode 100644 index 000000000000..593059ca8511 --- /dev/null +++ b/fs/smb/server/vfs.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_VFS_H__ +#define __KSMBD_VFS_H__ + +#include +#include +#include +#include +#include +#include + +#include "smbacl.h" +#include "xattr.h" + +/* + * Enumeration for stream type. + */ +enum { + DATA_STREAM = 1, /* type $DATA */ + DIR_STREAM /* type $INDEX_ALLOCATION */ +}; + +/* CreateOptions */ +#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) +#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) + +#define CREATE_OPTION_READONLY 0x10000000 +/* system. NB not sent over wire */ +#define CREATE_OPTION_SPECIAL 0x20000000 + +struct ksmbd_work; +struct ksmbd_file; +struct ksmbd_conn; + +struct ksmbd_dir_info { + const char *name; + char *wptr; + char *rptr; + int name_len; + int out_buf_len; + int num_entry; + int data_count; + int last_entry_offset; + bool hide_dot_file; + int flags; + int last_entry_off_align; +}; + +struct ksmbd_readdir_data { + struct dir_context ctx; + union { + void *private; + char *dirent; + }; + + unsigned int used; + unsigned int dirent_count; + unsigned int file_attr; + struct unicode_map *um; +}; + +/* ksmbd kstat wrapper to get valid create time when reading dir entry */ +struct ksmbd_kstat { + struct kstat *kstat; + unsigned long long create_time; + __le32 file_attributes; +}; + +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child); +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, + size_t count, loff_t *pos); +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); +int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname); +int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname); +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size); +struct srv_copychunk; +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written); +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, + char **xattr_buf); +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len); +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, + void *attr_value, size_t attr_size, int flags); +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name); +int ksmbd_vfs_kern_path(struct ksmbd_work *work, + char *name, unsigned int flags, struct path *path, + bool caseless); +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path); +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len); +struct file_allocated_range_buffer; +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count); +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry); +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat); +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len); +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd); +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode); +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, + struct inode *parent_inode); +#endif /* __KSMBD_VFS_H__ */ diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c new file mode 100644 index 000000000000..0ae5dd0829e9 --- /dev/null +++ b/fs/smb/server/vfs_cache.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "glob.h" +#include "vfs_cache.h" +#include "oplock.h" +#include "vfs.h" +#include "connection.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "smb_common.h" + +#define S_DEL_PENDING 1 +#define S_DEL_ON_CLS 2 +#define S_DEL_ON_CLS_STREAM 8 + +static unsigned int inode_hash_mask __read_mostly; +static unsigned int inode_hash_shift __read_mostly; +static struct hlist_head *inode_hashtable __read_mostly; +static DEFINE_RWLOCK(inode_hash_lock); + +static struct ksmbd_file_table global_ft; +static atomic_long_t fd_limit; +static struct kmem_cache *filp_cache; + +void ksmbd_set_fd_limit(unsigned long limit) +{ + limit = min(limit, get_max_files()); + atomic_long_set(&fd_limit, limit); +} + +static bool fd_limit_depleted(void) +{ + long v = atomic_long_dec_return(&fd_limit); + + if (v >= 0) + return false; + atomic_long_inc(&fd_limit); + return true; +} + +static void fd_limit_close(void) +{ + atomic_long_inc(&fd_limit); +} + +/* + * INODE hash + */ + +static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) +{ + unsigned long tmp; + + tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / + L1_CACHE_BYTES; + tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); + return tmp & inode_hash_mask; +} + +static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) +{ + struct hlist_head *head = inode_hashtable + + inode_hash(inode->i_sb, inode->i_ino); + struct ksmbd_inode *ci = NULL, *ret_ci = NULL; + + hlist_for_each_entry(ci, head, m_hash) { + if (ci->m_inode == inode) { + if (atomic_inc_not_zero(&ci->m_count)) + ret_ci = ci; + break; + } + } + return ret_ci; +} + +static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) +{ + return __ksmbd_inode_lookup(file_inode(fp->filp)); +} + +static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) +{ + struct ksmbd_inode *ci; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + read_unlock(&inode_hash_lock); + return ci; +} + +int ksmbd_query_inode_status(struct inode *inode) +{ + struct ksmbd_inode *ci; + int ret = KSMBD_INODE_STATUS_UNKNOWN; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + if (ci) { + ret = KSMBD_INODE_STATUS_OK; + if (ci->m_flags & S_DEL_PENDING) + ret = KSMBD_INODE_STATUS_PENDING_DELETE; + atomic_dec(&ci->m_count); + } + read_unlock(&inode_hash_lock); + return ret; +} + +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) +{ + return (fp->f_ci->m_flags & S_DEL_PENDING); +} + +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags |= S_DEL_PENDING; +} + +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags &= ~S_DEL_PENDING; +} + +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info) +{ + if (ksmbd_stream_fd(fp)) { + fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; + return; + } + + fp->f_ci->m_flags |= S_DEL_ON_CLS; +} + +static void ksmbd_inode_hash(struct ksmbd_inode *ci) +{ + struct hlist_head *b = inode_hashtable + + inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); + + hlist_add_head(&ci->m_hash, b); +} + +static void ksmbd_inode_unhash(struct ksmbd_inode *ci) +{ + write_lock(&inode_hash_lock); + hlist_del_init(&ci->m_hash); + write_unlock(&inode_hash_lock); +} + +static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) +{ + ci->m_inode = file_inode(fp->filp); + atomic_set(&ci->m_count, 1); + atomic_set(&ci->op_count, 0); + atomic_set(&ci->sop_count, 0); + ci->m_flags = 0; + ci->m_fattr = 0; + INIT_LIST_HEAD(&ci->m_fp_list); + INIT_LIST_HEAD(&ci->m_op_list); + rwlock_init(&ci->m_lock); + return 0; +} + +static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) +{ + struct ksmbd_inode *ci, *tmpci; + int rc; + + read_lock(&inode_hash_lock); + ci = ksmbd_inode_lookup(fp); + read_unlock(&inode_hash_lock); + if (ci) + return ci; + + ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); + if (!ci) + return NULL; + + rc = ksmbd_inode_init(ci, fp); + if (rc) { + pr_err("inode initialized failed\n"); + kfree(ci); + return NULL; + } + + write_lock(&inode_hash_lock); + tmpci = ksmbd_inode_lookup(fp); + if (!tmpci) { + ksmbd_inode_hash(ci); + } else { + kfree(ci); + ci = tmpci; + } + write_unlock(&inode_hash_lock); + return ci; +} + +static void ksmbd_inode_free(struct ksmbd_inode *ci) +{ + ksmbd_inode_unhash(ci); + kfree(ci); +} + +static void ksmbd_inode_put(struct ksmbd_inode *ci) +{ + if (atomic_dec_and_test(&ci->m_count)) + ksmbd_inode_free(ci); +} + +int __init ksmbd_inode_hash_init(void) +{ + unsigned int loop; + unsigned long numentries = 16384; + unsigned long bucketsize = sizeof(struct hlist_head); + unsigned long size; + + inode_hash_shift = ilog2(numentries); + inode_hash_mask = (1 << inode_hash_shift) - 1; + + size = bucketsize << inode_hash_shift; + + /* init master fp hash table */ + inode_hashtable = vmalloc(size); + if (!inode_hashtable) + return -ENOMEM; + + for (loop = 0; loop < (1U << inode_hash_shift); loop++) + INIT_HLIST_HEAD(&inode_hashtable[loop]); + return 0; +} + +void ksmbd_release_inode_hash(void) +{ + vfree(inode_hashtable); +} + +static void __ksmbd_inode_close(struct ksmbd_file *fp) +{ + struct dentry *dir, *dentry; + struct ksmbd_inode *ci = fp->f_ci; + int err; + struct file *filp; + + filp = fp->filp; + if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), + filp->f_path.dentry, + fp->stream.name); + if (err) + pr_err("remove xattr failed : %s\n", + fp->stream.name); + } + + if (atomic_dec_and_test(&ci->m_count)) { + write_lock(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { + dentry = filp->f_path.dentry; + dir = dentry->d_parent; + ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + write_unlock(&ci->m_lock); + ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); + write_lock(&ci->m_lock); + } + write_unlock(&ci->m_lock); + + ksmbd_inode_free(ci); + } +} + +static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) +{ + if (!has_file_id(fp->persistent_id)) + return; + + write_lock(&global_ft.lock); + idr_remove(global_ft.idr, fp->persistent_id); + write_unlock(&global_ft.lock); +} + +static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + if (!has_file_id(fp->volatile_id)) + return; + + write_lock(&fp->f_ci->m_lock); + list_del_init(&fp->node); + write_unlock(&fp->f_ci->m_lock); + + write_lock(&ft->lock); + idr_remove(ft->idr, fp->volatile_id); + write_unlock(&ft->lock); +} + +static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + struct file *filp; + struct ksmbd_lock *smb_lock, *tmp_lock; + + fd_limit_close(); + __ksmbd_remove_durable_fd(fp); + __ksmbd_remove_fd(ft, fp); + + close_id_del_oplock(fp); + filp = fp->filp; + + __ksmbd_inode_close(fp); + if (!IS_ERR_OR_NULL(filp)) + fput(filp); + + /* because the reference count of fp is 0, it is guaranteed that + * there are not accesses to fp->lock_list. + */ + list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { + spin_lock(&fp->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&fp->conn->llist_lock); + + list_del(&smb_lock->flist); + locks_free_lock(smb_lock->fl); + kfree(smb_lock); + } + + if (ksmbd_stream_fd(fp)) + kfree(fp->stream.name); + kmem_cache_free(filp_cache, fp); +} + +static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) +{ + if (!atomic_inc_not_zero(&fp->refcount)) + return NULL; + return fp; +} + +static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, + u64 id) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) + return NULL; + + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) + fp = ksmbd_fp_get(fp); + read_unlock(&ft->lock); + return fp; +} + +static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + __ksmbd_close_fd(&work->sess->file_table, fp); + atomic_dec(&work->conn->stats.open_files_count); +} + +static void set_close_state_blocked_works(struct ksmbd_file *fp) +{ + struct ksmbd_work *cancel_work; + + spin_lock(&fp->f_lock); + list_for_each_entry(cancel_work, &fp->blocked_works, + fp_entry) { + cancel_work->state = KSMBD_WORK_CLOSED; + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + spin_unlock(&fp->f_lock); +} + +int ksmbd_close_fd(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; + + if (!has_file_id(id)) + return 0; + + ft = &work->sess->file_table; + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) { + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } + read_unlock(&ft->lock); + + if (!fp) + return -EINVAL; + + __put_fd_final(work, fp); + return 0; +} + +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!fp) + return; + + if (!atomic_dec_and_test(&fp->refcount)) + return; + __put_fd_final(work, fp); +} + +static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) +{ + if (!fp) + return false; + if (fp->tcon != tcon) + return false; + return true; +} + +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) +{ + return __ksmbd_lookup_fd(&work->sess->file_table, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + + if (__sanity_check(work->tcon, fp)) + return fp; + + ksmbd_fd_put(work, fp); + return NULL; +} + +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) { + id = work->compound_fid; + pid = work->compound_pfid; + } + + fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + if (!__sanity_check(work->tcon, fp)) { + ksmbd_fd_put(work, fp); + return NULL; + } + if (fp->persistent_id != pid) { + ksmbd_fd_put(work, fp); + return NULL; + } + return fp; +} + +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) +{ + return __ksmbd_lookup_fd(&global_ft, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->create_guid, + cguid, + SMB2_CREATE_GUID_SIZE)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&global_ft.lock); + + return fp; +} + +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) +{ + struct ksmbd_file *lfp; + struct ksmbd_inode *ci; + + ci = ksmbd_inode_lookup_by_vfsinode(inode); + if (!ci) + return NULL; + + read_lock(&ci->m_lock); + list_for_each_entry(lfp, &ci->m_fp_list, node) { + if (inode == file_inode(lfp->filp)) { + atomic_dec(&ci->m_count); + lfp = ksmbd_fp_get(lfp); + read_unlock(&ci->m_lock); + return lfp; + } + } + atomic_dec(&ci->m_count); + read_unlock(&ci->m_lock); + return NULL; +} + +#define OPEN_ID_TYPE_VOLATILE_ID (0) +#define OPEN_ID_TYPE_PERSISTENT_ID (1) + +static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) +{ + if (type == OPEN_ID_TYPE_VOLATILE_ID) + fp->volatile_id = id; + if (type == OPEN_ID_TYPE_PERSISTENT_ID) + fp->persistent_id = id; +} + +static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + int type) +{ + u64 id = 0; + int ret; + + if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { + __open_id_set(fp, KSMBD_NO_FID, type); + return -EMFILE; + } + + idr_preload(GFP_KERNEL); + write_lock(&ft->lock); + ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); + if (ret >= 0) { + id = ret; + ret = 0; + } else { + id = KSMBD_NO_FID; + fd_limit_close(); + } + + __open_id_set(fp, id, type); + write_unlock(&ft->lock); + idr_preload_end(); + return ret; +} + +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) +{ + __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); + return fp->persistent_id; +} + +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) +{ + struct ksmbd_file *fp; + int ret; + + fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); + if (!fp) { + pr_err("Failed to allocate memory\n"); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&fp->blocked_works); + INIT_LIST_HEAD(&fp->node); + INIT_LIST_HEAD(&fp->lock_list); + spin_lock_init(&fp->f_lock); + atomic_set(&fp->refcount, 1); + + fp->filp = filp; + fp->conn = work->conn; + fp->tcon = work->tcon; + fp->volatile_id = KSMBD_NO_FID; + fp->persistent_id = KSMBD_NO_FID; + fp->f_ci = ksmbd_inode_get(fp); + + if (!fp->f_ci) { + ret = -ENOMEM; + goto err_out; + } + + ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (ret) { + ksmbd_inode_put(fp->f_ci); + goto err_out; + } + + atomic_inc(&work->conn->stats.open_files_count); + return fp; + +err_out: + kmem_cache_free(filp_cache, fp); + return ERR_PTR(ret); +} + +static int +__close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, + bool (*skip)(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp)) +{ + unsigned int id; + struct ksmbd_file *fp; + int num = 0; + + idr_for_each_entry(ft->idr, fp, id) { + if (skip(tcon, fp)) + continue; + + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + continue; + __ksmbd_close_fd(ft, fp); + num++; + } + return num; +} + +static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return fp->tcon != tcon; +} + +static bool session_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return false; +} + +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + tree_conn_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +void ksmbd_close_session_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + session_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +int ksmbd_init_global_file_table(void) +{ + return ksmbd_init_file_table(&global_ft); +} + +void ksmbd_free_global_file_table(void) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + idr_for_each_entry(global_ft.idr, fp, id) { + __ksmbd_remove_durable_fd(fp); + kmem_cache_free(filp_cache, fp); + } + + ksmbd_destroy_file_table(&global_ft); +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft) +{ + ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (!ft->idr) + return -ENOMEM; + + idr_init(ft->idr); + rwlock_init(&ft->lock); + return 0; +} + +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) +{ + if (!ft->idr) + return; + + __close_file_table_ids(ft, NULL, session_fd_check); + idr_destroy(ft->idr); + kfree(ft->idr); + ft->idr = NULL; +} + +int ksmbd_init_file_cache(void) +{ + filp_cache = kmem_cache_create("ksmbd_file_cache", + sizeof(struct ksmbd_file), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!filp_cache) + goto out; + + return 0; + +out: + pr_err("failed to allocate file cache\n"); + return -ENOMEM; +} + +void ksmbd_exit_file_cache(void) +{ + kmem_cache_destroy(filp_cache); +} diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h new file mode 100644 index 000000000000..fcb13413fa8d --- /dev/null +++ b/fs/smb/server/vfs_cache.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __VFS_CACHE_H__ +#define __VFS_CACHE_H__ + +#include +#include +#include +#include +#include +#include + +#include "vfs.h" + +/* Windows style file permissions for extended response */ +#define FILE_GENERIC_ALL 0x1F01FF +#define FILE_GENERIC_READ 0x120089 +#define FILE_GENERIC_WRITE 0x120116 +#define FILE_GENERIC_EXECUTE 0X1200a0 + +#define KSMBD_START_FID 0 +#define KSMBD_NO_FID (INT_MAX) +#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) + +struct ksmbd_conn; +struct ksmbd_session; + +struct ksmbd_lock { + struct file_lock *fl; + struct list_head clist; + struct list_head flist; + struct list_head llist; + unsigned int flags; + int cmd; + int zero_len; + unsigned long long start; + unsigned long long end; +}; + +struct stream { + char *name; + ssize_t size; +}; + +struct ksmbd_inode { + rwlock_t m_lock; + atomic_t m_count; + atomic_t op_count; + /* opinfo count for streams */ + atomic_t sop_count; + struct inode *m_inode; + unsigned int m_flags; + struct hlist_node m_hash; + struct list_head m_fp_list; + struct list_head m_op_list; + struct oplock_info *m_opinfo; + __le32 m_fattr; +}; + +struct ksmbd_file { + struct file *filp; + u64 persistent_id; + u64 volatile_id; + + spinlock_t f_lock; + + struct ksmbd_inode *f_ci; + struct ksmbd_inode *f_parent_ci; + struct oplock_info __rcu *f_opinfo; + struct ksmbd_conn *conn; + struct ksmbd_tree_connect *tcon; + + atomic_t refcount; + __le32 daccess; + __le32 saccess; + __le32 coption; + __le32 cdoption; + __u64 create_time; + __u64 itime; + + bool is_nt_open; + bool attrib_only; + + char client_guid[16]; + char create_guid[16]; + char app_instance_id[16]; + + struct stream stream; + struct list_head node; + struct list_head blocked_works; + struct list_head lock_list; + + int durable_timeout; + + /* if ls is happening on directory, below is valid*/ + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; +}; + +static inline void set_ctx_actor(struct dir_context *ctx, + filldir_t actor) +{ + ctx->actor = actor; +} + +#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG + +struct ksmbd_file_table { + rwlock_t lock; + struct idr *idr; +}; + +static inline bool has_file_id(u64 id) +{ + return id < KSMBD_NO_FID; +} + +static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) +{ + return fp->stream.name != NULL; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); +int ksmbd_close_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid); +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); +void ksmbd_close_session_fds(struct ksmbd_work *work); +int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); +int ksmbd_init_global_file_table(void); +void ksmbd_free_global_file_table(void); +void ksmbd_set_fd_limit(unsigned long limit); + +/* + * INODE hash + */ +int __init ksmbd_inode_hash_init(void); +void ksmbd_release_inode_hash(void); + +enum KSMBD_INODE_STATUS { + KSMBD_INODE_STATUS_OK, + KSMBD_INODE_STATUS_UNKNOWN, + KSMBD_INODE_STATUS_PENDING_DELETE, +}; + +int ksmbd_query_inode_status(struct inode *inode); +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info); +int ksmbd_init_file_cache(void); +void ksmbd_exit_file_cache(void); +#endif /* __VFS_CACHE_H__ */ diff --git a/fs/smb/server/xattr.h b/fs/smb/server/xattr.h new file mode 100644 index 000000000000..16499ca5c82d --- /dev/null +++ b/fs/smb/server/xattr.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + */ + +#ifndef __XATTR_H__ +#define __XATTR_H__ + +/* + * These are on-disk structures to store additional metadata into xattr to + * reproduce windows filesystem semantics. And they are encoded with NDR to + * compatible with samba's xattr meta format. The compatibility with samba + * is important because it can lose the information(file attribute, + * creation time, acls) about the existing files when switching between + * ksmbd and samba. + */ + +/* + * Dos attribute flags used for what variable is valid. + */ +enum { + XATTR_DOSINFO_ATTRIB = 0x00000001, + XATTR_DOSINFO_EA_SIZE = 0x00000002, + XATTR_DOSINFO_SIZE = 0x00000004, + XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, + XATTR_DOSINFO_CREATE_TIME = 0x00000010, + XATTR_DOSINFO_CHANGE_TIME = 0x00000020, + XATTR_DOSINFO_ITIME = 0x00000040 +}; + +/* + * Dos attribute structure which is compatible with samba's one. + * Storing it into the xattr named "DOSATTRIB" separately from inode + * allows ksmbd to faithfully reproduce windows filesystem semantics + * on top of a POSIX filesystem. + */ +struct xattr_dos_attrib { + __u16 version; /* version 3 or version 4 */ + __u32 flags; /* valid flags */ + __u32 attr; /* Dos attribute */ + __u32 ea_size; /* EA size */ + __u64 size; + __u64 alloc_size; + __u64 create_time; /* File creation time */ + __u64 change_time; /* File change time */ + __u64 itime; /* Invented/Initial time */ +}; + +/* + * Enumeration is used for computing posix acl hash. + */ +enum { + SMB_ACL_TAG_INVALID = 0, + SMB_ACL_USER, + SMB_ACL_USER_OBJ, + SMB_ACL_GROUP, + SMB_ACL_GROUP_OBJ, + SMB_ACL_OTHER, + SMB_ACL_MASK +}; + +#define SMB_ACL_READ 4 +#define SMB_ACL_WRITE 2 +#define SMB_ACL_EXECUTE 1 + +struct xattr_acl_entry { + int type; + uid_t uid; + gid_t gid; + mode_t perm; +}; + +/* + * xattr_smb_acl structure is used for computing posix acl hash. + */ +struct xattr_smb_acl { + int count; + int next; + struct xattr_acl_entry entries[]; +}; + +/* 64bytes hash in xattr_ntacl is computed with sha256 */ +#define XATTR_SD_HASH_TYPE_SHA256 0x1 +#define XATTR_SD_HASH_SIZE 64 + +/* + * xattr_ntacl is used for storing ntacl and hashes. + * Hash is used for checking valid posix acl and ntacl in xattr. + */ +struct xattr_ntacl { + __u16 version; /* version 4*/ + void *sd_buf; + __u32 sd_size; + __u16 hash_type; /* hash type */ + __u8 desc[10]; /* posix_acl description */ + __u16 desc_len; + __u64 current_time; + __u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */ + __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ +}; + +/* DOS ATTRIBUITE XATTR PREFIX */ +#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" +#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) +#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) +#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ + (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) + +/* STREAM XATTR PREFIX */ +#define STREAM_PREFIX "DosStream." +#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) +#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) +#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) + +/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */ +#define SD_PREFIX "NTACL" +#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) +#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX) +#define XATTR_NAME_SD_LEN \ + (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) + +#endif /* __XATTR_H__ */ diff --git a/fs/smbfs_common/Makefile b/fs/smbfs_common/Makefile deleted file mode 100644 index cafc61a3bfc3..000000000000 --- a/fs/smbfs_common/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for Linux filesystem routines that are shared by client and server. -# - -obj-$(CONFIG_SMBFS_COMMON) += cifs_arc4.o -obj-$(CONFIG_SMBFS_COMMON) += cifs_md4.o diff --git a/fs/smbfs_common/arc4.h b/fs/smbfs_common/arc4.h deleted file mode 100644 index 12e71ec033a1..000000000000 --- a/fs/smbfs_common/arc4.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Common values for ARC4 Cipher Algorithm - */ - -#ifndef _CRYPTO_ARC4_H -#define _CRYPTO_ARC4_H - -#include - -#define ARC4_MIN_KEY_SIZE 1 -#define ARC4_MAX_KEY_SIZE 256 -#define ARC4_BLOCK_SIZE 1 - -struct arc4_ctx { - u32 S[256]; - u32 x, y; -}; - -int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len); -void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len); - -#endif /* _CRYPTO_ARC4_H */ diff --git a/fs/smbfs_common/cifs_arc4.c b/fs/smbfs_common/cifs_arc4.c deleted file mode 100644 index 043e4cb839fa..000000000000 --- a/fs/smbfs_common/cifs_arc4.c +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API - * - * ARC4 Cipher Algorithm - * - * Jon Oberheide - */ - -#include -#include "arc4.h" - -MODULE_LICENSE("GPL"); - -int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len) -{ - int i, j = 0, k = 0; - - ctx->x = 1; - ctx->y = 0; - - for (i = 0; i < 256; i++) - ctx->S[i] = i; - - for (i = 0; i < 256; i++) { - u32 a = ctx->S[i]; - - j = (j + in_key[k] + a) & 0xff; - ctx->S[i] = ctx->S[j]; - ctx->S[j] = a; - if (++k >= key_len) - k = 0; - } - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_arc4_setkey); - -void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len) -{ - u32 *const S = ctx->S; - u32 x, y, a, b; - u32 ty, ta, tb; - - if (len == 0) - return; - - x = ctx->x; - y = ctx->y; - - a = S[x]; - y = (y + a) & 0xff; - b = S[y]; - - do { - S[y] = a; - a = (a + b) & 0xff; - S[x] = b; - x = (x + 1) & 0xff; - ta = S[x]; - ty = (y + ta) & 0xff; - tb = S[ty]; - *out++ = *in++ ^ S[a]; - if (--len == 0) - break; - y = ty; - a = ta; - b = tb; - } while (true); - - ctx->x = x; - ctx->y = y; -} -EXPORT_SYMBOL_GPL(cifs_arc4_crypt); diff --git a/fs/smbfs_common/cifs_md4.c b/fs/smbfs_common/cifs_md4.c deleted file mode 100644 index 50f78cfc6ce9..000000000000 --- a/fs/smbfs_common/cifs_md4.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Cryptographic API. - * - * MD4 Message Digest Algorithm (RFC1320). - * - * Implementation derived from Andrew Tridgell and Steve French's - * CIFS MD4 implementation, and the cryptoapi implementation - * originally based on the public domain implementation written - * by Colin Plumb in 1993. - * - * Copyright (c) Andrew Tridgell 1997-1998. - * Modified by Steve French (sfrench@us.ibm.com) 2002 - * Copyright (c) Cryptoapi developers. - * Copyright (c) 2002 David S. Miller (davem@redhat.com) - * Copyright (c) 2002 James Morris - * - */ -#include -#include -#include -#include -#include -#include -#include "md4.h" - -MODULE_LICENSE("GPL"); - -static inline u32 lshift(u32 x, unsigned int s) -{ - x &= 0xFFFFFFFF; - return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); -} - -static inline u32 F(u32 x, u32 y, u32 z) -{ - return (x & y) | ((~x) & z); -} - -static inline u32 G(u32 x, u32 y, u32 z) -{ - return (x & y) | (x & z) | (y & z); -} - -static inline u32 H(u32 x, u32 y, u32 z) -{ - return x ^ y ^ z; -} - -#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s)) -#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s)) -#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s)) - -static void md4_transform(u32 *hash, u32 const *in) -{ - u32 a, b, c, d; - - a = hash[0]; - b = hash[1]; - c = hash[2]; - d = hash[3]; - - ROUND1(a, b, c, d, in[0], 3); - ROUND1(d, a, b, c, in[1], 7); - ROUND1(c, d, a, b, in[2], 11); - ROUND1(b, c, d, a, in[3], 19); - ROUND1(a, b, c, d, in[4], 3); - ROUND1(d, a, b, c, in[5], 7); - ROUND1(c, d, a, b, in[6], 11); - ROUND1(b, c, d, a, in[7], 19); - ROUND1(a, b, c, d, in[8], 3); - ROUND1(d, a, b, c, in[9], 7); - ROUND1(c, d, a, b, in[10], 11); - ROUND1(b, c, d, a, in[11], 19); - ROUND1(a, b, c, d, in[12], 3); - ROUND1(d, a, b, c, in[13], 7); - ROUND1(c, d, a, b, in[14], 11); - ROUND1(b, c, d, a, in[15], 19); - - ROUND2(a, b, c, d, in[0], 3); - ROUND2(d, a, b, c, in[4], 5); - ROUND2(c, d, a, b, in[8], 9); - ROUND2(b, c, d, a, in[12], 13); - ROUND2(a, b, c, d, in[1], 3); - ROUND2(d, a, b, c, in[5], 5); - ROUND2(c, d, a, b, in[9], 9); - ROUND2(b, c, d, a, in[13], 13); - ROUND2(a, b, c, d, in[2], 3); - ROUND2(d, a, b, c, in[6], 5); - ROUND2(c, d, a, b, in[10], 9); - ROUND2(b, c, d, a, in[14], 13); - ROUND2(a, b, c, d, in[3], 3); - ROUND2(d, a, b, c, in[7], 5); - ROUND2(c, d, a, b, in[11], 9); - ROUND2(b, c, d, a, in[15], 13); - - ROUND3(a, b, c, d, in[0], 3); - ROUND3(d, a, b, c, in[8], 9); - ROUND3(c, d, a, b, in[4], 11); - ROUND3(b, c, d, a, in[12], 15); - ROUND3(a, b, c, d, in[2], 3); - ROUND3(d, a, b, c, in[10], 9); - ROUND3(c, d, a, b, in[6], 11); - ROUND3(b, c, d, a, in[14], 15); - ROUND3(a, b, c, d, in[1], 3); - ROUND3(d, a, b, c, in[9], 9); - ROUND3(c, d, a, b, in[5], 11); - ROUND3(b, c, d, a, in[13], 15); - ROUND3(a, b, c, d, in[3], 3); - ROUND3(d, a, b, c, in[11], 9); - ROUND3(c, d, a, b, in[7], 11); - ROUND3(b, c, d, a, in[15], 15); - - hash[0] += a; - hash[1] += b; - hash[2] += c; - hash[3] += d; -} - -static inline void md4_transform_helper(struct md4_ctx *ctx) -{ - le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block)); - md4_transform(ctx->hash, ctx->block); -} - -int cifs_md4_init(struct md4_ctx *mctx) -{ - memset(mctx, 0, sizeof(struct md4_ctx)); - mctx->hash[0] = 0x67452301; - mctx->hash[1] = 0xefcdab89; - mctx->hash[2] = 0x98badcfe; - mctx->hash[3] = 0x10325476; - mctx->byte_count = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_md4_init); - -int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len) -{ - const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); - - mctx->byte_count += len; - - if (avail > len) { - memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), - data, len); - return 0; - } - - memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), - data, avail); - - md4_transform_helper(mctx); - data += avail; - len -= avail; - - while (len >= sizeof(mctx->block)) { - memcpy(mctx->block, data, sizeof(mctx->block)); - md4_transform_helper(mctx); - data += sizeof(mctx->block); - len -= sizeof(mctx->block); - } - - memcpy(mctx->block, data, len); - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_md4_update); - -int cifs_md4_final(struct md4_ctx *mctx, u8 *out) -{ - const unsigned int offset = mctx->byte_count & 0x3f; - char *p = (char *)mctx->block + offset; - int padding = 56 - (offset + 1); - - *p++ = 0x80; - if (padding < 0) { - memset(p, 0x00, padding + sizeof(u64)); - md4_transform_helper(mctx); - p = (char *)mctx->block; - padding = 56; - } - - memset(p, 0, padding); - mctx->block[14] = mctx->byte_count << 3; - mctx->block[15] = mctx->byte_count >> 29; - le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - - sizeof(u64)) / sizeof(u32)); - md4_transform(mctx->hash, mctx->block); - cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash)); - memcpy(out, mctx->hash, sizeof(mctx->hash)); - memset(mctx, 0, sizeof(*mctx)); - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_md4_final); diff --git a/fs/smbfs_common/md4.h b/fs/smbfs_common/md4.h deleted file mode 100644 index 5337becc699a..000000000000 --- a/fs/smbfs_common/md4.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Common values for ARC4 Cipher Algorithm - */ - -#ifndef _CIFS_MD4_H -#define _CIFS_MD4_H - -#include - -#define MD4_DIGEST_SIZE 16 -#define MD4_HMAC_BLOCK_SIZE 64 -#define MD4_BLOCK_WORDS 16 -#define MD4_HASH_WORDS 4 - -struct md4_ctx { - u32 hash[MD4_HASH_WORDS]; - u32 block[MD4_BLOCK_WORDS]; - u64 byte_count; -}; - - -int cifs_md4_init(struct md4_ctx *mctx); -int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len); -int cifs_md4_final(struct md4_ctx *mctx, u8 *out); - -#endif /* _CIFS_MD4_H */ diff --git a/fs/smbfs_common/smb2pdu.h b/fs/smbfs_common/smb2pdu.h deleted file mode 100644 index 7d605db3bb3b..000000000000 --- a/fs/smbfs_common/smb2pdu.h +++ /dev/null @@ -1,1702 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -#ifndef _COMMON_SMB2PDU_H -#define _COMMON_SMB2PDU_H - -/* - * Note that, due to trying to use names similar to the protocol specifications, - * there are many mixed case field names in the structures below. Although - * this does not match typical Linux kernel style, it is necessary to be - * able to match against the protocol specfication. - * - * SMB2 commands - * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses - * (ie no useful data other than the SMB error code itself) and are marked such. - * Knowing this helps avoid response buffer allocations and copy in some cases. - */ - -/* List of commands in host endian */ -#define SMB2_NEGOTIATE_HE 0x0000 -#define SMB2_SESSION_SETUP_HE 0x0001 -#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ -#define SMB2_TREE_CONNECT_HE 0x0003 -#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ -#define SMB2_CREATE_HE 0x0005 -#define SMB2_CLOSE_HE 0x0006 -#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ -#define SMB2_READ_HE 0x0008 -#define SMB2_WRITE_HE 0x0009 -#define SMB2_LOCK_HE 0x000A -#define SMB2_IOCTL_HE 0x000B -#define SMB2_CANCEL_HE 0x000C -#define SMB2_ECHO_HE 0x000D -#define SMB2_QUERY_DIRECTORY_HE 0x000E -#define SMB2_CHANGE_NOTIFY_HE 0x000F -#define SMB2_QUERY_INFO_HE 0x0010 -#define SMB2_SET_INFO_HE 0x0011 -#define SMB2_OPLOCK_BREAK_HE 0x0012 - -/* The same list in little endian */ -#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) -#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) -#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) -#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) -#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) -#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) -#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) -#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) -#define SMB2_READ cpu_to_le16(SMB2_READ_HE) -#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) -#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) -#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) -#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) -#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) -#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) -#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) -#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) -#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) -#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) - -#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF) - -#define NUMBER_OF_SMB2_COMMANDS 0x0013 - -/* - * Size of the session key (crypto key encrypted with the password - */ -#define SMB2_NTLMV2_SESSKEY_SIZE 16 -#define SMB2_SIGNATURE_SIZE 16 -#define SMB2_HMACSHA256_SIZE 32 -#define SMB2_CMACAES_SIZE 16 -#define SMB3_GCM128_CRYPTKEY_SIZE 16 -#define SMB3_GCM256_CRYPTKEY_SIZE 32 - -/* - * Size of the smb3 encryption/decryption keys - * This size is big enough to store any cipher key types. - */ -#define SMB3_ENC_DEC_KEY_SIZE 32 - -/* - * Size of the smb3 signing key - */ -#define SMB3_SIGN_KEY_SIZE 16 - -#define CIFS_CLIENT_CHALLENGE_SIZE 8 - -/* Maximum buffer size value we can send with 1 credit */ -#define SMB2_MAX_BUFFER_SIZE 65536 - -/* - * The default wsize is 1M for SMB2 (and for some CIFS cases). - * find_get_pages seems to return a maximum of 256 - * pages in a single call. With PAGE_SIZE == 4k, this means we can - * fill a single wsize request with a single call. - */ -#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) - -/* - * SMB2 Header Definition - * - * "MBZ" : Must be Zero - * "BB" : BugBug, Something to check/review/analyze later - * "PDU" : "Protocol Data Unit" (ie a network "frame") - * - */ - -#define __SMB2_HEADER_STRUCTURE_SIZE 64 -#define SMB2_HEADER_STRUCTURE_SIZE \ - cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) - -#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) -#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) -#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc) - -/* - * SMB2 flag definitions - */ -#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) -#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) -#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) -#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) -#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */ -#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) -#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */ - -/* - * Definitions for SMB2 Protocol Data Units (network frames) - * - * See MS-SMB2.PDF specification for protocol details. - * The Naming convention is the lower case version of the SMB2 - * command code name for the struct. Note that structures must be packed. - * - */ - -/* See MS-SMB2 section 2.2.1 */ -struct smb2_hdr { - __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ - __le16 StructureSize; /* 64 */ - __le16 CreditCharge; /* MBZ */ - __le32 Status; /* Error from server */ - __le16 Command; - __le16 CreditRequest; /* CreditResponse */ - __le32 Flags; - __le32 NextCommand; - __le64 MessageId; - union { - struct { - __le32 ProcessId; - __le32 TreeId; - } __packed SyncId; - __le64 AsyncId; - } __packed Id; - __le64 SessionId; - __u8 Signature[16]; -} __packed; - -struct smb2_pdu { - struct smb2_hdr hdr; - __le16 StructureSize2; /* size of wct area (varies, request specific) */ -} __packed; - -#define SMB2_ERROR_STRUCTURE_SIZE2 9 -#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2) - -struct smb2_err_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; - __u8 ErrorContextCount; - __u8 Reserved; - __le32 ByteCount; /* even if zero, at least one byte follows */ - __u8 ErrorData[1]; /* variable length */ -} __packed; - -#define SMB3_AES_CCM_NONCE 11 -#define SMB3_AES_GCM_NONCE 12 - -/* Transform flags (for 3.0 dialect this flag indicates CCM */ -#define TRANSFORM_FLAG_ENCRYPTED 0x0001 -struct smb2_transform_hdr { - __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ - __u8 Signature[16]; - __u8 Nonce[16]; - __le32 OriginalMessageSize; - __u16 Reserved1; - __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */ - __le64 SessionId; -} __packed; - - -/* See MS-SMB2 2.2.42 */ -struct smb2_compression_transform_hdr_unchained { - __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ - __le32 OriginalCompressedSegmentSize; - __le16 CompressionAlgorithm; - __le16 Flags; - __le16 Length; /* if chained it is length, else offset */ -} __packed; - -/* See MS-SMB2 2.2.42.1 */ -#define SMB2_COMPRESSION_FLAG_NONE 0x0000 -#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 - -struct compression_payload_header { - __le16 CompressionAlgorithm; - __le16 Flags; - __le32 Length; /* length of compressed playload including field below if present */ - /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */ -} __packed; - -/* See MS-SMB2 2.2.42.2 */ -struct smb2_compression_transform_hdr_chained { - __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ - __le32 OriginalCompressedSegmentSize; - /* struct compression_payload_header[] */ -} __packed; - -/* See MS-SMB2 2.2.42.2.2 */ -struct compression_pattern_payload_v1 { - __le16 Pattern; - __le16 Reserved1; - __le16 Reserved2; - __le32 Repetitions; -} __packed; - -/* See MS-SMB2 section 2.2.9.2 */ -/* Context Types */ -#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000 -#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001) - -struct tree_connect_contexts { - __le16 ContextType; - __le16 DataLength; - __le32 Reserved; - __u8 Data[]; -} __packed; - -/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */ -struct smb3_blob_data { - __le16 BlobSize; - __u8 BlobData[]; -} __packed; - -/* Valid values for Attr */ -#define SE_GROUP_MANDATORY 0x00000001 -#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 -#define SE_GROUP_ENABLED 0x00000004 -#define SE_GROUP_OWNER 0x00000008 -#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 -#define SE_GROUP_INTEGRITY 0x00000020 -#define SE_GROUP_INTEGRITY_ENABLED 0x00000040 -#define SE_GROUP_RESOURCE 0x20000000 -#define SE_GROUP_LOGON_ID 0xC0000000 - -/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ - -struct sid_array_data { - __le16 SidAttrCount; - /* SidAttrList - array of sid_attr_data structs */ -} __packed; - -struct luid_attr_data { - -} __packed; - -/* - * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5 - * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA - */ - -struct privilege_array_data { - __le16 PrivilegeCount; - /* array of privilege_data structs */ -} __packed; - -struct remoted_identity_tcon_context { - __le16 TicketType; /* must be 0x0001 */ - __le16 TicketSize; /* total size of this struct */ - __le16 User; /* offset to SID_ATTR_DATA struct with user info */ - __le16 UserName; /* offset to null terminated Unicode username string */ - __le16 Domain; /* offset to null terminated Unicode domain name */ - __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */ - __le16 RestrictedGroups; /* similar to above */ - __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */ - __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */ - __le16 Owner; /* offset to BLOB_DATA struct */ - __le16 DefaultDacl; /* offset to BLOB_DATA struct */ - __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */ - __le16 UserClaims; /* offset to BLOB_DATA struct */ - __le16 DeviceClaims; /* offset to BLOB_DATA struct */ - __u8 TicketInfo[]; /* variable length buf - remoted identity data */ -} __packed; - -struct smb2_tree_connect_req_extension { - __le32 TreeConnectContextOffset; - __le16 TreeConnectContextCount; - __u8 Reserved[10]; - __u8 PathName[]; /* variable sized array */ - /* followed by array of TreeConnectContexts */ -} __packed; - -/* Flags/Reserved for SMB3.1.1 */ -#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001) -#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002) -#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004) - -struct smb2_tree_connect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 Flags; /* Flags in SMB3.1.1 */ - __le16 PathOffset; - __le16 PathLength; - __u8 Buffer[1]; /* variable length */ -} __packed; - -/* Possible ShareType values */ -#define SMB2_SHARE_TYPE_DISK 0x01 -#define SMB2_SHARE_TYPE_PIPE 0x02 -#define SMB2_SHARE_TYPE_PRINT 0x03 - -/* - * Possible ShareFlags - exactly one and only one of the first 4 caching flags - * must be set (any of the remaining, SHI1005, flags may be set individually - * or in combination. - */ -#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 -#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 -#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 -#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 -#define SHI1005_FLAGS_DFS 0x00000001 -#define SHI1005_FLAGS_DFS_ROOT 0x00000002 -#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 -#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 -#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 -#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 -#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 -#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 -#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 -#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 -#define SMB2_SHAREFLAG_IDENTITY_REMOTING 0x00040000 /* 3.1.1 */ -#define SMB2_SHAREFLAG_COMPRESS_DATA 0x00100000 /* 3.1.1 */ -#define SHI1005_FLAGS_ALL 0x0014FF33 - -/* Possible share capabilities */ -#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ -#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ -#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ -#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ -#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ -#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */ - -struct smb2_tree_connect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 16 */ - __u8 ShareType; /* see below */ - __u8 Reserved; - __le32 ShareFlags; /* see below */ - __le32 Capabilities; /* see below */ - __le32 MaximalAccess; -} __packed; - -struct smb2_tree_disconnect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_tree_disconnect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - - -/* - * SMB2_NEGOTIATE_PROTOCOL See MS-SMB2 section 2.2.3 - */ -/* SecurityMode flags */ -#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001 -#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) -#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 -#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) -#define SMB2_SEC_MODE_FLAGS_ALL 0x0003 - -/* Capabilities flags */ -#define SMB2_GLOBAL_CAP_DFS 0x00000001 -#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ -/* Internal types */ -#define SMB2_NT_FIND 0x00100000 -#define SMB2_LARGE_FILES 0x00200000 - -#define SMB2_CLIENT_GUID_SIZE 16 -#define SMB2_CREATE_GUID_SIZE 16 - -/* Dialects */ -#define SMB10_PROT_ID 0x0000 /* local only, not sent on wire w/CIFS negprot */ -#define SMB20_PROT_ID 0x0202 -#define SMB21_PROT_ID 0x0210 -#define SMB2X_PROT_ID 0x02FF -#define SMB30_PROT_ID 0x0300 -#define SMB302_PROT_ID 0x0302 -#define SMB311_PROT_ID 0x0311 -#define BAD_PROT_ID 0xFFFF - -#define SMB311_SALT_SIZE 32 -/* Hash Algorithm Types */ -#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) -#define SMB2_PREAUTH_HASH_SIZE 64 - -/* Negotiate Contexts - ContextTypes. See MS-SMB2 section 2.2.3.1 for details */ -#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) -#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) -#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) -#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) -#define SMB2_TRANSPORT_CAPABILITIES cpu_to_le16(6) -#define SMB2_RDMA_TRANSFORM_CAPABILITIES cpu_to_le16(7) -#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) -#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) - -struct smb2_neg_context { - __le16 ContextType; - __le16 DataLength; - __le32 Reserved; - /* Followed by array of data. NOTE: some servers require padding to 8 byte boundary */ -} __packed; - -/* - * SaltLength that the server send can be zero, so the only three required - * fields (all __le16) end up six bytes total, so the minimum context data len - * in the response is six bytes which accounts for - * - * HashAlgorithmCount, SaltLength, and 1 HashAlgorithm. - */ -#define MIN_PREAUTH_CTXT_DATA_LEN 6 - -struct smb2_preauth_neg_context { - __le16 ContextType; /* 1 */ - __le16 DataLength; - __le32 Reserved; - __le16 HashAlgorithmCount; /* 1 */ - __le16 SaltLength; - __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ - __u8 Salt[SMB311_SALT_SIZE]; -} __packed; - -/* Encryption Algorithms Ciphers */ -#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) -#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) -#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) -#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) - -/* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */ -#define MIN_ENCRYPT_CTXT_DATA_LEN 4 -struct smb2_encryption_neg_context { - __le16 ContextType; /* 2 */ - __le16 DataLength; - __le32 Reserved; - /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ - __le16 CipherCount; /* AES128-GCM and AES128-CCM by default */ - __le16 Ciphers[]; -} __packed; - -/* See MS-SMB2 2.2.3.1.3 */ -#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) -#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) -#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) -#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) -/* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */ -#define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */ - -/* Compression Flags */ -#define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000) -#define SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED cpu_to_le32(0x00000001) - -struct smb2_compression_capabilities_context { - __le16 ContextType; /* 3 */ - __le16 DataLength; - __le32 Reserved; - __le16 CompressionAlgorithmCount; - __le16 Padding; - __le32 Flags; - __le16 CompressionAlgorithms[3]; - __u16 Pad; /* Some servers require pad to DataLen multiple of 8 */ - /* Check if pad needed */ -} __packed; - -/* - * For smb2_netname_negotiate_context_id See MS-SMB2 2.2.3.1.4. - * Its struct simply contains NetName, an array of Unicode characters - */ -struct smb2_netname_neg_context { - __le16 ContextType; /* 5 */ - __le16 DataLength; - __le32 Reserved; - __le16 NetName[]; /* hostname of target converted to UCS-2 */ -} __packed; - -/* - * For smb2_transport_capabilities context see MS-SMB2 2.2.3.1.5 - * and 2.2.4.1.5 - */ - -/* Flags */ -#define SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY 0x00000001 - -struct smb2_transport_capabilities_context { - __le16 ContextType; /* 6 */ - __le16 DataLength; - __u32 Reserved; - __le32 Flags; - __u32 Pad; -} __packed; - -/* - * For rdma transform capabilities context see MS-SMB2 2.2.3.1.6 - * and 2.2.4.1.6 - */ - -/* RDMA Transform IDs */ -#define SMB2_RDMA_TRANSFORM_NONE 0x0000 -#define SMB2_RDMA_TRANSFORM_ENCRYPTION 0x0001 -#define SMB2_RDMA_TRANSFORM_SIGNING 0x0002 - -struct smb2_rdma_transform_capabilities_context { - __le16 ContextType; /* 7 */ - __le16 DataLength; - __u32 Reserved; - __le16 TransformCount; - __u16 Reserved1; - __u32 Reserved2; - __le16 RDMATransformIds[]; -} __packed; - -/* - * For signing capabilities context see MS-SMB2 2.2.3.1.7 - * and 2.2.4.1.7 - */ - -/* Signing algorithms */ -#define SIGNING_ALG_HMAC_SHA256 0 -#define SIGNING_ALG_HMAC_SHA256_LE cpu_to_le16(0) -#define SIGNING_ALG_AES_CMAC 1 -#define SIGNING_ALG_AES_CMAC_LE cpu_to_le16(1) -#define SIGNING_ALG_AES_GMAC 2 -#define SIGNING_ALG_AES_GMAC_LE cpu_to_le16(2) - -struct smb2_signing_capabilities { - __le16 ContextType; /* 8 */ - __le16 DataLength; - __le32 Reserved; - __le16 SigningAlgorithmCount; - __le16 SigningAlgorithms[]; - /* Followed by padding to 8 byte boundary (required by some servers) */ -} __packed; - -#define POSIX_CTXT_DATA_LEN 16 -struct smb2_posix_neg_context { - __le16 ContextType; /* 0x100 */ - __le16 DataLength; - __le32 Reserved; - __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ -} __packed; - -struct smb2_negotiate_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 36 */ - __le16 DialectCount; - __le16 SecurityMode; - __le16 Reserved; /* MBZ */ - __le32 Capabilities; - __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; - /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ - __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ - __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ - __le16 Reserved2; - __le16 Dialects[]; -} __packed; - -struct smb2_negotiate_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 65 */ - __le16 SecurityMode; - __le16 DialectRevision; - __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ - __u8 ServerGUID[16]; - __le32 Capabilities; - __le32 MaxTransactSize; - __le32 MaxReadSize; - __le32 MaxWriteSize; - __le64 SystemTime; /* MBZ */ - __le64 ServerStartTime; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - - -/* - * SMB2_SESSION_SETUP See MS-SMB2 section 2.2.5 - */ -/* Flags */ -#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 -#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 - -struct smb2_sess_setup_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 25 */ - __u8 Flags; - __u8 SecurityMode; - __le32 Capabilities; - __le32 Channel; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le64 PreviousSessionId; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -/* Currently defined SessionFlags */ -#define SMB2_SESSION_FLAG_IS_GUEST 0x0001 -#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) -#define SMB2_SESSION_FLAG_IS_NULL 0x0002 -#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) -#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004 -#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) - -struct smb2_sess_setup_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 SessionFlags; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - - -/* - * SMB2_LOGOFF See MS-SMB2 section 2.2.7 - */ -struct smb2_logoff_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_logoff_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - - -/* - * SMB2_CLOSE See MS-SMB2 section 2.2.15 - */ -/* Currently defined values for close flags */ -#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) -struct smb2_close_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Flags; - __le32 Reserved; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ -} __packed; - -/* - * Maximum size of a SMB2_CLOSE response is 64 (smb2 header) + 60 (data) - */ -#define MAX_SMB2_CLOSE_RESPONSE_SIZE 124 - -struct smb2_close_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* 60 */ - __le16 Flags; - __le32 Reserved; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ - __le64 EndOfFile; - __le32 Attributes; -} __packed; - - -/* - * SMB2_READ See MS-SMB2 section 2.2.19 - */ -/* For read request Flags field below, following flag is defined for SMB3.02 */ -#define SMB2_READFLAG_READ_UNBUFFERED 0x01 -#define SMB2_READFLAG_REQUEST_COMPRESSED 0x02 /* See MS-SMB2 2.2.19 */ - -/* Channel field for read and write: exactly one of following flags can be set*/ -#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) -#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) -#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) -#define SMB2_CHANNEL_RDMA_TRANSFORM cpu_to_le32(0x00000003) - -/* SMB2 read request without RFC1001 length at the beginning */ -struct smb2_read_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __u8 Padding; /* offset from start of SMB2 header to place read */ - __u8 Flags; /* MBZ unless SMB3.02 or later */ - __le32 Length; - __le64 Offset; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 MinimumCount; - __le32 Channel; /* MBZ except for SMB3 or later */ - __le32 RemainingBytes; - __le16 ReadChannelInfoOffset; - __le16 ReadChannelInfoLength; - __u8 Buffer[1]; -} __packed; - -/* Read flags */ -#define SMB2_READFLAG_RESPONSE_NONE cpu_to_le32(0x00000000) -#define SMB2_READFLAG_RESPONSE_RDMA_TRANSFORM cpu_to_le32(0x00000001) - -struct smb2_read_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __le32 Flags; - __u8 Buffer[1]; -} __packed; - - -/* - * SMB2_WRITE See MS-SMB2 section 2.2.21 - */ -/* For write request Flags field below the following flags are defined: */ -#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 /* SMB2.1 or later */ -#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */ - -struct smb2_write_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __le16 DataOffset; /* offset from start of SMB2 header to write data */ - __le32 Length; - __le64 Offset; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ - __le32 Channel; /* MBZ unless SMB3.02 or later */ - __le32 RemainingBytes; - __le16 WriteChannelInfoOffset; - __le16 WriteChannelInfoLength; - __le32 Flags; - __u8 Buffer[1]; -} __packed; - -struct smb2_write_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __u32 Reserved2; - __u8 Buffer[1]; -} __packed; - - -/* - * SMB2_FLUSH See MS-SMB2 section 2.2.17 - */ -struct smb2_flush_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Reserved1; - __le32 Reserved2; - __u64 PersistentFileId; - __u64 VolatileFileId; -} __packed; - -struct smb2_flush_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; - __le16 Reserved; -} __packed; - -#define SMB2_LOCKFLAG_SHARED 0x0001 -#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002 -#define SMB2_LOCKFLAG_UNLOCK 0x0004 -#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 -#define SMB2_LOCKFLAG_MASK 0x0007 - -struct smb2_lock_element { - __le64 Offset; - __le64 Length; - __le32 Flags; - __le32 Reserved; -} __packed; - -struct smb2_lock_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 48 */ - __le16 LockCount; - /* - * The least significant four bits are the index, the other 28 bits are - * the lock sequence number (0 to 64). See MS-SMB2 2.2.26 - */ - __le32 LockSequenceNumber; - __u64 PersistentFileId; - __u64 VolatileFileId; - /* Followed by at least one */ - struct smb2_lock_element locks[1]; -} __packed; - -struct smb2_lock_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_echo_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __u16 Reserved; -} __packed; - -struct smb2_echo_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __u16 Reserved; -} __packed; - -/* - * Valid FileInformation classes for query directory - * - * Note that these are a subset of the (file) QUERY_INFO levels defined - * later in this file (but since QUERY_DIRECTORY uses equivalent numbers - * we do not redefine them here) - * - * FileDirectoryInfomation 0x01 - * FileFullDirectoryInformation 0x02 - * FileIdFullDirectoryInformation 0x26 - * FileBothDirectoryInformation 0x03 - * FileIdBothDirectoryInformation 0x25 - * FileNamesInformation 0x0C - * FileIdExtdDirectoryInformation 0x3C - */ - -/* search (query_directory) Flags field */ -#define SMB2_RESTART_SCANS 0x01 -#define SMB2_RETURN_SINGLE_ENTRY 0x02 -#define SMB2_INDEX_SPECIFIED 0x04 -#define SMB2_REOPEN 0x10 - -struct smb2_query_directory_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 33 */ - __u8 FileInformationClass; - __u8 Flags; - __le32 FileIndex; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le16 FileNameOffset; - __le16 FileNameLength; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - -struct smb2_query_directory_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - -/* - * Maximum number of iovs we need for a set-info request. - * The largest one is rename/hardlink - * [0] : struct smb2_set_info_req + smb2_file_[rename|link]_info - * [1] : path - * [2] : compound padding - */ -#define SMB2_SET_INFO_IOV_SIZE 3 - -struct smb2_set_info_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 33 */ - __u8 InfoType; - __u8 FileInfoClass; - __le32 BufferLength; - __le16 BufferOffset; - __u16 Reserved; - __le32 AdditionalInformation; - __u64 PersistentFileId; - __u64 VolatileFileId; - __u8 Buffer[1]; -} __packed; - -struct smb2_set_info_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 2 */ -} __packed; - -/* - * SMB2_NOTIFY See MS-SMB2 section 2.2.35 - */ -/* notify flags */ -#define SMB2_WATCH_TREE 0x0001 - -/* notify completion filter flags. See MS-FSCC 2.6 and MS-SMB2 2.2.35 */ -#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 -#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 -#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 -#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 -#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 -#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 -#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 -#define FILE_NOTIFY_CHANGE_EA 0x00000080 -#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 -#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 -#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 -#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 - -/* SMB2 Notify Action Flags */ -#define FILE_ACTION_ADDED 0x00000001 -#define FILE_ACTION_REMOVED 0x00000002 -#define FILE_ACTION_MODIFIED 0x00000003 -#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 -#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 -#define FILE_ACTION_ADDED_STREAM 0x00000006 -#define FILE_ACTION_REMOVED_STREAM 0x00000007 -#define FILE_ACTION_MODIFIED_STREAM 0x00000008 -#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 - -struct smb2_change_notify_req { - struct smb2_hdr hdr; - __le16 StructureSize; - __le16 Flags; - __le32 OutputBufferLength; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ - __le32 CompletionFilter; - __u32 Reserved; -} __packed; - -struct smb2_change_notify_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; /* array of file notify structs */ -} __packed; - - -/* - * SMB2_CREATE See MS-SMB2 section 2.2.13 - */ -/* Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF -/* Non-spec internal type */ -#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 - -/* Impersonation Levels. See MS-WPO section 9.7 and MSDN-IMPERS */ -#define IL_ANONYMOUS cpu_to_le32(0x00000000) -#define IL_IDENTIFICATION cpu_to_le32(0x00000001) -#define IL_IMPERSONATION cpu_to_le32(0x00000002) -#define IL_DELEGATE cpu_to_le32(0x00000003) - -/* File Attrubutes */ -#define FILE_ATTRIBUTE_READONLY 0x00000001 -#define FILE_ATTRIBUTE_HIDDEN 0x00000002 -#define FILE_ATTRIBUTE_SYSTEM 0x00000004 -#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 -#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 -#define FILE_ATTRIBUTE_NORMAL 0x00000080 -#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 -#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 -#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 -#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 -#define FILE_ATTRIBUTE_OFFLINE 0x00001000 -#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 -#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 -#define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 -#define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 -#define FILE_ATTRIBUTE__MASK 0x00007FB7 - -#define FILE_ATTRIBUTE_READONLY_LE cpu_to_le32(0x00000001) -#define FILE_ATTRIBUTE_HIDDEN_LE cpu_to_le32(0x00000002) -#define FILE_ATTRIBUTE_SYSTEM_LE cpu_to_le32(0x00000004) -#define FILE_ATTRIBUTE_DIRECTORY_LE cpu_to_le32(0x00000010) -#define FILE_ATTRIBUTE_ARCHIVE_LE cpu_to_le32(0x00000020) -#define FILE_ATTRIBUTE_NORMAL_LE cpu_to_le32(0x00000080) -#define FILE_ATTRIBUTE_TEMPORARY_LE cpu_to_le32(0x00000100) -#define FILE_ATTRIBUTE_SPARSE_FILE_LE cpu_to_le32(0x00000200) -#define FILE_ATTRIBUTE_REPARSE_POINT_LE cpu_to_le32(0x00000400) -#define FILE_ATTRIBUTE_COMPRESSED_LE cpu_to_le32(0x00000800) -#define FILE_ATTRIBUTE_OFFLINE_LE cpu_to_le32(0x00001000) -#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED_LE cpu_to_le32(0x00002000) -#define FILE_ATTRIBUTE_ENCRYPTED_LE cpu_to_le32(0x00004000) -#define FILE_ATTRIBUTE_INTEGRITY_STREAM_LE cpu_to_le32(0x00008000) -#define FILE_ATTRIBUTE_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) -#define FILE_ATTRIBUTE_MASK_LE cpu_to_le32(0x00007FB7) - -/* Desired Access Flags */ -#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) -#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) -#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) -#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) -#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) -#define FILE_READ_EA_LE cpu_to_le32(0x00000008) -#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) -#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) -#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) -#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) -#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) -#define FILE_DELETE_LE cpu_to_le32(0x00010000) -#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) -#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) -#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) -#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) -#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) -#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) -#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) -#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) -#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) -#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) -#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) - - -#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ - FILE_READ_EA_LE | \ - FILE_GENERIC_READ_LE) -#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ - FILE_APPEND_DATA_LE | \ - FILE_WRITE_EA_LE | \ - FILE_WRITE_ATTRIBUTES_LE | \ - FILE_GENERIC_WRITE_LE) - -/* ShareAccess Flags */ -#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) -#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) -#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) -#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) - -/* CreateDisposition Flags */ -#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) -#define FILE_OPEN_LE cpu_to_le32(0x00000001) -#define FILE_CREATE_LE cpu_to_le32(0x00000002) -#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) -#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) -#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) -#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) - -#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ - | FILE_READ_ATTRIBUTES) -#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) -#define FILE_EXEC_RIGHTS (FILE_EXECUTE) - -/* CreateOptions Flags */ -#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) -/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */ -#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) -#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) -#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) -#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) -#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) -#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) -#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) -#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) -#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) -#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) -#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) -#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) -#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) -#define CREATE_OPTIONS_MASK_LE cpu_to_le32(0x00FFFFFF) - -#define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \ - | FILE_READ_ATTRIBUTES_LE) -#define FILE_WRITE_RIGHTS_LE (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE \ - | FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE) -#define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE) - -/* Create Context Values */ -#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ -#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" -#define SMB2_CREATE_ALLOCATION_SIZE "AISi" -#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" -#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" -#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" -#define SMB2_CREATE_REQUEST_LEASE "RqLs" -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" -#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" -#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" -#define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" -#define SVHDX_OPEN_DEVICE_CONTEXT "\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83" -#define SMB2_CREATE_TAG_AAPL "AAPL" - -/* Flag (SMB3 open response) values */ -#define SMB2_CREATE_FLAG_REPARSEPOINT 0x01 - -struct create_context { - __le32 Next; - __le16 NameOffset; - __le16 NameLength; - __le16 Reserved; - __le16 DataOffset; - __le32 DataLength; - __u8 Buffer[]; -} __packed; - -struct smb2_create_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 57 */ - __u8 SecurityFlags; - __u8 RequestedOplockLevel; - __le32 ImpersonationLevel; - __le64 SmbCreateFlags; - __le64 Reserved; - __le32 DesiredAccess; - __le32 FileAttributes; - __le32 ShareAccess; - __le32 CreateDisposition; - __le32 CreateOptions; - __le16 NameOffset; - __le16 NameLength; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[]; -} __packed; - -struct smb2_create_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 89 */ - __u8 OplockLevel; - __u8 Flags; /* 0x01 if reparse point */ - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndofFile; - __le32 FileAttributes; - __le32 Reserved2; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[1]; -} __packed; - -struct create_posix { - struct create_context ccontext; - __u8 Name[16]; - __le32 Mode; - __u32 Reserved; -} __packed; - -#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00) -#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01) -#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02) -#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) - -#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) - -#define SMB2_LEASE_KEY_SIZE 16 - -struct lease_context { - __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; - __le32 LeaseState; - __le32 LeaseFlags; - __le64 LeaseDuration; -} __packed; - -struct lease_context_v2 { - __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; - __le32 LeaseState; - __le32 LeaseFlags; - __le64 LeaseDuration; - __u8 ParentLeaseKey[SMB2_LEASE_KEY_SIZE]; - __le16 Epoch; - __le16 Reserved; -} __packed; - -struct create_lease { - struct create_context ccontext; - __u8 Name[8]; - struct lease_context lcontext; -} __packed; - -struct create_lease_v2 { - struct create_context ccontext; - __u8 Name[8]; - struct lease_context_v2 lcontext; - __u8 Pad[4]; -} __packed; - -/* See MS-SMB2 2.2.31 and 2.2.32 */ -struct smb2_ioctl_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 57 */ - __le16 Reserved; /* offset from start of SMB2 header to write data */ - __le32 CtlCode; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 InputOffset; /* Reserved MBZ */ - __le32 InputCount; - __le32 MaxInputResponse; - __le32 OutputOffset; - __le32 OutputCount; - __le32 MaxOutputResponse; - __le32 Flags; - __le32 Reserved2; - __u8 Buffer[]; -} __packed; - -struct smb2_ioctl_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __le16 Reserved; - __le32 CtlCode; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 InputOffset; /* Reserved MBZ */ - __le32 InputCount; - __le32 OutputOffset; - __le32 OutputCount; - __le32 Flags; - __le32 Reserved2; - __u8 Buffer[]; -} __packed; - -/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */ -struct file_zero_data_information { - __le64 FileOffset; - __le64 BeyondFinalZero; -} __packed; - -/* See MS-FSCC 2.3.7 */ -struct duplicate_extents_to_file { - __u64 PersistentFileHandle; /* source file handle, opaque endianness */ - __u64 VolatileFileHandle; - __le64 SourceFileOffset; - __le64 TargetFileOffset; - __le64 ByteCount; /* Bytes to be copied */ -} __packed; - -/* See MS-FSCC 2.3.8 */ -#define DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC 0x00000001 -struct duplicate_extents_to_file_ex { - __u64 PersistentFileHandle; /* source file handle, opaque endianness */ - __u64 VolatileFileHandle; - __le64 SourceFileOffset; - __le64 TargetFileOffset; - __le64 ByteCount; /* Bytes to be copied */ - __le32 Flags; - __le32 Reserved; -} __packed; - - -/* See MS-FSCC 2.3.20 */ -struct fsctl_get_integrity_information_rsp { - __le16 ChecksumAlgorithm; - __le16 Reserved; - __le32 Flags; - __le32 ChecksumChunkSizeInBytes; - __le32 ClusterSizeInBytes; -} __packed; - -/* See MS-FSCC 2.3.55 */ -struct fsctl_query_file_regions_req { - __le64 FileOffset; - __le64 Length; - __le32 DesiredUsage; - __le32 Reserved; -} __packed; - -/* DesiredUsage flags see MS-FSCC 2.3.56.1 */ -#define FILE_USAGE_INVALID_RANGE 0x00000000 -#define FILE_USAGE_VALID_CACHED_DATA 0x00000001 -#define FILE_USAGE_NONCACHED_DATA 0x00000002 - -struct file_region_info { - __le64 FileOffset; - __le64 Length; - __le32 DesiredUsage; - __le32 Reserved; -} __packed; - -/* See MS-FSCC 2.3.56 */ -struct fsctl_query_file_region_rsp { - __le32 Flags; - __le32 TotalRegionEntryCount; - __le32 RegionEntryCount; - __u32 Reserved; - struct file_region_info Regions[]; -} __packed; - -/* See MS-FSCC 2.3.58 */ -struct fsctl_query_on_disk_vol_info_rsp { - __le64 DirectoryCount; - __le64 FileCount; - __le16 FsFormatMajVersion; - __le16 FsFormatMinVersion; - __u8 FsFormatName[24]; - __le64 FormatTime; - __le64 LastUpdateTime; - __u8 CopyrightInfo[68]; - __u8 AbstractInfo[68]; - __u8 FormatImplInfo[68]; - __u8 LastModifyImplInfo[68]; -} __packed; - -/* See MS-FSCC 2.3.73 */ -struct fsctl_set_integrity_information_req { - __le16 ChecksumAlgorithm; - __le16 Reserved; - __le32 Flags; -} __packed; - -/* See MS-FSCC 2.3.75 */ -struct fsctl_set_integrity_info_ex_req { - __u8 EnableIntegrity; - __u8 KeepState; - __u16 Reserved; - __le32 Flags; - __u8 Version; - __u8 Reserved2[7]; -} __packed; - -/* Integrity ChecksumAlgorithm choices for above */ -#define CHECKSUM_TYPE_NONE 0x0000 -#define CHECKSUM_TYPE_CRC64 0x0002 -#define CHECKSUM_TYPE_UNCHANGED 0xFFFF /* set only */ - -/* Integrity flags for above */ -#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 - -/* Reparse structures - see MS-FSCC 2.1.2 */ - -/* struct fsctl_reparse_info_req is empty, only response structs (see below) */ -struct reparse_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __u8 DataBuffer[]; /* Variable Length */ -} __packed; - -struct reparse_guid_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __u8 ReparseGuid[16]; - __u8 DataBuffer[]; /* Variable Length */ -} __packed; - -struct reparse_mount_point_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __u8 PathBuffer[]; /* Variable Length */ -} __packed; - -#define SYMLINK_FLAG_RELATIVE 0x00000001 - -struct reparse_symlink_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __le32 Flags; - __u8 PathBuffer[]; /* Variable Length */ -} __packed; - -/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */ - -struct validate_negotiate_info_req { - __le32 Capabilities; - __u8 Guid[SMB2_CLIENT_GUID_SIZE]; - __le16 SecurityMode; - __le16 DialectCount; - __le16 Dialects[4]; /* BB expand this if autonegotiate > 4 dialects */ -} __packed; - -struct validate_negotiate_info_rsp { - __le32 Capabilities; - __u8 Guid[SMB2_CLIENT_GUID_SIZE]; - __le16 SecurityMode; - __le16 Dialect; /* Dialect in use for the connection */ -} __packed; - - -/* Possible InfoType values */ -#define SMB2_O_INFO_FILE 0x01 -#define SMB2_O_INFO_FILESYSTEM 0x02 -#define SMB2_O_INFO_SECURITY 0x03 -#define SMB2_O_INFO_QUOTA 0x04 - -/* SMB2 Query Info see MS-SMB2 (2.2.37) or MS-DTYP */ - -/* List of QUERY INFO levels (those also valid for QUERY_DIR are noted below */ -#define FILE_DIRECTORY_INFORMATION 1 /* also for QUERY_DIR */ -#define FILE_FULL_DIRECTORY_INFORMATION 2 /* also for QUERY_DIR */ -#define FILE_BOTH_DIRECTORY_INFORMATION 3 /* also for QUERY_DIR */ -#define FILE_BASIC_INFORMATION 4 -#define FILE_STANDARD_INFORMATION 5 -#define FILE_INTERNAL_INFORMATION 6 -#define FILE_EA_INFORMATION 7 -#define FILE_ACCESS_INFORMATION 8 -#define FILE_NAME_INFORMATION 9 -#define FILE_RENAME_INFORMATION 10 -#define FILE_LINK_INFORMATION 11 -#define FILE_NAMES_INFORMATION 12 /* also for QUERY_DIR */ -#define FILE_DISPOSITION_INFORMATION 13 -#define FILE_POSITION_INFORMATION 14 -#define FILE_FULL_EA_INFORMATION 15 -#define FILE_MODE_INFORMATION 16 -#define FILE_ALIGNMENT_INFORMATION 17 -#define FILE_ALL_INFORMATION 18 -#define FILE_ALLOCATION_INFORMATION 19 -#define FILE_END_OF_FILE_INFORMATION 20 -#define FILE_ALTERNATE_NAME_INFORMATION 21 -#define FILE_STREAM_INFORMATION 22 -#define FILE_PIPE_INFORMATION 23 -#define FILE_PIPE_LOCAL_INFORMATION 24 -#define FILE_PIPE_REMOTE_INFORMATION 25 -#define FILE_MAILSLOT_QUERY_INFORMATION 26 -#define FILE_MAILSLOT_SET_INFORMATION 27 -#define FILE_COMPRESSION_INFORMATION 28 -#define FILE_OBJECT_ID_INFORMATION 29 -/* Number 30 not defined in documents */ -#define FILE_MOVE_CLUSTER_INFORMATION 31 -#define FILE_QUOTA_INFORMATION 32 -#define FILE_REPARSE_POINT_INFORMATION 33 -#define FILE_NETWORK_OPEN_INFORMATION 34 -#define FILE_ATTRIBUTE_TAG_INFORMATION 35 -#define FILE_TRACKING_INFORMATION 36 -#define FILEID_BOTH_DIRECTORY_INFORMATION 37 /* also for QUERY_DIR */ -#define FILEID_FULL_DIRECTORY_INFORMATION 38 /* also for QUERY_DIR */ -#define FILE_VALID_DATA_LENGTH_INFORMATION 39 -#define FILE_SHORT_NAME_INFORMATION 40 -#define FILE_SFIO_RESERVE_INFORMATION 44 -#define FILE_SFIO_VOLUME_INFORMATION 45 -#define FILE_HARD_LINK_INFORMATION 46 -#define FILE_NORMALIZED_NAME_INFORMATION 48 -#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 -#define FILE_STANDARD_LINK_INFORMATION 54 -#define FILE_ID_INFORMATION 59 -#define FILE_ID_EXTD_DIRECTORY_INFORMATION 60 /* also for QUERY_DIR */ -/* Used for Query Info and Find File POSIX Info for SMB3.1.1 and SMB1 */ -#define SMB_FIND_FILE_POSIX_INFO 0x064 - -/* Security info type additionalinfo flags. */ -#define OWNER_SECINFO 0x00000001 -#define GROUP_SECINFO 0x00000002 -#define DACL_SECINFO 0x00000004 -#define SACL_SECINFO 0x00000008 -#define LABEL_SECINFO 0x00000010 -#define ATTRIBUTE_SECINFO 0x00000020 -#define SCOPE_SECINFO 0x00000040 -#define BACKUP_SECINFO 0x00010000 -#define UNPROTECTED_SACL_SECINFO 0x10000000 -#define UNPROTECTED_DACL_SECINFO 0x20000000 -#define PROTECTED_SACL_SECINFO 0x40000000 -#define PROTECTED_DACL_SECINFO 0x80000000 - -/* Flags used for FileFullEAinfo */ -#define SL_RESTART_SCAN 0x00000001 -#define SL_RETURN_SINGLE_ENTRY 0x00000002 -#define SL_INDEX_SPECIFIED 0x00000004 - -struct smb2_query_info_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 41 */ - __u8 InfoType; - __u8 FileInfoClass; - __le32 OutputBufferLength; - __le16 InputBufferOffset; - __u16 Reserved; - __le32 InputBufferLength; - __le32 AdditionalInformation; - __le32 Flags; - __u64 PersistentFileId; - __u64 VolatileFileId; - __u8 Buffer[1]; -} __packed; - -struct smb2_query_info_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - -/* - * PDU query infolevel structure definitions - */ - -/* See MS-FSCC 2.3.52 */ -struct file_allocated_range_buffer { - __le64 file_offset; - __le64 length; -} __packed; - -struct smb2_file_internal_info { - __le64 IndexNumber; -} __packed; /* level 6 Query */ - -struct smb2_file_rename_info { /* encoding of request for level 10 */ - __u8 ReplaceIfExists; /* 1 = replace existing target with new */ - /* 0 = fail if target already exists */ - __u8 Reserved[7]; - __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ - __le32 FileNameLength; - char FileName[]; /* New name to be assigned */ - /* padding - overall struct size must be >= 24 so filename + pad >= 6 */ -} __packed; /* level 10 Set */ - -struct smb2_file_link_info { /* encoding of request for level 11 */ - __u8 ReplaceIfExists; /* 1 = replace existing link with new */ - /* 0 = fail if link already exists */ - __u8 Reserved[7]; - __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ - __le32 FileNameLength; - char FileName[]; /* Name to be assigned to new link */ -} __packed; /* level 11 Set */ - -/* - * This level 18, although with struct with same name is different from cifs - * level 0x107. Level 0x107 has an extra u64 between AccessFlags and - * CurrentByteOffset. - */ -struct smb2_file_all_info { /* data block encoding of response to level 18 */ - __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ - __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ - __le64 IndexNumber; - __le32 EASize; - __le32 AccessFlags; - __le64 CurrentByteOffset; - __le32 Mode; - __le32 AlignmentRequirement; - __le32 FileNameLength; - char FileName[1]; -} __packed; /* level 18 Query */ - -struct smb2_file_eof_info { /* encoding of request for level 10 */ - __le64 EndOfFile; /* new end of file value */ -} __packed; /* level 20 Set */ - -/* Level 100 query info */ -struct smb311_posix_qinfo { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 DosAttributes; - __le64 Inode; - __le32 DeviceId; - __le32 Zero; - /* beginning of POSIX Create Context Response */ - __le32 HardLinks; - __le32 ReparseTag; - __le32 Mode; - u8 Sids[]; - /* - * var sized owner SID - * var sized group SID - * le32 filenamelength - * u8 filename[] - */ -} __packed; - -/* File System Information Classes */ -#define FS_VOLUME_INFORMATION 1 /* Query */ -#define FS_LABEL_INFORMATION 2 /* Set */ -#define FS_SIZE_INFORMATION 3 /* Query */ -#define FS_DEVICE_INFORMATION 4 /* Query */ -#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ -#define FS_CONTROL_INFORMATION 6 /* Query, Set */ -#define FS_FULL_SIZE_INFORMATION 7 /* Query */ -#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ -#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ -#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ -#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ - -struct smb2_fs_full_size_info { - __le64 TotalAllocationUnits; - __le64 CallerAvailableAllocationUnits; - __le64 ActualAvailableAllocationUnits; - __le32 SectorsPerAllocationUnit; - __le32 BytesPerSector; -} __packed; - -#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 -#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 -#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 -#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 - -/* sector size info struct */ -struct smb3_fs_ss_info { - __le32 LogicalBytesPerSector; - __le32 PhysicalBytesPerSectorForAtomicity; - __le32 PhysicalBytesPerSectorForPerf; - __le32 FSEffPhysicalBytesPerSectorForAtomicity; - __le32 Flags; - __le32 ByteOffsetForSectorAlignment; - __le32 ByteOffsetForPartitionAlignment; -} __packed; - -/* File System Control Information */ -struct smb2_fs_control_info { - __le64 FreeSpaceStartFiltering; - __le64 FreeSpaceThreshold; - __le64 FreeSpaceStopFiltering; - __le64 DefaultQuotaThreshold; - __le64 DefaultQuotaLimit; - __le32 FileSystemControlFlags; - __le32 Padding; -} __packed; - -/* volume info struct - see MS-FSCC 2.5.9 */ -#define MAX_VOL_LABEL_LEN 32 -struct smb3_fs_vol_info { - __le64 VolumeCreationTime; - __u32 VolumeSerialNumber; - __le32 VolumeLabelLength; /* includes trailing null */ - __u8 SupportsObjects; /* True if eg like NTFS, supports objects */ - __u8 Reserved; - __u8 VolumeLabel[]; /* variable len */ -} __packed; - -/* See MS-SMB2 2.2.23 through 2.2.25 */ -struct smb2_oplock_break { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __u8 OplockLevel; - __u8 Reserved; - __le32 Reserved2; - __u64 PersistentFid; - __u64 VolatileFid; -} __packed; - -#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) - -struct smb2_lease_break { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 44 */ - __le16 Epoch; - __le32 Flags; - __u8 LeaseKey[16]; - __le32 CurrentLeaseState; - __le32 NewLeaseState; - __le32 BreakReason; - __le32 AccessMaskHint; - __le32 ShareMaskHint; -} __packed; - -struct smb2_lease_ack { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 36 */ - __le16 Reserved; - __le32 Flags; - __u8 LeaseKey[16]; - __le32 LeaseState; - __le64 LeaseDuration; -} __packed; - -#define OP_BREAK_STRUCT_SIZE_20 24 -#define OP_BREAK_STRUCT_SIZE_21 36 -#endif /* _COMMON_SMB2PDU_H */ diff --git a/fs/smbfs_common/smbfsctl.h b/fs/smbfs_common/smbfsctl.h deleted file mode 100644 index edd7fc2a7921..000000000000 --- a/fs/smbfs_common/smbfsctl.h +++ /dev/null @@ -1,170 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * SMB, CIFS, SMB2 FSCTL definitions - * - * Copyright (c) International Business Machines Corp., 2002,2013 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -/* IOCTL information */ -/* - * List of ioctl/fsctl function codes that are or could be useful in the - * future to remote clients like cifs or SMB2/SMB3 client. This is probably - * a slightly larger set of fsctls that NTFS local filesystem could handle, - * including the seven below that we do not have struct definitions for. - * Even with protocol definitions for most of these now available, we still - * need to do some experimentation to identify which are practical to do - * remotely. Some of the following, such as the encryption/compression ones - * could be invoked from tools via a specialized hook into the VFS rather - * than via the standard vfs entry points - * - * See MS-SMB2 Section 2.2.31 (last checked September 2021, all of that list are - * below). Additional detail on less common ones can be found in MS-FSCC - * section 2.3. - */ - -#ifndef __SMBFSCTL_H -#define __SMBFSCTL_H - -/* - * FSCTL values are 32 bits and are constructed as - * - */ -/* Device */ -#define FSCTL_DEVICE_DFS (0x0006 << 16) -#define FSCTL_DEVICE_FILE_SYSTEM (0x0009 << 16) -#define FSCTL_DEVICE_NAMED_PIPE (0x0011 << 16) -#define FSCTL_DEVICE_NETWORK_FILE_SYSTEM (0x0014 << 16) -#define FSCTL_DEVICE_MASK 0xffff0000 -/* Access */ -#define FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS (0x00 << 14) -#define FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS (0x01 << 14) -#define FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS (0x02 << 14) -#define FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS (0x03 << 14) -#define FSCTL_DEVICE_ACCESS_MASK 0x0000c000 -/* Function */ -#define FSCTL_DEVICE_FUNCTION_MASK 0x00003ffc -/* Method */ -#define FSCTL_DEVICE_METHOD_BUFFERED 0x00 -#define FSCTL_DEVICE_METHOD_IN_DIRECT 0x01 -#define FSCTL_DEVICE_METHOD_OUT_DIRECT 0x02 -#define FSCTL_DEVICE_METHOD_NEITHER 0x03 -#define FSCTL_DEVICE_METHOD_MASK 0x00000003 - - -#define FSCTL_DFS_GET_REFERRALS 0x00060194 -#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 -#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 -#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 -#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 -#define FSCTL_LOCK_VOLUME 0x00090018 -#define FSCTL_UNLOCK_VOLUME 0x0009001C -#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ -#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ -#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ -#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ -/* Verify the next FSCTL number, we had it as 0x00090090 before */ -#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ -#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ -#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ -#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ -#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ -#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C -#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ -#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ -#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ -#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ -#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ -#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ -#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ -#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ -#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ -#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ -#define FSCTL_SET_ZERO_DATA 0x000980C8 -#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ -#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ -#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ -#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ -#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ -#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ -#define FSCTL_MARK_HANDLE 0x000900FC /* BB add struct */ -#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ -#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ -#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ -#define FSCTL_QUERY_ON_DISK_VOLUME_INFO 0x0009013C -#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ -#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ -#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C -#define FSCTL_QUERY_FILE_REGIONS 0x00090284 -#define FSCTL_GET_REFS_VOLUME_DATA 0x000902D8 /* See MS-FSCC 2.3.24 */ -#define FSCTL_SET_INTEGRITY_INFORMATION_EXT 0x00090380 -#define FSCTL_GET_RETRIEVAL_POINTERS_AND_REFCOUNT 0x000903d3 -#define FSCTL_GET_RETRIEVAL_POINTER_COUNT 0x0009042b -#define FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT 0x00090440 -#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF -#define FSCTL_OFFLOAD_READ 0x00094264 /* BB add struct */ -#define FSCTL_OFFLOAD_WRITE 0x00098268 /* BB add struct */ -#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ -#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ -#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 -#define FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX 0x000983E8 -#define FSCTL_SIS_LINK_FILES 0x0009C104 -#define FSCTL_SET_INTEGRITY_INFORMATION 0x0009C280 -#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ -#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ -/* strange that the number for this op is not sequential with previous op */ -#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ -/* Enumerate previous versions of a file */ -#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064 -/* Retrieve an opaque file reference for server-side data movement ie copy */ -#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078 -#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 -#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ -#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ -#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 -/* Perform server-side data movement */ -#define FSCTL_SRV_COPYCHUNK 0x001440F2 -#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 -#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */ -#define FSCTL_SRV_READ_HASH 0x001441BB /* BB add struct */ - -/* See FSCC 2.1.2.5 */ -#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 -#define IO_REPARSE_TAG_HSM 0xC0000004 -#define IO_REPARSE_TAG_SIS 0x80000007 -#define IO_REPARSE_TAG_HSM2 0x80000006 -#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 -/* Used by the DFS filter. See MS-DFSC */ -#define IO_REPARSE_TAG_DFS 0x8000000A -/* Used by the DFS filter See MS-DFSC */ -#define IO_REPARSE_TAG_DFSR 0x80000012 -#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B -/* See section MS-FSCC 2.1.2.4 */ -#define IO_REPARSE_TAG_SYMLINK 0xA000000C -#define IO_REPARSE_TAG_DEDUP 0x80000013 -#define IO_REPARSE_APPXSTREAM 0xC0000014 -/* NFS symlinks, Win 8/SMB3 and later */ -#define IO_REPARSE_TAG_NFS 0x80000014 -/* - * AzureFileSync - see - * https://docs.microsoft.com/en-us/azure/storage/files/storage-sync-cloud-tiering - */ -#define IO_REPARSE_TAG_AZ_FILE_SYNC 0x8000001e -/* WSL reparse tags */ -#define IO_REPARSE_TAG_LX_SYMLINK 0xA000001D -#define IO_REPARSE_TAG_AF_UNIX 0x80000023 -#define IO_REPARSE_TAG_LX_FIFO 0x80000024 -#define IO_REPARSE_TAG_LX_CHR 0x80000025 -#define IO_REPARSE_TAG_LX_BLK 0x80000026 - -#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) -#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) -#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) -#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) -#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) - -/* fsctl flags */ -/* If Flags is set to this value, the request is an FSCTL not ioctl request */ -#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 -#endif /* __SMBFSCTL_H */ -- cgit 1.4.1 From a1c449d00ff8ce2c5fcea5f755df682d1f6bc2ef Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 28 Jun 2023 11:12:41 +0200 Subject: Linux 6.1.36 Link: https://lore.kernel.org/r/20230626180800.476539630@linuxfoundation.org Tested-by: Takeshi Ogasawara Tested-by: Jon Hunter Tested-by: Conor Dooley Tested-by: Markus Reichelt Tested-by: Chris Paterson (CIP) Tested-by: Ron Economos Tested-by: Salvatore Bonaccorso Tested-by: Guenter Roeck Tested-by: Linux Kernel Functional Testing Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 46c06af912d9..cffb83d7a0fb 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 35 +SUBLEVEL = 36 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From a149174ff8bbdd8d5a0a591feaa3668ea4f43387 Mon Sep 17 00:00:00 2001 From: "Liam R. Howlett" Date: Sat, 17 Jun 2023 20:47:08 -0400 Subject: mm/mmap: Fix error path in do_vmi_align_munmap() commit 606c812eb1d5b5fb0dd9e330ca94b52d7c227830 upstream The error unrolling was leaving the VMAs detached in many cases and leaving the locked_vm statistic altered, and skipping the unrolling entirely in the case of the vma tree write failing. Fix the error path by re-attaching the detached VMAs and adding the necessary goto for the failed vma tree write, and fix the locked_vm statistic by only updating after the vma tree write succeeds. Fixes: 763ecb035029 ("mm: remove the vma linked list") Reported-by: Vegard Nossum Signed-off-by: Liam R. Howlett Signed-off-by: Linus Torvalds [ dwmw2: Strictly, the original patch wasn't *re-attaching* the detached VMAs. They *were* still attached but just had the 'detached' flag set, which is an optimisation. Which doesn't exist in 6.3, so drop that. Also drop the call to vma_start_write() which came in with the per-VMA locking in 6.4. ] [ dwmw2 (6.1): It's do_mas_align_munmap() here. And has two call sites for the now-removed munmap_sidetree() function. Inline them both rather then trying to backport various dependencies with potentially subtle interactions. ] Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index 14ca259189b7..d82a95956def 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2311,19 +2311,6 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma, return __split_vma(mm, vma, addr, new_below); } -static inline int munmap_sidetree(struct vm_area_struct *vma, - struct ma_state *mas_detach) -{ - mas_set_range(mas_detach, vma->vm_start, vma->vm_end - 1); - if (mas_store_gfp(mas_detach, vma, GFP_KERNEL)) - return -ENOMEM; - - if (vma->vm_flags & VM_LOCKED) - vma->vm_mm->locked_vm -= vma_pages(vma); - - return 0; -} - /* * do_mas_align_munmap() - munmap the aligned region from @start to @end. * @mas: The maple_state, ideally set up to alter the correct tree location. @@ -2345,6 +2332,7 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, struct maple_tree mt_detach; int count = 0; int error = -ENOMEM; + unsigned long locked_vm = 0; MA_STATE(mas_detach, &mt_detach, 0, 0); mt_init_flags(&mt_detach, mas->tree->ma_flags & MT_FLAGS_LOCK_MASK); mt_set_external_lock(&mt_detach, &mm->mmap_lock); @@ -2403,18 +2391,23 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, mas_set(mas, end); split = mas_prev(mas, 0); - error = munmap_sidetree(split, &mas_detach); + mas_set_range(&mas_detach, split->vm_start, split->vm_end - 1); + error = mas_store_gfp(&mas_detach, split, GFP_KERNEL); if (error) - goto munmap_sidetree_failed; + goto munmap_gather_failed; + if (next->vm_flags & VM_LOCKED) + locked_vm += vma_pages(split); count++; if (vma == next) vma = split; break; } - error = munmap_sidetree(next, &mas_detach); - if (error) - goto munmap_sidetree_failed; + mas_set_range(&mas_detach, next->vm_start, next->vm_end - 1); + if (mas_store_gfp(&mas_detach, next, GFP_KERNEL)) + goto munmap_gather_failed; + if (next->vm_flags & VM_LOCKED) + locked_vm += vma_pages(next); count++; #ifdef CONFIG_DEBUG_VM_MAPLE_TREE @@ -2464,6 +2457,8 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, } #endif mas_store_prealloc(mas, NULL); + + mm->locked_vm -= locked_vm; mm->map_count -= count; /* * Do not downgrade mmap_lock if we are next to VM_GROWSDOWN or @@ -2490,7 +2485,7 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, return downgrade ? 1 : 0; userfaultfd_error: -munmap_sidetree_failed: +munmap_gather_failed: end_split_failed: __mt_destroy(&mt_detach); start_split_failed: -- cgit 1.4.1 From 42a018a796d1eedb0d7c38b2778ef3dbf05aca36 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 28 Jun 2023 10:55:03 +0100 Subject: mm/mmap: Fix error return in do_vmi_align_munmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6c26bd4384da24841bac4f067741bbca18b0fb74 upstream, If mas_store_gfp() in the gather loop failed, the 'error' variable that ultimately gets returned was not being set. In many cases, its original value of -ENOMEM was still in place, and that was fine. But if VMAs had been split at the start or end of the range, then 'error' could be zero. Change to the 'error = foo(); if (error) goto …' idiom to fix the bug. Also clean up a later case which avoided the same bug by *explicitly* setting error = -ENOMEM right before calling the function that might return -ENOMEM. In a final cosmetic change, move the 'Point of no return' comment to *after* the goto. That's been in the wrong place since the preallocation was removed, and this new error path was added. Fixes: 606c812eb1d5 ("mm/mmap: Fix error path in do_vmi_align_munmap()") Signed-off-by: David Woodhouse Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Reviewed-by: Liam R. Howlett Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mm/mmap.c b/mm/mmap.c index d82a95956def..f031fa267913 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2404,7 +2404,8 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, break; } mas_set_range(&mas_detach, next->vm_start, next->vm_end - 1); - if (mas_store_gfp(&mas_detach, next, GFP_KERNEL)) + error = mas_store_gfp(&mas_detach, next, GFP_KERNEL); + if (error) goto munmap_gather_failed; if (next->vm_flags & VM_LOCKED) locked_vm += vma_pages(next); @@ -2456,6 +2457,7 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, mas_set_range(mas, start, end - 1); } #endif + /* Point of no return */ mas_store_prealloc(mas, NULL); mm->locked_vm -= locked_vm; -- cgit 1.4.1 From 69925a346acb70be33059f4940ed703ffe0b0756 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 20 Jun 2023 18:24:23 +0200 Subject: mptcp: ensure listener is unhashed before updating the sk status commit 57fc0f1ceaa4016354cf6f88533e20b56190e41a upstream. The MPTCP protocol access the listener subflow in a lockless manner in a couple of places (poll, diag). That works only if the msk itself leaves the listener status only after that the subflow itself has been closed/disconnected. Otherwise we risk deadlock in diag, as reported by Christoph. Address the issue ensuring that the first subflow (the listener one) is always disconnected before updating the msk socket status. Reported-by: Christoph Paasch Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/407 Fixes: b29fcfb54cd7 ("mptcp: full disconnect implementation") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/pm_netlink.c | 1 + net/mptcp/protocol.c | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index 01d34ee4525e..9127a7fd5269 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -1039,6 +1039,7 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk, return err; } + inet_sk_state_store(newsk, TCP_LISTEN); err = kernel_listen(ssock, backlog); if (err) { pr_warn("kernel_listen error, err=%d", err); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index a2c6ce40e426..4ca61e80f4bb 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2400,12 +2400,6 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, kfree_rcu(subflow, rcu); } else { /* otherwise tcp will dispose of the ssk and subflow ctx */ - if (ssk->sk_state == TCP_LISTEN) { - tcp_set_state(ssk, TCP_CLOSE); - mptcp_subflow_queue_clean(sk, ssk); - inet_csk_listen_stop(ssk); - } - __tcp_close(ssk, 0); /* close acquired an extra ref */ @@ -2939,6 +2933,24 @@ static __poll_t mptcp_check_readable(struct mptcp_sock *msk) return EPOLLIN | EPOLLRDNORM; } +static void mptcp_check_listen_stop(struct sock *sk) +{ + struct sock *ssk; + + if (inet_sk_state_load(sk) != TCP_LISTEN) + return; + + ssk = mptcp_sk(sk)->first; + if (WARN_ON_ONCE(!ssk || inet_sk_state_load(ssk) != TCP_LISTEN)) + return; + + lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); + mptcp_subflow_queue_clean(sk, ssk); + inet_csk_listen_stop(ssk); + tcp_set_state(ssk, TCP_CLOSE); + release_sock(ssk); +} + bool __mptcp_close(struct sock *sk, long timeout) { struct mptcp_subflow_context *subflow; @@ -2949,6 +2961,7 @@ bool __mptcp_close(struct sock *sk, long timeout) WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) { + mptcp_check_listen_stop(sk); inet_sk_state_store(sk, TCP_CLOSE); goto cleanup; } @@ -3062,6 +3075,7 @@ static int mptcp_disconnect(struct sock *sk, int flags) if (msk->fastopening) return -EBUSY; + mptcp_check_listen_stop(sk); inet_sk_state_store(sk, TCP_CLOSE); mptcp_stop_timer(sk); -- cgit 1.4.1 From 4af5960d7cd46c3834f65b75577b775cbcd0f7b2 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Fri, 21 Oct 2022 13:01:19 -0700 Subject: mm, hwpoison: try to recover from copy-on write faults commit a873dfe1032a132bf89f9e19a6ac44f5a0b78754 upstream. Patch series "Copy-on-write poison recovery", v3. Part 1 deals with the process that triggered the copy on write fault with a store to a shared read-only page. That process is send a SIGBUS with the usual machine check decoration to specify the virtual address of the lost page, together with the scope. Part 2 sets up to asynchronously take the page with the uncorrected error offline to prevent additional machine check faults. H/t to Miaohe Lin and Shuai Xue for pointing me to the existing function to queue a call to memory_failure(). On x86 there is some duplicate reporting (because the error is also signalled by the memory controller as well as by the core that triggered the machine check). Console logs look like this: This patch (of 2): If the kernel is copying a page as the result of a copy-on-write fault and runs into an uncorrectable error, Linux will crash because it does not have recovery code for this case where poison is consumed by the kernel. It is easy to set up a test case. Just inject an error into a private page, fork(2), and have the child process write to the page. I wrapped that neatly into a test at: git://git.kernel.org/pub/scm/linux/kernel/git/aegl/ras-tools.git just enable ACPI error injection and run: # ./einj_mem-uc -f copy-on-write Add a new copy_user_highpage_mc() function that uses copy_mc_to_kernel() on architectures where that is available (currently x86 and powerpc). When an error is detected during the page copy, return VM_FAULT_HWPOISON to caller of wp_page_copy(). This propagates up the call stack. Both x86 and powerpc have code in their fault handler to deal with this code by sending a SIGBUS to the application. Note that this patch avoids a system crash and signals the process that triggered the copy-on-write action. It does not take any action for the memory error that is still in the shared page. To handle that a call to memory_failure() is needed. But this cannot be done from wp_page_copy() because it holds mmap_lock(). Perhaps the architecture fault handlers can deal with this loose end in a subsequent patch? On Intel/x86 this loose end will often be handled automatically because the memory controller provides an additional notification of the h/w poison in memory, the handler for this will call memory_failure(). This isn't a 100% solution. If there are multiple errors, not all may be logged in this way. [tony.luck@intel.com: add call to kmsan_unpoison_memory(), per Miaohe Lin] Link: https://lkml.kernel.org/r/20221031201029.102123-2-tony.luck@intel.com Link: https://lkml.kernel.org/r/20221021200120.175753-1-tony.luck@intel.com Link: https://lkml.kernel.org/r/20221021200120.175753-2-tony.luck@intel.com Signed-off-by: Tony Luck Reviewed-by: Dan Williams Reviewed-by: Naoya Horiguchi Reviewed-by: Miaohe Lin Reviewed-by: Alexander Potapenko Tested-by: Shuai Xue Cc: Christophe Leroy Cc: Matthew Wilcox (Oracle) Cc: Michael Ellerman Cc: Nicholas Piggin Signed-off-by: Andrew Morton Igned-off-by: Jane Chu Signed-off-by: Greg Kroah-Hartman --- include/linux/highmem.h | 26 ++++++++++++++++++++++++++ mm/memory.c | 30 ++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/include/linux/highmem.h b/include/linux/highmem.h index e9912da5441b..44242268f53b 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -319,6 +319,32 @@ static inline void copy_user_highpage(struct page *to, struct page *from, #endif +#ifdef copy_mc_to_kernel +static inline int copy_mc_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + unsigned long ret; + char *vfrom, *vto; + + vfrom = kmap_local_page(from); + vto = kmap_local_page(to); + ret = copy_mc_to_kernel(vto, vfrom, PAGE_SIZE); + if (!ret) + kmsan_unpoison_memory(page_address(to), PAGE_SIZE); + kunmap_local(vto); + kunmap_local(vfrom); + + return ret; +} +#else +static inline int copy_mc_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + copy_user_highpage(to, from, vaddr, vma); + return 0; +} +#endif + #ifndef __HAVE_ARCH_COPY_HIGHPAGE static inline void copy_highpage(struct page *to, struct page *from) diff --git a/mm/memory.c b/mm/memory.c index 747b7ea30f89..bd8b04dcc851 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2843,10 +2843,16 @@ static inline int pte_unmap_same(struct vm_fault *vmf) return same; } -static inline bool __wp_page_copy_user(struct page *dst, struct page *src, - struct vm_fault *vmf) +/* + * Return: + * 0: copied succeeded + * -EHWPOISON: copy failed due to hwpoison in source page + * -EAGAIN: copied failed (some other reason) + */ +static inline int __wp_page_copy_user(struct page *dst, struct page *src, + struct vm_fault *vmf) { - bool ret; + int ret; void *kaddr; void __user *uaddr; bool locked = false; @@ -2855,8 +2861,9 @@ static inline bool __wp_page_copy_user(struct page *dst, struct page *src, unsigned long addr = vmf->address; if (likely(src)) { - copy_user_highpage(dst, src, addr, vma); - return true; + if (copy_mc_user_highpage(dst, src, addr, vma)) + return -EHWPOISON; + return 0; } /* @@ -2883,7 +2890,7 @@ static inline bool __wp_page_copy_user(struct page *dst, struct page *src, * and update local tlb only */ update_mmu_tlb(vma, addr, vmf->pte); - ret = false; + ret = -EAGAIN; goto pte_unlock; } @@ -2908,7 +2915,7 @@ static inline bool __wp_page_copy_user(struct page *dst, struct page *src, if (!likely(pte_same(*vmf->pte, vmf->orig_pte))) { /* The PTE changed under us, update local tlb */ update_mmu_tlb(vma, addr, vmf->pte); - ret = false; + ret = -EAGAIN; goto pte_unlock; } @@ -2927,7 +2934,7 @@ warn: } } - ret = true; + ret = 0; pte_unlock: if (locked) @@ -3099,6 +3106,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf) pte_t entry; int page_copied = 0; struct mmu_notifier_range range; + int ret; delayacct_wpcopy_start(); @@ -3116,19 +3124,21 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf) if (!new_page) goto oom; - if (!__wp_page_copy_user(new_page, old_page, vmf)) { + ret = __wp_page_copy_user(new_page, old_page, vmf); + if (ret) { /* * COW failed, if the fault was solved by other, * it's fine. If not, userspace would re-fault on * the same address and we will handle the fault * from the second attempt. + * The -EHWPOISON case will not be retried. */ put_page(new_page); if (old_page) put_page(old_page); delayacct_wpcopy_end(); - return 0; + return ret == -EHWPOISON ? VM_FAULT_HWPOISON : 0; } kmsan_copy_page_meta(new_page, old_page); } -- cgit 1.4.1 From 84f077802e56ae43f4b6c6eb9ad59b19df9db374 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Fri, 21 Oct 2022 13:01:20 -0700 Subject: mm, hwpoison: when copy-on-write hits poison, take page offline commit d302c2398ba269e788a4f37ae57c07a7fcabaa42 upstream. Cannot call memory_failure() directly from the fault handler because mmap_lock (and others) are held. It is important, but not urgent, to mark the source page as h/w poisoned and unmap it from other tasks. Use memory_failure_queue() to request a call to memory_failure() for the page with the error. Also provide a stub version for CONFIG_MEMORY_FAILURE=n Link: https://lkml.kernel.org/r/20221021200120.175753-3-tony.luck@intel.com Signed-off-by: Tony Luck Reviewed-by: Miaohe Lin Cc: Christophe Leroy Cc: Dan Williams Cc: Matthew Wilcox (Oracle) Cc: Michael Ellerman Cc: Naoya Horiguchi Cc: Nicholas Piggin Cc: Shuai Xue Signed-off-by: Andrew Morton [ Due to missing commits e591ef7d96d6e ("mm,hwpoison,hugetlb,memory_hotplug: hotremove memory section with hwpoisoned hugepage") 5033091de814a ("mm/hwpoison: introduce per-memory_block hwpoison counter") The impact of e591ef7d96d6e is its introduction of an additional flag in __get_huge_page_for_hwpoison() that serves as an indication a hwpoisoned hugetlb page should have its migratable bit cleared. The impact of 5033091de814a is contexual. Resolve by ignoring both missing commits. - jane] Signed-off-by: Jane Chu Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 5 ++++- mm/memory.c | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index e5e8acf8eb89..49e14d3afd44 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3295,7 +3295,6 @@ enum mf_flags { int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index, unsigned long count, int mf_flags); extern int memory_failure(unsigned long pfn, int flags); -extern void memory_failure_queue(unsigned long pfn, int flags); extern void memory_failure_queue_kick(int cpu); extern int unpoison_memory(unsigned long pfn); extern int sysctl_memory_failure_early_kill; @@ -3304,8 +3303,12 @@ extern void shake_page(struct page *p); extern atomic_long_t num_poisoned_pages __read_mostly; extern int soft_offline_page(unsigned long pfn, int flags); #ifdef CONFIG_MEMORY_FAILURE +extern void memory_failure_queue(unsigned long pfn, int flags); extern int __get_huge_page_for_hwpoison(unsigned long pfn, int flags); #else +static inline void memory_failure_queue(unsigned long pfn, int flags) +{ +} static inline int __get_huge_page_for_hwpoison(unsigned long pfn, int flags) { return 0; diff --git a/mm/memory.c b/mm/memory.c index bd8b04dcc851..bc8b2bd295cb 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2861,8 +2861,10 @@ static inline int __wp_page_copy_user(struct page *dst, struct page *src, unsigned long addr = vmf->address; if (likely(src)) { - if (copy_mc_user_highpage(dst, src, addr, vma)) + if (copy_mc_user_highpage(dst, src, addr, vma)) { + memory_failure_queue(page_to_pfn(src), 0); return -EHWPOISON; + } return 0; } -- cgit 1.4.1 From 94a69d6999419cd21365111b4493070182712299 Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Tue, 2 May 2023 19:53:50 +0200 Subject: x86/microcode/AMD: Load late on both threads too commit a32b0f0db3f396f1c9be2fe621e77c09ec3d8e7d upstream. Do the same as early loading - load on both threads. Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://lore.kernel.org/r/20230605141332.25948-1-bp@alien8.de Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/microcode/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 461e45d85add..9a3092ec9b27 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -705,7 +705,7 @@ static enum ucode_state apply_microcode_amd(int cpu) rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); /* need to apply patch? */ - if (rev >= mc_amd->hdr.patch_id) { + if (rev > mc_amd->hdr.patch_id) { ret = UCODE_OK; goto out; } -- cgit 1.4.1 From edadebb349e89461109643dd92ee986e01a47aa1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Apr 2023 18:37:00 +0200 Subject: x86/smp: Make stop_other_cpus() more robust commit 1f5e7eb7868e42227ac426c96d437117e6e06e8e upstream. Tony reported intermittent lockups on poweroff. His analysis identified the wbinvd() in stop_this_cpu() as the culprit. This was added to ensure that on SME enabled machines a kexec() does not leave any stale data in the caches when switching from encrypted to non-encrypted mode or vice versa. That wbinvd() is conditional on the SME feature bit which is read directly from CPUID. But that readout does not check whether the CPUID leaf is available or not. If it's not available the CPU will return the value of the highest supported leaf instead. Depending on the content the "SME" bit might be set or not. That's incorrect but harmless. Making the CPUID readout conditional makes the observed hangs go away, but it does not fix the underlying problem: CPU0 CPU1 stop_other_cpus() send_IPIs(REBOOT); stop_this_cpu() while (num_online_cpus() > 1); set_online(false); proceed... -> hang wbinvd() WBINVD is an expensive operation and if multiple CPUs issue it at the same time the resulting delays are even larger. But CPU0 already observed num_online_cpus() going down to 1 and proceeds which causes the system to hang. This issue exists independent of WBINVD, but the delays caused by WBINVD make it more prominent. Make this more robust by adding a cpumask which is initialized to the online CPU mask before sending the IPIs and CPUs clear their bit in stop_this_cpu() after the WBINVD completed. Check for that cpumask to become empty in stop_other_cpus() instead of watching num_online_cpus(). The cpumask cannot plug all holes either, but it's better than a raw counter and allows to restrict the NMI fallback IPI to be sent only the CPUs which have not reported within the timeout window. Fixes: 08f253ec3767 ("x86/cpu: Clear SME feature flag when not in use") Reported-by: Tony Battersby Signed-off-by: Thomas Gleixner Reviewed-by: Borislav Petkov (AMD) Reviewed-by: Ashok Raj Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/3817d810-e0f1-8ef8-0bbd-663b919ca49b@cybernetics.com Link: https://lore.kernel.org/r/87h6r770bv.ffs@tglx Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/cpu.h | 2 ++ arch/x86/kernel/process.c | 23 +++++++++++++++-- arch/x86/kernel/smp.c | 62 ++++++++++++++++++++++++++++++---------------- 3 files changed, 64 insertions(+), 23 deletions(-) diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index b472ef76826a..37639a2d9c34 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -96,4 +96,6 @@ static inline bool intel_cpu_signatures_match(unsigned int s1, unsigned int p1, extern u64 x86_read_arch_cap_msr(void); +extern struct cpumask cpus_stop_mask; + #endif /* _ASM_X86_CPU_H */ diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index e436c9c1ef3b..93df1f0feeb1 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -744,13 +744,23 @@ bool xen_set_default_idle(void) } #endif +struct cpumask cpus_stop_mask; + void __noreturn stop_this_cpu(void *dummy) { + unsigned int cpu = smp_processor_id(); + local_irq_disable(); + /* - * Remove this CPU: + * Remove this CPU from the online mask and disable it + * unconditionally. This might be redundant in case that the reboot + * vector was handled late and stop_other_cpus() sent an NMI. + * + * According to SDM and APM NMIs can be accepted even after soft + * disabling the local APIC. */ - set_cpu_online(smp_processor_id(), false); + set_cpu_online(cpu, false); disable_local_APIC(); mcheck_cpu_clear(this_cpu_ptr(&cpu_info)); @@ -768,6 +778,15 @@ void __noreturn stop_this_cpu(void *dummy) */ if (cpuid_eax(0x8000001f) & BIT(0)) native_wbinvd(); + + /* + * This brings a cache line back and dirties it, but + * native_stop_other_cpus() will overwrite cpus_stop_mask after it + * observed that all CPUs reported stop. This write will invalidate + * the related cache line on this CPU. + */ + cpumask_clear_cpu(cpu, &cpus_stop_mask); + for (;;) { /* * Use native_halt() so that memory contents don't change diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 375b33ecafa2..935bc6562fa4 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -146,31 +147,43 @@ static int register_stop_handler(void) static void native_stop_other_cpus(int wait) { - unsigned long flags; - unsigned long timeout; + unsigned int cpu = smp_processor_id(); + unsigned long flags, timeout; if (reboot_force) return; - /* - * Use an own vector here because smp_call_function - * does lots of things not suitable in a panic situation. - */ + /* Only proceed if this is the first CPU to reach this code */ + if (atomic_cmpxchg(&stopping_cpu, -1, cpu) != -1) + return; /* - * We start by using the REBOOT_VECTOR irq. - * The irq is treated as a sync point to allow critical - * regions of code on other cpus to release their spin locks - * and re-enable irqs. Jumping straight to an NMI might - * accidentally cause deadlocks with further shutdown/panic - * code. By syncing, we give the cpus up to one second to - * finish their work before we force them off with the NMI. + * 1) Send an IPI on the reboot vector to all other CPUs. + * + * The other CPUs should react on it after leaving critical + * sections and re-enabling interrupts. They might still hold + * locks, but there is nothing which can be done about that. + * + * 2) Wait for all other CPUs to report that they reached the + * HLT loop in stop_this_cpu() + * + * 3) If #2 timed out send an NMI to the CPUs which did not + * yet report + * + * 4) Wait for all other CPUs to report that they reached the + * HLT loop in stop_this_cpu() + * + * #3 can obviously race against a CPU reaching the HLT loop late. + * That CPU will have reported already and the "have all CPUs + * reached HLT" condition will be true despite the fact that the + * other CPU is still handling the NMI. Again, there is no + * protection against that as "disabled" APICs still respond to + * NMIs. */ - if (num_online_cpus() > 1) { - /* did someone beat us here? */ - if (atomic_cmpxchg(&stopping_cpu, -1, safe_smp_processor_id()) != -1) - return; + cpumask_copy(&cpus_stop_mask, cpu_online_mask); + cpumask_clear_cpu(cpu, &cpus_stop_mask); + if (!cpumask_empty(&cpus_stop_mask)) { /* sync above data before sending IRQ */ wmb(); @@ -183,12 +196,12 @@ static void native_stop_other_cpus(int wait) * CPUs reach shutdown state. */ timeout = USEC_PER_SEC; - while (num_online_cpus() > 1 && timeout--) + while (!cpumask_empty(&cpus_stop_mask) && timeout--) udelay(1); } /* if the REBOOT_VECTOR didn't work, try with the NMI */ - if (num_online_cpus() > 1) { + if (!cpumask_empty(&cpus_stop_mask)) { /* * If NMI IPI is enabled, try to register the stop handler * and send the IPI. In any case try to wait for the other @@ -200,7 +213,8 @@ static void native_stop_other_cpus(int wait) pr_emerg("Shutting down cpus with NMI\n"); - apic_send_IPI_allbutself(NMI_VECTOR); + for_each_cpu(cpu, &cpus_stop_mask) + apic->send_IPI(cpu, NMI_VECTOR); } /* * Don't wait longer than 10 ms if the caller didn't @@ -208,7 +222,7 @@ static void native_stop_other_cpus(int wait) * one or more CPUs do not reach shutdown state. */ timeout = USEC_PER_MSEC * 10; - while (num_online_cpus() > 1 && (wait || timeout--)) + while (!cpumask_empty(&cpus_stop_mask) && (wait || timeout--)) udelay(1); } @@ -216,6 +230,12 @@ static void native_stop_other_cpus(int wait) disable_local_APIC(); mcheck_cpu_clear(this_cpu_ptr(&cpu_info)); local_irq_restore(flags); + + /* + * Ensure that the cpus_stop_mask cache lines are invalidated on + * the other CPUs. See comment vs. SME in stop_this_cpu(). + */ + cpumask_clear(&cpus_stop_mask); } /* -- cgit 1.4.1 From e47037d28b7398d7a8f1a3e071087ea9dbfcebf5 Mon Sep 17 00:00:00 2001 From: Tony Battersby Date: Thu, 15 Jun 2023 22:33:52 +0200 Subject: x86/smp: Dont access non-existing CPUID leaf commit 9b040453d4440659f33dc6f0aa26af418ebfe70b upstream. stop_this_cpu() tests CPUID leaf 0x8000001f::EAX unconditionally. Intel CPUs return the content of the highest supported leaf when a non-existing leaf is read, while AMD CPUs return all zeros for unsupported leafs. So the result of the test on Intel CPUs is lottery. While harmless it's incorrect and causes the conditional wbinvd() to be issued where not required. Check whether the leaf is supported before reading it. [ tglx: Adjusted changelog ] Fixes: 08f253ec3767 ("x86/cpu: Clear SME feature flag when not in use") Signed-off-by: Tony Battersby Signed-off-by: Thomas Gleixner Reviewed-by: Mario Limonciello Reviewed-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/3817d810-e0f1-8ef8-0bbd-663b919ca49b@cybernetics.com Link: https://lore.kernel.org/r/20230615193330.322186388@linutronix.de Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/process.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 93df1f0feeb1..279b5e9be80f 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -748,6 +748,7 @@ struct cpumask cpus_stop_mask; void __noreturn stop_this_cpu(void *dummy) { + struct cpuinfo_x86 *c = this_cpu_ptr(&cpu_info); unsigned int cpu = smp_processor_id(); local_irq_disable(); @@ -762,7 +763,7 @@ void __noreturn stop_this_cpu(void *dummy) */ set_cpu_online(cpu, false); disable_local_APIC(); - mcheck_cpu_clear(this_cpu_ptr(&cpu_info)); + mcheck_cpu_clear(c); /* * Use wbinvd on processors that support SME. This provides support @@ -776,7 +777,7 @@ void __noreturn stop_this_cpu(void *dummy) * Test the CPUID bit directly because the machine might've cleared * X86_FEATURE_SME due to cmdline options. */ - if (cpuid_eax(0x8000001f) & BIT(0)) + if (c->extended_cpuid_level >= 0x8000001f && (cpuid_eax(0x8000001f) & BIT(0))) native_wbinvd(); /* -- cgit 1.4.1 From 50a1abc67702f76968162402d8fb113dd6e22f31 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 15 Jun 2023 22:33:54 +0200 Subject: x86/smp: Remove pointless wmb()s from native_stop_other_cpus() commit 2affa6d6db28855e6340b060b809c23477aa546e upstream. The wmb()s before sending the IPIs are not synchronizing anything. If at all then the apic IPI functions have to provide or act as appropriate barriers. Remove these cargo cult barriers which have no explanation of what they are synchronizing. Signed-off-by: Thomas Gleixner Reviewed-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615193330.378358382@linutronix.de Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/smp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 935bc6562fa4..d842875f986f 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -184,9 +184,6 @@ static void native_stop_other_cpus(int wait) cpumask_clear_cpu(cpu, &cpus_stop_mask); if (!cpumask_empty(&cpus_stop_mask)) { - /* sync above data before sending IRQ */ - wmb(); - apic_send_IPI_allbutself(REBOOT_VECTOR); /* @@ -208,9 +205,6 @@ static void native_stop_other_cpus(int wait) * CPUs to stop. */ if (!smp_no_nmi_ipi && !register_stop_handler()) { - /* Sync above data before sending IRQ */ - wmb(); - pr_emerg("Shutting down cpus with NMI\n"); for_each_cpu(cpu, &cpus_stop_mask) -- cgit 1.4.1 From 6d3b2e0aef6c0118596928f697cb4471f6258a26 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 15 Jun 2023 22:33:55 +0200 Subject: x86/smp: Use dedicated cache-line for mwait_play_dead() commit f9c9987bf52f4e42e940ae217333ebb5a4c3b506 upstream. Monitoring idletask::thread_info::flags in mwait_play_dead() has been an obvious choice as all what is needed is a cache line which is not written by other CPUs. But there is a use case where a "dead" CPU needs to be brought out of MWAIT: kexec(). This is required as kexec() can overwrite text, pagetables, stacks and the monitored cacheline of the original kernel. The latter causes MWAIT to resume execution which obviously causes havoc on the kexec kernel which results usually in triple faults. Use a dedicated per CPU storage to prepare for that. Signed-off-by: Thomas Gleixner Reviewed-by: Ashok Raj Reviewed-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615193330.434553750@linutronix.de Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/smpboot.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 3f3ea0287f69..b96f983e64cc 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -99,6 +99,17 @@ EXPORT_PER_CPU_SYMBOL(cpu_die_map); DEFINE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info); EXPORT_PER_CPU_SYMBOL(cpu_info); +struct mwait_cpu_dead { + unsigned int control; + unsigned int status; +}; + +/* + * Cache line aligned data for mwait_play_dead(). Separate on purpose so + * that it's unlikely to be touched by other CPUs. + */ +static DEFINE_PER_CPU_ALIGNED(struct mwait_cpu_dead, mwait_cpu_dead); + /* Logical package management. We might want to allocate that dynamically */ unsigned int __max_logical_packages __read_mostly; EXPORT_SYMBOL(__max_logical_packages); @@ -1746,10 +1757,10 @@ EXPORT_SYMBOL_GPL(cond_wakeup_cpu0); */ static inline void mwait_play_dead(void) { + struct mwait_cpu_dead *md = this_cpu_ptr(&mwait_cpu_dead); unsigned int eax, ebx, ecx, edx; unsigned int highest_cstate = 0; unsigned int highest_subcstate = 0; - void *mwait_ptr; int i; if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD || @@ -1784,13 +1795,6 @@ static inline void mwait_play_dead(void) (highest_subcstate - 1); } - /* - * This should be a memory location in a cache line which is - * unlikely to be touched by other processors. The actual - * content is immaterial as it is not actually modified in any way. - */ - mwait_ptr = ¤t_thread_info()->flags; - wbinvd(); while (1) { @@ -1802,9 +1806,9 @@ static inline void mwait_play_dead(void) * case where we return around the loop. */ mb(); - clflush(mwait_ptr); + clflush(md); mb(); - __monitor(mwait_ptr, 0, 0); + __monitor(md, 0, 0); mb(); __mwait(eax, 0); -- cgit 1.4.1 From 0af4750eaaeda20bc2ce8da414d85cc1653ae240 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 15 Jun 2023 22:33:57 +0200 Subject: x86/smp: Cure kexec() vs. mwait_play_dead() breakage commit d7893093a7417527c0d73c9832244e65c9d0114f upstream. TLDR: It's a mess. When kexec() is executed on a system with offline CPUs, which are parked in mwait_play_dead() it can end up in a triple fault during the bootup of the kexec kernel or cause hard to diagnose data corruption. The reason is that kexec() eventually overwrites the previous kernel's text, page tables, data and stack. If it writes to the cache line which is monitored by a previously offlined CPU, MWAIT resumes execution and ends up executing the wrong text, dereferencing overwritten page tables or corrupting the kexec kernels data. Cure this by bringing the offlined CPUs out of MWAIT into HLT. Write to the monitored cache line of each offline CPU, which makes MWAIT resume execution. The written control word tells the offlined CPUs to issue HLT, which does not have the MWAIT problem. That does not help, if a stray NMI, MCE or SMI hits the offlined CPUs as those make it come out of HLT. A follow up change will put them into INIT, which protects at least against NMI and SMI. Fixes: ea53069231f9 ("x86, hotplug: Use mwait to offline a processor, fix the legacy case") Reported-by: Ashok Raj Signed-off-by: Thomas Gleixner Tested-by: Ashok Raj Reviewed-by: Ashok Raj Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615193330.492257119@linutronix.de Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/smp.h | 2 ++ arch/x86/kernel/smp.c | 5 ++++ arch/x86/kernel/smpboot.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h index a73bced40e24..b3b34032ef23 100644 --- a/arch/x86/include/asm/smp.h +++ b/arch/x86/include/asm/smp.h @@ -132,6 +132,8 @@ void wbinvd_on_cpu(int cpu); int wbinvd_on_all_cpus(void); void cond_wakeup_cpu0(void); +void smp_kick_mwait_play_dead(void); + void native_smp_send_reschedule(int cpu); void native_send_call_func_ipi(const struct cpumask *mask); void native_send_call_func_single_ipi(int cpu); diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index d842875f986f..174d6232b87f 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -157,6 +158,10 @@ static void native_stop_other_cpus(int wait) if (atomic_cmpxchg(&stopping_cpu, -1, cpu) != -1) return; + /* For kexec, ensure that offline CPUs are out of MWAIT and in HLT */ + if (kexec_in_progress) + smp_kick_mwait_play_dead(); + /* * 1) Send an IPI on the reboot vector to all other CPUs. * diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index b96f983e64cc..f32ee967414e 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,9 @@ struct mwait_cpu_dead { unsigned int status; }; +#define CPUDEAD_MWAIT_WAIT 0xDEADBEEF +#define CPUDEAD_MWAIT_KEXEC_HLT 0x4A17DEAD + /* * Cache line aligned data for mwait_play_dead(). Separate on purpose so * that it's unlikely to be touched by other CPUs. @@ -166,6 +170,10 @@ static void smp_callin(void) { int cpuid; + /* Mop up eventual mwait_play_dead() wreckage */ + this_cpu_write(mwait_cpu_dead.status, 0); + this_cpu_write(mwait_cpu_dead.control, 0); + /* * If waken up by an INIT in an 82489DX configuration * cpu_callout_mask guarantees we don't get here before @@ -1795,6 +1803,10 @@ static inline void mwait_play_dead(void) (highest_subcstate - 1); } + /* Set up state for the kexec() hack below */ + md->status = CPUDEAD_MWAIT_WAIT; + md->control = CPUDEAD_MWAIT_WAIT; + wbinvd(); while (1) { @@ -1812,10 +1824,57 @@ static inline void mwait_play_dead(void) mb(); __mwait(eax, 0); + if (READ_ONCE(md->control) == CPUDEAD_MWAIT_KEXEC_HLT) { + /* + * Kexec is about to happen. Don't go back into mwait() as + * the kexec kernel might overwrite text and data including + * page tables and stack. So mwait() would resume when the + * monitor cache line is written to and then the CPU goes + * south due to overwritten text, page tables and stack. + * + * Note: This does _NOT_ protect against a stray MCE, NMI, + * SMI. They will resume execution at the instruction + * following the HLT instruction and run into the problem + * which this is trying to prevent. + */ + WRITE_ONCE(md->status, CPUDEAD_MWAIT_KEXEC_HLT); + while(1) + native_halt(); + } + cond_wakeup_cpu0(); } } +/* + * Kick all "offline" CPUs out of mwait on kexec(). See comment in + * mwait_play_dead(). + */ +void smp_kick_mwait_play_dead(void) +{ + u32 newstate = CPUDEAD_MWAIT_KEXEC_HLT; + struct mwait_cpu_dead *md; + unsigned int cpu, i; + + for_each_cpu_andnot(cpu, cpu_present_mask, cpu_online_mask) { + md = per_cpu_ptr(&mwait_cpu_dead, cpu); + + /* Does it sit in mwait_play_dead() ? */ + if (READ_ONCE(md->status) != CPUDEAD_MWAIT_WAIT) + continue; + + /* Wait up to 5ms */ + for (i = 0; READ_ONCE(md->status) != newstate && i < 1000; i++) { + /* Bring it out of mwait */ + WRITE_ONCE(md->control, newstate); + udelay(5); + } + + if (READ_ONCE(md->status) != newstate) + pr_err_once("CPU%u is stuck in mwait_play_dead()\n", cpu); + } +} + void hlt_play_dead(void) { if (__this_cpu_read(cpu_info.x86) >= 4) -- cgit 1.4.1 From 31cde3bdadca2c660f768b3df43425e88041a691 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 7 Jun 2023 09:27:08 +0200 Subject: can: isotp: isotp_sendmsg(): fix return error fix on TX path commit e38910c0072b541a91954682c8b074a93e57c09b upstream. With commit d674a8f123b4 ("can: isotp: isotp_sendmsg(): fix return error on FC timeout on TX path") the missing correct return value in the case of a protocol error was introduced. But the way the error value has been read and sent to the user space does not follow the common scheme to clear the error after reading which is provided by the sock_error() function. This leads to an error report at the following write() attempt although everything should be working. Fixes: d674a8f123b4 ("can: isotp: isotp_sendmsg(): fix return error on FC timeout on TX path") Reported-by: Carsten Schmidt Signed-off-by: Oliver Hartkopp Link: https://lore.kernel.org/all/20230607072708.38809-1-socketcan@hartkopp.net Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 1af623839bff..b3c2a49b189c 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1079,8 +1079,9 @@ wait_free_buffer: if (err) goto err_event_drop; - if (sk->sk_err) - return -sk->sk_err; + err = sock_error(sk); + if (err) + return err; } return size; -- cgit 1.4.1 From 4e2ad53ababeaac44d71162650984abfe783960c Mon Sep 17 00:00:00 2001 From: Peng Zhang Date: Sat, 6 May 2023 10:47:52 +0800 Subject: maple_tree: fix potential out-of-bounds access in mas_wr_end_piv() commit cd00dd2585c4158e81fdfac0bbcc0446afbad26d upstream. Check the write offset end bounds before using it as the offset into the pivot array. This avoids a possible out-of-bounds access on the pivot array if the write extends to the last slot in the node, in which case the node maximum should be used as the end pivot. akpm: this doesn't affect any current callers, but new users of mapletree may encounter this problem if backported into earlier kernels, so let's fix it in -stable kernels in case of this. Link: https://lkml.kernel.org/r/20230506024752.2550-1-zhangpeng.00@bytedance.com Fixes: 54a611b60590 ("Maple Tree: add new data structure") Signed-off-by: Peng Zhang Reviewed-by: Liam R. Howlett Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- lib/maple_tree.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/maple_tree.c b/lib/maple_tree.c index b72268848ade..2f1f661157c9 100644 --- a/lib/maple_tree.c +++ b/lib/maple_tree.c @@ -4281,11 +4281,13 @@ done: static inline void mas_wr_end_piv(struct ma_wr_state *wr_mas) { - while ((wr_mas->mas->last > wr_mas->end_piv) && - (wr_mas->offset_end < wr_mas->node_end)) - wr_mas->end_piv = wr_mas->pivots[++wr_mas->offset_end]; + while ((wr_mas->offset_end < wr_mas->node_end) && + (wr_mas->mas->last > wr_mas->pivots[wr_mas->offset_end])) + wr_mas->offset_end++; - if (wr_mas->mas->last > wr_mas->end_piv) + if (wr_mas->offset_end < wr_mas->node_end) + wr_mas->end_piv = wr_mas->pivots[wr_mas->offset_end]; + else wr_mas->end_piv = wr_mas->mas->max; } @@ -4442,7 +4444,6 @@ static inline void *mas_wr_store_entry(struct ma_wr_state *wr_mas) } /* At this point, we are at the leaf node that needs to be altered. */ - wr_mas->end_piv = wr_mas->r_max; mas_wr_end_piv(wr_mas); if (!wr_mas->entry) -- cgit 1.4.1 From d6a5c7a1a6e52d4c46fe181237ca96cd46a42386 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2023 15:17:36 -0700 Subject: mm: introduce new 'lock_mm_and_find_vma()' page fault helper commit c2508ec5a58db67093f4fb8bf89a9a7c53a109e9 upstream. .. and make x86 use it. This basically extracts the existing x86 "find and expand faulting vma" code, but extends it to also take the mmap lock for writing in case we actually do need to expand the vma. We've historically short-circuited that case, and have some rather ugly special logic to serialize the stack segment expansion (since we only hold the mmap lock for reading) that doesn't match the normal VM locking. That slight violation of locking worked well, right up until it didn't: the maple tree code really does want proper locking even for simple extension of an existing vma. So extract the code for "look up the vma of the fault" from x86, fix it up to do the necessary write locking, and make it available as a helper function for other architectures that can use the common helper. Note: I say "common helper", but it really only handles the normal stack-grows-down case. Which is all architectures except for PA-RISC and IA64. So some rare architectures can't use the helper, but if they care they'll just need to open-code this logic. It's also worth pointing out that this code really would like to have an optimistic "mmap_upgrade_trylock()" to make it quicker to go from a read-lock (for the common case) to taking the write lock (for having to extend the vma) in the normal single-threaded situation where there is no other locking activity. But that _is_ all the very uncommon special case, so while it would be nice to have such an operation, it probably doesn't matter in reality. I did put in the skeleton code for such a possible future expansion, even if it only acts as pseudo-documentation for what we're doing. Signed-off-by: Linus Torvalds [6.1: Ignore CONFIG_PER_VMA_LOCK context] Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/x86/Kconfig | 1 + arch/x86/mm/fault.c | 52 +--------------------- include/linux/mm.h | 2 + mm/Kconfig | 4 ++ mm/memory.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 50 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b2c0fce3f257..b3d5706579d4 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -271,6 +271,7 @@ config X86 select HAVE_GENERIC_VDSO select HOTPLUG_SMT if SMP select IRQ_FORCED_THREADING + select LOCK_MM_AND_FIND_VMA select NEED_PER_CPU_EMBED_FIRST_CHUNK select NEED_PER_CPU_PAGE_FIRST_CHUNK select NEED_SG_DMA_LENGTH diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 7b0d4ab894c8..1dbbad73192a 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -900,12 +900,6 @@ __bad_area(struct pt_regs *regs, unsigned long error_code, __bad_area_nosemaphore(regs, error_code, address, pkey, si_code); } -static noinline void -bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address) -{ - __bad_area(regs, error_code, address, 0, SEGV_MAPERR); -} - static inline bool bad_area_access_from_pkeys(unsigned long error_code, struct vm_area_struct *vma) { @@ -1354,51 +1348,10 @@ void do_user_addr_fault(struct pt_regs *regs, } #endif - /* - * Kernel-mode access to the user address space should only occur - * on well-defined single instructions listed in the exception - * tables. But, an erroneous kernel fault occurring outside one of - * those areas which also holds mmap_lock might deadlock attempting - * to validate the fault against the address space. - * - * Only do the expensive exception table search when we might be at - * risk of a deadlock. This happens if we - * 1. Failed to acquire mmap_lock, and - * 2. The access did not originate in userspace. - */ - if (unlikely(!mmap_read_trylock(mm))) { - if (!user_mode(regs) && !search_exception_tables(regs->ip)) { - /* - * Fault from code in kernel from - * which we do not expect faults. - */ - bad_area_nosemaphore(regs, error_code, address); - return; - } retry: - mmap_read_lock(mm); - } else { - /* - * The above down_read_trylock() might have succeeded in - * which case we'll have missed the might_sleep() from - * down_read(): - */ - might_sleep(); - } - - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (unlikely(!vma)) { - bad_area(regs, error_code, address); - return; - } - if (likely(vma->vm_start <= address)) - goto good_area; - if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) { - bad_area(regs, error_code, address); - return; - } - if (unlikely(expand_stack(vma, address))) { - bad_area(regs, error_code, address); + bad_area_nosemaphore(regs, error_code, address); return; } @@ -1406,7 +1359,6 @@ retry: * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ -good_area: if (unlikely(access_error(error_code, vma))) { bad_area_access_error(regs, error_code, address, vma); return; diff --git a/include/linux/mm.h b/include/linux/mm.h index 49e14d3afd44..8e1e89d10a4b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1932,6 +1932,8 @@ void unmap_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t nr, bool even_cows); void unmap_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen, int even_cows); +struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, + unsigned long address, struct pt_regs *regs); #else static inline vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address, unsigned int flags, diff --git a/mm/Kconfig b/mm/Kconfig index 57e1d8c5b505..35109a4a2f7c 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -1150,6 +1150,10 @@ config LRU_GEN_STATS This option has a per-memcg and per-node memory overhead. # } +config LOCK_MM_AND_FIND_VMA + bool + depends on !STACK_GROWSUP + source "mm/damon/Kconfig" endmenu diff --git a/mm/memory.c b/mm/memory.c index bc8b2bd295cb..04a7c3540247 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5258,6 +5258,127 @@ vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address, } EXPORT_SYMBOL_GPL(handle_mm_fault); +#ifdef CONFIG_LOCK_MM_AND_FIND_VMA +#include + +static inline bool get_mmap_lock_carefully(struct mm_struct *mm, struct pt_regs *regs) +{ + /* Even if this succeeds, make it clear we *might* have slept */ + if (likely(mmap_read_trylock(mm))) { + might_sleep(); + return true; + } + + if (regs && !user_mode(regs)) { + unsigned long ip = instruction_pointer(regs); + if (!search_exception_tables(ip)) + return false; + } + + mmap_read_lock(mm); + return true; +} + +static inline bool mmap_upgrade_trylock(struct mm_struct *mm) +{ + /* + * We don't have this operation yet. + * + * It should be easy enough to do: it's basically a + * atomic_long_try_cmpxchg_acquire() + * from RWSEM_READER_BIAS -> RWSEM_WRITER_LOCKED, but + * it also needs the proper lockdep magic etc. + */ + return false; +} + +static inline bool upgrade_mmap_lock_carefully(struct mm_struct *mm, struct pt_regs *regs) +{ + mmap_read_unlock(mm); + if (regs && !user_mode(regs)) { + unsigned long ip = instruction_pointer(regs); + if (!search_exception_tables(ip)) + return false; + } + mmap_write_lock(mm); + return true; +} + +/* + * Helper for page fault handling. + * + * This is kind of equivalend to "mmap_read_lock()" followed + * by "find_extend_vma()", except it's a lot more careful about + * the locking (and will drop the lock on failure). + * + * For example, if we have a kernel bug that causes a page + * fault, we don't want to just use mmap_read_lock() to get + * the mm lock, because that would deadlock if the bug were + * to happen while we're holding the mm lock for writing. + * + * So this checks the exception tables on kernel faults in + * order to only do this all for instructions that are actually + * expected to fault. + * + * We can also actually take the mm lock for writing if we + * need to extend the vma, which helps the VM layer a lot. + */ +struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, + unsigned long addr, struct pt_regs *regs) +{ + struct vm_area_struct *vma; + + if (!get_mmap_lock_carefully(mm, regs)) + return NULL; + + vma = find_vma(mm, addr); + if (likely(vma && (vma->vm_start <= addr))) + return vma; + + /* + * Well, dang. We might still be successful, but only + * if we can extend a vma to do so. + */ + if (!vma || !(vma->vm_flags & VM_GROWSDOWN)) { + mmap_read_unlock(mm); + return NULL; + } + + /* + * We can try to upgrade the mmap lock atomically, + * in which case we can continue to use the vma + * we already looked up. + * + * Otherwise we'll have to drop the mmap lock and + * re-take it, and also look up the vma again, + * re-checking it. + */ + if (!mmap_upgrade_trylock(mm)) { + if (!upgrade_mmap_lock_carefully(mm, regs)) + return NULL; + + vma = find_vma(mm, addr); + if (!vma) + goto fail; + if (vma->vm_start <= addr) + goto success; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto fail; + } + + if (expand_stack(vma, addr)) + goto fail; + +success: + mmap_write_downgrade(mm); + return vma; + +fail: + mmap_write_unlock(mm); + return NULL; +} +#endif + #ifndef __PAGETABLE_P4D_FOLDED /* * Allocate p4d page table. -- cgit 1.4.1 From 755aa1bc6aaf9961aa4bdb54f32faaba06c08792 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2023 16:17:48 -0700 Subject: mm: make the page fault mmap locking killable commit eda0047296a16d65a7f2bc60a408f70d178b2014 upstream. This is done as a separate patch from introducing the new lock_mm_and_find_vma() helper, because while it's an obvious change, it's not what x86 used to do in this area. We already abort the page fault on fatal signals anyway, so why should we wait for the mmap lock only to then abort later? With the new helper function that returns without the lock held on failure anyway, this is particularly easy and straightforward. Signed-off-by: Linus Torvalds Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 04a7c3540247..ade4d9eb52d8 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5275,8 +5275,7 @@ static inline bool get_mmap_lock_carefully(struct mm_struct *mm, struct pt_regs return false; } - mmap_read_lock(mm); - return true; + return !mmap_read_lock_killable(mm); } static inline bool mmap_upgrade_trylock(struct mm_struct *mm) @@ -5300,8 +5299,7 @@ static inline bool upgrade_mmap_lock_carefully(struct mm_struct *mm, struct pt_r if (!search_exception_tables(ip)) return false; } - mmap_write_lock(mm); - return true; + return !mmap_write_lock_killable(mm); } /* -- cgit 1.4.1 From b92cd80e5f0b14760a49ff68da23959a38452cda Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2023 17:11:44 -0700 Subject: arm64/mm: Convert to using lock_mm_and_find_vma() commit ae870a68b5d13d67cf4f18d47bb01ee3fee40acb upstream. This converts arm64 to use the new page fault helper. It was very straightforward, but still needed a fix for the "obvious" conversion I initially did. Thanks to Suren for the fix and testing. Fixed-and-tested-by: Suren Baghdasaryan Unnecessary-code-removal-by: Liam R. Howlett Signed-off-by: Linus Torvalds [6.1: Ignore CONFIG_PER_VMA_LOCK context] Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/arm64/Kconfig | 1 + arch/arm64/mm/fault.c | 46 +++++++++------------------------------------- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 43ff7c7a3ac9..20ee745c118a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -211,6 +211,7 @@ config ARM64 select IRQ_DOMAIN select IRQ_FORCED_THREADING select KASAN_VMALLOC if KASAN + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select NEED_DMA_MAP_STATE select NEED_SG_DMA_LENGTH diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 2fef1fa93e7b..6b6b8a82f294 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -483,27 +483,14 @@ static void do_bad_area(unsigned long far, unsigned long esr, #define VM_FAULT_BADMAP ((__force vm_fault_t)0x010000) #define VM_FAULT_BADACCESS ((__force vm_fault_t)0x020000) -static vm_fault_t __do_page_fault(struct mm_struct *mm, unsigned long addr, +static vm_fault_t __do_page_fault(struct mm_struct *mm, + struct vm_area_struct *vma, unsigned long addr, unsigned int mm_flags, unsigned long vm_flags, struct pt_regs *regs) { - struct vm_area_struct *vma = find_vma(mm, addr); - - if (unlikely(!vma)) - return VM_FAULT_BADMAP; - /* * Ok, we have a good vm_area for this memory access, so we can handle * it. - */ - if (unlikely(vma->vm_start > addr)) { - if (!(vma->vm_flags & VM_GROWSDOWN)) - return VM_FAULT_BADMAP; - if (expand_stack(vma, addr)) - return VM_FAULT_BADMAP; - } - - /* * Check that the permissions on the VMA allow for the fault which * occurred. */ @@ -535,6 +522,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr, unsigned long vm_flags; unsigned int mm_flags = FAULT_FLAG_DEFAULT; unsigned long addr = untagged_addr(far); + struct vm_area_struct *vma; if (kprobe_page_fault(regs, esr)) return 0; @@ -585,31 +573,14 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr, perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); - /* - * As per x86, we may deadlock here. However, since the kernel only - * validly references user space from well defined areas of the code, - * we can bug out early if this is from code which shouldn't. - */ - if (!mmap_read_trylock(mm)) { - if (!user_mode(regs) && !search_exception_tables(regs->pc)) - goto no_context; retry: - mmap_read_lock(mm); - } else { - /* - * The above mmap_read_trylock() might have succeeded in which - * case, we'll have missed the might_sleep() from down_read(). - */ - might_sleep(); -#ifdef CONFIG_DEBUG_VM - if (!user_mode(regs) && !search_exception_tables(regs->pc)) { - mmap_read_unlock(mm); - goto no_context; - } -#endif + vma = lock_mm_and_find_vma(mm, addr, regs); + if (unlikely(!vma)) { + fault = VM_FAULT_BADMAP; + goto done; } - fault = __do_page_fault(mm, addr, mm_flags, vm_flags, regs); + fault = __do_page_fault(mm, vma, addr, mm_flags, vm_flags, regs); /* Quick path to respond to signals */ if (fault_signal_pending(fault, regs)) { @@ -628,6 +599,7 @@ retry: } mmap_read_unlock(mm); +done: /* * Handle the "normal" (no error) case first. */ -- cgit 1.4.1 From 82972ea17b47e2f9b08a91d62e92731367475f11 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 16 Jun 2023 15:51:29 +1000 Subject: powerpc/mm: Convert to using lock_mm_and_find_vma() commit e6fe228c4ffafdfc970cf6d46883a1f481baf7ea upstream. Signed-off-by: Michael Ellerman Signed-off-by: Linus Torvalds Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/Kconfig | 1 + arch/powerpc/mm/fault.c | 39 +++------------------------------------ 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 2b1141645d9e..6050e6e10d32 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -257,6 +257,7 @@ config PPC select IRQ_DOMAIN select IRQ_FORCED_THREADING select KASAN_VMALLOC if KASAN && MODULES + select LOCK_MM_AND_FIND_VMA select MMU_GATHER_PAGE_SIZE select MMU_GATHER_RCU_TABLE_FREE select MMU_GATHER_MERGE_VMAS diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index af46aa88422b..644e4ec6ce99 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -84,11 +84,6 @@ static int __bad_area(struct pt_regs *regs, unsigned long address, int si_code) return __bad_area_nosemaphore(regs, address, si_code); } -static noinline int bad_area(struct pt_regs *regs, unsigned long address) -{ - return __bad_area(regs, address, SEGV_MAPERR); -} - static noinline int bad_access_pkey(struct pt_regs *regs, unsigned long address, struct vm_area_struct *vma) { @@ -481,40 +476,12 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address, * we will deadlock attempting to validate the fault against the * address space. Luckily the kernel only validly references user * space from well defined areas of code, which are listed in the - * exceptions table. - * - * As the vast majority of faults will be valid we will only perform - * the source reference check when there is a possibility of a deadlock. - * Attempt to lock the address space, if we cannot we then validate the - * source. If this is invalid we can skip the address space check, - * thus avoiding the deadlock. + * exceptions table. lock_mm_and_find_vma() handles that logic. */ - if (unlikely(!mmap_read_trylock(mm))) { - if (!is_user && !search_exception_tables(regs->nip)) - return bad_area_nosemaphore(regs, address); - retry: - mmap_read_lock(mm); - } else { - /* - * The above down_read_trylock() might have succeeded in - * which case we'll have missed the might_sleep() from - * down_read(): - */ - might_sleep(); - } - - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (unlikely(!vma)) - return bad_area(regs, address); - - if (unlikely(vma->vm_start > address)) { - if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) - return bad_area(regs, address); - - if (unlikely(expand_stack(vma, address))) - return bad_area(regs, address); - } + return bad_area_nosemaphore(regs, address); if (unlikely(access_pkey_error(is_write, is_exec, (error_code & DSISR_KEYFAULT), vma))) -- cgit 1.4.1 From 7227d70acc7813c77e797be00503177ce484228a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 22 Jun 2023 18:47:40 +0200 Subject: mips/mm: Convert to using lock_mm_and_find_vma() commit 4bce37a68ff884e821a02a731897a8119e0c37b7 upstream. Signed-off-by: Ben Hutchings Signed-off-by: Linus Torvalds Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/mips/Kconfig | 1 + arch/mips/mm/fault.c | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 2f5835e300a8..7b0856c76c9a 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -94,6 +94,7 @@ config MIPS select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT || !SMP select IRQ_FORCED_THREADING select ISA if EISA + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_REL if MODULES select MODULES_USE_ELF_RELA if MODULES && 64BIT select PERF_USE_VMALLOC diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index a27045f5a556..d7878208bd3f 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -99,21 +99,13 @@ static void __do_page_fault(struct pt_regs *regs, unsigned long write, perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); retry: - mmap_read_lock(mm); - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + goto bad_area_nosemaphore; /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ -good_area: si_code = SEGV_ACCERR; if (write) { -- cgit 1.4.1 From ac764deea709b4d13fa78265cb2ec463da05a5d6 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 22 Jun 2023 20:18:18 +0200 Subject: riscv/mm: Convert to using lock_mm_and_find_vma() commit 7267ef7b0b77f4ed23b7b3c87d8eca7bd9c2d007 upstream. Signed-off-by: Ben Hutchings Signed-off-by: Linus Torvalds [6.1: Kconfig context] Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/riscv/Kconfig | 1 + arch/riscv/mm/fault.c | 31 +++++++++++++------------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a85bbe28dcf4..6bf8dc0b8f93 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -114,6 +114,7 @@ config RISCV select HAVE_RSEQ select IRQ_DOMAIN select IRQ_FORCED_THREADING + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA if MODULES select MODULE_SECTIONS if MODULES select OF diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c index eb0774d9c03b..274bc6dd839f 100644 --- a/arch/riscv/mm/fault.c +++ b/arch/riscv/mm/fault.c @@ -83,13 +83,13 @@ static inline void mm_fault_error(struct pt_regs *regs, unsigned long addr, vm_f BUG(); } -static inline void bad_area(struct pt_regs *regs, struct mm_struct *mm, int code, unsigned long addr) +static inline void +bad_area_nosemaphore(struct pt_regs *regs, int code, unsigned long addr) { /* * Something tried to access memory that isn't in our memory map. * Fix it, but check if it's kernel or user first. */ - mmap_read_unlock(mm); /* User mode accesses just cause a SIGSEGV */ if (user_mode(regs)) { do_trap(regs, SIGSEGV, code, addr); @@ -99,6 +99,15 @@ static inline void bad_area(struct pt_regs *regs, struct mm_struct *mm, int code no_context(regs, addr); } +static inline void +bad_area(struct pt_regs *regs, struct mm_struct *mm, int code, + unsigned long addr) +{ + mmap_read_unlock(mm); + + bad_area_nosemaphore(regs, code, addr); +} + static inline void vmalloc_fault(struct pt_regs *regs, int code, unsigned long addr) { pgd_t *pgd, *pgd_k; @@ -281,23 +290,10 @@ asmlinkage void do_page_fault(struct pt_regs *regs) else if (cause == EXC_INST_PAGE_FAULT) flags |= FAULT_FLAG_INSTRUCTION; retry: - mmap_read_lock(mm); - vma = find_vma(mm, addr); + vma = lock_mm_and_find_vma(mm, addr, regs); if (unlikely(!vma)) { tsk->thread.bad_cause = cause; - bad_area(regs, mm, code, addr); - return; - } - if (likely(vma->vm_start <= addr)) - goto good_area; - if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) { - tsk->thread.bad_cause = cause; - bad_area(regs, mm, code, addr); - return; - } - if (unlikely(expand_stack(vma, addr))) { - tsk->thread.bad_cause = cause; - bad_area(regs, mm, code, addr); + bad_area_nosemaphore(regs, code, addr); return; } @@ -305,7 +301,6 @@ retry: * Ok, we have a good vm_area for this memory access, so * we can handle it. */ -good_area: code = SEGV_ACCERR; if (unlikely(access_error(cause, vma))) { -- cgit 1.4.1 From 1f4197f050dec016783663682b9eccbb603befa7 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 22 Jun 2023 21:24:30 +0200 Subject: arm/mm: Convert to using lock_mm_and_find_vma() commit 8b35ca3e45e35a26a21427f35d4093606e93ad0a upstream. arm has an additional check for address < FIRST_USER_ADDRESS before expanding the stack. Since FIRST_USER_ADDRESS is defined everywhere (generally as 0), move that check to the generic expand_downwards(). Signed-off-by: Ben Hutchings Signed-off-by: Linus Torvalds Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/arm/Kconfig | 1 + arch/arm/mm/fault.c | 63 ++++++++++++----------------------------------------- mm/mmap.c | 2 +- 3 files changed, 16 insertions(+), 50 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a08c9d092a33..0202e48e7a20 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -122,6 +122,7 @@ config ARM select HAVE_UID16 select HAVE_VIRT_CPU_ACCOUNTING_GEN select IRQ_FORCED_THREADING + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_REL select NEED_DMA_MAP_STATE select OF_EARLY_FLATTREE if OF diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index de988cba9a4b..b0db85310331 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -231,37 +231,11 @@ static inline bool is_permission_fault(unsigned int fsr) return false; } -static vm_fault_t __kprobes -__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int flags, - unsigned long vma_flags, struct pt_regs *regs) -{ - struct vm_area_struct *vma = find_vma(mm, addr); - if (unlikely(!vma)) - return VM_FAULT_BADMAP; - - if (unlikely(vma->vm_start > addr)) { - if (!(vma->vm_flags & VM_GROWSDOWN)) - return VM_FAULT_BADMAP; - if (addr < FIRST_USER_ADDRESS) - return VM_FAULT_BADMAP; - if (expand_stack(vma, addr)) - return VM_FAULT_BADMAP; - } - - /* - * ok, we have a good vm_area for this memory access, check the - * permissions on the VMA allow for the fault which occurred. - */ - if (!(vma->vm_flags & vma_flags)) - return VM_FAULT_BADACCESS; - - return handle_mm_fault(vma, addr & PAGE_MASK, flags, regs); -} - static int __kprobes do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; int sig, code; vm_fault_t fault; unsigned int flags = FAULT_FLAG_DEFAULT; @@ -300,31 +274,21 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); - /* - * As per x86, we may deadlock here. However, since the kernel only - * validly references user space from well defined areas of the code, - * we can bug out early if this is from code which shouldn't. - */ - if (!mmap_read_trylock(mm)) { - if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) - goto no_context; retry: - mmap_read_lock(mm); - } else { - /* - * The above down_read_trylock() might have succeeded in - * which case, we'll have missed the might_sleep() from - * down_read() - */ - might_sleep(); -#ifdef CONFIG_DEBUG_VM - if (!user_mode(regs) && - !search_exception_tables(regs->ARM_pc)) - goto no_context; -#endif + vma = lock_mm_and_find_vma(mm, addr, regs); + if (unlikely(!vma)) { + fault = VM_FAULT_BADMAP; + goto bad_area; } - fault = __do_page_fault(mm, addr, flags, vm_flags, regs); + /* + * ok, we have a good vm_area for this memory access, check the + * permissions on the VMA allow for the fault which occurred. + */ + if (!(vma->vm_flags & vm_flags)) + fault = VM_FAULT_BADACCESS; + else + fault = handle_mm_fault(vma, addr & PAGE_MASK, flags, regs); /* If we need to retry but a fatal signal is pending, handle the * signal first. We do not need to release the mmap_lock because @@ -355,6 +319,7 @@ retry: if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS)))) return 0; +bad_area: /* * If we are in kernel mode at this point, we * have no context to handle this fault with. diff --git a/mm/mmap.c b/mm/mmap.c index f031fa267913..fc7d09d81de1 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2045,7 +2045,7 @@ int expand_downwards(struct vm_area_struct *vma, unsigned long address) int error = 0; address &= PAGE_MASK; - if (address < mmap_min_addr) + if (address < mmap_min_addr || address < FIRST_USER_ADDRESS) return -EPERM; /* Enforce stack_guard_gap */ -- cgit 1.4.1 From 21ee33d51bf9f9489c7e0eb8cb17c803e2d03bd0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 24 Jun 2023 10:55:38 -0700 Subject: mm/fault: convert remaining simple cases to lock_mm_and_find_vma() commit a050ba1e7422f2cc60ff8bfde3f96d34d00cb585 upstream. This does the simple pattern conversion of alpha, arc, csky, hexagon, loongarch, nios2, sh, sparc32, and xtensa to the lock_mm_and_find_vma() helper. They all have the regular fault handling pattern without odd special cases. The remaining architectures all have something that keeps us from a straightforward conversion: ia64 and parisc have stacks that can grow both up as well as down (and ia64 has special address region checks). And m68k, microblaze, openrisc, sparc64, and um end up having extra rules about only expanding the stack down a limited amount below the user space stack pointer. That is something that x86 used to do too (long long ago), and it probably could just be skipped, but it still makes the conversion less than trivial. Note that this conversion was done manually and with the exception of alpha without any build testing, because I have a fairly limited cross- building environment. The cases are all simple, and I went through the changes several times, but... Signed-off-by: Linus Torvalds Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/alpha/Kconfig | 1 + arch/alpha/mm/fault.c | 13 +++---------- arch/arc/Kconfig | 1 + arch/arc/mm/fault.c | 11 +++-------- arch/csky/Kconfig | 1 + arch/csky/mm/fault.c | 22 +++++----------------- arch/hexagon/Kconfig | 1 + arch/hexagon/mm/vm_fault.c | 18 ++++-------------- arch/loongarch/Kconfig | 1 + arch/loongarch/mm/fault.c | 16 ++++++---------- arch/nios2/Kconfig | 1 + arch/nios2/mm/fault.c | 17 ++--------------- arch/sh/Kconfig | 1 + arch/sh/mm/fault.c | 17 ++--------------- arch/sparc/Kconfig | 1 + arch/sparc/mm/fault_32.c | 32 ++++++++------------------------ arch/xtensa/Kconfig | 1 + arch/xtensa/mm/fault.c | 14 +++----------- 18 files changed, 45 insertions(+), 124 deletions(-) diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 97fce7386b00..d95d82abdf29 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -28,6 +28,7 @@ config ALPHA select GENERIC_SMP_IDLE_THREAD select HAVE_ARCH_AUDITSYSCALL select HAVE_MOD_ARCH_SPECIFIC + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select ODD_RT_SIGACTION select OLD_SIGSUSPEND diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index ef427a6bdd1a..2b49aa94e4de 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -119,20 +119,12 @@ do_page_fault(unsigned long address, unsigned long mmcsr, flags |= FAULT_FLAG_USER; perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); retry: - mmap_read_lock(mm); - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + goto bad_area_nosemaphore; /* Ok, we have a good vm_area for this memory access, so we can handle it. */ - good_area: si_code = SEGV_ACCERR; if (cause < 0) { if (!(vma->vm_flags & VM_EXEC)) @@ -189,6 +181,7 @@ retry: bad_area: mmap_read_unlock(mm); + bad_area_nosemaphore: if (user_mode(regs)) goto do_sigsegv; diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index d9a13ccf89a3..cb1074f74c3f 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -41,6 +41,7 @@ config ARC select HAVE_PERF_EVENTS select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select OF select OF_EARLY_FLATTREE diff --git a/arch/arc/mm/fault.c b/arch/arc/mm/fault.c index 5ca59a482632..f59e722d147f 100644 --- a/arch/arc/mm/fault.c +++ b/arch/arc/mm/fault.c @@ -113,15 +113,9 @@ void do_page_fault(unsigned long address, struct pt_regs *regs) perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); retry: - mmap_read_lock(mm); - - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (!vma) - goto bad_area; - if (unlikely(address < vma->vm_start)) { - if (!(vma->vm_flags & VM_GROWSDOWN) || expand_stack(vma, address)) - goto bad_area; - } + goto bad_area_nosemaphore; /* * vm_area is good, now check permissions for this memory access @@ -161,6 +155,7 @@ retry: bad_area: mmap_read_unlock(mm); +bad_area_nosemaphore: /* * Major/minor page fault accounting * (in case of retry we only land here once) diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig index adee6ab36862..742009123fd5 100644 --- a/arch/csky/Kconfig +++ b/arch/csky/Kconfig @@ -96,6 +96,7 @@ config CSKY select HAVE_RSEQ select HAVE_STACKPROTECTOR select HAVE_SYSCALL_TRACEPOINTS + select LOCK_MM_AND_FIND_VMA select MAY_HAVE_SPARSE_IRQ select MODULES_USE_ELF_RELA if MODULES select OF diff --git a/arch/csky/mm/fault.c b/arch/csky/mm/fault.c index e15f736cca4b..ae9781b7d92e 100644 --- a/arch/csky/mm/fault.c +++ b/arch/csky/mm/fault.c @@ -97,13 +97,12 @@ static inline void mm_fault_error(struct pt_regs *regs, unsigned long addr, vm_f BUG(); } -static inline void bad_area(struct pt_regs *regs, struct mm_struct *mm, int code, unsigned long addr) +static inline void bad_area_nosemaphore(struct pt_regs *regs, struct mm_struct *mm, int code, unsigned long addr) { /* * Something tried to access memory that isn't in our memory map. * Fix it, but check if it's kernel or user first. */ - mmap_read_unlock(mm); /* User mode accesses just cause a SIGSEGV */ if (user_mode(regs)) { do_trap(regs, SIGSEGV, code, addr); @@ -238,20 +237,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs) if (is_write(regs)) flags |= FAULT_FLAG_WRITE; retry: - mmap_read_lock(mm); - vma = find_vma(mm, addr); + vma = lock_mm_and_find_vma(mm, address, regs); if (unlikely(!vma)) { - bad_area(regs, mm, code, addr); - return; - } - if (likely(vma->vm_start <= addr)) - goto good_area; - if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) { - bad_area(regs, mm, code, addr); - return; - } - if (unlikely(expand_stack(vma, addr))) { - bad_area(regs, mm, code, addr); + bad_area_nosemaphore(regs, mm, code, addr); return; } @@ -259,11 +247,11 @@ retry: * Ok, we have a good vm_area for this memory access, so * we can handle it. */ -good_area: code = SEGV_ACCERR; if (unlikely(access_error(regs, vma))) { - bad_area(regs, mm, code, addr); + mmap_read_unlock(mm); + bad_area_nosemaphore(regs, mm, code, addr); return; } diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig index 54eadf265178..6726f4941015 100644 --- a/arch/hexagon/Kconfig +++ b/arch/hexagon/Kconfig @@ -28,6 +28,7 @@ config HEXAGON select GENERIC_SMP_IDLE_THREAD select STACKTRACE_SUPPORT select GENERIC_CLOCKEVENTS_BROADCAST + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select GENERIC_CPU_DEVICES select ARCH_WANT_LD_ORPHAN_WARN diff --git a/arch/hexagon/mm/vm_fault.c b/arch/hexagon/mm/vm_fault.c index f73c7cbfe326..583b08727166 100644 --- a/arch/hexagon/mm/vm_fault.c +++ b/arch/hexagon/mm/vm_fault.c @@ -57,21 +57,10 @@ void do_page_fault(unsigned long address, long cause, struct pt_regs *regs) perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); retry: - mmap_read_lock(mm); - vma = find_vma(mm, address); - if (!vma) - goto bad_area; + vma = lock_mm_and_find_vma(mm, address, regs); + if (unlikely(!vma)) + goto bad_area_nosemaphore; - if (vma->vm_start <= address) - goto good_area; - - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - - if (expand_stack(vma, address)) - goto bad_area; - -good_area: /* Address space is OK. Now check access rights. */ si_code = SEGV_ACCERR; @@ -140,6 +129,7 @@ good_area: bad_area: mmap_read_unlock(mm); +bad_area_nosemaphore: if (user_mode(regs)) { force_sig_fault(SIGSEGV, si_code, (void __user *)address); return; diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 903096bd87f8..51d738ac12e5 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -107,6 +107,7 @@ config LOONGARCH select HAVE_VIRT_CPU_ACCOUNTING_GEN if !SMP select IRQ_FORCED_THREADING select IRQ_LOONGARCH_CPU + select LOCK_MM_AND_FIND_VMA select MMU_GATHER_MERGE_VMAS if MMU select MODULES_USE_ELF_RELA if MODULES select NEED_PER_CPU_EMBED_FIRST_CHUNK diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c index 1ccd53655cab..b829ab911a17 100644 --- a/arch/loongarch/mm/fault.c +++ b/arch/loongarch/mm/fault.c @@ -166,22 +166,18 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); retry: - mmap_read_lock(mm); - vma = find_vma(mm, address); - if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (!expand_stack(vma, address)) - goto good_area; + vma = lock_mm_and_find_vma(mm, address, regs); + if (unlikely(!vma)) + goto bad_area_nosemaphore; + goto good_area; + /* * Something tried to access memory that isn't in our memory map.. * Fix it, but check if it's kernel or user first.. */ bad_area: mmap_read_unlock(mm); +bad_area_nosemaphore: do_sigsegv(regs, write, address, si_code); return; diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index a582f72104f3..1fb78865a459 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -16,6 +16,7 @@ config NIOS2 select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_KGDB select IRQ_DOMAIN + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select OF select OF_EARLY_FLATTREE diff --git a/arch/nios2/mm/fault.c b/arch/nios2/mm/fault.c index edaca0a6c1c1..71939fb28c2e 100644 --- a/arch/nios2/mm/fault.c +++ b/arch/nios2/mm/fault.c @@ -86,27 +86,14 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause, perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); - if (!mmap_read_trylock(mm)) { - if (!user_mode(regs) && !search_exception_tables(regs->ea)) - goto bad_area_nosemaphore; retry: - mmap_read_lock(mm); - } - - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + goto bad_area_nosemaphore; /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ -good_area: code = SEGV_ACCERR; switch (cause) { diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 5f220e903e5a..8e4d1f757bcc 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -56,6 +56,7 @@ config SUPERH select HAVE_STACKPROTECTOR select HAVE_SYSCALL_TRACEPOINTS select IRQ_FORCED_THREADING + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select NEED_SG_DMA_LENGTH select NO_DMA if !MMU && !DMA_COHERENT diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index acd2f5e50bfc..06e6b4952924 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c @@ -439,21 +439,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, } retry: - mmap_read_lock(mm); - - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (unlikely(!vma)) { - bad_area(regs, error_code, address); - return; - } - if (likely(vma->vm_start <= address)) - goto good_area; - if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) { - bad_area(regs, error_code, address); - return; - } - if (unlikely(expand_stack(vma, address))) { - bad_area(regs, error_code, address); + bad_area_nosemaphore(regs, error_code, address); return; } @@ -461,7 +449,6 @@ retry: * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ -good_area: if (unlikely(access_error(error_code, vma))) { bad_area_access_error(regs, error_code, address); return; diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 84437a4c6545..dbb1760cbe8c 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -56,6 +56,7 @@ config SPARC32 select DMA_DIRECT_REMAP select GENERIC_ATOMIC64 select HAVE_UID16 + select LOCK_MM_AND_FIND_VMA select OLD_SIGACTION select ZONE_DMA diff --git a/arch/sparc/mm/fault_32.c b/arch/sparc/mm/fault_32.c index 91259f291c54..aef2aebe2379 100644 --- a/arch/sparc/mm/fault_32.c +++ b/arch/sparc/mm/fault_32.c @@ -143,28 +143,19 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, if (pagefault_disabled() || !mm) goto no_context; + if (!from_user && address >= PAGE_OFFSET) + goto no_context; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); retry: - mmap_read_lock(mm); - - if (!from_user && address >= PAGE_OFFSET) - goto bad_area; - - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + goto bad_area_nosemaphore; /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ -good_area: code = SEGV_ACCERR; if (write) { if (!(vma->vm_flags & VM_WRITE)) @@ -318,17 +309,9 @@ static void force_user_fault(unsigned long address, int write) code = SEGV_MAPERR; - mmap_read_lock(mm); - vma = find_vma(mm, address); + vma = lock_mm_and_find_vma(mm, address, regs); if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; -good_area: + goto bad_area_nosemaphore; code = SEGV_ACCERR; if (write) { if (!(vma->vm_flags & VM_WRITE)) @@ -347,6 +330,7 @@ good_area: return; bad_area: mmap_read_unlock(mm); +bad_area_nosemaphore: __do_fault_siginfo(code, SIGSEGV, tsk->thread.kregs, address); return; diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index bcb0c5d2abc2..6d3c9257aa13 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -49,6 +49,7 @@ config XTENSA select HAVE_SYSCALL_TRACEPOINTS select HAVE_VIRT_CPU_ACCOUNTING_GEN select IRQ_DOMAIN + select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select PERF_USE_VMALLOC select TRACE_IRQFLAGS_SUPPORT diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c index 8c781b05c0bd..d89b193c779f 100644 --- a/arch/xtensa/mm/fault.c +++ b/arch/xtensa/mm/fault.c @@ -130,23 +130,14 @@ void do_page_fault(struct pt_regs *regs) perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); retry: - mmap_read_lock(mm); - vma = find_vma(mm, address); - + vma = lock_mm_and_find_vma(mm, address, regs); if (!vma) - goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + goto bad_area_nosemaphore; /* Ok, we have a good vm_area for this memory access, so * we can handle it.. */ -good_area: code = SEGV_ACCERR; if (is_write) { @@ -205,6 +196,7 @@ good_area: */ bad_area: mmap_read_unlock(mm); +bad_area_nosemaphore: if (user_mode(regs)) { current->thread.bad_vaddr = address; current->thread.error_code = is_write; -- cgit 1.4.1 From 48c232819e77dcd7ff476e964bc671e0589daae6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 24 Jun 2023 11:17:05 -0700 Subject: powerpc/mm: convert coprocessor fault to lock_mm_and_find_vma() commit 2cd76c50d0b41cec5c87abfcdf25b236a2793fb6 upstream. This is one of the simple cases, except there's no pt_regs pointer. Which is fine, as lock_mm_and_find_vma() is set up to work fine with a NULL pt_regs. Powerpc already enabled LOCK_MM_AND_FIND_VMA for the main CPU faulting, so we can just use the helper without any extra work. Signed-off-by: Linus Torvalds Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/mm/copro_fault.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/mm/copro_fault.c b/arch/powerpc/mm/copro_fault.c index 7c507fb48182..f49fd873df8d 100644 --- a/arch/powerpc/mm/copro_fault.c +++ b/arch/powerpc/mm/copro_fault.c @@ -33,19 +33,11 @@ int copro_handle_mm_fault(struct mm_struct *mm, unsigned long ea, if (mm->pgd == NULL) return -EFAULT; - mmap_read_lock(mm); - ret = -EFAULT; - vma = find_vma(mm, ea); + vma = lock_mm_and_find_vma(mm, ea, NULL); if (!vma) - goto out_unlock; - - if (ea < vma->vm_start) { - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto out_unlock; - if (expand_stack(vma, ea)) - goto out_unlock; - } + return -EFAULT; + ret = -EFAULT; is_write = dsisr & DSISR_ISSTORE; if (is_write) { if (!(vma->vm_flags & VM_WRITE)) -- cgit 1.4.1 From 6a6b5616c3d04eba12dd0abc0522e5bae5f1ee5a Mon Sep 17 00:00:00 2001 From: "Liam R. Howlett" Date: Fri, 16 Jun 2023 15:58:54 -0700 Subject: mm: make find_extend_vma() fail if write lock not held commit f440fa1ac955e2898893f9301568435eb5cdfc4b upstream. Make calls to extend_vma() and find_extend_vma() fail if the write lock is required. To avoid making this a flag-day event, this still allows the old read-locking case for the trivial situations, and passes in a flag to say "is it write-locked". That way write-lockers can say "yes, I'm being careful", and legacy users will continue to work in all the common cases until they have been fully converted to the new world order. Co-Developed-by: Matthew Wilcox (Oracle) Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Liam R. Howlett Signed-off-by: Linus Torvalds Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_elf.c | 6 +++--- fs/exec.c | 5 +++-- include/linux/mm.h | 10 +++++++--- mm/memory.c | 2 +- mm/mmap.c | 50 +++++++++++++++++++++++++++++++++----------------- mm/nommu.c | 3 ++- 6 files changed, 49 insertions(+), 27 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 444302afc673..5688c3e6adc1 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -315,10 +315,10 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, * Grow the stack manually; some architectures have a limit on how * far ahead a user-space access may be in order to grow the stack. */ - if (mmap_read_lock_killable(mm)) + if (mmap_write_lock_killable(mm)) return -EINTR; - vma = find_extend_vma(mm, bprm->p); - mmap_read_unlock(mm); + vma = find_extend_vma_locked(mm, bprm->p, true); + mmap_write_unlock(mm); if (!vma) return -EFAULT; diff --git a/fs/exec.c b/fs/exec.c index a0b1f0337a62..7da70211b25e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -203,7 +203,8 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, #ifdef CONFIG_STACK_GROWSUP if (write) { - ret = expand_downwards(bprm->vma, pos); + /* We claim to hold the lock - nobody to race with */ + ret = expand_downwards(bprm->vma, pos, true); if (ret < 0) return NULL; } @@ -854,7 +855,7 @@ int setup_arg_pages(struct linux_binprm *bprm, stack_base = vma->vm_start - stack_expand; #endif current->mm->start_stack = bprm->p; - ret = expand_stack(vma, stack_base); + ret = expand_stack_locked(vma, stack_base, true); if (ret) ret = -EFAULT; diff --git a/include/linux/mm.h b/include/linux/mm.h index 8e1e89d10a4b..e1915eb18916 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2810,11 +2810,13 @@ extern vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf); extern unsigned long stack_guard_gap; /* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */ -extern int expand_stack(struct vm_area_struct *vma, unsigned long address); +int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, + bool write_locked); +#define expand_stack(vma,addr) expand_stack_locked(vma,addr,false) /* CONFIG_STACK_GROWSUP still needs to grow downwards at some places */ -extern int expand_downwards(struct vm_area_struct *vma, - unsigned long address); +int expand_downwards(struct vm_area_struct *vma, unsigned long address, + bool write_locked); #if VM_GROWSUP extern int expand_upwards(struct vm_area_struct *vma, unsigned long address); #else @@ -2915,6 +2917,8 @@ unsigned long change_prot_numa(struct vm_area_struct *vma, #endif struct vm_area_struct *find_extend_vma(struct mm_struct *, unsigned long addr); +struct vm_area_struct *find_extend_vma_locked(struct mm_struct *, + unsigned long addr, bool write_locked); int remap_pfn_range(struct vm_area_struct *, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t); int remap_pfn_range_notrack(struct vm_area_struct *vma, unsigned long addr, diff --git a/mm/memory.c b/mm/memory.c index ade4d9eb52d8..2d7bd0ca2924 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5364,7 +5364,7 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, goto fail; } - if (expand_stack(vma, addr)) + if (expand_stack_locked(vma, addr, true)) goto fail; success: diff --git a/mm/mmap.c b/mm/mmap.c index fc7d09d81de1..42bf63eaeeae 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1945,7 +1945,8 @@ static int acct_stack_growth(struct vm_area_struct *vma, * PA-RISC uses this for its stack; IA64 for its Register Backing Store. * vma is the last one with address > vma->vm_end. Have to extend vma. */ -int expand_upwards(struct vm_area_struct *vma, unsigned long address) +int expand_upwards(struct vm_area_struct *vma, unsigned long address, + bool write_locked) { struct mm_struct *mm = vma->vm_mm; struct vm_area_struct *next; @@ -1969,6 +1970,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) if (gap_addr < address || gap_addr > TASK_SIZE) gap_addr = TASK_SIZE; + if (!write_locked) + return -EAGAIN; next = find_vma_intersection(mm, vma->vm_end, gap_addr); if (next && vma_is_accessible(next)) { if (!(next->vm_flags & VM_GROWSUP)) @@ -2037,7 +2040,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) /* * vma is the first one with address < vma->vm_start. Have to extend vma. */ -int expand_downwards(struct vm_area_struct *vma, unsigned long address) +int expand_downwards(struct vm_area_struct *vma, unsigned long address, + bool write_locked) { struct mm_struct *mm = vma->vm_mm; MA_STATE(mas, &mm->mm_mt, vma->vm_start, vma->vm_start); @@ -2051,10 +2055,13 @@ int expand_downwards(struct vm_area_struct *vma, unsigned long address) /* Enforce stack_guard_gap */ prev = mas_prev(&mas, 0); /* Check that both stack segments have the same anon_vma? */ - if (prev && !(prev->vm_flags & VM_GROWSDOWN) && - vma_is_accessible(prev)) { - if (address - prev->vm_end < stack_guard_gap) + if (prev) { + if (!(prev->vm_flags & VM_GROWSDOWN) && + vma_is_accessible(prev) && + (address - prev->vm_end < stack_guard_gap)) return -ENOMEM; + if (!write_locked && (prev->vm_end == address)) + return -EAGAIN; } if (mas_preallocate(&mas, vma, GFP_KERNEL)) @@ -2132,13 +2139,14 @@ static int __init cmdline_parse_stack_guard_gap(char *p) __setup("stack_guard_gap=", cmdline_parse_stack_guard_gap); #ifdef CONFIG_STACK_GROWSUP -int expand_stack(struct vm_area_struct *vma, unsigned long address) +int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, + bool write_locked) { - return expand_upwards(vma, address); + return expand_upwards(vma, address, write_locked); } -struct vm_area_struct * -find_extend_vma(struct mm_struct *mm, unsigned long addr) +struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, + unsigned long addr, bool write_locked) { struct vm_area_struct *vma, *prev; @@ -2146,20 +2154,25 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) vma = find_vma_prev(mm, addr, &prev); if (vma && (vma->vm_start <= addr)) return vma; - if (!prev || expand_stack(prev, addr)) + if (!prev) + return NULL; + if (expand_stack_locked(prev, addr, write_locked)) return NULL; if (prev->vm_flags & VM_LOCKED) populate_vma_page_range(prev, addr, prev->vm_end, NULL); return prev; } #else -int expand_stack(struct vm_area_struct *vma, unsigned long address) +int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, + bool write_locked) { - return expand_downwards(vma, address); + if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) + return -EINVAL; + return expand_downwards(vma, address, write_locked); } -struct vm_area_struct * -find_extend_vma(struct mm_struct *mm, unsigned long addr) +struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, + unsigned long addr, bool write_locked) { struct vm_area_struct *vma; unsigned long start; @@ -2170,10 +2183,8 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) return NULL; if (vma->vm_start <= addr) return vma; - if (!(vma->vm_flags & VM_GROWSDOWN)) - return NULL; start = vma->vm_start; - if (expand_stack(vma, addr)) + if (expand_stack_locked(vma, addr, write_locked)) return NULL; if (vma->vm_flags & VM_LOCKED) populate_vma_page_range(vma, addr, start, NULL); @@ -2181,6 +2192,11 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) } #endif +struct vm_area_struct *find_extend_vma(struct mm_struct *mm, + unsigned long addr) +{ + return find_extend_vma_locked(mm, addr, false); +} EXPORT_SYMBOL_GPL(find_extend_vma); /* diff --git a/mm/nommu.c b/mm/nommu.c index 5b83938ecb67..2772d6b332ff 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -694,7 +694,8 @@ struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr) * expand a stack to a given address * - not supported under NOMMU conditions */ -int expand_stack(struct vm_area_struct *vma, unsigned long address) +int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, + bool write_locked) { return -ENOMEM; } -- cgit 1.4.1 From c4b31d1b694e101cae7469a20762647185e11721 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Jun 2023 11:34:15 -0700 Subject: execve: expand new process stack manually ahead of time commit f313c51d26aa87e69633c9b46efb37a930faca71 upstream. This is a small step towards a model where GUP itself would not expand the stack, and any user that needs GUP to not look up existing mappings, but actually expand on them, would have to do so manually before-hand, and with the mm lock held for writing. It turns out that execve() already did almost exactly that, except it didn't take the mm lock at all (it's single-threaded so no locking technically needed, but it could cause lockdep errors). And it only did it for the CONFIG_STACK_GROWSUP case, since in that case GUP has obviously never expanded the stack downwards. So just make that CONFIG_STACK_GROWSUP case do the right thing with locking, and enable it generally. This will eventually help GUP, and in the meantime avoids a special case and the lockdep issue. Signed-off-by: Linus Torvalds [6.1 Minor context from still having FOLL_FORCE flags set] Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- fs/exec.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 7da70211b25e..0d20f7d08696 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -198,34 +198,39 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, int write) { struct page *page; + struct vm_area_struct *vma = bprm->vma; + struct mm_struct *mm = bprm->mm; int ret; - unsigned int gup_flags = FOLL_FORCE; -#ifdef CONFIG_STACK_GROWSUP - if (write) { - /* We claim to hold the lock - nobody to race with */ - ret = expand_downwards(bprm->vma, pos, true); - if (ret < 0) + /* + * Avoid relying on expanding the stack down in GUP (which + * does not work for STACK_GROWSUP anyway), and just do it + * by hand ahead of time. + */ + if (write && pos < vma->vm_start) { + mmap_write_lock(mm); + ret = expand_downwards(vma, pos, true); + if (unlikely(ret < 0)) { + mmap_write_unlock(mm); return NULL; - } -#endif - - if (write) - gup_flags |= FOLL_WRITE; + } + mmap_write_downgrade(mm); + } else + mmap_read_lock(mm); /* * We are doing an exec(). 'current' is the process - * doing the exec and bprm->mm is the new process's mm. + * doing the exec and 'mm' is the new process's mm. */ - mmap_read_lock(bprm->mm); - ret = get_user_pages_remote(bprm->mm, pos, 1, gup_flags, + ret = get_user_pages_remote(mm, pos, 1, + write ? FOLL_WRITE : 0, &page, NULL, NULL); - mmap_read_unlock(bprm->mm); + mmap_read_unlock(mm); if (ret <= 0) return NULL; if (write) - acct_arg_size(bprm, vma_pages(bprm->vma)); + acct_arg_size(bprm, vma_pages(vma)); return page; } -- cgit 1.4.1 From e6bbad75712a97b9b16433563c1358652a33003e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 24 Jun 2023 13:45:51 -0700 Subject: mm: always expand the stack with the mmap write lock held commit 8d7071af890768438c14db6172cc8f9f4d04e184 upstream This finishes the job of always holding the mmap write lock when extending the user stack vma, and removes the 'write_locked' argument from the vm helper functions again. For some cases, we just avoid expanding the stack at all: drivers and page pinning really shouldn't be extending any stacks. Let's see if any strange users really wanted that. It's worth noting that architectures that weren't converted to the new lock_mm_and_find_vma() helper function are left using the legacy "expand_stack()" function, but it has been changed to drop the mmap_lock and take it for writing while expanding the vma. This makes it fairly straightforward to convert the remaining architectures. As a result of dropping and re-taking the lock, the calling conventions for this function have also changed, since the old vma may no longer be valid. So it will now return the new vma if successful, and NULL - and the lock dropped - if the area could not be extended. Signed-off-by: Linus Torvalds [6.1: Patch drivers/iommu/io-pgfault.c instead] Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- arch/ia64/mm/fault.c | 36 +++---------- arch/m68k/mm/fault.c | 9 ++-- arch/microblaze/mm/fault.c | 5 +- arch/openrisc/mm/fault.c | 5 +- arch/parisc/mm/fault.c | 23 ++++---- arch/s390/mm/fault.c | 5 +- arch/sparc/mm/fault_64.c | 8 +-- arch/um/kernel/trap.c | 11 ++-- drivers/iommu/amd/iommu_v2.c | 4 +- drivers/iommu/io-pgfault.c | 2 +- fs/binfmt_elf.c | 2 +- fs/exec.c | 4 +- include/linux/mm.h | 16 ++---- mm/gup.c | 6 +-- mm/memory.c | 10 +++- mm/mmap.c | 121 ++++++++++++++++++++++++++++++++++--------- mm/nommu.c | 18 +++---- 17 files changed, 169 insertions(+), 116 deletions(-) diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c index ef78c2d66cdd..99a09abe1d2c 100644 --- a/arch/ia64/mm/fault.c +++ b/arch/ia64/mm/fault.c @@ -110,10 +110,12 @@ retry: * register backing store that needs to expand upwards, in * this case vma will be null, but prev_vma will ne non-null */ - if (( !vma && prev_vma ) || (address < vma->vm_start) ) - goto check_expansion; + if (( !vma && prev_vma ) || (address < vma->vm_start) ) { + vma = expand_stack(mm, address); + if (!vma) + goto bad_area_nosemaphore; + } - good_area: code = SEGV_ACCERR; /* OK, we've got a good vm_area for this memory area. Check the access permissions: */ @@ -174,35 +176,9 @@ retry: mmap_read_unlock(mm); return; - check_expansion: - if (!(prev_vma && (prev_vma->vm_flags & VM_GROWSUP) && (address == prev_vma->vm_end))) { - if (!vma) - goto bad_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (REGION_NUMBER(address) != REGION_NUMBER(vma->vm_start) - || REGION_OFFSET(address) >= RGN_MAP_LIMIT) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; - } else { - vma = prev_vma; - if (REGION_NUMBER(address) != REGION_NUMBER(vma->vm_start) - || REGION_OFFSET(address) >= RGN_MAP_LIMIT) - goto bad_area; - /* - * Since the register backing store is accessed sequentially, - * we disallow growing it by more than a page at a time. - */ - if (address > vma->vm_end + PAGE_SIZE - sizeof(long)) - goto bad_area; - if (expand_upwards(vma, address)) - goto bad_area; - } - goto good_area; - bad_area: mmap_read_unlock(mm); + bad_area_nosemaphore: if ((isr & IA64_ISR_SP) || ((isr & IA64_ISR_NA) && (isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH)) { diff --git a/arch/m68k/mm/fault.c b/arch/m68k/mm/fault.c index 4d2837eb3e2a..6f62af8e293a 100644 --- a/arch/m68k/mm/fault.c +++ b/arch/m68k/mm/fault.c @@ -105,8 +105,9 @@ retry: if (address + 256 < rdusp()) goto map_err; } - if (expand_stack(vma, address)) - goto map_err; + vma = expand_stack(mm, address); + if (!vma) + goto map_err_nosemaphore; /* * Ok, we have a good vm_area for this memory access, so @@ -193,10 +194,12 @@ bus_err: goto send_sig; map_err: + mmap_read_unlock(mm); +map_err_nosemaphore: current->thread.signo = SIGSEGV; current->thread.code = SEGV_MAPERR; current->thread.faddr = address; - goto send_sig; + return send_fault_sig(regs); acc_err: current->thread.signo = SIGSEGV; diff --git a/arch/microblaze/mm/fault.c b/arch/microblaze/mm/fault.c index 5c40c3ebe52f..a409bb3f09f7 100644 --- a/arch/microblaze/mm/fault.c +++ b/arch/microblaze/mm/fault.c @@ -192,8 +192,9 @@ retry: && (kernel_mode(regs) || !store_updates_sp(regs))) goto bad_area; } - if (expand_stack(vma, address)) - goto bad_area; + vma = expand_stack(mm, address); + if (!vma) + goto bad_area_nosemaphore; good_area: code = SEGV_ACCERR; diff --git a/arch/openrisc/mm/fault.c b/arch/openrisc/mm/fault.c index b4762d66e9ef..e3ad46d02fbd 100644 --- a/arch/openrisc/mm/fault.c +++ b/arch/openrisc/mm/fault.c @@ -127,8 +127,9 @@ retry: if (address + PAGE_SIZE < regs->sp) goto bad_area; } - if (expand_stack(vma, address)) - goto bad_area; + vma = expand_stack(mm, address); + if (!vma) + goto bad_area_nosemaphore; /* * Ok, we have a good vm_area for this memory access, so diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index 869204e97ec9..1843b493910c 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -288,15 +288,19 @@ void do_page_fault(struct pt_regs *regs, unsigned long code, retry: mmap_read_lock(mm); vma = find_vma_prev(mm, address, &prev_vma); - if (!vma || address < vma->vm_start) - goto check_expansion; + if (!vma || address < vma->vm_start) { + if (!prev || !(prev->vm_flags & VM_GROWSUP)) + goto bad_area; + vma = expand_stack(mm, address); + if (!vma) + goto bad_area_nosemaphore; + } + /* * Ok, we have a good vm_area for this memory access. We still need to * check the access permissions. */ -good_area: - if ((vma->vm_flags & acc_type) != acc_type) goto bad_area; @@ -342,17 +346,13 @@ good_area: mmap_read_unlock(mm); return; -check_expansion: - vma = prev_vma; - if (vma && (expand_stack(vma, address) == 0)) - goto good_area; - /* * Something tried to access memory that isn't in our memory map.. */ bad_area: mmap_read_unlock(mm); +bad_area_nosemaphore: if (user_mode(regs)) { int signo, si_code; @@ -444,7 +444,7 @@ handle_nadtlb_fault(struct pt_regs *regs) { unsigned long insn = regs->iir; int breg, treg, xreg, val = 0; - struct vm_area_struct *vma, *prev_vma; + struct vm_area_struct *vma; struct task_struct *tsk; struct mm_struct *mm; unsigned long address; @@ -480,7 +480,7 @@ handle_nadtlb_fault(struct pt_regs *regs) /* Search for VMA */ address = regs->ior; mmap_read_lock(mm); - vma = find_vma_prev(mm, address, &prev_vma); + vma = vma_lookup(mm, address); mmap_read_unlock(mm); /* @@ -489,7 +489,6 @@ handle_nadtlb_fault(struct pt_regs *regs) */ acc_type = (insn & 0x40) ? VM_WRITE : VM_READ; if (vma - && address >= vma->vm_start && (vma->vm_flags & acc_type) == acc_type) val = 1; } diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 8e84ed2bb944..2ab388179833 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -429,8 +429,9 @@ retry: if (unlikely(vma->vm_start > address)) { if (!(vma->vm_flags & VM_GROWSDOWN)) goto out_up; - if (expand_stack(vma, address)) - goto out_up; + vma = expand_stack(mm, address); + if (!vma) + goto out; } /* diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c index 4acc12eafbf5..df685a241855 100644 --- a/arch/sparc/mm/fault_64.c +++ b/arch/sparc/mm/fault_64.c @@ -383,8 +383,9 @@ continue_fault: goto bad_area; } } - if (expand_stack(vma, address)) - goto bad_area; + vma = expand_stack(mm, address); + if (!vma) + goto bad_area_nosemaphore; /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. @@ -482,8 +483,9 @@ exit_exception: * Fix it, but check if it's kernel or user first.. */ bad_area: - insn = get_fault_insn(regs, insn); mmap_read_unlock(mm); +bad_area_nosemaphore: + insn = get_fault_insn(regs, insn); handle_kernel_fault: do_kernel_fault(regs, si_code, fault_code, insn, address); diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index d3ce21c4ca32..6d8ae86ae978 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -47,14 +47,15 @@ retry: vma = find_vma(mm, address); if (!vma) goto out; - else if (vma->vm_start <= address) + if (vma->vm_start <= address) goto good_area; - else if (!(vma->vm_flags & VM_GROWSDOWN)) + if (!(vma->vm_flags & VM_GROWSDOWN)) goto out; - else if (is_user && !ARCH_IS_STACKGROW(address)) - goto out; - else if (expand_stack(vma, address)) + if (is_user && !ARCH_IS_STACKGROW(address)) goto out; + vma = expand_stack(mm, address); + if (!vma) + goto out_nosemaphore; good_area: *code_out = SEGV_ACCERR; diff --git a/drivers/iommu/amd/iommu_v2.c b/drivers/iommu/amd/iommu_v2.c index 9f7fab49a5a9..75355ddca657 100644 --- a/drivers/iommu/amd/iommu_v2.c +++ b/drivers/iommu/amd/iommu_v2.c @@ -485,8 +485,8 @@ static void do_fault(struct work_struct *work) flags |= FAULT_FLAG_REMOTE; mmap_read_lock(mm); - vma = find_extend_vma(mm, address); - if (!vma || address < vma->vm_start) + vma = vma_lookup(mm, address); + if (!vma) /* failed to get a vma in the right range */ goto out; diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c index 1df8c1dcae77..16e564cb393b 100644 --- a/drivers/iommu/io-pgfault.c +++ b/drivers/iommu/io-pgfault.c @@ -89,7 +89,7 @@ iopf_handle_single(struct iopf_fault *iopf) mmap_read_lock(mm); - vma = find_extend_vma(mm, prm->addr); + vma = vma_lookup(mm, prm->addr); if (!vma) /* Unmapped area */ goto out_put_mm; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 5688c3e6adc1..e6c9c0e08448 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -317,7 +317,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, */ if (mmap_write_lock_killable(mm)) return -EINTR; - vma = find_extend_vma_locked(mm, bprm->p, true); + vma = find_extend_vma_locked(mm, bprm->p); mmap_write_unlock(mm); if (!vma) return -EFAULT; diff --git a/fs/exec.c b/fs/exec.c index 0d20f7d08696..283012eb1aeb 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -209,7 +209,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, */ if (write && pos < vma->vm_start) { mmap_write_lock(mm); - ret = expand_downwards(vma, pos, true); + ret = expand_downwards(vma, pos); if (unlikely(ret < 0)) { mmap_write_unlock(mm); return NULL; @@ -860,7 +860,7 @@ int setup_arg_pages(struct linux_binprm *bprm, stack_base = vma->vm_start - stack_expand; #endif current->mm->start_stack = bprm->p; - ret = expand_stack_locked(vma, stack_base, true); + ret = expand_stack_locked(vma, stack_base); if (ret) ret = -EFAULT; diff --git a/include/linux/mm.h b/include/linux/mm.h index e1915eb18916..f1f0fc179a90 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2810,18 +2810,11 @@ extern vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf); extern unsigned long stack_guard_gap; /* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */ -int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, - bool write_locked); -#define expand_stack(vma,addr) expand_stack_locked(vma,addr,false) +int expand_stack_locked(struct vm_area_struct *vma, unsigned long address); +struct vm_area_struct *expand_stack(struct mm_struct * mm, unsigned long addr); /* CONFIG_STACK_GROWSUP still needs to grow downwards at some places */ -int expand_downwards(struct vm_area_struct *vma, unsigned long address, - bool write_locked); -#if VM_GROWSUP -extern int expand_upwards(struct vm_area_struct *vma, unsigned long address); -#else - #define expand_upwards(vma, address) (0) -#endif +int expand_downwards(struct vm_area_struct *vma, unsigned long address); /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr); @@ -2916,9 +2909,8 @@ unsigned long change_prot_numa(struct vm_area_struct *vma, unsigned long start, unsigned long end); #endif -struct vm_area_struct *find_extend_vma(struct mm_struct *, unsigned long addr); struct vm_area_struct *find_extend_vma_locked(struct mm_struct *, - unsigned long addr, bool write_locked); + unsigned long addr); int remap_pfn_range(struct vm_area_struct *, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t); int remap_pfn_range_notrack(struct vm_area_struct *vma, unsigned long addr, diff --git a/mm/gup.c b/mm/gup.c index 028f3b4e8c3f..f4911ddd3070 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -1182,7 +1182,7 @@ static long __get_user_pages(struct mm_struct *mm, /* first iteration or cross vma bound */ if (!vma || start >= vma->vm_end) { - vma = find_extend_vma(mm, start); + vma = vma_lookup(mm, start); if (!vma && in_gate_area(mm, start)) { ret = get_gate_page(mm, start & PAGE_MASK, gup_flags, &vma, @@ -1351,8 +1351,8 @@ int fixup_user_fault(struct mm_struct *mm, fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; retry: - vma = find_extend_vma(mm, address); - if (!vma || address < vma->vm_start) + vma = vma_lookup(mm, address); + if (!vma) return -EFAULT; if (!vma_permits_fault(vma, fault_flags)) diff --git a/mm/memory.c b/mm/memory.c index 2d7bd0ca2924..77549434d13a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5364,7 +5364,7 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, goto fail; } - if (expand_stack_locked(vma, addr, true)) + if (expand_stack_locked(vma, addr)) goto fail; success: @@ -5648,6 +5648,14 @@ int __access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, if (mmap_read_lock_killable(mm)) return 0; + /* We might need to expand the stack to access it */ + vma = vma_lookup(mm, addr); + if (!vma) { + vma = expand_stack(mm, addr); + if (!vma) + return 0; + } + /* ignore errors, just check how much was successfully transferred */ while (len) { int bytes, ret, offset; diff --git a/mm/mmap.c b/mm/mmap.c index 42bf63eaeeae..b8af52db3bbe 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1945,8 +1945,7 @@ static int acct_stack_growth(struct vm_area_struct *vma, * PA-RISC uses this for its stack; IA64 for its Register Backing Store. * vma is the last one with address > vma->vm_end. Have to extend vma. */ -int expand_upwards(struct vm_area_struct *vma, unsigned long address, - bool write_locked) +static int expand_upwards(struct vm_area_struct *vma, unsigned long address) { struct mm_struct *mm = vma->vm_mm; struct vm_area_struct *next; @@ -1970,8 +1969,6 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address, if (gap_addr < address || gap_addr > TASK_SIZE) gap_addr = TASK_SIZE; - if (!write_locked) - return -EAGAIN; next = find_vma_intersection(mm, vma->vm_end, gap_addr); if (next && vma_is_accessible(next)) { if (!(next->vm_flags & VM_GROWSUP)) @@ -2039,15 +2036,18 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address, /* * vma is the first one with address < vma->vm_start. Have to extend vma. + * mmap_lock held for writing. */ -int expand_downwards(struct vm_area_struct *vma, unsigned long address, - bool write_locked) +int expand_downwards(struct vm_area_struct *vma, unsigned long address) { struct mm_struct *mm = vma->vm_mm; MA_STATE(mas, &mm->mm_mt, vma->vm_start, vma->vm_start); struct vm_area_struct *prev; int error = 0; + if (!(vma->vm_flags & VM_GROWSDOWN)) + return -EFAULT; + address &= PAGE_MASK; if (address < mmap_min_addr || address < FIRST_USER_ADDRESS) return -EPERM; @@ -2060,8 +2060,6 @@ int expand_downwards(struct vm_area_struct *vma, unsigned long address, vma_is_accessible(prev) && (address - prev->vm_end < stack_guard_gap)) return -ENOMEM; - if (!write_locked && (prev->vm_end == address)) - return -EAGAIN; } if (mas_preallocate(&mas, vma, GFP_KERNEL)) @@ -2139,14 +2137,12 @@ static int __init cmdline_parse_stack_guard_gap(char *p) __setup("stack_guard_gap=", cmdline_parse_stack_guard_gap); #ifdef CONFIG_STACK_GROWSUP -int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, - bool write_locked) +int expand_stack_locked(struct vm_area_struct *vma, unsigned long address) { - return expand_upwards(vma, address, write_locked); + return expand_upwards(vma, address); } -struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, - unsigned long addr, bool write_locked) +struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, unsigned long addr) { struct vm_area_struct *vma, *prev; @@ -2156,23 +2152,21 @@ struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, return vma; if (!prev) return NULL; - if (expand_stack_locked(prev, addr, write_locked)) + if (expand_stack_locked(prev, addr)) return NULL; if (prev->vm_flags & VM_LOCKED) populate_vma_page_range(prev, addr, prev->vm_end, NULL); return prev; } #else -int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, - bool write_locked) +int expand_stack_locked(struct vm_area_struct *vma, unsigned long address) { if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) return -EINVAL; - return expand_downwards(vma, address, write_locked); + return expand_downwards(vma, address); } -struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, - unsigned long addr, bool write_locked) +struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, unsigned long addr) { struct vm_area_struct *vma; unsigned long start; @@ -2184,7 +2178,7 @@ struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, if (vma->vm_start <= addr) return vma; start = vma->vm_start; - if (expand_stack_locked(vma, addr, write_locked)) + if (expand_stack_locked(vma, addr)) return NULL; if (vma->vm_flags & VM_LOCKED) populate_vma_page_range(vma, addr, start, NULL); @@ -2192,12 +2186,91 @@ struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm, } #endif -struct vm_area_struct *find_extend_vma(struct mm_struct *mm, - unsigned long addr) +/* + * IA64 has some horrid mapping rules: it can expand both up and down, + * but with various special rules. + * + * We'll get rid of this architecture eventually, so the ugliness is + * temporary. + */ +#ifdef CONFIG_IA64 +static inline bool vma_expand_ok(struct vm_area_struct *vma, unsigned long addr) +{ + return REGION_NUMBER(addr) == REGION_NUMBER(vma->vm_start) && + REGION_OFFSET(addr) < RGN_MAP_LIMIT; +} + +/* + * IA64 stacks grow down, but there's a special register backing store + * that can grow up. Only sequentially, though, so the new address must + * match vm_end. + */ +static inline int vma_expand_up(struct vm_area_struct *vma, unsigned long addr) +{ + if (!vma_expand_ok(vma, addr)) + return -EFAULT; + if (vma->vm_end != (addr & PAGE_MASK)) + return -EFAULT; + return expand_upwards(vma, addr); +} + +static inline bool vma_expand_down(struct vm_area_struct *vma, unsigned long addr) +{ + if (!vma_expand_ok(vma, addr)) + return -EFAULT; + return expand_downwards(vma, addr); +} + +#elif defined(CONFIG_STACK_GROWSUP) + +#define vma_expand_up(vma,addr) expand_upwards(vma, addr) +#define vma_expand_down(vma, addr) (-EFAULT) + +#else + +#define vma_expand_up(vma,addr) (-EFAULT) +#define vma_expand_down(vma, addr) expand_downwards(vma, addr) + +#endif + +/* + * expand_stack(): legacy interface for page faulting. Don't use unless + * you have to. + * + * This is called with the mm locked for reading, drops the lock, takes + * the lock for writing, tries to look up a vma again, expands it if + * necessary, and downgrades the lock to reading again. + * + * If no vma is found or it can't be expanded, it returns NULL and has + * dropped the lock. + */ +struct vm_area_struct *expand_stack(struct mm_struct *mm, unsigned long addr) { - return find_extend_vma_locked(mm, addr, false); + struct vm_area_struct *vma, *prev; + + mmap_read_unlock(mm); + if (mmap_write_lock_killable(mm)) + return NULL; + + vma = find_vma_prev(mm, addr, &prev); + if (vma && vma->vm_start <= addr) + goto success; + + if (prev && !vma_expand_up(prev, addr)) { + vma = prev; + goto success; + } + + if (vma && !vma_expand_down(vma, addr)) + goto success; + + mmap_write_unlock(mm); + return NULL; + +success: + mmap_write_downgrade(mm); + return vma; } -EXPORT_SYMBOL_GPL(find_extend_vma); /* * Ok - we have the memory areas we should free on a maple tree so release them, diff --git a/mm/nommu.c b/mm/nommu.c index 2772d6b332ff..b44ed69c109a 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -681,25 +681,21 @@ struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr) } EXPORT_SYMBOL(find_vma); -/* - * find a VMA - * - we don't extend stack VMAs under NOMMU conditions - */ -struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr) -{ - return find_vma(mm, addr); -} - /* * expand a stack to a given address * - not supported under NOMMU conditions */ -int expand_stack_locked(struct vm_area_struct *vma, unsigned long address, - bool write_locked) +int expand_stack_locked(struct vm_area_struct *vma, unsigned long addr) { return -ENOMEM; } +struct vm_area_struct *expand_stack(struct mm_struct *mm, unsigned long addr) +{ + mmap_read_unlock(mm); + return NULL; +} + /* * look up the first VMA exactly that exactly matches addr * - should be called with mm->mmap_lock at least held readlocked -- cgit 1.4.1 From cae85424957884cb5665241b3f12e52f0346d1d6 Mon Sep 17 00:00:00 2001 From: Zhang Shurong Date: Sun, 25 Jun 2023 00:16:49 +0800 Subject: fbdev: fix potential OOB read in fast_imageblit() commit c2d22806aecb24e2de55c30a06e5d6eb297d161d upstream. There is a potential OOB read at fast_imageblit, for "colortab[(*src >> 4)]" can become a negative value due to "const char *s = image->data, *src". This change makes sure the index for colortab always positive or zero. Similar commit: https://patchwork.kernel.org/patch/11746067 Potential bug report: https://groups.google.com/g/syzkaller-bugs/c/9ubBXKeKXf4/m/k-QXy4UgAAAJ Signed-off-by: Zhang Shurong Cc: stable@vger.kernel.org Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/sysimgblt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/sysimgblt.c b/drivers/video/fbdev/core/sysimgblt.c index 335e92b813fc..665ef7a0a249 100644 --- a/drivers/video/fbdev/core/sysimgblt.c +++ b/drivers/video/fbdev/core/sysimgblt.c @@ -189,7 +189,7 @@ static void fast_imageblit(const struct fb_image *image, struct fb_info *p, u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel; u32 ppw = 32/bpp, spitch = (image->width + 7)/8; u32 bit_mask, eorx, shift; - const char *s = image->data, *src; + const u8 *s = image->data, *src; u32 *dst; const u32 *tab; size_t tablen; -- cgit 1.4.1 From 879e79c3aead41b8aa2e91164354b30bd1c4ef3b Mon Sep 17 00:00:00 2001 From: Ludvig Michaelsson Date: Wed, 21 Jun 2023 13:17:43 +0200 Subject: HID: hidraw: fix data race on device refcount commit 944ee77dc6ec7b0afd8ec70ffc418b238c92f12b upstream. The hidraw_open() function increments the hidraw device reference counter. The counter has no dedicated synchronization mechanism, resulting in a potential data race when concurrently opening a device. The race is a regression introduced by commit 8590222e4b02 ("HID: hidraw: Replace hidraw device table mutex with a rwsem"). While minors_rwsem is intended to protect the hidraw_table itself, by instead acquiring the lock for writing, the reference counter is also protected. This is symmetrical to hidraw_release(). Link: https://github.com/systemd/systemd/issues/27947 Fixes: 8590222e4b02 ("HID: hidraw: Replace hidraw device table mutex with a rwsem") Cc: stable@vger.kernel.org Signed-off-by: Ludvig Michaelsson Link: https://lore.kernel.org/r/20230621-hidraw-race-v1-1-a58e6ac69bab@yubico.com Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hidraw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 197b1e7bf029..b617aada50b0 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -272,7 +272,12 @@ static int hidraw_open(struct inode *inode, struct file *file) goto out; } - down_read(&minors_rwsem); + /* + * Technically not writing to the hidraw_table but a write lock is + * required to protect the device refcount. This is symmetrical to + * hidraw_release(). + */ + down_write(&minors_rwsem); if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { err = -ENODEV; goto out_unlock; @@ -301,7 +306,7 @@ static int hidraw_open(struct inode *inode, struct file *file) spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); file->private_data = list; out_unlock: - up_read(&minors_rwsem); + up_write(&minors_rwsem); out: if (err < 0) kfree(list); -- cgit 1.4.1 From d89750b19681581796dfbe3689bbb5d439b99b24 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Thu, 8 Jun 2023 14:38:28 -0700 Subject: HID: wacom: Use ktime_t rather than int when dealing with timestamps commit 9a6c0e28e215535b2938c61ded54603b4e5814c5 upstream. Code which interacts with timestamps needs to use the ktime_t type returned by functions like ktime_get. The int type does not offer enough space to store these values, and attempting to use it is a recipe for problems. In this particular case, overflows would occur when calculating/storing timestamps leading to incorrect values being reported to userspace. In some cases these bad timestamps cause input handling in userspace to appear hung. Link: https://gitlab.freedesktop.org/libinput/libinput/-/issues/901 Fixes: 17d793f3ed53 ("HID: wacom: insert timestamp to packed Bluetooth (BT) events") CC: stable@vger.kernel.org Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20230608213828.2108-1-jason.gerecke@wacom.com Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_wac.c | 6 +++--- drivers/hid/wacom_wac.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 9c30dd30537a..15cd0cabee2a 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1309,7 +1309,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) struct input_dev *pen_input = wacom->pen_input; unsigned char *data = wacom->data; int number_of_valid_frames = 0; - int time_interval = 15000000; + ktime_t time_interval = 15000000; ktime_t time_packet_received = ktime_get(); int i; @@ -1343,7 +1343,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) if (number_of_valid_frames) { if (wacom->hid_data.time_delayed) time_interval = ktime_get() - wacom->hid_data.time_delayed; - time_interval /= number_of_valid_frames; + time_interval = div_u64(time_interval, number_of_valid_frames); wacom->hid_data.time_delayed = time_packet_received; } @@ -1354,7 +1354,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) bool range = frame[0] & 0x20; bool invert = frame[0] & 0x10; int frames_number_reversed = number_of_valid_frames - i - 1; - int event_timestamp = time_packet_received - frames_number_reversed * time_interval; + ktime_t event_timestamp = time_packet_received - frames_number_reversed * time_interval; if (!valid) continue; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 1a40bb8c5810..ee21bb260f22 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -324,7 +324,7 @@ struct hid_data { int ps_connected; bool pad_input_event_flag; unsigned short sequence_number; - int time_delayed; + ktime_t time_delayed; }; struct wacom_remote_data { -- cgit 1.4.1 From a536383ef030b15ace93b2ca865c4132a1fd8794 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sun, 18 Jun 2023 08:09:57 +0900 Subject: HID: logitech-hidpp: add HIDPP_QUIRK_DELAYED_INIT for the T651. commit 5fe251112646d8626818ea90f7af325bab243efa upstream. commit 498ba2069035 ("HID: logitech-hidpp: Don't restart communication if not necessary") put restarting communication behind that flag, and this was apparently necessary on the T651, but the flag was not set for it. Fixes: 498ba2069035 ("HID: logitech-hidpp: Don't restart communication if not necessary") Cc: stable@vger.kernel.org Signed-off-by: Mike Hommey Link: https://lore.kernel.org/r/20230617230957.6mx73th4blv7owqk@glandium.org Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-logitech-hidpp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index b2cd7527de19..0b4204b9a253 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -4348,7 +4348,7 @@ static const struct hid_device_id hidpp_devices[] = { { /* wireless touchpad T651 */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651), - .driver_data = HIDPP_QUIRK_CLASS_WTP }, + .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT }, { /* Mouse Logitech Anywhere MX */ LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, { /* Mouse logitech M560 */ -- cgit 1.4.1 From 00f04a3385f72f0e36f3a217ab5236b94daddb46 Mon Sep 17 00:00:00 2001 From: Ricardo Cañuelo Date: Thu, 25 May 2023 14:18:11 +0200 Subject: Revert "thermal/drivers/mediatek: Use devm_of_iomap to avoid resource leak in mtk_thermal_probe" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 86edac7d3888c715fe3a81bd61f3617ecfe2e1dd upstream. This reverts commit f05c7b7d9ea9477fcc388476c6f4ade8c66d2d26. That change was causing a regression in the generic-adc-thermal-probed bootrr test as reported in the kernelci-results list [1]. A proper rework will take longer, so revert it for now. [1] https://groups.io/g/kernelci-results/message/42660 Fixes: f05c7b7d9ea9 ("thermal/drivers/mediatek: Use devm_of_iomap to avoid resource leak in mtk_thermal_probe") Signed-off-by: Ricardo Cañuelo Suggested-by: AngeloGioacchino Del Regno Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20230525121811.3360268-1-ricardo.canuelo@collabora.com Signed-off-by: Greg Kroah-Hartman --- drivers/thermal/mtk_thermal.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 62f1e691659e..8440692e3890 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -1028,12 +1028,7 @@ static int mtk_thermal_probe(struct platform_device *pdev) return -ENODEV; } - auxadc_base = devm_of_iomap(&pdev->dev, auxadc, 0, NULL); - if (IS_ERR(auxadc_base)) { - of_node_put(auxadc); - return PTR_ERR(auxadc_base); - } - + auxadc_base = of_iomap(auxadc, 0); auxadc_phys_base = of_get_phys_base(auxadc); of_node_put(auxadc); @@ -1049,12 +1044,7 @@ static int mtk_thermal_probe(struct platform_device *pdev) return -ENODEV; } - apmixed_base = devm_of_iomap(&pdev->dev, apmixedsys, 0, NULL); - if (IS_ERR(apmixed_base)) { - of_node_put(apmixedsys); - return PTR_ERR(apmixed_base); - } - + apmixed_base = of_iomap(apmixedsys, 0); apmixed_phys_base = of_get_phys_base(apmixedsys); of_node_put(apmixedsys); -- cgit 1.4.1 From 0a1da2dde461cc8adac196a10d76a1fb977b7cdf Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 29 Jun 2023 20:41:24 -0700 Subject: sparc32: fix lock_mm_and_find_vma() conversion commit 0b26eadbf200abf6c97c6d870286c73219cdac65 upstream. The sparc32 conversion to lock_mm_and_find_vma() in commit a050ba1e7422 ("mm/fault: convert remaining simple cases to lock_mm_and_find_vma()") missed the fact that we didn't actually have a 'regs' pointer available in the 'force_user_fault()' case. It's there in the regular page fault path ("do_sparc_fault()"), but not the window underflow/overflow paths. Which is all fine - we can just pass in a NULL pointer. The register state is only used to avoid deadlock with kernel faults, which is not the case for any of these register window faults. Reported-by: Stephen Rothwell Fixes: a050ba1e7422 ("mm/fault: convert remaining simple cases to lock_mm_and_find_vma()") Signed-off-by: Linus Torvalds Cc: Naresh Kamboju Signed-off-by: Greg Kroah-Hartman --- arch/sparc/mm/fault_32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/mm/fault_32.c b/arch/sparc/mm/fault_32.c index aef2aebe2379..56eb40d385a8 100644 --- a/arch/sparc/mm/fault_32.c +++ b/arch/sparc/mm/fault_32.c @@ -309,7 +309,7 @@ static void force_user_fault(unsigned long address, int write) code = SEGV_MAPERR; - vma = lock_mm_and_find_vma(mm, address, regs); + vma = lock_mm_and_find_vma(mm, address, NULL); if (!vma) goto bad_area_nosemaphore; code = SEGV_ACCERR; -- cgit 1.4.1 From 4a1db15878aacb89e8f24c98a2f96fad6db3f967 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 29 Jun 2023 23:04:57 -0700 Subject: parisc: fix expand_stack() conversion commit ea3f8272876f2958463992f6736ab690fde7fa9c upstream. In commit 8d7071af8907 ("mm: always expand the stack with the mmap write lock held") I tried to deal with the remaining odd page fault handling cases. The oddest one is ia64, which has stacks that grow both up and down. And because ia64 was _so_ odd, I asked people to verify the end result. But a close second oddity is parisc, which is the only one that has a main stack growing up (our "CONFIG_STACK_GROWSUP" config option). But it looked obvious enough that I didn't worry about it. I should have worried a bit more. Not because it was particularly complex, but because I just used the wrong variable name. The previous vma isn't called "prev", it's called "prev_vma". Blush. Fixes: 8d7071af8907 ("mm: always expand the stack with the mmap write lock held") Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- arch/parisc/mm/fault.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index 1843b493910c..b00aa98b582c 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -289,7 +289,7 @@ retry: mmap_read_lock(mm); vma = find_vma_prev(mm, address, &prev_vma); if (!vma || address < vma->vm_start) { - if (!prev || !(prev->vm_flags & VM_GROWSUP)) + if (!prev_vma || !(prev_vma->vm_flags & VM_GROWSUP)) goto bad_area; vma = expand_stack(mm, address); if (!vma) -- cgit 1.4.1 From c2d89256de75c61764183a65534dea231d5ae66d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 29 Jun 2023 23:34:29 -0700 Subject: csky: fix up lock_mm_and_find_vma() conversion commit e55e5df193d247a38a5e1ac65a5316a0adcc22fa upstream. As already mentioned in my merge message for the 'expand-stack' branch, we have something like 24 different versions of the page fault path for all our different architectures, all just _slightly_ different due to various historical reasons (usually related to exactly when they branched off the original i386 version, and the details of the other architectures they had in their history). And a few of them had some silly mistake in the conversion. Most of the architectures call the faulting address 'address' in the fault path. But not all. Some just call it 'addr'. And if you end up doing a bit too much copy-and-paste, you end up with the wrong version in the places that do it differently. In this case it was csky. Fixes: a050ba1e7422 ("mm/fault: convert remaining simple cases to lock_mm_and_find_vma()") Reported-by: Guenter Roeck Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- arch/csky/mm/fault.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/csky/mm/fault.c b/arch/csky/mm/fault.c index ae9781b7d92e..a885518ce1dd 100644 --- a/arch/csky/mm/fault.c +++ b/arch/csky/mm/fault.c @@ -237,7 +237,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs) if (is_write(regs)) flags |= FAULT_FLAG_WRITE; retry: - vma = lock_mm_and_find_vma(mm, address, regs); + vma = lock_mm_and_find_vma(mm, addr, regs); if (unlikely(!vma)) { bad_area_nosemaphore(regs, mm, code, addr); return; -- cgit 1.4.1 From 323846590c55fd9b05dfb9d768d76583a556d254 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 30 Jun 2023 18:24:49 -0700 Subject: xtensa: fix NOMMU build with lock_mm_and_find_vma() conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d85a143b69abb4d7544227e26d12c4c7735ab27d upstream. It turns out that xtensa has a really odd configuration situation: you can do a no-MMU config, but still have the page fault code enabled. Which doesn't sound all that sensible, but it turns out that xtensa can have protection faults even without the MMU, and we have this: config PFAULT bool "Handle protection faults" if EXPERT && !MMU default y help Handle protection faults. MMU configurations must enable it. noMMU configurations may disable it if used memory map never generates protection faults or faults are always fatal. If unsure, say Y. which completely violated my expectations of the page fault handling. End result: Guenter reports that the xtensa no-MMU builds all fail with arch/xtensa/mm/fault.c: In function ‘do_page_fault’: arch/xtensa/mm/fault.c:133:8: error: implicit declaration of function ‘lock_mm_and_find_vma’ because I never exposed the new lock_mm_and_find_vma() function for the no-MMU case. Doing so is simple enough, and fixes the problem. Reported-and-tested-by: Guenter Roeck Fixes: a050ba1e7422 ("mm/fault: convert remaining simple cases to lock_mm_and_find_vma()") Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 5 +++-- mm/nommu.c | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index f1f0fc179a90..f5b6ef943ede 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1921,6 +1921,9 @@ void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to); void truncate_pagecache_range(struct inode *inode, loff_t offset, loff_t end); int generic_error_remove_page(struct address_space *mapping, struct page *page); +struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, + unsigned long address, struct pt_regs *regs); + #ifdef CONFIG_MMU extern vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address, unsigned int flags, @@ -1932,8 +1935,6 @@ void unmap_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t nr, bool even_cows); void unmap_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen, int even_cows); -struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, - unsigned long address, struct pt_regs *regs); #else static inline vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address, unsigned int flags, diff --git a/mm/nommu.c b/mm/nommu.c index b44ed69c109a..da26b3fec9eb 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -681,6 +681,17 @@ struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr) } EXPORT_SYMBOL(find_vma); +/* + * At least xtensa ends up having protection faults even with no + * MMU.. No stack expansion, at least. + */ +struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, + unsigned long addr, struct pt_regs *regs) +{ + mmap_read_lock(mm); + return vma_lookup(mm, addr); +} + /* * expand a stack to a given address * - not supported under NOMMU conditions -- cgit 1.4.1 From 0f4ac6b4c5f00f45b7a429c8a5b028a598c6400c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 1 Jul 2023 13:16:27 +0200 Subject: Linux 6.1.37 Link: https://lore.kernel.org/r/20230629184151.651069086@linuxfoundation.org Tested-by: Salvatore Bonaccorso Tested-by: Takeshi Ogasawara Link: https://lore.kernel.org/r/20230630055632.571288857@linuxfoundation.org Link: https://lore.kernel.org/r/20230630072124.944461414@linuxfoundation.org Tested-by: Takeshi Ogasawara Tested-by: Ron Economos Tested-by: Jon Hunter Tested-by: Salvatore Bonaccorso Tested-by: Markus Reichelt Tested-by: Linux Kernel Functional Testing Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index cffb83d7a0fb..26d73ce7e926 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 36 +SUBLEVEL = 37 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From 6b2849b3e05d5dcda36a000ec81a15caa828e0a8 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sat, 1 Jul 2023 03:31:55 -0700 Subject: xtensa: fix lock_mm_and_find_vma in case VMA not found commit 03f889378f33aa9a9d8e5f49ba94134cf6158090 upstream. MMU version of lock_mm_and_find_vma releases the mm lock before returning when VMA is not found. Do the same in noMMU version. This fixes hang on an attempt to handle protection fault. Fixes: d85a143b69ab ("xtensa: fix NOMMU build with lock_mm_and_find_vma() conversion") Signed-off-by: Max Filippov Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/nommu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mm/nommu.c b/mm/nommu.c index da26b3fec9eb..8e8fe491d914 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -688,8 +688,13 @@ EXPORT_SYMBOL(find_vma); struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, unsigned long addr, struct pt_regs *regs) { + struct vm_area_struct *vma; + mmap_read_lock(mm); - return vma_lookup(mm, addr); + vma = vma_lookup(mm, addr); + if (!vma) + mmap_read_unlock(mm); + return vma; } /* -- cgit 1.4.1 From dd6d6f9d47aebf50713fb857f91402a1c6c3131c Mon Sep 17 00:00:00 2001 From: Alvin Lee Date: Thu, 20 Oct 2022 11:46:49 -0400 Subject: drm/amd/display: Remove optimization for VRR updates commit 3442f4e0e55555d14b099c17382453fdfd2508d5 upstream. Optimization caused unexpected regression, so remove for now. Tested-by: Mark Broadworth Reviewed-by: Aric Cyr Acked-by: Rodrigo Siqueira Signed-off-by: Alvin Lee Signed-off-by: Alex Deucher Cc: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/core/dc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index af7aefe285ff..2e936930007e 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -401,9 +401,6 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, { int i; - if (memcmp(adjust, &stream->adjust, sizeof(struct dc_crtc_timing_adjust)) == 0) - return true; - stream->adjust.v_total_max = adjust->v_total_max; stream->adjust.v_total_mid = adjust->v_total_mid; stream->adjust.v_total_mid_frame_num = adjust->v_total_mid_frame_num; -- cgit 1.4.1 From a905b0b318ad7d37c3041573454129923e0a0723 Mon Sep 17 00:00:00 2001 From: Aric Cyr Date: Wed, 8 Feb 2023 19:51:42 -0500 Subject: drm/amd/display: Do not update DRR while BW optimizations pending commit 32953485c558cecf08f33fbfa251e80e44cef981 upstream. [why] While bandwidth optimizations are pending, it's possible a pstate change will occur. During this time, VSYNC handler should not also try to update DRR parameters causing pstate hang [how] Do not adjust DRR if optimize bandwidth is set. Reviewed-by: Aric Cyr Acked-by: Qingqing Zhuo Signed-off-by: Aric Cyr Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Cc: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/core/dc.c | 48 +++++++++++++++++++------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 2e936930007e..277ccee8b9f8 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -401,6 +401,13 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, { int i; + /* + * Don't adjust DRR while there's bandwidth optimizations pending to + * avoid conflicting with firmware updates. + */ + if (dc->optimized_required || dc->wm_optimized_required) + return false; + stream->adjust.v_total_max = adjust->v_total_max; stream->adjust.v_total_mid = adjust->v_total_mid; stream->adjust.v_total_mid_frame_num = adjust->v_total_mid_frame_num; @@ -2021,27 +2028,33 @@ void dc_post_update_surfaces_to_stream(struct dc *dc) post_surface_trace(dc); - if (dc->ctx->dce_version >= DCE_VERSION_MAX) - TRACE_DCN_CLOCK_STATE(&context->bw_ctx.bw.dcn.clk); - else + /* + * Only relevant for DCN behavior where we can guarantee the optimization + * is safe to apply - retain the legacy behavior for DCE. + */ + + if (dc->ctx->dce_version < DCE_VERSION_MAX) TRACE_DCE_CLOCK_STATE(&context->bw_ctx.bw.dce); + else { + TRACE_DCN_CLOCK_STATE(&context->bw_ctx.bw.dcn.clk); - if (is_flip_pending_in_pipes(dc, context)) - return; + if (is_flip_pending_in_pipes(dc, context)) + return; - for (i = 0; i < dc->res_pool->pipe_count; i++) - if (context->res_ctx.pipe_ctx[i].stream == NULL || - context->res_ctx.pipe_ctx[i].plane_state == NULL) { - context->res_ctx.pipe_ctx[i].pipe_idx = i; - dc->hwss.disable_plane(dc, &context->res_ctx.pipe_ctx[i]); - } + for (i = 0; i < dc->res_pool->pipe_count; i++) + if (context->res_ctx.pipe_ctx[i].stream == NULL || + context->res_ctx.pipe_ctx[i].plane_state == NULL) { + context->res_ctx.pipe_ctx[i].pipe_idx = i; + dc->hwss.disable_plane(dc, &context->res_ctx.pipe_ctx[i]); + } - process_deferred_updates(dc); + process_deferred_updates(dc); - dc->hwss.optimize_bandwidth(dc, context); + dc->hwss.optimize_bandwidth(dc, context); - if (dc->debug.enable_double_buffered_dsc_pg_support) - dc->hwss.update_dsc_pg(dc, context, true); + if (dc->debug.enable_double_buffered_dsc_pg_support) + dc->hwss.update_dsc_pg(dc, context, true); + } dc->optimized_required = false; dc->wm_optimized_required = false; @@ -3866,12 +3879,9 @@ void dc_commit_updates_for_stream(struct dc *dc, if (new_pipe->plane_state && new_pipe->plane_state != old_pipe->plane_state) new_pipe->plane_state->force_full_update = true; } - } else if (update_type == UPDATE_TYPE_FAST && dc_ctx->dce_version >= DCE_VERSION_MAX) { + } else if (update_type == UPDATE_TYPE_FAST) { /* * Previous frame finished and HW is ready for optimization. - * - * Only relevant for DCN behavior where we can guarantee the optimization - * is safe to apply - retain the legacy behavior for DCE. */ dc_post_update_surfaces_to_stream(dc); } -- cgit 1.4.1 From 788c76c33df9758778b7fa7f25309a91276c9be1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 21 Jun 2023 16:36:12 -0500 Subject: PCI/ACPI: Validate acpi_pci_set_power_state() parameter commit 5557b62634abbd55bab7b154ce4bca348ad7f96f upstream. Previously acpi_pci_set_power_state() assumed the requested power state was valid (PCI_D0 ... PCI_D3cold). If a caller supplied something else, we could index outside the state_conv[] array and pass junk to acpi_device_set_power(). Validate the pci_power_t parameter and return -EINVAL if it's invalid. Link: https://lore.kernel.org/r/20230621222857.GA122930@bhelgaas Signed-off-by: Bjorn Helgaas Reviewed-by: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-acpi.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 1698205dd73c..e6bf9604234c 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1053,32 +1053,37 @@ int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) [PCI_D3hot] = ACPI_STATE_D3_HOT, [PCI_D3cold] = ACPI_STATE_D3_COLD, }; - int error = -EINVAL; + int error; /* If the ACPI device has _EJ0, ignore the device */ if (!adev || acpi_has_method(adev->handle, "_EJ0")) return -ENODEV; switch (state) { - case PCI_D3cold: - if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) == - PM_QOS_FLAGS_ALL) { - error = -EBUSY; - break; - } - fallthrough; case PCI_D0: case PCI_D1: case PCI_D2: case PCI_D3hot: - error = acpi_device_set_power(adev, state_conv[state]); + case PCI_D3cold: + break; + default: + return -EINVAL; + } + + if (state == PCI_D3cold) { + if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) == + PM_QOS_FLAGS_ALL) + return -EBUSY; } - if (!error) - pci_dbg(dev, "power state changed by ACPI to %s\n", - acpi_power_state_string(adev->power.state)); + error = acpi_device_set_power(adev, state_conv[state]); + if (error) + return error; + + pci_dbg(dev, "power state changed by ACPI to %s\n", + acpi_power_state_string(adev->power.state)); - return error; + return 0; } pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) -- cgit 1.4.1 From d856e6f8a0b44cfb233b669fb289edb25b9c2fb9 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 20 Jun 2023 09:04:51 -0500 Subject: PCI/ACPI: Call _REG when transitioning D-states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 112a7f9c8edbf76f7cb83856a6cb6b60a210b659 upstream. ACPI r6.5, sec 6.5.4, describes how AML is unable to access an OperationRegion unless _REG has been called to connect a handler: The OS runs _REG control methods to inform AML code of a change in the availability of an operation region. When an operation region handler is unavailable, AML cannot access data fields in that region. (Operation region writes will be ignored and reads will return indeterminate data.) The PCI core does not call _REG at any time, leading to the undefined behavior mentioned in the spec. The spec explains that _REG should be executed to indicate whether a given region can be accessed: Once _REG has been executed for a particular operation region, indicating that the operation region handler is ready, a control method can access fields in the operation region. Conversely, control methods must not access fields in operation regions when _REG method execution has not indicated that the operation region handler is ready. An example included in the spec demonstrates calling _REG when devices are turned off: "when the host controller or bridge controller is turned off or disabled, PCI Config Space Operation Regions for child devices are no longer available. As such, ETH0’s _REG method will be run when it is turned off and will again be run when PCI1 is turned off." It is reported that ASMedia PCIe GPIO controllers fail functional tests after the system has returning from suspend (S3 or s2idle). This is because the BIOS checks whether the OSPM has called the _REG method to determine whether it can interact with the OperationRegion assigned to the device as part of the other AML called for the device. To fix this issue, call acpi_evaluate_reg() when devices are transitioning to D3cold or D0. [bhelgaas: split pci_power_t checking to preliminary patch] Link: https://uefi.org/specs/ACPI/6.5/06_Device_Configuration.html#reg-region Link: https://lore.kernel.org/r/20230620140451.21007-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-acpi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index e6bf9604234c..7aa1c20582ab 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1043,6 +1043,16 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev) return false; } +static void acpi_pci_config_space_access(struct pci_dev *dev, bool enable) +{ + int val = enable ? ACPI_REG_CONNECT : ACPI_REG_DISCONNECT; + int ret = acpi_evaluate_reg(ACPI_HANDLE(&dev->dev), + ACPI_ADR_SPACE_PCI_CONFIG, val); + if (ret) + pci_dbg(dev, "ACPI _REG %s evaluation failed (%d)\n", + enable ? "connect" : "disconnect", ret); +} + int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); @@ -1074,6 +1084,9 @@ int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) == PM_QOS_FLAGS_ALL) return -EBUSY; + + /* Notify AML lack of PCI config space availability */ + acpi_pci_config_space_access(dev, false); } error = acpi_device_set_power(adev, state_conv[state]); @@ -1083,6 +1096,15 @@ int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) pci_dbg(dev, "power state changed by ACPI to %s\n", acpi_power_state_string(adev->power.state)); + /* + * Notify AML of PCI config space availability. Config space is + * accessible in all states except D3cold; the only transitions + * that change availability are transitions to D3cold and from + * D3cold to D0. + */ + if (state == PCI_D0) + acpi_pci_config_space_access(dev, true); + return 0; } -- cgit 1.4.1 From 296927dbae7d2418cfd65b98788cbec2fd48afa1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 2 Jul 2023 23:20:17 -0700 Subject: execve: always mark stack as growing down during early stack setup commit f66066bc5136f25e36a2daff4896c768f18c211e upstream. While our user stacks can grow either down (all common architectures) or up (parisc and the ia64 register stack), the initial stack setup when we copy the argument and environment strings to the new stack at execve() time is always done by extending the stack downwards. But it turns out that in commit 8d7071af8907 ("mm: always expand the stack with the mmap write lock held"), as part of making the stack growing code more robust, 'expand_downwards()' was now made to actually check the vma flags: if (!(vma->vm_flags & VM_GROWSDOWN)) return -EFAULT; and that meant that this execve-time stack expansion started failing on parisc, because on that architecture, the stack flags do not contain the VM_GROWSDOWN bit. At the same time the new check in expand_downwards() is clearly correct, and simplified the callers, so let's not remove it. The solution is instead to just codify the fact that yes, during execve(), the stack grows down. This not only matches reality, it ends up being particularly simple: we already have special execve-time flags for the stack (VM_STACK_INCOMPLETE_SETUP) and use those flags to avoid page migration during this setup time (see vma_is_temporary_stack() and invalid_migration_vma()). So just add VM_GROWSDOWN to that set of temporary flags, and now our stack flags automatically match reality, and the parisc stack expansion works again. Note that the VM_STACK_INCOMPLETE_SETUP bits will be cleared when the stack is finalized, so we only add the extra VM_GROWSDOWN bit on CONFIG_STACK_GROWSUP architectures (ie parisc) rather than adding it in general. Link: https://lore.kernel.org/all/612eaa53-6904-6e16-67fc-394f4faa0e16@bell.net/ Link: https://lore.kernel.org/all/5fd98a09-4792-1433-752d-029ae3545168@gmx.de/ Fixes: 8d7071af8907 ("mm: always expand the stack with the mmap write lock held") Reported-by: John David Anglin Reported-and-tested-by: Helge Deller Reported-and-tested-by: Guenter Roeck Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index f5b6ef943ede..b8ed44f401b5 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -378,7 +378,7 @@ extern unsigned int kobjsize(const void *objp); #endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */ /* Bits set in the VMA until the stack is in its final location */ -#define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ) +#define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ | VM_STACK_EARLY) #define TASK_EXEC ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) @@ -400,8 +400,10 @@ extern unsigned int kobjsize(const void *objp); #ifdef CONFIG_STACK_GROWSUP #define VM_STACK VM_GROWSUP +#define VM_STACK_EARLY VM_GROWSDOWN #else #define VM_STACK VM_GROWSDOWN +#define VM_STACK_EARLY 0 #endif #define VM_STACK_FLAGS (VM_STACK | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT) -- cgit 1.4.1 From 67e3b5230cefed1eca470c460a2035f02986cebb Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Tue, 14 Mar 2023 19:51:59 +1100 Subject: nubus: Partially revert proc_create_single_data() conversion commit 0e96647cff9224db564a1cee6efccb13dbe11ee2 upstream. The conversion to proc_create_single_data() introduced a regression whereby reading a file in /proc/bus/nubus results in a seg fault: # grep -r . /proc/bus/nubus/e/ Data read fault at 0x00000020 in Super Data (pc=0x1074c2) BAD KERNEL BUSERR Oops: 00000000 Modules linked in: PC: [<001074c2>] PDE_DATA+0xc/0x16 SR: 2010 SP: 38284958 a2: 01152370 d0: 00000001 d1: 01013000 d2: 01002790 d3: 00000000 d4: 00000001 d5: 0008ce2e a0: 00000000 a1: 00222a40 Process grep (pid: 45, task=142f8727) Frame format=B ssw=074d isc=2008 isb=4e5e daddr=00000020 dobuf=01199e70 baddr=001074c8 dibuf=ffffffff ver=f Stack from 01199e48: 01199e70 00222a58 01002790 00000000 011a3000 01199eb0 015000c0 00000000 00000000 01199ec0 01199ec0 000d551a 011a3000 00000001 00000000 00018000 d003f000 00000003 00000001 0002800d 01052840 01199fa8 c01f8000 00000000 00000029 0b532b80 00000000 00000000 00000029 0b532b80 01199ee4 00103640 011198c0 d003f000 00018000 01199fa8 00000000 011198c0 00000000 01199f4c 000b3344 011198c0 d003f000 00018000 01199fa8 00000000 00018000 011198c0 Call Trace: [<00222a58>] nubus_proc_rsrc_show+0x18/0xa0 [<000d551a>] seq_read+0xc4/0x510 [<00018000>] fp_fcos+0x2/0x82 [<0002800d>] __sys_setreuid+0x115/0x1c6 [<00103640>] proc_reg_read+0x5c/0xb0 [<00018000>] fp_fcos+0x2/0x82 [<000b3344>] __vfs_read+0x2c/0x13c [<00018000>] fp_fcos+0x2/0x82 [<00018000>] fp_fcos+0x2/0x82 [<000b8aa2>] sys_statx+0x60/0x7e [<000b34b6>] vfs_read+0x62/0x12a [<00018000>] fp_fcos+0x2/0x82 [<00018000>] fp_fcos+0x2/0x82 [<000b39c2>] ksys_read+0x48/0xbe [<00018000>] fp_fcos+0x2/0x82 [<000b3a4e>] sys_read+0x16/0x1a [<00018000>] fp_fcos+0x2/0x82 [<00002b84>] syscall+0x8/0xc [<00018000>] fp_fcos+0x2/0x82 [<0000c016>] not_ext+0xa/0x18 Code: 4e5e 4e75 4e56 0000 206e 0008 2068 ffe8 <2068> 0020 2008 4e5e 4e75 4e56 0000 2f0b 206e 0008 2068 0004 2668 0020 206b ffe8 Disabling lock debugging due to kernel taint Segmentation fault The proc_create_single_data() conversion does not work because single_open(file, nubus_proc_rsrc_show, PDE_DATA(inode)) is not equivalent to the original code. Fixes: 3f3942aca6da ("proc: introduce proc_create_single{,_data}") Cc: Christoph Hellwig Cc: stable@vger.kernel.org # 5.6+ Signed-off-by: Finn Thain Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/d4e2a586e793cc8d9442595684ab8a077c0fe726.1678783919.git.fthain@linux-m68k.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/nubus/proc.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/nubus/proc.c b/drivers/nubus/proc.c index 1fd667852271..cd4bd06cf309 100644 --- a/drivers/nubus/proc.c +++ b/drivers/nubus/proc.c @@ -137,6 +137,18 @@ static int nubus_proc_rsrc_show(struct seq_file *m, void *v) return 0; } +static int nubus_rsrc_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, nubus_proc_rsrc_show, inode); +} + +static const struct proc_ops nubus_rsrc_proc_ops = { + .proc_open = nubus_rsrc_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + void nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir, const struct nubus_dirent *ent, unsigned int size) @@ -152,8 +164,8 @@ void nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir, pded = nubus_proc_alloc_pde_data(nubus_dirptr(ent), size); else pded = NULL; - proc_create_single_data(name, S_IFREG | 0444, procdir, - nubus_proc_rsrc_show, pded); + proc_create_data(name, S_IFREG | 0444, procdir, + &nubus_rsrc_proc_ops, pded); } void nubus_proc_add_rsrc(struct proc_dir_entry *procdir, @@ -166,9 +178,9 @@ void nubus_proc_add_rsrc(struct proc_dir_entry *procdir, return; snprintf(name, sizeof(name), "%x", ent->type); - proc_create_single_data(name, S_IFREG | 0444, procdir, - nubus_proc_rsrc_show, - nubus_proc_alloc_pde_data(data, 0)); + proc_create_data(name, S_IFREG | 0444, procdir, + &nubus_rsrc_proc_ops, + nubus_proc_alloc_pde_data(data, 0)); } /* -- cgit 1.4.1 From 50e36c2897ba97af09fc0ce4dfe2ded47ecebbb1 Mon Sep 17 00:00:00 2001 From: Krister Johansen Date: Wed, 25 Jan 2023 10:34:18 -0800 Subject: perf symbols: Symbol lookup with kcore can fail if multiple segments match stext commit 1c249565426e3a9940102c0ba9f63914f7cda73d upstream. This problem was encountered on an arm64 system with a lot of memory. Without kernel debug symbols installed, and with both kcore and kallsyms available, perf managed to get confused and returned "unknown" for all of the kernel symbols that it tried to look up. On this system, stext fell within the vmalloc segment. The kcore symbol matching code tries to find the first segment that contains stext and uses that to replace the segment generated from just the kallsyms information. In this case, however, there were two: a very large vmalloc segment, and the text segment. This caused perf to get confused because multiple overlapping segments were inserted into the RB tree that holds the discovered segments. However, that alone wasn't sufficient to cause the problem. Even when we could find the segment, the offsets were adjusted in such a way that the newly generated symbols didn't line up with the instruction addresses in the trace. The most obvious solution would be to consult which segment type is text from kcore, but this information is not exposed to users. Instead, select the smallest matching segment that contains stext instead of the first matching segment. This allows us to match the text segment instead of vmalloc, if one is contained within the other. Reviewed-by: Adrian Hunter Signed-off-by: Krister Johansen Cc: Alexander Shishkin Cc: David Reaver Cc: Ian Rogers Cc: Jiri Olsa Cc: Mark Rutland Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20230125183418.GD1963@templeofstupid.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Krister Johansen Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/symbol.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index a3a165ae933a..98014f937568 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1368,10 +1368,23 @@ static int dso__load_kcore(struct dso *dso, struct map *map, /* Find the kernel map using the '_stext' symbol */ if (!kallsyms__get_function_start(kallsyms_filename, "_stext", &stext)) { + u64 replacement_size = 0; + list_for_each_entry(new_map, &md.maps, node) { - if (stext >= new_map->start && stext < new_map->end) { + u64 new_size = new_map->end - new_map->start; + + if (!(stext >= new_map->start && stext < new_map->end)) + continue; + + /* + * On some architectures, ARM64 for example, the kernel + * text can get allocated inside of the vmalloc segment. + * Select the smallest matching segment, in case stext + * falls within more than one in the list. + */ + if (!replacement_map || new_size < replacement_size) { replacement_map = new_map; - break; + replacement_size = new_size; } } } -- cgit 1.4.1 From c437b26bc3ae511521c1d4884c13b36bb292dce6 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Mon, 15 May 2023 19:32:16 +0200 Subject: scripts/tags.sh: Resolve gtags empty index generation commit e1b37563caffc410bb4b55f153ccb14dede66815 upstream. gtags considers any file outside of its current working directory "outside the source tree" and refuses to index it. For O= kernel builds, or when "make" is invoked from a directory other then the kernel source tree, gtags ignores the entire kernel source and generates an empty index. Force-set gtags current working directory to the kernel source tree. Due to commit 9da0763bdd82 ("kbuild: Use relative path when building in a subdir of the source tree"), if the kernel build is done in a sub-directory of the kernel source tree, the kernel Makefile will set the kernel's $srctree to ".." for shorter compile-time and run-time warnings. Consequently, the list of files to be indexed will be in the "../*" form, rendering all such paths invalid once gtags switches to the kernel source tree as its current working directory. If gtags indexing is requested and the build directory is not the kernel source tree, index all files in absolute-path form. Note, indexing in absolute-path form will not affect the generated index, as paths in gtags indices are always relative to the gtags "root directory" anyway (as evidenced by "gtags --dump"). Signed-off-by: Ahmed S. Darwish Cc: Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- scripts/tags.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index 0d045182c08c..f23b9b7ed015 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -25,6 +25,13 @@ else tree=${srctree}/ fi +# gtags(1) refuses to index any file outside of its current working dir. +# If gtags indexing is requested and the build output directory is not +# the kernel source tree, index all files in absolute-path form. +if [[ "$1" == "gtags" && -n "${tree}" ]]; then + tree=$(realpath "$tree")/ +fi + # Detect if ALLSOURCE_ARCHS is set. If not, we assume SRCARCH if [ "${ALLSOURCE_ARCHS}" = "" ]; then ALLSOURCE_ARCHS=${SRCARCH} @@ -124,7 +131,7 @@ docscope() dogtags() { - all_target_sources | gtags -i -f - + all_target_sources | gtags -i -C "${tree:-.}" -f - "$PWD" } # Basic regular expressions with an optional /kind-spec/ for ctags and -- cgit 1.4.1 From fe56f507a11a72aeba7044a29653c049ce6c1374 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Mon, 15 May 2023 19:32:17 +0200 Subject: docs: Set minimal gtags / GNU GLOBAL version to 6.6.5 commit b230235b386589d8f0d631b1c77a95ca79bb0732 upstream. Kernel build now uses the gtags "-C (--directory)" option, available since GNU GLOBAL v6.6.5. Update the documentation accordingly. Signed-off-by: Ahmed S. Darwish Cc: Link: https://lists.gnu.org/archive/html/info-global/2020-09/msg00000.html Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- Documentation/process/changes.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index 9844ca3a71a6..c76b02d94094 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -60,6 +60,7 @@ openssl & libcrypto 1.0.0 openssl version bc 1.06.95 bc --version Sphinx\ [#f1]_ 1.7 sphinx-build --version cpio any cpio --version +gtags (optional) 6.6.5 gtags --version ====================== =============== ======================================== .. [#f1] Sphinx is needed only to build the Kernel documentation @@ -174,6 +175,12 @@ You will need openssl to build kernels 3.7 and higher if module signing is enabled. You will also need openssl development packages to build kernels 4.3 and higher. +gtags / GNU GLOBAL (optional) +----------------------------- + +The kernel build requires GNU GLOBAL version 6.6.5 or later to generate +tag files through ``make gtags``. This is due to its use of the gtags +``-C (--directory)`` flag. System utilities **************** -- cgit 1.4.1 From 9d0b2afadfd71e9bedd593358bd7ac4701e46477 Mon Sep 17 00:00:00 2001 From: Bas Nieuwenhuizen Date: Sat, 13 May 2023 14:51:00 +0200 Subject: drm/amdgpu: Validate VM ioctl flags. commit a2b308044dcaca8d3e580959a4f867a1d5c37fac upstream. None have been defined yet, so reject anybody setting any. Mesa sets it to 0 anyway. Signed-off-by: Bas Nieuwenhuizen Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 003aa9e47085..5a8a4cda7e98 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2363,6 +2363,10 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) long timeout = msecs_to_jiffies(2000); int r; + /* No valid flags defined yet */ + if (args->in.flags) + return -EINVAL; + switch (args->in.op) { case AMDGPU_VM_OP_RESERVE_VMID: /* We only have requirement to reserve vmid from gfxhub */ -- cgit 1.4.1 From c50065a3927932cd9baf3d5c94c91b58c31200d5 Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Fri, 24 Feb 2023 11:35:43 -0700 Subject: drm/amd/display: Ensure vmin and vmax adjust for DCE commit 2820433be2a33beb44b13b367e155cf221f29610 upstream. [Why & How] In the commit 32953485c558 ("drm/amd/display: Do not update DRR while BW optimizations pending"), a modification was added to avoid adjusting DRR if optimized bandwidth is set. This change was only intended for DCN, but one part of the patch changed the code path for DCE devices and caused regressions to the kms_vrr test. To address this problem, this commit adds a modification in which dc_stream_adjust_vmin_vmax will be fully executed in DCE devices. Fixes: 32953485c558 ("drm/amd/display: Do not update DRR while BW optimizations pending") Reviewed-by: Aric Cyr Acked-by: Qingqing Zhuo Signed-off-by: Rodrigo Siqueira Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/core/dc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 277ccee8b9f8..087a4838488b 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -405,8 +405,9 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, * Don't adjust DRR while there's bandwidth optimizations pending to * avoid conflicting with firmware updates. */ - if (dc->optimized_required || dc->wm_optimized_required) - return false; + if (dc->ctx->dce_version > DCE_VERSION_MAX) + if (dc->optimized_required || dc->wm_optimized_required) + return false; stream->adjust.v_total_max = adjust->v_total_max; stream->adjust.v_total_mid = adjust->v_total_mid; -- cgit 1.4.1 From 61fd484b2cf6bc8022e8e5ea6f693a9991740ac2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 5 Jul 2023 18:27:38 +0100 Subject: Linux 6.1.38 Link: https://lore.kernel.org/r/20230703184519.121965745@linuxfoundation.org Tested-by: Takeshi Ogasawara Link: https://lore.kernel.org/r/20230704084611.071971014@linuxfoundation.org Tested-by: Takeshi Ogasawara Tested-by: Bagas Sanjaya Tested-by: Conor Dooley Tested-by: Salvatore Bonaccorso Tested-by: Ron Economos Tested-by: Markus Reichelt Tested-by: Guenter Roeck Tested-by: Linux Kernel Functional Testing Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 26d73ce7e926..57c891b8b13c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 37 +SUBLEVEL = 38 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1 From 9f12effd40d7a3bc92cc4961f1cab1459c3dc520 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 19 Apr 2023 07:24:46 -0400 Subject: drm: use mgr->dev in drm_dbg_kms in drm_dp_add_payload_part2 commit 54d217406afe250d7a768783baaa79a035f21d38 upstream. I've been experiencing some intermittent crashes down in the display driver code. The symptoms are ususally a line like this in dmesg: amdgpu 0000:30:00.0: [drm] Failed to create MST payload for port 000000006d3a3885: -5 ...followed by an Oops due to a NULL pointer dereference. Switch to using mgr->dev instead of state->dev since "state" can be NULL in some cases. Link: https://bugzilla.redhat.com/show_bug.cgi?id=2184855 Suggested-by: Jani Nikula Signed-off-by: Jeff Layton Reviewed-by: Jani Nikula Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20230419112447.18471-1-jlayton@kernel.org Cc: "Limonciello, Mario" Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 38dab76ae69e..e2e21ce79510 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -3404,7 +3404,7 @@ int drm_dp_add_payload_part2(struct drm_dp_mst_topology_mgr *mgr, /* Skip failed payloads */ if (payload->vc_start_slot == -1) { - drm_dbg_kms(state->dev, "Part 1 of payload creation for %s failed, skipping part 2\n", + drm_dbg_kms(mgr->dev, "Part 1 of payload creation for %s failed, skipping part 2\n", payload->port->connector->name); return -EIO; } -- cgit 1.4.1 From 0fd958feaee129938c900d8f281bf48b874679b0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 21:56:12 +0200 Subject: fs: pipe: reveal missing function protoypes [ Upstream commit 247c8d2f9837a3e29e3b6b7a4aa9c36c37659dd4 ] A couple of functions from fs/pipe.c are used both internally and for the watch queue code, but the declaration is only visible when the latter is enabled: fs/pipe.c:1254:5: error: no previous prototype for 'pipe_resize_ring' fs/pipe.c:758:15: error: no previous prototype for 'account_pipe_buffers' fs/pipe.c:764:6: error: no previous prototype for 'too_many_pipe_buffers_soft' fs/pipe.c:771:6: error: no previous prototype for 'too_many_pipe_buffers_hard' fs/pipe.c:777:6: error: no previous prototype for 'pipe_is_unprivileged_user' Make the visible unconditionally to avoid these warnings. Fixes: c73be61cede5 ("pipe: Add general notification queue support") Signed-off-by: Arnd Bergmann Message-Id: <20230516195629.551602-1-arnd@kernel.org> Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- include/linux/pipe_fs_i.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 6cb65df3e3ba..28b3c6a67397 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -241,18 +241,14 @@ void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); extern const struct pipe_buf_operations nosteal_pipe_buf_ops; -#ifdef CONFIG_WATCH_QUEUE unsigned long account_pipe_buffers(struct user_struct *user, unsigned long old, unsigned long new); bool too_many_pipe_buffers_soft(unsigned long user_bufs); bool too_many_pipe_buffers_hard(unsigned long user_bufs); bool pipe_is_unprivileged_user(void); -#endif /* for F_SETPIPE_SZ and F_GETPIPE_SZ */ -#ifdef CONFIG_WATCH_QUEUE int pipe_resize_ring(struct pipe_inode_info *pipe, unsigned int nr_slots); -#endif long pipe_fcntl(struct file *, unsigned int, unsigned long arg); struct pipe_inode_info *get_pipe_info(struct file *file, bool for_splice); -- cgit 1.4.1 From 73b9d7ea08e240ffb1c1bb30c6ce8c34549fe54b Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 17 May 2023 10:42:21 -0700 Subject: block: Fix the type of the second bdev_op_is_zoned_write() argument [ Upstream commit 3ddbe2a7e0d4a155a805f69c906c9beed30d4cc4 ] Change the type of the second argument of bdev_op_is_zoned_write() from blk_opf_t into enum req_op because this function expects an operation without flags as second argument. Reviewed-by: Johannes Thumshirn Reviewed-by: Pankaj Raghav Reviewed-by: Christoph Hellwig Reviewed-by: Damien Le Moal Reviewed-by: Hannes Reinecke Cc: Ming Lei Fixes: 8cafdb5ab94c ("block: adapt blk_mq_plug() to not plug for writes that require a zone lock") Signed-off-by: Bart Van Assche Link: https://lore.kernel.org/r/20230517174230.897144-4-bvanassche@acm.org Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- include/linux/blkdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 891f8cbcd043..427e79ac7219 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1305,7 +1305,7 @@ static inline bool bdev_is_zoned(struct block_device *bdev) } static inline bool bdev_op_is_zoned_write(struct block_device *bdev, - blk_opf_t op) + enum req_op op) { if (!bdev_is_zoned(bdev)) return false; -- cgit 1.4.1 From 3564500b0d1e36cbdb1b7e12cd71606816ab6bcc Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 6 Dec 2022 14:03:52 +0800 Subject: erofs: clean up cached I/O strategies [ Upstream commit 1282dea37b09087b8aec59f0774572c16b52276a ] After commit 4c7e42552b3a ("erofs: remove useless cache strategy of DELAYEDALLOC"), only one cached I/O allocation strategy is supported: When cached I/O is preferred, page allocation is applied without direct reclaim. If allocation fails, fall back to inplace I/O. Let's get rid of z_erofs_cache_alloctype. No logical changes. Reviewed-by: Yue Hu Reviewed-by: Chao Yu Signed-off-by: Yue Hu Signed-off-by: Gao Xiang Link: https://lore.kernel.org/r/20221206060352.152830-1-xiang@kernel.org Stable-dep-of: 967c28b23f6c ("erofs: kill hooked chains to avoid loops on deduplicated compressed images") Signed-off-by: Sasha Levin --- fs/erofs/zdata.c | 77 +++++++++++++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index cf4871834ebb..ccf7c55d477f 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -175,16 +175,6 @@ static void z_erofs_free_pcluster(struct z_erofs_pcluster *pcl) DBG_BUGON(1); } -/* how to allocate cached pages for a pcluster */ -enum z_erofs_cache_alloctype { - DONTALLOC, /* don't allocate any cached pages */ - /* - * try to use cached I/O if page allocation succeeds or fallback - * to in-place I/O instead to avoid any direct reclaim. - */ - TRYALLOC, -}; - /* * tagged pointer with 1-bit tag for all compressed pages * tag 0 - the page is just found with an extra page reference @@ -292,12 +282,29 @@ struct z_erofs_decompress_frontend { .inode = __i, .owned_head = Z_EROFS_PCLUSTER_TAIL, \ .mode = Z_EROFS_PCLUSTER_FOLLOWED, .backmost = true } +static bool z_erofs_should_alloc_cache(struct z_erofs_decompress_frontend *fe) +{ + unsigned int cachestrategy = EROFS_I_SB(fe->inode)->opt.cache_strategy; + + if (cachestrategy <= EROFS_ZIP_CACHE_DISABLED) + return false; + + if (fe->backmost) + return true; + + if (cachestrategy >= EROFS_ZIP_CACHE_READAROUND && + fe->map.m_la < fe->headoffset) + return true; + + return false; +} + static void z_erofs_bind_cache(struct z_erofs_decompress_frontend *fe, - enum z_erofs_cache_alloctype type, struct page **pagepool) { struct address_space *mc = MNGD_MAPPING(EROFS_I_SB(fe->inode)); struct z_erofs_pcluster *pcl = fe->pcl; + bool shouldalloc = z_erofs_should_alloc_cache(fe); bool standalone = true; /* * optimistic allocation without direct reclaim since inplace I/O @@ -326,18 +333,19 @@ static void z_erofs_bind_cache(struct z_erofs_decompress_frontend *fe, } else { /* I/O is needed, no possible to decompress directly */ standalone = false; - switch (type) { - case TRYALLOC: - newpage = erofs_allocpage(pagepool, gfp); - if (!newpage) - continue; - set_page_private(newpage, - Z_EROFS_PREALLOCATED_PAGE); - t = tag_compressed_page_justfound(newpage); - break; - default: /* DONTALLOC */ + if (!shouldalloc) continue; - } + + /* + * try to use cached I/O if page allocation + * succeeds or fallback to in-place I/O instead + * to avoid any direct reclaim. + */ + newpage = erofs_allocpage(pagepool, gfp); + if (!newpage) + continue; + set_page_private(newpage, Z_EROFS_PREALLOCATED_PAGE); + t = tag_compressed_page_justfound(newpage); } if (!cmpxchg_relaxed(&pcl->compressed_bvecs[i].page, NULL, @@ -638,20 +646,6 @@ static bool z_erofs_collector_end(struct z_erofs_decompress_frontend *fe) return true; } -static bool should_alloc_managed_pages(struct z_erofs_decompress_frontend *fe, - unsigned int cachestrategy, - erofs_off_t la) -{ - if (cachestrategy <= EROFS_ZIP_CACHE_DISABLED) - return false; - - if (fe->backmost) - return true; - - return cachestrategy >= EROFS_ZIP_CACHE_READAROUND && - la < fe->headoffset; -} - static int z_erofs_read_fragment(struct inode *inode, erofs_off_t pos, struct page *page, unsigned int pageofs, unsigned int len) @@ -688,12 +682,9 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, struct page *page, struct page **pagepool) { struct inode *const inode = fe->inode; - struct erofs_sb_info *const sbi = EROFS_I_SB(inode); struct erofs_map_blocks *const map = &fe->map; const loff_t offset = page_offset(page); bool tight = true, exclusive; - - enum z_erofs_cache_alloctype cache_strategy; unsigned int cur, end, spiltted; int err = 0; @@ -747,13 +738,7 @@ repeat: fe->mode = Z_EROFS_PCLUSTER_FOLLOWED_NOINPLACE; } else { /* bind cache first when cached decompression is preferred */ - if (should_alloc_managed_pages(fe, sbi->opt.cache_strategy, - map->m_la)) - cache_strategy = TRYALLOC; - else - cache_strategy = DONTALLOC; - - z_erofs_bind_cache(fe, cache_strategy, pagepool); + z_erofs_bind_cache(fe, pagepool); } hitted: /* -- cgit 1.4.1 From 3379f13ebc1e8cc5ec866fabf7282a7bd7249007 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sat, 4 Feb 2023 17:30:36 +0800 Subject: erofs: avoid tagged pointers to mark sync decompression [ Upstream commit cdba55067f2f9fdc7870ffcb6aef912d3468cff8 ] We could just use a boolean in z_erofs_decompressqueue for sync decompression to simplify the code. Reviewed-by: Yue Hu Reviewed-by: Chao Yu Signed-off-by: Gao Xiang Link: https://lore.kernel.org/r/20230204093040.97967-2-hsiangkao@linux.alibaba.com Stable-dep-of: 967c28b23f6c ("erofs: kill hooked chains to avoid loops on deduplicated compressed images") Signed-off-by: Sasha Levin --- fs/erofs/zdata.c | 42 ++++++++++++++++-------------------------- fs/erofs/zdata.h | 2 +- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index ccf7c55d477f..8e80871a8c1d 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1157,12 +1157,12 @@ static void z_erofs_decompressqueue_work(struct work_struct *work) } static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io, - bool sync, int bios) + int bios) { struct erofs_sb_info *const sbi = EROFS_SB(io->sb); /* wake up the caller thread for sync decompression */ - if (sync) { + if (io->sync) { if (!atomic_add_return(bios, &io->pending_bios)) complete(&io->u.done); return; @@ -1294,9 +1294,8 @@ out: /* the only exit (for tracing and debugging) */ return page; } -static struct z_erofs_decompressqueue * -jobqueue_init(struct super_block *sb, - struct z_erofs_decompressqueue *fgq, bool *fg) +static struct z_erofs_decompressqueue *jobqueue_init(struct super_block *sb, + struct z_erofs_decompressqueue *fgq, bool *fg) { struct z_erofs_decompressqueue *q; @@ -1313,6 +1312,7 @@ fg_out: init_completion(&fgq->u.done); atomic_set(&fgq->pending_bios, 0); q->eio = false; + q->sync = true; } q->sb = sb; q->head = Z_EROFS_PCLUSTER_TAIL_CLOSED; @@ -1326,20 +1326,6 @@ enum { NR_JOBQUEUES, }; -static void *jobqueueset_init(struct super_block *sb, - struct z_erofs_decompressqueue *q[], - struct z_erofs_decompressqueue *fgq, bool *fg) -{ - /* - * if managed cache is enabled, bypass jobqueue is needed, - * no need to read from device for all pclusters in this queue. - */ - q[JQ_BYPASS] = jobqueue_init(sb, fgq + JQ_BYPASS, NULL); - q[JQ_SUBMIT] = jobqueue_init(sb, fgq + JQ_SUBMIT, fg); - - return tagptr_cast_ptr(tagptr_fold(tagptr1_t, q[JQ_SUBMIT], *fg)); -} - static void move_to_bypass_jobqueue(struct z_erofs_pcluster *pcl, z_erofs_next_pcluster_t qtail[], z_erofs_next_pcluster_t owned_head) @@ -1361,8 +1347,7 @@ static void move_to_bypass_jobqueue(struct z_erofs_pcluster *pcl, static void z_erofs_decompressqueue_endio(struct bio *bio) { - tagptr1_t t = tagptr_init(tagptr1_t, bio->bi_private); - struct z_erofs_decompressqueue *q = tagptr_unfold_ptr(t); + struct z_erofs_decompressqueue *q = bio->bi_private; blk_status_t err = bio->bi_status; struct bio_vec *bvec; struct bvec_iter_all iter_all; @@ -1381,7 +1366,7 @@ static void z_erofs_decompressqueue_endio(struct bio *bio) } if (err) q->eio = true; - z_erofs_decompress_kickoff(q, tagptr_unfold_tags(t), -1); + z_erofs_decompress_kickoff(q, -1); bio_put(bio); } @@ -1394,7 +1379,6 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f, struct address_space *mc = MNGD_MAPPING(EROFS_SB(sb)); z_erofs_next_pcluster_t qtail[NR_JOBQUEUES]; struct z_erofs_decompressqueue *q[NR_JOBQUEUES]; - void *bi_private; z_erofs_next_pcluster_t owned_head = f->owned_head; /* bio is NULL initially, so no need to initialize last_{index,bdev} */ pgoff_t last_index; @@ -1404,7 +1388,13 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f, unsigned long pflags; int memstall = 0; - bi_private = jobqueueset_init(sb, q, fgq, force_fg); + /* + * if managed cache is enabled, bypass jobqueue is needed, + * no need to read from device for all pclusters in this queue. + */ + q[JQ_BYPASS] = jobqueue_init(sb, fgq + JQ_BYPASS, NULL); + q[JQ_SUBMIT] = jobqueue_init(sb, fgq + JQ_SUBMIT, force_fg); + qtail[JQ_BYPASS] = &q[JQ_BYPASS]->head; qtail[JQ_SUBMIT] = &q[JQ_SUBMIT]->head; @@ -1473,7 +1463,7 @@ submit_bio_retry: last_bdev = mdev.m_bdev; bio->bi_iter.bi_sector = (sector_t)cur << LOG_SECTORS_PER_BLOCK; - bio->bi_private = bi_private; + bio->bi_private = q[JQ_SUBMIT]; if (f->readahead) bio->bi_opf |= REQ_RAHEAD; ++nr_bios; @@ -1506,7 +1496,7 @@ submit_bio_retry: kvfree(q[JQ_SUBMIT]); return; } - z_erofs_decompress_kickoff(q[JQ_SUBMIT], *force_fg, nr_bios); + z_erofs_decompress_kickoff(q[JQ_SUBMIT], nr_bios); } static void z_erofs_runqueue(struct z_erofs_decompress_frontend *f, diff --git a/fs/erofs/zdata.h b/fs/erofs/zdata.h index d98c95212985..b139de5473a9 100644 --- a/fs/erofs/zdata.h +++ b/fs/erofs/zdata.h @@ -110,7 +110,7 @@ struct z_erofs_decompressqueue { struct work_struct work; } u; - bool eio; + bool eio, sync; }; static inline bool z_erofs_is_inline_pcluster(struct z_erofs_pcluster *pcl) -- cgit 1.4.1 From 041ff2c21b00a5b6db07e231d1457ff580f1caa3 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sat, 4 Feb 2023 17:30:37 +0800 Subject: erofs: remove tagged pointer helpers [ Upstream commit b1ed220c6262bff63cdcb53692e492be0b05206c ] Just open-code the remaining one to simplify the code. Reviewed-by: Yue Hu Reviewed-by: Chao Yu Signed-off-by: Gao Xiang Link: https://lore.kernel.org/r/20230204093040.97967-3-hsiangkao@linux.alibaba.com Stable-dep-of: 967c28b23f6c ("erofs: kill hooked chains to avoid loops on deduplicated compressed images") Signed-off-by: Sasha Levin --- fs/erofs/tagptr.h | 107 ------------------------------------------------------ fs/erofs/zdata.c | 26 +++---------- fs/erofs/zdata.h | 1 - 3 files changed, 6 insertions(+), 128 deletions(-) delete mode 100644 fs/erofs/tagptr.h diff --git a/fs/erofs/tagptr.h b/fs/erofs/tagptr.h deleted file mode 100644 index 64ceb7270b5c..000000000000 --- a/fs/erofs/tagptr.h +++ /dev/null @@ -1,107 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * A tagged pointer implementation - */ -#ifndef __EROFS_FS_TAGPTR_H -#define __EROFS_FS_TAGPTR_H - -#include -#include - -/* - * the name of tagged pointer types are tagptr{1, 2, 3...}_t - * avoid directly using the internal structs __tagptr{1, 2, 3...} - */ -#define __MAKE_TAGPTR(n) \ -typedef struct __tagptr##n { \ - uintptr_t v; \ -} tagptr##n##_t; - -__MAKE_TAGPTR(1) -__MAKE_TAGPTR(2) -__MAKE_TAGPTR(3) -__MAKE_TAGPTR(4) - -#undef __MAKE_TAGPTR - -extern void __compiletime_error("bad tagptr tags") - __bad_tagptr_tags(void); - -extern void __compiletime_error("bad tagptr type") - __bad_tagptr_type(void); - -/* fix the broken usage of "#define tagptr2_t tagptr3_t" by users */ -#define __tagptr_mask_1(ptr, n) \ - __builtin_types_compatible_p(typeof(ptr), struct __tagptr##n) ? \ - (1UL << (n)) - 1 : - -#define __tagptr_mask(ptr) (\ - __tagptr_mask_1(ptr, 1) ( \ - __tagptr_mask_1(ptr, 2) ( \ - __tagptr_mask_1(ptr, 3) ( \ - __tagptr_mask_1(ptr, 4) ( \ - __bad_tagptr_type(), 0))))) - -/* generate a tagged pointer from a raw value */ -#define tagptr_init(type, val) \ - ((typeof(type)){ .v = (uintptr_t)(val) }) - -/* - * directly cast a tagged pointer to the native pointer type, which - * could be used for backward compatibility of existing code. - */ -#define tagptr_cast_ptr(tptr) ((void *)(tptr).v) - -/* encode tagged pointers */ -#define tagptr_fold(type, ptr, _tags) ({ \ - const typeof(_tags) tags = (_tags); \ - if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(type))) \ - __bad_tagptr_tags(); \ -tagptr_init(type, (uintptr_t)(ptr) | tags); }) - -/* decode tagged pointers */ -#define tagptr_unfold_ptr(tptr) \ - ((void *)((tptr).v & ~__tagptr_mask(tptr))) - -#define tagptr_unfold_tags(tptr) \ - ((tptr).v & __tagptr_mask(tptr)) - -/* operations for the tagger pointer */ -#define tagptr_eq(_tptr1, _tptr2) ({ \ - typeof(_tptr1) tptr1 = (_tptr1); \ - typeof(_tptr2) tptr2 = (_tptr2); \ - (void)(&tptr1 == &tptr2); \ -(tptr1).v == (tptr2).v; }) - -/* lock-free CAS operation */ -#define tagptr_cmpxchg(_ptptr, _o, _n) ({ \ - typeof(_ptptr) ptptr = (_ptptr); \ - typeof(_o) o = (_o); \ - typeof(_n) n = (_n); \ - (void)(&o == &n); \ - (void)(&o == ptptr); \ -tagptr_init(o, cmpxchg(&ptptr->v, o.v, n.v)); }) - -/* wrap WRITE_ONCE if atomic update is needed */ -#define tagptr_replace_tags(_ptptr, tags) ({ \ - typeof(_ptptr) ptptr = (_ptptr); \ - *ptptr = tagptr_fold(*ptptr, tagptr_unfold_ptr(*ptptr), tags); \ -*ptptr; }) - -#define tagptr_set_tags(_ptptr, _tags) ({ \ - typeof(_ptptr) ptptr = (_ptptr); \ - const typeof(_tags) tags = (_tags); \ - if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \ - __bad_tagptr_tags(); \ - ptptr->v |= tags; \ -*ptptr; }) - -#define tagptr_clear_tags(_ptptr, _tags) ({ \ - typeof(_ptptr) ptptr = (_ptptr); \ - const typeof(_tags) tags = (_tags); \ - if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \ - __bad_tagptr_tags(); \ - ptptr->v &= ~tags; \ -*ptptr; }) - -#endif /* __EROFS_FS_TAGPTR_H */ diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 8e80871a8c1d..3b5f73224c22 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -175,15 +175,6 @@ static void z_erofs_free_pcluster(struct z_erofs_pcluster *pcl) DBG_BUGON(1); } -/* - * tagged pointer with 1-bit tag for all compressed pages - * tag 0 - the page is just found with an extra page reference - */ -typedef tagptr1_t compressed_page_t; - -#define tag_compressed_page_justfound(page) \ - tagptr_fold(compressed_page_t, page, 1) - static struct workqueue_struct *z_erofs_workqueue __read_mostly; void z_erofs_exit_zip_subsystem(void) @@ -319,7 +310,7 @@ static void z_erofs_bind_cache(struct z_erofs_decompress_frontend *fe, for (i = 0; i < pcl->pclusterpages; ++i) { struct page *page; - compressed_page_t t; + void *t; /* mark pages just found for debugging */ struct page *newpage = NULL; /* the compressed page was loaded before */ @@ -329,7 +320,7 @@ static void z_erofs_bind_cache(struct z_erofs_decompress_frontend *fe, page = find_get_page(mc, pcl->obj.index + i); if (page) { - t = tag_compressed_page_justfound(page); + t = (void *)((unsigned long)page | 1); } else { /* I/O is needed, no possible to decompress directly */ standalone = false; @@ -345,11 +336,10 @@ static void z_erofs_bind_cache(struct z_erofs_decompress_frontend *fe, if (!newpage) continue; set_page_private(newpage, Z_EROFS_PREALLOCATED_PAGE); - t = tag_compressed_page_justfound(newpage); + t = (void *)((unsigned long)newpage | 1); } - if (!cmpxchg_relaxed(&pcl->compressed_bvecs[i].page, NULL, - tagptr_cast_ptr(t))) + if (!cmpxchg_relaxed(&pcl->compressed_bvecs[i].page, NULL, t)) continue; if (page) @@ -1192,8 +1182,6 @@ static struct page *pickup_page_for_submission(struct z_erofs_pcluster *pcl, struct address_space *mapping; struct page *oldpage, *page; - - compressed_page_t t; int justfound; repeat: @@ -1203,10 +1191,8 @@ repeat: if (!page) goto out_allocpage; - /* process the target tagged pointer */ - t = tagptr_init(compressed_page_t, page); - justfound = tagptr_unfold_tags(t); - page = tagptr_unfold_ptr(t); + justfound = (unsigned long)page & 1UL; + page = (struct page *)((unsigned long)page & ~1UL); /* * preallocated cached pages, which is used to avoid direct reclaim diff --git a/fs/erofs/zdata.h b/fs/erofs/zdata.h index b139de5473a9..f196a729c7e8 100644 --- a/fs/erofs/zdata.h +++ b/fs/erofs/zdata.h @@ -7,7 +7,6 @@ #define __EROFS_FS_ZDATA_H #include "internal.h" -#include "tagptr.h" #define Z_EROFS_PCLUSTER_MAX_PAGES (Z_EROFS_PCLUSTER_MAX_SIZE / PAGE_SIZE) #define Z_EROFS_INLINE_BVECS 2 -- cgit 1.4.1 From daed10290bc76748956baac2563e99a29827037d Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sat, 4 Feb 2023 17:30:38 +0800 Subject: erofs: move zdata.h into zdata.c [ Upstream commit a9a94d9373349e1a53f149d2015eb6f03a8517cf ] Definitions in zdata.h are only used in zdata.c and for internal use only. No logic changes. Reviewed-by: Yue Hu Reviewed-by: Chao Yu Signed-off-by: Gao Xiang Link: https://lore.kernel.org/r/20230204093040.97967-4-hsiangkao@linux.alibaba.com Stable-dep-of: 967c28b23f6c ("erofs: kill hooked chains to avoid loops on deduplicated compressed images") Signed-off-by: Sasha Levin --- fs/erofs/zdata.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++- fs/erofs/zdata.h | 177 ------------------------------------------------------- 2 files changed, 165 insertions(+), 178 deletions(-) delete mode 100644 fs/erofs/zdata.h diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 3b5f73224c22..aaddb6781465 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -4,13 +4,177 @@ * https://www.huawei.com/ * Copyright (C) 2022 Alibaba Cloud */ -#include "zdata.h" #include "compress.h" #include #include #include +#define Z_EROFS_PCLUSTER_MAX_PAGES (Z_EROFS_PCLUSTER_MAX_SIZE / PAGE_SIZE) +#define Z_EROFS_INLINE_BVECS 2 + +/* + * let's leave a type here in case of introducing + * another tagged pointer later. + */ +typedef void *z_erofs_next_pcluster_t; + +struct z_erofs_bvec { + struct page *page; + int offset; + unsigned int end; +}; + +#define __Z_EROFS_BVSET(name, total) \ +struct name { \ + /* point to the next page which contains the following bvecs */ \ + struct page *nextpage; \ + struct z_erofs_bvec bvec[total]; \ +} +__Z_EROFS_BVSET(z_erofs_bvset,); +__Z_EROFS_BVSET(z_erofs_bvset_inline, Z_EROFS_INLINE_BVECS); + +/* + * Structure fields follow one of the following exclusion rules. + * + * I: Modifiable by initialization/destruction paths and read-only + * for everyone else; + * + * L: Field should be protected by the pcluster lock; + * + * A: Field should be accessed / updated in atomic for parallelized code. + */ +struct z_erofs_pcluster { + struct erofs_workgroup obj; + struct mutex lock; + + /* A: point to next chained pcluster or TAILs */ + z_erofs_next_pcluster_t next; + + /* L: the maximum decompression size of this round */ + unsigned int length; + + /* L: total number of bvecs */ + unsigned int vcnt; + + /* I: page offset of start position of decompression */ + unsigned short pageofs_out; + + /* I: page offset of inline compressed data */ + unsigned short pageofs_in; + + union { + /* L: inline a certain number of bvec for bootstrap */ + struct z_erofs_bvset_inline bvset; + + /* I: can be used to free the pcluster by RCU. */ + struct rcu_head rcu; + }; + + union { + /* I: physical cluster size in pages */ + unsigned short pclusterpages; + + /* I: tailpacking inline compressed size */ + unsigned short tailpacking_size; + }; + + /* I: compression algorithm format */ + unsigned char algorithmformat; + + /* L: whether partial decompression or not */ + bool partial; + + /* L: indicate several pageofs_outs or not */ + bool multibases; + + /* A: compressed bvecs (can be cached or inplaced pages) */ + struct z_erofs_bvec compressed_bvecs[]; +}; + +/* let's avoid the valid 32-bit kernel addresses */ + +/* the chained workgroup has't submitted io (still open) */ +#define Z_EROFS_PCLUSTER_TAIL ((void *)0x5F0ECAFE) +/* the chained workgroup has already submitted io */ +#define Z_EROFS_PCLUSTER_TAIL_CLOSED ((void *)0x5F0EDEAD) + +#define Z_EROFS_PCLUSTER_NIL (NULL) + +struct z_erofs_decompressqueue { + struct super_block *sb; + atomic_t pending_bios; + z_erofs_next_pcluster_t head; + + union { + struct completion done; + struct work_struct work; + } u; + bool eio, sync; +}; + +static inline bool z_erofs_is_inline_pcluster(struct z_erofs_pcluster *pcl) +{ + return !pcl->obj.index; +} + +static inline unsigned int z_erofs_pclusterpages(struct z_erofs_pcluster *pcl) +{ + if (z_erofs_is_inline_pcluster(pcl)) + return 1; + return pcl->pclusterpages; +} + +/* + * bit 30: I/O error occurred on this page + * bit 0 - 29: remaining parts to complete this page + */ +#define Z_EROFS_PAGE_EIO (1 << 30) + +static inline void z_erofs_onlinepage_init(struct page *page) +{ + union { + atomic_t o; + unsigned long v; + } u = { .o = ATOMIC_INIT(1) }; + + set_page_private(page, u.v); + smp_wmb(); + SetPagePrivate(page); +} + +static inline void z_erofs_onlinepage_split(struct page *page) +{ + atomic_inc((atomic_t *)&page->private); +} + +static inline void z_erofs_page_mark_eio(struct page *page) +{ + int orig; + + do { + orig = atomic_read((atomic_t *)&page->private); + } while (atomic_cmpxchg((atomic_t *)&page->private, orig, + orig | Z_EROFS_PAGE_EIO) != orig); +} + +static inline void z_erofs_onlinepage_endio(struct page *page) +{ + unsigned int v; + + DBG_BUGON(!PagePrivate(page)); + v = atomic_dec_return((atomic_t *)&page->private); + if (!(v & ~Z_EROFS_PAGE_EIO)) { + set_page_private(page, 0); + ClearPagePrivate(page); + if (!(v & Z_EROFS_PAGE_EIO)) + SetPageUptodate(page); + unlock_page(page); + } +} + +#define Z_EROFS_ONSTACK_PAGES 32 + /* * since pclustersize is variable for big pcluster feature, introduce slab * pools implementation for different pcluster sizes. diff --git a/fs/erofs/zdata.h b/fs/erofs/zdata.h deleted file mode 100644 index f196a729c7e8..000000000000 --- a/fs/erofs/zdata.h +++ /dev/null @@ -1,177 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2018 HUAWEI, Inc. - * https://www.huawei.com/ - */ -#ifndef __EROFS_FS_ZDATA_H -#define __EROFS_FS_ZDATA_H - -#include "internal.h" - -#define Z_EROFS_PCLUSTER_MAX_PAGES (Z_EROFS_PCLUSTER_MAX_SIZE / PAGE_SIZE) -#define Z_EROFS_INLINE_BVECS 2 - -/* - * let's leave a type here in case of introducing - * another tagged pointer later. - */ -typedef void *z_erofs_next_pcluster_t; - -struct z_erofs_bvec { - struct page *page; - int offset; - unsigned int end; -}; - -#define __Z_EROFS_BVSET(name, total) \ -struct name { \ - /* point to the next page which contains the following bvecs */ \ - struct page *nextpage; \ - struct z_erofs_bvec bvec[total]; \ -} -__Z_EROFS_BVSET(z_erofs_bvset,); -__Z_EROFS_BVSET(z_erofs_bvset_inline, Z_EROFS_INLINE_BVECS); - -/* - * Structure fields follow one of the following exclusion rules. - * - * I: Modifiable by initialization/destruction paths and read-only - * for everyone else; - * - * L: Field should be protected by the pcluster lock; - * - * A: Field should be accessed / updated in atomic for parallelized code. - */ -struct z_erofs_pcluster { - struct erofs_workgroup obj; - struct mutex lock; - - /* A: point to next chained pcluster or TAILs */ - z_erofs_next_pcluster_t next; - - /* L: the maximum decompression size of this round */ - unsigned int length; - - /* L: total number of bvecs */ - unsigned int vcnt; - - /* I: page offset of start position of decompression */ - unsigned short pageofs_out; - - /* I: page offset of inline compressed data */ - unsigned short pageofs_in; - - union { - /* L: inline a certain number of bvec for bootstrap */ - struct z_erofs_bvset_inline bvset; - - /* I: can be used to free the pcluster by RCU. */ - struct rcu_head rcu; - }; - - union { - /* I: physical cluster size in pages */ - unsigned short pclusterpages; - - /* I: tailpacking inline compressed size */ - unsigned short tailpacking_size; - }; - - /* I: compression algorithm format */ - unsigned char algorithmformat; - - /* L: whether partial decompression or not */ - bool partial; - - /* L: indicate several pageofs_outs or not */ - bool multibases; - - /* A: compressed bvecs (can be cached or inplaced pages) */ - struct z_erofs_bvec compressed_bvecs[]; -}; - -/* let's avoid the valid 32-bit kernel addresses */ - -/* the chained workgroup has't submitted io (still open) */ -#define Z_EROFS_PCLUSTER_TAIL ((void *)0x5F0ECAFE) -/* the chained workgroup has already submitted io */ -#define Z_EROFS_PCLUSTER_TAIL_CLOSED ((void *)0x5F0EDEAD) - -#define Z_EROFS_PCLUSTER_NIL (NULL) - -struct z_erofs_decompressqueue { - struct super_block *sb; - atomic_t pending_bios; - z_erofs_next_pcluster_t head; - - union { - struct completion done; - struct work_struct work; - } u; - - bool eio, sync; -}; - -static inline bool z_erofs_is_inline_pcluster(struct z_erofs_pcluster *pcl) -{ - return !pcl->obj.index; -} - -static inline unsigned int z_erofs_pclusterpages(struct z_erofs_pcluster *pcl) -{ - if (z_erofs_is_inline_pcluster(pcl)) - return 1; - return pcl->pclusterpages; -} - -/* - * bit 30: I/O error occurred on this page - * bit 0 - 29: remaining parts to complete this page - */ -#define Z_EROFS_PAGE_EIO (1 << 30) - -static inline void z_erofs_onlinepage_init(struct page *page) -{ - union { - atomic_t o; - unsigned long v; - } u = { .o = ATOMIC_INIT(1) }; - - set_page_private(page, u.v); - smp_wmb(); - SetPagePrivate(page); -} - -static inline void z_erofs_onlinepage_split(struct page *page) -{ - atomic_inc((atomic_t *)&page->private); -} - -static inline void z_erofs_page_mark_eio(struct page *page) -{ - int orig; - - do { - orig = atomic_read((atomic_t *)&page->private); - } while (atomic_cmpxchg((atomic_t *)&page->private, orig, - orig | Z_EROFS_PAGE_EIO) != orig); -} - -static inline void z_erofs_onlinepage_endio(struct page *page) -{ - unsigned int v; - - DBG_BUGON(!PagePrivate(page)); - v = atomic_dec_return((atomic_t *)&page->private); - if (!(v & ~Z_EROFS_PAGE_EIO)) { - set_page_private(page, 0); - ClearPagePrivate(page); - if (!(v & Z_EROFS_PAGE_EIO)) - SetPageUptodate(page); - unlock_page(page); - } -} - -#define Z_EROFS_ONSTACK_PAGES 32 - -#endif -- cgit 1.4.1 From d3b39ea24835ac03da1a30f93ae7c05d55a40191 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sat, 27 May 2023 04:14:56 +0800 Subject: erofs: kill hooked chains to avoid loops on deduplicated compressed images [ Upstream commit 967c28b23f6c89bb8eef6a046ea88afe0d7c1029 ] After heavily stressing EROFS with several images which include a hand-crafted image of repeated patterns for more than 46 days, I found two chains could be linked with each other almost simultaneously and form a loop so that the entire loop won't be submitted. As a consequence, the corresponding file pages will remain locked forever. It can be _only_ observed on data-deduplicated compressed images. For example, consider two chains with five pclusters in total: Chain 1: 2->3->4->5 -- The tail pcluster is 5; Chain 2: 5->1->2 -- The tail pcluster is 2. Chain 2 could link to Chain 1 with pcluster 5; and Chain 1 could link to Chain 2 at the same time with pcluster 2. Since hooked chains are all linked locklessly now, I have no idea how to simply avoid the race. Instead, let's avoid hooked chains completely until I could work out a proper way to fix this and end users finally tell us that it's needed to add it back. Actually, this optimization can be found with multi-threaded workloads (especially even more often on deduplicated compressed images), yet I'm not sure about the overall system impacts of not having this compared with implementation complexity. Fixes: 267f2492c8f7 ("erofs: introduce multi-reference pclusters (fully-referenced)") Signed-off-by: Gao Xiang Reviewed-by: Yue Hu Link: https://lore.kernel.org/r/20230526201459.128169-4-hsiangkao@linux.alibaba.com Signed-off-by: Sasha Levin --- fs/erofs/zdata.c | 72 +++++++++----------------------------------------------- 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index aaddb6781465..92b2e4ddb7ce 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -94,11 +94,8 @@ struct z_erofs_pcluster { /* let's avoid the valid 32-bit kernel addresses */ -/* the chained workgroup has't submitted io (still open) */ +/* the end of a chain of pclusters */ #define Z_EROFS_PCLUSTER_TAIL ((void *)0x5F0ECAFE) -/* the chained workgroup has already submitted io */ -#define Z_EROFS_PCLUSTER_TAIL_CLOSED ((void *)0x5F0EDEAD) - #define Z_EROFS_PCLUSTER_NIL (NULL) struct z_erofs_decompressqueue { @@ -375,20 +372,6 @@ int __init z_erofs_init_zip_subsystem(void) enum z_erofs_pclustermode { Z_EROFS_PCLUSTER_INFLIGHT, - /* - * The current pclusters was the tail of an exist chain, in addition - * that the previous processed chained pclusters are all decided to - * be hooked up to it. - * A new chain will be created for the remaining pclusters which are - * not processed yet, so different from Z_EROFS_PCLUSTER_FOLLOWED, - * the next pcluster cannot reuse the whole page safely for inplace I/O - * in the following scenario: - * ________________________________________________________________ - * | tail (partial) page | head (partial) page | - * | (belongs to the next pcl) | (belongs to the current pcl) | - * |_______PCLUSTER_FOLLOWED______|________PCLUSTER_HOOKED__________| - */ - Z_EROFS_PCLUSTER_HOOKED, /* * a weak form of Z_EROFS_PCLUSTER_FOLLOWED, the difference is that it * could be dispatched into bypass queue later due to uptodated managed @@ -406,8 +389,8 @@ enum z_erofs_pclustermode { * ________________________________________________________________ * | tail (partial) page | head (partial) page | * | (of the current cl) | (of the previous collection) | - * | PCLUSTER_FOLLOWED or | | - * |_____PCLUSTER_HOOKED__|___________PCLUSTER_FOLLOWED____________| + * | | | + * |__PCLUSTER_FOLLOWED___|___________PCLUSTER_FOLLOWED____________| * * [ (*) the above page can be used as inplace I/O. ] */ @@ -420,7 +403,7 @@ struct z_erofs_decompress_frontend { struct z_erofs_bvec_iter biter; struct page *candidate_bvpage; - struct z_erofs_pcluster *pcl, *tailpcl; + struct z_erofs_pcluster *pcl; z_erofs_next_pcluster_t owned_head; enum z_erofs_pclustermode mode; @@ -626,19 +609,7 @@ static void z_erofs_try_to_claim_pcluster(struct z_erofs_decompress_frontend *f) return; } - /* - * type 2, link to the end of an existing open chain, be careful - * that its submission is controlled by the original attached chain. - */ - if (*owned_head != &pcl->next && pcl != f->tailpcl && - cmpxchg(&pcl->next, Z_EROFS_PCLUSTER_TAIL, - *owned_head) == Z_EROFS_PCLUSTER_TAIL) { - *owned_head = Z_EROFS_PCLUSTER_TAIL; - f->mode = Z_EROFS_PCLUSTER_HOOKED; - f->tailpcl = NULL; - return; - } - /* type 3, it belongs to a chain, but it isn't the end of the chain */ + /* type 2, it belongs to an ongoing chain */ f->mode = Z_EROFS_PCLUSTER_INFLIGHT; } @@ -699,9 +670,6 @@ static int z_erofs_register_pcluster(struct z_erofs_decompress_frontend *fe) goto err_out; } } - /* used to check tail merging loop due to corrupted images */ - if (fe->owned_head == Z_EROFS_PCLUSTER_TAIL) - fe->tailpcl = pcl; fe->owned_head = &pcl->next; fe->pcl = pcl; return 0; @@ -722,7 +690,6 @@ static int z_erofs_collector_begin(struct z_erofs_decompress_frontend *fe) /* must be Z_EROFS_PCLUSTER_TAIL or pointed to previous pcluster */ DBG_BUGON(fe->owned_head == Z_EROFS_PCLUSTER_NIL); - DBG_BUGON(fe->owned_head == Z_EROFS_PCLUSTER_TAIL_CLOSED); if (!(map->m_flags & EROFS_MAP_META)) { grp = erofs_find_workgroup(fe->inode->i_sb, @@ -741,10 +708,6 @@ static int z_erofs_collector_begin(struct z_erofs_decompress_frontend *fe) if (ret == -EEXIST) { mutex_lock(&fe->pcl->lock); - /* used to check tail merging loop due to corrupted images */ - if (fe->owned_head == Z_EROFS_PCLUSTER_TAIL) - fe->tailpcl = fe->pcl; - z_erofs_try_to_claim_pcluster(fe); } else if (ret) { return ret; @@ -901,8 +864,7 @@ hitted: * those chains are handled asynchronously thus the page cannot be used * for inplace I/O or bvpage (should be processed in a strict order.) */ - tight &= (fe->mode >= Z_EROFS_PCLUSTER_HOOKED && - fe->mode != Z_EROFS_PCLUSTER_FOLLOWED_NOINPLACE); + tight &= (fe->mode > Z_EROFS_PCLUSTER_FOLLOWED_NOINPLACE); cur = end - min_t(unsigned int, offset + end - map->m_la, end); if (!(map->m_flags & EROFS_MAP_MAPPED)) { @@ -1283,10 +1245,7 @@ static void z_erofs_decompress_queue(const struct z_erofs_decompressqueue *io, }; z_erofs_next_pcluster_t owned = io->head; - while (owned != Z_EROFS_PCLUSTER_TAIL_CLOSED) { - /* impossible that 'owned' equals Z_EROFS_WORK_TPTR_TAIL */ - DBG_BUGON(owned == Z_EROFS_PCLUSTER_TAIL); - /* impossible that 'owned' equals Z_EROFS_PCLUSTER_NIL */ + while (owned != Z_EROFS_PCLUSTER_TAIL) { DBG_BUGON(owned == Z_EROFS_PCLUSTER_NIL); be.pcl = container_of(owned, struct z_erofs_pcluster, next); @@ -1303,7 +1262,7 @@ static void z_erofs_decompressqueue_work(struct work_struct *work) container_of(work, struct z_erofs_decompressqueue, u.work); struct page *pagepool = NULL; - DBG_BUGON(bgq->head == Z_EROFS_PCLUSTER_TAIL_CLOSED); + DBG_BUGON(bgq->head == Z_EROFS_PCLUSTER_TAIL); z_erofs_decompress_queue(bgq, &pagepool); erofs_release_pages(&pagepool); @@ -1465,7 +1424,7 @@ fg_out: q->sync = true; } q->sb = sb; - q->head = Z_EROFS_PCLUSTER_TAIL_CLOSED; + q->head = Z_EROFS_PCLUSTER_TAIL; return q; } @@ -1483,11 +1442,7 @@ static void move_to_bypass_jobqueue(struct z_erofs_pcluster *pcl, z_erofs_next_pcluster_t *const submit_qtail = qtail[JQ_SUBMIT]; z_erofs_next_pcluster_t *const bypass_qtail = qtail[JQ_BYPASS]; - DBG_BUGON(owned_head == Z_EROFS_PCLUSTER_TAIL_CLOSED); - if (owned_head == Z_EROFS_PCLUSTER_TAIL) - owned_head = Z_EROFS_PCLUSTER_TAIL_CLOSED; - - WRITE_ONCE(pcl->next, Z_EROFS_PCLUSTER_TAIL_CLOSED); + WRITE_ONCE(pcl->next, Z_EROFS_PCLUSTER_TAIL); WRITE_ONCE(*submit_qtail, owned_head); WRITE_ONCE(*bypass_qtail, &pcl->next); @@ -1558,15 +1513,10 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f, unsigned int i = 0; bool bypass = true; - /* no possible 'owned_head' equals the following */ - DBG_BUGON(owned_head == Z_EROFS_PCLUSTER_TAIL_CLOSED); DBG_BUGON(owned_head == Z_EROFS_PCLUSTER_NIL); - pcl = container_of(owned_head, struct z_erofs_pcluster, next); + owned_head = READ_ONCE(pcl->next); - /* close the main owned chain at first */ - owned_head = cmpxchg(&pcl->next, Z_EROFS_PCLUSTER_TAIL, - Z_EROFS_PCLUSTER_TAIL_CLOSED); if (z_erofs_is_inline_pcluster(pcl)) { move_to_bypass_jobqueue(pcl, qtail, owned_head); continue; -- cgit 1.4.1 From c0df91653586e95b87790ccc40aae5af10350292 Mon Sep 17 00:00:00 2001 From: Shawn Wang Date: Mon, 15 May 2023 14:04:48 +0800 Subject: x86/resctrl: Only show tasks' pid in current pid namespace [ Upstream commit 2997d94b5dd0e8b10076f5e0b6f18410c73e28bd ] When writing a task id to the "tasks" file in an rdtgroup, rdtgroup_tasks_write() treats the pid as a number in the current pid namespace. But when reading the "tasks" file, rdtgroup_tasks_show() shows the list of global pids from the init namespace, which is confusing and incorrect. To be more robust, let the "tasks" file only show pids in the current pid namespace. Fixes: e02737d5b826 ("x86/intel_rdt: Add tasks files") Signed-off-by: Shawn Wang Signed-off-by: Borislav Petkov (AMD) Acked-by: Reinette Chatre Acked-by: Fenghua Yu Tested-by: Reinette Chatre Link: https://lore.kernel.org/all/20230116071246.97717-1-shawnwang@linux.alibaba.com/ Signed-off-by: Sasha Levin --- arch/x86/kernel/cpu/resctrl/rdtgroup.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/rdtgroup.c b/arch/x86/kernel/cpu/resctrl/rdtgroup.c index c7f1c7cb1963..15ee89ce8c68 100644 --- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c +++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c @@ -731,11 +731,15 @@ unlock: static void show_rdt_tasks(struct rdtgroup *r, struct seq_file *s) { struct task_struct *p, *t; + pid_t pid; rcu_read_lock(); for_each_process_thread(p, t) { - if (is_closid_match(t, r) || is_rmid_match(t, r)) - seq_printf(s, "%d\n", t->pid); + if (is_closid_match(t, r) || is_rmid_match(t, r)) { + pid = task_pid_vnr(t); + if (pid) + seq_printf(s, "%d\n", pid); + } } rcu_read_unlock(); } -- cgit 1.4.1 From 8ceeb3fc86a83700bb1585c189006080a47e8506 Mon Sep 17 00:00:00 2001 From: Li Nan Date: Sat, 27 May 2023 17:19:04 +0800 Subject: blk-iocost: use spin_lock_irqsave in adjust_inuse_and_calc_cost [ Upstream commit 8d211554679d0b23702bd32ba04aeac0c1c4f660 ] adjust_inuse_and_calc_cost() use spin_lock_irq() and IRQ will be enabled when unlock. DEADLOCK might happen if we have held other locks and disabled IRQ before invoking it. Fix it by using spin_lock_irqsave() instead, which can keep IRQ state consistent with before when unlock. ================================ WARNING: inconsistent lock state 5.10.0-02758-g8e5f91fd772f #26 Not tainted -------------------------------- inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. kworker/2:3/388 [HC0[0]:SC0[0]:HE0:SE1] takes: ffff888118c00c28 (&bfqd->lock){?.-.}-{2:2}, at: spin_lock_irq ffff888118c00c28 (&bfqd->lock){?.-.}-{2:2}, at: bfq_bio_merge+0x141/0x390 {IN-HARDIRQ-W} state was registered at: __lock_acquire+0x3d7/0x1070 lock_acquire+0x197/0x4a0 __raw_spin_lock_irqsave _raw_spin_lock_irqsave+0x3b/0x60 bfq_idle_slice_timer_body bfq_idle_slice_timer+0x53/0x1d0 __run_hrtimer+0x477/0xa70 __hrtimer_run_queues+0x1c6/0x2d0 hrtimer_interrupt+0x302/0x9e0 local_apic_timer_interrupt __sysvec_apic_timer_interrupt+0xfd/0x420 run_sysvec_on_irqstack_cond sysvec_apic_timer_interrupt+0x46/0xa0 asm_sysvec_apic_timer_interrupt+0x12/0x20 irq event stamp: 837522 hardirqs last enabled at (837521): [] __raw_spin_unlock_irqrestore hardirqs last enabled at (837521): [] _raw_spin_unlock_irqrestore+0x3d/0x40 hardirqs last disabled at (837522): [] __raw_spin_lock_irq hardirqs last disabled at (837522): [] _raw_spin_lock_irq+0x43/0x50 softirqs last enabled at (835852): [] __do_softirq+0x558/0x8ec softirqs last disabled at (835845): [] asm_call_irq_on_stack+0xf/0x20 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&bfqd->lock); lock(&bfqd->lock); *** DEADLOCK *** 3 locks held by kworker/2:3/388: #0: ffff888107af0f38 ((wq_completion)kthrotld){+.+.}-{0:0}, at: process_one_work+0x742/0x13f0 #1: ffff8881176bfdd8 ((work_completion)(&td->dispatch_work)){+.+.}-{0:0}, at: process_one_work+0x777/0x13f0 #2: ffff888118c00c28 (&bfqd->lock){?.-.}-{2:2}, at: spin_lock_irq #2: ffff888118c00c28 (&bfqd->lock){?.-.}-{2:2}, at: bfq_bio_merge+0x141/0x390 stack backtrace: CPU: 2 PID: 388 Comm: kworker/2:3 Not tainted 5.10.0-02758-g8e5f91fd772f #26 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a1990b-prebuilt.qemu.org 04/01/2014 Workqueue: kthrotld blk_throtl_dispatch_work_fn Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x107/0x167 print_usage_bug valid_state mark_lock_irq.cold+0x32/0x3a mark_lock+0x693/0xbc0 mark_held_locks+0x9e/0xe0 __trace_hardirqs_on_caller lockdep_hardirqs_on_prepare.part.0+0x151/0x360 trace_hardirqs_on+0x5b/0x180 __raw_spin_unlock_irq _raw_spin_unlock_irq+0x24/0x40 spin_unlock_irq adjust_inuse_and_calc_cost+0x4fb/0x970 ioc_rqos_merge+0x277/0x740 __rq_qos_merge+0x62/0xb0 rq_qos_merge bio_attempt_back_merge+0x12c/0x4a0 blk_mq_sched_try_merge+0x1b6/0x4d0 bfq_bio_merge+0x24a/0x390 __blk_mq_sched_bio_merge+0xa6/0x460 blk_mq_sched_bio_merge blk_mq_submit_bio+0x2e7/0x1ee0 __submit_bio_noacct_mq+0x175/0x3b0 submit_bio_noacct+0x1fb/0x270 blk_throtl_dispatch_work_fn+0x1ef/0x2b0 process_one_work+0x83e/0x13f0 process_scheduled_works worker_thread+0x7e3/0xd80 kthread+0x353/0x470 ret_from_fork+0x1f/0x30 Fixes: b0853ab4a238 ("blk-iocost: revamp in-period donation snapbacks") Signed-off-by: Li Nan Acked-by: Tejun Heo Reviewed-by: Yu Kuai Link: https://lore.kernel.org/r/20230527091904.3001833-1-linan666@huaweicloud.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/blk-iocost.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/block/blk-iocost.c b/block/blk-iocost.c index 00d59d2288f0..7dd6a33e1d6a 100644 --- a/block/blk-iocost.c +++ b/block/blk-iocost.c @@ -2437,6 +2437,7 @@ static u64 adjust_inuse_and_calc_cost(struct ioc_gq *iocg, u64 vtime, u32 hwi, adj_step; s64 margin; u64 cost, new_inuse; + unsigned long flags; current_hweight(iocg, NULL, &hwi); old_hwi = hwi; @@ -2455,11 +2456,11 @@ static u64 adjust_inuse_and_calc_cost(struct ioc_gq *iocg, u64 vtime, iocg->inuse == iocg->active) return cost; - spin_lock_irq(&ioc->lock); + spin_lock_irqsave(&ioc->lock, flags); /* we own inuse only when @iocg is in the normal active state */ if (iocg->abs_vdebt || list_empty(&iocg->active_list)) { - spin_unlock_irq(&ioc->lock); + spin_unlock_irqrestore(&ioc->lock, flags); return cost; } @@ -2480,7 +2481,7 @@ static u64 adjust_inuse_and_calc_cost(struct ioc_gq *iocg, u64 vtime, } while (time_after64(vtime + cost, now->vnow) && iocg->inuse != iocg->active); - spin_unlock_irq(&ioc->lock); + spin_unlock_irqrestore(&ioc->lock, flags); TRACE_IOCG_PATH(inuse_adjust, iocg, now, old_inuse, iocg->inuse, old_hwi, hwi); -- cgit 1.4.1 From 7ca5e95f2a3eb2032e6cb9d1087b6a442ce4a8fd Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Tue, 6 Jun 2023 09:51:22 -0500 Subject: x86/sev: Fix calculation of end address based on number of pages [ Upstream commit 5dee19b6b2b194216919b99a1f5af2949a754016 ] When calculating an end address based on an unsigned int number of pages, any value greater than or equal to 0x100000 that is shift PAGE_SHIFT bits results in a 0 value, resulting in an invalid end address. Change the number of pages variable in various routines from an unsigned int to an unsigned long to calculate the end address correctly. Fixes: 5e5ccff60a29 ("x86/sev: Add helper for validating pages in early enc attribute changes") Fixes: dc3f3d2474b8 ("x86/mm: Validate memory when changing the C-bit") Signed-off-by: Tom Lendacky Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/r/6a6e4eea0e1414402bac747744984fa4e9c01bb6.1686063086.git.thomas.lendacky@amd.com Signed-off-by: Sasha Levin --- arch/x86/include/asm/sev.h | 16 ++++++++-------- arch/x86/kernel/sev.c | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h index ebc271bb6d8e..a0a58c4122ec 100644 --- a/arch/x86/include/asm/sev.h +++ b/arch/x86/include/asm/sev.h @@ -187,12 +187,12 @@ static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate) } void setup_ghcb(void); void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, - unsigned int npages); + unsigned long npages); void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, - unsigned int npages); + unsigned long npages); void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op); -void snp_set_memory_shared(unsigned long vaddr, unsigned int npages); -void snp_set_memory_private(unsigned long vaddr, unsigned int npages); +void snp_set_memory_shared(unsigned long vaddr, unsigned long npages); +void snp_set_memory_private(unsigned long vaddr, unsigned long npages); void snp_set_wakeup_secondary_cpu(void); bool snp_init(struct boot_params *bp); void __init __noreturn snp_abort(void); @@ -207,12 +207,12 @@ static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate) static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long attrs) { return 0; } static inline void setup_ghcb(void) { } static inline void __init -early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned int npages) { } +early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned long npages) { } static inline void __init -early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, unsigned int npages) { } +early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, unsigned long npages) { } static inline void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op) { } -static inline void snp_set_memory_shared(unsigned long vaddr, unsigned int npages) { } -static inline void snp_set_memory_private(unsigned long vaddr, unsigned int npages) { } +static inline void snp_set_memory_shared(unsigned long vaddr, unsigned long npages) { } +static inline void snp_set_memory_private(unsigned long vaddr, unsigned long npages) { } static inline void snp_set_wakeup_secondary_cpu(void) { } static inline bool snp_init(struct boot_params *bp) { return false; } static inline void snp_abort(void) { } diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c index c680ac6342bb..afda719dd725 100644 --- a/arch/x86/kernel/sev.c +++ b/arch/x86/kernel/sev.c @@ -643,7 +643,7 @@ static u64 __init get_jump_table_addr(void) return ret; } -static void pvalidate_pages(unsigned long vaddr, unsigned int npages, bool validate) +static void pvalidate_pages(unsigned long vaddr, unsigned long npages, bool validate) { unsigned long vaddr_end; int rc; @@ -660,7 +660,7 @@ static void pvalidate_pages(unsigned long vaddr, unsigned int npages, bool valid } } -static void __init early_set_pages_state(unsigned long paddr, unsigned int npages, enum psc_op op) +static void __init early_set_pages_state(unsigned long paddr, unsigned long npages, enum psc_op op) { unsigned long paddr_end; u64 val; @@ -699,7 +699,7 @@ e_term: } void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, - unsigned int npages) + unsigned long npages) { /* * This can be invoked in early boot while running identity mapped, so @@ -721,7 +721,7 @@ void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long padd } void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, - unsigned int npages) + unsigned long npages) { /* * This can be invoked in early boot while running identity mapped, so @@ -877,7 +877,7 @@ static void __set_pages_state(struct snp_psc_desc *data, unsigned long vaddr, sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC); } -static void set_pages_state(unsigned long vaddr, unsigned int npages, int op) +static void set_pages_state(unsigned long vaddr, unsigned long npages, int op) { unsigned long vaddr_end, next_vaddr; struct snp_psc_desc *desc; @@ -902,7 +902,7 @@ static void set_pages_state(unsigned long vaddr, unsigned int npages, int op) kfree(desc); } -void snp_set_memory_shared(unsigned long vaddr, unsigned int npages) +void snp_set_memory_shared(unsigned long vaddr, unsigned long npages) { if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) return; @@ -912,7 +912,7 @@ void snp_set_memory_shared(unsigned long vaddr, unsigned int npages) set_pages_state(vaddr, npages, SNP_PAGE_STATE_SHARED); } -void snp_set_memory_private(unsigned long vaddr, unsigned int npages) +void snp_set_memory_private(unsigned long vaddr, unsigned long npages) { if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) return; -- cgit 1.4.1 From c2a0eb3b2017bd1ab8bc68f9296619036a3d06df Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 17 Jan 2023 18:13:56 +0100 Subject: virt: sevguest: Add CONFIG_CRYPTO dependency [ Upstream commit 84b9b44b99780d35fe72ac63c4724f158771e898 ] This driver fails to link when CRYPTO is disabled, or in a loadable module: WARNING: unmet direct dependencies detected for CRYPTO_GCM WARNING: unmet direct dependencies detected for CRYPTO_AEAD2 Depends on [m]: CRYPTO [=m] Selected by [y]: - SEV_GUEST [=y] && VIRT_DRIVERS [=y] && AMD_MEM_ENCRYPT [=y] x86_64-linux-ld: crypto/aead.o: in function `crypto_register_aeads': Fixes: fce96cf04430 ("virt: Add SEV-SNP guest driver") Signed-off-by: Arnd Bergmann Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/r/20230117171416.2715125-1-arnd@kernel.org Signed-off-by: Sasha Levin --- drivers/virt/coco/sev-guest/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/virt/coco/sev-guest/Kconfig b/drivers/virt/coco/sev-guest/Kconfig index f9db0799ae67..da2d7ca531f0 100644 --- a/drivers/virt/coco/sev-guest/Kconfig +++ b/drivers/virt/coco/sev-guest/Kconfig @@ -2,6 +2,7 @@ config SEV_GUEST tristate "AMD SEV Guest driver" default m depends on AMD_MEM_ENCRYPT + select CRYPTO select CRYPTO_AEAD2 select CRYPTO_GCM help -- cgit 1.4.1 From 931bd6758bcc0f3d27d0492456798afae028e52f Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Sat, 10 Jun 2023 10:30:43 +0800 Subject: blk-mq: fix potential io hang by wrong 'wake_batch' [ Upstream commit 4f1731df60f9033669f024d06ae26a6301260b55 ] In __blk_mq_tag_busy/idle(), updating 'active_queues' and calculating 'wake_batch' is not atomic: t1: t2: _blk_mq_tag_busy blk_mq_tag_busy inc active_queues // assume 1->2 inc active_queues // 2 -> 3 blk_mq_update_wake_batch // calculate based on 3 blk_mq_update_wake_batch /* calculate based on 2, while active_queues is actually 3. */ Fix this problem by protecting them wih 'tags->lock', this is not a hot path, so performance should not be concerned. And now that all writers are inside the lock, switch 'actives_queues' from atomic to unsigned int. Fixes: 180dccb0dba4 ("blk-mq: fix tag_get wait task can't be awakened") Signed-off-by: Yu Kuai Reviewed-by: Jan Kara Link: https://lore.kernel.org/r/20230610023043.2559121-1-yukuai1@huaweicloud.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/blk-mq-debugfs.c | 2 +- block/blk-mq-tag.c | 15 ++++++++++----- block/blk-mq.h | 3 +-- include/linux/blk-mq.h | 3 +-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index bd942341b638..7675e663df36 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -427,7 +427,7 @@ static void blk_mq_debugfs_tags_show(struct seq_file *m, seq_printf(m, "nr_tags=%u\n", tags->nr_tags); seq_printf(m, "nr_reserved_tags=%u\n", tags->nr_reserved_tags); seq_printf(m, "active_queues=%d\n", - atomic_read(&tags->active_queues)); + READ_ONCE(tags->active_queues)); seq_puts(m, "\nbitmap_tags:\n"); sbitmap_queue_show(&tags->bitmap_tags, m); diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index a80d7c62bdfe..100889c276c3 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -40,6 +40,7 @@ static void blk_mq_update_wake_batch(struct blk_mq_tags *tags, void __blk_mq_tag_busy(struct blk_mq_hw_ctx *hctx) { unsigned int users; + struct blk_mq_tags *tags = hctx->tags; /* * calling test_bit() prior to test_and_set_bit() is intentional, @@ -57,9 +58,11 @@ void __blk_mq_tag_busy(struct blk_mq_hw_ctx *hctx) return; } - users = atomic_inc_return(&hctx->tags->active_queues); - - blk_mq_update_wake_batch(hctx->tags, users); + spin_lock_irq(&tags->lock); + users = tags->active_queues + 1; + WRITE_ONCE(tags->active_queues, users); + blk_mq_update_wake_batch(tags, users); + spin_unlock_irq(&tags->lock); } /* @@ -92,9 +95,11 @@ void __blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) return; } - users = atomic_dec_return(&tags->active_queues); - + spin_lock_irq(&tags->lock); + users = tags->active_queues - 1; + WRITE_ONCE(tags->active_queues, users); blk_mq_update_wake_batch(tags, users); + spin_unlock_irq(&tags->lock); blk_mq_tag_wakeup_all(tags, false); } diff --git a/block/blk-mq.h b/block/blk-mq.h index 0b2870839cdd..c6eca452ea2a 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -362,8 +362,7 @@ static inline bool hctx_may_queue(struct blk_mq_hw_ctx *hctx, return true; } - users = atomic_read(&hctx->tags->active_queues); - + users = READ_ONCE(hctx->tags->active_queues); if (!users) return true; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index a9764cbf7f8d..e4f676e1042b 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -745,8 +745,7 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q, struct blk_mq_tags { unsigned int nr_tags; unsigned int nr_reserved_tags; - - atomic_t active_queues; + unsigned int active_queues; struct sbitmap_queue bitmap_tags; struct sbitmap_queue breserved_tags; -- cgit 1.4.1 From ce16368280c95db2289cd09c29aeda1ec95ca8f2 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 3 Jun 2023 07:14:14 +1000 Subject: lockd: drop inappropriate svc_get() from locked_get() [ Upstream commit 665e89ab7c5af1f2d260834c861a74b01a30f95f ] The below-mentioned patch was intended to simplify refcounting on the svc_serv used by locked. The goal was to only ever have a single reference from the single thread. To that end we dropped a call to lockd_start_svc() (except when creating thread) which would take a reference, and dropped the svc_put(serv) that would drop that reference. Unfortunately we didn't also remove the svc_get() from lockd_create_svc() in the case where the svc_serv already existed. So after the patch: - on the first call the svc_serv was allocated and the one reference was given to the thread, so there are no extra references - on subsequent calls svc_get() was called so there is now an extra reference. This is clearly not consistent. The inconsistency is also clear in the current code in lockd_get() takes *two* references, one on nlmsvc_serv and one by incrementing nlmsvc_users. This clearly does not match lockd_put(). So: drop that svc_get() from lockd_get() (which used to be in lockd_create_svc(). Reported-by: Ido Schimmel Closes: https://lore.kernel.org/linux-nfs/ZHsI%2FH16VX9kJQX1@shredder/T/#u Fixes: b73a2972041b ("lockd: move lockd_start_svc() call into lockd_create_svc()") Signed-off-by: NeilBrown Tested-by: Ido Schimmel Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/lockd/svc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 59ef8a1f843f..5579e67da17d 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -355,7 +355,6 @@ static int lockd_get(void) int error; if (nlmsvc_serv) { - svc_get(nlmsvc_serv); nlmsvc_users++; return 0; } -- cgit 1.4.1 From 3f6c98889780c9188afdce2e990bd030e60aaf51 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sun, 13 Nov 2022 13:24:05 +0200 Subject: nvme-auth: rename __nvme_auth_[reset|free] to nvme_auth[reset|free]_dhchap [ Upstream commit 0a7ce375f83f4ade7c2a835444093b6870fb8257 ] nvme_auth_[reset|free] operate on the controller while __nvme_auth_[reset|free] operate on a chap struct (which maps to a queue context). Rename it for clarity. Signed-off-by: Sagi Grimberg Reviewed-by: Chaitanya Kulkarni Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig Stable-dep-of: a836ca33c5b0 ("nvme-core: fix memory leak in dhchap_secret_store") Signed-off-by: Sasha Levin --- drivers/nvme/host/auth.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index c8a6db7c4498..d45333268fcf 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -654,7 +654,7 @@ gen_sesskey: return 0; } -static void __nvme_auth_reset(struct nvme_dhchap_queue_context *chap) +static void nvme_auth_reset_dhchap(struct nvme_dhchap_queue_context *chap) { kfree_sensitive(chap->host_response); chap->host_response = NULL; @@ -676,9 +676,9 @@ static void __nvme_auth_reset(struct nvme_dhchap_queue_context *chap) memset(chap->c2, 0, sizeof(chap->c2)); } -static void __nvme_auth_free(struct nvme_dhchap_queue_context *chap) +static void nvme_auth_free_dhchap(struct nvme_dhchap_queue_context *chap) { - __nvme_auth_reset(chap); + nvme_auth_reset_dhchap(chap); if (chap->shash_tfm) crypto_free_shash(chap->shash_tfm); if (chap->dh_tfm) @@ -868,7 +868,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) dev_dbg(ctrl->device, "qid %d: re-using context\n", qid); mutex_unlock(&ctrl->dhchap_auth_mutex); flush_work(&chap->auth_work); - __nvme_auth_reset(chap); + nvme_auth_reset_dhchap(chap); queue_work(nvme_wq, &chap->auth_work); return 0; } @@ -928,7 +928,7 @@ void nvme_auth_reset(struct nvme_ctrl *ctrl) list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) { mutex_unlock(&ctrl->dhchap_auth_mutex); flush_work(&chap->auth_work); - __nvme_auth_reset(chap); + nvme_auth_reset_dhchap(chap); } mutex_unlock(&ctrl->dhchap_auth_mutex); } @@ -1002,7 +1002,7 @@ void nvme_auth_free(struct nvme_ctrl *ctrl) list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry) { list_del_init(&chap->entry); flush_work(&chap->auth_work); - __nvme_auth_free(chap); + nvme_auth_free_dhchap(chap); } mutex_unlock(&ctrl->dhchap_auth_mutex); if (ctrl->host_key) { -- cgit 1.4.1 From 9de0a1dfe3e67fcb5ab92113d996034110cf0169 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sun, 13 Nov 2022 13:24:06 +0200 Subject: nvme-auth: rename authentication work elements [ Upstream commit 0c999e69c40a87285f910c400b550fad866e99d0 ] Use nvme_ctrl_auth_work and nvme_queue_auth_work for better readability. Signed-off-by: Sagi Grimberg Reviewed-by: Chaitanya Kulkarni Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig Stable-dep-of: a836ca33c5b0 ("nvme-core: fix memory leak in dhchap_secret_store") Signed-off-by: Sasha Levin --- drivers/nvme/host/auth.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index d45333268fcf..e3e801e2b78d 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -691,7 +691,7 @@ static void nvme_auth_free_dhchap(struct nvme_dhchap_queue_context *chap) kfree(chap); } -static void __nvme_auth_work(struct work_struct *work) +static void nvme_queue_auth_work(struct work_struct *work) { struct nvme_dhchap_queue_context *chap = container_of(work, struct nvme_dhchap_queue_context, auth_work); @@ -893,7 +893,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) return -ENOMEM; } - INIT_WORK(&chap->auth_work, __nvme_auth_work); + INIT_WORK(&chap->auth_work, nvme_queue_auth_work); list_add(&chap->entry, &ctrl->dhchap_auth_list); mutex_unlock(&ctrl->dhchap_auth_mutex); queue_work(nvme_wq, &chap->auth_work); @@ -934,7 +934,7 @@ void nvme_auth_reset(struct nvme_ctrl *ctrl) } EXPORT_SYMBOL_GPL(nvme_auth_reset); -static void nvme_dhchap_auth_work(struct work_struct *work) +static void nvme_ctrl_auth_work(struct work_struct *work) { struct nvme_ctrl *ctrl = container_of(work, struct nvme_ctrl, dhchap_auth_work); @@ -973,7 +973,7 @@ static void nvme_dhchap_auth_work(struct work_struct *work) void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) { INIT_LIST_HEAD(&ctrl->dhchap_auth_list); - INIT_WORK(&ctrl->dhchap_auth_work, nvme_dhchap_auth_work); + INIT_WORK(&ctrl->dhchap_auth_work, nvme_ctrl_auth_work); mutex_init(&ctrl->dhchap_auth_mutex); if (!ctrl->opts) return; -- cgit 1.4.1 From 3999c850e78a8c69cdd6aac8336e156c8ffdeb4b Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sun, 13 Nov 2022 13:24:07 +0200 Subject: nvme-auth: remove symbol export from nvme_auth_reset [ Upstream commit 100b555bc204fc754108351676297805f5affa49 ] Only the nvme module calls it. Reviewed-by: Hannes Reinecke Signed-off-by: Sagi Grimberg Reviewed-by: Chaitanya Kulkarni Signed-off-by: Christoph Hellwig Stable-dep-of: a836ca33c5b0 ("nvme-core: fix memory leak in dhchap_secret_store") Signed-off-by: Sasha Levin --- drivers/nvme/host/auth.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index e3e801e2b78d..2f823c6b84fd 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -932,7 +932,6 @@ void nvme_auth_reset(struct nvme_ctrl *ctrl) } mutex_unlock(&ctrl->dhchap_auth_mutex); } -EXPORT_SYMBOL_GPL(nvme_auth_reset); static void nvme_ctrl_auth_work(struct work_struct *work) { -- cgit 1.4.1 From 0a220ef9dda6901cf6e8c52ff06f2e10458b1d59 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sun, 13 Nov 2022 13:24:17 +0200 Subject: nvme-auth: no need to reset chap contexts on re-authentication [ Upstream commit e8a420efb637f52c586596283d6fd96f2a7ecb5c ] Now that the chap context is reset upon completion, this is no longer needed. Also remove nvme_auth_reset as no callers are left. Reviewed-by: Hannes Reinecke Signed-off-by: Sagi Grimberg Reviewed-by: Chaitanya Kulkarni Signed-off-by: Christoph Hellwig Stable-dep-of: a836ca33c5b0 ("nvme-core: fix memory leak in dhchap_secret_store") Signed-off-by: Sasha Levin --- drivers/nvme/host/auth.c | 13 ------------- drivers/nvme/host/core.c | 4 ---- drivers/nvme/host/nvme.h | 1 - 3 files changed, 18 deletions(-) diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index 2f823c6b84fd..1a27d7fb4fa9 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -920,19 +920,6 @@ int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid) } EXPORT_SYMBOL_GPL(nvme_auth_wait); -void nvme_auth_reset(struct nvme_ctrl *ctrl) -{ - struct nvme_dhchap_queue_context *chap; - - mutex_lock(&ctrl->dhchap_auth_mutex); - list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) { - mutex_unlock(&ctrl->dhchap_auth_mutex); - flush_work(&chap->auth_work); - nvme_auth_reset_dhchap(chap); - } - mutex_unlock(&ctrl->dhchap_auth_mutex); -} - static void nvme_ctrl_auth_work(struct work_struct *work) { struct nvme_ctrl *ctrl = diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index a7d9b5b42b38..b63511f481a7 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3832,8 +3832,6 @@ static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev, host_key = ctrl->host_key; ctrl->host_key = key; nvme_auth_free_key(host_key); - /* Key has changed; re-authentication with new key */ - nvme_auth_reset(ctrl); } /* Start re-authentication */ dev_info(ctrl->device, "re-authenticating controller\n"); @@ -3886,8 +3884,6 @@ static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev, ctrl_key = ctrl->ctrl_key; ctrl->ctrl_key = key; nvme_auth_free_key(ctrl_key); - /* Key has changed; re-authentication with new key */ - nvme_auth_reset(ctrl); } /* Start re-authentication */ dev_info(ctrl->device, "re-authenticating controller\n"); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 2aa514c3dfa1..5ed771d576c6 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -1032,7 +1032,6 @@ void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl); void nvme_auth_stop(struct nvme_ctrl *ctrl); int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid); int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid); -void nvme_auth_reset(struct nvme_ctrl *ctrl); void nvme_auth_free(struct nvme_ctrl *ctrl); #else static inline void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) {}; -- cgit 1.4.1 From 2e9b141307554521d60fecf6bf1d2edc8dd0181d Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Fri, 28 Apr 2023 00:31:12 -0700 Subject: nvme-core: fix memory leak in dhchap_secret_store [ Upstream commit a836ca33c5b07d34dd5347af9f64d25651d12674 ] Free dhchap_secret in nvme_ctrl_dhchap_secret_store() before we return fix following kmemleack:- unreferenced object 0xffff8886376ea800 (size 64): comm "check", pid 22048, jiffies 4344316705 (age 92.199s) hex dump (first 32 bytes): 44 48 48 43 2d 31 3a 30 30 3a 6e 78 72 35 4b 67 DHHC-1:00:nxr5Kg 75 58 34 75 6f 41 78 73 4a 61 34 63 2f 68 75 4c uX4uoAxsJa4c/huL backtrace: [<0000000030ce5d4b>] __kmalloc+0x4b/0x130 [<000000009be1cdc1>] nvme_ctrl_dhchap_secret_store+0x8f/0x160 [nvme_core] [<00000000ac06c96a>] kernfs_fop_write_iter+0x12b/0x1c0 [<00000000437e7ced>] vfs_write+0x2ba/0x3c0 [<00000000f9491baf>] ksys_write+0x5f/0xe0 [<000000001c46513d>] do_syscall_64+0x3b/0x90 [<00000000ecf348fe>] entry_SYSCALL_64_after_hwframe+0x72/0xdc unreferenced object 0xffff8886376eaf00 (size 64): comm "check", pid 22048, jiffies 4344316736 (age 92.168s) hex dump (first 32 bytes): 44 48 48 43 2d 31 3a 30 30 3a 6e 78 72 35 4b 67 DHHC-1:00:nxr5Kg 75 58 34 75 6f 41 78 73 4a 61 34 63 2f 68 75 4c uX4uoAxsJa4c/huL backtrace: [<0000000030ce5d4b>] __kmalloc+0x4b/0x130 [<000000009be1cdc1>] nvme_ctrl_dhchap_secret_store+0x8f/0x160 [nvme_core] [<00000000ac06c96a>] kernfs_fop_write_iter+0x12b/0x1c0 [<00000000437e7ced>] vfs_write+0x2ba/0x3c0 [<00000000f9491baf>] ksys_write+0x5f/0xe0 [<000000001c46513d>] do_syscall_64+0x3b/0x90 [<00000000ecf348fe>] entry_SYSCALL_64_after_hwframe+0x72/0xdc Fixes: f50fff73d620 ("nvme: implement In-Band authentication") Signed-off-by: Chaitanya Kulkarni Tested-by: Yi Zhang Reviewed-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index b63511f481a7..395616427225 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3825,14 +3825,17 @@ static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev, int ret; ret = nvme_auth_generate_key(dhchap_secret, &key); - if (ret) + if (ret) { + kfree(dhchap_secret); return ret; + } kfree(opts->dhchap_secret); opts->dhchap_secret = dhchap_secret; host_key = ctrl->host_key; ctrl->host_key = key; nvme_auth_free_key(host_key); - } + } else + kfree(dhchap_secret); /* Start re-authentication */ dev_info(ctrl->device, "re-authenticating controller\n"); queue_work(nvme_wq, &ctrl->dhchap_auth_work); -- cgit 1.4.1 From 43d0724d756a13694f612a8a151f835ad6425b93 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Fri, 28 Apr 2023 00:31:13 -0700 Subject: nvme-core: fix memory leak in dhchap_ctrl_secret [ Upstream commit 99c2dcc8ffc24e210a3aa05c204d92f3ef460b05 ] Free dhchap_secret in nvme_ctrl_dhchap_ctrl_secret_store() before we return when nvme_auth_generate_key() returns error. Fixes: f50fff73d620 ("nvme: implement In-Band authentication") Signed-off-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 395616427225..2b07a67958b4 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3880,14 +3880,17 @@ static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev, int ret; ret = nvme_auth_generate_key(dhchap_secret, &key); - if (ret) + if (ret) { + kfree(dhchap_secret); return ret; + } kfree(opts->dhchap_ctrl_secret); opts->dhchap_ctrl_secret = dhchap_secret; ctrl_key = ctrl->ctrl_key; ctrl->ctrl_key = key; nvme_auth_free_key(ctrl_key); - } + } else + kfree(dhchap_secret); /* Start re-authentication */ dev_info(ctrl->device, "re-authenticating controller\n"); queue_work(nvme_wq, &ctrl->dhchap_auth_work); -- cgit 1.4.1 From a584cf03ff8b546e6b1bae4f254223b2fcff6dfd Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sun, 13 Nov 2022 13:24:10 +0200 Subject: nvme-auth: don't ignore key generation failures when initializing ctrl keys [ Upstream commit 193a8c7e5f1a8481841636cec9c185543ec5c759 ] nvme_auth_generate_key can fail, don't ignore it upon initialization. Reviewed-by: Hannes Reinecke Signed-off-by: Sagi Grimberg Reviewed-by: Chaitanya Kulkarni Signed-off-by: Christoph Hellwig Stable-dep-of: 7ed5cf8e6d9b ("nvme-core: fix dev_pm_qos memleak") Signed-off-by: Sasha Levin --- drivers/nvme/host/auth.c | 19 +++++++++++++++---- drivers/nvme/host/core.c | 6 +++++- drivers/nvme/host/nvme.h | 7 +++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index 1a27d7fb4fa9..9dfd3d029305 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -956,15 +956,26 @@ static void nvme_ctrl_auth_work(struct work_struct *work) */ } -void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) +int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) { + int ret; + INIT_LIST_HEAD(&ctrl->dhchap_auth_list); INIT_WORK(&ctrl->dhchap_auth_work, nvme_ctrl_auth_work); mutex_init(&ctrl->dhchap_auth_mutex); if (!ctrl->opts) - return; - nvme_auth_generate_key(ctrl->opts->dhchap_secret, &ctrl->host_key); - nvme_auth_generate_key(ctrl->opts->dhchap_ctrl_secret, &ctrl->ctrl_key); + return 0; + ret = nvme_auth_generate_key(ctrl->opts->dhchap_secret, + &ctrl->host_key); + if (ret) + return ret; + ret = nvme_auth_generate_key(ctrl->opts->dhchap_ctrl_secret, + &ctrl->ctrl_key); + if (ret) { + nvme_auth_free_key(ctrl->host_key); + ctrl->host_key = NULL; + } + return ret; } EXPORT_SYMBOL_GPL(nvme_auth_init_ctrl); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 2b07a67958b4..09ff0d75aaf3 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -5171,9 +5171,13 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, nvme_fault_inject_init(&ctrl->fault_inject, dev_name(ctrl->device)); nvme_mpath_init_ctrl(ctrl); - nvme_auth_init_ctrl(ctrl); + ret = nvme_auth_init_ctrl(ctrl); + if (ret) + goto out_free_cdev; return 0; +out_free_cdev: + cdev_device_del(&ctrl->cdev, ctrl->device); out_free_name: nvme_put_ctrl(ctrl); kfree_const(ctrl->device->kobj.name); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 5ed771d576c6..69f9e69208f6 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -1028,13 +1028,16 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl) } #ifdef CONFIG_NVME_AUTH -void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl); +int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl); void nvme_auth_stop(struct nvme_ctrl *ctrl); int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid); int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid); void nvme_auth_free(struct nvme_ctrl *ctrl); #else -static inline void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) {}; +static inline int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) +{ + return 0; +} static inline void nvme_auth_stop(struct nvme_ctrl *ctrl) {}; static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) { -- cgit 1.4.1 From bf3c2caab9d125e864aefa26487d521d3d471a60 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Fri, 28 Apr 2023 00:31:14 -0700 Subject: nvme-core: add missing fault-injection cleanup [ Upstream commit 3a12a0b868a512fcada564699d00f5e652c0998c ] Add missing fault-injection cleanup in nvme_init_ctrl() in the error unwind path that also fixes following message for blktests:- linux-block (for-next) # grep debugfs debugfs-err.log [ 147.853464] debugfs: Directory 'nvme1' with parent '/' already present! [ 147.853973] nvme1: failed to create debugfs attr [ 148.802490] debugfs: Directory 'nvme1' with parent '/' already present! [ 148.803244] nvme1: failed to create debugfs attr [ 148.877304] debugfs: Directory 'nvme1' with parent '/' already present! [ 148.877775] nvme1: failed to create debugfs attr [ 149.816652] debugfs: Directory 'nvme1' with parent '/' already present! [ 149.818011] nvme1: failed to create debugfs attr Signed-off-by: Chaitanya Kulkarni Tested-by: Yi Zhang Reviewed-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Signed-off-by: Keith Busch Stable-dep-of: 7ed5cf8e6d9b ("nvme-core: fix dev_pm_qos memleak") Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 09ff0d75aaf3..8414e1a03646 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -5177,6 +5177,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, return 0; out_free_cdev: + nvme_fault_inject_fini(&ctrl->fault_inject); cdev_device_del(&ctrl->cdev, ctrl->device); out_free_name: nvme_put_ctrl(ctrl); -- cgit 1.4.1 From e1379e067b9485e5af03399fe3f0d39bccb023ad Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Fri, 28 Apr 2023 00:31:15 -0700 Subject: nvme-core: fix dev_pm_qos memleak [ Upstream commit 7ed5cf8e6d9bfb6a78d0471317edff14f0f2b4dd ] Call dev_pm_qos_hide_latency_tolerance() in the error unwind patch to avoid following kmemleak:- blktests (master) # kmemleak-clear; ./check nvme/044; blktests (master) # kmemleak-scan ; kmemleak-show nvme/044 (Test bi-directional authentication) [passed] runtime 2.111s ... 2.124s unreferenced object 0xffff888110c46240 (size 96): comm "nvme", pid 33461, jiffies 4345365353 (age 75.586s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<0000000069ac2cec>] kmalloc_trace+0x25/0x90 [<000000006acc66d5>] dev_pm_qos_update_user_latency_tolerance+0x6f/0x100 [<00000000cc376ea7>] nvme_init_ctrl+0x38e/0x410 [nvme_core] [<000000007df61b4b>] 0xffffffffc05e88b3 [<00000000d152b985>] 0xffffffffc05744cb [<00000000f04a4041>] vfs_write+0xc5/0x3c0 [<00000000f9491baf>] ksys_write+0x5f/0xe0 [<000000001c46513d>] do_syscall_64+0x3b/0x90 [<00000000ecf348fe>] entry_SYSCALL_64_after_hwframe+0x72/0xdc Link: https://lore.kernel.org/linux-nvme/CAHj4cs-nDaKzMx2txO4dbE+Mz9ePwLtU0e3egz+StmzOUgWUrA@mail.gmail.com/ Fixes: f50fff73d620 ("nvme: implement In-Band authentication") Signed-off-by: Chaitanya Kulkarni Tested-by: Yi Zhang Reviewed-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 8414e1a03646..560ce2f05a96 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -5178,6 +5178,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, return 0; out_free_cdev: nvme_fault_inject_fini(&ctrl->fault_inject); + dev_pm_qos_hide_latency_tolerance(ctrl->device); cdev_device_del(&ctrl->cdev, ctrl->device); out_free_name: nvme_put_ctrl(ctrl); -- cgit 1.4.1 From be1a3ec63a840cc9e59a033acf154f56255699a1 Mon Sep 17 00:00:00 2001 From: Li Nan Date: Mon, 15 May 2023 21:48:05 +0800 Subject: md/raid10: check slab-out-of-bounds in md_bitmap_get_counter [ Upstream commit 301867b1c16805aebbc306aafa6ecdc68b73c7e5 ] If we write a large number to md/bitmap_set_bits, md_bitmap_checkpage() will return -EINVAL because 'page >= bitmap->pages', but the return value was not checked immediately in md_bitmap_get_counter() in order to set *blocks value and slab-out-of-bounds occurs. Move check of 'page >= bitmap->pages' to md_bitmap_get_counter() and return directly if true. Fixes: ef4256733506 ("md/bitmap: optimise scanning of empty bitmaps.") Signed-off-by: Li Nan Reviewed-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230515134808.3936750-2-linan666@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/md-bitmap.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index e7cc6ba1b657..9640741e8d36 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -54,14 +54,7 @@ __acquires(bitmap->lock) { unsigned char *mappage; - if (page >= bitmap->pages) { - /* This can happen if bitmap_start_sync goes beyond - * End-of-device while looking for a whole page. - * It is harmless. - */ - return -EINVAL; - } - + WARN_ON_ONCE(page >= bitmap->pages); if (bitmap->bp[page].hijacked) /* it's hijacked, don't try to alloc */ return 0; @@ -1364,6 +1357,14 @@ __acquires(bitmap->lock) sector_t csize; int err; + if (page >= bitmap->pages) { + /* + * This can happen if bitmap_start_sync goes beyond + * End-of-device while looking for a whole page or + * user set a huge number to sysfs bitmap_set_bits. + */ + return NULL; + } err = md_bitmap_checkpage(bitmap, page, create, 0); if (bitmap->bp[page].hijacked || -- cgit 1.4.1 From b5cb16d31b8a6bacd6848ba347ee17221fa401b9 Mon Sep 17 00:00:00 2001 From: Li Nan Date: Mon, 22 May 2023 15:25:33 +0800 Subject: md/raid10: fix overflow of md/safe_mode_delay [ Upstream commit 6beb489b2eed25978523f379a605073f99240c50 ] There is no input check when echo md/safe_mode_delay in safe_delay_store(). And msec might also overflow when HZ < 1000 in safe_delay_show(), Fix it by checking overflow in safe_delay_store() and use unsigned long conversion in safe_delay_show(). Fixes: 72e02075a33f ("md: factor out parsing of fixed-point numbers") Signed-off-by: Li Nan Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230522072535.1523740-2-linan666@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/md.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index bb73a541bb19..0c97531d3503 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3830,8 +3830,9 @@ int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale) static ssize_t safe_delay_show(struct mddev *mddev, char *page) { - int msec = (mddev->safemode_delay*1000)/HZ; - return sprintf(page, "%d.%03d\n", msec/1000, msec%1000); + unsigned int msec = ((unsigned long)mddev->safemode_delay*1000)/HZ; + + return sprintf(page, "%u.%03u\n", msec/1000, msec%1000); } static ssize_t safe_delay_store(struct mddev *mddev, const char *cbuf, size_t len) @@ -3843,7 +3844,7 @@ safe_delay_store(struct mddev *mddev, const char *cbuf, size_t len) return -EINVAL; } - if (strict_strtoul_scaled(cbuf, &msec, 3) < 0) + if (strict_strtoul_scaled(cbuf, &msec, 3) < 0 || msec > UINT_MAX / HZ) return -EINVAL; if (msec == 0) mddev->safemode_delay = 0; -- cgit 1.4.1 From 05d10428e8dffed0bac2502f34151729fc189cd3 Mon Sep 17 00:00:00 2001 From: Li Nan Date: Mon, 22 May 2023 15:25:34 +0800 Subject: md/raid10: fix wrong setting of max_corr_read_errors [ Upstream commit f8b20a405428803bd9881881d8242c9d72c6b2b2 ] There is no input check when echo md/max_read_errors and overflow might occur. Add check of input number. Fixes: 1e50915fe0bb ("raid: improve MD/raid10 handling of correctable read errors.") Signed-off-by: Li Nan Reviewed-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230522072535.1523740-3-linan666@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/md.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/md.c b/drivers/md/md.c index 0c97531d3503..829e1bd9bcbf 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4513,6 +4513,8 @@ max_corrected_read_errors_store(struct mddev *mddev, const char *buf, size_t len rv = kstrtouint(buf, 10, &n); if (rv < 0) return rv; + if (n > INT_MAX) + return -EINVAL; atomic_set(&mddev->max_corr_read_errors, n); return len; } -- cgit 1.4.1 From 222cc459d59857ee28a5366dc225ab42b22f9272 Mon Sep 17 00:00:00 2001 From: Li Nan Date: Sat, 27 May 2023 15:22:15 +0800 Subject: md/raid10: fix null-ptr-deref of mreplace in raid10_sync_request [ Upstream commit 34817a2441747b48e444cb0e05d84e14bc9443da ] There are two check of 'mreplace' in raid10_sync_request(). In the first check, 'need_replace' will be set and 'mreplace' will be used later if no-Faulty 'mreplace' exists, In the second check, 'mreplace' will be set to NULL if it is Faulty, but 'need_replace' will not be changed accordingly. null-ptr-deref occurs if Faulty is set between two check. Fix it by merging two checks into one. And replace 'need_replace' with 'mreplace' because their values are always the same. Fixes: ee37d7314a32 ("md/raid10: Fix raid10 replace hang when new added disk faulty") Signed-off-by: Li Nan Reviewed-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230527072218.2365857-2-linan666@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/raid10.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 67398394cc9c..0eb2eb83ddb9 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -3436,7 +3436,6 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, int must_sync; int any_working; int need_recover = 0; - int need_replace = 0; struct raid10_info *mirror = &conf->mirrors[i]; struct md_rdev *mrdev, *mreplace; @@ -3448,11 +3447,10 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, !test_bit(Faulty, &mrdev->flags) && !test_bit(In_sync, &mrdev->flags)) need_recover = 1; - if (mreplace != NULL && - !test_bit(Faulty, &mreplace->flags)) - need_replace = 1; + if (mreplace && test_bit(Faulty, &mreplace->flags)) + mreplace = NULL; - if (!need_recover && !need_replace) { + if (!need_recover && !mreplace) { rcu_read_unlock(); continue; } @@ -3468,8 +3466,6 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, rcu_read_unlock(); continue; } - if (mreplace && test_bit(Faulty, &mreplace->flags)) - mreplace = NULL; /* Unless we are doing a full sync, or a replacement * we only need to recover the block if it is set in * the bitmap @@ -3592,11 +3588,11 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, bio = r10_bio->devs[1].repl_bio; if (bio) bio->bi_end_io = NULL; - /* Note: if need_replace, then bio + /* Note: if replace is not NULL, then bio * cannot be NULL as r10buf_pool_alloc will * have allocated it. */ - if (!need_replace) + if (!mreplace) break; bio->bi_next = biolist; biolist = bio; -- cgit 1.4.1 From a5a1ec06ef2f3cff22f699a761959d0087cd517f Mon Sep 17 00:00:00 2001 From: Li Nan Date: Fri, 2 Jun 2023 17:18:39 +0800 Subject: md/raid10: fix io loss while replacement replace rdev [ Upstream commit 2ae6aaf76912bae53c74b191569d2ab484f24bf3 ] When removing a disk with replacement, the replacement will be used to replace rdev. During this process, there is a brief window in which both rdev and replacement are read as NULL in raid10_write_request(). This will result in io not being submitted but it should be. //remove //write raid10_remove_disk raid10_write_request mirror->rdev = NULL read rdev -> NULL mirror->rdev = mirror->replacement mirror->replacement = NULL read replacement -> NULL Fix it by reading replacement first and rdev later, meanwhile, use smp_mb() to prevent memory reordering. Fixes: 475b0321a4df ("md/raid10: writes should get directed to replacement as well as original.") Signed-off-by: Li Nan Reviewed-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230602091839.743798-3-linan666@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/raid10.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 0eb2eb83ddb9..386c16c39826 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -779,8 +779,16 @@ static struct md_rdev *read_balance(struct r10conf *conf, disk = r10_bio->devs[slot].devnum; rdev = rcu_dereference(conf->mirrors[disk].replacement); if (rdev == NULL || test_bit(Faulty, &rdev->flags) || - r10_bio->devs[slot].addr + sectors > rdev->recovery_offset) + r10_bio->devs[slot].addr + sectors > + rdev->recovery_offset) { + /* + * Read replacement first to prevent reading both rdev + * and replacement as NULL during replacement replace + * rdev. + */ + smp_mb(); rdev = rcu_dereference(conf->mirrors[disk].rdev); + } if (rdev == NULL || test_bit(Faulty, &rdev->flags)) continue; @@ -1477,9 +1485,15 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, for (i = 0; i < conf->copies; i++) { int d = r10_bio->devs[i].devnum; - struct md_rdev *rdev = rcu_dereference(conf->mirrors[d].rdev); - struct md_rdev *rrdev = rcu_dereference( - conf->mirrors[d].replacement); + struct md_rdev *rdev, *rrdev; + + rrdev = rcu_dereference(conf->mirrors[d].replacement); + /* + * Read replacement first to prevent reading both rdev and + * replacement as NULL during replacement replace rdev. + */ + smp_mb(); + rdev = rcu_dereference(conf->mirrors[d].rdev); if (rdev == rrdev) rrdev = NULL; if (rdev && (test_bit(Faulty, &rdev->flags))) -- cgit 1.4.1 From fa0f13a8338ff256535d007f9356465940c6a183 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Mon, 29 May 2023 21:11:01 +0800 Subject: md/raid1-10: factor out a helper to add bio to plug [ Upstream commit 5ec6ca140a034682e421e2e808ef5ddfdfd65242 ] The code in raid1 and raid10 is identical, prepare to limit the number of plugged bios. Signed-off-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230529131106.2123367-3-yukuai1@huaweicloud.com Stable-dep-of: 7db922bae3ab ("md/raid1-10: submit write io directly if bitmap is not enabled") Signed-off-by: Sasha Levin --- drivers/md/raid1-10.c | 16 ++++++++++++++++ drivers/md/raid1.c | 12 +----------- drivers/md/raid10.c | 11 +---------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c index e61f6cad4e08..9bf19a3409ce 100644 --- a/drivers/md/raid1-10.c +++ b/drivers/md/raid1-10.c @@ -109,3 +109,19 @@ static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp, size -= len; } while (idx++ < RESYNC_PAGES && size > 0); } + +static inline bool raid1_add_bio_to_plug(struct mddev *mddev, struct bio *bio, + blk_plug_cb_fn unplug) +{ + struct raid1_plug_cb *plug = NULL; + struct blk_plug_cb *cb = blk_check_plugged(unplug, mddev, + sizeof(*plug)); + + if (!cb) + return false; + + plug = container_of(cb, struct raid1_plug_cb, cb); + bio_list_add(&plug->pending, bio); + + return true; +} diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 58f705f42948..f3f2078d69e7 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1343,8 +1343,6 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, struct bitmap *bitmap = mddev->bitmap; unsigned long flags; struct md_rdev *blocked_rdev; - struct blk_plug_cb *cb; - struct raid1_plug_cb *plug = NULL; int first_clone; int max_sectors; bool write_behind = false; @@ -1573,15 +1571,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, r1_bio->sector); /* flush_pending_writes() needs access to the rdev so...*/ mbio->bi_bdev = (void *)rdev; - - cb = blk_check_plugged(raid1_unplug, mddev, sizeof(*plug)); - if (cb) - plug = container_of(cb, struct raid1_plug_cb, cb); - else - plug = NULL; - if (plug) { - bio_list_add(&plug->pending, mbio); - } else { + if (!raid1_add_bio_to_plug(mddev, mbio, raid1_unplug)) { spin_lock_irqsave(&conf->device_lock, flags); bio_list_add(&conf->pending_bio_list, mbio); spin_unlock_irqrestore(&conf->device_lock, flags); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 386c16c39826..14001cceb618 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1288,8 +1288,6 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio, const blk_opf_t do_sync = bio->bi_opf & REQ_SYNC; const blk_opf_t do_fua = bio->bi_opf & REQ_FUA; unsigned long flags; - struct blk_plug_cb *cb; - struct raid1_plug_cb *plug = NULL; struct r10conf *conf = mddev->private; struct md_rdev *rdev; int devnum = r10_bio->devs[n_copy].devnum; @@ -1329,14 +1327,7 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio, atomic_inc(&r10_bio->remaining); - cb = blk_check_plugged(raid10_unplug, mddev, sizeof(*plug)); - if (cb) - plug = container_of(cb, struct raid1_plug_cb, cb); - else - plug = NULL; - if (plug) { - bio_list_add(&plug->pending, mbio); - } else { + if (!raid1_add_bio_to_plug(mddev, mbio, raid10_unplug)) { spin_lock_irqsave(&conf->device_lock, flags); bio_list_add(&conf->pending_bio_list, mbio); spin_unlock_irqrestore(&conf->device_lock, flags); -- cgit 1.4.1 From f98b89fbf8fae0033ecc5ad53be8085fc30d3850 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Mon, 29 May 2023 21:11:02 +0800 Subject: md/raid1-10: factor out a helper to submit normal write [ Upstream commit 8295efbe68c080047e98d9c0eb5cb933b238a8cb ] There are multiple places to do the same thing, factor out a helper to prevent redundant code, and the helper will be used in following patch as well. Signed-off-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230529131106.2123367-4-yukuai1@huaweicloud.com Stable-dep-of: 7db922bae3ab ("md/raid1-10: submit write io directly if bitmap is not enabled") Signed-off-by: Sasha Levin --- drivers/md/raid1-10.c | 17 +++++++++++++++++ drivers/md/raid1.c | 13 ++----------- drivers/md/raid10.c | 26 ++++---------------------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c index 9bf19a3409ce..506299bd55cb 100644 --- a/drivers/md/raid1-10.c +++ b/drivers/md/raid1-10.c @@ -110,6 +110,23 @@ static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp, } while (idx++ < RESYNC_PAGES && size > 0); } + +static inline void raid1_submit_write(struct bio *bio) +{ + struct md_rdev *rdev = (struct md_rdev *)bio->bi_bdev; + + bio->bi_next = NULL; + bio_set_dev(bio, rdev->bdev); + if (test_bit(Faulty, &rdev->flags)) + bio_io_error(bio); + else if (unlikely(bio_op(bio) == REQ_OP_DISCARD && + !bdev_max_discard_sectors(bio->bi_bdev))) + /* Just ignore it */ + bio_endio(bio); + else + submit_bio_noacct(bio); +} + static inline bool raid1_add_bio_to_plug(struct mddev *mddev, struct bio *bio, blk_plug_cb_fn unplug) { diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index f3f2078d69e7..ac64c587191b 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -799,17 +799,8 @@ static void flush_bio_list(struct r1conf *conf, struct bio *bio) while (bio) { /* submit pending writes */ struct bio *next = bio->bi_next; - struct md_rdev *rdev = (void *)bio->bi_bdev; - bio->bi_next = NULL; - bio_set_dev(bio, rdev->bdev); - if (test_bit(Faulty, &rdev->flags)) { - bio_io_error(bio); - } else if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && - !bdev_max_discard_sectors(bio->bi_bdev))) - /* Just ignore it */ - bio_endio(bio); - else - submit_bio_noacct(bio); + + raid1_submit_write(bio); bio = next; cond_resched(); } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 14001cceb618..ea6228ff3bb5 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -917,17 +917,8 @@ static void flush_pending_writes(struct r10conf *conf) while (bio) { /* submit pending writes */ struct bio *next = bio->bi_next; - struct md_rdev *rdev = (void*)bio->bi_bdev; - bio->bi_next = NULL; - bio_set_dev(bio, rdev->bdev); - if (test_bit(Faulty, &rdev->flags)) { - bio_io_error(bio); - } else if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && - !bdev_max_discard_sectors(bio->bi_bdev))) - /* Just ignore it */ - bio_endio(bio); - else - submit_bio_noacct(bio); + + raid1_submit_write(bio); bio = next; } blk_finish_plug(&plug); @@ -1136,17 +1127,8 @@ static void raid10_unplug(struct blk_plug_cb *cb, bool from_schedule) while (bio) { /* submit pending writes */ struct bio *next = bio->bi_next; - struct md_rdev *rdev = (void*)bio->bi_bdev; - bio->bi_next = NULL; - bio_set_dev(bio, rdev->bdev); - if (test_bit(Faulty, &rdev->flags)) { - bio_io_error(bio); - } else if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && - !bdev_max_discard_sectors(bio->bi_bdev))) - /* Just ignore it */ - bio_endio(bio); - else - submit_bio_noacct(bio); + + raid1_submit_write(bio); bio = next; } kfree(plug); -- cgit 1.4.1 From 067c08f78dd14911431d7ee5100942125db00da7 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Mon, 29 May 2023 21:11:03 +0800 Subject: md/raid1-10: submit write io directly if bitmap is not enabled [ Upstream commit 7db922bae3abdf0a1db81ef7228cc0b996a0c1e3 ] Commit 6cce3b23f6f8 ("[PATCH] md: write intent bitmap support for raid10") add bitmap support, and it changed that write io is submitted through daemon thread because bitmap need to be updated before write io. And later, plug is used to fix performance regression because all the write io will go to demon thread, which means io can't be issued concurrently. However, if bitmap is not enabled, the write io should not go to daemon thread in the first place, and plug is not needed as well. Fixes: 6cce3b23f6f8 ("[PATCH] md: write intent bitmap support for raid10") Signed-off-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230529131106.2123367-5-yukuai1@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/md-bitmap.c | 4 +--- drivers/md/md-bitmap.h | 7 +++++++ drivers/md/raid1-10.c | 13 +++++++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 9640741e8d36..8bbeeec70905 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -993,7 +993,6 @@ static int md_bitmap_file_test_bit(struct bitmap *bitmap, sector_t block) return set; } - /* this gets called when the md device is ready to unplug its underlying * (slave) device queues -- before we let any writes go down, we need to * sync the dirty pages of the bitmap file to disk */ @@ -1003,8 +1002,7 @@ void md_bitmap_unplug(struct bitmap *bitmap) int dirty, need_write; int writing = 0; - if (!bitmap || !bitmap->storage.filemap || - test_bit(BITMAP_STALE, &bitmap->flags)) + if (!md_bitmap_enabled(bitmap)) return; /* look at each page to see if there are any set bits that need to be diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h index cfd7395de8fd..3a4750952b3a 100644 --- a/drivers/md/md-bitmap.h +++ b/drivers/md/md-bitmap.h @@ -273,6 +273,13 @@ int md_bitmap_copy_from_slot(struct mddev *mddev, int slot, sector_t *lo, sector_t *hi, bool clear_bits); void md_bitmap_free(struct bitmap *bitmap); void md_bitmap_wait_behind_writes(struct mddev *mddev); + +static inline bool md_bitmap_enabled(struct bitmap *bitmap) +{ + return bitmap && bitmap->storage.filemap && + !test_bit(BITMAP_STALE, &bitmap->flags); +} + #endif #endif diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c index 506299bd55cb..73cc3cb9154d 100644 --- a/drivers/md/raid1-10.c +++ b/drivers/md/raid1-10.c @@ -131,9 +131,18 @@ static inline bool raid1_add_bio_to_plug(struct mddev *mddev, struct bio *bio, blk_plug_cb_fn unplug) { struct raid1_plug_cb *plug = NULL; - struct blk_plug_cb *cb = blk_check_plugged(unplug, mddev, - sizeof(*plug)); + struct blk_plug_cb *cb; + + /* + * If bitmap is not enabled, it's safe to submit the io directly, and + * this can get optimal performance. + */ + if (!md_bitmap_enabled(mddev->bitmap)) { + raid1_submit_write(bio); + return true; + } + cb = blk_check_plugged(unplug, mddev, sizeof(*plug)); if (!cb) return false; -- cgit 1.4.1 From aa07e56c6a9c7558165690d14eed4fe8babf34fb Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Sat, 10 Jun 2023 10:20:03 +0800 Subject: block: fix blktrace debugfs entries leakage [ Upstream commit dd7de3704af9989b780693d51eaea49a665bd9c2 ] Commit 99d055b4fd4b ("block: remove per-disk debugfs files in blk_unregister_queue") moves blk_trace_shutdown() from blk_release_queue() to blk_unregister_queue(), this is safe if blktrace is created through sysfs, however, there is a regression in corner case. blktrace can still be enabled after del_gendisk() through ioctl if the disk is opened before del_gendisk(), and if blktrace is not shutdown through ioctl before closing the disk, debugfs entries will be leaked. Fix this problem by shutdown blktrace in disk_release(), this is safe because blk_trace_remove() is reentrant. Fixes: 99d055b4fd4b ("block: remove per-disk debugfs files in blk_unregister_queue") Signed-off-by: Yu Kuai Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230610022003.2557284-4-yukuai1@huaweicloud.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/genhd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/genhd.c b/block/genhd.c index 62a61388e752..afab646d12c8 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -25,8 +25,9 @@ #include #include #include -#include "blk-throttle.h" +#include +#include "blk-throttle.h" #include "blk.h" #include "blk-mq-sched.h" #include "blk-rq-qos.h" @@ -1181,6 +1182,8 @@ static void disk_release(struct device *dev) might_sleep(); WARN_ON_ONCE(disk_live(disk)); + blk_trace_remove(disk->queue); + /* * To undo the all initialization from blk_mq_init_allocated_queue in * case of a probe failure where add_disk is never called we have to -- cgit 1.4.1 From 0cf83d3698fb4426248e577f64130ddc73180a3a Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Thu, 1 Jun 2023 17:56:14 +0200 Subject: irqchip/stm32-exti: Fix warning on initialized field overwritten [ Upstream commit 48f31e496488a25f443c0df52464da446fb1d10c ] While compiling with W=1, both gcc and clang complain about a tricky way to initialize an array by filling it with a non-zero value and then overrride some of the array elements. In this case the override is intentional, so just disable the specific warning for only this part of the code. Note: the flag "-Woverride-init" is recognized by both compilers, but the warning msg from clang reports "-Winitializer-overrides". The doc of clang clarifies that the two flags are synonyms, so use here only the flag name common on both compilers. Signed-off-by: Antonio Borneo Fixes: c297493336b7 ("irqchip/stm32-exti: Simplify irq description table") Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230601155614.34490-1-antonio.borneo@foss.st.com Signed-off-by: Sasha Levin --- drivers/irqchip/irq-stm32-exti.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 6a3f7498ea8e..8bbb2b114636 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -173,6 +173,16 @@ static struct irq_chip stm32_exti_h_chip_direct; #define EXTI_INVALID_IRQ U8_MAX #define STM32MP1_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp1_exti_banks) * IRQS_PER_BANK) +/* + * Use some intentionally tricky logic here to initialize the whole array to + * EXTI_INVALID_IRQ, but then override certain fields, requiring us to indicate + * that we "know" that there are overrides in this structure, and we'll need to + * disable that warning from W=1 builds. + */ +__diag_push(); +__diag_ignore_all("-Woverride-init", + "logic to initialize all and then override some is OK"); + static const u8 stm32mp1_desc_irq[] = { /* default value */ [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, @@ -266,6 +276,8 @@ static const u8 stm32mp13_desc_irq[] = { [70] = 98, }; +__diag_pop(); + static const struct stm32_exti_drv_data stm32mp1_drv_data = { .exti_banks = stm32mp1_exti_banks, .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), -- cgit 1.4.1 From faf004e98d02747eff9477e5a6d5393a669f8f8d Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz Date: Wed, 10 May 2023 18:33:42 +0200 Subject: irqchip/jcore-aic: Fix missing allocation of IRQ descriptors [ Upstream commit 4848229494a323eeaab62eee5574ef9f7de80374 ] The initialization function for the J-Core AIC aic_irq_of_init() is currently missing the call to irq_alloc_descs() which allocates and initializes all the IRQ descriptors. Add missing function call and return the error code from irq_alloc_descs() in case the allocation fails. Fixes: 981b58f66cfc ("irqchip/jcore-aic: Add J-Core AIC driver") Signed-off-by: John Paul Adrian Glaubitz Tested-by: Rob Landley Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230510163343.43090-1-glaubitz@physik.fu-berlin.de Signed-off-by: Sasha Levin --- drivers/irqchip/irq-jcore-aic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/irqchip/irq-jcore-aic.c b/drivers/irqchip/irq-jcore-aic.c index 5f47d8ee4ae3..b9dcc8e78c75 100644 --- a/drivers/irqchip/irq-jcore-aic.c +++ b/drivers/irqchip/irq-jcore-aic.c @@ -68,6 +68,7 @@ static int __init aic_irq_of_init(struct device_node *node, unsigned min_irq = JCORE_AIC2_MIN_HWIRQ; unsigned dom_sz = JCORE_AIC_MAX_HWIRQ+1; struct irq_domain *domain; + int ret; pr_info("Initializing J-Core AIC\n"); @@ -100,6 +101,12 @@ static int __init aic_irq_of_init(struct device_node *node, jcore_aic.irq_unmask = noop; jcore_aic.name = "AIC"; + ret = irq_alloc_descs(-1, min_irq, dom_sz - min_irq, + of_node_to_nid(node)); + + if (ret < 0) + return ret; + domain = irq_domain_add_legacy(node, dom_sz - min_irq, min_irq, min_irq, &jcore_aic_irqdomain_ops, &jcore_aic); -- cgit 1.4.1 From 423453bb506e54f4a2775c1dec313f0b4ebf7d6d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 12 Jun 2023 10:10:20 -0400 Subject: svcrdma: Prevent page release when nothing was received [ Upstream commit baf6d18b116b7dc84ed5e212c3a89f17cdc3f28c ] I noticed that svc_rqst_release_pages() was still unnecessarily releasing a page when svc_rdma_recvfrom() returns zero. Fixes: a53d5cb0646a ("svcrdma: Avoid releasing a page in svc_xprt_release()") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 53a7cb2f6c07..6da6608985ce 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -796,6 +796,12 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) struct svc_rdma_recv_ctxt *ctxt; int ret; + /* Prevent svc_xprt_release() from releasing pages in rq_pages + * when returning 0 or an error. + */ + rqstp->rq_respages = rqstp->rq_pages; + rqstp->rq_next_page = rqstp->rq_respages; + rqstp->rq_xprt_ctxt = NULL; ctxt = NULL; @@ -819,12 +825,6 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) DMA_FROM_DEVICE); svc_rdma_build_arg_xdr(rqstp, ctxt); - /* Prevent svc_xprt_release from releasing pages in rq_pages - * if we return 0 or an error. - */ - rqstp->rq_respages = rqstp->rq_pages; - rqstp->rq_next_page = rqstp->rq_respages; - ret = svc_rdma_xdr_decode_req(&rqstp->rq_arg, ctxt); if (ret < 0) goto out_err; -- cgit 1.4.1 From ec94df6bcfb046086ea080750a2549a73f26f1ee Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sat, 14 Jan 2023 23:08:23 +0800 Subject: erofs: simplify iloc() [ Upstream commit b780d3fc6107464dcc43631a6208c43b6421f1e6 ] Actually we could pass in inodes directly to clean up all callers. Also rename iloc() as erofs_iloc(). Link: https://lore.kernel.org/r/20230114150823.432069-1-xiang@kernel.org Reviewed-by: Yue Hu Reviewed-by: Jingbo Xu Reviewed-by: Chao Yu Signed-off-by: Gao Xiang Stable-dep-of: 001b8ccd0650 ("erofs: fix compact 4B support for 16k block size") Signed-off-by: Sasha Levin --- fs/erofs/data.c | 9 +++------ fs/erofs/inode.c | 2 +- fs/erofs/internal.h | 16 +++++++++------- fs/erofs/xattr.c | 20 +++++++------------- fs/erofs/zmap.c | 13 +++++-------- include/trace/events/erofs.h | 4 ++-- 6 files changed, 27 insertions(+), 37 deletions(-) diff --git a/fs/erofs/data.c b/fs/erofs/data.c index fe8ac0e163f7..b32801d716f8 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -95,11 +95,8 @@ static int erofs_map_blocks_flatmode(struct inode *inode, map->m_pa = blknr_to_addr(vi->raw_blkaddr) + map->m_la; map->m_plen = blknr_to_addr(lastblk) - offset; } else if (tailendpacking) { - /* 2 - inode inline B: inode, [xattrs], inline last blk... */ - struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); - - map->m_pa = iloc(sbi, vi->nid) + vi->inode_isize + - vi->xattr_isize + erofs_blkoff(map->m_la); + map->m_pa = erofs_iloc(inode) + vi->inode_isize + + vi->xattr_isize + erofs_blkoff(offset); map->m_plen = inode->i_size - offset; /* inline data should be located in the same meta block */ @@ -154,7 +151,7 @@ int erofs_map_blocks(struct inode *inode, unit = EROFS_BLOCK_MAP_ENTRY_SIZE; /* block map */ chunknr = map->m_la >> vi->chunkbits; - pos = ALIGN(iloc(EROFS_SB(sb), vi->nid) + vi->inode_isize + + pos = ALIGN(erofs_iloc(inode) + vi->inode_isize + vi->xattr_isize, unit) + unit * chunknr; kaddr = erofs_read_metabuf(&buf, sb, erofs_blknr(pos), EROFS_KMAP); diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c index ad2a82f2eb4c..5aadc73d5765 100644 --- a/fs/erofs/inode.c +++ b/fs/erofs/inode.c @@ -14,7 +14,7 @@ static void *erofs_read_inode(struct erofs_buf *buf, struct super_block *sb = inode->i_sb; struct erofs_sb_info *sbi = EROFS_SB(sb); struct erofs_inode *vi = EROFS_I(inode); - const erofs_off_t inode_loc = iloc(sbi, vi->nid); + const erofs_off_t inode_loc = erofs_iloc(inode); erofs_blk_t blkaddr, nblks = 0; void *kaddr; diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 340bd56a5755..d8d09fc3ed65 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -273,11 +273,6 @@ struct erofs_buf { #define erofs_blkoff(addr) ((addr) % EROFS_BLKSIZ) #define blknr_to_addr(nr) ((erofs_off_t)(nr) * EROFS_BLKSIZ) -static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid) -{ - return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits); -} - #define EROFS_FEATURE_FUNCS(name, compat, feature) \ static inline bool erofs_sb_has_##name(struct erofs_sb_info *sbi) \ { \ @@ -342,8 +337,15 @@ struct erofs_inode { struct inode vfs_inode; }; -#define EROFS_I(ptr) \ - container_of(ptr, struct erofs_inode, vfs_inode) +#define EROFS_I(ptr) container_of(ptr, struct erofs_inode, vfs_inode) + +static inline erofs_off_t erofs_iloc(struct inode *inode) +{ + struct erofs_sb_info *sbi = EROFS_I_SB(inode); + + return blknr_to_addr(sbi->meta_blkaddr) + + (EROFS_I(inode)->nid << sbi->islotbits); +} static inline unsigned long erofs_inode_datablocks(struct inode *inode) { diff --git a/fs/erofs/xattr.c b/fs/erofs/xattr.c index 8106bcb5a38d..a2776abf3698 100644 --- a/fs/erofs/xattr.c +++ b/fs/erofs/xattr.c @@ -22,8 +22,7 @@ static int init_inode_xattrs(struct inode *inode) struct xattr_iter it; unsigned int i; struct erofs_xattr_ibody_header *ih; - struct super_block *sb; - struct erofs_sb_info *sbi; + struct super_block *sb = inode->i_sb; int ret = 0; /* the most case is that xattrs of this inode are initialized. */ @@ -52,15 +51,14 @@ static int init_inode_xattrs(struct inode *inode) * undefined right now (maybe use later with some new sb feature). */ if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) { - erofs_err(inode->i_sb, + erofs_err(sb, "xattr_isize %d of nid %llu is not supported yet", vi->xattr_isize, vi->nid); ret = -EOPNOTSUPP; goto out_unlock; } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { if (vi->xattr_isize) { - erofs_err(inode->i_sb, - "bogus xattr ibody @ nid %llu", vi->nid); + erofs_err(sb, "bogus xattr ibody @ nid %llu", vi->nid); DBG_BUGON(1); ret = -EFSCORRUPTED; goto out_unlock; /* xattr ondisk layout error */ @@ -69,11 +67,9 @@ static int init_inode_xattrs(struct inode *inode) goto out_unlock; } - sb = inode->i_sb; - sbi = EROFS_SB(sb); it.buf = __EROFS_BUF_INITIALIZER; - it.blkaddr = erofs_blknr(iloc(sbi, vi->nid) + vi->inode_isize); - it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize); + it.blkaddr = erofs_blknr(erofs_iloc(inode) + vi->inode_isize); + it.ofs = erofs_blkoff(erofs_iloc(inode) + vi->inode_isize); /* read in shared xattr array (non-atomic, see kmalloc below) */ it.kaddr = erofs_read_metabuf(&it.buf, sb, it.blkaddr, EROFS_KMAP); @@ -159,7 +155,6 @@ static int inline_xattr_iter_begin(struct xattr_iter *it, struct inode *inode) { struct erofs_inode *const vi = EROFS_I(inode); - struct erofs_sb_info *const sbi = EROFS_SB(inode->i_sb); unsigned int xattr_header_sz, inline_xattr_ofs; xattr_header_sz = inlinexattr_header_size(inode); @@ -170,9 +165,8 @@ static int inline_xattr_iter_begin(struct xattr_iter *it, inline_xattr_ofs = vi->inode_isize + xattr_header_sz; - it->blkaddr = erofs_blknr(iloc(sbi, vi->nid) + inline_xattr_ofs); - it->ofs = erofs_blkoff(iloc(sbi, vi->nid) + inline_xattr_ofs); - + it->blkaddr = erofs_blknr(erofs_iloc(inode) + inline_xattr_ofs); + it->ofs = erofs_blkoff(erofs_iloc(inode) + inline_xattr_ofs); it->kaddr = erofs_read_metabuf(&it->buf, inode->i_sb, it->blkaddr, EROFS_KMAP_ATOMIC); if (IS_ERR(it->kaddr)) diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c index bb91cc649972..3961bb55dea1 100644 --- a/fs/erofs/zmap.c +++ b/fs/erofs/zmap.c @@ -55,8 +55,7 @@ static int z_erofs_fill_inode_lazy(struct inode *inode) if (test_bit(EROFS_I_Z_INITED_BIT, &vi->flags)) goto out_unlock; - pos = ALIGN(iloc(EROFS_SB(sb), vi->nid) + vi->inode_isize + - vi->xattr_isize, 8); + pos = ALIGN(erofs_iloc(inode) + vi->inode_isize + vi->xattr_isize, 8); kaddr = erofs_read_metabuf(&buf, sb, erofs_blknr(pos), EROFS_KMAP); if (IS_ERR(kaddr)) { err = PTR_ERR(kaddr); @@ -169,10 +168,9 @@ static int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m, { struct inode *const inode = m->inode; struct erofs_inode *const vi = EROFS_I(inode); - const erofs_off_t ibase = iloc(EROFS_I_SB(inode), vi->nid); const erofs_off_t pos = - Z_EROFS_VLE_LEGACY_INDEX_ALIGN(ibase + vi->inode_isize + - vi->xattr_isize) + + Z_EROFS_VLE_LEGACY_INDEX_ALIGN(erofs_iloc(inode) + + vi->inode_isize + vi->xattr_isize) + lcn * sizeof(struct z_erofs_vle_decompressed_index); struct z_erofs_vle_decompressed_index *di; unsigned int advise, type; @@ -376,9 +374,8 @@ static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m, struct inode *const inode = m->inode; struct erofs_inode *const vi = EROFS_I(inode); const unsigned int lclusterbits = vi->z_logical_clusterbits; - const erofs_off_t ebase = ALIGN(iloc(EROFS_I_SB(inode), vi->nid) + - vi->inode_isize + vi->xattr_isize, 8) + - sizeof(struct z_erofs_map_header); + const erofs_off_t ebase = sizeof(struct z_erofs_map_header) + + ALIGN(erofs_iloc(inode) + vi->inode_isize + vi->xattr_isize, 8); const unsigned int totalidx = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ); unsigned int compacted_4b_initial, compacted_2b; unsigned int amortizedshift; diff --git a/include/trace/events/erofs.h b/include/trace/events/erofs.h index 4f4c44ea3a65..e095d36db939 100644 --- a/include/trace/events/erofs.h +++ b/include/trace/events/erofs.h @@ -66,8 +66,8 @@ TRACE_EVENT(erofs_fill_inode, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->nid = EROFS_I(inode)->nid; - __entry->blkaddr = erofs_blknr(iloc(EROFS_I_SB(inode), __entry->nid)); - __entry->ofs = erofs_blkoff(iloc(EROFS_I_SB(inode), __entry->nid)); + __entry->blkaddr = erofs_blknr(erofs_iloc(inode)); + __entry->ofs = erofs_blkoff(erofs_iloc(inode)); ), TP_printk("dev = (%d,%d), nid = %llu, blkaddr %u ofs %u", -- cgit 1.4.1 From 9a534100382a13ac590702b9ae7670a642b33db8 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Thu, 1 Jun 2023 19:23:41 +0800 Subject: erofs: fix compact 4B support for 16k block size [ Upstream commit 001b8ccd0650727e54ec16ef72bf1b8eeab7168e ] In compact 4B, two adjacent lclusters are packed together as a unit to form on-disk indexes for effective random access, as below: (amortized = 4, vcnt = 2) _____________________________________________ |___@_____ encoded bits __________|_ blkaddr _| 0 . amortized * vcnt = 8 . . . . amortized * vcnt - 4 = 4 . . .____________________________. |_type (2 bits)_|_clusterofs_| Therefore, encoded bits for each pack are 32 bits (4 bytes). IOWs, since each lcluster can get 16 bits for its type and clusterofs, the maximum supported lclustersize for compact 4B format is 16k (14 bits). Fix this to enable compact 4B format for 16k lclusters (blocks), which is tested on an arm64 server with 16k page size. Fixes: 152a333a5895 ("staging: erofs: add compacted compression indexes support") Signed-off-by: Gao Xiang Link: https://lore.kernel.org/r/20230601112341.56960-1-hsiangkao@linux.alibaba.com Signed-off-by: Sasha Levin --- fs/erofs/zmap.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c index 3961bb55dea1..0337b70b2dac 100644 --- a/fs/erofs/zmap.c +++ b/fs/erofs/zmap.c @@ -271,7 +271,7 @@ static int unpack_compacted_index(struct z_erofs_maprecorder *m, u8 *in, type; bool big_pcluster; - if (1 << amortizedshift == 4) + if (1 << amortizedshift == 4 && lclusterbits <= 14) vcnt = 2; else if (1 << amortizedshift == 2 && lclusterbits == 12) vcnt = 16; @@ -373,7 +373,6 @@ static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m, { struct inode *const inode = m->inode; struct erofs_inode *const vi = EROFS_I(inode); - const unsigned int lclusterbits = vi->z_logical_clusterbits; const erofs_off_t ebase = sizeof(struct z_erofs_map_header) + ALIGN(erofs_iloc(inode) + vi->inode_isize + vi->xattr_isize, 8); const unsigned int totalidx = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ); @@ -381,9 +380,6 @@ static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m, unsigned int amortizedshift; erofs_off_t pos; - if (lclusterbits != 12) - return -EOPNOTSUPP; - if (lcn >= totalidx) return -EINVAL; -- cgit 1.4.1 From e7aff15ba29ba4b3052786b1636fa5c4aa39e179 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 1 Jun 2023 22:16:34 +0200 Subject: posix-timers: Prevent RT livelock in itimer_delete() [ Upstream commit 9d9e522010eb5685d8b53e8a24320653d9d4cbbf ] itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed, except for posix CPU timers which have HAVE_POSIX_CPU_TIMERS_TASK_WORK enabled. In that case and on RT kernels the existing task could live lock when preempting the task which does the timer delivery. Replace spin_unlock() with an invocation of timer_wait_running() to handle it the same way as the other retry loops in the posix timer code. Fixes: ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT") Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Link: https://lore.kernel.org/r/87v8g7c50d.ffs@tglx Signed-off-by: Sasha Levin --- kernel/time/posix-timers.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 808a247205a9..ed3c4a954398 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -1037,27 +1037,52 @@ retry_delete: } /* - * return timer owned by the process, used by exit_itimers + * Delete a timer if it is armed, remove it from the hash and schedule it + * for RCU freeing. */ static void itimer_delete(struct k_itimer *timer) { -retry_delete: - spin_lock_irq(&timer->it_lock); + unsigned long flags; + + /* + * irqsave is required to make timer_wait_running() work. + */ + spin_lock_irqsave(&timer->it_lock, flags); +retry_delete: + /* + * Even if the timer is not longer accessible from other tasks + * it still might be armed and queued in the underlying timer + * mechanism. Worse, that timer mechanism might run the expiry + * function concurrently. + */ if (timer_delete_hook(timer) == TIMER_RETRY) { - spin_unlock_irq(&timer->it_lock); + /* + * Timer is expired concurrently, prevent livelocks + * and pointless spinning on RT. + * + * timer_wait_running() drops timer::it_lock, which opens + * the possibility for another task to delete the timer. + * + * That's not possible here because this is invoked from + * do_exit() only for the last thread of the thread group. + * So no other task can access and delete that timer. + */ + if (WARN_ON_ONCE(timer_wait_running(timer, &flags) != timer)) + return; + goto retry_delete; } list_del(&timer->list); - spin_unlock_irq(&timer->it_lock); + spin_unlock_irqrestore(&timer->it_lock, flags); release_posix_timer(timer, IT_ID_SET); } /* - * This is called by do_exit or de_thread, only when nobody else can - * modify the signal->posix_timers list. Yet we need sighand->siglock - * to prevent the race with /proc/pid/timers. + * Invoked from do_exit() when the last thread of a thread group exits. + * At that point no other task can access the timers of the dying + * task anymore. */ void exit_itimers(struct task_struct *tsk) { @@ -1067,10 +1092,12 @@ void exit_itimers(struct task_struct *tsk) if (list_empty(&tsk->signal->posix_timers)) return; + /* Protect against concurrent read via /proc/$PID/timers */ spin_lock_irq(&tsk->sighand->siglock); list_replace_init(&tsk->signal->posix_timers, &timers); spin_unlock_irq(&tsk->sighand->siglock); + /* The timers are not longer accessible via tsk::signal */ while (!list_empty(&timers)) { tmr = list_first_entry(&timers, struct k_itimer, list); itimer_delete(tmr); -- cgit 1.4.1 From 77cc52f1b8d76c995648cb4286e57142cac8ce0a Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Fri, 5 May 2023 00:12:53 +0800 Subject: tick/rcu: Fix bogus ratelimit condition [ Upstream commit a7e282c77785c7eabf98836431b1f029481085ad ] The ratelimit logic in report_idle_softirq() is broken because the exit condition is always true: static int ratelimit; if (ratelimit < 10) return false; ---> always returns here ratelimit++; ---> no chance to run Make it check for >= 10 instead. Fixes: 0345691b24c0 ("tick/rcu: Stop allowing RCU_SOFTIRQ in idle") Signed-off-by: Wen Yang Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/tencent_5AAA3EEAB42095C9B7740BE62FBF9A67E007@qq.com Signed-off-by: Sasha Levin --- kernel/time/tick-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index d6fb6a676bbb..1ad89eec2a55 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -1046,7 +1046,7 @@ static bool report_idle_softirq(void) return false; } - if (ratelimit < 10) + if (ratelimit >= 10) return false; /* On RT, softirqs handling may be waiting on some lock */ -- cgit 1.4.1 From 2d3f42d22f32dde0ba565239e12edf39310739ce Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 18 Apr 2023 16:38:54 +0200 Subject: tracing/timer: Add missing hrtimer modes to decode_hrtimer_mode(). [ Upstream commit 2951580ba6adb082bb6b7154a5ecb24e7c1f7569 ] The trace output for the HRTIMER_MODE_.*_HARD modes is seen as a number since these modes are not decoded. The author was not aware of the fancy decoding function which makes the life easier. Extend decode_hrtimer_mode() with the additional HRTIMER_MODE_.*_HARD modes. Fixes: ae6683d815895 ("hrtimer: Introduce HARD expiry mode") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Reviewed-by: Mukesh Ojha Acked-by: Steven Rostedt (Google) Link: https://lore.kernel.org/r/20230418143854.8vHWQKLM@linutronix.de Signed-off-by: Sasha Levin --- include/trace/events/timer.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h index 3e8619c72f77..b4bc2828fa09 100644 --- a/include/trace/events/timer.h +++ b/include/trace/events/timer.h @@ -158,7 +158,11 @@ DEFINE_EVENT(timer_class, timer_cancel, { HRTIMER_MODE_ABS_SOFT, "ABS|SOFT" }, \ { HRTIMER_MODE_REL_SOFT, "REL|SOFT" }, \ { HRTIMER_MODE_ABS_PINNED_SOFT, "ABS|PINNED|SOFT" }, \ - { HRTIMER_MODE_REL_PINNED_SOFT, "REL|PINNED|SOFT" }) + { HRTIMER_MODE_REL_PINNED_SOFT, "REL|PINNED|SOFT" }, \ + { HRTIMER_MODE_ABS_HARD, "ABS|HARD" }, \ + { HRTIMER_MODE_REL_HARD, "REL|HARD" }, \ + { HRTIMER_MODE_ABS_PINNED_HARD, "ABS|PINNED|HARD" }, \ + { HRTIMER_MODE_REL_PINNED_HARD, "REL|PINNED|HARD" }) /** * hrtimer_init - called when the hrtimer is initialized -- cgit 1.4.1 From 919dd531ebb7514f205ae7aab87994337ebce1f6 Mon Sep 17 00:00:00 2001 From: Feng Mingxi Date: Tue, 25 Apr 2023 06:56:11 +0000 Subject: clocksource/drivers/cadence-ttc: Fix memory leak in ttc_timer_probe [ Upstream commit 8b5bf64c89c7100c921bd807ba39b2eb003061ab ] Smatch reports: drivers/clocksource/timer-cadence-ttc.c:529 ttc_timer_probe() warn: 'timer_baseaddr' from of_iomap() not released on lines: 498,508,516. timer_baseaddr may have the problem of not being released after use, I replaced it with the devm_of_iomap() function and added the clk_put() function to cleanup the "clk_ce" and "clk_cs". Fixes: e932900a3279 ("arm: zynq: Use standard timer binding") Fixes: 70504f311d4b ("clocksource/drivers/cadence_ttc: Convert init function to return error") Signed-off-by: Feng Mingxi Reviewed-by: Dongliang Mu Acked-by: Michal Simek Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20230425065611.702917-1-m202271825@hust.edu.cn Signed-off-by: Sasha Levin --- drivers/clocksource/timer-cadence-ttc.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/timer-cadence-ttc.c b/drivers/clocksource/timer-cadence-ttc.c index 4efd0cf3b602..0d52e28fea4d 100644 --- a/drivers/clocksource/timer-cadence-ttc.c +++ b/drivers/clocksource/timer-cadence-ttc.c @@ -486,10 +486,10 @@ static int __init ttc_timer_probe(struct platform_device *pdev) * and use it. Note that the event timer uses the interrupt and it's the * 2nd TTC hence the irq_of_parse_and_map(,1) */ - timer_baseaddr = of_iomap(timer, 0); - if (!timer_baseaddr) { + timer_baseaddr = devm_of_iomap(&pdev->dev, timer, 0, NULL); + if (IS_ERR(timer_baseaddr)) { pr_err("ERROR: invalid timer base address\n"); - return -ENXIO; + return PTR_ERR(timer_baseaddr); } irq = irq_of_parse_and_map(timer, 1); @@ -513,20 +513,27 @@ static int __init ttc_timer_probe(struct platform_device *pdev) clk_ce = of_clk_get(timer, clksel); if (IS_ERR(clk_ce)) { pr_err("ERROR: timer input clock not found\n"); - return PTR_ERR(clk_ce); + ret = PTR_ERR(clk_ce); + goto put_clk_cs; } ret = ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width); if (ret) - return ret; + goto put_clk_ce; ret = ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq); if (ret) - return ret; + goto put_clk_ce; pr_info("%pOFn #0 at %p, irq=%d\n", timer, timer_baseaddr, irq); return 0; + +put_clk_ce: + clk_put(clk_ce); +put_clk_cs: + clk_put(clk_cs); + return ret; } static const struct of_device_id ttc_timer_of_match[] = { -- cgit 1.4.1 From 6368a71dca09a227f26509b7e96d8a49aaf16ac8 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Tue, 18 Apr 2023 06:07:43 -0700 Subject: PM: domains: fix integer overflow issues in genpd_parse_state() [ Upstream commit e5d1c8722083f0332dcd3c85fa1273d85fb6bed8 ] Currently, while calculating residency and latency values, right operands may overflow if resulting values are big enough. To prevent this, albeit unlikely case, play it safe and convert right operands to left ones' type s64. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 30f604283e05 ("PM / Domains: Allow domain power states to be read from DT") Signed-off-by: Nikita Zhandarovich Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/base/power/domain.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index b411201f75bf..b33fc322e092 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2923,10 +2923,10 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, err = of_property_read_u32(state_node, "min-residency-us", &residency); if (!err) - genpd_state->residency_ns = 1000 * residency; + genpd_state->residency_ns = 1000LL * residency; - genpd_state->power_on_latency_ns = 1000 * exit_latency; - genpd_state->power_off_latency_ns = 1000 * entry_latency; + genpd_state->power_on_latency_ns = 1000LL * exit_latency; + genpd_state->power_off_latency_ns = 1000LL * entry_latency; genpd_state->fwnode = &state_node->fwnode; return 0; -- cgit 1.4.1 From 3e03681f072014f3f90dedfa32e0d7fed541ac9e Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 24 May 2023 17:44:32 +0100 Subject: perf/arm-cmn: Fix DTC reset [ Upstream commit 71746c995cac92fcf6a65661b51211cf2009d7f0 ] It turns out that my naive DTC reset logic fails to work as intended, since, after checking with the hardware designers, the PMU actually needs to be fully enabled in order to correctly clear any pending overflows. Therefore, invert the sequence to start with turning on both enables so that we can reliably get the DTCs into a known state, then moving to our normal counters-stopped state from there. Since all the DTM counters have already been unpaired during the initial discovery pass, we just need to additionally reset the cycle counters to ensure that no other unexpected overflows occur during this period. Fixes: 0ba64770a2f2 ("perf: Add Arm CMN-600 PMU driver") Reported-by: Geoff Blake Signed-off-by: Robin Murphy Link: https://lore.kernel.org/r/0ea4559261ea394f827c9aee5168c77a60aaee03.1684946389.git.robin.murphy@arm.com Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/perf/arm-cmn.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index ff86075edca4..90008e24d1cc 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -1898,9 +1898,10 @@ static int arm_cmn_init_dtc(struct arm_cmn *cmn, struct arm_cmn_node *dn, int id if (dtc->irq < 0) return dtc->irq; - writel_relaxed(0, dtc->base + CMN_DT_PMCR); + writel_relaxed(CMN_DT_DTC_CTL_DT_EN, dtc->base + CMN_DT_DTC_CTL); + writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN, dtc->base + CMN_DT_PMCR); + writeq_relaxed(0, dtc->base + CMN_DT_PMCCNTR); writel_relaxed(0x1ff, dtc->base + CMN_DT_PMOVSR_CLR); - writel_relaxed(CMN_DT_PMCR_OVFL_INTR_EN, dtc->base + CMN_DT_PMCR); return 0; } @@ -1960,7 +1961,7 @@ static int arm_cmn_init_dtcs(struct arm_cmn *cmn) dn->type = CMN_TYPE_CCLA; } - writel_relaxed(CMN_DT_DTC_CTL_DT_EN, cmn->dtc[0].base + CMN_DT_DTC_CTL); + arm_cmn_set_state(cmn, CMN_STATE_DISABLED); return 0; } -- cgit 1.4.1 From c598fefef3213b9905ac4e53ba6e72be5427128f Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Tue, 6 Jun 2023 12:56:20 +0300 Subject: x86/mm: Allow guest.enc_status_change_prepare() to fail [ Upstream commit 3f6819dd192ef4f0c568ec3e9d6d408b3fa1ad3d ] TDX code is going to provide guest.enc_status_change_prepare() that is able to fail. TDX will use the call to convert the GPA range from shared to private. This operation can fail. Add a way to return an error from the callback. Signed-off-by: Kirill A. Shutemov Signed-off-by: Dave Hansen Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/all/20230606095622.1939-2-kirill.shutemov%40linux.intel.com Stable-dep-of: 195edce08b63 ("x86/tdx: Fix race between set_memory_encrypted() and load_unaligned_zeropad()") Signed-off-by: Sasha Levin --- arch/x86/include/asm/x86_init.h | 2 +- arch/x86/kernel/x86_init.c | 2 +- arch/x86/mm/mem_encrypt_amd.c | 4 +++- arch/x86/mm/pat/set_memory.c | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index c1c8c581759d..034e62838b28 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -150,7 +150,7 @@ struct x86_init_acpi { * @enc_cache_flush_required Returns true if a cache flush is needed before changing page encryption status */ struct x86_guest { - void (*enc_status_change_prepare)(unsigned long vaddr, int npages, bool enc); + bool (*enc_status_change_prepare)(unsigned long vaddr, int npages, bool enc); bool (*enc_status_change_finish)(unsigned long vaddr, int npages, bool enc); bool (*enc_tlb_flush_required)(bool enc); bool (*enc_cache_flush_required)(void); diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index 10622cf2b30f..41e5b4cb898c 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -130,7 +130,7 @@ struct x86_cpuinit_ops x86_cpuinit = { static void default_nmi_init(void) { }; -static void enc_status_change_prepare_noop(unsigned long vaddr, int npages, bool enc) { } +static bool enc_status_change_prepare_noop(unsigned long vaddr, int npages, bool enc) { return true; } static bool enc_status_change_finish_noop(unsigned long vaddr, int npages, bool enc) { return false; } static bool enc_tlb_flush_required_noop(bool enc) { return false; } static bool enc_cache_flush_required_noop(void) { return false; } diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c index 9c4d8dbcb129..ff6c0462beee 100644 --- a/arch/x86/mm/mem_encrypt_amd.c +++ b/arch/x86/mm/mem_encrypt_amd.c @@ -319,7 +319,7 @@ static void enc_dec_hypercall(unsigned long vaddr, int npages, bool enc) #endif } -static void amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool enc) +static bool amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool enc) { /* * To maintain the security guarantees of SEV-SNP guests, make sure @@ -327,6 +327,8 @@ static void amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool */ if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP) && !enc) snp_set_memory_shared(vaddr, npages); + + return true; } /* Return true unconditionally: return value doesn't matter for the SEV side */ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index 2e5a045731de..5f0ce77a259d 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -2096,7 +2096,8 @@ static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc) cpa_flush(&cpa, x86_platform.guest.enc_cache_flush_required()); /* Notify hypervisor that we are about to set/clr encryption attribute. */ - x86_platform.guest.enc_status_change_prepare(addr, numpages, enc); + if (!x86_platform.guest.enc_status_change_prepare(addr, numpages, enc)) + return -EIO; ret = __change_page_attr_set_clr(&cpa, 1); -- cgit 1.4.1 From 6b025ec148e81edadc51ed219590e16f07eb17a7 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Tue, 6 Jun 2023 12:56:21 +0300 Subject: x86/tdx: Fix race between set_memory_encrypted() and load_unaligned_zeropad() [ Upstream commit 195edce08b63d293377f615f4f7f086715d2d212 ] tl;dr: There is a race in the TDX private<=>shared conversion code which could kill the TDX guest. Fix it by changing conversion ordering to eliminate the window. TDX hardware maintains metadata to track which pages are private and shared. Additionally, TDX guests use the guest x86 page tables to specify whether a given mapping is intended to be private or shared. Bad things happen when the intent and metadata do not match. So there are two thing in play: 1. "the page" -- the physical TDX page metadata 2. "the mapping" -- the guest-controlled x86 page table intent For instance, an unrecoverable exit to VMM occurs if a guest touches a private mapping that points to a shared physical page. In summary: * Private mapping => Private Page == OK (obviously) * Shared mapping => Shared Page == OK (obviously) * Private mapping => Shared Page == BIG BOOM! * Shared mapping => Private Page == OK-ish (It will read generate a recoverable #VE via handle_mmio()) Enter load_unaligned_zeropad(). It can touch memory that is adjacent but otherwise unrelated to the memory it needs to touch. It will cause one of those unrecoverable exits (aka. BIG BOOM) if it blunders into a shared mapping pointing to a private page. This is a problem when __set_memory_enc_pgtable() converts pages from shared to private. It first changes the mapping and second modifies the TDX page metadata. It's moving from: * Shared mapping => Shared Page == OK to: * Private mapping => Shared Page == BIG BOOM! This means that there is a window with a shared mapping pointing to a private page where load_unaligned_zeropad() can strike. Add a TDX handler for guest.enc_status_change_prepare(). This converts the page from shared to private *before* the page becomes private. This ensures that there is never a private mapping to a shared page. Leave a guest.enc_status_change_finish() in place but only use it for private=>shared conversions. This will delay updating the TDX metadata marking the page private until *after* the mapping matches the metadata. This also ensures that there is never a private mapping to a shared page. [ dhansen: rewrite changelog ] Fixes: 7dbde7631629 ("x86/mm/cpa: Add support for TDX shared memory") Signed-off-by: Kirill A. Shutemov Signed-off-by: Dave Hansen Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/all/20230606095622.1939-3-kirill.shutemov%40linux.intel.com Signed-off-by: Sasha Levin --- arch/x86/coco/tdx/tdx.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index b8998cf0508a..8a1d48b8c2a3 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -756,6 +756,30 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc) return true; } +static bool tdx_enc_status_change_prepare(unsigned long vaddr, int numpages, + bool enc) +{ + /* + * Only handle shared->private conversion here. + * See the comment in tdx_early_init(). + */ + if (enc) + return tdx_enc_status_changed(vaddr, numpages, enc); + return true; +} + +static bool tdx_enc_status_change_finish(unsigned long vaddr, int numpages, + bool enc) +{ + /* + * Only handle private->shared conversion here. + * See the comment in tdx_early_init(). + */ + if (!enc) + return tdx_enc_status_changed(vaddr, numpages, enc); + return true; +} + void __init tdx_early_init(void) { u64 cc_mask; @@ -780,9 +804,30 @@ void __init tdx_early_init(void) */ physical_mask &= cc_mask - 1; - x86_platform.guest.enc_cache_flush_required = tdx_cache_flush_required; - x86_platform.guest.enc_tlb_flush_required = tdx_tlb_flush_required; - x86_platform.guest.enc_status_change_finish = tdx_enc_status_changed; + /* + * The kernel mapping should match the TDX metadata for the page. + * load_unaligned_zeropad() can touch memory *adjacent* to that which is + * owned by the caller and can catch even _momentary_ mismatches. Bad + * things happen on mismatch: + * + * - Private mapping => Shared Page == Guest shutdown + * - Shared mapping => Private Page == Recoverable #VE + * + * guest.enc_status_change_prepare() converts the page from + * shared=>private before the mapping becomes private. + * + * guest.enc_status_change_finish() converts the page from + * private=>shared after the mapping becomes private. + * + * In both cases there is a temporary shared mapping to a private page, + * which can result in a #VE. But, there is never a private mapping to + * a shared page. + */ + x86_platform.guest.enc_status_change_prepare = tdx_enc_status_change_prepare; + x86_platform.guest.enc_status_change_finish = tdx_enc_status_change_finish; + + x86_platform.guest.enc_cache_flush_required = tdx_cache_flush_required; + x86_platform.guest.enc_tlb_flush_required = tdx_tlb_flush_required; pr_info("Guest detected\n"); } -- cgit 1.4.1 From be9c8c9c84b6d25a7b7d39954030aba6f759feb6 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Thu, 8 Jun 2023 19:43:26 +0800 Subject: drivers/perf: hisi: Don't migrate perf to the CPU going to teardown [ Upstream commit 7a6a9f1c5a0a875a421db798d4b2ee022dc1ee1a ] The driver needs to migrate the perf context if the current using CPU going to teardown. By the time calling the cpuhp::teardown() callback the cpu_online_mask() hasn't updated yet and still includes the CPU going to teardown. In current driver's implementation we may migrate the context to the teardown CPU and leads to the below calltrace: ... [ 368.104662][ T932] task:cpuhp/0 state:D stack: 0 pid: 15 ppid: 2 flags:0x00000008 [ 368.113699][ T932] Call trace: [ 368.116834][ T932] __switch_to+0x7c/0xbc [ 368.120924][ T932] __schedule+0x338/0x6f0 [ 368.125098][ T932] schedule+0x50/0xe0 [ 368.128926][ T932] schedule_preempt_disabled+0x18/0x24 [ 368.134229][ T932] __mutex_lock.constprop.0+0x1d4/0x5dc [ 368.139617][ T932] __mutex_lock_slowpath+0x1c/0x30 [ 368.144573][ T932] mutex_lock+0x50/0x60 [ 368.148579][ T932] perf_pmu_migrate_context+0x84/0x2b0 [ 368.153884][ T932] hisi_pcie_pmu_offline_cpu+0x90/0xe0 [hisi_pcie_pmu] [ 368.160579][ T932] cpuhp_invoke_callback+0x2a0/0x650 [ 368.165707][ T932] cpuhp_thread_fun+0xe4/0x190 [ 368.170316][ T932] smpboot_thread_fn+0x15c/0x1a0 [ 368.175099][ T932] kthread+0x108/0x13c [ 368.179012][ T932] ret_from_fork+0x10/0x18 ... Use function cpumask_any_but() to find one correct active cpu to fixes this issue. Fixes: 8404b0fbc7fb ("drivers/perf: hisi: Add driver for HiSilicon PCIe PMU") Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Reviewed-by: Yicong Yang Acked-by: Mark Rutland Link: https://lore.kernel.org/r/20230608114326.27649-1-hejunhao3@huawei.com Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/perf/hisilicon/hisi_pcie_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/perf/hisilicon/hisi_pcie_pmu.c b/drivers/perf/hisilicon/hisi_pcie_pmu.c index 071e63d9a9ac..b61f1f9aba21 100644 --- a/drivers/perf/hisilicon/hisi_pcie_pmu.c +++ b/drivers/perf/hisilicon/hisi_pcie_pmu.c @@ -672,7 +672,7 @@ static int hisi_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) pcie_pmu->on_cpu = -1; /* Choose a new CPU from all online cpus. */ - target = cpumask_first(cpu_online_mask); + target = cpumask_any_but(cpu_online_mask, cpu); if (target >= nr_cpu_ids) { pci_err(pcie_pmu->pdev, "There is no CPU to set\n"); return 0; -- cgit 1.4.1 From f1f5248ceddea92b2af17d4000679fde25c017f8 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 6 Jun 2023 22:00:00 +0800 Subject: powercap: RAPL: Fix CONFIG_IOSF_MBI dependency [ Upstream commit 4658fe81b3f8afe8adf37734ec5fe595d90415c6 ] After commit 3382388d7148 ("intel_rapl: abstract RAPL common code"), accessing to IOSF_MBI interface is done in the RAPL common code. Thus it is the CONFIG_INTEL_RAPL_CORE that has dependency of CONFIG_IOSF_MBI, while CONFIG_INTEL_RAPL_MSR does not. This problem was not exposed previously because all the previous RAPL common code users, aka, the RAPL MSR and MMIO I/F drivers, have CONFIG_IOSF_MBI selected. Fix the CONFIG_IOSF_MBI dependency in RAPL code. This also fixes a build time failure when the RAPL TPMI I/F driver is introduced without selecting CONFIG_IOSF_MBI. x86_64-linux-ld: vmlinux.o: in function `set_floor_freq_atom': intel_rapl_common.c:(.text+0x2dac9b8): undefined reference to `iosf_mbi_write' x86_64-linux-ld: intel_rapl_common.c:(.text+0x2daca66): undefined reference to `iosf_mbi_read' Reference to iosf_mbi.h is also removed from the RAPL MSR I/F driver. Fixes: 3382388d7148 ("intel_rapl: abstract RAPL common code") Reported-by: Arnd Bergmann Link: https://lore.kernel.org/all/20230601213246.3271412-1-arnd@kernel.org Signed-off-by: Zhang Rui Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/powercap/Kconfig | 4 +++- drivers/powercap/intel_rapl_msr.c | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index 515e3ceb3393..863e240b37e0 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -18,10 +18,12 @@ if POWERCAP # Client driver configurations go here. config INTEL_RAPL_CORE tristate + depends on PCI + select IOSF_MBI config INTEL_RAPL tristate "Intel RAPL Support via MSR Interface" - depends on X86 && IOSF_MBI + depends on X86 && PCI select INTEL_RAPL_CORE help This enables support for the Intel Running Average Power Limit (RAPL) diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c index bc6adda58883..65adb4cbaaf8 100644 --- a/drivers/powercap/intel_rapl_msr.c +++ b/drivers/powercap/intel_rapl_msr.c @@ -22,7 +22,6 @@ #include #include -#include #include #include -- cgit 1.4.1 From 3f9e54eb38ead726920d6247545818ce97213d3a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 30 May 2023 11:55:36 +0200 Subject: PM: domains: Move the verification of in-params from genpd_add_device() [ Upstream commit 4384a70c8813e8573d1841fd94eee873f80a7e1a ] Commit f38d1a6d0025 ("PM: domains: Allocate governor data dynamically based on a genpd governor") started to use the in-parameters in genpd_add_device(), without first doing a verification of them. This isn't really a big problem, as most callers do a verification already. Therefore, let's drop the verification from genpd_add_device() and make sure all the callers take care of it instead. Reported-by: Dan Carpenter Fixes: f38d1a6d0025 ("PM: domains: Allocate governor data dynamically based on a genpd governor") Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/base/power/domain.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index b33fc322e092..56ceba469802 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1624,9 +1624,6 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, dev_dbg(dev, "%s()\n", __func__); - if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) - return -EINVAL; - gpd_data = genpd_alloc_dev_data(dev, gd); if (IS_ERR(gpd_data)) return PTR_ERR(gpd_data); @@ -1668,6 +1665,9 @@ int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) { int ret; + if (!genpd || !dev) + return -EINVAL; + mutex_lock(&gpd_list_lock); ret = genpd_add_device(genpd, dev, dev); mutex_unlock(&gpd_list_lock); @@ -2514,6 +2514,9 @@ int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) struct generic_pm_domain *genpd; int ret; + if (!dev) + return -EINVAL; + mutex_lock(&gpd_list_lock); genpd = genpd_get_from_provider(genpdspec); -- cgit 1.4.1 From 50d64210eeb17f78686b8815f72dc78e55e28959 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Jun 2023 19:28:42 +0100 Subject: ARM: 9303/1: kprobes: avoid missing-declaration warnings [ Upstream commit 1b9c3ddcec6a55e15d3e38e7405e2d078db02020 ] checker_stack_use_t32strd() and kprobe_handler() can be made static since they are not used from other files, while coverage_start_registers() and __kprobes_test_case() are used from assembler code, and just need a declaration to avoid a warning with the global definition. arch/arm/probes/kprobes/checkers-common.c:43:18: error: no previous prototype for 'checker_stack_use_t32strd' arch/arm/probes/kprobes/core.c:236:16: error: no previous prototype for 'kprobe_handler' arch/arm/probes/kprobes/test-core.c:723:10: error: no previous prototype for 'coverage_start_registers' arch/arm/probes/kprobes/test-core.c:918:14: error: no previous prototype for '__kprobes_test_case_start' arch/arm/probes/kprobes/test-core.c:952:14: error: no previous prototype for '__kprobes_test_case_end_16' arch/arm/probes/kprobes/test-core.c:967:14: error: no previous prototype for '__kprobes_test_case_end_32' Fixes: 6624cf651f1a ("ARM: kprobes: collects stack consumption for store instructions") Fixes: 454f3e132d05 ("ARM/kprobes: Remove jprobe arm implementation") Acked-by: Masami Hiramatsu (Google) Reviewed-by: Kees Cook Signed-off-by: Arnd Bergmann Signed-off-by: Russell King (Oracle) Signed-off-by: Sasha Levin --- arch/arm/probes/kprobes/checkers-common.c | 2 +- arch/arm/probes/kprobes/core.c | 2 +- arch/arm/probes/kprobes/opt-arm.c | 2 -- arch/arm/probes/kprobes/test-core.c | 2 +- arch/arm/probes/kprobes/test-core.h | 4 ++++ 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/arm/probes/kprobes/checkers-common.c b/arch/arm/probes/kprobes/checkers-common.c index 4d720990cf2a..eba7ac4725c0 100644 --- a/arch/arm/probes/kprobes/checkers-common.c +++ b/arch/arm/probes/kprobes/checkers-common.c @@ -40,7 +40,7 @@ enum probes_insn checker_stack_use_imm_0xx(probes_opcode_t insn, * Different from other insn uses imm8, the real addressing offset of * STRD in T32 encoding should be imm8 * 4. See ARMARM description. */ -enum probes_insn checker_stack_use_t32strd(probes_opcode_t insn, +static enum probes_insn checker_stack_use_t32strd(probes_opcode_t insn, struct arch_probes_insn *asi, const struct decode_header *h) { diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c index 9090c3a74dcc..d8238da095df 100644 --- a/arch/arm/probes/kprobes/core.c +++ b/arch/arm/probes/kprobes/core.c @@ -233,7 +233,7 @@ singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) * kprobe, and that level is reserved for user kprobe handlers, so we can't * risk encountering a new kprobe in an interrupt handler. */ -void __kprobes kprobe_handler(struct pt_regs *regs) +static void __kprobes kprobe_handler(struct pt_regs *regs) { struct kprobe *p, *cur; struct kprobe_ctlblk *kcb; diff --git a/arch/arm/probes/kprobes/opt-arm.c b/arch/arm/probes/kprobes/opt-arm.c index dbef34ed933f..7f65048380ca 100644 --- a/arch/arm/probes/kprobes/opt-arm.c +++ b/arch/arm/probes/kprobes/opt-arm.c @@ -145,8 +145,6 @@ __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty) } } -extern void kprobe_handler(struct pt_regs *regs); - static void optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) { diff --git a/arch/arm/probes/kprobes/test-core.c b/arch/arm/probes/kprobes/test-core.c index c562832b8627..171c7076b89f 100644 --- a/arch/arm/probes/kprobes/test-core.c +++ b/arch/arm/probes/kprobes/test-core.c @@ -720,7 +720,7 @@ static const char coverage_register_lookup[16] = { [REG_TYPE_NOSPPCX] = COVERAGE_ANY_REG | COVERAGE_SP, }; -unsigned coverage_start_registers(const struct decode_header *h) +static unsigned coverage_start_registers(const struct decode_header *h) { unsigned regs = 0; int i; diff --git a/arch/arm/probes/kprobes/test-core.h b/arch/arm/probes/kprobes/test-core.h index 56ad3c0aaeea..c7297037c162 100644 --- a/arch/arm/probes/kprobes/test-core.h +++ b/arch/arm/probes/kprobes/test-core.h @@ -454,3 +454,7 @@ void kprobe_thumb32_test_cases(void); #else void kprobe_arm_test_cases(void); #endif + +void __kprobes_test_case_start(void); +void __kprobes_test_case_end_16(void); +void __kprobes_test_case_end_32(void); -- cgit 1.4.1 From 2b7e2251c434477bdf195ae87aa944b413b4398d Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 21 Jun 2023 09:58:39 +0300 Subject: cpufreq: intel_pstate: Fix energy_performance_preference for passive [ Upstream commit 03f44ffb3d5be2fceda375d92c70ab6de4df7081 ] If the intel_pstate driver is set to passive mode, then writing the same value to the energy_performance_preference sysfs twice will fail. This is caused by the wrong return value used (index of the matched energy_perf_string), instead of the length of the passed in parameter. Fix by forcing the internal return value to zero when the same preference is passed in by user. This same issue is not present when active mode is used for the driver. Fixes: f6ebbcf08f37 ("cpufreq: intel_pstate: Implement passive mode with HWP enabled") Reported-by: Niklas Neronin Signed-off-by: Tero Kristo Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/cpufreq/intel_pstate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6ff73c30769f..818eb2503cdd 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -837,6 +837,8 @@ static ssize_t store_energy_performance_preference( err = cpufreq_start_governor(policy); if (!ret) ret = err; + } else { + ret = 0; } } -- cgit 1.4.1 From 5d56a8d670a57402b244b8e70373a9fa6080f333 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 14 May 2023 20:46:05 +0200 Subject: thermal/drivers/sun8i: Fix some error handling paths in sun8i_ths_probe() [ Upstream commit 89382022b370dfd34eaae9c863baa123fcd4d132 ] Should an error occur after calling sun8i_ths_resource_init() in the probe function, some resources need to be released, as already done in the .remove() function. Switch to the devm_clk_get_enabled() helper and add a new devm_action to turn sun8i_ths_resource_init() into a fully managed function. Move the place where reset_control_deassert() is called so that the recommended order of reset release/clock enable steps is kept. A64 manual states that: 3.3.6.4. Gating and reset Make sure that the reset signal has been released before the release of module clock gating; This fixes the issue and removes some LoC at the same time. Fixes: dccc5c3b6f30 ("thermal/drivers/sun8i: Add thermal driver for H6/H5/H3/A64/A83T/R40") Signed-off-by: Christophe JAILLET Acked-by: Maxime Ripard Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/a8ae84bd2dc4b55fe428f8e20f31438bf8bb6762.1684089931.git.christophe.jaillet@wanadoo.fr Signed-off-by: Sasha Levin --- drivers/thermal/sun8i_thermal.c | 55 ++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index e64d06d1328c..80614b6adfac 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -319,6 +319,11 @@ out: return ret; } +static void sun8i_ths_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + static int sun8i_ths_resource_init(struct ths_device *tmdev) { struct device *dev = tmdev->dev; @@ -339,47 +344,35 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev) if (IS_ERR(tmdev->reset)) return PTR_ERR(tmdev->reset); - tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus"); + ret = reset_control_deassert(tmdev->reset); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, sun8i_ths_reset_control_assert, + tmdev->reset); + if (ret) + return ret; + + tmdev->bus_clk = devm_clk_get_enabled(&pdev->dev, "bus"); if (IS_ERR(tmdev->bus_clk)) return PTR_ERR(tmdev->bus_clk); } if (tmdev->chip->has_mod_clk) { - tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod"); + tmdev->mod_clk = devm_clk_get_enabled(&pdev->dev, "mod"); if (IS_ERR(tmdev->mod_clk)) return PTR_ERR(tmdev->mod_clk); } - ret = reset_control_deassert(tmdev->reset); - if (ret) - return ret; - - ret = clk_prepare_enable(tmdev->bus_clk); - if (ret) - goto assert_reset; - ret = clk_set_rate(tmdev->mod_clk, 24000000); if (ret) - goto bus_disable; - - ret = clk_prepare_enable(tmdev->mod_clk); - if (ret) - goto bus_disable; + return ret; ret = sun8i_ths_calibrate(tmdev); if (ret) - goto mod_disable; + return ret; return 0; - -mod_disable: - clk_disable_unprepare(tmdev->mod_clk); -bus_disable: - clk_disable_unprepare(tmdev->bus_clk); -assert_reset: - reset_control_assert(tmdev->reset); - - return ret; } static int sun8i_h3_thermal_init(struct ths_device *tmdev) @@ -530,17 +523,6 @@ static int sun8i_ths_probe(struct platform_device *pdev) return 0; } -static int sun8i_ths_remove(struct platform_device *pdev) -{ - struct ths_device *tmdev = platform_get_drvdata(pdev); - - clk_disable_unprepare(tmdev->mod_clk); - clk_disable_unprepare(tmdev->bus_clk); - reset_control_assert(tmdev->reset); - - return 0; -} - static const struct ths_thermal_chip sun8i_a83t_ths = { .sensor_num = 3, .scale = 705, @@ -642,7 +624,6 @@ MODULE_DEVICE_TABLE(of, of_ths_match); static struct platform_driver ths_driver = { .probe = sun8i_ths_probe, - .remove = sun8i_ths_remove, .driver = { .name = "sun8i-thermal", .of_match_table = of_ths_match, -- cgit 1.4.1 From d58f0f0ce6332ffeb406540295cc49732c26fb51 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 27 Apr 2023 10:50:47 -0700 Subject: rcu: Make rcu_cpu_starting() rely on interrupts being disabled [ Upstream commit 15d44dfa40305da1648de4bf001e91cc63148725 ] Currently, rcu_cpu_starting() is written so that it might be invoked with interrupts enabled. However, it is always called when interrupts are disabled, either by rcu_init(), notify_cpu_starting(), or from a call point prior to the call to notify_cpu_starting(). But why bother requiring that interrupts be disabled? The purpose is to allow the rcu_data structure's ->beenonline flag to be set after all early processing has completed for the incoming CPU, thus allowing this flag to be used to determine when workqueues have been set up for the incoming CPU, while still allowing this flag to be used as a diagnostic within rcu_core(). This commit therefore makes rcu_cpu_starting() rely on interrupts being disabled. Signed-off-by: Paul E. McKenney Stable-dep-of: 401b0de3ae4f ("rcu-tasks: Stop rcu_tasks_invoke_cbs() from using never-onlined CPUs") Signed-off-by: Sasha Levin --- kernel/rcu/tree.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index d03122f90cc4..5b5ed772f620 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4223,15 +4223,16 @@ int rcutree_offline_cpu(unsigned int cpu) * Note that this function is special in that it is invoked directly * from the incoming CPU rather than from the cpuhp_step mechanism. * This is because this function must be invoked at a precise location. + * This incoming CPU must not have enabled interrupts yet. */ void rcu_cpu_starting(unsigned int cpu) { - unsigned long flags; unsigned long mask; struct rcu_data *rdp; struct rcu_node *rnp; bool newcpu; + lockdep_assert_irqs_disabled(); rdp = per_cpu_ptr(&rcu_data, cpu); if (rdp->cpu_started) return; @@ -4239,7 +4240,6 @@ void rcu_cpu_starting(unsigned int cpu) rnp = rdp->mynode; mask = rdp->grpmask; - local_irq_save(flags); arch_spin_lock(&rcu_state.ofl_lock); rcu_dynticks_eqs_online(); raw_spin_lock(&rcu_state.barrier_lock); @@ -4258,17 +4258,16 @@ void rcu_cpu_starting(unsigned int cpu) /* An incoming CPU should never be blocking a grace period. */ if (WARN_ON_ONCE(rnp->qsmask & mask)) { /* RCU waiting on incoming CPU? */ /* rcu_report_qs_rnp() *really* wants some flags to restore */ - unsigned long flags2; + unsigned long flags; - local_irq_save(flags2); + local_irq_save(flags); rcu_disable_urgency_upon_qs(rdp); /* Report QS -after- changing ->qsmaskinitnext! */ - rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags2); + rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags); } else { raw_spin_unlock_rcu_node(rnp); } arch_spin_unlock(&rcu_state.ofl_lock); - local_irq_restore(flags); smp_mb(); /* Ensure RCU read-side usage follows above initialization. */ } -- cgit 1.4.1 From b1cdc56bc177c2e182c204bb08ad4e87bfd67942 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 26 Apr 2023 11:11:29 -0700 Subject: rcu-tasks: Stop rcu_tasks_invoke_cbs() from using never-onlined CPUs [ Upstream commit 401b0de3ae4fa49d1014c8941e26d9a25f37e7cf ] The rcu_tasks_invoke_cbs() function relies on queue_work_on() to silently fall back to WORK_CPU_UNBOUND when the specified CPU is offline. However, the queue_work_on() function's silent fallback mechanism relies on that CPU having been online at some time in the past. When queue_work_on() is passed a CPU that has never been online, workqueue lockups ensue, which can be bad for your kernel's general health and well-being. This commit therefore checks whether a given CPU has ever been online, and, if not substitutes WORK_CPU_UNBOUND in the subsequent call to queue_work_on(). Why not simply omit the queue_work_on() call entirely? Because this function is flooding callback-invocation notifications to all CPUs, and must deal with possibilities that include a sparse cpu_possible_mask. This commit also moves the setting of the rcu_data structure's ->beenonline field to rcu_cpu_starting(), which executes on the incoming CPU before that CPU has ever enabled interrupts. This ensures that the required workqueues are present. In addition, because the incoming CPU has not yet enabled its interrupts, there cannot yet have been any softirq handlers running on this CPU, which means that the WARN_ON_ONCE(!rdp->beenonline) within the RCU_SOFTIRQ handler cannot have triggered yet. Fixes: d363f833c6d88 ("rcu-tasks: Use workqueues for multiple rcu_tasks_invoke_cbs() invocations") Reported-by: Tejun Heo Signed-off-by: Paul E. McKenney Signed-off-by: Sasha Levin --- kernel/rcu/rcu.h | 6 ++++++ kernel/rcu/tasks.h | 7 +++++-- kernel/rcu/tree.c | 12 +++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h index be5979da07f5..48d8f754b730 100644 --- a/kernel/rcu/rcu.h +++ b/kernel/rcu/rcu.h @@ -583,4 +583,10 @@ void show_rcu_tasks_trace_gp_kthread(void); static inline void show_rcu_tasks_trace_gp_kthread(void) {} #endif +#ifdef CONFIG_TINY_RCU +static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; } +#else +bool rcu_cpu_beenfullyonline(int cpu); +#endif + #endif /* __LINUX_RCU_H */ diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h index c8409601fec3..df968321fead 100644 --- a/kernel/rcu/tasks.h +++ b/kernel/rcu/tasks.h @@ -455,6 +455,7 @@ static void rcu_tasks_invoke_cbs(struct rcu_tasks *rtp, struct rcu_tasks_percpu { int cpu; int cpunext; + int cpuwq; unsigned long flags; int len; struct rcu_head *rhp; @@ -465,11 +466,13 @@ static void rcu_tasks_invoke_cbs(struct rcu_tasks *rtp, struct rcu_tasks_percpu cpunext = cpu * 2 + 1; if (cpunext < smp_load_acquire(&rtp->percpu_dequeue_lim)) { rtpcp_next = per_cpu_ptr(rtp->rtpcpu, cpunext); - queue_work_on(cpunext, system_wq, &rtpcp_next->rtp_work); + cpuwq = rcu_cpu_beenfullyonline(cpunext) ? cpunext : WORK_CPU_UNBOUND; + queue_work_on(cpuwq, system_wq, &rtpcp_next->rtp_work); cpunext++; if (cpunext < smp_load_acquire(&rtp->percpu_dequeue_lim)) { rtpcp_next = per_cpu_ptr(rtp->rtpcpu, cpunext); - queue_work_on(cpunext, system_wq, &rtpcp_next->rtp_work); + cpuwq = rcu_cpu_beenfullyonline(cpunext) ? cpunext : WORK_CPU_UNBOUND; + queue_work_on(cpuwq, system_wq, &rtpcp_next->rtp_work); } } diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 5b5ed772f620..917a1e43f783 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4138,7 +4138,6 @@ int rcutree_prepare_cpu(unsigned int cpu) */ rnp = rdp->mynode; raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */ - rdp->beenonline = true; /* We have now been online. */ rdp->gp_seq = READ_ONCE(rnp->gp_seq); rdp->gp_seq_needed = rdp->gp_seq; rdp->cpu_no_qs.b.norm = true; @@ -4165,6 +4164,16 @@ static void rcutree_affinity_setting(unsigned int cpu, int outgoing) rcu_boost_kthread_setaffinity(rdp->mynode, outgoing); } +/* + * Has the specified (known valid) CPU ever been fully online? + */ +bool rcu_cpu_beenfullyonline(int cpu) +{ + struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); + + return smp_load_acquire(&rdp->beenonline); +} + /* * Near the end of the CPU-online process. Pretty much all services * enabled, and the CPU is now very much alive. @@ -4268,6 +4277,7 @@ void rcu_cpu_starting(unsigned int cpu) raw_spin_unlock_rcu_node(rnp); } arch_spin_unlock(&rcu_state.ofl_lock); + smp_store_release(&rdp->beenonline, true); smp_mb(); /* Ensure RCU read-side usage follows above initialization. */ } -- cgit 1.4.1 From a0a1f1c9248803614b7528c1464c8856700534dd Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 21 Mar 2023 16:40:08 -0700 Subject: rcutorture: Correct name of use_softirq module parameter [ Upstream commit b409afe0268faeb77267f028ea85f2d93438fced ] The BUSTED-BOOST and TREE03 scenarios specify a mythical tree.use_softirq module parameter, which means a failure to get full test coverage. This commit therefore corrects the name to rcutree.use_softirq. Fixes: e2b949d54392 ("rcutorture: Make TREE03 use real-time tree.use_softirq setting") Signed-off-by: Paul E. McKenney Reviewed-by: Joel Fernandes (Google) Signed-off-by: Sasha Levin --- tools/testing/selftests/rcutorture/configs/rcu/BUSTED-BOOST.boot | 2 +- tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/rcutorture/configs/rcu/BUSTED-BOOST.boot b/tools/testing/selftests/rcutorture/configs/rcu/BUSTED-BOOST.boot index f57720c52c0f..84f6bb98ce99 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/BUSTED-BOOST.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/BUSTED-BOOST.boot @@ -5,4 +5,4 @@ rcutree.gp_init_delay=3 rcutree.gp_cleanup_delay=3 rcutree.kthread_prio=2 threadirqs -tree.use_softirq=0 +rcutree.use_softirq=0 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot index 64f864f1f361..8e50bfd4b710 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot @@ -4,4 +4,4 @@ rcutree.gp_init_delay=3 rcutree.gp_cleanup_delay=3 rcutree.kthread_prio=2 threadirqs -tree.use_softirq=0 +rcutree.use_softirq=0 -- cgit 1.4.1 From 7a349221940823470a85210e0ff315f8abe8828c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 31 Jan 2023 12:08:54 -0800 Subject: rcuscale: Move shutdown from wait_event() to wait_event_idle() [ Upstream commit ef1ef3d47677dc191b88650a9f7f91413452cc1b ] The rcu_scale_shutdown() and kfree_scale_shutdown() kthreads/functions use wait_event() to wait for the rcuscale test to complete. However, each updater thread in such a test waits for at least 100 grace periods. If each grace period takes more than 1.2 seconds, which is long, but not insanely so, this can trigger the hung-task timeout. This commit therefore replaces those wait_event() calls with calls to wait_event_idle(), which do not trigger the hung-task timeout. Reported-by: kernel test robot Reported-by: Liam Howlett Signed-off-by: Paul E. McKenney Tested-by: Yujie Liu Signed-off-by: Boqun Feng Stable-dep-of: 23fc8df26dea ("rcu/rcuscale: Stop kfree_scale_thread thread(s) after unloading rcuscale") Signed-off-by: Sasha Levin --- kernel/rcu/rcuscale.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/rcu/rcuscale.c b/kernel/rcu/rcuscale.c index 3ef02d4a8108..de4d4e3844bb 100644 --- a/kernel/rcu/rcuscale.c +++ b/kernel/rcu/rcuscale.c @@ -630,8 +630,7 @@ static int compute_real(int n) static int rcu_scale_shutdown(void *arg) { - wait_event(shutdown_wq, - atomic_read(&n_rcu_scale_writer_finished) >= nrealwriters); + wait_event_idle(shutdown_wq, atomic_read(&n_rcu_scale_writer_finished) >= nrealwriters); smp_mb(); /* Wake before output. */ rcu_scale_cleanup(); kernel_power_off(); @@ -757,8 +756,8 @@ kfree_scale_cleanup(void) static int kfree_scale_shutdown(void *arg) { - wait_event(shutdown_wq, - atomic_read(&n_kfree_scale_thread_ended) >= kfree_nrealthreads); + wait_event_idle(shutdown_wq, + atomic_read(&n_kfree_scale_thread_ended) >= kfree_nrealthreads); smp_mb(); /* Wake before output. */ -- cgit 1.4.1 From 3506e64ec161aa79a63a015730cc63051a55fc8d Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Wed, 22 Mar 2023 19:42:40 +0800 Subject: rcu/rcuscale: Move rcu_scale_*() after kfree_scale_cleanup() [ Upstream commit bf5ddd736509a7d9077c0b6793e6f0852214dbea ] This code-movement-only commit moves the rcu_scale_cleanup() and rcu_scale_shutdown() functions to follow kfree_scale_cleanup(). This is code movement is in preparation for a bug-fix patch that invokes kfree_scale_cleanup() from rcu_scale_cleanup(). Signed-off-by: Qiuxu Zhuo Signed-off-by: Paul E. McKenney Reviewed-by: Joel Fernandes (Google) Stable-dep-of: 23fc8df26dea ("rcu/rcuscale: Stop kfree_scale_thread thread(s) after unloading rcuscale") Signed-off-by: Sasha Levin --- kernel/rcu/rcuscale.c | 194 +++++++++++++++++++++++++------------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/kernel/rcu/rcuscale.c b/kernel/rcu/rcuscale.c index de4d4e3844bb..63e4c2d629a9 100644 --- a/kernel/rcu/rcuscale.c +++ b/kernel/rcu/rcuscale.c @@ -521,89 +521,6 @@ rcu_scale_print_module_parms(struct rcu_scale_ops *cur_ops, const char *tag) scale_type, tag, nrealreaders, nrealwriters, verbose, shutdown); } -static void -rcu_scale_cleanup(void) -{ - int i; - int j; - int ngps = 0; - u64 *wdp; - u64 *wdpp; - - /* - * Would like warning at start, but everything is expedited - * during the mid-boot phase, so have to wait till the end. - */ - if (rcu_gp_is_expedited() && !rcu_gp_is_normal() && !gp_exp) - SCALEOUT_ERRSTRING("All grace periods expedited, no normal ones to measure!"); - if (rcu_gp_is_normal() && gp_exp) - SCALEOUT_ERRSTRING("All grace periods normal, no expedited ones to measure!"); - if (gp_exp && gp_async) - SCALEOUT_ERRSTRING("No expedited async GPs, so went with async!"); - - if (torture_cleanup_begin()) - return; - if (!cur_ops) { - torture_cleanup_end(); - return; - } - - if (reader_tasks) { - for (i = 0; i < nrealreaders; i++) - torture_stop_kthread(rcu_scale_reader, - reader_tasks[i]); - kfree(reader_tasks); - } - - if (writer_tasks) { - for (i = 0; i < nrealwriters; i++) { - torture_stop_kthread(rcu_scale_writer, - writer_tasks[i]); - if (!writer_n_durations) - continue; - j = writer_n_durations[i]; - pr_alert("%s%s writer %d gps: %d\n", - scale_type, SCALE_FLAG, i, j); - ngps += j; - } - pr_alert("%s%s start: %llu end: %llu duration: %llu gps: %d batches: %ld\n", - scale_type, SCALE_FLAG, - t_rcu_scale_writer_started, t_rcu_scale_writer_finished, - t_rcu_scale_writer_finished - - t_rcu_scale_writer_started, - ngps, - rcuscale_seq_diff(b_rcu_gp_test_finished, - b_rcu_gp_test_started)); - for (i = 0; i < nrealwriters; i++) { - if (!writer_durations) - break; - if (!writer_n_durations) - continue; - wdpp = writer_durations[i]; - if (!wdpp) - continue; - for (j = 0; j < writer_n_durations[i]; j++) { - wdp = &wdpp[j]; - pr_alert("%s%s %4d writer-duration: %5d %llu\n", - scale_type, SCALE_FLAG, - i, j, *wdp); - if (j % 100 == 0) - schedule_timeout_uninterruptible(1); - } - kfree(writer_durations[i]); - } - kfree(writer_tasks); - kfree(writer_durations); - kfree(writer_n_durations); - } - - /* Do torture-type-specific cleanup operations. */ - if (cur_ops->cleanup != NULL) - cur_ops->cleanup(); - - torture_cleanup_end(); -} - /* * Return the number if non-negative. If -1, the number of CPUs. * If less than -1, that much less than the number of CPUs, but @@ -623,20 +540,6 @@ static int compute_real(int n) return nr; } -/* - * RCU scalability shutdown kthread. Just waits to be awakened, then shuts - * down system. - */ -static int -rcu_scale_shutdown(void *arg) -{ - wait_event_idle(shutdown_wq, atomic_read(&n_rcu_scale_writer_finished) >= nrealwriters); - smp_mb(); /* Wake before output. */ - rcu_scale_cleanup(); - kernel_power_off(); - return -EINVAL; -} - /* * kfree_rcu() scalability tests: Start a kfree_rcu() loop on all CPUs for number * of iterations and measure total time and number of GP for all iterations to complete. @@ -811,6 +714,103 @@ unwind: return firsterr; } +static void +rcu_scale_cleanup(void) +{ + int i; + int j; + int ngps = 0; + u64 *wdp; + u64 *wdpp; + + /* + * Would like warning at start, but everything is expedited + * during the mid-boot phase, so have to wait till the end. + */ + if (rcu_gp_is_expedited() && !rcu_gp_is_normal() && !gp_exp) + SCALEOUT_ERRSTRING("All grace periods expedited, no normal ones to measure!"); + if (rcu_gp_is_normal() && gp_exp) + SCALEOUT_ERRSTRING("All grace periods normal, no expedited ones to measure!"); + if (gp_exp && gp_async) + SCALEOUT_ERRSTRING("No expedited async GPs, so went with async!"); + + if (torture_cleanup_begin()) + return; + if (!cur_ops) { + torture_cleanup_end(); + return; + } + + if (reader_tasks) { + for (i = 0; i < nrealreaders; i++) + torture_stop_kthread(rcu_scale_reader, + reader_tasks[i]); + kfree(reader_tasks); + } + + if (writer_tasks) { + for (i = 0; i < nrealwriters; i++) { + torture_stop_kthread(rcu_scale_writer, + writer_tasks[i]); + if (!writer_n_durations) + continue; + j = writer_n_durations[i]; + pr_alert("%s%s writer %d gps: %d\n", + scale_type, SCALE_FLAG, i, j); + ngps += j; + } + pr_alert("%s%s start: %llu end: %llu duration: %llu gps: %d batches: %ld\n", + scale_type, SCALE_FLAG, + t_rcu_scale_writer_started, t_rcu_scale_writer_finished, + t_rcu_scale_writer_finished - + t_rcu_scale_writer_started, + ngps, + rcuscale_seq_diff(b_rcu_gp_test_finished, + b_rcu_gp_test_started)); + for (i = 0; i < nrealwriters; i++) { + if (!writer_durations) + break; + if (!writer_n_durations) + continue; + wdpp = writer_durations[i]; + if (!wdpp) + continue; + for (j = 0; j < writer_n_durations[i]; j++) { + wdp = &wdpp[j]; + pr_alert("%s%s %4d writer-duration: %5d %llu\n", + scale_type, SCALE_FLAG, + i, j, *wdp); + if (j % 100 == 0) + schedule_timeout_uninterruptible(1); + } + kfree(writer_durations[i]); + } + kfree(writer_tasks); + kfree(writer_durations); + kfree(writer_n_durations); + } + + /* Do torture-type-specific cleanup operations. */ + if (cur_ops->cleanup != NULL) + cur_ops->cleanup(); + + torture_cleanup_end(); +} + +/* + * RCU scalability shutdown kthread. Just waits to be awakened, then shuts + * down system. + */ +static int +rcu_scale_shutdown(void *arg) +{ + wait_event_idle(shutdown_wq, atomic_read(&n_rcu_scale_writer_finished) >= nrealwriters); + smp_mb(); /* Wake before output. */ + rcu_scale_cleanup(); + kernel_power_off(); + return -EINVAL; +} + static int __init rcu_scale_init(void) { -- cgit 1.4.1 From b8a6ba524d41f4da102e65f90498d9a910839621 Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Wed, 22 Mar 2023 19:42:41 +0800 Subject: rcu/rcuscale: Stop kfree_scale_thread thread(s) after unloading rcuscale [ Upstream commit 23fc8df26dead16687ae6eb47b0561a4a832e2f6 ] Running the 'kfree_rcu_test' test case [1] results in a splat [2]. The root cause is the kfree_scale_thread thread(s) continue running after unloading the rcuscale module. This commit fixes that isue by invoking kfree_scale_cleanup() from rcu_scale_cleanup() when removing the rcuscale module. [1] modprobe rcuscale kfree_rcu_test=1 // After some time rmmod rcuscale rmmod torture [2] BUG: unable to handle page fault for address: ffffffffc0601a87 #PF: supervisor instruction fetch in kernel mode #PF: error_code(0x0010) - not-present page PGD 11de4f067 P4D 11de4f067 PUD 11de51067 PMD 112f4d067 PTE 0 Oops: 0010 [#1] PREEMPT SMP NOPTI CPU: 1 PID: 1798 Comm: kfree_scale_thr Not tainted 6.3.0-rc1-rcu+ #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 0.0.0 02/06/2015 RIP: 0010:0xffffffffc0601a87 Code: Unable to access opcode bytes at 0xffffffffc0601a5d. RSP: 0018:ffffb25bc2e57e18 EFLAGS: 00010297 RAX: 0000000000000000 RBX: ffffffffc061f0b6 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffffff962fd0de RDI: ffffffff962fd0de RBP: ffffb25bc2e57ea8 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000001 R12: 0000000000000000 R13: 0000000000000000 R14: 000000000000000a R15: 00000000001c1dbe FS: 0000000000000000(0000) GS:ffff921fa2200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffffffc0601a5d CR3: 000000011de4c006 CR4: 0000000000370ee0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: ? kvfree_call_rcu+0xf0/0x3a0 ? kthread+0xf3/0x120 ? kthread_complete_and_exit+0x20/0x20 ? ret_from_fork+0x1f/0x30 Modules linked in: rfkill sunrpc ... [last unloaded: torture] CR2: ffffffffc0601a87 ---[ end trace 0000000000000000 ]--- Fixes: e6e78b004fa7 ("rcuperf: Add kfree_rcu() performance Tests") Reviewed-by: Davidlohr Bueso Reviewed-by: Joel Fernandes (Google) Signed-off-by: Qiuxu Zhuo Signed-off-by: Paul E. McKenney Signed-off-by: Sasha Levin --- kernel/rcu/rcuscale.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/rcu/rcuscale.c b/kernel/rcu/rcuscale.c index 63e4c2d629a9..7854dc3226e1 100644 --- a/kernel/rcu/rcuscale.c +++ b/kernel/rcu/rcuscale.c @@ -734,6 +734,11 @@ rcu_scale_cleanup(void) if (gp_exp && gp_async) SCALEOUT_ERRSTRING("No expedited async GPs, so went with async!"); + if (kfree_rcu_test) { + kfree_scale_cleanup(); + return; + } + if (torture_cleanup_begin()) return; if (!cur_ops) { -- cgit 1.4.1 From 64d09c0e832d261f028884c91c1c505e237052ae Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 17 Apr 2023 11:47:43 +0100 Subject: kselftest: vDSO: Fix accumulation of uninitialized ret when CLOCK_REALTIME is undefined [ Upstream commit 375b9ff53cb6f9c042817b75f2be0a650626dc4f ] In the unlikely case that CLOCK_REALTIME is not defined, variable ret is not initialized and further accumulation of return values to ret can leave ret in an undefined state. Fix this by initialized ret to zero and changing the assignment of ret to an accumulation for the CLOCK_REALTIME case. Fixes: 03f55c7952c9 ("kselftest: Extend vDSO selftest to clock_getres") Signed-off-by: Colin Ian King Reviewed-by: Vincenzo Frascino Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- tools/testing/selftests/vDSO/vdso_test_clock_getres.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/vDSO/vdso_test_clock_getres.c b/tools/testing/selftests/vDSO/vdso_test_clock_getres.c index 15dcee16ff72..38d46a8bf7cb 100644 --- a/tools/testing/selftests/vDSO/vdso_test_clock_getres.c +++ b/tools/testing/selftests/vDSO/vdso_test_clock_getres.c @@ -84,12 +84,12 @@ static inline int vdso_test_clock(unsigned int clock_id) int main(int argc, char **argv) { - int ret; + int ret = 0; #if _POSIX_TIMERS > 0 #ifdef CLOCK_REALTIME - ret = vdso_test_clock(CLOCK_REALTIME); + ret += vdso_test_clock(CLOCK_REALTIME); #endif #ifdef CLOCK_BOOTTIME -- cgit 1.4.1 From 3745f628c3288a6e77851de5ed408bf99a05e5db Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 4 May 2023 16:30:01 +0530 Subject: perf/ibs: Fix interface via core pmu events [ Upstream commit 2fad201fe38ff9a692acedb1990ece2c52a29f95 ] Although, IBS pmus can be invoked via their own interface, indirect IBS invocation via core pmu events is also supported with fixed set of events: cpu-cycles:p, r076:p (same as cpu-cycles:p) and r0C1:p (micro-ops) for user convenience. This indirect IBS invocation is broken since commit 66d258c5b048 ("perf/core: Optimize perf_init_event()"), which added RAW pmu under 'pmu_idr' list and thus if event_init() fails with RAW pmu, it started returning error instead of trying other pmus. Forward precise events from core pmu to IBS by overwriting 'type' and 'config' in the kernel copy of perf_event_attr. Overwriting will cause perf_init_event() to retry with updated 'type' and 'config', which will automatically forward event to IBS pmu. Without patch: $ sudo ./perf record -C 0 -e r076:p -- sleep 1 Error: The r076:p event is not supported. With patch: $ sudo ./perf record -C 0 -e r076:p -- sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.341 MB perf.data (37 samples) ] Fixes: 66d258c5b048 ("perf/core: Optimize perf_init_event()") Reported-by: Stephane Eranian Signed-off-by: Ravi Bangoria Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20230504110003.2548-3-ravi.bangoria@amd.com Signed-off-by: Sasha Levin --- arch/x86/events/amd/core.c | 2 +- arch/x86/events/amd/ibs.c | 53 +++++++++++++++++++-------------------- arch/x86/include/asm/perf_event.h | 2 ++ 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index 8ca5e827f30b..6672a3f05fc6 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -374,7 +374,7 @@ static int amd_pmu_hw_config(struct perf_event *event) /* pass precise event sampling to ibs: */ if (event->attr.precise_ip && get_ibs_caps()) - return -ENOENT; + return forward_event_to_ibs(event); if (has_branch_stack(event) && !x86_pmu.lbr_nr) return -EOPNOTSUPP; diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c index 4cb710efbdd9..37cbbc5c659a 100644 --- a/arch/x86/events/amd/ibs.c +++ b/arch/x86/events/amd/ibs.c @@ -190,7 +190,7 @@ static struct perf_ibs *get_ibs_pmu(int type) } /* - * Use IBS for precise event sampling: + * core pmu config -> IBS config * * perf record -a -e cpu-cycles:p ... # use ibs op counting cycle count * perf record -a -e r076:p ... # same as -e cpu-cycles:p @@ -199,25 +199,9 @@ static struct perf_ibs *get_ibs_pmu(int type) * IbsOpCntCtl (bit 19) of IBS Execution Control Register (IbsOpCtl, * MSRC001_1033) is used to select either cycle or micro-ops counting * mode. - * - * The rip of IBS samples has skid 0. Thus, IBS supports precise - * levels 1 and 2 and the PERF_EFLAGS_EXACT is set. In rare cases the - * rip is invalid when IBS was not able to record the rip correctly. - * We clear PERF_EFLAGS_EXACT and take the rip from pt_regs then. - * */ -static int perf_ibs_precise_event(struct perf_event *event, u64 *config) +static int core_pmu_ibs_config(struct perf_event *event, u64 *config) { - switch (event->attr.precise_ip) { - case 0: - return -ENOENT; - case 1: - case 2: - break; - default: - return -EOPNOTSUPP; - } - switch (event->attr.type) { case PERF_TYPE_HARDWARE: switch (event->attr.config) { @@ -243,22 +227,37 @@ static int perf_ibs_precise_event(struct perf_event *event, u64 *config) return -EOPNOTSUPP; } +/* + * The rip of IBS samples has skid 0. Thus, IBS supports precise + * levels 1 and 2 and the PERF_EFLAGS_EXACT is set. In rare cases the + * rip is invalid when IBS was not able to record the rip correctly. + * We clear PERF_EFLAGS_EXACT and take the rip from pt_regs then. + */ +int forward_event_to_ibs(struct perf_event *event) +{ + u64 config = 0; + + if (!event->attr.precise_ip || event->attr.precise_ip > 2) + return -EOPNOTSUPP; + + if (!core_pmu_ibs_config(event, &config)) { + event->attr.type = perf_ibs_op.pmu.type; + event->attr.config = config; + } + return -ENOENT; +} + static int perf_ibs_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct perf_ibs *perf_ibs; u64 max_cnt, config; - int ret; perf_ibs = get_ibs_pmu(event->attr.type); - if (perf_ibs) { - config = event->attr.config; - } else { - perf_ibs = &perf_ibs_op; - ret = perf_ibs_precise_event(event, &config); - if (ret) - return ret; - } + if (!perf_ibs) + return -ENOENT; + + config = event->attr.config; if (event->pmu != &perf_ibs->pmu) return -ENOENT; diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 5d0f6891ae61..4d810b9478a4 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -467,8 +467,10 @@ struct pebs_xmm { #ifdef CONFIG_X86_LOCAL_APIC extern u32 get_ibs_caps(void); +extern int forward_event_to_ibs(struct perf_event *event); #else static inline u32 get_ibs_caps(void) { return 0; } +static inline int forward_event_to_ibs(struct perf_event *event) { return -ENOENT; } #endif #ifdef CONFIG_PERF_EVENTS -- cgit 1.4.1 From 6b54f5c68474edc241434ab50bae4a08ce45f835 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Mon, 6 Mar 2023 13:32:59 +0100 Subject: x86/mm: Fix __swp_entry_to_pte() for Xen PV guests [ Upstream commit 0f88130e8a6fd185b0aeb5d8e286083735f2585a ] Normally __swp_entry_to_pte() is never called with a value translating to a valid PTE. The only known exception is pte_swap_tests(), resulting in a WARN splat in Xen PV guests, as __pte_to_swp_entry() did translate the PFN of the valid PTE to a guest local PFN, while __swp_entry_to_pte() doesn't do the opposite translation. Fix that by using __pte() in __swp_entry_to_pte() instead of open coding the native variant of it. For correctness do the similar conversion for __swp_entry_to_pmd(). Fixes: 05289402d717 ("mm/debug_vm_pgtable: add tests validating arch helpers for core MM features") Signed-off-by: Juergen Gross Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/r/20230306123259.12461-1-jgross@suse.com Signed-off-by: Sasha Levin --- arch/x86/include/asm/pgtable_64.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h index e479491da8d5..07cd53eeec77 100644 --- a/arch/x86/include/asm/pgtable_64.h +++ b/arch/x86/include/asm/pgtable_64.h @@ -237,8 +237,8 @@ static inline void native_pgd_clear(pgd_t *pgd) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val((pte)) }) #define __pmd_to_swp_entry(pmd) ((swp_entry_t) { pmd_val((pmd)) }) -#define __swp_entry_to_pte(x) ((pte_t) { .pte = (x).val }) -#define __swp_entry_to_pmd(x) ((pmd_t) { .pmd = (x).val }) +#define __swp_entry_to_pte(x) (__pte((x).val)) +#define __swp_entry_to_pmd(x) (__pmd((x).val)) extern int kern_addr_valid(unsigned long addr); extern void cleanup_highmap(void); -- cgit 1.4.1 From ea1432a402ab5d401ea66372a4438ad55061143c Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 5 Jun 2023 08:00:58 +0100 Subject: locking/atomic: arm: fix sync ops [ Upstream commit dda5f312bb09e56e7a1c3e3851f2000eb2e9c879 ] The sync_*() ops on arch/arm are defined in terms of the regular bitops with no special handling. This is not correct, as UP kernels elide barriers for the fully-ordered operations, and so the required ordering is lost when such UP kernels are run under a hypervsior on an SMP system. Fix this by defining sync ops with the required barriers. Note: On 32-bit arm, the sync_*() ops are currently only used by Xen, which requires ARMv7, but the semantics can be implemented for ARMv6+. Fixes: e54d2f61528165bb ("xen/arm: sync_bitops") Signed-off-by: Mark Rutland Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20230605070124.3741859-2-mark.rutland@arm.com Signed-off-by: Sasha Levin --- arch/arm/include/asm/assembler.h | 17 +++++++++++++++++ arch/arm/include/asm/sync_bitops.h | 29 +++++++++++++++++++++++++---- arch/arm/lib/bitops.h | 14 +++++++++++--- arch/arm/lib/testchangebit.S | 4 ++++ arch/arm/lib/testclearbit.S | 4 ++++ arch/arm/lib/testsetbit.S | 4 ++++ 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 90fbe4a3f9c8..84912b19cac8 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -402,6 +402,23 @@ ALT_UP_B(.L0_\@) #endif .endm +/* + * Raw SMP data memory barrier + */ + .macro __smp_dmb mode +#if __LINUX_ARM_ARCH__ >= 7 + .ifeqs "\mode","arm" + dmb ish + .else + W(dmb) ish + .endif +#elif __LINUX_ARM_ARCH__ == 6 + mcr p15, 0, r0, c7, c10, 5 @ dmb +#else + .error "Incompatible SMP platform" +#endif + .endm + #if defined(CONFIG_CPU_V7M) /* * setmode is used to assert to be in svc mode during boot. For v7-M diff --git a/arch/arm/include/asm/sync_bitops.h b/arch/arm/include/asm/sync_bitops.h index 6f5d627c44a3..f46b3c570f92 100644 --- a/arch/arm/include/asm/sync_bitops.h +++ b/arch/arm/include/asm/sync_bitops.h @@ -14,14 +14,35 @@ * ops which are SMP safe even on a UP kernel. */ +/* + * Unordered + */ + #define sync_set_bit(nr, p) _set_bit(nr, p) #define sync_clear_bit(nr, p) _clear_bit(nr, p) #define sync_change_bit(nr, p) _change_bit(nr, p) -#define sync_test_and_set_bit(nr, p) _test_and_set_bit(nr, p) -#define sync_test_and_clear_bit(nr, p) _test_and_clear_bit(nr, p) -#define sync_test_and_change_bit(nr, p) _test_and_change_bit(nr, p) #define sync_test_bit(nr, addr) test_bit(nr, addr) -#define arch_sync_cmpxchg arch_cmpxchg +/* + * Fully ordered + */ + +int _sync_test_and_set_bit(int nr, volatile unsigned long * p); +#define sync_test_and_set_bit(nr, p) _sync_test_and_set_bit(nr, p) + +int _sync_test_and_clear_bit(int nr, volatile unsigned long * p); +#define sync_test_and_clear_bit(nr, p) _sync_test_and_clear_bit(nr, p) + +int _sync_test_and_change_bit(int nr, volatile unsigned long * p); +#define sync_test_and_change_bit(nr, p) _sync_test_and_change_bit(nr, p) + +#define arch_sync_cmpxchg(ptr, old, new) \ +({ \ + __typeof__(*(ptr)) __ret; \ + __smp_mb__before_atomic(); \ + __ret = arch_cmpxchg_relaxed((ptr), (old), (new)); \ + __smp_mb__after_atomic(); \ + __ret; \ +}) #endif diff --git a/arch/arm/lib/bitops.h b/arch/arm/lib/bitops.h index 95bd35991288..f069d1b2318e 100644 --- a/arch/arm/lib/bitops.h +++ b/arch/arm/lib/bitops.h @@ -28,7 +28,7 @@ UNWIND( .fnend ) ENDPROC(\name ) .endm - .macro testop, name, instr, store + .macro __testop, name, instr, store, barrier ENTRY( \name ) UNWIND( .fnstart ) ands ip, r1, #3 @@ -38,7 +38,7 @@ UNWIND( .fnstart ) mov r0, r0, lsr #5 add r1, r1, r0, lsl #2 @ Get word offset mov r3, r2, lsl r3 @ create mask - smp_dmb + \barrier #if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) .arch_extension mp ALT_SMP(W(pldw) [r1]) @@ -50,13 +50,21 @@ UNWIND( .fnstart ) strex ip, r2, [r1] cmp ip, #0 bne 1b - smp_dmb + \barrier cmp r0, #0 movne r0, #1 2: bx lr UNWIND( .fnend ) ENDPROC(\name ) .endm + + .macro testop, name, instr, store + __testop \name, \instr, \store, smp_dmb + .endm + + .macro sync_testop, name, instr, store + __testop \name, \instr, \store, __smp_dmb + .endm #else .macro bitop, name, instr ENTRY( \name ) diff --git a/arch/arm/lib/testchangebit.S b/arch/arm/lib/testchangebit.S index 4ebecc67e6e0..f13fe9bc2399 100644 --- a/arch/arm/lib/testchangebit.S +++ b/arch/arm/lib/testchangebit.S @@ -10,3 +10,7 @@ .text testop _test_and_change_bit, eor, str + +#if __LINUX_ARM_ARCH__ >= 6 +sync_testop _sync_test_and_change_bit, eor, str +#endif diff --git a/arch/arm/lib/testclearbit.S b/arch/arm/lib/testclearbit.S index 009afa0f5b4a..4d2c5ca620eb 100644 --- a/arch/arm/lib/testclearbit.S +++ b/arch/arm/lib/testclearbit.S @@ -10,3 +10,7 @@ .text testop _test_and_clear_bit, bicne, strne + +#if __LINUX_ARM_ARCH__ >= 6 +sync_testop _sync_test_and_clear_bit, bicne, strne +#endif diff --git a/arch/arm/lib/testsetbit.S b/arch/arm/lib/testsetbit.S index f3192e55acc8..649dbab65d8d 100644 --- a/arch/arm/lib/testsetbit.S +++ b/arch/arm/lib/testsetbit.S @@ -10,3 +10,7 @@ .text testop _test_and_set_bit, orreq, streq + +#if __LINUX_ARM_ARCH__ >= 6 +sync_testop _sync_test_and_set_bit, orreq, streq +#endif -- cgit 1.4.1 From b050ade6e05b5a54606230f8d5630774359d371f Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Mon, 6 Mar 2023 11:40:36 +0100 Subject: evm: Complete description of evm_inode_setattr() [ Upstream commit b1de86d4248b273cb12c4cd7d20c08d459519f7d ] Add the description for missing parameters of evm_inode_setattr() to avoid the warning arising with W=n compile option. Fixes: 817b54aa45db ("evm: add evm_inode_setattr to prevent updating an invalid security.evm") # v3.2+ Fixes: c1632a0f1120 ("fs: port ->setattr() to pass mnt_idmap") # v6.3+ Signed-off-by: Roberto Sassu Reviewed-by: Stefan Berger Signed-off-by: Mimi Zohar Signed-off-by: Sasha Levin --- security/integrity/evm/evm_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 23d484e05e6f..f1a26d50c1d5 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -776,7 +776,9 @@ static int evm_attr_change(struct user_namespace *mnt_userns, /** * evm_inode_setattr - prevent updating an invalid EVM extended attribute + * @idmap: idmap of the mount * @dentry: pointer to the affected dentry + * @attr: iattr structure containing the new file attributes * * Permit update of file attributes when files have a valid EVM signature, * except in the case of them having an immutable portable signature. -- cgit 1.4.1 From 9085f2ca941facdb72c2b8fea7cba99159c61e2c Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Tue, 6 Jun 2023 09:41:12 +0200 Subject: evm: Fix build warnings [ Upstream commit 996e0a97ebd7b11cb785794e2a83c20c1add9d92 ] Fix build warnings (function parameters description) for evm_read_protected_xattrs(), evm_set_key() and evm_verifyxattr(). Fixes: 7626676320f3 ("evm: provide a function to set the EVM key from the kernel") # v4.5+ Fixes: 8314b6732ae4 ("ima: Define new template fields xattrnames, xattrlengths and xattrvalues") # v5.14+ Fixes: 2960e6cb5f7c ("evm: additional parameter to pass integrity cache entry 'iint'") # v3.2+ Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar Signed-off-by: Sasha Levin --- security/integrity/evm/evm_crypto.c | 2 +- security/integrity/evm/evm_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 708de9656bbd..b9395f8ef582 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -40,7 +40,7 @@ static const char evm_hmac[] = "hmac(sha1)"; /** * evm_set_key() - set EVM HMAC key from the kernel * @key: pointer to a buffer with the key data - * @size: length of the key data + * @keylen: length of the key data * * This function allows setting the EVM HMAC key from the kernel * without using the "encrypted" key subsystem keys. It can be used diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index f1a26d50c1d5..a338f19447d0 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -318,7 +318,6 @@ int evm_protected_xattr_if_enabled(const char *req_xattr_name) /** * evm_read_protected_xattrs - read EVM protected xattr names, lengths, values * @dentry: dentry of the read xattrs - * @inode: inode of the read xattrs * @buffer: buffer xattr names, lengths or values are copied to * @buffer_size: size of buffer * @type: n: names, l: lengths, v: values @@ -390,6 +389,7 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, * @xattr_name: requested xattr * @xattr_value: requested xattr value * @xattr_value_len: requested xattr value length + * @iint: inode integrity metadata * * Calculate the HMAC for the given dentry and verify it against the stored * security.evm xattr. For performance, use the xattr value and length -- cgit 1.4.1 From 2672144b86a162f274320ae0e48bdf6f67497978 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Tue, 6 Jun 2023 09:41:13 +0200 Subject: ima: Fix build warnings [ Upstream commit 95526d13038c2bbddd567a4d8e39fac42484e182 ] Fix build warnings (function parameters description) for ima_collect_modsig(), ima_match_policy() and ima_parse_add_rule(). Fixes: 15588227e086 ("ima: Collect modsig") # v5.4+ Fixes: 2fe5d6def167 ("ima: integrity appraisal extension") # v5.14+ Fixes: 4af4662fa4a9 ("integrity: IMA policy") # v3.2+ Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar Signed-off-by: Sasha Levin --- security/integrity/ima/ima_modsig.c | 3 +++ security/integrity/ima/ima_policy.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c index fb25723c65bc..3e7bee30080f 100644 --- a/security/integrity/ima/ima_modsig.c +++ b/security/integrity/ima/ima_modsig.c @@ -89,6 +89,9 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, /** * ima_collect_modsig - Calculate the file hash without the appended signature. + * @modsig: parsed module signature + * @buf: data to verify the signature on + * @size: data size * * Since the modsig is part of the file contents, the hash used in its signature * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 2edff7f58c25..bdc40535ff48 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -694,6 +694,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) * @secid: LSM secid of the task to be validated * @func: IMA hook identifier * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) + * @flags: IMA actions to consider (e.g. IMA_MEASURE | IMA_APPRAISE) * @pcr: set the pcr to extend * @template_desc: the template that should be used for this rule * @func_data: func specific data, may be NULL @@ -1885,7 +1886,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) /** * ima_parse_add_rule - add a rule to ima_policy_rules - * @rule - ima measurement policy rule + * @rule: ima measurement policy rule * * Avoid locking by allowing just one writer at a time in ima_write_policy() * Returns the length of the rule parsed, an error code on failure -- cgit 1.4.1 From f57ba91a46d3fc52bfdac9cca5cf5572ec7afd6d Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Wed, 14 Jun 2023 17:37:33 +0800 Subject: pstore/ram: Add check for kstrdup [ Upstream commit d97038d5ec2062733c1e016caf9baaf68cf64ea1 ] Add check for the return value of kstrdup() and return the error if it fails in order to avoid NULL pointer dereference. Fixes: e163fdb3f7f8 ("pstore/ram: Regularize prz label allocation lifetime") Signed-off-by: Jiasheng Jiang Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20230614093733.36048-1-jiasheng@iscas.ac.cn Signed-off-by: Sasha Levin --- fs/pstore/ram_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 8bf09886e7e6..2384de1c2d18 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -591,6 +591,8 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, raw_spin_lock_init(&prz->buffer_lock); prz->flags = flags; prz->label = kstrdup(label, GFP_KERNEL); + if (!prz->label) + goto err; ret = persistent_ram_buffer_map(start, size, prz, memtype); if (ret) -- cgit 1.4.1 From bb3a9ed2b11a6fc0068289293648ecdc994b2b3c Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 18 Apr 2023 15:30:42 +0200 Subject: igc: Enable and fix RX hash usage by netstack [ Upstream commit 84214ab4689f962b4bfc47fc9a5838d25ac4274d ] When function igc_rx_hash() was introduced in v4.20 via commit 0507ef8a0372 ("igc: Add transmit and receive fastpath and interrupt handlers"), the hardware wasn't configured to provide RSS hash, thus it made sense to not enable net_device NETIF_F_RXHASH feature bit. The NIC hardware was configured to enable RSS hash info in v5.2 via commit 2121c2712f82 ("igc: Add multiple receive queues control supporting"), but forgot to set the NETIF_F_RXHASH feature bit. The original implementation of igc_rx_hash() didn't extract the associated pkt_hash_type, but statically set PKT_HASH_TYPE_L3. The largest portions of this patch are about extracting the RSS Type from the hardware and mapping this to enum pkt_hash_types. This was based on Foxville i225 software user manual rev-1.3.1 and tested on Intel Ethernet Controller I225-LM (rev 03). For UDP it's worth noting that RSS (type) hashing have been disabled both for IPv4 and IPv6 (see IGC_MRQC_RSS_FIELD_IPV4_UDP + IGC_MRQC_RSS_FIELD_IPV6_UDP) because hardware RSS doesn't handle fragmented pkts well when enabled (can cause out-of-order). This results in PKT_HASH_TYPE_L3 for UDP packets, and hash value doesn't include UDP port numbers. Not being PKT_HASH_TYPE_L4, have the effect that netstack will do a software based hash calc calling into flow_dissect, but only when code calls skb_get_hash(), which doesn't necessary happen for local delivery. For QA verification testing I wrote a small bpftrace prog: [0] https://github.com/xdp-project/xdp-project/blob/master/areas/hints/monitor_skb_hash_on_dev.bt Fixes: 2121c2712f82 ("igc: Add multiple receive queues control supporting") Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Daniel Borkmann Acked-by: Song Yoong Siang Link: https://lore.kernel.org/bpf/168182464270.616355.11391652654430626584.stgit@firesoul Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igc/igc.h | 28 ++++++++++++++++++++++++++++ drivers/net/ethernet/intel/igc/igc_main.c | 31 +++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index df3e26c0cf01..f83cbc4a1afa 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "igc_hw.h" @@ -311,6 +312,33 @@ extern char igc_driver_name[]; #define IGC_MRQC_RSS_FIELD_IPV4_UDP 0x00400000 #define IGC_MRQC_RSS_FIELD_IPV6_UDP 0x00800000 +/* RX-desc Write-Back format RSS Type's */ +enum igc_rss_type_num { + IGC_RSS_TYPE_NO_HASH = 0, + IGC_RSS_TYPE_HASH_TCP_IPV4 = 1, + IGC_RSS_TYPE_HASH_IPV4 = 2, + IGC_RSS_TYPE_HASH_TCP_IPV6 = 3, + IGC_RSS_TYPE_HASH_IPV6_EX = 4, + IGC_RSS_TYPE_HASH_IPV6 = 5, + IGC_RSS_TYPE_HASH_TCP_IPV6_EX = 6, + IGC_RSS_TYPE_HASH_UDP_IPV4 = 7, + IGC_RSS_TYPE_HASH_UDP_IPV6 = 8, + IGC_RSS_TYPE_HASH_UDP_IPV6_EX = 9, + IGC_RSS_TYPE_MAX = 10, +}; +#define IGC_RSS_TYPE_MAX_TABLE 16 +#define IGC_RSS_TYPE_MASK GENMASK(3,0) /* 4-bits (3:0) = mask 0x0F */ + +/* igc_rss_type - Rx descriptor RSS type field */ +static inline u32 igc_rss_type(const union igc_adv_rx_desc *rx_desc) +{ + /* RSS Type 4-bits (3:0) number: 0-9 (above 9 is reserved) + * Accessing the same bits via u16 (wb.lower.lo_dword.hs_rss.pkt_info) + * is slightly slower than via u32 (wb.lower.lo_dword.data) + */ + return le32_get_bits(rx_desc->wb.lower.lo_dword.data, IGC_RSS_TYPE_MASK); +} + /* Interrupt defines */ #define IGC_START_ITR 648 /* ~6000 ints/sec */ #define IGC_4K_ITR 980 diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 3509974c1f8e..b67a6a81474f 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -1684,14 +1684,36 @@ static void igc_rx_checksum(struct igc_ring *ring, le32_to_cpu(rx_desc->wb.upper.status_error)); } +/* Mapping HW RSS Type to enum pkt_hash_types */ +static const enum pkt_hash_types igc_rss_type_table[IGC_RSS_TYPE_MAX_TABLE] = { + [IGC_RSS_TYPE_NO_HASH] = PKT_HASH_TYPE_L2, + [IGC_RSS_TYPE_HASH_TCP_IPV4] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_IPV4] = PKT_HASH_TYPE_L3, + [IGC_RSS_TYPE_HASH_TCP_IPV6] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_IPV6_EX] = PKT_HASH_TYPE_L3, + [IGC_RSS_TYPE_HASH_IPV6] = PKT_HASH_TYPE_L3, + [IGC_RSS_TYPE_HASH_TCP_IPV6_EX] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_UDP_IPV4] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_UDP_IPV6] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_UDP_IPV6_EX] = PKT_HASH_TYPE_L4, + [10] = PKT_HASH_TYPE_NONE, /* RSS Type above 9 "Reserved" by HW */ + [11] = PKT_HASH_TYPE_NONE, /* keep array sized for SW bit-mask */ + [12] = PKT_HASH_TYPE_NONE, /* to handle future HW revisons */ + [13] = PKT_HASH_TYPE_NONE, + [14] = PKT_HASH_TYPE_NONE, + [15] = PKT_HASH_TYPE_NONE, +}; + static inline void igc_rx_hash(struct igc_ring *ring, union igc_adv_rx_desc *rx_desc, struct sk_buff *skb) { - if (ring->netdev->features & NETIF_F_RXHASH) - skb_set_hash(skb, - le32_to_cpu(rx_desc->wb.lower.hi_dword.rss), - PKT_HASH_TYPE_L3); + if (ring->netdev->features & NETIF_F_RXHASH) { + u32 rss_hash = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss); + u32 rss_type = igc_rss_type(rx_desc); + + skb_set_hash(skb, rss_hash, igc_rss_type_table[rss_type]); + } } static void igc_rx_vlan(struct igc_ring *rx_ring, @@ -6518,6 +6540,7 @@ static int igc_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; netdev->features |= NETIF_F_TSO_ECN; + netdev->features |= NETIF_F_RXHASH; netdev->features |= NETIF_F_RXCSUM; netdev->features |= NETIF_F_HW_CSUM; netdev->features |= NETIF_F_SCTP_CRC; -- cgit 1.4.1 From 6928d6e9b094631ad11163cb0512c9a5d96be2d7 Mon Sep 17 00:00:00 2001 From: Peter Seiderer Date: Wed, 26 Apr 2023 17:35:00 +0300 Subject: wifi: ath9k: fix AR9003 mac hardware hang check register offset calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3e56c80931c7615250fe4bf83f93b57881969266 ] Fix ath9k_hw_verify_hang()/ar9003_hw_detect_mac_hang() register offset calculation (do not overflow the shift for the second register/queues above five, use the register layout described in the comments above ath9k_hw_verify_hang() instead). Fixes: 222e04830ff0 ("ath9k: Fix MAC HW hang check for AR9003") Reported-by: Gregg Wonderly Link: https://lore.kernel.org/linux-wireless/E3A9C354-0CB7-420C-ADEF-F0177FB722F4@seqtechllc.com/ Signed-off-by: Peter Seiderer Acked-by: Toke Høiland-Jørgensen Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230422212423.26065-1-ps.report@gmx.net Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 42f00a2a8c80..cf5648188459 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -1099,17 +1099,22 @@ static bool ath9k_hw_verify_hang(struct ath_hw *ah, unsigned int queue) { u32 dma_dbg_chain, dma_dbg_complete; u8 dcu_chain_state, dcu_complete_state; + unsigned int dbg_reg, reg_offset; int i; - for (i = 0; i < NUM_STATUS_READS; i++) { - if (queue < 6) - dma_dbg_chain = REG_READ(ah, AR_DMADBG_4); - else - dma_dbg_chain = REG_READ(ah, AR_DMADBG_5); + if (queue < 6) { + dbg_reg = AR_DMADBG_4; + reg_offset = queue * 5; + } else { + dbg_reg = AR_DMADBG_5; + reg_offset = (queue - 6) * 5; + } + for (i = 0; i < NUM_STATUS_READS; i++) { + dma_dbg_chain = REG_READ(ah, dbg_reg); dma_dbg_complete = REG_READ(ah, AR_DMADBG_6); - dcu_chain_state = (dma_dbg_chain >> (5 * queue)) & 0x1f; + dcu_chain_state = (dma_dbg_chain >> reg_offset) & 0x1f; dcu_complete_state = dma_dbg_complete & 0x3; if ((dcu_chain_state != 0x6) || (dcu_complete_state != 0x1)) @@ -1128,6 +1133,7 @@ static bool ar9003_hw_detect_mac_hang(struct ath_hw *ah) u8 dcu_chain_state, dcu_complete_state; bool dcu_wait_frdone = false; unsigned long chk_dcu = 0; + unsigned int reg_offset; unsigned int i = 0; dma_dbg_4 = REG_READ(ah, AR_DMADBG_4); @@ -1139,12 +1145,15 @@ static bool ar9003_hw_detect_mac_hang(struct ath_hw *ah) goto exit; for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { - if (i < 6) + if (i < 6) { chk_dbg = dma_dbg_4; - else + reg_offset = i * 5; + } else { chk_dbg = dma_dbg_5; + reg_offset = (i - 6) * 5; + } - dcu_chain_state = (chk_dbg >> (5 * i)) & 0x1f; + dcu_chain_state = (chk_dbg >> reg_offset) & 0x1f; if (dcu_chain_state == 0x6) { dcu_wait_frdone = true; chk_dcu |= BIT(i); -- cgit 1.4.1 From d1c2ff2bd84c3692c9df267a2b991ce92bfca8ef Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Wed, 26 Apr 2023 17:35:01 +0300 Subject: wifi: ath9k: avoid referencing uninit memory in ath9k_wmi_ctrl_rx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f24292e827088bba8de7158501ac25a59b064953 ] For the reasons also described in commit b383e8abed41 ("wifi: ath9k: avoid uninit memory read in ath9k_htc_rx_msg()"), ath9k_htc_rx_msg() should validate pkt_len before accessing the SKB. For example, the obtained SKB may have been badly constructed with pkt_len = 8. In this case, the SKB can only contain a valid htc_frame_hdr but after being processed in ath9k_htc_rx_msg() and passed to ath9k_wmi_ctrl_rx() endpoint RX handler, it is expected to have a WMI command header which should be located inside its data payload. Implement sanity checking inside ath9k_wmi_ctrl_rx(). Otherwise, uninit memory can be referenced. Tested on Qualcomm Atheros Communications AR9271 802.11n . Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.") Reported-and-tested-by: syzbot+f2cb6e0ffdb961921e4d@syzkaller.appspotmail.com Signed-off-by: Fedor Pchelkin Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230424183348.111355-1-pchelkin@ispras.ru Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 19345b8f7bfd..d652c647d56b 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -221,6 +221,10 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, if (unlikely(wmi->stopped)) goto free_skb; + /* Validate the obtained SKB. */ + if (unlikely(skb->len < sizeof(struct wmi_cmd_hdr))) + goto free_skb; + hdr = (struct wmi_cmd_hdr *) skb->data; cmd_id = be16_to_cpu(hdr->command_id); -- cgit 1.4.1 From a7434a4dcc715709d759da4ecf697f5ea13b87fe Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Thu, 27 Apr 2023 18:36:38 -0700 Subject: libbpf: btf_dump_type_data_check_overflow needs to consider BTF_MEMBER_BITFIELD_SIZE [ Upstream commit c39028b333f3a3a765c5c0b9726b8e38aedf0ba1 ] The btf_dump/struct_data selftest is failing with: [...] test_btf_dump_struct_data:FAIL:unexpected return value dumping fs_context unexpected unexpected return value dumping fs_context: actual -7 != expected 264 [...] The reason is in btf_dump_type_data_check_overflow(). It does not use BTF_MEMBER_BITFIELD_SIZE from the struct's member (btf_member). Instead, it is using the enum size which is 4. It had been working till the recent commit 4e04143c869c ("fs_context: drop the unused lsm_flags member") removed an integer member which also removed the 4 bytes padding at the end of the fs_context. Missing this 4 bytes padding exposed this bug. In particular, when btf_dump_type_data_check_overflow() reaches the member 'phase', -E2BIG is returned. The fix is to pass bit_sz to btf_dump_type_data_check_overflow(). In btf_dump_type_data_check_overflow(), it does a different size check when bit_sz is not zero. The current fs_context: [3600] ENUM 'fs_context_purpose' encoding=UNSIGNED size=4 vlen=3 'FS_CONTEXT_FOR_MOUNT' val=0 'FS_CONTEXT_FOR_SUBMOUNT' val=1 'FS_CONTEXT_FOR_RECONFIGURE' val=2 [3601] ENUM 'fs_context_phase' encoding=UNSIGNED size=4 vlen=7 'FS_CONTEXT_CREATE_PARAMS' val=0 'FS_CONTEXT_CREATING' val=1 'FS_CONTEXT_AWAITING_MOUNT' val=2 'FS_CONTEXT_AWAITING_RECONF' val=3 'FS_CONTEXT_RECONF_PARAMS' val=4 'FS_CONTEXT_RECONFIGURING' val=5 'FS_CONTEXT_FAILED' val=6 [3602] STRUCT 'fs_context' size=264 vlen=21 'ops' type_id=3603 bits_offset=0 'uapi_mutex' type_id=235 bits_offset=64 'fs_type' type_id=872 bits_offset=1216 'fs_private' type_id=21 bits_offset=1280 'sget_key' type_id=21 bits_offset=1344 'root' type_id=781 bits_offset=1408 'user_ns' type_id=251 bits_offset=1472 'net_ns' type_id=984 bits_offset=1536 'cred' type_id=1785 bits_offset=1600 'log' type_id=3621 bits_offset=1664 'source' type_id=42 bits_offset=1792 'security' type_id=21 bits_offset=1856 's_fs_info' type_id=21 bits_offset=1920 'sb_flags' type_id=20 bits_offset=1984 'sb_flags_mask' type_id=20 bits_offset=2016 's_iflags' type_id=20 bits_offset=2048 'purpose' type_id=3600 bits_offset=2080 bitfield_size=8 'phase' type_id=3601 bits_offset=2088 bitfield_size=8 'need_free' type_id=67 bits_offset=2096 bitfield_size=1 'global' type_id=67 bits_offset=2097 bitfield_size=1 'oldapi' type_id=67 bits_offset=2098 bitfield_size=1 Fixes: 920d16af9b42 ("libbpf: BTF dumper support for typed data") Signed-off-by: Martin KaFai Lau Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20230428013638.1581263-1-martin.lau@linux.dev Signed-off-by: Sasha Levin --- tools/lib/bpf/btf_dump.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 4cd1d49c94d6..713264899250 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -2213,9 +2213,25 @@ static int btf_dump_type_data_check_overflow(struct btf_dump *d, const struct btf_type *t, __u32 id, const void *data, - __u8 bits_offset) + __u8 bits_offset, + __u8 bit_sz) { - __s64 size = btf__resolve_size(d->btf, id); + __s64 size; + + if (bit_sz) { + /* bits_offset is at most 7. bit_sz is at most 128. */ + __u8 nr_bytes = (bits_offset + bit_sz + 7) / 8; + + /* When bit_sz is non zero, it is called from + * btf_dump_struct_data() where it only cares about + * negative error value. + * Return nr_bytes in success case to make it + * consistent as the regular integer case below. + */ + return data + nr_bytes > d->typed_dump->data_end ? -E2BIG : nr_bytes; + } + + size = btf__resolve_size(d->btf, id); if (size < 0 || size >= INT_MAX) { pr_warn("unexpected size [%zu] for id [%u]\n", @@ -2370,7 +2386,7 @@ static int btf_dump_dump_type_data(struct btf_dump *d, { int size, err = 0; - size = btf_dump_type_data_check_overflow(d, t, id, data, bits_offset); + size = btf_dump_type_data_check_overflow(d, t, id, data, bits_offset, bit_sz); if (size < 0) return size; err = btf_dump_type_data_check_zero(d, t, id, data, bits_offset, bit_sz); -- cgit 1.4.1 From 56c25f2763a16db4fa1b486e6a21dc246cd992bd Mon Sep 17 00:00:00 2001 From: Pengcheng Yang Date: Fri, 5 May 2023 16:50:58 +0800 Subject: samples/bpf: Fix buffer overflow in tcp_basertt [ Upstream commit f4dea9689c5fea3d07170c2cb0703e216f1a0922 ] Using sizeof(nv) or strlen(nv)+1 is correct. Fixes: c890063e4404 ("bpf: sample BPF_SOCKET_OPS_BASE_RTT program") Signed-off-by: Pengcheng Yang Link: https://lore.kernel.org/r/1683276658-2860-1-git-send-email-yangpc@wangsu.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- samples/bpf/tcp_basertt_kern.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/tcp_basertt_kern.c b/samples/bpf/tcp_basertt_kern.c index 8dfe09a92fec..822b0742b815 100644 --- a/samples/bpf/tcp_basertt_kern.c +++ b/samples/bpf/tcp_basertt_kern.c @@ -47,7 +47,7 @@ int bpf_basertt(struct bpf_sock_ops *skops) case BPF_SOCK_OPS_BASE_RTT: n = bpf_getsockopt(skops, SOL_TCP, TCP_CONGESTION, cong, sizeof(cong)); - if (!n && !__builtin_memcmp(cong, nv, sizeof(nv)+1)) { + if (!n && !__builtin_memcmp(cong, nv, sizeof(nv))) { /* Set base_rtt to 80us */ rv = 80; } else if (n) { -- cgit 1.4.1 From ded1a7a570b0c667a7616706edcd5f01fa14d372 Mon Sep 17 00:00:00 2001 From: Vijaya Krishna Nivarthi Date: Tue, 25 Apr 2023 14:12:08 +0530 Subject: spi: spi-geni-qcom: Correct CS_TOGGLE bit in SPI_TRANS_CFG [ Upstream commit 5fd7c99ecf45c8ee8a9b1268f0ffc91cc6271da2 ] The CS_TOGGLE bit when set is supposed to instruct FW to toggle CS line between words. The driver with intent of disabling this behaviour has been unsetting BIT(0). This has not caused any trouble so far because the original BIT(1) is untouched and BIT(0) likely wasn't being used. Correct this to prevent a potential future bug. Signed-off-by: Vijaya Krishna Nivarthi --- drivers/spi/spi-geni-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index dd1581893fe7..689b94fc5570 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -35,7 +35,7 @@ #define CS_DEMUX_OUTPUT_SEL GENMASK(3, 0) #define SE_SPI_TRANS_CFG 0x25c -#define CS_TOGGLE BIT(0) +#define CS_TOGGLE BIT(1) #define SE_SPI_WORD_LEN 0x268 #define WORD_LEN_MSK GENMASK(9, 0) -- cgit 1.4.1 From 51cb8329f21032f7af0d0cda770cca0745e36983 Mon Sep 17 00:00:00 2001 From: Amisha Patel Date: Fri, 21 Apr 2023 18:10:20 +0000 Subject: wifi: wilc1000: fix for absent RSN capabilities WFA testcase [ Upstream commit 9ce4bb09123e9754996e358bd808d39f5d112899 ] Mandatory WFA testcase CT_Security_WPA2Personal_STA_RSNEBoundsVerification-AbsentRSNCap, performs bounds verfication on Beacon and/or Probe response frames. It failed and observed the reason to be absence of cipher suite and AKM suite in RSN information. To fix this, enable the RSN flag before extracting RSN capabilities. Fixes: cd21d99e595e ("wifi: wilc1000: validate pairwise and authentication suite offsets") Signed-off-by: Amisha Patel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230421181005.4865-1-amisha.patel@microchip.com Signed-off-by: Sasha Levin --- drivers/net/wireless/microchip/wilc1000/hif.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 67df8221b5ae..a1b75feec6ed 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -485,6 +485,9 @@ void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, int rsn_ie_len = sizeof(struct element) + rsn_ie[1]; int offset = 8; + param->mode_802_11i = 2; + param->rsn_found = true; + /* extract RSN capabilities */ if (offset < rsn_ie_len) { /* skip over pairwise suites */ @@ -494,11 +497,8 @@ void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, /* skip over authentication suites */ offset += (rsn_ie[offset] * 4) + 2; - if (offset + 1 < rsn_ie_len) { - param->mode_802_11i = 2; - param->rsn_found = true; + if (offset + 1 < rsn_ie_len) memcpy(param->rsn_cap, &rsn_ie[offset], 2); - } } } } -- cgit 1.4.1 From f21f2ae562101bb913e1babda989577c8e076f89 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 6 May 2023 15:53:15 +0200 Subject: wifi: mwifiex: Fix the size of a memory allocation in mwifiex_ret_802_11_scan() [ Upstream commit d9aef04fcfa81ee4fb2804a21a3712b7bbd936af ] The type of "mwifiex_adapter->nd_info" is "struct cfg80211_wowlan_nd_info", not "struct cfg80211_wowlan_nd_match". Use struct_size() to ease the computation of the needed size. The current code over-allocates some memory, so is safe. But it wastes 32 bytes. Fixes: 7d7f07d8c5d3 ("mwifiex: add wowlan net-detect support") Signed-off-by: Christophe JAILLET Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/7a6074fb056d2181e058a3cc6048d8155c20aec7.1683371982.git.christophe.jaillet@wanadoo.fr Signed-off-by: Sasha Levin --- drivers/net/wireless/marvell/mwifiex/scan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index ac8001c84293..644b1e134b01 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -2187,9 +2187,9 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, if (nd_config) { adapter->nd_info = - kzalloc(sizeof(struct cfg80211_wowlan_nd_match) + - sizeof(struct cfg80211_wowlan_nd_match *) * - scan_rsp->number_of_sets, GFP_ATOMIC); + kzalloc(struct_size(adapter->nd_info, matches, + scan_rsp->number_of_sets), + GFP_ATOMIC); if (adapter->nd_info) adapter->nd_info->n_matches = scan_rsp->number_of_sets; -- cgit 1.4.1 From 8404f8de1e23690524b92dded514dee29160f7ad Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Thu, 11 May 2023 15:25:06 +0200 Subject: sctp: add bpf_bypass_getsockopt proto callback [ Upstream commit 2598619e012cee5273a2821441b9a051ad931249 ] Implement ->bpf_bypass_getsockopt proto callback and filter out SCTP_SOCKOPT_PEELOFF, SCTP_SOCKOPT_PEELOFF_FLAGS and SCTP_SOCKOPT_CONNECTX3 socket options from running eBPF hook on them. SCTP_SOCKOPT_PEELOFF and SCTP_SOCKOPT_PEELOFF_FLAGS options do fd_install(), and if BPF_CGROUP_RUN_PROG_GETSOCKOPT hook returns an error after success of the original handler sctp_getsockopt(...), userspace will receive an error from getsockopt syscall and will be not aware that fd was successfully installed into a fdtable. As pointed by Marcelo Ricardo Leitner it seems reasonable to skip bpf getsockopt hook for SCTP_SOCKOPT_CONNECTX3 sockopt too. Because internaly, it triggers connect() and if error is masked then userspace will be confused. This patch was born as a result of discussion around a new SCM_PIDFD interface: https://lore.kernel.org/all/20230413133355.350571-3-aleksandr.mikhalitsyn@canonical.com/ Fixes: 0d01da6afc54 ("bpf: implement getsockopt and setsockopt hooks") Cc: Daniel Borkmann Cc: Christian Brauner Cc: Stanislav Fomichev Cc: Neil Horman Cc: Marcelo Ricardo Leitner Cc: Xin Long Cc: linux-sctp@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: netdev@vger.kernel.org Suggested-by: Stanislav Fomichev Acked-by: Stanislav Fomichev Signed-off-by: Alexander Mikhalitsyn Acked-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sctp/socket.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index bc3d08bd7cef..e1011311bc87 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -8279,6 +8279,22 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, return retval; } +static bool sctp_bpf_bypass_getsockopt(int level, int optname) +{ + if (level == SOL_SCTP) { + switch (optname) { + case SCTP_SOCKOPT_PEELOFF: + case SCTP_SOCKOPT_PEELOFF_FLAGS: + case SCTP_SOCKOPT_CONNECTX3: + return true; + default: + return false; + } + } + + return false; +} + static int sctp_hash(struct sock *sk) { /* STUB */ @@ -9643,6 +9659,7 @@ struct proto sctp_prot = { .shutdown = sctp_shutdown, .setsockopt = sctp_setsockopt, .getsockopt = sctp_getsockopt, + .bpf_bypass_getsockopt = sctp_bpf_bypass_getsockopt, .sendmsg = sctp_sendmsg, .recvmsg = sctp_recvmsg, .bind = sctp_bind, @@ -9698,6 +9715,7 @@ struct proto sctpv6_prot = { .shutdown = sctp_shutdown, .setsockopt = sctp_setsockopt, .getsockopt = sctp_getsockopt, + .bpf_bypass_getsockopt = sctp_bpf_bypass_getsockopt, .sendmsg = sctp_sendmsg, .recvmsg = sctp_recvmsg, .bind = sctp_bind, -- cgit 1.4.1 From 34fe7aa8ef1d159ca6b6f01728788e8b702ac7c5 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 8 May 2023 23:55:02 -0700 Subject: libbpf: fix offsetof() and container_of() to work with CO-RE [ Upstream commit bdeeed3498c7871c17465bb4f11d1bc67f9098af ] It seems like __builtin_offset() doesn't preserve CO-RE field relocations properly. So if offsetof() macro is defined through __builtin_offset(), CO-RE-enabled BPF code using container_of() will be subtly and silently broken. To avoid this problem, redefine offsetof() and container_of() in the form that works with CO-RE relocations more reliably. Fixes: 5fbc220862fc ("tools/libpf: Add offsetof/container_of macro in bpf_helpers.h") Reported-by: Lennart Poettering Signed-off-by: Andrii Nakryiko Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20230509065502.2306180-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/bpf_helpers.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h index d37c4fe2849d..2b3b300bf4df 100644 --- a/tools/lib/bpf/bpf_helpers.h +++ b/tools/lib/bpf/bpf_helpers.h @@ -77,16 +77,21 @@ /* * Helper macros to manipulate data structures */ -#ifndef offsetof -#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) -#endif -#ifndef container_of + +/* offsetof() definition that uses __builtin_offset() might not preserve field + * offset CO-RE relocation properly, so force-redefine offsetof() using + * old-school approach which works with CO-RE correctly + */ +#undef offsetof +#define offsetof(type, member) ((unsigned long)&((type *)0)->member) + +/* redefined container_of() to ensure we use the above offsetof() macro */ +#undef container_of #define container_of(ptr, type, member) \ ({ \ void *__mptr = (void *)(ptr); \ ((type *)(__mptr - offsetof(type, member))); \ }) -#endif /* * Compiler (optimization) barrier. -- cgit 1.4.1 From c6a9fc82fe188f0f1c07839c2ca4b40a2b14621f Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Thu, 11 May 2023 10:04:53 -0700 Subject: bpf: Don't EFAULT for {g,s}setsockopt with wrong optlen [ Upstream commit 29ebbba7d46136cba324264e513a1e964ca16c0a ] With the way the hooks implemented right now, we have a special condition: optval larger than PAGE_SIZE will expose only first 4k into BPF; any modifications to the optval are ignored. If the BPF program doesn't handle this condition by resetting optlen to 0, the userspace will get EFAULT. The intention of the EFAULT was to make it apparent to the developers that the program is doing something wrong. However, this inadvertently might affect production workloads with the BPF programs that are not too careful (i.e., returning EFAULT for perfectly valid setsockopt/getsockopt calls). Let's try to minimize the chance of BPF program screwing up userspace by ignoring the output of those BPF programs (instead of returning EFAULT to the userspace). pr_info_once those cases to the dmesg to help with figuring out what's going wrong. Fixes: 0d01da6afc54 ("bpf: implement getsockopt and setsockopt hooks") Suggested-by: Martin KaFai Lau Signed-off-by: Stanislav Fomichev Link: https://lore.kernel.org/r/20230511170456.1759459-2-sdf@google.com Signed-off-by: Martin KaFai Lau Signed-off-by: Sasha Levin --- kernel/bpf/cgroup.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index b86b907e566c..bb70f400c25e 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1826,6 +1826,12 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, ret = 1; } else if (ctx.optlen > max_optlen || ctx.optlen < -1) { /* optlen is out of bounds */ + if (*optlen > PAGE_SIZE && ctx.optlen >= 0) { + pr_info_once("bpf setsockopt: ignoring program buffer with optlen=%d (max_optlen=%d)\n", + ctx.optlen, max_optlen); + ret = 0; + goto out; + } ret = -EFAULT; } else { /* optlen within bounds, run kernel handler */ @@ -1881,8 +1887,10 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, .optname = optname, .current_task = current, }; + int orig_optlen; int ret; + orig_optlen = max_optlen; ctx.optlen = max_optlen; max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf); if (max_optlen < 0) @@ -1905,6 +1913,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, ret = -EFAULT; goto out; } + orig_optlen = ctx.optlen; if (copy_from_user(ctx.optval, optval, min(ctx.optlen, max_optlen)) != 0) { @@ -1922,6 +1931,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, goto out; if (optval && (ctx.optlen > max_optlen || ctx.optlen < 0)) { + if (orig_optlen > PAGE_SIZE && ctx.optlen >= 0) { + pr_info_once("bpf getsockopt: ignoring program buffer with optlen=%d (max_optlen=%d)\n", + ctx.optlen, max_optlen); + ret = retval; + goto out; + } ret = -EFAULT; goto out; } -- cgit 1.4.1 From 34d04d70194d19ba9847400c12fcdeae6826630c Mon Sep 17 00:00:00 2001 From: Joy Chakraborty Date: Fri, 12 May 2023 10:47:45 +0000 Subject: spi: dw: Round of n_bytes to power of 2 [ Upstream commit 9f34baf67e4d08908fd94ff29c825bb673295336 ] n_bytes variable in the driver represents the number of bytes per word that needs to be sent/copied to fifo. Bits/word can be between 8 and 32 bits from the client but in memory they are a power of 2, same is mentioned in spi.h header: " * @bits_per_word: Data transfers involve one or more words; word sizes * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). * This may be changed by the device's driver, or left at the * default (0) indicating protocol words are eight bit bytes. * The spi_transfer.bits_per_word can override this for each transfer. " Hence, round of n_bytes to a power of 2 to avoid values like 3 which would generate unalligned/odd accesses to memory/fifo. * tested on Baikal-T1 based system with DW SPI-looped back interface transferring a chunk of data with DFS:8,12,16. Fixes: a51acc2400d4 ("spi: dw: Add support for 32-bits max xfer size") Suggested-by: Andy Shevchenko --- drivers/spi/spi-dw-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index c3bfb6c84cab..4976e3b8923e 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -426,7 +426,10 @@ static int dw_spi_transfer_one(struct spi_controller *master, int ret; dws->dma_mapped = 0; - dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); + dws->n_bytes = + roundup_pow_of_two(DIV_ROUND_UP(transfer->bits_per_word, + BITS_PER_BYTE)); + dws->tx = (void *)transfer->tx_buf; dws->tx_len = transfer->len / dws->n_bytes; dws->rx = transfer->rx_buf; -- cgit 1.4.1 From bac93b35f973c3db72129c0bf7cdd9f71a957a8f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 13 May 2023 13:52:04 +0200 Subject: nfc: llcp: fix possible use of uninitialized variable in nfc_llcp_send_connect() [ Upstream commit 0d9b41daa5907756a31772d8af8ac5ff25cf17c1 ] If sock->service_name is NULL, the local variable service_name_tlv_length will not be assigned by nfc_llcp_build_tlv(), later leading to using value frmo the stack. Smatch warning: net/nfc/llcp_commands.c:442 nfc_llcp_send_connect() error: uninitialized symbol 'service_name_tlv_length'. Fixes: de9e5aeb4f40 ("NFC: llcp: Fix usage of llcp_add_tlv()") Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/nfc/llcp_commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 41e3a20c8935..cdb001de0692 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -390,7 +390,8 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) const u8 *service_name_tlv = NULL; const u8 *miux_tlv = NULL; const u8 *rw_tlv = NULL; - u8 service_name_tlv_length, miux_tlv_length, rw_tlv_length, rw; + u8 service_name_tlv_length = 0; + u8 miux_tlv_length, rw_tlv_length, rw; int err; u16 size = 0; __be16 miux; -- cgit 1.4.1 From 1949721c741b5fc966b6a981ab965b04f708d1f9 Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Fri, 12 May 2023 12:31:34 +0100 Subject: bpftool: JIT limited misreported as negative value on aarch64 [ Upstream commit 04cb8453a91c7c22f60ddadb6cef0d19abb33bb5 ] On aarch64, "bpftool feature" reports an incorrect BPF JIT limit: $ sudo /sbin/bpftool feature Scanning system configuration... bpf() syscall restricted to privileged users JIT compiler is enabled JIT compiler hardening is disabled JIT compiler kallsyms exports are enabled for root skipping kernel config, can't open file: No such file or directory Global memory limit for JIT compiler for unprivileged users is -201326592 bytes This is because /proc/sys/net/core/bpf_jit_limit reports $ sudo cat /proc/sys/net/core/bpf_jit_limit 68169519595520 ...and an int is assumed in read_procfs(). Change read_procfs() to return a long to avoid negative value reporting. Fixes: 7a4522bbef0c ("tools: bpftool: add probes for /proc/ eBPF parameters") Reported-by: Nicky Veitch Signed-off-by: Alan Maguire Signed-off-by: Daniel Borkmann Acked-by: Jiri Olsa Acked-by: Quentin Monnet Link: https://lore.kernel.org/bpf/20230512113134.58996-1-alan.maguire@oracle.com Signed-off-by: Sasha Levin --- tools/bpf/bpftool/feature.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index 36cf0f1517c9..4460399bc8ed 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -167,12 +167,12 @@ static int get_vendor_id(int ifindex) return strtol(buf, NULL, 0); } -static int read_procfs(const char *path) +static long read_procfs(const char *path) { char *endptr, *line = NULL; size_t len = 0; FILE *fd; - int res; + long res; fd = fopen(path, "r"); if (!fd) @@ -194,7 +194,7 @@ static int read_procfs(const char *path) static void probe_unprivileged_disabled(void) { - int res; + long res; /* No support for C-style ouptut */ @@ -216,14 +216,14 @@ static void probe_unprivileged_disabled(void) printf("Unable to retrieve required privileges for bpf() syscall\n"); break; default: - printf("bpf() syscall restriction has unknown value %d\n", res); + printf("bpf() syscall restriction has unknown value %ld\n", res); } } } static void probe_jit_enable(void) { - int res; + long res; /* No support for C-style ouptut */ @@ -245,7 +245,7 @@ static void probe_jit_enable(void) printf("Unable to retrieve JIT-compiler status\n"); break; default: - printf("JIT-compiler status has unknown value %d\n", + printf("JIT-compiler status has unknown value %ld\n", res); } } @@ -253,7 +253,7 @@ static void probe_jit_enable(void) static void probe_jit_harden(void) { - int res; + long res; /* No support for C-style ouptut */ @@ -275,7 +275,7 @@ static void probe_jit_harden(void) printf("Unable to retrieve JIT hardening status\n"); break; default: - printf("JIT hardening status has unknown value %d\n", + printf("JIT hardening status has unknown value %ld\n", res); } } @@ -283,7 +283,7 @@ static void probe_jit_harden(void) static void probe_jit_kallsyms(void) { - int res; + long res; /* No support for C-style ouptut */ @@ -302,14 +302,14 @@ static void probe_jit_kallsyms(void) printf("Unable to retrieve JIT kallsyms export status\n"); break; default: - printf("JIT kallsyms exports status has unknown value %d\n", res); + printf("JIT kallsyms exports status has unknown value %ld\n", res); } } } static void probe_jit_limit(void) { - int res; + long res; /* No support for C-style ouptut */ @@ -322,7 +322,7 @@ static void probe_jit_limit(void) printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n"); break; default: - printf("Global memory limit for JIT compiler for unprivileged users is %d bytes\n", res); + printf("Global memory limit for JIT compiler for unprivileged users is %ld bytes\n", res); } } } -- cgit 1.4.1 From 8ea165e1f89d7ddd9a16046809013f9792bb33c3 Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Mon, 15 May 2023 13:08:48 +0000 Subject: bpf: Remove bpf trampoline selector [ Upstream commit 47e79cbeea4b3891ad476047f4c68543eb51c8e0 ] After commit e21aa341785c ("bpf: Fix fexit trampoline."), the selector is only used to indicate how many times the bpf trampoline image are updated and been displayed in the trampoline ksym name. After the trampoline is freed, the selector will start from 0 again. So the selector is a useless value to the user. We can remove it. If the user want to check whether the bpf trampoline image has been updated or not, the user can compare the address. Each time the trampoline image is updated, the address will change consequently. Jiri also pointed out another issue that perf is still using the old name "bpf_trampoline_%lu", so this change can fix the issue in perf. Fixes: e21aa341785c ("bpf: Fix fexit trampoline.") Signed-off-by: Yafang Shao Signed-off-by: Daniel Borkmann Acked-by: Song Liu Cc: Jiri Olsa Link: https://lore.kernel.org/bpf/ZFvOOlrmHiY9AgXE@krava Link: https://lore.kernel.org/bpf/20230515130849.57502-3-laoar.shao@gmail.com Signed-off-by: Sasha Levin --- include/linux/bpf.h | 1 - kernel/bpf/trampoline.c | 11 ++++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 942f9ac9fa7b..8cef9ec3a89c 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -933,7 +933,6 @@ struct bpf_trampoline { int progs_cnt[BPF_TRAMP_MAX]; /* Executable image of trampoline */ struct bpf_tramp_image *cur_image; - u64 selector; struct module *mod; }; diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index f1504cb5b6e1..91d8de938a3d 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -372,7 +372,7 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im) call_rcu_tasks_trace(&im->rcu, __bpf_tramp_image_put_rcu_tasks); } -static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, u32 idx) +static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key) { struct bpf_tramp_image *im; struct bpf_ksym *ksym; @@ -399,7 +399,7 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, u32 idx) ksym = &im->ksym; INIT_LIST_HEAD_RCU(&ksym->lnode); - snprintf(ksym->name, KSYM_NAME_LEN, "bpf_trampoline_%llu_%u", key, idx); + snprintf(ksym->name, KSYM_NAME_LEN, "bpf_trampoline_%llu", key); bpf_image_ksym_add(image, ksym); return im; @@ -429,11 +429,10 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut err = unregister_fentry(tr, tr->cur_image->image); bpf_tramp_image_put(tr->cur_image); tr->cur_image = NULL; - tr->selector = 0; goto out; } - im = bpf_tramp_image_alloc(tr->key, tr->selector); + im = bpf_tramp_image_alloc(tr->key); if (IS_ERR(im)) { err = PTR_ERR(im); goto out; @@ -471,8 +470,7 @@ again: set_memory_ro((long)im->image, 1); set_memory_x((long)im->image, 1); - WARN_ON(tr->cur_image && tr->selector == 0); - WARN_ON(!tr->cur_image && tr->selector); + WARN_ON(tr->cur_image && total == 0); if (tr->cur_image) /* progs already running at this address */ err = modify_fentry(tr, tr->cur_image->image, im->image, lock_direct_mutex); @@ -502,7 +500,6 @@ again: if (tr->cur_image) bpf_tramp_image_put(tr->cur_image); tr->cur_image = im; - tr->selector++; out: /* If any error happens, restore previous flags */ if (err) -- cgit 1.4.1 From 20109ddd5bea2c24d790debf5d02584ef24c3f5e Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Mon, 15 May 2023 13:08:47 +0000 Subject: bpf: Fix memleak due to fentry attach failure [ Upstream commit 108598c39eefbedc9882273ac0df96127a629220 ] If it fails to attach fentry, the allocated bpf trampoline image will be left in the system. That can be verified by checking /proc/kallsyms. This meamleak can be verified by a simple bpf program as follows: SEC("fentry/trap_init") int fentry_run() { return 0; } It will fail to attach trap_init because this function is freed after kernel init, and then we can find the trampoline image is left in the system by checking /proc/kallsyms. $ tail /proc/kallsyms ffffffffc0613000 t bpf_trampoline_6442453466_1 [bpf] ffffffffc06c3000 t bpf_trampoline_6442453466_1 [bpf] $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep "FUNC 'trap_init'" [2522] FUNC 'trap_init' type_id=119 linkage=static $ echo $((6442453466 & 0x7fffffff)) 2522 Note that there are two left bpf trampoline images, that is because the libbpf will fallback to raw tracepoint if -EINVAL is returned. Fixes: e21aa341785c ("bpf: Fix fexit trampoline.") Signed-off-by: Yafang Shao Signed-off-by: Daniel Borkmann Acked-by: Song Liu Cc: Jiri Olsa Link: https://lore.kernel.org/bpf/20230515130849.57502-2-laoar.shao@gmail.com Signed-off-by: Sasha Levin --- kernel/bpf/trampoline.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 91d8de938a3d..30af8f66e17b 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -279,11 +279,8 @@ bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total, bool *ip_a return tlinks; } -static void __bpf_tramp_image_put_deferred(struct work_struct *work) +static void bpf_tramp_image_free(struct bpf_tramp_image *im) { - struct bpf_tramp_image *im; - - im = container_of(work, struct bpf_tramp_image, work); bpf_image_ksym_del(&im->ksym); bpf_jit_free_exec(im->image); bpf_jit_uncharge_modmem(PAGE_SIZE); @@ -291,6 +288,14 @@ static void __bpf_tramp_image_put_deferred(struct work_struct *work) kfree_rcu(im, rcu); } +static void __bpf_tramp_image_put_deferred(struct work_struct *work) +{ + struct bpf_tramp_image *im; + + im = container_of(work, struct bpf_tramp_image, work); + bpf_tramp_image_free(im); +} + /* callback, fexit step 3 or fentry step 2 */ static void __bpf_tramp_image_put_rcu(struct rcu_head *rcu) { @@ -465,7 +470,7 @@ again: &tr->func.model, tr->flags, tlinks, tr->func.addr); if (err < 0) - goto out; + goto out_free; set_memory_ro((long)im->image, 1); set_memory_x((long)im->image, 1); @@ -495,7 +500,7 @@ again: } #endif if (err) - goto out; + goto out_free; if (tr->cur_image) bpf_tramp_image_put(tr->cur_image); @@ -506,6 +511,10 @@ out: tr->flags = orig_flags; kfree(tlinks); return err; + +out_free: + bpf_tramp_image_free(im); + goto out; } static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog) -- cgit 1.4.1 From be84e69082b20770aa44400e4a8cecd9a88b6e9e Mon Sep 17 00:00:00 2001 From: Alexey Gladkov Date: Wed, 17 May 2023 11:49:46 +0200 Subject: selftests/bpf: Do not use sign-file as testcase [ Upstream commit f04a32b2c5b539e3c097cb5c7c1df12a8f4a0cf0 ] The sign-file utility (from scripts/) is used in prog_tests/verify_pkcs7_sig.c, but the utility should not be called as a test. Executing this utility produces the following error: selftests: /linux/tools/testing/selftests/bpf: urandom_read ok 16 selftests: /linux/tools/testing/selftests/bpf: urandom_read selftests: /linux/tools/testing/selftests/bpf: sign-file not ok 17 selftests: /linux/tools/testing/selftests/bpf: sign-file # exit=2 Also, urandom_read is mistakenly used as a test. It does not lead to an error, but should be moved over to TEST_GEN_FILES as well. The empty TEST_CUSTOM_PROGS can then be removed. Fixes: fc97590668ae ("selftests/bpf: Add test for bpf_verify_pkcs7_signature() kfunc") Signed-off-by: Alexey Gladkov Signed-off-by: Daniel Borkmann Reviewed-by: Roberto Sassu Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/ZEuWFk3QyML9y5QQ@example.org Link: https://lore.kernel.org/bpf/88e3ab23029d726a2703adcf6af8356f7a2d3483.1684316821.git.legion@kernel.org Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 0465ddc81f35..3b57fbf8fff4 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -85,8 +85,7 @@ TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \ test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \ xskxceiver xdp_redirect_multi xdp_synproxy veristat -TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read $(OUTPUT)/sign-file -TEST_GEN_FILES += liburandom_read.so +TEST_GEN_FILES += liburandom_read.so urandom_read sign-file # Emit succinct information message describing current building step # $1 - generic step name (e.g., CC, LINK, etc); -- cgit 1.4.1 From 6a241e6b9ed22db22916f153f99ebade49de849f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 25 May 2023 13:13:58 +0200 Subject: regulator: core: Fix more error checking for debugfs_create_dir() [ Upstream commit 2715bb11cfff964aa33946847f9527cfbd4874f5 ] In case of failure, debugfs_create_dir() does not return NULL, but an error pointer. Most incorrect error checks were fixed, but the one in create_regulator() was forgotten. Fix the remaining error check. Fixes: 2bf1c45be3b8f3a3 ("regulator: Fix error checking for debugfs_create_dir") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/ee980a108b5854dd8ce3630f8f673e784e057d17.1685013051.git.geert+renesas@glider.be Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e01cade8be0c..eaf9d99f1f43 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1918,7 +1918,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, if (err != -EEXIST) regulator->debugfs = debugfs_create_dir(supply_name, rdev->debugfs); - if (!regulator->debugfs) { + if (IS_ERR(regulator->debugfs)) { rdev_dbg(rdev, "Failed to create debugfs directory\n"); } else { debugfs_create_u32("uA_load", 0444, regulator->debugfs, -- cgit 1.4.1 From 4fc6481323df6c49ed20dd2c676aca757d09a2c6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 25 May 2023 13:13:59 +0200 Subject: regulator: core: Streamline debugfs operations [ Upstream commit 08880713ceec023dd94d634f1e8902728c385939 ] If CONFIG_DEBUG_FS is not set: regulator: Failed to create debugfs directory ... regulator-dummy: Failed to create debugfs directory As per the comments for debugfs_create_dir(), errors returned by this function should be expected, and ignored: * If debugfs is not enabled in the kernel, the value -%ENODEV will be * returned. * * NOTE: it's expected that most callers should _ignore_ the errors returned * by this function. Other debugfs functions handle the fact that the "dentry" * passed to them could be an error and they don't crash in that case. * Drivers should generally work fine even if debugfs fails to init anyway. Adhere to the debugfs spirit, and streamline all operations by: 1. Demoting the importance of the printed error messages to debug level, like is already done in create_regulator(), 2. Further ignoring any returned errors, as by design, all debugfs functions are no-ops when passed an error pointer. Fixes: 2bf1c45be3b8f3a3 ("regulator: Fix error checking for debugfs_create_dir") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/2f8bb6e113359ddfab7b59e4d4274bd4c06d6d0a.1685013051.git.geert+renesas@glider.be Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/regulator/core.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index eaf9d99f1f43..351f0fd225b1 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1918,19 +1918,17 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, if (err != -EEXIST) regulator->debugfs = debugfs_create_dir(supply_name, rdev->debugfs); - if (IS_ERR(regulator->debugfs)) { + if (IS_ERR(regulator->debugfs)) rdev_dbg(rdev, "Failed to create debugfs directory\n"); - } else { - debugfs_create_u32("uA_load", 0444, regulator->debugfs, - ®ulator->uA_load); - debugfs_create_u32("min_uV", 0444, regulator->debugfs, - ®ulator->voltage[PM_SUSPEND_ON].min_uV); - debugfs_create_u32("max_uV", 0444, regulator->debugfs, - ®ulator->voltage[PM_SUSPEND_ON].max_uV); - debugfs_create_file("constraint_flags", 0444, - regulator->debugfs, regulator, - &constraint_flags_fops); - } + + debugfs_create_u32("uA_load", 0444, regulator->debugfs, + ®ulator->uA_load); + debugfs_create_u32("min_uV", 0444, regulator->debugfs, + ®ulator->voltage[PM_SUSPEND_ON].min_uV); + debugfs_create_u32("max_uV", 0444, regulator->debugfs, + ®ulator->voltage[PM_SUSPEND_ON].max_uV); + debugfs_create_file("constraint_flags", 0444, regulator->debugfs, + regulator, &constraint_flags_fops); /* * Check now if the regulator is an always on regulator - if @@ -5257,10 +5255,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) } rdev->debugfs = debugfs_create_dir(rname, debugfs_root); - if (IS_ERR(rdev->debugfs)) { - rdev_warn(rdev, "Failed to create debugfs directory\n"); - return; - } + if (IS_ERR(rdev->debugfs)) + rdev_dbg(rdev, "Failed to create debugfs directory\n"); debugfs_create_u32("use_count", 0444, rdev->debugfs, &rdev->use_count); @@ -6180,7 +6176,7 @@ static int __init regulator_init(void) debugfs_root = debugfs_create_dir("regulator", NULL); if (IS_ERR(debugfs_root)) - pr_warn("regulator: Failed to create debugfs directory\n"); + pr_debug("regulator: Failed to create debugfs directory\n"); #ifdef CONFIG_DEBUG_FS debugfs_create_file("supply_map", 0444, debugfs_root, NULL, -- cgit 1.4.1 From 9a201822ade56a8cd950252cdb6b8a8da2021a4a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 May 2023 09:29:46 +0200 Subject: wifi: orinoco: Fix an error handling path in spectrum_cs_probe() [ Upstream commit 925244325159824385209e3e0e3f91fa6bf0646c ] Should spectrum_cs_config() fail, some resources need to be released as already done in the remove function. While at it, remove a useless and erroneous comment. The probe is spectrum_cs_probe(), not spectrum_cs_attach(). Fixes: 15b99ac17295 ("[PATCH] pcmcia: add return value to _config() functions") Signed-off-by: Christophe JAILLET Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/c0bc0c21c58ca477fc5521607615bafbf2aef8eb.1684567733.git.christophe.jaillet@wanadoo.fr Signed-off-by: Sasha Levin --- drivers/net/wireless/intersil/orinoco/spectrum_cs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intersil/orinoco/spectrum_cs.c b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c index 291ef97ed45e..841d623c621a 100644 --- a/drivers/net/wireless/intersil/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c @@ -157,6 +157,7 @@ spectrum_cs_probe(struct pcmcia_device *link) { struct orinoco_private *priv; struct orinoco_pccard *card; + int ret; priv = alloc_orinocodev(sizeof(*card), &link->dev, spectrum_cs_hard_reset, @@ -169,8 +170,16 @@ spectrum_cs_probe(struct pcmcia_device *link) card->p_dev = link; link->priv = priv; - return spectrum_cs_config(link); -} /* spectrum_cs_attach */ + ret = spectrum_cs_config(link); + if (ret) + goto err_free_orinocodev; + + return 0; + +err_free_orinocodev: + free_orinocodev(priv); + return ret; +} static void spectrum_cs_detach(struct pcmcia_device *link) { -- cgit 1.4.1 From e48b7c2416d96327f8793a7a81192bbfc0f6008f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 May 2023 09:38:22 +0200 Subject: wifi: orinoco: Fix an error handling path in orinoco_cs_probe() [ Upstream commit 67a81d911c01225f426cc6bee2373df044c1a9b7 ] Should orinoco_cs_config() fail, some resources need to be released as already done in the remove function. While at it, remove a useless and erroneous comment. The probe is orinoco_cs_probe(), not orinoco_cs_attach(). Fixes: 15b99ac17295 ("[PATCH] pcmcia: add return value to _config() functions") Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/e24735ce4d82901d5f7ea08419eea53bfdde3d65.1684568286.git.christophe.jaillet@wanadoo.fr Signed-off-by: Sasha Levin --- drivers/net/wireless/intersil/orinoco/orinoco_cs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_cs.c b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c index a956f965a1e5..03bfd2482656 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c @@ -96,6 +96,7 @@ orinoco_cs_probe(struct pcmcia_device *link) { struct orinoco_private *priv; struct orinoco_pccard *card; + int ret; priv = alloc_orinocodev(sizeof(*card), &link->dev, orinoco_cs_hard_reset, NULL); @@ -107,8 +108,16 @@ orinoco_cs_probe(struct pcmcia_device *link) card->p_dev = link; link->priv = priv; - return orinoco_cs_config(link); -} /* orinoco_cs_attach */ + ret = orinoco_cs_config(link); + if (ret) + goto err_free_orinocodev; + + return 0; + +err_free_orinocodev: + free_orinocodev(priv); + return ret; +} static void orinoco_cs_detach(struct pcmcia_device *link) { -- cgit 1.4.1 From d5569b970b81557b0854251d676868abe8656b40 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 May 2023 09:53:14 +0200 Subject: wifi: atmel: Fix an error handling path in atmel_probe() [ Upstream commit 6b92e4351a29af52c285fe235e6e4d1a75de04b2 ] Should atmel_config() fail, some resources need to be released as already done in the remove function. While at it, remove a useless and erroneous comment. The probe is atmel_probe(), not atmel_attach(). Fixes: 15b99ac17295 ("[PATCH] pcmcia: add return value to _config() functions") Signed-off-by: Christophe JAILLET Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1e65f174607a83348034197fa7d603bab10ba4a9.1684569156.git.christophe.jaillet@wanadoo.fr Signed-off-by: Sasha Levin --- drivers/net/wireless/atmel/atmel_cs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/atmel/atmel_cs.c b/drivers/net/wireless/atmel/atmel_cs.c index 453bb84cb338..58bba9875d36 100644 --- a/drivers/net/wireless/atmel/atmel_cs.c +++ b/drivers/net/wireless/atmel/atmel_cs.c @@ -72,6 +72,7 @@ struct local_info { static int atmel_probe(struct pcmcia_device *p_dev) { struct local_info *local; + int ret; dev_dbg(&p_dev->dev, "atmel_attach()\n"); @@ -82,8 +83,16 @@ static int atmel_probe(struct pcmcia_device *p_dev) p_dev->priv = local; - return atmel_config(p_dev); -} /* atmel_attach */ + ret = atmel_config(p_dev); + if (ret) + goto err_free_priv; + + return 0; + +err_free_priv: + kfree(p_dev->priv); + return ret; +} static void atmel_detach(struct pcmcia_device *link) { -- cgit 1.4.1 From 3d218755c4b6eb9827142182ab241362864d8ae3 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 May 2023 10:05:08 +0200 Subject: wifi: wl3501_cs: Fix an error handling path in wl3501_probe() [ Upstream commit 391af06a02e7642039ac5f6c4b2c034ab0992b5d ] Should wl3501_config() fail, some resources need to be released as already done in the remove function. Fixes: 15b99ac17295 ("[PATCH] pcmcia: add return value to _config() functions") Signed-off-by: Christophe JAILLET Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/7cc9c9316489b7d69b36aeb0edd3123538500b41.1684569865.git.christophe.jaillet@wanadoo.fr Signed-off-by: Sasha Levin --- drivers/net/wireless/wl3501_cs.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 7fb2f9513476..c45c4b7cbbaf 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1862,6 +1862,7 @@ static int wl3501_probe(struct pcmcia_device *p_dev) { struct net_device *dev; struct wl3501_card *this; + int ret; /* The io structure describes IO port mapping */ p_dev->resource[0]->end = 16; @@ -1873,8 +1874,7 @@ static int wl3501_probe(struct pcmcia_device *p_dev) dev = alloc_etherdev(sizeof(struct wl3501_card)); if (!dev) - goto out_link; - + return -ENOMEM; dev->netdev_ops = &wl3501_netdev_ops; dev->watchdog_timeo = 5 * HZ; @@ -1887,9 +1887,15 @@ static int wl3501_probe(struct pcmcia_device *p_dev) netif_stop_queue(dev); p_dev->priv = dev; - return wl3501_config(p_dev); -out_link: - return -ENOMEM; + ret = wl3501_config(p_dev); + if (ret) + goto out_free_etherdev; + + return 0; + +out_free_etherdev: + free_netdev(dev); + return ret; } static int wl3501_config(struct pcmcia_device *link) -- cgit 1.4.1 From 0d89e50952b5be640ed0027dd0f737eaba08e3b4 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 May 2023 10:13:22 +0200 Subject: wifi: ray_cs: Fix an error handling path in ray_probe() [ Upstream commit 4f8d66a9fb2edcd05c1e563456a55a08910bfb37 ] Should ray_config() fail, some resources need to be released as already done in the remove function. While at it, remove a useless and erroneous comment. The probe is ray_probe(), not ray_attach(). Fixes: 15b99ac17295 ("[PATCH] pcmcia: add return value to _config() functions") Signed-off-by: Christophe JAILLET Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/8c544d18084f8b37dd108e844f7e79e85ff708ff.1684570373.git.christophe.jaillet@wanadoo.fr Signed-off-by: Sasha Levin --- drivers/net/wireless/ray_cs.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 1f57a0055bbd..38782d4c4694 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -270,13 +270,14 @@ static int ray_probe(struct pcmcia_device *p_dev) { ray_dev_t *local; struct net_device *dev; + int ret; dev_dbg(&p_dev->dev, "ray_attach()\n"); /* Allocate space for private device-specific data */ dev = alloc_etherdev(sizeof(ray_dev_t)); if (!dev) - goto fail_alloc_dev; + return -ENOMEM; local = netdev_priv(dev); local->finder = p_dev; @@ -313,11 +314,16 @@ static int ray_probe(struct pcmcia_device *p_dev) timer_setup(&local->timer, NULL, 0); this_device = p_dev; - return ray_config(p_dev); + ret = ray_config(p_dev); + if (ret) + goto err_free_dev; + + return 0; -fail_alloc_dev: - return -ENOMEM; -} /* ray_attach */ +err_free_dev: + free_netdev(dev); + return ret; +} static void ray_detach(struct pcmcia_device *link) { -- cgit 1.4.1 From 09740fa9827cfbaf23ecd041e602a426f99be888 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Wed, 17 May 2023 18:03:17 +0300 Subject: wifi: ath9k: don't allow to overwrite ENDPOINT0 attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 061b0cb9327b80d7a0f63a33e7c3e2a91a71f142 ] A bad USB device is able to construct a service connection response message with target endpoint being ENDPOINT0 which is reserved for HTC_CTRL_RSVD_SVC and should not be modified to be used for any other services. Reject such service connection responses. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.") Reported-by: syzbot+b68fbebe56d8362907e8@syzkaller.appspotmail.com Signed-off-by: Fedor Pchelkin Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230516150427.79469-1-pchelkin@ispras.ru Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/htc_hst.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index fe62ff668f75..99667aba289d 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -114,7 +114,13 @@ static void htc_process_conn_rsp(struct htc_target *target, if (svc_rspmsg->status == HTC_SERVICE_SUCCESS) { epid = svc_rspmsg->endpoint_id; - if (epid < 0 || epid >= ENDPOINT_MAX) + + /* Check that the received epid for the endpoint to attach + * a new service is valid. ENDPOINT0 can't be used here as it + * is already reserved for HTC_CTRL_RSVD_SVC service and thus + * should not be modified. + */ + if (epid <= ENDPOINT0 || epid >= ENDPOINT_MAX) return; service_id = be16_to_cpu(svc_rspmsg->service_id); -- cgit 1.4.1 From cf5beb8ce97acc6c4d3aafacc220d536abc88ec9 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 30 May 2023 16:30:41 +0200 Subject: samples/bpf: xdp1 and xdp2 reduce XDPBUFSIZE to 60 [ Upstream commit 60548b825b082cedf89b275c21c28b1e1d030e50 ] Default samples/pktgen scripts send 60 byte packets as hardware adds 4-bytes FCS checksum, which fulfils minimum Ethernet 64 bytes frame size. XDP layer will not necessary have access to the 4-bytes FCS checksum. This leads to bpf_xdp_load_bytes() failing as it tries to copy 64-bytes from an XDP packet that only have 60-bytes available. Fixes: 772251742262 ("samples/bpf: fixup some tools to be able to support xdp multibuffer") Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Daniel Borkmann Reviewed-by: Tariq Toukan Link: https://lore.kernel.org/bpf/168545704139.2996228.2516528552939485216.stgit@firesoul Signed-off-by: Sasha Levin --- samples/bpf/xdp1_kern.c | 2 +- samples/bpf/xdp2_kern.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/bpf/xdp1_kern.c b/samples/bpf/xdp1_kern.c index 0a5c704badd0..d91f27cbcfa9 100644 --- a/samples/bpf/xdp1_kern.c +++ b/samples/bpf/xdp1_kern.c @@ -39,7 +39,7 @@ static int parse_ipv6(void *data, u64 nh_off, void *data_end) return ip6h->nexthdr; } -#define XDPBUFSIZE 64 +#define XDPBUFSIZE 60 SEC("xdp.frags") int xdp_prog1(struct xdp_md *ctx) { diff --git a/samples/bpf/xdp2_kern.c b/samples/bpf/xdp2_kern.c index 67804ecf7ce3..8bca674451ed 100644 --- a/samples/bpf/xdp2_kern.c +++ b/samples/bpf/xdp2_kern.c @@ -55,7 +55,7 @@ static int parse_ipv6(void *data, u64 nh_off, void *data_end) return ip6h->nexthdr; } -#define XDPBUFSIZE 64 +#define XDPBUFSIZE 60 SEC("xdp.frags") int xdp_prog1(struct xdp_md *ctx) { -- cgit 1.4.1 From 272240f20df3d575bfde9169b56bccec80b943f2 Mon Sep 17 00:00:00 2001 From: Youghandhar Chintala Date: Fri, 26 May 2023 12:41:08 +0300 Subject: wifi: ath10k: Trigger STA disconnect after reconfig complete on hardware restart [ Upstream commit 75bd32f5ce94bc365ba0b9b68bcf9de84a391d37 ] Currently, on WCN3990, the station disconnect after hardware recovery is not working as expected. This is because of setting the IEEE80211_SDATA_DISCONNECT_HW_RESTART flag very early in the hardware recovery process even before the driver invokes ieee80211_hw_restart(). On the contrary, mac80211 expects this flag to be set after ieee80211_hw_restart() is invoked for it to trigger station disconnect. Set the IEEE80211_SDATA_DISCONNECT_HW_RESTART flag in ath10k_reconfig_complete() instead to fix this. The other targets are not affected by this change, since the hardware params flag is not set. Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.2.2.c10-00754-QCAHLSWMTPL-1 Fixes: 2c3fc50591ff ("ath10k: Trigger sta disconnect on hardware restart") Signed-off-by: Youghandhar Chintala Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230518101515.3820-1-quic_youghand@quicinc.com Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath10k/core.c | 9 --------- drivers/net/wireless/ath/ath10k/mac.c | 7 +++++++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 5eb131ab916f..b6052dcc45eb 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2504,7 +2504,6 @@ EXPORT_SYMBOL(ath10k_core_napi_sync_disable); static void ath10k_core_restart(struct work_struct *work) { struct ath10k *ar = container_of(work, struct ath10k, restart_work); - struct ath10k_vif *arvif; int ret; set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags); @@ -2543,14 +2542,6 @@ static void ath10k_core_restart(struct work_struct *work) ar->state = ATH10K_STATE_RESTARTING; ath10k_halt(ar); ath10k_scan_finish(ar); - if (ar->hw_params.hw_restart_disconnect) { - list_for_each_entry(arvif, &ar->arvifs, list) { - if (arvif->is_up && - arvif->vdev_type == WMI_VDEV_TYPE_STA) - ieee80211_hw_restart_disconnect(arvif->vif); - } - } - ieee80211_restart_hw(ar->hw); break; case ATH10K_STATE_OFF: diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ec8d5b29bc72..f0729acdec50 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8108,6 +8108,7 @@ static void ath10k_reconfig_complete(struct ieee80211_hw *hw, enum ieee80211_reconfig_type reconfig_type) { struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif; if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) return; @@ -8122,6 +8123,12 @@ static void ath10k_reconfig_complete(struct ieee80211_hw *hw, ar->state = ATH10K_STATE_ON; ieee80211_wake_queues(ar->hw); clear_bit(ATH10K_FLAG_RESTARTING, &ar->dev_flags); + if (ar->hw_params.hw_restart_disconnect) { + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA) + ieee80211_hw_restart_disconnect(arvif->vif); + } + } } mutex_unlock(&ar->conf_mutex); -- cgit 1.4.1 From da79a0bc36c595dfb95d81ae36d1cd91b4eb55b2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 4 Jun 2023 12:11:20 +0300 Subject: wifi: mac80211: recalc min chandef for new STA links [ Upstream commit ba7af2654e3b7b810c750b3c6106f6f20b81cc88 ] When adding a new link to a station, this needs to cause a recalculation of the minimum chandef since otherwise we can have a higher bandwidth station connected on that link than the link is operating at. Do the appropriate recalc. Fixes: cb71f1d136a6 ("wifi: mac80211: add sta link addition/removal") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230604120651.377adf3c789a.I91bf28f399e16e6ac1f83bacd1029a698b4e6685@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/sta_info.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 30efa26f977f..b8c6f6a668fc 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2861,6 +2861,8 @@ int ieee80211_sta_activate_link(struct sta_info *sta, unsigned int link_id) if (!test_sta_flag(sta, WLAN_STA_INSERTED)) goto hash; + ieee80211_recalc_min_chandef(sdata, link_id); + /* Ensure the values are updated for the driver, * redone by sta_remove_link on failure. */ -- cgit 1.4.1 From 54257a763443d2c1af1bf83dc9c5afcf0cfc3a23 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 6 Jun 2023 13:30:47 +0200 Subject: selftests/bpf: Fix check_mtu using wrong variable type [ Upstream commit 095641817e1bf6aa2560e025e47575188ee3edaf ] Dan Carpenter found via Smatch static checker, that unsigned 'mtu_lo' is never less than zero. Variable mtu_lo should have been an 'int', because read_mtu_device_lo() uses minus as error indications. Fixes: b62eba563229 ("selftests/bpf: Tests using bpf_check_mtu BPF-helper") Reported-by: Dan Carpenter Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Daniel Borkmann Reviewed-by: Simon Horman Link: https://lore.kernel.org/bpf/168605104733.3636467.17945947801753092590.stgit@firesoul Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/check_mtu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/check_mtu.c b/tools/testing/selftests/bpf/prog_tests/check_mtu.c index 12f4395f18b3..a756760a45ed 100644 --- a/tools/testing/selftests/bpf/prog_tests/check_mtu.c +++ b/tools/testing/selftests/bpf/prog_tests/check_mtu.c @@ -183,7 +183,7 @@ cleanup: void serial_test_check_mtu(void) { - __u32 mtu_lo; + int mtu_lo; if (test__start_subtest("bpf_check_mtu XDP-attach")) test_check_mtu_xdp_attach(); -- cgit 1.4.1 From eb205a06908122f50b1dd1baa43f7c8036bfc7dc Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 28 May 2023 00:28:33 +0200 Subject: wifi: rsi: Do not configure WoWlan in shutdown hook if not enabled [ Upstream commit b241e260820b68c09586e8a0ae0fc23c0e3215bd ] In case WoWlan was never configured during the operation of the system, the hw->wiphy->wowlan_config will be NULL. rsi_config_wowlan() checks whether wowlan_config is non-NULL and if it is not, then WARNs about it. The warning is valid, as during normal operation the rsi_config_wowlan() should only ever be called with non-NULL wowlan_config. In shutdown this rsi_config_wowlan() should only ever be called if WoWlan was configured before by the user. Add checks for non-NULL wowlan_config into the shutdown hook. While at it, check whether the wiphy is also non-NULL before accessing wowlan_config . Drop the single-use wowlan_config variable, just inline it into function call. Fixes: 16bbc3eb8372 ("rsi: fix null pointer dereference during rsi_shutdown()") Signed-off-by: Marek Vasut Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230527222833.273741-1-marex@denx.de Signed-off-by: Sasha Levin --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index d09998796ac0..6e33a2563fdb 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -1463,10 +1463,8 @@ static void rsi_shutdown(struct device *dev) rsi_dbg(ERR_ZONE, "SDIO Bus shutdown =====>\n"); - if (hw) { - struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config; - - if (rsi_config_wowlan(adapter, wowlan)) + if (hw && hw->wiphy && hw->wiphy->wowlan_config) { + if (rsi_config_wowlan(adapter, hw->wiphy->wowlan_config)) rsi_dbg(ERR_ZONE, "Failed to configure WoWLAN\n"); } -- cgit 1.4.1 From 77f09d836bc92e0783982ee9bc5486325387c2dd Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 28 May 2023 00:28:59 +0200 Subject: wifi: rsi: Do not set MMC_PM_KEEP_POWER in shutdown [ Upstream commit e74f562328b03fbe9cf438f958464dff3a644dfc ] It makes no sense to set MMC_PM_KEEP_POWER in shutdown. The flag indicates to the MMC subsystem to keep the slot powered on during suspend, but in shutdown the slot should actually be powered off. Drop this call. Fixes: 063848c3e155 ("rsi: sdio: Add WOWLAN support for S5 shutdown state") Signed-off-by: Marek Vasut Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230527222859.273768-1-marex@denx.de Signed-off-by: Sasha Levin --- drivers/net/wireless/rsi/rsi_91x_sdio.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index 6e33a2563fdb..1911fef3bbad 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -1479,9 +1479,6 @@ static void rsi_shutdown(struct device *dev) if (sdev->write_fail) rsi_dbg(INFO_ZONE, "###### Device is not ready #######\n"); - if (rsi_set_sdio_pm_caps(adapter)) - rsi_dbg(INFO_ZONE, "Setting power management caps failed\n"); - rsi_dbg(INFO_ZONE, "***** RSI module shut down *****\n"); } -- cgit 1.4.1 From 75704a10eac2ee1f83a127e5ea6dff5bcfd3996f Mon Sep 17 00:00:00 2001 From: Karol Kolacinski Date: Thu, 1 Jun 2023 14:15:03 -0700 Subject: ice: handle extts in the miscellaneous interrupt thread [ Upstream commit 6e8b2c88fc8cf95ed09de25946b20b7536c88cd5 ] The ice_ptp_extts_work() and ice_ptp_periodic_work() functions are both scheduled on the same kthread worker, pf.ptp.kworker. The ice_ptp_periodic_work() function sends to the firmware to interact with the PHY, and must block to wait for responses. This can cause delay in responding to the PFINT_OICR_TSYN_EVNT interrupt cause, ultimately resulting in disruption to processing an input signal of the frequency is high enough. In our testing, even 100 Hz signals get disrupted. Fix this by instead processing the signal inside the miscellaneous interrupt thread prior to handling Tx timestamps. Use atomic bits in a new pf->misc_thread bitmap in order to safely communicate which tasks require processing within the ice_misc_intr_thread_fn(). This ensures the communication of desired tasks from the ice_misc_intr() are correctly processed without racing even in the event that the interrupt triggers again before the thread function exits. Fixes: 172db5f91d5f ("ice: add support for auxiliary input/output pins") Signed-off-by: Karol Kolacinski Signed-off-by: Jacob Keller Tested-by: Arpana Arland (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice.h | 7 +++++++ drivers/net/ethernet/intel/ice/ice_main.c | 29 +++++++++++++++++++++-------- drivers/net/ethernet/intel/ice/ice_ptp.c | 12 +++--------- drivers/net/ethernet/intel/ice/ice_ptp.h | 4 ++-- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index ca6b877fdde8..f2be383d97df 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -491,6 +491,12 @@ enum ice_pf_flags { ICE_PF_FLAGS_NBITS /* must be last */ }; +enum ice_misc_thread_tasks { + ICE_MISC_THREAD_EXTTS_EVENT, + ICE_MISC_THREAD_TX_TSTAMP, + ICE_MISC_THREAD_NBITS /* must be last */ +}; + struct ice_switchdev_info { struct ice_vsi *control_vsi; struct ice_vsi *uplink_vsi; @@ -532,6 +538,7 @@ struct ice_pf { DECLARE_BITMAP(features, ICE_F_MAX); DECLARE_BITMAP(state, ICE_STATE_NBITS); DECLARE_BITMAP(flags, ICE_PF_FLAGS_NBITS); + DECLARE_BITMAP(misc_thread, ICE_MISC_THREAD_NBITS); unsigned long *avail_txqs; /* bitmap to track PF Tx queue usage */ unsigned long *avail_rxqs; /* bitmap to track PF Rx queue usage */ unsigned long serv_tmr_period; diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 4095fe40dfc9..7a5ec3ce3407 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -3116,20 +3116,28 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data) if (oicr & PFINT_OICR_TSYN_TX_M) { ena_mask &= ~PFINT_OICR_TSYN_TX_M; - if (!hw->reset_ongoing) + if (!hw->reset_ongoing) { + set_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread); ret = IRQ_WAKE_THREAD; + } } if (oicr & PFINT_OICR_TSYN_EVNT_M) { u8 tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; u32 gltsyn_stat = rd32(hw, GLTSYN_STAT(tmr_idx)); - /* Save EVENTs from GTSYN register */ - pf->ptp.ext_ts_irq |= gltsyn_stat & (GLTSYN_STAT_EVENT0_M | - GLTSYN_STAT_EVENT1_M | - GLTSYN_STAT_EVENT2_M); ena_mask &= ~PFINT_OICR_TSYN_EVNT_M; - kthread_queue_work(pf->ptp.kworker, &pf->ptp.extts_work); + + if (hw->func_caps.ts_func_info.src_tmr_owned) { + /* Save EVENTs from GLTSYN register */ + pf->ptp.ext_ts_irq |= gltsyn_stat & + (GLTSYN_STAT_EVENT0_M | + GLTSYN_STAT_EVENT1_M | + GLTSYN_STAT_EVENT2_M); + + set_bit(ICE_MISC_THREAD_EXTTS_EVENT, pf->misc_thread); + ret = IRQ_WAKE_THREAD; + } } #define ICE_AUX_CRIT_ERR (PFINT_OICR_PE_CRITERR_M | PFINT_OICR_HMC_ERR_M | PFINT_OICR_PE_PUSH_M) @@ -3173,8 +3181,13 @@ static irqreturn_t ice_misc_intr_thread_fn(int __always_unused irq, void *data) if (ice_is_reset_in_progress(pf->state)) return IRQ_HANDLED; - while (!ice_ptp_process_ts(pf)) - usleep_range(50, 100); + if (test_and_clear_bit(ICE_MISC_THREAD_EXTTS_EVENT, pf->misc_thread)) + ice_ptp_extts_event(pf); + + if (test_and_clear_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread)) { + while (!ice_ptp_process_ts(pf)) + usleep_range(50, 100); + } return IRQ_HANDLED; } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index a3585ede829b..46b0063a5e12 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1478,15 +1478,11 @@ static int ice_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm) } /** - * ice_ptp_extts_work - Workqueue task function - * @work: external timestamp work structure - * - * Service for PTP external clock event + * ice_ptp_extts_event - Process PTP external clock event + * @pf: Board private structure */ -static void ice_ptp_extts_work(struct kthread_work *work) +void ice_ptp_extts_event(struct ice_pf *pf) { - struct ice_ptp *ptp = container_of(work, struct ice_ptp, extts_work); - struct ice_pf *pf = container_of(ptp, struct ice_pf, ptp); struct ptp_clock_event event; struct ice_hw *hw = &pf->hw; u8 chan, tmr_idx; @@ -2512,7 +2508,6 @@ void ice_ptp_prepare_for_reset(struct ice_pf *pf) ice_ptp_cfg_timestamp(pf, false); kthread_cancel_delayed_work_sync(&ptp->work); - kthread_cancel_work_sync(&ptp->extts_work); if (test_bit(ICE_PFR_REQ, pf->state)) return; @@ -2610,7 +2605,6 @@ static int ice_ptp_init_work(struct ice_pf *pf, struct ice_ptp *ptp) /* Initialize work functions */ kthread_init_delayed_work(&ptp->work, ice_ptp_periodic_work); - kthread_init_work(&ptp->extts_work, ice_ptp_extts_work); /* Allocate a kworker for handling work required for the ports * connected to the PTP hardware clock. diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 028349295b71..e689c05bb001 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -159,7 +159,6 @@ struct ice_ptp_port { * struct ice_ptp - data used for integrating with CONFIG_PTP_1588_CLOCK * @port: data for the PHY port initialization procedure * @work: delayed work function for periodic tasks - * @extts_work: work function for handling external Tx timestamps * @cached_phc_time: a cached copy of the PHC time for timestamp extension * @cached_phc_jiffies: jiffies when cached_phc_time was last updated * @ext_ts_chan: the external timestamp channel in use @@ -180,7 +179,6 @@ struct ice_ptp_port { struct ice_ptp { struct ice_ptp_port port; struct kthread_delayed_work work; - struct kthread_work extts_work; u64 cached_phc_time; unsigned long cached_phc_jiffies; u8 ext_ts_chan; @@ -246,6 +244,7 @@ int ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr); void ice_ptp_cfg_timestamp(struct ice_pf *pf, bool ena); int ice_get_ptp_clock_index(struct ice_pf *pf); +void ice_ptp_extts_event(struct ice_pf *pf); s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb); bool ice_ptp_process_ts(struct ice_pf *pf); @@ -274,6 +273,7 @@ static inline int ice_get_ptp_clock_index(struct ice_pf *pf) return -1; } +static inline void ice_ptp_extts_event(struct ice_pf *pf) { } static inline s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) { -- cgit 1.4.1 From b27af27fc9d58084c0a3d0245079c804f54348e1 Mon Sep 17 00:00:00 2001 From: Haifeng Xu Date: Mon, 22 May 2023 09:52:33 +0000 Subject: selftests: cgroup: fix unexpected failure on test_memcg_low [ Upstream commit 19ab365762c6cc39dfdee9e13ab0d12fe4b5540d ] Since commit f079a020ba95 ("selftests: memcg: factor out common parts of memory.{low,min} tests"), the value used in second alloc_anon has changed from 148M to 170M. Because memory.low allows reclaiming page cache in child cgroups, so the memory.current is close to 30M instead of 50M. Therefore, adjust the expected value of parent cgroup. Link: https://lkml.kernel.org/r/20230522095233.4246-2-haifeng.xu@shopee.com Fixes: f079a020ba95 ("selftests: memcg: factor out common parts of memory.{low,min} tests") Signed-off-by: Haifeng Xu Cc: Johannes Weiner Cc: Michal Hocko Cc: Roman Gushchin Cc: Shakeel Butt Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/cgroup/test_memcontrol.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c index fe4f9f430282..5a526a8e7d33 100644 --- a/tools/testing/selftests/cgroup/test_memcontrol.c +++ b/tools/testing/selftests/cgroup/test_memcontrol.c @@ -284,6 +284,7 @@ static int test_memcg_protection(const char *root, bool min) char *children[4] = {NULL}; const char *attribute = min ? "memory.min" : "memory.low"; long c[4]; + long current; int i, attempts; int fd; @@ -392,7 +393,8 @@ static int test_memcg_protection(const char *root, bool min) goto cleanup; } - if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3)) + current = min ? MB(50) : MB(30); + if (!values_close(cg_read_long(parent[1], "memory.current"), current, 3)) goto cleanup; if (min) { -- cgit 1.4.1 From a3cf423b582abc59385d58d2c6eeaa965534792b Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 19 May 2023 10:18:25 -0700 Subject: watchdog/perf: define dummy watchdog_update_hrtimer_threshold() on correct config [ Upstream commit 5e008df11c55228a86a1bae692cc2002503572c9 ] Patch series "watchdog/hardlockup: Add the buddy hardlockup detector", v5. This patch series adds the "buddy" hardlockup detector. In brief, the buddy hardlockup detector can detect hardlockups without arch-level support by having CPUs checkup on a "buddy" CPU periodically. Given the new design of this patch series, testing all combinations is fairly difficult. I've attempted to make sure that all combinations of CONFIG_ options are good, but it wouldn't surprise me if I missed something. I apologize in advance and I'll do my best to fix any problems that are found. This patch (of 18): The real watchdog_update_hrtimer_threshold() is defined in kernel/watchdog_hld.c. That file is included if CONFIG_HARDLOCKUP_DETECTOR_PERF and the function is defined in that file if CONFIG_HARDLOCKUP_CHECK_TIMESTAMP. The dummy version of the function in "nmi.h" didn't get that quite right. While this doesn't appear to be a huge deal, it's nice to make it consistent. It doesn't break builds because CHECK_TIMESTAMP is only defined by x86 so others don't get a double definition, and x86 uses perf lockup detector, so it gets the out of line version. Link: https://lkml.kernel.org/r/20230519101840.v5.18.Ia44852044cdcb074f387e80df6b45e892965d4a1@changeid Link: https://lkml.kernel.org/r/20230519101840.v5.1.I8cbb2f4fa740528fcfade4f5439b6cdcdd059251@changeid Fixes: 7edaeb6841df ("kernel/watchdog: Prevent false positives with turbo modes") Signed-off-by: Douglas Anderson Reviewed-by: Nicholas Piggin Reviewed-by: Petr Mladek Cc: Andi Kleen Cc: Catalin Marinas Cc: Chen-Yu Tsai Cc: Christophe Leroy Cc: Daniel Thompson Cc: "David S. Miller" Cc: Guenter Roeck Cc: Ian Rogers Cc: Lecopzer Chen Cc: Marc Zyngier Cc: Mark Rutland Cc: Masayoshi Mizuma Cc: Matthias Kaehlcke Cc: Michael Ellerman Cc: Pingfan Liu Cc: Randy Dunlap Cc: "Ravi V. Shankar" Cc: Ricardo Neri Cc: Stephane Eranian Cc: Stephen Boyd Cc: Sumit Garg Cc: Tzung-Bi Shih Cc: Will Deacon Cc: Colin Cross Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- include/linux/nmi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/nmi.h b/include/linux/nmi.h index f700ff2df074..0db377ff8f60 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -197,7 +197,7 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh); #endif #if defined(CONFIG_HARDLOCKUP_CHECK_TIMESTAMP) && \ - defined(CONFIG_HARDLOCKUP_DETECTOR) + defined(CONFIG_HARDLOCKUP_DETECTOR_PERF) void watchdog_update_hrtimer_threshold(u64 period); #else static inline void watchdog_update_hrtimer_threshold(u64 period) { } -- cgit 1.4.1 From 6525435d147b3476045c2a489ceceeaeab509b5e Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 19 May 2023 10:18:26 -0700 Subject: watchdog/perf: more properly prevent false positives with turbo modes [ Upstream commit 4379e59fe5665cfda737e45b8bf2f05321ef049c ] Currently, in the watchdog_overflow_callback() we first check to see if the watchdog had been touched and _then_ we handle the workaround for turbo mode. This order should be reversed. Specifically, "touching" the hardlockup detector's watchdog should avoid lockups being detected for one period that should be roughly the same regardless of whether we're running turbo or not. That means that we should do the extra accounting for turbo _before_ we look at (and clear) the global indicating that we've been touched. NOTE: this fix is made based on code inspection. I am not aware of any reports where the old code would have generated false positives. That being said, this order seems more correct and also makes it easier down the line to share code with the "buddy" hardlockup detector. Link: https://lkml.kernel.org/r/20230519101840.v5.2.I843b0d1de3e096ba111a179f3adb16d576bef5c7@changeid Fixes: 7edaeb6841df ("kernel/watchdog: Prevent false positives with turbo modes") Signed-off-by: Douglas Anderson Cc: Andi Kleen Cc: Catalin Marinas Cc: Chen-Yu Tsai Cc: Christophe Leroy Cc: Colin Cross Cc: Daniel Thompson Cc: "David S. Miller" Cc: Guenter Roeck Cc: Ian Rogers Cc: Lecopzer Chen Cc: Marc Zyngier Cc: Mark Rutland Cc: Masayoshi Mizuma Cc: Matthias Kaehlcke Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Petr Mladek Cc: Pingfan Liu Cc: Randy Dunlap Cc: "Ravi V. Shankar" Cc: Ricardo Neri Cc: Stephane Eranian Cc: Stephen Boyd Cc: Sumit Garg Cc: Tzung-Bi Shih Cc: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- kernel/watchdog_hld.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/watchdog_hld.c b/kernel/watchdog_hld.c index 247bf0b1582c..1e8a49dc956e 100644 --- a/kernel/watchdog_hld.c +++ b/kernel/watchdog_hld.c @@ -114,14 +114,14 @@ static void watchdog_overflow_callback(struct perf_event *event, /* Ensure the watchdog never gets throttled */ event->hw.interrupts = 0; + if (!watchdog_check_timestamp()) + return; + if (__this_cpu_read(watchdog_nmi_touch) == true) { __this_cpu_write(watchdog_nmi_touch, false); return; } - if (!watchdog_check_timestamp()) - return; - /* check for a hardlockup * This is done by making sure our timer interrupt * is incrementing. The timer interrupt should have -- cgit 1.4.1 From fd4f89302fe08b220730b0757f5cc7ed74a226cc Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Sat, 27 May 2023 20:34:34 +0800 Subject: kexec: fix a memory leak in crash_shrink_memory() [ Upstream commit 1cba6c4309f03de570202c46f03df3f73a0d4c82 ] Patch series "kexec: enable kexec_crash_size to support two crash kernel regions". When crashkernel=X fails to reserve region under 4G, it will fall back to reserve region above 4G and a region of the default size will also be reserved under 4G. Unfortunately, /sys/kernel/kexec_crash_size only supports one crash kernel region now, the user cannot sense the low memory reserved by reading /sys/kernel/kexec_crash_size. Also, low memory cannot be freed by writing this file. For example: resource_size(crashk_res) = 512M resource_size(crashk_low_res) = 256M The result of 'cat /sys/kernel/kexec_crash_size' is 512M, but it should be 768M. When we execute 'echo 0 > /sys/kernel/kexec_crash_size', the size of crashk_res becomes 0 and resource_size(crashk_low_res) is still 256 MB, which is incorrect. Since crashk_res manages the memory with high address and crashk_low_res manages the memory with low address, crashk_low_res is shrunken only when all crashk_res is shrunken. And because when there is only one crash kernel region, crashk_res is always used. Therefore, if all crashk_res is shrunken and crashk_low_res still exists, swap them. This patch (of 6): If the value of parameter 'new_size' is in the semi-open and semi-closed interval (crashk_res.end - KEXEC_CRASH_MEM_ALIGN + 1, crashk_res.end], the calculation result of ram_res is: ram_res->start = crashk_res.end + 1 ram_res->end = crashk_res.end The operation of insert_resource() fails, and ram_res is not added to iomem_resource. As a result, the memory of the control block ram_res is leaked. In fact, on all architectures, the start address and size of crashk_res are already aligned by KEXEC_CRASH_MEM_ALIGN. Therefore, we do not need to round up crashk_res.start again. Instead, we should round up 'new_size' in advance. Link: https://lkml.kernel.org/r/20230527123439.772-1-thunder.leizhen@huawei.com Link: https://lkml.kernel.org/r/20230527123439.772-2-thunder.leizhen@huawei.com Fixes: 6480e5a09237 ("kdump: add missing RAM resource in crash_shrink_memory()") Fixes: 06a7f711246b ("kexec: premit reduction of the reserved memory size") Signed-off-by: Zhen Lei Acked-by: Baoquan He Cc: Cong Wang Cc: Eric W. Biederman Cc: Michael Holzheu Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- kernel/kexec_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index ca2743f9c634..79c012fbb49c 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -1035,6 +1035,7 @@ int crash_shrink_memory(unsigned long new_size) start = crashk_res.start; end = crashk_res.end; old_size = (end == 0) ? 0 : end - start + 1; + new_size = roundup(new_size, KEXEC_CRASH_MEM_ALIGN); if (new_size >= old_size) { ret = (new_size == old_size) ? 0 : -EINVAL; goto unlock; @@ -1046,9 +1047,7 @@ int crash_shrink_memory(unsigned long new_size) goto unlock; } - start = roundup(start, KEXEC_CRASH_MEM_ALIGN); - end = roundup(start + new_size, KEXEC_CRASH_MEM_ALIGN); - + end = start + new_size; crash_free_reserved_phys_range(end, crashk_res.end); if ((start == end) && (crashk_res.parent != NULL)) -- cgit 1.4.1 From 93126e39662b6d508d52f5f2dddaa9c9a5074179 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 10 May 2023 06:44:54 -0700 Subject: mmc: mediatek: Avoid ugly error message when SDIO wakeup IRQ isn't used [ Upstream commit a3332b7aad346b14770797e03ddd02ebdb14db41 ] When I boot a kukui-kodama board, I see an ugly warning in my kernel log: mtk-msdc 11240000.mmc: error -ENXIO: IRQ sdio_wakeup not found It's pretty normal not to have an "sdio_wakeup" IRQ defined. In fact, no device trees in mainline seem to have it. Let's use the platform_get_irq_byname_optional() to avoid the error message. Fixes: 527f36f5efa4 ("mmc: mediatek: add support for SDIO eint wakup IRQ") Signed-off-by: Douglas Anderson Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/r/20230510064434.1.I935404c5396e6bf952e99bb7ffb744c6f7fd430b@changeid Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/host/mtk-sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 425efb3fba04..1a0d4dc24717 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -2685,7 +2685,7 @@ static int msdc_drv_probe(struct platform_device *pdev) /* Support for SDIO eint irq ? */ if ((mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) && (mmc->pm_caps & MMC_PM_KEEP_POWER)) { - host->eint_irq = platform_get_irq_byname(pdev, "sdio_wakeup"); + host->eint_irq = platform_get_irq_byname_optional(pdev, "sdio_wakeup"); if (host->eint_irq > 0) { host->pins_eint = pinctrl_lookup_state(host->pinctrl, "state_eint"); if (IS_ERR(host->pins_eint)) { -- cgit 1.4.1 From 16b5292beeeef8171f0caaedd864dd9503526ce4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 22:27:04 +0200 Subject: memstick r592: make memstick_debug_get_tpc_name() static [ Upstream commit 434587df9f7fd68575f99a889cc5f2efc2eaee5e ] There are no other files referencing this function, apparently it was left global to avoid an 'unused function' warning when the only caller is left out. With a 'W=1' build, it causes a 'missing prototype' warning though: drivers/memstick/host/r592.c:47:13: error: no previous prototype for 'memstick_debug_get_tpc_name' [-Werror=missing-prototypes] Annotate the function as 'static __maybe_unused' to avoid both problems. Fixes: 926341250102 ("memstick: add driver for Ricoh R5C592 card reader") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230516202714.560929-1-arnd@kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/memstick/host/r592.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c index 42bfc46842b8..461f5ffd02bc 100644 --- a/drivers/memstick/host/r592.c +++ b/drivers/memstick/host/r592.c @@ -44,12 +44,10 @@ static const char *tpc_names[] = { * memstick_debug_get_tpc_name - debug helper that returns string for * a TPC number */ -const char *memstick_debug_get_tpc_name(int tpc) +static __maybe_unused const char *memstick_debug_get_tpc_name(int tpc) { return tpc_names[tpc-1]; } -EXPORT_SYMBOL(memstick_debug_get_tpc_name); - /* Read a register*/ static inline u32 r592_read_reg(struct r592_device *dev, int address) -- cgit 1.4.1 From 41fc1c56787fbb213f4292b43356fa2cbfafc6c5 Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Fri, 9 Jun 2023 11:37:44 +0200 Subject: wifi: ath9k: Fix possible stall on ath9k_txq_list_has_key() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 75086cc6dee046e3fbb3dba148b376d8802f83bc ] On EDMA capable hardware, ath9k_txq_list_has_key() can enter infinite loop if it is called while all txq_fifos have packets that use different key that the one we are looking for. Fix it by exiting the loop if all txq_fifos have been checked already. Because this loop is called under spin_lock_bh() (see ath_txq_lock) it causes the following rcu stall: rcu: INFO: rcu_sched self-detected stall on CPU ath10k_pci 0000:01:00.0: failed to read temperature -11 rcu: 1-....: (5254 ticks this GP) idle=189/1/0x4000000000000002 softirq=8442983/8442984 fqs=2579 (t=5257 jiffies g=17983297 q=334) Task dump for CPU 1: task:hostapd state:R running task stack: 0 pid: 297 ppid: 289 flags:0x0000000a Call trace: dump_backtrace+0x0/0x170 show_stack+0x1c/0x24 sched_show_task+0x140/0x170 dump_cpu_task+0x48/0x54 rcu_dump_cpu_stacks+0xf0/0x134 rcu_sched_clock_irq+0x8d8/0x9fc update_process_times+0xa0/0xec tick_sched_timer+0x5c/0xd0 __hrtimer_run_queues+0x154/0x320 hrtimer_interrupt+0x120/0x2f0 arch_timer_handler_virt+0x38/0x44 handle_percpu_devid_irq+0x9c/0x1e0 handle_domain_irq+0x64/0x90 gic_handle_irq+0x78/0xb0 call_on_irq_stack+0x28/0x38 do_interrupt_handler+0x54/0x5c el1_interrupt+0x2c/0x4c el1h_64_irq_handler+0x14/0x1c el1h_64_irq+0x74/0x78 ath9k_txq_has_key+0x1bc/0x250 [ath9k] ath9k_set_key+0x1cc/0x3dc [ath9k] drv_set_key+0x78/0x170 ieee80211_key_replace+0x564/0x6cc ieee80211_key_link+0x174/0x220 ieee80211_add_key+0x11c/0x300 nl80211_new_key+0x12c/0x330 genl_family_rcv_msg_doit+0xbc/0x11c genl_rcv_msg+0xd8/0x1c4 netlink_rcv_skb+0x40/0x100 genl_rcv+0x3c/0x50 netlink_unicast+0x1ec/0x2c0 netlink_sendmsg+0x198/0x3c0 ____sys_sendmsg+0x210/0x250 ___sys_sendmsg+0x78/0xc4 __sys_sendmsg+0x4c/0x90 __arm64_sys_sendmsg+0x28/0x30 invoke_syscall.constprop.0+0x60/0x100 do_el0_svc+0x48/0xd0 el0_svc+0x14/0x50 el0t_64_sync_handler+0xa8/0xb0 el0t_64_sync+0x158/0x15c This rcu stall is hard to reproduce as is, but changing ATH_TXFIFO_DEPTH from 8 to 2 makes it reasonably easy to reproduce. Fixes: ca2848022c12 ("ath9k: Postpone key cache entry deletion for TXQ frames reference it") Signed-off-by: Remi Pommarel Tested-by: Nicolas Escande Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230609093744.1985-1-repk@triplefau.lt Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a4197c14f0a9..7f9f06ea8a05 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -850,7 +850,7 @@ static bool ath9k_txq_list_has_key(struct list_head *txq_list, u32 keyix) static bool ath9k_txq_has_key(struct ath_softc *sc, u32 keyix) { struct ath_hw *ah = sc->sc_ah; - int i; + int i, j; struct ath_txq *txq; bool key_in_use = false; @@ -868,8 +868,9 @@ static bool ath9k_txq_has_key(struct ath_softc *sc, u32 keyix) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { int idx = txq->txq_tailidx; - while (!key_in_use && - !list_empty(&txq->txq_fifo[idx])) { + for (j = 0; !key_in_use && + !list_empty(&txq->txq_fifo[idx]) && + j < ATH_TXFIFO_DEPTH; j++) { key_in_use = ath9k_txq_list_has_key( &txq->txq_fifo[idx], keyix); INCR(idx, ATH_TXFIFO_DEPTH); -- cgit 1.4.1 From c682018f5c925c9639e0ae7dcc0a47f5cb531b80 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 11 Jun 2023 12:14:28 +0300 Subject: wifi: mac80211: Fix permissions for valid_links debugfs entry [ Upstream commit 4cacadc0dbd8013e6161aa8843d8e9d8ad435b47 ] The entry should be a read only one and not a write only one. Fix it. Fixes: 3d9011029227 ("wifi: mac80211: implement link switching") Signed-off-by: Ilan Peer Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230611121219.c75316990411.I1565a7fcba8a37f83efffb0cc6b71c572b896e94@changeid [remove x16 change since it doesn't work yet] Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/debugfs_netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 5b014786fd2d..08a1d7564b7f 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -694,7 +694,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD_MODE(uapsd_queues, 0600); DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600); DEBUGFS_ADD_MODE(tdls_wider_bw, 0600); - DEBUGFS_ADD_MODE(valid_links, 0200); + DEBUGFS_ADD_MODE(valid_links, 0400); DEBUGFS_ADD_MODE(active_links, 0600); } -- cgit 1.4.1 From 228dd5d5fda0aa4d9c92b534c4d03238785c9053 Mon Sep 17 00:00:00 2001 From: Edwin Peer Date: Sun, 11 Jun 2023 13:51:08 +0300 Subject: rtnetlink: extend RTEXT_FILTER_SKIP_STATS to IFLA_VF_INFO [ Upstream commit fa0e21fa44438a0e856d42224bfa24641d37b979 ] This filter already exists for excluding IPv6 SNMP stats. Extend its definition to also exclude IFLA_VF_INFO stats in RTM_GETLINK. This patch constitutes a partial fix for a netlink attribute nesting overflow bug in IFLA_VFINFO_LIST. By excluding the stats when the requester doesn't need them, the truncation of the VF list is avoided. While it was technically only the stats added in commit c5a9f6f0ab40 ("net/core: Add drop counters to VF statistics") breaking the camel's back, the appreciable size of the stats data should never have been included without due consideration for the maximum number of VFs supported by PCI. Fixes: 3b766cd83232 ("net/core: Add reading VF statistics through the PF netdevice") Fixes: c5a9f6f0ab40 ("net/core: Add drop counters to VF statistics") Signed-off-by: Edwin Peer Cc: Edwin Peer Signed-off-by: Gal Pressman Link: https://lore.kernel.org/r/20230611105108.122586-1-gal@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/core/rtnetlink.c | 96 ++++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b192c69f3936..ef2a07ae5496 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -957,24 +957,27 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev, nla_total_size(sizeof(struct ifla_vf_rate)) + nla_total_size(sizeof(struct ifla_vf_link_state)) + nla_total_size(sizeof(struct ifla_vf_rss_query_en)) + - nla_total_size(0) + /* nest IFLA_VF_STATS */ - /* IFLA_VF_STATS_RX_PACKETS */ - nla_total_size_64bit(sizeof(__u64)) + - /* IFLA_VF_STATS_TX_PACKETS */ - nla_total_size_64bit(sizeof(__u64)) + - /* IFLA_VF_STATS_RX_BYTES */ - nla_total_size_64bit(sizeof(__u64)) + - /* IFLA_VF_STATS_TX_BYTES */ - nla_total_size_64bit(sizeof(__u64)) + - /* IFLA_VF_STATS_BROADCAST */ - nla_total_size_64bit(sizeof(__u64)) + - /* IFLA_VF_STATS_MULTICAST */ - nla_total_size_64bit(sizeof(__u64)) + - /* IFLA_VF_STATS_RX_DROPPED */ - nla_total_size_64bit(sizeof(__u64)) + - /* IFLA_VF_STATS_TX_DROPPED */ - nla_total_size_64bit(sizeof(__u64)) + nla_total_size(sizeof(struct ifla_vf_trust))); + if (~ext_filter_mask & RTEXT_FILTER_SKIP_STATS) { + size += num_vfs * + (nla_total_size(0) + /* nest IFLA_VF_STATS */ + /* IFLA_VF_STATS_RX_PACKETS */ + nla_total_size_64bit(sizeof(__u64)) + + /* IFLA_VF_STATS_TX_PACKETS */ + nla_total_size_64bit(sizeof(__u64)) + + /* IFLA_VF_STATS_RX_BYTES */ + nla_total_size_64bit(sizeof(__u64)) + + /* IFLA_VF_STATS_TX_BYTES */ + nla_total_size_64bit(sizeof(__u64)) + + /* IFLA_VF_STATS_BROADCAST */ + nla_total_size_64bit(sizeof(__u64)) + + /* IFLA_VF_STATS_MULTICAST */ + nla_total_size_64bit(sizeof(__u64)) + + /* IFLA_VF_STATS_RX_DROPPED */ + nla_total_size_64bit(sizeof(__u64)) + + /* IFLA_VF_STATS_TX_DROPPED */ + nla_total_size_64bit(sizeof(__u64))); + } return size; } else return 0; @@ -1253,7 +1256,8 @@ static noinline_for_stack int rtnl_fill_stats(struct sk_buff *skb, static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb, struct net_device *dev, int vfs_num, - struct nlattr *vfinfo) + struct nlattr *vfinfo, + u32 ext_filter_mask) { struct ifla_vf_rss_query_en vf_rss_query_en; struct nlattr *vf, *vfstats, *vfvlanlist; @@ -1359,33 +1363,35 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb, goto nla_put_vf_failure; } nla_nest_end(skb, vfvlanlist); - memset(&vf_stats, 0, sizeof(vf_stats)); - if (dev->netdev_ops->ndo_get_vf_stats) - dev->netdev_ops->ndo_get_vf_stats(dev, vfs_num, - &vf_stats); - vfstats = nla_nest_start_noflag(skb, IFLA_VF_STATS); - if (!vfstats) - goto nla_put_vf_failure; - if (nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_PACKETS, - vf_stats.rx_packets, IFLA_VF_STATS_PAD) || - nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_PACKETS, - vf_stats.tx_packets, IFLA_VF_STATS_PAD) || - nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_BYTES, - vf_stats.rx_bytes, IFLA_VF_STATS_PAD) || - nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_BYTES, - vf_stats.tx_bytes, IFLA_VF_STATS_PAD) || - nla_put_u64_64bit(skb, IFLA_VF_STATS_BROADCAST, - vf_stats.broadcast, IFLA_VF_STATS_PAD) || - nla_put_u64_64bit(skb, IFLA_VF_STATS_MULTICAST, - vf_stats.multicast, IFLA_VF_STATS_PAD) || - nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_DROPPED, - vf_stats.rx_dropped, IFLA_VF_STATS_PAD) || - nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_DROPPED, - vf_stats.tx_dropped, IFLA_VF_STATS_PAD)) { - nla_nest_cancel(skb, vfstats); - goto nla_put_vf_failure; + if (~ext_filter_mask & RTEXT_FILTER_SKIP_STATS) { + memset(&vf_stats, 0, sizeof(vf_stats)); + if (dev->netdev_ops->ndo_get_vf_stats) + dev->netdev_ops->ndo_get_vf_stats(dev, vfs_num, + &vf_stats); + vfstats = nla_nest_start_noflag(skb, IFLA_VF_STATS); + if (!vfstats) + goto nla_put_vf_failure; + if (nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_PACKETS, + vf_stats.rx_packets, IFLA_VF_STATS_PAD) || + nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_PACKETS, + vf_stats.tx_packets, IFLA_VF_STATS_PAD) || + nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_BYTES, + vf_stats.rx_bytes, IFLA_VF_STATS_PAD) || + nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_BYTES, + vf_stats.tx_bytes, IFLA_VF_STATS_PAD) || + nla_put_u64_64bit(skb, IFLA_VF_STATS_BROADCAST, + vf_stats.broadcast, IFLA_VF_STATS_PAD) || + nla_put_u64_64bit(skb, IFLA_VF_STATS_MULTICAST, + vf_stats.multicast, IFLA_VF_STATS_PAD) || + nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_DROPPED, + vf_stats.rx_dropped, IFLA_VF_STATS_PAD) || + nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_DROPPED, + vf_stats.tx_dropped, IFLA_VF_STATS_PAD)) { + nla_nest_cancel(skb, vfstats); + goto nla_put_vf_failure; + } + nla_nest_end(skb, vfstats); } - nla_nest_end(skb, vfstats); nla_nest_end(skb, vf); return 0; @@ -1418,7 +1424,7 @@ static noinline_for_stack int rtnl_fill_vf(struct sk_buff *skb, return -EMSGSIZE; for (i = 0; i < num_vfs; i++) { - if (rtnl_fill_vfinfo(skb, dev, i, vfinfo)) + if (rtnl_fill_vfinfo(skb, dev, i, vfinfo, ext_filter_mask)) return -EMSGSIZE; } -- cgit 1.4.1 From 41b1704fad64a343e2793309640d09c54c691c48 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 13 Jun 2023 12:19:40 +0300 Subject: wifi: ath11k: Add missing check for ioremap [ Upstream commit 16e0077e14a73866e9b0f4a6bf4ad3d4a5cb0f2a ] Add check for ioremap() and return the error if it fails in order to guarantee the success of ioremap(), same as in ath11k_qmi_load_file_target_mem(). Fixes: 6ac04bdc5edb ("ath11k: Use reserved host DDR addresses from DT for PCI devices") Signed-off-by: Jiasheng Jiang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230608022858.27405-1-jiasheng@iscas.ac.cn Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/qmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 381c6b390dd7..01b02c03fa89 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -2058,6 +2058,9 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) ab->qmi.target_mem[idx].iaddr = ioremap(ab->qmi.target_mem[idx].paddr, ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].iaddr) + return -EIO; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; host_ddr_sz = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; @@ -2083,6 +2086,8 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) ab->qmi.target_mem[idx].iaddr = ioremap(ab->qmi.target_mem[idx].paddr, ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].iaddr) + return -EIO; } else { ab->qmi.target_mem[idx].paddr = ATH11K_QMI_CALDB_ADDRESS; -- cgit 1.4.1 From 657a83f079ba69c49491338782b83509749da483 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 14 Jun 2023 12:41:22 +0300 Subject: wifi: iwlwifi: pull from TXQs with softirqs disabled [ Upstream commit 96fb6f47db24a712d650b0a9b9074873f273fb0e ] In mac80211, it's required that we pull from TXQs by calling ieee80211_tx_dequeue() only with softirqs disabled. However, in iwl_mvm_queue_state_change() we're often called with them enabled, e.g. from flush if anything was flushed, triggering a mac80211 warning. Fix that by disabling the softirqs across the TX call. Fixes: cfbc6c4c5b91 ("iwlwifi: mvm: support mac80211 TXQs model") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230614123446.0feef7fa81db.I4dd62542d955b40dd8f0af34fa4accb9d0d17c7e@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 994f597a7102..864f5fb26040 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1686,8 +1686,11 @@ static void iwl_mvm_queue_state_change(struct iwl_op_mode *op_mode, else set_bit(IWL_MVM_TXQ_STATE_STOP_FULL, &mvmtxq->state); - if (start && mvmsta->sta_state != IEEE80211_STA_NOTEXIST) + if (start && mvmsta->sta_state != IEEE80211_STA_NOTEXIST) { + local_bh_disable(); iwl_mvm_mac_itxq_xmit(mvm->hw, txq); + local_bh_enable(); + } } out: -- cgit 1.4.1 From 2d690495eb2766d58e25c83676f422219c4fcf18 Mon Sep 17 00:00:00 2001 From: Anjaneyulu Date: Wed, 14 Jun 2023 12:41:32 +0300 Subject: wifi: iwlwifi: pcie: fix NULL pointer dereference in iwl_pcie_irq_rx_msix_handler() [ Upstream commit 1902f1953b8ba100ee8705cb8a6f1a9795550eca ] rxq can be NULL only when trans_pcie->rxq is NULL and entry->entry is zero. For the case when entry->entry is not equal to 0, rxq won't be NULL even if trans_pcie->rxq is NULL. Modify checker to check for trans_pcie->rxq. Fixes: abc599efa67b ("iwlwifi: pcie: don't crash when rx queues aren't allocated in interrupt") Signed-off-by: Anjaneyulu Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230614123446.5a5eb3889a4a.I375a1d58f16b48cd2044e7b7caddae512d7c86fd@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 9c9f87fe8377..b455e981faa1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1620,14 +1620,14 @@ irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id) struct msix_entry *entry = dev_id; struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry); struct iwl_trans *trans = trans_pcie->trans; - struct iwl_rxq *rxq = &trans_pcie->rxq[entry->entry]; + struct iwl_rxq *rxq; trace_iwlwifi_dev_irq_msix(trans->dev, entry, false, 0, 0); if (WARN_ON(entry->entry >= trans->num_rx_queues)) return IRQ_NONE; - if (!rxq) { + if (!trans_pcie->rxq) { if (net_ratelimit()) IWL_ERR(trans, "[%d] Got MSI-X interrupt before we have Rx queues\n", @@ -1635,6 +1635,7 @@ irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id) return IRQ_NONE; } + rxq = &trans_pcie->rxq[entry->entry]; lock_map_acquire(&trans->sync_cmd_lockdep_map); IWL_DEBUG_ISR(trans, "[%d] Got interrupt\n", entry->entry); -- cgit 1.4.1 From d875120c3520e7041feb080ceaec7d5035335b7c Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Wed, 14 Jun 2023 15:26:48 +0200 Subject: wifi: mac80211: Remove "Missing iftype sband data/EHT cap" spam [ Upstream commit 6e21e7b8cd897193cee3c2649640efceb3004ba5 ] In mesh mode, ieee80211_chandef_he_6ghz_oper() is called by mesh_matches_local() for every received mesh beacon. On a 6 GHz mesh of a HE-only phy, this spams that the hardware does not have EHT capabilities, even if the received mesh beacon does not have an EHT element. Unlike HE, not supporting EHT in the 6 GHz band is not an error so do not print anything in this case. Fixes: 5dca295dd767 ("mac80211: Add initial support for EHT and 320 MHz channels") Signed-off-by: Nicolas Cavallari Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230614132648.28995-1-nicolas.cavallari@green-communications.fr Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/util.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 784b9ba61581..98806c359b17 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3599,10 +3599,8 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, } eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype); - if (!eht_cap) { - sdata_info(sdata, "Missing iftype sband data/EHT cap"); + if (!eht_cap) eht_oper = NULL; - } he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); -- cgit 1.4.1 From 0862669693cf3b4fd9562cfa4d929074ce373716 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 16 Jun 2023 09:54:03 +0300 Subject: wifi: cfg80211: rewrite merging of inherited elements [ Upstream commit dfd9aa3e7a456d57b18021d66472ab7ff8373ab7 ] The cfg80211_gen_new_ie function merges the IEs using inheritance rules. Rewrite this function to fix issues around inheritance rules. In particular, vendor elements do not require any special handling, as they are either all inherited or overridden by the subprofile. Also, add fragmentation handling as this may be needed in some cases. This also changes the function to not require making a copy. The new version could be optimized a bit by explicitly tracking which IEs have been handled already rather than looking that up again every time. Note that a small behavioural change is the removal of the SSID special handling. This should be fine for the MBSSID element, as the SSID must be included in the subelement. Fixes: 0b8fb8235be8 ("cfg80211: Parsing of Multiple BSSID information in scanning") Signed-off-by: Benjamin Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230616094949.bc6152e146db.I2b5f3bc45085e1901e5b5192a674436adaf94748@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/scan.c | 213 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 124 insertions(+), 89 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 6c2b73c0d36e..9fcb6f06b8cb 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -262,117 +262,152 @@ bool cfg80211_is_element_inherited(const struct element *elem, } EXPORT_SYMBOL(cfg80211_is_element_inherited); -static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, - const u8 *subelement, size_t subie_len, - u8 *new_ie, gfp_t gfp) +static size_t cfg80211_copy_elem_with_frags(const struct element *elem, + const u8 *ie, size_t ie_len, + u8 **pos, u8 *buf, size_t buf_len) { - u8 *pos, *tmp; - const u8 *tmp_old, *tmp_new; - const struct element *non_inherit_elem; - u8 *sub_copy; + if (WARN_ON((u8 *)elem < ie || elem->data > ie + ie_len || + elem->data + elem->datalen > ie + ie_len)) + return 0; - /* copy subelement as we need to change its content to - * mark an ie after it is processed. - */ - sub_copy = kmemdup(subelement, subie_len, gfp); - if (!sub_copy) + if (elem->datalen + 2 > buf + buf_len - *pos) return 0; - pos = &new_ie[0]; + memcpy(*pos, elem, elem->datalen + 2); + *pos += elem->datalen + 2; - /* set new ssid */ - tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len); - if (tmp_new) { - memcpy(pos, tmp_new, tmp_new[1] + 2); - pos += (tmp_new[1] + 2); + /* Finish if it is not fragmented */ + if (elem->datalen != 255) + return *pos - buf; + + ie_len = ie + ie_len - elem->data - elem->datalen; + ie = (const u8 *)elem->data + elem->datalen; + + for_each_element(elem, ie, ie_len) { + if (elem->id != WLAN_EID_FRAGMENT) + break; + + if (elem->datalen + 2 > buf + buf_len - *pos) + return 0; + + memcpy(*pos, elem, elem->datalen + 2); + *pos += elem->datalen + 2; + + if (elem->datalen != 255) + break; } - /* get non inheritance list if exists */ - non_inherit_elem = - cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, - sub_copy, subie_len); + return *pos - buf; +} - /* go through IEs in ie (skip SSID) and subelement, - * merge them into new_ie +static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, + const u8 *subie, size_t subie_len, + u8 *new_ie, size_t new_ie_len) +{ + const struct element *non_inherit_elem, *parent, *sub; + u8 *pos = new_ie; + u8 id, ext_id; + unsigned int match_len; + + non_inherit_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, + subie, subie_len); + + /* We copy the elements one by one from the parent to the generated + * elements. + * If they are not inherited (included in subie or in the non + * inheritance element), then we copy all occurrences the first time + * we see this element type. */ - tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); - tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie; - - while (tmp_old + 2 - ie <= ielen && - tmp_old + tmp_old[1] + 2 - ie <= ielen) { - if (tmp_old[0] == 0) { - tmp_old++; + for_each_element(parent, ie, ielen) { + if (parent->id == WLAN_EID_FRAGMENT) continue; + + if (parent->id == WLAN_EID_EXTENSION) { + if (parent->datalen < 1) + continue; + + id = WLAN_EID_EXTENSION; + ext_id = parent->data[0]; + match_len = 1; + } else { + id = parent->id; + match_len = 0; } - if (tmp_old[0] == WLAN_EID_EXTENSION) - tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy, - subie_len); - else - tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy, - subie_len); + /* Find first occurrence in subie */ + sub = cfg80211_find_elem_match(id, subie, subie_len, + &ext_id, match_len, 0); - if (!tmp) { - const struct element *old_elem = (void *)tmp_old; + /* Copy from parent if not in subie and inherited */ + if (!sub && + cfg80211_is_element_inherited(parent, non_inherit_elem)) { + if (!cfg80211_copy_elem_with_frags(parent, + ie, ielen, + &pos, new_ie, + new_ie_len)) + return 0; - /* ie in old ie but not in subelement */ - if (cfg80211_is_element_inherited(old_elem, - non_inherit_elem)) { - memcpy(pos, tmp_old, tmp_old[1] + 2); - pos += tmp_old[1] + 2; - } - } else { - /* ie in transmitting ie also in subelement, - * copy from subelement and flag the ie in subelement - * as copied (by setting eid field to WLAN_EID_SSID, - * which is skipped anyway). - * For vendor ie, compare OUI + type + subType to - * determine if they are the same ie. - */ - if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) { - if (tmp_old[1] >= 5 && tmp[1] >= 5 && - !memcmp(tmp_old + 2, tmp + 2, 5)) { - /* same vendor ie, copy from - * subelement - */ - memcpy(pos, tmp, tmp[1] + 2); - pos += tmp[1] + 2; - tmp[0] = WLAN_EID_SSID; - } else { - memcpy(pos, tmp_old, tmp_old[1] + 2); - pos += tmp_old[1] + 2; - } - } else { - /* copy ie from subelement into new ie */ - memcpy(pos, tmp, tmp[1] + 2); - pos += tmp[1] + 2; - tmp[0] = WLAN_EID_SSID; - } + continue; } - if (tmp_old + tmp_old[1] + 2 - ie == ielen) - break; + /* Already copied if an earlier element had the same type */ + if (cfg80211_find_elem_match(id, ie, (u8 *)parent - ie, + &ext_id, match_len, 0)) + continue; - tmp_old += tmp_old[1] + 2; + /* Not inheriting, copy all similar elements from subie */ + while (sub) { + if (!cfg80211_copy_elem_with_frags(sub, + subie, subie_len, + &pos, new_ie, + new_ie_len)) + return 0; + + sub = cfg80211_find_elem_match(id, + sub->data + sub->datalen, + subie_len + subie - + (sub->data + + sub->datalen), + &ext_id, match_len, 0); + } } - /* go through subelement again to check if there is any ie not - * copied to new ie, skip ssid, capability, bssid-index ie + /* The above misses elements that are included in subie but not in the + * parent, so do a pass over subie and append those. + * Skip the non-tx BSSID caps and non-inheritance element. */ - tmp_new = sub_copy; - while (tmp_new + 2 - sub_copy <= subie_len && - tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) { - if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP || - tmp_new[0] == WLAN_EID_SSID)) { - memcpy(pos, tmp_new, tmp_new[1] + 2); - pos += tmp_new[1] + 2; + for_each_element(sub, subie, subie_len) { + if (sub->id == WLAN_EID_NON_TX_BSSID_CAP) + continue; + + if (sub->id == WLAN_EID_FRAGMENT) + continue; + + if (sub->id == WLAN_EID_EXTENSION) { + if (sub->datalen < 1) + continue; + + id = WLAN_EID_EXTENSION; + ext_id = sub->data[0]; + match_len = 1; + + if (ext_id == WLAN_EID_EXT_NON_INHERITANCE) + continue; + } else { + id = sub->id; + match_len = 0; } - if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len) - break; - tmp_new += tmp_new[1] + 2; + + /* Processed if one was included in the parent */ + if (cfg80211_find_elem_match(id, ie, ielen, + &ext_id, match_len, 0)) + continue; + + if (!cfg80211_copy_elem_with_frags(sub, subie, subie_len, + &pos, new_ie, new_ie_len)) + return 0; } - kfree(sub_copy); return pos - new_ie; } @@ -2224,7 +2259,7 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy, new_ie_len = cfg80211_gen_new_ie(ie, ielen, profile, profile_len, new_ie, - gfp); + IEEE80211_MAX_DATA_LEN); if (!new_ie_len) continue; -- cgit 1.4.1 From 27268ba34774c9c7b81e17491af29d7890de0dcc Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 16 Jun 2023 09:54:04 +0300 Subject: wifi: cfg80211: drop incorrect nontransmitted BSS update code [ Upstream commit 39432f8a3752a87a53fd8d5e51824a43aaae5cab ] The removed code ran for any BSS that was not included in the MBSSID element in order to update it. However, instead of using the correct inheritance rules, it would simply copy the elements from the transmitting AP. The result is that we would report incorrect elements in this case. After some discussions, it seems that there are likely not even APs actually using this feature. Either way, removing the code decreases complexity and makes the cfg80211 behaviour more correct. Fixes: 0b8fb8235be8 ("cfg80211: Parsing of Multiple BSSID information in scanning") Signed-off-by: Benjamin Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230616094949.cfd6d8db1f26.Ia1044902b86cd7d366400a4bfb93691b8f05d68c@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/scan.c | 154 ++++------------------------------------------------ 1 file changed, 11 insertions(+), 143 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9fcb6f06b8cb..efe9283e9893 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -2308,118 +2308,6 @@ cfg80211_inform_bss_data(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_inform_bss_data); -static void -cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy, - struct cfg80211_inform_bss *data, - struct ieee80211_mgmt *mgmt, size_t len, - struct cfg80211_non_tx_bss *non_tx_data, - gfp_t gfp) -{ - enum cfg80211_bss_frame_type ftype; - const u8 *ie = mgmt->u.probe_resp.variable; - size_t ielen = len - offsetof(struct ieee80211_mgmt, - u.probe_resp.variable); - - ftype = ieee80211_is_beacon(mgmt->frame_control) ? - CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP; - - cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid, - le64_to_cpu(mgmt->u.probe_resp.timestamp), - le16_to_cpu(mgmt->u.probe_resp.beacon_int), - ie, ielen, non_tx_data, gfp); -} - -static void -cfg80211_update_notlisted_nontrans(struct wiphy *wiphy, - struct cfg80211_bss *nontrans_bss, - struct ieee80211_mgmt *mgmt, size_t len) -{ - u8 *ie, *new_ie, *pos; - const struct element *nontrans_ssid; - const u8 *trans_ssid, *mbssid; - size_t ielen = len - offsetof(struct ieee80211_mgmt, - u.probe_resp.variable); - size_t new_ie_len; - struct cfg80211_bss_ies *new_ies; - const struct cfg80211_bss_ies *old; - size_t cpy_len; - - lockdep_assert_held(&wiphy_to_rdev(wiphy)->bss_lock); - - ie = mgmt->u.probe_resp.variable; - - new_ie_len = ielen; - trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); - if (!trans_ssid) - return; - new_ie_len -= trans_ssid[1]; - mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen); - /* - * It's not valid to have the MBSSID element before SSID - * ignore if that happens - the code below assumes it is - * after (while copying things inbetween). - */ - if (!mbssid || mbssid < trans_ssid) - return; - new_ie_len -= mbssid[1]; - - nontrans_ssid = ieee80211_bss_get_elem(nontrans_bss, WLAN_EID_SSID); - if (!nontrans_ssid) - return; - - new_ie_len += nontrans_ssid->datalen; - - /* generate new ie for nontrans BSS - * 1. replace SSID with nontrans BSS' SSID - * 2. skip MBSSID IE - */ - new_ie = kzalloc(new_ie_len, GFP_ATOMIC); - if (!new_ie) - return; - - new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, GFP_ATOMIC); - if (!new_ies) - goto out_free; - - pos = new_ie; - - /* copy the nontransmitted SSID */ - cpy_len = nontrans_ssid->datalen + 2; - memcpy(pos, nontrans_ssid, cpy_len); - pos += cpy_len; - /* copy the IEs between SSID and MBSSID */ - cpy_len = trans_ssid[1] + 2; - memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len))); - pos += (mbssid - (trans_ssid + cpy_len)); - /* copy the IEs after MBSSID */ - cpy_len = mbssid[1] + 2; - memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len))); - - /* update ie */ - new_ies->len = new_ie_len; - new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); - new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control); - memcpy(new_ies->data, new_ie, new_ie_len); - if (ieee80211_is_probe_resp(mgmt->frame_control)) { - old = rcu_access_pointer(nontrans_bss->proberesp_ies); - rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies); - rcu_assign_pointer(nontrans_bss->ies, new_ies); - if (old) - kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); - } else { - old = rcu_access_pointer(nontrans_bss->beacon_ies); - rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies); - cfg80211_update_hidden_bsses(bss_from_pub(nontrans_bss), - new_ies, old); - rcu_assign_pointer(nontrans_bss->ies, new_ies); - if (old) - kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); - } - -out_free: - kfree(new_ie); -} - /* cfg80211_inform_bss_width_frame helper */ static struct cfg80211_bss * cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, @@ -2561,51 +2449,31 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, struct ieee80211_mgmt *mgmt, size_t len, gfp_t gfp) { - struct cfg80211_bss *res, *tmp_bss; + struct cfg80211_bss *res; const u8 *ie = mgmt->u.probe_resp.variable; - const struct cfg80211_bss_ies *ies1, *ies2; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + enum cfg80211_bss_frame_type ftype; struct cfg80211_non_tx_bss non_tx_data = {}; res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt, len, gfp); + if (!res) + return NULL; /* don't do any further MBSSID handling for S1G */ if (ieee80211_is_s1g_beacon(mgmt->frame_control)) return res; - if (!res || !wiphy->support_mbssid || - !cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID, ie, ielen)) - return res; - if (wiphy->support_only_he_mbssid && - !cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen)) - return res; - + ftype = ieee80211_is_beacon(mgmt->frame_control) ? + CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP; non_tx_data.tx_bss = res; - /* process each non-transmitting bss */ - cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len, - &non_tx_data, gfp); - - spin_lock_bh(&wiphy_to_rdev(wiphy)->bss_lock); - /* check if the res has other nontransmitting bss which is not - * in MBSSID IE - */ - ies1 = rcu_access_pointer(res->ies); - - /* go through nontrans_list, if the timestamp of the BSS is - * earlier than the timestamp of the transmitting BSS then - * update it - */ - list_for_each_entry(tmp_bss, &res->nontrans_list, - nontrans_list) { - ies2 = rcu_access_pointer(tmp_bss->ies); - if (ies2->tsf < ies1->tsf) - cfg80211_update_notlisted_nontrans(wiphy, tmp_bss, - mgmt, len); - } - spin_unlock_bh(&wiphy_to_rdev(wiphy)->bss_lock); + /* process each non-transmitting bss */ + cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid, + le64_to_cpu(mgmt->u.probe_resp.timestamp), + le16_to_cpu(mgmt->u.probe_resp.beacon_int), + ie, ielen, &non_tx_data, gfp); return res; } -- cgit 1.4.1 From 132b7129c5fe90197d2581dd5e786542f9a09d86 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 16 Jun 2023 22:28:45 +0200 Subject: wifi: cfg80211: fix regulatory disconnect with OCB/NAN [ Upstream commit e8c2af660ba0790afd14d5cbc2fd05c6dc85e207 ] Since regulatory disconnect was added, OCB and NAN interface types were added, which made it completely unusable for any driver that allowed OCB/NAN. Add OCB/NAN (though NAN doesn't do anything, we don't have any info) and also remove all the logic that opts out, so it won't be broken again if/when new interface types are added. Fixes: 6e0bd6c35b02 ("cfg80211: 802.11p OCB mode handling") Fixes: cb3b7d87652a ("cfg80211: add start / stop NAN commands") Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20230616222844.2794d1625a26.I8e78a3789a29e6149447b3139df724a6f1b46fc3@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- include/net/regulatory.h | 13 +------------ net/wireless/core.c | 16 ---------------- net/wireless/reg.c | 14 ++++++++++---- 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 896191f420d5..b2cb4a9eb04d 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -140,17 +140,6 @@ struct regulatory_request { * otherwise initiating radiation is not allowed. This will enable the * relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration * option - * @REGULATORY_IGNORE_STALE_KICKOFF: the regulatory core will _not_ make sure - * all interfaces on this wiphy reside on allowed channels. If this flag - * is not set, upon a regdomain change, the interfaces are given a grace - * period (currently 60 seconds) to disconnect or move to an allowed - * channel. Interfaces on forbidden channels are forcibly disconnected. - * Currently these types of interfaces are supported for enforcement: - * NL80211_IFTYPE_ADHOC, NL80211_IFTYPE_STATION, NL80211_IFTYPE_AP, - * NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_MONITOR, - * NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, - * NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device - * includes any modes unsupported for enforcement checking. * @REGULATORY_WIPHY_SELF_MANAGED: for devices that employ wiphy-specific * regdom management. These devices will ignore all regdom changes not * originating from their own wiphy. @@ -177,7 +166,7 @@ enum ieee80211_regulatory_flags { REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3), REGULATORY_COUNTRY_IE_IGNORE = BIT(4), REGULATORY_ENABLE_RELAX_NO_IR = BIT(5), - REGULATORY_IGNORE_STALE_KICKOFF = BIT(6), + /* reuse bit 6 next time */ REGULATORY_WIPHY_SELF_MANAGED = BIT(7), }; diff --git a/net/wireless/core.c b/net/wireless/core.c index b3ec9eaec36b..609b79fe4a74 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -721,22 +721,6 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } - /* - * if a wiphy has unsupported modes for regulatory channel enforcement, - * opt-out of enforcement checking - */ - if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_P2P_DEVICE) | - BIT(NL80211_IFTYPE_NAN) | - BIT(NL80211_IFTYPE_AP_VLAN) | - BIT(NL80211_IFTYPE_MONITOR))) - wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; - if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) && (wiphy->regulatory_flags & (REGULATORY_CUSTOM_REG | diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 522180919a1a..3b44fa59dbba 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2429,9 +2429,17 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) case NL80211_IFTYPE_P2P_DEVICE: /* no enforcement required */ break; + case NL80211_IFTYPE_OCB: + if (!wdev->u.ocb.chandef.chan) + continue; + chandef = wdev->u.ocb.chandef; + break; + case NL80211_IFTYPE_NAN: + /* we have no info, but NAN is also pretty universal */ + continue; default: /* others not implemented for now */ - WARN_ON(1); + WARN_ON_ONCE(1); break; } @@ -2490,9 +2498,7 @@ static void reg_check_chans_work(struct work_struct *work) rtnl_lock(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) - if (!(rdev->wiphy.regulatory_flags & - REGULATORY_IGNORE_STALE_KICKOFF)) - reg_leave_invalid_chans(&rdev->wiphy); + reg_leave_invalid_chans(&rdev->wiphy); rtnl_unlock(); } -- cgit 1.4.1 From ffb0733664cc398b36831730e64503951fa82597 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 7 Sep 2022 17:23:09 +0300 Subject: wifi: cfg80211/mac80211: Fix ML element common size calculation [ Upstream commit 1403b109c9a5244dc6ab79154f04eecc209ef3d2 ] The common size is part of the length in the data so don't add it again. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg Stable-dep-of: ce6e1f600b0c ("wifi: ieee80211: Fix the common size calculation for reconfiguration ML") Signed-off-by: Sasha Levin --- include/linux/ieee80211.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d3088666f3f4..470fafce2e70 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4573,18 +4573,17 @@ static inline u8 ieee80211_mle_common_size(const u8 *data) switch (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: - common += sizeof(struct ieee80211_mle_basic_common_info); - break; case IEEE80211_ML_CONTROL_TYPE_PREQ: - common += sizeof(struct ieee80211_mle_preq_common_info); + case IEEE80211_ML_CONTROL_TYPE_TDLS: + /* + * The length is the first octet pointed by mle->variable so no + * need to add anything + */ break; case IEEE80211_ML_CONTROL_TYPE_RECONF: if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR) common += ETH_ALEN; return common; - case IEEE80211_ML_CONTROL_TYPE_TDLS: - common += sizeof(struct ieee80211_mle_tdls_common_info); - break; case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: if (control & IEEE80211_MLC_PRIO_ACCESS_PRES_AP_MLD_MAC_ADDR) common += ETH_ALEN; -- cgit 1.4.1 From f114b159b2c19c68d55e43e9abbcdc7613c98237 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 18 Jun 2023 21:49:45 +0300 Subject: wifi: ieee80211: Fix the common size calculation for reconfiguration ML [ Upstream commit ce6e1f600b0cfc563a7d607de702262a58cd835d ] The common information length is found in the first octet of the common information. Fixes: 0f48b8b88aa9 ("wifi: ieee80211: add definitions for multi-link element") Signed-off-by: Ilan Peer Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230618214435.3c7ed4817338.I42ef706cb827b4dade6e4ffbb6e7f341eaccd398@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- include/linux/ieee80211.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 470fafce2e70..870ae4cd8202 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -4575,15 +4575,12 @@ static inline u8 ieee80211_mle_common_size(const u8 *data) case IEEE80211_ML_CONTROL_TYPE_BASIC: case IEEE80211_ML_CONTROL_TYPE_PREQ: case IEEE80211_ML_CONTROL_TYPE_TDLS: + case IEEE80211_ML_CONTROL_TYPE_RECONF: /* * The length is the first octet pointed by mle->variable so no * need to add anything */ break; - case IEEE80211_ML_CONTROL_TYPE_RECONF: - if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR) - common += ETH_ALEN; - return common; case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: if (control & IEEE80211_MLC_PRIO_ACCESS_PRES_AP_MLD_MAC_ADDR) common += ETH_ALEN; -- cgit 1.4.1 From ed98f5c074917e0a75fc91d01eeec632fa6d66ca Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 20 Jun 2023 12:27:13 +0200 Subject: mmc: Add MMC_QUIRK_BROKEN_SD_CACHE for Kingston Canvas Go Plus from 11/2019 [ Upstream commit c467c8f081859d4f4ca4eee4fba54bb5d85d6c97 ] This microSD card never clears Flush Cache bit after cache flush has been started in sd_flush_cache(). This leads e.g. to failure to mount file system. Add a quirk which disables the SD cache for this specific card from specific manufacturing date of 11/2019, since on newer dated cards from 05/2023 the cache flush works correctly. Fixes: 08ebf903af57 ("mmc: core: Fixup support for writeback-cache for eMMC and SD") Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20230620102713.7701-1-marex@denx.de Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin --- drivers/mmc/core/card.h | 30 +++++++++++++++++++++++------- drivers/mmc/core/quirks.h | 13 +++++++++++++ drivers/mmc/core/sd.c | 2 +- include/linux/mmc/card.h | 1 + 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index cfdd1ff40b86..4edf9057fa79 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -53,6 +53,10 @@ struct mmc_fixup { unsigned int manfid; unsigned short oemid; + /* Manufacturing date */ + unsigned short year; + unsigned char month; + /* SDIO-specific fields. You can use SDIO_ANY_ID here of course */ u16 cis_vendor, cis_device; @@ -68,6 +72,8 @@ struct mmc_fixup { #define CID_MANFID_ANY (-1u) #define CID_OEMID_ANY ((unsigned short) -1) +#define CID_YEAR_ANY ((unsigned short) -1) +#define CID_MONTH_ANY ((unsigned char) -1) #define CID_NAME_ANY (NULL) #define EXT_CSD_REV_ANY (-1u) @@ -81,17 +87,21 @@ struct mmc_fixup { #define CID_MANFID_APACER 0x27 #define CID_MANFID_KINGSTON 0x70 #define CID_MANFID_HYNIX 0x90 +#define CID_MANFID_KINGSTON_SD 0x9F #define CID_MANFID_NUMONYX 0xFE #define END_FIXUP { NULL } -#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \ - _cis_vendor, _cis_device, \ - _fixup, _data, _ext_csd_rev) \ +#define _FIXUP_EXT(_name, _manfid, _oemid, _year, _month, \ + _rev_start, _rev_end, \ + _cis_vendor, _cis_device, \ + _fixup, _data, _ext_csd_rev) \ { \ .name = (_name), \ .manfid = (_manfid), \ .oemid = (_oemid), \ + .year = (_year), \ + .month = (_month), \ .rev_start = (_rev_start), \ .rev_end = (_rev_end), \ .cis_vendor = (_cis_vendor), \ @@ -103,8 +113,8 @@ struct mmc_fixup { #define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \ _fixup, _data, _ext_csd_rev) \ - _FIXUP_EXT(_name, _manfid, \ - _oemid, _rev_start, _rev_end, \ + _FIXUP_EXT(_name, _manfid, _oemid, CID_YEAR_ANY, CID_MONTH_ANY, \ + _rev_start, _rev_end, \ SDIO_ANY_ID, SDIO_ANY_ID, \ _fixup, _data, _ext_csd_rev) \ @@ -118,8 +128,9 @@ struct mmc_fixup { _ext_csd_rev) #define SDIO_FIXUP(_vendor, _device, _fixup, _data) \ - _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \ - CID_OEMID_ANY, 0, -1ull, \ + _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, CID_OEMID_ANY, \ + CID_YEAR_ANY, CID_MONTH_ANY, \ + 0, -1ull, \ _vendor, _device, \ _fixup, _data, EXT_CSD_REV_ANY) \ @@ -264,4 +275,9 @@ static inline int mmc_card_broken_sd_discard(const struct mmc_card *c) return c->quirks & MMC_QUIRK_BROKEN_SD_DISCARD; } +static inline int mmc_card_broken_sd_cache(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_BROKEN_SD_CACHE; +} + #endif diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 29b9497936df..a7ffbc930ea9 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -53,6 +53,15 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_BLK_NO_CMD23), + /* + * Kingston Canvas Go! Plus microSD cards never finish SD cache flush. + * This has so far only been observed on cards from 11/2019, while new + * cards from 2023/05 do not exhibit this behavior. + */ + _FIXUP_EXT("SD64G", CID_MANFID_KINGSTON_SD, 0x5449, 2019, 11, + 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, + MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), + /* * Some SD cards lockup while using CMD23 multiblock transfers. */ @@ -209,6 +218,10 @@ static inline void mmc_fixup_device(struct mmc_card *card, if (f->of_compatible && !mmc_fixup_of_compatible_match(card, f->of_compatible)) continue; + if (f->year != CID_YEAR_ANY && f->year != card->cid.year) + continue; + if (f->month != CID_MONTH_ANY && f->month != card->cid.month) + continue; dev_dbg(&card->dev, "calling %ps\n", f->vendor_fixup); f->vendor_fixup(card, f->data); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 72b664ed90cf..246ce027ae0a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1170,7 +1170,7 @@ static int sd_parse_ext_reg_perf(struct mmc_card *card, u8 fno, u8 page, card->ext_perf.feature_support |= SD_EXT_PERF_HOST_MAINT; /* Cache support at bit 0. */ - if (reg_buf[4] & BIT(0)) + if ((reg_buf[4] & BIT(0)) && !mmc_card_broken_sd_cache(card)) card->ext_perf.feature_support |= SD_EXT_PERF_CACHE; /* Command queue support indicated via queue depth bits (0 to 4). */ diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index c726ea781255..daa2f40d9ce6 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -294,6 +294,7 @@ struct mmc_card { #define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */ #define MMC_QUIRK_BROKEN_HPI (1<<13) /* Disable broken HPI support */ #define MMC_QUIRK_BROKEN_SD_DISCARD (1<<14) /* Disable broken SD discard support */ +#define MMC_QUIRK_BROKEN_SD_CACHE (1<<15) /* Disable broken SD cache support */ bool reenable_cmdq; /* Re-enable Command Queue */ -- cgit 1.4.1 From 52bc4b89cdee2476fe395fcadf8bcdb2768ae80a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 20 Jun 2023 13:04:01 +0300 Subject: wifi: iwlwifi: mvm: indicate HW decrypt for beacon protection [ Upstream commit 2db72b8a700943aa54dce0aabe6ff1b72b615162 ] We've already done the 'decryption' here, so tell mac80211 it need not do it again. Fixes: b1fdc2505abc ("iwlwifi: mvm: advertise BIGTK client support if available") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230620125813.a50cf68fbf2e.Ieceacbe3789d81ea02ae085ad8d1f8813a33c31b@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 02c2a0630107..f268a31ce26d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -296,7 +296,8 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, struct ieee80211_hdr *hdr, struct iwl_rx_mpdu_desc *desc, - u32 status) + u32 status, + struct ieee80211_rx_status *stats) { struct iwl_mvm_sta *mvmsta; struct iwl_mvm_vif *mvmvif; @@ -325,8 +326,10 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta, /* good cases */ if (likely(status & IWL_RX_MPDU_STATUS_MIC_OK && - !(status & IWL_RX_MPDU_STATUS_REPLAY_ERROR))) + !(status & IWL_RX_MPDU_STATUS_REPLAY_ERROR))) { + stats->flag |= RX_FLAG_DECRYPTED; return 0; + } if (!sta) return -1; @@ -395,7 +398,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (unlikely(ieee80211_is_mgmt(hdr->frame_control) && !ieee80211_has_protected(hdr->frame_control))) - return iwl_mvm_rx_mgmt_prot(sta, hdr, desc, status); + return iwl_mvm_rx_mgmt_prot(sta, hdr, desc, status, stats); if (!ieee80211_has_protected(hdr->frame_control) || (status & IWL_RX_MPDU_STATUS_SEC_MASK) == -- cgit 1.4.1 From e3754e97414ef65da0a790817e09ffe3adcc7745 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 13 Jun 2023 16:46:55 +0300 Subject: wifi: ath9k: convert msecs to jiffies where needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2aa083acea9f61be3280184384551178f510ff51 ] Since 'ieee80211_queue_delayed_work()' expects timeout in jiffies and not milliseconds, 'msecs_to_jiffies()' should be used in 'ath_restart_work()' and '__ath9k_flush()'. Fixes: d63ffc45c5d3 ("ath9k: rename tx_complete_work to hw_check_work") Signed-off-by: Dmitry Antipov Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230613134655.248728-1-dmantipov@yandex.ru Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 7f9f06ea8a05..6360d3356e25 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -203,7 +203,7 @@ void ath_cancel_work(struct ath_softc *sc) void ath_restart_work(struct ath_softc *sc) { ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work, - ATH_HW_CHECK_POLL_INT); + msecs_to_jiffies(ATH_HW_CHECK_POLL_INT)); if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9330(sc->sc_ah)) ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, @@ -2240,7 +2240,7 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop, } ieee80211_queue_delayed_work(hw, &sc->hw_check_work, - ATH_HW_CHECK_POLL_INT); + msecs_to_jiffies(ATH_HW_CHECK_POLL_INT)); } static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) -- cgit 1.4.1 From 5e9b38de6633f9fa1893490454acda9f21896a0d Mon Sep 17 00:00:00 2001 From: Gilad Sever Date: Wed, 21 Jun 2023 13:42:08 +0300 Subject: bpf: Factor out socket lookup functions for the TC hookpoint. [ Upstream commit 6e98730bc0b44acaf86eccc75f823128aa9c9e79 ] Change BPF helper socket lookup functions to use TC specific variants: bpf_tc_sk_lookup_tcp() / bpf_tc_sk_lookup_udp() / bpf_tc_skc_lookup_tcp() instead of sharing implementation with the cg / sk_skb hooking points. This allows introducing a separate logic for the TC flow. The tc functions are identical to the original code. Signed-off-by: Gilad Sever Signed-off-by: Daniel Borkmann Reviewed-by: Shmulik Ladkani Reviewed-by: Eyal Birger Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20230621104211.301902-2-gilad9366@gmail.com Stable-dep-of: 9a5cb79762e0 ("bpf: Fix bpf socket lookup from tc/xdp to respect socket VRF bindings") Signed-off-by: Sasha Levin --- net/core/filter.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index b79a070fa824..e0f73ed8821e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6646,6 +6646,63 @@ static const struct bpf_func_proto bpf_sk_lookup_udp_proto = { .arg5_type = ARG_ANYTHING, }; +BPF_CALL_5(bpf_tc_skc_lookup_tcp, struct sk_buff *, skb, + struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) +{ + return (unsigned long)bpf_skc_lookup(skb, tuple, len, IPPROTO_TCP, + netns_id, flags); +} + +static const struct bpf_func_proto bpf_tc_skc_lookup_tcp_proto = { + .func = bpf_tc_skc_lookup_tcp, + .gpl_only = false, + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCK_COMMON_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +}; + +BPF_CALL_5(bpf_tc_sk_lookup_tcp, struct sk_buff *, skb, + struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) +{ + return (unsigned long)bpf_sk_lookup(skb, tuple, len, IPPROTO_TCP, + netns_id, flags); +} + +static const struct bpf_func_proto bpf_tc_sk_lookup_tcp_proto = { + .func = bpf_tc_sk_lookup_tcp, + .gpl_only = false, + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +}; + +BPF_CALL_5(bpf_tc_sk_lookup_udp, struct sk_buff *, skb, + struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) +{ + return (unsigned long)bpf_sk_lookup(skb, tuple, len, IPPROTO_UDP, + netns_id, flags); +} + +static const struct bpf_func_proto bpf_tc_sk_lookup_udp_proto = { + .func = bpf_tc_sk_lookup_udp, + .gpl_only = false, + .pkt_access = true, + .ret_type = RET_PTR_TO_SOCKET_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +}; + BPF_CALL_1(bpf_sk_release, struct sock *, sk) { if (sk && sk_is_refcounted(sk)) @@ -7902,9 +7959,9 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) #endif #ifdef CONFIG_INET case BPF_FUNC_sk_lookup_tcp: - return &bpf_sk_lookup_tcp_proto; + return &bpf_tc_sk_lookup_tcp_proto; case BPF_FUNC_sk_lookup_udp: - return &bpf_sk_lookup_udp_proto; + return &bpf_tc_sk_lookup_udp_proto; case BPF_FUNC_sk_release: return &bpf_sk_release_proto; case BPF_FUNC_tcp_sock: @@ -7912,7 +7969,7 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_get_listener_sock: return &bpf_get_listener_sock_proto; case BPF_FUNC_skc_lookup_tcp: - return &bpf_skc_lookup_tcp_proto; + return &bpf_tc_skc_lookup_tcp_proto; case BPF_FUNC_tcp_check_syncookie: return &bpf_tcp_check_syncookie_proto; case BPF_FUNC_skb_ecn_set_ce: -- cgit 1.4.1 From c7415c521abdc1d174d8b03dca62ae847ba1c49c Mon Sep 17 00:00:00 2001 From: Gilad Sever Date: Wed, 21 Jun 2023 13:42:09 +0300 Subject: bpf: Call __bpf_sk_lookup()/__bpf_skc_lookup() directly via TC hookpoint [ Upstream commit 97fbfeb86917bdbe9c41d5143e335a929147f405 ] skb->dev always exists in the tc flow. There is no need to use bpf_skc_lookup(), bpf_sk_lookup() from this code path. This change facilitates fixing the tc flow to be VRF aware. Signed-off-by: Gilad Sever Signed-off-by: Daniel Borkmann Reviewed-by: Shmulik Ladkani Reviewed-by: Eyal Birger Acked-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20230621104211.301902-3-gilad9366@gmail.com Stable-dep-of: 9a5cb79762e0 ("bpf: Fix bpf socket lookup from tc/xdp to respect socket VRF bindings") Signed-off-by: Sasha Levin --- net/core/filter.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index e0f73ed8821e..1b60a6cc975a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6649,8 +6649,12 @@ static const struct bpf_func_proto bpf_sk_lookup_udp_proto = { BPF_CALL_5(bpf_tc_skc_lookup_tcp, struct sk_buff *, skb, struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) { - return (unsigned long)bpf_skc_lookup(skb, tuple, len, IPPROTO_TCP, - netns_id, flags); + struct net *caller_net = dev_net(skb->dev); + int ifindex = skb->dev->ifindex; + + return (unsigned long)__bpf_skc_lookup(skb, tuple, len, caller_net, + ifindex, IPPROTO_TCP, netns_id, + flags); } static const struct bpf_func_proto bpf_tc_skc_lookup_tcp_proto = { @@ -6668,8 +6672,12 @@ static const struct bpf_func_proto bpf_tc_skc_lookup_tcp_proto = { BPF_CALL_5(bpf_tc_sk_lookup_tcp, struct sk_buff *, skb, struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) { - return (unsigned long)bpf_sk_lookup(skb, tuple, len, IPPROTO_TCP, - netns_id, flags); + struct net *caller_net = dev_net(skb->dev); + int ifindex = skb->dev->ifindex; + + return (unsigned long)__bpf_sk_lookup(skb, tuple, len, caller_net, + ifindex, IPPROTO_TCP, netns_id, + flags); } static const struct bpf_func_proto bpf_tc_sk_lookup_tcp_proto = { @@ -6687,8 +6695,12 @@ static const struct bpf_func_proto bpf_tc_sk_lookup_tcp_proto = { BPF_CALL_5(bpf_tc_sk_lookup_udp, struct sk_buff *, skb, struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) { - return (unsigned long)bpf_sk_lookup(skb, tuple, len, IPPROTO_UDP, - netns_id, flags); + struct net *caller_net = dev_net(skb->dev); + int ifindex = skb->dev->ifindex; + + return (unsigned long)__bpf_sk_lookup(skb, tuple, len, caller_net, + ifindex, IPPROTO_UDP, netns_id, + flags); } static const struct bpf_func_proto bpf_tc_sk_lookup_udp_proto = { -- cgit 1.4.1 From cfb310623413d5b13cfb723ed7e22b22fd5bf6a7 Mon Sep 17 00:00:00 2001 From: Gilad Sever Date: Wed, 21 Jun 2023 13:42:10 +0300 Subject: bpf: Fix bpf socket lookup from tc/xdp to respect socket VRF bindings [ Upstream commit 9a5cb79762e0eda17ca15c2a6eaca4622383c21c ] When calling bpf_sk_lookup_tcp(), bpf_sk_lookup_udp() or bpf_skc_lookup_tcp() from tc/xdp ingress, VRF socket bindings aren't respoected, i.e. unbound sockets are returned, and bound sockets aren't found. VRF binding is determined by the sdif argument to sk_lookup(), however when called from tc the IP SKB control block isn't initialized and thus inet{,6}_sdif() always returns 0. Fix by calculating sdif for the tc/xdp flows by observing the device's l3 enslaved state. The cg/sk_skb hooking points which are expected to support inet{,6}_sdif() pass sdif=-1 which makes __bpf_skc_lookup() use the existing logic. Fixes: 6acc9b432e67 ("bpf: Add helper to retrieve socket in BPF") Signed-off-by: Gilad Sever Signed-off-by: Daniel Borkmann Reviewed-by: Shmulik Ladkani Reviewed-by: Eyal Birger Acked-by: Stanislav Fomichev Cc: David Ahern Link: https://lore.kernel.org/bpf/20230621104211.301902-4-gilad9366@gmail.com Signed-off-by: Sasha Levin --- include/linux/netdevice.h | 9 +++++++ net/core/filter.c | 69 ++++++++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d5eb3ab8e38f..5a04fbf72476 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -5028,6 +5028,15 @@ static inline bool netif_is_l3_slave(const struct net_device *dev) return dev->priv_flags & IFF_L3MDEV_SLAVE; } +static inline int dev_sdif(const struct net_device *dev) +{ +#ifdef CONFIG_NET_L3_MASTER_DEV + if (netif_is_l3_slave(dev)) + return dev->ifindex; +#endif + return 0; +} + static inline bool netif_is_bridge_master(const struct net_device *dev) { return dev->priv_flags & IFF_EBRIDGE; diff --git a/net/core/filter.c b/net/core/filter.c index 1b60a6cc975a..da71e6812ab5 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6474,12 +6474,11 @@ static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple, static struct sock * __bpf_skc_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len, struct net *caller_net, u32 ifindex, u8 proto, u64 netns_id, - u64 flags) + u64 flags, int sdif) { struct sock *sk = NULL; struct net *net; u8 family; - int sdif; if (len == sizeof(tuple->ipv4)) family = AF_INET; @@ -6491,10 +6490,12 @@ __bpf_skc_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len, if (unlikely(flags || !((s32)netns_id < 0 || netns_id <= S32_MAX))) goto out; - if (family == AF_INET) - sdif = inet_sdif(skb); - else - sdif = inet6_sdif(skb); + if (sdif < 0) { + if (family == AF_INET) + sdif = inet_sdif(skb); + else + sdif = inet6_sdif(skb); + } if ((s32)netns_id < 0) { net = caller_net; @@ -6514,10 +6515,11 @@ out: static struct sock * __bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len, struct net *caller_net, u32 ifindex, u8 proto, u64 netns_id, - u64 flags) + u64 flags, int sdif) { struct sock *sk = __bpf_skc_lookup(skb, tuple, len, caller_net, - ifindex, proto, netns_id, flags); + ifindex, proto, netns_id, flags, + sdif); if (sk) { struct sock *sk2 = sk_to_full_sk(sk); @@ -6557,7 +6559,7 @@ bpf_skc_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len, } return __bpf_skc_lookup(skb, tuple, len, caller_net, ifindex, proto, - netns_id, flags); + netns_id, flags, -1); } static struct sock * @@ -6649,12 +6651,13 @@ static const struct bpf_func_proto bpf_sk_lookup_udp_proto = { BPF_CALL_5(bpf_tc_skc_lookup_tcp, struct sk_buff *, skb, struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) { - struct net *caller_net = dev_net(skb->dev); - int ifindex = skb->dev->ifindex; + struct net_device *dev = skb->dev; + int ifindex = dev->ifindex, sdif = dev_sdif(dev); + struct net *caller_net = dev_net(dev); return (unsigned long)__bpf_skc_lookup(skb, tuple, len, caller_net, ifindex, IPPROTO_TCP, netns_id, - flags); + flags, sdif); } static const struct bpf_func_proto bpf_tc_skc_lookup_tcp_proto = { @@ -6672,12 +6675,13 @@ static const struct bpf_func_proto bpf_tc_skc_lookup_tcp_proto = { BPF_CALL_5(bpf_tc_sk_lookup_tcp, struct sk_buff *, skb, struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) { - struct net *caller_net = dev_net(skb->dev); - int ifindex = skb->dev->ifindex; + struct net_device *dev = skb->dev; + int ifindex = dev->ifindex, sdif = dev_sdif(dev); + struct net *caller_net = dev_net(dev); return (unsigned long)__bpf_sk_lookup(skb, tuple, len, caller_net, ifindex, IPPROTO_TCP, netns_id, - flags); + flags, sdif); } static const struct bpf_func_proto bpf_tc_sk_lookup_tcp_proto = { @@ -6695,12 +6699,13 @@ static const struct bpf_func_proto bpf_tc_sk_lookup_tcp_proto = { BPF_CALL_5(bpf_tc_sk_lookup_udp, struct sk_buff *, skb, struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags) { - struct net *caller_net = dev_net(skb->dev); - int ifindex = skb->dev->ifindex; + struct net_device *dev = skb->dev; + int ifindex = dev->ifindex, sdif = dev_sdif(dev); + struct net *caller_net = dev_net(dev); return (unsigned long)__bpf_sk_lookup(skb, tuple, len, caller_net, ifindex, IPPROTO_UDP, netns_id, - flags); + flags, sdif); } static const struct bpf_func_proto bpf_tc_sk_lookup_udp_proto = { @@ -6732,12 +6737,13 @@ static const struct bpf_func_proto bpf_sk_release_proto = { BPF_CALL_5(bpf_xdp_sk_lookup_udp, struct xdp_buff *, ctx, struct bpf_sock_tuple *, tuple, u32, len, u32, netns_id, u64, flags) { - struct net *caller_net = dev_net(ctx->rxq->dev); - int ifindex = ctx->rxq->dev->ifindex; + struct net_device *dev = ctx->rxq->dev; + int ifindex = dev->ifindex, sdif = dev_sdif(dev); + struct net *caller_net = dev_net(dev); return (unsigned long)__bpf_sk_lookup(NULL, tuple, len, caller_net, ifindex, IPPROTO_UDP, netns_id, - flags); + flags, sdif); } static const struct bpf_func_proto bpf_xdp_sk_lookup_udp_proto = { @@ -6755,12 +6761,13 @@ static const struct bpf_func_proto bpf_xdp_sk_lookup_udp_proto = { BPF_CALL_5(bpf_xdp_skc_lookup_tcp, struct xdp_buff *, ctx, struct bpf_sock_tuple *, tuple, u32, len, u32, netns_id, u64, flags) { - struct net *caller_net = dev_net(ctx->rxq->dev); - int ifindex = ctx->rxq->dev->ifindex; + struct net_device *dev = ctx->rxq->dev; + int ifindex = dev->ifindex, sdif = dev_sdif(dev); + struct net *caller_net = dev_net(dev); return (unsigned long)__bpf_skc_lookup(NULL, tuple, len, caller_net, ifindex, IPPROTO_TCP, netns_id, - flags); + flags, sdif); } static const struct bpf_func_proto bpf_xdp_skc_lookup_tcp_proto = { @@ -6778,12 +6785,13 @@ static const struct bpf_func_proto bpf_xdp_skc_lookup_tcp_proto = { BPF_CALL_5(bpf_xdp_sk_lookup_tcp, struct xdp_buff *, ctx, struct bpf_sock_tuple *, tuple, u32, len, u32, netns_id, u64, flags) { - struct net *caller_net = dev_net(ctx->rxq->dev); - int ifindex = ctx->rxq->dev->ifindex; + struct net_device *dev = ctx->rxq->dev; + int ifindex = dev->ifindex, sdif = dev_sdif(dev); + struct net *caller_net = dev_net(dev); return (unsigned long)__bpf_sk_lookup(NULL, tuple, len, caller_net, ifindex, IPPROTO_TCP, netns_id, - flags); + flags, sdif); } static const struct bpf_func_proto bpf_xdp_sk_lookup_tcp_proto = { @@ -6803,7 +6811,8 @@ BPF_CALL_5(bpf_sock_addr_skc_lookup_tcp, struct bpf_sock_addr_kern *, ctx, { return (unsigned long)__bpf_skc_lookup(NULL, tuple, len, sock_net(ctx->sk), 0, - IPPROTO_TCP, netns_id, flags); + IPPROTO_TCP, netns_id, flags, + -1); } static const struct bpf_func_proto bpf_sock_addr_skc_lookup_tcp_proto = { @@ -6822,7 +6831,7 @@ BPF_CALL_5(bpf_sock_addr_sk_lookup_tcp, struct bpf_sock_addr_kern *, ctx, { return (unsigned long)__bpf_sk_lookup(NULL, tuple, len, sock_net(ctx->sk), 0, IPPROTO_TCP, - netns_id, flags); + netns_id, flags, -1); } static const struct bpf_func_proto bpf_sock_addr_sk_lookup_tcp_proto = { @@ -6841,7 +6850,7 @@ BPF_CALL_5(bpf_sock_addr_sk_lookup_udp, struct bpf_sock_addr_kern *, ctx, { return (unsigned long)__bpf_sk_lookup(NULL, tuple, len, sock_net(ctx->sk), 0, IPPROTO_UDP, - netns_id, flags); + netns_id, flags, -1); } static const struct bpf_func_proto bpf_sock_addr_sk_lookup_udp_proto = { -- cgit 1.4.1 From 787b4042098fd942c2861ba959cd34e44f8c2071 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Sun, 11 Jun 2023 11:57:26 +0900 Subject: can: length: fix bitstuffing count [ Upstream commit 9fde4c557f78ee2f3626e92b4089ce9d54a2573a ] The Stuff Bit Count is always coded on 4 bits [1]. Update the Stuff Bit Count size accordingly. In addition, the CRC fields of CAN FD Frames contain stuff bits at fixed positions called fixed stuff bits [2]. The CRC field starts with a fixed stuff bit and then has another fixed stuff bit after each fourth bit [2], which allows us to derive this formula: FSB count = 1 + round_down(len(CRC field)/4) The length of the CRC field is [1]: len(CRC field) = len(Stuff Bit Count) + len(CRC) = 4 + len(CRC) with len(CRC) either 17 or 21 bits depending of the payload length. In conclusion, for CRC17: FSB count = 1 + round_down((4 + 17)/4) = 6 and for CRC 21: FSB count = 1 + round_down((4 + 21)/4) = 7 Add a Fixed Stuff bits (FSB) field with above values and update CANFD_FRAME_OVERHEAD_SFF and CANFD_FRAME_OVERHEAD_EFF accordingly. [1] ISO 11898-1:2015 section 10.4.2.6 "CRC field": The CRC field shall contain the CRC sequence followed by a recessive CRC delimiter. For FD Frames, the CRC field shall also contain the stuff count. Stuff count If FD Frames, the stuff count shall be at the beginning of the CRC field. It shall consist of the stuff bit count modulo 8 in a 3-bit gray code followed by a parity bit [...] [2] ISO 11898-1:2015 paragraph 10.5 "Frame coding": In the CRC field of FD Frames, the stuff bits shall be inserted at fixed positions; they are called fixed stuff bits. There shall be a fixed stuff bit before the first bit of the stuff count, even if the last bits of the preceding field are a sequence of five consecutive bits of identical value, there shall be only the fixed stuff bit, there shall not be two consecutive stuff bits. A further fixed stuff bit shall be inserted after each fourth bit of the CRC field [...] Fixes: 85d99c3e2a13 ("can: length: can_skb_get_frame_len(): introduce function to get data length of frame in data link layer") Suggested-by: Thomas Kopp Signed-off-by: Vincent Mailhol Reviewed-by: Thomas Kopp Link: https://lore.kernel.org/all/20230611025728.450837-2-mailhol.vincent@wanadoo.fr Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- include/linux/can/length.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/linux/can/length.h b/include/linux/can/length.h index 6995092b774e..ef1fd32cef16 100644 --- a/include/linux/can/length.h +++ b/include/linux/can/length.h @@ -69,17 +69,18 @@ * Error Status Indicator (ESI) 1 * Data length code (DLC) 4 * Data field 0...512 - * Stuff Bit Count (SBC) 0...16: 4 20...64:5 + * Stuff Bit Count (SBC) 4 * CRC 0...16: 17 20...64:21 * CRC delimiter (CD) 1 + * Fixed Stuff bits (FSB) 0...16: 6 20...64:7 * ACK slot (AS) 1 * ACK delimiter (AD) 1 * End-of-frame (EOF) 7 * Inter frame spacing 3 * - * assuming CRC21, rounded up and ignoring bitstuffing + * assuming CRC21, rounded up and ignoring dynamic bitstuffing */ -#define CANFD_FRAME_OVERHEAD_SFF DIV_ROUND_UP(61, 8) +#define CANFD_FRAME_OVERHEAD_SFF DIV_ROUND_UP(67, 8) /* * Size of a CAN-FD Extended Frame @@ -98,17 +99,18 @@ * Error Status Indicator (ESI) 1 * Data length code (DLC) 4 * Data field 0...512 - * Stuff Bit Count (SBC) 0...16: 4 20...64:5 + * Stuff Bit Count (SBC) 4 * CRC 0...16: 17 20...64:21 * CRC delimiter (CD) 1 + * Fixed Stuff bits (FSB) 0...16: 6 20...64:7 * ACK slot (AS) 1 * ACK delimiter (AD) 1 * End-of-frame (EOF) 7 * Inter frame spacing 3 * - * assuming CRC21, rounded up and ignoring bitstuffing + * assuming CRC21, rounded up and ignoring dynamic bitstuffing */ -#define CANFD_FRAME_OVERHEAD_EFF DIV_ROUND_UP(80, 8) +#define CANFD_FRAME_OVERHEAD_EFF DIV_ROUND_UP(86, 8) /* * Maximum size of a Classical CAN frame -- cgit 1.4.1 From 70ace9ba20521f3201746c5fe0e5c633ccb69c72 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Mon, 29 May 2023 15:42:37 +0200 Subject: can: kvaser_pciefd: Add function to set skb hwtstamps [ Upstream commit 2d55e9f9b4427e1ad59b974f2267767aac3788d3 ] Add new function, kvaser_pciefd_set_skb_timestamp(), to set skb hwtstamps. Signed-off-by: Jimmy Assarsson Reviewed-by: Vincent Mailhol Link: https://lore.kernel.org/all/20230529134248.752036-4-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Stable-dep-of: ec681b91befa ("can: kvaser_pciefd: Set hardware timestamp on transmitted packets") Signed-off-by: Sasha Levin --- drivers/net/can/kvaser_pciefd.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 956a4a57396f..0b537292c7b1 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -538,6 +538,13 @@ static int kvaser_pciefd_set_tx_irq(struct kvaser_pciefd_can *can) return 0; } +static inline void kvaser_pciefd_set_skb_timestamp(const struct kvaser_pciefd *pcie, + struct sk_buff *skb, u64 timestamp) +{ + skb_hwtstamps(skb)->hwtstamp = + ns_to_ktime(div_u64(timestamp * 1000, pcie->freq_to_ticks_div)); +} + static void kvaser_pciefd_setup_controller(struct kvaser_pciefd_can *can) { u32 mode; @@ -1171,7 +1178,6 @@ static int kvaser_pciefd_handle_data_packet(struct kvaser_pciefd *pcie, struct canfd_frame *cf; struct can_priv *priv; struct net_device_stats *stats; - struct skb_shared_hwtstamps *shhwtstamps; u8 ch_id = (p->header[1] >> KVASER_PCIEFD_PACKET_CHID_SHIFT) & 0x7; if (ch_id >= pcie->nr_channels) @@ -1214,12 +1220,7 @@ static int kvaser_pciefd_handle_data_packet(struct kvaser_pciefd *pcie, stats->rx_bytes += cf->len; } stats->rx_packets++; - - shhwtstamps = skb_hwtstamps(skb); - - shhwtstamps->hwtstamp = - ns_to_ktime(div_u64(p->timestamp * 1000, - pcie->freq_to_ticks_div)); + kvaser_pciefd_set_skb_timestamp(pcie, skb, p->timestamp); return netif_rx(skb); } @@ -1282,7 +1283,6 @@ static int kvaser_pciefd_rx_error_frame(struct kvaser_pciefd_can *can, struct net_device *ndev = can->can.dev; struct sk_buff *skb; struct can_frame *cf = NULL; - struct skb_shared_hwtstamps *shhwtstamps; struct net_device_stats *stats = &ndev->stats; old_state = can->can.state; @@ -1323,10 +1323,7 @@ static int kvaser_pciefd_rx_error_frame(struct kvaser_pciefd_can *can, return -ENOMEM; } - shhwtstamps = skb_hwtstamps(skb); - shhwtstamps->hwtstamp = - ns_to_ktime(div_u64(p->timestamp * 1000, - can->kv_pcie->freq_to_ticks_div)); + kvaser_pciefd_set_skb_timestamp(can->kv_pcie, skb, p->timestamp); cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_CNT; cf->data[6] = bec.txerr; @@ -1374,7 +1371,6 @@ static int kvaser_pciefd_handle_status_resp(struct kvaser_pciefd_can *can, struct net_device *ndev = can->can.dev; struct sk_buff *skb; struct can_frame *cf; - struct skb_shared_hwtstamps *shhwtstamps; skb = alloc_can_err_skb(ndev, &cf); if (!skb) { @@ -1394,10 +1390,7 @@ static int kvaser_pciefd_handle_status_resp(struct kvaser_pciefd_can *can, cf->can_id |= CAN_ERR_RESTARTED; } - shhwtstamps = skb_hwtstamps(skb); - shhwtstamps->hwtstamp = - ns_to_ktime(div_u64(p->timestamp * 1000, - can->kv_pcie->freq_to_ticks_div)); + kvaser_pciefd_set_skb_timestamp(can->kv_pcie, skb, p->timestamp); cf->data[6] = bec.txerr; cf->data[7] = bec.rxerr; -- cgit 1.4.1 From 12bcb533280b0506ce309fa371c3588704186f2b Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Mon, 29 May 2023 15:42:38 +0200 Subject: can: kvaser_pciefd: Set hardware timestamp on transmitted packets [ Upstream commit ec681b91befa982477e24a150dd6452427fe6473 ] Set hardware timestamp on transmitted packets. Fixes: 26ad340e582d ("can: kvaser_pciefd: Add driver for Kvaser PCIEcan devices") Signed-off-by: Jimmy Assarsson Reviewed-by: Vincent Mailhol Link: https://lore.kernel.org/all/20230529134248.752036-5-extja@kvaser.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- drivers/net/can/kvaser_pciefd.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/kvaser_pciefd.c b/drivers/net/can/kvaser_pciefd.c index 0b537292c7b1..74a47244f129 100644 --- a/drivers/net/can/kvaser_pciefd.c +++ b/drivers/net/can/kvaser_pciefd.c @@ -1519,6 +1519,7 @@ static void kvaser_pciefd_handle_nack_packet(struct kvaser_pciefd_can *can, if (skb) { cf->can_id |= CAN_ERR_BUSERROR; + kvaser_pciefd_set_skb_timestamp(can->kv_pcie, skb, p->timestamp); netif_rx(skb); } else { stats->rx_dropped++; @@ -1550,8 +1551,15 @@ static int kvaser_pciefd_handle_ack_packet(struct kvaser_pciefd *pcie, netdev_dbg(can->can.dev, "Packet was flushed\n"); } else { int echo_idx = p->header[0] & KVASER_PCIEFD_PACKET_SEQ_MSK; - int dlc = can_get_echo_skb(can->can.dev, echo_idx, NULL); - u8 count = ioread32(can->reg_base + + int dlc; + u8 count; + struct sk_buff *skb; + + skb = can->can.echo_skb[echo_idx]; + if (skb) + kvaser_pciefd_set_skb_timestamp(pcie, skb, p->timestamp); + dlc = can_get_echo_skb(can->can.dev, echo_idx, NULL); + count = ioread32(can->reg_base + KVASER_PCIEFD_KCAN_TX_NPACKETS_REG) & 0xff; if (count < KVASER_PCIEFD_CAN_TX_MAX_COUNT && -- cgit 1.4.1 From 509d5d40c24954ac6e38ec4a022cb1613e8728a3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 21 Jun 2023 15:55:37 +0200 Subject: net: stmmac: fix double serdes powerdown [ Upstream commit c4fc88ad2a765224a648db8ab35f125e120fe41b ] Commit 49725ffc15fc ("net: stmmac: power up/down serdes in stmmac_open/release") correctly added a call to the serdes_powerdown() callback to stmmac_release() but did not remove the one from stmmac_remove() which leads to a doubled call to serdes_powerdown(). This can lead to all kinds of problems: in the case of the qcom ethqos driver, it caused an unbalanced regulator disable splat. Fixes: 49725ffc15fc ("net: stmmac: power up/down serdes in stmmac_open/release") Signed-off-by: Bartosz Golaszewski Reviewed-by: Jiri Pirko Acked-by: Junxiao Chang Reviewed-by: Andrew Halaney Tested-by: Andrew Halaney Link: https://lore.kernel.org/r/20230621135537.376649-1-brgl@bgdev.pl Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 30ce07305578..a07bcb2f5d2e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -7388,12 +7388,6 @@ int stmmac_dvr_remove(struct device *dev) netif_carrier_off(ndev); unregister_netdev(ndev); - /* Serdes power down needs to happen after VLAN filter - * is deleted that is triggered by unregister_netdev(). - */ - if (priv->plat->serdes_powerdown) - priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv); - #ifdef CONFIG_DEBUG_FS stmmac_exit_fs(ndev); #endif -- cgit 1.4.1 From 61ffe8b1ee084e5c82a4e4bbf9e7b68e0c06e464 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 21 Jun 2023 15:43:37 +0000 Subject: netlink: fix potential deadlock in netlink_set_err() [ Upstream commit 8d61f926d42045961e6b65191c09e3678d86a9cf ] syzbot reported a possible deadlock in netlink_set_err() [1] A similar issue was fixed in commit 1d482e666b8e ("netlink: disable IRQs for netlink_lock_table()") in netlink_lock_table() This patch adds IRQ safety to netlink_set_err() and __netlink_diag_dump() which were not covered by cited commit. [1] WARNING: possible irq lock inversion dependency detected 6.4.0-rc6-syzkaller-00240-g4e9f0ec38852 #0 Not tainted syz-executor.2/23011 just changed the state of lock: ffffffff8e1a7a58 (nl_table_lock){.+.?}-{2:2}, at: netlink_set_err+0x2e/0x3a0 net/netlink/af_netlink.c:1612 but this lock was taken by another, SOFTIRQ-safe lock in the past: (&local->queue_stop_reason_lock){..-.}-{2:2} and interrupts could create inverse lock ordering between them. other info that might help us debug this: Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(nl_table_lock); local_irq_disable(); lock(&local->queue_stop_reason_lock); lock(nl_table_lock); lock(&local->queue_stop_reason_lock); *** DEADLOCK *** Fixes: 1d482e666b8e ("netlink: disable IRQs for netlink_lock_table()") Reported-by: syzbot+a7d200a347f912723e5c@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=a7d200a347f912723e5c Link: https://lore.kernel.org/netdev/000000000000e38d1605fea5747e@google.com/T/#u Signed-off-by: Eric Dumazet Cc: Johannes Berg Link: https://lore.kernel.org/r/20230621154337.1668594-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/netlink/af_netlink.c | 5 +++-- net/netlink/diag.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6d493a0ccf39..ed123cf462af 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1589,6 +1589,7 @@ out: int netlink_set_err(struct sock *ssk, u32 portid, u32 group, int code) { struct netlink_set_err_data info; + unsigned long flags; struct sock *sk; int ret = 0; @@ -1598,12 +1599,12 @@ int netlink_set_err(struct sock *ssk, u32 portid, u32 group, int code) /* sk->sk_err wants a positive error value */ info.code = -code; - read_lock(&nl_table_lock); + read_lock_irqsave(&nl_table_lock, flags); sk_for_each_bound(sk, &nl_table[ssk->sk_protocol].mc_list) ret += do_one_set_err(sk, &info); - read_unlock(&nl_table_lock); + read_unlock_irqrestore(&nl_table_lock, flags); return ret; } EXPORT_SYMBOL(netlink_set_err); diff --git a/net/netlink/diag.c b/net/netlink/diag.c index c6255eac305c..4143b2ea4195 100644 --- a/net/netlink/diag.c +++ b/net/netlink/diag.c @@ -94,6 +94,7 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net *net = sock_net(skb->sk); struct netlink_diag_req *req; struct netlink_sock *nlsk; + unsigned long flags; struct sock *sk; int num = 2; int ret = 0; @@ -152,7 +153,7 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, num++; mc_list: - read_lock(&nl_table_lock); + read_lock_irqsave(&nl_table_lock, flags); sk_for_each_bound(sk, &tbl->mc_list) { if (sk_hashed(sk)) continue; @@ -173,7 +174,7 @@ mc_list: } num++; } - read_unlock(&nl_table_lock); + read_unlock_irqrestore(&nl_table_lock, flags); done: cb->args[0] = num; -- cgit 1.4.1 From e9331c8fa4c69f09d2c71682af75586f77266e81 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 21 Jun 2023 17:47:20 +0000 Subject: netlink: do not hard code device address lenth in fdb dumps [ Upstream commit aa5406950726e336c5c9585b09799a734b6e77bf ] syzbot reports that some netdev devices do not have a six bytes address [1] Replace ETH_ALEN by dev->addr_len. [1] (Case of a device where dev->addr_len = 4) BUG: KMSAN: kernel-infoleak in instrument_copy_to_user include/linux/instrumented.h:114 [inline] BUG: KMSAN: kernel-infoleak in copyout+0xb8/0x100 lib/iov_iter.c:169 instrument_copy_to_user include/linux/instrumented.h:114 [inline] copyout+0xb8/0x100 lib/iov_iter.c:169 _copy_to_iter+0x6d8/0x1d00 lib/iov_iter.c:536 copy_to_iter include/linux/uio.h:206 [inline] simple_copy_to_iter+0x68/0xa0 net/core/datagram.c:513 __skb_datagram_iter+0x123/0xdc0 net/core/datagram.c:419 skb_copy_datagram_iter+0x5c/0x200 net/core/datagram.c:527 skb_copy_datagram_msg include/linux/skbuff.h:3960 [inline] netlink_recvmsg+0x4ae/0x15a0 net/netlink/af_netlink.c:1970 sock_recvmsg_nosec net/socket.c:1019 [inline] sock_recvmsg net/socket.c:1040 [inline] ____sys_recvmsg+0x283/0x7f0 net/socket.c:2722 ___sys_recvmsg+0x223/0x840 net/socket.c:2764 do_recvmmsg+0x4f9/0xfd0 net/socket.c:2858 __sys_recvmmsg net/socket.c:2937 [inline] __do_sys_recvmmsg net/socket.c:2960 [inline] __se_sys_recvmmsg net/socket.c:2953 [inline] __x64_sys_recvmmsg+0x397/0x490 net/socket.c:2953 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd Uninit was stored to memory at: __nla_put lib/nlattr.c:1009 [inline] nla_put+0x1c6/0x230 lib/nlattr.c:1067 nlmsg_populate_fdb_fill+0x2b8/0x600 net/core/rtnetlink.c:4071 nlmsg_populate_fdb net/core/rtnetlink.c:4418 [inline] ndo_dflt_fdb_dump+0x616/0x840 net/core/rtnetlink.c:4456 rtnl_fdb_dump+0x14ff/0x1fc0 net/core/rtnetlink.c:4629 netlink_dump+0x9d1/0x1310 net/netlink/af_netlink.c:2268 netlink_recvmsg+0xc5c/0x15a0 net/netlink/af_netlink.c:1995 sock_recvmsg_nosec+0x7a/0x120 net/socket.c:1019 ____sys_recvmsg+0x664/0x7f0 net/socket.c:2720 ___sys_recvmsg+0x223/0x840 net/socket.c:2764 do_recvmmsg+0x4f9/0xfd0 net/socket.c:2858 __sys_recvmmsg net/socket.c:2937 [inline] __do_sys_recvmmsg net/socket.c:2960 [inline] __se_sys_recvmmsg net/socket.c:2953 [inline] __x64_sys_recvmmsg+0x397/0x490 net/socket.c:2953 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd Uninit was created at: slab_post_alloc_hook+0x12d/0xb60 mm/slab.h:716 slab_alloc_node mm/slub.c:3451 [inline] __kmem_cache_alloc_node+0x4ff/0x8b0 mm/slub.c:3490 kmalloc_trace+0x51/0x200 mm/slab_common.c:1057 kmalloc include/linux/slab.h:559 [inline] __hw_addr_create net/core/dev_addr_lists.c:60 [inline] __hw_addr_add_ex+0x2e5/0x9e0 net/core/dev_addr_lists.c:118 __dev_mc_add net/core/dev_addr_lists.c:867 [inline] dev_mc_add+0x9a/0x130 net/core/dev_addr_lists.c:885 igmp6_group_added+0x267/0xbc0 net/ipv6/mcast.c:680 ipv6_mc_up+0x296/0x3b0 net/ipv6/mcast.c:2754 ipv6_mc_remap+0x1e/0x30 net/ipv6/mcast.c:2708 addrconf_type_change net/ipv6/addrconf.c:3731 [inline] addrconf_notify+0x4d3/0x1d90 net/ipv6/addrconf.c:3699 notifier_call_chain kernel/notifier.c:93 [inline] raw_notifier_call_chain+0xe4/0x430 kernel/notifier.c:461 call_netdevice_notifiers_info net/core/dev.c:1935 [inline] call_netdevice_notifiers_extack net/core/dev.c:1973 [inline] call_netdevice_notifiers+0x1ee/0x2d0 net/core/dev.c:1987 bond_enslave+0xccd/0x53f0 drivers/net/bonding/bond_main.c:1906 do_set_master net/core/rtnetlink.c:2626 [inline] rtnl_newlink_create net/core/rtnetlink.c:3460 [inline] __rtnl_newlink net/core/rtnetlink.c:3660 [inline] rtnl_newlink+0x378c/0x40e0 net/core/rtnetlink.c:3673 rtnetlink_rcv_msg+0x16a6/0x1840 net/core/rtnetlink.c:6395 netlink_rcv_skb+0x371/0x650 net/netlink/af_netlink.c:2546 rtnetlink_rcv+0x34/0x40 net/core/rtnetlink.c:6413 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0xf28/0x1230 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x122f/0x13d0 net/netlink/af_netlink.c:1913 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg net/socket.c:747 [inline] ____sys_sendmsg+0x999/0xd50 net/socket.c:2503 ___sys_sendmsg+0x28d/0x3c0 net/socket.c:2557 __sys_sendmsg net/socket.c:2586 [inline] __do_sys_sendmsg net/socket.c:2595 [inline] __se_sys_sendmsg net/socket.c:2593 [inline] __x64_sys_sendmsg+0x304/0x490 net/socket.c:2593 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd Bytes 2856-2857 of 3500 are uninitialized Memory access of size 3500 starts at ffff888018d99104 Data copied to user address 0000000020000480 Fixes: d83b06036048 ("net: add fdb generic dump routine") Reported-by: syzbot Signed-off-by: Eric Dumazet Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/20230621174720.1845040-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/rtnetlink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index ef2a07ae5496..5625ed30a06f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3992,7 +3992,7 @@ static int nlmsg_populate_fdb_fill(struct sk_buff *skb, ndm->ndm_ifindex = dev->ifindex; ndm->ndm_state = ndm_state; - if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr)) + if (nla_put(skb, NDA_LLADDR, dev->addr_len, addr)) goto nla_put_failure; if (vid) if (nla_put(skb, NDA_VLAN, sizeof(u16), &vid)) @@ -4006,10 +4006,10 @@ nla_put_failure: return -EMSGSIZE; } -static inline size_t rtnl_fdb_nlmsg_size(void) +static inline size_t rtnl_fdb_nlmsg_size(const struct net_device *dev) { return NLMSG_ALIGN(sizeof(struct ndmsg)) + - nla_total_size(ETH_ALEN) + /* NDA_LLADDR */ + nla_total_size(dev->addr_len) + /* NDA_LLADDR */ nla_total_size(sizeof(u16)) + /* NDA_VLAN */ 0; } @@ -4021,7 +4021,7 @@ static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type, struct sk_buff *skb; int err = -ENOBUFS; - skb = nlmsg_new(rtnl_fdb_nlmsg_size(), GFP_ATOMIC); + skb = nlmsg_new(rtnl_fdb_nlmsg_size(dev), GFP_ATOMIC); if (!skb) goto errout; -- cgit 1.4.1 From 37b6143376a578265add04f35161b257eeb84a5e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 22 Jun 2023 15:23:04 +0000 Subject: bonding: do not assume skb mac_header is set [ Upstream commit 6a940abdef3162e5723f1495b8a49859d1708f79 ] Drivers must not assume in their ndo_start_xmit() that skbs have their mac_header set. skb->data is all what is needed. bonding seems to be one of the last offender as caught by syzbot: WARNING: CPU: 1 PID: 12155 at include/linux/skbuff.h:2907 skb_mac_offset include/linux/skbuff.h:2913 [inline] WARNING: CPU: 1 PID: 12155 at include/linux/skbuff.h:2907 bond_xmit_hash drivers/net/bonding/bond_main.c:4170 [inline] WARNING: CPU: 1 PID: 12155 at include/linux/skbuff.h:2907 bond_xmit_3ad_xor_slave_get drivers/net/bonding/bond_main.c:5149 [inline] WARNING: CPU: 1 PID: 12155 at include/linux/skbuff.h:2907 bond_3ad_xor_xmit drivers/net/bonding/bond_main.c:5186 [inline] WARNING: CPU: 1 PID: 12155 at include/linux/skbuff.h:2907 __bond_start_xmit drivers/net/bonding/bond_main.c:5442 [inline] WARNING: CPU: 1 PID: 12155 at include/linux/skbuff.h:2907 bond_start_xmit+0x14ab/0x19d0 drivers/net/bonding/bond_main.c:5470 Modules linked in: CPU: 1 PID: 12155 Comm: syz-executor.3 Not tainted 6.1.30-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/25/2023 RIP: 0010:skb_mac_header include/linux/skbuff.h:2907 [inline] RIP: 0010:skb_mac_offset include/linux/skbuff.h:2913 [inline] RIP: 0010:bond_xmit_hash drivers/net/bonding/bond_main.c:4170 [inline] RIP: 0010:bond_xmit_3ad_xor_slave_get drivers/net/bonding/bond_main.c:5149 [inline] RIP: 0010:bond_3ad_xor_xmit drivers/net/bonding/bond_main.c:5186 [inline] RIP: 0010:__bond_start_xmit drivers/net/bonding/bond_main.c:5442 [inline] RIP: 0010:bond_start_xmit+0x14ab/0x19d0 drivers/net/bonding/bond_main.c:5470 Code: 8b 7c 24 30 e8 76 dd 1a 01 48 85 c0 74 0d 48 89 c3 e8 29 67 2e fe e9 15 ef ff ff e8 1f 67 2e fe e9 10 ef ff ff e8 15 67 2e fe <0f> 0b e9 45 f8 ff ff e8 09 67 2e fe e9 dc fa ff ff e8 ff 66 2e fe RSP: 0018:ffffc90002fff6e0 EFLAGS: 00010283 RAX: ffffffff835874db RBX: 000000000000ffff RCX: 0000000000040000 RDX: ffffc90004dcf000 RSI: 00000000000000b5 RDI: 00000000000000b6 RBP: ffffc90002fff8b8 R08: ffffffff83586d16 R09: ffffffff83586584 R10: 0000000000000007 R11: ffff8881599fc780 R12: ffff88811b6a7b7e R13: 1ffff110236d4f6f R14: ffff88811b6a7ac0 R15: 1ffff110236d4f76 FS: 00007f2e9eb47700(0000) GS:ffff8881f6b00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000001b2e421000 CR3: 000000010e6d4000 CR4: 00000000003526e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: [] netdev_start_xmit include/linux/netdevice.h:4925 [inline] [] __dev_direct_xmit+0x4ef/0x850 net/core/dev.c:4380 [] dev_direct_xmit include/linux/netdevice.h:3043 [inline] [] packet_direct_xmit+0x18b/0x300 net/packet/af_packet.c:284 [] packet_snd net/packet/af_packet.c:3112 [inline] [] packet_sendmsg+0x4a22/0x64d0 net/packet/af_packet.c:3143 [] sock_sendmsg_nosec net/socket.c:716 [inline] [] sock_sendmsg net/socket.c:736 [inline] [] __sys_sendto+0x472/0x5f0 net/socket.c:2139 [] __do_sys_sendto net/socket.c:2151 [inline] [] __se_sys_sendto net/socket.c:2147 [inline] [] __x64_sys_sendto+0xe5/0x100 net/socket.c:2147 [] do_syscall_x64 arch/x86/entry/common.c:50 [inline] [] do_syscall_64+0x2f/0x50 arch/x86/entry/common.c:80 [] entry_SYSCALL_64_after_hwframe+0x63/0xcd Fixes: 7b8fc0103bb5 ("bonding: add a vlan+srcmac tx hashing option") Reported-by: syzbot Signed-off-by: Eric Dumazet Cc: Jarod Wilson Cc: Moshe Tal Cc: Jussi Maki Cc: Jay Vosburgh Cc: Andy Gospodarek Cc: Vladimir Oltean Link: https://lore.kernel.org/r/20230622152304.2137482-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 9ed80f710651..91d84df91123 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4168,7 +4168,7 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb) return skb->hash; return __bond_xmit_hash(bond, skb, skb->data, skb->protocol, - skb_mac_offset(skb), skb_network_offset(skb), + 0, skb_network_offset(skb), skb_headlen(skb)); } -- cgit 1.4.1 From b48c24392d86c7832d3965431985457806801a99 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 22 Jun 2023 23:03:34 +0200 Subject: selftests: rtnetlink: remove netdevsim device after ipsec offload test [ Upstream commit 5f789f103671fec3733ebe756e56adf15c90c21d ] On systems where netdevsim is built-in or loaded before the test starts, kci_test_ipsec_offload doesn't remove the netdevsim device it created during the test. Fixes: e05b2d141fef ("netdevsim: move netdev creation/destruction to dev probe") Signed-off-by: Sabrina Dubroca Reviewed-by: Simon Horman Reviewed-by: Jiri Pirko Link: https://lore.kernel.org/r/e1cb94f4f82f4eca4a444feec4488a1323396357.1687466906.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/rtnetlink.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 275491be3da2..cafd14b1ed2a 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -835,6 +835,7 @@ EOF fi # clean up any leftovers + echo 0 > /sys/bus/netdevsim/del_device $probed && rmmod netdevsim if [ $ret -ne 0 ]; then -- cgit 1.4.1 From 17d6b6354f0025b7c10a56da783fd0cbb3819c5d Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 22 Jun 2023 14:32:31 -0700 Subject: gtp: Fix use-after-free in __gtp_encap_destroy(). [ Upstream commit ce3aee7114c575fab32a5e9e939d4bbb3dcca79f ] syzkaller reported use-after-free in __gtp_encap_destroy(). [0] It shows the same process freed sk and touched it illegally. Commit e198987e7dd7 ("gtp: fix suspicious RCU usage") added lock_sock() and release_sock() in __gtp_encap_destroy() to protect sk->sk_user_data, but release_sock() is called after sock_put() releases the last refcnt. [0]: BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:96 [inline] BUG: KASAN: slab-use-after-free in atomic_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:541 [inline] BUG: KASAN: slab-use-after-free in queued_spin_lock include/asm-generic/qspinlock.h:111 [inline] BUG: KASAN: slab-use-after-free in do_raw_spin_lock include/linux/spinlock.h:186 [inline] BUG: KASAN: slab-use-after-free in __raw_spin_lock_bh include/linux/spinlock_api_smp.h:127 [inline] BUG: KASAN: slab-use-after-free in _raw_spin_lock_bh+0x75/0xe0 kernel/locking/spinlock.c:178 Write of size 4 at addr ffff88800dbef398 by task syz-executor.2/2401 CPU: 1 PID: 2401 Comm: syz-executor.2 Not tainted 6.4.0-rc5-01219-gfa0e21fa4443 #2 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106 print_address_description mm/kasan/report.c:351 [inline] print_report+0xcc/0x620 mm/kasan/report.c:462 kasan_report+0xb2/0xe0 mm/kasan/report.c:572 check_region_inline mm/kasan/generic.c:181 [inline] kasan_check_range+0x39/0x1c0 mm/kasan/generic.c:187 instrument_atomic_read_write include/linux/instrumented.h:96 [inline] atomic_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:541 [inline] queued_spin_lock include/asm-generic/qspinlock.h:111 [inline] do_raw_spin_lock include/linux/spinlock.h:186 [inline] __raw_spin_lock_bh include/linux/spinlock_api_smp.h:127 [inline] _raw_spin_lock_bh+0x75/0xe0 kernel/locking/spinlock.c:178 spin_lock_bh include/linux/spinlock.h:355 [inline] release_sock+0x1f/0x1a0 net/core/sock.c:3526 gtp_encap_disable_sock drivers/net/gtp.c:651 [inline] gtp_encap_disable+0xb9/0x220 drivers/net/gtp.c:664 gtp_dev_uninit+0x19/0x50 drivers/net/gtp.c:728 unregister_netdevice_many_notify+0x97e/0x1520 net/core/dev.c:10841 rtnl_delete_link net/core/rtnetlink.c:3216 [inline] rtnl_dellink+0x3c0/0xb30 net/core/rtnetlink.c:3268 rtnetlink_rcv_msg+0x450/0xb10 net/core/rtnetlink.c:6423 netlink_rcv_skb+0x15d/0x450 net/netlink/af_netlink.c:2548 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x700/0x930 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x91c/0xe30 net/netlink/af_netlink.c:1913 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg+0x1b7/0x200 net/socket.c:747 ____sys_sendmsg+0x75a/0x990 net/socket.c:2493 ___sys_sendmsg+0x11d/0x1c0 net/socket.c:2547 __sys_sendmsg+0xfe/0x1d0 net/socket.c:2576 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc RIP: 0033:0x7f1168b1fe5d Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 73 9f 1b 00 f7 d8 64 89 01 48 RSP: 002b:00007f1167edccc8 EFLAGS: 00000246 ORIG_RAX: 000000000000002e RAX: ffffffffffffffda RBX: 00000000004bbf80 RCX: 00007f1168b1fe5d RDX: 0000000000000000 RSI: 00000000200002c0 RDI: 0000000000000003 RBP: 00000000004bbf80 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 000000000000000b R14: 00007f1168b80530 R15: 0000000000000000 Allocated by task 1483: kasan_save_stack+0x22/0x50 mm/kasan/common.c:45 kasan_set_track+0x25/0x30 mm/kasan/common.c:52 __kasan_slab_alloc+0x59/0x70 mm/kasan/common.c:328 kasan_slab_alloc include/linux/kasan.h:186 [inline] slab_post_alloc_hook mm/slab.h:711 [inline] slab_alloc_node mm/slub.c:3451 [inline] slab_alloc mm/slub.c:3459 [inline] __kmem_cache_alloc_lru mm/slub.c:3466 [inline] kmem_cache_alloc+0x16d/0x340 mm/slub.c:3475 sk_prot_alloc+0x5f/0x280 net/core/sock.c:2073 sk_alloc+0x34/0x6c0 net/core/sock.c:2132 inet6_create net/ipv6/af_inet6.c:192 [inline] inet6_create+0x2c7/0xf20 net/ipv6/af_inet6.c:119 __sock_create+0x2a1/0x530 net/socket.c:1535 sock_create net/socket.c:1586 [inline] __sys_socket_create net/socket.c:1623 [inline] __sys_socket_create net/socket.c:1608 [inline] __sys_socket+0x137/0x250 net/socket.c:1651 __do_sys_socket net/socket.c:1664 [inline] __se_sys_socket net/socket.c:1662 [inline] __x64_sys_socket+0x72/0xb0 net/socket.c:1662 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc Freed by task 2401: kasan_save_stack+0x22/0x50 mm/kasan/common.c:45 kasan_set_track+0x25/0x30 mm/kasan/common.c:52 kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521 ____kasan_slab_free mm/kasan/common.c:236 [inline] ____kasan_slab_free mm/kasan/common.c:200 [inline] __kasan_slab_free+0x10c/0x1b0 mm/kasan/common.c:244 kasan_slab_free include/linux/kasan.h:162 [inline] slab_free_hook mm/slub.c:1781 [inline] slab_free_freelist_hook mm/slub.c:1807 [inline] slab_free mm/slub.c:3786 [inline] kmem_cache_free+0xb4/0x490 mm/slub.c:3808 sk_prot_free net/core/sock.c:2113 [inline] __sk_destruct+0x500/0x720 net/core/sock.c:2207 sk_destruct+0xc1/0xe0 net/core/sock.c:2222 __sk_free+0xed/0x3d0 net/core/sock.c:2233 sk_free+0x7c/0xa0 net/core/sock.c:2244 sock_put include/net/sock.h:1981 [inline] __gtp_encap_destroy+0x165/0x1b0 drivers/net/gtp.c:634 gtp_encap_disable_sock drivers/net/gtp.c:651 [inline] gtp_encap_disable+0xb9/0x220 drivers/net/gtp.c:664 gtp_dev_uninit+0x19/0x50 drivers/net/gtp.c:728 unregister_netdevice_many_notify+0x97e/0x1520 net/core/dev.c:10841 rtnl_delete_link net/core/rtnetlink.c:3216 [inline] rtnl_dellink+0x3c0/0xb30 net/core/rtnetlink.c:3268 rtnetlink_rcv_msg+0x450/0xb10 net/core/rtnetlink.c:6423 netlink_rcv_skb+0x15d/0x450 net/netlink/af_netlink.c:2548 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x700/0x930 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x91c/0xe30 net/netlink/af_netlink.c:1913 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg+0x1b7/0x200 net/socket.c:747 ____sys_sendmsg+0x75a/0x990 net/socket.c:2493 ___sys_sendmsg+0x11d/0x1c0 net/socket.c:2547 __sys_sendmsg+0xfe/0x1d0 net/socket.c:2576 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc The buggy address belongs to the object at ffff88800dbef300 which belongs to the cache UDPv6 of size 1344 The buggy address is located 152 bytes inside of freed 1344-byte region [ffff88800dbef300, ffff88800dbef840) The buggy address belongs to the physical page: page:00000000d31bfed5 refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff88800dbeed40 pfn:0xdbe8 head:00000000d31bfed5 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 memcg:ffff888008ee0801 flags: 0x100000000010200(slab|head|node=0|zone=1) page_type: 0xffffffff() raw: 0100000000010200 ffff88800c7a3000 dead000000000122 0000000000000000 raw: ffff88800dbeed40 0000000080160015 00000001ffffffff ffff888008ee0801 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88800dbef280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88800dbef300: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff88800dbef380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff88800dbef400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88800dbef480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb Fixes: e198987e7dd7 ("gtp: fix suspicious RCU usage") Reported-by: syzkaller Signed-off-by: Kuniyuki Iwashima Reviewed-by: Pablo Neira Ayuso Link: https://lore.kernel.org/r/20230622213231.24651-1-kuniyu@amazon.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/gtp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 15c7dc82107f..acb20ad4e37e 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -631,7 +631,9 @@ static void __gtp_encap_destroy(struct sock *sk) gtp->sk1u = NULL; udp_sk(sk)->encap_type = 0; rcu_assign_sk_user_data(sk, NULL); + release_sock(sk); sock_put(sk); + return; } release_sock(sk); } -- cgit 1.4.1 From 8c438ff5d9e309cf16b3a4ceeb37e3c13fdf74a1 Mon Sep 17 00:00:00 2001 From: Maxim Kochetkov Date: Thu, 22 Jun 2023 22:22:45 +0300 Subject: net: axienet: Move reset before 64-bit DMA detection [ Upstream commit f1bc9fc4a06de0108e0dca2a9a7e99ba1fc632f9 ] 64-bit DMA detection will fail if axienet was started before (by boot loader, boot ROM, etc). In this state axienet will not start properly. XAXIDMA_TX_CDESC_OFFSET + 4 register (MM2S_CURDESC_MSB) is used to detect 64-bit DMA capability here. But datasheet says: When DMACR.RS is 1 (axienet is in enabled state), CURDESC_PTR becomes Read Only (RO) and is used to fetch the first descriptor. So iowrite32()/ioread32() trick to this register to detect 64-bit DMA will not work. So move axienet reset before 64-bit DMA detection. Fixes: f735c40ed93c ("net: axienet: Autodetect 64-bit DMA capability") Signed-off-by: Maxim Kochetkov Reviewed-by: Robert Hancock Reviewed-by: Radhey Shyam Pandey Link: https://lore.kernel.org/r/20230622192245.116864-1-fido_max@inbox.ru Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d1d772580da9..d14648558338 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -2043,6 +2043,11 @@ static int axienet_probe(struct platform_device *pdev) goto cleanup_clk; } + /* Reset core now that clocks are enabled, prior to accessing MDIO */ + ret = __axienet_device_reset(lp); + if (ret) + goto cleanup_clk; + /* Autodetect the need for 64-bit DMA pointers. * When the IP is configured for a bus width bigger than 32 bits, * writing the MSB registers is mandatory, even if they are all 0. @@ -2097,11 +2102,6 @@ static int axienet_probe(struct platform_device *pdev) lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD; lp->coalesce_usec_tx = XAXIDMA_DFT_TX_USEC; - /* Reset core now that clocks are enabled, prior to accessing MDIO */ - ret = __axienet_device_reset(lp); - if (ret) - goto cleanup_clk; - ret = axienet_mdio_setup(lp); if (ret) dev_warn(&pdev->dev, -- cgit 1.4.1 From 94817712b500593841ee7e0a67163b1965332e78 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 23 Jun 2023 23:55:10 +0100 Subject: ocfs2: Fix use of slab data with sendpage [ Upstream commit 86d7bd6e66e9925f0f04a7bcf3c92c05fdfefb5a ] ocfs2 uses kzalloc() to allocate buffers for o2net_hand, o2net_keep_req and o2net_keep_resp and then passes these to sendpage. This isn't really allowed as the lifetime of slab objects is not controlled by page ref - though in this case it will probably work. sendmsg() with MSG_SPLICE_PAGES will, however, print a warning and give an error. Fix it to use folio_alloc() instead to allocate a buffer for the handshake message, keepalive request and reply messages. Fixes: 98211489d414 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Signed-off-by: David Howells cc: Mark Fasheh cc: Kurt Hackel cc: Joel Becker cc: Joseph Qi cc: ocfs2-devel@oss.oracle.com Link: https://lore.kernel.org/r/20230623225513.2732256-14-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- fs/ocfs2/cluster/tcp.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 785cabd71d67..4e083e25d0e7 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -2083,18 +2083,24 @@ void o2net_stop_listening(struct o2nm_node *node) int o2net_init(void) { + struct folio *folio; + void *p; unsigned long i; o2quo_init(); - o2net_debugfs_init(); - o2net_hand = kzalloc(sizeof(struct o2net_handshake), GFP_KERNEL); - o2net_keep_req = kzalloc(sizeof(struct o2net_msg), GFP_KERNEL); - o2net_keep_resp = kzalloc(sizeof(struct o2net_msg), GFP_KERNEL); - if (!o2net_hand || !o2net_keep_req || !o2net_keep_resp) + folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, 0); + if (!folio) goto out; + p = folio_address(folio); + o2net_hand = p; + p += sizeof(struct o2net_handshake); + o2net_keep_req = p; + p += sizeof(struct o2net_msg); + o2net_keep_resp = p; + o2net_hand->protocol_version = cpu_to_be64(O2NET_PROTOCOL_VERSION); o2net_hand->connector_id = cpu_to_be64(1); @@ -2120,9 +2126,6 @@ int o2net_init(void) return 0; out: - kfree(o2net_hand); - kfree(o2net_keep_req); - kfree(o2net_keep_resp); o2net_debugfs_exit(); o2quo_exit(); return -ENOMEM; @@ -2131,8 +2134,6 @@ out: void o2net_exit(void) { o2quo_exit(); - kfree(o2net_hand); - kfree(o2net_keep_req); - kfree(o2net_keep_resp); o2net_debugfs_exit(); + folio_put(virt_to_folio(o2net_hand)); } -- cgit 1.4.1 From 446f5567934331923d0aec4ce045e4ecb0174aae Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Fri, 23 Jun 2023 15:34:48 +0100 Subject: sfc: fix crash when reading stats while NIC is resetting [ Upstream commit d1b355438b8325a486f087e506d412c4e852f37b ] efx_net_stats() (.ndo_get_stats64) can be called during an ethtool selftest, during which time nic_data->mc_stats is NULL as the NIC has been fini'd. In this case do not attempt to fetch the latest stats from the hardware, else we will crash on a NULL dereference: BUG: kernel NULL pointer dereference, address: 0000000000000038 RIP efx_nic_update_stats abridged calltrace: efx_ef10_update_stats_pf efx_net_stats dev_get_stats dev_seq_printf_stats Skipping the read is safe, we will simply give out stale stats. To ensure that the free in efx_ef10_fini_nic() does not race against efx_ef10_update_stats_pf(), which could cause a TOCTTOU bug, take the efx->stats_lock in fini_nic (it is already held across update_stats). Fixes: d3142c193dca ("sfc: refactor EF10 stats handling") Reviewed-by: Pieter Jansen van Vuuren Signed-off-by: Edward Cree Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/sfc/ef10.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index b63e47af6365..8c019f382a7f 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -1297,8 +1297,10 @@ static void efx_ef10_fini_nic(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; + spin_lock_bh(&efx->stats_lock); kfree(nic_data->mc_stats); nic_data->mc_stats = NULL; + spin_unlock_bh(&efx->stats_lock); } static int efx_ef10_init_nic(struct efx_nic *efx) @@ -1852,9 +1854,14 @@ static size_t efx_ef10_update_stats_pf(struct efx_nic *efx, u64 *full_stats, efx_ef10_get_stat_mask(efx, mask); - efx_nic_copy_stats(efx, nic_data->mc_stats); - efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, - mask, stats, nic_data->mc_stats, false); + /* If NIC was fini'd (probably resetting), then we can't read + * updated stats right now. + */ + if (nic_data->mc_stats) { + efx_nic_copy_stats(efx, nic_data->mc_stats); + efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, + mask, stats, nic_data->mc_stats, false); + } /* Update derived statistics */ efx_nic_fix_nodesc_drop_stat(efx, -- cgit 1.4.1 From 425d9d3a92df7d96b3cfb7ee5c240293a21cbde3 Mon Sep 17 00:00:00 2001 From: Lin Ma Date: Sun, 25 Jun 2023 17:10:07 +0800 Subject: net: nfc: Fix use-after-free caused by nfc_llcp_find_local [ Upstream commit 6709d4b7bc2e079241fdef15d1160581c5261c10 ] This commit fixes several use-after-free that caused by function nfc_llcp_find_local(). For example, one UAF can happen when below buggy time window occurs. // nfc_genl_llc_get_params | // nfc_unregister_device | dev = nfc_get_device(idx); | device_lock(...) if (!dev) | dev->shutting_down = true; return -ENODEV; | device_unlock(...); | device_lock(...); | // nfc_llcp_unregister_device | nfc_llcp_find_local() nfc_llcp_find_local(...); | | local_cleanup() if (!local) { | rc = -ENODEV; | // nfc_llcp_local_put goto exit; | kref_put(.., local_release) } | | // local_release | list_del(&local->list) // nfc_genl_send_params | kfree() local->dev->idx !!!UAF!!! | | and the crash trace for the one of the discussed UAF like: BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045 Read of size 8 at addr ffff888105b0e410 by task 20114 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106 print_address_description mm/kasan/report.c:319 [inline] print_report+0xcc/0x620 mm/kasan/report.c:430 kasan_report+0xb2/0xe0 mm/kasan/report.c:536 nfc_genl_send_params net/nfc/netlink.c:999 [inline] nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045 genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968 genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline] genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065 netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548 genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg+0x1b6/0x200 net/socket.c:747 ____sys_sendmsg+0x6e9/0x890 net/socket.c:2501 ___sys_sendmsg+0x110/0x1b0 net/socket.c:2555 __sys_sendmsg+0xf7/0x1d0 net/socket.c:2584 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc RIP: 0033:0x7f34640a2389 RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389 RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006 RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000 Allocated by task 20116: kasan_save_stack+0x22/0x50 mm/kasan/common.c:45 kasan_set_track+0x25/0x30 mm/kasan/common.c:52 ____kasan_kmalloc mm/kasan/common.c:374 [inline] __kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383 kmalloc include/linux/slab.h:580 [inline] kzalloc include/linux/slab.h:720 [inline] nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567 nfc_register_device+0x61/0x260 net/nfc/core.c:1124 nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257 virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148 misc_open+0x379/0x4a0 drivers/char/misc.c:165 chrdev_open+0x26c/0x780 fs/char_dev.c:414 do_dentry_open+0x6c4/0x12a0 fs/open.c:920 do_open fs/namei.c:3560 [inline] path_openat+0x24fe/0x37e0 fs/namei.c:3715 do_filp_open+0x1ba/0x410 fs/namei.c:3742 do_sys_openat2+0x171/0x4c0 fs/open.c:1356 do_sys_open fs/open.c:1372 [inline] __do_sys_openat fs/open.c:1388 [inline] __se_sys_openat fs/open.c:1383 [inline] __x64_sys_openat+0x143/0x200 fs/open.c:1383 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc Freed by task 20115: kasan_save_stack+0x22/0x50 mm/kasan/common.c:45 kasan_set_track+0x25/0x30 mm/kasan/common.c:52 kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521 ____kasan_slab_free mm/kasan/common.c:236 [inline] ____kasan_slab_free mm/kasan/common.c:200 [inline] __kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244 kasan_slab_free include/linux/kasan.h:162 [inline] slab_free_hook mm/slub.c:1781 [inline] slab_free_freelist_hook mm/slub.c:1807 [inline] slab_free mm/slub.c:3787 [inline] __kmem_cache_free+0x7a/0x190 mm/slub.c:3800 local_release net/nfc/llcp_core.c:174 [inline] kref_put include/linux/kref.h:65 [inline] nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline] nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline] nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620 nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179 virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163 __fput+0x252/0xa20 fs/file_table.c:321 task_work_run+0x174/0x270 kernel/task_work.c:179 resume_user_mode_work include/linux/resume_user_mode.h:49 [inline] exit_to_user_mode_loop kernel/entry/common.c:171 [inline] exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204 __syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline] syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297 do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86 entry_SYSCALL_64_after_hwframe+0x72/0xdc Last potentially related work creation: kasan_save_stack+0x22/0x50 mm/kasan/common.c:45 __kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491 kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328 drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735 unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773 unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753 neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895 addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684 notifier_call_chain+0xbe/0x210 kernel/notifier.c:87 call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937 call_netdevice_notifiers_extack net/core/dev.c:1975 [inline] call_netdevice_notifiers net/core/dev.c:1989 [inline] dev_change_name+0x3c3/0x870 net/core/dev.c:1211 dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376 dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542 sock_do_ioctl+0x160/0x260 net/socket.c:1213 sock_ioctl+0x3f9/0x670 net/socket.c:1316 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:870 [inline] __se_sys_ioctl fs/ioctl.c:856 [inline] __x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc The buggy address belongs to the object at ffff888105b0e400 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 16 bytes inside of freed 1024-byte region [ffff888105b0e400, ffff888105b0e800) The buggy address belongs to the physical page: head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x200000000010200(slab|head|node=0|zone=2) raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10 raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb In summary, this patch solves those use-after-free by 1. Re-implement the nfc_llcp_find_local(). The current version does not grab the reference when getting the local from the linked list. For example, the llcp_sock_bind() gets the reference like below: // llcp_sock_bind() local = nfc_llcp_find_local(dev); // A ..... \ | raceable ..... / llcp_sock->local = nfc_llcp_local_get(local); // B There is an apparent race window that one can drop the reference and free the local object fetched in (A) before (B) gets the reference. 2. Some callers of the nfc_llcp_find_local() do not grab the reference at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions. We add the nfc_llcp_local_put() for them. Moreover, we add the necessary error handling function to put the reference. 3. Add the nfc_llcp_remove_local() helper. The local object is removed from the linked list in local_release() when all reference is gone. This patch removes it when nfc_llcp_unregister_device() is called. Therefore, every caller of nfc_llcp_find_local() will get a reference even when the nfc_llcp_unregister_device() is called. This promises no use-after-free for the local object is ever possible. Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support") Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket") Signed-off-by: Lin Ma Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/nfc/llcp.h | 1 - net/nfc/llcp_commands.c | 12 +++++++++--- net/nfc/llcp_core.c | 49 ++++++++++++++++++++++++++++++++++++++++++------- net/nfc/llcp_sock.c | 18 ++++++++++-------- net/nfc/netlink.c | 20 +++++++++++++++----- net/nfc/nfc.h | 1 + 6 files changed, 77 insertions(+), 24 deletions(-) diff --git a/net/nfc/llcp.h b/net/nfc/llcp.h index c1d9be636933..d8345ed57c95 100644 --- a/net/nfc/llcp.h +++ b/net/nfc/llcp.h @@ -201,7 +201,6 @@ void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *s); void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *s); void nfc_llcp_socket_remote_param_init(struct nfc_llcp_sock *sock); struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); -struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local); int nfc_llcp_local_put(struct nfc_llcp_local *local); u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, struct nfc_llcp_sock *sock); diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index cdb001de0692..e2680a3bef79 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -359,6 +359,7 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) struct sk_buff *skb; struct nfc_llcp_local *local; u16 size = 0; + int err; local = nfc_llcp_find_local(dev); if (local == NULL) @@ -368,8 +369,10 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; skb = alloc_skb(size, GFP_KERNEL); - if (skb == NULL) - return -ENOMEM; + if (skb == NULL) { + err = -ENOMEM; + goto out; + } skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); @@ -379,8 +382,11 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX); - return nfc_data_exchange(dev, local->target_idx, skb, + err = nfc_data_exchange(dev, local->target_idx, skb, nfc_llcp_recv, local); +out: + nfc_llcp_local_put(local); + return err; } int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index a27e1842b2a0..f60e424e0607 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -17,6 +17,8 @@ static u8 llcp_magic[3] = {0x46, 0x66, 0x6d}; static LIST_HEAD(llcp_devices); +/* Protects llcp_devices list */ +static DEFINE_SPINLOCK(llcp_devices_lock); static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb); @@ -141,7 +143,7 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device, write_unlock(&local->raw_sockets.lock); } -struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local) +static struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local) { kref_get(&local->ref); @@ -169,7 +171,6 @@ static void local_release(struct kref *ref) local = container_of(ref, struct nfc_llcp_local, ref); - list_del(&local->list); local_cleanup(local); kfree(local); } @@ -282,12 +283,33 @@ static void nfc_llcp_sdreq_timer(struct timer_list *t) struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev) { struct nfc_llcp_local *local; + struct nfc_llcp_local *res = NULL; + spin_lock(&llcp_devices_lock); list_for_each_entry(local, &llcp_devices, list) - if (local->dev == dev) + if (local->dev == dev) { + res = nfc_llcp_local_get(local); + break; + } + spin_unlock(&llcp_devices_lock); + + return res; +} + +static struct nfc_llcp_local *nfc_llcp_remove_local(struct nfc_dev *dev) +{ + struct nfc_llcp_local *local, *tmp; + + spin_lock(&llcp_devices_lock); + list_for_each_entry_safe(local, tmp, &llcp_devices, list) + if (local->dev == dev) { + list_del(&local->list); + spin_unlock(&llcp_devices_lock); return local; + } + spin_unlock(&llcp_devices_lock); - pr_debug("No device found\n"); + pr_warn("Shutting down device not found\n"); return NULL; } @@ -608,12 +630,15 @@ u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len) *general_bytes_len = local->gb_len; + nfc_llcp_local_put(local); + return local->gb; } int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len) { struct nfc_llcp_local *local; + int err; if (gb_len < 3 || gb_len > NFC_MAX_GT_LEN) return -EINVAL; @@ -630,12 +655,16 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len) if (memcmp(local->remote_gb, llcp_magic, 3)) { pr_err("MAC does not support LLCP\n"); - return -EINVAL; + err = -EINVAL; + goto out; } - return nfc_llcp_parse_gb_tlv(local, + err = nfc_llcp_parse_gb_tlv(local, &local->remote_gb[3], local->remote_gb_len - 3); +out: + nfc_llcp_local_put(local); + return err; } static u8 nfc_llcp_dsap(const struct sk_buff *pdu) @@ -1517,6 +1546,8 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) __nfc_llcp_recv(local, skb); + nfc_llcp_local_put(local); + return 0; } @@ -1533,6 +1564,8 @@ void nfc_llcp_mac_is_down(struct nfc_dev *dev) /* Close and purge all existing sockets */ nfc_llcp_socket_release(local, true, 0); + + nfc_llcp_local_put(local); } void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx, @@ -1558,6 +1591,8 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx, mod_timer(&local->link_timer, jiffies + msecs_to_jiffies(local->remote_lto)); } + + nfc_llcp_local_put(local); } int nfc_llcp_register_device(struct nfc_dev *ndev) @@ -1608,7 +1643,7 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) void nfc_llcp_unregister_device(struct nfc_dev *dev) { - struct nfc_llcp_local *local = nfc_llcp_find_local(dev); + struct nfc_llcp_local *local = nfc_llcp_remove_local(dev); if (local == NULL) { pr_debug("No such device\n"); diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 77642d18a3b4..645677f84dba 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -99,7 +99,7 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) } llcp_sock->dev = dev; - llcp_sock->local = nfc_llcp_local_get(local); + llcp_sock->local = local; llcp_sock->nfc_protocol = llcp_addr.nfc_protocol; llcp_sock->service_name_len = min_t(unsigned int, llcp_addr.service_name_len, @@ -186,7 +186,7 @@ static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr, } llcp_sock->dev = dev; - llcp_sock->local = nfc_llcp_local_get(local); + llcp_sock->local = local; llcp_sock->nfc_protocol = llcp_addr.nfc_protocol; nfc_llcp_sock_link(&local->raw_sockets, sk); @@ -696,22 +696,22 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, if (dev->dep_link_up == false) { ret = -ENOLINK; device_unlock(&dev->dev); - goto put_dev; + goto sock_llcp_put_local; } device_unlock(&dev->dev); if (local->rf_mode == NFC_RF_INITIATOR && addr->target_idx != local->target_idx) { ret = -ENOLINK; - goto put_dev; + goto sock_llcp_put_local; } llcp_sock->dev = dev; - llcp_sock->local = nfc_llcp_local_get(local); + llcp_sock->local = local; llcp_sock->ssap = nfc_llcp_get_local_ssap(local); if (llcp_sock->ssap == LLCP_SAP_MAX) { ret = -ENOMEM; - goto sock_llcp_put_local; + goto sock_llcp_nullify; } llcp_sock->reserved_ssap = llcp_sock->ssap; @@ -757,11 +757,13 @@ sock_unlink: sock_llcp_release: nfc_llcp_put_ssap(local, llcp_sock->ssap); -sock_llcp_put_local: - nfc_llcp_local_put(llcp_sock->local); +sock_llcp_nullify: llcp_sock->local = NULL; llcp_sock->dev = NULL; +sock_llcp_put_local: + nfc_llcp_local_put(local); + put_dev: nfc_put_device(dev); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index b9264e730fd9..e9ac6a6f934e 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1039,11 +1039,14 @@ static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info) msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { rc = -ENOMEM; - goto exit; + goto put_local; } rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq); +put_local: + nfc_llcp_local_put(local); + exit: device_unlock(&dev->dev); @@ -1105,7 +1108,7 @@ static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) { if (dev->dep_link_up) { rc = -EINPROGRESS; - goto exit; + goto put_local; } local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]); @@ -1117,6 +1120,9 @@ static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) local->miux = cpu_to_be16(miux); +put_local: + nfc_llcp_local_put(local); + exit: device_unlock(&dev->dev); @@ -1172,7 +1178,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) if (rc != 0) { rc = -EINVAL; - goto exit; + goto put_local; } if (!sdp_attrs[NFC_SDP_ATTR_URI]) @@ -1191,7 +1197,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len); if (sdreq == NULL) { rc = -ENOMEM; - goto exit; + goto put_local; } tlvs_len += sdreq->tlv_len; @@ -1201,10 +1207,14 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) if (hlist_empty(&sdreq_list)) { rc = -EINVAL; - goto exit; + goto put_local; } rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len); + +put_local: + nfc_llcp_local_put(local); + exit: device_unlock(&dev->dev); diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index de2ec66d7e83..0b1e6466f4fb 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -52,6 +52,7 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len); u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len); int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb); struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); +int nfc_llcp_local_put(struct nfc_llcp_local *local); int __init nfc_llcp_init(void); void nfc_llcp_exit(void); void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp); -- cgit 1.4.1 From cfcb9f0a499dafadcbfe43b1c587f20e4e953b19 Mon Sep 17 00:00:00 2001 From: Jeremy Sowden Date: Mon, 19 Jun 2023 20:06:57 +0100 Subject: lib/ts_bm: reset initial match offset for every block of text [ Upstream commit 6f67fbf8192da80c4db01a1800c7fceaca9cf1f9 ] The `shift` variable which indicates the offset in the string at which to start matching the pattern is initialized to `bm->patlen - 1`, but it is not reset when a new block is retrieved. This means the implemen- tation may start looking at later and later positions in each successive block and miss occurrences of the pattern at the beginning. E.g., consider a HTTP packet held in a non-linear skb, where the HTTP request line occurs in the second block: [... 52 bytes of packet headers ...] GET /bmtest HTTP/1.1\r\nHost: www.example.com\r\n\r\n and the pattern is "GET /bmtest". Once the first block comprising the packet headers has been examined, `shift` will be pointing to somewhere near the end of the block, and so when the second block is examined the request line at the beginning will be missed. Reinitialize the variable for each new block. Fixes: 8082e4ed0a61 ("[LIB]: Boyer-Moore extension for textsearch infrastructure strike #2") Link: https://bugzilla.netfilter.org/show_bug.cgi?id=1390 Signed-off-by: Jeremy Sowden Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- lib/ts_bm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ts_bm.c b/lib/ts_bm.c index 1f2234221dd1..c8ecbf74ef29 100644 --- a/lib/ts_bm.c +++ b/lib/ts_bm.c @@ -60,10 +60,12 @@ static unsigned int bm_find(struct ts_config *conf, struct ts_state *state) struct ts_bm *bm = ts_config_priv(conf); unsigned int i, text_len, consumed = state->offset; const u8 *text; - int shift = bm->patlen - 1, bs; + int bs; const u8 icase = conf->flags & TS_IGNORECASE; for (;;) { + int shift = bm->patlen - 1; + text_len = conf->get_next_block(consumed, &text, conf, state); if (unlikely(text_len == 0)) -- cgit 1.4.1 From 5c618daa5038712c4a4ef8923905a2ea1b8836a1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 21 Jun 2023 17:56:53 +0200 Subject: netfilter: conntrack: dccp: copy entire header to stack buffer, not just basic one [ Upstream commit ff0a3a7d52ff7282dbd183e7fc29a1fe386b0c30 ] Eric Dumazet says: nf_conntrack_dccp_packet() has an unique: dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); And nothing more is 'pulled' from the packet, depending on the content. dh->dccph_doff, and/or dh->dccph_x ...) So dccp_ack_seq() is happily reading stuff past the _dh buffer. BUG: KASAN: stack-out-of-bounds in nf_conntrack_dccp_packet+0x1134/0x11c0 Read of size 4 at addr ffff000128f66e0c by task syz-executor.2/29371 [..] Fix this by increasing the stack buffer to also include room for the extra sequence numbers and all the known dccp packet type headers, then pull again after the initial validation of the basic header. While at it, mark packets invalid that lack 48bit sequence bit but where RFC says the type MUST use them. Compile tested only. v2: first skb_header_pointer() now needs to adjust the size to only pull the generic header. (Eric) Heads-up: I intend to remove dccp conntrack support later this year. Fixes: 2bc780499aa3 ("[NETFILTER]: nf_conntrack: add DCCP protocol support") Reported-by: Eric Dumazet Signed-off-by: Florian Westphal Reviewed-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_proto_dccp.c | 52 +++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index c1557d47ccd1..d4fd626d2b8c 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -432,9 +432,19 @@ static bool dccp_error(const struct dccp_hdr *dh, struct sk_buff *skb, unsigned int dataoff, const struct nf_hook_state *state) { + static const unsigned long require_seq48 = 1 << DCCP_PKT_REQUEST | + 1 << DCCP_PKT_RESPONSE | + 1 << DCCP_PKT_CLOSEREQ | + 1 << DCCP_PKT_CLOSE | + 1 << DCCP_PKT_RESET | + 1 << DCCP_PKT_SYNC | + 1 << DCCP_PKT_SYNCACK; unsigned int dccp_len = skb->len - dataoff; unsigned int cscov; const char *msg; + u8 type; + + BUILD_BUG_ON(DCCP_PKT_INVALID >= BITS_PER_LONG); if (dh->dccph_doff * 4 < sizeof(struct dccp_hdr) || dh->dccph_doff * 4 > dccp_len) { @@ -459,34 +469,70 @@ static bool dccp_error(const struct dccp_hdr *dh, goto out_invalid; } - if (dh->dccph_type >= DCCP_PKT_INVALID) { + type = dh->dccph_type; + if (type >= DCCP_PKT_INVALID) { msg = "nf_ct_dccp: reserved packet type "; goto out_invalid; } + + if (test_bit(type, &require_seq48) && !dh->dccph_x) { + msg = "nf_ct_dccp: type lacks 48bit sequence numbers"; + goto out_invalid; + } + return false; out_invalid: nf_l4proto_log_invalid(skb, state, IPPROTO_DCCP, "%s", msg); return true; } +struct nf_conntrack_dccp_buf { + struct dccp_hdr dh; /* generic header part */ + struct dccp_hdr_ext ext; /* optional depending dh->dccph_x */ + union { /* depends on header type */ + struct dccp_hdr_ack_bits ack; + struct dccp_hdr_request req; + struct dccp_hdr_response response; + struct dccp_hdr_reset rst; + } u; +}; + +static struct dccp_hdr * +dccp_header_pointer(const struct sk_buff *skb, int offset, const struct dccp_hdr *dh, + struct nf_conntrack_dccp_buf *buf) +{ + unsigned int hdrlen = __dccp_hdr_len(dh); + + if (hdrlen > sizeof(*buf)) + return NULL; + + return skb_header_pointer(skb, offset, hdrlen, buf); +} + int nf_conntrack_dccp_packet(struct nf_conn *ct, struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, const struct nf_hook_state *state) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - struct dccp_hdr _dh, *dh; + struct nf_conntrack_dccp_buf _dh; u_int8_t type, old_state, new_state; enum ct_dccp_roles role; unsigned int *timeouts; + struct dccp_hdr *dh; - dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); + dh = skb_header_pointer(skb, dataoff, sizeof(*dh), &_dh.dh); if (!dh) return NF_DROP; if (dccp_error(dh, skb, dataoff, state)) return -NF_ACCEPT; + /* pull again, including possible 48 bit sequences and subtype header */ + dh = dccp_header_pointer(skb, dataoff, dh, &_dh); + if (!dh) + return NF_DROP; + type = dh->dccph_type; if (!nf_ct_is_confirmed(ct) && !dccp_new(ct, skb, dh, state)) return -NF_ACCEPT; -- cgit 1.4.1 From d6cf5026af734ef68805d10adb6495dd83e7f566 Mon Sep 17 00:00:00 2001 From: "Ilia.Gavrilov" Date: Fri, 23 Jun 2023 11:23:46 +0000 Subject: netfilter: nf_conntrack_sip: fix the ct_sip_parse_numerical_param() return value. [ Upstream commit f188d30087480eab421cd8ca552fb15f55d57f4d ] ct_sip_parse_numerical_param() returns only 0 or 1 now. But process_register_request() and process_register_response() imply checking for a negative value if parsing of a numerical header parameter failed. The invocation in nf_nat_sip() looks correct: if (ct_sip_parse_numerical_param(...) > 0 && ...) { ... } Make the return value of the function ct_sip_parse_numerical_param() a tristate to fix all the cases a) return 1 if value is found; *val is set b) return 0 if value is not found; *val is unchanged c) return -1 on error; *val is undefined Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 0f32a40fc91a ("[NETFILTER]: nf_conntrack_sip: create signalling expectations") Signed-off-by: Ilia.Gavrilov Reviewed-by: Simon Horman Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_sip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 77f5e82d8e3f..d0eac27f6ba0 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -611,7 +611,7 @@ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, start += strlen(name); *val = simple_strtoul(start, &end, 0); if (start == end) - return 0; + return -1; if (matchoff && matchlen) { *matchoff = start - dptr; *matchlen = end - start; -- cgit 1.4.1 From f6d2e25c647f5424b1c90010fab3daf4bc8e4a49 Mon Sep 17 00:00:00 2001 From: Cambda Zhu Date: Mon, 26 Jun 2023 17:33:47 +0800 Subject: ipvlan: Fix return value of ipvlan_queue_xmit() [ Upstream commit 8a9922e7be6d042fa00f894c376473b17a162b66 ] ipvlan_queue_xmit() should return NET_XMIT_XXX, but ipvlan_xmit_mode_l2/l3() returns rx_handler_result_t or NET_RX_XXX in some cases. ipvlan_rcv_frame() will only return RX_HANDLER_CONSUMED in ipvlan_xmit_mode_l2/l3() because 'local' is true. It's equal to NET_XMIT_SUCCESS. But dev_forward_skb() can return NET_RX_SUCCESS or NET_RX_DROP, and returning NET_RX_DROP(NET_XMIT_DROP) will increase both ipvlan and ipvlan->phy_dev drops counter. The skb to forward can be treated as xmitted successfully. This patch makes ipvlan_queue_xmit() return NET_XMIT_SUCCESS for forward skb. Fixes: 2ad7bf363841 ("ipvlan: Initial check-in of the IPVLAN driver.") Signed-off-by: Cambda Zhu Link: https://lore.kernel.org/r/20230626093347.7492-1-cambda@linux.alibaba.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ipvlan/ipvlan_core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 2de3bd3b0c27..59e29e08398a 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -585,7 +585,8 @@ static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev) consume_skb(skb); return NET_XMIT_DROP; } - return ipvlan_rcv_frame(addr, &skb, true); + ipvlan_rcv_frame(addr, &skb, true); + return NET_XMIT_SUCCESS; } } out: @@ -611,7 +612,8 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev) consume_skb(skb); return NET_XMIT_DROP; } - return ipvlan_rcv_frame(addr, &skb, true); + ipvlan_rcv_frame(addr, &skb, true); + return NET_XMIT_SUCCESS; } } skb = skb_share_check(skb, GFP_ATOMIC); @@ -623,7 +625,8 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev) * the skb for the main-dev. At the RX side we just return * RX_PASS for it to be processed further on the stack. */ - return dev_forward_skb(ipvlan->phy_dev, skb); + dev_forward_skb(ipvlan->phy_dev, skb); + return NET_XMIT_SUCCESS; } else if (is_multicast_ether_addr(eth->h_dest)) { skb_reset_mac_header(skb); -- cgit 1.4.1 From 3b3186c770204bd61f17cb7e6df4c7404cdca39b Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 26 Jun 2023 09:43:13 -0700 Subject: netlink: Add __sock_i_ino() for __netlink_diag_dump(). [ Upstream commit 25a9c8a4431c364f97f75558cb346d2ad3f53fbb ] syzbot reported a warning in __local_bh_enable_ip(). [0] Commit 8d61f926d420 ("netlink: fix potential deadlock in netlink_set_err()") converted read_lock(&nl_table_lock) to read_lock_irqsave() in __netlink_diag_dump() to prevent a deadlock. However, __netlink_diag_dump() calls sock_i_ino() that uses read_lock_bh() and read_unlock_bh(). If CONFIG_TRACE_IRQFLAGS=y, read_unlock_bh() finally enables IRQ even though it should stay disabled until the following read_unlock_irqrestore(). Using read_lock() in sock_i_ino() would trigger a lockdep splat in another place that was fixed in commit f064af1e500a ("net: fix a lockdep splat"), so let's add __sock_i_ino() that would be safe to use under BH disabled. [0]: WARNING: CPU: 0 PID: 5012 at kernel/softirq.c:376 __local_bh_enable_ip+0xbe/0x130 kernel/softirq.c:376 Modules linked in: CPU: 0 PID: 5012 Comm: syz-executor487 Not tainted 6.4.0-rc7-syzkaller-00202-g6f68fc395f49 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/27/2023 RIP: 0010:__local_bh_enable_ip+0xbe/0x130 kernel/softirq.c:376 Code: 45 bf 01 00 00 00 e8 91 5b 0a 00 e8 3c 15 3d 00 fb 65 8b 05 ec e9 b5 7e 85 c0 74 58 5b 5d c3 65 8b 05 b2 b6 b4 7e 85 c0 75 a2 <0f> 0b eb 9e e8 89 15 3d 00 eb 9f 48 89 ef e8 6f 49 18 00 eb a8 0f RSP: 0018:ffffc90003a1f3d0 EFLAGS: 00010046 RAX: 0000000000000000 RBX: 0000000000000201 RCX: 1ffffffff1cf5996 RDX: 0000000000000000 RSI: 0000000000000201 RDI: ffffffff8805c6f3 RBP: ffffffff8805c6f3 R08: 0000000000000001 R09: ffff8880152b03a3 R10: ffffed1002a56074 R11: 0000000000000005 R12: 00000000000073e4 R13: dffffc0000000000 R14: 0000000000000002 R15: 0000000000000000 FS: 0000555556726300(0000) GS:ffff8880b9800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000000000045ad50 CR3: 000000007c646000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: sock_i_ino+0x83/0xa0 net/core/sock.c:2559 __netlink_diag_dump+0x45c/0x790 net/netlink/diag.c:171 netlink_diag_dump+0xd6/0x230 net/netlink/diag.c:207 netlink_dump+0x570/0xc50 net/netlink/af_netlink.c:2269 __netlink_dump_start+0x64b/0x910 net/netlink/af_netlink.c:2374 netlink_dump_start include/linux/netlink.h:329 [inline] netlink_diag_handler_dump+0x1ae/0x250 net/netlink/diag.c:238 __sock_diag_cmd net/core/sock_diag.c:238 [inline] sock_diag_rcv_msg+0x31e/0x440 net/core/sock_diag.c:269 netlink_rcv_skb+0x165/0x440 net/netlink/af_netlink.c:2547 sock_diag_rcv+0x2a/0x40 net/core/sock_diag.c:280 netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] netlink_unicast+0x547/0x7f0 net/netlink/af_netlink.c:1365 netlink_sendmsg+0x925/0xe30 net/netlink/af_netlink.c:1914 sock_sendmsg_nosec net/socket.c:724 [inline] sock_sendmsg+0xde/0x190 net/socket.c:747 ____sys_sendmsg+0x71c/0x900 net/socket.c:2503 ___sys_sendmsg+0x110/0x1b0 net/socket.c:2557 __sys_sendmsg+0xf7/0x1c0 net/socket.c:2586 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x39/0xb0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7f5303aaabb9 Code: 28 c3 e8 2a 14 00 00 66 2e 0f 1f 84 00 00 00 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffc7506e548 EFLAGS: 00000246 ORIG_RAX: 000000000000002e RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f5303aaabb9 RDX: 0000000000000000 RSI: 0000000020000180 RDI: 0000000000000003 RBP: 00007f5303a6ed60 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5303a6edf0 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 Fixes: 8d61f926d420 ("netlink: fix potential deadlock in netlink_set_err()") Reported-by: syzbot+5da61cf6a9bc1902d422@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=5da61cf6a9bc1902d422 Suggested-by: Eric Dumazet Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20230626164313.52528-1-kuniyu@amazon.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/sock.h | 1 + net/core/sock.c | 17 ++++++++++++++--- net/netlink/diag.c | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 2f35b82a123f..1bbdddcf6154 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2124,6 +2124,7 @@ static inline void sock_graft(struct sock *sk, struct socket *parent) } kuid_t sock_i_uid(struct sock *sk); +unsigned long __sock_i_ino(struct sock *sk); unsigned long sock_i_ino(struct sock *sk); static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk) diff --git a/net/core/sock.c b/net/core/sock.c index b021cb9c95ef..0c1baa5517f1 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2542,13 +2542,24 @@ kuid_t sock_i_uid(struct sock *sk) } EXPORT_SYMBOL(sock_i_uid); -unsigned long sock_i_ino(struct sock *sk) +unsigned long __sock_i_ino(struct sock *sk) { unsigned long ino; - read_lock_bh(&sk->sk_callback_lock); + read_lock(&sk->sk_callback_lock); ino = sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_ino : 0; - read_unlock_bh(&sk->sk_callback_lock); + read_unlock(&sk->sk_callback_lock); + return ino; +} +EXPORT_SYMBOL(__sock_i_ino); + +unsigned long sock_i_ino(struct sock *sk) +{ + unsigned long ino; + + local_bh_disable(); + ino = __sock_i_ino(sk); + local_bh_enable(); return ino; } EXPORT_SYMBOL(sock_i_ino); diff --git a/net/netlink/diag.c b/net/netlink/diag.c index 4143b2ea4195..e4f21b1067bc 100644 --- a/net/netlink/diag.c +++ b/net/netlink/diag.c @@ -168,7 +168,7 @@ mc_list: NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - sock_i_ino(sk)) < 0) { + __sock_i_ino(sk)) < 0) { ret = 1; break; } -- cgit 1.4.1 From 064e33b3591ee43f07776ce64f8a027e8a96f60f Mon Sep 17 00:00:00 2001 From: Wesley Chalmers Date: Wed, 10 Jun 2020 11:49:16 -0400 Subject: drm/amd/display: Add logging for display MALL refresh setting [ Upstream commit cd8f067a46d34dee3188da184912ae3d64d98444 ] [WHY] Add log entry for when display refresh from MALL settings are sent to SMU. Fixes: 1664641ea946 ("drm/amd/display: Add logger for SMU msg") Signed-off-by: Wesley Chalmers Acked-by: Aurabindo Pillai Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr_smu_msg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr_smu_msg.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr_smu_msg.c index 1fbf1c105dc1..bdbf18306698 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr_smu_msg.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr_smu_msg.c @@ -312,6 +312,9 @@ void dcn30_smu_set_display_refresh_from_mall(struct clk_mgr_internal *clk_mgr, b /* bits 8:7 for cache timer scale, bits 6:1 for cache timer delay, bit 0 = 1 for enable, = 0 for disable */ uint32_t param = (cache_timer_scale << 7) | (cache_timer_delay << 1) | (enable ? 1 : 0); + smu_print("SMU Set display refresh from mall: enable = %d, cache_timer_delay = %d, cache_timer_scale = %d\n", + enable, cache_timer_delay, cache_timer_scale); + dcn30_smu_send_msg_with_param(clk_mgr, DALSMC_MSG_SetDisplayRefreshFromMall, param, NULL); } -- cgit 1.4.1 From cb86b0e3d9d38ba351dd10caef483529653dd481 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Thu, 13 Apr 2023 08:12:28 -0700 Subject: radeon: avoid double free in ci_dpm_init() [ Upstream commit 20c3dffdccbd494e0dd631d1660aeecbff6775f2 ] Several calls to ci_dpm_fini() will attempt to free resources that either have been freed before or haven't been allocated yet. This may lead to undefined or dangerous behaviour. For instance, if r600_parse_extended_power_table() fails, it might call r600_free_extended_power_table() as will ci_dpm_fini() later during error handling. Fix this by only freeing pointers to objects previously allocated. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: cc8dbbb4f62a ("drm/radeon: add dpm support for CI dGPUs (v2)") Co-developed-by: Natalia Petrova Signed-off-by: Nikita Zhandarovich Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/radeon/ci_dpm.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 8ef25ab305ae..b8f4dac68d85 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -5517,6 +5517,7 @@ static int ci_parse_power_table(struct radeon_device *rdev) u8 frev, crev; u8 *power_state_offset; struct ci_ps *ps; + int ret; if (!atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset)) @@ -5546,11 +5547,15 @@ static int ci_parse_power_table(struct radeon_device *rdev) non_clock_array_index = power_state->v2.nonClockInfoIndex; non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) &non_clock_info_array->nonClockInfo[non_clock_array_index]; - if (!rdev->pm.power_state[i].clock_info) - return -EINVAL; + if (!rdev->pm.power_state[i].clock_info) { + ret = -EINVAL; + goto err_free_ps; + } ps = kzalloc(sizeof(struct ci_ps), GFP_KERNEL); - if (ps == NULL) - return -ENOMEM; + if (ps == NULL) { + ret = -ENOMEM; + goto err_free_ps; + } rdev->pm.dpm.ps[i].ps_priv = ps; ci_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], non_clock_info, @@ -5590,6 +5595,12 @@ static int ci_parse_power_table(struct radeon_device *rdev) } return 0; + +err_free_ps: + for (i = 0; i < rdev->pm.dpm.num_ps; i++) + kfree(rdev->pm.dpm.ps[i].ps_priv); + kfree(rdev->pm.dpm.ps); + return ret; } static int ci_get_vbios_boot_values(struct radeon_device *rdev, @@ -5678,25 +5689,26 @@ int ci_dpm_init(struct radeon_device *rdev) ret = ci_get_vbios_boot_values(rdev, &pi->vbios_boot_state); if (ret) { - ci_dpm_fini(rdev); + kfree(rdev->pm.dpm.priv); return ret; } ret = r600_get_platform_caps(rdev); if (ret) { - ci_dpm_fini(rdev); + kfree(rdev->pm.dpm.priv); return ret; } ret = r600_parse_extended_power_table(rdev); if (ret) { - ci_dpm_fini(rdev); + kfree(rdev->pm.dpm.priv); return ret; } ret = ci_parse_power_table(rdev); if (ret) { - ci_dpm_fini(rdev); + kfree(rdev->pm.dpm.priv); + r600_free_extended_power_table(rdev); return ret; } -- cgit 1.4.1 From 9fbe61e3c245fd16d86b2383499458a229c0cd22 Mon Sep 17 00:00:00 2001 From: Nicholas Kazlauskas Date: Thu, 2 May 2019 13:21:48 -0400 Subject: drm/amd/display: Explicitly specify update type per plane info change [ Upstream commit 710cc1e7cd461446a9325c9bd1e9a54daa462952 ] [Why] The bit for flip addr is being set causing the determination for FAST vs MEDIUM to always return MEDIUM when plane info is provided as a surface update. This causes extreme stuttering for the typical atomic update path on Linux. [How] Don't use update_flags->raw for determining FAST vs MEDIUM. It's too fragile to changes like this. Explicitly specify the update type per update flag instead. It's not as clever as checking the bits itself but at least it's correct. Fixes: aa5fdb1ab5b6 ("drm/amd/display: Explicitly specify update type per plane info change") Reviewed-by: Rodrigo Siqueira Signed-off-by: Nicholas Kazlauskas Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/core/dc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 087a4838488b..b405f2e86927 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -2360,9 +2360,6 @@ static enum surface_update_type det_surface_update(const struct dc *dc, enum surface_update_type overall_type = UPDATE_TYPE_FAST; union surface_update_flags *update_flags = &u->surface->update_flags; - if (u->flip_addr) - update_flags->bits.addr_update = 1; - if (!is_surface_in_context(context, u->surface) || u->surface->force_full_update) { update_flags->raw = 0xFFFFFFFF; return UPDATE_TYPE_FULL; -- cgit 1.4.1 From 9d27705e3ce1b48c5ec58bf10d8aba3c36da859e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 16 Apr 2023 17:30:46 +0200 Subject: drm/bridge: it6505: Move a variable assignment behind a null pointer check in receive_timing_debugfs_show() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0be05a75de2916421e88e0d64b001984f54df0bd ] The address of a data structure member was determined before a corresponding null pointer check in the implementation of the function “receive_timing_debugfs_show”. Thus avoid the risk for undefined behaviour by moving the assignment for the variable “vid” behind the null pointer check. This issue was detected by using the Coccinelle software. Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver") Signed-off-by: Markus Elfring Link: https://patchwork.freedesktop.org/patch/msgid/fa69384f-1485-142b-c4ee-3df54ac68a89@web.de Reviewed-by: Robert Foss Signed-off-by: Robert Foss Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/ite-it6505.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 99123eec4551..292c4f6da04a 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -3085,7 +3085,7 @@ static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf, size_t len, loff_t *ppos) { struct it6505 *it6505 = file->private_data; - struct drm_display_mode *vid = &it6505->video_info; + struct drm_display_mode *vid; u8 read_buf[READ_BUFFER_SIZE]; u8 *str = read_buf, *end = read_buf + READ_BUFFER_SIZE; ssize_t ret, count; @@ -3094,6 +3094,7 @@ static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf, return -ENODEV; it6505_calc_video_info(it6505); + vid = &it6505->video_info; str += scnprintf(str, end - str, "---video timing---\n"); str += scnprintf(str, end - str, "PCLK:%d.%03dMHz\n", vid->clock / 1000, vid->clock % 1000); -- cgit 1.4.1 From df3b7e337d712e7730469a2f6ac5e87ebc053cee Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 1 May 2023 17:01:45 -0700 Subject: Input: drv260x - sleep between polling GO bit [ Upstream commit efef661dfa6bf8cbafe4cd6a97433fcef0118967 ] When doing the initial startup there's no need to poll without any delay and spam the I2C bus. Let's sleep 15ms between each attempt, which is the same time as used in the vendor driver. Fixes: 7132fe4f5687 ("Input: drv260x - add TI drv260x haptics driver") Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20230430-drv260x-improvements-v1-2-1fb28b4cc698@z3ntu.xyz Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin --- drivers/input/misc/drv260x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 0efe56f49aa9..1923924fdd44 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -435,6 +435,7 @@ static int drv260x_init(struct drv260x_data *haptics) } do { + usleep_range(15000, 15500); error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf); if (error) { dev_err(&haptics->client->dev, -- cgit 1.4.1 From 63f3bc83b1dff34841fe26c1a26b4b6c0d4d0d9e Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 4 May 2023 08:53:16 +0200 Subject: drm/bridge: ti-sn65dsi83: Fix enable error path [ Upstream commit 8a91b29f1f50ce7742cdbe5cf11d17f128511f3f ] If PLL locking failed, the regulator needs to be disabled again. Fixes: 5664e3c907e2 ("drm/bridge: ti-sn65dsi83: Add vcc supply regulator support") Signed-off-by: Alexander Stein Reviewed-by: Laurent Pinchart Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230504065316.2640739-1-alexander.stein@ew.tq-group.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 047c14ddbbf1..0635b824e368 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -478,6 +478,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, dev_err(ctx->dev, "failed to lock PLL, ret=%i\n", ret); /* On failure, disable PLL again and exit. */ regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00); + regulator_disable(ctx->vcc); return; } -- cgit 1.4.1 From c4cf126320bc4a5169e93f43b640bb0adc2621ef Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:26 +0200 Subject: drm/bridge: tc358768: always enable HS video mode [ Upstream commit 75a8aeac2573ab258c53676eba9b3796ea691988 ] Always enable HS video mode setting the TXMD bit, without this change no video output is present with DSI sinks that are setting MIPI_DSI_MODE_LPM flag (tested with LT8912B DSI-HDMI bridge). Previously the driver was enabling HS mode only when the DSI sink was not explicitly setting the MIPI_DSI_MODE_LPM, however this is not correct. The MIPI_DSI_MODE_LPM is supposed to indicate that the sink is willing to receive data in low power mode, however clearing the TC358768_DSI_CONTROL_TXMD bit will make the TC358768 send video in LP mode that is not the intended behavior. Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-2-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 4c4b77ce8aba..23a7a1206e90 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -867,8 +867,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) val = TC358768_DSI_CONFW_MODE_SET | TC358768_DSI_CONFW_ADDR_DSI_CONTROL; val |= (dsi_dev->lanes - 1) << 1; - if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_LPM)) - val |= TC358768_DSI_CONTROL_TXMD; + val |= TC358768_DSI_CONTROL_TXMD; if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) val |= TC358768_DSI_CONTROL_HSCKMD; -- cgit 1.4.1 From d2aad3c1e4008a0c3b18176c44e91e8eee70b7f5 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:27 +0200 Subject: drm/bridge: tc358768: fix PLL parameters computation [ Upstream commit 6a4020b4c63911977aaf8047f904a300d15de739 ] According to Toshiba documentation the PLL input clock after the divider should be not less than 4MHz, fix the PLL parameters computation accordingly. Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-3-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 23a7a1206e90..f961259dc85b 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -335,13 +335,17 @@ static int tc358768_calc_pll(struct tc358768_priv *priv, u32 fbd; for (fbd = 0; fbd < 512; ++fbd) { - u32 pll, diff; + u32 pll, diff, pll_in; pll = (u32)div_u64((u64)refclk * (fbd + 1), divisor); if (pll >= max_pll || pll < min_pll) continue; + pll_in = (u32)div_u64((u64)refclk, prd + 1); + if (pll_in < 4000000) + continue; + diff = max(pll, target_pll) - min(pll, target_pll); if (diff < best_diff) { -- cgit 1.4.1 From 9d56ec0b24bf39dd10a781fa09148d89f0f38d8d Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:28 +0200 Subject: drm/bridge: tc358768: fix PLL target frequency [ Upstream commit ffd2e4bbea626d565b9817312b0fcfb382fecb88 ] Correctly compute the PLL target frequency, the current formula works correctly only when the input bus width is 24bit, actually to properly compute the PLL target frequency what is relevant is the bits-per-pixel on the DSI link. No regression expected since the DSI format is currently hard-coded as MIPI_DSI_FMT_RGB888. Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-4-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index f961259dc85b..9bc726b79dd5 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -147,6 +147,7 @@ struct tc358768_priv { u32 pd_lines; /* number of Parallel Port Input Data Lines */ u32 dsi_lanes; /* number of DSI Lanes */ + u32 dsi_bpp; /* number of Bits Per Pixel over DSI */ /* Parameters for PLL programming */ u32 fbd; /* PLL feedback divider */ @@ -285,12 +286,12 @@ static void tc358768_hw_disable(struct tc358768_priv *priv) static u32 tc358768_pll_to_pclk(struct tc358768_priv *priv, u32 pll_clk) { - return (u32)div_u64((u64)pll_clk * priv->dsi_lanes, priv->pd_lines); + return (u32)div_u64((u64)pll_clk * priv->dsi_lanes, priv->dsi_bpp); } static u32 tc358768_pclk_to_pll(struct tc358768_priv *priv, u32 pclk) { - return (u32)div_u64((u64)pclk * priv->pd_lines, priv->dsi_lanes); + return (u32)div_u64((u64)pclk * priv->dsi_bpp, priv->dsi_lanes); } static int tc358768_calc_pll(struct tc358768_priv *priv, @@ -427,6 +428,7 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, priv->output.panel = panel; priv->dsi_lanes = dev->lanes; + priv->dsi_bpp = mipi_dsi_pixel_format_to_bpp(dev->format); /* get input ep (port0/endpoint0) */ ret = -EINVAL; @@ -438,7 +440,7 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, } if (ret) - priv->pd_lines = mipi_dsi_pixel_format_to_bpp(dev->format); + priv->pd_lines = priv->dsi_bpp; drm_bridge_add(&priv->bridge); -- cgit 1.4.1 From 06dc491cf4e32b66b2ba8e4f90b24011676fa3b4 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:29 +0200 Subject: drm/bridge: tc358768: fix TCLK_ZEROCNT computation [ Upstream commit f9cf811374f42fca31ac34aaf59ee2ae72b89879 ] Correct computation of TCLK_ZEROCNT register. This register must be set to a value that ensure that (TCLK-PREPARECNT + TCLK-ZERO) > 300ns with the actual value of (TCLK-PREPARECNT + TCLK-ZERO) being (1 to 2) + (TCLK_ZEROCNT + 1)) x HSByteClkCycle + (PHY output delay) with PHY output delay being about (2 to 3) x MIPIBitClk cycle in the BitClk conversion. Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-5-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 9bc726b79dd5..765bdebbd06f 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -743,10 +743,10 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) /* 38ns < TCLK_PREPARE < 95ns */ val = tc358768_ns_to_cnt(65, dsibclk_nsk) - 1; - /* TCLK_PREPARE > 300ns */ - val2 = tc358768_ns_to_cnt(300 + tc358768_to_ns(3 * ui_nsk), - dsibclk_nsk); - val |= (val2 - tc358768_to_ns(phy_delay_nsk - dsibclk_nsk)) << 8; + /* TCLK_PREPARE + TCLK_ZERO > 300ns */ + val2 = tc358768_ns_to_cnt(300 - tc358768_to_ns(2 * ui_nsk), + dsibclk_nsk) - 2; + val |= val2 << 8; dev_dbg(priv->dev, "TCLK_HEADERCNT: 0x%x\n", val); tc358768_write(priv, TC358768_TCLK_HEADERCNT, val); -- cgit 1.4.1 From 587ba0805e4f4e57691041748427bb081e6a2dd6 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 30 Mar 2023 11:59:41 +0200 Subject: drm/bridge: tc358768: Add atomic_get_input_bus_fmts() implementation [ Upstream commit cec5ccef85bd0128cf895612de54a9d21d2015d0 ] Add atomic_get_input_bus_fmts() implementation, tc358768 has a parallel RGB input interface with the actual bus format depending on the amount of parallel input data lines. Without this change when the tc358768 is used with less than 24bit the color mapping is completely wrong. Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230330095941.428122-7-francesco@dolcini.it Stable-dep-of: ee18698e212b ("drm/bridge: tc358768: fix TCLK_TRAILCNT computation") Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 765bdebbd06f..8f7efb14ebdc 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -919,6 +920,44 @@ static void tc358768_bridge_enable(struct drm_bridge *bridge) } } +#define MAX_INPUT_SEL_FORMATS 1 + +static u32 * +tc358768_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct tc358768_priv *priv = bridge_to_tc358768(bridge); + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + switch (priv->pd_lines) { + case 16: + input_fmts[0] = MEDIA_BUS_FMT_RGB565_1X16; + break; + case 18: + input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X18; + break; + default: + case 24: + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + break; + }; + + *num_input_fmts = MAX_INPUT_SEL_FORMATS; + + return input_fmts; +} + static const struct drm_bridge_funcs tc358768_bridge_funcs = { .attach = tc358768_bridge_attach, .mode_valid = tc358768_bridge_mode_valid, @@ -926,6 +965,11 @@ static const struct drm_bridge_funcs tc358768_bridge_funcs = { .enable = tc358768_bridge_enable, .disable = tc358768_bridge_disable, .post_disable = tc358768_bridge_post_disable, + + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_get_input_bus_fmts = tc358768_atomic_get_input_bus_fmts, }; static const struct drm_bridge_timings default_tc358768_timings = { -- cgit 1.4.1 From 2545a8d06ad8c38428e86a41ab02bfd3eebba5d2 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:30 +0200 Subject: drm/bridge: tc358768: fix TCLK_TRAILCNT computation [ Upstream commit ee18698e212b1659dd0850d7e2ae0f22e16ed3d3 ] Correct computation of TCLK_TRAILCNT register. The driver does not implement non-continuous clock mode, so the actual value doesn't make a practical difference yet. However this change also ensures that the value does not write to reserved registers bits in case of under/overflow. This register must be set to a value that ensures that TCLK-TRAIL > 60ns and TEOT <= (105 ns + 12 x UI) with the actual value of TCLK-TRAIL being (TCLK_TRAILCNT + (1 to 2)) xHSByteClkCycle + (2 + (1 to 2)) * HSBYTECLKCycle - (PHY output delay) with PHY output delay being about (2 to 3) x MIPIBitClk cycle in the BitClk conversion. Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-2-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-3-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-4-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-5-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-2-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-3-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-4-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-5-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-2-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-3-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-4-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-5-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-2-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-3-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-4-francesco@dolcini.it Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-5-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 8f7efb14ebdc..822de6e356a2 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -640,6 +641,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) struct mipi_dsi_device *dsi_dev = priv->output.dev; unsigned long mode_flags = dsi_dev->mode_flags; u32 val, val2, lptxcnt, hact, data_type; + s32 raw_val; const struct drm_display_mode *mode; u32 dsibclk_nsk, dsiclk_nsk, ui_nsk, phy_delay_nsk; u32 dsiclk, dsibclk, video_start; @@ -751,9 +753,9 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) dev_dbg(priv->dev, "TCLK_HEADERCNT: 0x%x\n", val); tc358768_write(priv, TC358768_TCLK_HEADERCNT, val); - /* TCLK_TRAIL > 60ns + 3*UI */ - val = 60 + tc358768_to_ns(3 * ui_nsk); - val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 5; + /* TCLK_TRAIL > 60ns AND TEOT <= 105 ns + 12*UI */ + raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(2 * ui_nsk), dsibclk_nsk) - 5; + val = clamp(raw_val, 0, 127); dev_dbg(priv->dev, "TCLK_TRAILCNT: 0x%x\n", val); tc358768_write(priv, TC358768_TCLK_TRAILCNT, val); -- cgit 1.4.1 From 7b19315737020def8b004b58683494879411a716 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:31 +0200 Subject: drm/bridge: tc358768: fix THS_ZEROCNT computation [ Upstream commit 77a089328da791118af9692543a5eedc79eb5fd4 ] Correct computation of THS_ZEROCNT register. This register must be set to a value that ensure that THS_PREPARE + THS_ZERO > 145ns + 10*UI with the actual value of (THS_PREPARE + THS_ZERO) being ((1 to 2) + 1 + (TCLK_ZEROCNT + 1) + (3 to 4)) x ByteClk cycle + + HSByteClk x (2 + (1 to 2)) + (PHY delay) with PHY delay being about (8 + (5 to 6)) x MIPIBitClk cycle in the BitClk conversion. Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-7-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 822de6e356a2..014585b9582b 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -762,9 +762,10 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) /* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */ val = 50 + tc358768_to_ns(4 * ui_nsk); val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1; - /* THS_ZERO > 145ns + 10*UI */ - val2 = tc358768_ns_to_cnt(145 - tc358768_to_ns(ui_nsk), dsibclk_nsk); - val |= (val2 - tc358768_to_ns(phy_delay_nsk)) << 8; + /* THS_PREPARE + THS_ZERO > 145ns + 10*UI */ + raw_val = tc358768_ns_to_cnt(145 - tc358768_to_ns(3 * ui_nsk), dsibclk_nsk) - 10; + val2 = clamp(raw_val, 0, 127); + val |= val2 << 8; dev_dbg(priv->dev, "THS_HEADERCNT: 0x%x\n", val); tc358768_write(priv, TC358768_THS_HEADERCNT, val); -- cgit 1.4.1 From 010f68aecde491b7037fa16ac14863fd2e79cbee Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:32 +0200 Subject: drm/bridge: tc358768: fix TXTAGOCNT computation [ Upstream commit 3666aad8185af8d0ce164fd3c4974235417d6d0b ] Correct computation of TXTAGOCNT register. This register must be set to a value that ensure that the TTA-GO period = (4 x TLPX) with the actual value of TTA-GO being 4 x (TXTAGOCNT + 1) x (HSByteClk cycle) Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-8-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 014585b9582b..ab408f3407ba 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -797,7 +797,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) /* TXTAGOCNT[26:16] RXTASURECNT[10:0] */ val = tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk * 4); - val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1; + val = tc358768_ns_to_cnt(val, dsibclk_nsk) / 4 - 1; val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk), dsibclk_nsk) - 2; val = val << 16 | val2; -- cgit 1.4.1 From 8e739c8c6efb33dfa179d2e2cfc06a3a5f5af7e9 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Thu, 27 Apr 2023 16:29:33 +0200 Subject: drm/bridge: tc358768: fix THS_TRAILCNT computation [ Upstream commit bac7842cd179572e8e0fc2d7b5254e40c6e9e057 ] Correct computation of THS_TRAILCNT register. This register must be set to a value that ensure that THS_TRAIL > 60 ns + 4 x UI and THS_TRAIL > 8 x UI and THS_TRAIL < TEOT with TEOT = 105 ns + (12 x UI) with the actual value of THS_TRAIL being (1 + THS_TRAILCNT) x ByteClk cycle + ((1 to 2) + 2) xHSBYTECLK cycle + - (PHY output delay) with PHY output delay being about (8 + (5 to 6)) x MIPIBitClk cycle in the BitClk conversion. Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver") Signed-off-by: Francesco Dolcini Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230427142934.55435-9-francesco@dolcini.it Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358768.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index ab408f3407ba..2d0ac9987b58 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -781,9 +781,10 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) dev_dbg(priv->dev, "TCLK_POSTCNT: 0x%x\n", val); tc358768_write(priv, TC358768_TCLK_POSTCNT, val); - /* 60ns + 4*UI < THS_PREPARE < 105ns + 12*UI */ - val = tc358768_ns_to_cnt(60 + tc358768_to_ns(15 * ui_nsk), - dsibclk_nsk) - 5; + /* max(60ns + 4*UI, 8*UI) < THS_TRAILCNT < 105ns + 12*UI */ + raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(18 * ui_nsk), + dsibclk_nsk) - 4; + val = clamp(raw_val, 0, 15); dev_dbg(priv->dev, "THS_TRAILCNT: 0x%x\n", val); tc358768_write(priv, TC358768_THS_TRAILCNT, val); -- cgit 1.4.1 From 2dc8b685d99c3e3c118c4bb61e86c527c364bc6f Mon Sep 17 00:00:00 2001 From: Luc Ma Date: Mon, 8 May 2023 08:09:16 +0800 Subject: drm/vram-helper: fix function names in vram helper doc [ Upstream commit b8e392245105b50706f18418054821e71e637288 ] Refer to drmm_vram_helper_init() instead of the non-existent drmm_vram_helper_alloc_mm(). Fixes: a5f23a72355d ("drm/vram-helper: Managed vram helpers") Signed-off-by: Luc Ma Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/64583db2.630a0220.eb75d.8f51@mx.google.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gem_vram_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 125160b534be..928e08e0b5b8 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -44,7 +44,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * the frame's scanout buffer or the cursor image. If there's no more space * left in VRAM, inactive GEM objects can be moved to system memory. * - * To initialize the VRAM helper library call drmm_vram_helper_alloc_mm(). + * To initialize the VRAM helper library call drmm_vram_helper_init(). * The function allocates and initializes an instance of &struct drm_vram_mm * in &struct drm_device.vram_mm . Use &DRM_GEM_VRAM_DRIVER to initialize * &struct drm_driver and &DRM_VRAM_MM_FILE_OPERATIONS to initialize @@ -72,7 +72,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * // setup device, vram base and size * // ... * - * ret = drmm_vram_helper_alloc_mm(dev, vram_base, vram_size); + * ret = drmm_vram_helper_init(dev, vram_base, vram_size); * if (ret) * return ret; * return 0; @@ -85,7 +85,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * to userspace. * * You don't have to clean up the instance of VRAM MM. - * drmm_vram_helper_alloc_mm() is a managed interface that installs a + * drmm_vram_helper_init() is a managed interface that installs a * clean-up handler to run during the DRM device's release. * * For drawing or scanout operations, rsp. buffer objects have to be pinned -- cgit 1.4.1 From 2b55a985727833f37c39911f34096b3fdf2a367d Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 3 May 2023 14:28:30 +0200 Subject: ARM: dts: BCM5301X: Drop "clock-names" from the SPI node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d3c8e2c5757153bbfad70019ec1decbca86f3def ] There is no such property in the SPI controller binding documentation. Also Linux driver doesn't look for it. This fixes: arch/arm/boot/dts/bcm4708-asus-rt-ac56u.dtb: spi@18029200: Unevaluated properties are not allowed ('clock-names' was unexpected) From schema: Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.yaml Signed-off-by: Rafał Miłecki Link: https://lore.kernel.org/r/20230503122830.3200-1-zajec5@gmail.com Signed-off-by: Florian Fainelli Signed-off-by: Sasha Levin --- arch/arm/boot/dts/bcm5301x.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/boot/dts/bcm5301x.dtsi b/arch/arm/boot/dts/bcm5301x.dtsi index 5fc1b847f4aa..787a0dd8216b 100644 --- a/arch/arm/boot/dts/bcm5301x.dtsi +++ b/arch/arm/boot/dts/bcm5301x.dtsi @@ -542,7 +542,6 @@ "spi_lr_session_done", "spi_lr_overread"; clocks = <&iprocmed>; - clock-names = "iprocmed"; num-cs = <2>; #address-cells = <1>; #size-cells = <0>; -- cgit 1.4.1 From 5cf97c2df27f95af2d3f9251dd9e6fbe47c51115 Mon Sep 17 00:00:00 2001 From: "hfdevel@gmx.net" Date: Fri, 7 Apr 2023 16:36:25 +0200 Subject: ARM: dts: meson8b: correct uart_B and uart_C clock references [ Upstream commit d542ce8d4769cdef6a7bc3437e59cfed9c68f0e4 ] With the current device tree for meson8b, uarts B (e.g. available on pins 8/10 on Odroid-C1) and C (pins 3/5 on Odroid-C1) do not work, because they are relying on incorrect clocks. Change the references of pclk to the correct CLKID, to allow use of the two uarts. Fixes: 3375aa77135f ("ARM: dts: meson8b: Fix the UART device-tree schema validation") Signed-off-by: Hans-Frieder Vogt Reviewed-by: Martin Blumenstingl Link: https://lore.kernel.org/r/trinity-bf20bcb9-790b-4ab9-99e3-0831ef8257f4-1680878185420@3c-app-gmx-bap55 Signed-off-by: Neil Armstrong Signed-off-by: Sasha Levin --- arch/arm/boot/dts/meson8b.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/meson8b.dtsi b/arch/arm/boot/dts/meson8b.dtsi index cf9c04a61ba3..d3e0f085904d 100644 --- a/arch/arm/boot/dts/meson8b.dtsi +++ b/arch/arm/boot/dts/meson8b.dtsi @@ -737,13 +737,13 @@ &uart_B { compatible = "amlogic,meson8b-uart"; - clocks = <&xtal>, <&clkc CLKID_UART0>, <&clkc CLKID_CLK81>; + clocks = <&xtal>, <&clkc CLKID_UART1>, <&clkc CLKID_CLK81>; clock-names = "xtal", "pclk", "baud"; }; &uart_C { compatible = "amlogic,meson8b-uart"; - clocks = <&xtal>, <&clkc CLKID_UART0>, <&clkc CLKID_CLK81>; + clocks = <&xtal>, <&clkc CLKID_UART2>, <&clkc CLKID_CLK81>; clock-names = "xtal", "pclk", "baud"; }; -- cgit 1.4.1 From 50fb32197f60333666dac9ae23aa107d2c9ad8ba Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Mon, 22 May 2023 17:43:08 -0700 Subject: mm: call arch_swap_restore() from do_swap_page() commit 6dca4ac6fc91fd41ea4d6c4511838d37f4e0eab2 upstream. Commit c145e0b47c77 ("mm: streamline COW logic in do_swap_page()") moved the call to swap_free() before the call to set_pte_at(), which meant that the MTE tags could end up being freed before set_pte_at() had a chance to restore them. Fix it by adding a call to the arch_swap_restore() hook before the call to swap_free(). Link: https://lkml.kernel.org/r/20230523004312.1807357-2-pcc@google.com Link: https://linux-review.googlesource.com/id/I6470efa669e8bd2f841049b8c61020c510678965 Fixes: c145e0b47c77 ("mm: streamline COW logic in do_swap_page()") Signed-off-by: Peter Collingbourne Reported-by: Qun-wei Lin Closes: https://lore.kernel.org/all/5050805753ac469e8d727c797c2218a9d780d434.camel@mediatek.com/ Acked-by: David Hildenbrand Acked-by: "Huang, Ying" Reviewed-by: Steven Price Acked-by: Catalin Marinas Cc: [6.1+] Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mm/memory.c b/mm/memory.c index 77549434d13a..2083078cd061 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3980,6 +3980,13 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) } } + /* + * Some architectures may have to restore extra metadata to the page + * when reading from swap. This metadata may be indexed by swap entry + * so this must be called before swap_free(). + */ + arch_swap_restore(entry, folio); + /* * Remove the swap entry and conditionally try to free up the swapcache. * We're already holding a reference on the page but haven't mapped it -- cgit 1.4.1 From 548b67c0aad4a50988e4ad11e071c523e41e6358 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 14 Jan 2023 15:34:58 -0800 Subject: clk: vc5: Use `clamp()` to restrict PLL range [ Upstream commit 3ed741db04f58e8df0d46cec7ecfc4bfd075f047 ] The VCO frequency needs to be within a certain range and the driver enforces this. Make use of the clamp macro to implement this instead of open-coding it. This makes the code a bit shorter and also semanticly stronger. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20230114233500.3294789-1-lars@metafoo.de Reviewed-by: Luca Ceresoli Signed-off-by: Stephen Boyd Stable-dep-of: be3471c5bd9b ("clk: vc5: Fix .driver_data content in i2c_device_id") Signed-off-by: Sasha Levin --- drivers/clk/clk-versaclock5.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index 88689415aff9..abfe59ac4e48 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -450,10 +450,7 @@ static long vc5_pll_round_rate(struct clk_hw *hw, unsigned long rate, u32 div_int; u64 div_frc; - if (rate < VC5_PLL_VCO_MIN) - rate = VC5_PLL_VCO_MIN; - if (rate > VC5_PLL_VCO_MAX) - rate = VC5_PLL_VCO_MAX; + rate = clamp(rate, VC5_PLL_VCO_MIN, VC5_PLL_VCO_MAX); /* Determine integer part, which is 12 bit wide */ div_int = rate / *parent_rate; -- cgit 1.4.1 From 4db655d1b25dc3c838bfc88dd91509cc88678950 Mon Sep 17 00:00:00 2001 From: Liu Shixin Date: Tue, 4 Jul 2023 18:19:42 +0800 Subject: bootmem: remove the vmemmap pages from kmemleak in free_bootmem_page commit 028725e73375a1ff080bbdf9fb503306d0116f28 upstream. commit dd0ff4d12dd2 ("bootmem: remove the vmemmap pages from kmemleak in put_page_bootmem") fix an overlaps existing problem of kmemleak. But the problem still existed when HAVE_BOOTMEM_INFO_NODE is disabled, because in this case, free_bootmem_page() will call free_reserved_page() directly. Fix the problem by adding kmemleak_free_part() in free_bootmem_page() when HAVE_BOOTMEM_INFO_NODE is disabled. Link: https://lkml.kernel.org/r/20230704101942.2819426-1-liushixin2@huawei.com Fixes: f41f2ed43ca5 ("mm: hugetlb: free the vmemmap pages associated with each HugeTLB page") Signed-off-by: Liu Shixin Acked-by: Muchun Song Cc: Matthew Wilcox Cc: Mike Kravetz Cc: Oscar Salvador Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/bootmem_info.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/bootmem_info.h b/include/linux/bootmem_info.h index cc35d010fa94..e1a3c9c9754c 100644 --- a/include/linux/bootmem_info.h +++ b/include/linux/bootmem_info.h @@ -3,6 +3,7 @@ #define __LINUX_BOOTMEM_INFO_H #include +#include /* * Types for free bootmem stored in page->lru.next. These have to be in @@ -59,6 +60,7 @@ static inline void get_page_bootmem(unsigned long info, struct page *page, static inline void free_bootmem_page(struct page *page) { + kmemleak_free_part(page_to_virt(page), PAGE_SIZE); free_reserved_page(page); } #endif -- cgit 1.4.1 From 53b5b4d1a80e5d691dcb4f2db086c9419f4c12ac Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 7 May 2023 15:39:04 +0200 Subject: clk: vc5: Fix .driver_data content in i2c_device_id [ Upstream commit be3471c5bd9b921c9adfab7948e8021611639164 ] The .driver_data content in i2c_device_id table must match the .data content in of_device_id table, else device_get_match_data() would return bogus value on i2c_device_id match. Align the two tables. The i2c_device_id table is now converted from of_device_id using 's@.compatible = "idt,\([^"]\+"\), .data = \(.*\)@"\1, .driver_data = (kernel_ulong_t)\2@' Fixes: 9adddb01ce5f ("clk: vc5: Add structure to describe particular chip features") Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20230507133906.15061-1-marek.vasut+renesas@mailbox.org Reviewed-by: Luca Ceresoli Reviewed-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-versaclock5.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index abfe59ac4e48..5f2c9093c5b9 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -1255,13 +1255,13 @@ static const struct vc5_chip_info idt_5p49v6975_info = { }; static const struct i2c_device_id vc5_id[] = { - { "5p49v5923", .driver_data = IDT_VC5_5P49V5923 }, - { "5p49v5925", .driver_data = IDT_VC5_5P49V5925 }, - { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 }, - { "5p49v5935", .driver_data = IDT_VC5_5P49V5935 }, - { "5p49v6901", .driver_data = IDT_VC6_5P49V6901 }, - { "5p49v6965", .driver_data = IDT_VC6_5P49V6965 }, - { "5p49v6975", .driver_data = IDT_VC6_5P49V6975 }, + { "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info }, + { "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info }, + { "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info }, + { "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info }, + { "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info }, + { "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info }, + { "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info }, { } }; MODULE_DEVICE_TABLE(i2c, vc5_id); -- cgit 1.4.1 From 6014e7422c8d49f20c69d831f1b5d07079125da3 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 7 May 2023 15:39:05 +0200 Subject: clk: vc7: Fix .driver_data content in i2c_device_id [ Upstream commit b5e10beeafaa3266559c582dde7534ae3fe8cefb ] The .driver_data content in i2c_device_id table must match the .data content in of_device_id table, else device_get_match_data() would return bogus value on i2c_device_id match. Align the two tables. The i2c_device_id table is now converted from of_device_id using 's@.compatible = "renesas,\([^"]\+"\), .data = \(.*\)@"\1, .driver_data = (kernel_ulong_t)\2@' Fixes: 48c5e98fedd9 ("clk: Renesas versaclock7 ccf device driver") Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20230507133906.15061-2-marek.vasut+renesas@mailbox.org Reviewed-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-versaclock7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-versaclock7.c b/drivers/clk/clk-versaclock7.c index 8e4f86e852aa..0ae191f50b4b 100644 --- a/drivers/clk/clk-versaclock7.c +++ b/drivers/clk/clk-versaclock7.c @@ -1282,7 +1282,7 @@ static const struct regmap_config vc7_regmap_config = { }; static const struct i2c_device_id vc7_i2c_id[] = { - { "rc21008a", VC7_RC21008A }, + { "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info }, {} }; MODULE_DEVICE_TABLE(i2c, vc7_i2c_id); -- cgit 1.4.1 From 3134cc51e996132ad698fda65522e4230659131e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 7 May 2023 15:39:06 +0200 Subject: clk: rs9: Fix .driver_data content in i2c_device_id [ Upstream commit ad527ca87e4ea42d7baad2ce710b44069287931b ] The .driver_data content in i2c_device_id table must match the .data content in of_device_id table, else device_get_match_data() would return bogus value on i2c_device_id match. Align the two tables. The i2c_device_id table is now converted from of_device_id using 's@.compatible = "renesas,\([^"]\+"\), .data = \(.*\)@"\1, .driver_data = (kernel_ulong_t)\2@' Fixes: 892e0ddea1aa ("clk: rs9: Add Renesas 9-series PCIe clock generator driver") Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20230507133906.15061-3-marek.vasut+renesas@mailbox.org Reviewed-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-renesas-pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-renesas-pcie.c b/drivers/clk/clk-renesas-pcie.c index 3e98a16eba6b..35b2519f1696 100644 --- a/drivers/clk/clk-renesas-pcie.c +++ b/drivers/clk/clk-renesas-pcie.c @@ -353,7 +353,7 @@ static const struct rs9_chip_info renesas_9fgv0241_info = { }; static const struct i2c_device_id rs9_id[] = { - { "9fgv0241", .driver_data = RENESAS_9FGV0241 }, + { "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info }, { } }; MODULE_DEVICE_TABLE(i2c, rs9_id); -- cgit 1.4.1 From ac96a15163f5158d1e6a38b4743b9ea7eaff71c7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 10 May 2023 17:27:55 -0700 Subject: Input: adxl34x - do not hardcode interrupt trigger type [ Upstream commit e96220bce5176ed2309f77f061dcc0430b82b25e ] Instead of hardcoding IRQ trigger type to IRQF_TRIGGER_HIGH, let's respect the settings specified in the firmware description. Fixes: e27c729219ad ("Input: add driver for ADXL345/346 Digital Accelerometers") Signed-off-by: Marek Vasut Acked-by: Michael Hennerich Link: https://lore.kernel.org/r/20230509203555.549158-1-marex@denx.de Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin --- drivers/input/misc/adxl34x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index a4af314392a9..69e359ff5180 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -811,8 +811,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, AC_WRITE(ac, POWER_CTL, 0); err = request_threaded_irq(ac->irq, NULL, adxl34x_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - dev_name(dev), ac); + IRQF_ONESHOT, dev_name(dev), ac); if (err) { dev_err(dev, "irq %d busy?\n", ac->irq); goto err_free_mem; -- cgit 1.4.1 From aeca0e1c33748d438f5901f334a0396c5442db54 Mon Sep 17 00:00:00 2001 From: XuDong Liu Date: Sun, 30 Apr 2023 19:23:46 +0800 Subject: drm: sun4i_tcon: use devm_clk_get_enabled in `sun4i_tcon_init_clocks` [ Upstream commit 123ee07ba5b7123e0ce0e0f9d64938026c16a2ce ] Smatch reports: drivers/gpu/drm/sun4i/sun4i_tcon.c:805 sun4i_tcon_init_clocks() warn: 'tcon->clk' from clk_prepare_enable() not released on lines: 792,801. In the function sun4i_tcon_init_clocks(), tcon->clk and tcon->sclk0 are not disabled in the error handling, which affects the release of these variable. Although sun4i_tcon_bind(), which calls sun4i_tcon_init_clocks(), use sun4i_tcon_free_clocks to disable the variables mentioned, but the error handling branch of sun4i_tcon_init_clocks() ignores the required disable process. To fix this issue, use the devm_clk_get_enabled to automatically balance enable and disabled calls. As original implementation use sun4i_tcon_free_clocks() to disable clk explicitly, we delete the related calls and error handling that are no longer needed. Fixes: 9026e0d122ac ("drm: Add Allwinner A10 Display Engine support") Fixes: b14e945bda8a ("drm/sun4i: tcon: Prepare and enable TCON channel 0 clock at init") Fixes: 8e9240472522 ("drm/sun4i: support TCONs without channel 1") Fixes: 34d698f6e349 ("drm/sun4i: Add has_channel_0 TCON quirk") Signed-off-by: XuDong Liu Reviewed-by: Dongliang Mu Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20230430112347.4689-1-m202071377@hust.edu.cn Signed-off-by: Sasha Levin --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 523a6d787921..936796851ffd 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -778,21 +778,19 @@ static irqreturn_t sun4i_tcon_handler(int irq, void *private) static int sun4i_tcon_init_clocks(struct device *dev, struct sun4i_tcon *tcon) { - tcon->clk = devm_clk_get(dev, "ahb"); + tcon->clk = devm_clk_get_enabled(dev, "ahb"); if (IS_ERR(tcon->clk)) { dev_err(dev, "Couldn't get the TCON bus clock\n"); return PTR_ERR(tcon->clk); } - clk_prepare_enable(tcon->clk); if (tcon->quirks->has_channel_0) { - tcon->sclk0 = devm_clk_get(dev, "tcon-ch0"); + tcon->sclk0 = devm_clk_get_enabled(dev, "tcon-ch0"); if (IS_ERR(tcon->sclk0)) { dev_err(dev, "Couldn't get the TCON channel 0 clock\n"); return PTR_ERR(tcon->sclk0); } } - clk_prepare_enable(tcon->sclk0); if (tcon->quirks->has_channel_1) { tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); @@ -805,12 +803,6 @@ static int sun4i_tcon_init_clocks(struct device *dev, return 0; } -static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon) -{ - clk_disable_unprepare(tcon->sclk0); - clk_disable_unprepare(tcon->clk); -} - static int sun4i_tcon_init_irq(struct device *dev, struct sun4i_tcon *tcon) { @@ -1223,14 +1215,14 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, ret = sun4i_tcon_init_regmap(dev, tcon); if (ret) { dev_err(dev, "Couldn't init our TCON regmap\n"); - goto err_free_clocks; + goto err_assert_reset; } if (tcon->quirks->has_channel_0) { ret = sun4i_dclk_create(dev, tcon); if (ret) { dev_err(dev, "Couldn't create our TCON dot clock\n"); - goto err_free_clocks; + goto err_assert_reset; } } @@ -1293,8 +1285,6 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, err_free_dotclock: if (tcon->quirks->has_channel_0) sun4i_dclk_free(tcon); -err_free_clocks: - sun4i_tcon_free_clocks(tcon); err_assert_reset: reset_control_assert(tcon->lcd_rst); return ret; @@ -1308,7 +1298,6 @@ static void sun4i_tcon_unbind(struct device *dev, struct device *master, list_del(&tcon->list); if (tcon->quirks->has_channel_0) sun4i_dclk_free(tcon); - sun4i_tcon_free_clocks(tcon); } static const struct component_ops sun4i_tcon_ops = { -- cgit 1.4.1 From 39bdb97f87204273024938963952f7ed16d1bc4a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 7 May 2023 20:26:38 +0300 Subject: drm/panel: sharp-ls043t1le01: adjust mode settings [ Upstream commit dee23b2c9e3ff46d59c5d45e1436eceb878e7c9a ] Using current settings causes panel flickering on APQ8074 dragonboard. Adjust panel settings to follow the vendor-provided mode. This also enables MIPI_DSI_MODE_VIDEO_SYNC_PULSE, which is also specified by the vendor dtsi for the mentioned dragonboard. Fixes: ee0172383190 ("drm/panel: Add Sharp LS043T1LE01 MIPI DSI panel") Signed-off-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20230507172639.2320934-1-dmitry.baryshkov@linaro.org Signed-off-by: Sasha Levin --- drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c index d1ec80a3e3c7..ef148504cf24 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c @@ -192,15 +192,15 @@ static int sharp_nt_panel_enable(struct drm_panel *panel) } static const struct drm_display_mode default_mode = { - .clock = 41118, + .clock = (540 + 48 + 32 + 80) * (960 + 3 + 10 + 15) * 60 / 1000, .hdisplay = 540, .hsync_start = 540 + 48, - .hsync_end = 540 + 48 + 80, - .htotal = 540 + 48 + 80 + 32, + .hsync_end = 540 + 48 + 32, + .htotal = 540 + 48 + 32 + 80, .vdisplay = 960, .vsync_start = 960 + 3, - .vsync_end = 960 + 3 + 15, - .vtotal = 960 + 3 + 15 + 1, + .vsync_end = 960 + 3 + 10, + .vtotal = 960 + 3 + 10 + 15, }; static int sharp_nt_panel_get_modes(struct drm_panel *panel, @@ -280,6 +280,7 @@ static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi) dsi->lanes = 2; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_NO_EOT_PACKET; -- cgit 1.4.1 From 49fca83f6f3f0cafe5bf5b43e8ee81cf73c2d5e0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 21 Apr 2023 13:44:54 +0300 Subject: driver: soc: xilinx: use _safe loop iterator to avoid a use after free [ Upstream commit c58da0ba3e5c86e51e2c1557afaf6f71e00c4533 ] The hash_for_each_possible() loop dereferences "eve_data" to get the next item on the list. However the loop frees eve_data so it leads to a use after free. Use hash_for_each_possible_safe() instead. Fixes: c7fdb2404f66 ("drivers: soc: xilinx: add xilinx event management driver") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/761e0e4a-4caf-4a71-8f47-1c6ad908a848@kili.mountain Signed-off-by: Michal Simek Signed-off-by: Sasha Levin --- drivers/soc/xilinx/xlnx_event_manager.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/soc/xilinx/xlnx_event_manager.c b/drivers/soc/xilinx/xlnx_event_manager.c index c76381899ef4..f9d9b82b562d 100644 --- a/drivers/soc/xilinx/xlnx_event_manager.c +++ b/drivers/soc/xilinx/xlnx_event_manager.c @@ -192,11 +192,12 @@ static int xlnx_remove_cb_for_suspend(event_cb_func_t cb_fun) struct registered_event_data *eve_data; struct agent_cb *cb_pos; struct agent_cb *cb_next; + struct hlist_node *tmp; is_need_to_unregister = false; /* Check for existing entry in hash table for given cb_type */ - hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) { + hash_for_each_possible_safe(reg_driver_map, eve_data, tmp, hentry, PM_INIT_SUSPEND_CB) { if (eve_data->cb_type == PM_INIT_SUSPEND_CB) { /* Delete the list of callback */ list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) { @@ -228,11 +229,12 @@ static int xlnx_remove_cb_for_notify_event(const u32 node_id, const u32 event, u64 key = ((u64)node_id << 32U) | (u64)event; struct agent_cb *cb_pos; struct agent_cb *cb_next; + struct hlist_node *tmp; is_need_to_unregister = false; /* Check for existing entry in hash table for given key id */ - hash_for_each_possible(reg_driver_map, eve_data, hentry, key) { + hash_for_each_possible_safe(reg_driver_map, eve_data, tmp, hentry, key) { if (eve_data->key == key) { /* Delete the list of callback */ list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) { -- cgit 1.4.1 From 9d59f5f52cb44013e353c44ddf59a8b3bd77c4a7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 12 May 2023 12:32:59 -0500 Subject: ASoC: Intel: sof_sdw: remove SOF_SDW_TGL_HDMI for MeteorLake devices [ Upstream commit 0db94947c9d3da16aa31d152b7d26fab78b02cb9 ] Topologies support three HDMI links on MeteorLake devices only. Fixes: 18489174e4fb ("ASoC: intel: sof_sdw: add RT711 SDCA card for MTL platform") Signed-off-by: Bard Liao --- sound/soc/intel/boards/sof_sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d4f92bb5e29f..a37c85d30147 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -372,7 +372,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"), }, - .driver_data = (void *)(RT711_JD1 | SOF_SDW_TGL_HDMI), + .driver_data = (void *)(RT711_JD1), }, {} }; -- cgit 1.4.1 From fa4ee16e814a41a2afcaa85d443dfa2a923e9e38 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Tue, 18 Apr 2023 10:05:21 -0300 Subject: drm/vkms: isolate pixel conversion functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 322d716a3e8a74fb75cd0f657647be4df253fd2f ] Currently, the pixel conversion functions repeat the same loop to iterate the rows. Instead of repeating the same code for each pixel format, create a function to wrap the loop and isolate the pixel conversion functionality. Suggested-by: Arthur Grillo Signed-off-by: Maíra Canal Reviewed-by: Arthur Grillo Signed-off-by: Maíra Canal Link: https://patchwork.freedesktop.org/patch/msgid/20230418130525.128733-2-mcanal@igalia.com Stable-dep-of: ab87f558dcfb ("drm/vkms: Fix RGB565 pixel conversion") Signed-off-by: Sasha Levin --- drivers/gpu/drm/vkms/vkms_composer.c | 4 +- drivers/gpu/drm/vkms/vkms_drv.h | 4 +- drivers/gpu/drm/vkms/vkms_formats.c | 125 ++++++++++++++--------------------- drivers/gpu/drm/vkms/vkms_formats.h | 2 +- drivers/gpu/drm/vkms/vkms_plane.c | 2 +- 5 files changed, 56 insertions(+), 81 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 8e53fa80742b..80164e79af00 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -99,7 +99,7 @@ static void blend(struct vkms_writeback_job *wb, if (!check_y_limit(plane[i]->frame_info, y)) continue; - plane[i]->plane_read(stage_buffer, plane[i]->frame_info, y); + vkms_compose_row(stage_buffer, plane[i], y); pre_mul_alpha_blend(plane[i]->frame_info, stage_buffer, output_buffer); } @@ -118,7 +118,7 @@ static int check_format_funcs(struct vkms_crtc_state *crtc_state, u32 n_active_planes = crtc_state->num_active_planes; for (size_t i = 0; i < n_active_planes; i++) - if (!planes[i]->plane_read) + if (!planes[i]->pixel_read) return -1; if (active_wb && !active_wb->wb_write) diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 0a67b8073f7e..de4efc0a3bd0 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -56,8 +56,7 @@ struct vkms_writeback_job { struct vkms_plane_state { struct drm_shadow_plane_state base; struct vkms_frame_info *frame_info; - void (*plane_read)(struct line_buffer *buffer, - const struct vkms_frame_info *frame_info, int y); + void (*pixel_read)(u8 *src_buffer, struct pixel_argb_u16 *out_pixel); }; struct vkms_plane { @@ -155,6 +154,7 @@ int vkms_verify_crc_source(struct drm_crtc *crtc, const char *source_name, /* Composer Support */ void vkms_composer_worker(struct work_struct *work); void vkms_set_composer(struct vkms_output *out, bool enabled); +void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state *plane, int y); /* Writeback */ int vkms_enable_writeback_connector(struct vkms_device *vkmsdev); diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c index d4950688b3f1..8d948c73741e 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.c +++ b/drivers/gpu/drm/vkms/vkms_formats.c @@ -42,100 +42,75 @@ static void *get_packed_src_addr(const struct vkms_frame_info *frame_info, int y return packed_pixels_addr(frame_info, x_src, y_src); } -static void ARGB8888_to_argb_u16(struct line_buffer *stage_buffer, - const struct vkms_frame_info *frame_info, int y) +static void ARGB8888_to_argb_u16(u8 *src_pixels, struct pixel_argb_u16 *out_pixel) { - struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; - u8 *src_pixels = get_packed_src_addr(frame_info, y); - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - stage_buffer->n_pixels); - - for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { - /* - * The 257 is the "conversion ratio". This number is obtained by the - * (2^16 - 1) / (2^8 - 1) division. Which, in this case, tries to get - * the best color value in a pixel format with more possibilities. - * A similar idea applies to others RGB color conversions. - */ - out_pixels[x].a = (u16)src_pixels[3] * 257; - out_pixels[x].r = (u16)src_pixels[2] * 257; - out_pixels[x].g = (u16)src_pixels[1] * 257; - out_pixels[x].b = (u16)src_pixels[0] * 257; - } + /* + * The 257 is the "conversion ratio". This number is obtained by the + * (2^16 - 1) / (2^8 - 1) division. Which, in this case, tries to get + * the best color value in a pixel format with more possibilities. + * A similar idea applies to others RGB color conversions. + */ + out_pixel->a = (u16)src_pixels[3] * 257; + out_pixel->r = (u16)src_pixels[2] * 257; + out_pixel->g = (u16)src_pixels[1] * 257; + out_pixel->b = (u16)src_pixels[0] * 257; } -static void XRGB8888_to_argb_u16(struct line_buffer *stage_buffer, - const struct vkms_frame_info *frame_info, int y) +static void XRGB8888_to_argb_u16(u8 *src_pixels, struct pixel_argb_u16 *out_pixel) { - struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; - u8 *src_pixels = get_packed_src_addr(frame_info, y); - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - stage_buffer->n_pixels); - - for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { - out_pixels[x].a = (u16)0xffff; - out_pixels[x].r = (u16)src_pixels[2] * 257; - out_pixels[x].g = (u16)src_pixels[1] * 257; - out_pixels[x].b = (u16)src_pixels[0] * 257; - } + out_pixel->a = (u16)0xffff; + out_pixel->r = (u16)src_pixels[2] * 257; + out_pixel->g = (u16)src_pixels[1] * 257; + out_pixel->b = (u16)src_pixels[0] * 257; } -static void ARGB16161616_to_argb_u16(struct line_buffer *stage_buffer, - const struct vkms_frame_info *frame_info, - int y) +static void ARGB16161616_to_argb_u16(u8 *src_pixels, struct pixel_argb_u16 *out_pixel) { - struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; - u16 *src_pixels = get_packed_src_addr(frame_info, y); - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - stage_buffer->n_pixels); + u16 *pixels = (u16 *)src_pixels; - for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { - out_pixels[x].a = le16_to_cpu(src_pixels[3]); - out_pixels[x].r = le16_to_cpu(src_pixels[2]); - out_pixels[x].g = le16_to_cpu(src_pixels[1]); - out_pixels[x].b = le16_to_cpu(src_pixels[0]); - } + out_pixel->a = le16_to_cpu(pixels[3]); + out_pixel->r = le16_to_cpu(pixels[2]); + out_pixel->g = le16_to_cpu(pixels[1]); + out_pixel->b = le16_to_cpu(pixels[0]); } -static void XRGB16161616_to_argb_u16(struct line_buffer *stage_buffer, - const struct vkms_frame_info *frame_info, - int y) +static void XRGB16161616_to_argb_u16(u8 *src_pixels, struct pixel_argb_u16 *out_pixel) { - struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; - u16 *src_pixels = get_packed_src_addr(frame_info, y); - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - stage_buffer->n_pixels); + u16 *pixels = (u16 *)src_pixels; - for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { - out_pixels[x].a = (u16)0xffff; - out_pixels[x].r = le16_to_cpu(src_pixels[2]); - out_pixels[x].g = le16_to_cpu(src_pixels[1]); - out_pixels[x].b = le16_to_cpu(src_pixels[0]); - } + out_pixel->a = (u16)0xffff; + out_pixel->r = le16_to_cpu(pixels[2]); + out_pixel->g = le16_to_cpu(pixels[1]); + out_pixel->b = le16_to_cpu(pixels[0]); } -static void RGB565_to_argb_u16(struct line_buffer *stage_buffer, - const struct vkms_frame_info *frame_info, int y) +static void RGB565_to_argb_u16(u8 *src_pixels, struct pixel_argb_u16 *out_pixel) { - struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; - u16 *src_pixels = get_packed_src_addr(frame_info, y); - int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), - stage_buffer->n_pixels); + u16 *pixels = (u16 *)src_pixels; s64 fp_rb_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(31)); s64 fp_g_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(63)); - for (size_t x = 0; x < x_limit; x++, src_pixels++) { - u16 rgb_565 = le16_to_cpu(*src_pixels); - s64 fp_r = drm_int2fixp((rgb_565 >> 11) & 0x1f); - s64 fp_g = drm_int2fixp((rgb_565 >> 5) & 0x3f); - s64 fp_b = drm_int2fixp(rgb_565 & 0x1f); + u16 rgb_565 = le16_to_cpu(*pixels); + s64 fp_r = drm_int2fixp((rgb_565 >> 11) & 0x1f); + s64 fp_g = drm_int2fixp((rgb_565 >> 5) & 0x3f); + s64 fp_b = drm_int2fixp(rgb_565 & 0x1f); - out_pixels[x].a = (u16)0xffff; - out_pixels[x].r = drm_fixp2int(drm_fixp_mul(fp_r, fp_rb_ratio)); - out_pixels[x].g = drm_fixp2int(drm_fixp_mul(fp_g, fp_g_ratio)); - out_pixels[x].b = drm_fixp2int(drm_fixp_mul(fp_b, fp_rb_ratio)); - } + out_pixel->a = (u16)0xffff; + out_pixel->r = drm_fixp2int(drm_fixp_mul(fp_r, fp_rb_ratio)); + out_pixel->g = drm_fixp2int(drm_fixp_mul(fp_g, fp_g_ratio)); + out_pixel->b = drm_fixp2int(drm_fixp_mul(fp_b, fp_rb_ratio)); +} + +void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state *plane, int y) +{ + struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; + struct vkms_frame_info *frame_info = plane->frame_info; + u8 *src_pixels = get_packed_src_addr(frame_info, y); + int limit = min_t(size_t, drm_rect_width(&frame_info->dst), stage_buffer->n_pixels); + + for (size_t x = 0; x < limit; x++, src_pixels += frame_info->cpp) + plane->pixel_read(src_pixels, &out_pixels[x]); } /* @@ -249,7 +224,7 @@ static void argb_u16_to_RGB565(struct vkms_frame_info *frame_info, } } -void *get_frame_to_line_function(u32 format) +void *get_pixel_conversion_function(u32 format) { switch (format) { case DRM_FORMAT_ARGB8888: diff --git a/drivers/gpu/drm/vkms/vkms_formats.h b/drivers/gpu/drm/vkms/vkms_formats.h index 43b7c1979018..c5b113495d0c 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.h +++ b/drivers/gpu/drm/vkms/vkms_formats.h @@ -5,7 +5,7 @@ #include "vkms_drv.h" -void *get_frame_to_line_function(u32 format); +void *get_pixel_conversion_function(u32 format); void *get_line_to_frame_function(u32 format); diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index c3a845220e10..80e964589cec 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -123,7 +123,7 @@ static void vkms_plane_atomic_update(struct drm_plane *plane, frame_info->offset = fb->offsets[0]; frame_info->pitch = fb->pitches[0]; frame_info->cpp = fb->format->cpp[0]; - vkms_plane_state->plane_read = get_frame_to_line_function(fmt); + vkms_plane_state->pixel_read = get_pixel_conversion_function(fmt); } static int vkms_plane_atomic_check(struct drm_plane *plane, -- cgit 1.4.1 From 048b7168acf85cb856b0db1d0483584cfff3498f Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 12 May 2023 07:40:44 -0300 Subject: drm: Add fixed-point helper to get rounded integer values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8b25320887d7feac98875546ea0f521628b745bb ] Create a new fixed-point helper to allow us to return the rounded value of our fixed point value. [v2]: * Create the function drm_fixp2int_round() (Melissa Wen). [v3]: * Use drm_fixp2int() instead of shifting manually (Arthur Grillo). Signed-off-by: Maíra Canal Reviewed-by: Arthur Grillo Signed-off-by: Maíra Canal Link: https://patchwork.freedesktop.org/patch/msgid/20230512104044.65034-1-mcanal@igalia.com Stable-dep-of: ab87f558dcfb ("drm/vkms: Fix RGB565 pixel conversion") Signed-off-by: Sasha Levin --- include/drm/drm_fixed.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h index 553210c02ee0..03cb890690e8 100644 --- a/include/drm/drm_fixed.h +++ b/include/drm/drm_fixed.h @@ -70,6 +70,7 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) } #define DRM_FIXED_POINT 32 +#define DRM_FIXED_POINT_HALF 16 #define DRM_FIXED_ONE (1ULL << DRM_FIXED_POINT) #define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1) #define DRM_FIXED_DIGITS_MASK (~DRM_FIXED_DECIMAL_MASK) @@ -86,6 +87,11 @@ static inline int drm_fixp2int(s64 a) return ((s64)a) >> DRM_FIXED_POINT; } +static inline int drm_fixp2int_round(s64 a) +{ + return drm_fixp2int(a + (1 << (DRM_FIXED_POINT_HALF - 1))); +} + static inline int drm_fixp2int_ceil(s64 a) { if (a > 0) -- cgit 1.4.1 From 7a3c39e34cab4a991379e3781bea0d44d8fb0e3e Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 12 May 2023 07:40:45 -0300 Subject: drm/vkms: Fix RGB565 pixel conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ab87f558dcfb2562c3497e89600dec798a446665 ] Currently, the pixel conversion isn't rounding the fixed-point values before assigning it to the RGB coefficients, which is causing the IGT pixel-format tests to fail. So, use the drm_fixp2int_round() fixed-point helper to round the values when assigning it to the RGB coefficients. Tested with igt@kms_plane@pixel-format and igt@kms_plane@pixel-format-source-clamping. [v2]: * Use drm_fixp2int_round() to fix the pixel conversion instead of casting the values to s32 (Melissa Wen). Fixes: 89b03aeaef16 ("drm/vkms: fix 32bit compilation error by replacing macros") Signed-off-by: Maíra Canal Reviewed-by: Arthur Grillo Signed-off-by: Maíra Canal Link: https://patchwork.freedesktop.org/patch/msgid/20230512104044.65034-2-mcanal@igalia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/vkms/vkms_formats.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c index 8d948c73741e..b11342026485 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.c +++ b/drivers/gpu/drm/vkms/vkms_formats.c @@ -97,9 +97,9 @@ static void RGB565_to_argb_u16(u8 *src_pixels, struct pixel_argb_u16 *out_pixel) s64 fp_b = drm_int2fixp(rgb_565 & 0x1f); out_pixel->a = (u16)0xffff; - out_pixel->r = drm_fixp2int(drm_fixp_mul(fp_r, fp_rb_ratio)); - out_pixel->g = drm_fixp2int(drm_fixp_mul(fp_g, fp_g_ratio)); - out_pixel->b = drm_fixp2int(drm_fixp_mul(fp_b, fp_rb_ratio)); + out_pixel->r = drm_fixp2int_round(drm_fixp_mul(fp_r, fp_rb_ratio)); + out_pixel->g = drm_fixp2int_round(drm_fixp_mul(fp_g, fp_g_ratio)); + out_pixel->b = drm_fixp2int_round(drm_fixp_mul(fp_b, fp_rb_ratio)); } void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state *plane, int y) @@ -216,9 +216,9 @@ static void argb_u16_to_RGB565(struct vkms_frame_info *frame_info, s64 fp_g = drm_int2fixp(in_pixels[x].g); s64 fp_b = drm_int2fixp(in_pixels[x].b); - u16 r = drm_fixp2int(drm_fixp_div(fp_r, fp_rb_ratio)); - u16 g = drm_fixp2int(drm_fixp_div(fp_g, fp_g_ratio)); - u16 b = drm_fixp2int(drm_fixp_div(fp_b, fp_rb_ratio)); + u16 r = drm_fixp2int_round(drm_fixp_div(fp_r, fp_rb_ratio)); + u16 g = drm_fixp2int_round(drm_fixp_div(fp_g, fp_g_ratio)); + u16 b = drm_fixp2int_round(drm_fixp_div(fp_b, fp_rb_ratio)); *dst_pixels = cpu_to_le16(r << 11 | g << 5 | b); } -- cgit 1.4.1 From 46a8dff2103bee5d4d54745df6d0cff46a02a239 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 5 May 2023 23:37:29 +0200 Subject: ARM: dts: stm32: Move ethernet MAC EEPROM from SoM to carrier boards [ Upstream commit 9660efc2af37f3c12dc6e6a5511ad99e0addc297 ] The ethernet MAC EEPROM is not populated on the SoM itself, it has to be populated on each carrier board. Move the EEPROM into the correct place in DTs, i.e. the carrier board DTs. Add label to the EEPROM too. Fixes: 7e76f82acd9e1 ("ARM: dts: stm32: Split Avenger96 into DHCOR SoM and Avenger96 board") Signed-off-by: Marek Vasut Signed-off-by: Alexandre Torgue Signed-off-by: Sasha Levin --- arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi | 6 ++++++ arch/arm/boot/dts/stm32mp15xx-dhcor-drc-compact.dtsi | 6 ++++++ arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi | 6 ------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi index b6957cbdeff5..2c246ac64153 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi @@ -322,6 +322,12 @@ }; }; }; + + dh_mac_eeprom: eeprom@53 { + compatible = "atmel,24c02"; + reg = <0x53>; + pagesize = <16>; + }; }; <dc { diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcor-drc-compact.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcor-drc-compact.dtsi index 27477bb219de..bb4ac6c13cbd 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dhcor-drc-compact.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dhcor-drc-compact.dtsi @@ -192,6 +192,12 @@ reg = <0x50>; pagesize = <16>; }; + + dh_mac_eeprom: eeprom@53 { + compatible = "atmel,24c02"; + reg = <0x53>; + pagesize = <16>; + }; }; &sdmmc1 { /* MicroSD */ diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi index bb40fb46da81..bba19f21e527 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi @@ -213,12 +213,6 @@ status = "disabled"; }; }; - - eeprom@53 { - compatible = "atmel,24c02"; - reg = <0x53>; - pagesize = <16>; - }; }; &ipcc { -- cgit 1.4.1 From 13602e6132464dbbbf5e4c941a118f27921aa012 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 17 May 2023 10:04:16 +0300 Subject: bus: ti-sysc: Fix dispc quirk masking bool variables [ Upstream commit f620596fa347170852da499e778a5736d79a4b79 ] Fix warning drivers/bus/ti-sysc.c:1806 sysc_quirk_dispc() warn: masking a bool. While at it let's add a comment for what were doing to make the code a bit easier to follow. Fixes: 7324a7a0d5e2 ("bus: ti-sysc: Implement display subsystem reset quirk") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-omap/a8ec8a68-9c2c-4076-bf47-09fccce7659f@kili.mountain/ Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin --- drivers/bus/ti-sysc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 9a7d12332fad..cae078bffc71 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -1808,7 +1808,7 @@ static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset, if (!ddata->module_va) return -EIO; - /* DISP_CONTROL */ + /* DISP_CONTROL, shut down lcd and digit on disable if enabled */ val = sysc_read(ddata, dispc_offset + 0x40); lcd_en = val & lcd_en_mask; digit_en = val & digit_en_mask; @@ -1820,7 +1820,7 @@ static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset, else irq_mask |= BIT(2) | BIT(3); /* EVSYNC bits */ } - if (disable & (lcd_en | digit_en)) + if (disable && (lcd_en || digit_en)) sysc_write(ddata, dispc_offset + 0x40, val & ~(lcd_en_mask | digit_en_mask)); -- cgit 1.4.1 From 929b6c6e6a562556171411182277cc165d0afc38 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 21 Feb 2023 11:50:37 +0100 Subject: arm64: dts: microchip: sparx5: do not use PSCI on reference boards [ Upstream commit 70be83708c925b3f72c508e4756e48ad2330c830 ] PSCI is not implemented on SparX-5 at all, there is no ATF and U-boot that is shipped does not implement it as well. I have tried flashing the latest BSP 2022.12 U-boot which did not work. After contacting Microchip, they confirmed that there is no ATF for the SoC nor PSCI implementation which is unfortunate in 2023. So, disable PSCI as otherwise kernel crashes as soon as it tries probing PSCI with, and the crash is only visible if earlycon is used. Since PSCI is not implemented, switch core bringup to use spin-tables which are implemented in the vendor U-boot and actually work. Tested on PCB134 with eMMC (VSC5640EV). Fixes: 6694aee00a4b ("arm64: dts: sparx5: Add basic cpu support") Signed-off-by: Robert Marko Acked-by: Steen Hegelund Link: https://lore.kernel.org/r/20230221105039.316819-1-robert.marko@sartura.hr Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/microchip/sparx5.dtsi | 2 +- arch/arm64/boot/dts/microchip/sparx5_pcb_common.dtsi | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/microchip/sparx5.dtsi b/arch/arm64/boot/dts/microchip/sparx5.dtsi index 2dd5e38820b1..088d89801c27 100644 --- a/arch/arm64/boot/dts/microchip/sparx5.dtsi +++ b/arch/arm64/boot/dts/microchip/sparx5.dtsi @@ -61,7 +61,7 @@ interrupt-affinity = <&cpu0>, <&cpu1>; }; - psci { + psci: psci { compatible = "arm,psci-0.2"; method = "smc"; }; diff --git a/arch/arm64/boot/dts/microchip/sparx5_pcb_common.dtsi b/arch/arm64/boot/dts/microchip/sparx5_pcb_common.dtsi index 9d1a082de3e2..32bb76b3202a 100644 --- a/arch/arm64/boot/dts/microchip/sparx5_pcb_common.dtsi +++ b/arch/arm64/boot/dts/microchip/sparx5_pcb_common.dtsi @@ -6,6 +6,18 @@ /dts-v1/; #include "sparx5.dtsi" +&psci { + status = "disabled"; +}; + +&cpu0 { + enable-method = "spin-table"; +}; + +&cpu1 { + enable-method = "spin-table"; +}; + &uart0 { status = "okay"; }; -- cgit 1.4.1 From 3fc9637f37a763113df7ea81bad995b566bf1eb3 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 17 May 2023 14:21:06 +0200 Subject: drm/bridge: tc358767: Switch to devm MIPI-DSI helpers [ Upstream commit f47d6140b7a4c858d82d263e7577ff6fb5279a9c ] DSI device registering and attaching needs to be undone upon deregistration. This fixes module unload/load. Fixes: bbfd3190b656 ("drm/bridge: tc358767: Add DSI-to-DPI mode support") Signed-off-by: Alexander Stein Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230517122107.1766673-1-alexander.stein@ew.tq-group.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/tc358767.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index b9b681086fc4..7ef78283e3d3 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1890,7 +1890,7 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc) if (dsi_lanes < 0) return dsi_lanes; - dsi = mipi_dsi_device_register_full(host, &info); + dsi = devm_mipi_dsi_device_register_full(dev, host, &info); if (IS_ERR(dsi)) return dev_err_probe(dev, PTR_ERR(dsi), "failed to create dsi device\n"); @@ -1901,7 +1901,7 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "failed to attach dsi to host: %d\n", ret); return ret; -- cgit 1.4.1 From 08cc7cd2c2a29a2abf5bceb8f048c0734d3694ba Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Apr 2023 17:23:01 +0300 Subject: clk: imx: scu: use _safe list iterator to avoid a use after free [ Upstream commit 632c60ecd25dbacee54d5581fe3aeb834b57010a ] This loop is freeing "clk" so it needs to use list_for_each_entry_safe(). Otherwise it dereferences a freed variable to get the next item on the loop. Fixes: 77d8f3068c63 ("clk: imx: scu: add two cells binding support") Signed-off-by: Dan Carpenter Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/0793fbd1-d2b5-4ec2-9403-3c39343a3e2d@kili.mountain Signed-off-by: Abel Vesa Signed-off-by: Sasha Levin --- drivers/clk/imx/clk-scu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index 1e6870f3671f..db307890e4c1 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -707,11 +707,11 @@ struct clk_hw *imx_clk_scu_alloc_dev(const char *name, void imx_clk_scu_unregister(void) { - struct imx_scu_clk_node *clk; + struct imx_scu_clk_node *clk, *n; int i; for (i = 0; i < IMX_SC_R_LAST; i++) { - list_for_each_entry(clk, &imx_scu_clks[i], node) { + list_for_each_entry_safe(clk, n, &imx_scu_clks[i], node) { clk_hw_unregister(clk->hw); kfree(clk); } -- cgit 1.4.1 From 280e58d8b0e902556fa0c8dddc622ea9ea19af91 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Wed, 10 May 2023 07:35:37 -0700 Subject: hwmon: (f71882fg) prevent possible division by zero [ Upstream commit 0babf89c9cca7e074d6e59893e462e4886f481cc ] In the unlikely event that something goes wrong with the device and its registers, the fan_from_reg() function may return 0. This value will cause a division-by-zero error in the show_pwm() function. To prevent this, test the value of fan_from_reg(data->fan_full_speed[nr]) against 0 before performing the division. If the division-by-zero error is avoided, assign 0 to the val variable. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: df9ec2dae094 ("hwmon: (f71882fg) Reorder symbols to get rid of a few forward declarations") Signed-off-by: Nikita Zhandarovich Link: https://lore.kernel.org/r/20230510143537.145060-1-n.zhandarovich@fintech.ru Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/f71882fg.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 70121482a617..27207ec6f7fe 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1096,8 +1096,11 @@ static ssize_t show_pwm(struct device *dev, val = data->pwm[nr]; else { /* RPM mode */ - val = 255 * fan_from_reg(data->fan_target[nr]) - / fan_from_reg(data->fan_full_speed[nr]); + if (fan_from_reg(data->fan_full_speed[nr])) + val = 255 * fan_from_reg(data->fan_target[nr]) + / fan_from_reg(data->fan_full_speed[nr]); + else + val = 0; } mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", val); -- cgit 1.4.1 From b43b064498702f0429e090f543a677f5c37c5857 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Thu, 18 May 2023 23:48:11 -0700 Subject: RDMA/bnxt_re: Disable/kill tasklet only if it is enabled [ Upstream commit ab112ee7899d6171da5acd77a7ed7ae103f488de ] When the ulp hook to start the IRQ fails because the rings are not available, tasklets are not enabled. In this case when the driver is unloaded, driver calls CREQ tasklet_kill. This causes an indefinite hang as the tasklet is not enabled. Driver shouldn't call tasklet_kill if it is not enabled. So using the creq->requested and nq->requested flags to identify if both tasklets/irqs are registered. Checking this flag while scheduling the tasklet from ISR. Also, added a cleanup for disabling tasklet, in case request_irq fails during start_irq. Check for return value for bnxt_qplib_rcfw_start_irq and in case the bnxt_qplib_rcfw_start_irq fails, return bnxt_re_start_irq without attempting to start NQ IRQs. Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Link: https://lore.kernel.org/r/1684478897-12247-2-git-send-email-selvin.xavier@broadcom.com Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/main.c | 12 +++++++++--- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 16 ++++++++++------ drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 14 +++++++++----- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 8c0c80a8d338..1589b1105f2e 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -333,15 +333,21 @@ static void bnxt_re_start_irq(void *handle, struct bnxt_msix_entry *ent) for (indx = 0; indx < rdev->num_msix; indx++) rdev->msix_entries[indx].vector = ent[indx].vector; - bnxt_qplib_rcfw_start_irq(rcfw, msix_ent[BNXT_RE_AEQ_IDX].vector, - false); + rc = bnxt_qplib_rcfw_start_irq(rcfw, msix_ent[BNXT_RE_AEQ_IDX].vector, + false); + if (rc) { + ibdev_warn(&rdev->ibdev, "Failed to reinit CREQ\n"); + return; + } for (indx = BNXT_RE_NQ_IDX ; indx < rdev->num_msix; indx++) { nq = &rdev->nq[indx - 1]; rc = bnxt_qplib_nq_start_irq(nq, indx - 1, msix_ent[indx].vector, false); - if (rc) + if (rc) { ibdev_warn(&rdev->ibdev, "Failed to reinit NQ index %d\n", indx - 1); + return; + } } } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index ab2cc1c67f70..a143bd3580a2 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -405,6 +405,9 @@ static irqreturn_t bnxt_qplib_nq_irq(int irq, void *dev_instance) void bnxt_qplib_nq_stop_irq(struct bnxt_qplib_nq *nq, bool kill) { + if (!nq->requested) + return; + tasklet_disable(&nq->nq_tasklet); /* Mask h/w interrupt */ bnxt_qplib_ring_nq_db(&nq->nq_db.dbinfo, nq->res->cctx, false); @@ -412,11 +415,10 @@ void bnxt_qplib_nq_stop_irq(struct bnxt_qplib_nq *nq, bool kill) synchronize_irq(nq->msix_vec); if (kill) tasklet_kill(&nq->nq_tasklet); - if (nq->requested) { - irq_set_affinity_hint(nq->msix_vec, NULL); - free_irq(nq->msix_vec, nq); - nq->requested = false; - } + + irq_set_affinity_hint(nq->msix_vec, NULL); + free_irq(nq->msix_vec, nq); + nq->requested = false; } void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq) @@ -455,8 +457,10 @@ int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, snprintf(nq->name, sizeof(nq->name), "bnxt_qplib_nq-%d", nq_indx); rc = request_irq(nq->msix_vec, bnxt_qplib_nq_irq, 0, nq->name, nq); - if (rc) + if (rc) { + tasklet_disable(&nq->nq_tasklet); return rc; + } cpumask_clear(&nq->mask); cpumask_set_cpu(nq_indx, &nq->mask); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 061b2895dd9b..e28f0eb5b55d 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -635,6 +635,10 @@ void bnxt_qplib_rcfw_stop_irq(struct bnxt_qplib_rcfw *rcfw, bool kill) struct bnxt_qplib_creq_ctx *creq; creq = &rcfw->creq; + + if (!creq->requested) + return; + tasklet_disable(&creq->creq_tasklet); /* Mask h/w interrupts */ bnxt_qplib_ring_nq_db(&creq->creq_db.dbinfo, rcfw->res->cctx, false); @@ -643,10 +647,8 @@ void bnxt_qplib_rcfw_stop_irq(struct bnxt_qplib_rcfw *rcfw, bool kill) if (kill) tasklet_kill(&creq->creq_tasklet); - if (creq->requested) { - free_irq(creq->msix_vec, rcfw); - creq->requested = false; - } + free_irq(creq->msix_vec, rcfw); + creq->requested = false; } void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw) @@ -692,8 +694,10 @@ int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector, tasklet_enable(&creq->creq_tasklet); rc = request_irq(creq->msix_vec, bnxt_qplib_creq_irq, 0, "bnxt_qplib_creq", rcfw); - if (rc) + if (rc) { + tasklet_disable(&creq->creq_tasklet); return rc; + } creq->requested = true; bnxt_qplib_ring_nq_db(&creq->creq_db.dbinfo, rcfw->res->cctx, true); -- cgit 1.4.1 From f2279e3e38760463a9b93537778ce80b3fb653cf Mon Sep 17 00:00:00 2001 From: Kalesh AP Date: Thu, 18 May 2023 23:48:12 -0700 Subject: RDMA/bnxt_re: Fix to remove unnecessary return labels [ Upstream commit 9b3ee47796f529e5bc31a355d6cb756d68a7079a ] If there is no cleanup needed then just return directly. This cleans up the code and improve readability. Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Link: https://lore.kernel.org/r/1684478897-12247-3-git-send-email-selvin.xavier@broadcom.com Reviewed-by: Kashyap Desai Reviewed-by: Saravanan Vajravel Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index a143bd3580a2..4abe1f59b368 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -1605,7 +1605,7 @@ static int bnxt_qplib_put_inline(struct bnxt_qplib_qp *qp, il_src = (void *)wqe->sg_list[indx].addr; t_len += len; if (t_len > qp->max_inline_data) - goto bad; + return -ENOMEM; while (len) { if (pull_dst) { pull_dst = false; @@ -1629,8 +1629,6 @@ static int bnxt_qplib_put_inline(struct bnxt_qplib_qp *qp, } return t_len; -bad: - return -ENOMEM; } static u32 bnxt_qplib_put_sges(struct bnxt_qplib_hwq *hwq, @@ -2060,7 +2058,7 @@ int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) hwq_attr.sginfo = &cq->sg_info; rc = bnxt_qplib_alloc_init_hwq(&cq->hwq, &hwq_attr); if (rc) - goto exit; + return rc; RCFW_CMD_PREP(req, CREATE_CQ, cmd_flags); @@ -2101,7 +2099,6 @@ int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) fail: bnxt_qplib_free_hwq(res, &cq->hwq); -exit: return rc; } -- cgit 1.4.1 From 66eb6c47b570aa21ffef3e8260cb8873e4f6025f Mon Sep 17 00:00:00 2001 From: Kalesh AP Date: Thu, 18 May 2023 23:48:13 -0700 Subject: RDMA/bnxt_re: Use unique names while registering interrupts [ Upstream commit ff2e4bfd162cf66a112a81509e419805add44d64 ] bnxt_re currently uses the names "bnxt_qplib_creq" and "bnxt_qplib_nq-0" while registering IRQs. There is no way to distinguish the IRQs of different device ports when there are multiple IB devices registered. This could make the scenarios worse where one want to pin IRQs of a device port to certain CPUs. Fixed the code to use unique names which has PCI BDF information while registering interrupts like: "bnxt_re-nq-0@pci:0000:65:00.0" and "bnxt_re-creq@pci:0000:65:00.1". Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Link: https://lore.kernel.org/r/1684478897-12247-4-git-send-email-selvin.xavier@broadcom.com Reviewed-by: Bhargava Chenna Marreddy Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 12 ++++++++++-- drivers/infiniband/hw/bnxt_re/qplib_fp.h | 2 +- drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 15 +++++++++++++-- drivers/infiniband/hw/bnxt_re/qplib_rcfw.h | 1 + 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 4abe1f59b368..640d932bec37 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -418,6 +418,8 @@ void bnxt_qplib_nq_stop_irq(struct bnxt_qplib_nq *nq, bool kill) irq_set_affinity_hint(nq->msix_vec, NULL); free_irq(nq->msix_vec, nq); + kfree(nq->name); + nq->name = NULL; nq->requested = false; } @@ -444,6 +446,7 @@ void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq) int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, int msix_vector, bool need_init) { + struct bnxt_qplib_res *res = nq->res; int rc; if (nq->requested) @@ -455,9 +458,14 @@ int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, else tasklet_enable(&nq->nq_tasklet); - snprintf(nq->name, sizeof(nq->name), "bnxt_qplib_nq-%d", nq_indx); + nq->name = kasprintf(GFP_KERNEL, "bnxt_re-nq-%d@pci:%s", + nq_indx, pci_name(res->pdev)); + if (!nq->name) + return -ENOMEM; rc = request_irq(nq->msix_vec, bnxt_qplib_nq_irq, 0, nq->name, nq); if (rc) { + kfree(nq->name); + nq->name = NULL; tasklet_disable(&nq->nq_tasklet); return rc; } @@ -471,7 +479,7 @@ int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, nq->msix_vec, nq_indx); } nq->requested = true; - bnxt_qplib_ring_nq_db(&nq->nq_db.dbinfo, nq->res->cctx, true); + bnxt_qplib_ring_nq_db(&nq->nq_db.dbinfo, res->cctx, true); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index 037501952543..f859710f9a7f 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -471,7 +471,7 @@ typedef int (*srqn_handler_t)(struct bnxt_qplib_nq *nq, struct bnxt_qplib_nq { struct pci_dev *pdev; struct bnxt_qplib_res *res; - char name[32]; + char *name; struct bnxt_qplib_hwq hwq; struct bnxt_qplib_nq_db nq_db; u16 ring_id; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index e28f0eb5b55d..9c63b8b62edf 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -648,6 +648,8 @@ void bnxt_qplib_rcfw_stop_irq(struct bnxt_qplib_rcfw *rcfw, bool kill) tasklet_kill(&creq->creq_tasklet); free_irq(creq->msix_vec, rcfw); + kfree(creq->irq_name); + creq->irq_name = NULL; creq->requested = false; } @@ -680,9 +682,11 @@ int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector, bool need_init) { struct bnxt_qplib_creq_ctx *creq; + struct bnxt_qplib_res *res; int rc; creq = &rcfw->creq; + res = rcfw->res; if (creq->requested) return -EFAULT; @@ -692,15 +696,22 @@ int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector, tasklet_setup(&creq->creq_tasklet, bnxt_qplib_service_creq); else tasklet_enable(&creq->creq_tasklet); + + creq->irq_name = kasprintf(GFP_KERNEL, "bnxt_re-creq@pci:%s", + pci_name(res->pdev)); + if (!creq->irq_name) + return -ENOMEM; rc = request_irq(creq->msix_vec, bnxt_qplib_creq_irq, 0, - "bnxt_qplib_creq", rcfw); + creq->irq_name, rcfw); if (rc) { + kfree(creq->irq_name); + creq->irq_name = NULL; tasklet_disable(&creq->creq_tasklet); return rc; } creq->requested = true; - bnxt_qplib_ring_nq_db(&creq->creq_db.dbinfo, rcfw->res->cctx, true); + bnxt_qplib_ring_nq_db(&creq->creq_db.dbinfo, res->cctx, true); return 0; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h index 0a3d8e7da3d4..b887e7fbad9e 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h @@ -174,6 +174,7 @@ struct bnxt_qplib_creq_ctx { u16 ring_id; int msix_vec; bool requested; /*irq handler installed */ + char *irq_name; }; /* RCFW Communication Channels */ -- cgit 1.4.1 From c37eca42ac6ba337cb5aa5b78b901a8dba2400e7 Mon Sep 17 00:00:00 2001 From: Kalesh AP Date: Thu, 18 May 2023 23:48:14 -0700 Subject: RDMA/bnxt_re: Remove a redundant check inside bnxt_re_update_gid [ Upstream commit b989f90cef0af48aa5679b6a75476371705ec53c ] The NULL check inside bnxt_re_update_gid() always return false. If sgid_tbl->tbl is not allocated, then dev_init would have failed. Fixes: 5fac5b1b297f ("RDMA/bnxt_re: Add vlan tag for untagged RoCE traffic when PFC is configured") Link: https://lore.kernel.org/r/1684478897-12247-5-git-send-email-selvin.xavier@broadcom.com Reviewed-by: Saravanan Vajravel Reviewed-by: Damodharam Ammepalli Reviewed-by: Ajit Khaparde Signed-off-by: Selvin Xavier Signed-off-by: Kalesh AP Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/main.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 1589b1105f2e..e58893387bb4 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -1182,12 +1182,6 @@ static int bnxt_re_update_gid(struct bnxt_re_dev *rdev) if (!ib_device_try_get(&rdev->ibdev)) return 0; - if (!sgid_tbl) { - ibdev_err(&rdev->ibdev, "QPLIB: SGID table not allocated"); - rc = -EINVAL; - goto out; - } - for (index = 0; index < sgid_tbl->active; index++) { gid_idx = sgid_tbl->hw_id[index]; @@ -1205,7 +1199,7 @@ static int bnxt_re_update_gid(struct bnxt_re_dev *rdev) rc = bnxt_qplib_update_sgid(sgid_tbl, &gid, gid_idx, rdev->qplib_res.netdev->dev_addr); } -out: + ib_device_put(&rdev->ibdev); return rc; } -- cgit 1.4.1 From dd129da1fd740d91901c8d980f954f0c879c2953 Mon Sep 17 00:00:00 2001 From: Kalesh AP Date: Thu, 18 May 2023 23:48:15 -0700 Subject: RDMA/bnxt_re: Fix to remove an unnecessary log [ Upstream commit 43774bc156614346fe5dacabc8e8c229167f2536 ] During destroy_qp, driver sets the qp handle in the existing CQEs belonging to the QP being destroyed to NULL. As a result, a poll_cq after destroy_qp can report unnecessary messages. Remove this noise from system logs. Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Link: https://lore.kernel.org/r/1684478897-12247-6-git-send-email-selvin.xavier@broadcom.com Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 640d932bec37..74d56900387a 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -2734,11 +2734,8 @@ static int bnxt_qplib_cq_process_terminal(struct bnxt_qplib_cq *cq, qp = (struct bnxt_qplib_qp *)((unsigned long) le64_to_cpu(hwcqe->qp_handle)); - if (!qp) { - dev_err(&cq->hwq.pdev->dev, - "FP: CQ Process terminal qp is NULL\n"); + if (!qp) return -EINVAL; - } /* Must block new posting of SQ and RQ */ qp->state = CMDQ_MODIFY_QP_NEW_STATE_ERR; -- cgit 1.4.1 From 74abb8d3cd9721fd498bcc602696e63b13935005 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 1 May 2023 04:12:57 +0300 Subject: drm/msm/dsi: don't allow enabling 14nm VCO with unprogrammed rate [ Upstream commit 1e0a97f84d73ea1182740f62069690c7f3271abb ] If the dispcc uses CLK_OPS_PARENT_ENABLE (e.g. on QCM2290), CCF can try enabling VCO before the rate has been programmed. This can cause clock lockups and/or other boot issues. Program the VCO to the minimal PLL rate if the read rate is 0 Hz. Cc: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Reported-by: Vladimir Zapolskiy Reported-by: Konrad Dybcio Reviewed-by: Konrad Dybcio Fixes: f079f6d999cb ("drm/msm/dsi: Add PHY/PLL for 8x96") Patchwork: https://patchwork.freedesktop.org/patch/534813/ Link: https://lore.kernel.org/r/20230501011257.3460103-1-dmitry.baryshkov@linaro.org Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c index 0f8f4ca46429..f0780c40b379 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c @@ -539,6 +539,9 @@ static int dsi_pll_14nm_vco_prepare(struct clk_hw *hw) if (unlikely(pll_14nm->phy->pll_on)) return 0; + if (dsi_pll_14nm_vco_recalc_rate(hw, VCO_REF_CLK_RATE) == 0) + dsi_pll_14nm_vco_set_rate(hw, pll_14nm->phy->cfg->min_pll_rate, VCO_REF_CLK_RATE); + dsi_phy_write(base + REG_DSI_14nm_PHY_PLL_VREF_CFG1, 0x10); dsi_phy_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 1); -- cgit 1.4.1 From f4c6e5d7349ecd2f5b677a0e46380c9662f044d7 Mon Sep 17 00:00:00 2001 From: Vinod Polimera Date: Thu, 2 Mar 2023 22:03:08 +0530 Subject: drm/msm/disp/dpu: get timing engine status from intf status register [ Upstream commit e3969eadc8ee78a5bdca65b8ed0a421a359e4090 ] Recommended way of reading the interface timing gen status is via status register. Timing gen status register will give a reliable status of the interface especially during ON/OFF transitions. This support was added from DPU version 5.0.0. Signed-off-by: Vinod Polimera Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/524724/ Link: https://lore.kernel.org/r/1677774797-31063-6-git-send-email-quic_vpolimer@quicinc.com Signed-off-by: Dmitry Baryshkov Stable-dep-of: a7129231edf3 ("drm/msm/dpu: Set DPU_DATA_HCTL_EN for in INTF_SC7180_MASK") Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 3 ++- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 12 +++++++----- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | 8 +++++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index 32a3c42ec45b..715c7a254632 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -79,7 +79,8 @@ #define INTF_SDM845_MASK (0) -#define INTF_SC7180_MASK BIT(DPU_INTF_INPUT_CTRL) | BIT(DPU_INTF_TE) +#define INTF_SC7180_MASK \ + (BIT(DPU_INTF_INPUT_CTRL) | BIT(DPU_INTF_TE) | BIT(DPU_INTF_STATUS_SUPPORTED)) #define INTF_SC7280_MASK INTF_SC7180_MASK | BIT(DPU_DATA_HCTL_EN) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index 38aa38ab1568..77c46ce5a22f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -203,17 +203,19 @@ enum { /** * INTF sub-blocks - * @DPU_INTF_INPUT_CTRL Supports the setting of pp block from which - * pixel data arrives to this INTF - * @DPU_INTF_TE INTF block has TE configuration support - * @DPU_DATA_HCTL_EN Allows data to be transferred at different rate - than video timing + * @DPU_INTF_INPUT_CTRL Supports the setting of pp block from which + * pixel data arrives to this INTF + * @DPU_INTF_TE INTF block has TE configuration support + * @DPU_DATA_HCTL_EN Allows data to be transferred at different rate + * than video timing + * @DPU_INTF_STATUS_SUPPORTED INTF block has INTF_STATUS register * @DPU_INTF_MAX */ enum { DPU_INTF_INPUT_CTRL = 0x1, DPU_INTF_TE, DPU_DATA_HCTL_EN, + DPU_INTF_STATUS_SUPPORTED, DPU_INTF_MAX }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c index b2a94b9a3e98..b9dddf576c02 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c @@ -57,6 +57,7 @@ #define INTF_PROG_FETCH_START 0x170 #define INTF_PROG_ROT_START 0x174 #define INTF_MUX 0x25C +#define INTF_STATUS 0x26C #define INTF_CFG_ACTIVE_H_EN BIT(29) #define INTF_CFG_ACTIVE_V_EN BIT(30) @@ -292,8 +293,13 @@ static void dpu_hw_intf_get_status( struct intf_status *s) { struct dpu_hw_blk_reg_map *c = &intf->hw; + unsigned long cap = intf->cap->features; + + if (cap & BIT(DPU_INTF_STATUS_SUPPORTED)) + s->is_en = DPU_REG_READ(c, INTF_STATUS) & BIT(0); + else + s->is_en = DPU_REG_READ(c, INTF_TIMING_ENGINE_EN); - s->is_en = DPU_REG_READ(c, INTF_TIMING_ENGINE_EN); s->is_prog_fetch_en = !!(DPU_REG_READ(c, INTF_CONFIG) & BIT(31)); if (s->is_en) { s->frame_count = DPU_REG_READ(c, INTF_FRAME_COUNT); -- cgit 1.4.1 From ed41f708b35a151a266e97b4db0ef6708491b1cd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 19 May 2023 20:49:59 +0200 Subject: drm/msm/dpu: Set DPU_DATA_HCTL_EN for in INTF_SC7180_MASK [ Upstream commit a7129231edf329a00e92dbd2d741f6da728a4a06 ] DPU5 and newer targets enable this unconditionally. Move it from the SC7280 mask to the SC7180 one. Fixes: 7e6ee55320f0 ("drm/msm/disp/dpu1: enable DATA_HCTL_EN for sc7280 target") Reviewed-by: Dmitry Baryshkov Reviewed-by: Marijn Suijten Signed-off-by: Konrad Dybcio Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/538159/ Link: https://lore.kernel.org/r/20230508-topic-hctl_en-v2-1-e7bea9f1f5dd@linaro.org [DB: removed BIT(DPU_INTF_DATA_COMPRESS), which is not yet merged] Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index 715c7a254632..80e59d3f9dd3 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -80,9 +80,12 @@ #define INTF_SDM845_MASK (0) #define INTF_SC7180_MASK \ - (BIT(DPU_INTF_INPUT_CTRL) | BIT(DPU_INTF_TE) | BIT(DPU_INTF_STATUS_SUPPORTED)) + (BIT(DPU_INTF_INPUT_CTRL) | \ + BIT(DPU_INTF_TE) | \ + BIT(DPU_INTF_STATUS_SUPPORTED) | \ + BIT(DPU_DATA_HCTL_EN)) -#define INTF_SC7280_MASK INTF_SC7180_MASK | BIT(DPU_DATA_HCTL_EN) +#define INTF_SC7280_MASK (INTF_SC7180_MASK) #define IRQ_SDM845_MASK (BIT(MDP_SSPP_TOP0_INTR) | \ BIT(MDP_SSPP_TOP0_INTR2) | \ -- cgit 1.4.1 From 0f2c11ccfdcc97b42b78bf407176d220cb990ada Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Mon, 15 May 2023 12:39:48 +0100 Subject: iommu/virtio: Detach domain on endpoint release [ Upstream commit 809d0810e3520da669d231303608cdf5fe5c1a70 ] When an endpoint is released, for example a PCIe VF being destroyed or a function hot-unplugged, it should be detached from its domain. Send a DETACH request. Fixes: edcd69ab9a32 ("iommu: Add virtio-iommu driver") Reported-by: Akihiko Odaki Link: https://lore.kernel.org/all/15bf1b00-3aa0-973a-3a86-3fa5c4d41d2c@daynix.com/ Signed-off-by: Jean-Philippe Brucker Tested-by: Akihiko Odaki Link: https://lore.kernel.org/r/20230515113946.1017624-2-jean-philippe@linaro.org Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/virtio-iommu.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c index 8b1b5c270e50..fe02ac772b65 100644 --- a/drivers/iommu/virtio-iommu.c +++ b/drivers/iommu/virtio-iommu.c @@ -789,6 +789,29 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev) return 0; } +static void viommu_detach_dev(struct viommu_endpoint *vdev) +{ + int i; + struct virtio_iommu_req_detach req; + struct viommu_domain *vdomain = vdev->vdomain; + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(vdev->dev); + + if (!vdomain) + return; + + req = (struct virtio_iommu_req_detach) { + .head.type = VIRTIO_IOMMU_T_DETACH, + .domain = cpu_to_le32(vdomain->id), + }; + + for (i = 0; i < fwspec->num_ids; i++) { + req.endpoint = cpu_to_le32(fwspec->ids[i]); + WARN_ON(viommu_send_req_sync(vdev->viommu, &req, sizeof(req))); + } + vdomain->nr_endpoints--; + vdev->vdomain = NULL; +} + static int viommu_map_pages(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t pgsize, size_t pgcount, int prot, gfp_t gfp, size_t *mapped) @@ -991,6 +1014,7 @@ static void viommu_release_device(struct device *dev) { struct viommu_endpoint *vdev = dev_iommu_priv_get(dev); + viommu_detach_dev(vdev); iommu_put_resv_regions(dev, &vdev->resv_regions); kfree(vdev); } -- cgit 1.4.1 From 2128318c91303d07689d47414c129dd7976129f0 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Mon, 15 May 2023 12:39:50 +0100 Subject: iommu/virtio: Return size mapped for a detached domain [ Upstream commit 7061b6af34686e7e2364b7240cfb061293218f2d ] When map() is called on a detached domain, the domain does not exist in the device so we do not send a MAP request, but we do update the internal mapping tree, to be replayed on the next attach. Since this constitutes a successful iommu_map() call, return *mapped in this case too. Fixes: 7e62edd7a33a ("iommu/virtio: Add map/unmap_pages() callbacks implementation") Signed-off-by: Jean-Philippe Brucker Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20230515113946.1017624-3-jean-philippe@linaro.org Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/virtio-iommu.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c index fe02ac772b65..fd86ccb709ec 100644 --- a/drivers/iommu/virtio-iommu.c +++ b/drivers/iommu/virtio-iommu.c @@ -834,25 +834,26 @@ static int viommu_map_pages(struct iommu_domain *domain, unsigned long iova, if (ret) return ret; - map = (struct virtio_iommu_req_map) { - .head.type = VIRTIO_IOMMU_T_MAP, - .domain = cpu_to_le32(vdomain->id), - .virt_start = cpu_to_le64(iova), - .phys_start = cpu_to_le64(paddr), - .virt_end = cpu_to_le64(end), - .flags = cpu_to_le32(flags), - }; - - if (!vdomain->nr_endpoints) - return 0; + if (vdomain->nr_endpoints) { + map = (struct virtio_iommu_req_map) { + .head.type = VIRTIO_IOMMU_T_MAP, + .domain = cpu_to_le32(vdomain->id), + .virt_start = cpu_to_le64(iova), + .phys_start = cpu_to_le64(paddr), + .virt_end = cpu_to_le64(end), + .flags = cpu_to_le32(flags), + }; - ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map)); - if (ret) - viommu_del_mappings(vdomain, iova, end); - else if (mapped) + ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map)); + if (ret) { + viommu_del_mappings(vdomain, iova, end); + return ret; + } + } + if (mapped) *mapped = size; - return ret; + return 0; } static size_t viommu_unmap_pages(struct iommu_domain *domain, unsigned long iova, -- cgit 1.4.1 From af5bcfb4f8b5cb36b88b9859c22100673749f230 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 18 May 2023 16:23:34 +0100 Subject: clk: renesas: rzg2l: Fix CPG_SIPLL5_CLK1 register write [ Upstream commit d1c20885d3b01e6a62e920af4b227abd294d22f3 ] As per the RZ/G2L HW(Rev.1.30 May2023) manual, there are no "write enable" bits in the CPG_SIPLL5_CLK1 register. So fix the CPG_SIPLL5_CLK register write by removing the "write enable" bits. Fixes: 1561380ee72f ("clk: renesas: rzg2l: Add FOUTPOSTDIV clk support") Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230518152334.514922-1-biju.das.jz@bp.renesas.com [geert: Remove CPG_SIPLL5_CLK1_*_WEN bit definitions] Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- drivers/clk/renesas/rzg2l-cpg.c | 6 ++---- drivers/clk/renesas/rzg2l-cpg.h | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 3ff6ecd61756..2c877576c572 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -600,10 +600,8 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw, } /* Output clock setting 1 */ - writel(CPG_SIPLL5_CLK1_POSTDIV1_WEN | CPG_SIPLL5_CLK1_POSTDIV2_WEN | - CPG_SIPLL5_CLK1_REFDIV_WEN | (params.pl5_postdiv1 << 0) | - (params.pl5_postdiv2 << 4) | (params.pl5_refdiv << 8), - priv->base + CPG_SIPLL5_CLK1); + writel((params.pl5_postdiv1 << 0) | (params.pl5_postdiv2 << 4) | + (params.pl5_refdiv << 8), priv->base + CPG_SIPLL5_CLK1); /* Output clock setting, SSCG modulation value setting 3 */ writel((params.pl5_fracin << 8), priv->base + CPG_SIPLL5_CLK3); diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index cecbdf5e4f93..b33a3e79161b 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -32,9 +32,6 @@ #define CPG_SIPLL5_STBY_RESETB_WEN BIT(16) #define CPG_SIPLL5_STBY_SSCG_EN_WEN BIT(18) #define CPG_SIPLL5_STBY_DOWNSPREAD_WEN BIT(20) -#define CPG_SIPLL5_CLK1_POSTDIV1_WEN BIT(16) -#define CPG_SIPLL5_CLK1_POSTDIV2_WEN BIT(20) -#define CPG_SIPLL5_CLK1_REFDIV_WEN BIT(24) #define CPG_SIPLL5_CLK4_RESV_LSB (0xFF) #define CPG_SIPLL5_MON_PLL5_LOCK BIT(4) -- cgit 1.4.1 From 93a03780357e4ceeaf90b2fb98b41a7196971164 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 17 May 2023 13:32:25 +0300 Subject: ARM: dts: gta04: Move model property out of pinctrl node [ Upstream commit 4ffec92e70ac5097b9f67ec154065305b16a3b46 ] The model property should be at the top level, let's move it out of the pinctrl node. Fixes: d2eaf949d2c3 ("ARM: dts: omap3-gta04a5one: define GTA04A5 variant with OneNAND") Cc: Andreas Kemnade Cc: H. Nikolaus Schaller Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin --- arch/arm/boot/dts/omap3-gta04a5one.dts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/omap3-gta04a5one.dts b/arch/arm/boot/dts/omap3-gta04a5one.dts index 9db9fe67cd63..95df45cc70c0 100644 --- a/arch/arm/boot/dts/omap3-gta04a5one.dts +++ b/arch/arm/boot/dts/omap3-gta04a5one.dts @@ -5,9 +5,11 @@ #include "omap3-gta04a5.dts" -&omap3_pmx_core { +/ { model = "Goldelico GTA04A5/Letux 2804 with OneNAND"; +}; +&omap3_pmx_core { gpmc_pins: pinmux_gpmc_pins { pinctrl-single,pins = < -- cgit 1.4.1 From 4536679f7911d9d33bbca0a089285712d9609af3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 18 Nov 2022 23:35:51 +0100 Subject: drm/bridge: anx7625: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 71450f8c824f5571d4af9e6e021b733085c8e690 ] The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20221118224540.619276-18-uwe@kleine-koenig.org Signed-off-by: Robert Foss Stable-dep-of: 1464e48d69ab ("drm/bridge: anx7625: Prevent endless probe loop") Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/analogix/anx7625.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index b0ff1ecb80a5..86a52c5f4fbc 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2562,8 +2562,7 @@ static void anx7625_runtime_disable(void *data) pm_runtime_disable(data); } -static int anx7625_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int anx7625_i2c_probe(struct i2c_client *client) { struct anx7625_data *platform; struct anx7625_platform_data *pdata; @@ -2756,7 +2755,7 @@ static struct i2c_driver anx7625_driver = { .of_match_table = anx_match_table, .pm = &anx7625_pm_ops, }, - .probe = anx7625_i2c_probe, + .probe_new = anx7625_i2c_probe, .remove = anx7625_i2c_remove, .id_table = anx7625_id, -- cgit 1.4.1 From 376daf3aa8978e89d9135909765bb5677b2aa7a4 Mon Sep 17 00:00:00 2001 From: "Nícolas F. R. A. Prado" Date: Thu, 18 May 2023 15:39:02 -0400 Subject: drm/bridge: anx7625: Prevent endless probe loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1464e48d69ab7a50a377c9d39f5e5eb3cee2722e ] During probe, the driver registers i2c dummy devices and populates the aux bus, which registers a device for the panel. After doing that, the driver can still defer probe if needed. This ordering of operations is troublesome however, because the deferred probe work will retry probing all pending devices every time a new device is registered. Therefore, if modules need to be loaded in order to satisfy the dependencies for this driver to complete probe, the kernel will stall, since it'll keep trying to probe the anx7625 driver, but never succeed, given that modules would only be loaded after the deferred probe work completes. Two changes are required to avoid this issue: * Move of_find_mipi_dsi_host_by_node(), which can defer probe, to before anx7625_register_i2c_dummy_clients() and devm_of_dp_aux_populate_ep_devices(), which register devices. * Make use of the done_probing callback when populating the aux bus, so that the bridge registration is only done after the panel is probed. This is required because the panel might need to defer probe, but the aux bus population needs the i2c dummy devices working, so this call couldn't just be moved to an earlier point in probe. One caveat is that if the panel is described outside the aux bus, the probe loop issue can still happen, but we don't have a way to avoid it in that case since there's no callback available. With this patch applied, it's possible to boot on mt8192-asurada-spherion with CONFIG_DRM_ANALOGIX_ANX7625=y CONFIG_MTK_MMSYS=m CONFIG_BACKLIGHT_PWM=y and also with CONFIG_DRM_ANALOGIX_ANX7625=y CONFIG_MTK_MMSYS=y CONFIG_BACKLIGHT_PWM=m Fixes: adca62ec370c ("drm/bridge: anx7625: Support reading edid through aux channel") Fixes: 269332997a16 ("drm/bridge: anx7625: Return -EPROBE_DEFER if the dsi host was not found") Reported-by: "kernelci.org bot" Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230518193902.891121-1-nfraprado@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/analogix/anx7625.c | 128 ++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 86a52c5f4fbc..213263ad6a06 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1689,6 +1689,14 @@ static int anx7625_parse_dt(struct device *dev, if (of_property_read_bool(np, "analogix,audio-enable")) pdata->audio_en = 1; + return 0; +} + +static int anx7625_parse_dt_panel(struct device *dev, + struct anx7625_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + pdata->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); if (IS_ERR(pdata->panel_bridge)) { if (PTR_ERR(pdata->panel_bridge) == -ENODEV) { @@ -2034,7 +2042,7 @@ static int anx7625_register_audio(struct device *dev, struct anx7625_data *ctx) return 0; } -static int anx7625_attach_dsi(struct anx7625_data *ctx) +static int anx7625_setup_dsi_device(struct anx7625_data *ctx) { struct mipi_dsi_device *dsi; struct device *dev = &ctx->client->dev; @@ -2044,9 +2052,6 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx) .channel = 0, .node = NULL, }; - int ret; - - DRM_DEV_DEBUG_DRIVER(dev, "attach dsi\n"); host = of_find_mipi_dsi_host_by_node(ctx->pdata.mipi_host_node); if (!host) { @@ -2067,14 +2072,24 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx) MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_HS_PKT_END_ALIGNED; - ret = devm_mipi_dsi_attach(dev, dsi); + ctx->dsi = dsi; + + return 0; +} + +static int anx7625_attach_dsi(struct anx7625_data *ctx) +{ + struct device *dev = &ctx->client->dev; + int ret; + + DRM_DEV_DEBUG_DRIVER(dev, "attach dsi\n"); + + ret = devm_mipi_dsi_attach(dev, ctx->dsi); if (ret) { DRM_DEV_ERROR(dev, "fail to attach dsi to host.\n"); return ret; } - ctx->dsi = dsi; - DRM_DEV_DEBUG_DRIVER(dev, "attach dsi succeeded.\n"); return 0; @@ -2562,6 +2577,40 @@ static void anx7625_runtime_disable(void *data) pm_runtime_disable(data); } +static int anx7625_link_bridge(struct drm_dp_aux *aux) +{ + struct anx7625_data *platform = container_of(aux, struct anx7625_data, aux); + struct device *dev = aux->dev; + int ret; + + ret = anx7625_parse_dt_panel(dev, &platform->pdata); + if (ret) { + DRM_DEV_ERROR(dev, "fail to parse DT for panel : %d\n", ret); + return ret; + } + + platform->bridge.funcs = &anx7625_bridge_funcs; + platform->bridge.of_node = dev->of_node; + if (!anx7625_of_panel_on_aux_bus(dev)) + platform->bridge.ops |= DRM_BRIDGE_OP_EDID; + if (!platform->pdata.panel_bridge) + platform->bridge.ops |= DRM_BRIDGE_OP_HPD | + DRM_BRIDGE_OP_DETECT; + platform->bridge.type = platform->pdata.panel_bridge ? + DRM_MODE_CONNECTOR_eDP : + DRM_MODE_CONNECTOR_DisplayPort; + + drm_bridge_add(&platform->bridge); + + if (!platform->pdata.is_dpi) { + ret = anx7625_attach_dsi(platform); + if (ret) + drm_bridge_remove(&platform->bridge); + } + + return ret; +} + static int anx7625_i2c_probe(struct i2c_client *client) { struct anx7625_data *platform; @@ -2636,6 +2685,24 @@ static int anx7625_i2c_probe(struct i2c_client *client) platform->aux.wait_hpd_asserted = anx7625_wait_hpd_asserted; drm_dp_aux_init(&platform->aux); + ret = anx7625_parse_dt(dev, pdata); + if (ret) { + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); + goto free_wq; + } + + if (!platform->pdata.is_dpi) { + ret = anx7625_setup_dsi_device(platform); + if (ret < 0) + goto free_wq; + } + + /* + * Registering the i2c devices will retrigger deferred probe, so it + * needs to be done after calls that might return EPROBE_DEFER, + * otherwise we can get an infinite loop. + */ if (anx7625_register_i2c_dummy_clients(platform, client) != 0) { ret = -ENOMEM; DRM_DEV_ERROR(dev, "fail to reserve I2C bus.\n"); @@ -2650,13 +2717,21 @@ static int anx7625_i2c_probe(struct i2c_client *client) if (ret) goto free_wq; - devm_of_dp_aux_populate_ep_devices(&platform->aux); - - ret = anx7625_parse_dt(dev, pdata); + /* + * Populating the aux bus will retrigger deferred probe, so it needs to + * be done after calls that might return EPROBE_DEFER, otherwise we can + * get an infinite loop. + */ + ret = devm_of_dp_aux_populate_bus(&platform->aux, anx7625_link_bridge); if (ret) { - if (ret != -EPROBE_DEFER) - DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); - goto free_wq; + if (ret != -ENODEV) { + DRM_DEV_ERROR(dev, "failed to populate aux bus : %d\n", ret); + goto free_wq; + } + + ret = anx7625_link_bridge(&platform->aux); + if (ret) + goto free_wq; } if (!platform->pdata.low_power_mode) { @@ -2669,27 +2744,6 @@ static int anx7625_i2c_probe(struct i2c_client *client) if (platform->pdata.intp_irq) queue_work(platform->workqueue, &platform->work); - platform->bridge.funcs = &anx7625_bridge_funcs; - platform->bridge.of_node = client->dev.of_node; - if (!anx7625_of_panel_on_aux_bus(&client->dev)) - platform->bridge.ops |= DRM_BRIDGE_OP_EDID; - if (!platform->pdata.panel_bridge) - platform->bridge.ops |= DRM_BRIDGE_OP_HPD | - DRM_BRIDGE_OP_DETECT; - platform->bridge.type = platform->pdata.panel_bridge ? - DRM_MODE_CONNECTOR_eDP : - DRM_MODE_CONNECTOR_DisplayPort; - - drm_bridge_add(&platform->bridge); - - if (!platform->pdata.is_dpi) { - ret = anx7625_attach_dsi(platform); - if (ret) { - DRM_DEV_ERROR(dev, "Fail to attach to dsi : %d\n", ret); - goto unregister_bridge; - } - } - if (platform->pdata.audio_en) anx7625_register_audio(dev, platform); @@ -2697,12 +2751,6 @@ static int anx7625_i2c_probe(struct i2c_client *client) return 0; -unregister_bridge: - drm_bridge_remove(&platform->bridge); - - if (!platform->pdata.low_power_mode) - pm_runtime_put_sync_suspend(&client->dev); - free_wq: if (platform->workqueue) destroy_workqueue(platform->workqueue); -- cgit 1.4.1 From 0cff84682006f8a7d781516b16955314c807940d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 10 Apr 2023 19:52:32 +0200 Subject: ARM: dts: qcom: msm8974: do not use underscore in node name (again) [ Upstream commit 311bbc884b2edcf584b67d331be85ce43b27586f ] Align RPM requests node with DT schema by using hyphen instead of underscore. Fixes: f300826d27be ("ARM: dts: qcom-msm8974: Sort and clean up nodes") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230410175232.22317-1-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm/boot/dts/qcom-msm8974.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi index 7a9be0acf3f5..c4b2e9ac2494 100644 --- a/arch/arm/boot/dts/qcom-msm8974.dtsi +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi @@ -300,7 +300,7 @@ qcom,ipc = <&apcs 8 0>; qcom,smd-edge = <15>; - rpm_requests: rpm_requests { + rpm_requests: rpm-requests { compatible = "qcom,rpm-msm8974"; qcom,smd-channels = "rpm_requests"; -- cgit 1.4.1 From aa2d2407f5d257d3b5d03a5a8fc26e0bd429a75b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:40 +0200 Subject: arm64: dts: qcom: msm8916: correct camss unit address [ Upstream commit 48798d992ce276cf0d57bf75318daf8eabd02aa4 ] Match unit-address to reg entry to fix dtbs W=1 warnings: Warning (simple_bus_reg): /soc@0/camss@1b00000: simple-bus unit address format error, expected "1b0ac00" Fixes: 58f479f90a7c ("arm64: dts: qcom: msm8916: Add CAMSS support") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-2-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/msm8916.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 9743cb270639..99aa9b4dce8a 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -1088,7 +1088,7 @@ }; }; - camss: camss@1b00000 { + camss: camss@1b0ac00 { compatible = "qcom,msm8916-camss"; reg = <0x01b0ac00 0x200>, <0x01b00030 0x4>, -- cgit 1.4.1 From 98cd4052177333a8ba0dde3ae15b6e5eecb9589f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:41 +0200 Subject: arm64: dts: qcom: msm8916: correct MMC unit address [ Upstream commit 72644bc76d5145c098c268829554a0b98fab1de1 ] Match unit-address to reg entry to fix dtbs W=1 warnings: Warning (simple_bus_reg): /soc@0/mmc@7824000: simple-bus unit address format error, expected "7824900" Warning (simple_bus_reg): /soc@0/mmc@7864000: simple-bus unit address format error, expected "7864900" Fixes: c4da5a561627 ("arm64: dts: qcom: Add msm8916 sdhci configuration nodes") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-3-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/msm8916.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 99aa9b4dce8a..f84b3c1a03c5 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -1480,7 +1480,7 @@ #sound-dai-cells = <1>; }; - sdhc_1: mmc@7824000 { + sdhc_1: mmc@7824900 { compatible = "qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"; reg = <0x07824900 0x11c>, <0x07824000 0x800>; reg-names = "hc", "core"; @@ -1498,7 +1498,7 @@ status = "disabled"; }; - sdhc_2: mmc@7864000 { + sdhc_2: mmc@7864900 { compatible = "qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"; reg = <0x07864900 0x11c>, <0x07864000 0x800>; reg-names = "hc", "core"; -- cgit 1.4.1 From 4d810c12d6e1228a6537ffafe40a7e73df8948a4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:46 +0200 Subject: arm64: dts: qcom: msm8994: correct SPMI unit address [ Upstream commit 24f0f6a8059c7108d4ee3476c95db1e7ff4feb79 ] Match unit-address to reg entry to fix dtbs W=1 warnings: Warning (simple_bus_reg): /soc/spmi@fc4c0000: simple-bus unit address format error, expected "fc4cf000" Fixes: b0ad598f8ec0 ("arm64: dts: qcom: msm8994: Add SPMI PMIC arbiter device") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-8-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/msm8994.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8994.dtsi b/arch/arm64/boot/dts/qcom/msm8994.dtsi index 7ed59e698c14..3c6c2cf99fb9 100644 --- a/arch/arm64/boot/dts/qcom/msm8994.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8994.dtsi @@ -746,7 +746,7 @@ reg = <0xfc4ab000 0x4>; }; - spmi_bus: spmi@fc4c0000 { + spmi_bus: spmi@fc4cf000 { compatible = "qcom,spmi-pmic-arb"; reg = <0xfc4cf000 0x1000>, <0xfc4cb000 0x1000>, -- cgit 1.4.1 From 173b6412a5c28fa182a3738f01b7bd19274d02f3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:47 +0200 Subject: arm64: dts: qcom: msm8996: correct camss unit address [ Upstream commit e959ced1d0e5ef0b1f66a0c2d0e1ae80790e5ca5 ] Match unit-address to reg entry to fix dtbs W=1 warnings: Warning (simple_bus_reg): /soc/camss@a00000: simple-bus unit address format error, expected "a34000" Fixes: e0531312e78f ("arm64: dts: qcom: msm8996: Add CAMSS support") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-9-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/msm8996.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 9f8910054201..9d6ec59d1cd3 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -2045,7 +2045,7 @@ }; }; - camss: camss@a00000 { + camss: camss@a34000 { compatible = "qcom,msm8996-camss"; reg = <0x00a34000 0x1000>, <0x00a00030 0x4>, -- cgit 1.4.1 From e3789d63a33be341b9ea9ef3fc324a00f4c64c9e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:48 +0200 Subject: arm64: dts: qcom: sdm630: correct camss unit address [ Upstream commit c8b7faa7e9913a94444b3f00b6480e53a174fcfd ] Match unit-address to reg entry to fix dtbs W=1 warnings: Warning (simple_bus_reg): /soc/camss@ca00000: simple-bus unit address format error, expected "ca00020" Fixes: f3d5d3cc6971 ("arm64: dts: qcom: sdm630: Configure the camera subsystem") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-10-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index e119060ac56c..2430549265d3 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -1902,7 +1902,7 @@ }; }; - camss: camss@ca00000 { + camss: camss@ca00020 { compatible = "qcom,sdm660-camss"; reg = <0x0ca00020 0x10>, <0x0ca30000 0x100>, -- cgit 1.4.1 From aa14fefca239a0e38902915472c1c710f5c49d02 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:49 +0200 Subject: arm64: dts: qcom: sdm845: correct camss unit address [ Upstream commit a05b913a27e46926ba60ba2bcacc7ec7a8403e4c ] Match unit-address to reg entry to fix dtbs W=1 warnings: Warning (simple_bus_reg): /soc@0/camss@a00000: simple-bus unit address format error, expected "acb3000" Fixes: d48a6698a6b7 ("arm64: dts: qcom: sdm845: Add CAMSS ISP node") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-11-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm845.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index a99eda497101..6f4cc3329400 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -4207,7 +4207,7 @@ #reset-cells = <1>; }; - camss: camss@a00000 { + camss: camss@acb3000 { compatible = "qcom,sdm845-camss"; reg = <0 0xacb3000 0 0x1000>, -- cgit 1.4.1 From 42d0fbbbf4ca3678d56d6531e8d9ab49775509a1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 18 Oct 2022 19:03:51 -0400 Subject: arm64: dts: qcom: sm8350: Add GPI DMA compatible fallback [ Upstream commit b561e225dee5412609fd98340ca71ba0ab2e4b36 ] Use SM6350 as fallback for GPI DMA, to indicate devices are compatible and that drivers can bind with only one compatible. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221018230352.1238479-5-krzysztof.kozlowski@linaro.org Stable-dep-of: 41d6bca799b3 ("arm64: dts: qcom: sm8350: correct DMA controller unit address") Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8350.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi index ca7c428a741d..9430166ee45b 100644 --- a/arch/arm64/boot/dts/qcom/sm8350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi @@ -678,7 +678,7 @@ }; gpi_dma2: dma-controller@800000 { - compatible = "qcom,sm8350-gpi-dma"; + compatible = "qcom,sm8350-gpi-dma", "qcom,sm6350-gpi-dma"; reg = <0 0x00800000 0 0x60000>; interrupts = , , @@ -904,7 +904,7 @@ }; gpi_dma0: dma-controller@900000 { - compatible = "qcom,sm8350-gpi-dma"; + compatible = "qcom,sm8350-gpi-dma", "qcom,sm6350-gpi-dma"; reg = <0 0x09800000 0 0x60000>; interrupts = , , @@ -1207,7 +1207,7 @@ }; gpi_dma1: dma-controller@a00000 { - compatible = "qcom,sm8350-gpi-dma"; + compatible = "qcom,sm8350-gpi-dma", "qcom,sm6350-gpi-dma"; reg = <0 0x00a00000 0 0x60000>; interrupts = , , -- cgit 1.4.1 From 266cf247dd88af07b8ffc13af3504567759532f7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:51 +0200 Subject: arm64: dts: qcom: sm8350: correct DMA controller unit address [ Upstream commit 41d6bca799b3f40d4d3c22dd4545aeac7c210e33 ] Match unit-address to reg entry to fix dtbs W=1 warnings: Warning (simple_bus_reg): /soc@0/dma-controller@900000: simple-bus unit address format error, expected "9800000" Fixes: bc08fbf49bc8 ("arm64: dts: qcom: sm8350: Define GPI DMA engines") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-13-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8350.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi index 9430166ee45b..7fd1c3f71c0f 100644 --- a/arch/arm64/boot/dts/qcom/sm8350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi @@ -903,7 +903,7 @@ }; }; - gpi_dma0: dma-controller@900000 { + gpi_dma0: dma-controller@9800000 { compatible = "qcom,sm8350-gpi-dma", "qcom,sm6350-gpi-dma"; reg = <0 0x09800000 0 0x60000>; interrupts = , -- cgit 1.4.1 From 2ad75715fc488fcc90372169a72a854fa03aecbf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Apr 2023 23:18:56 +0200 Subject: arm64: dts: qcom: sdm845-polaris: add missing touchscreen child node reg [ Upstream commit 4a0156b8862665a3e31c8280607388e3001ace3d ] Add missing reg property to touchscreen child node to fix dtbs W=1 warnings: Warning (unit_address_vs_reg): /soc@0/geniqup@ac0000/i2c@a98000/touchscreen@20/rmi4-f12@12: node has a unit name, but no reg or ranges property Fixes: be497abe19bf ("arm64: dts: qcom: Add support for Xiaomi Mi Mix2s") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Reviewed-by: Molly Sophia Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230419211856.79332-18-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts index 74c6832e0598..093b04359ec3 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts @@ -484,6 +484,7 @@ }; rmi4-f12@12 { + reg = <0x12>; syna,rezero-wait-ms = <0xc8>; syna,clip-x-high = <0x438>; syna,clip-y-high = <0x870>; -- cgit 1.4.1 From 638d54f5c566a3a642583da7a3aaa3767aaa1530 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 17 May 2023 20:48:40 +0200 Subject: arm64: dts: qcom: apq8016-sbc: Fix regulator constraints [ Upstream commit e27654df20d77ad7549a3cf6739ebaa3aa59a088 ] For some reason DB410c has completely bogus regulator constraints that actually just correspond to the programmable voltages which are already provided by the regulator driver. Some of them are not just outside the recommended operating conditions of the APQ8016E SoC but even exceed the absolute maximum ratings, potentially risking permanent device damage. In practice it's not quite as dangerous thanks to the RPM firmware: It turns out that it has its own voltage constraints and silently clamps all regulator requests. For example, requesting 3.3V for L5 (allowed by the current regulator constraints!) still results in 1.8V being programmed in the actual regulator hardware. Experimentation with various voltages shows that the internal RPM voltage constraints roughly correspond to the safe "specified range" in the PM8916 Device Specification (rather than the "programmable range" used inside apq8016-sbc.dtsi right now). Combine those together with some fixed voltages used in the old msm-3.10 device tree from Qualcomm to give DB410c some actually valid voltage constraints. Cc: Srinivas Kandagatla Fixes: 4c7d53d16d77 ("arm64: dts: apq8016-sbc: add regulators support") Signed-off-by: Stephan Gerhold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230510-msm8916-regulators-v1-1-54d4960a05fc@gerhold.net Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/apq8016-sbc.dts | 64 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dts b/arch/arm64/boot/dts/qcom/apq8016-sbc.dts index 1b613098fb4a..c6f4ee7b8204 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dts +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dts @@ -448,21 +448,21 @@ vdd_l7-supply = <&pm8916_s4>; s3 { - regulator-min-microvolt = <375000>; - regulator-max-microvolt = <1562000>; + regulator-min-microvolt = <1250000>; + regulator-max-microvolt = <1350000>; }; s4 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1850000>; + regulator-max-microvolt = <2150000>; regulator-always-on; regulator-boot-on; }; l1 { - regulator-min-microvolt = <375000>; - regulator-max-microvolt = <1525000>; + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1225000>; }; l2 { @@ -471,13 +471,13 @@ }; l4 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <2050000>; + regulator-max-microvolt = <2050000>; }; l5 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; }; l6 { @@ -486,45 +486,45 @@ }; l7 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; }; l8 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <2900000>; + regulator-max-microvolt = <2900000>; }; l9 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; }; l10 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; }; l11 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; regulator-allow-set-load; regulator-system-load = <200000>; }; l12 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2950000>; }; l13 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <3075000>; + regulator-max-microvolt = <3075000>; }; l14 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; }; /** @@ -532,14 +532,14 @@ * for mezzanine boards */ l15 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; regulator-always-on; }; l16 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; }; l17 { @@ -548,8 +548,8 @@ }; l18 { - regulator-min-microvolt = <1750000>; - regulator-max-microvolt = <3337000>; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; }; }; -- cgit 1.4.1 From 1d9473b88e010d4ed25c7086b0ae8b00321557b3 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 17 May 2023 20:48:41 +0200 Subject: arm64: dts: qcom: apq8016-sbc: Fix 1.8V power rail on LS expansion [ Upstream commit 5500f823db38db073d30557af159b77fb1f2bf26 ] The 96Boards specification expects a 1.8V power rail on the low-speed expansion connector that is able to provide at least 0.18W / 100 mA. According to the DB410c hardware user manual this is done by connecting both L15 and L16 in parallel with up to 55mA each (for 110 mA total) [1]. Unfortunately the current regulator setup in the DB410c device tree does not implement the specification correctly and only provides 5 mA: - Only L15 is marked always-on, so L16 is never enabled. - Without specifying a load the regulator is put into LPM where it can only provide 5 mA. Fix this by: - Adding proper voltage constraints for L16. - Making L16 always-on. - Adding regulator-system-load for both L15 and L16. 100 mA should be available in total, so specify 50 mA for each. (The regulator hardware can only be in normal (55 mA) or low-power mode (5 mA) so this will actually result in the expected 110 mA total...) [1]: https://www.96boards.org/documentation/consumer/dragonboard/dragonboard410c/hardware-docs/hardware-user-manual.md.html#power-supplies Cc: Srinivas Kandagatla Fixes: 828dd5d66f0f ("arm64: dts: apq8016-sbc: make 1.8v available on LS expansion") Signed-off-by: Stephan Gerhold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230510-msm8916-regulators-v1-2-54d4960a05fc@gerhold.net Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/apq8016-sbc.dts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dts b/arch/arm64/boot/dts/qcom/apq8016-sbc.dts index c6f4ee7b8204..e3e90ad92cc5 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dts +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dts @@ -527,19 +527,27 @@ regulator-max-microvolt = <3300000>; }; - /** - * 1.8v required on LS expansion - * for mezzanine boards + /* + * The 96Boards specification expects a 1.8V power rail on the low-speed + * expansion connector that is able to provide at least 0.18W / 100 mA. + * L15/L16 are connected in parallel to provide 55 mA each. A minimum load + * must be specified to ensure the regulators are not put in LPM where they + * would only provide 5 mA. */ l15 { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + regulator-system-load = <50000>; + regulator-allow-set-load; regulator-always-on; }; l16 { regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; + regulator-max-microvolt = <1800000>; + regulator-system-load = <50000>; + regulator-allow-set-load; + regulator-always-on; }; l17 { -- cgit 1.4.1 From 5044e5f2511c9afdf9880d2bb6b9d37dfc345dac Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 5 Dec 2022 17:33:26 +0000 Subject: drm/bridge: Introduce pre_enable_prev_first to alter bridge init order [ Upstream commit 4fb912e5e19075874379cfcf074d90bd51ebf8ea ] DSI sink devices typically want the DSI host powered up and configured before they are powered up. pre_enable is the place this would normally happen, but they are called in reverse order from panel/connector towards the encoder, which is the "wrong" order. Add a new flag pre_enable_prev_first that any bridge can set to swap the order of pre_enable (and post_disable) for that and the immediately previous bridge. Should the immediately previous bridge also set the pre_enable_prev_first flag, the previous bridge to that will be called before either of those which requested pre_enable_prev_first. eg: - Panel - Bridge 1 - Bridge 2 pre_enable_prev_first - Bridge 3 - Bridge 4 pre_enable_prev_first - Bridge 5 pre_enable_prev_first - Bridge 6 - Encoder Would result in pre_enable's being called as Panel, Bridge 1, Bridge 3, Bridge 2, Bridge 6, Bridge 5, Bridge 4, Encoder. Signed-off-by: Dave Stevenson Tested-by: Frieder Schrempf Reviewed-by: Frieder Schrempf Link: https://lore.kernel.org/r/20221205173328.1395350-5-dave.stevenson@raspberrypi.com Signed-off-by: Maxime Ripard Stable-dep-of: dd9e329af723 ("drm/bridge: ti-sn65dsi83: Fix enable/disable flow to meet spec") Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_bridge.c | 145 ++++++++++++++++++++++++++++++++++++------- include/drm/drm_bridge.h | 8 +++ 2 files changed, 129 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 1545c50fd1c8..7044e339a82c 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -691,6 +691,25 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); +static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *old_state) +{ + if (old_state && bridge->funcs->atomic_post_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_post_disable(bridge, + old_bridge_state); + } else if (bridge->funcs->post_disable) { + bridge->funcs->post_disable(bridge); + } +} + /** * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges * in the encoder chain @@ -702,36 +721,86 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); * starting from the first bridge to the last. These are called after completing * &drm_encoder_helper_funcs.atomic_disable * + * If a bridge sets @pre_enable_prev_first, then the @post_disable for that + * bridge will be called before the previous one to reverse the @pre_enable + * calling direction. + * * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, struct drm_atomic_state *old_state) { struct drm_encoder *encoder; + struct drm_bridge *next, *limit; if (!bridge) return; encoder = bridge->encoder; + list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_post_disable) { - struct drm_bridge_state *old_bridge_state; + limit = NULL; + + if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) { + next = list_next_entry(bridge, chain_node); + + if (next->pre_enable_prev_first) { + /* next bridge had requested that prev + * was enabled first, so disabled last + */ + limit = next; + + /* Find the next bridge that has NOT requested + * prev to be enabled first / disabled last + */ + list_for_each_entry_from(next, &encoder->bridge_chain, + chain_node) { + if (next->pre_enable_prev_first) { + next = list_prev_entry(next, chain_node); + limit = next; + break; + } + } + + /* Call these bridges in reverse order */ + list_for_each_entry_from_reverse(next, &encoder->bridge_chain, + chain_node) { + if (next == bridge) + break; + + drm_atomic_bridge_call_post_disable(next, + old_state); + } + } + } - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - bridge); - if (WARN_ON(!old_bridge_state)) - return; + drm_atomic_bridge_call_post_disable(bridge, old_state); - bridge->funcs->atomic_post_disable(bridge, - old_bridge_state); - } else if (bridge->funcs->post_disable) { - bridge->funcs->post_disable(bridge); - } + if (limit) + /* Jump all bridges that we have already post_disabled */ + bridge = limit; } } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); +static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *old_state) +{ + if (old_state && bridge->funcs->atomic_pre_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_pre_enable(bridge, old_bridge_state); + } else if (bridge->funcs->pre_enable) { + bridge->funcs->pre_enable(bridge); + } +} + /** * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in * the encoder chain @@ -743,32 +812,60 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); * starting from the last bridge to the first. These are called before calling * &drm_encoder_helper_funcs.atomic_enable * + * If a bridge sets @pre_enable_prev_first, then the pre_enable for the + * prev bridge will be called before pre_enable of this bridge. + * * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *old_state) { struct drm_encoder *encoder; - struct drm_bridge *iter; + struct drm_bridge *iter, *next, *limit; if (!bridge) return; encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_pre_enable) { - struct drm_bridge_state *old_bridge_state; + if (iter->pre_enable_prev_first) { + next = iter; + limit = bridge; + list_for_each_entry_from_reverse(next, + &encoder->bridge_chain, + chain_node) { + if (next == bridge) + break; + + if (!next->pre_enable_prev_first) { + /* Found first bridge that does NOT + * request prev to be enabled first + */ + limit = list_prev_entry(next, chain_node); + break; + } + } + + list_for_each_entry_from(next, &encoder->bridge_chain, chain_node) { + /* Call requested prev bridge pre_enable + * in order. + */ + if (next == iter) + /* At the first bridge to request prev + * bridges called first. + */ + break; + + drm_atomic_bridge_call_pre_enable(next, old_state); + } + } - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - iter); - if (WARN_ON(!old_bridge_state)) - return; + drm_atomic_bridge_call_pre_enable(iter, old_state); - iter->funcs->atomic_pre_enable(iter, old_bridge_state); - } else if (iter->funcs->pre_enable) { - iter->funcs->pre_enable(iter); - } + if (iter->pre_enable_prev_first) + /* Jump all bridges that we have already pre_enabled */ + iter = limit; if (iter == bridge) break; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 288c6feda5de..6b656ea23b96 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -768,6 +768,14 @@ struct drm_bridge { * modes. */ bool interlace_allowed; + /** + * @pre_enable_prev_first: The bridge requires that the prev + * bridge @pre_enable function is called before its @pre_enable, + * and conversely for post_disable. This is most frequently a + * requirement for DSI devices which need the host to be initialised + * before the peripheral. + */ + bool pre_enable_prev_first; /** * @ddc: Associated I2C adapter for DDC access, if any. */ -- cgit 1.4.1 From 25a724c2fa3df4d9ab5576d5c37f65b6900c1da5 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Wed, 3 May 2023 18:33:07 +0200 Subject: drm/bridge: ti-sn65dsi83: Fix enable/disable flow to meet spec [ Upstream commit dd9e329af7236e34c566d3705ea32a63069b9b13 ] The datasheet describes the following initialization flow including minimum delay times between each step: 1. DSI data lanes need to be in LP-11 and the clock lane in HS mode 2. toggle EN signal 3. initialize registers 4. enable PLL 5. soft reset 6. enable DSI stream 7. check error status register To meet this requirement we need to make sure the host bridge's pre_enable() is called first by using the pre_enable_prev_first flag. Furthermore we need to split enable() into pre_enable() which covers steps 2-5 from above and enable() which covers step 7 and is called after the host bridge's enable(). Signed-off-by: Frieder Schrempf Fixes: ceb515ba29ba ("drm/bridge: ti-sn65dsi83: Add TI SN65DSI83 and SN65DSI84 driver") Tested-by: Alexander Stein #TQMa8MxML/MBa8Mx Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20230503163313.2640898-3-frieder@fris.de Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 0635b824e368..55efd3eb6672 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -321,8 +321,8 @@ static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx) return dsi_div - 1; } -static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) +static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); struct drm_atomic_state *state = old_bridge_state->base.state; @@ -485,11 +485,22 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, /* Trigger reset after CSR register update. */ regmap_write(ctx->regmap, REG_RC_RESET, REG_RC_RESET_SOFT_RESET); + /* Wait for 10ms after soft reset as specified in datasheet */ + usleep_range(10000, 12000); +} + +static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); + unsigned int pval; + /* Clear all errors that got asserted during initialization. */ regmap_read(ctx->regmap, REG_IRQ_STAT, &pval); regmap_write(ctx->regmap, REG_IRQ_STAT, pval); - usleep_range(10000, 12000); + /* Wait for 1ms and check for errors in status register */ + usleep_range(1000, 1100); regmap_read(ctx->regmap, REG_IRQ_STAT, &pval); if (pval) dev_err(ctx->dev, "Unexpected link status 0x%02x\n", pval); @@ -556,6 +567,7 @@ static const struct drm_bridge_funcs sn65dsi83_funcs = { .attach = sn65dsi83_attach, .detach = sn65dsi83_detach, .atomic_enable = sn65dsi83_atomic_enable, + .atomic_pre_enable = sn65dsi83_atomic_pre_enable, .atomic_disable = sn65dsi83_atomic_disable, .mode_valid = sn65dsi83_mode_valid, @@ -696,6 +708,7 @@ static int sn65dsi83_probe(struct i2c_client *client, ctx->bridge.funcs = &sn65dsi83_funcs; ctx->bridge.of_node = dev->of_node; + ctx->bridge.pre_enable_prev_first = true; drm_bridge_add(&ctx->bridge); ret = sn65dsi83_host_attach(ctx); -- cgit 1.4.1 From deda0761dc6161f03278da4679d96d4727992e91 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Tue, 16 May 2023 10:50:39 +0200 Subject: drm/panel: simple: fix active size for Ampire AM-480272H3TMQW-T01H [ Upstream commit f24b49550814fdee4a98b9552e35e243ccafd4a8 ] The previous setting was related to the overall dimension and not to the active display area. In the "PHYSICAL SPECIFICATIONS" section, the datasheet shows the following parameters: ---------------------------------------------------------- | Item | Specifications | unit | ---------------------------------------------------------- | Display area | 98.7 (W) x 57.5 (H) | mm | ---------------------------------------------------------- | Overall dimension | 105.5(W) x 67.2(H) x 4.96(D) | mm | ---------------------------------------------------------- Fixes: 966fea78adf2 ("drm/panel: simple: Add support for Ampire AM-480272H3TMQW-T01H") Signed-off-by: Dario Binacchi Reviewed-by: Neil Armstrong [narmstrong: fixed Fixes commit id length] Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20230516085039.3797303-1-dario.binacchi@amarulasolutions.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panel/panel-simple.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 8a3b685c2fcc..7ca00b032336 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -759,8 +759,8 @@ static const struct panel_desc ampire_am_480272h3tmqw_t01h = { .num_modes = 1, .bpc = 8, .size = { - .width = 105, - .height = 67, + .width = 99, + .height = 58, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; -- cgit 1.4.1 From e91ffbd6553348cdd3d04b263f8207d919681fac Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 17:30:58 +0200 Subject: ARM: ep93xx: fix missing-prototype warnings [ Upstream commit 419013740ea1e4343d8ade535d999f59fa28e460 ] ep93xx_clocksource_read() is only called from the file it is declared in, while ep93xx_timer_init() is declared in a header that is not included here. arch/arm/mach-ep93xx/timer-ep93xx.c:120:13: error: no previous prototype for 'ep93xx_timer_init' arch/arm/mach-ep93xx/timer-ep93xx.c:63:5: error: no previous prototype for 'ep93xx_clocksource_read' Fixes: 000bc17817bf ("ARM: ep93xx: switch to GENERIC_CLOCKEVENTS") Acked-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20230516153109.514251-3-arnd@kernel.org Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/mach-ep93xx/timer-ep93xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ep93xx/timer-ep93xx.c b/arch/arm/mach-ep93xx/timer-ep93xx.c index dd4b164d1831..a9efa7bc2fa1 100644 --- a/arch/arm/mach-ep93xx/timer-ep93xx.c +++ b/arch/arm/mach-ep93xx/timer-ep93xx.c @@ -9,6 +9,7 @@ #include #include #include "soc.h" +#include "platform.h" /************************************************************************* * Timer handling for EP93xx @@ -60,7 +61,7 @@ static u64 notrace ep93xx_read_sched_clock(void) return ret; } -u64 ep93xx_clocksource_read(struct clocksource *c) +static u64 ep93xx_clocksource_read(struct clocksource *c) { u64 ret; -- cgit 1.4.1 From c63997426da6f24f33ae6caf2423170d5bb80ebb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 17:31:04 +0200 Subject: ARM: omap2: fix missing tick_broadcast() prototype [ Upstream commit 861bc1d2886d47bd57a2cbf2cda87fdbe3eb9d08 ] omap2 contains a hack to define tick_broadcast() on non-SMP configurations in place of the normal SMP definition. This one causes a warning because of a missing prototype: arch/arm/mach-omap2/board-generic.c:44:6: error: no previous prototype for 'tick_broadcast' Make sure to always include the header with the declaration. Fixes: d86ad463d670 ("ARM: OMAP2+: Fix regression for using local timer on non-SMP SoCs") Acked-by: Aaro Koskinen Link: https://lore.kernel.org/r/20230516153109.514251-9-arnd@kernel.org Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/mach-omap2/board-generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index 1610c567a6a3..10d2f078e4a8 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include -- cgit 1.4.1 From 75c019119ebcc919e717fbce5552c2a0908405cf Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 7 Apr 2023 09:45:44 +0200 Subject: arm64: dts: qcom: pm7250b: add missing spmi-vadc include [ Upstream commit 83022f6484b11a60dbf9a95a88c7ef8e59c4b19c ] This file is using definitions from the spmi-vadc header, so we need to include it. Fixes: 11975b9b8135 ("arm64: dts: qcom: Add pm7250b PMIC") Signed-off-by: Luca Weiss Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230407-pm7250b-sid-v1-1-fc648478cc25@fairphone.com Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/pm7250b.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/qcom/pm7250b.dtsi b/arch/arm64/boot/dts/qcom/pm7250b.dtsi index 61f7a6345150..694fe912536e 100644 --- a/arch/arm64/boot/dts/qcom/pm7250b.dtsi +++ b/arch/arm64/boot/dts/qcom/pm7250b.dtsi @@ -3,6 +3,7 @@ * Copyright (C) 2022 Luca Weiss */ +#include #include #include -- cgit 1.4.1 From 1bdb9751b4c64f5709cb3608fd93a709c4e9b2e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 7 May 2023 19:45:16 +0200 Subject: arm64: dts: qcom: apq8096: fix fixed regulator name property [ Upstream commit c77612a07d18d4425fd8ddd532a8a9b8e1970c53 ] Correct the typo in 'regulator-name' property. apq8096-ifc6640.dtb: v1p05-regulator: 'regulator-name' is a required property apq8096-ifc6640.dtb: v1p05-regulator: Unevaluated properties are not allowed ('reglator-name' was unexpected) Fixes: 6cbdec2d3ca6 ("arm64: dts: qcom: msm8996: Introduce IFC6640") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230507174516.264936-3-krzysztof.kozlowski@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/apq8096-ifc6640.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/apq8096-ifc6640.dts b/arch/arm64/boot/dts/qcom/apq8096-ifc6640.dts index 92f264891d84..9b20c1a47a18 100644 --- a/arch/arm64/boot/dts/qcom/apq8096-ifc6640.dts +++ b/arch/arm64/boot/dts/qcom/apq8096-ifc6640.dts @@ -26,7 +26,7 @@ v1p05: v1p05-regulator { compatible = "regulator-fixed"; - reglator-name = "v1p05"; + regulator-name = "v1p05"; regulator-always-on; regulator-boot-on; @@ -38,7 +38,7 @@ v12_poe: v12-poe-regulator { compatible = "regulator-fixed"; - reglator-name = "v12_poe"; + regulator-name = "v12_poe"; regulator-always-on; regulator-boot-on; -- cgit 1.4.1 From 666be7fef4d39d67c041460e29ddf2b875101133 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 15 May 2023 13:13:52 -0700 Subject: arm64: dts: mediatek: mt8183: Add mediatek,broken-save-restore-fw to kukui [ Upstream commit 42127f578ebde652d1373e0233356fbd351675c4 ] Firmware shipped on mt8183 Chromebooks is affected by the GICR save/restore issue as described by the patch ("dt-bindings: interrupt-controller: arm,gic-v3: Add quirk for Mediatek SoCs w/ broken FW"). Add the quirk property. Fixes: cd894e274b74 ("arm64: dts: mt8183: Add krane-sku176 board") Reviewed-by: Julius Werner Signed-off-by: Douglas Anderson Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230515131353.v2.3.I525a2ed4260046d43c885ee1275e91707743df1c@changeid Signed-off-by: Matthias Brugger Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi b/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi index b4b86bb1f1a7..632fd89e7596 100644 --- a/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi @@ -297,6 +297,10 @@ }; }; +&gic { + mediatek,broken-save-restore-fw; +}; + &gpu { mali-supply = <&mt6358_vgpu_reg>; sram-supply = <&mt6358_vsram_gpu_reg>; -- cgit 1.4.1 From 09722ac9f1e557ea65098202d0b18336c3f04420 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 18 May 2023 02:42:32 +0200 Subject: ARM: dts: stm32: Shorten the AV96 HDMI sound card name [ Upstream commit 0cf765e598712addec34d0208cc1418c151fefb2 ] Fix the following error in kernel log due to too long sound card name: " asoc-audio-graph-card sound: ASoC: driver name too long 'STM32MP1-AV96-HDMI' -> 'STM32MP1-AV96-H' " Fixes: e027da342772 ("ARM: dts: stm32: Add bindings for audio on AV96") Signed-off-by: Marek Vasut Signed-off-by: Alexandre Torgue Signed-off-by: Sasha Levin --- arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi index 2c246ac64153..f068e4fcc404 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi @@ -88,7 +88,7 @@ sound { compatible = "audio-graph-card"; - label = "STM32MP1-AV96-HDMI"; + label = "STM32-AV96-HDMI"; dais = <&sai2a_port>; status = "okay"; }; -- cgit 1.4.1 From 9f79e638d45100dad43c56ad9b47eaff1b98fe9a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 13 May 2023 13:29:31 +0200 Subject: memory: brcmstb_dpfe: fix testing array offset after use [ Upstream commit 1d9e93fad549bc38f593147479ee063f2872c170 ] Code should first check for valid value of array offset, then use it as the index. Fixes smatch warning: drivers/memory/brcmstb_dpfe.c:443 __send_command() error: testing array offset 'cmd' after use. Fixes: 2f330caff577 ("memory: brcmstb: Add driver for DPFE") Acked-by: Markus Mayer Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20230513112931.176066-1-krzysztof.kozlowski@linaro.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/memory/brcmstb_dpfe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/memory/brcmstb_dpfe.c b/drivers/memory/brcmstb_dpfe.c index 76c82e9c8fce..9339f80b21c5 100644 --- a/drivers/memory/brcmstb_dpfe.c +++ b/drivers/memory/brcmstb_dpfe.c @@ -434,15 +434,17 @@ static void __finalize_command(struct brcmstb_dpfe_priv *priv) static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd, u32 result[]) { - const u32 *msg = priv->dpfe_api->command[cmd]; void __iomem *regs = priv->regs; unsigned int i, chksum, chksum_idx; + const u32 *msg; int ret = 0; u32 resp; if (cmd >= DPFE_CMD_MAX) return -1; + msg = priv->dpfe_api->command[cmd]; + mutex_lock(&priv->lock); /* Wait for DCPU to become ready */ -- cgit 1.4.1 From c02f27c2950abfed58bd8aa6bf50e79d9cc1fc77 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 7 May 2023 22:07:33 +0300 Subject: ARM: dts: qcom: apq8074-dragonboard: Set DMA as remotely controlled [ Upstream commit e60c230588d88036f974cec7e93361e2c4f62226 ] Add the qcom,controlled-remotely property for the blsp2_bam controller node. This board requires this, otherwise the board stalls during the boot for some reason (most probably because TZ mishandles the protection error and keeps on looping somewhere inside). Fixes: 62bc81792223 dts: msm8974: Add blsp2_bam dma node Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230507190735.2333145-3-dmitry.baryshkov@linaro.org Signed-off-by: Sasha Levin --- arch/arm/boot/dts/qcom-apq8074-dragonboard.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts index 91716298ec5e..be1ab7eff8ff 100644 --- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts +++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts @@ -23,6 +23,10 @@ status = "okay"; }; +&blsp2_dma { + qcom,controlled-remotely; +}; + &blsp2_i2c5 { status = "okay"; clock-frequency = <200000>; -- cgit 1.4.1 From 3b575d93020f20f6a711efd8c69bfed26837d694 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 30 May 2023 21:11:38 +0300 Subject: ASoC: es8316: Increment max value for ALC Capture Target Volume control [ Upstream commit 6f073429037cd79d7311cd8236311c53f5ea8f01 ] The following error occurs when trying to restore a previously saved ALSA mixer state (tested on a Rock 5B board): $ alsactl --no-ucm -f /tmp/asound.state store hw:Analog $ alsactl --no-ucm -I -f /tmp/asound.state restore hw:Analog alsactl: set_control:1475: Cannot write control '2:0:0:ALC Capture Target Volume:0' : Invalid argument According to ES8316 datasheet, the register at address 0x2B, which is related to the above mixer control, contains by default the value 0xB0. Considering the corresponding ALC target bits (ALCLVL) are 7:4, the control is initialized with 11, which is one step above the maximum value allowed by the driver: ALCLVL | dB gain -------+-------- 0000 | -16.5 0001 | -15.0 0010 | -13.5 .... | ..... 0111 | -6.0 1000 | -4.5 1001 | -3.0 1010 | -1.5 .... | ..... 1111 | -1.5 The tests performed using the VU meter feature (--vumeter=TYPE) of arecord/aplay confirm the specs are correct and there is no measured gain if the 1011-1111 range would have been mapped to 0 dB: dB gain | VU meter % --------+----------- -6.0 | 30-31 -4.5 | 35-36 -3.0 | 42-43 -1.5 | 50-51 0.0 | 50-51 Increment the max value allowed for ALC Capture Target Volume control, so that it matches the hardware default. Additionally, update the related TLV to prevent an artificial extension of the dB gain range. Fixes: b8b88b70875a ("ASoC: add es8316 codec driver") Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/20230530181140.483936-2-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/es8316.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index f7d7a9c91e04..2bfcd9af3917 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -52,7 +52,12 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0); -static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv, + 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0), + 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 0), +); + static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv, 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0), 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0), @@ -115,7 +120,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = { alc_max_gain_tlv), SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0, alc_min_gain_tlv), - SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 10, 0, + SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 11, 0, alc_target_tlv), SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0), SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0), -- cgit 1.4.1 From 863054be8d4d2c9b38985371166a37c0e14111e1 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 30 May 2023 21:11:39 +0300 Subject: ASoC: es8316: Do not set rate constraints for unsupported MCLKs [ Upstream commit 60413129ee2b38a80347489270af7f6e1c1de4d0 ] When using the codec through the generic audio graph card, there are at least two calls of es8316_set_dai_sysclk(), with the effect of limiting the allowed sample rates according to the MCLK/LRCK ratios supported by the codec: 1. During audio card setup, to set the initial MCLK - see asoc_simple_init_dai(). 2. Before opening a stream, to update MCLK, according to the stream sample rate and the multiplication factor - see asoc_simple_hw_params(). In some cases the initial MCLK might be set to a frequency that doesn't match any of the supported ratios, e.g. 12287999 instead of 12288000, which is only 1 Hz below the supported clock, as that is what the hardware reports. This creates an empty list of rate constraints, which is further passed to snd_pcm_hw_constraint_list() via es8316_pcm_startup(), and causes the following error on the very first access of the sound card: $ speaker-test -D hw:Analog,0 -F S16_LE -c 2 -t wav Broken configuration for playback: no configurations available: Invalid argument Setting of hwparams failed: Invalid argument Note that all subsequent retries succeed thanks to the updated MCLK set at point 2 above, which uses a computed frequency value instead of a reading from the hardware registers. Normally this would have mitigated the issue, but es8316_pcm_startup() executes before the 2nd call to es8316_set_dai_sysclk(), hence it cannot make use of the updated constraints. Since es8316_pcm_hw_params() performs anyway a final validation of MCLK against the stream sample rate and the supported MCLK/LRCK ratios, fix the issue by ensuring that sysclk_constraints list is only set when at least one supported sample rate is autodetected by the codec. Fixes: b8b88b70875a ("ASoC: add es8316 codec driver") Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/20230530181140.483936-3-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/es8316.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 2bfcd9af3917..87775378362e 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -369,13 +369,11 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, int count = 0; es8316->sysclk = freq; + es8316->sysclk_constraints.list = NULL; + es8316->sysclk_constraints.count = 0; - if (freq == 0) { - es8316->sysclk_constraints.list = NULL; - es8316->sysclk_constraints.count = 0; - + if (freq == 0) return 0; - } ret = clk_set_rate(es8316->mclk, freq); if (ret) @@ -391,8 +389,10 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, es8316->allowed_rates[count++] = freq / ratio; } - es8316->sysclk_constraints.list = es8316->allowed_rates; - es8316->sysclk_constraints.count = count; + if (count) { + es8316->sysclk_constraints.list = es8316->allowed_rates; + es8316->sysclk_constraints.count = count; + } return 0; } -- cgit 1.4.1 From b2194d7dfc95a404990da73ebd394f6f6946a4a0 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 16 May 2023 22:30:29 +0200 Subject: ARM: dts: meson8: correct uart_B and uart_C clock references [ Upstream commit 98b503c7fb13a17a47d8ebf15fa8f7c10118e75c ] On Meson8 uart_B and uart_C do not work, because they are relying on incorrect clocks. Change the references of pclk to the correct CLKID (UART1 for uart_B and UART2 for uart_C), to allow use of the two uarts. This was originally reported by Hans-Frieder Vogt for Meson8b [0], but the same bug is also present in meson8.dtsi [0] https://lore.kernel.org/linux-amlogic/trinity-bf20bcb9-790b-4ab9-99e3-0831ef8257f4-1680878185420@3c-app-gmx-bap55/ Fixes: 57007bfb5469 ("ARM: dts: meson8: Fix the UART device-tree schema validation") Reported-by: Hans-Frieder Vogt # for meson8b.dtsi Signed-off-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20230516203029.1031174-1-martin.blumenstingl@googlemail.com Signed-off-by: Neil Armstrong Signed-off-by: Sasha Levin --- arch/arm/boot/dts/meson8.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/meson8.dtsi b/arch/arm/boot/dts/meson8.dtsi index 0f8bac8bac8b..f9da7faa2186 100644 --- a/arch/arm/boot/dts/meson8.dtsi +++ b/arch/arm/boot/dts/meson8.dtsi @@ -749,13 +749,13 @@ &uart_B { compatible = "amlogic,meson8-uart"; - clocks = <&xtal>, <&clkc CLKID_UART0>, <&clkc CLKID_CLK81>; + clocks = <&xtal>, <&clkc CLKID_UART1>, <&clkc CLKID_CLK81>; clock-names = "xtal", "pclk", "baud"; }; &uart_C { compatible = "amlogic,meson8-uart"; - clocks = <&xtal>, <&clkc CLKID_UART0>, <&clkc CLKID_CLK81>; + clocks = <&xtal>, <&clkc CLKID_UART2>, <&clkc CLKID_CLK81>; clock-names = "xtal", "pclk", "baud"; }; -- cgit 1.4.1 From 750f0a302a10dc2327a6656860a22b7da7251cea Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 21 May 2023 15:52:16 -0700 Subject: soc/fsl/qe: fix usb.c build errors [ Upstream commit 7b1a78babd0d2cd27aa07255dee0c2d7ac0f31e3 ] Fix build errors in soc/fsl/qe/usb.c when QUICC_ENGINE is not set. This happens when PPC_EP88XC is set, which selects CPM1 & CPM. When CPM is set, USB_FSL_QE can be set without QUICC_ENGINE being set. When USB_FSL_QE is set, QE_USB deafults to y, which causes build errors when QUICC_ENGINE is not set. Making QE_USB depend on QUICC_ENGINE prevents QE_USB from defaulting to y. Fixes these build errors: drivers/soc/fsl/qe/usb.o: in function `qe_usb_clock_set': usb.c:(.text+0x1e): undefined reference to `qe_immr' powerpc-linux-ld: usb.c:(.text+0x2a): undefined reference to `qe_immr' powerpc-linux-ld: usb.c:(.text+0xbc): undefined reference to `qe_setbrg' powerpc-linux-ld: usb.c:(.text+0xca): undefined reference to `cmxgcr_lock' powerpc-linux-ld: usb.c:(.text+0xce): undefined reference to `cmxgcr_lock' Fixes: 5e41486c408e ("powerpc/QE: add support for QE USB clocks routing") Signed-off-by: Randy Dunlap Reported-by: kernel test robot Link: https://lore.kernel.org/all/202301101500.pillNv6R-lkp@intel.com/ Suggested-by: Michael Ellerman Cc: Christophe Leroy Cc: Leo Li Cc: Masahiro Yamada Cc: Nicolas Schier Cc: Qiang Zhao Cc: linuxppc-dev Cc: linux-arm-kernel@lists.infradead.org Cc: Kumar Gala Acked-by: Nicolas Schier Signed-off-by: Li Yang Signed-off-by: Sasha Levin --- drivers/soc/fsl/qe/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index 357c5800b112..7afa796dbbb8 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -39,6 +39,7 @@ config QE_TDM config QE_USB bool + depends on QUICC_ENGINE default y if USB_FSL_QE help QE USB Controller support -- cgit 1.4.1 From ebec507398e11b1c25ce9fb05fb509878233051c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 May 2023 13:18:45 +0200 Subject: RDMA/irdma: avoid fortify-string warning in irdma_clr_wqes [ Upstream commit b002760f877c0d91ecd3c78565b52f4bbac379dd ] Commit df8fc4e934c1 ("kbuild: Enable -fstrict-flex-arrays=3") triggers a warning for fortified memset(): In function 'fortify_memset_chk', inlined from 'irdma_clr_wqes' at drivers/infiniband/hw/irdma/uk.c:103:4: include/linux/fortify-string.h:493:25: error: call to '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror=attribute-warning] 493 | __write_overflow_field(p_size_field, size); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The problem here isthat the inner array only has four 8-byte elements, so clearing 4096 bytes overflows that. As this structure is part of an outer array, change the code to pass a pointer to the irdma_qp_quanta instead, and change the size argument for readability, matching the comment above it. Fixes: 551c46edc769 ("RDMA/irdma: Add user/kernel shared libraries") Link: https://lore.kernel.org/r/20230523111859.2197825-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Acked-by: Shiraz Saleem Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/uk.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/irdma/uk.c b/drivers/infiniband/hw/irdma/uk.c index 16183e894da7..dd428d915c17 100644 --- a/drivers/infiniband/hw/irdma/uk.c +++ b/drivers/infiniband/hw/irdma/uk.c @@ -93,16 +93,18 @@ static int irdma_nop_1(struct irdma_qp_uk *qp) */ void irdma_clr_wqes(struct irdma_qp_uk *qp, u32 qp_wqe_idx) { - __le64 *wqe; + struct irdma_qp_quanta *sq; u32 wqe_idx; if (!(qp_wqe_idx & 0x7F)) { wqe_idx = (qp_wqe_idx + 128) % qp->sq_ring.size; - wqe = qp->sq_base[wqe_idx].elem; + sq = qp->sq_base + wqe_idx; if (wqe_idx) - memset(wqe, qp->swqe_polarity ? 0 : 0xFF, 0x1000); + memset(sq, qp->swqe_polarity ? 0 : 0xFF, + 128 * sizeof(*sq)); else - memset(wqe, qp->swqe_polarity ? 0xFF : 0, 0x1000); + memset(sq, qp->swqe_polarity ? 0xFF : 0, + 128 * sizeof(*sq)); } } -- cgit 1.4.1 From b99395ab605fb0570d1e62c9459425ac6fc58d46 Mon Sep 17 00:00:00 2001 From: Brendan Cunningham Date: Fri, 19 May 2023 12:32:16 -0400 Subject: IB/hfi1: Fix wrong mmu_node used for user SDMA packet after invalidate [ Upstream commit c9358de193ecfb360c3ce75f27ce839ca0b0bc8c ] The hfi1 user SDMA pinned-page cache will leave a stale cache entry when the cache-entry's virtual address range is invalidated but that cache entry is in-use by an outstanding SDMA request. Subsequent user SDMA requests with buffers in or spanning the virtual address range of the stale cache entry will result in packets constructed from the wrong memory, the physical pages pointed to by the stale cache entry. To fix this, remove mmu_rb_node cache entries from the mmu_rb_handler cache independent of the cache entry's refcount. Add 'struct kref refcount' to struct mmu_rb_node and manage mmu_rb_node lifetime with kref_get() and kref_put(). mmu_rb_node.refcount makes sdma_mmu_node.refcount redundant. Remove 'atomic_t refcount' from struct sdma_mmu_node and change sdma_mmu_node code to use mmu_rb_node.refcount. Move the mmu_rb_handler destructor call after a wait-for-SDMA-request-completion call so mmu_rb_nodes that need mmu_rb_handler's workqueue to queue themselves up for destruction from an interrupt context may do so. Fixes: f48ad614c100 ("IB/hfi1: Move driver out of staging") Fixes: 00cbce5cbf88 ("IB/hfi1: Fix bugs with non-PAGE_SIZE-end multi-iovec user SDMA requests") Link: https://lore.kernel.org/r/168451393605.3700681.13493776139032178861.stgit@awfm-02.cornelisnetworks.com Reviewed-by: Dean Luick Signed-off-by: Brendan Cunningham Signed-off-by: Dennis Dalessandro Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hfi1/ipoib_tx.c | 4 +- drivers/infiniband/hw/hfi1/mmu_rb.c | 101 ++++++++++++++--------- drivers/infiniband/hw/hfi1/mmu_rb.h | 3 + drivers/infiniband/hw/hfi1/sdma.c | 23 ++++-- drivers/infiniband/hw/hfi1/sdma.h | 47 +++++++---- drivers/infiniband/hw/hfi1/sdma_txreq.h | 2 + drivers/infiniband/hw/hfi1/user_sdma.c | 137 +++++++++++++------------------- drivers/infiniband/hw/hfi1/user_sdma.h | 1 - drivers/infiniband/hw/hfi1/vnic_sdma.c | 4 +- 9 files changed, 177 insertions(+), 145 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/ipoib_tx.c b/drivers/infiniband/hw/hfi1/ipoib_tx.c index 8973a081d641..e7d831330278 100644 --- a/drivers/infiniband/hw/hfi1/ipoib_tx.c +++ b/drivers/infiniband/hw/hfi1/ipoib_tx.c @@ -215,11 +215,11 @@ static int hfi1_ipoib_build_ulp_payload(struct ipoib_txreq *tx, const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ret = sdma_txadd_page(dd, - NULL, txreq, skb_frag_page(frag), frag->bv_offset, - skb_frag_size(frag)); + skb_frag_size(frag), + NULL, NULL, NULL); if (unlikely(ret)) break; } diff --git a/drivers/infiniband/hw/hfi1/mmu_rb.c b/drivers/infiniband/hw/hfi1/mmu_rb.c index 71b9ac018887..94f170166730 100644 --- a/drivers/infiniband/hw/hfi1/mmu_rb.c +++ b/drivers/infiniband/hw/hfi1/mmu_rb.c @@ -19,8 +19,7 @@ static int mmu_notifier_range_start(struct mmu_notifier *, const struct mmu_notifier_range *); static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *, unsigned long, unsigned long); -static void do_remove(struct mmu_rb_handler *handler, - struct list_head *del_list); +static void release_immediate(struct kref *refcount); static void handle_remove(struct work_struct *work); static const struct mmu_notifier_ops mn_opts = { @@ -103,7 +102,11 @@ void hfi1_mmu_rb_unregister(struct mmu_rb_handler *handler) } spin_unlock_irqrestore(&handler->lock, flags); - do_remove(handler, &del_list); + while (!list_empty(&del_list)) { + rbnode = list_first_entry(&del_list, struct mmu_rb_node, list); + list_del(&rbnode->list); + kref_put(&rbnode->refcount, release_immediate); + } /* Now the mm may be freed. */ mmdrop(handler->mn.mm); @@ -131,12 +134,6 @@ int hfi1_mmu_rb_insert(struct mmu_rb_handler *handler, } __mmu_int_rb_insert(mnode, &handler->root); list_add_tail(&mnode->list, &handler->lru_list); - - ret = handler->ops->insert(handler->ops_arg, mnode); - if (ret) { - __mmu_int_rb_remove(mnode, &handler->root); - list_del(&mnode->list); /* remove from LRU list */ - } mnode->handler = handler; unlock: spin_unlock_irqrestore(&handler->lock, flags); @@ -180,6 +177,48 @@ static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *handler, return node; } +/* + * Must NOT call while holding mnode->handler->lock. + * mnode->handler->ops->remove() may sleep and mnode->handler->lock is a + * spinlock. + */ +static void release_immediate(struct kref *refcount) +{ + struct mmu_rb_node *mnode = + container_of(refcount, struct mmu_rb_node, refcount); + mnode->handler->ops->remove(mnode->handler->ops_arg, mnode); +} + +/* Caller must hold mnode->handler->lock */ +static void release_nolock(struct kref *refcount) +{ + struct mmu_rb_node *mnode = + container_of(refcount, struct mmu_rb_node, refcount); + list_move(&mnode->list, &mnode->handler->del_list); + queue_work(mnode->handler->wq, &mnode->handler->del_work); +} + +/* + * struct mmu_rb_node->refcount kref_put() callback. + * Adds mmu_rb_node to mmu_rb_node->handler->del_list and queues + * handler->del_work on handler->wq. + * Does not remove mmu_rb_node from handler->lru_list or handler->rb_root. + * Acquires mmu_rb_node->handler->lock; do not call while already holding + * handler->lock. + */ +void hfi1_mmu_rb_release(struct kref *refcount) +{ + struct mmu_rb_node *mnode = + container_of(refcount, struct mmu_rb_node, refcount); + struct mmu_rb_handler *handler = mnode->handler; + unsigned long flags; + + spin_lock_irqsave(&handler->lock, flags); + list_move(&mnode->list, &mnode->handler->del_list); + spin_unlock_irqrestore(&handler->lock, flags); + queue_work(handler->wq, &handler->del_work); +} + void hfi1_mmu_rb_evict(struct mmu_rb_handler *handler, void *evict_arg) { struct mmu_rb_node *rbnode, *ptr; @@ -194,6 +233,10 @@ void hfi1_mmu_rb_evict(struct mmu_rb_handler *handler, void *evict_arg) spin_lock_irqsave(&handler->lock, flags); list_for_each_entry_safe(rbnode, ptr, &handler->lru_list, list) { + /* refcount == 1 implies mmu_rb_handler has only rbnode ref */ + if (kref_read(&rbnode->refcount) > 1) + continue; + if (handler->ops->evict(handler->ops_arg, rbnode, evict_arg, &stop)) { __mmu_int_rb_remove(rbnode, &handler->root); @@ -206,7 +249,7 @@ void hfi1_mmu_rb_evict(struct mmu_rb_handler *handler, void *evict_arg) spin_unlock_irqrestore(&handler->lock, flags); list_for_each_entry_safe(rbnode, ptr, &del_list, list) { - handler->ops->remove(handler->ops_arg, rbnode); + kref_put(&rbnode->refcount, release_immediate); } } @@ -218,7 +261,6 @@ static int mmu_notifier_range_start(struct mmu_notifier *mn, struct rb_root_cached *root = &handler->root; struct mmu_rb_node *node, *ptr = NULL; unsigned long flags; - bool added = false; spin_lock_irqsave(&handler->lock, flags); for (node = __mmu_int_rb_iter_first(root, range->start, range->end-1); @@ -227,38 +269,16 @@ static int mmu_notifier_range_start(struct mmu_notifier *mn, ptr = __mmu_int_rb_iter_next(node, range->start, range->end - 1); trace_hfi1_mmu_mem_invalidate(node->addr, node->len); - if (handler->ops->invalidate(handler->ops_arg, node)) { - __mmu_int_rb_remove(node, root); - /* move from LRU list to delete list */ - list_move(&node->list, &handler->del_list); - added = true; - } + /* Remove from rb tree and lru_list. */ + __mmu_int_rb_remove(node, root); + list_del_init(&node->list); + kref_put(&node->refcount, release_nolock); } spin_unlock_irqrestore(&handler->lock, flags); - if (added) - queue_work(handler->wq, &handler->del_work); - return 0; } -/* - * Call the remove function for the given handler and the list. This - * is expected to be called with a delete list extracted from handler. - * The caller should not be holding the handler lock. - */ -static void do_remove(struct mmu_rb_handler *handler, - struct list_head *del_list) -{ - struct mmu_rb_node *node; - - while (!list_empty(del_list)) { - node = list_first_entry(del_list, struct mmu_rb_node, list); - list_del(&node->list); - handler->ops->remove(handler->ops_arg, node); - } -} - /* * Work queue function to remove all nodes that have been queued up to * be removed. The key feature is that mm->mmap_lock is not being held @@ -271,11 +291,16 @@ static void handle_remove(struct work_struct *work) del_work); struct list_head del_list; unsigned long flags; + struct mmu_rb_node *node; /* remove anything that is queued to get removed */ spin_lock_irqsave(&handler->lock, flags); list_replace_init(&handler->del_list, &del_list); spin_unlock_irqrestore(&handler->lock, flags); - do_remove(handler, &del_list); + while (!list_empty(&del_list)) { + node = list_first_entry(&del_list, struct mmu_rb_node, list); + list_del(&node->list); + handler->ops->remove(handler->ops_arg, node); + } } diff --git a/drivers/infiniband/hw/hfi1/mmu_rb.h b/drivers/infiniband/hw/hfi1/mmu_rb.h index ed75acdb7b83..dd2c4a0ae95b 100644 --- a/drivers/infiniband/hw/hfi1/mmu_rb.h +++ b/drivers/infiniband/hw/hfi1/mmu_rb.h @@ -16,6 +16,7 @@ struct mmu_rb_node { struct rb_node node; struct mmu_rb_handler *handler; struct list_head list; + struct kref refcount; }; /* @@ -51,6 +52,8 @@ int hfi1_mmu_rb_register(void *ops_arg, void hfi1_mmu_rb_unregister(struct mmu_rb_handler *handler); int hfi1_mmu_rb_insert(struct mmu_rb_handler *handler, struct mmu_rb_node *mnode); +void hfi1_mmu_rb_release(struct kref *refcount); + void hfi1_mmu_rb_evict(struct mmu_rb_handler *handler, void *evict_arg); struct mmu_rb_node *hfi1_mmu_rb_get_first(struct mmu_rb_handler *handler, unsigned long addr, diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index bb2552dd29c1..26c62162759b 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -1593,7 +1593,20 @@ static inline void sdma_unmap_desc( struct hfi1_devdata *dd, struct sdma_desc *descp) { - system_descriptor_complete(dd, descp); + switch (sdma_mapping_type(descp)) { + case SDMA_MAP_SINGLE: + dma_unmap_single(&dd->pcidev->dev, sdma_mapping_addr(descp), + sdma_mapping_len(descp), DMA_TO_DEVICE); + break; + case SDMA_MAP_PAGE: + dma_unmap_page(&dd->pcidev->dev, sdma_mapping_addr(descp), + sdma_mapping_len(descp), DMA_TO_DEVICE); + break; + } + + if (descp->pinning_ctx && descp->ctx_put) + descp->ctx_put(descp->pinning_ctx); + descp->pinning_ctx = NULL; } /* @@ -3113,8 +3126,8 @@ int ext_coal_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx, /* Add descriptor for coalesce buffer */ tx->desc_limit = MAX_DESC; - return _sdma_txadd_daddr(dd, SDMA_MAP_SINGLE, NULL, tx, - addr, tx->tlen); + return _sdma_txadd_daddr(dd, SDMA_MAP_SINGLE, tx, + addr, tx->tlen, NULL, NULL, NULL); } return 1; @@ -3157,9 +3170,9 @@ int _pad_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx) make_tx_sdma_desc( tx, SDMA_MAP_NONE, - NULL, dd->sdma_pad_phys, - sizeof(u32) - (tx->packet_len & (sizeof(u32) - 1))); + sizeof(u32) - (tx->packet_len & (sizeof(u32) - 1)), + NULL, NULL, NULL); tx->num_desc++; _sdma_close_tx(dd, tx); return rval; diff --git a/drivers/infiniband/hw/hfi1/sdma.h b/drivers/infiniband/hw/hfi1/sdma.h index 95aaec14c6c2..7fdebab202c4 100644 --- a/drivers/infiniband/hw/hfi1/sdma.h +++ b/drivers/infiniband/hw/hfi1/sdma.h @@ -594,9 +594,11 @@ static inline dma_addr_t sdma_mapping_addr(struct sdma_desc *d) static inline void make_tx_sdma_desc( struct sdma_txreq *tx, int type, - void *pinning_ctx, dma_addr_t addr, - size_t len) + size_t len, + void *pinning_ctx, + void (*ctx_get)(void *), + void (*ctx_put)(void *)) { struct sdma_desc *desc = &tx->descp[tx->num_desc]; @@ -613,7 +615,11 @@ static inline void make_tx_sdma_desc( << SDMA_DESC0_PHY_ADDR_SHIFT) | (((u64)len & SDMA_DESC0_BYTE_COUNT_MASK) << SDMA_DESC0_BYTE_COUNT_SHIFT); + desc->pinning_ctx = pinning_ctx; + desc->ctx_put = ctx_put; + if (pinning_ctx && ctx_get) + ctx_get(pinning_ctx); } /* helper to extend txreq */ @@ -645,18 +651,20 @@ static inline void _sdma_close_tx(struct hfi1_devdata *dd, static inline int _sdma_txadd_daddr( struct hfi1_devdata *dd, int type, - void *pinning_ctx, struct sdma_txreq *tx, dma_addr_t addr, - u16 len) + u16 len, + void *pinning_ctx, + void (*ctx_get)(void *), + void (*ctx_put)(void *)) { int rval = 0; make_tx_sdma_desc( tx, type, - pinning_ctx, - addr, len); + addr, len, + pinning_ctx, ctx_get, ctx_put); WARN_ON(len > tx->tlen); tx->num_desc++; tx->tlen -= len; @@ -676,11 +684,18 @@ static inline int _sdma_txadd_daddr( /** * sdma_txadd_page() - add a page to the sdma_txreq * @dd: the device to use for mapping - * @pinning_ctx: context to be released at descriptor retirement * @tx: tx request to which the page is added * @page: page to map * @offset: offset within the page * @len: length in bytes + * @pinning_ctx: context to be stored on struct sdma_desc .pinning_ctx. Not + * added if coalesce buffer is used. E.g. pointer to pinned-page + * cache entry for the sdma_desc. + * @ctx_get: optional function to take reference to @pinning_ctx. Not called if + * @pinning_ctx is NULL. + * @ctx_put: optional function to release reference to @pinning_ctx after + * sdma_desc completes. May be called in interrupt context so must + * not sleep. Not called if @pinning_ctx is NULL. * * This is used to add a page/offset/length descriptor. * @@ -692,11 +707,13 @@ static inline int _sdma_txadd_daddr( */ static inline int sdma_txadd_page( struct hfi1_devdata *dd, - void *pinning_ctx, struct sdma_txreq *tx, struct page *page, unsigned long offset, - u16 len) + u16 len, + void *pinning_ctx, + void (*ctx_get)(void *), + void (*ctx_put)(void *)) { dma_addr_t addr; int rval; @@ -720,7 +737,8 @@ static inline int sdma_txadd_page( return -ENOSPC; } - return _sdma_txadd_daddr(dd, SDMA_MAP_PAGE, pinning_ctx, tx, addr, len); + return _sdma_txadd_daddr(dd, SDMA_MAP_PAGE, tx, addr, len, + pinning_ctx, ctx_get, ctx_put); } /** @@ -754,8 +772,8 @@ static inline int sdma_txadd_daddr( return rval; } - return _sdma_txadd_daddr(dd, SDMA_MAP_NONE, NULL, tx, - addr, len); + return _sdma_txadd_daddr(dd, SDMA_MAP_NONE, tx, addr, len, + NULL, NULL, NULL); } /** @@ -801,7 +819,8 @@ static inline int sdma_txadd_kvaddr( return -ENOSPC; } - return _sdma_txadd_daddr(dd, SDMA_MAP_SINGLE, NULL, tx, addr, len); + return _sdma_txadd_daddr(dd, SDMA_MAP_SINGLE, tx, addr, len, + NULL, NULL, NULL); } struct iowait_work; @@ -1034,6 +1053,4 @@ u16 sdma_get_descq_cnt(void); extern uint mod_num_sdma; void sdma_update_lmc(struct hfi1_devdata *dd, u64 mask, u32 lid); - -void system_descriptor_complete(struct hfi1_devdata *dd, struct sdma_desc *descp); #endif diff --git a/drivers/infiniband/hw/hfi1/sdma_txreq.h b/drivers/infiniband/hw/hfi1/sdma_txreq.h index fad946cb5e0d..85ae7293c274 100644 --- a/drivers/infiniband/hw/hfi1/sdma_txreq.h +++ b/drivers/infiniband/hw/hfi1/sdma_txreq.h @@ -20,6 +20,8 @@ struct sdma_desc { /* private: don't use directly */ u64 qw[2]; void *pinning_ctx; + /* Release reference to @pinning_ctx. May be called in interrupt context. Must not sleep. */ + void (*ctx_put)(void *ctx); }; /** diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index ae58b48afe07..02bd62b857b7 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -62,18 +62,14 @@ static int defer_packet_queue( static void activate_packet_queue(struct iowait *wait, int reason); static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, unsigned long len); -static int sdma_rb_insert(void *arg, struct mmu_rb_node *mnode); static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, void *arg2, bool *stop); static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode); -static int sdma_rb_invalidate(void *arg, struct mmu_rb_node *mnode); static struct mmu_rb_ops sdma_rb_ops = { .filter = sdma_rb_filter, - .insert = sdma_rb_insert, .evict = sdma_rb_evict, .remove = sdma_rb_remove, - .invalidate = sdma_rb_invalidate }; static int add_system_pages_to_sdma_packet(struct user_sdma_request *req, @@ -247,14 +243,14 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd, spin_unlock(&fd->pq_rcu_lock); synchronize_srcu(&fd->pq_srcu); /* at this point there can be no more new requests */ - if (pq->handler) - hfi1_mmu_rb_unregister(pq->handler); iowait_sdma_drain(&pq->busy); /* Wait until all requests have been freed. */ wait_event_interruptible( pq->wait, !atomic_read(&pq->n_reqs)); kfree(pq->reqs); + if (pq->handler) + hfi1_mmu_rb_unregister(pq->handler); bitmap_free(pq->req_in_use); kmem_cache_destroy(pq->txreq_cache); flush_pq_iowait(pq); @@ -1275,25 +1271,17 @@ static void free_system_node(struct sdma_mmu_node *node) kfree(node); } -static inline void acquire_node(struct sdma_mmu_node *node) -{ - atomic_inc(&node->refcount); - WARN_ON(atomic_read(&node->refcount) < 0); -} - -static inline void release_node(struct mmu_rb_handler *handler, - struct sdma_mmu_node *node) -{ - atomic_dec(&node->refcount); - WARN_ON(atomic_read(&node->refcount) < 0); -} - +/* + * kref_get()'s an additional kref on the returned rb_node to prevent rb_node + * from being released until after rb_node is assigned to an SDMA descriptor + * (struct sdma_desc) under add_system_iovec_to_sdma_packet(), even if the + * virtual address range for rb_node is invalidated between now and then. + */ static struct sdma_mmu_node *find_system_node(struct mmu_rb_handler *handler, unsigned long start, unsigned long end) { struct mmu_rb_node *rb_node; - struct sdma_mmu_node *node; unsigned long flags; spin_lock_irqsave(&handler->lock, flags); @@ -1302,11 +1290,12 @@ static struct sdma_mmu_node *find_system_node(struct mmu_rb_handler *handler, spin_unlock_irqrestore(&handler->lock, flags); return NULL; } - node = container_of(rb_node, struct sdma_mmu_node, rb); - acquire_node(node); + + /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ + kref_get(&rb_node->refcount); spin_unlock_irqrestore(&handler->lock, flags); - return node; + return container_of(rb_node, struct sdma_mmu_node, rb); } static int pin_system_pages(struct user_sdma_request *req, @@ -1355,6 +1344,13 @@ retry: return 0; } +/* + * kref refcount on *node_p will be 2 on successful addition: one kref from + * kref_init() for mmu_rb_handler and one kref to prevent *node_p from being + * released until after *node_p is assigned to an SDMA descriptor (struct + * sdma_desc) under add_system_iovec_to_sdma_packet(), even if the virtual + * address range for *node_p is invalidated between now and then. + */ static int add_system_pinning(struct user_sdma_request *req, struct sdma_mmu_node **node_p, unsigned long start, unsigned long len) @@ -1368,6 +1364,12 @@ static int add_system_pinning(struct user_sdma_request *req, if (!node) return -ENOMEM; + /* First kref "moves" to mmu_rb_handler */ + kref_init(&node->rb.refcount); + + /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ + kref_get(&node->rb.refcount); + node->pq = pq; ret = pin_system_pages(req, start, len, node, PFN_DOWN(len)); if (ret == 0) { @@ -1431,15 +1433,15 @@ static int get_system_cache_entry(struct user_sdma_request *req, return 0; } - SDMA_DBG(req, "prepend: node->rb.addr %lx, node->refcount %d", - node->rb.addr, atomic_read(&node->refcount)); + SDMA_DBG(req, "prepend: node->rb.addr %lx, node->rb.refcount %d", + node->rb.addr, kref_read(&node->rb.refcount)); prepend_len = node->rb.addr - start; /* * This node will not be returned, instead a new node * will be. So release the reference. */ - release_node(handler, node); + kref_put(&node->rb.refcount, hfi1_mmu_rb_release); /* Prepend a node to cover the beginning of the allocation */ ret = add_system_pinning(req, node_p, start, prepend_len); @@ -1451,6 +1453,20 @@ static int get_system_cache_entry(struct user_sdma_request *req, } } +static void sdma_mmu_rb_node_get(void *ctx) +{ + struct mmu_rb_node *node = ctx; + + kref_get(&node->refcount); +} + +static void sdma_mmu_rb_node_put(void *ctx) +{ + struct sdma_mmu_node *node = ctx; + + kref_put(&node->rb.refcount, hfi1_mmu_rb_release); +} + static int add_mapping_to_sdma_packet(struct user_sdma_request *req, struct user_sdma_txreq *tx, struct sdma_mmu_node *cache_entry, @@ -1494,9 +1510,12 @@ static int add_mapping_to_sdma_packet(struct user_sdma_request *req, ctx = cache_entry; } - ret = sdma_txadd_page(pq->dd, ctx, &tx->txreq, + ret = sdma_txadd_page(pq->dd, &tx->txreq, cache_entry->pages[page_index], - page_offset, from_this_page); + page_offset, from_this_page, + ctx, + sdma_mmu_rb_node_get, + sdma_mmu_rb_node_put); if (ret) { /* * When there's a failure, the entire request is freed by @@ -1518,8 +1537,6 @@ static int add_system_iovec_to_sdma_packet(struct user_sdma_request *req, struct user_sdma_iovec *iovec, size_t from_this_iovec) { - struct mmu_rb_handler *handler = req->pq->handler; - while (from_this_iovec > 0) { struct sdma_mmu_node *cache_entry; size_t from_this_cache_entry; @@ -1540,15 +1557,15 @@ static int add_system_iovec_to_sdma_packet(struct user_sdma_request *req, ret = add_mapping_to_sdma_packet(req, tx, cache_entry, start, from_this_cache_entry); + + /* + * Done adding cache_entry to zero or more sdma_desc. Can + * kref_put() the "safety" kref taken under + * get_system_cache_entry(). + */ + kref_put(&cache_entry->rb.refcount, hfi1_mmu_rb_release); + if (ret) { - /* - * We're guaranteed that there will be no descriptor - * completion callback that releases this node - * because only the last descriptor referencing it - * has a context attached, and a failure means the - * last descriptor was never added. - */ - release_node(handler, cache_entry); SDMA_DBG(req, "add system segment failed %d", ret); return ret; } @@ -1599,42 +1616,12 @@ static int add_system_pages_to_sdma_packet(struct user_sdma_request *req, return 0; } -void system_descriptor_complete(struct hfi1_devdata *dd, - struct sdma_desc *descp) -{ - switch (sdma_mapping_type(descp)) { - case SDMA_MAP_SINGLE: - dma_unmap_single(&dd->pcidev->dev, sdma_mapping_addr(descp), - sdma_mapping_len(descp), DMA_TO_DEVICE); - break; - case SDMA_MAP_PAGE: - dma_unmap_page(&dd->pcidev->dev, sdma_mapping_addr(descp), - sdma_mapping_len(descp), DMA_TO_DEVICE); - break; - } - - if (descp->pinning_ctx) { - struct sdma_mmu_node *node = descp->pinning_ctx; - - release_node(node->rb.handler, node); - } -} - static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, unsigned long len) { return (bool)(node->addr == addr); } -static int sdma_rb_insert(void *arg, struct mmu_rb_node *mnode) -{ - struct sdma_mmu_node *node = - container_of(mnode, struct sdma_mmu_node, rb); - - atomic_inc(&node->refcount); - return 0; -} - /* * Return 1 to remove the node from the rb tree and call the remove op. * @@ -1647,10 +1634,6 @@ static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, container_of(mnode, struct sdma_mmu_node, rb); struct evict_data *evict_data = evict_arg; - /* is this node still being used? */ - if (atomic_read(&node->refcount)) - return 0; /* keep this node */ - /* this node will be evicted, add its pages to our count */ evict_data->cleared += node->npages; @@ -1668,13 +1651,3 @@ static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode) free_system_node(node); } - -static int sdma_rb_invalidate(void *arg, struct mmu_rb_node *mnode) -{ - struct sdma_mmu_node *node = - container_of(mnode, struct sdma_mmu_node, rb); - - if (!atomic_read(&node->refcount)) - return 1; - return 0; -} diff --git a/drivers/infiniband/hw/hfi1/user_sdma.h b/drivers/infiniband/hw/hfi1/user_sdma.h index a241836371dc..548347d4c5bc 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.h +++ b/drivers/infiniband/hw/hfi1/user_sdma.h @@ -104,7 +104,6 @@ struct hfi1_user_sdma_comp_q { struct sdma_mmu_node { struct mmu_rb_node rb; struct hfi1_user_sdma_pkt_q *pq; - atomic_t refcount; struct page **pages; unsigned int npages; }; diff --git a/drivers/infiniband/hw/hfi1/vnic_sdma.c b/drivers/infiniband/hw/hfi1/vnic_sdma.c index 727eedfba332..cc6324d2d1dd 100644 --- a/drivers/infiniband/hw/hfi1/vnic_sdma.c +++ b/drivers/infiniband/hw/hfi1/vnic_sdma.c @@ -64,11 +64,11 @@ static noinline int build_vnic_ulp_payload(struct sdma_engine *sde, /* combine physically continuous fragments later? */ ret = sdma_txadd_page(sde->dd, - NULL, &tx->txreq, skb_frag_page(frag), skb_frag_off(frag), - skb_frag_size(frag)); + skb_frag_size(frag), + NULL, NULL, NULL); if (unlikely(ret)) goto bail_txadd; } -- cgit 1.4.1 From 766e0b6f4c9649f126e59c06f100b8581d0773b8 Mon Sep 17 00:00:00 2001 From: Chengchang Tang Date: Tue, 23 May 2023 20:16:40 +0800 Subject: RDMA/hns: Fix hns_roce_table_get return value [ Upstream commit cf5b608fb0e369c473a8303cad6ddb386505e5b8 ] The return value of set_hem has been fixed to ENODEV, which will lead a diagnostic information missing. Fixes: 9a4435375cd1 ("IB/hns: Add driver files for hns RoCE driver") Link: https://lore.kernel.org/r/20230523121641.3132102-3-huangjunxian6@hisilicon.com Signed-off-by: Chengchang Tang Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_hem.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index aa8a08d1c014..f30274986c0d 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -595,11 +595,12 @@ int hns_roce_table_get(struct hns_roce_dev *hr_dev, } /* Set HEM base address(128K/page, pa) to Hardware */ - if (hr_dev->hw->set_hem(hr_dev, table, obj, HEM_HOP_STEP_DIRECT)) { + ret = hr_dev->hw->set_hem(hr_dev, table, obj, HEM_HOP_STEP_DIRECT); + if (ret) { hns_roce_free_hem(hr_dev, table->hem[i]); table->hem[i] = NULL; - ret = -ENODEV; - dev_err(dev, "set HEM base address to HW failed.\n"); + dev_err(dev, "set HEM base address to HW failed, ret = %d.\n", + ret); goto out; } -- cgit 1.4.1 From 5d14292dba9554881a137039c048a67ddf321395 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 23 May 2023 17:35:16 +0200 Subject: ARM: dts: iwg20d-q7-common: Fix backlight pwm specifier [ Upstream commit 0501fdec106a291c43b3c1b525cf22ab4c24b2d8 ] make dtbs_check: arch/arm/boot/dts/renesas/r8a7743-iwg20d-q7.dtb: backlight: pwms: [[58, 0, 5000000], [0]] is too long From schema: Documentation/devicetree/bindings/leds/backlight/pwm-backlight.yaml arch/arm/boot/dts/renesas/r8a7743-iwg20d-q7-dbcm-ca.dtb: backlight: pwms: [[67, 0, 5000000], [0]] is too long From schema: Documentation/devicetree/bindings/leds/backlight/pwm-backlight.yaml arch/arm/boot/dts/renesas/r8a7744-iwg20d-q7-dbcm-ca.dtb: backlight: pwms: [[67, 0, 5000000], [0]] is too long From schema: Documentation/devicetree/bindings/leds/backlight/pwm-backlight.yaml arch/arm/boot/dts/renesas/r8a7744-iwg20d-q7.dtb: backlight: pwms: [[58, 0, 5000000], [0]] is too long From schema: Documentation/devicetree/bindings/leds/backlight/pwm-backlight.yaml PWM specifiers referring to R-Car PWM Timer Controllers should contain only two cells. Fix this by dropping the bogus third cell. Fixes: 6f89dd9e9325d05b ("ARM: dts: iwg20d-q7-common: Add LCD support") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/6e5c3167424a43faf8c1fa68d9667b3d87dc86d8.1684855911.git.geert+renesas@glider.be Signed-off-by: Sasha Levin --- arch/arm/boot/dts/iwg20d-q7-common.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/iwg20d-q7-common.dtsi b/arch/arm/boot/dts/iwg20d-q7-common.dtsi index 03caea6fc6ff..4351c5a02fa5 100644 --- a/arch/arm/boot/dts/iwg20d-q7-common.dtsi +++ b/arch/arm/boot/dts/iwg20d-q7-common.dtsi @@ -49,7 +49,7 @@ lcd_backlight: backlight { compatible = "pwm-backlight"; - pwms = <&pwm3 0 5000000 0>; + pwms = <&pwm3 0 5000000>; brightness-levels = <0 4 8 16 32 64 128 255>; default-brightness-level = <7>; enable-gpios = <&gpio5 14 GPIO_ACTIVE_HIGH>; -- cgit 1.4.1 From 6878bdd7571827babc1c4c1ff66ea1affe951020 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 25 May 2023 10:48:22 +0200 Subject: arm64: dts: renesas: ulcb-kf: Remove flow control for SCIF1 [ Upstream commit 1a2c4e5635177939a088d22fa35c6a7032725663 ] The schematics are misleading, the flow control is for HSCIF1. We need SCIF1 for GNSS/GPS which does not use flow control. Fixes: c6c816e22bc8 ("arm64: dts: ulcb-kf: enable SCIF1") Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230525084823.4195-2-wsa+renesas@sang-engineering.com Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/renesas/ulcb-kf.dtsi | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi b/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi index 408871c2859d..588b14b66b6f 100644 --- a/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi +++ b/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi @@ -393,7 +393,7 @@ }; scif1_pins: scif1 { - groups = "scif1_data_b", "scif1_ctrl"; + groups = "scif1_data_b"; function = "scif1"; }; @@ -447,7 +447,6 @@ &scif1 { pinctrl-0 = <&scif1_pins>; pinctrl-names = "default"; - uart-has-rtscts; status = "okay"; }; -- cgit 1.4.1 From 52b04ac85f5f4b485bf658101e464143225e68f9 Mon Sep 17 00:00:00 2001 From: Kuogee Hsieh Date: Thu, 25 May 2023 10:40:49 -0700 Subject: drm/msm/dpu: set DSC flush bit correctly at MDP CTL flush register [ Upstream commit 12cef323c903bd8b13d1f6ff24a9695c2cdc360b ] The CTL_FLUSH register should be programmed with the 22th bit (DSC_IDX) to flush the DSC hardware blocks, not the literal value of 22 (which corresponds to flushing VIG1, VIG2 and RGB1 instead). Changes in V12: -- split this patch out of "separate DSC flush update out of interface" Changes in V13: -- rewording the commit text Changes in V14: -- drop 'DSC" from "The DSC CTL_FLUSH register" at commit text Fixes: 77f6da90487c ("drm/msm/disp/dpu1: Add DSC support in hw_ctl") Signed-off-by: Kuogee Hsieh Reviewed-by: Marijn Suijten Patchwork: https://patchwork.freedesktop.org/patch/539496/ Link: https://lore.kernel.org/r/1685036458-22683-2-git-send-email-quic_khsieh@quicinc.com Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index a35ecb6676c8..696c32d30d10 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -550,7 +550,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx, DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE, BIT(cfg->merge_3d - MERGE_3D_0)); if (cfg->dsc) { - DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, DSC_IDX); + DPU_REG_WRITE(&ctx->hw, CTL_FLUSH, BIT(DSC_IDX)); DPU_REG_WRITE(c, CTL_DSC_ACTIVE, cfg->dsc); } } -- cgit 1.4.1 From 3b4c21804076e461a6453ee4d09872172336aa1d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 4 Jun 2023 17:42:28 +0200 Subject: fbdev: omapfb: lcd_mipid: Fix an error handling path in mipid_spi_probe() [ Upstream commit 79a3908d1ea6c35157a6d907b1a9d8ec06015e7a ] If 'mipid_detect()' fails, we must free 'md' to avoid a memory leak. Fixes: 66d2f99d0bb5 ("omapfb: add support for MIPI-DCS compatible LCDs") Signed-off-by: Christophe JAILLET Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/video/fbdev/omap/lcd_mipid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/omap/lcd_mipid.c b/drivers/video/fbdev/omap/lcd_mipid.c index 03cff39d392d..cc1079aad61f 100644 --- a/drivers/video/fbdev/omap/lcd_mipid.c +++ b/drivers/video/fbdev/omap/lcd_mipid.c @@ -563,11 +563,15 @@ static int mipid_spi_probe(struct spi_device *spi) r = mipid_detect(md); if (r < 0) - return r; + goto free_md; omapfb_register_panel(&md->panel); return 0; + +free_md: + kfree(md); + return r; } static void mipid_spi_remove(struct spi_device *spi) -- cgit 1.4.1 From 421ce97657a84b81ce2cb915e75037e1a356736a Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 19 Apr 2023 09:30:06 +0530 Subject: arm64: dts: ti: k3-j7200: Fix physical address of pin [ Upstream commit 3d011933000ed9054c649952d83162d24f020a93 ] wkup_pmx splits into multiple regions. Like wkup_pmx0 -> 13 pins (WKUP_PADCONFIG 0 - 12) wkup_pmx1 -> 2 pins (WKUP_PADCONFIG 14 - 15) wkup_pmx2 -> 59 pins (WKUP_PADCONFIG 26 - 84) wkup_pmx3 -> 8 pins (WKUP_PADCONFIG 93 - 100) With this split, pin offset needs to be adjusted to match with new pmx for all pins above wkup_pmx0. Example a pin under wkup_pmx1 should start from 0 instead of old offset(0x38 WKUP_PADCONFIG 14 offset) J7200 Datasheet (Table 6-106, Section 6.4 Pin Multiplexing) : https://www.ti.com/lit/ds/symlink/dra821u.pdf Fixes: 9ae21ac445e9 ("arm64: dts: ti: k3-j7200: Fix wakeup pinmux range") Signed-off-by: Keerthy Signed-off-by: Udit Kumar Link: https://lore.kernel.org/r/20230419040007.3022780-2-u-kumar1@ti.com Signed-off-by: Vignesh Raghavendra Signed-off-by: Sasha Levin --- .../boot/dts/ti/k3-j7200-common-proc-board.dts | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts index 50009f963a32..5840063f6129 100644 --- a/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts +++ b/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts @@ -83,25 +83,25 @@ &wkup_pmx2 { mcu_cpsw_pins_default: mcu-cpsw-pins-default { pinctrl-single,pins = < - J721E_WKUP_IOPAD(0x0068, PIN_OUTPUT, 0) /* MCU_RGMII1_TX_CTL */ - J721E_WKUP_IOPAD(0x006c, PIN_INPUT, 0) /* MCU_RGMII1_RX_CTL */ - J721E_WKUP_IOPAD(0x0070, PIN_OUTPUT, 0) /* MCU_RGMII1_TD3 */ - J721E_WKUP_IOPAD(0x0074, PIN_OUTPUT, 0) /* MCU_RGMII1_TD2 */ - J721E_WKUP_IOPAD(0x0078, PIN_OUTPUT, 0) /* MCU_RGMII1_TD1 */ - J721E_WKUP_IOPAD(0x007c, PIN_OUTPUT, 0) /* MCU_RGMII1_TD0 */ - J721E_WKUP_IOPAD(0x0088, PIN_INPUT, 0) /* MCU_RGMII1_RD3 */ - J721E_WKUP_IOPAD(0x008c, PIN_INPUT, 0) /* MCU_RGMII1_RD2 */ - J721E_WKUP_IOPAD(0x0090, PIN_INPUT, 0) /* MCU_RGMII1_RD1 */ - J721E_WKUP_IOPAD(0x0094, PIN_INPUT, 0) /* MCU_RGMII1_RD0 */ - J721E_WKUP_IOPAD(0x0080, PIN_OUTPUT, 0) /* MCU_RGMII1_TXC */ - J721E_WKUP_IOPAD(0x0084, PIN_INPUT, 0) /* MCU_RGMII1_RXC */ + J721E_WKUP_IOPAD(0x0000, PIN_OUTPUT, 0) /* MCU_RGMII1_TX_CTL */ + J721E_WKUP_IOPAD(0x0004, PIN_INPUT, 0) /* MCU_RGMII1_RX_CTL */ + J721E_WKUP_IOPAD(0x0008, PIN_OUTPUT, 0) /* MCU_RGMII1_TD3 */ + J721E_WKUP_IOPAD(0x000c, PIN_OUTPUT, 0) /* MCU_RGMII1_TD2 */ + J721E_WKUP_IOPAD(0x0010, PIN_OUTPUT, 0) /* MCU_RGMII1_TD1 */ + J721E_WKUP_IOPAD(0x0014, PIN_OUTPUT, 0) /* MCU_RGMII1_TD0 */ + J721E_WKUP_IOPAD(0x0020, PIN_INPUT, 0) /* MCU_RGMII1_RD3 */ + J721E_WKUP_IOPAD(0x0024, PIN_INPUT, 0) /* MCU_RGMII1_RD2 */ + J721E_WKUP_IOPAD(0x0028, PIN_INPUT, 0) /* MCU_RGMII1_RD1 */ + J721E_WKUP_IOPAD(0x002c, PIN_INPUT, 0) /* MCU_RGMII1_RD0 */ + J721E_WKUP_IOPAD(0x0018, PIN_OUTPUT, 0) /* MCU_RGMII1_TXC */ + J721E_WKUP_IOPAD(0x001c, PIN_INPUT, 0) /* MCU_RGMII1_RXC */ >; }; mcu_mdio_pins_default: mcu-mdio1-pins-default { pinctrl-single,pins = < - J721E_WKUP_IOPAD(0x009c, PIN_OUTPUT, 0) /* (L1) MCU_MDIO0_MDC */ - J721E_WKUP_IOPAD(0x0098, PIN_INPUT, 0) /* (L4) MCU_MDIO0_MDIO */ + J721E_WKUP_IOPAD(0x0034, PIN_OUTPUT, 0) /* (L1) MCU_MDIO0_MDC */ + J721E_WKUP_IOPAD(0x0030, PIN_INPUT, 0) /* (L4) MCU_MDIO0_MDIO */ >; }; }; -- cgit 1.4.1 From 03b2c470a136a83a9961a2a855cde59498361598 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Tue, 6 Jun 2023 12:05:32 -0700 Subject: Input: pm8941-powerkey - fix debounce on gen2+ PMICs [ Upstream commit 8c9cce9cb81b5fdc6e66bf3f129727b89e8daab7 ] Since PM8998/PM660, the power key debounce register was redefined to support shorter debounce times. On PM8941 the shortest debounce time (represented by register value 0) was 15625us, on PM8998 the shortest debounce time is 62us, with the default being 2ms. Adjust the bit shift to correctly program debounce on PM8998 and newer. Fixes: 68c581d5e7d8 ("Input: add Qualcomm PM8941 power key driver") Signed-off-by: Caleb Connolly Link: https://lore.kernel.org/r/20230529-pm8941-pwrkey-debounce-v1-2-c043a6d5c814@linaro.org Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin --- drivers/input/misc/pm8941-pwrkey.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index 549df01b6ee3..5dd68a02c445 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -50,7 +50,10 @@ #define PON_RESIN_PULL_UP BIT(0) #define PON_DBC_CTL 0x71 -#define PON_DBC_DELAY_MASK 0x7 +#define PON_DBC_DELAY_MASK_GEN1 0x7 +#define PON_DBC_DELAY_MASK_GEN2 0xf +#define PON_DBC_SHIFT_GEN1 6 +#define PON_DBC_SHIFT_GEN2 14 struct pm8941_data { unsigned int pull_up_bit; @@ -247,7 +250,7 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) struct device *parent; struct device_node *regmap_node; const __be32 *addr; - u32 req_delay; + u32 req_delay, mask, delay_shift; int error; if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay)) @@ -336,12 +339,20 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev) pwrkey->input->phys = pwrkey->data->phys; if (pwrkey->data->supports_debounce_config) { - req_delay = (req_delay << 6) / USEC_PER_SEC; + if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY) { + mask = PON_DBC_DELAY_MASK_GEN2; + delay_shift = PON_DBC_SHIFT_GEN2; + } else { + mask = PON_DBC_DELAY_MASK_GEN1; + delay_shift = PON_DBC_SHIFT_GEN1; + } + + req_delay = (req_delay << delay_shift) / USEC_PER_SEC; req_delay = ilog2(req_delay); error = regmap_update_bits(pwrkey->regmap, pwrkey->baseaddr + PON_DBC_CTL, - PON_DBC_DELAY_MASK, + mask, req_delay); if (error) { dev_err(&pdev->dev, "failed to set debounce: %d\n", -- cgit 1.4.1 From dc2707deeb8d0634e14df60b78ad3ecd7c742eef Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 6 Jun 2023 20:01:12 +0200 Subject: ARM: dts: stm32: Fix audio routing on STM32MP15xx DHCOM PDK2 [ Upstream commit e3f2778b1b6ced649bffdc7cbb05b80bb92f2108 ] The audio routing flow is not correct, the flow should be from source (second element in the pair) to sink (first element in the pair). The flow now is from "HP_OUT" to "Playback", where "Playback" is source and "HP_OUT" is sink, i.e. the direction is swapped and there is no direct link between the two either. Fill in the correct routing, where "HP_OUT" supplies the "Headphone Jack", "Line In Jack" supplies "LINE_IN" input, "Microphone Jack" supplies "MIC_IN" input and "Mic Bias" supplies "Microphone Jack". Fixes: 34e0c7847dcf ("ARM: dts: stm32: Add DH Electronics DHCOM STM32MP1 SoM and PDK2 board") Signed-off-by: Marek Vasut Signed-off-by: Alexandre Torgue Signed-off-by: Sasha Levin --- arch/arm/boot/dts/stm32mp15xx-dhcom-pdk2.dtsi | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcom-pdk2.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcom-pdk2.dtsi index 5f586f024060..38f46c2c83aa 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dhcom-pdk2.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dhcom-pdk2.dtsi @@ -137,10 +137,13 @@ sound { compatible = "audio-graph-card"; - routing = - "MIC_IN", "Capture", - "Capture", "Mic Bias", - "Playback", "HP_OUT"; + widgets = "Headphone", "Headphone Jack", + "Line", "Line In Jack", + "Microphone", "Microphone Jack"; + routing = "Headphone Jack", "HP_OUT", + "LINE_IN", "Line In Jack", + "MIC_IN", "Microphone Jack", + "Microphone Jack", "Mic Bias"; dais = <&sai2a_port &sai2b_port>; status = "okay"; }; -- cgit 1.4.1 From 9e633411d1438f27648412bc6429d11a0e09f980 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Tue, 6 Jun 2023 13:56:04 +0200 Subject: ARM: dts: stm32: fix i2s endpoint format property for stm32mp15xx-dkx [ Upstream commit 076c74c592cabe4a47537fe5205b5b678bed010d ] Use "dai-format" to configure DAI audio format as specified in audio-graph-port.yaml bindings. Fixes: 144d1ba70548 ("ARM: dts: stm32: Adapt STM32MP157 DK boards to stm32 DT diversity") Signed-off-by: Olivier Moysan Signed-off-by: Alexandre Torgue Signed-off-by: Sasha Levin --- arch/arm/boot/dts/stm32mp15xx-dkx.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi b/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi index 8b48d3c89a04..fdc48536e97d 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi @@ -438,7 +438,7 @@ i2s2_port: port { i2s2_endpoint: endpoint { remote-endpoint = <&sii9022_tx_endpoint>; - format = "i2s"; + dai-format = "i2s"; mclk-fs = <256>; }; }; -- cgit 1.4.1 From 421d35912766ecbf8fb9474bf78ec20bfc292589 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 6 Jun 2023 08:30:04 -0700 Subject: hwmon: (gsc-hwmon) fix fan pwm temperature scaling [ Upstream commit a6d80df47ee2c69db99e4f2f8871aa4db154620b ] The GSC fan pwm temperature register is in centidegrees celcius but the Linux hwmon convention is to use milidegrees celcius. Fix the scaling. Fixes: 3bce5377ef66 ("hwmon: Add Gateworks System Controller support") Signed-off-by: Tim Harvey Link: https://lore.kernel.org/r/20230606153004.1448086-1-tharvey@gateworks.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/gsc-hwmon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index b60ec95b5edb..74bfc21c2767 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -82,8 +82,8 @@ static ssize_t pwm_auto_point_temp_store(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = clamp_val(temp, 0, 10000); - temp = DIV_ROUND_CLOSEST(temp, 10); + temp = clamp_val(temp, 0, 100000); + temp = DIV_ROUND_CLOSEST(temp, 100); regs[0] = temp & 0xff; regs[1] = (temp >> 8) & 0xff; @@ -100,7 +100,7 @@ static ssize_t pwm_auto_point_pwm_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - return sprintf(buf, "%d\n", 255 * (50 + (attr->index * 10)) / 100); + return sprintf(buf, "%d\n", 255 * (50 + (attr->index * 10))); } static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm_auto_point_pwm, 0); -- cgit 1.4.1 From 1ae94553dcfb603363946e5366e348f8ab735a17 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 2 Jun 2023 14:34:47 -0700 Subject: hwmon: (pmbus/adm1275) Fix problems with temperature monitoring on ADM1272 [ Upstream commit b153a0bb4199566abd337119207f82b59a8cd1ca ] The PMON_CONFIG register on ADM1272 is a 16 bit register. Writing a 8 bit value into it clears the upper 8 bits of the register, resulting in unexpected side effects. Fix by writing the 16 bit register value. Also, it has been reported that temperature readings are sometimes widely inaccurate, to the point where readings may result in device shutdown due to errant overtemperature faults. Improve by enabling temperature sampling. While at it, move the common code for ADM1272 and ADM1278 into a separate function, and clarify in the error message that an attempt was made to enable both VOUT and temperature monitoring. Last but not least, return the error code reported by the underlying I2C controller and not -ENODEV if updating the PMON_CONFIG register fails. After all, this does not indicate that the chip is not present, but an error in the communication with the chip. Fixes: 4ff0ce227a1e ("hwmon: (pmbus/adm1275) Add support for ADM1272") Fixes: 9da9c2dc57b2 ("hwmon: (adm1275) enable adm1272 temperature reporting") Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20230602213447.3557346-1-linux@roeck-us.net Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/pmbus/adm1275.c | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 3b07bfb43e93..b8543c06d022 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -37,10 +37,13 @@ enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 }; #define ADM1272_IRANGE BIT(0) +#define ADM1278_TSFILT BIT(15) #define ADM1278_TEMP1_EN BIT(3) #define ADM1278_VIN_EN BIT(2) #define ADM1278_VOUT_EN BIT(1) +#define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT) + #define ADM1293_IRANGE_25 0 #define ADM1293_IRANGE_50 BIT(6) #define ADM1293_IRANGE_100 BIT(7) @@ -462,6 +465,22 @@ static const struct i2c_device_id adm1275_id[] = { }; MODULE_DEVICE_TABLE(i2c, adm1275_id); +/* Enable VOUT & TEMP1 if not enabled (disabled by default) */ +static int adm1275_enable_vout_temp(struct i2c_client *client, int config) +{ + int ret; + + if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) { + config |= ADM1278_PMON_DEFCONFIG; + ret = i2c_smbus_write_word_data(client, ADM1275_PMON_CONFIG, config); + if (ret < 0) { + dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n"); + return ret; + } + } + return 0; +} + static int adm1275_probe(struct i2c_client *client) { s32 (*config_read_fn)(const struct i2c_client *client, u8 reg); @@ -615,19 +634,10 @@ static int adm1275_probe(struct i2c_client *client) PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - /* Enable VOUT & TEMP1 if not enabled (disabled by default) */ - if ((config & (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) != - (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) { - config |= ADM1278_VOUT_EN | ADM1278_TEMP1_EN; - ret = i2c_smbus_write_byte_data(client, - ADM1275_PMON_CONFIG, - config); - if (ret < 0) { - dev_err(&client->dev, - "Failed to enable VOUT monitoring\n"); - return -ENODEV; - } - } + ret = adm1275_enable_vout_temp(client, config); + if (ret) + return ret; + if (config & ADM1278_VIN_EN) info->func[0] |= PMBUS_HAVE_VIN; break; @@ -684,19 +694,9 @@ static int adm1275_probe(struct i2c_client *client) PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - /* Enable VOUT & TEMP1 if not enabled (disabled by default) */ - if ((config & (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) != - (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) { - config |= ADM1278_VOUT_EN | ADM1278_TEMP1_EN; - ret = i2c_smbus_write_word_data(client, - ADM1275_PMON_CONFIG, - config); - if (ret < 0) { - dev_err(&client->dev, - "Failed to enable VOUT monitoring\n"); - return -ENODEV; - } - } + ret = adm1275_enable_vout_temp(client, config); + if (ret) + return ret; if (config & ADM1278_VIN_EN) info->func[0] |= PMBUS_HAVE_VIN; -- cgit 1.4.1 From 90d4c487cd658b51212eb65ae804ab11af193672 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 8 Jun 2023 17:36:29 +0200 Subject: ARM: dts: BCM5301X: fix duplex-full => full-duplex [ Upstream commit fd274b733bfdde3ca72f0fa2a37f032f3a8c402c ] this typo was found by the dtbs_check | ports:port@5:fixed-link: 'oneOf' conditional failed, | {'speed': [[1000]], 'duplex-full': True} is not of type 'array' | 'duplex-full' does not match any of the regexes: 'pinctrl-[0-]..." this should have been full-duplex; Fixes: 935327a73553 ("ARM: dts: BCM5301X: Add DT for Meraki MR26") Fixes: ec88a9c344d9 ("ARM: BCM5301X: Add DT for Meraki MR32") Signed-off-by: Christian Lamparter Link: https://lore.kernel.org/r/50522f45566951a9eabd22820647924cc6b4a264.1686238550.git.chunkeey@gmail.com Signed-off-by: Florian Fainelli Signed-off-by: Sasha Levin --- arch/arm/boot/dts/bcm53015-meraki-mr26.dts | 2 +- arch/arm/boot/dts/bcm53016-meraki-mr32.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/bcm53015-meraki-mr26.dts b/arch/arm/boot/dts/bcm53015-meraki-mr26.dts index 14f58033efeb..ca2266b936ee 100644 --- a/arch/arm/boot/dts/bcm53015-meraki-mr26.dts +++ b/arch/arm/boot/dts/bcm53015-meraki-mr26.dts @@ -128,7 +128,7 @@ fixed-link { speed = <1000>; - duplex-full; + full-duplex; }; }; }; diff --git a/arch/arm/boot/dts/bcm53016-meraki-mr32.dts b/arch/arm/boot/dts/bcm53016-meraki-mr32.dts index e678bc03d816..008de8ee2584 100644 --- a/arch/arm/boot/dts/bcm53016-meraki-mr32.dts +++ b/arch/arm/boot/dts/bcm53016-meraki-mr32.dts @@ -187,7 +187,7 @@ fixed-link { speed = <1000>; - duplex-full; + full-duplex; }; }; }; -- cgit 1.4.1 From 4812faba0a76b80eaec31727fa5f0c30a3e30f7c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 5 May 2023 13:25:03 +0200 Subject: clk: Export clk_hw_forward_rate_request() [ Upstream commit ed046ac74da0b5602566073023a1519b5ae657b7 ] Commit 262ca38f4b6e ("clk: Stop forwarding clk_rate_requests to the parent") introduced the public clk_hw_forward_rate_request() function, but didn't export the symbol. Make sure it's the case. Fixes: 262ca38f4b6e ("clk: Stop forwarding clk_rate_requests to the parent") Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20221018-clk-range-checks-fixes-v4-1-971d5077e7d2@cerno.tech Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 57b83665e5c3..d4a74759fe29 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1525,6 +1525,7 @@ void clk_hw_forward_rate_request(const struct clk_hw *hw, parent->core, req, parent_rate); } +EXPORT_SYMBOL_GPL(clk_hw_forward_rate_request); static bool clk_core_can_round(struct clk_core * const core) { -- cgit 1.4.1 From 36786e2a733143426dd7628e939735465425fbb1 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 17 Apr 2023 23:35:08 +0200 Subject: drm/amd/display: Fix a test CalculatePrefetchSchedule() [ Upstream commit 960e27a5741cd3001996ff6ddfb3eb0ed3a4909d ] It is likely Height was expected here, instead of Width. Test the correct variable. Fixes: 17529ea2acfa ("drm/amd/display: Optimizations for DML math") Signed-off-by: Christophe JAILLET Signed-off-by: Hamza Mahfooz Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c index b7c2844d0cbe..f294f2f8c75b 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c @@ -810,7 +810,7 @@ static bool CalculatePrefetchSchedule( *swath_width_chroma_ub = dml_ceil(SwathWidthY / 2 - 1, myPipe->BlockWidth256BytesC) + myPipe->BlockWidth256BytesC; } else { *swath_width_luma_ub = dml_ceil(SwathWidthY - 1, myPipe->BlockHeight256BytesY) + myPipe->BlockHeight256BytesY; - if (myPipe->BlockWidth256BytesC > 0) + if (myPipe->BlockHeight256BytesC > 0) *swath_width_chroma_ub = dml_ceil(SwathWidthY / 2 - 1, myPipe->BlockHeight256BytesC) + myPipe->BlockHeight256BytesC; } -- cgit 1.4.1 From 384717042de89dfd99087d2a54aad72620ac7fcb Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 17 Apr 2023 23:41:11 +0200 Subject: drm/amd/display: Fix a test dml32_rq_dlg_get_rq_reg() [ Upstream commit bafc31166aa7df5fa26ae0ad8196d1717e6cdea9 ] It is likely p1_min_meta_chunk_bytes was expected here, instead of min_meta_chunk_bytes. Test the correct variable. Fixes: dda4fb85e433 ("drm/amd/display: DML changes for DCN32/321") Signed-off-by: Christophe JAILLET Signed-off-by: Hamza Mahfooz Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c index 395ae8761980..9ba6cb67655f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c @@ -116,7 +116,7 @@ void dml32_rq_dlg_get_rq_reg(display_rq_regs_st *rq_regs, else rq_regs->rq_regs_l.min_meta_chunk_size = dml_log2(min_meta_chunk_bytes) - 6 + 1; - if (min_meta_chunk_bytes == 0) + if (p1_min_meta_chunk_bytes == 0) rq_regs->rq_regs_c.min_meta_chunk_size = 0; else rq_regs->rq_regs_c.min_meta_chunk_size = dml_log2(p1_min_meta_chunk_bytes) - 6 + 1; -- cgit 1.4.1 From fabadad9e28dabecf25ad3c947aa8ba5f2b0eecf Mon Sep 17 00:00:00 2001 From: Daniil Dulov Date: Thu, 11 May 2023 04:23:14 -0700 Subject: drm/amdkfd: Fix potential deallocation of previously deallocated memory. [ Upstream commit cabbdea1f1861098991768d7bbf5a49ed1608213 ] Pointer mqd_mem_obj can be deallocated in kfd_gtt_sa_allocate(). The function then returns non-zero value, which causes the second deallocation. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: d1f8f0d17d40 ("drm/amdkfd: Move non-sdma mqd allocation out of init_mqd") Signed-off-by: Daniil Dulov Signed-off-by: Felix Kuehling Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index 0778e587a2d6..eaf084acb706 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -115,18 +115,19 @@ static struct kfd_mem_obj *allocate_mqd(struct kfd_dev *kfd, &(mqd_mem_obj->gtt_mem), &(mqd_mem_obj->gpu_addr), (void *)&(mqd_mem_obj->cpu_ptr), true); + + if (retval) { + kfree(mqd_mem_obj); + return NULL; + } } else { retval = kfd_gtt_sa_allocate(kfd, sizeof(struct v9_mqd), &mqd_mem_obj); - } - - if (retval) { - kfree(mqd_mem_obj); - return NULL; + if (retval) + return NULL; } return mqd_mem_obj; - } static void init_mqd(struct mqd_manager *mm, void **mqd, -- cgit 1.4.1 From 82934a338b4f6c6577448dbceeb09e7f765e6d91 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 31 May 2023 14:35:30 +0800 Subject: soc: mediatek: SVS: Fix MT8192 GPU node name [ Upstream commit 95094495401bdf6a0649d220dfd095e6079b5e39 ] Device tree node names should be generic. The planned device node name for the GPU, according to the bindings and posted DT changes, is "gpu", not "mali". Fix the GPU node name in the SVS driver to follow. Fixes: 0bbb09b2af9d ("soc: mediatek: SVS: add mt8192 SVS GPU driver") Signed-off-by: Chen-Yu Tsai Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Alexandre Mergnat Link: https://lore.kernel.org/r/20230531063532.2240038-1-wenst@chromium.org Signed-off-by: Matthias Brugger Signed-off-by: Sasha Levin --- drivers/soc/mediatek/mtk-svs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index e55fb16fdc5a..f00cd5c72349 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -2114,9 +2114,9 @@ static int svs_mt8192_platform_probe(struct svs_platform *svsp) svsb = &svsp->banks[idx]; if (svsb->type == SVSB_HIGH) - svsb->opp_dev = svs_add_device_link(svsp, "mali"); + svsb->opp_dev = svs_add_device_link(svsp, "gpu"); else if (svsb->type == SVSB_LOW) - svsb->opp_dev = svs_get_subsys_device(svsp, "mali"); + svsb->opp_dev = svs_get_subsys_device(svsp, "gpu"); if (IS_ERR(svsb->opp_dev)) return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev), -- cgit 1.4.1 From c1164aeb9691817d23c8e8ed886c91ea1bdca76e Mon Sep 17 00:00:00 2001 From: Aurabindo Pillai Date: Wed, 17 May 2023 14:39:46 -0400 Subject: drm/amd/display: Fix artifacting on eDP panels when engaging freesync video mode [ Upstream commit b18f05a0666aecd5cb19c26a8305bcfa4e9d6502 ] [Why] When freesync video mode is enabled, switching resolution from native mode to one of the freesync video compatible modes can trigger continous artifacts on some eDP panels when running under KDE. The articating can be seen in the attached bug report. [How] Fix this by restricting updates that require full commit by using the same checks for stream and scaling changes in the the enable pass of dm_update_crtc_state() along with the check for compatible timings for freesync vide mode. Bug: https://gitlab.freedesktop.org/drm/amd/-/issues/2162 Fixes: da5e14909776 ("drm/amd/display: Fix hang when skipping modeset") Signed-off-by: Aurabindo Pillai Reviewed-by: Rodrigo Siqueira Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 91c308cf27eb..b854eec2787e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -8873,6 +8873,8 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, /* Now check if we should set freesync video mode */ if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream && + dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) && + dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream) && is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state)) { new_crtc_state->mode_changed = false; -- cgit 1.4.1 From 1420545b8a155416b8bc2bb86a7709e9ca0c620c Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Fri, 19 May 2023 08:33:27 -0700 Subject: drm/radeon: fix possible division-by-zero errors [ Upstream commit 1becc57cd1a905e2aa0e1eca60d2a37744525c4a ] Function rv740_get_decoded_reference_divider() may return 0 due to unpredictable reference divider value calculated in radeon_atom_get_clock_dividers(). This will lead to division-by-zero error once that value is used as a divider in calculating 'clk_s'. While unlikely, this issue should nonetheless be prevented so add a sanity check for such cases by testing 'decoded_ref' value against 0. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. v2: minor coding style fixes (Alex) In practice this should actually happen as the vbios should be properly populated. Fixes: 66229b200598 ("drm/radeon/kms: add dpm support for rv7xx (v4)") Signed-off-by: Nikita Zhandarovich Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/radeon/cypress_dpm.c | 8 ++++++-- drivers/gpu/drm/radeon/ni_dpm.c | 8 ++++++-- drivers/gpu/drm/radeon/rv740_dpm.c | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index fdddbbaecbb7..72a0768df00f 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -557,8 +557,12 @@ static int cypress_populate_mclk_value(struct radeon_device *rdev, ASIC_INTERNAL_MEMORY_SS, vco_freq)) { u32 reference_clock = rdev->clock.mpll.reference_freq; u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div); - u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate); - u32 clk_v = ss.percentage * + u32 clk_s, clk_v; + + if (!decoded_ref) + return -EINVAL; + clk_s = reference_clock * 5 / (decoded_ref * ss.rate); + clk_v = ss.percentage * (0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625); mpll_ss1 &= ~CLKV_MASK; diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 672d2239293e..3e1c1a392fb7 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -2241,8 +2241,12 @@ static int ni_populate_mclk_value(struct radeon_device *rdev, ASIC_INTERNAL_MEMORY_SS, vco_freq)) { u32 reference_clock = rdev->clock.mpll.reference_freq; u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div); - u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate); - u32 clk_v = ss.percentage * + u32 clk_s, clk_v; + + if (!decoded_ref) + return -EINVAL; + clk_s = reference_clock * 5 / (decoded_ref * ss.rate); + clk_v = ss.percentage * (0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625); mpll_ss1 &= ~CLKV_MASK; diff --git a/drivers/gpu/drm/radeon/rv740_dpm.c b/drivers/gpu/drm/radeon/rv740_dpm.c index d57a3e1df8d6..4464fd21a302 100644 --- a/drivers/gpu/drm/radeon/rv740_dpm.c +++ b/drivers/gpu/drm/radeon/rv740_dpm.c @@ -249,8 +249,12 @@ int rv740_populate_mclk_value(struct radeon_device *rdev, ASIC_INTERNAL_MEMORY_SS, vco_freq)) { u32 reference_clock = rdev->clock.mpll.reference_freq; u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div); - u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate); - u32 clk_v = 0x40000 * ss.percentage * + u32 clk_s, clk_v; + + if (!decoded_ref) + return -EINVAL; + clk_s = reference_clock * 5 / (decoded_ref * ss.rate); + clk_v = 0x40000 * ss.percentage * (dividers.whole_fb_div + (dividers.frac_fb_div / 8)) / (clk_s * 10000); mpll_ss1 &= ~CLKV_MASK; -- cgit 1.4.1 From 78cb71dd609b4348b1fd5152c46df041c4125fe4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 23 May 2023 17:10:59 +0200 Subject: HID: uclogic: Modular KUnit tests should not depend on KUNIT=y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 49904a0ebf23b15aad288a10f5354e7cd8193121 ] While KUnit tests that cannot be built as a loadable module must depend on "KUNIT=y", this is not true for modular tests, where it adds an unnecessary limitation. Fix this by relaxing the dependency to "KUNIT". Fixes: 08809e482a1c44d9 ("HID: uclogic: KUnit best practices and naming conventions") Signed-off-by: Geert Uytterhoeven Reviewed-by: David Gow Reviewed-by: José Expósito Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 185a077d59cd..c1873ccc7248 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1262,7 +1262,7 @@ config HID_MCP2221 config HID_KUNIT_TEST tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS - depends on KUNIT=y + depends on KUNIT depends on HID_UCLOGIC default KUNIT_ALL_TESTS help -- cgit 1.4.1 From 34bbf074f729cdb24922489789780a3dbc667541 Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Thu, 3 Nov 2022 12:09:59 -0500 Subject: RDMA/rxe: Add ibdev_dbg macros for rxe [ Upstream commit 4554bac48a8c464ff00136a64efe8847e4da4ea8 ] Add macros borrowed from siw to call dynamic debug macro ibdev_dbg. Link: https://lore.kernel.org/r/20221103171013.20659-2-rpearsonhpe@gmail.com Signed-off-by: Bob Pearson Signed-off-by: Jason Gunthorpe Stable-dep-of: 425e1c9018fd ("RDMA/rxe: Fix access checks in rxe_check_bind_mw") Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/infiniband/sw/rxe/rxe.h b/drivers/infiniband/sw/rxe/rxe.h index 30fbdf3bc76a..ab334900fcc3 100644 --- a/drivers/infiniband/sw/rxe/rxe.h +++ b/drivers/infiniband/sw/rxe/rxe.h @@ -38,6 +38,25 @@ #define RXE_ROCE_V2_SPORT (0xc000) +#define rxe_dbg(rxe, fmt, ...) ibdev_dbg(&(rxe)->ib_dev, \ + "%s: " fmt, __func__, ##__VA_ARGS__) +#define rxe_dbg_uc(uc, fmt, ...) ibdev_dbg((uc)->ibuc.device, \ + "uc#%d %s: " fmt, (uc)->elem.index, __func__, ##__VA_ARGS__) +#define rxe_dbg_pd(pd, fmt, ...) ibdev_dbg((pd)->ibpd.device, \ + "pd#%d %s: " fmt, (pd)->elem.index, __func__, ##__VA_ARGS__) +#define rxe_dbg_ah(ah, fmt, ...) ibdev_dbg((ah)->ibah.device, \ + "ah#%d %s: " fmt, (ah)->elem.index, __func__, ##__VA_ARGS__) +#define rxe_dbg_srq(srq, fmt, ...) ibdev_dbg((srq)->ibsrq.device, \ + "srq#%d %s: " fmt, (srq)->elem.index, __func__, ##__VA_ARGS__) +#define rxe_dbg_qp(qp, fmt, ...) ibdev_dbg((qp)->ibqp.device, \ + "qp#%d %s: " fmt, (qp)->elem.index, __func__, ##__VA_ARGS__) +#define rxe_dbg_cq(cq, fmt, ...) ibdev_dbg((cq)->ibcq.device, \ + "cq#%d %s: " fmt, (cq)->elem.index, __func__, ##__VA_ARGS__) +#define rxe_dbg_mr(mr, fmt, ...) ibdev_dbg((mr)->ibmr.device, \ + "mr#%d %s: " fmt, (mr)->elem.index, __func__, ##__VA_ARGS__) +#define rxe_dbg_mw(mw, fmt, ...) ibdev_dbg((mw)->ibmw.device, \ + "mw#%d %s: " fmt, (mw)->elem.index, __func__, ##__VA_ARGS__) + void rxe_set_mtu(struct rxe_dev *rxe, unsigned int dev_mtu); int rxe_add(struct rxe_dev *rxe, unsigned int mtu, const char *ibdev_name); -- cgit 1.4.1 From 0cd210c594a69e5a7b66c8cfd228e041396e145f Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Thu, 3 Nov 2022 12:10:03 -0500 Subject: RDMA/rxe: Replace pr_xxx by rxe_dbg_xxx in rxe_mw.c [ Upstream commit e8a87efdf87455454d0a14fd486c679769bfeee2 ] Replace calls to pr_xxx() int rxe_mw.c with rxe_dbg_xxx(). Link: https://lore.kernel.org/r/20221103171013.20659-6-rpearsonhpe@gmail.com Signed-off-by: Bob Pearson Signed-off-by: Jason Gunthorpe Stable-dep-of: 425e1c9018fd ("RDMA/rxe: Fix access checks in rxe_check_bind_mw") Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_mw.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c index 902b7df7aaed..70252991320a 100644 --- a/drivers/infiniband/sw/rxe/rxe_mw.c +++ b/drivers/infiniband/sw/rxe/rxe_mw.c @@ -52,14 +52,14 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, { if (mw->ibmw.type == IB_MW_TYPE_1) { if (unlikely(mw->state != RXE_MW_STATE_VALID)) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to bind a type 1 MW not in the valid state\n"); return -EINVAL; } /* o10-36.2.2 */ if (unlikely((mw->access & IB_ZERO_BASED))) { - pr_err_once("attempt to bind a zero based type 1 MW\n"); + rxe_dbg_mw(mw, "attempt to bind a zero based type 1 MW\n"); return -EINVAL; } } @@ -67,21 +67,21 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, if (mw->ibmw.type == IB_MW_TYPE_2) { /* o10-37.2.30 */ if (unlikely(mw->state != RXE_MW_STATE_FREE)) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to bind a type 2 MW not in the free state\n"); return -EINVAL; } /* C10-72 */ if (unlikely(qp->pd != to_rpd(mw->ibmw.pd))) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to bind type 2 MW with qp with different PD\n"); return -EINVAL; } /* o10-37.2.40 */ if (unlikely(!mr || wqe->wr.wr.mw.length == 0)) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to invalidate type 2 MW by binding with NULL or zero length MR\n"); return -EINVAL; } @@ -92,13 +92,13 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, return 0; if (unlikely(mr->access & IB_ZERO_BASED)) { - pr_err_once("attempt to bind MW to zero based MR\n"); + rxe_dbg_mw(mw, "attempt to bind MW to zero based MR\n"); return -EINVAL; } /* C10-73 */ if (unlikely(!(mr->access & IB_ACCESS_MW_BIND))) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to bind an MW to an MR without bind access\n"); return -EINVAL; } @@ -107,7 +107,7 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, if (unlikely((mw->access & (IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_ATOMIC)) && !(mr->access & IB_ACCESS_LOCAL_WRITE))) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to bind an Writable MW to an MR without local write access\n"); return -EINVAL; } @@ -115,7 +115,7 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, /* C10-75 */ if (mw->access & IB_ZERO_BASED) { if (unlikely(wqe->wr.wr.mw.length > mr->ibmr.length)) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to bind a ZB MW outside of the MR\n"); return -EINVAL; } @@ -123,7 +123,7 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, if (unlikely((wqe->wr.wr.mw.addr < mr->ibmr.iova) || ((wqe->wr.wr.mw.addr + wqe->wr.wr.mw.length) > (mr->ibmr.iova + mr->ibmr.length)))) { - pr_err_once( + rxe_dbg_mw(mw, "attempt to bind a VA MW outside of the MR\n"); return -EINVAL; } -- cgit 1.4.1 From 1afca9e0fe94c65a95c205f695394bc54f3f65b6 Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Tue, 30 May 2023 17:13:32 -0500 Subject: RDMA/rxe: Fix access checks in rxe_check_bind_mw [ Upstream commit 425e1c9018fdf25cb4531606cc92d9d01a55534f ] The subroutine rxe_check_bind_mw() in rxe_mw.c performs checks on the mw access flags before they are set so they always succeed. This patch instead checks the access flags passed in the send wqe. Fixes: 32a577b4c3a9 ("RDMA/rxe: Add support for bind MW work requests") Link: https://lore.kernel.org/r/20230530221334.89432-4-rpearsonhpe@gmail.com Signed-off-by: Bob Pearson Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_mw.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c index 70252991320a..cebc9f0f428d 100644 --- a/drivers/infiniband/sw/rxe/rxe_mw.c +++ b/drivers/infiniband/sw/rxe/rxe_mw.c @@ -48,7 +48,7 @@ int rxe_dealloc_mw(struct ib_mw *ibmw) } static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, - struct rxe_mw *mw, struct rxe_mr *mr) + struct rxe_mw *mw, struct rxe_mr *mr, int access) { if (mw->ibmw.type == IB_MW_TYPE_1) { if (unlikely(mw->state != RXE_MW_STATE_VALID)) { @@ -58,7 +58,7 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } /* o10-36.2.2 */ - if (unlikely((mw->access & IB_ZERO_BASED))) { + if (unlikely((access & IB_ZERO_BASED))) { rxe_dbg_mw(mw, "attempt to bind a zero based type 1 MW\n"); return -EINVAL; } @@ -104,7 +104,7 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } /* C10-74 */ - if (unlikely((mw->access & + if (unlikely((access & (IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_ATOMIC)) && !(mr->access & IB_ACCESS_LOCAL_WRITE))) { rxe_dbg_mw(mw, @@ -113,7 +113,7 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } /* C10-75 */ - if (mw->access & IB_ZERO_BASED) { + if (access & IB_ZERO_BASED) { if (unlikely(wqe->wr.wr.mw.length > mr->ibmr.length)) { rxe_dbg_mw(mw, "attempt to bind a ZB MW outside of the MR\n"); @@ -133,12 +133,12 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } static void rxe_do_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, - struct rxe_mw *mw, struct rxe_mr *mr) + struct rxe_mw *mw, struct rxe_mr *mr, int access) { u32 key = wqe->wr.wr.mw.rkey & 0xff; mw->rkey = (mw->rkey & ~0xff) | key; - mw->access = wqe->wr.wr.mw.access; + mw->access = access; mw->state = RXE_MW_STATE_VALID; mw->addr = wqe->wr.wr.mw.addr; mw->length = wqe->wr.wr.mw.length; @@ -169,6 +169,7 @@ int rxe_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe) struct rxe_dev *rxe = to_rdev(qp->ibqp.device); u32 mw_rkey = wqe->wr.wr.mw.mw_rkey; u32 mr_lkey = wqe->wr.wr.mw.mr_lkey; + int access = wqe->wr.wr.mw.access; mw = rxe_pool_get_index(&rxe->mw_pool, mw_rkey >> 8); if (unlikely(!mw)) { @@ -198,11 +199,11 @@ int rxe_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe) spin_lock_bh(&mw->lock); - ret = rxe_check_bind_mw(qp, wqe, mw, mr); + ret = rxe_check_bind_mw(qp, wqe, mw, mr, access); if (ret) goto err_unlock; - rxe_do_bind_mw(qp, wqe, mw, mr); + rxe_do_bind_mw(qp, wqe, mw, mr, access); err_unlock: spin_unlock_bh(&mw->lock); err_drop_mr: -- cgit 1.4.1 From b10db1d2137415e5e7f9706d96cfe77539c499d4 Mon Sep 17 00:00:00 2001 From: Chia-I Wu Date: Thu, 1 Jun 2023 15:44:12 -0700 Subject: amdgpu: validate offset_in_bo of drm_amdgpu_gem_va MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9f0bcf49e9895cb005d78b33a5eebfa11711b425 ] This is motivated by OOB access in amdgpu_vm_update_range when offset_in_bo+map_size overflows. v2: keep the validations in amdgpu_vm_bo_map v3: add the validations to amdgpu_vm_bo_map/amdgpu_vm_bo_replace_map rather than to amdgpu_gem_va_ioctl Fixes: 9f7eb5367d00 ("drm/amdgpu: actually use the VM map parameters") Reviewed-by: Christian König Signed-off-by: Chia-I Wu Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 5a8a4cda7e98..58fe7279599f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1427,14 +1427,14 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev, uint64_t eaddr; /* validate the parameters */ - if (saddr & ~PAGE_MASK || offset & ~PAGE_MASK || - size == 0 || size & ~PAGE_MASK) + if (saddr & ~PAGE_MASK || offset & ~PAGE_MASK || size & ~PAGE_MASK) + return -EINVAL; + if (saddr + size <= saddr || offset + size <= offset) return -EINVAL; /* make sure object fit at this offset */ eaddr = saddr + size - 1; - if (saddr >= eaddr || - (bo && offset + size > amdgpu_bo_size(bo)) || + if ((bo && offset + size > amdgpu_bo_size(bo)) || (eaddr >= adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT)) return -EINVAL; @@ -1493,14 +1493,14 @@ int amdgpu_vm_bo_replace_map(struct amdgpu_device *adev, int r; /* validate the parameters */ - if (saddr & ~PAGE_MASK || offset & ~PAGE_MASK || - size == 0 || size & ~PAGE_MASK) + if (saddr & ~PAGE_MASK || offset & ~PAGE_MASK || size & ~PAGE_MASK) + return -EINVAL; + if (saddr + size <= saddr || offset + size <= offset) return -EINVAL; /* make sure object fit at this offset */ eaddr = saddr + size - 1; - if (saddr >= eaddr || - (bo && offset + size > amdgpu_bo_size(bo)) || + if ((bo && offset + size > amdgpu_bo_size(bo)) || (eaddr >= adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT)) return -EINVAL; -- cgit 1.4.1 From 154bed0fd609e1c195d582ec3b6f647c5859acaf Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Apr 2023 04:13:29 +0300 Subject: drm/msm/a5xx: really check for A510 in a5xx_gpu_init [ Upstream commit 736a9327365644b460e4498b1ce172ca411efcbc ] The commit 010c8bbad2cb ("drm: msm: adreno: Disable preemption on Adreno 510") added special handling for a510 (this SKU doesn't seem to support preemption, so the driver should clamp nr_rings to 1). However the gpu->revn is not yet set (it is set later, in adreno_gpu_init()) and thus the condition is always false. Check config->rev instead. Fixes: 010c8bbad2cb ("drm: msm: adreno: Disable preemption on Adreno 510") Reported-by: Adam Skladowski Signed-off-by: Dmitry Baryshkov Tested-by: Adam Skladowski Patchwork: https://patchwork.freedesktop.org/patch/531511/ Signed-off-by: Rob Clark Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 24feae285ccd..0829eaf2cd4e 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1740,6 +1740,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev = priv->gpu_pdev; + struct adreno_platform_config *config = pdev->dev.platform_data; struct a5xx_gpu *a5xx_gpu = NULL; struct adreno_gpu *adreno_gpu; struct msm_gpu *gpu; @@ -1766,7 +1767,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) nr_rings = 4; - if (adreno_is_a510(adreno_gpu)) + if (adreno_cmp_rev(ADRENO_REV(5, 1, 0, ANY_ID), config->rev)) nr_rings = 1; ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, nr_rings); -- cgit 1.4.1 From c9be352be9bb15e6b83e40abc4df7f4776b435ba Mon Sep 17 00:00:00 2001 From: Kashyap Desai Date: Fri, 9 Jun 2023 04:01:38 -0700 Subject: RDMA/bnxt_re: wraparound mbox producer index [ Upstream commit 0af91306e17ef3d18e5f100aa58aa787869118af ] Driver is not handling the wraparound of the mbox producer index correctly. Currently the wraparound happens once u32 max is reached. Bit 31 of the producer index register is special and should be set only once for the first command. Because the producer index overflow setting bit31 after a long time, FW goes to initialization sequence and this causes FW hang. Fix is to wraparound the mbox producer index once it reaches u16 max. Fixes: cee0c7bba486 ("RDMA/bnxt_re: Refactor command queue management code") Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Signed-off-by: Kashyap Desai Signed-off-by: Selvin Xavier Link: https://lore.kernel.org/r/1686308514-11996-2-git-send-email-selvin.xavier@broadcom.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 9c63b8b62edf..3d76fa71641a 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -181,7 +181,7 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, struct cmdq_base *req, } while (size > 0); cmdq->seq_num++; - cmdq_prod = hwq->prod; + cmdq_prod = hwq->prod & 0xFFFF; if (test_bit(FIRMWARE_FIRST_FLAG, &cmdq->flags)) { /* The very first doorbell write * is required to set this flag @@ -598,7 +598,7 @@ int bnxt_qplib_alloc_rcfw_channel(struct bnxt_qplib_res *res, rcfw->cmdq_depth = BNXT_QPLIB_CMDQE_MAX_CNT_8192; sginfo.pgsize = bnxt_qplib_cmdqe_page_size(rcfw->cmdq_depth); - hwq_attr.depth = rcfw->cmdq_depth; + hwq_attr.depth = rcfw->cmdq_depth & 0x7FFFFFFF; hwq_attr.stride = BNXT_QPLIB_CMDQE_UNITS; hwq_attr.type = HWQ_TYPE_CTX; if (bnxt_qplib_alloc_init_hwq(&cmdq->hwq, &hwq_attr)) { -- cgit 1.4.1 From bf7ab557d64a32307ffe05a7c5383ddab83a4a03 Mon Sep 17 00:00:00 2001 From: Kashyap Desai Date: Fri, 9 Jun 2023 04:01:39 -0700 Subject: RDMA/bnxt_re: Avoid calling wake_up threads from spin_lock context [ Upstream commit 3099bcdc19b701f732f638ee45679858c08559bb ] bnxt_qplib_service_creq can be called from interrupt or tasklet or process context. So the function take irq variant of spin_lock. But when wake_up is invoked with the lock held, it is putting the calling context to sleep. [exception RIP: __wake_up_common+190] RIP: ffffffffb7539d7e RSP: ffffa73300207ad8 RFLAGS: 00000083 RAX: 0000000000000001 RBX: ffff91fa295f69b8 RCX: dead000000000200 RDX: ffffa733344af940 RSI: ffffa73336527940 RDI: ffffa73336527940 RBP: 000000000000001c R8: 0000000000000002 R9: 00000000000299c0 R10: 0000017230de82c5 R11: 0000000000000002 R12: ffffa73300207b28 R13: 0000000000000000 R14: ffffa733341bf928 R15: 0000000000000000 ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018 Call the wakeup after releasing the lock. Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Signed-off-by: Kashyap Desai Signed-off-by: Selvin Xavier Link: https://lore.kernel.org/r/1686308514-11996-3-git-send-email-selvin.xavier@broadcom.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 3d76fa71641a..75e0c42f6f42 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -299,7 +299,8 @@ static int bnxt_qplib_process_func_event(struct bnxt_qplib_rcfw *rcfw, } static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, - struct creq_qp_event *qp_event) + struct creq_qp_event *qp_event, + u32 *num_wait) { struct creq_qp_error_notification *err_event; struct bnxt_qplib_hwq *hwq = &rcfw->cmdq.hwq; @@ -308,6 +309,7 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, u16 cbit, blocked = 0; struct pci_dev *pdev; unsigned long flags; + u32 wait_cmds = 0; __le16 mcookie; u16 cookie; int rc = 0; @@ -367,9 +369,10 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, crsqe->req_size = 0; if (!blocked) - wake_up(&rcfw->cmdq.waitq); + wait_cmds++; spin_unlock_irqrestore(&hwq->lock, flags); } + *num_wait += wait_cmds; return rc; } @@ -383,6 +386,7 @@ static void bnxt_qplib_service_creq(struct tasklet_struct *t) struct creq_base *creqe; u32 sw_cons, raw_cons; unsigned long flags; + u32 num_wakeup = 0; /* Service the CREQ until budget is over */ spin_lock_irqsave(&hwq->lock, flags); @@ -401,7 +405,8 @@ static void bnxt_qplib_service_creq(struct tasklet_struct *t) switch (type) { case CREQ_BASE_TYPE_QP_EVENT: bnxt_qplib_process_qp_event - (rcfw, (struct creq_qp_event *)creqe); + (rcfw, (struct creq_qp_event *)creqe, + &num_wakeup); creq->stats.creq_qp_event_processed++; break; case CREQ_BASE_TYPE_FUNC_EVENT: @@ -429,6 +434,8 @@ static void bnxt_qplib_service_creq(struct tasklet_struct *t) rcfw->res->cctx, true); } spin_unlock_irqrestore(&hwq->lock, flags); + if (num_wakeup) + wake_up_nr(&rcfw->cmdq.waitq, num_wakeup); } static irqreturn_t bnxt_qplib_creq_irq(int irq, void *dev_instance) -- cgit 1.4.1 From 1839032251a66f2ae5a043c495532830a55d28c4 Mon Sep 17 00:00:00 2001 From: Kai Ma Date: Tue, 18 Apr 2023 11:34:51 +0000 Subject: clk: imx: clk-imxrt1050: fix memory leak in imxrt1050_clocks_probe [ Upstream commit 1b280598ab3bd8a2dc8b96a12530d5b1ee7a8f4a ] Use devm_of_iomap() instead of of_iomap() to automatically handle the unused ioremap region. If any error occurs, regions allocated by kzalloc() will leak, but using devm_kzalloc() instead will automatically free the memory using devm_kfree(). Also, fix error handling of hws by adding unregister_hws label, which unregisters remaining hws when iomap failed. Fixes: 7154b046d8f3 ("clk: imx: Add initial support for i.MXRT1050 clock driver") Signed-off-by: Kai Ma Reviewed-by: Peng Fan Acked-by: Jesse Taube Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230418113451.151312-1-kaima@hust.edu.cn Signed-off-by: Abel Vesa Signed-off-by: Sasha Levin --- drivers/clk/imx/clk-imxrt1050.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk-imxrt1050.c b/drivers/clk/imx/clk-imxrt1050.c index 26108e9f7e67..64d8b65a8104 100644 --- a/drivers/clk/imx/clk-imxrt1050.c +++ b/drivers/clk/imx/clk-imxrt1050.c @@ -42,7 +42,7 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev) struct device_node *anp; int ret; - clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMXRT1050_CLK_END), GFP_KERNEL); if (WARN_ON(!clk_hw_data)) return -ENOMEM; @@ -53,10 +53,12 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev) hws[IMXRT1050_CLK_OSC] = imx_obtain_fixed_clk_hw(np, "osc"); anp = of_find_compatible_node(NULL, NULL, "fsl,imxrt-anatop"); - pll_base = of_iomap(anp, 0); + pll_base = devm_of_iomap(dev, anp, 0, NULL); of_node_put(anp); - if (WARN_ON(!pll_base)) - return -ENOMEM; + if (WARN_ON(IS_ERR(pll_base))) { + ret = PTR_ERR(pll_base); + goto unregister_hws; + } /* Anatop clocks */ hws[IMXRT1050_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0UL); @@ -104,8 +106,10 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev) /* CCM clocks */ ccm_base = devm_platform_ioremap_resource(pdev, 0); - if (WARN_ON(IS_ERR(ccm_base))) - return PTR_ERR(ccm_base); + if (WARN_ON(IS_ERR(ccm_base))) { + ret = PTR_ERR(ccm_base); + goto unregister_hws; + } hws[IMXRT1050_CLK_ARM_PODF] = imx_clk_hw_divider("arm_podf", "pll1_arm", ccm_base + 0x10, 0, 3); hws[IMXRT1050_CLK_PRE_PERIPH_SEL] = imx_clk_hw_mux("pre_periph_sel", ccm_base + 0x18, 18, 2, @@ -148,8 +152,12 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev) ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); if (ret < 0) { dev_err(dev, "Failed to register clks for i.MXRT1050.\n"); - imx_unregister_hw_clocks(hws, IMXRT1050_CLK_END); + goto unregister_hws; } + return 0; + +unregister_hws: + imx_unregister_hw_clocks(hws, IMXRT1050_CLK_END); return ret; } static const struct of_device_id imxrt1050_clk_of_match[] = { -- cgit 1.4.1 From 9ba3693b0350b154fdd7830559bbc7b04c067096 Mon Sep 17 00:00:00 2001 From: Hao Luo Date: Tue, 11 Apr 2023 09:51:07 +0800 Subject: clk: imx: clk-imx8mn: fix memory leak in imx8mn_clocks_probe [ Upstream commit 188d070de9132667956f5aadd98d2bd87d3eac89 ] Use devm_of_iomap() instead of of_iomap() to automatically handle the unused ioremap region. If any error occurs, regions allocated by kzalloc() will leak, but using devm_kzalloc() instead will automatically free the memory using devm_kfree(). Fixes: daeb14545514 ("clk: imx: imx8mn: Switch to clk_hw based API") Fixes: 96d6392b54db ("clk: imx: Add support for i.MX8MN clock driver") Signed-off-by: Hao Luo Reviewed-by: Dongliang Mu Reviewed-by: Peng Fan Link: https://lore.kernel.org/r/20230411015107.2645-1-m202171776@hust.edu.cn Signed-off-by: Abel Vesa Signed-off-by: Sasha Levin --- drivers/clk/imx/clk-imx8mn.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 2afea905f7f3..b2b8b8b3f0ab 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -323,7 +323,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) void __iomem *base; int ret; - clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMX8MN_CLK_END), GFP_KERNEL); if (WARN_ON(!clk_hw_data)) return -ENOMEM; @@ -340,10 +340,10 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) hws[IMX8MN_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4"); np = of_find_compatible_node(NULL, NULL, "fsl,imx8mn-anatop"); - base = of_iomap(np, 0); + base = devm_of_iomap(dev, np, 0, NULL); of_node_put(np); - if (WARN_ON(!base)) { - ret = -ENOMEM; + if (WARN_ON(IS_ERR(base))) { + ret = PTR_ERR(base); goto unregister_hws; } -- cgit 1.4.1 From 280a5ff665e12d1e0c54c20cedc9c5008aa686a5 Mon Sep 17 00:00:00 2001 From: Zhanhao Hu Date: Thu, 1 Jun 2023 03:38:25 +0000 Subject: clk: imx93: fix memory leak and missing unwind goto in imx93_clocks_probe [ Upstream commit e02ba11b457647050cb16e7cad16cec3c252fade ] In function probe(), it returns directly without unregistered hws when error occurs. Fix this by adding 'goto unregister_hws;' on line 295 and line 310. Use devm_kzalloc() instead of kzalloc() to automatically free the memory using devm_kfree() when error occurs. Replace of_iomap() with devm_of_iomap() to automatically handle the unused ioremap region and delete 'iounmap(anatop_base);' in unregister_hws. Fixes: 24defbe194b6 ("clk: imx: add i.MX93 clk") Signed-off-by: Zhanhao Hu Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230601033825.336558-1-zero12113@hust.edu.cn Signed-off-by: Abel Vesa Signed-off-by: Sasha Levin --- drivers/clk/imx/clk-imx93.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index 02d6a9894521..5e3d299190c8 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -261,7 +261,7 @@ static int imx93_clocks_probe(struct platform_device *pdev) void __iomem *base, *anatop_base; int i, ret; - clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMX93_CLK_END), GFP_KERNEL); if (!clk_hw_data) return -ENOMEM; @@ -285,10 +285,12 @@ static int imx93_clocks_probe(struct platform_device *pdev) "sys_pll_pfd2", 1, 2); np = of_find_compatible_node(NULL, NULL, "fsl,imx93-anatop"); - anatop_base = of_iomap(np, 0); + anatop_base = devm_of_iomap(dev, np, 0, NULL); of_node_put(np); - if (WARN_ON(!anatop_base)) - return -ENOMEM; + if (WARN_ON(IS_ERR(anatop_base))) { + ret = PTR_ERR(base); + goto unregister_hws; + } clks[IMX93_CLK_AUDIO_PLL] = imx_clk_fracn_gppll("audio_pll", "osc_24m", anatop_base + 0x1200, &imx_fracn_gppll); @@ -298,8 +300,8 @@ static int imx93_clocks_probe(struct platform_device *pdev) np = dev->of_node; base = devm_platform_ioremap_resource(pdev, 0); if (WARN_ON(IS_ERR(base))) { - iounmap(anatop_base); - return PTR_ERR(base); + ret = PTR_ERR(base); + goto unregister_hws; } for (i = 0; i < ARRAY_SIZE(root_array); i++) { @@ -329,7 +331,6 @@ static int imx93_clocks_probe(struct platform_device *pdev) unregister_hws: imx_unregister_hw_clocks(clks, IMX93_CLK_END); - iounmap(anatop_base); return ret; } -- cgit 1.4.1 From 6317d0302655f7e854cd4f31e93b47d35cb058bb Mon Sep 17 00:00:00 2001 From: Yuxing Liu Date: Wed, 3 May 2023 07:06:07 +0000 Subject: clk: imx: clk-imx8mp: improve error handling in imx8mp_clocks_probe() [ Upstream commit 878b02d5f3b56cb090dbe2c70c89273be144087f ] Replace of_iomap() and kzalloc() with devm_of_iomap() and devm_kzalloc() which can automatically release the related memory when the device or driver is removed or unloaded to avoid potential memory leak. In this case, iounmap(anatop_base) in line 427,433 are removed as manual release is not required. Besides, referring to clk-imx8mq.c, check the return code of of_clk_add_hw_provider, if it returns negtive, print error info and unregister hws, which makes the program more robust. Fixes: 9c140d992676 ("clk: imx: Add support for i.MX8MP clock driver") Signed-off-by: Yuxing Liu Reviewed-by: Dongliang Mu Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20230503070607.2462-1-lyx2022@hust.edu.cn Signed-off-by: Abel Vesa Signed-off-by: Sasha Levin --- drivers/clk/imx/clk-imx8mp.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index 5d68d975b4eb..05c02f4e2a14 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -413,25 +413,22 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np; void __iomem *anatop_base, *ccm_base; + int err; np = of_find_compatible_node(NULL, NULL, "fsl,imx8mp-anatop"); - anatop_base = of_iomap(np, 0); + anatop_base = devm_of_iomap(dev, np, 0, NULL); of_node_put(np); - if (WARN_ON(!anatop_base)) - return -ENOMEM; + if (WARN_ON(IS_ERR(anatop_base))) + return PTR_ERR(anatop_base); np = dev->of_node; ccm_base = devm_platform_ioremap_resource(pdev, 0); - if (WARN_ON(IS_ERR(ccm_base))) { - iounmap(anatop_base); + if (WARN_ON(IS_ERR(ccm_base))) return PTR_ERR(ccm_base); - } - clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, IMX8MP_CLK_END), GFP_KERNEL); - if (WARN_ON(!clk_hw_data)) { - iounmap(anatop_base); + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMX8MP_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) return -ENOMEM; - } clk_hw_data->num = IMX8MP_CLK_END; hws = clk_hw_data->hws; @@ -711,7 +708,12 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) imx_check_clk_hws(hws, IMX8MP_CLK_END); - of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + if (err < 0) { + dev_err(dev, "failed to register hws for i.MX8MP\n"); + imx_unregister_hw_clocks(hws, IMX8MP_CLK_END); + return err; + } imx_register_uart_clocks(4); -- cgit 1.4.1 From 7089f1aa0b55c2a5196c127bd93aa3c148039832 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 31 May 2023 15:22:40 +0200 Subject: arm64: dts: qcom: sdm845: Flush RSC sleep & wake votes [ Upstream commit 91e83140b5dd5598fbcfada3ee1f8b2b410c3731 ] The rpmh driver will cache sleep and wake votes until the cluster power-domain is about to enter idle, to avoid unnecessary writes. So associate the apps_rsc with the cluster pd, so that it can be notified about this event. Without this, only AMC votes are being commited. Fixes: c83545d95376 ("arm64: dts: sdm845: Add rpmh-rsc node") Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230531-topic-rsc-v1-6-b4a985f57b8b@linaro.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sdm845.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index 6f4cc3329400..b7ba70857d0a 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -5043,6 +5043,7 @@ , , ; + power-domains = <&CLUSTER_PD>; apps_bcm_voter: bcm-voter { compatible = "qcom,bcm-voter"; -- cgit 1.4.1 From 4842a846398a19692b86e5b97faa8b7a0902efa8 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 6 Jun 2023 23:14:18 +0200 Subject: arm64: dts: qcom: sm8250-edo: Panel framebuffer is 2.5k instead of 4k [ Upstream commit 223ce29c8b7e5b00f01a68387aabeefd77d97f06 ] The framebuffer configuration for edo pdx203, written in edo dtsi (which is overwritten in pdx206 dts for its smaller panel) has to use a 1096x2560 configuration as this is what the panel (and framebuffer area) has been initialized to. Downstream userspace also has access to (and uses) this 2.5k mode by default, and only switches the panel to 4k when requested. This is similar to commit be8de06dc397 ("arm64: dts: qcom: sm8150-kumano: Panel framebuffer is 2.5k instead of 4k") which fixed the same for the previous generation Sony platform. Fixes: 69cdb97ef652 ("arm64: dts: qcom: sm8250: Add support for SONY Xperia 1 II / 5 II (Edo platform)") Signed-off-by: Marijn Suijten Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230606211418.587676-1-marijn.suijten@somainline.org Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi b/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi index e4769dcfaad7..390b90a8ddf7 100644 --- a/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi @@ -26,9 +26,10 @@ framebuffer: framebuffer@9c000000 { compatible = "simple-framebuffer"; reg = <0 0x9c000000 0 0x2300000>; - width = <1644>; - height = <3840>; - stride = <(1644 * 4)>; + /* pdx203 BL initializes in 2.5k mode, not 4k */ + width = <1096>; + height = <2560>; + stride = <(1096 * 4)>; format = "a8r8g8b8"; /* * That's a lot of clocks, but it's necessary due -- cgit 1.4.1 From 9ff9f928c6380cbd8afd65294a3dade4fe0f63f9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 21 Apr 2023 13:41:01 +0300 Subject: clk: bcm: rpi: Fix off by one in raspberrypi_discover_clocks() [ Upstream commit da2edb3e3c09fd1451b7f400ccd1070ef086619a ] Smatch detected an off by one in this code: drivers/clk/bcm/clk-raspberrypi.c:374 raspberrypi_discover_clocks() error: buffer overflow 'data->hws' 16 <= 16 The data->hws[] array has RPI_FIRMWARE_NUM_CLK_ID elements so the > comparison needs to changed to >=. Fixes: 12c90f3f27bb ("clk: bcm: rpi: Add variant structure") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/5a850b08-d2f5-4794-aceb-a6b468965139@kili.mountain Reviewed-by: Stefan Wahren Reviewed-by: Florian Fainelli Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/bcm/clk-raspberrypi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 679f4649a7ef..278f84557281 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -375,9 +375,9 @@ static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, while (clks->id) { struct raspberrypi_clk_variant *variant; - if (clks->id > RPI_FIRMWARE_NUM_CLK_ID) { + if (clks->id >= RPI_FIRMWARE_NUM_CLK_ID) { dev_err(rpi->dev, "Unknown clock id: %u (max: %u)\n", - clks->id, RPI_FIRMWARE_NUM_CLK_ID); + clks->id, RPI_FIRMWARE_NUM_CLK_ID - 1); return -EINVAL; } -- cgit 1.4.1 From b35cb0c05b8dafe23ae5e8b605a91b88bcf4aba7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 11 May 2023 20:01:20 +0300 Subject: clk: clocking-wizard: Fix Oops in clk_wzrd_register_divider() [ Upstream commit 9c632a6396505a019ea6d12b5ab45e659a542a93 ] Smatch detected this potential error pointer dereference clk_wzrd_register_divider(). If devm_clk_hw_register() fails then it sets "hw" to an error pointer and then dereferences it on the next line. Return the error directly instead. Fixes: 5a853722eb32 ("staging: clocking-wizard: Add support for dynamic reconfiguration") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/f0e39b5c-4554-41e0-80d9-54ca3fabd060@kili.mountain Reviewed-by: Michal Simek Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/xilinx/clk-xlnx-clock-wizard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index eb1dfe7ecc1b..4a23583933bc 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -354,7 +354,7 @@ static struct clk *clk_wzrd_register_divider(struct device *dev, hw = &div->hw; ret = devm_clk_hw_register(dev, hw); if (ret) - hw = ERR_PTR(ret); + return ERR_PTR(ret); return hw->clk; } -- cgit 1.4.1 From 96bafece6ff380138896f009141fd7337070e680 Mon Sep 17 00:00:00 2001 From: Yuan Can Date: Fri, 9 Dec 2022 09:41:24 +0000 Subject: clk: tegra: tegra124-emc: Fix potential memory leak [ Upstream commit 53a06e5924c0d43c11379a08c5a78529c3e61595 ] The tegra and tegra needs to be freed in the error handling path, otherwise it will be leaked. Fixes: 2db04f16b589 ("clk: tegra: Add EMC clock driver") Signed-off-by: Yuan Can Link: https://lore.kernel.org/r/20221209094124.71043-1-yuancan@huawei.com Acked-by: Thierry Reding Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/tegra/clk-tegra124-emc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/tegra/clk-tegra124-emc.c b/drivers/clk/tegra/clk-tegra124-emc.c index 219c80653dbd..2a6db0434281 100644 --- a/drivers/clk/tegra/clk-tegra124-emc.c +++ b/drivers/clk/tegra/clk-tegra124-emc.c @@ -464,6 +464,7 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, err = load_one_timing_from_dt(tegra, timing, child); if (err) { of_node_put(child); + kfree(tegra->timings); return err; } @@ -515,6 +516,7 @@ struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np err = load_timings_from_dt(tegra, node, node_ram_code); if (err) { of_node_put(node); + kfree(tegra); return ERR_PTR(err); } } -- cgit 1.4.1 From d28b83252e150155b8b8c65b612c555e93c8b45f Mon Sep 17 00:00:00 2001 From: Su Hui Date: Thu, 15 Jun 2023 10:17:32 +0800 Subject: ALSA: ac97: Fix possible NULL dereference in snd_ac97_mixer [ Upstream commit 79597c8bf64ca99eab385115743131d260339da5 ] smatch error: sound/pci/ac97/ac97_codec.c:2354 snd_ac97_mixer() error: we previously assumed 'rac97' could be null (see line 2072) remove redundant assignment, return error if rac97 is NULL. Fixes: da3cec35dd3c ("ALSA: Kill snd_assert() in sound/pci/*") Signed-off-by: Su Hui Link: https://lore.kernel.org/r/20230615021732.1972194-1-suhui@nfschina.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/pci/ac97/ac97_codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index ff685321f1a1..534ea7a256ec 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -2070,8 +2070,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, .dev_disconnect = snd_ac97_dev_disconnect, }; - if (rac97) - *rac97 = NULL; + if (!rac97) + return -EINVAL; if (snd_BUG_ON(!bus || !template)) return -EINVAL; if (snd_BUG_ON(template->num >= 4)) -- cgit 1.4.1 From 7dca0dde50af0a69533e57cd47b4bad5d6109691 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 12 Jun 2023 21:25:33 +0300 Subject: drm/msm/dpu: do not enable color-management if DSPPs are not available [ Upstream commit 3bcfc7b90465efd337d39b91b43972162f0d1908 ] We can not support color management without DSPP blocks being provided in the HW catalog. Do not enable color management for CRTCs if num_dspps is 0. Fixes: 4259ff7ae509 ("drm/msm/dpu: add support for pcc color block in dpu driver") Reported-by: Yongqin Liu Signed-off-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Reviewed-by: Marijn Suijten Reviewed-by: Sumit Semwal Tested-by: Yongqin Liu Patchwork: https://patchwork.freedesktop.org/patch/542141/ Link: https://lore.kernel.org/r/20230612182534.3345805-1-dmitry.baryshkov@linaro.org Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index c9d1c412628e..6c0ffe8e4adb 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1579,6 +1579,8 @@ static const struct drm_crtc_helper_funcs dpu_crtc_helper_funcs = { struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane, struct drm_plane *cursor) { + struct msm_drm_private *priv = dev->dev_private; + struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms); struct drm_crtc *crtc = NULL; struct dpu_crtc *dpu_crtc = NULL; int i; @@ -1610,7 +1612,8 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane, drm_crtc_helper_add(crtc, &dpu_crtc_helper_funcs); - drm_crtc_enable_color_mgmt(crtc, 0, true, 0); + if (dpu_kms->catalog->dspp_count) + drm_crtc_enable_color_mgmt(crtc, 0, true, 0); /* save user friendly CRTC name for later */ snprintf(dpu_crtc->name, DPU_CRTC_NAME_SIZE, "crtc%u", crtc->base.id); -- cgit 1.4.1 From bc6d856b1c80ff6d04983b02fa07c9a7d99ec41e Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Wed, 24 May 2023 10:45:20 -0700 Subject: drm/msm/dpu: Fix slice_last_group_size calculation [ Upstream commit c223059e6f8340f7eac2319470984cbfc39c433b ] Correct the math for slice_last_group_size so that it matches the calculations downstream. Fixes: c110cfd1753e ("drm/msm/disp/dpu1: Add support for DSC") Reviewed-by: Dmitry Baryshkov Reviewed-by: Marijn Suijten Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/539269/ Link: https://lore.kernel.org/r/20230329-rfc-msm-dsc-helper-v14-7-bafc7be95691@quicinc.com Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c index 3662df698dae..c8f14555834a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c @@ -52,9 +52,10 @@ static void dpu_hw_dsc_config(struct dpu_hw_dsc *hw_dsc, if (is_cmd_mode) initial_lines += 1; - slice_last_group_size = 3 - (dsc->slice_width % 3); + slice_last_group_size = (dsc->slice_width + 2) % 3; + data = (initial_lines << 20); - data |= ((slice_last_group_size - 1) << 18); + data |= (slice_last_group_size << 18); /* bpp is 6.4 format, 4 LSBs bits are for fractional part */ data |= (dsc->bits_per_pixel << 8); data |= (dsc->block_pred_enable << 7); -- cgit 1.4.1 From 937da3db61bff9aa0905e7eaf968e08739df3007 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 22 Dec 2022 00:19:37 +0100 Subject: drm/msm/dsi: Use DSC slice(s) packet size to compute word count [ Upstream commit bc6b6ff8135c4b96787eda88f3baf653939a75ce ] According to downstream the value to use for WORD_COUNT is bytes_per_pkt, which denotes the number of bytes in a packet based on how many slices have been configured by the panel driver times the width of a slice times the number of bytes per pixel. The DSC panels seen thus far use one byte per pixel, only one slice per packet, and a slice width of half the panel width leading to the desired bytes_per_pkt+1 value to be equal to hdisplay/2+1. This however isn't the case anymore for panels that configure two slices per packet, where the value should now be hdisplay+1. Note that the aforementioned panel (on a Sony Xperia XZ3, sdm845) with slice_count=1 has also been tested to successfully accept slice_count=2, which would have shown corrupted output previously. Fixes: 08802f515c3c ("drm/msm/dsi: Add support for DSC configuration") Signed-off-by: Marijn Suijten Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/515694/ Link: https://lore.kernel.org/r/20221221231943.1961117-3-marijn.suijten@somainline.org Signed-off-by: Dmitry Baryshkov Stable-dep-of: 155fa3a91d64 ("drm/msm/dsi: Remove incorrect references to slice_count") Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dsi/dsi_host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index f167a45f1fbd..5ab5e872c3cf 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -987,7 +987,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) if (!msm_host->dsc) wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1; else - wc = mode->hdisplay / 2 + 1; + wc = msm_host->dsc->slice_chunk_size * msm_host->dsc->slice_count + 1; dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | -- cgit 1.4.1 From ef25872788e538c4c879375142d9f8288b458aac Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 22 Dec 2022 00:19:38 +0100 Subject: drm/msm/dsi: Flip greater-than check for slice_count and slice_per_intf [ Upstream commit 82e72fd22a8f9eff4e75c08be68319008ea90a29 ] According to downstream /and the comment copied from it/ this comparison should be the other way around. In other words, when the panel driver requests to use more slices per packet than what could be sent over this interface, it is bumped down to only use a single slice per packet (and strangely not the number of slices that could fit on the interface). Fixes: 08802f515c3c ("drm/msm/dsi: Add support for DSC configuration") Signed-off-by: Marijn Suijten Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/515686/ Link: https://lore.kernel.org/r/20221221231943.1961117-4-marijn.suijten@somainline.org Signed-off-by: Dmitry Baryshkov Stable-dep-of: 155fa3a91d64 ("drm/msm/dsi: Remove incorrect references to slice_count") Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dsi/dsi_host.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 5ab5e872c3cf..ef988e4c2104 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -853,11 +853,12 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod */ slice_per_intf = DIV_ROUND_UP(hdisplay, dsc->slice_width); - /* If slice_per_pkt is greater than slice_per_intf + /* + * If slice_count is greater than slice_per_intf * then default to 1. This can happen during partial * update. */ - if (slice_per_intf > dsc->slice_count) + if (dsc->slice_count > slice_per_intf) dsc->slice_count = 1; total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; -- cgit 1.4.1 From ecf02762d4763bd5efa68b52a3592b4face7c166 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 9 Jun 2023 15:57:17 -0700 Subject: drm/msm/dsi: Remove incorrect references to slice_count [ Upstream commit 155fa3a91d64221eb0885fd221cc8085dbef908f ] Currently, slice_count is being used to calculate word count and pkt_per_line. Instead, these values should be calculated using slice per packet, which is not the same as slice_count. Slice count represents the number of slices per interface, and its value will not always match that of slice per packet. For example, it is possible to have cases where there are multiple slices per interface but the panel specifies only one slice per packet. Thus, use the default value of one slice per packet and remove slice_count from the aforementioned calculations. Fixes: 08802f515c3c ("drm/msm/dsi: Add support for DSC configuration") Fixes: bc6b6ff8135c ("drm/msm/dsi: Use DSC slice(s) packet size to compute word count") Reviewed-by: Marijn Suijten Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/541965/ Link: https://lore.kernel.org/r/20230405-add-dsc-support-v6-5-95eab864d1b6@quicinc.com Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dsi/dsi_host.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index ef988e4c2104..b433ccfe4d7d 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -853,18 +853,17 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod */ slice_per_intf = DIV_ROUND_UP(hdisplay, dsc->slice_width); - /* - * If slice_count is greater than slice_per_intf - * then default to 1. This can happen during partial - * update. - */ - if (dsc->slice_count > slice_per_intf) - dsc->slice_count = 1; - total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; eol_byte_num = total_bytes_per_intf % 3; - pkt_per_line = slice_per_intf / dsc->slice_count; + + /* + * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. + * + * Since the current driver only supports slice_per_pkt = 1, + * pkt_per_line will be equal to slice per intf for now. + */ + pkt_per_line = slice_per_intf; if (is_cmd_mode) /* packet data type */ reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); @@ -988,7 +987,14 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) if (!msm_host->dsc) wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1; else - wc = msm_host->dsc->slice_chunk_size * msm_host->dsc->slice_count + 1; + /* + * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. + * Currently, the driver only supports default value of slice_per_pkt = 1 + * + * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info + * and adjust DSC math to account for slice_per_pkt. + */ + wc = msm_host->dsc->slice_chunk_size + 1; dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | -- cgit 1.4.1 From 4e9f1a2367aea7d61f6781213e25313cd983b0d7 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 12 Jun 2023 15:02:59 -0700 Subject: drm/msm/dp: Free resources after unregistering them [ Upstream commit fa0048a4b1fa7a50c8b0e514f5b428abdf69a6f8 ] The DP component's unbind operation walks through the submodules to unregister and clean things up. But if the unbind happens because the DP controller itself is being removed, all the memory for those submodules has just been freed. Change the order of these operations to avoid the many use-after-free that otherwise happens in this code path. Fixes: c943b4948b58 ("drm/msm/dp: add displayPort driver support") Signed-off-by: Bjorn Andersson Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/542166/ Link: https://lore.kernel.org/r/20230612220259.1884381-1-quic_bjorande@quicinc.com Signed-off-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 57b82e5d0ab1..d16c12351adb 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -1342,9 +1342,9 @@ static int dp_display_remove(struct platform_device *pdev) { struct dp_display_private *dp = dev_get_dp_display_private(&pdev->dev); + component_del(&pdev->dev, &dp_display_comp_ops); dp_display_deinit_sub_modules(dp); - component_del(&pdev->dev, &dp_display_comp_ops); platform_set_drvdata(pdev, NULL); return 0; -- cgit 1.4.1 From 846c79d2a5f65d1c1c34fa0087d5300b16e4a626 Mon Sep 17 00:00:00 2001 From: Allen-KH Cheng Date: Fri, 17 Mar 2023 14:19:44 +0800 Subject: arm64: dts: mediatek: Add cpufreq nodes for MT8192 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9d498cce9298a71e3896e2d1aee24a1a4c531d81 ] Add the cpufreq nodes for MT8192 SoC. Signed-off-by: Allen-KH Cheng Tested-by: Chen-Yu Tsai Reviewed-by: Nícolas F. R. A. Prado Tested-by: Nícolas F. R. A. Prado Reviewed-by: AngeloGioacchino Del Regno Tested-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230317061944.15434-1-allen-kh.cheng@mediatek.com Signed-off-by: Matthias Brugger Stable-dep-of: a4366b5695c9 ("arm64: dts: mediatek: mt8192: Fix CPUs capacity-dmips-mhz") Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/mediatek/mt8192.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi index ef1294d96014..ff2310fe3f1d 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi @@ -64,6 +64,7 @@ clock-frequency = <1701000000>; cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; + performance-domains = <&performance 0>; capacity-dmips-mhz = <530>; }; @@ -75,6 +76,7 @@ clock-frequency = <1701000000>; cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; + performance-domains = <&performance 0>; capacity-dmips-mhz = <530>; }; @@ -86,6 +88,7 @@ clock-frequency = <1701000000>; cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; + performance-domains = <&performance 0>; capacity-dmips-mhz = <530>; }; @@ -97,6 +100,7 @@ clock-frequency = <1701000000>; cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; + performance-domains = <&performance 0>; capacity-dmips-mhz = <530>; }; @@ -108,6 +112,7 @@ clock-frequency = <2171000000>; cpu-idle-states = <&cpu_sleep_b &cluster_sleep_b>; next-level-cache = <&l2_1>; + performance-domains = <&performance 1>; capacity-dmips-mhz = <1024>; }; @@ -119,6 +124,7 @@ clock-frequency = <2171000000>; cpu-idle-states = <&cpu_sleep_b &cluster_sleep_b>; next-level-cache = <&l2_1>; + performance-domains = <&performance 1>; capacity-dmips-mhz = <1024>; }; @@ -130,6 +136,7 @@ clock-frequency = <2171000000>; cpu-idle-states = <&cpu_sleep_b &cluster_sleep_b>; next-level-cache = <&l2_1>; + performance-domains = <&performance 1>; capacity-dmips-mhz = <1024>; }; @@ -141,6 +148,7 @@ clock-frequency = <2171000000>; cpu-idle-states = <&cpu_sleep_b &cluster_sleep_b>; next-level-cache = <&l2_1>; + performance-domains = <&performance 1>; capacity-dmips-mhz = <1024>; }; @@ -257,6 +265,12 @@ compatible = "simple-bus"; ranges; + performance: performance-controller@11bc10 { + compatible = "mediatek,cpufreq-hw"; + reg = <0 0x0011bc10 0 0x120>, <0 0x0011bd30 0 0x120>; + #performance-domain-cells = <1>; + }; + gic: interrupt-controller@c000000 { compatible = "arm,gic-v3"; #interrupt-cells = <4>; -- cgit 1.4.1 From 643a85190a9869c29ca3c406d621114bdeaa0845 Mon Sep 17 00:00:00 2001 From: "Nícolas F. R. A. Prado" Date: Fri, 2 Jun 2023 14:35:15 -0400 Subject: arm64: dts: mediatek: mt8192: Fix CPUs capacity-dmips-mhz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a4366b5695c984b8a3fc8b31de9e758c8f6d1aed ] The capacity-dmips-mhz parameter was miscalculated: this SoC runs the first (Cortex-A55) cluster at a maximum of 2000MHz and the second (Cortex-A76) cluster at a maximum of 2200MHz. In order to calculate the right capacity-dmips-mhz, the following test was performed: 1. CPUFREQ governor was set to 'performance' on both clusters 2. Ran dhrystone with 500000000 iterations for 10 times on each cluster 3. Calculated the mean result for each cluster 4. Calculated DMIPS/MHz: dmips_mhz = dmips_per_second / cpu_mhz 5. Scaled results to 1024: result_c0 = dmips_mhz_c0 / dmips_mhz_c1 * 1024 The mean results for this SoC are: Cluster 0 (LITTLE): 12016411 Dhry/s Cluster 1 (BIG): 31702034 Dhry/s The calculated scaled results are: Cluster 0: 426.953226899238 (rounded to 427) Cluster 1: 1024 Fixes: 48489980e27e ("arm64: dts: Add Mediatek SoC MT8192 and evaluation board dts and Makefile") Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230602183515.3778780-1-nfraprado@collabora.com Signed-off-by: Matthias Brugger Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/mediatek/mt8192.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi index ff2310fe3f1d..2f40c6cc407c 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi @@ -65,7 +65,7 @@ cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; performance-domains = <&performance 0>; - capacity-dmips-mhz = <530>; + capacity-dmips-mhz = <427>; }; cpu1: cpu@100 { @@ -77,7 +77,7 @@ cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; performance-domains = <&performance 0>; - capacity-dmips-mhz = <530>; + capacity-dmips-mhz = <427>; }; cpu2: cpu@200 { @@ -89,7 +89,7 @@ cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; performance-domains = <&performance 0>; - capacity-dmips-mhz = <530>; + capacity-dmips-mhz = <427>; }; cpu3: cpu@300 { @@ -101,7 +101,7 @@ cpu-idle-states = <&cpu_sleep_l &cluster_sleep_l>; next-level-cache = <&l2_0>; performance-domains = <&performance 0>; - capacity-dmips-mhz = <530>; + capacity-dmips-mhz = <427>; }; cpu4: cpu@400 { -- cgit 1.4.1 From 8d68ba92554b79a93f52bea0cf778eb7821c9901 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Fri, 9 Jun 2023 14:06:43 +0530 Subject: drm/amdgpu: Fix memcpy() in sienna_cichlid_append_powerplay_table function. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d50dc746ff72b9c48812dac3344fa87fbde940a3 ] Fixes the following gcc with W=1: In file included from ./include/linux/string.h:253, from ./include/linux/bitmap.h:11, from ./include/linux/cpumask.h:12, from ./arch/x86/include/asm/cpumask.h:5, from ./arch/x86/include/asm/msr.h:11, from ./arch/x86/include/asm/processor.h:22, from ./arch/x86/include/asm/cpufeature.h:5, from ./arch/x86/include/asm/thread_info.h:53, from ./include/linux/thread_info.h:60, from ./arch/x86/include/asm/preempt.h:7, from ./include/linux/preempt.h:78, from ./include/linux/spinlock.h:56, from ./include/linux/mmzone.h:8, from ./include/linux/gfp.h:7, from ./include/linux/firmware.h:7, from drivers/gpu/drm/amd/amdgpu/../pm/swsmu/smu11/sienna_cichlid_ppt.c:26: In function ‘fortify_memcpy_chk’, inlined from ‘sienna_cichlid_append_powerplay_table’ at drivers/gpu/drm/amd/amdgpu/../pm/swsmu/smu11/sienna_cichlid_ppt.c:444:2, inlined from ‘sienna_cichlid_setup_pptable’ at drivers/gpu/drm/amd/amdgpu/../pm/swsmu/smu11/sienna_cichlid_ppt.c:506:8, inlined from ‘sienna_cichlid_setup_pptable’ at drivers/gpu/drm/amd/amdgpu/../pm/swsmu/smu11/sienna_cichlid_ppt.c:494:12: ./include/linux/fortify-string.h:413:4: warning: call to ‘__read_overflow2_field’ declared with attribute warning: detected read beyond size of field (2nd parameter); maybe use struct_group()? [-Wattribute-warning] 413 | __read_overflow2_field(q_size_field, size); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ the compiler complains about the size calculation in the memcpy() - "sizeof(*smc_dpm_table) - sizeof(smc_dpm_table->table_header)" is much larger than what fits into table_member. Hence, reuse 'smu_memcpy_trailing' for nv1x Fixes: 7077b19a38240 ("drm/amd/pm: use macro to get pptable members") Suggested-by: Evan Quan Cc: Evan Quan Cc: Chengming Gui Cc: Christian König Cc: Alex Deucher Signed-off-by: Srinivasan Shanmugam Reviewed-by: Evan Quan Acked-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- .../gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index 85d53597eb07..f7ed3e655e39 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -431,7 +431,13 @@ static int sienna_cichlid_append_powerplay_table(struct smu_context *smu) { struct atom_smc_dpm_info_v4_9 *smc_dpm_table; int index, ret; - I2cControllerConfig_t *table_member; + PPTable_beige_goby_t *ppt_beige_goby; + PPTable_t *ppt; + + if (smu->adev->ip_versions[MP1_HWIP][0] == IP_VERSION(11, 0, 13)) + ppt_beige_goby = smu->smu_table.driver_pptable; + else + ppt = smu->smu_table.driver_pptable; index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, smc_dpm_info); @@ -440,9 +446,13 @@ static int sienna_cichlid_append_powerplay_table(struct smu_context *smu) (uint8_t **)&smc_dpm_table); if (ret) return ret; - GET_PPTABLE_MEMBER(I2cControllers, &table_member); - memcpy(table_member, smc_dpm_table->I2cControllers, - sizeof(*smc_dpm_table) - sizeof(smc_dpm_table->table_header)); + + if (smu->adev->ip_versions[MP1_HWIP][0] == IP_VERSION(11, 0, 13)) + smu_memcpy_trailing(ppt_beige_goby, I2cControllers, BoardReserved, + smc_dpm_table, I2cControllers); + else + smu_memcpy_trailing(ppt, I2cControllers, BoardReserved, + smc_dpm_table, I2cControllers); return 0; } -- cgit 1.4.1 From 0e2c51a16fcb9e69923906bdaecdbbe1ea4fb8e9 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Sat, 10 Jun 2023 06:19:15 -0400 Subject: drm/amdgpu: Fix usage of UMC fill record in RAS [ Upstream commit 71344a718a9fda8c551cdc4381d354f9a9907f6f ] The fixed commit listed in the Fixes tag below, introduced a bug in amdgpu_ras.c::amdgpu_reserve_page_direct(), in that when introducing the new amdgpu_umc_fill_error_record() and internally in that new function the physical address (argument "uint64_t retired_page"--wrong name) is right-shifted by AMDGPU_GPU_PAGE_SHIFT. Thus, in amdgpu_reserve_page_direct() when we pass "address" to that new function, we should NOT right-shift it, since this results, erroneously, in the page address to be 0 for first 2^(2*AMDGPU_GPU_PAGE_SHIFT) memory addresses. This commit fixes this bug. Cc: Tao Zhou Cc: Hawking Zhang Cc: Alex Deucher Fixes: 400013b268cb ("drm/amdgpu: add umc_fill_error_record to make code more simple") Signed-off-by: Luben Tuikov Link: https://lore.kernel.org/r/20230610113536.10621-1-luben.tuikov@amd.com Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index a4b47e1bd111..09fc464f5f12 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -170,8 +170,7 @@ static int amdgpu_reserve_page_direct(struct amdgpu_device *adev, uint64_t addre memset(&err_rec, 0x0, sizeof(struct eeprom_table_record)); err_data.err_addr = &err_rec; - amdgpu_umc_fill_error_record(&err_data, address, - (address >> AMDGPU_GPU_PAGE_SHIFT), 0, 0); + amdgpu_umc_fill_error_record(&err_data, address, address, 0, 0); if (amdgpu_bad_page_threshold != 0) { amdgpu_ras_add_bad_pages(adev, err_data.err_addr, -- cgit 1.4.1 From af8affd12375f01905977780d7f98ee83e22ded3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 13 Jun 2023 03:09:41 +0300 Subject: drm/msm/dpu: correct MERGE_3D length [ Upstream commit 9a6c13b847d61b0c3796820ca6e976789df59cd8 ] Each MERGE_3D block has just two registers. Correct the block length accordingly. Fixes: 4369c93cf36b ("drm/msm/dpu: initial support for merge3D hardware block") Signed-off-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/542177/ Reviewed-by: Marijn Suijten Link: https://lore.kernel.org/r/20230613001004.3426676-3-dmitry.baryshkov@linaro.org Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index 80e59d3f9dd3..b2f330e99b0c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -1203,7 +1203,7 @@ static struct dpu_pingpong_cfg qcm2290_pp[] = { #define MERGE_3D_BLK(_name, _id, _base) \ {\ .name = _name, .id = _id, \ - .base = _base, .len = 0x100, \ + .base = _base, .len = 0x8, \ .features = MERGE_3D_SM8150_MASK, \ .sblk = NULL \ } -- cgit 1.4.1 From 0b5c9e9695f8fc00befbad3c317de3dc783bd0b8 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 May 2023 12:39:06 +0300 Subject: clk: vc5: check memory returned by kasprintf() [ Upstream commit 144601f6228de5598f03e693822b60a95c367a17 ] kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: f491276a5168 ("clk: vc5: Allow Versaclock driver to support multiple instances") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230530093913.1656095-2-claudiu.beznea@microchip.com Reviewed-by: Luca Ceresoli Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-versaclock5.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index 5f2c9093c5b9..ef50ab78dd90 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -1027,6 +1027,11 @@ static int vc5_probe(struct i2c_client *client) } init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node); + if (!init.name) { + ret = -ENOMEM; + goto err_clk; + } + init.ops = &vc5_mux_ops; init.flags = 0; init.parent_names = parent_names; @@ -1041,6 +1046,10 @@ static int vc5_probe(struct i2c_client *client) memset(&init, 0, sizeof(init)); init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl", client->dev.of_node); + if (!init.name) { + ret = -ENOMEM; + goto err_clk; + } init.ops = &vc5_dbl_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; @@ -1056,6 +1065,10 @@ static int vc5_probe(struct i2c_client *client) /* Register PFD */ memset(&init, 0, sizeof(init)); init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node); + if (!init.name) { + ret = -ENOMEM; + goto err_clk; + } init.ops = &vc5_pfd_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; @@ -1073,6 +1086,10 @@ static int vc5_probe(struct i2c_client *client) /* Register PLL */ memset(&init, 0, sizeof(init)); init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node); + if (!init.name) { + ret = -ENOMEM; + goto err_clk; + } init.ops = &vc5_pll_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; @@ -1092,6 +1109,10 @@ static int vc5_probe(struct i2c_client *client) memset(&init, 0, sizeof(init)); init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d", client->dev.of_node, idx); + if (!init.name) { + ret = -ENOMEM; + goto err_clk; + } init.ops = &vc5_fod_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; @@ -1110,6 +1131,10 @@ static int vc5_probe(struct i2c_client *client) memset(&init, 0, sizeof(init)); init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb", client->dev.of_node); + if (!init.name) { + ret = -ENOMEM; + goto err_clk; + } init.ops = &vc5_clk_out_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; @@ -1136,6 +1161,10 @@ static int vc5_probe(struct i2c_client *client) memset(&init, 0, sizeof(init)); init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d", client->dev.of_node, idx + 1); + if (!init.name) { + ret = -ENOMEM; + goto err_clk; + } init.ops = &vc5_clk_out_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; -- cgit 1.4.1 From 5470a0e81f6f500385b339bc3916e015d644a319 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 May 2023 12:39:07 +0300 Subject: clk: cdce925: check return value of kasprintf() [ Upstream commit bb7d09ddbf361d51eae46f38e7c8a2b85914ea2a ] kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: 19fbbbbcd3a3 ("Add TI CDCE925 I2C controlled clock synthesizer driver") Depends-on: e665f029a283 ("clk: Convert to using %pOFn instead of device_node.name") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230530093913.1656095-3-claudiu.beznea@microchip.com Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-cdce925.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index ef9a2d44e40c..d88e1d681a2c 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -714,6 +714,10 @@ static int cdce925_probe(struct i2c_client *client) for (i = 0; i < data->chip_info->num_plls; ++i) { pll_clk_name[i] = kasprintf(GFP_KERNEL, "%pOFn.pll%d", client->dev.of_node, i); + if (!pll_clk_name[i]) { + err = -ENOMEM; + goto error; + } init.name = pll_clk_name[i]; data->pll[i].chip = data; data->pll[i].hw.init = &init; @@ -755,6 +759,10 @@ static int cdce925_probe(struct i2c_client *client) init.num_parents = 1; init.parent_names = &parent_name; /* Mux Y1 to input */ init.name = kasprintf(GFP_KERNEL, "%pOFn.Y1", client->dev.of_node); + if (!init.name) { + err = -ENOMEM; + goto error; + } data->clk[0].chip = data; data->clk[0].hw.init = &init; data->clk[0].index = 0; @@ -773,6 +781,10 @@ static int cdce925_probe(struct i2c_client *client) for (i = 1; i < data->chip_info->num_outputs; ++i) { init.name = kasprintf(GFP_KERNEL, "%pOFn.Y%d", client->dev.of_node, i+1); + if (!init.name) { + err = -ENOMEM; + goto error; + } data->clk[i].chip = data; data->clk[i].hw.init = &init; data->clk[i].index = i; -- cgit 1.4.1 From 0a89a906ba9f9a54e4aca034648130a397ddedce Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 May 2023 12:39:08 +0300 Subject: clk: si5341: return error if one synth clock registration fails [ Upstream commit 2560114c06d7a752b3f4639f28cece58fed11267 ] In case devm_clk_hw_register() fails for one of synth clocks the probe continues. Later on, when registering output clocks which have as parents all the synth clocks, in case there is registration failure for at least one synth clock the information passed to clk core for registering output clock is not right: init.num_parents is fixed but init.parents may contain an array with less parents. Fixes: 3044a860fd09 ("clk: Add Si5341/Si5340 driver") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230530093913.1656095-4-claudiu.beznea@microchip.com Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-si5341.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index 0e528d7ba656..6dca3288c894 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -1553,7 +1553,7 @@ static int si5341_probe(struct i2c_client *client) struct clk_init_data init; struct clk *input; const char *root_clock_name; - const char *synth_clock_names[SI5341_NUM_SYNTH]; + const char *synth_clock_names[SI5341_NUM_SYNTH] = { NULL }; int err; unsigned int i; struct clk_si5341_output_config config[SI5341_MAX_NUM_OUTPUTS]; @@ -1705,6 +1705,7 @@ static int si5341_probe(struct i2c_client *client) if (err) { dev_err(&client->dev, "synth N%u registration failed\n", i); + goto free_clk_names; } } @@ -1782,16 +1783,17 @@ static int si5341_probe(struct i2c_client *client) goto cleanup; } +free_clk_names: /* Free the names, clk framework makes copies */ for (i = 0; i < data->num_synth; ++i) devm_kfree(&client->dev, (void *)synth_clock_names[i]); - return 0; - cleanup: - for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { - if (data->clk[i].vddo_reg) - regulator_disable(data->clk[i].vddo_reg); + if (err) { + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { + if (data->clk[i].vddo_reg) + regulator_disable(data->clk[i].vddo_reg); + } } return err; } -- cgit 1.4.1 From 11581850a714342b709c81aebdec1658d2bbb977 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 May 2023 12:39:09 +0300 Subject: clk: si5341: check return value of {devm_}kasprintf() [ Upstream commit 36e4ef82016a2b785cf2317eade77e76699b7bff ] {devm_}kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: 3044a860fd09 ("clk: Add Si5341/Si5340 driver") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230530093913.1656095-5-claudiu.beznea@microchip.com Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-si5341.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index 6dca3288c894..b2cf7edc8b30 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -1697,6 +1697,10 @@ static int si5341_probe(struct i2c_client *client) for (i = 0; i < data->num_synth; ++i) { synth_clock_names[i] = devm_kasprintf(&client->dev, GFP_KERNEL, "%s.N%u", client->dev.of_node->name, i); + if (!synth_clock_names[i]) { + err = -ENOMEM; + goto free_clk_names; + } init.name = synth_clock_names[i]; data->synth[i].index = i; data->synth[i].data = data; @@ -1715,6 +1719,10 @@ static int si5341_probe(struct i2c_client *client) for (i = 0; i < data->num_outputs; ++i) { init.name = kasprintf(GFP_KERNEL, "%s.%d", client->dev.of_node->name, i); + if (!init.name) { + err = -ENOMEM; + goto free_clk_names; + } init.flags = config[i].synth_master ? CLK_SET_RATE_PARENT : 0; data->clk[i].index = i; data->clk[i].data = data; -- cgit 1.4.1 From a20450f4735b0eb3462b62cf8422a6bfd49537e9 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 May 2023 12:39:10 +0300 Subject: clk: si5341: free unused memory on probe failure [ Upstream commit 267ad94b13c53d8c99a336f0841b1fa1595b1d0f ] Pointers from synth_clock_names[] should be freed at the end of probe either on probe success or failure path. Fixes: b7bbf6ec4940 ("clk: si5341: Allow different output VDD_SEL values") Fixes: 9b13ff4340df ("clk: si5341: Add sysfs properties to allow checking/resetting device faults") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230530093913.1656095-6-claudiu.beznea@microchip.com Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk-si5341.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index b2cf7edc8b30..c7d8cbd22bac 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -1744,7 +1744,7 @@ static int si5341_probe(struct i2c_client *client) if (err) { dev_err(&client->dev, "output %u registration failed\n", i); - goto cleanup; + goto free_clk_names; } if (config[i].always_on) clk_prepare(data->clk[i].hw.clk); @@ -1754,7 +1754,7 @@ static int si5341_probe(struct i2c_client *client) data); if (err) { dev_err(&client->dev, "unable to add clk provider\n"); - goto cleanup; + goto free_clk_names; } if (initialization_required) { @@ -1762,11 +1762,11 @@ static int si5341_probe(struct i2c_client *client) regcache_cache_only(data->regmap, false); err = regcache_sync(data->regmap); if (err < 0) - goto cleanup; + goto free_clk_names; err = si5341_finalize_defaults(data); if (err < 0) - goto cleanup; + goto free_clk_names; } /* wait for device to report input clock present and PLL lock */ @@ -1775,21 +1775,19 @@ static int si5341_probe(struct i2c_client *client) 10000, 250000); if (err) { dev_err(&client->dev, "Error waiting for input clock or PLL lock\n"); - goto cleanup; + goto free_clk_names; } /* clear sticky alarm bits from initialization */ err = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0); if (err) { dev_err(&client->dev, "unable to clear sticky status\n"); - goto cleanup; + goto free_clk_names; } err = sysfs_create_files(&client->dev.kobj, si5341_attributes); - if (err) { + if (err) dev_err(&client->dev, "unable to create sysfs files\n"); - goto cleanup; - } free_clk_names: /* Free the names, clk framework makes copies */ -- cgit 1.4.1 From 67684f0688deda40bff928bf45891185b783dc33 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 May 2023 12:39:11 +0300 Subject: clk: keystone: sci-clk: check return value of kasprintf() [ Upstream commit b73ed981da6d25c921aaefa7ca3df85bbd85b7fc ] kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: b745c0794e2f ("clk: keystone: Add sci-clk driver support") Depends-on: 96488c09b0f4 ("clk: keystone: sci-clk: cut down the clock name length") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230530093913.1656095-7-claudiu.beznea@microchip.com Reviewed-by: Tony Lindgren Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/keystone/sci-clk.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index d4b4e74e22da..254f2cf24be2 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -294,6 +294,8 @@ static int _sci_clk_build(struct sci_clk_provider *provider, name = kasprintf(GFP_KERNEL, "clk:%d:%d", sci_clk->dev_id, sci_clk->clk_id); + if (!name) + return -ENOMEM; init.name = name; -- cgit 1.4.1 From 511b47f8cb8b94144e9fb0886ec126d6f8ccefbd Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 30 May 2023 12:39:12 +0300 Subject: clk: ti: clkctrl: check return value of kasprintf() [ Upstream commit bd46cd0b802d9c9576ca78007aa084ae3e74907b ] kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: 852049594b9a ("clk: ti: clkctrl: convert subclocks to use proper names also") Fixes: 6c3090520554 ("clk: ti: clkctrl: Fix hidden dependency to node name") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230530093913.1656095-8-claudiu.beznea@microchip.com Reviewed-by: Tony Lindgren Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/ti/clkctrl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c index ae5862879417..57611bfb299c 100644 --- a/drivers/clk/ti/clkctrl.c +++ b/drivers/clk/ti/clkctrl.c @@ -258,6 +258,9 @@ static const char * __init clkctrl_get_clock_name(struct device_node *np, if (clkctrl_name && !legacy_naming) { clock_name = kasprintf(GFP_KERNEL, "%s-clkctrl:%04x:%d", clkctrl_name, offset, index); + if (!clock_name) + return NULL; + strreplace(clock_name, '_', '-'); return clock_name; @@ -586,6 +589,10 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node) if (clkctrl_name) { provider->clkdm_name = kasprintf(GFP_KERNEL, "%s_clkdm", clkctrl_name); + if (!provider->clkdm_name) { + kfree(provider); + return; + } goto clkdm_found; } -- cgit 1.4.1 From 83356d6f0ad2b69acaaa52cf4a3995f30b129d16 Mon Sep 17 00:00:00 2001 From: Alexey Romanov Date: Sat, 10 Jun 2023 12:04:14 +0300 Subject: drivers: meson: secure-pwrc: always enable DMA domain [ Upstream commit 0bb4644d583789c97e74d3e3047189f0c59c4742 ] Starting from commit e45f243409db ("firmware: meson_sm: populate platform devices from sm device tree data") pwrc is probed successfully and disables unused pwr domains. By A1 SoC family design, any TEE requires DMA pwr domain always enabled. Fixes: b3dde5013e13 ("soc: amlogic: Add support for Secure power domains controller") Signed-off-by: Alexey Romanov Acked-by: Neil Armstrong Link: https://lore.kernel.org/r/20230610090414.90529-1-avromanov@sberdevices.ru [narmstrong: added fixes tag] Signed-off-by: Neil Armstrong Signed-off-by: Sasha Levin --- drivers/soc/amlogic/meson-secure-pwrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-secure-pwrc.c b/drivers/soc/amlogic/meson-secure-pwrc.c index e93518763526..25b4b71df9b8 100644 --- a/drivers/soc/amlogic/meson-secure-pwrc.c +++ b/drivers/soc/amlogic/meson-secure-pwrc.c @@ -105,7 +105,7 @@ static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { SEC_PD(ACODEC, 0), SEC_PD(AUDIO, 0), SEC_PD(OTP, 0), - SEC_PD(DMA, 0), + SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE), SEC_PD(SD_EMMC, 0), SEC_PD(RAMA, 0), /* SRAMB is used as ATF runtime memory, and should be always on */ -- cgit 1.4.1 From e4f2a1feebb3f209a0fca82aa53507a5b8be4d53 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 3 Apr 2023 11:29:59 +0300 Subject: ovl: update of dentry revalidate flags after copy up [ Upstream commit b07d5cc93e1b28df47a72c519d09d0a836043613 ] After copy up, we may need to update d_flags if upper dentry is on a remote fs and lower dentries are not. Add helpers to allow incremental update of the revalidate flags. Fixes: bccece1ead36 ("ovl: allow remote upper") Reviewed-by: Gao Xiang Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi Signed-off-by: Sasha Levin --- fs/overlayfs/copy_up.c | 2 ++ fs/overlayfs/dir.c | 3 +-- fs/overlayfs/export.c | 3 +-- fs/overlayfs/namei.c | 3 +-- fs/overlayfs/overlayfs.h | 6 ++++-- fs/overlayfs/super.c | 2 +- fs/overlayfs/util.c | 24 ++++++++++++++++++++---- 7 files changed, 30 insertions(+), 13 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 91a95bfad0d1..edc1ebff33f5 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -538,6 +538,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) /* Restore timestamps on parent (best effort) */ ovl_set_timestamps(ofs, upperdir, &c->pstat); ovl_dentry_set_upper_alias(c->dentry); + ovl_dentry_update_reval(c->dentry, upper); } } inode_unlock(udir); @@ -857,6 +858,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) inode_unlock(udir); ovl_dentry_set_upper_alias(c->dentry); + ovl_dentry_update_reval(c->dentry, ovl_dentry_upper(c->dentry)); } out: diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index c3032cef391e..5339ff08bd0f 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -269,8 +269,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode, ovl_dir_modified(dentry->d_parent, false); ovl_dentry_set_upper_alias(dentry); - ovl_dentry_update_reval(dentry, newdentry, - DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_reval(dentry, newdentry); if (!hardlink) { /* diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index ac9c3ad04016..e55363d343dc 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -326,8 +326,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, if (upper_alias) ovl_dentry_set_upper_alias(dentry); - ovl_dentry_update_reval(dentry, upper, - DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_reval(dentry, upper); return d_instantiate_anon(dentry, inode); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 0fd1d5fdfc72..655d08d6f3f1 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1116,8 +1116,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ovl_set_flag(OVL_UPPERDATA, inode); } - ovl_dentry_update_reval(dentry, upperdentry, - DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_reval(dentry, upperdentry); revert_creds(old_cred); if (origin_path) { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index e74a610a117e..052226aa7de0 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -360,8 +360,10 @@ bool ovl_index_all(struct super_block *sb); bool ovl_verify_lower(struct super_block *sb); struct ovl_entry *ovl_alloc_entry(unsigned int numlower); bool ovl_dentry_remote(struct dentry *dentry); -void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *upperdentry, - unsigned int mask); +void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry); +void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry); +void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, + unsigned int mask); bool ovl_dentry_weird(struct dentry *dentry); enum ovl_path_type ovl_path_type(struct dentry *dentry); void ovl_path_upper(struct dentry *dentry, struct path *path); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 3d14a3f1465d..51eec4a8e82b 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1980,7 +1980,7 @@ static struct dentry *ovl_get_root(struct super_block *sb, ovl_dentry_set_flag(OVL_E_CONNECTED, root); ovl_set_upperdata(d_inode(root)); ovl_inode_init(d_inode(root), &oip, ino, fsid); - ovl_dentry_update_reval(root, upperdentry, DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_flags(root, upperdentry, DCACHE_OP_WEAK_REVALIDATE); return root; } diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 81a57a8d80d9..850e8d1bf829 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -94,14 +94,30 @@ struct ovl_entry *ovl_alloc_entry(unsigned int numlower) return oe; } +#define OVL_D_REVALIDATE (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE) + bool ovl_dentry_remote(struct dentry *dentry) { - return dentry->d_flags & - (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + return dentry->d_flags & OVL_D_REVALIDATE; +} + +void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry) +{ + if (!ovl_dentry_remote(realdentry)) + return; + + spin_lock(&dentry->d_lock); + dentry->d_flags |= realdentry->d_flags & OVL_D_REVALIDATE; + spin_unlock(&dentry->d_lock); +} + +void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry) +{ + return ovl_dentry_init_flags(dentry, upperdentry, OVL_D_REVALIDATE); } -void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *upperdentry, - unsigned int mask) +void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, + unsigned int mask) { struct ovl_entry *oe = OVL_E(dentry); unsigned int i, flags = 0; -- cgit 1.4.1 From 03a705c1d7cb9c7dd55a0b96e1fc9ef7c236d35a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 14 Jun 2023 15:15:09 +0300 Subject: ASoC: imx-audmix: check return value of devm_kasprintf() [ Upstream commit 2f76e1d6ca524a888d29aafe29f2ad2003857971 ] devm_kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: b86ef5367761 ("ASoC: fsl: Add Audio Mixer machine driver") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230614121509.443926-1-claudiu.beznea@microchip.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/fsl/imx-audmix.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 1292a845c424..d8e99b263ab2 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -228,6 +228,8 @@ static int imx_audmix_probe(struct platform_device *pdev) dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s", fe_name_pref, args.np->full_name + 1); + if (!dai_name) + return -ENOMEM; dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name); @@ -236,6 +238,8 @@ static int imx_audmix_probe(struct platform_device *pdev) capture_dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", dai_name, "CPU-Capture"); + if (!capture_dai_name) + return -ENOMEM; } priv->dai[i].cpus = &dlc[0]; @@ -266,6 +270,8 @@ static int imx_audmix_probe(struct platform_device *pdev) "AUDMIX-Playback-%d", i); be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL, "AUDMIX-Capture-%d", i); + if (!be_name || !be_pb || !be_cp) + return -ENOMEM; priv->dai[num_dai + i].cpus = &dlc[3]; priv->dai[num_dai + i].codecs = &dlc[4]; @@ -293,6 +299,9 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dapm_routes[i].source = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", dai_name, "CPU-Playback"); + if (!priv->dapm_routes[i].source) + return -ENOMEM; + priv->dapm_routes[i].sink = be_pb; priv->dapm_routes[num_dai + i].source = be_pb; priv->dapm_routes[num_dai + i].sink = be_cp; -- cgit 1.4.1 From 49451db71b746df990888068961f1033f7c9b734 Mon Sep 17 00:00:00 2001 From: Fei Shao Date: Mon, 19 Jun 2023 11:22:53 +0800 Subject: clk: Fix memory leak in devm_clk_notifier_register() [ Upstream commit 7fb933e56f77a57ef7cfc59fc34cbbf1b1fa31ff ] devm_clk_notifier_register() allocates a devres resource for clk notifier but didn't register that to the device, so the notifier didn't get unregistered on device detach and the allocated resource was leaked. Fix the issue by registering the resource through devres_add(). This issue was found with kmemleak on a Chromebook. Fixes: 6d30d50d037d ("clk: add devm variant of clk_notifier_register") Signed-off-by: Fei Shao Link: https://lore.kernel.org/r/20230619112253.v2.1.I13f060c10549ef181603e921291bdea95f83033c@changeid Reviewed-by: Dan Carpenter Signed-off-by: Stephen Boyd Signed-off-by: Sasha Levin --- drivers/clk/clk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index d4a74759fe29..e0de6565800d 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -4651,6 +4651,7 @@ int devm_clk_notifier_register(struct device *dev, struct clk *clk, if (!ret) { devres->clk = clk; devres->nb = nb; + devres_add(dev, devres); } else { devres_free(devres); } -- cgit 1.4.1 From 712a7f3a06c9372eaf78ceeb3246016f56e1e8f9 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 16 Jun 2023 15:18:39 +0200 Subject: ARM: dts: lan966x: kontron-d10: fix board reset [ Upstream commit bfcd5714f6424c03e385e0e9296dcd69855cfea7 ] The pinctrl node was missing which change the pin mux to GPIO mode. Add it. Fixes: 79d83b3a458e ("ARM: dts: lan966x: add basic Kontron KSwitch D10 support") Signed-off-by: Michael Walle [claudiu.beznea: moved pinctrl-* bindings after compatible] Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230616-feature-d10-dt-cleanups-v1-1-50dd0452b8fe@kernel.org Signed-off-by: Sasha Levin --- arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi b/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi index 0097e72e3fb2..42be207509a4 100644 --- a/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi +++ b/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi @@ -18,6 +18,8 @@ gpio-restart { compatible = "gpio-restart"; + pinctrl-0 = <&reset_pins>; + pinctrl-names = "default"; gpios = <&gpio 56 GPIO_ACTIVE_LOW>; priority = <200>; }; @@ -59,6 +61,12 @@ function = "miim_c"; }; + reset_pins: reset-pins { + /* SYS_RST# */ + pins = "GPIO_56"; + function = "gpio"; + }; + sgpio_a_pins: sgpio-a-pins { /* SCK, D0, D1 */ pins = "GPIO_32", "GPIO_33", "GPIO_34"; -- cgit 1.4.1 From c6764757e84398263ebf62f3787aa218a4902d60 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 16 Jun 2023 15:18:40 +0200 Subject: ARM: dts: lan966x: kontron-d10: fix SPI CS [ Upstream commit fcb79ee3f0b15ed15f35eca5f24e952fdced9c61 ] The pinctrl node was missing which change the pin mux to GPIO mode. Add it so we don't have to rely on the bootloader to set the correct mode. Fixes: 79d83b3a458e ("ARM: dts: lan966x: add basic Kontron KSwitch D10 support") Signed-off-by: Michael Walle Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230616-feature-d10-dt-cleanups-v1-2-50dd0452b8fe@kernel.org Signed-off-by: Sasha Levin --- arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi b/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi index 42be207509a4..f4df4cc1dfa5 100644 --- a/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi +++ b/arch/arm/boot/dts/lan966x-kontron-kswitch-d10-mmt.dtsi @@ -41,7 +41,7 @@ status = "okay"; spi3: spi@400 { - pinctrl-0 = <&fc3_b_pins>; + pinctrl-0 = <&fc3_b_pins>, <&spi3_cs_pins>; pinctrl-names = "default"; status = "okay"; cs-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; @@ -79,6 +79,12 @@ function = "sgpio_b"; }; + spi3_cs_pins: spi3-cs-pins { + /* CS# */ + pins = "GPIO_46"; + function = "gpio"; + }; + usart0_pins: usart0-pins { /* RXD, TXD */ pins = "GPIO_25", "GPIO_26"; -- cgit 1.4.1 From 79e1d940fde5ebf87a2c901c23318cd6007bda29 Mon Sep 17 00:00:00 2001 From: Syed Saba Kareem Date: Thu, 22 Jun 2023 20:53:38 +0530 Subject: ASoC: amd: acp: clear pdm dma interrupt mask [ Upstream commit ad60672394bd1f95c58d3d9336902f47e05126fc ] Clear pdm dma interrupt mask in acp_dmic_shutdown(). 'Fixes: c32bd332ce5c9 ("ASoC: amd: acp: Add generic support for PDM controller on ACP")' Signed-off-by: Syed Saba Kareem Link: https://lore.kernel.org/r/Message-Id: <20230622152406.3709231-1-Syed.SabaKareem@amd.com> Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/acp/acp-pdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c index 66ec6b6a5972..f8030b79ac17 100644 --- a/sound/soc/amd/acp/acp-pdm.c +++ b/sound/soc/amd/acp/acp-pdm.c @@ -176,7 +176,7 @@ static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream, /* Disable DMIC interrupts */ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); - ext_int_ctrl |= ~PDM_DMA_INTR_MASK; + ext_int_ctrl &= ~PDM_DMA_INTR_MASK; writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); } -- cgit 1.4.1 From ebafa12c8f233af5ec55949f4272952369f23297 Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Wed, 15 Mar 2023 12:38:00 +0530 Subject: PCI: cadence: Fix Gen2 Link Retraining process [ Upstream commit 0e12f830236928b6fadf40d917a7527f0a048d2f ] The Link Retraining process is initiated to account for the Gen2 defect in the Cadence PCIe controller in J721E SoC. The errata corresponding to this is i2085, documented at: https://www.ti.com/lit/er/sprz455c/sprz455c.pdf The existing workaround implemented for the errata waits for the Data Link initialization to complete and assumes that the link retraining process at the Physical Layer has completed. However, it is possible that the Physical Layer training might be ongoing as indicated by the PCI_EXP_LNKSTA_LT bit in the PCI_EXP_LNKSTA register. Fix the existing workaround, to ensure that the Physical Layer training has also completed, in addition to the Data Link initialization. Link: https://lore.kernel.org/r/20230315070800.1615527-1-s-vadapalli@ti.com Fixes: 4740b969aaf5 ("PCI: cadence: Retrain Link to work around Gen2 training defect") Signed-off-by: Siddharth Vadapalli Signed-off-by: Lorenzo Pieralisi Reviewed-by: Vignesh Raghavendra Signed-off-by: Sasha Levin --- drivers/pci/controller/cadence/pcie-cadence-host.c | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 940c7dd701d6..5b14f7ee3c79 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -12,6 +12,8 @@ #include "pcie-cadence.h" +#define LINK_RETRAIN_TIMEOUT HZ + static u64 bar_max_size[] = { [RP_BAR0] = _ULL(128 * SZ_2G), [RP_BAR1] = SZ_2G, @@ -77,6 +79,27 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; +static int cdns_pcie_host_training_complete(struct cdns_pcie *pcie) +{ + u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; + unsigned long end_jiffies; + u16 lnk_stat; + + /* Wait for link training to complete. Exit after timeout. */ + end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; + do { + lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); + if (!(lnk_stat & PCI_EXP_LNKSTA_LT)) + break; + usleep_range(0, 1000); + } while (time_before(jiffies, end_jiffies)); + + if (!(lnk_stat & PCI_EXP_LNKSTA_LT)) + return 0; + + return -ETIMEDOUT; +} + static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) { struct device *dev = pcie->dev; @@ -118,6 +141,10 @@ static int cdns_pcie_retrain(struct cdns_pcie *pcie) cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, lnk_ctl); + ret = cdns_pcie_host_training_complete(pcie); + if (ret) + return ret; + ret = cdns_pcie_host_wait_for_link(pcie); } return ret; -- cgit 1.4.1 From c52502b67424a076cbb92e589de5201c61c3086d Mon Sep 17 00:00:00 2001 From: Nirmal Patel Date: Fri, 24 Feb 2023 13:28:11 -0700 Subject: PCI: vmd: Reset VMD config register between soft reboots [ Upstream commit b61cf04c49c3dfa70a0d6725d3eb40bf9b35cf71 ] VMD driver can disable or enable MSI remapping by changing VMCONFIG_MSI_REMAP register. This register needs to be set to the default value during soft reboots. Drives failed to enumerate when Windows boots after performing a soft reboot from Linux. Windows doesn't support MSI remapping disable feature and stale register value hinders Windows VMD driver initialization process. Adding vmd_shutdown function to make sure to set the VMCONFIG register to the default value. Link: https://lore.kernel.org/r/20230224202811.644370-1-nirmal.patel@linux.intel.com Fixes: ee81ee84f873 ("PCI: vmd: Disable MSI-X remapping when possible") Signed-off-by: Nirmal Patel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Jon Derrick Signed-off-by: Sasha Levin --- drivers/pci/controller/vmd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 769eedeb8802..50a187a29a1d 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -979,6 +979,13 @@ static void vmd_remove(struct pci_dev *dev) ida_simple_remove(&vmd_instance_ida, vmd->instance); } +static void vmd_shutdown(struct pci_dev *dev) +{ + struct vmd_dev *vmd = pci_get_drvdata(dev); + + vmd_remove_irq_domain(vmd); +} + #ifdef CONFIG_PM_SLEEP static int vmd_suspend(struct device *dev) { @@ -1056,6 +1063,7 @@ static struct pci_driver vmd_drv = { .id_table = vmd_ids, .probe = vmd_probe, .remove = vmd_remove, + .shutdown = vmd_shutdown, .driver = { .pm = &vmd_dev_pm_ops, }, -- cgit 1.4.1 From c316bde418af4c2a9df51149ed01d1bd8ca5bebf Mon Sep 17 00:00:00 2001 From: Jinhong Zhu Date: Tue, 2 May 2023 22:00:21 +0800 Subject: scsi: qedf: Fix NULL dereference in error handling [ Upstream commit f025312b089474a54e4859f3453771314d9e3d4f ] Smatch reported: drivers/scsi/qedf/qedf_main.c:3056 qedf_alloc_global_queues() warn: missing unwind goto? At this point in the function, nothing has been allocated so we can return directly. In particular the "qedf->global_queues" have not been allocated so calling qedf_free_global_queues() will lead to a NULL dereference when we check if (!gl[i]) and "gl" is NULL. Fixes: 61d8658b4a43 ("scsi: qedf: Add QLogic FastLinQ offload FCoE driver framework.") Signed-off-by: Jinhong Zhu Link: https://lore.kernel.org/r/20230502140022.2852-1-jinhongzhu@hust.edu.cn Reviewed-by: Dan Carpenter Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/qedf/qedf_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index e045c6e25090..ecff2ec83a00 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -3046,9 +3046,8 @@ static int qedf_alloc_global_queues(struct qedf_ctx *qedf) * addresses of our queues */ if (!qedf->p_cpuq) { - status = -EINVAL; QEDF_ERR(&qedf->dbg_ctx, "p_cpuq is NULL.\n"); - goto mem_alloc_failure; + return -EINVAL; } qedf->global_queues = kzalloc((sizeof(struct global_queue *) -- cgit 1.4.1 From 727fb7083e658d58441558cab8dacc749be51aad Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 16 Apr 2023 23:43:41 +0200 Subject: pinctrl: bcm2835: Handle gpiochip_add_pin_range() errors [ Upstream commit cdf7e616120065007687fe1df0412154f259daec ] gpiochip_add_pin_range() can fail, so better return its error code than a hard coded '0'. Fixes: d2b67744fd99 ("pinctrl: bcm2835: implement hook for missing gpio-ranges") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/98c3b5890bb72415145c9fe4e1d974711edae376.1681681402.git.christophe.jaillet@wanadoo.fr Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/bcm/pinctrl-bcm2835.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index 0f1ab0829ffe..fe8da3ccb0b5 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -376,10 +376,8 @@ static int bcm2835_of_gpio_ranges_fallback(struct gpio_chip *gc, if (!pctldev) return 0; - gpiochip_add_pin_range(gc, pinctrl_dev_get_devname(pctldev), 0, 0, - gc->ngpio); - - return 0; + return gpiochip_add_pin_range(gc, pinctrl_dev_get_devname(pctldev), 0, 0, + gc->ngpio); } static const struct gpio_chip bcm2835_gpio_chip = { -- cgit 1.4.1 From 3e6c92a346608d21f8388d9148149c40276421f0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 30 Apr 2023 18:57:50 +0200 Subject: platform/x86: lenovo-yogabook: Fix work race on remove() [ Upstream commit 9148cd2eb4450a8e9c49c8a14201fb82f651128f ] When yogabook_wmi_remove() runs yogabook_wmi_work might still be running and using the devices which yogabook_wmi_remove() puts. To avoid this move to explicitly cancelling the work rather then using devm_work_autocancel(). This requires also making the yogabook_backside_hall_irq handler non devm managed, so that it cannot re-queue the work while yogabook_wmi_remove() runs. Fixes: c0549b72d99d ("platform/x86: lenovo-yogabook-wmi: Add driver for Lenovo Yoga Book") Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230430165807.472798-3-hdegoede@redhat.com Signed-off-by: Sasha Levin --- drivers/platform/x86/lenovo-yogabook-wmi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/lenovo-yogabook-wmi.c b/drivers/platform/x86/lenovo-yogabook-wmi.c index 5f4bd1eec38a..3a6de4ab74a4 100644 --- a/drivers/platform/x86/lenovo-yogabook-wmi.c +++ b/drivers/platform/x86/lenovo-yogabook-wmi.c @@ -2,7 +2,6 @@ /* WMI driver for Lenovo Yoga Book YB1-X90* / -X91* tablets */ #include -#include #include #include #include @@ -248,10 +247,7 @@ static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context) data->brightness = YB_KBD_BL_DEFAULT; set_bit(YB_KBD_IS_ON, &data->flags); set_bit(YB_DIGITIZER_IS_ON, &data->flags); - - r = devm_work_autocancel(&wdev->dev, &data->work, yogabook_wmi_work); - if (r) - return r; + INIT_WORK(&data->work, yogabook_wmi_work); data->kbd_adev = acpi_dev_get_first_match_dev("GDIX1001", NULL, -1); if (!data->kbd_adev) { @@ -299,10 +295,9 @@ static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context) } data->backside_hall_irq = r; - r = devm_request_irq(&wdev->dev, data->backside_hall_irq, - yogabook_backside_hall_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "backside_hall_sw", data); + r = request_irq(data->backside_hall_irq, yogabook_backside_hall_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "backside_hall_sw", data); if (r) { dev_err_probe(&wdev->dev, r, "Requesting backside_hall_sw IRQ\n"); goto error_put_devs; @@ -318,11 +313,14 @@ static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context) r = devm_led_classdev_register(&wdev->dev, &data->kbd_bl_led); if (r < 0) { dev_err_probe(&wdev->dev, r, "Registering backlight LED device\n"); - goto error_put_devs; + goto error_free_irq; } return 0; +error_free_irq: + free_irq(data->backside_hall_irq, data); + cancel_work_sync(&data->work); error_put_devs: put_device(data->dig_dev); put_device(data->kbd_dev); @@ -335,6 +333,8 @@ static void yogabook_wmi_remove(struct wmi_device *wdev) { struct yogabook_wmi *data = dev_get_drvdata(&wdev->dev); + free_irq(data->backside_hall_irq, data); + cancel_work_sync(&data->work); put_device(data->dig_dev); put_device(data->kbd_dev); acpi_dev_put(data->dig_adev); -- cgit 1.4.1 From fa177f7011c8765f4d7a4aa1336a5216b8b91b5e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 30 Apr 2023 18:57:51 +0200 Subject: platform/x86: lenovo-yogabook: Reprobe devices on remove() [ Upstream commit 711bcc0cb34e96a60e88d7b0260862781de3e530 ] Ensure that both the keyboard touchscreen and the digitizer have their driver bound after remove(). Without this modprobing lenovo-yogabook-wmi after a rmmod fails because lenovo-yogabook-wmi defers probing until both devices have their driver bound. Fixes: c0549b72d99d ("platform/x86: lenovo-yogabook-wmi: Add driver for Lenovo Yoga Book") Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230430165807.472798-4-hdegoede@redhat.com Signed-off-by: Sasha Levin --- drivers/platform/x86/lenovo-yogabook-wmi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/x86/lenovo-yogabook-wmi.c b/drivers/platform/x86/lenovo-yogabook-wmi.c index 3a6de4ab74a4..5948ffa74acd 100644 --- a/drivers/platform/x86/lenovo-yogabook-wmi.c +++ b/drivers/platform/x86/lenovo-yogabook-wmi.c @@ -332,9 +332,20 @@ error_put_devs: static void yogabook_wmi_remove(struct wmi_device *wdev) { struct yogabook_wmi *data = dev_get_drvdata(&wdev->dev); + int r = 0; free_irq(data->backside_hall_irq, data); cancel_work_sync(&data->work); + + if (!test_bit(YB_KBD_IS_ON, &data->flags)) + r |= device_reprobe(data->kbd_dev); + + if (!test_bit(YB_DIGITIZER_IS_ON, &data->flags)) + r |= device_reprobe(data->dig_dev); + + if (r) + dev_warn(&wdev->dev, "Reprobe of devices failed\n"); + put_device(data->dig_dev); put_device(data->kbd_dev); acpi_dev_put(data->dig_adev); -- cgit 1.4.1 From 3a080e1b11452eef4c1e80ab616f159b224be9b0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 30 Apr 2023 18:57:52 +0200 Subject: platform/x86: lenovo-yogabook: Set default keyboard backligh brightness on probe() [ Upstream commit 9e6380d6573181c555ca1b5019b08d19a9ee581c ] Set default keyboard backlight brightness on probe(), this fixes the backlight being off after a rmmod + modprobe. Fixes: c0549b72d99d ("platform/x86: lenovo-yogabook-wmi: Add driver for Lenovo Yoga Book") Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230430165807.472798-5-hdegoede@redhat.com Signed-off-by: Sasha Levin --- drivers/platform/x86/lenovo-yogabook-wmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/lenovo-yogabook-wmi.c b/drivers/platform/x86/lenovo-yogabook-wmi.c index 5948ffa74acd..d57fcc838851 100644 --- a/drivers/platform/x86/lenovo-yogabook-wmi.c +++ b/drivers/platform/x86/lenovo-yogabook-wmi.c @@ -295,6 +295,9 @@ static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context) } data->backside_hall_irq = r; + /* Set default brightness before enabling the IRQ */ + yogabook_wmi_set_kbd_backlight(data->wdev, YB_KBD_BL_DEFAULT); + r = request_irq(data->backside_hall_irq, yogabook_backside_hall_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "backside_hall_sw", data); -- cgit 1.4.1 From 7aecdd47910c51707696e8b0e045b9f88bd4230f Mon Sep 17 00:00:00 2001 From: Ding Hui Date: Sun, 7 May 2023 11:40:57 +0800 Subject: PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free [ Upstream commit 456d8aa37d0f56fc9e985e812496e861dcd6f2f2 ] Struct pcie_link_state->downstream is a pointer to the pci_dev of function 0. Previously we retained that pointer when removing function 0, and subsequent ASPM policy changes dereferenced it, resulting in a use-after-free warning from KASAN, e.g.: # echo 1 > /sys/bus/pci/devices/0000:03:00.0/remove # echo powersave > /sys/module/pcie_aspm/parameters/policy BUG: KASAN: slab-use-after-free in pcie_config_aspm_link+0x42d/0x500 Call Trace: kasan_report+0xae/0xe0 pcie_config_aspm_link+0x42d/0x500 pcie_aspm_set_policy+0x8e/0x1a0 param_attr_store+0x162/0x2c0 module_attr_store+0x3e/0x80 PCIe spec r6.0, sec 7.5.3.7, recommends that software program the same ASPM Control value in all functions of multi-function devices. Disable ASPM and free the pcie_link_state when any child function is removed so we can discard the dangling pcie_link_state->downstream pointer and maintain the same ASPM Control configuration for all functions. [bhelgaas: commit log and comment] Debugged-by: Zongquan Qin Suggested-by: Bjorn Helgaas Fixes: b5a0a9b59c81 ("PCI/ASPM: Read and set up L1 substate capabilities") Link: https://lore.kernel.org/r/20230507034057.20970-1-dinghui@sangfor.com.cn Signed-off-by: Ding Hui Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin --- drivers/pci/pcie/aspm.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 4b4184563a92..74b8183c305d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1010,21 +1010,24 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - /* - * All PCIe functions are in one slot, remove one function will remove - * the whole slot, so just wait until we are the last function left. - */ - if (!list_empty(&parent->subordinate->devices)) - goto out; link = parent->link_state; root = link->root; parent_link = link->parent; - /* All functions are removed, so just disable ASPM for the link */ + /* + * link->downstream is a pointer to the pci_dev of function 0. If + * we remove that function, the pci_dev is about to be deallocated, + * so we can't use link->downstream again. Free the link state to + * avoid this. + * + * If we're removing a non-0 function, it's possible we could + * retain the link state, but PCIe r6.0, sec 7.5.3.7, recommends + * programming the same ASPM Control value for all functions of + * multi-function devices, so disable ASPM for all of them. + */ pcie_config_aspm_link(link, 0); list_del(&link->sibling); - /* Clock PM is for endpoint device */ free_link_state(link); /* Recheck latencies and configure upstream links */ @@ -1032,7 +1035,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) pcie_update_aspm_capable(root); pcie_config_aspm_path(parent_link); } -out: + mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); } -- cgit 1.4.1 From 6c1b079e266bb70c699497548e3e4c8b57e5992e Mon Sep 17 00:00:00 2001 From: Yuchen Yang Date: Fri, 5 May 2023 22:12:55 +0800 Subject: scsi: 3w-xxxx: Add error handling for initialization failure in tw_probe() [ Upstream commit 2e2fe5ac695a00ab03cab4db1f4d6be07168ed9d ] Smatch complains that: tw_probe() warn: missing error code 'retval' This patch adds error checking to tw_probe() to handle initialization failure. If tw_reset_sequence() function returns a non-zero value, the function will return -EINVAL to indicate initialization failure. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Yuchen Yang Link: https://lore.kernel.org/r/20230505141259.7730-1-u202114568@hust.edu.cn Reviewed-by: Dan Carpenter Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/3w-xxxx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index ffdecb12d654..9bd70e4618d5 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -2305,8 +2305,10 @@ static int tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) TW_DISABLE_INTERRUPTS(tw_dev); /* Initialize the card */ - if (tw_reset_sequence(tw_dev)) + if (tw_reset_sequence(tw_dev)) { + retval = -EINVAL; goto out_release_mem_region; + } /* Set host specific parameters */ host->max_id = TW_MAX_UNITS; -- cgit 1.4.1 From a982c13e11fbfd2d978ecafc00c10cfa91bb08cd Mon Sep 17 00:00:00 2001 From: Rongguang Wei Date: Fri, 12 May 2023 10:15:18 +0800 Subject: PCI: pciehp: Cancel bringup sequence if card is not present [ Upstream commit e8afd0d9fccc27c8ad263db5cf5952cfcf72d6fe ] If a PCIe hotplug slot has an Attention Button, the normal hot-add flow is: - Slot is empty and slot power is off - User inserts card in slot and presses Attention Button - OS blinks Power Indicator for 5 seconds - After 5 seconds, OS turns on Power Indicator, turns on slot power, and enumerates the device Previously, if a user pressed the Attention Button on an *empty* slot, pciehp logged the following messages and blinked the Power Indicator until a second button press: [0.000] pciehp: Button press: will power on in 5 sec [0.001] # Power Indicator starts blinking [5.001] # 5 second timeout; slot is empty, so we should cancel the request to power on and turn off Power Indicator [7.000] # Power Indicator still blinking [8.000] # possible card insertion [9.000] pciehp: Button press: canceling request to power on The first button press incorrectly left the slot in BLINKINGON_STATE, so the second was interpreted as a "cancel power on" event regardless of whether a card was present. If the slot is empty, turn off the Power Indicator and return from BLINKINGON_STATE to OFF_STATE after 5 seconds, effectively canceling the request to power on. Putting the slot in OFF_STATE also means the second button press will correctly request a slot power on if the slot is occupied. [bhelgaas: commit log] Link: https://lore.kernel.org/r/20230512021518.336460-1-clementwei90@163.com Fixes: d331710ea78f ("PCI: pciehp: Become resilient to missed events") Suggested-by: Lukas Wunner Signed-off-by: Rongguang Wei Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner Signed-off-by: Sasha Levin --- drivers/pci/hotplug/pciehp_ctrl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 529c34808440..32baba1b7f13 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -256,6 +256,14 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) present = pciehp_card_present(ctrl); link_active = pciehp_check_link_active(ctrl); if (present <= 0 && link_active <= 0) { + if (ctrl->state == BLINKINGON_STATE) { + ctrl->state = OFF_STATE; + cancel_delayed_work(&ctrl->button_work); + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + INDICATOR_NOOP); + ctrl_info(ctrl, "Slot(%s): Card not present\n", + slot_name(ctrl)); + } mutex_unlock(&ctrl->state_lock); return; } -- cgit 1.4.1 From bc796f65cdc870113418a37daf1f91cd303722e3 Mon Sep 17 00:00:00 2001 From: Junyan Ye Date: Mon, 8 May 2023 12:36:41 +0800 Subject: PCI: ftpci100: Release the clock resources [ Upstream commit c60738de85f40b0b9f5cb23c21f9246e5a47908c ] Smatch reported: 1. drivers/pci/controller/pci-ftpci100.c:526 faraday_pci_probe() warn: 'clk' from clk_prepare_enable() not released on lines: 442,451,462,478,512,517. 2. drivers/pci/controller/pci-ftpci100.c:526 faraday_pci_probe() warn: 'p->bus_clk' from clk_prepare_enable() not released on lines: 451,462,478,512,517. The clock resource is obtained by devm_clk_get(), and then clk_prepare_enable() makes the clock resource ready for use. After that, clk_disable_unprepare() should be called to release the clock resource when it is no longer needed. However, while doing some error handling in faraday_pci_probe(), clk_disable_unprepare() is not called to release clk and p->bus_clk before returning. These return lines are exactly 442, 451, 462, 478, 512, 517. Fix this warning by replacing devm_clk_get() with devm_clk_get_enabled(), which is equivalent to devm_clk_get() + clk_prepare_enable(). And with devm_clk_get_enabled(), the clock will automatically be disabled, unprepared and freed when the device is unbound from the bus. Link: https://lore.kernel.org/r/20230508043641.23807-1-yejunyan@hust.edu.cn Fixes: b3c433efb8a3 ("PCI: faraday: Fix wrong pointer passed to PTR_ERR()") Fixes: 2eeb02b28579 ("PCI: faraday: Add clock handling") Fixes: 783a862563f7 ("PCI: faraday: Use pci_parse_request_of_pci_ranges()") Fixes: d3c68e0a7e34 ("PCI: faraday: Add Faraday Technology FTPCI100 PCI Host Bridge driver") Fixes: f1e8bd21e39e ("PCI: faraday: Convert IRQ masking to raw PCI config accessors") Signed-off-by: Junyan Ye Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dongliang Mu Reviewed-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pci/controller/pci-ftpci100.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index 0cfd9d5a497c..f7e61e169d83 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -429,22 +429,12 @@ static int faraday_pci_probe(struct platform_device *pdev) p->dev = dev; /* Retrieve and enable optional clocks */ - clk = devm_clk_get(dev, "PCLK"); + clk = devm_clk_get_enabled(dev, "PCLK"); if (IS_ERR(clk)) return PTR_ERR(clk); - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "could not prepare PCLK\n"); - return ret; - } - p->bus_clk = devm_clk_get(dev, "PCICLK"); + p->bus_clk = devm_clk_get_enabled(dev, "PCICLK"); if (IS_ERR(p->bus_clk)) return PTR_ERR(p->bus_clk); - ret = clk_prepare_enable(p->bus_clk); - if (ret) { - dev_err(dev, "could not prepare PCICLK\n"); - return ret; - } p->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p->base)) -- cgit 1.4.1 From 019d4fd93a5ad51648c3cd1fcff41d214224b39a Mon Sep 17 00:00:00 2001 From: Wells Lu Date: Sun, 28 May 2023 20:34:37 +0800 Subject: pinctrl: sunplus: Add check for kmalloc [ Upstream commit a5961bed5429cf1134d7f539b4ed60317012f84d ] Fix Smatch static checker warning: potential null dereference 'configs'. (kmalloc returns null) Fixes: aa74c44be19c ("pinctrl: Add driver for Sunplus SP7021") Signed-off-by: Wells Lu Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/1685277277-12209-1-git-send-email-wellslutw@gmail.com Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/sunplus/sppctl.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/sunplus/sppctl.c b/drivers/pinctrl/sunplus/sppctl.c index 2b3335ab56c6..0449595b943c 100644 --- a/drivers/pinctrl/sunplus/sppctl.c +++ b/drivers/pinctrl/sunplus/sppctl.c @@ -838,11 +838,6 @@ static int sppctl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node int i, size = 0; list = of_get_property(np_config, "sunplus,pins", &size); - - if (nmG <= 0) - nmG = 0; - - parent = of_get_parent(np_config); *num_maps = size / sizeof(*list); /* @@ -870,10 +865,14 @@ static int sppctl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node } } + if (nmG <= 0) + nmG = 0; + *map = kcalloc(*num_maps + nmG, sizeof(**map), GFP_KERNEL); - if (*map == NULL) + if (!(*map)) return -ENOMEM; + parent = of_get_parent(np_config); for (i = 0; i < (*num_maps); i++) { dt_pin = be32_to_cpu(list[i]); pin_num = FIELD_GET(GENMASK(31, 24), dt_pin); @@ -887,6 +886,8 @@ static int sppctl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node (*map)[i].data.configs.num_configs = 1; (*map)[i].data.configs.group_or_pin = pin_get_name(pctldev, pin_num); configs = kmalloc(sizeof(*configs), GFP_KERNEL); + if (!configs) + goto sppctl_map_err; *configs = FIELD_GET(GENMASK(7, 0), dt_pin); (*map)[i].data.configs.configs = configs; @@ -900,6 +901,8 @@ static int sppctl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node (*map)[i].data.configs.num_configs = 1; (*map)[i].data.configs.group_or_pin = pin_get_name(pctldev, pin_num); configs = kmalloc(sizeof(*configs), GFP_KERNEL); + if (!configs) + goto sppctl_map_err; *configs = SPPCTL_IOP_CONFIGS; (*map)[i].data.configs.configs = configs; @@ -969,6 +972,15 @@ static int sppctl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node of_node_put(parent); dev_dbg(pctldev->dev, "%d pins mapped\n", *num_maps); return 0; + +sppctl_map_err: + for (i = 0; i < (*num_maps); i++) + if (((*map)[i].type == PIN_MAP_TYPE_CONFIGS_PIN) && + (*map)[i].data.configs.configs) + kfree((*map)[i].data.configs.configs); + kfree(*map); + of_node_put(parent); + return -ENOMEM; } static const struct pinctrl_ops sppctl_pctl_ops = { -- cgit 1.4.1 From 7d3664d24f32413993badf7b66804c8b69bc7232 Mon Sep 17 00:00:00 2001 From: Sui Jingfeng Date: Wed, 31 May 2023 18:27:44 +0800 Subject: PCI: Add pci_clear_master() stub for non-CONFIG_PCI [ Upstream commit 2aa5ac633259843f656eb6ecff4cf01e8e810c5e ] Add a pci_clear_master() stub when CONFIG_PCI is not set so drivers that support both PCI and platform devices don't need #ifdefs or extra Kconfig symbols for the PCI parts. [bhelgaas: commit log] Fixes: 6a479079c072 ("PCI: Add pci_clear_master() as opposite of pci_set_master()") Link: https://lore.kernel.org/r/20230531102744.2354313-1-suijingfeng@loongson.cn Signed-off-by: Sui Jingfeng Signed-off-by: Bjorn Helgaas Reviewed-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- include/linux/pci.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index d20695184e0b..9f617ffdb863 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1809,6 +1809,7 @@ static inline int pci_dev_present(const struct pci_device_id *ids) #define pci_dev_put(dev) do { } while (0) static inline void pci_set_master(struct pci_dev *dev) { } +static inline void pci_clear_master(struct pci_dev *dev) { } static inline int pci_enable_device(struct pci_dev *dev) { return -EIO; } static inline void pci_disable_device(struct pci_dev *dev) { } static inline int pcim_enable_device(struct pci_dev *pdev) { return -EIO; } -- cgit 1.4.1 From 251c6615a7e9571b32661751f783dd72aed1691a Mon Sep 17 00:00:00 2001 From: Justin Tee Date: Tue, 23 May 2023 11:32:01 -0700 Subject: scsi: lpfc: Revise NPIV ELS unsol rcv cmpl logic to drop ndlp based on nlp_state [ Upstream commit 9914a3d033d3e1d836a43e93e9738e7dd44a096a ] When NPIV ports are zoned to devices that support both initiator and target mode, a remote device's initiated PRLI results in unintended final kref clean up of the device's ndlp structure. This disrupts NPIV ports' discovery for target devices that support both initiator and target mode. Modify the NPIV lpfc_drop_node clause such that we allow the ndlp to live so long as it was in NLP_STE_PLOGI_ISSUE, NLP_STE_REG_LOGIN_ISSUE, or NLP_STE_PRLI_ISSUE nlp_state. This allows lpfc's issued PRLI completion routine to determine if the final kref clean up should execute rather than a remote device's issued PRLI. Fixes: db651ec22524 ("scsi: lpfc: Correct used_rpi count when devloss tmo fires with no recovery") Signed-off-by: Justin Tee Link: https://lore.kernel.org/r/20230523183206.7728-5-justintee8345@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/lpfc/lpfc_els.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index ddd5949d8fc0..e21c73a3803e 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -5413,9 +5413,19 @@ out: ndlp->nlp_flag &= ~NLP_RELEASE_RPI; spin_unlock_irq(&ndlp->lock); } + lpfc_drop_node(vport, ndlp); + } else if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE && + ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE && + ndlp->nlp_state != NLP_STE_PRLI_ISSUE) { + /* Drop ndlp if there is no planned or outstanding + * issued PRLI. + * + * In cases when the ndlp is acting as both an initiator + * and target function, let our issued PRLI determine + * the final ndlp kref drop. + */ + lpfc_drop_node(vport, ndlp); } - - lpfc_drop_node(vport, ndlp); } /* Release the originating I/O reference. */ -- cgit 1.4.1 From 1ebe7d40ed7fcc4f081fb56772498f9c692f22d6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 2 Jun 2023 15:38:25 -0300 Subject: perf bench: Add missing setlocale() call to allow usage of %'d style formatting [ Upstream commit 16203e9cd01896b4244100a8e3fb9f6e612ab2b1 ] Without this we were not getting the thousands separator for big numbers. Noticed while developing 'perf bench uprobe', but the use of %' predates that, for instance 'perf bench syscall' uses it. Before: # perf bench uprobe all # Running uprobe/baseline benchmark... # Executed 1000 usleep(1000) calls Total time: 1054082243ns 1054082.243000 nsecs/op # After: # perf bench uprobe all # Running uprobe/baseline benchmark... # Executed 1,000 usleep(1000) calls Total time: 1,053,715,144ns 1,053,715.144000 nsecs/op # Fixes: c2a08203052f8975 ("perf bench: Add basic syscall benchmark") Cc: Adrian Hunter Cc: Andre Fredette Cc: Clark Williams Cc: Dave Tucker Cc: Davidlohr Bueso Cc: Derek Barbosa Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Tiezhu Yang Link: https://lore.kernel.org/lkml/ZH3lcepZ4tBYr1jv@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-bench.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 334ab897aae3..a5c2a6938b4e 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -21,6 +21,7 @@ #include "builtin.h" #include "bench/bench.h" +#include #include #include #include @@ -256,6 +257,7 @@ int cmd_bench(int argc, const char **argv) /* Unbuffered output */ setvbuf(stdout, NULL, _IONBF, 0); + setlocale(LC_ALL, ""); if (argc < 2) { /* No collection specified. */ -- cgit 1.4.1 From d1390b057d594f0b8c8ea15780902f462cb2d70a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 5 Jun 2023 17:37:34 +0300 Subject: pinctrl: cherryview: Return correct value if pin in push-pull mode [ Upstream commit 5835196a17be5cfdcad0b617f90cf4abe16951a4 ] Currently the getter returns ENOTSUPP on pin configured in the push-pull mode. Fix this by adding the missed switch case. Fixes: ccdf81d08dbe ("pinctrl: cherryview: add option to set open-drain pin config") Fixes: 6e08d6bbebeb ("pinctrl: Add Intel Cherryview/Braswell pin controller support") Acked-by: Mika Westerberg Signed-off-by: Andy Shevchenko Signed-off-by: Sasha Levin --- drivers/pinctrl/intel/pinctrl-cherryview.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 5c4fd16e5b01..0d6b5fab2f7e 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -947,11 +947,6 @@ static int chv_config_get(struct pinctrl_dev *pctldev, unsigned int pin, break; - case PIN_CONFIG_DRIVE_OPEN_DRAIN: - if (!(ctrl1 & CHV_PADCTRL1_ODEN)) - return -EINVAL; - break; - case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: { u32 cfg; @@ -961,6 +956,16 @@ static int chv_config_get(struct pinctrl_dev *pctldev, unsigned int pin, return -EINVAL; break; + + case PIN_CONFIG_DRIVE_PUSH_PULL: + if (ctrl1 & CHV_PADCTRL1_ODEN) + return -EINVAL; + break; + + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (!(ctrl1 & CHV_PADCTRL1_ODEN)) + return -EINVAL; + break; } default: -- cgit 1.4.1 From 61545eb787fe7b6274a239f9a7637e357c8c8011 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Thu, 1 Jun 2023 16:05:45 -0400 Subject: platform/x86: think-lmi: mutex protection around multiple WMI calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c41e0121a1221894a1a9c4666156db9e1def4d6c ] When an attribute is being changed if the Admin account is enabled, or if a password is being updated then multiple WMI calls are needed. Add mutex protection to ensure no race conditions are introduced. Fixes: b49f72e7f96d ("platform/x86: think-lmi: Certificate authentication support") Signed-off-by: Mark Pearson Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230601200552.4396-1-mpearson-lenovo@squebb.ca Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/think-lmi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 336b9029d151..4e6040c0a220 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,7 @@ static const char * const level_options[] = { }; static struct think_lmi tlmi_priv; static struct class *fw_attr_class; +static DEFINE_MUTEX(tlmi_mutex); /* ------ Utility functions ------------*/ /* Strip out CR if one is present */ @@ -437,6 +439,9 @@ static ssize_t new_password_store(struct kobject *kobj, /* Strip out CR if one is present, setting password won't work if it is present */ strip_cr(new_pwd); + /* Use lock in case multiple WMI operations needed */ + mutex_lock(&tlmi_mutex); + pwdlen = strlen(new_pwd); /* pwdlen == 0 is allowed to clear the password */ if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) { @@ -493,6 +498,7 @@ static ssize_t new_password_store(struct kobject *kobj, kfree(auth_str); } out: + mutex_unlock(&tlmi_mutex); kfree(new_pwd); return ret ?: count; } @@ -982,6 +988,9 @@ static ssize_t current_value_store(struct kobject *kobj, /* Strip out CR if one is present */ strip_cr(new_setting); + /* Use lock in case multiple WMI operations needed */ + mutex_lock(&tlmi_mutex); + /* Check if certificate authentication is enabled and active */ if (tlmi_priv.certificate_support && tlmi_priv.pwd_admin->cert_installed) { if (!tlmi_priv.pwd_admin->signature || !tlmi_priv.pwd_admin->save_signature) { @@ -1040,6 +1049,7 @@ static ssize_t current_value_store(struct kobject *kobj, kobject_uevent(&tlmi_priv.class_dev->kobj, KOBJ_CHANGE); } out: + mutex_unlock(&tlmi_mutex); kfree(auth_str); kfree(set_str); kfree(new_setting); -- cgit 1.4.1 From 699b59310126e6ba74e42b9ee219e474051f809f Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Thu, 1 Jun 2023 16:05:47 -0400 Subject: platform/x86: think-lmi: Correct System password interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 97eef5983372d7aee6549d644d788fd0c10d2b6e ] The system password identification was incorrect. This means that if the password was enabled it wouldn't be detected correctly; and setting it would not work. Also updated code to use TLMI_SMP_PWD instead of TLMI_SYS_PWD to be in sync with Lenovo documentation. Fixes: 640a5fa50a42 ("platform/x86: think-lmi: Opcode support") Signed-off-by: Mark Pearson Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230601200552.4396-3-mpearson-lenovo@squebb.ca Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/think-lmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 4e6040c0a220..3a511c0f13e4 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -172,7 +172,7 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support"); #define TLMI_POP_PWD (1 << 0) #define TLMI_PAP_PWD (1 << 1) #define TLMI_HDD_PWD (1 << 2) -#define TLMI_SYS_PWD (1 << 3) +#define TLMI_SMP_PWD (1 << 6) /* System Management */ #define TLMI_CERT (1 << 7) #define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj) @@ -1522,11 +1522,11 @@ static int tlmi_analyze(void) tlmi_priv.pwd_power->valid = true; if (tlmi_priv.opcode_support) { - tlmi_priv.pwd_system = tlmi_create_auth("sys", "system"); + tlmi_priv.pwd_system = tlmi_create_auth("smp", "system"); if (!tlmi_priv.pwd_system) goto fail_clear_attr; - if (tlmi_priv.pwdcfg.core.password_state & TLMI_SYS_PWD) + if (tlmi_priv.pwdcfg.core.password_state & TLMI_SMP_PWD) tlmi_priv.pwd_system->valid = true; tlmi_priv.pwd_hdd = tlmi_create_auth("hdd", "hdd"); -- cgit 1.4.1 From 8362ea6158038d84b33699dd18032beb3c522479 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Thu, 1 Jun 2023 16:05:50 -0400 Subject: platform/x86: think-lmi: Correct NVME password handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4cebb42412248d28df6de01420cfac5654428d41 ] NVME passwords identifier have been standardised across the Lenovo systems and now use udrp and adrp (user and admin level) instead of unvp and mnvp. This should apparently be backwards compatible. Fixes: 640a5fa50a42 ("platform/x86: think-lmi: Opcode support") Signed-off-by: Mark Pearson Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230601200552.4396-6-mpearson-lenovo@squebb.ca Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/think-lmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 3a511c0f13e4..3cbb92b6c521 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -461,9 +461,9 @@ static ssize_t new_password_store(struct kobject *kobj, sprintf(pwd_type, "mhdp%d", setting->index); } else if (setting == tlmi_priv.pwd_nvme) { if (setting->level == TLMI_LEVEL_USER) - sprintf(pwd_type, "unvp%d", setting->index); + sprintf(pwd_type, "udrp%d", setting->index); else - sprintf(pwd_type, "mnvp%d", setting->index); + sprintf(pwd_type, "adrp%d", setting->index); } else { sprintf(pwd_type, "%s", setting->pwd_type); } -- cgit 1.4.1 From fc45a8be5e5289439ac9b0b3795d709baff04bf1 Mon Sep 17 00:00:00 2001 From: Wells Lu Date: Sun, 28 May 2023 20:34:37 +0800 Subject: pinctrl:sunplus: Add check for kmalloc [ Upstream commit 73f8ce7f961afcb3be49352efeb7c26cc1c00cc4 ] Fix Smatch static checker warning: potential null dereference 'configs'. (kmalloc returns null) Changes in v2: 1. Add free allocated memory before returned -ENOMEM. 2. Add call of_node_put() before returned -ENOMEM. Fixes: aa74c44be19c ("pinctrl: Add driver for Sunplus SP7021") Signed-off-by: Wells Lu Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/1685277277-12209-1-git-send-email-wellslutw@gmail.com [Rebased on the patch from Lu Hongfei] Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/sunplus/sppctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pinctrl/sunplus/sppctl.c b/drivers/pinctrl/sunplus/sppctl.c index 0449595b943c..6b24fa0e63c0 100644 --- a/drivers/pinctrl/sunplus/sppctl.c +++ b/drivers/pinctrl/sunplus/sppctl.c @@ -975,8 +975,7 @@ static int sppctl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node sppctl_map_err: for (i = 0; i < (*num_maps); i++) - if (((*map)[i].type == PIN_MAP_TYPE_CONFIGS_PIN) && - (*map)[i].data.configs.configs) + if ((*map)[i].type == PIN_MAP_TYPE_CONFIGS_PIN) kfree((*map)[i].data.configs.configs); kfree(*map); of_node_put(parent); -- cgit 1.4.1 From c32afc7e864c9a3fe910b5da058719ad686efeff Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Wed, 7 Jun 2023 17:58:29 +0800 Subject: pinctrl: npcm7xx: Add missing check for ioremap [ Upstream commit ad64639417161e90b30dda00486570eb150aeee5 ] Add check for ioremap() and return the error if it fails in order to guarantee the success of ioremap(). Fixes: 3b588e43ee5c ("pinctrl: nuvoton: add NPCM7xx pinctrl and GPIO driver") Signed-off-by: Jiasheng Jiang Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230607095829.1345-1-jiasheng@iscas.ac.cn Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c b/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c index 1c4e89b046de..ac4c69132fa0 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c +++ b/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c @@ -1878,6 +1878,8 @@ static int npcm7xx_gpio_of(struct npcm7xx_pinctrl *pctrl) } pctrl->gpio_bank[id].base = ioremap(res.start, resource_size(&res)); + if (!pctrl->gpio_bank[id].base) + return -EINVAL; ret = bgpio_init(&pctrl->gpio_bank[id].gc, dev, 4, pctrl->gpio_bank[id].base + NPCM7XX_GP_N_DIN, -- cgit 1.4.1 From 081f642b316f8be8140ae30dc2b44f406292ddc5 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 12 May 2023 17:31:17 +0200 Subject: kcsan: Don't expect 64 bits atomic builtins from 32 bits architectures [ Upstream commit 353e7300a1db928e427462f2745f9a2cd1625b3d ] Activating KCSAN on a 32 bits architecture leads to the following link-time failure: LD .tmp_vmlinux.kallsyms1 powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_load': kernel/kcsan/core.c:1273: undefined reference to `__atomic_load_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_store': kernel/kcsan/core.c:1273: undefined reference to `__atomic_store_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_exchange': kernel/kcsan/core.c:1273: undefined reference to `__atomic_exchange_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_fetch_add': kernel/kcsan/core.c:1273: undefined reference to `__atomic_fetch_add_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_fetch_sub': kernel/kcsan/core.c:1273: undefined reference to `__atomic_fetch_sub_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_fetch_and': kernel/kcsan/core.c:1273: undefined reference to `__atomic_fetch_and_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_fetch_or': kernel/kcsan/core.c:1273: undefined reference to `__atomic_fetch_or_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_fetch_xor': kernel/kcsan/core.c:1273: undefined reference to `__atomic_fetch_xor_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_fetch_nand': kernel/kcsan/core.c:1273: undefined reference to `__atomic_fetch_nand_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_compare_exchange_strong': kernel/kcsan/core.c:1273: undefined reference to `__atomic_compare_exchange_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_compare_exchange_weak': kernel/kcsan/core.c:1273: undefined reference to `__atomic_compare_exchange_8' powerpc64-linux-ld: kernel/kcsan/core.o: in function `__tsan_atomic64_compare_exchange_val': kernel/kcsan/core.c:1273: undefined reference to `__atomic_compare_exchange_8' 32 bits architectures don't have 64 bits atomic builtins. Only include DEFINE_TSAN_ATOMIC_OPS(64) on 64 bits architectures. Fixes: 0f8ad5f2e934 ("kcsan: Add support for atomic builtins") Suggested-by: Marco Elver Signed-off-by: Christophe Leroy Reviewed-by: Marco Elver Acked-by: Marco Elver Signed-off-by: Michael Ellerman Link: https://msgid.link/d9c6afc28d0855240171a4e0ad9ffcdb9d07fceb.1683892665.git.christophe.leroy@csgroup.eu Signed-off-by: Sasha Levin --- kernel/kcsan/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c index 5a60cc52adc0..8a7baf4e332e 100644 --- a/kernel/kcsan/core.c +++ b/kernel/kcsan/core.c @@ -1270,7 +1270,9 @@ static __always_inline void kcsan_atomic_builtin_memorder(int memorder) DEFINE_TSAN_ATOMIC_OPS(8); DEFINE_TSAN_ATOMIC_OPS(16); DEFINE_TSAN_ATOMIC_OPS(32); +#ifdef CONFIG_64BIT DEFINE_TSAN_ATOMIC_OPS(64); +#endif void __tsan_atomic_thread_fence(int memorder); void __tsan_atomic_thread_fence(int memorder) -- cgit 1.4.1 From af0c61c5bba034e8f3f6df0af71f489b95fdcd7b Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 5 Jun 2023 10:55:26 +0200 Subject: powerpc/interrupt: Don't read MSR from interrupt_exit_kernel_prepare() [ Upstream commit 0eb089a72fda3f7969e6277804bde75dc1474a14 ] A disassembly of interrupt_exit_kernel_prepare() shows a useless read of MSR register. This is shown by r9 being re-used immediately without doing anything with the value read. c000e0e0: 60 00 00 00 nop c000e0e4: 7d 3a c2 a6 mfmd_ap r9 c000e0e8: 7d 20 00 a6 mfmsr r9 c000e0ec: 7c 51 13 a6 mtspr 81,r2 c000e0f0: 81 3f 00 84 lwz r9,132(r31) c000e0f4: 71 29 80 00 andi. r9,r9,32768 This is due to the use of local_irq_save(). The flags read by local_irq_save() are never used, use local_irq_disable() instead. Fixes: 13799748b957 ("powerpc/64: use interrupt restart table to speed up return from interrupt") Signed-off-by: Christophe Leroy Reviewed-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://msgid.link/df36c6205ab64326fb1b991993c82057e92ace2f.1685955214.git.christophe.leroy@csgroup.eu Signed-off-by: Sasha Levin --- arch/powerpc/kernel/interrupt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index 0ec1581619db..cf770d86c03c 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -368,7 +368,6 @@ void preempt_schedule_irq(void); notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs) { - unsigned long flags; unsigned long ret = 0; unsigned long kuap; bool stack_store = read_thread_flags() & _TIF_EMULATE_STACK_STORE; @@ -392,7 +391,7 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs) kuap = kuap_get_and_assert_locked(); - local_irq_save(flags); + local_irq_disable(); if (!arch_irq_disabled_regs(regs)) { /* Returning to a kernel context with local irqs enabled. */ -- cgit 1.4.1 From 7cfd3101113525c512044d0b1a060ae55055490f Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 5 Jun 2023 10:58:35 +0200 Subject: powerpc/signal32: Force inlining of __unsafe_save_user_regs() and save_tm_user_regs_unsafe() [ Upstream commit a03b1a0b19398a47489fdcef02ec19c2ba05a15d ] Looking at generated code for handle_signal32() shows calls to a function called __unsafe_save_user_regs.constprop.0 while user access is open. And that __unsafe_save_user_regs.constprop.0 function has two nops at the begining, allowing it to be traced, which is unexpected during user access open window. The solution could be to mark __unsafe_save_user_regs() no trace, but to be on the safe side the most efficient is to flag it __always_inline as already done for function __unsafe_restore_general_regs(). The function is relatively small and only called twice, so the size increase will remain in the noise. Do the same with save_tm_user_regs_unsafe() as it may suffer the same issue. Fixes: ef75e7318294 ("powerpc/signal32: Transform save_user_regs() and save_tm_user_regs() in 'unsafe' version") Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://msgid.link/7e469c8f01860a69c1ada3ca6a5e2aa65f0f74b2.1685955220.git.christophe.leroy@csgroup.eu Signed-off-by: Sasha Levin --- arch/powerpc/kernel/signal_32.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index c114c7f25645..7a718ed32b27 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -264,8 +264,9 @@ static void prepare_save_user_regs(int ctx_has_vsx_region) #endif } -static int __unsafe_save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, - struct mcontext __user *tm_frame, int ctx_has_vsx_region) +static __always_inline int +__unsafe_save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, + struct mcontext __user *tm_frame, int ctx_has_vsx_region) { unsigned long msr = regs->msr; @@ -364,8 +365,9 @@ static void prepare_save_tm_user_regs(void) current->thread.ckvrsave = mfspr(SPRN_VRSAVE); } -static int save_tm_user_regs_unsafe(struct pt_regs *regs, struct mcontext __user *frame, - struct mcontext __user *tm_frame, unsigned long msr) +static __always_inline int +save_tm_user_regs_unsafe(struct pt_regs *regs, struct mcontext __user *frame, + struct mcontext __user *tm_frame, unsigned long msr) { /* Save both sets of general registers */ unsafe_save_general_regs(¤t->thread.ckpt_regs, frame, failed); @@ -444,8 +446,9 @@ failed: #else static void prepare_save_tm_user_regs(void) { } -static int save_tm_user_regs_unsafe(struct pt_regs *regs, struct mcontext __user *frame, - struct mcontext __user *tm_frame, unsigned long msr) +static __always_inline int +save_tm_user_regs_unsafe(struct pt_regs *regs, struct mcontext __user *frame, + struct mcontext __user *tm_frame, unsigned long msr) { return 0; } -- cgit 1.4.1 From 4309bd9e984a3a9f38b6354def5fc332e01ccb07 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 6 Jun 2023 16:11:10 -0300 Subject: perf script: Fix allocation of evsel->priv related to per-event dump files [ Upstream commit 36d3e4138e1b6cc9ab179f3f397b5548f8b1eaae ] When printing output we may want to generate per event files, where the --per-event-dump option should be used, creating perf.data.EVENT.dump files instead of printing to stdout. The callback thar processes event thus expects that evsel->priv->fp should point to either the per-event FILE descriptor or to stdout. The a3af66f51bd0bca7 ("perf script: Fix crash because of missing evsel->priv") changeset fixed a case where evsel->priv wasn't setup, thus set to NULL, causing a segfault when trying to access evsel->priv->fp. But it did it for the non --per-event-dump case by allocating a 'struct perf_evsel_script' just to set its ->fp to stdout. Since evsel->priv is only freed when --per-event-dump is used, we ended up with a memory leak, detected using ASAN. Fix it by using the same method as perf_script__setup_per_event_dump(), and reuse that static 'struct perf_evsel_script'. Also check if evsel_script__new() failed. Fixes: a3af66f51bd0bca7 ("perf script: Fix crash because of missing evsel->priv") Reported-by: Ian Rogers Tested-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Ravi Bangoria Link: https://lore.kernel.org/lkml/ZH+F0wGAWV14zvMP@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-script.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 7ca238277d83..a794a3d2e47b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2390,6 +2390,9 @@ out_put: return ret; } +// Used when scr->per_event_dump is not set +static struct evsel_script es_stdout; + static int process_attr(struct perf_tool *tool, union perf_event *event, struct evlist **pevlist) { @@ -2398,7 +2401,6 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, struct evsel *evsel, *pos; u64 sample_type; int err; - static struct evsel_script *es; err = perf_event__process_attr(tool, event, pevlist); if (err) @@ -2408,14 +2410,13 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, evsel = evlist__last(*pevlist); if (!evsel->priv) { - if (scr->per_event_dump) { + if (scr->per_event_dump) { evsel->priv = evsel_script__new(evsel, scr->session->data); - } else { - es = zalloc(sizeof(*es)); - if (!es) + if (!evsel->priv) return -ENOMEM; - es->fp = stdout; - evsel->priv = es; + } else { // Replicate what is done in perf_script__setup_per_event_dump() + es_stdout.fp = stdout; + evsel->priv = &es_stdout; } } @@ -2721,7 +2722,6 @@ out_err_fclose: static int perf_script__setup_per_event_dump(struct perf_script *script) { struct evsel *evsel; - static struct evsel_script es_stdout; if (script->per_event_dump) return perf_script__fopen_per_event_dump(script); -- cgit 1.4.1 From 4e06e8b1f9139d3b14ae2e30428f0abcfd7f2be5 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Tue, 6 Jun 2023 11:18:04 -0400 Subject: platform/x86: thinkpad_acpi: Fix lkp-tests warnings for platform profiles [ Upstream commit f999e23ce66c1555d7b653fba171a88ecee53704 ] Fix issues identified in dytc_profile_refresh identified by lkp-tests. drivers/platform/x86/thinkpad_acpi.c:10538 dytc_profile_refresh() error: uninitialized symbol 'funcmode'. drivers/platform/x86/thinkpad_acpi.c:10531 dytc_profile_refresh() error: uninitialized symbol 'output'. drivers/platform/x86/thinkpad_acpi.c:10537 dytc_profile_refresh() error: uninitialized symbol 'output'. These issues should not lead to real problems in the field as the refresh function should only be called if MMC or PSC mode enabled. But good to fix. Thanks to Dan Carpenter and the lkp-tests project for flagging these. Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202306011202.1hbgLRD4-lkp@intel.com/ Fixes: 1bc5d819f0b9 ("platform/x86: thinkpad_acpi: Fix profile modes on Intel platforms") Signed-off-by: Mark Pearson Link: https://lore.kernel.org/r/20230606151804.8819-1-mpearson-lenovo@squebb.ca Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/thinkpad_acpi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 5b2c8dd2861b..e7ece2738de9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -10528,8 +10528,8 @@ unlock: static void dytc_profile_refresh(void) { enum platform_profile_option profile; - int output, err = 0; - int perfmode, funcmode; + int output = 0, err = 0; + int perfmode, funcmode = 0; mutex_lock(&dytc_mutex); if (dytc_capabilities & BIT(DYTC_FC_MMC)) { @@ -10542,6 +10542,8 @@ static void dytc_profile_refresh(void) err = dytc_command(DYTC_CMD_GET, &output); /* Check if we are PSC mode, or have AMT enabled */ funcmode = (output >> DYTC_GET_FUNCTION_BIT) & 0xF; + } else { /* Unknown profile mode */ + err = -ENODEV; } mutex_unlock(&dytc_mutex); if (err) -- cgit 1.4.1 From 9999a9f004e109271696c894a930f5fc73846faf Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 12 Jun 2023 16:41:01 -0700 Subject: perf dwarf-aux: Fix off-by-one in die_get_varname() [ Upstream commit 3abfcfd847717d232e36963f31a361747c388fe7 ] The die_get_varname() returns "(unknown_type)" string if it failed to find a type for the variable. But it had a space before the opening parenthesis and it made the closing parenthesis cut off due to the off-by-one in the string length (14). Signed-off-by: Namhyung Kim Fixes: 88fd633cdfa19060 ("perf probe: No need to use formatting strbuf method") Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20230612234102.3909116-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dwarf-aux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 623527edeac1..b125eaadcec4 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1081,7 +1081,7 @@ int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf) ret = die_get_typename(vr_die, buf); if (ret < 0) { pr_debug("Failed to get type, make it unknown.\n"); - ret = strbuf_add(buf, " (unknown_type)", 14); + ret = strbuf_add(buf, "(unknown_type)", 14); } return ret < 0 ? ret : strbuf_addf(buf, "\t%s", dwarf_diename(vr_die)); -- cgit 1.4.1 From c94376dbd6cbf915f568f9936439dbc2dc0a154b Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Tue, 13 Jun 2023 11:43:10 +0300 Subject: platform/x86/dell/dell-rbtn: Fix resources leaking on error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 966cca72ab20289083521a385fa56035d85a222d ] Currently rbtn_add() in case of failure is leaking resources. Fix this by adding a proper rollback. Move devm_kzalloc() before rbtn_acquire(), so it doesn't require rollback in case of failure. While at it, remove unnecessary assignment of NULL to device->driver_data and unnecessary whitespace, plus add a break for the default case in a switch. Suggested-by: Ilpo Järvinen Suggested-by: Pali Rohár Fixes: 817a5cdb40c8 ("dell-rbtn: Dell Airplane Mode Switch driver") Signed-off-by: Michal Wilczynski Reviewed-by: Andy Shevchenko Acked-by: Rafael J. Wysocki Reviewed-by: Pali Rohár Link: https://lore.kernel.org/r/20230613084310.2775896-1-michal.wilczynski@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Sasha Levin --- drivers/platform/x86/dell/dell-rbtn.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c index a89fad47ff13..d3b1cb73de97 100644 --- a/drivers/platform/x86/dell/dell-rbtn.c +++ b/drivers/platform/x86/dell/dell-rbtn.c @@ -395,16 +395,16 @@ static int rbtn_add(struct acpi_device *device) return -EINVAL; } + rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL); + if (!rbtn_data) + return -ENOMEM; + ret = rbtn_acquire(device, true); if (ret < 0) { dev_err(&device->dev, "Cannot enable device\n"); return ret; } - rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL); - if (!rbtn_data) - return -ENOMEM; - rbtn_data->type = type; device->driver_data = rbtn_data; @@ -420,10 +420,12 @@ static int rbtn_add(struct acpi_device *device) break; default: ret = -EINVAL; + break; } + if (ret) + rbtn_acquire(device, false); return ret; - } static int rbtn_remove(struct acpi_device *device) @@ -442,7 +444,6 @@ static int rbtn_remove(struct acpi_device *device) } rbtn_acquire(device, false); - device->driver_data = NULL; return 0; } -- cgit 1.4.1 From 0dafc849b9436a16bd2398a6989182853cc12946 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Tue, 13 Jun 2023 15:25:04 +0530 Subject: perf tool x86: Consolidate is_amd check into single function [ Upstream commit 0cd1ca4650c9cf5f318110f67d39cbebae3693b3 ] There are multiple places where x86 specific code determines AMD vs Intel arch and acts based on that. Consolidate those checks into a single function. Signed-off-by: Ravi Bangoria Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Ali Saidi Cc: Ananth Narayan Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Link: https://lore.kernel.org/r/20230613095506.547-3-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo Stable-dep-of: 99d4850062a8 ("perf tool x86: Fix perf_env memory leak") Signed-off-by: Sasha Levin --- tools/perf/arch/x86/util/Build | 1 + tools/perf/arch/x86/util/env.c | 19 +++++++++++++++++++ tools/perf/arch/x86/util/env.h | 7 +++++++ tools/perf/arch/x86/util/evsel.c | 16 ++-------------- tools/perf/arch/x86/util/mem-events.c | 19 ++----------------- 5 files changed, 31 insertions(+), 31 deletions(-) create mode 100644 tools/perf/arch/x86/util/env.c create mode 100644 tools/perf/arch/x86/util/env.h diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build index dbeb04cb336e..93c3ee5f9543 100644 --- a/tools/perf/arch/x86/util/Build +++ b/tools/perf/arch/x86/util/Build @@ -10,6 +10,7 @@ perf-y += evlist.o perf-y += mem-events.o perf-y += evsel.o perf-y += iostat.o +perf-y += env.o perf-$(CONFIG_DWARF) += dwarf-regs.o perf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o diff --git a/tools/perf/arch/x86/util/env.c b/tools/perf/arch/x86/util/env.c new file mode 100644 index 000000000000..33b87f8ac1cc --- /dev/null +++ b/tools/perf/arch/x86/util/env.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "linux/string.h" +#include "util/env.h" +#include "env.h" + +bool x86__is_amd_cpu(void) +{ + struct perf_env env = { .total_mem = 0, }; + static int is_amd; /* 0: Uninitialized, 1: Yes, -1: No */ + + if (is_amd) + goto ret; + + perf_env__cpuid(&env); + is_amd = env.cpuid && strstarts(env.cpuid, "AuthenticAMD") ? 1 : -1; + +ret: + return is_amd >= 1 ? true : false; +} diff --git a/tools/perf/arch/x86/util/env.h b/tools/perf/arch/x86/util/env.h new file mode 100644 index 000000000000..d78f080b6b3f --- /dev/null +++ b/tools/perf/arch/x86/util/env.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _X86_ENV_H +#define _X86_ENV_H + +bool x86__is_amd_cpu(void); + +#endif /* _X86_ENV_H */ diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index ea3972d785d1..d72390cdf391 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -7,6 +7,7 @@ #include "linux/string.h" #include "evsel.h" #include "util/debug.h" +#include "env.h" #define IBS_FETCH_L3MISSONLY (1ULL << 59) #define IBS_OP_L3MISSONLY (1ULL << 16) @@ -97,23 +98,10 @@ void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr) { struct perf_pmu *evsel_pmu, *ibs_fetch_pmu, *ibs_op_pmu; static int warned_once; - /* 0: Uninitialized, 1: Yes, -1: No */ - static int is_amd; - if (warned_once || is_amd == -1) + if (warned_once || !x86__is_amd_cpu()) return; - if (!is_amd) { - struct perf_env *env = evsel__env(evsel); - - if (!perf_env__cpuid(env) || !env->cpuid || - !strstarts(env->cpuid, "AuthenticAMD")) { - is_amd = -1; - return; - } - is_amd = 1; - } - evsel_pmu = evsel__find_pmu(evsel); if (!evsel_pmu) return; diff --git a/tools/perf/arch/x86/util/mem-events.c b/tools/perf/arch/x86/util/mem-events.c index f683ac702247..efc0fae9ed0a 100644 --- a/tools/perf/arch/x86/util/mem-events.c +++ b/tools/perf/arch/x86/util/mem-events.c @@ -4,6 +4,7 @@ #include "map_symbol.h" #include "mem-events.h" #include "linux/string.h" +#include "env.h" static char mem_loads_name[100]; static bool mem_loads_name__init; @@ -26,28 +27,12 @@ static struct perf_mem_event perf_mem_events_amd[PERF_MEM_EVENTS__MAX] = { E("mem-ldst", "ibs_op//", "ibs_op"), }; -static int perf_mem_is_amd_cpu(void) -{ - struct perf_env env = { .total_mem = 0, }; - - perf_env__cpuid(&env); - if (env.cpuid && strstarts(env.cpuid, "AuthenticAMD")) - return 1; - return -1; -} - struct perf_mem_event *perf_mem_events__ptr(int i) { - /* 0: Uninitialized, 1: Yes, -1: No */ - static int is_amd; - if (i >= PERF_MEM_EVENTS__MAX) return NULL; - if (!is_amd) - is_amd = perf_mem_is_amd_cpu(); - - if (is_amd == 1) + if (x86__is_amd_cpu()) return &perf_mem_events_amd[i]; return &perf_mem_events_intel[i]; -- cgit 1.4.1 From 75d65c1cc439606ada882755fd205d13c2c7907d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 13 Jun 2023 16:54:16 -0700 Subject: perf tool x86: Fix perf_env memory leak [ Upstream commit 99d4850062a84564f36923764bb93935ef2ed108 ] Found by leak sanitizer: ``` ==1632594==ERROR: LeakSanitizer: detected memory leaks Direct leak of 21 byte(s) in 1 object(s) allocated from: #0 0x7f2953a7077b in __interceptor_strdup ../../../../src/libsanitizer/asan/asan_interceptors.cpp:439 #1 0x556701d6fbbf in perf_env__read_cpuid util/env.c:369 #2 0x556701d70589 in perf_env__cpuid util/env.c:465 #3 0x55670204bba2 in x86__is_amd_cpu arch/x86/util/env.c:14 #4 0x5567020487a2 in arch__post_evsel_config arch/x86/util/evsel.c:83 #5 0x556701d8f78b in evsel__config util/evsel.c:1366 #6 0x556701ef5872 in evlist__config util/record.c:108 #7 0x556701cd6bcd in test__PERF_RECORD tests/perf-record.c:112 #8 0x556701cacd07 in run_test tests/builtin-test.c:236 #9 0x556701cacfac in test_and_print tests/builtin-test.c:265 #10 0x556701cadddb in __cmd_test tests/builtin-test.c:402 #11 0x556701caf2aa in cmd_test tests/builtin-test.c:559 #12 0x556701d3b557 in run_builtin tools/perf/perf.c:323 #13 0x556701d3bac8 in handle_internal_command tools/perf/perf.c:377 #14 0x556701d3be90 in run_argv tools/perf/perf.c:421 #15 0x556701d3c3f8 in main tools/perf/perf.c:537 #16 0x7f2952a46189 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 SUMMARY: AddressSanitizer: 21 byte(s) leaked in 1 allocation(s). ``` Fixes: f7b58cbdb3ff36eb ("perf mem/c2c: Add load store event mappings for AMD") Signed-off-by: Ian Rogers Acked-by: Ravi Bangoria Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Link: https://lore.kernel.org/r/20230613235416.1650755-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/arch/x86/util/env.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/util/env.c b/tools/perf/arch/x86/util/env.c index 33b87f8ac1cc..3e537ffb1353 100644 --- a/tools/perf/arch/x86/util/env.c +++ b/tools/perf/arch/x86/util/env.c @@ -13,7 +13,7 @@ bool x86__is_amd_cpu(void) perf_env__cpuid(&env); is_amd = env.cpuid && strstarts(env.cpuid, "AuthenticAMD") ? 1 : -1; - + perf_env__exit(&env); ret: return is_amd >= 1 ? true : false; } -- cgit 1.4.1 From 4e82f92c349ea603736ade1e814861c0182a55ad Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 7 Jun 2023 20:10:24 +1000 Subject: powerpc/64s: Fix VAS mm use after free [ Upstream commit b4bda59b47879cce38a6ec5a01cd3cac702b5331 ] The refcount on mm is dropped before the coprocessor is detached. Reported-by: Sachin Sant Fixes: 7bc6f71bdff5f ("powerpc/vas: Define and use common vas_window struct") Fixes: b22f2d88e435c ("powerpc/pseries/vas: Integrate API with open/close windows") Signed-off-by: Nicholas Piggin Tested-by: Sachin Sant Signed-off-by: Michael Ellerman Link: https://msgid.link/20230607101024.14559-1-npiggin@gmail.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/powernv/vas-window.c | 2 +- arch/powerpc/platforms/pseries/vas.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/powernv/vas-window.c b/arch/powerpc/platforms/powernv/vas-window.c index 0072682531d8..b664838008c1 100644 --- a/arch/powerpc/platforms/powernv/vas-window.c +++ b/arch/powerpc/platforms/powernv/vas-window.c @@ -1310,8 +1310,8 @@ int vas_win_close(struct vas_window *vwin) /* if send window, drop reference to matching receive window */ if (window->tx_win) { if (window->user_win) { - put_vas_user_win_ref(&vwin->task_ref); mm_context_remove_vas_window(vwin->task_ref.mm); + put_vas_user_win_ref(&vwin->task_ref); } put_rx_win(window->rxwin); } diff --git a/arch/powerpc/platforms/pseries/vas.c b/arch/powerpc/platforms/pseries/vas.c index 94c023bb13e0..880b962afc05 100644 --- a/arch/powerpc/platforms/pseries/vas.c +++ b/arch/powerpc/platforms/pseries/vas.c @@ -507,8 +507,8 @@ static int vas_deallocate_window(struct vas_window *vwin) vascaps[win->win_type].nr_open_windows--; mutex_unlock(&vas_pseries_mutex); - put_vas_user_win_ref(&vwin->task_ref); mm_context_remove_vas_window(vwin->task_ref.mm); + put_vas_user_win_ref(&vwin->task_ref); kfree(win); return 0; -- cgit 1.4.1 From 35404a47ba77607d33802cabe9f049e4fcb0e4ba Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 15 Jun 2023 13:53:32 +0300 Subject: pinctrl: microchip-sgpio: check return value of devm_kasprintf() [ Upstream commit 310cd4c206cd04696ccbfd1927b5ab6973e8cc8e ] devm_kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: 7e5ea974e61c ("pinctrl: pinctrl-microchip-sgpio: Add pinctrl driver for Microsemi Serial GPIO") Signed-off-by: Claudiu Beznea Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230615105333.585304-3-claudiu.beznea@microchip.com Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/pinctrl-microchip-sgpio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pinctrl/pinctrl-microchip-sgpio.c b/drivers/pinctrl/pinctrl-microchip-sgpio.c index af27b72c8958..96a20ce94f5e 100644 --- a/drivers/pinctrl/pinctrl-microchip-sgpio.c +++ b/drivers/pinctrl/pinctrl-microchip-sgpio.c @@ -816,6 +816,9 @@ static int microchip_sgpio_register_bank(struct device *dev, pctl_desc->name = devm_kasprintf(dev, GFP_KERNEL, "%s-%sput", dev_name(dev), bank->is_input ? "in" : "out"); + if (!pctl_desc->name) + return -ENOMEM; + pctl_desc->pctlops = &sgpio_pctl_ops; pctl_desc->pmxops = &sgpio_pmx_ops; pctl_desc->confops = &sgpio_confops; -- cgit 1.4.1 From 0af388fce352ed2ab383fd5d1a08db551ca15c38 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 15 Jun 2023 13:53:33 +0300 Subject: pinctrl: at91-pio4: check return value of devm_kasprintf() [ Upstream commit f6fd5d4ff8ca0b24cee1af4130bcb1fa96b61aa0 ] devm_kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: 776180848b57 ("pinctrl: introduce driver for Atmel PIO4 controller") Depends-on: 1c4e5c470a56 ("pinctrl: at91: use devm_kasprintf() to avoid potential leaks") Depends-on: 5a8f9cf269e8 ("pinctrl: at91-pio4: use proper format specifier for unsigned int") Signed-off-by: Claudiu Beznea Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230615105333.585304-4-claudiu.beznea@microchip.com Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/pinctrl-at91-pio4.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c index 0b7cc6f063e0..f71c6457e350 100644 --- a/drivers/pinctrl/pinctrl-at91-pio4.c +++ b/drivers/pinctrl/pinctrl-at91-pio4.c @@ -1122,6 +1122,8 @@ static int atmel_pinctrl_probe(struct platform_device *pdev) /* Pin naming convention: P(bank_name)(bank_pin_number). */ pin_desc[i].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "P%c%d", bank + 'A', line); + if (!pin_desc[i].name) + return -ENOMEM; group->name = group_names[i] = pin_desc[i].name; group->pin = pin_desc[i].number; -- cgit 1.4.1 From d9a1aaea856002cb58dfb7c8d8770400fa1a0299 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 8 Jun 2023 10:58:49 +0100 Subject: powerpc/powernv/sriov: perform null check on iov before dereferencing iov [ Upstream commit f4f913c980bc6abe0ccfe88fe3909c125afe4a2d ] Currently pointer iov is being dereferenced before the null check of iov which can lead to null pointer dereference errors. Fix this by moving the iov null check before the dereferencing. Detected using cppcheck static analysis: linux/arch/powerpc/platforms/powernv/pci-sriov.c:597:12: warning: Either the condition '!iov' is redundant or there is possible null pointer dereference: iov. [nullPointerRedundantCheck] num_vfs = iov->num_vfs; ^ Fixes: 052da31d45fc ("powerpc/powernv/sriov: De-indent setup and teardown") Signed-off-by: Colin Ian King Signed-off-by: Michael Ellerman Link: https://msgid.link/20230608095849.1147969-1-colin.i.king@gmail.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/powernv/pci-sriov.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 7195133b26bb..59882da3e742 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -594,12 +594,12 @@ static void pnv_pci_sriov_disable(struct pci_dev *pdev) struct pnv_iov_data *iov; iov = pnv_iov_get(pdev); - num_vfs = iov->num_vfs; - base_pe = iov->vf_pe_arr[0].pe_number; - if (WARN_ON(!iov)) return; + num_vfs = iov->num_vfs; + base_pe = iov->vf_pe_arr[0].pe_number; + /* Release VF PEs */ pnv_ioda_release_vf_PE(pdev); -- cgit 1.4.1 From 4cff1be1cbf69365bd5c00cf3c065262aa35c89f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 27 Nov 2022 22:49:31 +1000 Subject: powerpc: simplify ppc_save_regs [ Upstream commit 37195b820d32c23bdefce3f460ed7de48a57e5e4 ] Adjust the pt_regs pointer so the interrupt frame offsets can be used to save registers. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20221127124942.1665522-7-npiggin@gmail.com Stable-dep-of: b684c09f09e7 ("powerpc: update ppc_save_regs to save current r1 in pt_regs") Signed-off-by: Sasha Levin --- arch/powerpc/kernel/ppc_save_regs.S | 57 ++++++++++--------------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/arch/powerpc/kernel/ppc_save_regs.S b/arch/powerpc/kernel/ppc_save_regs.S index 2d4d21bb46a9..6e86f3bf4673 100644 --- a/arch/powerpc/kernel/ppc_save_regs.S +++ b/arch/powerpc/kernel/ppc_save_regs.S @@ -21,60 +21,33 @@ * different ABIs, though). */ _GLOBAL(ppc_save_regs) - PPC_STL r0,0*SZL(r3) + /* This allows stack frame accessor macros and offsets to be used */ + subi r3,r3,STACK_FRAME_OVERHEAD + PPC_STL r0,GPR0(r3) #ifdef CONFIG_PPC32 - stmw r2, 2*SZL(r3) + stmw r2,GPR2(r3) #else - PPC_STL r2,2*SZL(r3) - PPC_STL r3,3*SZL(r3) - PPC_STL r4,4*SZL(r3) - PPC_STL r5,5*SZL(r3) - PPC_STL r6,6*SZL(r3) - PPC_STL r7,7*SZL(r3) - PPC_STL r8,8*SZL(r3) - PPC_STL r9,9*SZL(r3) - PPC_STL r10,10*SZL(r3) - PPC_STL r11,11*SZL(r3) - PPC_STL r12,12*SZL(r3) - PPC_STL r13,13*SZL(r3) - PPC_STL r14,14*SZL(r3) - PPC_STL r15,15*SZL(r3) - PPC_STL r16,16*SZL(r3) - PPC_STL r17,17*SZL(r3) - PPC_STL r18,18*SZL(r3) - PPC_STL r19,19*SZL(r3) - PPC_STL r20,20*SZL(r3) - PPC_STL r21,21*SZL(r3) - PPC_STL r22,22*SZL(r3) - PPC_STL r23,23*SZL(r3) - PPC_STL r24,24*SZL(r3) - PPC_STL r25,25*SZL(r3) - PPC_STL r26,26*SZL(r3) - PPC_STL r27,27*SZL(r3) - PPC_STL r28,28*SZL(r3) - PPC_STL r29,29*SZL(r3) - PPC_STL r30,30*SZL(r3) - PPC_STL r31,31*SZL(r3) + SAVE_GPRS(2, 31, r3) lbz r0,PACAIRQSOFTMASK(r13) - PPC_STL r0,SOFTE-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,SOFTE(r3) #endif /* go up one stack frame for SP */ PPC_LL r4,0(r1) - PPC_STL r4,1*SZL(r3) + PPC_STL r4,GPR1(r3) /* get caller's LR */ PPC_LL r0,LRSAVE(r4) - PPC_STL r0,_LINK-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,_LINK(r3) mflr r0 - PPC_STL r0,_NIP-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,_NIP(r3) mfmsr r0 - PPC_STL r0,_MSR-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,_MSR(r3) mfctr r0 - PPC_STL r0,_CTR-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,_CTR(r3) mfxer r0 - PPC_STL r0,_XER-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,_XER(r3) mfcr r0 - PPC_STL r0,_CCR-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,_CCR(r3) li r0,0 - PPC_STL r0,_TRAP-STACK_FRAME_OVERHEAD(r3) - PPC_STL r0,ORIG_GPR3-STACK_FRAME_OVERHEAD(r3) + PPC_STL r0,_TRAP(r3) + PPC_STL r0,ORIG_GPR3(r3) blr -- cgit 1.4.1 From 865d128cab0ded06c41b06cfdc191ef3d121a95f Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 15 Jun 2023 14:40:47 +0530 Subject: powerpc: update ppc_save_regs to save current r1 in pt_regs [ Upstream commit b684c09f09e7a6af3794d4233ef785819e72db79 ] ppc_save_regs() skips one stack frame while saving the CPU register states. Instead of saving current R1, it pulls the previous stack frame pointer. When vmcores caused by direct panic call (such as `echo c > /proc/sysrq-trigger`), are debugged with gdb, gdb fails to show the backtrace correctly. On further analysis, it was found that it was because of mismatch between r1 and NIP. GDB uses NIP to get current function symbol and uses corresponding debug info of that function to unwind previous frames, but due to the mismatching r1 and NIP, the unwinding does not work, and it fails to unwind to the 2nd frame and hence does not show the backtrace. GDB backtrace with vmcore of kernel without this patch: --------- (gdb) bt #0 0xc0000000002a53e8 in crash_setup_regs (oldregs=, newregs=0xc000000004f8f8d8) at ./arch/powerpc/include/asm/kexec.h:69 #1 __crash_kexec (regs=) at kernel/kexec_core.c:974 #2 0x0000000000000063 in ?? () #3 0xc000000003579320 in ?? () --------- Further analysis revealed that the mismatch occurred because "ppc_save_regs" was saving the previous stack's SP instead of the current r1. This patch fixes this by storing current r1 in the saved pt_regs. GDB backtrace with vmcore of patched kernel: -------- (gdb) bt #0 0xc0000000002a53e8 in crash_setup_regs (oldregs=0x0, newregs=0xc00000000670b8d8) at ./arch/powerpc/include/asm/kexec.h:69 #1 __crash_kexec (regs=regs@entry=0x0) at kernel/kexec_core.c:974 #2 0xc000000000168918 in panic (fmt=fmt@entry=0xc000000001654a60 "sysrq triggered crash\n") at kernel/panic.c:358 #3 0xc000000000b735f8 in sysrq_handle_crash (key=) at drivers/tty/sysrq.c:155 #4 0xc000000000b742cc in __handle_sysrq (key=key@entry=99, check_mask=check_mask@entry=false) at drivers/tty/sysrq.c:602 #5 0xc000000000b7506c in write_sysrq_trigger (file=, buf=, count=2, ppos=) at drivers/tty/sysrq.c:1163 #6 0xc00000000069a7bc in pde_write (ppos=, count=, buf=, file=, pde=0xc00000000362cb40) at fs/proc/inode.c:340 #7 proc_reg_write (file=, buf=, count=, ppos=) at fs/proc/inode.c:352 #8 0xc0000000005b3bbc in vfs_write (file=file@entry=0xc000000006aa6b00, buf=buf@entry=0x61f498b4f60 , count=count@entry=2, pos=pos@entry=0xc00000000670bda0) at fs/read_write.c:582 #9 0xc0000000005b4264 in ksys_write (fd=, buf=0x61f498b4f60 , count=2) at fs/read_write.c:637 #10 0xc00000000002ea2c in system_call_exception (regs=0xc00000000670be80, r0=) at arch/powerpc/kernel/syscall.c:171 #11 0xc00000000000c270 in system_call_vectored_common () at arch/powerpc/kernel/interrupt_64.S:192 -------- Nick adds: So this now saves regs as though it was an interrupt taken in the caller, at the instruction after the call to ppc_save_regs, whereas previously the NIP was there, but R1 came from the caller's caller and that mismatch is what causes gdb's dwarf unwinder to go haywire. Signed-off-by: Aditya Gupta Fixes: d16a58f8854b1 ("powerpc: Improve ppc_save_regs()") Reivewed-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://msgid.link/20230615091047.90433-1-adityag@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/kernel/ppc_save_regs.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/ppc_save_regs.S b/arch/powerpc/kernel/ppc_save_regs.S index 6e86f3bf4673..235ae2428451 100644 --- a/arch/powerpc/kernel/ppc_save_regs.S +++ b/arch/powerpc/kernel/ppc_save_regs.S @@ -31,10 +31,10 @@ _GLOBAL(ppc_save_regs) lbz r0,PACAIRQSOFTMASK(r13) PPC_STL r0,SOFTE(r3) #endif - /* go up one stack frame for SP */ - PPC_LL r4,0(r1) - PPC_STL r4,GPR1(r3) + /* store current SP */ + PPC_STL r1,GPR1(r3) /* get caller's LR */ + PPC_LL r4,0(r1) PPC_LL r0,LRSAVE(r4) PPC_STL r0,_LINK(r3) mflr r0 -- cgit 1.4.1 From db962c7a711c3393a80a18219960cd54fb33c53d Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Mar 2023 13:41:00 +0530 Subject: PCI: qcom: Remove PCIE20_ prefix from register definitions [ Upstream commit 39171b33f6523f28c1c1256427e5f50c74b69639 ] The PCIE part is redundant and 20 doesn't represent anything across the SoCs supported now. So let's get rid of the prefix. This involves adding the IP version suffix to one definition of PARF_SLV_ADDR_SPACE_SIZE that defines offset specific to that version. The other definition is generic for the rest of the versions. Also, the register PCIE20_LNK_CONTROL2_LINK_STATUS2 is not used anywhere, hence removed. Link: https://lore.kernel.org/r/20230316081117.14288-3-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Stable-dep-of: 60f0072d7fb7 ("PCI: qcom: Use DWC helpers for modifying the read-only DBI registers") Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 184 ++++++++++++++++----------------- 1 file changed, 91 insertions(+), 93 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index dbe6df0cb611..b587957211c3 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -31,7 +31,7 @@ #include "../../pci.h" #include "pcie-designware.h" -#define PCIE20_PARF_SYS_CTRL 0x00 +#define PARF_SYS_CTRL 0x00 #define MST_WAKEUP_EN BIT(13) #define SLV_WAKEUP_EN BIT(12) #define MSTR_ACLK_CGC_DIS BIT(10) @@ -41,39 +41,39 @@ #define L23_CLK_RMV_DIS BIT(2) #define L1_CLK_RMV_DIS BIT(1) -#define PCIE20_PARF_PM_CTRL 0x20 +#define PARF_PM_CTRL 0x20 #define REQ_NOT_ENTR_L1 BIT(5) -#define PCIE20_PARF_PHY_CTRL 0x40 +#define PARF_PHY_CTRL 0x40 #define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK GENMASK(20, 16) #define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) ((x) << 16) -#define PCIE20_PARF_PHY_REFCLK 0x4C +#define PARF_PHY_REFCLK 0x4C #define PHY_REFCLK_SSP_EN BIT(16) #define PHY_REFCLK_USE_PAD BIT(12) -#define PCIE20_PARF_DBI_BASE_ADDR 0x168 -#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C -#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 +#define PARF_DBI_BASE_ADDR 0x168 +#define PARF_SLV_ADDR_SPACE_SIZE_2_3_3 0x16C /* Register offset specific to IP rev 2.3.3 */ +#define PARF_MHI_CLOCK_RESET_CTRL 0x174 #define AHB_CLK_EN BIT(0) #define MSTR_AXI_CLK_EN BIT(1) #define BYPASS BIT(4) -#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 -#define PCIE20_PARF_LTSSM 0x1B0 -#define PCIE20_PARF_SID_OFFSET 0x234 -#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C -#define PCIE20_PARF_DEVICE_TYPE 0x1000 -#define PCIE20_PARF_BDF_TO_SID_TABLE_N 0x2000 +#define PARF_AXI_MSTR_WR_ADDR_HALT 0x178 +#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 +#define PARF_LTSSM 0x1B0 +#define PARF_SID_OFFSET 0x234 +#define PARF_BDF_TRANSLATE_CFG 0x24C +#define PARF_DEVICE_TYPE 0x1000 +#define PARF_BDF_TO_SID_TABLE_N 0x2000 -#define PCIE20_ELBI_SYS_CTRL 0x04 -#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) +#define ELBI_SYS_CTRL 0x04 +#define ELBI_SYS_CTRL_LT_ENABLE BIT(0) -#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 +#define AXI_MSTR_RESP_COMP_CTRL0 0x818 #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_4K 0x5 -#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c +#define AXI_MSTR_RESP_COMP_CTRL1 0x81c #define CFG_BRIDGE_SB_INIT BIT(0) #define PCIE_CAP_SLOT_POWER_LIMIT_VAL FIELD_PREP(PCI_EXP_SLTCAP_SPLV, \ @@ -91,30 +91,28 @@ PCIE_CAP_SLOT_POWER_LIMIT_VAL | \ PCIE_CAP_SLOT_POWER_LIMIT_SCALE) -#define PCIE20_PARF_Q2A_FLUSH 0x1AC +#define PARF_Q2A_FLUSH 0x1AC -#define PCIE20_MISC_CONTROL_1_REG 0x8BC +#define MISC_CONTROL_1_REG 0x8BC #define DBI_RO_WR_EN 1 #define PERST_DELAY_US 1000 /* PARF registers */ -#define PCIE20_PARF_PCS_DEEMPH 0x34 +#define PARF_PCS_DEEMPH 0x34 #define PCS_DEEMPH_TX_DEEMPH_GEN1(x) ((x) << 16) #define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) ((x) << 8) #define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) ((x) << 0) -#define PCIE20_PARF_PCS_SWING 0x38 +#define PARF_PCS_SWING 0x38 #define PCS_SWING_TX_SWING_FULL(x) ((x) << 8) #define PCS_SWING_TX_SWING_LOW(x) ((x) << 0) -#define PCIE20_PARF_CONFIG_BITS 0x50 +#define PARF_CONFIG_BITS 0x50 #define PHY_RX0_EQ(x) ((x) << 24) -#define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 +#define PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 -#define PCIE20_LNK_CONTROL2_LINK_STATUS2 0xa0 - #define DEVICE_TYPE_RC 0x4 #define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 @@ -258,9 +256,9 @@ static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) u32 val; /* enable link training */ - val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL); - val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE; - writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); + val = readl(pcie->elbi + ELBI_SYS_CTRL); + val |= ELBI_SYS_CTRL_LT_ENABLE; + writel(val, pcie->elbi + ELBI_SYS_CTRL); } static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) @@ -330,7 +328,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) reset_control_assert(res->ext_reset); reset_control_assert(res->phy_reset); - writel(1, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(1, pcie->parf + PARF_PHY_CTRL); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); } @@ -420,9 +418,9 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) int ret; /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(val, pcie->parf + PARF_PHY_CTRL); ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); if (ret) @@ -433,37 +431,37 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) writel(PCS_DEEMPH_TX_DEEMPH_GEN1(24) | PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(24) | PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(34), - pcie->parf + PCIE20_PARF_PCS_DEEMPH); + pcie->parf + PARF_PCS_DEEMPH); writel(PCS_SWING_TX_SWING_FULL(120) | PCS_SWING_TX_SWING_LOW(120), - pcie->parf + PCIE20_PARF_PCS_SWING); - writel(PHY_RX0_EQ(4), pcie->parf + PCIE20_PARF_CONFIG_BITS); + pcie->parf + PARF_PCS_SWING); + writel(PHY_RX0_EQ(4), pcie->parf + PARF_CONFIG_BITS); } if (of_device_is_compatible(node, "qcom,pcie-ipq8064")) { /* set TX termination offset */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK; val |= PHY_CTRL_PHY_TX0_TERM_OFFSET(7); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(val, pcie->parf + PARF_PHY_CTRL); } /* enable external reference clock */ - val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); + val = readl(pcie->parf + PARF_PHY_REFCLK); /* USE_PAD is required only for ipq806x */ if (!of_device_is_compatible(node, "qcom,pcie-apq8064")) val &= ~PHY_REFCLK_USE_PAD; val |= PHY_REFCLK_SSP_EN; - writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); + writel(val, pcie->parf + PARF_PHY_REFCLK); /* wait for clock acquisition */ usleep_range(1000, 1500); /* Set the Max TLP size to 2K, instead of using default of 4K */ writel(CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K, - pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); + pci->dbi_base + AXI_MSTR_RESP_COMP_CTRL0); writel(CFG_BRIDGE_SB_INIT, - pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); + pci->dbi_base + AXI_MSTR_RESP_COMP_CTRL1); return 0; } @@ -571,13 +569,13 @@ err_res: static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie) { /* change DBI base address */ - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + writel(0, pcie->parf + PARF_DBI_BASE_ADDR); if (IS_ENABLED(CONFIG_PCI_MSI)) { - u32 val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + u32 val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT); val |= BIT(31); - writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT); } return 0; @@ -588,9 +586,9 @@ static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie) u32 val; /* enable link training */ - val = readl(pcie->parf + PCIE20_PARF_LTSSM); + val = readl(pcie->parf + PARF_LTSSM); val |= BIT(8); - writel(val, pcie->parf + PCIE20_PARF_LTSSM); + writel(val, pcie->parf + PARF_LTSSM); } static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) @@ -695,25 +693,25 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) u32 val; /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(val, pcie->parf + PARF_PHY_CTRL); /* change DBI base address */ - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + writel(0, pcie->parf + PARF_DBI_BASE_ADDR); /* MAC PHY_POWERDOWN MUX DISABLE */ - val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val = readl(pcie->parf + PARF_SYS_CTRL); val &= ~BIT(29); - writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + writel(val, pcie->parf + PARF_SYS_CTRL); - val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val = readl(pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); val |= BIT(4); - writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + writel(val, pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); - val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); val |= BIT(31); - writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); return 0; } @@ -974,25 +972,25 @@ static int qcom_pcie_post_init_2_4_0(struct qcom_pcie *pcie) u32 val; /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(val, pcie->parf + PARF_PHY_CTRL); /* change DBI base address */ - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + writel(0, pcie->parf + PARF_DBI_BASE_ADDR); /* MAC PHY_POWERDOWN MUX DISABLE */ - val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val = readl(pcie->parf + PARF_SYS_CTRL); val &= ~BIT(29); - writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + writel(val, pcie->parf + PARF_SYS_CTRL); - val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val = readl(pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); val |= BIT(4); - writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + writel(val, pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); - val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); val |= BIT(31); - writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); return 0; } @@ -1137,22 +1135,22 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) u32 val; writel(SLV_ADDR_SPACE_SZ, - pcie->parf + PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE); + pcie->parf + PARF_SLV_ADDR_SPACE_SIZE_2_3_3); - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(val, pcie->parf + PARF_PHY_CTRL); - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + writel(0, pcie->parf + PARF_DBI_BASE_ADDR); writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS | SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS | AUX_PWR_DET | L23_CLK_RMV_DIS | L1_CLK_RMV_DIS, - pcie->parf + PCIE20_PARF_SYS_CTRL); - writel(0, pcie->parf + PCIE20_PARF_Q2A_FLUSH); + pcie->parf + PARF_SYS_CTRL); + writel(0, pcie->parf + PARF_Q2A_FLUSH); writel(PCI_COMMAND_MASTER, pci->dbi_base + PCI_COMMAND); - writel(DBI_RO_WR_EN, pci->dbi_base + PCIE20_MISC_CONTROL_1_REG); + writel(DBI_RO_WR_EN, pci->dbi_base + MISC_CONTROL_1_REG); writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); @@ -1252,33 +1250,33 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie) usleep_range(1000, 1500); /* configure PCIe to RC mode */ - writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE); + writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE); /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(val, pcie->parf + PARF_PHY_CTRL); /* change DBI base address */ - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + writel(0, pcie->parf + PARF_DBI_BASE_ADDR); /* MAC PHY_POWERDOWN MUX DISABLE */ - val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val = readl(pcie->parf + PARF_SYS_CTRL); val &= ~BIT(29); - writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + writel(val, pcie->parf + PARF_SYS_CTRL); - val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val = readl(pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); val |= BIT(4); - writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + writel(val, pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); /* Enable L1 and L1SS */ - val = readl(pcie->parf + PCIE20_PARF_PM_CTRL); + val = readl(pcie->parf + PARF_PM_CTRL); val &= ~REQ_NOT_ENTR_L1; - writel(val, pcie->parf + PCIE20_PARF_PM_CTRL); + writel(val, pcie->parf + PARF_PM_CTRL); - val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); val |= BIT(31); - writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); return 0; err_disable_clocks: @@ -1366,17 +1364,17 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) int i; writel(SLV_ADDR_SPACE_SZ, - pcie->parf + PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE); + pcie->parf + PARF_SLV_ADDR_SPACE_SIZE); - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + writel(val, pcie->parf + PARF_PHY_CTRL); - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + writel(0, pcie->parf + PARF_DBI_BASE_ADDR); - writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE); + writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE); writel(BYPASS | MSTR_AXI_CLK_EN | AHB_CLK_EN, - pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); writel(GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS | GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL, pci->dbi_base + GEN3_RELATED_OFF); @@ -1384,9 +1382,9 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS | SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS | AUX_PWR_DET | L23_CLK_RMV_DIS | L1_CLK_RMV_DIS, - pcie->parf + PCIE20_PARF_SYS_CTRL); + pcie->parf + PARF_SYS_CTRL); - writel(0, pcie->parf + PCIE20_PARF_Q2A_FLUSH); + writel(0, pcie->parf + PARF_Q2A_FLUSH); dw_pcie_dbi_ro_wr_en(pci); writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); @@ -1399,7 +1397,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) PCI_EXP_DEVCTL2); for (i = 0; i < 256; i++) - writel(0, pcie->parf + PCIE20_PARF_BDF_TO_SID_TABLE_N + (4 * i)); + writel(0, pcie->parf + PARF_BDF_TO_SID_TABLE_N + (4 * i)); return 0; } @@ -1421,7 +1419,7 @@ static int qcom_pcie_config_sid_sm8250(struct qcom_pcie *pcie) u32 smmu_sid; u32 smmu_sid_len; } *map; - void __iomem *bdf_to_sid_base = pcie->parf + PCIE20_PARF_BDF_TO_SID_TABLE_N; + void __iomem *bdf_to_sid_base = pcie->parf + PARF_BDF_TO_SID_TABLE_N; struct device *dev = pcie->pci->dev; u8 qcom_pcie_crc8_table[CRC8_TABLE_SIZE]; int i, nr_map, size = 0; -- cgit 1.4.1 From a350f1077711b54a51b46b7ddbbb262d6b43b0ef Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Mar 2023 13:41:01 +0530 Subject: PCI: qcom: Sort and group registers and bitfield definitions [ Upstream commit 769e49d87b15c302c9aadd87c7d114cfe7052320 ] Sorting the registers and their bit definitions will make it easier to add more definitions in the future and it also helps in maintenance. While at it, let's also group the registers and bit definitions separately as done in the pcie-qcom-ep driver. Link: https://lore.kernel.org/r/20230316081117.14288-4-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Stable-dep-of: 60f0072d7fb7 ("PCI: qcom: Use DWC helpers for modifying the read-only DBI registers") Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 108 +++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index b587957211c3..1c8200656aef 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -31,7 +31,36 @@ #include "../../pci.h" #include "pcie-designware.h" +/* PARF registers */ #define PARF_SYS_CTRL 0x00 +#define PARF_PM_CTRL 0x20 +#define PARF_PCS_DEEMPH 0x34 +#define PARF_PCS_SWING 0x38 +#define PARF_PHY_CTRL 0x40 +#define PARF_PHY_REFCLK 0x4C +#define PARF_CONFIG_BITS 0x50 +#define PARF_DBI_BASE_ADDR 0x168 +#define PARF_SLV_ADDR_SPACE_SIZE_2_3_3 0x16C /* Register offset specific to IP ver 2.3.3 */ +#define PARF_MHI_CLOCK_RESET_CTRL 0x174 +#define PARF_AXI_MSTR_WR_ADDR_HALT 0x178 +#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 +#define PARF_Q2A_FLUSH 0x1AC +#define PARF_LTSSM 0x1B0 +#define PARF_SID_OFFSET 0x234 +#define PARF_BDF_TRANSLATE_CFG 0x24C +#define PARF_SLV_ADDR_SPACE_SIZE 0x358 +#define PARF_DEVICE_TYPE 0x1000 +#define PARF_BDF_TO_SID_TABLE_N 0x2000 + +/* ELBI registers */ +#define ELBI_SYS_CTRL 0x04 + +/* DBI registers */ +#define AXI_MSTR_RESP_COMP_CTRL0 0x818 +#define AXI_MSTR_RESP_COMP_CTRL1 0x81c +#define MISC_CONTROL_1_REG 0x8BC + +/* PARF_SYS_CTRL register fields */ #define MST_WAKEUP_EN BIT(13) #define SLV_WAKEUP_EN BIT(12) #define MSTR_ACLK_CGC_DIS BIT(10) @@ -41,45 +70,56 @@ #define L23_CLK_RMV_DIS BIT(2) #define L1_CLK_RMV_DIS BIT(1) -#define PARF_PM_CTRL 0x20 +/* PARF_PM_CTRL register fields */ #define REQ_NOT_ENTR_L1 BIT(5) -#define PARF_PHY_CTRL 0x40 +/* PARF_PCS_DEEMPH register fields */ +#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) ((x) << 16) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) ((x) << 8) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) ((x) << 0) + +/* PARF_PCS_SWING register fields */ +#define PCS_SWING_TX_SWING_FULL(x) ((x) << 8) +#define PCS_SWING_TX_SWING_LOW(x) ((x) << 0) + +/* PARF_PHY_CTRL register fields */ #define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK GENMASK(20, 16) #define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) ((x) << 16) -#define PARF_PHY_REFCLK 0x4C +/* PARF_PHY_REFCLK register fields */ #define PHY_REFCLK_SSP_EN BIT(16) #define PHY_REFCLK_USE_PAD BIT(12) -#define PARF_DBI_BASE_ADDR 0x168 -#define PARF_SLV_ADDR_SPACE_SIZE_2_3_3 0x16C /* Register offset specific to IP rev 2.3.3 */ -#define PARF_MHI_CLOCK_RESET_CTRL 0x174 +/* PARF_CONFIG_BITS register fields */ +#define PHY_RX0_EQ(x) ((x) << 24) + +/* PARF_SLV_ADDR_SPACE_SIZE register value */ +#define SLV_ADDR_SPACE_SZ 0x10000000 + +/* PARF_MHI_CLOCK_RESET_CTRL register fields */ #define AHB_CLK_EN BIT(0) #define MSTR_AXI_CLK_EN BIT(1) #define BYPASS BIT(4) -#define PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 -#define PARF_LTSSM 0x1B0 -#define PARF_SID_OFFSET 0x234 -#define PARF_BDF_TRANSLATE_CFG 0x24C -#define PARF_DEVICE_TYPE 0x1000 -#define PARF_BDF_TO_SID_TABLE_N 0x2000 +/* PARF_DEVICE_TYPE register fields */ +#define DEVICE_TYPE_RC 0x4 -#define ELBI_SYS_CTRL 0x04 +/* ELBI_SYS_CTRL register fields */ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) -#define AXI_MSTR_RESP_COMP_CTRL0 0x818 +/* AXI_MSTR_RESP_COMP_CTRL0 register fields */ #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_4K 0x5 -#define AXI_MSTR_RESP_COMP_CTRL1 0x81c + +/* AXI_MSTR_RESP_COMP_CTRL1 register fields */ #define CFG_BRIDGE_SB_INIT BIT(0) -#define PCIE_CAP_SLOT_POWER_LIMIT_VAL FIELD_PREP(PCI_EXP_SLTCAP_SPLV, \ - 250) -#define PCIE_CAP_SLOT_POWER_LIMIT_SCALE FIELD_PREP(PCI_EXP_SLTCAP_SPLS, \ - 1) +/* MISC_CONTROL_1_REG register fields */ +#define DBI_RO_WR_EN 1 + +/* PCI_EXP_SLTCAP register fields */ +#define PCIE_CAP_SLOT_POWER_LIMIT_VAL FIELD_PREP(PCI_EXP_SLTCAP_SPLV, 250) +#define PCIE_CAP_SLOT_POWER_LIMIT_SCALE FIELD_PREP(PCI_EXP_SLTCAP_SPLS, 1) #define PCIE_CAP_SLOT_VAL (PCI_EXP_SLTCAP_ABP | \ PCI_EXP_SLTCAP_PCP | \ PCI_EXP_SLTCAP_MRLSP | \ @@ -91,34 +131,12 @@ PCIE_CAP_SLOT_POWER_LIMIT_VAL | \ PCIE_CAP_SLOT_POWER_LIMIT_SCALE) -#define PARF_Q2A_FLUSH 0x1AC - -#define MISC_CONTROL_1_REG 0x8BC -#define DBI_RO_WR_EN 1 - #define PERST_DELAY_US 1000 -/* PARF registers */ -#define PARF_PCS_DEEMPH 0x34 -#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) ((x) << 16) -#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) ((x) << 8) -#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) ((x) << 0) - -#define PARF_PCS_SWING 0x38 -#define PCS_SWING_TX_SWING_FULL(x) ((x) << 8) -#define PCS_SWING_TX_SWING_LOW(x) ((x) << 0) - -#define PARF_CONFIG_BITS 0x50 -#define PHY_RX0_EQ(x) ((x) << 24) - -#define PARF_SLV_ADDR_SPACE_SIZE 0x358 -#define SLV_ADDR_SPACE_SZ 0x10000000 - -#define DEVICE_TYPE_RC 0x4 -#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 -#define QCOM_PCIE_2_1_0_MAX_CLOCKS 5 +#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 +#define QCOM_PCIE_2_1_0_MAX_CLOCKS 5 -#define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0)) +#define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0)) struct qcom_pcie_resources_2_1_0 { struct clk_bulk_data clks[QCOM_PCIE_2_1_0_MAX_CLOCKS]; -- cgit 1.4.1 From 8640e941fd399e8b0559258920fe87ce6a69c485 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Mar 2023 13:41:04 +0530 Subject: PCI: qcom: Use lower case for hex [ Upstream commit 94ebd232dbc84dfdfbf0c406137a8b2aa8b37a01 ] To maintain uniformity, let's use lower case for representing hexadecimal numbers. Link: https://lore.kernel.org/r/20230316081117.14288-7-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Stable-dep-of: 60f0072d7fb7 ("PCI: qcom: Use DWC helpers for modifying the read-only DBI registers") Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 1c8200656aef..e65f4bf50f92 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -37,17 +37,17 @@ #define PARF_PCS_DEEMPH 0x34 #define PARF_PCS_SWING 0x38 #define PARF_PHY_CTRL 0x40 -#define PARF_PHY_REFCLK 0x4C +#define PARF_PHY_REFCLK 0x4c #define PARF_CONFIG_BITS 0x50 #define PARF_DBI_BASE_ADDR 0x168 -#define PARF_SLV_ADDR_SPACE_SIZE_2_3_3 0x16C /* Register offset specific to IP ver 2.3.3 */ +#define PARF_SLV_ADDR_SPACE_SIZE_2_3_3 0x16c /* Register offset specific to IP ver 2.3.3 */ #define PARF_MHI_CLOCK_RESET_CTRL 0x174 #define PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 -#define PARF_Q2A_FLUSH 0x1AC -#define PARF_LTSSM 0x1B0 +#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 +#define PARF_Q2A_FLUSH 0x1ac +#define PARF_LTSSM 0x1b0 #define PARF_SID_OFFSET 0x234 -#define PARF_BDF_TRANSLATE_CFG 0x24C +#define PARF_BDF_TRANSLATE_CFG 0x24c #define PARF_SLV_ADDR_SPACE_SIZE 0x358 #define PARF_DEVICE_TYPE 0x1000 #define PARF_BDF_TO_SID_TABLE_N 0x2000 @@ -58,7 +58,7 @@ /* DBI registers */ #define AXI_MSTR_RESP_COMP_CTRL0 0x818 #define AXI_MSTR_RESP_COMP_CTRL1 0x81c -#define MISC_CONTROL_1_REG 0x8BC +#define MISC_CONTROL_1_REG 0x8bc /* PARF_SYS_CTRL register fields */ #define MST_WAKEUP_EN BIT(13) -- cgit 1.4.1 From 1cf0ecb0c74c24840b1cb2d7eabb03216bf857ea Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:01 +0530 Subject: PCI: qcom: Use DWC helpers for modifying the read-only DBI registers [ Upstream commit 60f0072d7fb7996b9a524ef0d152e21205473192 ] DWC core already exposes dw_pcie_dbi_ro_wr_{en/dis} helper APIs for enabling and disabling the write access to read only DBI registers. So let's use them instead of doing it manually. Also, the existing code doesn't disable the write access when it's done. This is also fixed now. Link: https://lore.kernel.org/r/20230619150408.8468-3-manivannan.sadhasivam@linaro.org Fixes: 5d76117f070d ("PCI: qcom: Add support for IPQ8074 PCIe controller") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index e65f4bf50f92..77f4dc244b3f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -58,7 +58,6 @@ /* DBI registers */ #define AXI_MSTR_RESP_COMP_CTRL0 0x818 #define AXI_MSTR_RESP_COMP_CTRL1 0x81c -#define MISC_CONTROL_1_REG 0x8bc /* PARF_SYS_CTRL register fields */ #define MST_WAKEUP_EN BIT(13) @@ -114,9 +113,6 @@ /* AXI_MSTR_RESP_COMP_CTRL1 register fields */ #define CFG_BRIDGE_SB_INIT BIT(0) -/* MISC_CONTROL_1_REG register fields */ -#define DBI_RO_WR_EN 1 - /* PCI_EXP_SLTCAP register fields */ #define PCIE_CAP_SLOT_POWER_LIMIT_VAL FIELD_PREP(PCI_EXP_SLTCAP_SPLV, 250) #define PCIE_CAP_SLOT_POWER_LIMIT_SCALE FIELD_PREP(PCI_EXP_SLTCAP_SPLS, 1) @@ -1168,7 +1164,9 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) writel(0, pcie->parf + PARF_Q2A_FLUSH); writel(PCI_COMMAND_MASTER, pci->dbi_base + PCI_COMMAND); - writel(DBI_RO_WR_EN, pci->dbi_base + MISC_CONTROL_1_REG); + + dw_pcie_dbi_ro_wr_en(pci); + writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); -- cgit 1.4.1 From d25166e1e9d489d9216c2f6432370cd82ca234f5 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:02 +0530 Subject: PCI: qcom: Disable write access to read only registers for IP v2.9.0 [ Upstream commit 200b8f85f2021362adcc8efb575652a2aa44c099 ] In the post init sequence of v2.9.0, write access to read only registers are not disabled after updating the registers. Fix it by disabling the access after register update. While at it, let's also add a newline after existing dw_pcie_dbi_ro_wr_en() guard function to align with rest of the driver. Link: https://lore.kernel.org/r/20230619150408.8468-4-manivannan.sadhasivam@linaro.org Fixes: 0cf7c2efe8ac ("PCI: qcom: Add IPQ60xx support") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dmitry Baryshkov Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 77f4dc244b3f..49905b2a9960 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1403,6 +1403,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) writel(0, pcie->parf + PARF_Q2A_FLUSH); dw_pcie_dbi_ro_wr_en(pci); + writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); @@ -1412,6 +1413,8 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) writel(PCI_EXP_DEVCTL2_COMP_TMOUT_DIS, pci->dbi_base + offset + PCI_EXP_DEVCTL2); + dw_pcie_dbi_ro_wr_dis(pci); + for (i = 0; i < 256; i++) writel(0, pcie->parf + PARF_BDF_TO_SID_TABLE_N + (4 * i)); -- cgit 1.4.1 From 526129937c4785f590665c3fc33ccb8a2be56612 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Sun, 23 Apr 2023 09:42:26 +0800 Subject: riscv: uprobes: Restore thread.bad_cause [ Upstream commit 58b1294dd1d65bb62f08dddbf418f954210c2057 ] thread.bad_cause is saved in arch_uprobe_pre_xol(), it should be restored in arch_uprobe_{post,abort}_xol() accordingly, otherwise the save operation is meaningless, this change is similar with x86 and powerpc. Signed-off-by: Tiezhu Yang Acked-by: Oleg Nesterov Reviewed-by: Guo Ren Fixes: 74784081aac8 ("riscv: Add uprobes supported") Link: https://lore.kernel.org/r/1682214146-3756-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Palmer Dabbelt Signed-off-by: Sasha Levin --- arch/riscv/kernel/probes/uprobes.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c index c976a21cd4bd..194f166b2cc4 100644 --- a/arch/riscv/kernel/probes/uprobes.c +++ b/arch/riscv/kernel/probes/uprobes.c @@ -67,6 +67,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) struct uprobe_task *utask = current->utask; WARN_ON_ONCE(current->thread.bad_cause != UPROBE_TRAP_NR); + current->thread.bad_cause = utask->autask.saved_cause; instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size); @@ -102,6 +103,7 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) { struct uprobe_task *utask = current->utask; + current->thread.bad_cause = utask->autask.saved_cause; /* * Task has received a fatal signal, so reset back to probbed * address. -- cgit 1.4.1 From ea356080c1cf1c1f153d39b07d4d4906454b4108 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Fri, 16 Jun 2023 16:38:13 +0530 Subject: powerpc/book3s64/mm: Fix DirectMap stats in /proc/meminfo [ Upstream commit 0da90af431abc3f497a38ec9ef6e43b0d0dabe80 ] On memory unplug reduce DirectMap page count correctly. root@ubuntu-guest:# grep Direct /proc/meminfo DirectMap4k: 0 kB DirectMap64k: 0 kB DirectMap2M: 115343360 kB DirectMap1G: 0 kB Before fix: root@ubuntu-guest:# ndctl disable-namespace all disabled 1 namespace root@ubuntu-guest:# grep Direct /proc/meminfo DirectMap4k: 0 kB DirectMap64k: 0 kB DirectMap2M: 115343360 kB DirectMap1G: 0 kB After fix: root@ubuntu-guest:# ndctl disable-namespace all disabled 1 namespace root@ubuntu-guest:# grep Direct /proc/meminfo DirectMap4k: 0 kB DirectMap64k: 0 kB DirectMap2M: 104857600 kB DirectMap1G: 0 kB Fixes: a2dc009afa9a ("powerpc/mm/book3s/radix: Add mapping statistics") Signed-off-by: Aneesh Kumar K.V Tested-by: Sachin Sant > Signed-off-by: Michael Ellerman Link: https://msgid.link/20230616110826.344417-4-aneesh.kumar@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/mm/book3s64/radix_pgtable.c | 34 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 2297aa764ecd..e8db8c8efe35 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -745,9 +745,9 @@ static void free_pud_table(pud_t *pud_start, p4d_t *p4d) } static void remove_pte_table(pte_t *pte_start, unsigned long addr, - unsigned long end) + unsigned long end, bool direct) { - unsigned long next; + unsigned long next, pages = 0; pte_t *pte; pte = pte_start + pte_index(addr); @@ -769,13 +769,16 @@ static void remove_pte_table(pte_t *pte_start, unsigned long addr, } pte_clear(&init_mm, addr, pte); + pages++; } + if (direct) + update_page_count(mmu_virtual_psize, -pages); } static void __meminit remove_pmd_table(pmd_t *pmd_start, unsigned long addr, - unsigned long end) + unsigned long end, bool direct) { - unsigned long next; + unsigned long next, pages = 0; pte_t *pte_base; pmd_t *pmd; @@ -793,19 +796,22 @@ static void __meminit remove_pmd_table(pmd_t *pmd_start, unsigned long addr, continue; } pte_clear(&init_mm, addr, (pte_t *)pmd); + pages++; continue; } pte_base = (pte_t *)pmd_page_vaddr(*pmd); - remove_pte_table(pte_base, addr, next); + remove_pte_table(pte_base, addr, next, direct); free_pte_table(pte_base, pmd); } + if (direct) + update_page_count(MMU_PAGE_2M, -pages); } static void __meminit remove_pud_table(pud_t *pud_start, unsigned long addr, - unsigned long end) + unsigned long end, bool direct) { - unsigned long next; + unsigned long next, pages = 0; pmd_t *pmd_base; pud_t *pud; @@ -823,16 +829,20 @@ static void __meminit remove_pud_table(pud_t *pud_start, unsigned long addr, continue; } pte_clear(&init_mm, addr, (pte_t *)pud); + pages++; continue; } pmd_base = pud_pgtable(*pud); - remove_pmd_table(pmd_base, addr, next); + remove_pmd_table(pmd_base, addr, next, direct); free_pmd_table(pmd_base, pud); } + if (direct) + update_page_count(MMU_PAGE_1G, -pages); } -static void __meminit remove_pagetable(unsigned long start, unsigned long end) +static void __meminit remove_pagetable(unsigned long start, unsigned long end, + bool direct) { unsigned long addr, next; pud_t *pud_base; @@ -861,7 +871,7 @@ static void __meminit remove_pagetable(unsigned long start, unsigned long end) } pud_base = p4d_pgtable(*p4d); - remove_pud_table(pud_base, addr, next); + remove_pud_table(pud_base, addr, next, direct); free_pud_table(pud_base, p4d); } @@ -884,7 +894,7 @@ int __meminit radix__create_section_mapping(unsigned long start, int __meminit radix__remove_section_mapping(unsigned long start, unsigned long end) { - remove_pagetable(start, end); + remove_pagetable(start, end, true); return 0; } #endif /* CONFIG_MEMORY_HOTPLUG */ @@ -920,7 +930,7 @@ int __meminit radix__vmemmap_create_mapping(unsigned long start, #ifdef CONFIG_MEMORY_HOTPLUG void __meminit radix__vmemmap_remove_mapping(unsigned long start, unsigned long page_size) { - remove_pagetable(start, start + page_size); + remove_pagetable(start, start + page_size, false); } #endif #endif -- cgit 1.4.1 From 4e6c406ccb6fd5b22a9182306822da80aa0ea106 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Fri, 16 Jun 2023 16:38:15 +0530 Subject: powerpc/mm/dax: Fix the condition when checking if altmap vmemap can cross-boundary [ Upstream commit c8eebc4a99f15280654f23e914e746c40a516e50 ] Without this fix, the last subsection vmemmap can end up in memory even if the namespace is created with -M mem and has sufficient space in the altmap area. Fixes: cf387d9644d8 ("libnvdimm/altmap: Track namespace boundaries in altmap") Signed-off-by: Aneesh Kumar K.V Tested-by: Sachin Sant > Signed-off-by: Michael Ellerman Link: https://msgid.link/20230616110826.344417-6-aneesh.kumar@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/mm/init_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index 05b0d584e50b..fe1b83020e0d 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -189,7 +189,7 @@ static bool altmap_cross_boundary(struct vmem_altmap *altmap, unsigned long star unsigned long nr_pfn = page_size / sizeof(struct page); unsigned long start_pfn = page_to_pfn((struct page *)start); - if ((start_pfn + nr_pfn) > altmap->end_pfn) + if ((start_pfn + nr_pfn - 1) > altmap->end_pfn) return true; if (start_pfn < altmap->base_pfn) -- cgit 1.4.1 From 38b64bdb72e053627ae9135807de1b05b21efef8 Mon Sep 17 00:00:00 2001 From: Shunsuke Mie Date: Mon, 15 Aug 2022 11:50:06 +0900 Subject: PCI: endpoint: Fix Kconfig indent style [ Upstream commit 2759ddf7535d63381f9b9b1412e4c46e13ed773a ] Change to follow the Kconfig style guide. This patch fixes to use tab rather than space to indent, while help text is indented an additional two spaces. Link: https://lore.kernel.org/r/20220815025006.48167-1-mie@igel.co.jp Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Shunsuke Mie Signed-off-by: Lorenzo Pieralisi Stable-dep-of: 37587673cda9 ("PCI: endpoint: Fix a Kconfig prompt of vNTB driver") Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/Kconfig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig index 295a033ee9a2..9fd560886871 100644 --- a/drivers/pci/endpoint/functions/Kconfig +++ b/drivers/pci/endpoint/functions/Kconfig @@ -27,13 +27,13 @@ config PCI_EPF_NTB If in doubt, say "N" to disable Endpoint NTB driver. config PCI_EPF_VNTB - tristate "PCI Endpoint NTB driver" - depends on PCI_ENDPOINT - depends on NTB - select CONFIGFS_FS - help - Select this configuration option to enable the Non-Transparent - Bridge (NTB) driver for PCIe Endpoint. NTB driver implements NTB - between PCI Root Port and PCIe Endpoint. + tristate "PCI Endpoint NTB driver" + depends on PCI_ENDPOINT + depends on NTB + select CONFIGFS_FS + help + Select this configuration option to enable the Non-Transparent + Bridge (NTB) driver for PCIe Endpoint. NTB driver implements NTB + between PCI Root Port and PCIe Endpoint. - If in doubt, say "N" to disable Endpoint NTB driver. + If in doubt, say "N" to disable Endpoint NTB driver. -- cgit 1.4.1 From e14379d026d56bde9ec0eb95f253de296e65ef27 Mon Sep 17 00:00:00 2001 From: Shunsuke Mie Date: Thu, 2 Feb 2023 19:38:32 +0900 Subject: PCI: endpoint: Fix a Kconfig prompt of vNTB driver [ Upstream commit 37587673cda963ec950e4983db5023802f9b5ff2 ] vNTB driver and NTB driver have same Kconfig prompt. Changed to make it distinguishable. Link: https://lore.kernel.org/r/20230202103832.2038286-1-mie@igel.co.jp Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Shunsuke Mie Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig index 9fd560886871..8efb6a869e7c 100644 --- a/drivers/pci/endpoint/functions/Kconfig +++ b/drivers/pci/endpoint/functions/Kconfig @@ -27,7 +27,7 @@ config PCI_EPF_NTB If in doubt, say "N" to disable Endpoint NTB driver. config PCI_EPF_VNTB - tristate "PCI Endpoint NTB driver" + tristate "PCI Endpoint Virtual NTB driver" depends on PCI_ENDPOINT depends on NTB select CONFIGFS_FS -- cgit 1.4.1 From 222f64e56b3585af82971127f8bf6497a65d0cc7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 12 Apr 2023 15:34:47 +0900 Subject: PCI: endpoint: functions/pci-epf-test: Fix dma_chan direction [ Upstream commit 880d51c729a3fa944794feb19f605eefe55916fc ] In pci_epf_test_init_dma_chan() epf_test->dma_chan_rx is assigned from dma_request_channel() with DMA_DEV_TO_MEM as filter.dma_mask. However, in pci_epf_test_data_transfer() if the dir is DMA_DEV_TO_MEM, epf->dma_chan_rx should be used but instead we are using epf_test->dma_chan_tx. Fix it. Link: https://lore.kernel.org/r/20230412063447.2841177-1-yoshihiro.shimoda.uh@renesas.com Fixes: 8353813c88ef ("PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities") Tested-by: Kunihiko Hayashi Signed-off-by: Yoshihiro Shimoda Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 55283d2379a6..f0c4d0f77453 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -112,7 +112,7 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, size_t len, dma_addr_t dma_remote, enum dma_transfer_direction dir) { - struct dma_chan *chan = (dir == DMA_DEV_TO_MEM) ? + struct dma_chan *chan = (dir == DMA_MEM_TO_DEV) ? epf_test->dma_chan_tx : epf_test->dma_chan_rx; dma_addr_t dma_local = (dir == DMA_MEM_TO_DEV) ? dma_src : dma_dst; enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; -- cgit 1.4.1 From e2e52c8dfbfdbe10e7a49e4282df33a9f1677b0c Mon Sep 17 00:00:00 2001 From: Xinghui Li Date: Thu, 20 Apr 2023 17:43:31 +0800 Subject: PCI: vmd: Fix uninitialized variable usage in vmd_enable_domain() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0c0206dc4f5ba2d18b15e24d2047487d6f73916b ] The ret variable in the vmd_enable_domain() function was used uninitialized when printing a warning message upon failure of the pci_reset_bus() function. Thus, fix the issue by assigning ret with the value returned from pci_reset_bus() before referencing it in the warning message. This was detected by Smatch: drivers/pci/controller/vmd.c:931 vmd_enable_domain() error: uninitialized symbol 'ret'. [kwilczynski: drop the second patch from the series, add missing reported by tag, commit log] Fixes: 0a584655ef89 ("PCI: vmd: Fix secondary bus reset for Intel bridges") Link: https://lore.kernel.org/all/202305270219.B96IiIfv-lkp@intel.com Link: https://lore.kernel.org/linux-pci/20230420094332.1507900-2-korantwork@gmail.com Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Xinghui Li Signed-off-by: Krzysztof Wilczyński Reviewed-by: Nirmal Patel Signed-off-by: Sasha Levin --- drivers/pci/controller/vmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 50a187a29a1d..d1eb17e3f147 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -872,7 +872,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) if (!list_empty(&child->devices)) { dev = list_first_entry(&child->devices, struct pci_dev, bus_list); - if (pci_reset_bus(dev)) + ret = pci_reset_bus(dev); + if (ret) pci_warn(dev, "can't reset device: %d\n", ret); break; -- cgit 1.4.1 From 8f98749d536d099bfdeaa01e646f0e9eace2191f Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Mon, 26 Jun 2023 15:36:42 +0200 Subject: vfio/mdev: Move the compat_class initialization to module init [ Upstream commit ff598081e5b9d0bdd6874bfe340811bbb75b35e4 ] The pointer to mdev_bus_compat_class is statically defined at the top of mdev_core, and was originally (commit 7b96953bc640 ("vfio: Mediated device Core driver") serialized by the parent_list_lock. The blamed commit removed this mutex, leaving the pointer initialization unserialized. As a result, the creation of multiple MDEVs in parallel (such as during boot) can encounter errors during the creation of the sysfs entries, such as: [ 8.337509] sysfs: cannot create duplicate filename '/class/mdev_bus' [ 8.337514] vfio_ccw 0.0.01d8: MDEV: Registered [ 8.337516] CPU: 13 PID: 946 Comm: driverctl Not tainted 6.4.0-rc7 #20 [ 8.337522] Hardware name: IBM 3906 M05 780 (LPAR) [ 8.337525] Call Trace: [ 8.337528] [<0000000162b0145a>] dump_stack_lvl+0x62/0x80 [ 8.337540] [<00000001622aeb30>] sysfs_warn_dup+0x78/0x88 [ 8.337549] [<00000001622aeca6>] sysfs_create_dir_ns+0xe6/0xf8 [ 8.337552] [<0000000162b04504>] kobject_add_internal+0xf4/0x340 [ 8.337557] [<0000000162b04d48>] kobject_add+0x78/0xd0 [ 8.337561] [<0000000162b04e0a>] kobject_create_and_add+0x6a/0xb8 [ 8.337565] [<00000001627a110e>] class_compat_register+0x5e/0x90 [ 8.337572] [<000003ff7fd815da>] mdev_register_parent+0x102/0x130 [mdev] [ 8.337581] [<000003ff7fdc7f2c>] vfio_ccw_sch_probe+0xe4/0x178 [vfio_ccw] [ 8.337588] [<0000000162a7833c>] css_probe+0x44/0x80 [ 8.337599] [<000000016279f4da>] really_probe+0xd2/0x460 [ 8.337603] [<000000016279fa08>] driver_probe_device+0x40/0xf0 [ 8.337606] [<000000016279fb78>] __device_attach_driver+0xc0/0x140 [ 8.337610] [<000000016279cbe0>] bus_for_each_drv+0x90/0xd8 [ 8.337618] [<00000001627a00b0>] __device_attach+0x110/0x190 [ 8.337621] [<000000016279c7c8>] bus_rescan_devices_helper+0x60/0xb0 [ 8.337626] [<000000016279cd48>] drivers_probe_store+0x48/0x80 [ 8.337632] [<00000001622ac9b0>] kernfs_fop_write_iter+0x138/0x1f0 [ 8.337635] [<00000001621e5e14>] vfs_write+0x1ac/0x2f8 [ 8.337645] [<00000001621e61d8>] ksys_write+0x70/0x100 [ 8.337650] [<0000000162b2bdc4>] __do_syscall+0x1d4/0x200 [ 8.337656] [<0000000162b3c828>] system_call+0x70/0x98 [ 8.337664] kobject: kobject_add_internal failed for mdev_bus with -EEXIST, don't try to register things with the same name in the same directory. [ 8.337668] kobject: kobject_create_and_add: kobject_add error: -17 [ 8.337674] vfio_ccw: probe of 0.0.01d9 failed with error -12 [ 8.342941] vfio_ccw_mdev aeb9ca91-10c6-42bc-a168-320023570aea: Adding to iommu group 2 Move the initialization of the mdev_bus_compat_class pointer to the init path, to match the cleanup in module exit. This way the code in mdev_register_parent() can simply link the new parent to it, rather than determining whether initialization is required first. Fixes: 89345d5177aa ("vfio/mdev: embedd struct mdev_parent in the parent data structure") Reported-by: Alexander Egorenkov Signed-off-by: Eric Farman Reviewed-by: Kevin Tian Reviewed-by: Christoph Hellwig Reviewed-by: Tony Krowiak Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20230626133642.2939168-1-farman@linux.ibm.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- drivers/vfio/mdev/mdev_core.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index 58f91b3bd670..ed4737de4528 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -72,12 +72,6 @@ int mdev_register_parent(struct mdev_parent *parent, struct device *dev, parent->nr_types = nr_types; atomic_set(&parent->available_instances, mdev_driver->max_instances); - if (!mdev_bus_compat_class) { - mdev_bus_compat_class = class_compat_register("mdev_bus"); - if (!mdev_bus_compat_class) - return -ENOMEM; - } - ret = parent_create_sysfs_files(parent); if (ret) return ret; @@ -251,13 +245,24 @@ int mdev_device_remove(struct mdev_device *mdev) static int __init mdev_init(void) { - return bus_register(&mdev_bus_type); + int ret; + + ret = bus_register(&mdev_bus_type); + if (ret) + return ret; + + mdev_bus_compat_class = class_compat_register("mdev_bus"); + if (!mdev_bus_compat_class) { + bus_unregister(&mdev_bus_type); + return -ENOMEM; + } + + return 0; } static void __exit mdev_exit(void) { - if (mdev_bus_compat_class) - class_compat_unregister(mdev_bus_compat_class); + class_compat_unregister(mdev_bus_compat_class); bus_unregister(&mdev_bus_type); } -- cgit 1.4.1 From 22c30022cde6e2c88612b3a499223cfa912f1bc7 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 4 May 2023 11:59:32 +0800 Subject: hwrng: virtio - Fix race on data_avail and actual data [ Upstream commit ac52578d6e8d300dd50f790f29a24169b1edd26c ] The virtio rng device kicks off a new entropy request whenever the data available reaches zero. When a new request occurs at the end of a read operation, that is, when the result of that request is only needed by the next reader, then there is a race between the writing of the new data and the next reader. This is because there is no synchronisation whatsoever between the writer and the reader. Fix this by writing data_avail with smp_store_release and reading it with smp_load_acquire when we first enter read. The subsequent reads are safe because they're either protected by the first load acquire, or by the completion mechanism. Also remove the redundant zeroing of data_idx in random_recv_done (data_idx must already be zero at this point) and data_avail in request_entropy (ditto). Reported-by: syzbot+726dc8c62c3536431ceb@syzkaller.appspotmail.com Fixes: f7f510ec1957 ("virtio: An entropy device, as suggested by hpa.") Signed-off-by: Herbert Xu Acked-by: Michael S. Tsirkin Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/char/hw_random/virtio-rng.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index a6f3a8a2aca6..35304117338a 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -4,6 +4,7 @@ * Copyright (C) 2007, 2008 Rusty Russell IBM Corporation */ +#include #include #include #include @@ -37,13 +38,13 @@ struct virtrng_info { static void random_recv_done(struct virtqueue *vq) { struct virtrng_info *vi = vq->vdev->priv; + unsigned int len; /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!virtqueue_get_buf(vi->vq, &vi->data_avail)) + if (!virtqueue_get_buf(vi->vq, &len)) return; - vi->data_idx = 0; - + smp_store_release(&vi->data_avail, len); complete(&vi->have_data); } @@ -52,7 +53,6 @@ static void request_entropy(struct virtrng_info *vi) struct scatterlist sg; reinit_completion(&vi->have_data); - vi->data_avail = 0; vi->data_idx = 0; sg_init_one(&sg, vi->data, sizeof(vi->data)); @@ -88,7 +88,7 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) read = 0; /* copy available data */ - if (vi->data_avail) { + if (smp_load_acquire(&vi->data_avail)) { chunk = copy_data(vi, buf, size); size -= chunk; read += chunk; -- cgit 1.4.1 From 2be41ef57c5a483cab1202a45e1aae478244fcce Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 15 May 2023 00:27:19 +0900 Subject: modpost: remove broken calculation of exception_table_entry size [ Upstream commit d0acc76a49aa917c1a455d11d32d34a01e8b2835 ] find_extable_entry_size() is completely broken. It has awesome comments about how to calculate sizeof(struct exception_table_entry). It was based on these assumptions: - struct exception_table_entry has two fields - both of the fields have the same size Then, we came up with this equation: (offset of the second field) * 2 == (size of struct) It was true for all architectures when commit 52dc0595d540 ("modpost: handle relocations mismatch in __ex_table.") was applied. Our mathematics broke when commit 548acf19234d ("x86/mm: Expand the exception table logic to allow new handling options") introduced the third field. Now, the definition of exception_table_entry is highly arch-dependent. For x86, sizeof(struct exception_table_entry) is apparently 12, but find_extable_entry_size() sets extable_entry_size to 8. I could fix it, but I do not see much value in this code. extable_entry_size is used just for selecting a slightly different error message. If the first field ("insn") references to a non-executable section, The relocation at %s+0x%lx references section "%s" which is not executable, IOW it is not possible for the kernel to fault at that address. Something is seriously wrong and should be fixed. If the second field ("fixup") references to a non-executable section, The relocation at %s+0x%lx references section "%s" which is not executable, IOW the kernel will fault if it ever tries to jump to it. Something is seriously wrong and should be fixed. Merge the two error messages rather than adding even more complexity. Change fatal() to error() to make it continue running and catch more possible errors. Fixes: 548acf19234d ("x86/mm: Expand the exception table logic to allow new handling options") Signed-off-by: Masahiro Yamada Signed-off-by: Sasha Levin --- scripts/mod/modpost.c | 60 +++------------------------------------------------ 1 file changed, 3 insertions(+), 57 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 1dfa80c6b471..d08cf73e4a3f 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1299,43 +1299,6 @@ static int is_executable_section(struct elf_info* elf, unsigned int section_inde return ((elf->sechdrs[section_index].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR); } -/* - * We rely on a gross hack in section_rel[a]() calling find_extable_entry_size() - * to know the sizeof(struct exception_table_entry) for the target architecture. - */ -static unsigned int extable_entry_size = 0; -static void find_extable_entry_size(const char* const sec, const Elf_Rela* r) -{ - /* - * If we're currently checking the second relocation within __ex_table, - * that relocation offset tells us the offsetof(struct - * exception_table_entry, fixup) which is equal to sizeof(struct - * exception_table_entry) divided by two. We use that to our advantage - * since there's no portable way to get that size as every architecture - * seems to go with different sized types. Not pretty but better than - * hard-coding the size for every architecture.. - */ - if (!extable_entry_size) - extable_entry_size = r->r_offset * 2; -} - -static inline bool is_extable_fault_address(Elf_Rela *r) -{ - /* - * extable_entry_size is only discovered after we've handled the - * _second_ relocation in __ex_table, so only abort when we're not - * handling the first reloc and extable_entry_size is zero. - */ - if (r->r_offset && extable_entry_size == 0) - fatal("extable_entry size hasn't been discovered!\n"); - - return ((r->r_offset == 0) || - (r->r_offset % extable_entry_size == 0)); -} - -#define is_second_extable_reloc(Start, Cur, Sec) \ - (((Cur) == (Start) + 1) && (strcmp("__ex_table", (Sec)) == 0)) - static void report_extable_warnings(const char* modname, struct elf_info* elf, const struct sectioncheck* const mismatch, Elf_Rela* r, Elf_Sym* sym, @@ -1392,22 +1355,9 @@ static void extable_mismatch_handler(const char* modname, struct elf_info *elf, "You might get more information about where this is\n" "coming from by using scripts/check_extable.sh %s\n", fromsec, (long)r->r_offset, tosec, modname); - else if (!is_executable_section(elf, get_secindex(elf, sym))) { - if (is_extable_fault_address(r)) - fatal("The relocation at %s+0x%lx references\n" - "section \"%s\" which is not executable, IOW\n" - "it is not possible for the kernel to fault\n" - "at that address. Something is seriously wrong\n" - "and should be fixed.\n", - fromsec, (long)r->r_offset, tosec); - else - fatal("The relocation at %s+0x%lx references\n" - "section \"%s\" which is not executable, IOW\n" - "the kernel will fault if it ever tries to\n" - "jump to it. Something is seriously wrong\n" - "and should be fixed.\n", - fromsec, (long)r->r_offset, tosec); - } + else if (!is_executable_section(elf, get_secindex(elf, sym))) + error("%s+0x%lx references non-executable section '%s'\n", + fromsec, (long)r->r_offset, tosec); } static void check_section_mismatch(const char *modname, struct elf_info *elf, @@ -1569,8 +1519,6 @@ static void section_rela(const char *modname, struct elf_info *elf, /* Skip special sections */ if (is_shndx_special(sym->st_shndx)) continue; - if (is_second_extable_reloc(start, rela, fromsec)) - find_extable_entry_size(fromsec, &r); check_section_mismatch(modname, elf, &r, sym, fromsec); } } @@ -1628,8 +1576,6 @@ static void section_rel(const char *modname, struct elf_info *elf, /* Skip special sections */ if (is_shndx_special(sym->st_shndx)) continue; - if (is_second_extable_reloc(start, rel, fromsec)) - find_extable_entry_size(fromsec, &r); check_section_mismatch(modname, elf, &r, sym, fromsec); } } -- cgit 1.4.1 From 5a4adb1ecebaf0fd64c336e3b5578633b8074a4d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 19 May 2023 15:33:34 -0700 Subject: crypto: nx - fix build warnings when DEBUG_FS is not enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b04b076fb56560b39d695ac3744db457e12278fd ] Fix build warnings when DEBUG_FS is not enabled by using an empty do-while loop instead of a value: In file included from ../drivers/crypto/nx/nx.c:27: ../drivers/crypto/nx/nx.c: In function 'nx_register_algs': ../drivers/crypto/nx/nx.h:173:33: warning: statement with no effect [-Wunused-value] 173 | #define NX_DEBUGFS_INIT(drv) (0) ../drivers/crypto/nx/nx.c:573:9: note: in expansion of macro 'NX_DEBUGFS_INIT' 573 | NX_DEBUGFS_INIT(&nx_driver); ../drivers/crypto/nx/nx.c: In function 'nx_remove': ../drivers/crypto/nx/nx.h:174:33: warning: statement with no effect [-Wunused-value] 174 | #define NX_DEBUGFS_FINI(drv) (0) ../drivers/crypto/nx/nx.c:793:17: note: in expansion of macro 'NX_DEBUGFS_FINI' 793 | NX_DEBUGFS_FINI(&nx_driver); Also, there is no need to build nx_debugfs.o when DEBUG_FS is not enabled, so change the Makefile to accommodate that. Fixes: ae0222b7289d ("powerpc/crypto: nx driver code supporting nx encryption") Fixes: aef7b31c8833 ("powerpc/crypto: Build files for the nx device driver") Signed-off-by: Randy Dunlap Cc: Breno Leitão Cc: Nayna Jain Cc: Paulo Flabiano Smorigo Cc: Herbert Xu Cc: "David S. Miller" Cc: linux-crypto@vger.kernel.org Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Christophe Leroy Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/nx/Makefile | 2 +- drivers/crypto/nx/nx.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/nx/Makefile b/drivers/crypto/nx/Makefile index d00181a26dd6..483cef62acee 100644 --- a/drivers/crypto/nx/Makefile +++ b/drivers/crypto/nx/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CRYPTO_DEV_NX_ENCRYPT) += nx-crypto.o nx-crypto-objs := nx.o \ - nx_debugfs.o \ nx-aes-cbc.o \ nx-aes-ecb.o \ nx-aes-gcm.o \ @@ -11,6 +10,7 @@ nx-crypto-objs := nx.o \ nx-sha256.o \ nx-sha512.o +nx-crypto-$(CONFIG_DEBUG_FS) += nx_debugfs.o obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_PSERIES) += nx-compress-pseries.o nx-compress.o obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_POWERNV) += nx-compress-powernv.o nx-compress.o nx-compress-objs := nx-842.o diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h index c6233173c612..2697baebb6a3 100644 --- a/drivers/crypto/nx/nx.h +++ b/drivers/crypto/nx/nx.h @@ -170,8 +170,8 @@ struct nx_sg *nx_walk_and_build(struct nx_sg *, unsigned int, void nx_debugfs_init(struct nx_crypto_driver *); void nx_debugfs_fini(struct nx_crypto_driver *); #else -#define NX_DEBUGFS_INIT(drv) (0) -#define NX_DEBUGFS_FINI(drv) (0) +#define NX_DEBUGFS_INIT(drv) do {} while (0) +#define NX_DEBUGFS_FINI(drv) do {} while (0) #endif #define NX_PAGE_NUM(x) ((u64)(x) & 0xfffffffffffff000ULL) -- cgit 1.4.1 From 1df287bd89c70860c36755bf18b6906429e9b8a3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 1 Jun 2023 21:09:55 +0900 Subject: modpost: fix section mismatch message for R_ARM_ABS32 [ Upstream commit b7c63520f6703a25eebb4f8138fed764fcae1c6f ] addend_arm_rel() processes R_ARM_ABS32 in a wrong way. Here, test code. [test code 1] #include int __initdata foo; int get_foo(void) { return foo; } If you compile it with ARM versatile_defconfig, modpost will show the symbol name, (unknown). WARNING: modpost: vmlinux.o: section mismatch in reference: get_foo (section: .text) -> (unknown) (section: .init.data) (You need to use GNU linker instead of LLD to reproduce it.) If you compile it for other architectures, modpost will show the correct symbol name. WARNING: modpost: vmlinux.o: section mismatch in reference: get_foo (section: .text) -> foo (section: .init.data) For R_ARM_ABS32, addend_arm_rel() sets r->r_addend to a wrong value. I just mimicked the code in arch/arm/kernel/module.c. However, there is more difficulty for ARM. Here, test code. [test code 2] #include int __initdata foo; int get_foo(void) { return foo; } int __initdata bar; int get_bar(void) { return bar; } With this commit applied, modpost will show the following messages for ARM versatile_defconfig: WARNING: modpost: vmlinux.o: section mismatch in reference: get_foo (section: .text) -> foo (section: .init.data) WARNING: modpost: vmlinux.o: section mismatch in reference: get_bar (section: .text) -> foo (section: .init.data) The reference from 'get_bar' to 'foo' seems wrong. I have no solution for this because it is true in assembly level. In the following output, relocation at 0x1c is no longer associated with 'bar'. The two relocation entries point to the same symbol, and the offset to 'bar' is encoded in the instruction 'r0, [r3, #4]'. Disassembly of section .text: 00000000 : 0: e59f3004 ldr r3, [pc, #4] @ c 4: e5930000 ldr r0, [r3] 8: e12fff1e bx lr c: 00000000 .word 0x00000000 00000010 : 10: e59f3004 ldr r3, [pc, #4] @ 1c 14: e5930004 ldr r0, [r3, #4] 18: e12fff1e bx lr 1c: 00000000 .word 0x00000000 Relocation section '.rel.text' at offset 0x244 contains 2 entries: Offset Info Type Sym.Value Sym. Name 0000000c 00000c02 R_ARM_ABS32 00000000 .init.data 0000001c 00000c02 R_ARM_ABS32 00000000 .init.data When find_elf_symbol() gets into a situation where relsym->st_name is zero, there is no guarantee to get the symbol name as written in C. I am keeping the current logic because it is useful in many architectures, but the symbol name is not always correct depending on the optimization. I left some comments in find_tosym(). Fixes: 56a974fa2d59 ("kbuild: make better section mismatch reports on arm") Signed-off-by: Masahiro Yamada Signed-off-by: Sasha Levin --- scripts/mod/modpost.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index d08cf73e4a3f..f7a67f111d84 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1157,6 +1157,10 @@ static Elf_Sym *find_elf_symbol(struct elf_info *elf, Elf64_Sword addr, if (relsym->st_name != 0) return relsym; + /* + * Strive to find a better symbol name, but the resulting name may not + * match the symbol referenced in the original code. + */ relsym_secindex = get_secindex(elf, relsym); for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) { if (get_secindex(elf, sym) != relsym_secindex) @@ -1418,12 +1422,14 @@ static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) { unsigned int r_typ = ELF_R_TYPE(r->r_info); + Elf_Sym *sym = elf->symtab_start + ELF_R_SYM(r->r_info); + void *loc = reloc_location(elf, sechdr, r); + uint32_t inst; switch (r_typ) { case R_ARM_ABS32: - /* From ARM ABI: (S + A) | T */ - r->r_addend = (int)(long) - (elf->symtab_start + ELF_R_SYM(r->r_info)); + inst = TO_NATIVE(*(uint32_t *)loc); + r->r_addend = inst + sym->st_value; break; case R_ARM_PC24: case R_ARM_CALL: -- cgit 1.4.1 From 6852d82e6c59c1c4d9437d5fdf4e73baf50c06af Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 1 Jun 2023 21:09:56 +0900 Subject: modpost: fix section mismatch message for R_ARM_{PC24,CALL,JUMP24} [ Upstream commit 56a24b8ce6a7f9c4a21b2276a8644f6f3d8fc14d ] addend_arm_rel() processes R_ARM_PC24, R_ARM_CALL, R_ARM_JUMP24 in a wrong way. Here, test code. [test code for R_ARM_JUMP24] .section .init.text,"ax" bar: bx lr .section .text,"ax" .globl foo foo: b bar [test code for R_ARM_CALL] .section .init.text,"ax" bar: bx lr .section .text,"ax" .globl foo foo: push {lr} bl bar pop {pc} If you compile it with ARM multi_v7_defconfig, modpost will show the symbol name, (unknown). WARNING: modpost: vmlinux.o: section mismatch in reference: foo (section: .text) -> (unknown) (section: .init.text) (You need to use GNU linker instead of LLD to reproduce it.) Fix the code to make modpost show the correct symbol name. I imported (with adjustment) sign_extend32() from include/linux/bitops.h. The '+8' is the compensation for pc-relative instruction. It is documented in "ELF for the Arm Architecture" [1]. "If the relocation is pc-relative then compensation for the PC bias (the PC value is 8 bytes ahead of the executing instruction in Arm state and 4 bytes in Thumb state) must be encoded in the relocation by the object producer." [1]: https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst Fixes: 56a974fa2d59 ("kbuild: make better section mismatch reports on arm") Fixes: 6e2e340b59d2 ("ARM: 7324/1: modpost: Fix section warnings for ARM for many compilers") Signed-off-by: Masahiro Yamada Signed-off-by: Sasha Levin --- scripts/mod/modpost.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index f7a67f111d84..9e370e77f52d 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1419,12 +1419,20 @@ static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) #define R_ARM_THM_JUMP19 51 #endif +static int32_t sign_extend32(int32_t value, int index) +{ + uint8_t shift = 31 - index; + + return (int32_t)(value << shift) >> shift; +} + static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) { unsigned int r_typ = ELF_R_TYPE(r->r_info); Elf_Sym *sym = elf->symtab_start + ELF_R_SYM(r->r_info); void *loc = reloc_location(elf, sechdr, r); uint32_t inst; + int32_t offset; switch (r_typ) { case R_ARM_ABS32: @@ -1434,6 +1442,10 @@ static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) case R_ARM_PC24: case R_ARM_CALL: case R_ARM_JUMP24: + inst = TO_NATIVE(*(uint32_t *)loc); + offset = sign_extend32((inst & 0x00ffffff) << 2, 25); + r->r_addend = offset + sym->st_value + 8; + break; case R_ARM_THM_CALL: case R_ARM_THM_JUMP24: case R_ARM_THM_JUMP19: -- cgit 1.4.1 From 7ab0e37f8064d1f85f34e3dc344a8e62a4a98e0d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 May 2023 10:33:04 +0200 Subject: crypto: marvell/cesa - Fix type mismatch warning [ Upstream commit efbc7764c4446566edb76ca05e903b5905673d2e ] Commit df8fc4e934c1 ("kbuild: Enable -fstrict-flex-arrays=3") uncovered a type mismatch in cesa 3des support that leads to a memcpy beyond the end of a structure: In function 'fortify_memcpy_chk', inlined from 'mv_cesa_des3_ede_setkey' at drivers/crypto/marvell/cesa/cipher.c:307:2: include/linux/fortify-string.h:583:25: error: call to '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror=attribute-warning] 583 | __write_overflow_field(p_size_field, size); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is probably harmless as the actual data that is copied has the correct type, but clearly worth fixing nonetheless. Fixes: 4ada48397823 ("crypto: marvell/cesa - add Triple-DES support") Cc: Kees Cook Cc: Gustavo A. R. Silva Signed-off-by: Arnd Bergmann Reviewed-by: Kees Cook Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/marvell/cesa/cipher.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/marvell/cesa/cipher.c b/drivers/crypto/marvell/cesa/cipher.c index c6f2fa753b7c..0f37dfd42d85 100644 --- a/drivers/crypto/marvell/cesa/cipher.c +++ b/drivers/crypto/marvell/cesa/cipher.c @@ -297,7 +297,7 @@ static int mv_cesa_des_setkey(struct crypto_skcipher *cipher, const u8 *key, static int mv_cesa_des3_ede_setkey(struct crypto_skcipher *cipher, const u8 *key, unsigned int len) { - struct mv_cesa_des_ctx *ctx = crypto_skcipher_ctx(cipher); + struct mv_cesa_des3_ctx *ctx = crypto_skcipher_ctx(cipher); int err; err = verify_skcipher_des3_key(cipher, key); -- cgit 1.4.1 From 64c358c9abae443e9e8745884006668a8ad92c93 Mon Sep 17 00:00:00 2001 From: Stephan Müller Date: Thu, 25 May 2023 19:00:05 +0200 Subject: crypto: jitter - correct health test during initialization [ Upstream commit d23659769ad1bf2cbafaa0efcbae20ef1a74f77e ] With the update of the permanent and intermittent health errors, the actual indicator for the health test indicates a potential error only for the one offending time stamp gathered in the current iteration round. The next iteration round will "overwrite" the health test result. Thus, the entropy collection loop in jent_gen_entropy checks for the health test failure upon each loop iteration. However, the initialization operation checked for the APT health test once for an APT window which implies it would not catch most errors. Thus, the check for all health errors is now invoked unconditionally during each loop iteration for the startup test. With the change, the error JENT_ERCT becomes unused as all health errors are only reported with the JENT_HEALTH return code. This allows the removal of the error indicator. Fixes: 3fde2fe99aa6 ("crypto: jitter - permanent and intermittent health errors" ) Reported-by: Joachim Vandersmissen Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/jitterentropy.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crypto/jitterentropy.c b/crypto/jitterentropy.c index 22f48bf4c6f5..227cedfa4f0a 100644 --- a/crypto/jitterentropy.c +++ b/crypto/jitterentropy.c @@ -117,7 +117,6 @@ struct rand_data { * zero). */ #define JENT_ESTUCK 8 /* Too many stuck results during init. */ #define JENT_EHEALTH 9 /* Health test failed during initialization */ -#define JENT_ERCT 10 /* RCT failed during initialization */ /* * The output n bits can receive more than n bits of min entropy, of course, @@ -762,14 +761,12 @@ int jent_entropy_init(void) if ((nonstuck % JENT_APT_WINDOW_SIZE) == 0) { jent_apt_reset(&ec, delta & JENT_APT_WORD_MASK); - if (jent_health_failure(&ec)) - return JENT_EHEALTH; } } - /* Validate RCT */ - if (jent_rct_failure(&ec)) - return JENT_ERCT; + /* Validate health test result */ + if (jent_health_failure(&ec)) + return JENT_EHEALTH; /* test whether we have an increasing timer */ if (!(time2 > time)) -- cgit 1.4.1 From dd872d5576cc94528f427c7264c2c438928cc6d2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 8 Jun 2023 11:23:40 +0300 Subject: modpost: fix off by one in is_executable_section() [ Upstream commit 3a3f1e573a105328a2cca45a7cfbebabbf5e3192 ] The > comparison should be >= to prevent an out of bounds array access. Fixes: 52dc0595d540 ("modpost: handle relocations mismatch in __ex_table.") Signed-off-by: Dan Carpenter Signed-off-by: Masahiro Yamada Signed-off-by: Sasha Levin --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 9e370e77f52d..e6be7fc2625f 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1297,7 +1297,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf, static int is_executable_section(struct elf_info* elf, unsigned int section_index) { - if (section_index > elf->num_sections) + if (section_index >= elf->num_sections) fatal("section_index is outside elf->num_sections!\n"); return ((elf->sechdrs[section_index].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR); -- cgit 1.4.1 From c14964fe8e9514c4e14a3c8f9a4b9227bff6bbc0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 12 Jun 2023 00:50:50 +0900 Subject: ARC: define ASM_NL and __ALIGN(_STR) outside #ifdef __ASSEMBLY__ guard [ Upstream commit 92e2921eeafdfca9acd9b83f07d2b7ca099bac24 ] ASM_NL is useful not only in *.S files but also in .c files for using inline assembler in C code. On ARC, however, ASM_NL is evaluated inconsistently. It is expanded to a backquote (`) in *.S files, but a semicolon (;) in *.c files because arch/arc/include/asm/linkage.h defines it inside #ifdef __ASSEMBLY__, so the definition for C code falls back to the default value defined in include/linux/linkage.h. If ASM_NL is used in inline assembler in .c files, it will result in wrong assembly code because a semicolon is not an instruction separator, but the start of a comment for ARC. Move ASM_NL (also __ALIGN and __ALIGN_STR) out of the #ifdef. Fixes: 9df62f054406 ("arch: use ASM_NL instead of ';' for assembler new line character in the macro") Fixes: 8d92e992a785 ("ARC: define __ALIGN_STR and __ALIGN symbols for ARC") Signed-off-by: Masahiro Yamada Signed-off-by: Sasha Levin --- arch/arc/include/asm/linkage.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arc/include/asm/linkage.h b/arch/arc/include/asm/linkage.h index c9434ff3aa4c..8a3fb71e9cfa 100644 --- a/arch/arc/include/asm/linkage.h +++ b/arch/arc/include/asm/linkage.h @@ -8,6 +8,10 @@ #include +#define ASM_NL ` /* use '`' to mark new line in macro */ +#define __ALIGN .align 4 +#define __ALIGN_STR __stringify(__ALIGN) + #ifdef __ASSEMBLY__ .macro ST2 e, o, off @@ -28,10 +32,6 @@ #endif .endm -#define ASM_NL ` /* use '`' to mark new line in macro */ -#define __ALIGN .align 4 -#define __ALIGN_STR __stringify(__ALIGN) - /* annotation for data we want in DCCM - if enabled in .config */ .macro ARCFP_DATA nm #ifdef CONFIG_ARC_HAS_DCCM -- cgit 1.4.1 From 2db49992fcec7ed03791e8cb4e7fcdda3f79f43a Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 22 Nov 2022 17:24:01 +0800 Subject: crypto: kpp - Add helper to set reqsize [ Upstream commit 56861cbde1b9f3b34d300e6ba87f2c3de1a9c309 ] The value of reqsize should only be changed through a helper. To do so we need to first add a helper for this. Signed-off-by: Herbert Xu Stable-dep-of: eb7713f5ca97 ("crypto: qat - unmap buffer before free for DH") Signed-off-by: Sasha Levin --- include/crypto/internal/kpp.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/crypto/internal/kpp.h b/include/crypto/internal/kpp.h index 9cb0662ebe87..31ff3c1986ef 100644 --- a/include/crypto/internal/kpp.h +++ b/include/crypto/internal/kpp.h @@ -50,6 +50,12 @@ static inline void *kpp_request_ctx(struct kpp_request *req) return req->__ctx; } +static inline void kpp_set_reqsize(struct crypto_kpp *kpp, + unsigned int reqsize) +{ + crypto_kpp_alg(kpp)->reqsize = reqsize; +} + static inline void *kpp_tfm_ctx(struct crypto_kpp *tfm) { return tfm->base.__crt_ctx; -- cgit 1.4.1 From da1729e6619c414f34ce679247721603ebb957dc Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 22 Nov 2022 17:30:58 +0800 Subject: crypto: qat - Use helper to set reqsize [ Upstream commit 80e62ad58db084920d8cf23323b713391e09f374 ] The value of reqsize must only be changed through the helper. Signed-off-by: Herbert Xu Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Stable-dep-of: eb7713f5ca97 ("crypto: qat - unmap buffer before free for DH") Signed-off-by: Sasha Levin --- drivers/crypto/qat/qat_common/qat_asym_algs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/qat/qat_common/qat_asym_algs.c b/drivers/crypto/qat/qat_common/qat_asym_algs.c index 94a26702aeae..935a7e012946 100644 --- a/drivers/crypto/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/qat/qat_common/qat_asym_algs.c @@ -494,6 +494,8 @@ static int qat_dh_init_tfm(struct crypto_kpp *tfm) if (!inst) return -EINVAL; + kpp_set_reqsize(tfm, sizeof(struct qat_asym_request) + 64); + ctx->p_size = 0; ctx->g2 = false; ctx->inst = inst; @@ -1230,6 +1232,8 @@ static int qat_rsa_init_tfm(struct crypto_akcipher *tfm) if (!inst) return -EINVAL; + akcipher_set_reqsize(tfm, sizeof(struct qat_asym_request) + 64); + ctx->key_sz = 0; ctx->inst = inst; return 0; @@ -1252,7 +1256,6 @@ static struct akcipher_alg rsa = { .max_size = qat_rsa_max_size, .init = qat_rsa_init_tfm, .exit = qat_rsa_exit_tfm, - .reqsize = sizeof(struct qat_asym_request) + 64, .base = { .cra_name = "rsa", .cra_driver_name = "qat-rsa", @@ -1269,7 +1272,6 @@ static struct kpp_alg dh = { .max_size = qat_dh_max_size, .init = qat_dh_init_tfm, .exit = qat_dh_exit_tfm, - .reqsize = sizeof(struct qat_asym_request) + 64, .base = { .cra_name = "dh", .cra_driver_name = "qat-dh", -- cgit 1.4.1 From 32b09834c3c7063cd2bab6951ae98d04d2622f9c Mon Sep 17 00:00:00 2001 From: Hareshx Sankar Raj Date: Mon, 5 Jun 2023 22:06:06 +0100 Subject: crypto: qat - unmap buffer before free for DH [ Upstream commit eb7713f5ca97697b92f225127440d1525119b8de ] The callback function for DH frees the memory allocated for the destination buffer before unmapping it. This sequence is wrong. Change the cleanup sequence to unmap the buffer before freeing it. Fixes: 029aa4624a7f ("crypto: qat - remove dma_free_coherent() for DH") Signed-off-by: Hareshx Sankar Raj Co-developed-by: Bolemx Sivanagaleela Signed-off-by: Bolemx Sivanagaleela Reviewed-by: Giovanni Cabiddu Reviewed-by: Andy Shevchenko Signed-off-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/qat/qat_common/qat_asym_algs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/qat/qat_common/qat_asym_algs.c b/drivers/crypto/qat/qat_common/qat_asym_algs.c index 935a7e012946..8806242469a0 100644 --- a/drivers/crypto/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/qat/qat_common/qat_asym_algs.c @@ -170,15 +170,14 @@ static void qat_dh_cb(struct icp_qat_fw_pke_resp *resp) } areq->dst_len = req->ctx.dh->p_size; + dma_unmap_single(dev, req->out.dh.r, req->ctx.dh->p_size, + DMA_FROM_DEVICE); if (req->dst_align) { scatterwalk_map_and_copy(req->dst_align, areq->dst, 0, areq->dst_len, 1); kfree_sensitive(req->dst_align); } - dma_unmap_single(dev, req->out.dh.r, req->ctx.dh->p_size, - DMA_FROM_DEVICE); - dma_unmap_single(dev, req->phy_in, sizeof(struct qat_dh_input_params), DMA_TO_DEVICE); dma_unmap_single(dev, req->phy_out, -- cgit 1.4.1 From 08749a9005de0ee54d30d1be8dbd5b9203a6bcf7 Mon Sep 17 00:00:00 2001 From: Hareshx Sankar Raj Date: Mon, 5 Jun 2023 22:06:07 +0100 Subject: crypto: qat - unmap buffers before free for RSA [ Upstream commit d776b25495f2c71b9dbf1f5e53b642215ba72f3c ] The callback function for RSA frees the memory allocated for the source and destination buffers before unmapping them. This sequence is wrong. Change the cleanup sequence to unmap the buffers before freeing them. Fixes: 3dfaf0071ed7 ("crypto: qat - remove dma_free_coherent() for RSA") Signed-off-by: Hareshx Sankar Raj Co-developed-by: Bolemx Sivanagaleela Signed-off-by: Bolemx Sivanagaleela Reviewed-by: Giovanni Cabiddu Reviewed-by: Andy Shevchenko Signed-off-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/qat/qat_common/qat_asym_algs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/qat/qat_common/qat_asym_algs.c b/drivers/crypto/qat/qat_common/qat_asym_algs.c index 8806242469a0..4128200a9032 100644 --- a/drivers/crypto/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/qat/qat_common/qat_asym_algs.c @@ -520,12 +520,14 @@ static void qat_rsa_cb(struct icp_qat_fw_pke_resp *resp) err = (err == ICP_QAT_FW_COMN_STATUS_FLAG_OK) ? 0 : -EINVAL; - kfree_sensitive(req->src_align); - dma_unmap_single(dev, req->in.rsa.enc.m, req->ctx.rsa->key_sz, DMA_TO_DEVICE); + kfree_sensitive(req->src_align); + areq->dst_len = req->ctx.rsa->key_sz; + dma_unmap_single(dev, req->out.rsa.enc.c, req->ctx.rsa->key_sz, + DMA_FROM_DEVICE); if (req->dst_align) { scatterwalk_map_and_copy(req->dst_align, areq->dst, 0, areq->dst_len, 1); @@ -533,9 +535,6 @@ static void qat_rsa_cb(struct icp_qat_fw_pke_resp *resp) kfree_sensitive(req->dst_align); } - dma_unmap_single(dev, req->out.rsa.enc.c, req->ctx.rsa->key_sz, - DMA_FROM_DEVICE); - dma_unmap_single(dev, req->phy_in, sizeof(struct qat_rsa_input_params), DMA_TO_DEVICE); dma_unmap_single(dev, req->phy_out, -- cgit 1.4.1 From 705317843644f54d4cb9b2014d1bb9383045ab47 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Thu, 15 Jun 2023 11:19:46 +0000 Subject: NFSv4.2: fix wrong shrinker_id [ Upstream commit 7f7ab336898f281e58540ef781a8fb375acc32a9 ] Currently, the list_lru::shrinker_id corresponding to the nfs4_xattr shrinkers is wrong: >>> prog["nfs4_xattr_cache_lru"].shrinker_id (int)0 >>> prog["nfs4_xattr_entry_lru"].shrinker_id (int)0 >>> prog["nfs4_xattr_large_entry_lru"].shrinker_id (int)0 >>> prog["nfs4_xattr_cache_shrinker"].id (int)18 >>> prog["nfs4_xattr_entry_shrinker"].id (int)19 >>> prog["nfs4_xattr_large_entry_shrinker"].id (int)20 This is not what we expect, which will cause these shrinkers not to be found in shrink_slab_memcg(). We should assign shrinker::id before calling list_lru_init_memcg(), so that the corresponding list_lru::shrinker_id will be assigned the correct value like below: >>> prog["nfs4_xattr_cache_lru"].shrinker_id (int)16 >>> prog["nfs4_xattr_entry_lru"].shrinker_id (int)17 >>> prog["nfs4_xattr_large_entry_lru"].shrinker_id (int)18 >>> prog["nfs4_xattr_cache_shrinker"].id (int)16 >>> prog["nfs4_xattr_entry_shrinker"].id (int)17 >>> prog["nfs4_xattr_large_entry_shrinker"].id (int)18 So just do it. Fixes: 95ad37f90c33 ("NFSv4.2: add client side xattr caching.") Signed-off-by: Qi Zheng Signed-off-by: Trond Myklebust Signed-off-by: Sasha Levin --- fs/nfs/nfs42xattr.c | 79 +++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/fs/nfs/nfs42xattr.c b/fs/nfs/nfs42xattr.c index 76ae11834206..911f634ba3da 100644 --- a/fs/nfs/nfs42xattr.c +++ b/fs/nfs/nfs42xattr.c @@ -991,6 +991,29 @@ static void nfs4_xattr_cache_init_once(void *p) INIT_LIST_HEAD(&cache->dispose); } +static int nfs4_xattr_shrinker_init(struct shrinker *shrinker, + struct list_lru *lru, const char *name) +{ + int ret = 0; + + ret = register_shrinker(shrinker, name); + if (ret) + return ret; + + ret = list_lru_init_memcg(lru, shrinker); + if (ret) + unregister_shrinker(shrinker); + + return ret; +} + +static void nfs4_xattr_shrinker_destroy(struct shrinker *shrinker, + struct list_lru *lru) +{ + unregister_shrinker(shrinker); + list_lru_destroy(lru); +} + int __init nfs4_xattr_cache_init(void) { int ret = 0; @@ -1002,44 +1025,30 @@ int __init nfs4_xattr_cache_init(void) if (nfs4_xattr_cache_cachep == NULL) return -ENOMEM; - ret = list_lru_init_memcg(&nfs4_xattr_large_entry_lru, - &nfs4_xattr_large_entry_shrinker); - if (ret) - goto out4; - - ret = list_lru_init_memcg(&nfs4_xattr_entry_lru, - &nfs4_xattr_entry_shrinker); - if (ret) - goto out3; - - ret = list_lru_init_memcg(&nfs4_xattr_cache_lru, - &nfs4_xattr_cache_shrinker); - if (ret) - goto out2; - - ret = register_shrinker(&nfs4_xattr_cache_shrinker, "nfs-xattr_cache"); + ret = nfs4_xattr_shrinker_init(&nfs4_xattr_cache_shrinker, + &nfs4_xattr_cache_lru, + "nfs-xattr_cache"); if (ret) goto out1; - ret = register_shrinker(&nfs4_xattr_entry_shrinker, "nfs-xattr_entry"); + ret = nfs4_xattr_shrinker_init(&nfs4_xattr_entry_shrinker, + &nfs4_xattr_entry_lru, + "nfs-xattr_entry"); if (ret) - goto out; + goto out2; - ret = register_shrinker(&nfs4_xattr_large_entry_shrinker, - "nfs-xattr_large_entry"); + ret = nfs4_xattr_shrinker_init(&nfs4_xattr_large_entry_shrinker, + &nfs4_xattr_large_entry_lru, + "nfs-xattr_large_entry"); if (!ret) return 0; - unregister_shrinker(&nfs4_xattr_entry_shrinker); -out: - unregister_shrinker(&nfs4_xattr_cache_shrinker); -out1: - list_lru_destroy(&nfs4_xattr_cache_lru); + nfs4_xattr_shrinker_destroy(&nfs4_xattr_entry_shrinker, + &nfs4_xattr_entry_lru); out2: - list_lru_destroy(&nfs4_xattr_entry_lru); -out3: - list_lru_destroy(&nfs4_xattr_large_entry_lru); -out4: + nfs4_xattr_shrinker_destroy(&nfs4_xattr_cache_shrinker, + &nfs4_xattr_cache_lru); +out1: kmem_cache_destroy(nfs4_xattr_cache_cachep); return ret; @@ -1047,11 +1056,11 @@ out4: void nfs4_xattr_cache_exit(void) { - unregister_shrinker(&nfs4_xattr_large_entry_shrinker); - unregister_shrinker(&nfs4_xattr_entry_shrinker); - unregister_shrinker(&nfs4_xattr_cache_shrinker); - list_lru_destroy(&nfs4_xattr_large_entry_lru); - list_lru_destroy(&nfs4_xattr_entry_lru); - list_lru_destroy(&nfs4_xattr_cache_lru); + nfs4_xattr_shrinker_destroy(&nfs4_xattr_large_entry_shrinker, + &nfs4_xattr_large_entry_lru); + nfs4_xattr_shrinker_destroy(&nfs4_xattr_entry_shrinker, + &nfs4_xattr_entry_lru); + nfs4_xattr_shrinker_destroy(&nfs4_xattr_cache_shrinker, + &nfs4_xattr_cache_lru); kmem_cache_destroy(nfs4_xattr_cache_cachep); } -- cgit 1.4.1 From c2bf8d7b8f025f4f8583daf8e6977ef742452b58 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Sun, 18 Jun 2023 17:32:25 -0400 Subject: NFSv4.1: freeze the session table upon receiving NFS4ERR_BADSESSION [ Upstream commit c907e72f58ed979a24a9fdcadfbc447c51d5e509 ] When the client received NFS4ERR_BADSESSION, it schedules recovery and start the state manager thread which in turn freezes the session table and does not allow for any new requests to use the no-longer valid session. However, it is possible that before the state manager thread runs, a new operation would use the released slot that received BADSESSION and was therefore not updated its sequence number. Such re-use of the slot can lead the application errors. Fixes: 5c441544f045 ("NFSv4.x: Handle bad/dead sessions correctly in nfs41_sequence_process()") Signed-off-by: Olga Kornievskaia Signed-off-by: Trond Myklebust Signed-off-by: Sasha Levin --- fs/nfs/nfs4proc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 70e76359909c..177cb7b089b9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -921,6 +921,7 @@ out: out_noaction: return ret; session_recover: + set_bit(NFS4_SLOT_TBL_DRAINING, &session->fc_slot_table.slot_tbl_state); nfs4_schedule_session_recovery(session, status); dprintk("%s ERROR: %d Reset session\n", __func__, status); nfs41_sequence_free_slot(res); -- cgit 1.4.1 From 1bf709b9625001eefdd41048c5f4c7544ee33394 Mon Sep 17 00:00:00 2001 From: Bharath SM Date: Sun, 18 Jun 2023 19:02:24 +0000 Subject: SMB3: Do not send lease break acknowledgment if all file handles have been closed [ Upstream commit da787d5b74983f7525d1eb4b9c0b4aff2821511a ] In case if all existing file handles are deferred handles and if all of them gets closed due to handle lease break then we dont need to send lease break acknowledgment to server, because last handle close will be considered as lease break ack. After closing deferred handels, we check for openfile list of inode, if its empty then we skip sending lease break ack. Fixes: 59a556aebc43 ("SMB3: drop reference to cfile before sending oplock break") Reviewed-by: Tom Talpey Signed-off-by: Bharath SM Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/file.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 87dcffece762..9a367d4c74e4 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -5140,20 +5140,19 @@ oplock_break_ack: _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); /* - * releasing stale oplock after recent reconnect of smb session using - * a now incorrect file handle is not a data integrity issue but do - * not bother sending an oplock release if session to server still is - * disconnected since oplock already released by the server + * MS-SMB2 3.2.5.19.1 and 3.2.5.19.2 (and MS-CIFS 3.2.5.42) do not require + * an acknowledgment to be sent when the file has already been closed. + * check for server null, since can race with kill_sb calling tree disconnect. */ - if (!oplock_break_cancelled) { - /* check for server null since can race with kill_sb calling tree disconnect */ - if (tcon->ses && tcon->ses->server) { - rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, - volatile_fid, net_fid, cinode); - cifs_dbg(FYI, "Oplock release rc = %d\n", rc); - } else - pr_warn_once("lease break not sent for unmounted share\n"); - } + spin_lock(&cinode->open_file_lock); + if (tcon->ses && tcon->ses->server && !oplock_break_cancelled && + !list_empty(&cinode->openFileList)) { + spin_unlock(&cinode->open_file_lock); + rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, + volatile_fid, net_fid, cinode); + cifs_dbg(FYI, "Oplock release rc = %d\n", rc); + } else + spin_unlock(&cinode->open_file_lock); cifs_done_oplock_break(cinode); } -- cgit 1.4.1 From 03859868ab82d57bfdd0cea1bf31f9319a5dded0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 2 Jun 2023 23:13:54 -0700 Subject: dax: Fix dax_mapping_release() use after free [ Upstream commit 6d24b170a9db0456f577b1ab01226a2254c016a8 ] A CONFIG_DEBUG_KOBJECT_RELEASE test of removing a device-dax region provider (like modprobe -r dax_hmem) yields: kobject: 'mapping0' (ffff93eb460e8800): kobject_release, parent 0000000000000000 (delayed 2000) [..] DEBUG_LOCKS_WARN_ON(1) WARNING: CPU: 23 PID: 282 at kernel/locking/lockdep.c:232 __lock_acquire+0x9fc/0x2260 [..] RIP: 0010:__lock_acquire+0x9fc/0x2260 [..] Call Trace: [..] lock_acquire+0xd4/0x2c0 ? ida_free+0x62/0x130 _raw_spin_lock_irqsave+0x47/0x70 ? ida_free+0x62/0x130 ida_free+0x62/0x130 dax_mapping_release+0x1f/0x30 device_release+0x36/0x90 kobject_delayed_cleanup+0x46/0x150 Due to attempting ida_free() on an ida object that has already been freed. Devices typically only hold a reference on their parent while registered. If a child needs a parent object to complete its release it needs to hold a reference that it drops from its release callback. Arrange for a dax_mapping to pin its parent dev_dax instance until dax_mapping_release(). Fixes: 0b07ce872a9e ("device-dax: introduce 'mapping' devices") Signed-off-by: Dan Williams Link: https://lore.kernel.org/r/168577283412.1672036.16111545266174261446.stgit@dwillia2-xfh.jf.intel.com Reviewed-by: Dave Jiang Reviewed-by: Fan Ni Reviewed-by: Ira Weiny Signed-off-by: Vishal Verma Signed-off-by: Sasha Levin --- drivers/dax/bus.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index c64e7076537c..d7a838f65181 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -621,10 +621,12 @@ EXPORT_SYMBOL_GPL(alloc_dax_region); static void dax_mapping_release(struct device *dev) { struct dax_mapping *mapping = to_dax_mapping(dev); - struct dev_dax *dev_dax = to_dev_dax(dev->parent); + struct device *parent = dev->parent; + struct dev_dax *dev_dax = to_dev_dax(parent); ida_free(&dev_dax->ida, mapping->id); kfree(mapping); + put_device(parent); } static void unregister_dax_mapping(void *data) @@ -764,6 +766,7 @@ static int devm_register_dax_mapping(struct dev_dax *dev_dax, int range_id) dev = &mapping->dev; device_initialize(dev); dev->parent = &dev_dax->dev; + get_device(dev->parent); dev->type = &dax_mapping_type; dev_set_name(dev, "mapping%d", mapping->id); rc = device_add(dev); -- cgit 1.4.1 From 7b8106d9057253b16e3eb2f9213ea330eedd22a9 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 2 Jun 2023 23:14:05 -0700 Subject: dax: Introduce alloc_dev_dax_id() [ Upstream commit 70aab281e18c68a1284bc387de127c2fc0bed3f8 ] The reference counting of dax_region objects is needlessly complicated, has lead to confusion [1], and has hidden a bug [2]. Towards cleaning up that mess introduce alloc_dev_dax_id() to minimize the holding of a dax_region reference to only what dev_dax_release() needs, the dax_region->ida. Part of the reason for the mess was the design to dereference a dax_region in all cases in free_dev_dax_id() even if the id was statically assigned by the upper level dax_region driver. Remove the need to call "is_static(dax_region)" by tracking whether the id is dynamic directly in the dev_dax instance itself. With that flag the dax_region pinning and release per dev_dax instance can move to alloc_dev_dax_id() and free_dev_dax_id() respectively. A follow-on cleanup address the unnecessary references in the dax_region setup and drivers. Fixes: 0f3da14a4f05 ("device-dax: introduce 'seed' devices") Link: http://lore.kernel.org/r/20221203095858.612027-1-liuyongqiang13@huawei.com [1] Link: http://lore.kernel.org/r/3cf0890b-4eb0-e70e-cd9c-2ecc3d496263@hpe.com [2] Reported-by: Yongqiang Liu Reported-by: Paul Cassella Reported-by: Ira Weiny Signed-off-by: Dan Williams Link: https://lore.kernel.org/r/168577284563.1672036.13493034988900989554.stgit@dwillia2-xfh.jf.intel.com Reviewed-by: Ira Weiny Signed-off-by: Vishal Verma Signed-off-by: Sasha Levin --- drivers/dax/bus.c | 56 ++++++++++++++++++++++++++++------------------- drivers/dax/dax-private.h | 4 +++- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index d7a838f65181..3a9348c7f728 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -432,18 +432,34 @@ static void unregister_dev_dax(void *dev) put_device(dev); } +static void dax_region_free(struct kref *kref) +{ + struct dax_region *dax_region; + + dax_region = container_of(kref, struct dax_region, kref); + kfree(dax_region); +} + +void dax_region_put(struct dax_region *dax_region) +{ + kref_put(&dax_region->kref, dax_region_free); +} +EXPORT_SYMBOL_GPL(dax_region_put); + /* a return value >= 0 indicates this invocation invalidated the id */ static int __free_dev_dax_id(struct dev_dax *dev_dax) { - struct dax_region *dax_region = dev_dax->region; struct device *dev = &dev_dax->dev; + struct dax_region *dax_region; int rc = dev_dax->id; device_lock_assert(dev); - if (is_static(dax_region) || dev_dax->id < 0) + if (!dev_dax->dyn_id || dev_dax->id < 0) return -1; + dax_region = dev_dax->region; ida_free(&dax_region->ida, dev_dax->id); + dax_region_put(dax_region); dev_dax->id = -1; return rc; } @@ -459,6 +475,20 @@ static int free_dev_dax_id(struct dev_dax *dev_dax) return rc; } +static int alloc_dev_dax_id(struct dev_dax *dev_dax) +{ + struct dax_region *dax_region = dev_dax->region; + int id; + + id = ida_alloc(&dax_region->ida, GFP_KERNEL); + if (id < 0) + return id; + kref_get(&dax_region->kref); + dev_dax->dyn_id = true; + dev_dax->id = id; + return id; +} + static ssize_t delete_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -546,20 +576,6 @@ static const struct attribute_group *dax_region_attribute_groups[] = { NULL, }; -static void dax_region_free(struct kref *kref) -{ - struct dax_region *dax_region; - - dax_region = container_of(kref, struct dax_region, kref); - kfree(dax_region); -} - -void dax_region_put(struct dax_region *dax_region) -{ - kref_put(&dax_region->kref, dax_region_free); -} -EXPORT_SYMBOL_GPL(dax_region_put); - static void dax_region_unregister(void *region) { struct dax_region *dax_region = region; @@ -1284,12 +1300,10 @@ static const struct attribute_group *dax_attribute_groups[] = { static void dev_dax_release(struct device *dev) { struct dev_dax *dev_dax = to_dev_dax(dev); - struct dax_region *dax_region = dev_dax->region; struct dax_device *dax_dev = dev_dax->dax_dev; put_dax(dax_dev); free_dev_dax_id(dev_dax); - dax_region_put(dax_region); kfree(dev_dax->pgmap); kfree(dev_dax); } @@ -1313,6 +1327,7 @@ struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data) if (!dev_dax) return ERR_PTR(-ENOMEM); + dev_dax->region = dax_region; if (is_static(dax_region)) { if (dev_WARN_ONCE(parent, data->id < 0, "dynamic id specified to static region\n")) { @@ -1328,13 +1343,11 @@ struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data) goto err_id; } - rc = ida_alloc(&dax_region->ida, GFP_KERNEL); + rc = alloc_dev_dax_id(dev_dax); if (rc < 0) goto err_id; - dev_dax->id = rc; } - dev_dax->region = dax_region; dev = &dev_dax->dev; device_initialize(dev); dev_set_name(dev, "dax%d.%d", dax_region->id, dev_dax->id); @@ -1375,7 +1388,6 @@ struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data) dev_dax->target_node = dax_region->target_node; dev_dax->align = dax_region->align; ida_init(&dev_dax->ida); - kref_get(&dax_region->kref); inode = dax_inode(dax_dev); dev->devt = inode->i_rdev; diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index 1c974b7caae6..afcada6fd2ed 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -52,7 +52,8 @@ struct dax_mapping { * @region - parent region * @dax_dev - core dax functionality * @target_node: effective numa node if dev_dax memory range is onlined - * @id: ida allocated id + * @dyn_id: is this a dynamic or statically created instance + * @id: ida allocated id when the dax_region is not static * @ida: mapping id allocator * @dev - device core * @pgmap - pgmap for memmap setup / lifetime (driver owned) @@ -64,6 +65,7 @@ struct dev_dax { struct dax_device *dax_dev; unsigned int align; int target_node; + bool dyn_id; int id; struct ida ida; struct device dev; -- cgit 1.4.1 From d88158d8161734a89d79fc86256e48281b9b1ecd Mon Sep 17 00:00:00 2001 From: Tarun Sahu Date: Wed, 21 Jun 2023 21:20:25 +0530 Subject: dax/kmem: Pass valid argument to memory_group_register_static [ Upstream commit 46e66dab8565f742374e9cc4ff7d35f344d774e2 ] memory_group_register_static takes maximum number of pages as the argument while dev_dax_kmem_probe passes total_len (in bytes) as the argument. IIUC, I don't see any crash/panic impact as such. As, memory_group_register_static just set the max_pages limit which is used in auto_movable_zone_for_pfn to determine the zone. which might cause these condition to behave differently, This will be true always so jump will happen to kernel_zone ... if (!auto_movable_can_online_movable(NUMA_NO_NODE, group, nr_pages)) goto kernel_zone; ... kernel_zone: return default_kernel_zone_for_pfn(nid, pfn, nr_pages); Here, In below, zone_intersects compare range will be larger as nr_pages will be higher (derived from total_len passed in dev_dax_kmem_probe). ... static struct zone *default_kernel_zone_for_pfn(int nid, unsigned long start_pfn, unsigned long nr_pages) { struct pglist_data *pgdat = NODE_DATA(nid); int zid; for (zid = 0; zid < ZONE_NORMAL; zid++) { struct zone *zone = &pgdat->node_zones[zid]; if (zone_intersects(zone, start_pfn, nr_pages)) return zone; } return &pgdat->node_zones[ZONE_NORMAL]; } Incorrect zone will be returned here, which in later time might cause bigger problem. Fixes: eedf634aac3b ("dax/kmem: use a single static memory group for a single probed unit") Signed-off-by: Tarun Sahu Link: https://lore.kernel.org/r/20230621155025.370672-1-tsahu@linux.ibm.com Reviewed-by: Vishal Verma Signed-off-by: Vishal Verma Signed-off-by: Sasha Levin --- drivers/dax/kmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c index 4aa758a2b3d1..1a33616ceb06 100644 --- a/drivers/dax/kmem.c +++ b/drivers/dax/kmem.c @@ -99,7 +99,7 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) if (!data->res_name) goto err_res_name; - rc = memory_group_register_static(numa_node, total_len); + rc = memory_group_register_static(numa_node, PFN_UP(total_len)); if (rc < 0) goto err_reg_mgid; data->mgid = rc; -- cgit 1.4.1 From c5696a8a5484c8b6d6c5d3d4f7a3ef5fd46f2358 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Fri, 16 Jun 2023 09:58:13 +0100 Subject: hwrng: st - keep clock enabled while hwrng is registered [ Upstream commit 501e197a02d4aef157f53ba3a0b9049c3e52fedc ] The st-rng driver uses devres to register itself with the hwrng core, the driver will be unregistered from hwrng when its device goes out of scope. This happens after the driver's remove function is called. However, st-rng's clock is disabled in the remove function. There's a short timeframe where st-rng is still registered with the hwrng core although its clock is disabled. I suppose the clock must be active to access the hardware and serve requests from the hwrng core. Switch to devm_clk_get_enabled and let devres disable the clock and unregister the hwrng. This avoids the race condition. Fixes: 3e75241be808 ("hwrng: drivers - Use device-managed registration API") Signed-off-by: Martin Kaiser Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/char/hw_random/st-rng.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/drivers/char/hw_random/st-rng.c b/drivers/char/hw_random/st-rng.c index 15ba1e6fae4d..6e9dfac9fc9f 100644 --- a/drivers/char/hw_random/st-rng.c +++ b/drivers/char/hw_random/st-rng.c @@ -42,7 +42,6 @@ struct st_rng_data { void __iomem *base; - struct clk *clk; struct hwrng ops; }; @@ -85,26 +84,18 @@ static int st_rng_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - clk = devm_clk_get(&pdev->dev, NULL); + clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) return PTR_ERR(clk); - ret = clk_prepare_enable(clk); - if (ret) - return ret; - ddata->ops.priv = (unsigned long)ddata; ddata->ops.read = st_rng_read; ddata->ops.name = pdev->name; ddata->base = base; - ddata->clk = clk; - - dev_set_drvdata(&pdev->dev, ddata); ret = devm_hwrng_register(&pdev->dev, &ddata->ops); if (ret) { dev_err(&pdev->dev, "Failed to register HW RNG\n"); - clk_disable_unprepare(clk); return ret; } @@ -113,15 +104,6 @@ static int st_rng_probe(struct platform_device *pdev) return 0; } -static int st_rng_remove(struct platform_device *pdev) -{ - struct st_rng_data *ddata = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(ddata->clk); - - return 0; -} - static const struct of_device_id st_rng_match[] __maybe_unused = { { .compatible = "st,rng" }, {}, @@ -134,7 +116,6 @@ static struct platform_driver st_rng_driver = { .of_match_table = of_match_ptr(st_rng_match), }, .probe = st_rng_probe, - .remove = st_rng_remove }; module_platform_driver(st_rng_driver); -- cgit 1.4.1 From 1e596c181c5344979c99ca1ee69517abeda645ff Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 23 Jun 2023 00:11:43 +0000 Subject: kbuild: Disable GCOV for *.mod.o [ Upstream commit 25a21fbb934a0d989e1858f83c2ddf4cfb2ebe30 ] With GCOV_PROFILE_ALL, Clang injects __llvm_gcov_* functions to each object file, including the *.mod.o. As we filter out CC_FLAGS_CFI for *.mod.o, the compiler won't generate type hashes for the injected functions, and therefore indirectly calling them during module loading trips indirect call checking. Enabling CFI for *.mod.o isn't sufficient to fix this issue after commit 0c3e806ec0f9 ("x86/cfi: Add boot time hash randomization"), as *.mod.o aren't processed by objtool, which means any hashes emitted there won't be randomized. Therefore, in addition to disabling CFI for *.mod.o, also disable GCOV, as the object files don't otherwise contain any executable code. Fixes: cf68fffb66d6 ("add support for Clang CFI") Reported-by: Joe Fradley Signed-off-by: Sami Tolvanen Acked-by: Peter Zijlstra (Intel) Reviewed-by: Kees Cook Reviewed-by: Nick Desaulniers Signed-off-by: Masahiro Yamada Signed-off-by: Sasha Levin --- scripts/Makefile.modfinal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal index 25bedd83644b..3af5e5807983 100644 --- a/scripts/Makefile.modfinal +++ b/scripts/Makefile.modfinal @@ -23,7 +23,7 @@ modname = $(notdir $(@:.mod.o=)) part-of-module = y quiet_cmd_cc_o_c = CC [M] $@ - cmd_cc_o_c = $(CC) $(filter-out $(CC_FLAGS_CFI), $(c_flags)) -c -o $@ $< + cmd_cc_o_c = $(CC) $(filter-out $(CC_FLAGS_CFI) $(CFLAGS_GCOV), $(c_flags)) -c -o $@ $< %.mod.o: %.mod.c FORCE $(call if_changed_dep,cc_o_c) -- cgit 1.4.1 From e28d7a3f4bc5e9ed72984952c2ff449254b1dda0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 27 Jun 2023 09:33:09 +0200 Subject: efi/libstub: Disable PCI DMA before grabbing the EFI memory map [ Upstream commit 2e28a798c3092ea42b968fa16ac835969d124898 ] Currently, the EFI stub will disable PCI DMA as the very last thing it does before calling ExitBootServices(), to avoid interfering with the firmware's normal operation as much as possible. However, the stub will invoke DisconnectController() on all endpoints downstream of the PCI bridges it disables, and this may affect the layout of the EFI memory map, making it substantially more likely that ExitBootServices() will fail the first time around, and that the EFI memory map needs to be reloaded. This, in turn, increases the likelihood that the slack space we allocated is insufficient (and we can no longer allocate memory via boot services after having called ExitBootServices() once), causing the second call to GetMemoryMap (and therefore the boot) to fail. This makes the PCI DMA disable feature a bit more fragile than it already is, so let's make it more robust, by allocating the space for the EFI memory map after disabling PCI DMA. Fixes: 4444f8541dad16fe ("efi: Allow disabling PCI busmastering on bridges during boot") Reported-by: Glenn Washburn Acked-by: Matthew Garrett Signed-off-by: Ard Biesheuvel Signed-off-by: Sasha Levin --- drivers/firmware/efi/libstub/efi-stub-helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 0c493521b25b..3d9b2469a0df 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -521,6 +521,9 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv, struct efi_boot_memmap *map; efi_status_t status; + if (efi_disable_pci_dma) + efi_pci_disable_bridge_busmaster(); + status = efi_get_memory_map(&map, true); if (status != EFI_SUCCESS) return status; @@ -531,9 +534,6 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv, return status; } - if (efi_disable_pci_dma) - efi_pci_disable_bridge_busmaster(); - status = efi_bs_call(exit_boot_services, handle, map->map_key); if (status == EFI_INVALID_PARAMETER) { -- cgit 1.4.1 From 4fe07d55a5461e66a55fbefb57f85ff0facea32b Mon Sep 17 00:00:00 2001 From: Shyam Prasad N Date: Thu, 22 Jun 2023 18:16:03 +0000 Subject: cifs: prevent use-after-free by freeing the cfile later [ Upstream commit 33f736187d08f6bc822117629f263b97d3df4165 ] In smb2_compound_op we have a possible use-after-free which can cause hard to debug problems later on. This was revealed during stress testing with KASAN enabled kernel. Fixing it by moving the cfile free call to a few lines below, after the usage. Fixes: 76894f3e2f71 ("cifs: improve symlink handling for smb2+") Reviewed-by: Paulo Alcantara (SUSE) Signed-off-by: Shyam Prasad N Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/smb2inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index c97e049e29dd..57526bdbab17 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -397,9 +397,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, rsp_iov); finished: - if (cfile) - cifsFileInfo_put(cfile); - SMB2_open_free(&rqst[0]); if (rc == -EREMCHG) { pr_warn_once("server share %s deleted\n", tcon->tree_name); @@ -513,6 +510,9 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, break; } + if (cfile) + cifsFileInfo_put(cfile); + if (rc && err_iov && err_buftype) { memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov)); memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype)); -- cgit 1.4.1 From 9fb981a86ae5008f05018e6ecff052b2ea9daecc Mon Sep 17 00:00:00 2001 From: Shyam Prasad N Date: Thu, 22 Jun 2023 18:16:04 +0000 Subject: cifs: do all necessary checks for credits within or before locking [ Upstream commit 326a8d04f147e2bf393f6f9cdb74126ee6900607 ] All the server credits and in-flight info is protected by req_lock. Once the req_lock is held, and we've determined that we have enough credits to continue, this lock cannot be dropped till we've made the changes to credits and in-flight count. However, we used to drop the lock in order to avoid deadlock with the recent srv_lock. This could cause the checks already made to be invalidated. Fixed it by moving the server status check to before locking req_lock. Fixes: d7d7a66aacd6 ("cifs: avoid use of global locks for high contention data") Signed-off-by: Shyam Prasad N Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/smb2ops.c | 19 ++++++++++--------- fs/smb/client/transport.c | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index d512440d35b6..e6a191a7499e 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -208,6 +208,16 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, spin_lock(&server->req_lock); while (1) { + spin_unlock(&server->req_lock); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + spin_lock(&server->req_lock); if (server->credits <= 0) { spin_unlock(&server->req_lock); cifs_num_waiters_inc(server); @@ -218,15 +228,6 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, return rc; spin_lock(&server->req_lock); } else { - spin_unlock(&server->req_lock); - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - spin_lock(&server->req_lock); scredits = server->credits; /* can deadlock with reopen */ if (scredits <= 8) { diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index c961b90f92b9..e03ffcf7e201 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -549,6 +549,16 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, } while (1) { + spin_unlock(&server->req_lock); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + spin_lock(&server->req_lock); if (*credits < num_credits) { scredits = *credits; spin_unlock(&server->req_lock); @@ -574,15 +584,6 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, return -ERESTARTSYS; spin_lock(&server->req_lock); } else { - spin_unlock(&server->req_lock); - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - /* * For normal commands, reserve the last MAX_COMPOUND * credits to compound requests. @@ -596,7 +597,6 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, * for servers that are slow to hand out credits on * new sessions. */ - spin_lock(&server->req_lock); if (!optype && num_credits == 1 && server->in_flight > 2 * MAX_COMPOUND && *credits <= MAX_COMPOUND) { -- cgit 1.4.1 From babaab6ef64166abcc7a49717319d826ced155d6 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Tue, 27 Jun 2023 21:24:49 -0300 Subject: smb: client: fix broken file attrs with nodfs mounts [ Upstream commit d439b29057e26464120fc6c18f97433aa003b5fe ] *_get_inode_info() functions expect -EREMOTE when query path info calls find a DFS link, regardless whether !CONFIG_CIFS_DFS_UPCALL or 'nodfs' mount option. Otherwise, those files will miss the fake DFS file attributes. Before patch $ mount.cifs //srv/dfs /mnt/1 -o ...,nodfs $ ls -l /mnt/1 ls: cannot access '/mnt/1/link': Operation not supported total 0 -rwxr-xr-x 1 root root 0 Jul 26 2022 dfstest2_file1.txt drwxr-xr-x 2 root root 0 Aug 8 2022 dir1 d????????? ? ? ? ? ? link After patch $ mount.cifs //srv/dfs /mnt/1 -o ...,nodfs $ ls -l /mnt/1 total 0 -rwxr-xr-x 1 root root 0 Jul 26 2022 dfstest2_file1.txt drwxr-xr-x 2 root root 0 Aug 8 2022 dir1 drwx--x--x 2 root root 0 Jun 26 20:29 link Fixes: c877ce47e137 ("cifs: reduce roundtrips on create/qinfo requests") Signed-off-by: Paulo Alcantara (SUSE) Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/smb2inode.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 57526bdbab17..5ddc629e6216 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -592,9 +592,6 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, if (islink) rc = -EREMOTE; } - if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) - rc = -EOPNOTSUPP; } out: -- cgit 1.4.1 From be54803be8b95a22377dd641f956e7be7bef3fdd Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 19 Jun 2023 10:19:38 +0200 Subject: ksmbd: avoid field overflow warning [ Upstream commit 9cedc58bdbe9fff9aacd0ca19ee5777659f28fd7 ] clang warns about a possible field overflow in a memcpy: In file included from fs/smb/server/smb_common.c:7: include/linux/fortify-string.h:583:4: error: call to '__write_overflow_field' declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror,-Wattribute-warning] __write_overflow_field(p_size_field, size); It appears to interpret the "&out[baselen + 4]" as referring to a single byte of the character array, while the equivalen "out + baselen + 4" is seen as an offset into the array. I don't see that kind of warning elsewhere, so just go with the simple rework. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Signed-off-by: Arnd Bergmann Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smb_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index 05d7f3e910bf..d937e2f45c82 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -536,7 +536,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, out[baselen + 3] = PERIOD; if (dot_present) - memcpy(&out[baselen + 4], extension, 4); + memcpy(out + baselen + 4, extension, 4); else out[baselen + 4] = '\0'; smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, -- cgit 1.4.1 From 97669214944e80d3756657c21c4f286f3da6a423 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 28 Jun 2023 16:56:05 +0100 Subject: arm64: sme: Use STR P to clear FFR context field in streaming SVE mode [ Upstream commit 893b24181b4c4bf1fa2841b1ed192e5413a97cb1 ] The FFR is a predicate register which can vary between 16 and 256 bits in size depending upon the configured vector length. When saving the SVE state in streaming SVE mode, the FFR register is inaccessible and so commit 9f5848665788 ("arm64/sve: Make access to FFR optional") simply clears the FFR field of the in-memory context structure. Unfortunately, it achieves this using an unconditional 8-byte store and so if the SME vector length is anything other than 64 bytes in size we will either fail to clear the entire field or, worse, we will corrupt memory immediately following the structure. This has led to intermittent kfence splats in CI [1] and can trigger kmalloc Redzone corruption messages when running the 'fp-stress' kselftest: | ============================================================================= | BUG kmalloc-1k (Not tainted): kmalloc Redzone overwritten | ----------------------------------------------------------------------------- | | 0xffff000809bf1e22-0xffff000809bf1e27 @offset=7714. First byte 0x0 instead of 0xcc | Allocated in do_sme_acc+0x9c/0x220 age=2613 cpu=1 pid=531 | __kmalloc+0x8c/0xcc | do_sme_acc+0x9c/0x220 | ... Replace the 8-byte store with a store of a predicate register which has been zero-initialised with PFALSE, ensuring that the entire field is cleared in memory. [1] https://lore.kernel.org/r/CA+G9fYtU7HsV0R0dp4XEH5xXHSJFw8KyDf5VQrLLfMxWfxQkag@mail.gmail.com Cc: Mark Brown Cc: Mark Rutland Cc: Naresh Kamboju Fixes: 9f5848665788 ("arm64/sve: Make access to FFR optional") Reported-by: Linux Kernel Functional Testing Signed-off-by: Will Deacon Reviewed-by: Mark Brown Tested-by: Anders Roxell Link: https://lore.kernel.org/r/20230628155605.22296-1-will@kernel.org Signed-off-by: Catalin Marinas Signed-off-by: Sasha Levin --- arch/arm64/include/asm/fpsimdmacros.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/fpsimdmacros.h b/arch/arm64/include/asm/fpsimdmacros.h index 5e0910cf4832..696d247cf8fb 100644 --- a/arch/arm64/include/asm/fpsimdmacros.h +++ b/arch/arm64/include/asm/fpsimdmacros.h @@ -294,12 +294,12 @@ _for n, 0, 15, _sve_str_p \n, \nxbase, \n - 16 cbz \save_ffr, 921f _sve_rdffr 0 - _sve_str_p 0, \nxbase - _sve_ldr_p 0, \nxbase, -16 b 922f 921: - str xzr, [x\nxbase] // Zero out FFR + _sve_pfalse 0 // Zero out FFR 922: + _sve_str_p 0, \nxbase + _sve_ldr_p 0, \nxbase, -16 mrs x\nxtmp, fpsr str w\nxtmp, [\xpfpsr] mrs x\nxtmp, fpcr -- cgit 1.4.1 From b6872b4a7d8b4e3697b5146b57d0297a849d26e4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 29 Jun 2023 21:35:19 +0200 Subject: x86/efi: Make efi_set_virtual_address_map IBT safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0303c9729afc4094ef53e552b7b8cff7436028d6 ] Niklāvs reported a boot regression on an Alderlake machine and bisected it to commit 9df9d2f0471b ("init: Invoke arch_cpu_finalize_init() earlier"). By moving the invocation of arch_cpu_finalize_init() further down he identified that efi_enter_virtual_mode() is the function which causes the boot hang. The main difference of the earlier invocation is that the boot CPU is already fully initialized and mitigations and alternatives are applied. But the only really interesting change turned out to be IBT, which is now enabled before efi_enter_virtual_mode(). "ibt=off" on the kernel command line cured the problem. Inspection of the involved calls in efi_enter_virtual_mode() unearthed that efi_set_virtual_address_map() is the only place in the kernel which invokes an EFI call without the IBT safe wrapper. This went obviously unnoticed so far as IBT was enabled later. Use arch_efi_call_virt() instead of efi_call() to cure that. Fixes: fe379fa4d199 ("x86/ibt: Disable IBT around firmware") Fixes: 9df9d2f0471b ("init: Invoke arch_cpu_finalize_init() earlier") Reported-by: Niklāvs Koļesņikovs Signed-off-by: Thomas Gleixner Reviewed-by: Ard Biesheuvel Link: https://bugzilla.kernel.org/show_bug.cgi?id=217602 Link: https://lore.kernel.org/r/87jzvm12q0.ffs@tglx Signed-off-by: Sasha Levin --- arch/x86/platform/efi/efi_64.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index b36596bf0fc3..601908bdbead 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -847,9 +847,9 @@ efi_set_virtual_address_map(unsigned long memory_map_size, /* Disable interrupts around EFI calls: */ local_irq_save(flags); - status = efi_call(efi.runtime->set_virtual_address_map, - memory_map_size, descriptor_size, - descriptor_version, virtual_map); + status = arch_efi_call_virt(efi.runtime, set_virtual_address_map, + memory_map_size, descriptor_size, + descriptor_version, virtual_map); local_irq_restore(flags); efi_fpu_end(); -- cgit 1.4.1 From f57e2c083040bf3427b0db5c1c0a87b31c6428c0 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Fri, 16 Jun 2023 09:21:36 +0800 Subject: md/raid1-10: fix casting from randomized structure in raid1_submit_write() commit b5a99602b74bbfa655be509c615181dd95b0719e upstream. Following build error triggered while build with clang version 17.0.0 with W=1(this can't be reporduced with gcc 13.1.0): drivers/md/raid1-10.c:117:25: error: casting from randomized structure pointer type 'struct block_device *' to 'struct md_rdev *' 117 | struct md_rdev *rdev = (struct md_rdev *)bio->bi_bdev; | ^ Fix this by casting 'bio->bi_bdev' to 'void *', as it used to be. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202306142042.fmjfmTF8-lkp@intel.com/ Fixes: 8295efbe68c0 ("md/raid1-10: factor out a helper to submit normal write") Signed-off-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230616012136.3047071-1-yukuai1@huaweicloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid1-10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c index 73cc3cb9154d..e0c8ac814633 100644 --- a/drivers/md/raid1-10.c +++ b/drivers/md/raid1-10.c @@ -113,7 +113,7 @@ static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp, static inline void raid1_submit_write(struct bio *bio) { - struct md_rdev *rdev = (struct md_rdev *)bio->bi_bdev; + struct md_rdev *rdev = (void *)bio->bi_bdev; bio->bi_next = NULL; bio_set_dev(bio, rdev->bdev); -- cgit 1.4.1 From a0b369620326c5aac4898d8d884a3084bed07db1 Mon Sep 17 00:00:00 2001 From: Davide Tronchin Date: Thu, 22 Jun 2023 11:29:21 +0200 Subject: USB: serial: option: add LARA-R6 01B PIDs commit ffa5f7a3bf28c1306eef85d4056539c2d4b8eb09 upstream. The new LARA-R6 product variant identified by the "01B" string can be configured (by AT interface) in three different USB modes: * Default mode (Vendor ID: 0x1546 Product ID: 0x1311) with 4 serial interfaces * RmNet mode (Vendor ID: 0x1546 Product ID: 0x1312) with 4 serial interfaces and 1 RmNet virtual network interface * CDC-ECM mode (Vendor ID: 0x1546 Product ID: 0x1313) with 4 serial interface and 1 CDC-ECM virtual network interface The first 4 interfaces of all the 3 USB configurations (default, RmNet, CDC-ECM) are the same. In default mode LARA-R6 01B exposes the following interfaces: If 0: Diagnostic If 1: AT parser If 2: AT parser If 3: AT parser/alternative functions In RmNet mode LARA-R6 01B exposes the following interfaces: If 0: Diagnostic If 1: AT parser If 2: AT parser If 3: AT parser/alternative functions If 4: RMNET interface In CDC-ECM mode LARA-R6 01B exposes the following interfaces: If 0: Diagnostic If 1: AT parser If 2: AT parser If 3: AT parser/alternative functions If 4: CDC-ECM interface Signed-off-by: Davide Tronchin Link: https://lore.kernel.org/r/20230622092921.12651-1-davide.tronchin.94@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e12fec4c2e2f..6692440c1e0a 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1151,6 +1151,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x90fa), .driver_info = RSVD(3) }, /* u-blox products */ + { USB_DEVICE(UBLOX_VENDOR_ID, 0x1311) }, /* u-blox LARA-R6 01B */ + { USB_DEVICE(UBLOX_VENDOR_ID, 0x1312), /* u-blox LARA-R6 01B (RMNET) */ + .driver_info = RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(UBLOX_VENDOR_ID, 0x1313, 0xff) }, /* u-blox LARA-R6 01B (ECM) */ { USB_DEVICE(UBLOX_VENDOR_ID, 0x1341) }, /* u-blox LARA-L6 */ { USB_DEVICE(UBLOX_VENDOR_ID, 0x1342), /* u-blox LARA-L6 (RMNET) */ .driver_info = RSVD(4) }, -- cgit 1.4.1 From c2a0884134383b7ef48f16758150ce23868b3e6d Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sun, 18 Jun 2023 17:39:49 +0530 Subject: usb: dwc3: gadget: Propagate core init errors to UDC during pullup commit c0aabed9cabe057309779a9e26fe86a113d24dad upstream. In scenarios where pullup relies on resume (get sync) to initialize the controller and set the run stop bit, then core_init is followed by gadget_resume which will eventually set run stop bit. But in cases where the core_init fails, the return value is not sent back to udc appropriately. So according to UDC the controller has started but in reality we never set the run stop bit. On systems like Android, there are uevents sent to HAL depending on whether the configfs_bind / configfs_disconnect were invoked. In the above mentioned scnenario, if the core init fails, the run stop won't be set and the cable plug-out won't result in generation of any disconnect event and userspace would never get any uevent regarding cable plug out and we never call pullup(0) again. Furthermore none of the next Plug-In/Plug-Out's would be known to configfs. Return back the appropriate result to UDC to let the userspace/ configfs know that the pullup failed so they can take appropriate action. Fixes: 77adb8bdf422 ("usb: dwc3: gadget: Allow runtime suspend if UDC unbinded") Cc: stable Signed-off-by: Krishna Kurapati Acked-by: Thinh Nguyen Message-ID: <20230618120949.14868-1-quic_kriskura@quicinc.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7e94d31687e9..c3590a0c4203 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2631,7 +2631,9 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) ret = pm_runtime_get_sync(dwc->dev); if (!ret || ret < 0) { pm_runtime_put(dwc->dev); - return 0; + if (ret < 0) + pm_runtime_set_suspended(dwc->dev); + return ret; } if (dwc->pullups_connected == is_on) { -- cgit 1.4.1 From 238edc04ddb9d272b38f5419bcd419ad3b92b91b Mon Sep 17 00:00:00 2001 From: EJ Hsu Date: Fri, 9 Jun 2023 14:29:32 +0800 Subject: phy: tegra: xusb: Clear the driver reference in usb-phy dev commit c0c2fcb1325d0d4f3b322b5ee49385f8eca2560d upstream. For the dual-role port, it will assign the phy dev to usb-phy dev and use the port dev driver as the dev driver of usb-phy. When we try to destroy the port dev, it will destroy its dev driver as well. But we did not remove the reference from usb-phy dev. This might cause the use-after-free issue in KASAN. Fixes: e8f7d2f409a1 ("phy: tegra: xusb: Add usb-phy support") Cc: stable@vger.kernel.org Signed-off-by: EJ Hsu Signed-off-by: Haotien Hsu Acked-by: Thierry Reding Acked-by: Jon Hunter Link: https://lore.kernel.org/r/20230609062932.3276509-1-haotienh@nvidia.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/tegra/xusb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index ce14645a86ec..c3fa22ee2d8d 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -562,6 +562,7 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port) usb_role_switch_unregister(port->usb_role_sw); cancel_work_sync(&port->usb_phy_work); usb_remove_phy(&port->usb_phy); + port->usb_phy.dev->driver = NULL; } if (port->ops->remove) -- cgit 1.4.1 From b61f26a8a048efcf2f5c9e0ce7fbb4402e699820 Mon Sep 17 00:00:00 2001 From: Fabrizio Lamarque Date: Tue, 30 May 2023 09:53:07 +0200 Subject: iio: adc: ad7192: Fix null ad7192_state pointer access commit 9e58e3a6f8e1c483c86a04903b7b7aa0923e4426 upstream. Pointer to indio_dev structure is obtained via spi_get_drvdata() at the beginning of function ad7192_setup(), but the spi->dev->driver_data member is not initialized, hence a NULL pointer is returned. Fix by changing ad7192_setup() signature to take pointer to struct iio_dev, and get ad7192_state pointer via st = iio_priv(indio_dev); Fixes: bd5dcdeb3fd0 ("iio: adc: ad7192: convert to device-managed functions") Signed-off-by: Fabrizio Lamarque Reviewed-by: Nuno Sa Cc: Link: https://lore.kernel.org/r/20230530075311.400686-2-fl.scratchpad@gmail.com Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad7192.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index a8f6fa48daa8..c15fa10a469c 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -381,9 +381,9 @@ static int ad7192_of_clock_select(struct ad7192_state *st) return clock_sel; } -static int ad7192_setup(struct ad7192_state *st, struct device_node *np) +static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) { - struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi); + struct ad7192_state *st = iio_priv(indio_dev); bool rej60_en, refin2_en; bool buf_en, bipolar, burnout_curr_en; unsigned long long scale_uv; @@ -1078,7 +1078,7 @@ static int ad7192_probe(struct spi_device *spi) } } - ret = ad7192_setup(st, spi->dev.of_node); + ret = ad7192_setup(indio_dev, spi->dev.of_node); if (ret) return ret; -- cgit 1.4.1 From fcdae54e3d2d9d4d53caf1f9e63fd0961981c4e3 Mon Sep 17 00:00:00 2001 From: Fabrizio Lamarque Date: Tue, 30 May 2023 09:53:08 +0200 Subject: iio: adc: ad7192: Fix internal/external clock selection commit f7d9e21dd274b97dc0a8dbc136a2ea8506063a96 upstream. Fix wrong selection of internal clock when mclk is defined. Resolve a logical inversion introduced in c9ec2cb328e3. Fixes: c9ec2cb328e3 ("iio: adc: ad7192: use devm_clk_get_optional() for mclk") Signed-off-by: Fabrizio Lamarque Reviewed-by: Nuno Sa Cc: Link: https://lore.kernel.org/r/20230530075311.400686-3-fl.scratchpad@gmail.com Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad7192.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index c15fa10a469c..8720ac43a4a4 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -368,7 +368,7 @@ static int ad7192_of_clock_select(struct ad7192_state *st) clock_sel = AD7192_CLK_INT; /* use internal clock */ - if (st->mclk) { + if (!st->mclk) { if (of_property_read_bool(np, "adi,int-clock-output-enable")) clock_sel = AD7192_CLK_INT_CO; } else { -- cgit 1.4.1 From 04a579517b88d577cb49c5f0a1182ae37e807bae Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 5 Jun 2023 12:32:22 +0200 Subject: iio: accel: fxls8962af: errata bug only applicable for FXLS8962AF commit b410a9307bc3a7cdee3c930c98f6fc9cf1d2c484 upstream. Remove special errata handling if FXLS8964AF is used. Fixes: af959b7b96b8 ("iio: accel: fxls8962af: fix errata bug E3 - I2C burst reads") Signed-off-by: Sean Nyekjaer Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230605103223.1400980-2-sean@geanix.com Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/fxls8962af-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index 8874d6d61725..68672372996f 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -905,9 +905,10 @@ static int fxls8962af_fifo_transfer(struct fxls8962af_data *data, int total_length = samples * sample_length; int ret; - if (i2c_verify_client(dev)) + if (i2c_verify_client(dev) && + data->chip_info->chip_id == FXLS8962AF_DEVICE_ID) /* - * Due to errata bug: + * Due to errata bug (only applicable on fxls8962af): * E3: FIFO burst read operation error using I2C interface * We have to avoid burst reads on I2C.. */ -- cgit 1.4.1 From 96b1bc9a6f6555acf460998c058c683127565b54 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 5 Jun 2023 12:32:21 +0200 Subject: iio: accel: fxls8962af: fixup buffer scan element type commit d1cfbd52ede5e5fabc09992894c5733b4057f159 upstream. Scan elements for x,y,z channels is little endian and requires no bit shifts. LE vs. BE is controlled in register SENS_CONFIG2 and bit LE_BE, default value is LE. Fixes: a3e0b51884ee ("iio: accel: add support for FXLS8962AF/FXLS8964AF accelerometers") Signed-off-by: Sean Nyekjaer Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230605103223.1400980-1-sean@geanix.com Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/fxls8962af-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index 68672372996f..8bc516a57e35 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -725,8 +725,7 @@ static const struct iio_event_spec fxls8962af_event[] = { .sign = 's', \ .realbits = 12, \ .storagebits = 16, \ - .shift = 4, \ - .endianness = IIO_BE, \ + .endianness = IIO_LE, \ }, \ .event_spec = fxls8962af_event, \ .num_event_specs = ARRAY_SIZE(fxls8962af_event), \ -- cgit 1.4.1 From b91748bdbfb10673bc128179eb71cf66cb9641c4 Mon Sep 17 00:00:00 2001 From: Hersen Wu Date: Mon, 26 Jun 2023 13:40:58 -0400 Subject: Revert "drm/amd/display: edp do not add non-edid timings" commit d6149086b45e150c170beaa4546495fd1880724c upstream. This change causes regression when eDP and external display in mirror mode. When external display supports low resolution than eDP, use eDP timing to driver external display may cause corruption on external display. This reverts commit e749dd10e5f292061ad63d2b030194bf7d7d452c. Cc: stable@vger.kernel.org Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2655 Signed-off-by: Hersen Wu Reviewed-by: Mario Limonciello Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index b854eec2787e..9be3769d68a8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -6972,13 +6972,7 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) drm_add_modes_noedid(connector, 640, 480); } else { amdgpu_dm_connector_ddc_get_modes(connector, edid); - /* most eDP supports only timings from its edid, - * usually only detailed timings are available - * from eDP edid. timings which are not from edid - * may damage eDP - */ - if (connector->connector_type != DRM_MODE_CONNECTOR_eDP) - amdgpu_dm_connector_add_common_modes(encoder, connector); + amdgpu_dm_connector_add_common_modes(encoder, connector); amdgpu_dm_connector_add_freesync_modes(connector, edid); } amdgpu_dm_fbc_init(connector); -- cgit 1.4.1 From e0d7a96b278abe69c207615c2639ab8bb11ba321 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Mon, 10 Jul 2023 17:46:32 -0700 Subject: mm/mmap: Fix VM_LOCKED check in do_vmi_align_munmap() 6.1 backport of the patch [1] uses 'next' vma instead of 'split' vma. Fix the mistake. [1] commit 606c812eb1d5 ("mm/mmap: Fix error path in do_vmi_align_munmap()") Fixes: a149174ff8bb ("mm/mmap: Fix error path in do_vmi_align_munmap()") Signed-off-by: Suren Baghdasaryan Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/mmap.c b/mm/mmap.c index b8af52db3bbe..1597a96b667f 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2484,7 +2484,7 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, error = mas_store_gfp(&mas_detach, split, GFP_KERNEL); if (error) goto munmap_gather_failed; - if (next->vm_flags & VM_LOCKED) + if (split->vm_flags & VM_LOCKED) locked_vm += vma_pages(split); count++; -- cgit 1.4.1 From 21ce551a8592e55e6757a6e0d144ed81a89bb949 Mon Sep 17 00:00:00 2001 From: Andy Chi Date: Mon, 26 Jun 2023 21:03:00 +0800 Subject: ALSA: hda/realtek: Enable mute/micmute LEDs and limit mic boost on EliteBook commit e94f1f96f108ba96c0ed8bf3fbdd8ee6a6703880 upstream. On HP EliteBook 835/845/845W G10, the audio LEDs can be enabled by ALC285_FIXUP_HP_MUTE_LED. So use it accordingly. Signed-off-by: Andy Chi Cc: Fixes: 3e10f6ca76c4 ("ALSA: hda/realtek: Add quirk for HP EliteBook G10 laptops") Link: https://lore.kernel.org/r/20230626130301.301712-1-andy.chi@canonical.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eb049014f87a..0b2a0891f546 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9490,9 +9490,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b77, "HP ElieBook 865 G10", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8b7a, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b7d, "HP", ALC236_FIXUP_HP_GPIO_LED), -- cgit 1.4.1 From 83c6725556d4b0132de2621fa785e4fed278727e Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Wed, 28 Jun 2023 17:54:34 +0200 Subject: ALSA: hda/realtek: Add quirk for Clevo NPx0SNx commit 22065e4214c1196b54fc164892c2e193a743caf3 upstream. This applies a SND_PCI_QUIRK(...) to the Clevo NPx0SNx barebones fixing the microphone not being detected on the headset combo port. Signed-off-by: Werner Sembach Cc: Link: https://lore.kernel.org/r/20230628155434.584159-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0b2a0891f546..72fa1509cc4b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9680,6 +9680,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL5[03]RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), -- cgit 1.4.1 From 14eb1a2b6f9152b1303dcce663a923e8911a824a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Jul 2023 17:53:57 +0200 Subject: ALSA: jack: Fix mutex call in snd_jack_report() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 89dbb335cb6a627a4067bc42caa09c8bc3326d40 upstream. snd_jack_report() is supposed to be callable from an IRQ context, too, and it's indeed used in that way from virtsnd driver. The fix for input_dev race in commit 1b6a6fc5280e ("ALSA: jack: Access input_dev under mutex"), however, introduced a mutex lock in snd_jack_report(), and this resulted in a potential sleep-in-atomic. For addressing that problem, this patch changes the relevant code to use the object get/put and removes the mutex usage. That is, snd_jack_report(), it takes input_get_device() and leaves with input_put_device() for assuring the input_dev being assigned. Although the whole mutex could be reduced, we keep it because it can be still a protection for potential races between creation and deletion. Fixes: 1b6a6fc5280e ("ALSA: jack: Access input_dev under mutex") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/cf95f7fe-a748-4990-8378-000491b40329@moroto.mountain Tested-by: Amadeusz Sławiński Cc: Link: https://lore.kernel.org/r/20230706155357.3470-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/jack.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/core/jack.c b/sound/core/jack.c index 88493cc31914..03d155ed362b 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -654,6 +654,7 @@ void snd_jack_report(struct snd_jack *jack, int status) struct snd_jack_kctl *jack_kctl; unsigned int mask_bits = 0; #ifdef CONFIG_SND_JACK_INPUT_DEV + struct input_dev *idev; int i; #endif @@ -670,17 +671,15 @@ void snd_jack_report(struct snd_jack *jack, int status) status & jack_kctl->mask_bits); #ifdef CONFIG_SND_JACK_INPUT_DEV - mutex_lock(&jack->input_dev_lock); - if (!jack->input_dev) { - mutex_unlock(&jack->input_dev_lock); + idev = input_get_device(jack->input_dev); + if (!idev) return; - } for (i = 0; i < ARRAY_SIZE(jack->key); i++) { int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits); if (jack->type & testbit) - input_report_key(jack->input_dev, jack->key[i], + input_report_key(idev, jack->key[i], status & testbit); } @@ -688,13 +687,13 @@ void snd_jack_report(struct snd_jack *jack, int status) int testbit = ((1 << i) & ~mask_bits); if (jack->type & testbit) - input_report_switch(jack->input_dev, + input_report_switch(idev, jack_switch_types[i], status & testbit); } - input_sync(jack->input_dev); - mutex_unlock(&jack->input_dev_lock); + input_sync(idev); + input_put_device(idev); #endif /* CONFIG_SND_JACK_INPUT_DEV */ } EXPORT_SYMBOL(snd_jack_report); -- cgit 1.4.1 From 3eb4e47a94e3f76521d7d344696db61e6a9619c7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 3 Jul 2023 13:24:30 +0200 Subject: ALSA: pcm: Fix potential data race at PCM memory allocation helpers commit bd55842ed998a622ba6611fe59b3358c9f76773d upstream. The PCM memory allocation helpers have a sanity check against too many buffer allocations. However, the check is performed without a proper lock and the allocation isn't serialized; this allows user to allocate more memories than predefined max size. Practically seen, this isn't really a big problem, as it's more or less some "soft limit" as a sanity check, and it's not possible to allocate unlimitedly. But it's still better to address this for more consistent behavior. The patch covers the size check in do_alloc_pages() with the card->memory_mutex, and increases the allocated size there for preventing the further overflow. When the actual allocation fails, the size is decreased accordingly. Reported-by: BassCheck Reported-by: Tuo Li Link: https://lore.kernel.org/r/CADm8Tek6t0WedK+3Y6rbE5YEt19tML8BUL45N2ji4ZAz1KcN_A@mail.gmail.com Reviewed-by: Jaroslav Kysela Cc: Link: https://lore.kernel.org/r/20230703112430.30634-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm_memory.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 7bde7fb64011..a0b951471699 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -31,15 +31,41 @@ static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL; module_param(max_alloc_per_card, ulong, 0644); MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card."); +static void __update_allocated_size(struct snd_card *card, ssize_t bytes) +{ + card->total_pcm_alloc_bytes += bytes; +} + +static void update_allocated_size(struct snd_card *card, ssize_t bytes) +{ + mutex_lock(&card->memory_mutex); + __update_allocated_size(card, bytes); + mutex_unlock(&card->memory_mutex); +} + +static void decrease_allocated_size(struct snd_card *card, size_t bytes) +{ + mutex_lock(&card->memory_mutex); + WARN_ON(card->total_pcm_alloc_bytes < bytes); + __update_allocated_size(card, -(ssize_t)bytes); + mutex_unlock(&card->memory_mutex); +} + static int do_alloc_pages(struct snd_card *card, int type, struct device *dev, int str, size_t size, struct snd_dma_buffer *dmab) { enum dma_data_direction dir; int err; + /* check and reserve the requested size */ + mutex_lock(&card->memory_mutex); if (max_alloc_per_card && - card->total_pcm_alloc_bytes + size > max_alloc_per_card) + card->total_pcm_alloc_bytes + size > max_alloc_per_card) { + mutex_unlock(&card->memory_mutex); return -ENOMEM; + } + __update_allocated_size(card, size); + mutex_unlock(&card->memory_mutex); if (str == SNDRV_PCM_STREAM_PLAYBACK) dir = DMA_TO_DEVICE; @@ -47,9 +73,14 @@ static int do_alloc_pages(struct snd_card *card, int type, struct device *dev, dir = DMA_FROM_DEVICE; err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab); if (!err) { - mutex_lock(&card->memory_mutex); - card->total_pcm_alloc_bytes += dmab->bytes; - mutex_unlock(&card->memory_mutex); + /* the actual allocation size might be bigger than requested, + * and we need to correct the account + */ + if (dmab->bytes != size) + update_allocated_size(card, dmab->bytes - size); + } else { + /* take back on allocation failure */ + decrease_allocated_size(card, size); } return err; } @@ -58,10 +89,7 @@ static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab) { if (!dmab->area) return; - mutex_lock(&card->memory_mutex); - WARN_ON(card->total_pcm_alloc_bytes < dmab->bytes); - card->total_pcm_alloc_bytes -= dmab->bytes; - mutex_unlock(&card->memory_mutex); + decrease_allocated_size(card, dmab->bytes); snd_dma_free_pages(dmab); dmab->area = NULL; } -- cgit 1.4.1 From a4c79ea1e9ed5cc1cfb786112e4717e67ed5fa9e Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Wed, 21 Jun 2023 08:17:23 +1200 Subject: block: fix signed int overflow in Amiga partition support commit fc3d092c6bb48d5865fec15ed5b333c12f36288c upstream. The Amiga partition parser module uses signed int for partition sector address and count, which will overflow for disks larger than 1 TB. Use sector_t as type for sector address and size to allow using disks up to 2 TB without LBD support, and disks larger than 2 TB with LBD. This bug was reported originally in 2012, and the fix was created by the RDB author, Joanne Dow . A patch had been discussed and reviewed on linux-m68k at that time but never officially submitted. This patch differs from Joanne's patch only in its use of sector_t instead of unsigned int. No checking for overflows is done (see patch 3 of this series for that). Reported-by: Martin Steigerwald Closes: https://bugzilla.kernel.org/show_bug.cgi?id=43511 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Message-ID: <201206192146.09327.Martin@lichtvoll.de> Cc: # 5.2 Signed-off-by: Michael Schmitz Tested-by: Martin Steigerwald Reviewed-by: Geert Uytterhoeven Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230620201725.7020-2-schmitzmic@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/partitions/amiga.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/block/partitions/amiga.c b/block/partitions/amiga.c index 5c8624e26a54..85c5c79aae48 100644 --- a/block/partitions/amiga.c +++ b/block/partitions/amiga.c @@ -31,7 +31,8 @@ int amiga_partition(struct parsed_partitions *state) unsigned char *data; struct RigidDiskBlock *rdb; struct PartitionBlock *pb; - int start_sect, nr_sects, blk, part, res = 0; + sector_t start_sect, nr_sects; + int blk, part, res = 0; int blksize = 1; /* Multiplier for disk block size */ int slot = 1; @@ -96,14 +97,14 @@ int amiga_partition(struct parsed_partitions *state) /* Tell Kernel about it */ - nr_sects = (be32_to_cpu(pb->pb_Environment[10]) + 1 - - be32_to_cpu(pb->pb_Environment[9])) * + nr_sects = ((sector_t)be32_to_cpu(pb->pb_Environment[10]) + 1 - + be32_to_cpu(pb->pb_Environment[9])) * be32_to_cpu(pb->pb_Environment[3]) * be32_to_cpu(pb->pb_Environment[5]) * blksize; if (!nr_sects) continue; - start_sect = be32_to_cpu(pb->pb_Environment[9]) * + start_sect = (sector_t)be32_to_cpu(pb->pb_Environment[9]) * be32_to_cpu(pb->pb_Environment[3]) * be32_to_cpu(pb->pb_Environment[5]) * blksize; -- cgit 1.4.1 From 40d6a1261a9c6247c9c00e695ff45f433e13339b Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Wed, 21 Jun 2023 08:17:25 +1200 Subject: block: add overflow checks for Amiga partition support commit b6f3f28f604ba3de4724ad82bea6adb1300c0b5f upstream. The Amiga partition parser module uses signed int for partition sector address and count, which will overflow for disks larger than 1 TB. Use u64 as type for sector address and size to allow using disks up to 2 TB without LBD support, and disks larger than 2 TB with LBD. The RBD format allows to specify disk sizes up to 2^128 bytes (though native OS limitations reduce this somewhat, to max 2^68 bytes), so check for u64 overflow carefully to protect against overflowing sector_t. Bail out if sector addresses overflow 32 bits on kernels without LBD support. This bug was reported originally in 2012, and the fix was created by the RDB author, Joanne Dow . A patch had been discussed and reviewed on linux-m68k at that time but never officially submitted (now resubmitted as patch 1 in this series). This patch adds additional error checking and warning messages. Reported-by: Martin Steigerwald Closes: https://bugzilla.kernel.org/show_bug.cgi?id=43511 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Message-ID: <201206192146.09327.Martin@lichtvoll.de> Cc: # 5.2 Signed-off-by: Michael Schmitz Reviewed-by: Geert Uytterhoeven Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230620201725.7020-4-schmitzmic@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/partitions/amiga.c | 103 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/block/partitions/amiga.c b/block/partitions/amiga.c index 85c5c79aae48..ed222b9c901b 100644 --- a/block/partitions/amiga.c +++ b/block/partitions/amiga.c @@ -11,10 +11,18 @@ #define pr_fmt(fmt) fmt #include +#include +#include #include #include "check.h" +/* magic offsets in partition DosEnvVec */ +#define NR_HD 3 +#define NR_SECT 5 +#define LO_CYL 9 +#define HI_CYL 10 + static __inline__ u32 checksum_block(__be32 *m, int size) { @@ -31,9 +39,12 @@ int amiga_partition(struct parsed_partitions *state) unsigned char *data; struct RigidDiskBlock *rdb; struct PartitionBlock *pb; - sector_t start_sect, nr_sects; - int blk, part, res = 0; - int blksize = 1; /* Multiplier for disk block size */ + u64 start_sect, nr_sects; + sector_t blk, end_sect; + u32 cylblk; /* rdb_CylBlocks = nr_heads*sect_per_track */ + u32 nr_hd, nr_sect, lo_cyl, hi_cyl; + int part, res = 0; + unsigned int blksize = 1; /* Multiplier for disk block size */ int slot = 1; for (blk = 0; ; blk++, put_dev_sector(sect)) { @@ -41,7 +52,7 @@ int amiga_partition(struct parsed_partitions *state) goto rdb_done; data = read_part_sector(state, blk, §); if (!data) { - pr_err("Dev %s: unable to read RDB block %d\n", + pr_err("Dev %s: unable to read RDB block %llu\n", state->disk->disk_name, blk); res = -1; goto rdb_done; @@ -58,12 +69,12 @@ int amiga_partition(struct parsed_partitions *state) *(__be32 *)(data+0xdc) = 0; if (checksum_block((__be32 *)data, be32_to_cpu(rdb->rdb_SummedLongs) & 0x7F)==0) { - pr_err("Trashed word at 0xd0 in block %d ignored in checksum calculation\n", + pr_err("Trashed word at 0xd0 in block %llu ignored in checksum calculation\n", blk); break; } - pr_err("Dev %s: RDB in block %d has bad checksum\n", + pr_err("Dev %s: RDB in block %llu has bad checksum\n", state->disk->disk_name, blk); } @@ -80,10 +91,15 @@ int amiga_partition(struct parsed_partitions *state) blk = be32_to_cpu(rdb->rdb_PartitionList); put_dev_sector(sect); for (part = 1; blk>0 && part<=16; part++, put_dev_sector(sect)) { - blk *= blksize; /* Read in terms partition table understands */ + /* Read in terms partition table understands */ + if (check_mul_overflow(blk, (sector_t) blksize, &blk)) { + pr_err("Dev %s: overflow calculating partition block %llu! Skipping partitions %u and beyond\n", + state->disk->disk_name, blk, part); + break; + } data = read_part_sector(state, blk, §); if (!data) { - pr_err("Dev %s: unable to read partition block %d\n", + pr_err("Dev %s: unable to read partition block %llu\n", state->disk->disk_name, blk); res = -1; goto rdb_done; @@ -95,19 +111,70 @@ int amiga_partition(struct parsed_partitions *state) if (checksum_block((__be32 *)pb, be32_to_cpu(pb->pb_SummedLongs) & 0x7F) != 0 ) continue; - /* Tell Kernel about it */ + /* RDB gives us more than enough rope to hang ourselves with, + * many times over (2^128 bytes if all fields max out). + * Some careful checks are in order, so check for potential + * overflows. + * We are multiplying four 32 bit numbers to one sector_t! + */ + + nr_hd = be32_to_cpu(pb->pb_Environment[NR_HD]); + nr_sect = be32_to_cpu(pb->pb_Environment[NR_SECT]); + + /* CylBlocks is total number of blocks per cylinder */ + if (check_mul_overflow(nr_hd, nr_sect, &cylblk)) { + pr_err("Dev %s: heads*sects %u overflows u32, skipping partition!\n", + state->disk->disk_name, cylblk); + continue; + } + + /* check for consistency with RDB defined CylBlocks */ + if (cylblk > be32_to_cpu(rdb->rdb_CylBlocks)) { + pr_warn("Dev %s: cylblk %u > rdb_CylBlocks %u!\n", + state->disk->disk_name, cylblk, + be32_to_cpu(rdb->rdb_CylBlocks)); + } + + /* RDB allows for variable logical block size - + * normalize to 512 byte blocks and check result. + */ + + if (check_mul_overflow(cylblk, blksize, &cylblk)) { + pr_err("Dev %s: partition %u bytes per cyl. overflows u32, skipping partition!\n", + state->disk->disk_name, part); + continue; + } + + /* Calculate partition start and end. Limit of 32 bit on cylblk + * guarantees no overflow occurs if LBD support is enabled. + */ + + lo_cyl = be32_to_cpu(pb->pb_Environment[LO_CYL]); + start_sect = ((u64) lo_cyl * cylblk); + + hi_cyl = be32_to_cpu(pb->pb_Environment[HI_CYL]); + nr_sects = (((u64) hi_cyl - lo_cyl + 1) * cylblk); - nr_sects = ((sector_t)be32_to_cpu(pb->pb_Environment[10]) + 1 - - be32_to_cpu(pb->pb_Environment[9])) * - be32_to_cpu(pb->pb_Environment[3]) * - be32_to_cpu(pb->pb_Environment[5]) * - blksize; if (!nr_sects) continue; - start_sect = (sector_t)be32_to_cpu(pb->pb_Environment[9]) * - be32_to_cpu(pb->pb_Environment[3]) * - be32_to_cpu(pb->pb_Environment[5]) * - blksize; + + /* Warn user if partition end overflows u32 (AmigaDOS limit) */ + + if ((start_sect + nr_sects) > UINT_MAX) { + pr_warn("Dev %s: partition %u (%llu-%llu) needs 64 bit device support!\n", + state->disk->disk_name, part, + start_sect, start_sect + nr_sects); + } + + if (check_add_overflow(start_sect, nr_sects, &end_sect)) { + pr_err("Dev %s: partition %u (%llu-%llu) needs LBD device support, skipping partition!\n", + state->disk->disk_name, part, + start_sect, end_sect); + continue; + } + + /* Tell Kernel about it */ + put_partition(state,slot++,start_sect,nr_sects); { /* Be even more informative to aid mounting */ -- cgit 1.4.1 From 28b58a8d10b36383542460a43611af75ce3f78c7 Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Wed, 21 Jun 2023 08:17:24 +1200 Subject: block: change all __u32 annotations to __be32 in affs_hardblocks.h commit 95a55437dc49fb3342c82e61f5472a71c63d9ed0 upstream. The Amiga partition parser module uses signed int for partition sector address and count, which will overflow for disks larger than 1 TB. Use u64 as type for sector address and size to allow using disks up to 2 TB without LBD support, and disks larger than 2 TB with LBD. The RBD format allows to specify disk sizes up to 2^128 bytes (though native OS limitations reduce this somewhat, to max 2^68 bytes), so check for u64 overflow carefully to protect against overflowing sector_t. This bug was reported originally in 2012, and the fix was created by the RDB author, Joanne Dow . A patch had been discussed and reviewed on linux-m68k at that time but never officially submitted (now resubmitted as patch 1 of this series). Patch 3 (this series) adds additional error checking and warning messages. One of the error checks now makes use of the previously unused rdb_CylBlocks field, which causes a 'sparse' warning (cast to restricted __be32). Annotate all 32 bit fields in affs_hardblocks.h as __be32, as the on-disk format of RDB and partition blocks is always big endian. Reported-by: Martin Steigerwald Closes: https://bugzilla.kernel.org/show_bug.cgi?id=43511 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Message-ID: <201206192146.09327.Martin@lichtvoll.de> Cc: # 5.2 Signed-off-by: Michael Schmitz Reviewed-by: Christoph Hellwig Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230620201725.7020-3-schmitzmic@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/affs_hardblocks.h | 68 ++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/include/uapi/linux/affs_hardblocks.h b/include/uapi/linux/affs_hardblocks.h index 5e2fb8481252..a5aff2eb5f70 100644 --- a/include/uapi/linux/affs_hardblocks.h +++ b/include/uapi/linux/affs_hardblocks.h @@ -7,42 +7,42 @@ /* Just the needed definitions for the RDB of an Amiga HD. */ struct RigidDiskBlock { - __u32 rdb_ID; + __be32 rdb_ID; __be32 rdb_SummedLongs; - __s32 rdb_ChkSum; - __u32 rdb_HostID; + __be32 rdb_ChkSum; + __be32 rdb_HostID; __be32 rdb_BlockBytes; - __u32 rdb_Flags; - __u32 rdb_BadBlockList; + __be32 rdb_Flags; + __be32 rdb_BadBlockList; __be32 rdb_PartitionList; - __u32 rdb_FileSysHeaderList; - __u32 rdb_DriveInit; - __u32 rdb_Reserved1[6]; - __u32 rdb_Cylinders; - __u32 rdb_Sectors; - __u32 rdb_Heads; - __u32 rdb_Interleave; - __u32 rdb_Park; - __u32 rdb_Reserved2[3]; - __u32 rdb_WritePreComp; - __u32 rdb_ReducedWrite; - __u32 rdb_StepRate; - __u32 rdb_Reserved3[5]; - __u32 rdb_RDBBlocksLo; - __u32 rdb_RDBBlocksHi; - __u32 rdb_LoCylinder; - __u32 rdb_HiCylinder; - __u32 rdb_CylBlocks; - __u32 rdb_AutoParkSeconds; - __u32 rdb_HighRDSKBlock; - __u32 rdb_Reserved4; + __be32 rdb_FileSysHeaderList; + __be32 rdb_DriveInit; + __be32 rdb_Reserved1[6]; + __be32 rdb_Cylinders; + __be32 rdb_Sectors; + __be32 rdb_Heads; + __be32 rdb_Interleave; + __be32 rdb_Park; + __be32 rdb_Reserved2[3]; + __be32 rdb_WritePreComp; + __be32 rdb_ReducedWrite; + __be32 rdb_StepRate; + __be32 rdb_Reserved3[5]; + __be32 rdb_RDBBlocksLo; + __be32 rdb_RDBBlocksHi; + __be32 rdb_LoCylinder; + __be32 rdb_HiCylinder; + __be32 rdb_CylBlocks; + __be32 rdb_AutoParkSeconds; + __be32 rdb_HighRDSKBlock; + __be32 rdb_Reserved4; char rdb_DiskVendor[8]; char rdb_DiskProduct[16]; char rdb_DiskRevision[4]; char rdb_ControllerVendor[8]; char rdb_ControllerProduct[16]; char rdb_ControllerRevision[4]; - __u32 rdb_Reserved5[10]; + __be32 rdb_Reserved5[10]; }; #define IDNAME_RIGIDDISK 0x5244534B /* "RDSK" */ @@ -50,16 +50,16 @@ struct RigidDiskBlock { struct PartitionBlock { __be32 pb_ID; __be32 pb_SummedLongs; - __s32 pb_ChkSum; - __u32 pb_HostID; + __be32 pb_ChkSum; + __be32 pb_HostID; __be32 pb_Next; - __u32 pb_Flags; - __u32 pb_Reserved1[2]; - __u32 pb_DevFlags; + __be32 pb_Flags; + __be32 pb_Reserved1[2]; + __be32 pb_DevFlags; __u8 pb_DriveName[32]; - __u32 pb_Reserved2[15]; + __be32 pb_Reserved2[15]; __be32 pb_Environment[17]; - __u32 pb_EReserved[15]; + __be32 pb_EReserved[15]; }; #define IDNAME_PARTITION 0x50415254 /* "PART" */ -- cgit 1.4.1 From defc91422764710d88a2485cafd495ae4d6651df Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Wed, 7 Jun 2023 13:08:37 -0400 Subject: block: increment diskseq on all media change events commit b90ecc0379eb7bbe79337b0c7289390a98752646 upstream. Currently, associating a loop device with a different file descriptor does not increment its diskseq. This allows the following race condition: 1. Program X opens a loop device 2. Program X gets the diskseq of the loop device. 3. Program X associates a file with the loop device. 4. Program X passes the loop device major, minor, and diskseq to something. 5. Program X exits. 6. Program Y detaches the file from the loop device. 7. Program Y attaches a different file to the loop device. 8. The opener finally gets around to opening the loop device and checks that the diskseq is what it expects it to be. Even though the diskseq is the expected value, the result is that the opener is accessing the wrong file. From discussions with Christoph Hellwig, it appears that disk_force_media_change() was supposed to call inc_diskseq(), but in fact it does not. Adding a Fixes: tag to indicate this. Christoph's Reported-by is because he stated that disk_force_media_change() calls inc_diskseq(), which is what led me to discover that it should but does not. Reported-by: Christoph Hellwig Signed-off-by: Demi Marie Obenour Fixes: e6138dc12de9 ("block: add a helper to raise a media changed event") Cc: stable@vger.kernel.org # 5.15+ Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230607170837.1559-1-demi@invisiblethingslab.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/disk-events.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/disk-events.c b/block/disk-events.c index aee25a7e1ab7..450c2cbe23d5 100644 --- a/block/disk-events.c +++ b/block/disk-events.c @@ -307,6 +307,7 @@ bool disk_force_media_change(struct gendisk *disk, unsigned int events) if (!(events & DISK_EVENT_MEDIA_CHANGE)) return false; + inc_diskseq(disk); if (__invalidate_device(disk->part0, true)) pr_warn("VFS: busy inodes on changed media %s\n", disk->disk_name); -- cgit 1.4.1 From 6f1c81886b0b56cb88b311e5d2f203625474d892 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 19 Jun 2023 17:21:48 +0100 Subject: btrfs: fix race when deleting free space root from the dirty cow roots list commit babebf023e661b90b1c78b2baa384fb03a226879 upstream. When deleting the free space tree we are deleting the free space root from the list fs_info->dirty_cowonly_roots without taking the lock that protects it, which is struct btrfs_fs_info::trans_lock. This unsynchronized list manipulation may cause chaos if there's another concurrent manipulation of this list, such as when adding a root to it with ctree.c:add_root_to_dirty_list(). This can result in all sorts of weird failures caused by a race, such as the following crash: [337571.278245] general protection fault, probably for non-canonical address 0xdead000000000108: 0000 [#1] PREEMPT SMP PTI [337571.278933] CPU: 1 PID: 115447 Comm: btrfs Tainted: G W 6.4.0-rc6-btrfs-next-134+ #1 [337571.279153] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a1990b-prebuilt.qemu.org 04/01/2014 [337571.279572] RIP: 0010:commit_cowonly_roots+0x11f/0x250 [btrfs] [337571.279928] Code: 85 38 06 00 (...) [337571.280363] RSP: 0018:ffff9f63446efba0 EFLAGS: 00010206 [337571.280582] RAX: ffff942d98ec2638 RBX: ffff9430b82b4c30 RCX: 0000000449e1c000 [337571.280798] RDX: dead000000000100 RSI: ffff9430021e4900 RDI: 0000000000036070 [337571.281015] RBP: ffff942d98ec2000 R08: ffff942d98ec2000 R09: 000000000000015b [337571.281254] R10: 0000000000000009 R11: 0000000000000001 R12: ffff942fe8fbf600 [337571.281476] R13: ffff942dabe23040 R14: ffff942dabe20800 R15: ffff942d92cf3b48 [337571.281723] FS: 00007f478adb7340(0000) GS:ffff94349fa40000(0000) knlGS:0000000000000000 [337571.281950] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [337571.282184] CR2: 00007f478ab9a3d5 CR3: 000000001e02c001 CR4: 0000000000370ee0 [337571.282416] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [337571.282647] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [337571.282874] Call Trace: [337571.283101] [337571.283327] ? __die_body+0x1b/0x60 [337571.283570] ? die_addr+0x39/0x60 [337571.283796] ? exc_general_protection+0x22e/0x430 [337571.284022] ? asm_exc_general_protection+0x22/0x30 [337571.284251] ? commit_cowonly_roots+0x11f/0x250 [btrfs] [337571.284531] btrfs_commit_transaction+0x42e/0xf90 [btrfs] [337571.284803] ? _raw_spin_unlock+0x15/0x30 [337571.285031] ? release_extent_buffer+0x103/0x130 [btrfs] [337571.285305] reset_balance_state+0x152/0x1b0 [btrfs] [337571.285578] btrfs_balance+0xa50/0x11e0 [btrfs] [337571.285864] ? __kmem_cache_alloc_node+0x14a/0x410 [337571.286086] btrfs_ioctl+0x249a/0x3320 [btrfs] [337571.286358] ? mod_objcg_state+0xd2/0x360 [337571.286577] ? refill_obj_stock+0xb0/0x160 [337571.286798] ? seq_release+0x25/0x30 [337571.287016] ? __rseq_handle_notify_resume+0x3ba/0x4b0 [337571.287235] ? percpu_counter_add_batch+0x2e/0xa0 [337571.287455] ? __x64_sys_ioctl+0x88/0xc0 [337571.287675] __x64_sys_ioctl+0x88/0xc0 [337571.287901] do_syscall_64+0x38/0x90 [337571.288126] entry_SYSCALL_64_after_hwframe+0x72/0xdc [337571.288352] RIP: 0033:0x7f478aaffe9b So fix this by locking struct btrfs_fs_info::trans_lock before deleting the free space root from that list. Fixes: a5ed91828518 ("Btrfs: implement the free space B-tree") CC: stable@vger.kernel.org # 4.14+ Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/free-space-tree.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index e040eea3937d..a07450f64abb 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1275,7 +1275,10 @@ int btrfs_delete_free_space_tree(struct btrfs_fs_info *fs_info) goto abort; btrfs_global_root_delete(free_space_root); + + spin_lock(&fs_info->trans_lock); list_del(&free_space_root->dirty_list); + spin_unlock(&fs_info->trans_lock); btrfs_tree_lock(free_space_root->node); btrfs_clean_tree_block(free_space_root->node); -- cgit 1.4.1 From ef047411887ff0845afd642d6a687819308e1a4e Mon Sep 17 00:00:00 2001 From: Ding Hui Date: Mon, 15 May 2023 10:13:07 +0800 Subject: SUNRPC: Fix UAF in svc_tcp_listen_data_ready() commit fc80fc2d4e39137869da3150ee169b40bf879287 upstream. After the listener svc_sock is freed, and before invoking svc_tcp_accept() for the established child sock, there is a window that the newsock retaining a freed listener svc_sock in sk_user_data which cloning from parent. In the race window, if data is received on the newsock, we will observe use-after-free report in svc_tcp_listen_data_ready(). Reproduce by two tasks: 1. while :; do rpc.nfsd 0 ; rpc.nfsd; done 2. while :; do echo "" | ncat -4 127.0.0.1 2049 ; done KASAN report: ================================================================== BUG: KASAN: slab-use-after-free in svc_tcp_listen_data_ready+0x1cf/0x1f0 [sunrpc] Read of size 8 at addr ffff888139d96228 by task nc/102553 CPU: 7 PID: 102553 Comm: nc Not tainted 6.3.0+ #18 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 Call Trace: dump_stack_lvl+0x33/0x50 print_address_description.constprop.0+0x27/0x310 print_report+0x3e/0x70 kasan_report+0xae/0xe0 svc_tcp_listen_data_ready+0x1cf/0x1f0 [sunrpc] tcp_data_queue+0x9f4/0x20e0 tcp_rcv_established+0x666/0x1f60 tcp_v4_do_rcv+0x51c/0x850 tcp_v4_rcv+0x23fc/0x2e80 ip_protocol_deliver_rcu+0x62/0x300 ip_local_deliver_finish+0x267/0x350 ip_local_deliver+0x18b/0x2d0 ip_rcv+0x2fb/0x370 __netif_receive_skb_one_core+0x166/0x1b0 process_backlog+0x24c/0x5e0 __napi_poll+0xa2/0x500 net_rx_action+0x854/0xc90 __do_softirq+0x1bb/0x5de do_softirq+0xcb/0x100 ... Allocated by task 102371: kasan_save_stack+0x1e/0x40 kasan_set_track+0x21/0x30 __kasan_kmalloc+0x7b/0x90 svc_setup_socket+0x52/0x4f0 [sunrpc] svc_addsock+0x20d/0x400 [sunrpc] __write_ports_addfd+0x209/0x390 [nfsd] write_ports+0x239/0x2c0 [nfsd] nfsctl_transaction_write+0xac/0x110 [nfsd] vfs_write+0x1c3/0xae0 ksys_write+0xed/0x1c0 do_syscall_64+0x38/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc Freed by task 102551: kasan_save_stack+0x1e/0x40 kasan_set_track+0x21/0x30 kasan_save_free_info+0x2a/0x50 __kasan_slab_free+0x106/0x190 __kmem_cache_free+0x133/0x270 svc_xprt_free+0x1e2/0x350 [sunrpc] svc_xprt_destroy_all+0x25a/0x440 [sunrpc] nfsd_put+0x125/0x240 [nfsd] nfsd_svc+0x2cb/0x3c0 [nfsd] write_threads+0x1ac/0x2a0 [nfsd] nfsctl_transaction_write+0xac/0x110 [nfsd] vfs_write+0x1c3/0xae0 ksys_write+0xed/0x1c0 do_syscall_64+0x38/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc Fix the UAF by simply doing nothing in svc_tcp_listen_data_ready() if state != TCP_LISTEN, that will avoid dereferencing svsk for all child socket. Link: https://lore.kernel.org/lkml/20230507091131.23540-1-dinghui@sangfor.com.cn/ Fixes: fa9251afc33c ("SUNRPC: Call the default socket callbacks instead of open coding") Signed-off-by: Ding Hui Cc: Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/svcsock.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index d808c00cdbac..23b4c728de59 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -688,12 +688,6 @@ static void svc_tcp_listen_data_ready(struct sock *sk) { struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; - if (svsk) { - /* Refer to svc_setup_socket() for details. */ - rmb(); - svsk->sk_odata(sk); - } - /* * This callback may called twice when a new connection * is established as a child socket inherits everything @@ -702,13 +696,18 @@ static void svc_tcp_listen_data_ready(struct sock *sk) * when one of child sockets become ESTABLISHED. * 2) data_ready method of the child socket may be called * when it receives data before the socket is accepted. - * In case of 2, we should ignore it silently. + * In case of 2, we should ignore it silently and DO NOT + * dereference svsk. */ - if (sk->sk_state == TCP_LISTEN) { - if (svsk) { - set_bit(XPT_CONN, &svsk->sk_xprt.xpt_flags); - svc_xprt_enqueue(&svsk->sk_xprt); - } + if (sk->sk_state != TCP_LISTEN) + return; + + if (svsk) { + /* Refer to svc_setup_socket() for details. */ + rmb(); + svsk->sk_odata(sk); + set_bit(XPT_CONN, &svsk->sk_xprt.xpt_flags); + svc_xprt_enqueue(&svsk->sk_xprt); } } -- cgit 1.4.1 From eab648537764fcd1d4be74f71f39142547aee3f0 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 27 Apr 2023 13:21:52 +0200 Subject: w1: w1_therm: fix locking behavior in convert_t [ Upstream commit dca5480ab7b77a889088ab7cac81934604510ac7 ] The commit 67b392f7b8ed ("w1_therm: optimizing temperature read timings") accidentially inverted the logic for lock handling of the bus mutex. Before: pullup -> release lock before sleep no pullup -> release lock after sleep After: pullup -> release lock after sleep no pullup -> release lock before sleep This cause spurious measurements of 85 degree (powerup value) on the Tarragon board with connected 1-w temperature sensor (w1_therm.w1_strong_pull=0). In the meantime a new feature for polling the conversion completion has been integrated in these branches with commit 021da53e65fd ("w1: w1_therm: Add sysfs entries to control conversion time and driver features"). But this feature isn't available for parasite power mode, so handle this separately. Link: https://lore.kernel.org/regressions/2023042645-attentive-amends-7b0b@gregkh/T/ Fixes: 67b392f7b8ed ("w1_therm: optimizing temperature read timings") Signed-off-by: Stefan Wahren Link: https://lore.kernel.org/r/20230427112152.12313-1-stefan.wahren@i2se.com Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/w1/slaves/w1_therm.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 067692626cf0..99c58bd9d2df 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -1159,29 +1159,26 @@ static int convert_t(struct w1_slave *sl, struct therm_info *info) w1_write_8(dev_master, W1_CONVERT_TEMP); - if (strong_pullup) { /*some device need pullup */ + if (SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) { + ret = w1_poll_completion(dev_master, W1_POLL_CONVERT_TEMP); + if (ret) { + dev_dbg(&sl->dev, "%s: Timeout\n", __func__); + goto mt_unlock; + } + mutex_unlock(&dev_master->bus_mutex); + } else if (!strong_pullup) { /*no device need pullup */ sleep_rem = msleep_interruptible(t_conv); if (sleep_rem != 0) { ret = -EINTR; goto mt_unlock; } mutex_unlock(&dev_master->bus_mutex); - } else { /*no device need pullup */ - if (SLAVE_FEATURES(sl) & W1_THERM_POLL_COMPLETION) { - ret = w1_poll_completion(dev_master, W1_POLL_CONVERT_TEMP); - if (ret) { - dev_dbg(&sl->dev, "%s: Timeout\n", __func__); - goto mt_unlock; - } - mutex_unlock(&dev_master->bus_mutex); - } else { - /* Fixed delay */ - mutex_unlock(&dev_master->bus_mutex); - sleep_rem = msleep_interruptible(t_conv); - if (sleep_rem != 0) { - ret = -EINTR; - goto dec_refcnt; - } + } else { /*some device need pullup */ + mutex_unlock(&dev_master->bus_mutex); + sleep_rem = msleep_interruptible(t_conv); + if (sleep_rem != 0) { + ret = -EINTR; + goto dec_refcnt; } } ret = read_scratchpad(sl, info); -- cgit 1.4.1 From c3f5604abab78628a2c3c98df464ca605a68e568 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 May 2021 17:17:45 +0300 Subject: w1: fix loop in w1_fini() [ Upstream commit 83f3fcf96fcc7e5405b37d9424c7ef26bfa203f8 ] The __w1_remove_master_device() function calls: list_del(&dev->w1_master_entry); So presumably this can cause an endless loop. Fixes: 7785925dd8e0 ("[PATCH] w1: cleanups.") Signed-off-by: Dan Carpenter Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/w1/w1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 4a2ddf730a3a..2eee26b7fc4a 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -1263,10 +1263,10 @@ err_out_exit_init: static void __exit w1_fini(void) { - struct w1_master *dev; + struct w1_master *dev, *n; /* Set netlink removal messages and some cleanup */ - list_for_each_entry(dev, &w1_masters, w1_master_entry) + list_for_each_entry_safe(dev, n, &w1_masters, w1_master_entry) __w1_remove_master_device(dev); w1_fini_netlink(); -- cgit 1.4.1 From 190bdec8a77f44966c8a1e7d7c4cd938052bdc61 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 19 Apr 2023 12:41:06 +0200 Subject: dt-bindings: power: reset: qcom-pon: Only allow reboot-mode pre-pmk8350 [ Upstream commit d41dab4c031edaa460a484113394327aa52dc0bd ] As pointed out by Shazad [1], PMICs using a separate HLOS+PBS scheme (so PMK8350 and newer) are expected to pass reboot mode data through SDAM, as the reboot mode registers are absent in the HLOS reg space. Limit the reboot-mode.yaml inclusion to PMICs without a separate PBS region. [1] https://lore.kernel.org/linux-arm-msm/12f13183-c381-25f7-459e-62e0c2b19498@quicinc.com/ Fixes: 03fccdc76dce ("dt-bindings: power: reset: qcom-pon: Add new compatible "qcom,pmk8350-pon"") Signed-off-by: Konrad Dybcio Acked-by: Rob Herring Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- Documentation/devicetree/bindings/power/reset/qcom,pon.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml index d96170eecbd2..0b1eca734d3b 100644 --- a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml +++ b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml @@ -56,7 +56,6 @@ required: unevaluatedProperties: false allOf: - - $ref: reboot-mode.yaml# - if: properties: compatible: @@ -66,6 +65,9 @@ allOf: - qcom,pms405-pon - qcom,pm8998-pon then: + allOf: + - $ref: reboot-mode.yaml# + properties: reg: maxItems: 1 -- cgit 1.4.1 From d6dd477436957aed54cfbbcd6469bd250fd0a5c3 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Apr 2023 00:47:11 +0800 Subject: f2fs: do not allow to defragment files have FI_COMPRESS_RELEASED [ Upstream commit 7cd2e5f75b86a1befa99834f3ed1d735eeff69e6 ] If a file has FI_COMPRESS_RELEASED, all writes for it should not be allowed. Fixes: 5fdb322ff2c2 ("f2fs: add F2FS_IOC_DECOMPRESS_FILE and F2FS_IOC_COMPRESS_FILE") Signed-off-by: Qi Han Signed-off-by: Yangtao Li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/file.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index dbad2db68f1b..370c40858b42 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2588,6 +2588,11 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, inode_lock(inode); + if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) { + err = -EINVAL; + goto unlock_out; + } + /* if in-place-update policy is enabled, don't waste time here */ set_inode_flag(inode, FI_OPU_WRITE); if (f2fs_should_update_inplace(inode, NULL)) { @@ -2712,6 +2717,7 @@ clear_out: clear_inode_flag(inode, FI_SKIP_WRITES); out: clear_inode_flag(inode, FI_OPU_WRITE); +unlock_out: inode_unlock(inode); if (!err) range->len = (u64)total << PAGE_SHIFT; -- cgit 1.4.1 From e91366b72c36ebccc64373a80c6dfcfba209a3ae Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz Date: Wed, 3 May 2023 14:57:41 +0200 Subject: sh: j2: Use ioremap() to translate device tree address into kernel memory [ Upstream commit bc9d1f0cecd2407cfb2364a7d4be2f52d1d46a9d ] Addresses the following warning when building j2_defconfig: arch/sh/kernel/cpu/sh2/probe.c: In function 'scan_cache': arch/sh/kernel/cpu/sh2/probe.c:24:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] 24 | j2_ccr_base = (u32 __iomem *)of_flat_dt_translate_address(node); | Fixes: 5a846abad07f ("sh: add support for J-Core J2 processor") Reviewed-by: Geert Uytterhoeven Tested-by: Rob Landley Signed-off-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/20230503125746.331835-1-glaubitz@physik.fu-berlin.de Signed-off-by: John Paul Adrian Glaubitz Signed-off-by: Sasha Levin --- arch/sh/kernel/cpu/sh2/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/kernel/cpu/sh2/probe.c b/arch/sh/kernel/cpu/sh2/probe.c index d342ea08843f..70a07f4f2142 100644 --- a/arch/sh/kernel/cpu/sh2/probe.c +++ b/arch/sh/kernel/cpu/sh2/probe.c @@ -21,7 +21,7 @@ static int __init scan_cache(unsigned long node, const char *uname, if (!of_flat_dt_is_compatible(node, "jcore,cache")) return 0; - j2_ccr_base = (u32 __iomem *)of_flat_dt_translate_address(node); + j2_ccr_base = ioremap(of_flat_dt_translate_address(node), 4); return 1; } -- cgit 1.4.1 From 98b6582b37da030873056a4f3ac29c56183ad952 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 17 Oct 2022 21:59:14 +0200 Subject: usb: dwc2: platform: Improve error reporting for problems during .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 55f223b8b408cbfd85fb1c5b74ab85ccab319a69 ] Returning an error value in a platform driver's remove callback results in a generic error message being emitted by the driver core, but otherwise it doesn't make a difference. The device goes away anyhow. For each case where ret is non-zero the driver already emits an error message, so suppress the generic error message by returning zero unconditionally. (Side note: The return value handling was unreliable anyhow as the value returned by dwc2_exit_hibernation() was overwritten anyhow if hsotg->in_ppd was non-zero.) Signed-off-by: Uwe Kleine-König Acked-by: Minas Harutyunyan Link: https://lore.kernel.org/r/20221017195914.1426297-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman Stable-dep-of: ada050c69108 ("usb: dwc2: Fix some error handling paths") Signed-off-by: Sasha Levin --- drivers/usb/dwc2/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 0c02ef7628fd..d1589ba7d322 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -309,7 +309,7 @@ static int dwc2_driver_remove(struct platform_device *dev) reset_control_assert(hsotg->reset); reset_control_assert(hsotg->reset_ecc); - return ret; + return 0; } /** -- cgit 1.4.1 From b6e30a54a5925b2b733d02cce36a17538d43f855 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 5 May 2023 19:15:08 +0200 Subject: usb: dwc2: Fix some error handling paths [ Upstream commit ada050c69108bc34be13ecc11f7fad0f20ebadc4 ] dwc2_driver_probe() calls dwc2_lowlevel_hw_init() which deassert some reset lines. Should an error happen in dwc2_lowlevel_hw_init() after calling reset_control_deassert() or in the probe after calling dwc2_lowlevel_hw_init(), the reset lines remain deasserted. Add some devm_add_action_or_reset() calls to re-assert the lines if needed. Update the remove function accordingly. This change is compile-tested only. Fixes: 83f8da562f8b ("usb: dwc2: Add reset control to dwc2") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/c64537b5339342bd00f7c2152b8fc23792b9f95a.1683306479.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/dwc2/platform.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index d1589ba7d322..58f53faab340 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -175,6 +175,11 @@ int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) return ret; } +static void dwc2_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) { int i, ret; @@ -185,6 +190,10 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) "error getting reset control\n"); reset_control_deassert(hsotg->reset); + ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert, + hsotg->reset); + if (ret) + return ret; hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc"); if (IS_ERR(hsotg->reset_ecc)) @@ -192,6 +201,10 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) "error getting reset control for ecc\n"); reset_control_deassert(hsotg->reset_ecc); + ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert, + hsotg->reset_ecc); + if (ret) + return ret; /* * Attempt to find a generic PHY, then look for an old style @@ -306,9 +319,6 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->ll_hw_enabled) dwc2_lowlevel_hw_disable(hsotg); - reset_control_assert(hsotg->reset); - reset_control_assert(hsotg->reset_ecc); - return 0; } -- cgit 1.4.1 From 9812b33d175cf90fda8e5f1a36855c048be4984a Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 8 May 2023 11:20:11 +0300 Subject: serial: 8250: omap: Fix freeing of resources on failed register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b9ab22c2bc8652324a803b3e2be69838920b4025 ] If serial8250_register_8250_port() fails, the SoC can hang as the deferred PMQoS work will still run as is not flushed and removed. Fixes: 61929cf0169d ("tty: serial: Add 8250-core based omap driver") Signed-off-by: Tony Lindgren Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20230508082014.23083-2-tony@atomide.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/8250/8250_omap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 3f33014022f0..79bdf933c67e 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1453,7 +1453,9 @@ static int omap8250_probe(struct platform_device *pdev) err: pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); + flush_work(&priv->qos_work); pm_runtime_disable(&pdev->dev); + cpu_latency_qos_remove_request(&priv->pm_qos_request); return ret; } -- cgit 1.4.1 From 51e5f4e7206dbbfa52c8cb3c98427f2a10c97c87 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 8 May 2023 18:33:19 +0300 Subject: clk: qcom: mmcc-msm8974: remove oxili_ocmemgx_clk [ Upstream commit 853c064b57491d739bfd0cc35ff75c5ea9c5e8f5 ] After the internal discussions, it looks like this clock is managed by RPM itself. Linux kernel should not touch it on its own, as this causes disagreement with RPM. Shutting down this clock causes the OCMEM<->GPU interface to stop working, resulting in GPU hangchecks/timeouts. Fixes: d8b212014e69 ("clk: qcom: Add support for MSM8974's multimedia clock controller (MMCC)") Suggested-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Tested-by: Luca Weiss Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230508153319.2371645-1-dmitry.baryshkov@linaro.org Signed-off-by: Sasha Levin --- drivers/clk/qcom/mmcc-msm8974.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index f74662925a58..7d0b16d688c9 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -2192,23 +2192,6 @@ static struct clk_branch ocmemcx_ocmemnoc_clk = { }, }; -static struct clk_branch oxili_ocmemgx_clk = { - .halt_reg = 0x402c, - .clkr = { - .enable_reg = 0x402c, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "oxili_ocmemgx_clk", - .parent_names = (const char *[]){ - "gfx3d_clk_src", - }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch ocmemnoc_clk = { .halt_reg = 0x50b4, .clkr = { @@ -2500,7 +2483,6 @@ static struct clk_regmap *mmcc_msm8226_clocks[] = { [MMSS_MMSSNOC_AXI_CLK] = &mmss_mmssnoc_axi_clk.clkr, [MMSS_S0_AXI_CLK] = &mmss_s0_axi_clk.clkr, [OCMEMCX_AHB_CLK] = &ocmemcx_ahb_clk.clkr, - [OXILI_OCMEMGX_CLK] = &oxili_ocmemgx_clk.clkr, [OXILI_GFX3D_CLK] = &oxili_gfx3d_clk.clkr, [OXILICX_AHB_CLK] = &oxilicx_ahb_clk.clkr, [OXILICX_AXI_CLK] = &oxilicx_axi_clk.clkr, @@ -2658,7 +2640,6 @@ static struct clk_regmap *mmcc_msm8974_clocks[] = { [MMSS_S0_AXI_CLK] = &mmss_s0_axi_clk.clkr, [OCMEMCX_AHB_CLK] = &ocmemcx_ahb_clk.clkr, [OCMEMCX_OCMEMNOC_CLK] = &ocmemcx_ocmemnoc_clk.clkr, - [OXILI_OCMEMGX_CLK] = &oxili_ocmemgx_clk.clkr, [OCMEMNOC_CLK] = &ocmemnoc_clk.clkr, [OXILI_GFX3D_CLK] = &oxili_gfx3d_clk.clkr, [OXILICX_AHB_CLK] = &oxilicx_ahb_clk.clkr, -- cgit 1.4.1 From f0cafc443c53e4be8cf63e07bf10bb354abc8888 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Mon, 1 May 2023 19:59:32 +0530 Subject: clk: qcom: camcc-sc7180: Add parent dependency to all camera GDSCs [ Upstream commit 3e4d179532423f299554cd0dedabdd9d2fdd238d ] Camera titan top GDSC is a parent supply to all other camera GDSCs. Titan top GDSC is required to be enabled before enabling any other camera GDSCs and it should be disabled only after all other camera GDSCs are disabled. Ensure this behavior by marking titan top GDSC as parent of all other camera GDSCs. Fixes: 15d09e830bbc ("clk: qcom: camcc: Add camera clock controller driver for SC7180") Signed-off-by: Taniya Das Acked-by: Stephen Boyd Reviewed-by: Bryan O'Donoghue Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230501142932.13049-1-quic_tdas@quicinc.com Signed-off-by: Sasha Levin --- drivers/clk/qcom/camcc-sc7180.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/clk/qcom/camcc-sc7180.c b/drivers/clk/qcom/camcc-sc7180.c index e2b4804695f3..8a4ba7a19ed1 100644 --- a/drivers/clk/qcom/camcc-sc7180.c +++ b/drivers/clk/qcom/camcc-sc7180.c @@ -1480,12 +1480,21 @@ static struct clk_branch cam_cc_sys_tmr_clk = { }, }; +static struct gdsc titan_top_gdsc = { + .gdscr = 0xb134, + .pd = { + .name = "titan_top_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + static struct gdsc bps_gdsc = { .gdscr = 0x6004, .pd = { .name = "bps_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, .flags = HW_CTRL, }; @@ -1495,6 +1504,7 @@ static struct gdsc ife_0_gdsc = { .name = "ife_0_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, }; static struct gdsc ife_1_gdsc = { @@ -1503,6 +1513,7 @@ static struct gdsc ife_1_gdsc = { .name = "ife_1_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .parent = &titan_top_gdsc.pd, }; static struct gdsc ipe_0_gdsc = { @@ -1512,15 +1523,9 @@ static struct gdsc ipe_0_gdsc = { }, .pwrsts = PWRSTS_OFF_ON, .flags = HW_CTRL, + .parent = &titan_top_gdsc.pd, }; -static struct gdsc titan_top_gdsc = { - .gdscr = 0xb134, - .pd = { - .name = "titan_top_gdsc", - }, - .pwrsts = PWRSTS_OFF_ON, -}; static struct clk_hw *cam_cc_sc7180_hws[] = { [CAM_CC_PLL2_OUT_EARLY] = &cam_cc_pll2_out_early.hw, -- cgit 1.4.1 From 792998a8cf6b772d5301ab417dc52072dd71adb1 Mon Sep 17 00:00:00 2001 From: Mantas Pucka Date: Tue, 25 Apr 2023 12:11:49 +0300 Subject: clk: qcom: gcc-ipq6018: Use floor ops for sdcc clocks [ Upstream commit 56e5ae0116aef87273cf1812d608645b076e4f02 ] SDCC clocks must be rounded down to avoid overclocking the controller. Fixes: d9db07f088af ("clk: qcom: Add ipq6018 Global Clock Controller support") Signed-off-by: Mantas Pucka Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1682413909-24927-1-git-send-email-mantas@8devices.com Signed-off-by: Sasha Levin --- drivers/clk/qcom/gcc-ipq6018.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/gcc-ipq6018.c b/drivers/clk/qcom/gcc-ipq6018.c index 3f9c2f61a5d9..5c5d1b04ea7a 100644 --- a/drivers/clk/qcom/gcc-ipq6018.c +++ b/drivers/clk/qcom/gcc-ipq6018.c @@ -1654,7 +1654,7 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { .name = "sdcc1_apps_clk_src", .parent_data = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, .num_parents = 4, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; -- cgit 1.4.1 From 0b3d2aa627ad63a7af4bcef011e3118d3ba3e900 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 3 Apr 2023 19:48:07 +0200 Subject: clk: qcom: gcc-qcm2290: Mark RCGs shared where applicable [ Upstream commit 7bf654a0d95e75b415f454e10627309d650762d0 ] The vast majority of shared RCGs were not marked as such. Fix it. Fixes: 496d1a13d405 ("clk: qcom: Add Global Clock Controller driver for QCM2290") Signed-off-by: Konrad Dybcio Acked-by: Shawn Guo Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230403174807.345185-1-konrad.dybcio@linaro.org Signed-off-by: Sasha Levin --- drivers/clk/qcom/gcc-qcm2290.c | 62 +++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/clk/qcom/gcc-qcm2290.c b/drivers/clk/qcom/gcc-qcm2290.c index 096deff2ba25..48995e50c6bd 100644 --- a/drivers/clk/qcom/gcc-qcm2290.c +++ b/drivers/clk/qcom/gcc-qcm2290.c @@ -650,7 +650,7 @@ static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { .name = "gcc_usb30_prim_mock_utmi_clk_src", .parent_data = gcc_parents_0, .num_parents = ARRAY_SIZE(gcc_parents_0), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -686,7 +686,7 @@ static struct clk_rcg2 gcc_camss_axi_clk_src = { .name = "gcc_camss_axi_clk_src", .parent_data = gcc_parents_4, .num_parents = ARRAY_SIZE(gcc_parents_4), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -706,7 +706,7 @@ static struct clk_rcg2 gcc_camss_cci_clk_src = { .name = "gcc_camss_cci_clk_src", .parent_data = gcc_parents_9, .num_parents = ARRAY_SIZE(gcc_parents_9), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -728,7 +728,7 @@ static struct clk_rcg2 gcc_camss_csi0phytimer_clk_src = { .name = "gcc_camss_csi0phytimer_clk_src", .parent_data = gcc_parents_5, .num_parents = ARRAY_SIZE(gcc_parents_5), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -742,7 +742,7 @@ static struct clk_rcg2 gcc_camss_csi1phytimer_clk_src = { .name = "gcc_camss_csi1phytimer_clk_src", .parent_data = gcc_parents_5, .num_parents = ARRAY_SIZE(gcc_parents_5), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -764,7 +764,7 @@ static struct clk_rcg2 gcc_camss_mclk0_clk_src = { .parent_data = gcc_parents_3, .num_parents = ARRAY_SIZE(gcc_parents_3), .flags = CLK_OPS_PARENT_ENABLE, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -779,7 +779,7 @@ static struct clk_rcg2 gcc_camss_mclk1_clk_src = { .parent_data = gcc_parents_3, .num_parents = ARRAY_SIZE(gcc_parents_3), .flags = CLK_OPS_PARENT_ENABLE, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -794,7 +794,7 @@ static struct clk_rcg2 gcc_camss_mclk2_clk_src = { .parent_data = gcc_parents_3, .num_parents = ARRAY_SIZE(gcc_parents_3), .flags = CLK_OPS_PARENT_ENABLE, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -809,7 +809,7 @@ static struct clk_rcg2 gcc_camss_mclk3_clk_src = { .parent_data = gcc_parents_3, .num_parents = ARRAY_SIZE(gcc_parents_3), .flags = CLK_OPS_PARENT_ENABLE, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -830,7 +830,7 @@ static struct clk_rcg2 gcc_camss_ope_ahb_clk_src = { .name = "gcc_camss_ope_ahb_clk_src", .parent_data = gcc_parents_6, .num_parents = ARRAY_SIZE(gcc_parents_6), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -854,7 +854,7 @@ static struct clk_rcg2 gcc_camss_ope_clk_src = { .parent_data = gcc_parents_6, .num_parents = ARRAY_SIZE(gcc_parents_6), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -888,7 +888,7 @@ static struct clk_rcg2 gcc_camss_tfe_0_clk_src = { .name = "gcc_camss_tfe_0_clk_src", .parent_data = gcc_parents_7, .num_parents = ARRAY_SIZE(gcc_parents_7), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -912,7 +912,7 @@ static struct clk_rcg2 gcc_camss_tfe_0_csid_clk_src = { .name = "gcc_camss_tfe_0_csid_clk_src", .parent_data = gcc_parents_8, .num_parents = ARRAY_SIZE(gcc_parents_8), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -926,7 +926,7 @@ static struct clk_rcg2 gcc_camss_tfe_1_clk_src = { .name = "gcc_camss_tfe_1_clk_src", .parent_data = gcc_parents_7, .num_parents = ARRAY_SIZE(gcc_parents_7), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -940,7 +940,7 @@ static struct clk_rcg2 gcc_camss_tfe_1_csid_clk_src = { .name = "gcc_camss_tfe_1_csid_clk_src", .parent_data = gcc_parents_8, .num_parents = ARRAY_SIZE(gcc_parents_8), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -963,7 +963,7 @@ static struct clk_rcg2 gcc_camss_tfe_cphy_rx_clk_src = { .parent_data = gcc_parents_10, .num_parents = ARRAY_SIZE(gcc_parents_10), .flags = CLK_OPS_PARENT_ENABLE, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -984,7 +984,7 @@ static struct clk_rcg2 gcc_camss_top_ahb_clk_src = { .name = "gcc_camss_top_ahb_clk_src", .parent_data = gcc_parents_4, .num_parents = ARRAY_SIZE(gcc_parents_4), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1006,7 +1006,7 @@ static struct clk_rcg2 gcc_gp1_clk_src = { .name = "gcc_gp1_clk_src", .parent_data = gcc_parents_2, .num_parents = ARRAY_SIZE(gcc_parents_2), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1020,7 +1020,7 @@ static struct clk_rcg2 gcc_gp2_clk_src = { .name = "gcc_gp2_clk_src", .parent_data = gcc_parents_2, .num_parents = ARRAY_SIZE(gcc_parents_2), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1034,7 +1034,7 @@ static struct clk_rcg2 gcc_gp3_clk_src = { .name = "gcc_gp3_clk_src", .parent_data = gcc_parents_2, .num_parents = ARRAY_SIZE(gcc_parents_2), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1054,7 +1054,7 @@ static struct clk_rcg2 gcc_pdm2_clk_src = { .name = "gcc_pdm2_clk_src", .parent_data = gcc_parents_0, .num_parents = ARRAY_SIZE(gcc_parents_0), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1082,7 +1082,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s0_clk_src_init = { .name = "gcc_qupv3_wrap0_s0_clk_src", .parent_data = gcc_parents_1, .num_parents = ARRAY_SIZE(gcc_parents_1), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }; static struct clk_rcg2 gcc_qupv3_wrap0_s0_clk_src = { @@ -1098,7 +1098,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = { .name = "gcc_qupv3_wrap0_s1_clk_src", .parent_data = gcc_parents_1, .num_parents = ARRAY_SIZE(gcc_parents_1), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }; static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = { @@ -1114,7 +1114,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s2_clk_src_init = { .name = "gcc_qupv3_wrap0_s2_clk_src", .parent_data = gcc_parents_1, .num_parents = ARRAY_SIZE(gcc_parents_1), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }; static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = { @@ -1130,7 +1130,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s3_clk_src_init = { .name = "gcc_qupv3_wrap0_s3_clk_src", .parent_data = gcc_parents_1, .num_parents = ARRAY_SIZE(gcc_parents_1), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }; static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = { @@ -1146,7 +1146,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = { .name = "gcc_qupv3_wrap0_s4_clk_src", .parent_data = gcc_parents_1, .num_parents = ARRAY_SIZE(gcc_parents_1), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }; static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { @@ -1162,7 +1162,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = { .name = "gcc_qupv3_wrap0_s5_clk_src", .parent_data = gcc_parents_1, .num_parents = ARRAY_SIZE(gcc_parents_1), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }; static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = { @@ -1219,7 +1219,7 @@ static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { .name = "gcc_sdcc1_ice_core_clk_src", .parent_data = gcc_parents_0, .num_parents = ARRAY_SIZE(gcc_parents_0), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1266,7 +1266,7 @@ static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { .name = "gcc_usb30_prim_master_clk_src", .parent_data = gcc_parents_0, .num_parents = ARRAY_SIZE(gcc_parents_0), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1280,7 +1280,7 @@ static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { .name = "gcc_usb3_prim_phy_aux_clk_src", .parent_data = gcc_parents_13, .num_parents = ARRAY_SIZE(gcc_parents_13), - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -1303,7 +1303,7 @@ static struct clk_rcg2 gcc_video_venus_clk_src = { .parent_data = gcc_parents_14, .num_parents = ARRAY_SIZE(gcc_parents_14), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; -- cgit 1.4.1 From a8af55f7f4879c8fdd3db60205b64ef79ef70dcc Mon Sep 17 00:00:00 2001 From: Daniil Dulov Date: Tue, 14 Mar 2023 10:04:49 -0700 Subject: media: usb: Check az6007_read() return value [ Upstream commit fdaca63186f59fc664b346c45b76576624b48e57 ] If az6007_read() returns error, there is no sence to continue. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 3af2f4f15a61 ("[media] az6007: Change the az6007 read/write routine parameter") Signed-off-by: Daniil Dulov Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/usb/dvb-usb-v2/az6007.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 62ee09f28a0b..7524c90f5da6 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -202,7 +202,8 @@ static int az6007_rc_query(struct dvb_usb_device *d) unsigned code; enum rc_proto proto; - az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10); + if (az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10) < 0) + return -EIO; if (st->data[1] == 0x44) return 0; -- cgit 1.4.1 From bc43061b42c0179a399d46c85115fe13831485ec Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Fri, 14 Apr 2023 09:55:42 +0800 Subject: media: amphion: drop repeated codec data for vc1l format [ Upstream commit 668ee1a3a1870381225002c246972419b98e4253 ] For format V4L2_PIX_FMT_VC1_ANNEX_L, the codec data is replaced with startcode, and then driver drop it, otherwise it may led to decoding error. It's amphion vpu's limitation Driver has dropped the first codec data, but need to drop the repeated codec data too. Fixes: e670f5d672ef ("media: amphion: only insert the first sequence startcode for vc1l format") Signed-off-by: Ming Qian Tested-by: xiahong.bao Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/platform/amphion/vpu_malone.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index ae094cdc9bfc..36e563d29621 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -1317,6 +1317,8 @@ static int vpu_malone_insert_scode_vc1_l_seq(struct malone_scode_t *scode) int size = 0; u8 rcv_seqhdr[MALONE_VC1_RCV_SEQ_HEADER_LEN]; + if (vpu_vb_is_codecconfig(to_vb2_v4l2_buffer(scode->vb))) + scode->need_data = 0; if (scode->inst->total_input_count) return 0; scode->need_data = 0; -- cgit 1.4.1 From c0d500726ce25394a7310f07e6aba54525b67be6 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Fri, 14 Apr 2023 09:55:43 +0800 Subject: media: amphion: drop repeated codec data for vc1g format [ Upstream commit e1d2ccc2cdd6333584aa3d5386dc667d0837c48f ] For format V4L2_PIX_FMT_VC1_ANNEX_G, the separate codec data is required only once. The repeated codec data may introduce some decoding error. so drop the repeated codec data. It's amphion vpu's limitation Fixes: e670f5d672ef ("media: amphion: only insert the first sequence startcode for vc1l format") Signed-off-by: Ming Qian Tested-by: xiahong.bao Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/platform/amphion/vpu_malone.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index 36e563d29621..c2f4fb12c3b6 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -1286,6 +1286,15 @@ static int vpu_malone_insert_scode_pic(struct malone_scode_t *scode, u32 codec_i return sizeof(hdr); } +static int vpu_malone_insert_scode_vc1_g_seq(struct malone_scode_t *scode) +{ + if (!scode->inst->total_input_count) + return 0; + if (vpu_vb_is_codecconfig(to_vb2_v4l2_buffer(scode->vb))) + scode->need_data = 0; + return 0; +} + static int vpu_malone_insert_scode_vc1_g_pic(struct malone_scode_t *scode) { struct vb2_v4l2_buffer *vbuf; @@ -1423,6 +1432,7 @@ static const struct malone_scode_handler scode_handlers[] = { }, { .pixelformat = V4L2_PIX_FMT_VC1_ANNEX_G, + .insert_scode_seq = vpu_malone_insert_scode_vc1_g_seq, .insert_scode_pic = vpu_malone_insert_scode_vc1_g_pic, }, { -- cgit 1.4.1 From cb8e8950d70e204bc6eea23191c05e9b47bfb09d Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Sat, 6 May 2023 16:47:35 +0800 Subject: media: amphion: initiate a drain of the capture queue in dynamic resolution change [ Upstream commit 076b6289b2c12d76fab248659896682830fa7766 ] The last buffer from before the change must be marked with the V4L2_BUF_FLAG_LAST flag, similarly to the Drain sequence above. initiate a drain of the capture queue in dynamic resolution change Fixes: 6de8d628df6e ("media: amphion: add v4l2 m2m vpu decoder stateful driver") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/platform/amphion/vdec.c | 7 ++++--- drivers/media/platform/amphion/venc.c | 4 ++-- drivers/media/platform/amphion/vpu_v4l2.c | 5 +++-- drivers/media/platform/amphion/vpu_v4l2.h | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 4918547793dc..c08b5a2bfc1d 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -229,6 +229,7 @@ static void vdec_handle_resolution_change(struct vpu_inst *inst) vdec->source_change--; vpu_notify_source_change(inst); + vpu_set_last_buffer_dequeued(inst, false); } static int vdec_update_state(struct vpu_inst *inst, enum vpu_codec_state state, u32 force) @@ -264,7 +265,7 @@ static void vdec_set_last_buffer_dequeued(struct vpu_inst *inst) return; if (vdec->eos_received) { - if (!vpu_set_last_buffer_dequeued(inst)) { + if (!vpu_set_last_buffer_dequeued(inst, true)) { vdec->eos_received--; vdec_update_state(inst, VPU_CODEC_STATE_DRAIN, 0); } @@ -517,7 +518,7 @@ static int vdec_drain(struct vpu_inst *inst) return 0; if (!vdec->params.frame_count) { - vpu_set_last_buffer_dequeued(inst); + vpu_set_last_buffer_dequeued(inst, true); return 0; } @@ -556,7 +557,7 @@ static int vdec_cmd_stop(struct vpu_inst *inst) vpu_trace(inst->dev, "[%d]\n", inst->id); if (inst->state == VPU_CODEC_STATE_DEINIT) { - vpu_set_last_buffer_dequeued(inst); + vpu_set_last_buffer_dequeued(inst, true); } else { vdec->drain = 1; vdec_drain(inst); diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 37212f087fdd..e8cb22da938e 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -468,7 +468,7 @@ static int venc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd vpu_inst_lock(inst); if (cmd->cmd == V4L2_ENC_CMD_STOP) { if (inst->state == VPU_CODEC_STATE_DEINIT) - vpu_set_last_buffer_dequeued(inst); + vpu_set_last_buffer_dequeued(inst, true); else venc_request_eos(inst); } @@ -888,7 +888,7 @@ static void venc_set_last_buffer_dequeued(struct vpu_inst *inst) struct venc_t *venc = inst->priv; if (venc->stopped && list_empty(&venc->frames)) - vpu_set_last_buffer_dequeued(inst); + vpu_set_last_buffer_dequeued(inst, true); } static void venc_stop_done(struct vpu_inst *inst) diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index 590d1084e5a5..a74953191c22 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -100,7 +100,7 @@ int vpu_notify_source_change(struct vpu_inst *inst) return 0; } -int vpu_set_last_buffer_dequeued(struct vpu_inst *inst) +int vpu_set_last_buffer_dequeued(struct vpu_inst *inst, bool eos) { struct vb2_queue *q; @@ -116,7 +116,8 @@ int vpu_set_last_buffer_dequeued(struct vpu_inst *inst) vpu_trace(inst->dev, "last buffer dequeued\n"); q->last_buffer_dequeued = true; wake_up(&q->done_wq); - vpu_notify_eos(inst); + if (eos) + vpu_notify_eos(inst); return 0; } diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h index 795ca33a6a50..000af24a06ba 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.h +++ b/drivers/media/platform/amphion/vpu_v4l2.h @@ -26,7 +26,7 @@ struct vb2_v4l2_buffer *vpu_find_buf_by_idx(struct vpu_inst *inst, u32 type, u32 void vpu_v4l2_set_error(struct vpu_inst *inst); int vpu_notify_eos(struct vpu_inst *inst); int vpu_notify_source_change(struct vpu_inst *inst); -int vpu_set_last_buffer_dequeued(struct vpu_inst *inst); +int vpu_set_last_buffer_dequeued(struct vpu_inst *inst, bool eos); void vpu_vb2_buffers_return(struct vpu_inst *inst, unsigned int type, enum vb2_buffer_state state); int vpu_get_num_buffers(struct vpu_inst *inst, u32 type); bool vpu_is_source_empty(struct vpu_inst *inst); -- cgit 1.4.1 From e230146b86b207d7b2214c0a8cce7d4df4761806 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 18 May 2023 15:36:49 +0200 Subject: media: videodev2.h: Fix struct v4l2_input tuner index comment [ Upstream commit 26ae58f65e64fa7ba61d64bae752e59e08380c6a ] VIDIOC_ENUMINPUT documentation describes the tuner field of struct v4l2_input as index: Documentation/userspace-api/media/v4l/vidioc-enuminput.rst " * - __u32 - ``tuner`` - Capture devices can have zero or more tuners (RF demodulators). When the ``type`` is set to ``V4L2_INPUT_TYPE_TUNER`` this is an RF connector and this field identifies the tuner. It corresponds to struct :c:type:`v4l2_tuner` field ``index``. For details on tuners see :ref:`tuner`. " Drivers I could find also use the 'tuner' field as an index, e.g.: drivers/media/pci/bt8xx/bttv-driver.c bttv_enum_input() drivers/media/usb/go7007/go7007-v4l2.c vidioc_enum_input() However, the UAPI comment claims this field is 'enum v4l2_tuner_type': include/uapi/linux/videodev2.h This field being 'enum v4l2_tuner_type' is unlikely as it seems to be never used that way in drivers, and documentation confirms it. It seem this comment got in accidentally in the commit which this patch fixes. Fix the UAPI comment to stop confusion. This was pointed out by Dmitry while reviewing VIDIOC_ENUMINPUT support for strace. Fixes: 6016af82eafc ("[media] v4l2: use __u32 rather than enums in ioctl() structs") Signed-off-by: Marek Vasut Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- include/uapi/linux/videodev2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 29da1f4b4578..45fa03882ef1 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1693,7 +1693,7 @@ struct v4l2_input { __u8 name[32]; /* Label */ __u32 type; /* Type of input */ __u32 audioset; /* Associated audios (bitfield) */ - __u32 tuner; /* enum v4l2_tuner_type */ + __u32 tuner; /* Tuner index */ v4l2_std_id std; __u32 status; __u32 capabilities; -- cgit 1.4.1 From 8abb53c5167cfb5bb275512a3da4ec2468478626 Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Tue, 23 May 2023 07:59:32 +0800 Subject: media: usb: siano: Fix warning due to null work_func_t function pointer [ Upstream commit 6f489a966fbeb0da63d45c2c66a8957eab604bf6 ] The previous commit ebad8e731c1c ("media: usb: siano: Fix use after free bugs caused by do_submit_urb") adds cancel_work_sync() in smsusb_stop_streaming(). But smsusb_stop_streaming() may be called, even if the work_struct surb->wq has not been initialized. As a result, the warning will occur. One of the processes that could lead to warning is shown below: smsusb_probe() smsusb_init_device() if (!dev->in_ep || !dev->out_ep || align < 0) { smsusb_term_device(intf); smsusb_stop_streaming() cancel_work_sync(&dev->surbs[i].wq); __cancel_work_timer() __flush_work() if (WARN_ON(!work->func)) // work->func is null The log reported by syzbot is shown below: WARNING: CPU: 0 PID: 897 at kernel/workqueue.c:3066 __flush_work+0x798/0xa80 kernel/workqueue.c:3063 Modules linked in: CPU: 0 PID: 897 Comm: kworker/0:2 Not tainted 6.2.0-rc1-syzkaller #0 RIP: 0010:__flush_work+0x798/0xa80 kernel/workqueue.c:3066 ... RSP: 0018:ffffc9000464ebf8 EFLAGS: 00010246 RAX: 1ffff11002dbb420 RBX: 0000000000000021 RCX: 1ffffffff204fa4e RDX: dffffc0000000000 RSI: 0000000000000001 RDI: ffff888016dda0e8 RBP: ffffc9000464ed98 R08: 0000000000000001 R09: ffffffff90253b2f R10: 0000000000000001 R11: 0000000000000000 R12: ffff888016dda0e8 R13: ffff888016dda0e8 R14: ffff888016dda100 R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff8880b9a00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ffd4331efe8 CR3: 000000000b48e000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: __cancel_work_timer+0x315/0x460 kernel/workqueue.c:3160 smsusb_stop_streaming drivers/media/usb/siano/smsusb.c:182 [inline] smsusb_term_device+0xda/0x2d0 drivers/media/usb/siano/smsusb.c:344 smsusb_init_device+0x400/0x9ce drivers/media/usb/siano/smsusb.c:419 smsusb_probe+0xbbd/0xc55 drivers/media/usb/siano/smsusb.c:567 ... This patch adds check before cancel_work_sync(). If surb->wq has not been initialized, the cancel_work_sync() will not be executed. Reported-by: syzbot+27b0b464864741b18b99@syzkaller.appspotmail.com Fixes: ebad8e731c1c ("media: usb: siano: Fix use after free bugs caused by do_submit_urb") Signed-off-by: Duoming Zhou Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/usb/siano/smsusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index 6f443c542c6d..640737d3b8ae 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -179,7 +179,8 @@ static void smsusb_stop_streaming(struct smsusb_device_t *dev) for (i = 0; i < MAX_URBS; i++) { usb_kill_urb(&dev->surbs[i].urb); - cancel_work_sync(&dev->surbs[i].wq); + if (dev->surbs[i].wq.func) + cancel_work_sync(&dev->surbs[i].wq); if (dev->surbs[i].cb) { smscore_putbuffer(dev->coredev, dev->surbs[i].cb); -- cgit 1.4.1 From 1ac45cab77d68664001d6ab1932dbcf695b31e0d Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Tue, 2 May 2023 11:35:46 +0100 Subject: media: i2c: Correct format propagation for st-mipid02 [ Upstream commit 306c3190b30d4d6a098888b9d7d4cefaa0ddcb91 ] Format propagation in the st-mipid02 driver is incorrect in that when setting format for V4L2_SUBDEV_FORMAT_TRY on the source pad, the _active_ rather than _try_ format from the sink pad is propagated. This causes problems with format negotiation - update the function to propagate the correct format. Fixes: 642bb5e88fed ("media: st-mipid02: MIPID02 CSI-2 to PARALLEL bridge driver") Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/i2c/st-mipid02.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index 31b89aff0e86..f20f87562bf1 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -736,8 +736,13 @@ static void mipid02_set_fmt_source(struct v4l2_subdev *sd, { struct mipid02_dev *bridge = to_mipid02_dev(sd); - /* source pad mirror active sink pad */ - format->format = bridge->fmt; + /* source pad mirror sink pad */ + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + format->format = bridge->fmt; + else + format->format = *v4l2_subdev_get_try_format(sd, sd_state, + MIPID02_SINK_0); + /* but code may need to be converted */ format->format.code = serial_to_parallel_code(format->format.code); -- cgit 1.4.1 From 42ec6269f98edd915ee37da3c6456bb6243ea56a Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Tue, 25 Apr 2023 11:47:47 +0200 Subject: media: hi846: fix usage of pm_runtime_get_if_in_use() [ Upstream commit 04fc06f6dc1592ed5d675311ac50d8fba5db62ab ] pm_runtime_get_if_in_use() does not only return nonzero values when the device is in use, it can return a negative errno too. And especially during resuming from system suspend, when runtime pm is not yet up again, -EAGAIN is being returned, so the subsequent pm_runtime_put() call results in a refcount underflow. Fix system-resume by handling -EAGAIN of pm_runtime_get_if_in_use(). Signed-off-by: Martin Kepplinger Fixes: e8c0882685f9 ("media: i2c: add driver for the SK Hynix Hi-846 8M pixel camera") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/i2c/hi846.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index 306dc35e925f..f8709cdf28b3 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1353,7 +1353,8 @@ static int hi846_set_ctrl(struct v4l2_ctrl *ctrl) exposure_max); } - if (!pm_runtime_get_if_in_use(&client->dev)) + ret = pm_runtime_get_if_in_use(&client->dev); + if (!ret || ret == -EAGAIN) return 0; switch (ctrl->id) { -- cgit 1.4.1 From 0e481ef854a53cc2891aac6111aa9c7a673bec6e Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 25 May 2023 02:40:07 +0100 Subject: media: mediatek: vcodec: using decoder status instead of core work count [ Upstream commit 2864e304faec04c2674328aad0e820a9cd84cdec ] Adding the definition of decoder status to separate different decoder period for core hardware. core_work_cnt is the number of core work queued to work queue, the control is very complex, leading to some unreasonable test result. Using parameter status to indicate whether queue core work to work queue. Fixes: 2e0ef56d81cb ("media: mediatek: vcodec: making sure queue_work successfully") Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- .../platform/mediatek/vcodec/vdec_msg_queue.c | 33 +++++++++------------- .../platform/mediatek/vcodec/vdec_msg_queue.h | 16 +++++++++-- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c index f3073d1e7f42..03f8d7cd8edd 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c @@ -71,7 +71,6 @@ static void vdec_msg_queue_dec(struct vdec_msg_queue *msg_queue, int hardware_in int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf *buf) { struct list_head *head; - int status; head = vdec_get_buf_list(msg_ctx->hardware_index, buf); if (!head) { @@ -87,12 +86,9 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf if (msg_ctx->hardware_index != MTK_VDEC_CORE) { wake_up_all(&msg_ctx->ready_to_use); } else { - if (buf->ctx->msg_queue.core_work_cnt < - atomic_read(&buf->ctx->msg_queue.core_list_cnt)) { - status = queue_work(buf->ctx->dev->core_workqueue, - &buf->ctx->msg_queue.core_work); - if (status) - buf->ctx->msg_queue.core_work_cnt++; + if (!(buf->ctx->msg_queue.status & CONTEXT_LIST_QUEUED)) { + queue_work(buf->ctx->dev->core_workqueue, &buf->ctx->msg_queue.core_work); + buf->ctx->msg_queue.status |= CONTEXT_LIST_QUEUED; } } @@ -261,7 +257,10 @@ static void vdec_msg_queue_core_work(struct work_struct *work) container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue); struct mtk_vcodec_dev *dev = ctx->dev; struct vdec_lat_buf *lat_buf; - int status; + + spin_lock(&ctx->dev->msg_queue_core_ctx.ready_lock); + ctx->msg_queue.status &= ~CONTEXT_LIST_QUEUED; + spin_unlock(&ctx->dev->msg_queue_core_ctx.ready_lock); lat_buf = vdec_msg_queue_dqbuf(&dev->msg_queue_core_ctx); if (!lat_buf) @@ -278,17 +277,13 @@ static void vdec_msg_queue_core_work(struct work_struct *work) vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); wake_up_all(&ctx->msg_queue.core_dec_done); - spin_lock(&dev->msg_queue_core_ctx.ready_lock); - lat_buf->ctx->msg_queue.core_work_cnt--; - - if (lat_buf->ctx->msg_queue.core_work_cnt < - atomic_read(&lat_buf->ctx->msg_queue.core_list_cnt)) { - status = queue_work(lat_buf->ctx->dev->core_workqueue, - &lat_buf->ctx->msg_queue.core_work); - if (status) - lat_buf->ctx->msg_queue.core_work_cnt++; + if (!(ctx->msg_queue.status & CONTEXT_LIST_QUEUED) && + atomic_read(&msg_queue->core_list_cnt)) { + spin_lock(&ctx->dev->msg_queue_core_ctx.ready_lock); + ctx->msg_queue.status |= CONTEXT_LIST_QUEUED; + spin_unlock(&ctx->dev->msg_queue_core_ctx.ready_lock); + queue_work(ctx->dev->core_workqueue, &msg_queue->core_work); } - spin_unlock(&dev->msg_queue_core_ctx.ready_lock); } int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, @@ -303,13 +298,13 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, return 0; msg_queue->ctx = ctx; - msg_queue->core_work_cnt = 0; vdec_msg_queue_init_ctx(&msg_queue->lat_ctx, MTK_VDEC_LAT0); INIT_WORK(&msg_queue->core_work, vdec_msg_queue_core_work); atomic_set(&msg_queue->lat_list_cnt, 0); atomic_set(&msg_queue->core_list_cnt, 0); init_waitqueue_head(&msg_queue->core_dec_done); + msg_queue->status = CONTEXT_LIST_EMPTY; msg_queue->wdma_addr.size = vde_msg_queue_get_trans_size(ctx->picinfo.buf_w, diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h index a5d44bc97c16..8f82d1484772 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h @@ -21,6 +21,18 @@ struct mtk_vcodec_ctx; struct mtk_vcodec_dev; typedef int (*core_decode_cb_t)(struct vdec_lat_buf *lat_buf); +/** + * enum core_ctx_status - Context decode status for core hardwre. + * @CONTEXT_LIST_EMPTY: No buffer queued on core hardware(must always be 0) + * @CONTEXT_LIST_QUEUED: Buffer queued to core work list + * @CONTEXT_LIST_DEC_DONE: context decode done + */ +enum core_ctx_status { + CONTEXT_LIST_EMPTY = 0, + CONTEXT_LIST_QUEUED, + CONTEXT_LIST_DEC_DONE, +}; + /** * struct vdec_msg_queue_ctx - represents a queue for buffers ready to be processed * @ready_to_use: ready used queue used to signalize when get a job queue @@ -77,7 +89,7 @@ struct vdec_lat_buf { * @lat_list_cnt: used to record each instance lat list count * @core_list_cnt: used to record each instance core list count * @core_dec_done: core work queue decode done event - * @core_work_cnt: the number of core work in work queue + * @status: current context decode status for core hardware */ struct vdec_msg_queue { struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT]; @@ -93,7 +105,7 @@ struct vdec_msg_queue { atomic_t lat_list_cnt; atomic_t core_list_cnt; wait_queue_head_t core_dec_done; - int core_work_cnt; + int status; }; /** -- cgit 1.4.1 From b20854ef6c4955be3310975a72f02d92cb01d6d4 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 7 Nov 2022 14:28:59 +0100 Subject: clk: qcom: reset: support resetting multiple bits [ Upstream commit 4a5210893625f89723ea210d7c630b730abb37ad ] This patch adds the support for giving the complete bitmask in reset structure and reset operation will use this bitmask for all reset operations. Currently, reset structure only takes a single bit for each reset and then calculates the bitmask by using the BIT() macro. However, this is not sufficient anymore for newer SoC-s like IPQ8074, IPQ6018 and more, since their networking resets require multiple bits to be asserted in order to properly reset the HW block completely. So, in order to allow asserting multiple bits add "bitmask" field to qcom_reset_map, and then use that bitmask value if its populated in the driver, if its not populated, then we just default to existing behaviour and calculate the bitmask on the fly. Signed-off-by: Robert Marko Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221107132901.489240-1-robimarko@gmail.com Stable-dep-of: 349b5bed539b ("clk: qcom: ipq6018: fix networking resets") Signed-off-by: Sasha Levin --- drivers/clk/qcom/reset.c | 4 ++-- drivers/clk/qcom/reset.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/reset.c b/drivers/clk/qcom/reset.c index 2a16adb572d2..0e914ec7aeae 100644 --- a/drivers/clk/qcom/reset.c +++ b/drivers/clk/qcom/reset.c @@ -30,7 +30,7 @@ qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) rst = to_qcom_reset_controller(rcdev); map = &rst->reset_map[id]; - mask = BIT(map->bit); + mask = map->bitmask ? map->bitmask : BIT(map->bit); return regmap_update_bits(rst->regmap, map->reg, mask, mask); } @@ -44,7 +44,7 @@ qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) rst = to_qcom_reset_controller(rcdev); map = &rst->reset_map[id]; - mask = BIT(map->bit); + mask = map->bitmask ? map->bitmask : BIT(map->bit); return regmap_update_bits(rst->regmap, map->reg, mask, 0); } diff --git a/drivers/clk/qcom/reset.h b/drivers/clk/qcom/reset.h index b8c113582072..9a47c838d9b1 100644 --- a/drivers/clk/qcom/reset.h +++ b/drivers/clk/qcom/reset.h @@ -12,6 +12,7 @@ struct qcom_reset_map { unsigned int reg; u8 bit; u8 udelay; + u32 bitmask; }; struct regmap; -- cgit 1.4.1 From b80c4629e966ec6df3faa10984ebd22f9fb537b1 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 26 May 2023 21:08:55 +0200 Subject: clk: qcom: ipq6018: fix networking resets [ Upstream commit 349b5bed539b491b7894a5186a895751fd8ba6c7 ] Networking resets in IPQ6018 all use bitmask as they require multiple bits to be set and cleared instead of a single bit. So, current networking resets have the same register and bit 0 set which is clearly incorrect. Fixes: d9db07f088af ("clk: qcom: Add ipq6018 Global Clock Controller support") Signed-off-by: Robert Marko Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230526190855.2941291-2-robimarko@gmail.com Signed-off-by: Sasha Levin --- drivers/clk/qcom/gcc-ipq6018.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/clk/qcom/gcc-ipq6018.c b/drivers/clk/qcom/gcc-ipq6018.c index 5c5d1b04ea7a..cde62a11f573 100644 --- a/drivers/clk/qcom/gcc-ipq6018.c +++ b/drivers/clk/qcom/gcc-ipq6018.c @@ -4517,24 +4517,24 @@ static const struct qcom_reset_map gcc_ipq6018_resets[] = { [GCC_PCIE0_AHB_ARES] = { 0x75040, 5 }, [GCC_PCIE0_AXI_MASTER_STICKY_ARES] = { 0x75040, 6 }, [GCC_PCIE0_AXI_SLAVE_STICKY_ARES] = { 0x75040, 7 }, - [GCC_PPE_FULL_RESET] = { 0x68014, 0 }, - [GCC_UNIPHY0_SOFT_RESET] = { 0x56004, 0 }, + [GCC_PPE_FULL_RESET] = { .reg = 0x68014, .bitmask = 0xf0000 }, + [GCC_UNIPHY0_SOFT_RESET] = { .reg = 0x56004, .bitmask = 0x3ff2 }, [GCC_UNIPHY0_XPCS_RESET] = { 0x56004, 2 }, - [GCC_UNIPHY1_SOFT_RESET] = { 0x56104, 0 }, + [GCC_UNIPHY1_SOFT_RESET] = { .reg = 0x56104, .bitmask = 0x32 }, [GCC_UNIPHY1_XPCS_RESET] = { 0x56104, 2 }, - [GCC_EDMA_HW_RESET] = { 0x68014, 0 }, - [GCC_NSSPORT1_RESET] = { 0x68014, 0 }, - [GCC_NSSPORT2_RESET] = { 0x68014, 0 }, - [GCC_NSSPORT3_RESET] = { 0x68014, 0 }, - [GCC_NSSPORT4_RESET] = { 0x68014, 0 }, - [GCC_NSSPORT5_RESET] = { 0x68014, 0 }, - [GCC_UNIPHY0_PORT1_ARES] = { 0x56004, 0 }, - [GCC_UNIPHY0_PORT2_ARES] = { 0x56004, 0 }, - [GCC_UNIPHY0_PORT3_ARES] = { 0x56004, 0 }, - [GCC_UNIPHY0_PORT4_ARES] = { 0x56004, 0 }, - [GCC_UNIPHY0_PORT5_ARES] = { 0x56004, 0 }, - [GCC_UNIPHY0_PORT_4_5_RESET] = { 0x56004, 0 }, - [GCC_UNIPHY0_PORT_4_RESET] = { 0x56004, 0 }, + [GCC_EDMA_HW_RESET] = { .reg = 0x68014, .bitmask = 0x300000 }, + [GCC_NSSPORT1_RESET] = { .reg = 0x68014, .bitmask = 0x1000003 }, + [GCC_NSSPORT2_RESET] = { .reg = 0x68014, .bitmask = 0x200000c }, + [GCC_NSSPORT3_RESET] = { .reg = 0x68014, .bitmask = 0x4000030 }, + [GCC_NSSPORT4_RESET] = { .reg = 0x68014, .bitmask = 0x8000300 }, + [GCC_NSSPORT5_RESET] = { .reg = 0x68014, .bitmask = 0x10000c00 }, + [GCC_UNIPHY0_PORT1_ARES] = { .reg = 0x56004, .bitmask = 0x30 }, + [GCC_UNIPHY0_PORT2_ARES] = { .reg = 0x56004, .bitmask = 0xc0 }, + [GCC_UNIPHY0_PORT3_ARES] = { .reg = 0x56004, .bitmask = 0x300 }, + [GCC_UNIPHY0_PORT4_ARES] = { .reg = 0x56004, .bitmask = 0xc00 }, + [GCC_UNIPHY0_PORT5_ARES] = { .reg = 0x56004, .bitmask = 0x3000 }, + [GCC_UNIPHY0_PORT_4_5_RESET] = { .reg = 0x56004, .bitmask = 0x3c02 }, + [GCC_UNIPHY0_PORT_4_RESET] = { .reg = 0x56004, .bitmask = 0xc02 }, [GCC_LPASS_BCR] = {0x1F000, 0}, [GCC_UBI32_TBU_BCR] = {0x65000, 0}, [GCC_LPASS_TBU_BCR] = {0x6C000, 0}, -- cgit 1.4.1 From 203ab7670432fba3fe74e61b119aaac0f00624dd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 14 Apr 2023 13:06:35 +0200 Subject: clk: qcom: dispcc-qcm2290: Fix BI_TCXO_AO handling [ Upstream commit 92dfee0fc889b5b00ffb6b1de87ce64c483bcb7b ] BI_TCXO_AO (.fw_name = "bi_tcxo_ao") was previously made to reuse the same parent enum entry as BI_TCXO (.fw_name = "bi_tcxo") in parent_map_2. Resolve it by introducing its own entry in the parent enum and correctly assigning it in disp_cc_parent_map_2[]. Fixes: cc517ea3333f ("clk: qcom: Add display clock controller driver for QCM2290") Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230412-topic-qcm_dispcc-v2-1-bce7dd512fe4@linaro.org Signed-off-by: Sasha Levin --- drivers/clk/qcom/dispcc-qcm2290.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/dispcc-qcm2290.c b/drivers/clk/qcom/dispcc-qcm2290.c index 24755dc841f9..4a4993470852 100644 --- a/drivers/clk/qcom/dispcc-qcm2290.c +++ b/drivers/clk/qcom/dispcc-qcm2290.c @@ -23,6 +23,7 @@ enum { P_BI_TCXO, + P_BI_TCXO_AO, P_DISP_CC_PLL0_OUT_MAIN, P_DSI0_PHY_PLL_OUT_BYTECLK, P_DSI0_PHY_PLL_OUT_DSICLK, @@ -81,7 +82,7 @@ static const struct clk_parent_data disp_cc_parent_data_1[] = { }; static const struct parent_map disp_cc_parent_map_2[] = { - { P_BI_TCXO, 0 }, + { P_BI_TCXO_AO, 0 }, { P_GPLL0_OUT_MAIN, 4 }, }; @@ -150,7 +151,7 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { }; static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = { - F(19200000, P_BI_TCXO, 1, 0, 0), + F(19200000, P_BI_TCXO_AO, 1, 0, 0), F(37500000, P_GPLL0_OUT_MAIN, 8, 0, 0), F(75000000, P_GPLL0_OUT_MAIN, 4, 0, 0), { } -- cgit 1.4.1 From 3c4f7d49909d94669bacce152d811c4479698a0f Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 14 Apr 2023 13:06:36 +0200 Subject: clk: qcom: dispcc-qcm2290: Fix GPLL0_OUT_DIV handling [ Upstream commit 63d56adf04b5795e54440dc5b7afddecb2966863 ] GPLL0_OUT_DIV (.fw_name = "gcc_disp_gpll0_div_clk_src") was previously made to reuse the same parent enum entry as GPLL0_OUT_MAIN (.fw_name = "gcc_disp_gpll0_clk_src") in parent_map_2. Resolve it by introducing its own entry in the parent enum and correctly assigning it in disp_cc_parent_map_2[]. Fixes: cc517ea3333f ("clk: qcom: Add display clock controller driver for QCM2290") Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230412-topic-qcm_dispcc-v2-2-bce7dd512fe4@linaro.org Signed-off-by: Sasha Levin --- drivers/clk/qcom/dispcc-qcm2290.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/clk/qcom/dispcc-qcm2290.c b/drivers/clk/qcom/dispcc-qcm2290.c index 4a4993470852..5cec98c4e837 100644 --- a/drivers/clk/qcom/dispcc-qcm2290.c +++ b/drivers/clk/qcom/dispcc-qcm2290.c @@ -27,6 +27,7 @@ enum { P_DISP_CC_PLL0_OUT_MAIN, P_DSI0_PHY_PLL_OUT_BYTECLK, P_DSI0_PHY_PLL_OUT_DSICLK, + P_GPLL0_OUT_DIV, P_GPLL0_OUT_MAIN, P_SLEEP_CLK, }; @@ -83,7 +84,7 @@ static const struct clk_parent_data disp_cc_parent_data_1[] = { static const struct parent_map disp_cc_parent_map_2[] = { { P_BI_TCXO_AO, 0 }, - { P_GPLL0_OUT_MAIN, 4 }, + { P_GPLL0_OUT_DIV, 4 }, }; static const struct clk_parent_data disp_cc_parent_data_2[] = { @@ -152,8 +153,8 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = { F(19200000, P_BI_TCXO_AO, 1, 0, 0), - F(37500000, P_GPLL0_OUT_MAIN, 8, 0, 0), - F(75000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(37500000, P_GPLL0_OUT_DIV, 8, 0, 0), + F(75000000, P_GPLL0_OUT_DIV, 4, 0, 0), { } }; -- cgit 1.4.1 From 2ed441a76374f6def22bee39bcef88505e1dd8e4 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 7 May 2023 20:53:34 +0300 Subject: clk: qcom: mmcc-msm8974: use clk_rcg2_shared_ops for mdp_clk_src clock [ Upstream commit 8fd492e77ff71f68f7311c22f7bc960182465cd7 ] The mdp_clk_src clock should not be turned off. Instead it should be 'parked' to the XO, as most of other mdp_clk_src clocks. Fix that by using the clk_rcg2_shared_ops. Fixes: d8b212014e69 ("clk: qcom: Add support for MSM8974's multimedia clock controller (MMCC)") Signed-off-by: Dmitry Baryshkov Tested-by: Luca Weiss Acked-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230507175335.2321503-1-dmitry.baryshkov@linaro.org Signed-off-by: Sasha Levin --- drivers/clk/qcom/mmcc-msm8974.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index 7d0b16d688c9..7e3f88658f6b 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -473,7 +473,7 @@ static struct clk_rcg2 mdp_clk_src = { .name = "mdp_clk_src", .parent_names = mmcc_xo_mmpll0_dsi_hdmi_gpll0, .num_parents = 6, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; -- cgit 1.4.1 From 6aecf5e19b8a6f052d5c7b75a28ebf46aa61b1dc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 22:25:55 +0200 Subject: staging: vchiq_arm: mark vchiq_platform_init() static [ Upstream commit e152c58d7a48194d6b530d8e004d650fd01568b6 ] This function has no callers from other files, and the declaration was removed a while ago, causing a W=1 warning: drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c:465:5: error: no previous prototype for 'vchiq_platform_init' Marking it static solves this problem but introduces a new warning since gcc determines that 'g_fragments_base' is never initialized in some kernel configurations: In file included from include/linux/string.h:254, from include/linux/bitmap.h:11, from include/linux/cpumask.h:12, from include/linux/mm_types_task.h:14, from include/linux/mm_types.h:5, from include/linux/buildid.h:5, from include/linux/module.h:14, from drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c:8: In function 'memcpy_to_page', inlined from 'free_pagelist' at drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c:433:4: include/linux/fortify-string.h:57:33: error: argument 2 null where non-null expected [-Werror=nonnull] include/linux/highmem.h:427:9: note: in expansion of macro 'memcpy' 427 | memcpy(to + offset, from, len); | ^~~~~~ Add a NULL pointer check for this in addition to the static annotation to avoid both. Fixes: 89cc4218f640 ("staging: vchiq_arm: drop unnecessary declarations") Signed-off-by: Arnd Bergmann Reviewed-by: Florian Fainelli Reviewed-by: Umang Jain Link: https://lore.kernel.org/r/20230516202603.560554-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index dc33490ba7fb..705c5e283c27 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -415,7 +415,7 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel pagelistinfo->scatterlist_mapped = 0; /* Deal with any partial cache lines (fragments) */ - if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) { + if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS && g_fragments_base) { char *fragments = g_fragments_base + (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS) * g_fragments_size; @@ -462,7 +462,7 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel cleanup_pagelistinfo(instance, pagelistinfo); } -int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) +static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) { struct device *dev = &pdev->dev; struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev); -- cgit 1.4.1 From c3b322b84ab5dda7eaca9ded763628b7467734f4 Mon Sep 17 00:00:00 2001 From: Vladislav Efanov Date: Wed, 17 May 2023 20:25:18 +0300 Subject: usb: dwc3: qcom: Fix potential memory leak [ Upstream commit 097fb3ee710d4de83b8d4f5589e8ee13e0f0541e ] Function dwc3_qcom_probe() allocates memory for resource structure which is pointed by parent_res pointer. This memory is not freed. This leads to memory leak. Use stack memory to prevent memory leak. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 2bc02355f8ba ("usb: dwc3: qcom: Add support for booting with ACPI") Signed-off-by: Vladislav Efanov Acked-by: Shawn Guo Link: https://lore.kernel.org/r/20230517172518.442591-1-VEfanov@ispras.ru Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/dwc3/dwc3-qcom.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 79b22abf9727..482260182d65 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -800,6 +800,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dwc3_qcom *qcom; struct resource *res, *parent_res = NULL; + struct resource local_res; int ret, i; bool ignore_pipe_clk; bool wakeup_source; @@ -851,9 +852,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (np) { parent_res = res; } else { - parent_res = kmemdup(res, sizeof(struct resource), GFP_KERNEL); - if (!parent_res) - return -ENOMEM; + memcpy(&local_res, res, sizeof(struct resource)); + parent_res = &local_res; parent_res->start = res->start + qcom->acpi_pdata->qscratch_base_offset; -- cgit 1.4.1 From e60a827ac074ce6bd58305fe5a86afab5fce6a04 Mon Sep 17 00:00:00 2001 From: Prashanth K Date: Fri, 5 May 2023 14:48:37 +0530 Subject: usb: gadget: u_serial: Add null pointer check in gserial_suspend [ Upstream commit 2f6ecb89fe8feb2b60a53325b0eeb9866d88909a ] Consider a case where gserial_disconnect has already cleared gser->ioport. And if gserial_suspend gets called afterwards, it will lead to accessing of gser->ioport and thus causing null pointer dereference. Avoid this by adding a null pointer check. Added a static spinlock to prevent gser->ioport from becoming null after the newly added null pointer check. Fixes: aba3a8d01d62 ("usb: gadget: u_serial: add suspend resume callbacks") Signed-off-by: Prashanth K Link: https://lore.kernel.org/r/1683278317-11774-1-git-send-email-quic_prashk@quicinc.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/gadget/function/u_serial.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index db6fd0238d4b..ea2c5b6cde8c 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1419,10 +1419,19 @@ EXPORT_SYMBOL_GPL(gserial_disconnect); void gserial_suspend(struct gserial *gser) { - struct gs_port *port = gser->ioport; + struct gs_port *port; unsigned long flags; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&serial_port_lock, flags); + port = gser->ioport; + + if (!port) { + spin_unlock_irqrestore(&serial_port_lock, flags); + return; + } + + spin_lock(&port->port_lock); + spin_unlock(&serial_port_lock); port->suspended = true; spin_unlock_irqrestore(&port->port_lock, flags); } -- cgit 1.4.1 From e4c94de6ca2f70cee0ebe703ce452835046b2831 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 Mar 2023 16:39:52 +0200 Subject: extcon: Fix kernel doc of property fields to avoid warnings [ Upstream commit 7e77e0b7a9f4cdf91cb0950749b40c840ea63efc ] Kernel documentation has to be synchronized with a code, otherwise the validator is not happy: Function parameter or member 'usb_propval' not described in 'extcon_cable' Function parameter or member 'chg_propval' not described in 'extcon_cable' Function parameter or member 'jack_propval' not described in 'extcon_cable' Function parameter or member 'disp_propval' not described in 'extcon_cable' Describe the fields added in the past. Fixes: 067c1652e7a7 ("extcon: Add the support for extcon property according to extcon type") Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi Signed-off-by: Sasha Levin --- drivers/extcon/extcon.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index e1c71359b605..01fd920685c2 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -206,6 +206,10 @@ static const struct __extcon_info { * @attr_name: "name" sysfs entry * @attr_state: "state" sysfs entry * @attrs: the array pointing to attr_name and attr_state for attr_g + * @usb_propval: the array of USB connector properties + * @chg_propval: the array of charger connector properties + * @jack_propval: the array of jack connector properties + * @disp_propval: the array of display connector properties */ struct extcon_cable { struct extcon_dev *edev; -- cgit 1.4.1 From ac961d0571b4faad9ee6970248ccfc124c5b1463 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 Mar 2023 16:39:53 +0200 Subject: extcon: Fix kernel doc of property capability fields to avoid warnings [ Upstream commit 73346b9965ebda2feb7fef8629e9b28baee820e3 ] Kernel documentation has to be synchronized with a code, otherwise the validator is not happy: Function parameter or member 'usb_bits' not described in 'extcon_cable' Function parameter or member 'chg_bits' not described in 'extcon_cable' Function parameter or member 'jack_bits' not described in 'extcon_cable' Function parameter or member 'disp_bits' not described in 'extcon_cable' Describe the fields added in the past. Fixes: ceaa98f442cf ("extcon: Add the support for the capability of each property") Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi Signed-off-by: Sasha Levin --- drivers/extcon/extcon.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 01fd920685c2..7141dd0d407d 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -210,6 +210,10 @@ static const struct __extcon_info { * @chg_propval: the array of charger connector properties * @jack_propval: the array of jack connector properties * @disp_propval: the array of display connector properties + * @usb_bits: the bit array of the USB connector property capabilities + * @chg_bits: the bit array of the charger connector property capabilities + * @jack_bits: the bit array of the jack connector property capabilities + * @disp_bits: the bit array of the display connector property capabilities */ struct extcon_cable { struct extcon_dev *edev; -- cgit 1.4.1 From dd9b7c89a80428cc5f4ae0d2e1311fdedb2a1aac Mon Sep 17 00:00:00 2001 From: Li Yang Date: Thu, 20 Apr 2023 22:08:31 +0800 Subject: usb: phy: phy-tahvo: fix memory leak in tahvo_usb_probe() [ Upstream commit 342161c11403ea00e9febc16baab1d883d589d04 ] Smatch reports: drivers/usb/phy/phy-tahvo.c: tahvo_usb_probe() warn: missing unwind goto? After geting irq, if ret < 0, it will return without error handling to free memory. Just add error handling to fix this problem. Fixes: 0d45a1373e66 ("usb: phy: tahvo: add IRQ check") Signed-off-by: Li Yang Reviewed-by: Dongliang Mu Link: https://lore.kernel.org/r/20230420140832.9110-1-lidaxian@hust.edu.cn Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/phy/phy-tahvo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index f2d2cc586c5b..da63d7e4d270 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -391,7 +391,7 @@ static int tahvo_usb_probe(struct platform_device *pdev) tu->irq = ret = platform_get_irq(pdev, 0); if (ret < 0) - return ret; + goto err_remove_phy; ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, IRQF_ONESHOT, "tahvo-vbus", tu); -- cgit 1.4.1 From b5ab04a19ef1ec44c0b01824b637b097261447c5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 22:17:42 +0200 Subject: usb: hide unused usbfs_notify_suspend/resume functions [ Upstream commit 8e6bd945e6dde64fbc60ec3fe252164493a8d3a2 ] The declaration is in an #ifdef, which causes warnings when building with 'make W=1' and without CONFIG_PM: drivers/usb/core/devio.c:742:6: error: no previous prototype for 'usbfs_notify_suspend' drivers/usb/core/devio.c:747:6: error: no previous prototype for 'usbfs_notify_resume' Use the same #ifdef check around the function definitions to avoid the warnings and slightly shrink the USB core. Fixes: 7794f486ed0b ("usbfs: Add ioctls for runtime power management") Signed-off-by: Arnd Bergmann Reviewed-by: Sebastian Reichel Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230516202103.558301-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/core/devio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 984de3f5e8eb..4184cd65a6aa 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -747,6 +747,7 @@ static int driver_resume(struct usb_interface *intf) return 0; } +#ifdef CONFIG_PM /* The following routines apply to the entire device, not interfaces */ void usbfs_notify_suspend(struct usb_device *udev) { @@ -765,6 +766,7 @@ void usbfs_notify_resume(struct usb_device *udev) } mutex_unlock(&usbfs_mutex); } +#endif struct usb_driver usbfs_driver = { .name = "usbfs", -- cgit 1.4.1 From c494fe1b66632dfa50b7a8e4213f858f2e01dba9 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Thu, 18 May 2023 02:47:51 +0530 Subject: usb: misc: eud: Fix eud sysfs path (use 'qcom_eud') [ Upstream commit f16135918b5f8b510db014ecf0a069e34c02382e ] The eud sysfs enablement path is currently mentioned in the Documentation as: /sys/bus/platform/drivers/eud/.../enable Instead it should be: /sys/bus/platform/drivers/qcom_eud/.../enable Fix the same. Fixes: 9a1bf58ccd44 ("usb: misc: eud: Add driver support for Embedded USB Debugger(EUD)") Reviewed-by: Konrad Dybcio Acked-by: Manivannan Sadhasivam Signed-off-by: Bhupesh Sharma Link: https://lore.kernel.org/r/20230517211756.2483552-2-bhupesh.sharma@linaro.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- Documentation/ABI/testing/sysfs-driver-eud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-driver-eud b/Documentation/ABI/testing/sysfs-driver-eud index 83f3872182a4..2bab0db2d2f0 100644 --- a/Documentation/ABI/testing/sysfs-driver-eud +++ b/Documentation/ABI/testing/sysfs-driver-eud @@ -1,4 +1,4 @@ -What: /sys/bus/platform/drivers/eud/.../enable +What: /sys/bus/platform/drivers/qcom_eud/.../enable Date: February 2022 Contact: Souradeep Chowdhury Description: -- cgit 1.4.1 From 44470207dbc53b267c867f96a62bdf30142f8cd9 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Thu, 25 May 2023 11:37:53 +0206 Subject: serial: core: lock port for stop_rx() in uart_suspend_port() [ Upstream commit abcb0cf1f5b2d99b1d117a4dbce334120e358d6d ] The uarts_ops stop_rx() callback expects that the port->lock is taken and interrupts are disabled. Fixes: c9d2325cdb92 ("serial: core: Do stop_rx in suspend path for console if console_suspend is disabled") Signed-off-by: John Ogness Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20230525093159.223817-3-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/serial_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 23a7ab0de444..b0a467717206 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2334,8 +2334,11 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) * able to Re-start_rx later. */ if (!console_suspend_enabled && uart_console(uport)) { - if (uport->ops->start_rx) + if (uport->ops->start_rx) { + spin_lock_irq(&uport->lock); uport->ops->stop_rx(uport); + spin_unlock_irq(&uport->lock); + } goto unlock; } -- cgit 1.4.1 From 65a7cfc009b388a96fff95e3aabecfbd589649bf Mon Sep 17 00:00:00 2001 From: John Ogness Date: Thu, 25 May 2023 11:37:54 +0206 Subject: serial: 8250: lock port for stop_rx() in omap8250_irq() [ Upstream commit ca73a892c5bec4b08a2fa22b3015e98ed905abb7 ] The uarts_ops stop_rx() callback expects that the port->lock is taken and interrupts are disabled. Fixes: 1fe0e1fa3209 ("serial: 8250_omap: Handle optional overrun-throttle-ms property") Signed-off-by: John Ogness Reviewed-by: Tony Lindgren Link: https://lore.kernel.org/r/20230525093159.223817-4-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/8250/8250_omap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 79bdf933c67e..7ecd2d379292 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -641,7 +641,9 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) up->ier = port->serial_in(port, UART_IER); if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + spin_lock(&port->lock); port->ops->stop_rx(port); + spin_unlock(&port->lock); } else { /* Keep restarting the timer until * the input overrun subsides. -- cgit 1.4.1 From 3a1ab191e00f7ff6170db4c0fd1eda251dd09ef8 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Thu, 25 May 2023 11:37:55 +0206 Subject: serial: core: lock port for start_rx() in uart_resume_port() [ Upstream commit 51e45fba14bf08b66bca764a083c7f2e2ff62f01 ] The only user of the start_rx() callback (qcom_geni) directly calls its own stop_rx() callback. Since stop_rx() requires that the port->lock is taken and interrupts are disabled, the start_rx() callback has the same requirement. Fixes: cfab87c2c271 ("serial: core: Introduce callback for start_rx and do stop_rx in suspend only if this callback implementation is present.") Signed-off-by: John Ogness Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20230525093159.223817-5-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/serial_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index b0a467717206..2cc5c68c8689 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2431,8 +2431,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) if (console_suspend_enabled) uart_change_pm(state, UART_PM_STATE_ON); uport->ops->set_termios(uport, &termios, NULL); - if (!console_suspend_enabled && uport->ops->start_rx) + if (!console_suspend_enabled && uport->ops->start_rx) { + spin_lock_irq(&uport->lock); uport->ops->start_rx(uport); + spin_unlock_irq(&uport->lock); + } if (console_suspend_enabled) console_start(uport->cons); } -- cgit 1.4.1 From 9c4f52b61804f075d981c654107c6dbb52816aa3 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Thu, 25 May 2023 11:37:58 +0206 Subject: serial: 8250: lock port for UART_IER access in omap8250_irq() [ Upstream commit 25614735a647693c1260f253dc3ab32127697806 ] omap8250_irq() accesses UART_IER. This register is modified twice by each console write (serial8250_console_write()) under the port lock. omap8250_irq() must also take the port lock to guanentee synchronized access to UART_IER. Since the port lock is already being taken for the stop_rx() callback and since it is safe to call cancel_delayed_work() while holding the port lock, simply extend the port lock region to include UART_IER access. Fixes: 1fe0e1fa3209 ("serial: 8250_omap: Handle optional overrun-throttle-ms property") Signed-off-by: John Ogness Reviewed-by: Tony Lindgren Link: https://lore.kernel.org/r/20230525093159.223817-8-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/8250/8250_omap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 7ecd2d379292..17a230281ebe 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -639,17 +639,18 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) if ((lsr & UART_LSR_OE) && up->overrun_backoff_time_ms > 0) { unsigned long delay; + /* Synchronize UART_IER access against the console. */ + spin_lock(&port->lock); up->ier = port->serial_in(port, UART_IER); if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { - spin_lock(&port->lock); port->ops->stop_rx(port); - spin_unlock(&port->lock); } else { /* Keep restarting the timer until * the input overrun subsides. */ cancel_delayed_work(&up->overrun_backoff); } + spin_unlock(&port->lock); delay = msecs_to_jiffies(up->overrun_backoff_time_ms); schedule_delayed_work(&up->overrun_backoff, delay); -- cgit 1.4.1 From f5d80ad7b6780c8b2692491029109d551ec41b61 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 23 May 2023 10:40:17 +0800 Subject: kernfs: fix missing kernfs_idr_lock to remove an ID from the IDR [ Upstream commit 30480b988f88c279752f3202a26b6fee5f586aef ] The root->ino_idr is supposed to be protected by kernfs_idr_lock, fix it. Fixes: 488dee96bb62 ("kernfs: allow creating kernfs objects with arbitrary uid/gid") Signed-off-by: Muchun Song Acked-by: Tejun Heo Link: https://lore.kernel.org/r/20230523024017.24851-1-songmuchun@bytedance.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- fs/kernfs/dir.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index f33b3baad07c..44842e6cf0a9 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -652,7 +652,9 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, return kn; err_out3: + spin_lock(&kernfs_idr_lock); idr_remove(&root->ino_idr, (u32)kernfs_ino(kn)); + spin_unlock(&kernfs_idr_lock); err_out2: kmem_cache_free(kernfs_node_cache, kn); err_out1: -- cgit 1.4.1 From c0ed8b804934a9b8ecb245617b6988fd6482c422 Mon Sep 17 00:00:00 2001 From: Yue Zhao Date: Thu, 4 May 2023 00:29:44 +0800 Subject: lkdtm: replace ll_rw_block with submit_bh [ Upstream commit b290df06811852d4cc36f4b8a2a30c2063197a74 ] Function ll_rw_block was removed in commit 79f597842069 ("fs/buffer: remove ll_rw_block() helper"). There is no unified function to sumbit read or write buffer in block layer for now. Consider similar sematics, we can choose submit_bh() to replace ll_rw_block() as predefined crash point. In submit_bh(), it also takes read or write flag as the first argument and invoke submit_bio() to submit I/O request to block layer. Fixes: 79f597842069 ("fs/buffer: remove ll_rw_block() helper") Signed-off-by: Yue Zhao Acked-by: Kees Cook Link: https://lore.kernel.org/r/20230503162944.3969-1-findns94@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- Documentation/fault-injection/provoke-crashes.rst | 2 +- drivers/misc/lkdtm/core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/fault-injection/provoke-crashes.rst b/Documentation/fault-injection/provoke-crashes.rst index 3abe84225613..1f087e502ca6 100644 --- a/Documentation/fault-injection/provoke-crashes.rst +++ b/Documentation/fault-injection/provoke-crashes.rst @@ -29,7 +29,7 @@ recur_count cpoint_name Where in the kernel to trigger the action. It can be one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY, - FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_QUEUE_RQ, or DIRECT. + FS_SUBMIT_BH, MEM_SWAPOUT, TIMERADD, SCSI_QUEUE_RQ, or DIRECT. cpoint_type Indicates the action to be taken on hitting the crash point. diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index b4712ff196b4..0772e4a4757e 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -79,7 +79,7 @@ static struct crashpoint crashpoints[] = { CRASHPOINT("INT_HARDWARE_ENTRY", "do_IRQ"), CRASHPOINT("INT_HW_IRQ_EN", "handle_irq_event"), CRASHPOINT("INT_TASKLET_ENTRY", "tasklet_action"), - CRASHPOINT("FS_DEVRW", "ll_rw_block"), + CRASHPOINT("FS_SUBMIT_BH", "submit_bh"), CRASHPOINT("MEM_SWAPOUT", "shrink_inactive_list"), CRASHPOINT("TIMERADD", "hrtimer_start"), CRASHPOINT("SCSI_QUEUE_RQ", "scsi_queue_rq"), -- cgit 1.4.1 From 76efcb6cdaf94650fdb11a1dd851592de132fea0 Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Wed, 17 May 2023 11:30:29 +0800 Subject: i3c: master: svc: fix cpu schedule in spin lock [ Upstream commit 33beadb3b1ab74e69db2c49d9663f3a93a273943 ] pm_runtime_resume_and_get() may call sleep(). It cannot be used in svc_i3c_master_start_xfer_locked(), because it is in a spin lock. Move the pm runtime operations to svc_i3c_master_enqueue_xfer(). Signed-off-by: Clark Wang Fixes: 05be23ef78f7 ("i3c: master: svc: add runtime pm support") Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/r/20230517033030.3068085-2-xiaoning.wang@nxp.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master/svc-i3c-master.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index d6e9ed74cdcf..d47360f8a1f3 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -1090,12 +1090,6 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master) if (!xfer) return; - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__); - return; - } - svc_i3c_master_clear_merrwarn(master); svc_i3c_master_flush_fifo(master); @@ -1110,9 +1104,6 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master) break; } - pm_runtime_mark_last_busy(master->dev); - pm_runtime_put_autosuspend(master->dev); - xfer->ret = ret; complete(&xfer->comp); @@ -1133,6 +1124,13 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master, struct svc_i3c_xfer *xfer) { unsigned long flags; + int ret; + + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__); + return; + } init_completion(&xfer->comp); spin_lock_irqsave(&master->xferqueue.lock, flags); @@ -1143,6 +1141,9 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master, svc_i3c_master_start_xfer_locked(master); } spin_unlock_irqrestore(&master->xferqueue.lock, flags); + + pm_runtime_mark_last_busy(master->dev); + pm_runtime_put_autosuspend(master->dev); } static bool -- cgit 1.4.1 From 6d702c7a220307e0e2d63e1a8acd731f6531fcb2 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 25 Apr 2023 15:35:28 +0100 Subject: coresight: Fix loss of connection info when a module is unloaded [ Upstream commit c45b2835e7b205783bdfe08cc98fa86a7c5eeb74 ] child_fwnode should be a read only property based on the DT or ACPI. If it's cleared on the parent device when a child is unloaded, then when the child is loaded again the connection won't be remade. child_dev should be cleared instead which signifies that the connection should be remade when the child_fwnode registers a new coresight_device. Similarly the reference count shouldn't be decremented as long as the parent device exists. The correct place to drop the reference is in coresight_release_platform_data() which is already done. Reproducible on Juno with the following steps: # load all coresight modules. $ cd /sys/bus/coresight/devices/ $ echo 1 > tmc_etr0/enable_sink $ echo 1 > etm0/enable_source # Works fine ^ $ echo 0 > etm0/enable_source $ rmmod coresight-funnel $ modprobe coresight-funnel $ echo 1 > etm0/enable_source -bash: echo: write error: Invalid argument Fixes: 37ea1ffddffa ("coresight: Use fwnode handle instead of device names") Fixes: 2af89ebacf29 ("coresight: Clear the connection field properly") Tested-by: Suzuki K Poulose Reviewed-by: Mike Leach Signed-off-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20230425143542.2305069-2-james.clark@arm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-core.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index f3068175ca9d..bcb08fadccf2 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1446,13 +1446,8 @@ static int coresight_remove_match(struct device *dev, void *data) if (csdev->dev.fwnode == conn->child_fwnode) { iterator->orphan = true; coresight_remove_links(iterator, conn); - /* - * Drop the reference to the handle for the remote - * device acquired in parsing the connections from - * platform data. - */ - fwnode_handle_put(conn->child_fwnode); - conn->child_fwnode = NULL; + + conn->child_dev = NULL; /* No need to continue */ break; } -- cgit 1.4.1 From 8339bd9181b1d2292b24bc2d6b1c86630595b0d0 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 15 May 2023 22:57:10 +0200 Subject: mfd: rt5033: Drop rt5033-battery sub-device [ Upstream commit 43db1344e0f8c1eb687a1d6cd5b0de3009ab66cb ] The fuel gauge in the RT5033 PMIC (rt5033-battery) has its own I2C bus and interrupt lines. Therefore, it is not part of the MFD device and needs to be specified separately in the device tree. Fixes: 0b271258544b ("mfd: rt5033: Add Richtek RT5033 driver core.") Signed-off-by: Stephan Gerhold Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/6a8a19bc67b5be3732882e8131ad2ffcb546ac03.1684182964.git.jahau@rocketmail.com Signed-off-by: Sasha Levin --- drivers/mfd/rt5033.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mfd/rt5033.c b/drivers/mfd/rt5033.c index f1236a9acf30..df095e91e266 100644 --- a/drivers/mfd/rt5033.c +++ b/drivers/mfd/rt5033.c @@ -41,9 +41,6 @@ static const struct mfd_cell rt5033_devs[] = { { .name = "rt5033-charger", .of_compatible = "richtek,rt5033-charger", - }, { - .name = "rt5033-battery", - .of_compatible = "richtek,rt5033-battery", }, { .name = "rt5033-led", .of_compatible = "richtek,rt5033-led", -- cgit 1.4.1 From c8470b7de8b44d083378f9cac44faa18c7464050 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 12 Sep 2020 20:03:01 +0100 Subject: media: venus: helpers: Fix ALIGN() of non power of two [ Upstream commit 927e78ac8bc58155316cf6f46026e1912bbbbcfc ] ALIGN() expects its second argument to be a power of 2, otherwise incorrect results are produced for some inputs. The output can be both larger or smaller than what is expected. For example, ALIGN(304, 192) equals 320 instead of 384, and ALIGN(65, 192) equals 256 instead of 192. However, nestling two ALIGN() as is done in this case seem to only produce results equal to or bigger than the expected result if ALIGN() had handled non powers of two, and that in turn results in framesizes that are either the correct size or too large. Fortunately, since 192 * 4 / 3 equals 256, it turns out that one ALIGN() is sufficient. Fixes: ab1eda449c6e ("media: venus: vdec: handle 10bit bitstreams") Signed-off-by: Rikard Falkeborn Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/venus/helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index ab6a29ffc81e..ca6555bdc92f 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -1036,8 +1036,8 @@ static u32 get_framesize_raw_yuv420_tp10_ubwc(u32 width, u32 height) u32 extradata = SZ_16K; u32 size; - y_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); - uv_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); + y_stride = ALIGN(width * 4 / 3, 256); + uv_stride = ALIGN(width * 4 / 3, 256); y_sclines = ALIGN(height, 16); uv_sclines = ALIGN((height + 1) >> 1, 16); -- cgit 1.4.1 From d199218881d7882442819e3c93c61e161ead6deb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 26 May 2023 12:53:23 +0100 Subject: media: atomisp: gmin_platform: fix out_len in gmin_get_config_dsm_var() [ Upstream commit 1657f2934daf89e8d9fa4b2697008909eb22c73e ] Ideally, strlen(cur->string.pointer) and strlen(out) would be the same. But this code is using strscpy() to avoid a potential buffer overflow. So in the same way we should take the strlen() of the smaller string to avoid a buffer overflow in the caller, gmin_get_var_int(). Link: https://lore.kernel.org/r/26124bcd-8132-4483-9d67-225c87d424e8@kili.mountain Fixes: 387041cda44e ("media: atomisp: improve sensor detection code to use _DSM table") Signed-off-by: Dan Carpenter Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 3d41fab661cf..789b13db80b2 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -1280,7 +1280,7 @@ static int gmin_get_config_dsm_var(struct device *dev, dev_info(dev, "found _DSM entry for '%s': %s\n", var, cur->string.pointer); strscpy(out, cur->string.pointer, *out_len); - *out_len = strlen(cur->string.pointer); + *out_len = strlen(out); ACPI_FREE(obj); return 0; -- cgit 1.4.1 From f5d7f9e155175a099e47db40926cf82f46348bcc Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Thu, 1 Jun 2023 23:22:17 +0300 Subject: sh: Avoid using IRQ0 on SH3 and SH4 [ Upstream commit a8ac2961148e8c720dc760f2e06627cd5c55a154 ] IRQ0 is no longer returned by platform_get_irq() and its ilk -- they now return -EINVAL instead. However, the kernel code supporting SH3/4-based SoCs still maps the IRQ #s starting at 0 -- modify that code to start the IRQ #s from 16 instead. The patch should mostly affect the AP-SH4A-3A/AP-SH4AD-0A boards as they indeed are using IRQ0 for the SMSC911x compatible Ethernet chip. Fixes: ce753ad1549c ("platform: finally disallow IRQ0 in platform_get_irq() and its ilk") Signed-off-by: Sergey Shtylyov Reviewed-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Reviewed-by: John Paul Adrian Glaubitz Tested-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/71105dbf-cdb0-72e1-f9eb-eeda8e321696@omp.ru Signed-off-by: John Paul Adrian Glaubitz Signed-off-by: Sasha Levin --- arch/sh/include/mach-common/mach/highlander.h | 2 +- arch/sh/include/mach-common/mach/r2d.h | 2 +- arch/sh/include/mach-dreamcast/mach/sysasic.h | 2 +- arch/sh/include/mach-se/mach/se7724.h | 2 +- arch/sh/kernel/cpu/sh3/entry.S | 4 ++-- include/linux/sh_intc.h | 6 +++--- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/sh/include/mach-common/mach/highlander.h b/arch/sh/include/mach-common/mach/highlander.h index fb44c299d033..b12c79558422 100644 --- a/arch/sh/include/mach-common/mach/highlander.h +++ b/arch/sh/include/mach-common/mach/highlander.h @@ -176,7 +176,7 @@ #define IVDR_CK_ON 4 /* iVDR Clock ON */ #endif -#define HL_FPGA_IRQ_BASE 200 +#define HL_FPGA_IRQ_BASE (200 + 16) #define HL_NR_IRL 15 #define IRQ_AX88796 (HL_FPGA_IRQ_BASE + 0) diff --git a/arch/sh/include/mach-common/mach/r2d.h b/arch/sh/include/mach-common/mach/r2d.h index 0d7e483c7d3f..69bc1907c563 100644 --- a/arch/sh/include/mach-common/mach/r2d.h +++ b/arch/sh/include/mach-common/mach/r2d.h @@ -47,7 +47,7 @@ #define IRLCNTR1 (PA_BCR + 0) /* Interrupt Control Register1 */ -#define R2D_FPGA_IRQ_BASE 100 +#define R2D_FPGA_IRQ_BASE (100 + 16) #define IRQ_VOYAGER (R2D_FPGA_IRQ_BASE + 0) #define IRQ_EXT (R2D_FPGA_IRQ_BASE + 1) diff --git a/arch/sh/include/mach-dreamcast/mach/sysasic.h b/arch/sh/include/mach-dreamcast/mach/sysasic.h index ed69ce7f2030..3b27be9a527e 100644 --- a/arch/sh/include/mach-dreamcast/mach/sysasic.h +++ b/arch/sh/include/mach-dreamcast/mach/sysasic.h @@ -22,7 +22,7 @@ takes. */ -#define HW_EVENT_IRQ_BASE 48 +#define HW_EVENT_IRQ_BASE (48 + 16) /* IRQ 13 */ #define HW_EVENT_VSYNC (HW_EVENT_IRQ_BASE + 5) /* VSync */ diff --git a/arch/sh/include/mach-se/mach/se7724.h b/arch/sh/include/mach-se/mach/se7724.h index 1fe28820dfa9..ea6c46633b33 100644 --- a/arch/sh/include/mach-se/mach/se7724.h +++ b/arch/sh/include/mach-se/mach/se7724.h @@ -37,7 +37,7 @@ #define IRQ2_IRQ evt2irq(0x640) /* Bits in IRQ012 registers */ -#define SE7724_FPGA_IRQ_BASE 220 +#define SE7724_FPGA_IRQ_BASE (220 + 16) /* IRQ0 */ #define IRQ0_BASE SE7724_FPGA_IRQ_BASE diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index e48b3dd996f5..b1f5b3c58a01 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S @@ -470,9 +470,9 @@ ENTRY(handle_interrupt) mov r4, r0 ! save vector->jmp table offset for later shlr2 r4 ! vector to IRQ# conversion - add #-0x10, r4 - cmp/pz r4 ! is it a valid IRQ? + mov #0x10, r5 + cmp/hs r5, r4 ! is it a valid IRQ? bt 10f /* diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index 37ad81058d6a..27ae79191bdc 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h @@ -13,9 +13,9 @@ /* * Convert back and forth between INTEVT and IRQ values. */ -#ifdef CONFIG_CPU_HAS_INTEVT -#define evt2irq(evt) (((evt) >> 5) - 16) -#define irq2evt(irq) (((irq) + 16) << 5) +#ifdef CONFIG_CPU_HAS_INTEVT /* Avoid IRQ0 (invalid for platform devices) */ +#define evt2irq(evt) ((evt) >> 5) +#define irq2evt(irq) ((irq) << 5) #else #define evt2irq(evt) (evt) #define irq2evt(irq) (irq) -- cgit 1.4.1 From 2e980eb9550d01d8d86b7bb29ab16a12fc57c6fa Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Mon, 12 Jun 2023 12:26:23 -0500 Subject: gfs2: Fix duplicate should_fault_in_pages() call [ Upstream commit c8ed1b35931245087968fd95b2ec3dfc50f77769 ] In gfs2_file_buffered_write(), we currently jump from the second call of function should_fault_in_pages() to above the first call, so should_fault_in_pages() is getting called twice in a row, causing it to accidentally fall back to single-page writes rather than trying the more efficient multi-page writes first. Fix that by moving the retry label to the correct place, behind the first call to should_fault_in_pages(). Fixes: e1fa9ea85ce8 ("gfs2: Stop using glock holder auto-demotion for now") Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher Signed-off-by: Sasha Levin --- fs/gfs2/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index bc6cd5f4b107..c367f1678d5d 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1029,8 +1029,8 @@ static ssize_t gfs2_file_buffered_write(struct kiocb *iocb, } gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, gh); -retry: if (should_fault_in_pages(from, iocb, &prev_count, &window_size)) { +retry: window_size -= fault_in_iov_iter_readable(from, window_size); if (!window_size) { ret = -EFAULT; -- cgit 1.4.1 From 15c073e752d22934b9763016679cbe2850020d6a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 14 May 2023 16:07:23 +0800 Subject: f2fs: fix potential deadlock due to unpaired node_write lock use [ Upstream commit f082c6b205a06953f26c40bdc7621cc5a58ceb7c ] If S_NOQUOTA is cleared from inode during data page writeback of quota file, it may miss to unlock node_write lock, result in potential deadlock, fix to use the lock in paired. Kworker Thread - writepage if (IS_NOQUOTA()) f2fs_down_read(&sbi->node_write); - vfs_cleanup_quota_inode - inode->i_flags &= ~S_NOQUOTA; if (IS_NOQUOTA()) f2fs_up_read(&sbi->node_write); Fixes: 79963d967b49 ("f2fs: shrink node_write lock coverage") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/compress.c | 7 ++++--- fs/f2fs/data.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index b160863eca14..e50d5848c100 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1235,6 +1235,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, unsigned int last_index = cc->cluster_size - 1; loff_t psize; int i, err; + bool quota_inode = IS_NOQUOTA(inode); /* we should bypass data pages to proceed the kworkder jobs */ if (unlikely(f2fs_cp_error(sbi))) { @@ -1242,7 +1243,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, goto out_free; } - if (IS_NOQUOTA(inode)) { + if (quota_inode) { /* * We need to wait for node_write to avoid block allocation during * checkpoint. This can only happen to quota writes which can cause @@ -1364,7 +1365,7 @@ unlock_continue: set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); f2fs_put_dnode(&dn); - if (IS_NOQUOTA(inode)) + if (quota_inode) f2fs_up_read(&sbi->node_write); else f2fs_unlock_op(sbi); @@ -1390,7 +1391,7 @@ out_put_cic: out_put_dnode: f2fs_put_dnode(&dn); out_unlock_op: - if (IS_NOQUOTA(inode)) + if (quota_inode) f2fs_up_read(&sbi->node_write); else f2fs_unlock_op(sbi); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 36db9aab4779..c230824ab5e6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2759,6 +2759,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted, loff_t psize = (loff_t)(page->index + 1) << PAGE_SHIFT; unsigned offset = 0; bool need_balance_fs = false; + bool quota_inode = IS_NOQUOTA(inode); int err = 0; struct f2fs_io_info fio = { .sbi = sbi, @@ -2816,19 +2817,19 @@ write: goto out; /* Dentry/quota blocks are controlled by checkpoint */ - if (S_ISDIR(inode->i_mode) || IS_NOQUOTA(inode)) { + if (S_ISDIR(inode->i_mode) || quota_inode) { /* * We need to wait for node_write to avoid block allocation during * checkpoint. This can only happen to quota writes which can cause * the below discard race condition. */ - if (IS_NOQUOTA(inode)) + if (quota_inode) f2fs_down_read(&sbi->node_write); fio.need_lock = LOCK_DONE; err = f2fs_do_write_data_page(&fio); - if (IS_NOQUOTA(inode)) + if (quota_inode) f2fs_up_read(&sbi->node_write); goto done; -- cgit 1.4.1 From ebe83e9bb8a6b3db28603fe938ee80ccaa01ed53 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 23 May 2023 14:17:25 +0800 Subject: f2fs: fix to avoid NULL pointer dereference f2fs_write_end_io() [ Upstream commit d8189834d4348ae608083e1f1f53792cfcc2a9bc ] butt3rflyh4ck reports a bug as below: When a thread always calls F2FS_IOC_RESIZE_FS to resize fs, if resize fs is failed, f2fs kernel thread would invoke callback function to update f2fs io info, it would call f2fs_write_end_io and may trigger null-ptr-deref in NODE_MAPPING. general protection fault, probably for non-canonical address KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037] RIP: 0010:NODE_MAPPING fs/f2fs/f2fs.h:1972 [inline] RIP: 0010:f2fs_write_end_io+0x727/0x1050 fs/f2fs/data.c:370 bio_endio+0x5af/0x6c0 block/bio.c:1608 req_bio_endio block/blk-mq.c:761 [inline] blk_update_request+0x5cc/0x1690 block/blk-mq.c:906 blk_mq_end_request+0x59/0x4c0 block/blk-mq.c:1023 lo_complete_rq+0x1c6/0x280 drivers/block/loop.c:370 blk_complete_reqs+0xad/0xe0 block/blk-mq.c:1101 __do_softirq+0x1d4/0x8ef kernel/softirq.c:571 run_ksoftirqd kernel/softirq.c:939 [inline] run_ksoftirqd+0x31/0x60 kernel/softirq.c:931 smpboot_thread_fn+0x659/0x9e0 kernel/smpboot.c:164 kthread+0x33e/0x440 kernel/kthread.c:379 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:308 The root cause is below race case can cause leaving dirty metadata in f2fs after filesystem is remount as ro: Thread A Thread B - f2fs_ioc_resize_fs - f2fs_readonly --- return false - f2fs_resize_fs - f2fs_remount - write_checkpoint - set f2fs as ro - free_segment_range - update meta_inode's data Then, if f2fs_put_super() fails to write_checkpoint due to readonly status, and meta_inode's dirty data will be writebacked after node_inode is put, finally, f2fs_write_end_io will access NULL pointer on sbi->node_inode. Thread A IRQ context - f2fs_put_super - write_checkpoint fails - iput(node_inode) - node_inode = NULL - iput(meta_inode) - write_inode_now - f2fs_write_meta_page - f2fs_write_end_io - NODE_MAPPING(sbi) : access NULL pointer on node_inode Fixes: b4b10061ef98 ("f2fs: refactor resize_fs to avoid meta updates in progress") Reported-by: butt3rflyh4ck Closes: https://lore.kernel.org/r/1684480657-2375-1-git-send-email-yangtiezhu@loongson.cn Tested-by: butt3rflyh4ck Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 21 ++++++++++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8d7dc76e6f93..4d1e48c676fa 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3817,7 +3817,7 @@ void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi); block_t f2fs_start_bidx_of_node(unsigned int node_ofs, struct inode *inode); int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control); void f2fs_build_gc_manager(struct f2fs_sb_info *sbi); -int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count); +int f2fs_resize_fs(struct file *filp, __u64 block_count); int __init f2fs_create_garbage_collection_cache(void); void f2fs_destroy_garbage_collection_cache(void); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 370c40858b42..7b94f047cbf7 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -3279,7 +3279,7 @@ static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg) sizeof(block_count))) return -EFAULT; - return f2fs_resize_fs(sbi, block_count); + return f2fs_resize_fs(filp, block_count); } static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 7e497f5b349c..3de887d07c06 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -2110,8 +2110,9 @@ static void update_fs_metadata(struct f2fs_sb_info *sbi, int secs) } } -int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count) +int f2fs_resize_fs(struct file *filp, __u64 block_count) { + struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp)); __u64 old_block_count, shrunk_blocks; struct cp_control cpc = { CP_RESIZE, 0, 0, 0 }; unsigned int secs; @@ -2149,12 +2150,18 @@ int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count) return -EINVAL; } + err = mnt_want_write_file(filp); + if (err) + return err; + shrunk_blocks = old_block_count - block_count; secs = div_u64(shrunk_blocks, BLKS_PER_SEC(sbi)); /* stop other GC */ - if (!f2fs_down_write_trylock(&sbi->gc_lock)) - return -EAGAIN; + if (!f2fs_down_write_trylock(&sbi->gc_lock)) { + err = -EAGAIN; + goto out_drop_write; + } /* stop CP to protect MAIN_SEC in free_segment_range */ f2fs_lock_op(sbi); @@ -2174,10 +2181,18 @@ int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count) out_unlock: f2fs_unlock_op(sbi); f2fs_up_write(&sbi->gc_lock); +out_drop_write: + mnt_drop_write_file(filp); if (err) return err; freeze_super(sbi->sb); + + if (f2fs_readonly(sbi->sb)) { + thaw_super(sbi->sb); + return -EROFS; + } + f2fs_down_write(&sbi->gc_lock); f2fs_down_write(&sbi->cp_global_sem); -- cgit 1.4.1 From 90159b329f5cc021af605a24e38ced1771275003 Mon Sep 17 00:00:00 2001 From: Nico Boehr Date: Fri, 24 Mar 2023 15:54:23 +0100 Subject: KVM: s390: fix KVM_S390_GET_CMMA_BITS for GFNs in memslot holes [ Upstream commit 285cff4c0454340a4dc53f46e67f2cb1c293bd74 ] The KVM_S390_GET_CMMA_BITS ioctl may return incorrect values when userspace specifies a start_gfn outside of memslots. This can occur when a VM has multiple memslots with a hole in between: +-----+----------+--------+--------+ | ... | Slot N-1 | | Slot N | +-----+----------+--------+--------+ ^ ^ ^ ^ | | | | GFN A A+B | | A+B+C | A+B+C+D When userspace specifies a GFN in [A+B, A+B+C), it would expect to get the CMMA values of the first dirty page in Slot N. However, userspace may get a start_gfn of A+B+C+D with a count of 0, hence completely skipping over any dirty pages in slot N. The error is in kvm_s390_next_dirty_cmma(), which assumes gfn_to_memslot_approx() will return the memslot _below_ the specified GFN when the specified GFN lies outside a memslot. In reality it may return either the memslot below or above the specified GFN. When a memslot above the specified GFN is returned this happens: - ofs is calculated, but since the memslot's base_gfn is larger than the specified cur_gfn, ofs will underflow to a huge number. - ofs is passed to find_next_bit(). Since ofs will exceed the memslot's number of pages, the number of pages in the memslot is returned, completely skipping over all bits in the memslot userspace would be interested in. Fix this by resetting ofs to zero when a memslot _above_ cur_gfn is returned (cur_gfn < ms->base_gfn). Signed-off-by: Nico Boehr Reviewed-by: Claudio Imbrenda Fixes: afdad61615cc ("KVM: s390: Fix storage attributes migration with memory slots") Message-Id: <20230324145424.293889-2-nrb@linux.ibm.com> Signed-off-by: Claudio Imbrenda Signed-off-by: Janosch Frank Signed-off-by: Sasha Levin --- arch/s390/kvm/kvm-s390.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 26f89ec3062b..3775363471f0 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2147,6 +2147,10 @@ static unsigned long kvm_s390_next_dirty_cmma(struct kvm_memslots *slots, ms = container_of(mnode, struct kvm_memory_slot, gfn_node[slots->node_idx]); ofs = 0; } + + if (cur_gfn < ms->base_gfn) + ofs = 0; + ofs = find_next_bit(kvm_second_dirty_bitmap(ms), ms->npages, ofs); while (ofs >= ms->npages && (mnode = rb_next(mnode))) { ms = container_of(mnode, struct kvm_memory_slot, gfn_node[slots->node_idx]); -- cgit 1.4.1 From 63b3360d43e438edfbd439e2a48fa6ebc6141e14 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 4 Jun 2023 17:04:37 +0200 Subject: usb: dwc3: qcom: Release the correct resources in dwc3_qcom_remove() [ Upstream commit 8fd95da2cfb5046c4bb5a3cdc9eb7963ba8b10dd ] In the probe, some resources are allocated with dwc3_qcom_of_register_core() or dwc3_qcom_acpi_register_core(). The corresponding resources are already coorectly freed in the error handling path of the probe, but not in the remove function. Fix it. Fixes: 2bc02355f8ba ("usb: dwc3: qcom: Add support for booting with ACPI") Signed-off-by: Christophe JAILLET Reviewed-by: Andrew Halaney Message-ID: Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/dwc3/dwc3-qcom.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 482260182d65..9c01e963ae46 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -950,11 +950,15 @@ reset_assert: static int dwc3_qcom_remove(struct platform_device *pdev) { struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int i; device_remove_software_node(&qcom->dwc3->dev); - of_platform_depopulate(dev); + if (np) + of_platform_depopulate(&pdev->dev); + else + platform_device_put(pdev); for (i = qcom->num_clocks - 1; i >= 0; i--) { clk_disable_unprepare(qcom->clks[i]); -- cgit 1.4.1 From 155bb9b4e32e63833d4c309e87d84d40ad888c0e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 4 Jun 2023 16:56:34 +0200 Subject: usb: dwc3: qcom: Fix an error handling path in dwc3_qcom_probe() [ Upstream commit 4a944da707123686d372ec01ea60056902fadf35 ] If dwc3_qcom_create_urs_usb_platdev() fails, some resources still need to be released, as already done in the other error handling path of the probe. Fixes: c25c210f590e ("usb: dwc3: qcom: add URS Host support for sdm845 ACPI boot") Signed-off-by: Christophe JAILLET Reviewed-by: Andrew Halaney Message-ID: Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/dwc3/dwc3-qcom.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 9c01e963ae46..72c22851d7ee 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -865,9 +865,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (IS_ERR_OR_NULL(qcom->urs_usb)) { dev_err(dev, "failed to create URS USB platdev\n"); if (!qcom->urs_usb) - return -ENODEV; + ret = -ENODEV; else - return PTR_ERR(qcom->urs_usb); + ret = PTR_ERR(qcom->urs_usb); + goto clk_disable; } } } -- cgit 1.4.1 From 04b1c0798deb567e79c245be8d4b6d88e1b3b20e Mon Sep 17 00:00:00 2001 From: Prashanth K Date: Wed, 31 May 2023 20:11:14 +0530 Subject: usb: common: usb-conn-gpio: Set last role to unknown before initial detection [ Upstream commit edd60d24bd858cef165274e4cd6cab43bdc58d15 ] Currently if we bootup a device without cable connected, then usb-conn-gpio won't call set_role() since last_role is same as current role. This happens because during probe last_role gets initialised to zero. To avoid this, added a new constant in enum usb_role, last_role is set to USB_ROLE_UNKNOWN before performing initial detection. While at it, also handle default case for the usb_role switch in cdns3, intel-xhci-usb-role-switch & musb/jz4740 to avoid build warnings. Fixes: 4602f3bff266 ("usb: common: add USB GPIO based connection detection driver") Signed-off-by: Prashanth K Reviewed-by: AngeloGioacchino Del Regno Message-ID: <1685544074-17337-1-git-send-email-quic_prashk@quicinc.com> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/cdns3/core.c | 2 ++ drivers/usb/common/usb-conn-gpio.c | 3 +++ drivers/usb/musb/jz4740.c | 2 ++ drivers/usb/roles/intel-xhci-usb-role-switch.c | 2 ++ include/linux/usb/role.h | 1 + 5 files changed, 10 insertions(+) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index dbcdf3b24b47..69d2921f2d3b 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -252,6 +252,8 @@ static enum usb_role cdns_hw_role_state_machine(struct cdns *cdns) if (!vbus) role = USB_ROLE_NONE; break; + default: + break; } dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role); diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index e20874caba36..30bdb81934bc 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -257,6 +257,9 @@ static int usb_conn_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); device_set_wakeup_capable(&pdev->dev, true); + /* Set last role to unknown before performing the initial detection */ + info->last_role = USB_ROLE_UNKNOWN; + /* Perform initial detection */ usb_conn_queue_dwork(info, 0); diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index d1e4e0deb753..df4e9d397d98 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -91,6 +91,8 @@ static int jz4740_musb_role_switch_set(struct usb_role_switch *sw, case USB_ROLE_HOST: atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy); break; + default: + break; } return 0; diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index 5c96e929acea..4d6a3dd06e01 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -97,6 +97,8 @@ static int intel_xhci_usb_set_role(struct usb_role_switch *sw, val |= SW_VBUS_VALID; drd_config = DRD_CONFIG_STATIC_DEVICE; break; + default: + break; } val |= SW_IDPIN_EN; if (data->enable_sw_switch) { diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index b5deafd91f67..65e790a28913 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -11,6 +11,7 @@ enum usb_role { USB_ROLE_NONE, USB_ROLE_HOST, USB_ROLE_DEVICE, + USB_ROLE_UNKNOWN, }; typedef int (*usb_role_switch_set_t)(struct usb_role_switch *sw, -- cgit 1.4.1 From 7a37abf096c255131293e9d5db1de44277e82f6d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 10 Jun 2023 15:32:52 +0200 Subject: usb: dwc3-meson-g12a: Fix an error handling path in dwc3_meson_g12a_probe() [ Upstream commit 01052b91c9808e3c3b068ae2721cb728ec9aa4c0 ] If dwc3_meson_g12a_otg_init() fails, resources allocated by the previous of_platform_populate() call should be released, as already done in the error handling path. Fixes: 1e355f21d3fb ("usb: dwc3: Add Amlogic A1 DWC3 glue") Signed-off-by: Christophe JAILLET Reviewed-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Message-ID: <9d28466de1808ccc756b4cc25fc72c482d133d13.1686403934.git.christophe.jaillet@wanadoo.fr> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/dwc3/dwc3-meson-g12a.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index b282ad0e69c6..eaea944ebd2c 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -805,7 +805,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) ret = dwc3_meson_g12a_otg_init(pdev, priv); if (ret) - goto err_phys_power; + goto err_plat_depopulate; pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -813,6 +813,9 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) return 0; +err_plat_depopulate: + of_platform_depopulate(dev); + err_phys_power: for (i = 0 ; i < PHY_COUNT ; ++i) phy_power_off(priv->phys[i]); -- cgit 1.4.1 From 2e8ab6846063eef192e4ca736d14cbda36550150 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 May 2023 09:10:54 +0200 Subject: mfd: wcd934x: Fix an error handling path in wcd934x_slim_probe() [ Upstream commit f190b4891a3f9fac123a7afd378d4143a2723313 ] If devm_gpiod_get_optional() fails, some resources need to be released, as already done in the .remove() function. While at it, remove the unneeded error code from a dev_err_probe() call. It is already added in a human readable way by dev_err_probe() itself. Fixes: 6a0ee2a61a31 ("mfd: wcd934x: Replace legacy gpio interface for gpiod") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/02d8447f6d1df52cc8357aae698152e9a9be67c6.1684565021.git.christophe.jaillet@wanadoo.fr Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/wcd934x.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c index 68e2fa2fda99..32ed2bd86375 100644 --- a/drivers/mfd/wcd934x.c +++ b/drivers/mfd/wcd934x.c @@ -253,8 +253,9 @@ static int wcd934x_slim_probe(struct slim_device *sdev) usleep_range(600, 650); reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(reset_gpio)) { - return dev_err_probe(dev, PTR_ERR(reset_gpio), - "Failed to get reset gpio: err = %ld\n", PTR_ERR(reset_gpio)); + ret = dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to get reset gpio\n"); + goto err_disable_regulators; } msleep(20); gpiod_set_value(reset_gpio, 1); @@ -264,6 +265,10 @@ static int wcd934x_slim_probe(struct slim_device *sdev) dev_set_drvdata(dev, ddata); return 0; + +err_disable_regulators: + regulator_bulk_disable(WCD934X_MAX_SUPPLY, ddata->supplies); + return ret; } static void wcd934x_slim_remove(struct slim_device *sdev) -- cgit 1.4.1 From 22b1e2af69eced0c064b7c4be411caf7ffdee21b Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Fri, 9 Jun 2023 09:48:18 +0800 Subject: mfd: intel-lpss: Add missing check for platform_get_resource [ Upstream commit d918e0d5824495a75d00b879118b098fcab36fdb ] Add the missing check for platform_get_resource and return error if it fails. Fixes: 4b45efe85263 ("mfd: Add support for Intel Sunrisepoint LPSS devices") Signed-off-by: Jiasheng Jiang Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230609014818.28475-1-jiasheng@iscas.ac.cn Signed-off-by: Sasha Levin --- drivers/mfd/intel-lpss-acpi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index a143c8dca2d9..212818aef93e 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -183,6 +183,9 @@ static int intel_lpss_acpi_probe(struct platform_device *pdev) return -ENOMEM; info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!info->mem) + return -ENODEV; + info->irq = platform_get_irq(pdev, 0); ret = intel_lpss_probe(&pdev->dev, info); -- cgit 1.4.1 From e34817340025e6cc4edaef6fa5eace09d42917c9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 15 Jun 2023 11:30:35 +0200 Subject: Revert "usb: common: usb-conn-gpio: Set last role to unknown before initial detection" [ Upstream commit df49f2a0ac4a34c0cb4b5c233fcfa0add644c43c ] This reverts commit edd60d24bd858cef165274e4cd6cab43bdc58d15. Heikki reports that this should not be a global flag just to work around one broken driver and should be fixed differently, so revert it. Reported-by: Heikki Krogerus Fixes: edd60d24bd85 ("usb: common: usb-conn-gpio: Set last role to unknown before initial detection") Link: https://lore.kernel.org/r/ZImE4L3YgABnCIsP@kuha.fi.intel.com Cc: Prashanth K Cc: AngeloGioacchino Del Regno Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/cdns3/core.c | 2 -- drivers/usb/common/usb-conn-gpio.c | 3 --- drivers/usb/musb/jz4740.c | 2 -- drivers/usb/roles/intel-xhci-usb-role-switch.c | 2 -- include/linux/usb/role.h | 1 - 5 files changed, 10 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 69d2921f2d3b..dbcdf3b24b47 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -252,8 +252,6 @@ static enum usb_role cdns_hw_role_state_machine(struct cdns *cdns) if (!vbus) role = USB_ROLE_NONE; break; - default: - break; } dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role); diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 30bdb81934bc..e20874caba36 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -257,9 +257,6 @@ static int usb_conn_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); device_set_wakeup_capable(&pdev->dev, true); - /* Set last role to unknown before performing the initial detection */ - info->last_role = USB_ROLE_UNKNOWN; - /* Perform initial detection */ usb_conn_queue_dwork(info, 0); diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index df4e9d397d98..d1e4e0deb753 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -91,8 +91,6 @@ static int jz4740_musb_role_switch_set(struct usb_role_switch *sw, case USB_ROLE_HOST: atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy); break; - default: - break; } return 0; diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index 4d6a3dd06e01..5c96e929acea 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -97,8 +97,6 @@ static int intel_xhci_usb_set_role(struct usb_role_switch *sw, val |= SW_VBUS_VALID; drd_config = DRD_CONFIG_STATIC_DEVICE; break; - default: - break; } val |= SW_IDPIN_EN; if (data->enable_sw_switch) { diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index 65e790a28913..b5deafd91f67 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -11,7 +11,6 @@ enum usb_role { USB_ROLE_NONE, USB_ROLE_HOST, USB_ROLE_DEVICE, - USB_ROLE_UNKNOWN, }; typedef int (*usb_role_switch_set_t)(struct usb_role_switch *sw, -- cgit 1.4.1 From 852659fe834ab720de07658f2bcd1332805bb752 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 14 Jun 2023 07:59:19 +0300 Subject: serial: 8250_omap: Use force_suspend and resume for system suspend [ Upstream commit 20a41a62618df85f3a2981008edec5cadd785e0a ] We should not rely on autosuspend timeout for system suspend. Instead, let's use force_suspend and force_resume functions. Otherwise the serial port controller device may not be idled on suspend. As we are doing a register write on suspend to configure the serial port, we still need to runtime PM resume the port on suspend. While at it, let's switch to pm_runtime_resume_and_get() and check for errors returned. And let's add the missing line break before return to the suspend function while at it. Fixes: 09d8b2bdbc5c ("serial: 8250: omap: Provide ability to enable/disable UART as wakeup source") Signed-off-by: Tony Lindgren Tested-by: Dhruva Gole Message-ID: <20230614045922.4798-1-tony@atomide.com> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/tty/serial/8250/8250_omap.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 17a230281ebe..adc85e250822 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1505,25 +1505,35 @@ static int omap8250_suspend(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up = serial8250_get_port(priv->line); + int err; serial8250_suspend_port(priv->line); - pm_runtime_get_sync(dev); + err = pm_runtime_resume_and_get(dev); + if (err) + return err; if (!device_may_wakeup(dev)) priv->wer = 0; serial_out(up, UART_OMAP_WER, priv->wer); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - + err = pm_runtime_force_suspend(dev); flush_work(&priv->qos_work); - return 0; + + return err; } static int omap8250_resume(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); + int err; + err = pm_runtime_force_resume(dev); + if (err) + return err; serial8250_resume_port(priv->line); + /* Paired with pm_runtime_resume_and_get() in omap8250_suspend() */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; } #else -- cgit 1.4.1 From 73209e3f8ad75e3226fbc94e04c67c1a121d7c99 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Wed, 7 Dec 2022 15:22:18 +0400 Subject: device property: Fix documentation for fwnode_get_next_parent() [ Upstream commit f18caf261398a7f2de4fa3f600deb87072fe7b8d ] Use fwnode_handle_put() on the node pointer to release the refcount. Change fwnode_handle_node() to fwnode_handle_put(). Fixes: 233872585de1 ("device property: Add fwnode_get_next_parent()") Reviewed-by: Andy Shevchenko Reviewed-by: Daniel Scally Acked-by: Sakari Ailus Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20221207112219.2652411-1-linmq006@gmail.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 39d422555e43 ("drivers: fwnode: fix fwnode_irq_get[_byname]()") Signed-off-by: Sasha Levin --- drivers/base/property.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 7f338cb4fb7b..f2f7829ad36b 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_parent); * node's parents. * * Returns a node pointer with refcount incremented, use - * fwnode_handle_node() on it when done. + * fwnode_handle_put() on it when done. */ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode) { -- cgit 1.4.1 From 51ae92e329f59ce464afc1f3eb7da53af755739c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Feb 2023 15:33:44 +0200 Subject: device property: Clarify description of returned value in some functions [ Upstream commit 295209ca7b5b3aa6375d6190311b2ae804dbcf65 ] Some of the functions do not provide Return: section on absence of which kernel-doc complains. Besides that several functions return the fwnode handle with incremented reference count. Add a respective note to make sure that the caller decrements it when it's not needed anymore. While at it, unify the style of the Return: sections. Reported-by: Daniel Kaehn Signed-off-by: Andy Shevchenko Acked-by: Sakari Ailus Acked-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20230217133344.79278-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 39d422555e43 ("drivers: fwnode: fix fwnode_irq_get[_byname]()") Signed-off-by: Sasha Levin --- drivers/base/property.c | 124 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 34 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index f2f7829ad36b..868adeac2a84 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -30,6 +30,8 @@ EXPORT_SYMBOL_GPL(dev_fwnode); * @propname: Name of the property * * Check if property @propname is present in the device firmware description. + * + * Return: true if property @propname is present. Otherwise, returns false. */ bool device_property_present(struct device *dev, const char *propname) { @@ -41,6 +43,8 @@ EXPORT_SYMBOL_GPL(device_property_present); * fwnode_property_present - check if a property of a firmware node is present * @fwnode: Firmware node whose property to check * @propname: Name of the property + * + * Return: true if property @propname is present. Otherwise, returns false. */ bool fwnode_property_present(const struct fwnode_handle *fwnode, const char *propname) @@ -500,10 +504,10 @@ EXPORT_SYMBOL_GPL(fwnode_property_match_string); * Obtain a reference based on a named property in an fwnode, with * integer arguments. * - * Caller is responsible to call fwnode_handle_put() on the returned - * args->fwnode pointer. + * The caller is responsible for calling fwnode_handle_put() on the returned + * @args->fwnode pointer. * - * Returns: %0 on success + * Return: %0 on success * %-ENOENT when the index is out of bounds, the index has an empty * reference or the property was not found * %-EINVAL on parse error @@ -539,8 +543,11 @@ EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); * * @index can be used when the named reference holds a table of references. * - * Returns pointer to the reference fwnode, or ERR_PTR. Caller is responsible to - * call fwnode_handle_put() on the returned fwnode pointer. + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. + * + * Return: a pointer to the reference fwnode, when found. Otherwise, + * returns an error pointer. */ struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode, const char *name, @@ -559,7 +566,7 @@ EXPORT_SYMBOL_GPL(fwnode_find_reference); * fwnode_get_name - Return the name of a node * @fwnode: The firmware node * - * Returns a pointer to the node name. + * Return: a pointer to the node name, or %NULL. */ const char *fwnode_get_name(const struct fwnode_handle *fwnode) { @@ -571,7 +578,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_name); * fwnode_get_name_prefix - Return the prefix of node for printing purposes * @fwnode: The firmware node * - * Returns the prefix of a node, intended to be printed right before the node. + * Return: the prefix of a node, intended to be printed right before the node. * The prefix works also as a separator between the nodes. */ const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode) @@ -583,7 +590,10 @@ const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode) * fwnode_get_parent - Return parent firwmare node * @fwnode: Firmware whose parent is retrieved * - * Return parent firmware node of the given node if possible or %NULL if no + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. + * + * Return: parent firmware node of the given node if possible or %NULL if no * parent was available. */ struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode) @@ -600,8 +610,12 @@ EXPORT_SYMBOL_GPL(fwnode_get_parent); * on the passed node, making it suitable for iterating through a * node's parents. * - * Returns a node pointer with refcount incremented, use - * fwnode_handle_put() on it when done. + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. Note that this function also puts a reference to @fwnode + * unconditionally. + * + * Return: parent firmware node of the given node if possible or %NULL if no + * parent was available. */ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode) { @@ -621,8 +635,10 @@ EXPORT_SYMBOL_GPL(fwnode_get_next_parent); * firmware node that has a corresponding struct device and returns that struct * device. * - * The caller of this function is expected to call put_device() on the returned - * device when they are done. + * The caller is responsible for calling put_device() on the returned device + * pointer. + * + * Return: a pointer to the device of the @fwnode's closest ancestor. */ struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode) { @@ -643,7 +659,7 @@ struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode) * fwnode_count_parents - Return the number of parents a node has * @fwnode: The node the parents of which are to be counted * - * Returns the number of parents a node has. + * Return: the number of parents a node has. */ unsigned int fwnode_count_parents(const struct fwnode_handle *fwnode) { @@ -662,12 +678,12 @@ EXPORT_SYMBOL_GPL(fwnode_count_parents); * @fwnode: The node the parent of which is requested * @depth: Distance of the parent from the node * - * Returns the nth parent of a node. If there is no parent at the requested + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. + * + * Return: the nth parent of a node. If there is no parent at the requested * @depth, %NULL is returned. If @depth is 0, the functionality is equivalent to * fwnode_handle_get(). For @depth == 1, it is fwnode_get_parent() and so on. - * - * The caller is responsible for calling fwnode_handle_put() for the returned - * node. */ struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode, unsigned int depth) @@ -692,7 +708,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_nth_parent); * * A node is considered an ancestor of itself too. * - * Returns true if @ancestor is an ancestor of @child. Otherwise, returns false. + * Return: true if @ancestor is an ancestor of @child. Otherwise, returns false. */ bool fwnode_is_ancestor_of(struct fwnode_handle *ancestor, struct fwnode_handle *child) { @@ -717,6 +733,10 @@ bool fwnode_is_ancestor_of(struct fwnode_handle *ancestor, struct fwnode_handle * fwnode_get_next_child_node - Return the next child node handle for a node * @fwnode: Firmware node to find the next child node for. * @child: Handle to one of the node's child nodes or a %NULL handle. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. Note that this function also puts a reference to @child + * unconditionally. */ struct fwnode_handle * fwnode_get_next_child_node(const struct fwnode_handle *fwnode, @@ -727,10 +747,13 @@ fwnode_get_next_child_node(const struct fwnode_handle *fwnode, EXPORT_SYMBOL_GPL(fwnode_get_next_child_node); /** - * fwnode_get_next_available_child_node - Return the next - * available child node handle for a node + * fwnode_get_next_available_child_node - Return the next available child node handle for a node * @fwnode: Firmware node to find the next child node for. * @child: Handle to one of the node's child nodes or a %NULL handle. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. Note that this function also puts a reference to @child + * unconditionally. */ struct fwnode_handle * fwnode_get_next_available_child_node(const struct fwnode_handle *fwnode, @@ -754,7 +777,11 @@ EXPORT_SYMBOL_GPL(fwnode_get_next_available_child_node); /** * device_get_next_child_node - Return the next child node handle for a device * @dev: Device to find the next child node for. - * @child: Handle to one of the device's child nodes or a null handle. + * @child: Handle to one of the device's child nodes or a %NULL handle. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. Note that this function also puts a reference to @child + * unconditionally. */ struct fwnode_handle *device_get_next_child_node(struct device *dev, struct fwnode_handle *child) @@ -779,6 +806,9 @@ EXPORT_SYMBOL_GPL(device_get_next_child_node); * fwnode_get_named_child_node - Return first matching named child node handle * @fwnode: Firmware node to find the named child node for. * @childname: String to match child node name against. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. */ struct fwnode_handle * fwnode_get_named_child_node(const struct fwnode_handle *fwnode, @@ -792,6 +822,9 @@ EXPORT_SYMBOL_GPL(fwnode_get_named_child_node); * device_get_named_child_node - Return first matching named child node handle * @dev: Device to find the named child node for. * @childname: String to match child node name against. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. */ struct fwnode_handle *device_get_named_child_node(struct device *dev, const char *childname) @@ -804,7 +837,10 @@ EXPORT_SYMBOL_GPL(device_get_named_child_node); * fwnode_handle_get - Obtain a reference to a device node * @fwnode: Pointer to the device node to obtain the reference to. * - * Returns the fwnode handle. + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. + * + * Return: the fwnode handle. */ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode) { @@ -833,6 +869,8 @@ EXPORT_SYMBOL_GPL(fwnode_handle_put); * fwnode_device_is_available - check if a device is available for use * @fwnode: Pointer to the fwnode of the device. * + * Return: true if device is available for use. Otherwise, returns false. + * * For fwnode node types that don't implement the .device_is_available() * operation, this function returns true. */ @@ -851,6 +889,8 @@ EXPORT_SYMBOL_GPL(fwnode_device_is_available); /** * device_get_child_node_count - return the number of child nodes for device * @dev: Device to cound the child nodes for + * + * Return: the number of child nodes for a given device. */ unsigned int device_get_child_node_count(struct device *dev) { @@ -926,7 +966,7 @@ EXPORT_SYMBOL_GPL(device_get_phy_mode); * @fwnode: Pointer to the firmware node * @index: Index of the IO range * - * Returns a pointer to the mapped memory. + * Return: a pointer to the mapped memory. */ void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index) { @@ -939,8 +979,8 @@ EXPORT_SYMBOL(fwnode_iomap); * @fwnode: Pointer to the firmware node * @index: Zero-based index of the IRQ * - * Returns Linux IRQ number on success. Other values are determined - * accordingly to acpi_/of_ irq_get() operation. + * Return: Linux IRQ number on success. Other values are determined + * according to acpi_irq_get() or of_irq_get() operation. */ int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index) { @@ -959,8 +999,7 @@ EXPORT_SYMBOL(fwnode_irq_get); * number of the IRQ resource corresponding to the index of the matched * string. * - * Return: - * Linux IRQ number on success, or negative errno otherwise. + * Return: Linux IRQ number on success, or negative errno otherwise. */ int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name) { @@ -982,7 +1021,11 @@ EXPORT_SYMBOL(fwnode_irq_get_byname); * @fwnode: Pointer to the parent firmware node * @prev: Previous endpoint node or %NULL to get the first * - * Returns an endpoint firmware node pointer or %NULL if no more endpoints + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. Note that this function also puts a reference to @prev + * unconditionally. + * + * Return: an endpoint firmware node pointer or %NULL if no more endpoints * are available. */ struct fwnode_handle * @@ -1022,6 +1065,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint); * fwnode_graph_get_port_parent - Return the device fwnode of a port endpoint * @endpoint: Endpoint firmware node of the port * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. + * * Return: the firmware node of the device the @endpoint belongs to. */ struct fwnode_handle * @@ -1043,6 +1089,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_port_parent); * @fwnode: Endpoint firmware node pointing to the remote endpoint * * Extracts firmware node of a remote device the @fwnode points to. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. */ struct fwnode_handle * fwnode_graph_get_remote_port_parent(const struct fwnode_handle *fwnode) @@ -1063,6 +1112,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port_parent); * @fwnode: Endpoint firmware node pointing to the remote endpoint * * Extracts firmware node of a remote port the @fwnode points to. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. */ struct fwnode_handle * fwnode_graph_get_remote_port(const struct fwnode_handle *fwnode) @@ -1076,6 +1128,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port); * @fwnode: Endpoint firmware node pointing to the remote endpoint * * Extracts firmware node of a remote endpoint the @fwnode points to. + * + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. */ struct fwnode_handle * fwnode_graph_get_remote_endpoint(const struct fwnode_handle *fwnode) @@ -1103,8 +1158,11 @@ static bool fwnode_graph_remote_available(struct fwnode_handle *ep) * @endpoint: identifier of the endpoint node under the port node * @flags: fwnode lookup flags * - * Return the fwnode handle of the local endpoint corresponding the port and - * endpoint IDs or NULL if not found. + * The caller is responsible for calling fwnode_handle_put() on the returned + * fwnode pointer. + * + * Return: the fwnode handle of the local endpoint corresponding the port and + * endpoint IDs or %NULL if not found. * * If FWNODE_GRAPH_ENDPOINT_NEXT is passed in @flags and the specified endpoint * has not been found, look for the closest endpoint ID greater than the @@ -1112,9 +1170,6 @@ static bool fwnode_graph_remote_available(struct fwnode_handle *ep) * * Does not return endpoints that belong to disabled devices or endpoints that * are unconnected, unless FWNODE_GRAPH_DEVICE_DISABLED is passed in @flags. - * - * The returned endpoint needs to be released by calling fwnode_handle_put() on - * it when it is not needed any more. */ struct fwnode_handle * fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, @@ -1320,7 +1375,8 @@ EXPORT_SYMBOL_GPL(fwnode_connection_find_match); * @fwnode and other device nodes. @match will be used to convert the * connection description to data the caller is expecting to be returned * through the @matches array. - * If @matches is NULL @matches_len is ignored and the total number of resolved + * + * If @matches is %NULL @matches_len is ignored and the total number of resolved * matches is returned. * * Return: Number of matches resolved, or negative errno. -- cgit 1.4.1 From e3a71d821e244726ab07df3c97afd31d2a16b380 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Mon, 29 May 2023 09:22:33 +0300 Subject: drivers: fwnode: fix fwnode_irq_get[_byname]() [ Upstream commit 39d422555e43379516d4d13f5b7162a3dee6e646 ] The fwnode_irq_get() and the fwnode_irq_get_byname() return 0 upon device-tree IRQ mapping failure. This is contradicting the fwnode_irq_get_byname() function documentation and can potentially be a source of errors like: int probe(...) { ... irq = fwnode_irq_get_byname(); if (irq <= 0) return irq; ... } Here we do correctly check the return value from fwnode_irq_get_byname() but the driver probe will now return success. (There was already one such user in-tree). Change the fwnode_irq_get_byname() to work as documented and make also the fwnode_irq_get() follow same common convention returning a negative errno upon failure. Fixes: ca0acb511c21 ("device property: Add fwnode_irq_get_byname") Suggested-by: Sakari Ailus Suggested-by: Jonathan Cameron Signed-off-by: Matti Vaittinen Reviewed-by: Andy Shevchenko Reviewed-by: Jonathan Cameron Message-ID: <3e64fe592dc99e27ef9a0b247fc49fa26b6b8a58.1685340157.git.mazziesaccount@gmail.com> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/property.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 868adeac2a84..b0c40d973484 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -979,12 +979,18 @@ EXPORT_SYMBOL(fwnode_iomap); * @fwnode: Pointer to the firmware node * @index: Zero-based index of the IRQ * - * Return: Linux IRQ number on success. Other values are determined - * according to acpi_irq_get() or of_irq_get() operation. + * Return: Linux IRQ number on success. Negative errno on failure. */ int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index) { - return fwnode_call_int_op(fwnode, irq_get, index); + int ret; + + ret = fwnode_call_int_op(fwnode, irq_get, index); + /* We treat mapping errors as invalid case */ + if (ret == 0) + return -EINVAL; + + return ret; } EXPORT_SYMBOL(fwnode_irq_get); -- cgit 1.4.1 From 27918479409d08394195e03323ca49d036528c80 Mon Sep 17 00:00:00 2001 From: Yi Yingao Date: Tue, 9 May 2023 16:52:36 +0800 Subject: nvmem: sunplus-ocotp: release otp->clk before return [ Upstream commit 095bb8ba45f28ed15296eb5b7662e03e57d5e34e ] Smatch reports: drivers/nvmem/sunplus-ocotp.c:205 sp_ocotp_probe() warn: 'otp->clk' from clk_prepare() not released on lines: 196. In the function sp_ocotp_probe(struct platform_device *pdev), otp->clk may not be released before return. To fix this issue, using function clk_unprepare() to release otp->clk. Fixes: 8747ec2e9762 ("nvmem: Add driver for OCOTP in Sunplus SP7021") Signed-off-by: Yi Yingao Reviewed-by: Dongliang Mu Message-ID: <20230509085237.5917-1-m202271736@hust.edu.cn> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/nvmem/sunplus-ocotp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/sunplus-ocotp.c b/drivers/nvmem/sunplus-ocotp.c index 52b928a7a6d5..f85350b17d67 100644 --- a/drivers/nvmem/sunplus-ocotp.c +++ b/drivers/nvmem/sunplus-ocotp.c @@ -192,9 +192,11 @@ static int sp_ocotp_probe(struct platform_device *pdev) sp_ocotp_nvmem_config.dev = dev; nvmem = devm_nvmem_register(dev, &sp_ocotp_nvmem_config); - if (IS_ERR(nvmem)) - return dev_err_probe(&pdev->dev, PTR_ERR(nvmem), + if (IS_ERR(nvmem)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(nvmem), "register nvmem device fail\n"); + goto err; + } platform_set_drvdata(pdev, nvmem); @@ -203,6 +205,9 @@ static int sp_ocotp_probe(struct platform_device *pdev) (int)OTP_WORD_SIZE, (int)QAC628_OTP_SIZE); return 0; +err: + clk_unprepare(otp->clk); + return ret; } static const struct of_device_id sp_ocotp_dt_ids[] = { -- cgit 1.4.1 From e27948f329f7e02591ed1feb9a7710c2ccf89a83 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Sun, 11 Jun 2023 15:03:09 +0100 Subject: nvmem: rmem: Use NVMEM_DEVID_AUTO [ Upstream commit 09dd7b993eddb3b48634fd5ddf27aa799785a9ee ] It is reasonable to declare multiple nvmem blocks. Unless a unique 'id' is passed in for each block there may be name clashes. Avoid this by using the magic token NVMEM_DEVID_AUTO. Fixes: 5a3fa75a4d9c ("nvmem: Add driver to expose reserved memory as nvmem") Signed-off-by: Phil Elwell Signed-off-by: Ivan T. Ivanov Reviewed-by: Stefan Wahren Signed-off-by: Srinivas Kandagatla Message-ID: <20230611140330.154222-6-srinivas.kandagatla@linaro.org> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/nvmem/rmem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/rmem.c b/drivers/nvmem/rmem.c index 80cb187f1481..752d0bf4445e 100644 --- a/drivers/nvmem/rmem.c +++ b/drivers/nvmem/rmem.c @@ -71,6 +71,7 @@ static int rmem_probe(struct platform_device *pdev) config.dev = dev; config.priv = priv; config.name = "rmem"; + config.id = NVMEM_DEVID_AUTO; config.size = mem->size; config.reg_read = rmem_read; -- cgit 1.4.1 From 5bd9dc3e767edf582be483be8d6bbc7433bd4cf8 Mon Sep 17 00:00:00 2001 From: Laurentiu Tudor Date: Tue, 13 Jun 2023 19:07:18 +0300 Subject: bus: fsl-mc: don't assume child devices are all fsl-mc devices [ Upstream commit 303c9c63abb9390e906052863f82bb4e9824e5c0 ] Changes in VFIO caused a pseudo-device to be created as child of fsl-mc devices causing a crash [1] when trying to bind a fsl-mc device to VFIO. Fix this by checking the device type when enumerating fsl-mc child devices. [1] Modules linked in: Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP CPU: 6 PID: 1289 Comm: sh Not tainted 6.2.0-rc5-00047-g7c46948a6e9c #2 Hardware name: NXP Layerscape LX2160ARDB (DT) pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : mc_send_command+0x24/0x1f0 lr : dprc_get_obj_region+0xfc/0x1c0 sp : ffff80000a88b900 x29: ffff80000a88b900 x28: ffff48a9429e1400 x27: 00000000000002b2 x26: ffff48a9429e1718 x25: 0000000000000000 x24: 0000000000000000 x23: ffffd59331ba3918 x22: ffffd59331ba3000 x21: 0000000000000000 x20: ffff80000a88b9b8 x19: 0000000000000000 x18: 0000000000000001 x17: 7270642f636d2d6c x16: 73662e3030303030 x15: ffffffffffffffff x14: ffffd59330f1d668 x13: ffff48a8727dc389 x12: ffff48a8727dc386 x11: 0000000000000002 x10: 00008ceaf02f35d4 x9 : 0000000000000012 x8 : 0000000000000000 x7 : 0000000000000006 x6 : ffff80000a88bab0 x5 : 0000000000000000 x4 : 0000000000000000 x3 : ffff80000a88b9e8 x2 : ffff80000a88b9e8 x1 : 0000000000000000 x0 : ffff48a945142b80 Call trace: mc_send_command+0x24/0x1f0 dprc_get_obj_region+0xfc/0x1c0 fsl_mc_device_add+0x340/0x590 fsl_mc_obj_device_add+0xd0/0xf8 dprc_scan_objects+0x1c4/0x340 dprc_scan_container+0x38/0x60 vfio_fsl_mc_probe+0x9c/0xf8 fsl_mc_driver_probe+0x24/0x70 really_probe+0xbc/0x2a8 __driver_probe_device+0x78/0xe0 device_driver_attach+0x30/0x68 bind_store+0xa8/0x130 drv_attr_store+0x24/0x38 sysfs_kf_write+0x44/0x60 kernfs_fop_write_iter+0x128/0x1b8 vfs_write+0x334/0x448 ksys_write+0x68/0xf0 __arm64_sys_write+0x1c/0x28 invoke_syscall+0x44/0x108 el0_svc_common.constprop.1+0x94/0xf8 do_el0_svc+0x38/0xb0 el0_svc+0x20/0x50 el0t_64_sync_handler+0x98/0xc0 el0t_64_sync+0x174/0x178 Code: aa0103f4 a9025bf5 d5384100 b9400801 (79401260) ---[ end trace 0000000000000000 ]--- Fixes: 3c28a76124b2 ("vfio: Add struct device to vfio_device") Signed-off-by: Laurentiu Tudor Tested-by: Ioana Ciornei Reviewed-by: Ioana Ciornei Message-ID: <20230613160718.29500-1-laurentiu.tudor@nxp.com> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/bus/fsl-mc/dprc-driver.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index 5e70f9775a0e..d1e2d2987dd3 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -46,6 +46,9 @@ static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) struct fsl_mc_child_objs *objs; struct fsl_mc_device *mc_dev; + if (!dev_is_fsl_mc(dev)) + return 0; + mc_dev = to_fsl_mc_device(dev); objs = data; @@ -65,6 +68,9 @@ static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) static int __fsl_mc_device_remove(struct device *dev, void *data) { + if (!dev_is_fsl_mc(dev)) + return 0; + fsl_mc_device_remove(to_fsl_mc_device(dev)); return 0; } -- cgit 1.4.1 From 18abe5f4c3c6c3436c82382502c437505a7bd1ba Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 9 Jun 2023 11:28:03 +0200 Subject: mfd: stmfx: Fix error path in stmfx_chip_init [ Upstream commit f592cf624531286f8b52e40dcfc157a5a7fb115c ] In error path, disable vdd regulator if it exists, but don't overload ret. Because if regulator_disable() is successful, stmfx_chip_init will exit successfully while chip init failed. Fixes: 06252ade9156 ("mfd: Add ST Multi-Function eXpander (STMFX) core driver") Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20230609092804.793100-1-amelie.delaunay@foss.st.com Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/stmfx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c index 5dd7d9688459..61a8aad564a4 100644 --- a/drivers/mfd/stmfx.c +++ b/drivers/mfd/stmfx.c @@ -387,7 +387,7 @@ static int stmfx_chip_init(struct i2c_client *client) err: if (stmfx->vdd) - return regulator_disable(stmfx->vdd); + regulator_disable(stmfx->vdd); return ret; } -- cgit 1.4.1 From e716693f0236d968b4720534faa3b3708116517f Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 9 Jun 2023 11:28:04 +0200 Subject: mfd: stmfx: Nullify stmfx->vdd in case of error [ Upstream commit 7c81582c0bccb4757186176f0ee12834597066ad ] Nullify stmfx->vdd in case devm_regulator_get_optional() returns an error. And simplify code by returning an error only if return code is not -ENODEV, which means there is no vdd regulator and it is not an issue. Fixes: d75846ed08e6 ("mfd: stmfx: Fix dev_err_probe() call in stmfx_chip_init()") Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20230609092804.793100-2-amelie.delaunay@foss.st.com Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/stmfx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c index 61a8aad564a4..b1ecd85ad2a8 100644 --- a/drivers/mfd/stmfx.c +++ b/drivers/mfd/stmfx.c @@ -330,9 +330,8 @@ static int stmfx_chip_init(struct i2c_client *client) stmfx->vdd = devm_regulator_get_optional(&client->dev, "vdd"); ret = PTR_ERR_OR_ZERO(stmfx->vdd); if (ret) { - if (ret == -ENODEV) - stmfx->vdd = NULL; - else + stmfx->vdd = NULL; + if (ret != -ENODEV) return dev_err_probe(&client->dev, ret, "Failed to get VDD regulator\n"); } -- cgit 1.4.1 From c78ad1060ce98e1c09b7964c254850962806ebde Mon Sep 17 00:00:00 2001 From: Pierre Morel Date: Wed, 10 May 2023 17:42:58 +0200 Subject: KVM: s390: vsie: fix the length of APCB bitmap [ Upstream commit 246be7d2720ea9a795b576067ecc5e5c7a1e7848 ] bit_and() uses the count of bits as the woking length. Fix the previous implementation and effectively use the right bitmap size. Fixes: 19fd83a64718 ("KVM: s390: vsie: allow CRYCB FORMAT-1") Fixes: 56019f9aca22 ("KVM: s390: vsie: Allow CRYCB FORMAT-2") Signed-off-by: Pierre Morel Reviewed-by: Janosch Frank Link: https://lore.kernel.org/kvm/20230511094719.9691-1-pmorel@linux.ibm.com/ Signed-off-by: Janosch Frank Signed-off-by: Sasha Levin --- arch/s390/kvm/vsie.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index ace2541ababd..740f8b56e63f 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -169,7 +169,8 @@ static int setup_apcb00(struct kvm_vcpu *vcpu, unsigned long *apcb_s, sizeof(struct kvm_s390_apcb0))) return -EFAULT; - bitmap_and(apcb_s, apcb_s, apcb_h, sizeof(struct kvm_s390_apcb0)); + bitmap_and(apcb_s, apcb_s, apcb_h, + BITS_PER_BYTE * sizeof(struct kvm_s390_apcb0)); return 0; } @@ -191,7 +192,8 @@ static int setup_apcb11(struct kvm_vcpu *vcpu, unsigned long *apcb_s, sizeof(struct kvm_s390_apcb1))) return -EFAULT; - bitmap_and(apcb_s, apcb_s, apcb_h, sizeof(struct kvm_s390_apcb1)); + bitmap_and(apcb_s, apcb_s, apcb_h, + BITS_PER_BYTE * sizeof(struct kvm_s390_apcb1)); return 0; } -- cgit 1.4.1 From 86bfb18bad60fc468e5f112cbbd918462a8dd435 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 15 May 2023 10:42:34 +0200 Subject: KVM: s390/diag: fix racy access of physical cpu number in diag 9c handler [ Upstream commit 0bc380beb78aa352eadbc21d934dd9606fcee808 ] We do check for target CPU == -1, but this might change at the time we are going to use it. Hold the physical target CPU in a local variable to avoid out-of-bound accesses to the cpu arrays. Cc: Pierre Morel Fixes: 87e28a15c42c ("KVM: s390: diag9c (directed yield) forwarding") Reported-by: Marc Hartmayer Reviewed-by: Nico Boehr Reviewed-by: Pierre Morel Signed-off-by: Christian Borntraeger Signed-off-by: Janosch Frank Signed-off-by: Sasha Levin --- arch/s390/kvm/diag.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c index 807fa9da1e72..3c65b8258ae6 100644 --- a/arch/s390/kvm/diag.c +++ b/arch/s390/kvm/diag.c @@ -166,6 +166,7 @@ static int diag9c_forwarding_overrun(void) static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu) { struct kvm_vcpu *tcpu; + int tcpu_cpu; int tid; tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; @@ -181,14 +182,15 @@ static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu) goto no_yield; /* target guest VCPU already running */ - if (READ_ONCE(tcpu->cpu) >= 0) { + tcpu_cpu = READ_ONCE(tcpu->cpu); + if (tcpu_cpu >= 0) { if (!diag9c_forwarding_hz || diag9c_forwarding_overrun()) goto no_yield; /* target host CPU already running */ - if (!vcpu_is_preempted(tcpu->cpu)) + if (!vcpu_is_preempted(tcpu_cpu)) goto no_yield; - smp_yield_cpu(tcpu->cpu); + smp_yield_cpu(tcpu_cpu); VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d: yield forwarded", tid); -- cgit 1.4.1 From 7e3ee25e8c7c7be1eacdfc6d9f5f0e550a2af241 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 5 Jun 2023 15:18:12 +0100 Subject: cpufreq: mediatek: correct voltages for MT7622 and MT7623 [ Upstream commit f85534113f5ae90a52521cdb9e9977a43ee42626 ] The MT6380 regulator typically used together with MT7622 does not support the current maximum processor and SRAM voltage in the cpufreq driver (1360000uV). For MT7622 limit processor and SRAM supply voltages to 1350000uV to avoid having the tracking algorithm request unsupported voltages from the regulator. On MT7623 there is no separate SRAM supply and the maximum voltage used is 1300000uV. Create dedicated platform data for MT7623 to cover that case as well. Fixes: 0883426fd07e3 ("cpufreq: mediatek: Raise proc and sram max voltage for MT7622/7623") Suggested-by: Jia-wei Chang Signed-off-by: Daniel Golle Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Viresh Kumar Signed-off-by: Sasha Levin --- drivers/cpufreq/mediatek-cpufreq.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index 9a39a7ccfae9..fef68cb2b38f 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -696,9 +696,16 @@ static const struct mtk_cpufreq_platform_data mt2701_platform_data = { static const struct mtk_cpufreq_platform_data mt7622_platform_data = { .min_volt_shift = 100000, .max_volt_shift = 200000, - .proc_max_volt = 1360000, + .proc_max_volt = 1350000, .sram_min_volt = 0, - .sram_max_volt = 1360000, + .sram_max_volt = 1350000, + .ccifreq_supported = false, +}; + +static const struct mtk_cpufreq_platform_data mt7623_platform_data = { + .min_volt_shift = 100000, + .max_volt_shift = 200000, + .proc_max_volt = 1300000, .ccifreq_supported = false, }; @@ -734,7 +741,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = { { .compatible = "mediatek,mt2701", .data = &mt2701_platform_data }, { .compatible = "mediatek,mt2712", .data = &mt2701_platform_data }, { .compatible = "mediatek,mt7622", .data = &mt7622_platform_data }, - { .compatible = "mediatek,mt7623", .data = &mt7622_platform_data }, + { .compatible = "mediatek,mt7623", .data = &mt7623_platform_data }, { .compatible = "mediatek,mt8167", .data = &mt8516_platform_data }, { .compatible = "mediatek,mt817x", .data = &mt2701_platform_data }, { .compatible = "mediatek,mt8173", .data = &mt2701_platform_data }, -- cgit 1.4.1 From 94f3bcfcd17c7e932e2431dbbc7b172d4b647c11 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 15 Jun 2023 13:25:46 +0300 Subject: misc: fastrpc: check return value of devm_kasprintf() [ Upstream commit af2e19d82a116bc622eea84c9faadd5f7e20bec4 ] devm_kasprintf() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: 3abe3ab3cdab ("misc: fastrpc: add secure domain support") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230615102546.581899-1-claudiu.beznea@microchip.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/misc/fastrpc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 8b1e8661c3d7..e5cabb901213 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2030,6 +2030,9 @@ static int fastrpc_device_register(struct device *dev, struct fastrpc_channel_ct fdev->miscdev.fops = &fastrpc_fops; fdev->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "fastrpc-%s%s", domain, is_secured ? "-secure" : ""); + if (!fdev->miscdev.name) + return -ENOMEM; + err = misc_register(&fdev->miscdev); if (!err) { if (is_secured) -- cgit 1.4.1 From 34eef9e8c8e3b2c9c89835578209329091e17719 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 7 May 2023 20:53:35 +0300 Subject: clk: qcom: mmcc-msm8974: fix MDSS_GDSC power flags [ Upstream commit 4e13c7a55cf752887f2b8d8008711dbbc64ea796 ] Using PWRSTS_RET on msm8974's MDSS_GDSC causes display to stop working. The gdsc doesn't fully come out of retention mode. Change it's pwrsts flags to PWRSTS_OFF_ON. Fixes: d399723950c4 ("clk: qcom: gdsc: Fix the handling of PWRSTS_RET support") Signed-off-by: Dmitry Baryshkov Reviewed-by: Rajendra Nayak Tested-by: Luca Weiss Link: https://lore.kernel.org/r/20230507175335.2321503-2-dmitry.baryshkov@linaro.org Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/clk/qcom/mmcc-msm8974.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index 7e3f88658f6b..17ed52046170 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -2372,7 +2372,7 @@ static struct gdsc mdss_gdsc = { .pd = { .name = "mdss", }, - .pwrsts = PWRSTS_RET_ON, + .pwrsts = PWRSTS_OFF_ON, }; static struct gdsc camss_jpeg_gdsc = { -- cgit 1.4.1 From f7d56de13caa851291c0311e47a099873ca35268 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 21 Jun 2023 17:28:04 +0800 Subject: hwtracing: hisi_ptt: Fix potential sleep in atomic context [ Upstream commit 6c50384ef8b94a527445e3694ae6549e1f15d859 ] We're using pci_irq_vector() to obtain the interrupt number and then bind it to the CPU start perf under the protection of spinlock in pmu::start(). pci_irq_vector() might sleep since [1] because it will call msi_domain_get_virq() to get the MSI interrupt number and it needs to acquire dev->msi.data->mutex. Getting a mutex will sleep on contention. So use pci_irq_vector() in an atomic context is problematic. This patch cached the interrupt number in the probe() and uses the cached data instead to avoid potential sleep. [1] commit 82ff8e6b78fc ("PCI/MSI: Use msi_get_virq() in pci_get_vector()") Fixes: ff0de066b463 ("hwtracing: hisi_ptt: Add trace function support for HiSilicon PCIe Tune and Trace device") Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20230621092804.15120-6-yangyicong@huawei.com Signed-off-by: Sasha Levin --- drivers/hwtracing/ptt/hisi_ptt.c | 12 +++++------- drivers/hwtracing/ptt/hisi_ptt.h | 2 ++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 30f1525639b5..4140efd66409 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -341,13 +341,13 @@ static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt) if (ret < 0) return ret; - ret = devm_request_threaded_irq(&pdev->dev, - pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ), + hisi_ptt->trace_irq = pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ); + ret = devm_request_threaded_irq(&pdev->dev, hisi_ptt->trace_irq, NULL, hisi_ptt_isr, 0, DRV_NAME, hisi_ptt); if (ret) { pci_err(pdev, "failed to request irq %d, ret = %d\n", - pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ), ret); + hisi_ptt->trace_irq, ret); return ret; } @@ -757,8 +757,7 @@ static void hisi_ptt_pmu_start(struct perf_event *event, int flags) * core in event_function_local(). If CPU passed is offline we'll fail * here, just log it since we can do nothing here. */ - ret = irq_set_affinity(pci_irq_vector(hisi_ptt->pdev, HISI_PTT_TRACE_DMA_IRQ), - cpumask_of(cpu)); + ret = irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(cpu)); if (ret) dev_warn(dev, "failed to set the affinity of trace interrupt\n"); @@ -1018,8 +1017,7 @@ static int hisi_ptt_cpu_teardown(unsigned int cpu, struct hlist_node *node) * Also make sure the interrupt bind to the migrated CPU as well. Warn * the user on failure here. */ - if (irq_set_affinity(pci_irq_vector(hisi_ptt->pdev, HISI_PTT_TRACE_DMA_IRQ), - cpumask_of(target))) + if (irq_set_affinity(hisi_ptt->trace_irq, cpumask_of(target))) dev_warn(dev, "failed to set the affinity of trace interrupt\n"); hisi_ptt->trace_ctrl.on_cpu = target; diff --git a/drivers/hwtracing/ptt/hisi_ptt.h b/drivers/hwtracing/ptt/hisi_ptt.h index 5beb1648c93a..948a4c423152 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.h +++ b/drivers/hwtracing/ptt/hisi_ptt.h @@ -166,6 +166,7 @@ struct hisi_ptt_pmu_buf { * @pdev: pci_dev of this PTT device * @tune_lock: lock to serialize the tune process * @pmu_lock: lock to serialize the perf process + * @trace_irq: interrupt number used by trace * @upper_bdf: the upper BDF range of the PCI devices managed by this PTT device * @lower_bdf: the lower BDF range of the PCI devices managed by this PTT device * @port_filters: the filter list of root ports @@ -180,6 +181,7 @@ struct hisi_ptt { struct pci_dev *pdev; struct mutex tune_lock; spinlock_t pmu_lock; + int trace_irq; u32 upper_bdf; u32 lower_bdf; -- cgit 1.4.1 From 055ea8efdfbbbb1ccdf3862fe59765dc3a50460c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 17 Jun 2023 12:43:16 +0200 Subject: mfd: stmpe: Only disable the regulators if they are enabled [ Upstream commit 104d32bd81f620bb9f67fbf7d1159c414e89f05f ] In stmpe_probe(), if some regulator_enable() calls fail, probing continues and there is only a dev_warn(). So, if stmpe_probe() is called the regulator may not be enabled. It is cleaner to test it before calling regulator_disable() in the remove function. Fixes: 9c9e321455fb ("mfd: stmpe: add optional regulators") Signed-off-by: Christophe JAILLET Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/8de3aaf297931d655b9ad6aed548f4de8b85425a.1686998575.git.christophe.jaillet@wanadoo.fr Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/stmpe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 0c4f74197d3e..aef29221d7c1 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -1485,9 +1485,9 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) void stmpe_remove(struct stmpe *stmpe) { - if (!IS_ERR(stmpe->vio)) + if (!IS_ERR(stmpe->vio) && regulator_is_enabled(stmpe->vio)) regulator_disable(stmpe->vio); - if (!IS_ERR(stmpe->vcc)) + if (!IS_ERR(stmpe->vcc) && regulator_is_enabled(stmpe->vcc)) regulator_disable(stmpe->vcc); __stmpe_disable(stmpe, STMPE_BLOCK_ADC); -- cgit 1.4.1 From d986fb02a1eae6263a9b646ee538905b3dde5493 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 31 May 2023 10:39:50 +0300 Subject: phy: tegra: xusb: check return value of devm_kzalloc() [ Upstream commit 44faada0f38fc333d392af04c343b0e23f8f5d81 ] devm_kzalloc() returns a pointer to dynamically allocated memory. Pointer could be NULL in case allocation fails. Check pointer validity. Identified with coccinelle (kmerr.cocci script). Fixes: f67213cee2b3 ("phy: tegra: xusb: Add usb-role-switch support") Signed-off-by: Claudiu Beznea Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20230531073950.145339-1-claudiu.beznea@microchip.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/tegra/xusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index c3fa22ee2d8d..4d5b4071d47d 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -670,6 +670,9 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port) port->dev.driver = devm_kzalloc(&port->dev, sizeof(struct device_driver), GFP_KERNEL); + if (!port->dev.driver) + return -ENOMEM; + port->dev.driver->owner = THIS_MODULE; port->usb_role_sw = usb_role_switch_register(&port->dev, -- cgit 1.4.1 From 29ddfd5460638469a03f033f24e64fd0e164cc0c Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Mon, 27 Feb 2023 11:24:36 -0800 Subject: lib/bitmap: drop optimization of bitmap_{from,to}_arr64 [ Upstream commit c1d2ba10f594046831d14b03f194e8d05e78abad ] bitmap_{from,to}_arr64() optimization is overly optimistic on 32-bit LE architectures when it's wired to bitmap_copy_clear_tail(). bitmap_copy_clear_tail() takes care of unused bits in the bitmap up to the next word boundary. But on 32-bit machines when copying bits from bitmap to array of 64-bit words, it's expected that the unused part of a recipient array must be cleared up to 64-bit boundary, so the last 4 bytes may stay untouched when nbits % 64 <= 32. While the copying part of the optimization works correct, that clear-tail trick makes corresponding tests reasonably fail: test_bitmap: bitmap_to_arr64(nbits == 1): tail is not safely cleared: 0xa5a5a5a500000001 (must be 0x0000000000000001) Fix it by removing bitmap_{from,to}_arr64() optimization for 32-bit LE arches. Reported-by: Guenter Roeck Link: https://lore.kernel.org/lkml/20230225184702.GA3587246@roeck-us.net/ Fixes: 0a97953fd221 ("lib: add bitmap_{from,to}_arr64") Signed-off-by: Yury Norov Tested-by: Guenter Roeck Reviewed-by: Andy Shevchenko Reviewed-by: Alexander Lobakin Signed-off-by: Sasha Levin --- include/linux/bitmap.h | 8 +++----- lib/bitmap.c | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index 7d6d73b78147..03644237e1ef 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -302,12 +302,10 @@ void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap, #endif /* - * On 64-bit systems bitmaps are represented as u64 arrays internally. On LE32 - * machines the order of hi and lo parts of numbers match the bitmap structure. - * In both cases conversion is not needed when copying data from/to arrays of - * u64. + * On 64-bit systems bitmaps are represented as u64 arrays internally. So, + * the conversion is not needed when copying data from/to arrays of u64. */ -#if (BITS_PER_LONG == 32) && defined(__BIG_ENDIAN) +#if BITS_PER_LONG == 32 void bitmap_from_arr64(unsigned long *bitmap, const u64 *buf, unsigned int nbits); void bitmap_to_arr64(u64 *buf, const unsigned long *bitmap, unsigned int nbits); #else diff --git a/lib/bitmap.c b/lib/bitmap.c index 1c81413c51f8..ddb31015e38a 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -1495,7 +1495,7 @@ void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap, unsigned int nbits) EXPORT_SYMBOL(bitmap_to_arr32); #endif -#if (BITS_PER_LONG == 32) && defined(__BIG_ENDIAN) +#if BITS_PER_LONG == 32 /** * bitmap_from_arr64 - copy the contents of u64 array of bits to bitmap * @bitmap: array of unsigned longs, the destination bitmap -- cgit 1.4.1 From c8fa254b77a3238b9e6c489022095556652d353f Mon Sep 17 00:00:00 2001 From: Fancy Fang Date: Fri, 5 May 2023 14:58:39 +0800 Subject: pwm: imx-tpm: force 'real_period' to be zero in suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 661dfb7f46298e53f6c3deaa772fa527aae86193 ] During suspend, all the tpm registers will lose values. So the 'real_period' value of struct 'imx_tpm_pwm_chip' should be forced to be zero to force the period update code can be executed after system resume back. Signed-off-by: Fancy Fang Signed-off-by: Clark Wang Acked-by: Uwe Kleine-König Fixes: 738a1cfec2ed ("pwm: Add i.MX TPM PWM driver support") Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- drivers/pwm/pwm-imx-tpm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index ed1aad96fff0..318dc0be974b 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -399,6 +399,13 @@ static int __maybe_unused pwm_imx_tpm_suspend(struct device *dev) if (tpm->enable_count > 0) return -EBUSY; + /* + * Force 'real_period' to be zero to force period update code + * can be executed after system resume back, since suspend causes + * the period related registers to become their reset values. + */ + tpm->real_period = 0; + clk_disable_unprepare(tpm->clk); return 0; -- cgit 1.4.1 From 61aad933e53d5ad2284401b2d0de02aaabcad9e2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 12 May 2023 18:47:36 +0200 Subject: pwm: sysfs: Do not apply state to already disabled PWMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 38ba83598633373f47951384cfc389181c8d1bed ] If the PWM is exported but not enabled, do not call pwm_class_apply_state(). First of all, in this case, period may still be unconfigured and this would make pwm_class_apply_state() return -EINVAL, and then suspend would fail. Second, it makes little sense to apply state onto PWM that is not enabled before suspend. Failing case: " $ echo 1 > /sys/class/pwm/pwmchip4/export $ echo mem > /sys/power/state ... pwm pwmchip4: PM: dpm_run_callback(): pwm_class_suspend+0x1/0xa8 returns -22 pwm pwmchip4: PM: failed to suspend: error -22 PM: Some devices failed to suspend, or early wake event detected " Working case: " $ echo 1 > /sys/class/pwm/pwmchip4/export $ echo 100 > /sys/class/pwm/pwmchip4/pwm1/period $ echo 10 > /sys/class/pwm/pwmchip4/pwm1/duty_cycle $ echo mem > /sys/power/state ... " Do not call pwm_class_apply_state() in case the PWM is disabled to fix this issue. Fixes: 7fd4edc57bbae ("pwm: sysfs: Add suspend/resume support") Signed-off-by: Marek Vasut Fixes: ef2bf4997f7d ("pwm: Improve args checking in pwm_apply_state()") Reviewed-by: Brian Norris Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- drivers/pwm/sysfs.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index e7db8e45001c..ba125253857e 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -424,6 +424,13 @@ static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm) if (!export) continue; + /* If pwmchip was not enabled before suspend, do nothing. */ + if (!export->suspend.enabled) { + /* release lock taken in pwm_class_get_state */ + mutex_unlock(&export->lock); + continue; + } + state.enabled = export->suspend.enabled; ret = pwm_class_apply_state(export, pwm, &state); if (ret < 0) @@ -448,7 +455,17 @@ static int pwm_class_suspend(struct device *parent) if (!export) continue; + /* + * If pwmchip was not enabled before suspend, save + * state for resume time and do nothing else. + */ export->suspend = state; + if (!state.enabled) { + /* release lock taken in pwm_class_get_state */ + mutex_unlock(&export->lock); + continue; + } + state.enabled = false; ret = pwm_class_apply_state(export, pwm, &state); if (ret < 0) { -- cgit 1.4.1 From 75439e6cd2a355070dd68808243fca384b7efef0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 22 May 2023 14:07:42 +0300 Subject: pwm: ab8500: Fix error code in probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit cdcffafc4d845cc0c6392cba168c7a942734cce7 ] This code accidentally return positive EINVAL instead of negative -EINVAL. Fixes: eb41f334589d ("pwm: ab8500: Fix register offset calculation to not depend on probe order") Signed-off-by: Dan Carpenter Reviewed-by: Uwe Kleine-König Reviewed-by: Linus Walleij Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- drivers/pwm/pwm-ab8500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c index ad37bc46f272..5fa91f4cda7a 100644 --- a/drivers/pwm/pwm-ab8500.c +++ b/drivers/pwm/pwm-ab8500.c @@ -96,7 +96,7 @@ static int ab8500_pwm_probe(struct platform_device *pdev) int err; if (pdev->id < 1 || pdev->id > 31) - return dev_err_probe(&pdev->dev, EINVAL, "Invalid device id %d\n", pdev->id); + return dev_err_probe(&pdev->dev, -EINVAL, "Invalid device id %d\n", pdev->id); /* * Nothing to be done in probe, this is required to get the -- cgit 1.4.1 From 07e81c9208d9a3d51402ed981427d805c324a2a6 Mon Sep 17 00:00:00 2001 From: Shuijing Li Date: Wed, 31 May 2023 11:10:01 +0800 Subject: pwm: mtk_disp: Fix the disable flow of disp_pwm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit bc13d60e4e1e945b34769a4a4c2b172e8552abe5 ] There is a flow error in the original mtk_disp_pwm_apply() function. If this function is called when the clock is disabled, there will be a chance to operate the disp_pwm register, resulting in disp_pwm exception. Fix this accordingly. Fixes: 888a623db5d0 ("pwm: mtk-disp: Implement atomic API .apply()") Signed-off-by: Shuijing Li Reviewed-by: Matthias Brugger Tested-by: Fei Shao Acked-by: Uwe Kleine-König Reviewed-by: Alexandre Mergnat Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- drivers/pwm/pwm-mtk-disp.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 3811578fcff0..db286284f3ee 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -79,14 +79,11 @@ static int mtk_disp_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL; - if (!state->enabled) { - mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask, - 0x0); - - if (mdp->enabled) { - clk_disable_unprepare(mdp->clk_mm); - clk_disable_unprepare(mdp->clk_main); - } + if (!state->enabled && mdp->enabled) { + mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, + mdp->data->enable_mask, 0x0); + clk_disable_unprepare(mdp->clk_mm); + clk_disable_unprepare(mdp->clk_main); mdp->enabled = false; return 0; -- cgit 1.4.1 From 3781d0e6c11b5905f5c278d84018d7bb08af7170 Mon Sep 17 00:00:00 2001 From: Li Nan Date: Fri, 9 Jun 2023 17:43:20 +0800 Subject: md/raid10: fix the condition to call bio_end_io_acct() [ Upstream commit 125bfc7cd750e68c99f1d446e2c22abea08c237f ] /sys/block/[device]/queue/iostats is used to control whether to count io stat. Write 0 to it will clear queue_flags QUEUE_FLAG_IO_STAT which means iostats is disabled. If we disable iostats and later endable it, the io issued during this period will be counted incorrectly, inflight will be decreased to -1. //T1 set iostats echo 0 > /sys/block/md0/queue/iostats clear QUEUE_FLAG_IO_STAT //T2 issue io if (QUEUE_FLAG_IO_STAT) -> false bio_start_io_acct inflight++ echo 1 > /sys/block/md0/queue/iostats set QUEUE_FLAG_IO_STAT //T3 io end if (QUEUE_FLAG_IO_STAT) -> true bio_end_io_acct inflight-- -> -1 Also, if iostats is enabled while issuing io but disabled while io end, inflight will never be decreased. Fix it by checking start_time when io end. If start_time is not 0, call bio_end_io_acct(). Fixes: 528bc2cf2fcc ("md/raid10: enable io accounting") Signed-off-by: Li Nan Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230609094320.2397604-1-linan666@huaweicloud.com Signed-off-by: Sasha Levin --- drivers/md/raid10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index ea6228ff3bb5..d2098fcd6a27 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -325,7 +325,7 @@ static void raid_end_bio_io(struct r10bio *r10_bio) if (!test_bit(R10BIO_Uptodate, &r10_bio->state)) bio->bi_status = BLK_STS_IOERR; - if (blk_queue_io_stat(bio->bi_bdev->bd_disk->queue)) + if (r10_bio->start_time) bio_end_io_acct(bio, r10_bio->start_time); bio_endio(bio); /* -- cgit 1.4.1 From df53f7a3dbf471ccbf063d7f0f6f96c5fe8e9886 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 8 Jun 2023 21:11:42 +0200 Subject: rtc: st-lpc: Release some resources in st_rtc_probe() in case of error [ Upstream commit 06c6e1b01d9261f03629cefd1f3553503291e6cf ] If an error occurs after clk_get(), the corresponding resources should be released. Use devm_clk_get() to fix it. Fixes: b5b2bdfc2893 ("rtc: st: Add new driver for ST's LPC RTC") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/866af6adbc7454a7b4505eb6c28fbdc86ccff39e.1686251455.git.christophe.jaillet@wanadoo.fr Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-st-lpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c index 0f8e4231098e..d04d46f9cc65 100644 --- a/drivers/rtc/rtc-st-lpc.c +++ b/drivers/rtc/rtc-st-lpc.c @@ -228,7 +228,7 @@ static int st_rtc_probe(struct platform_device *pdev) enable_irq_wake(rtc->irq); disable_irq(rtc->irq); - rtc->clk = clk_get(&pdev->dev, NULL); + rtc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rtc->clk)) { dev_err(&pdev->dev, "Unable to request clock\n"); return PTR_ERR(rtc->clk); -- cgit 1.4.1 From 61070305d5207742027d2e46a0d72f81959125b8 Mon Sep 17 00:00:00 2001 From: Jouni Högander Date: Tue, 20 Jun 2023 14:17:45 +0300 Subject: drm/i915/psr: Use hw.adjusted mode when calculating io/fast wake times MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5311892a0ad1d301aafd53ca0154091b3eb407ea ] Encoder compute config is changing hw.adjusted mode. Uapi.adjusted mode doesn't get updated before psr compute config gets called. This causes io and fast wake line calculation using adjusted mode containing values before encoder adjustments. Fix this by using hw.adjusted mode instead of uapi.adjusted mode. Cc: Stanislav Lisovskiy Signed-off-by: Jouni Högander Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/8475 Fixes: cb42e8ede5b4 ("drm/i915/psr: Use calculated io and fast wake lines") Reviewed-by: Mika Kahola Link: https://patchwork.freedesktop.org/patch/msgid/20230620111745.2870706-1-jouni.hogander@intel.com (cherry picked from commit ef0af9db2a21257885116949f471fe5565b2f0ab) Signed-off-by: Tvrtko Ursulin Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_psr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index bf18423c7a00..e2d7c0a6802a 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -857,9 +857,9 @@ static bool _compute_psr2_wake_times(struct intel_dp *intel_dp, } io_wake_lines = intel_usecs_to_scanlines( - &crtc_state->uapi.adjusted_mode, io_wake_time); + &crtc_state->hw.adjusted_mode, io_wake_time); fast_wake_lines = intel_usecs_to_scanlines( - &crtc_state->uapi.adjusted_mode, fast_wake_time); + &crtc_state->hw.adjusted_mode, fast_wake_time); if (io_wake_lines > max_wake_lines || fast_wake_lines > max_wake_lines) -- cgit 1.4.1 From dcb526d768359095a438336f3aca5e8d98b7d2e6 Mon Sep 17 00:00:00 2001 From: Vinay Belgaumkar Date: Tue, 20 Jun 2023 18:42:57 -0700 Subject: drm/i915/guc/slpc: Apply min softlimit correctly [ Upstream commit 3e49de73fb89272dea01ba420c7ccbcf6b96aed7 ] The scenario being fixed here is depicted in the following sequence- modprobe i915 echo 1 > /sys/class/drm/card0/gt/gt0/slpc_ignore_eff_freq echo 300 > /sys/class/drm/card0/gt_min_freq_mhz (RPn) cat /sys/class/drm/card0/gt_cur_freq_mhz --> cur == RPn as expected echo 1 > /sys/kernel/debug/dri/0/gt0/reset --> reset cat /sys/class/drm/card0/gt_min_freq_mhz --> cached freq is RPn cat /sys/class/drm/card0/gt_cur_freq_mhz --> it's not RPn, but RPe!! When SLPC reinitializes, it sets SLPC min freq to efficient frequency. Even if we disable efficient freq post that, we should restore the cached min freq (via H2G) for it to take effect. v2: Clarify commit message (Ashutosh) Fixes: 95ccf312a1e4 ("drm/i915/guc/slpc: Allow SLPC to use efficient frequency") Reviewed-by: Ashutosh Dixit Signed-off-by: Vinay Belgaumkar Signed-off-by: Daniele Ceraolo Spurio Link: https://patchwork.freedesktop.org/patch/msgid/20230621014257.1769564-1-vinay.belgaumkar@intel.com (cherry picked from commit da86b2b13f1d1ca26745b951ac94421f3137539a) Signed-off-by: Tvrtko Ursulin Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c index fdd895f73f9f..72ba1c758ca7 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c @@ -580,7 +580,7 @@ static int slpc_set_softlimits(struct intel_guc_slpc *slpc) if (unlikely(ret)) return ret; slpc_to_gt(slpc)->defaults.min_freq = slpc->min_freq_softlimit; - } else if (slpc->min_freq_softlimit != slpc->min_freq) { + } else { return intel_guc_slpc_set_min_freq(slpc, slpc->min_freq_softlimit); } -- cgit 1.4.1 From 0623f13959fddd4dff9e0c9e0f24b1bfae8a3ec4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 6 Jun 2023 14:19:01 +0800 Subject: f2fs: check return value of freeze_super() [ Upstream commit 8bec7dd1b3f7d7769d433d67bde404de948a2d95 ] freeze_super() can fail, it needs to check its return value and do error handling in f2fs_resize_fs(). Fixes: 04f0b2eaa3b3 ("f2fs: ioctl for removing a range from F2FS") Fixes: b4b10061ef98 ("f2fs: refactor resize_fs to avoid meta updates in progress") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/gc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 3de887d07c06..aa4d513daa8f 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -2186,7 +2186,9 @@ out_drop_write: if (err) return err; - freeze_super(sbi->sb); + err = freeze_super(sbi->sb); + if (err) + return err; if (f2fs_readonly(sbi->sb)) { thaw_super(sbi->sb); -- cgit 1.4.1 From 9dbcfc01d6868b81c440f35a4c0eef9ed86f34c3 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 8 Jun 2023 04:54:35 +0200 Subject: media: cec: i2c: ch7322: also select REGMAP [ Upstream commit 29f96ac23648b2259f42d40703c47dd18fd172ca ] Selecting only REGMAP_I2C can leave REGMAP unset, causing build errors, so also select REGMAP to prevent the build errors. ../drivers/media/cec/i2c/ch7322.c:158:21: error: variable 'ch7322_regmap' has initializer but incomplete type 158 | static const struct regmap_config ch7322_regmap = { ../drivers/media/cec/i2c/ch7322.c:159:10: error: 'const struct regmap_config' has no member named 'reg_bits' 159 | .reg_bits = 8, ../drivers/media/cec/i2c/ch7322.c:159:21: warning: excess elements in struct initializer 159 | .reg_bits = 8, ../drivers/media/cec/i2c/ch7322.c:160:10: error: 'const struct regmap_config' has no member named 'val_bits' 160 | .val_bits = 8, ../drivers/media/cec/i2c/ch7322.c:160:21: warning: excess elements in struct initializer 160 | .val_bits = 8, ../drivers/media/cec/i2c/ch7322.c:161:10: error: 'const struct regmap_config' has no member named 'max_register' 161 | .max_register = 0x7f, ../drivers/media/cec/i2c/ch7322.c:161:25: warning: excess elements in struct initializer 161 | .max_register = 0x7f, ../drivers/media/cec/i2c/ch7322.c:162:10: error: 'const struct regmap_config' has no member named 'disable_locking' 162 | .disable_locking = true, ../drivers/media/cec/i2c/ch7322.c:162:28: warning: excess elements in struct initializer 162 | .disable_locking = true, ../drivers/media/cec/i2c/ch7322.c: In function 'ch7322_probe': ../drivers/media/cec/i2c/ch7322.c:468:26: error: implicit declaration of function 'devm_regmap_init_i2c' [-Werror=implicit-function-declaration] 468 | ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap); ../drivers/media/cec/i2c/ch7322.c:468:24: warning: assignment to 'struct regmap *' from 'int' makes pointer from integer without a cast [-Wint-conversion] 468 | ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap); ../drivers/media/cec/i2c/ch7322.c: At top level: ../drivers/media/cec/i2c/ch7322.c:158:35: error: storage size of 'ch7322_regmap' isn't known 158 | static const struct regmap_config ch7322_regmap = { Link: https://lore.kernel.org/linux-media/20230608025435.29249-1-rdunlap@infradead.org Fixes: 21b9a47e0ec7 ("media: cec: i2c: ch7322: Add ch7322 CEC controller driver") Signed-off-by: Randy Dunlap Cc: Jeff Chase Cc: Hans Verkuil Cc: Joe Tessler Cc: Arnd Bergmann Cc: Mark Brown Cc: Masahiro Yamada Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin --- drivers/media/cec/i2c/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/cec/i2c/Kconfig b/drivers/media/cec/i2c/Kconfig index 70432a1d6918..d912d143fb31 100644 --- a/drivers/media/cec/i2c/Kconfig +++ b/drivers/media/cec/i2c/Kconfig @@ -5,6 +5,7 @@ config CEC_CH7322 tristate "Chrontel CH7322 CEC controller" depends on I2C + select REGMAP select REGMAP_I2C select CEC_CORE help -- cgit 1.4.1 From 1aa5a6a6d28c77e364feaba35ff7f12d2d74fec1 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Tue, 27 Jun 2023 12:03:40 +0000 Subject: sctp: fix potential deadlock on &net->sctp.addr_wq_lock [ Upstream commit 6feb37b3b06e9049e20dcf7e23998f92c9c5be9a ] As &net->sctp.addr_wq_lock is also acquired by the timer sctp_addr_wq_timeout_handler() in protocal.c, the same lock acquisition at sctp_auto_asconf_init() seems should disable irq since it is called from sctp_accept() under process context. Possible deadlock scenario: sctp_accept() -> sctp_sock_migrate() -> sctp_auto_asconf_init() -> spin_lock(&net->sctp.addr_wq_lock) -> sctp_addr_wq_timeout_handler() -> spin_lock_bh(&net->sctp.addr_wq_lock); (deadlock here) This flaw was found using an experimental static analysis tool we are developing for irq-related deadlock. The tentative patch fix the potential deadlock by spin_lock_bh(). Signed-off-by: Chengfeng Ye Fixes: 34e5b0118685 ("sctp: delay auto_asconf init until binding the first addr") Acked-by: Xin Long Link: https://lore.kernel.org/r/20230627120340.19432-1-dg573847474@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sctp/socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index e1011311bc87..c806d272107a 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -363,9 +363,9 @@ static void sctp_auto_asconf_init(struct sctp_sock *sp) struct net *net = sock_net(&sp->inet.sk); if (net->sctp.default_auto_asconf) { - spin_lock(&net->sctp.addr_wq_lock); + spin_lock_bh(&net->sctp.addr_wq_lock); list_add_tail(&sp->auto_asconf_list, &net->sctp.auto_asconf_splist); - spin_unlock(&net->sctp.addr_wq_lock); + spin_unlock_bh(&net->sctp.addr_wq_lock); sp->do_auto_asconf = 1; } } -- cgit 1.4.1 From 201948effabfaf470249de25a6c747800f9a726c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 27 Jun 2023 14:38:11 +0200 Subject: net/sched: act_ipt: add sanity checks on table name and hook locations [ Upstream commit b4ee93380b3c891fea996af8d1d3ca0e36ad31f0 ] Looks like "tc" hard-codes "mangle" as the only supported table name, but on kernel side there are no checks. This is wrong. Not all xtables targets are safe to call from tc. E.g. "nat" targets assume skb has a conntrack object assigned to it. Normally those get called from netfilter nat core which consults the nat table to obtain the address mapping. "tc" userspace either sets PRE or POSTROUTING as hook number, but there is no validation of this on kernel side, so update netlink policy to reject bogus numbers. Some targets may assume skb_dst is set for input/forward hooks, so prevent those from being used. act_ipt uses the hook number in two places: 1. the state hook number, this is fine as-is 2. to set par.hook_mask The latter is a bit mask, so update the assignment to make xt_check_target() to the right thing. Followup patch adds required checks for the skb/packet headers before calling the targets evaluation function. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Florian Westphal Reviewed-by: Simon Horman Acked-by: Jamal Hadi Salim Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/act_ipt.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 1625e1037416..dc92975a9374 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -47,7 +47,7 @@ static int ipt_init_target(struct net *net, struct xt_entry_target *t, par.entryinfo = &e; par.target = target; par.targinfo = t->data; - par.hook_mask = hook; + par.hook_mask = 1 << hook; par.family = NFPROTO_IPV4; ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); @@ -84,7 +84,8 @@ static void tcf_ipt_release(struct tc_action *a) static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = { [TCA_IPT_TABLE] = { .type = NLA_STRING, .len = IFNAMSIZ }, - [TCA_IPT_HOOK] = { .type = NLA_U32 }, + [TCA_IPT_HOOK] = NLA_POLICY_RANGE(NLA_U32, NF_INET_PRE_ROUTING, + NF_INET_NUMHOOKS), [TCA_IPT_INDEX] = { .type = NLA_U32 }, [TCA_IPT_TARG] = { .len = sizeof(struct xt_entry_target) }, }; @@ -157,15 +158,27 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla, return -EEXIST; } } + + err = -EINVAL; hook = nla_get_u32(tb[TCA_IPT_HOOK]); + switch (hook) { + case NF_INET_PRE_ROUTING: + break; + case NF_INET_POST_ROUTING: + break; + default: + goto err1; + } + + if (tb[TCA_IPT_TABLE]) { + /* mangle only for now */ + if (nla_strcmp(tb[TCA_IPT_TABLE], "mangle")) + goto err1; + } - err = -ENOMEM; - tname = kmalloc(IFNAMSIZ, GFP_KERNEL); + tname = kstrdup("mangle", GFP_KERNEL); if (unlikely(!tname)) goto err1; - if (tb[TCA_IPT_TABLE] == NULL || - nla_strscpy(tname, tb[TCA_IPT_TABLE], IFNAMSIZ) >= IFNAMSIZ) - strcpy(tname, "mangle"); t = kmemdup(td, td->u.target_size, GFP_KERNEL); if (unlikely(!t)) -- cgit 1.4.1 From a6c9b0f7ba95311d797b6c5341b4e3ecdb920796 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 28 Jan 2023 10:58:30 -0500 Subject: net: add a couple of helpers for iph tot_len [ Upstream commit 058a8f7f73aae1cc22b53fcefec031b9e391b54d ] This patch adds three APIs to replace the iph->tot_len setting and getting in all places where IPv4 BIG TCP packets may reach, they will be used in the following patches. Note that iph_totlen() will be used when iph is not in linear data of the skb. Signed-off-by: Xin Long Reviewed-by: David Ahern Reviewed-by: Eric Dumazet Signed-off-by: Jakub Kicinski Stable-dep-of: b2dc32dcba08 ("net/sched: act_ipt: add sanity checks on skb before calling target") Signed-off-by: Sasha Levin --- include/linux/ip.h | 21 +++++++++++++++++++++ include/net/route.h | 3 --- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/linux/ip.h b/include/linux/ip.h index 3d9c6750af62..d11c25f5030a 100644 --- a/include/linux/ip.h +++ b/include/linux/ip.h @@ -35,4 +35,25 @@ static inline unsigned int ip_transport_len(const struct sk_buff *skb) { return ntohs(ip_hdr(skb)->tot_len) - skb_network_header_len(skb); } + +static inline unsigned int iph_totlen(const struct sk_buff *skb, const struct iphdr *iph) +{ + u32 len = ntohs(iph->tot_len); + + return (len || !skb_is_gso(skb) || !skb_is_gso_tcp(skb)) ? + len : skb->len - skb_network_offset(skb); +} + +static inline unsigned int skb_ip_totlen(const struct sk_buff *skb) +{ + return iph_totlen(skb, ip_hdr(skb)); +} + +/* IPv4 datagram length is stored into 16bit field (tot_len) */ +#define IP_MAX_MTU 0xFFFFU + +static inline void iph_set_totlen(struct iphdr *iph, unsigned int len) +{ + iph->tot_len = len <= IP_MAX_MTU ? htons(len) : 0; +} #endif /* _LINUX_IP_H */ diff --git a/include/net/route.h b/include/net/route.h index 6e92dd5bcd61..fe00b0a2e475 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -35,9 +35,6 @@ #include #include -/* IPv4 datagram length is stored into 16bit field (tot_len) */ -#define IP_MAX_MTU 0xFFFFU - #define RTO_ONLINK 0x01 #define RT_CONN_FLAGS(sk) (RT_TOS(inet_sk(sk)->tos) | sock_flag(sk, SOCK_LOCALROUTE)) -- cgit 1.4.1 From 946edfb7d436df99e02d3d18bc8972b5542354d9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 27 Jun 2023 14:38:12 +0200 Subject: net/sched: act_ipt: add sanity checks on skb before calling target [ Upstream commit b2dc32dcba08bf55cec600caa76f4afd2e3614df ] Netfilter targets make assumptions on the skb state, for example iphdr is supposed to be in the linear area. This is normally done by IP stack, but in act_ipt case no such checks are made. Some targets can even assume that skb_dst will be valid. Make a minimum effort to check for this: - Don't call the targets eval function for non-ipv4 skbs. - Don't call the targets eval function for POSTROUTING emulation when the skb has no dst set. v3: use skb_protocol helper (Davide Caratti) Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Florian Westphal Reviewed-by: Simon Horman Acked-by: Jamal Hadi Salim Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/act_ipt.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index dc92975a9374..29974de689ec 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -229,6 +229,26 @@ static int tcf_xt_init(struct net *net, struct nlattr *nla, a, &act_xt_ops, tp, flags); } +static bool tcf_ipt_act_check(struct sk_buff *skb) +{ + const struct iphdr *iph; + unsigned int nhoff, len; + + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + return false; + + nhoff = skb_network_offset(skb); + iph = ip_hdr(skb); + if (iph->ihl < 5 || iph->version != 4) + return false; + + len = skb_ip_totlen(skb); + if (skb->len < nhoff + len || len < (iph->ihl * 4u)) + return false; + + return pskb_may_pull(skb, iph->ihl * 4u); +} + static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -242,9 +262,22 @@ static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a, .pf = NFPROTO_IPV4, }; + if (skb_protocol(skb, false) != htons(ETH_P_IP)) + return TC_ACT_UNSPEC; + if (skb_unclone(skb, GFP_ATOMIC)) return TC_ACT_UNSPEC; + if (!tcf_ipt_act_check(skb)) + return TC_ACT_UNSPEC; + + if (state.hook == NF_INET_POST_ROUTING) { + if (!skb_dst(skb)) + return TC_ACT_UNSPEC; + + state.out = skb->dev; + } + spin_lock(&ipt->tcf_lock); tcf_lastuse_update(&ipt->tcf_tm); -- cgit 1.4.1 From a252547c8920a5b790d609b28d1702a01ea8ff57 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 29 Jun 2023 12:58:47 +0300 Subject: spi: spi-geni-qcom: enable SPI_CONTROLLER_MUST_TX for GPI DMA mode [ Upstream commit d10005837be83906bbd2078c3b4f9dfcbd6c95b6 ] The GPI DMA mode requires for TX DMA to be prepared. Force SPI core to provide TX buffer even if the caller didn't provide one by setting the SPI_CONTROLLER_MUST_TX flag. Fixes: b59c122484ec ("spi: spi-geni-qcom: Add support for GPI dma") Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230629095847.3648597-1-dmitry.baryshkov@linaro.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-geni-qcom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 689b94fc5570..7b76dcd11e2b 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -979,6 +979,12 @@ static int spi_geni_probe(struct platform_device *pdev) if (mas->cur_xfer_mode == GENI_SE_FIFO) spi->set_cs = spi_geni_set_cs; + /* + * TX is required per GSI spec, see setup_gsi_xfer(). + */ + if (mas->cur_xfer_mode == GENI_GPI_DMA) + spi->flags = SPI_CONTROLLER_MUST_TX; + ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi); if (ret) goto spi_geni_release_dma; -- cgit 1.4.1 From 7826202689af8d5547d8712e2e2badff0d9165ad Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 27 Jun 2023 19:31:12 +0300 Subject: net: mscc: ocelot: don't report that RX timestamping is enabled by default [ Upstream commit 4fd44b82b7aceaa35c2901c6546d2c4198e0799d ] PTP RX timestamping should be enabled when the user requests it, not by default. If it is enabled by default, it can be problematic when the ocelot driver is a DSA master, and it sidesteps what DSA tries to avoid through __dsa_master_hwtstamp_validate(). Additionally, after the change which made ocelot trap PTP packets only to the CPU at ocelot_hwtstamp_set() time, it is no longer even true that RX timestamping is enabled by default, because until ocelot_hwtstamp_set() is called, the PTP traps are actually not set up. So the rx_filter field of ocelot->hwtstamp_config reflects an incorrect reality. Fixes: 96ca08c05838 ("net: mscc: ocelot: set up traps for PTP packets") Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support") Signed-off-by: Vladimir Oltean Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mscc/ocelot_ptp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c index 2180ae94c744..673bfd70867a 100644 --- a/drivers/net/ethernet/mscc/ocelot_ptp.c +++ b/drivers/net/ethernet/mscc/ocelot_ptp.c @@ -824,11 +824,6 @@ int ocelot_init_timestamp(struct ocelot *ocelot, ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC); - /* There is no device reconfiguration, PTP Rx stamping is always - * enabled. - */ - ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; - return 0; } EXPORT_SYMBOL(ocelot_init_timestamp); -- cgit 1.4.1 From e9dda2b68cb9f21b2518937fc6a6c76c4a86f88f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 27 Jun 2023 19:31:13 +0300 Subject: net: mscc: ocelot: don't keep PTP configuration of all ports in single structure [ Upstream commit 45d0fcb5bc9558d0bf3d2fa7fabc5d8a88d35439 ] In a future change, the driver will need to determine whether PTP RX timestamping is enabled on a port (including whether traps were set up on that port in particular) and that is currently not possible. The driver supports different RX filters (L2, L4) and kinds of TX timestamping (one-step, two-step) on its ports, but it saves all configuration in a single struct hwtstamp_config that is global to the switch. So, the latest timestamping configuration on one port (including a request to disable timestamping) affects what gets reported for all ports, even though the configuration itself is still individual to each port. The port timestamping configurations are only coupled because of the common structure, so replace the hwtstamp_config with a mask of trapped protocols saved per port. We also have the ptp_cmd to distinguish between one-step and two-step PTP timestamping, so with those 2 bits of information we can fully reconstruct a descriptive struct hwtstamp_config for each port, during the SIOCGHWTSTAMP ioctl. Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support") Fixes: 96ca08c05838 ("net: mscc: ocelot: set up traps for PTP packets") Signed-off-by: Vladimir Oltean Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mscc/ocelot.c | 1 - drivers/net/ethernet/mscc/ocelot_ptp.c | 61 +++++++++++++++++++++++----------- include/soc/mscc/ocelot.h | 10 ++++-- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 13b14110a060..01b6e13f4692 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2728,7 +2728,6 @@ int ocelot_init(struct ocelot *ocelot) } } - mutex_init(&ocelot->ptp_lock); mutex_init(&ocelot->mact_lock); mutex_init(&ocelot->fwd_domain_lock); mutex_init(&ocelot->tas_lock); diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c index 673bfd70867a..cb32234a5bf1 100644 --- a/drivers/net/ethernet/mscc/ocelot_ptp.c +++ b/drivers/net/ethernet/mscc/ocelot_ptp.c @@ -439,8 +439,12 @@ static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port) static int ocelot_setup_ptp_traps(struct ocelot *ocelot, int port, bool l2, bool l4) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; int err; + ocelot_port->trap_proto &= ~(OCELOT_PROTO_PTP_L2 | + OCELOT_PROTO_PTP_L4); + if (l2) err = ocelot_l2_ptp_trap_add(ocelot, port); else @@ -464,6 +468,11 @@ static int ocelot_setup_ptp_traps(struct ocelot *ocelot, int port, if (err) return err; + if (l2) + ocelot_port->trap_proto |= OCELOT_PROTO_PTP_L2; + if (l4) + ocelot_port->trap_proto |= OCELOT_PROTO_PTP_L4; + return 0; err_ipv6: @@ -474,10 +483,38 @@ err_ipv4: return err; } +static int ocelot_traps_to_ptp_rx_filter(unsigned int proto) +{ + if ((proto & OCELOT_PROTO_PTP_L2) && (proto & OCELOT_PROTO_PTP_L4)) + return HWTSTAMP_FILTER_PTP_V2_EVENT; + else if (proto & OCELOT_PROTO_PTP_L2) + return HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + else if (proto & OCELOT_PROTO_PTP_L4) + return HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + + return HWTSTAMP_FILTER_NONE; +} + int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr) { - return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, - sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct hwtstamp_config cfg = {}; + + switch (ocelot_port->ptp_cmd) { + case IFH_REW_OP_TWO_STEP_PTP: + cfg.tx_type = HWTSTAMP_TX_ON; + break; + case IFH_REW_OP_ORIGIN_PTP: + cfg.tx_type = HWTSTAMP_TX_ONESTEP_SYNC; + break; + default: + cfg.tx_type = HWTSTAMP_TX_OFF; + break; + } + + cfg.rx_filter = ocelot_traps_to_ptp_rx_filter(ocelot_port->trap_proto); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } EXPORT_SYMBOL(ocelot_hwstamp_get); @@ -509,8 +546,6 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) return -ERANGE; } - mutex_lock(&ocelot->ptp_lock); - switch (cfg.rx_filter) { case HWTSTAMP_FILTER_NONE: break; @@ -531,28 +566,14 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) l4 = true; break; default: - mutex_unlock(&ocelot->ptp_lock); return -ERANGE; } err = ocelot_setup_ptp_traps(ocelot, port, l2, l4); - if (err) { - mutex_unlock(&ocelot->ptp_lock); + if (err) return err; - } - - if (l2 && l4) - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; - else if (l2) - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; - else if (l4) - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; - else - cfg.rx_filter = HWTSTAMP_FILTER_NONE; - /* Commit back the result & save it */ - memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg)); - mutex_unlock(&ocelot->ptp_lock); + cfg.rx_filter = ocelot_traps_to_ptp_rx_filter(ocelot_port->trap_proto); return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 967ba30ea636..195ca8f0b6f9 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -902,6 +902,11 @@ enum macaccess_entry_type { ENTRYTYPE_MACv6, }; +enum ocelot_proto { + OCELOT_PROTO_PTP_L2 = BIT(0), + OCELOT_PROTO_PTP_L4 = BIT(1), +}; + #define OCELOT_QUIRK_PCS_PERFORMS_RATE_ADAPTATION BIT(0) #define OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP BIT(1) @@ -939,6 +944,8 @@ struct ocelot_port { unsigned int ptp_skbs_in_flight; struct sk_buff_head tx_skbs; + unsigned int trap_proto; + u16 mrp_ring_id; u8 ptp_cmd; @@ -1032,12 +1039,9 @@ struct ocelot { u8 ptp:1; struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_info; - struct hwtstamp_config hwtstamp_config; unsigned int ptp_skbs_in_flight; /* Protects the 2-step TX timestamp ID logic */ spinlock_t ts_id_lock; - /* Protects the PTP interface state */ - struct mutex ptp_lock; /* Protects the PTP clock */ spinlock_t ptp_clock_lock; struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM]; -- cgit 1.4.1 From 2f99d19dc620bc59034f239784d11b586ae007b1 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 27 Jun 2023 19:31:14 +0300 Subject: net: dsa: felix: don't drop PTP frames with tag_8021q when RX timestamping is disabled [ Upstream commit 2edcfcbb3c5946609be1d8875473a240b170673b ] The driver implements a workaround for the fact that it doesn't have an IRQ source to tell it whether PTP frames are available through the extraction registers, for those frames to be processed and passed towards the network stack. That workaround is to configure the switch, through felix_hwtstamp_set() -> felix_update_trapping_destinations(), to create two copies of PTP packets: one sent over Ethernet to the DSA master, and one to be consumed through the aforementioned CPU extraction queue registers. The reason why we want PTP packets to be consumed through the CPU extraction registers in the first place is because we want to see their hardware RX timestamp. With tag_8021q, that is only visible that way, and it isn't visible with the copy of the packet that's transmitted over Ethernet. The problem with the workaround implementation is that it drops the packet received over Ethernet, in expectation of its copy being present in the CPU extraction registers. However, if felix_hwtstamp_set() hasn't run (aka PTP RX timestamping is disabled), the driver will drop the original PTP frame and there will be no copy of it in the CPU extraction registers. So, the network stack will simply not see any PTP frame. Look at the port's trapping configuration to see whether the driver has previously enabled the CPU extraction registers. If it hasn't, just don't RX timestamp the frame and let it be passed up the stack by DSA, which is perfectly fine. Fixes: 0a6f17c6ae21 ("net: dsa: tag_ocelot_8021q: add support for PTP timestamping") Signed-off-by: Vladimir Oltean Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/dsa/ocelot/felix.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index dd3a18cc89dd..4faabc4364aa 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1705,6 +1705,18 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port, u32 tstamp_hi; u64 tstamp; + switch (type & PTP_CLASS_PMASK) { + case PTP_CLASS_L2: + if (!(ocelot->ports[port]->trap_proto & OCELOT_PROTO_PTP_L2)) + return false; + break; + case PTP_CLASS_IPV4: + case PTP_CLASS_IPV6: + if (!(ocelot->ports[port]->trap_proto & OCELOT_PROTO_PTP_L4)) + return false; + break; + } + /* If the "no XTR IRQ" workaround is in use, tell DSA to defer this skb * for RX timestamping. Then free it, and poll for its copy through * MMIO in the CPU port module, and inject that into the stack from -- cgit 1.4.1 From 060d36670d50c6390f05092394950cd407d1eebe Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 27 Jun 2023 12:42:06 +0300 Subject: net: dsa: sja1105: always enable the INCL_SRCPT option [ Upstream commit b4638af8885af93cd70351081da1909c59342440 ] Link-local traffic on bridged SJA1105 ports is sometimes tagged by the hardware with source port information (when the port is under a VLAN aware bridge). The tag_8021q source port identification has become more loose ("imprecise") and will report a plausible rather than exact bridge port, when under a bridge (be it VLAN-aware or VLAN-unaware). But link-local traffic always needs to know the precise source port. Modify the driver logic (and therefore: the tagging protocol itself) to always include the source port information with link-local packets, regardless of whether the port is standalone, under a VLAN-aware or VLAN-unaware bridge. This makes it possible for the tagging driver to give priority to that information over the tag_8021q VLAN header. The big drawback with INCL_SRCPT is that it makes it impossible to distinguish between an original MAC DA of 01:80:C2:XX:YY:ZZ and 01:80:C2:AA:BB:ZZ, because the tagger just patches MAC DA bytes 3 and 4 with zeroes. Only if PTP RX timestamping is enabled, the switch will generate a META follow-up frame containing the RX timestamp and the original bytes 3 and 4 of the MAC DA. Those will be used to patch up the original packet. Nonetheless, in the absence of PTP RX timestamping, we have to live with this limitation, since it is more important to have the more precise source port information for link-local traffic. Fixes: d7f9787a763f ("net: dsa: tag_8021q: add support for imprecise RX based on the VBID") Fixes: 91495f21fcec ("net: dsa: tag_8021q: replace the SVL bridging with VLAN-unaware IVL bridging") Signed-off-by: Vladimir Oltean Reviewed-by: Simon Horman Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/dsa/sja1105/sja1105_main.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index b70dcf32a26d..8bd61f2ebb2a 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -866,11 +866,11 @@ static int sja1105_init_general_params(struct sja1105_private *priv) .hostprio = 7, .mac_fltres1 = SJA1105_LINKLOCAL_FILTER_A, .mac_flt1 = SJA1105_LINKLOCAL_FILTER_A_MASK, - .incl_srcpt1 = false, + .incl_srcpt1 = true, .send_meta1 = false, .mac_fltres0 = SJA1105_LINKLOCAL_FILTER_B, .mac_flt0 = SJA1105_LINKLOCAL_FILTER_B_MASK, - .incl_srcpt0 = false, + .incl_srcpt0 = true, .send_meta0 = false, /* Default to an invalid value */ .mirr_port = priv->ds->num_ports, @@ -2407,11 +2407,6 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, general_params->tpid = tpid; /* EtherType used to identify outer tagged (S-tag) VLAN traffic */ general_params->tpid2 = tpid2; - /* When VLAN filtering is on, we need to at least be able to - * decode management traffic through the "backup plan". - */ - general_params->incl_srcpt1 = enabled; - general_params->incl_srcpt0 = enabled; for (port = 0; port < ds->num_ports; port++) { if (dsa_is_unused_port(ds, port)) -- cgit 1.4.1 From f7210424471442e50883f2793ee2a527fb72fe04 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 27 Jun 2023 12:42:07 +0300 Subject: net: dsa: tag_sja1105: always prefer source port information from INCL_SRCPT [ Upstream commit c1ae02d876898b1b8ca1e12c6f84d7b406263800 ] Currently the sja1105 tagging protocol prefers using the source port information from the VLAN header if that is available, falling back to the INCL_SRCPT option if it isn't. The VLAN header is available for all frames except for META frames initiated by the switch (containing RX timestamps), and thus, the "if (is_link_local)" branch is practically dead. The tag_8021q source port identification has become more loose ("imprecise") and will report a plausible rather than exact bridge port, when under a bridge (be it VLAN-aware or VLAN-unaware). But link-local traffic always needs to know the precise source port. With incorrect source port reporting, for example PTP traffic over 2 bridged ports will all be seen on sockets opened on the first such port, which is incorrect. Now that the tagging protocol has been changed to make link-local frames always contain source port information, we can reverse the order of the checks so that we always give precedence to that information (which is always precise) in lieu of the tag_8021q VID which is only precise for a standalone port. Fixes: d7f9787a763f ("net: dsa: tag_8021q: add support for imprecise RX based on the VBID") Fixes: 91495f21fcec ("net: dsa: tag_8021q: replace the SVL bridging with VLAN-unaware IVL bridging") Signed-off-by: Vladimir Oltean Reviewed-by: Simon Horman Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/dsa/tag_sja1105.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 1a85125bda6d..731dc3a111ef 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -540,10 +540,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, is_link_local = sja1105_is_link_local(skb); is_meta = sja1105_is_meta_frame(skb); - if (sja1105_skb_has_tag_8021q(skb)) { - /* Normal traffic path. */ - sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid); - } else if (is_link_local) { + if (is_link_local) { /* Management traffic path. Switch embeds the switch ID and * port ID into bytes of the destination MAC, courtesy of * the incl_srcpt options. @@ -557,16 +554,39 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, sja1105_meta_unpack(skb, &meta); source_port = meta.source_port; switch_id = meta.switch_id; - } else { + } + + /* Normal data plane traffic and link-local frames are tagged with + * a tag_8021q VLAN which we have to strip + */ + if (sja1105_skb_has_tag_8021q(skb)) { + int tmp_source_port = -1, tmp_switch_id = -1; + + sja1105_vlan_rcv(skb, &tmp_source_port, &tmp_switch_id, &vbid, + &vid); + /* Preserve the source information from the INCL_SRCPT option, + * if available. This allows us to not overwrite a valid source + * port and switch ID with zeroes when receiving link-local + * frames from a VLAN-unaware bridged port (non-zero vbid) or a + * VLAN-aware bridged port (non-zero vid). + */ + if (source_port == -1) + source_port = tmp_source_port; + if (switch_id == -1) + switch_id = tmp_switch_id; + } else if (source_port == -1 && switch_id == -1) { + /* Packets with no source information have no chance of + * getting accepted, drop them straight away. + */ return NULL; } - if (vbid >= 1) + if (source_port != -1 && switch_id != -1) + skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); + else if (vbid >= 1) skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid); - else if (source_port == -1 || switch_id == -1) - skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); else - skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); + skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); if (!skb->dev) { netdev_warn(netdev, "Couldn't decode source port\n"); return NULL; -- cgit 1.4.1 From 102f3555cebe11a145a3ee4468142aeb178501e7 Mon Sep 17 00:00:00 2001 From: Tobias Heider Date: Wed, 28 Jun 2023 02:13:32 +0200 Subject: Add MODULE_FIRMWARE() for FIRMWARE_TG357766. [ Upstream commit 046f753da6143ee16452966915087ec8b0de3c70 ] Fixes a bug where on the M1 mac mini initramfs-tools fails to include the necessary firmware into the initrd. Fixes: c4dab50697ff ("tg3: Download 57766 EEE service patch firmware") Signed-off-by: Tobias Heider Reviewed-by: Michael Chan Link: https://lore.kernel.org/r/ZJt7LKzjdz8+dClx@tobhe.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/tg3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index af9ea5e4371b..960904101677 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -224,6 +224,7 @@ MODULE_AUTHOR("David S. Miller (davem@redhat.com) and Jeff Garzik (jgarzik@pobox MODULE_DESCRIPTION("Broadcom Tigon3 ethernet driver"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(FIRMWARE_TG3); +MODULE_FIRMWARE(FIRMWARE_TG357766); MODULE_FIRMWARE(FIRMWARE_TG3TSO); MODULE_FIRMWARE(FIRMWARE_TG3TSO5); -- cgit 1.4.1 From 018b12ff1661f5542133091daf740b1408337bf3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 31 May 2023 11:04:23 +0200 Subject: Bluetooth: fix invalid-bdaddr quirk for non-persistent setup [ Upstream commit 0cb7365850bacb8c2a9975cae672d65714d8daa1 ] Devices that lack persistent storage for the device address can indicate this by setting the HCI_QUIRK_INVALID_BDADDR which causes the controller to be marked as unconfigured until user space has set a valid address. Once configured, the device address must be set on every setup for controllers with HCI_QUIRK_NON_PERSISTENT_SETUP to avoid marking the controller as unconfigured and requiring the address to be set again. Fixes: 740011cfe948 ("Bluetooth: Add new quirk for non-persistent setup settings") Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bluetooth/hci_sync.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 1805ddee0cd0..37131a36700a 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4567,23 +4567,17 @@ static int hci_dev_setup_sync(struct hci_dev *hdev) invalid_bdaddr = test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); if (!ret) { - if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) { - if (!bacmp(&hdev->public_addr, BDADDR_ANY)) - hci_dev_get_bd_addr_from_property(hdev); - - if (bacmp(&hdev->public_addr, BDADDR_ANY) && - hdev->set_bdaddr) { - ret = hdev->set_bdaddr(hdev, - &hdev->public_addr); - - /* If setting of the BD_ADDR from the device - * property succeeds, then treat the address - * as valid even if the invalid BD_ADDR - * quirk indicates otherwise. - */ - if (!ret) - invalid_bdaddr = false; - } + if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks) && + !bacmp(&hdev->public_addr, BDADDR_ANY)) + hci_dev_get_bd_addr_from_property(hdev); + + if ((invalid_bdaddr || + test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) && + bacmp(&hdev->public_addr, BDADDR_ANY) && + hdev->set_bdaddr) { + ret = hdev->set_bdaddr(hdev, &hdev->public_addr); + if (!ret) + invalid_bdaddr = false; } } -- cgit 1.4.1 From 4aa515393f02f69cb33121f8aac9078c6c925297 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Thu, 1 Jun 2023 09:34:43 +0300 Subject: Bluetooth: ISO: use hci_sync for setting CIG parameters [ Upstream commit 6b9545dc9f8ff01d8bc1229103960d9cd265343f ] When reconfiguring CIG after disconnection of the last CIS, LE Remove CIG shall be sent before LE Set CIG Parameters. Otherwise, it fails because CIG is in the inactive state and not configurable (Core v5.3 Vol 6 Part B Sec. 4.5.14.3). This ordering is currently wrong under suitable timing conditions, because LE Remove CIG is sent via the hci_sync queue and may be delayed, but Set CIG Parameters is via hci_send_cmd. Make the ordering well-defined by sending also Set CIG Parameters via hci_sync. Fixes: 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bluetooth/hci_conn.c | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ab9f00252dc2..fef09d212138 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -770,6 +770,11 @@ static void le_conn_timeout(struct work_struct *work) hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); } +struct iso_cig_params { + struct hci_cp_le_set_cig_params cp; + struct hci_cis_params cis[0x1f]; +}; + struct iso_list_data { union { u8 cig; @@ -781,10 +786,7 @@ struct iso_list_data { u16 sync_handle; }; int count; - struct { - struct hci_cp_le_set_cig_params cp; - struct hci_cis_params cis[0x11]; - } pdu; + struct iso_cig_params pdu; }; static void bis_list(struct hci_conn *conn, void *data) @@ -1705,10 +1707,33 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp); } +static void set_cig_params_complete(struct hci_dev *hdev, void *data, int err) +{ + struct iso_cig_params *pdu = data; + + bt_dev_dbg(hdev, ""); + + if (err) + bt_dev_err(hdev, "Unable to set CIG parameters: %d", err); + + kfree(pdu); +} + +static int set_cig_params_sync(struct hci_dev *hdev, void *data) +{ + struct iso_cig_params *pdu = data; + u32 plen; + + plen = sizeof(pdu->cp) + pdu->cp.num_cis * sizeof(pdu->cis[0]); + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS, plen, pdu, + HCI_CMD_TIMEOUT); +} + static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) { struct hci_dev *hdev = conn->hdev; struct iso_list_data data; + struct iso_cig_params *pdu; memset(&data, 0, sizeof(data)); @@ -1779,12 +1804,18 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) if (qos->cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis) return false; - if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS, - sizeof(data.pdu.cp) + - (data.pdu.cp.num_cis * sizeof(*data.pdu.cis)), - &data.pdu) < 0) + pdu = kzalloc(sizeof(*pdu), GFP_KERNEL); + if (!pdu) return false; + memcpy(pdu, &data.pdu, sizeof(*pdu)); + + if (hci_cmd_sync_queue(hdev, set_cig_params_sync, pdu, + set_cig_params_complete) < 0) { + kfree(pdu); + return false; + } + return true; } -- cgit 1.4.1 From 1a7f268ccc3cd7d7a08c7cf2b5da9c87c05f2938 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Mon, 30 Jan 2023 20:37:01 +0200 Subject: Bluetooth: MGMT: add CIS feature bits to controller information [ Upstream commit 2394186a2cefb9a45a029281a55749804dd8c556 ] Userspace needs to know whether the adapter has feature support for Connected Isochronous Stream - Central/Peripheral, so it can set up LE Audio features accordingly. Expose these feature bits as settings in MGMT controller info. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 73f55453ea52 ("Bluetooth: MGMT: Fix marking SCAN_RSP as not connectable") Signed-off-by: Sasha Levin --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/mgmt.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 743f6f59dff8..e18a927669c0 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -109,6 +109,8 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_STATIC_ADDRESS 0x00008000 #define MGMT_SETTING_PHY_CONFIGURATION 0x00010000 #define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000 +#define MGMT_SETTING_CIS_CENTRAL 0x00040000 +#define MGMT_SETTING_CIS_PERIPHERAL 0x00080000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fc4ba0884da9..815f2abe918e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -859,6 +859,12 @@ static u32 get_supported_settings(struct hci_dev *hdev) hdev->set_bdaddr) settings |= MGMT_SETTING_CONFIGURATION; + if (cis_central_capable(hdev)) + settings |= MGMT_SETTING_CIS_CENTRAL; + + if (cis_peripheral_capable(hdev)) + settings |= MGMT_SETTING_CIS_PERIPHERAL; + settings |= MGMT_SETTING_PHY_CONFIGURATION; return settings; @@ -932,6 +938,12 @@ static u32 get_current_settings(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED)) settings |= MGMT_SETTING_WIDEBAND_SPEECH; + if (cis_central_capable(hdev)) + settings |= MGMT_SETTING_CIS_CENTRAL; + + if (cis_peripheral_capable(hdev)) + settings |= MGMT_SETTING_CIS_PERIPHERAL; + return settings; } -- cgit 1.4.1 From 40ca66eef3d77484788ca17c59a4bffbf8d112e9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 13 Feb 2023 14:28:55 -0800 Subject: Bluetooth: MGMT: Use BIT macro when defining bitfields [ Upstream commit a80d2c545ded86d0350b9a870735565d8b749786 ] This makes use of BIT macro when defining bitfields which makes it clearer what bit it is toggling. Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 73f55453ea52 ("Bluetooth: MGMT: Fix marking SCAN_RSP as not connectable") Signed-off-by: Sasha Levin --- include/net/bluetooth/mgmt.h | 80 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index e18a927669c0..a5801649f619 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -91,26 +91,26 @@ struct mgmt_rp_read_index_list { #define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1) #define MGMT_MAX_SHORT_NAME_LENGTH (HCI_MAX_SHORT_NAME_LENGTH + 1) -#define MGMT_SETTING_POWERED 0x00000001 -#define MGMT_SETTING_CONNECTABLE 0x00000002 -#define MGMT_SETTING_FAST_CONNECTABLE 0x00000004 -#define MGMT_SETTING_DISCOVERABLE 0x00000008 -#define MGMT_SETTING_BONDABLE 0x00000010 -#define MGMT_SETTING_LINK_SECURITY 0x00000020 -#define MGMT_SETTING_SSP 0x00000040 -#define MGMT_SETTING_BREDR 0x00000080 -#define MGMT_SETTING_HS 0x00000100 -#define MGMT_SETTING_LE 0x00000200 -#define MGMT_SETTING_ADVERTISING 0x00000400 -#define MGMT_SETTING_SECURE_CONN 0x00000800 -#define MGMT_SETTING_DEBUG_KEYS 0x00001000 -#define MGMT_SETTING_PRIVACY 0x00002000 -#define MGMT_SETTING_CONFIGURATION 0x00004000 -#define MGMT_SETTING_STATIC_ADDRESS 0x00008000 -#define MGMT_SETTING_PHY_CONFIGURATION 0x00010000 -#define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000 -#define MGMT_SETTING_CIS_CENTRAL 0x00040000 -#define MGMT_SETTING_CIS_PERIPHERAL 0x00080000 +#define MGMT_SETTING_POWERED BIT(0) +#define MGMT_SETTING_CONNECTABLE BIT(1) +#define MGMT_SETTING_FAST_CONNECTABLE BIT(2) +#define MGMT_SETTING_DISCOVERABLE BIT(3) +#define MGMT_SETTING_BONDABLE BIT(4) +#define MGMT_SETTING_LINK_SECURITY BIT(5) +#define MGMT_SETTING_SSP BIT(6) +#define MGMT_SETTING_BREDR BIT(7) +#define MGMT_SETTING_HS BIT(8) +#define MGMT_SETTING_LE BIT(9) +#define MGMT_SETTING_ADVERTISING BIT(10) +#define MGMT_SETTING_SECURE_CONN BIT(11) +#define MGMT_SETTING_DEBUG_KEYS BIT(12) +#define MGMT_SETTING_PRIVACY BIT(13) +#define MGMT_SETTING_CONFIGURATION BIT(14) +#define MGMT_SETTING_STATIC_ADDRESS BIT(15) +#define MGMT_SETTING_PHY_CONFIGURATION BIT(16) +#define MGMT_SETTING_WIDEBAND_SPEECH BIT(17) +#define MGMT_SETTING_CIS_CENTRAL BIT(18) +#define MGMT_SETTING_CIS_PERIPHERAL BIT(19) #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 @@ -635,21 +635,21 @@ struct mgmt_rp_get_phy_configuration { } __packed; #define MGMT_GET_PHY_CONFIGURATION_SIZE 0 -#define MGMT_PHY_BR_1M_1SLOT 0x00000001 -#define MGMT_PHY_BR_1M_3SLOT 0x00000002 -#define MGMT_PHY_BR_1M_5SLOT 0x00000004 -#define MGMT_PHY_EDR_2M_1SLOT 0x00000008 -#define MGMT_PHY_EDR_2M_3SLOT 0x00000010 -#define MGMT_PHY_EDR_2M_5SLOT 0x00000020 -#define MGMT_PHY_EDR_3M_1SLOT 0x00000040 -#define MGMT_PHY_EDR_3M_3SLOT 0x00000080 -#define MGMT_PHY_EDR_3M_5SLOT 0x00000100 -#define MGMT_PHY_LE_1M_TX 0x00000200 -#define MGMT_PHY_LE_1M_RX 0x00000400 -#define MGMT_PHY_LE_2M_TX 0x00000800 -#define MGMT_PHY_LE_2M_RX 0x00001000 -#define MGMT_PHY_LE_CODED_TX 0x00002000 -#define MGMT_PHY_LE_CODED_RX 0x00004000 +#define MGMT_PHY_BR_1M_1SLOT BIT(0) +#define MGMT_PHY_BR_1M_3SLOT BIT(1) +#define MGMT_PHY_BR_1M_5SLOT BIT(2) +#define MGMT_PHY_EDR_2M_1SLOT BIT(3) +#define MGMT_PHY_EDR_2M_3SLOT BIT(4) +#define MGMT_PHY_EDR_2M_5SLOT BIT(5) +#define MGMT_PHY_EDR_3M_1SLOT BIT(6) +#define MGMT_PHY_EDR_3M_3SLOT BIT(7) +#define MGMT_PHY_EDR_3M_5SLOT BIT(8) +#define MGMT_PHY_LE_1M_TX BIT(9) +#define MGMT_PHY_LE_1M_RX BIT(10) +#define MGMT_PHY_LE_2M_TX BIT(11) +#define MGMT_PHY_LE_2M_RX BIT(12) +#define MGMT_PHY_LE_CODED_TX BIT(13) +#define MGMT_PHY_LE_CODED_RX BIT(14) #define MGMT_PHY_BREDR_MASK (MGMT_PHY_BR_1M_1SLOT | MGMT_PHY_BR_1M_3SLOT | \ MGMT_PHY_BR_1M_5SLOT | MGMT_PHY_EDR_2M_1SLOT | \ @@ -974,11 +974,11 @@ struct mgmt_ev_auth_failed { __u8 status; } __packed; -#define MGMT_DEV_FOUND_CONFIRM_NAME 0x01 -#define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02 -#define MGMT_DEV_FOUND_NOT_CONNECTABLE 0x04 -#define MGMT_DEV_FOUND_INITIATED_CONN 0x08 -#define MGMT_DEV_FOUND_NAME_REQUEST_FAILED 0x10 +#define MGMT_DEV_FOUND_CONFIRM_NAME BIT(0) +#define MGMT_DEV_FOUND_LEGACY_PAIRING BIT(1) +#define MGMT_DEV_FOUND_NOT_CONNECTABLE BIT(2) +#define MGMT_DEV_FOUND_INITIATED_CONN BIT(3) +#define MGMT_DEV_FOUND_NAME_REQUEST_FAILED BIT(4) #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { -- cgit 1.4.1 From c07efe4dbc12d7046fe2480c1d798c7df4f2c1be Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 7 Jun 2023 12:33:47 -0700 Subject: Bluetooth: MGMT: Fix marking SCAN_RSP as not connectable [ Upstream commit 73f55453ea5236a586a7f1b3d5e2ee051d655351 ] When receiving a scan response there is no way to know if the remote device is connectable or not, so when it cannot be merged don't make any assumption and instead just mark it with a new flag defined as MGMT_DEV_FOUND_SCAN_RSP so userspace can tell it is a standalone SCAN_RSP. Link: https://lore.kernel.org/linux-bluetooth/CABBYNZ+CYMsDSPTxBn09Js3BcdC-x7vZFfyLJ3ppZGGwJKmUTw@mail.gmail.com/ Fixes: c70a7e4cc8d2 ("Bluetooth: Add support for Not Connectable flag for Device Found events") Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/hci_event.c | 15 +++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index a5801649f619..5e68b3dd4422 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -979,6 +979,7 @@ struct mgmt_ev_auth_failed { #define MGMT_DEV_FOUND_NOT_CONNECTABLE BIT(2) #define MGMT_DEV_FOUND_INITIATED_CONN BIT(3) #define MGMT_DEV_FOUND_NAME_REQUEST_FAILED BIT(4) +#define MGMT_DEV_FOUND_SCAN_RSP BIT(5) #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 21416ccc30ab..b272cc1f3648 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6307,23 +6307,18 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, return; } - /* When receiving non-connectable or scannable undirected - * advertising reports, this means that the remote device is - * not connectable and then clearly indicate this in the - * device found event. - * - * When receiving a scan response, then there is no way to + /* When receiving a scan response, then there is no way to * know if the remote device is connectable or not. However * since scan responses are merged with a previously seen * advertising report, the flags field from that report * will be used. * - * In the really unlikely case that a controller get confused - * and just sends a scan response event, then it is marked as - * not connectable as well. + * In the unlikely case that a controller just sends a scan + * response event that doesn't match the pending report, then + * it is marked as a standalone SCAN_RSP. */ if (type == LE_ADV_SCAN_RSP) - flags = MGMT_DEV_FOUND_NOT_CONNECTABLE; + flags = MGMT_DEV_FOUND_SCAN_RSP; /* If there's nothing pending either store the data from this * event or send an immediate device found event if the data -- cgit 1.4.1 From f67ef8f9f6776e2b2073cad7c5cf29de850f83d7 Mon Sep 17 00:00:00 2001 From: Nick Child Date: Wed, 28 Jun 2023 13:22:44 -0500 Subject: ibmvnic: Do not reset dql stats on NON_FATAL err [ Upstream commit 48538ccb825b05544ec308a509e2cc9c013402db ] All ibmvnic resets, make a call to netdev_tx_reset_queue() when re-opening the device. netdev_tx_reset_queue() resets the num_queued and num_completed byte counters. These stats are used in Byte Queue Limit (BQL) algorithms. The difference between these two stats tracks the number of bytes currently sitting on the physical NIC. ibmvnic increases the number of queued bytes though calls to netdev_tx_sent_queue() in the drivers xmit function. When, VIOS reports that it is done transmitting bytes, the ibmvnic device increases the number of completed bytes through calls to netdev_tx_completed_queue(). It is important to note that the driver batches its transmit calls and num_queued is increased every time that an skb is added to the next batch, not necessarily when the batch is sent to VIOS for transmission. Unlike other reset types, a NON FATAL reset will not flush the sub crq tx buffers. Therefore, it is possible for the batched skb array to be partially full. So if there is call to netdev_tx_reset_queue() when re-opening the device, the value of num_queued (0) would not account for the skb's that are currently batched. Eventually, when the batch is sent to VIOS, the call to netdev_tx_completed_queue() would increase num_completed to a value greater than the num_queued. This causes a BUG_ON crash: ibmvnic 30000002: Firmware reports error, cause: adapter problem. Starting recovery... ibmvnic 30000002: tx error 600 ibmvnic 30000002: tx error 600 ibmvnic 30000002: tx error 600 ibmvnic 30000002: tx error 600 ------------[ cut here ]------------ kernel BUG at lib/dynamic_queue_limits.c:27! Oops: Exception in kernel mode, sig: 5 [....] NIP dql_completed+0x28/0x1c0 LR ibmvnic_complete_tx.isra.0+0x23c/0x420 [ibmvnic] Call Trace: ibmvnic_complete_tx.isra.0+0x3f8/0x420 [ibmvnic] (unreliable) ibmvnic_interrupt_tx+0x40/0x70 [ibmvnic] __handle_irq_event_percpu+0x98/0x270 ---[ end trace ]--- Therefore, do not reset the dql stats when performing a NON_FATAL reset. Fixes: 0d973388185d ("ibmvnic: Introduce xmit_more support using batched subCRQ hcalls") Signed-off-by: Nick Child Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ibm/ibmvnic.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 9282381a438f..bc97f24b0827 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1625,7 +1625,14 @@ static int __ibmvnic_open(struct net_device *netdev) if (prev_state == VNIC_CLOSED) enable_irq(adapter->tx_scrq[i]->irq); enable_scrq_irq(adapter, adapter->tx_scrq[i]); - netdev_tx_reset_queue(netdev_get_tx_queue(netdev, i)); + /* netdev_tx_reset_queue will reset dql stats. During NON_FATAL + * resets, don't reset the stats because there could be batched + * skb's waiting to be sent. If we reset dql stats, we risk + * num_completed being greater than num_queued. This will cause + * a BUG_ON in dql_completed(). + */ + if (adapter->reset_reason != VNIC_RESET_NON_FATAL) + netdev_tx_reset_queue(netdev_get_tx_queue(netdev, i)); } rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_UP); -- cgit 1.4.1 From 0b24d3e4b989caf1f4e34eba14659d2a7ac468b2 Mon Sep 17 00:00:00 2001 From: Pawel Dembicki Date: Wed, 28 Jun 2023 21:43:27 +0200 Subject: net: dsa: vsc73xx: fix MTU configuration [ Upstream commit 3cf62c8177adb0db9e15c8b898c44f997acf3ebf ] Switch in MAXLEN register stores the maximum size of a data frame. The MTU size is 18 bytes smaller than the frame size. The current settings are causing problems with packet forwarding. This patch fixes the MTU settings to proper values. Fixes: fb77ffc6ec86 ("net: dsa: vsc73xx: make the MTU configurable") Reviewed-by: Linus Walleij Signed-off-by: Pawel Dembicki Reviewed-by: Vladimir Oltean Link: https://lore.kernel.org/r/20230628194327.1765644-1-paweldembicki@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/vitesse-vsc73xx-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index ae55167ce0a6..ef1a4a7c47b2 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -1025,17 +1025,17 @@ static int vsc73xx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) struct vsc73xx *vsc = ds->priv; return vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, - VSC73XX_MAXLEN, new_mtu); + VSC73XX_MAXLEN, new_mtu + ETH_HLEN + ETH_FCS_LEN); } /* According to application not "VSC7398 Jumbo Frames" setting - * up the MTU to 9.6 KB does not affect the performance on standard + * up the frame size to 9.6 KB does not affect the performance on standard * frames. It is clear from the application note that * "9.6 kilobytes" == 9600 bytes. */ static int vsc73xx_get_max_mtu(struct dsa_switch *ds, int port) { - return 9600; + return 9600 - ETH_HLEN - ETH_FCS_LEN; } static const struct dsa_switch_ops vsc73xx_ds_ops = { -- cgit 1.4.1 From d4f5b1dd816dccd4ee6bb60b2a81a3d4373636a9 Mon Sep 17 00:00:00 2001 From: Zhengchao Shao Date: Fri, 30 Jun 2023 09:26:47 +0800 Subject: mlxsw: minimal: fix potential memory leak in mlxsw_m_linecards_init [ Upstream commit 08fc75735fda3be97194bfbf3c899c87abb3d0fe ] The line cards array is not freed in the error path of mlxsw_m_linecards_init(), which can lead to a memory leak. Fix by freeing the array in the error path, thereby making the error path identical to mlxsw_m_linecards_fini(). Fixes: 01328e23a476 ("mlxsw: minimal: Extend module to port mapping with slot index") Signed-off-by: Zhengchao Shao Reviewed-by: Petr Machata Reviewed-by: Ido Schimmel Link: https://lore.kernel.org/r/20230630012647.1078002-1-shaozhengchao@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlxsw/minimal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 55b3c42bb007..15116d9305f8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -430,6 +430,7 @@ static int mlxsw_m_linecards_init(struct mlxsw_m *mlxsw_m) err_kmalloc_array: for (i--; i >= 0; i--) kfree(mlxsw_m->line_cards[i]); + kfree(mlxsw_m->line_cards); err_kcalloc: kfree(mlxsw_m->ports); return err; -- cgit 1.4.1 From d3dcdb43c872a3b967345144151a2c9bb9124c9b Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Thu, 29 Jun 2023 15:43:05 +0200 Subject: spi: bcm-qspi: return error if neither hif_mspi nor mspi is available [ Upstream commit 7c1f23ad34fcdace50275a6aa1e1969b41c6233f ] If neither a "hif_mspi" nor "mspi" resource is present, the driver will just early exit in probe but still return success. Apart from not doing anything meaningful, this would then also lead to a null pointer access on removal, as platform_get_drvdata() would return NULL, which it would then try to dereference when trying to unregister the spi master. Fix this by unconditionally calling devm_ioremap_resource(), as it can handle a NULL res and will then return a viable ERR_PTR() if we get one. The "return 0;" was previously a "goto qspi_resource_err;" where then ret was returned, but since ret was still initialized to 0 at this place this was a valid conversion in 63c5395bb7a9 ("spi: bcm-qspi: Fix use-after-free on unbind"). The issue was not introduced by this commit, only made more obvious. Fixes: fa236a7ef240 ("spi: bcm-qspi: Add Broadcom MSPI driver") Signed-off-by: Jonas Gorski Reviewed-by: Kamal Dasu Link: https://lore.kernel.org/r/20230629134306.95823-1-jonas.gorski@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-bcm-qspi.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index cad2d55dcd3d..137e7315a3cf 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1543,13 +1543,9 @@ int bcm_qspi_probe(struct platform_device *pdev, res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mspi"); - if (res) { - qspi->base[MSPI] = devm_ioremap_resource(dev, res); - if (IS_ERR(qspi->base[MSPI])) - return PTR_ERR(qspi->base[MSPI]); - } else { - return 0; - } + qspi->base[MSPI] = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->base[MSPI])) + return PTR_ERR(qspi->base[MSPI]); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi"); if (res) { -- cgit 1.4.1 From 0d4e60e23c7d6a54f80e1b8ceec9a8c3df736dad Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 20 Jun 2023 13:18:13 +0200 Subject: drm/amdgpu: fix number of fence calculations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 570b295248b00c3cf4cf59e397de5cb2361e10c2 ] Since adding gang submit we need to take the gang size into account while reserving fences. Signed-off-by: Christian König Fixes: 4624459c84d7 ("drm/amdgpu: add gang submit frontend v6") Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 365e3fb6a9e5..b60b6e6149bf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -129,9 +129,6 @@ static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p, bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); p->uf_entry.priority = 0; p->uf_entry.tv.bo = &bo->tbo; - /* One for TTM and two for the CS job */ - p->uf_entry.tv.num_shared = 3; - drm_gem_object_put(gobj); size = amdgpu_bo_size(bo); @@ -883,15 +880,19 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, mutex_lock(&p->bo_list->bo_list_mutex); - /* One for TTM and one for the CS job */ + /* One for TTM and one for each CS job */ amdgpu_bo_list_for_each_entry(e, p->bo_list) - e->tv.num_shared = 2; + e->tv.num_shared = 1 + p->gang_size; + p->uf_entry.tv.num_shared = 1 + p->gang_size; amdgpu_bo_list_get_list(p->bo_list, &p->validated); INIT_LIST_HEAD(&duplicates); amdgpu_vm_get_pd_bo(&fpriv->vm, &p->validated, &p->vm_pd); + /* Two for VM updates, one for TTM and one for each CS job */ + p->vm_pd.tv.num_shared = 3 + p->gang_size; + if (p->uf_entry.tv.bo && !ttm_to_amdgpu_bo(p->uf_entry.tv.bo)->parent) list_add(&p->uf_entry.tv.head, &p->validated); -- cgit 1.4.1 From 4033b47642c7e2956bb556f2dd953b5e9e47d927 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 22 Jun 2023 22:18:39 -0500 Subject: drm/amd: Don't try to enable secure display TA multiple times [ Upstream commit 5c6d52ff4b61e5267b25be714eb5a9ba2a338199 ] If the securedisplay TA failed to load the first time, it's unlikely to work again after a suspend/resume cycle or reset cycle and it appears to be causing problems in futher attempts. Fixes: e42dfa66d592 ("drm/amdgpu: Add secure display TA load for Renoir") Reported-by: Filip Hejsek Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/2633 Signed-off-by: Mario Limonciello Acked-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index a3cd816f98a1..0af9fb4098e8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -1959,6 +1959,8 @@ static int psp_securedisplay_initialize(struct psp_context *psp) psp_securedisplay_parse_resp_status(psp, securedisplay_cmd->status); dev_err(psp->adev->dev, "SECUREDISPLAY: query securedisplay TA failed. ret 0x%x\n", securedisplay_cmd->securedisplay_out_message.query_ta.query_cmd_ret); + /* don't try again */ + psp->securedisplay_context.context.bin_desc.size_bytes = 0; } return 0; -- cgit 1.4.1 From cfdb9c1a74d8394fab6ef4b27a5f4d4af8338a83 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Tue, 20 Jun 2023 20:00:22 -0500 Subject: mailbox: ti-msgmgr: Fill non-message tx data fields with 0x0 [ Upstream commit 1b712f18c461bd75f018033a15cf381e712806b5 ] Sec proxy/message manager data buffer is 60 bytes with the last of the registers indicating transmission completion. This however poses a bit of a challenge. The backing memory for sec_proxy / message manager is regular memory, and all sec proxy does is to trigger a burst of all 60 bytes of data over to the target thread backing ring accelerator. It doesn't do a memory scrub when it moves data out in the burst. When we transmit multiple messages, remnants of previous message is also transmitted which results in some random data being set in TISCI fields of messages that have been expanded forward. The entire concept of backward compatibility hinges on the fact that the unused message fields remain 0x0 allowing for 0x0 value to be specially considered when backward compatibility of message extension is done. So, instead of just writing the completion register, we continue to fill the message buffer up with 0x0 (note: for partial message involving completion, we already do this). This allows us to scale and introduce ABI changes back also work with other boot stages that may have left data in the internal memory. While at this, be consistent and explicit with the data_reg pointer increment. Fixes: aace66b170ce ("mailbox: Introduce TI message manager driver") Signed-off-by: Nishanth Menon Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/ti-msgmgr.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index ddac423ac1a9..03048cbda525 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -430,14 +430,20 @@ static int ti_msgmgr_send_data(struct mbox_chan *chan, void *data) /* Ensure all unused data is 0 */ data_trail &= 0xFFFFFFFF >> (8 * (sizeof(u32) - trail_bytes)); writel(data_trail, data_reg); - data_reg++; + data_reg += sizeof(u32); } + /* * 'data_reg' indicates next register to write. If we did not already * write on tx complete reg(last reg), we must do so for transmit + * In addition, we also need to make sure all intermediate data + * registers(if any required), are reset to 0 for TISCI backward + * compatibility to be maintained. */ - if (data_reg <= qinst->queue_buff_end) - writel(0, qinst->queue_buff_end); + while (data_reg <= qinst->queue_buff_end) { + writel(0, data_reg); + data_reg += sizeof(u32); + } /* If we are in polled mode, wait for a response before proceeding */ if (ti_msgmgr_chan_has_polled_queue_rx(message->chan_rx)) -- cgit 1.4.1 From c2c5c6d2c474eb771b35322d8eeac0905efd14ff Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 29 Jun 2023 09:41:02 +0800 Subject: f2fs: fix error path handling in truncate_dnode() [ Upstream commit 0135c482fa97e2fd8245cb462784112a00ed1211 ] If truncate_node() fails in truncate_dnode(), it missed to call f2fs_put_page(), fix it. Fixes: 7735730d39d7 ("f2fs: fix to propagate error from __get_meta_page()") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin --- fs/f2fs/node.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 07419c3e42a5..a010b4bc36d2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -941,8 +941,10 @@ static int truncate_dnode(struct dnode_of_data *dn) dn->ofs_in_node = 0; f2fs_truncate_data_blocks(dn); err = truncate_node(dn); - if (err) + if (err) { + f2fs_put_page(page, 1); return err; + } return 1; } -- cgit 1.4.1 From bd246c92d2d53a37e930206a096be23f46532500 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Fri, 30 Jun 2023 11:58:43 +0530 Subject: octeontx2-af: Fix mapping for NIX block from CGX connection [ Upstream commit 2e7bc57b976bb016c6569a54d95c1b8d88f9450a ] Firmware configures NIX block mapping for all MAC blocks. The current implementation reads the configuration and creates the mapping between RVU PF and NIX blocks. But this configuration is only valid for silicons that support multiple blocks. For all other silicons, all MAC blocks map to NIX0. This patch corrects the mapping by adding a check for the same. Fixes: c5a73b632b90 ("octeontx2-af: Map NIX block from CGX connection") Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Goutham Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu.h | 11 +++++++++++ drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index d493b533cf76..a3346ea7876c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -23,6 +23,7 @@ #define PCI_DEVID_OCTEONTX2_LBK 0xA061 /* Subsystem Device ID */ +#define PCI_SUBSYS_DEVID_98XX 0xB100 #define PCI_SUBSYS_DEVID_96XX 0xB200 #define PCI_SUBSYS_DEVID_CN10K_A 0xB900 #define PCI_SUBSYS_DEVID_CNF10K_B 0xBC00 @@ -646,6 +647,16 @@ static inline u16 rvu_nix_chan_cpt(struct rvu *rvu, u8 chan) return rvu->hw->cpt_chan_base + chan; } +static inline bool is_rvu_supports_nix1(struct rvu *rvu) +{ + struct pci_dev *pdev = rvu->pdev; + + if (pdev->subsystem_device == PCI_SUBSYS_DEVID_98XX) + return true; + + return false; +} + /* Function Prototypes * RVU */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 9eca38547b78..e284a14ca120 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -112,7 +112,7 @@ static void rvu_map_cgx_nix_block(struct rvu *rvu, int pf, p2x = cgx_lmac_get_p2x(cgx_id, lmac_id); /* Firmware sets P2X_SELECT as either NIX0 or NIX1 */ pfvf->nix_blkaddr = BLKADDR_NIX0; - if (p2x == CMR_P2X_SEL_NIX1) + if (is_rvu_supports_nix1(rvu) && p2x == CMR_P2X_SEL_NIX1) pfvf->nix_blkaddr = BLKADDR_NIX1; } -- cgit 1.4.1 From a5485a943193e55c79150382e6461e8ea759e96e Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Fri, 30 Jun 2023 11:58:44 +0530 Subject: octeontx2-af: Add validation before accessing cgx and lmac [ Upstream commit 79ebb53772c95d3a6ae51b3c65f9985fdd430df6 ] with the addition of new MAC blocks like CN10K RPM and CN10KB RPM_USX, LMACs are noncontiguous and CGX blocks are also noncontiguous. But during RVU driver initialization, the driver is assuming they are contiguous and trying to access cgx or lmac with their id which is resulting in kernel panic. This patch fixes the issue by adding proper checks. [ 23.219150] pc : cgx_lmac_read+0x38/0x70 [ 23.219154] lr : rvu_program_channels+0x3f0/0x498 [ 23.223852] sp : ffff000100d6fc80 [ 23.227158] x29: ffff000100d6fc80 x28: ffff00010009f880 x27: 000000000000005a [ 23.234288] x26: ffff000102586768 x25: 0000000000002500 x24: fffffffffff0f000 Fixes: 91c6945ea1f9 ("octeontx2-af: cn10k: Add RPM MAC support") Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Goutham Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index afdddfced7e6..65c0373d34d1 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -167,6 +167,9 @@ void cgx_lmac_write(int cgx_id, int lmac_id, u64 offset, u64 val) { struct cgx *cgx_dev = cgx_get_pdata(cgx_id); + /* Software must not access disabled LMAC registers */ + if (!is_lmac_valid(cgx_dev, lmac_id)) + return; cgx_write(cgx_dev, lmac_id, offset, val); } @@ -174,6 +177,10 @@ u64 cgx_lmac_read(int cgx_id, int lmac_id, u64 offset) { struct cgx *cgx_dev = cgx_get_pdata(cgx_id); + /* Software must not access disabled LMAC registers */ + if (!is_lmac_valid(cgx_dev, lmac_id)) + return 0; + return cgx_read(cgx_dev, lmac_id, offset); } -- cgit 1.4.1 From c86a2517df6c9304db8fb12b77136ec7a5d85994 Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Thu, 8 Dec 2022 00:28:07 +0800 Subject: ntfs: Fix panic about slab-out-of-bounds caused by ntfs_listxattr() [ Upstream commit 3c675ddffb17a8b1e32efad5c983254af18b12c2 ] Here is a BUG report from syzbot: BUG: KASAN: slab-out-of-bounds in ntfs_list_ea fs/ntfs3/xattr.c:191 [inline] BUG: KASAN: slab-out-of-bounds in ntfs_listxattr+0x401/0x570 fs/ntfs3/xattr.c:710 Read of size 1 at addr ffff888021acaf3d by task syz-executor128/3632 Call Trace: ntfs_list_ea fs/ntfs3/xattr.c:191 [inline] ntfs_listxattr+0x401/0x570 fs/ntfs3/xattr.c:710 vfs_listxattr fs/xattr.c:457 [inline] listxattr+0x293/0x2d0 fs/xattr.c:804 Fix the logic of ea_all iteration. When the ea->name_len is 0, return immediately, or Add2Ptr() would visit invalid memory in the next loop. Fixes: be71b5cba2e6 ("fs/ntfs3: Add attrib operations") Reported-by: syzbot+9fcea5ef6dc4dc72d334@syzkaller.appspotmail.com Signed-off-by: Zeng Heng [almaz.alexandrovich@paragon-software.com: lines of the patch have changed] Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/xattr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index ea582b4fe1d9..88866bcd1a21 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -178,6 +178,9 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, for (ret = 0, off = 0; off < size; off += unpacked_ea_size(ea)) { ea = Add2Ptr(ea_all, off); + if (!ea->name_len) + break; + if (buffer) { if (ret + ea->name_len + 1 > bytes_per_buffer) { err = -ERANGE; -- cgit 1.4.1 From d33b0ddf7afa7fa71ba3a8d0f8b1948dad003257 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 30 Jun 2023 22:47:12 -0700 Subject: powerpc: allow PPC_EARLY_DEBUG_CPM only when SERIAL_CPM=y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 39f49684036d24af800ff194c33c7b2653c591d7 ] In a randconfig with CONFIG_SERIAL_CPM=m and CONFIG_PPC_EARLY_DEBUG_CPM=y, there is a build error: ERROR: modpost: "udbg_putc" [drivers/tty/serial/cpm_uart/cpm_uart.ko] undefined! Prevent the build error by allowing PPC_EARLY_DEBUG_CPM only when SERIAL_CPM=y. Fixes: c374e00e17f1 ("[POWERPC] Add early debug console for CPM serial ports.") Signed-off-by: Randy Dunlap Reviewed-by: Pali Rohár Reviewed-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://msgid.link/20230701054714.30512-1-rdunlap@infradead.org Signed-off-by: Sasha Levin --- arch/powerpc/Kconfig.debug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 6aaf8dc60610..2a54fadbeaf5 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -240,7 +240,7 @@ config PPC_EARLY_DEBUG_40x config PPC_EARLY_DEBUG_CPM bool "Early serial debugging for Freescale CPM-based serial ports" - depends on SERIAL_CPM + depends on SERIAL_CPM=y help Select this to enable early debugging for Freescale chips using a CPM-based serial port. This assumes that the bootwrapper -- cgit 1.4.1 From d50baa75c689d0e10b93547378dd4e039b0f6da1 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 5 May 2023 19:28:18 +0200 Subject: powerpc: dts: turris1x.dts: Fix PCIe MEM size for pci2 node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit abaa02fc944f2f9f2c2e1925ddaceaf35c48528c ] Freescale PCIe controllers on their PCIe Root Ports do not have any mappable PCI BAR allocate from PCIe MEM. Information about 1MB window on BAR0 of PCIe Root Port was misleading because Freescale PCIe controllers have at BAR0 position different register PEXCSRBAR, and kernel correctly skipts BAR0 for these Freescale PCIe Root Ports. So update comment about P2020 PCIe Root Port and decrease PCIe MEM size required for PCIe controller (pci2 node) on which is on-board xHCI controller. lspci confirms that on P2020 PCIe Root Port is no PCI BAR and /proc/iomem sees that only c0000000-c000ffff and c0010000-c0011fff ranges are used. Fixes: 54c15ec3b738 ("powerpc: dts: Add DTS file for CZ.NIC Turris 1.x routers") Signed-off-by: Pali Rohár Signed-off-by: Michael Ellerman Link: https://msgid.link/20230505172818.18416-1-pali@kernel.org Signed-off-by: Sasha Levin --- arch/powerpc/boot/dts/turris1x.dts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/boot/dts/turris1x.dts b/arch/powerpc/boot/dts/turris1x.dts index e9cda34a140e..9377055d5565 100644 --- a/arch/powerpc/boot/dts/turris1x.dts +++ b/arch/powerpc/boot/dts/turris1x.dts @@ -453,12 +453,12 @@ * channel 1 (but only USB 2.0 subset) to USB 2.0 pins on mPCIe * slot 1 (CN5), channels 2 and 3 to connector P600. * - * P2020 PCIe Root Port uses 1MB of PCIe MEM and xHCI controller + * P2020 PCIe Root Port does not use PCIe MEM and xHCI controller * uses 64kB + 8kB of PCIe MEM. No PCIe IO is used or required. - * So allocate 2MB of PCIe MEM for this PCIe bus. + * So allocate 128kB of PCIe MEM for this PCIe bus. */ reg = <0 0xffe08000 0 0x1000>; - ranges = <0x02000000 0x0 0xc0000000 0 0xc0000000 0x0 0x00200000>, /* MEM */ + ranges = <0x02000000 0x0 0xc0000000 0 0xc0000000 0x0 0x00020000>, /* MEM */ <0x01000000 0x0 0x00000000 0 0xffc20000 0x0 0x00010000>; /* IO */ pcie@0 { -- cgit 1.4.1 From fd03500476c80b80de89031d4beb398fbe1e4f29 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 30 Jun 2023 19:41:18 +0300 Subject: net: bridge: keep ports without IFF_UNICAST_FLT in BR_PROMISC mode [ Upstream commit 6ca3c005d0604e8d2b439366e3923ea58db99641 ] According to the synchronization rules for .ndo_get_stats() as seen in Documentation/networking/netdevices.rst, acquiring a plain spin_lock() should not be illegal, but the bridge driver implementation makes it so. After running these commands, I am being faced with the following lockdep splat: $ ip link add link swp0 name macsec0 type macsec encrypt on && ip link set swp0 up $ ip link add dev br0 type bridge vlan_filtering 1 && ip link set br0 up $ ip link set macsec0 master br0 && ip link set macsec0 up ======================================================== WARNING: possible irq lock inversion dependency detected 6.4.0-04295-g31b577b4bd4a #603 Not tainted -------------------------------------------------------- swapper/1/0 just changed the state of lock: ffff6bd348724cd8 (&br->lock){+.-.}-{3:3}, at: br_forward_delay_timer_expired+0x34/0x198 but this lock took another, SOFTIRQ-unsafe lock in the past: (&ocelot->stats_lock){+.+.}-{3:3} and interrupts could create inverse lock ordering between them. other info that might help us debug this: Chain exists of: &br->lock --> &br->hash_lock --> &ocelot->stats_lock Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&ocelot->stats_lock); local_irq_disable(); lock(&br->lock); lock(&br->hash_lock); lock(&br->lock); *** DEADLOCK *** (details about the 3 locks skipped) swp0 is instantiated by drivers/net/dsa/ocelot/felix.c, and this only matters to the extent that its .ndo_get_stats64() method calls spin_lock(&ocelot->stats_lock). Documentation/locking/lockdep-design.rst says: | A lock is irq-safe means it was ever used in an irq context, while a lock | is irq-unsafe means it was ever acquired with irq enabled. (...) | Furthermore, the following usage based lock dependencies are not allowed | between any two lock-classes:: | | -> | -> Lockdep marks br->hash_lock as softirq-safe, because it is sometimes taken in softirq context (for example br_fdb_update() which runs in NET_RX softirq), and when it's not in softirq context it blocks softirqs by using spin_lock_bh(). Lockdep marks ocelot->stats_lock as softirq-unsafe, because it never blocks softirqs from running, and it is never taken from softirq context. So it can always be interrupted by softirqs. There is a call path through which a function that holds br->hash_lock: fdb_add_hw_addr() will call a function that acquires ocelot->stats_lock: ocelot_port_get_stats64(). This can be seen below: ocelot_port_get_stats64+0x3c/0x1e0 felix_get_stats64+0x20/0x38 dsa_slave_get_stats64+0x3c/0x60 dev_get_stats+0x74/0x2c8 rtnl_fill_stats+0x4c/0x150 rtnl_fill_ifinfo+0x5cc/0x7b8 rtmsg_ifinfo_build_skb+0xe4/0x150 rtmsg_ifinfo+0x5c/0xb0 __dev_notify_flags+0x58/0x200 __dev_set_promiscuity+0xa0/0x1f8 dev_set_promiscuity+0x30/0x70 macsec_dev_change_rx_flags+0x68/0x88 __dev_set_promiscuity+0x1a8/0x1f8 __dev_set_rx_mode+0x74/0xa8 dev_uc_add+0x74/0xa0 fdb_add_hw_addr+0x68/0xd8 fdb_add_local+0xc4/0x110 br_fdb_add_local+0x54/0x88 br_add_if+0x338/0x4a0 br_add_slave+0x20/0x38 do_setlink+0x3a4/0xcb8 rtnl_newlink+0x758/0x9d0 rtnetlink_rcv_msg+0x2f0/0x550 netlink_rcv_skb+0x128/0x148 rtnetlink_rcv+0x24/0x38 the plain English explanation for it is: The macsec0 bridge port is created without p->flags & BR_PROMISC, because it is what br_manage_promisc() decides for a VLAN filtering bridge with a single auto port. As part of the br_add_if() procedure, br_fdb_add_local() is called for the MAC address of the device, and this results in a call to dev_uc_add() for macsec0 while the softirq-safe br->hash_lock is taken. Because macsec0 does not have IFF_UNICAST_FLT, dev_uc_add() ends up calling __dev_set_promiscuity() for macsec0, which is propagated by its implementation, macsec_dev_change_rx_flags(), to the lower device: swp0. This triggers the call path: dev_set_promiscuity(swp0) -> rtmsg_ifinfo() -> dev_get_stats() -> ocelot_port_get_stats64() with a calling context that lockdep doesn't like (br->hash_lock held). Normally we don't see this, because even though many drivers that can be bridge ports don't support IFF_UNICAST_FLT, we need a driver that (a) doesn't support IFF_UNICAST_FLT, *and* (b) it forwards the IFF_PROMISC flag to another driver, and (c) *that* driver implements ndo_get_stats64() using a softirq-unsafe spinlock. Condition (b) is necessary because the first __dev_set_rx_mode() calls __dev_set_promiscuity() with "bool notify=false", and thus, the rtmsg_ifinfo() code path won't be entered. The same criteria also hold true for DSA switches which don't report IFF_UNICAST_FLT. When the DSA master uses a spin_lock() in its ndo_get_stats64() method, the same lockdep splat can be seen. I think the deadlock possibility is real, even though I didn't reproduce it, and I'm thinking of the following situation to support that claim: fdb_add_hw_addr() runs on a CPU A, in a context with softirqs locally disabled and br->hash_lock held, and may end up attempting to acquire ocelot->stats_lock. In parallel, ocelot->stats_lock is currently held by a thread B (say, ocelot_check_stats_work()), which is interrupted while holding it by a softirq which attempts to lock br->hash_lock. Thread B cannot make progress because br->hash_lock is held by A. Whereas thread A cannot make progress because ocelot->stats_lock is held by B. When taking the issue at face value, the bridge can avoid that problem by simply making the ports promiscuous from a code path with a saner calling context (br->hash_lock not held). A bridge port without IFF_UNICAST_FLT is going to become promiscuous as soon as we call dev_uc_add() on it (which we do unconditionally), so why not be preemptive and make it promiscuous right from the beginning, so as to not be taken by surprise. With this, we've broken the links between code that holds br->hash_lock or br->lock and code that calls into the ndo_change_rx_flags() or ndo_get_stats64() ops of the bridge port. Fixes: 2796d0c648c9 ("bridge: Automatically manage port promiscuous mode.") Signed-off-by: Vladimir Oltean Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/bridge/br_if.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 228fd5b20f10..0989074f316e 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -166,8 +166,9 @@ void br_manage_promisc(struct net_bridge *br) * This lets us disable promiscuous mode and write * this config to hw. */ - if (br->auto_cnt == 0 || - (br->auto_cnt == 1 && br_auto_port(p))) + if ((p->dev->priv_flags & IFF_UNICAST_FLT) && + (br->auto_cnt == 0 || + (br->auto_cnt == 1 && br_auto_port(p)))) br_port_clear_promisc(p); else br_port_set_promisc(p); -- cgit 1.4.1 From 6469dc1c138897d55180d05c15328daa2d4606be Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 1 Jul 2023 01:20:10 +0300 Subject: net: dsa: tag_sja1105: fix source port decoding in vlan_filtering=0 bridge mode [ Upstream commit a398b9ea0c3b791b7a0f4c6029a62cf628f97f22 ] There was a regression introduced by the blamed commit, where pinging to a VLAN-unaware bridge would fail with the repeated message "Couldn't decode source port" coming from the tagging protocol driver. When receiving packets with a bridge_vid as determined by dsa_tag_8021q_bridge_join(), dsa_8021q_rcv() will decode: - source_port = 0 (which isn't really valid, more like "don't know") - switch_id = 0 (which isn't really valid, more like "don't know") - vbid = value in range 1-7 Since the blamed patch has reversed the order of the checks, we are now going to believe that source_port != -1 and switch_id != -1, so they're valid, but they aren't. The minimal solution to the problem is to only populate source_port and switch_id with what dsa_8021q_rcv() came up with, if the vbid is zero, i.e. the source port information is trustworthy. Fixes: c1ae02d87689 ("net: dsa: tag_sja1105: always prefer source port information from INCL_SRCPT") Signed-off-by: Vladimir Oltean Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/dsa/tag_sja1105.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 731dc3a111ef..6e3699d859db 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -568,11 +568,14 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, * if available. This allows us to not overwrite a valid source * port and switch ID with zeroes when receiving link-local * frames from a VLAN-unaware bridged port (non-zero vbid) or a - * VLAN-aware bridged port (non-zero vid). + * VLAN-aware bridged port (non-zero vid). Furthermore, the + * tag_8021q source port information is only of trust when the + * vbid is 0 (precise port). Otherwise, tmp_source_port and + * tmp_switch_id will be zeroes. */ - if (source_port == -1) + if (vbid == 0 && source_port == -1) source_port = tmp_source_port; - if (switch_id == -1) + if (vbid == 0 && switch_id == -1) switch_id = tmp_switch_id; } else if (source_port == -1 && switch_id == -1) { /* Packets with no source information have no chance of -- cgit 1.4.1 From ced61418f46993d571385812bafed3a7d4ab6918 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 1 Jul 2023 02:48:24 +0000 Subject: net: fix net_dev_start_xmit trace event vs skb_transport_offset() [ Upstream commit f88fcb1d7d961b4b402d675109726f94db87571c ] After blamed commit, we must be more careful about using skb_transport_offset(), as reminded us by syzbot: WARNING: CPU: 0 PID: 10 at include/linux/skbuff.h:2868 skb_transport_offset include/linux/skbuff.h:2977 [inline] WARNING: CPU: 0 PID: 10 at include/linux/skbuff.h:2868 perf_trace_net_dev_start_xmit+0x89a/0xce0 include/trace/events/net.h:14 Modules linked in: CPU: 0 PID: 10 Comm: kworker/u4:1 Not tainted 6.1.30-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/27/2023 Workqueue: bat_events batadv_iv_send_outstanding_bat_ogm_packet RIP: 0010:skb_transport_header include/linux/skbuff.h:2868 [inline] RIP: 0010:skb_transport_offset include/linux/skbuff.h:2977 [inline] RIP: 0010:perf_trace_net_dev_start_xmit+0x89a/0xce0 include/trace/events/net.h:14 Code: 8b 04 25 28 00 00 00 48 3b 84 24 c0 00 00 00 0f 85 4e 04 00 00 48 8d 65 d8 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc e8 56 22 01 fd <0f> 0b e9 f6 fc ff ff 89 f9 80 e1 07 80 c1 03 38 c1 0f 8c 86 f9 ff RSP: 0018:ffffc900002bf700 EFLAGS: 00010293 RAX: ffffffff8485d8ca RBX: 000000000000ffff RCX: ffff888100914280 RDX: 0000000000000000 RSI: 000000000000ffff RDI: 000000000000ffff RBP: ffffc900002bf818 R08: ffffffff8485d5b6 R09: fffffbfff0f8fb5e R10: 0000000000000000 R11: dffffc0000000001 R12: 1ffff110217d8f67 R13: ffff88810bec7b3a R14: dffffc0000000000 R15: dffffc0000000000 FS: 0000000000000000(0000) GS:ffff8881f6a00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f96cf6d52f0 CR3: 000000012224c000 CR4: 0000000000350ef0 Call Trace: [] trace_net_dev_start_xmit include/trace/events/net.h:14 [inline] [] xmit_one net/core/dev.c:3643 [inline] [] dev_hard_start_xmit+0x705/0x980 net/core/dev.c:3660 [] __dev_queue_xmit+0x16b2/0x3370 net/core/dev.c:4324 [] dev_queue_xmit include/linux/netdevice.h:3030 [inline] [] batadv_send_skb_packet+0x3f3/0x680 net/batman-adv/send.c:108 [] batadv_send_broadcast_skb+0x24/0x30 net/batman-adv/send.c:127 [] batadv_iv_ogm_send_to_if net/batman-adv/bat_iv_ogm.c:393 [inline] [] batadv_iv_ogm_emit net/batman-adv/bat_iv_ogm.c:421 [inline] [] batadv_iv_send_outstanding_bat_ogm_packet+0x69a/0x840 net/batman-adv/bat_iv_ogm.c:1701 [] process_one_work+0x8ac/0x1170 kernel/workqueue.c:2289 [] worker_thread+0xaa8/0x12d0 kernel/workqueue.c:2436 Fixes: 66e4c8d95008 ("net: warn if transport header was not set") Signed-off-by: Eric Dumazet Reported-by: syzbot Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/trace/events/net.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/trace/events/net.h b/include/trace/events/net.h index da611a7aaf97..f667c76a3b02 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -51,7 +51,8 @@ TRACE_EVENT(net_dev_start_xmit, __entry->network_offset = skb_network_offset(skb); __entry->transport_offset_valid = skb_transport_header_was_set(skb); - __entry->transport_offset = skb_transport_offset(skb); + __entry->transport_offset = skb_transport_header_was_set(skb) ? + skb_transport_offset(skb) : 0; __entry->tx_flags = skb_shinfo(skb)->tx_flags; __entry->gso_size = skb_shinfo(skb)->gso_size; __entry->gso_segs = skb_shinfo(skb)->gso_segs; -- cgit 1.4.1 From cd398daabeb807ba8e2083f147a5fa4f63cc2ef4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 29 Jun 2023 16:41:50 +0000 Subject: tcp: annotate data races in __tcp_oow_rate_limited() [ Upstream commit 998127cdb4699b9d470a9348ffe9f1154346be5f ] request sockets are lockless, __tcp_oow_rate_limited() could be called on the same object from different cpus. This is harmless. Add READ_ONCE()/WRITE_ONCE() annotations to avoid a KCSAN report. Fixes: 4ce7e93cb3fe ("tcp: rate limit ACK sent by SYN_RECV request sockets") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/ipv4/tcp_input.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5dabb38b857f..e2d3ea2e3456 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3590,8 +3590,11 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 static bool __tcp_oow_rate_limited(struct net *net, int mib_idx, u32 *last_oow_ack_time) { - if (*last_oow_ack_time) { - s32 elapsed = (s32)(tcp_jiffies32 - *last_oow_ack_time); + /* Paired with the WRITE_ONCE() in this function. */ + u32 val = READ_ONCE(*last_oow_ack_time); + + if (val) { + s32 elapsed = (s32)(tcp_jiffies32 - val); if (0 <= elapsed && elapsed < READ_ONCE(net->ipv4.sysctl_tcp_invalid_ratelimit)) { @@ -3600,7 +3603,10 @@ static bool __tcp_oow_rate_limited(struct net *net, int mib_idx, } } - *last_oow_ack_time = tcp_jiffies32; + /* Paired with the prior READ_ONCE() and with itself, + * as we might be lockless. + */ + WRITE_ONCE(*last_oow_ack_time, tcp_jiffies32); return false; /* not rate-limited: go ahead, send dupack now! */ } -- cgit 1.4.1 From 6baa6e4836d75a5d693c18dbf79b0695f05697e9 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Sat, 1 Jul 2023 17:14:47 +0000 Subject: bpf, btf: Warn but return no error for NULL btf from __register_btf_kfunc_id_set() [ Upstream commit 3de4d22cc9ac7c9f38e10edcf54f9a8891a9c2aa ] __register_btf_kfunc_id_set() assumes .BTF to be part of the module's .ko file if CONFIG_DEBUG_INFO_BTF is enabled. If that's not the case, the function prints an error message and return an error. As a result, such modules cannot be loaded. However, the section could be stripped out during a build process. It would be better to let the modules loaded, because their basic functionalities have no problem [0], though the BTF functionalities will not be supported. Make the function to lower the level of the message from error to warn, and return no error. [0] https://lore.kernel.org/bpf/20220219082037.ow2kbq5brktf4f2u@apollo.legion Fixes: c446fdacb10d ("bpf: fix register_btf_kfunc_id_set for !CONFIG_DEBUG_INFO_BTF") Reported-by: Alexander Egorenkov Suggested-by: Kumar Kartikeya Dwivedi Signed-off-by: SeongJae Park Signed-off-by: Daniel Borkmann Acked-by: Jiri Olsa Link: https://lore.kernel.org/bpf/87y228q66f.fsf@oc8242746057.ibm.com Link: https://lore.kernel.org/bpf/20220219082037.ow2kbq5brktf4f2u@apollo.legion Link: https://lore.kernel.org/bpf/20230701171447.56464-1-sj@kernel.org Signed-off-by: Sasha Levin --- kernel/bpf/btf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 8220caa488c5..fb78bb26786f 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -7469,10 +7469,8 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, pr_err("missing vmlinux BTF, cannot register kfuncs\n"); return -ENOENT; } - if (kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) { - pr_err("missing module BTF, cannot register kfuncs\n"); - return -ENOENT; - } + if (kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) + pr_warn("missing module BTF, cannot register kfuncs\n"); return 0; } if (IS_ERR(btf)) -- cgit 1.4.1 From 67eb4aee2c01b8fe51a899344bedc003c7930379 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 3 Jul 2023 19:53:29 +0200 Subject: xsk: Honor SO_BINDTODEVICE on bind [ Upstream commit f7306acec9aae9893d15e745c8791124d42ab10a ] Initial creation of an AF_XDP socket requires CAP_NET_RAW capability. A privileged process might create the socket and pass it to a non-privileged process for later use. However, that process will be able to bind the socket to any network interface. Even though it will not be able to receive any traffic without modification of the BPF map, the situation is not ideal. Sockets already have a mechanism that can be used to restrict what interface they can be attached to. That is SO_BINDTODEVICE. To change the SO_BINDTODEVICE binding the process will need CAP_NET_RAW. Make xsk_bind() honor the SO_BINDTODEVICE in order to allow safer workflow when non-privileged process is using AF_XDP. The intended workflow is following: 1. First process creates a bare socket with socket(AF_XDP, ...). 2. First process loads the XSK program to the interface. 3. First process adds the socket fd to a BPF map. 4. First process ties socket fd to a particular interface using SO_BINDTODEVICE. 5. First process sends socket fd to a second process. 6. Second process allocates UMEM. 7. Second process binds socket to the interface with bind(...). 8. Second process sends/receives the traffic. All the steps above are possible today if the first process is privileged and the second one has sufficient RLIMIT_MEMLOCK and no capabilities. However, the second process will be able to bind the socket to any interface it wants on step 7 and send traffic from it. With the proposed change, the second process will be able to bind the socket only to a specific interface chosen by the first process at step 4. Fixes: 965a99098443 ("xsk: add support for bind for Rx") Signed-off-by: Ilya Maximets Signed-off-by: Daniel Borkmann Acked-by: Magnus Karlsson Acked-by: John Fastabend Acked-by: Jason Wang Link: https://lore.kernel.org/bpf/20230703175329.3259672-1-i.maximets@ovn.org Signed-off-by: Sasha Levin --- Documentation/networking/af_xdp.rst | 9 +++++++++ net/xdp/xsk.c | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst index 60b217b436be..5b77b9e5ac7e 100644 --- a/Documentation/networking/af_xdp.rst +++ b/Documentation/networking/af_xdp.rst @@ -433,6 +433,15 @@ start N bytes into the buffer leaving the first N bytes for the application to use. The final option is the flags field, but it will be dealt with in separate sections for each UMEM flag. +SO_BINDTODEVICE setsockopt +-------------------------- + +This is a generic SOL_SOCKET option that can be used to tie AF_XDP +socket to a particular network interface. It is useful when a socket +is created by a privileged process and passed to a non-privileged one. +Once the option is set, kernel will refuse attempts to bind that socket +to a different interface. Updating the value requires CAP_NET_RAW. + XDP_STATISTICS getsockopt ------------------------- diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 13f62d2402e7..371d269d22fa 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -886,6 +886,7 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len) struct sock *sk = sock->sk; struct xdp_sock *xs = xdp_sk(sk); struct net_device *dev; + int bound_dev_if; u32 flags, qid; int err = 0; @@ -899,6 +900,10 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len) XDP_USE_NEED_WAKEUP)) return -EINVAL; + bound_dev_if = READ_ONCE(sk->sk_bound_dev_if); + if (bound_dev_if && bound_dev_if != sxdp->sxdp_ifindex) + return -EINVAL; + rtnl_lock(); mutex_lock(&xs->mutex); if (xs->state != XSK_READY) { -- cgit 1.4.1 From 18d78c5552d8f6aaee5ea41639e3abad556a3993 Mon Sep 17 00:00:00 2001 From: Lin Ma Date: Mon, 3 Jul 2023 19:08:42 +0800 Subject: net/sched: act_pedit: Add size check for TCA_PEDIT_PARMS_EX [ Upstream commit 30c45b5361d39b4b793780ffac5538090b9e2eb1 ] The attribute TCA_PEDIT_PARMS_EX is not be included in pedit_policy and one malicious user could fake a TCA_PEDIT_PARMS_EX whose length is smaller than the intended sizeof(struct tc_pedit). Hence, the dereference in tcf_pedit_init() could access dirty heap data. static int tcf_pedit_init(...) { // ... pattr = tb[TCA_PEDIT_PARMS]; // TCA_PEDIT_PARMS is included if (!pattr) pattr = tb[TCA_PEDIT_PARMS_EX]; // but this is not // ... parm = nla_data(pattr); index = parm->index; // parm is able to be smaller than 4 bytes // and this dereference gets dirty skb_buff // data created in netlink_sendmsg } This commit adds TCA_PEDIT_PARMS_EX length in pedit_policy which avoid the above case, just like the TCA_PEDIT_PARMS. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Signed-off-by: Lin Ma Reviewed-by: Pedro Tammela Link: https://lore.kernel.org/r/20230703110842.590282-1-linma@zju.edu.cn Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/act_pedit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 180669aa9d09..aee2e13f1db6 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -28,6 +28,7 @@ static struct tc_action_ops act_pedit_ops; static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = { [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) }, + [TCA_PEDIT_PARMS_EX] = { .len = sizeof(struct tc_pedit) }, [TCA_PEDIT_KEYS_EX] = { .type = NLA_NESTED }, }; -- cgit 1.4.1 From 78c6cf1dc7de56fffb9a29522153ccbd2cf5cff7 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Thu, 29 Jun 2023 07:20:44 +0300 Subject: fanotify: disallow mount/sb marks on kernel internal pseudo fs [ Upstream commit 69562eb0bd3e6bb8e522a7b254334e0fb30dff0c ] Hopefully, nobody is trying to abuse mount/sb marks for watching all anonymous pipes/inodes. I cannot think of a good reason to allow this - it looks like an oversight that dated back to the original fanotify API. Link: https://lore.kernel.org/linux-fsdevel/20230628101132.kvchg544mczxv2pm@quack3/ Fixes: 0ff21db9fcc3 ("fanotify: hooks the fanotify_mark syscall to the vfsmount code") Signed-off-by: Amir Goldstein Reviewed-by: Christian Brauner Signed-off-by: Jan Kara Message-Id: <20230629042044.25723-1-amir73il@gmail.com> Signed-off-by: Sasha Levin --- fs/notify/fanotify/fanotify_user.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 4546da4a54f9..9df5db0f10ff 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1574,6 +1574,20 @@ static int fanotify_events_supported(struct fsnotify_group *group, path->mnt->mnt_sb->s_type->fs_flags & FS_DISALLOW_NOTIFY_PERM) return -EINVAL; + /* + * mount and sb marks are not allowed on kernel internal pseudo fs, + * like pipe_mnt, because that would subscribe to events on all the + * anonynous pipes in the system. + * + * SB_NOUSER covers all of the internal pseudo fs whose objects are not + * exposed to user's mount namespace, but there are other SB_KERNMOUNT + * fs, like nsfs, debugfs, for which the value of allowing sb and mount + * mark is questionable. For now we leave them alone. + */ + if (mark_type != FAN_MARK_INODE && + path->mnt->mnt_sb->s_flags & SB_NOUSER) + return -EINVAL; + /* * We shouldn't have allowed setting dirent events and the directory * flags FAN_ONDIR and FAN_EVENT_ON_CHILD in mask of non-dir inode, -- cgit 1.4.1 From 0a1b80ff4f721c4be98707bfe9d20238df133eb8 Mon Sep 17 00:00:00 2001 From: Woody Zhang Date: Wed, 14 Jun 2023 21:19:07 +0800 Subject: riscv: move memblock_allow_resize() after linear mapping is ready [ Upstream commit 85fadc0d04119c2fe4a20287767ab904c6d21ba1 ] The initial memblock metadata is accessed from kernel image mapping. The regions arrays need to "reallocated" from memblock and accessed through linear mapping to cover more memblock regions. So the resizing should not be allowed until linear mapping is ready. Note that there are memblock allocations when building linear mapping. This patch is similar to 24cc61d8cb5a ("arm64: memblock: don't permit memblock resizing until linear mapping is up"). In following log, many memblock regions are reserved before create_linear_mapping_page_table(). And then it triggered reallocation of memblock.reserved.regions and memcpy the old array in kernel image mapping to the new array in linear mapping which caused a page fault. [ 0.000000] memblock_reserve: [0x00000000bf01f000-0x00000000bf01ffff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf021000-0x00000000bf021fff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf023000-0x00000000bf023fff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf025000-0x00000000bf025fff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf027000-0x00000000bf027fff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf029000-0x00000000bf029fff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf02b000-0x00000000bf02bfff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf02d000-0x00000000bf02dfff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf02f000-0x00000000bf02ffff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] memblock_reserve: [0x00000000bf030000-0x00000000bf030fff] early_init_fdt_scan_reserved_mem+0x28c/0x2c6 [ 0.000000] OF: reserved mem: 0x0000000080000000..0x000000008007ffff (512 KiB) map non-reusable mmode_resv0@80000000 [ 0.000000] memblock_reserve: [0x00000000bf000000-0x00000000bf001fed] paging_init+0x19a/0x5ae [ 0.000000] memblock_phys_alloc_range: 4096 bytes align=0x1000 from=0x0000000000000000 max_addr=0x0000000000000000 alloc_pmd_fixmap+0x14/0x1c [ 0.000000] memblock_reserve: [0x000000017ffff000-0x000000017fffffff] memblock_alloc_range_nid+0xb8/0x128 [ 0.000000] memblock: reserved is doubled to 256 at [0x000000017fffd000-0x000000017fffe7ff] [ 0.000000] Unable to handle kernel paging request at virtual address ff600000ffffd000 [ 0.000000] Oops [#1] [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 6.4.0-rc1-00011-g99a670b2069c #66 [ 0.000000] Hardware name: riscv-virtio,qemu (DT) [ 0.000000] epc : __memcpy+0x60/0xf8 [ 0.000000] ra : memblock_double_array+0x192/0x248 [ 0.000000] epc : ffffffff8081d214 ra : ffffffff80a3dfc0 sp : ffffffff81403bd0 [ 0.000000] gp : ffffffff814fbb38 tp : ffffffff8140dac0 t0 : 0000000001600000 [ 0.000000] t1 : 0000000000000000 t2 : 000000008f001000 s0 : ffffffff81403c60 [ 0.000000] s1 : ffffffff80c0bc98 a0 : ff600000ffffd000 a1 : ffffffff80c0bcd8 [ 0.000000] a2 : 0000000000000c00 a3 : ffffffff80c0c8d8 a4 : 0000000080000000 [ 0.000000] a5 : 0000000000080000 a6 : 0000000000000000 a7 : 0000000080200000 [ 0.000000] s2 : ff600000ffffd000 s3 : 0000000000002000 s4 : 0000000000000c00 [ 0.000000] s5 : ffffffff80c0bc60 s6 : ffffffff80c0bcc8 s7 : 0000000000000000 [ 0.000000] s8 : ffffffff814fd0a8 s9 : 000000017fffe7ff s10: 0000000000000000 [ 0.000000] s11: 0000000000001000 t3 : 0000000000001000 t4 : 0000000000000000 [ 0.000000] t5 : 000000008f003000 t6 : ff600000ffffd000 [ 0.000000] status: 0000000200000100 badaddr: ff600000ffffd000 cause: 000000000000000f [ 0.000000] [] __memcpy+0x60/0xf8 [ 0.000000] [] memblock_add_range.isra.14+0x12c/0x162 [ 0.000000] [] memblock_reserve+0x6e/0x8c [ 0.000000] [] memblock_alloc_range_nid+0xb8/0x128 [ 0.000000] [] memblock_phys_alloc_range+0x5e/0x6a [ 0.000000] [] alloc_pmd_fixmap+0x14/0x1c [ 0.000000] [] alloc_p4d_fixmap+0xc/0x14 [ 0.000000] [] create_pgd_mapping+0x98/0x17c [ 0.000000] [] create_linear_mapping_range.constprop.10+0xe4/0x112 [ 0.000000] [] paging_init+0x3ec/0x5ae [ 0.000000] [] setup_arch+0xb2/0x576 [ 0.000000] [] start_kernel+0x72/0x57e [ 0.000000] Code: b303 0285 b383 0305 be03 0385 be83 0405 bf03 0485 (b023) 00ef [ 0.000000] ---[ end trace 0000000000000000 ]--- [ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task! [ 0.000000] ---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]--- Fixes: 671f9a3e2e24 ("RISC-V: Setup initial page tables in two stages") Signed-off-by: Woody Zhang Tested-by: Song Shuai Link: https://lore.kernel.org/r/tencent_FBB94CE615C5CCE7701CD39C15CCE0EE9706@qq.com Signed-off-by: Palmer Dabbelt Signed-off-by: Sasha Levin --- arch/riscv/mm/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index d8d97df80190..9390cdff39ff 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -255,7 +255,6 @@ static void __init setup_bootmem(void) dma_contiguous_reserve(dma32_phys_limit); if (IS_ENABLED(CONFIG_64BIT)) hugetlb_cma_reserve(PUD_SHIFT - PAGE_SHIFT); - memblock_allow_resize(); } #ifdef CONFIG_MMU @@ -1211,6 +1210,9 @@ void __init paging_init(void) { setup_bootmem(); setup_vm_final(); + + /* Depend on that Linear Mapping is ready */ + memblock_allow_resize(); } void __init misc_mem_init(void) -- cgit 1.4.1 From 97a6d99c5434930cb9aaa67ee393e808952c1512 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Mon, 3 Jul 2023 19:14:46 +0200 Subject: pptp: Fix fib lookup calls. [ Upstream commit 84bef5b6037c15180ef88ac4216dc621d16df1a6 ] PPTP uses pppox sockets (struct pppox_sock). These sockets don't embed an inet_sock structure, so it's invalid to call inet_sk() on them. Therefore, the ip_route_output_ports() call in pptp_connect() has two problems: * The tos variable is set with RT_CONN_FLAGS(sk), which calls inet_sk() on the pppox socket. * ip_route_output_ports() tries to retrieve routing flags using inet_sk_flowi_flags(), which is also going to call inet_sk() on the pppox socket. While PPTP doesn't use inet sockets, it's actually really layered on top of IP and therefore needs a proper way to do fib lookups. So let's define pptp_route_output() to get a struct rtable from a pptp socket. Let's also replace the ip_route_output_ports() call of pptp_xmit() for consistency. In practice, this means that: * pptp_connect() sets ->flowi4_tos and ->flowi4_flags to zero instead of using bits of unrelated struct pppox_sock fields. * pptp_xmit() now respects ->sk_mark and ->sk_uid. * pptp_xmit() now calls the security_sk_classify_flow() security hook, thus allowing to set ->flowic_secid. * pptp_xmit() now passes the pppox socket to xfrm_lookup_route(). Found by code inspection. Fixes: 00959ade36ac ("PPTP: PPP over IPv4 (Point-to-Point Tunneling Protocol)") Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ppp/pptp.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 0fe78826c8fa..32183f24e63f 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -128,6 +129,23 @@ static void del_chan(struct pppox_sock *sock) spin_unlock(&chan_lock); } +static struct rtable *pptp_route_output(struct pppox_sock *po, + struct flowi4 *fl4) +{ + struct sock *sk = &po->sk; + struct net *net; + + net = sock_net(sk); + flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0, + RT_SCOPE_UNIVERSE, IPPROTO_GRE, 0, + po->proto.pptp.dst_addr.sin_addr.s_addr, + po->proto.pptp.src_addr.sin_addr.s_addr, + 0, 0, sock_net_uid(net, sk)); + security_sk_classify_flow(sk, flowi4_to_flowi_common(fl4)); + + return ip_route_output_flow(net, fl4, sk); +} + static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) { struct sock *sk = (struct sock *) chan->private; @@ -151,11 +169,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) if (sk_pppox(po)->sk_state & PPPOX_DEAD) goto tx_error; - rt = ip_route_output_ports(net, &fl4, NULL, - opt->dst_addr.sin_addr.s_addr, - opt->src_addr.sin_addr.s_addr, - 0, 0, IPPROTO_GRE, - RT_TOS(0), sk->sk_bound_dev_if); + rt = pptp_route_output(po, &fl4); if (IS_ERR(rt)) goto tx_error; @@ -438,12 +452,7 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, po->chan.private = sk; po->chan.ops = &pptp_chan_ops; - rt = ip_route_output_ports(sock_net(sk), &fl4, sk, - opt->dst_addr.sin_addr.s_addr, - opt->src_addr.sin_addr.s_addr, - 0, 0, - IPPROTO_GRE, RT_CONN_FLAGS(sk), - sk->sk_bound_dev_if); + rt = pptp_route_output(po, &fl4); if (IS_ERR(rt)) { error = -EHOSTUNREACH; goto end; -- cgit 1.4.1 From 079dc659e31f13f5685e2d0ca966576317d196ad Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 4 Jul 2023 01:05:44 +0300 Subject: net: dsa: tag_sja1105: fix MAC DA patching from meta frames [ Upstream commit 1dcf6efd5f0c1f4496b3ef7ec5a7db104a53b38c ] The SJA1105 manual says that at offset 4 into the meta frame payload we have "MAC destination byte 2" and at offset 5 we have "MAC destination byte 1". These are counted from the LSB, so byte 1 is h_dest[ETH_HLEN-2] aka h_dest[4] and byte 2 is h_dest[ETH_HLEN-3] aka h_dest[3]. The sja1105_meta_unpack() function decodes these the other way around, so a frame with MAC DA 01:80:c2:11:22:33 is received by the network stack as having 01:80:c2:22:11:33. Fixes: e53e18a6fe4d ("net: dsa: sja1105: Receive and decode meta frames") Signed-off-by: Vladimir Oltean Reviewed-by: Simon Horman Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/dsa/tag_sja1105.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 6e3699d859db..d3f27cd5fe24 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -113,8 +113,8 @@ static void sja1105_meta_unpack(const struct sk_buff *skb, * a unified unpacking command for both device series. */ packing(buf, &meta->tstamp, 31, 0, 4, UNPACK, 0); - packing(buf + 4, &meta->dmac_byte_4, 7, 0, 1, UNPACK, 0); - packing(buf + 5, &meta->dmac_byte_3, 7, 0, 1, UNPACK, 0); + packing(buf + 4, &meta->dmac_byte_3, 7, 0, 1, UNPACK, 0); + packing(buf + 5, &meta->dmac_byte_4, 7, 0, 1, UNPACK, 0); packing(buf + 6, &meta->source_port, 7, 0, 1, UNPACK, 0); packing(buf + 7, &meta->switch_id, 7, 0, 1, UNPACK, 0); } -- cgit 1.4.1 From 947d741adf6622581e8a8e7bc24e6abb94cc46a3 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 4 Jul 2023 01:05:45 +0300 Subject: net: dsa: sja1105: always enable the send_meta options [ Upstream commit a372d66af48506d9f7aaae2a474cd18f14d98cb8 ] incl_srcpt has the limitation, mentioned in commit b4638af8885a ("net: dsa: sja1105: always enable the INCL_SRCPT option"), that frames with a MAC DA of 01:80:c2:xx:yy:zz will be received as 01:80:c2:00:00:zz unless PTP RX timestamping is enabled. The incl_srcpt option was initially unconditionally enabled, then that changed with commit 42824463d38d ("net: dsa: sja1105: Limit use of incl_srcpt to bridge+vlan mode"), then again with b4638af8885a ("net: dsa: sja1105: always enable the INCL_SRCPT option"). Bottom line is that it now needs to be always enabled, otherwise the driver does not have a reliable source of information regarding source_port and switch_id for link-local traffic (tag_8021q VLANs may be imprecise since now they identify an entire bridging domain when ports are not standalone). If we accept that PTP RX timestamping (and therefore, meta frame generation) is always enabled in hardware, then that limitation could be avoided and packets with any MAC DA can be properly received, because meta frames do contain the original bytes from the MAC DA of their associated link-local packet. This change enables meta frame generation unconditionally, which also has the nice side effects of simplifying the switch control path (a switch reset is no longer required on hwtstamping settings change) and the tagger data path (it no longer needs to be informed whether to expect meta frames or not - it always does). Fixes: 227d07a07ef1 ("net: dsa: sja1105: Add support for traffic through standalone ports") Signed-off-by: Vladimir Oltean Reviewed-by: Simon Horman Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/dsa/sja1105/sja1105.h | 2 +- drivers/net/dsa/sja1105/sja1105_main.c | 5 ++-- drivers/net/dsa/sja1105/sja1105_ptp.c | 48 +++------------------------------- include/linux/dsa/sja1105.h | 4 --- net/dsa/tag_sja1105.c | 45 ------------------------------- 5 files changed, 7 insertions(+), 97 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 9ba2ec2b966d..fb3cd4c78faa 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -250,6 +250,7 @@ struct sja1105_private { unsigned long ucast_egress_floods; unsigned long bcast_egress_floods; unsigned long hwts_tx_en; + unsigned long hwts_rx_en; const struct sja1105_info *info; size_t max_xfer_len; struct spi_device *spidev; @@ -287,7 +288,6 @@ struct sja1105_spi_message { /* From sja1105_main.c */ enum sja1105_reset_reason { SJA1105_VLAN_FILTERING = 0, - SJA1105_RX_HWTSTAMPING, SJA1105_AGEING_TIME, SJA1105_SCHEDULING, SJA1105_BEST_EFFORT_POLICING, diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 8bd61f2ebb2a..947e8f7c0988 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -867,11 +867,11 @@ static int sja1105_init_general_params(struct sja1105_private *priv) .mac_fltres1 = SJA1105_LINKLOCAL_FILTER_A, .mac_flt1 = SJA1105_LINKLOCAL_FILTER_A_MASK, .incl_srcpt1 = true, - .send_meta1 = false, + .send_meta1 = true, .mac_fltres0 = SJA1105_LINKLOCAL_FILTER_B, .mac_flt0 = SJA1105_LINKLOCAL_FILTER_B_MASK, .incl_srcpt0 = true, - .send_meta0 = false, + .send_meta0 = true, /* Default to an invalid value */ .mirr_port = priv->ds->num_ports, /* No TTEthernet */ @@ -2215,7 +2215,6 @@ static int sja1105_reload_cbs(struct sja1105_private *priv) static const char * const sja1105_reset_reasons[] = { [SJA1105_VLAN_FILTERING] = "VLAN filtering", - [SJA1105_RX_HWTSTAMPING] = "RX timestamping", [SJA1105_AGEING_TIME] = "Ageing time", [SJA1105_SCHEDULING] = "Time-aware scheduling", [SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing", diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 30fb2cc40164..a7d41e781398 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -58,35 +58,10 @@ enum sja1105_ptp_clk_mode { #define ptp_data_to_sja1105(d) \ container_of((d), struct sja1105_private, ptp_data) -/* Must be called only while the RX timestamping state of the tagger - * is turned off - */ -static int sja1105_change_rxtstamping(struct sja1105_private *priv, - bool on) -{ - struct sja1105_ptp_data *ptp_data = &priv->ptp_data; - struct sja1105_general_params_entry *general_params; - struct sja1105_table *table; - - table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; - general_params = table->entries; - general_params->send_meta1 = on; - general_params->send_meta0 = on; - - ptp_cancel_worker_sync(ptp_data->clock); - skb_queue_purge(&ptp_data->skb_txtstamp_queue); - skb_queue_purge(&ptp_data->skb_rxtstamp_queue); - - return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING); -} - int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) { - struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(ds); struct sja1105_private *priv = ds->priv; struct hwtstamp_config config; - bool rx_on; - int rc; if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) return -EFAULT; @@ -104,26 +79,13 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: - rx_on = false; + priv->hwts_rx_en &= ~BIT(port); break; default: - rx_on = true; + priv->hwts_rx_en |= BIT(port); break; } - if (rx_on != tagger_data->rxtstamp_get_state(ds)) { - tagger_data->rxtstamp_set_state(ds, false); - - rc = sja1105_change_rxtstamping(priv, rx_on); - if (rc < 0) { - dev_err(ds->dev, - "Failed to change RX timestamping: %d\n", rc); - return rc; - } - if (rx_on) - tagger_data->rxtstamp_set_state(ds, true); - } - if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) return -EFAULT; return 0; @@ -131,7 +93,6 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr) { - struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(ds); struct sja1105_private *priv = ds->priv; struct hwtstamp_config config; @@ -140,7 +101,7 @@ int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr) config.tx_type = HWTSTAMP_TX_ON; else config.tx_type = HWTSTAMP_TX_OFF; - if (tagger_data->rxtstamp_get_state(ds)) + if (priv->hwts_rx_en & BIT(port)) config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; else config.rx_filter = HWTSTAMP_FILTER_NONE; @@ -413,11 +374,10 @@ static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp) bool sja1105_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) { - struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(ds); struct sja1105_private *priv = ds->priv; struct sja1105_ptp_data *ptp_data = &priv->ptp_data; - if (!tagger_data->rxtstamp_get_state(ds)) + if (!(priv->hwts_rx_en & BIT(port))) return false; /* We need to read the full PTP clock to reconstruct the Rx diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h index 159e43171ccc..c177322f793d 100644 --- a/include/linux/dsa/sja1105.h +++ b/include/linux/dsa/sja1105.h @@ -48,13 +48,9 @@ struct sja1105_deferred_xmit_work { /* Global tagger data */ struct sja1105_tagger_data { - /* Tagger to switch */ void (*xmit_work_fn)(struct kthread_work *work); void (*meta_tstamp_handler)(struct dsa_switch *ds, int port, u8 ts_id, enum sja1110_meta_tstamp dir, u64 tstamp); - /* Switch to tagger */ - bool (*rxtstamp_get_state)(struct dsa_switch *ds); - void (*rxtstamp_set_state)(struct dsa_switch *ds, bool on); }; struct sja1105_skb_cb { diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index d3f27cd5fe24..143348962a21 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -53,11 +53,8 @@ #define SJA1110_TX_TRAILER_LEN 4 #define SJA1110_MAX_PADDING_LEN 15 -#define SJA1105_HWTS_RX_EN 0 - struct sja1105_tagger_private { struct sja1105_tagger_data data; /* Must be first */ - unsigned long state; /* Protects concurrent access to the meta state machine * from taggers running on multiple ports on SMP systems */ @@ -387,10 +384,6 @@ static struct sk_buff priv = sja1105_tagger_private(ds); - if (!test_bit(SJA1105_HWTS_RX_EN, &priv->state)) - /* Do normal processing. */ - return skb; - spin_lock(&priv->meta_lock); /* Was this a link-local frame instead of the meta * that we were expecting? @@ -426,12 +419,6 @@ static struct sk_buff priv = sja1105_tagger_private(ds); - /* Drop the meta frame if we're not in the right state - * to process it. - */ - if (!test_bit(SJA1105_HWTS_RX_EN, &priv->state)) - return NULL; - spin_lock(&priv->meta_lock); stampable_skb = priv->stampable_skb; @@ -467,30 +454,6 @@ static struct sk_buff return skb; } -static bool sja1105_rxtstamp_get_state(struct dsa_switch *ds) -{ - struct sja1105_tagger_private *priv = sja1105_tagger_private(ds); - - return test_bit(SJA1105_HWTS_RX_EN, &priv->state); -} - -static void sja1105_rxtstamp_set_state(struct dsa_switch *ds, bool on) -{ - struct sja1105_tagger_private *priv = sja1105_tagger_private(ds); - - if (on) - set_bit(SJA1105_HWTS_RX_EN, &priv->state); - else - clear_bit(SJA1105_HWTS_RX_EN, &priv->state); - - /* Initialize the meta state machine to a known state */ - if (!priv->stampable_skb) - return; - - kfree_skb(priv->stampable_skb); - priv->stampable_skb = NULL; -} - static bool sja1105_skb_has_tag_8021q(const struct sk_buff *skb) { u16 tpid = ntohs(eth_hdr(skb)->h_proto); @@ -547,9 +510,6 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, */ source_port = hdr->h_dest[3]; switch_id = hdr->h_dest[4]; - /* Clear the DMAC bytes that were mangled by the switch */ - hdr->h_dest[3] = 0; - hdr->h_dest[4] = 0; } else if (is_meta) { sja1105_meta_unpack(skb, &meta); source_port = meta.source_port; @@ -780,7 +740,6 @@ static void sja1105_disconnect(struct dsa_switch *ds) static int sja1105_connect(struct dsa_switch *ds) { - struct sja1105_tagger_data *tagger_data; struct sja1105_tagger_private *priv; struct kthread_worker *xmit_worker; int err; @@ -800,10 +759,6 @@ static int sja1105_connect(struct dsa_switch *ds) } priv->xmit_worker = xmit_worker; - /* Export functions for switch driver use */ - tagger_data = &priv->data; - tagger_data->rxtstamp_get_state = sja1105_rxtstamp_get_state; - tagger_data->rxtstamp_set_state = sja1105_rxtstamp_set_state; ds->tagger_data = priv; return 0; -- cgit 1.4.1 From 4a141c3c0306b61f151d1190e2574ca816e50bb2 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Tue, 4 Jul 2023 09:56:53 +0530 Subject: octeontx-af: fix hardware timestamp configuration [ Upstream commit 14bb236b29922c4f57d8c05bfdbcb82677f917c9 ] MAC block on CN10K (RPM) supports hardware timestamp configuration. The previous patch which added timestamp configuration support has a bug. Though the netdev driver requests to disable timestamp configuration, the driver is always enabling it. This patch fixes the same. Fixes: d1489208681d ("octeontx2-af: cn10k: RPM hardware timestamp configuration") Signed-off-by: Hariprasad Kelam Signed-off-by: Sunil Goutham Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index e284a14ca120..c60b9580ca96 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -751,7 +751,7 @@ static int rvu_cgx_ptp_rx_cfg(struct rvu *rvu, u16 pcifunc, bool enable) cgxd = rvu_cgx_pdata(cgx_id, rvu); mac_ops = get_mac_ops(cgxd); - mac_ops->mac_enadis_ptp_config(cgxd, lmac_id, true); + mac_ops->mac_enadis_ptp_config(cgxd, lmac_id, enable); /* If PTP is enabled then inform NPC that packets to be * parsed by this PF will have their data shifted by 8 bytes * and if PTP is disabled then no shift is required -- cgit 1.4.1 From f5ea303502b9c0cc4f251d97897d93f47721910c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 4 Jul 2023 20:22:15 +0100 Subject: afs: Fix accidental truncation when storing data [ Upstream commit 03275585cabd0240944f19f33d7584a1b099a3a8 ] When an AFS FS.StoreData RPC call is made, amongst other things it is given the resultant file size to be. On the server, this is processed by truncating the file to new size and then writing the data. Now, kafs has a lock (vnode->io_lock) that serves to serialise operations against a specific vnode (ie. inode), but the parameters for the op are set before the lock is taken. This allows two writebacks (say sync and kswapd) to race - and if writes are ongoing the writeback for a later write could occur before the writeback for an earlier one if the latter gets interrupted. Note that afs_writepages() cannot take i_mutex and only takes a shared lock on vnode->validate_lock. Also note that the server does the truncation and the write inside a lock, so there's no problem at that end. Fix this by moving the calculation for the proposed new i_size inside the vnode->io_lock. Also reset the iterator (which we might have read from) and update the mtime setting there. Fixes: bd80d8a80e12 ("afs: Use ITER_XARRAY for writing") Reported-by: Marc Dionne Signed-off-by: David Howells Reviewed-by: Jeffrey Altman Reviewed-by: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org Link: https://lore.kernel.org/r/3526895.1687960024@warthog.procyon.org.uk/ Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin --- fs/afs/write.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/afs/write.c b/fs/afs/write.c index 08fd456dde67..3ecc212b6209 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -381,17 +381,19 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t afs_op_set_vnode(op, 0, vnode); op->file[0].dv_delta = 1; op->file[0].modification = true; - op->store.write_iter = iter; op->store.pos = pos; op->store.size = size; - op->store.i_size = max(pos + size, vnode->netfs.remote_i_size); op->store.laundering = laundering; - op->mtime = vnode->netfs.inode.i_mtime; op->flags |= AFS_OPERATION_UNINTR; op->ops = &afs_store_data_operation; try_next_key: afs_begin_vnode_operation(op); + + op->store.write_iter = iter; + op->store.i_size = max(pos + size, vnode->netfs.remote_i_size); + op->mtime = vnode->netfs.inode.i_mtime; + afs_wait_for_operation(op); switch (op->error) { -- cgit 1.4.1 From b837c692360087316cc0c587fc2d7bfbd66c03e8 Mon Sep 17 00:00:00 2001 From: Thorsten Winkler Date: Tue, 4 Jul 2023 16:41:21 +0200 Subject: s390/qeth: Fix vipa deletion [ Upstream commit 80de809bd35e2a8999edf9f5aaa2d8de18921f11 ] Change boolean parameter of function "qeth_l3_vipa_store" inside the "qeth_l3_dev_vipa_del4_store" function from "true" to "false" because "true" is used for adding a virtual ip address and "false" for deleting. Fixes: 2390166a6b45 ("s390/qeth: clean up L3 sysfs code") Reviewed-by: Alexandra Winter Reviewed-by: Wenjia Zhang Signed-off-by: Thorsten Winkler Signed-off-by: Alexandra Winter Reviewed-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/s390/net/qeth_l3_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index 1082380b21f8..dba9b307020c 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -652,7 +652,7 @@ static QETH_DEVICE_ATTR(vipa_add4, add4, 0644, static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV4); + return qeth_l3_vipa_store(dev, buf, false, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL, -- cgit 1.4.1 From 8fb11fa4805699c6b73a9c8a9d45807f9874abe3 Mon Sep 17 00:00:00 2001 From: Artur Rojek Date: Sat, 27 May 2023 18:44:50 +0200 Subject: sh: dma: Fix DMA channel offset calculation [ Upstream commit e82e47584847129a20b8c9f4a1dcde09374fb0e0 ] Various SoCs of the SH3, SH4 and SH4A family, which use this driver, feature a differing number of DMA channels, which can be distributed between up to two DMAC modules. The existing implementation fails to correctly accommodate for all those variations, resulting in wrong channel offset calculations and leading to kernel panics. Rewrite dma_base_addr() in order to properly calculate channel offsets in a DMAC module. Fix dmaor_read_reg() and dmaor_write_reg(), so that the correct DMAC module base is selected for the DMAOR register. Fixes: 7f47c7189b3e8f19 ("sh: dma: More legacy cpu dma chainsawing.") Signed-off-by: Artur Rojek Reviewed-by: Geert Uytterhoeven Reviewed-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/20230527164452.64797-2-contact@artur-rojek.eu Signed-off-by: John Paul Adrian Glaubitz Signed-off-by: Sasha Levin --- arch/sh/drivers/dma/dma-sh.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c index 96c626c2cd0a..306fba1564e5 100644 --- a/arch/sh/drivers/dma/dma-sh.c +++ b/arch/sh/drivers/dma/dma-sh.c @@ -18,6 +18,18 @@ #include #include +/* + * Some of the SoCs feature two DMAC modules. In such a case, the channels are + * distributed equally among them. + */ +#ifdef SH_DMAC_BASE1 +#define SH_DMAC_NR_MD_CH (CONFIG_NR_ONCHIP_DMA_CHANNELS / 2) +#else +#define SH_DMAC_NR_MD_CH CONFIG_NR_ONCHIP_DMA_CHANNELS +#endif + +#define SH_DMAC_CH_SZ 0x10 + /* * Define the default configuration for dual address memory-memory transfer. * The 0x400 value represents auto-request, external->external. @@ -29,7 +41,7 @@ static unsigned long dma_find_base(unsigned int chan) unsigned long base = SH_DMAC_BASE0; #ifdef SH_DMAC_BASE1 - if (chan >= 6) + if (chan >= SH_DMAC_NR_MD_CH) base = SH_DMAC_BASE1; #endif @@ -40,13 +52,13 @@ static unsigned long dma_base_addr(unsigned int chan) { unsigned long base = dma_find_base(chan); - /* Normalize offset calculation */ - if (chan >= 9) - chan -= 6; - if (chan >= 4) - base += 0x10; + chan = (chan % SH_DMAC_NR_MD_CH) * SH_DMAC_CH_SZ; + + /* DMAOR is placed inside the channel register space. Step over it. */ + if (chan >= DMAOR) + base += SH_DMAC_CH_SZ; - return base + (chan * 0x10); + return base + chan; } #ifdef CONFIG_SH_DMA_IRQ_MULTI @@ -250,12 +262,11 @@ static int sh_dmac_get_dma_residue(struct dma_channel *chan) #define NR_DMAOR 1 #endif -/* - * DMAOR bases are broken out amongst channel groups. DMAOR0 manages - * channels 0 - 5, DMAOR1 6 - 11 (optional). - */ -#define dmaor_read_reg(n) __raw_readw(dma_find_base((n)*6)) -#define dmaor_write_reg(n, data) __raw_writew(data, dma_find_base(n)*6) +#define dmaor_read_reg(n) __raw_readw(dma_find_base((n) * \ + SH_DMAC_NR_MD_CH) + DMAOR) +#define dmaor_write_reg(n, data) __raw_writew(data, \ + dma_find_base((n) * \ + SH_DMAC_NR_MD_CH) + DMAOR) static inline int dmaor_reset(int no) { -- cgit 1.4.1 From e9fbb7c2f65e900fc6505274f70cd9db3c876667 Mon Sep 17 00:00:00 2001 From: Danila Chernetsov Date: Tue, 4 Apr 2023 19:05:49 +0000 Subject: apparmor: fix missing error check for rhashtable_insert_fast [ Upstream commit 000518bc5aef25d3f703592a0296d578c98b1517 ] rhashtable_insert_fast() could return err value when memory allocation is failed. but unpack_profile() do not check values and this always returns success value. This patch just adds error check code. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: e025be0f26d5 ("apparmor: support querying extended trusted helper extra data") Signed-off-by: Danila Chernetsov Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy_unpack.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 9d26bbb90133..9c3fec2c7cf6 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -917,8 +917,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } - rhashtable_insert_fast(profile->data, &data->head, - profile->data->p); + if (rhashtable_insert_fast(profile->data, &data->head, + profile->data->p)) { + kfree_sensitive(data->key); + kfree_sensitive(data); + info = "failed to insert data to table"; + goto fail; + } } if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { -- cgit 1.4.1 From 15970b0828c142f7f79926fe1c48f2ab58b7a07d Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Tue, 6 Jun 2023 12:25:58 -0600 Subject: i2c: xiic: Don't try to handle more interrupt events after error [ Upstream commit cb6e45c9a0ad9e0f8664fd06db0227d185dc76ab ] In xiic_process, it is possible that error events such as arbitration lost or TX error can be raised in conjunction with other interrupt flags such as TX FIFO empty or bus not busy. Error events result in the controller being reset and the error returned to the calling request, but the function could potentially try to keep handling the other events, such as by writing more messages into the TX FIFO. Since the transaction has already failed, this is not helpful and will just cause issues. This problem has been present ever since: commit 7f9906bd7f72 ("i2c: xiic: Service all interrupts in isr") which allowed non-error events to be handled after errors, but became more obvious after: commit 743e227a8959 ("i2c: xiic: Defer xiic_wakeup() and __xiic_start_xfer() in xiic_process()") which reworked the code to add a WARN_ON which triggers if both the xfer_more and wakeup_req flags were set, since this combination is not supposed to happen, but was occurring in this scenario. Skip further interrupt handling after error flags are detected to avoid this problem. Fixes: 7f9906bd7f72 ("i2c: xiic: Service all interrupts in isr") Signed-off-by: Robert Hancock Acked-by: Andi Shyti Signed-off-by: Wolfram Sang Signed-off-by: Sasha Levin --- drivers/i2c/busses/i2c-xiic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index effae4d46729..b41a6709e47f 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -431,6 +431,8 @@ static irqreturn_t xiic_process(int irq, void *dev_id) wakeup_req = 1; wakeup_code = STATE_ERROR; } + /* don't try to handle other events */ + goto out; } if (pend & XIIC_INTR_RX_FULL_MASK) { /* Receive register/FIFO is full */ -- cgit 1.4.1 From 0783867a30b52f0363b9d5cbfd004d6a11138cf0 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Mon, 30 Jan 2023 21:43:57 +0100 Subject: dm: fix undue/missing spaces [ Upstream commit 43be9c743c2553519c2093d1798b542f28095a51 ] Signed-off-by: Heinz Mauelshagen Signed-off-by: Mike Snitzer Stable-dep-of: 249bed821b4d ("dm ioctl: Avoid double-fetch of version") Signed-off-by: Sasha Levin --- drivers/md/dm-ioctl.c | 4 ++-- drivers/md/dm-mpath.c | 2 +- drivers/md/dm-ps-service-time.c | 3 +-- drivers/md/dm-snap.c | 6 +++--- drivers/md/dm-table.c | 2 +- drivers/md/dm-uevent.h | 2 +- drivers/md/dm-writecache.c | 4 ++-- drivers/md/dm-zoned-metadata.c | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 6ae1c19b8243..9fc4a5d51b3f 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1572,7 +1572,7 @@ static void retrieve_deps(struct dm_table *table, /* * Count the devices. */ - list_for_each (tmp, dm_table_get_devices(table)) + list_for_each(tmp, dm_table_get_devices(table)) count++; /* @@ -1589,7 +1589,7 @@ static void retrieve_deps(struct dm_table *table, */ deps->count = count; count = 0; - list_for_each_entry (dd, dm_table_get_devices(table), list) + list_for_each_entry(dd, dm_table_get_devices(table), list) deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev); param->data_size = param->data_start + needed; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 91c25ad8eed8..66032ab3c4e9 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1086,7 +1086,7 @@ static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m) goto fail; } j = sprintf(p, "%d", hw_argc - 1); - for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1) + for (i = 0, p += j + 1; i <= hw_argc - 2; i++, p += j + 1) j = sprintf(p, "%s", as->argv[i]); } dm_consume_args(as, hw_argc - 1); diff --git a/drivers/md/dm-ps-service-time.c b/drivers/md/dm-ps-service-time.c index 84d26234dc05..eba2293be686 100644 --- a/drivers/md/dm-ps-service-time.c +++ b/drivers/md/dm-ps-service-time.c @@ -127,8 +127,7 @@ static int st_add_path(struct path_selector *ps, struct dm_path *path, * The valid range: 0- * If not given, minimum value '1' is used. * If '0' is given, the path isn't selected while - * other paths having a positive value are - * available. + * other paths having a positive value are available. */ if (argc > 2) { *error = "service-time ps: incorrect number of arguments"; diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index c64d987c544d..cb80d03b3737 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -388,7 +388,7 @@ static struct origin *__lookup_origin(struct block_device *origin) struct origin *o; ol = &_origins[origin_hash(origin)]; - list_for_each_entry (o, ol, hash_list) + list_for_each_entry(o, ol, hash_list) if (bdev_equal(o->bdev, origin)) return o; @@ -407,7 +407,7 @@ static struct dm_origin *__lookup_dm_origin(struct block_device *origin) struct dm_origin *o; ol = &_dm_origins[origin_hash(origin)]; - list_for_each_entry (o, ol, hash_list) + list_for_each_entry(o, ol, hash_list) if (bdev_equal(o->dev->bdev, origin)) return o; @@ -2446,7 +2446,7 @@ static int __origin_write(struct list_head *snapshots, sector_t sector, chunk_t chunk; /* Do all the snapshots on this origin */ - list_for_each_entry (snap, snapshots, list) { + list_for_each_entry(snap, snapshots, list) { /* * Don't make new exceptions in a merging snapshot * because it has effectively been deleted diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 3acded2f976d..337e667323c4 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -211,7 +211,7 @@ static struct dm_dev_internal *find_device(struct list_head *l, dev_t dev) { struct dm_dev_internal *dd; - list_for_each_entry (dd, l, list) + list_for_each_entry(dd, l, list) if (dd->dm_dev->bdev->bd_dev == dev) return dd; diff --git a/drivers/md/dm-uevent.h b/drivers/md/dm-uevent.h index 2c9ba561fd8e..12a5d4fb7d44 100644 --- a/drivers/md/dm-uevent.h +++ b/drivers/md/dm-uevent.h @@ -3,7 +3,7 @@ * Device Mapper Uevent Support * * Copyright IBM Corporation, 2007 - * Author: Mike Anderson + * Author: Mike Anderson */ #ifndef DM_UEVENT_H #define DM_UEVENT_H diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 431c84595ddb..c6ff43a8f0b2 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -85,12 +85,12 @@ struct wc_entry { unsigned short wc_list_contiguous; bool write_in_progress #if BITS_PER_LONG == 64 - :1 + : 1 #endif ; unsigned long index #if BITS_PER_LONG == 64 - :47 + : 47 #endif ; unsigned long age; diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index c795ea7da791..13070c25adc3 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -1013,7 +1013,7 @@ static int dmz_check_sb(struct dmz_metadata *zmd, struct dmz_sb *dsb, } sb_block = le64_to_cpu(sb->sb_block); - if (sb_block != (u64)dsb->zone->id << zmd->zone_nr_blocks_shift ) { + if (sb_block != (u64)dsb->zone->id << zmd->zone_nr_blocks_shift) { dmz_dev_err(dev, "Invalid superblock position " "(is %llu expected %llu)", sb_block, -- cgit 1.4.1 From fd4497aca33bec7a0d59000d9c70778deb2e563a Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Fri, 3 Feb 2023 18:55:47 +0100 Subject: dm: avoid split of quoted strings where possible [ Upstream commit 2e84fecf19e1694338deec8bf6c90ff84f8f31fb ] Signed-off-by: Heinz Mauelshagen Signed-off-by: Mike Snitzer Stable-dep-of: 249bed821b4d ("dm ioctl: Avoid double-fetch of version") Signed-off-by: Sasha Levin --- drivers/md/dm-crypt.c | 3 +-- drivers/md/dm-ioctl.c | 6 ++---- drivers/md/dm-log-userspace-transfer.c | 5 ++--- drivers/md/dm-log.c | 3 +-- drivers/md/dm-raid1.c | 7 +++---- drivers/md/dm-snap-persistent.c | 6 ++---- drivers/md/dm-snap-transient.c | 3 +-- drivers/md/dm-snap.c | 21 +++++++-------------- drivers/md/dm-stripe.c | 12 ++++-------- drivers/md/dm-table.c | 18 ++++++------------ drivers/md/dm-thin-metadata.c | 8 ++++---- drivers/md/dm-zoned-metadata.c | 12 ++++-------- 12 files changed, 37 insertions(+), 67 deletions(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index ee269b1d09fa..ff515437d81e 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -733,8 +733,7 @@ static int crypt_iv_eboiv_ctr(struct crypt_config *cc, struct dm_target *ti, } if (crypto_skcipher_blocksize(any_tfm(cc)) != cc->iv_size) { - ti->error = "Block size of EBOIV cipher does " - "not match IV size of block cipher"; + ti->error = "Block size of EBOIV cipher does not match IV size of block cipher"; return -EINVAL; } diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 9fc4a5d51b3f..2ced382cdd70 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -434,8 +434,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, hc = __get_name_cell(new); if (hc) { - DMERR("Unable to change %s on mapped device %s to one that " - "already exists: %s", + DMERR("Unable to change %s on mapped device %s to one that already exists: %s", change_uuid ? "uuid" : "name", param->name, new); dm_put(hc->md); @@ -1822,8 +1821,7 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user) if ((DM_VERSION_MAJOR != version[0]) || (DM_VERSION_MINOR < version[1])) { - DMERR("ioctl interface mismatch: " - "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", + DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, version[0], version[1], version[2], cmd); diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 072559b709ed..ee5586e8e1c1 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -108,9 +108,8 @@ static int fill_pkg(struct cn_msg *msg, struct dm_ulog_request *tfr) if (pkg->error != -EAGAIN) *(pkg->data_size) = 0; } else if (tfr->data_size > *(pkg->data_size)) { - DMERR("Insufficient space to receive package [%u] " - "(%u vs %zu)", tfr->request_type, - tfr->data_size, *(pkg->data_size)); + DMERR("Insufficient space to receive package [%u] (%u vs %zu)", + tfr->request_type, tfr->data_size, *(pkg->data_size)); *(pkg->data_size) = 0; pkg->error = -ENOSPC; diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 159f2c05dfd3..05141eea18d3 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -382,8 +382,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, else if (!strcmp(argv[1], "nosync")) sync = NOSYNC; else { - DMWARN("unrecognised sync argument to " - "dirty region log: %s", argv[1]); + DMWARN("unrecognised sync argument to dirty region log: %s", argv[1]); return -EINVAL; } } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 8bd7e87d3538..c38e63706d91 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -236,8 +236,8 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type) * Better to issue requests to same failing device * than to risk returning corrupt data. */ - DMERR("Primary mirror (%s) failed while out-of-sync: " - "Reads may fail.", m->dev->name); + DMERR("Primary mirror (%s) failed while out-of-sync: Reads may fail.", + m->dev->name); goto out; } @@ -517,8 +517,7 @@ static void read_callback(unsigned long error, void *context) fail_mirror(m, DM_RAID1_READ_ERROR); if (likely(default_ok(m)) || mirror_available(m->ms, bio)) { - DMWARN_LIMIT("Read failure on mirror device %s. " - "Trying alternative device.", + DMWARN_LIMIT("Read failure on mirror device %s. Trying alternative device.", m->dev->name); queue_bio(m->ms, bio, bio_data_dir(bio)); return; diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 5176810f5d24..80b95746a43e 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -354,8 +354,7 @@ static int read_header(struct pstore *ps, int *new_snapshot) return 0; if (chunk_size_supplied) - DMWARN("chunk size %u in device metadata overrides " - "table chunk size of %u.", + DMWARN("chunk size %u in device metadata overrides table chunk size of %u.", chunk_size, ps->store->chunk_size); /* We had a bogus chunk_size. Fix stuff up. */ @@ -958,8 +957,7 @@ int dm_persistent_snapshot_init(void) r = dm_exception_store_type_register(&_persistent_compat_type); if (r) { - DMERR("Unable to register old-style persistent exception " - "store type"); + DMERR("Unable to register old-style persistent exception store type"); dm_exception_store_type_unregister(&_persistent_type); return r; } diff --git a/drivers/md/dm-snap-transient.c b/drivers/md/dm-snap-transient.c index d83a0565bd10..11de107f5f46 100644 --- a/drivers/md/dm-snap-transient.c +++ b/drivers/md/dm-snap-transient.c @@ -140,8 +140,7 @@ int dm_transient_snapshot_init(void) r = dm_exception_store_type_register(&_transient_compat_type); if (r) { - DMWARN("Unable to register old-style transient " - "exception store type"); + DMWARN("Unable to register old-style transient exception store type"); dm_exception_store_type_unregister(&_transient_type); return r; } diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index cb80d03b3737..b748901a4fb5 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -490,8 +490,7 @@ static int __validate_exception_handover(struct dm_snapshot *snap) if ((__find_snapshots_sharing_cow(snap, &snap_src, &snap_dest, &snap_merge) == 2) || snap_dest) { - snap->ti->error = "Snapshot cow pairing for exception " - "table handover failed"; + snap->ti->error = "Snapshot cow pairing for exception table handover failed"; return -EINVAL; } @@ -518,8 +517,7 @@ static int __validate_exception_handover(struct dm_snapshot *snap) if (!snap_src->store->type->prepare_merge || !snap_src->store->type->commit_merge) { - snap->ti->error = "Snapshot exception store does not " - "support snapshot-merge."; + snap->ti->error = "Snapshot exception store does not support snapshot-merge."; return -EINVAL; } @@ -937,8 +935,7 @@ static int __remove_single_exception_chunk(struct dm_snapshot *s, e = dm_lookup_exception(&s->complete, old_chunk); if (!e) { - DMERR("Corruption detected: exception for block %llu is " - "on disk but not in memory", + DMERR("Corruption detected: exception for block %llu is on disk but not in memory", (unsigned long long)old_chunk); return -EINVAL; } @@ -965,8 +962,7 @@ static int __remove_single_exception_chunk(struct dm_snapshot *s, e->new_chunk++; } else if (old_chunk != e->old_chunk + dm_consecutive_chunk_count(e)) { - DMERR("Attempt to merge block %llu from the " - "middle of a chunk range [%llu - %llu]", + DMERR("Attempt to merge block %llu from the middle of a chunk range [%llu - %llu]", (unsigned long long)old_chunk, (unsigned long long)e->old_chunk, (unsigned long long) @@ -1059,8 +1055,7 @@ static void snapshot_merge_next_chunks(struct dm_snapshot *s) &new_chunk); if (linear_chunks <= 0) { if (linear_chunks < 0) { - DMERR("Read error in exception store: " - "shutting down merge"); + DMERR("Read error in exception store: shutting down merge"); down_write(&s->lock); s->merge_failed = true; up_write(&s->lock); @@ -2208,12 +2203,10 @@ static int snapshot_preresume(struct dm_target *ti) if (snap_src && snap_dest) { down_read(&snap_src->lock); if (s == snap_src) { - DMERR("Unable to resume snapshot source until " - "handover completes."); + DMERR("Unable to resume snapshot source until handover completes."); r = -EINVAL; } else if (!dm_suspended(snap_src->ti)) { - DMERR("Unable to perform snapshot handover until " - "source is suspended."); + DMERR("Unable to perform snapshot handover until source is suspended."); r = -EINVAL; } up_read(&snap_src->lock); diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index a81ed080730a..547aefe85c07 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -108,15 +108,13 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) width = ti->len; if (sector_div(width, stripes)) { - ti->error = "Target length not divisible by " - "number of stripes"; + ti->error = "Target length not divisible by number of stripes"; return -EINVAL; } tmp_len = width; if (sector_div(tmp_len, chunk_size)) { - ti->error = "Target length not divisible by " - "chunk size"; + ti->error = "Target length not divisible by chunk size"; return -EINVAL; } @@ -124,15 +122,13 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) * Do we have enough arguments for that many stripes ? */ if (argc != (2 + 2 * stripes)) { - ti->error = "Not enough destinations " - "specified"; + ti->error = "Not enough destinations specified"; return -EINVAL; } sc = kmalloc(struct_size(sc, stripe, stripes), GFP_KERNEL); if (!sc) { - ti->error = "Memory allocation for striped context " - "failed"; + ti->error = "Memory allocation for striped context failed"; return -ENOMEM; } diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 337e667323c4..288f600ee56d 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -234,8 +234,7 @@ static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev, return 0; if ((start >= dev_size) || (start + len > dev_size)) { - DMERR("%s: %pg too small for target: " - "start=%llu, len=%llu, dev_size=%llu", + DMERR("%s: %pg too small for target: start=%llu, len=%llu, dev_size=%llu", dm_device_name(ti->table->md), bdev, (unsigned long long)start, (unsigned long long)len, @@ -280,8 +279,7 @@ static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev, return 0; if (start & (logical_block_size_sectors - 1)) { - DMERR("%s: start=%llu not aligned to h/w " - "logical block size %u of %pg", + DMERR("%s: start=%llu not aligned to h/w logical block size %u of %pg", dm_device_name(ti->table->md), (unsigned long long)start, limits->logical_block_size, bdev); @@ -289,8 +287,7 @@ static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev, } if (len & (logical_block_size_sectors - 1)) { - DMERR("%s: len=%llu not aligned to h/w " - "logical block size %u of %pg", + DMERR("%s: len=%llu not aligned to h/w logical block size %u of %pg", dm_device_name(ti->table->md), (unsigned long long)len, limits->logical_block_size, bdev); @@ -880,8 +877,7 @@ static int dm_table_determine_type(struct dm_table *t) bio_based = 1; if (bio_based && request_based) { - DMERR("Inconsistent table: different target types" - " can't be mixed up"); + DMERR("Inconsistent table: different target types can't be mixed up"); return -EINVAL; } } @@ -1184,8 +1180,7 @@ static int dm_table_register_integrity(struct dm_table *t) * profile the new profile should not conflict. */ if (blk_integrity_compare(dm_disk(md), template_disk) < 0) { - DMERR("%s: conflict with existing integrity profile: " - "%s profile mismatch", + DMERR("%s: conflict with existing integrity profile: %s profile mismatch", dm_device_name(t->md), template_disk->disk_name); return 1; @@ -1698,8 +1693,7 @@ combine_limits: * for the table. */ if (blk_stack_limits(limits, &ti_limits, 0) < 0) - DMWARN("%s: adding target device " - "(start sect %llu len %llu) " + DMWARN("%s: adding target device (start sect %llu len %llu) " "caused an alignment inconsistency", dm_device_name(t->md), (unsigned long long) ti->begin, diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 59eb1cb7037a..4a0e15109997 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -265,15 +265,15 @@ static int sb_check(struct dm_block_validator *v, __le32 csum_le; if (dm_block_location(b) != le64_to_cpu(disk_super->blocknr)) { - DMERR("sb_check failed: blocknr %llu: " - "wanted %llu", le64_to_cpu(disk_super->blocknr), + DMERR("sb_check failed: blocknr %llu: wanted %llu", + le64_to_cpu(disk_super->blocknr), (unsigned long long)dm_block_location(b)); return -ENOTBLK; } if (le64_to_cpu(disk_super->magic) != THIN_SUPERBLOCK_MAGIC) { - DMERR("sb_check failed: magic %llu: " - "wanted %llu", le64_to_cpu(disk_super->magic), + DMERR("sb_check failed: magic %llu: wanted %llu", + le64_to_cpu(disk_super->magic), (unsigned long long)THIN_SUPERBLOCK_MAGIC); return -EILSEQ; } diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index 13070c25adc3..0548b5d925f7 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -1014,10 +1014,8 @@ static int dmz_check_sb(struct dmz_metadata *zmd, struct dmz_sb *dsb, sb_block = le64_to_cpu(sb->sb_block); if (sb_block != (u64)dsb->zone->id << zmd->zone_nr_blocks_shift) { - dmz_dev_err(dev, "Invalid superblock position " - "(is %llu expected %llu)", - sb_block, - (u64)dsb->zone->id << zmd->zone_nr_blocks_shift); + dmz_dev_err(dev, "Invalid superblock position (is %llu expected %llu)", + sb_block, (u64)dsb->zone->id << zmd->zone_nr_blocks_shift); return -EINVAL; } if (zmd->sb_version > 1) { @@ -1030,16 +1028,14 @@ static int dmz_check_sb(struct dmz_metadata *zmd, struct dmz_sb *dsb, } else if (uuid_is_null(&zmd->uuid)) { uuid_copy(&zmd->uuid, &sb_uuid); } else if (!uuid_equal(&zmd->uuid, &sb_uuid)) { - dmz_dev_err(dev, "mismatching DM-Zoned uuid, " - "is %pUl expected %pUl", + dmz_dev_err(dev, "mismatching DM-Zoned uuid, is %pUl expected %pUl", &sb_uuid, &zmd->uuid); return -ENXIO; } if (!strlen(zmd->label)) memcpy(zmd->label, sb->dmz_label, BDEVNAME_SIZE); else if (memcmp(zmd->label, sb->dmz_label, BDEVNAME_SIZE)) { - dmz_dev_err(dev, "mismatching DM-Zoned label, " - "is %s expected %s", + dmz_dev_err(dev, "mismatching DM-Zoned label, is %s expected %s", sb->dmz_label, zmd->label); return -ENXIO; } -- cgit 1.4.1 From 27987794197e71309cb830944a849f470bd20431 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Tue, 7 Feb 2023 21:47:45 +0100 Subject: dm ioctl: have constant on the right side of the test [ Upstream commit 5cae0aa77397015f530aeb34f3ced32db6ac2875 ] Signed-off-by: Heinz Mauelshagen Signed-off-by: Mike Snitzer Stable-dep-of: 249bed821b4d ("dm ioctl: Avoid double-fetch of version") Signed-off-by: Sasha Levin --- drivers/md/dm-ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 2ced382cdd70..6aeae095086d 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1819,8 +1819,8 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user) if (copy_from_user(version, user->version, sizeof(version))) return -EFAULT; - if ((DM_VERSION_MAJOR != version[0]) || - (DM_VERSION_MINOR < version[1])) { + if ((version[0] != DM_VERSION_MAJOR) || + (version[1] > DM_VERSION_MINOR)) { DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, -- cgit 1.4.1 From d5eb0375d78a56d08e9fd5fa3da60c2a05ff038c Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Sat, 3 Jun 2023 10:52:42 -0400 Subject: dm ioctl: Avoid double-fetch of version [ Upstream commit 249bed821b4db6d95a99160f7d6d236ea5fe6362 ] The version is fetched once in check_version(), which then does some validation and then overwrites the version in userspace with the API version supported by the kernel. copy_params() then fetches the version from userspace *again*, and this time no validation is done. The result is that the kernel's version number is completely controllable by userspace, provided that userspace can win a race condition. Fix this flaw by not copying the version back to the kernel the second time. This is not exploitable as the version is not further used in the kernel. However, it could become a problem if future patches start relying on the version field. Cc: stable@vger.kernel.org Signed-off-by: Demi Marie Obenour Signed-off-by: Mike Snitzer Signed-off-by: Sasha Levin --- drivers/md/dm-ioctl.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 6aeae095086d..2afd2d2a0f40 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1811,30 +1811,36 @@ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags) * As well as checking the version compatibility this always * copies the kernel interface version out. */ -static int check_version(unsigned int cmd, struct dm_ioctl __user *user) +static int check_version(unsigned int cmd, struct dm_ioctl __user *user, + struct dm_ioctl *kernel_params) { - uint32_t version[3]; int r = 0; - if (copy_from_user(version, user->version, sizeof(version))) + /* Make certain version is first member of dm_ioctl struct */ + BUILD_BUG_ON(offsetof(struct dm_ioctl, version) != 0); + + if (copy_from_user(kernel_params->version, user->version, sizeof(kernel_params->version))) return -EFAULT; - if ((version[0] != DM_VERSION_MAJOR) || - (version[1] > DM_VERSION_MINOR)) { + if ((kernel_params->version[0] != DM_VERSION_MAJOR) || + (kernel_params->version[1] > DM_VERSION_MINOR)) { DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, - version[0], version[1], version[2], cmd); + kernel_params->version[0], + kernel_params->version[1], + kernel_params->version[2], + cmd); r = -EINVAL; } /* * Fill in the kernel version. */ - version[0] = DM_VERSION_MAJOR; - version[1] = DM_VERSION_MINOR; - version[2] = DM_VERSION_PATCHLEVEL; - if (copy_to_user(user->version, version, sizeof(version))) + kernel_params->version[0] = DM_VERSION_MAJOR; + kernel_params->version[1] = DM_VERSION_MINOR; + kernel_params->version[2] = DM_VERSION_PATCHLEVEL; + if (copy_to_user(user->version, kernel_params->version, sizeof(kernel_params->version))) return -EFAULT; return r; @@ -1860,7 +1866,10 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kern const size_t minimum_data_size = offsetof(struct dm_ioctl, data); unsigned int noio_flag; - if (copy_from_user(param_kernel, user, minimum_data_size)) + /* check_version() already copied version from userspace, avoid TOCTOU */ + if (copy_from_user((char *)param_kernel + sizeof(param_kernel->version), + (char __user *)user + sizeof(param_kernel->version), + minimum_data_size - sizeof(param_kernel->version))) return -EFAULT; if (param_kernel->data_size < minimum_data_size) { @@ -1972,7 +1981,7 @@ static int ctl_ioctl(struct file *file, uint command, struct dm_ioctl __user *us * Check the interface version passed in. This also * writes out the kernel's interface version. */ - r = check_version(cmd, user); + r = check_version(cmd, user, ¶m_kernel); if (r) return r; -- cgit 1.4.1 From ee08e1fc9480b45b49eef4b3328db09db211dd1e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 18 Nov 2022 23:35:44 +0100 Subject: extcon: usbc-tusb320: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5313121b22fd11db0d14f305c110168b8176efdc ] The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Signed-off-by: Chanwoo Choi Stable-dep-of: 3adbaa30d973 ("extcon: usbc-tusb320: Unregister typec port on driver removal") Signed-off-by: Sasha Levin --- drivers/extcon/extcon-usbc-tusb320.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index 9dfa545427ca..b408ce989c22 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -428,8 +428,7 @@ static int tusb320_typec_probe(struct i2c_client *client, return 0; } -static int tusb320_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tusb320_probe(struct i2c_client *client) { struct tusb320_priv *priv; const void *match_data; @@ -502,7 +501,7 @@ static const struct of_device_id tusb320_extcon_dt_match[] = { MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); static struct i2c_driver tusb320_extcon_driver = { - .probe = tusb320_probe, + .probe_new = tusb320_probe, .driver = { .name = "extcon-tusb320", .of_match_table = tusb320_extcon_dt_match, -- cgit 1.4.1 From 2445a35d05cdf24d27713b32d920a953de0f2405 Mon Sep 17 00:00:00 2001 From: Alvin Šipraga Date: Wed, 15 Mar 2023 15:15:47 +0100 Subject: extcon: usbc-tusb320: Unregister typec port on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3adbaa30d973093a4f37927baf9596cca51b593d ] The driver can register a typec port if suitable firmware properties are present. But if the driver is removed through sysfs unbind, rmmod or similar, then it does not clean up after itself and the typec port device remains registered. This can be seen in sysfs, where stale typec ports get left over in /sys/class/typec. In order to fix this we have to add an i2c_driver remove function and call typec_unregister_port(), which is a no-op in the case where no typec port is created and the pointer remains NULL. In the process we should also put the fwnode_handle when the typec port isn't registered anymore, including if an error occurs during probe. The typec subsystem does not increase or decrease the reference counter for us, so we track it in the driver's private data. Note that the conditional check on TYPEC_PWR_MODE_PD was removed in the probe path because a call to tusb320_set_adv_pwr_mode() will perform an even more robust validation immediately after, hence there is no functional change here. Fixes: bf7571c00dca ("extcon: usbc-tusb320: Add USB TYPE-C support") Cc: stable@vger.kernel.org Signed-off-by: Alvin Šipraga Signed-off-by: Chanwoo Choi Signed-off-by: Sasha Levin --- drivers/extcon/extcon-usbc-tusb320.c | 42 +++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index b408ce989c22..10dff1c512c4 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -78,6 +78,7 @@ struct tusb320_priv { struct typec_capability cap; enum typec_port_type port_type; enum typec_pwr_opmode pwr_opmode; + struct fwnode_handle *connector_fwnode; }; static const char * const tusb_attached_states[] = { @@ -391,27 +392,25 @@ static int tusb320_typec_probe(struct i2c_client *client, /* Type-C connector found. */ ret = typec_get_fw_cap(&priv->cap, connector); if (ret) - return ret; + goto err_put; priv->port_type = priv->cap.type; /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */ ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str); if (ret) - return ret; + goto err_put; ret = typec_find_pwr_opmode(cap_str); if (ret < 0) - return ret; - if (ret == TYPEC_PWR_MODE_PD) - return -EINVAL; + goto err_put; priv->pwr_opmode = ret; /* Initialize the hardware with the devicetree settings. */ ret = tusb320_set_adv_pwr_mode(priv); if (ret) - return ret; + goto err_put; priv->cap.revision = USB_TYPEC_REV_1_1; priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO; @@ -422,10 +421,25 @@ static int tusb320_typec_probe(struct i2c_client *client, priv->cap.fwnode = connector; priv->port = typec_register_port(&client->dev, &priv->cap); - if (IS_ERR(priv->port)) - return PTR_ERR(priv->port); + if (IS_ERR(priv->port)) { + ret = PTR_ERR(priv->port); + goto err_put; + } + + priv->connector_fwnode = connector; return 0; + +err_put: + fwnode_handle_put(connector); + + return ret; +} + +static void tusb320_typec_remove(struct tusb320_priv *priv) +{ + typec_unregister_port(priv->port); + fwnode_handle_put(priv->connector_fwnode); } static int tusb320_probe(struct i2c_client *client) @@ -438,7 +452,9 @@ static int tusb320_probe(struct i2c_client *client) priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->dev = &client->dev; + i2c_set_clientdata(client, priv); priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config); if (IS_ERR(priv->regmap)) @@ -489,10 +505,19 @@ static int tusb320_probe(struct i2c_client *client) tusb320_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, priv); + if (ret) + tusb320_typec_remove(priv); return ret; } +static void tusb320_remove(struct i2c_client *client) +{ + struct tusb320_priv *priv = i2c_get_clientdata(client); + + tusb320_typec_remove(priv); +} + static const struct of_device_id tusb320_extcon_dt_match[] = { { .compatible = "ti,tusb320", .data = &tusb320_ops, }, { .compatible = "ti,tusb320l", .data = &tusb320l_ops, }, @@ -502,6 +527,7 @@ MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); static struct i2c_driver tusb320_extcon_driver = { .probe_new = tusb320_probe, + .remove = tusb320_remove, .driver = { .name = "extcon-tusb320", .of_match_table = tusb320_extcon_dt_match, -- cgit 1.4.1 From b990e3760395d346592d4e7370cefc85aa7fc597 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 8 Jun 2023 11:27:41 +0100 Subject: btrfs: do not BUG_ON() on tree mod log failure at balance_level() [ Upstream commit 39020d8abc7ec62c4de9b260e3d10d4a1c2478ce ] At balance_level(), instead of doing a BUG_ON() in case we fail to record tree mod log operations, do a transaction abort and return the error to the callers. There's really no need for the BUG_ON() as we can release all resources in this context, and we have to abort because other future tree searches that use the tree mod log (btrfs_search_old_slot()) may get inconsistent results if other operations modify the tree after that failure and before the tree mod log based search. CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/ctree.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index ab9f8d6c4f1b..9a20b9e3ebd0 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -935,7 +935,12 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, } ret = btrfs_tree_mod_log_insert_root(root->node, child, true); - BUG_ON(ret < 0); + if (ret < 0) { + btrfs_tree_unlock(child); + free_extent_buffer(child); + btrfs_abort_transaction(trans, ret); + goto enospc; + } rcu_assign_pointer(root->node, child); add_root_to_dirty_list(root); @@ -1017,7 +1022,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, btrfs_node_key(right, &right_key, 0); ret = btrfs_tree_mod_log_insert_key(parent, pslot + 1, BTRFS_MOD_LOG_KEY_REPLACE, GFP_NOFS); - BUG_ON(ret < 0); + if (ret < 0) { + btrfs_abort_transaction(trans, ret); + goto enospc; + } btrfs_set_node_key(parent, &right_key, pslot + 1); btrfs_mark_buffer_dirty(parent); } @@ -1063,7 +1071,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, btrfs_node_key(mid, &mid_key, 0); ret = btrfs_tree_mod_log_insert_key(parent, pslot, BTRFS_MOD_LOG_KEY_REPLACE, GFP_NOFS); - BUG_ON(ret < 0); + if (ret < 0) { + btrfs_abort_transaction(trans, ret); + goto enospc; + } btrfs_set_node_key(parent, &mid_key, pslot); btrfs_mark_buffer_dirty(parent); } -- cgit 1.4.1 From 8753eeb2d35ab7fd84da7606a5a918940b6cbf7b Mon Sep 17 00:00:00 2001 From: Shuai Jiang Date: Tue, 18 Apr 2023 21:56:12 +0800 Subject: i2c: qup: Add missing unwind goto in qup_i2c_probe() commit cd9489623c29aa2f8cc07088168afb6e0d5ef06d upstream. Smatch Warns: drivers/i2c/busses/i2c-qup.c:1784 qup_i2c_probe() warn: missing unwind goto? The goto label "fail_runtime" and "fail" will disable qup->pclk, but here qup->pclk failed to obtain, in order to be consistent, change the direct return to goto label "fail_dma". Fixes: 9cedf3b2f099 ("i2c: qup: Add bam dma capabilities") Signed-off-by: Shuai Jiang Reviewed-by: Dongliang Mu Reviewed-by: Andi Shyti Signed-off-by: Wolfram Sang Cc: # v4.6+ Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-qup.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 2e153f2f71b6..78682388e02e 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1752,16 +1752,21 @@ nodma: if (!clk_freq || clk_freq > I2C_MAX_FAST_MODE_PLUS_FREQ) { dev_err(qup->dev, "clock frequency not supported %d\n", clk_freq); - return -EINVAL; + ret = -EINVAL; + goto fail_dma; } qup->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(qup->base)) - return PTR_ERR(qup->base); + if (IS_ERR(qup->base)) { + ret = PTR_ERR(qup->base); + goto fail_dma; + } qup->irq = platform_get_irq(pdev, 0); - if (qup->irq < 0) - return qup->irq; + if (qup->irq < 0) { + ret = qup->irq; + goto fail_dma; + } if (has_acpi_companion(qup->dev)) { ret = device_property_read_u32(qup->dev, @@ -1775,13 +1780,15 @@ nodma: qup->clk = devm_clk_get(qup->dev, "core"); if (IS_ERR(qup->clk)) { dev_err(qup->dev, "Could not get core clock\n"); - return PTR_ERR(qup->clk); + ret = PTR_ERR(qup->clk); + goto fail_dma; } qup->pclk = devm_clk_get(qup->dev, "iface"); if (IS_ERR(qup->pclk)) { dev_err(qup->dev, "Could not get iface clock\n"); - return PTR_ERR(qup->pclk); + ret = PTR_ERR(qup->pclk); + goto fail_dma; } qup_i2c_enable_clocks(qup); src_clk_freq = clk_get_rate(qup->clk); -- cgit 1.4.1 From f672f2ca9d6f02f599f2b8ede9a4cdae137a08e0 Mon Sep 17 00:00:00 2001 From: Liu Peibao Date: Wed, 14 Jun 2023 19:59:33 +0800 Subject: irqchip/loongson-pch-pic: Fix potential incorrect hwirq assignment commit 783422e704ca0fa41cb2fe9ed79e46b6fe7eae29 upstream. In DeviceTree path, when ht_vec_base is not zero, the hwirq of PCH PIC will be assigned incorrectly. Because when pch_pic_domain_translate() adds the ht_vec_base to hwirq, the hwirq does not have the ht_vec_base subtracted when calling irq_domain_set_info(). The ht_vec_base is designed for the parent irq chip/domain of the PCH PIC. It seems not proper to deal this in callbacks of the PCH PIC domain and let's put this back like the initial commit ef8c01eb64ca ("irqchip: Add Loongson PCH PIC controller"). Fixes: bcdd75c596c8 ("irqchip/loongson-pch-pic: Add ACPI init support") Cc: stable@vger.kernel.org Reviewed-by: Huacai Chen Signed-off-by: Liu Peibao Signed-off-by: Jianmin Lv Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230614115936.5950-3-lvjianmin@loongson.cn Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-loongson-pch-pic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c index 679e2b68e6e9..46be04d13249 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -162,7 +162,7 @@ static int pch_pic_domain_translate(struct irq_domain *d, if (fwspec->param_count < 2) return -EINVAL; - *hwirq = fwspec->param[0] + priv->ht_vec_base; + *hwirq = fwspec->param[0]; *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; } else { *hwirq = fwspec->param[0] - priv->gsi_base; @@ -188,7 +188,7 @@ static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq, parent_fwspec.fwnode = domain->parent->fwnode; parent_fwspec.param_count = 1; - parent_fwspec.param[0] = hwirq; + parent_fwspec.param[0] = hwirq + priv->ht_vec_base; err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec); if (err) -- cgit 1.4.1 From 2b4e43b5ad99498bdc3d74cdcb2ae510fe334a5b Mon Sep 17 00:00:00 2001 From: Dai Ngo Date: Tue, 6 Jun 2023 16:41:02 -0700 Subject: NFSD: add encoding of op_recall flag for write delegation commit 58f5d894006d82ed7335e1c37182fbc5f08c2f51 upstream. Modified nfsd4_encode_open to encode the op_recall flag properly for OPEN result with write delegation granted. Signed-off-by: Dai Ngo Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4xdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 8f5b41dc0773..8f90a87ee9ca 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3872,7 +3872,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op p = xdr_reserve_space(xdr, 32); if (!p) return nfserr_resource; - *p++ = cpu_to_be32(0); + *p++ = cpu_to_be32(open->op_recall); /* * TODO: space_limit's in delegations -- cgit 1.4.1 From 9440b24fbcb969a71c65703bd9cc0b3ecd5c756c Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Wed, 14 Jun 2023 19:59:32 +0800 Subject: irqchip/loongson-pch-pic: Fix initialization of HT vector register commit f679616565f1cf1a4acb245dbc0032dafcd40637 upstream. In an ACPI-based dual-bridge system, IRQ of each bridge's PCH PIC sent to CPU is always a zero-based number, which means that the IRQ on PCH PIC of each bridge is mapped into vector range from 0 to 63 of upstream irqchip(e.g. EIOINTC). EIOINTC N: [0 ... 63 | 64 ... 255] -------- ---------- ^ ^ | | PCH PIC N | PCH MSI N For example, the IRQ vector number of sata controller on PCH PIC of each bridge is 16, which is sent to upstream irqchip of EIOINTC when an interrupt occurs, which will set bit 16 of EIOINTC. Since hwirq of 16 on EIOINTC has been mapped to a irq_desc for sata controller during hierarchy irq allocation, the related mapped IRQ will be found through irq_resolve_mapping() in the IRQ domain of EIOINTC. So, the IRQ number set in HT vector register should be fixed to be a zero-based number. Cc: stable@vger.kernel.org Reviewed-by: Huacai Chen Co-developed-by: liuyun Signed-off-by: liuyun Signed-off-by: Jianmin Lv Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230614115936.5950-2-lvjianmin@loongson.cn Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-loongson-pch-pic.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c index 46be04d13249..5839395099d6 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -350,14 +350,12 @@ static int __init acpi_cascade_irqdomain_init(void) int __init pch_pic_acpi_init(struct irq_domain *parent, struct acpi_madt_bio_pic *acpi_pchpic) { - int ret, vec_base; + int ret; struct fwnode_handle *domain_handle; if (find_pch_pic(acpi_pchpic->gsi_base) >= 0) return 0; - vec_base = acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ; - domain_handle = irq_domain_alloc_fwnode(&acpi_pchpic->address); if (!domain_handle) { pr_err("Unable to allocate domain handle\n"); @@ -365,7 +363,7 @@ int __init pch_pic_acpi_init(struct irq_domain *parent, } ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size, - vec_base, parent, domain_handle, acpi_pchpic->gsi_base); + 0, parent, domain_handle, acpi_pchpic->gsi_base); if (ret < 0) { irq_domain_free_fwnode(domain_handle); -- cgit 1.4.1 From b50d6e06cca7b67a3d73ca660dda27662b76e6ea Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 11 Jun 2023 21:14:09 -0600 Subject: io_uring: wait interruptibly for request completions on exit commit 4826c59453b3b4677d6bf72814e7ababdea86949 upstream. WHen the ring exits, cleanup is done and the final cancelation and waiting on completions is done by io_ring_exit_work. That function is invoked by kworker, which doesn't take any signals. Because of that, it doesn't really matter if we wait for completions in TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE state. However, it does matter to the hung task detection checker! Normally we expect cancelations and completions to happen rather quickly. Some test cases, however, will exit the ring and park the owning task stopped (eg via SIGSTOP). If the owning task needs to run task_work to complete requests, then io_ring_exit_work won't make any progress until the task is runnable again. Hence io_ring_exit_work can trigger the hung task detection, which is particularly problematic if panic-on-hung-task is enabled. As the ring exit doesn't take signals to begin with, have it wait interruptibly rather than uninterruptibly. io_uring has a separate stuck-exit warning that triggers independently anyway, so we're not really missing anything by making this switch. Cc: stable@vger.kernel.org # 5.10+ Link: https://lore.kernel.org/r/b0e4aaef-7088-56ce-244c-976edeac0e66@kernel.dk Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/io_uring.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index cc35aba1e495..a85d8879c070 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -2748,7 +2748,18 @@ static __cold void io_ring_exit_work(struct work_struct *work) /* there is little hope left, don't run it too often */ interval = HZ * 60; } - } while (!wait_for_completion_timeout(&ctx->ref_comp, interval)); + /* + * This is really an uninterruptible wait, as it has to be + * complete. But it's also run from a kworker, which doesn't + * take signals, so it's fine to make it interruptible. This + * avoids scenarios where we knowingly can wait much longer + * on completions, for example if someone does a SIGSTOP on + * a task that needs to finish task_work to make this loop + * complete. That's a synthetic situation that should not + * cause a stuck task backtrace, and hence a potential panic + * on stuck tasks if that is enabled. + */ + } while (!wait_for_completion_interruptible_timeout(&ctx->ref_comp, interval)); init_completion(&exit.completion); init_task_work(&exit.task_work, io_tctx_exit_cb); @@ -2772,7 +2783,12 @@ static __cold void io_ring_exit_work(struct work_struct *work) continue; mutex_unlock(&ctx->uring_lock); - wait_for_completion(&exit.completion); + /* + * See comment above for + * wait_for_completion_interruptible_timeout() on why this + * wait is marked as interruptible. + */ + wait_for_completion_interruptible(&exit.completion); mutex_lock(&ctx->uring_lock); } mutex_unlock(&ctx->uring_lock); -- cgit 1.4.1 From 182bf07a24c4c1022bf83c90be619d60427745c3 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 19 Jun 2023 21:35:58 +0200 Subject: mmc: core: disable TRIM on Kingston EMMC04G-M627 commit f1738a1f816233e6dfc2407f24a31d596643fd90 upstream. It seems that Kingston EMMC04G-M627 despite advertising TRIM support does not work when the core is trying to use REQ_OP_WRITE_ZEROES. We are seeing I/O errors in OpenWrt under 6.1 on Zyxel NBG7815 that we did not previously have and tracked it down to REQ_OP_WRITE_ZEROES. Trying to use fstrim seems to also throw errors like: [93010.835112] I/O error, dev loop0, sector 16902 op 0x3:(DISCARD) flags 0x800 phys_seg 1 prio class 2 Disabling TRIM makes the error go away, so lets add a quirk for this eMMC to disable TRIM. Signed-off-by: Robert Marko Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230619193621.437358-1-robimarko@gmail.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/quirks.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index a7ffbc930ea9..6640b587ca07 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -109,6 +109,13 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_TRIM_BROKEN), + /* + * Kingston EMMC04G-M627 advertises TRIM but it does not seems to + * support being used to offload WRITE_ZEROES. + */ + MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), + /* * Some SD cards reports discard support while they don't */ -- cgit 1.4.1 From 69bc3203513c4fcd6ca84b0185de454bacf87883 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 30 May 2023 23:32:59 +0200 Subject: mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M commit dbfbddcddcebc9ce8a08757708d4e4a99d238e44 upstream. It seems that Micron MTFC4GACAJCN-1M despite advertising TRIM support does not work when the core is trying to use REQ_OP_WRITE_ZEROES. We are seeing the following errors in OpenWrt under 6.1 on Qnap Qhora 301W that we did not previously have and tracked it down to REQ_OP_WRITE_ZEROES: [ 18.085950] I/O error, dev loop0, sector 596 op 0x9:(WRITE_ZEROES) flags 0x800 phys_seg 0 prio class 2 Disabling TRIM makes the error go away, so lets add a quirk for this eMMC to disable TRIM. Signed-off-by: Robert Marko Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230530213259.1776512-1-robimarko@gmail.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/quirks.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 6640b587ca07..857315f185fc 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -116,6 +116,13 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, MMC_QUIRK_TRIM_BROKEN), + /* + * Micron MTFC4GACAJCN-1M advertises TRIM but it does not seems to + * support being used to offload WRITE_ZEROES. + */ + MMC_FIXUP("Q2J54A", CID_MANFID_MICRON, 0x014e, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), + /* * Some SD cards reports discard support while they don't */ -- cgit 1.4.1 From 30c5f362b6f5445def8352510e5e122a9a762e09 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 12 Jun 2023 16:37:30 +0200 Subject: mmc: mmci: Set PROBE_PREFER_ASYNCHRONOUS commit 3108eb2e8aa7e955a9dd3a4c1bf19a7898961822 upstream. All mmc host drivers should have the asynchronous probe option enabled, but it seems like we failed to set it for mmci, so let's do that now. Fixes: 21b2cec61c04 ("mmc: Set PROBE_PREFER_ASYNCHRONOUS for drivers that existed in v4.4") Signed-off-by: Ulf Hansson Tested-by: Linus Walleij Tested-by: Yann Gautier Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230612143730.210390-1-ulf.hansson@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/mmci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 1c326e4307f4..9728b093f4db 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2456,6 +2456,7 @@ static struct amba_driver mmci_driver = { .drv = { .name = DRIVER_NAME, .pm = &mmci_dev_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = mmci_probe, .remove = mmci_remove, -- cgit 1.4.1 From 5b2b6586c5db61267f183907f7b6fba0c2b92f3e Mon Sep 17 00:00:00 2001 From: Chevron Li Date: Tue, 23 May 2023 19:11:14 +0800 Subject: mmc: sdhci: fix DMA configure compatibility issue when 64bit DMA mode is used. commit 20dbd07ef0a8bc29eb03d6a95258ac8934cbe52d upstream. Bayhub SD host has hardware limitation: 1.The upper 32bit address is inhibited to be written at SD Host Register [03E][13]=0 (32bits addressing) mode, is admitted to be written only at SD Host Register [03E][13]=1 (64bits addressing) mode. 2.Because of above item#1, need to configure SD Host Register [03E][13] to 1(64bits addressing mode) before set 64bit ADMA system address's higher 32bits SD Host Register [05F~05C] if 64 bits addressing mode is used. The hardware limitation is reasonable for below reasons: 1.Normal flow should set DMA working mode first, then do DMA-transfer-related configuration, such as system address. 2.The hardware limitation may avoid the software to configure wrong higher 32bit address at 32bits addressing mode although it is redundant. The change that set 32bits/64bits addressing mode before set ADMA address, has no side-effect to other host IPs for below reason: The setting order is reasonable and standard: DMA Mode setting first and then DMA address setting. It meets all DMA setting sequence. Signed-off-by: Chevron Li Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230523111114.18124-1-chevron_li@126.com Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 632341911b6e..ad73d528a1bd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1181,6 +1181,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } } + sdhci_config_dma(host); + if (host->flags & SDHCI_REQ_USE_DMA) { int sg_cnt = sdhci_pre_dma_transfer(host, data, COOKIE_MAPPED); @@ -1200,8 +1202,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } } - sdhci_config_dma(host); - if (!(host->flags & SDHCI_REQ_USE_DMA)) { int flags; -- cgit 1.4.1 From 1a312d5a8c846bf0a1934759e7cb179aa6e5826f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 16 Jun 2023 22:28:44 +0200 Subject: wifi: cfg80211: fix regulatory disconnect for non-MLO commit b22552fcaf1970360005c805d7fba4046cf2ab4a upstream. The multi-link loop here broke disconnect when multi-link operation (MLO) isn't active for a given interface, since in that case valid_links is 0 (indicating no links, i.e. no MLO.) Fix this by taking that into account properly and skipping the link only if there are valid_links in the first place. Cc: stable@vger.kernel.org Fixes: 7b0a0e3c3a88 ("wifi: cfg80211: do some rework towards MLO link APIs") Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20230616222844.eb073d650c75.I72739923ef80919889ea9b50de9e4ba4baa836ae@changeid Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3b44fa59dbba..5da1a641ef17 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2390,7 +2390,7 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) if (!wdev->valid_links && link > 0) break; - if (!(wdev->valid_links & BIT(link))) + if (wdev->valid_links && !(wdev->valid_links & BIT(link))) continue; switch (iftype) { case NL80211_IFTYPE_AP: -- cgit 1.4.1 From d8985a0e44bc56a44db2b789a221dc7fd157ef66 Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Thu, 23 Mar 2023 17:55:27 +0100 Subject: wifi: ath10k: Serialize wake_tx_queue ops commit b719ebc37a1eacd4fd4f1264f731b016e5ec0c6e upstream. Serialize the ath10k implementation of the wake_tx_queue ops. ath10k_mac_op_wake_tx_queue() must not run concurrent since it's using ieee80211_txq_schedule_start(). The intend of this patch is to sort out an issue discovered in the discussion referred to by the Link tag. I can't test it with real hardware and thus just implemented the per-ac queue lock Felix suggested. One obvious alternative to the per-ac lock would be to bring back the txqs_lock commit bb2edb733586 ("ath10k: migrate to mac80211 txq scheduling") dropped. Fixes: bb2edb733586 ("ath10k: migrate to mac80211 txq scheduling") Reported-by: Felix Fietkau Link: https://lore.kernel.org/r/519b5bb9-8899-ae7c-4eff-f3116cdfdb56@nbd.name CC: Signed-off-by: Alexander Wetzel Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230323165527.156414-1-alexander@wetzel-home.de Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath10k/core.c | 3 +++ drivers/net/wireless/ath/ath10k/core.h | 3 +++ drivers/net/wireless/ath/ath10k/mac.c | 6 ++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b6052dcc45eb..6cdb225b7eac 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -3634,6 +3634,9 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, mutex_init(&ar->dump_mutex); spin_lock_init(&ar->data_lock); + for (int ac = 0; ac < IEEE80211_NUM_ACS; ac++) + spin_lock_init(&ar->queue_lock[ac]); + INIT_LIST_HEAD(&ar->peers); init_waitqueue_head(&ar->peer_mapping_wq); init_waitqueue_head(&ar->htt.empty_tx_wq); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index f5de8ce8fb45..4b5239de4018 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -1170,6 +1170,9 @@ struct ath10k { /* protects shared structure data */ spinlock_t data_lock; + /* serialize wake_tx_queue calls per ac */ + spinlock_t queue_lock[IEEE80211_NUM_ACS]; + struct list_head arvifs; struct list_head peers; struct ath10k_peer *peer_map[ATH10K_MAX_NUM_PEER_IDS]; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index f0729acdec50..ec5c54672dfe 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4732,13 +4732,14 @@ static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; int ret; - u8 ac; + u8 ac = txq->ac; ath10k_htt_tx_txq_update(hw, txq); if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH) return; - ac = txq->ac; + spin_lock_bh(&ar->queue_lock[ac]); + ieee80211_txq_schedule_start(hw, ac); txq = ieee80211_next_txq(hw, ac); if (!txq) @@ -4753,6 +4754,7 @@ static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw, ath10k_htt_tx_txq_update(hw, txq); out: ieee80211_txq_schedule_end(hw, ac); + spin_unlock_bh(&ar->queue_lock[ac]); } /* Must not be called with conf_mutex held as workers can use that also. */ -- cgit 1.4.1 From 97ccc14d114b1cf3bc16670fa09f74ec7233b643 Mon Sep 17 00:00:00 2001 From: Quan Zhou Date: Wed, 5 Jul 2023 23:26:38 +0800 Subject: wifi: mt76: mt7921e: fix init command fail with enabled device commit 525c469e5de9bf7e53574396196e80fc716ac9eb upstream. For some cases as below, we may encounter the unpreditable chip stats in driver probe() * The system reboot flow do not work properly, such as kernel oops while rebooting, and then the driver do not go back to default status at this moment. * Similar to the flow above. If the device was enabled in BIOS or UEFI, the system may switch to Linux without driver fully shutdown. To avoid the problem, force push the device back to default in probe() * mt7921e_mcu_fw_pmctrl() : return control privilege to chip side. * mt7921_wfsys_reset() : cleanup chip config before resource init. Error log [59007.600714] mt7921e 0000:02:00.0: ASIC revision: 79220010 [59010.889773] mt7921e 0000:02:00.0: Message 00000010 (seq 1) timeout [59010.889786] mt7921e 0000:02:00.0: Failed to get patch semaphore [59014.217839] mt7921e 0000:02:00.0: Message 00000010 (seq 2) timeout [59014.217852] mt7921e 0000:02:00.0: Failed to get patch semaphore [59017.545880] mt7921e 0000:02:00.0: Message 00000010 (seq 3) timeout [59017.545893] mt7921e 0000:02:00.0: Failed to get patch semaphore [59020.874086] mt7921e 0000:02:00.0: Message 00000010 (seq 4) timeout [59020.874099] mt7921e 0000:02:00.0: Failed to get patch semaphore [59024.202019] mt7921e 0000:02:00.0: Message 00000010 (seq 5) timeout [59024.202033] mt7921e 0000:02:00.0: Failed to get patch semaphore [59027.530082] mt7921e 0000:02:00.0: Message 00000010 (seq 6) timeout [59027.530096] mt7921e 0000:02:00.0: Failed to get patch semaphore [59030.857888] mt7921e 0000:02:00.0: Message 00000010 (seq 7) timeout [59030.857904] mt7921e 0000:02:00.0: Failed to get patch semaphore [59034.185946] mt7921e 0000:02:00.0: Message 00000010 (seq 8) timeout [59034.185961] mt7921e 0000:02:00.0: Failed to get patch semaphore [59037.514249] mt7921e 0000:02:00.0: Message 00000010 (seq 9) timeout [59037.514262] mt7921e 0000:02:00.0: Failed to get patch semaphore [59040.842362] mt7921e 0000:02:00.0: Message 00000010 (seq 10) timeout [59040.842375] mt7921e 0000:02:00.0: Failed to get patch semaphore [59040.923845] mt7921e 0000:02:00.0: hardware init failed Cc: stable@vger.kernel.org Fixes: 5c14a5f944b9 ("mt76: mt7921: introduce mt7921e support") Tested-by: Kai-Heng Feng Tested-by: Juan Martinez Co-developed-by: Leon Yen Signed-off-by: Leon Yen Signed-off-by: Quan Zhou Signed-off-by: Deren Wu Message-ID: <39fcb7cee08d4ab940d38d82f21897483212483f.1688569385.git.deren.wu@mediatek.com> Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt7921/dma.c | 4 ---- drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 8 -------- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 8 ++++++++ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c index fd57c87a29ae..7a305a4f292b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c @@ -231,10 +231,6 @@ int mt7921_dma_init(struct mt7921_dev *dev) if (ret) return ret; - ret = mt7921_wfsys_reset(dev); - if (ret) - return ret; - /* init tx queue */ ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7921_TXQ_BAND0, MT7921_TX_RING_SIZE, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index d3507e86e9cf..10dda1693d7d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -449,12 +449,6 @@ static int mt7921_load_firmware(struct mt7921_dev *dev) { int ret; - ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY); - if (ret && mt76_is_mmio(&dev->mt76)) { - dev_dbg(dev->mt76.dev, "Firmware is already download\n"); - goto fw_loaded; - } - ret = mt76_connac2_load_patch(&dev->mt76, mt7921_patch_name(dev)); if (ret) return ret; @@ -477,8 +471,6 @@ static int mt7921_load_firmware(struct mt7921_dev *dev) return -EIO; } -fw_loaded: - #ifdef CONFIG_PM dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support; #endif /* CONFIG_PM */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index c64b0b4e9358..b125694d6a2d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -316,6 +316,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, bus_ops->rmw = mt7921_rmw; dev->mt76.bus = bus_ops; + ret = mt7921e_mcu_fw_pmctrl(dev); + if (ret) + goto err_free_dev; + ret = __mt7921e_mcu_drv_pmctrl(dev); if (ret) goto err_free_dev; @@ -324,6 +328,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, (mt7921_l1_rr(dev, MT_HW_REV) & 0xff); dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + ret = mt7921_wfsys_reset(dev); + if (ret) + goto err_free_dev; + mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA, 0); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); -- cgit 1.4.1 From 25ec4779d0fb3ed9cac1e4d9e0e4261b4a12f6ed Mon Sep 17 00:00:00 2001 From: Mingzhe Zou Date: Thu, 15 Jun 2023 20:12:23 +0800 Subject: bcache: fixup btree_cache_wait list damage commit f0854489fc07d2456f7cc71a63f4faf9c716ffbe upstream. We get a kernel crash about "list_add corruption. next->prev should be prev (ffff9c801bc01210), but was ffff9c77b688237c. (next=ffffae586d8afe68)." crash> struct list_head 0xffff9c801bc01210 struct list_head { next = 0xffffae586d8afe68, prev = 0xffffae586d8afe68 } crash> struct list_head 0xffff9c77b688237c struct list_head { next = 0x0, prev = 0x0 } crash> struct list_head 0xffffae586d8afe68 struct list_head struct: invalid kernel virtual address: ffffae586d8afe68 type: "gdb_readmem_callback" Cannot access memory at address 0xffffae586d8afe68 [230469.019492] Call Trace: [230469.032041] prepare_to_wait+0x8a/0xb0 [230469.044363] ? bch_btree_keys_free+0x6c/0xc0 [escache] [230469.056533] mca_cannibalize_lock+0x72/0x90 [escache] [230469.068788] mca_alloc+0x2ae/0x450 [escache] [230469.080790] bch_btree_node_get+0x136/0x2d0 [escache] [230469.092681] bch_btree_check_thread+0x1e1/0x260 [escache] [230469.104382] ? finish_wait+0x80/0x80 [230469.115884] ? bch_btree_check_recurse+0x1a0/0x1a0 [escache] [230469.127259] kthread+0x112/0x130 [230469.138448] ? kthread_flush_work_fn+0x10/0x10 [230469.149477] ret_from_fork+0x35/0x40 bch_btree_check_thread() and bch_dirty_init_thread() may call mca_cannibalize() to cannibalize other cached btree nodes. Only one thread can do it at a time, so the op of other threads will be added to the btree_cache_wait list. We must call finish_wait() to remove op from btree_cache_wait before free it's memory address. Otherwise, the list will be damaged. Also should call bch_cannibalize_unlock() to release the btree_cache_alloc_lock and wake_up other waiters. Fixes: 8e7102273f59 ("bcache: make bch_btree_check() to be multithreaded") Fixes: b144e45fc576 ("bcache: make bch_sectors_dirty_init() to be multithreaded") Cc: stable@vger.kernel.org Signed-off-by: Mingzhe Zou Signed-off-by: Coly Li Link: https://lore.kernel.org/r/20230615121223.22502-7-colyli@suse.de Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/btree.c | 11 ++++++++++- drivers/md/bcache/btree.h | 1 + drivers/md/bcache/writeback.c | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 147c493a989a..df9986a7300d 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -885,7 +885,7 @@ static struct btree *mca_cannibalize(struct cache_set *c, struct btree_op *op, * cannibalize_bucket() will take. This means every time we unlock the root of * the btree, we need to release this lock if we have it held. */ -static void bch_cannibalize_unlock(struct cache_set *c) +void bch_cannibalize_unlock(struct cache_set *c) { spin_lock(&c->btree_cannibalize_lock); if (c->btree_cache_alloc_lock == current) { @@ -1968,6 +1968,15 @@ static int bch_btree_check_thread(void *arg) c->gc_stats.nodes++; bch_btree_op_init(&op, 0); ret = bcache_btree(check_recurse, p, c->root, &op); + /* + * The op may be added to cache_set's btree_cache_wait + * in mca_cannibalize(), must ensure it is removed from + * the list and release btree_cache_alloc_lock before + * free op memory. + * Otherwise, the btree_cache_wait will be damaged. + */ + bch_cannibalize_unlock(c); + finish_wait(&c->btree_cache_wait, &(&op)->wait); if (ret) goto out; } diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h index 1b5fdbc0d83e..a2920bbfcad5 100644 --- a/drivers/md/bcache/btree.h +++ b/drivers/md/bcache/btree.h @@ -282,6 +282,7 @@ void bch_initial_gc_finish(struct cache_set *c); void bch_moving_gc(struct cache_set *c); int bch_btree_check(struct cache_set *c); void bch_initial_mark_key(struct cache_set *c, int level, struct bkey *k); +void bch_cannibalize_unlock(struct cache_set *c); static inline void wake_up_gc(struct cache_set *c) { diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 0285b676e983..7bac2a88b794 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -890,6 +890,16 @@ static int bch_root_node_dirty_init(struct cache_set *c, if (ret < 0) pr_warn("sectors dirty init failed, ret=%d!\n", ret); + /* + * The op may be added to cache_set's btree_cache_wait + * in mca_cannibalize(), must ensure it is removed from + * the list and release btree_cache_alloc_lock before + * free op memory. + * Otherwise, the btree_cache_wait will be damaged. + */ + bch_cannibalize_unlock(c); + finish_wait(&c->btree_cache_wait, &(&op.op)->wait); + return ret; } -- cgit 1.4.1 From 68118c339c6e1e16ae017bef160dbe28a27ae9c8 Mon Sep 17 00:00:00 2001 From: Zheng Wang Date: Thu, 15 Jun 2023 20:12:21 +0800 Subject: bcache: Remove unnecessary NULL point check in node allocations commit 028ddcac477b691dd9205c92f991cc15259d033e upstream. Due to the previous fix of __bch_btree_node_alloc, the return value will never be a NULL pointer. So IS_ERR is enough to handle the failure situation. Fix it by replacing IS_ERR_OR_NULL check by an IS_ERR check. Fixes: cafe56359144 ("bcache: A block layer cache") Cc: stable@vger.kernel.org Signed-off-by: Zheng Wang Signed-off-by: Coly Li Link: https://lore.kernel.org/r/20230615121223.22502-5-colyli@suse.de Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/btree.c | 10 +++++----- drivers/md/bcache/super.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index df9986a7300d..d7b5dd886a15 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1138,7 +1138,7 @@ static struct btree *btree_node_alloc_replacement(struct btree *b, { struct btree *n = bch_btree_node_alloc(b->c, op, b->level, b->parent); - if (!IS_ERR_OR_NULL(n)) { + if (!IS_ERR(n)) { mutex_lock(&n->write_lock); bch_btree_sort_into(&b->keys, &n->keys, &b->c->sort); bkey_copy_key(&n->key, &b->key); @@ -1340,7 +1340,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op, memset(new_nodes, 0, sizeof(new_nodes)); closure_init_stack(&cl); - while (nodes < GC_MERGE_NODES && !IS_ERR_OR_NULL(r[nodes].b)) + while (nodes < GC_MERGE_NODES && !IS_ERR(r[nodes].b)) keys += r[nodes++].keys; blocks = btree_default_blocks(b->c) * 2 / 3; @@ -1352,7 +1352,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op, for (i = 0; i < nodes; i++) { new_nodes[i] = btree_node_alloc_replacement(r[i].b, NULL); - if (IS_ERR_OR_NULL(new_nodes[i])) + if (IS_ERR(new_nodes[i])) goto out_nocoalesce; } @@ -1487,7 +1487,7 @@ out_nocoalesce: bch_keylist_free(&keylist); for (i = 0; i < nodes; i++) - if (!IS_ERR_OR_NULL(new_nodes[i])) { + if (!IS_ERR(new_nodes[i])) { btree_node_free(new_nodes[i]); rw_unlock(true, new_nodes[i]); } @@ -1669,7 +1669,7 @@ static int bch_btree_gc_root(struct btree *b, struct btree_op *op, if (should_rewrite) { n = btree_node_alloc_replacement(b, NULL); - if (!IS_ERR_OR_NULL(n)) { + if (!IS_ERR(n)) { bch_btree_node_write_sync(n); bch_btree_set_root(n); diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index ba3909bb6bea..7660962e7b8b 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1724,7 +1724,7 @@ static void cache_set_flush(struct closure *cl) if (!IS_ERR_OR_NULL(c->gc_thread)) kthread_stop(c->gc_thread); - if (!IS_ERR_OR_NULL(c->root)) + if (!IS_ERR(c->root)) list_add(&c->root->list, &c->btree_cache); /* @@ -2088,7 +2088,7 @@ static int run_cache_set(struct cache_set *c) err = "cannot allocate new btree root"; c->root = __bch_btree_node_alloc(c, NULL, 0, true, NULL); - if (IS_ERR_OR_NULL(c->root)) + if (IS_ERR(c->root)) goto err; mutex_lock(&c->root->write_lock); -- cgit 1.4.1 From 7ecea5ce3dc17339c280c75b58ac93d8c8620d9f Mon Sep 17 00:00:00 2001 From: Zheng Wang Date: Thu, 15 Jun 2023 20:12:22 +0800 Subject: bcache: Fix __bch_btree_node_alloc to make the failure behavior consistent commit 80fca8a10b604afad6c14213fdfd816c4eda3ee4 upstream. In some specific situations, the return value of __bch_btree_node_alloc may be NULL. This may lead to a potential NULL pointer dereference in caller function like a calling chain : btree_split->bch_btree_node_alloc->__bch_btree_node_alloc. Fix it by initializing the return value in __bch_btree_node_alloc. Fixes: cafe56359144 ("bcache: A block layer cache") Cc: stable@vger.kernel.org Signed-off-by: Zheng Wang Signed-off-by: Coly Li Link: https://lore.kernel.org/r/20230615121223.22502-6-colyli@suse.de Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/btree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index d7b5dd886a15..68b9d7ca864e 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1090,10 +1090,12 @@ struct btree *__bch_btree_node_alloc(struct cache_set *c, struct btree_op *op, struct btree *parent) { BKEY_PADDED(key) k; - struct btree *b = ERR_PTR(-EAGAIN); + struct btree *b; mutex_lock(&c->bucket_lock); retry: + /* return ERR_PTR(-EAGAIN) when it fails */ + b = ERR_PTR(-EAGAIN); if (__bch_bucket_alloc_set(c, RESERVE_BTREE, &k.key, wait)) goto err; -- cgit 1.4.1 From 219a9ec09dd1aef3514b7b9e2030e1d0e804b38c Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant Date: Mon, 5 Jun 2023 20:06:16 +0530 Subject: watch_queue: prevent dangling pipe pointer commit 943211c87427f25bd22e0e63849fb486bb5f87fa upstream. NULL the dangling pipe reference while clearing watch_queue. If not done, a reference to a freed pipe remains in the watch_queue, as this function is called before freeing a pipe in free_pipe_info() (see line 834 of fs/pipe.c). The sole use of wqueue->defunct is for checking if the watch queue has been cleared, but wqueue->pipe is also NULLed while clearing. Thus, wqueue->defunct is superfluous, as wqueue->pipe can be checked for NULL. Hence, the former can be removed. Tested with keyutils testsuite. Cc: stable@vger.kernel.org # 6.1 Signed-off-by: Siddh Raman Pant Acked-by: David Howells Message-Id: <20230605143616.640517-1-code@siddh.me> Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- include/linux/watch_queue.h | 3 +-- kernel/watch_queue.c | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/linux/watch_queue.h b/include/linux/watch_queue.h index fc6bba20273b..45cd42f55d49 100644 --- a/include/linux/watch_queue.h +++ b/include/linux/watch_queue.h @@ -38,7 +38,7 @@ struct watch_filter { struct watch_queue { struct rcu_head rcu; struct watch_filter __rcu *filter; - struct pipe_inode_info *pipe; /* The pipe we're using as a buffer */ + struct pipe_inode_info *pipe; /* Pipe we use as a buffer, NULL if queue closed */ struct hlist_head watches; /* Contributory watches */ struct page **notes; /* Preallocated notifications */ unsigned long *notes_bitmap; /* Allocation bitmap for notes */ @@ -46,7 +46,6 @@ struct watch_queue { spinlock_t lock; unsigned int nr_notes; /* Number of notes */ unsigned int nr_pages; /* Number of pages in notes[] */ - bool defunct; /* T when queues closed */ }; /* diff --git a/kernel/watch_queue.c b/kernel/watch_queue.c index f10f403104e7..28ed71d277bd 100644 --- a/kernel/watch_queue.c +++ b/kernel/watch_queue.c @@ -43,7 +43,7 @@ MODULE_LICENSE("GPL"); static inline bool lock_wqueue(struct watch_queue *wqueue) { spin_lock_bh(&wqueue->lock); - if (unlikely(wqueue->defunct)) { + if (unlikely(!wqueue->pipe)) { spin_unlock_bh(&wqueue->lock); return false; } @@ -105,9 +105,6 @@ static bool post_one_notification(struct watch_queue *wqueue, unsigned int head, tail, mask, note, offset, len; bool done = false; - if (!pipe) - return false; - spin_lock_irq(&pipe->rd_wait.lock); mask = pipe->ring_size - 1; @@ -604,8 +601,11 @@ void watch_queue_clear(struct watch_queue *wqueue) rcu_read_lock(); spin_lock_bh(&wqueue->lock); - /* Prevent new notifications from being stored. */ - wqueue->defunct = true; + /* + * This pipe can be freed by callers like free_pipe_info(). + * Removing this reference also prevents new notifications. + */ + wqueue->pipe = NULL; while (!hlist_empty(&wqueue->watches)) { watch = hlist_entry(wqueue->watches.first, struct watch, queue_node); -- cgit 1.4.1 From 0cbbb029ffe9b5fc2c16fdd8a679563ca7d8f599 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 6 Jun 2023 15:24:45 -0700 Subject: um: Use HOST_DIR for mrproper commit a5a319ec2c2236bb96d147c16196d2f1f3799301 upstream. When HEADER_ARCH was introduced, the MRPROPER_FILES (then MRPROPER_DIRS) list wasn't adjusted, leaving SUBARCH as part of the path argument. This resulted in the "mrproper" target not cleaning up arch/x86/... when SUBARCH was specified. Since HOST_DIR is arch/$(HEADER_ARCH), use it instead to get the correct path. Cc: Richard Weinberger Cc: Anton Ivanov Cc: Johannes Berg Cc: Azeem Shaikh Cc: linux-um@lists.infradead.org Fixes: 7bbe7204e937 ("um: merge Makefile-{i386,x86_64}") Cc: stable@vger.kernel.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20230606222442.never.807-kees@kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/um/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/um/Makefile b/arch/um/Makefile index f1d4d67157be..3dbd0e3b660e 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -148,7 +148,7 @@ export LDFLAGS_vmlinux := $(LDFLAGS_EXECSTACK) # When cleaning we don't include .config, so we don't include # TT or skas makefiles and don't clean skas_ptregs.h. CLEAN_FILES += linux x.i gmon.out -MRPROPER_FILES += arch/$(SUBARCH)/include/generated +MRPROPER_FILES += $(HOST_DIR)/include/generated archclean: @find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \ -- cgit 1.4.1 From cd52323ac4c1ba42660f0478ec7a40ea92b78714 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 1 Jun 2023 14:42:44 +0800 Subject: integrity: Fix possible multiple allocation in integrity_inode_get() commit 9df6a4870dc371136e90330cfbbc51464ee66993 upstream. When integrity_inode_get() is querying and inserting the cache, there is a conditional race in the concurrent environment. The race condition is the result of not properly implementing "double-checked locking". In this case, it first checks to see if the iint cache record exists before taking the lock, but doesn't check again after taking the integrity_iint_lock. Fixes: bf2276d10ce5 ("ima: allocating iint improvements") Signed-off-by: Tianjia Zhang Cc: Dmitry Kasatkin Cc: # v3.10+ Signed-off-by: Mimi Zohar Signed-off-by: Greg Kroah-Hartman --- security/integrity/iint.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 8638976f7990..65418e0906c1 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -43,12 +43,10 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) else if (inode > iint->inode) n = n->rb_right; else - break; + return iint; } - if (!n) - return NULL; - return iint; + return NULL; } /* @@ -121,10 +119,15 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode) parent = *p; test_iint = rb_entry(parent, struct integrity_iint_cache, rb_node); - if (inode < test_iint->inode) + if (inode < test_iint->inode) { p = &(*p)->rb_left; - else + } else if (inode > test_iint->inode) { p = &(*p)->rb_right; + } else { + write_unlock(&integrity_iint_lock); + kmem_cache_free(iint_cache, iint); + return test_iint; + } } iint->inode = inode; -- cgit 1.4.1 From 33893c6c1f1860d9434c393665632bbc16841d8d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 May 2023 10:19:35 +0200 Subject: autofs: use flexible array in ioctl structure commit e910c8e3aa02dc456e2f4c32cb479523c326b534 upstream. Commit df8fc4e934c1 ("kbuild: Enable -fstrict-flex-arrays=3") introduced a warning for the autofs_dev_ioctl structure: In function 'check_name', inlined from 'validate_dev_ioctl' at fs/autofs/dev-ioctl.c:131:9, inlined from '_autofs_dev_ioctl' at fs/autofs/dev-ioctl.c:624:8: fs/autofs/dev-ioctl.c:33:14: error: 'strchr' reading 1 or more bytes from a region of size 0 [-Werror=stringop-overread] 33 | if (!strchr(name, '/')) | ^~~~~~~~~~~~~~~~~ In file included from include/linux/auto_dev-ioctl.h:10, from fs/autofs/autofs_i.h:10, from fs/autofs/dev-ioctl.c:14: include/uapi/linux/auto_dev-ioctl.h: In function '_autofs_dev_ioctl': include/uapi/linux/auto_dev-ioctl.h:112:14: note: source object 'path' of size 0 112 | char path[0]; | ^~~~ This is easily fixed by changing the gnu 0-length array into a c99 flexible array. Since this is a uapi structure, we have to be careful about possible regressions but this one should be fine as they are equivalent here. While it would break building with ancient gcc versions that predate c99, it helps building with --std=c99 and -Wpedantic builds in user space, as well as non-gnu compilers. This means we probably also want it fixed in stable kernels. Cc: stable@vger.kernel.org Cc: Kees Cook Cc: "Gustavo A. R. Silva" Signed-off-by: Arnd Bergmann Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20230523081944.581710-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/autofs-mount-control.rst | 2 +- Documentation/filesystems/autofs.rst | 2 +- include/uapi/linux/auto_dev-ioctl.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/filesystems/autofs-mount-control.rst b/Documentation/filesystems/autofs-mount-control.rst index bf4b511cdbe8..b5a379d25c40 100644 --- a/Documentation/filesystems/autofs-mount-control.rst +++ b/Documentation/filesystems/autofs-mount-control.rst @@ -196,7 +196,7 @@ information and return operation results:: struct args_ismountpoint ismountpoint; }; - char path[0]; + char path[]; }; The ioctlfd field is a mount point file descriptor of an autofs mount diff --git a/Documentation/filesystems/autofs.rst b/Documentation/filesystems/autofs.rst index 4f490278d22f..3b6e38e646cd 100644 --- a/Documentation/filesystems/autofs.rst +++ b/Documentation/filesystems/autofs.rst @@ -467,7 +467,7 @@ Each ioctl is passed a pointer to an `autofs_dev_ioctl` structure:: struct args_ismountpoint ismountpoint; }; - char path[0]; + char path[]; }; For the **OPEN_MOUNT** and **IS_MOUNTPOINT** commands, the target diff --git a/include/uapi/linux/auto_dev-ioctl.h b/include/uapi/linux/auto_dev-ioctl.h index 62e625356dc8..08be539605fc 100644 --- a/include/uapi/linux/auto_dev-ioctl.h +++ b/include/uapi/linux/auto_dev-ioctl.h @@ -109,7 +109,7 @@ struct autofs_dev_ioctl { struct args_ismountpoint ismountpoint; }; - char path[0]; + char path[]; }; static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -- cgit 1.4.1 From 23fbff67b06d023d179133b93a57f408b5f64985 Mon Sep 17 00:00:00 2001 From: Ryan Roberts Date: Fri, 2 Jun 2023 10:29:47 +0100 Subject: mm/damon/ops-common: atomically test and clear young on ptes and pmds commit c11d34fa139e4b0fb4249a30f37b178353533fa1 upstream. It is racy to non-atomically read a pte, then clear the young bit, then write it back as this could discard dirty information. Further, it is bad practice to directly set a pte entry within a table. Instead clearing young must go through the arch-provided helper, ptep_test_and_clear_young() to ensure it is modified atomically and to give the arch code visibility and allow it to check (and potentially modify) the operation. Link: https://lkml.kernel.org/r/20230602092949.545577-3-ryan.roberts@arm.com Fixes: 3f49584b262c ("mm/damon: implement primitives for the virtual memory address spaces"). Signed-off-by: Ryan Roberts Reviewed-by: Zi Yan Reviewed-by: SeongJae Park Reviewed-by: Mike Rapoport (IBM) Cc: Christoph Hellwig Cc: Kirill A. Shutemov Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Uladzislau Rezki (Sony) Cc: Yu Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/ops-common.c | 16 ++++++---------- mm/damon/ops-common.h | 4 ++-- mm/damon/paddr.c | 4 ++-- mm/damon/vaddr.c | 4 ++-- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 75409601f934..13b99975cbc2 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -33,7 +33,7 @@ struct page *damon_get_page(unsigned long pfn) return page; } -void damon_ptep_mkold(pte_t *pte, struct mm_struct *mm, unsigned long addr) +void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr) { bool referenced = false; struct page *page = damon_get_page(pte_pfn(*pte)); @@ -41,13 +41,11 @@ void damon_ptep_mkold(pte_t *pte, struct mm_struct *mm, unsigned long addr) if (!page) return; - if (pte_young(*pte)) { + if (ptep_test_and_clear_young(vma, addr, pte)) referenced = true; - *pte = pte_mkold(*pte); - } #ifdef CONFIG_MMU_NOTIFIER - if (mmu_notifier_clear_young(mm, addr, addr + PAGE_SIZE)) + if (mmu_notifier_clear_young(vma->vm_mm, addr, addr + PAGE_SIZE)) referenced = true; #endif /* CONFIG_MMU_NOTIFIER */ @@ -58,7 +56,7 @@ void damon_ptep_mkold(pte_t *pte, struct mm_struct *mm, unsigned long addr) put_page(page); } -void damon_pmdp_mkold(pmd_t *pmd, struct mm_struct *mm, unsigned long addr) +void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr) { #ifdef CONFIG_TRANSPARENT_HUGEPAGE bool referenced = false; @@ -67,13 +65,11 @@ void damon_pmdp_mkold(pmd_t *pmd, struct mm_struct *mm, unsigned long addr) if (!page) return; - if (pmd_young(*pmd)) { + if (pmdp_test_and_clear_young(vma, addr, pmd)) referenced = true; - *pmd = pmd_mkold(*pmd); - } #ifdef CONFIG_MMU_NOTIFIER - if (mmu_notifier_clear_young(mm, addr, addr + HPAGE_PMD_SIZE)) + if (mmu_notifier_clear_young(vma->vm_mm, addr, addr + HPAGE_PMD_SIZE)) referenced = true; #endif /* CONFIG_MMU_NOTIFIER */ diff --git a/mm/damon/ops-common.h b/mm/damon/ops-common.h index 8d82d3722204..e062a8874e41 100644 --- a/mm/damon/ops-common.h +++ b/mm/damon/ops-common.h @@ -9,8 +9,8 @@ struct page *damon_get_page(unsigned long pfn); -void damon_ptep_mkold(pte_t *pte, struct mm_struct *mm, unsigned long addr); -void damon_pmdp_mkold(pmd_t *pmd, struct mm_struct *mm, unsigned long addr); +void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr); +void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr); int damon_cold_score(struct damon_ctx *c, struct damon_region *r, struct damos *s); diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index 402d30b37aba..7bc8d79c7fa0 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -24,9 +24,9 @@ static bool __damon_pa_mkold(struct folio *folio, struct vm_area_struct *vma, while (page_vma_mapped_walk(&pvmw)) { addr = pvmw.address; if (pvmw.pte) - damon_ptep_mkold(pvmw.pte, vma->vm_mm, addr); + damon_ptep_mkold(pvmw.pte, vma, addr); else - damon_pmdp_mkold(pvmw.pmd, vma->vm_mm, addr); + damon_pmdp_mkold(pvmw.pmd, vma, addr); } return true; } diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index 15f03df66db6..26d561af74e4 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -311,7 +311,7 @@ static int damon_mkold_pmd_entry(pmd_t *pmd, unsigned long addr, } if (pmd_trans_huge(*pmd)) { - damon_pmdp_mkold(pmd, walk->mm, addr); + damon_pmdp_mkold(pmd, walk->vma, addr); spin_unlock(ptl); return 0; } @@ -323,7 +323,7 @@ static int damon_mkold_pmd_entry(pmd_t *pmd, unsigned long addr, pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl); if (!pte_present(*pte)) goto out; - damon_ptep_mkold(pte, walk->mm, addr); + damon_ptep_mkold(pte, walk->vma, addr); out: pte_unmap_unlock(pte, ptl); return 0; -- cgit 1.4.1 From 1f34bf8b442c6d720e7fa6f15e8702427e48aea9 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 7 Jun 2023 18:15:23 +0200 Subject: shmem: use ramfs_kill_sb() for kill_sb method of ramfs-based tmpfs commit 36ce9d76b0a93bae799e27e4f5ac35478c676592 upstream. As the ramfs-based tmpfs uses ramfs_init_fs_context() for the init_fs_context method, which allocates fc->s_fs_info, use ramfs_kill_sb() to free it and avoid a memory leak. Link: https://lkml.kernel.org/r/20230607161523.2876433-1-roberto.sassu@huaweicloud.com Fixes: c3b1b1cbf002 ("ramfs: add support for "mode=" mount option") Signed-off-by: Roberto Sassu Cc: Hugh Dickins Cc: David Howells Cc: Al Viro Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ramfs/inode.c | 2 +- include/linux/ramfs.h | 1 + mm/shmem.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index b3257e852820..f368a16906fb 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -278,7 +278,7 @@ int ramfs_init_fs_context(struct fs_context *fc) return 0; } -static void ramfs_kill_sb(struct super_block *sb) +void ramfs_kill_sb(struct super_block *sb) { kfree(sb->s_fs_info); kill_litter_super(sb); diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index 917528d102c4..d506dc63dd47 100644 --- a/include/linux/ramfs.h +++ b/include/linux/ramfs.h @@ -7,6 +7,7 @@ struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev); extern int ramfs_init_fs_context(struct fs_context *fc); +extern void ramfs_kill_sb(struct super_block *sb); #ifdef CONFIG_MMU static inline int diff --git a/mm/shmem.c b/mm/shmem.c index a8d9fd039d0a..aba041a3df73 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -4137,7 +4137,7 @@ static struct file_system_type shmem_fs_type = { .name = "tmpfs", .init_fs_context = ramfs_init_fs_context, .parameters = ramfs_fs_parameters, - .kill_sb = kill_litter_super, + .kill_sb = ramfs_kill_sb, .fs_flags = FS_USERNS_MOUNT, }; -- cgit 1.4.1 From 6df680709d901346831ef8f221cc90a42062c526 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 6 May 2023 06:56:12 +0200 Subject: jffs2: reduce stack usage in jffs2_build_xattr_subsystem() commit 1168f095417643f663caa341211e117db552989f upstream. Use kcalloc() for allocation/flush of 128 pointers table to reduce stack usage. Function now returns -ENOMEM or 0 on success. stackusage Before: ./fs/jffs2/xattr.c:775 jffs2_build_xattr_subsystem 1208 dynamic,bounded After: ./fs/jffs2/xattr.c:775 jffs2_build_xattr_subsystem 192 dynamic,bounded Also update definition when CONFIG_JFFS2_FS_XATTR is not enabled Tested with an MTD mount point and some user set/getfattr. Many current target on OpenWRT also suffer from a compilation warning (that become an error with CONFIG_WERROR) with the following output: fs/jffs2/xattr.c: In function 'jffs2_build_xattr_subsystem': fs/jffs2/xattr.c:887:1: error: the frame size of 1088 bytes is larger than 1024 bytes [-Werror=frame-larger-than=] 887 | } | ^ Using dynamic allocation fix this compilation warning. Fixes: c9f700f840bd ("[JFFS2][XATTR] using 'delete marker' for xdatum/xref deletion") Reported-by: Tim Gardner Reported-by: kernel test robot Reported-by: Ron Economos Reported-by: Nathan Chancellor Reviewed-by: Nick Desaulniers Signed-off-by: Fabian Frederick Signed-off-by: Christian Marangi Cc: stable@vger.kernel.org Message-Id: <20230506045612.16616-1-ansuelsmth@gmail.com> Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- fs/jffs2/build.c | 5 ++++- fs/jffs2/xattr.c | 13 +++++++++---- fs/jffs2/xattr.h | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 837cd55fd4c5..6ae9d6fefb86 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -211,7 +211,10 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) ic->scan_dents = NULL; cond_resched(); } - jffs2_build_xattr_subsystem(c); + ret = jffs2_build_xattr_subsystem(c); + if (ret) + goto exit; + c->flags &= ~JFFS2_SB_FLAG_BUILDING; dbg_fsbuild("FS build complete\n"); diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index da3e18503c65..acb4492f5970 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -772,10 +772,10 @@ void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) } #define XREF_TMPHASH_SIZE (128) -void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) +int jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) { struct jffs2_xattr_ref *ref, *_ref; - struct jffs2_xattr_ref *xref_tmphash[XREF_TMPHASH_SIZE]; + struct jffs2_xattr_ref **xref_tmphash; struct jffs2_xattr_datum *xd, *_xd; struct jffs2_inode_cache *ic; struct jffs2_raw_node_ref *raw; @@ -784,9 +784,12 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING)); + xref_tmphash = kcalloc(XREF_TMPHASH_SIZE, + sizeof(struct jffs2_xattr_ref *), GFP_KERNEL); + if (!xref_tmphash) + return -ENOMEM; + /* Phase.1 : Merge same xref */ - for (i=0; i < XREF_TMPHASH_SIZE; i++) - xref_tmphash[i] = NULL; for (ref=c->xref_temp; ref; ref=_ref) { struct jffs2_xattr_ref *tmp; @@ -884,6 +887,8 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) "%u of xref (%u dead, %u orphan) found.\n", xdatum_count, xdatum_unchecked_count, xdatum_orphan_count, xref_count, xref_dead_count, xref_orphan_count); + kfree(xref_tmphash); + return 0; } struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 720007b2fd65..1b5030a3349d 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -71,7 +71,7 @@ static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref) #ifdef CONFIG_JFFS2_FS_XATTR extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c); -extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c); +extern int jffs2_build_xattr_subsystem(struct jffs2_sb_info *c); extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, @@ -103,7 +103,7 @@ extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); #else #define jffs2_init_xattr_subsystem(c) -#define jffs2_build_xattr_subsystem(c) +#define jffs2_build_xattr_subsystem(c) (0) #define jffs2_clear_xattr_subsystem(c) #define jffs2_xattr_do_crccheck_inode(c, ic) -- cgit 1.4.1 From 606e463eefeddf1c425940e6d71387bef8265035 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 7 Jun 2023 19:28:48 +0200 Subject: fs: avoid empty option when generating legacy mount string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 62176420274db5b5127cd7a0083a9aeb461756ee upstream. As each option string fragment is always prepended with a comma it would happen that the whole string always starts with a comma. This could be interpreted by filesystem drivers as an empty option and may produce errors. For example the NTFS driver from ntfs.ko behaves like this and fails when mounted via the new API. Link: https://github.com/util-linux/util-linux/issues/2298 Signed-off-by: Thomas Weißschuh Fixes: 3e1aeb00e6d1 ("vfs: Implement a filesystem superblock creation/configuration context") Cc: stable@vger.kernel.org Message-Id: <20230607-fs-empty-option-v1-1-20c8dbf4671b@weissschuh.net> Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- fs/fs_context.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/fs_context.c b/fs/fs_context.c index 24ce12f0db32..851214d1d013 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -561,7 +561,8 @@ static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param) return -ENOMEM; } - ctx->legacy_data[size++] = ','; + if (size) + ctx->legacy_data[size++] = ','; len = strlen(param->key); memcpy(ctx->legacy_data + size, param->key, len); size += len; -- cgit 1.4.1 From 6aaa22ec7310d0fe1aec1ef9297230b3210b8083 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 1 Jun 2023 12:58:21 +0200 Subject: ext4: Remove ext4 locking of moved directory commit 3658840cd363f2be094f5dfd2f0b174a9055dd0f upstream. Remove locking of moved directory in ext4_rename2(). We will take care of it in VFS instead. This effectively reverts commit 0813299c586b ("ext4: Fix possible corruption when moving a directory") and followup fixes. CC: Ted Tso CC: stable@vger.kernel.org Signed-off-by: Jan Kara Message-Id: <20230601105830.13168-1-jack@suse.cz> Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- fs/ext4/namei.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 5a3dbbabe23a..0e1aeb9cb4a7 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3834,19 +3834,10 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir, return retval; } - /* - * We need to protect against old.inode directory getting converted - * from inline directory format into a normal one. - */ - if (S_ISDIR(old.inode->i_mode)) - inode_lock_nested(old.inode, I_MUTEX_NONDIR2); - old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, &old.inlined); - if (IS_ERR(old.bh)) { - retval = PTR_ERR(old.bh); - goto unlock_moved_dir; - } + if (IS_ERR(old.bh)) + return PTR_ERR(old.bh); /* * Check for inode number is _not_ due to possible IO errors. @@ -4043,10 +4034,6 @@ release_bh: brelse(old.bh); brelse(new.bh); -unlock_moved_dir: - if (S_ISDIR(old.inode->i_mode)) - inode_unlock(old.inode); - return retval; } -- cgit 1.4.1 From 6654d2a165e4f36f68a20b9fe988874c4ea1c9da Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 1 Jun 2023 12:58:23 +0200 Subject: Revert "f2fs: fix potential corruption when moving a directory" commit cde3c9d7e2a359e337216855dcb333a19daaa436 upstream. This reverts commit d94772154e524b329a168678836745d2773a6e02. The locking is going to be provided by VFS. CC: Jaegeuk Kim CC: stable@vger.kernel.org Signed-off-by: Jan Kara Message-Id: <20230601105830.13168-3-jack@suse.cz> Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/namei.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index d879a295b688..bd020a992c2e 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -1002,20 +1002,12 @@ static int f2fs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, goto out; } - /* - * Copied from ext4_rename: we need to protect against old.inode - * directory getting converted from inline directory format into - * a normal one. - */ - if (S_ISDIR(old_inode->i_mode)) - inode_lock_nested(old_inode, I_MUTEX_NONDIR2); - err = -ENOENT; old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) { if (IS_ERR(old_page)) err = PTR_ERR(old_page); - goto out_unlock_old; + goto out; } if (S_ISDIR(old_inode->i_mode)) { @@ -1123,9 +1115,6 @@ static int f2fs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, f2fs_unlock_op(sbi); - if (S_ISDIR(old_inode->i_mode)) - inode_unlock(old_inode); - if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) f2fs_sync_fs(sbi->sb, 1); @@ -1140,9 +1129,6 @@ out_dir: f2fs_put_page(old_dir_page, 0); out_old: f2fs_put_page(old_page, 0); -out_unlock_old: - if (S_ISDIR(old_inode->i_mode)) - inode_unlock(old_inode); out: iput(whiteout); return err; -- cgit 1.4.1 From 10c159f994b985cf0c8b8eb8b851ae9d46a820a8 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 1 Jun 2023 12:58:24 +0200 Subject: fs: Establish locking order for unrelated directories commit f23ce757185319886ca80c4864ce5f81ac6cc9e9 upstream. Currently the locking order of inode locks for directories that are not in ancestor relationship is not defined because all operations that needed to lock two directories like this were serialized by sb->s_vfs_rename_mutex. However some filesystems need to lock two subdirectories for RENAME_EXCHANGE operations and for this we need the locking order established even for two tree-unrelated directories. Provide a helper function lock_two_inodes() that establishes lock ordering for any two inodes and use it in lock_two_directories(). CC: stable@vger.kernel.org Signed-off-by: Jan Kara Message-Id: <20230601105830.13168-4-jack@suse.cz> Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- fs/inode.c | 42 ++++++++++++++++++++++++++++++++++++++++++ fs/internal.h | 2 ++ fs/namei.c | 4 ++-- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 8c4078889754..6ae760db1311 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1103,6 +1103,48 @@ void discard_new_inode(struct inode *inode) } EXPORT_SYMBOL(discard_new_inode); +/** + * lock_two_inodes - lock two inodes (may be regular files but also dirs) + * + * Lock any non-NULL argument. The caller must make sure that if he is passing + * in two directories, one is not ancestor of the other. Zero, one or two + * objects may be locked by this function. + * + * @inode1: first inode to lock + * @inode2: second inode to lock + * @subclass1: inode lock subclass for the first lock obtained + * @subclass2: inode lock subclass for the second lock obtained + */ +void lock_two_inodes(struct inode *inode1, struct inode *inode2, + unsigned subclass1, unsigned subclass2) +{ + if (!inode1 || !inode2) { + /* + * Make sure @subclass1 will be used for the acquired lock. + * This is not strictly necessary (no current caller cares) but + * let's keep things consistent. + */ + if (!inode1) + swap(inode1, inode2); + goto lock; + } + + /* + * If one object is directory and the other is not, we must make sure + * to lock directory first as the other object may be its child. + */ + if (S_ISDIR(inode2->i_mode) == S_ISDIR(inode1->i_mode)) { + if (inode1 > inode2) + swap(inode1, inode2); + } else if (!S_ISDIR(inode1->i_mode)) + swap(inode1, inode2); +lock: + if (inode1) + inode_lock_nested(inode1, subclass1); + if (inode2 && inode2 != inode1) + inode_lock_nested(inode2, subclass2); +} + /** * lock_two_nondirectories - take two i_mutexes on non-directory objects * diff --git a/fs/internal.h b/fs/internal.h index 5545c26d86ae..46caa33373a4 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -153,6 +153,8 @@ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc); int dentry_needs_remove_privs(struct user_namespace *, struct dentry *dentry); bool in_group_or_capable(struct user_namespace *mnt_userns, const struct inode *inode, vfsgid_t vfsgid); +void lock_two_inodes(struct inode *inode1, struct inode *inode2, + unsigned subclass1, unsigned subclass2); /* * fs-writeback.c diff --git a/fs/namei.c b/fs/namei.c index 9155ecb547ce..734d516b81fd 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3007,8 +3007,8 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) return p; } - inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); - inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2); + lock_two_inodes(p1->d_inode, p2->d_inode, + I_MUTEX_PARENT, I_MUTEX_PARENT2); return NULL; } EXPORT_SYMBOL(lock_rename); -- cgit 1.4.1 From f40d621387d43befac244c84022edce2b762bb37 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 1 Jun 2023 12:58:25 +0200 Subject: fs: Lock moved directories commit 28eceeda130f5058074dd007d9c59d2e8bc5af2e upstream. When a directory is moved to a different directory, some filesystems (udf, ext4, ocfs2, f2fs, and likely gfs2, reiserfs, and others) need to update their pointer to the parent and this must not race with other operations on the directory. Lock the directories when they are moved. Although not all filesystems need this locking, we perform it in vfs_rename() because getting the lock ordering right is really difficult and we don't want to expose these locking details to filesystems. CC: stable@vger.kernel.org Signed-off-by: Jan Kara Message-Id: <20230601105830.13168-5-jack@suse.cz> Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/directory-locking.rst | 26 +++++++++++++------------ fs/namei.c | 22 +++++++++++++-------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/Documentation/filesystems/directory-locking.rst b/Documentation/filesystems/directory-locking.rst index 504ba940c36c..dccd61c7c5c3 100644 --- a/Documentation/filesystems/directory-locking.rst +++ b/Documentation/filesystems/directory-locking.rst @@ -22,12 +22,11 @@ exclusive. 3) object removal. Locking rules: caller locks parent, finds victim, locks victim and calls the method. Locks are exclusive. -4) rename() that is _not_ cross-directory. Locking rules: caller locks -the parent and finds source and target. In case of exchange (with -RENAME_EXCHANGE in flags argument) lock both. In any case, -if the target already exists, lock it. If the source is a non-directory, -lock it. If we need to lock both, lock them in inode pointer order. -Then call the method. All locks are exclusive. +4) rename() that is _not_ cross-directory. Locking rules: caller locks the +parent and finds source and target. We lock both (provided they exist). If we +need to lock two inodes of different type (dir vs non-dir), we lock directory +first. If we need to lock two inodes of the same type, lock them in inode +pointer order. Then call the method. All locks are exclusive. NB: we might get away with locking the source (and target in exchange case) shared. @@ -44,15 +43,17 @@ All locks are exclusive. rules: * lock the filesystem - * lock parents in "ancestors first" order. + * lock parents in "ancestors first" order. If one is not ancestor of + the other, lock them in inode pointer order. * find source and target. * if old parent is equal to or is a descendent of target fail with -ENOTEMPTY * if new parent is equal to or is a descendent of source fail with -ELOOP - * If it's an exchange, lock both the source and the target. - * If the target exists, lock it. If the source is a non-directory, - lock it. If we need to lock both, do so in inode pointer order. + * Lock both the source and the target provided they exist. If we + need to lock two inodes of different type (dir vs non-dir), we lock + the directory first. If we need to lock two inodes of the same type, + lock them in inode pointer order. * call the method. All ->i_rwsem are taken exclusive. Again, we might get away with locking @@ -66,8 +67,9 @@ If no directory is its own ancestor, the scheme above is deadlock-free. Proof: - First of all, at any moment we have a partial ordering of the - objects - A < B iff A is an ancestor of B. + First of all, at any moment we have a linear ordering of the + objects - A < B iff (A is an ancestor of B) or (B is not an ancestor + of A and ptr(A) < ptr(B)). That ordering can change. However, the following is true: diff --git a/fs/namei.c b/fs/namei.c index 734d516b81fd..41d71a29bd79 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4661,7 +4661,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname * sb->s_vfs_rename_mutex. We might be more accurate, but that's another * story. * c) we have to lock _four_ objects - parents and victim (if it exists), - * and source (if it is not a directory). + * and source. * And that - after we got ->i_mutex on parents (until then we don't know * whether the target exists). Solution: try to be smart with locking * order for inodes. We rely on the fact that tree topology may change @@ -4745,10 +4745,16 @@ int vfs_rename(struct renamedata *rd) take_dentry_name_snapshot(&old_name, old_dentry); dget(new_dentry); - if (!is_dir || (flags & RENAME_EXCHANGE)) - lock_two_nondirectories(source, target); - else if (target) - inode_lock(target); + /* + * Lock all moved children. Moved directories may need to change parent + * pointer so they need the lock to prevent against concurrent + * directory changes moving parent pointer. For regular files we've + * historically always done this. The lockdep locking subclasses are + * somewhat arbitrary but RENAME_EXCHANGE in particular can swap + * regular files and directories so it's difficult to tell which + * subclasses to use. + */ + lock_two_inodes(source, target, I_MUTEX_NORMAL, I_MUTEX_NONDIR2); error = -EPERM; if (IS_SWAPFILE(source) || (target && IS_SWAPFILE(target))) @@ -4796,9 +4802,9 @@ int vfs_rename(struct renamedata *rd) d_exchange(old_dentry, new_dentry); } out: - if (!is_dir || (flags & RENAME_EXCHANGE)) - unlock_two_nondirectories(source, target); - else if (target) + if (source) + inode_unlock(source); + if (target) inode_unlock(target); dput(new_dentry); if (!error) { -- cgit 1.4.1 From 7b67af8dea95051305177e0e4a40227d8706672e Mon Sep 17 00:00:00 2001 From: Wayne Chang Date: Tue, 31 Jan 2023 17:57:44 +0000 Subject: i2c: nvidia-gpu: Add ACPI property to align with device-tree commit f510b0a3565b9231e828e23a7e0f9790b97edf96 upstream. Device-tree uses the 'firmware-name' string property to pass a name of the firmware build to the Cypress CCGx driver. Add a new ACPI string property to the NVIDIA GPU I2C driver to align with device-tree so that we can migrate to using a common property name for both ACPI and device-tree. Signed-off-by: Wayne Chang Co-developed-by: Jon Hunter Signed-off-by: Jon Hunter Reviewed-by: Heikki Krogerus Acked-by: Ajay Gupta Acked-by: Wolfram Sang Link: https://lore.kernel.org/r/20230131175748.256423-3-jonathanh@nvidia.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-nvidia-gpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 12e330cd7635..6d81ea530a83 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -261,6 +261,7 @@ MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); static const struct property_entry ccgx_props[] = { /* Use FW built for NVIDIA (nv) only */ PROPERTY_ENTRY_U16("ccgx,firmware-build", ('n' << 8) | 'v'), + PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"), { } }; -- cgit 1.4.1 From f2a6ce3eec14bc8912c949d53b0902b5bedbcdc2 Mon Sep 17 00:00:00 2001 From: Wayne Chang Date: Tue, 31 Jan 2023 17:57:46 +0000 Subject: i2c: nvidia-gpu: Remove ccgx,firmware-build property commit 430b38764fbb931c6dbd1af13c8b2e4508994662 upstream. Now the Cypress CCG driver has been updated to support the 'firmware-name' property to align with device-tree, remove the 'ccgx,firmware-build' property as this is no longer needed. Signed-off-by: Wayne Chang Signed-off-by: Jon Hunter Acked-by: Ajay Gupta Acked-by: Wolfram Sang Link: https://lore.kernel.org/r/20230131175748.256423-5-jonathanh@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-nvidia-gpu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 6d81ea530a83..a8b99e7f6262 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -259,8 +259,7 @@ static const struct pci_device_id gpu_i2c_ids[] = { MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); static const struct property_entry ccgx_props[] = { - /* Use FW built for NVIDIA (nv) only */ - PROPERTY_ENTRY_U16("ccgx,firmware-build", ('n' << 8) | 'v'), + /* Use FW built for NVIDIA GPU only */ PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"), { } }; -- cgit 1.4.1 From 759e582b1cea5457ed058f42c60c9bef4b30db9d Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 18 May 2023 11:11:50 -0500 Subject: usb: typec: ucsi: Mark dGPUs as DEVICE scope commit a7fbfd44c0204f0629288edfd0d77829edb4a2f8 upstream. power_supply_is_system_supplied() checks whether any power supplies are present that aren't batteries to decide whether the system is running on DC or AC. Downstream drivers use this to make performance decisions. Navi dGPUs include an UCSI function that has been exported since commit 17631e8ca2d3 ("i2c: designware: Add driver support for AMD NAVI GPU"). This UCSI function registers a power supply since commit 992a60ed0d5e ("usb: typec: ucsi: register with power_supply class") but this is not a system power supply. As the power supply for a dGPU is only for powering devices connected to dGPU, create a device property to indicate that the UCSI endpoint is only for the scope of `POWER_SUPPLY_SCOPE_DEVICE`. Link: https://lore.kernel.org/lkml/20230516182541.5836-2-mario.limonciello@amd.com/ Reviewed-by: Evan Quan Tested-by: Evan Quan Signed-off-by: Mario Limonciello Reviewed-by: Heikki Krogerus Reviewed-by: Sebastian Reichel Acked-by: Andi Shyti Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-designware-pcidrv.c | 13 ++++++++++++- drivers/i2c/busses/i2c-nvidia-gpu.c | 3 +++ drivers/usb/typec/ucsi/psy.c | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 782fe1ef3ca1..61d7a27aa070 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -234,6 +235,16 @@ static const struct dev_pm_ops i2c_dw_pm_ops = { SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, i2c_dw_pci_runtime_resume, NULL) }; +static const struct property_entry dgpu_properties[] = { + /* USB-C doesn't power the system */ + PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE), + {} +}; + +static const struct software_node dgpu_node = { + .properties = dgpu_properties, +}; + static int i2c_dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -325,7 +336,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, } if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { - dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, NULL); + dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, &dgpu_node); if (IS_ERR(dev->slave)) return dev_err_probe(dev->dev, PTR_ERR(dev->slave), "register UCSI failed\n"); diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index a8b99e7f6262..26622d24bb1b 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -261,6 +262,8 @@ MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); static const struct property_entry ccgx_props[] = { /* Use FW built for NVIDIA GPU only */ PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"), + /* USB-C doesn't power the system */ + PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE), { } }; diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 56bf56517f75..384b42267f1f 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -27,8 +27,20 @@ static enum power_supply_property ucsi_psy_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_SCOPE, }; +static int ucsi_psy_get_scope(struct ucsi_connector *con, + union power_supply_propval *val) +{ + u8 scope = POWER_SUPPLY_SCOPE_UNKNOWN; + struct device *dev = con->ucsi->dev; + + device_property_read_u8(dev, "scope", &scope); + val->intval = scope; + return 0; +} + static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { @@ -194,6 +206,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy, return ucsi_psy_get_current_max(con, val); case POWER_SUPPLY_PROP_CURRENT_NOW: return ucsi_psy_get_current_now(con, val); + case POWER_SUPPLY_PROP_SCOPE: + return ucsi_psy_get_scope(con, val); default: return -EINVAL; } -- cgit 1.4.1 From 8fcb478b5508fc648fc34c9a546264fcdd3c4c3f Mon Sep 17 00:00:00 2001 From: Abhijeet Rastogi Date: Tue, 16 May 2023 20:08:49 -0700 Subject: ipvs: increase ip_vs_conn_tab_bits range for 64BIT commit 04292c695f82b6cf0d25dd5ae494f16ddbb621f6 upstream. Current range [8, 20] is set purely due to historical reasons because at the time, ~1M (2^20) was considered sufficient. With this change, 27 is the upper limit for 64-bit, 20 otherwise. Previous change regarding this limit is here. Link: https://lore.kernel.org/all/86eabeb9dd62aebf1e2533926fdd13fed48bab1f.1631289960.git.aclaudi@redhat.com/T/#u Signed-off-by: Abhijeet Rastogi Acked-by: Julian Anastasov Acked-by: Simon Horman Signed-off-by: Pablo Neira Ayuso Cc: Allen Pais Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/Kconfig | 27 ++++++++++++++------------- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig index 271da8447b29..2a3017b9c001 100644 --- a/net/netfilter/ipvs/Kconfig +++ b/net/netfilter/ipvs/Kconfig @@ -44,7 +44,8 @@ config IP_VS_DEBUG config IP_VS_TAB_BITS int "IPVS connection table size (the Nth power of 2)" - range 8 20 + range 8 20 if !64BIT + range 8 27 if 64BIT default 12 help The IPVS connection hash table uses the chaining scheme to handle @@ -54,24 +55,24 @@ config IP_VS_TAB_BITS Note the table size must be power of 2. The table size will be the value of 2 to the your input number power. The number to choose is - from 8 to 20, the default number is 12, which means the table size - is 4096. Don't input the number too small, otherwise you will lose - performance on it. You can adapt the table size yourself, according - to your virtual server application. It is good to set the table size - not far less than the number of connections per second multiplying - average lasting time of connection in the table. For example, your - virtual server gets 200 connections per second, the connection lasts - for 200 seconds in average in the connection table, the table size - should be not far less than 200x200, it is good to set the table - size 32768 (2**15). + from 8 to 27 for 64BIT(20 otherwise), the default number is 12, + which means the table size is 4096. Don't input the number too + small, otherwise you will lose performance on it. You can adapt the + table size yourself, according to your virtual server application. + It is good to set the table size not far less than the number of + connections per second multiplying average lasting time of + connection in the table. For example, your virtual server gets 200 + connections per second, the connection lasts for 200 seconds in + average in the connection table, the table size should be not far + less than 200x200, it is good to set the table size 32768 (2**15). Another note that each connection occupies 128 bytes effectively and each hash entry uses 8 bytes, so you can estimate how much memory is needed for your box. You can overwrite this number setting conn_tab_bits module parameter - or by appending ip_vs.conn_tab_bits=? to the kernel command line - if IP VS was compiled built-in. + or by appending ip_vs.conn_tab_bits=? to the kernel command line if + IP VS was compiled built-in. comment "IPVS transport protocol load balancing support" diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 13534e02346c..e1b9b52909a5 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1484,8 +1484,8 @@ int __init ip_vs_conn_init(void) int idx; /* Compute size and mask */ - if (ip_vs_conn_tab_bits < 8 || ip_vs_conn_tab_bits > 20) { - pr_info("conn_tab_bits not in [8, 20]. Using default value\n"); + if (ip_vs_conn_tab_bits < 8 || ip_vs_conn_tab_bits > 27) { + pr_info("conn_tab_bits not in [8, 27]. Using default value\n"); ip_vs_conn_tab_bits = CONFIG_IP_VS_TAB_BITS; } ip_vs_conn_tab_size = 1 << ip_vs_conn_tab_bits; -- cgit 1.4.1 From 4fadf53fa95142f01f215012e97c384529759a72 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 5 Jun 2023 16:49:45 -0700 Subject: btrfs: add handling for RAID1C23/DUP to btrfs_reduce_alloc_profile commit 160fe8f6fdb13da6111677be6263e5d65e875987 upstream. Callers of `btrfs_reduce_alloc_profile` expect it to return exactly one allocation profile flag, and failing to do so may ultimately result in a WARN_ON and remount-ro when allocating new blocks, like the below transaction abort on 6.1. `btrfs_reduce_alloc_profile` has two ways of determining the profile, first it checks if a conversion balance is currently running and uses the profile we're converting to. If no balance is currently running, it returns the max-redundancy profile which at least one block in the selected block group has. This works by simply checking each known allocation profile bit in redundancy order. However, `btrfs_reduce_alloc_profile` has not been updated as new flags have been added - first with the `DUP` profile and later with the RAID1C34 profiles. Because of the way it checks, if we have blocks with different profiles and at least one is known, that profile will be selected. However, if none are known we may return a flag set with multiple allocation profiles set. This is currently only possible when a balance from one of the three unhandled profiles to another of the unhandled profiles is canceled after allocating at least one block using the new profile. In that case, a transaction abort like the below will occur and the filesystem will need to be mounted with -o skip_balance to get it mounted rw again (but the balance cannot be resumed without a similar abort). [770.648] ------------[ cut here ]------------ [770.648] BTRFS: Transaction aborted (error -22) [770.648] WARNING: CPU: 43 PID: 1159593 at fs/btrfs/extent-tree.c:4122 find_free_extent+0x1d94/0x1e00 [btrfs] [770.648] CPU: 43 PID: 1159593 Comm: btrfs Tainted: G W 6.1.0-0.deb11.7-powerpc64le #1 Debian 6.1.20-2~bpo11+1a~test [770.648] Hardware name: T2P9D01 REV 1.00 POWER9 0x4e1202 opal:skiboot-bc106a0 PowerNV [770.648] NIP: c00800000f6784fc LR: c00800000f6784f8 CTR: c000000000d746c0 [770.648] REGS: c000200089afe9a0 TRAP: 0700 Tainted: G W (6.1.0-0.deb11.7-powerpc64le Debian 6.1.20-2~bpo11+1a~test) [770.648] MSR: 9000000002029033 CR: 28848282 XER: 20040000 [770.648] CFAR: c000000000135110 IRQMASK: 0 GPR00: c00800000f6784f8 c000200089afec40 c00800000f7ea800 0000000000000026 GPR04: 00000001004820c2 c000200089afea00 c000200089afe9f8 0000000000000027 GPR08: c000200ffbfe7f98 c000000002127f90 ffffffffffffffd8 0000000026d6a6e8 GPR12: 0000000028848282 c000200fff7f3800 5deadbeef0000122 c00000002269d000 GPR16: c0002008c7797c40 c000200089afef17 0000000000000000 0000000000000000 GPR20: 0000000000000000 0000000000000001 c000200008bc5a98 0000000000000001 GPR24: 0000000000000000 c0000003c73088d0 c000200089afef17 c000000016d3a800 GPR28: c0000003c7308800 c00000002269d000 ffffffffffffffea 0000000000000001 [770.648] NIP [c00800000f6784fc] find_free_extent+0x1d94/0x1e00 [btrfs] [770.648] LR [c00800000f6784f8] find_free_extent+0x1d90/0x1e00 [btrfs] [770.648] Call Trace: [770.648] [c000200089afec40] [c00800000f6784f8] find_free_extent+0x1d90/0x1e00 [btrfs] (unreliable) [770.648] [c000200089afed30] [c00800000f681398] btrfs_reserve_extent+0x1a0/0x2f0 [btrfs] [770.648] [c000200089afeea0] [c00800000f681bf0] btrfs_alloc_tree_block+0x108/0x670 [btrfs] [770.648] [c000200089afeff0] [c00800000f66bd68] __btrfs_cow_block+0x170/0x850 [btrfs] [770.648] [c000200089aff100] [c00800000f66c58c] btrfs_cow_block+0x144/0x288 [btrfs] [770.648] [c000200089aff1b0] [c00800000f67113c] btrfs_search_slot+0x6b4/0xcb0 [btrfs] [770.648] [c000200089aff2a0] [c00800000f679f60] lookup_inline_extent_backref+0x128/0x7c0 [btrfs] [770.648] [c000200089aff3b0] [c00800000f67b338] lookup_extent_backref+0x70/0x190 [btrfs] [770.648] [c000200089aff470] [c00800000f67b54c] __btrfs_free_extent+0xf4/0x1490 [btrfs] [770.648] [c000200089aff5a0] [c00800000f67d770] __btrfs_run_delayed_refs+0x328/0x1530 [btrfs] [770.648] [c000200089aff740] [c00800000f67ea2c] btrfs_run_delayed_refs+0xb4/0x3e0 [btrfs] [770.648] [c000200089aff800] [c00800000f699aa4] btrfs_commit_transaction+0x8c/0x12b0 [btrfs] [770.648] [c000200089aff8f0] [c00800000f6dc628] reset_balance_state+0x1c0/0x290 [btrfs] [770.648] [c000200089aff9a0] [c00800000f6e2f7c] btrfs_balance+0x1164/0x1500 [btrfs] [770.648] [c000200089affb40] [c00800000f6f8e4c] btrfs_ioctl+0x2b54/0x3100 [btrfs] [770.648] [c000200089affc80] [c00000000053be14] sys_ioctl+0x794/0x1310 [770.648] [c000200089affd70] [c00000000002af98] system_call_exception+0x138/0x250 [770.648] [c000200089affe10] [c00000000000c654] system_call_common+0xf4/0x258 [770.648] --- interrupt: c00 at 0x7fff94126800 [770.648] NIP: 00007fff94126800 LR: 0000000107e0b594 CTR: 0000000000000000 [770.648] REGS: c000200089affe80 TRAP: 0c00 Tainted: G W (6.1.0-0.deb11.7-powerpc64le Debian 6.1.20-2~bpo11+1a~test) [770.648] MSR: 900000000000d033 CR: 24002848 XER: 00000000 [770.648] IRQMASK: 0 GPR00: 0000000000000036 00007fffc9439da0 00007fff94217100 0000000000000003 GPR04: 00000000c4009420 00007fffc9439ee8 0000000000000000 0000000000000000 GPR08: 00000000803c7416 0000000000000000 0000000000000000 0000000000000000 GPR12: 0000000000000000 00007fff9467d120 0000000107e64c9c 0000000107e64d0a GPR16: 0000000107e64d06 0000000107e64cf1 0000000107e64cc4 0000000107e64c73 GPR20: 0000000107e64c31 0000000107e64bf1 0000000107e64be7 0000000000000000 GPR24: 0000000000000000 00007fffc9439ee0 0000000000000003 0000000000000001 GPR28: 00007fffc943f713 0000000000000000 00007fffc9439ee8 0000000000000000 [770.648] NIP [00007fff94126800] 0x7fff94126800 [770.648] LR [0000000107e0b594] 0x107e0b594 [770.648] --- interrupt: c00 [770.648] Instruction dump: [770.648] 3b00ffe4 e8898828 481175f5 60000000 4bfff4fc 3be00000 4bfff570 3d220000 [770.648] 7fc4f378 e8698830 4811cd95 e8410018 <0fe00000> f9c10060 f9e10068 fa010070 [770.648] ---[ end trace 0000000000000000 ]--- [770.648] BTRFS: error (device dm-2: state A) in find_free_extent_update_loop:4122: errno=-22 unknown [770.648] BTRFS info (device dm-2: state EA): forced readonly [770.648] BTRFS: error (device dm-2: state EA) in __btrfs_free_extent:3070: errno=-22 unknown [770.648] BTRFS error (device dm-2: state EA): failed to run delayed ref for logical 17838685708288 num_bytes 24576 type 184 action 2 ref_mod 1: -22 [770.648] BTRFS: error (device dm-2: state EA) in btrfs_run_delayed_refs:2144: errno=-22 unknown [770.648] BTRFS: error (device dm-2: state EA) in reset_balance_state:3599: errno=-22 unknown Fixes: 47e6f7423b91 ("btrfs: add support for 3-copy replication (raid1c3)") Fixes: 8d6fac0087e5 ("btrfs: add support for 4-copy replication (raid1c4)") CC: stable@vger.kernel.org # 5.10+ Signed-off-by: Matt Corallo Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/block-group.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 74a5c94898b0..e70039cd789d 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -79,14 +79,21 @@ static u64 btrfs_reduce_alloc_profile(struct btrfs_fs_info *fs_info, u64 flags) } allowed &= flags; - if (allowed & BTRFS_BLOCK_GROUP_RAID6) + /* Select the highest-redundancy RAID level. */ + if (allowed & BTRFS_BLOCK_GROUP_RAID1C4) + allowed = BTRFS_BLOCK_GROUP_RAID1C4; + else if (allowed & BTRFS_BLOCK_GROUP_RAID6) allowed = BTRFS_BLOCK_GROUP_RAID6; + else if (allowed & BTRFS_BLOCK_GROUP_RAID1C3) + allowed = BTRFS_BLOCK_GROUP_RAID1C3; else if (allowed & BTRFS_BLOCK_GROUP_RAID5) allowed = BTRFS_BLOCK_GROUP_RAID5; else if (allowed & BTRFS_BLOCK_GROUP_RAID10) allowed = BTRFS_BLOCK_GROUP_RAID10; else if (allowed & BTRFS_BLOCK_GROUP_RAID1) allowed = BTRFS_BLOCK_GROUP_RAID1; + else if (allowed & BTRFS_BLOCK_GROUP_DUP) + allowed = BTRFS_BLOCK_GROUP_DUP; else if (allowed & BTRFS_BLOCK_GROUP_RAID0) allowed = BTRFS_BLOCK_GROUP_RAID0; -- cgit 1.4.1 From 85608610951515be02dc5a22ded5b0f19e0c06b3 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Tue, 6 Jun 2023 14:36:33 +0900 Subject: btrfs: delete unused BGs while reclaiming BGs commit 3ed01616bad6c7e3de196676b542ae3df8058592 upstream. The reclaiming process only starts after the filesystem volumes are allocated to a certain level (75% by default). Thus, the list of reclaiming target block groups can build up so huge at the time the reclaim process kicks in. On a test run, there were over 1000 BGs in the reclaim list. As the reclaim involves rewriting the data, it takes really long time to reclaim the BGs. While the reclaim is running, btrfs_delete_unused_bgs() won't proceed because the reclaim side is holding fs_info->reclaim_bgs_lock. As a result, we will have a large number of unused BGs kept in the unused list. On my test run, I got 1057 unused BGs. Since deleting a block group is relatively easy and fast work, we can call btrfs_delete_unused_bgs() while it reclaims BGs, to avoid building up unused BGs. Fixes: 18bb8bbf13c1 ("btrfs: zoned: automatically reclaim zones") CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Filipe Manana Reviewed-by: Johannes Thumshirn Signed-off-by: Naohiro Aota Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/block-group.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index e70039cd789d..f1a918c07352 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1636,10 +1636,24 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) next: btrfs_put_block_group(bg); + + mutex_unlock(&fs_info->reclaim_bgs_lock); + /* + * Reclaiming all the block groups in the list can take really + * long. Prioritize cleaning up unused block groups. + */ + btrfs_delete_unused_bgs(fs_info); + /* + * If we are interrupted by a balance, we can just bail out. The + * cleaner thread restart again if necessary. + */ + if (!mutex_trylock(&fs_info->reclaim_bgs_lock)) + goto end; spin_lock(&fs_info->unused_bgs_lock); } spin_unlock(&fs_info->unused_bgs_lock); mutex_unlock(&fs_info->reclaim_bgs_lock); +end: btrfs_exclop_finish(fs_info); sb_end_write(fs_info->sb); } -- cgit 1.4.1 From 3702c5342cf17c4cda6d53dec6c8261e289a85bc Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Tue, 6 Jun 2023 14:36:35 +0900 Subject: btrfs: bail out reclaim process if filesystem is read-only commit 93463ff7b54626f8276c0bd3d3f968fbf8d5d380 upstream. When a filesystem is read-only, we cannot reclaim a block group as it cannot rewrite the data. Just bail out in that case. Note that it can drop block groups in this case. As we did sb_start_write(), read-only filesystem means we got a fatal error and forced read-only. There is no chance to reclaim them again. Fixes: 18bb8bbf13c1 ("btrfs: zoned: automatically reclaim zones") CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Filipe Manana Reviewed-by: Johannes Thumshirn Signed-off-by: Naohiro Aota Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/block-group.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index f1a918c07352..94f50bc8df0d 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1603,8 +1603,15 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) } spin_unlock(&bg->lock); - /* Get out fast, in case we're unmounting the filesystem */ - if (btrfs_fs_closing(fs_info)) { + /* + * Get out fast, in case we're read-only or unmounting the + * filesystem. It is OK to drop block groups from the list even + * for the read-only case. As we did sb_start_write(), + * "mount -o remount,ro" won't happen and read-only filesystem + * means it is forced read-only due to a fatal error. So, it + * never gets back to read-write to let us reclaim again. + */ + if (btrfs_need_cleaner_sleep(fs_info)) { up_write(&space_info->groups_sem); goto next; } -- cgit 1.4.1 From d9f1e518ab05dcc657e5ce318fa9418ee3db07e2 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 1 Jun 2023 00:33:01 +0200 Subject: btrfs: add block-group tree to lockdep classes commit 1a1b0e729d227f9f758f7b5f1c997e874e94156e upstream. The block group tree was not present among the lockdep classes. We could get potentially lockdep warnings but so far none has been seen, also because block-group-tree is a relatively new feature. CC: stable@vger.kernel.org # 6.1+ Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/locking.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c index 0eab3cb274a1..df98e64d7dc6 100644 --- a/fs/btrfs/locking.c +++ b/fs/btrfs/locking.c @@ -56,8 +56,8 @@ static struct btrfs_lockdep_keyset { u64 id; /* root objectid */ - /* Longest entry: btrfs-free-space-00 */ - char names[BTRFS_MAX_LEVEL][20]; + /* Longest entry: btrfs-block-group-00 */ + char names[BTRFS_MAX_LEVEL][24]; struct lock_class_key keys[BTRFS_MAX_LEVEL]; } btrfs_lockdep_keysets[] = { { .id = BTRFS_ROOT_TREE_OBJECTID, DEFINE_NAME("root") }, @@ -71,6 +71,7 @@ static struct btrfs_lockdep_keyset { { .id = BTRFS_DATA_RELOC_TREE_OBJECTID, DEFINE_NAME("dreloc") }, { .id = BTRFS_UUID_TREE_OBJECTID, DEFINE_NAME("uuid") }, { .id = BTRFS_FREE_SPACE_TREE_OBJECTID, DEFINE_NAME("free-space") }, + { .id = BTRFS_BLOCK_GROUP_TREE_OBJECTID, DEFINE_NAME("block-group") }, { .id = 0, DEFINE_NAME("tree") }, }; -- cgit 1.4.1 From 9634e5360bd3ac86788fbbce426c862cb58b6fd9 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Tue, 6 Jun 2023 14:36:36 +0900 Subject: btrfs: reinsert BGs failed to reclaim commit 7e27180994383b7c741ad87749db01e4989a02ba upstream. The reclaim process can temporarily fail. For example, if the space is getting tight, it fails to make the block group read-only. If there are no further writes on that block group, the block group will never get back to the reclaim list, and the BG never gets reclaimed. In a certain workload, we can leave many such block groups never reclaimed. So, let's get it back to the list and give it a chance to be reclaimed. Fixes: 18bb8bbf13c1 ("btrfs: zoned: automatically reclaim zones") CC: stable@vger.kernel.org # 5.15+ Signed-off-by: Naohiro Aota Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/block-group.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 94f50bc8df0d..30b264eb9d20 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1642,6 +1642,8 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) } next: + if (ret) + btrfs_mark_bg_to_reclaim(bg); btrfs_put_block_group(bg); mutex_unlock(&fs_info->reclaim_bgs_lock); -- cgit 1.4.1 From a53d78d9a8551e72c46ded23e8b0a56e55d32032 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 19 Jun 2023 17:21:47 +0100 Subject: btrfs: fix race when deleting quota root from the dirty cow roots list commit b31cb5a6eb7a48b0a7bfdf06832b1fd5088d8c79 upstream. When disabling quotas we are deleting the quota root from the list fs_info->dirty_cowonly_roots without taking the lock that protects it, which is struct btrfs_fs_info::trans_lock. This unsynchronized list manipulation may cause chaos if there's another concurrent manipulation of this list, such as when adding a root to it with ctree.c:add_root_to_dirty_list(). This can result in all sorts of weird failures caused by a race, such as the following crash: [337571.278245] general protection fault, probably for non-canonical address 0xdead000000000108: 0000 [#1] PREEMPT SMP PTI [337571.278933] CPU: 1 PID: 115447 Comm: btrfs Tainted: G W 6.4.0-rc6-btrfs-next-134+ #1 [337571.279153] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a1990b-prebuilt.qemu.org 04/01/2014 [337571.279572] RIP: 0010:commit_cowonly_roots+0x11f/0x250 [btrfs] [337571.279928] Code: 85 38 06 00 (...) [337571.280363] RSP: 0018:ffff9f63446efba0 EFLAGS: 00010206 [337571.280582] RAX: ffff942d98ec2638 RBX: ffff9430b82b4c30 RCX: 0000000449e1c000 [337571.280798] RDX: dead000000000100 RSI: ffff9430021e4900 RDI: 0000000000036070 [337571.281015] RBP: ffff942d98ec2000 R08: ffff942d98ec2000 R09: 000000000000015b [337571.281254] R10: 0000000000000009 R11: 0000000000000001 R12: ffff942fe8fbf600 [337571.281476] R13: ffff942dabe23040 R14: ffff942dabe20800 R15: ffff942d92cf3b48 [337571.281723] FS: 00007f478adb7340(0000) GS:ffff94349fa40000(0000) knlGS:0000000000000000 [337571.281950] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [337571.282184] CR2: 00007f478ab9a3d5 CR3: 000000001e02c001 CR4: 0000000000370ee0 [337571.282416] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [337571.282647] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [337571.282874] Call Trace: [337571.283101] [337571.283327] ? __die_body+0x1b/0x60 [337571.283570] ? die_addr+0x39/0x60 [337571.283796] ? exc_general_protection+0x22e/0x430 [337571.284022] ? asm_exc_general_protection+0x22/0x30 [337571.284251] ? commit_cowonly_roots+0x11f/0x250 [btrfs] [337571.284531] btrfs_commit_transaction+0x42e/0xf90 [btrfs] [337571.284803] ? _raw_spin_unlock+0x15/0x30 [337571.285031] ? release_extent_buffer+0x103/0x130 [btrfs] [337571.285305] reset_balance_state+0x152/0x1b0 [btrfs] [337571.285578] btrfs_balance+0xa50/0x11e0 [btrfs] [337571.285864] ? __kmem_cache_alloc_node+0x14a/0x410 [337571.286086] btrfs_ioctl+0x249a/0x3320 [btrfs] [337571.286358] ? mod_objcg_state+0xd2/0x360 [337571.286577] ? refill_obj_stock+0xb0/0x160 [337571.286798] ? seq_release+0x25/0x30 [337571.287016] ? __rseq_handle_notify_resume+0x3ba/0x4b0 [337571.287235] ? percpu_counter_add_batch+0x2e/0xa0 [337571.287455] ? __x64_sys_ioctl+0x88/0xc0 [337571.287675] __x64_sys_ioctl+0x88/0xc0 [337571.287901] do_syscall_64+0x38/0x90 [337571.288126] entry_SYSCALL_64_after_hwframe+0x72/0xdc [337571.288352] RIP: 0033:0x7f478aaffe9b So fix this by locking struct btrfs_fs_info::trans_lock before deleting the quota root from that list. Fixes: bed92eae26cc ("Btrfs: qgroup implementation and prototypes") CC: stable@vger.kernel.org # 4.14+ Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/qgroup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index f8b054898885..04ca0a4075b6 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1296,7 +1296,9 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info) goto out; } + spin_lock(&fs_info->trans_lock); list_del("a_root->dirty_list); + spin_unlock(&fs_info->trans_lock); btrfs_tree_lock(quota_root->node); btrfs_clean_tree_block(quota_root->node); -- cgit 1.4.1 From bc662a1e1f9faee90c914b39b5ee08f0e5fd5de4 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 8 Jun 2023 11:27:38 +0100 Subject: btrfs: fix extent buffer leak after tree mod log failure at split_node() commit ede600e497b1461d06d22a7d17703d9096868bc3 upstream. At split_node(), if we fail to log the tree mod log copy operation, we return without unlocking the split extent buffer we just allocated and without decrementing the reference we own on it. Fix this by unlocking it and decrementing the ref count before returning. Fixes: 5de865eebb83 ("Btrfs: fix tree mod logging") CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ctree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 9a20b9e3ebd0..9aed5c7c2734 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2861,6 +2861,8 @@ static noinline int split_node(struct btrfs_trans_handle *trans, ret = btrfs_tree_mod_log_eb_copy(split, c, 0, mid, c_nritems - mid); if (ret) { + btrfs_tree_unlock(split); + free_extent_buffer(split); btrfs_abort_transaction(trans, ret); return ret; } -- cgit 1.4.1 From 3929b5dd8fdd3c99e299b5d9fd0de7e9c1d2da3c Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 8 Jun 2023 11:27:40 +0100 Subject: btrfs: do not BUG_ON() on tree mod log failure at __btrfs_cow_block() commit 40b0a749388517de244643c09bdbb98f7dcb6ef1 upstream. At __btrfs_cow_block(), instead of doing a BUG_ON() in case we fail to record a tree mod log root insertion operation, do a transaction abort instead. There's really no need for the BUG_ON(), we can properly release all resources in this context and turn the filesystem to RO mode and in an error state instead. CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ctree.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 9aed5c7c2734..1a327eb3580b 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -475,9 +475,14 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV) parent_start = buf->start; - atomic_inc(&cow->refs); ret = btrfs_tree_mod_log_insert_root(root->node, cow, true); - BUG_ON(ret < 0); + if (ret < 0) { + btrfs_tree_unlock(cow); + free_extent_buffer(cow); + btrfs_abort_transaction(trans, ret); + return ret; + } + atomic_inc(&cow->refs); rcu_assign_pointer(root->node, cow); btrfs_free_tree_block(trans, btrfs_root_id(root), buf, -- cgit 1.4.1 From 1c9b356bbe99570aaba30fac6da159b393a53ec6 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 12 Jun 2023 11:05:32 +0200 Subject: ASoC: mediatek: mt8173: Fix irq error path commit f9c058d14f4fe23ef523a7ff73734d51c151683c upstream. After reordering the irq probe, the error path was not properly done. Lets fix it. Reported-by: Dan Carpenter Cc: stable@kernel.org Fixes: 4cbb264d4e91 ("ASoC: mediatek: mt8173: Enable IRQ when pdata is ready") Signed-off-by: Ricardo Ribalda Delgado Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230612-mt8173-fixup-v2-2-432aa99ce24d@chromium.org Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8173/mt8173-afe-pcm.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index bc155dd937e0..50097a8e1b49 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1070,6 +1070,10 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = &pdev->dev; + irq_id = platform_get_irq(pdev, 0); + if (irq_id <= 0) + return irq_id < 0 ? irq_id : -ENXIO; + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); @@ -1175,14 +1179,11 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) goto err_cleanup_components; - irq_id = platform_get_irq(pdev, 0); - if (irq_id <= 0) - return irq_id < 0 ? irq_id : -ENXIO; ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler, 0, "Afe_ISR_Handle", (void *)afe); if (ret) { dev_err(afe->dev, "could not request_irq\n"); - goto err_pm_disable; + goto err_cleanup_components; } dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n"); -- cgit 1.4.1 From d9eaa90d7dbb2d85daddc6e466b9bf3ddb213667 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 12 Jun 2023 11:05:31 +0200 Subject: ASoC: mediatek: mt8173: Fix snd_soc_component_initialize error path commit a46d37012a5be1737393b8f82fd35665e4556eee upstream. If the second component fails to initialize, cleanup the first on. Reported-by: Dan Carpenter Cc: stable@kernel.org Fixes: f1b5bf07365d ("ASoC: mt2701/mt8173: replace platform to component") Signed-off-by: Ricardo Ribalda Delgado Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230612-mt8173-fixup-v2-1-432aa99ce24d@chromium.org Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8173/mt8173-afe-pcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 50097a8e1b49..a41611671ef6 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1160,14 +1160,14 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL); if (!comp_hdmi) { ret = -ENOMEM; - goto err_pm_disable; + goto err_cleanup_components; } ret = snd_soc_component_initialize(comp_hdmi, &mt8173_afe_hdmi_dai_component, &pdev->dev); if (ret) - goto err_pm_disable; + goto err_cleanup_components; #ifdef CONFIG_DEBUG_FS comp_hdmi->debugfs_prefix = "hdmi"; -- cgit 1.4.1 From 9030a7e83632b20229d47b25bcaeb489015c125f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 7 May 2023 16:46:56 +0200 Subject: regulator: tps65219: Fix matching interrupts for their regulators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f050e56de80591fee55bedbdf5b6b998c740cd0c upstream. The driver's probe() first registers regulators in a loop and then in a second loop passes them as irq data to the interrupt handlers. However the function to get the regulator for given name tps65219_get_rdev_by_name() was a no-op due to argument passed by value, not pointer, thus the second loop assigned always same value - from previous loop. The interrupts, when fired, where executed with wrong data. Compiler also noticed it: drivers/regulator/tps65219-regulator.c: In function ‘tps65219_get_rdev_by_name’: drivers/regulator/tps65219-regulator.c:292:60: error: parameter ‘dev’ set but not used [-Werror=unused-but-set-parameter] Fixes: c12ac5fc3e0a ("regulator: drivers: Add TI TPS65219 PMIC regulators support") Cc: --- drivers/regulator/tps65219-regulator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c index 58f6541b6417..b0d8d6fed24d 100644 --- a/drivers/regulator/tps65219-regulator.c +++ b/drivers/regulator/tps65219-regulator.c @@ -289,13 +289,13 @@ static irqreturn_t tps65219_regulator_irq_handler(int irq, void *data) static int tps65219_get_rdev_by_name(const char *regulator_name, struct regulator_dev *rdevtbl[7], - struct regulator_dev *dev) + struct regulator_dev **dev) { int i; for (i = 0; i < ARRAY_SIZE(regulators); i++) { if (strcmp(regulator_name, regulators[i].name) == 0) { - dev = rdevtbl[i]; + *dev = rdevtbl[i]; return 0; } } @@ -348,7 +348,7 @@ static int tps65219_regulator_probe(struct platform_device *pdev) irq_data[i].dev = tps->dev; irq_data[i].type = irq_type; - tps65219_get_rdev_by_name(irq_type->regulator_name, rdevtbl, rdev); + tps65219_get_rdev_by_name(irq_type->regulator_name, rdevtbl, &rdev); if (IS_ERR(rdev)) { dev_err(tps->dev, "Failed to get rdev for %s\n", irq_type->regulator_name); -- cgit 1.4.1 From 600b51aa44de8b4efb1c3a265dda0dd6c0e968f1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 20 Apr 2023 09:28:11 +0200 Subject: ARM: dts: qcom: ipq4019: fix broken NAND controller properties override commit edcbdd57de499305e2a3737d4a73fe387f71d84c upstream. After renaming NAND controller node name from "qpic-nand" to "nand-controller", the board DTS/DTSI also have to be updated: Warning (unit_address_vs_reg): /soc/qpic-nand@79b0000: node has a unit name, but no reg or ranges property Cc: Fixes: 9e1e00f18afc ("ARM: dts: qcom: Fix node name for NAND controller node") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Reviewed-by: Manivannan Sadhasivam Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230420072811.36947-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts | 8 ++++---- arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi | 10 +++++----- arch/arm/boot/dts/qcom-ipq4019-ap.dk07.1.dtsi | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts index 79b0c6318e52..0993f840d1fc 100644 --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts @@ -11,9 +11,9 @@ dma-controller@7984000 { status = "okay"; }; - - qpic-nand@79b0000 { - status = "okay"; - }; }; }; + +&nand { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi index a63b3778636d..468ebc40d2ad 100644 --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi @@ -102,10 +102,10 @@ status = "okay"; perst-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; }; - - qpic-nand@79b0000 { - pinctrl-0 = <&nand_pins>; - pinctrl-names = "default"; - }; }; }; + +&nand { + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; +}; diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk07.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk07.1.dtsi index 0107f552f520..7ef635997efa 100644 --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk07.1.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk07.1.dtsi @@ -65,11 +65,11 @@ dma-controller@7984000 { status = "okay"; }; - - qpic-nand@79b0000 { - pinctrl-0 = <&nand_pins>; - pinctrl-names = "default"; - status = "okay"; - }; }; }; + +&nand { + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + status = "okay"; +}; -- cgit 1.4.1 From 9077ec19adc24ef0a3dad8e3cf77a902de2a4f11 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 17:31:05 +0200 Subject: ARM: orion5x: fix d2net gpio initialization commit f8ef1233939495c405a9faa4bd1ae7d3f581bae4 upstream. The DT version of this board has a custom file with the gpio device. However, it does nothing because the d2net_init() has no caller or prototype: arch/arm/mach-orion5x/board-d2net.c:101:13: error: no previous prototype for 'd2net_init' Call it from the board-dt file as intended. Fixes: 94b0bd366e36 ("ARM: orion5x: convert d2net to Device Tree") Reviewed-by: Andrew Lunn Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230516153109.514251-10-arnd@kernel.org Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-orion5x/board-dt.c | 3 +++ arch/arm/mach-orion5x/common.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/arch/arm/mach-orion5x/board-dt.c b/arch/arm/mach-orion5x/board-dt.c index e3736ffc8347..be47492c6640 100644 --- a/arch/arm/mach-orion5x/board-dt.c +++ b/arch/arm/mach-orion5x/board-dt.c @@ -60,6 +60,9 @@ static void __init orion5x_dt_init(void) if (of_machine_is_compatible("maxtor,shared-storage-2")) mss2_init(); + if (of_machine_is_compatible("lacie,d2-network")) + d2net_init(); + of_platform_default_populate(NULL, orion5x_auxdata_lookup, NULL); } diff --git a/arch/arm/mach-orion5x/common.h b/arch/arm/mach-orion5x/common.h index eb96009e21c4..b9cfdb456456 100644 --- a/arch/arm/mach-orion5x/common.h +++ b/arch/arm/mach-orion5x/common.h @@ -75,6 +75,12 @@ extern void mss2_init(void); static inline void mss2_init(void) {} #endif +#ifdef CONFIG_MACH_D2NET_DT +void d2net_init(void); +#else +static inline void d2net_init(void) {} +#endif + /***************************************************************************** * Helpers to access Orion registers ****************************************************************************/ -- cgit 1.4.1 From ab0bd172d6289310a05a0cd15e1432e828d386ae Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 19 Apr 2023 23:07:39 +0200 Subject: leds: trigger: netdev: Recheck NETDEV_LED_MODE_LINKUP on dev rename commit cee4bd16c3195a701be683f7da9e88c6e11acb73 upstream. Dev can be renamed also while up for supported device. We currently wrongly clear the NETDEV_LED_MODE_LINKUP flag on NETDEV_CHANGENAME event. Fix this by rechecking if the carrier is ok on NETDEV_CHANGENAME and correctly set the NETDEV_LED_MODE_LINKUP bit. Fixes: 5f820ed52371 ("leds: trigger: netdev: fix handling on interface rename") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Christian Marangi Reviewed-by: Andrew Lunn Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230419210743.3594-2-ansuelsmth@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/leds/trigger/ledtrig-netdev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index d5e774d83021..f4d670ec30bc 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -318,6 +318,9 @@ static int netdev_trig_notify(struct notifier_block *nb, clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); switch (evt) { case NETDEV_CHANGENAME: + if (netif_carrier_ok(dev)) + set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); + fallthrough; case NETDEV_REGISTER: if (trigger_data->net_dev) dev_put(trigger_data->net_dev); -- cgit 1.4.1 From d53879f54b54a598609f21296e250bcf3fe604b0 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Sat, 10 Jun 2023 10:20:01 +0800 Subject: blktrace: use inline function for blk_trace_remove() while blktrace is disabled commit cbe7cff4a76bc749dd70264ca5cf924e2adf9296 upstream. If config is disabled, call blk_trace_remove() directly will trigger build warning, hence use inline function instead, prepare to fix blktrace debugfs entries leakage. Signed-off-by: Yu Kuai Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230610022003.2557284-2-yukuai1@huaweicloud.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- include/linux/blktrace_api.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index cfbda114348c..122c62e561fc 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -85,10 +85,14 @@ extern int blk_trace_remove(struct request_queue *q); # define blk_add_driver_data(rq, data, len) do {} while (0) # define blk_trace_setup(q, name, dev, bdev, arg) (-ENOTTY) # define blk_trace_startstop(q, start) (-ENOTTY) -# define blk_trace_remove(q) (-ENOTTY) # define blk_add_trace_msg(q, fmt, ...) do { } while (0) # define blk_add_cgroup_trace_msg(q, cg, fmt, ...) do { } while (0) # define blk_trace_note_message_enabled(q) (false) + +static inline int blk_trace_remove(struct request_queue *q) +{ + return -ENOTTY; +} #endif /* CONFIG_BLK_DEV_IO_TRACE */ #ifdef CONFIG_COMPAT -- cgit 1.4.1 From 6b7c52f373fb7ed0cada1819d5b9c901faedbb00 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 3 Jul 2023 16:49:11 +0200 Subject: fs: no need to check source commit 66d8fc0539b0d49941f313c9509a8384e4245ac1 upstream. The @source inode must be valid. It is even checked via IS_SWAPFILE() above making it pretty clear. So no need to check it when we unlock. What doesn't need to exist is the @target inode. The lock_two_inodes() helper currently swaps the @inode1 and @inode2 arguments if @inode1 is NULL to have consistent lock class usage. However, we know that at least for vfs_rename() that @inode1 is @source and thus is never NULL as per above. We also know that @source is a different inode than @target as that is checked right at the beginning of vfs_rename(). So we know that @source is valid and locked and that @target is locked. So drop the check whether @source is non-NULL. Fixes: 28eceeda130f ("fs: Lock moved directories") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202307030026.9sE2pk2x-lkp@intel.com Message-Id: <20230703-vfs-rename-source-v1-1-37eebb29b65b@kernel.org> [brauner: use commit message from patch I sent concurrently] Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- fs/namei.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 41d71a29bd79..5b3865ad9d05 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4802,8 +4802,7 @@ int vfs_rename(struct renamedata *rd) d_exchange(old_dentry, new_dentry); } out: - if (source) - inode_unlock(source); + inode_unlock(source); if (target) inode_unlock(target); dput(new_dentry); -- cgit 1.4.1 From f6e37e24007d84a576ac63b39ab9ab54f9e50dcb Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sat, 15 Jul 2023 09:31:11 +0300 Subject: xfs: explicitly specify cpu when forcing inodegc delayed work to run immediately commit 03e0add80f4cf3f7393edb574eeb3a89a1db7758 upstream. I've been noticing odd racing behavior in the inodegc code that could only be explained by one cpu adding an inode to its inactivation llist at the same time that another cpu is processing that cpu's llist. Preemption is disabled between get/put_cpu_ptr, so the only explanation is scheduler mayhem. I inserted the following debug code into xfs_inodegc_worker (see the next patch): ASSERT(gc->cpu == smp_processor_id()); This assertion tripped during overnight tests on the arm64 machines, but curiously not on x86_64. I think we haven't observed any resource leaks here because the lockfree list code can handle simultaneous llist_add and llist_del_all functions operating on the same list. However, the whole point of having percpu inodegc lists is to take advantage of warm memory caches by inactivating inodes on the last processor to touch the inode. The incorrect scheduling seems to occur after an inodegc worker is subjected to mod_delayed_work(). This wraps mod_delayed_work_on with WORK_CPU_UNBOUND specified as the cpu number. Unbound allows for scheduling on any cpu, not necessarily the same one that scheduled the work. Because preemption is disabled for as long as we have the gc pointer, I think it's safe to use current_cpu() (aka smp_processor_id) to queue the delayed work item on the correct cpu. Fixes: 7cf2b0f9611b ("xfs: bound maximum wait time for inodegc work") Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner Signed-off-by: Amir Goldstein Acked-by: Darrick J. Wong Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_icache.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index eae7427062cf..536885f8b8a8 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -2052,7 +2052,8 @@ xfs_inodegc_queue( queue_delay = 0; trace_xfs_inodegc_queue(mp, __return_address); - mod_delayed_work(mp->m_inodegc_wq, &gc->work, queue_delay); + mod_delayed_work_on(current_cpu(), mp->m_inodegc_wq, &gc->work, + queue_delay); put_cpu_ptr(gc); if (xfs_inodegc_want_flush_work(ip, items, shrinker_hits)) { @@ -2096,7 +2097,8 @@ xfs_inodegc_cpu_dead( if (xfs_is_inodegc_enabled(mp)) { trace_xfs_inodegc_queue(mp, __return_address); - mod_delayed_work(mp->m_inodegc_wq, &gc->work, 0); + mod_delayed_work_on(current_cpu(), mp->m_inodegc_wq, &gc->work, + 0); } put_cpu_ptr(gc); } -- cgit 1.4.1 From 25c1991f9f7707b91f974c0843307a191d964d1a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sat, 15 Jul 2023 09:31:12 +0300 Subject: xfs: check that per-cpu inodegc workers actually run on that cpu commit b37c4c8339cd394ea6b8b415026603320a185651 upstream. Now that we've allegedly worked out the problem of the per-cpu inodegc workers being scheduled on the wrong cpu, let's put in a debugging knob to let us know if a worker ever gets mis-scheduled again. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner Signed-off-by: Amir Goldstein Acked-by: Darrick J. Wong Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_icache.c | 2 ++ fs/xfs/xfs_mount.h | 3 +++ fs/xfs/xfs_super.c | 3 +++ 3 files changed, 8 insertions(+) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 536885f8b8a8..7ce262dcabca 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1848,6 +1848,8 @@ xfs_inodegc_worker( struct llist_node *node = llist_del_all(&gc->list); struct xfs_inode *ip, *n; + ASSERT(gc->cpu == smp_processor_id()); + WRITE_ONCE(gc->items, 0); if (!node) diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 8aca2cc173ac..69ddd5319634 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -66,6 +66,9 @@ struct xfs_inodegc { /* approximate count of inodes in the list */ unsigned int items; unsigned int shrinker_hits; +#if defined(DEBUG) || defined(XFS_WARN) + unsigned int cpu; +#endif }; /* diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index ee4b429a2f2c..4b179526913f 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1084,6 +1084,9 @@ xfs_inodegc_init_percpu( for_each_possible_cpu(cpu) { gc = per_cpu_ptr(mp->m_inodegc, cpu); +#if defined(DEBUG) || defined(XFS_WARN) + gc->cpu = cpu; +#endif init_llist_head(&gc->list); gc->items = 0; INIT_DELAYED_WORK(&gc->work, xfs_inodegc_worker); -- cgit 1.4.1 From 1b206852957cdce29e9d7147ce3c39306e251950 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sat, 15 Jul 2023 09:31:13 +0300 Subject: xfs: disable reaping in fscounters scrub commit 2d5f38a31980d7090f5bf91021488dc61a0ba8ee upstream. The fscounters scrub code doesn't work properly because it cannot quiesce updates to the percpu counters in the filesystem, hence it returns false corruption reports. This has been fixed properly in one of the online repair patchsets that are under review by replacing the xchk_disable_reaping calls with an exclusive filesystem freeze. Disabling background gc isn't sufficient to fix the problem. In other words, scrub doesn't need to call xfs_inodegc_stop, which is just as well since it wasn't correct to allow scrub to call xfs_inodegc_start when something else could be calling xfs_inodegc_stop (e.g. trying to freeze the filesystem). Neuter the scrubber for now, and remove the xchk_*_reaping functions. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner Signed-off-by: Amir Goldstein Acked-by: Darrick J. Wong Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/common.c | 26 -------------------------- fs/xfs/scrub/common.h | 2 -- fs/xfs/scrub/fscounters.c | 13 ++++++------- fs/xfs/scrub/scrub.c | 2 -- fs/xfs/scrub/scrub.h | 1 - 5 files changed, 6 insertions(+), 38 deletions(-) diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index 9bbbf20f401b..e71449658ecc 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -865,29 +865,3 @@ xchk_ilock_inverted( } return -EDEADLOCK; } - -/* Pause background reaping of resources. */ -void -xchk_stop_reaping( - struct xfs_scrub *sc) -{ - sc->flags |= XCHK_REAPING_DISABLED; - xfs_blockgc_stop(sc->mp); - xfs_inodegc_stop(sc->mp); -} - -/* Restart background reaping of resources. */ -void -xchk_start_reaping( - struct xfs_scrub *sc) -{ - /* - * Readonly filesystems do not perform inactivation or speculative - * preallocation, so there's no need to restart the workers. - */ - if (!xfs_is_readonly(sc->mp)) { - xfs_inodegc_start(sc->mp); - xfs_blockgc_start(sc->mp); - } - sc->flags &= ~XCHK_REAPING_DISABLED; -} diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index 454145db10e7..2ca80102e704 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h @@ -148,7 +148,5 @@ static inline bool xchk_skip_xref(struct xfs_scrub_metadata *sm) int xchk_metadata_inode_forks(struct xfs_scrub *sc); int xchk_ilock_inverted(struct xfs_inode *ip, uint lock_mode); -void xchk_stop_reaping(struct xfs_scrub *sc); -void xchk_start_reaping(struct xfs_scrub *sc); #endif /* __XFS_SCRUB_COMMON_H__ */ diff --git a/fs/xfs/scrub/fscounters.c b/fs/xfs/scrub/fscounters.c index 6a6f8fe7f87c..88d6961e3886 100644 --- a/fs/xfs/scrub/fscounters.c +++ b/fs/xfs/scrub/fscounters.c @@ -128,13 +128,6 @@ xchk_setup_fscounters( if (error) return error; - /* - * Pause background reclaim while we're scrubbing to reduce the - * likelihood of background perturbations to the counters throwing off - * our calculations. - */ - xchk_stop_reaping(sc); - return xchk_trans_alloc(sc, 0); } @@ -353,6 +346,12 @@ xchk_fscounters( if (fdblocks > mp->m_sb.sb_dblocks) xchk_set_corrupt(sc); + /* + * XXX: We can't quiesce percpu counter updates, so exit early. + * This can be re-enabled when we gain exclusive freeze functionality. + */ + return 0; + /* * If ifree exceeds icount by more than the minimum variance then * something's probably wrong with the counters. diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 2e8e400f10a9..95132490fda5 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -171,8 +171,6 @@ xchk_teardown( } if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) mnt_drop_write_file(sc->file); - if (sc->flags & XCHK_REAPING_DISABLED) - xchk_start_reaping(sc); if (sc->buf) { kmem_free(sc->buf); sc->buf = NULL; diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h index 3de5287e98d8..4cb32c27df10 100644 --- a/fs/xfs/scrub/scrub.h +++ b/fs/xfs/scrub/scrub.h @@ -88,7 +88,6 @@ struct xfs_scrub { /* XCHK state flags grow up from zero, XREP state flags grown down from 2^31 */ #define XCHK_TRY_HARDER (1 << 0) /* can't get resources, try again */ -#define XCHK_REAPING_DISABLED (1 << 2) /* background block reaping paused */ #define XREP_ALREADY_FIXED (1 << 31) /* checking our repair work */ /* Metadata scrubbers */ -- cgit 1.4.1 From 9222068bc85b65122c8e68857043777f92898fd3 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sat, 15 Jul 2023 09:31:14 +0300 Subject: xfs: fix xfs_inodegc_stop racing with mod_delayed_work commit 2254a7396a0ca6309854948ee1c0a33fa4268cec upstream. syzbot reported this warning from the faux inodegc shrinker that tries to kick off inodegc work: ------------[ cut here ]------------ WARNING: CPU: 1 PID: 102 at kernel/workqueue.c:1445 __queue_work+0xd44/0x1120 kernel/workqueue.c:1444 RIP: 0010:__queue_work+0xd44/0x1120 kernel/workqueue.c:1444 Call Trace: __queue_delayed_work+0x1c8/0x270 kernel/workqueue.c:1672 mod_delayed_work_on+0xe1/0x220 kernel/workqueue.c:1746 xfs_inodegc_shrinker_scan fs/xfs/xfs_icache.c:2212 [inline] xfs_inodegc_shrinker_scan+0x250/0x4f0 fs/xfs/xfs_icache.c:2191 do_shrink_slab+0x428/0xaa0 mm/vmscan.c:853 shrink_slab+0x175/0x660 mm/vmscan.c:1013 shrink_one+0x502/0x810 mm/vmscan.c:5343 shrink_many mm/vmscan.c:5394 [inline] lru_gen_shrink_node mm/vmscan.c:5511 [inline] shrink_node+0x2064/0x35f0 mm/vmscan.c:6459 kswapd_shrink_node mm/vmscan.c:7262 [inline] balance_pgdat+0xa02/0x1ac0 mm/vmscan.c:7452 kswapd+0x677/0xd60 mm/vmscan.c:7712 kthread+0x2e8/0x3a0 kernel/kthread.c:376 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:308 This warning corresponds to this code in __queue_work: /* * For a draining wq, only works from the same workqueue are * allowed. The __WQ_DESTROYING helps to spot the issue that * queues a new work item to a wq after destroy_workqueue(wq). */ if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) && WARN_ON_ONCE(!is_chained_work(wq)))) return; For this to trip, we must have a thread draining the inodedgc workqueue and a second thread trying to queue inodegc work to that workqueue. This can happen if freezing or a ro remount race with reclaim poking our faux inodegc shrinker and another thread dropping an unlinked O_RDONLY file: Thread 0 Thread 1 Thread 2 xfs_inodegc_stop xfs_inodegc_shrinker_scan xfs_is_inodegc_enabled xfs_clear_inodegc_enabled xfs_inodegc_queue_all xfs_inodegc_queue xfs_is_inodegc_enabled drain_workqueue llist_empty mod_delayed_work_on(..., 0) __queue_work In other words, everything between the access to inodegc_enabled state and the decision to poke the inodegc workqueue requires some kind of coordination to avoid the WQ_DRAINING state. We could perhaps introduce a lock here, but we could also try to eliminate WQ_DRAINING from the picture. We could replace the drain_workqueue call with a loop that flushes the workqueue and queues workers as long as there is at least one inode present in the per-cpu inodegc llists. We've disabled inodegc at this point, so we know that the number of queued inodes will eventually hit zero as long as xfs_inodegc_start cannot reactivate the workers. There are four callers of xfs_inodegc_start. Three of them come from the VFS with s_umount held: filesystem thawing, failed filesystem freezing, and the rw remount transition. The fourth caller is mounting rw (no remount or freezing possible). There are three callers ofs xfs_inodegc_stop. One is unmounting (no remount or thaw possible). Two of them come from the VFS with s_umount held: fs freezing and ro remount transition. Hence, it is correct to replace the drain_workqueue call with a loop that drains the inodegc llists. Fixes: 6191cf3ad59f ("xfs: flush inodegc workqueue tasks before cancel") Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner Signed-off-by: Amir Goldstein Acked-by: Darrick J. Wong Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_icache.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 7ce262dcabca..d884cba1d707 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -431,18 +431,23 @@ xfs_iget_check_free_state( } /* Make all pending inactivation work start immediately. */ -static void +static bool xfs_inodegc_queue_all( struct xfs_mount *mp) { struct xfs_inodegc *gc; int cpu; + bool ret = false; for_each_online_cpu(cpu) { gc = per_cpu_ptr(mp->m_inodegc, cpu); - if (!llist_empty(&gc->list)) + if (!llist_empty(&gc->list)) { mod_delayed_work_on(cpu, mp->m_inodegc_wq, &gc->work, 0); + ret = true; + } } + + return ret; } /* @@ -1894,24 +1899,41 @@ xfs_inodegc_flush( /* * Flush all the pending work and then disable the inode inactivation background - * workers and wait for them to stop. + * workers and wait for them to stop. Caller must hold sb->s_umount to + * coordinate changes in the inodegc_enabled state. */ void xfs_inodegc_stop( struct xfs_mount *mp) { + bool rerun; + if (!xfs_clear_inodegc_enabled(mp)) return; + /* + * Drain all pending inodegc work, including inodes that could be + * queued by racing xfs_inodegc_queue or xfs_inodegc_shrinker_scan + * threads that sample the inodegc state just prior to us clearing it. + * The inodegc flag state prevents new threads from queuing more + * inodes, so we queue pending work items and flush the workqueue until + * all inodegc lists are empty. IOWs, we cannot use drain_workqueue + * here because it does not allow other unserialized mechanisms to + * reschedule inodegc work while this draining is in progress. + */ xfs_inodegc_queue_all(mp); - drain_workqueue(mp->m_inodegc_wq); + do { + flush_workqueue(mp->m_inodegc_wq); + rerun = xfs_inodegc_queue_all(mp); + } while (rerun); trace_xfs_inodegc_stop(mp, __return_address); } /* * Enable the inode inactivation background workers and schedule deferred inode - * inactivation work if there is any. + * inactivation work if there is any. Caller must hold sb->s_umount to + * coordinate changes in the inodegc_enabled state. */ void xfs_inodegc_start( -- cgit 1.4.1 From a02c6dc0eff249970a74c4111985b6c1fabe9851 Mon Sep 17 00:00:00 2001 From: "Liam R. Howlett" Date: Thu, 6 Jul 2023 14:51:35 -0400 Subject: mm/mmap: Fix extra maple tree write based on commit 0503ea8f5ba73eb3ab13a81c1eefbaf51405385a upstream. This was inadvertently fixed during the removal of __vma_adjust(). When __vma_adjust() is adjusting next with a negative value (pushing vma->vm_end lower), there would be two writes to the maple tree. The first write is unnecessary and uses all allocated nodes in the maple state. The second write is necessary but will need to allocate nodes since the first write has used the allocated nodes. This may be a problem as it may not be safe to allocate at this time, such as a low memory situation. Fix the issue by avoiding the first write and only write the adjusted "next" VMA. Reported-by: John Hsu Link: https://lore.kernel.org/lkml/9cb8c599b1d7f9c1c300d1a334d5eb70ec4d7357.camel@mediatek.com/ Cc: stable@vger.kernel.org Cc: linux-mm@kvack.org Signed-off-by: Liam R. Howlett Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/mmap.c b/mm/mmap.c index 1597a96b667f..41a240bd81df 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -767,7 +767,8 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, } if (end != vma->vm_end) { if (vma->vm_end > end) { - if (!insert || (insert->vm_start != end)) { + if ((vma->vm_end + adjust_next != end) && + (!insert || (insert->vm_start != end))) { vma_mas_szero(&mas, end, vma->vm_end); mas_reset(&mas); VM_WARN_ON(insert && -- cgit 1.4.1 From eaa0043a85795fd4ab10285750cabdf5c2abc8cd Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 22 Sep 2022 20:21:48 +0300 Subject: drm/i915: Fix TypeC mode initialization during system resume commit a82796a2e332d108b2d3aff38509caad370f69b5 upstream. During system resume DP MST requires AUX to be working already before the HW state readout of the given encoder. Since AUX requires the encoder/PHY TypeC mode to be initialized, which atm only happens during HW state readout, these AUX transfers can change the TypeC mode incorrectly (disconnecting the PHY for an enabled encoder) and trigger the state check WARNs in intel_tc_port_sanitize(). Fix this by initializing the TypeC mode earlier both during driver loading and system resume and making sure that the mode can't change until the encoder's state is read out. While at it add the missing DocBook comments and rename intel_tc_port_sanitize()->intel_tc_port_sanitize_mode() for consistency. Signed-off-by: Imre Deak Reviewed-by: Mika Kahola Link: https://patchwork.freedesktop.org/patch/msgid/20220922172148.2913088-1-imre.deak@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_ddi.c | 8 +++- drivers/gpu/drm/i915/display/intel_tc.c | 66 ++++++++++++++++++++++++-------- drivers/gpu/drm/i915/display/intel_tc.h | 3 +- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 69ecf2a3d6c6..706e2d956801 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3579,7 +3579,7 @@ static void intel_ddi_sync_state(struct intel_encoder *encoder, enum phy phy = intel_port_to_phy(i915, encoder->port); if (intel_phy_is_tc(i915, phy)) - intel_tc_port_sanitize(enc_to_dig_port(encoder)); + intel_tc_port_sanitize_mode(enc_to_dig_port(encoder)); if (crtc_state && intel_crtc_has_dp_encoder(crtc_state)) intel_dp_sync_state(encoder, crtc_state); @@ -3789,11 +3789,17 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder) static void intel_ddi_encoder_reset(struct drm_encoder *encoder) { + struct drm_i915_private *i915 = to_i915(encoder->dev); struct intel_dp *intel_dp = enc_to_intel_dp(to_intel_encoder(encoder)); + struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder)); + enum phy phy = intel_port_to_phy(i915, dig_port->base.port); intel_dp->reset_link_params = true; intel_pps_encoder_reset(intel_dp); + + if (intel_phy_is_tc(i915, phy)) + intel_tc_port_init_mode(dig_port); } static const struct drm_encoder_funcs intel_ddi_funcs = { diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 8d6dac32c896..eecdf5abb41c 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -686,19 +686,59 @@ static void intel_tc_port_update_mode(struct intel_digital_port *dig_port, static void intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port, int refcount) +{ + dig_port->tc_link_refcount = refcount; +} + +/** + * intel_tc_port_init_mode: Read out HW state and init the given port's TypeC mode + * @dig_port: digital port + * + * Read out the HW state and initialize the TypeC mode of @dig_port. The mode + * will be locked until intel_tc_port_sanitize_mode() is called. + */ +void intel_tc_port_init_mode(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + intel_wakeref_t tc_cold_wref; + enum intel_display_power_domain domain; + + mutex_lock(&dig_port->tc_lock); + drm_WARN_ON(&i915->drm, dig_port->tc_mode != TC_PORT_DISCONNECTED); + drm_WARN_ON(&i915->drm, dig_port->tc_lock_wakeref); drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount); - dig_port->tc_link_refcount = refcount; + + tc_cold_wref = tc_cold_block(dig_port, &domain); + + dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port); + /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */ + intel_tc_port_link_init_refcount(dig_port, 1); + dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); + + tc_cold_unblock(dig_port, domain, tc_cold_wref); + + drm_dbg_kms(&i915->drm, "Port %s: init mode (%s)\n", + dig_port->tc_port_name, + tc_port_mode_name(dig_port->tc_mode)); + + mutex_unlock(&dig_port->tc_lock); } -void intel_tc_port_sanitize(struct intel_digital_port *dig_port) +/** + * intel_tc_port_sanitize_mode: Sanitize the given port's TypeC mode + * @dig_port: digital port + * + * Sanitize @dig_port's TypeC mode wrt. the encoder's state right after driver + * loading and system resume: + * If the encoder is enabled keep the TypeC mode/PHY connected state locked until + * the encoder is disabled. + * If the encoder is disabled make sure the PHY is disconnected. + */ +void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_encoder *encoder = &dig_port->base; - intel_wakeref_t tc_cold_wref; - enum intel_display_power_domain domain; int active_links = 0; mutex_lock(&dig_port->tc_lock); @@ -708,21 +748,14 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port) else if (encoder->base.crtc) active_links = to_intel_crtc(encoder->base.crtc)->active; - drm_WARN_ON(&i915->drm, dig_port->tc_mode != TC_PORT_DISCONNECTED); - drm_WARN_ON(&i915->drm, dig_port->tc_lock_wakeref); - - tc_cold_wref = tc_cold_block(dig_port, &domain); + drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1); + intel_tc_port_link_init_refcount(dig_port, active_links); - dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port); if (active_links) { if (!icl_tc_phy_is_connected(dig_port)) drm_dbg_kms(&i915->drm, "Port %s: PHY disconnected with %d active link(s)\n", dig_port->tc_port_name, active_links); - intel_tc_port_link_init_refcount(dig_port, active_links); - - dig_port->tc_lock_wakeref = tc_cold_block(dig_port, - &dig_port->tc_lock_power_domain); } else { /* * TBT-alt is the default mode in any case the PHY ownership is not @@ -736,9 +769,10 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port) dig_port->tc_port_name, tc_port_mode_name(dig_port->tc_mode)); icl_tc_phy_disconnect(dig_port); - } - tc_cold_unblock(dig_port, domain, tc_cold_wref); + tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain, + fetch_and_zero(&dig_port->tc_lock_wakeref)); + } drm_dbg_kms(&i915->drm, "Port %s: sanitize mode (%s)\n", dig_port->tc_port_name, @@ -923,4 +957,6 @@ void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) dig_port->tc_mode = TC_PORT_DISCONNECTED; dig_port->tc_link_refcount = 0; tc_port_load_fia_params(i915, dig_port); + + intel_tc_port_init_mode(dig_port); } diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h index 6b47b29f551c..d54082e2d5e8 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.h +++ b/drivers/gpu/drm/i915/display/intel_tc.h @@ -24,7 +24,8 @@ int intel_tc_port_fia_max_lane_count(struct intel_digital_port *dig_port); void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port, int required_lanes); -void intel_tc_port_sanitize(struct intel_digital_port *dig_port); +void intel_tc_port_init_mode(struct intel_digital_port *dig_port); +void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port); void intel_tc_port_lock(struct intel_digital_port *dig_port); void intel_tc_port_unlock(struct intel_digital_port *dig_port); void intel_tc_port_flush_work(struct intel_digital_port *dig_port); -- cgit 1.4.1 From 99025116f5c685d5af32ffd8552c47360d3adcb0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:12 +0200 Subject: drm/i915/tc: Fix TC port link ref init for DP MST during HW readout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 67165722c27cc46de112a4e10b450170c8980a6f upstream. An enabled TC MST port holds one TC port link reference, regardless of the number of enabled streams on it, but the TC port HW readout takes one reference for each active MST stream. Fix the HW readout, taking only one reference for MST ports. This didn't cause an actual problem, since the encoder HW readout doesn't yet support reading out the MST HW state. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Reviewed-by: Mika Kahola Reviewed-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-3-imre.deak@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_tc.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index eecdf5abb41c..f50c92ca0dff 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -683,11 +683,14 @@ static void intel_tc_port_update_mode(struct intel_digital_port *dig_port, tc_cold_unblock(dig_port, domain, wref); } -static void -intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port, - int refcount) +static void __intel_tc_port_get_link(struct intel_digital_port *dig_port) { - dig_port->tc_link_refcount = refcount; + dig_port->tc_link_refcount++; +} + +static void __intel_tc_port_put_link(struct intel_digital_port *dig_port) +{ + dig_port->tc_link_refcount--; } /** @@ -713,7 +716,7 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port); /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */ - intel_tc_port_link_init_refcount(dig_port, 1); + __intel_tc_port_get_link(dig_port); dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); tc_cold_unblock(dig_port, domain, tc_cold_wref); @@ -749,8 +752,6 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) active_links = to_intel_crtc(encoder->base.crtc)->active; drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1); - intel_tc_port_link_init_refcount(dig_port, active_links); - if (active_links) { if (!icl_tc_phy_is_connected(dig_port)) drm_dbg_kms(&i915->drm, @@ -769,6 +770,7 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) dig_port->tc_port_name, tc_port_mode_name(dig_port->tc_mode)); icl_tc_phy_disconnect(dig_port); + __intel_tc_port_put_link(dig_port); tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain, fetch_and_zero(&dig_port->tc_lock_wakeref)); @@ -880,14 +882,14 @@ void intel_tc_port_get_link(struct intel_digital_port *dig_port, int required_lanes) { __intel_tc_port_lock(dig_port, required_lanes); - dig_port->tc_link_refcount++; + __intel_tc_port_get_link(dig_port); intel_tc_port_unlock(dig_port); } void intel_tc_port_put_link(struct intel_digital_port *dig_port) { intel_tc_port_lock(dig_port); - --dig_port->tc_link_refcount; + __intel_tc_port_put_link(dig_port); intel_tc_port_unlock(dig_port); /* -- cgit 1.4.1 From 1bdcffaa0d2c1dd0510d1b364c409e1c8fc96aa3 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:14 +0200 Subject: drm/i915/tc: Fix system resume MST mode restore for DP-alt sinks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 06f66261a1567d66b9d35c87393b6edfbea4c8f8 upstream. At least restoring the MST topology during system resume needs to use AUX before the display HW readout->sanitization sequence is complete, but on TC ports the PHY may be in the wrong mode for this, resulting in the AUX transfers to fail. The initial TC port mode is kept fixed as BIOS left it for the above HW readout sequence (to prevent changing the mode on an enabled port). If the port is disabled this initial mode is TBT - as in any case the PHY ownership is not held - even if a DP-alt sink is connected. Thus, the AUX transfers during this time will use TBT mode instead of the expected DP-alt mode and so time out. Fix the above by connecting the PHY during port initialization if the port is disabled, which will switch to the expected mode (DP-alt in the above case). As the encoder/pipe HW state isn't read-out yet at this point, check if the port is enabled based on the DDI_BUF enabled flag. Save the read-out initial mode, so intel_tc_port_sanitize_mode() can check this wrt. the read-out encoder HW state. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-5-imre.deak@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_tc.c | 49 ++++++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 63b7105e818a..a8bf91a21cb2 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1763,6 +1763,7 @@ struct intel_digital_port { bool tc_legacy_port:1; char tc_port_name[8]; enum tc_port_mode tc_mode; + enum tc_port_mode tc_init_mode; enum phy_fia tc_phy_fia; u8 tc_phy_fia_idx; diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index f50c92ca0dff..bda77828dc95 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -5,6 +5,7 @@ #include "i915_drv.h" #include "i915_reg.h" +#include "intel_de.h" #include "intel_display.h" #include "intel_display_power_map.h" #include "intel_display_types.h" @@ -116,6 +117,24 @@ assert_tc_cold_blocked(struct intel_digital_port *dig_port) drm_WARN_ON(&i915->drm, !enabled); } +static enum intel_display_power_domain +tc_port_power_domain(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); + + return POWER_DOMAIN_PORT_DDI_LANES_TC1 + tc_port - TC_PORT_1; +} + +static void +assert_tc_port_power_enabled(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + drm_WARN_ON(&i915->drm, + !intel_display_power_is_enabled(i915, tc_port_power_domain(dig_port))); +} + u32 intel_tc_port_get_lane_mask(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -693,6 +712,16 @@ static void __intel_tc_port_put_link(struct intel_digital_port *dig_port) dig_port->tc_link_refcount--; } +static bool tc_port_is_enabled(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + assert_tc_port_power_enabled(dig_port); + + return intel_de_read(i915, DDI_BUF_CTL(dig_port->base.port)) & + DDI_BUF_CTL_ENABLE; +} + /** * intel_tc_port_init_mode: Read out HW state and init the given port's TypeC mode * @dig_port: digital port @@ -715,9 +744,23 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) tc_cold_wref = tc_cold_block(dig_port, &domain); dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port); + /* + * Save the initial mode for the state check in + * intel_tc_port_sanitize_mode(). + */ + dig_port->tc_init_mode = dig_port->tc_mode; + dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); + + /* + * The PHY needs to be connected for AUX to work during HW readout and + * MST topology resume, but the PHY mode can only be changed if the + * port is disabled. + */ + if (!tc_port_is_enabled(dig_port)) + intel_tc_port_update_mode(dig_port, 1, false); + /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */ __intel_tc_port_get_link(dig_port); - dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); tc_cold_unblock(dig_port, domain, tc_cold_wref); @@ -764,11 +807,11 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) * we'll just switch to disconnected mode from it here without * a note. */ - if (dig_port->tc_mode != TC_PORT_TBT_ALT) + if (dig_port->tc_init_mode != TC_PORT_TBT_ALT) drm_dbg_kms(&i915->drm, "Port %s: PHY left in %s mode on disabled port, disconnecting it\n", dig_port->tc_port_name, - tc_port_mode_name(dig_port->tc_mode)); + tc_port_mode_name(dig_port->tc_init_mode)); icl_tc_phy_disconnect(dig_port); __intel_tc_port_put_link(dig_port); -- cgit 1.4.1 From ad2928e7f3f6120a0bd18aa1056b3b24068027c5 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Wed, 16 Nov 2022 13:49:32 +0100 Subject: mtd: parsers: refer to ARCH_BCMBCA instead of ARCH_BCM4908 commit 085679b15b5af65f9610f619afde41da0f966194 upstream. Commit dd5c672d7ca9 ("arm64: bcmbca: Merge ARCH_BCM4908 to ARCH_BCMBCA") removes config ARCH_BCM4908 as config ARCH_BCMBCA has the same intent. Probably due to concurrent development, commit 002181f5b150 ("mtd: parsers: add Broadcom's U-Boot parser") introduces 'Broadcom's U-Boot partition parser' that depends on ARCH_BCM4908, but this use was not visible during the config refactoring from the commit above. Hence, these two changes create a reference to a non-existing config symbol. Adjust the MTD_BRCM_U_BOOT definition to refer to ARCH_BCMBCA instead of ARCH_BCM4908 to remove the reference to the non-existing config symbol ARCH_BCM4908. Signed-off-by: Lukas Bulwahn Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221116124932.4748-1-lukas.bulwahn@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/parsers/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index aaa06050c9bc..c8f628a0689d 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -22,7 +22,7 @@ config MTD_BCM63XX_PARTS config MTD_BRCM_U_BOOT tristate "Broadcom's U-Boot partition parser" - depends on ARCH_BCM4908 || COMPILE_TEST + depends on ARCH_BCMBCA || COMPILE_TEST help Broadcom uses a custom way of storing U-Boot environment variables. They are placed inside U-Boot partition itself at unspecified offset. -- cgit 1.4.1 From f145373334054a8423fac198e0dcfeeb49b96ec8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 26 Jun 2023 00:42:18 +0200 Subject: netfilter: nf_tables: unbind non-anonymous set if rule construction fails commit 3e70489721b6c870252c9082c496703677240f53 upstream. Otherwise a dangling reference to a rule object that is gone remains in the set binding list. Fixes: 26b5a5712eb8 ("netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_tables_api.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 7f71bdbc8267..5e534c9fe947 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -5139,6 +5139,8 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set, nft_set_trans_unbind(ctx, set); if (nft_set_is_anonymous(set)) nft_deactivate_next(ctx->net, set); + else + list_del_rcu(&binding->list); set->use--; break; -- cgit 1.4.1 From 05561f822f27b9fa88fa5504ddec34bf38833034 Mon Sep 17 00:00:00 2001 From: Florent Revest Date: Mon, 3 Jul 2023 16:52:16 +0200 Subject: netfilter: conntrack: Avoid nf_ct_helper_hash uses after free commit 6eef7a2b933885a17679eb8ed0796ddf0ee5309b upstream. If nf_conntrack_init_start() fails (for example due to a register_nf_conntrack_bpf() failure), the nf_conntrack_helper_fini() clean-up path frees the nf_ct_helper_hash map. When built with NF_CONNTRACK=y, further netfilter modules (e.g: netfilter_conntrack_ftp) can still be loaded and call nf_conntrack_helpers_register(), independently of whether nf_conntrack initialized correctly. This accesses the nf_ct_helper_hash dangling pointer and causes a uaf, possibly leading to random memory corruption. This patch guards nf_conntrack_helper_register() from accessing a freed or uninitialized nf_ct_helper_hash pointer and fixes possible uses-after-free when loading a conntrack module. Cc: stable@vger.kernel.org Fixes: 12f7a505331e ("netfilter: add user-space connection tracking helper infrastructure") Signed-off-by: Florent Revest Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index ff737a76052e..bf09a1e06248 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -358,6 +358,9 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); + if (!nf_ct_helper_hash) + return -ENOENT; + if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) return -EINVAL; @@ -513,4 +516,5 @@ int nf_conntrack_helper_init(void) void nf_conntrack_helper_fini(void) { kvfree(nf_ct_helper_hash); + nf_ct_helper_hash = NULL; } -- cgit 1.4.1 From fc95c8b02c6160936f1f3d8d9d7f4f66f3c84b49 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 5 Jul 2023 09:12:55 -0300 Subject: netfilter: nf_tables: do not ignore genmask when looking up chain by id commit 515ad530795c118f012539ed76d02bacfd426d89 upstream. When adding a rule to a chain referring to its ID, if that chain had been deleted on the same batch, the rule might end up referring to a deleted chain. This will lead to a WARNING like following: [ 33.098431] ------------[ cut here ]------------ [ 33.098678] WARNING: CPU: 5 PID: 69 at net/netfilter/nf_tables_api.c:2037 nf_tables_chain_destroy+0x23d/0x260 [ 33.099217] Modules linked in: [ 33.099388] CPU: 5 PID: 69 Comm: kworker/5:1 Not tainted 6.4.0+ #409 [ 33.099726] Workqueue: events nf_tables_trans_destroy_work [ 33.100018] RIP: 0010:nf_tables_chain_destroy+0x23d/0x260 [ 33.100306] Code: 8b 7c 24 68 e8 64 9c ed fe 4c 89 e7 e8 5c 9c ed fe 48 83 c4 08 5b 41 5c 41 5d 41 5e 41 5f 5d 31 c0 89 c6 89 c7 c3 cc cc cc cc <0f> 0b 48 83 c4 08 5b 41 5c 41 5d 41 5e 41 5f 5d 31 c0 89 c6 89 c7 [ 33.101271] RSP: 0018:ffffc900004ffc48 EFLAGS: 00010202 [ 33.101546] RAX: 0000000000000001 RBX: ffff888006fc0a28 RCX: 0000000000000000 [ 33.101920] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 33.102649] RBP: ffffc900004ffc78 R08: 0000000000000000 R09: 0000000000000000 [ 33.103018] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8880135ef500 [ 33.103385] R13: 0000000000000000 R14: dead000000000122 R15: ffff888006fc0a10 [ 33.103762] FS: 0000000000000000(0000) GS:ffff888024c80000(0000) knlGS:0000000000000000 [ 33.104184] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 33.104493] CR2: 00007fe863b56a50 CR3: 00000000124b0001 CR4: 0000000000770ee0 [ 33.104872] PKRU: 55555554 [ 33.104999] Call Trace: [ 33.105113] [ 33.105214] ? show_regs+0x72/0x90 [ 33.105371] ? __warn+0xa5/0x210 [ 33.105520] ? nf_tables_chain_destroy+0x23d/0x260 [ 33.105732] ? report_bug+0x1f2/0x200 [ 33.105902] ? handle_bug+0x46/0x90 [ 33.106546] ? exc_invalid_op+0x19/0x50 [ 33.106762] ? asm_exc_invalid_op+0x1b/0x20 [ 33.106995] ? nf_tables_chain_destroy+0x23d/0x260 [ 33.107249] ? nf_tables_chain_destroy+0x30/0x260 [ 33.107506] nf_tables_trans_destroy_work+0x669/0x680 [ 33.107782] ? mark_held_locks+0x28/0xa0 [ 33.107996] ? __pfx_nf_tables_trans_destroy_work+0x10/0x10 [ 33.108294] ? _raw_spin_unlock_irq+0x28/0x70 [ 33.108538] process_one_work+0x68c/0xb70 [ 33.108755] ? lock_acquire+0x17f/0x420 [ 33.108977] ? __pfx_process_one_work+0x10/0x10 [ 33.109218] ? do_raw_spin_lock+0x128/0x1d0 [ 33.109435] ? _raw_spin_lock_irq+0x71/0x80 [ 33.109634] worker_thread+0x2bd/0x700 [ 33.109817] ? __pfx_worker_thread+0x10/0x10 [ 33.110254] kthread+0x18b/0x1d0 [ 33.110410] ? __pfx_kthread+0x10/0x10 [ 33.110581] ret_from_fork+0x29/0x50 [ 33.110757] [ 33.110866] irq event stamp: 1651 [ 33.111017] hardirqs last enabled at (1659): [] __up_console_sem+0x79/0xa0 [ 33.111379] hardirqs last disabled at (1666): [] __up_console_sem+0x5e/0xa0 [ 33.111740] softirqs last enabled at (1616): [] __irq_exit_rcu+0x9e/0xe0 [ 33.112094] softirqs last disabled at (1367): [] __irq_exit_rcu+0x9e/0xe0 [ 33.112453] ---[ end trace 0000000000000000 ]--- This is due to the nft_chain_lookup_byid ignoring the genmask. After this change, adding the new rule will fail as it will not find the chain. Fixes: 837830a4b439 ("netfilter: nf_tables: add NFTA_RULE_CHAIN_ID attribute") Cc: stable@vger.kernel.org Reported-by: Mingi Cho of Theori working with ZDI Signed-off-by: Thadeu Lima de Souza Cascardo Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_tables_api.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 5e534c9fe947..938cfa9a3adb 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2628,7 +2628,7 @@ err: static struct nft_chain *nft_chain_lookup_byid(const struct net *net, const struct nft_table *table, - const struct nlattr *nla) + const struct nlattr *nla, u8 genmask) { struct nftables_pernet *nft_net = nft_pernet(net); u32 id = ntohl(nla_get_be32(nla)); @@ -2639,7 +2639,8 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net, if (trans->msg_type == NFT_MSG_NEWCHAIN && chain->table == table && - id == nft_trans_chain_id(trans)) + id == nft_trans_chain_id(trans) && + nft_active_genmask(chain, genmask)) return chain; } return ERR_PTR(-ENOENT); @@ -3629,7 +3630,8 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, return -EOPNOTSUPP; } else if (nla[NFTA_RULE_CHAIN_ID]) { - chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]); + chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID], + genmask); if (IS_ERR(chain)) { NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]); return PTR_ERR(chain); @@ -10137,7 +10139,8 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, genmask); } else if (tb[NFTA_VERDICT_CHAIN_ID]) { chain = nft_chain_lookup_byid(ctx->net, ctx->table, - tb[NFTA_VERDICT_CHAIN_ID]); + tb[NFTA_VERDICT_CHAIN_ID], + genmask); if (IS_ERR(chain)) return PTR_ERR(chain); } else { -- cgit 1.4.1 From 40f83dd66a823400d8592e3b71e190e3ad978eb5 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 5 Jul 2023 18:05:35 -0300 Subject: netfilter: nf_tables: prevent OOB access in nft_byteorder_eval commit caf3ef7468f7534771b5c44cd8dbd6f7f87c2cbd upstream. When evaluating byteorder expressions with size 2, a union with 32-bit and 16-bit members is used. Since the 16-bit members are aligned to 32-bit, the array accesses will be out-of-bounds. It may lead to a stack-out-of-bounds access like the one below: [ 23.095215] ================================================================== [ 23.095625] BUG: KASAN: stack-out-of-bounds in nft_byteorder_eval+0x13c/0x320 [ 23.096020] Read of size 2 at addr ffffc90000007948 by task ping/115 [ 23.096358] [ 23.096456] CPU: 0 PID: 115 Comm: ping Not tainted 6.4.0+ #413 [ 23.096770] Call Trace: [ 23.096910] [ 23.097030] dump_stack_lvl+0x60/0xc0 [ 23.097218] print_report+0xcf/0x630 [ 23.097388] ? nft_byteorder_eval+0x13c/0x320 [ 23.097577] ? kasan_addr_to_slab+0xd/0xc0 [ 23.097760] ? nft_byteorder_eval+0x13c/0x320 [ 23.097949] kasan_report+0xc9/0x110 [ 23.098106] ? nft_byteorder_eval+0x13c/0x320 [ 23.098298] __asan_load2+0x83/0xd0 [ 23.098453] nft_byteorder_eval+0x13c/0x320 [ 23.098659] nft_do_chain+0x1c8/0xc50 [ 23.098852] ? __pfx_nft_do_chain+0x10/0x10 [ 23.099078] ? __kasan_check_read+0x11/0x20 [ 23.099295] ? __pfx___lock_acquire+0x10/0x10 [ 23.099535] ? __pfx___lock_acquire+0x10/0x10 [ 23.099745] ? __kasan_check_read+0x11/0x20 [ 23.099929] nft_do_chain_ipv4+0xfe/0x140 [ 23.100105] ? __pfx_nft_do_chain_ipv4+0x10/0x10 [ 23.100327] ? lock_release+0x204/0x400 [ 23.100515] ? nf_hook.constprop.0+0x340/0x550 [ 23.100779] nf_hook_slow+0x6c/0x100 [ 23.100977] ? __pfx_nft_do_chain_ipv4+0x10/0x10 [ 23.101223] nf_hook.constprop.0+0x334/0x550 [ 23.101443] ? __pfx_ip_local_deliver_finish+0x10/0x10 [ 23.101677] ? __pfx_nf_hook.constprop.0+0x10/0x10 [ 23.101882] ? __pfx_ip_rcv_finish+0x10/0x10 [ 23.102071] ? __pfx_ip_local_deliver_finish+0x10/0x10 [ 23.102291] ? rcu_read_lock_held+0x4b/0x70 [ 23.102481] ip_local_deliver+0xbb/0x110 [ 23.102665] ? __pfx_ip_rcv+0x10/0x10 [ 23.102839] ip_rcv+0x199/0x2a0 [ 23.102980] ? __pfx_ip_rcv+0x10/0x10 [ 23.103140] __netif_receive_skb_one_core+0x13e/0x150 [ 23.103362] ? __pfx___netif_receive_skb_one_core+0x10/0x10 [ 23.103647] ? mark_held_locks+0x48/0xa0 [ 23.103819] ? process_backlog+0x36c/0x380 [ 23.103999] __netif_receive_skb+0x23/0xc0 [ 23.104179] process_backlog+0x91/0x380 [ 23.104350] __napi_poll.constprop.0+0x66/0x360 [ 23.104589] ? net_rx_action+0x1cb/0x610 [ 23.104811] net_rx_action+0x33e/0x610 [ 23.105024] ? _raw_spin_unlock+0x23/0x50 [ 23.105257] ? __pfx_net_rx_action+0x10/0x10 [ 23.105485] ? mark_held_locks+0x48/0xa0 [ 23.105741] __do_softirq+0xfa/0x5ab [ 23.105956] ? __dev_queue_xmit+0x765/0x1c00 [ 23.106193] do_softirq.part.0+0x49/0xc0 [ 23.106423] [ 23.106547] [ 23.106670] __local_bh_enable_ip+0xf5/0x120 [ 23.106903] __dev_queue_xmit+0x789/0x1c00 [ 23.107131] ? __pfx___dev_queue_xmit+0x10/0x10 [ 23.107381] ? find_held_lock+0x8e/0xb0 [ 23.107585] ? lock_release+0x204/0x400 [ 23.107798] ? neigh_resolve_output+0x185/0x350 [ 23.108049] ? mark_held_locks+0x48/0xa0 [ 23.108265] ? neigh_resolve_output+0x185/0x350 [ 23.108514] neigh_resolve_output+0x246/0x350 [ 23.108753] ? neigh_resolve_output+0x246/0x350 [ 23.109003] ip_finish_output2+0x3c3/0x10b0 [ 23.109250] ? __pfx_ip_finish_output2+0x10/0x10 [ 23.109510] ? __pfx_nf_hook+0x10/0x10 [ 23.109732] __ip_finish_output+0x217/0x390 [ 23.109978] ip_finish_output+0x2f/0x130 [ 23.110207] ip_output+0xc9/0x170 [ 23.110404] ip_push_pending_frames+0x1a0/0x240 [ 23.110652] raw_sendmsg+0x102e/0x19e0 [ 23.110871] ? __pfx_raw_sendmsg+0x10/0x10 [ 23.111093] ? lock_release+0x204/0x400 [ 23.111304] ? __mod_lruvec_page_state+0x148/0x330 [ 23.111567] ? find_held_lock+0x8e/0xb0 [ 23.111777] ? find_held_lock+0x8e/0xb0 [ 23.111993] ? __rcu_read_unlock+0x7c/0x2f0 [ 23.112225] ? aa_sk_perm+0x18a/0x550 [ 23.112431] ? filemap_map_pages+0x4f1/0x900 [ 23.112665] ? __pfx_aa_sk_perm+0x10/0x10 [ 23.112880] ? find_held_lock+0x8e/0xb0 [ 23.113098] inet_sendmsg+0xa0/0xb0 [ 23.113297] ? inet_sendmsg+0xa0/0xb0 [ 23.113500] ? __pfx_inet_sendmsg+0x10/0x10 [ 23.113727] sock_sendmsg+0xf4/0x100 [ 23.113924] ? move_addr_to_kernel.part.0+0x4f/0xa0 [ 23.114190] __sys_sendto+0x1d4/0x290 [ 23.114391] ? __pfx___sys_sendto+0x10/0x10 [ 23.114621] ? __pfx_mark_lock.part.0+0x10/0x10 [ 23.114869] ? lock_release+0x204/0x400 [ 23.115076] ? find_held_lock+0x8e/0xb0 [ 23.115287] ? rcu_is_watching+0x23/0x60 [ 23.115503] ? __rseq_handle_notify_resume+0x6e2/0x860 [ 23.115778] ? __kasan_check_write+0x14/0x30 [ 23.116008] ? blkcg_maybe_throttle_current+0x8d/0x770 [ 23.116285] ? mark_held_locks+0x28/0xa0 [ 23.116503] ? do_syscall_64+0x37/0x90 [ 23.116713] __x64_sys_sendto+0x7f/0xb0 [ 23.116924] do_syscall_64+0x59/0x90 [ 23.117123] ? irqentry_exit_to_user_mode+0x25/0x30 [ 23.117387] ? irqentry_exit+0x77/0xb0 [ 23.117593] ? exc_page_fault+0x92/0x140 [ 23.117806] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 [ 23.118081] RIP: 0033:0x7f744aee2bba [ 23.118282] Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 15 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 7e c3 0f 1f 44 00 00 41 54 48 83 ec 30 44 89 [ 23.119237] RSP: 002b:00007ffd04a7c9f8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c [ 23.119644] RAX: ffffffffffffffda RBX: 00007ffd04a7e0a0 RCX: 00007f744aee2bba [ 23.120023] RDX: 0000000000000040 RSI: 000056488e9e6300 RDI: 0000000000000003 [ 23.120413] RBP: 000056488e9e6300 R08: 00007ffd04a80320 R09: 0000000000000010 [ 23.120809] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000040 [ 23.121219] R13: 00007ffd04a7dc38 R14: 00007ffd04a7ca00 R15: 00007ffd04a7e0a0 [ 23.121617] [ 23.121749] [ 23.121845] The buggy address belongs to the virtual mapping at [ 23.121845] [ffffc90000000000, ffffc90000009000) created by: [ 23.121845] irq_init_percpu_irqstack+0x1cf/0x270 [ 23.122707] [ 23.122803] The buggy address belongs to the physical page: [ 23.123104] page:0000000072ac19f0 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x24a09 [ 23.123609] flags: 0xfffffc0001000(reserved|node=0|zone=1|lastcpupid=0x1fffff) [ 23.123998] page_type: 0xffffffff() [ 23.124194] raw: 000fffffc0001000 ffffea0000928248 ffffea0000928248 0000000000000000 [ 23.124610] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 [ 23.125023] page dumped because: kasan: bad access detected [ 23.125326] [ 23.125421] Memory state around the buggy address: [ 23.125682] ffffc90000007800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 23.126072] ffffc90000007880: 00 00 00 00 00 f1 f1 f1 f1 f1 f1 00 00 f2 f2 00 [ 23.126455] >ffffc90000007900: 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00 00 00 [ 23.126840] ^ [ 23.127138] ffffc90000007980: 00 00 00 00 00 00 00 00 00 00 00 00 00 f3 f3 f3 [ 23.127522] ffffc90000007a00: f3 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 [ 23.127906] ================================================================== [ 23.128324] Disabling lock debugging due to kernel taint Using simple s16 pointers for the 16-bit accesses fixes the problem. For the 32-bit accesses, src and dst can be used directly. Fixes: 96518518cc41 ("netfilter: add nftables") Cc: stable@vger.kernel.org Reported-by: Tanguy DUBROCA (@SidewayRE) from @Synacktiv working with ZDI Signed-off-by: Thadeu Lima de Souza Cascardo Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_byteorder.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index f952a80275a8..2e2eb2cb17bc 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -30,11 +30,11 @@ void nft_byteorder_eval(const struct nft_expr *expr, const struct nft_byteorder *priv = nft_expr_priv(expr); u32 *src = ®s->data[priv->sreg]; u32 *dst = ®s->data[priv->dreg]; - union { u32 u32; u16 u16; } *s, *d; + u16 *s16, *d16; unsigned int i; - s = (void *)src; - d = (void *)dst; + s16 = (void *)src; + d16 = (void *)dst; switch (priv->size) { case 8: { @@ -62,11 +62,11 @@ void nft_byteorder_eval(const struct nft_expr *expr, switch (priv->op) { case NFT_BYTEORDER_NTOH: for (i = 0; i < priv->len / 4; i++) - d[i].u32 = ntohl((__force __be32)s[i].u32); + dst[i] = ntohl((__force __be32)src[i]); break; case NFT_BYTEORDER_HTON: for (i = 0; i < priv->len / 4; i++) - d[i].u32 = (__force __u32)htonl(s[i].u32); + dst[i] = (__force __u32)htonl(src[i]); break; } break; @@ -74,11 +74,11 @@ void nft_byteorder_eval(const struct nft_expr *expr, switch (priv->op) { case NFT_BYTEORDER_NTOH: for (i = 0; i < priv->len / 2; i++) - d[i].u16 = ntohs((__force __be16)s[i].u16); + d16[i] = ntohs((__force __be16)s16[i]); break; case NFT_BYTEORDER_HTON: for (i = 0; i < priv->len / 2; i++) - d[i].u16 = (__force __u16)htons(s[i].u16); + d16[i] = (__force __u16)htons(s16[i]); break; } break; -- cgit 1.4.1 From 561aaadf0d07ce0503a3ec8e684004345be5c93f Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 3 Jul 2023 03:27:04 +0200 Subject: wireguard: queueing: use saner cpu selection wrapping commit 7387943fa35516f6f8017a3b0e9ce48a3bef9faa upstream. Using `% nr_cpumask_bits` is slow and complicated, and not totally robust toward dynamic changes to CPU topologies. Rather than storing the next CPU in the round-robin, just store the last one, and also return that value. This simplifies the loop drastically into a much more common pattern. Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") Cc: stable@vger.kernel.org Reported-by: Linus Torvalds Tested-by: Manuel Leiner Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireguard/queueing.c | 1 + drivers/net/wireguard/queueing.h | 25 +++++++++++-------------- drivers/net/wireguard/receive.c | 2 +- drivers/net/wireguard/send.c | 2 +- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireguard/queueing.c b/drivers/net/wireguard/queueing.c index 8084e7408c0a..26d235d15235 100644 --- a/drivers/net/wireguard/queueing.c +++ b/drivers/net/wireguard/queueing.c @@ -28,6 +28,7 @@ int wg_packet_queue_init(struct crypt_queue *queue, work_func_t function, int ret; memset(queue, 0, sizeof(*queue)); + queue->last_cpu = -1; ret = ptr_ring_init(&queue->ring, len, GFP_KERNEL); if (ret) return ret; diff --git a/drivers/net/wireguard/queueing.h b/drivers/net/wireguard/queueing.h index 583adb37ee1e..1d4f9196bfe1 100644 --- a/drivers/net/wireguard/queueing.h +++ b/drivers/net/wireguard/queueing.h @@ -117,20 +117,17 @@ static inline int wg_cpumask_choose_online(int *stored_cpu, unsigned int id) return cpu; } -/* This function is racy, in the sense that next is unlocked, so it could return - * the same CPU twice. A race-free version of this would be to instead store an - * atomic sequence number, do an increment-and-return, and then iterate through - * every possible CPU until we get to that index -- choose_cpu. However that's - * a bit slower, and it doesn't seem like this potential race actually - * introduces any performance loss, so we live with it. +/* This function is racy, in the sense that it's called while last_cpu is + * unlocked, so it could return the same CPU twice. Adding locking or using + * atomic sequence numbers is slower though, and the consequences of racing are + * harmless, so live with it. */ -static inline int wg_cpumask_next_online(int *next) +static inline int wg_cpumask_next_online(int *last_cpu) { - int cpu = *next; - - while (unlikely(!cpumask_test_cpu(cpu, cpu_online_mask))) - cpu = cpumask_next(cpu, cpu_online_mask) % nr_cpumask_bits; - *next = cpumask_next(cpu, cpu_online_mask) % nr_cpumask_bits; + int cpu = cpumask_next(*last_cpu, cpu_online_mask); + if (cpu >= nr_cpu_ids) + cpu = cpumask_first(cpu_online_mask); + *last_cpu = cpu; return cpu; } @@ -159,7 +156,7 @@ static inline void wg_prev_queue_drop_peeked(struct prev_queue *queue) static inline int wg_queue_enqueue_per_device_and_peer( struct crypt_queue *device_queue, struct prev_queue *peer_queue, - struct sk_buff *skb, struct workqueue_struct *wq, int *next_cpu) + struct sk_buff *skb, struct workqueue_struct *wq) { int cpu; @@ -173,7 +170,7 @@ static inline int wg_queue_enqueue_per_device_and_peer( /* Then we queue it up in the device queue, which consumes the * packet as soon as it can. */ - cpu = wg_cpumask_next_online(next_cpu); + cpu = wg_cpumask_next_online(&device_queue->last_cpu); if (unlikely(ptr_ring_produce_bh(&device_queue->ring, skb))) return -EPIPE; queue_work_on(cpu, wq, &per_cpu_ptr(device_queue->worker, cpu)->work); diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c index 7135d51d2d87..0b3f0c843550 100644 --- a/drivers/net/wireguard/receive.c +++ b/drivers/net/wireguard/receive.c @@ -524,7 +524,7 @@ static void wg_packet_consume_data(struct wg_device *wg, struct sk_buff *skb) goto err; ret = wg_queue_enqueue_per_device_and_peer(&wg->decrypt_queue, &peer->rx_queue, skb, - wg->packet_crypt_wq, &wg->decrypt_queue.last_cpu); + wg->packet_crypt_wq); if (unlikely(ret == -EPIPE)) wg_queue_enqueue_per_peer_rx(skb, PACKET_STATE_DEAD); if (likely(!ret || ret == -EPIPE)) { diff --git a/drivers/net/wireguard/send.c b/drivers/net/wireguard/send.c index 5368f7c35b4b..95c853b59e1d 100644 --- a/drivers/net/wireguard/send.c +++ b/drivers/net/wireguard/send.c @@ -318,7 +318,7 @@ static void wg_packet_create_data(struct wg_peer *peer, struct sk_buff *first) goto err; ret = wg_queue_enqueue_per_device_and_peer(&wg->encrypt_queue, &peer->tx_queue, first, - wg->packet_crypt_wq, &wg->encrypt_queue.last_cpu); + wg->packet_crypt_wq); if (unlikely(ret == -EPIPE)) wg_queue_enqueue_per_peer_tx(first, PACKET_STATE_DEAD); err: -- cgit 1.4.1 From 3173bfdf89ac59923349ced182af014f1abd34cb Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 3 Jul 2023 03:27:05 +0200 Subject: wireguard: netlink: send staged packets when setting initial private key commit f58d0a9b4c6a7a5199c3af967e43cc8b654604d4 upstream. Packets bound for peers can queue up prior to the device private key being set. For example, if persistent keepalive is set, a packet is queued up to be sent as soon as the device comes up. However, if the private key hasn't been set yet, the handshake message never sends, and no timer is armed to retry, since that would be pointless. But, if a user later sets a private key, the expectation is that those queued packets, such as a persistent keepalive, are actually sent. So adjust the configuration logic to account for this edge case, and add a test case to make sure this works. Maxim noticed this with a wg-quick(8) config to the tune of: [Interface] PostUp = wg set %i private-key somefile [Peer] PublicKey = ... Endpoint = ... PersistentKeepalive = 25 Here, the private key gets set after the device comes up using a PostUp script, triggering the bug. Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") Cc: stable@vger.kernel.org Reported-by: Maxim Cournoyer Tested-by: Maxim Cournoyer Link: https://lore.kernel.org/wireguard/87fs7xtqrv.fsf@gmail.com/ Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireguard/netlink.c | 14 +++++++++----- tools/testing/selftests/wireguard/netns.sh | 30 ++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c index 43c8c84e7ea8..6d1bd9f52d02 100644 --- a/drivers/net/wireguard/netlink.c +++ b/drivers/net/wireguard/netlink.c @@ -546,6 +546,7 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info) u8 *private_key = nla_data(info->attrs[WGDEVICE_A_PRIVATE_KEY]); u8 public_key[NOISE_PUBLIC_KEY_LEN]; struct wg_peer *peer, *temp; + bool send_staged_packets; if (!crypto_memneq(wg->static_identity.static_private, private_key, NOISE_PUBLIC_KEY_LEN)) @@ -564,14 +565,17 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info) } down_write(&wg->static_identity.lock); - wg_noise_set_static_identity_private_key(&wg->static_identity, - private_key); - list_for_each_entry_safe(peer, temp, &wg->peer_list, - peer_list) { + send_staged_packets = !wg->static_identity.has_identity && netif_running(wg->dev); + wg_noise_set_static_identity_private_key(&wg->static_identity, private_key); + send_staged_packets = send_staged_packets && wg->static_identity.has_identity; + + wg_cookie_checker_precompute_device_keys(&wg->cookie_checker); + list_for_each_entry_safe(peer, temp, &wg->peer_list, peer_list) { wg_noise_precompute_static_static(peer); wg_noise_expire_current_peer_keypairs(peer); + if (send_staged_packets) + wg_packet_send_staged_packets(peer); } - wg_cookie_checker_precompute_device_keys(&wg->cookie_checker); up_write(&wg->static_identity.lock); } skip_set_private_key: diff --git a/tools/testing/selftests/wireguard/netns.sh b/tools/testing/selftests/wireguard/netns.sh index 69c7796c7ca9..405ff262ca93 100755 --- a/tools/testing/selftests/wireguard/netns.sh +++ b/tools/testing/selftests/wireguard/netns.sh @@ -514,10 +514,32 @@ n2 bash -c 'printf 0 > /proc/sys/net/ipv4/conf/all/rp_filter' n1 ping -W 1 -c 1 192.168.241.2 [[ $(n2 wg show wg0 endpoints) == "$pub1 10.0.0.3:1" ]] -ip1 link del veth1 -ip1 link del veth3 -ip1 link del wg0 -ip2 link del wg0 +ip1 link del dev veth3 +ip1 link del dev wg0 +ip2 link del dev wg0 + +# Make sure persistent keep alives are sent when an adapter comes up +ip1 link add dev wg0 type wireguard +n1 wg set wg0 private-key <(echo "$key1") peer "$pub2" endpoint 10.0.0.1:1 persistent-keepalive 1 +read _ _ tx_bytes < <(n1 wg show wg0 transfer) +[[ $tx_bytes -eq 0 ]] +ip1 link set dev wg0 up +read _ _ tx_bytes < <(n1 wg show wg0 transfer) +[[ $tx_bytes -gt 0 ]] +ip1 link del dev wg0 +# This should also happen even if the private key is set later +ip1 link add dev wg0 type wireguard +n1 wg set wg0 peer "$pub2" endpoint 10.0.0.1:1 persistent-keepalive 1 +read _ _ tx_bytes < <(n1 wg show wg0 transfer) +[[ $tx_bytes -eq 0 ]] +ip1 link set dev wg0 up +read _ _ tx_bytes < <(n1 wg show wg0 transfer) +[[ $tx_bytes -eq 0 ]] +n1 wg set wg0 private-key <(echo "$key1") +read _ _ tx_bytes < <(n1 wg show wg0 transfer) +[[ $tx_bytes -gt 0 ]] +ip1 link del dev veth1 +ip1 link del dev wg0 # We test that Netlink/IPC is working properly by doing things that usually cause split responses ip0 link add dev wg0 type wireguard -- cgit 1.4.1 From da012a025f70ba56ddca41b6799fd5a0cf492d19 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Mon, 19 Jun 2023 16:06:13 +0800 Subject: tty: serial: fsl_lpuart: add earlycon for imx8ulp platform commit e0edfdc15863ec80a1d9ac6e174dbccc00206dd0 upstream. Add earlycon support for imx8ulp platform. Signed-off-by: Sherry Sun Cc: stable Link: https://lore.kernel.org/r/20230619080613.16522-1-sherry.sun@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 1093c74b5284..f6d0ea2c6be4 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2627,6 +2627,7 @@ OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup); OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup); OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1028a-lpuart", ls1028a_early_console_setup); OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup); +OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8ulp-lpuart", lpuart32_imx_early_console_setup); OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8qxp-lpuart", lpuart32_imx_early_console_setup); OF_EARLYCON_DECLARE(lpuart32, "fsl,imxrt1050-lpuart", lpuart32_imx_early_console_setup); EARLYCON_DECLARE(lpuart, lpuart_early_console_setup); -- cgit 1.4.1 From 899cc8f7989dcdcad43ba8a641ed957eaebba3bc Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Wed, 5 Jul 2023 11:38:08 +1200 Subject: block/partition: fix signedness issue for Amiga partitions commit 7eb1e47696aa231b1a567846bbe3a1e1befe1854 upstream. Making 'blk' sector_t (i.e. 64 bit if LBD support is active) fails the 'blk>0' test in the partition block loop if a value of (signed int) -1 is used to mark the end of the partition block list. Explicitly cast 'blk' to signed int to allow use of -1 to terminate the partition block linked list. Fixes: b6f3f28f604b ("block: add overflow checks for Amiga partition support") Reported-by: Christian Zigotzky Link: https://lore.kernel.org/r/024ce4fa-cc6d-50a2-9aae-3701d0ebf668@xenosoft.de Signed-off-by: Michael Schmitz Reviewed-by: Martin Steigerwald Tested-by: Christian Zigotzky Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/partitions/amiga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/partitions/amiga.c b/block/partitions/amiga.c index ed222b9c901b..506921095412 100644 --- a/block/partitions/amiga.c +++ b/block/partitions/amiga.c @@ -90,7 +90,7 @@ int amiga_partition(struct parsed_partitions *state) } blk = be32_to_cpu(rdb->rdb_PartitionList); put_dev_sector(sect); - for (part = 1; blk>0 && part<=16; part++, put_dev_sector(sect)) { + for (part = 1; (s32) blk>0 && part<=16; part++, put_dev_sector(sect)) { /* Read in terms partition table understands */ if (check_mul_overflow(blk, (sector_t) blksize, &blk)) { pr_err("Dev %s: overflow calculating partition block %llu! Skipping partitions %u and beyond\n", -- cgit 1.4.1 From fe7daa313d13085c9323711554bfe173a083a166 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 9 Jul 2023 13:15:49 +0200 Subject: sh: mach-r2d: Handle virq offset in cascaded IRL demux commit ab8aa4f0956d2e0fb8344deadb823ef743581795 upstream. When booting rts7751r2dplus_defconfig on QEMU, the system hangs due to an interrupt storm on IRQ 20. IRQ 20 aka event 0x280 is a cascaded IRL interrupt, which maps to IRQ_VOYAGER, the interrupt used by the Silicon Motion SM501 multimedia companion chip. As rts7751r2d_irq_demux() does not take into account the new virq offset, the interrupt is no longer translated, leading to an unhandled interrupt. Fix this by taking into account the virq offset when translating cascaded IRL interrupts. Fixes: a8ac2961148e8c72 ("sh: Avoid using IRQ0 on SH3 and SH4") Reported-by: Guenter Roeck Closes: https://lore.kernel.org/r/fbfea3ad-d327-4ad5-ac9c-648c7ca3fe1f@roeck-us.net Signed-off-by: Geert Uytterhoeven Reviewed-by: John Paul Adrian Glaubitz Tested-by: John Paul Adrian Glaubitz Tested-by: Guenter Roeck Link: https://lore.kernel.org/r/2c99d5df41c40691f6c407b7b6a040d406bc81ac.1688901306.git.geert+renesas@glider.be Signed-off-by: John Paul Adrian Glaubitz Signed-off-by: Greg Kroah-Hartman --- arch/sh/boards/mach-r2d/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/sh/boards/mach-r2d/irq.c b/arch/sh/boards/mach-r2d/irq.c index e34f81e9ae81..d0a54a9adbce 100644 --- a/arch/sh/boards/mach-r2d/irq.c +++ b/arch/sh/boards/mach-r2d/irq.c @@ -117,10 +117,10 @@ static unsigned char irl2irq[R2D_NR_IRL]; int rts7751r2d_irq_demux(int irq) { - if (irq >= R2D_NR_IRL || irq < 0 || !irl2irq[irq]) + if (irq >= R2D_NR_IRL + 16 || irq < 16 || !irl2irq[irq - 16]) return irq; - return irl2irq[irq]; + return irl2irq[irq - 16]; } /* -- cgit 1.4.1 From 5628b9aa31798387a1340d889d4c6d7a33ecabe1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 9 Jul 2023 15:10:23 +0200 Subject: sh: mach-highlander: Handle virq offset in cascaded IRL demux commit a2601b8d8f077368c6d113b4d496559415c6d495 upstream. Take into account the virq offset when translating cascaded IRL interrupts. Fixes: a8ac2961148e8c72 ("sh: Avoid using IRQ0 on SH3 and SH4") Signed-off-by: Geert Uytterhoeven Reviewed-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/4fcb0d08a2b372431c41e04312742dc9e41e1be4.1688908186.git.geert+renesas@glider.be Signed-off-by: John Paul Adrian Glaubitz Signed-off-by: Greg Kroah-Hartman --- arch/sh/boards/mach-highlander/setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/sh/boards/mach-highlander/setup.c b/arch/sh/boards/mach-highlander/setup.c index 533393d779c2..01565660a669 100644 --- a/arch/sh/boards/mach-highlander/setup.c +++ b/arch/sh/boards/mach-highlander/setup.c @@ -389,10 +389,10 @@ static unsigned char irl2irq[HL_NR_IRL]; static int highlander_irq_demux(int irq) { - if (irq >= HL_NR_IRL || irq < 0 || !irl2irq[irq]) + if (irq >= HL_NR_IRL + 16 || irq < 16 || !irl2irq[irq - 16]) return irq; - return irl2irq[irq]; + return irl2irq[irq - 16]; } static void __init highlander_init_irq(void) -- cgit 1.4.1 From 0ff5d219eb8ae8a4bcdf4ec3e92c26f829d86cda Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 9 Jul 2023 15:10:43 +0200 Subject: sh: mach-dreamcast: Handle virq offset in cascaded IRQ demux commit 3d20f7a6eb76afdf9d4ad9cb864c2e2da9c38e1f upstream. Take into account the virq offset when translating cascaded interrupts. Fixes: a8ac2961148e8c72 ("sh: Avoid using IRQ0 on SH3 and SH4") Signed-off-by: Geert Uytterhoeven Reviewed-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/7d0cb246c9f1cd24bb1f637ec5cb67e799a4c3b8.1688908227.git.geert+renesas@glider.be Signed-off-by: John Paul Adrian Glaubitz Signed-off-by: Greg Kroah-Hartman --- arch/sh/boards/mach-dreamcast/irq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/sh/boards/mach-dreamcast/irq.c b/arch/sh/boards/mach-dreamcast/irq.c index cc06e4cdb4cd..0eec82fb85e7 100644 --- a/arch/sh/boards/mach-dreamcast/irq.c +++ b/arch/sh/boards/mach-dreamcast/irq.c @@ -108,13 +108,13 @@ int systemasic_irq_demux(int irq) __u32 j, bit; switch (irq) { - case 13: + case 13 + 16: level = 0; break; - case 11: + case 11 + 16: level = 1; break; - case 9: + case 9 + 16: level = 2; break; default: -- cgit 1.4.1 From c55b552e0b605636e3f210125c7578b025a1dec1 Mon Sep 17 00:00:00 2001 From: Artur Rojek Date: Tue, 11 Jul 2023 01:31:32 +0200 Subject: sh: hd64461: Handle virq offset for offchip IRQ base and HD64461 IRQ commit 7c28a35e19fafa1d3b367bcd3ec4021427a9397b upstream. A recent change to start counting SuperH IRQ #s from 16 breaks support for the Hitachi HD64461 companion chip. Move the offchip IRQ base and HD64461 IRQ # by 16 in order to accommodate for the new virq numbering rules. Fixes: a8ac2961148e ("sh: Avoid using IRQ0 on SH3 and SH4") Signed-off-by: Artur Rojek Reviewed-by: Geert Uytterhoeven Reviewed-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/20230710233132.69734-1-contact@artur-rojek.eu Signed-off-by: John Paul Adrian Glaubitz Signed-off-by: Greg Kroah-Hartman --- arch/sh/cchips/Kconfig | 4 ++-- arch/sh/include/asm/hd64461.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/sh/cchips/Kconfig b/arch/sh/cchips/Kconfig index efde2edb5627..9659a0bc58de 100644 --- a/arch/sh/cchips/Kconfig +++ b/arch/sh/cchips/Kconfig @@ -29,9 +29,9 @@ endchoice config HD64461_IRQ int "HD64461 IRQ" depends on HD64461 - default "36" + default "52" help - The default setting of the HD64461 IRQ is 36. + The default setting of the HD64461 IRQ is 52. Do not change this unless you know what you are doing. diff --git a/arch/sh/include/asm/hd64461.h b/arch/sh/include/asm/hd64461.h index afb24cb034b1..d2c485fa333b 100644 --- a/arch/sh/include/asm/hd64461.h +++ b/arch/sh/include/asm/hd64461.h @@ -229,7 +229,7 @@ #define HD64461_NIMR HD64461_IO_OFFSET(0x5002) #define HD64461_IRQBASE OFFCHIP_IRQ_BASE -#define OFFCHIP_IRQ_BASE 64 +#define OFFCHIP_IRQ_BASE (64 + 16) #define HD64461_IRQ_NUM 16 #define HD64461_IRQ_UART (HD64461_IRQBASE+5) -- cgit 1.4.1 From f32dfc802e8733028088edf54499d5669cb0ef69 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sun, 16 Jul 2023 12:13:06 -0600 Subject: io_uring: Use io_schedule* in cqring wait Commit 8a796565cec3601071cbbd27d6304e202019d014 upstream. I observed poor performance of io_uring compared to synchronous IO. That turns out to be caused by deeper CPU idle states entered with io_uring, due to io_uring using plain schedule(), whereas synchronous IO uses io_schedule(). The losses due to this are substantial. On my cascade lake workstation, t/io_uring from the fio repository e.g. yields regressions between 20% and 40% with the following command: ./t/io_uring -r 5 -X0 -d 1 -s 1 -c 1 -p 0 -S$use_sync -R 0 /mnt/t2/fio/write.0.0 This is repeatable with different filesystems, using raw block devices and using different block devices. Use io_schedule_prepare() / io_schedule_finish() in io_cqring_wait_schedule() to address the difference. After that using io_uring is on par or surpassing synchronous IO (using registered files etc makes it reliably win, but arguably is a less fair comparison). There are other calls to schedule() in io_uring/, but none immediately jump out to be similarly situated, so I did not touch them. Similarly, it's possible that mutex_lock_io() should be used, but it's not clear if there are cases where that matters. Cc: stable@vger.kernel.org # 5.10+ Cc: Pavel Begunkov Cc: io-uring@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Andres Freund Link: https://lore.kernel.org/r/20230707162007.194068-1-andres@anarazel.de [axboe: minor style fixup] Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/io_uring.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index a85d8879c070..38bc0073a7d4 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -2346,7 +2346,7 @@ static inline int io_cqring_wait_schedule(struct io_ring_ctx *ctx, struct io_wait_queue *iowq, ktime_t *timeout) { - int ret; + int token, ret; unsigned long check_cq; /* make sure we run task_work before checking for signals */ @@ -2362,9 +2362,18 @@ static inline int io_cqring_wait_schedule(struct io_ring_ctx *ctx, if (check_cq & BIT(IO_CHECK_CQ_DROPPED_BIT)) return -EBADR; } + + /* + * Use io_schedule_prepare/finish, so cpufreq can take into account + * that the task is waiting for IO - turns out to be important for low + * QD IO. + */ + token = io_schedule_prepare(); + ret = 1; if (!schedule_hrtimeout(timeout, HRTIMER_MODE_ABS)) - return -ETIME; - return 1; + ret = -ETIME; + io_schedule_finish(token); + return ret; } /* -- cgit 1.4.1 From a456e17438819ed77f63d16926f96101ca215f09 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Jul 2023 16:22:18 +0200 Subject: Linux 6.1.39 Link: https://lore.kernel.org/r/20230716194923.861634455@linuxfoundation.org Tested-by: Takeshi Ogasawara Tested-by: Conor Dooley Tested-by: Shuah Khan Link: https://lore.kernel.org/r/20230717185609.886113843@linuxfoundation.org Link: https://lore.kernel.org/r/20230717201547.359923764@linuxfoundation.org Tested-by: Takeshi Ogasawara Tested-by: Bagas Sanjaya Tested-by: Jon Hunter Tested-by: Chris Paterson (CIP) Tested-by: Ron Economos Tested-by: Guenter Roeck Tested-by: Joel Fernandes (Google) Tested-by: Allen Pais Tested-by: SeongJae Park Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 57c891b8b13c..f0619754c29a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 38 +SUBLEVEL = 39 EXTRAVERSION = NAME = Curry Ramen -- cgit 1.4.1